VOID CancelSelectSuspend(IN PTDeviceExtension DeviceExtension) { PIRP irp; KIRQL oldIrql; irp = NULL; BulkUsb_DbgPrint(3, ("file bulkdev: CancelSelectSuspend - begins\n")); KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql); if(!CanDeviceSuspend(DeviceExtension)) { BulkUsb_DbgPrint(3, ("file bulkdev: Device is not idle\n")); irp = (PIRP)InterlockedExchangePointer(&DeviceExtension->PendingIdleIrp, NULL); } KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql); if(irp) { IoCancelIrp(irp); if(0 == InterlockedDecrement(&DeviceExtension->FreeIdleIrpCount)) { BulkUsb_DbgPrint(3, ("file bulkdev: CancelSelectSuspend frees the irp\n")); IoFreeIrp(irp); KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE); } } BulkUsb_DbgPrint(3, ("file bulkdev: CancelSelectSuspend - ends\n")); return; }
NTSTATUS SubmitIdleRequestIrp( IN PDEVICE_EXTENSION DeviceExtension ) /*++ Routine Description: This routine builds an idle request irp with an associated callback routine and a completion routine in the driver and passes the irp down the stack. Arguments: DeviceExtension - pointer to device extension Return Value: NT status value --*/ { PIRP irp; NTSTATUS ntStatus; KIRQL oldIrql; PUSB_IDLE_CALLBACK_INFO idleCallbackInfo; PIO_STACK_LOCATION nextStack; // // initialize variables // irp = NULL; idleCallbackInfo = NULL; BulkUsb_DbgPrint(3, ("SubmitIdleRequest - begins\n")); ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); if(PowerDeviceD0 != DeviceExtension->DevPower) { ntStatus = STATUS_POWER_STATE_INVALID; goto SubmitIdleRequestIrp_Exit; } KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql); if(InterlockedExchange(&DeviceExtension->IdleReqPend, 1)) { BulkUsb_DbgPrint(1, ("Idle request pending..\n")); KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql); ntStatus = STATUS_DEVICE_BUSY; goto SubmitIdleRequestIrp_Exit; } // // clear the NoIdleReqPendEvent because we are about // to submit an idle request. Since we are so early // to clear this event, make sure that if we fail this // request we set back the event. // KeClearEvent(&DeviceExtension->NoIdleReqPendEvent); idleCallbackInfo = ExAllocatePool(NonPagedPool, sizeof(struct _USB_IDLE_CALLBACK_INFO)); if(idleCallbackInfo) { idleCallbackInfo->IdleCallback = IdleNotificationCallback; idleCallbackInfo->IdleContext = (PVOID)DeviceExtension; ASSERT(DeviceExtension->IdleCallbackInfo == NULL); DeviceExtension->IdleCallbackInfo = idleCallbackInfo; // // we use IoAllocateIrp to create an irp to selectively suspend the // device. This irp lies pending with the hub driver. When appropriate // the hub driver will invoked callback, where we power down. The completion // routine is invoked when we power back. // irp = IoAllocateIrp(DeviceExtension->TopOfStackDeviceObject->StackSize, FALSE); if(irp == NULL) { BulkUsb_DbgPrint(1, ("cannot build idle request irp\n")); KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE); InterlockedExchange(&DeviceExtension->IdleReqPend, 0); KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql); ExFreePool(idleCallbackInfo); ntStatus = STATUS_INSUFFICIENT_RESOURCES; goto SubmitIdleRequestIrp_Exit; } nextStack = IoGetNextIrpStackLocation(irp); nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION; nextStack->Parameters.DeviceIoControl.Type3InputBuffer = idleCallbackInfo; nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(struct _USB_IDLE_CALLBACK_INFO); IoSetCompletionRoutine(irp, IdleNotificationRequestComplete, DeviceExtension, TRUE, TRUE, TRUE); DeviceExtension->PendingIdleIrp = irp; // // we initialize the count to 2. // The reason is, if the CancelSelectSuspend routine manages // to grab the irp from the device extension, then the last of the // CancelSelectSuspend routine/IdleNotificationRequestComplete routine // to execute will free this irp. We need to have this schema so that // 1. completion routine does not attempt to touch the irp freed by // CancelSelectSuspend routine. // 2. CancelSelectSuspend routine doesnt wait for ever for the completion // routine to complete! // DeviceExtension->FreeIdleIrpCount = 2; KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql); // // check if the device is idle. // A check here ensures that a race condition did not // completely reverse the call sequence of SubmitIdleRequestIrp // and CancelSelectiveSuspend // if(!CanDeviceSuspend(DeviceExtension) || PowerDeviceD0 != DeviceExtension->DevPower) { // // IRPs created using IoBuildDeviceIoControlRequest should be // completed by calling IoCompleteRequest and not merely // deallocated. // BulkUsb_DbgPrint(1, ("Device is not idle\n")); KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql); DeviceExtension->IdleCallbackInfo = NULL; DeviceExtension->PendingIdleIrp = NULL; KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE); InterlockedExchange(&DeviceExtension->IdleReqPend, 0); KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql); if(idleCallbackInfo) { ExFreePool(idleCallbackInfo); } // // it is still safe to touch the local variable "irp" here. // the irp has not been passed down the stack, the irp has // no cancellation routine. The worse position is that the // CancelSelectSuspend has run after we released the spin // lock above. It is still essential to free the irp. // if(irp) { IoFreeIrp(irp); } goto SubmitIdleRequestIrp_Exit; } BulkUsb_DbgPrint(3, ("Cancel the timers\n")); // // 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 we already have an idle irp pending. // The timers are re-initialized in the completion routine. // KeCancelTimer(&DeviceExtension->Timer); ntStatus = IoCallDriver(DeviceExtension->TopOfStackDeviceObject, irp); if(!NT_SUCCESS(ntStatus)) { BulkUsb_DbgPrint(1, ("IoCallDriver failed\n")); goto SubmitIdleRequestIrp_Exit; } } else { BulkUsb_DbgPrint(1, ("Memory allocation for idleCallbackInfo failed\n")); KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE); InterlockedExchange(&DeviceExtension->IdleReqPend, 0); KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } SubmitIdleRequestIrp_Exit: BulkUsb_DbgPrint(3, ("SubmitIdleRequest - ends\n")); return ntStatus; }
NTSTATUS SubmitIdleRequestIrp(IN PTDeviceExtension DeviceExtension) { PIRP irp; NTSTATUS ntStatus; KIRQL oldIrql; PUSB_IDLE_CALLBACK_INFO idleCallbackInfo; PIO_STACK_LOCATION nextStack; irp = NULL; idleCallbackInfo = NULL; BulkUsb_DbgPrint(3, ("file bulkdev: SubmitIdleRequest - process\n")); ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); if(PowerDeviceD0 != DeviceExtension->DevPower) { // ntStatus = STATUS_POWER_STATE_INVALID; // goto SubmitIdleRequestIrp_Exit; return STATUS_POWER_STATE_INVALID; } KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql); if(InterlockedExchange(&DeviceExtension->IdleReqPend, 1)) { BulkUsb_DbgPrint(1, ("file bulkdev: Idle request pending..\n")); KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql); // ntStatus = STATUS_DEVICE_BUSY; // goto SubmitIdleRequestIrp_Exit; return STATUS_DEVICE_BUSY; } KeClearEvent(&DeviceExtension->NoIdleReqPendEvent); idleCallbackInfo = ExAllocatePool(NonPagedPool, sizeof(struct _USB_IDLE_CALLBACK_INFO)); if(idleCallbackInfo) { idleCallbackInfo->IdleCallback = IdleNotificationCallback; idleCallbackInfo->IdleContext = (PVOID)DeviceExtension; ASSERT(DeviceExtension->IdleCallbackInfo == NULL); DeviceExtension->IdleCallbackInfo = idleCallbackInfo; irp = IoAllocateIrp(DeviceExtension->TopOfStackDeviceObject->StackSize, FALSE); if(irp == NULL) { BulkUsb_DbgPrint(1, ("file bulkdev: cannot build idle request irp\n")); KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE); InterlockedExchange(&DeviceExtension->IdleReqPend, 0); KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql); ExFreePool(idleCallbackInfo); // ntStatus = STATUS_INSUFFICIENT_RESOURCES; // goto SubmitIdleRequestIrp_Exit; return STATUS_INSUFFICIENT_RESOURCES; } nextStack = IoGetNextIrpStackLocation(irp); nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION; nextStack->Parameters.DeviceIoControl.Type3InputBuffer = idleCallbackInfo; nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(struct _USB_IDLE_CALLBACK_INFO); IoSetCompletionRoutine(irp, IdleNotificationRequestComplete, DeviceExtension, TRUE, TRUE, TRUE); DeviceExtension->PendingIdleIrp = irp; DeviceExtension->FreeIdleIrpCount = 2; KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql); if(!CanDeviceSuspend(DeviceExtension) || PowerDeviceD0 != DeviceExtension->DevPower) { BulkUsb_DbgPrint(1, ("file bulkdev: Device is not idle\n")); KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql); DeviceExtension->IdleCallbackInfo = NULL; DeviceExtension->PendingIdleIrp = NULL; KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE); InterlockedExchange(&DeviceExtension->IdleReqPend, 0); KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql); if(idleCallbackInfo) { ExFreePool(idleCallbackInfo); } if(irp) { IoFreeIrp(irp); } // ntStatus = STATUS_UNSUCCESSFUL; // goto SubmitIdleRequestIrp_Exit; return STATUS_UNSUCCESSFUL; } BulkUsb_DbgPrint(3, ("file bulkdev: Cancel the timers\n")); KeCancelTimer(&DeviceExtension->Timer); ntStatus = IoCallDriver(DeviceExtension->TopOfStackDeviceObject, irp); if(!NT_SUCCESS(ntStatus)) { // BulkUsb_DbgPrint(1, ("file bulkdev: IoCallDriver failed\n")); // goto SubmitIdleRequestIrp_Exit; return ntStatus; } } else { BulkUsb_DbgPrint(1, ("file bulkdev: Memory allocation for idleCallbackInfo failed\n")); KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE); InterlockedExchange(&DeviceExtension->IdleReqPend, 0); KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } //SubmitIdleRequestIrp_Exit: BulkUsb_DbgPrint(3, ("file bulkdev: SubmitIdleRequest - ends\n")); return ntStatus; }
VOID CancelSelectSuspend( IN PDEVICE_EXTENSION DeviceExtension ) /*++ Routine Description: This routine is invoked to cancel selective suspend request. Arguments: DeviceExtension - pointer to device extension Return Value: None. --*/ { PIRP irp; KIRQL oldIrql; irp = NULL; BulkUsb_DbgPrint(3, ("CancelSelectSuspend - begins\n")); KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql); if(!CanDeviceSuspend(DeviceExtension)) { BulkUsb_DbgPrint(3, ("Device is not idle\n")); irp = (PIRP) InterlockedExchangePointer( &DeviceExtension->PendingIdleIrp, NULL); } KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql); // // since we have a valid Irp ptr, // we can call IoCancelIrp on it, // without the fear of the irp // being freed underneath us. // if(irp) { // // This routine has the irp pointer. // It is safe to call IoCancelIrp because we know that // the compleiton routine will not free this irp unless... // // if(IoCancelIrp(irp)) { BulkUsb_DbgPrint(3, ("IoCancelIrp returns TRUE\n")); } else { BulkUsb_DbgPrint(3, ("IoCancelIrp returns FALSE\n")); } // // ....we decrement the FreeIdleIrpCount from 2 to 1. // if completion routine runs ahead of us, then this routine // decrements the FreeIdleIrpCount from 1 to 0 and hence shall // free the irp. // if(0 == InterlockedDecrement(&DeviceExtension->FreeIdleIrpCount)) { BulkUsb_DbgPrint(3, ("CancelSelectSuspend frees the irp\n")); IoFreeIrp(irp); KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE); } } BulkUsb_DbgPrint(3, ("CancelSelectSuspend - ends\n")); return; }