1
/* play.c - command to play a tune */
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. */
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. */
37
/* If 0, follow state of SPEAKER_DATA bit, otherwise enable output
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
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
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. */
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
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
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)
110
grub_uint16_t duration;
116
unsigned char status;
118
status = grub_inb (SPEAKER);
119
grub_outb (status & ~(SPEAKER_TMR2 | SPEAKER_DATA), SPEAKER);
123
beep_on (grub_uint16_t pitch)
125
unsigned char status;
126
unsigned int counter;
130
else if (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 */
142
status = grub_inb (SPEAKER);
143
grub_outb (status | SPEAKER_TMR2 | SPEAKER_DATA, SPEAKER);
146
/* Returns whether playing should continue. */
148
play (unsigned tempo, struct note *note)
152
if (note->pitch == T_FINE || grub_checkkey () >= 0)
155
grub_dprintf ("play", "pitch = %d, duration = %d\n", note->pitch,
165
beep_on (note->pitch);
169
to = grub_get_rtc () + BASE_TEMPO * note->duration / tempo;
170
while (((unsigned int) grub_get_rtc () <= to) && (grub_checkkey () < 0))
177
grub_cmd_play (grub_command_t cmd __attribute__ ((unused)),
178
int argc, char **args)
182
return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name or tempo and notes required");
190
file = grub_file_open (args[0]);
193
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
195
if (grub_file_read (file, &tempo, sizeof (tempo)) != sizeof (tempo))
197
grub_file_close (file);
198
return grub_error (GRUB_ERR_FILE_READ_ERROR,
199
"file doesn't even contains a full tempo record");
202
tempo = grub_le_to_cpu32 (tempo);
203
grub_dprintf ("play","tempo = %d\n", tempo);
205
while (grub_file_read (file, &buf,
206
sizeof (struct note)) == sizeof (struct note))
208
buf.pitch = grub_le_to_cpu16 (buf.pitch);
209
buf.duration = grub_le_to_cpu16 (buf.duration);
211
if (play (tempo, &buf))
215
grub_file_close (file);
224
tempo = grub_strtoul (args[0], &end, 0);
227
/* Was not a number either, assume it was supposed to be a file name. */
228
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
230
grub_dprintf ("play","tempo = %d\n", tempo);
232
for (i = 1; i + 1 < argc; i += 2)
234
note.pitch = grub_strtoul (args[i], &end, 0);
237
grub_error (GRUB_ERR_BAD_NUMBER, "bogus pitch number");
241
note.duration = grub_strtoul (args[i + 1], &end, 0);
244
grub_error (GRUB_ERR_BAD_NUMBER, "bogus duration number");
248
if (play (tempo, ¬e))
255
while (grub_checkkey () > 0)
261
static grub_command_t cmd;
265
cmd = grub_register_command ("play", grub_cmd_play,
266
N_("FILE | TEMPO [PITCH1 DURATION1] [PITCH2 DURATION2] ... "),
272
grub_unregister_command (cmd);