PUSB_INTERFACE_DESCRIPTOR WINAPI USBD_ParseConfigurationDescriptorEx( PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, PVOID StartPosition, LONG InterfaceNumber, LONG AlternateSetting, LONG InterfaceClass, LONG InterfaceSubClass, LONG InterfaceProtocol ) { /* http://blogs.msdn.com/usbcoreblog/archive/2009/12/12/ * what-is-the-right-way-to-validate-and-parse-configuration-descriptors.aspx */ PUSB_INTERFACE_DESCRIPTOR interface; TRACE( "(%p, %p, %d, %d, %d, %d, %d)\n", ConfigurationDescriptor, StartPosition, InterfaceNumber, AlternateSetting, InterfaceClass, InterfaceSubClass, InterfaceProtocol ); interface = (PUSB_INTERFACE_DESCRIPTOR) USBD_ParseDescriptors( ConfigurationDescriptor, ConfigurationDescriptor->wTotalLength, StartPosition, USB_INTERFACE_DESCRIPTOR_TYPE ); while (interface != NULL) { if ((InterfaceNumber == -1 || interface->bInterfaceNumber == InterfaceNumber) && (AlternateSetting == -1 || interface->bAlternateSetting == AlternateSetting) && (InterfaceClass == -1 || interface->bInterfaceClass == InterfaceClass) && (InterfaceSubClass == -1 || interface->bInterfaceSubClass == InterfaceSubClass) && (InterfaceProtocol == -1 || interface->bInterfaceProtocol == InterfaceProtocol)) { return interface; } interface = (PUSB_INTERFACE_DESCRIPTOR) USBD_ParseDescriptors( ConfigurationDescriptor, ConfigurationDescriptor->wTotalLength, interface + 1, USB_INTERFACE_DESCRIPTOR_TYPE ); } return NULL; }
PURB WINAPI USBD_CreateConfigurationRequest( PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, PUSHORT Siz ) { URB *urb = NULL; USBD_INTERFACE_LIST_ENTRY *interfaceList; ULONG interfaceListSize; USB_INTERFACE_DESCRIPTOR *interfaceDesc; int i; TRACE( "(%p, %p)\n", ConfigurationDescriptor, Siz ); /* http://www.microsoft.com/whdc/archive/usbfaq.mspx * claims USBD_CreateConfigurationRequest doesn't support > 1 interface, * but is this on Windows 98 only or all versions? */ *Siz = 0; interfaceListSize = (ConfigurationDescriptor->bNumInterfaces + 1) * sizeof(USBD_INTERFACE_LIST_ENTRY); interfaceList = ExAllocatePool( NonPagedPool, interfaceListSize ); if (interfaceList) { RtlZeroMemory( interfaceList, interfaceListSize ); interfaceDesc = (PUSB_INTERFACE_DESCRIPTOR) USBD_ParseDescriptors( ConfigurationDescriptor, ConfigurationDescriptor->wTotalLength, ConfigurationDescriptor, USB_INTERFACE_DESCRIPTOR_TYPE ); for (i = 0; i < ConfigurationDescriptor->bNumInterfaces && interfaceDesc != NULL; i++) { interfaceList[i].InterfaceDescriptor = interfaceDesc; interfaceDesc = (PUSB_INTERFACE_DESCRIPTOR) USBD_ParseDescriptors( ConfigurationDescriptor, ConfigurationDescriptor->wTotalLength, interfaceDesc + 1, USB_INTERFACE_DESCRIPTOR_TYPE ); } urb = USBD_CreateConfigurationRequestEx( ConfigurationDescriptor, interfaceList ); if (urb) *Siz = urb->u.UrbHeader.Length; ExFreePool( interfaceList ); } return urb; }
PURB WINAPI USBD_CreateConfigurationRequestEx( PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, PUSBD_INTERFACE_LIST_ENTRY InterfaceList ) { URB *urb; ULONG size = 0; USBD_INTERFACE_LIST_ENTRY *interfaceEntry; ULONG interfaceCount = 0; TRACE( "(%p, %p)\n", ConfigurationDescriptor, InterfaceList ); size = sizeof(struct _URB_SELECT_CONFIGURATION); for (interfaceEntry = InterfaceList; interfaceEntry->InterfaceDescriptor; interfaceEntry++) { ++interfaceCount; size += (interfaceEntry->InterfaceDescriptor->bNumEndpoints - 1) * sizeof(USBD_PIPE_INFORMATION); } size += (interfaceCount - 1) * sizeof(USBD_INTERFACE_INFORMATION); urb = ExAllocatePool( NonPagedPool, size ); if (urb) { USBD_INTERFACE_INFORMATION *interfaceInfo; RtlZeroMemory( urb, size ); urb->u.UrbSelectConfiguration.Hdr.Length = size; urb->u.UrbSelectConfiguration.Hdr.Function = URB_FUNCTION_SELECT_CONFIGURATION; urb->u.UrbSelectConfiguration.ConfigurationDescriptor = ConfigurationDescriptor; interfaceInfo = &urb->u.UrbSelectConfiguration.Interface; for (interfaceEntry = InterfaceList; interfaceEntry->InterfaceDescriptor; interfaceEntry++) { int i; USB_INTERFACE_DESCRIPTOR *currentInterface; USB_ENDPOINT_DESCRIPTOR *endpointDescriptor; interfaceInfo->InterfaceNumber = interfaceEntry->InterfaceDescriptor->bInterfaceNumber; interfaceInfo->AlternateSetting = interfaceEntry->InterfaceDescriptor->bAlternateSetting; interfaceInfo->Class = interfaceEntry->InterfaceDescriptor->bInterfaceClass; interfaceInfo->SubClass = interfaceEntry->InterfaceDescriptor->bInterfaceSubClass; interfaceInfo->Protocol = interfaceEntry->InterfaceDescriptor->bInterfaceProtocol; interfaceInfo->NumberOfPipes = interfaceEntry->InterfaceDescriptor->bNumEndpoints; currentInterface = USBD_ParseConfigurationDescriptorEx( ConfigurationDescriptor, ConfigurationDescriptor, interfaceEntry->InterfaceDescriptor->bInterfaceNumber, -1, -1, -1, -1 ); endpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) USBD_ParseDescriptors( ConfigurationDescriptor, ConfigurationDescriptor->wTotalLength, currentInterface, USB_ENDPOINT_DESCRIPTOR_TYPE ); for (i = 0; i < interfaceInfo->NumberOfPipes && endpointDescriptor; i++) { interfaceInfo->Pipes[i].MaximumPacketSize = endpointDescriptor->wMaxPacketSize; interfaceInfo->Pipes[i].EndpointAddress = endpointDescriptor->bEndpointAddress; interfaceInfo->Pipes[i].Interval = endpointDescriptor->bInterval; switch (endpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK) { case USB_ENDPOINT_TYPE_CONTROL: interfaceInfo->Pipes[i].PipeType = UsbdPipeTypeControl; break; case USB_ENDPOINT_TYPE_BULK: interfaceInfo->Pipes[i].PipeType = UsbdPipeTypeBulk; break; case USB_ENDPOINT_TYPE_INTERRUPT: interfaceInfo->Pipes[i].PipeType = UsbdPipeTypeInterrupt; break; case USB_ENDPOINT_TYPE_ISOCHRONOUS: interfaceInfo->Pipes[i].PipeType = UsbdPipeTypeIsochronous; break; } endpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) USBD_ParseDescriptors( ConfigurationDescriptor, ConfigurationDescriptor->wTotalLength, endpointDescriptor + 1, USB_ENDPOINT_DESCRIPTOR_TYPE ); } interfaceInfo->Length = sizeof(USBD_INTERFACE_INFORMATION) + (i - 1) * sizeof(USBD_PIPE_INFORMATION); interfaceEntry->Interface = interfaceInfo; interfaceInfo = (USBD_INTERFACE_INFORMATION*)(((char*)interfaceInfo)+interfaceInfo->Length); } } return urb; }
NTSTATUS StartDevice( PDEVICE_OBJECT fdo, PCM_PARTIAL_RESOURCE_LIST raw, PCM_PARTIAL_RESOURCE_LIST translated ) { USB_CONFIGURATION_DESCRIPTOR tcd; PUSB_CONFIGURATION_DESCRIPTOR pcd; PUSB_STRING_DESCRIPTOR desc; HANDLE RecoveryHandle; PURB selurb; URB urb; NTSTATUS status; status = STATUS_SUCCESS; PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; selurb = NULL; pcd = NULL; // Read our device descriptor. The only real purpose to this would be to find out how many // configurations there are so we can read their descriptors. In this simplest of examples, // there's only one configuration. KdPrint((DRIVERNAME " - Start device\n")); UsbBuildGetDescriptorRequest( &urb, sizeof(_URB_CONTROL_DESCRIPTOR_REQUEST), USB_DEVICE_DESCRIPTOR_TYPE, 0, LangId, &pdx->dd, NULL, sizeof(pdx->dd), NULL ); status = SendAwaitUrb( fdo, &urb ); if(!NT_SUCCESS(status)) { KdPrint((DRIVERNAME " - Error %X trying to retrieve device descriptor\n", status)); goto cleanup; } // allocate the buffer for the descriptor; take extra space to null terminate the bString member desc = (PUSB_STRING_DESCRIPTOR)ExAllocatePoolWithTag(NonPagedPool, sizeof(USB_STRING_DESCRIPTOR) + StringDescriptorBytes, SPOT_TAG ); if(!desc) { KdPrint((DRIVERNAME " - Unable to allocate %X bytes for string descriptor\n", sizeof(USB_STRING_DESCRIPTOR) + StringDescriptorBytes)); status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } pdx->devHash = desc; UsbBuildGetDescriptorRequest(&urb, sizeof(_URB_CONTROL_DESCRIPTOR_REQUEST), USB_STRING_DESCRIPTOR_TYPE, DeviceId, LangId, desc, NULL, sizeof(USB_STRING_DESCRIPTOR) + StringDescriptorBytes, NULL); status = SendAwaitUrb(fdo, &urb); if(!NT_SUCCESS(status)) { KdPrint((DRIVERNAME " - Error %X trying to retrieve string descriptor for DeviceId\n", status)); goto cleanup; } // null terminate the buffer; we allocated one more wchar_t for the purpose desc->bString[ (desc->bLength / 2) - 1 ] = L'\0'; UpdateDeviceInformation( fdo ); // Read the descriptor of the first configuration. This requires two steps. The first step // reads the fixed-size configuration descriptor alone. The second step reads the // configuration descriptor plus all imbedded interface and endpoint descriptors. UsbBuildGetDescriptorRequest(&urb, sizeof(_URB_CONTROL_DESCRIPTOR_REQUEST), USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, LangId, &tcd, NULL, sizeof(tcd), NULL); status = SendAwaitUrb(fdo, &urb); if(!NT_SUCCESS(status)) { KdPrint((DRIVERNAME " - Error %X trying to read configuration descriptor 1\n", status)); goto cleanup; } ULONG size = tcd.wTotalLength; pcd = (PUSB_CONFIGURATION_DESCRIPTOR) ExAllocatePoolWithTag(NonPagedPool, size, SPOT_TAG); if(!pcd) { KdPrint((DRIVERNAME " - Unable to allocate %X bytes for configuration descriptor\n", size)); status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } UsbBuildGetDescriptorRequest(&urb, sizeof(_URB_CONTROL_DESCRIPTOR_REQUEST), USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, LangId, pcd, NULL, size, NULL); status = SendAwaitUrb(fdo, &urb); if(!NT_SUCCESS(status)) { KdPrint((DRIVERNAME " - Error %X trying to read configuration descriptor 1\n", status)); goto cleanup; } // Locate the descriptor for the one and only interface we expect to find PUSB_INTERFACE_DESCRIPTOR pid = USBD_ParseConfigurationDescriptorEx(pcd, pcd, -1, -1, -1, -1, -1); ASSERT(pid); // Create a URB to use in selecting a configuration. USBD_INTERFACE_LIST_ENTRY interfaces[2] = { {pid, NULL}, {NULL, NULL}, // fence to terminate the array }; selurb = USBD_CreateConfigurationRequestEx(pcd, interfaces); if(!selurb) { KdPrint((DRIVERNAME " - Unable to create configuration request\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } // Verify that the interface describes exactly the endpoints we expect if(pid->bNumEndpoints != 2) { KdPrint((DRIVERNAME " - %d is the wrong number of endpoints\n", pid->bNumEndpoints)); status = STATUS_DEVICE_CONFIGURATION_ERROR; goto cleanup; } PUSB_ENDPOINT_DESCRIPTOR ped = (PUSB_ENDPOINT_DESCRIPTOR) pid; ped = (PUSB_ENDPOINT_DESCRIPTOR) USBD_ParseDescriptors(pcd, tcd.wTotalLength, ped, USB_ENDPOINT_DESCRIPTOR_TYPE); if(!ped || 0 == (ped->bEndpointAddress & 0x80) || ped->bmAttributes != USB_ENDPOINT_TYPE_BULK || ped->wMaxPacketSize > 64) { KdPrint((DRIVERNAME " - Endpoint has wrong attributes\n")); status = STATUS_DEVICE_CONFIGURATION_ERROR; goto cleanup; } ++ped; if(!ped || 0 != (ped->bEndpointAddress & 0x80) || ped->bmAttributes != USB_ENDPOINT_TYPE_BULK || ped->wMaxPacketSize > 64) { KdPrint((DRIVERNAME " - Endpoint has wrong attributes\n")); status = STATUS_DEVICE_CONFIGURATION_ERROR; goto cleanup; } ++ped; PUSBD_INTERFACE_INFORMATION pii = interfaces[0].Interface; ASSERT(pii->NumberOfPipes == pid->bNumEndpoints); // Initialize the maximum transfer size for each of the endpoints. The // default would be PAGE_SIZE. The firmware itself only has a 4096-byte // ring buffer, though. We need to restrict the test applet to that many // bytes. In order to exercise the multi-segment aspect of the transfer code, // therefore, reduce the maximum transfer size to 1024 bytes. pii->Pipes[0].MaximumTransferSize = USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE; pii->Pipes[1].MaximumTransferSize = USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE; pdx->maxtransfer = USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE; // Submit the set-configuration request status = SendAwaitUrb(fdo, selurb); if(!NT_SUCCESS(status)) { KdPrint((DRIVERNAME " - Error %X trying to select configuration\n", status)); goto cleanup; } // Save the configuration and pipe handles pdx->hconfig = selurb->UrbSelectConfiguration.ConfigurationHandle; pdx->hinpipe = pii->Pipes[0].PipeHandle; pdx->houtpipe = pii->Pipes[1].PipeHandle; // Transfer ownership of the configuration descriptor to the device extension pdx->pcd = pcd; pcd = NULL; // Enable the interface IoSetDeviceInterfaceState(&pdx->operationsInterfaceName, TRUE); // Enable the interface IoSetDeviceInterfaceState(&pdx->inquiriesInterfaceName, TRUE); // create recovery thread status = PsCreateSystemThread(&RecoveryHandle, 0, NULL, NULL, NULL, RecoveryThread, (PVOID)pdx); if(!NT_SUCCESS(status)) { KdPrint((DRIVERNAME " - PsCreateSystemThread failed with error %08x\n", status)); goto cleanup; } status = ObReferenceObjectByHandle( RecoveryHandle, SYNCHRONIZE, NULL, KernelMode, (PVOID*)&pdx->RecoveryThread, NULL ); ASSERT(NT_SUCCESS(status)); ZwClose(RecoveryHandle); // Start polling status = StartPolling(pdx); if(!NT_SUCCESS(status)) { KdPrint((DRIVERNAME " - StartPolling failed 0x%08x\n", status)); if(pdx->RecoveryThread) { // shutdown recovery thread pdx->RecoveryExit = TRUE; KeSetEvent(&pdx->RecoveryEvent, 0, FALSE); // wait for polling thread to exit KeWaitForSingleObject(pdx->RecoveryThread, Executive, KernelMode, FALSE, NULL); ObDereferenceObject(pdx->RecoveryThread); pdx->RecoveryThread = NULL; } goto cleanup; } cleanup: if(selurb) ExFreePool(selurb); if(pcd ) ExFreePool(pcd ); // get rid of return codes like STATUS_PENDING if(NT_SUCCESS(status)) { return STATUS_SUCCESS; } return status; }