Hello,
I've managed to get the tivaware usblib modified to successfully respond to Microsoft's OS string descriptor - which is a non-standard descriptor type. A screenshot of this USB transaction exchange is shown below. This descriptor exchange is denoted by the EE in the data0 setup packet.
This descriptor exchange looks like it matches all the standard descriptor exchanges perfectly. See a standard one below.
Now, the issue I'm having is that after windows sends an OS string descriptor it sends a compatible ID descriptor. I respond to this descriptor request the same way and the response fails. I have been testing different ways to modify the usblib to respond and they all seem to fail in the same way as shown below.
The error I get on the protocol analyzer states "Invalid PID sequence" and elaborates "an invalid sequence of packets has been observed".
I've tried using the following function to respond with no luck. I do however see that the DATA1 of the "IN" packet has the correct data. I'm trying to send the following descriptor response.
const uint8_t g_pui8MicrosoftCompatibleIDFeatureDesc[] = //uint8_t g_pui8MicrosoftCompatibleIDFeatureDesc[] = { 0x28, 0x00, 0x00, 0x00, // Desc Length 0x00, 0x01, // Version '1.0' 0x04, 0x00, // Compatibility ID descriptor index 0x0004 0x01, // Number of Sections 0x00, 0x00, 0x00, 0x00, // RESERVED 0x00, 0x00, 0x00, // RESERVED 0x00, // Interface Number (Interface #0) 0x01, // RESERVED 0x57, 0x49, 0x4E, 0x55, // (8 bytes) Compatibility ID "WINUSB\0\0" 0x53, 0x42, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, // (8 bytes) Sub-Compatible ID (UNUSED) 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, // (6 bytes) RESERVED 0x00, 0x00, // };
with this function:
USBDCDSendDataEP0(0, g_pui8MicrosoftCompatibleIDFeatureDesc, 40);
Currently for testing I've also tried the following to respond.
// // Need to ACK the data on end point 0 without setting last data as there // will be a data phase. // MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); // // // Return the externally specified MS OS string descriptor. // g_psDCDInst[0].pui8EP0Data = (uint8_t *)g_pui8MicrosoftCompatibleIDFeatureDesc; // // The total size of a string descriptor is in byte 0. // g_psDCDInst[0].ui32EP0DataRemain = g_pui8MicrosoftCompatibleIDFeatureDesc[0]; // // Save the total size of the data sent. // g_psDCDInst[0].ui32OUTDataSize = g_pui8MicrosoftCompatibleIDFeatureDesc[0]; USBDEP0StateTx(0);
These code snippets are found in the following function in the usbdenum.c file.
//***************************************************************************** // // This internal function reads a request data packet and dispatches it to // either a standard request handler or the registered device request // callback depending upon the request type. // // \return None. // //***************************************************************************** static void USBDReadAndDispatchRequest(uint32_t ui32Index) { uint32_t ui32Size; tUSBRequest *psRequest; // // Cast the buffer to a request structure. // psRequest = (tUSBRequest *)g_pui8DataBufferIn; // // Set the buffer size. // ui32Size = EP0_MAX_PACKET_SIZE; // // Get the data from the USB controller end point 0. // MAP_USBEndpointDataGet(USB0_BASE, USB_EP_0, g_pui8DataBufferIn, &ui32Size); // // If there was a null setup packet then just return. // if(!ui32Size) { return; } // // See if this is a standard request or not. // if((psRequest->bmRequestType & USB_RTYPE_TYPE_M) != USB_RTYPE_STANDARD) { // // Since this is not a standard request, see if there is // an external handler present. // if(g_ppsDevInfo[0]->psCallbacks->pfnRequestHandler) { // !!RV!! maybe pass entire g_psDCDInst instead of just pvCBData to // get access to EP0 stuff. - doesn't work due to casting but can // add the main g_... pointer to pvCBData, but usbdbulk.c doesn't // know about g_psDCDInst data type so can't use it. //g_psDCDInst[0].pvCBData = (void*)g_psDCDInst; //g_ppsDevInfo[0]->psCallbacks->pfnRequestHandler( // g_psDCDInst[0].pvCBData, // psRequest); //USBDCDSendDataEP0(0, g_pui8MicrosoftCompatibleIDFeatureDesc, 40); //USBDCDStallEP0(0); // // Need to ACK the data on end point 0 without setting last data as there // will be a data phase. // MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); // // // Return the externally specified MS OS string descriptor. // g_psDCDInst[0].pui8EP0Data = (uint8_t *)g_pui8MicrosoftCompatibleIDFeatureDesc; // // The total size of a string descriptor is in byte 0. // g_psDCDInst[0].ui32EP0DataRemain = g_pui8MicrosoftCompatibleIDFeatureDesc[0]; // // Save the total size of the data sent. // g_psDCDInst[0].ui32OUTDataSize = g_pui8MicrosoftCompatibleIDFeatureDesc[0]; USBDEP0StateTx(0); } else { // // If there is no handler then stall this request. // USBDCDStallEP0(0); } } else { // // Assure that the jump table is not out of bounds. // if((psRequest->bRequest < (sizeof(g_psUSBDStdRequests) / sizeof(tStdRequest))) && (g_psUSBDStdRequests[psRequest->bRequest] != 0)) { // // Jump table to the appropriate handler. // g_psUSBDStdRequests[psRequest->bRequest](&g_psDCDInst[0], psRequest); } else { // // If there is no handler then stall this request. // USBDCDStallEP0(0); } } }
I've also tried using the handler/callback approach, but because this isn't working I'm simplifying the situation to add the code I need to respond to this descriptor directly in the dispatch handler functinon call area for non-standard descriptors. Does anyone know why these packet/PID sequence would be out of order with these (rough at the moment) code mods to handle the descriptor requests?
It seem strange that handling the OS descriptor (first one) works fine with the following code. You can see here I added an if statement to find the 0xEE value and respond to this descriptor in the string handler case towards the bottom.
//***************************************************************************** // // This function handles the GET_DESCRIPTOR standard USB request. // // \param pvInstance is the USB device controller instance data. // \param psUSBRequest holds the data for this request. // // This function will return most of the descriptors requested by the host // controller. The descriptor specified by \e // pvInstance->psInfo->pui8DeviceDescriptor will be returned when the device // descriptor is requested. If a request for a specific configuration // descriptor is made, then the appropriate descriptor from the \e // g_pConfigDescriptors will be returned. When a request for a string // descriptor is made, the appropriate string from the // \e pvInstance->psInfo->pStringDescriptors will be returned. If the // \e pvInstance->psInfo->psCallbacks->GetDescriptor is specified it will be // called to handle the request. In this case it must call the // USBDCDSendDataEP0() function to send the data to the host controller. If // the callback is not specified, and the descriptor request is not for a // device, configuration, or string descriptor then this function will stall // the request to indicate that the request was not supported by the device. // // \return None. // //***************************************************************************** static void USBDGetDescriptor(void *pvInstance, tUSBRequest *psUSBRequest) { bool bConfig; tDCDInstance *psUSBControl; tDeviceInfo *psDevice; const tConfigHeader *psConfig; const tDeviceDescriptor *psDeviceDesc; uint8_t ui8Index; int32_t i32Index; ASSERT(psUSBRequest != 0); ASSERT(pvInstance != 0); // // Create the device information pointer. // psUSBControl = (tDCDInstance *)pvInstance; psDevice = g_ppsDevInfo[0]; // // Need to ACK the data on end point 0 without setting last data as there // will be a data phase. // MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); // // Assume we are not sending the configuration descriptor until we // determine otherwise. // bConfig = false; // // Which descriptor are we being asked for? // switch(psUSBRequest->wValue >> 8) { // // This request was for a device descriptor. // case USB_DTYPE_DEVICE: { // // Return the externally provided device descriptor. // psUSBControl->pui8EP0Data = (uint8_t *)psDevice->pui8DeviceDescriptor; // // The size of the device descriptor is in the first byte. // psUSBControl->ui32EP0DataRemain = psDevice->pui8DeviceDescriptor[0]; break; } // // This request was for a configuration descriptor. // case USB_DTYPE_CONFIGURATION: { // // Which configuration are we being asked for? // ui8Index = (uint8_t)(psUSBRequest->wValue & 0xFF); // // Is this valid? // psDeviceDesc = (const tDeviceDescriptor *)psDevice->pui8DeviceDescriptor; if(ui8Index >= psDeviceDesc->bNumConfigurations) { // // This is an invalid configuration index. Stall EP0 to // indicate a request error. // USBDCDStallEP0(0); psUSBControl->pui8EP0Data = 0; psUSBControl->ui32EP0DataRemain = 0; } else { // // Return the externally specified configuration descriptor. // psConfig = psDevice->ppsConfigDescriptors[ui8Index]; // // Start by sending data from the beginning of the first // descriptor. // psUSBControl->ui8ConfigSection = 0; psUSBControl->ui16SectionOffset = 0; psUSBControl->pui8EP0Data = (uint8_t *)psConfig->psSections[0]->pui8Data; // // Determine the total size of the configuration descriptor // by counting the sizes of the sections comprising it. // psUSBControl->ui32EP0DataRemain = USBDCDConfigDescGetSize(psConfig); // // Remember that we need to send the configuration descriptor // and which descriptor we need to send. // psUSBControl->ui8ConfigIndex = ui8Index; bConfig = true; } break; } // // This request was for a string descriptor. // case USB_DTYPE_STRING: { // // Determine the correct descriptor index based on the requested // language ID and index. // i32Index = USBDStringIndexFromRequest(psUSBRequest->wIndex, psUSBRequest->wValue & 0xFF); // // If the mapping function returned -1 then stall the request to // indicate that the request was not valid. // if(i32Index == -1) { USBDCDStallEP0(0); break; } // // !!RV!! // Check for microsoft OS string descriptor and if found // handle. Break immediatley after handling. // if(i32Index == 0xEE) { // // // Return the externally specified MS OS string descriptor. // psUSBControl->pui8EP0Data = (uint8_t *)g_pui8MicrosoftOSString; // // The total size of a string descriptor is in byte 0. // psUSBControl->ui32EP0DataRemain = g_pui8MicrosoftOSString[0]; //USBDCDStallEP0(0); break; } // // // Return the externally specified configuration descriptor. // psUSBControl->pui8EP0Data = (uint8_t *)psDevice->ppui8StringDescriptors[i32Index]; // // The total size of a string descriptor is in byte 0. // psUSBControl->ui32EP0DataRemain = psDevice->ppui8StringDescriptors[i32Index][0]; break; } // // Any other request is not handled by the default enumeration handler // so see if it needs to be passed on to another handler. // default: { // // If there is a handler for requests that are not handled then // call it. // if(psDevice->psCallbacks->pfnGetDescriptor) { psDevice->psCallbacks->pfnGetDescriptor(g_psDCDInst[0].pvCBData, psUSBRequest); } else { // // Whatever this was this handler does not understand it so // just stall the request. // USBDCDStallEP0(0); } return; } } // // If this request has data to send, then send it. // if(psUSBControl->pui8EP0Data) { // // If there is more data to send than is requested then just // send the requested amount of data. // if(psUSBControl->ui32EP0DataRemain > psUSBRequest->wLength) { psUSBControl->ui32EP0DataRemain = psUSBRequest->wLength; } // // Now in the transmit data state. Be careful to call the correct // function since we need to handle the configuration descriptor // differently from the others. // if(!bConfig) { USBDEP0StateTx(0); } else { USBDEP0StateTxConfig(0); } } }
So I'm stuck here trying to debug this issue of the non-standard descriptor handling not working. Even TI's documentation says the first function should be able to handle this "I think".
Any help is greatly appreciated!