Sync with trunk r58740.
[reactos.git] / drivers / hid / hidusb / hidusb.c
index 01b614a..7d5d239 100644 (file)
 
 #include "hidusb.h"
 
+PUSBD_PIPE_INFORMATION
+HidUsb_GetInputInterruptInterfaceHandle(
+    PUSBD_INTERFACE_INFORMATION InterfaceInformation)
+{
+    ULONG Index;
+
+     //
+     // sanity check
+     //
+     ASSERT(InterfaceInformation->NumberOfPipes);
+
+     for(Index = 0; Index < InterfaceInformation->NumberOfPipes; Index++)
+     {
+         //DPRINT1("[HIDUSB] EndpointAddress %x PipeType %x PipeHandle %x\n", InterfaceInformation->Pipes[Index].EndpointAddress, InterfaceInformation->Pipes[Index].PipeType, InterfaceInformation->Pipes[Index].PipeHandle);
+         if (InterfaceInformation->Pipes[Index].PipeType == UsbdPipeTypeInterrupt && (InterfaceInformation->Pipes[Index].EndpointAddress & USB_ENDPOINT_DIRECTION_MASK))
+         {
+             //
+             // found handle
+             //
+             return &InterfaceInformation->Pipes[Index];
+         }
+    }
+
+    //
+    // not found
+    //
+    return NULL;
+}
+
+NTSTATUS
+HidUsb_GetPortStatus(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PULONG PortStatus)
+{
+    PIRP Irp;
+    KEVENT Event;
+    IO_STATUS_BLOCK IoStatus;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+    PIO_STACK_LOCATION IoStack;
+    NTSTATUS Status;
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+    //
+    // init result
+    //
+    *PortStatus = 0;
+
+    //
+    // init event
+    //
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    //
+    // build irp
+    //
+    Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_GET_PORT_STATUS, DeviceExtension->NextDeviceObject, NULL, 0, NULL, 0, TRUE, &Event, &IoStatus);
+    if (!Irp)
+    {
+        //
+        // no memory
+        //
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // get stack location
+    //
+    IoStack = IoGetNextIrpStackLocation(Irp);
+
+    //
+    // store result buffer
+    //
+   IoStack->Parameters.Others.Argument1 = (PVOID)PortStatus;
+
+   //
+   // call driver
+   //
+   Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
+   if (Status == STATUS_PENDING)
+   {
+       //
+       // wait for completion
+       //
+       KeWaitForSingleObject(&Event, Executive, KernelMode, 0, NULL);
+       return IoStatus.Status;
+   }
+
+   //
+   // done
+   //
+   return Status;
+}
+
+NTSTATUS
+HidUsb_ResetInterruptPipe(
+    IN PDEVICE_OBJECT DeviceObject)
+{
+    PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+    PUSBD_PIPE_INFORMATION PipeInformation;
+    PURB Urb;
+    NTSTATUS Status;
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
+
+    //
+    // get interrupt pipe handle
+    //
+    ASSERT(HidDeviceExtension->InterfaceInfo);
+    PipeInformation = HidUsb_GetInputInterruptInterfaceHandle(HidDeviceExtension->InterfaceInfo);
+    ASSERT(PipeInformation);
+    ASSERT(PipeInformation->PipeHandle);
+
+    //
+    // allocate urb
+    //
+    Urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST));
+    if (!Urb)
+    {
+        //
+        // no memory
+        //
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // init urb
+    //
+    RtlZeroMemory(Urb, sizeof(struct _URB_PIPE_REQUEST));
+    Urb->UrbHeader.Function = URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL;
+    Urb->UrbHeader.Length = sizeof(struct _URB_PIPE_REQUEST);
+    Urb->UrbPipeRequest.PipeHandle = PipeInformation->PipeHandle;
+
+    //
+    // dispatch request
+    //
+    Status = Hid_DispatchUrb(DeviceObject, Urb);
+
+    //
+    // free urb
+    //
+    ExFreePool(Urb);
+
+    //
+    // done
+    //
+    return Status;
+}
+
+NTSTATUS
+HidUsb_AbortPipe(
+    IN PDEVICE_OBJECT DeviceObject)
+{
+    PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+    PURB Urb;
+    NTSTATUS Status;
+    PUSBD_PIPE_INFORMATION PipeInformation;
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
+
+    //
+    // allocate urb
+    //
+    Urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST));
+    if (!Urb)
+    {
+        //
+        // no memory
+        //
+        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 = PipeInformation->PipeHandle;
+
+    //
+    // dispatch request
+    //
+    Status = Hid_DispatchUrb(DeviceObject, Urb);
+
+    //
+    // free urb
+    //
+    ExFreePool(Urb);
+
+    //
+    // done
+    //
+    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
 HidCreate(
@@ -38,7 +305,7 @@ HidCreate(
     //
     // informal debug print
     //
-    DPRINT1("HIDUSB Request: %x\n", IoStack->MajorFunction);
+    DPRINT("HIDUSB Request: %x\n", IoStack->MajorFunction);
 
     //
     // done
@@ -46,63 +313,1496 @@ HidCreate(
     return STATUS_SUCCESS;
 }
 
-NTSTATUS
+VOID
 NTAPI
-HidInternalDeviceControl(
+HidUsb_ResetWorkerRoutine(
     IN PDEVICE_OBJECT DeviceObject,
-    IN PIRP Irp)
+    IN PVOID Ctx)
 {
-    UNIMPLEMENTED
-    ASSERT(FALSE);
-    return STATUS_NOT_IMPLEMENTED;
+    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
-HidPower(
+HidUsb_ReadReportCompletion(
     IN PDEVICE_OBJECT DeviceObject,
-    IN PIRP Irp)
+    IN PIRP Irp,
+    IN PVOID Context)
 {
-    UNIMPLEMENTED
-    ASSERT(FALSE);
-    return STATUS_NOT_IMPLEMENTED;
+    PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+    PURB Urb;
+    PHID_USB_RESET_CONTEXT ResetContext;
+
+    //
+    // get urb
+    //
+    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);
+    }
+
+    //
+    // did the reading report succeed / cancelled
+    //
+    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;
+    }
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
+
+    //
+    // allocate reset context
+    //
+    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);
+    }
+
+    //
+    // free urb
+    //
+    ExFreePool(Urb);
+
+    //
+    // complete request
+    //
+    return STATUS_CONTINUE_COMPLETION;
 }
 
