--- /dev/null
+/*
+ * 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;
+ }
+ }
+}