The Bug
During rx buffer starvation, usb serial will corrupt ~0.01% of packets received from the host.
(skip ahead to "The Fix" if you want a fairly wild tldr)
Tools
Bare Teensy 3.2 plugged into usb.
I am using platformio on a mac. I have verified this issue on platformio and teensyduino. I wrote the driver code on a mac, but it'll probably work on linux, too (not tested).
Mac OS 10.13.6 (High Sierra)
Platformio Core 5.1.1, Home 3.3.4
Teensyduino | Arduino 1.8.13
platform.txt says version=1.8.5 in both environments.
Testing strategy
- Create 1MB data file: 1024 blocks of 1024 bytes each.
- Precompute a hash for each block with cksum, hardcode them into the sketch.
- Feed 1MB data file to Teensy 3.2
- Teensy hashes the incoming data, compares result to pre-computed hash.
- If the hashes differ, send the rx'd block back to the host for debugging.
This lets us easily reproduce a rare bug, and capture the data so we can figure out what happened.
1MB Data File
I used teensy library source code, with runs of whitespace collapsed. The structured text makes it easier to see the corruption with diff.
data.in/
data.all - all the blocks cat'd together
data.0000
data.0001
data.0002
Driver program
Two distinct execution phases:
- Copy data from stdin to teensy until EOF.
- Copy data from teensy to stdout until a timeout.
build it:
$ gcc -o driver driver.c
Argument is the path to the teensy device file.
macs: /dev/tty.usbmodem12345
linux: /dev/ttyACM0
$ ./driver /dev/tty.usbmodem9319881 <data.in/data.all >data.out
If the whole 1MB is transferred successfully:
$ cat data.out
success
If there was corruption:
$ cat data.out
bad block 784, hash=535335854, expected=2079977378
c functions to wait for ready and send/receive packets
static inline void usb_wait_in_ready(void)
...
(the bit about "c functions" is the source code used to make the input data file)
Diff it
opendiff data.in/data.0784 data.out

Running the driver in a loop
The driver returns 0 on success, so it's easy to run it in a loop:
$ while ./driver /dev/tty.usbmodem9319881 <data.in/data.all >data.out; do head -n 1 data.out; sleep 2; done; cat data.out; echo
It will stop on the first failure, which is recorded in data.out
Testing the test
If we do the following, the bug will not be exposed, letting us test the success condition.
- turn off the slowdown code, line 100
- compile with optimization=Faster, clock=96MHz
On a Teensy 3.2, this runs fast enough to keep the usb rx buffers from filling. No backpressure, so no corruption.
Debugging
It looks like two usb packets are getting swapped. I thought possibly an EVEN/ODD state machine bug, or maybe uninitialized memory from usb_malloc() or use-after-free?
Modify usb_mem.c to fill packet buffers using memset(buf, ch, 64);
ch = 'M' an uninitialized packet fresh from usb_malloc()
ch = 'F' a buffer on the free list, done in usb_free()
ch = 'R' a buffer forwarded from usb_free() to usb_rx_memory()
Run driver, examine data.out
> // endpoint descriptor, URRRRRRRRRRRRRR[..]RRRRRRRRRRRRcriptorType

