cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F407 USB Host Library - HID Class Issues

PMera.1
Associate II

Im trying to connect a keyboard to a STM32F407 discovery board.

The board correctly identifies the keyboard and works as expected, for the most part, except I cant get the multimedia keys and the "regular" keys to work at the same time. The keyboard is a HID device with two interfaces: a boot interface and a multimedia keys dedicated one.

In order to get all the interfaces identified, I changed the Interface initialization to the following:

static USBH_StatusTypeDef USBH_HID_InterfaceInit2(USBH_HandleTypeDef *phost)
{
  USBH_StatusTypeDef status;
 
 
  HID_HandleTypeDef** HID_Handles;
 
  uint8_t max_ep;
  uint8_t num = 0U;
 
  uint8_t* interfaces;
  interfaces = malloc(phost->device.CfgDesc.bNumInterfaces);
  HID_Handles = (HID_HandleTypeDef**)malloc(sizeof(HID_HandleTypeDef*) * phost->device.CfgDesc.bNumInterfaces);
  for (int i = 0; i < phost->device.CfgDesc.bNumInterfaces; ++i)
  {
    interfaces[i] = USBH_FindInterface(phost, phost->pActiveClass->ClassCode, phost->device.CfgDesc.Itf_Desc[i].bInterfaceSubClass, 0xFFU);
    if(interfaces[i] == 0xFFU || interfaces[i] >= USBH_MAX_NUM_INTERFACES)
    {
      //INTERFACE NOT FOUND
      DEBUG_PRINT("INTERFACE NOT FOUND\n");
      continue;
    }
    status = USBH_SelectInterface(phost, interfaces[i]);
    if(status != USBH_OK) continue;
    phost->pActiveClass->pData = (HID_HandleTypeDef *)USBH_malloc(sizeof(HID_HandleTypeDef));
    HID_Handles[i] = (HID_HandleTypeDef *) phost->pActiveClass->pData;
    if (HID_Handles[i] == NULL)
    {
      DEBUG_PRINT("Cannot allocate memory for HID Handle");
      continue;
    }
    USBH_memset(HID_Handles[i], 0, sizeof(HID_HandleTypeDef));
    HID_Handles[i]->state = HID_ERROR;
    /*Decode Bootclass Protocol: Mouse or Keyboard*/
    if (phost->device.CfgDesc.Itf_Desc[interfaces[i]].bInterfaceProtocol == HID_KEYBRD_BOOT_CODE)
    {
      DEBUG_PRINT("BOOT KEYBOARD FOUND!\n");
      HID_Handles[i]->Init = USBH_HID_KeybdInit;
    }
    else if (phost->device.CfgDesc.Itf_Desc[interfaces[i]].bInterfaceProtocol  == HID_MOUSE_BOOT_CODE)
    {
      DEBUG_PRINT("MOUSE DEVICE FOUND\n");
      HID_Handles[i]->Init = USBH_HID_MouseInit;
    }
    else if (phost->device.CfgDesc.Itf_Desc[interfaces[i]].bInterfaceProtocol  == 0x00)
    {
      DEBUG_PRINT("KEYBOARD DEVICE FOUND\n");
      HID_Handles[i]->Init = USBH_HID_KeyboardMultimedia;
    }
    else
    {
      DEBUG_PRINT("Protocol not supported.\n");
      return USBH_FAIL;
    }
 
    HID_Handles[i]->state     = HID_INIT;
    HID_Handles[i]->ctl_state = HID_REQ_INIT;
    HID_Handles[i]->ep_addr   = phost->device.CfgDesc.Itf_Desc[interfaces[i]].Ep_Desc[0].bEndpointAddress;
    HID_Handles[i]->length    = phost->device.CfgDesc.Itf_Desc[interfaces[i]].Ep_Desc[0].wMaxPacketSize;
    HID_Handles[i]->poll      = phost->device.CfgDesc.Itf_Desc[interfaces[i]].Ep_Desc[0].bInterval;
 
    DEBUG_PRINT("INTERFACE %u has endpoint address %02X\n", i, HID_Handles[i]->ep_addr);
 
    if (HID_Handles[i]->poll  < HID_MIN_POLL)
    {
      HID_Handles[i]->poll = HID_MIN_POLL;
    }
 
    /* Check fo available number of endpoints */
    /* Find the number of EPs in the Interface Descriptor */
    /* Choose the lower number in order not to overrun the buffer allocated */
    max_ep = ((phost->device.CfgDesc.Itf_Desc[interfaces[i]].bNumEndpoints <= USBH_MAX_NUM_ENDPOINTS) ? phost->device.CfgDesc.Itf_Desc[interfaces[i]].bNumEndpoints : USBH_MAX_NUM_ENDPOINTS);
 
    for (num = 0; num < max_ep; num++)
    {
      if (phost->device.CfgDesc.Itf_Desc[interfaces[i]].Ep_Desc[num].bEndpointAddress & 0x80U)
      {
        HID_Handles[i]->InEp = (phost->device.CfgDesc.Itf_Desc[interfaces[i]].Ep_Desc[num].bEndpointAddress);
        HID_Handles[i]->InPipe = USBH_AllocPipe(phost, HID_Handles[i]->InEp);
 
        /* Open pipe for IN endpoint */
        USBH_OpenPipe(phost, HID_Handles[i]->InPipe, HID_Handles[i]->InEp, phost->device.address, phost->device.speed, USB_EP_TYPE_INTR, HID_Handles[i]->length);
 
        USBH_LL_SetToggle(phost, HID_Handles[i]->InPipe, 0U);
        DEBUG_PRINT("OPENED EP: %u %u %u\n",HID_Handles[i]->InPipe, HID_Handles[i]->InEp, phost->device.address);
      }
      else
      {
        HID_Handles[i]->OutEp = (phost->device.CfgDesc.Itf_Desc[interfaces[i]].Ep_Desc[num].bEndpointAddress);
        HID_Handles[i]->OutPipe  = USBH_AllocPipe(phost, HID_Handles[i]->OutEp);
 
        /* Open pipe for OUT endpoint */
        USBH_OpenPipe(phost, HID_Handles[i]->OutPipe, HID_Handles[i]->OutEp, phost->device.address, phost->device.speed, USB_EP_TYPE_INTR, HID_Handles[i]->length);
 
        USBH_LL_SetToggle(phost, HID_Handles[i]->OutPipe, 0U);
      }
    }
  }
 
  /* Decode endpoint IN and OUT address from interface descriptor */
 
 
  return USBH_OK;
}

