From 41730ed9907966962f7d8880590d7a920c88cd6f Mon Sep 17 00:00:00 2001 From: subdino Date: Sat, 3 Dec 2005 16:18:27 +0000 Subject: [PATCH] 2005-12-03 Vincent Pelletier * commands/i386/pc/play.c: New file. * conf/i386-pc.rmk (pkgdata_MODULES): Added play.mod. (play_mod_SOURCES, play_mod_CFLAGS, play_mod_LDFLAGS): New macros. --- ChangeLog | 7 ++ commands/i386/pc/play.c | 233 ++++++++++++++++++++++++++++++++++++++++ conf/i386-pc.mk | 53 ++++++++- conf/i386-pc.rmk | 7 +- 4 files changed, 298 insertions(+), 2 deletions(-) create mode 100644 commands/i386/pc/play.c diff --git a/ChangeLog b/ChangeLog index 2af205e33..a788355ed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2005-12-03 Vincent Pelletier + + * commands/i386/pc/play.c: New file. + * conf/i386-pc.rmk (pkgdata_MODULES): Added play.mod. + (play_mod_SOURCES, play_mod_CFLAGS, play_mod_LDFLAGS): New + macros. + 2005-11-27 Marco Gerards * include/grub/dl.h (GRUB_MOD_INIT): Use `__attribute__ diff --git a/commands/i386/pc/play.c b/commands/i386/pc/play.c new file mode 100644 index 000000000..f3f524b50 --- /dev/null +++ b/commands/i386/pc/play.c @@ -0,0 +1,233 @@ +/* play.c - command to play a tune */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005 Free Software Foundation, Inc. + * + * GRUB is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Lots of this file is borrowed from GNU/Hurd generic-speaker driver. */ + +#include +#include +#include +#include +#include +#include +#include + +#define BASE_TEMPO 120 + +/* Read a byte from a port. */ +static inline unsigned char +inb (unsigned short port) +{ + unsigned char value; + asm volatile ("inb %w1, %0" : "=a" (value) : "Nd" (port)); + return value; +} + +/* Write a byte to a port. */ +static inline void +outb (unsigned short port, unsigned char value) +{ + asm volatile ("outb %b0, %w1" : : "a" (value), "Nd" (port)); +} + +/* The speaker port. */ +#define SPEAKER 0x61 + +/* If 0, follow state of SPEAKER_DATA bit, otherwise enable output + from timer 2. */ +#define SPEAKER_TMR2 0x01 + +/* If SPEAKER_TMR2 is not set, this provides direct input into the + speaker. Otherwise, this enables or disables the output from the + timer. */ +#define SPEAKER_DATA 0x02 + +/* The PIT channel value ports. You can write to and read from them. + Do not mess with timer 0 or 1. */ +#define PIT_COUNTER_0 0x40 +#define PIT_COUNTER_1 0x41 +#define PIT_COUNTER_2 0x42 + +/* The frequency of the PIT clock. */ +#define PIT_FREQUENCY 0x1234dd + +/* The PIT control port. You can only write to it. Do not mess with + timer 0 or 1. */ +#define PIT_CTRL 0x43 +#define PIT_CTRL_SELECT_MASK 0xc0 +#define PIT_CTRL_SELECT_0 0x00 +#define PIT_CTRL_SELECT_1 0x40 +#define PIT_CTRL_SELECT_2 0x80 + +/* Read and load control. */ +#define PIT_CTRL_READLOAD_MASK 0x30 +#define PIT_CTRL_COUNTER_LATCH 0x00 /* Hold timer value until read. */ +#define PIT_CTRL_READLOAD_LSB 0x10 /* Read/load the LSB. */ +#define PIT_CTRL_READLOAD_MSB 0x20 /* Read/load the MSB. */ +#define PIT_CTRL_READLOAD_WORD 0x30 /* Read/load the LSB then the MSB. */ + +/* Mode control. */ +#define PIT_CTRL_MODE_MASK 0x0e + +/* Interrupt on terminal count. Setting the mode sets output to low. + When counter is set and terminated, output is set to high. */ +#define PIT_CTRL_INTR_ON_TERM 0x00 + +/* Programmable one-shot. When loading counter, output is set to + high. When counter terminated, output is set to low. Can be + triggered again from that point on by setting the gate pin to + high. */ +#define PIT_CTRL_PROGR_ONE_SHOT 0x02 + +/* Rate generator. Output is low for one period of the counter, and + high for the other. */ +#define PIT_CTRL_RATE_GEN 0x04 + +/* Square wave generator. Output is low for one half of the period, + and high for the other half. */ +#define PIT_CTRL_SQUAREWAVE_GEN 0x06 + +/* Software triggered strobe. Setting the mode sets output to high. + When counter is set and terminated, output is set to low. */ +#define PIT_CTRL_SOFTSTROBE 0x08 + +/* Hardware triggered strobe. Like software triggered strobe, but + only starts the counter when the gate pin is set to high. */ +#define PIT_CTRL_HARDSTROBE 0x0a + +/* Count mode. */ +#define PIT_CTRL_COUNT_MASK 0x01 +#define PIT_CTRL_COUNT_BINARY 0x00 /* 16-bit binary counter. */ +#define PIT_CTRL_COUNT_BCD 0x01 /* 4-decade BCD counter. */ + +#define T_REST ((short) 0) +#define T_FINE ((short) -1) + +struct note +{ + short pitch; + short duration; +}; + +static void +beep_off (void) +{ + unsigned char status; + + status = inb (SPEAKER); + outb (SPEAKER, status & ~(SPEAKER_TMR2 | SPEAKER_DATA)); +} + +static void +beep_on (short pitch) +{ + unsigned char status; + unsigned int counter; + + if (pitch < 20) + pitch = 20; + else if (pitch > 20000) + pitch = 20000; + + counter = PIT_FREQUENCY / pitch; + + /* Program timer 2. */ + outb (PIT_CTRL, PIT_CTRL_SELECT_2 | PIT_CTRL_READLOAD_WORD + | PIT_CTRL_SQUAREWAVE_GEN | PIT_CTRL_COUNT_BINARY); + outb (PIT_COUNTER_2, counter & 0xff); /* LSB */ + outb (PIT_COUNTER_2, (counter >> 8) & 0xff); /* MSB */ + + /* Start speaker. */ + status = inb (SPEAKER); + outb (SPEAKER, status | SPEAKER_TMR2 | SPEAKER_DATA); + +} + +static grub_err_t +grub_cmd_play (struct grub_arg_list *state __attribute__ ((unused)), + int argc, char **args) +{ + grub_file_t file; + struct note buf; + int tempo; + unsigned int to; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required"); + + file = grub_file_open (args[0]); + if (! file) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + + if (grub_file_read (file, (void *) &tempo, sizeof(tempo)) != sizeof(tempo)) + { + grub_file_close (file); + return grub_error (GRUB_ERR_FILE_READ_ERROR, + "file doesn't even contains a full tempo record"); + } + + grub_dprintf ("play","tempo = %d\n", tempo); + + while (grub_file_read (file, (void *) &buf, + sizeof (struct note)) == sizeof (struct note) + && buf.pitch != T_FINE && grub_checkkey () < 0) + { + + grub_dprintf ("play", "pitch = %d, duration = %d\n", buf.pitch, + buf.duration); + + switch (buf.pitch) + { + case T_REST: + beep_off (); + break; + + default: + beep_on (buf.pitch); + break; + } + + to = grub_get_rtc () + BASE_TEMPO * buf.duration / tempo; + while (((unsigned int) grub_get_rtc () <= to) && (grub_checkkey () < 0)) + ; + + } + + beep_off (); + + grub_file_close (file); + + while (grub_checkkey () > 0) + grub_getkey (); + + return 0; +} + + +GRUB_MOD_INIT(play) +{ + (void)mod; /* To stop warning. */ + grub_register_command ("play", grub_cmd_play, GRUB_COMMAND_FLAG_BOTH, + "play FILE", "Play a tune", 0); +} + +GRUB_MOD_FINI(play) +{ + grub_unregister_command ("play"); +} diff --git a/conf/i386-pc.mk b/conf/i386-pc.mk index cb3f58491..05f60f81a 100644 --- a/conf/i386-pc.mk +++ b/conf/i386-pc.mk @@ -1336,7 +1336,7 @@ grub-install: util/i386/pc/grub-install.in config.status # Modules. pkgdata_MODULES = _chain.mod _linux.mod linux.mod normal.mod vga.mod \ _multiboot.mod chain.mod multiboot.mod reboot.mod halt.mod \ - vbe.mod vesafb.mod vbetest.mod vbeinfo.mod + vbe.mod vesafb.mod vbetest.mod vbeinfo.mod play.mod # For _chain.mod. _chain_mod_SOURCES = loader/i386/pc/chainloader.c @@ -2328,4 +2328,55 @@ fs-vbetest_mod-commands_i386_pc_vbetest.lst: commands/i386/pc/vbetest.c genfslis vbetest_mod_CFLAGS = $(COMMON_CFLAGS) vbetest_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For play.mod. +play_mod_SOURCES = commands/i386/pc/play.c +CLEANFILES += play.mod mod-play.o mod-play.c pre-play.o play_mod-commands_i386_pc_play.o def-play.lst und-play.lst +MOSTLYCLEANFILES += play_mod-commands_i386_pc_play.d +DEFSYMFILES += def-play.lst +UNDSYMFILES += und-play.lst + +play.mod: pre-play.o mod-play.o + -rm -f $@ + $(LD) $(play_mod_LDFLAGS) $(LDFLAGS) -r -d -o $@ $^ + $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R .note -R .comment $@ + +pre-play.o: play_mod-commands_i386_pc_play.o + -rm -f $@ + $(LD) $(play_mod_LDFLAGS) -r -d -o $@ $^ + +mod-play.o: mod-play.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(play_mod_CFLAGS) -c -o $@ $< + +mod-play.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh 'play' $< > $@ || (rm -f $@; exit 1) + +def-play.lst: pre-play.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 play/' > $@ + +und-play.lst: pre-play.o + echo 'play' > $@ + $(NM) -u -P -p $< | cut -f1 -d' ' >> $@ + +play_mod-commands_i386_pc_play.o: commands/i386/pc/play.c + $(CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(CPPFLAGS) $(CFLAGS) $(play_mod_CFLAGS) -c -o $@ $< + +play_mod-commands_i386_pc_play.d: commands/i386/pc/play.c + set -e; $(CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(CPPFLAGS) $(CFLAGS) $(play_mod_CFLAGS) -M $< | sed 's,play\.o[ :]*,play_mod-commands_i386_pc_play.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ + +-include play_mod-commands_i386_pc_play.d + +CLEANFILES += cmd-play_mod-commands_i386_pc_play.lst fs-play_mod-commands_i386_pc_play.lst +COMMANDFILES += cmd-play_mod-commands_i386_pc_play.lst +FSFILES += fs-play_mod-commands_i386_pc_play.lst + +cmd-play_mod-commands_i386_pc_play.lst: commands/i386/pc/play.c gencmdlist.sh + set -e; $(CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(CPPFLAGS) $(CFLAGS) $(play_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh play > $@ || (rm -f $@; exit 1) + +fs-play_mod-commands_i386_pc_play.lst: commands/i386/pc/play.c genfslist.sh + set -e; $(CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(CPPFLAGS) $(CFLAGS) $(play_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh play > $@ || (rm -f $@; exit 1) + + +play_mod_CFLAGS = $(COMMON_CFLAGS) +play_mod_LDFLAGS = $(COMMON_LDFLAGS) + include $(srcdir)/conf/common.mk diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index ef971ad1c..4cf37dc54 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -114,7 +114,7 @@ grub_install_SOURCES = util/i386/pc/grub-install.in # Modules. pkgdata_MODULES = _chain.mod _linux.mod linux.mod normal.mod vga.mod \ _multiboot.mod chain.mod multiboot.mod reboot.mod halt.mod \ - vbe.mod vesafb.mod vbetest.mod vbeinfo.mod + vbe.mod vesafb.mod vbetest.mod vbeinfo.mod play.mod # For _chain.mod. _chain_mod_SOURCES = loader/i386/pc/chainloader.c @@ -196,4 +196,9 @@ vbetest_mod_SOURCES = commands/i386/pc/vbetest.c vbetest_mod_CFLAGS = $(COMMON_CFLAGS) vbetest_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For play.mod. +play_mod_SOURCES = commands/i386/pc/play.c +play_mod_CFLAGS = $(COMMON_CFLAGS) +play_mod_LDFLAGS = $(COMMON_LDFLAGS) + include $(srcdir)/conf/common.mk