USB device stack

Dependents:   mbed-mX-USB-TEST1 USBMSD_SD_HID_HelloWorld HidTest MIDI_usb_bridge ... more

Issue: STM32F207ZG (Nucleo F207ZG) - Second USB Port Problem

Hello,

to use the second usb interface as a device with the library I changed the file USBDevice/targets/TARGET_STM/USBHAL_STM_144_64pins.h. I used a preprocessor switch to switch between the first (Port A) and second (on Port B) interface. This is the code:

Added a new usb port configuration for USBHAL_STM_144_64pins.h

/* Copyright (c) 2016 mbed.org, MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef USBHAL_STM32_144_64
#define USBHAL_STM32_144_64

//#define USBDOM_PORTB

#ifndef USBDOM_PORTB
    #define USBHAL_IRQn  OTG_FS_IRQn
#else
    #define USBHAL_IRQn  OTG_HS_IRQn
#endif

/*  must be multiple of 4 bytes */
#ifndef USBDOM_PORTB
    #define NB_ENDPOINT 4
    #define MAXTRANSFER_SIZE  0x200
#else
    #define NB_ENDPOINT 4
    #define MAXTRANSFER_SIZE  0x200
#endif

#define FIFO_USB_RAM_SIZE (MAXTRANSFER_SIZE+MAX_PACKET_SIZE_EP0+MAX_PACKET_SIZE_EP1+MAX_PACKET_SIZE_EP2+MAX_PACKET_SIZE_EP3)
#if (FIFO_USB_RAM_SIZE > 0x500)
#error "FIFO dimensioning incorrect"
#endif

typedef struct
{
    USBHAL *inst;
    void (USBHAL::*bus_reset)(void);
    void (USBHAL::*sof)(int frame);
    void (USBHAL::*connect_change)(unsigned int  connected);
    void (USBHAL::*suspend_change)(unsigned int suspended);
    void (USBHAL::*ep0_setup)(void);
    void (USBHAL::*ep0_in)(void);
    void (USBHAL::*ep0_out)(void);
    void (USBHAL::*ep0_read)(void);
    bool (USBHAL::*ep_realise)(uint8_t endpoint, uint32_t maxPacket, uint32_t flags);
    bool (USBHAL::*epCallback[2*NB_ENDPOINT-2])(void);
    /*  memorize dummy buffer used for reception */
    uint32_t pBufRx[MAXTRANSFER_SIZE>>2];
    uint32_t pBufRx0[MAX_PACKET_SIZE_EP0>>2];
    uint8_t epComplete[2*NB_ENDPOINT];
}USBHAL_Private_t;

uint32_t HAL_PCDEx_GetTxFiFo(PCD_HandleTypeDef *hpcd, uint8_t fifo)
{
    uint32_t len;
    if (fifo == 0) len = hpcd->Instance->DIEPTXF0_HNPTXFSIZ>>16;
    else
        len =  hpcd->Instance->DIEPTXF[fifo - 1] >> 16;
    return len*4;
}
void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) 
{
    USBHAL_Private_t *priv=((USBHAL_Private_t *)(hpcd->pData));
    USBHAL *obj= priv->inst;
    USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
    uint32_t sofnum = (USBx_DEVICE->DSTS & USB_OTG_DSTS_FNSOF) >> 8;
    void (USBHAL::*func)(int frame) = priv->sof;
    (obj->*func)(sofnum);
}

USBHAL * USBHAL::instance;

