mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 01:55:19 +00:00
1965 lines
51 KiB
C
1965 lines
51 KiB
C
/*
|
|
* PROJECT: ReactOS Universal Serial Bus Human Interface Device Driver
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: drivers/usb/hidusb/hidusb.c
|
|
* PURPOSE: HID USB Interface Driver
|
|
* PROGRAMMERS:
|
|
* Michael Martin (michael.martin@reactos.org)
|
|
* Johannes Anderwald (johannes.anderwald@reactos.org)
|
|
*/
|
|
|
|
#include "hidusb.h"
|
|
|
|
PUSBD_PIPE_INFORMATION
|
|
HidUsb_GetInputInterruptInterfaceHandle(
|
|
PUSBD_INTERFACE_INFORMATION InterfaceInformation)
|
|
{
|
|
ULONG Index;
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
ASSERT(InterfaceInformation->NumberOfPipes);
|
|
|
|
for (Index = 0; Index < InterfaceInformation->NumberOfPipes; Index++)
|
|
{
|
|
//DPRINT1("[HIDUSB] EndpointAddress %x PipeType %x PipeHandle %x\n", InterfaceInformation->Pipes[Index].EndpointAddress, InterfaceInformation->Pipes[Index].PipeType, InterfaceInformation->Pipes[Index].PipeHandle);
|
|
if (InterfaceInformation->Pipes[Index].PipeType == UsbdPipeTypeInterrupt && (InterfaceInformation->Pipes[Index].EndpointAddress & USB_ENDPOINT_DIRECTION_MASK))
|
|
{
|
|
//
|
|
// found handle
|
|
//
|
|
return &InterfaceInformation->Pipes[Index];
|
|
}
|
|
}
|
|
|
|
//
|
|
// not found
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
NTSTATUS
|
|
HidUsb_GetPortStatus(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PULONG PortStatus)
|
|
{
|
|
PIRP Irp;
|
|
KEVENT Event;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
PIO_STACK_LOCATION IoStack;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// init result
|
|
//
|
|
*PortStatus = 0;
|
|
|
|
//
|
|
// init event
|
|
//
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// build irp
|
|
//
|
|
Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_GET_PORT_STATUS,
|
|
DeviceExtension->NextDeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
TRUE,
|
|
&Event,
|
|
&IoStatus);
|
|
if (!Irp)
|
|
{
|
|
//
|
|
// no memory
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// get stack location
|
|
//
|
|
IoStack = IoGetNextIrpStackLocation(Irp);
|
|
|
|
//
|
|
// store result buffer
|
|
//
|
|
IoStack->Parameters.Others.Argument1 = PortStatus;
|
|
|
|
//
|
|
// call driver
|
|
//
|
|
Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
//
|
|
// wait for completion
|
|
//
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, 0, NULL);
|
|
return IoStatus.Status;
|
|
}
|
|
|
|
//
|
|
// done
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
HidUsb_ResetInterruptPipe(
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
PUSBD_PIPE_INFORMATION PipeInformation;
|
|
PURB Urb;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
HidDeviceExtension = DeviceExtension->MiniDeviceExtension;
|
|
|
|
//
|
|
// get interrupt pipe handle
|
|
//
|
|
ASSERT(HidDeviceExtension->InterfaceInfo);
|
|
PipeInformation = HidUsb_GetInputInterruptInterfaceHandle(HidDeviceExtension->InterfaceInfo);
|
|
ASSERT(PipeInformation);
|
|
ASSERT(PipeInformation->PipeHandle);
|
|
|
|
//
|
|
// allocate urb
|
|
//
|
|
Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST), HIDUSB_URB_TAG);
|
|
if (!Urb)
|
|
{
|
|
//
|
|
// no memory
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// init urb
|
|
//
|
|
RtlZeroMemory(Urb, sizeof(struct _URB_PIPE_REQUEST));
|
|
Urb->UrbHeader.Function = URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL;
|
|
Urb->UrbHeader.Length = sizeof(struct _URB_PIPE_REQUEST);
|
|
Urb->UrbPipeRequest.PipeHandle = PipeInformation->PipeHandle;
|
|
|
|
//
|
|
// dispatch request
|
|
//
|
|
Status = Hid_DispatchUrb(DeviceObject, Urb);
|
|
|
|
//
|
|
// free urb
|
|
//
|
|
ExFreePoolWithTag(Urb, HIDUSB_URB_TAG);
|
|
|
|
//
|
|
// done
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
HidUsb_AbortPipe(
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
PURB Urb;
|
|
NTSTATUS Status;
|
|
PUSBD_PIPE_INFORMATION PipeInformation;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
HidDeviceExtension = DeviceExtension->MiniDeviceExtension;
|
|
|
|
//
|
|
// allocate urb
|
|
//
|
|
Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST), HIDUSB_URB_TAG);
|
|
if (!Urb)
|
|
{
|
|
//
|
|
// no memory
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// get pipe information
|
|
//
|
|
PipeInformation = HidUsb_GetInputInterruptInterfaceHandle(HidDeviceExtension->InterfaceInfo);
|
|
ASSERT(PipeInformation);
|
|
ASSERT(PipeInformation->PipeHandle);
|
|
|
|
//
|
|
// init urb
|
|
//
|
|
RtlZeroMemory(Urb, sizeof(struct _URB_PIPE_REQUEST));
|
|
Urb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE;
|
|
Urb->UrbHeader.Length = sizeof(struct _URB_PIPE_REQUEST);
|
|
Urb->UrbPipeRequest.PipeHandle = PipeInformation->PipeHandle;
|
|
|
|
//
|
|
// dispatch request
|
|
//
|
|
Status = Hid_DispatchUrb(DeviceObject, Urb);
|
|
|
|
//
|
|
// free urb
|
|
//
|
|
ExFreePoolWithTag(Urb, HIDUSB_URB_TAG);
|
|
|
|
//
|
|
// done
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
HidUsb_ResetPort(
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
KEVENT Event;
|
|
PIRP Irp;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// init event
|
|
//
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// build irp
|
|
//
|
|
Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_RESET_PORT,
|
|
DeviceExtension->NextDeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
TRUE,
|
|
&Event,
|
|
&IoStatusBlock);
|
|
if (!Irp)
|
|
{
|
|
//
|
|
// no memory
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// send the irp
|
|
//
|
|
Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
//
|
|
// wait for request completion
|
|
//
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
Status = IoStatusBlock.Status;
|
|
}
|
|
|
|
//
|
|
// done
|
|
//
|
|
return IoStatusBlock.Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
HidCreate(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
{
|
|
PIO_STACK_LOCATION IoStack;
|
|
|
|
//
|
|
// get current irp stack location
|
|
//
|
|
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// sanity check for hidclass driver
|
|
//
|
|
ASSERT(IoStack->MajorFunction == IRP_MJ_CREATE || IoStack->MajorFunction == IRP_MJ_CLOSE);
|
|
|
|
//
|
|
// informational debug print
|
|
//
|
|
DPRINT("HIDUSB Request: %x\n", IoStack->MajorFunction);
|
|
|
|
//
|
|
// complete request
|
|
//
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
//
|
|
// done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
HidUsb_ResetWorkerRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Ctx)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG PortStatus;
|
|
PHID_USB_RESET_CONTEXT ResetContext;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
|
|
DPRINT("[HIDUSB] ResetWorkerRoutine\n");
|
|
|
|
//
|
|
// get context
|
|
//
|
|
ResetContext = Ctx;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = ResetContext->DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// get port status
|
|
//
|
|
Status = HidUsb_GetPortStatus(ResetContext->DeviceObject, &PortStatus);
|
|
DPRINT("[HIDUSB] ResetWorkerRoutine GetPortStatus %x PortStatus %x\n", Status, PortStatus);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (!(PortStatus & USB_PORT_STATUS_ENABLE))
|
|
{
|
|
//
|
|
// port is disabled
|
|
//
|
|
Status = HidUsb_ResetInterruptPipe(ResetContext->DeviceObject);
|
|
DPRINT1("[HIDUSB] ResetWorkerRoutine ResetPipe %x\n", Status);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// abort pipe
|
|
//
|
|
Status = HidUsb_AbortPipe(ResetContext->DeviceObject);
|
|
DPRINT1("[HIDUSB] ResetWorkerRoutine AbortPipe %x\n", Status);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// reset port
|
|
//
|
|
Status = HidUsb_ResetPort(ResetContext->DeviceObject);
|
|
DPRINT1("[HIDUSB] ResetPort %x\n", Status);
|
|
if (Status == STATUS_DEVICE_DATA_ERROR)
|
|
{
|
|
//
|
|
// invalidate device state
|
|
//
|
|
IoInvalidateDeviceState(DeviceExtension->PhysicalDeviceObject);
|
|
}
|
|
|
|
//
|
|
// reset interrupt pipe
|
|
//
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// reset pipe
|
|
//
|
|
Status = HidUsb_ResetInterruptPipe(ResetContext->DeviceObject);
|
|
DPRINT1("[HIDUSB] ResetWorkerRoutine ResetPipe %x\n", Status);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// cleanup
|
|
//
|
|
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
IoFreeWorkItem(ResetContext->WorkItem);
|
|
IoCompleteRequest(ResetContext->Irp, IO_NO_INCREMENT);
|
|
ExFreePoolWithTag(ResetContext, HIDUSB_TAG);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
HidUsb_ReadReportCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context)
|
|
{
|
|
PURB Urb;
|
|
PHID_USB_RESET_CONTEXT ResetContext;
|
|
|
|
//
|
|
// get urb
|
|
//
|
|
Urb = Context;
|
|
ASSERT(Urb);
|
|
|
|
DPRINT("[HIDUSB] HidUsb_ReadReportCompletion %p Status %x Urb Status %x\n", Irp, Irp->IoStatus, Urb->UrbHeader.Status);
|
|
|
|
if (Irp->PendingReturned)
|
|
{
|
|
//
|
|
// mark irp pending
|
|
//
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
|
|
//
|
|
// did the reading report succeed / cancelled
|
|
//
|
|
if (NT_SUCCESS(Irp->IoStatus.Status) || Irp->IoStatus.Status == STATUS_CANCELLED || Irp->IoStatus.Status == STATUS_DEVICE_NOT_CONNECTED)
|
|
{
|
|
//
|
|
// store result length
|
|
//
|
|
Irp->IoStatus.Information = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
|
|
|
|
//
|
|
// FIXME handle error
|
|
//
|
|
ASSERT(Urb->UrbHeader.Status == USBD_STATUS_SUCCESS || Urb->UrbHeader.Status == USBD_STATUS_DEVICE_GONE);
|
|
|
|
//
|
|
// free the urb
|
|
//
|
|
ExFreePoolWithTag(Urb, HIDUSB_URB_TAG);
|
|
|
|
//
|
|
// finish completion
|
|
//
|
|
return STATUS_CONTINUE_COMPLETION;
|
|
}
|
|
|
|
//
|
|
// allocate reset context
|
|
//
|
|
ResetContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(HID_USB_RESET_CONTEXT), HIDUSB_TAG);
|
|
if (ResetContext)
|
|
{
|
|
//
|
|
// allocate work item
|
|
//
|
|
ResetContext->WorkItem = IoAllocateWorkItem(DeviceObject);
|
|
if (ResetContext->WorkItem)
|
|
{
|
|
//
|
|
// init reset context
|
|
//
|
|
ResetContext->Irp = Irp;
|
|
ResetContext->DeviceObject = DeviceObject;
|
|
|
|
//
|
|
// queue the work item
|
|
//
|
|
IoQueueWorkItem(ResetContext->WorkItem, HidUsb_ResetWorkerRoutine, DelayedWorkQueue, ResetContext);
|
|
|
|
//
|
|
// free urb
|
|
//
|
|
ExFreePoolWithTag(Urb, HIDUSB_URB_TAG);
|
|
|
|
//
|
|
// defer completion
|
|
//
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
//
|
|
// free context
|
|
//
|
|
ExFreePoolWithTag(ResetContext, HIDUSB_TAG);
|
|
}
|
|
|
|
//
|
|
// free urb
|
|
//
|
|
ExFreePoolWithTag(Urb, HIDUSB_URB_TAG);
|
|
|
|
//
|
|
// complete request
|
|
//
|
|
return STATUS_CONTINUE_COMPLETION;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
HidUsb_ReadReport(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
{
|
|
PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
PIO_STACK_LOCATION IoStack;
|
|
PURB Urb;
|
|
PUSBD_PIPE_INFORMATION PipeInformation;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
HidDeviceExtension = DeviceExtension->MiniDeviceExtension;
|
|
|
|
//
|
|
// get current stack location
|
|
//
|
|
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// sanity checks
|
|
//
|
|
ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength);
|
|
ASSERT(Irp->UserBuffer);
|
|
ASSERT(HidDeviceExtension->InterfaceInfo);
|
|
|
|
//
|
|
// get interrupt input pipe
|
|
//
|
|
PipeInformation = HidUsb_GetInputInterruptInterfaceHandle(HidDeviceExtension->InterfaceInfo);
|
|
ASSERT(PipeInformation);
|
|
|
|
//
|
|
// lets allocate urb
|
|
//
|
|
Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), HIDUSB_URB_TAG);
|
|
if (!Urb)
|
|
{
|
|
//
|
|
// no memory
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// init urb
|
|
//
|
|
RtlZeroMemory(Urb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
ASSERT(Irp->UserBuffer);
|
|
ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength);
|
|
ASSERT(PipeInformation->PipeHandle);
|
|
|
|
//
|
|
// build the urb
|
|
//
|
|
UsbBuildInterruptOrBulkTransferRequest(Urb,
|
|
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
|
|
PipeInformation->PipeHandle,
|
|
Irp->UserBuffer,
|
|
NULL,
|
|
IoStack->Parameters.DeviceIoControl.OutputBufferLength,
|
|
USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
|
|
NULL);
|
|
|
|
//
|
|
// store configuration handle
|
|
//
|
|
Urb->UrbHeader.UsbdDeviceHandle = HidDeviceExtension->ConfigurationHandle;
|
|
|
|
//
|
|
// get next location to setup irp
|
|
//
|
|
IoStack = IoGetNextIrpStackLocation(Irp);
|
|
|
|
//
|
|
// init irp for lower driver
|
|
//
|
|
IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
|
|
IoStack->Parameters.DeviceIoControl.InputBufferLength = 0;
|
|
IoStack->Parameters.DeviceIoControl.OutputBufferLength = 0;
|
|
IoStack->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
|
|
IoStack->Parameters.Others.Argument1 = Urb;
|
|
|
|
|
|
//
|
|
// set completion routine
|
|
//
|
|
IoSetCompletionRoutine(Irp, HidUsb_ReadReportCompletion, Urb, TRUE, TRUE, TRUE);
|
|
|
|
//
|
|
// call driver
|
|
//
|
|
return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
HidUsb_GetReportDescriptor(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
{
|
|
PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
PVOID Report = NULL;
|
|
ULONG BufferLength, Length;
|
|
PIO_STACK_LOCATION IoStack;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
HidDeviceExtension = DeviceExtension->MiniDeviceExtension;
|
|
|
|
//
|
|
// sanity checks
|
|
//
|
|
ASSERT(HidDeviceExtension);
|
|
ASSERT(HidDeviceExtension->HidDescriptor);
|
|
ASSERT(HidDeviceExtension->HidDescriptor->bNumDescriptors >= 1);
|
|
ASSERT(HidDeviceExtension->HidDescriptor->DescriptorList[0].bReportType == HID_REPORT_DESCRIPTOR_TYPE);
|
|
ASSERT(HidDeviceExtension->HidDescriptor->DescriptorList[0].wReportLength > 0);
|
|
|
|
//
|
|
// FIXME: support old hid version
|
|
//
|
|
BufferLength = HidDeviceExtension->HidDescriptor->DescriptorList[0].wReportLength;
|
|
Status = Hid_GetDescriptor(DeviceObject,
|
|
URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE,
|
|
sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
|
|
&Report,
|
|
&BufferLength,
|
|
HidDeviceExtension->HidDescriptor->DescriptorList[0].bReportType,
|
|
0,
|
|
HidDeviceExtension->InterfaceInfo->InterfaceNumber);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// failed to get descriptor
|
|
// try with old hid version
|
|
//
|
|
BufferLength = HidDeviceExtension->HidDescriptor->DescriptorList[0].wReportLength;
|
|
Status = Hid_GetDescriptor(DeviceObject,
|
|
URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT,
|
|
sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
|
|
&Report,
|
|
&BufferLength,
|
|
HidDeviceExtension->HidDescriptor->DescriptorList[0].bReportType,
|
|
0,
|
|
0 /* FIXME*/);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("[HIDUSB] failed to get report descriptor with %x\n", Status);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// get current stack location
|
|
//
|
|
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
|
DPRINT("[HIDUSB] GetReportDescriptor: Status %x ReportLength %lu OutputBufferLength %lu TransferredLength %lu\n", Status, HidDeviceExtension->HidDescriptor->DescriptorList[0].wReportLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength, BufferLength);
|
|
|
|
//
|
|
// get length to copy
|
|
//
|
|
Length = min(IoStack->Parameters.DeviceIoControl.OutputBufferLength, BufferLength);
|
|
ASSERT(Length);
|
|
|
|
//
|
|
// copy result
|
|
//
|
|
RtlCopyMemory(Irp->UserBuffer, Report, Length);
|
|
|
|
//
|
|
// store result length
|
|
//
|
|
Irp->IoStatus.Information = Length;
|
|
|
|
//
|
|
// free the report buffer
|
|
//
|
|
ExFreePoolWithTag(Report, HIDUSB_TAG);
|
|
|
|
//
|
|
// done
|
|
//
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
HidInternalDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
{
|
|
PIO_STACK_LOCATION IoStack;
|
|
PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
PHID_DEVICE_ATTRIBUTES Attributes;
|
|
ULONG Length;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
HidDeviceExtension = DeviceExtension->MiniDeviceExtension;
|
|
|
|
//
|
|
// get current stack location
|
|
//
|
|
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
switch (IoStack->Parameters.DeviceIoControl.IoControlCode)
|
|
{
|
|
case IOCTL_HID_GET_DEVICE_ATTRIBUTES:
|
|
{
|
|
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_DEVICE_ATTRIBUTES))
|
|
{
|
|
//
|
|
// invalid request
|
|
//
|
|
Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
|
|
DPRINT1("[HIDUSB] IOCTL_HID_GET_DEVICE_ATTRIBUTES invalid buffer\n");
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_INVALID_BUFFER_SIZE;
|
|
}
|
|
//
|
|
// store result
|
|
//
|
|
DPRINT("[HIDUSB] IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
|
|
ASSERT(HidDeviceExtension->DeviceDescriptor);
|
|
Irp->IoStatus.Information = sizeof(HID_DESCRIPTOR);
|
|
Attributes = Irp->UserBuffer;
|
|
Attributes->Size = sizeof(HID_DEVICE_ATTRIBUTES);
|
|
Attributes->VendorID = HidDeviceExtension->DeviceDescriptor->idVendor;
|
|
Attributes->ProductID = HidDeviceExtension->DeviceDescriptor->idProduct;
|
|
Attributes->VersionNumber = HidDeviceExtension->DeviceDescriptor->bcdDevice;
|
|
|
|
//
|
|
// complete request
|
|
//
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
case IOCTL_HID_GET_DEVICE_DESCRIPTOR:
|
|
{
|
|
//
|
|
// sanity check
|
|
//
|
|
ASSERT(HidDeviceExtension->HidDescriptor);
|
|
DPRINT("[HIDUSB] IOCTL_HID_GET_DEVICE_DESCRIPTOR DescriptorLength %lu OutputBufferLength %lu\n", HidDeviceExtension->HidDescriptor->bLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength);
|
|
|
|
//
|
|
// store length
|
|
//
|
|
Length = min(HidDeviceExtension->HidDescriptor->bLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength);
|
|
|
|
//
|
|
// copy descriptor
|
|
//
|
|
RtlCopyMemory(Irp->UserBuffer, HidDeviceExtension->HidDescriptor, Length);
|
|
|
|
//
|
|
// store result length
|
|
//
|
|
Irp->IoStatus.Information = HidDeviceExtension->HidDescriptor->bLength;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
/* complete request */
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
case IOCTL_HID_GET_REPORT_DESCRIPTOR:
|
|
{
|
|
Status = HidUsb_GetReportDescriptor(DeviceObject, Irp);
|
|
DPRINT("[HIDUSB] IOCTL_HID_GET_REPORT_DESCRIPTOR Status %x\n", Status);
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return Status;
|
|
}
|
|
case IOCTL_HID_READ_REPORT:
|
|
{
|
|
DPRINT("[HIDUSB] IOCTL_HID_READ_REPORT\n");
|
|
Status = HidUsb_ReadReport(DeviceObject, Irp);
|
|
return Status;
|
|
}
|
|
case IOCTL_HID_WRITE_REPORT:
|
|
{
|
|
DPRINT1("[HIDUSB] IOCTL_HID_WRITE_REPORT not implemented \n");
|
|
ASSERT(FALSE);
|
|
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
case IOCTL_GET_PHYSICAL_DESCRIPTOR:
|
|
{
|
|
DPRINT1("[HIDUSB] IOCTL_GET_PHYSICAL_DESCRIPTOR not implemented \n");
|
|
ASSERT(FALSE);
|
|
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
case IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST:
|
|
{
|
|
DPRINT1("[HIDUSB] IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST not implemented \n");
|
|
ASSERT(FALSE);
|
|
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
case IOCTL_HID_GET_FEATURE:
|
|
{
|
|
DPRINT1("[HIDUSB] IOCTL_HID_GET_FEATURE not implemented \n");
|
|
ASSERT(FALSE);
|
|
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
case IOCTL_HID_SET_FEATURE:
|
|
{
|
|
DPRINT1("[HIDUSB] IOCTL_HID_SET_FEATURE not implemented \n");
|
|
ASSERT(FALSE);
|
|
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
case IOCTL_HID_SET_OUTPUT_REPORT:
|
|
{
|
|
DPRINT1("[HIDUSB] IOCTL_HID_SET_OUTPUT_REPORT not implemented \n");
|
|
ASSERT(FALSE);
|
|
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
case IOCTL_HID_GET_INPUT_REPORT:
|
|
{
|
|
DPRINT1("[HIDUSB] IOCTL_HID_GET_INPUT_REPORT not implemented \n");
|
|
ASSERT(FALSE);
|
|
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
case IOCTL_HID_GET_INDEXED_STRING:
|
|
{
|
|
DPRINT1("[HIDUSB] IOCTL_HID_GET_INDEXED_STRING not implemented \n");
|
|
ASSERT(FALSE);
|
|
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
case IOCTL_HID_GET_MS_GENRE_DESCRIPTOR:
|
|
{
|
|
DPRINT1("[HIDUSB] IOCTL_HID_GET_MS_GENRE_DESCRIPTOR not implemented \n");
|
|
ASSERT(FALSE);
|
|
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
default:
|
|
{
|
|
UNIMPLEMENTED;
|
|
ASSERT(FALSE);
|
|
Status = Irp->IoStatus.Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
HidPower(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
{
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
PoStartNextPowerIrp(Irp);
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return PoCallDriver(DeviceExtension->NextDeviceObject, Irp);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
HidSystemControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
{
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
|
|
//
|
|
// get hid device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// skip stack location
|
|
//
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// submit request
|
|
//
|
|
return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
Hid_PnpCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context)
|
|
{
|
|
//
|
|
// signal event
|
|
//
|
|
KeSetEvent(Context, 0, FALSE);
|
|
|
|
//
|
|
// done
|
|
//
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
Hid_DispatchUrb(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PURB Urb)
|
|
{
|
|
PIRP Irp;
|
|
KEVENT Event;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PIO_STACK_LOCATION IoStack;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// init event
|
|
//
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// build irp
|
|
//
|
|
Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_SUBMIT_URB,
|
|
DeviceExtension->NextDeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
TRUE,
|
|
&Event,
|
|
&IoStatus);
|
|
if (!Irp)
|
|
{
|
|
//
|
|
// no memory
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// get next stack location
|
|
//
|
|
IoStack = IoGetNextIrpStackLocation(Irp);
|
|
|
|
//
|
|
// store urb
|
|
//
|
|
IoStack->Parameters.Others.Argument1 = Urb;
|
|
|
|
//
|
|
// set completion routine
|
|
//
|
|
IoSetCompletionRoutine(Irp, Hid_PnpCompletion, &Event, TRUE, TRUE, TRUE);
|
|
|
|
//
|
|
// call driver
|
|
//
|
|
Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
|
|
|
|
//
|
|
// wait for the request to finish
|
|
//
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
}
|
|
|
|
//
|
|
// complete request
|
|
//
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
//
|
|
// get final status
|
|
//
|
|
Status = IoStatus.Status;
|
|
}
|
|
|
|
DPRINT("[HIDUSB] DispatchUrb %x\n", Status);
|
|
|
|
|
|
//
|
|
// done
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
Hid_GetDescriptor(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN USHORT UrbFunction,
|
|
IN USHORT UrbLength,
|
|
IN OUT PVOID *UrbBuffer,
|
|
IN OUT PULONG UrbBufferLength,
|
|
IN UCHAR DescriptorType,
|
|
IN UCHAR Index,
|
|
IN USHORT LanguageIndex)
|
|
{
|
|
PURB Urb;
|
|
NTSTATUS Status;
|
|
UCHAR Allocated = FALSE;
|
|
|
|
//
|
|
// allocate urb
|
|
//
|
|
Urb = ExAllocatePoolWithTag(NonPagedPool, UrbLength, HIDUSB_URB_TAG);
|
|
if (!Urb)
|
|
{
|
|
//
|
|
// no memory
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// is there an urb buffer
|
|
//
|
|
if (!*UrbBuffer)
|
|
{
|
|
//
|
|
// allocate buffer
|
|
//
|
|
*UrbBuffer = ExAllocatePoolWithTag(NonPagedPool, *UrbBufferLength, HIDUSB_TAG);
|
|
if (!*UrbBuffer)
|
|
{
|
|
//
|
|
// no memory
|
|
//
|
|
ExFreePoolWithTag(Urb, HIDUSB_URB_TAG);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// zero buffer
|
|
//
|
|
RtlZeroMemory(*UrbBuffer, *UrbBufferLength);
|
|
Allocated = TRUE;
|
|
}
|
|
|
|
//
|
|
// zero urb
|
|
//
|
|
RtlZeroMemory(Urb, UrbLength);
|
|
|
|
//
|
|
// build descriptor request
|
|
//
|
|
UsbBuildGetDescriptorRequest(Urb, UrbLength, DescriptorType, Index, LanguageIndex, *UrbBuffer, NULL, *UrbBufferLength, NULL);
|
|
|
|
//
|
|
// set urb function
|
|
//
|
|
Urb->UrbHeader.Function = UrbFunction;
|
|
|
|
//
|
|
// dispatch urb
|
|
//
|
|
Status = Hid_DispatchUrb(DeviceObject, Urb);
|
|
|
|
//
|
|
// did the request fail
|
|
//
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Allocated)
|
|
{
|
|
//
|
|
// free allocated buffer
|
|
//
|
|
ExFreePoolWithTag(*UrbBuffer, HIDUSB_TAG);
|
|
*UrbBuffer = NULL;
|
|
}
|
|
|
|
//
|
|
// free urb
|
|
//
|
|
ExFreePoolWithTag(Urb, HIDUSB_URB_TAG);
|
|
*UrbBufferLength = 0;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// did urb request fail
|
|
//
|
|
if (!NT_SUCCESS(Urb->UrbHeader.Status))
|
|
{
|
|
if (Allocated)
|
|
{
|
|
//
|
|
// free allocated buffer
|
|
//
|
|
ExFreePoolWithTag(*UrbBuffer, HIDUSB_TAG);
|
|
*UrbBuffer = NULL;
|
|
}
|
|
|
|
//
|
|
// free urb
|
|
//
|
|
ExFreePoolWithTag(Urb, HIDUSB_URB_TAG);
|
|
*UrbBufferLength = 0;
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// store result length
|
|
//
|
|
*UrbBufferLength = Urb->UrbControlDescriptorRequest.TransferBufferLength;
|
|
|
|
//
|
|
// free urb
|
|
//
|
|
ExFreePoolWithTag(Urb, HIDUSB_URB_TAG);
|
|
|
|
//
|
|
// completed successfully
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
Hid_SelectConfiguration(
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
|
|
NTSTATUS Status;
|
|
USBD_INTERFACE_LIST_ENTRY InterfaceList[2];
|
|
PURB Urb;
|
|
PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
HidDeviceExtension = DeviceExtension->MiniDeviceExtension;
|
|
|
|
//
|
|
// now parse the descriptors
|
|
//
|
|
InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(HidDeviceExtension->ConfigurationDescriptor,
|
|
HidDeviceExtension->ConfigurationDescriptor,
|
|
-1,
|
|
-1,
|
|
USB_DEVICE_CLASS_HUMAN_INTERFACE,
|
|
-1,
|
|
-1);
|
|
if (!InterfaceDescriptor)
|
|
{
|
|
//
|
|
// bogus configuration descriptor
|
|
//
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
ASSERT(InterfaceDescriptor);
|
|
ASSERT(InterfaceDescriptor->bInterfaceClass == USB_DEVICE_CLASS_HUMAN_INTERFACE);
|
|
ASSERT(InterfaceDescriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE);
|
|
ASSERT(InterfaceDescriptor->bLength == sizeof(USB_INTERFACE_DESCRIPTOR));
|
|
|
|
//
|
|
// setup interface list
|
|
//
|
|
RtlZeroMemory(InterfaceList, sizeof(InterfaceList));
|
|
InterfaceList[0].InterfaceDescriptor = InterfaceDescriptor;
|
|
|
|
//
|
|
// build urb
|
|
//
|
|
Urb = USBD_CreateConfigurationRequestEx(HidDeviceExtension->ConfigurationDescriptor, InterfaceList);
|
|
if (!Urb)
|
|
{
|
|
//
|
|
// no memory
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// dispatch request
|
|
//
|
|
Status = Hid_DispatchUrb(DeviceObject, Urb);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// store configuration handle
|
|
//
|
|
HidDeviceExtension->ConfigurationHandle = Urb->UrbSelectConfiguration.ConfigurationHandle;
|
|
|
|
//
|
|
// copy interface info
|
|
//
|
|
HidDeviceExtension->InterfaceInfo = ExAllocatePoolWithTag(NonPagedPool, Urb->UrbSelectConfiguration.Interface.Length, HIDUSB_TAG);
|
|
if (HidDeviceExtension->InterfaceInfo)
|
|
{
|
|
//
|
|
// copy interface info
|
|
//
|
|
RtlCopyMemory(HidDeviceExtension->InterfaceInfo, &Urb->UrbSelectConfiguration.Interface, Urb->UrbSelectConfiguration.Interface.Length);
|
|
}
|
|
}
|
|
|
|
//
|
|
// free urb request
|
|
//
|
|
ExFreePoolWithTag(Urb, 0);
|
|
|
|
//
|
|
// done
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
Hid_DisableConfiguration(
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
|
|
NTSTATUS Status;
|
|
PURB Urb;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
HidDeviceExtension = DeviceExtension->MiniDeviceExtension;
|
|
|
|
//
|
|
// build urb
|
|
//
|
|
Urb = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(struct _URB_SELECT_CONFIGURATION),
|
|
HIDUSB_URB_TAG);
|
|
if (!Urb)
|
|
{
|
|
//
|
|
// no memory
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// format urb
|
|
//
|
|
UsbBuildSelectConfigurationRequest(Urb,
|
|
sizeof(struct _URB_SELECT_CONFIGURATION),
|
|
NULL);
|
|
|
|
//
|
|
// dispatch request
|
|
//
|
|
Status = Hid_DispatchUrb(DeviceObject, Urb);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("[HIDUSB] Dispatching unconfigure URB failed with %lx\n", Status);
|
|
}
|
|
else if (!USBD_SUCCESS(Urb->UrbHeader.Status))
|
|
{
|
|
DPRINT("[HIDUSB] Unconfigure URB failed with %lx\n", Status);
|
|
}
|
|
|
|
//
|
|
// free urb
|
|
//
|
|
ExFreePoolWithTag(Urb, HIDUSB_URB_TAG);
|
|
|
|
//
|
|
// free resources
|
|
//
|
|
HidDeviceExtension->ConfigurationHandle = NULL;
|
|
|
|
if (HidDeviceExtension->InterfaceInfo)
|
|
{
|
|
ExFreePoolWithTag(HidDeviceExtension->InterfaceInfo, HIDUSB_TAG);
|
|
HidDeviceExtension->InterfaceInfo = NULL;
|
|
}
|
|
|
|
if (HidDeviceExtension->ConfigurationDescriptor)
|
|
{
|
|
ExFreePoolWithTag(HidDeviceExtension->ConfigurationDescriptor, HIDUSB_TAG);
|
|
HidDeviceExtension->ConfigurationDescriptor = NULL;
|
|
HidDeviceExtension->HidDescriptor = NULL;
|
|
}
|
|
|
|
if (HidDeviceExtension->DeviceDescriptor)
|
|
{
|
|
ExFreePoolWithTag(HidDeviceExtension->DeviceDescriptor, HIDUSB_TAG);
|
|
HidDeviceExtension->DeviceDescriptor = NULL;
|
|
}
|
|
|
|
//
|
|
// done
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
Hid_SetIdle(
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PURB Urb;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// allocate urb
|
|
//
|
|
Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST), HIDUSB_URB_TAG);
|
|
if (!Urb)
|
|
{
|
|
//
|
|
// no memory
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// zero urb
|
|
//
|
|
RtlZeroMemory(Urb, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
|
|
|
|
//
|
|
// format urb
|
|
//
|
|
UsbBuildVendorRequest(Urb,
|
|
URB_FUNCTION_CLASS_INTERFACE,
|
|
sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST),
|
|
0,
|
|
0,
|
|
USB_SET_IDLE_REQUEST, // HID_SET_IDLE
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
NULL);
|
|
|
|
//
|
|
// dispatch urb
|
|
//
|
|
Status = Hid_DispatchUrb(DeviceObject, Urb);
|
|
|
|
//
|
|
// free urb
|
|
//
|
|
ExFreePoolWithTag(Urb, HIDUSB_URB_TAG);
|
|
|
|
//
|
|
// print status
|
|
//
|
|
DPRINT1("Status %x\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
Hid_GetProtocol(
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
PURB Urb;
|
|
UCHAR Protocol[1];
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
HidDeviceExtension = DeviceExtension->MiniDeviceExtension;
|
|
ASSERT(HidDeviceExtension->InterfaceInfo);
|
|
|
|
if (HidDeviceExtension->InterfaceInfo->SubClass != 0x1)
|
|
{
|
|
//
|
|
// device does not support the boot protocol
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// allocate urb
|
|
//
|
|
Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST), HIDUSB_URB_TAG);
|
|
if (!Urb)
|
|
{
|
|
//
|
|
// no memory
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// zero urb
|
|
//
|
|
RtlZeroMemory(Urb, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
|
|
|
|
//
|
|
// format urb
|
|
//
|
|
UsbBuildVendorRequest(Urb,
|
|
URB_FUNCTION_CLASS_INTERFACE,
|
|
sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST),
|
|
USBD_TRANSFER_DIRECTION_IN,
|
|
0,
|
|
USB_GET_PROTOCOL_REQUEST,
|
|
0,
|
|
0,
|
|
Protocol,
|
|
NULL,
|
|
1,
|
|
NULL);
|
|
Protocol[0] = 0xFF;
|
|
|
|
//
|
|
// dispatch urb
|
|
//
|
|
Hid_DispatchUrb(DeviceObject, Urb);
|
|
|
|
//
|
|
// free urb
|
|
//
|
|
ExFreePoolWithTag(Urb, HIDUSB_URB_TAG);
|
|
|
|
//
|
|
// boot protocol active 0x00 disabled 0x1
|
|
//
|
|
if (Protocol[0] != 0x1)
|
|
{
|
|
if (Protocol[0] == 0x00)
|
|
{
|
|
DPRINT1("[HIDUSB] Need to disable boot protocol!\n");
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("[HIDUSB] Unexpected protocol value %x\n", Protocol[0] & 0xFF);
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
Hid_PnpStart(
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
NTSTATUS Status;
|
|
ULONG DescriptorLength;
|
|
PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
|
|
PHID_DESCRIPTOR HidDescriptor;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
HidDeviceExtension = DeviceExtension->MiniDeviceExtension;
|
|
|
|
//
|
|
// get device descriptor
|
|
//
|
|
DescriptorLength = sizeof(USB_DEVICE_DESCRIPTOR);
|
|
Status = Hid_GetDescriptor(DeviceObject,
|
|
URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE,
|
|
sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
|
|
(PVOID *)&HidDeviceExtension->DeviceDescriptor,
|
|
&DescriptorLength,
|
|
USB_DEVICE_DESCRIPTOR_TYPE,
|
|
0,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// failed to obtain device descriptor
|
|
//
|
|
DPRINT1("[HIDUSB] failed to get device descriptor %x\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// now get the configuration descriptor
|
|
//
|
|
DescriptorLength = sizeof(USB_CONFIGURATION_DESCRIPTOR);
|
|
Status = Hid_GetDescriptor(DeviceObject,
|
|
URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE,
|
|
sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
|
|
(PVOID *)&HidDeviceExtension->ConfigurationDescriptor,
|
|
&DescriptorLength,
|
|
USB_CONFIGURATION_DESCRIPTOR_TYPE,
|
|
0,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// failed to obtain device descriptor
|
|
//
|
|
DPRINT1("[HIDUSB] failed to get device descriptor %x\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
ASSERT(DescriptorLength);
|
|
ASSERT(HidDeviceExtension->ConfigurationDescriptor);
|
|
ASSERT(HidDeviceExtension->ConfigurationDescriptor->bLength);
|
|
|
|
//
|
|
// store full length
|
|
//
|
|
DescriptorLength = HidDeviceExtension->ConfigurationDescriptor->wTotalLength;
|
|
|
|
//
|
|
// delete partial configuration descriptor
|
|
//
|
|
ExFreePoolWithTag(HidDeviceExtension->ConfigurationDescriptor, HIDUSB_TAG);
|
|
HidDeviceExtension->ConfigurationDescriptor = NULL;
|
|
|
|
//
|
|
// get full configuration descriptor
|
|
//
|
|
Status = Hid_GetDescriptor(DeviceObject,
|
|
URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE,
|
|
sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
|
|
(PVOID *)&HidDeviceExtension->ConfigurationDescriptor,
|
|
&DescriptorLength,
|
|
USB_CONFIGURATION_DESCRIPTOR_TYPE,
|
|
0,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// failed to obtain device descriptor
|
|
//
|
|
DPRINT1("[HIDUSB] failed to get device descriptor %x\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// now parse the descriptors
|
|
//
|
|
InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(HidDeviceExtension->ConfigurationDescriptor,
|
|
HidDeviceExtension->ConfigurationDescriptor,
|
|
-1,
|
|
-1,
|
|
USB_DEVICE_CLASS_HUMAN_INTERFACE,
|
|
-1,
|
|
-1);
|
|
if (!InterfaceDescriptor)
|
|
{
|
|
//
|
|
// no interface class
|
|
//
|
|
DPRINT1("[HIDUSB] HID Interface descriptor not found\n");
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
ASSERT(InterfaceDescriptor->bInterfaceClass == USB_DEVICE_CLASS_HUMAN_INTERFACE);
|
|
ASSERT(InterfaceDescriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE);
|
|
ASSERT(InterfaceDescriptor->bLength == sizeof(USB_INTERFACE_DESCRIPTOR));
|
|
|
|
//
|
|
// move to next descriptor
|
|
//
|
|
HidDescriptor = (PHID_DESCRIPTOR)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength);
|
|
ASSERT(HidDescriptor->bLength >= 2);
|
|
|
|
//
|
|
// check if this is the hid descriptor
|
|
//
|
|
if (HidDescriptor->bLength == sizeof(HID_DESCRIPTOR) && HidDescriptor->bDescriptorType == HID_HID_DESCRIPTOR_TYPE)
|
|
{
|
|
//
|
|
// found
|
|
//
|
|
HidDeviceExtension->HidDescriptor = HidDescriptor;
|
|
|
|
//
|
|
// select configuration
|
|
//
|
|
Status = Hid_SelectConfiguration(DeviceObject);
|
|
|
|
//
|
|
// done
|
|
//
|
|
DPRINT("[HIDUSB] SelectConfiguration %x\n", Status);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// now set the device idle
|
|
//
|
|
Hid_SetIdle(DeviceObject);
|
|
|
|
//
|
|
// get protocol
|
|
//
|
|
Hid_GetProtocol(DeviceObject);
|
|
return Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// FIXME parse hid descriptor
|
|
// select configuration
|
|
// set idle
|
|
// and get protocol
|
|
//
|
|
UNIMPLEMENTED;
|
|
ASSERT(FALSE);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
HidPnp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IoStack;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
KEVENT Event;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// get current stack location
|
|
//
|
|
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
|
DPRINT("[HIDUSB] Pnp %x\n", IoStack->MinorFunction);
|
|
|
|
//
|
|
// handle requests based on request type
|
|
//
|
|
switch (IoStack->MinorFunction)
|
|
{
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
{
|
|
//
|
|
// unconfigure device
|
|
// FIXME: Call this on IRP_MN_SURPRISE_REMOVAL, but don't send URBs
|
|
// FIXME: Don't call this after we've already seen a surprise removal or stop
|
|
//
|
|
Hid_DisableConfiguration(DeviceObject);
|
|
|
|
//
|
|
// pass request onto lower driver
|
|
//
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
|
|
|
|
return Status;
|
|
}
|
|
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
|
{
|
|
//
|
|
// device can not be disabled
|
|
//
|
|
Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE;
|
|
|
|
//
|
|
// pass request to next request
|
|
//
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
|
|
|
|
//
|
|
// done
|
|
//
|
|
return Status;
|
|
}
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
{
|
|
//
|
|
// we're fine with it
|
|
//
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// pass request to next driver
|
|
//
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
|
|
|
|
//
|
|
// done
|
|
//
|
|
return Status;
|
|
}
|
|
case IRP_MN_STOP_DEVICE:
|
|
{
|
|
//
|
|
// unconfigure device
|
|
//
|
|
Hid_DisableConfiguration(DeviceObject);
|
|
|
|
//
|
|
// prepare irp
|
|
//
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp, Hid_PnpCompletion, &Event, TRUE, TRUE, TRUE);
|
|
|
|
//
|
|
// send irp and wait for completion
|
|
//
|
|
Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
Status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
//
|
|
// done
|
|
//
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return Status;
|
|
}
|
|
case IRP_MN_QUERY_CAPABILITIES:
|
|
{
|
|
//
|
|
// prepare irp
|
|
//
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp, Hid_PnpCompletion, &Event, TRUE, TRUE, TRUE);
|
|
|
|
//
|
|
// send irp and wait for completion
|
|
//
|
|
Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
Status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status) && IoStack->Parameters.DeviceCapabilities.Capabilities != NULL)
|
|
{
|
|
//
|
|
// don't need to safely remove
|
|
//
|
|
IoStack->Parameters.DeviceCapabilities.Capabilities->SurpriseRemovalOK = TRUE;
|
|
}
|
|
|
|
//
|
|
// done
|
|
//
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return Status;
|
|
}
|
|
case IRP_MN_START_DEVICE:
|
|
{
|
|
//
|
|
// prepare irp
|
|
//
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp, Hid_PnpCompletion, &Event, TRUE, TRUE, TRUE);
|
|
|
|
//
|
|
// send irp and wait for completion
|
|
//
|
|
Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
Status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
//
|
|
// did the device successfully start
|
|
//
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// failed
|
|
//
|
|
DPRINT1("HIDUSB: IRP_MN_START_DEVICE failed with %x\n", Status);
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// start device
|
|
//
|
|
Status = Hid_PnpStart(DeviceObject);
|
|
|
|
//
|
|
// complete request
|
|
//
|
|
Irp->IoStatus.Status = Status;
|
|
DPRINT("[HIDUSB] IRP_MN_START_DEVICE Status %x\n", Status);
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return Status;
|
|
}
|
|
default:
|
|
{
|
|
//
|
|
// forward and forget request
|
|
//
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
HidAddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PHID_USB_DEVICE_EXTENSION HidDeviceExtension;
|
|
PHID_DEVICE_EXTENSION DeviceExtension;
|
|
|
|
//
|
|
// get device extension
|
|
//
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
HidDeviceExtension = DeviceExtension->MiniDeviceExtension;
|
|
|
|
//
|
|
// init event
|
|
//
|
|
KeInitializeEvent(&HidDeviceExtension->Event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
Hid_Unload(
|
|
IN PDRIVER_OBJECT DriverObject)
|
|
{
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegPath)
|
|
{
|
|
HID_MINIDRIVER_REGISTRATION Registration;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// initialize driver object
|
|
//
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = HidCreate;
|
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = HidCreate;
|
|
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = HidInternalDeviceControl;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = HidPower;
|
|
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = HidSystemControl;
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = HidPnp;
|
|
DriverObject->DriverExtension->AddDevice = HidAddDevice;
|
|
DriverObject->DriverUnload = Hid_Unload;
|
|
|
|
//
|
|
// prepare registration info
|
|
//
|
|
RtlZeroMemory(&Registration, sizeof(HID_MINIDRIVER_REGISTRATION));
|
|
|
|
//
|
|
// fill in registration info
|
|
//
|
|
Registration.Revision = HID_REVISION;
|
|
Registration.DriverObject = DriverObject;
|
|
Registration.RegistryPath = RegPath;
|
|
Registration.DeviceExtensionSize = sizeof(HID_USB_DEVICE_EXTENSION);
|
|
Registration.DevicesArePolled = FALSE;
|
|
|
|
//
|
|
// register driver
|
|
//
|
|
Status = HidRegisterMinidriver(&Registration);
|
|
|
|
//
|
|
// informal debug
|
|
//
|
|
DPRINT("********* HIDUSB *********\n");
|
|
DPRINT("HIDUSB Registration Status %x\n", Status);
|
|
|
|
return Status;
|
|
}
|