Sync with trunk r58687.
[reactos.git] / drivers / bluetooth / fbtusb / fbtdev.c
1 // Copyright (c) 2004, Antony C. Roberts
2
3 // Use of this file is subject to the terms
4 // described in the LICENSE.TXT file that
5 // accompanies this file.
6 //
7 // Your use of this file indicates your
8 // acceptance of the terms described in
9 // LICENSE.TXT.
10 //
11 // http://www.freebt.net
12
13 #include "fbtusb.h"
14 #include "fbtpnp.h"
15 #include "fbtpwr.h"
16 #include "fbtdev.h"
17 #include "fbtwmi.h"
18 #include "fbtrwr.h"
19
20 #include "fbtusr.h"
21
22 // Dispatch routine for CreateHandle
23 NTSTATUS NTAPI FreeBT_DispatchCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
24 {
25 //ULONG i;
26 NTSTATUS ntStatus;
27 PFILE_OBJECT fileObject;
28 PDEVICE_EXTENSION deviceExtension;
29 PIO_STACK_LOCATION irpStack;
30 //PFREEBT_PIPE_CONTEXT pipeContext;
31 PUSBD_INTERFACE_INFORMATION interface;
32
33 PAGED_CODE();
34
35 FreeBT_DbgPrint(3, ("FreeBT_DispatchCreate: Entered\n"));
36
37 irpStack = IoGetCurrentIrpStackLocation(Irp);
38 fileObject = irpStack->FileObject;
39 deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
40
41 if (deviceExtension->DeviceState != Working)
42 {
43 ntStatus = STATUS_INVALID_DEVICE_STATE;
44 goto FreeBT_DispatchCreate_Exit;
45
46 }
47
48 if (deviceExtension->UsbInterface)
49 {
50 interface = deviceExtension->UsbInterface;
51
52 }
53
54 else
55 {
56 FreeBT_DbgPrint(1, ("UsbInterface not found\n"));
57 ntStatus = STATUS_INVALID_DEVICE_STATE;
58 goto FreeBT_DispatchCreate_Exit;
59
60 }
61
62 if (fileObject)
63 {
64 fileObject->FsContext = NULL;
65 }
66
67 else
68 {
69 ntStatus = STATUS_INVALID_PARAMETER;
70 goto FreeBT_DispatchCreate_Exit;
71
72 }
73
74 if (deviceExtension->OpenHandleCount>0)
75 {
76 ntStatus = STATUS_ACCESS_VIOLATION;
77 goto FreeBT_DispatchCreate_Exit;
78
79 }
80
81 // opening a device as opposed to pipe.
82 ntStatus = STATUS_SUCCESS;
83
84 InterlockedIncrement(&deviceExtension->OpenHandleCount);
85
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);
90
91 FreeBT_DispatchCreate_Exit:
92 Irp->IoStatus.Status = ntStatus;
93 Irp->IoStatus.Information = 0;
94 IoCompleteRequest(Irp, IO_NO_INCREMENT);
95
96 FreeBT_DbgPrint(3, ("FreeBT_DispatchCreate: Leaving\n"));
97 return ntStatus;
98
99 }
100
101 // Dispatch routine for CloseHandle
102 NTSTATUS NTAPI FreeBT_DispatchClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
103 {
104 NTSTATUS ntStatus;
105 PFILE_OBJECT fileObject;
106 PDEVICE_EXTENSION deviceExtension;
107 PIO_STACK_LOCATION irpStack;
108 //PFREEBT_PIPE_CONTEXT pipeContext;
109 //PUSBD_PIPE_INFORMATION pipeInformation;
110
111 PAGED_CODE();
112
113 irpStack = IoGetCurrentIrpStackLocation(Irp);
114 fileObject = irpStack->FileObject;
115 //pipeContext = NULL;
116 //pipeInformation = NULL;
117 deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
118
119 FreeBT_DbgPrint(3, ("FreeBT_DispatchClose: Entered\n"));
120
121 ntStatus = STATUS_SUCCESS;
122 Irp->IoStatus.Status = ntStatus;
123 Irp->IoStatus.Information = 0;
124 IoCompleteRequest(Irp, IO_NO_INCREMENT);
125
126 InterlockedDecrement(&deviceExtension->OpenHandleCount);
127
128 FreeBT_DbgPrint(3, ("FreeBT_DispatchClose: Leaving\n"));
129
130 return ntStatus;
131
132 }
133
134 // Called when a HCI Send on the control pipe completes
135 NTSTATUS NTAPI FreeBT_HCISendCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
136 {
137 //ULONG stageLength;
138 NTSTATUS ntStatus;
139
140 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_HCISendCompletion, status=0x%08X\n", Irp->IoStatus.Status));
141
142 if (Irp->PendingReturned)
143 IoMarkIrpPending(Irp);
144
145 ExFreePool(Context);
146 FreeBT_IoDecrement(DeviceObject->DeviceExtension);
147 ntStatus = Irp->IoStatus.Status;
148 Irp->IoStatus.Information = 0;
149
150 return ntStatus;
151
152 }
153
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 NTAPI FreeBT_SendHCICommand(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID IoBuffer, IN ULONG InputBufferLength)
157 {
158 PDEVICE_EXTENSION deviceExtension;
159 //ULONG urbFlags;
160 //ULONG stageLength;
161 //PVOID pBuffer;
162 PURB urb;
163 NTSTATUS ntStatus;
164 PIO_STACK_LOCATION nextStack;
165 //PFBT_HCI_CMD_HEADER pHCICommand;
166 //LARGE_INTEGER delay;
167
168 deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
169 if (!deviceExtension)
170 {
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);
176 return ntStatus;
177
178 }
179
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)
184 {
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);
190
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,
195 Executive,
196 UserMode,
197 FALSE,
198 &delay);
199
200 FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_SendHCICommand: Finished sleeping\n"));
201
202
203 }*/
204
205 // Create the URB
206 urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
207 if(urb == NULL)
208 {
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);
214 return ntStatus;
215
216 }
217
218 UsbBuildVendorRequest(
219 urb,
220 URB_FUNCTION_CLASS_DEVICE, // This works, for CSR and Silicon Wave
221 sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST),
222 0,
223 0,
224 0,
225 0,
226 0,
227 IoBuffer,
228 NULL,
229 InputBufferLength,
230 NULL);
231
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;
237
238 IoSetCompletionRoutine(
239 Irp,
240 (PIO_COMPLETION_ROUTINE)FreeBT_HCISendCompletion,
241 urb,
242 TRUE,
243 TRUE,
244 TRUE);
245
246 // We return STATUS_PENDING; call IoMarkIrpPending.
247 IoMarkIrpPending(Irp);
248
249 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand::"));
250 FreeBT_IoIncrement(deviceExtension);
251
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))
255 {
256 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand: IoCallDriver fails with status %X\n", ntStatus));
257
258 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand::"));
259 FreeBT_IoDecrement(deviceExtension);
260
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);
265
266 else
267 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand: ntStatus is STATUS_CANCELLED or STATUS_DEVICE_NOT_CONNECTED\n"));
268
269 Irp->IoStatus.Status = ntStatus;
270 Irp->IoStatus.Information = 0;
271 IoCompleteRequest(Irp, IO_NO_INCREMENT);
272
273 return ntStatus;
274
275 }
276
277 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand: Completed successfully\n"));
278
279 return STATUS_PENDING;
280
281 }
282
283 // Called when a HCI Get on the event pipe completes
284 NTSTATUS NTAPI FreeBT_HCIEventCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
285 {
286 //ULONG stageLength;
287 NTSTATUS ntStatus;
288 PIO_STACK_LOCATION nextStack;
289 PURB urb;
290
291 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_HCIEventCompletion, status=0x%08X\n", Irp->IoStatus.Status));
292
293 if (Irp->PendingReturned)
294 IoMarkIrpPending(Irp);
295
296 // initialize variables
297 urb=(PURB)Context;
298 ntStatus = Irp->IoStatus.Status;
299 Irp->IoStatus.Information = urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
300 nextStack = IoGetNextIrpStackLocation(Irp);
301
302 ExFreePool(Context);
303 FreeBT_IoDecrement(DeviceObject->DeviceExtension);
304
305 return ntStatus;
306
307 }
308
309 // Called from the DeviceIOControl handler to wait for an event on the interrupt pipe
310 NTSTATUS NTAPI FreeBT_GetHCIEvent(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID IoBuffer, IN ULONG InputBufferLength)
311 {
312 PDEVICE_EXTENSION deviceExtension;
313 PURB urb;
314 NTSTATUS ntStatus;
315 PIO_STACK_LOCATION nextStack;
316
317 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: Entered\n"));
318
319 urb = NULL;
320 deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
321
322 urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
323 if (urb==NULL)
324 {
325 FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_GetHCIEvent: Failed to alloc mem for urb\n"));
326 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
327 goto FreeBT_GetHCIEvent_Exit;
328
329 }
330
331 UsbBuildInterruptOrBulkTransferRequest(
332 urb,
333 sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
334 deviceExtension->EventPipe.PipeHandle,
335 IoBuffer,
336 NULL,
337 InputBufferLength,
338 USBD_SHORT_TRANSFER_OK|USBD_TRANSFER_DIRECTION_IN,
339 NULL);
340
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;
347
348 IoSetCompletionRoutine(
349 Irp,
350 (PIO_COMPLETION_ROUTINE)FreeBT_HCIEventCompletion,
351 urb,
352 TRUE,
353 TRUE,
354 TRUE);
355
356 // We return STATUS_PENDING; call IoMarkIrpPending.
357 IoMarkIrpPending(Irp);
358
359 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent::"));
360 FreeBT_IoIncrement(deviceExtension);
361
362 ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
363 if (!NT_SUCCESS(ntStatus))
364 {
365 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: IoCallDriver fails with status %X\n", ntStatus));
366
367 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent::"));
368 FreeBT_IoDecrement(deviceExtension);
369
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))
373 {
374 ntStatus = FreeBT_ResetPipe(DeviceObject, deviceExtension->EventPipe.PipeHandle);
375 if(!NT_SUCCESS(ntStatus))
376 {
377 FreeBT_DbgPrint(1, ("FreeBT_ResetPipe failed\n"));
378 ntStatus = FreeBT_ResetDevice(DeviceObject);
379
380 }
381
382 }
383
384 else
385 {
386 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: ntStatus is STATUS_CANCELLED or STATUS_DEVICE_NOT_CONNECTED\n"));
387
388 }
389
390 goto FreeBT_GetHCIEvent_Exit;
391
392 }
393
394 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: Leaving\n"));
395
396 // Return STATUS_PENDING, when the lower driver completes the request,
397 // the FreeBT_HCIEventCompletion completion routine.
398 return STATUS_PENDING;
399
400 FreeBT_GetHCIEvent_Exit:
401 Irp->IoStatus.Status=ntStatus;
402 Irp->IoStatus.Information=0;
403
404 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: Failure (0x%08x), completing IRP\n", ntStatus));
405 IoCompleteRequest(Irp, IO_NO_INCREMENT);
406
407 return ntStatus;
408
409 }
410
411 // DeviceIOControl dispatch
412 NTSTATUS NTAPI FreeBT_DispatchDevCtrl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
413 {
414 ULONG code;
415 PVOID ioBuffer;
416 ULONG inputBufferLength;
417 ULONG outputBufferLength;
418 ULONG info;
419 NTSTATUS ntStatus;
420 PDEVICE_EXTENSION deviceExtension;
421 PIO_STACK_LOCATION irpStack;
422
423 info = 0;
424 irpStack = IoGetCurrentIrpStackLocation(Irp);
425 code = irpStack->Parameters.DeviceIoControl.IoControlCode;
426 deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
427
428 ioBuffer = Irp->AssociatedIrp.SystemBuffer;
429 inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
430 outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
431
432 if (deviceExtension->DeviceState != Working)
433 {
434 FreeBT_DbgPrint(1, ("FBTUSB: Invalid device state\n"));
435 ntStatus = STATUS_INVALID_DEVICE_STATE;
436 goto FreeBT_DispatchDevCtrlExit;
437
438 }
439
440 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchDevCtrl::"));
441
442 // Make sure that any selective suspend request has been completed.
443 if (deviceExtension->SSEnable)
444 {
445 FreeBT_DbgPrint(3, ("Waiting on the IdleReqPendEvent\n"));
446 KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,
447 Executive,
448 KernelMode,
449 FALSE,
450 NULL);
451
452 }
453
454 switch(code)
455 {
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)
459 {
460 ntStatus = STATUS_BUFFER_TOO_SMALL;
461 FreeBT_DbgPrint(3, ("FBTUSB: IOCTL_FREEBT_HCI_SEND_CMD: Buffer too small\n"));
462 goto FreeBT_DispatchDevCtrlExit;
463
464 }
465
466 if (inputBufferLength>FBT_HCI_CMD_MAX_SIZE)
467 {
468 ntStatus = STATUS_INVALID_BUFFER_SIZE;
469 FreeBT_DbgPrint(3, ("FBTUSB: IOCTL_FREEBT_HCI_SEND_CMD: Buffer too long\n"));
470 goto FreeBT_DispatchDevCtrlExit;
471
472 }
473
474 return FreeBT_SendHCICommand(DeviceObject, Irp, ioBuffer, inputBufferLength);
475 break;
476
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)
480 {
481 ntStatus = STATUS_BUFFER_TOO_SMALL;
482 FreeBT_DbgPrint(3, ("FBTUSB: IOCTL_FREEBT_HCI_GET_EVENT: Buffer too small\n"));
483 goto FreeBT_DispatchDevCtrlExit;
484
485 }
486
487 return FreeBT_GetHCIEvent(DeviceObject, Irp, ioBuffer, outputBufferLength);
488 break;
489
490 default:
491 FreeBT_DbgPrint(3, ("FBTUSB: Invalid IOCTL 0x%08x received\n", code));
492 ntStatus = STATUS_INVALID_DEVICE_REQUEST;
493 break;
494
495 }
496
497 FreeBT_DispatchDevCtrlExit:
498 Irp->IoStatus.Information = 0;
499 Irp->IoStatus.Status = ntStatus;
500 IoCompleteRequest(Irp, IO_NO_INCREMENT);
501
502 return ntStatus;
503 }
504
505 // Submit URB_FUNCTION_RESET_PIPE
506 NTSTATUS NTAPI FreeBT_ResetPipe(IN PDEVICE_OBJECT DeviceObject, IN USBD_PIPE_HANDLE PipeHandle)
507 {
508 PURB urb;
509 NTSTATUS ntStatus;
510 PDEVICE_EXTENSION deviceExtension;
511
512 urb = NULL;
513 deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
514
515 urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST));
516 if (urb)
517 {
518 urb->UrbHeader.Length = (USHORT) sizeof(struct _URB_PIPE_REQUEST);
519 urb->UrbHeader.Function = URB_FUNCTION_RESET_PIPE;
520 urb->UrbPipeRequest.PipeHandle = PipeHandle;
521
522 ntStatus = CallUSBD(DeviceObject, urb);
523
524 ExFreePool(urb);
525
526 }
527
528 else
529 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
530
531 if(NT_SUCCESS(ntStatus))
532 {
533 FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_ResetPipe - success\n"));
534 ntStatus = STATUS_SUCCESS;
535
536 }
537
538 else
539 FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_ResetPipe - failed\n"));
540
541 return ntStatus;
542
543 }
544
545 // Call FreeBT_ResetParentPort to reset the device
546 NTSTATUS NTAPI FreeBT_ResetDevice(IN PDEVICE_OBJECT DeviceObject)
547 {
548 NTSTATUS ntStatus;
549 ULONG portStatus;
550
551 FreeBT_DbgPrint(3, ("FreeBT_ResetDevice: Entered\n"));
552
553 ntStatus = FreeBT_GetPortStatus(DeviceObject, &portStatus);
554
555 if ( (NT_SUCCESS(ntStatus)) && (!(portStatus & USBD_PORT_ENABLED)) && (portStatus & USBD_PORT_CONNECTED))
556 ntStatus=FreeBT_ResetParentPort(DeviceObject);
557
558 FreeBT_DbgPrint(3, ("FreeBT_ResetDevice: Leaving\n"));
559
560 return ntStatus;
561
562 }
563
564 // Read port status from the lower driver (USB class driver)
565 NTSTATUS NTAPI FreeBT_GetPortStatus(IN PDEVICE_OBJECT DeviceObject, IN OUT PULONG PortStatus)
566 {
567 NTSTATUS ntStatus;
568 KEVENT event;
569 PIRP irp;
570 IO_STATUS_BLOCK ioStatus;
571 PIO_STACK_LOCATION nextStack;
572 PDEVICE_EXTENSION deviceExtension;
573
574 FreeBT_DbgPrint(3, ("FreeBT_GetPortStatus: Entered\n"));
575
576 deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
577 *PortStatus = 0;
578
579 KeInitializeEvent(&event, NotificationEvent, FALSE);
580 irp = IoBuildDeviceIoControlRequest(
581 IOCTL_INTERNAL_USB_GET_PORT_STATUS,
582 deviceExtension->TopOfStackDeviceObject,
583 NULL,
584 0,
585 NULL,
586 0,
587 TRUE,
588 &event,
589 &ioStatus);
590
591 if (NULL == irp)
592 {
593 FreeBT_DbgPrint(1, ("memory alloc for irp failed\n"));
594 return STATUS_INSUFFICIENT_RESOURCES;
595
596 }
597
598 nextStack = IoGetNextIrpStackLocation(irp);
599 ASSERT(nextStack != NULL);
600 nextStack->Parameters.Others.Argument1 = PortStatus;
601
602 ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, irp);
603 if (STATUS_PENDING==ntStatus)
604 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
605
606 else
607 ioStatus.Status = ntStatus;
608
609 ntStatus = ioStatus.Status;
610 FreeBT_DbgPrint(3, ("FreeBT_GetPortStatus: Leaving\n"));
611
612 return ntStatus;
613
614 }
615
616 // Sends an IOCTL_INTERNAL_USB_RESET_PORT via the lower driver
617 NTSTATUS NTAPI FreeBT_ResetParentPort(IN PDEVICE_OBJECT DeviceObject)
618 {
619 NTSTATUS ntStatus;
620 KEVENT event;
621 PIRP irp;
622 IO_STATUS_BLOCK ioStatus;
623 PIO_STACK_LOCATION nextStack;
624 PDEVICE_EXTENSION deviceExtension;
625
626 FreeBT_DbgPrint(3, ("FreeBT_ResetParentPort: Entered\n"));
627
628 deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
629
630 KeInitializeEvent(&event, NotificationEvent, FALSE);
631 irp = IoBuildDeviceIoControlRequest(
632 IOCTL_INTERNAL_USB_RESET_PORT,
633 deviceExtension->TopOfStackDeviceObject,
634 NULL,
635 0,
636 NULL,
637 0,
638 TRUE,
639 &event,
640 &ioStatus);
641
642 if (NULL == irp)
643 {
644 FreeBT_DbgPrint(1, ("memory alloc for irp failed\n"));
645 return STATUS_INSUFFICIENT_RESOURCES;
646
647 }
648
649 nextStack = IoGetNextIrpStackLocation(irp);
650 ASSERT(nextStack != NULL);
651
652 ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, irp);
653 if(STATUS_PENDING == ntStatus)
654 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
655
656 else
657 ioStatus.Status = ntStatus;
658
659
660 ntStatus = ioStatus.Status;
661
662 FreeBT_DbgPrint(3, ("FreeBT_ResetParentPort: Leaving\n"));
663
664 return ntStatus;
665
666 }
667
668 // Send an idle request to the lower driver
669 NTSTATUS NTAPI SubmitIdleRequestIrp(IN PDEVICE_EXTENSION DeviceExtension)
670 {
671 PIRP irp;
672 NTSTATUS ntStatus;
673 KIRQL oldIrql;
674 PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;
675 PIO_STACK_LOCATION nextStack;
676
677 FreeBT_DbgPrint(3, ("SubmitIdleRequest: Entered\n"));
678
679 irp = NULL;
680 idleCallbackInfo = NULL;
681
682 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
683
684 if(PowerDeviceD0 != DeviceExtension->DevPower) {
685
686 ntStatus = STATUS_POWER_STATE_INVALID;
687
688 goto SubmitIdleRequestIrp_Exit;
689 }
690
691 KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql);
692
693 if(InterlockedExchange(&DeviceExtension->IdleReqPend, 1)) {
694
695 FreeBT_DbgPrint(1, ("Idle request pending..\n"));
696
697 KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
698
699 ntStatus = STATUS_DEVICE_BUSY;
700
701 goto SubmitIdleRequestIrp_Exit;
702 }
703
704 //
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.
709 //
710 KeClearEvent(&DeviceExtension->NoIdleReqPendEvent);
711
712 idleCallbackInfo = (PUSB_IDLE_CALLBACK_INFO)ExAllocatePool(NonPagedPool, sizeof(struct _USB_IDLE_CALLBACK_INFO));
713
714 if(idleCallbackInfo) {
715
716 idleCallbackInfo->IdleCallback = (USB_IDLE_CALLBACK)IdleNotificationCallback;
717
718 idleCallbackInfo->IdleContext = (PVOID)DeviceExtension;
719
720 ASSERT(DeviceExtension->IdleCallbackInfo == NULL);
721
722 DeviceExtension->IdleCallbackInfo = idleCallbackInfo;
723
724 //
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.
729 //
730 irp = IoAllocateIrp(DeviceExtension->TopOfStackDeviceObject->StackSize,
731 FALSE);
732
733 if(irp == NULL) {
734
735 FreeBT_DbgPrint(1, ("cannot build idle request irp\n"));
736
737 KeSetEvent(&DeviceExtension->NoIdleReqPendEvent,
738 IO_NO_INCREMENT,
739 FALSE);
740
741 InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
742
743 KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
744
745 ExFreePool(idleCallbackInfo);
746
747 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
748
749 goto SubmitIdleRequestIrp_Exit;
750 }
751
752 nextStack = IoGetNextIrpStackLocation(irp);
753
754 nextStack->MajorFunction =
755 IRP_MJ_INTERNAL_DEVICE_CONTROL;
756
757 nextStack->Parameters.DeviceIoControl.IoControlCode =
758 IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION;
759
760 nextStack->Parameters.DeviceIoControl.Type3InputBuffer =
761 idleCallbackInfo;
762
763 nextStack->Parameters.DeviceIoControl.InputBufferLength =
764 sizeof(struct _USB_IDLE_CALLBACK_INFO);
765
766
767 IoSetCompletionRoutine(irp,
768 (PIO_COMPLETION_ROUTINE)IdleNotificationRequestComplete,
769 DeviceExtension,
770 TRUE,
771 TRUE,
772 TRUE);
773
774 DeviceExtension->PendingIdleIrp = irp;
775
776 //
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!
786 //
787 DeviceExtension->FreeIdleIrpCount = 2;
788
789 KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
790
791 //
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
796 //
797
798 if(!CanDeviceSuspend(DeviceExtension) ||
799 PowerDeviceD0 != DeviceExtension->DevPower) {
800
801 //
802 // IRPs created using IoBuildDeviceIoControlRequest should be
803 // completed by calling IoCompleteRequest and not merely
804 // deallocated.
805 //
806
807 FreeBT_DbgPrint(1, ("Device is not idle\n"));
808
809 KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql);
810
811 DeviceExtension->IdleCallbackInfo = NULL;
812
813 DeviceExtension->PendingIdleIrp = NULL;
814
815 KeSetEvent(&DeviceExtension->NoIdleReqPendEvent,
816 IO_NO_INCREMENT,
817 FALSE);
818
819 InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
820
821 KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
822
823 if(idleCallbackInfo) {
824
825 ExFreePool(idleCallbackInfo);
826 }
827
828 //
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.
834 //
835 if(irp) {
836
837 IoFreeIrp(irp);
838 }
839
840 ntStatus = STATUS_UNSUCCESSFUL;
841
842 goto SubmitIdleRequestIrp_Exit;
843 }
844
845 FreeBT_DbgPrint(3, ("Cancel the timers\n"));
846 //
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.
851 //
852 KeCancelTimer(&DeviceExtension->Timer);
853
854 ntStatus = IoCallDriver(DeviceExtension->TopOfStackDeviceObject, irp);
855
856 if(!NT_SUCCESS(ntStatus)) {
857
858 FreeBT_DbgPrint(1, ("IoCallDriver failed\n"));
859
860 goto SubmitIdleRequestIrp_Exit;
861 }
862 }
863 else {
864
865 FreeBT_DbgPrint(1, ("Memory allocation for idleCallbackInfo failed\n"));
866
867 KeSetEvent(&DeviceExtension->NoIdleReqPendEvent,
868 IO_NO_INCREMENT,
869 FALSE);
870
871 InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
872
873 KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
874
875 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
876 }
877
878 SubmitIdleRequestIrp_Exit:
879
880 FreeBT_DbgPrint(3, ("SubmitIdleRequest: Leaving\n"));
881
882 return ntStatus;
883 }
884
885
886 VOID NTAPI IdleNotificationCallback(IN PDEVICE_EXTENSION DeviceExtension)
887 {
888 NTSTATUS ntStatus;
889 POWER_STATE powerState;
890 KEVENT irpCompletionEvent;
891 PIRP_COMPLETION_CONTEXT irpContext;
892
893 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationCallback: Entered\n"));
894
895 //
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
899 //
900
901 if(DeviceExtension->DeviceState != Working) {
902
903 return;
904 }
905
906 //
907 // If there is not already a WW IRP pending, submit one now
908 //
909 if(DeviceExtension->WaitWakeEnable) {
910
911 IssueWaitWake(DeviceExtension);
912 }
913
914
915 //
916 // power down the device
917 //
918
919 irpContext = (PIRP_COMPLETION_CONTEXT)
920 ExAllocatePool(NonPagedPool,
921 sizeof(IRP_COMPLETION_CONTEXT));
922
923 if(!irpContext) {
924
925 FreeBT_DbgPrint(1, ("FBTUSB: IdleNotificationCallback: Failed to alloc memory for irpContext\n"));
926 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
927 }
928 else {
929
930 //
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
936 //
937
938 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationCallback::"));
939 FreeBT_IoIncrement(DeviceExtension);
940
941 powerState.DeviceState = (DEVICE_POWER_STATE) DeviceExtension->PowerDownLevel;
942
943 KeInitializeEvent(&irpCompletionEvent, NotificationEvent, FALSE);
944
945 irpContext->DeviceExtension = DeviceExtension;
946 irpContext->Event = &irpCompletionEvent;
947
948 ntStatus = PoRequestPowerIrp(
949 DeviceExtension->PhysicalDeviceObject,
950 IRP_MN_SET_POWER,
951 powerState,
952 (PREQUEST_POWER_COMPLETE) PoIrpCompletionFunc,
953 irpContext,
954 NULL);
955
956 if(STATUS_PENDING == ntStatus) {
957
958 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationCallback::"
959 "waiting for the power irp to complete\n"));
960
961 KeWaitForSingleObject(&irpCompletionEvent,
962 Executive,
963 KernelMode,
964 FALSE,
965 NULL);
966 }
967 }
968
969 if(!NT_SUCCESS(ntStatus)) {
970
971 if(irpContext) {
972
973 ExFreePool(irpContext);
974 }
975 }
976
977 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationCallback: Leaving\n"));
978 }
979
980
981 NTSTATUS NTAPI IdleNotificationRequestComplete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PDEVICE_EXTENSION DeviceExtension)
982 {
983 NTSTATUS ntStatus;
984 POWER_STATE powerState;
985 KIRQL oldIrql;
986 LARGE_INTEGER dueTime;
987 PIRP idleIrp;
988 PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;
989
990 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationRequestCompete: Entered\n"));
991
992 idleIrp = NULL;
993
994 ntStatus = Irp->IoStatus.Status;
995 if(!NT_SUCCESS(ntStatus) && ntStatus != STATUS_NOT_SUPPORTED)
996 {
997 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationRequestCompete: Idle irp completes with error::"));
998 switch(ntStatus)
999 {
1000 case STATUS_INVALID_DEVICE_REQUEST:
1001 FreeBT_DbgPrint(3, ("STATUS_INVALID_DEVICE_REQUEST\n"));
1002 break;
1003
1004 case STATUS_CANCELLED:
1005 FreeBT_DbgPrint(3, ("STATUS_CANCELLED\n"));
1006 break;
1007
1008 case STATUS_DEVICE_BUSY:
1009 FreeBT_DbgPrint(3, ("STATUS_DEVICE_BUSY\n"));
1010 break;
1011
1012 case STATUS_POWER_STATE_INVALID:
1013 FreeBT_DbgPrint(3, ("STATUS_POWER_STATE_INVALID\n"));
1014 goto IdleNotificationRequestComplete_Exit;
1015
1016 default:
1017 FreeBT_DbgPrint(3, ("default: status = %X\n", ntStatus));
1018 break;
1019
1020 }
1021
1022 // if in error, issue a SetD0
1023 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationRequestComplete::"));
1024 FreeBT_IoIncrement(DeviceExtension);
1025
1026 powerState.DeviceState = PowerDeviceD0;
1027 ntStatus = PoRequestPowerIrp(
1028 DeviceExtension->PhysicalDeviceObject,
1029 IRP_MN_SET_POWER,
1030 powerState,
1031 (PREQUEST_POWER_COMPLETE) PoIrpAsyncCompletionFunc,
1032 DeviceExtension,
1033 NULL);
1034
1035 if(!NT_SUCCESS(ntStatus))
1036 FreeBT_DbgPrint(1, ("PoRequestPowerIrp failed\n"));
1037
1038 }
1039
1040 IdleNotificationRequestComplete_Exit:
1041 KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql);
1042 idleCallbackInfo = DeviceExtension->IdleCallbackInfo;
1043 DeviceExtension->IdleCallbackInfo = NULL;
1044
1045 idleIrp = (PIRP) InterlockedExchangePointer(&DeviceExtension->PendingIdleIrp, NULL);
1046 InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
1047
1048 KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
1049
1050 if(idleCallbackInfo)
1051 ExFreePool(idleCallbackInfo);
1052
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.
1057 if(idleIrp)
1058 {
1059 FreeBT_DbgPrint(3, ("completion routine has a valid irp and frees it\n"));
1060 IoFreeIrp(Irp);
1061 KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE);
1062
1063 }
1064
1065 else
1066 {
1067 // The CancelSelectiveSuspend routine has grabbed the Irp from the device
1068 // extension. Now the last one to decrement the FreeIdleIrpCount should
1069 // free the irp.
1070 if (0 == InterlockedDecrement(&DeviceExtension->FreeIdleIrpCount))
1071 {
1072 FreeBT_DbgPrint(3, ("completion routine frees the irp\n"));
1073 IoFreeIrp(Irp);
1074 KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE);
1075
1076 }
1077
1078 }
1079
1080 if(DeviceExtension->SSEnable)
1081 {
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"));
1086
1087 }
1088
1089 return STATUS_MORE_PROCESSING_REQUIRED;
1090
1091 }
1092
1093 VOID NTAPI CancelSelectSuspend(IN PDEVICE_EXTENSION DeviceExtension)
1094 {
1095 PIRP irp;
1096 KIRQL oldIrql;
1097
1098 FreeBT_DbgPrint(3, ("CancelSelectSuspend: Entered\n"));
1099
1100 irp = NULL;
1101
1102 KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql);
1103
1104 if(!CanDeviceSuspend(DeviceExtension))
1105 {
1106 FreeBT_DbgPrint(3, ("Device is not idle\n"));
1107 irp = (PIRP) InterlockedExchangePointer(&DeviceExtension->PendingIdleIrp, NULL);
1108
1109 }
1110
1111 KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
1112
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.
1117 if(irp)
1118 {
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...
1122 //
1123 //
1124 if(IoCancelIrp(irp))
1125 {
1126 FreeBT_DbgPrint(3, ("IoCancelIrp returns TRUE\n"));
1127
1128 }
1129
1130 else
1131 {
1132 FreeBT_DbgPrint(3, ("IoCancelIrp returns FALSE\n"));
1133
1134 }
1135
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
1139 // free the irp.
1140 if(0 == InterlockedDecrement(&DeviceExtension->FreeIdleIrpCount))
1141 {
1142 FreeBT_DbgPrint(3, ("CancelSelectSuspend frees the irp\n"));
1143 IoFreeIrp(irp);
1144 KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE);
1145
1146 }
1147
1148 }
1149
1150 FreeBT_DbgPrint(3, ("CancelSelectSuspend: Leaving\n"));
1151
1152 return;
1153
1154 }
1155
1156 VOID NTAPI PoIrpCompletionFunc(IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus)
1157 {
1158 PIRP_COMPLETION_CONTEXT irpContext;
1159 irpContext = NULL;
1160
1161 FreeBT_DbgPrint(3, ("PoIrpCompletionFunc::"));
1162
1163 if(Context)
1164 irpContext = (PIRP_COMPLETION_CONTEXT) Context;
1165
1166 // all we do is set the event and decrement the count
1167 if(irpContext)
1168 {
1169 KeSetEvent(irpContext->Event, 0, FALSE);
1170 FreeBT_IoDecrement(irpContext->DeviceExtension);
1171 ExFreePool(irpContext);
1172
1173 }
1174
1175 return;
1176
1177 }
1178
1179 VOID NTAPI PoIrpAsyncCompletionFunc(IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus)
1180 {
1181 PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) Context;
1182 FreeBT_DbgPrint(3, ("PoIrpAsyncCompletionFunc::"));
1183 FreeBT_IoDecrement(DeviceExtension);
1184
1185 return;
1186
1187 }
1188
1189 VOID NTAPI WWIrpCompletionFunc(IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus)
1190 {
1191 PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) Context;
1192
1193 FreeBT_DbgPrint(3, ("WWIrpCompletionFunc::"));
1194 FreeBT_IoDecrement(DeviceExtension);
1195
1196 return;
1197
1198 }