|
@@ -31,7 +31,7 @@
|
|
|
|
|
|
#define WGO_CHUNK 256
|
|
|
#define STREAMBUF_SIZE 2048
|
|
|
-#define BUF_SLACK 32
|
|
|
+#define BUF_SLACK (STREAMBUF_SIZE >> 1)
|
|
|
|
|
|
static char enq_str[] = "\026\035MAX80 v0\004\r\n";
|
|
|
static const char fwupload_start[] =
|
|
@@ -39,15 +39,16 @@ static const char fwupload_start[] =
|
|
|
|
|
|
void TTY::reset()
|
|
|
{
|
|
|
- rx.rlen = 0;
|
|
|
- rx.state = rx_state::normal;
|
|
|
+ rx.rlen = 0;
|
|
|
+ rx.state = rx_state::normal;
|
|
|
+ rx.b64_bits = 0;
|
|
|
}
|
|
|
|
|
|
|
|
|
TTY::TTY(Stream &port)
|
|
|
{
|
|
|
_port = &port;
|
|
|
- rx_sbuf = NULL;
|
|
|
+ rx_sbuf = true ? xStreamBufferCreate(STREAMBUF_SIZE, 1) : NULL;
|
|
|
reset();
|
|
|
}
|
|
|
|
|
@@ -64,13 +65,30 @@ int TTY::rxdata(void *buf, size_t len)
|
|
|
if (!rx_sbuf)
|
|
|
return 0;
|
|
|
|
|
|
- while (tx_credits_ok - tx_credits_sent >= WGO_CHUNK &&
|
|
|
- port().write(WGO)) {
|
|
|
- tx_credits_sent += WGO_CHUNK;
|
|
|
- }
|
|
|
+ int rcv = 0;
|
|
|
+ while (!rcv) {
|
|
|
+ if (tx_credits_reset) {
|
|
|
+ // Drain input before WRST
|
|
|
+ rcv = xStreamBufferReceive(rx_sbuf, buf, len, 0);
|
|
|
+
|
|
|
+ if (rcv)
|
|
|
+ break;
|
|
|
|
|
|
- int rcv = xStreamBufferReceive(rx_sbuf, buf, len, portMAX_DELAY);
|
|
|
- tx_credits_ok += rcv;
|
|
|
+ if (port().write(WRST)) {
|
|
|
+ tx_credits_reset = false;
|
|
|
+ tx_credits = STREAMBUF_SIZE - BUF_SLACK;
|
|
|
+ } else {
|
|
|
+ // Uhm... wait one tick and then try again to sent WRST?
|
|
|
+ rcv = xStreamBufferReceive(rx_sbuf, buf, len, 1);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ while (tx_credits >= WGO_CHUNK && port().write(WGO))
|
|
|
+ tx_credits -= WGO_CHUNK;
|
|
|
+
|
|
|
+ rcv = xStreamBufferReceive(rx_sbuf, buf, len, 1);
|
|
|
+ tx_credits += rcv;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
printf("got %d\n", rcv);
|
|
|
return rcv;
|
|
@@ -97,13 +115,14 @@ void TTY::_upload_begin()
|
|
|
goto can;
|
|
|
|
|
|
port().write(RS);
|
|
|
- tx_credits_sent = 0;
|
|
|
- tx_credits_ok = STREAMBUF_SIZE - BUF_SLACK;
|
|
|
- rx.state = rx_state::hdr;
|
|
|
+ tx_credits_reset = true;
|
|
|
+ rx.state = rx_state::stxwait;
|
|
|
+ rx.last_ack = 0;
|
|
|
rx.rlen = 0;
|
|
|
+ rx.b64_bits = 0;
|
|
|
|
|
|
printf("[TTY] firmware_update_start()\n");
|
|
|
- if (firmware_update_start(TTY::rxdata, (token_t)this))
|
|
|
+ if (firmware_update_start(TTY::rxdata, (token_t)this, true))
|
|
|
goto can;
|
|
|
|
|
|
return;
|
|
@@ -116,24 +135,54 @@ can:
|
|
|
|
|
|
void TTY::_onerr()
|
|
|
{
|
|
|
- if (rx.state != rx_state::normal)
|
|
|
- port().write(WRST);
|
|
|
+ if (rx.state != rx_state::normal) {
|
|
|
+ port().write(NAK);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int filter_echo(int byte)
|
|
|
+{
|
|
|
+ if (byte >= ' ' && byte < 127)
|
|
|
+ return byte;
|
|
|
+ if (byte == '\t' || byte == '\r' || byte == '\n')
|
|
|
+ return byte;
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+// Decode a base64 data byte (using simple offset-63)
|
|
|
+// Call with -1 or any invalid value to invalidate the input buffer
|
|
|
+int TTY::_decode_data(int input)
|
|
|
+{
|
|
|
+ unsigned int buf = rx.b64_buf;
|
|
|
+ unsigned int inval = input - '?';
|
|
|
+
|
|
|
+ if (inval > 63) {
|
|
|
+ rx.b64_bits = 0;
|
|
|
+ return -2; // Invalid input
|
|
|
+ }
|
|
|
+
|
|
|
+ rx.b64_buf = buf = (buf << 6) + inval;
|
|
|
+ rx.b64_bits += 6;
|
|
|
+
|
|
|
+ if (rx.b64_bits >= 8) {
|
|
|
+ rx.b64_bits -= 8;
|
|
|
+ return (uint8_t)(buf >> rx.b64_bits);
|
|
|
+ } else {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// Change this to be a buffer...
|
|
|
void TTY::_onrx()
|
|
|
{
|
|
|
- int byte;
|
|
|
- int len;
|
|
|
-
|
|
|
- while (1) {
|
|
|
- int byte = port().read();
|
|
|
- if (byte == -1)
|
|
|
- break; // No more data
|
|
|
+ int byte, data;
|
|
|
|
|
|
+ while ((byte = port().read()) >= 0) {
|
|
|
switch (rx.state) {
|
|
|
case rx_state::normal:
|
|
|
- if (byte == fwupload_start[rx.rlen]) {
|
|
|
+ if (rx.rlen < sizeof fwupload_start &&
|
|
|
+ byte == fwupload_start[rx.rlen]) {
|
|
|
if (!fwupload_start[++rx.rlen]) {
|
|
|
_upload_begin();
|
|
|
byte = -1;
|
|
@@ -151,69 +200,137 @@ void TTY::_onrx()
|
|
|
byte = ETB; // Not in file upload state
|
|
|
break;
|
|
|
default:
|
|
|
- // Normal echo
|
|
|
+ // Echo if printable
|
|
|
+ byte = filter_echo(byte);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
- case rx_state::hdr:
|
|
|
- if (rx.rlen > 0 || byte == STX) {
|
|
|
+ case rx_state::stxwait:
|
|
|
+ switch (byte) {
|
|
|
+ case STX:
|
|
|
+ rx.rlen = 0;
|
|
|
rx.hdr_raw[rx.rlen++] = byte;
|
|
|
+ rx.b64_bits = 0;
|
|
|
+ rx.state = rx_state::hdr;
|
|
|
+ byte = -1;
|
|
|
+ break;
|
|
|
+ case ETX:
|
|
|
+ case CAN:
|
|
|
+ printf("[UPLD] Received <%02X> waiting for STX\n", byte);
|
|
|
+ reset();
|
|
|
+ byte = CAN;
|
|
|
+ break;
|
|
|
+ case ENQ:
|
|
|
+ rx.rlen = 0;
|
|
|
+ byte = GS; // In upload wait for STX state
|
|
|
+ break;
|
|
|
+ case SYN:
|
|
|
+ rx.rlen = 0;
|
|
|
+ tx_credits_reset = true; // Request to resync credits
|
|
|
byte = -1;
|
|
|
+ break;
|
|
|
+ case EOT:
|
|
|
+ // Upload complete, no more data
|
|
|
+ byte = ETB;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ byte = -1; // No echo
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case rx_state::hdr:
|
|
|
+ data = _decode_data(byte);
|
|
|
+ byte = -1;
|
|
|
+ if (data < -1) {
|
|
|
+ rx.state = rx_state::stxwait;
|
|
|
+ rx.rlen = 0;
|
|
|
+ tx_credits_reset = true;
|
|
|
+ byte = US; // Framing error
|
|
|
+ } else if (data == -1) {
|
|
|
+ // Nothing to do
|
|
|
+ } else if (rx.rlen >= sizeof rx.hdr_raw) {
|
|
|
+ // ERROR THIS SHOULD NEVER HAPPEN
|
|
|
+ printf("[UPLD] Header buffer overrun!!!\n");
|
|
|
+ reset();
|
|
|
+ byte = CAN;
|
|
|
+ } else {
|
|
|
+ rx.hdr_raw[rx.rlen++] = data;
|
|
|
if (rx.rlen == sizeof rx.hdr) {
|
|
|
// Start of data packet
|
|
|
+ printf("[UPLD] Start packet hdr %d length %d offset %d last_ack %d\n",
|
|
|
+ rx.rlen, rx.hdr.len+1, rx.hdr.offs, rx.last_ack);
|
|
|
rx.state = rx_state::data;
|
|
|
rx.rlen = 0;
|
|
|
}
|
|
|
- } else {
|
|
|
- switch (byte) {
|
|
|
- case ETX:
|
|
|
- case EOT:
|
|
|
- case CAN:
|
|
|
- reset();
|
|
|
- byte = CAN;
|
|
|
- break;
|
|
|
- case ENQ:
|
|
|
- byte = SYN; // In file upload state
|
|
|
- break;
|
|
|
- default:
|
|
|
- // Normal echo
|
|
|
- break;
|
|
|
- }
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case rx_state::data:
|
|
|
- rx_data[rx.rlen++] = byte;
|
|
|
+ data = _decode_data(byte);
|
|
|
byte = -1;
|
|
|
- // rx.hdr.len = packet data len - 1
|
|
|
- if (rx.rlen > rx.hdr.len) {
|
|
|
- int have = rx.rlen;
|
|
|
- uint32_t crc = crc32_le(0, rx_data, have);
|
|
|
-
|
|
|
- if (crc != rx.hdr.crc) {
|
|
|
- byte = NAK;
|
|
|
- } else if (rx.hdr.offs > rx.last_ack) {
|
|
|
- byte = EM;
|
|
|
- } else {
|
|
|
- int sent = 0;
|
|
|
-
|
|
|
- if (rx.hdr.offs + rx.rlen <= rx.last_ack) {
|
|
|
- have = 0;
|
|
|
+ if (data < -1) {
|
|
|
+ rx.state = rx_state::stxwait;
|
|
|
+ rx.rlen = 0;
|
|
|
+ tx_credits_reset = true;
|
|
|
+ byte = US; // Framing error
|
|
|
+ } else if (data == -1) {
|
|
|
+ // Nothing to do
|
|
|
+ } else if (rx.rlen >= sizeof rx_data) {
|
|
|
+ // ERROR THIS SHOULD NEVER HAPPEN
|
|
|
+ printf("[UPLD] Packet data buffer overrun!!!\n");
|
|
|
+ reset();
|
|
|
+ byte = CAN;
|
|
|
+ } else {
|
|
|
+ rx_data[rx.rlen++] = data;
|
|
|
+ // rx.hdr.len = packet data len - 1
|
|
|
+ if (rx.rlen > rx.hdr.len) {
|
|
|
+ int have = rx.rlen;
|
|
|
+ uint32_t crc;
|
|
|
+
|
|
|
+ if (have != rx.hdr.len + 1) {
|
|
|
+ printf("[UPLD] Invalid packet length (should not happen...)\n");
|
|
|
+ byte = NAK;
|
|
|
+ } else if ((crc = crc32_le(0, rx_data, have))
|
|
|
+ != rx.hdr.crc) {
|
|
|
+ printf("[UPLD] Packet CRC error hdr %08x data %08x\n", rx.hdr.crc, crc);
|
|
|
+ printf("\"");
|
|
|
+ for (int i = 0; i < have; i++)
|
|
|
+ printf("\\x%02x", rx_data[i]);
|
|
|
+ printf("\"\n");
|
|
|
+ byte = NAK;
|
|
|
+ } else if (rx.hdr.offs > rx.last_ack) {
|
|
|
+ printf("[UPLD] Invalid packet offsets [%d..%d) at %d\n",
|
|
|
+ rx.hdr.offs, rx.hdr.offs + have, rx.last_ack);
|
|
|
+ byte = EM;
|
|
|
+ } else if (rx.hdr.offs + have <= rx.last_ack) {
|
|
|
+ // Ack and discard packet below current window (transmitter is catching up)
|
|
|
+ byte = ACK;
|
|
|
} else {
|
|
|
+ int sent = 0;
|
|
|
+
|
|
|
uint32_t skip = rx.last_ack - rx.hdr.offs;
|
|
|
have -= skip;
|
|
|
sent = xStreamBufferSend(rx_sbuf, rx_data+skip, have, 0);
|
|
|
rx.last_ack += sent;
|
|
|
- }
|
|
|
|
|
|
- byte = (sent == have) ? ACK : WRST;
|
|
|
+ if (sent != have) {
|
|
|
+ printf("[UPLD] Packet underrun, got %d, expected %d\n", sent, have);
|
|
|
+ byte = NAK;
|
|
|
+ } else {
|
|
|
+ printf("[UPLD] %d bytes received OK\n", sent);
|
|
|
+ byte = ACK;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (byte != ACK)
|
|
|
+ tx_credits_reset = true;
|
|
|
+ rx.state = rx_state::stxwait;
|
|
|
+ rx.rlen = 0;
|
|
|
}
|
|
|
-
|
|
|
- rx.state = rx_state::hdr;
|
|
|
- rx.rlen = 0;
|
|
|
}
|
|
|
+
|
|
|
break;
|
|
|
}
|
|
|
|