USBHAL::USBHAL(void) {
    /*  init parameter  */
    USBHAL_Private_t *HALPriv = new(USBHAL_Private_t);
    #ifndef USBDOM_PORTB
        hpcd.Instance = USB_OTG_FS; //Port A
    #else
        hpcd.Instance = USB_OTG_HS; //Port B
    #endif
    memset(&hpcd.Init, 0, sizeof(hpcd.Init));
    hpcd.Init.dev_endpoints = NB_ENDPOINT;
    hpcd.Init.ep0_mps =   MAX_PACKET_SIZE_EP0;
    hpcd.Init.phy_itface = PCD_PHY_EMBEDDED;
    hpcd.Init.Sof_enable = 0;

    hpcd.Init.speed = PCD_SPEED_FULL;
    hpcd.Init.vbus_sensing_enable = 0;
    //hpcd.Init.lpm_enable = 0;
    /*  pass instance for usage inside call back */
    HALPriv->inst = this;
    HALPriv->bus_reset = &USBHAL::busReset;
    HALPriv->suspend_change = &USBHAL::suspendStateChanged;
    HALPriv->connect_change = &USBHAL::connectStateChanged;
    HALPriv->sof = &USBHAL::SOF;
    HALPriv->ep0_setup = &USBHAL::EP0setupCallback;
    HALPriv->ep_realise = &USBHAL::realiseEndpoint;
    HALPriv->ep0_in = &USBHAL::EP0in;
    HALPriv->ep0_out = &USBHAL::EP0out;
    HALPriv->ep0_read = &USBHAL::EP0read;
    hpcd.pData = (void*)HALPriv;
    HALPriv->epCallback[0] = &USBHAL::EP1_OUT_callback;
    HALPriv->epCallback[1] = &USBHAL::EP1_IN_callback;
    HALPriv->epCallback[2] = &USBHAL::EP2_OUT_callback;
    HALPriv->epCallback[3] = &USBHAL::EP2_IN_callback;
    HALPriv->epCallback[4] = &USBHAL::EP3_OUT_callback;
    HALPriv->epCallback[5] = &USBHAL::EP3_IN_callback;
    instance = this;
    
    // Enable power and clocking
    /*  board 144 pin all similar  */
    #ifndef USBDOM_PORTB
        __HAL_RCC_GPIOA_CLK_ENABLE(); //Port A
    #else
        __HAL_RCC_GPIOB_CLK_ENABLE(); //Port B
    #endif
    
    #ifndef USBDOM_PORTB
        //First Interface (Port A)
        pin_function(PA_8, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_FS));  /* OTG_FS_SOF */
        pin_function(PA_9, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, GPIO_AF10_OTG_FS));  /* OTG_FS_VBUS */
        pin_function(PA_10, STM_PIN_DATA(STM_MODE_AF_OD, GPIO_PULLUP, GPIO_AF10_OTG_FS)); /* OTG_FS_ID */
        pin_function(PA_11, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_FS)); /* OTG_FS_DM */
        pin_function(PA_12, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_FS)); /* OTG_FS_DP */
    #else    
        //Second Interface (Port B)
        pin_function(PA_4, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF12_OTG_HS_FS));  /* OTG_HS_SOF */
        pin_function(PB_13, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, GPIO_AF12_OTG_HS_FS)); /* OTG_HS_VBUS */
        pin_function(PB_12, STM_PIN_DATA(STM_MODE_AF_OD, GPIO_PULLUP, GPIO_AF12_OTG_HS_FS)); /* OTG_HS_ID */
        pin_function(PB_14, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF12_OTG_HS_FS)); /* OTG_HS_DM */
        pin_function(PB_15, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF12_OTG_HS_FS)); /* OTG_HS_DP */
    #endif
    
    #ifndef USBDOM_PORTB
        __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); //Port A
    #else
        __HAL_RCC_USB_OTG_HS_CLK_ENABLE(); //Port B
    #endif
    
    __HAL_RCC_SYSCFG_CLK_ENABLE();
    hpcd.State = HAL_PCD_STATE_RESET;
    HAL_PCD_Init(&hpcd);
    /* 1.25kbytes */
    /* min value 16 (= 16 x 4 bytes) */
    /*  max value 256 (= 1K bytes ) */
    /*  maximum sum is 0x140 */
    HAL_PCDEx_SetRxFiFo(&hpcd, (MAXTRANSFER_SIZE/4));
    /*  bulk/int 64 bytes in FS  */
    HAL_PCDEx_SetTxFiFo(&hpcd, 0, (MAX_PACKET_SIZE_EP0/4)+1);
    /*  bulk/int bytes in FS */
    HAL_PCDEx_SetTxFiFo(&hpcd, 1, (MAX_PACKET_SIZE_EP1/4)+1);
    HAL_PCDEx_SetTxFiFo(&hpcd, 2, (MAX_PACKET_SIZE_EP2/4));
    /* ISOchronous */
    HAL_PCDEx_SetTxFiFo(&hpcd, 3, (MAX_PACKET_SIZE_EP3/4));
    NVIC_SetVector(USBHAL_IRQn, (uint32_t)&_usbisr);
    NVIC_SetPriority(USBHAL_IRQn, 1);
    HAL_PCD_Start(&hpcd);
}
#endif

