Combining HID devices in one

General discussions about V-USB, our firmware-only implementation of a low speed USB device on Atmel's AVR microcontrollers
Post Reply
Urvin
Posts: 16
Joined: Wed Sep 16, 2009 3:26 pm

Combining HID devices in one

Post by Urvin » Tue Sep 22, 2009 11:12 am

Hi!
I'm a beginner with V-USB, but I have great plans! I decided to begin my learning process with modifying code of EasyLogger. So, I want to create a USB multimedia keyboard on ATTity45 microcontroller. The multimedia functions I wanna use are volume control and a 'mute' button.

I tried to send keycodes and modifiers and... it works! Going deeper, i've changed a line of HID descriptor to

Code: Select all

0x25, 0x85,                    //   LOGICAL_MAXIMUM (133)
to use external keycodes (referencing usb.org hid document), and decided if I send decimal 128 (Keyboard Volume Up) the volume should increment... No effect in Windows, nor Ubuntu.

Ok, I found another way, microsoft documents say:

Code: Select all

Consumer Page Audio Controls Supported in Windows Operating Systems
0xE2    Mute
0xE9    Volume Increment
0xEA    Volume Decrement

and there is an example of usage in hid header:

Code: Select all

Usage Page (Consumer)
Usage (Consumer Control)
Collection (Application)
    Logical Minimum (0)
    Logical Maximum (1)
    Usage (Volume Increment)
    Usage (Volume Decrement)
    Report Size (1)
    Report Count (2)
    Input (Data, Variable, Absolute, Preferred)
    Usage (Mute)
    Report Count (1)
    Input (Data, Variable, Relative, Preferred)
    Report Count (5)
    Input (Constant)
End Collection


And the question is... How can I combine and use keyboard and volume control together? :roll:

The next step is to combine that with the third hid device, that sends two bytes to computer and receives about 40 bytes. My own win application should work with this device.

Grendel
Rank 4
Rank 4
Posts: 167
Joined: Sat Dec 16, 2006 9:53 pm
Location: Oregon, USA
Contact:

Re: Combining HID devices in one

Post by Grendel » Tue Sep 22, 2009 8:48 pm

Urvin wrote:I tried to send keycodes and modifiers and... it works! Going deeper, i've changed a line of HID descriptor to

Code: Select all

0x25, 0x85,                    //   LOGICAL_MAXIMUM (133)
to use external keycodes (referencing usb.org hid document), and decided if I send decimal 128 (Keyboard Volume Up) the volume should increment... No effect in Windows, nor Ubuntu.

Most values in the report descriptor are signed, 0x85 is -123. For 133 as logical max use:

Code: Select all

0x26, 0x85, 0x00,          // LOGICAL_MAXIMUM (133)

Note that the report descripter size increases by 1.

Guest

Re: Combining HID devices in one

Post by Guest » Wed Sep 23, 2009 9:16 am

I've changed hid header to this

Code: Select all

    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x06,                    // USAGE (Keyboard)
    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, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x26, 0xa4, 0x00,              //   LOGICAL_MAXIMUM (164)
    0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0xa4,                    //   USAGE_MAXIMUM (Keyboard ExSel)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0xc0                           // END_COLLECTION


It still works like a char keyboard, but no media effect obtained.

Grendel
Rank 4
Rank 4
Posts: 167
Joined: Sat Dec 16, 2006 9:53 pm
Location: Oregon, USA
Contact:

Re: Combining HID devices in one

Post by Grendel » Wed Sep 23, 2009 11:31 pm

Urvin wrote:..if I send decimal 128 (Keyboard Volume Up) the volume should increment... No effect in Windows, nor Ubuntu.

Ok, I found another way, microsoft documents say:

Code: Select all

Consumer Page Audio Controls Supported in Windows Operating Systems
0xE2    Mute
0xE9    Volume Increment
0xEA    Volume Decrement

Which one are you using ? Should be the ones defined by the HID Usage Tables document (0x7F, 0x80, 0x81).

Also, for Windows (XP) check the following:

- Is your "HID Input Service" running ? If not, start it and set it to "Automatic".
- If the service was running or you get an error starting it, check if the "ServiceDll" registry entry in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HidServ\Parameters is %SystemRoot%\System32\hidserv.dll (type is REG_EXPAND_SZ).

herbert12345
Posts: 1
Joined: Wed Sep 23, 2009 1:18 pm

Re: Combining HID devices in one

Post by herbert12345 » Thu Sep 24, 2009 12:37 am

You need an additional collection in your report descriptor:

PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] =
{ // USB report descriptor
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x01, // REPORT_ID (1)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard RightGUI)
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, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (101)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0, // END_COLLECTION

0x05, 0x0c, // USAGE_PAGE (Consumer Devices)
0x09, 0x01, // USAGE (Consumer Control)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x02, // REPORT_ID (2)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x09, 0xe9, // USAGE (Volume Up)
0x09, 0xea, // USAGE (Volume Down)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0xe2, // USAGE (Mute)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x06, // INPUT (Data,Var,Rel)
0x95, 0x05, // REPORT_COUNT (5)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0xc0 // END_COLLECTION
};

