1 // Copyright (c) 2004, Antony C. Roberts
3 // Use of this file is subject to the terms
4 // described in the LICENSE.TXT file that
5 // accompanies this file.
7 // Your use of this file indicates your
8 // acceptance of the terms described in
11 // http://www.freebt.net
22 // Dispatch routine for CreateHandle
23 NTSTATUS
FreeBT_DispatchCreate(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
)
27 PFILE_OBJECT fileObject
;
28 PDEVICE_EXTENSION deviceExtension
;
29 PIO_STACK_LOCATION irpStack
;
30 PFREEBT_PIPE_CONTEXT pipeContext
;
31 PUSBD_INTERFACE_INFORMATION interface
;
35 FreeBT_DbgPrint(3, ("FreeBT_DispatchCreate: Entered\n"));
37 irpStack
= IoGetCurrentIrpStackLocation(Irp
);
38 fileObject
= irpStack
->FileObject
;
39 deviceExtension
= (PDEVICE_EXTENSION
) DeviceObject
->DeviceExtension
;
41 if (deviceExtension
->DeviceState
!= Working
)
43 ntStatus
= STATUS_INVALID_DEVICE_STATE
;
44 goto FreeBT_DispatchCreate_Exit
;
48 if (deviceExtension
->UsbInterface
)
50 interface
= deviceExtension
->UsbInterface
;
56 FreeBT_DbgPrint(1, ("UsbInterface not found\n"));
57 ntStatus
= STATUS_INVALID_DEVICE_STATE
;
58 goto FreeBT_DispatchCreate_Exit
;
64 fileObject
->FsContext
= NULL
;
69 ntStatus
= STATUS_INVALID_PARAMETER
;
70 goto FreeBT_DispatchCreate_Exit
;
74 if (deviceExtension
->OpenHandleCount
>0)
76 ntStatus
= STATUS_ACCESS_VIOLATION
;
77 goto FreeBT_DispatchCreate_Exit
;
81 // opening a device as opposed to pipe.
82 ntStatus
= STATUS_SUCCESS
;
84 InterlockedIncrement(&deviceExtension
->OpenHandleCount
);
86 // the device is idle if it has no open handles or pending PnP Irps
87 // since we just received an open handle request, cancel idle req.
88 if (deviceExtension
->SSEnable
)
89 CancelSelectSuspend(deviceExtension
);
91 FreeBT_DispatchCreate_Exit
:
92 Irp
->IoStatus
.Status
= ntStatus
;
93 Irp
->IoStatus
.Information
= 0;
94 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
96 FreeBT_DbgPrint(3, ("FreeBT_DispatchCreate: Leaving\n"));
101 // Dispatch routine for CloseHandle
102 NTSTATUS
FreeBT_DispatchClose(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
)
105 PFILE_OBJECT fileObject
;
106 PDEVICE_EXTENSION deviceExtension
;
107 PIO_STACK_LOCATION irpStack
;
108 PFREEBT_PIPE_CONTEXT pipeContext
;
109 PUSBD_PIPE_INFORMATION pipeInformation
;
113 irpStack
= IoGetCurrentIrpStackLocation(Irp
);
114 fileObject
= irpStack
->FileObject
;
116 pipeInformation
= NULL
;
117 deviceExtension
= (PDEVICE_EXTENSION
) DeviceObject
->DeviceExtension
;
119 FreeBT_DbgPrint(3, ("FreeBT_DispatchClose: Entered\n"));
121 ntStatus
= STATUS_SUCCESS
;
122 Irp
->IoStatus
.Status
= ntStatus
;
123 Irp
->IoStatus
.Information
= 0;
124 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
126 InterlockedDecrement(&deviceExtension
->OpenHandleCount
);
128 FreeBT_DbgPrint(3, ("FreeBT_DispatchClose: Leaving\n"));
134 // Called when a HCI Send on the control pipe completes
135 NTSTATUS
FreeBT_HCISendCompletion(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
, IN PVOID Context
)
140 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_HCISendCompletion, status=0x%08X\n", Irp
->IoStatus
.Status
));
142 if (Irp
->PendingReturned
)
143 IoMarkIrpPending(Irp
);
146 FreeBT_IoDecrement(DeviceObject
->DeviceExtension
);
147 ntStatus
= Irp
->IoStatus
.Status
;
148 Irp
->IoStatus
.Information
= 0;
154 // Called the DeviceIOControl handler to send an HCI command received from the user
155 // HCI Commands are sent on the (default) control pipe
156 NTSTATUS
FreeBT_SendHCICommand(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
, IN PVOID IoBuffer
, IN ULONG InputBufferLength
)
158 PDEVICE_EXTENSION deviceExtension
;
164 PIO_STACK_LOCATION nextStack
;
165 //PFBT_HCI_CMD_HEADER pHCICommand;
166 //LARGE_INTEGER delay;
168 deviceExtension
= (PDEVICE_EXTENSION
) DeviceObject
->DeviceExtension
;
169 if (!deviceExtension
)
171 ntStatus
=STATUS_INVALID_PARAMETER
;
172 FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_SendHCICommand: Failed to get DeviceExtension\n"));
173 Irp
->IoStatus
.Status
= ntStatus
;
174 Irp
->IoStatus
.Information
= 0;
175 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
180 // The user is doing a reset, reset all the pipes as well, so that any
181 // old events or data are removed
182 /*pHCICommand=(PFBT_HCI_CMD_HEADER)IoBuffer;
183 if (pHCICommand->OpCode==FBT_HCI_CMD_RESET)
185 FreeBT_ResetPipe(DeviceObject, deviceExtension->EventPipe.PipeHandle);
186 FreeBT_ResetPipe(DeviceObject, deviceExtension->DataInPipe.PipeHandle);
187 FreeBT_ResetPipe(DeviceObject, deviceExtension->DataOutPipe.PipeHandle);
188 FreeBT_ResetPipe(DeviceObject, deviceExtension->AudioInPipe.PipeHandle);
189 FreeBT_ResetPipe(DeviceObject, deviceExtension->AudioOutPipe.PipeHandle);
191 // Wait a second for the device to recover
192 FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_SendHCICommand: Sleeping\n"));
193 delay.QuadPart = -10000 * 5000; // 5s
194 KeWaitForSingleObject(&deviceExtension->DelayEvent,
200 FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_SendHCICommand: Finished sleeping\n"));
206 urb
= (PURB
)ExAllocatePool(NonPagedPool
, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST
));
209 FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_SendHCICommand: Failed to alloc mem for urb\n"));
210 ntStatus
= STATUS_INSUFFICIENT_RESOURCES
;
211 Irp
->IoStatus
.Status
= ntStatus
;
212 Irp
->IoStatus
.Information
= 0;
213 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
218 UsbBuildVendorRequest(
220 URB_FUNCTION_CLASS_DEVICE
, // This works, for CSR and Silicon Wave
221 sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST
),
232 // use the original irp as an internal device control irp
233 nextStack
= IoGetNextIrpStackLocation(Irp
);
234 nextStack
->MajorFunction
= IRP_MJ_INTERNAL_DEVICE_CONTROL
;
235 nextStack
->Parameters
.Others
.Argument1
= (PVOID
) urb
;
236 nextStack
->Parameters
.DeviceIoControl
.IoControlCode
= IOCTL_INTERNAL_USB_SUBMIT_URB
;
238 IoSetCompletionRoutine(
240 (PIO_COMPLETION_ROUTINE
)FreeBT_HCISendCompletion
,
246 // We return STATUS_PENDING; call IoMarkIrpPending.
247 IoMarkIrpPending(Irp
);
249 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand::"));
250 FreeBT_IoIncrement(deviceExtension
);
252 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand: Sending IRP %X to underlying driver\n", Irp
));
253 ntStatus
=IoCallDriver(deviceExtension
->TopOfStackDeviceObject
, Irp
);
254 if(!NT_SUCCESS(ntStatus
))
256 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand: IoCallDriver fails with status %X\n", ntStatus
));
258 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand::"));
259 FreeBT_IoDecrement(deviceExtension
);
261 // If the device was surprise removed out, the pipeInformation field is invalid.
262 // similarly if the request was cancelled, then we need not reset the device.
263 if((ntStatus
!= STATUS_CANCELLED
) && (ntStatus
!= STATUS_DEVICE_NOT_CONNECTED
))
264 ntStatus
= FreeBT_ResetDevice(DeviceObject
);
267 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand: ntStatus is STATUS_CANCELLED or STATUS_DEVICE_NOT_CONNECTED\n"));
269 Irp
->IoStatus
.Status
= ntStatus
;
270 Irp
->IoStatus
.Information
= 0;
271 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
277 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand: Completed successfully\n"));
279 return STATUS_PENDING
;
283 // Called when a HCI Get on the event pipe completes
284 NTSTATUS
FreeBT_HCIEventCompletion(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
, IN PVOID Context
)
288 PIO_STACK_LOCATION nextStack
;
291 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_HCIEventCompletion, status=0x%08X\n", Irp
->IoStatus
.Status
));
293 if (Irp
->PendingReturned
)
294 IoMarkIrpPending(Irp
);
296 // initialize variables
298 ntStatus
= Irp
->IoStatus
.Status
;
299 Irp
->IoStatus
.Information
= urb
->UrbBulkOrInterruptTransfer
.TransferBufferLength
;
300 nextStack
= IoGetNextIrpStackLocation(Irp
);
303 FreeBT_IoDecrement(DeviceObject
->DeviceExtension
);
309 // Called from the DeviceIOControl handler to wait for an event on the interrupt pipe
310 NTSTATUS
FreeBT_GetHCIEvent(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
, IN PVOID IoBuffer
, IN ULONG InputBufferLength
)
312 PDEVICE_EXTENSION deviceExtension
;
315 PIO_STACK_LOCATION nextStack
;
317 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: Entered\n"));
320 deviceExtension
= (PDEVICE_EXTENSION
) DeviceObject
->DeviceExtension
;
322 urb
= (PURB
)ExAllocatePool(NonPagedPool
, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER
));
325 FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_GetHCIEvent: Failed to alloc mem for urb\n"));
326 ntStatus
= STATUS_INSUFFICIENT_RESOURCES
;
327 goto FreeBT_GetHCIEvent_Exit
;
331 UsbBuildInterruptOrBulkTransferRequest(
333 sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER
),
334 deviceExtension
->EventPipe
.PipeHandle
,
338 USBD_SHORT_TRANSFER_OK
|USBD_TRANSFER_DIRECTION_IN
,
341 // use the original irp as an internal device control irp, which we send down to the
342 // USB class driver in order to get our request out on the wire
343 nextStack
= IoGetNextIrpStackLocation(Irp
);
344 nextStack
->MajorFunction
= IRP_MJ_INTERNAL_DEVICE_CONTROL
;
345 nextStack
->Parameters
.Others
.Argument1
= (PVOID
) urb
;
346 nextStack
->Parameters
.DeviceIoControl
.IoControlCode
= IOCTL_INTERNAL_USB_SUBMIT_URB
;
348 IoSetCompletionRoutine(
350 (PIO_COMPLETION_ROUTINE
)FreeBT_HCIEventCompletion
,
356 // We return STATUS_PENDING; call IoMarkIrpPending.
357 IoMarkIrpPending(Irp
);
359 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent::"));
360 FreeBT_IoIncrement(deviceExtension
);
362 ntStatus
= IoCallDriver(deviceExtension
->TopOfStackDeviceObject
, Irp
);
363 if (!NT_SUCCESS(ntStatus
))
365 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: IoCallDriver fails with status %X\n", ntStatus
));
367 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent::"));
368 FreeBT_IoDecrement(deviceExtension
);
370 // If the device was surprise removed out, the pipeInformation field is invalid.
371 // similarly if the request was cancelled, then we need not reset the pipe.
372 if((ntStatus
!= STATUS_CANCELLED
) && (ntStatus
!= STATUS_DEVICE_NOT_CONNECTED
))
374 ntStatus
= FreeBT_ResetPipe(DeviceObject
, deviceExtension
->EventPipe
.PipeHandle
);
375 if(!NT_SUCCESS(ntStatus
))
377 FreeBT_DbgPrint(1, ("FreeBT_ResetPipe failed\n"));
378 ntStatus
= FreeBT_ResetDevice(DeviceObject
);
386 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: ntStatus is STATUS_CANCELLED or STATUS_DEVICE_NOT_CONNECTED\n"));
390 goto FreeBT_GetHCIEvent_Exit
;
394 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: Leaving\n"));
396 // Return STATUS_PENDING, when the lower driver completes the request,
397 // the FreeBT_HCIEventCompletion completion routine.
398 return STATUS_PENDING
;
400 FreeBT_GetHCIEvent_Exit
:
401 Irp
->IoStatus
.Status
=ntStatus
;
402 Irp
->IoStatus
.Information
=0;
404 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: Failure (0x%08x), completing IRP\n", ntStatus
));
405 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
411 // DeviceIOControl dispatch
412 NTSTATUS
FreeBT_DispatchDevCtrl(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
)
416 ULONG inputBufferLength
;
417 ULONG outputBufferLength
;
420 PDEVICE_EXTENSION deviceExtension
;
421 PIO_STACK_LOCATION irpStack
;
424 irpStack
= IoGetCurrentIrpStackLocation(Irp
);
425 code
= irpStack
->Parameters
.DeviceIoControl
.IoControlCode
;
426 deviceExtension
= (PDEVICE_EXTENSION
) DeviceObject
->DeviceExtension
;
428 ioBuffer
= Irp
->AssociatedIrp
.SystemBuffer
;
429 inputBufferLength
= irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
;
430 outputBufferLength
= irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
;
432 if (deviceExtension
->DeviceState
!= Working
)
434 FreeBT_DbgPrint(1, ("FBTUSB: Invalid device state\n"));
435 ntStatus
= STATUS_INVALID_DEVICE_STATE
;
436 goto FreeBT_DispatchDevCtrlExit
;
440 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchDevCtrl::"));
442 // Make sure that any selective suspend request has been completed.
443 if (deviceExtension
->SSEnable
)
445 FreeBT_DbgPrint(3, ("Waiting on the IdleReqPendEvent\n"));
446 KeWaitForSingleObject(&deviceExtension
->NoIdleReqPendEvent
,
456 case IOCTL_FREEBT_HCI_SEND_CMD
:
457 FreeBT_DbgPrint(3, ("FBTUSB: IOCTL_FREEBT_HCI_SEND_CMD received\n"));
458 if (inputBufferLength
<FBT_HCI_CMD_MIN_SIZE
)
460 ntStatus
= STATUS_BUFFER_TOO_SMALL
;
461 FreeBT_DbgPrint(3, ("FBTUSB: IOCTL_FREEBT_HCI_SEND_CMD: Buffer too small\n"));
462 goto FreeBT_DispatchDevCtrlExit
;
466 if (inputBufferLength
>FBT_HCI_CMD_MAX_SIZE
)
468 ntStatus
= STATUS_INVALID_BUFFER_SIZE
;
469 FreeBT_DbgPrint(3, ("FBTUSB: IOCTL_FREEBT_HCI_SEND_CMD: Buffer too long\n"));
470 goto FreeBT_DispatchDevCtrlExit
;
474 return FreeBT_SendHCICommand(DeviceObject
, Irp
, ioBuffer
, inputBufferLength
);
477 case IOCTL_FREEBT_HCI_GET_EVENT
:
478 FreeBT_DbgPrint(3, ("FBTUSB: IOCTL_FREEBT_HCI_GET_EVENT received\n"));
479 if (outputBufferLength
<FBT_HCI_EVENT_MAX_SIZE
)
481 ntStatus
= STATUS_BUFFER_TOO_SMALL
;
482 FreeBT_DbgPrint(3, ("FBTUSB: IOCTL_FREEBT_HCI_GET_EVENT: Buffer too small\n"));
483 goto FreeBT_DispatchDevCtrlExit
;
487 return FreeBT_GetHCIEvent(DeviceObject
, Irp
, ioBuffer
, outputBufferLength
);
491 FreeBT_DbgPrint(3, ("FBTUSB: Invalid IOCTL 0x%08x received\n", code
));
492 ntStatus
= STATUS_INVALID_DEVICE_REQUEST
;
497 FreeBT_DispatchDevCtrlExit
:
498 Irp
->IoStatus
.Information
= 0;
499 Irp
->IoStatus
.Status
= ntStatus
;
500 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
505 // Submit URB_FUNCTION_RESET_PIPE
506 NTSTATUS
FreeBT_ResetPipe(IN PDEVICE_OBJECT DeviceObject
, IN USBD_PIPE_HANDLE PipeHandle
)
510 PDEVICE_EXTENSION deviceExtension
;
513 deviceExtension
= (PDEVICE_EXTENSION
) DeviceObject
->DeviceExtension
;
515 urb
= (PURB
)ExAllocatePool(NonPagedPool
, sizeof(struct _URB_PIPE_REQUEST
));
518 urb
->UrbHeader
.Length
= (USHORT
) sizeof(struct _URB_PIPE_REQUEST
);
519 urb
->UrbHeader
.Function
= URB_FUNCTION_RESET_PIPE
;
520 urb
->UrbPipeRequest
.PipeHandle
= PipeHandle
;
522 ntStatus
= CallUSBD(DeviceObject
, urb
);
529 ntStatus
= STATUS_INSUFFICIENT_RESOURCES
;
531 if(NT_SUCCESS(ntStatus
))
533 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_ResetPipe - success\n"));
534 ntStatus
= STATUS_SUCCESS
;
539 FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_ResetPipe - failed\n"));
545 // Call FreeBT_ResetParentPort to reset the device
546 NTSTATUS
FreeBT_ResetDevice(IN PDEVICE_OBJECT DeviceObject
)
551 FreeBT_DbgPrint(3, ("FreeBT_ResetDevice: Entered\n"));
553 ntStatus
= FreeBT_GetPortStatus(DeviceObject
, &portStatus
);
555 if ( (NT_SUCCESS(ntStatus
)) && (!(portStatus
& USBD_PORT_ENABLED
)) && (portStatus
& USBD_PORT_CONNECTED
))
556 ntStatus
=FreeBT_ResetParentPort(DeviceObject
);
558 FreeBT_DbgPrint(3, ("FreeBT_ResetDevice: Leaving\n"));
564 // Read port status from the lower driver (USB class driver)
565 NTSTATUS
FreeBT_GetPortStatus(IN PDEVICE_OBJECT DeviceObject
, IN OUT PULONG PortStatus
)
570 IO_STATUS_BLOCK ioStatus
;
571 PIO_STACK_LOCATION nextStack
;
572 PDEVICE_EXTENSION deviceExtension
;
574 FreeBT_DbgPrint(3, ("FreeBT_GetPortStatus: Entered\n"));
576 deviceExtension
= (PDEVICE_EXTENSION
) DeviceObject
->DeviceExtension
;
579 KeInitializeEvent(&event
, NotificationEvent
, FALSE
);
580 irp
= IoBuildDeviceIoControlRequest(
581 IOCTL_INTERNAL_USB_GET_PORT_STATUS
,
582 deviceExtension
->TopOfStackDeviceObject
,
593 FreeBT_DbgPrint(1, ("memory alloc for irp failed\n"));
594 return STATUS_INSUFFICIENT_RESOURCES
;
598 nextStack
= IoGetNextIrpStackLocation(irp
);
599 ASSERT(nextStack
!= NULL
);
600 nextStack
->Parameters
.Others
.Argument1
= PortStatus
;
602 ntStatus
= IoCallDriver(deviceExtension
->TopOfStackDeviceObject
, irp
);
603 if (STATUS_PENDING
==ntStatus
)
604 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
607 ioStatus
.Status
= ntStatus
;
609 ntStatus
= ioStatus
.Status
;
610 FreeBT_DbgPrint(3, ("FreeBT_GetPortStatus: Leaving\n"));
616 // Sends an IOCTL_INTERNAL_USB_RESET_PORT via the lower driver
617 NTSTATUS
FreeBT_ResetParentPort(IN PDEVICE_OBJECT DeviceObject
)
622 IO_STATUS_BLOCK ioStatus
;
623 PIO_STACK_LOCATION nextStack
;
624 PDEVICE_EXTENSION deviceExtension
;
626 FreeBT_DbgPrint(3, ("FreeBT_ResetParentPort: Entered\n"));
628 deviceExtension
= (PDEVICE_EXTENSION
) DeviceObject
->DeviceExtension
;
630 KeInitializeEvent(&event
, NotificationEvent
, FALSE
);
631 irp
= IoBuildDeviceIoControlRequest(
632 IOCTL_INTERNAL_USB_RESET_PORT
,
633 deviceExtension
->TopOfStackDeviceObject
,
644 FreeBT_DbgPrint(1, ("memory alloc for irp failed\n"));
645 return STATUS_INSUFFICIENT_RESOURCES
;
649 nextStack
= IoGetNextIrpStackLocation(irp
);
650 ASSERT(nextStack
!= NULL
);
652 ntStatus
= IoCallDriver(deviceExtension
->TopOfStackDeviceObject
, irp
);
653 if(STATUS_PENDING
== ntStatus
)
654 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
657 ioStatus
.Status
= ntStatus
;
660 ntStatus
= ioStatus
.Status
;
662 FreeBT_DbgPrint(3, ("FreeBT_ResetParentPort: Leaving\n"));
668 // Send an idle request to the lower driver
669 NTSTATUS
SubmitIdleRequestIrp(IN PDEVICE_EXTENSION DeviceExtension
)
674 PUSB_IDLE_CALLBACK_INFO idleCallbackInfo
;
675 PIO_STACK_LOCATION nextStack
;
677 FreeBT_DbgPrint(3, ("SubmitIdleRequest: Entered\n"));
680 idleCallbackInfo
= NULL
;
682 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
684 if(PowerDeviceD0
!= DeviceExtension
->DevPower
) {
686 ntStatus
= STATUS_POWER_STATE_INVALID
;
688 goto SubmitIdleRequestIrp_Exit
;
691 KeAcquireSpinLock(&DeviceExtension
->IdleReqStateLock
, &oldIrql
);
693 if(InterlockedExchange(&DeviceExtension
->IdleReqPend
, 1)) {
695 FreeBT_DbgPrint(1, ("Idle request pending..\n"));
697 KeReleaseSpinLock(&DeviceExtension
->IdleReqStateLock
, oldIrql
);
699 ntStatus
= STATUS_DEVICE_BUSY
;
701 goto SubmitIdleRequestIrp_Exit
;
705 // clear the NoIdleReqPendEvent because we are about
706 // to submit an idle request. Since we are so early
707 // to clear this event, make sure that if we fail this
708 // request we set back the event.
710 KeClearEvent(&DeviceExtension
->NoIdleReqPendEvent
);
712 idleCallbackInfo
= (PUSB_IDLE_CALLBACK_INFO
)ExAllocatePool(NonPagedPool
, sizeof(struct _USB_IDLE_CALLBACK_INFO
));
714 if(idleCallbackInfo
) {
716 idleCallbackInfo
->IdleCallback
= (USB_IDLE_CALLBACK
)IdleNotificationCallback
;
718 idleCallbackInfo
->IdleContext
= (PVOID
)DeviceExtension
;
720 ASSERT(DeviceExtension
->IdleCallbackInfo
== NULL
);
722 DeviceExtension
->IdleCallbackInfo
= idleCallbackInfo
;
725 // we use IoAllocateIrp to create an irp to selectively suspend the
726 // device. This irp lies pending with the hub driver. When appropriate
727 // the hub driver will invoked callback, where we power down. The completion
728 // routine is invoked when we power back.
730 irp
= IoAllocateIrp(DeviceExtension
->TopOfStackDeviceObject
->StackSize
,
735 FreeBT_DbgPrint(1, ("cannot build idle request irp\n"));
737 KeSetEvent(&DeviceExtension
->NoIdleReqPendEvent
,
741 InterlockedExchange(&DeviceExtension
->IdleReqPend
, 0);
743 KeReleaseSpinLock(&DeviceExtension
->IdleReqStateLock
, oldIrql
);
745 ExFreePool(idleCallbackInfo
);
747 ntStatus
= STATUS_INSUFFICIENT_RESOURCES
;
749 goto SubmitIdleRequestIrp_Exit
;
752 nextStack
= IoGetNextIrpStackLocation(irp
);
754 nextStack
->MajorFunction
=
755 IRP_MJ_INTERNAL_DEVICE_CONTROL
;
757 nextStack
->Parameters
.DeviceIoControl
.IoControlCode
=
758 IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION
;
760 nextStack
->Parameters
.DeviceIoControl
.Type3InputBuffer
=
763 nextStack
->Parameters
.DeviceIoControl
.InputBufferLength
=
764 sizeof(struct _USB_IDLE_CALLBACK_INFO
);
767 IoSetCompletionRoutine(irp
,
768 (PIO_COMPLETION_ROUTINE
)IdleNotificationRequestComplete
,
774 DeviceExtension
->PendingIdleIrp
= irp
;
777 // we initialize the count to 2.
778 // The reason is, if the CancelSelectSuspend routine manages
779 // to grab the irp from the device extension, then the last of the
780 // CancelSelectSuspend routine/IdleNotificationRequestComplete routine
781 // to execute will free this irp. We need to have this schema so that
782 // 1. completion routine does not attempt to touch the irp freed by
783 // CancelSelectSuspend routine.
784 // 2. CancelSelectSuspend routine doesnt wait for ever for the completion
785 // routine to complete!
787 DeviceExtension
->FreeIdleIrpCount
= 2;
789 KeReleaseSpinLock(&DeviceExtension
->IdleReqStateLock
, oldIrql
);
792 // check if the device is idle.
793 // A check here ensures that a race condition did not
794 // completely reverse the call sequence of SubmitIdleRequestIrp
795 // and CancelSelectiveSuspend
798 if(!CanDeviceSuspend(DeviceExtension
) ||
799 PowerDeviceD0
!= DeviceExtension
->DevPower
) {
802 // IRPs created using IoBuildDeviceIoControlRequest should be
803 // completed by calling IoCompleteRequest and not merely
807 FreeBT_DbgPrint(1, ("Device is not idle\n"));
809 KeAcquireSpinLock(&DeviceExtension
->IdleReqStateLock
, &oldIrql
);
811 DeviceExtension
->IdleCallbackInfo
= NULL
;
813 DeviceExtension
->PendingIdleIrp
= NULL
;
815 KeSetEvent(&DeviceExtension
->NoIdleReqPendEvent
,
819 InterlockedExchange(&DeviceExtension
->IdleReqPend
, 0);
821 KeReleaseSpinLock(&DeviceExtension
->IdleReqStateLock
, oldIrql
);
823 if(idleCallbackInfo
) {
825 ExFreePool(idleCallbackInfo
);
829 // it is still safe to touch the local variable "irp" here.
830 // the irp has not been passed down the stack, the irp has
831 // no cancellation routine. The worse position is that the
832 // CancelSelectSuspend has run after we released the spin
833 // lock above. It is still essential to free the irp.
840 ntStatus
= STATUS_UNSUCCESSFUL
;
842 goto SubmitIdleRequestIrp_Exit
;
845 FreeBT_DbgPrint(3, ("Cancel the timers\n"));
847 // Cancel the timer so that the DPCs are no longer fired.
848 // Thus, we are making judicious usage of our resources.
849 // we do not need DPCs because we already have an idle irp pending.
850 // The timers are re-initialized in the completion routine.
852 KeCancelTimer(&DeviceExtension
->Timer
);
854 ntStatus
= IoCallDriver(DeviceExtension
->TopOfStackDeviceObject
, irp
);
856 if(!NT_SUCCESS(ntStatus
)) {
858 FreeBT_DbgPrint(1, ("IoCallDriver failed\n"));
860 goto SubmitIdleRequestIrp_Exit
;
865 FreeBT_DbgPrint(1, ("Memory allocation for idleCallbackInfo failed\n"));
867 KeSetEvent(&DeviceExtension
->NoIdleReqPendEvent
,
871 InterlockedExchange(&DeviceExtension
->IdleReqPend
, 0);
873 KeReleaseSpinLock(&DeviceExtension
->IdleReqStateLock
, oldIrql
);
875 ntStatus
= STATUS_INSUFFICIENT_RESOURCES
;
878 SubmitIdleRequestIrp_Exit
:
880 FreeBT_DbgPrint(3, ("SubmitIdleRequest: Leaving\n"));
886 VOID
IdleNotificationCallback(IN PDEVICE_EXTENSION DeviceExtension
)
889 POWER_STATE powerState
;
890 KEVENT irpCompletionEvent
;
891 PIRP_COMPLETION_CONTEXT irpContext
;
893 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationCallback: Entered\n"));
896 // Dont idle, if the device was just disconnected or being stopped
897 // i.e. return for the following DeviceState(s)
898 // NotStarted, Stopped, PendingStop, PendingRemove, SurpriseRemoved, Removed
901 if(DeviceExtension
->DeviceState
!= Working
) {
907 // If there is not already a WW IRP pending, submit one now
909 if(DeviceExtension
->WaitWakeEnable
) {
911 IssueWaitWake(DeviceExtension
);
916 // power down the device
919 irpContext
= (PIRP_COMPLETION_CONTEXT
)
920 ExAllocatePool(NonPagedPool
,
921 sizeof(IRP_COMPLETION_CONTEXT
));
925 FreeBT_DbgPrint(1, ("FBTUSB: IdleNotificationCallback: Failed to alloc memory for irpContext\n"));
926 ntStatus
= STATUS_INSUFFICIENT_RESOURCES
;
931 // increment the count. In the HoldIoRequestWorkerRoutine, the
932 // count is decremented twice (one for the system Irp and the
933 // other for the device Irp. An increment here compensates for
934 // the sytem irp..The decrement corresponding to this increment
935 // is in the completion function
938 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationCallback::"));
939 FreeBT_IoIncrement(DeviceExtension
);
941 powerState
.DeviceState
= (DEVICE_POWER_STATE
) DeviceExtension
->PowerDownLevel
;
943 KeInitializeEvent(&irpCompletionEvent
, NotificationEvent
, FALSE
);
945 irpContext
->DeviceExtension
= DeviceExtension
;
946 irpContext
->Event
= &irpCompletionEvent
;
948 ntStatus
= PoRequestPowerIrp(
949 DeviceExtension
->PhysicalDeviceObject
,
952 (PREQUEST_POWER_COMPLETE
) PoIrpCompletionFunc
,
956 if(STATUS_PENDING
== ntStatus
) {
958 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationCallback::"
959 "waiting for the power irp to complete\n"));
961 KeWaitForSingleObject(&irpCompletionEvent
,
969 if(!NT_SUCCESS(ntStatus
)) {
973 ExFreePool(irpContext
);
977 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationCallback: Leaving\n"));
981 NTSTATUS
IdleNotificationRequestComplete(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
, IN PDEVICE_EXTENSION DeviceExtension
)
984 POWER_STATE powerState
;
986 LARGE_INTEGER dueTime
;
988 PUSB_IDLE_CALLBACK_INFO idleCallbackInfo
;
990 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationRequestCompete: Entered\n"));
994 ntStatus
= Irp
->IoStatus
.Status
;
995 if(!NT_SUCCESS(ntStatus
) && ntStatus
!= STATUS_NOT_SUPPORTED
)
997 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationRequestCompete: Idle irp completes with error::"));
1000 case STATUS_INVALID_DEVICE_REQUEST
:
1001 FreeBT_DbgPrint(3, ("STATUS_INVALID_DEVICE_REQUEST\n"));
1004 case STATUS_CANCELLED
:
1005 FreeBT_DbgPrint(3, ("STATUS_CANCELLED\n"));
1008 case STATUS_DEVICE_BUSY
:
1009 FreeBT_DbgPrint(3, ("STATUS_DEVICE_BUSY\n"));
1012 case STATUS_POWER_STATE_INVALID
:
1013 FreeBT_DbgPrint(3, ("STATUS_POWER_STATE_INVALID\n"));
1014 goto IdleNotificationRequestComplete_Exit
;
1017 FreeBT_DbgPrint(3, ("default: status = %X\n", ntStatus
));
1022 // if in error, issue a SetD0
1023 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationRequestComplete::"));
1024 FreeBT_IoIncrement(DeviceExtension
);
1026 powerState
.DeviceState
= PowerDeviceD0
;
1027 ntStatus
= PoRequestPowerIrp(
1028 DeviceExtension
->PhysicalDeviceObject
,
1031 (PREQUEST_POWER_COMPLETE
) PoIrpAsyncCompletionFunc
,
1035 if(!NT_SUCCESS(ntStatus
))
1036 FreeBT_DbgPrint(1, ("PoRequestPowerIrp failed\n"));
1040 IdleNotificationRequestComplete_Exit
:
1041 KeAcquireSpinLock(&DeviceExtension
->IdleReqStateLock
, &oldIrql
);
1042 idleCallbackInfo
= DeviceExtension
->IdleCallbackInfo
;
1043 DeviceExtension
->IdleCallbackInfo
= NULL
;
1045 idleIrp
= (PIRP
) InterlockedExchangePointer(&DeviceExtension
->PendingIdleIrp
, NULL
);
1046 InterlockedExchange(&DeviceExtension
->IdleReqPend
, 0);
1048 KeReleaseSpinLock(&DeviceExtension
->IdleReqStateLock
, oldIrql
);
1050 if(idleCallbackInfo
)
1051 ExFreePool(idleCallbackInfo
);
1053 // Since the irp was created using IoAllocateIrp,
1054 // the Irp needs to be freed using IoFreeIrp.
1055 // Also return STATUS_MORE_PROCESSING_REQUIRED so that
1056 // the kernel does not reference this in the near future.
1059 FreeBT_DbgPrint(3, ("completion routine has a valid irp and frees it\n"));
1061 KeSetEvent(&DeviceExtension
->NoIdleReqPendEvent
, IO_NO_INCREMENT
, FALSE
);
1067 // The CancelSelectiveSuspend routine has grabbed the Irp from the device
1068 // extension. Now the last one to decrement the FreeIdleIrpCount should
1070 if (0 == InterlockedDecrement(&DeviceExtension
->FreeIdleIrpCount
))
1072 FreeBT_DbgPrint(3, ("completion routine frees the irp\n"));
1074 KeSetEvent(&DeviceExtension
->NoIdleReqPendEvent
, IO_NO_INCREMENT
, FALSE
);
1080 if(DeviceExtension
->SSEnable
)
1082 FreeBT_DbgPrint(3, ("Set the timer to fire DPCs\n"));
1083 dueTime
.QuadPart
= -10000 * IDLE_INTERVAL
; // 5000 ms
1084 KeSetTimerEx(&DeviceExtension
->Timer
, dueTime
, IDLE_INTERVAL
, &DeviceExtension
->DeferredProcCall
);
1085 FreeBT_DbgPrint(3, ("IdleNotificationRequestCompete: Leaving\n"));
1089 return STATUS_MORE_PROCESSING_REQUIRED
;
1093 VOID
CancelSelectSuspend(IN PDEVICE_EXTENSION DeviceExtension
)
1098 FreeBT_DbgPrint(3, ("CancelSelectSuspend: Entered\n"));
1102 KeAcquireSpinLock(&DeviceExtension
->IdleReqStateLock
, &oldIrql
);
1104 if(!CanDeviceSuspend(DeviceExtension
))
1106 FreeBT_DbgPrint(3, ("Device is not idle\n"));
1107 irp
= (PIRP
) InterlockedExchangePointer(&DeviceExtension
->PendingIdleIrp
, NULL
);
1111 KeReleaseSpinLock(&DeviceExtension
->IdleReqStateLock
, oldIrql
);
1113 // since we have a valid Irp ptr,
1114 // we can call IoCancelIrp on it,
1115 // without the fear of the irp
1116 // being freed underneath us.
1119 // This routine has the irp pointer.
1120 // It is safe to call IoCancelIrp because we know that
1121 // the compleiton routine will not free this irp unless...
1124 if(IoCancelIrp(irp
))
1126 FreeBT_DbgPrint(3, ("IoCancelIrp returns TRUE\n"));
1132 FreeBT_DbgPrint(3, ("IoCancelIrp returns FALSE\n"));
1136 // ....we decrement the FreeIdleIrpCount from 2 to 1.
1137 // if completion routine runs ahead of us, then this routine
1138 // decrements the FreeIdleIrpCount from 1 to 0 and hence shall
1140 if(0 == InterlockedDecrement(&DeviceExtension
->FreeIdleIrpCount
))
1142 FreeBT_DbgPrint(3, ("CancelSelectSuspend frees the irp\n"));
1144 KeSetEvent(&DeviceExtension
->NoIdleReqPendEvent
, IO_NO_INCREMENT
, FALSE
);
1150 FreeBT_DbgPrint(3, ("CancelSelectSuspend: Leaving\n"));
1156 VOID
PoIrpCompletionFunc(IN PDEVICE_OBJECT DeviceObject
, IN UCHAR MinorFunction
, IN POWER_STATE PowerState
, IN PVOID Context
, IN PIO_STATUS_BLOCK IoStatus
)
1158 PIRP_COMPLETION_CONTEXT irpContext
;
1161 FreeBT_DbgPrint(3, ("PoIrpCompletionFunc::"));
1164 irpContext
= (PIRP_COMPLETION_CONTEXT
) Context
;
1166 // all we do is set the event and decrement the count
1169 KeSetEvent(irpContext
->Event
, 0, FALSE
);
1170 FreeBT_IoDecrement(irpContext
->DeviceExtension
);
1171 ExFreePool(irpContext
);
1179 VOID
PoIrpAsyncCompletionFunc(IN PDEVICE_OBJECT DeviceObject
, IN UCHAR MinorFunction
, IN POWER_STATE PowerState
, IN PVOID Context
, IN PIO_STATUS_BLOCK IoStatus
)
1181 PDEVICE_EXTENSION DeviceExtension
= (PDEVICE_EXTENSION
) Context
;
1182 FreeBT_DbgPrint(3, ("PoIrpAsyncCompletionFunc::"));
1183 FreeBT_IoDecrement(DeviceExtension
);
1189 VOID
WWIrpCompletionFunc(IN PDEVICE_OBJECT DeviceObject
, IN UCHAR MinorFunction
, IN POWER_STATE PowerState
, IN PVOID Context
, IN PIO_STATUS_BLOCK IoStatus
)
1191 PDEVICE_EXTENSION DeviceExtension
= (PDEVICE_EXTENSION
) Context
;
1193 FreeBT_DbgPrint(3, ("WWIrpCompletionFunc::"));
1194 FreeBT_IoDecrement(DeviceExtension
);