#define BAUD_RATE 115200 #include "tty.h" #include "config.h" #include "fw.h" #include "rom/crc.h" #include #include #define SOH '\001' #define STX '\002' #define ETX '\003' #define EOT '\004' #define ENQ '\005' #define ACK '\006' #define XON '\021' // DC1 #define WRST '\022' // DC2 - window = 0 #define XOFF '\023' // DC3 #define WGO '\024' // DC4 - window + 256 bytes #define NAK '\025' #define SYN '\026' #define ETB '\027' // Not in upload mode #define CAN '\030' #define EM '\031' // Packet offset too high #define FS '\034' #define GS '\035' #define RS '\036' #define US '\037' #define WGO_CHUNK 256 #define STREAMBUF_SIZE 2048 #define BUF_SLACK 32 static char enq_str[] = "\026\035MAX80 v0\004\r\n"; static const char fwupload_start[] = "\034\001: /// MAX80 FW UPLOAD ~@~ $\r\n\035"; void TTY::reset() { rx.rlen = 0; rx.state = rx_state::normal; } TTY::TTY(Stream &port) { _port = &port; rx_sbuf = NULL; reset(); } TTY::~TTY() { if (rx_sbuf) vStreamBufferDelete(rx_sbuf); } int TTY::rxdata(void *buf, size_t len) { printf("[TTY] rxdata(%zu) ", 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 = xStreamBufferReceive(rx_sbuf, buf, len, portMAX_DELAY); tx_credits_ok += rcv; printf("got %d\n", rcv); return rcv; } int TTY::rxdata(token_t me, void *buf, size_t len) { TTY *tty = (TTY *)me; return tty->rxdata(buf, len); } void TTY::_upload_begin() { printf("[TTY] _upload_begin\n"); if (rx_sbuf) xStreamBufferReset(rx_sbuf); else rx_sbuf = xStreamBufferCreate(STREAMBUF_SIZE, 1); printf("[TTY] rx_sbuf = %p\n", rx_sbuf); if (!rx_sbuf) goto can; port().write(RS); tx_credits_sent = 0; tx_credits_ok = STREAMBUF_SIZE - BUF_SLACK; rx.state = rx_state::hdr; rx.rlen = 0; printf("[TTY] firmware_update_start()\n"); if (firmware_update_start(TTY::rxdata, (token_t)this)) goto can; return; can: port().write(CAN); rx.state = rx_state::normal; return; } void TTY::_onerr() { if (rx.state != rx_state::normal) port().write(WRST); } // 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 switch (rx.state) { case rx_state::normal: if (byte == fwupload_start[rx.rlen]) { if (!fwupload_start[++rx.rlen]) { _upload_begin(); byte = -1; } } else { rx.rlen = 0; switch (byte) { case ENQ: port().write(enq_str); byte = ETB; break; case ETX: case EOT: case CAN: byte = ETB; // Not in file upload state break; default: // Normal echo break; } } break; case rx_state::hdr: if (rx.rlen > 0 || byte == STX) { rx.hdr_raw[rx.rlen++] = byte; byte = -1; if (rx.rlen == sizeof rx.hdr) { // Start of data packet 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; 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; } else { 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; } rx.state = rx_state::hdr; rx.rlen = 0; } break; } if (byte >= 0) port().write(byte); } } void TTY::_onconnect() { port().write(XON); } void TTY::_onbreak() { reset(); } void TTY::_ondisconnect() { reset(); } static TTY *uart_tty, *usb_tty; void TTY::usb_onevent(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { switch (event_id) { case ARDUINO_USB_CDC_CONNECTED_EVENT: case ARDUINO_USB_CDC_LINE_STATE_EVENT: usb_tty->_onconnect(); break; case ARDUINO_USB_CDC_DISCONNECTED_EVENT: usb_tty->_ondisconnect(); break; case ARDUINO_USB_CDC_RX_EVENT: usb_tty->_onrx(); break; case ARDUINO_USB_CDC_RX_OVERFLOW_EVENT: usb_tty->_onerr(); break; default: // Do nothing break; } } void TTY::uart_onrx(void) { uart_tty->_onrx(); } void TTY::uart_onerr(hardwareSerial_error_t err) { switch (err) { case UART_BREAK_ERROR: uart_tty->_onbreak(); break; default: uart_tty->_onerr(); break; } } void TTY::init() { enq_str[sizeof(enq_str)-5] += max80_board_version; uart_tty = new TTY(Serial0); Serial0.begin(BAUD_RATE); Serial0.onReceive(uart_onrx, false); Serial0.onReceiveError(uart_onerr); usb_tty = new TTY(Serial); Serial.onEvent(usb_onevent); } void TTY::ping() { }