|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
usb.c
Author:
ervinp
Environment:
Kernel mode
Revision History:
--*/
#include <wdm.h>
#include <usbdi.h>
#include <usbdlib.h>
#include <usbioctl.h>
#include "usb8023.h"
#include "debug.h"
/*
* USB- and WDM- specific prototypes (won't compile in common header) */ NTSTATUS SubmitUrb(PDEVICE_OBJECT pdo, PURB urb, BOOLEAN synchronous, PVOID completionRoutine, PVOID completionContext); NTSTATUS SubmitUrbIrp(PDEVICE_OBJECT pdo, PIRP irp, PURB urb, BOOLEAN synchronous, PVOID completionRoutine, PVOID completionContext); NTSTATUS CallDriverSync(PDEVICE_OBJECT devObj, PIRP irp); NTSTATUS CallDriverSyncCompletion(IN PDEVICE_OBJECT devObjOrNULL, IN PIRP irp, IN PVOID context); NTSTATUS ReadPipeCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context); NTSTATUS WritePipeCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context); NTSTATUS NotificationCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context); NTSTATUS ControlPipeWriteCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context); NTSTATUS ControlPipeReadCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context);
BOOLEAN InitUSB(ADAPTEREXT *adapter) /*++
Routine Description:
Intialize USB-related data
Arguments:
adapter - adapter context
Return Value:
TRUE iff successful
--*/ { NTSTATUS status; BOOLEAN result = FALSE;
status = GetDeviceDescriptor(adapter); if (NT_SUCCESS(status)){ PUSB_DEVICE_DESCRIPTOR deviceDesc = adapter->deviceDesc; if (deviceDesc->bDeviceClass == USB_DEVICE_CLASS_CDC){
status = GetConfigDescriptor(adapter); if (NT_SUCCESS(status)){
status = SelectConfiguration(adapter); if (NT_SUCCESS(status)){
/*
* Find the read and write pipe handles. */ status = FindUSBPipeHandles(adapter); if (NT_SUCCESS(status)){
/*
* Now that we know the notify length, * initialize structures for reading the notify pipe. * Add some buffer space for a guard word. */ adapter->notifyBuffer = AllocPool(adapter->notifyPipeLength+sizeof(ULONG)); adapter->notifyIrpPtr = IoAllocateIrp(adapter->nextDevObj->StackSize, FALSE); adapter->notifyUrbPtr = AllocPool(sizeof(URB)); if (adapter->notifyBuffer && adapter->notifyIrpPtr && adapter->notifyUrbPtr){ KeInitializeEvent(&adapter->notifyCancelEvent, NotificationEvent, FALSE); adapter->cancellingNotify = FALSE; } else { /*
* Alloc failure. Memory will be cleaned up by FreeAdapter(). */ status = STATUS_INSUFFICIENT_RESOURCES; }
if (NT_SUCCESS(status)){ result = TRUE; } else { /*
* Alloc failure. Memory will be cleaned up by FreeAdapter(). */ DBGERR(("Couldn't allocate notify structs")); } } } } } else { DBGERR(("InitUSB: device descriptor has wrong bDeviceClass==%xh.", (ULONG)deviceDesc->bDeviceClass)); status = STATUS_DEVICE_DATA_ERROR; } }
return result; }
VOID StartUSBReadLoop(ADAPTEREXT *adapter) { ULONG i;
for (i = 0; i < NUM_READ_PACKETS; i++){ TryReadUSB(adapter); } }
VOID TryReadUSB(ADAPTEREXT *adapter) { KIRQL oldIrql;
/*
* ReadPipeCompletion re-issues a read irp directly via this function. * Ordinarily the hardware can't keep up fast enough to * make us loop, but this check forces an unwind in extenuating circumstances. */ if (InterlockedIncrement(&adapter->readReentrancyCount) > 3){ KeAcquireSpinLock(&adapter->adapterSpinLock, &oldIrql); adapter->readDeficit++; KeReleaseSpinLock(&adapter->adapterSpinLock, oldIrql); QueueAdapterWorkItem(adapter); DBGWARN(("TryReadUSB: reentered %d times, aborting to prevent stack overflow", adapter->readReentrancyCount)); } else { USBPACKET *packet = DequeueFreePacket(adapter); if (packet){ NTSTATUS status;
EnqueuePendingReadPacket(packet);
status = SubmitUSBReadPacket(packet); } else { KeAcquireSpinLock(&adapter->adapterSpinLock, &oldIrql); adapter->readDeficit++; KeReleaseSpinLock(&adapter->adapterSpinLock, oldIrql); QueueAdapterWorkItem(adapter); } }
InterlockedDecrement(&adapter->readReentrancyCount);
}
NTSTATUS GetDeviceDescriptor(ADAPTEREXT *adapter) /*++
Routine Description:
Function retrieves the device descriptor from the device
Arguments:
adapter - adapter context
Return Value:
NT status code
--*/ { URB urb; NTSTATUS status;
UsbBuildGetDescriptorRequest(&urb, (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, adapter->deviceDesc, NULL, sizeof(USB_DEVICE_DESCRIPTOR), NULL);
status = SubmitUrb(adapter->nextDevObj, &urb, TRUE, NULL, NULL);
if (NT_SUCCESS(status)){ ASSERT(urb.UrbControlDescriptorRequest.TransferBufferLength == sizeof(USB_DEVICE_DESCRIPTOR)); DBGVERBOSE(("Got device desc @ %ph.", (PVOID)&adapter->deviceDesc)); }
ASSERT(NT_SUCCESS(status)); return status; }
NTSTATUS GetConfigDescriptor(ADAPTEREXT *adapter) /*++
Routine Description:
Function retrieves the configuration descriptor from the device
Arguments:
adapter - adapter context
Return Value:
NT status code
--*/ { URB urb = { 0 }; NTSTATUS status; USB_CONFIGURATION_DESCRIPTOR tmpConfigDesc = { 0 };
/*
* First get the initial part of the config descriptor * to find out how long the entire descriptor is. */ UsbBuildGetDescriptorRequest(&urb, (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, (PVOID)&tmpConfigDesc, NULL, sizeof(USB_CONFIGURATION_DESCRIPTOR), NULL); status = SubmitUrb(adapter->nextDevObj, &urb, TRUE, NULL, NULL); if (NT_SUCCESS(status)){
ASSERT(urb.UrbControlDescriptorRequest.TransferBufferLength == sizeof(USB_CONFIGURATION_DESCRIPTOR)); ASSERT(tmpConfigDesc.wTotalLength > sizeof(USB_CONFIGURATION_DESCRIPTOR));
adapter->configDesc = AllocPool((ULONG)tmpConfigDesc.wTotalLength); if (adapter->configDesc){ RtlZeroMemory(adapter->configDesc, (ULONG)tmpConfigDesc.wTotalLength); UsbBuildGetDescriptorRequest(&urb, (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, adapter->configDesc, NULL, tmpConfigDesc.wTotalLength, NULL); status = SubmitUrb(adapter->nextDevObj, &urb, TRUE, NULL, NULL); if (NT_SUCCESS(status)){ ASSERT(((PUSB_CONFIGURATION_DESCRIPTOR)adapter->configDesc)->wTotalLength == tmpConfigDesc.wTotalLength); ASSERT(urb.UrbControlDescriptorRequest.TransferBufferLength == (ULONG)tmpConfigDesc.wTotalLength); DBGVERBOSE(("Got config desc @ %ph, len=%xh.", adapter->configDesc, urb.UrbControlDescriptorRequest.TransferBufferLength)); } else { ASSERT(NT_SUCCESS(status)); FreePool(adapter->configDesc); adapter->configDesc = NULL; } } else { status = STATUS_INSUFFICIENT_RESOURCES; } }
ASSERT(NT_SUCCESS(status)); return status; }
/*
* SelectConfiguration * * */ NTSTATUS SelectConfiguration(ADAPTEREXT *adapter) { PUSB_CONFIGURATION_DESCRIPTOR configDesc = (PUSB_CONFIGURATION_DESCRIPTOR)adapter->configDesc; NTSTATUS status; PURB urb = NULL; ULONG i;
ASSERT(configDesc->bNumInterfaces > 0);
#if SPECIAL_WIN98SE_BUILD
/*
* Hack to load on Win98 gold */ { USHORT dummySize = 0; ASSERT(configDesc->bNumInterfaces >= 2); urb = USBD_CreateConfigurationRequest(configDesc, &dummySize); } #else
if (configDesc->bNumInterfaces >= 2){ PUSBD_INTERFACE_LIST_ENTRY interfaceList; interfaceList = AllocPool((configDesc->bNumInterfaces+1)*sizeof(USBD_INTERFACE_LIST_ENTRY)); if (interfaceList){
for (i = 0; i < configDesc->bNumInterfaces; i++){
/*
* Note: try to use USBD_ParseConfigurationDescriptor instead of * USBD_ParseConfigurationDescriptorEx so that we work * on Win98 gold. */ interfaceList[i].InterfaceDescriptor = USBD_ParseConfigurationDescriptor( configDesc, (UCHAR)i, (UCHAR)0); if (!interfaceList[i].InterfaceDescriptor){ break; } } interfaceList[i].InterfaceDescriptor = NULL; ASSERT(i == configDesc->bNumInterfaces);
urb = USBD_CreateConfigurationRequestEx(configDesc, interfaceList);
FreePool(interfaceList); } } else { ASSERT(configDesc->bNumInterfaces >= 2); } #endif
if (urb){ PUSBD_INTERFACE_INFORMATION interfaceInfo;
/*
* Fill in the interfaceInfo Class fields, * since USBD_CreateConfigurationRequestEx doesn't do that. */ interfaceInfo = &urb->UrbSelectConfiguration.Interface; for (i = 0; i < configDesc->bNumInterfaces; i++){ PUSB_INTERFACE_DESCRIPTOR ifaceDesc; ifaceDesc = USBD_ParseConfigurationDescriptor(configDesc, (UCHAR)i, (UCHAR)0); interfaceInfo->Class = ifaceDesc->bInterfaceClass; interfaceInfo = (PUSBD_INTERFACE_INFORMATION)((PUCHAR)interfaceInfo+interfaceInfo->Length); }
/*
* Increase the transfer size for all data endpoints up to the maximum. * The data interface follows the master interface. */ interfaceInfo = &urb->UrbSelectConfiguration.Interface; if (interfaceInfo->Class != USB_DEVICE_CLASS_DATA){ interfaceInfo = (PUSBD_INTERFACE_INFORMATION)((PUCHAR)interfaceInfo+interfaceInfo->Length); } if (interfaceInfo->Class == USB_DEVICE_CLASS_DATA){ for (i = 0; i < interfaceInfo->NumberOfPipes; i++){ interfaceInfo->Pipes[i].MaximumTransferSize = PACKET_BUFFER_SIZE; } status = SubmitUrb(adapter->nextDevObj, urb, TRUE, NULL, NULL); } else { ASSERT(interfaceInfo->Class == USB_DEVICE_CLASS_DATA); status = STATUS_DEVICE_DATA_ERROR; }
if (NT_SUCCESS(status)){ PUSBD_INTERFACE_INFORMATION interfaceInfo2;
adapter->configHandle = (PVOID)urb->UrbSelectConfiguration.ConfigurationHandle;
/*
* A USB RNDIS device has two interfaces: * - a 'master' CDC class interface with one interrupt endpoint for notification * - a Data class interface with two bulk endpoints * * They may be in either order, so check class fields to assign * pointers correctly. */
interfaceInfo = &urb->UrbSelectConfiguration.Interface; interfaceInfo2 = (PUSBD_INTERFACE_INFORMATION)((PUCHAR)interfaceInfo+interfaceInfo->Length);
if ((interfaceInfo->Class == USB_DEVICE_CLASS_CDC) && (interfaceInfo2->Class == USB_DEVICE_CLASS_DATA)){ adapter->interfaceInfoMaster = MemDup(interfaceInfo, interfaceInfo->Length); adapter->interfaceInfo = MemDup(interfaceInfo2, interfaceInfo2->Length); } else if ((interfaceInfo->Class == USB_DEVICE_CLASS_DATA) && (interfaceInfo2->Class == USB_DEVICE_CLASS_CDC)){ DBGWARN(("COVERAGE - Data interface precedes master CDC interface")); adapter->interfaceInfo = MemDup(interfaceInfo, interfaceInfo->Length); adapter->interfaceInfoMaster = MemDup(interfaceInfo2, interfaceInfo2->Length); } else { DBGERR(("improper interface classes")); adapter->interfaceInfo = NULL; adapter->interfaceInfoMaster = NULL; }
if (adapter->interfaceInfo && adapter->interfaceInfoMaster){ DBGVERBOSE(("SelectConfiguration: interfaceInfo @ %ph, interfaceInfoMaster @ %ph.", adapter->interfaceInfo, adapter->interfaceInfoMaster)); } else { if (adapter->interfaceInfoMaster) FreePool(adapter->interfaceInfoMaster); if (adapter->interfaceInfo) FreePool(adapter->interfaceInfo); status = STATUS_INSUFFICIENT_RESOURCES; } } else { DBGERR(("SelectConfiguration: selectConfig URB failed w/ %xh.", status)); }
ExFreePool(urb); } else { status = STATUS_INSUFFICIENT_RESOURCES; }
ASSERT(NT_SUCCESS(status)); return status; }
NTSTATUS FindUSBPipeHandles(ADAPTEREXT *adapter) {
/*
* Algorithm for identifying the endpoints: * The longest interrupt or bulk IN endpoint on the data interface * is the read endpoint; * The longest interrupt or bulk OUT endpoint on the data interface * is the write endpoint; * The first interrupt IN endpoint on the master interface * is the notification endpoint. */ PUSBD_INTERFACE_INFORMATION interfaceInfo = adapter->interfaceInfo; PUSBD_INTERFACE_INFORMATION notifyInterfaceInfo = adapter->interfaceInfoMaster; LONG pipeIndex; LONG longestInputPipeIndex = -1, longestOutputPipeIndex = -1, notifyPipeIndex = -1; ULONG longestInputPipeLength = 0, longestOutputPipeLength = 0, notifyPipeLength; NTSTATUS status;
/*
* Find the IN and OUT endpoints. */ for (pipeIndex = 0; pipeIndex < (LONG)interfaceInfo->NumberOfPipes; pipeIndex++){ PUSBD_PIPE_INFORMATION pipeInfo = &interfaceInfo->Pipes[pipeIndex];
if ((pipeInfo->PipeType == UsbdPipeTypeInterrupt) || (pipeInfo->PipeType == UsbdPipeTypeBulk)){
if (pipeInfo->EndpointAddress & USB_ENDPOINT_DIRECTION_MASK){ if (pipeInfo->MaximumPacketSize > longestInputPipeLength){ longestInputPipeIndex = pipeIndex; longestInputPipeLength = pipeInfo->MaximumPacketSize; } } else { if (pipeInfo->MaximumPacketSize > longestOutputPipeLength){ longestOutputPipeIndex = pipeIndex; longestOutputPipeLength = pipeInfo->MaximumPacketSize; } } } }
/*
* Find the Notify endpoint. */ for (pipeIndex = 0; pipeIndex < (LONG)notifyInterfaceInfo->NumberOfPipes; pipeIndex++){ PUSBD_PIPE_INFORMATION pipeInfo = ¬ifyInterfaceInfo->Pipes[pipeIndex];
if ((pipeInfo->PipeType == UsbdPipeTypeInterrupt) && (pipeInfo->EndpointAddress & USB_ENDPOINT_DIRECTION_MASK) && ((notifyInterfaceInfo != interfaceInfo) || (pipeIndex != longestInputPipeIndex))){
notifyPipeIndex = pipeIndex; notifyPipeLength = pipeInfo->MaximumPacketSize; break; } }
if ((longestInputPipeIndex >= 0) && (longestOutputPipeIndex >= 0) && (notifyPipeIndex >= 0)){
adapter->readPipeHandle = interfaceInfo->Pipes[longestInputPipeIndex].PipeHandle; adapter->writePipeHandle = interfaceInfo->Pipes[longestOutputPipeIndex].PipeHandle; adapter->notifyPipeHandle = notifyInterfaceInfo->Pipes[notifyPipeIndex].PipeHandle;
adapter->readPipeLength = longestInputPipeLength; adapter->writePipeLength = longestOutputPipeLength; adapter->notifyPipeLength = notifyPipeLength;
adapter->readPipeEndpointAddr = interfaceInfo->Pipes[longestInputPipeIndex].EndpointAddress; adapter->writePipeEndpointAddr = interfaceInfo->Pipes[longestOutputPipeIndex].EndpointAddress; adapter->notifyPipeEndpointAddr = notifyInterfaceInfo->Pipes[notifyPipeIndex].EndpointAddress;
DBGVERBOSE(("FindUSBPipeHandles: got readPipe %ph,len=%xh; writePipe %ph,len=%xh; notifyPipe %ph,len=%xh.", adapter->readPipeHandle, adapter->readPipeLength, adapter->writePipeHandle, adapter->writePipeLength, adapter->notifyPipeHandle, adapter->notifyPipeLength)); status = STATUS_SUCCESS; } else { DBGERR(("FindUSBPipeHandles: couldn't find right set of pipe handles (indices: %xh,%xh,%xh).", longestInputPipeIndex, longestOutputPipeIndex, notifyPipeIndex)); status = STATUS_DEVICE_DATA_ERROR; }
return status; }
NTSTATUS SubmitUrb( PDEVICE_OBJECT pdo, PURB urb, BOOLEAN synchronous, PVOID completionRoutine, PVOID completionContext) /*++
Routine Description:
Send the URB to the USB device. If synchronous is TRUE, ignore the completion info and synchonize the IRP; otherwise, don't synchronize and set the provided completion routine for the IRP.
Arguments:
Return Value:
NT status code
--*/ { NTSTATUS status; PIRP irp;
/*
* Allocate the IRP to send the buffer down the USB stack. * * Don't use IoBuildDeviceIoControlRequest (because it queues * the IRP on the current thread's irp list and may * cause the calling process to hang if the IopCompleteRequest APC * does not fire and dequeue the IRP). */ irp = IoAllocateIrp(pdo->StackSize, FALSE); if (irp){ PIO_STACK_LOCATION nextSp;
DBGVERBOSE(("SubmitUrb: submitting URB %ph on IRP %ph (sync=%d)", urb, irp, synchronous));
nextSp = IoGetNextIrpStackLocation(irp); nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
/*
* Attach the URB to this IRP. */ nextSp->Parameters.Others.Argument1 = urb;
if (synchronous){
status = CallDriverSync(pdo, irp);
IoFreeIrp(irp); } else { /*
* Caller's completion routine will free the irp * when it completes. */ ASSERT(completionRoutine); ASSERT(completionContext);
irp->IoStatus.Status = STATUS_NOT_SUPPORTED; IoSetCompletionRoutine( irp, completionRoutine, completionContext, TRUE, TRUE, TRUE); status = IoCallDriver(pdo, irp); } } else { status = STATUS_INSUFFICIENT_RESOURCES; } return status; }
NTSTATUS SubmitUrbIrp( PDEVICE_OBJECT pdo, PIRP irp, PURB urb, BOOLEAN synchronous, PVOID completionRoutine, PVOID completionContext) /*++
Routine Description:
Send the URB to the USB device. If synchronous is TRUE, ignore the completion info and synchonize the IRP; otherwise, don't synchronize and set the provided completion routine for the IRP.
Arguments:
Return Value:
NT status code
--*/ { NTSTATUS status; PIO_STACK_LOCATION nextSp;
DBGVERBOSE(("SubmitUrb: submitting URB %ph on IRP %ph (sync=%d)", urb, irp, synchronous));
nextSp = IoGetNextIrpStackLocation(irp); nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
irp->Cancel = FALSE;
/*
* Attach the URB to this IRP. */ nextSp->Parameters.Others.Argument1 = urb;
if (synchronous){ status = CallDriverSync(pdo, irp); ASSERT(!irp->CancelRoutine); } else { ASSERT(completionRoutine); ASSERT(completionContext);
irp->IoStatus.Status = STATUS_NOT_SUPPORTED; IoSetCompletionRoutine( irp, completionRoutine, completionContext, TRUE, TRUE, TRUE); status = IoCallDriver(pdo, irp); }
return status; }
NTSTATUS SubmitUSBReadPacket(USBPACKET *packet) { NTSTATUS status; PURB urb = packet->urbPtr; PIRP irp = packet->irpPtr; ULONG readLength;
readLength = packet->dataBufferMaxLength;
DBGVERBOSE(("SubmitUSBReadPacket: read %xh bytes, packet # %xh.", readLength, packet->packetId));
urb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER; urb->UrbBulkOrInterruptTransfer.Hdr.Length = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER); urb->UrbBulkOrInterruptTransfer.PipeHandle = packet->adapter->readPipeHandle; urb->UrbBulkOrInterruptTransfer.TransferBufferLength = readLength; urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL; urb->UrbBulkOrInterruptTransfer.TransferBuffer = packet->dataBuffer; urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_IN; urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
status = SubmitUrbIrp( packet->adapter->nextDevObj, irp, urb, FALSE, // asynchronous
ReadPipeCompletion, // completion routine
packet // completion context
); return status; }
NTSTATUS ReadPipeCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context) { USBPACKET *packet = (USBPACKET *)context; ADAPTEREXT *adapter = packet->adapter; NTSTATUS status = irp->IoStatus.Status;
ASSERT(packet->sig == DRIVER_SIG); ASSERT(adapter->sig == DRIVER_SIG); ASSERT(packet->irpPtr == irp); ASSERT(!irp->CancelRoutine); ASSERT(status != STATUS_PENDING); // saw UHCD doing this ?
/*
* Dequeue the packet from the usbPendingReadPackets queue * BEFORE checking if it was cancelled to avoid race with CancelAllPendingPackets. */ DequeuePendingReadPacket(packet);
if (packet->cancelled){ /*
* This packet was cancelled because of a halt or reset. * Get the packet is back in the free list first, then * set the event so CancelAllPendingPackets can proceed. */ DBGVERBOSE((" ... read packet #%xh cancelled.", packet->packetId)); packet->cancelled = FALSE;
EnqueueFreePacket(packet); KeSetEvent(&packet->cancelEvent, 0, FALSE); } else if (adapter->halting){ EnqueueFreePacket(packet); } else { PURB urb = packet->urbPtr;
if (NT_SUCCESS(status)){ BOOLEAN ethernetPacketComplete;
adapter->numConsecutiveReadFailures = 0;
/*
* Fix the packet's dataBufferCurrentLength to indicate the actual length * of the returned data. * Note: the KLSI device rounds this up to a multiple of the endpoint * packet size, so the returned length may actually be larger than * the actual data. */ packet->dataBufferCurrentLength = urb->UrbBulkOrInterruptTransfer.TransferBufferLength; ASSERT(packet->dataBufferCurrentLength); ASSERT(packet->dataBufferCurrentLength <= packet->dataBufferMaxLength);
DBGVERBOSE(("ReadPipeCompletion: %xh bytes, packet # %xh.", packet->dataBufferCurrentLength, packet->packetId));
ethernetPacketComplete = (packet->dataBufferCurrentLength >= MINIMUM_ETHERNET_PACKET_SIZE);
if (ethernetPacketComplete){ /*
* A complete ethernet packet has been received. * The entire ethernet packet is now in the current (final) USB packet. * Put our USB packet on the completed list and indicate it to RNDIS. */ DBGSHOWBYTES("ReadPipeCompletion (COMPLETE packet)", packet->dataBuffer, packet->dataBufferCurrentLength);
EnqueueCompletedReadPacket(packet);
status = IndicateRndisMessage(packet, TRUE); if (status != STATUS_PENDING){ DequeueCompletedReadPacket(packet); EnqueueFreePacket(packet); } } else { DBGWARN(("Device returned %xh-length packet @ %ph.", packet->dataBufferCurrentLength, packet->dataBuffer)); DBGSHOWBYTES("ReadPipeCompletion (partial packet)", packet->dataBuffer, packet->dataBufferCurrentLength); EnqueueFreePacket(packet); }
TryReadUSB(adapter); } else { KIRQL oldIrql;
/*
* The read failed. Put the packet back in the free list. */ DBGWARN(("ReadPipeCompletion: read failed with status %xh on adapter %xh (urb status = %xh).", status, adapter, urb->UrbHeader.Status)); #if DO_FULL_RESET
switch (USBD_STATUS(urb->UrbBulkOrInterruptTransfer.Hdr.Status)){ case USBD_STATUS(USBD_STATUS_STALL_PID): case USBD_STATUS(USBD_STATUS_DEV_NOT_RESPONDING): case USBD_STATUS(USBD_STATUS_ENDPOINT_HALTED): /*
* Set a flag so we do a full reset in the workItem * (QueueAdapterWorkItem is called below) */ adapter->needFullReset = TRUE; break; } #endif
EnqueueFreePacket(packet);
/*
* We're probably halting or resetting. * Don't reissue a read synchronously here because it will probably * keep failing on the same thread and cause us to blow the stack. */ KeAcquireSpinLock(&adapter->adapterSpinLock, &oldIrql); adapter->numConsecutiveReadFailures++; adapter->readDeficit++; KeReleaseSpinLock(&adapter->adapterSpinLock, oldIrql); QueueAdapterWorkItem(adapter); }
}
return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS SubmitUSBWritePacket(USBPACKET *packet) { NTSTATUS status; ADAPTEREXT *adapter = packet->adapter; PURB urb = packet->urbPtr; PIRP irp = packet->irpPtr;
/*
* Some device USB controllers cannot detect the end of a transfer unless there * is a short packet at the end. So if the transfer is a multiple of the * endpoint's wMaxPacketSize, add a byte to force a short packet at the end. */ if ((packet->dataBufferCurrentLength % adapter->writePipeLength) == 0){ packet->dataBuffer[packet->dataBufferCurrentLength++] = 0x00; }
ASSERT(packet->dataBufferCurrentLength <= PACKET_BUFFER_SIZE); DBGVERBOSE(("SubmitUSBWritePacket: %xh bytes, packet # %xh.", packet->dataBufferCurrentLength, packet->packetId)); DBGSHOWBYTES("SubmitUSBWritePacket", packet->dataBuffer, packet->dataBufferCurrentLength);
urb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER; urb->UrbBulkOrInterruptTransfer.Hdr.Length = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER); urb->UrbBulkOrInterruptTransfer.PipeHandle = adapter->writePipeHandle; urb->UrbBulkOrInterruptTransfer.TransferBufferLength = packet->dataBufferCurrentLength; urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL; urb->UrbBulkOrInterruptTransfer.TransferBuffer = packet->dataBuffer; urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_OUT; urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
status = SubmitUrbIrp( adapter->nextDevObj, irp, urb, FALSE, // asynchronous
WritePipeCompletion, // completion routine
packet // completion context
);
if (!NT_SUCCESS(status)){ DBGERR(("SubmitUSBWritePacket: packet @ %ph status %xh.", packet, status)); }
return status; }
NTSTATUS WritePipeCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context) { USBPACKET *packet = (USBPACKET *)context; ADAPTEREXT *adapter = packet->adapter; NTSTATUS status = irp->IoStatus.Status; KIRQL oldIrql;
ASSERT(packet->sig == DRIVER_SIG); ASSERT(adapter->sig == DRIVER_SIG); ASSERT(packet->irpPtr == irp); ASSERT(!irp->CancelRoutine); ASSERT(status != STATUS_PENDING); // saw UHCD doing this ?
if (NT_SUCCESS(status)){ DBGVERBOSE(("WritePipeCompletion: packet # %xh completed.", packet->packetId)); } else { DBGWARN(("WritePipeCompletion: packet # %xh failed with status %xh on adapter %xh.", packet->packetId, status, adapter)); }
IndicateSendStatusToRNdis(packet, status);
/*
* Dequeue the packet from the usbPendingWritePackets queue * BEFORE checking if it was cancelled to avoid race with CancelAllPendingPackets. */ DequeuePendingWritePacket(packet);
if (packet->cancelled){ /*
* This packet was cancelled because of a halt or reset. * Put the packet back in the free list first, then * set the event so CancelAllPendingPackets can proceed. */ DBGVERBOSE((" ... write packet #%xh cancelled.", packet->packetId)); packet->cancelled = FALSE;
EnqueueFreePacket(packet); KeSetEvent(&packet->cancelEvent, 0, FALSE); } else { EnqueueFreePacket(packet); }
return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS SubmitNotificationRead(ADAPTEREXT *adapter, BOOLEAN synchronous) { NTSTATUS status; PURB urb = adapter->notifyUrbPtr; PIRP irp = adapter->notifyIrpPtr; ULONG guardWord = GUARD_WORD; KIRQL oldIrql;
ASSERT(adapter->notifyPipeHandle); DBGVERBOSE(("SubmitNotificationRead: read %xh bytes.", adapter->notifyPipeLength)); /*
* Fill the notify buffer with invalid data just in case a device replies with * no data at all. A previously received valid message may still be there. * Apparently it won't be overwritten by the USB stack unless the device * supplies data. */ RtlFillMemory(adapter->notifyBuffer, adapter->notifyPipeLength, 0xfe);
/*
* Place a guard word at the end of the notify buffer * to catch overwrites by the host controller (which we've seen). * Use RtlCopyMemory in case pointer is unaligned. */ RtlCopyMemory(adapter->notifyBuffer+adapter->notifyPipeLength, &guardWord, sizeof(ULONG));
/*
* The notify pipe actually fills out a buffer with the fields given * in the spec as URB fields. Read the notify pipe like any interrupt pipe. */ urb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER; urb->UrbBulkOrInterruptTransfer.Hdr.Length = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER); urb->UrbBulkOrInterruptTransfer.PipeHandle = adapter->notifyPipeHandle; urb->UrbBulkOrInterruptTransfer.TransferBufferLength = adapter->notifyPipeLength; urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL; urb->UrbBulkOrInterruptTransfer.TransferBuffer = adapter->notifyBuffer; urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_IN; urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
KeAcquireSpinLock(&adapter->adapterSpinLock, &oldIrql); adapter->notifyBufferCurrentLength = 0; adapter->notifyStopped = FALSE; KeReleaseSpinLock(&adapter->adapterSpinLock, oldIrql);
if (synchronous){ status = SubmitUrbIrp(adapter->nextDevObj, irp, urb, TRUE, NULL, NULL); } else { status = SubmitUrbIrp( adapter->nextDevObj, irp, urb, FALSE, // asynchronous
NotificationCompletion, // completion routine
adapter // completion context
); }
return status; }
NTSTATUS NotificationCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context) { ADAPTEREXT *adapter = context; PURB urb = adapter->notifyUrbPtr; NTSTATUS status = irp->IoStatus.Status; BOOLEAN notifyStopped = FALSE; BOOLEAN setCancelEvent = FALSE; ULONG guardWord; KIRQL oldIrql;
ASSERT(adapter->sig == DRIVER_SIG); ASSERT(irp == adapter->notifyIrpPtr); ASSERT(!irp->CancelRoutine); ASSERT(status != STATUS_PENDING); // saw UHCD doing this ?
/*
* Check the guard word at the end of the notify buffer * to catch overwrites by the host controller * (we've seen this on VIA host controllers). * Use RtlCopyMemory in case pointer is unaligned. */ RtlCopyMemory(&guardWord, adapter->notifyBuffer+adapter->notifyPipeLength, sizeof(ULONG)); if (guardWord != GUARD_WORD){ ASSERT(guardWord == GUARD_WORD);
#ifndef SPECIAL_WIN98SE_BUILD
{ /*
* Raise an exception so we catch this in retail builds. */ EXCEPTION_RECORD exceptionRec; exceptionRec.ExceptionCode = STATUS_ADAPTER_HARDWARE_ERROR; exceptionRec.ExceptionFlags = EXCEPTION_NONCONTINUABLE_EXCEPTION; exceptionRec.ExceptionRecord = NULL; exceptionRec.ExceptionAddress = (PVOID)NotificationCompletion; // actual fault is in bus-mastering hardware
exceptionRec.NumberParameters = 0; // ExRaiseException(&exceptionRec);
// Changed to KeBugCheckEx since ExRaiseException is not a WDM call.
// We want to be loadable on WinMe.
KeBugCheckEx(FATAL_UNHANDLED_HARD_ERROR, STATUS_ADAPTER_HARDWARE_ERROR, (ULONG_PTR)adapter, (ULONG_PTR)guardWord, (ULONG_PTR)NULL); } #endif
}
/*
* In order to synchronize with CancelAllPendingPackets, * we need to either send the irp down again, mark the notifyIrp as stopped, * or set the notifyCancelEvent. */ KeAcquireSpinLock(&adapter->adapterSpinLock, &oldIrql); if (adapter->cancellingNotify){ /*
* This irp was cancelled by CancelAllPendingPackets. * After dropping the spinlock, we'll set the cancel event * so that CancelAllPendingPackets stops waiting. */ notifyStopped = TRUE; setCancelEvent = TRUE; } else if (!NT_SUCCESS(status)){ /*
* The notify irp can get failed on an unplug BEFORE we get the halted. * Since we're not going to send the notify IRP down again, we need to * make sure that we don't wait for it forever in CancelAllPendingPackets. * We do this by synchronously setting notifyStopped * as an indication that this irp doesn't need to be cancelled. */ DBGWARN(("NotificationCompletion: read failed with status %xh on adapter %xh (urb status = %xh).", status, adapter, urb->UrbHeader.Status)); notifyStopped = adapter->notifyStopped = TRUE; } KeReleaseSpinLock(&adapter->adapterSpinLock, oldIrql);
if (!notifyStopped){ ULONG notifyLen = urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
ASSERT(notifyLen <= adapter->notifyPipeLength); adapter->notifyBufferCurrentLength = MIN(notifyLen, adapter->notifyPipeLength);
RNDISProcessNotification(adapter);
SubmitNotificationRead(adapter, FALSE); }
if (setCancelEvent){ DBGVERBOSE((" ... notify read packet cancelled.")); KeSetEvent(&adapter->notifyCancelEvent, 0, FALSE); }
return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS SubmitPacketToControlPipe( USBPACKET *packet, BOOLEAN synchronous, BOOLEAN simulated) { NTSTATUS status; ADAPTEREXT *adapter = packet->adapter; PURB urb = packet->urbPtr; PIRP irp = packet->irpPtr; PUSBD_INTERFACE_INFORMATION interfaceInfoControl;
DBGVERBOSE(("SubmitPacketToControlPipe: packet # %xh.", packet->packetId)); DBGSHOWBYTES("SubmitPacketToControlPipe", packet->dataBuffer, packet->dataBufferCurrentLength);
ASSERT(adapter->interfaceInfoMaster); interfaceInfoControl = adapter->interfaceInfoMaster;
urb->UrbHeader.Length = (USHORT) sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST); urb->UrbHeader.Function = URB_FUNCTION_CLASS_INTERFACE; urb->UrbControlVendorClassRequest.Reserved = 0; urb->UrbControlVendorClassRequest.TransferFlags = USBD_TRANSFER_DIRECTION_OUT; urb->UrbControlVendorClassRequest.TransferBufferLength = packet->dataBufferCurrentLength; urb->UrbControlVendorClassRequest.TransferBuffer = packet->dataBuffer; urb->UrbControlVendorClassRequest.TransferBufferMDL = NULL; urb->UrbControlVendorClassRequest.UrbLink = NULL; urb->UrbControlVendorClassRequest.RequestTypeReservedBits = 0; urb->UrbControlVendorClassRequest.Request = NATIVE_RNDIS_SEND_ENCAPSULATED_COMMAND; urb->UrbControlVendorClassRequest.Value = 0; urb->UrbControlVendorClassRequest.Index = interfaceInfoControl->InterfaceNumber; urb->UrbControlVendorClassRequest.Reserved1 = 0;
if (synchronous){ /*
* Send the URB down synchronously, * then call the completion routine to clean up ourselves. */ status = SubmitUrbIrp(adapter->nextDevObj, irp, urb, TRUE, NULL, NULL); if (!simulated){ ControlPipeWriteCompletion(adapter->nextDevObj, irp, packet); } } else { status = SubmitUrbIrp( adapter->nextDevObj, irp, urb, FALSE, // asynchronous
ControlPipeWriteCompletion, // completion routine
packet // completion context
); }
return status; }
NTSTATUS ControlPipeWriteCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context) { USBPACKET *packet = (USBPACKET *)context; ADAPTEREXT *adapter = packet->adapter; NTSTATUS status = irp->IoStatus.Status; KIRQL oldIrql;
ASSERT(packet->sig == DRIVER_SIG); ASSERT(adapter->sig == DRIVER_SIG); ASSERT(packet->irpPtr == irp); ASSERT(!irp->CancelRoutine); ASSERT(status != STATUS_PENDING); // saw UHCD doing this ?
if (NT_SUCCESS(status)){ DBGVERBOSE(("ControlPipeWriteCompletion: packet # %xh completed.", packet->packetId)); } else { DBGWARN(("ControlPipeWriteCompletion: packet # %xh failed with status %xh on adapter %xh.", packet->packetId, status, adapter)); }
IndicateSendStatusToRNdis(packet, status);
/*
* Dequeue the packet from the usbPendingWritePackets queue * BEFORE checking if it was cancelled to avoid race with CancelAllPendingPackets. */ DequeuePendingWritePacket(packet);
if (packet->cancelled){ /*
* This packet was cancelled because of a halt or reset. * Put the packet back in the free list first, then * set the event so CancelAllPendingPackets can proceed. */ DBGVERBOSE((" ... write packet #%xh cancelled.", packet->packetId)); packet->cancelled = FALSE;
EnqueueFreePacket(packet); KeSetEvent(&packet->cancelEvent, 0, FALSE); } else { EnqueueFreePacket(packet); }
if (NT_SUCCESS(status)){ } else { #if DO_FULL_RESET
adapter->needFullReset = TRUE; QueueAdapterWorkItem(adapter); #endif
}
return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS ReadPacketFromControlPipe(USBPACKET *packet, BOOLEAN synchronous) { NTSTATUS status; ADAPTEREXT *adapter = packet->adapter; PURB urb = packet->urbPtr; PIRP irp = packet->irpPtr; PUSBD_INTERFACE_INFORMATION interfaceInfoControl; ULONG bytesToRead = MAXIMUM_DEVICE_MESSAGE_SIZE+1;
DBGVERBOSE(("ReadPacketFromControlPipe: read %xh bytes, packet #%xh.", bytesToRead, packet->packetId));
ASSERT(adapter->interfaceInfoMaster); interfaceInfoControl = adapter->interfaceInfoMaster; urb->UrbHeader.Length = (USHORT) sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST); urb->UrbHeader.Function = URB_FUNCTION_CLASS_INTERFACE; urb->UrbControlVendorClassRequest.Reserved = 0; urb->UrbControlVendorClassRequest.TransferFlags = USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_IN; urb->UrbControlVendorClassRequest.TransferBufferLength = bytesToRead; urb->UrbControlVendorClassRequest.TransferBuffer = packet->dataBuffer; urb->UrbControlVendorClassRequest.TransferBufferMDL = NULL; urb->UrbControlVendorClassRequest.UrbLink = NULL; urb->UrbControlVendorClassRequest.RequestTypeReservedBits = 0; urb->UrbControlVendorClassRequest.Request = NATIVE_RNDIS_GET_ENCAPSULATED_RESPONSE; urb->UrbControlVendorClassRequest.Value = 0; urb->UrbControlVendorClassRequest.Index = interfaceInfoControl->InterfaceNumber; urb->UrbControlVendorClassRequest.Reserved1 = 0;
if (synchronous){ status = SubmitUrbIrp(adapter->nextDevObj, irp, urb, TRUE, NULL, NULL); } else { status = SubmitUrbIrp( adapter->nextDevObj, irp, urb, FALSE, // asynchronous
ControlPipeReadCompletion, // completion routine
packet // completion context
); }
ASSERT(NT_SUCCESS(status)); return status; }
NTSTATUS ControlPipeReadCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context) { USBPACKET *packet = (USBPACKET *)context; ADAPTEREXT *adapter = packet->adapter; NTSTATUS status = irp->IoStatus.Status; KIRQL oldIrql;
ASSERT(packet->sig == DRIVER_SIG); ASSERT(adapter->sig == DRIVER_SIG); ASSERT(packet->irpPtr == irp); ASSERT(!irp->CancelRoutine); ASSERT(status != STATUS_PENDING); // saw UHCD doing this ?
/*
* Dequeue the packet from the usbPendingReadPackets queue * BEFORE checking if it was cancelled to avoid race with CancelAllPendingPackets. */ DequeuePendingReadPacket(packet);
if (packet->cancelled){ /*
* This packet was cancelled because of a halt or reset. * Get the packet is back in the free list first, then * set the event so CancelAllPendingPackets can proceed. */ DBGVERBOSE((" ... read packet #%xh cancelled.", packet->packetId)); packet->cancelled = FALSE;
EnqueueFreePacket(packet); KeSetEvent(&packet->cancelEvent, 0, FALSE); } else if (adapter->halting){ EnqueueFreePacket(packet); } else { if (NT_SUCCESS(status)){ PURB urb = packet->urbPtr; /*
* Fix the packet's dataBufferCurrentLength to indicate the actual length * of the returned data. * Note: the KLSI device rounds this up to a multiple of the endpoint * packet size, so the returned length may actually be larger than * the actual data. */ packet->dataBufferCurrentLength = urb->UrbBulkOrInterruptTransfer.TransferBufferLength; ASSERT(packet->dataBufferCurrentLength); ASSERT(packet->dataBufferCurrentLength <= packet->dataBufferMaxLength);
DBGVERBOSE(("ControlPipeReadCompletion: packet # %xh.", packet->packetId)); DBGSHOWBYTES("ControlPipeReadCompletion", packet->dataBuffer, packet->dataBufferCurrentLength);
EnqueueCompletedReadPacket(packet);
status = IndicateRndisMessage(packet, FALSE);
if (status != STATUS_PENDING){ DequeueCompletedReadPacket(packet); EnqueueFreePacket(packet); } } else { /*
* The read failed. Put the packet back in the free list. */ DBGWARN(("ControlPipeReadCompletion: read failed with status %xh on adapter %xh.", status, adapter)); EnqueueFreePacket(packet);
#if DO_FULL_RESET
adapter->needFullReset = TRUE; QueueAdapterWorkItem(adapter); #endif
}
}
return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS CallDriverSync(PDEVICE_OBJECT devObj, PIRP irp) /*++
Routine Description:
Call IoCallDriver to send the irp to the device object; then, synchronize with the completion routine. When CallDriverSync returns, the action has completed and the irp again belongs to the current driver.
NOTE: In order to keep the device object from getting freed while this IRP is pending, you should call IncrementPendingActionCount() and DecrementPendingActionCount() around the CallDriverSync call.
Arguments:
devObj - targetted device object irp - Io Request Packet
Return Value:
NT status code, indicates result returned by lower driver for this IRP.
--*/ { KEVENT event; NTSTATUS status;
ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp->IoStatus.Status = STATUS_NOT_SUPPORTED; IoSetCompletionRoutine( irp, CallDriverSyncCompletion, &event, // context
TRUE, TRUE, TRUE);
status = IoCallDriver(devObj, irp);
KeWaitForSingleObject( &event, Executive, // wait reason
KernelMode, FALSE, // not alertable
NULL ); // no timeout
status = irp->IoStatus.Status;
ASSERT(status != STATUS_PENDING); if (!NT_SUCCESS(status)){ DBGWARN(("CallDriverSync: irp failed w/ status %xh.", status)); }
return status; }
NTSTATUS CallDriverSyncCompletion(IN PDEVICE_OBJECT devObjOrNULL, IN PIRP irp, IN PVOID context) /*++
Routine Description:
Completion routine for CallDriverSync.
Arguments:
devObjOrNULL - Usually, this is this driver's device object. However, if this driver created the IRP, there is no stack location in the IRP for this driver; so the kernel has no place to store the device object; ** so devObj will be NULL in this case **.
irp - completed Io Request Packet context - context passed to IoSetCompletionRoutine by CallDriverSync.
Return Value:
NT status code, indicates result returned by lower driver for this IRP.
--*/ { PKEVENT event = context;
ASSERT(!irp->CancelRoutine);
if (!NT_SUCCESS(irp->IoStatus.Status)){ DBGWARN(("CallDriverSyncCompletion: irp failed w/ status %xh.", irp->IoStatus.Status)); }
KeSetEvent(event, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED; }
#if 0
NTSTATUS GetStringDescriptor( ADAPTEREXT *adapter, UCHAR stringIndex, PUCHAR buffer, ULONG bufferLen) /*++
Routine Description:
Function retrieves a string descriptor from the device
Arguments:
adapter - adapter context
Return Value:
NT status code
--*/ { URB urb; NTSTATUS status;
UsbBuildGetDescriptorRequest(&urb, (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), USB_STRING_DESCRIPTOR_TYPE, stringIndex, 0x0409, // language = US English
buffer, NULL, bufferLen, NULL);
status = SubmitUrb(adapter->nextDevObj, &urb, TRUE, NULL, NULL);
if (NT_SUCCESS(status)){ DBGVERBOSE(("Got string desc (index %xh) @ %ph, len = %xh.", (ULONG)stringIndex, buffer, urb.UrbControlDescriptorRequest.TransferBufferLength)); ASSERT(urb.UrbControlDescriptorRequest.TransferBufferLength <= bufferLen); } else { DBGERR(("GetStringDescriptor: failed to get string (index %xh) with status %xh on adapter %xh.", (ULONG)stringIndex, status, adapter)); }
ASSERT(NT_SUCCESS(status)); return status; }
/*
* CreateSingleInterfaceConfigDesc * * Allocate a configuration descriptor that excludes all interfaces * but the given interface * (e.g. for multiple-interface devices like the Intel cable modem, * for which we don't load on top of the generic parent). * * Note: interfaceDesc must point inside configDesc. * */ PUSB_CONFIGURATION_DESCRIPTOR CreateSingleInterfaceConfigDesc( PUSB_CONFIGURATION_DESCRIPTOR configDesc, PUSB_INTERFACE_DESCRIPTOR interfaceDesc) { PUSB_CONFIGURATION_DESCRIPTOR ifaceConfigDesc; ASSERT(interfaceDesc); ASSERT((PVOID)interfaceDesc > (PVOID)configDesc); ASSERT((PUCHAR)interfaceDesc - (PUCHAR)configDesc < configDesc->wTotalLength);
ifaceConfigDesc = AllocPool(configDesc->wTotalLength); if (ifaceConfigDesc){ PUSB_COMMON_DESCRIPTOR srcDesc, newDesc; USHORT totalLen;
/*
* Copy the configuration descriptor itself. */ RtlCopyMemory(ifaceConfigDesc, configDesc, configDesc->bLength); totalLen = configDesc->bLength;
/*
* Copy the given interface descriptor. */ srcDesc = (PUSB_COMMON_DESCRIPTOR)interfaceDesc; newDesc = (PUSB_COMMON_DESCRIPTOR)((PUCHAR)ifaceConfigDesc + ifaceConfigDesc->bLength); RtlCopyMemory(newDesc, srcDesc, srcDesc->bLength); totalLen += srcDesc->bLength; srcDesc = (PUSB_COMMON_DESCRIPTOR)((PUCHAR)srcDesc + srcDesc->bLength); newDesc = (PUSB_COMMON_DESCRIPTOR)((PUCHAR)newDesc + newDesc->bLength);
/*
* Copy the given interface descriptors and all following descriptors * up to either the next interface descriptor or the end of the original * configuration descriptor. */ while ((PUCHAR)srcDesc - (PUCHAR)configDesc < configDesc->wTotalLength){ if (srcDesc->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE){ break; } else { RtlCopyMemory(newDesc, srcDesc, srcDesc->bLength); totalLen += srcDesc->bLength; srcDesc = (PUSB_COMMON_DESCRIPTOR)((PUCHAR)srcDesc + srcDesc->bLength); newDesc = (PUSB_COMMON_DESCRIPTOR)((PUCHAR)newDesc + newDesc->bLength); } }
ifaceConfigDesc->bNumInterfaces = 1; ifaceConfigDesc->wTotalLength = totalLen; DBGVERBOSE(("CreateSingleInterfaceConfigDesc: build partial configDesc @ %ph, len=%xh.", ifaceConfigDesc, ifaceConfigDesc->wTotalLength)); } return ifaceConfigDesc; } #endif
|