RFC 2217 working with Diabolo bootloader

pull/140/merge^2
Christophe Duparquet 9 years ago
parent cb75396552
commit 291b55cb4d
  1. 1
      .gitignore
  2. 4
      Makefile
  3. 521
      serial/serbridge.c
  4. 26
      serial/serbridge.h

1
.gitignore vendored

@ -15,5 +15,6 @@ esp-link.opensdf
esp-link.sdf
espfs/mkespfsimage/mman-win32/libmman.a
.localhistory/
Makefile.local
tools/
*.tgz

@ -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.

@ -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<len; i++ ) {
dbgf(" %02X", buf[i]);
}
dbgf(" \n");
for ( int i=0 ; i<len ; i++ ) {
uint8_t c = buf[i];
if ( state == ST_NORMAL ) {
if ( c == IAC )
state = ST_IAC ;
else
uart0_write_char(c) ;
}
else if ( state == ST_IAC ) {
if ( c == IAC ) {
/*
* Dual IAC means char \xFF
*/
uart0_write_char(0xFF);
}
else if ( c == DONT || c == DO || c == WONT || c == WILL ) {
/*
* Beginning of negociation
*/
opt = c ;
state = ST_NEGO ;
}
else if ( c == SB ) {
/*
* Beginning of sub option
*/
opt = c ;
state = ST_SB ;
}
else if ( c == SE ) {
/*
* End of sub option
*/
opt = c ;
state = ST_NORMAL ;
}
}
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 ;
}
/*
* Send reply
*/
espbuffsend( conn, (char[]){IAC,opt,c}, 3 );
state = ST_NORMAL ;
}
else if ( state == ST_SB ) {
if ( c == COM_PORT_OPTION )
state = ST_COMPORT ;
else
state = ST_WAIT_IAC ;
}
else if ( state == ST_WAIT_IAC ) {
if ( c == IAC )
state = ST_IAC ;
}
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 if ( state == ST_BAUDRATE ) {
/*
* Get 4 bytes of baudrate (MSB first)
*/
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 ;
}
}
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<MAX_CONN; i++) {
if (connData[i].conn) {
espbuffsend(&connData[i], buf, len);
if ( connData[i].conn_mode == cmTelnet ) {
/*
* Double 0xFF bytes in buf of Telnet connections
*/
// How many bytes for the new buffer?
//
int n = 0 ;
for ( int i=0 ; i<len ; i++ )
if ( buf[i] == 0xFF )
n++ ;
// Allocate and populate new buffer if necessary
//
if ( n ) {
char *tmpbuf = os_zalloc(len+n);
if ( tmpbuf == NULL ) {
os_printf("serbridge: could not allocate buffer for telnet "
"escape, conn %p\n", &connData[i]);
connData[i].txoverflow_at = system_get_time();
return ;
}
n = 0 ;
for ( int i=0 ; i<len ; i++ ) {
tmpbuf[n++] = buf[i] ;
if ( buf[i] == 0xFF )
tmpbuf[n++] = 0xFF ;
}
// Send buffer
//
espbuffsend( &connData[i], tmpbuf, n );
os_free( tmpbuf );
}
else
espbuffsend( &connData[i], buf, len );
}
else
espbuffsend( &connData[i], buf, len );
}
}
}
// callback with a buffer of characters that have arrived on the uart
void ICACHE_FLASH_ATTR
serbridgeUartCb(char *buf, short length)

@ -12,22 +12,24 @@
#define MAX_TXBUFFER (2*1460)
enum connModes {
cmInit = 0, // initialization mode: nothing received yet
cmPGMInit, // initialization mode for programming
cmInit = 0, // initialization mode: nothing received yet
cmPGMInit, // initialization mode for programming
cmTransparent, // transparent mode
cmPGM, // Arduino/AVR/ARM programming mode
cmTelnet, // use telnet escape sequences for programming mode
cmPGM, // Arduino/AVR/ARM programming mode
cmTelnet, // use telnet escape sequences for programming mode
};
typedef struct serbridgeConnData {
struct espconn *conn;
enum connModes conn_mode; // connection mode
uint8_t telnet_state;
uint16 txbufferlen; // length of data in txbuffer
char *txbuffer; // buffer for the data to send
char *sentbuffer; // buffer sent, awaiting callback to get freed
uint32_t txoverflow_at; // when the transmitter started to overflow
bool readytosend; // true, if txbuffer can be sent by espconn_sent
struct espconn *conn;
enum connModes conn_mode; // connection mode
uint8_t tn_state; // Telnet state machine state
char tn_opt ; // Telnet option
int tn_vlen ; // Telnet option value length
uint16 txbufferlen; // length of data in txbuffer
char *txbuffer; // buffer for the data to send
char *sentbuffer; // buffer sent, awaiting callback to get freed
uint32_t txoverflow_at; // when the transmitter started to overflow
bool readytosend; // true, if txbuffer can be sent by espconn_sent
} serbridgeConnData;
// port1 is transparent&programming, second port is programming only

Loading…
Cancel
Save