summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--util/nvmutil/nvmutil.c126
-rw-r--r--util/spkmodem_decode/spkmodem-decode.c355
2 files changed, 274 insertions, 207 deletions
diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c
index b718ac6e..da6336aa 100644
--- a/util/nvmutil/nvmutil.c
+++ b/util/nvmutil/nvmutil.c
@@ -47,9 +47,6 @@
* 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)
@@ -85,10 +82,6 @@
* 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.
@@ -441,10 +434,10 @@ static const char *argv0;
#define ARGC_4 4
enum {
- LESEN,
- PLESEN,
- SCHREIB,
- PSCHREIB
+ IO_READ,
+ IO_WRITE,
+ IO_PREAD,
+ IO_PWRITE
};
/*
@@ -567,6 +560,10 @@ 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];
+typedef char assert_read[(IO_READ==0)?1:-1];
+typedef char assert_write[(IO_WRITE==1)?1:-1];
+typedef char assert_pread[(IO_PREAD==2)?1:-1];
+typedef char assert_pwrite[(IO_PWRITE==3)?1:-1];
static int use_prng = 0;
@@ -736,9 +733,6 @@ sanitize_command_index(size_t c)
if (command[c].flags != O_RDONLY &&
command[c].flags != O_RDWR)
err(EINVAL, "invalid cmd.flags setting");
-
- if (!((!LESEN) && (PLESEN == 1) && (SCHREIB == 2) && (PSCHREIB == 3)))
- err(EINVAL, "rw type integers are the wrong values");
}
static void
@@ -906,7 +900,7 @@ read_gbe_file(void)
for (p = 0; p < 2; p++) {
if (do_read[p])
- rw_gbe_file_part(p, PLESEN, "pread");
+ rw_gbe_file_part(p, IO_PREAD, "pread");
}
}
@@ -1133,20 +1127,12 @@ 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
- */
+ if (use_prng)
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)
+ if (rw_file_exact(urandom_fd, rnum, n, 0, IO_READ) == -1)
err(errno, "Randomisation failed");
}
@@ -1305,7 +1291,7 @@ gbe_cat_buf(uint8_t *b)
while (1) {
rval = rw_file_exact(STDOUT_FILENO, b,
- GBE_PART_SIZE, 0, SCHREIB);
+ GBE_PART_SIZE, 0, IO_WRITE);
if (rval >= 0) {
/*
@@ -1346,7 +1332,7 @@ write_gbe_file(void)
if (update_checksum)
set_checksum(partnum);
- rw_gbe_file_part(partnum, PSCHREIB, "pwrite");
+ rw_gbe_file_part(partnum, IO_PWRITE, "pwrite");
}
}
@@ -1470,7 +1456,7 @@ rw_gbe_file_part(size_t p, int rw_type,
uint8_t *mem_offset;
- if (rw_type == SCHREIB || rw_type == PSCHREIB)
+ if (rw_type == IO_WRITE || rw_type == IO_PWRITE)
invert = 0;
/*
@@ -1559,8 +1545,8 @@ gbe_x_offset(size_t p, const char *f_op, const char *d_type,
* 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
+ * a specified offset. So if using IO_PWRITE or
+ * IO_PREAD, 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().
@@ -1572,19 +1558,26 @@ rw_file_exact(int fd, uint8_t *mem, size_t len,
ssize_t rv;
size_t rc;
- if (fd < 0 || !len || len > (size_t)SSIZE_MAX) {
+ if (fd < 0 || !len || len > (size_t)SSIZE_MAX
+ || (unsigned int)rw_type > IO_PWRITE) {
errno = EIO;
return -1;
}
- for (rc = 0, rv = 0; rc < len; rc += (size_t)rv) {
- if ((rv = rw_file_once(fd, mem, len, off, rw_type, rc)) == -1)
+ for (rc = 0, rv = 0; rc < len; ) {
+ if ((rv = rw_file_once(fd, mem, len, off, rw_type, rc)) <= 0)
return -1;
+
+ rc += (size_t)rv;
}
return rc;
}
+/*
+ * May not return all requested bytes (len).
+ * Use rw_file_exact for guaranteed length.
+ */
static ssize_t
rw_file_once(int fd, uint8_t *mem, size_t len,
off_t off, int rw_type, size_t rc)
@@ -1594,61 +1587,46 @@ rw_file_once(int fd, uint8_t *mem, size_t len,
size_t max_retries = 10;
read_again:
+ if ((unsigned int)rw_type > IO_PWRITE)
+ goto err_rw_file_once;
+
rv = do_rw(fd, mem + rc, len - rc, off + rc, rw_type);
- if (rv < 0 && errno == EINTR) {
+ 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;
+ if (rv < 0)
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;
- }
+ if ((size_t)rv > SSIZE_MAX /* theoretical buggy libc */
+ || (size_t)rv > (len - rc))/* don't overflow */
+ goto err_rw_file_once;
+
+ if (rv != 0)
+ return rv;
+
+ if (retries_on_zero++ < max_retries)
+ goto read_again;
- return rv;
+err_rw_file_once:
+ errno = EIO;
+ return -1;
}
static ssize_t
do_rw(int fd, uint8_t *mem,
size_t len, off_t off, int rw_type)
{
- if (rw_type == LESEN || rw_type == PLESEN << 2)
+ if (rw_type == IO_READ)
return read(fd, mem, len);
- if (rw_type == SCHREIB || rw_type == PSCHREIB << 2)
+ if (rw_type == IO_WRITE)
return write(fd, mem, len);
- if (rw_type == PLESEN || rw_type == PSCHREIB)
+ if (rw_type == IO_PREAD || rw_type == IO_PWRITE)
return prw(fd, mem, len, off, rw_type);
- errno = EINVAL;
+ errno = EIO;
return -1;
}
@@ -1668,6 +1646,14 @@ prw(int fd, void *mem, size_t nrw,
off_t off_orig;
ssize_t r;
int saved_errno;
+ int prw_type;
+
+ prw_type = rw_type ^ IO_PREAD;
+
+ if ((unsigned int)prw_type > IO_WRITE) {
+ errno = EIO;
+ return -1;
+ }
if ((off_orig = lseek_eintr(fd, (off_t)0, SEEK_CUR)) == (off_t)-1)
return -1;
@@ -1675,7 +1661,7 @@ prw(int fd, void *mem, size_t nrw,
return -1;
do {
- r = do_rw(fd, mem, nrw, off, rw_type << 2);
+ r = do_rw(fd, mem, nrw, off, prw_type);
} while (r < 0 && errno == EINTR);
saved_errno = errno;
diff --git a/util/spkmodem_decode/spkmodem-decode.c b/util/spkmodem_decode/spkmodem-decode.c
index 0c98faf6..3b3b33f8 100644
--- a/util/spkmodem_decode/spkmodem-decode.c
+++ b/util/spkmodem_decode/spkmodem-decode.c
@@ -63,6 +63,7 @@
#endif
#include <errno.h>
+#include <limits.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
@@ -143,7 +144,7 @@
* Convert tone frequency ranges into pulse counts within the
* sliding analysis window.
*
- * pulse_count ≈ tone_frequency / FRAME_RATE
+ * pulse_count = tone_frequency / FRAME_RATE
* where FRAME_RATE = SAMPLE_RATE / SAMPLES_PER_FRAME.
*/
#define FREQ_SEP_MIN ((SEP_TONE_MIN_HZ) / (FRAME_RATE))
@@ -205,34 +206,69 @@ struct decoder_state {
int freq_max;
int freq_threshold;
int learn_frames;
+
+ /* previous sample used for edge detection */
+ signed short prev_sample;
};
static const char *argv0;
+/*
+ * 16-bit little endian words are read
+ * continuously. we will swap them, if
+ * the host cpu is big endian.
+ */
static int host_is_big_endian(void);
+
+/* main loop */
static void handle_audio(struct decoder_state *st);
+
+/* separate tone tolerances */
+static void select_separator_tone(struct decoder_state *st);
+static int is_valid_signal(struct decoder_state *st);
+
+/* output to terminal */
+static int set_ascii_bit(struct decoder_state *st);
+static void print_char(struct decoder_state *st);
+static void reset_char(struct decoder_state *st);
+
+/* process samples/frames */
+static void decode_pulse(struct decoder_state *st);
+static signed short read_sample(struct decoder_state *st);
+static void read_words(struct decoder_state *st);
+
+/* continually adjust tone */
static void detect_tone(struct decoder_state *st);
static int silent_signal(struct decoder_state *st);
static void select_low_tone(struct decoder_state *st);
-static void collect_separator_tone(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);
+
+/* debug */
static void print_stats(struct decoder_state *st);
-static void reset_char(struct decoder_state *st);
+/* error handling / usage */
static void err(int errval, const char *msg, ...);
static void usage(void);
static const char *progname(void);
+/* portability (old systems) */
int getopt(int, char * const *, const char *);
extern char *optarg;
extern int optind;
extern int opterr;
extern int optopt;
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+typedef char static_assert_char_is_8_bits[(CHAR_BIT == 8) ? 1 : -1];
+typedef char static_assert_char_is_1[(sizeof(char) == 1) ? 1 : -1];
+typedef char static_assert_short[(sizeof(short) == 2) ? 1 : -1];
+typedef char static_assert_int_is_4[(sizeof(int) >= 4) ? 1 : -1];
+typedef char static_assert_twos_complement[
+ ((-1 & 3) == 3) ? 1 : -1
+];
+
int
main(int argc, char **argv)
{
@@ -297,130 +333,115 @@ handle_audio(struct decoder_state *st)
int sample;
/*
- * If the modem signal disappears for several frames,
- * discard the partially assembled character.
+ * If the modem signal disappears for several (read: 3)
+ * frames, discard the partially assembled character.
*/
- if (st->sample_count >= (3 * SAMPLES_PER_FRAME))
+ if (st->sample_count >= (3 * SAMPLES_PER_FRAME) ||
+ st->freq_separator <= 0)
reset_char(st);
- collect_separator_tone(st);
- decode_pulse(st);
-
- if (set_ascii_bit(st) < 0)
- print_char(st);
-
st->sample_count = 0;
+
+ /* process exactly one frame */
for (sample = 0; sample < SAMPLES_PER_FRAME; sample++)
decode_pulse(st);
+ select_separator_tone(st);
+
+ if (set_ascii_bit(st) < 0)
+ print_char(st);
+
/* Detect tone per each frame */
detect_tone(st);
}
/*
- * Automatically detect spkmodem tone
+ * collect separator tone statistics
+ * (and auto-adjust tolerances)
*/
static void
-detect_tone(struct decoder_state *st)
+select_separator_tone(struct decoder_state *st)
{
- if (st->learn_frames >= LEARN_FRAMES)
+ int avg;
+
+ if (!is_valid_signal(st))
return;
- st->learn_frames++;
+ st->sep_sum += st->freq_separator;
+ st->sep_samples++;
- if (silent_signal(st))
+ if (st->sep_samples != 50)
return;
- select_low_tone(st);
+ avg = st->sep_sum / st->sep_samples;
- if (st->learn_frames == LEARN_FRAMES) {
- st->freq_threshold =
- (st->freq_min + st->freq_max) / 2;
+ st->sep_min = avg - SEP_TOLERANCE_PULSES;
+ st->sep_max = avg + SEP_TOLERANCE_PULSES;
- if (st->debug)
- printf("auto threshold: %dHz\n",
- st->freq_threshold * FRAME_RATE);
- }
+ /* reset calibration accumulators */
+ st->sep_sum = 0;
+ st->sep_samples = 0;
+
+ if (st->debug)
+ printf("separator calibrated: %dHz\n",
+ avg * FRAME_RATE);
}
/*
- * Ignore silence / near silence.
- * Both FIR windows will be near zero when no signal exists.
+ * Verify that the observed pulse densities fall within the
+ * expected ranges for spkmodem tones. This prevents random noise
+ * from being misinterpreted as data.
*/
static int
-silent_signal(struct decoder_state *st)
+is_valid_signal(struct decoder_state *st)
{
- return (st->freq_data <= 2 &&
- st->freq_separator <= 2);
+ if (st->freq_data <= 0)
+ return 0;
+
+ if (st->freq_separator < st->sep_min ||
+ st->freq_separator > st->sep_max)
+ return 0;
+
+ return 1;
}
/*
- * Choose the lowest active tone.
- * Separator frames carry tone in the separator window,
- * data frames carry tone in the data window.
+ * Each validated frame contributes one bit of modem data.
+ * Bits are accumulated MSB-first into the ASCII byte.
*/
-static void
-select_low_tone(struct decoder_state *st)
+static int
+set_ascii_bit(struct decoder_state *st)
{
- int f;
-
- f = st->freq_data;
+ if (st->debug)
+ print_stats(st);
- if (f <= 0 || (st->freq_separator > 0 &&
- st->freq_separator < f))
- f = st->freq_separator;
+ if (!is_valid_signal(st))
+ return st->ascii_bit;
- if (f <= 0)
- return;
+ if (st->freq_data < st->freq_threshold)
+ st->ascii |= (1 << st->ascii_bit);
- if (f < st->freq_min)
- st->freq_min = f;
+ st->ascii_bit--;
- if (f > st->freq_max)
- st->freq_max = f;
+ return st->ascii_bit;
}
-/*
- * collect separator tone statistics
- * (and auto-adjust tolerances)
- */
static void
-collect_separator_tone(struct decoder_state *st)
+print_char(struct decoder_state *st)
{
- int avg;
-
- if (valid_signal(st))
- return;
-
- if (st->sep_samples >= 50 && st->freq_separator <= 0)
- return;
-
- st->sep_sum += st->freq_separator;
- st->sep_samples++;
-
- if (st->sep_samples != 50)
- return;
-
- avg = st->sep_sum / st->sep_samples;
-
- st->sep_min = avg - SEP_TOLERANCE_PULSES;
- st->sep_max = avg + SEP_TOLERANCE_PULSES;
-
if (st->debug)
- printf("separator calibrated: %dHz\n",
- avg * FRAME_RATE);
+ printf("<%c,%x>", st->ascii, st->ascii);
+ else
+ putchar(st->ascii);
+
+ reset_char(st);
}
-/*
- * Verify that the observed pulse densities fall within the
- * expected ranges for spkmodem tones. This prevents random noise
- * from being misinterpreted as data.
- */
-static int
-valid_signal(struct decoder_state *st)
+static void
+reset_char(struct decoder_state *st)
{
- return (st->freq_separator > 0 &&
- st->freq_data > 0);
+ st->ascii = 0;
+ st->ascii_bit = 7;
}
/*
@@ -431,9 +452,11 @@ decode_pulse(struct decoder_state *st)
{
unsigned char old_ring, old_sep;
unsigned char new_pulse;
+ signed short sample;
int ringpos;
int sep_pos;
- signed short sample;
+ int diff_edge;
+ int diff_amp;
ringpos = st->ringpos;
sep_pos = st->sep_pos;
@@ -469,18 +492,33 @@ decode_pulse(struct decoder_state *st)
sample = read_sample(st);
/*
- * Convert the waveform sample into a pulse (0 or 1).
+ * Avoid startup edge. Since
+ * it's zero at startup, this
+ * may wrongly produce a pulse
+ */
+ if (st->sample_count == 0)
+ st->prev_sample = sample;
+
+ /*
+ * Detect edges instead of amplitude.
+ * This is more tolerant of weak microphones
+ * and speaker distortion..
*
- * The unsigned comparison creates a small dead zone near zero,
- * suppressing small amplitude noise from microphones or
- * cheap ADCs. Real PC speaker tones are far outside this
- * range, so they still produce clean pulses.
+ * However, we check both slope edges and
+ * amplitude, to mitagate noise.
*/
- if ((unsigned)(sample + THRESHOLD)
- > (unsigned)(2 * THRESHOLD))
+ diff_amp = sample;
+ diff_edge = sample - st->prev_sample;
+ if (diff_edge < 0)
+ diff_edge = -diff_edge;
+ if (diff_amp < 0)
+ diff_amp = -diff_amp;
+ if (diff_edge > THRESHOLD &&
+ diff_amp > THRESHOLD)
new_pulse = 1;
else
new_pulse = 0;
+ st->prev_sample = sample;
st->pulse[ringpos] = new_pulse;
st->freq_separator += new_pulse;
@@ -490,11 +528,9 @@ decode_pulse(struct decoder_state *st)
* The separator window always stays one frame ahead
* of the data window.
*/
- ringpos++;
- if (ringpos >= MAX_SAMPLES)
+ if (++ringpos >= MAX_SAMPLES)
ringpos = 0;
- sep_pos++;
- if (sep_pos >= MAX_SAMPLES)
+ if (++sep_pos >= MAX_SAMPLES)
sep_pos = 0;
st->ringpos = ringpos;
@@ -506,25 +542,11 @@ decode_pulse(struct decoder_state *st)
static signed short
read_sample(struct decoder_state *st)
{
- size_t n;
signed short sample;
unsigned short u;
- while (st->inpos >= st->inlen) {
-
- n = fread(st->inbuf, sizeof(st->inbuf[0]),
- READ_BUF, stdin);
-
- if (n == 0) {
- if (ferror(stdin))
- err(errno, "stdin read");
- if (feof(stdin))
- exit(EXIT_SUCCESS);
- }
-
- st->inpos = 0;
- st->inlen = n;
- }
+ while (st->inpos >= st->inlen)
+ read_words(st);
sample = st->inbuf[st->inpos++];
@@ -538,31 +560,97 @@ read_sample(struct decoder_state *st)
return sample;
}
+static void
+read_words(struct decoder_state *st)
+{
+ size_t n;
+
+ n = fread(st->inbuf, sizeof(st->inbuf[0]),
+ READ_BUF, stdin);
+
+ if (n != 0) {
+ st->inpos = 0;
+ st->inlen = n;
+
+ return;
+ }
+
+ if (ferror(stdin))
+ err(errno, "stdin read");
+ if (feof(stdin))
+ exit(EXIT_SUCCESS);
+}
+
/*
- * Each validated frame contributes one bit of modem data.
- * Bits are accumulated MSB-first into the ASCII byte.
+ * Automatically detect spkmodem tone
*/
-static int
-set_ascii_bit(struct decoder_state *st)
+static void
+detect_tone(struct decoder_state *st)
{
+ if (st->learn_frames >= LEARN_FRAMES)
+ return;
+
+ st->learn_frames++;
+
+ if (silent_signal(st))
+ return;
+
+ select_low_tone(st);
+
+ if (st->learn_frames != LEARN_FRAMES)
+ return;
+
+ /*
+ * If the observed frequencies are too close,
+ * learning likely failed (only one tone seen).
+ * Keep the default threshold.
+ */
+ if (st->freq_max - st->freq_min < 2)
+ return;
+
+ st->freq_threshold =
+ (st->freq_min + st->freq_max) / 2;
+
if (st->debug)
- print_stats(st);
- if (st->freq_data < st->freq_threshold)
- st->ascii |= (1 << st->ascii_bit);
+ printf("auto threshold: %dHz\n",
+ st->freq_threshold * FRAME_RATE);
+}
- st->ascii_bit--;
- return st->ascii_bit;
+/*
+ * Ignore silence / near silence.
+ * Both FIR windows will be near zero when no signal exists.
+ */
+static int
+silent_signal(struct decoder_state *st)
+{
+ return (st->freq_data <= 2 &&
+ st->freq_separator <= 2);
}
+/*
+ * Choose the lowest active tone.
+ * Separator frames carry tone in the separator window,
+ * data frames carry tone in the data window.
+ */
static void
-print_char(struct decoder_state *st)
+select_low_tone(struct decoder_state *st)
{
- if (st->debug)
- printf("<%c,%x>", st->ascii, st->ascii);
- else
- putchar(st->ascii);
+ int f;
- reset_char(st);
+ f = st->freq_data;
+
+ if (f <= 0 || (st->freq_separator > 0 &&
+ st->freq_separator < f))
+ f = st->freq_separator;
+
+ if (f <= 0)
+ return;
+
+ if (f < st->freq_min)
+ st->freq_min = f;
+
+ if (f > st->freq_max)
+ st->freq_max = f;
}
static void
@@ -599,13 +687,6 @@ print_stats(struct decoder_state *st)
}
static void
-reset_char(struct decoder_state *st)
-{
- st->ascii = 0;
- st->ascii_bit = 7;
-}
-
-static void
err(int errval, const char *msg, ...)
{
va_list ap;