/*++ Copyright (c) 1996-2001 Microsoft Corporation Module Name: USB.C Abstract: This source file contains the functions for communicating with the usb bus. Environment: kernel mode Revision History: Sep 2001 : Copied from USBMASS --*/ //***************************************************************************** // I N C L U D E S //***************************************************************************** #include "genusb.h" //***************************************************************************** // L O C A L F U N C T I O N P R O T O T Y P E S //***************************************************************************** #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, GenUSB_GetDescriptor) #pragma alloc_text(PAGE, GenUSB_GetDescriptors) #pragma alloc_text(PAGE, GenUSB_GetStringDescriptors) #pragma alloc_text(PAGE, GenUSB_VendorControlRequest) #pragma alloc_text(PAGE, GenUSB_ResetPipe) #endif //****************************************************************************** // // GenUSB_GetDescriptors() // // This routine is called at START_DEVICE time for the FDO to retrieve the // Device and Configurations descriptors from the device and store them in // the device extension. // //****************************************************************************** NTSTATUS GenUSB_GetDescriptors ( IN PDEVICE_OBJECT DeviceObject ) { PDEVICE_EXTENSION deviceExtension; PUCHAR descriptor; ULONG descriptorLength; NTSTATUS status; PAGED_CODE(); DBGPRINT(2, ("enter: GenUSB_GetDescriptors\n")); deviceExtension = DeviceObject->DeviceExtension; descriptor = NULL; LOGENTRY(deviceExtension, 'GDSC', DeviceObject, 0, 0); // // Get Device Descriptor // status = GenUSB_GetDescriptor(DeviceObject, USB_RECIPIENT_DEVICE, USB_DEVICE_DESCRIPTOR_TYPE, 0, // Index 0, // LanguageId 2, // RetryCount sizeof(USB_DEVICE_DESCRIPTOR), &descriptor); if (!NT_SUCCESS(status)) { DBGPRINT(1, ("Get Device Descriptor failed\n")); goto GenUSB_GetDescriptorsDone; } ASSERT(NULL == deviceExtension->DeviceDescriptor); deviceExtension->DeviceDescriptor = (PUSB_DEVICE_DESCRIPTOR)descriptor; descriptor = NULL; // // Get Configuration Descriptor (just the Configuration Descriptor) // status = GenUSB_GetDescriptor(DeviceObject, USB_RECIPIENT_DEVICE, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, // Index 0, // LanguageId 2, // RetryCount sizeof(USB_CONFIGURATION_DESCRIPTOR), &descriptor); if (!NT_SUCCESS(status)) { DBGPRINT(1, ("Get Configuration Descriptor failed (1)\n")); goto GenUSB_GetDescriptorsDone; } descriptorLength = ((PUSB_CONFIGURATION_DESCRIPTOR)descriptor)->wTotalLength; ExFreePool(descriptor); descriptor = NULL; if (descriptorLength < sizeof(USB_CONFIGURATION_DESCRIPTOR)) { status = STATUS_DEVICE_DATA_ERROR; DBGPRINT(1, ("Get Configuration Descriptor failed (2)\n")); goto GenUSB_GetDescriptorsDone; } // // Get Configuration Descriptor (and Interface and Endpoint Descriptors) // status = GenUSB_GetDescriptor(DeviceObject, USB_RECIPIENT_DEVICE, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, // Index 0, // LanguageId 2, // RetryCount descriptorLength, &descriptor); if (!NT_SUCCESS(status)) { DBGPRINT(1, ("Get Configuration Descriptor failed (3)\n")); goto GenUSB_GetDescriptorsDone; } ASSERT(NULL == deviceExtension->ConfigurationDescriptor); deviceExtension->ConfigurationDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)descriptor; // // Get the Serial Number String Descriptor, if there is one // if (deviceExtension->DeviceDescriptor->iSerialNumber) { GenUSB_GetStringDescriptors(DeviceObject); } #if DBG DumpDeviceDesc(deviceExtension->DeviceDescriptor); DumpConfigDesc(deviceExtension->ConfigurationDescriptor); #endif GenUSB_GetDescriptorsDone: DBGPRINT(2, ("exit: GenUSB_GetDescriptors %08X\n", status)); LOGENTRY(deviceExtension, 'gdsc', status, deviceExtension->DeviceDescriptor, deviceExtension->ConfigurationDescriptor); return status; } //****************************************************************************** // // GenUSB_GetDescriptor() // // Must be called at IRQL PASSIVE_LEVEL // //****************************************************************************** NTSTATUS GenUSB_GetDescriptor ( IN PDEVICE_OBJECT DeviceObject, IN UCHAR Function, IN UCHAR DescriptorType, IN UCHAR Index, IN USHORT LanguageId, IN ULONG RetryCount, IN ULONG DescriptorLength, OUT PUCHAR *Descriptor ) { PURB urb; NTSTATUS status; BOOLEAN descriptorAllocated = FALSE; PAGED_CODE(); DBGPRINT(2, ("enter: GenUSB_GetDescriptor\n")); if (NULL == *Descriptor) { // Allocate a descriptor buffer *Descriptor = ExAllocatePool(NonPagedPool, DescriptorLength); descriptorAllocated = TRUE; } if (NULL != *Descriptor) { // Allocate a URB for the Get Descriptor request urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST)); if (NULL != urb) { do { // Initialize the URB urb->UrbHeader.Function = Function; urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST); urb->UrbControlDescriptorRequest.TransferBufferLength = DescriptorLength; urb->UrbControlDescriptorRequest.TransferBuffer = *Descriptor; urb->UrbControlDescriptorRequest.TransferBufferMDL = NULL; urb->UrbControlDescriptorRequest.UrbLink = NULL; urb->UrbControlDescriptorRequest.DescriptorType = DescriptorType; urb->UrbControlDescriptorRequest.Index = Index; urb->UrbControlDescriptorRequest.LanguageId = LanguageId; // Send the URB down the stack status = GenUSB_SyncSendUsbRequest(DeviceObject, urb); if (NT_SUCCESS(status)) { // No error, make sure the length and type are correct if ((DescriptorLength == urb->UrbControlDescriptorRequest.TransferBufferLength) && (DescriptorType == ((PUSB_COMMON_DESCRIPTOR)*Descriptor)->bDescriptorType)) { // The length and type are correct, all done break; } else { // No error, but the length or type is incorrect status = STATUS_DEVICE_DATA_ERROR; } } } while (RetryCount-- > 0); ExFreePool(urb); } else { // Failed to allocate the URB status = STATUS_INSUFFICIENT_RESOURCES; } } else { // Failed to allocate the descriptor buffer status = STATUS_INSUFFICIENT_RESOURCES; } if (!NT_SUCCESS(status)) { if ((*Descriptor != NULL) && descriptorAllocated) { ExFreePool(*Descriptor); *Descriptor = NULL; } } DBGPRINT(2, ("exit: GenUSB_GetDescriptor %08X\n", status)); return status; } //****************************************************************************** // // GenUSB_GetStringDescriptors() // // This routine is called at START_DEVICE time for the FDO to retrieve the // Serial Number string descriptor from the device and store it in // the device extension. // //****************************************************************************** GenUSB_GetStringDescriptors ( IN PDEVICE_OBJECT DeviceObject ) { PDEVICE_EXTENSION deviceExtension; PUCHAR descriptor; ULONG descriptorLength; ULONG i, numIds; NTSTATUS status; PAGED_CODE(); DBGPRINT(2, ("enter: GenUSB_GetStringDescriptors\n")); deviceExtension = DeviceObject->DeviceExtension; descriptor = NULL; LOGENTRY(deviceExtension, 'GSDC', DeviceObject, 0, 0); // Get the list of Language IDs (descriptor header only) status = GenUSB_GetDescriptor(DeviceObject, USB_RECIPIENT_DEVICE, USB_STRING_DESCRIPTOR_TYPE, 0, // Index 0, // LanguageId 2, // RetryCount sizeof(USB_COMMON_DESCRIPTOR), &descriptor); if (!NT_SUCCESS(status)) { DBGPRINT(1, ("Get Language IDs failed (1) %08X\n", status)); goto GenUSB_GetStringDescriptorsDone; } descriptorLength = ((PUSB_COMMON_DESCRIPTOR)descriptor)->bLength; ExFreePool(descriptor); descriptor = NULL; if ((descriptorLength < sizeof(USB_COMMON_DESCRIPTOR) + sizeof(USHORT)) || (descriptorLength & 1)) { status = STATUS_DEVICE_DATA_ERROR; DBGPRINT(1, ("Get Language IDs failed (2) %d\n", descriptorLength)); goto GenUSB_GetStringDescriptorsDone; } // Get the list of Language IDs (complete descriptor) status = GenUSB_GetDescriptor(DeviceObject, USB_RECIPIENT_DEVICE, USB_STRING_DESCRIPTOR_TYPE, 0, // Index 0, // LanguageId 2, // RetryCount descriptorLength, &descriptor); if (!NT_SUCCESS(status)) { DBGPRINT(1, ("Get Language IDs failed (3) %08X\n", status)); goto GenUSB_GetStringDescriptorsDone; } // Search the list of LanguageIDs for US-English (0x0409). If we find // it in the list, that's the LanguageID we'll use. Else just default // to the first LanguageID in the list. numIds = (descriptorLength - sizeof(USB_COMMON_DESCRIPTOR)) / sizeof(USHORT); deviceExtension->LanguageId = ((PUSHORT)descriptor)[1]; for (i = 2; i <= numIds; i++) { if (((PUSHORT)descriptor)[i] == 0x0409) { deviceExtension->LanguageId = 0x0409; break; } } ExFreePool(descriptor); descriptor = NULL; // // Get the Serial Number (descriptor header only) // status = GenUSB_GetDescriptor(DeviceObject, USB_RECIPIENT_DEVICE, USB_STRING_DESCRIPTOR_TYPE, deviceExtension->DeviceDescriptor->iSerialNumber, deviceExtension->LanguageId, 2, // RetryCount sizeof(USB_COMMON_DESCRIPTOR), &descriptor); if (!NT_SUCCESS(status)) { DBGPRINT(1, ("Get Serial Number failed (1) %08X\n", status)); goto GenUSB_GetStringDescriptorsDone; } descriptorLength = ((PUSB_COMMON_DESCRIPTOR)descriptor)->bLength; ExFreePool(descriptor); descriptor = NULL; if ((descriptorLength < sizeof(USB_COMMON_DESCRIPTOR) + sizeof(USHORT)) || (descriptorLength & 1)) { status = STATUS_DEVICE_DATA_ERROR; DBGPRINT(1, ("Get Serial Number failed (2) %d\n", descriptorLength)); goto GenUSB_GetStringDescriptorsDone; } // // Get the Serial Number (complete descriptor) // status = GenUSB_GetDescriptor(DeviceObject, USB_RECIPIENT_DEVICE, USB_STRING_DESCRIPTOR_TYPE, deviceExtension->DeviceDescriptor->iSerialNumber, deviceExtension->LanguageId, 2, // RetryCount descriptorLength, &descriptor); if (!NT_SUCCESS(status)) { DBGPRINT(1, ("Get Serial Number failed (3) %08X\n", status)); goto GenUSB_GetStringDescriptorsDone; } ASSERT(NULL == deviceExtension->SerialNumber); deviceExtension->SerialNumber = (PUSB_STRING_DESCRIPTOR)descriptor; GenUSB_GetStringDescriptorsDone: DBGPRINT(2, ("exit: GenUSB_GetStringDescriptors %08X %08X\n", status, deviceExtension->SerialNumber)); LOGENTRY(deviceExtension, 'gdsc', status, deviceExtension->LanguageId, deviceExtension->SerialNumber); return status; } NTSTATUS GenUSB_VendorControlRequest ( IN PDEVICE_OBJECT DeviceObject, IN UCHAR RequestType, IN UCHAR Request, IN USHORT Value, IN USHORT Index, IN USHORT Length, IN ULONG RetryCount, OUT PULONG UrbStatus, OUT PUSHORT ResultLength, OUT PUCHAR *Descriptor ) { PURB urb; NTSTATUS status; BOOLEAN descriptorAllocated = FALSE; PAGED_CODE(); DBGPRINT(2, ("enter: GenUSB_GetDescriptor\n")); if (NULL == *Descriptor) { // Allocate a descriptor buffer *Descriptor = ExAllocatePool(NonPagedPool, Length); descriptorAllocated = TRUE; } if (NULL != *Descriptor) { // Allocate a URB for the Get Descriptor request urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_TRANSFER)); if (NULL != urb) { do { // Initialize the URB urb->UrbHeader.Function = URB_FUNCTION_CONTROL_TRANSFER; urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_TRANSFER); urb->UrbHeader.Status = USBD_STATUS_SUCCESS; urb->UrbControlTransfer.PipeHandle = NULL; urb->UrbControlTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN | USBD_DEFAULT_PIPE_TRANSFER | USBD_SHORT_TRANSFER_OK; urb->UrbControlTransfer.TransferBufferLength = Length; urb->UrbControlTransfer.TransferBuffer = *Descriptor; urb->UrbControlTransfer.TransferBufferMDL = NULL; urb->UrbControlTransfer.UrbLink = NULL; urb->UrbControlTransfer.SetupPacket [0] = RequestType; urb->UrbControlTransfer.SetupPacket [1] = Request; ((WCHAR *) urb->UrbControlTransfer.SetupPacket) [1] = Value; ((WCHAR *) urb->UrbControlTransfer.SetupPacket) [2] = Index; ((WCHAR *) urb->UrbControlTransfer.SetupPacket) [3] = Length; // Send the URB down the stack status = GenUSB_SyncSendUsbRequest(DeviceObject, urb); if (NT_SUCCESS(status)) { break; } } while (RetryCount-- > 0); *UrbStatus = urb->UrbHeader.Status; *ResultLength = (USHORT) urb->UrbControlTransfer.TransferBufferLength; ExFreePool(urb); } else { // Failed to allocate the URB status = STATUS_INSUFFICIENT_RESOURCES; } } else { // Failed to allocate the descriptor buffer status = STATUS_INSUFFICIENT_RESOURCES; } if (!NT_SUCCESS(status)) { if ((*Descriptor != NULL) && descriptorAllocated) { ExFreePool(*Descriptor); *Descriptor = NULL; } } DBGPRINT(2, ("exit: GenUSB_GetDescriptor %08X\n", status)); return status; } void blah() { return; } VOID GenUSB_ParseConfigurationDescriptors( IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, IN ULONG NumberInterfaces, IN USB_INTERFACE_DESCRIPTOR DesiredArray[], OUT USB_INTERFACE_DESCRIPTOR FoundArray[], OUT PUCHAR InterfacesFound, OUT PUSBD_INTERFACE_LIST_ENTRY DescriptorArray ) /*++ Routine Description: Parses a standard USB configuration descriptor (returned from a device) for an array of specific interfaces, alternate setting class subclass or protocol codes Arguments: Return Value: NT status code. --*/ { ULONG i; ULONG foo; PUSB_INTERFACE_DESCRIPTOR inter; PAGED_CODE(); ASSERT (NULL != InterfacesFound); ASSERT (NULL != DescriptorArray); *InterfacesFound = 0; // none found yet. ASSERT(ConfigurationDescriptor->bDescriptorType == USB_CONFIGURATION_DESCRIPTOR_TYPE); // // we walk the table of desired interfaces descriptors looking for an // looking for all of them in the configuration descriptor // // // here we use ParseConfigurationDescriptorEx, which walks through the // entire configuration descriptor looking for matches. While this is // more or less order n^2 things are not much better if done by hand // so just use the given routine. // for (i = 0; i < NumberInterfaces; i++) { inter = USBD_ParseConfigurationDescriptorEx ( ConfigurationDescriptor, ConfigurationDescriptor, (CHAR) DesiredArray[i].bInterfaceNumber, (CHAR) DesiredArray[i].bAlternateSetting, (CHAR) DesiredArray[i].bInterfaceClass, (CHAR) DesiredArray[i].bInterfaceSubClass, (CHAR) DesiredArray[i].bInterfaceProtocol); if (NULL != inter) { DescriptorArray[*InterfacesFound].InterfaceDescriptor = inter; (*InterfacesFound)++; FoundArray[i] = *inter; } } } //****************************************************************************** // // GenUSB_SelectConfiguration() // // Must be called at IRQL PASSIVE_LEVEL // //****************************************************************************** NTSTATUS GenUSB_SelectConfiguration ( IN PDEVICE_EXTENSION DeviceExtension, IN ULONG NumberInterfaces, IN PUSB_INTERFACE_DESCRIPTOR DesiredArray, OUT PUSB_INTERFACE_DESCRIPTOR FoundArray ) { PGENUSB_INTERFACE inter; // Apparently the compiler will not allow a local variable of the name // interface for some probably valid but frustrating reason. PURB urb; NTSTATUS status; PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor; PUSBD_INTERFACE_INFORMATION interfaceInfo; PUSBD_INTERFACE_LIST_ENTRY interfaceList; ULONG i,j; ULONG size; UCHAR interfacesFound; BOOLEAN directionIn; KIRQL irql; ExAcquireFastMutex (&DeviceExtension->ConfigMutex); DBGPRINT(2, ("enter: GenUSB_SelectConfiguration\n")); LOGENTRY(DeviceExtension, 'SCON', DeviceExtension->Self, 0, 0); urb = 0; interfaceList = 0; // // We shouldn't have a currently selected interface. // You must unconfigure the device before configuring it again. // if (NULL != DeviceExtension->ConfigurationHandle) { status = STATUS_INVALID_PARAMETER; goto GenUSB_SelectConfigurationReject; } ASSERT (NULL == DeviceExtension->Interface); ASSERT (0 == DeviceExtension->InterfacesFound); configurationDescriptor = DeviceExtension->ConfigurationDescriptor; // Allocate storage for an Inteface List to use as an input/output // parameter to USBD_CreateConfigurationRequestEx(). // interfaceList = ExAllocatePool(PagedPool, sizeof(USBD_INTERFACE_LIST_ENTRY) * (NumberInterfaces + 1)); if (NULL == interfaceList) { status = STATUS_INSUFFICIENT_RESOURCES; goto GenUSB_SelectConfigurationReject; } // NB we are holding a Fast Mutex whist calling this function GenUSB_ParseConfigurationDescriptors(configurationDescriptor, NumberInterfaces, DesiredArray, FoundArray, &interfacesFound, interfaceList); if (interfacesFound < NumberInterfaces) { // We couldn't select all of the interfaces. // For now, allow that. // status = STATUS_INVALID_PARAMETER; // goto GenUSB_SelectConfigurationReject; ; } ASSERT (interfacesFound <= NumberInterfaces); // Terminate the list. interfaceList[interfacesFound].InterfaceDescriptor = NULL; // Create a SELECT_CONFIGURATION URB, turning the Interface // Descriptors in the interfaceList into USBD_INTERFACE_INFORMATION // structures in the URB. // // NB we are holding a Fast Mutex whist calling this function urb = USBD_CreateConfigurationRequestEx( configurationDescriptor, interfaceList); if (NULL == urb) { status = STATUS_INSUFFICIENT_RESOURCES; goto GenUSB_SelectConfigurationReject; } // Now issue the USB request to set the Configuration // NB we are holding a Fast Mutex whist calling this function status = GenUSB_SyncSendUsbRequest(DeviceExtension->Self, urb); if (!NT_SUCCESS(status)) { goto GenUSB_SelectConfigurationReject; } KeAcquireSpinLock (&DeviceExtension->SpinLock, &irql); { DeviceExtension->InterfacesFound = interfacesFound; DeviceExtension->TotalNumberOfPipes = 0; // Save the configuration handle for this device in // the Device Extension. DeviceExtension->ConfigurationHandle = urb->UrbSelectConfiguration.ConfigurationHandle; // // Now for each interface in the list... // Save a copy of the interface information returned // by the SELECT_CONFIGURATION request in the Device // Extension. This gives us a list of PIPE_INFORMATION // structures for each pipe opened in this configuration. // size = interfacesFound * sizeof (PVOID); DeviceExtension->Interface = ExAllocatePool (NonPagedPool, size); if (NULL == DeviceExtension->Interface) { status = STATUS_INSUFFICIENT_RESOURCES; } else { RtlZeroMemory (DeviceExtension->Interface, size); interfaceInfo = &urb->UrbSelectConfiguration.Interface; for (i=0; i < interfacesFound; i++) { size = sizeof (GENUSB_INTERFACE) + (interfaceInfo->NumberOfPipes * sizeof(GENUSB_PIPE_INFO)); inter = DeviceExtension->Interface[i] = ExAllocatePool (NonPagedPool, size); if (inter) { RtlZeroMemory (inter, size); inter->InterfaceNumber = interfaceInfo->InterfaceNumber; inter->CurrentAlternate = interfaceInfo->AlternateSetting; inter->Handle = interfaceInfo->InterfaceHandle; inter->NumberOfPipes = (UCHAR)interfaceInfo->NumberOfPipes; DeviceExtension->TotalNumberOfPipes += inter->NumberOfPipes; for (j=0; j < inter->NumberOfPipes; j++) { inter->Pipes[j].Info = interfaceInfo->Pipes[j]; // Set the default timeout for this device to be zero. // (structure already initialized to zero.) // // inter->Pipes[j].Properties.DefaultTimeout = 0; // inter->Pipes[j].CurrentTimeout = 0; // set the outstanding number of transactions for this // pipe to be 0. // // inter->Pipes[j].OutandingIO = 0; directionIn = USBD_PIPE_DIRECTION_IN (&inter->Pipes[j].Info); if (directionIn) { // // by default we always truncate reads requests from // the device. // inter->Pipes[j].Properties.DirectionIn = TRUE; inter->Pipes[j].Properties.NoTruncateToMaxPacket = FALSE; } } } else { // Could not allocate a copy of interface information status = STATUS_INSUFFICIENT_RESOURCES; break; } // // find the next interfaceInfo // interfaceInfo = (PUSBD_INTERFACE_INFORMATION) ((PUCHAR) interfaceInfo + interfaceInfo->Length); } } // // Regardless of whether or not we've been successful... // We've just invalidated the pipe table so blow away the // read and write values. // DeviceExtension->ReadInterface = -1; DeviceExtension->ReadPipe = -1; DeviceExtension->WriteInterface = -1; DeviceExtension->WritePipe = -1; } KeReleaseSpinLock (&DeviceExtension->SpinLock, irql); if (!NT_SUCCESS (status)) { goto GenUSB_SelectConfigurationReject; } IoInitializeRemoveLock (&DeviceExtension->ConfigurationRemoveLock, POOL_TAG, 0, 0); IoStartTimer (DeviceExtension->Self); ExFreePool(urb); ExFreePool(interfaceList); DBGPRINT(2, ("exit: GenUSB_SelectConfiguration %08X\n", status)); LOGENTRY(DeviceExtension, 'scon', 0, 0, status); ExReleaseFastMutex (&DeviceExtension->ConfigMutex); return status; GenUSB_SelectConfigurationReject: if (interfaceList) { ExFreePool (interfaceList); } if (urb) { ExFreePool (urb); } GenUSB_FreeInterfaceTable (DeviceExtension); LOGENTRY(DeviceExtension, 'scon', 0, 0, status); ExReleaseFastMutex (&DeviceExtension->ConfigMutex); return status; } VOID GenUSB_FreeInterfaceTable ( PDEVICE_EXTENSION DeviceExtension ) { KIRQL irql; ULONG i; KeAcquireSpinLock (&DeviceExtension->SpinLock, &irql); { if (DeviceExtension->Interface) { for (i = 0; i < DeviceExtension->InterfacesFound; i++) { if (DeviceExtension->Interface[i]) { ExFreePool (DeviceExtension->Interface[i]); } } ExFreePool (DeviceExtension->Interface); DeviceExtension->Interface = 0; } DeviceExtension->InterfacesFound = 0; DeviceExtension->ConfigurationHandle = NULL; // Freed automatically by USB } KeReleaseSpinLock (&DeviceExtension->SpinLock, irql); } //****************************************************************************** // // GenUSB_UnConfigure() // // Must be called at IRQL PASSIVE_LEVEL // //****************************************************************************** NTSTATUS GenUSB_DeselectConfiguration ( PDEVICE_EXTENSION DeviceExtension, BOOLEAN SendUrb ) { NTSTATUS status; PURB urb; ULONG ulSize; ExAcquireFastMutex (&DeviceExtension->ConfigMutex); DBGPRINT(2, ("enter: GenUSB_UnConfigure\n")); LOGENTRY(DeviceExtension, 'UCON', DeviceExtension->Self, 0, 0); if (NULL == DeviceExtension->ConfigurationHandle) { status = STATUS_UNSUCCESSFUL; LOGENTRY(DeviceExtension, 'ucon', 1, 0, status); ExReleaseFastMutex (&DeviceExtension->ConfigMutex); return status; } status = STATUS_SUCCESS; IoStopTimer (DeviceExtension->Self); // Allocate a URB for the SELECT_CONFIGURATION request. As we are // unconfiguring the device, the request needs no pipe and interface // information structures. if (SendUrb) { ulSize = sizeof(struct _URB_SELECT_CONFIGURATION); urb = ExAllocatePool (NonPagedPool, ulSize); if (urb) { // Initialize the URB. A NULL Configuration Descriptor indicates // that the device should be unconfigured. // UsbBuildSelectConfigurationRequest(urb, (USHORT)ulSize, NULL); // Now issue the USB request to set the Configuration // status = GenUSB_SyncSendUsbRequest(DeviceExtension->Self, urb); ASSERT ((STATUS_SUCCESS == status) || (STATUS_DEVICE_NOT_CONNECTED == status) || (STATUS_DEVICE_POWERED_OFF == status)); ExFreePool (urb); } else { // Could not allocate the URB. // status = STATUS_INSUFFICIENT_RESOURCES; } } // // We need to wait for all outstanding IO to finish before // freeing the pipe table // // In order to use ReleaseAndWait, we need to first take the lock another // time. // status = IoAcquireRemoveLock (&DeviceExtension->ConfigurationRemoveLock, DeviceExtension); ASSERT (STATUS_SUCCESS == status); IoReleaseRemoveLockAndWait (&DeviceExtension->ConfigurationRemoveLock, DeviceExtension); GenUSB_FreeInterfaceTable (DeviceExtension); // // We've just invalidated the pipe table so blow away the // read and write values. // DeviceExtension->ReadInterface = -1; DeviceExtension->ReadPipe = -1; DeviceExtension->WriteInterface = -1; DeviceExtension->WritePipe = -1; DBGPRINT(2, ("exit: GenUSB_UnConfigure %08X\n", status)); LOGENTRY(DeviceExtension, 'ucon', 0, 0, status); ExReleaseFastMutex (&DeviceExtension->ConfigMutex); return status; } NTSTATUS GenUSB_GetSetPipe ( IN PDEVICE_EXTENSION DeviceExtension, IN PUCHAR InterfaceIndex, // Optional IN PUCHAR InterfaceNumber, // Optional IN PUCHAR PipeIndex, // Optional IN PUCHAR EndpointAddress, // Optional IN PGENUSB_PIPE_PROPERTIES SetPipeProperties, // Optional OUT PGENUSB_PIPE_INFORMATION PipeInfo, // Optional OUT PGENUSB_PIPE_PROPERTIES GetPipeProperties, // Optional OUT USBD_PIPE_HANDLE * UsbdPipeHandle // Optional ) { KIRQL irql; UCHAR i; NTSTATUS status; BOOLEAN directionIn; UCHAR trueInterIndex; PGENUSB_INTERFACE genusbInterface; PGENUSB_PIPE_INFO pipe; status = IoAcquireRemoveLock (&DeviceExtension->ConfigurationRemoveLock, PipeInfo); if (!NT_SUCCESS(status)) { return status; } status = STATUS_INVALID_PARAMETER; KeAcquireSpinLock (&DeviceExtension->SpinLock, &irql); { if (NULL != InterfaceNumber) { // We need to walk the list of interfaces looking for this // interface number. // // set trueInterIndex to an invalid value to start so that if it's // not found, we will fall through the error path. // trueInterIndex = DeviceExtension->InterfacesFound; for (i=0; iInterfacesFound; i++) { genusbInterface = DeviceExtension->Interface[i]; if (genusbInterface->InterfaceNumber == *InterfaceNumber) { trueInterIndex = i; break; } } } else { ASSERT (NULL != InterfaceIndex); trueInterIndex = *InterfaceIndex; } if (trueInterIndex < DeviceExtension->InterfacesFound) { genusbInterface = DeviceExtension->Interface[trueInterIndex]; // // Find the Pipe in question using either the PipeIndex // or the Endpoint Address. // pipe = NULL; if (NULL != PipeIndex) { ASSERT (0 == EndpointAddress); if (*PipeIndex < genusbInterface->NumberOfPipes) { pipe = &genusbInterface->Pipes[*PipeIndex]; } } else { for (i=0; i < genusbInterface->NumberOfPipes; i++) { if (genusbInterface->Pipes[i].Info.EndpointAddress == *EndpointAddress) { // *PipeInfo = genusbInterface->Pipes[i].Info; pipe = &genusbInterface->Pipes[i]; break; } } } if (NULL != pipe) { // // Now that we have the Pipe, retrieve and set optional info // if (PipeInfo) { // *PipeInfo = pipe->Info; PipeInfo->MaximumPacketSize = pipe->Info.MaximumPacketSize; PipeInfo->EndpointAddress = pipe->Info.EndpointAddress; PipeInfo->Interval = pipe->Info.Interval; PipeInfo->PipeType = pipe->Info.PipeType; PipeInfo->MaximumTransferSize = pipe->Info.MaximumTransferSize; PipeInfo->PipeFlags = pipe->Info.PipeFlags; ((PGENUSB_PIPE_HANDLE)&PipeInfo->PipeHandle)->InterfaceIndex = trueInterIndex; ((PGENUSB_PIPE_HANDLE)&PipeInfo->PipeHandle)->PipeIndex = i; ((PGENUSB_PIPE_HANDLE)&PipeInfo->PipeHandle)->Signature = CONFIGURATION_CHECK_BITS (DeviceExtension); status = STATUS_SUCCESS; } if (SetPipeProperties) { C_ASSERT (RTL_FIELD_SIZE (GENUSB_PIPE_PROPERTIES, ReservedFields) + FIELD_OFFSET (GENUSB_PIPE_PROPERTIES, ReservedFields) == sizeof (GENUSB_PIPE_PROPERTIES)); // ensure that this is a valid set request. // the check bits must be present that were set when the // caller did a get, and the unused fields must be zero. if (!VERIFY_PIPE_PROPERTIES_HANDLE (SetPipeProperties, pipe)) { ; // status is already set. } else if (RtlEqualMemory (pipe->Properties.ReservedFields, SetPipeProperties->ReservedFields, RTL_FIELD_SIZE (GENUSB_PIPE_PROPERTIES, ReservedFields))) { // This field is not settable directionIn = pipe->Properties.DirectionIn; pipe->Properties = *SetPipeProperties; // the timeout must be greater than one so fix that here. if (1 == pipe->Properties.Timeout) { pipe->Properties.Timeout++; } pipe->Properties.DirectionIn = directionIn; status = STATUS_SUCCESS; } } if (GetPipeProperties) { *GetPipeProperties = pipe->Properties; // set the checkbits before returning to the user GetPipeProperties->PipePropertyHandle = PIPE_PROPERTIES_CHECK_BITS (pipe); status = STATUS_SUCCESS; } if (UsbdPipeHandle) { *UsbdPipeHandle = pipe->Info.PipeHandle; status = STATUS_SUCCESS; } } } } KeReleaseSpinLock (&DeviceExtension->SpinLock, irql); IoReleaseRemoveLock (&DeviceExtension->ConfigurationRemoveLock, PipeInfo); return status; } NTSTATUS GenUSB_SetReadWritePipes ( IN PDEVICE_EXTENSION DeviceExtension, IN PGENUSB_PIPE_HANDLE ReadPipe, IN PGENUSB_PIPE_HANDLE WritePipe ) { NTSTATUS status; KIRQL irql; PGENUSB_INTERFACE inter; BOOLEAN isReadPipe; BOOLEAN isWritePipe; PUSBD_PIPE_INFORMATION pipeInfo; isReadPipe = isWritePipe = TRUE; status = IoAcquireRemoveLock (&DeviceExtension->ConfigurationRemoveLock, GenUSB_SetReadWritePipes); if (!NT_SUCCESS(status)) { return status; } if ((0 == ReadPipe->Signature) && (0 == ReadPipe->InterfaceIndex) && (0 == ReadPipe->PipeIndex)) { isReadPipe = FALSE; } else if (! VERIFY_PIPE_HANDLE_SIG (ReadPipe, DeviceExtension)) { status = STATUS_INVALID_PARAMETER; } if ((0 == WritePipe->Signature) && (0 == WritePipe->InterfaceIndex) && (0 == WritePipe->PipeIndex)) { isWritePipe = FALSE; } else if (! VERIFY_PIPE_HANDLE_SIG (WritePipe, DeviceExtension)) { status = STATUS_INVALID_PARAMETER; } if (NT_SUCCESS (status)) { KeAcquireSpinLock (&DeviceExtension->SpinLock, &irql); // // Verify that the given values for read and write pipes are // within range and then set them. // if (isReadPipe) { if (ReadPipe->InterfaceIndex < DeviceExtension->InterfacesFound) { inter = DeviceExtension->Interface[ReadPipe->InterfaceIndex]; if (ReadPipe->PipeIndex < inter->NumberOfPipes) { // ok the ranges are now valid, test to make sure that // we are configuring for pipes in the correct direction // and for the correct endpoint type // // Right now we only support Bulk and Interrupt. pipeInfo = &inter->Pipes[ReadPipe->PipeIndex].Info; if ( (USBD_PIPE_DIRECTION_IN (pipeInfo)) && ( (UsbdPipeTypeBulk == pipeInfo->PipeType) || (UsbdPipeTypeInterrupt == pipeInfo->PipeType))) { DeviceExtension->ReadInterface = ReadPipe->InterfaceIndex; DeviceExtension->ReadPipe = ReadPipe->PipeIndex; status = STATUS_SUCCESS; } } } } if (isWritePipe) { if (WritePipe->InterfaceIndex < DeviceExtension->InterfacesFound) { inter = DeviceExtension->Interface[WritePipe->InterfaceIndex]; if (WritePipe->PipeIndex < inter->NumberOfPipes) { // ok the ranges are now valid, test to make sure that // we are configuring for pipes in the correct direction // and for the correct endpoint type // // Right now we only support Bulk and Interrupt. pipeInfo = &inter->Pipes[WritePipe->PipeIndex].Info; if ( (!USBD_PIPE_DIRECTION_IN (pipeInfo)) && ( (UsbdPipeTypeBulk == pipeInfo->PipeType) || (UsbdPipeTypeInterrupt == pipeInfo->PipeType))) { DeviceExtension->WriteInterface = WritePipe->InterfaceIndex; DeviceExtension->WritePipe = WritePipe->PipeIndex; status = STATUS_SUCCESS; } } } } KeReleaseSpinLock (&DeviceExtension->SpinLock, irql); } IoReleaseRemoveLock (&DeviceExtension->ConfigurationRemoveLock, GenUSB_SetReadWritePipes); return status; } NTSTATUS GenUSB_TransmitReceiveComplete ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PGENUSB_TRANS_RECV Trc ) { PVOID context; USBD_STATUS urbStatus; ULONG length; PDEVICE_EXTENSION deviceExtension; PGENUSB_PIPE_INFO pipe; PGENUSB_COMPLETION_ROUTINE complete; ASSERT (NULL != Trc); deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; complete = Trc->CompletionRoutine; context = Trc->Context; urbStatus = Trc->TransUrb.Hdr.Status; length = Trc->TransUrb.TransferBufferLength; pipe = Trc->Pipe; LOGENTRY(deviceExtension, 'TR_C', Irp, urbStatus, Irp->IoStatus.Status); // // JD has convinced me that auto reset is not something we should // do for the vendors, since there are so many different cases that // require special handling of the data. They will need to do a reset // themselves explicitely. // // if (pipe->Properties.AutoReset && // (!USBD_SUCCESS(urbStatus)) && // urbStatus != USBD_STATUS_CANCELED) // { // GenUSB_ResetPipe (deviceExtension, Irp, Trc); // return STATUS_MORE_PROCESSING_REQUIRED; // } InterlockedDecrement (&pipe->OutstandingIO); ExFreePool (Trc); IoReleaseRemoveLock (&deviceExtension->ConfigurationRemoveLock, Irp); IoReleaseRemoveLock (&deviceExtension->RemoveLock, Irp); return ((*complete) (DeviceObject, Irp, context, urbStatus, length)); } //****************************************************************************** // // GenUSB_TransmitReceive() // // This routine may run at DPC level. // // Basic idea: // // Initializes the Bulk or Interrupt transfer Urb and sends it down the stack // // scratch: Transfer Flags: USBD_SHORT_TRANSFER_OK // USBD_DEFAULT_PIPE_TRANSFER // USBD_TRANSFER_DIRECTION_OUT // USBD_TRANSFER_DIRECTION_IN // // //****************************************************************************** NTSTATUS GenUSB_TransmitReceive ( IN PDEVICE_EXTENSION DeviceExtension, IN PIRP Irp, IN UCHAR InterfaceNo, IN UCHAR PipeNo, IN ULONG TransferFlags, IN PCHAR Buffer, IN PMDL BufferMDL, IN ULONG BufferLength, IN PVOID Context, IN PGENUSB_COMPLETION_ROUTINE CompletionRoutine ) { PIO_STACK_LOCATION stack; KIRQL irql; NTSTATUS status; GENUSB_TRANS_RECV * trc; PGENUSB_PIPE_INFO pipe; DBGPRINT(3, ("enter: GenUSB_TransmitReceive\n")); LOGENTRY(DeviceExtension, 'TR__', DeviceExtension, ((InterfaceNo << 16) | PipeNo), ((NULL == Buffer) ? (PCHAR) BufferMDL : Buffer)); trc = NULL; pipe = NULL; // // Add another ref count to the remove lock for this completion routine. // since the caller will in all cases free the reference that it took. // status = IoAcquireRemoveLock (&DeviceExtension->RemoveLock, Irp); if (!NT_SUCCESS(status)) { Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); return status; } status = IoAcquireRemoveLock (&DeviceExtension->ConfigurationRemoveLock, Irp); if (!NT_SUCCESS(status)) { IoReleaseRemoveLock (&DeviceExtension->RemoveLock, Irp); Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); return status; } // // find the pipe in question. // KeAcquireSpinLock (&DeviceExtension->SpinLock, &irql); { if (InterfaceNo < DeviceExtension->InterfacesFound) { if (PipeNo < DeviceExtension->Interface[InterfaceNo]->NumberOfPipes) { pipe = &DeviceExtension->Interface[InterfaceNo]->Pipes[PipeNo]; } } } KeReleaseSpinLock (&DeviceExtension->SpinLock, irql); if (NULL == pipe) { status = STATUS_INVALID_PARAMETER; goto GenUSB_TransmitReceiveReject; } trc = ExAllocatePool (NonPagedPool, sizeof (GENUSB_TRANS_RECV)); if (NULL == trc) { status = STATUS_INSUFFICIENT_RESOURCES; goto GenUSB_TransmitReceiveReject; } RtlZeroMemory (trc, sizeof (GENUSB_TRANS_RECV)); trc->Context = Context; trc->Pipe = pipe; trc->CompletionRoutine = CompletionRoutine; // truncate a read packet to the max packet size if necessary. if ((pipe->Properties.DirectionIn) && (!pipe->Properties.NoTruncateToMaxPacket) && (BufferLength > pipe->Info.MaximumPacketSize)) { BufferLength -= (BufferLength % pipe->Info.MaximumPacketSize); } // Initialize the TransferURB trc->TransUrb.Hdr.Length = sizeof (trc->TransUrb); trc->TransUrb.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER; trc->TransUrb.PipeHandle = pipe->Info.PipeHandle; trc->TransUrb.TransferFlags = TransferFlags; trc->TransUrb.TransferBufferLength = BufferLength; trc->TransUrb.TransferBuffer = Buffer; trc->TransUrb.TransferBufferMDL = BufferMDL; // Set up the Irp stack = IoGetNextIrpStackLocation (Irp); stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; stack->Parameters.Others.Argument1 = &trc->TransUrb; InterlockedIncrement (&pipe->OutstandingIO); // Reset the timer Value. pipe->CurrentTimeout = pipe->Properties.Timeout; IoSetCompletionRoutine (Irp, GenUSB_TransmitReceiveComplete, trc, TRUE, TRUE, TRUE); // // The rule is: if your completion routine is going to cause the // Irp to be completed asynchronously (by returning // STATUS_MORE_PROCESSING_REQUIRED) or if it is going to change // the status of the Irp, then the dispatch function must mark // the Irp as pending and return STATUS_PENDING. The completion // routine TransmitReceiveComplete doesn't change the status, // but ProbeAndSubmitTransferComlete might. // // In either case this prevents us from having to perculate the pending // bit in the completion routine as well. // IoMarkIrpPending (Irp); status = IoCallDriver (DeviceExtension->StackDeviceObject, Irp); DBGPRINT(3, ("exit: GenUSB_TransRcv %08X\n", status)); LOGENTRY(DeviceExtension, 'TR_x', Irp, trc, status); status = STATUS_PENDING; return status; GenUSB_TransmitReceiveReject: if (trc) { IoReleaseRemoveLock (&DeviceExtension->ConfigurationRemoveLock, Irp); IoReleaseRemoveLock (&DeviceExtension->RemoveLock, Irp); ExFreePool (trc); } LOGENTRY(DeviceExtension, 'TR_x', Irp, trc, status); ((*CompletionRoutine) (DeviceExtension->Self, Irp, Context, 0, 0)); // // Complete the Irp only after this routine fires, since we pass the // Irp into this routine. // Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); return status; } //****************************************************************************** // // GenUSB_Timer() // // // // This is a watchdog timer routine. The assumption here is that this is not // a highly acurate timer (in fact it only has an accuracy of a single second. // the point is to see if there is any pipe on this device that has // outstanding transactions that are stuck, and then reset that pipe. // We therefore do not spend any effort closing the race conditions // between a just completing transaction and the timer firing. // It is sufficient to know that it got that close to reset the pipe. // // Apon pipe reset, all outstanding IRPs on the pipe should return with // and error. (which is just fine.) // // //****************************************************************************** typedef struct _GENUSB_ABORT_CONTEXT { ULONG NumHandles; PIO_WORKITEM WorkItem; USBD_PIPE_HANDLE Handles[]; } GENUSB_ABORT_CONTEXT, *PGENUSB_ABORT_CONTEXT; VOID GenUSB_AbortPipeWorker ( IN PDEVICE_OBJECT DeviceObject, IN PGENUSB_ABORT_CONTEXT Context ) { ULONG i; NTSTATUS status; PDEVICE_EXTENSION deviceExtension; struct _URB_PIPE_REQUEST urb; deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; for (i=0; i < Context->NumHandles; i++) { RtlZeroMemory (&urb, sizeof (urb)); urb.Hdr.Length = sizeof (urb); urb.Hdr.Function = URB_FUNCTION_ABORT_PIPE; urb.PipeHandle = Context->Handles [i]; LOGENTRY (deviceExtension, 'Abor', urb.PipeHandle, 0, 0); status = GenUSB_SyncSendUsbRequest (DeviceObject, (PURB) &urb); if (!NT_SUCCESS (status)) { LOGENTRY (deviceExtension, 'Abor', urb.PipeHandle, 0, status); } } IoReleaseRemoveLock (&deviceExtension->ConfigurationRemoveLock, GenUSB_Timer); IoFreeWorkItem (Context->WorkItem); ExFreePool (Context); } VOID GenUSB_Timer ( PDEVICE_OBJECT DeviceObject, PVOID Unused ) { PGENUSB_PIPE_INFO pipe; PGENUSB_INTERFACE inter; ULONG i,j; PDEVICE_EXTENSION deviceExtension; KIRQL irql; PGENUSB_ABORT_CONTEXT context; ULONG size; NTSTATUS status; deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; status = IoAcquireRemoveLock (&deviceExtension->ConfigurationRemoveLock, GenUSB_Timer); if (!NT_SUCCESS(status)) { return; } // // BUGBUG preallocate this structure; // allowing the workitem and this timer to run at the same time. // size = sizeof (GENUSB_ABORT_CONTEXT) + sizeof (USBD_PIPE_HANDLE) * deviceExtension->TotalNumberOfPipes; context = ExAllocatePool (NonPagedPool, size); if (NULL == context) { status = STATUS_INSUFFICIENT_RESOURCES; return; } context->WorkItem = IoAllocateWorkItem (DeviceObject); if (NULL == context->WorkItem) { status = STATUS_INSUFFICIENT_RESOURCES; ExFreePool (context); return; } context->NumHandles = 0; KeAcquireSpinLock (&deviceExtension->SpinLock, &irql); { // Walk through the list of interfaces and then pipes on those interfaces // to find any pipes that may need a bit of a bump. for (i=0; i < deviceExtension->InterfacesFound; i++) { inter = deviceExtension->Interface [i]; for (j=0; j < inter->NumberOfPipes; j++) { pipe = &inter->Pipes[j]; // now test for the timeout (given the assumptions above) if (pipe->OutstandingIO) { if (0 != pipe->Properties.Timeout) { ASSERT (0 < pipe->CurrentTimeout); if (0 == InterlockedDecrement (&pipe->CurrentTimeout)) { // abort this pipe. context->Handles[context->NumHandles] = pipe->Info.PipeHandle; context->NumHandles++; } } } } } } KeReleaseSpinLock (&deviceExtension->SpinLock, irql); LOGENTRY(deviceExtension, 'Time', deviceExtension->InterfacesFound, deviceExtension->TotalNumberOfPipes, context->NumHandles); if (0 < context->NumHandles) { IoQueueWorkItem (context->WorkItem, GenUSB_AbortPipeWorker, DelayedWorkQueue, context); } else { IoFreeWorkItem (context->WorkItem); ExFreePool (context); IoReleaseRemoveLock (&deviceExtension->ConfigurationRemoveLock, GenUSB_Timer); } return; } //****************************************************************************** // // GenUSB_ResetPipe() // //****************************************************************************** NTSTATUS GenUSB_ResetPipe ( IN PDEVICE_EXTENSION DeviceExtension, IN USBD_PIPE_HANDLE UsbdPipeHandle, IN BOOLEAN ResetPipe, IN BOOLEAN ClearStall, IN BOOLEAN FlushData ) { NTSTATUS status; struct _URB_PIPE_REQUEST urb; PAGED_CODE (); DBGPRINT(2, ("enter: GenUSB_ResetPipe\n")); LOGENTRY(DeviceExtension, 'RESP', UsbdPipeHandle, (ResetPipe << 24) | (ClearStall << 16) | (FlushData << 8), 0); RtlZeroMemory (&urb, sizeof (urb)); urb.Hdr.Length = sizeof (urb); urb.PipeHandle = UsbdPipeHandle; if (ResetPipe && ClearStall) { urb.Hdr.Function = URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL; } else if (ResetPipe) { urb.Hdr.Function = URB_FUNCTION_SYNC_RESET_PIPE; } else if (ClearStall) { urb.Hdr.Function = URB_FUNCTION_SYNC_CLEAR_STALL; } status = GenUSB_SyncSendUsbRequest (DeviceExtension->Self, (PURB) &urb); LOGENTRY(DeviceExtension, 'resp', UsbdPipeHandle, (ResetPipe << 24) | (ClearStall << 16) | (FlushData << 8), status); DBGPRINT(2, ("exit: GenUSB_ResetPipe %08X\n", status)); return status; }