To test this code I took a USBSerial (CDC) and just fire an output every 250ms.

If i use the predefined (Port A) USB interface the code is working well.

With the Port B the USB descriptor is successfully sent and then the host disconnects the device. It seems that the interrupt routine is not working with my configuration and I have no clue, why this should be the case.

Here some outputs that shows the linux kernel complaining about the device:

dmesg shows successfully registering the device and then it disconnects the device

[ 5419.410077] usb 1-9: new full-speed USB device number 8 using xhci_hcd
[ 5419.749329] usb 1-9: New USB device found, idVendor=1f00, idProduct=2012
[ 5419.749334] usb 1-9: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 5419.749338] usb 1-9: Product: CDC DEVICE
[ 5419.749341] usb 1-9: Manufacturer: mbed.org
[ 5419.749344] usb 1-9: SerialNumber: 0123456789
[ 5419.771362] cdc_acm 1-9:1.0: ttyACM1: USB ACM device
[ 5421.330501] cdc_acm 1-9:1.0: failed to set dtr/rts
[ 5421.654303] usb 1-9: USB disconnect, device number 8
[ 5421.654479] cdc_acm 1-9:1.0: failed to set dtr/rts
[ 5421.966054] usb 1-9: new full-speed USB device number 9 using xhci_hcd
[ 5422.094065] usb 1-9: device descriptor read/64, error -71
[ 5422.330030] usb 1-9: device descriptor read/64, error -71
[ 5422.566031] usb 1-9: new full-speed USB device number 10 using xhci_hcd
[ 5422.694043] usb 1-9: device descriptor read/64, error -71
[ 5422.930046] usb 1-9: device descriptor read/64, error -71
[ 5423.038072] usb usb1-port9: attempt power cycle
[ 5423.690002] usb 1-9: new full-speed USB device number 11 using xhci_hcd
[ 5423.690150] usb 1-9: Device not responding to setup address.
[ 5423.898146] usb 1-9: Device not responding to setup address.
[ 5424.105995] usb 1-9: device not accepting address 11, error -71
[ 5424.234001] usb 1-9: new full-speed USB device number 12 using xhci_hcd
[ 5424.234148] usb 1-9: Device not responding to setup address.
[ 5424.442029] usb 1-9: Device not responding to setup address.
[ 5424.653998] usb 1-9: device not accepting address 12, error -71
[ 5424.654077] usb usb1-port9: unable to enumerate USB device

To debug this, I placed printfs in the USBCallback_request method of USBCDC.cpp and print the request ID the nucleo gets (via stlinks cdc implementation).

Logged USBRequests comparison PortA (USB_FS) and PortB (USB_HS)

//Port A and B are identical.
CALLBACK REQUEST 06
CALLBACK REQUEST 05
CALLBACK REQUEST 06
CALLBACK REQUEST 06
CALLBACK REQUEST 06
CALLBACK REQUEST 06
CALLBACK REQUEST 06
CALLBACK REQUEST 06
CALLBACK REQUEST 06
CALLBACK REQUEST 09
CALLBACK REQUEST 20
//Port B output ends here.
//Port A continues:
CALLBACK REQUEST 22
CALLBACK REQUEST 20
CALLBACK REQUEST 22
CALLBACK REQUEST 22

So the blocking connect procedure is working and after that the USBDevice lib stops communication with the host. I guess the interrupt is not working if I want to use the second instead of the first interface. A test to confirm this is: The usb connect with descriptor is only working if I reset the board. If I just pull and connect the usb line, the Nucleo isn't sending something and the pc doesn't recognize the usb device either.

Am I missing something ? Sadly I'm running out of ideas what to try next at the moment.