Skip to main content
Graduate
March 3, 2025
Question

USB Host "In" Endpoint limitations in stm32yyxx_ll_usb.c

  • March 3, 2025
  • 3 replies
  • 1061 views

I'm trying to code a USB host on my stm32h743 board, and I would like more than one active IN endpoint.

I'm basing my code on the STM32Cube_FW_L0_V1.12.1 STM32H743I-EVAL application USB_Host CDC_Standalone.

I have a modified version of USBH_CDC_InterfaceInit that (I think) sets up two sets of CDC endpoints, and I can send characters on either of them and receive interrupts fine. When a character is due to be sent back on the first endpoint, I receive that character. But on the second endpoint, I just receive a NULL character.

It's a little hackish in that one of the endpoint sets is hardcoded from the configuration descriptor because we're after an interface subclass and protocol both of 0xff and USBH_FindInterface treats 0xff as a wildcard. But it doesn't matter which order I initialise them, the first one I initialise works and the second one only returns NULL character(s).

static Danish_HandleTypeDef Danish_Interface;

static USBH_StatusTypeDef USBH_Danish_InterfaceInit(USBH_HandleTypeDef *phost) {
 USBH_StatusTypeDef status;
 uint8_t interface;
 Danish_HandleTypeDef *Danish_Handle;
 phost->pActiveClass->pData = &Danish_Interface;
 Danish_Handle = (Danish_HandleTypeDef *)phost->pActiveClass->pData;
 if (Danish_Handle == NULL) {
 USBH_DbgLog("Cannot allocate memory for Danish Handle");
 return USBH_FAIL;
 }
 USBH_memset(Danish_Handle, 0, sizeof(Danish_HandleTypeDef)); /* Initialize cdc handler */
 for (unsigned iPort = 0; iPort < NUM_PORTS; ++iPort) {
 if (iPort == 0) {
 interface = USBH_FindInterface(phost, 0xff, 0xfe, 0xff);
 if ((interface == 0xFFU) || (interface >= USBH_MAX_NUM_INTERFACES)) { /* No Valid Interface */
 USBH_DbgLog("Cannot Find the interface for %s.", phost->pActiveClass->Name);
 return USBH_FAIL;
 }
 } else if (iPort == 1) {
 interface = 1;
 }
 status = USBH_SelectInterface(phost, interface);
 if (status != USBH_OK) {
 return USBH_FAIL;
 }
 for (unsigned i = 0; i < phost->device.CfgDesc.Itf_Desc[interface].bNumEndpoints; ++i) {
 if ((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress & 0x80U) != 0U && (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bmAttributes == 3)) { // Interrupt In
 Danish_Handle->port[iPort].CommItf.NotifEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress;
 Danish_Handle->port[iPort].CommItf.NotifEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize;
 Danish_Handle->port[iPort].InterruptInterval = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bInterval;
 } else if ((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress & 0x80U) != 0U && (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bmAttributes == 2)) { // Bulk In
 Danish_Handle->port[iPort].DataItf.InEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress;
 Danish_Handle->port[iPort].DataItf.InEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize;
 } else if ((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress & 0x80U) == 0U && (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bmAttributes == 2)) { // Bulk Out
 Danish_Handle->port[iPort].DataItf.OutEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress;
 Danish_Handle->port[iPort].DataItf.OutEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize;
 }
 }
 if (Danish_Handle->port[iPort].InterruptInterval < 16)
 Danish_Handle->port[iPort].InterruptInterval = 16; // Override if too short
 Danish_Handle->port[iPort].CommItf.NotifPipe = USBH_AllocPipe(phost, Danish_Handle->port[iPort].CommItf.NotifEp); /*Allocate the length for host channel number in*/
 (void)USBH_OpenPipe(phost, Danish_Handle->port[iPort].CommItf.NotifPipe, Danish_Handle->port[iPort].CommItf.NotifEp, /* Open pipe for Notification endpoint */
 phost->device.address, phost->device.speed, USB_EP_TYPE_INTR,
 Danish_Handle->port[iPort].CommItf.NotifEpSize);
 (void)USBH_LL_SetToggle(phost, Danish_Handle->port[iPort].CommItf.NotifPipe, 0U);
 Danish_Handle->port[iPort].DataItf.OutPipe = USBH_AllocPipe(phost, Danish_Handle->port[iPort].DataItf.OutEp); /*Allocate the length for host channel number out*/
 Danish_Handle->port[iPort].DataItf.InPipe = USBH_AllocPipe(phost, Danish_Handle->port[iPort].DataItf.InEp); /*Allocate the length for host channel number in*/
 (void)USBH_OpenPipe(phost, Danish_Handle->port[iPort].DataItf.OutPipe, Danish_Handle->port[iPort].DataItf.OutEp, /* Open channel for OUT endpoint */
 phost->device.address, phost->device.speed, USB_EP_TYPE_BULK,
 Danish_Handle->port[iPort].DataItf.OutEpSize);
 (void)USBH_OpenPipe(phost, Danish_Handle->port[iPort].DataItf.InPipe, Danish_Handle->port[iPort].DataItf.InEp, /* Open channel for IN endpoint */
 phost->device.address, phost->device.speed, USB_EP_TYPE_BULK,
 Danish_Handle->port[iPort].DataItf.InEpSize);
 if (iPort == 0) {
 Danish_Handle->port[iPort].chosenInterface = 1;
 } else if (iPort == 1) {
 Danish_Handle->port[iPort].chosenInterface = 2;
 }
 Danish_Handle->port[iPort].state = Danish_Startup_state;
 (void)USBH_LL_SetToggle(phost, Danish_Handle->port[iPort].DataItf.OutPipe, 0U);
 (void)USBH_LL_SetToggle(phost, Danish_Handle->port[iPort].DataItf.InPipe, 0U);
 printf("iPort %d interface %d Interrupt EP %02x, Bulk Out EP %02x, Bulk In EP %02x\n", iPort, Danish_Handle->port[iPort].chosenInterface, Danish_Handle->port[iPort].CommItf.NotifEp, Danish_Handle->port[iPort].DataItf.InEp, Danish_Handle->port[iPort].DataItf.OutEp);
 }
 return USBH_OK;
}

