// 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 "fbtusb.h" #include "fbtpwr.h" #include "fbtpnp.h" #include "fbtdev.h" #include "fbtrwr.h" #include "fbtwmi.h" #include "fbtusr.h" // Handle power events NTSTATUS NTAPI FreeBT_DispatchPower(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS ntStatus = STATUS_SUCCESS; PIO_STACK_LOCATION irpStack; //PUNICODE_STRING tagString; PDEVICE_EXTENSION deviceExtension; irpStack = IoGetCurrentIrpStackLocation(Irp); deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; // We don't queue power Irps, we'll only check if the // device was removed, otherwise we'll take appropriate // action and send it to the next lower driver. In general // drivers should not cause long delays while handling power // IRPs. If a driver cannot handle a power IRP in a brief time, // it should return STATUS_PENDING and queue all incoming // IRPs until the IRP completes. if (Removed == deviceExtension->DeviceState) { // Even if a driver fails the IRP, it must nevertheless call // PoStartNextPowerIrp to inform the Power Manager that it // is ready to handle another power IRP. PoStartNextPowerIrp(Irp); Irp->IoStatus.Status = ntStatus = STATUS_DELETE_PENDING; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return ntStatus; } if (NotStarted == deviceExtension->DeviceState) { // if the device is not started yet, pass it down PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); return PoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp); } FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchPower::")); FreeBT_IoIncrement(deviceExtension); switch(irpStack->MinorFunction) { case IRP_MN_SET_POWER: // The Power Manager sends this IRP for one of the // following reasons: // 1) To notify drivers of a change to the system power state. // 2) To change the power state of a device for which // the Power Manager is performing idle detection. // A driver sends IRP_MN_SET_POWER to change the power // state of its device if it's a power policy owner for the // device. FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchPower: IRP_MN_SET_POWER\n")); IoMarkIrpPending(Irp); switch(irpStack->Parameters.Power.Type) { case SystemPowerState: HandleSystemSetPower(DeviceObject, Irp); ntStatus = STATUS_PENDING; break; case DevicePowerState: HandleDeviceSetPower(DeviceObject, Irp); ntStatus = STATUS_PENDING; break; } break; case IRP_MN_QUERY_POWER: // The Power Manager sends a power IRP with the minor // IRP code IRP_MN_QUERY_POWER to determine whether it // can safely change to the specified system power state // (S1-S5) and to allow drivers to prepare for such a change. // If a driver can put its device in the requested state, // it sets status to STATUS_SUCCESS and passes the IRP down. FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchPower: IRP_MN_QUERY_POWER\n")); IoMarkIrpPending(Irp); switch(irpStack->Parameters.Power.Type) { case SystemPowerState: HandleSystemQueryPower(DeviceObject, Irp); ntStatus = STATUS_PENDING; break; case DevicePowerState: HandleDeviceQueryPower(DeviceObject, Irp); ntStatus = STATUS_PENDING; break; } break; case IRP_MN_WAIT_WAKE: // The minor power IRP code IRP_MN_WAIT_WAKE provides // for waking a device or waking the system. Drivers // of devices that can wake themselves or the system // send IRP_MN_WAIT_WAKE. The system sends IRP_MN_WAIT_WAKE // only to devices that always wake the system, such as // the power-on switch. FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchPower: IRP_MN_WAIT_WAKE\n")); IoMarkIrpPending(Irp); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine( Irp, (PIO_COMPLETION_ROUTINE)WaitWakeCompletionRoutine, deviceExtension, TRUE, TRUE, TRUE); PoStartNextPowerIrp(Irp); ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp); if(!NT_SUCCESS(ntStatus)) { FreeBT_DbgPrint(1, ("FBTUSB: Lower drivers failed the wait-wake Irp\n")); } ntStatus = STATUS_PENDING; // push back the count HERE and NOT in completion routine // a pending Wait Wake Irp should not impede stopping the device FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchPower: IRP_MN_WAIT_WAKE::")); FreeBT_IoDecrement(deviceExtension); break; case IRP_MN_POWER_SEQUENCE: // A driver sends this IRP as an optimization to determine // whether its device actually entered a specific power state. // This IRP is optional. Power Manager cannot send this IRP. FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchPower: IRP_MN_POWER_SEQUENCE\n")); default: PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp); if(!NT_SUCCESS(ntStatus)) { FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_DispatchPower: Lower drivers failed this Irp\n")); } FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchPower::")); FreeBT_IoDecrement(deviceExtension); break; } return ntStatus; } NTSTATUS NTAPI HandleSystemQueryPower(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS ntStatus; PDEVICE_EXTENSION deviceExtension; SYSTEM_POWER_STATE systemState; PIO_STACK_LOCATION irpStack; FreeBT_DbgPrint(3, ("FBTUSB: HandleSystemQueryPower: Entered\n")); // initialize variables deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; irpStack = IoGetCurrentIrpStackLocation(Irp); systemState = irpStack->Parameters.Power.State.SystemState; FreeBT_DbgPrint(3, ("FBTUSB: HandleSystemQueryPower: Query for system power state S%X\n" "FBTUSB: HandleSystemQueryPower: Current system power state S%X\n", systemState - 1, deviceExtension->SysPower - 1)); // Fail a query for a power state incompatible with waking up the system if ((deviceExtension->WaitWakeEnable) && (systemState > deviceExtension->DeviceCapabilities.SystemWake)) { FreeBT_DbgPrint(1, ("FBTUSB: HandleSystemQueryPower: Query for an incompatible system power state\n")); PoStartNextPowerIrp(Irp); Irp->IoStatus.Status = ntStatus = STATUS_INVALID_DEVICE_STATE; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); FreeBT_DbgPrint(3, ("FBTUSB: HandleSystemQueryPower::")); FreeBT_IoDecrement(deviceExtension); return ntStatus; } // if querying for a lower S-state, issue a wait-wake if((systemState > deviceExtension->SysPower) && (deviceExtension->WaitWakeEnable)) { IssueWaitWake(deviceExtension); } IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine( Irp, (PIO_COMPLETION_ROUTINE)SysPoCompletionRoutine, deviceExtension, TRUE, TRUE, TRUE); ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp); FreeBT_DbgPrint(3, ("FBTUSB: HandleSystemQueryPower: Leaving\n")); return STATUS_PENDING; } NTSTATUS NTAPI HandleSystemSetPower(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS ntStatus; PDEVICE_EXTENSION deviceExtension; SYSTEM_POWER_STATE systemState; PIO_STACK_LOCATION irpStack; FreeBT_DbgPrint(3, ("FBTUSB: HandleSystemSetPower: Entered\n")); deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; irpStack = IoGetCurrentIrpStackLocation(Irp); systemState = irpStack->Parameters.Power.State.SystemState; FreeBT_DbgPrint(3, ("FBTUSB: HandleSystemSetPower: Set request for system power state S%X\n" "FBTUSB: HandleSystemSetPower: Current system power state S%X\n", systemState - 1, deviceExtension->SysPower - 1)); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine( Irp, (PIO_COMPLETION_ROUTINE)SysPoCompletionRoutine, deviceExtension, TRUE, TRUE, TRUE); ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp); FreeBT_DbgPrint(3, ("FBTUSB: HandleSystemSetPower: Leaving\n")); return STATUS_PENDING; } NTSTATUS NTAPI HandleDeviceQueryPower(PDEVICE_OBJECT DeviceObject, PIRP Irp) { NTSTATUS ntStatus; PDEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION irpStack; DEVICE_POWER_STATE deviceState; FreeBT_DbgPrint(3, ("FBTUSB: HandleDeviceQueryPower: Entered\n")); deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; irpStack = IoGetCurrentIrpStackLocation(Irp); deviceState = irpStack->Parameters.Power.State.DeviceState; FreeBT_DbgPrint(3, ("FBTUSB: HandleDeviceQueryPower: Query for device power state D%X\n" "FBTUSB: HandleDeviceQueryPower: Current device power state D%X\n", deviceState - 1, deviceExtension->DevPower - 1)); if (deviceExtension->WaitWakeEnable && deviceState > deviceExtension->DeviceCapabilities.DeviceWake) { PoStartNextPowerIrp(Irp); Irp->IoStatus.Status = ntStatus = STATUS_INVALID_DEVICE_STATE; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); FreeBT_DbgPrint(3, ("FBTUSB: HandleDeviceQueryPower::")); FreeBT_IoDecrement(deviceExtension); return ntStatus; } if (deviceState < deviceExtension->DevPower) { ntStatus = STATUS_SUCCESS; } else { ntStatus = HoldIoRequests(DeviceObject, Irp); if(STATUS_PENDING == ntStatus) { return ntStatus; } } // on error complete the Irp. // on success pass it to the lower layers PoStartNextPowerIrp(Irp); Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; if(!NT_SUCCESS(ntStatus)) { IoCompleteRequest(Irp, IO_NO_INCREMENT); } else { IoSkipCurrentIrpStackLocation(Irp); ntStatus=PoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp); } FreeBT_DbgPrint(3, ("FBTUSB: HandleDeviceQueryPower::")); FreeBT_IoDecrement(deviceExtension); FreeBT_DbgPrint(3, ("FBTUSB: HandleDeviceQueryPower: Leaving\n")); return ntStatus; } NTSTATUS NTAPI SysPoCompletionRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PDEVICE_EXTENSION DeviceExtension) { NTSTATUS ntStatus; PIO_STACK_LOCATION irpStack; ntStatus = Irp->IoStatus.Status; irpStack = IoGetCurrentIrpStackLocation(Irp); FreeBT_DbgPrint(3, ("FBTUSB: SysPoCompletionRoutine: Entered\n")); // lower drivers failed this Irp if(!NT_SUCCESS(ntStatus)) { PoStartNextPowerIrp(Irp); FreeBT_DbgPrint(3, ("FBTUSB: SysPoCompletionRoutine::")); FreeBT_IoDecrement(DeviceExtension); return STATUS_SUCCESS; } // ..otherwise update the cached system power state (IRP_MN_SET_POWER) if(irpStack->MinorFunction == IRP_MN_SET_POWER) { DeviceExtension->SysPower = irpStack->Parameters.Power.State.SystemState; } // queue device irp and return STATUS_MORE_PROCESSING_REQUIRED SendDeviceIrp(DeviceObject, Irp); FreeBT_DbgPrint(3, ("FBTUSB: SysPoCompletionRoutine: Leaving\n")); return STATUS_MORE_PROCESSING_REQUIRED; } VOID NTAPI SendDeviceIrp(IN PDEVICE_OBJECT DeviceObject, IN PIRP SIrp ) { NTSTATUS ntStatus; POWER_STATE powState; PDEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION irpStack; SYSTEM_POWER_STATE systemState; DEVICE_POWER_STATE devState; PPOWER_COMPLETION_CONTEXT powerContext; irpStack = IoGetCurrentIrpStackLocation(SIrp); systemState = irpStack->Parameters.Power.State.SystemState; deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; FreeBT_DbgPrint(3, ("FBTUSB: SendDeviceIrp: Entered\n")); // Read out the D-IRP out of the S->D mapping array captured in QueryCap's. // we can choose deeper sleep states than our mapping but never choose // lighter ones. devState = deviceExtension->DeviceCapabilities.DeviceState[systemState]; powState.DeviceState = devState; powerContext = (PPOWER_COMPLETION_CONTEXT) ExAllocatePool(NonPagedPool, sizeof(POWER_COMPLETION_CONTEXT)); if (!powerContext) { FreeBT_DbgPrint(1, ("FBTUSB: SendDeviceIrp: Failed to alloc memory for powerContext\n")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } else { powerContext->DeviceObject = DeviceObject; powerContext->SIrp = SIrp; // in win2k PoRequestPowerIrp can take fdo or pdo. ntStatus = PoRequestPowerIrp( deviceExtension->PhysicalDeviceObject, irpStack->MinorFunction, powState, (PREQUEST_POWER_COMPLETE)DevPoCompletionRoutine, powerContext, NULL); } if (!NT_SUCCESS(ntStatus)) { if (powerContext) { ExFreePool(powerContext); } PoStartNextPowerIrp(SIrp); SIrp->IoStatus.Status = ntStatus; SIrp->IoStatus.Information = 0; IoCompleteRequest(SIrp, IO_NO_INCREMENT); FreeBT_DbgPrint(3, ("FBTUSB: SendDeviceIrp::")); FreeBT_IoDecrement(deviceExtension); } FreeBT_DbgPrint(3, ("FBTUSB: SendDeviceIrp: Leaving\n")); } VOID NTAPI DevPoCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus ) { PIRP sIrp; PDEVICE_EXTENSION deviceExtension; PPOWER_COMPLETION_CONTEXT powerContext; powerContext = (PPOWER_COMPLETION_CONTEXT) Context; sIrp = powerContext->SIrp; deviceExtension = (PDEVICE_EXTENSION) powerContext->DeviceObject->DeviceExtension; FreeBT_DbgPrint(3, ("FBTUSB: DevPoCompletionRoutine: Entered\n")); sIrp->IoStatus.Status = IoStatus->Status; PoStartNextPowerIrp(sIrp); sIrp->IoStatus.Information = 0; IoCompleteRequest(sIrp, IO_NO_INCREMENT); FreeBT_DbgPrint(3, ("FBTUSB: DevPoCompletionRoutine::")); FreeBT_IoDecrement(deviceExtension); ExFreePool(powerContext); FreeBT_DbgPrint(3, ("FBTUSB: DevPoCompletionRoutine: Leaving\n")); } NTSTATUS NTAPI HandleDeviceSetPower(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { KIRQL oldIrql; NTSTATUS ntStatus; POWER_STATE newState; PIO_STACK_LOCATION irpStack; PDEVICE_EXTENSION deviceExtension; DEVICE_POWER_STATE newDevState, oldDevState; FreeBT_DbgPrint(3, ("FBTUSB: HandleDeviceSetPower: Entered\n")); deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; irpStack = IoGetCurrentIrpStackLocation(Irp); oldDevState = deviceExtension->DevPower; newState = irpStack->Parameters.Power.State; newDevState = newState.DeviceState; FreeBT_DbgPrint(3, ("FBTUSB: HandleDeviceSetPower: Set request for device power state D%X\n" "FBTUSB: HandleDeviceSetPower: Current device power state D%X\n", newDevState - 1, deviceExtension->DevPower - 1)); if (newDevState < oldDevState) { FreeBT_DbgPrint(3, ("FBTUSB: HandleDeviceSetPower: Adding power to the device\n")); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine( Irp, (PIO_COMPLETION_ROUTINE)FinishDevPoUpIrp, deviceExtension, TRUE, TRUE, TRUE); ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp); } else { // newDevState >= oldDevState // hold I/O if transition from D0 -> DX (X = 1, 2, 3) // if transition from D1 or D2 to deeper sleep states, // I/O queue is already on hold. if(PowerDeviceD0 == oldDevState && newDevState > oldDevState) { // D0 -> DX transition FreeBT_DbgPrint(3, ("FBTUSB: HandleDeviceSetPower: Removing power from the device\n")); ntStatus = HoldIoRequests(DeviceObject, Irp); if (!NT_SUCCESS(ntStatus)) { PoStartNextPowerIrp(Irp); Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); FreeBT_DbgPrint(3, ("FBTUSB: HandleDeviceSetPower::")); FreeBT_IoDecrement(deviceExtension); return ntStatus; } else { goto HandleDeviceSetPower_Exit; } } else if (PowerDeviceD0 == oldDevState && PowerDeviceD0 == newDevState) { // D0 -> D0 // unblock the queue which may have been blocked processing // query irp FreeBT_DbgPrint(3, ("FBTUSB: HandleDeviceSetPower: A SetD0 request\n")); KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql); deviceExtension->QueueState = AllowRequests; KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql); ProcessQueuedRequests(deviceExtension); } IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine( Irp, (PIO_COMPLETION_ROUTINE) FinishDevPoDnIrp, deviceExtension, TRUE, TRUE, TRUE); ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp); if(!NT_SUCCESS(ntStatus)) { FreeBT_DbgPrint(1, ("FBTUSB: HandleDeviceSetPower: Lower drivers failed a power Irp\n")); } } HandleDeviceSetPower_Exit: FreeBT_DbgPrint(3, ("FBTUSB: HandleDeviceSetPower: Leaving\n")); return STATUS_PENDING; } NTSTATUS NTAPI FinishDevPoUpIrp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PDEVICE_EXTENSION DeviceExtension) { NTSTATUS ntStatus; FreeBT_DbgPrint(3, ("FBTUSB: FinishDevPoUpIrp: Entered\n")); ntStatus = Irp->IoStatus.Status; if(Irp->PendingReturned) { IoMarkIrpPending(Irp); } if(!NT_SUCCESS(ntStatus)) { PoStartNextPowerIrp(Irp); FreeBT_DbgPrint(3, ("FBTUSB: FinishDevPoUpIrp::")); FreeBT_IoDecrement(DeviceExtension); return STATUS_SUCCESS; } SetDeviceFunctional(DeviceObject, Irp, DeviceExtension); FreeBT_DbgPrint(3, ("FBTUSB: FinishDevPoUpIrp: Leaving\n")); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS NTAPI SetDeviceFunctional(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PDEVICE_EXTENSION DeviceExtension) { KIRQL oldIrql; NTSTATUS ntStatus; POWER_STATE newState; PIO_STACK_LOCATION irpStack; DEVICE_POWER_STATE newDevState, oldDevState; ntStatus = Irp->IoStatus.Status; irpStack = IoGetCurrentIrpStackLocation(Irp); newState = irpStack->Parameters.Power.State; newDevState = newState.DeviceState; oldDevState = DeviceExtension->DevPower; FreeBT_DbgPrint(3, ("FBTUSB: SetDeviceFunctional: Entered\n")); // update the cached state DeviceExtension->DevPower = newDevState; // restore appropriate amount of state to our h/w // this driver does not implement partial context // save/restore. PoSetPowerState(DeviceObject, DevicePowerState, newState); if(PowerDeviceD0 == newDevState) { KeAcquireSpinLock(&DeviceExtension->DevStateLock, &oldIrql); DeviceExtension->QueueState = AllowRequests; KeReleaseSpinLock(&DeviceExtension->DevStateLock, oldIrql); ProcessQueuedRequests(DeviceExtension); } PoStartNextPowerIrp(Irp); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); FreeBT_DbgPrint(3, ("FBTUSB: SetDeviceFunctional::")); FreeBT_IoDecrement(DeviceExtension); FreeBT_DbgPrint(3, ("FBTUSB: SetDeviceFunctional: Leaving\n")); return STATUS_SUCCESS; } NTSTATUS NTAPI FinishDevPoDnIrp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PDEVICE_EXTENSION DeviceExtension) { NTSTATUS ntStatus; POWER_STATE newState; PIO_STACK_LOCATION irpStack; FreeBT_DbgPrint(3, ("FBTUSB: FinishDevPoDnIrp: Entered\n")); ntStatus = Irp->IoStatus.Status; irpStack = IoGetCurrentIrpStackLocation(Irp); newState = irpStack->Parameters.Power.State; if (NT_SUCCESS(ntStatus) && irpStack->MinorFunction == IRP_MN_SET_POWER) { FreeBT_DbgPrint(3, ("FBTUSB: updating cache..\n")); DeviceExtension->DevPower = newState.DeviceState; PoSetPowerState(DeviceObject, DevicePowerState, newState); } PoStartNextPowerIrp(Irp); FreeBT_DbgPrint(3, ("FBTUSB: FinishDevPoDnIrp::")); FreeBT_IoDecrement(DeviceExtension); FreeBT_DbgPrint(3, ("FBTUSB: FinishDevPoDnIrp: Leaving\n")); return STATUS_SUCCESS; } NTSTATUS NTAPI HoldIoRequests(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS ntStatus; PIO_WORKITEM item; PDEVICE_EXTENSION deviceExtension; PWORKER_THREAD_CONTEXT context; FreeBT_DbgPrint(3, ("FBTUSB: HoldIoRequests: Entered\n")); deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; deviceExtension->QueueState = HoldRequests; context = (PWORKER_THREAD_CONTEXT) ExAllocatePool(NonPagedPool, sizeof(WORKER_THREAD_CONTEXT)); if(context) { item = IoAllocateWorkItem(DeviceObject); context->Irp = Irp; context->DeviceObject = DeviceObject; context->WorkItem = item; if (item) { IoMarkIrpPending(Irp); IoQueueWorkItem(item, HoldIoRequestsWorkerRoutine, DelayedWorkQueue, context); ntStatus = STATUS_PENDING; } else { FreeBT_DbgPrint(3, ("FBTUSB: HoldIoRequests: Failed to allocate memory for workitem\n")); ExFreePool(context); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } else { FreeBT_DbgPrint(1, ("FBTUSB: HoldIoRequests: Failed to alloc memory for worker thread context\n")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } FreeBT_DbgPrint(3, ("FBTUSB: HoldIoRequests: Leaving\n")); return ntStatus; } VOID NTAPI HoldIoRequestsWorkerRoutine(IN PDEVICE_OBJECT DeviceObject, IN PVOID Context) { PIRP irp; NTSTATUS ntStatus; PDEVICE_EXTENSION deviceExtension; PWORKER_THREAD_CONTEXT context; FreeBT_DbgPrint(3, ("FBTUSB: HoldIoRequestsWorkerRoutine: Entered\n")); deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; context = (PWORKER_THREAD_CONTEXT) Context; irp = (PIRP) context->Irp; // wait for I/O in progress to finish. // the stop event is signalled when the counter drops to 1. // invoke FreeBT_IoDecrement twice: once each for the S-Irp and D-Irp. FreeBT_DbgPrint(3, ("FBTUSB: HoldIoRequestsWorkerRoutine::")); FreeBT_IoDecrement(deviceExtension); FreeBT_DbgPrint(3, ("FBTUSB: HoldIoRequestsWorkerRoutine::")); FreeBT_IoDecrement(deviceExtension); KeWaitForSingleObject(&deviceExtension->StopEvent, Executive, KernelMode, FALSE, NULL); // Increment twice to restore the count FreeBT_DbgPrint(3, ("FBTUSB: HoldIoRequestsWorkerRoutine::")); FreeBT_IoIncrement(deviceExtension); FreeBT_DbgPrint(3, ("FBTUSB: HoldIoRequestsWorkerRoutine::")); FreeBT_IoIncrement(deviceExtension); // now send the Irp down IoCopyCurrentIrpStackLocationToNext(irp); IoSetCompletionRoutine( irp, (PIO_COMPLETION_ROUTINE) FinishDevPoDnIrp, deviceExtension, TRUE, TRUE, TRUE); ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject, irp); if(!NT_SUCCESS(ntStatus)) { FreeBT_DbgPrint(1, ("FBTUSB: HoldIoRequestsWorkerRoutine: Lower driver fail a power Irp\n")); } IoFreeWorkItem(context->WorkItem); ExFreePool((PVOID)context); FreeBT_DbgPrint(3, ("FBTUSB: HoldIoRequestsWorkerRoutine: Leaving\n")); } NTSTATUS NTAPI QueueRequest(IN OUT PDEVICE_EXTENSION DeviceExtension, IN PIRP Irp) { KIRQL oldIrql; NTSTATUS ntStatus; FreeBT_DbgPrint(3, ("FBTUSB: QueueRequests: Entered\n")); ntStatus = STATUS_PENDING; ASSERT(HoldRequests == DeviceExtension->QueueState); KeAcquireSpinLock(&DeviceExtension->QueueLock, &oldIrql); InsertTailList(&DeviceExtension->NewRequestsQueue, &Irp->Tail.Overlay.ListEntry); IoMarkIrpPending(Irp); IoSetCancelRoutine(Irp, CancelQueued); KeReleaseSpinLock(&DeviceExtension->QueueLock, oldIrql); FreeBT_DbgPrint(3, ("FBTUSB: QueueRequests: Leaving\n")); return ntStatus; } VOID NTAPI CancelQueued(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PDEVICE_EXTENSION deviceExtension; KIRQL oldIrql; FreeBT_DbgPrint(3, ("FBTUSB: CancelQueued: Entered\n")); deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; oldIrql = Irp->CancelIrql; // Release the cancel spin lock IoReleaseCancelSpinLock(Irp->CancelIrql); // Acquire the queue lock KeAcquireSpinLockAtDpcLevel(&deviceExtension->QueueLock); // Remove the cancelled Irp from queue and release the lock RemoveEntryList(&Irp->Tail.Overlay.ListEntry); KeReleaseSpinLock(&deviceExtension->QueueLock, oldIrql); // complete with STATUS_CANCELLED Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); FreeBT_DbgPrint(3, ("FBTUSB: CancelQueued: Leaving\n")); return; } NTSTATUS NTAPI IssueWaitWake(IN PDEVICE_EXTENSION DeviceExtension) { POWER_STATE poState; NTSTATUS ntStatus; FreeBT_DbgPrint(3, ("FBTUSB: IssueWaitWake: Entered\n")); if(InterlockedExchange(&DeviceExtension->FlagWWOutstanding, 1)) { return STATUS_DEVICE_BUSY; } InterlockedExchange(&DeviceExtension->FlagWWCancel, 0); // lowest state from which this Irp will wake the system poState.SystemState = DeviceExtension->DeviceCapabilities.SystemWake; ntStatus = PoRequestPowerIrp(DeviceExtension->PhysicalDeviceObject, IRP_MN_WAIT_WAKE, poState, (PREQUEST_POWER_COMPLETE) WaitWakeCallback, DeviceExtension, &DeviceExtension->WaitWakeIrp); if(!NT_SUCCESS(ntStatus)) { InterlockedExchange(&DeviceExtension->FlagWWOutstanding, 0); } FreeBT_DbgPrint(3, ("FBTUSB: IssueWaitWake: Leaving\n")); return ntStatus; } VOID NTAPI CancelWaitWake(IN PDEVICE_EXTENSION DeviceExtension) { PIRP Irp; FreeBT_DbgPrint(3, ("FBTUSB: CancelWaitWake: Entered\n")); Irp = (PIRP) InterlockedExchangePointer((PVOID*)&DeviceExtension->WaitWakeIrp, NULL); if(Irp) { IoCancelIrp(Irp); if(InterlockedExchange(&DeviceExtension->FlagWWCancel, 1)) { PoStartNextPowerIrp(Irp); Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); } } FreeBT_DbgPrint(3, ("FBTUSB: CancelWaitWake: Leaving\n")); } NTSTATUS NTAPI WaitWakeCompletionRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PDEVICE_EXTENSION DeviceExtension) { FreeBT_DbgPrint(3, ("FBTUSB: WaitWakeCompletionRoutine: Entered\n")); if(Irp->PendingReturned) { IoMarkIrpPending(Irp); } // Nullify the WaitWakeIrp pointer-the Irp is released // as part of the completion process. If it's already NULL, // avoid race with the CancelWaitWake routine. if(InterlockedExchangePointer((PVOID*)&DeviceExtension->WaitWakeIrp, NULL)) { PoStartNextPowerIrp(Irp); return STATUS_SUCCESS; } // CancelWaitWake has run. // If FlagWWCancel != 0, complete the Irp. // If FlagWWCancel == 0, CancelWaitWake completes it. if(InterlockedExchange(&DeviceExtension->FlagWWCancel, 1)) { PoStartNextPowerIrp(Irp); return STATUS_CANCELLED; } FreeBT_DbgPrint(3, ("FBTUSB: WaitWakeCompletionRoutine: Leaving\n")); return STATUS_MORE_PROCESSING_REQUIRED; } VOID NTAPI WaitWakeCallback( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus) { NTSTATUS ntStatus; POWER_STATE powerState; PDEVICE_EXTENSION deviceExtension; FreeBT_DbgPrint(3, ("FBTUSB: WaitWakeCallback: Entered\n")); deviceExtension = (PDEVICE_EXTENSION) Context; InterlockedExchange(&deviceExtension->FlagWWOutstanding, 0); if(!NT_SUCCESS(IoStatus->Status)) { return; } // wake up the device if(deviceExtension->DevPower == PowerDeviceD0) { FreeBT_DbgPrint(3, ("FBTUSB: WaitWakeCallback: Device already powered up...\n")); return; } FreeBT_DbgPrint(3, ("FBTUSB: WaitWakeCallback::")); FreeBT_IoIncrement(deviceExtension); powerState.DeviceState = PowerDeviceD0; ntStatus = PoRequestPowerIrp(deviceExtension->PhysicalDeviceObject, IRP_MN_SET_POWER, powerState, (PREQUEST_POWER_COMPLETE) WWIrpCompletionFunc, deviceExtension, NULL); if(deviceExtension->WaitWakeEnable) { IssueWaitWake(deviceExtension); } FreeBT_DbgPrint(3, ("FBTUSB: WaitWakeCallback: Leaving\n")); return; } PCHAR NTAPI PowerMinorFunctionString (IN UCHAR MinorFunction) { switch (MinorFunction) { case IRP_MN_SET_POWER: return "IRP_MN_SET_POWER\n"; case IRP_MN_QUERY_POWER: return "IRP_MN_QUERY_POWER\n"; case IRP_MN_POWER_SEQUENCE: return "IRP_MN_POWER_SEQUENCE\n"; case IRP_MN_WAIT_WAKE: return "IRP_MN_WAIT_WAKE\n"; default: return "IRP_MN_?????\n"; } }