Browse Source

Serial cleanup - flush, timeout

As per the forum posts, worked on combining the claim code for extracting which endpoints are RX and TX and create the pipes and the like.

Also worked on live cases.  Reading and writing to AX servos... Found some issues Probably more to find.

I left in some debug code to help find cases of deleting a pipe may hang.  Also macros in serial.cpp to set or toggle IO pins (when in debug mode). This helps find where things are happing using Logic Analyzer as a way to find the approriate USB packets.
main
Kurt Eckhardt 7 years ago
parent
commit
4eee8627d2
3 changed files with 138 additions and 207 deletions
  1. +5
    -0
      USBHost_t36.h
  2. +1
    -1
      examples/SerialTest/SerialTest.ino
  3. +132
    -206
      serial.cpp

+ 5
- 0
USBHost_t36.h View File



// FIXME: need different USBSerial, with bigger buffers for 480 Mbit & faster speed // FIXME: need different USBSerial, with bigger buffers for 480 Mbit & faster speed
enum { BUFFER_SIZE = 648 }; // must hold at least 6 max size packets, plus 2 extra bytes enum { BUFFER_SIZE = 648 }; // must hold at least 6 max size packets, plus 2 extra bytes
enum { DEFAULT_WRITE_TIMEOUT = 3500};
USBSerial(USBHost &host) : txtimer(this) { init(); } USBSerial(USBHost &host) : txtimer(this) { init(); }
void begin(uint32_t baud, uint32_t format=USBHOST_SERIAL_8N1); void begin(uint32_t baud, uint32_t format=USBHOST_SERIAL_8N1);
void end(void); void end(void);
uint32_t writeTimeout() {return write_timeout_;}
void writeTimeOut(uint32_t write_timeout) {write_timeout_ = write_timeout;} // Will not impact current ones.
virtual int available(void); virtual int available(void);
virtual int peek(void); virtual int peek(void);
virtual int read(void); virtual int read(void);
virtual int availableForWrite(); virtual int availableForWrite();
virtual size_t write(uint8_t c); virtual size_t write(uint8_t c);
virtual void flush(void);


using Print::write; using Print::write;
protected: protected:
uint8_t setupdata[16]; // uint8_t setupdata[16]; //
uint32_t baudrate; uint32_t baudrate;
uint32_t format_; uint32_t format_;
uint32_t write_timeout_ = DEFAULT_WRITE_TIMEOUT;
Pipe_t *rxpipe; Pipe_t *rxpipe;
Pipe_t *txpipe; Pipe_t *txpipe;
uint8_t *rx1; // location for first incoming packet uint8_t *rx1; // location for first incoming packet

+ 1
- 1
examples/SerialTest/SerialTest.ino View File

while ((ch = userial.read()) == -1) { while ((ch = userial.read()) == -1) {
if ((millis() - ulStart) > 10) { if ((millis() - ulStart) > 10) {
//if (!--ulCounter) { //if (!--ulCounter) {
// Serial.println("Timeout");
Serial.println("Timeout");
return 0; // Timeout return 0; // Timeout
} }
} }

+ 132
- 206
serial.cpp View File

#define print USBHost::print_ #define print USBHost::print_
#define println USBHost::println_ #define println USBHost::println_


//#define ENABLE_DEBUG_PINS

#ifdef ENABLE_DEBUG_PINS
#define debugDigitalToggle(pin) {digitalWriteFast(pin, !digitalReadFast(pin));}
#define debugDigitalWrite(pin, state) {digitalWriteFast(pin, state);}
#else
#define debugDigitalToggle(pin) {;}
#define debugDigitalWrite(pin, state) {;}
#endif

