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