HID keyboard with backchannel
HID keyboard with backchannel
Hi,
I'm trying to create a HID keyboard based on HIDKeys with a backchannel for sending HID feature reports. I'm having trouble setting up the descriptor for this. Can anybody suggest where to look for something like this? I just need to be able to use this backchannel to send one feature report to issue a reboot of the device to bootloader for configuration and easy firmware upgrades. The most important bit is that communication with the device via this back channel has to work on all platforms: Windows, Linux and OSX.
Any suggestions on which direction I have to go would be highly appreciated.
Regards,
Arvydas
I'm trying to create a HID keyboard based on HIDKeys with a backchannel for sending HID feature reports. I'm having trouble setting up the descriptor for this. Can anybody suggest where to look for something like this? I just need to be able to use this backchannel to send one feature report to issue a reboot of the device to bootloader for configuration and easy firmware upgrades. The most important bit is that communication with the device via this back channel has to work on all platforms: Windows, Linux and OSX.
Any suggestions on which direction I have to go would be highly appreciated.
Regards,
Arvydas
Re: HID keyboard with backchannel
I don't have any experience for your direct question.
If I were adding such a feature I'd use something totally standard so that there was no question of OS support. I've seen some adapters have the user press some certain keystrokes to change modes. For host initiation, it cound send data via the LED indicators, e.g. a certain sequence of 3-bit codes through them puts it into program mode. Perhaps a combination of these, e.g. user holds certain keys and host sends this sequence, only then will it reboot. Less-desirable of course because this reinterprets formerly-valid data as a reboot command.
If I were adding such a feature I'd use something totally standard so that there was no question of OS support. I've seen some adapters have the user press some certain keystrokes to change modes. For host initiation, it cound send data via the LED indicators, e.g. a certain sequence of 3-bit codes through them puts it into program mode. Perhaps a combination of these, e.g. user holds certain keys and host sends this sequence, only then will it reboot. Less-desirable of course because this reinterprets formerly-valid data as a reboot command.
Re: HID keyboard with backchannel
Feature reports are beautiful, I use them a lot myself as you can send/receive data and the device is HID compatible (doesn't require a driver).
Here's a code snippet.
Use the simplehidwrite tool from here http://janaxelson.com/files/SimpleHIDWrite3.zip to eperiment with until you build your own app to talk to the device.
Use "Set Feature" and "Get Feature" in the tool.
Here's a code snippet.
Use the simplehidwrite tool from here http://janaxelson.com/files/SimpleHIDWrite3.zip to eperiment with until you build your own app to talk to the device.
Use "Set Feature" and "Get Feature" in the tool.
Code: Select all
//usbconfig.h
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 49 /* total length of report descriptor */
//main.c
#define FEAT_SIZE 8
static uchar reportBuffer[8]; // buffer for HID reports
static uchar idleRate; // in 4 ms units
static uchar FeatureReportWaiting = 0;
const PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = // USB report descriptor
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard) 06
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x07, // REPORT_COUNT (7)
0x75, 0x08, // REPORT_SIZE (8)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
// Feature report starts here
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8) (8 bits)
0x95, FEAT_SIZE, // REPORT_COUNT (FEAT_SIZE) bytes, 1 of which is the report type
0x09, 0x00, // USAGE (Undefined)
0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf)
0xc0 // END_COLLECTION
};
uchar usbFunctionSetup(uchar data[8])
{
usbRequest_t *rq = (void *)data;
usbMsgPtr = reportBuffer;
// (0x60) (32)
if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) // class request type
{
if(rq->bRequest == USBRQ_HID_GET_REPORT) //(0x01)
{
if (rq->wLength.word == FEAT_SIZE) // only take reports if they are FEAT_SIZE bytes to prevent collision with other reports
{
//put your feature report data in reportBuffer[]
}
return sizeof(reportBuffer);
}
else if (rq->bRequest == USBRQ_HID_GET_IDLE) //(0x02)
{
usbMsgPtr = &idleRate;
return 1;
}
else if (rq->bRequest == USBRQ_HID_SET_IDLE) //(0x0a)
{
idleRate = rq->wValue.bytes[1];
}
else if (rq->bRequest == USBRQ_HID_SET_REPORT) //(0x09)
{
if (rq->wLength.word == FEAT_SIZE) // only take reports if they are FEAT_SIZE bytes
{
FeatureReportWaiting = 1; // safety lock, to make sure that only the intended report is directed to usbFunctionWrite
return 0xff; // tell driver to call usbFunctionWrite with data for us to take
}
}
}
return 0;
}
uchar usbFunctionWrite(uchar *data, uchar len)
{
if (data[1] == 1 && len == FEAT_SIZE)
{
// do something with data[]
FeatureReportWaiting = 0;
}
return 1;
}
Re: HID keyboard with backchannel
There is an easy way for this: to define a second device in the HID descriptor. You simply keep what you have for the HIDkeys, and define another regular HID device. Then the system will see the device as two. I learned this in this forum, but don't remember which post though.
-Xiangrui
-Xiangrui
arvydas wrote:Hi,
I'm trying to create a HID keyboard based on HIDKeys with a backchannel for sending HID feature reports. I'm having trouble setting up the descriptor for this. Can anybody suggest where to look for something like this? I just need to be able to use this backchannel to send one feature report to issue a reboot of the device to bootloader for configuration and easy firmware upgrades. The most important bit is that communication with the device via this back channel has to work on all platforms: Windows, Linux and OSX.
Any suggestions on which direction I have to go would be highly appreciated.
Regards,
Arvydas
Re: HID keyboard with backchannel
xiangrui wrote:There is an easy way for this: to define a second device in the HID descriptor. You simply keep what you have for the HIDkeys, and define another regular HID device. Then the system will see the device as two. I learned this in this forum, but don't remember which post though.
This is exactly what I need, but I just couldn't find a way to do that. Would you be so kind to share some code snippets on how you achieved it? I'll try to search on this forum too.
Arvydas
Re: HID keyboard with backchannel
arvydas wrote:xiangrui wrote:There is an easy way for this: to define a second device in the HID descriptor. You simply keep what you have for the HIDkeys, and define another regular HID device. Then the system will see the device as two. I learned this in this forum, but don't remember which post though.
This is exactly what I need, but I just couldn't find a way to do that. Would you be so kind to share some code snippets on how you achieved it? I'll try to search on this forum too.
Arvydas
This is my usbHidReportDescriptor. Compared to original HIDKeys, I removed the part for control keys etc. I also added ReportID, since the second device uses ReportID. Due to this change, you will need to include ReportID as the first byte of key event report. The second device is flexible according to your needs. Cheers. -Xiangrui
Code: Select all
PROGMEM char usbHidReportDescriptor[] = { /* USB report descriptor */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x85, 0x02, // REPORT_ID (2)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0, // END_COLLECTION
0x06, 0x00, 0xff, // USAGE_PAGE (Generic Desktop)
0x09, 0x01, // USAGE (Vendor Usage 1)
0xa1, 0x01, // COLLECTION (Application)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x85, 0x01, // REPORT_ID (1)
0x95, 0x03, // REPORT_COUNT (3)
0x09, 0x00, // USAGE (Undefined)
0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x00, // USAGE (Undefined)
0x92, 0x02, 0x01, // OUTPUT (Data,Var,Abs,Buf)
0x85, 0x03, // REPORT_ID (3)
0x95, 0x05, // REPORT_COUNT (5)
0x09, 0x00, // USAGE (Undefined)
0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf)
0x85, 0x04, // REPORT_ID (4)
0x09, 0x00, // USAGE (Undefined)
0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf)
0xc0 // END_COLLECTION
};
Re: HID keyboard with backchannel
Is a report ID mandatory when using features? I took the earlier post implying that one could use features with a single device.
Re: HID keyboard with backchannel
blargg wrote:Is a report ID mandatory when using features? I took the earlier post implying that one could use features with a single device.
No, it is optional. But when you want to have different features for different purpose, reportID can be used to distinguish them.
I believe, although I haven't tried, it is okay to use a single device for Linux. But for Windows, it is likely the second non-keyboard device is the easy way to go.
-Xiangrui
Re: HID keyboard with backchannel
I believe, although I haven't tried, it is okay to use a single device for Linux. But for Windows, it is likely the second non-keyboard device is the easy way to go.
Works perfectly well in Windows.
Re: HID keyboard with backchannel
Bob wrote:Works perfectly well in Windows.
Do you mean for single device? That is good to know either way.
I tried it under Windows with PsychHID from PsychToolbox (http://psychtoolbox.org/) under Matlab. I can't access the keyboard due to access privilege. It seems it is due to the limitation of driver or toolkit.
What driver do you use, Bob?
-Xiangrui
Re: HID keyboard with backchannel
Yes, Single device, for example a keyboard.
To access a locked windows device (mouse or keyboard are always locked), you must access it with the appropriate flags of ACCESS_TYPE_NONE when using the CreateFile method using Windows Driver API calls.
This tricks the driver into giving you access.
handle = CreateFile(deviceDetails->DevicePath, ACCESS_TYPE_NONE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlag, NULL);
If using LibUSB I think you use a Control Transfer.
The beauty of Feature and Control transfers is that data can be sent / recieved more often than the minimum poll (10mS for USB 1.1) Interval and acts more like a BulK Transfer.
Perhaps you can try this with PsychHID if these flags and methods are exposed ?
To access a locked windows device (mouse or keyboard are always locked), you must access it with the appropriate flags of ACCESS_TYPE_NONE when using the CreateFile method using Windows Driver API calls.
This tricks the driver into giving you access.
handle = CreateFile(deviceDetails->DevicePath, ACCESS_TYPE_NONE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlag, NULL);
If using LibUSB I think you use a Control Transfer.
The beauty of Feature and Control transfers is that data can be sent / recieved more often than the minimum poll (10mS for USB 1.1) Interval and acts more like a BulK Transfer.
Perhaps you can try this with PsychHID if these flags and methods are exposed ?