reactos/drivers/hid/mouhid/mouhid.c
2021-06-11 15:33:08 +03:00

1256 lines
40 KiB
C

/*
* PROJECT: ReactOS HID Stack
* LICENSE: GPL - See COPYING in the top level directory
* FILE: drivers/hid/mouhid/mouhid.c
* PURPOSE: Mouse HID Driver
* PROGRAMMERS:
* Michael Martin (michael.martin@reactos.org)
* Johannes Anderwald (johannes.anderwald@reactos.org)
*/
#include "mouhid.h"
static USHORT MouHid_ButtonUpFlags[] =
{
0xFF, /* unused */
MOUSE_LEFT_BUTTON_DOWN,
MOUSE_RIGHT_BUTTON_DOWN,
MOUSE_MIDDLE_BUTTON_DOWN,
MOUSE_BUTTON_4_DOWN,
MOUSE_BUTTON_5_DOWN
};
static USHORT MouHid_ButtonDownFlags[] =
{
0xFF, /* unused */
MOUSE_LEFT_BUTTON_UP,
MOUSE_RIGHT_BUTTON_UP,
MOUSE_MIDDLE_BUTTON_UP,
MOUSE_BUTTON_4_UP,
MOUSE_BUTTON_5_UP
};
VOID
MouHid_GetButtonMove(
IN PMOUHID_DEVICE_EXTENSION DeviceExtension,
OUT PLONG LastX,
OUT PLONG LastY)
{
NTSTATUS Status;
ULONG ValueX, ValueY;
/* init result */
*LastX = 0;
*LastY = 0;
if (!DeviceExtension->MouseAbsolute)
{
/* get scaled usage value x */
Status = HidP_GetScaledUsageValue(HidP_Input,
HID_USAGE_PAGE_GENERIC,
HIDP_LINK_COLLECTION_UNSPECIFIED,
HID_USAGE_GENERIC_X,
LastX,
DeviceExtension->PreparsedData,
DeviceExtension->Report,
DeviceExtension->ReportLength);
if (Status != HIDP_STATUS_SUCCESS)
{
/* FIXME: handle more errors */
if (Status == HIDP_STATUS_BAD_LOG_PHY_VALUES)
{
/* FIXME: assume it operates in absolute mode */
DeviceExtension->MouseAbsolute = TRUE;
/* get unscaled value */
Status = HidP_GetUsageValue(HidP_Input,
HID_USAGE_PAGE_GENERIC,
HIDP_LINK_COLLECTION_UNSPECIFIED,
HID_USAGE_GENERIC_X,
&ValueX,
DeviceExtension->PreparsedData,
DeviceExtension->Report,
DeviceExtension->ReportLength);
/* FIXME handle error */
ASSERT(Status == HIDP_STATUS_SUCCESS);
/* absolute pointing devices values need be in range 0 - 0xffff */
ASSERT(DeviceExtension->ValueCapsX.LogicalMax > 0);
ASSERT(DeviceExtension->ValueCapsX.LogicalMax > DeviceExtension->ValueCapsX.LogicalMin);
/* convert to logical range */
*LastX = (ValueX * VIRTUAL_SCREEN_SIZE_X) / DeviceExtension->ValueCapsX.LogicalMax;
}
}
}
else
{
/* get unscaled value */
Status = HidP_GetUsageValue(HidP_Input,
HID_USAGE_PAGE_GENERIC,
HIDP_LINK_COLLECTION_UNSPECIFIED,
HID_USAGE_GENERIC_X,
&ValueX,
DeviceExtension->PreparsedData,
DeviceExtension->Report,
DeviceExtension->ReportLength);
/* FIXME handle error */
ASSERT(Status == HIDP_STATUS_SUCCESS);
/* absolute pointing devices values need be in range 0 - 0xffff */
ASSERT(DeviceExtension->ValueCapsX.LogicalMax > 0);
ASSERT(DeviceExtension->ValueCapsX.LogicalMax > DeviceExtension->ValueCapsX.LogicalMin);
/* convert to logical range */
*LastX = (ValueX * VIRTUAL_SCREEN_SIZE_X) / DeviceExtension->ValueCapsX.LogicalMax;
}
if (!DeviceExtension->MouseAbsolute)
{
/* get scaled usage value y */
Status = HidP_GetScaledUsageValue(HidP_Input,
HID_USAGE_PAGE_GENERIC,
HIDP_LINK_COLLECTION_UNSPECIFIED,
HID_USAGE_GENERIC_Y,
LastY,
DeviceExtension->PreparsedData,
DeviceExtension->Report,
DeviceExtension->ReportLength);
if (Status != HIDP_STATUS_SUCCESS)
{
// FIXME: handle more errors
if (Status == HIDP_STATUS_BAD_LOG_PHY_VALUES)
{
// assume it operates in absolute mode
DeviceExtension->MouseAbsolute = TRUE;
// get unscaled value
Status = HidP_GetUsageValue(HidP_Input,
HID_USAGE_PAGE_GENERIC,
HIDP_LINK_COLLECTION_UNSPECIFIED,
HID_USAGE_GENERIC_Y,
&ValueY,
DeviceExtension->PreparsedData,
DeviceExtension->Report,
DeviceExtension->ReportLength);
/* FIXME handle error */
ASSERT(Status == HIDP_STATUS_SUCCESS);
/* absolute pointing devices values need be in range 0 - 0xffff */
ASSERT(DeviceExtension->ValueCapsY.LogicalMax > 0);
ASSERT(DeviceExtension->ValueCapsY.LogicalMax > DeviceExtension->ValueCapsY.LogicalMin);
/* convert to logical range */
*LastY = (ValueY * VIRTUAL_SCREEN_SIZE_Y) / DeviceExtension->ValueCapsY.LogicalMax;
}
}
}
else
{
// get unscaled value
Status = HidP_GetUsageValue(HidP_Input,
HID_USAGE_PAGE_GENERIC,
HIDP_LINK_COLLECTION_UNSPECIFIED,
HID_USAGE_GENERIC_Y,
&ValueY,
DeviceExtension->PreparsedData,
DeviceExtension->Report,
DeviceExtension->ReportLength);
/* FIXME handle error */
ASSERT(Status == HIDP_STATUS_SUCCESS);
/* absolute pointing devices values need be in range 0 - 0xffff */
ASSERT(DeviceExtension->ValueCapsY.LogicalMax > 0);
ASSERT(DeviceExtension->ValueCapsY.LogicalMax > DeviceExtension->ValueCapsY.LogicalMin);
/* convert to logical range */
*LastY = (ValueY * VIRTUAL_SCREEN_SIZE_Y) / DeviceExtension->ValueCapsY.LogicalMax;
}
}
VOID
MouHid_GetButtonFlags(
IN PMOUHID_DEVICE_EXTENSION DeviceExtension,
OUT PUSHORT ButtonFlags,
OUT PUSHORT Flags)
{
NTSTATUS Status;
USAGE Usage;
ULONG Index;
PUSAGE TempList;
ULONG CurrentUsageListLength;
/* init flags */
*ButtonFlags = 0;
*Flags = 0;
/* get usages */
CurrentUsageListLength = DeviceExtension->UsageListLength;
Status = HidP_GetUsages(HidP_Input,
HID_USAGE_PAGE_BUTTON,
HIDP_LINK_COLLECTION_UNSPECIFIED,
DeviceExtension->CurrentUsageList,
&CurrentUsageListLength,
DeviceExtension->PreparsedData,
DeviceExtension->Report,
DeviceExtension->ReportLength);
if (Status != HIDP_STATUS_SUCCESS)
{
DPRINT1("MouHid_GetButtonFlags failed to get usages with %x\n", Status);
return;
}
/* extract usage list difference */
Status = HidP_UsageListDifference(DeviceExtension->PreviousUsageList,
DeviceExtension->CurrentUsageList,
DeviceExtension->BreakUsageList,
DeviceExtension->MakeUsageList,
DeviceExtension->UsageListLength);
if (Status != HIDP_STATUS_SUCCESS)
{
DPRINT1("MouHid_GetButtonFlags failed to get usages with %x\n", Status);
return;
}
if (DeviceExtension->UsageListLength)
{
Index = 0;
do
{
/* get usage */
Usage = DeviceExtension->BreakUsageList[Index];
if (!Usage)
break;
if (Usage <= 5)
{
/* max 5 buttons supported */
*ButtonFlags |= MouHid_ButtonDownFlags[Usage];
}
/* move to next index*/
Index++;
}while(Index < DeviceExtension->UsageListLength);
}
if (DeviceExtension->UsageListLength)
{
Index = 0;
do
{
/* get usage */
Usage = DeviceExtension->MakeUsageList[Index];
if (!Usage)
break;
if (Usage <= 5)
{
/* max 5 buttons supported */
*ButtonFlags |= MouHid_ButtonUpFlags[Usage];
}
/* move to next index*/
Index++;
}while(Index < DeviceExtension->UsageListLength);
}
/* now switch the previous list with current list */
TempList = DeviceExtension->CurrentUsageList;
DeviceExtension->CurrentUsageList = DeviceExtension->PreviousUsageList;
DeviceExtension->PreviousUsageList = TempList;
if (DeviceExtension->MouseAbsolute)
{
// mouse operates absolute
*Flags |= MOUSE_MOVE_ABSOLUTE;
}
}
VOID
MouHid_DispatchInputData(
IN PMOUHID_DEVICE_EXTENSION DeviceExtension,
IN PMOUSE_INPUT_DATA InputData)
{
KIRQL OldIrql;
ULONG InputDataConsumed;
if (!DeviceExtension->ClassService)
return;
/* sanity check */
ASSERT(DeviceExtension->ClassService);
ASSERT(DeviceExtension->ClassDeviceObject);
/* raise irql */
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
/* dispatch input data */
(*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->ClassService)(DeviceExtension->ClassDeviceObject, InputData, InputData + 1, &InputDataConsumed);
/* lower irql to previous level */
KeLowerIrql(OldIrql);
}
NTSTATUS
NTAPI
MouHid_ReadCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
PMOUHID_DEVICE_EXTENSION DeviceExtension;
USHORT ButtonFlags;
LONG UsageValue;
NTSTATUS Status;
LONG LastX, LastY;
MOUSE_INPUT_DATA MouseInputData;
USHORT Flags;
/* get device extension */
DeviceExtension = Context;
if (Irp->IoStatus.Status == STATUS_PRIVILEGE_NOT_HELD ||
Irp->IoStatus.Status == STATUS_DEVICE_NOT_CONNECTED ||
Irp->IoStatus.Status == STATUS_CANCELLED ||
DeviceExtension->StopReadReport)
{
/* failed to read or should be stopped*/
DPRINT1("[MOUHID] ReadCompletion terminating read Status %x\n", Irp->IoStatus.Status);
/* report no longer active */
DeviceExtension->ReadReportActive = FALSE;
/* request stopping of the report cycle */
DeviceExtension->StopReadReport = FALSE;
/* signal completion event */
KeSetEvent(&DeviceExtension->ReadCompletionEvent, 0, 0);
return STATUS_MORE_PROCESSING_REQUIRED;
}
/* get mouse change */
MouHid_GetButtonMove(DeviceExtension, &LastX, &LastY);
/* get mouse change flags */
MouHid_GetButtonFlags(DeviceExtension, &ButtonFlags, &Flags);
/* init input data */
RtlZeroMemory(&MouseInputData, sizeof(MOUSE_INPUT_DATA));
/* init input data */
MouseInputData.ButtonFlags = ButtonFlags;
MouseInputData.Flags = Flags;
MouseInputData.LastX = LastX;
MouseInputData.LastY = LastY;
/* detect mouse wheel change */
if (DeviceExtension->MouseIdentifier == WHEELMOUSE_HID_HARDWARE)
{
/* get usage */
UsageValue = 0;
Status = HidP_GetScaledUsageValue(HidP_Input,
HID_USAGE_PAGE_GENERIC,
HIDP_LINK_COLLECTION_UNSPECIFIED,
HID_USAGE_GENERIC_WHEEL,
&UsageValue,
DeviceExtension->PreparsedData,
DeviceExtension->Report,
DeviceExtension->ReportLength);
if (Status == HIDP_STATUS_SUCCESS && UsageValue != 0)
{
/* store wheel status */
MouseInputData.ButtonFlags |= MOUSE_WHEEL;
MouseInputData.ButtonData = (USHORT)(UsageValue * WHEEL_DELTA);
}
else
{
DPRINT("[MOUHID] failed to get wheel status with %x\n", Status);
}
}
DPRINT("[MOUHID] ReportData %02x %02x %02x %02x %02x %02x %02x\n",
DeviceExtension->Report[0] & 0xFF,
DeviceExtension->Report[1] & 0xFF, DeviceExtension->Report[2] & 0xFF,
DeviceExtension->Report[3] & 0xFF, DeviceExtension->Report[4] & 0xFF,
DeviceExtension->Report[5] & 0xFF, DeviceExtension->Report[6] & 0xFF);
DPRINT("[MOUHID] LastX %ld LastY %ld Flags %x ButtonFlags %x ButtonData %x\n", MouseInputData.LastX, MouseInputData.LastY, MouseInputData.Flags, MouseInputData.ButtonFlags, MouseInputData.ButtonData);
/* dispatch mouse action */
MouHid_DispatchInputData(DeviceExtension, &MouseInputData);
/* re-init read */
MouHid_InitiateRead(DeviceExtension);
/* stop completion */
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
MouHid_InitiateRead(
IN PMOUHID_DEVICE_EXTENSION DeviceExtension)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
/* re-use irp */
IoReuseIrp(DeviceExtension->Irp, STATUS_SUCCESS);
/* init irp */
DeviceExtension->Irp->MdlAddress = DeviceExtension->ReportMDL;
/* get next stack location */
IoStack = IoGetNextIrpStackLocation(DeviceExtension->Irp);
/* init stack location */
IoStack->Parameters.Read.Length = DeviceExtension->ReportLength;
IoStack->Parameters.Read.Key = 0;
IoStack->Parameters.Read.ByteOffset.QuadPart = 0LL;
IoStack->MajorFunction = IRP_MJ_READ;
IoStack->FileObject = DeviceExtension->FileObject;
/* set completion routine */
IoSetCompletionRoutine(DeviceExtension->Irp, MouHid_ReadCompletion, DeviceExtension, TRUE, TRUE, TRUE);
/* read is active */
DeviceExtension->ReadReportActive = TRUE;
/* start the read */
Status = IoCallDriver(DeviceExtension->NextDeviceObject, DeviceExtension->Irp);
/* done */
return Status;
}
NTSTATUS
NTAPI
MouHid_CreateCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
KeSetEvent(Context, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
NTAPI
MouHid_Create(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
KEVENT Event;
PMOUHID_DEVICE_EXTENSION DeviceExtension;
DPRINT("MOUHID: IRP_MJ_CREATE\n");
/* get device extension */
DeviceExtension = DeviceObject->DeviceExtension;
/* get stack location */
IoStack = IoGetCurrentIrpStackLocation(Irp);
/* copy stack location to next */
IoCopyCurrentIrpStackLocationToNext(Irp);
/* init event */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* prepare irp */
IoSetCompletionRoutine(Irp, MouHid_CreateCompletion, &Event, TRUE, TRUE, TRUE);
/* call lower driver */
Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* request pending */
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
}
/* check for success */
if (!NT_SUCCESS(Status))
{
/* failed */
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
/* is the driver already in use */
if (DeviceExtension->FileObject == NULL)
{
/* did the caller specify correct attributes */
ASSERT(IoStack->Parameters.Create.SecurityContext);
if (IoStack->Parameters.Create.SecurityContext->DesiredAccess)
{
/* store file object */
DeviceExtension->FileObject = IoStack->FileObject;
/* reset event */
KeClearEvent(&DeviceExtension->ReadCompletionEvent);
/* initiating read */
Status = MouHid_InitiateRead(DeviceExtension);
DPRINT("[MOUHID] MouHid_InitiateRead: status %x\n", Status);
if (Status == STATUS_PENDING)
{
/* report irp is pending */
Status = STATUS_SUCCESS;
}
}
}
/* complete request */
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
NTSTATUS
NTAPI
MouHid_Close(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PMOUHID_DEVICE_EXTENSION DeviceExtension;
/* get device extension */
DeviceExtension = DeviceObject->DeviceExtension;
DPRINT("[MOUHID] IRP_MJ_CLOSE ReadReportActive %x\n", DeviceExtension->ReadReportActive);
if (DeviceExtension->ReadReportActive)
{
/* request stopping of the report cycle */
DeviceExtension->StopReadReport = TRUE;
/* wait until the reports have been read */
KeWaitForSingleObject(&DeviceExtension->ReadCompletionEvent, Executive, KernelMode, FALSE, NULL);
/* cancel irp */
IoCancelIrp(DeviceExtension->Irp);
}
DPRINT("[MOUHID] IRP_MJ_CLOSE ReadReportActive %x\n", DeviceExtension->ReadReportActive);
/* remove file object */
DeviceExtension->FileObject = NULL;
/* skip location */
IoSkipCurrentIrpStackLocation(Irp);
/* pass irp to down the stack */
return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
}
NTSTATUS
NTAPI
MouHid_InternalDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
PMOUSE_ATTRIBUTES Attributes;
PMOUHID_DEVICE_EXTENSION DeviceExtension;
PCONNECT_DATA Data;
/* get current stack location */
IoStack = IoGetCurrentIrpStackLocation(Irp);
DPRINT("[MOUHID] InternalDeviceControl %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode);
/* get device extension */
DeviceExtension = DeviceObject->DeviceExtension;
/* handle requests */
switch (IoStack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_MOUSE_QUERY_ATTRIBUTES:
/* verify output buffer length */
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUSE_ATTRIBUTES))
{
/* invalid request */
DPRINT1("[MOUHID] IOCTL_MOUSE_QUERY_ATTRIBUTES Buffer too small\n");
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_BUFFER_TOO_SMALL;
}
/* get output buffer */
Attributes = Irp->AssociatedIrp.SystemBuffer;
/* type of mouse */
Attributes->MouseIdentifier = DeviceExtension->MouseIdentifier;
/* number of buttons */
Attributes->NumberOfButtons = DeviceExtension->UsageListLength;
/* sample rate not used for usb */
Attributes->SampleRate = 0;
/* queue length */
Attributes->InputDataQueueLength = 2;
DPRINT("[MOUHID] MouseIdentifier %x\n", Attributes->MouseIdentifier);
DPRINT("[MOUHID] NumberOfButtons %x\n", Attributes->NumberOfButtons);
DPRINT("[MOUHID] SampleRate %x\n", Attributes->SampleRate);
DPRINT("[MOUHID] InputDataQueueLength %x\n", Attributes->InputDataQueueLength);
/* complete request */
Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES);
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
case IOCTL_INTERNAL_MOUSE_CONNECT:
/* verify input buffer length */
if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONNECT_DATA))
{
/* invalid request */
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_PARAMETER;
}
/* is it already connected */
if (DeviceExtension->ClassService)
{
/* already connected */
Irp->IoStatus.Status = STATUS_SHARING_VIOLATION;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SHARING_VIOLATION;
}
/* get connect data */
Data = IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
/* store connect details */
DeviceExtension->ClassDeviceObject = Data->ClassDeviceObject;
DeviceExtension->ClassService = Data->ClassService;
/* completed successfully */
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
case IOCTL_INTERNAL_MOUSE_DISCONNECT:
/* not supported */
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_NOT_IMPLEMENTED;
case IOCTL_INTERNAL_MOUSE_ENABLE:
/* not supported */
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_NOT_SUPPORTED;
case IOCTL_INTERNAL_MOUSE_DISABLE:
/* not supported */
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_DEVICE_REQUEST;
}
DPRINT1("[MOUHID] Unknown DeviceControl %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode);
/* unknown request not supported */
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_NOT_SUPPORTED;
}
NTSTATUS
NTAPI
MouHid_DeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PMOUHID_DEVICE_EXTENSION DeviceExtension;
/* get device extension */
DeviceExtension = DeviceObject->DeviceExtension;
/* skip stack location */
IoSkipCurrentIrpStackLocation(Irp);
/* pass and forget */
return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
}
NTSTATUS
NTAPI
MouHid_Power(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PMOUHID_DEVICE_EXTENSION DeviceExtension;
DeviceExtension = DeviceObject->DeviceExtension;
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(DeviceExtension->NextDeviceObject, Irp);
}
NTSTATUS
NTAPI
MouHid_SystemControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PMOUHID_DEVICE_EXTENSION DeviceExtension;
DeviceExtension = DeviceObject->DeviceExtension;
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
}
NTSTATUS
MouHid_SubmitRequest(
PDEVICE_OBJECT DeviceObject,
ULONG IoControlCode,
ULONG InputBufferSize,
PVOID InputBuffer,
ULONG OutputBufferSize,
PVOID OutputBuffer)
{
KEVENT Event;
PMOUHID_DEVICE_EXTENSION DeviceExtension;
PIRP Irp;
NTSTATUS Status;
IO_STATUS_BLOCK IoStatus;
/* get device extension */
DeviceExtension = DeviceObject->DeviceExtension;
/* init event */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* build request */
Irp = IoBuildDeviceIoControlRequest(IoControlCode,
DeviceExtension->NextDeviceObject,
InputBuffer,
InputBufferSize,
OutputBuffer,
OutputBufferSize,
FALSE,
&Event,
&IoStatus);
if (!Irp)
{
/* no memory */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* send request */
Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* wait for request to complete */
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatus.Status;
}
/* done */
return Status;
}
NTSTATUS
NTAPI
MouHid_StartDevice(
IN PDEVICE_OBJECT DeviceObject)
{
NTSTATUS Status;
ULONG Buttons;
HID_COLLECTION_INFORMATION Information;
PVOID PreparsedData;
HIDP_CAPS Capabilities;
USHORT ValueCapsLength;
HIDP_VALUE_CAPS ValueCaps;
PMOUHID_DEVICE_EXTENSION DeviceExtension;
PUSAGE Buffer;
/* get device extension */
DeviceExtension = DeviceObject->DeviceExtension;
/* query collection information */
Status = MouHid_SubmitRequest(DeviceObject,
IOCTL_HID_GET_COLLECTION_INFORMATION,
0,
NULL,
sizeof(HID_COLLECTION_INFORMATION),
&Information);
if (!NT_SUCCESS(Status))
{
/* failed to query collection information */
DPRINT1("[MOUHID] failed to obtain collection information with %x\n", Status);
return Status;
}
/* lets allocate space for preparsed data */
PreparsedData = ExAllocatePoolWithTag(NonPagedPool, Information.DescriptorSize, MOUHID_TAG);
if (!PreparsedData)
{
/* no memory */
DPRINT1("[MOUHID] no memory size %u\n", Information.DescriptorSize);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* now obtain the preparsed data */
Status = MouHid_SubmitRequest(DeviceObject,
IOCTL_HID_GET_COLLECTION_DESCRIPTOR,
0,
NULL,
Information.DescriptorSize,
PreparsedData);
if (!NT_SUCCESS(Status))
{
/* failed to get preparsed data */
DPRINT1("[MOUHID] failed to obtain collection information with %x\n", Status);
ExFreePoolWithTag(PreparsedData, MOUHID_TAG);
return Status;
}
/* lets get the caps */
Status = HidP_GetCaps(PreparsedData, &Capabilities);
if (Status != HIDP_STATUS_SUCCESS)
{
/* failed to get capabilities */
DPRINT1("[MOUHID] failed to obtain caps with %x\n", Status);
ExFreePoolWithTag(PreparsedData, MOUHID_TAG);
return Status;
}
DPRINT("[MOUHID] Usage %x UsagePage %x InputReportLength %lu\n", Capabilities.Usage, Capabilities.UsagePage, Capabilities.InputReportByteLength);
/* verify capabilities */
if ((Capabilities.Usage != HID_USAGE_GENERIC_POINTER && Capabilities.Usage != HID_USAGE_GENERIC_MOUSE) || Capabilities.UsagePage != HID_USAGE_PAGE_GENERIC)
{
/* not supported */
ExFreePoolWithTag(PreparsedData, MOUHID_TAG);
return STATUS_UNSUCCESSFUL;
}
/* init input report */
DeviceExtension->ReportLength = Capabilities.InputReportByteLength;
ASSERT(DeviceExtension->ReportLength);
DeviceExtension->Report = ExAllocatePoolWithTag(NonPagedPool, DeviceExtension->ReportLength, MOUHID_TAG);
ASSERT(DeviceExtension->Report);
RtlZeroMemory(DeviceExtension->Report, DeviceExtension->ReportLength);
/* build mdl */
DeviceExtension->ReportMDL = IoAllocateMdl(DeviceExtension->Report,
DeviceExtension->ReportLength,
FALSE,
FALSE,
NULL);
ASSERT(DeviceExtension->ReportMDL);
/* init mdl */
MmBuildMdlForNonPagedPool(DeviceExtension->ReportMDL);
/* get max number of buttons */
Buttons = HidP_MaxUsageListLength(HidP_Input,
HID_USAGE_PAGE_BUTTON,
PreparsedData);
DPRINT("[MOUHID] Buttons %lu\n", Buttons);
ASSERT(Buttons > 0);
/* now allocate an array for those buttons */
Buffer = ExAllocatePoolWithTag(NonPagedPool, sizeof(USAGE) * 4 * Buttons, MOUHID_TAG);
if (!Buffer)
{
/* no memory */
ExFreePoolWithTag(PreparsedData, MOUHID_TAG);
return STATUS_INSUFFICIENT_RESOURCES;
}
DeviceExtension->UsageListBuffer = Buffer;
/* init usage lists */
RtlZeroMemory(Buffer, sizeof(USAGE) * 4 * Buttons);
DeviceExtension->CurrentUsageList = Buffer;
Buffer += Buttons;
DeviceExtension->PreviousUsageList = Buffer;
Buffer += Buttons;
DeviceExtension->MakeUsageList = Buffer;
Buffer += Buttons;
DeviceExtension->BreakUsageList = Buffer;
/* store number of buttons */
DeviceExtension->UsageListLength = (USHORT)Buttons;
/* store preparsed data */
DeviceExtension->PreparsedData = PreparsedData;
ValueCapsLength = 1;
HidP_GetSpecificValueCaps(HidP_Input,
HID_USAGE_PAGE_GENERIC,
HIDP_LINK_COLLECTION_UNSPECIFIED,
HID_USAGE_GENERIC_X,
&DeviceExtension->ValueCapsX,
&ValueCapsLength,
PreparsedData);
ValueCapsLength = 1;
HidP_GetSpecificValueCaps(HidP_Input,
HID_USAGE_PAGE_GENERIC,
HIDP_LINK_COLLECTION_UNSPECIFIED,
HID_USAGE_GENERIC_Y,
&DeviceExtension->ValueCapsY,
&ValueCapsLength,
PreparsedData);
/* now check for wheel mouse support */
ValueCapsLength = 1;
Status = HidP_GetSpecificValueCaps(HidP_Input,
HID_USAGE_PAGE_GENERIC,
HIDP_LINK_COLLECTION_UNSPECIFIED,
HID_USAGE_GENERIC_WHEEL,
&ValueCaps,
&ValueCapsLength,
PreparsedData);
if (Status == HIDP_STATUS_SUCCESS )
{
/* mouse has wheel support */
DeviceExtension->MouseIdentifier = WHEELMOUSE_HID_HARDWARE;
DeviceExtension->WheelUsagePage = ValueCaps.UsagePage;
DPRINT("[MOUHID] mouse wheel support detected\n", Status);
}
else
{
/* check if the mouse has z-axis */
ValueCapsLength = 1;
Status = HidP_GetSpecificValueCaps(HidP_Input,
HID_USAGE_PAGE_GENERIC,
HIDP_LINK_COLLECTION_UNSPECIFIED,
HID_USAGE_GENERIC_Z,
&ValueCaps,
&ValueCapsLength,
PreparsedData);
if (Status == HIDP_STATUS_SUCCESS && ValueCapsLength == 1)
{
/* wheel support */
DeviceExtension->MouseIdentifier = WHEELMOUSE_HID_HARDWARE;
DeviceExtension->WheelUsagePage = ValueCaps.UsagePage;
DPRINT("[MOUHID] mouse wheel support detected with z-axis\n", Status);
}
}
/* check if mice is absolute */
if (DeviceExtension->ValueCapsY.IsAbsolute &&
DeviceExtension->ValueCapsX.IsAbsolute)
{
/* mice is absolute */
DeviceExtension->MouseAbsolute = TRUE;
}
/* completed successfully */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
MouHid_StartDeviceCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
KeSetEvent(Context, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
NTAPI
MouHid_FreeResources(
IN PDEVICE_OBJECT DeviceObject)
{
PMOUHID_DEVICE_EXTENSION DeviceExtension;
/* get device extension */
DeviceExtension = DeviceObject->DeviceExtension;
/* free resources */
if (DeviceExtension->PreparsedData)
{
ExFreePoolWithTag(DeviceExtension->PreparsedData, MOUHID_TAG);
DeviceExtension->PreparsedData = NULL;
}
if (DeviceExtension->UsageListBuffer)
{
ExFreePoolWithTag(DeviceExtension->UsageListBuffer, MOUHID_TAG);
DeviceExtension->UsageListBuffer = NULL;
DeviceExtension->CurrentUsageList = NULL;
DeviceExtension->PreviousUsageList = NULL;
DeviceExtension->MakeUsageList = NULL;
DeviceExtension->BreakUsageList = NULL;
}
if (DeviceExtension->ReportMDL)
{
IoFreeMdl(DeviceExtension->ReportMDL);
DeviceExtension->ReportMDL = NULL;
}
if (DeviceExtension->Report)
{
ExFreePoolWithTag(DeviceExtension->Report, MOUHID_TAG);
DeviceExtension->Report = NULL;
}
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
MouHid_Flush(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
PMOUHID_DEVICE_EXTENSION DeviceExtension;
/* get device extension */
DeviceExtension = DeviceObject->DeviceExtension;
/* skip current stack location */
IoSkipCurrentIrpStackLocation(Irp);
/* get next stack location */
IoStack = IoGetNextIrpStackLocation(Irp);
/* change request to hid flush queue request */
IoStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_FLUSH_QUEUE;
/* call device */
return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
}
NTSTATUS
NTAPI
MouHid_Pnp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
KEVENT Event;
NTSTATUS Status;
PMOUHID_DEVICE_EXTENSION DeviceExtension;
/* get device extension */
DeviceExtension = DeviceObject->DeviceExtension;
/* get current irp stack */
IoStack = IoGetCurrentIrpStackLocation(Irp);
DPRINT("[MOUHID] IRP_MJ_PNP Request: %x\n", IoStack->MinorFunction);
switch (IoStack->MinorFunction)
{
case IRP_MN_STOP_DEVICE:
case IRP_MN_SURPRISE_REMOVAL:
/* free resources */
MouHid_FreeResources(DeviceObject);
case IRP_MN_CANCEL_REMOVE_DEVICE:
case IRP_MN_QUERY_STOP_DEVICE:
case IRP_MN_CANCEL_STOP_DEVICE:
case IRP_MN_QUERY_REMOVE_DEVICE:
/* indicate success */
Irp->IoStatus.Status = STATUS_SUCCESS;
/* skip irp stack location */
IoSkipCurrentIrpStackLocation(Irp);
/* dispatch to lower device */
return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
case IRP_MN_REMOVE_DEVICE:
/* FIXME synchronization */
/* request stop */
DeviceExtension->StopReadReport = TRUE;
/* cancel irp */
IoCancelIrp(DeviceExtension->Irp);
/* free resources */
MouHid_FreeResources(DeviceObject);
/* indicate success */
Irp->IoStatus.Status = STATUS_SUCCESS;
/* skip irp stack location */
IoSkipCurrentIrpStackLocation(Irp);
/* dispatch to lower device */
Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
/* wait for completion of stop event */
KeWaitForSingleObject(&DeviceExtension->ReadCompletionEvent, Executive, KernelMode, FALSE, NULL);
/* free irp */
IoFreeIrp(DeviceExtension->Irp);
/* detach device */
IoDetachDevice(DeviceExtension->NextDeviceObject);
/* delete device */
IoDeleteDevice(DeviceObject);
/* done */
return Status;
case IRP_MN_START_DEVICE:
/* init event */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* copy stack location */
IoCopyCurrentIrpStackLocationToNext (Irp);
/* set completion routine */
IoSetCompletionRoutine(Irp, MouHid_StartDeviceCompletion, &Event, TRUE, TRUE, TRUE);
Irp->IoStatus.Status = 0;
/* pass request */
Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = Irp->IoStatus.Status;
}
if (!NT_SUCCESS(Status))
{
/* failed */
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
/* lets start the device */
Status = MouHid_StartDevice(DeviceObject);
DPRINT("MouHid_StartDevice %x\n", Status);
/* complete request */
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
/* done */
return Status;
default:
/* skip irp stack location */
IoSkipCurrentIrpStackLocation(Irp);
/* dispatch to lower device */
return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
}
}
NTSTATUS
NTAPI
MouHid_AddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject)
{
NTSTATUS Status;
PDEVICE_OBJECT DeviceObject, NextDeviceObject;
PMOUHID_DEVICE_EXTENSION DeviceExtension;
POWER_STATE State;
/* create device object */
Status = IoCreateDevice(DriverObject,
sizeof(MOUHID_DEVICE_EXTENSION),
NULL,
FILE_DEVICE_MOUSE,
0,
FALSE,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
/* failed to create device object */
return Status;
}
/* now attach it */
NextDeviceObject = IoAttachDeviceToDeviceStack(DeviceObject, PhysicalDeviceObject);
if (!NextDeviceObject)
{
/* failed to attach */
IoDeleteDevice(DeviceObject);
return STATUS_DEVICE_NOT_CONNECTED;
}
/* get device extension */
DeviceExtension = DeviceObject->DeviceExtension;
/* zero extension */
RtlZeroMemory(DeviceExtension, sizeof(MOUHID_DEVICE_EXTENSION));
/* init device extension */
DeviceExtension->MouseIdentifier = MOUSE_HID_HARDWARE;
DeviceExtension->WheelUsagePage = 0;
DeviceExtension->NextDeviceObject = NextDeviceObject;
KeInitializeEvent(&DeviceExtension->ReadCompletionEvent, NotificationEvent, FALSE);
DeviceExtension->Irp = IoAllocateIrp(NextDeviceObject->StackSize, FALSE);
/* FIXME handle allocation error */
ASSERT(DeviceExtension->Irp);
/* FIXME query parameter 'FlipFlopWheel', 'WheelScalingFactor' */
/* set power state to D0 */
State.DeviceState = PowerDeviceD0;
PoSetPowerState(DeviceObject, DevicePowerState, State);
/* init device object */
DeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
/* completed successfully */
return STATUS_SUCCESS;
}
VOID
NTAPI
MouHid_Unload(
IN PDRIVER_OBJECT DriverObject)
{
UNIMPLEMENTED;
}
NTSTATUS
NTAPI
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegPath)
{
/* FIXME check for parameters 'UseOnlyMice', 'TreatAbsoluteAsRelative', 'TreatAbsolutePointerAsAbsolute' */
/* initialize driver object */
DriverObject->MajorFunction[IRP_MJ_CREATE] = MouHid_Create;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = MouHid_Close;
DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = MouHid_Flush;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MouHid_DeviceControl;
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = MouHid_InternalDeviceControl;
DriverObject->MajorFunction[IRP_MJ_POWER] = MouHid_Power;
DriverObject->MajorFunction[IRP_MJ_PNP] = MouHid_Pnp;
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = MouHid_SystemControl;
DriverObject->DriverUnload = MouHid_Unload;
DriverObject->DriverExtension->AddDevice = MouHid_AddDevice;
/* done */
return STATUS_SUCCESS;
}