Skip to content

Commit

Permalink
dist/tools/ethos: Build escaped packet in buffer before writing to UART
Browse files Browse the repository at this point in the history
Significantly improves throughput and latency on FRDM-KW41Z.

Ping with a packet of size 100 yields a 30 ms roundtrip with this
patch, 177 ms without. Size 1000 ping before: 1160 ms, after: 188 ms
  • Loading branch information
Joakim Nohlgård committed Aug 18, 2017
1 parent 3648b08 commit 503976c
Showing 1 changed file with 42 additions and 21 deletions.
63 changes: 42 additions & 21 deletions dist/tools/ethos/ethos.c
Expand Up @@ -41,6 +41,9 @@
#define TCP_DEV "tcp:"
#define IOTLAB_TCP_PORT "20000"

/* Size of serial write buffer */
#define SERIAL_BUFFER_SIZE 64

static void usage(void)
{
fprintf(stderr, "Usage: ethos <tap> <serial> [baudrate]\n");
Expand All @@ -49,12 +52,17 @@ static void usage(void)

static void checked_write(int handle, void *buffer, int nbyte)
{
if (write(handle, buffer, nbyte) != nbyte) {
fprintf(stderr, "write to fd %i failed: %s\n", handle, strerror(errno));
while (nbyte > 0) {
ssize_t res = write(handle, buffer, nbyte);
if (res <= 0) {
fprintf(stderr, "write to fd %i failed: %s\n", handle, strerror(errno));
return;
}
nbyte -= res;
}
}

int set_serial_attribs (int fd, int speed, int parity)
int set_serial_attribs(int fd, int speed, int parity)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
Expand Down Expand Up @@ -97,7 +105,7 @@ int set_serial_attribs (int fd, int speed, int parity)
return 0;
}

void set_blocking (int fd, int should_block)
void set_blocking(int fd, int should_block)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
Expand Down Expand Up @@ -241,27 +249,40 @@ static int _serial_handle_byte(serial_t *serial, char c)

static void _write_escaped(int fd, char* buf, ssize_t n)
{
uint8_t out[2];
/*
* Certain USB-to-UART adapters/drivers will immediately send a USB packet
* with a single byte instead of buffering internally when the application
* does writes one byte at a time. Since USB Full Speed can only send 1
* packet per 1 ms, this causes huge latencies for the network, because each
* byte of data will then add at least 1 ms on the latency.
* Observed on NXP OpenSDAv2 (Kinetis FRDM boards), both CMSIS/mbed DAPlink
* and Segger Jlink firmware are affected.
*/
/* Our workaround is to prepare the data to send in a local buffer and then
* call write() on the buffer instead of one char at a time */
uint8_t out[SERIAL_BUFFER_SIZE];
size_t escaped = 0;
size_t buffered = 0;

while(n--) {
char c = *buf++;
switch(c) {
case LINE_FRAME_DELIMITER:
out[0] = LINE_ESC_CHAR;
out[1] = (LINE_FRAME_DELIMITER ^ 0x20);
checked_write(fd, out, 2);
escaped++;
break;
case LINE_ESC_CHAR:
out[0] = LINE_ESC_CHAR;
out[1] = (LINE_ESC_CHAR ^ 0x20);
checked_write(fd, out, 2);
escaped++;
break;
default:
checked_write(fd, &c, 1);
unsigned char c = *buf++;
if (c == LINE_FRAME_DELIMITER || c == LINE_ESC_CHAR) {
out[buffered++] = LINE_ESC_CHAR;
c ^= 0x20;
++escaped;
if (buffered >= SERIAL_BUFFER_SIZE) {
checked_write(fd, out, buffered);
buffered = 0;
}
}
out[buffered++] = c;
if (buffered >= SERIAL_BUFFER_SIZE) {
checked_write(fd, out, buffered);
buffered = 0;
}
}
if (buffered) {
checked_write(fd, out, buffered);
}
}

Expand Down

0 comments on commit 503976c

Please sign in to comment.