AnsweredAssumed Answered

USB CDC Device hung fix

Question asked by volkov.oleg on Aug 16, 2012
Latest reply on Dec 17, 2012 by merkulov.serafim

Looks like there is a subtle bug in the USB CDC device implementation.

I played recently with STM32F4DISCOVERY board. In order to test USB operation in VCP mode I've created simple code which just echo everything back. At the host side I've launched test application writing the sequence of random bytes with random length to the port and reading the sequence back. What I've found rather quickly is that the read from the port hung occasionally in the state where the ARM is thinking the data is sent but application still can't receive them. After 2 days of thinking and experimenting I've found the solution - see the attached diff.  In short - the receiver is detecting the end of the message by looking at the size of the last received chunk. If it is equal to the maximum transfer size it may expect more data coming and refuse to return the buffered data to application. So in case the last transaction size is a multiple of the maximum transfer size we have to transmit zero sized packet just to indicate that we have no more data to transmit.


--- STM32_USB_Device_Library\Class\cdc\src\usbd_cdc_core.c.1    Fri Oct 28 10:31:18 2011
+++ STM32_USB_Device_Library\Class\cdc\src\usbd_cdc_core.c  Thu Aug 16 22:29:38 2012
@@ -624,7 +624,17 @@
     if (APP_Rx_length == 0)
-      USB_Tx_State = 0;
+      if (((USB_OTG_CORE_HANDLE*)pdev)->dev.in_ep[epnum].xfer_len != CDC_DATA_IN_PACKET_SIZE)
+      {
+        USB_Tx_State = 0;
+        return USBD_OK;
+      }
+      /* Transmit zero sized packet in case the last one has maximum allowed size. Otherwise
+       * the recipient may expect more data coming soon and not return buffered data to app.
+       * See section 5.8.3 Bulk Transfer Packet Size Constraints
+       * of the USB Specification document.
+       */
+      USB_Tx_length = 0;
@@ -643,13 +653,12 @@
         APP_Rx_ptr_out += APP_Rx_length;
         APP_Rx_length = 0;
-      /* Prepare the available data buffer to be sent on IN endpoint */
-      DCD_EP_Tx (pdev,
+    }
+    /* Prepare the available data buffer to be sent on IN endpoint */
+    DCD_EP_Tx (pdev,
-    }
   return USBD_OK;