What makes me think this might be a bug in the stm32h7xx_ll_usb.c is because reception is routed through USB_ReadPacket which always reads from USBx_DFIFO(0), whereas USB_WritePacket writes to USBx_DFIFO((uint32_t)ch_ep_num) which comes from ep->num or hc->ch_num

Has anyone had success using stm32 as a host with more than 1 IN endpoint?

Did you base your code on the stm32 example(s)?

Any recollection as to any changes you had to make?

(Backstory: I'm trying to communicate control a cellular modem and have simultaneous PPP and AT-commands. I can do this with one brand of modem over the UART serial port using CMUX protocol, but there's another brand of modem with built-in GPS that I'd like to use, only I can't get CMUX to work. So I'm trying USB with stm32 as host and two live USB CDC interfaces. In this case they are ECM not ACM)

    This topic has been closed for replies.

    3 replies

    Danish1Author
    Graduate
    March 3, 2025

    Before anyone picks me up on this, I have just spotted the error in my printf, where I had the in and out endpoint numbers swapped (IN ones have bit 7 set). But that's just a diagnostic to show me that the system has picked up unique and credible endpoint numbers from the configuration descriptor.

    I have looked further in the Reference Manual and I am struggling to understand it. (That's why I decided to experiment with how HAL and other ST examples drive the USB peripheral).

    I read there is one* FIFO for incoming packets in host mode, and that it has control word(s) to tell you about where each packet should be routed. But I don't see the reading of those words in USB_ReadPacket, so maybe the example I was trying to build from uses a simpler control modem that can't cope with multiple IN endpoints. Which begs the question: is there an example of using the stm32 as a host where it can have multiple IN endpoints?

    Trying to debug the example code, I am confused by the IN endpoints repeatedly being disabled (USB_HC_Halt being called from HCD_HC_IN_IRQHandler) with each NAK, and then I guess being re-enabled elsewhere (not traced where that happens). That might be necessary to force the peripheral to poll the next active IN endpoint.

    *So the stm32yyxx_ll_usb.c code does look credible. And my problem is likely to lie elsewhere.

    Super User
    March 3, 2025

    I don't use Cube nor the 'Hxx, but perhaps you may want to read what I wrote about the Synopsys OTG's  RXFLVL interrupts here - that's from Device perspective, but Host in this regard is analogous.

    JW

    Danish1Author
    Graduate
    March 6, 2025

    I have now managed to get things working. I'm not sure I fully understand the why or how. But it seems to work if, within usbh_conf.h, I enable dma_enable, and stop working if I disable it. This might have something to do with all the data-caches in 'Hxx

     hhcd_USB_OTG_HS.Init.dma_enable = ENABLE;