// 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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; iNumberOfPipes; 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; iNumberOfPipes; 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI CanDeviceSuspend(IN PDEVICE_EXTENSION DeviceExtension) { FreeBT_DbgPrint(3, ("FBTUSB: CanDeviceSuspend: Entered\n")); if ((DeviceExtension->OpenHandleCount == 0) && (DeviceExtension->OutStandingIO == 1)) return TRUE; return FALSE; } NTSTATUS NTAPI 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; iNumberOfPipes; 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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 NTAPI 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"; } }