From 291b55cb4d005dba018d5ef18a9f249301c85f1f Mon Sep 17 00:00:00 2001 From: Christophe Duparquet Date: Wed, 13 Apr 2016 18:42:58 +0200 Subject: [PATCH 1/2] RFC 2217 working with Diabolo bootloader --- .gitignore | 1 + Makefile | 4 + serial/serbridge.c | 521 ++++++++++++++++++++++++++++++++++++++++++++- serial/serbridge.h | 26 +-- 4 files changed, 536 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index e462231..ad8ac78 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,6 @@ esp-link.opensdf esp-link.sdf espfs/mkespfsimage/mman-win32/libmman.a .localhistory/ +Makefile.local tools/ *.tgz diff --git a/Makefile b/Makefile index 78342c1..4826bb9 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,10 @@ # Original from esphttpd and others... #VERBOSE=1 +# Include optionnal local configuration +# +-include Makefile.local + # --------------- toolchain configuration --------------- # Base directory for the compiler. Needs a / at the end. diff --git a/serial/serbridge.c b/serial/serbridge.c index 8d400fd..23e1751 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -1,5 +1,33 @@ // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt +/* Modified by Christophe Duparquet: implementation of RFC 2217 to use the + * Diabolo bootloader through pySerial-3.0. + * + * Verified on ESP-WROOM-02 with the following configuration: + * Reset: gpio5 + * ISP/Flash: disabled + * Conn LED: gpio4 + * Serial LED: disabled + * UART pins: swapped + * RX pull-up: yes + * + * ESP connections: + * GPIO2 (#7) / 3V3 (#1) -> 1kR + * GPIO0 (#8) / 3V3 (#1) -> 1kR + * GPIO0 (#8) = USB serial RTS + * GPIO13 (#5) / GPIO15 (#6) -> 1kR + * GPIO15 (#6) / GND (#9) -> 10kR + * EN (#2) / 3V3 (#1) -> 10 kR + * #18 = #13 = #1 = GND + * RST (#15) -> USB serial RTS + * + * ATtiny85 (3V3) connections: + * RESET (#1) -> ESP pin GPIO5 (#14) + * RXTX (#2) -> ESP pin GPIO13 (#5) + * + * Command: diabolo -t rfc2217://192.168.1.78:23 + */ + #include "esp8266.h" #include "uart.h" @@ -13,6 +41,12 @@ #define SKIP_AT_RESET +#define IROM ICACHE_FLASH_ATTR +#define REG_BRR (*(volatile uint32_t*)0x60000014) +#define REG_CONF0 (*(volatile uint32_t*)0x60000020) +#define REG_CONF1 (*(volatile uint32_t*)0x60000024) + + static struct espconn serbridgeConn1; // plain bridging port static struct espconn serbridgeConn2; // programming port static esp_tcp serbridgeTcp1, serbridgeTcp2; @@ -25,6 +59,442 @@ void (*programmingCB)(char *buffer, short length) = NULL; // Connection pool serbridgeConnData connData[MAX_CONN]; +static sint8 IROM espbuffsend(serbridgeConnData *conn, const char *data, uint16 len) ; + + +// Telnet protocol (RFC854) characters +// +#define IAC 0xFF // escape +#define DONT 0xFE // negociation (RFC855) +#define DO 0xFD // negociation +#define WONT 0xFC // negociation +#define WILL 0xFB // negociation +#define SB 0xFA // subnegotiation begin +#define SE 0xF0 // subnegotiation end + +#define BINARY 0x00 // RFC856 +#define ECHO 0x01 // RFC857 +#define SUPPRESS_GO_AHEAD 0x03 // RFC858 + +#define COM_PORT_OPTION 0x2C // RFC2217 +#define SET_BAUDRATE 1 // suboption "BAUDRATE" +#define SET_DATASIZE 2 // suboption "DATASIZE" +#define SET_PARITY 3 // suboption "PARITY" +#define SET_STOPSIZE 4 // suboption "STOPSIZE" +#define SET_CONTROL 5 // suboption "CONTROL" +#define PURGE_DATA 12 // suboption "PURGE" + +#define SERBR_DBG + +#ifdef SERBR_DBG +# define dbgf(...) do { os_printf(__VA_ARGS__); }while(0) +#else +# define dbgf(...) do{}while(0) +#endif + + +/* Telnet state machine states + */ +enum { + ST_NORMAL, + ST_IAC, + ST_NEGO, + ST_SB, + ST_COMPORT, + ST_BAUDRATE, + ST_DATASIZE, + ST_PARITY, + ST_STOPSIZE, + ST_CONTROL, + ST_PURGE, + ST_WAIT_IAC +}; + + +/* Process bytes comming from a telnet connection + */ +static void IROM telnet_unwrap ( serbridgeConnData *conn, uint8_t *buf, int len ) +{ +#define state conn->tn_state +#define opt conn->tn_opt +#define vlen conn->tn_vlen + + static uint32_t value ; // Value of subnegociated paramater + + dbgf("TELNET:"); + for ( int i=0; i> 8 ; + char v2 = value >> 16 ; + char v3 = value >> 24 ; + espbuffsend( conn, + (char[]){IAC,SB,COM_PORT_OPTION,100+SET_BAUDRATE,v3,v2,v1,v0,IAC,SE}, + 10 ); + state = ST_WAIT_IAC ; + } + } + else if ( state == ST_DATASIZE ) { + if ( c == 0 ) { + /* + * Null value means the client requests the actual value + */ + c = 5 + ((REG_CONF0>>2) & 0x03) ; + } + else if ( c >= 5 && c <= 8 ) { + /* + * Store databits in bits 3..2 of register conf0 + */ + REG_CONF0 = (REG_CONF0 & ~0xC) | ((c-5)<<2) ; + } + if ( c >= 5 && c <= 8 ) { + /* + * Acknowledge + */ + dbgf(" %d\n", c ); + espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_DATASIZE,c,IAC,SE},7 ); + } + state = ST_WAIT_IAC ; + } + else if ( state == ST_PARITY ) { + /* + * CONF0 bits 1..0 + */ + if ( c == 0 ) { + /* + * Request Current Parity + */ + c = (REG_CONF0>>2) & 0x03 ; + if ( c==2 ) + c = 1 ; + else if ( c==3 ) + c = 2 ; + else + c = 3 ; + goto parity_notify ; + } + if ( c==1 || c==2 || c==3 ) { + /* + * 1 NONE + * 2 ODD + * 3 EVEN + */ + char d ; + if ( c==1 ) + d = 0 ; + else if ( c==2 ) + d = 3 ; + else + d = 2 ; + REG_CONF0 = (REG_CONF0 & ~0x3) | d ; + goto parity_notify ; + } + state = ST_WAIT_IAC ; + return ; + + parity_notify: + dbgf(" %d\n", c ); + espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_PARITY,c,IAC,SE},7 ); + state = ST_WAIT_IAC ; + } + else if ( state == ST_STOPSIZE ) { + /* + * CONF0 bits 5..4 + */ + if ( c == 0 ) { + /* + * 0 Request Current Stop Bit Size + */ + c = REG_CONF0>>4 & 0x03 ; + if ( c==2 ) + c = 3 ; + else if ( c==3 ) + c = 2 ; + } + else if ( c >= 1 && c <= 3 ) { + /* + * 1 1 bit + * 2 2 bits + * 3 1.5 bit + */ + char d = c ; + if ( c==2 ) + d = 3 ; + else if ( c==3 ) + d = 2 ; + REG_CONF0 = (REG_CONF0 & ~0x30) | (d<<4) ; + } + if ( c >= 1 && c <= 3 ) { + dbgf(" %d\n", c ); + espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_STOPSIZE,c,IAC,SE},7 ); + } + state = ST_WAIT_IAC ; + } + else if ( state == ST_CONTROL ) { + if ( c == 1 ) { + /* + * Use No Flow Control (outbound/both) + * + * Disable TX hardware flow: conf0 bit 15 = 0 + * Disable RX hardware flow: conf1 bit 23 = 0 + */ + REG_CONF0 &= ~(1ULL<<15) ; + REG_CONF1 &= ~(1ULL<<23) ; + goto control_notify ; + } + else if ( c == 5 ) { + /* + * Set BREAK State ON + */ + REG_CONF0 |= (1ULL<<8) ; + goto control_notify ; + } + else if ( c == 6 ) { + /* + * Set BREAK State OFF + */ + REG_CONF0 &= ~(1ULL<<8) ; + goto control_notify ; + } + else if ( c == 8 ) { +#if 0 + /* + * Set DTR Signal State ON + * + * Assert DTR: conf0 bit 7 + */ + REG_CONF0 |= (1ULL<<7) ; +#else + /* + * Set DTR Signal State ON + * + * Drive MCU reset LOW + */ + if (mcu_reset_pin >= 0) { + dbgf("MCU reset gpio%d\n", mcu_reset_pin); + GPIO_OUTPUT_SET(mcu_reset_pin, 0); + } + else dbgf("MCU reset: no pin\n"); +#endif + goto control_notify ; + } + else if ( c == 9 ) { +#if 0 + /* + * Set DTR Signal State OFF + * + * Assert DTR: conf0 bit 7 + */ + REG_CONF0 &= ~(1ULL<<7) ; +#else + /* + * Set DTR Signal State OFF + * + * Drive MCU reset HIGH + */ + if (mcu_reset_pin >= 0) { + dbgf("MCU reset gpio%d\n", mcu_reset_pin); + GPIO_OUTPUT_SET(mcu_reset_pin, 1); + } + else dbgf("MCU reset: no pin\n"); +#endif + goto control_notify ; + } + else if ( c == 11 ) { + /* + * Set RTS Signal State ON + * + * Assert RTS: conf0 bit 6 = 1 + */ + REG_CONF0 |= (1ULL<<6) ; + goto control_notify ; + } + else if ( c == 12 ) { + /* + * Set RTS Signal State OFF + * + * Assert RTS: conf0 bit 6 = 0 + */ + REG_CONF0 &= ~(1ULL<<6) ; + goto control_notify ; + } + state = ST_WAIT_IAC ; + return ; + + control_notify: + dbgf(" %d\n", c ); + espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_CONTROL,c,IAC,SE},7 ); + state = ST_WAIT_IAC ; + } + else if ( state == ST_PURGE ) { + if ( c == 1 ) { + /* + * Purge access server receive data buffer + * + * Reset RX FIFO: conf0 bit 17 = 1 + */ + // REG_CONF0 |= (1ULL<<17) ; + goto purge_notify ; + } + else if ( c == 2 ) { + /* + * Purge access server transmit data buffer + * + * Reset TX FIFO: conf0 bit 18 = 1 + */ + // REG_CONF0 |= (1ULL<<18) ; + goto purge_notify ; + } + state = ST_WAIT_IAC ; + return ; + + purge_notify: + dbgf(" %d\n", c ); + espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+PURGE_DATA,c,IAC,SE},7 ); + state = ST_WAIT_IAC ; + } + } + +#undef state +#undef opt +#undef vlen +} + + +#if 0 /* OLD TELNET */ //===== TCP -> UART // Telnet protocol characters @@ -135,6 +605,8 @@ telnetUnwrap(uint8_t *inBuf, int len, uint8_t state) } return state; } +#endif /* OLD TELNET */ + // Generate a reset pulse for the attached microcontroller void ICACHE_FLASH_ATTR @@ -180,9 +652,10 @@ serbridgeRecvCb(void *arg, char *data, unsigned short len) // If the connection starts with a telnet negotiation we will do telnet } - else if (len >= 3 && strncmp(data, (char[]){IAC, WILL, ComPortOpt}, 3) == 0) { + // else if (len >= 3 && strncmp(data, (char[]){IAC, WILL, COM_PORT_OPTION}, 3) == 0) { + else if ( len>2 && data[0]==IAC && (data[1]==WILL || data[1]==DO) ) { conn->conn_mode = cmTelnet; - conn->telnet_state = TN_normal; + conn->tn_state = ST_NORMAL; // note that the three negotiation chars will be gobbled-up by telnetUnwrap #ifdef SERBR_DBG os_printf("telnet mode\n"); @@ -227,7 +700,7 @@ serbridgeRecvCb(void *arg, char *data, unsigned short len) // write the buffer to the uart if (conn->conn_mode == cmTelnet) { - conn->telnet_state = telnetUnwrap((uint8_t *)data, len, conn->telnet_state); + telnet_unwrap(conn, (uint8_t *)data, len); } else { uart0_tx_buffer(data, len); } @@ -339,11 +812,51 @@ console_process(char *buf, short len) // push the buffer into each open connection for (short i=0; i Date: Fri, 15 Apr 2016 12:03:46 +0200 Subject: [PATCH 2/2] RFC2217 option value stored in connection object. Acknowledged baudrate value escaped. Code reorganized. --- serial/serbridge.c | 951 ++++++++++++++++++++++----------------------- serial/serbridge.h | 3 +- 2 files changed, 466 insertions(+), 488 deletions(-) diff --git a/serial/serbridge.c b/serial/serbridge.c index 23e1751..1bbc700 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -1,7 +1,6 @@ // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt -/* Modified by Christophe Duparquet: implementation of RFC 2217 to use the - * Diabolo bootloader through pySerial-3.0. +/* Modified by Christophe Duparquet: extended implementation of RFC 2217 * * Verified on ESP-WROOM-02 with the following configuration: * Reset: gpio5 @@ -41,6 +40,9 @@ #define SKIP_AT_RESET + +/* ESP8266 + */ #define IROM ICACHE_FLASH_ATTR #define REG_BRR (*(volatile uint32_t*)0x60000014) #define REG_CONF0 (*(volatile uint32_t*)0x60000020) @@ -60,6 +62,7 @@ void (*programmingCB)(char *buffer, short length) = NULL; serbridgeConnData connData[MAX_CONN]; static sint8 IROM espbuffsend(serbridgeConnData *conn, const char *data, uint16 len) ; +static sint8 IROM espbuffsend_tn(serbridgeConnData *conn, const char *data, uint16 len) ; // Telnet protocol (RFC854) characters @@ -84,7 +87,6 @@ static sint8 IROM espbuffsend(serbridgeConnData *conn, const char *data, uint16 #define SET_CONTROL 5 // suboption "CONTROL" #define PURGE_DATA 12 // suboption "PURGE" -#define SERBR_DBG #ifdef SERBR_DBG # define dbgf(...) do { os_printf(__VA_ARGS__); }while(0) @@ -111,501 +113,469 @@ enum { }; -/* Process bytes comming from a telnet connection +/* Telnet state machine: process one byte comming from a telnet connection */ -static void IROM telnet_unwrap ( serbridgeConnData *conn, uint8_t *buf, int len ) +static void IROM telnet_process_char ( serbridgeConnData *conn, char c ) { #define state conn->tn_state #define opt conn->tn_opt #define vlen conn->tn_vlen +#define value conn->tn_value + + if ( state == ST_NORMAL ) { + if ( c == IAC ) + state = ST_IAC ; + else + uart0_write_char(c) ; + return ; + } - static uint32_t value ; // Value of subnegociated paramater + if ( state == ST_IAC ) { + if ( c == IAC ) { + /* + * Dual IAC means char \xFF + */ + uart0_write_char(0xFF); + state = ST_NORMAL ; + return ; + } + else if ( c == DONT || c == DO || c == WONT || c == WILL ) { + /* + * Beginning of negociation + */ + opt = c ; + state = ST_NEGO ; + return ; + } + else if ( c == SB ) { + /* + * Beginning of sub option + */ + opt = c ; + state = ST_SB ; + return ; + } + else if ( c == SE ) { + /* + * End of sub option + */ + opt = c ; + state = ST_NORMAL ; + return ; + } + return ; + } - dbgf("TELNET:"); - for ( int i=0; i> 8 ; + char v2 = value >> 16 ; + char v3 = value >> 24 ; + espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_BAUDRATE}, 4 ); + espbuffsend_tn( conn, (char[]){v3,v2,v1,v0}, 4 ); + espbuffsend( conn, (char[]){IAC,SE}, 2 ); + state = ST_WAIT_IAC ; + return ; } - else if ( state == ST_NEGO ) { - dbgf("TELNET: IAC NEGO (%02X)", c); - if ( c == BINARY || - c == ECHO || - c == SUPPRESS_GO_AHEAD || - c == COM_PORT_OPTION ) { - /* - * Acknowledge positive requests for known options - */ - dbgf(": OK\n"); - if ( opt == DO ) - opt = WILL ; - else if ( opt == WILL ) - opt = DO ; - } - else { - /* - * Deny positive requests for unknown options - */ - dbgf(": REJECTED\n"); - if ( opt == DO ) - opt = WONT ; - else if ( opt == WILL ) - opt = DONT ; - } + return ; + } + + if ( state == ST_DATASIZE ) { + if ( c >= 5 && c <= 8 ) { /* - * Send reply + * Set data size + * Store databits in bits 3..2 of register conf0 */ - espbuffsend( conn, (char[]){IAC,opt,c}, 3 ); - state = ST_NORMAL ; + REG_CONF0 = (REG_CONF0 & ~0xC) | ((c-5)<<2) ; } - else if ( state == ST_SB ) { - if ( c == COM_PORT_OPTION ) - state = ST_COMPORT ; + else { + /* + * Get data size + */ + c = 5 + ((REG_CONF0>>2) & 0x03) ; + } + + /* Acknowledge datasize + */ + dbgf(" %d\n", c ); + espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_DATASIZE,c,IAC,SE},7 ); + state = ST_WAIT_IAC ; + return ; + } + + if ( state == ST_PARITY ) { + if ( c == 0 ) { + /* + * Get actual parity: CONF0 bits 1..0 + */ + c = (REG_CONF0>>2) & 0x03 ; + if ( c==2 ) + c = 1 ; + else if ( c==3 ) + c = 2 ; else - state = ST_WAIT_IAC ; + c = 3 ; } - else if ( state == ST_WAIT_IAC ) { - if ( c == IAC ) - state = ST_IAC ; + else if ( c==1 || c==2 || c==3 ) { + /* + * Set parity + * 1 NONE + * 2 ODD + * 3 EVEN + */ + char d ; + if ( c==1 ) + d = 0 ; + else if ( c==2 ) + d = 3 ; + else + d = 2 ; + REG_CONF0 = (REG_CONF0 & ~0x3) | d ; } - else if ( state == ST_COMPORT ) { - vlen = 0 ; - if ( c == SET_BAUDRATE ) { - dbgf(" BAUDRATE:"); - state = ST_BAUDRATE ; - } else if ( c == SET_DATASIZE ) { - dbgf(" DATASIZE:"); - state = ST_DATASIZE ; - } else if ( c == SET_PARITY ) { - dbgf(" PARITY:"); - state = ST_PARITY ; - } else if ( c == SET_STOPSIZE ) { - dbgf(" STOPSIZE:"); - state = ST_STOPSIZE ; - } else if ( c == SET_CONTROL ) { - dbgf(" CONTROL:"); - state = ST_CONTROL ; - } else if ( c == PURGE_DATA ) { - dbgf(" PURGE:"); - state = ST_PURGE ; - } else { - dbgf("UNKNOWN: %02X\n", c); - state = ST_WAIT_IAC ; - } + else { + /* + * Do not acknowledge unknown parity value + */ + state = ST_WAIT_IAC ; + return ; } - else if ( state == ST_BAUDRATE ) { + + /* Acknowledge parity + */ + dbgf(" %d\n", c ); + espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_PARITY,c,IAC,SE},7 ); + state = ST_WAIT_IAC ; + return ; + } + + if ( state == ST_STOPSIZE ) { + if ( c == 0 ) { /* - * Get 4 bytes of baudrate (MSB first) + * Get actual stop bits: CONF0 bits 5..4 */ - dbgf(" %02X", c); - - value <<= 8 ; - value += c ; - vlen++ ; - if ( vlen == 4 ) { - if ( value == 0 ) { - /* - * Null value means the client requests the actual value - */ - value = (int)(0.5 + 80e6 / REG_BRR ); - } - else { - /* - * Write non-null value - */ - REG_BRR = (int)(0.5 + 80e6/value) ; - } - /* - * Acknowledge - */ - dbgf(" = %ld\n", value); - char v0 = value ; - char v1 = value >> 8 ; - char v2 = value >> 16 ; - char v3 = value >> 24 ; - espbuffsend( conn, - (char[]){IAC,SB,COM_PORT_OPTION,100+SET_BAUDRATE,v3,v2,v1,v0,IAC,SE}, - 10 ); - state = ST_WAIT_IAC ; - } + c = REG_CONF0>>4 & 0x03 ; + if ( c==2 ) + c = 3 ; + else if ( c==3 ) + c = 2 ; } - else if ( state == ST_DATASIZE ) { - if ( c == 0 ) { - /* - * Null value means the client requests the actual value - */ - c = 5 + ((REG_CONF0>>2) & 0x03) ; - } - else if ( c >= 5 && c <= 8 ) { - /* - * Store databits in bits 3..2 of register conf0 - */ - REG_CONF0 = (REG_CONF0 & ~0xC) | ((c-5)<<2) ; - } - if ( c >= 5 && c <= 8 ) { - /* - * Acknowledge - */ - dbgf(" %d\n", c ); - espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_DATASIZE,c,IAC,SE},7 ); - } - state = ST_WAIT_IAC ; + else if ( c >= 1 && c <= 3 ) { + /* + * Set stop bits + * 1 1 bit + * 2 2 bits + * 3 1.5 bit + */ + char d = c ; + if ( c==2 ) + d = 3 ; + else if ( c==3 ) + d = 2 ; + REG_CONF0 = (REG_CONF0 & ~0x30) | (d<<4) ; } - else if ( state == ST_PARITY ) { + else { /* - * CONF0 bits 1..0 + * Do not acknowledge unknown stop bits value */ - if ( c == 0 ) { - /* - * Request Current Parity - */ - c = (REG_CONF0>>2) & 0x03 ; - if ( c==2 ) - c = 1 ; - else if ( c==3 ) - c = 2 ; - else - c = 3 ; - goto parity_notify ; - } - if ( c==1 || c==2 || c==3 ) { - /* - * 1 NONE - * 2 ODD - * 3 EVEN - */ - char d ; - if ( c==1 ) - d = 0 ; - else if ( c==2 ) - d = 3 ; - else - d = 2 ; - REG_CONF0 = (REG_CONF0 & ~0x3) | d ; - goto parity_notify ; - } state = ST_WAIT_IAC ; return ; + } - parity_notify: - dbgf(" %d\n", c ); - espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_PARITY,c,IAC,SE},7 ); - state = ST_WAIT_IAC ; + /* Acknowledge stop bits + */ + dbgf(" %d\n", c ); + espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_STOPSIZE,c,IAC,SE},7 ); + state = ST_WAIT_IAC ; + return ; + } + + if ( state == ST_CONTROL ) { + if ( c == 1 ) { + /* + * Use No Flow Control (outbound/both) + * + * Disable TX hardware flow: CONF0 bit 15 = 0 + * Disable RX hardware flow: CONF1 bit 23 = 0 + */ + REG_CONF0 &= ~(1ULL<<15) ; + REG_CONF1 &= ~(1ULL<<23) ; } - else if ( state == ST_STOPSIZE ) { + else if ( c == 5 ) { /* - * CONF0 bits 5..4 + * Set BREAK State ON */ - if ( c == 0 ) { - /* - * 0 Request Current Stop Bit Size - */ - c = REG_CONF0>>4 & 0x03 ; - if ( c==2 ) - c = 3 ; - else if ( c==3 ) - c = 2 ; - } - else if ( c >= 1 && c <= 3 ) { - /* - * 1 1 bit - * 2 2 bits - * 3 1.5 bit - */ - char d = c ; - if ( c==2 ) - d = 3 ; - else if ( c==3 ) - d = 2 ; - REG_CONF0 = (REG_CONF0 & ~0x30) | (d<<4) ; - } - if ( c >= 1 && c <= 3 ) { - dbgf(" %d\n", c ); - espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_STOPSIZE,c,IAC,SE},7 ); - } - state = ST_WAIT_IAC ; + REG_CONF0 |= (1ULL<<8) ; } - else if ( state == ST_CONTROL ) { - if ( c == 1 ) { - /* - * Use No Flow Control (outbound/both) - * - * Disable TX hardware flow: conf0 bit 15 = 0 - * Disable RX hardware flow: conf1 bit 23 = 0 - */ - REG_CONF0 &= ~(1ULL<<15) ; - REG_CONF1 &= ~(1ULL<<23) ; - goto control_notify ; - } - else if ( c == 5 ) { - /* - * Set BREAK State ON - */ - REG_CONF0 |= (1ULL<<8) ; - goto control_notify ; - } - else if ( c == 6 ) { - /* - * Set BREAK State OFF - */ - REG_CONF0 &= ~(1ULL<<8) ; - goto control_notify ; - } - else if ( c == 8 ) { -#if 0 - /* - * Set DTR Signal State ON - * - * Assert DTR: conf0 bit 7 - */ - REG_CONF0 |= (1ULL<<7) ; + else if ( c == 6 ) { + /* + * Set BREAK State OFF + */ + REG_CONF0 &= ~(1ULL<<8) ; + } + else if ( c == 8 ) { +#ifdef USE_UART_CONTROL_LINES + /* + * Set DTR Signal State ON + * + * Assert DTR: CONF0 bit 7 + */ + REG_CONF0 |= (1ULL<<7) ; #else - /* - * Set DTR Signal State ON - * - * Drive MCU reset LOW - */ - if (mcu_reset_pin >= 0) { - dbgf("MCU reset gpio%d\n", mcu_reset_pin); - GPIO_OUTPUT_SET(mcu_reset_pin, 0); - } - else dbgf("MCU reset: no pin\n"); -#endif - goto control_notify ; + /* + * Set DTR Signal State ON + * + * Drive MCU reset LOW + */ + if (mcu_reset_pin >= 0) { + dbgf("MCU reset gpio%d\n", mcu_reset_pin); + GPIO_OUTPUT_SET(mcu_reset_pin, 0); } - else if ( c == 9 ) { -#if 0 - /* - * Set DTR Signal State OFF - * - * Assert DTR: conf0 bit 7 - */ - REG_CONF0 &= ~(1ULL<<7) ; -#else - /* - * Set DTR Signal State OFF - * - * Drive MCU reset HIGH - */ - if (mcu_reset_pin >= 0) { - dbgf("MCU reset gpio%d\n", mcu_reset_pin); - GPIO_OUTPUT_SET(mcu_reset_pin, 1); - } - else dbgf("MCU reset: no pin\n"); + else + dbgf("MCU reset: no pin\n"); #endif - goto control_notify ; + } + else if ( c == 9 ) { +#ifdef USE_UART_CONTROL_LINES + /* + * Set DTR Signal State OFF + * + * Assert DTR: CONF0 bit 7 + */ + REG_CONF0 &= ~(1ULL<<7) ; +#else + /* + * Set DTR Signal State OFF + * + * Drive MCU reset HIGH + */ + if (mcu_reset_pin >= 0) { + dbgf("MCU reset gpio%d\n", mcu_reset_pin); + GPIO_OUTPUT_SET(mcu_reset_pin, 1); } - else if ( c == 11 ) { - /* - * Set RTS Signal State ON - * - * Assert RTS: conf0 bit 6 = 1 - */ - REG_CONF0 |= (1ULL<<6) ; - goto control_notify ; + else + dbgf("MCU reset: no pin\n"); +#endif + } + else if ( c == 11 ) { +#ifdef USE_UART_CONTROL_LINES + /* + * Set RTS Signal State ON + * + * Assert RTS: CONF0 bit 6 = 1 + */ + REG_CONF0 |= (1ULL<<6) ; +#else + /* + * Set RTS Signal State ON + * + * Drive MCU ISP pin LOW + */ + if (mcu_isp_pin >= 0) { + dbgf("MCU ISP gpio%d\n", mcu_isp_pin); + GPIO_OUTPUT_SET(mcu_isp_pin, 0); + os_delay_us(100L); } - else if ( c == 12 ) { - /* - * Set RTS Signal State OFF - * - * Assert RTS: conf0 bit 6 = 0 - */ - REG_CONF0 &= ~(1ULL<<6) ; - goto control_notify ; + else + dbgf("MCU isp: no pin\n"); + slip_disabled++; +#endif + } + else if ( c == 12 ) { +#ifdef USE_UART_CONTROL_LINES + /* + * Set RTS Signal State OFF + * + * Assert RTS: CONF0 bit 6 = 0 + */ + REG_CONF0 &= ~(1ULL<<6) ; +#else + /* + * Set RTS Signal State OFF + * + * Drive MCU ISP pin HIGH + */ + if (mcu_isp_pin >= 0) { + GPIO_OUTPUT_SET(mcu_isp_pin, 1); + os_delay_us(100L); } + if (slip_disabled > 0) slip_disabled--; +#endif + } + else { + /* + * Do not acknowledge unknown control + */ state = ST_WAIT_IAC ; return ; + } - control_notify: - dbgf(" %d\n", c ); - espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_CONTROL,c,IAC,SE},7 ); - state = ST_WAIT_IAC ; + /* Acknowledge control + */ + dbgf(" %d\n", c ); + espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_CONTROL,c,IAC,SE},7 ); + state = ST_WAIT_IAC ; + return ; + } + + if ( state == ST_PURGE ) { + if ( c == 1 ) { + /* + * Purge access server receive data buffer + * + * Reset RX FIFO: CONF0 bit 17 = 1 + */ + // REG_CONF0 |= (1ULL<<17) ; } - else if ( state == ST_PURGE ) { - if ( c == 1 ) { - /* - * Purge access server receive data buffer - * - * Reset RX FIFO: conf0 bit 17 = 1 - */ - // REG_CONF0 |= (1ULL<<17) ; - goto purge_notify ; - } - else if ( c == 2 ) { - /* - * Purge access server transmit data buffer - * - * Reset TX FIFO: conf0 bit 18 = 1 - */ - // REG_CONF0 |= (1ULL<<18) ; - goto purge_notify ; - } + else if ( c == 2 ) { + /* + * Purge access server transmit data buffer + * + * Reset TX FIFO: CONF0 bit 18 = 1 + */ + // REG_CONF0 |= (1ULL<<18) ; + } + else { state = ST_WAIT_IAC ; return ; - - purge_notify: - dbgf(" %d\n", c ); - espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+PURGE_DATA,c,IAC,SE},7 ); - state = ST_WAIT_IAC ; } + + /* Acknowledge purge + */ + dbgf(" %d\n", c ); + espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+PURGE_DATA,c,IAC,SE},7 ); + state = ST_WAIT_IAC ; + return ; } + /* Unexpected char + */ + state = ST_WAIT_IAC ; + #undef state #undef opt #undef vlen +#undef value } -#if 0 /* OLD TELNET */ -//===== TCP -> UART - -// Telnet protocol characters -#define IAC 255 // escape -#define WILL 251 // negotiation -#define SB 250 // subnegotiation begin -#define SE 240 // subnegotiation end -#define ComPortOpt 44 // COM port options -#define SetControl 5 // Set control lines -#define DTR_ON 8 // used here to reset microcontroller -#define DTR_OFF 9 -#define RTS_ON 11 // used here to signal ISP (in-system-programming) to uC -#define RTS_OFF 12 - -// telnet state machine states -enum { TN_normal, TN_iac, TN_will, TN_start, TN_end, TN_comPort, TN_setControl }; - -// process a buffer-full on a telnet connection and return the ending telnet state -static uint8_t ICACHE_FLASH_ATTR -telnetUnwrap(uint8_t *inBuf, int len, uint8_t state) +/* Process bytes comming from a telnet connection + */ +static void IROM telnet_process_buf ( serbridgeConnData *conn, uint8_t *buf, int len ) { - for (int i=0; i write one to outbuf and go normal again - state = TN_normal; - uart0_write_char(c); - break; - case WILL: // negotiation - state = TN_will; - break; - case SB: // command sequence begin - state = TN_start; - break; - case SE: // command sequence end - state = TN_normal; - break; - default: // not sure... let's ignore - uart0_write_char(IAC); - uart0_write_char(c); - } - break; - case TN_will: - state = TN_normal; // yes, we do COM port options, let's go back to normal - break; - case TN_start: // in command seq, now comes the type of cmd - if (c == ComPortOpt) state = TN_comPort; - else state = TN_end; // an option we don't know, skip 'til the end seq - break; - case TN_end: // wait for end seq - if (c == IAC) state = TN_iac; // simple wait to accept end or next escape seq - break; - case TN_comPort: - if (c == SetControl) state = TN_setControl; - else state = TN_end; - break; - case TN_setControl: // switch control line and delay a tad - switch (c) { - case DTR_ON: - if (mcu_reset_pin >= 0) { #ifdef SERBR_DBG - os_printf("MCU reset gpio%d\n", mcu_reset_pin); -#endif - GPIO_OUTPUT_SET(mcu_reset_pin, 0); - os_delay_us(100L); - } -#ifdef SERBR_DBG - else { os_printf("MCU reset: no pin\n"); } -#endif - break; - case DTR_OFF: - if (mcu_reset_pin >= 0) { - GPIO_OUTPUT_SET(mcu_reset_pin, 1); - os_delay_us(100L); - } - break; - case RTS_ON: - if (mcu_isp_pin >= 0) { -#ifdef SERBR_DBG - os_printf("MCU ISP gpio%d\n", mcu_isp_pin); -#endif - GPIO_OUTPUT_SET(mcu_isp_pin, 0); - os_delay_us(100L); - } -#ifdef SERBR_DBG - else { os_printf("MCU isp: no pin\n"); } -#endif - slip_disabled++; - break; - case RTS_OFF: - if (mcu_isp_pin >= 0) { - GPIO_OUTPUT_SET(mcu_isp_pin, 1); - os_delay_us(100L); - } - if (slip_disabled > 0) slip_disabled--; - break; - } - state = TN_end; - break; - } + os_printf("TELNET:"); + for ( int i=0; iconn_mode = cmPGM; - // If the connection starts with a telnet negotiation we will do telnet + // If the connection starts with a telnet negotiation we will do telnet } // else if (len >= 3 && strncmp(data, (char[]){IAC, WILL, COM_PORT_OPTION}, 3) == 0) { else if ( len>2 && data[0]==IAC && (data[1]==WILL || data[1]==DO) ) { @@ -661,14 +631,14 @@ serbridgeRecvCb(void *arg, char *data, unsigned short len) os_printf("telnet mode\n"); #endif - // looks like a plain-vanilla connection! + // looks like a plain-vanilla connection! } else { conn->conn_mode = cmTransparent; } - // if we start out in cmPGM mode due to a connection to the second port we need to do the - // reset dance right away + // if we start out in cmPGM mode due to a connection to the second port we need to do the + // reset dance right away } else if (conn->conn_mode == cmPGMInit) { conn->conn_mode = cmPGM; startPGM = true; @@ -697,12 +667,14 @@ serbridgeRecvCb(void *arg, char *data, unsigned short len) #endif } - - // write the buffer to the uart - if (conn->conn_mode == cmTelnet) { - telnet_unwrap(conn, (uint8_t *)data, len); + if ( conn->conn_mode == cmTelnet ) { + /* + * Process Telnet protocol + */ + telnet_process_buf( conn, (uint8_t *)data, len ); } else { - uart0_tx_buffer(data, len); + // write the buffer to the uart + uart0_tx_buffer( data, len ); } serledFlash(50); // short blink on serial LED @@ -735,13 +707,54 @@ sendtxbuffer(serbridgeConnData *conn) return result; } + +// Escape '\xFF' bytes in buffer for Telnet protocol before sending it over the +// air +// +static sint8 ICACHE_FLASH_ATTR +espbuffsend_tn ( serbridgeConnData *conn, const char *data, uint16 len ) +{ + // How many bytes for the new buffer? + // + int n = 0 ; + for ( int i=0 ; itxbufferlen >= MAX_TXBUFFER) goto overflow; @@ -771,7 +784,7 @@ espbuffsend(serbridgeConnData *conn, const char *data, uint16 len) } return result; -overflow: + overflow: if (conn->txoverflow_at) { // we've already been overflowing if (system_get_time() - conn->txoverflow_at > 10*1000*1000) { @@ -812,44 +825,8 @@ console_process(char *buf, short len) // push the buffer into each open connection for (short i=0; i