I'm implementing an HID class device and have based my program on the HID USB program supplied by Keil. I'm new to USB but am a fairly experienced programmer otherwise.
I have got a basic program working fine bit with single byte input and output reports. I've tested the functionality using a client called SimpleHIDWrite.exe
I need to expand both in and out reports to 8 bytes each to communicate with the host. Has anybody successfully modified this example program or does anyone have any advice on how to do it properly?
My guess is that I need to edit the report descriptor and also set up the inreport and outreport as arrays. Is there anything I need to watch out for?
My target is the LPC2141.
Any advice or information would be much appreciated!
Thanks, Gareth.
Here is modification of the KEIL example. In this snippet, we implement, - Interrupt OUT EP - Feature report handling - Loopback of an output report to an input report from interrupt OUT EP to interrupt IN EP - Loopback of a feature report from Set_Report( feature ) to Get_Report( feature )
Jan Axelson's generic_hid_cs is good for the PC test application of this mods (Thanks to Jan! ) "The HID Page" http://www.lvr.com/hidpage.htm generic_hid_cs www.lvr.com/.../generic_hid_cs_46.zip
The snippet below shows just mods on the firmware code.
usbcfg.h #define USB_MAX_PACKET0 64
usbdesc.c #include "hiduser.h" // moved to hiduser.h //#define HID_INPUT_REPORT_BYTES 1 /* size of report in Bytes */ //#define HID_OUTPUT_REPORT_BYTES 1 /* size of report in Bytes */ //#define HID_FEATURE_REPORT_BYTES 1 /* size of report in Bytes */ /* USB Configuration Descriptor */ /* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor) */ const U8 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 + // <-------- USB_ENDPOINT_DESC_SIZE // <-------- increased by OUT EP ), 0x01, /* bNumInterfaces */ 0x01, /* bConfigurationValue: 0x01 is used to select this configuration */ 0x00, /* iConfiguration: no string to describe this configuration */ USB_CONFIG_BUS_POWERED /*|*/ /* bmAttributes */ /*USB_CONFIG_REMOTE_WAKEUP*/, USB_CONFIG_POWER_MA(100), /* bMaxPower, device power consumption is 100 mA */ /* Interface 0, Alternate Setting 0, HID Class */ USB_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ 0x00, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ 0x02, /* bNumEndpoints */ // <-------- increased to 2 EP USB_DEVICE_CLASS_HUMAN_INTERFACE, /* bInterfaceClass */ HID_SUBCLASS_NONE, /* bInterfaceSubClass */ HID_PROTOCOL_NONE, /* bInterfaceProtocol */ 0x04, /* 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(1), /* bEndpointAddress */ USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes */ WBVAL(0x0040), /* wMaxPacketSize */ // <-------- increased to 64 bytes 0x01, /* 1ms */ /* bInterval */ // <-------- changed to 1ms /* Endpoint, HID Interrupt Out */ // <-------- added OUT EP USB_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_OUT(1), /* bEndpointAddress */ USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes */ WBVAL(0x0040), /* wMaxPacketSize */ 0x01, /* 1ms */ /* bInterval */ /* Terminator */ 0 /* bLength */ };
usbuser.c #include <string.h> static OS_SEM USB_IN_EP1_Semaphore; // semaphore for IN EP1 /* * USB Endpoint 1 Task * Handles USB Endpoint 1 Events */ #if (USB_EP_EVENT & (1 << 1)) __task void USB_EndPoint1 (void) { U16 evt; U32 report_size; for (;;) { os_evt_wait_or(0xFFFF, 0xFFFF); /* Wait for an Event */ evt = os_evt_get(); /* Get Event Flags */ if (evt & USB_EVT_OUT) { // OUT EP1 interrupt comes report_size = USB_ReadEP( HID_EP_OUT, OutReport ); // read out the output report // usually, your firmware parse the output report, here // in this snippet, just copy output report to input one directly, for loopback memcpy( InReport, OutReport, report_size ); // send an input report to IN EP os_sem_wait( USB_IN_EP1_Semaphore, 0xffff ); // wait until IN EP is available USB_WriteEP( HID_EP_IN, InReport, HID_INPUT_REPORT_BYTES ); // pass it to the endpoint } if (evt & USB_EVT_IN) { // IN EP1 interrupt comes os_sem_send( USB_IN_EP1_Semaphore ); // IN EP1 is free /* GetInReport(); USB_WriteEP(HID_EP_IN, &InReport, sizeof(InReport)); */ } } } #endif /* * USB Core Task * Handles USB Core Events */ __task void USB_Core (void) { #if (USB_CONFIGURE_EVENT || USB_INTERFACE_EVENT || USB_FEATURE_EVENT) U16 evt; #endif for (;;) { os_evt_wait_or(0xFFFF, 0xFFFF); /* Wait for an Event */ #if (USB_CONFIGURE_EVENT || USB_INTERFACE_EVENT || USB_FEATURE_EVENT) evt = os_evt_get(); /* Get Event Flags */ #endif #if USB_CONFIGURE_EVENT if (evt & USB_EVT_SET_CFG) { if (USB_Configuration) { /* Check if USB is configured */ os_sem_init( USB_IN_EP1_Semaphore, 1 ); // IN EP1 is available now /* GetInReport(); USB_WriteEP(HID_EP_IN, &InReport, sizeof(InReport)); */ } } #endif #if USB_INTERFACE_EVENT if (evt & USB_EVT_SET_IF) { } #endif #if USB_FEATURE_EVENT if (evt & USB_EVT_SET_FEATURE) { } if (evt & USB_EVT_CLR_FEATURE) { } #endif } }
hiduser.h /* HID report size (count) */ #define HID_INPUT_REPORT_BYTES 64 /* size of report in Bytes */ #define HID_OUTPUT_REPORT_BYTES 64 /* size of report in Bytes */ #define HID_FEATURE_REPORT_BYTES 64 /* size of report in Bytes */ /* HID In/Out Endpoint Address */ #define HID_EP_OUT 0x01 #define HID_EP_IN 0x81
hiduser.c #include <string.h> BOOL HID_GetReport (void) { /* ReportID = SetupPacket.wValue.WB.L; */ switch (SetupPacket.wValue.WB.H) { case HID_REPORT_INPUT: GetInReport(); // EP0Buf[0] = InReport; memcpy( EP0Buf, InReport, SetupPacket.wLength ); break; case HID_REPORT_OUTPUT: return (__FALSE); /* Not Supported */ case HID_REPORT_FEATURE: GetFeatureReport(); memcpy( EP0Buf, FeatureReport, SetupPacket.wLength ); break; } return (__TRUE); } BOOL HID_SetReport (void) { /* ReportID = SetupPacket.wValue.WB.L; */ switch (SetupPacket.wValue.WB.H) { case HID_REPORT_INPUT: return (__FALSE); /* Not Supported */ case HID_REPORT_OUTPUT: // OutReport = EP0Buf[0]; memcpy( OutReport, EP0Buf, SetupPacket.wLength ); SetOutReport(); break; case HID_REPORT_FEATURE: memcpy( FeatureReport, EP0Buf, SetupPacket.wLength ); SetFeatureReport(); break; } return (__TRUE); }
demo.h /* HID Demo Variables */ extern U8 InReport[ HID_INPUT_REPORT_BYTES ]; extern U8 OutReport[ HID_OUTPUT_REPORT_BYTES ]; extern U8 FeatureReport[ HID_FEATURE_REPORT_BYTES ]; /* HID Demo Functions */ extern void GetInReport (void); extern void SetOutReport (void); extern void GetFeatureReport (void); extern void SetFeatureReport (void);
demo.c #include "hiduser.h" #include "demo.h" U8 InReport[ HID_INPUT_REPORT_BYTES ]; /* HID Input Report */ U8 OutReport[ HID_OUTPUT_REPORT_BYTES ]; /* HID Out Report */ U8 FeatureReport[ HID_FEATURE_REPORT_BYTES ]; /* HID Feature Report */ /*------------------------------------------------------------------------------ Get HID Input Report -> InReport *------------------------------------------------------------------------------*/ void GetInReport (void) { // this function is called just when Get_Report( input ) request comes // fill InReport[] with an input report, here // if ((FIO2PIN & PB_INT0) == 0) { /* Check if PBINT is pressed */ // InReport = 0x01; // } else { // InReport = 0x00; // } } /*------------------------------------------------------------------------------ Set HID Output Report <- OutReport *------------------------------------------------------------------------------*/ void SetOutReport (void) { // this function is called just when Set_Report( output ) request comes // At the entry of this function, OutReport[] holds an output report from the host // IOPIN1 = (IOPIN1 & ~LED_MSK) | (OutReport << 16); } /*------------------------------------------------------------------------------ Get HID Feature Report -> FeatureReport *------------------------------------------------------------------------------*/ void GetFeatureReport (void) { // this function is called just when Get_Report( feature ) request comes // fill FeatureReport[] with an input report, here } /*------------------------------------------------------------------------------ Set HID Feature Report <- FeatureReport *------------------------------------------------------------------------------*/ void SetFeatureReport (void) { // this function is called just when Set_Report( feature ) request comes // At the entry of this function, FeatureReport[] holds an feature report from the host }
Tsuneo