We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Hello,
I am useing an SAM7S256 controller. I want to implement a USB HID communication. I have already succeded to some degree with the Keil sample code for HID.
My problem is the Report descriptor. I want to be able to transmit 1 to 1024 bytes from HOST to DEVICE and DEVICE to HOST. I could define a report descriptor for 1026 bytes and use 2 bytes for the length of the actual data. However I don't want to waste so much bandwidth if there is only a few bytes actual information to send. I came up with the idea to use report IDs and to define say 17 report IDs with sizes from 64-bytes to 1026 bytes. My problem is I don't know how to specify this so it works correctly. Are you aware of a tool to test the descriptors in depth. The Tools I have found on the usb.org page only tell me how many errors there are in my report descriptor but not what exactly the problem is?
My descriptor so far ...
/* HID Report Descriptor */ const BYTE HID_ReportDescriptor[] = { HID_UsagePageVendor(0x00), HID_Usage( 0x01), HID_Collection( HID_Application), // INPUT (DEVICE -> HOST) HID_Collection( HID_Logical), HID_ReportID( 1), HID_Usage( 0x01), HID_LogicalMin( 0), HID_LogicalMax( 255), HID_ReportCount( 64), HID_ReportSize( 8), HID_Input( HID_Data | HID_Variable | HID_Absolute), HID_EndCollection, HID_Collection( HID_Logical), HID_ReportID( 2), HID_Usage( 0x01), HID_LogicalMin( 0), HID_LogicalMax( 255), HID_ReportCount( 128), HID_ReportSize( 8), HID_Input( HID_Data | HID_Variable | HID_Absolute), HID_EndCollection, // INPUT (HOST -> DEVICE) HID_Collection( HID_Logical), HID_ReportID( 1), HID_UsagePage(HID_USAGE_PAGE_GENERIC), HID_Usage(HID_USAGE_GENERIC_COUNTED_BUFFER), HID_LogicalMin(0), HID_LogicalMax(255), HID_ReportCount(64), HID_ReportSize(8), HID_Output(HID_Data | HID_Variable | HID_Absolute), HID_EndCollection, HID_Collection( HID_Logical), HID_ReportID( 2), HID_UsagePage(HID_USAGE_PAGE_GENERIC), HID_Usage(HID_USAGE_GENERIC_COUNTED_BUFFER), HID_LogicalMin(0), HID_LogicalMax(255), HID_ReportCount(128), HID_ReportSize(8), HID_Output(HID_Data | HID_Variable | HID_Absolute), HID_EndCollection, HID_Collection( HID_Logical), HID_ReportID( 3), HID_UsagePage(HID_USAGE_PAGE_GENERIC), HID_Usage(HID_USAGE_GENERIC_COUNTED_BUFFER), HID_LogicalMin(0), HID_LogicalMax(255), HID_ReportCount(192), HID_ReportSize(8), HID_Output(HID_Data | HID_Variable | HID_Absolute), HID_EndCollection, HID_EndCollection, };
1) sub-collections are not required In your report descriptor, you declare sub-collection to each report ID, like
HID_Collection( HID_Logical), ... HID_EndCollection,
Just to declare report ID, these sub-collections are not required. Delete them all.
Just the top-level collection is required.
HID_Collection( HID_Application), HID_EndCollection,
2) Declaration of duplicated report ID is not allowed throughout entire report descriptor. Rather, combine the Input and Output under the same report ID.
3) For Global items, one time declaration is enough You don't need to declare these global items every time, unless you assign another value to them. The value is held once declared. - LogicalMin - LogicalMax - ReportSize
4) Detailed usage is not required For vendor specific HID, there isn't so much advantage to specify the detailed usage.
/* HID Report Descriptor */ const BYTE HID_ReportDescriptor[] = { 0x06, 0x00, 0xff, // USAGE_PAGE (Vendor Defined Page 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, 0x40, // REPORT_COUNT (64) 0x09, 0x01, // USAGE (Vendor Usage 1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x09, 0x01, // USAGE (Vendor Usage 1) 0x91, 0x02, // OUTPUT (Data,Var,Abs) 0x85, 0x02, // REPORT_ID (2) 0x95, 0x80, // REPORT_COUNT (128) 0x09, 0x01, // USAGE (Vendor Usage 1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x09, 0x01, // USAGE (Vendor Usage 1) 0x91, 0x02, // OUTPUT (Data,Var,Abs) 0x85, 0x03, // REPORT_ID (3) 0x95, 0xc0, // REPORT_COUNT (192) 0x09, 0x01, // USAGE (Vendor Usage 1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x09, 0x01, // USAGE (Vendor Usage 1) 0x91, 0x02, // OUTPUT (Data,Var,Abs) ... ... 0xc0 // END_COLLECTION };
Tsuneo
Dear Tsuneo - Thank you.
This all sounds logical and makes it better readable. Unfortinately I didn't get it to work. Do I have to change something else in a different descriptor? In the USB_ConfigDescriptor I am telling: bNumDescriptors is 0x01 and bDescriptorType is HID_REPORT_DESCRIPTOR.
My problem is that if I delete the HID_Usage(0x01) above the HID_Collection(HID_Application) line my PC application (the C++ example from the USB complete book) cannot find my USB Device (my SAM7 ARM uC) anymore - I guess enumeration has failed). If I put in this line with your example at least the OUTPUT - (sending from Host to device) works but I get a Read File Timeout "Can't Read From Device". I have changed the Report ID to 1 (PC application) for test for the OutputReport[0]. I shouldn't need to do it for the InputReport since the PC has to takes what it gets on a request - sholdn't it?
This was the problem with my Report descriptor even before I started to use Report IDs.
The INPUT (device to host) only worked when I used HID_UsagePage(HID_USAGE_PAGE_GENERIC) and HID_Usage(HID_USAGE_GENERIC_COUNTED_BUFFER) together with HID_OUTPUT(...)
I don't know why since I have the same opinion as you that detailed usage is not required ...
Looking forward to hear from you again ... Thank you.
1) HID Descriptor Tool Make up your report descriptor on HID Descriptor Tool, and check it.
"HID Descriptor Tool" on USB.org www.usb.org/.../dt2_4.zip
The GUI of this tool is troublesome to make up a huge report on it. Make up this minimum descriptor on the GUI first.
/* HID Report Descriptor */ const BYTE HID_ReportDescriptor[] = { 0x06, 0x00, 0xff, // USAGE_PAGE (Vendor Defined Page 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, 0x40, // REPORT_COUNT (64) 0x09, 0x01, // USAGE (Vendor Usage 1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x09, 0x01, // USAGE (Vendor Usage 1) 0x91, 0x02, // OUTPUT (Data,Var,Abs) 0xc0 // END_COLLECTION };
The next block is copy-pasted as follows, - Click on REPORT_ID(1) line, and click on OUTPUT(Data,Var,Abs) line with shift key -- above REPORT_ID (1) block is selected - Copy these lines (CTRL-C) - Click on END_COLLECTION line - Paste it (CTRL-V)
To modify the new block, - Double click on REPORT_ID (1) and enter new report ID - Double click on REPORT_COUNT (64) and enter new report count
Repeat this paste and modify process for more blocks - block is already copied, then just paste it.
In this way, you can make up the huge report descriptor in a few minute. When you finish the descriptor, check it using "Parse Descriptor"
As you can see, no HID_Usage(0x01) before the HID_Collection(HID_Application) line is required.
When the descriptor passes the check, save it with "Save As". - File type -- Header File Then, you'll get a text file like above. Copy the contents to your code. 2) Descriptor definitions
"Do I have to change something else in a different descriptor?"
The size of the report descriptor is altered. - wDescriptorLength on the HID class descriptor - Get_Descriptor( HID Report ) handling
Using sizeof() as follows, compiler automatically takes care of it.
#if defined BIG_ENDIAN #define LE(x) ((((x)&0x00FF)<<8)|(((x)&0xFF00)>>8)) // convert to little endian #else #define LE(x) (x) // no conversion #endif /* HID Report Descriptor */ const BYTE HID_ReportDescriptor[] = { // // your descriptor comes here // }; const unsigned int HID_ReportDescriptor_size = sizeof( HID_ReportDescriptor ); // export the size of report descriptor for Get_Descriptor /* Device Descriptor */ ... /* Configuration Descriptor set */ const BYTE USB_ConfigDescriptor[] = { ... { // HID class descriptor sizeof(THID_class_descriptor), // bLength DSC_SUBTYPE_CS_HID_CLASS, // bDescriptorType LE( 0x0111 ), // bcdHID (ver1.11) 0x00, // bCountryCode 0x01, // bNumDescriptors HID_REPORT_DESCRIPTOR, // bDescriptorType LE( sizeof( HID_ReportDescriptor ) ), // wDescriptorLength }, ... };
Dear Tsuneo,
thanks again. I did what you said. But still I didn't get it to work. The strange thing is the USBCV13.exe command line verifier programme - doing a compliance test (chapter 9 Test + HID Test) everything is O.K. The tool even shows me all the Data like Vendor ID, Product ID, String descriptor text. But when I switch back and use USB View to check it only shows me the Device Descriptor and a fragment of the next information block. The device descriptor seems O.K. but the fragment shows low speed etc. which is not O.K.
Device Descriptor: bcdUSB: 0x0110 bDeviceClass: 0x00 bDeviceSubClass: 0x00 bDeviceProtocol: 0x00 bMaxPacketSize0: 0x08 (8) idVendor: 0x197D idProduct: 0x0300 bcdDevice: 0x0100 iManufacturer: 0x04 iProduct: 0x26 iSerialNumber: 0x48 bNumConfigurations: 0x01
ConnectionStatus: DeviceConnected Current Config Value: 0x00 Device Bus Speed: Low Device Address: 0x00 Open Pipes: 0
I have used the simple Version of your ReportDescriptor:
const BYTE HID_ReportDescriptor[] = { 0x06, 0x00, 0xff, // USAGE_PAGE (Vendor Defined Page 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, 0x40, // REPORT_COUNT (64) 0x09, 0x01, // USAGE (Vendor Usage 1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x09, 0x01, // USAGE (Vendor Usage 1) 0x91, 0x02, // OUTPUT (Data,Var,Abs) 0xc0, // END_COLLECTION };
my configuration descriptor:
/* USB Configuration Descriptor */ /* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */ const BYTE USB_ConfigDescriptor[] = { /* Configuration 1 */ USB_CONFIGUARTION_DESC_SIZE, /* bLength */ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */ WBVAL( /* wTotalLength */ USB_CONFIGUARTION_DESC_SIZE + USB_INTERFACE_DESC_SIZE + HID_DESC_SIZE + USB_ENDPOINT_DESC_SIZE*2 ), 0x01, /* bNumInterfaces */ 0x01, /* bConfigurationValue */ 0x00, /* iConfiguration */ USB_CONFIG_BUS_POWERED /*|*/ /* bmAttributes */ /*USB_CONFIG_REMOTE_WAKEUP*/, USB_CONFIG_POWER_MA(100), /* bMaxPower */ /* Interface 0, Alternate Setting 0, HID Class */ USB_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ 0x00, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ 0x02, /* bNumEndpoints */ USB_DEVICE_CLASS_HUMAN_INTERFACE, /* bInterfaceClass */ HID_SUBCLASS_NONE, /* bInterfaceSubClass */ HID_PROTOCOL_NONE, /* bInterfaceProtocol */ 0x6A, /* iInterface */ // HID Class Descriptor // HID_DESC_OFFSET = 0x0012 HID_DESC_SIZE, /* bLength */ HID_HID_DESCRIPTOR_TYPE, /* bDescriptorType */ WBVAL(0x0100), /* 1.00 */ /* bcdHID */ 0x00, /* bCountryCode */ 0x01, /* bNumDescriptors */ HID_REPORT_DESCRIPTOR_TYPE, /* bDescriptorType */ WBVAL(HID_REPORT_DESC_SIZE), /* wDescriptorLength */ // Endpoint, HID Interrupt In * USB_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_IN(3), /* bEndpointAddress */ USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes */ WBVAL(0x0040), /* wMaxPacketSize */ 0x20, /* 32ms */ /* bInterval */ // Endpoint, HID Interrupt Out USB_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_OUT(2), /* bEndpointAddress */ USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes */ WBVAL(0x0040), /* wMaxPacketSize */ 0x20, /* 32ms */ /* bInterval */ /* Terminator */ 0 /* bLength */ };
I really don't know where to look at next. something seems to be wrong somewhere else and there are some side effects which come thru.
I'd be glad if you could help once more - thanks.
I have found out, when I put in the USAGE at the beginning - enumeration works again and the compliance tool also says that my descriptors are O.K..
I can also communicate using my PC application.
/* HID Report Descriptor */ const BYTE HID_ReportDescriptor[] = { 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) 0x75, 0x08, // REPORT_SIZE (8) 0x85, 0x01, // REPORT_ID (1) 0x95, 0x3F, // REPORT_COUNT (63) 0x09, 0x01, // USAGE (Vendor Usage 1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x09, 0x01, // USAGE (Vendor Usage 1) 0x91, 0x02, // OUTPUT (Data,Var,Abs) 0xc0, // END_COLLECTION };
looks good now - thanks Tsuneo ...
Auuu, sorry, you are right.
The usage just before the collection is required.
6.2.2.6 Collection, End Collection Items (HID1_11.pdf p34) "Remarks - Collection items do not generate data. However, a Usage item tag must be associated with any collection (such as a mouse or throttle)."
Even without this Usage, HID Descriptor Tool doesn't put any error, and Windows XP (SP2) accepts the report. Then, I wasn't aware of it.
Thank you for correcting me.