cancel
Showing results for 
Search instead for 
Did you mean: 

USB CDC -- zero length packets

Geoffrey1
Associate III
Posted on May 25, 2016 at 23:27

I used stm32cubeMX to generate a CDC project for the stm32f3 discovery board.  After much fussing, I have a pretty stable echo going.  If I use a program to generate output/input from the host, I'm finding writes of 64 bytes bring the whole thing to a halt.   This of course is a magic number which should result in a zero-length packet (ZLP) being sent to complete the transaction.

So here are my questions -- for the CDC class code as generated from cube,

1)  how are ZLPs handled on input ?    Are they passed to  

CDC_

Receive

_FS ?

2) How  should I generate them on output ?  Is this handled automatically in  USBD_CDC_TransmitPacket ?

Just as a side note I found the generated example app code to be full of issues -- it took a lot longer than it should have to get echo going.

Geoffrey
5 REPLIES 5
Geoffrey1
Associate III
Posted on May 31, 2016 at 22:24

Just to close this issue.   If you transmit a buffer that is a multiple of the packet size (e.g. 64 for FS),  you must follow it with another packet (e.g. one of zero length) to complete the transaction.   Surprisingly, the generic CDC code generated by stm32cubemx does not do this.    It took a while to find this bug -- it might be a feature if ST had documented their library routines sufficiently -- it's a bug since they didn't.

Actually, the absolute worst thing about the cube libraries is their pathetic documentation.

Geoffrey

Walid FTITI_O
Senior II
Posted on June 01, 2016 at 11:53

Hi brown.geoffrey.001,

Thank you for your contribution. To make things more clear, you mean that the generated code does not behave like the code in library package (Hal or Std) ?

Can you localize the missing step ( sending zero-length packet)  ? which file and which function should exist ?

Thanks.

-Hannibal-

Geoffrey1
Associate III
Posted on June 02, 2016 at 01:46

There are several places this could be handled.  In general USB application not sending a zero length packet until the end of several transmissions might be fine, but in the case of a virtual com port it is probably not the right behavior -- unless clearly documented.  Here's what I did (follows).   I employed busy waiting in CDC_Transmit_FS to make sure the transmission was completed.  Unfortunately, there is no callback for this which makes using buffered output a challenge.   

if (CDC_

Transmit

_FS(buf, len) == USBD_OK) {

    if (!(len & 63)) // transmitting multiple of packet size

      CDC_

Transmit

_FS(buf,0);  // send zero length packet

    return len;

It'd be fine if 

USBD_CDC_

Transmit

Packet

were properly documented - i.e.

1) Completion needs to be checked separately

2) In case of a  transmission of multiples of 64 bytes (or whatever the maximum packet size is), you must follow this with another transmission -- possibly of zero length.

</span>

David1
Associate II

Not sure if this is 100% right as I am only just learning how this works, but if anyone is interested I 'fixed'? this (for STM32H7) in "usb_cdc.c"

static uint8_t  USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum)
{
  USBD_CDC_HandleTypeDef   *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData;
  
  if(pdev->pClassData != NULL)
  {
/* My Change --------------------------------------------------------------------------------------*/
	/*If it is a multiple of packet size, need to send zero length packet.  */
	if((hcdc->TxLength) && ((hcdc->TxLength & 63) == 0)){
                /*Clear TxLength so it doesn't come back in here.  */
		hcdc->TxLength = 0;
                /*Send zero length packet.   */
		USBD_LL_Transmit(pdev, CDC_IN_EP, NULL, 0);		
               /*NOTE: This will trigger new interrupt and next interrupt will clear TxState. */
	}
	else{
		hcdc->TxState = 0;
	}
/*--------------------------------------------------------------------------------------------------------*/
	//This is the default ST library implementation. (Clear TxState every time.)
       //hcdc->TxState = 0;
    return USBD_OK;
  }
  else
  {
    return USBD_FAIL;
  }
}

Pavel A.
Evangelist III

@David​ 

IMHO this fix is not optimal, it will insert a ZLP after any max.size (64 bytes) packet.

Though it may work in many use cases.

Rather, the sender path should append a ZLP explicitly when it wants the receiving side to see the data ASAP.

For example:

USBD_StatusTypeDef CDC_send_alot(const void *buf, uint16_t len)
{
  USBD_StatusTypeDef result;
  while (1) {
    result = CDC_Transmit_FS((void*)buf, len);
    if (result != USBD_BUSY)
       // this should break if USB reset or disconnect occurred 
       break;      
  }
 
  if (result == USBD_OK && (len % 64 == 0) {
    // Send ZLP
    while(1) {
       result = CDC_Transmit_FS("", 0); 
       if (result != USBD_BUSY)
         break;
    }
  }
  return result;
}

Regards,

-- pa