HID keyboard with backchannel

General discussions about V-USB, our firmware-only implementation of a low speed USB device on Atmel's AVR microcontrollers
Post Reply
arvydas
Posts: 3
Joined: Fri Oct 31, 2014 5:55 pm

HID keyboard with backchannel

Post by arvydas » Fri Oct 31, 2014 6:13 pm

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

blargg
Rank 3
Rank 3
Posts: 102
Joined: Thu Nov 14, 2013 10:01 pm

Re: HID keyboard with backchannel

Post by blargg » Fri Oct 31, 2014 11:24 pm

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.

Bob
Posts: 17
Joined: Sun Jun 10, 2007 7:10 pm
Location: Melbourne

Re: HID keyboard with backchannel

Post by Bob » Sat Nov 08, 2014 1:39 pm

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.

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;
}


xiangrui
Rank 1
Rank 1
Posts: 30
Joined: Sun Jan 01, 2012 5:19 am

Re: HID keyboard with backchannel

Post by xiangrui » Tue Nov 11, 2014 12:41 am

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


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

arvydas
Posts: 3
Joined: Fri Oct 31, 2014 5:55 pm

Re: HID keyboard with backchannel

Post by arvydas » Thu Nov 13, 2014 10:06 pm

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

xiangrui
Rank 1
Rank 1
Posts: 30
Joined: Sun Jan 01, 2012 5:19 am

Re: HID keyboard with backchannel

Post by xiangrui » Fri Nov 14, 2014 7:20 pm

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
};

blargg
Rank 3
Rank 3
Posts: 102
Joined: Thu Nov 14, 2013 10:01 pm

Re: HID keyboard with backchannel

Post by blargg » Fri Nov 14, 2014 8:53 pm

Is a report ID mandatory when using features? I took the earlier post implying that one could use features with a single device.

xiangrui
Rank 1
Rank 1
Posts: 30
Joined: Sun Jan 01, 2012 5:19 am

Re: HID keyboard with backchannel

Post by xiangrui » Tue Nov 18, 2014 9:52 pm

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

Bob
Posts: 17
Joined: Sun Jun 10, 2007 7:10 pm
Location: Melbourne

Re: HID keyboard with backchannel

Post by Bob » Wed Nov 19, 2014 1:49 pm

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.

xiangrui
Rank 1
Rank 1
Posts: 30
Joined: Sun Jan 01, 2012 5:19 am

Re: HID keyboard with backchannel

Post by xiangrui » Wed Nov 19, 2014 4:03 pm

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

Bob
Posts: 17
Joined: Sun Jun 10, 2007 7:10 pm
Location: Melbourne

Re: HID keyboard with backchannel

Post by Bob » Wed Nov 19, 2014 10:44 pm

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 ?

Post Reply