+
 NTSTATUS
 NTAPI
-HidSystemControl(
+HidUsb_ReadReport(
     IN PDEVICE_OBJECT DeviceObject,
     IN PIRP Irp)
 {
+    PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
     PHID_DEVICE_EXTENSION DeviceExtension;
+    PIO_STACK_LOCATION IoStack;
+    PURB Urb;
+    PUSBD_PIPE_INFORMATION PipeInformation;
 
     //
-    // get hid device extension
+    // get device extension
     //
     DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
 
     //
-    // copy stack location
+    // get current stack location
     //
-    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
 
     //
-    // submit request
+    // sanity checks
+    //
+    ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength);
+    ASSERT(Irp->UserBuffer);
+    ASSERT(HidDeviceExtension->InterfaceInfo);
+
+    //
+    // get interrupt input pipe
+    //
+    PipeInformation = HidUsb_GetInputInterruptInterfaceHandle(HidDeviceExtension->InterfaceInfo);
+    ASSERT(PipeInformation);
+
+    //
+    // lets allocate urb
+    //
+    Urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
+    if (!Urb)
+    {
+        //
+        // no memory
+        //
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // init urb
+    //
+    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
+    //
+    UsbBuildInterruptOrBulkTransferRequest(Urb,
+                                           sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
+                                           PipeInformation->PipeHandle,
+                                           Irp->UserBuffer,
+                                           NULL,
+                                           IoStack->Parameters.DeviceIoControl.OutputBufferLength,
+                                           USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
+                                           NULL);
+
+    //
+    // store configuration handle
+    //
+    Urb->UrbHeader.UsbdDeviceHandle = HidDeviceExtension->ConfigurationHandle;
+
+    //
+    // get next location to setup irp
+    //
+    IoStack = IoGetNextIrpStackLocation(Irp);
+
+    //
+    // init irp for lower driver
+    //
+    IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+    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
+    //
+    IoSetCompletionRoutine(Irp, HidUsb_ReadReportCompletion, (PVOID)Urb, TRUE, TRUE, TRUE);
+
+    //
+    // call driver
     //
     return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
 }
 
+
 NTSTATUS
 NTAPI
-HidPnp(
+HidUsb_GetReportDescriptor(
     IN PDEVICE_OBJECT DeviceObject,
     IN PIRP Irp)
 {
-    UNIMPLEMENTED
-    ASSERT(FALSE);
-    return STATUS_NOT_IMPLEMENTED;
+    PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+    PVOID Report = NULL;
+    ULONG BufferLength, Length;
+    PIO_STACK_LOCATION IoStack;
+    NTSTATUS Status;
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
+
+    //
+    // sanity checks
+    //
+    ASSERT(HidDeviceExtension);
+    ASSERT(HidDeviceExtension->HidDescriptor);
+    ASSERT(HidDeviceExtension->HidDescriptor->bNumDescriptors >= 1);
+    ASSERT(HidDeviceExtension->HidDescriptor->DescriptorList[0].bReportType == HID_REPORT_DESCRIPTOR_TYPE);
+    ASSERT(HidDeviceExtension->HidDescriptor->DescriptorList[0].wReportLength > 0);
+
+    //
+    // 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, HidDeviceExtension->InterfaceInfo->InterfaceNumber);
+    if (!NT_SUCCESS(Status))
+    {
+        //
+        // failed to get descriptor
+        // try with old hid version
+        //
+        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);
+    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
+    //
+    Length = min(IoStack->Parameters.DeviceIoControl.OutputBufferLength, BufferLength);
+    ASSERT(Length);
+
+    //
+    // copy result
+    //
+    RtlCopyMemory(Irp->UserBuffer, Report, Length);
+
+    //
+    // store result length
+    //
+    Irp->IoStatus.Information = Length;
+
+    //
+    // done
+    //
+    return Status;
+
 }
 
+NTSTATUS
+NTAPI
+HidInternalDeviceControl(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PIO_STACK_LOCATION IoStack;
+    PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+    PHID_DEVICE_ATTRIBUTES Attributes;
+    ULONG Length;
+    NTSTATUS Status;
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
+
+    //
+    // get current stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+
+    switch(IoStack->Parameters.DeviceIoControl.IoControlCode)
+    {
+        case IOCTL_HID_GET_DEVICE_ATTRIBUTES:
+        {
+            if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_DEVICE_ATTRIBUTES))
+            {
+                //
+                // invalid request
+                //
+                Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
+                DPRINT1("[HIDUSB] IOCTL_HID_GET_DEVICE_ATTRIBUTES invalid buffer\n");
+                IoCompleteRequest(Irp, IO_NO_INCREMENT);
+                return STATUS_INVALID_BUFFER_SIZE;
+            }
+            //
+            // store result
+            //
+            DPRINT("[HIDUSB] IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
+            ASSERT(HidDeviceExtension->DeviceDescriptor);
+            Irp->IoStatus.Information = sizeof(HID_DESCRIPTOR);
+            Attributes = (PHID_DEVICE_ATTRIBUTES)Irp->UserBuffer;
+            Attributes->Size = sizeof(HID_DEVICE_ATTRIBUTES);
+            Attributes->VendorID = HidDeviceExtension->DeviceDescriptor->idVendor;
+            Attributes->ProductID = HidDeviceExtension->DeviceDescriptor->idProduct;
+            Attributes->VersionNumber = HidDeviceExtension->DeviceDescriptor->bcdDevice;
+
+            //
+            // complete request
+            //
+            Irp->IoStatus.Status = STATUS_SUCCESS;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_SUCCESS;
+        }
+        case IOCTL_HID_GET_DEVICE_DESCRIPTOR:
+        {
+            //
+            // sanity check
+            //
+            ASSERT(HidDeviceExtension->HidDescriptor);
+            DPRINT("[HIDUSB] IOCTL_HID_GET_DEVICE_DESCRIPTOR DescriptorLength %lu OutputBufferLength %lu\n", HidDeviceExtension->HidDescriptor->bLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength);
+
+            //
+            // store length
+            //
+            Length = min(HidDeviceExtension->HidDescriptor->bLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength);
+
+            //
+            // copy descriptor
+            //
+            RtlCopyMemory(Irp->UserBuffer, HidDeviceExtension->HidDescriptor, Length);
+
+            //
+            // store result length
+            //
+            Irp->IoStatus.Information = HidDeviceExtension->HidDescriptor->bLength;
+            Irp->IoStatus.Status = STATUS_SUCCESS;
+
+            /* complete request */
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_SUCCESS;
+        }
+        case IOCTL_HID_GET_REPORT_DESCRIPTOR:
+        {
+            Status = HidUsb_GetReportDescriptor(DeviceObject, Irp);
+            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:
+        {
+            DPRINT("[HIDUSB] IOCTL_HID_READ_REPORT\n");
+            Status = HidUsb_ReadReport(DeviceObject, Irp);
+            return Status;
+        }
+        case IOCTL_HID_WRITE_REPORT:
+        {
+            DPRINT1("[HIDUSB] IOCTL_HID_WRITE_REPORT not implemented \n");
+            ASSERT(FALSE);
+            Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_NOT_IMPLEMENTED;
+        }
+        case IOCTL_GET_PHYSICAL_DESCRIPTOR:
+        {
+            DPRINT1("[HIDUSB] IOCTL_GET_PHYSICAL_DESCRIPTOR not implemented \n");
+            ASSERT(FALSE);
+            Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_NOT_IMPLEMENTED;
+        }
+        case IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST:
+        {
+            DPRINT1("[HIDUSB] IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST not implemented \n");
+            ASSERT(FALSE);
+            Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_NOT_IMPLEMENTED;
+        }
+        case IOCTL_HID_GET_FEATURE:
+        {
+            DPRINT1("[HIDUSB] IOCTL_HID_GET_FEATURE not implemented \n");
+            ASSERT(FALSE);
+            Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_NOT_IMPLEMENTED;
+        }
+        case IOCTL_HID_SET_FEATURE:
+        {
+            DPRINT1("[HIDUSB] IOCTL_HID_SET_FEATURE not implemented \n");
+            ASSERT(FALSE);
+            Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_NOT_IMPLEMENTED;
+        }
+        case IOCTL_HID_SET_OUTPUT_REPORT:
+        {
+            DPRINT1("[HIDUSB] IOCTL_HID_SET_OUTPUT_REPORT not implemented \n");
+            ASSERT(FALSE);
+            Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_NOT_IMPLEMENTED;
+        }
+        case IOCTL_HID_GET_INPUT_REPORT:
+        {
+            DPRINT1("[HIDUSB] IOCTL_HID_GET_INPUT_REPORT not implemented \n");
+            ASSERT(FALSE);
+            Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_NOT_IMPLEMENTED;
+        }
+        case IOCTL_HID_GET_INDEXED_STRING:
+        {
+            DPRINT1("[HIDUSB] IOCTL_HID_GET_INDEXED_STRING not implemented \n");
+            ASSERT(FALSE);
+            Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_NOT_IMPLEMENTED;
+        }
+        case IOCTL_HID_GET_MS_GENRE_DESCRIPTOR:
+        {
+            DPRINT1("[HIDUSB] IOCTL_HID_GET_MS_GENRE_DESCRIPTOR not implemented \n");
+            ASSERT(FALSE);
+            Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_NOT_IMPLEMENTED;
+        }
+        default:
+        {
+            UNIMPLEMENTED
+            ASSERT(FALSE);
+            Status = Irp->IoStatus.Status;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return Status;
+        }
+    }
+}
+
+NTSTATUS
+NTAPI
+HidPower(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    UNIMPLEMENTED
+    ASSERT(FALSE);
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS
+NTAPI
+HidSystemControl(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PHID_DEVICE_EXTENSION DeviceExtension;
+
+    //
+    // get hid device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+    //
+    // copy stack location
+    //
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+
+    //
+    // submit request
+    //
+    return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
+}
+
+NTSTATUS
+NTAPI
+Hid_PnpCompletion(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Context)
+{
+    //
+    // signal event
+    //
+    KeSetEvent((PRKEVENT)Context, 0, FALSE);
+
+    //
+    // done
+    //
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+
+NTSTATUS
+Hid_DispatchUrb(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PURB Urb)
+{
+    PIRP Irp;
+    KEVENT Event;
+    PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+    IO_STATUS_BLOCK IoStatus;
+    PIO_STACK_LOCATION IoStack;
+    NTSTATUS Status;
+
+    //
+    // init event
+    //
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
+
+
+    //
+    // build irp
+    //
+    Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_SUBMIT_URB, DeviceExtension->NextDeviceObject, NULL, 0, NULL, 0, TRUE, &Event, &IoStatus);
+    if (!Irp)
+    {
+        //
+        // no memory
+        //
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // get next stack location
+    //
+    IoStack = IoGetNextIrpStackLocation(Irp);
+
+    //
+    // store urb
+    //
+    IoStack->Parameters.Others.Argument1 = (PVOID)Urb;
+
+    //
+    // set completion routine
+    //
+    IoSetCompletionRoutine(Irp, Hid_PnpCompletion, (PVOID)&Event, TRUE, TRUE, TRUE);
+
+    //
+    // call driver
+    //
+    Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
+
+    //
+    // wait for the request to finish
+    //
+    if (Status == STATUS_PENDING)
+    {
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+    }
+
+    //
+    // complete request
+    //
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    if (Status == STATUS_PENDING)
+    {
+        //
+        // get final status
+        //
+        Status = IoStatus.Status;
+    }
+
+    DPRINT("[HIDUSB] DispatchUrb %x\n", Status);
+
+
+    //
+    // done
+    //
+    return Status;
+}
+
+NTSTATUS
+Hid_GetDescriptor(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN USHORT UrbFunction,
+    IN USHORT UrbLength,
+    IN OUT PVOID *UrbBuffer,
+    IN OUT PULONG UrbBufferLength,
+    IN UCHAR DescriptorType, 
+    IN UCHAR Index,
+    IN USHORT LanguageIndex)
+{
+    PURB Urb;
+    NTSTATUS Status;
+    UCHAR Allocated = FALSE;
+
+    //
+    // allocate urb
+    //
+    Urb = (PURB)ExAllocatePool(NonPagedPool, UrbLength);
+    if (!Urb)
+    {
+        //
+        // no memory
+        //
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // is there an urb buffer
+    //
+    if (!*UrbBuffer)
+    {
+        //
+        // allocate buffer
+        //
+        *UrbBuffer = ExAllocatePool(NonPagedPool, *UrbBufferLength);
+        if (!*UrbBuffer)
+        {
+            //
+            // no memory
+            //
+            ExFreePool(Urb);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        //
+        // zero buffer
+        //
+        RtlZeroMemory(*UrbBuffer, *UrbBufferLength);
+        Allocated = TRUE;
+    }
+
+    //
+    // zero urb
+    //
+    RtlZeroMemory(Urb, UrbLength);
+
+    //
+    // build descriptor request
+    //
+    UsbBuildGetDescriptorRequest(Urb, UrbLength, DescriptorType, Index, LanguageIndex, *UrbBuffer, NULL, *UrbBufferLength, NULL);
+
+    //
+    // set urb function
+    //
+   Urb->UrbHeader.Function = UrbFunction;
+
+    //
+    // dispatch urb
+    //
+    Status = Hid_DispatchUrb(DeviceObject, Urb);
+
+    //
+    // did the request fail
+    //
+    if (!NT_SUCCESS(Status))
+    {
+        if (Allocated)
+        {
+            //
+            // free allocated buffer
+            //
+            ExFreePool(*UrbBuffer);
+            *UrbBuffer = NULL;
+        }
+
+        //
+        // free urb
+        //
+        ExFreePool(Urb);
+        *UrbBufferLength = 0;
+        return Status;
+    }
+
+    //
+    // did urb request fail
+    //
+    if (!NT_SUCCESS(Urb->UrbHeader.Status))
+    {
+        if (Allocated)
+        {
+            //
+            // free allocated buffer
+            //
+            ExFreePool(*UrbBuffer);
+            *UrbBuffer = NULL;
+        }
+
+        //
+        // free urb
+        //
+        ExFreePool(Urb);
+        *UrbBufferLength = 0;
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    //
+    // store result length
+    //
+    *UrbBufferLength = Urb->UrbControlDescriptorRequest.TransferBufferLength;
+
+    //
+    // free urb
+    //
+    ExFreePool(Urb);
+
+    //
+    // completed successfully
+    //
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+Hid_SelectConfiguration(
+    IN PDEVICE_OBJECT DeviceObject)
+{
+    PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
+    NTSTATUS Status;
+    USBD_INTERFACE_LIST_ENTRY InterfaceList[2];
+    PURB Urb;
+    PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
+
+    //
+    // now parse the descriptors
+    //
+    InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(HidDeviceExtension->ConfigurationDescriptor,
+                                                              HidDeviceExtension->ConfigurationDescriptor,
+                                                             -1,
+                                                             -1,
+                                                              USB_DEVICE_CLASS_HUMAN_INTERFACE,
+                                                             -1,
+                                                             -1);
+    if (!InterfaceDescriptor)
+    {
+        //
+        // bogus configuration descriptor
+        //
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    //
+    // sanity check
+    //
+    ASSERT(InterfaceDescriptor);
+    ASSERT(InterfaceDescriptor->bInterfaceClass == USB_DEVICE_CLASS_HUMAN_INTERFACE);
+    ASSERT(InterfaceDescriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE);
+    ASSERT(InterfaceDescriptor->bLength == sizeof(USB_INTERFACE_DESCRIPTOR));
+
+    //
+    // setup interface list
+    //
+    RtlZeroMemory(InterfaceList, sizeof(InterfaceList));
+    InterfaceList[0].InterfaceDescriptor = InterfaceDescriptor;
+
+    //
+    // build urb
+    //
+    Urb = USBD_CreateConfigurationRequestEx(HidDeviceExtension->ConfigurationDescriptor, InterfaceList);
+    if (!Urb)
+    {
+        //
+        // no memory
+        //
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // dispatch request
+    //
+    Status = Hid_DispatchUrb(DeviceObject, Urb);
+    if (NT_SUCCESS(Status))
+    {
+        //
+        // store configuration handle
+        //
+        HidDeviceExtension->ConfigurationHandle = Urb->UrbSelectConfiguration.ConfigurationHandle;
+
+        //
+        // copy interface info
+        //
+        HidDeviceExtension->InterfaceInfo = (PUSBD_INTERFACE_INFORMATION)ExAllocatePool(NonPagedPool, Urb->UrbSelectConfiguration.Interface.Length);
+        if (HidDeviceExtension->InterfaceInfo)
+        {
+            //
+            // copy interface info
+            //
+            RtlCopyMemory(HidDeviceExtension->InterfaceInfo, &Urb->UrbSelectConfiguration.Interface, Urb->UrbSelectConfiguration.Interface.Length);
+        }
+    }
+
+    //
+    // free urb request
+    //
+    ExFreePool(Urb);
+
+    //
+    // done
+    //
+    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(
+    IN PDEVICE_OBJECT DeviceObject)
+{
+    PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+    NTSTATUS Status;
+    ULONG DescriptorLength;
+    PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
+    PHID_DESCRIPTOR HidDescriptor;
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
+
+    //
+    // get device descriptor
+    //
+    DescriptorLength = sizeof(USB_DEVICE_DESCRIPTOR);
+    Status = Hid_GetDescriptor(DeviceObject, URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), (PVOID*)&HidDeviceExtension->DeviceDescriptor, &DescriptorLength, USB_DEVICE_DESCRIPTOR_TYPE, 0, 0);
+    if (!NT_SUCCESS(Status))
+    {
+        //
+        // failed to obtain device descriptor
+        //
+        DPRINT1("[HIDUSB] failed to get device descriptor %x\n", Status);
+        return Status;
+    }
+
+    //
+    // now get the configuration descriptor
+    //
+    DescriptorLength = sizeof(USB_CONFIGURATION_DESCRIPTOR);
+    Status = Hid_GetDescriptor(DeviceObject, URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), (PVOID*)&HidDeviceExtension->ConfigurationDescriptor, &DescriptorLength, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0);
+    if (!NT_SUCCESS(Status))
+    {
+        //
+        // failed to obtain device descriptor
+        //
+        DPRINT1("[HIDUSB] failed to get device descriptor %x\n", Status);
+        return Status;
+    }
+
+    //
+    // sanity check
+    //
+    ASSERT(DescriptorLength);
+    ASSERT(HidDeviceExtension->ConfigurationDescriptor);
+    ASSERT(HidDeviceExtension->ConfigurationDescriptor->bLength);
+
+    //
+    // store full length
+    //
+    DescriptorLength = HidDeviceExtension->ConfigurationDescriptor->wTotalLength;
+
+    //
+    // delete partial configuration descriptor
+    //
+    ExFreePool(HidDeviceExtension->ConfigurationDescriptor);
+    HidDeviceExtension->ConfigurationDescriptor = NULL;
+
+    //
+    // get full configuration descriptor
+    //
+    Status = Hid_GetDescriptor(DeviceObject, URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), (PVOID*)&HidDeviceExtension->ConfigurationDescriptor, &DescriptorLength, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0);
+    if (!NT_SUCCESS(Status))
+    {
+        //
+        // failed to obtain device descriptor
+        //
+        DPRINT1("[HIDUSB] failed to get device descriptor %x\n", Status);
+        return Status;
+    }
+
+    //
+    // now parse the descriptors
+    //
+    InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(HidDeviceExtension->ConfigurationDescriptor,
+                                                              HidDeviceExtension->ConfigurationDescriptor,
+                                                             -1,
+                                                             -1,
+                                                              USB_DEVICE_CLASS_HUMAN_INTERFACE,
+                                                             -1,
+                                                             -1);
+    if (!InterfaceDescriptor)
+    {
+        //
+        // no interface class
+        //
+        DPRINT1("[HIDUSB] HID Interface descriptor not found\n");
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    //
+    // sanity check
+    //
+    ASSERT(InterfaceDescriptor->bInterfaceClass == USB_DEVICE_CLASS_HUMAN_INTERFACE);
+    ASSERT(InterfaceDescriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE);
+    ASSERT(InterfaceDescriptor->bLength == sizeof(USB_INTERFACE_DESCRIPTOR));
+
+    //
+    // move to next descriptor
+    //
+    HidDescriptor = (PHID_DESCRIPTOR)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength);
+    ASSERT(HidDescriptor->bLength >= 2);
+
+    //
+    // check if this is the hid descriptor
+    //
+    if (HidDescriptor->bLength == sizeof(HID_DESCRIPTOR) && HidDescriptor->bDescriptorType == HID_HID_DESCRIPTOR_TYPE)
+    {
+        //
+        // found
+        //
+        HidDeviceExtension->HidDescriptor = HidDescriptor;
+
+        //
+        // select configuration
+        //
+        Status = Hid_SelectConfiguration(DeviceObject);
+
+        //
+        // done
+        //
+        DPRINT1("[HIDUSB] SelectConfiguration %x\n", Status);
+
+        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;
+}
+
+
+NTSTATUS
+NTAPI
+HidPnp(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    NTSTATUS Status;
+    PIO_STACK_LOCATION IoStack;
+    PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+    KEVENT Event;
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
+
+    //
+    // get current stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+    DPRINT("[HIDUSB] Pnp %x\n", IoStack->MinorFunction);
+
+    //
+    // handle requests based on request type
+    //
+    switch(IoStack->MinorFunction)
+    {
+        case IRP_MN_REMOVE_DEVICE:
+        {
+            //
+            // pass request onto lower driver
+            //
+            IoSkipCurrentIrpStackLocation(Irp);
+            Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
+            //
+            // free resources
+            //
+            if (HidDeviceExtension->ConfigurationDescriptor)
+            {
+                ExFreePool(HidDeviceExtension->ConfigurationDescriptor);
+                HidDeviceExtension->ConfigurationDescriptor = NULL;
+            }
+
+            //
+            // delete and detach device
+            //
+            IoDetachDevice(DeviceExtension->NextDeviceObject);
+            IoDeleteDevice(DeviceObject);
+
+            return Status;
+        }
+        case IRP_MN_QUERY_PNP_DEVICE_STATE:
+        {
+            //
+            // device can not be disabled
+            //
+            Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE;
+
+            //
+            // pass request to next request
+            //
+            IoSkipCurrentIrpStackLocation(Irp);
+            Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
+
+            //
+            // done
+            //
+            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:
+        {
+            //
+            // FIXME: unconfigure the device
+            //
+
+            //
+            // prepare irp
+            //
+            KeInitializeEvent(&Event, NotificationEvent, FALSE);
+            IoCopyCurrentIrpStackLocationToNext(Irp);
+            IoSetCompletionRoutine(Irp, Hid_PnpCompletion, (PVOID)&Event, TRUE, TRUE, TRUE);
+
+            //
+            // send irp and wait for completion
+            //
+            Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
+            if (Status == STATUS_PENDING)
+            {
+                KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+                Status = Irp->IoStatus.Status;
+            }
+
+            //
+            // free resources
+            //
+            if (HidDeviceExtension->HidDescriptor)
+            {
+                ExFreePool(HidDeviceExtension->HidDescriptor);
+                HidDeviceExtension->HidDescriptor = NULL;
+            }
+
+            //
+            // done
+            //
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return Status;
+        }
+        case IRP_MN_QUERY_CAPABILITIES:
+        {
+            //
+            // prepare irp
+            //
+            KeInitializeEvent(&Event, NotificationEvent, FALSE);
+            IoCopyCurrentIrpStackLocationToNext(Irp);
+            IoSetCompletionRoutine(Irp, Hid_PnpCompletion, (PVOID)&Event, TRUE, TRUE, TRUE);
+
+            //
+            // send irp and wait for completion
+            //
+            Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
+            if (Status == STATUS_PENDING)
+            {
+                KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+                Status = Irp->IoStatus.Status;
+            }
+
+            if (NT_SUCCESS(Status) && IoStack->Parameters.DeviceCapabilities.Capabilities != NULL)
+            {
+                //
+                // don't need to safely remove
+                //
+                IoStack->Parameters.DeviceCapabilities.Capabilities->SurpriseRemovalOK = TRUE;
+            }
+
+            //
+            // done
+            //
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return Status;
+        }
+        case IRP_MN_START_DEVICE:
+        {
+            //
+            // prepare irp
+            //
+            KeInitializeEvent(&Event, NotificationEvent, FALSE);
+            IoCopyCurrentIrpStackLocationToNext(Irp);
+            IoSetCompletionRoutine(Irp, Hid_PnpCompletion, (PVOID)&Event, TRUE, TRUE, TRUE);
+
+            //
+            // send irp and wait for completion
+            //
+            Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
+            if (Status == STATUS_PENDING)
+            {
+                KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+                Status = Irp->IoStatus.Status;
+            }
+
+            //
+            // did the device successfully start
+            //
+            if (!NT_SUCCESS(Status))
+            {
+                //
+                // failed
+                //
+                DPRINT1("HIDUSB: IRP_MN_START_DEVICE failed with %x\n", Status);
+                IoCompleteRequest(Irp, IO_NO_INCREMENT);
+                return Status;
+            }
+
+            //
+            // start device
+            //
+            Status = Hid_PnpStart(DeviceObject);
+
+            //
+            // complete request
+            //
+            Irp->IoStatus.Status = Status;
+            DPRINT("[HIDUSB] IRP_MN_START_DEVICE Status %x\n", Status);
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return Status;
+        }
+        default:
+        {
+             //
+             // forward and forget request
+             //
+             IoSkipCurrentIrpStackLocation(Irp);
+             return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
+        }
+    }
+}
+
+NTSTATUS
+NTAPI
+HidAddDevice(
+    IN PDRIVER_OBJECT DriverObject,
+    IN PDEVICE_OBJECT DeviceObject)
+{
+    PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
+    PHID_DEVICE_EXTENSION DeviceExtension;
+
+    //
+    // get device extension
+    //
+    DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension;
+
+    //
+    // init event
+    //
+    KeInitializeEvent(&HidDeviceExtension->Event, NotificationEvent, FALSE);
+
+    //
+    // done
+    //
+    return STATUS_SUCCESS;
+}
+
+VOID
+NTAPI
+Hid_Unload(
+    IN PDRIVER_OBJECT DriverObject)
+{
+    UNIMPLEMENTED
+}
+
+
 NTSTATUS
 NTAPI
 DriverEntry(
@@ -121,6 +1821,8 @@ DriverEntry(
     DriverObject->MajorFunction[IRP_MJ_POWER] = HidPower;
     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = HidSystemControl;
     DriverObject->MajorFunction[IRP_MJ_PNP] = HidPnp;
+    DriverObject->DriverExtension->AddDevice = HidAddDevice;
+    DriverObject->DriverUnload = Hid_Unload;
 
     //
     // prepare registration info
@@ -144,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;
 }