/* * PROJECT: ReactOS Universal Serial Bus Human Interface Device Driver * LICENSE: GPL - See COPYING in the top level directory * FILE: drivers/hid/hidclass/hidclass.c * PURPOSE: HID Class Driver * PROGRAMMERS: * Michael Martin (michael.martin@reactos.org) * Johannes Anderwald (johannes.anderwald@reactos.org) */ #include "precomp.h" #define NDEBUG #include static LPWSTR ClientIdentificationAddress = L"HIDCLASS"; static ULONG HidClassDeviceNumber = 0; NTSTATUS NTAPI DllInitialize( IN PUNICODE_STRING RegistryPath) { return STATUS_SUCCESS; } NTSTATUS NTAPI DllUnload(VOID) { return STATUS_SUCCESS; } NTSTATUS NTAPI HidClassAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject) { WCHAR CharDeviceName[64]; NTSTATUS Status; UNICODE_STRING DeviceName; PDEVICE_OBJECT NewDeviceObject; PHIDCLASS_FDO_EXTENSION FDODeviceExtension; ULONG DeviceExtensionSize; PHIDCLASS_DRIVER_EXTENSION DriverExtension; /* increment device number */ InterlockedIncrement((PLONG)&HidClassDeviceNumber); /* construct device name */ swprintf(CharDeviceName, L"\\Device\\_HID%08x", HidClassDeviceNumber); /* initialize device name */ RtlInitUnicodeString(&DeviceName, CharDeviceName); /* get driver object extension */ DriverExtension = IoGetDriverObjectExtension(DriverObject, ClientIdentificationAddress); if (!DriverExtension) { /* device removed */ ASSERT(FALSE); return STATUS_DEVICE_CONFIGURATION_ERROR; } /* calculate device extension size */ DeviceExtensionSize = sizeof(HIDCLASS_FDO_EXTENSION) + DriverExtension->DeviceExtensionSize; /* now create the device */ Status = IoCreateDevice(DriverObject, DeviceExtensionSize, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &NewDeviceObject); if (!NT_SUCCESS(Status)) { /* failed to create device object */ ASSERT(FALSE); return Status; } /* get device extension */ FDODeviceExtension = NewDeviceObject->DeviceExtension; /* zero device extension */ RtlZeroMemory(FDODeviceExtension, sizeof(HIDCLASS_FDO_EXTENSION)); /* initialize device extension */ 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(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject); /* increment stack size */ NewDeviceObject->StackSize++; /* init device object */ NewDeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE; NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; /* now call driver provided add device routine */ ASSERT(DriverExtension->AddDevice != 0); Status = DriverExtension->AddDevice(DriverObject, NewDeviceObject); if (!NT_SUCCESS(Status)) { /* failed */ DPRINT1("HIDCLASS: AddDevice failed with %x\n", Status); IoDetachDevice(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject); IoDeleteDevice(NewDeviceObject); return Status; } /* succeeded */ return Status; } 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) { // // only supported for PDO // Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_UNSUCCESSFUL; } // // 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) { PIO_STACK_LOCATION IoStack; PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension; PIRP SubIrp; KEVENT Event; IO_STATUS_BLOCK IoStatusBlock; HID_XFER_PACKET XferPacket; NTSTATUS Status; ULONG Length; IoStack = IoGetCurrentIrpStackLocation(Irp); Length = IoStack->Parameters.Write.Length; if (Length < 1) { Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INVALID_PARAMETER; } RtlZeroMemory(&XferPacket, sizeof(XferPacket)); XferPacket.reportBufferLen = Length; XferPacket.reportBuffer = Irp->UserBuffer; XferPacket.reportId = XferPacket.reportBuffer[0]; CommonDeviceExtension = DeviceObject->DeviceExtension; SubIrp = IoBuildDeviceIoControlRequest( IOCTL_HID_WRITE_REPORT, CommonDeviceExtension->HidDeviceExtension.NextDeviceObject, NULL, 0, NULL, 0, TRUE, &Event, &IoStatusBlock); if (!SubIrp) { Irp->IoStatus.Status = STATUS_NO_MEMORY; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_NOT_IMPLEMENTED; } SubIrp->UserBuffer = &XferPacket; KeInitializeEvent(&Event, SynchronizationEvent, FALSE); Status = IoCallDriver(CommonDeviceExtension->HidDeviceExtension.NextDeviceObject, SubIrp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; } NTSTATUS NTAPI 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; } case IOCTL_HID_GET_FEATURE: { PIRP SubIrp; KEVENT Event; IO_STATUS_BLOCK IoStatusBlock; HID_XFER_PACKET XferPacket; NTSTATUS Status; PHIDP_REPORT_IDS ReportDescription; if (IoStack->Parameters.DeviceIoControl.InputBufferLength < 1) { Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INVALID_PARAMETER; } ReportDescription = HidClassPDO_GetReportDescriptionByReportID(&PDODeviceExtension->Common.DeviceDescription, ((PUCHAR)Irp->AssociatedIrp.SystemBuffer)[0]); if (!ReportDescription || IoStack->Parameters.DeviceIoControl.OutputBufferLength < ReportDescription->FeatureLength) { Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INVALID_PARAMETER; } RtlZeroMemory(&XferPacket, sizeof(XferPacket)); XferPacket.reportBufferLen = ReportDescription->FeatureLength; XferPacket.reportBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); XferPacket.reportId = ((PUCHAR)Irp->AssociatedIrp.SystemBuffer)[0]; if (!XferPacket.reportBuffer) { Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INSUFFICIENT_RESOURCES; } SubIrp = IoBuildDeviceIoControlRequest( IOCTL_HID_GET_FEATURE, CommonDeviceExtension->HidDeviceExtension.NextDeviceObject, NULL, 0, NULL, 0, TRUE, &Event, &IoStatusBlock); if (!SubIrp) { Irp->IoStatus.Status = STATUS_NO_MEMORY; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_NOT_IMPLEMENTED; } SubIrp->UserBuffer = &XferPacket; KeInitializeEvent(&Event, SynchronizationEvent, FALSE); Status = IoCallDriver(CommonDeviceExtension->HidDeviceExtension.NextDeviceObject, SubIrp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; } case IOCTL_HID_SET_FEATURE: { PIRP SubIrp; KEVENT Event; IO_STATUS_BLOCK IoStatusBlock; HID_XFER_PACKET XferPacket; NTSTATUS Status; PHIDP_REPORT_IDS ReportDescription; if (IoStack->Parameters.DeviceIoControl.InputBufferLength < 1) { Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INVALID_PARAMETER; } ReportDescription = HidClassPDO_GetReportDescriptionByReportID(&PDODeviceExtension->Common.DeviceDescription, ((PUCHAR)Irp->AssociatedIrp.SystemBuffer)[0]); if (!ReportDescription || IoStack->Parameters.DeviceIoControl.InputBufferLength < ReportDescription->FeatureLength) { Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INVALID_PARAMETER; } RtlZeroMemory(&XferPacket, sizeof(XferPacket)); XferPacket.reportBufferLen = ReportDescription->FeatureLength; XferPacket.reportBuffer = Irp->AssociatedIrp.SystemBuffer; XferPacket.reportId = XferPacket.reportBuffer[0]; SubIrp = IoBuildDeviceIoControlRequest( IOCTL_HID_SET_FEATURE, CommonDeviceExtension->HidDeviceExtension.NextDeviceObject, NULL, 0, NULL, 0, TRUE, &Event, &IoStatusBlock); if (!SubIrp) { Irp->IoStatus.Status = STATUS_NO_MEMORY; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_NOT_IMPLEMENTED; } SubIrp->UserBuffer = &XferPacket; KeInitializeEvent(&Event, SynchronizationEvent, FALSE); Status = IoCallDriver(CommonDeviceExtension->HidDeviceExtension.NextDeviceObject, SubIrp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; } 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( IN PHID_MINIDRIVER_REGISTRATION MinidriverRegistration) { NTSTATUS Status; PHIDCLASS_DRIVER_EXTENSION DriverExtension; /* check if the version matches */ if (MinidriverRegistration->Revision > HID_REVISION) { /* revision mismatch */ ASSERT(FALSE); return STATUS_REVISION_MISMATCH; } /* now allocate the driver object extension */ Status = IoAllocateDriverObjectExtension(MinidriverRegistration->DriverObject, ClientIdentificationAddress, sizeof(HIDCLASS_DRIVER_EXTENSION), (PVOID *)&DriverExtension); if (!NT_SUCCESS(Status)) { /* failed to allocate driver extension */ ASSERT(FALSE); return Status; } /* zero driver extension */ RtlZeroMemory(DriverExtension, sizeof(HIDCLASS_DRIVER_EXTENSION)); /* init driver extension */ DriverExtension->DriverObject = MinidriverRegistration->DriverObject; DriverExtension->DeviceExtensionSize = MinidriverRegistration->DeviceExtensionSize; DriverExtension->DevicesArePolled = MinidriverRegistration->DevicesArePolled; DriverExtension->AddDevice = MinidriverRegistration->DriverObject->DriverExtension->AddDevice; DriverExtension->DriverUnload = MinidriverRegistration->DriverObject->DriverUnload; /* copy driver dispatch routines */ RtlCopyMemory(DriverExtension->MajorFunction, MinidriverRegistration->DriverObject->MajorFunction, sizeof(PDRIVER_DISPATCH) * (IRP_MJ_MAXIMUM_FUNCTION + 1)); /* initialize lock */ KeInitializeSpinLock(&DriverExtension->Lock); /* now replace dispatch routines */ DriverExtension->DriverObject->DriverExtension->AddDevice = HidClassAddDevice; DriverExtension->DriverObject->DriverUnload = HidClassDriverUnload; DriverExtension->DriverObject->MajorFunction[IRP_MJ_CREATE] = HidClassDispatch; DriverExtension->DriverObject->MajorFunction[IRP_MJ_CLOSE] = HidClassDispatch; DriverExtension->DriverObject->MajorFunction[IRP_MJ_READ] = HidClassDispatch; DriverExtension->DriverObject->MajorFunction[IRP_MJ_WRITE] = HidClassDispatch; DriverExtension->DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HidClassDispatch; DriverExtension->DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = HidClassDispatch; DriverExtension->DriverObject->MajorFunction[IRP_MJ_POWER] = HidClassDispatch; DriverExtension->DriverObject->MajorFunction[IRP_MJ_PNP] = HidClassDispatch; /* done */ return STATUS_SUCCESS; }