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!
"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.
Tsuneo
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.