Combining HID devices in one
Posted: Tue Sep 22, 2009 11:12 am
by Urvin
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?
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.
Re: Combining HID devices in one
Posted: Tue Sep 22, 2009 8:48 pm
by Grendel
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.
Re: Combining HID devices in one
Posted: Wed Sep 23, 2009 9:16 am
by Guest
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.
Re: Combining HID devices in one
Posted: Wed Sep 23, 2009 11:31 pm
by Grendel
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).
Re: Combining HID devices in one
Posted: Thu Sep 24, 2009 12:37 am
by herbert12345
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)
Re: Combining HID devices in one
Posted: Tue Sep 29, 2009 12:25 pm
by Urvin
That's great! Now I can emulate keyboard and use volume and playback control.
Thanks!
Re: Combining HID devices in one
Posted: Wed Sep 30, 2009 11:10 am
by Urvin
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?
Re: Combining HID devices in one
Posted: Fri Oct 02, 2009 10:13 am
by Urvin
Please, help me!
I don't know how to solve this problem
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
Re: Combining HID devices in one
Posted: Mon Oct 05, 2009 11:26 am
by Urvin
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.