Sync with trunk r58740.
[reactos.git] / drivers / hid / hidusb / hidusb.c
index 7b3ffbd..7d5d239 100644 (file)
@@ -175,6 +175,7 @@ HidUsb_AbortPipe(
     PHID_DEVICE_EXTENSION DeviceExtension;
     PURB Urb;
     NTSTATUS Status;
+    PUSBD_PIPE_INFORMATION PipeInformation;
 
     //
     // get device extension
@@ -194,13 +195,20 @@ HidUsb_AbortPipe(
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
+    //
+    // get pipe information
+    //
+    PipeInformation = HidUsb_GetInputInterruptInterfaceHandle(HidDeviceExtension->InterfaceInfo);
+    ASSERT(PipeInformation);
+    ASSERT(PipeInformation->PipeHandle);
+
     //
     // init urb
     //
     RtlZeroMemory(Urb, sizeof(struct _URB_PIPE_REQUEST));
     Urb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE;
     Urb->UrbHeader.Length = sizeof(struct _URB_PIPE_REQUEST);
-    Urb->UrbPipeRequest.PipeHandle = HidDeviceExtension->ConfigurationHandle;
+    Urb->UrbPipeRequest.PipeHandle = PipeInformation->PipeHandle;
 
     //
     // dispatch request
@@ -218,7 +226,56 @@ HidUsb_AbortPipe(
     return Status;
 }
 
+NTSTATUS
+HidUsb_ResetPort(
+    IN PDEVICE_OBJECT DeviceObject)
+{
+    KEVENT Event;
+    PIRP Irp;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+    IO_STATUS_BLOCK IoStatusBlock;
+    NTSTATUS Status;
 
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+    //
+    // init event
+    //
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    //
+    // build irp
+    //
+    Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_RESET_PORT, DeviceExtension->NextDeviceObject, NULL, 0, NULL, 0, TRUE, &Event, &IoStatusBlock);
+    if (!Irp)
+    {
+        //
+        // no memory
+        //
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // send the irp
+    //
+    Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
+    if (Status == STATUS_PENDING)
+    {
+        //
+        // wait for request completion
+        //
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+        Status = IoStatusBlock.Status;
+    }
+
+    //
+    // done
+    //
+    return IoStatusBlock.Status;
+}
 
 NTSTATUS
 NTAPI
@@ -248,7 +305,7 @@ HidCreate(
     //
     // informal debug print
     //
-    DPRINT1("HIDUSB Request: %x\n", IoStack->MajorFunction);
+    DPRINT("HIDUSB Request: %x\n", IoStack->MajorFunction);
 
     //
     // done
@@ -256,6 +313,91 @@ HidCreate(
     return STATUS_SUCCESS;
 }
 
+VOID
+NTAPI
+HidUsb_ResetWorkerRoutine(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PVOID Ctx)
+{
+    NTSTATUS Status;
+    ULONG PortStatus;
+    PHID_USB_RESET_CONTEXT ResetContext;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+
+    DPRINT("[HIDUSB] ResetWorkerRoutine\n");
+
+    //
+    // get context
+    //
+    ResetContext = (PHID_USB_RESET_CONTEXT)Ctx;
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)ResetContext->DeviceObject->DeviceExtension;
+
+    //
+    // get port status
+    //
+    Status = HidUsb_GetPortStatus(ResetContext->DeviceObject, &PortStatus);
+    DPRINT("[HIDUSB] ResetWorkerRoutine GetPortStatus %x PortStatus %x\n", Status, PortStatus);
+    if (NT_SUCCESS(Status))
+    {
+        if (!(PortStatus & USB_PORT_STATUS_ENABLE))
+        {
+            //
+            // port is disabled
+            //
+            Status = HidUsb_ResetInterruptPipe(ResetContext->DeviceObject);
+            DPRINT1("[HIDUSB] ResetWorkerRoutine ResetPipe %x\n", Status);
+        }
+        else
+        {
+            //
+            // abort pipe
+            //
+            Status = HidUsb_AbortPipe(ResetContext->DeviceObject);
+            DPRINT1("[HIDUSB] ResetWorkerRoutine AbortPipe %x\n", Status);
+            if (NT_SUCCESS(Status))
+            {
+                //
+                // reset port
+                //
+                Status = HidUsb_ResetPort(ResetContext->DeviceObject);
+                DPRINT1("[HIDUSB] ResetPort %x\n", Status);
+                if (Status == STATUS_DEVICE_DATA_ERROR)
+                {
+                    //
+                    // invalidate device state
+                    //
+                    IoInvalidateDeviceState(DeviceExtension->PhysicalDeviceObject);
+                }
+
+                //
+                // reset interrupt pipe
+                //
+                if (NT_SUCCESS(Status))
+                {
+                    //
+                    // reset pipe
+                    //
+                    Status = HidUsb_ResetInterruptPipe(ResetContext->DeviceObject);
+                    DPRINT1("[HIDUSB] ResetWorkerRoutine ResetPipe %x\n", Status);
+                }
+            }
+        }
+    }
+
+    //
+    // cleanup
+    //
+    ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
+    IoFreeWorkItem(ResetContext->WorkItem);
+    IoCompleteRequest(ResetContext->Irp, IO_NO_INCREMENT);
+    ExFreePool(ResetContext);
+}
+
+
 NTSTATUS
 NTAPI
 HidUsb_ReadReportCompletion(
@@ -266,13 +408,7 @@ HidUsb_ReadReportCompletion(
     PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
     PHID_DEVICE_EXTENSION DeviceExtension;
     PURB Urb;
-    PUCHAR Buffer;
-
-    //
-    // get device extension
-    //
-    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
-    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
+    PHID_USB_RESET_CONTEXT ResetContext;
 
     //
     // get urb
@@ -280,30 +416,96 @@ HidUsb_ReadReportCompletion(
     Urb = (PURB)Context;
     ASSERT(Urb);
 
+    DPRINT("[HIDUSB] HidUsb_ReadReportCompletion %p Status %x Urb Status %x\n", Irp, Irp->IoStatus, Urb->UrbHeader.Status);
+
+    if (Irp->PendingReturned)
+    {
+        //
+        // mark irp pending
+        //
+        IoMarkIrpPending(Irp);
+    }
+
     //
-    // FIXME handle error
+    // did the reading report succeed / cancelled
     //
-    ASSERT(Urb->UrbHeader.Status == USBD_STATUS_SUCCESS);
+    if (NT_SUCCESS(Irp->IoStatus.Status) || Irp->IoStatus.Status == STATUS_CANCELLED || Irp->IoStatus.Status == STATUS_DEVICE_NOT_CONNECTED)
+    {
+        //
+        // store result length
+        //
+        Irp->IoStatus.Information = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
+
+        //
+        // FIXME handle error
+        //
+        ASSERT(Urb->UrbHeader.Status == USBD_STATUS_SUCCESS);
+
+        //
+        // free the urb
+        //
+        ExFreePool(Context);
+
+        //
+        // finish completion
+        //
+        return STATUS_CONTINUE_COMPLETION;
+    }
 
     //
-    // FIXME handle error
+    // get device extension
     //
-    ASSERT(Irp->IoStatus.Status == STATUS_SUCCESS);
-
-    Buffer = Urb->UrbBulkOrInterruptTransfer.TransferBuffer;
-    ASSERT(Urb->UrbBulkOrInterruptTransfer.TransferBufferLength == 4);
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
 
     //
-    // store result
+    // allocate reset context
     //
-    Irp->IoStatus.Information = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
+    ResetContext = (PHID_USB_RESET_CONTEXT)ExAllocatePool(NonPagedPool, sizeof(HID_USB_RESET_CONTEXT));
+    if (ResetContext)
+    {
+        //
+        // allocate work item
+        //
+        ResetContext->WorkItem = IoAllocateWorkItem(DeviceObject);
+        if (ResetContext->WorkItem)
+        {
+            //
+            // init reset context
+            //
+            ResetContext->Irp = Irp;
+            ResetContext->DeviceObject = DeviceObject;
+
+            //
+            // queue the work item
+            //
+            IoQueueWorkItem(ResetContext->WorkItem, HidUsb_ResetWorkerRoutine, DelayedWorkQueue, ResetContext);
+
+            //
+            // free urb
+            //
+            ExFreePool(Urb);
+
+            //
+            // defer completion
+            //
+            return STATUS_MORE_PROCESSING_REQUIRED;
+        }
+        //
+        // free context
+        //
+        ExFreePool(ResetContext);
+    }
 
-    DPRINT("[HIDUSB] ReadCompletion Information %lu Buffer %x %x %x %x\n", Buffer[0] & 0xFF, Buffer[1] & 0xFF, Buffer[2] & 0xFF, Buffer[3] & 0xFF);
+    //
+    // free urb
+    //
+    ExFreePool(Urb);
 
     //
     // complete request
     //
-    return STATUS_SUCCESS;
+    return STATUS_CONTINUE_COMPLETION;
 }
 
 
@@ -360,6 +562,13 @@ HidUsb_ReadReport(
     //
     RtlZeroMemory(Urb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
 
+    //
+    // sanity check
+    //
+    ASSERT(Irp->UserBuffer);
+    ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength);
+    ASSERT(PipeInformation->PipeHandle);
+
     //
     // build the urb
     //
@@ -372,6 +581,11 @@ HidUsb_ReadReport(
                                            USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
                                            NULL);
 
+    //
+    // store configuration handle
+    //
+    Urb->UrbHeader.UsbdDeviceHandle = HidDeviceExtension->ConfigurationHandle;
+
     //
     // get next location to setup irp
     //
@@ -381,8 +595,12 @@ HidUsb_ReadReport(
     // init irp for lower driver
     //
     IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
-    IoStack->Parameters.Others.Argument1 = (PVOID)Urb;
     IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
+    IoStack->Parameters.DeviceIoControl.InputBufferLength = 0;
+    IoStack->Parameters.DeviceIoControl.OutputBufferLength = 0;
+    IoStack->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
+    IoStack->Parameters.Others.Argument1 = (PVOID)Urb;
+
 
     //
     // set completion routine
@@ -404,7 +622,7 @@ HidUsb_GetReportDescriptor(
 {
     PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
     PHID_DEVICE_EXTENSION DeviceExtension;
-    PUCHAR Report = NULL;
+    PVOID Report = NULL;
     ULONG BufferLength, Length;
     PIO_STACK_LOCATION IoStack;
     NTSTATUS Status;
@@ -428,22 +646,27 @@ HidUsb_GetReportDescriptor(
     // FIXME: support old hid version
     //
     BufferLength = HidDeviceExtension->HidDescriptor->DescriptorList[0].wReportLength;
-    Status = Hid_GetDescriptor(DeviceObject, URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), &Report, &BufferLength, HidDeviceExtension->HidDescriptor->DescriptorList[0].bReportType, 0, 0);
+    Status = Hid_GetDescriptor(DeviceObject, URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), &Report, &BufferLength, HidDeviceExtension->HidDescriptor->DescriptorList[0].bReportType, 0, HidDeviceExtension->InterfaceInfo->InterfaceNumber);
     if (!NT_SUCCESS(Status))
     {
         //
         // failed to get descriptor
+        // try with old hid version
         //
-        DPRINT("[HIDUSB] failed to get report descriptor with %x\n", Status);
-        ASSERT(FALSE);
-        return Status;
+        BufferLength = HidDeviceExtension->HidDescriptor->DescriptorList[0].wReportLength;
+        Status = Hid_GetDescriptor(DeviceObject, URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), &Report, &BufferLength, HidDeviceExtension->HidDescriptor->DescriptorList[0].bReportType, 0, 0 /* FIXME*/);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT("[HIDUSB] failed to get report descriptor with %x\n", Status);
+            return Status;
+        }
     }
 
     //
     // get current stack location
     //
     IoStack = IoGetCurrentIrpStackLocation(Irp);
-    DPRINT1("[HIDUSB] GetReportDescriptor: Status %x ReportLength %lu OutputBufferLength %lu TransferredLength %lu\n", Status, HidDeviceExtension->HidDescriptor->DescriptorList[0].wReportLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength, BufferLength);
+    DPRINT("[HIDUSB] GetReportDescriptor: Status %x ReportLength %lu OutputBufferLength %lu TransferredLength %lu\n", Status, HidDeviceExtension->HidDescriptor->DescriptorList[0].wReportLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength, BufferLength);
 
     //
     // get length to copy
@@ -509,7 +732,7 @@ HidInternalDeviceControl(
             //
             // store result
             //
-            DPRINT1("[HIDUSB] IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
+            DPRINT("[HIDUSB] IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
             ASSERT(HidDeviceExtension->DeviceDescriptor);
             Irp->IoStatus.Information = sizeof(HID_DESCRIPTOR);
             Attributes = (PHID_DEVICE_ATTRIBUTES)Irp->UserBuffer;
@@ -531,7 +754,7 @@ HidInternalDeviceControl(
             // sanity check
             //
             ASSERT(HidDeviceExtension->HidDescriptor);
-            DPRINT1("[HIDUSB] IOCTL_HID_GET_DEVICE_DESCRIPTOR DescriptorLength %lu OutputBufferLength %lu\n", HidDeviceExtension->HidDescriptor->bLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength);
+            DPRINT("[HIDUSB] IOCTL_HID_GET_DEVICE_DESCRIPTOR DescriptorLength %lu OutputBufferLength %lu\n", HidDeviceExtension->HidDescriptor->bLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength);
 
             //
             // store length
@@ -556,14 +779,14 @@ HidInternalDeviceControl(
         case IOCTL_HID_GET_REPORT_DESCRIPTOR:
         {
             Status = HidUsb_GetReportDescriptor(DeviceObject, Irp);
-            DPRINT1("[HIDUSB] IOCTL_HID_GET_REPORT_DESCRIPTOR Status %x\n", Status);
+            DPRINT("[HIDUSB] IOCTL_HID_GET_REPORT_DESCRIPTOR Status %x\n", Status);
             Irp->IoStatus.Status = Status;
             IoCompleteRequest(Irp, IO_NO_INCREMENT);
             return Status;
         }
         case IOCTL_HID_READ_REPORT:
         {
-            //DPRINT1("[HIDUSB] IOCTL_HID_READ_REPORT\n");
+            DPRINT("[HIDUSB] IOCTL_HID_READ_REPORT\n");
             Status = HidUsb_ReadReport(DeviceObject, Irp);
             return Status;
         }
@@ -703,6 +926,7 @@ Hid_PnpCompletion(
     return STATUS_MORE_PROCESSING_REQUIRED;
 }
 
+
 NTSTATUS
 Hid_DispatchUrb(
     IN PDEVICE_OBJECT DeviceObject,
@@ -781,7 +1005,7 @@ Hid_DispatchUrb(
         Status = IoStatus.Status;
     }
 
-    DPRINT1("[HIDUSB] DispatchUrb %x\n", Status);
+    DPRINT("[HIDUSB] DispatchUrb %x\n", Status);
 
 
     //
@@ -949,6 +1173,13 @@ Hid_SelectConfiguration(
                                                               USB_DEVICE_CLASS_HUMAN_INTERFACE,
                                                              -1,
                                                              -1);
+    if (!InterfaceDescriptor)
+    {
+        //
+        // bogus configuration descriptor
+        //
+        return STATUS_INVALID_PARAMETER;
+    }
 
     //
     // sanity check
@@ -1011,6 +1242,156 @@ Hid_SelectConfiguration(
     return Status;
 }
 
+NTSTATUS
+Hid_SetIdle(
+    IN PDEVICE_OBJECT DeviceObject)
+{
+    PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+    PURB Urb;
+    NTSTATUS Status;
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
+
+    //
+    // allocate urb
+    //
+    Urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
+    if (!Urb)
+    {
+        //
+        // no memory
+        //
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // zero urb
+    //
+    RtlZeroMemory(Urb, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
+
+    //
+    // format urb
+    //
+    UsbBuildVendorRequest(Urb,
+                          URB_FUNCTION_CLASS_INTERFACE, 
+                          sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST),
+                          0,
+                          0,
+                          USB_SET_IDLE_REQUEST, // HID_SET_IDLE
+                          0,
+                          0,
+                          NULL,
+                          NULL,
+                          0,
+                          NULL);
+
+    //
+    // dispatch urb
+    //
+    Status = Hid_DispatchUrb(DeviceObject, Urb);
+
+    //
+    // free urb
+    //
+    ExFreePool(Urb);
+
+    //
+    // print status
+    //
+    DPRINT1("Status %x\n", Status);
+    return Status;
+}
+
+
+VOID
+Hid_GetProtocol(
+    IN PDEVICE_OBJECT DeviceObject)
+{
+    PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+    PURB Urb;
+    NTSTATUS Status;
+    UCHAR Protocol[1];
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
+    ASSERT(HidDeviceExtension->InterfaceInfo);
+
+    if (HidDeviceExtension->InterfaceInfo->SubClass != 0x1)
+    {
+        //
+        // device does not support the boot protocol
+        //
+        return;
+    }
+
+
+    //
+    // allocate urb
+    //
+    Urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
+    if (!Urb)
+    {
+        //
+        // no memory
+        //
+        return;
+    }
+
+    //
+    // zero urb
+    //
+    RtlZeroMemory(Urb, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
+
+    //
+    // format urb
+    //
+    UsbBuildVendorRequest(Urb,
+                          URB_FUNCTION_CLASS_INTERFACE, 
+                          sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST),
+                          USBD_TRANSFER_DIRECTION_IN,
+                          0,
+                          USB_GET_PROTOCOL_REQUEST,
+                          0,
+                          0,
+                          Protocol,
+                          NULL,
+                          1,
+                          NULL);
+    Protocol[0] = 0xFF;
+    //
+    // dispatch urb
+    //
+    Status = Hid_DispatchUrb(DeviceObject, Urb);
+
+    //
+    // free urb
+    //
+    ExFreePool(Urb);
+
+    //
+    // boot protocol active 0x00 disabled 0x1
+    //
+    if (Protocol[0] != 0x1)
+    {
+        if (Protocol[0] == 0x00)
+        {
+            DPRINT1("[HIDUSB] Need to disable boot protocol!\n");
+        }
+        else
+        {
+            DPRINT1("[HIDUSB] Unexpected protocol value %x\n", Protocol[0] & 0xFF);
+        }
+    }
+}
 
 NTSTATUS
 Hid_PnpStart(
@@ -1103,7 +1484,7 @@ Hid_PnpStart(
         //
         // no interface class
         //
-        DPRINT1("[HIDUSB] HID Class found\n");
+        DPRINT1("[HIDUSB] HID Interface descriptor not found\n");
         return STATUS_UNSUCCESSFUL;
     }
 
@@ -1139,15 +1520,33 @@ Hid_PnpStart(
         // done
         //
         DPRINT1("[HIDUSB] SelectConfiguration %x\n", Status);
-        return Status;
-    }
 
-    //
-    // FIXME parse hid descriptor
-    //
-    UNIMPLEMENTED
-    ASSERT(FALSE);
-    return STATUS_SUCCESS;
+        if (NT_SUCCESS(Status))
+        {
+            //
+            // now set the device idle
+            //
+            Hid_SetIdle(DeviceObject);
+
+            //
+            // get protocol
+            //
+            Hid_GetProtocol(DeviceObject);
+            return Status;
+        }
+    }
+    else
+    {
+        //
+        // FIXME parse hid descriptor
+        // select configuration
+        // set idle
+        // and get protocol
+        //
+        UNIMPLEMENTED
+        ASSERT(FALSE);
+    }
+    return Status;
 }
 
 
@@ -1173,7 +1572,7 @@ HidPnp(
     // get current stack location
     //
     IoStack = IoGetCurrentIrpStackLocation(Irp);
-    DPRINT1("[HIDUSB] Pnp %x\n", IoStack->MinorFunction);
+    DPRINT("[HIDUSB] Pnp %x\n", IoStack->MinorFunction);
 
     //
     // handle requests based on request type
@@ -1191,15 +1590,18 @@ HidPnp(
             //
             // free resources
             //
-            if (HidDeviceExtension->HidDescriptor)
+            if (HidDeviceExtension->ConfigurationDescriptor)
             {
-                ExFreePool(HidDeviceExtension->HidDescriptor);
-                HidDeviceExtension->HidDescriptor = NULL;
+                ExFreePool(HidDeviceExtension->ConfigurationDescriptor);
+                HidDeviceExtension->ConfigurationDescriptor = NULL;
             }
 
             //
-            // done
+            // delete and detach device
             //
+            IoDetachDevice(DeviceExtension->NextDeviceObject);
+            IoDeleteDevice(DeviceObject);
+
             return Status;
         }
         case IRP_MN_QUERY_PNP_DEVICE_STATE:
@@ -1220,6 +1622,25 @@ HidPnp(
             //
             return Status;
         }
+        case IRP_MN_QUERY_STOP_DEVICE:
+        case IRP_MN_QUERY_REMOVE_DEVICE:
+        {
+            //
+            // we're fine with it
+            //
+            Irp->IoStatus.Status = STATUS_SUCCESS;
+
+            //
+            // pass request to next driver
+            //
+            IoSkipCurrentIrpStackLocation(Irp);
+            Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
+
+            //
+            // done
+            //
+            return Status;
+        }
         case IRP_MN_STOP_DEVICE:
         {
             //
@@ -1277,13 +1698,12 @@ HidPnp(
                 Status = Irp->IoStatus.Status;
             }
 
-            if (NT_SUCCESS(Status))
+            if (NT_SUCCESS(Status) && IoStack->Parameters.DeviceCapabilities.Capabilities != NULL)
             {
                 //
-                // driver supports D1 & D2
+                // don't need to safely remove
                 //
-                IoStack->Parameters.DeviceCapabilities.Capabilities->DeviceD1 = TRUE;
-                IoStack->Parameters.DeviceCapabilities.Capabilities->DeviceD2 = TRUE;
+                IoStack->Parameters.DeviceCapabilities.Capabilities->SurpriseRemovalOK = TRUE;
             }
 
             //
@@ -1333,6 +1753,7 @@ HidPnp(
             // complete request
             //
             Irp->IoStatus.Status = Status;
+            DPRINT("[HIDUSB] IRP_MN_START_DEVICE Status %x\n", Status);
             IoCompleteRequest(Irp, IO_NO_INCREMENT);
             return Status;
         }
@@ -1373,6 +1794,15 @@ HidAddDevice(
     return STATUS_SUCCESS;
 }
 
+VOID
+NTAPI
+Hid_Unload(
+    IN PDRIVER_OBJECT DriverObject)
+{
+    UNIMPLEMENTED
+}
+
+
 NTSTATUS
 NTAPI
 DriverEntry(
@@ -1392,6 +1822,7 @@ DriverEntry(
     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = HidSystemControl;
     DriverObject->MajorFunction[IRP_MJ_PNP] = HidPnp;
     DriverObject->DriverExtension->AddDevice = HidAddDevice;
+    DriverObject->DriverUnload = Hid_Unload;
 
     //
     // prepare registration info
@@ -1415,8 +1846,8 @@ DriverEntry(
     //
     // informal debug
     //
-    DPRINT1("********* HIDUSB *********\n");
-    DPRINT1("HIDUSB Registration Status %x\n", Status);
+    DPRINT("********* HIDUSB *********\n");
+    DPRINT("HIDUSB Registration Status %x\n", Status);
 
     return Status;
 }