2018-11-01 11:19 AM
I have my custom HID device running and working fine. I would like it to also emulate a standard PC keyboard at the same time. So when my board enumerates, I would like the attached PC to find my custom device as well as a keyboard.
I think I can just add another device config descriptor, interface and end points to the existing custom HID descriptors and then add another report descriptor for the keyboard. Please confirm.
I then tried of setting up two complete sets of descriptors and calling USBD_init, USBD_RegisterClass and USBD_Start for each set of descriptors. I tried this and was never able to get the keyboard to enumerate, could be any of many reasons. Also, it looked like it was killing the first device, my custom HID.
Not being sure how to do this, I would appreciate advice. If both ways would work, then I guess I would use the first as it would require fewer lines of code.
I hesitate to try to the first way I described above as adding all the device descriptors, etc, are a lot of work to get them all aligned.
Thank you.
Jerry
2018-11-03 06:55 PM
Apologies for delay. Jerry, if you take variant A, there will be only one interface, no need to change USBD_HID_Setup. All the difference is in the HID report descriptor.
Below is example of HID report descriptor with 3 collections: Keyboard, application keys and mouse.
You can keep the keyboard, maybe app. keys too, and replace the last one with your encoder.
__ALIGN_BEGIN uint8_t CUSTOM_HID_ReportDesc_FS[] __ALIGN_END =
{
// KEYBOARD
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID (1)
/* Modifier keys, 1 byte */
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE7, // Usage Maximum (0xE7)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
/* 6 bytes key codes */
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x7f, // Logical Maximum (127)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0x00, // Usage Minimum (0x00)
0x29, 0x7f, // Usage Maximum (0x7f)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
// App keys
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x02, // Report ID (2)
0x05, 0x0C, // Usage Page (Consumer)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x09, 0xB5, // Usage (Scan Next Track)
0x09, 0xB6, // Usage (Scan Previous Track)
0x09, 0xB7, // Usage (Stop)
0x09, 0xB8, // Usage (Eject)
0x09, 0xCD, // Usage (Play/Pause)
0x09, 0xE2, // Usage (Mute)
0x09, 0xE9, // Usage (Volume Increment)
0x09, 0xEA, // Usage (Volume Decrement)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x03, // Report ID (3)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // Usage (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
};
-- pa
2018-11-03 07:11 PM
And the config descriptor:
__ALIGN_BEGIN uint8_t USBD_CUSTOM_HID_CfgDesc[USB_CUSTOM_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
USB_CUSTOM_HID_CONFIG_DESC_SIZ, /* wTotalLength */
0x00,
0x01, /*bNumInterfaces: 1 interface*/
0x01, /*bConfigurationValue: Configuration value*/
0x00, /*iConfiguration: no string*/
0xC0, /*bmAttributes: bus powered */
0x32, /*MaxPower 100 mA */
/************** Descriptor of interface #0 ****************/
/* 09 */
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE, /*bDescriptorType: Interface*/
0x00, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x02, /*bNumEndpoints*/
0x03, /*bInterfaceClass: HID*/
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: Index of interface string descriptor*/
/******************** Descriptor of CUSTOM_HID *************************/
/* 18 */
0x09, /*bLength: HID Descriptor size*/
CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
0x11, /*bHID: HID Class Spec release number 1.11*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of CUSTOM_HID class descriptors to follow*/
0x22, /*bDescriptorType*/
sizeof(CUSTOM_HID_ReportDesc_FS), /*wItemLength*/
0x00,
/******************** Descriptor of Custom HID endpoints ********************/
/* 27 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
CUSTOM_HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
CUSTOM_HID_EPIN_SIZE, /*wMaxPacketSize */
0x00,
0x20, /*bInterval: Polling Interval (32 ms)*/
/* 34 */
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: */
CUSTOM_HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (OUT)*/
0x03, /* bmAttributes: Interrupt endpoint */
CUSTOM_HID_EPOUT_SIZE, /* wMaxPacketSize */
0x00,
0x20, /* bInterval: Polling Interval (32 ms) */
/* 41 */
};
-- pa
2018-11-03 09:57 PM
Pavel, Thank you. But here's what I don't understand. The custom application looks for the device based on the VID/PID. When I have my collection, the application is finding two devices. The first I assume is my encoder as I had it first in the collection. The second is the keyboard which it doesn't understand. The VID is 0C26 and the PID is 001E. Most keyboards and HID collections I've seen are like 045E, 046d, etc.
The other issue in my coding was that my keyboard ended up being interface 0x01 but the encoder requires a 0x01 in all incoming packets in position[0]. So I keep thinking I need to have a separate device descriptor for the encoder (0c26/001e) and the keyboard (046D/C52b) for example. Otherwise the encoder application and DLL must be grabbing both the encoder and keyboard.
I'll look through your example but correct me if I'm wrong, my application won't find the encoder as in your device descriptor, your don't have an address coded and I would think I would need the descriptor to look like this:
0x12 bLength
0x01 bDescriptorType
0x0200 bcdUSB
0x00 bDeviceClass
0x00 bDeviceSubClass
0x00 bDeviceProtocol
0x40 bMaxPacketSize0 (64 bytes)
0x0C26 idVendor
0x001E idProduct
0x0001 bcdDevice
0x01 iManufacturer "Hanler Inc."
0x02 iProduct "HANLER REMOTE ENCODER"
0x03 iSerialNumber "HE-XX 01010101" // Test device serial number
0x01 bNumConfigurations
Note that this descriptor is running stand alone (not a collection) in this case.
Thanks again for the reply. I am so close, yet really stuck. I didn't write the application code or HIDCTRL.DLL.
Jerry
2018-11-04 12:55 AM
Pavel, can you also send me your devicedesciptor, string table and hid descriptor? Also, in USBD_HID.c they have the HID separated out as in:
/* USB HID device Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_HID_Desc[USB_HID_DESC_SIZ] __ALIGN_END =
{
/* 18 */
0x09, /*bLength: HID Descriptor size*/
0x21, /*bDescriptorType: HID*/
0x11, /*bcdHID: HID Class Spec release number*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
0x22, /*bDescriptorType*/
0x2d, /*was 2d wItemLength: Total length of Report descriptor*/
0x00,
};
It is read and returned separately.
Thanks again but I think I am hitting a wall because of the custom application and DLL. I was thinking that even if I get the keyboard working, it will only work with the transceiver unless the PC application has focus. This isn't a bad thing.
Icom makes a pricey rotary encoder that attaches to both their radios and PC remote control software. I've reverse engineered it and have the encoder functions working great. I want a small 8 button keypad to integrate into the device on the touchscreen. I am, or was, hoping to use a USB keyboard. The alternative is to use FET switches and a second cable that runs to the back of the radio. I don't see this as clean when the USB encoder is already attached. If I can just get both the encoder working and the keypad I can wrap this up and decide what to do with it commercially, if at all, or just give it away to colleagues.
Thanks for your help along with Ben.
Jerry
2018-11-04 12:26 PM
> The custom application looks for the device based on the VID/PID.
Assuming it is Windows application and you don't have custom drivers - vendor and product ID do not matter, Windows recognizes HID devices entirely based on USB class.
If class & subclass are 0s in device descriptor, they are specified in the interface descriptor.
I don't know what is HIDCTRL.DLL. It is not a in-box DLL of Windows. If it does not understand HID-enumerated devices (collection-based) or report IDs, and wants entire device or function - too bad. In that case maybe a composite device design (plan B) could help.
-- pa
2018-11-04 12:36 PM
Here is the device descriptor:
__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
{
0x12, /*bLength */
USB_DESC_TYPE_DEVICE, /*bDescriptorType*/
0x00, /*bcdUSB */
0x02,
0x00, /*bDeviceClass*/
0x00, /*bDeviceSubClass*/
0x00, /*bDeviceProtocol*/
USB_MAX_EP0_SIZE, /*bMaxPacketSize*/
LOBYTE(USBD_VID), /*idVendor*/
HIBYTE(USBD_VID), /*idVendor*/
LOBYTE(USBD_PID), /*idProduct*/
HIBYTE(USBD_PID), /*idProduct*/
0x00, /*bcdDevice rel. 2.00*/
0x02,
USBD_IDX_MFC_STR, /*Index of manufacturer string*/
USBD_IDX_PRODUCT_STR, /*Index of product string*/
USBD_IDX_SERIAL_STR, /*Index of serial number string*/
USBD_MAX_NUM_CONFIGURATION /*bNumConfigurations*/
};
2018-11-04 02:54 PM
Thx. I am going to take another crack at using your descriptors. Today I built a simple keyboard project and kept changing things towards making it the encoder and I've gotten pretty far and the driver isn't grabbing the keyboard.
2018-11-05 02:18 PM
Pavel, Ben, Here's where I am:
I looked at Pavel's code and then tried this:
Descriptor
Config Desc with 2 interfaces
Interface for Keyboard
Hid for Keyboard
End Point for Keyboard
Interface for encoder
HID for encoder
Endpoint for encoder
Endpoint for encoder
My code is below. It matches a set of descriptors almost exactly coded for a PIC that another person did. I need the second HID to as the class descriptors between the keyboard and encoder are different. I also need to set string IDs differently for the keyboard and encoder. The PC never gets to the point where it reads the report descriptors. It just sends requests for the Device Descriptor and Config descriptors three times and quits with an error. I am going to paste my descriptors below but at this point I am at a loss. I'll keep messing with it, there must be something wrong with the config descriptor itself but given it never reads the reports, what could it be? I've been over the settings and must have tried a hundred times with traces and dumps.
If anyone can glance at it, I would appreciate it.
Here's my Device Descriptor:
0x12, /*bLength */
USB_DESC_TYPE_DEVICE,
0x02,
0x00,
0x00,
0x00,
0x00,
USB_MAX_EP0_SIZE,
LOBYTE(USBD_VID),
HIBYTE(USBD_VID),
LOBYTE(USBD_PID_FS),
HIBYTE(USBD_PID_FS),
0x02,
0x00,
USBD_IDX_MFC_STR,
USBD_IDX_PRODUCT_STR,
USBD_IDX_SERIAL_STR,
0x01
And config desciptor:
__ALIGN_BEGIN static uint8_t USBD_HID_CfgDesc[66] __ALIGN_END =
{
0x09,
USB_DESC_TYPE_CONFIGURATION,
0x42, /* wTotalLength: Bytes returned 66 bytes */
0x00,
0x02,
0x01,
0x00,
0xE0,
0x32,
/************** Descriptor of keyboard interface ****************/
/* 09 */
0x09,
USB_DESC_TYPE_INTERFACE,
0x00,
0x00,
0x01,
0x03,
0x01,
0x01,
0x02,
/******************** Descriptor of keyboard HID ********************/
/* 18 */
0x09,
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
0x11,
0x01,
0x00,
0x01,
0x22,
0x3f,
0x00,
/* 27 */
0x07,
USB_DESC_TYPE_ENDPOINT,
HID_EPIN_ADDR,
0x03,
HID_EPIN_SIZE,
0x00,
HID_FS_BINTERVAL,
/* 34 */
/************** Descriptor of encoder interface ****************/
0x09,
0x04,
0x01,
0x00,
0x02,
0x03,
0x01,
0x00,
0x02,
/******************** Descriptor of encoder HID ********************/
/* 09 */
0x09,
0x21,
0x11,
0x01,
0x00,
0x01,
0x22,
0x2e,
0x00,
/******************** Descriptor of encoder In endpoint ********************/
/* 18 */
0x07,
0x05,
0x81,
0x03,
0x40,
0x00,
0x01,
/* 25 */
0x07,
0x05,
0x01,
0x03,
0x40,
0x00,
0x01,
/* 32 */
// total is 34 + 32 == 66
} ;
Thank you! I really appreciate the help.
Jerry
2018-11-05 04:03 PM
By the way, the device descriptor has the 0x02 in byte 2 one byte higher due to a screwup when I copied it in.
2018-11-06 03:09 PM
don't know why my last didn't come thru...
When I plugged it into another computer the device enumerated and the keyboard is working. This is great. The encoder isn't working. The encoder is the second interface. When I traced the enumeration, it looks like the report descriptors are being requested three times. In all cases, the report returned is the combined report. Should the stm board be returning the report for each interface or both at once?
I think this is clearly progress as the config descriptor now looks fine. Just need to know what the board should be returning in a composite - the entire combined or just the individual reports.
Thanks for all the help, I feel like progress has been made!
Jerry