summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--util/nvmutil/Makefile12
-rw-r--r--util/nvmutil/nvmutil.c391
-rw-r--r--util/spkmodem_recv/spkmodem-recv.c123
3 files changed, 361 insertions, 165 deletions
diff --git a/util/nvmutil/Makefile b/util/nvmutil/Makefile
index bef6f28c..719e1c1e 100644
--- a/util/nvmutil/Makefile
+++ b/util/nvmutil/Makefile
@@ -1,9 +1,10 @@
# SPDX-License-Identifier: MIT
-# SPDX-FileCopyrightText: 2022,2026 Leah Rowe <leah@libreboot.org>
-# SPDX-FileCopyrightText: 2023 Riku Viitanen <riku.viitanen@protonmail.com>
+# Copyright (c) 2022,2026 Leah Rowe <leah@libreboot.org>
+# Copyright (c) 2023 Riku Viitanen <riku.viitanen@protonmail.com>
CC?=cc
CFLAGS?=-Os -Wall -Wextra -Werror -pedantic -std=c90
+LDFLAGS?=
DESTDIR?=
PREFIX?=/usr/local
INSTALL?=install
@@ -13,11 +14,12 @@ PROG=nvmutil
all: $(PROG)
$(PROG): nvmutil.c
- $(CC) $(CFLAGS) nvmutil.c -o $(PROG)
+ $(CC) $(CFLAGS) $(LDFLAGS) nvmutil.c -o $(PROG)
install: $(PROG)
- mkdir -p $(DESTDIR)$(PREFIX)/bin/
- install $(PROG) $(DESTDIR)$(PREFIX)/bin/
+ $(INSTALL) -d $(DESTDIR)$(PREFIX)/bin
+ $(INSTALL) $(PROG) $(DESTDIR)$(PREFIX)/bin/$(PROG)
+ chmod 755 $(DESTDIR)$(PREFIX)/bin/$(PROG)
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROG)
diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c
index 68e041a3..b718ac6e 100644
--- a/util/nvmutil/nvmutil.c
+++ b/util/nvmutil/nvmutil.c
@@ -12,9 +12,138 @@
*
* Recommended CFLAGS for Clang/GCC:
*
- * -Os -Wall -Wextra -Werror -pedantic -std=c99
+ * -Os -Wall -Wextra -Werror -pedantic -std=c90
*/
+/*
+ * Major TODO: split this into multiple files.
+ * This program has become quite large now, mostly
+ * due to all the extra sanity checks / portability.
+ * Make most of nvmutil a *library* for re-use
+ *
+ * TODO: gettimeofday not posible - use portable functions.
+ * TODO: uint32_t fallback: modify the program instead
+ * to run on 16-bit systems: smaller buffers, and do
+ * operations byte-based instead of word-based.
+ *
+ * TODO: _XOPEN_SOURCE 500 probably not needed anymore.
+ * the portable fallbacks alone are likely enough.
+ * e.g. i don't need stdint, and i don't use pwrite/pread
+ * anymore.
+ *
+ * TODO: version detection of various BSDs to detect
+ * arc4random, use that if available. but also work on
+ * older versions of those BSDs (also MacOS) that lack it.
+ *
+ * TODO: portability/testing on non-Unix systems:
+ * old DOS. all windows versions (probably irrelevant
+ * because you can use cygwin/wsl, whatever), classic MacOS,
+ * also test really old unix e.g. sunos and irix. Be/Haiku too!
+ *
+ * TODO: reliance on global variables for status. make
+ * functions use structs passed as args instead, make
+ * functions re-useable (including libraries), etc.
+ *
+ * TODO: bound checks for files per-command, e.g. only
+ * first 6 bytes for CMD_SETMAC
+ *
+ * TODO: clean up the do_rw function: make PSCHREIB and
+ * so on clearer, probably just define them inline and
+ * validate them inline (no define).
+ * TODO: in command sanitizer: verify that each given
+ * entry corresponds to the correct function, in the
+ * pointer (this check is currently missing)
+ *
+ * TODO: general modularisierung of the entire codebase.
+ * TODO: better explain copy/swap read inversion trick
+ * by improving existing comments
+ * TODO: lots of overwritten comments in code. tidy it up.
+ *
+ * TODO: use getopt for nvmutil args, so that multiple
+ * operations can be performed, and also on many
+ * files at once (noting limitations with cat)
+ * BONUS: implement own getopt(), for portability
+ *
+ * TODO: document fuzzing / static analysis methods
+ * for the code, and:
+ * TODO: implement rigorous unit tests (separate util)
+ * NOTE: this would *include* known good test files
+ * in various configurations, also invalid files.
+ * the tests would likely be portable posix shell
+ * scripts rather than a new C program, but a modularisiert
+ * codebase would allow me to write a separate C
+ * program to test some finer intricacies
+ * TODO: the unit tests would basically test regressions
+ * TODO: after writing back a gbe to file, close() and
+ * open() it again, read it again, and check that
+ * the contents were written correctly, providing
+ * a warning if they were. do this in the main
+ * program.
+ * TODO: the unit tests would include an aggressive set
+ * of fuzz tests, under controlled conditions
+ *
+ * TODO: also document the layout of Intel GbE files, so
+ * that wily individuals can easily expand the
+ * featureset of nvmutil.
+ * TODO: remove some clever code, e.g.:
+ * rw_type == PLESEN << 2
+ * make stuff like that clearer.
+ * ditto the invert copy/swap trick
+ * TODO: write a manpage
+ * TODO: simplify the command sanitization, implement more
+ * of it as build time checks, e.g. static asserts.
+ * generally remove cleverness from the code, instead
+ * prefyerring readibility
+ * TODO: also document nvmutil's coding style, which is
+ * its own style at this point!
+ * TODO: when all the above (and possibly more) is done,
+ * submit this tool to coreboot with a further change
+ * to their build system that lets users modify
+ * GbE images, especially set MAC addresses, when
+ * including GbE files in coreboot configs.
+ */
+/*
+ BONUS TODO:
+ CI/CD. woodpecker is good enough, sourcehut also has one.
+ tie this in with other things mentioned here,
+ e.g. fuzzer / unit tests
+*/
+
+/* Major TODO: reproducible builds
+Test with and without these:
+
+CFLAGS += -fno-record-gcc-switches
+CFLAGS += -ffile-prefix-map=$(PWD)=.
+CFLAGS += -fdebug-prefix-map=$(PWD)=.
+
+I already avoid unique timestamps per-build,
+by not using them, e.g. not reporting build
+time in the program.
+
+When splitting the nvmutil.c file later, do e.g.:
+
+SRC = main.c io.c nvm.c cmd.c
+OBJ = $(SRC:.c=.o)
+
+^ explicitly declare the order in which to build
+*/
+
+/*
+TODO:
+further note when fuzzing is implemented:
+use deterministic randomisation, with a
+guaranteed seed - so e.g. don't use /dev/urandom
+in test builds. e.g. just use normal rand()
+but with a static seed e.g. 1234
+*/
+/*
+TODO: stricter build flags, e.g.
+CFLAGS += -fstack-protector-strong
+CFLAGS += -fno-common
+CFLAGS += -D_FORTIFY_SOURCE=2
+CFLAGS += -fPIE
+*/
+
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 500
#endif
@@ -27,6 +156,7 @@
#include <sys/param.h>
#endif
#include <sys/types.h>
+#include <sys/time.h>
#include <sys/stat.h>
#include <errno.h>
@@ -51,6 +181,7 @@ typedef unsigned int uint32_t;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
typedef char static_assert_char_is_8_bits[(CHAR_BIT == 8) ? 1 : -1];
@@ -161,6 +292,8 @@ static void set_mac_nib(size_t mac_str_pos,
size_t mac_byte_pos, size_t mac_nib_pos);
static uint16_t hextonum(char ch_s);
static uint16_t rhex(void);
+static uint16_t fallback_rand(void);
+static unsigned long entropy_jitter(void);
static void write_mac_part(size_t partnum);
/*
@@ -211,6 +344,8 @@ static off_t gbe_x_offset(size_t part, const char *f_op,
const char *d_type, off_t nsize, off_t ncmp);
static ssize_t rw_file_exact(int fd, uint8_t *mem, size_t len,
off_t off, int rw_type);
+static ssize_t rw_file_once(int fd, uint8_t *mem, size_t len,
+ off_t off, int rw_type, size_t rc);
static ssize_t do_rw(int fd,
uint8_t *mem, size_t len, off_t off, int rw_type);
static ssize_t prw(int fd, void *mem, size_t nrw,
@@ -223,7 +358,6 @@ static off_t lseek_eintr(int fd, off_t off, int whence);
static void err(int nvm_errval, const char *msg, ...);
static void close_files(void);
static const char *getnvmprogname(void);
-static void set_err_if_unset(int errval);
static void usage(uint8_t usage_exit);
/*
@@ -261,7 +395,6 @@ static void usage(uint8_t usage_exit);
#define items(x) (sizeof((x)) / sizeof((x)[0]))
static const char newrandom[] = "/dev/urandom";
-static const char oldrandom[] = "/dev/random"; /* fallback on OLD unix */
static const char *rname = NULL;
/*
@@ -435,6 +568,8 @@ static size_t cmd_index = CMD_NULL;
typedef char assert_argc3[(ARGC_3==3)?1:-1];
typedef char assert_argc4[(ARGC_4==4)?1:-1];
+static int use_prng = 0;
+
int
main(int argc, char *argv[])
{
@@ -448,8 +583,8 @@ main(int argc, char *argv[])
#ifdef NVMUTIL_UNVEIL
if (pledge("stdio rpath wpath unveil", NULL) == -1)
err(errno, "pledge");
- if (unveil("/dev/null", "r") == -1)
- err(errno, "unveil '/dev/null'");
+ if (unveil("/dev/urandom", "r") == -1)
+ err(errno, "unveil /dev/urandom");
#else
if (pledge("stdio rpath wpath", NULL) == -1)
err(errno, "pledge");
@@ -503,20 +638,13 @@ main(int argc, char *argv[])
read_gbe_file();
read_checksums();
- errno = 0;
run_cmd(cmd_index);
- if (errno && (!(part_valid[0] || part_valid[1])))
- err(errno, "%s: Unhandled error (WRITE SKIPPED)", fname);
-
if (command[cmd_index].flags == O_RDWR)
write_gbe_file();
close_files();
- if (errno)
- err(errno, "Unhandled error on exit");
-
return EXIT_SUCCESS;
}
@@ -714,7 +842,7 @@ xstrxcmp(const char *a, const char *b, size_t maxlen)
/*
* Should never reach here. This keeps compilers happy.
*/
- set_err_if_unset(EINVAL);
+ errno = EINVAL;
return -1;
}
@@ -722,26 +850,13 @@ static void
open_dev_urandom(void)
{
rname = newrandom;
- urandom_fd = open(rname, O_RDONLY | O_BINARY | O_NONBLOCK);
+ urandom_fd = open(rname, O_RDONLY);
if (urandom_fd != -1)
return;
- /*
- * Fall back to /dev/random on very old Unix.
- *
- * We must reset errno, to remove stale state
- * set by reading /dev/urandom
- */
-
- fprintf(stderr, "Can't open %s (will use %s instead)\n",
- newrandom, oldrandom);
-
- errno = 0;
-
- rname = oldrandom;
- urandom_fd = open(rname, O_RDONLY | O_BINARY | O_NONBLOCK);
- if (urandom_fd == -1)
- err(errno, "%s: could not open", rname);
+ /* fallback on VERY VERY VERY old unix */
+ use_prng = 1;
+ srand((unsigned)(time(NULL) ^ getpid()));
}
static void
@@ -837,14 +952,11 @@ read_checksums(void)
++num_invalid;
}
- if (num_invalid < max_invalid)
- errno = 0;
-
if (num_invalid >= max_invalid) {
if (max_invalid == 1)
- err(EINVAL, "%s: part %lu has a bad checksum",
+ err(ECANCELED, "%s: part %lu has a bad checksum",
fname, (unsigned long)part);
- err(EINVAL, "%s: No valid checksum found in file",
+ err(ECANCELED, "%s: No valid checksum found in file",
fname);
}
}
@@ -858,7 +970,6 @@ good_checksum(size_t partnum)
if (current_checksum == expected_checksum)
return 1;
- set_err_if_unset(EINVAL);
return 0;
}
@@ -874,7 +985,7 @@ static void
check_command_num(size_t c)
{
if (!valid_command(c))
- err(errno, "Invalid run_cmd arg: %lu",
+ err(EINVAL, "Invalid run_cmd arg: %lu",
(unsigned long)c);
}
@@ -1022,16 +1133,72 @@ rhex(void)
static size_t n = 0;
static uint8_t rnum[12];
+ if (use_prng) {
+ /*
+ * On very old Unix systems that
+ * lack /dev/random and /dev/urandom
+ */
+ return fallback_rand();
+ }
+
+ if (urandom_fd < 0)
+ err(ECANCELED, "Your operating system has no /dev/[u]random");
+
if (!n) {
n = sizeof(rnum);
if (rw_file_exact(urandom_fd, rnum, n, 0, LESEN) == -1)
err(errno, "Randomisation failed");
- errno = 0;
}
return (uint16_t)(rnum[--n] & 0xf);
}
+static uint16_t
+fallback_rand(void)
+{
+ struct timeval tv;
+ unsigned long mix;
+ static unsigned long counter = 0;
+
+ gettimeofday(&tv, NULL);
+
+ mix = (unsigned long)tv.tv_sec
+ ^ (unsigned long)tv.tv_usec
+ ^ (unsigned long)getpid()
+ ^ (unsigned long)&mix
+ ^ counter++
+ ^ entropy_jitter();
+
+ /*
+ * Stack addresses can vary between
+ * calls, thus increasing entropy.
+ */
+ mix ^= (unsigned long)&mix;
+ mix ^= (unsigned long)&tv;
+ mix ^= (unsigned long)&counter;
+
+ return (uint16_t)(mix & 0xf);
+}
+
+static unsigned long
+entropy_jitter(void)
+{
+ struct timeval a, b;
+ unsigned long mix = 0;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ gettimeofday(&a, NULL);
+ getpid();
+ gettimeofday(&b, NULL);
+
+ mix ^= (unsigned long)(b.tv_usec - a.tv_usec);
+ mix ^= (unsigned long)&mix;
+ }
+
+ return mix;
+}
+
static void
write_mac_part(size_t partnum)
{
@@ -1057,9 +1224,6 @@ cmd_helper_dump(void)
part_valid[0] = good_checksum(0);
part_valid[1] = good_checksum(1);
- if (part_valid[0] || part_valid[1])
- errno = 0;
-
for (partnum = 0; partnum < 2; partnum++) {
if (!part_valid[partnum])
fprintf(stderr,
@@ -1079,9 +1243,10 @@ static void
print_mac_from_nvm(size_t partnum)
{
size_t c;
+ uint16_t val16;
for (c = 0; c < 3; c++) {
- uint16_t val16 = nvm_word(c, partnum);
+ val16 = nvm_word(c, partnum);
printf("%02x:%02x", val16 & 0xff, val16 >> 8);
if (c == 2)
printf("\n");
@@ -1155,19 +1320,7 @@ gbe_cat_buf(uint8_t *b)
if (errno != EAGAIN)
err(errno, "stdout: cat");
-
- /*
- * We assume that no data
- * was written to stdout.
- */
- errno = 0;
}
-
- /*
- * No errors here.
- * Avoid the warning in main()
- */
- errno = 0;
}
static void
@@ -1331,8 +1484,6 @@ rw_gbe_file_part(size_t p, int rw_type,
rw_type) == -1)
err(errno, "%s: %s: part %lu",
fname, rw_type_str, (unsigned long)p);
-
- errno = 0;
}
/*
@@ -1402,41 +1553,89 @@ gbe_x_offset(size_t p, const char *f_op, const char *d_type,
* be used on sockets or pipes, because 0-byte
* reads are treated like fatal errors. This
* means that EOF is also considered fatal.
+ *
+ * WARNING: Do not use O_APPEND on open() when
+ * using this function. If you do, POSIX allows
+ * write() to ignore the current file offset and
+ * write at EOF, which means that our use of
+ * lseek in prw() does not guarantee writing at
+ * a specified offset. So if using PSCHREIB or
+ * PLESEN, make sure not to pass a file descriptor
+ * with the O_APPEND flag. Alternatively, modify
+ * do_rw() to directly use pwrite() and pread()
+ * instead of prw().
*/
static ssize_t
rw_file_exact(int fd, uint8_t *mem, size_t len,
off_t off, int rw_type)
{
- ssize_t rval = 0;
- size_t rc = 0;
+ ssize_t rv;
+ size_t rc;
if (fd < 0 || !len || len > (size_t)SSIZE_MAX) {
- set_err_if_unset(EIO);
+ errno = EIO;
return -1;
}
- while (rc < len) {
- rval = do_rw(fd, mem + rc, len - rc, off + rc, rw_type);
-
- if (rval < 0 && errno == EINTR) {
- continue;
- } else if (rval < 0) {
- set_err_if_unset(EIO);
- return -1;
- }
- if ((size_t)rval > (len - rc) /* Prevent overflow */
- || rval == 0) { /* Prevent infinite 0-byte loop */
- set_err_if_unset(EIO);
+ for (rc = 0, rv = 0; rc < len; rc += (size_t)rv) {
+ if ((rv = rw_file_once(fd, mem, len, off, rw_type, rc)) == -1)
return -1;
- }
-
- rc += (size_t)rval;
}
return rc;
}
static ssize_t
+rw_file_once(int fd, uint8_t *mem, size_t len,
+ off_t off, int rw_type, size_t rc)
+{
+ ssize_t rv;
+ size_t retries_on_zero = 0;
+ size_t max_retries = 10;
+
+read_again:
+ rv = do_rw(fd, mem + rc, len - rc, off + rc, rw_type);
+
+ if (rv < 0 && errno == EINTR) {
+ goto read_again;
+ } else if (rv < 0) {
+ errno = EIO;
+ return -1;
+ }
+
+ /*
+ * Theoretical bug: if a buggy libc returned
+ * a size larger than SSIZE_MAX, the cast may
+ * cause an overflow. Specifications guarantee
+ * this won't happen, but spec != implementation
+ */
+ if ((size_t)rv > SSIZE_MAX) {
+ errno = EIO;
+ return -1;
+ /* we will not tolerate your buggy libc */
+ }
+
+ if ((size_t)rv > (len - rc) /* Prevent overflow */
+ || rv == 0) { /* Prevent infinite 0-byte loop */
+ if (rv == 0) {
+ /*
+ * Fault tolerance against infinite
+ * zero-byte loop: re-try a finite
+ * number of times. This mitigates
+ * otherwise OK but slow filesystems
+ * e.g. NFS or slow media.
+ */
+ if (retries_on_zero++ < max_retries)
+ goto read_again;
+ }
+ errno = EIO;
+ return -1;
+ }
+
+ return rv;
+}
+
+static ssize_t
do_rw(int fd, uint8_t *mem,
size_t len, off_t off, int rw_type)
{
@@ -1449,7 +1648,7 @@ do_rw(int fd, uint8_t *mem,
if (rw_type == PLESEN || rw_type == PSCHREIB)
return prw(fd, mem, len, off, rw_type);
- set_err_if_unset(EINVAL);
+ errno = EINVAL;
return -1;
}
@@ -1507,23 +1706,12 @@ err(int nvm_errval, const char *msg, ...)
{
va_list args;
- /*
- * We need to ensure that files are closed
- * on exit, including error exits. This
- * would otherwise recurse, because the
- * close_files() function also calls err(),
- * but with -1 on nvm_errval. It's the only
- * one that does this.
- *
- * Since the errval is for setting errno, -1
- * would be incorrect. Therefore, set_err_if_unset()
- * avoids overriding errno if the given value
- * is negative.
- *
- * Be careful modifying err() and close_files().
- */
- if (nvm_errval != -1)
+ if (nvm_errval >= 0) {
close_files();
+ errno = nvm_errval;
+ }
+ if (errno <= 0)
+ errno = ECANCELED;
fprintf(stderr, "%s: ", getnvmprogname());
@@ -1531,7 +1719,6 @@ err(int nvm_errval, const char *msg, ...)
vfprintf(stderr, msg, args);
va_end(args);
- set_err_if_unset(nvm_errval);
fprintf(stderr, ": %s", strerror(errno));
fprintf(stderr, "\n");
@@ -1570,26 +1757,6 @@ getnvmprogname(void)
return argv0;
}
-/*
- * Set errno only if it hasn't already been set.
- * This prevents overriding real libc errors.
- *
- * We use errno for regular program state, while
- * being careful not to clobber what was set by
- * real libc function, or a minority of our stub
- * functions such as prw()
- */
-static void
-set_err_if_unset(int x)
-{
- if (errno)
- return;
- if (x > 0)
- errno = x;
- else
- errno = ECANCELED;
-}
-
static void
usage(uint8_t usage_exit)
{
diff --git a/util/spkmodem_recv/spkmodem-recv.c b/util/spkmodem_recv/spkmodem-recv.c
index d448c84b..48b06527 100644
--- a/util/spkmodem_recv/spkmodem-recv.c
+++ b/util/spkmodem_recv/spkmodem-recv.c
@@ -1,11 +1,24 @@
-/* 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 (NOTE: little endian!):
+ * 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
@@ -61,10 +74,12 @@ struct decoder_state {
unsigned char ascii;
int debug;
+ int swap_bytes;
};
static const char *argv0;
+static int host_is_big_endian(void);
static void handle_audio(struct decoder_state *st);
static int valid_signal(struct decoder_state *st);
static void decode_pulse(struct decoder_state *st);
@@ -84,10 +99,6 @@ extern int optind;
extern int opterr;
extern int optopt;
-#ifndef errno
-extern int errno;
-#endif
-
int
main(int argc, char **argv)
{
@@ -110,15 +121,15 @@ main(int argc, char **argv)
argv0 = argv[0];
while ((c = getopt(argc, argv, "d")) != -1) {
- switch (c) {
- case 'd':
- st.debug = 1;
- break;
- default:
+ if (c != 'd')
usage();
- }
+ st.debug = 1;
+ break;
}
+ if (host_is_big_endian())
+ st.swap_bytes = 1;
+
setvbuf(stdout, NULL, _IONBF, 0);
for (;;)
@@ -127,6 +138,13 @@ main(int argc, char **argv)
return EXIT_SUCCESS;
}
+static int
+host_is_big_endian(void)
+{
+ unsigned int x = 1;
+ return (*(unsigned char *)&x == 0);
+}
+
static void
handle_audio(struct decoder_state *st)
{
@@ -134,20 +152,17 @@ handle_audio(struct decoder_state *st)
if (st->sample_count > (3 * SAMPLES_PER_FRAME))
reset_char(st);
+ if (!valid_signal(st)) {
+ decode_pulse(st);
+ return;
+ }
- if (valid_signal(st)) {
-
- if (set_ascii_bit(st) < 0)
- print_char(st);
-
- st->sample_count = 0;
-
- for (sample = 0; sample < SAMPLES_PER_FRAME; sample++)
- decode_pulse(st);
+ if (set_ascii_bit(st) < 0)
+ print_char(st);
- } else {
+ st->sample_count = 0;
+ for (sample = 0; sample < SAMPLES_PER_FRAME; sample++)
decode_pulse(st);
- }
}
static int
@@ -164,28 +179,35 @@ decode_pulse(struct decoder_state *st)
{
unsigned char old_ring, old_sep;
unsigned char new_pulse;
+ int ringpos;
+ signed short sample;
- old_ring = st->pulse[st->ringpos];
+ ringpos = st->ringpos;
+
+ old_ring = st->pulse[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);
+ sample = read_sample(st);
+ st->frame[ringpos] = sample;
- if (st->frame[st->ringpos] > THRESHOLD ||
- st->frame[st->ringpos] < -THRESHOLD)
+ if ((unsigned)(sample + THRESHOLD)
+ > (unsigned)(2 * THRESHOLD))
new_pulse = 1;
else
new_pulse = 0;
- st->pulse[st->ringpos] = new_pulse;
+ st->pulse[ringpos] = new_pulse;
st->freq_separator += new_pulse;
- st->ringpos++;
- if (st->ringpos >= MAX_SAMPLES)
- st->ringpos = 0;
+ ringpos++;
+ if (ringpos >= MAX_SAMPLES)
+ ringpos = 0;
+
+ st->ringpos = ringpos;
st->sep_pos++;
if (st->sep_pos >= MAX_SAMPLES)
@@ -198,6 +220,8 @@ static signed short
read_sample(struct decoder_state *st)
{
size_t n;
+ signed short sample;
+ unsigned short u;
while (st->inpos >= st->inlen) {
@@ -205,16 +229,26 @@ read_sample(struct decoder_state *st)
READ_BUF, stdin);
if (n == 0) {
+ if (ferror(stdin))
+ err(errno, "stdin read");
if (feof(stdin))
exit(EXIT_SUCCESS);
- err(errno, "stdin read");
}
st->inpos = 0;
st->inlen = n;
}
- return st->inbuf[st->inpos++];
+ sample = st->inbuf[st->inpos++];
+
+ if (st->swap_bytes) {
+ u = (unsigned short)sample;
+ u = (u >> 8) | (u << 8);
+
+ sample = (signed short)u;
+ }
+
+ return sample;
}
static int
@@ -222,12 +256,10 @@ set_ascii_bit(struct decoder_state *st)
{
if (st->debug)
print_stats(st);
-
if (st->freq_data < FREQ_DATA_THRESHOLD)
st->ascii |= (1 << st->ascii_bit);
st->ascii_bit--;
-
return st->ascii_bit;
}
@@ -247,9 +279,7 @@ print_stats(struct decoder_state *st)
{
long pos;
- pos = ftell(stdin);
-
- if (pos == -1) {
+ if ((pos = ftell(stdin)) == -1) {
printf("%d %d %d\n",
st->freq_data,
st->freq_separator,
@@ -282,10 +312,7 @@ err(int errval, const char *msg, ...)
vfprintf(stderr, msg, ap);
va_end(ap);
- if (!errno)
- errno = errval;
-
- fprintf(stderr, ": %s\n", strerror(errno));
+ fprintf(stderr, ": %s\n", strerror(errval));
exit(EXIT_FAILURE);
}