reactos/drivers/bluetooth/fbtusb/fbtpnp.c

1911 lines
56 KiB
C
Raw Normal View History

// Copyright (c) 2004, Antony C. Roberts
// Use of this file is subject to the terms
// described in the LICENSE.TXT file that
// accompanies this file.
//
// Your use of this file indicates your
// acceptance of the terms described in
// LICENSE.TXT.
//
// http://www.freebt.net
#include "stdio.h"
#include "fbtusb.h"
#include "fbtpnp.h"
#include "fbtpwr.h"
#include "fbtdev.h"
#include "fbtrwr.h"
#include "fbtwmi.h"
#include "fbtusr.h"
// Handle PNP events
NTSTATUS FreeBT_DispatchPnP(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PIO_STACK_LOCATION irpStack;
PDEVICE_EXTENSION deviceExtension;
KEVENT startDeviceEvent;
NTSTATUS ntStatus;
irpStack = IoGetCurrentIrpStackLocation(Irp);
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
// since the device is removed, fail the Irp.
if (Removed == deviceExtension->DeviceState)
{
ntStatus = STATUS_DELETE_PENDING;
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
}
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchPnP::"));
FreeBT_IoIncrement(deviceExtension);
if (irpStack->MinorFunction == IRP_MN_START_DEVICE)
{
ASSERT(deviceExtension->IdleReqPend == 0);
}
else
{
if (deviceExtension->SSEnable)
{
CancelSelectSuspend(deviceExtension);
}
}
FreeBT_DbgPrint(3, ("FBTUSB: ///////////////////////////////////////////\n"));
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchPnP::"));
FreeBT_DbgPrint(2, (PnPMinorFunctionString(irpStack->MinorFunction)));
switch (irpStack->MinorFunction)
{
case IRP_MN_START_DEVICE:
ntStatus = HandleStartDevice(DeviceObject, Irp);
break;
case IRP_MN_QUERY_STOP_DEVICE:
// if we cannot stop the device, we fail the query stop irp
ntStatus = CanStopDevice(DeviceObject, Irp);
if(NT_SUCCESS(ntStatus))
{
ntStatus = HandleQueryStopDevice(DeviceObject, Irp);
return ntStatus;
}
break;
case IRP_MN_CANCEL_STOP_DEVICE:
ntStatus = HandleCancelStopDevice(DeviceObject, Irp);
break;
case IRP_MN_STOP_DEVICE:
ntStatus = HandleStopDevice(DeviceObject, Irp);
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchPnP::IRP_MN_STOP_DEVICE::"));
FreeBT_IoDecrement(deviceExtension);
return ntStatus;
case IRP_MN_QUERY_REMOVE_DEVICE:
// if we cannot remove the device, we fail the query remove irp
ntStatus = HandleQueryRemoveDevice(DeviceObject, Irp);
return ntStatus;
case IRP_MN_CANCEL_REMOVE_DEVICE:
ntStatus = HandleCancelRemoveDevice(DeviceObject, Irp);
break;
case IRP_MN_SURPRISE_REMOVAL:
ntStatus = HandleSurpriseRemoval(DeviceObject, Irp);
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchPnP::IRP_MN_SURPRISE_REMOVAL::"));
FreeBT_IoDecrement(deviceExtension);
return ntStatus;
case IRP_MN_REMOVE_DEVICE:
ntStatus = HandleRemoveDevice(DeviceObject, Irp);
return ntStatus;
case IRP_MN_QUERY_CAPABILITIES:
ntStatus = HandleQueryCapabilities(DeviceObject, Irp);
break;
default:
IoSkipCurrentIrpStackLocation(Irp);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchPnP::default::"));
FreeBT_IoDecrement(deviceExtension);
return ntStatus;
}
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchPnP::"));
FreeBT_IoDecrement(deviceExtension);
return ntStatus;
}
NTSTATUS HandleStartDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
KIRQL oldIrql;
KEVENT startDeviceEvent;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
LARGE_INTEGER dueTime;
FreeBT_DbgPrint(3, ("FBTUSB: HandleStartDevice: Entered\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
deviceExtension->UsbConfigurationDescriptor = NULL;
deviceExtension->UsbInterface = NULL;
deviceExtension->PipeContext = NULL;
// We cannot touch the device (send it any non pnp irps) until a
// start device has been passed down to the lower drivers.
// first pass the Irp down
KeInitializeEvent(&startDeviceEvent, NotificationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE)IrpCompletionRoutine,
(PVOID)&startDeviceEvent,
TRUE,
TRUE,
TRUE);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
if (ntStatus == STATUS_PENDING)
{
KeWaitForSingleObject(&startDeviceEvent, Executive, KernelMode, FALSE, NULL);
ntStatus = Irp->IoStatus.Status;
}
if (!NT_SUCCESS(ntStatus))
{
FreeBT_DbgPrint(1, ("FBTUSB: HandleStartDevice: Lower drivers failed this Irp (0x%08x)\n", ntStatus));
return ntStatus;
}
// Read the device descriptor, configuration descriptor
// and select the interface descriptors
ntStatus = ReadandSelectDescriptors(DeviceObject);
if (!NT_SUCCESS(ntStatus))
{
FreeBT_DbgPrint(1, ("FBTUSB: HandleStartDevice: ReadandSelectDescriptors failed (0x%08x)\n", ntStatus));
return ntStatus;
}
// enable the symbolic links for system components to open
// handles to the device
ntStatus = IoSetDeviceInterfaceState(&deviceExtension->InterfaceName, TRUE);
if (!NT_SUCCESS(ntStatus))
{
FreeBT_DbgPrint(1, ("FBTUSB: HandleStartDevice: IoSetDeviceInterfaceState failed (0x%08x)\n", ntStatus));
return ntStatus;
}
KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);
SET_NEW_PNP_STATE(deviceExtension, Working);
deviceExtension->QueueState = AllowRequests;
KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);
deviceExtension->FlagWWOutstanding = 0;
deviceExtension->FlagWWCancel = 0;
deviceExtension->WaitWakeIrp = NULL;
if (deviceExtension->WaitWakeEnable)
{
IssueWaitWake(deviceExtension);
}
ProcessQueuedRequests(deviceExtension);
if (WinXpOrBetter == deviceExtension->WdmVersion)
{
deviceExtension->SSEnable = deviceExtension->SSRegistryEnable;
// set timer.for selective suspend requests
if (deviceExtension->SSEnable)
{
dueTime.QuadPart = -10000 * IDLE_INTERVAL; // 5000 ms
KeSetTimerEx(&deviceExtension->Timer, dueTime, IDLE_INTERVAL, &deviceExtension->DeferredProcCall);
deviceExtension->FreeIdleIrpCount = 0;
}
}
FreeBT_DbgPrint(3, ("FBTUSB: HandleStartDevice: Leaving\n"));
return ntStatus;
}
NTSTATUS ReadandSelectDescriptors(IN PDEVICE_OBJECT DeviceObject)
{
PURB urb;
ULONG siz;
NTSTATUS ntStatus;
PUSB_DEVICE_DESCRIPTOR deviceDescriptor;
urb = NULL;
deviceDescriptor = NULL;
// 1. Read the device descriptor
urb = (PURB) ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
if(urb)
{
siz = sizeof(USB_DEVICE_DESCRIPTOR);
deviceDescriptor = (PUSB_DEVICE_DESCRIPTOR) ExAllocatePool(NonPagedPool, siz);
if (deviceDescriptor)
{
UsbBuildGetDescriptorRequest(
urb,
(USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
USB_DEVICE_DESCRIPTOR_TYPE,
0,
0,
deviceDescriptor,
NULL,
siz,
NULL);
ntStatus = CallUSBD(DeviceObject, urb);
if (NT_SUCCESS(ntStatus))
{
ASSERT(deviceDescriptor->bNumConfigurations);
ntStatus = ConfigureDevice(DeviceObject);
}
ExFreePool(urb);
ExFreePool(deviceDescriptor);
}
else
{
FreeBT_DbgPrint(1, ("FBTUSB: ReadandSelectDescriptors: Failed to allocate memory for deviceDescriptor"));
ExFreePool(urb);
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
FreeBT_DbgPrint(1, ("FBTUSB: ReadandSelectDescriptors: Failed to allocate memory for urb"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
return ntStatus;
}
NTSTATUS ConfigureDevice(IN PDEVICE_OBJECT DeviceObject)
{
PURB urb;
ULONG siz;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor;
urb = NULL;
configurationDescriptor = NULL;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
// Read the first configuration descriptor
// This requires two steps:
// 1. Read the fixed sized configuration desciptor (CD)
// 2. Read the CD with all embedded interface and endpoint descriptors
urb = (PURB) ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
if (urb)
{
siz = sizeof(USB_CONFIGURATION_DESCRIPTOR);
configurationDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR) ExAllocatePool(NonPagedPool, siz);
if(configurationDescriptor)
{
UsbBuildGetDescriptorRequest(
urb,
(USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
USB_CONFIGURATION_DESCRIPTOR_TYPE,
0,
0,
configurationDescriptor,
NULL,
sizeof(USB_CONFIGURATION_DESCRIPTOR),
NULL);
ntStatus = CallUSBD(DeviceObject, urb);
if(!NT_SUCCESS(ntStatus))
{
FreeBT_DbgPrint(1, ("FBTUSB: ConfigureDevice: UsbBuildGetDescriptorRequest failed\n"));
goto ConfigureDevice_Exit;
}
}
else
{
FreeBT_DbgPrint(1, ("FBTUSB: ConfigureDevice: Failed to allocate mem for config Descriptor\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto ConfigureDevice_Exit;
}
siz = configurationDescriptor->wTotalLength;
ExFreePool(configurationDescriptor);
configurationDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR) ExAllocatePool(NonPagedPool, siz);
if (configurationDescriptor)
{
UsbBuildGetDescriptorRequest(
urb,
(USHORT)sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
USB_CONFIGURATION_DESCRIPTOR_TYPE,
0,
0,
configurationDescriptor,
NULL,
siz,
NULL);
ntStatus = CallUSBD(DeviceObject, urb);
if (!NT_SUCCESS(ntStatus))
{
FreeBT_DbgPrint(1,("FBTUSB: ConfigureDevice: Failed to read configuration descriptor"));
goto ConfigureDevice_Exit;
}
}
else
{
FreeBT_DbgPrint(1, ("FBTUSB: ConfigureDevice: Failed to alloc mem for config Descriptor\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto ConfigureDevice_Exit;
}
}
else
{
FreeBT_DbgPrint(1, ("FBTUSB: ConfigureDevice: Failed to allocate memory for urb\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto ConfigureDevice_Exit;
}
if (configurationDescriptor)
{
// save a copy of configurationDescriptor in deviceExtension
// remember to free it later.
deviceExtension->UsbConfigurationDescriptor = configurationDescriptor;
if (configurationDescriptor->bmAttributes & REMOTE_WAKEUP_MASK)
{
// this configuration supports remote wakeup
deviceExtension->WaitWakeEnable = 1;
}
else
{
deviceExtension->WaitWakeEnable = 0;
}
ntStatus = SelectInterfaces(DeviceObject, configurationDescriptor);
}
else
{
deviceExtension->UsbConfigurationDescriptor = NULL;
}
ConfigureDevice_Exit:
if (urb)
{
ExFreePool(urb);
}
return ntStatus;
}
NTSTATUS SelectInterfaces(IN PDEVICE_OBJECT DeviceObject, IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
{
LONG numberOfInterfaces, interfaceNumber, interfaceindex;
ULONG i;
PURB urb;
PUCHAR pInf;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor;
PUSBD_INTERFACE_LIST_ENTRY interfaceList,
tmp;
PUSBD_INTERFACE_INFORMATION Interface;
urb = NULL;
Interface = NULL;
interfaceDescriptor = NULL;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
numberOfInterfaces = ConfigurationDescriptor->bNumInterfaces;
interfaceindex = interfaceNumber = 0;
// Parse the configuration descriptor for the interface;
tmp = interfaceList = (PUSBD_INTERFACE_LIST_ENTRY)
ExAllocatePool(NonPagedPool, sizeof(USBD_INTERFACE_LIST_ENTRY) * (numberOfInterfaces + 1));
if (!tmp)
{
FreeBT_DbgPrint(1, ("FBTUSB: SelectInterfaces: Failed to allocate mem for interfaceList\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
FreeBT_DbgPrint(3, ("FBTUSB: -------------\n"));
FreeBT_DbgPrint(3, ("FBTUSB: Number of interfaces %d\n", numberOfInterfaces));
while (interfaceNumber < numberOfInterfaces)
{
interfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
ConfigurationDescriptor,
ConfigurationDescriptor,
interfaceindex,
0, -1, -1, -1);
if (interfaceDescriptor)
{
interfaceList->InterfaceDescriptor = interfaceDescriptor;
interfaceList->Interface = NULL;
interfaceList++;
interfaceNumber++;
}
interfaceindex++;
}
interfaceList->InterfaceDescriptor = NULL;
interfaceList->Interface = NULL;
urb = USBD_CreateConfigurationRequestEx(ConfigurationDescriptor, tmp);
if (urb)
{
Interface = &urb->UrbSelectConfiguration.Interface;
for (i=0; i<Interface->NumberOfPipes; i++)
{
// perform pipe initialization here
// set the transfer size and any pipe flags we use
// USBD sets the rest of the Interface struct members
Interface->Pipes[i].MaximumTransferSize = USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE;
}
ntStatus = CallUSBD(DeviceObject, urb);
if (NT_SUCCESS(ntStatus))
{
// save a copy of interface information in the device extension.
deviceExtension->UsbInterface = (PUSBD_INTERFACE_INFORMATION) ExAllocatePool(NonPagedPool, Interface->Length);
if (deviceExtension->UsbInterface)
{
RtlCopyMemory(deviceExtension->UsbInterface, Interface, Interface->Length);
}
else
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
FreeBT_DbgPrint(1, ("FBTUSB: SelectInterfaces: Memory alloc for UsbInterface failed\n"));
}
// Dump the interface to the debugger
Interface = &urb->UrbSelectConfiguration.Interface;
FreeBT_DbgPrint(3, ("FBTUSB: ---------\n"));
FreeBT_DbgPrint(3, ("FBTUSB: NumberOfPipes 0x%x\n", Interface->NumberOfPipes));
FreeBT_DbgPrint(3, ("FBTUSB: Length 0x%x\n", Interface->Length));
FreeBT_DbgPrint(3, ("FBTUSB: Alt Setting 0x%x\n", Interface->AlternateSetting));
FreeBT_DbgPrint(3, ("FBTUSB: Interface Number 0x%x\n", Interface->InterfaceNumber));
FreeBT_DbgPrint(3, ("FBTUSB: Class, subclass, protocol 0x%x 0x%x 0x%x\n",
Interface->Class,
Interface->SubClass,
Interface->Protocol));
if (Interface->Class==FREEBT_USB_STDCLASS && Interface->SubClass==FREEBT_USB_STDSUBCLASS &&
Interface->Protocol==FREEBT_USB_STDPROTOCOL)
{
FreeBT_DbgPrint(3, ("FBTUSB: This is a standard USB Bluetooth device\n"));
}
else
{
FreeBT_DbgPrint(3, ("FBTUSB: WARNING: This device does not report itself as a standard USB Bluetooth device\n"));
}
// Initialize the PipeContext
// Dump the pipe info
deviceExtension->PipeContext = (PFREEBT_PIPE_CONTEXT) ExAllocatePool(
NonPagedPool,
Interface->NumberOfPipes *
sizeof(FREEBT_PIPE_CONTEXT));
if (!deviceExtension->PipeContext)
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
FreeBT_DbgPrint(1, ("FBTUSB: Memory alloc for UsbInterface failed\n"));
}
else
{
FreeBT_DbgPrint(3, ("FBTUSB: SelectInterfaces: Allocated PipeContext %p\n", deviceExtension->PipeContext));
for (i=0; i<Interface->NumberOfPipes; i++)
{
deviceExtension->PipeContext[i].PipeOpen = FALSE;
FreeBT_DbgPrint(3, ("FBTUSB: ---------\n"));
FreeBT_DbgPrint(3, ("FBTUSB: PipeType 0x%x\n", Interface->Pipes[i].PipeType));
FreeBT_DbgPrint(3, ("FBTUSB: EndpointAddress 0x%x\n", Interface->Pipes[i].EndpointAddress));
FreeBT_DbgPrint(3, ("FBTUSB: MaxPacketSize 0x%x\n", Interface->Pipes[i].MaximumPacketSize));
FreeBT_DbgPrint(3, ("FBTUSB: Interval 0x%x\n", Interface->Pipes[i].Interval));
FreeBT_DbgPrint(3, ("FBTUSB: Handle 0x%x\n", Interface->Pipes[i].PipeHandle));
FreeBT_DbgPrint(3, ("FBTUSB: MaximumTransferSize 0x%x\n", Interface->Pipes[i].MaximumTransferSize));
// Log the pipes
// Note the HCI Command endpoint won't appear here, because the Default Control Pipe
// is used for this. The Default Control Pipe is always present at EndPointAddress 0x0
switch (Interface->Pipes[i].EndpointAddress)
{
case FREEBT_STDENDPOINT_HCIEVENT:
deviceExtension->PipeContext[i].PipeType=HciEventPipe;
deviceExtension->EventPipe=Interface->Pipes[i];
FreeBT_DbgPrint(3, ("FBTUSB: HCI Event Endpoint\n"));
break;
case FREEBT_STDENDPOINT_ACLIN:
deviceExtension->PipeContext[i].PipeType=AclDataIn;
deviceExtension->DataInPipe=Interface->Pipes[i];
FreeBT_DbgPrint(3, ("FBTUSB: ACL Data In Endpoint\n"));
break;
case FREEBT_STDENDPOINT_ACLOUT:
deviceExtension->PipeContext[i].PipeType=AclDataOut;
deviceExtension->DataOutPipe=Interface->Pipes[i];
FreeBT_DbgPrint(3, ("FBTUSB: ACL Data Out Endpoint\n"));
break;
case FREEBT_STDENDPOINT_AUDIOIN:
deviceExtension->PipeContext[i].PipeType=SCODataIn;
deviceExtension->AudioInPipe=Interface->Pipes[i];
FreeBT_DbgPrint(3, ("FBTUSB: ACL Data Out Endpoint\n"));
break;
case FREEBT_STDENDPOINT_AUDIOOUT:
deviceExtension->PipeContext[i].PipeType=SCODataOut;
deviceExtension->AudioOutPipe=Interface->Pipes[i];
FreeBT_DbgPrint(3, ("FBTUSB: ACL Data Out Endpoint\n"));
break;
}
}
}
FreeBT_DbgPrint(3, ("FBTUSB: ---------\n"));
}
else
{
FreeBT_DbgPrint(1, ("FBTUSB: SelectInterfaces: Failed to select an interface\n"));
}
}
else
{
FreeBT_DbgPrint(1, ("FBTUSB: SelectInterfaces: USBD_CreateConfigurationRequestEx failed\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
if (tmp)
{
ExFreePool(tmp);
}
if (urb)
{
ExFreePool(urb);
}
return ntStatus;
}
NTSTATUS DeconfigureDevice(IN PDEVICE_OBJECT DeviceObject)
{
PURB urb;
ULONG siz;
NTSTATUS ntStatus;
siz = sizeof(struct _URB_SELECT_CONFIGURATION);
urb = (PURB) ExAllocatePool(NonPagedPool, siz);
if (urb)
{
UsbBuildSelectConfigurationRequest(urb, (USHORT)siz, NULL);
ntStatus = CallUSBD(DeviceObject, urb);
if(!NT_SUCCESS(ntStatus))
{
FreeBT_DbgPrint(3, ("FBTUSB: DeconfigureDevice: Failed to deconfigure device\n"));
}
ExFreePool(urb);
}
else
{
FreeBT_DbgPrint(1, ("FBTUSB: DeconfigureDevice: Failed to allocate urb\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
return ntStatus;
}
NTSTATUS CallUSBD(IN PDEVICE_OBJECT DeviceObject, IN PURB Urb)
{
PIRP irp;
KEVENT event;
NTSTATUS ntStatus;
IO_STATUS_BLOCK ioStatus;
PIO_STACK_LOCATION nextStack;
PDEVICE_EXTENSION deviceExtension;
irp = NULL;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_SUBMIT_URB,
deviceExtension->TopOfStackDeviceObject,
NULL,
0,
NULL,
0,
TRUE,
&event,
&ioStatus);
if (!irp)
{
FreeBT_DbgPrint(1, ("FBTUSB: CallUSBD: IoBuildDeviceIoControlRequest failed\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
nextStack = IoGetNextIrpStackLocation(irp);
ASSERT(nextStack != NULL);
nextStack->Parameters.Others.Argument1 = Urb;
FreeBT_DbgPrint(3, ("FBTUSB: CallUSBD::"));
FreeBT_IoIncrement(deviceExtension);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, irp);
if (ntStatus == STATUS_PENDING)
{
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
ntStatus = ioStatus.Status;
}
FreeBT_DbgPrint(3, ("FBTUSB: CallUSBD::"));
FreeBT_IoDecrement(deviceExtension);
return ntStatus;
}
NTSTATUS HandleQueryStopDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
KIRQL oldIrql;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
FreeBT_DbgPrint(3, ("FBTUSB: HandleQueryStopDevice: Entered\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
// If we can stop the device, we need to set the QueueState to
// HoldRequests so further requests will be queued.
KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);
SET_NEW_PNP_STATE(deviceExtension, PendingStop);
deviceExtension->QueueState = HoldRequests;
KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);
// wait for the existing ones to be finished.
// first, decrement this operation
FreeBT_DbgPrint(3, ("FBTUSB: HandleQueryStopDevice::"));
FreeBT_IoDecrement(deviceExtension);
KeWaitForSingleObject(&deviceExtension->StopEvent, Executive, KernelMode, FALSE, NULL);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoSkipCurrentIrpStackLocation(Irp);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
FreeBT_DbgPrint(3, ("FBTUSB: HandleQueryStopDevice: Leaving\n"));
return ntStatus;
}
NTSTATUS HandleCancelStopDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
KIRQL oldIrql;
KEVENT event;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
FreeBT_DbgPrint(3, ("FBTUSB: HandleCancelStopDevice: Entered\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
// Send this IRP down and wait for it to come back.
// Set the QueueState flag to AllowRequests,
// and process all the previously queued up IRPs.
// First check to see whether you have received cancel-stop
// without first receiving a query-stop. This could happen if someone
// above us fails a query-stop and passes down the subsequent
// cancel-stop.
if(PendingStop == deviceExtension->DeviceState)
{
KeInitializeEvent(&event, NotificationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE)IrpCompletionRoutine,
(PVOID)&event,
TRUE,
TRUE,
TRUE);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
if(ntStatus == STATUS_PENDING)
{
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
ntStatus = Irp->IoStatus.Status;
}
if(NT_SUCCESS(ntStatus))
{
KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);
RESTORE_PREVIOUS_PNP_STATE(deviceExtension);
deviceExtension->QueueState = AllowRequests;
ASSERT(deviceExtension->DeviceState == Working);
KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);
ProcessQueuedRequests(deviceExtension);
}
}
else
{
// spurious Irp
ntStatus = STATUS_SUCCESS;
}
FreeBT_DbgPrint(3, ("FBTUSB: HandleCancelStopDevice: Leaving\n"));
return ntStatus;
}
NTSTATUS HandleStopDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
KIRQL oldIrql;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
FreeBT_DbgPrint(3, ("FBTUSB: HandleStopDevice: Entered\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
if(WinXpOrBetter == deviceExtension->WdmVersion)
{
if(deviceExtension->SSEnable)
{
// Cancel the timer so that the DPCs are no longer fired.
// Thus, we are making judicious usage of our resources.
// we do not need DPCs because the device is stopping.
// The timers are re-initialized while handling the start
// device irp.
KeCancelTimer(&deviceExtension->Timer);
// after the device is stopped, it can be surprise removed.
// we set this to 0, so that we do not attempt to cancel
// the timer while handling surprise remove or remove irps.
// when we get the start device request, this flag will be
// reinitialized.
deviceExtension->SSEnable = 0;
// make sure that if a DPC was fired before we called cancel timer,
// then the DPC and work-time have run to their completion
KeWaitForSingleObject(&deviceExtension->NoDpcWorkItemPendingEvent, Executive, KernelMode, FALSE, NULL);
// make sure that the selective suspend request has been completed.
KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent, Executive, KernelMode, FALSE, NULL);
}
}
// after the stop Irp is sent to the lower driver object,
// the driver must not send any more Irps down that touch
// the device until another Start has occurred.
if (deviceExtension->WaitWakeEnable)
{
CancelWaitWake(deviceExtension);
}
KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);
SET_NEW_PNP_STATE(deviceExtension, Stopped);
KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);
// This is the right place to actually give up all the resources used
// This might include calls to IoDisconnectInterrupt, MmUnmapIoSpace,
// etc.
ReleaseMemory(DeviceObject);
ntStatus = DeconfigureDevice(DeviceObject);
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = 0;
IoSkipCurrentIrpStackLocation(Irp);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
FreeBT_DbgPrint(3, ("FBTUSB: HandleStopDevice: Leaving\n"));
return ntStatus;
}
NTSTATUS HandleQueryRemoveDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
KIRQL oldIrql;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
FreeBT_DbgPrint(3, ("FBTUSB: HandleQueryRemoveDevice: Entered\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
// If we can allow removal of the device, we should set the QueueState
// to HoldRequests so further requests will be queued. This is required
// so that we can process queued up requests in cancel-remove just in
// case somebody else in the stack fails the query-remove.
ntStatus = CanRemoveDevice(DeviceObject, Irp);
KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);
deviceExtension->QueueState = HoldRequests;
SET_NEW_PNP_STATE(deviceExtension, PendingRemove);
KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);
FreeBT_DbgPrint(3, ("FBTUSB: HandleQueryRemoveDevice::"));
FreeBT_IoDecrement(deviceExtension);
// Wait for all the requests to be completed
KeWaitForSingleObject(&deviceExtension->StopEvent, Executive, KernelMode, FALSE, NULL);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoSkipCurrentIrpStackLocation(Irp);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
FreeBT_DbgPrint(3, ("FBTUSB: HandleQueryRemoveDevice: Leaving\n"));
return ntStatus;
}
NTSTATUS HandleCancelRemoveDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
KIRQL oldIrql;
KEVENT event;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
FreeBT_DbgPrint(3, ("FBTUSB: HandleCancelRemoveDevice: Entered\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
// We need to reset the QueueState flag to ProcessRequest,
// since the device resume its normal activities.
// First check to see whether you have received cancel-remove
// without first receiving a query-remove. This could happen if
// someone above us fails a query-remove and passes down the
// subsequent cancel-remove.
if(PendingRemove == deviceExtension->DeviceState)
{
KeInitializeEvent(&event, NotificationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE)IrpCompletionRoutine,
(PVOID)&event,
TRUE,
TRUE,
TRUE);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
if(ntStatus == STATUS_PENDING)
{
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
ntStatus = Irp->IoStatus.Status;
}
if (NT_SUCCESS(ntStatus))
{
KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);
deviceExtension->QueueState = AllowRequests;
RESTORE_PREVIOUS_PNP_STATE(deviceExtension);
KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);
// process the queued requests that arrive between
// QUERY_REMOVE and CANCEL_REMOVE
ProcessQueuedRequests(deviceExtension);
}
}
else
{
// spurious cancel-remove
ntStatus = STATUS_SUCCESS;
}
FreeBT_DbgPrint(3, ("FBTUSB: HandleCancelRemoveDevice: Leaving\n"));
return ntStatus;
}
NTSTATUS HandleSurpriseRemoval(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
KIRQL oldIrql;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
FreeBT_DbgPrint(3, ("FBTUSB: HandleSurpriseRemoval: Entered\n"));
// initialize variables
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
// 1. fail pending requests
// 2. return device and memory resources
// 3. disable interfaces
if(deviceExtension->WaitWakeEnable)
{
CancelWaitWake(deviceExtension);
}
if (WinXpOrBetter == deviceExtension->WdmVersion)
{
if (deviceExtension->SSEnable)
{
// Cancel the timer so that the DPCs are no longer fired.
// we do not need DPCs because the device has been surprise
// removed
KeCancelTimer(&deviceExtension->Timer);
deviceExtension->SSEnable = 0;
// make sure that if a DPC was fired before we called cancel timer,
// then the DPC and work-time have run to their completion
KeWaitForSingleObject(&deviceExtension->NoDpcWorkItemPendingEvent, Executive, KernelMode, FALSE, NULL);
// make sure that the selective suspend request has been completed.
KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent, Executive, KernelMode, FALSE, NULL);
}
}
KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);
deviceExtension->QueueState = FailRequests;
SET_NEW_PNP_STATE(deviceExtension, SurpriseRemoved);
KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);
ProcessQueuedRequests(deviceExtension);
ntStatus = IoSetDeviceInterfaceState(&deviceExtension->InterfaceName, FALSE);
if(!NT_SUCCESS(ntStatus))
{
FreeBT_DbgPrint(1, ("FBTUSB: HandleSurpriseRemoval: IoSetDeviceInterfaceState::disable:failed\n"));
}
FreeBT_AbortPipes(DeviceObject);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoSkipCurrentIrpStackLocation(Irp);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
FreeBT_DbgPrint(3, ("FBTUSB: HandleSurpriseRemoval: Leaving\n"));
return ntStatus;
}
NTSTATUS HandleRemoveDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
KIRQL oldIrql;
KEVENT event;
ULONG requestCount;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
FreeBT_DbgPrint(3, ("FBTUSB: HandleRemoveDevice: Entered\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
// The Plug & Play system has dictated the removal of this device. We
// have no choice but to detach and delete the device object.
// (If we wanted to express an interest in preventing this removal,
// we should have failed the query remove IRP).
if(SurpriseRemoved != deviceExtension->DeviceState)
{
// we are here after QUERY_REMOVE
KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);
deviceExtension->QueueState = FailRequests;
KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);
if(deviceExtension->WaitWakeEnable)
{
CancelWaitWake(deviceExtension);
}
if(WinXpOrBetter == deviceExtension->WdmVersion)
{
if (deviceExtension->SSEnable)
{
// Cancel the timer so that the DPCs are no longer fired.
// we do not need DPCs because the device has been removed
KeCancelTimer(&deviceExtension->Timer);
deviceExtension->SSEnable = 0;
// make sure that if a DPC was fired before we called cancel timer,
// then the DPC and work-time have run to their completion
KeWaitForSingleObject(&deviceExtension->NoDpcWorkItemPendingEvent, Executive, KernelMode, FALSE, NULL);
// make sure that the selective suspend request has been completed.
KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent, Executive, KernelMode, FALSE, NULL);
}
}
ProcessQueuedRequests(deviceExtension);
ntStatus = IoSetDeviceInterfaceState(&deviceExtension->InterfaceName, FALSE);
if(!NT_SUCCESS(ntStatus))
{
FreeBT_DbgPrint(1, ("FBTUSB: HandleRemoveDevice: IoSetDeviceInterfaceState::disable:failed\n"));
}
FreeBT_AbortPipes(DeviceObject);
}
KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);
SET_NEW_PNP_STATE(deviceExtension, Removed);
KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);
#ifdef ENABLE_WMI
FreeBT_WmiDeRegistration(deviceExtension);
#endif
// Need 2 decrements
FreeBT_DbgPrint(3, ("FBTUSB: HandleRemoveDevice::"));
requestCount = FreeBT_IoDecrement(deviceExtension);
ASSERT(requestCount > 0);
FreeBT_DbgPrint(3, ("FBTUSB: HandleRemoveDevice::"));
requestCount = FreeBT_IoDecrement(deviceExtension);
KeWaitForSingleObject(&deviceExtension->RemoveEvent,
Executive,
KernelMode,
FALSE,
NULL);
ReleaseMemory(DeviceObject);
// We need to send the remove down the stack before we detach,
// but we don't need to wait for the completion of this operation
// (and to register a completion routine).
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoSkipCurrentIrpStackLocation(Irp);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
IoDetachDevice(deviceExtension->TopOfStackDeviceObject);
IoDeleteDevice(DeviceObject);
FreeBT_DbgPrint(3, ("FBTUSB: HandleRemoveDevice: Leaving\n"));
return ntStatus;
}
NTSTATUS HandleQueryCapabilities(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
ULONG i;
KEVENT event;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
PDEVICE_CAPABILITIES pdc;
PIO_STACK_LOCATION irpStack;
FreeBT_DbgPrint(3, ("FBTUSB: HandleQueryCapabilities: Entered\n"));
irpStack = IoGetCurrentIrpStackLocation(Irp);
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
pdc = irpStack->Parameters.DeviceCapabilities.Capabilities;
if(pdc->Version < 1 || pdc->Size < sizeof(DEVICE_CAPABILITIES))
{
FreeBT_DbgPrint(1, ("FBTUSB: HandleQueryCapabilities::request failed\n"));
ntStatus = STATUS_UNSUCCESSFUL;
return ntStatus;
}
// Add in the SurpriseRemovalOK bit before passing it down.
pdc->SurpriseRemovalOK = TRUE;
Irp->IoStatus.Status = STATUS_SUCCESS;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE)IrpCompletionRoutine,
(PVOID)&event,
TRUE,
TRUE,
TRUE);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
if(ntStatus == STATUS_PENDING)
{
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
ntStatus = Irp->IoStatus.Status;
}
// initialize PowerDownLevel to disabled
deviceExtension->PowerDownLevel = PowerDeviceUnspecified;
if(NT_SUCCESS(ntStatus))
{
deviceExtension->DeviceCapabilities = *pdc;
for(i = PowerSystemSleeping1; i <= PowerSystemSleeping3; i++)
{
if(deviceExtension->DeviceCapabilities.DeviceState[i] < PowerDeviceD3)
{
deviceExtension->PowerDownLevel = deviceExtension->DeviceCapabilities.DeviceState[i];
}
}
// since its safe to surprise-remove this device, we shall
// set the SurpriseRemoveOK flag to supress any dialog to
// user.
pdc->SurpriseRemovalOK = 1;
}
if(deviceExtension->PowerDownLevel == PowerDeviceUnspecified ||
deviceExtension->PowerDownLevel <= PowerDeviceD0)
{
deviceExtension->PowerDownLevel = PowerDeviceD2;
}
FreeBT_DbgPrint(3, ("FBTUSB: HandleQueryCapabilities: Leaving\n"));
return ntStatus;
}
VOID DpcRoutine(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
/*++
DPC routine triggered by the timer to check the idle state
of the device and submit an idle request for the device.
--*/
{
NTSTATUS ntStatus;
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION deviceExtension;
PIO_WORKITEM item;
FreeBT_DbgPrint(3, ("FBTUSB: DpcRoutine: Entered\n"));
deviceObject = (PDEVICE_OBJECT)DeferredContext;
deviceExtension = (PDEVICE_EXTENSION)deviceObject->DeviceExtension;
// Clear this event since a DPC has been fired!
KeClearEvent(&deviceExtension->NoDpcWorkItemPendingEvent);
if(CanDeviceSuspend(deviceExtension))
{
FreeBT_DbgPrint(3, ("FBTUSB: DpcRoutine: Device is Idle\n"));
item = IoAllocateWorkItem(deviceObject);
if (item)
{
IoQueueWorkItem(item, IdleRequestWorkerRoutine, DelayedWorkQueue, item);
ntStatus = STATUS_PENDING;
}
else
{
FreeBT_DbgPrint(3, ("FBTUSB: DpcRoutine: Cannot alloc memory for work item\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
KeSetEvent(&deviceExtension->NoDpcWorkItemPendingEvent, IO_NO_INCREMENT, FALSE);
}
}
else
{
FreeBT_DbgPrint(3, ("FBTUSB: DpcRoutine: Idle event not signaled\n"));
KeSetEvent(&deviceExtension->NoDpcWorkItemPendingEvent, IO_NO_INCREMENT, FALSE);
}
FreeBT_DbgPrint(3, ("FBTUSB: DpcRoutine: Leaving\n"));
}
VOID IdleRequestWorkerRoutine(IN PDEVICE_OBJECT DeviceObject, IN PVOID Context)
{
PIRP irp;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
PIO_WORKITEM workItem;
FreeBT_DbgPrint(3, ("FBTUSB: IdleRequestWorkerRoutine: Entered\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
workItem = (PIO_WORKITEM) Context;
if(CanDeviceSuspend(deviceExtension))
{
FreeBT_DbgPrint(3, ("FBTUSB: IdleRequestWorkerRoutine: Device is idle\n"));
ntStatus = SubmitIdleRequestIrp(deviceExtension);
if(!NT_SUCCESS(ntStatus))
{
FreeBT_DbgPrint(1, ("FBTUSB: IdleRequestWorkerRoutine: SubmitIdleRequestIrp failed\n"));
}
}
else
{
FreeBT_DbgPrint(3, ("FBTUSB: IdleRequestWorkerRoutine: Device is not idle\n"));
}
IoFreeWorkItem(workItem);
KeSetEvent(&deviceExtension->NoDpcWorkItemPendingEvent, IO_NO_INCREMENT, FALSE);
FreeBT_DbgPrint(3, ("FBTUSB: IdleRequestsWorkerRoutine: Leaving\n"));
}
VOID ProcessQueuedRequests(IN OUT PDEVICE_EXTENSION DeviceExtension)
/*++
Routine Description:
Remove and process the entries in the queue. If this routine is called
when processing IRP_MN_CANCEL_STOP_DEVICE, IRP_MN_CANCEL_REMOVE_DEVICE
or IRP_MN_START_DEVICE, the requests are passed to the next lower driver.
If the routine is called when IRP_MN_REMOVE_DEVICE is received, the IRPs
are complete with STATUS_DELETE_PENDING
Arguments:
DeviceExtension - pointer to device extension
Return Value:
None
--*/
{
KIRQL oldIrql;
PIRP nextIrp,
cancelledIrp;
PVOID cancelRoutine;
LIST_ENTRY cancelledIrpList;
PLIST_ENTRY listEntry;
FreeBT_DbgPrint(3, ("FBTUSB: ProcessQueuedRequests: Entered\n"));
cancelRoutine = NULL;
InitializeListHead(&cancelledIrpList);
// 1. dequeue the entries in the queue
// 2. reset the cancel routine
// 3. process them
// 3a. if the device is active, send them down
// 3b. else complete with STATUS_DELETE_PENDING
while(1)
{
KeAcquireSpinLock(&DeviceExtension->QueueLock, &oldIrql);
if(IsListEmpty(&DeviceExtension->NewRequestsQueue))
{
KeReleaseSpinLock(&DeviceExtension->QueueLock, oldIrql);
break;
}
listEntry = RemoveHeadList(&DeviceExtension->NewRequestsQueue);
nextIrp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
cancelRoutine = IoSetCancelRoutine(nextIrp, NULL);
// check if its already cancelled
if (nextIrp->Cancel)
{
if(cancelRoutine)
{
// the cancel routine for this IRP hasnt been called yet
// so queue the IRP in the cancelledIrp list and complete
// after releasing the lock
InsertTailList(&cancelledIrpList, listEntry);
}
else
{
// the cancel routine has run
// it must be waiting to hold the queue lock
// so initialize the IRPs listEntry
InitializeListHead(listEntry);
}
KeReleaseSpinLock(&DeviceExtension->QueueLock, oldIrql);
}
else
{
KeReleaseSpinLock(&DeviceExtension->QueueLock, oldIrql);
if(FailRequests == DeviceExtension->QueueState)
{
nextIrp->IoStatus.Information = 0;
nextIrp->IoStatus.Status = STATUS_DELETE_PENDING;
IoCompleteRequest(nextIrp, IO_NO_INCREMENT);
}
else
{
PIO_STACK_LOCATION irpStack;
FreeBT_DbgPrint(3, ("FBTUSB: ProcessQueuedRequests::"));
FreeBT_IoIncrement(DeviceExtension);
IoSkipCurrentIrpStackLocation(nextIrp);
IoCallDriver(DeviceExtension->TopOfStackDeviceObject, nextIrp);
FreeBT_DbgPrint(3, ("FBTUSB: ProcessQueuedRequests::"));
FreeBT_IoDecrement(DeviceExtension);
}
}
}
while(!IsListEmpty(&cancelledIrpList))
{
PLIST_ENTRY cancelEntry = RemoveHeadList(&cancelledIrpList);
cancelledIrp = CONTAINING_RECORD(cancelEntry, IRP, Tail.Overlay.ListEntry);
cancelledIrp->IoStatus.Status = STATUS_CANCELLED;
cancelledIrp->IoStatus.Information = 0;
IoCompleteRequest(cancelledIrp, IO_NO_INCREMENT);
}
FreeBT_DbgPrint(3, ("FBTUSB: ProcessQueuedRequests: Leaving\n"));
return;
}
NTSTATUS FreeBT_GetRegistryDword(IN PWCHAR RegPath, IN PWCHAR ValueName, IN OUT PULONG Value)
{
ULONG defaultData;
WCHAR buffer[MAXIMUM_FILENAME_LENGTH];
NTSTATUS ntStatus;
UNICODE_STRING regPath;
RTL_QUERY_REGISTRY_TABLE paramTable[2];
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetRegistryDword: Entered\n"));
regPath.Length = 0;
regPath.MaximumLength = MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR);
regPath.Buffer = buffer;
RtlZeroMemory(regPath.Buffer, regPath.MaximumLength);
RtlMoveMemory(regPath.Buffer, RegPath, wcslen(RegPath) * sizeof(WCHAR));
RtlZeroMemory(paramTable, sizeof(paramTable));
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[0].Name = ValueName;
paramTable[0].EntryContext = Value;
paramTable[0].DefaultType = REG_DWORD;
paramTable[0].DefaultData = &defaultData;
paramTable[0].DefaultLength = sizeof(ULONG);
ntStatus = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE |
RTL_REGISTRY_OPTIONAL,
regPath.Buffer,
paramTable,
NULL,
NULL);
if (NT_SUCCESS(ntStatus))
{
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetRegistryDword: Success, Value = %X\n", *Value));
return STATUS_SUCCESS;
}
else
{
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetRegistryDword: Failed\n"));
*Value = 0;
return STATUS_UNSUCCESSFUL;
}
}
NTSTATUS FreeBT_DispatchClean(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PDEVICE_EXTENSION deviceExtension;
KIRQL oldIrql;
LIST_ENTRY cleanupList;
PLIST_ENTRY thisEntry,
nextEntry,
listHead;
PIRP pendingIrp;
PIO_STACK_LOCATION pendingIrpStack,
irpStack;
NTSTATUS ntStatus;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
irpStack = IoGetCurrentIrpStackLocation(Irp);
InitializeListHead(&cleanupList);
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchClean::"));
FreeBT_IoIncrement(deviceExtension);
KeAcquireSpinLock(&deviceExtension->QueueLock, &oldIrql);
listHead = &deviceExtension->NewRequestsQueue;
for(thisEntry = listHead->Flink, nextEntry = thisEntry->Flink;
thisEntry != listHead;
thisEntry = nextEntry, nextEntry = thisEntry->Flink)
{
pendingIrp = CONTAINING_RECORD(thisEntry, IRP, Tail.Overlay.ListEntry);
pendingIrpStack = IoGetCurrentIrpStackLocation(pendingIrp);
if (irpStack->FileObject == pendingIrpStack->FileObject)
{
RemoveEntryList(thisEntry);
if (NULL == IoSetCancelRoutine(pendingIrp, NULL))
{
InitializeListHead(thisEntry);
}
else
{
InsertTailList(&cleanupList, thisEntry);
}
}
}
KeReleaseSpinLock(&deviceExtension->QueueLock, oldIrql);
while(!IsListEmpty(&cleanupList))
{
thisEntry = RemoveHeadList(&cleanupList);
pendingIrp = CONTAINING_RECORD(thisEntry, IRP, Tail.Overlay.ListEntry);
pendingIrp->IoStatus.Information = 0;
pendingIrp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(pendingIrp, IO_NO_INCREMENT);
}
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchClean::"));
FreeBT_IoDecrement(deviceExtension);
return STATUS_SUCCESS;
}
BOOLEAN CanDeviceSuspend(IN PDEVICE_EXTENSION DeviceExtension)
{
FreeBT_DbgPrint(3, ("FBTUSB: CanDeviceSuspend: Entered\n"));
if ((DeviceExtension->OpenHandleCount == 0) && (DeviceExtension->OutStandingIO == 1))
return TRUE;
return FALSE;
}
NTSTATUS FreeBT_AbortPipes(IN PDEVICE_OBJECT DeviceObject)
{
PURB urb;
ULONG i;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
PFREEBT_PIPE_CONTEXT pipeContext;
PUSBD_PIPE_INFORMATION pipeInformation;
PUSBD_INTERFACE_INFORMATION interfaceInfo;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
pipeContext = deviceExtension->PipeContext;
interfaceInfo = deviceExtension->UsbInterface;
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_AbortPipes: Entered\n"));
if(interfaceInfo == NULL || pipeContext == NULL)
return STATUS_SUCCESS;
for(i=0; i<interfaceInfo->NumberOfPipes; i++)
{
if(pipeContext[i].PipeOpen)
{
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_AbortPipes: Aborting open pipe %d\n", i));
urb = (PURB) ExAllocatePool(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST));
if (urb)
{
urb->UrbHeader.Length = sizeof(struct _URB_PIPE_REQUEST);
urb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE;
urb->UrbPipeRequest.PipeHandle = interfaceInfo->Pipes[i].PipeHandle;
ntStatus = CallUSBD(DeviceObject, urb);
ExFreePool(urb);
}
else
{
FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_AbortPipes: Failed to alloc memory for urb\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
return ntStatus;
}
if(NT_SUCCESS(ntStatus))
pipeContext[i].PipeOpen = FALSE;
}
}
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_AbortPipes: Leaving\n"));
return STATUS_SUCCESS;
}
// Completion routine for PNP IRPs
NTSTATUS IrpCompletionRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
{
PKEVENT event = (PKEVENT) Context;
KeSetEvent(event, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
LONG FreeBT_IoIncrement(IN OUT PDEVICE_EXTENSION DeviceExtension)
{
LONG result = 0;
KIRQL oldIrql;
KeAcquireSpinLock(&DeviceExtension->IOCountLock, &oldIrql);
result = InterlockedIncrement((PLONG)(&DeviceExtension->OutStandingIO));
// When OutStandingIO bumps from 1 to 2, clear the StopEvent
if (result == 2)
KeClearEvent(&DeviceExtension->StopEvent);
KeReleaseSpinLock(&DeviceExtension->IOCountLock, oldIrql);
FreeBT_DbgPrint(3, ("FreeBT_IoIncrement::%d\n", result));
return result;
}
LONG FreeBT_IoDecrement(IN OUT PDEVICE_EXTENSION DeviceExtension)
{
LONG result = 0;
KIRQL oldIrql;
KeAcquireSpinLock(&DeviceExtension->IOCountLock, &oldIrql);
result = InterlockedDecrement((PLONG)(&DeviceExtension->OutStandingIO));
if (result == 1)
KeSetEvent(&DeviceExtension->StopEvent, IO_NO_INCREMENT, FALSE);
if(result == 0)
{
ASSERT(Removed == DeviceExtension->DeviceState);
KeSetEvent(&DeviceExtension->RemoveEvent, IO_NO_INCREMENT, FALSE);
}
KeReleaseSpinLock(&DeviceExtension->IOCountLock, oldIrql);
FreeBT_DbgPrint(3, ("FreeBT_IoDecrement::%d\n", result));
return result;
}
NTSTATUS CanStopDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
// For the time being, just allow it to be stopped
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Irp);
return STATUS_SUCCESS;
}
NTSTATUS CanRemoveDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
// For the time being, just allow it to be removed
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Irp);
return STATUS_SUCCESS;
}
NTSTATUS ReleaseMemory(IN PDEVICE_OBJECT DeviceObject)
{
// Disconnect from the interrupt and unmap any I/O ports
PDEVICE_EXTENSION deviceExtension;
UNICODE_STRING uniDeviceName;
NTSTATUS ntStatus;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
if (deviceExtension->UsbConfigurationDescriptor)
{
FreeBT_DbgPrint(3, ("FBTUSB: ReleaseMemory: Freeing UsbConfigurationDescriptor\n"));
ExFreePool(deviceExtension->UsbConfigurationDescriptor);
deviceExtension->UsbConfigurationDescriptor = NULL;
}
if(deviceExtension->UsbInterface)
{
FreeBT_DbgPrint(3, ("FBTUSB: ReleaseMemory: Freeing UsbInterface\n"));
ExFreePool(deviceExtension->UsbInterface);
deviceExtension->UsbInterface = NULL;
}
if(deviceExtension->PipeContext)
{
RtlInitUnicodeString(&uniDeviceName, deviceExtension->wszDosDeviceName);
ntStatus = IoDeleteSymbolicLink(&uniDeviceName);
if (!NT_SUCCESS(ntStatus))
FreeBT_DbgPrint(3, ("FBTUSB: Failed to delete symbolic link %ws\n", deviceExtension->wszDosDeviceName));
FreeBT_DbgPrint(3, ("FBTUSB: ReleaseMemory: Freeing PipeContext %p\n", deviceExtension->PipeContext));
ExFreePool(deviceExtension->PipeContext);
deviceExtension->PipeContext = NULL;
}
return STATUS_SUCCESS;
}
PCHAR PnPMinorFunctionString (UCHAR MinorFunction)
{
switch (MinorFunction)
{
case IRP_MN_START_DEVICE:
return "IRP_MN_START_DEVICE\n";
case IRP_MN_QUERY_REMOVE_DEVICE:
return "IRP_MN_QUERY_REMOVE_DEVICE\n";
case IRP_MN_REMOVE_DEVICE:
return "IRP_MN_REMOVE_DEVICE\n";
case IRP_MN_CANCEL_REMOVE_DEVICE:
return "IRP_MN_CANCEL_REMOVE_DEVICE\n";
case IRP_MN_STOP_DEVICE:
return "IRP_MN_STOP_DEVICE\n";
case IRP_MN_QUERY_STOP_DEVICE:
return "IRP_MN_QUERY_STOP_DEVICE\n";
case IRP_MN_CANCEL_STOP_DEVICE:
return "IRP_MN_CANCEL_STOP_DEVICE\n";
case IRP_MN_QUERY_DEVICE_RELATIONS:
return "IRP_MN_QUERY_DEVICE_RELATIONS\n";
case IRP_MN_QUERY_INTERFACE:
return "IRP_MN_QUERY_INTERFACE\n";
case IRP_MN_QUERY_CAPABILITIES:
return "IRP_MN_QUERY_CAPABILITIES\n";
case IRP_MN_QUERY_RESOURCES:
return "IRP_MN_QUERY_RESOURCES\n";
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
return "IRP_MN_QUERY_RESOURCE_REQUIREMENTS\n";
case IRP_MN_QUERY_DEVICE_TEXT:
return "IRP_MN_QUERY_DEVICE_TEXT\n";
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
return "IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n";
case IRP_MN_READ_CONFIG:
return "IRP_MN_READ_CONFIG\n";
case IRP_MN_WRITE_CONFIG:
return "IRP_MN_WRITE_CONFIG\n";
case IRP_MN_EJECT:
return "IRP_MN_EJECT\n";
case IRP_MN_SET_LOCK:
return "IRP_MN_SET_LOCK\n";
case IRP_MN_QUERY_ID:
return "IRP_MN_QUERY_ID\n";
case IRP_MN_QUERY_PNP_DEVICE_STATE:
return "IRP_MN_QUERY_PNP_DEVICE_STATE\n";
case IRP_MN_QUERY_BUS_INFORMATION:
return "IRP_MN_QUERY_BUS_INFORMATION\n";
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
return "IRP_MN_DEVICE_USAGE_NOTIFICATION\n";
case IRP_MN_SURPRISE_REMOVAL:
return "IRP_MN_SURPRISE_REMOVAL\n";
default:
return "IRP_MN_?????\n";
}
}