/************************************************************/ /************************************************************/
// Define mapping VID/PID - to Serial Device type. // Define mapping VID/PID - to Serial Device type.
/************************************************************/ /************************************************************/
println(", tx:", tx_ep); println(", tx:", tx_ep);
if (!rx_ep || !tx_ep) return false; // did not get our two end points if (!rx_ep || !tx_ep) return false; // did not get our two end points
if (!init_buffers(rx_size, tx_size)) return false; if (!init_buffers(rx_size, tx_size)) return false;
println(" rx buffer size:", rxsize);
println(" tx buffer size:", txsize);
rxpipe = new_Pipe(dev, 2, rx_ep & 15, 1, rx_size); rxpipe = new_Pipe(dev, 2, rx_ep & 15, 1, rx_size);
if (!rxpipe) return false; if (!rxpipe) return false;
txpipe = new_Pipe(dev, 2, tx_ep, 0, tx_size); txpipe = new_Pipe(dev, 2, tx_ep, 0, tx_size);
break; break;
} }
} }
if (sertype == UNKNOWN) return false; // not one of ours

// Lets try to locate the end points. Code is common across these devices
println("len = ", len);
uint8_t count_end_points = descriptors[4];
if (count_end_points < 2) return false; // not enough end points
if (len < 23) return false;
if (descriptors[0] != 9) return false; // length 9

// Lets walk through end points and see if we
// can find an RX and TX bulk transfer end point.
//Example vid=67B, pid=2303
// 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9
//09 04 00 00 03 FF 00 00 00 07 05 81 03 0A 00 01 07 05 02 02 40 00 00 07 05 83 02 40 00 00
uint32_t rxep = 0;
uint32_t txep = 0;
uint16_t rx_size = 0;
uint16_t tx_size = 0;
uint32_t descriptor_index = 9;
while (count_end_points-- && ((rxep == 0) || txep == 0)) {
if (descriptors[descriptor_index] != 7) return false; // length 7
if (descriptors[descriptor_index+1] != 5) return false; // ep desc
if ((descriptors[descriptor_index+3] == 2)
&& (descriptors[descriptor_index+4] <= 64)
&& (descriptors[descriptor_index+5] == 0)) {
// have a bulk EP size
if (descriptors[descriptor_index+2] & 0x80 ) {
rxep = descriptors[descriptor_index+2];
rx_size = descriptors[descriptor_index+4];
} else {
txep = descriptors[descriptor_index+2];
tx_size = descriptors[descriptor_index+4];
}
}
descriptor_index += 7; // setup to look at next one...
}
// Try to verify the end points.
if (!check_rxtx_ep(rxep, txep)) return false;
print("USBSerial, rxep=", rxep & 15);
print("(", rx_size);
print("), txep=", txep);
print("(", tx_size);
println(")");

if (!init_buffers(rx_size, tx_size)) return false;
println(" rx buffer size:", rxsize);
println(" tx buffer size:", txsize);

rxpipe = new_Pipe(dev, 2, rxep & 15, 1, rx_size);
if (!rxpipe) return false;
txpipe = new_Pipe(dev, 2, txep, 0, tx_size);
if (!txpipe) {
//free_Pipe(rxpipe);
return false;
}
rxpipe->callback_function = rx_callback;
queue_Data_Transfer(rxpipe, rx1, rx_size, this);
rxstate = 1;
txstate = 0;
txpipe->callback_function = tx_callback;
baudrate = 115200;

