2025-03-22 4:51 AM - edited 2025-03-22 12:29 PM
I've read many posts here and on the web, even tried CoPilot, but I could not find how to get the USB controller to send data when the volume keys are pressed.
My Host keyboard driver works perfectly, but USBH_LL_GetURBState() is not returning with data for anything but "standard" key presses.
After many hours invested due to a total lack of documentation from STM, I found that the report descriptor of 146 bytes is read back by USBH_HID_GetHIDReportDescriptor() but the "consumer device" entry is not used.
But if STM had one support person publish a short guide it would save many developers many hours.
USBH_StatusTypeDef USBH_HID_KeybdInit(USBH_HandleTypeDef *phost)
{
uint32_t x;
HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) phost->pActiveClass->pData;
keybd_info.lctrl = 0U;
keybd_info.lshift = 0U;
keybd_info.lalt = 0U;
keybd_info.lgui = 0U;
keybd_info.rctrl = 0U;
keybd_info.rshift = 0U;
keybd_info.ralt = 0U;
keybd_info.rgui = 0U;
for (x = 0U; x < sizeof(keybd_report_data); x++)
{
keybd_report_data[x] = 0U;
keybd_rx_report_buf[x] = 0U;
}
if (HID_Handle->length > (sizeof(keybd_report_data)))
{
HID_Handle->length = (uint16_t)(sizeof(keybd_report_data));
}
HID_Handle->pData = keybd_rx_report_buf;
if ((HID_QUEUE_SIZE * sizeof(keybd_report_data)) > sizeof(phost->device.Data))
{
return USBH_FAIL;
}
else
{
** in the line below, I think it needs to point to the report table 65 bytes in for the "consumer devices" usage:-
USBH_HID_FifoInit(&HID_Handle->fifo, phost->device.Data, (uint16_t)(HID_QUEUE_SIZE * sizeof(keybd_report_data)));
}
return USBH_OK;
}
const char USB_CLASS_SPECIFIC_DESC[] = {
//hid report descriptor for interface 1 (keyboard)
0x05, 0x01, //usage page (generic desktop) //52, 53
0x09, 0x06, //usage (keyboard) //54, 55
0xA1, 0x01, //collection (application) //56, 57
0x85, 0x01, /* Report ID1 */
0x05, 0x07, //usage page (key codes) //58, 59
0x19, 0xE0, //usage min (224) //60, 61
0x29, 0xE7, //usage max (231) //62, 63
0x15, 0x00, //logical min (0) //64, 65
0x25, 0x01, //logical max (1) //66, 67
0x75, 0x01, //report size (1) //68, 69
0x95, 0x08, //report count (8) //70, 71
0x81, 0x02, //input (data, variable, absolute) [modifier byte] //72, 73
0x95, 0x01, //report count (1) //74, 75
0x75, 0x08, //report size (8) //76, 77
0x81, 0x01, //input (constant) [reserved byte] //78, 79
0x95, 0x05, //report count (5) //80, 81
0x75, 0x01, //report size (1) //82, 83
0x05, 0x08, //usage page (page# for leds) //84, 85
0x19, 0x01, //usage min (1) //86, 87
0x29, 0x05, //usage max (5) //88, 89
0x91, 0x02, //output (data, var, abs) [led report] //90, 91
0x95, 0x01, //report count (1) //92, 93
0x75, 0x03, //report size (3) //94, 95
0x91, 0x01, //output (constant) [led report padding] //96, 97
0x95, 0x05, //report count (5) //98, 99
0x75, 0x08, //report size (8) //100, 101
0x15, 0x00, //logical min (0) //102, 103
0x25, 0x65, //logical max (101) //104, 105
0x05, 0x07, //usage page (key codes) //106, 107
0x19, 0x00, //usage min (0) //108, 109
0x29, 0x65, //usage max (101) //110, 111
0x81, 0x00, //input (data, array) //112, 113
0xC0, //end collection //114
//
0x05, 0x0C, /* Usage Page (Consumer Devices) */
0x09, 0x01, /* Usage (Consumer Control) */
0xA1, 0x01, /* Collection (Application) */
0x85, 0x02, /* Report ID=2 */
0x05, 0x0C, /* Usage Page (Consumer Devices) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x07, /* Report Count (7) */
0x09, 0xB5, /* Usage (Scan Next Track) */
0x09, 0xB6, /* Usage (Scan Previous Track) */
0x09, 0xB7, /* Usage (Stop) */
0x09, 0xCD, /* Usage (Play / Pause) */
0x09, 0xE2, /* Usage (Mute) */
0x09, 0xE9, /* Usage (Volume Up) */
0x09, 0xEA, /* Usage (Volume Down) */
0x81, 0x02, /* Input (Data, Variable, Absolute) */
0x95, 0x01, /* Report Count (1) */
0x81, 0x01, /* Input (Constant) */
0xC0,
2025-03-22 1:42 PM
The set idle call seems to be important to maintain dataflow, is this change going to be needed to get the volume key data to arrive?
case USBH_HID_REQ_SET_IDLE:
// First Set_Idle for standard keyboard Report ID (0x00)
classReqStatus = USBH_HID_SetIdle(phost, 0U, 0x00); // Send Set_Idle request
if (classReqStatus == USBH_OK) {
HID_Handle->ctl_state = USBH_HID_REQ_SET_IDLE_CONSUMER; // Move to next state
} else if (classReqStatus == USBH_NOT_SUPPORTED) {
HID_Handle->ctl_state = USBH_HID_REQ_SET_IDLE_CONSUMER; // Skip and continue
}
break;
case USBH_HID_REQ_SET_IDLE_CONSUMER:
// Second Set_Idle for Consumer Control Report ID (e.g., 0x02)
classReqStatus = USBH_HID_SetIdle(phost, 0U, 0x02);
if (classReqStatus == USBH_OK) {
HID_Handle->ctl_state = USBH_HID_REQ_SET_PROTOCOL; // Proceed to Set Protocol state
} else if (classReqStatus == USBH_NOT_SUPPORTED) {
HID_Handle->ctl_state = USBH_HID_REQ_SET_PROTOCOL; // Skip and continue
}
break;
2025-03-23 3:51 PM - edited 2025-03-23 3:54 PM
The USBH_HID_SetProtocol is important to set the "report" mode.
Does your code call USBH_HID_SetProtocol(hpost, 0) successfully?
/* surprise! this function inverts the 2nd arg so 0 means the "report protocol", USB numeric value 1. And v.v. 1 means the "boot" protocol, numeric value 0 */
2025-03-24 2:56 AM - edited 2025-03-24 2:56 AM
Yes it does, returning USBH_OK ***:-
case USBH_HID_REQ_SET_PROTOCOL:
/* set protocol */
classReqStatus = USBH_HID_SetProtocol(phost, 0U);
if (classReqStatus == USBH_OK)
{
HID_Handle->ctl_state = USBH_HID_REQ_IDLE; ***
So the meaning here is that the STM USB keyboard driver uses boot protocol mode, meaning basic keyboard as required at boot up, but for more complex keyboards, report mode is needed?
You're the expert on this, is there not a single example of how to implement the reading of the volume (consumer) keys?
2025-03-24 5:41 AM
So I changed the SetProtocol mode to reports, and I changed setIdle too, but there is still no response to the consumer keys, and strangely the standard keys still work!
Is there a bug in the H743VIT6 Synopsis USB controller or am I still missing some key fact?
I would happily pay $$ someone to get this working!!!