Page 1 of 1

USB Serial blocking

Posted: Tue Aug 25, 2015 5:30 pm
by dalbert
I am new to ChibiOS and am using it with lwIP over Ethernet. The code generally works, but for debugging, I added some chsnprintf() statements to display status on the USB serial shell each time I transmit a lwIP packet. I've noticed that if an active USB serial shell connection is not present, the Ethernet activity eventually stops. I suspect that chsnprintf() fills the USB serial output buffer and then blocks the thread. Can someone more experienced provide any guidance? Is there a recommended way to handle generic debug text output in ChibiOS?

The relevant code snippet is below:

Code: Select all

  
   usb_serial_init();
   shellInit();
   chThdCreateStatic(wa_lwip_thread, LWIP_THREAD_STACK_SIZE, NORMALPRIO + 2,
                    lwip_thread, NULL);
    while (TRUE) {
     
    WATCHDOG_RELOAD;
   
    if (!shelltp && (SDU1.config->usbp->state == USB_ACTIVE)) {
      shelltp = shellCreate(&shell_cfg1, SHELL_WA_SIZE, NORMALPRIO);
      debugChP = (BaseSequentialStream *)&SDU1;
    } else if (chThdTerminated(shelltp)) {
      chThdRelease(shelltp);    /* Recovers memory of the previous shell.   */
      shelltp = NULL;           /* Triggers spawning of a new shell.        */
      debugChP = NULL;
    }
    ...
            if (debugChP) {
                chprintf(debugChP, report_buf);
            }
            send_report(&server_ip, report_buf, strlen(report_buf));
   ...
   } // forever


Many thanks!

Re: USB Serial blocking

Posted: Tue Aug 25, 2015 5:44 pm
by Giovanni
Hi,

Debug output is best done in its own thread (or a shell thread), this way nothing happens if the output blocks.

Alternatively, you may use a real serial port, those never blocks indefinitely like USB-serial does.

Giovanni

Re: USB Serial blocking

Posted: Tue Aug 25, 2015 8:26 pm
by dalbert
Many thanks Giovanni!
I didn't realize the virtual serial port blocked when not connected, I had assumed it behaved like a standard serial port. I appreciate you confirming this!
- Dave

Re: USB Serial blocking

Posted: Tue Aug 25, 2015 8:32 pm
by Giovanni
It is the nature of USB, it is not the device sending the data, it is the USB host requesting it.

Giovanni

Re: USB Serial blocking

Posted: Mon Aug 31, 2015 11:11 am
by iggarpe
I faced the same problem: couldn't afford stalling of a thread writing to the virtual USB serial port. I considered several approaches to fixing the issue and ended up patching ChibiOS, which seemed just the simpler solution.

Basically it adds a couple of fields to the SerialUSBConfig structure which are the timeouts to use when wiriting/reading to/from the associated queue. If you use TIME_INFINITE in both cases you'll get exactly the same default ChibiOS behaviour.

Note that using TIME_IMMEDIATE or some other finite timeout in the writes might have a weird effect on the stream you receive on the PC side. If the cable is disconnected the queue will accept chars until it's full, then discard everything. When you first connect the cable what you'll get first is what you sent first possibly ages ago, not what you attempted to send recently.

Code: Select all

diff -rupN a/os/hal/include/serial_usb.h b/os/hal/include/serial_usb.h
--- a/os/hal/include/serial_usb.h   2013-05-11 08:13:40.000000000 +0200
+++ b/os/hal/include/serial_usb.h   2015-08-18 20:45:10.213602883 +0200
@@ -161,6 +161,14 @@ typedef struct {
    * @brief   Interrupt IN endpoint used for notifications.
    */
   usbep_t                   int_in;
+  /**
+   * @brief   Default timeout to use for output operations (write/put).
+   */
+  systime_t                 out_tout;
+  /**
+   * @brief   Default timeout to use for input operations (read/get).
+   */
+  systime_t                 in_tout;
 } SerialUSBConfig;
 
 /**
diff -rupN a/os/hal/src/serial_usb.c b/os/hal/src/serial_usb.c
--- a/os/hal/src/serial_usb.c   2013-05-11 08:13:46.000000000 +0200
+++ b/os/hal/src/serial_usb.c   2015-08-18 20:45:46.060759983 +0200
@@ -69,23 +69,25 @@ static cdc_linecoding_t linecoding = {
 static size_t write(void *ip, const uint8_t *bp, size_t n) {
 
   return chOQWriteTimeout(&((SerialUSBDriver *)ip)->oqueue, bp,
-                          n, TIME_INFINITE);
+                          n, ((SerialUSBDriver *)ip)->config->out_tout);
 }
 
 static size_t read(void *ip, uint8_t *bp, size_t n) {
 
   return chIQReadTimeout(&((SerialUSBDriver *)ip)->iqueue, bp,
-                         n, TIME_INFINITE);
+                         n, ((SerialUSBDriver *)ip)->config->in_tout);
 }
 
 static msg_t put(void *ip, uint8_t b) {
 
-  return chOQPutTimeout(&((SerialUSBDriver *)ip)->oqueue, b, TIME_INFINITE);
+  return chOQPutTimeout(&((SerialUSBDriver *)ip)->oqueue,
+                        b, ((SerialUSBDriver *)ip)->config->out_tout);
 }
 
 static msg_t get(void *ip) {
 
-  return chIQGetTimeout(&((SerialUSBDriver *)ip)->iqueue, TIME_INFINITE);
+  return chIQGetTimeout(&((SerialUSBDriver *)ip)->iqueue,
+                        ((SerialUSBDriver *)ip)->config->in_tout);
 }
 
 static msg_t putt(void *ip, uint8_t b, systime_t timeout) {