Drew DeVault
2020-Jan-22 02:22 UTC
[syslinux] [PATCH] com32/modules: introduce play module
This plays a tune through the PC speaker and is compatible with the GRUB2 command of the same name. Signed-off-by: Drew DeVault <sir at cmpwn.com> --- com32/modules/Makefile | 2 +- com32/modules/play.c | 95 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 com32/modules/play.c diff --git a/com32/modules/Makefile b/com32/modules/Makefile index c01d6338..5e69a0d7 100644 --- a/com32/modules/Makefile +++ b/com32/modules/Makefile @@ -20,7 +20,7 @@ include $(MAKEDIR)/elf.mk # BIOS-specific modules MOD_BIOS = disk.c32 elf.c32 ethersel.c32 gpxecmd.c32 ifmemdsk.c32 ifplop.c32 \ - kbdmap.c32 kontron_wdt.c32 pcitest.c32 pmload.c32 poweroff.c32 \ + kbdmap.c32 kontron_wdt.c32 pcitest.c32 play.c32 pmload.c32 poweroff.c32 \ prdhcp.c32 pxechn.c32 sanboot.c32 sdi.c32 vesainfo.c32 # All-architecture modules diff --git a/com32/modules/play.c b/com32/modules/play.c new file mode 100644 index 00000000..68741bf5 --- /dev/null +++ b/com32/modules/play.c @@ -0,0 +1,95 @@ +/* + * Plays a tune through the PC speaker. + * + * This file is based loosely on similar GRUB2 code. + */ +#include <stdlib.h> +#include <stdio.h> +#include <sys/io.h> +#include <unistd.h> + +#define SPEAKER_PIT_FREQUENCY 0x1234dd +#define BASE_TEMPO (60 * 1000) +#define T_REST 0 + +#define PIT_COUNTER_2 0x42 +#define PIT_CTRL 0x43 +#define PIT_SPEAKER_PORT 0x61 + +#define PIT_SPK_TMR2 0x01 +#define PIT_SPK_DATA 0x02 +#define PIT_SPK_TMR2_LATCH 0x20 +#define PIT_CTRL_SELECT_2 0x80 +#define PIT_CTRL_READLOAD_WORD 0x30 +#define PIT_CTRL_SQUAREWAVE_GEN 0x06 +#define PIT_CTRL_COUNT_BINARY 0x00 + +static void beep_off(void) +{ + unsigned char status; + + status = inb(PIT_SPEAKER_PORT); + outb(status & ~(PIT_SPK_TMR2 | PIT_SPK_DATA), PIT_SPEAKER_PORT); +} + +static void beep_on(uint16_t pitch) +{ + unsigned char status; + unsigned int counter; + + if (pitch < 20) + pitch = 20; + else if (pitch > 20000) + pitch = 20000; + + counter = SPEAKER_PIT_FREQUENCY / pitch; + outb(PIT_CTRL_SELECT_2 + | PIT_CTRL_READLOAD_WORD + | PIT_CTRL_SQUAREWAVE_GEN + | PIT_CTRL_COUNT_BINARY, PIT_CTRL); + outb(counter & 0xff, PIT_COUNTER_2); + outb((counter >> 8) & 0xff, PIT_COUNTER_2); + + status = inb(PIT_SPEAKER_PORT); + outb(status | PIT_SPK_TMR2 | PIT_SPK_DATA, PIT_SPEAKER_PORT); +} + +static void play(unsigned int tempo, uint16_t pitch, uint16_t duration) +{ + unsigned int ms = BASE_TEMPO * duration / tempo; + + switch (pitch) { + case T_REST: + beep_off(); + break; + default: + beep_on(pitch); + break; + } + + msleep(ms); +} + +int main(int argc, char *argv[]) +{ + int i; + unsigned int tempo = 0; + + tempo = strtol(argv[1], NULL, 10); + if (!tempo) { + printf("usage: play [<tempo>] [<pitch> <duration>...]\n"); + return 1; + } + + for (i = 2; i + 1 < argc; i += 2) { + uint16_t pitch; + uint16_t duration; + + pitch = strtol(argv[i], NULL, 10); + duration = strtol(argv[i + 1], NULL, 10); + play(tempo, pitch, duration); + } + + beep_off(); + return 0; +} -- 2.25.0