This solution works well on my pc running WinXP and my MacBook running SnowLeopard.
(you have to change your main code a little bit to support the 2 report ids)

Urvin
Posts: 16
Joined: Wed Sep 16, 2009 3:26 pm

Re: Combining HID devices in one

Post by Urvin » Tue Sep 29, 2009 12:25 pm

Image
That's great! Now I can emulate keyboard and use volume and playback control.
Thanks!

Urvin
Posts: 16
Joined: Wed Sep 16, 2009 3:26 pm

Re: Combining HID devices in one

Post by Urvin » Wed Sep 30, 2009 11:10 am

Now I'm trying to teach my device how to transfer data, like hiddata example does.

This is a part of hid descriptor:

Code: Select all

    0x06, 0x00, 0xff,              // USAGE_PAGE (Vendor Defined Page 1)
    0x09, 0x01,                    // USAGE (Vendor Usage 1)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x85, 0x04,                    //   REPORT_ID (4)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x80,                    //   REPORT_COUNT (128)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0xc0                           // END_COLLECTION


Through I have many arrays, i combine them using pointer array. These are variables and data array init function:

Code: Select all

static uchar usbreportid = 4;
static uchar*   dataout[128];
static uchar    currentAddress;
static uchar    bytesRemaining;

static uchar buttonsADC[32];
static uchar keycodes[76];
static uchar canHoldButtons[17];

static void dataoutInit(void)
{
   uchar i=0;
   
   // 0
   dataout[0] = &usbreportid;
   
   //32
   for(i=0; i<32; i++)
   {
      dataout[i+1] = &buttonsADC[i];
   }
   
   // 48
   for(i=0; i<16; i++)
   {
      dataout[i+33] = &canHoldButtons[i+1];
   }
   
   // 124
   for(i=0; i<76; i++)
   {
      dataout[i+ 49] = &keycodes[i];
   }
}


usbFunctionSetup (without keyboard and volume control part)

Code: Select all

uchar   usbFunctionSetup(uchar data[8])
{
usbRequest_t    *rq = (void *)data;
   
    if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){    /* class request type */
        if(rq->bRequest == USBRQ_HID_GET_REPORT){  /* wValue: ReportType (highbyte), ReportID (lowbyte) */
         if (rq->wValue.bytes[0] == 1)
                        // ...
         else if (rq->wValue.bytes[0] == 4) // If data transfer report
         {
            bytesRemaining = 128;
            currentAddress = 0;
            return USB_NO_MSG;
         }
         
         return 0;
      }
      else if(rq->bRequest == USBRQ_HID_SET_REPORT)
      {
         if (rq->wValue.bytes[0] == 4)
         {
            bytesRemaining = 128;
            currentAddress = 0;
            return USB_NO_MSG;
         }
      }
        else if(rq->bRequest == USBRQ_HID_GET_IDLE)
   {
            usbMsgPtr = &idleRate;
            return 1;
        }
   else if(rq->bRequest == USBRQ_HID_SET_IDLE)
   {
            idleRate = rq->wValue.bytes[1];
        }
    }
   else
   {
        /* no vendor specific requests implemented */
    }
   return 0;
}


and usbFunctionRead:

Code: Select all

uchar usbFunctionRead(uchar *data, uchar len)
{
   if(len > bytesRemaining)
        len = bytesRemaining;
   
   uchar i=0;
   
   for (i=0; i<len; i++)
   {
      if (currentAddress + i <= 124)
      {
         data[i] = *dataout[currentAddress + i];
      }
      else
      {
         data[i] = 123; // Just meaningless bytes
      }
   }
   
   currentAddress += len;
        bytesRemaining -= len;

   return len;
}


Host application function call:

Code: Select all

#define USBREPORTBUFFERLEN 129
#define USB_REPORT_ID 4

usbDevice_t* usbDevice;
char usbReportBuffer[USBREPORTBUFFERLEN];

void read_hid_device()
{
    int len = USBREPORTBUFFERLEN;

    int err = usbhidGetReport(usbDevice, USB_REPORT_ID, usbReportBuffer, &len);

    if (err != 0)
    {
        cout<<"Error reading data: "<<usbErrorMessage(err)<<endl;  // usbErrorMessage returns string with error description
    }
    else
    {
        //success
    }
}



In my host application I can connect to device, but if I want to read device data I always get "Error reading data: communication error with device".

What I'm doing wrong?

Urvin
Posts: 16
Joined: Wed Sep 16, 2009 3:26 pm

Re: Combining HID devices in one

Post by Urvin » Fri Oct 02, 2009 10:13 am

Please, help me!
I don't know how to solve this problem :cry: But I know that this is not a proplem of pc software - it works pretty with hid-data firmware.
I think that problem should be around report ids...

I promise to buy a hobbyist license if this would work :)

Urvin
Posts: 16
Joined: Wed Sep 16, 2009 3:26 pm

Re: Combining HID devices in one

Post by Urvin » Mon Oct 05, 2009 11:26 am

Would it be better if I share the whole commented sources?
I still can't solve the problem.

I don't want to use libusb because of some problems with installing it on different (win) computers and I don't need a hi-speed transfer.

Post Reply