Ah ha! usb_rx_memory() is implicated.
Weirdness in usb_rx_memory()
usb_rx_memory() in usb_dev.c:783
More debugging. Before we give the packet to the endpoint, set:
packet->buf[0] = 'E'; or
packet->buf[0] = 'O';
so we know which branch it passed through.
...and that change fixed the corruption ??? !!!
The Fix: how does this even work?
Writing 'E' or 'O' to the empty buffer fixes the bug 100% of the time, at all clock speeds, optimization levels, etc. You need the write in both if bodies. The memsets in usb_mem.c are unnecessary.
Kinetis USB experts: What the hell, man?
I hope someone can explain what is going on here. I'm especially interested to know if similar behavior could occur in other parts of the usb serial code, because this change alone was not enough to get my larger, more complex usb serial project working.
Contents of haunted-usb.zip (has everything you need)
sketch.cpp
driver.c
usb_mem.c
usb_dev.c
data.in/
sketch.cpp
driver.c
During rx buffer starvation, usb serial will corrupt ~0.01% of packets received from the host.
(skip ahead to "The Fix" if you want a fairly wild tldr)
Tools
Bare Teensy 3.2 plugged into usb.
I am using platformio on a mac. I have verified this issue on platformio and teensyduino. I wrote the driver code on a mac, but it'll probably work on linux, too (not tested).
Mac OS 10.13.6 (High Sierra)
Platformio Core 5.1.1, Home 3.3.4
Teensyduino | Arduino 1.8.13
platform.txt says version=1.8.5 in both environments.
Testing strategy
- Create 1MB data file: 1024 blocks of 1024 bytes each.
- Precompute a hash for each block with cksum, hardcode them into the sketch.
- Feed 1MB data file to Teensy 3.2
- Teensy hashes the incoming data, compares result to pre-computed hash.
- If the hashes differ, send the rx'd block back to the host for debugging.
This lets us easily reproduce a rare bug, and capture the data so we can figure out what happened.
1MB Data File
I used teensy library source code, with runs of whitespace collapsed. The structured text makes it easier to see the corruption with diff.
data.in/
data.all - all the blocks cat'd together
data.0000
data.0001
data.0002
Driver program
Two distinct execution phases:
- Copy data from stdin to teensy until EOF.
- Copy data from teensy to stdout until a timeout.
build it:
$ gcc -o driver driver.c
Argument is the path to the teensy device file.
macs: /dev/tty.usbmodem12345
linux: /dev/ttyACM0
$ ./driver /dev/tty.usbmodem9319881 <data.in/data.all >data.out
If the whole 1MB is transferred successfully:
$ cat data.out
success
If there was corruption:
$ cat data.out
bad block 784, hash=535335854, expected=2079977378
c functions to wait for ready and send/receive packets
static inline void usb_wait_in_ready(void)
...
(the bit about "c functions" is the source code used to make the input data file)
Diff it
opendiff data.in/data.0784 data.out

Running the driver in a loop
The driver returns 0 on success, so it's easy to run it in a loop:
$ while ./driver /dev/tty.usbmodem9319881 <data.in/data.all >data.out; do head -n 1 data.out; sleep 2; done; cat data.out; echo
It will stop on the first failure, which is recorded in data.out
Testing the test
If we do the following, the bug will not be exposed, letting us test the success condition.
- turn off the slowdown code, line 100
- compile with optimization=Faster, clock=96MHz
On a Teensy 3.2, this runs fast enough to keep the usb rx buffers from filling. No backpressure, so no corruption.
Debugging
It looks like two usb packets are getting swapped. I thought possibly an EVEN/ODD state machine bug, or maybe uninitialized memory from usb_malloc() or use-after-free?
Modify usb_mem.c to fill packet buffers using memset(buf, ch, 64);
ch = 'M' an uninitialized packet fresh from usb_malloc()
ch = 'F' a buffer on the free list, done in usb_free()
ch = 'R' a buffer forwarded from usb_free() to usb_rx_memory()
Run driver, examine data.out
> // endpoint descriptor, URRRRRRRRRRRRRR[..]RRRRRRRRRRRRcriptorType