// Now do specific setup per type
switch (sertype) { switch (sertype) {
//--------------------------------------------------------------------- //---------------------------------------------------------------------
// FTDI // FTDI
case FTDI: case FTDI:
{ {
// FTDI FT232
println("len = ", len);
if (len < 23) return false;
if (descriptors[0] != 9) return false; // length 9
if (descriptors[9] != 7) return false; // length 7
if (descriptors[10] != 5) return false; // ep desc
uint32_t rxep = descriptors[11];
if (descriptors[12] != 2) return false; // bulk type
if (descriptors[13] != 64) return false; // size 64
if (descriptors[14] != 0) return false;
if (descriptors[16] != 7) return false; // length 7
if (descriptors[17] != 5) return false; // ep desc
uint32_t txep = descriptors[18];
if (descriptors[19] != 2) return false; // bulk type
if (descriptors[20] != 64) return false; // size 64
if (descriptors[21] != 0) return false;
if (!check_rxtx_ep(rxep, txep)) return false;
print("FTDI, rxep=", rxep & 15);
println(", txep=", txep);
if (!init_buffers(64, 64)) return false;
rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64);
if (!rxpipe) return false;
txpipe = new_Pipe(dev, 2, txep, 0, 64);
if (!txpipe) {
// TODO: free rxpipe
return false;
}
sertype = FTDI;
rxpipe->callback_function = rx_callback;
rxsize = 64;
queue_Data_Transfer(rxpipe, rx1, 64, this);
rxstate = 1;
txsize = 64;
txstate = 0;
txpipe->callback_function = tx_callback;
baudrate = 115200;
pending_control = 0x0F; pending_control = 0x0F;
mk_setup(setup, 0x40, 0, 0, 0, 0); // reset port mk_setup(setup, 0x40, 0, 0, 0, 0); // reset port
queue_Control_Transfer(dev, &setup, NULL, this); queue_Control_Transfer(dev, &setup, NULL, this);
// TODO: Note: there are probably more vendor/product pairs.. Maybe should create table of them // TODO: Note: there are probably more vendor/product pairs.. Maybe should create table of them
case PL2303: case PL2303:
{ {
// Prolific Technology, Inc. PL2303 Serial Port
println("len = ", len);
uint8_t count_end_points = descriptors[4];
if (count_end_points < 2) return false; // not enough end points
if (len < 23) return false;
if (descriptors[0] != 9) return false; // length 9

// Lets walk through end points and see if we
// can find an RX and TX bulk transfer end point.
//vid=67B, pid=2303
// 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9
//09 04 00 00 03 FF 00 00 00 07 05 81 03 0A 00 01 07 05 02 02 40 00 00 07 05 83 02 40 00 00
uint32_t rxep = 0;
uint32_t txep = 0;
uint32_t descriptor_index = 9;
while (count_end_points-- && ((rxep == 0) || txep == 0)) {
if (descriptors[descriptor_index] != 7) return false; // length 7
if (descriptors[descriptor_index+1] != 5) return false; // ep desc
if ((descriptors[descriptor_index+3] == 2)
&& (descriptors[descriptor_index+4] == 64)
&& (descriptors[descriptor_index+5] == 0)) {
// have a bulk EP size
if (descriptors[descriptor_index+2] & 0x80 ) {
rxep = descriptors[descriptor_index+2];
rxsize = descriptors[descriptor_index+4];
} else {
txep = descriptors[descriptor_index+2];
txsize = descriptors[descriptor_index+4];
}
}
descriptor_index += 7; // setup to look at next one...
}
// Try to verify the end points.
if (!check_rxtx_ep(rxep, txep)) return false;
print("PL2303, rxep=", rxep & 15);
println(", txep=", txep);
if (!init_buffers(64, 64)) return false;
rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64);
if (!rxpipe) return false;
txpipe = new_Pipe(dev, 2, txep, 0, 64);
if (!txpipe) {
// TODO: free rxpipe
return false;
}

sertype = PL2303;
rxpipe->callback_function = rx_callback;
queue_Data_Transfer(rxpipe, rx1, 64, this);
rxstate = 1;
txstate = 0;
txpipe->callback_function = tx_callback;
baudrate = 115200;

// First attempt keep it simple... // First attempt keep it simple...
println("PL2303: readRegister(0x04)"); println("PL2303: readRegister(0x04)");
// Need to setup the data the line coding data // Need to setup the data the line coding data
// CH341 // CH341
case CH341: case CH341:
{ {
println("len = ", len);
uint8_t count_end_points = descriptors[4];
if (count_end_points < 2) return false; // not enough end points
if (len < 23) return false;
if (descriptors[0] != 9) return false; // length 9

// Lets walk through end points and see if we
// can find an RX and TX bulk transfer end point.
// vid=1A86, pid=7523, bDeviceClass = 255, bDeviceSubClass = 0, bDeviceProtocol = 0
// 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9
//09 04 00 00 03 FF 01 02 00 07 05 82 02 20 00 00 07 05 02 02 20 00 00 07 05 81 03 08 00 01
uint32_t rxep = 0;
uint32_t txep = 0;
uint32_t descriptor_index = 9;
while (count_end_points-- && ((rxep == 0) || txep == 0)) {
if (descriptors[descriptor_index] != 7) return false; // length 7
if (descriptors[descriptor_index+1] != 5) return false; // ep desc
if ((descriptors[descriptor_index+3] == 2)
&& (descriptors[descriptor_index+4] <= 64)
&& (descriptors[descriptor_index+5] == 0)) {
// have a bulk EP size
if (descriptors[descriptor_index+2] & 0x80 ) {
rxep = descriptors[descriptor_index+2];
rxsize = descriptors[descriptor_index+4];
} else {
txep = descriptors[descriptor_index+2];
txsize = descriptors[descriptor_index+4];
}
}
descriptor_index += 7; // setup to look at next one...
}
// Try to verify the end points.
if (!check_rxtx_ep(rxep, txep)) return false;
print("ch341, rxep=", rxep & 15);
println(", txep=", txep);
if (!init_buffers(rxsize, txsize)) return false;
rxpipe = new_Pipe(dev, 2, rxep & 15, 1, rxsize);
if (!rxpipe) return false;
txpipe = new_Pipe(dev, 2, txep, 0, txsize);
if (!txpipe) {
// TODO: free rxpipe
return false;
}

rxpipe->callback_function = rx_callback;
queue_Data_Transfer(rxpipe, rx1, rxsize, this);
rxstate = 1;
txstate = 0;
txpipe->callback_function = tx_callback;
baudrate = 115200;

println("CH341: 0xC0, 0x5f, 0, 0, 8"); println("CH341: 0xC0, 0x5f, 0, 0, 8");
// Need to setup the data the line coding data // Need to setup the data the line coding data
mk_setup(setup, 0xC0, 0x5f, 0, 0, sizeof(setupdata)); mk_setup(setup, 0xC0, 0x5f, 0, 0, sizeof(setupdata));
// CP210X // CP210X
case CP210X: case CP210X:
{ {
println("len = ", len);
uint8_t count_end_points = descriptors[4];
if (count_end_points < 2) return false; // not enough end points
if (len < 23) return false;
if (descriptors[0] != 9) return false; // length 9

// Lets walk through end points and see if we
// can find an RX and TX bulk transfer end point.
// BUGBUG: should factor out this code as duplicated...
// vid=10C4, pid=EA60, bDeviceClass = 0, bDeviceSubClass = 0, bDeviceProtocol = 0
// 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9
//09 04 00 00 02 FF 00 00 02 07 05 81 02 40 00 00 07 05 01 02 40 00 00
uint32_t rxep = 0;
uint32_t txep = 0;
uint32_t descriptor_index = 9;
while (count_end_points-- && ((rxep == 0) || txep == 0)) {
if (descriptors[descriptor_index] != 7) return false; // length 7
if (descriptors[descriptor_index+1] != 5) return false; // ep desc
if ((descriptors[descriptor_index+3] == 2)
&& (descriptors[descriptor_index+4] <= 64)
&& (descriptors[descriptor_index+5] == 0)) {
// have a bulk EP size
if (descriptors[descriptor_index+2] & 0x80 ) {
rxep = descriptors[descriptor_index+2];
rxsize = descriptors[descriptor_index+4];
} else {
txep = descriptors[descriptor_index+2];
txsize = descriptors[descriptor_index+4];
}
}
descriptor_index += 7; // setup to look at next one...
}
// Try to verify the end points.
if (!check_rxtx_ep(rxep, txep)) return false;
print("CP210X, rxep=", rxep & 15);
println(", txep=", txep);
if (!init_buffers(rxsize, txsize)) return false;
rxpipe = new_Pipe(dev, 2, rxep & 15, 1, rxsize);
if (!rxpipe) return false;
txpipe = new_Pipe(dev, 2, txep, 0, txsize);
if (!txpipe) {
// TODO: free rxpipe
return false;
}

rxpipe->callback_function = rx_callback;
queue_Data_Transfer(rxpipe, rx1, rxsize, this);
rxstate = 1;
txstate = 0;
txpipe->callback_function = tx_callback;
baudrate = 115200;

println("CP210X: 0x41, 0x11, 0, 0, 0 - reset port"); println("CP210X: 0x41, 0x11, 0, 0, 0 - reset port");
// Need to setup the data the line coding data // Need to setup the data the line coding data
mk_setup(setup, 0x41, 0x11, 0, 0, 0); mk_setup(setup, 0x41, 0x11, 0, 0, 0);
{ {
uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF); uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF);


debugDigitalToggle(6);
// first update rxstate bitmask, since buffer is no longer queued // first update rxstate bitmask, since buffer is no longer queued
if (transfer->buffer == rx1) { if (transfer->buffer == rx1) {
rxstate &= 0xFE; rxstate &= 0xFE;
} }
} }
if (len > 0) { if (len > 0) {
print("rx token: ", transfer->qtd.token, HEX);
print(" transfer length: ", transfer->length, DEC);
print(" len:", len, DEC);
print(" - ", *p, HEX);
println(" ", *(p+1), HEX);
print("rx: "); print("rx: ");
print_hexbytes(p, len); print_hexbytes(p, len);
} }
{ {
uint32_t mask; uint32_t mask;
uint8_t *p = (uint8_t *)transfer->buffer; uint8_t *p = (uint8_t *)transfer->buffer;
debugDigitalWrite(5, HIGH);
if (p == tx1) { if (p == tx1) {
println("tx1:"); println("tx1:");
mask = 1; mask = 1;
mask = 2; mask = 2;
//txstate &= 0xFD; //txstate &= 0xFD;
} else { } else {
debugDigitalWrite(5, LOW);
return; // should never happen return; // should never happen
} }
// check how much more data remains in the transmit buffer // check how much more data remains in the transmit buffer
count = txsize + head - tail; count = txsize + head - tail;
} }
uint32_t packetsize = tx2 - tx1; uint32_t packetsize = tx2 - tx1;
if (count < packetsize) {
// Only output full packets unless the flush bit was set.
if ((count == 0) || ((count < packetsize) && ((txstate & 0x4) == 0) )) {
// not enough data in buffer to fill a full packet // not enough data in buffer to fill a full packet
txstate &= ~mask;
txstate &= ~(mask | 4); // turn off that transfer and make sure the flush bit is not set
debugDigitalWrite(5, LOW);
return; return;
} }
// immediately transmit another full packet, if we have enough data // immediately transmit another full packet, if we have enough data
if (count >= packetsize) count = packetsize;
else txstate &= ~(mask | 4); // This packet will complete any outstanding flush

println("TX:moar data!!!!"); println("TX:moar data!!!!");
if (++tail >= txsize) tail = 0; if (++tail >= txsize) tail = 0;
uint32_t n = txsize - tail; uint32_t n = txsize - tail;
if (n > packetsize) n = packetsize;
if (n > count) n = count;
memcpy(p, txbuf + tail, n); memcpy(p, txbuf + tail, n);
if (n >= packetsize) {
if (n >= count) {
tail += n - 1; tail += n - 1;
if (tail >= txsize) tail = 0; if (tail >= txsize) tail = 0;
} else { } else {
uint32_t len = packetsize - n;
uint32_t len = count - n;
memcpy(p + n, txbuf, len); memcpy(p + n, txbuf, len);
tail = len - 1; tail = len - 1;
} }
txtail = tail; txtail = tail;
queue_Data_Transfer(txpipe, p, packetsize, this);
queue_Data_Transfer(txpipe, p, count, this);
debugDigitalWrite(5, LOW);
} }


void USBSerial::flush()
{
print("USBSerial::flush");
if (txhead == txtail) {
println(" - Empty");
return; // empty.
}
debugDigitalWrite(32, HIGH);
NVIC_DISABLE_IRQ(IRQ_USBHS);
txtimer.stop(); // Stop longer timer.
txtimer.start(100); // Start a mimimal timeout
// timer_event(nullptr); // Try calling direct - fails to work
NVIC_ENABLE_IRQ(IRQ_USBHS);
while (txstate & 3) ; // wait for all of the USB packets to be sent.
println(" completed");
debugDigitalWrite(32, LOW);
}




void USBSerial::timer_event(USBDriverTimer *whichTimer) void USBSerial::timer_event(USBDriverTimer *whichTimer)
{ {
debugDigitalWrite(7, HIGH);
println("txtimer"); println("txtimer");
uint32_t count; uint32_t count;
uint32_t head = txhead; uint32_t head = txhead;
uint32_t tail = txtail; uint32_t tail = txtail;
if (pending_control) {
// We are still doing setup postpone for awhile..
txtimer.start(1200);
println(" Postpone: setup pending_control");
return; // no outgoing buffers available, try again later
}
if (head == tail) { if (head == tail) {
println(" *** Empty ***"); println(" *** Empty ***");
debugDigitalWrite(7, LOW);
return; // nothing to transmit return; // nothing to transmit
} else if (head > tail) { } else if (head > tail) {
count = head - tail; count = head - tail;
} else { } else {
count = txsize + head - tail; count = txsize + head - tail;
} }

uint8_t *p; uint8_t *p;
if ((txstate & 0x01) == 0) { if ((txstate & 0x01) == 0) {
p = tx1; p = tx1;
p = tx2; p = tx2;
txstate |= 0x02; txstate |= 0x02;
} else { } else {
txtimer.start(1200);
txstate |= 4; // Tell the TX code to do flush code.
println(" *** No buffers ***"); println(" *** No buffers ***");
debugDigitalWrite(7, LOW);
return; // no outgoing buffers available, try again later return; // no outgoing buffers available, try again later
} }

uint32_t packetsize = tx2 - tx1;

// Possible for remaining ? packet size and not have both?
if (count > packetsize) {
txstate |= 4; // One of the active transfers will handle the remaining parts
count = packetsize;
}

if (++tail >= txsize) tail = 0; if (++tail >= txsize) tail = 0;
uint32_t n = txsize - tail; uint32_t n = txsize - tail;
if (n > count) n = count; if (n > count) n = count;
print(") "); print(") ");
print_hexbytes(p, count); print_hexbytes(p, count);
queue_Data_Transfer(txpipe, p, count, this); queue_Data_Transfer(txpipe, p, count, this);
debugDigitalWrite(7, LOW);
} }




} }
txtail = tail; txtail = tail;
//println("queue tx packet, newtail=", tail); //println("queue tx packet, newtail=", tail);
debugDigitalWrite(7, HIGH);
queue_Data_Transfer(txpipe, p, packetsize, this); queue_Data_Transfer(txpipe, p, packetsize, this);
debugDigitalWrite(7, LOW);
NVIC_ENABLE_IRQ(IRQ_USBHS); NVIC_ENABLE_IRQ(IRQ_USBHS);
return 1; return 1;
} }
} }
// otherwise, set a latency timer to later transmit partial packet // otherwise, set a latency timer to later transmit partial packet
txtimer.stop(); txtimer.stop();
txtimer.start(3500);
txtimer.start(write_timeout_);
NVIC_ENABLE_IRQ(IRQ_USBHS); NVIC_ENABLE_IRQ(IRQ_USBHS);
return 1; return 1;
} }

Loading…
Cancel
Save