diff options
| -rw-r--r-- | util/spkmodem_recv/Makefile | 2 | ||||
| -rw-r--r-- | util/spkmodem_recv/spkmodem-recv.c | 325 |
2 files changed, 210 insertions, 117 deletions
diff --git a/util/spkmodem_recv/Makefile b/util/spkmodem_recv/Makefile index 95683df2..293d7475 100644 --- a/util/spkmodem_recv/Makefile +++ b/util/spkmodem_recv/Makefile @@ -17,7 +17,7 @@ $(PROG): spkmodem-recv.c install: $(PROG) mkdir -p $(DESTDIR)$(PREFIX)/bin/ - install $(PROG) $(DESTDIR)$(PREFIX)/bin/ + install -c $(PROG) $(DESTDIR)$(PREFIX)/bin/ uninstall: rm -f $(DESTDIR)$(PREFIX)/bin/$(PROG) diff --git a/util/spkmodem_recv/spkmodem-recv.c b/util/spkmodem_recv/spkmodem-recv.c index 9307ac12..4ff1fb5f 100644 --- a/util/spkmodem_recv/spkmodem-recv.c +++ b/util/spkmodem_recv/spkmodem-recv.c @@ -1,14 +1,31 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* SPDX-FileCopyrightText: 2013 Free Software Foundation, Inc. */ -/* Usage: parec --channels=1 --rate=48000 --format=s16le | ./spkmodem-recv */ - -/* Forked from coreboot's version, at util/spkmodem_recv/ in coreboot.git, - * revision 5c2b5fcf2f9c9259938fd03cfa3ea06b36a007f0 as of 3 January 2022. - * This version is heavily modified, re-written based on OpenBSD Kernel Source - * File Style Guide (KNF); this change is Copyright 2023,2026 Leah Rowe. */ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2013 Free Software Foundation, Inc. + * Copyright (c) 2023, 2026 Leah Rowe <leah@libreboot.org> + * + * This program receives text encoded as pulses on the PC speaker, + * and decodes them. This is a special type of interface provided + * by coreboot and GRUB, for computers that lack serial ports. + * + * Usage example: + * parec --channels=1 --rate=48000 --format=s16le | ./spkmodem-recv + * + * Originally provided by GNU GRUB, this version is a heavily + * modified fork that complies with the OpenBSD Kernel Source + * File Style Guide (KNF) instead of GNU coding standards; it + * emphasises strict error handling, portability and code + * quality, as characterised by OpenBSD projects. + * + * This fork of spkmodem-recv is provided with Libreboot releases: + * https://libreboot.org/ + */ #define _POSIX_SOURCE +/* + * For OpenBSD define, to detect version + * for deciding whether to use pledge(2) + */ #ifdef __OpenBSD__ #include <sys/param.h> #endif @@ -20,180 +37,267 @@ #include <string.h> #include <unistd.h> -static int set_ascii_bit(void); -static void handle_audio(void), decode_pulse(void), print_char(void), - print_stats(void); -static void err(int nvm_errval, const char *msg, ...); -static const char * getnvmprogname(void); -static void set_err_if_unset(int x); - -int getopt(int, char * const *, const char *); -extern char *optarg; -extern int optind, opterr, optopt; - -#if defined(__OpenBSD__) && defined(OpenBSD) -#if OpenBSD >= 509 -#ifndef HAVE_PLEDGE -#define HAVE_PLEDGE 1 -#endif -#endif -#endif - #define SAMPLES_PER_FRAME 240 #define MAX_SAMPLES (2 * SAMPLES_PER_FRAME) + #define FREQ_SEP_MIN 5 #define FREQ_SEP_MAX 15 + #define FREQ_DATA_MIN 15 #define FREQ_DATA_THRESHOLD 25 #define FREQ_DATA_MAX 60 + #define THRESHOLD 500 -#define reset_char() ascii = 0, ascii_bit = 7 +#define READ_BUF 4096 + +struct decoder_state { + signed short frame[MAX_SAMPLES]; + unsigned char pulse[MAX_SAMPLES]; + + signed short inbuf[READ_BUF]; + size_t inpos; + size_t inlen; + + int ringpos; + int sep_pos; + + /* + * Sliding window pulse counters + * used to detect modem tones + */ + int freq_data; + int freq_separator; + int sample_count; + + int ascii_bit; + unsigned char ascii; -signed short frame[MAX_SAMPLES], pulse[MAX_SAMPLES]; -int ringpos, debug, freq_data, freq_separator, sample_count, ascii_bit = 7; -char ascii = 0; + int debug; +}; static const char *argv0; +static void handle_audio(struct decoder_state *st); +static int valid_signal(struct decoder_state *st); +static void decode_pulse(struct decoder_state *st); +static signed short read_sample(struct decoder_state *st); +static int set_ascii_bit(struct decoder_state *st); +static void print_char(struct decoder_state *st); +static void print_stats(struct decoder_state *st); +static void reset_char(struct decoder_state *st); + +static void err(int errval, const char *msg, ...); +static void usage(void); +static const char *progname(void); + +int getopt(int, char * const *, const char *); +extern char *optarg; +extern int optind; +extern int opterr; +extern int optopt; + +#ifndef errno +extern int errno; +#endif + int -main(int argc, char *argv[]) +main(int argc, char **argv) { - int c = 0; - - argv0 = argv[0]; + struct decoder_state st; + int c; -#ifdef HAVE_PLEDGE -#if HAVE_PLEDGE > 0 +#if defined (__OpenBSD__) && defined(OpenBSD) +#if OpenBSD >= 509 if (pledge("stdio", NULL) == -1) err(errno, "pledge"); #endif #endif - while (c != -1) { - c = getopt(argc, argv, "d"); - if (c == 'd') - debug = 1; - else if (c != -1) - err(EINVAL, "Invalid argument"); + memset(&st, 0, sizeof(st)); + st.ascii_bit = 7; + + st.ringpos = 0; + st.sep_pos = SAMPLES_PER_FRAME; + + argv0 = argv[0]; + + while ((c = getopt(argc, argv, "d")) != -1) { + if (c != 'd') + usage(); + st.debug = 1; + break; } setvbuf(stdout, NULL, _IONBF, 0); - while (1) - handle_audio(); + for (;;) + handle_audio(&st); return EXIT_SUCCESS; } static void -handle_audio(void) +handle_audio(struct decoder_state *st) { int sample; - if (sample_count > (3 * SAMPLES_PER_FRAME)) - sample_count = reset_char(); - - if ((freq_separator <= FREQ_SEP_MIN) || (freq_separator >= FREQ_SEP_MAX) - || (freq_data <= FREQ_DATA_MIN) || (freq_data >= FREQ_DATA_MAX)) { - decode_pulse(); + if (st->sample_count > (3 * SAMPLES_PER_FRAME)) + reset_char(st); + if (!valid_signal(st)) { + decode_pulse(st); return; } - if (set_ascii_bit() < 0) - print_char(); - - sample_count = 0; + if (set_ascii_bit(st) < 0) + print_char(st); + st->sample_count = 0; for (sample = 0; sample < SAMPLES_PER_FRAME; sample++) - decode_pulse(); + decode_pulse(st); +} + +static int +valid_signal(struct decoder_state *st) +{ + return (st->freq_separator > FREQ_SEP_MIN && + st->freq_separator < FREQ_SEP_MAX && + st->freq_data > FREQ_DATA_MIN && + st->freq_data < FREQ_DATA_MAX); } static void -decode_pulse(void) +decode_pulse(struct decoder_state *st) +{ + unsigned char old_ring, old_sep; + unsigned char new_pulse; + + old_ring = st->pulse[st->ringpos]; + old_sep = st->pulse[st->sep_pos]; + + st->freq_data -= old_ring; + st->freq_data += old_sep; + st->freq_separator -= old_sep; + + st->frame[st->ringpos] = read_sample(st); + + if ((unsigned)(st->frame[st->ringpos] + THRESHOLD) + > (unsigned)(2 * THRESHOLD)) + new_pulse = 1; + else + new_pulse = 0; + + st->pulse[st->ringpos] = new_pulse; + st->freq_separator += new_pulse; + + st->ringpos++; + if (st->ringpos >= MAX_SAMPLES) + st->ringpos = 0; + + st->sep_pos++; + if (st->sep_pos >= MAX_SAMPLES) + st->sep_pos = 0; + + st->sample_count++; +} + +static signed short +read_sample(struct decoder_state *st) { size_t n; - size_t frame_size; - int next_ringpos = (ringpos + SAMPLES_PER_FRAME) % MAX_SAMPLES; + while (st->inpos >= st->inlen) { - freq_data -= pulse[ringpos]; - freq_data += pulse[next_ringpos]; - freq_separator -= pulse[next_ringpos]; + n = fread(st->inbuf, sizeof(st->inbuf[0]), + READ_BUF, stdin); - frame_size = sizeof(frame[0]); - n = fread(&frame[ringpos], 1, frame_size, stdin); + if (n == 0) { + if (feof(stdin)) + exit(EXIT_SUCCESS); + err(errno, "stdin read"); + } - if (n != frame_size) { - if (feof(stdin)) - exit(EXIT_SUCCESS); - if (ferror(stdin)) - err(errno, "Could not read from frame."); + st->inpos = 0; + st->inlen = n; } - pulse[ringpos] = (abs(frame[ringpos]) > THRESHOLD) ? 1 : 0; - freq_separator += pulse[ringpos++]; - ringpos %= MAX_SAMPLES; - ++sample_count; + return st->inbuf[st->inpos++]; } static int -set_ascii_bit(void) +set_ascii_bit(struct decoder_state *st) { - if (debug) - print_stats(); - - if (freq_data < FREQ_DATA_THRESHOLD) - ascii |= (1 << ascii_bit); + if (st->debug) + print_stats(st); + if (st->freq_data < FREQ_DATA_THRESHOLD) + st->ascii |= (1 << st->ascii_bit); - --ascii_bit; - return ascii_bit; + st->ascii_bit--; + return st->ascii_bit; } static void -print_stats(void) +print_char(struct decoder_state *st) { - long stdin_pos; - - if ((stdin_pos = ftell(stdin)) == -1) - err(errno, "ftell stdin"); + if (st->debug) + printf("<%c,%x>", st->ascii, st->ascii); + else + putchar(st->ascii); - printf ("%d %d %d @%ld\n", freq_data, freq_separator, - FREQ_DATA_THRESHOLD, stdin_pos - sizeof(frame)); + reset_char(st); } static void -print_char(void) +print_stats(struct decoder_state *st) { - if (debug) - printf("<%c, %x>", ascii, ascii); - else - printf("%c", ascii); + long pos; - reset_char(); + if ((pos = ftell(stdin)) == -1) { + printf("%d %d %d\n", + st->freq_data, + st->freq_separator, + FREQ_DATA_THRESHOLD); + return; + } + + printf("%d %d %d @%ld\n", + st->freq_data, + st->freq_separator, + FREQ_DATA_THRESHOLD, + pos - sizeof(st->frame)); } static void -err(int nvm_errval, const char *msg, ...) +reset_char(struct decoder_state *st) { - va_list args; + st->ascii = 0; + st->ascii_bit = 7; +} - fprintf(stderr, "%s: ", getnvmprogname()); +static void +err(int errval, const char *msg, ...) +{ + va_list ap; - va_start(args, msg); - vfprintf(stderr, msg, args); - va_end(args); + fprintf(stderr, "%s: ", progname()); - set_err_if_unset(nvm_errval); - fprintf(stderr, ": %s", strerror(errno)); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); - fprintf(stderr, "\n"); + fprintf(stderr, ": %s\n", strerror(errval)); + exit(EXIT_FAILURE); +} +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-d]\n", progname()); exit(EXIT_FAILURE); } static const char * -getnvmprogname(void) +progname(void) { const char *p; @@ -207,14 +311,3 @@ getnvmprogname(void) else return argv0; } - -static void -set_err_if_unset(int x) -{ - if (errno) - return; - if (x > 0) - errno = x; - else - errno = ECANCELED; -} |
