1348 lines
44 KiB
C
1348 lines
44 KiB
C
/***************************************************************************
|
||
|
||
Copyright (c) 2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Dot4Usb.sys - Lower Filter Driver for Dot4.sys for USB connected
|
||
IEEE 1284.4 devices.
|
||
|
||
File Name:
|
||
|
||
Usb.c
|
||
|
||
Abstract:
|
||
|
||
Interface USB DeviceObject below us
|
||
|
||
Environment:
|
||
|
||
Kernel mode only
|
||
|
||
Notes:
|
||
|
||
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
||
PURPOSE.
|
||
|
||
Copyright (c) 2000 Microsoft Corporation. All Rights Reserved.
|
||
|
||
Revision History:
|
||
|
||
01/18/2000 : created
|
||
|
||
Author(s):
|
||
|
||
Joby Lafky (JobyL)
|
||
Doug Fritz (DFritz)
|
||
|
||
****************************************************************************/
|
||
|
||
#include "pch.h"
|
||
|
||
|
||
NTSTATUS
|
||
UsbBuildPipeList(
|
||
IN PDEVICE_OBJECT DevObj
|
||
)
|
||
// Parse the interface descriptor to find the pipes that we want
|
||
// to use and save pointers to those pipes in our extension for
|
||
// easier access
|
||
{
|
||
PDEVICE_EXTENSION devExt = DevObj->DeviceExtension;
|
||
PUSBD_INTERFACE_INFORMATION InterfaceDescriptor;
|
||
ULONG i;
|
||
KIRQL oldIrql;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
TR_VERBOSE(("UsbBuildPipeList - enter"));
|
||
|
||
// need to lock extension to prevent Remove handler from freeing
|
||
// Interface out from under us causing an AV
|
||
KeAcquireSpinLock( &devExt->SpinLock, &oldIrql );
|
||
InterfaceDescriptor = devExt->Interface;
|
||
if( !InterfaceDescriptor ) {
|
||
KeReleaseSpinLock( &devExt->SpinLock, oldIrql );
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto targetExit;
|
||
}
|
||
|
||
for( i=0; i<InterfaceDescriptor->NumberOfPipes; i++ ) {
|
||
TR_VERBOSE(("about to look at endpoint with address 0x%x)",InterfaceDescriptor->Pipes[i].EndpointAddress));
|
||
if(((InterfaceDescriptor->Pipes[i].EndpointAddress)&0x80)==0) {
|
||
|
||
// EndPointAddress bit 7 == 0 means OUT endpoint - WritePipe
|
||
TR_VERBOSE(("Found write pipe"));
|
||
devExt->WritePipe = &(InterfaceDescriptor->Pipes[i]);
|
||
|
||
} else {
|
||
|
||
// EndPointAddress bit 7 == 1 means IN endpoint - ReadPipe
|
||
if( InterfaceDescriptor->Pipes[i].PipeType == UsbdPipeTypeBulk ) {
|
||
TR_VERBOSE(("Found bulk read pipe"));
|
||
devExt->ReadPipe = &(InterfaceDescriptor->Pipes[i]);
|
||
} else if( InterfaceDescriptor->Pipes[i].PipeType == UsbdPipeTypeInterrupt ) {
|
||
TR_VERBOSE(("Found interrupt read pipe"));
|
||
devExt->InterruptPipe = &(InterfaceDescriptor->Pipes[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
KeReleaseSpinLock( &devExt->SpinLock, oldIrql );
|
||
|
||
targetExit:
|
||
return status;
|
||
}
|
||
|
||
|
||
LONG
|
||
UsbGet1284Id(
|
||
IN PDEVICE_OBJECT DevObj,
|
||
PVOID Buffer,
|
||
LONG BufferLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Requests and returns Printer 1284 Device ID
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to the device object for this instance of the printer device.
|
||
pIoBuffer - pointer to IO buffer from user mode
|
||
iLen - Length of *pIoBuffer;
|
||
|
||
|
||
|
||
|
||
Return Value:
|
||
|
||
Success: Length of data written to *pIoBuffer (icluding lenght field in first two bytes of data)
|
||
Failure: -1
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
PURB urb;
|
||
LONG iReturn = -1;
|
||
PDEVICE_EXTENSION devExt = DevObj->DeviceExtension;
|
||
LARGE_INTEGER timeOut;
|
||
KIRQL oldIrql;
|
||
|
||
TR_VERBOSE(("UsbGet1284Id - enter"));
|
||
|
||
urb = ExAllocatePool(NonPagedPool,sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
|
||
|
||
if( !urb ) {
|
||
iReturn = -1;
|
||
goto targetExit;
|
||
}
|
||
|
||
KeAcquireSpinLock( &devExt->SpinLock, &oldIrql );
|
||
if( !devExt->Interface ) {
|
||
KeReleaseSpinLock( &devExt->SpinLock, oldIrql );
|
||
iReturn = -1;
|
||
goto targetCleanup;
|
||
}
|
||
|
||
UsbBuildVendorRequest( urb,
|
||
URB_FUNCTION_CLASS_INTERFACE, //request target
|
||
sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST), //request len
|
||
USBD_TRANSFER_DIRECTION_IN|USBD_SHORT_TRANSFER_OK, //flags
|
||
0, //reserved bits
|
||
0, //request code
|
||
0, //wValue
|
||
(USHORT)(devExt->Interface->InterfaceNumber<<8), //wIndex
|
||
Buffer, //return buffer address
|
||
NULL, //mdl
|
||
BufferLength, //return length
|
||
NULL); //link param
|
||
|
||
KeReleaseSpinLock( &devExt->SpinLock, oldIrql );
|
||
|
||
timeOut.QuadPart = FAILURE_TIMEOUT;
|
||
ntStatus = UsbCallUsbd(DevObj, urb, &timeOut);
|
||
TR_VERBOSE(("urb->Hdr.Status=%d",((struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST *)urb)->Hdr.Status));
|
||
|
||
if( NT_SUCCESS(ntStatus) && urb->UrbControlVendorClassRequest.TransferBufferLength > 2) {
|
||
iReturn= (LONG)(*((unsigned char *)Buffer));
|
||
iReturn<<=8;
|
||
iReturn+=(LONG)(*(((unsigned char *)Buffer)+1));
|
||
if ( iReturn > 0 && iReturn < BufferLength ) {
|
||
*(((char *)Buffer)+iReturn)='\0';
|
||
} else {
|
||
iReturn = -1;
|
||
}
|
||
} else {
|
||
iReturn=-1;
|
||
}
|
||
|
||
targetCleanup:
|
||
ExFreePool(urb);
|
||
|
||
targetExit:
|
||
TR_VERBOSE(("UsbGet1284Id - exit w/return value = decimal %d",iReturn));
|
||
return iReturn;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
UsbGetDescriptor(
|
||
IN PDEVICE_EXTENSION DevExt
|
||
)
|
||
// get USB descriptor
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PURB urb = ExAllocatePool(NonPagedPool, sizeof(URB));
|
||
PUSB_DEVICE_DESCRIPTOR deviceDescriptor = NULL;
|
||
ULONG siz;
|
||
LARGE_INTEGER timeOut;
|
||
|
||
TR_VERBOSE(("UsbGetDescriptor - enter"));
|
||
|
||
if( urb ) {
|
||
siz = sizeof(USB_DEVICE_DESCRIPTOR);
|
||
deviceDescriptor = ExAllocatePool(NonPagedPool,siz);
|
||
if (deviceDescriptor) {
|
||
UsbBuildGetDescriptorRequest(urb,
|
||
(USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
|
||
USB_DEVICE_DESCRIPTOR_TYPE,
|
||
0,
|
||
0,
|
||
deviceDescriptor,
|
||
NULL,
|
||
siz,
|
||
NULL);
|
||
|
||
timeOut.QuadPart = FAILURE_TIMEOUT;
|
||
status = UsbCallUsbd(DevExt->DevObj, urb, &timeOut);
|
||
}
|
||
} else {
|
||
TR_VERBOSE(("UsbGetDescriptor - no pool for urb"));
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if( NT_SUCCESS(status) ) {
|
||
TR_VERBOSE(("Device Descriptor = %x, len %x", deviceDescriptor, urb->UrbControlDescriptorRequest.TransferBufferLength));
|
||
TR_VERBOSE(("bLength........... 0x%x", deviceDescriptor->bLength));
|
||
TR_VERBOSE(("bDescriptorType 0x%x", deviceDescriptor->bDescriptorType));
|
||
TR_VERBOSE(("bcdUSB 0x%x", deviceDescriptor->bcdUSB));
|
||
TR_VERBOSE(("bDeviceClass 0x%x", deviceDescriptor->bDeviceClass));
|
||
TR_VERBOSE(("bDeviceSubClass....0x%x", deviceDescriptor->bDeviceSubClass));
|
||
TR_VERBOSE(("bDeviceProtocol 0x%x", deviceDescriptor->bDeviceProtocol));
|
||
TR_VERBOSE(("bMaxPacketSize0 0x%x", deviceDescriptor->bMaxPacketSize0));
|
||
TR_VERBOSE(("idVendor 0x%x", deviceDescriptor->idVendor));
|
||
TR_VERBOSE(("idProduct......... 0x%x", deviceDescriptor->idProduct));
|
||
TR_VERBOSE(("bcdDevice 0x%x", deviceDescriptor->bcdDevice));
|
||
TR_VERBOSE(("iManufacturer 0x%x", deviceDescriptor->iManufacturer));
|
||
TR_VERBOSE(("iProduct 0x%x", deviceDescriptor->iProduct));
|
||
TR_VERBOSE(("iSerialNumber..... 0x%x", deviceDescriptor->iSerialNumber));
|
||
TR_VERBOSE(("bNumConfigurations 0x%x", deviceDescriptor->bNumConfigurations));
|
||
}
|
||
|
||
if( urb ) {
|
||
ExFreePool( urb );
|
||
urb = NULL;
|
||
}
|
||
if( deviceDescriptor ) {
|
||
ExFreePool( deviceDescriptor );
|
||
deviceDescriptor = NULL;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
UsbConfigureDevice(
|
||
IN PDEVICE_EXTENSION DevExt
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
PURB urb;
|
||
ULONG siz;
|
||
PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = NULL;
|
||
LARGE_INTEGER timeOut;
|
||
|
||
timeOut.QuadPart = FAILURE_TIMEOUT;
|
||
|
||
urb = ExAllocatePool(NonPagedPool,sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
|
||
|
||
if (urb) {
|
||
|
||
siz = sizeof(USB_CONFIGURATION_DESCRIPTOR)+256;
|
||
|
||
get_config_descriptor_retry:
|
||
|
||
configurationDescriptor = ExAllocatePool(NonPagedPool,siz);
|
||
|
||
if (configurationDescriptor) {
|
||
|
||
UsbBuildGetDescriptorRequest(urb,
|
||
(USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
|
||
USB_CONFIGURATION_DESCRIPTOR_TYPE,
|
||
0,
|
||
0,
|
||
configurationDescriptor,
|
||
NULL,
|
||
siz,
|
||
NULL);
|
||
|
||
status = UsbCallUsbd(DevExt->DevObj, urb, &timeOut);
|
||
if(!NT_SUCCESS(status)) {
|
||
TR_VERBOSE(("Get Configuration descriptor failed"));
|
||
} else {
|
||
//
|
||
// if we got some data see if it was enough.
|
||
//
|
||
// NOTE: we may get an error in URB because of buffer overrun
|
||
if( ( urb->UrbControlDescriptorRequest.TransferBufferLength > 0 ) &&
|
||
( configurationDescriptor->wTotalLength > siz ) ) {
|
||
|
||
siz = configurationDescriptor->wTotalLength;
|
||
ExFreePool(configurationDescriptor);
|
||
configurationDescriptor = NULL;
|
||
goto get_config_descriptor_retry;
|
||
}
|
||
}
|
||
|
||
TR_VERBOSE(("Configuration Descriptor = %x, len %x",
|
||
configurationDescriptor, urb->UrbControlDescriptorRequest.TransferBufferLength));
|
||
} else {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
ExFreePool( urb );
|
||
|
||
} else {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if( configurationDescriptor ) {
|
||
|
||
//
|
||
// We have the configuration descriptor for the configuration
|
||
// we want.
|
||
//
|
||
// Now we issue the select configuration command to get
|
||
// the pipes associated with this configuration.
|
||
//
|
||
if( NT_SUCCESS(status) ) {
|
||
TR_VERBOSE(("got a configurationDescriptor - next try to select interface"));
|
||
status = UsbSelectInterface( DevExt->DevObj, configurationDescriptor );
|
||
}
|
||
ExFreePool( configurationDescriptor );
|
||
}
|
||
|
||
TR_VERBOSE(("dbgUSB2 - exit w/status = %x", status));
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
UsbSelectInterface(
|
||
IN PDEVICE_OBJECT DevObj,
|
||
IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor
|
||
)
|
||
{
|
||
PDEVICE_EXTENSION devExt = DevObj->DeviceExtension;
|
||
NTSTATUS status;
|
||
PURB urb = NULL;
|
||
PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor = NULL;
|
||
PUSBD_INTERFACE_INFORMATION Interface = NULL;
|
||
USBD_INTERFACE_LIST_ENTRY InterfaceList[2];
|
||
LARGE_INTEGER timeOut;
|
||
|
||
timeOut.QuadPart = FAILURE_TIMEOUT;
|
||
|
||
TR_VERBOSE(("dbgUSB3 - enter"));
|
||
|
||
//
|
||
// Look for a *.*.3 interface in the ConfigurationDescriptor
|
||
//
|
||
interfaceDescriptor = USBD_ParseConfigurationDescriptorEx( ConfigurationDescriptor,
|
||
ConfigurationDescriptor,
|
||
-1, // InterfaceNumber - ignore
|
||
-1, // AlternateSetting - ignore
|
||
-1, // InterfaceClass - ignore
|
||
-1, // InterfaceSubClass - ignore
|
||
3 // InterfaceProtocol
|
||
);
|
||
if( !interfaceDescriptor ) {
|
||
TR_VERBOSE(("ParseConfigurationDescriptorEx FAILED"));
|
||
status = STATUS_DEVICE_CONFIGURATION_ERROR;
|
||
goto targetExit;
|
||
}
|
||
|
||
TR_VERBOSE(("ParseConfigurationDescriptorEx SUCCESS"));
|
||
|
||
InterfaceList[0].InterfaceDescriptor=interfaceDescriptor;
|
||
InterfaceList[1].InterfaceDescriptor=NULL;
|
||
|
||
urb = USBD_CreateConfigurationRequestEx(ConfigurationDescriptor,InterfaceList);
|
||
if( !urb ) {
|
||
TR_VERBOSE(("no pool for URB - dbgUSB3"));
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto targetExit;
|
||
}
|
||
|
||
Interface = InterfaceList[0].Interface;
|
||
|
||
// handle larger transfers on pipes (perf requirement by scanning)
|
||
{
|
||
PUSBD_INTERFACE_INFORMATION myInterface = &urb->UrbSelectConfiguration.Interface;
|
||
ULONG i;
|
||
ULONG pipeCount = Interface->NumberOfPipes;
|
||
ULONG newMax = 128 * 1024 - 1;
|
||
for( i=0 ; i < pipeCount ; ++i ) {
|
||
myInterface->Pipes[i].MaximumTransferSize = newMax;
|
||
}
|
||
}
|
||
|
||
status = UsbCallUsbd(DevObj, urb, &timeOut);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Save the configuration handle for this device
|
||
//
|
||
|
||
devExt->ConfigHandle = urb->UrbSelectConfiguration.ConfigurationHandle;
|
||
|
||
devExt->Interface = ExAllocatePool(NonPagedPool,Interface->Length);
|
||
|
||
if( devExt->Interface ) {
|
||
ULONG j;
|
||
//
|
||
// save a copy of the interface information returned
|
||
//
|
||
RtlCopyMemory(devExt->Interface, Interface, Interface->Length);
|
||
|
||
//
|
||
// Dump the interface to the debugger
|
||
//
|
||
TR_VERBOSE(("NumberOfPipes 0x%x", devExt->Interface->NumberOfPipes));
|
||
TR_VERBOSE(("Length 0x%x", devExt->Interface->Length));
|
||
TR_VERBOSE(("Alt Setting 0x%x", devExt->Interface->AlternateSetting));
|
||
TR_VERBOSE(("Interface Number 0x%x", devExt->Interface->InterfaceNumber));
|
||
TR_VERBOSE(("Class, subclass, protocol 0x%x 0x%x 0x%x",
|
||
devExt->Interface->Class, devExt->Interface->SubClass, devExt->Interface->Protocol));
|
||
|
||
// Dump the pipe info
|
||
for( j=0; j<Interface->NumberOfPipes; ++j ) {
|
||
PUSBD_PIPE_INFORMATION pipeInformation;
|
||
|
||
pipeInformation = &devExt->Interface->Pipes[j];
|
||
|
||
TR_VERBOSE(("PipeType 0x%x", pipeInformation->PipeType));
|
||
TR_VERBOSE(("EndpointAddress 0x%x", pipeInformation->EndpointAddress));
|
||
TR_VERBOSE(("MaxPacketSize 0x%x", pipeInformation->MaximumPacketSize));
|
||
TR_VERBOSE(("Interval 0x%x", pipeInformation->Interval));
|
||
TR_VERBOSE(("Handle 0x%x", pipeInformation->PipeHandle));
|
||
TR_VERBOSE(("MaximumTransferSize 0x%x", pipeInformation->MaximumTransferSize));
|
||
}
|
||
|
||
} else {
|
||
TR_VERBOSE(("Alloc failed in SelectInterface"));
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
if( urb ) {
|
||
ExFreePool( urb );
|
||
}
|
||
|
||
targetExit:
|
||
|
||
TR_VERBOSE(("dbgUSB3 exit w/status = %x", status));
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
PURB
|
||
UsbBuildAsyncRequest(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PUSBD_PIPE_INFORMATION PipeHandle,
|
||
IN BOOLEAN Read
|
||
)
|
||
// return an initialized async URB, or NULL on error
|
||
{
|
||
ULONG siz;
|
||
PURB urb;
|
||
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
|
||
if( NULL == Irp->MdlAddress ) {
|
||
return NULL;
|
||
}
|
||
|
||
siz = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
|
||
urb = ExAllocatePool( NonPagedPool, siz );
|
||
|
||
if( urb ) {
|
||
RtlZeroMemory(urb, siz);
|
||
urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT) siz;
|
||
urb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
|
||
urb->UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle->PipeHandle;
|
||
urb->UrbBulkOrInterruptTransfer.TransferFlags = Read ? USBD_TRANSFER_DIRECTION_IN : 0;
|
||
|
||
// short packet is not treated as an error.
|
||
urb->UrbBulkOrInterruptTransfer.TransferFlags |= USBD_SHORT_TRANSFER_OK;
|
||
|
||
// no linkage for now
|
||
urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
|
||
|
||
urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = Irp->MdlAddress;
|
||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = MmGetMdlByteCount(Irp->MdlAddress);
|
||
}
|
||
|
||
return urb;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
UsbAsyncReadWriteComplete(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to the device object for the USBPRINT device.
|
||
|
||
Irp - Irp completed.
|
||
|
||
Context - Driver defined context.
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status from the operation.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PUSB_RW_CONTEXT rwContext = Context;
|
||
PURB urb;
|
||
LONG ResetPending;
|
||
PDOT4USB_WORKITEM_CONTEXT pResetWorkItemObj;
|
||
PDEVICE_EXTENSION deviceExtension;
|
||
|
||
|
||
deviceExtension=DeviceObject->DeviceExtension;
|
||
|
||
|
||
if (Irp->PendingReturned) {
|
||
IoMarkIrpPending(Irp);
|
||
}
|
||
|
||
urb = rwContext->Urb;
|
||
|
||
TR_VERBOSE(("UsbAsyncReadWriteComplete - enter - TransferBufferLength= %d, UrbStatus= 0x%08X",
|
||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength,
|
||
urb->UrbHeader.Status));
|
||
|
||
status=urb->UrbHeader.Status;
|
||
|
||
// set the length based on the TransferBufferLength value in the URB
|
||
Irp->IoStatus.Information = urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
|
||
|
||
if((!NT_SUCCESS(status))&&(status!=STATUS_CANCELLED)&&(status!=STATUS_DEVICE_NOT_CONNECTED))
|
||
{
|
||
ResetPending=InterlockedCompareExchange(&deviceExtension->ResetWorkItemPending,1,0); //Check to see if ResetWorkItem is 0, if so, set it to 1, and start a Reset
|
||
if(!ResetPending)
|
||
{
|
||
pResetWorkItemObj=ExAllocatePool(NonPagedPool,sizeof(DOT4USB_WORKITEM_CONTEXT));
|
||
if(pResetWorkItemObj)
|
||
{
|
||
pResetWorkItemObj->ioWorkItem=IoAllocateWorkItem(DeviceObject);
|
||
if(pResetWorkItemObj==NULL)
|
||
{
|
||
TR_FAIL(("DOT4USB.SYS: Unable to allocate IoAllocateWorkItem in ReadWrite_Complete\n"));
|
||
ExFreePool(pResetWorkItemObj);
|
||
pResetWorkItemObj=NULL;
|
||
}
|
||
} //if ALloc RestWorkItem OK
|
||
else
|
||
{
|
||
TR_FAIL(("DOT4USB.SYS: Unable to allocate WorkItemObj in ReadWrite_Complete\n"));
|
||
}
|
||
if(pResetWorkItemObj)
|
||
{
|
||
pResetWorkItemObj->irp=Irp;
|
||
pResetWorkItemObj->deviceObject=DeviceObject;
|
||
if(rwContext->IsWrite)
|
||
pResetWorkItemObj->pPipeInfo=deviceExtension->WritePipe;
|
||
else
|
||
pResetWorkItemObj->pPipeInfo=deviceExtension->ReadPipe;
|
||
IoQueueWorkItem(pResetWorkItemObj->ioWorkItem,DOT4USB_ResetWorkItem,DelayedWorkQueue,pResetWorkItemObj);
|
||
status=STATUS_MORE_PROCESSING_REQUIRED;
|
||
} //end if allocs all OK
|
||
|
||
} //end if not already resetting
|
||
|
||
} //end if we need to reset
|
||
|
||
IoReleaseRemoveLock( &(deviceExtension->RemoveLock), Irp );
|
||
ExFreePool(rwContext);
|
||
ExFreePool(urb);
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS DOT4USB_ResetWorkItem(IN PDEVICE_OBJECT deviceObject, IN PVOID Context)
|
||
{
|
||
|
||
PDOT4USB_WORKITEM_CONTEXT pResetWorkItemObj;
|
||
PDEVICE_EXTENSION DeviceExtension;
|
||
NTSTATUS ntStatus;
|
||
PDEVICE_OBJECT devObj;
|
||
|
||
UNREFERENCED_PARAMETER(deviceObject);
|
||
TR_VERBOSE(("USBPRINT.SYS: Entering USBPRINT_ResetWorkItem\n"));
|
||
pResetWorkItemObj=(PDOT4USB_WORKITEM_CONTEXT)Context;
|
||
DeviceExtension=pResetWorkItemObj->deviceObject->DeviceExtension;
|
||
ntStatus=UsbResetPipe(pResetWorkItemObj->deviceObject,pResetWorkItemObj->pPipeInfo,FALSE);
|
||
IoCompleteRequest(pResetWorkItemObj->irp,IO_NO_INCREMENT);
|
||
IoFreeWorkItem(pResetWorkItemObj->ioWorkItem);
|
||
|
||
// save off work item device object before freeing work item
|
||
devObj = pResetWorkItemObj->deviceObject;
|
||
ExFreePool(pResetWorkItemObj);
|
||
InterlockedExchange(&(DeviceExtension->ResetWorkItemPending),0);
|
||
return ntStatus;
|
||
}
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
UsbReadInterruptPipeLoopCompletionRoutine(
|
||
IN PDEVICE_OBJECT DevObj,
|
||
IN PIRP Irp,
|
||
IN PDEVICE_EXTENSION devExt
|
||
)
|
||
{
|
||
PURB urb;
|
||
PDEVICE_OBJECT devObj;
|
||
PUSB_RW_CONTEXT context;
|
||
PCHAR scratchBuffer;
|
||
KIRQL oldIrql;
|
||
ULONG sizeOfUrb;
|
||
PIO_STACK_LOCATION irpSp;
|
||
NTSTATUS status;
|
||
BOOLEAN queueNewRequest;
|
||
|
||
UNREFERENCED_PARAMETER( DevObj ); // we created this Irp via IoAllocateIrp() and we didn't reserve an IO_STACK_LOCATION
|
||
// for ourselves, so we can't use this
|
||
|
||
|
||
if(devExt->InterruptContext)
|
||
{
|
||
context = devExt->InterruptContext;
|
||
urb = context->Urb;
|
||
devObj = context->DevObj;
|
||
scratchBuffer = urb->UrbBulkOrInterruptTransfer.TransferBuffer;
|
||
}
|
||
else
|
||
{
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
// must have freed up the context stuff, so just return
|
||
KeAcquireSpinLock( &devExt->SpinLock, &oldIrql );
|
||
if( !Irp->Cancel && devExt->Dot4Event && NT_SUCCESS(Irp->IoStatus.Status) ) {
|
||
queueNewRequest = TRUE;
|
||
KeSetEvent( devExt->Dot4Event, 1, FALSE ); // signal dot4.sys that peripheral has data to be read
|
||
} else {
|
||
TR_TMP1(("UsbReadInterruptPipeLoopCompletionRoutine - cancel, Dot4 event gone, or bad status in irp - time to clean up"));
|
||
if( STATUS_SUCCESS != Irp->IoStatus.Status ) {
|
||
TR_TMP1(("UsbReadInterruptPipeLoopCompletionRoutine - IoStatus.Status = %x\n",Irp->IoStatus.Status));
|
||
}
|
||
queueNewRequest = FALSE;
|
||
}
|
||
KeReleaseSpinLock( &devExt->SpinLock, oldIrql );
|
||
|
||
if( queueNewRequest ) {
|
||
// queue another read request in the interrupt pipe
|
||
sizeOfUrb = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
|
||
RtlZeroMemory( urb, sizeOfUrb );
|
||
urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT)sizeOfUrb;
|
||
urb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
|
||
urb->UrbBulkOrInterruptTransfer.PipeHandle = devExt->InterruptPipe->PipeHandle;
|
||
urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK;
|
||
urb->UrbBulkOrInterruptTransfer.TransferBuffer = scratchBuffer;
|
||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = SCRATCH_BUFFER_SIZE;
|
||
urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;
|
||
urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
|
||
|
||
IoReuseIrp( Irp, STATUS_NOT_SUPPORTED );
|
||
|
||
irpSp = IoGetNextIrpStackLocation( Irp );
|
||
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
|
||
irpSp->Parameters.Others.Argument1 = urb;
|
||
|
||
IoSetCompletionRoutine( Irp, UsbReadInterruptPipeLoopCompletionRoutine, devExt, TRUE, TRUE, TRUE );
|
||
|
||
status = IoCallDriver(devExt->LowerDevObj, Irp);
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
// bummer - Irp is in limbo - stop polling and mark Irp for cleanup
|
||
D4UAssert(!"UsbReadInterruptPipeLoopCompletionRoutine - IoCallDriver failed");
|
||
|
||
if(devExt->InterruptContext)
|
||
{
|
||
InterlockedExchangePointer(&devExt->InterruptContext, NULL);
|
||
ExFreePool( urb );
|
||
ExFreePool( context );
|
||
ExFreePool( scratchBuffer );
|
||
KeSetEvent( &devExt->PollIrpEvent, 0, FALSE ); // signal dispatch routine that it is safe to touch the Irp - including IoFreeIrp()
|
||
}
|
||
}
|
||
|
||
} else {
|
||
if(devExt->InterruptContext)
|
||
{
|
||
// clean up - either Irp was cancelled or we got a datalink disconnect IOCTL from dot4
|
||
InterlockedExchangePointer(&devExt->InterruptContext, NULL);
|
||
ExFreePool( urb );
|
||
ExFreePool( context );
|
||
ExFreePool( scratchBuffer );
|
||
TR_TMP1(("UsbReadInterruptPipeLoopCompletionRoutine - signalling PollIrpEvent"));
|
||
KeSetEvent( &devExt->PollIrpEvent, 0, FALSE ); // signal dispatch routine that it is safe to touch the Irp - including IoFreeIrp()
|
||
}
|
||
}
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED; // always
|
||
}
|
||
|
||
|
||
/************************************************************************/
|
||
/* UsbStopReadInterruptPipeLoop */
|
||
/************************************************************************/
|
||
//
|
||
// Routine Description:
|
||
//
|
||
// - Stop the polling of the device interrupt pipe started by
|
||
// UsbStartReadInterruptPipeLoop and free the Irp.
|
||
//
|
||
// - It is legal for devExt->PollIrp to be NULL on entry to this function.
|
||
//
|
||
// - This function is called from the DataLink Disconnect IOCTL
|
||
// handler, from the PnP Surprise Removal handler and from the
|
||
// PnP Remove handler. It is safe to call this function multiple
|
||
// times between PollIrp creations.
|
||
//
|
||
// - This is the only function in the driver that should call
|
||
// IoFreeIrp on devExt->PollIrp and it is the only function
|
||
// that should change devExt->PollIrp from !NULL -> NULL
|
||
//
|
||
// - This function will block until the PollIrp, if any, has
|
||
// been cleaned up. The block should be for a very short
|
||
// period of time unless there is a driver bug here or in
|
||
// the USB stack below us.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// DevObj - pointer to Dot4Usb.sys driver object
|
||
//
|
||
// Return Value:
|
||
//
|
||
// NONE
|
||
//
|
||
/************************************************************************/
|
||
VOID
|
||
UsbStopReadInterruptPipeLoop(
|
||
IN PDEVICE_OBJECT DevObj
|
||
)
|
||
{
|
||
PDEVICE_EXTENSION devExt = DevObj->DeviceExtension;
|
||
KIRQL oldIrql;
|
||
|
||
TR_VERBOSE(("UsbStopReadInterruptPipeLoop - enter"));
|
||
|
||
//
|
||
// We must hold this SpinLock in order to change devExt->PollIrp
|
||
//
|
||
KeAcquireSpinLock( &devExt->PollIrpSpinLock, &oldIrql );
|
||
|
||
if( devExt->PollIrp ) {
|
||
|
||
//
|
||
// We have a PollIrp - Cancel the Irp so that the completion
|
||
// routine detects that it should take the Irp out of play and
|
||
// signal us when it is safe for us to touch the irp.
|
||
//
|
||
NTSTATUS status;
|
||
LARGE_INTEGER timeOut;
|
||
PIRP irp;
|
||
|
||
irp = devExt->PollIrp;
|
||
devExt->PollIrp = NULL;
|
||
|
||
//
|
||
// Safe to let go of the SpinLock - everything from here on is local to this function
|
||
//
|
||
KeReleaseSpinLock( &devExt->PollIrpSpinLock, oldIrql );
|
||
|
||
//
|
||
// Completion routine will detect that the Irp has been cancelled
|
||
//
|
||
retryCancel:
|
||
IoCancelIrp( irp );
|
||
|
||
//
|
||
// Completion routine will set PollIrpEvent when it has taken
|
||
// the Irp out of play and it is safe for us to touch the Irp
|
||
//
|
||
// 500ms (in 100ns units) - magic number chosen as "reasonable" timeout
|
||
//
|
||
timeOut.QuadPart = - 500 * 10 * 1000;
|
||
status = KeWaitForSingleObject( &devExt->PollIrpEvent, Executive, KernelMode, FALSE, &timeOut );
|
||
|
||
if( STATUS_SUCCESS == status ) {
|
||
//
|
||
// Completion routine has signalled that we now own the irp - clean it up
|
||
//
|
||
IoFreeIrp( irp );
|
||
|
||
//
|
||
// This irp will no longer block a Remove
|
||
//
|
||
IoReleaseRemoveLock( &devExt->RemoveLock, irp );
|
||
|
||
} else if( STATUS_TIMEOUT == status ) {
|
||
//
|
||
// Cancel and wait again - either we hit a timing window where our completion
|
||
// routine lost our cancel request, or the Irp is wedged in a driver somewhere
|
||
// below us.
|
||
//
|
||
goto retryCancel;
|
||
|
||
} else {
|
||
//
|
||
// We specified that we were NOT alertable - but check for this condition anyway
|
||
//
|
||
D4UAssert(!"UsbStopReadInterruptPipeLoop - unexpected status from KeWaitForSingleObject?!?");
|
||
goto retryCancel;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We don't have a PollIrp - nothing for us to clean up.
|
||
//
|
||
TR_VERBOSE(("UsbStopReadInterruptPipeLoop - NULL PollIrp"));
|
||
KeReleaseSpinLock( &devExt->PollIrpSpinLock, oldIrql );
|
||
|
||
}
|
||
}
|
||
|
||
|
||
/************************************************************************/
|
||
/* UsbStartReadInterruptPipeLoop */
|
||
/************************************************************************/
|
||
//
|
||
// Routine Description:
|
||
//
|
||
// - Create a read request (Irp) for the device's interrupt pipe. Save a
|
||
// pointer to the Irp in our device extension for cleanup later by
|
||
// UsbStopReadInterruptPipeLoop().
|
||
//
|
||
// - This is the only function in the driver that should change
|
||
// devExt->PollIrp from NULL -> !NULL
|
||
//
|
||
// Arguments:
|
||
//
|
||
// DevObj - pointer to Dot4Usb.sys driver object
|
||
//
|
||
// Return Value:
|
||
//
|
||
// NTSTATUS
|
||
//
|
||
/************************************************************************/
|
||
NTSTATUS
|
||
UsbStartReadInterruptPipeLoop(
|
||
IN PDEVICE_OBJECT DevObj
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
PDEVICE_EXTENSION devExt = DevObj->DeviceExtension;
|
||
PUSBD_PIPE_INFORMATION pipe;
|
||
ULONG sizeOfUrb;
|
||
PIRP irp;
|
||
PIO_STACK_LOCATION irpSp;
|
||
PURB urb;
|
||
PUSB_RW_CONTEXT context;
|
||
PCHAR scratchBuffer;
|
||
KIRQL oldIrql;
|
||
|
||
TR_VERBOSE(("UsbStartReadInterruptPipeLoop - enter"));
|
||
|
||
|
||
//
|
||
// We must hold this SpinLock in order to change devExt->PollIrp
|
||
//
|
||
// BUGBUG - This SpinLock is protecting some code that doesn't need protection,
|
||
// which means that we are at Raised Irql when we don't need to be.
|
||
// Revisit this later to move the Acquire and Release of this SpinLock
|
||
// so that it only protects code that needs protection.
|
||
//
|
||
KeAcquireSpinLock( &devExt->PollIrpSpinLock, &oldIrql );
|
||
|
||
|
||
//
|
||
// Driver state machine check - we should never get two calls to this
|
||
// function without a cleanup (UsbStopReadInterruptPipeLoop) call in between.
|
||
//
|
||
D4UAssert( !devExt->PollIrp );
|
||
|
||
|
||
//
|
||
// Verify that we have an interrupt pipe
|
||
//
|
||
pipe = devExt->InterruptPipe;
|
||
if( !pipe ) {
|
||
TR_FAIL(("UsbStartReadInterruptPipeLoop - no interrupt pipe"));
|
||
status = STATUS_INVALID_HANDLE;
|
||
goto targetError;
|
||
}
|
||
|
||
|
||
//
|
||
// Pipe type/look ok?
|
||
//
|
||
D4UAssert( UsbdPipeTypeInterrupt == pipe->PipeType && USBD_PIPE_DIRECTION_IN(pipe) );
|
||
|
||
|
||
//
|
||
// Allocate pool that we need for this request
|
||
//
|
||
sizeOfUrb = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
|
||
urb = ExAllocatePool( NonPagedPool, sizeOfUrb );
|
||
if( !urb ) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto targetError;
|
||
}
|
||
|
||
context = ExAllocatePool( NonPagedPool, sizeof(USB_RW_CONTEXT) );
|
||
if( !context ) {
|
||
ExFreePool( urb );
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto targetError;
|
||
}
|
||
|
||
scratchBuffer = ExAllocatePool( NonPagedPool, SCRATCH_BUFFER_SIZE );
|
||
if( !scratchBuffer ) {
|
||
ExFreePool( urb );
|
||
ExFreePool( context );
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto targetError;
|
||
}
|
||
|
||
|
||
//
|
||
// Set up Context for completion routine
|
||
//
|
||
// - We send down a pointer to our Device Object in context
|
||
// because we create this IRP via IoAllocateIrp and we don't
|
||
// reserve a stack location for ourselves, so the PDEVICE_OBJECT
|
||
// parameter that our completion routine receives is bogus
|
||
// (probably NULL)
|
||
//
|
||
context->Urb = urb;
|
||
context->DevObj = DevObj;
|
||
|
||
|
||
//
|
||
// Initialize URB for read on interrupt pipe
|
||
//
|
||
RtlZeroMemory( urb, sizeOfUrb );
|
||
|
||
urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT)sizeOfUrb;
|
||
urb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
|
||
urb->UrbBulkOrInterruptTransfer.PipeHandle = pipe->PipeHandle;
|
||
urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK;
|
||
urb->UrbBulkOrInterruptTransfer.TransferBuffer = scratchBuffer;
|
||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = SCRATCH_BUFFER_SIZE; // note - likely only one byte to read
|
||
urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;
|
||
urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
|
||
|
||
|
||
//
|
||
// Allocate and set up the IRP, stack location, and completion routine
|
||
//
|
||
irp = IoAllocateIrp( devExt->LowerDevObj->StackSize, FALSE );
|
||
if( !irp ) {
|
||
ExFreePool( urb );
|
||
ExFreePool( context );
|
||
ExFreePool( scratchBuffer );
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto targetError;
|
||
}
|
||
|
||
irpSp = IoGetNextIrpStackLocation( irp );
|
||
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
|
||
irpSp->Parameters.Others.Argument1 = urb;
|
||
|
||
IoSetCompletionRoutine( irp, UsbReadInterruptPipeLoopCompletionRoutine, devExt, TRUE, TRUE, TRUE );
|
||
|
||
|
||
//
|
||
// This event will be SET by the completion routine when it is
|
||
// safe for a dispatch routine to touch this Irp
|
||
//
|
||
KeClearEvent( &devExt->PollIrpEvent );
|
||
|
||
|
||
//
|
||
// We're about to put the Irp in play - make sure that our device
|
||
// doesn't get removed while this Irp is in use
|
||
//
|
||
status = IoAcquireRemoveLock( &devExt->RemoveLock, irp );
|
||
if( STATUS_SUCCESS != status ) {
|
||
//
|
||
// We're being removed - clean up and bail out
|
||
//
|
||
IoFreeIrp( irp );
|
||
ExFreePool( urb );
|
||
ExFreePool( context );
|
||
ExFreePool( scratchBuffer );
|
||
status = STATUS_DELETE_PENDING;
|
||
goto targetError;
|
||
}
|
||
|
||
//
|
||
// Save a pointer to this Irp in our extension so that UsbStopReadInterruptPipeLoop()
|
||
// can find it to IoFreeIrp() it later.
|
||
//
|
||
D4UAssert( !devExt->PollIrp );
|
||
devExt->PollIrp = irp;
|
||
|
||
// save interrupt context in device extension
|
||
InterlockedExchangePointer(&devExt->InterruptContext, context);
|
||
|
||
|
||
//
|
||
// Kick off the first read. Subsequent reads will come from the
|
||
// completion routine as it reuses/bounces the IRP. The completion routine
|
||
// is responsible taking the Irp out of play when it detects either a termination
|
||
// condition or request error. UsbStopReadInterruptPipeLoop() will clean up the
|
||
// Irp after the completion routine has taken the Irp out of play and signaled
|
||
// PollIrpEvent that it is safe to touch the Irp.
|
||
//
|
||
status = IoCallDriver( devExt->LowerDevObj, irp );
|
||
|
||
targetError:
|
||
|
||
//
|
||
// CURRENTLY... all paths to here hold the SpinLock - this should change after cleanup
|
||
//
|
||
KeReleaseSpinLock( &devExt->PollIrpSpinLock, oldIrql );
|
||
|
||
|
||
//
|
||
// If the Irp is Pending then we have been successful
|
||
//
|
||
if( STATUS_PENDING == status ) {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
UsbDeferIrpCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Event
|
||
)
|
||
{
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
UNREFERENCED_PARAMETER( Irp );
|
||
KeSetEvent( (PKEVENT)Event, 1, FALSE );
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
UsbCallUsbd(
|
||
IN PDEVICE_OBJECT DevObj,
|
||
IN PURB Urb,
|
||
IN PLARGE_INTEGER pTimeout
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Passes a URB to the USBD class driver
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to the device object for this printer
|
||
|
||
Urb - pointer to Urb request block
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if successful,
|
||
STATUS_UNSUCCESSFUL otherwise
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS ntStatus, status = STATUS_SUCCESS;
|
||
PDEVICE_EXTENSION devExt = DevObj->DeviceExtension;
|
||
PIRP irp;
|
||
KEVENT event;
|
||
PIO_STACK_LOCATION nextStack;
|
||
|
||
TR_VERBOSE(("UsbCallUsbd - enter"));
|
||
|
||
//
|
||
// issue a synchronous request
|
||
//
|
||
|
||
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
|
||
if ( (irp = IoAllocateIrp(devExt->LowerDevObj->StackSize,
|
||
FALSE)) == NULL )
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
//
|
||
// Call the class driver to perform the operation. If the returned status
|
||
// is PENDING, wait for the request to complete.
|
||
//
|
||
|
||
nextStack = IoGetNextIrpStackLocation(irp);
|
||
D4UAssert(nextStack != NULL);
|
||
|
||
//
|
||
// pass the URB to the USB driver stack
|
||
//
|
||
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
|
||
nextStack->Parameters.Others.Argument1 = Urb;
|
||
|
||
IoSetCompletionRoutine(irp,
|
||
UsbDeferIrpCompletion,
|
||
&event,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
ntStatus = IoCallDriver(devExt->LowerDevObj, irp);
|
||
|
||
if ( ntStatus == STATUS_PENDING ) {
|
||
status = KeWaitForSingleObject(&event,Suspended,KernelMode,FALSE,pTimeout);
|
||
//
|
||
// If the request timed out cancel the request
|
||
// and wait for it to complete
|
||
//
|
||
if ( status == STATUS_TIMEOUT ) {
|
||
TR_VERBOSE(("UsbCallUsbd: Cancelling IRP %x because of timeout", irp));
|
||
IoCancelIrp(irp);
|
||
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
|
||
}
|
||
|
||
ntStatus = irp->IoStatus.Status;
|
||
}
|
||
|
||
IoFreeIrp(irp);
|
||
|
||
TR_VERBOSE(("UsbCallUsbd - exit w/status=%x", ntStatus));
|
||
|
||
return ntStatus;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
UsbResetPipe(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PUSBD_PIPE_INFORMATION Pipe,
|
||
IN BOOLEAN IsoClearStall
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reset a given USB pipe.
|
||
|
||
NOTES:
|
||
|
||
This will reset the host to Data0 and should also reset the device
|
||
to Data0 for Bulk and Interrupt pipes.
|
||
|
||
For Iso pipes this will set the virgin state of pipe so that ASAP
|
||
transfers begin with the current bus frame instead of the next frame
|
||
after the last transfer occurred.
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS ntStatus;
|
||
PURB urb;
|
||
LARGE_INTEGER timeOut;
|
||
|
||
|
||
timeOut.QuadPart = FAILURE_TIMEOUT;
|
||
|
||
|
||
TR_VERBOSE(("Entering UsbResetPipe; pipe # %x\n", Pipe));
|
||
|
||
urb = ExAllocatePool(NonPagedPool,sizeof(struct _URB_PIPE_REQUEST));
|
||
|
||
if (urb) {
|
||
|
||
urb->UrbHeader.Length = (USHORT) sizeof (struct _URB_PIPE_REQUEST);
|
||
urb->UrbHeader.Function = URB_FUNCTION_RESET_PIPE;
|
||
urb->UrbPipeRequest.PipeHandle =
|
||
Pipe->PipeHandle;
|
||
|
||
ntStatus = UsbCallUsbd(DeviceObject, urb, &timeOut);
|
||
|
||
ExFreePool(urb);
|
||
|
||
} else {
|
||
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Memphis RESET_PIPE will send a Clear-Feature Endpoint Stall to
|
||
// reset the data toggle of non-Iso pipes as part of a RESET_PIPE
|
||
// request. It does not do this for Iso pipes as Iso pipes do not use
|
||
// the data toggle (all Iso packets are Data0). However, we also use
|
||
// the Clear-Feature Endpoint Stall request in our device firmware to
|
||
// reset data buffer points inside the device so we explicitly send
|
||
// this request to the device for Iso pipes if desired.
|
||
//
|
||
if (NT_SUCCESS(ntStatus) && IsoClearStall &&
|
||
(Pipe->PipeType == UsbdPipeTypeIsochronous)) {
|
||
|
||
urb = ExAllocatePool(NonPagedPool,sizeof(struct _URB_CONTROL_FEATURE_REQUEST));
|
||
|
||
if (urb) {
|
||
|
||
UsbBuildFeatureRequest(urb,
|
||
URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT,
|
||
USB_FEATURE_ENDPOINT_STALL,
|
||
Pipe->EndpointAddress,
|
||
NULL);
|
||
|
||
ntStatus = UsbCallUsbd(DeviceObject, urb, &timeOut);
|
||
|
||
ExFreePool(urb);
|
||
} else {
|
||
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
return ntStatus;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
UsbReadWrite(
|
||
IN PDEVICE_OBJECT DevObj,
|
||
IN PIRP Irp,
|
||
PUSBD_PIPE_INFORMATION Pipe,
|
||
USB_REQUEST_TYPE RequestType
|
||
)
|
||
/*
|
||
- Caller must verify that:
|
||
- Irp->MdlAddress != NULL
|
||
- Pipe != NULL
|
||
- RequestType matches Pipe->PipeType
|
||
|
||
*/
|
||
{
|
||
PDEVICE_EXTENSION devExt;
|
||
PIO_STACK_LOCATION nextIrpSp;
|
||
PURB urb;
|
||
PUSB_RW_CONTEXT context;
|
||
ULONG sizeOfUrb = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
TR_VERBOSE(("UsbReadWrite - enter"));
|
||
|
||
D4UAssert( Irp->MdlAddress ); // calling routine should catch and fail this case
|
||
D4UAssert( Pipe ); // calling routine should catch and fail this case
|
||
|
||
urb = ExAllocatePool( NonPagedPool, sizeOfUrb );
|
||
if( !urb ) {
|
||
TR_FAIL(("UsbReadWrite - no pool for URB"));
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto targetError;
|
||
}
|
||
|
||
context = ExAllocatePool( NonPagedPool, sizeof(USB_RW_CONTEXT) );
|
||
if( !context ) {
|
||
TR_FAIL(("UsbReadWrite - no pool for context"));
|
||
ExFreePool( urb );
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto targetError;
|
||
}
|
||
|
||
context->Urb = (PURB)urb;
|
||
context->DevObj = DevObj;
|
||
|
||
RtlZeroMemory(urb, sizeOfUrb);
|
||
|
||
UsbBuildInterruptOrBulkTransferRequest( urb,
|
||
(USHORT)sizeOfUrb,
|
||
Pipe->PipeHandle,
|
||
NULL, // transferBuffer
|
||
Irp->MdlAddress,
|
||
MmGetMdlByteCount(Irp->MdlAddress),
|
||
0, // transfer Flags
|
||
NULL );
|
||
|
||
if( UsbReadRequest == RequestType ) {
|
||
context->IsWrite=FALSE;
|
||
TR_VERBOSE(("UsbReadWrite - requesttype is READ"))
|
||
urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN;
|
||
urb->UrbBulkOrInterruptTransfer.TransferFlags |= USBD_SHORT_TRANSFER_OK;
|
||
} else {
|
||
context->IsWrite=TRUE;
|
||
TR_VERBOSE(("UsbReadWrite - requesttype is WRITE"))
|
||
}
|
||
|
||
nextIrpSp = IoGetNextIrpStackLocation( Irp );
|
||
nextIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
nextIrpSp->Parameters.Others.Argument1 = urb;
|
||
nextIrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
|
||
|
||
IoSetCompletionRoutine( Irp, UsbAsyncReadWriteComplete, context, TRUE, TRUE, TRUE );
|
||
|
||
devExt = DevObj->DeviceExtension;
|
||
status = IoCallDriver( devExt->LowerDevObj, Irp );
|
||
|
||
goto targetDone;
|
||
|
||
targetError:
|
||
|
||
Irp->IoStatus.Status = status;
|
||
Irp->IoStatus.Information = 0;
|
||
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
||
|
||
targetDone:
|
||
|
||
TR_VERBOSE(("UsbReadWrite - exit - status= %x",status));
|
||
return status;
|
||
|
||
}
|
||
|
||
|