HID Feature
I have implemented a detach feature in a HID device which report descriptor is:
const BYTE IPLB_ReportDescriptor[IPLB_SIZ_REPORT_DESC] = {
0x06, 0xA0, 0xFF, // USAGE_PAGE (Consumer Page) 0x09, 0x01, // USAGE (Consumer Control Usage 0x01) 0xA1, 0x01, // COLLECTION (Application)
//INPUT 0x09, 0x03, 0x15, 0x00, // LOGICAL_MINIMUM(0) 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM(255) 0x75, 0x08, // REPORT_SIZE 0x95, BUFFER_SIZE, // REPORT_COUNT 0x81, 0x02, // INPUT (Data, Variable,Abs)
//OUTPUT 0x09, 0x04, 0x15, 0x00, // LOGICAL_MINIMUM(0) 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM(255) 0x75, 0x08, // REPORT_SIZE 0x95, BUFFER_SIZE, // REPORT_COUNT 0x91, 0x02, // OUTPUT (Data, Variable,Abs)
//Feature 0x09, 0x05, 0x15, 0x00, // LOGICAL_MINIMUM(0) 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM(255) 0x75, 0x08, // REPORT_SIZE 0x95, BUFFER_SIZE, // REPORT_COUNT 0xB1, 0x02, // Feature (Data, Variable,Abs)
//DETACH FEATURE 0x06,0x00,0xFF, 0x09,0x55, 0x15,0x00, 0x26,0xFF,0x00, 0x75,0x08, 0x95,BUFFER_SIZE, 0xB1,0x82, 0xC0 // END_COLLECTION }
In my software I am able to detect the device, to recognize the detach feature ability using:
while ((cnt < pWalk->Caps.NumberFeatureValueCaps) { if (pValue->UsagePage == VENDOR_USAGE_PAGE) { HidDetachFound = true; break; } cnt ++; pValue++; }
but when I send the Set_feature request I have no answer from the device?!
if (!HidD_SetFeature(pWalk->HidDevice, Feature,64)) HandleTxtError("Unable to enter DFU mode: Set Feature HID Detach failed !"); else { Sleep(1000); HandleTxtSuccess("Successfully entered DFU Mode !"); AfxMessageBox("Detach command successful ! Device list refresh will be done...\n\nPlease re-select your device in DFU mode"); Refresh(); }
This set_feature request uses a control transfer (EP0) but debugging in my firmware on the USB_CORE I see no data arriving.
Does someone have an idea where the problem can be, any help will be welcome!
Seems a problem on the report descriptor - or mismatch of the report size on the HidD_SetFeature
Format and attach comment to above report descriptor, so that it can be readable to us, and post it again :-) What is the exact number of BUFFER_SIZE ?
Tsuneo
Tsuneo, Thanks for your answer!
The Buffer Size is 64 (0x40). In the HidD_SetFeature I also use 64 bytes.
0x06, 0xA0, 0xFF,// USAGE_PAGE (Consumer Page)
0x09, 0x01, // USAGE (Consumer Control Usage)
0xA1, 0x01, // COLLECTION (Application)
//INPUT
0x09, 0x03,
0x15, 0x00, // LOGICAL_MINIMUM(0)
0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM(255)
0x75, 0x08, // REPORT_SIZE
0x95, BUFFER_SIZE, // REPORT_COUNT (64)
0x81, 0x02, // INPUT (Data,Variable,Abs)
//OUTPUT
0x09, 0x04,
0x75, 0x08, // REPORT_SIZE (8)
0x91, 0x02, // OUTPUT(Data, Variable,Abs
//Feature
0x09, 0x05,
0xB1, 0x02, // Feature (Data, Variable,Abs)
//DETACH FEATURE
0x06,0x00,0xFF, // VENDOR DEFINED USAGE PAGE
0x09,0x55, // USAGE DETACH
0x15,0x00, // LOGICAL_MINIMUM(0)
0x26,0xFF,0x00, // LOGICAL_MAXIMUM(255)
0x75,0x08, // REPORT_SIZE (8)
0x95,BUFFER_SIZE, // REPORT_COUNT (64)
0xB1,0x82, // Feature (Data, Var,Abs,Vol)
0xC0 // END_COLLECTION
}
This is the your original report descriptor.
const BYTE IPLB_ReportDescriptor[IPLB_SIZ_REPORT_DESC] = { 0x06, 0xA0, 0xFF, // USAGE_PAGE (Vendor Defined page 0xA1) 0x09, 0x01, // USAGE (Vendor Usage 0x01) 0xA1, 0x01, // COLLECTION (Application) // INPUT 0x09, 0x03, // USAGE (Vendor Usage 0x03) 0x15, 0x00, // LOGICAL_MINIMUM(0) 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM(255) 0x75, 0x08, // REPORT_SIZE 0x95, BUFFER_SIZE, // REPORT_COUNT (64) 0x81, 0x02, // INPUT (Data,Variable,Abs) // OUTPUT 0x09, 0x04, // USAGE (Vendor Usage 0x04) 0x15, 0x00, // LOGICAL_MINIMUM(0) 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM(255) 0x75, 0x08, // REPORT_SIZE (8) 0x95, BUFFER_SIZE, // REPORT_COUNT (64) 0x91, 0x02, // OUTPUT(Data, Variable,Abs // Feature 0x09, 0x05, // USAGE (Vendor Usage 0x05) 0x15, 0x00, // LOGICAL_MINIMUM(0) 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM(255) 0x75, 0x08, // REPORT_SIZE (8) 0x95, BUFFER_SIZE, // REPORT_COUNT (64) 0xB1, 0x02, // Feature (Data, Variable,Abs) // DETACH FEATURE 0x06,0x00,0xFF, // USAGE_PAGE (Vendor Defined page 1) 0x09,0x55, // USAGE (Vendor Usage 0x55) - USAGE DETACH 0x15,0x00, // LOGICAL_MINIMUM(0) 0x26,0xFF,0x00, // LOGICAL_MAXIMUM(255) 0x75,0x08, // REPORT_SIZE (8) 0x95,BUFFER_SIZE, // REPORT_COUNT (64) 0xB1,0x82, // Feature (Data, Var,Abs,Vol) 0xC0 // END_COLLECTION }
This report descriptor has no error, but maybe it defines the feature report format differently as you expect.
Input report: 64 bytes Output report: 64 bytes Feature report: 128 bytes
That is, the first "Feature" field joins to the second "Feature" field together, it makes 128 bytes as the total. I think it isn't the result you expect.
Delete the "DETACH FEATURE" part at all, as follows. Then, the descriptor defines just 64 bytes feature.
const BYTE IPLB_ReportDescriptor[IPLB_SIZ_REPORT_DESC] = { 0x06, 0xA0, 0xFF, // USAGE_PAGE (Vendor Defined page 0xA1) 0x09, 0x01, // USAGE (Vendor Usage 0x01) 0xA1, 0x01, // COLLECTION (Application) // INPUT 0x09, 0x03, // USAGE (Vendor Usage 0x03) 0x15, 0x00, // LOGICAL_MINIMUM(0) 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM(255) 0x75, 0x08, // REPORT_SIZE 0x95, BUFFER_SIZE, // REPORT_COUNT (64) 0x81, 0x02, // INPUT (Data,Variable,Abs) // OUTPUT 0x09, 0x04, // USAGE (Vendor Usage 0x04) 0x15, 0x00, // LOGICAL_MINIMUM(0) 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM(255) 0x75, 0x08, // REPORT_SIZE (8) 0x95, BUFFER_SIZE, // REPORT_COUNT (64) 0x91, 0x02, // OUTPUT(Data, Variable,Abs // Feature 0x09, 0x05, // USAGE (Vendor Usage 0x05) 0x15, 0x00, // LOGICAL_MINIMUM(0) 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM(255) 0x75, 0x08, // REPORT_SIZE (8) 0x95, BUFFER_SIZE, // REPORT_COUNT (64) 0xB1, 0x02, // Feature (Data, Variable,Abs) 0xC0 // END_COLLECTION }
Also, you can simplify above descriptor as follows.
const BYTE IPLB_ReportDescriptor[IPLB_SIZ_REPORT_DESC] = { 0x06, 0xA0, 0xFF, // USAGE_PAGE (Vendor Defined page 0xA1) 0x09, 0x01, // USAGE (Vendor Usage 0x01) 0xA1, 0x01, // COLLECTION (Application) // Global items 0x15, 0x00, // LOGICAL_MINIMUM(0) 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM(255) 0x75, 0x08, // REPORT_SIZE 0x95, BUFFER_SIZE, // REPORT_COUNT (64) // INPUT 0x09, 0x03, // USAGE (Vendor Usage 0x03) 0x81, 0x02, // INPUT (Data,Variable,Abs) // OUTPUT 0x09, 0x04, // USAGE (Vendor Usage 0x04) 0x91, 0x02, // OUTPUT(Data, Variable,Abs // Feature 0x09, 0x05, // USAGE (Vendor Usage 0x05) 0xB1, 0x02, // Feature (Data, Variable,Abs) 0xC0 // END_COLLECTION }
On the host app, You have to add default report ID at the top of the buffer
unsigned char Feature[65]; Feature[0] = 0; // default report ID Feature[1] = 0x55; // detach HidD_SetFeature( pWalk->HidDevice, Feature, 65 );
On the firmware, in the Set_Report( Feature ) handler, Above default ID disappears.
if ( Feature[0] == 0x55 ) { // detach command ... }
Aha, you've posted it to USB-IF, too. www.usb.org/.../viewtopic.php
HidD_SetFeature() gives error because ReportBufferLength parameter doesn't match to the Feature report defined in the report descriptor. - in your original report descriptor, the size is 128 bytes. - you have to add 1 to this value, for the default report ID
You may think the default report ID on the host app is redundant, because it is deleted by the HID device driver after all. But it's the Windows way.
Thanks once more Tsuneo.
I have try everything since yesterday. I did as you told me and I start receiving something in my firmware the package is always empty, all 0x00.
I am reading the buffer inside the usb_core.c with:
pProp->RxEP_buffer[i] is always 0
or in this buffer:
pBuf= (WORD*)(GetEPRxAddr(ENDP0)+PMAAddr);
The only good thing is that:
if (pInfo->USBwLength == 0x40) // I get 64 bytes
Before I analyse the package I call this function:
_ClearEP_CTR_RX(ENDP0); // on usb_int.c file /* SETUP bit kept frozen while CTR_RX = 1 */
Is it interfering with something?
Thanks for your help, I am a bit lost now.
"I did as you told me and I start receiving something in my firmware the package is always empty, all 0x00... The only good thing is that: if (pInfo->USBwLength == 0x40) // I get 64 bytes"
Then, you've gotten over the host app problem. Now, on the firmware side.
Maybe, it is the usual pitfall :-) I believe the host sends a right report, but you are handling it in wrong place on the firmware.
For requests carried over control read transfer (ex. Get_Descriptor) or no-data control transfer (ex. Set_Configuration), the requests are handled just after SETUP stage. However, the request over control write transfer like Set_Report, it is handled after DATA stage, too.
What is the base example on which you are making? KEIL example? And which MCU? Post the link to the base example. Then, I'll show you the place where you have to insert your code.
Hi Tsuneo, my project was based in a mouse HID and I added DFU support in order to be able to switch to DFU mode. The uC that I am using is the STR912FA44 and the base example is from ST. I have just posted my project in the ST Forum so that you can help me:
www.st.com/.../forums-cat-7737-21.html
Thanks a lot for all this help!
Are you working on the Custom_HID example? Then, modify it as follows.
usb_prop.c /* Private define ------------------------------------------------------------*/ enum _HID_REPORTS { HID_INPUT = 1, HID_OUTPUT, HID_FEATURE }; /* Private variables ---------------------------------------------------------*/ u8 Request = 0; u8 feature_buf[ BUFFER_SIZE ]; u8 *CustomHID_SetReport(u16); /******************************************************************************* * Function Name : CustomHID_Status_In. * Description : Joystick status IN routine. * Input : None. * Output : None. * Return : None. *******************************************************************************/ void CustomHID_Status_In(void) { if (Request == SET_REPORT) { // // Your Set_Report( Feature ) handler comes here // data is held in feature_buf[] // Request = 0; } } /******************************************************************************* * Function Name : CustomHID_Data_Setup * Description : Handle the data class specific requests. * Input : Request Nb. * Output : None. * Return : USB_UNSUPPORT or USB_SUCCESS. *******************************************************************************/ RESULT CustomHID_Data_Setup(u8 RequestNo) { u8 *(*CopyRoutine)(u16); CopyRoutine = NULL; if ((RequestNo == GET_DESCRIPTOR) && (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT)) && (pInformation->USBwIndex0 == 0)) { if (pInformation->USBwValue1 == REPORT_DESCRIPTOR) { CopyRoutine = CustomHID_GetReportDescriptor; } else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE) { CopyRoutine = CustomHID_GetHIDDescriptor; } } /* End of GET_DESCRIPTOR */ else if ( (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) ) { /*** GET_PROTOCOL ***/ if (RequestNo == GET_PROTOCOL) { CopyRoutine = CustomHID_GetProtocolValue; } /*** SET_REPORT ***/ else if ( (RequestNo == SET_REPORT) && (pInformation->USBwValue1 == HID_FEATURE) ) { CopyRoutine = CustomHID_SetReport; Request = SET_REPORT; } } if (CopyRoutine == NULL) { return USB_UNSUPPORT; } pInformation->Ctrl_Info.CopyData = CopyRoutine; pInformation->Ctrl_Info.Usb_wOffset = 0; (*CopyRoutine)(0); return USB_SUCCESS; } /******************************************************************************* * Function Name : CustomHID_SetReport. * Description : Receive report. * Input : Length. * Output : None. * Return : report buffer base address. *******************************************************************************/ u8 *CustomHID_SetReport(u16 Length) { if (Length == 0) { pInformation->Ctrl_Info.Usb_wLength = sizeof(feature_buf); return NULL; } return feature_buf; }
Auu, sorry, I mistakenly pickup STM32F10x USB library in above post. Almost same, but a little different.
This is revised one.
usb_prop.c /* Private define ------------------------------------------------------------*/ enum _HID_REPORTS { HID_INPUT = 1, HID_OUTPUT, HID_FEATURE }; /* Private variables ---------------------------------------------------------*/ u8 Request = 0; u8 feature_buf[ BUFFER_SIZE ]; u8 *IPLB_SetReport(u16); /******************************************************************************* * Function Name : IPLB_Status_In. * Description : Joystick status IN routine. * Input : None. * Output : None. * Return : None. *******************************************************************************/ void IPLB_Status_In(void) { if (Request == SET_REPORT) { // // Your Set_Report( Feature ) handler comes here // data is held in feature_buf[] // Request = 0; } } RESULT IPLB_Data_Setup(BYTE RequestNo) { DEVICE_INFO *pInfo = &Device_Info; BYTE *(*CopyRoutine)(WORD); BYTE *pbLen; WORD wLen; BSP_ToggleLED(3); CopyRoutine = NULL; if (RequestNo == GET_DESCRIPTOR && Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT)) { if (pInfo->USBwIndex0 != 0) return UNSUPPORT; switch (pInfo->USBwValue1) { case HID_DESCRIPTOR: CopyRoutine = Mouse_GetHidDescriptor; break; case REPORT_DESCRIPTOR: CopyRoutine = Mouse_GetReportDescriptor; break; default: return UNSUPPORT; } } /* End of GET_DESCRIPTOR */ else /*** GET_IDLE/GET_PROTOCOL ***/ if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) { if (RequestNo == GET_IDLE) { /*return the idle time*/ /*not supported here*/ return UNSUPPORT; } else if (RequestNo == GET_PROTOCOL) { CopyRoutine = Mouse_GetProtocolValue; } /*** SET_REPORT ***/ else if ( (RequestNo == SET_REPORT) && (pInfo->USBwValue1 == HID_FEATURE) ) { CopyRoutine = IPLB_SetReport; Request = SET_REPORT; } // else return UNSUPPORT; } // else return UNSUPPORT; if (CopyRoutine == NULL) return UNSUPPORT; pInfo->Ctrl_Info.CopyData = CopyRoutine; pInfo->Ctrl_Info.Usb_wOffset = 0; (*CopyRoutine)(0); // pbLen = (*CopyRoutine)(0); // delete these lines // wLen = (WORD)((DWORD)pbLen); // pInfo->Ctrl_Info.Usb_wLength = wLen; return USB_SUCCESS; } /* IPLB_Data_Setup */ /******************************************************************************* * Function Name : IPLB_SetReport. * Description : Receive report. * Input : Length. * Output : None. * Return : report buffer base address. *******************************************************************************/ u8 *IPLB_SetReport(u16 Length) { if (Length == 0) { pInformation->Ctrl_Info.Usb_wLength = sizeof(feature_buf); return NULL; } return feature_buf; }
Hi Tsuneo!
That's exacly what I was looking for.
Just a small thing, after testing this changes I realise that after deleting there 3 lines according to your instructions, the device is not recognize anymore.
// pbLen = (*CopyRoutine)(0); // delete these lines // wLen = (WORD)((DWORD)pbLen); // pInfo->Ctrl_Info.Usb_wLength = wLen;
and with the lines the firmware never gets to this function, so I can't process the feature ...
void IPLB_Status_In(void) { if (Request == SET_REPORT) { // // Your Set_Report( Feature ) handler comes here // data is held in feature_buf[] // Request = 0; } }
Any idea what can be wrong?
Leave it Tsuneo, I have just solve it with a flag. It is working now! Thanks a lot for all your help and have a nice week!
Milton
Please post more specific information about what you had to do to fix your problem. If you just end this thread with your last post, other people who find this thread will not know about what you did...
Maybe he wasn't aware to modify "(*CopyRoutine)(0);" line, because I missed to highlight it in above post.
RESULT IPLB_Data_Setup(BYTE RequestNo) { ... (*CopyRoutine)(0); // pbLen = (*CopyRoutine)(0); // delete these lines // wLen = (WORD)((DWORD)pbLen); // pInfo->Ctrl_Info.Usb_wLength = wLen; return USB_SUCCESS; } /* IPLB_Data_Setup */
The implementation of HID Set_Report request is same as CDC Set_Line_Coding. Both requests are carried over Control Write transfer.
Actually, I referred the CDC example of the ST USB stack to make above code.
Hi!
I saw the line but it wasn't working anyway. What I did to solve it was:
else if ( (RequestNo == SET_REPORT) && (pInfo->USBwValue1 == HID_FEATURE) ) { CopyRoutine = IPLB_SetReport; Request = SET_REPORT; // detach feature falls here! feature_flag=1; } } if (CopyRoutine == NULL) return UNSUPPORT; pInfo->Ctrl_Info.CopyData = CopyRoutine; pInfo->Ctrl_Info.Usb_wOffset = 0; if (feature_flag==1) { (*CopyRoutine)(0); feature_flag=0; } else { pbLen = (*CopyRoutine)(0); wLen = (WORD)((DWORD)pbLen); pInfo->Ctrl_Info.Usb_wLength = wLen; } return USB_SUCCESS; } /* IPLB_Data_Setup */
So it only uses (*CopyRoutine)(0); if it is a feature request. If I will use it for all requests the device is not recognize anymore ...
Humm.. Seems that the version of the stack is different... I referred the latest stack from ST Micro web page.
In the recent stack, this process is done in the "CopyRoutine", assigned in each request.
pbLen = (*CopyRoutine)(0); wLen = (WORD)((DWORD)pbLen); pInfo->Ctrl_Info.Usb_wLength = wLen;
In your code, the GetProtocol handler is as follows,
BYTE *Mouse_GetProtocolValue(WORD Length) { return (BYTE *)&ProtocolValue; }
In the recent stack (Sep-2008) from ST Micro "STR7/STR9 USB developer kit software" www.st.com/.../um0290.zip
u8 *Joystick_GetProtocolValue(u16 Length) { if (Length == 0) { pInformation->Ctrl_Info.Usb_wLength = 1; return NULL; } else return (u8 *)(&ProtocolValue); }
Maybe they've revised it in the recent stack.