sconsole

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 3d15b4afbd0c51b6a7fa63d19e9d253ce9e26bf6
parent 0c863e887026f2e62d052ce7dfd0291aad116ef0
Author: Brian Swetland <swetland@frotz.net>
Date:   Thu,  5 May 2022 16:26:32 -0700

support litex bootloader serial download protocol

- any number of files may be specified with -bfilename@hexaddr
- in response to the serial boot challenge, they will be downloaded in order
- after downloading, the bootloader will be instructed to jump to the *first* item
- rearranged sconsole.c a bit
- pushed apple compatibility (no clue if we still need this) and
  swv decoder stuff into their own files

Diffstat:
Aapple.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msconsole.c | 562++++++++++++++++++++++++++++++++++++-------------------------------------------
Aswv.c | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 467 insertions(+), 308 deletions(-)

diff --git a/apple.c b/apple.c @@ -0,0 +1,66 @@ +// Copyright 2005-2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 + +#include <IOKit/serial/ioss.h> +#define B4000000 4000000 +#define B3500000 3500000 +#define B3000000 3000000 +#define B2500000 2500000 +#define B2000000 2000000 +#define B1500000 1500000 +#define B1152000 1152000 +#define B1000000 1000000 +#define B921600 921600 + +/* Darwin's poll is broken. Implement a replacement using select */ +int select_poll(struct pollfd *fds, int nfds, int timeout) +{ + fd_set rfds; + fd_set wfds; + fd_set efds; + int max_fd = 0; + int retval; + struct timeval tv; + int i; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + + for (i = 0; i < nfds; i++) { + if (fds[i].events == POLLIN) + FD_SET(fds[i].fd, &rfds); + if (fds[i].events == POLLOUT) + FD_SET(fds[i].fd, &wfds); + FD_SET(fds[i].fd, &efds); + + if (fds[i].fd > max_fd) + max_fd = fds[i].fd; + + fds[i].revents = 0; + } + + retval = select(max_fd + 1, &rfds, &wfds, &efds, NULL); + + if (errno == EAGAIN) { + return 0; + } + + if (retval < 0) { + return -1; + } + + for (i = 0; i < nfds; i++ ) { + if (FD_ISSET(fds[i].fd, &rfds)) + fds[i].revents |= POLLIN; + if (FD_ISSET(fds[i].fd, &wfds)) + fds[i].revents |= POLLOUT; + if (FD_ISSET(fds[i].fd, &efds)) + fds[i].revents |= POLLERR; + } + + return 1; +} + +#define poll select_poll + diff --git a/sconsole.c b/sconsole.c @@ -1,38 +1,14 @@ -/* sconsole - cheap serial console (for xterm, etc) - * - * Copyright (c) 2005-2015 - * Brian Swetland. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the authors nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ +// Copyright 2005-2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 +// +// sconsole - cheap serial console (for xterm, etc) #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> +#include <fcntl.h> #include <termios.h> #include <signal.h> @@ -44,23 +20,41 @@ #include <sys/fcntl.h> #include <sys/types.h> +#include "litex/sfl.h" +#include "litex/crc16.c" + #ifdef __APPLE__ -#include <IOKit/serial/ioss.h> -#define B4000000 4000000 -#define B3500000 3500000 -#define B3000000 3000000 -#define B2500000 2500000 -#define B2000000 2000000 -#define B1500000 1500000 -#define B1152000 1152000 -#define B1000000 1000000 -#define B921600 921600 +#include "apple.h" #endif +void *load_file(const char *fn, size_t *_sz) { + int fd; + off_t sz; + void *data = NULL; + fd = open(fn, O_RDONLY); + if (fd < 0) goto fail; + sz = lseek(fd, 0, SEEK_END); + if (sz < 0) goto fail; + if (lseek(fd, 0, SEEK_SET)) goto fail; + if ((data = malloc(sz + 4)) == NULL) goto fail; + if (read(fd, data, sz) != sz) goto fail; + *_sz = sz; + return data; +fail: + if (data) free(data); + if (fd >= 0) close(fd); + return NULL; +} + +typedef struct bootinfo { + struct bootinfo* next; + const char* name; + unsigned addr; +} bootinfo_t; + static struct termios tio_save; -static void stdin_raw_init(void) -{ +static void stdin_raw_init(void) { struct termios tio; if (tcgetattr(0, &tio)) @@ -79,22 +73,19 @@ static void stdin_raw_init(void) tcflush(0, TCIFLUSH); } -static void stdin_raw_restore(void) -{ +static void stdin_raw_restore(void) { tcsetattr(0, TCSANOW, &tio_save); tcflush(0, TCIFLUSH); } -void oops(int x) -{ +void oops(int x) { char *msg = "\n[ killed by signal ]\n"; write(2, msg, strlen(msg)); stdin_raw_restore(); exit(1); } -int text2speed(const char *s) -{ +int text2speed(const char *s) { char *e; unsigned n = strtoul(s, &e, 10); switch (*e) { @@ -128,8 +119,7 @@ int text2speed(const char *s) } } -int openserial(const char *device, int speed) -{ +int openserial(const char *device, int speed) { struct termios tio; int fd; int fl; @@ -193,61 +183,6 @@ static unsigned char valid[256]; #define STATE_PREFIX 1 #define STATE_COMMAND 2 -#ifdef __APPLE__ -/* Darwin's poll is broken. Implement a replacement using select */ -int select_poll(struct pollfd *fds, int nfds, int timeout) -{ - fd_set rfds; - fd_set wfds; - fd_set efds; - int max_fd = 0; - int retval; - struct timeval tv; - int i; - - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_ZERO(&efds); - - for (i = 0; i < nfds; i++) { - if (fds[i].events == POLLIN) - FD_SET(fds[i].fd, &rfds); - if (fds[i].events == POLLOUT) - FD_SET(fds[i].fd, &wfds); - FD_SET(fds[i].fd, &efds); - - if (fds[i].fd > max_fd) - max_fd = fds[i].fd; - - fds[i].revents = 0; - } - - retval = select(max_fd + 1, &rfds, &wfds, &efds, NULL); - - if (errno == EAGAIN) { - return 0; - } - - if (retval < 0) { - return -1; - } - - for (i = 0; i < nfds; i++ ) { - if (FD_ISSET(fds[i].fd, &rfds)) - fds[i].revents |= POLLIN; - if (FD_ISSET(fds[i].fd, &wfds)) - fds[i].revents |= POLLOUT; - if (FD_ISSET(fds[i].fd, &efds)) - fds[i].revents |= POLLERR; - } - - return 1; -} - -#define poll select_poll - -#endif /* __APPLE__ */ - void usage(void) { fprintf(stderr, "usage: sconsole [ <flag> ]* [ <device> [ <speed> ] ]\n" @@ -256,157 +191,208 @@ void usage(void) { " -l log to console.log (or -lsomeotherlogfile)\n" " -c convert NL to CR on transmit\n" " -x display characters in hex\n" + " -b serialboot file at addr (-bfilename@1000)\n" "\n" "default device /dev/ttyUSB and speed 115200\n" ); } -typedef enum { - SWV_SYNC, - SWV_1X1, - SWV_1X2, - SWV_1X4, - SWV_2X2, - SWV_2X4, - SWV_3X4, - SWV_4X4, - SWV_PROTO, - SWV_IDLE, -} swv_state_t; - -typedef struct { - swv_state_t state; - unsigned zcount; - unsigned ccount; - unsigned id; - unsigned val; - unsigned char data[8]; -} swv_t; - -static swv_t swv = { - .state = SWV_SYNC, - .zcount = 0 -}; - -void handle_swv_src(unsigned id, unsigned val, unsigned n) { - printf("SRC %s %02x %08x\n", (id & 0x100) ? "HW" : "SW", id & 0xFF, val); +int sfd = -1; + +int logfd = -1; +int hexmode = 0; +int decode_swv = 0; + +unsigned bootmatch = 0; +bootinfo_t* bootlist = NULL; +bootinfo_t* bootlast = NULL; // end of list + +bootinfo_t* bootitem = NULL; +unsigned char* bootdata; +unsigned char* bootptr; +size_t bootsize = 0; +unsigned bootaddr = 0; +unsigned bootfail = 0; + +struct sfl_frame frame; + +void send_frame(unsigned cmd, unsigned param, void* data, unsigned len) { + frame.cmd = cmd; + if (param == SFL_CMD_ABORT) { + frame.payload_length = 0; + } else { + frame.payload_length = len + 4; + frame.payload[0] = param >> 24; + frame.payload[1] = param >> 16; + frame.payload[2] = param >> 8; + frame.payload[3] = param; + memcpy(frame.payload + 4, data, len); + } + unsigned crc = crc16(&frame.cmd, frame.payload_length + 1); + frame.crc[0] = crc >> 8; + frame.crc[1] = crc; + write(sfd, &frame, frame.payload_length + 4); + bootfail = 0; +} + +void resend_frame(void) { + write(sfd, &frame, frame.payload_length + 4); + bootfail++; } -void handle_swv_proto(unsigned char *data, unsigned len) { - switch (len) { - case 1: - printf("PRO %02x\n", data[0]); - break; - case 2: - printf("PRO %02x %02x\n", data[0], data[1]); - break; - case 3: - printf("PRO %02x %02x %02x\n", - data[0], data[1], data[2]); - break; - case 4: - printf("PRO %02x %02x %02x %02x\n", - data[0], data[1], data[2], data[3]); - break; - case 5: - printf("PRO %02x %02x %02x %02x %02x\n", - data[0], data[1], data[2], data[3], data[4]);; - break; - case 6: - printf("PRO %02x %02x %02x %02x %02x %02x\n", - data[0], data[1], data[2], data[3], - data[4], data[5]); - break; - case 7: - printf("PRO %02x %02x %02x %02x %02x %02x %02x\n", - data[0], data[1], data[2], data[3], - data[4], data[5], data[6]); - break; +int boot_start(bootinfo_t* item); + +int boot_next(int x) { + if ((x > ' ') && (x < 127)) { + fprintf(stderr, "%c", x); + } else { + fprintf(stderr, "."); + } + switch (x) { + case SFL_ACK_SUCCESS: { + if (bootsize == 0) { + free(bootdata); + if (bootitem->next) { + return boot_start(bootitem->next); + } + fprintf(stderr, "\n[ jump to 0x%08x ]\n", bootlist->addr); + send_frame(SFL_CMD_JUMP, bootlist->addr, NULL, 0); + bootitem = NULL; + return -1; + } + unsigned xfer = bootsize > 64 ? 64 : bootsize; + send_frame(SFL_CMD_LOAD, bootaddr, bootptr, xfer); + bootptr += xfer; + bootaddr += xfer; + bootsize -= xfer; + return 250; + } + case -1: // timeout + case SFL_ACK_CRCERROR: + case SFL_ACK_UNKNOWN: + case SFL_ACK_ERROR: + if (bootfail == 10) { + fprintf(stderr, "\n[ serial boot failed ]\n"); + free(bootdata); + bootitem = NULL; + send_frame(SFL_CMD_ABORT, 0, NULL, 0); + return 250; + } + resend_frame(); + } +} + +int boot_start(bootinfo_t* item) { + bootitem = item; + bootdata = load_file(bootitem->name, &bootsize); + bootaddr = bootitem->addr; + if (bootdata == NULL) { + fprintf(stderr, "\n[ could not load '%s' ]\n", bootitem->name); + bootitem = NULL; + send_frame(SFL_CMD_ABORT, 0, NULL, 0); + return -1; + } else { + fprintf(stderr, "\n[ sending '%s' to 0x%08x ]\n", + bootitem->name, bootitem->addr); + bootptr = bootdata; + return boot_next(SFL_ACK_SUCCESS); } } -void handle_swv(swv_t *swv, unsigned x) { - //printf("%02x ", x); fflush(stdout); - // any sequence ending in 00 00 00 00 00 80 is a re-sync - if (x == 0) { - swv->zcount++; +int serial_rx(int x) { + if (bootitem) { + return boot_next(x); + } + if (bootlist) { + // if we can serialboot, listen for magic to start + if (SFL_MAGIC_REQ[bootmatch] == x) { + bootmatch++; + if (bootmatch == SFL_MAGIC_LEN) { + fprintf(stderr, "\n[ serial boot requested ]\n"); + bootmatch = 0; + write(sfd, SFL_MAGIC_ACK, SFL_MAGIC_LEN); + return boot_start(bootlist); + } + } else { + bootmatch = 0; + } + } + if (hexmode) { + char hex[4]; + sprintf(hex, "%02x ", x); + write(1, hex, 3); + if (logfd != -1) { + write(logfd, &x, 1); + } +#if WITH_SWV + } else if (decode_swv) { + handle_swv(&swv, x); +#endif } else { - if ((swv->zcount >= 5) && (x == 0x80)) { - swv->state = SWV_IDLE; - swv->zcount = 0; - printf("SYNC\n"); - return; + unsigned char c = x; + if (!valid[x]) { + c = '.'; + } + if (valid[x] != -1) { + write(1, &c, 1); + if (logfd != -1) { + write(logfd, &c, 1); + } } - swv->zcount = 0; } + return -1; +} + +unsigned char ESC = 27; +static int escape = 0; +int map_nl_to_cr = 0; - switch (swv->state) { - case SWV_IDLE: - if (x & 7) { - // AAAAAHSS source packet - swv->id = (x >> 3) | ((x & 4) << 6); - swv->val = 0; - swv->state = (x & 3); - } else if (x != 0) { - // CXXXXX00 protocol packet - swv->data[0] = x; - if (x & 0x80) { - swv->ccount = 1; - swv->state = SWV_PROTO; +void console_rx(int x) { + switch (escape) { + case 0: + if (x == 27) { + escape = 1; + } else { + if ((x == '\n') && map_nl_to_cr) { + x = '\r'; + write(sfd, &x, 1); } else { - handle_swv_proto(swv->data, 1); + write(sfd, &x, 1); } - } else { - // 00 packets are for sync, ignore } break; - case SWV_PROTO: - swv->data[swv->ccount++] = x; - // protocol packets end at 7 total bytes or a byte with bit7 clear - if ((swv->ccount == 7) || (!(x & 0x80))) { - handle_swv_proto(swv->data, swv->ccount); - swv->state = SWV_IDLE; + case 1: + if (x == 27) { + escape = 2; + fprintf(stderr, "\n[ (b)reak? e(x)it? ]\n"); + } else { + escape = 0; + write(sfd, &ESC, 1); + write(sfd, &x, 1); } break; - case SWV_1X1: - handle_swv_src(swv->id, x, 1); - swv->state = SWV_IDLE; - break; - case SWV_1X2: - swv->val = x; - swv->state = SWV_2X2; - break; - case SWV_2X2: - handle_swv_src(swv->id, swv->val | (x << 8), 2); - swv->state = SWV_IDLE; - break; - case SWV_1X4: - swv->val = x; - swv->state = SWV_2X4; - break; - case SWV_2X4: - swv->val |= (x << 8); - swv->state = SWV_3X4; - break; - case SWV_3X4: - swv->val |= (x << 16); - swv->state = SWV_4X4; - break; - case SWV_4X4: - handle_swv_src(swv->id, swv->val | (x << 24), 4); - swv->state = SWV_IDLE; - break; - case SWV_SYNC: + case 2: + escape = 0; + switch (x) { + case 27: + write(sfd, &x, 1); + break; + case 'b': + fprintf(stderr, "[ break ]\n"); + tcsendbreak(sfd, 0); + break; + case 'x': + fprintf(stderr, "[ exit ]\n"); + stdin_raw_restore(); + exit(0); + default: + fprintf(stderr, "[ huh? ]\n"); break; - default: - // impossible - printf("fatal error, bad state %d\n", swv->state); - exit(1); + } } } -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { struct pollfd fds[2]; int speed = B115200; #ifdef __APPLE__ @@ -415,13 +401,8 @@ int main(int argc, char *argv[]) const char *device = "/dev/ttyUSB0"; #endif const char *logfile = "console.log"; - int fd, n; - int map_nl_to_cr = 0; - int escape = 0; - int logfd = -1; - int hexmode = 0; - unsigned char ESC = 27; - int decode_swv = 0; + int timeout; + int n, r; for (n = ' '; n < 127; n++) valid[n] = 1; @@ -434,6 +415,25 @@ int main(int argc, char *argv[]) while ((argc > 1) && (argv[1][0] == '-')) { switch (argv[1][1]) { + case 'b': { + bootinfo_t *item = malloc(sizeof(bootinfo_t)); + item->name = &argv[1][2]; + item->next = NULL; + char *tmp = strchr(item->name, '@'); + if (tmp) { + item->addr = strtoul(tmp + 1, NULL, 16); + *tmp = 0; + } else { + item->addr = 0x40000000; + } + if (bootlast) { + bootlast->next = item; + } else { + bootlist = item; + } + bootlast = item; + break; + } case 'x': hexmode = 1; break; @@ -442,8 +442,7 @@ int main(int argc, char *argv[]) for (n = 0; n < 256; n++) valid[n] = 1; break; - case 'l': - /* log */ + case 'l': /* log */ if (argv[1][2]) logfile = &argv[1][2]; logfd = open(logfile, O_CREAT | O_WRONLY, 0644); @@ -455,9 +454,11 @@ int main(int argc, char *argv[]) case 'h': usage(); return 0; +#if WITH_SWV case 's': decode_swv = 1; break; +#endif default: fprintf(stderr, "error: unknown option %s\n\n", argv[1]); usage(); @@ -480,8 +481,8 @@ int main(int argc, char *argv[]) argv++; } - fd = openserial(device, speed); - if (fd < 0) { + sfd = openserial(device, speed); + if (sfd < 0) { fprintf(stderr, "error opening '%s': %s\n", device, strerror(errno)); return -1; @@ -493,17 +494,21 @@ int main(int argc, char *argv[]) fds[0].fd = 0; fds[0].events = POLLIN; - fds[1].fd = fd; + fds[1].fd = sfd; fds[1].events = POLLIN; fprintf(stderr, "[ %s ]\n", device); + timeout = -1; for (;;) { - unsigned char x, t; + unsigned char x; - if (poll(fds, 2, -1) < 1) + r = poll(fds, 2, timeout); + if (r < 1 ) { continue; - + } else if (r == 0) { + timeout = serial_rx(-1); + } if (fds[0].revents & (POLLERR | POLLHUP)) { fprintf(stderr, "\n[ stdin port closed ]\n"); break; @@ -513,71 +518,12 @@ int main(int argc, char *argv[]) break; } if ((fds[0].revents & POLLIN) && (read(0, &x, 1) == 1)) { - switch (escape) { - case 0: - if (x == 27) { - escape = 1; - } else { - if ((x == '\n') && map_nl_to_cr) { - x = '\r'; - write(fd, &x, 1); - } else { - write(fd, &x, 1); - } - } - break; - case 1: - if (x == 27) { - escape = 2; - fprintf(stderr, "\n[ (b)reak? e(x)it? ]\n"); - } else { - escape = 0; - write(fd, &ESC, 1); - write(fd, &x, 1); - } - break; - case 2: - escape = 0; - switch (x) { - case 27: - write(fd, &x, 1); - break; - case 'b': - fprintf(stderr, "[ break ]\n"); - tcsendbreak(fd, 0); - break; - case 'x': - fprintf(stderr, "[ exit ]\n"); - goto done; - default: - fprintf(stderr, "[ huh? ]\n"); - break; - } - break; - } + console_rx(x); } - if ((fds[1].revents & POLLIN) && (read(fd, &x, 1) == 1)) { - if (hexmode) { - char hex[4]; - sprintf(hex, "%02x ", x); - write(1, hex, 3); - } else if (decode_swv) { - handle_swv(&swv, x); - } else { - unsigned char c = x; - if (!valid[x]) - c = '.'; - - if (valid[x] != -1) { - write(1, &c, 1); - if (logfd != -1) - write(logfd, &c, 1); - } - } + if ((fds[1].revents & POLLIN) && (read(sfd, &x, 1) == 1)) { + timeout = serial_rx(x); } } -done: - stdin_raw_restore(); return 0; } diff --git a/swv.c b/swv.c @@ -0,0 +1,147 @@ +// Copyright 2005-2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 + +typedef enum { + SWV_SYNC, + SWV_1X1, + SWV_1X2, + SWV_1X4, + SWV_2X2, + SWV_2X4, + SWV_3X4, + SWV_4X4, + SWV_PROTO, + SWV_IDLE, +} swv_state_t; + +typedef struct { + swv_state_t state; + unsigned zcount; + unsigned ccount; + unsigned id; + unsigned val; + unsigned char data[8]; +} swv_t; + +static swv_t swv = { + .state = SWV_SYNC, + .zcount = 0 +}; + +void handle_swv_src(unsigned id, unsigned val, unsigned n) { + printf("SRC %s %02x %08x\n", (id & 0x100) ? "HW" : "SW", id & 0xFF, val); +} + +void handle_swv_proto(unsigned char *data, unsigned len) { + switch (len) { + case 1: + printf("PRO %02x\n", data[0]); + break; + case 2: + printf("PRO %02x %02x\n", data[0], data[1]); + break; + case 3: + printf("PRO %02x %02x %02x\n", + data[0], data[1], data[2]); + break; + case 4: + printf("PRO %02x %02x %02x %02x\n", + data[0], data[1], data[2], data[3]); + break; + case 5: + printf("PRO %02x %02x %02x %02x %02x\n", + data[0], data[1], data[2], data[3], data[4]);; + break; + case 6: + printf("PRO %02x %02x %02x %02x %02x %02x\n", + data[0], data[1], data[2], data[3], + data[4], data[5]); + break; + case 7: + printf("PRO %02x %02x %02x %02x %02x %02x %02x\n", + data[0], data[1], data[2], data[3], + data[4], data[5], data[6]); + break; + } +} + +void handle_swv(swv_t *swv, unsigned x) { + //printf("%02x ", x); fflush(stdout); + // any sequence ending in 00 00 00 00 00 80 is a re-sync + if (x == 0) { + swv->zcount++; + } else { + if ((swv->zcount >= 5) && (x == 0x80)) { + swv->state = SWV_IDLE; + swv->zcount = 0; + printf("SYNC\n"); + return; + } + swv->zcount = 0; + } + + switch (swv->state) { + case SWV_IDLE: + if (x & 7) { + // AAAAAHSS source packet + swv->id = (x >> 3) | ((x & 4) << 6); + swv->val = 0; + swv->state = (x & 3); + } else if (x != 0) { + // CXXXXX00 protocol packet + swv->data[0] = x; + if (x & 0x80) { + swv->ccount = 1; + swv->state = SWV_PROTO; + } else { + handle_swv_proto(swv->data, 1); + } + } else { + // 00 packets are for sync, ignore + } + break; + case SWV_PROTO: + swv->data[swv->ccount++] = x; + // protocol packets end at 7 total bytes or a byte with bit7 clear + if ((swv->ccount == 7) || (!(x & 0x80))) { + handle_swv_proto(swv->data, swv->ccount); + swv->state = SWV_IDLE; + } + break; + case SWV_1X1: + handle_swv_src(swv->id, x, 1); + swv->state = SWV_IDLE; + break; + case SWV_1X2: + swv->val = x; + swv->state = SWV_2X2; + break; + case SWV_2X2: + handle_swv_src(swv->id, swv->val | (x << 8), 2); + swv->state = SWV_IDLE; + break; + case SWV_1X4: + swv->val = x; + swv->state = SWV_2X4; + break; + case SWV_2X4: + swv->val |= (x << 8); + swv->state = SWV_3X4; + break; + case SWV_3X4: + swv->val |= (x << 16); + swv->state = SWV_4X4; + break; + case SWV_4X4: + handle_swv_src(swv->id, swv->val | (x << 24), 4); + swv->state = SWV_IDLE; + break; + case SWV_SYNC: + break; + default: + // impossible + printf("fatal error, bad state %d\n", swv->state); + exit(1); + } +} +