cancel
Showing results for 
Search instead for 
Did you mean: 

USB Host "In" Endpoint limitations in stm32yyxx_ll_usb.c

Danish1
Lead III

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)

2 REPLIES 2
Danish1
Lead III

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.

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