Sync with trunk r63270.
[reactos.git] / drivers / hid / hidclass / hidclass.c
index db03ad9..3011482 100644 (file)
 
 #include "precomp.h"
 
+#define NDEBUG
+#include <debug.h>
+
 static LPWSTR ClientIdentificationAddress = L"HIDCLASS";
 static ULONG HidClassDeviceNumber = 0;
 
-ULONG
+NTSTATUS
 NTAPI
-DllInitialize(ULONG Unknown)
+DllInitialize(
+    IN PUNICODE_STRING RegistryPath)
 {
-    return 0;
+    return STATUS_SUCCESS;
 }
 
-ULONG
+NTSTATUS
 NTAPI
-DllUnload()
+DllUnload(VOID)
 {
-    return 0;
+    return STATUS_SUCCESS;
 }
 
 NTSTATUS
@@ -37,11 +41,10 @@ HidClassAddDevice(
     NTSTATUS Status;
     UNICODE_STRING DeviceName;
     PDEVICE_OBJECT NewDeviceObject;
-    PHIDCLASS_DEVICE_EXTENSION DeviceExtension;
+    PHIDCLASS_FDO_EXTENSION FDODeviceExtension;
     ULONG DeviceExtensionSize;
     PHIDCLASS_DRIVER_EXTENSION DriverExtension;
 
-
     /* increment device number */
     InterlockedIncrement((PLONG)&HidClassDeviceNumber);
 
@@ -52,7 +55,7 @@ HidClassAddDevice(
     RtlInitUnicodeString(&DeviceName, CharDeviceName);
 
     /* get driver object extension */
-    DriverExtension = (PHIDCLASS_DRIVER_EXTENSION) IoGetDriverObjectExtension(DriverObject, ClientIdentificationAddress);
+    DriverExtension = IoGetDriverObjectExtension(DriverObject, ClientIdentificationAddress);
     if (!DriverExtension)
     {
         /* device removed */
@@ -61,7 +64,7 @@ HidClassAddDevice(
     }
 
     /* calculate device extension size */
-    DeviceExtensionSize = sizeof(HIDCLASS_DEVICE_EXTENSION) + DriverExtension->DeviceExtensionSize;
+    DeviceExtensionSize = sizeof(HIDCLASS_FDO_EXTENSION) + DriverExtension->DeviceExtensionSize;
 
     /* now create the device */
     Status = IoCreateDevice(DriverObject, DeviceExtensionSize, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &NewDeviceObject);
@@ -73,20 +76,27 @@ HidClassAddDevice(
     }
 
     /* get device extension */
-    DeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)NewDeviceObject->DeviceExtension;
+    FDODeviceExtension = NewDeviceObject->DeviceExtension;
 
     /* zero device extension */
-    RtlZeroMemory(DeviceExtension, sizeof(HIDCLASS_DEVICE_EXTENSION));
+    RtlZeroMemory(FDODeviceExtension, sizeof(HIDCLASS_FDO_EXTENSION));
 
     /* initialize device extension */
-    DeviceExtension->HidDeviceExtension.PhysicalDeviceObject = PhysicalDeviceObject;
-    DeviceExtension->HidDeviceExtension.MiniDeviceExtension = (PVOID)((ULONG_PTR)DeviceExtension + sizeof(HIDCLASS_DEVICE_EXTENSION));
-    DeviceExtension->HidDeviceExtension.NextDeviceObject = IoAttachDeviceToDeviceStack(NewDeviceObject, PhysicalDeviceObject);
-    DeviceExtension->IsFDO = TRUE;
-    DeviceExtension->DriverExtension = DriverExtension;
+    FDODeviceExtension->Common.IsFDO = TRUE;
+    FDODeviceExtension->Common.DriverExtension = DriverExtension;
+    FDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject = PhysicalDeviceObject;
+    FDODeviceExtension->Common.HidDeviceExtension.MiniDeviceExtension = (PVOID)((ULONG_PTR)FDODeviceExtension + sizeof(HIDCLASS_FDO_EXTENSION));
+    FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject = IoAttachDeviceToDeviceStack(NewDeviceObject, PhysicalDeviceObject);
+    if (FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject == NULL)
+    {
+        /* no PDO */
+        IoDeleteDevice(NewDeviceObject);
+        DPRINT1("[HIDCLASS] failed to attach to device stack\n");
+        return STATUS_DEVICE_REMOVED;
+    }
 
     /* sanity check */
-    ASSERT(DeviceExtension->HidDeviceExtension.NextDeviceObject);
+    ASSERT(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject);
 
     /* increment stack size */
     NewDeviceObject->StackSize++;
@@ -102,7 +112,7 @@ HidClassAddDevice(
     {
         /* failed */
         DPRINT1("HIDCLASS: AddDevice failed with %x\n", Status);
-        IoDetachDevice(DeviceExtension->HidDeviceExtension.NextDeviceObject);
+        IoDetachDevice(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject);
         IoDeleteDevice(NewDeviceObject);
         return Status;
     }
@@ -115,22 +125,973 @@ VOID
 NTAPI
 HidClassDriverUnload(
     IN PDRIVER_OBJECT DriverObject)
+{
+    UNIMPLEMENTED
+}
+
+NTSTATUS
+NTAPI
+HidClass_Create(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PIO_STACK_LOCATION IoStack;
+    PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
+    PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
+    PHIDCLASS_FILEOP_CONTEXT Context;
+
+    //
+    // get device extension
+    //
+    CommonDeviceExtension = DeviceObject->DeviceExtension;
+    if (CommonDeviceExtension->IsFDO)
+    {
+#ifndef __REACTOS__
+
+         //
+         // only supported for PDO
+         //
+         Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
+         IoCompleteRequest(Irp, IO_NO_INCREMENT);
+         return STATUS_UNSUCCESSFUL;
+#else
+         //
+         // ReactOS PnP manager [...]
+         //
+         DPRINT1("[HIDCLASS] PnP HACK\n");
+         Irp->IoStatus.Status = STATUS_SUCCESS;
+         IoCompleteRequest(Irp, IO_NO_INCREMENT);
+         return STATUS_SUCCESS;
+#endif
+    }
+
+    //
+    // must be a PDO
+    //
+    ASSERT(CommonDeviceExtension->IsFDO == FALSE);
+
+    //
+    // get device extension
+    //
+    PDODeviceExtension = DeviceObject->DeviceExtension;
+
+    //
+    // get stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+
+    DPRINT("ShareAccess %x\n", IoStack->Parameters.Create.ShareAccess);
+    DPRINT("Options %x\n", IoStack->Parameters.Create.Options);
+    DPRINT("DesiredAccess %x\n", IoStack->Parameters.Create.SecurityContext->DesiredAccess);
+
+    //
+    // allocate context
+    //
+    Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(HIDCLASS_FILEOP_CONTEXT), HIDCLASS_TAG);
+    if (!Context)
+    {
+        //
+        // no memory
+        //
+        Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // init context
+    //
+    RtlZeroMemory(Context, sizeof(HIDCLASS_FILEOP_CONTEXT));
+    Context->DeviceExtension = PDODeviceExtension;
+    KeInitializeSpinLock(&Context->Lock);
+    InitializeListHead(&Context->ReadPendingIrpListHead);
+    InitializeListHead(&Context->IrpCompletedListHead);
+    KeInitializeEvent(&Context->IrpReadComplete, NotificationEvent, FALSE);
+
+    //
+    // store context
+    //
+    ASSERT(IoStack->FileObject);
+    IoStack->FileObject->FsContext = Context;
+
+    //
+    // done
+    //
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+HidClass_Close(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PIO_STACK_LOCATION IoStack;
+    PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
+    PHIDCLASS_FILEOP_CONTEXT IrpContext;
+    BOOLEAN IsRequestPending = FALSE;
+    KIRQL OldLevel;
+    PLIST_ENTRY Entry;
+    PIRP ListIrp;
+
+    //
+    // get device extension
+    //
+    CommonDeviceExtension = DeviceObject->DeviceExtension;
+
+    //
+    // is it a FDO request
+    //
+    if (CommonDeviceExtension->IsFDO)
+    {
+        //
+        // how did the request get there
+        //
+        Irp->IoStatus.Status = STATUS_INVALID_PARAMETER_1;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        return STATUS_INVALID_PARAMETER_1;
+    }
+
+    //
+    // get stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+
+    //
+    // sanity checks
+    //
+    ASSERT(IoStack->FileObject);
+    ASSERT(IoStack->FileObject->FsContext);
+
+    //
+    // get irp context
+    //
+    IrpContext = IoStack->FileObject->FsContext;
+    ASSERT(IrpContext);
+
+    //
+    // acquire lock
+    //
+    KeAcquireSpinLock(&IrpContext->Lock, &OldLevel);
+
+    if (!IsListEmpty(&IrpContext->ReadPendingIrpListHead))
+    {
+        //
+        // FIXME cancel irp
+        //
+        IsRequestPending = TRUE;
+    }
+
+    //
+    // signal stop
+    //
+    IrpContext->StopInProgress = TRUE;
+
+    //
+    // release lock
+    //
+    KeReleaseSpinLock(&IrpContext->Lock, OldLevel);
+
+    if (IsRequestPending)
+    {
+        //
+        // wait for request to complete
+        //
+        DPRINT1("[HIDCLASS] Waiting for read irp completion...\n");
+        KeWaitForSingleObject(&IrpContext->IrpReadComplete, Executive, KernelMode, FALSE, NULL);
+    }
+
+    //
+    // acquire lock
+    //
+    KeAcquireSpinLock(&IrpContext->Lock, &OldLevel);
+
+    //
+    // sanity check
+    //
+    ASSERT(IsListEmpty(&IrpContext->ReadPendingIrpListHead));
+
+    //
+    // now free all irps
+    //
+    while (!IsListEmpty(&IrpContext->IrpCompletedListHead))
+    {
+        //
+        // remove head irp
+        //
+        Entry = RemoveHeadList(&IrpContext->IrpCompletedListHead);
+
+        //
+        // get irp
+        //
+        ListIrp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
+
+        //
+        // free the irp
+        //
+        IoFreeIrp(ListIrp);
+    }
+
+    //
+    // release lock
+    //
+    KeReleaseSpinLock(&IrpContext->Lock, OldLevel);
+
+    //
+    // remove context
+    //
+    IoStack->FileObject->FsContext = NULL;
+
+    //
+    // free context
+    //
+    ExFreePoolWithTag(IrpContext, HIDCLASS_TAG);
+
+    //
+    // complete request
+    //
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+HidClass_ReadCompleteIrp(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Ctx)
+{
+    PHIDCLASS_IRP_CONTEXT IrpContext;
+    KIRQL OldLevel;
+    PUCHAR Address;
+    ULONG Offset;
+    PHIDP_COLLECTION_DESC CollectionDescription;
+    PHIDP_REPORT_IDS ReportDescription;
+    BOOLEAN IsEmpty;
+
+    //
+    // get irp context
+    //
+    IrpContext = Ctx;
+
+    DPRINT("HidClass_ReadCompleteIrp Irql %lu\n", KeGetCurrentIrql());
+    DPRINT("HidClass_ReadCompleteIrp Status %lx\n", Irp->IoStatus.Status);
+    DPRINT("HidClass_ReadCompleteIrp Length %lu\n", Irp->IoStatus.Information);
+    DPRINT("HidClass_ReadCompleteIrp Irp %p\n", Irp);
+    DPRINT("HidClass_ReadCompleteIrp InputReportBuffer %p\n", IrpContext->InputReportBuffer);
+    DPRINT("HidClass_ReadCompleteIrp InputReportBufferLength %li\n", IrpContext->InputReportBufferLength);
+    DPRINT("HidClass_ReadCompleteIrp OriginalIrp %p\n", IrpContext->OriginalIrp);
+
+    //
+    // copy result
+    //
+    if (Irp->IoStatus.Information)
+    {
+        //
+        // get address
+        //
+        Address = MmGetSystemAddressForMdlSafe(IrpContext->OriginalIrp->MdlAddress, NormalPagePriority);
+        if (Address)
+        {
+            //
+            // reports may have a report id prepended
+            //
+            Offset = 0;
+
+            //
+            // get collection description
+            //
+            CollectionDescription = HidClassPDO_GetCollectionDescription(&IrpContext->FileOp->DeviceExtension->Common.DeviceDescription,
+                                                                         IrpContext->FileOp->DeviceExtension->CollectionNumber);
+            ASSERT(CollectionDescription);
+
+            //
+            // get report description
+            //
+            ReportDescription = HidClassPDO_GetReportDescription(&IrpContext->FileOp->DeviceExtension->Common.DeviceDescription,
+                                                                 IrpContext->FileOp->DeviceExtension->CollectionNumber);
+            ASSERT(ReportDescription);
+
+            if (CollectionDescription && ReportDescription)
+            {
+                //
+                // calculate offset
+                //
+                ASSERT(CollectionDescription->InputLength >= ReportDescription->InputLength);
+                Offset = CollectionDescription->InputLength - ReportDescription->InputLength;
+            }
+
+            //
+            // copy result
+            //
+            RtlCopyMemory(&Address[Offset], IrpContext->InputReportBuffer, IrpContext->InputReportBufferLength);
+        }
+    }
+
+    //
+    // copy result status
+    //
+    IrpContext->OriginalIrp->IoStatus.Status = Irp->IoStatus.Status;
+    IrpContext->OriginalIrp->IoStatus.Information = Irp->IoStatus.Information;
+
+    //
+    // free input report buffer
+    //
+    ExFreePoolWithTag(IrpContext->InputReportBuffer, HIDCLASS_TAG);
+
+    //
+    // remove us from pending list
+    //
+    KeAcquireSpinLock(&IrpContext->FileOp->Lock, &OldLevel);
+
+    //
+    // remove from pending list
+    //
+    RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
+
+    //
+    // is list empty
+    //
+    IsEmpty = IsListEmpty(&IrpContext->FileOp->ReadPendingIrpListHead);
+
+    //
+    // insert into completed list
+    //
+    InsertTailList(&IrpContext->FileOp->IrpCompletedListHead, &Irp->Tail.Overlay.ListEntry);
+
+    //
+    // release lock
+    //
+    KeReleaseSpinLock(&IrpContext->FileOp->Lock, OldLevel);
+
+    //
+    // complete original request
+    //
+    IoCompleteRequest(IrpContext->OriginalIrp, IO_NO_INCREMENT);
+
+
+    DPRINT("StopInProgress %x IsEmpty %x\n", IrpContext->FileOp->StopInProgress, IsEmpty);
+    if (IrpContext->FileOp->StopInProgress && IsEmpty)
+    {
+        //
+        // last pending irp
+        //
+        DPRINT1("[HIDCLASS] LastPendingTransfer Signalling\n");
+        KeSetEvent(&IrpContext->FileOp->IrpReadComplete, 0, FALSE);
+    }
+
+    if (IrpContext->FileOp->StopInProgress && IsEmpty)
+    {
+        //
+        // last pending irp
+        //
+        DPRINT1("[HIDCLASS] LastPendingTransfer Signalling\n");
+        KeSetEvent(&IrpContext->FileOp->IrpReadComplete, 0, FALSE);
+    }
+
+    //
+    // free irp context
+    //
+    ExFreePoolWithTag(IrpContext, HIDCLASS_TAG);
+
+    //
+    // done
+    //
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+PIRP
+HidClass_GetIrp(
+    IN PHIDCLASS_FILEOP_CONTEXT Context)
+{
+   KIRQL OldLevel;
+   PIRP Irp = NULL;
+   PLIST_ENTRY ListEntry;
+
+    //
+    // acquire lock
+    //
+    KeAcquireSpinLock(&Context->Lock, &OldLevel);
+
+    //
+    // is list empty?
+    //
+    if (!IsListEmpty(&Context->IrpCompletedListHead))
+    {
+        //
+        // grab first entry
+        //
+        ListEntry = RemoveHeadList(&Context->IrpCompletedListHead);
+
+        //
+        // get irp
+        //
+        Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
+    }
+
+    //
+    // release lock
+    //
+    KeReleaseSpinLock(&Context->Lock, OldLevel);
+
+    //
+    // done
+    //
+    return Irp;
+}
+
+NTSTATUS
+HidClass_BuildIrp(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP RequestIrp,
+    IN PHIDCLASS_FILEOP_CONTEXT Context,
+    IN ULONG DeviceIoControlCode,
+    IN ULONG BufferLength,
+    OUT PIRP *OutIrp,
+    OUT PHIDCLASS_IRP_CONTEXT *OutIrpContext)
+{
+    PIRP Irp;
+    PIO_STACK_LOCATION IoStack;
+    PHIDCLASS_IRP_CONTEXT IrpContext;
+    PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
+    PHIDP_COLLECTION_DESC CollectionDescription;
+    PHIDP_REPORT_IDS ReportDescription;
+
+    //
+    // get an irp from fresh list
+    //
+    Irp = HidClass_GetIrp(Context);
+    if (!Irp)
+    {
+        //
+        // build new irp
+        //
+        Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
+        if (!Irp)
+        {
+            //
+            // no memory
+            //
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+    else
+    {
+        //
+        // re-use irp
+        //
+        IoReuseIrp(Irp, STATUS_SUCCESS);
+    }
+
+    //
+    // allocate completion context
+    //
+    IrpContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(HIDCLASS_IRP_CONTEXT), HIDCLASS_TAG);
+    if (!IrpContext)
+    {
+        //
+        // no memory
+        //
+        IoFreeIrp(Irp);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // get device extension
+    //
+    PDODeviceExtension = DeviceObject->DeviceExtension;
+    ASSERT(PDODeviceExtension->Common.IsFDO == FALSE);
+
+    //
+    // init irp context
+    //
+    RtlZeroMemory(IrpContext, sizeof(HIDCLASS_IRP_CONTEXT));
+    IrpContext->OriginalIrp = RequestIrp;
+    IrpContext->FileOp = Context;
+
+    //
+    // get collection description
+    //
+    CollectionDescription = HidClassPDO_GetCollectionDescription(&IrpContext->FileOp->DeviceExtension->Common.DeviceDescription,
+                                                                 IrpContext->FileOp->DeviceExtension->CollectionNumber);
+    ASSERT(CollectionDescription);
+
+    //
+    // get report description
+    //
+    ReportDescription = HidClassPDO_GetReportDescription(&IrpContext->FileOp->DeviceExtension->Common.DeviceDescription,
+                                                         IrpContext->FileOp->DeviceExtension->CollectionNumber);
+    ASSERT(ReportDescription);
+
+    //
+    // sanity check
+    //
+    ASSERT(CollectionDescription->InputLength >= ReportDescription->InputLength);
+
+    if (Context->StopInProgress)
+    {
+         //
+         // stop in progress
+         //
+         DPRINT1("[HIDCLASS] Stop In Progress\n");
+         Irp->IoStatus.Status = STATUS_CANCELLED;
+         IoCompleteRequest(Irp, IO_NO_INCREMENT);
+         return STATUS_CANCELLED;
+
+    }
+
+    //
+    // store report length
+    //
+    IrpContext->InputReportBufferLength = ReportDescription->InputLength;
+
+    //
+    // allocate buffer
+    //
+    IrpContext->InputReportBuffer = ExAllocatePoolWithTag(NonPagedPool, IrpContext->InputReportBufferLength, HIDCLASS_TAG);
+    if (!IrpContext->InputReportBuffer)
+    {
+        //
+        // no memory
+        //
+        IoFreeIrp(Irp);
+        ExFreePoolWithTag(IrpContext, HIDCLASS_TAG);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // get stack location
+    //
+    IoStack = IoGetNextIrpStackLocation(Irp);
+
+    //
+    // init stack location
+    //
+    IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+    IoStack->Parameters.DeviceIoControl.IoControlCode = DeviceIoControlCode;
+    IoStack->Parameters.DeviceIoControl.OutputBufferLength = IrpContext->InputReportBufferLength;
+    IoStack->Parameters.DeviceIoControl.InputBufferLength = 0;
+    IoStack->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
+    Irp->UserBuffer = IrpContext->InputReportBuffer;
+    IoStack->DeviceObject = DeviceObject;
+
+    //
+    // store result
+    //
+    *OutIrp = Irp;
+    *OutIrpContext = IrpContext;
+
+    //
+    // done
+    //
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+HidClass_Read(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PIO_STACK_LOCATION IoStack;
+    PHIDCLASS_FILEOP_CONTEXT Context;
+    KIRQL OldLevel;
+    NTSTATUS Status;
+    PIRP NewIrp;
+    PHIDCLASS_IRP_CONTEXT NewIrpContext;
+    PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
+
+    //
+    // get current stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+
+    //
+    // get device extension
+    //
+    CommonDeviceExtension = DeviceObject->DeviceExtension;
+    ASSERT(CommonDeviceExtension->IsFDO == FALSE);
+
+    //
+    // sanity check
+    //
+    ASSERT(IoStack->FileObject);
+    ASSERT(IoStack->FileObject->FsContext);
+
+    //
+    // get context
+    //
+    Context = IoStack->FileObject->FsContext;
+    ASSERT(Context);
+
+    //
+    // FIXME support polled devices
+    //
+    ASSERT(Context->DeviceExtension->Common.DriverExtension->DevicesArePolled == FALSE);
+
+    if (Context->StopInProgress)
+    {
+        //
+        // stop in progress
+        //
+        DPRINT1("[HIDCLASS] Stop In Progress\n");
+        Irp->IoStatus.Status = STATUS_CANCELLED;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        return STATUS_CANCELLED;
+    }
+
+    //
+    // build irp request
+    //
+    Status = HidClass_BuildIrp(DeviceObject,
+                               Irp,
+                               Context,
+                               IOCTL_HID_READ_REPORT,
+                               IoStack->Parameters.Read.Length,
+                               &NewIrp,
+                               &NewIrpContext);
+    if (!NT_SUCCESS(Status))
+    {
+        //
+        // failed
+        //
+        DPRINT1("HidClass_BuildIrp failed with %x\n", Status);
+        Irp->IoStatus.Status = Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        return Status;
+    }
+
+    //
+    // acquire lock
+    //
+    KeAcquireSpinLock(&Context->Lock, &OldLevel);
+
+    //
+    // insert irp into pending list
+    //
+    InsertTailList(&Context->ReadPendingIrpListHead, &NewIrp->Tail.Overlay.ListEntry);
+
+    //
+    // set completion routine
+    //
+    IoSetCompletionRoutine(NewIrp, HidClass_ReadCompleteIrp, NewIrpContext, TRUE, TRUE, TRUE);
+
+    //
+    // make next location current
+    //
+    IoSetNextIrpStackLocation(NewIrp);
+
+    //
+    // release spin lock
+    //
+    KeReleaseSpinLock(&Context->Lock, OldLevel);
+
+    //
+    // mark irp pending
+    //
+    IoMarkIrpPending(Irp);
+
+    //
+    // let's dispatch the request
+    //
+    ASSERT(Context->DeviceExtension);
+    Status = Context->DeviceExtension->Common.DriverExtension->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL](Context->DeviceExtension->FDODeviceObject, NewIrp);
+
+    //
+    // complete
+    //
+    return STATUS_PENDING;
+}
+
+NTSTATUS
+NTAPI
+HidClass_Write(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
 {
     UNIMPLEMENTED
     ASSERT(FALSE);
+    Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    return STATUS_NOT_IMPLEMENTED;
 }
 
 NTSTATUS
 NTAPI
-HidClassDispatch(
+HidClass_DeviceControl(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PIO_STACK_LOCATION IoStack;
+    PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
+    PHID_COLLECTION_INFORMATION CollectionInformation;
+    PHIDP_COLLECTION_DESC CollectionDescription;
+    PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
+
+    //
+    // get device extension
+    //
+    CommonDeviceExtension = DeviceObject->DeviceExtension;
+
+    //
+    // only PDO are supported
+    //
+    if (CommonDeviceExtension->IsFDO)
+    {
+        //
+        // invalid request
+        //
+        DPRINT1("[HIDCLASS] DeviceControl Irp for FDO arrived\n");
+        Irp->IoStatus.Status = STATUS_INVALID_PARAMETER_1;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        return STATUS_INVALID_PARAMETER_1;
+    }
+
+    ASSERT(CommonDeviceExtension->IsFDO == FALSE);
+
+    //
+    // get pdo device extension
+    //
+    PDODeviceExtension = DeviceObject->DeviceExtension;
+
+    //
+    // get stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+
+    switch (IoStack->Parameters.DeviceIoControl.IoControlCode)
+    {
+        case IOCTL_HID_GET_COLLECTION_INFORMATION:
+        {
+            //
+            // check if output buffer is big enough
+            //
+            if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_COLLECTION_INFORMATION))
+            {
+                //
+                // invalid buffer size
+                //
+                Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
+                IoCompleteRequest(Irp, IO_NO_INCREMENT);
+                return STATUS_INVALID_BUFFER_SIZE;
+            }
+
+            //
+            // get output buffer
+            //
+            CollectionInformation = Irp->AssociatedIrp.SystemBuffer;
+            ASSERT(CollectionInformation);
+
+            //
+            // get collection description
+            //
+            CollectionDescription = HidClassPDO_GetCollectionDescription(&CommonDeviceExtension->DeviceDescription,
+                                                                         PDODeviceExtension->CollectionNumber);
+            ASSERT(CollectionDescription);
+
+            //
+            // init result buffer
+            //
+            CollectionInformation->DescriptorSize = CollectionDescription->PreparsedDataLength;
+            CollectionInformation->Polled = CommonDeviceExtension->DriverExtension->DevicesArePolled;
+            CollectionInformation->VendorID = CommonDeviceExtension->Attributes.VendorID;
+            CollectionInformation->ProductID = CommonDeviceExtension->Attributes.ProductID;
+            CollectionInformation->VersionNumber = CommonDeviceExtension->Attributes.VersionNumber;
+
+            //
+            // complete request
+            //
+            Irp->IoStatus.Information = sizeof(HID_COLLECTION_INFORMATION);
+            Irp->IoStatus.Status = STATUS_SUCCESS;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_SUCCESS;
+        }
+        case IOCTL_HID_GET_COLLECTION_DESCRIPTOR:
+        {
+            //
+            // get collection description
+            //
+            CollectionDescription = HidClassPDO_GetCollectionDescription(&CommonDeviceExtension->DeviceDescription,
+                                                                         PDODeviceExtension->CollectionNumber);
+            ASSERT(CollectionDescription);
+
+            //
+            // check if output buffer is big enough
+            //
+            if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < CollectionDescription->PreparsedDataLength)
+            {
+                //
+                // invalid buffer size
+                //
+                Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
+                IoCompleteRequest(Irp, IO_NO_INCREMENT);
+                return STATUS_INVALID_BUFFER_SIZE;
+            }
+
+            //
+            // copy result
+            //
+            ASSERT(Irp->UserBuffer);
+            RtlCopyMemory(Irp->UserBuffer, CollectionDescription->PreparsedData, CollectionDescription->PreparsedDataLength);
+
+            //
+            // complete request
+            //
+            Irp->IoStatus.Information = CollectionDescription->PreparsedDataLength;
+            Irp->IoStatus.Status = STATUS_SUCCESS;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_SUCCESS;
+        }
+        default:
+        {
+            DPRINT1("[HIDCLASS] DeviceControl IoControlCode 0x%x not implemented\n", IoStack->Parameters.DeviceIoControl.IoControlCode);
+            Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_NOT_IMPLEMENTED;
+        }
+    }
+}
+
+NTSTATUS
+NTAPI
+HidClass_InternalDeviceControl(
     IN PDEVICE_OBJECT DeviceObject,
     IN PIRP Irp)
 {
     UNIMPLEMENTED
     ASSERT(FALSE);
+    Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
     return STATUS_NOT_IMPLEMENTED;
 }
 
+NTSTATUS
+NTAPI
+HidClass_Power(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
+    CommonDeviceExtension = DeviceObject->DeviceExtension;
+    if (CommonDeviceExtension->IsFDO)
+    {
+        IoCopyCurrentIrpStackLocationToNext(Irp);
+        return HidClassFDO_DispatchRequest(DeviceObject, Irp);
+    }
+    else
+    {
+        Irp->IoStatus.Status = STATUS_SUCCESS;
+        PoStartNextPowerIrp(Irp);
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        return STATUS_SUCCESS;
+    }
+}
+
+NTSTATUS
+NTAPI
+HidClass_PnP(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
+
+    //
+    // get common device extension
+    //
+    CommonDeviceExtension = DeviceObject->DeviceExtension;
+
+    //
+    // check type of device object
+    //
+    if (CommonDeviceExtension->IsFDO)
+    {
+        //
+        // handle request
+        //
+        return HidClassFDO_PnP(DeviceObject, Irp);
+    }
+    else
+    {
+        //
+        // handle request
+        //
+        return HidClassPDO_PnP(DeviceObject, Irp);
+    }
+}
+
+NTSTATUS
+NTAPI
+HidClass_DispatchDefault(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
+
+    //
+    // get common device extension
+    //
+    CommonDeviceExtension = DeviceObject->DeviceExtension;
+
+    //
+    // FIXME: support PDO
+    //
+    ASSERT(CommonDeviceExtension->IsFDO == TRUE);
+
+    //
+    // skip current irp stack location
+    //
+    IoSkipCurrentIrpStackLocation(Irp);
+
+    //
+    // dispatch to lower device object
+    //
+    return IoCallDriver(CommonDeviceExtension->HidDeviceExtension.NextDeviceObject, Irp);
+}
+
+NTSTATUS
+NTAPI
+HidClassDispatch(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PIO_STACK_LOCATION IoStack;
+
+    //
+    // get current stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+    DPRINT("[HIDCLASS] Dispatch Major %x Minor %x\n", IoStack->MajorFunction, IoStack->MinorFunction);
+
+    //
+    // dispatch request based on major function
+    //
+    switch (IoStack->MajorFunction)
+    {
+        case IRP_MJ_CREATE:
+            return HidClass_Create(DeviceObject, Irp);
+        case IRP_MJ_CLOSE:
+            return HidClass_Close(DeviceObject, Irp);
+        case IRP_MJ_READ:
+            return HidClass_Read(DeviceObject, Irp);
+        case IRP_MJ_WRITE:
+            return HidClass_Write(DeviceObject, Irp);
+        case IRP_MJ_DEVICE_CONTROL:
+            return HidClass_DeviceControl(DeviceObject, Irp);
+        case IRP_MJ_INTERNAL_DEVICE_CONTROL:
+           return HidClass_InternalDeviceControl(DeviceObject, Irp);
+        case IRP_MJ_POWER:
+            return HidClass_Power(DeviceObject, Irp);
+        case IRP_MJ_PNP:
+            return HidClass_PnP(DeviceObject, Irp);
+        default:
+            return HidClass_DispatchDefault(DeviceObject, Irp);
+    }
+}
+
 NTSTATUS
 NTAPI
 HidRegisterMinidriver(
@@ -148,7 +1109,10 @@ HidRegisterMinidriver(
     }
 
     /* now allocate the driver object extension */
-    Status = IoAllocateDriverObjectExtension(MinidriverRegistration->DriverObject, (PVOID)ClientIdentificationAddress, sizeof(HIDCLASS_DRIVER_EXTENSION), (PVOID*)&DriverExtension);
+    Status = IoAllocateDriverObjectExtension(MinidriverRegistration->DriverObject,
+                                             ClientIdentificationAddress,
+                                             sizeof(HIDCLASS_DRIVER_EXTENSION),
+                                             (PVOID *)&DriverExtension);
     if (!NT_SUCCESS(Status))
     {
         /* failed to allocate driver extension */
@@ -167,7 +1131,9 @@ HidRegisterMinidriver(
     DriverExtension->DriverUnload = MinidriverRegistration->DriverObject->DriverUnload;
 
     /* copy driver dispatch routines */
-    RtlCopyMemory(DriverExtension->MajorFunction, MinidriverRegistration->DriverObject->MajorFunction, sizeof(PDRIVER_DISPATCH) * IRP_MJ_MAXIMUM_FUNCTION);
+    RtlCopyMemory(DriverExtension->MajorFunction,
+                  MinidriverRegistration->DriverObject->MajorFunction,
+                  sizeof(PDRIVER_DISPATCH) * (IRP_MJ_MAXIMUM_FUNCTION + 1));
 
     /* initialize lock */
     KeInitializeSpinLock(&DriverExtension->Lock);