windows-nt/Source/XPSP1/NT/net/rndis/usb8023/usb.c
2020-09-26 16:20:57 +08:00

1538 lines
52 KiB
C

/*++
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 = &notifyInterfaceInfo->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