diff --git a/ChangeLog b/ChangeLog index 96aca6677..f1c53e389 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2013-01-16 Vladimir Serbinenko + + New terminal outputs using serial: morse and spkmodem. + 2013-01-16 Vladimir Serbinenko Improve bidi handling in entry editor. diff --git a/docs/grub.texi b/docs/grub.texi index e75bae9fc..efedd27e1 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -1229,9 +1229,9 @@ Select the terminal input device. You may select multiple devices here, separated by spaces. Valid terminal input names depend on the platform, but may include -@samp{console} (PC BIOS and EFI consoles), @samp{serial} (serial terminal), -@samp{ofconsole} (Open Firmware console), @samp{at_keyboard} (PC AT -keyboard, mainly useful with Coreboot), or @samp{usb_keyboard} (USB keyboard +@samp{console} (native platform console), @samp{serial} (serial terminal), +@samp{serial_} (serial terminal with explicit port selection), +@samp{at_keyboard} (PC AT keyboard), or @samp{usb_keyboard} (USB keyboard using the HID Boot Protocol, for cases where the firmware does not handle this). @@ -1242,9 +1242,21 @@ Select the terminal output device. You may select multiple devices here, separated by spaces. Valid terminal output names depend on the platform, but may include -@samp{console} (PC BIOS and EFI consoles), @samp{serial} (serial terminal), -@samp{gfxterm} (graphics-mode output), @samp{ofconsole} (Open Firmware -console), or @samp{vga_text} (VGA text output, mainly useful with Coreboot). +@samp{console} (native platform console), @samp{serial} (serial terminal), +@samp{serial_} (serial terminal with explicit port selection), +@samp{gfxterm} (graphics-mode output), @samp{vga_text} (VGA text output), +@samp{mda_text} (MDA text output), @samp{morse} (Morse-coding using system +beeper) or @samp{spkmodem} (simple data protocol using system speaker). + +@samp{spkmodem} is useful when no serial port is available. Connect the output +of sending system (where GRUB is running) to line-in of receiving system +(usually developper machine). +On receiving system compile @samp{spkmodem-recv} from +@samp{util/spkmodem-recv.c} and run: + +@example +parecord --channels=1 --rate=48000 --format=s16le | ./spkmodem-recv +@end example The default is to use the platform's native terminal output. diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index f4dd645db..baf80b860 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -778,6 +778,18 @@ module = { enable = x86; }; +module = { + name = spkmodem; + x86 = term/spkmodem.c; + enable = x86; +}; + +module = { + name = morse; + x86 = term/morse.c; + enable = x86; +}; + module = { name = probe; common = commands/probe.c; diff --git a/grub-core/commands/i386/pc/play.c b/grub-core/commands/i386/pc/play.c index 10a018181..40798c96c 100644 --- a/grub-core/commands/i386/pc/play.c +++ b/grub-core/commands/i386/pc/play.c @@ -28,80 +28,12 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); #define BASE_TEMPO (60 * 1000) -/* 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 ((grub_uint16_t) 0) #define T_FINE ((grub_uint16_t) -1) @@ -112,39 +44,6 @@ struct note grub_uint16_t duration; }; -static void -beep_off (void) -{ - unsigned char status; - - status = grub_inb (SPEAKER); - grub_outb (status & ~(SPEAKER_TMR2 | SPEAKER_DATA), SPEAKER); -} - -static void -beep_on (grub_uint16_t 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. */ - grub_outb (PIT_CTRL_SELECT_2 | PIT_CTRL_READLOAD_WORD - | PIT_CTRL_SQUAREWAVE_GEN | PIT_CTRL_COUNT_BINARY, PIT_CTRL); - grub_outb (counter & 0xff, PIT_COUNTER_2); /* LSB */ - grub_outb ((counter >> 8) & 0xff, PIT_COUNTER_2); /* MSB */ - - /* Start speaker. */ - status = grub_inb (SPEAKER); - grub_outb (status | SPEAKER_TMR2 | SPEAKER_DATA, SPEAKER); -} - /* Returns whether playing should continue. */ static int play (unsigned tempo, struct note *note) @@ -160,11 +59,11 @@ play (unsigned tempo, struct note *note) switch (note->pitch) { case T_REST: - beep_off (); + grub_speaker_beep_off (); break; default: - beep_on (note->pitch); + grub_speaker_beep_on (note->pitch); break; } @@ -263,7 +162,7 @@ grub_cmd_play (grub_command_t cmd __attribute__ ((unused)), } } - beep_off (); + grub_speaker_beep_off (); return 0; } diff --git a/grub-core/kern/i386/pit.c b/grub-core/kern/i386/pit.c index 82a17d3e0..092481ae5 100644 --- a/grub-core/kern/i386/pit.c +++ b/grub-core/kern/i386/pit.c @@ -20,37 +20,30 @@ #include #include -#define TIMER2_REG_CONTROL 0x42 -#define TIMER_REG_COMMAND 0x43 -#define TIMER2_REG_LATCH 0x61 - -#define TIMER2_SELECT 0x80 -#define TIMER_ENABLE_LSB 0x20 -#define TIMER_ENABLE_MSB 0x10 -#define TIMER2_LATCH 0x20 -#define TIMER2_SPEAKER 0x02 -#define TIMER2_GATE 0x01 - void grub_pit_wait (grub_uint16_t tics) { /* Disable timer2 gate and speaker. */ - grub_outb (grub_inb (TIMER2_REG_LATCH) & ~ (TIMER2_SPEAKER | TIMER2_GATE), - TIMER2_REG_LATCH); + grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT) + & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2), + GRUB_PIT_SPEAKER_PORT); /* Set tics. */ - grub_outb (TIMER2_SELECT | TIMER_ENABLE_LSB | TIMER_ENABLE_MSB, TIMER_REG_COMMAND); - grub_outb (tics & 0xff, TIMER2_REG_CONTROL); - grub_outb (tics >> 8, TIMER2_REG_CONTROL); + grub_outb (GRUB_PIT_CTRL_SELECT_2 | GRUB_PIT_CTRL_READLOAD_WORD, + GRUB_PIT_CTRL); + grub_outb (tics & 0xff, GRUB_PIT_COUNTER_2); + grub_outb (tics >> 8, GRUB_PIT_COUNTER_2); /* Enable timer2 gate, keep speaker disabled. */ - grub_outb ((grub_inb (TIMER2_REG_LATCH) & ~ TIMER2_SPEAKER) | TIMER2_GATE, - TIMER2_REG_LATCH); + grub_outb ((grub_inb (GRUB_PIT_SPEAKER_PORT) & ~ GRUB_PIT_SPK_DATA) + | GRUB_PIT_SPK_TMR2, + GRUB_PIT_SPEAKER_PORT); /* Wait. */ - while ((grub_inb (TIMER2_REG_LATCH) & TIMER2_LATCH) == 0x00); + while ((grub_inb (GRUB_PIT_SPEAKER_PORT) & GRUB_PIT_SPK_TMR2_LATCH) == 0x00); /* Disable timer2 gate and speaker. */ - grub_outb (grub_inb (TIMER2_REG_LATCH) & ~ (TIMER2_SPEAKER | TIMER2_GATE), - TIMER2_REG_LATCH); + grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT) + & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2), + GRUB_PIT_SPEAKER_PORT); } diff --git a/grub-core/term/morse.c b/grub-core/term/morse.c new file mode 100644 index 000000000..3d6c65088 --- /dev/null +++ b/grub-core/term/morse.c @@ -0,0 +1,131 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011,2012,2013 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 3 of the License, or + * (at your option) any later version. + * + * GRUB 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define BASE_TIME 250 + +static const char codes[0x80][6] = + { + ['0'] = { 3, 3, 3, 3, 3, 0 }, + ['1'] = { 1, 3, 3, 3, 3, 0 }, + ['2'] = { 1, 1, 3, 3, 3, 0 }, + ['3'] = { 1, 1, 1, 3, 3, 0 }, + ['4'] = { 1, 1, 1, 1, 3, 0 }, + ['5'] = { 1, 1, 1, 1, 1, 0 }, + ['6'] = { 3, 1, 1, 1, 1, 0 }, + ['7'] = { 3, 3, 1, 1, 1, 0 }, + ['8'] = { 3, 3, 3, 1, 1, 0 }, + ['9'] = { 3, 3, 3, 3, 1, 0 }, + ['a'] = { 1, 3, 0 }, + ['b'] = { 3, 1, 1, 1, 0 }, + ['c'] = { 3, 1, 3, 1, 0 }, + ['d'] = { 3, 1, 1, 0 }, + ['e'] = { 1, 0 }, + ['f'] = { 1, 1, 3, 1, 0 }, + ['g'] = { 3, 3, 1, 0 }, + ['h'] = { 1, 1, 1, 1, 0 }, + ['i'] = { 1, 1, 0 }, + ['j'] = { 1, 3, 3, 3, 0 }, + ['k'] = { 3, 1, 3, 0 }, + ['l'] = { 1, 3, 1, 1, 0 }, + ['m'] = { 3, 3, 0 }, + ['n'] = { 3, 1, 0 }, + ['o'] = { 3, 3, 3, 0 }, + ['p'] = { 1, 3, 3, 1, 0 }, + ['q'] = { 3, 3, 1, 3, 0 }, + ['r'] = { 1, 3, 1, 0 }, + ['s'] = { 1, 1, 1, 0 }, + ['t'] = { 3, 0 }, + ['u'] = { 1, 1, 3, 0 }, + ['v'] = { 1, 1, 1, 3, 0 }, + ['w'] = { 1, 3, 3, 0 }, + ['x'] = { 3, 1, 1, 3, 0 }, + ['y'] = { 3, 1, 3, 3, 0 }, + ['z'] = { 3, 3, 1, 1, 0 } + }; + +static void +grub_audio_tone (int length) +{ + grub_speaker_beep_on (1000); + grub_millisleep (length); + grub_speaker_beep_off (); +} + +static void +grub_audio_putchar (struct grub_term_output *term __attribute__ ((unused)), + const struct grub_unicode_glyph *c_in) +{ + grub_uint8_t c; + int i; + + /* For now, do not try to use a surrogate pair. */ + if (c_in->base > 0x7f) + c = '?'; + else + c = grub_tolower (c_in->base); + for (i = 0; codes[c][i]; i++) + { + grub_audio_tone (codes[c][i] * BASE_TIME); + grub_millisleep (BASE_TIME); + } + grub_millisleep (2 * BASE_TIME); +} + + +static int +dummy (void) +{ + return 0; +} + +static struct grub_term_output grub_audio_term_output = + { + .name = "morse", + .init = (void *) dummy, + .fini = (void *) dummy, + .putchar = grub_audio_putchar, + .getwh = (void *) dummy, + .getxy = (void *) dummy, + .gotoxy = (void *) dummy, + .cls = (void *) dummy, + .setcolorstate = (void *) dummy, + .setcursor = (void *) dummy, + .normal_color = GRUB_TERM_DEFAULT_NORMAL_COLOR, + .highlight_color = GRUB_TERM_DEFAULT_HIGHLIGHT_COLOR, + .flags = GRUB_TERM_CODE_TYPE_ASCII | GRUB_TERM_DUMB + }; + +GRUB_MOD_INIT (morse) +{ + grub_term_register_output ("audio", &grub_audio_term_output); +} + +GRUB_MOD_FINI (morse) +{ + grub_term_unregister_output (&grub_audio_term_output); +} diff --git a/grub-core/term/spkmodem.c b/grub-core/term/spkmodem.c new file mode 100644 index 000000000..31dab65ab --- /dev/null +++ b/grub-core/term/spkmodem.c @@ -0,0 +1,106 @@ +/* console.c -- Open Firmware console for GRUB. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2004,2005,2007,2008,2009 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 3 of the License, or + * (at your option) any later version. + * + * GRUB 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +extern struct grub_terminfo_output_state grub_spkmodem_terminfo_output; + +static void +put (struct grub_term_output *term __attribute__ ((unused)), const int c) +{ + int i; + + for (i = 7; i >= 0; i--) + { + if ((c >> i) & 1) + grub_speaker_beep_on (2000); + else + grub_speaker_beep_on (4000); + grub_millisleep (10); + grub_speaker_beep_on (1000); + grub_millisleep (10); + } + grub_speaker_beep_on (50); +} + +static grub_err_t +grub_spkmodem_init_output (struct grub_term_output *term) +{ + grub_speaker_beep_on (50); + grub_millisleep (50); + + grub_terminfo_output_init (term); + + return 0; +} + +static grub_err_t +grub_spkmodem_fini_output (struct grub_term_output *term __attribute__ ((unused))) +{ + grub_speaker_beep_off (); + return 0; +} + + + + +struct grub_terminfo_output_state grub_spkmodem_terminfo_output = + { + .put = put, + .width = 80, + .height = 24 + }; + +static struct grub_term_output grub_spkmodem_term_output = + { + .name = "spkmodem", + .init = grub_spkmodem_init_output, + .fini = grub_spkmodem_fini_output, + .putchar = grub_terminfo_putchar, + .getxy = grub_terminfo_getxy, + .getwh = grub_terminfo_getwh, + .gotoxy = grub_terminfo_gotoxy, + .cls = grub_terminfo_cls, + .setcolorstate = grub_terminfo_setcolorstate, + .setcursor = grub_terminfo_setcursor, + .flags = GRUB_TERM_CODE_TYPE_ASCII, + .data = &grub_spkmodem_terminfo_output, + .normal_color = GRUB_TERM_DEFAULT_NORMAL_COLOR, + .highlight_color = GRUB_TERM_DEFAULT_HIGHLIGHT_COLOR, + }; + +GRUB_MOD_INIT (spkmodem) +{ + grub_term_register_output ("spkmodem", &grub_spkmodem_term_output); +} + + +GRUB_MOD_FINI (spkmodem) +{ + grub_term_unregister_output (&grub_spkmodem_term_output); +} diff --git a/include/grub/i386/pit.h b/include/grub/i386/pit.h index ff9b9a6d7..e1c92cd16 100644 --- a/include/grub/i386/pit.h +++ b/include/grub/i386/pit.h @@ -22,6 +22,84 @@ #include #include +enum + { + /* The PIT channel value ports. You can write to and read from them. + Do not mess with timer 0 or 1. */ + GRUB_PIT_COUNTER_0 = 0x40, + GRUB_PIT_COUNTER_1 = 0x41, + GRUB_PIT_COUNTER_2 = 0x42, + /* The PIT control port. You can only write to it. Do not mess with + timer 0 or 1. */ + GRUB_PIT_CTRL = 0x43, + /* The speaker port. */ + GRUB_PIT_SPEAKER_PORT = 0x61, + }; + + +/* The speaker port. */ +enum + { + /* If 0, follow state of SPEAKER_DATA bit, otherwise enable output + from timer 2. */ + GRUB_PIT_SPK_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. */ + GRUB_PIT_SPK_DATA = 0x02, + + GRUB_PIT_SPK_TMR2_LATCH = 0x20 + }; + +/* The PIT control port. You can only write to it. Do not mess with + timer 0 or 1. */ +enum + { + GRUB_PIT_CTRL_SELECT_MASK = 0xc0, + GRUB_PIT_CTRL_SELECT_0 = 0x00, + GRUB_PIT_CTRL_SELECT_1 = 0x40, + GRUB_PIT_CTRL_SELECT_2 = 0x80, + + /* Read and load control. */ + GRUB_PIT_CTRL_READLOAD_MASK= 0x30, + GRUB_PIT_CTRL_COUNTER_LATCH = 0x00, /* Hold timer value until read. */ + GRUB_PIT_CTRL_READLOAD_LSB = 0x10, /* Read/load the LSB. */ + GRUB_PIT_CTRL_READLOAD_MSB = 0x20, /* Read/load the MSB. */ + GRUB_PIT_CTRL_READLOAD_WORD = 0x30, /* Read/load the LSB then the MSB. */ + + /* Mode control. */ + GRUB_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. */ + GRUB_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. */ + GRUB_PIT_CTRL_PROGR_ONE_SHOT = 0x02, + + /* Rate generator. Output is low for one period of the counter, and + high for the other. */ + GRUB_PIT_CTRL_RATE_GEN = 0x04, + + /* Square wave generator. Output is low for one half of the period, + and high for the other half. */ + GRUB_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. */ + GRUB_PIT_CTRL_SOFTSTROBE = 0x08, + + /* Hardware triggered strobe. Like software triggered strobe, but + only starts the counter when the gate pin is set to high. */ + GRUB_PIT_CTRL_HARDSTROBE = 0x0a, + + + /* Count mode. */ + GRUB_PIT_CTRL_COUNT_MASK = 0x01, + GRUB_PIT_CTRL_COUNT_BINARY = 0x00, /* 16-bit binary counter. */ + GRUB_PIT_CTRL_COUNT_BCD = 0x01 /* 4-decade BCD counter. */ + }; + void EXPORT_FUNC(grub_pit_wait) (grub_uint16_t tics); #endif /* ! KERNEL_CPU_PIT_HEADER */ diff --git a/include/grub/speaker.h b/include/grub/speaker.h new file mode 100644 index 000000000..a076fcfb7 --- /dev/null +++ b/include/grub/speaker.h @@ -0,0 +1,47 @@ +#ifndef GRUB_SPEAKER_HEADER +#define GRUB_SPEAKER_HEADER 1 + +#include +#include + +/* The frequency of the PIT clock. */ +#define GRUB_SPEAKER_PIT_FREQUENCY 0x1234dd + +static inline void +grub_speaker_beep_off (void) +{ + unsigned char status; + + status = grub_inb (GRUB_PIT_SPEAKER_PORT); + grub_outb (status & ~(GRUB_PIT_SPK_TMR2 | GRUB_PIT_SPK_DATA), + GRUB_PIT_SPEAKER_PORT); +} + +static inline void +grub_speaker_beep_on (grub_uint16_t pitch) +{ + unsigned char status; + unsigned int counter; + + if (pitch < 20) + pitch = 20; + else if (pitch > 20000) + pitch = 20000; + + counter = GRUB_SPEAKER_PIT_FREQUENCY / pitch; + + /* Program timer 2. */ + grub_outb (GRUB_PIT_CTRL_SELECT_2 + | GRUB_PIT_CTRL_READLOAD_WORD + | GRUB_PIT_CTRL_SQUAREWAVE_GEN + | GRUB_PIT_CTRL_COUNT_BINARY, GRUB_PIT_CTRL); + grub_outb (counter & 0xff, GRUB_PIT_COUNTER_2); /* LSB */ + grub_outb ((counter >> 8) & 0xff, GRUB_PIT_COUNTER_2); /* MSB */ + + /* Start speaker. */ + status = grub_inb (GRUB_PIT_SPEAKER_PORT); + grub_outb (status | GRUB_PIT_SPK_TMR2 | GRUB_PIT_SPK_DATA, + GRUB_PIT_SPEAKER_PORT); +} + +#endif diff --git a/util/spkmodem-recv.c b/util/spkmodem-recv.c new file mode 100644 index 000000000..cbec3af0e --- /dev/null +++ b/util/spkmodem-recv.c @@ -0,0 +1,98 @@ +#include +#include +#include + +/* Compilation: gcc -o spkmodem-recv spkmodem-recv */ +/* Usage: parecord --channels=1 --rate=48000 --format=s16le | ./spkmodem-recv */ + +#define RATE 48000 +#define SAMPLES_PER_TRAME 480 +#define AMPLITUDE_THRESHOLD 100000 +#define FREQ_SEP_MIN 15 +#define FREQ_SEP_NOM 20 +#define FREQ_SEP_MAX 25 + +#define FREQ_DATA_MIN 10 +#define FREQ_DATA_THRESHOLD 60 +#define FREQ_DATA_MAX 120 +#define AMPLITUDE_SAMPLES 2 * SAMPLES_PER_TRAME + +#define THRESHOLD 1000 + +#define DEBUG 0 + +static signed short trame[2 * SAMPLES_PER_TRAME]; +static signed short pulse[2 * SAMPLES_PER_TRAME]; +static int ringpos = 0; +static int pos, f1, f2; +static int amplitude = 0; +static int lp = 0; + +static void +read_sample (void) +{ + amplitude -= abs (trame[ringpos]); + f1 -= pulse[ringpos]; + f1 += pulse[(ringpos + SAMPLES_PER_TRAME) % (2 * SAMPLES_PER_TRAME)]; + f2 -= pulse[(ringpos + SAMPLES_PER_TRAME) % (2 * SAMPLES_PER_TRAME)]; + fread (trame + ringpos, 1, sizeof (trame[0]), stdin); + amplitude += abs (trame[ringpos]); + + if (pos ? (trame[ringpos] < -THRESHOLD) + : (trame[ringpos] > +THRESHOLD)) + { + pulse[ringpos] = 1; + pos = !pos; + f2++; + } + else + pulse[ringpos] = 0; + ringpos++; + ringpos %= 2 * SAMPLES_PER_TRAME; + lp++; +} + +int +main () +{ + int bitn = 7; + char c = 0; + int i; + while (!feof (stdin)) + { + if (lp > 20000) + { + fflush (stdout); + bitn = 7; + c = 0; + lp = 0; + } + if (f2 > 12 && f2 < 25 + && f1 > 5 && f1 < 120) + { +#if DEBUG + printf ("%d %d %d @%d\n", f1, f2, FREQ_DATA_THRESHOLD, + ftell (stdin) - sizeof (trame)); +#endif + if (f1 < 60) + c |= (1 << bitn); + bitn--; + if (bitn < 0) + { +#if DEBUG + printf ("<%c, %x>", c, c); +#else + printf ("%c", c); +#endif + bitn = 7; + c = 0; + } + lp = 0; + for (i = 0; i < SAMPLES_PER_TRAME; i++) + read_sample (); + continue; + } + read_sample (); + } + return 0; +}