isodev.c
上传用户:leituo004
上传日期:2014-11-03
资源大小:159k
文件大小:37k
源码类别:

驱动编程

开发平台:

Visual C++

  1. /*++
  2. Copyright (c) 2000  Microsoft Corporation
  3. Module Name:
  4.     isodev.c
  5. Abstract:
  6.     This file contains dispatch routines 
  7.     for create and close. This file also 
  8.     contains routines to selectively suspend 
  9.     the device. The selective suspend feature
  10.     is usb specific and not hardware specific.
  11. Environment:
  12.     Kernel mode
  13. Notes:
  14.     Copyright (c) 2000 Microsoft Corporation.  
  15.     All Rights Reserved.
  16. --*/
  17. #include "isousb.h"
  18. #include "isopnp.h"
  19. #include "isopwr.h"
  20. #include "isodev.h"
  21. #include "isousr.h"
  22. #include "isowmi.h"
  23. #include "isorwr.h"
  24. #include "isostrm.h"
  25. NTSTATUS
  26. IsoUsb_DispatchCreate(
  27.     IN PDEVICE_OBJECT DeviceObject,
  28.     IN PIRP           Irp
  29.     )
  30. /*++
  31.  
  32. Routine Description:
  33.     Dispatch routine for create.
  34. Arguments:
  35.     DeviceObject - pointer to device object
  36.     Irp - I/O request packet.
  37. Return Value:
  38.     NT status value
  39. --*/
  40. {
  41.     LONG                        i;
  42.     NTSTATUS                    ntStatus;
  43.     PFILE_OBJECT                fileObject;
  44.     PDEVICE_EXTENSION           deviceExtension;
  45.     PIO_STACK_LOCATION          irpStack;
  46.     PFILE_OBJECT_CONTENT        fileObjectContent;
  47.     PUSBD_INTERFACE_INFORMATION interface;
  48.     i = 0;
  49.     irpStack = IoGetCurrentIrpStackLocation(Irp);
  50.     fileObject = irpStack->FileObject;
  51.     deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  52.     PAGED_CODE();
  53.     IsoUsb_DbgPrint(3, ("IsoUsb_DispatchCreate - beginsn"));
  54.     if(deviceExtension->DeviceState != Working) {
  55.         ntStatus = STATUS_INVALID_DEVICE_STATE;
  56.         goto IsoUsb_DispatchCreate_Exit;
  57.     }
  58.     if(deviceExtension->UsbInterface) {
  59.     
  60.         interface = deviceExtension->UsbInterface;    
  61.     }
  62.     else {
  63.         IsoUsb_DbgPrint(1, ("UsbInterface not foundn"));
  64.         ntStatus = STATUS_INVALID_DEVICE_STATE;
  65.         goto IsoUsb_DispatchCreate_Exit;
  66.     }
  67.     if(fileObject) {
  68.         
  69.         fileObject->FsContext = NULL;
  70.     }
  71.     else {
  72.         ntStatus = STATUS_INVALID_PARAMETER;
  73.         goto IsoUsb_DispatchCreate_Exit;
  74.     }
  75.     fileObject->FsContext = ExAllocatePool(NonPagedPool,
  76.                                            sizeof(FILE_OBJECT_CONTENT));
  77.     if(NULL == fileObject->FsContext) {
  78.         IsoUsb_DbgPrint(1, ("failed to alloc memory for FILE_OBJECT_CONTENTn"));
  79.         ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  80.         goto IsoUsb_DispatchCreate_Exit;
  81.     }
  82.     fileObjectContent = (PFILE_OBJECT_CONTENT) fileObject->FsContext;
  83.     fileObjectContent->PipeInformation = NULL;
  84.     fileObjectContent->StreamInformation = NULL;
  85.     if(0 != fileObject->FileName.Length) {
  86.         i = IsoUsb_ParseStringForPipeNumber(&fileObject->FileName);
  87.         IsoUsb_DbgPrint(3, ("create request for pipe # %Xn", i));
  88.         if((i < 0) || 
  89.            (i >= (LONG)(deviceExtension->UsbInterface->NumberOfPipes))) {
  90.     
  91.             ntStatus = STATUS_INVALID_PARAMETER;
  92.             IsoUsb_DbgPrint(1, ("invalid pipe numbern"));
  93.             ExFreePool(fileObject->FsContext);
  94.             fileObject->FsContext = NULL;
  95.             goto IsoUsb_DispatchCreate_Exit;
  96.         }
  97.         fileObjectContent->PipeInformation = (PVOID) &interface->Pipes[i];
  98.     }
  99.     ntStatus = STATUS_SUCCESS;
  100.     InterlockedIncrement(&deviceExtension->OpenHandleCount);
  101.         
  102.     //
  103.     // the device is idle if it has no open handles or pending PnP Irps
  104.     // since we just received an open handle request, cancel idle req.
  105.     //
  106.     if(deviceExtension->SSEnable) {
  107.     
  108.         CancelSelectSuspend(deviceExtension);
  109.     }
  110. IsoUsb_DispatchCreate_Exit:
  111.     Irp->IoStatus.Status = ntStatus;
  112.     Irp->IoStatus.Information = 0;
  113.     IoCompleteRequest(Irp, IO_NO_INCREMENT);
  114.     IsoUsb_DbgPrint(3, ("IsoUsb_DispatchCreate - endsn"));
  115.     
  116.     return ntStatus;
  117. }
  118. NTSTATUS
  119. IsoUsb_DispatchClose(
  120.     IN PDEVICE_OBJECT DeviceObject,
  121.     IN PIRP           Irp
  122.     )
  123. /*++
  124.  
  125. Routine Description:
  126.     Dispatch routine for close.
  127. Arguments:
  128.     DeviceObject - pointer to device object
  129.     Irp - I/O request packet
  130. Return Value:
  131.     NT status value
  132. --*/
  133. {
  134.     NTSTATUS             ntStatus;
  135.     PFILE_OBJECT         fileObject;
  136.     PDEVICE_EXTENSION    deviceExtension;
  137.     PIO_STACK_LOCATION   irpStack;
  138.     PFILE_OBJECT_CONTENT fileObjectContent;
  139.     
  140.     PAGED_CODE();
  141.     //
  142.     // initialize variables
  143.     //
  144.     irpStack = IoGetCurrentIrpStackLocation(Irp);
  145.     fileObject = irpStack->FileObject;
  146.     deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  147.     
  148.     IsoUsb_DbgPrint(3, ("IsoUsb_DispatchClose - beginsn"));
  149.     if(fileObject && fileObject->FsContext) {
  150.         fileObjectContent = (PFILE_OBJECT_CONTENT) fileObject->FsContext;
  151.         ASSERT(NULL == fileObjectContent->StreamInformation);
  152.         ExFreePool(fileObjectContent);
  153.         fileObject->FsContext = NULL;
  154.     }
  155.     //
  156.     // set ntStatus to STATUS_SUCCESS 
  157.     //
  158.     ntStatus = STATUS_SUCCESS;
  159.     Irp->IoStatus.Status = ntStatus;
  160.     Irp->IoStatus.Information = 0;
  161.     IoCompleteRequest(Irp, IO_NO_INCREMENT);
  162.     InterlockedDecrement(&deviceExtension->OpenHandleCount);
  163.     IsoUsb_DbgPrint(3, ("IsoUsb_DispatchClose - endsn"));
  164.     return ntStatus;
  165. }
  166. NTSTATUS
  167. IsoUsb_DispatchDevCtrl(
  168.     IN PDEVICE_OBJECT DeviceObject,
  169.     IN PIRP Irp
  170.     )
  171. /*++
  172.  
  173. Routine Description:
  174.     Dispatch routine for IRP_MJ_DEVICE_CONTROL
  175. Arguments:
  176.     DeviceObject - pointer to device object
  177.     Irp - I/O request packet
  178. Return Value:
  179.     NT status value
  180. --*/
  181. {
  182.     ULONG              code;
  183.     PVOID              ioBuffer;
  184.     ULONG              inputBufferLength;
  185.     ULONG              outputBufferLength;
  186.     ULONG              info;
  187.     NTSTATUS           ntStatus;
  188.     PFILE_OBJECT       fileObject;
  189.     PDEVICE_EXTENSION  deviceExtension;
  190.     PIO_STACK_LOCATION irpStack;
  191.     //
  192.     // initialize variables
  193.     //
  194.     info = 0;
  195.     irpStack = IoGetCurrentIrpStackLocation(Irp);
  196.     fileObject = irpStack->FileObject;
  197.     code = irpStack->Parameters.DeviceIoControl.IoControlCode;
  198.     deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  199.     ioBuffer           = Irp->AssociatedIrp.SystemBuffer;
  200.     inputBufferLength  = irpStack->Parameters.DeviceIoControl.InputBufferLength;
  201.     outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
  202.     if(deviceExtension->DeviceState != Working) {
  203.         IsoUsb_DbgPrint(1, ("Invalid device staten"));
  204.         Irp->IoStatus.Status = ntStatus = STATUS_INVALID_DEVICE_STATE;
  205.         Irp->IoStatus.Information = info;
  206.         IoCompleteRequest(Irp, IO_NO_INCREMENT);
  207.         return ntStatus;
  208.     }
  209.     IsoUsb_DbgPrint(3, ("IsoUsb_DispatchDevCtrl::"));
  210.     IsoUsb_IoIncrement(deviceExtension);
  211.     //
  212.     // make sure that the selective suspend request has been completed.
  213.     //
  214.     if(deviceExtension->SSEnable) {
  215.         //
  216.         // It is true that the client driver cancelled the selective suspend
  217.         // request in the dispatch routine for create.
  218.         // But there is no guarantee that it has indeed been completed.
  219.         // so wait on the NoIdleReqPendEvent and proceed only if this event
  220.         // is signalled.
  221.         //
  222.         IsoUsb_DbgPrint(3, ("Waiting on the IdleReqPendEventn"));
  223.         
  224.         KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent, 
  225.                               Executive, 
  226.                               KernelMode, 
  227.                               FALSE, 
  228.                               NULL);
  229.     }
  230.     switch(code) {
  231.     case IOCTL_ISOUSB_RESET_PIPE:
  232.     {
  233.         PUSBD_PIPE_INFORMATION pipe;
  234.         pipe = NULL;
  235.         if(fileObject && fileObject->FsContext) {
  236.             
  237.             pipe = (PUSBD_PIPE_INFORMATION)
  238.                    ((PFILE_OBJECT_CONTENT)fileObject->FsContext)->PipeInformation;
  239.         }
  240.         if(pipe == NULL) {
  241.             ntStatus = STATUS_INVALID_PARAMETER;
  242.         }
  243.         else {
  244.             
  245.             ntStatus = IsoUsb_ResetPipe(DeviceObject, pipe);
  246.         }
  247.         break;
  248.     }
  249.     case IOCTL_ISOUSB_GET_CONFIG_DESCRIPTOR:
  250.     {
  251.         ULONG length;
  252.         if(deviceExtension->UsbConfigurationDescriptor) {
  253.             length = deviceExtension->UsbConfigurationDescriptor->wTotalLength;
  254.             if(outputBufferLength >= length) {
  255.                 RtlCopyMemory(ioBuffer,
  256.                               deviceExtension->UsbConfigurationDescriptor,
  257.                               length);
  258.                 info = length;
  259.                 ntStatus = STATUS_SUCCESS;
  260.             }
  261.             else {
  262.                 
  263.                 ntStatus = STATUS_INVALID_BUFFER_SIZE;
  264.             }
  265.         }
  266.         else {
  267.             
  268.             ntStatus = STATUS_UNSUCCESSFUL;
  269.         }
  270.         break;
  271.     }
  272.     case IOCTL_ISOUSB_RESET_DEVICE:
  273.         
  274.         ntStatus = IsoUsb_ResetDevice(DeviceObject);
  275.         break;
  276.     case IOCTL_ISOUSB_START_ISO_STREAM:
  277.         ntStatus = IsoUsb_StartIsoStream(DeviceObject, Irp);
  278.         return STATUS_SUCCESS;
  279.     case IOCTL_ISOUSB_STOP_ISO_STREAM:
  280.     {
  281.            
  282.         PFILE_OBJECT_CONTENT fileObjectContent;
  283.         
  284.         if(fileObject && fileObject->FsContext) {
  285.             fileObjectContent = (PFILE_OBJECT_CONTENT)
  286.                                 fileObject->FsContext;
  287.             ntStatus = IsoUsb_StopIsoStream(
  288.                             DeviceObject,
  289.                             InterlockedExchangePointer(
  290.                                 &fileObjectContent->StreamInformation,
  291.                                 NULL),
  292.                             Irp);
  293.         }
  294.         else {
  295.             ntStatus = STATUS_UNSUCCESSFUL;
  296.         }
  297.         break;
  298.     }
  299.     default :
  300.         ntStatus = STATUS_INVALID_DEVICE_REQUEST;
  301.         break;
  302.     }
  303.     Irp->IoStatus.Status = ntStatus;
  304.     Irp->IoStatus.Information = info;
  305.     IoCompleteRequest(Irp, IO_NO_INCREMENT);
  306.     IsoUsb_DbgPrint(3, ("IsoUsb_DispatchDevCtrl::"));
  307.     IsoUsb_IoDecrement(deviceExtension);
  308.     return ntStatus;
  309. }
  310. LONG
  311. IsoUsb_ParseStringForPipeNumber(
  312.     IN PUNICODE_STRING PipeName
  313.     )
  314. /*++
  315.  
  316. Routine Description:
  317.     This routine parses the PipeName for the Pipe#
  318. Arguments:
  319.     PipeName - Unicode string for the pipe name
  320. Return Value:
  321.     Pipe number
  322. --*/
  323. {
  324.     LONG  bufferIndex;
  325.     ULONG uval;
  326.     ULONG umultiplier;
  327.     if(PipeName->Length == 0) {
  328.         return -1;
  329.     }
  330.     bufferIndex = (PipeName->Length / sizeof(WCHAR)) - 1;
  331.     while((bufferIndex > -1) && 
  332.           ((PipeName->Buffer[bufferIndex] < (WCHAR) '0')  ||
  333.            (PipeName->Buffer[bufferIndex] > (WCHAR) '9')))           {
  334.         
  335.         bufferIndex--;
  336.     }
  337.     if(bufferIndex > -1) {
  338.         uval = 0;
  339.         umultiplier = 1;
  340.         while((bufferIndex > -1) &&
  341.               (PipeName->Buffer[bufferIndex] >= (WCHAR) '0') &&
  342.               (PipeName->Buffer[bufferIndex] <= (WCHAR) '9'))        {
  343.             uval += (umultiplier *
  344.                      (ULONG) (PipeName->Buffer[bufferIndex] - (WCHAR) '0'));
  345.             bufferIndex--;
  346.             umultiplier *= 10;
  347.         }
  348.         return uval;
  349.     }
  350.     return -1;
  351. }
  352. NTSTATUS
  353. IsoUsb_ResetPipe(
  354.     IN PDEVICE_OBJECT         DeviceObject,
  355.     IN PUSBD_PIPE_INFORMATION PipeInfo
  356.     )
  357. /*++
  358.  
  359. Routine Description:
  360.     This routine submits an irp/urb pair synchronously
  361.     with function code URB_FUNCTION_RESET_PIPE to reset
  362.     the pipe
  363. Arguments:
  364.     DeviceObject - pointer to device object
  365.     PipeInfo - pointer to USBD_PIPE_INFORMATION
  366. Return Value:
  367.     NT status value
  368. --*/
  369. {
  370.     PURB              urb;
  371.     NTSTATUS          ntStatus;
  372.     USBD_STATUS       usbdStatus;
  373.     PDEVICE_EXTENSION deviceExtension;
  374.     //
  375.     // initialize variables
  376.     //
  377.     urb = NULL;
  378.     deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  379.     urb = ExAllocatePool(NonPagedPool, 
  380.                          sizeof(struct _URB_PIPE_REQUEST));
  381.     if(urb) {
  382.         urb->UrbHeader.Length = (USHORT) sizeof(struct _URB_PIPE_REQUEST);
  383.         urb->UrbHeader.Function = URB_FUNCTION_RESET_PIPE;
  384.         urb->UrbPipeRequest.PipeHandle = PipeInfo->PipeHandle;
  385.         ntStatus = CallUSBD(DeviceObject, urb);
  386.         usbdStatus = urb->UrbHeader.Status;
  387.         ExFreePool(urb);
  388.     }
  389.     else {
  390.         ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  391.     }
  392.     if(NT_SUCCESS(ntStatus) &&
  393.        USBD_SUCCESS(usbdStatus)) {
  394.     
  395.         IsoUsb_DbgPrint(3, ("IsoUsb_ResetPipe - successn"));
  396.         ntStatus = STATUS_SUCCESS;
  397.     }
  398.     else {
  399.         IsoUsb_DbgPrint(1, ("IsoUsb_ResetPipe - failed with "
  400.                             "Irp status = %X and Urb status = %Xn",
  401.                             ntStatus,
  402.                             usbdStatus));
  403.     }
  404.     return ntStatus;
  405. }
  406. NTSTATUS
  407. IsoUsb_ResetDevice(
  408.     IN PDEVICE_OBJECT DeviceObject
  409.     )
  410. /*++
  411.  
  412. Routine Description:
  413.     This routine resets the device
  414. Arguments:
  415.     DeviceObject - pointer to device object
  416. Return Value:
  417.     NT status value
  418. --*/
  419. {
  420.     NTSTATUS ntStatus;
  421.     ULONG    portStatus;
  422.     IsoUsb_DbgPrint(3, ("IsoUsb_ResetDevice - beginsn"));
  423.     ntStatus = IsoUsb_GetPortStatus(DeviceObject, &portStatus);
  424.     if((NT_SUCCESS(ntStatus))                 &&
  425.        (!(portStatus & USBD_PORT_ENABLED))    &&
  426.        (portStatus & USBD_PORT_CONNECTED)) {
  427.         ntStatus = IsoUsb_ResetParentPort(DeviceObject);
  428.     }
  429.     IsoUsb_DbgPrint(3, ("IsoUsb_ResetDevice - endsn"));
  430.     return ntStatus;
  431. }
  432. NTSTATUS
  433. IsoUsb_GetPortStatus(
  434.     IN PDEVICE_OBJECT DeviceObject,
  435.     IN OUT PULONG     PortStatus
  436.     )
  437. /*++
  438.  
  439. Routine Description:
  440.     This routine fetches the port status value
  441. Arguments:
  442.     DeviceObject - pointer to device object
  443.     PortStatus - pointer to ULONG to contain the status value
  444. Return Value:
  445.     NT status value
  446. --*/
  447. {
  448.     NTSTATUS           ntStatus;
  449.     KEVENT             event;
  450.     PIRP               irp;
  451.     IO_STATUS_BLOCK    ioStatus;
  452.     PIO_STACK_LOCATION nextStack;
  453.     PDEVICE_EXTENSION  deviceExtension;
  454.     //
  455.     // initialize variables
  456.     //
  457.     deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  458.     *PortStatus = 0;
  459.     IsoUsb_DbgPrint(3, ("IsoUsb_GetPortStatus - beginsn"));
  460.     KeInitializeEvent(&event, NotificationEvent, FALSE);
  461.     irp = IoBuildDeviceIoControlRequest(
  462.                     IOCTL_INTERNAL_USB_GET_PORT_STATUS,
  463.                     deviceExtension->TopOfStackDeviceObject,
  464.                     NULL,
  465.                     0,
  466.                     NULL,
  467.                     0,
  468.                     TRUE,
  469.                     &event,
  470.                     &ioStatus);
  471.     if(NULL == irp) {
  472.         IsoUsb_DbgPrint(1, ("memory alloc for irp failedn"));
  473.         return STATUS_INSUFFICIENT_RESOURCES;
  474.     }
  475.     nextStack = IoGetNextIrpStackLocation(irp);
  476.     ASSERT(nextStack != NULL);
  477.     nextStack->Parameters.Others.Argument1 = PortStatus;
  478.     IsoUsb_DbgPrint(3, ("IsoUsb_GetPortStatus::"));
  479.     IsoUsb_IoIncrement(deviceExtension);
  480.     ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, irp);
  481.     if(STATUS_PENDING == ntStatus) {
  482.         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
  483.     }
  484.     else {
  485.         ioStatus.Status = ntStatus;
  486.     }
  487.     IsoUsb_DbgPrint(3, ("IsoUsb_GetPortStatus::"));
  488.     IsoUsb_IoDecrement(deviceExtension);
  489.     ntStatus = ioStatus.Status;
  490.     IsoUsb_DbgPrint(3, ("IsoUsb_GetPortStatus - endsn"));
  491.     return ntStatus;
  492. }
  493. NTSTATUS
  494. IsoUsb_ResetParentPort(
  495.     IN PDEVICE_OBJECT DeviceObject
  496.     )
  497. /*++
  498.  
  499. Routine Description:
  500.     This routine will submit an IOCTL_INTERNAL_USB_RESET_PORT,
  501.     down the stack
  502. Arguments:
  503.     DeviceObject - pointer to device object
  504. Return Value:
  505.     NT status value
  506. --*/
  507. {
  508.     NTSTATUS           ntStatus;
  509.     KEVENT             event;
  510.     PIRP               irp;
  511.     IO_STATUS_BLOCK    ioStatus;
  512.     PIO_STACK_LOCATION nextStack;
  513.     PDEVICE_EXTENSION  deviceExtension;
  514.     //
  515.     // initialize variables
  516.     //
  517.     deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  518.     IsoUsb_DbgPrint(3, ("IsoUsb_ResetParentPort - beginsn"));
  519.     KeInitializeEvent(&event, NotificationEvent, FALSE);
  520.     irp = IoBuildDeviceIoControlRequest(
  521.                     IOCTL_INTERNAL_USB_RESET_PORT,
  522.                     deviceExtension->TopOfStackDeviceObject,
  523.                     NULL,
  524.                     0,
  525.                     NULL,
  526.                     0,
  527.                     TRUE,
  528.                     &event,
  529.                     &ioStatus);
  530.     if(NULL == irp) {
  531.         IsoUsb_DbgPrint(1, ("memory alloc for irp failedn"));
  532.         return STATUS_INSUFFICIENT_RESOURCES;
  533.     }
  534.     nextStack = IoGetNextIrpStackLocation(irp);
  535.     ASSERT(nextStack != NULL);
  536.     IsoUsb_DbgPrint(3, ("IsoUsb_ResetParentPort"));
  537.     IsoUsb_IoIncrement(deviceExtension);
  538.     ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, irp);
  539.     if(STATUS_PENDING == ntStatus) {
  540.         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
  541.     }
  542.     else {
  543.         ioStatus.Status = ntStatus;
  544.     }
  545.     IsoUsb_DbgPrint(3, ("IsoUsb_ResetParentPort"));
  546.     IsoUsb_IoDecrement(deviceExtension);
  547.     ntStatus = ioStatus.Status;
  548.     IsoUsb_DbgPrint(3, ("IsoUsb_ResetParentPort - endsn"));
  549.     return ntStatus;
  550. }
  551. NTSTATUS
  552. SubmitIdleRequestIrp(
  553.     IN PDEVICE_EXTENSION DeviceExtension
  554.     )
  555. /*++
  556.  
  557. Routine Description:
  558.     This routine builds an idle request irp with an associated callback routine
  559.     and a completion routine in the driver and passes the irp down the stack.
  560. Arguments:
  561.     DeviceExtension - pointer to device extension
  562. Return Value:
  563.     NT status value
  564. --*/
  565. {
  566.     PIRP                    irp;
  567.     NTSTATUS                ntStatus;
  568.     KIRQL                   oldIrql;
  569.     PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;
  570.     PIO_STACK_LOCATION      nextStack;
  571.     //
  572.     // initialize variables
  573.     //
  574.     
  575.     irp = NULL;
  576.     idleCallbackInfo = NULL;
  577.     IsoUsb_DbgPrint(3, ("SubmitIdleRequest - beginsn"));
  578.     ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
  579.     if(PowerDeviceD0 != DeviceExtension->DevPower) {
  580.         ntStatus = STATUS_POWER_STATE_INVALID;
  581.         goto SubmitIdleRequestIrp_Exit;
  582.     }
  583.     KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql);
  584.     if(InterlockedExchange(&DeviceExtension->IdleReqPend, 1)) {
  585.         IsoUsb_DbgPrint(1, ("Idle request pending..n"));
  586.         KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
  587.         ntStatus = STATUS_DEVICE_BUSY;
  588.         goto SubmitIdleRequestIrp_Exit;
  589.     }
  590.     //
  591.     // clear the NoIdleReqPendEvent because we are about 
  592.     // to submit an idle request. Since we are so early
  593.     // to clear this event, make sure that if we fail this
  594.     // request we set back the event.
  595.     //
  596.     KeClearEvent(&DeviceExtension->NoIdleReqPendEvent);
  597.     idleCallbackInfo = ExAllocatePool(NonPagedPool, 
  598.                                       sizeof(struct _USB_IDLE_CALLBACK_INFO));
  599.     if(idleCallbackInfo) {
  600.         idleCallbackInfo->IdleCallback = IdleNotificationCallback;
  601.         idleCallbackInfo->IdleContext = (PVOID)DeviceExtension;
  602.         ASSERT(DeviceExtension->IdleCallbackInfo == NULL);
  603.         DeviceExtension->IdleCallbackInfo = idleCallbackInfo;
  604.         //
  605.         // we use IoAllocateIrp to create an irp to selectively suspend the 
  606.         // device. This irp lies pending with the hub driver. When appropriate
  607.         // the hub driver will invoked callback, where we power down. The completion
  608.         // routine is invoked when we power back.
  609.         //
  610.         irp = IoAllocateIrp(DeviceExtension->TopOfStackDeviceObject->StackSize,
  611.                             FALSE);
  612.         if(irp == NULL) {
  613.             IsoUsb_DbgPrint(1, ("cannot build idle request irpn"));
  614.             KeSetEvent(&DeviceExtension->NoIdleReqPendEvent,
  615.                        IO_NO_INCREMENT,
  616.                        FALSE);
  617.             InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
  618.             KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
  619.             ExFreePool(idleCallbackInfo);
  620.             ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  621.             goto SubmitIdleRequestIrp_Exit;
  622.         }
  623.         nextStack = IoGetNextIrpStackLocation(irp);
  624.         nextStack->MajorFunction = 
  625.                     IRP_MJ_INTERNAL_DEVICE_CONTROL;
  626.         nextStack->Parameters.DeviceIoControl.IoControlCode = 
  627.                     IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION;
  628.         nextStack->Parameters.DeviceIoControl.Type3InputBuffer =
  629.                     idleCallbackInfo;
  630.         nextStack->Parameters.DeviceIoControl.InputBufferLength =
  631.                     sizeof(struct _USB_IDLE_CALLBACK_INFO);
  632.         IoSetCompletionRoutine(irp, 
  633.                                IdleNotificationRequestComplete,
  634.                                DeviceExtension, 
  635.                                TRUE, 
  636.                                TRUE, 
  637.                                TRUE);
  638.         DeviceExtension->PendingIdleIrp = irp;
  639.         //
  640.         // we initialize the count to 2.
  641.         // The reason is, if the CancelSelectSuspend routine manages
  642.         // to grab the irp from the device extension, then the last of the
  643.         // CancelSelectSuspend routine/IdleNotificationRequestComplete routine 
  644.         // to execute will free this irp. We need to have this schema so that
  645.         // 1. completion routine does not attempt to touch the irp freed by 
  646.         //    CancelSelectSuspend routine.
  647.         // 2. CancelSelectSuspend routine doesnt wait for ever for the completion
  648.         //    routine to complete!
  649.         //
  650.         DeviceExtension->FreeIdleIrpCount = 2;
  651.         KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
  652.         //
  653.         // check if the device is idle.
  654.         // A check here ensures that a race condition did not 
  655.         // completely reverse the call sequence of SubmitIdleRequestIrp
  656.         // and CancelSelectiveSuspend
  657.         //
  658.         if(!CanDeviceSuspend(DeviceExtension) ||
  659.            PowerDeviceD0 != DeviceExtension->DevPower) {
  660.             //
  661.             // device cannot suspend - abort.
  662.             // also irps created using IoAllocateIrp 
  663.             // needs to be deallocated.
  664.             //
  665.      
  666.             IsoUsb_DbgPrint(1, ("Device is not idlen"));
  667.             KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql);
  668.             DeviceExtension->IdleCallbackInfo = NULL;
  669.             DeviceExtension->PendingIdleIrp = NULL;
  670.             KeSetEvent(&DeviceExtension->NoIdleReqPendEvent,
  671.                        IO_NO_INCREMENT,
  672.                        FALSE);
  673.             InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
  674.             KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
  675.             if(idleCallbackInfo) {
  676.                 ExFreePool(idleCallbackInfo);
  677.             }
  678.             //
  679.             // it is still safe to touch the local variable "irp" here.
  680.             // the irp has not been passed down the stack, the irp has
  681.             // no cancellation routine. The worse position is that the
  682.             // CancelSelectSuspend has run after we released the spin 
  683.             // lock above. It is still essential to free the irp.
  684.             //
  685.             if(irp) {
  686.             
  687.                 IoFreeIrp(irp);
  688.             }
  689.             goto SubmitIdleRequestIrp_Exit;
  690.         }
  691.         IsoUsb_DbgPrint(3, ("Cancel the timersn"));
  692.         //
  693.         // Cancel the timer so that the DPCs are no longer fired.
  694.         // Thus, we are making judicious usage of our resources.
  695.         // we do not need DPCs because we already have an idle irp pending.
  696.         // The timers are re-initialized in the completion routine.
  697.         //
  698.         KeCancelTimer(&DeviceExtension->Timer);
  699.         ntStatus = IoCallDriver(DeviceExtension->TopOfStackDeviceObject, irp);
  700.         if(!NT_SUCCESS(ntStatus)) {
  701.             IsoUsb_DbgPrint(1, ("IoCallDriver failedn"));
  702.             goto SubmitIdleRequestIrp_Exit;
  703.         }
  704.     }
  705.     else {
  706.         IsoUsb_DbgPrint(1, ("Memory allocation for idleCallbackInfo failedn"));
  707.         KeSetEvent(&DeviceExtension->NoIdleReqPendEvent,
  708.                    IO_NO_INCREMENT,
  709.                    FALSE);
  710.         InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
  711.         KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
  712.         ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  713.     }
  714. SubmitIdleRequestIrp_Exit:
  715.     IsoUsb_DbgPrint(3, ("SubmitIdleRequest - endsn"));
  716.     return ntStatus;
  717. }
  718. VOID
  719. IdleNotificationCallback(
  720.     IN PDEVICE_EXTENSION DeviceExtension
  721.     )
  722. /*++
  723.  
  724. Routine Description:
  725.   "A pointer to a callback function in your driver is passed down the stack with
  726.    this IOCTL, and it is this callback function that is called by USBHUB when it
  727.    safe for your device to power down."
  728.   "When the callback in your driver is called, all you really need to do is to
  729.    to first ensure that a WaitWake Irp has been submitted for your device, if 
  730.    remote wake is possible for your device and then request a SetD2 (or DeviceWake)"
  731. Arguments:
  732. Return Value:
  733. --*/
  734. {
  735.     NTSTATUS                ntStatus;
  736.     POWER_STATE             powerState;
  737.     KEVENT                  irpCompletionEvent;
  738.     PIRP_COMPLETION_CONTEXT irpContext;
  739.     IsoUsb_DbgPrint(3, ("IdleNotificationCallback - beginsn"));
  740.     //
  741.     // Dont idle, if the device was just disconnected or being stopped
  742.     // i.e. return for the following DeviceState(s)
  743.     // NotStarted, Stopped, PendingStop, PendingRemove, SurpriseRemoved, Removed
  744.     //
  745.     if(DeviceExtension->DeviceState != Working) {
  746.         return;
  747.     }
  748.     //
  749.     // If there is not already a WW IRP pending, submit one now
  750.     //
  751.     if(DeviceExtension->WaitWakeEnable) {
  752.         IssueWaitWake(DeviceExtension);
  753.     }
  754.     //
  755.     // power down the device
  756.     //
  757.     irpContext = (PIRP_COMPLETION_CONTEXT) 
  758.                  ExAllocatePool(NonPagedPool,
  759.                                 sizeof(IRP_COMPLETION_CONTEXT));
  760.     if(!irpContext) {
  761.         IsoUsb_DbgPrint(1, ("Failed to alloc memory for irpContextn"));
  762.         ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  763.     }
  764.     else {
  765.         //
  766.         // increment the count. In the HoldIoRequestWorkerRoutine, the
  767.         // count is decremented twice (one for the system Irp and the 
  768.         // other for the device Irp. An increment here compensates for 
  769.         // the sytem irp..The decrement corresponding to this increment 
  770.         // is in the completion function
  771.         //
  772.         IsoUsb_DbgPrint(3, ("IdleNotificationCallback::"));
  773.         IsoUsb_IoIncrement(DeviceExtension);
  774.         powerState.DeviceState = DeviceExtension->PowerDownLevel;
  775.         KeInitializeEvent(&irpCompletionEvent, NotificationEvent, FALSE);
  776.         irpContext->DeviceExtension = DeviceExtension;
  777.         irpContext->Event = &irpCompletionEvent;
  778.         ntStatus = PoRequestPowerIrp(
  779.                           DeviceExtension->PhysicalDeviceObject, 
  780.                           IRP_MN_SET_POWER, 
  781.                           powerState, 
  782.                           (PREQUEST_POWER_COMPLETE) PoIrpCompletionFunc,
  783.                           irpContext, 
  784.                           NULL);
  785.         if(STATUS_PENDING == ntStatus) {
  786.             IsoUsb_DbgPrint(3, ("IdleNotificationCallback::"
  787.                            "waiting for the power irp to completen"));
  788.             KeWaitForSingleObject(&irpCompletionEvent,
  789.                                   Executive,
  790.                                   KernelMode,
  791.                                   FALSE,
  792.                                   NULL);
  793.         }
  794.     }
  795.     
  796.     if(!NT_SUCCESS(ntStatus)) {
  797.         if(irpContext) {
  798.             ExFreePool(irpContext);
  799.         }
  800.     }
  801.     IsoUsb_DbgPrint(3, ("IdleNotificationCallback - endsn"));
  802. }
  803. NTSTATUS
  804. IdleNotificationRequestComplete(
  805.     IN PDEVICE_OBJECT    DeviceObject,
  806.     IN PIRP              Irp,
  807.     IN PDEVICE_EXTENSION DeviceExtension
  808.     )
  809. /*++
  810.  
  811. Routine Description:
  812.   Completion routine for idle notification irp
  813. Arguments:
  814.     DeviceObject - pointer to device object
  815.     Irp - I/O request packet
  816.     DeviceExtension - pointer to device extension
  817. Return Value:
  818.     NT status value
  819. --*/
  820. {
  821.     NTSTATUS                ntStatus;
  822.     POWER_STATE             powerState;
  823.     KIRQL                   oldIrql;
  824.     PIRP                    idleIrp;
  825.     LARGE_INTEGER           dueTime;
  826.     PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;
  827.     IsoUsb_DbgPrint(3, ("IdleNotificationRequestCompete - beginsn"));
  828.     idleIrp = NULL;
  829.     //
  830.     // check the Irp status
  831.     //
  832.     ntStatus = Irp->IoStatus.Status;
  833.     if(!NT_SUCCESS(ntStatus) && ntStatus != STATUS_NOT_SUPPORTED) {
  834.         IsoUsb_DbgPrint(1, ("Idle irp completes with error::"));
  835.         switch(ntStatus) {
  836.             
  837.         case STATUS_INVALID_DEVICE_REQUEST:
  838.             IsoUsb_DbgPrint(1, ("STATUS_INVALID_DEVICE_REQUESTn"));
  839.             break;
  840.         case STATUS_CANCELLED:
  841.             IsoUsb_DbgPrint(1, ("STATUS_CANCELLEDn"));
  842.             break;
  843.         case STATUS_POWER_STATE_INVALID:
  844.             IsoUsb_DbgPrint(1, ("STATUS_POWER_STATE_INVALIDn"));
  845.             goto IdleNotificationRequestComplete_Exit;
  846.         case STATUS_DEVICE_BUSY:
  847.             IsoUsb_DbgPrint(1, ("STATUS_DEVICE_BUSYn"));
  848.             break;
  849.         default:
  850.             IsoUsb_DbgPrint(1, ("defaultn"));
  851.             break;
  852.         }
  853.         //
  854.         // if in error, issue a SetD0
  855.         //
  856.         IsoUsb_DbgPrint(3, ("IdleNotificationRequestComplete::"));
  857.         IsoUsb_IoIncrement(DeviceExtension);
  858.         powerState.DeviceState = PowerDeviceD0;
  859.         ntStatus = PoRequestPowerIrp(
  860.                           DeviceExtension->PhysicalDeviceObject, 
  861.                           IRP_MN_SET_POWER, 
  862.                           powerState, 
  863.                           (PREQUEST_POWER_COMPLETE) PoIrpAsyncCompletionFunc, 
  864.                           DeviceExtension, 
  865.                           NULL);
  866.         if(!NT_SUCCESS(ntStatus)) {
  867.     
  868.             IsoUsb_DbgPrint(1, ("PoRequestPowerIrp failedn"));
  869.         }
  870.     }
  871. IdleNotificationRequestComplete_Exit:
  872.     KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql);
  873.     idleCallbackInfo = DeviceExtension->IdleCallbackInfo;
  874.     DeviceExtension->IdleCallbackInfo = NULL;
  875.     idleIrp = (PIRP) InterlockedExchangePointer(
  876.                                         &DeviceExtension->PendingIdleIrp,
  877.                                         NULL);
  878.     InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
  879.     KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
  880.     if(idleCallbackInfo) {
  881.         ExFreePool(idleCallbackInfo);
  882.     }
  883.     //
  884.     // since we allocated the irp, we need to free it.
  885.     // return STATUS_MORE_PROCESSING_REQUIRED so that 
  886.     // the kernel does not touch it.
  887.     //
  888.     if(idleIrp) {
  889.         IsoUsb_DbgPrint(3, ("completion routine has a valid irp and frees itn"));
  890.         IoFreeIrp(Irp);
  891.         KeSetEvent(&DeviceExtension->NoIdleReqPendEvent,
  892.                    IO_NO_INCREMENT,
  893.                    FALSE);
  894.     }
  895.     else {
  896.         //
  897.         // The CancelSelectiveSuspend routine has grabbed the Irp from the device 
  898.         // extension. Now the last one to decrement the FreeIdleIrpCount should
  899.         // free the irp.
  900.         //
  901.         if(0 == InterlockedDecrement(&DeviceExtension->FreeIdleIrpCount)) {
  902.             IsoUsb_DbgPrint(3, ("completion routine frees the irpn"));
  903.             
  904.             IoFreeIrp(Irp);
  905.             KeSetEvent(&DeviceExtension->NoIdleReqPendEvent,
  906.                        IO_NO_INCREMENT,
  907.                        FALSE);
  908.         }
  909.     }
  910.     if(DeviceExtension->SSEnable) {
  911.         IsoUsb_DbgPrint(3, ("Set the timer to fire DPCsn"));
  912.         dueTime.QuadPart = -10000 * IDLE_INTERVAL;               // 5000 ms
  913.         KeSetTimerEx(&DeviceExtension->Timer, 
  914.                      dueTime,
  915.                      IDLE_INTERVAL,                              // 5000 ms
  916.                      &DeviceExtension->DeferredProcCall);
  917.         IsoUsb_DbgPrint(3, ("IdleNotificationRequestCompete - endsn"));
  918.     }
  919.     return STATUS_MORE_PROCESSING_REQUIRED;
  920. }
  921. VOID
  922. CancelSelectSuspend(
  923.     IN PDEVICE_EXTENSION DeviceExtension
  924.     )
  925. /*++
  926.  
  927. Routine Description:
  928.     This routine is invoked to cancel selective suspend request.
  929. Arguments:
  930.     DeviceExtension - pointer to device extension
  931. Return Value:
  932.     None.
  933. --*/
  934. {
  935.     PIRP  irp;
  936.     KIRQL oldIrql;
  937.     irp = NULL;
  938.     IsoUsb_DbgPrint(3, ("CancelSelectSuspend - beginsn"));
  939.     KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql);
  940.     if(!CanDeviceSuspend(DeviceExtension))
  941.     {
  942.         IsoUsb_DbgPrint(3, ("Device is not idlen"));
  943.     
  944.         irp = (PIRP) InterlockedExchangePointer(
  945.                             &DeviceExtension->PendingIdleIrp, 
  946.                             NULL);
  947.     }
  948.     KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
  949.     //
  950.     // since we have a valid Irp ptr,
  951.     // we can call IoCancelIrp on it,
  952.     // without the fear of the irp 
  953.     // being freed underneath us.
  954.     //
  955.     if(irp) {
  956.         //
  957.         // This routine has the irp pointer.
  958.         // It is safe to call IoCancelIrp because we know that
  959.         // the compleiton routine will not free this irp unless...
  960.         // 
  961.         
  962.         if(IoCancelIrp(irp)) {
  963.             IsoUsb_DbgPrint(3, ("IoCancelIrp returns TRUEn"));
  964.         }
  965.         else {
  966.             IsoUsb_DbgPrint(3, ("IoCancelIrp returns FALSEn"));
  967.         }
  968.         //
  969.         // ....we decrement the FreeIdleIrpCount from 2 to 1.
  970.         // if completion routine runs ahead of us, then this routine 
  971.         // decrements the FreeIdleIrpCount from 1 to 0 and hence shall
  972.         // free the irp.
  973.         //
  974.         if(0 == InterlockedDecrement(&DeviceExtension->FreeIdleIrpCount)) {
  975.             IsoUsb_DbgPrint(3, ("CancelSelectSuspend frees the irpn"));
  976.             
  977.             IoFreeIrp(irp);
  978.             KeSetEvent(&DeviceExtension->NoIdleReqPendEvent,
  979.                        IO_NO_INCREMENT,
  980.                        FALSE);
  981.         }
  982.     }
  983.     IsoUsb_DbgPrint(3, ("CancelSelectSuspend - endsn"));
  984.     return;
  985. }
  986. VOID
  987. PoIrpCompletionFunc(
  988.     IN PDEVICE_OBJECT DeviceObject,
  989.     IN UCHAR MinorFunction,
  990.     IN POWER_STATE PowerState,
  991.     IN PVOID Context,
  992.     IN PIO_STATUS_BLOCK IoStatus
  993.     )
  994. /*++
  995.  
  996. Routine Description:
  997.     Completion routine for power irp PoRequested in IdleNotification
  998.     RequestComplete routine.
  999. Arguments:
  1000.     DeviceObject - pointer to device object
  1001.     MinorFunciton - minor function for the irp.
  1002.     PowerState - irp power state
  1003.     Context - context passed to the completion function
  1004.     IoStatus - status block.
  1005. Return Value:
  1006.     None
  1007. --*/
  1008. {
  1009.     PIRP_COMPLETION_CONTEXT irpContext;
  1010.     
  1011.     //
  1012.     // initialize variables
  1013.     //
  1014.     if(Context) {
  1015.         irpContext = (PIRP_COMPLETION_CONTEXT) Context;
  1016.     }
  1017.     //
  1018.     // all we do is set the event and decrement the count
  1019.     //
  1020.     if(irpContext) {
  1021.         KeSetEvent(irpContext->Event, 0, FALSE);
  1022.         IsoUsb_DbgPrint(3, ("PoIrpCompletionFunc::"));
  1023.         IsoUsb_IoDecrement(irpContext->DeviceExtension);
  1024.         ExFreePool(irpContext);
  1025.     }
  1026.     return;
  1027. }
  1028. VOID
  1029. PoIrpAsyncCompletionFunc(
  1030.     IN PDEVICE_OBJECT DeviceObject,
  1031.     IN UCHAR MinorFunction,
  1032.     IN POWER_STATE PowerState,
  1033.     IN PVOID Context,
  1034.     IN PIO_STATUS_BLOCK IoStatus
  1035.     )
  1036. /*++
  1037. Routine Description:
  1038.   Completion routine for PoRequest wait wake irp
  1039. Arguments:
  1040.     DeviceObject - pointer to device object
  1041.     MinorFunciton - minor function for the irp.
  1042.     PowerState - irp power state
  1043.     Context - context passed to the completion function
  1044.     IoStatus - status block.    
  1045. Return Value:
  1046.     None
  1047. --*/
  1048. {
  1049.     PDEVICE_EXTENSION DeviceExtension;
  1050.     
  1051.     //
  1052.     // initialize variables
  1053.     //
  1054.     DeviceExtension = (PDEVICE_EXTENSION) Context;
  1055.     //
  1056.     // all we do is decrement the count
  1057.     //
  1058.     
  1059.     IsoUsb_DbgPrint(3, ("PoIrpAsyncCompletionFunc::"));
  1060.     IsoUsb_IoDecrement(DeviceExtension);
  1061.     return;
  1062. }
  1063. VOID
  1064. WWIrpCompletionFunc(
  1065.     IN PDEVICE_OBJECT DeviceObject,
  1066.     IN UCHAR MinorFunction,
  1067.     IN POWER_STATE PowerState,
  1068.     IN PVOID Context,
  1069.     IN PIO_STATUS_BLOCK IoStatus
  1070.     )
  1071. /*++
  1072.  
  1073. Routine Description:
  1074.   Completion routine for idle notification irp
  1075. Arguments:
  1076. Return Value:
  1077. --*/
  1078. {
  1079.     PDEVICE_EXTENSION DeviceExtension;
  1080.     
  1081.     //
  1082.     // initialize variables
  1083.     //
  1084.     DeviceExtension = (PDEVICE_EXTENSION) Context;
  1085.     //
  1086.     // all we do is decrement the count
  1087.     //
  1088.     
  1089.     IsoUsb_DbgPrint(3, ("WWIrpCompletionFunc::"));
  1090.     IsoUsb_IoDecrement(DeviceExtension);
  1091.     return;
  1092. }