Ah ha! usb_rx_memory() is implicated.
Weirdness in usb_rx_memory()
usb_rx_memory() in usb_dev.c:783
More debugging. Before we give the packet to the endpoint, set:
packet->buf[0] = 'E'; or
packet->buf[0] = 'O';
so we know which branch it passed through.
Code:
// Called from usb_free, but only when usb_rx_memory_needed > 0, indicating
// receive endpoints are starving for memory. The intention is to give
// endpoints needing receive memory priority over the user's code, which is
// likely calling usb_malloc to obtain memory for transmitting. When the
// user is creating data very quickly, their consumption could starve reception
// without this prioritization. The packet buffer (input) is assigned to the
// first endpoint needing memory.
//
void usb_rx_memory(usb_packet_t *packet)
{
unsigned int i;
const uint8_t *cfg;
cfg = usb_endpoint_config_table;
//serial_print("rx_mem:");
__disable_irq();
for (i=1; i <= NUM_ENDPOINTS; i++) {
#ifdef AUDIO_INTERFACE
if (i == AUDIO_RX_ENDPOINT) continue;
#endif
if (*cfg++ & USB_ENDPT_EPRXEN) {
if (table[index(i, RX, EVEN)].desc == 0) {
[COLOR="#FF0000"]packet->buf[0] = 'E';[/COLOR]
table[index(i, RX, EVEN)].addr = packet->buf;
table[index(i, RX, EVEN)].desc = BDT_DESC(64, 0);
usb_rx_memory_needed--;
__enable_irq();
//serial_phex(i);
//serial_print(",even\n");
return;
}
if (table[index(i, RX, ODD)].desc == 0) {
[COLOR="#FF0000"]packet->buf[0] = 'O';[/COLOR]
table[index(i, RX, ODD)].addr = packet->buf;
table[index(i, RX, ODD)].desc = BDT_DESC(64, 1);
usb_rx_memory_needed--;
__enable_irq();
//serial_phex(i);
//serial_print(",odd\n");
return;
}
}
}
__enable_irq();
// we should never reach this point. If we get here, it means
// usb_rx_memory_needed was set greater than zero, but no memory
// was actually needed.
usb_rx_memory_needed = 0;
usb_free(packet);
return;
}
...and that change fixed the corruption ??? !!!
The Fix: how does this even work?
Writing 'E' or 'O' to the empty buffer fixes the bug 100% of the time, at all clock speeds, optimization levels, etc. You need the write in both if bodies. The memsets in usb_mem.c are unnecessary.
Kinetis USB experts: What the hell, man?
I hope someone can explain what is going on here. I'm especially interested to know if similar behavior could occur in other parts of the usb serial code, because this change alone was not enough to get my larger, more complex usb serial project working.
Contents of haunted-usb.zip (has everything you need)
sketch.cpp
driver.c
usb_mem.c
usb_dev.c
data.in/
- data.all - all blocks cat'd together
- data.0000
- data.0001
- ...
- data.1023
sketch.cpp
Code:
#include <Arduino.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <FastCRC.h>
#define PIN_LED 13
// Enable %f in printf.
asm(".global _printf_float");
#define SCB_AIRCR (*(volatile uint32_t *)0xE000ED0C) // Application Interrupt and Reset Control location
void reboot_now()
{
Serial.end(); //clears the serial monitor if used
SCB_AIRCR = 0x05FA0004; //write value for restart
}
void serial_printf(const char* fmt, ...) {
va_list ap;
static char buf[1024];
if (!Serial) return;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
Serial.print(buf);
}
// Same as linux/mac cksum command.
uint32_t cksum(const uint8_t* buf, uint32_t len) {
FastCRC32 crc; // thanks Frank B!
crc.cksum(buf, len);
// add length to hash
uint8_t buf2[4];
uint32_t buf2_len = 0;
while (len) {
buf2[buf2_len++] = len;
len >>= 8;
}
return crc.cksum_upd(buf2, buf2_len);
}
int discard_input() {
int n = 0;
while (Serial.available()) {
Serial.read();
n++;
}
return n;
}
// precomputed hashes of every 1k block of data.in
extern const uint32_t hashes[1024];
// timestamp of last received byte, used for timeout
uint32_t last_rx;
// the current block we're receiving
uint8_t block_buf[1024];
uint32_t block_buf_len = 0;
// block number for looking up hash
int block_n = 0;
// once we detect a bad block, remember it here
uint32_t bad_block_hash;
int bad_block = -1;
int did_print = 0;
void setup() {
// enable LED
digitalWrite(PIN_LED, LOW);
pinMode(PIN_LED, OUTPUT);
// enable cycle counter
ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
// wait for connection
Serial.begin(9600);
while (!Serial);
last_rx = ARM_DWT_CYCCNT;
}
void loop() {
const int total_blocks = sizeof(hashes) / sizeof(hashes[0]);
if (bad_block < 0 && block_n < total_blocks) {
// input is good so far, keep hashing blocks
while (Serial.available() && block_buf_len < sizeof(block_buf)) {
char ch = Serial.read();
block_buf[block_buf_len++] = ch;
last_rx = ARM_DWT_CYCCNT;
#if 1 // Slowdown
// Required to expose bug at higher clockspeeds / optimization.
// Starve the usb rx buffers to apply backpressure to the host.
static uint32_t slowdown = 0;
if (++slowdown == 10) {
delayMicroseconds(5);
slowdown = 0;
}
#endif
}
if (block_buf_len == sizeof(block_buf)) {
// finished reading 1024 byte block, now check hash
uint32_t h = cksum(block_buf, block_buf_len);
uint32_t expect = hashes[block_n];
if (h != expect) {
// block has been corrupted!
digitalWriteFast(PIN_LED, HIGH);
bad_block = block_n;
bad_block_hash = h;
}
else {
// block is ok, get ready for next block
block_buf_len = 0;
block_n++;
}
}
}
else {
// We already found 1 bad block, or we read all the blocks we have
// hashes for. Discard rest of input.
if (discard_input() > 0) {
last_rx = ARM_DWT_CYCCNT;
}
}
// Wait 250ms after receiving last character.
if (!did_print && ARM_DWT_CYCCNT - last_rx > F_CPU/4) {
if (bad_block < 0) {
serial_printf("success\n");
}
else {
serial_printf("bad block %04i, hash=%u, expected=%u\n",
bad_block, bad_block_hash, hashes[bad_block]);
Serial.write(block_buf, sizeof(block_buf));
}
did_print = 1;
}
// When host program exits, restart teensy.
if (!Serial) {
reboot_now();
}
}
// Checksums of each 1024 byte block.
//
// $ cksum data.in/data.0000 data.in/data.0001
// 4228457909 1024 data.in/data.0000
// 1756643378 1024 data.in/data.0001
const uint32_t hashes[1024] = {
4228457909,
1756643378,
1390827839,
2423965792,
3390210587,
819127511,
2310046989,
386830310,
3652457661,
987039731,
2548782095,
2987884763,
1004123995,
1058494358,
668239705,
3416804312,
2041977815,
4207152148,
818227541,
163579598,
3073125877,
3674374627,
3999065213,
714336666,
864300185,
906008376,
307008169,
1903170675,
4132093258,
1653988441,
181337998,
202195943,
3427156145,
368111983,
2351377580,
1308352079,
1439376271,
3217826155,
2099482396,
3049572432,
3294376581,
3569505994,
2299310252,
2650502404,
2062629456,
3279958704,
268896769,
1984675841,
2355746807,
251681255,
2350789702,
3752250568,
219351725,
620031159,
4125324168,
3109165981,
1915742819,
3189872568,
2216643619,
2726555708,
1856515034,
4037622595,
1620346867,
2608530759,
1277827894,
2598211528,
2842307299,
1158928656,
2231975796,
3328892756,
4094583104,
409973335,
3869639691,
270410755,
4173822740,
1092149366,
684260591,
1635409117,
1170171106,
1266962644,
403737307,
3234847936,
2675343944,
566904840,
690822646,
1593692758,
735247398,
2402308738,
2581553842,
1910412035,
1208479315,
600176231,
2201994854,
912727099,
2302160704,
3468867073,
2245733362,
53100879,
3701912105,
1627385418,
319746318,
1913497855,
748975578,
2478661640,
3717276380,
1349240238,
2588641539,
3892255661,
1681462016,
1733437855,
4184048993,
3795962870,
4258801974,
2907249718,
2871794919,
3079251805,
4192490343,
4091299786,
1695264126,
504838142,
4016645931,
3952163723,
3616539693,
315429236,
145504950,
3236273733,
93948482,
4262811900,
1567270237,
599432975,
3664031792,
203075792,
687923672,
2787304704,
574133491,
2316008897,
3188332485,
581015821,
2439859455,
2834389843,
62169563,
2149379195,
3629188085,
3357350926,
1900374679,
2260202036,
4167297033,
983809120,
1654661994,
1726495612,
2652221839,
998128034,
692745280,
1624742499,
575200295,
2691572258,
1324408938,
2529151751,
3566600743,
3759550045,
1314425539,
1627980226,
2518259122,
1182735720,
1521728637,
461643767,
877987243,
108081537,
552405374,
3059659157,
1346220304,
3089130055,
3140594371,
459672485,
986223909,
4019916964,
2808366536,
4077295779,
745327720,
3400263726,
4163121490,
1948953946,
185300733,
1596048090,
150362168,
1392771251,
212114285,
2887261551,
1795619219,
2152954366,
1144088574,
3063788939,
2036249126,
3843925671,
2084035227,
4026541489,
487809556,
2346917752,
2457799402,
2057472648,
1017823506,
2612519871,
2314484872,
2109314817,
1217634752,
1919382256,
2898342974,
3788850556,
4205699320,
530615463,
3790041155,
1539240416,
3837702307,
2490858337,
3234617832,
1433294999,
1184279191,
2560831498,
4137283872,
1584463547,
362095747,
3765944996,
2716312889,
2706301828,
1680334248,
4053613019,
3747398777,
3254549220,
488550417,
782198462,
3803862661,
4141447081,
1681572757,
1160350737,
3474199297,
14281397,
3792655904,
3576591402,
3438669867,
4184246550,
2791752941,
3995310130,
567228884,
1825913475,
2778748631,
1387256045,
1658851854,
4092403033,
22299972,
2247422987,
817376339,
1786550895,
3682532477,
2979719780,
3969081839,
3417254538,
3076555530,
392240533,
3000296006,
2388761683,
3337167294,
3016230549,
4176372266,
2219392765,
3845339116,
1289664162,
2098905627,
2731933115,
1274742276,
2010679328,
1237732175,
3644796161,
3757564542,
1834659281,
809999113,
505515421,
1898245875,
1438091384,
2027712496,
13662573,
699141999,
1752722072,
137410894,
2118817467,
2514803465,
3714195288,
3825616016,
405676097,
3723495148,
4274725231,
4051112385,
2995735322,
2977915443,
2337175023,
2457742691,
3510173071,
2369462207,
4183458513,
1908213379,
3404581316,
2792306434,
3234989930,
1604064510,
594182718,
1831226062,
3978185905,
3442942973,
3139441302,
2301978086,
3248370654,
3503734647,
1191278100,
3388824394,
798812097,
1735943781,
3305015007,
3842106820,
1387160312,
911327979,
1638272308,
4039102,
216256582,
558021307,
610634028,
3988649340,
3499354157,
1041616805,
3191861971,
4140659338,
38829754,
2300601372,
4283937937,
975129639,
519989810,
2380065917,
1394357164,
3705794712,
99518253,
3186569165,
3338469537,
1921051860,
225086159,
996210737,
3502431520,
3916530528,
3284949844,
1516474917,
476638579,
2252382272,
2772742280,
2610846583,
4019373611,
1080334508,
781589714,
2639299527,
3426061970,
3612442794,
2203276915,
1619722755,
1761160097,
4047344417,
4209496951,
2777327083,
453572060,
3708222832,
425601650,
1699657184,
3158095196,
4214455188,
4254915366,
2723575519,
3106850957,
3557899724,
2375075598,
3007132570,
938941618,
210899559,
610937873,
4271845890,
365975988,
539453223,
142278793,
674092505,
1077493502,
2324250293,
2091268683,
1853987413,
3185755475,
2751245798,
3182665498,
80962339,
2369989329,
2652994471,
2231416430,
1480570071,
2192904042,
1279374774,
3647725975,
2518488295,
3658588678,
2107726852,
2002962395,
3259043220,
2029263516,
2359701965,
2964371983,
1234696275,
3205868353,
1692899510,
3894361519,
2535644745,
51539275,
1318156843,
3704876662,
918288243,
3103195605,
1768290910,
801858278,
2107496250,
3866438334,
3403053924,
3375755112,
3841454297,
791038047,
2467240041,
2451631466,
1312174670,
2616626609,
1486693720,
2206968738,
2278244604,
2808010612,
3168147231,
2586646722,
3390933060,
3492614060,
641130206,
1020955840,
183270028,
1820300065,
3493718458,
2889461959,
2439829454,
1485406265,
1231136854,
1306803660,
4021067144,
854428499,
3822945221,
2660611126,
1100588512,
929909407,
1847342147,
3537923951,
2775737987,
4241889591,
53526193,
2592184543,
1288386233,
850389287,
399371489,
833497661,
552432283,
2045791457,
3322984641,
1253031751,
1648339538,
3409548651,
768731120,
3082184998,
3509170595,
1778288959,
3366303032,
509546330,
1606716597,
3691820127,
1649299278,
21555563,
3475196934,
1293701658,
2932218299,
1162143343,
22588651,
2378978907,
168856027,
2527010465,
3494772866,
1179343321,
2304756311,
3470072239,
472972227,
2146896523,
3830561359,
1733400211,
3440169064,
2203370933,
1162315460,
1396900645,
2618863675,
540744707,
2963203849,
1002013412,
851226491,
264471460,
4121109889,
174417358,
1553125135,
3184573988,
1034332621,
583151622,
2405736973,
2240469143,
1429440091,
756098332,
2036259300,
3739596220,
3961230038,
569201915,
2012597518,
1003359704,
3495387777,
3375386366,
3064409430,
3107824733,
59212460,
4047217607,
1300169818,
1094978420,
1068884882,
1108585116,
2804436769,
197566978,
3100739570,
2079257405,
3962061112,
3576074192,
788601916,
4128899557,
1076603238,
242875979,
2335782986,
2017611665,
4253169991,
2065743221,
552630866,
2962851619,
354986631,
2165605118,
2012032843,
2889878242,
2092890265,
639338364,
1347989204,
942470730,
2252829580,
1679280633,
3426926805,
2460386877,
2268837592,
2939787632,
4044478738,
3743691795,
3744446995,
2933455444,
2557686782,
2253515640,
3543288114,
1463733644,
2707846633,
2255502178,
806460925,
1117612866,
2747555290,
2385356366,
2852673631,
784156444,
1400988719,
2227697536,
2144080323,
3713193879,
1245510972,
1307436318,
1598550267,
2058302879,
1394077381,
30602995,
3144357521,
3051790873,
2762847484,
1369525587,
3502578773,
2133852051,
2618843137,
2996807407,
2857016292,
482164482,
934888861,
545538528,
2616352346,
505241062,
3763900197,
2888722510,
1264763076,
1822329382,
1257845551,
1535649277,
2089532395,
2215424118,
2295771800,
543052005,
1141685060,
119138419,
749337407,
13485545,
3874106929,
2928428394,
2544595324,
1228498129,
1197623568,
1163938508,
4260956477,
3103969210,
3731678889,
592819979,
2525340870,
1682392315,
883013012,
1770592201,
3523558385,
3597948874,
714171287,
1247046859,
3534161901,
819669005,
2612080378,
2273018927,
3463627335,
2296444105,
532263474,
1822625065,
3534652387,
4290659295,
494490863,
2866134605,
3793072803,
3753946994,
2459788002,
3122139320,
3885792534,
509826136,
483427050,
3621836263,
1841963022,
3730194881,
1230764812,
54717181,
3415843127,
1111984570,
1799932995,
3294193988,
2758729771,
1279730562,
2670542501,
3076917323,
2104303795,
110997706,
764560334,
2859025702,
550037746,
2767121614,
4009470551,
1651207639,
1157514380,
935267206,
2856750827,
791504634,
2787305692,
1369497738,
140669096,
1167606214,
447718239,
3199820089,
692933545,
4006063109,
3318495127,
1974568827,
2407546656,
3156014333,
753373809,
1365768043,
1152274450,
2504822738,
3063048204,
1458625971,
3875243945,
2088409538,
4278629151,
2410743102,
1602604338,
4160672589,
3496134650,
2985507213,
921713997,
2250016173,
2219237146,
2259378922,
1017714192,
4263355383,
4125383889,
1332102076,
977758853,
11517204,
4252283914,
875599260,
2611944926,
3369895365,
2145835305,
4213884397,
30376195,
3998991217,
2973438463,
272696580,
3403848954,
3330273010,
3550774645,
697293896,
458911542,
2869908738,
257978978,
1610095969,
2648072319,
3420444038,
4239107752,
4046061194,
3066050737,
571741668,
4090638442,
1748018516,
1730529058,
115503973,
3275262561,
1082241110,
997365964,
1034532362,
346659654,
2344763600,
2392040371,
3696933128,
488276171,
3257693904,
3788867234,
405387546,
3258297266,
3614344809,
2053739007,
1135563987,
80601375,
2182172673,
822192382,
4097975004,
1869701365,
217647104,
469743348,
3590368804,
980536618,
50792880,
674778571,
2225018826,
665571307,
1587057614,
2569231910,
734555407,
522818910,
107174795,
3875667478,
1439704813,
2374353360,
3543171206,
4193995686,
3741405368,
4171638274,
4211530504,
1716255036,
1641991372,
2079977378,
1306797535,
447815821,
184278330,
4226118535,
2838563860,
802348545,
1614345970,
2329598559,
3909980848,
1268621525,
2910204840,
1465685642,
1561351736,
1927231228,
1548526690,
1561223606,
2433630100,
674764382,
290283463,
3064867503,
464875768,
331506926,
1743642855,
2014210677,
1923373952,
1555936407,
2387160762,
252276859,
2019717069,
2882159161,
2606121970,
167021300,
1670947197,
624713933,
812278327,
1588855871,
258173063,
3126389784,
3401108312,
3677825498,
898327590,
3790121792,
2267783899,
2758400098,
757228551,
2759778248,
4020813753,
1983408194,
2831266649,
1734052469,
811793678,
2052628729,
3491123026,
2255200905,
1727176909,
4021556895,
1924026098,
3790846287,
1565871025,
3352945393,
634090299,
1927973038,
2689764125,
97795552,
1363275155,
3917753002,
2861498920,
2666182825,
954443217,
3623065706,
2293231534,
2802864606,
678094452,
3151743662,
1553250607,
1760085022,
3114502212,
3105637317,
568278596,
706068127,
3006995427,
3246670460,
4134305585,
3428237913,
2381301418,
2269013226,
4115708403,
3123335344,
1720952799,
2894760103,
3648216607,
783401454,
141466472,
3462339531,
4020245973,
2120596800,
3485628784,
1502924605,
2325915897,
3841260664,
4215096758,
2467110716,
23966786,
1677554518,
3370487916,
3498802623,
124232462,
936780707,
3520529797,
25531820,
3555334839,
897047873,
3239975404,
999420941,
1569267646,
82647075,
2156564772,
1213520264,
471524678,
2230196703,
2546986008,
3456694406,
4271038195,
2738224225,
917532559,
3478214647,
2085024962,
2668576920,
1260040878,
3249721556,
2136556717,
1083903407,
2630801199,
705789379,
336268124,
673164310,
2614194481,
3554853175,
2812987894,
764894752,
1264208642,
993726103,
2174326119,
2804413130,
609781530,
1454346894,
1050324932,
3524141160,
621770131,
3933908286,
3587849499,
3358489601,
3681207795,
3748704848,
3299570571,
1822895091,
1579436793,
3356730487,
2060923311,
1937632676,
1370711317,
2361771164,
1105910994,
3233032443,
1102730797,
683213159,
4243832508,
3792838247,
2894441402,
1588817832,
420296804,
4068210459,
3285386607,
3222423606,
27870307,
1264762071,
1050979653,
885792108,
103739607,
2858077429,
2944899248,
1627737897,
1339106172,
1160275329,
1211859825,
2754082985,
990012903,
1040656622,
410708768,
880528050,
1815830020,
4033895564,
4286314265,
29863736,
90225554,
3510311305,
1217873367,
3945490646,
2866722547,
1619844166,
1236948233,
1345316903,
2449350112,
3171207069,
191718008,
1456138340,
3495979759,
1930947001,
3578448158,
3397996154,
2050137091,
3358810265,
3151063028,
473639363,
967838860,
4067402421,
3787458760,
1589600924,
967390492,
95884283,
609410709,
4179210441,
1785459396,
61479931,
1778744016,
1992167485,
3913397592,
176615387,
1399125722,
1259649046,
4239333143,
2057858776,
2124394241,
1738068790,
199501278,
848616408,
2178850397,
2585695393,
2138395007
};
driver.c
Code:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <termios.h>
// Wall time in floating-point seconds, starting at 0.0
double now() {
static struct timeval tv_0 = { 0 };
if (tv_0.tv_sec == 0) {
gettimeofday(&tv_0, 0);
return 0.0;
}
struct timeval tv;
gettimeofday(&tv, 0);
double d = tv.tv_usec;
d -= tv_0.tv_usec;
d *= 1e-6;
d += tv.tv_sec - tv_0.tv_sec;
return d;
}
int main(int argc, const char* argv[]) {
int error = 0;
if (argc != 2) {
fprintf(stderr, "Usage: ./driver /dev/tty.usbmodem123 <data.in/data.all >data.out\n");
return 3;
}
// store the first little bit of the teensy reply, just enough
// to detect "success"
char reply[] = "success";
int reply_n = 0;
// use stdin and stdout for data
int fd_in = 0;
int fd_out = 1;
const char* teensy_dev_path = argv[1];
int fd_usb = open(teensy_dev_path, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd_usb == -1) {
error = errno;
fprintf(stderr, "Cannot open teensy device %s\n", teensy_dev_path);
goto quit;
}
struct termios tios;
memset(&tios, 0, sizeof(tios));
tios.c_iflag = IGNBRK | IGNPAR;
tios.c_oflag = 0;
tios.c_cflag = CS8 | CREAD | CLOCAL;
tios.c_lflag = 0;
tios.c_cc[VTIME] = 0;
tios.c_cc[VMIN] = 0;
cfsetspeed(&tios, 9600);
tcflush(fd_usb, TCIFLUSH);
tcsetattr(fd_usb, TCSANOW, &tios);
char buf[1024];
while (1) {
int n = read(fd_in, buf, sizeof(buf));
if (n < 0) {
error = errno;
fprintf(stderr, "Error reading input.\n");
goto quit;
}
if (n == 0) {
// done reading input
break;
}
// write data to teensy
const char* p = buf;
while (n > 0) {
// this would be simpler if we didn't use O_NONBLOCK
int r = write(fd_usb, p, n);
if (r < 0) {
if (errno == EAGAIN) continue;
error = errno;
fprintf(stderr, "Error writing to teensy.\n");
goto quit;
}
p += r;
n -= r;
}
}
// done sending data, now read teensy reply
// 500ms timeout to know teensy is done txing
double last_rx = now();
while (now() - last_rx < 0.5) {
int n = read(fd_usb, buf, sizeof(buf));
if (n < 0) {
if (errno == EAGAIN) continue;
error = errno;
fprintf(stderr, "Error reading from teensy.\n");
goto quit;
}
if (n > 0) {
last_rx = now();
int r = write(fd_out, buf, n);
if (r < 0) {
error = errno;
fprintf(stderr, "Error writing output.\n");
goto quit;
}
int reply_free = sizeof(reply) - reply_n;
if (r > 0 && reply_free > 0) {
if (r > reply_free) r = reply_free;
memcpy(reply + reply_n, buf, r);
reply_n += r;
}
}
}
quit:
if (error) {
fprintf(stderr, "errno=%i %s\n", error, strerror(error));
// force non-zero return
reply[0] = 0;
}
close(fd_usb);
if (reply_n == sizeof(reply)) reply_n--;
reply[reply_n] = 0;
return strcmp(reply, "success") != 0;
}