However, the last interface identified is the only one that that gets selected. If I comment the if branch that gets the multimedia keys, the regular ones work correctly. If I dont, the multimedia keys work perfectly, but the regular keys no longer generate an event. Of any kind.

Has someone managed to get a multimedia keyboard working?

3 REPLIES 3
Pavel A.
Evangelist III

Do you actually poll both interfaces? The code above shows only initialization.

-- pa

PMera.1
Associate II

Thanks for your answer, Pavel. I originally wanted to post the usb descriptor for the keyboard in its entirety as well, but I maxed out of post characters :).

No, I dont think I poll all the interfaces. Scratch that, Im sure Im not.

In static USBH_StatusTypeDef USBH_HID_Process(USBH_HandleTypeDef *phost) only the multimedia keyboard interface is polled.

So I need to somehow point to the first interface, for the polling mechanism. I have no idea how to do that, though.

PMera.1
Associate II

Ok, I think I figured it out. Feels kinda hacky. In the initialization method shown above, I added the following line. pDataCollection is a void** i added to the USBH_ClassTypeDef.

phost->pActiveClass->pDataCollection[i] = HID_Handles[i];
  }
  return USBH_OK;
}

now, as per Pavel's sugestion, I changed

void MX_USB_HOST_Process(void)  
if(HAL_GetTick() % 2 == 0)
  {
    //USBH_SelectInterface(&hUsbHostHS, 0);
    hUsbHostHS.pActiveClass->pData = hUsbHostHS.pActiveClass->pDataCollection[0];
    USBH_Process(&hUsbHostHS, 0);
 
  }
  else
  {
    //USBH_SelectInterface(&hUsbHostHS, 1);
    hUsbHostHS.pActiveClass->pData = hUsbHostHS.pActiveClass->pDataCollection[1];
    USBH_Process(&hUsbHostHS, 1);
  }

Initially, I believed that simply switching to a different interface would suffice, but apparently that's not the case.

So I manually overwrite pActiveClass with the HID_Handles I get during the initialization phase. Feels hacky and noobish as hell. And I would love to hear your thoughts on how this should be done (properly).