Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / drivers / hid / hidclass / fdo.c
diff --git a/drivers/hid/hidclass/fdo.c b/drivers/hid/hidclass/fdo.c
new file mode 100644 (file)
index 0000000..8ea6590
--- /dev/null
@@ -0,0 +1,683 @@
+/*
+ * PROJECT:     ReactOS Universal Serial Bus Human Interface Device Driver
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        drivers/hid/hidclass/fdo.c
+ * PURPOSE:     HID Class Driver
+ * PROGRAMMERS:
+ *              Michael Martin (michael.martin@reactos.org)
+ *              Johannes Anderwald (johannes.anderwald@reactos.org)
+ */
+
+#include "precomp.h"
+
+#define NDEBUG
+#include <debug.h>
+
+NTSTATUS
+NTAPI
+HidClassFDO_QueryCapabilitiesCompletionRoutine(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Context)
+{
+    //
+    // set event
+    //
+    KeSetEvent(Context, 0, FALSE);
+
+    //
+    // completion is done in the HidClassFDO_QueryCapabilities routine
+    //
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+NTSTATUS
+HidClassFDO_QueryCapabilities(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN OUT PDEVICE_CAPABILITIES Capabilities)
+{
+    PIRP Irp;
+    KEVENT Event;
+    NTSTATUS Status;
+    PIO_STACK_LOCATION IoStack;
+    PHIDCLASS_FDO_EXTENSION FDODeviceExtension;
+
+    //
+    // get device extension
+    //
+    FDODeviceExtension = DeviceObject->DeviceExtension;
+    ASSERT(FDODeviceExtension->Common.IsFDO);
+
+    //
+    // init event
+    //
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    //
+    // now allocate the irp
+    //
+    Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
+    if (!Irp)
+    {
+        //
+        // no memory
+        //
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // get next stack location
+    //
+    IoStack = IoGetNextIrpStackLocation(Irp);
+
+    //
+    // init stack location
+    //
+    IoStack->MajorFunction = IRP_MJ_PNP;
+    IoStack->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
+    IoStack->Parameters.DeviceCapabilities.Capabilities = Capabilities;
+
+    //
+    // set completion routine
+    //
+    IoSetCompletionRoutine(Irp, HidClassFDO_QueryCapabilitiesCompletionRoutine, &Event, TRUE, TRUE, TRUE);
+
+    //
+    // init capabilities
+    //
+    RtlZeroMemory(Capabilities, sizeof(DEVICE_CAPABILITIES));
+    Capabilities->Size = sizeof(DEVICE_CAPABILITIES);
+    Capabilities->Version = 1; // FIXME hardcoded constant
+    Capabilities->Address = MAXULONG;
+    Capabilities->UINumber = MAXULONG;
+
+    //
+    // pnp irps have default completion code
+    //
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+    //
+    // call lower  device
+    //
+    Status = IoCallDriver(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject, Irp);
+    if (Status == STATUS_PENDING)
+    {
+        //
+        // wait for completion
+        //
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+    }
+
+    //
+    // get status
+    //
+    Status = Irp->IoStatus.Status;
+
+    //
+    // complete request
+    //
+    IoFreeIrp(Irp);
+
+    //
+    // done
+    //
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+HidClassFDO_DispatchRequestSynchronousCompletion(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Context)
+{
+    //
+    // signal event
+    //
+    KeSetEvent(Context, 0, FALSE);
+
+    //
+    // done
+    //
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+
+NTSTATUS
+HidClassFDO_DispatchRequestSynchronous(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    KEVENT Event;
+    PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
+    NTSTATUS Status;
+    PIO_STACK_LOCATION IoStack;
+
+    //
+    // init event
+    //
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    //
+    // get device extension
+    //
+    CommonDeviceExtension = DeviceObject->DeviceExtension;
+
+    //
+    // set completion routine
+    //
+    IoSetCompletionRoutine(Irp, HidClassFDO_DispatchRequestSynchronousCompletion, &Event, TRUE, TRUE, TRUE);
+
+    ASSERT(Irp->CurrentLocation > 0);
+    //
+    // create stack location
+    //
+    IoSetNextIrpStackLocation(Irp);
+
+    //
+    // get next stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+
+    //
+    // store device object
+    //
+    IoStack->DeviceObject = DeviceObject;
+
+    //
+    // sanity check
+    //
+    ASSERT(CommonDeviceExtension->DriverExtension->MajorFunction[IoStack->MajorFunction] != NULL);
+
+    //
+    // call minidriver (hidusb)
+    //
+    Status = CommonDeviceExtension->DriverExtension->MajorFunction[IoStack->MajorFunction](DeviceObject, Irp);
+
+    //
+    // wait for the request to finish
+    //
+    if (Status == STATUS_PENDING)
+    {
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+
+        //
+        // update status
+        //
+        Status = Irp->IoStatus.Status;
+    }
+
+    //
+    // done
+    //
+    return Status;
+}
+
+NTSTATUS
+HidClassFDO_DispatchRequest(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
+    NTSTATUS Status;
+    PIO_STACK_LOCATION IoStack;
+
+    //
+    // get device extension
+    //
+    CommonDeviceExtension = DeviceObject->DeviceExtension;
+
+    ASSERT(Irp->CurrentLocation > 0);
+
+    //
+    // create stack location
+    //
+    IoSetNextIrpStackLocation(Irp);
+
+    //
+    // get next stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+
+    //
+    // store device object
+    //
+    IoStack->DeviceObject = DeviceObject;
+
+    //
+    // sanity check
+    //
+    ASSERT(CommonDeviceExtension->DriverExtension->MajorFunction[IoStack->MajorFunction] != NULL);
+
+    //
+    // call driver
+    //
+    Status = CommonDeviceExtension->DriverExtension->MajorFunction[IoStack->MajorFunction](DeviceObject, Irp);
+
+    //
+    // done
+    //
+    return Status;
+}
+
+NTSTATUS
+HidClassFDO_GetDescriptors(
+    IN PDEVICE_OBJECT DeviceObject)
+{
+    PHIDCLASS_FDO_EXTENSION FDODeviceExtension;
+    PIRP Irp;
+    PIO_STACK_LOCATION IoStack;
+    NTSTATUS Status;
+
+    //
+    // get device extension
+    //
+    FDODeviceExtension = DeviceObject->DeviceExtension;
+    ASSERT(FDODeviceExtension->Common.IsFDO);
+
+    //
+    // let's allocate irp
+    //
+    Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
+    if (!Irp)
+    {
+        //
+        // no memory
+        //
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // get stack location
+    //
+    IoStack = IoGetNextIrpStackLocation(Irp);
+
+    //
+    // init stack location
+    //
+    IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+    IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_GET_DEVICE_DESCRIPTOR;
+    IoStack->Parameters.DeviceIoControl.OutputBufferLength = sizeof(HID_DESCRIPTOR);
+    IoStack->Parameters.DeviceIoControl.InputBufferLength = 0;
+    IoStack->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
+    Irp->UserBuffer = &FDODeviceExtension->HidDescriptor;
+
+    //
+    // send request
+    //
+    Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp);
+    if (!NT_SUCCESS(Status))
+    {
+        //
+        // failed to get device descriptor
+        //
+        DPRINT1("[HIDCLASS] IOCTL_HID_GET_DEVICE_DESCRIPTOR failed with %x\n", Status);
+        IoFreeIrp(Irp);
+        return Status;
+    }
+
+    //
+    // let's get device attributes
+    //
+    IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_GET_DEVICE_ATTRIBUTES;
+    IoStack->Parameters.DeviceIoControl.OutputBufferLength = sizeof(HID_DEVICE_ATTRIBUTES);
+    Irp->UserBuffer = &FDODeviceExtension->Common.Attributes;
+
+    //
+    // send request
+    //
+    Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp);
+    if (!NT_SUCCESS(Status))
+    {
+        //
+        // failed to get device descriptor
+        //
+        DPRINT1("[HIDCLASS] IOCTL_HID_GET_DEVICE_ATTRIBUTES failed with %x\n", Status);
+        IoFreeIrp(Irp);
+        return Status;
+    }
+
+    //
+    // sanity checks
+    //
+    ASSERT(FDODeviceExtension->HidDescriptor.bLength == sizeof(HID_DESCRIPTOR));
+    ASSERT(FDODeviceExtension->HidDescriptor.bNumDescriptors > 0);
+    ASSERT(FDODeviceExtension->HidDescriptor.DescriptorList[0].wReportLength > 0);
+    ASSERT(FDODeviceExtension->HidDescriptor.DescriptorList[0].bReportType == HID_REPORT_DESCRIPTOR_TYPE);
+
+    //
+    // now allocate space for the report descriptor
+    //
+    FDODeviceExtension->ReportDescriptor = ExAllocatePoolWithTag(NonPagedPool,
+                                                                 FDODeviceExtension->HidDescriptor.DescriptorList[0].wReportLength,
+                                                                 HIDCLASS_TAG);
+    if (!FDODeviceExtension->ReportDescriptor)
+    {
+        //
+        // not enough memory
+        //
+        IoFreeIrp(Irp);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // init stack location
+    //
+    IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_GET_REPORT_DESCRIPTOR;
+    IoStack->Parameters.DeviceIoControl.OutputBufferLength = FDODeviceExtension->HidDescriptor.DescriptorList[0].wReportLength;
+    Irp->UserBuffer = FDODeviceExtension->ReportDescriptor;
+
+    //
+    // send request
+    //
+    Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp);
+    if (!NT_SUCCESS(Status))
+    {
+        //
+        // failed to get device descriptor
+        //
+        DPRINT1("[HIDCLASS] IOCTL_HID_GET_REPORT_DESCRIPTOR failed with %x\n", Status);
+        IoFreeIrp(Irp);
+        return Status;
+    }
+
+    //
+    // completed successfully
+    //
+    IoFreeIrp(Irp);
+    return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+HidClassFDO_StartDevice(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    NTSTATUS Status;
+    PHIDCLASS_FDO_EXTENSION FDODeviceExtension;
+
+    //
+    // get device extension
+    //
+    FDODeviceExtension = DeviceObject->DeviceExtension;
+    ASSERT(FDODeviceExtension->Common.IsFDO);
+
+    //
+    // query capabilities
+    //
+    Status = HidClassFDO_QueryCapabilities(DeviceObject, &FDODeviceExtension->Capabilities);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("[HIDCLASS] Failed to retrieve capabilities %x\n", Status);
+        Irp->IoStatus.Status = Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        return Status;
+    }
+
+    //
+    // let's start the lower device too
+    //
+    IoSkipCurrentIrpStackLocation(Irp);
+    Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("[HIDCLASS] Failed to start lower device with %x\n", Status);
+        Irp->IoStatus.Status = Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        return Status;
+    }
+
+    //
+    // let's get the descriptors
+    //
+    Status = HidClassFDO_GetDescriptors(DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("[HIDCLASS] Failed to retrieve the descriptors %x\n", Status);
+        Irp->IoStatus.Status = Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        return Status;
+    }
+
+    //
+    // now get the the collection description
+    //
+    Status = HidP_GetCollectionDescription(FDODeviceExtension->ReportDescriptor, FDODeviceExtension->HidDescriptor.DescriptorList[0].wReportLength, NonPagedPool, &FDODeviceExtension->Common.DeviceDescription);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("[HIDCLASS] Failed to retrieve the collection description %x\n", Status);
+        Irp->IoStatus.Status = Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        return Status;
+    }
+
+    //
+    // complete request
+    //
+    Irp->IoStatus.Status = Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    return Status;
+}
+
+NTSTATUS
+HidClassFDO_RemoveDevice(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    NTSTATUS Status;
+    PHIDCLASS_FDO_EXTENSION FDODeviceExtension;
+
+    //
+    // get device extension
+    //
+    FDODeviceExtension = DeviceObject->DeviceExtension;
+    ASSERT(FDODeviceExtension->Common.IsFDO);
+
+    /* FIXME cleanup */
+
+    //
+    // dispatch to minidriver
+    //
+    IoSkipCurrentIrpStackLocation(Irp);
+    Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp);
+
+    //
+    // complete request
+    //
+    Irp->IoStatus.Status = Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    //
+    // detach and delete device
+    //
+    IoDetachDevice(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject);
+    IoDeleteDevice(DeviceObject);
+
+    return Status;
+}
+
+NTSTATUS
+HidClassFDO_CopyDeviceRelations(
+    IN PDEVICE_OBJECT DeviceObject,
+    OUT PDEVICE_RELATIONS *OutRelations)
+{
+    PDEVICE_RELATIONS DeviceRelations;
+    PHIDCLASS_FDO_EXTENSION FDODeviceExtension;
+    ULONG Index;
+
+    //
+    // get device extension
+    //
+    FDODeviceExtension = DeviceObject->DeviceExtension;
+    ASSERT(FDODeviceExtension->Common.IsFDO);
+
+    //
+    // allocate result
+    //
+    DeviceRelations = ExAllocatePoolWithTag(NonPagedPool,
+                                            sizeof(DEVICE_RELATIONS) + (FDODeviceExtension->DeviceRelations->Count - 1) * sizeof(PDEVICE_OBJECT),
+                                            HIDCLASS_TAG);
+    if (!DeviceRelations)
+    {
+        //
+        // no memory
+        //
+        *OutRelations = NULL;
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // copy device objects
+    //
+    for (Index = 0; Index < FDODeviceExtension->DeviceRelations->Count; Index++)
+    {
+        //
+        // reference pdo
+        //
+        ObReferenceObject(FDODeviceExtension->DeviceRelations->Objects[Index]);
+
+        //
+        // store object
+        //
+        DeviceRelations->Objects[Index] = FDODeviceExtension->DeviceRelations->Objects[Index];
+    }
+
+    //
+    // set object count
+    //
+    DeviceRelations->Count = FDODeviceExtension->DeviceRelations->Count;
+
+    //
+    // store result
+    //
+    *OutRelations = DeviceRelations;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+HidClassFDO_DeviceRelations(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PHIDCLASS_FDO_EXTENSION FDODeviceExtension;
+    PIO_STACK_LOCATION IoStack;
+    NTSTATUS Status;
+    PDEVICE_RELATIONS DeviceRelations;
+
+    //
+    // get device extension
+    //
+    FDODeviceExtension = DeviceObject->DeviceExtension;
+    ASSERT(FDODeviceExtension->Common.IsFDO);
+
+    //
+    // get current irp stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+
+    //
+    // check relations type
+    //
+    if (IoStack->Parameters.QueryDeviceRelations.Type != BusRelations)
+    {
+        //
+        // only bus relations are handled
+        //
+        IoSkipCurrentIrpStackLocation(Irp);
+        return IoCallDriver(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject, Irp);
+    }
+
+    if (FDODeviceExtension->DeviceRelations == NULL)
+    {
+        //
+        // time to create the pdos
+        //
+        Status = HidClassPDO_CreatePDO(DeviceObject, &FDODeviceExtension->DeviceRelations);
+        if (!NT_SUCCESS(Status))
+        {
+            //
+            // failed
+            //
+            DPRINT1("[HIDCLASS] HidClassPDO_CreatePDO failed with %x\n", Status);
+            Irp->IoStatus.Status = Status;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_SUCCESS;
+        }
+        //
+        // sanity check
+        //
+        ASSERT(FDODeviceExtension->DeviceRelations->Count > 0);
+    }
+
+    //
+    // now copy device relations
+    //
+    Status = HidClassFDO_CopyDeviceRelations(DeviceObject, &DeviceRelations);
+    //
+    // store result
+    //
+    Irp->IoStatus.Status = Status;
+    Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
+
+    //
+    // complete request
+    //
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    return Status;
+}
+
+NTSTATUS
+HidClassFDO_PnP(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PIO_STACK_LOCATION IoStack;
+    PHIDCLASS_FDO_EXTENSION FDODeviceExtension;
+    NTSTATUS Status;
+
+    //
+    // get device extension
+    //
+    FDODeviceExtension = DeviceObject->DeviceExtension;
+    ASSERT(FDODeviceExtension->Common.IsFDO);
+
+    //
+    // get current irp stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+    switch (IoStack->MinorFunction)
+    {
+        case IRP_MN_START_DEVICE:
+        {
+             return HidClassFDO_StartDevice(DeviceObject, Irp);
+        }
+        case IRP_MN_REMOVE_DEVICE:
+        {
+             return HidClassFDO_RemoveDevice(DeviceObject, Irp);
+        }
+        case IRP_MN_QUERY_DEVICE_RELATIONS:
+        {
+             return HidClassFDO_DeviceRelations(DeviceObject, Irp);
+        }
+        case IRP_MN_QUERY_REMOVE_DEVICE:
+        case IRP_MN_QUERY_STOP_DEVICE:
+        case IRP_MN_CANCEL_REMOVE_DEVICE:
+        case IRP_MN_CANCEL_STOP_DEVICE:
+        {
+            //
+            // set status to success and fall through
+            //
+            Irp->IoStatus.Status = STATUS_SUCCESS;
+        }
+        default:
+        {
+            //
+            // dispatch to mini driver
+            //
+           IoCopyCurrentIrpStackLocationToNext(Irp);
+           Status = HidClassFDO_DispatchRequest(DeviceObject, Irp);
+           return Status;
+        }
+    }
+}