2020-04-24 12:29 PM
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?
2020-04-24 05:25 PM
Do you actually poll both interfaces? The code above shows only initialization.
-- pa
2020-04-25 06:47 AM
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.
2020-04-25 09:26 AM
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).