cancel
Showing results for 
Search instead for 
Did you mean: 

How Does This USB Transfer Code Work for Packets > 64?

jvavra
Associate III

I have a USB HID device based on the STM32L432 that I inherited maintenance on. My particular device has a HID Report Descriptor that is greater than 64 bytes. I'm debugging using an ST-Link and single-stepping, trying to figure out how the code handles transmitting this large packet to the host via EP0, since EP0 has a max packet size of 64.

This code is directly from the STCube libraries (stm32l4xx_ll_usb.c):

HAL_StatusTypeDef USB_EPStartXfer(USB_TypeDef *USBx , USB_EPTypeDef *ep, uint8_t dma)
{
  uint16_t pmabuffer = 0;
  uint32_t len = ep->xfer_len;
  
  /* IN endpoint */
  if (ep->is_in == 1)
  {
    /*Multi packet transfer*/
    if (ep->xfer_len > ep->maxpacket)
    {
      len=ep->maxpacket;
      ep->xfer_len-=len; 
    }
    else
    {  
      len=ep->xfer_len;
      ep->xfer_len =0;
    }
    
    /* configure and validate Tx endpoint */
    if (ep->doublebuffer == 0) 
    {
      USB_WritePMA(USBx, ep->xfer_buff, ep->pmaadress, len);
      PCD_SET_EP_TX_CNT(USBx, ep->num, len);
    }
    else
    {
      /* Write the data to the USB endpoint */
      if (PCD_GET_ENDPOINT(USBx, ep->num)& USB_EP_DTOG_TX)
      {
        /* Set the Double buffer counter for pmabuffer1 */
        PCD_SET_EP_DBUF1_CNT(USBx, ep->num, ep->is_in, len);
        pmabuffer = ep->pmaaddr1;
      }
      else
      {
        /* Set the Double buffer counter for pmabuffer0 */
        PCD_SET_EP_DBUF0_CNT(USBx, ep->num, ep->is_in, len);
        pmabuffer = ep->pmaaddr0;
      }
      USB_WritePMA(USBx, ep->xfer_buff, pmabuffer, len);
      PCD_FreeUserBuffer(USBx, ep->num, ep->is_in);
    }
    
    PCD_SET_EP_TX_STATUS(USBx, ep->num, USB_EP_TX_VALID);
  }
  else /* OUT endpoint */
  {
    /* Multi packet transfer*/
    if (ep->xfer_len > ep->maxpacket)
    {
      len=ep->maxpacket;
      ep->xfer_len-=len; 
    }
    else
    {
      len=ep->xfer_len;
      ep->xfer_len =0;
    }
    
    /* configure and validate Rx endpoint */
    if (ep->doublebuffer == 0) 
    {
      /*Set RX buffer count*/
      PCD_SET_EP_RX_CNT(USBx, ep->num, len);
    }
    else
    {
      /*Set the Double buffer counter*/
      PCD_SET_EP_DBUF1_CNT(USBx, ep->num, ep->is_in, len);
    }
    
    PCD_SET_EP_RX_STATUS(USBx, ep->num, USB_EP_RX_VALID);
  }
  
  return HAL_OK;
}

I can see where it checks if the buffer size is greater than 64 here, and then subtracts 64 from the buffer length to leave the remaining buffer size to be transferred:

    /*Multi packet transfer*/
    if (ep->xfer_len > ep->maxpacket)
    {
      len=ep->maxpacket;
      ep->xfer_len-=len; 
    }
    else
    {  
      len=ep->xfer_len;
      ep->xfer_len =0;
    }

But I must be missing something, because it never actually seems to go back and do ANYTHING with that remaining data in the buffer. But the code works: my full 170-byte report descriptor is transferred fine. What am I missing?

4 REPLIES 4
Pavel A.
Evangelist III

USB is time sensitive. Delays added by debugger can (will) cause the host to reset the connection.

Single stepping here is entirely the wrong approach, you're far too slow.

a​) walk the code in your head to understand the potential flow and responses.

b) instrument the decision points and internal states, so you can follow or audit the flow in real time.​

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Not sure I'm following. I'm saying I don't see the code that services the transmit of the remainder of the buffer.

It's the same function, called from the transmit-complete handler within the USB ISR. Your version is different, but for the current CubeL4 see here.

As others said, you cannot debug/study this using breakpoints and single-stepping.

JW