GRUB-1.98 changes
[grub2/jjazz.git] / commands / i386 / pc / play.c
blob44d98a1f03e096a4a1ad84d328d9f6ab096494e4
1 /* play.c - command to play a tune */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2007,2009 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 /* Lots of this file is borrowed from GNU/Hurd generic-speaker driver. */
22 #include <grub/dl.h>
23 #include <grub/file.h>
24 #include <grub/disk.h>
25 #include <grub/term.h>
26 #include <grub/misc.h>
27 #include <grub/machine/time.h>
28 #include <grub/cpu/io.h>
29 #include <grub/command.h>
30 #include <grub/i18n.h>
32 #define BASE_TEMPO (60 * GRUB_TICKS_PER_SECOND)
34 /* The speaker port. */
35 #define SPEAKER 0x61
37 /* If 0, follow state of SPEAKER_DATA bit, otherwise enable output
38 from timer 2. */
39 #define SPEAKER_TMR2 0x01
41 /* If SPEAKER_TMR2 is not set, this provides direct input into the
42 speaker. Otherwise, this enables or disables the output from the
43 timer. */
44 #define SPEAKER_DATA 0x02
46 /* The PIT channel value ports. You can write to and read from them.
47 Do not mess with timer 0 or 1. */
48 #define PIT_COUNTER_0 0x40
49 #define PIT_COUNTER_1 0x41
50 #define PIT_COUNTER_2 0x42
52 /* The frequency of the PIT clock. */
53 #define PIT_FREQUENCY 0x1234dd
55 /* The PIT control port. You can only write to it. Do not mess with
56 timer 0 or 1. */
57 #define PIT_CTRL 0x43
58 #define PIT_CTRL_SELECT_MASK 0xc0
59 #define PIT_CTRL_SELECT_0 0x00
60 #define PIT_CTRL_SELECT_1 0x40
61 #define PIT_CTRL_SELECT_2 0x80
63 /* Read and load control. */
64 #define PIT_CTRL_READLOAD_MASK 0x30
65 #define PIT_CTRL_COUNTER_LATCH 0x00 /* Hold timer value until read. */
66 #define PIT_CTRL_READLOAD_LSB 0x10 /* Read/load the LSB. */
67 #define PIT_CTRL_READLOAD_MSB 0x20 /* Read/load the MSB. */
68 #define PIT_CTRL_READLOAD_WORD 0x30 /* Read/load the LSB then the MSB. */
70 /* Mode control. */
71 #define PIT_CTRL_MODE_MASK 0x0e
73 /* Interrupt on terminal count. Setting the mode sets output to low.
74 When counter is set and terminated, output is set to high. */
75 #define PIT_CTRL_INTR_ON_TERM 0x00
77 /* Programmable one-shot. When loading counter, output is set to
78 high. When counter terminated, output is set to low. Can be
79 triggered again from that point on by setting the gate pin to
80 high. */
81 #define PIT_CTRL_PROGR_ONE_SHOT 0x02
83 /* Rate generator. Output is low for one period of the counter, and
84 high for the other. */
85 #define PIT_CTRL_RATE_GEN 0x04
87 /* Square wave generator. Output is low for one half of the period,
88 and high for the other half. */
89 #define PIT_CTRL_SQUAREWAVE_GEN 0x06
91 /* Software triggered strobe. Setting the mode sets output to high.
92 When counter is set and terminated, output is set to low. */
93 #define PIT_CTRL_SOFTSTROBE 0x08
95 /* Hardware triggered strobe. Like software triggered strobe, but
96 only starts the counter when the gate pin is set to high. */
97 #define PIT_CTRL_HARDSTROBE 0x0a
99 /* Count mode. */
100 #define PIT_CTRL_COUNT_MASK 0x01
101 #define PIT_CTRL_COUNT_BINARY 0x00 /* 16-bit binary counter. */
102 #define PIT_CTRL_COUNT_BCD 0x01 /* 4-decade BCD counter. */
104 #define T_REST ((grub_uint16_t) 0)
105 #define T_FINE ((grub_uint16_t) -1)
107 struct note
109 grub_uint16_t pitch;
110 grub_uint16_t duration;
113 static void
114 beep_off (void)
116 unsigned char status;
118 status = grub_inb (SPEAKER);
119 grub_outb (status & ~(SPEAKER_TMR2 | SPEAKER_DATA), SPEAKER);
122 static void
123 beep_on (grub_uint16_t pitch)
125 unsigned char status;
126 unsigned int counter;
128 if (pitch < 20)
129 pitch = 20;
130 else if (pitch > 20000)
131 pitch = 20000;
133 counter = PIT_FREQUENCY / pitch;
135 /* Program timer 2. */
136 grub_outb (PIT_CTRL_SELECT_2 | PIT_CTRL_READLOAD_WORD
137 | PIT_CTRL_SQUAREWAVE_GEN | PIT_CTRL_COUNT_BINARY, PIT_CTRL);
138 grub_outb (counter & 0xff, PIT_COUNTER_2); /* LSB */
139 grub_outb ((counter >> 8) & 0xff, PIT_COUNTER_2); /* MSB */
141 /* Start speaker. */
142 status = grub_inb (SPEAKER);
143 grub_outb (status | SPEAKER_TMR2 | SPEAKER_DATA, SPEAKER);
146 /* Returns whether playing should continue. */
147 static int
148 play (unsigned tempo, struct note *note)
150 unsigned int to;
152 if (note->pitch == T_FINE || grub_checkkey () >= 0)
153 return 1;
155 grub_dprintf ("play", "pitch = %d, duration = %d\n", note->pitch,
156 note->duration);
158 switch (note->pitch)
160 case T_REST:
161 beep_off ();
162 break;
164 default:
165 beep_on (note->pitch);
166 break;
169 to = grub_get_rtc () + BASE_TEMPO * note->duration / tempo;
170 while (((unsigned int) grub_get_rtc () <= to) && (grub_checkkey () < 0))
173 return 0;
176 static grub_err_t
177 grub_cmd_play (grub_command_t cmd __attribute__ ((unused)),
178 int argc, char **args)
180 grub_file_t file;
182 if (argc < 1)
183 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name or tempo and notes required");
185 file = grub_file_open (args[0]);
186 if (file)
188 struct note buf;
189 grub_uint32_t tempo;
191 if (grub_file_read (file, &tempo, sizeof (tempo)) != sizeof (tempo))
193 grub_file_close (file);
194 return grub_error (GRUB_ERR_FILE_READ_ERROR,
195 "file doesn't even contains a full tempo record");
198 tempo = grub_le_to_cpu32 (tempo);
199 grub_dprintf ("play","tempo = %d\n", tempo);
201 while (grub_file_read (file, &buf,
202 sizeof (struct note)) == sizeof (struct note))
204 buf.pitch = grub_le_to_cpu16 (buf.pitch);
205 buf.duration = grub_le_to_cpu16 (buf.duration);
207 if (play (tempo, &buf))
208 break;
211 grub_file_close (file);
213 else
215 char *end;
216 unsigned tempo;
217 struct note note;
218 int i;
220 tempo = grub_strtoul (args[0], &end, 0);
222 if (*end)
223 /* Was not a number either, assume it was supposed to be a file name. */
224 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
226 grub_dprintf ("play","tempo = %d\n", tempo);
228 for (i = 1; i + 1 < argc; i += 2)
230 note.pitch = grub_strtoul (args[i], &end, 0);
231 if (*end)
233 grub_error (GRUB_ERR_BAD_NUMBER, "bogus pitch number");
234 break;
237 note.duration = grub_strtoul (args[i + 1], &end, 0);
238 if (*end)
240 grub_error (GRUB_ERR_BAD_NUMBER, "bogus duration number");
241 break;
244 if (play (tempo, &note))
245 break;
249 beep_off ();
251 while (grub_checkkey () > 0)
252 grub_getkey ();
254 return 0;
257 static grub_command_t cmd;
259 GRUB_MOD_INIT(play)
261 cmd = grub_register_command ("play", grub_cmd_play,
262 N_("FILE | TEMPO [PITCH1 DURATION1] [PITCH2 DURATION2] ... "),
263 N_("Play a tune."));
266 GRUB_MOD_FINI(play)
268 grub_unregister_command (cmd);