USB Serial blocking

Discussions and support about ChibiOS/RT, the free embedded RTOS.
Post Reply
dalbert
Posts: 16
Joined: Mon Mar 02, 2015 8:02 pm

USB Serial blocking

Post 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!
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: USB Serial blocking

Post 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
dalbert
Posts: 16
Joined: Mon Mar 02, 2015 8:02 pm

Re: USB Serial blocking

Post 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
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: USB Serial blocking

Post by Giovanni »

It is the nature of USB, it is not the device sending the data, it is the USB host requesting it.

Giovanni
iggarpe
Posts: 129
Joined: Sun Sep 30, 2012 8:32 pm

Re: USB Serial blocking

Post 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) {
Post Reply