|  | @@ -0,0 +1,300 @@
 | 
	
		
			
				|  |  | +#define BAUD_RATE 115200
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include "tty.h"
 | 
	
		
			
				|  |  | +#include "config.h"
 | 
	
		
			
				|  |  | +#include "fw.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include "rom/crc.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <USB.h>
 | 
	
		
			
				|  |  | +#include <HardwareSerial.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#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	8192
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static char enq_str[] = "\026\035MAX80 v0\004\r\n";
 | 
	
		
			
				|  |  | +static const char fwupload_start[] =
 | 
	
		
			
				|  |  | +    "\034\001: /// MAX80 FW UPLOAD ~@~ $\035\r\n";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void TTY::reset()
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    rx.state  = rx_state::normal;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +TTY::TTY(Stream &port)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    _port = &port;
 | 
	
		
			
				|  |  | +    rx_sbuf = NULL;
 | 
	
		
			
				|  |  | +    win_sem = xSemaphoreCreateMutexStatic(&win_sem_ss);
 | 
	
		
			
				|  |  | +    reset();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +TTY::~TTY()
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    if (rx_sbuf)
 | 
	
		
			
				|  |  | +	vStreamBufferDelete(rx_sbuf);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void TTY::_update_window(bool rst)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    xSemaphoreTake(win_sem, portMAX_DELAY);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (rst) {
 | 
	
		
			
				|  |  | +	port().write(WRST);
 | 
	
		
			
				|  |  | +	tx_window = 0;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    while (xStreamBufferSpacesAvailable(rx_sbuf) >= tx_window + WGO_CHUNK) {
 | 
	
		
			
				|  |  | +	tx_window += WGO_CHUNK;
 | 
	
		
			
				|  |  | +	port().write(WGO);	// Space for one more window
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    xSemaphoreGive(win_sem);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +int TTY::rxdata(void *buf, size_t len)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    if (!rx_sbuf)
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    size_t rcv = xStreamBufferReceive(rx_sbuf, buf, len, portMAX_DELAY);
 | 
	
		
			
				|  |  | +    _update_window(false);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    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()
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    if (rx_sbuf)
 | 
	
		
			
				|  |  | +	xStreamBufferReset(rx_sbuf);
 | 
	
		
			
				|  |  | +    else
 | 
	
		
			
				|  |  | +	rx_sbuf = xStreamBufferCreate(STREAMBUF_SIZE, 1);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (!rx_sbuf)
 | 
	
		
			
				|  |  | +	goto nak;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (firmware_update_start((read_func_t)TTY::rxdata, (token_t)this))
 | 
	
		
			
				|  |  | +	goto nak;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // Respond with WRST + n*WGO
 | 
	
		
			
				|  |  | +    _update_window(true);
 | 
	
		
			
				|  |  | +    rx.state = rx_state::hdr;
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + nak:
 | 
	
		
			
				|  |  | +    port().write(NAK);
 | 
	
		
			
				|  |  | +    rx.state = rx_state::normal;
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void TTY::_onerr()
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    if (rx.state != rx_state::normal)
 | 
	
		
			
				|  |  | +	_update_window(true);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// 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();
 | 
	
		
			
				|  |  | +	    } 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;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		    tx_window -= rx.rlen;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		    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;
 | 
	
		
			
				|  |  | +		    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		    if (sent == have)
 | 
	
		
			
				|  |  | +			byte = ACK;
 | 
	
		
			
				|  |  | +		    else
 | 
	
		
			
				|  |  | +			_update_window(true);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		    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()
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +}
 |