mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1537 lines
52 KiB
1537 lines
52 KiB
/*++
|
|
|
|
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
|
|
|
|
|