1047 lines
31 KiB
C
1047 lines
31 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1997 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
pnp.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module contains plug & play code for the HID Mouse Filter Driver,
|
||
|
including code for the creation and removal of HID mouse device contexts.
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
Kernel & user mode.
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
Jan-1997 : Initial writing, Dan Markarian
|
||
|
|
||
|
--*/
|
||
|
|
||
|
//
|
||
|
// For this module only we set the INITGUID macro before including wdm.h and
|
||
|
// hidclass.h. This not only declares the GUIDs but also initializes them.
|
||
|
//
|
||
|
|
||
|
#include "mouhid.h"
|
||
|
#include "hidclass.h"
|
||
|
#include <initguid.h>
|
||
|
#include <wdmguid.h>
|
||
|
|
||
|
#ifdef ALLOC_PRAGMA
|
||
|
#pragma alloc_text(PAGE,MouHid_CallHidClass)
|
||
|
#pragma alloc_text(PAGE,MouHid_AddDevice)
|
||
|
#pragma alloc_text(PAGE,MouHid_StartDevice)
|
||
|
#pragma alloc_text(PAGE,MouHid_PnP)
|
||
|
#endif
|
||
|
|
||
|
NTSTATUS
|
||
|
MouHid_CallHidClass(
|
||
|
IN PDEVICE_EXTENSION Data,
|
||
|
IN ULONG Ioctl,
|
||
|
IN PVOID InputBuffer,
|
||
|
IN ULONG InputBufferLength,
|
||
|
IN OUT PVOID OutputBuffer,
|
||
|
IN ULONG OutputBufferLength
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Make a *synchronous* request of the HID class driver
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Ioctl - Value of the IOCTL request.
|
||
|
|
||
|
InputBuffer - Buffer to be sent to the HID class driver.
|
||
|
|
||
|
InputBufferLength - Size of buffer to be sent to the HID class driver.
|
||
|
|
||
|
OutputBuffer - Buffer for received data from the HID class driver.
|
||
|
|
||
|
OutputBufferLength - Size of receive buffer from the HID class.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
STATUS_SUCCESS if successful,
|
||
|
STATUS_UNSUCCESSFUL otherwise
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KEVENT event;
|
||
|
IO_STATUS_BLOCK ioStatus;
|
||
|
PIRP irp;
|
||
|
PIO_STACK_LOCATION nextStack;
|
||
|
NTSTATUS status = STATUS_SUCCESS;
|
||
|
|
||
|
PAGED_CODE ();
|
||
|
|
||
|
Print (DBG_PNP_TRACE, ("PNP-CallHidClass: Enter." ));
|
||
|
|
||
|
//
|
||
|
// Prepare to issue a synchronous request.
|
||
|
//
|
||
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
|
|
||
|
irp = IoBuildDeviceIoControlRequest (
|
||
|
Ioctl,
|
||
|
Data->TopOfStack,
|
||
|
InputBuffer,
|
||
|
InputBufferLength,
|
||
|
OutputBuffer,
|
||
|
OutputBufferLength,
|
||
|
FALSE, // external IOCTL
|
||
|
&event,
|
||
|
&ioStatus);
|
||
|
|
||
|
if (irp == NULL) {
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Call the class driver to perform the operation. If the returned status
|
||
|
// is PENDING, wait for the request to complete.
|
||
|
//
|
||
|
|
||
|
nextStack = IoGetNextIrpStackLocation(irp);
|
||
|
ASSERT(nextStack != NULL);
|
||
|
|
||
|
status = IoCallDriver(Data->TopOfStack, irp);
|
||
|
|
||
|
if (status == STATUS_PENDING) {
|
||
|
|
||
|
status = KeWaitForSingleObject(
|
||
|
&event,
|
||
|
Executive, // wait reason
|
||
|
KernelMode,
|
||
|
FALSE, // not alertable
|
||
|
NULL); // no time out
|
||
|
}
|
||
|
|
||
|
if (NT_SUCCESS (status)) {
|
||
|
status = ioStatus.Status;
|
||
|
}
|
||
|
|
||
|
Print (DBG_PNP_TRACE, ("PNP-CallHidClass: Enter." ));
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
MouHid_QueryDeviceKey (
|
||
|
IN HANDLE Handle,
|
||
|
IN PWCHAR ValueNameString,
|
||
|
OUT PVOID Data,
|
||
|
IN ULONG DataLength
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS status;
|
||
|
UNICODE_STRING valueName;
|
||
|
ULONG length;
|
||
|
PKEY_VALUE_FULL_INFORMATION fullInfo;
|
||
|
|
||
|
RtlInitUnicodeString (&valueName, ValueNameString);
|
||
|
|
||
|
length = sizeof (KEY_VALUE_FULL_INFORMATION)
|
||
|
+ valueName.MaximumLength
|
||
|
+ DataLength;
|
||
|
|
||
|
fullInfo = ExAllocatePool (PagedPool, length);
|
||
|
|
||
|
if (fullInfo) {
|
||
|
status = ZwQueryValueKey (Handle,
|
||
|
&valueName,
|
||
|
KeyValueFullInformation,
|
||
|
fullInfo,
|
||
|
length,
|
||
|
&length);
|
||
|
|
||
|
if (NT_SUCCESS (status)) {
|
||
|
ASSERT (DataLength == fullInfo->DataLength);
|
||
|
RtlCopyMemory (Data,
|
||
|
((PUCHAR) fullInfo) + fullInfo->DataOffset,
|
||
|
fullInfo->DataLength);
|
||
|
}
|
||
|
|
||
|
ExFreePool (fullInfo);
|
||
|
} else {
|
||
|
status = STATUS_NO_MEMORY;
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
MouHid_AddDevice (
|
||
|
IN PDRIVER_OBJECT Driver,
|
||
|
IN PDEVICE_OBJECT PDO
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS result code.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS status = STATUS_SUCCESS;
|
||
|
PDEVICE_EXTENSION data;
|
||
|
PDEVICE_OBJECT device;
|
||
|
HANDLE devInstRegKey;
|
||
|
ULONG tmp = 0;
|
||
|
POWER_STATE state;
|
||
|
|
||
|
PAGED_CODE ();
|
||
|
|
||
|
|
||
|
Print (DBG_PNP_TRACE, ("enter Add Device \n"));
|
||
|
|
||
|
status = IoCreateDevice(Driver,
|
||
|
sizeof(DEVICE_EXTENSION),
|
||
|
NULL, // no name for this Filter DO
|
||
|
FILE_DEVICE_MOUSE,
|
||
|
0,
|
||
|
FALSE,
|
||
|
&device);
|
||
|
|
||
|
if (!NT_SUCCESS (status)) {
|
||
|
return( status );
|
||
|
}
|
||
|
|
||
|
data = (PDEVICE_EXTENSION) device->DeviceExtension;
|
||
|
|
||
|
//
|
||
|
// Initialize the fields.
|
||
|
//
|
||
|
data->TopOfStack = IoAttachDeviceToDeviceStack (device, PDO);
|
||
|
if (data->TopOfStack == NULL) {
|
||
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
||
|
|
||
|
//
|
||
|
// Not good; in only extreme cases will this fail
|
||
|
//
|
||
|
errorLogEntry = (PIO_ERROR_LOG_PACKET)
|
||
|
IoAllocateErrorLogEntry(Driver,
|
||
|
(UCHAR) sizeof(IO_ERROR_LOG_PACKET));
|
||
|
|
||
|
if (errorLogEntry) {
|
||
|
errorLogEntry->ErrorCode = MOUHID_ATTACH_DEVICE_FAILED;
|
||
|
errorLogEntry->DumpDataSize = 0;
|
||
|
errorLogEntry->SequenceNumber = 0;
|
||
|
errorLogEntry->MajorFunctionCode = 0;
|
||
|
errorLogEntry->IoControlCode = 0;
|
||
|
errorLogEntry->RetryCount = 0;
|
||
|
errorLogEntry->UniqueErrorValue = 0;
|
||
|
errorLogEntry->FinalStatus = STATUS_DEVICE_NOT_CONNECTED;
|
||
|
|
||
|
IoWriteErrorLogEntry(errorLogEntry);
|
||
|
}
|
||
|
|
||
|
IoDeleteDevice(device);
|
||
|
return STATUS_DEVICE_NOT_CONNECTED;
|
||
|
}
|
||
|
|
||
|
ASSERT (data->TopOfStack);
|
||
|
|
||
|
data->Self = device;
|
||
|
data->Started = FALSE;
|
||
|
data->Initialized = FALSE;
|
||
|
data->UnitId = (USHORT) InterlockedIncrement (&Globals.UnitId);
|
||
|
data->PDO = PDO;
|
||
|
data->ReadIrp = IoAllocateIrp (data->TopOfStack->StackSize, FALSE);
|
||
|
// Initializiation happens automatically.
|
||
|
if (NULL == data->ReadIrp) {
|
||
|
IoDetachDevice (data->TopOfStack);
|
||
|
IoDeleteDevice (device);
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
|
||
|
KeInitializeEvent (&data->ReadCompleteEvent, SynchronizationEvent, FALSE);
|
||
|
KeInitializeEvent (&data->ReadSentEvent, NotificationEvent, TRUE);
|
||
|
IoInitializeRemoveLock (&data->RemoveLock, MOUHID_POOL_TAG, 1, 10);
|
||
|
data->ReadFile = NULL;
|
||
|
ExInitializeFastMutex (&data->CreateCloseMutex);
|
||
|
|
||
|
data->InputData.UnitId = data->UnitId;
|
||
|
|
||
|
//
|
||
|
// Initialize the mouse attributes.
|
||
|
//
|
||
|
data->Attributes.MouseIdentifier = MOUSE_HID_HARDWARE;
|
||
|
data->Attributes.SampleRate = 0;
|
||
|
data->Attributes.InputDataQueueLength = 2;
|
||
|
|
||
|
//
|
||
|
// Find device specific parameters for this hid mouse device.
|
||
|
//
|
||
|
|
||
|
if (NT_SUCCESS (status)) {
|
||
|
status = IoOpenDeviceRegistryKey (PDO,
|
||
|
PLUGPLAY_REGKEY_DEVICE,
|
||
|
STANDARD_RIGHTS_ALL,
|
||
|
&devInstRegKey);
|
||
|
|
||
|
data->FlipFlop = FALSE;
|
||
|
|
||
|
if (NT_SUCCESS (status)) {
|
||
|
status = MouHid_QueryDeviceKey (devInstRegKey,
|
||
|
FLIP_FLOP_WHEEL,
|
||
|
&tmp,
|
||
|
sizeof (tmp));
|
||
|
if (NT_SUCCESS (status)) {
|
||
|
data->FlipFlop = (BOOLEAN) tmp;
|
||
|
}
|
||
|
status = MouHid_QueryDeviceKey (devInstRegKey,
|
||
|
SCALING_FACTOR_WHEEL,
|
||
|
&tmp,
|
||
|
sizeof (tmp));
|
||
|
if (NT_SUCCESS (status)) {
|
||
|
data->WheelScalingFactor = (ULONG) tmp;
|
||
|
} else {
|
||
|
data->WheelScalingFactor = 120;
|
||
|
}
|
||
|
ZwClose (devInstRegKey);
|
||
|
}
|
||
|
status = STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
state.DeviceState = PowerDeviceD0;
|
||
|
PoSetPowerState (device, DevicePowerState, state);
|
||
|
|
||
|
data->WmiLibInfo.GuidCount = sizeof (MouHid_WmiGuidList) /
|
||
|
sizeof (WMIGUIDREGINFO);
|
||
|
ASSERT (1 == data->WmiLibInfo.GuidCount);
|
||
|
data->WmiLibInfo.GuidList = MouHid_WmiGuidList;
|
||
|
data->WmiLibInfo.QueryWmiRegInfo = MouHid_QueryWmiRegInfo;
|
||
|
data->WmiLibInfo.QueryWmiDataBlock = MouHid_QueryWmiDataBlock;
|
||
|
data->WmiLibInfo.SetWmiDataBlock = MouHid_SetWmiDataBlock;
|
||
|
data->WmiLibInfo.SetWmiDataItem = MouHid_SetWmiDataItem;
|
||
|
data->WmiLibInfo.ExecuteWmiMethod = NULL;
|
||
|
data->WmiLibInfo.WmiFunctionControl = NULL;
|
||
|
|
||
|
device->Flags |= DO_POWER_PAGABLE;
|
||
|
device->Flags &= ~DO_DEVICE_INITIALIZING;
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
MouHid_StartDevice (
|
||
|
IN PDEVICE_EXTENSION Data
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS result code.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
HIDP_CAPS caps; // the capabilities of the found hid device
|
||
|
HID_COLLECTION_INFORMATION info;
|
||
|
NTSTATUS status = STATUS_SUCCESS;
|
||
|
PHIDP_PREPARSED_DATA preparsedData = NULL;
|
||
|
PHID_EXTENSION hid = NULL;
|
||
|
ULONG length, inputBufferLength, usageListLength;
|
||
|
USHORT maxUsages;
|
||
|
PCHAR buffer;
|
||
|
USHORT slength;
|
||
|
HIDP_VALUE_CAPS valueCaps;
|
||
|
|
||
|
PAGED_CODE ();
|
||
|
|
||
|
Print (DBG_PNP_TRACE, ("enter START Device \n"));
|
||
|
|
||
|
//
|
||
|
// Retrieve the capabilities of this hid device
|
||
|
// IOCTL_HID_GET_COLLECTION_INFORMATION fills in HID_COLLECTION_INFORMATION.
|
||
|
// we are interested in the Descriptor Size, which tells us how big a
|
||
|
// buffer to allocate for the preparsed data.
|
||
|
//
|
||
|
if (!NT_SUCCESS (status = MouHid_CallHidClass (
|
||
|
Data,
|
||
|
IOCTL_HID_GET_COLLECTION_INFORMATION,
|
||
|
0, 0, // no input
|
||
|
&info, sizeof (info)))) {
|
||
|
goto MouHid_StartDeviceReject;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Allocate memory to hold the preparsed data.
|
||
|
//
|
||
|
preparsedData = (PHIDP_PREPARSED_DATA)
|
||
|
ExAllocatePool (NonPagedPool, info.DescriptorSize);
|
||
|
|
||
|
if (!preparsedData) {
|
||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
goto MouHid_StartDeviceReject;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Retrieve that information.
|
||
|
//
|
||
|
|
||
|
if (!NT_SUCCESS (status = MouHid_CallHidClass (
|
||
|
Data,
|
||
|
IOCTL_HID_GET_COLLECTION_DESCRIPTOR,
|
||
|
0, 0, // no input
|
||
|
preparsedData, info.DescriptorSize))) {
|
||
|
goto MouHid_StartDeviceReject;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Call the parser to determine the capabilites of this HID device.
|
||
|
//
|
||
|
|
||
|
if (!NT_SUCCESS (status = HidP_GetCaps (preparsedData, &caps))) {
|
||
|
goto MouHid_StartDeviceReject;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Is this the thing we want?
|
||
|
//
|
||
|
// In this particular case we are looking for a keyboard.
|
||
|
//
|
||
|
if ( (HID_USAGE_PAGE_GENERIC == caps.UsagePage) &&
|
||
|
( (HID_USAGE_GENERIC_MOUSE == caps.Usage) ||
|
||
|
( (HID_USAGE_GENERIC_POINTER == caps.Usage) &&
|
||
|
(!Globals.UseOnlyMice)))) {
|
||
|
;
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// Someone made an INF blunder!
|
||
|
//
|
||
|
ASSERT ( (HID_USAGE_PAGE_GENERIC == caps.UsagePage) &&
|
||
|
( (HID_USAGE_GENERIC_MOUSE == caps.Usage) ||
|
||
|
( (HID_USAGE_GENERIC_POINTER == caps.Usage) &&
|
||
|
(!Globals.UseOnlyMice))));
|
||
|
|
||
|
status = STATUS_UNSUCCESSFUL;
|
||
|
|
||
|
goto MouHid_StartDeviceReject;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Note: here we might also want to check the button and value capabilities
|
||
|
// of the device as well.
|
||
|
//
|
||
|
// Then let's use it.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// a buffer length to allow an Input buffer, output buffer, feature buffer,
|
||
|
// and the total number of usages that can be returned from a read packet.
|
||
|
//
|
||
|
|
||
|
maxUsages = (USHORT) HidP_MaxUsageListLength (HidP_Input,
|
||
|
HID_USAGE_PAGE_BUTTON,
|
||
|
preparsedData);
|
||
|
|
||
|
//
|
||
|
// Create space in the device extension for the buffer storage when working
|
||
|
// with this HID device.
|
||
|
//
|
||
|
// We need four buffers to hold the button codes (length returned from
|
||
|
// HidP_MaxUsageListLength) this will hold the current list of usages,
|
||
|
// the previous list of usages, the ``Make'' and the ``Break'' lists.
|
||
|
// We also need a place to put the input, output, and feature report
|
||
|
// buffers.
|
||
|
//
|
||
|
|
||
|
usageListLength = ALIGNPTRLEN(maxUsages * sizeof (USAGE));
|
||
|
inputBufferLength = ALIGNPTRLEN(caps.InputReportByteLength);
|
||
|
|
||
|
length = (4 * usageListLength)
|
||
|
+ inputBufferLength
|
||
|
+ sizeof (HID_EXTENSION);
|
||
|
|
||
|
Data->HidExtension = hid = ExAllocatePool (NonPagedPool, length);
|
||
|
|
||
|
if (!hid) {
|
||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
goto MouHid_StartDeviceReject;
|
||
|
}
|
||
|
|
||
|
RtlZeroMemory (hid, length);
|
||
|
|
||
|
//
|
||
|
// Initialize the fields.
|
||
|
//
|
||
|
hid->Ppd = preparsedData;
|
||
|
hid->Caps = caps;
|
||
|
hid->MaxUsages = maxUsages;
|
||
|
|
||
|
Data->Attributes.NumberOfButtons = (USHORT) maxUsages;
|
||
|
|
||
|
|
||
|
hid->InputBuffer = buffer = hid->Buffer;
|
||
|
hid->PreviousUsageList = (PUSAGE) (buffer += inputBufferLength);
|
||
|
hid->CurrentUsageList = (PUSAGE) (buffer += usageListLength);
|
||
|
hid->BreakUsageList = (PUSAGE) (buffer += usageListLength);
|
||
|
hid->MakeUsageList = (PUSAGE) (buffer + usageListLength);
|
||
|
|
||
|
//
|
||
|
// Create the MDLs
|
||
|
// HidClass uses direct IO so you need MDLs
|
||
|
//
|
||
|
|
||
|
hid->InputMdl = IoAllocateMdl (hid->InputBuffer, // The virtual address
|
||
|
caps.InputReportByteLength, // length
|
||
|
FALSE, // No associated IRP => not secondary
|
||
|
FALSE, // No quota charge
|
||
|
0); // No associated IRP
|
||
|
if (NULL == hid->InputMdl) {
|
||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
goto MouHid_StartDeviceReject;
|
||
|
}
|
||
|
MmBuildMdlForNonPagedPool (hid->InputMdl); // Build this MDL.
|
||
|
|
||
|
//
|
||
|
// Determine if X,Y,Z values are absolute or relative for this device.
|
||
|
// Only check X axis (assume Y,Z are the same -- we have no choice but
|
||
|
// to make this assumption since the MOUSE_INPUT_DATA structure does
|
||
|
// not accomodate mixed absolute/relative position fields).
|
||
|
//
|
||
|
slength = 1;
|
||
|
if (!NT_SUCCESS (status = HidP_GetSpecificValueCaps(
|
||
|
HidP_Input,
|
||
|
HID_USAGE_PAGE_GENERIC,
|
||
|
0,
|
||
|
HID_USAGE_GENERIC_X,
|
||
|
&valueCaps,
|
||
|
&slength,
|
||
|
preparsedData) ) ) {
|
||
|
goto MouHid_StartDeviceReject;
|
||
|
}
|
||
|
|
||
|
ASSERT (1 == slength);
|
||
|
|
||
|
if (valueCaps.IsAbsolute) {
|
||
|
if ((HID_USAGE_GENERIC_POINTER == caps.Usage) &&
|
||
|
(Globals.TreatAbsolutePointerAsAbsolute)) {
|
||
|
//
|
||
|
// All pointers that declare themselfs as Absolute should be
|
||
|
// treated as such, regardless of the TreatAbsoluteAsRelative flag
|
||
|
//
|
||
|
Data->InputData.Flags = MOUSE_MOVE_ABSOLUTE;
|
||
|
hid->IsAbsolute = TRUE;
|
||
|
|
||
|
} else if (Globals.TreatAbsoluteAsRelative) {
|
||
|
//
|
||
|
// Here we have overriden the HID descriptors absolute flag.
|
||
|
// We will treat this as a relative device even though it claims
|
||
|
// to be an absolute device.
|
||
|
//
|
||
|
Data->InputData.Flags = MOUSE_MOVE_RELATIVE;
|
||
|
hid->IsAbsolute = FALSE;
|
||
|
|
||
|
//
|
||
|
// Report the problem with this mouse's report descriptor and
|
||
|
// report it to the user.
|
||
|
//
|
||
|
Data->ProblemFlags |= PROBLEM_BAD_ABSOLUTE_FLAG_X_Y;
|
||
|
|
||
|
MouHid_LogError(Data->Self->DriverObject,
|
||
|
MOUHID_INVALID_ABSOLUTE_AXES,
|
||
|
NULL);
|
||
|
} else {
|
||
|
//
|
||
|
// No switches with which to play. Do what seems natural
|
||
|
//
|
||
|
Data->InputData.Flags = MOUSE_MOVE_ABSOLUTE;
|
||
|
hid->IsAbsolute = TRUE;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
Data->InputData.Flags = MOUSE_MOVE_RELATIVE;
|
||
|
hid->IsAbsolute = FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Determine X axis usage value's bit size.
|
||
|
//
|
||
|
hid->BitSize.X = valueCaps.BitSize;
|
||
|
hid->MaxX = valueCaps.PhysicalMax;
|
||
|
hid->MaxX = (hid->MaxX) ? (hid->MaxX) : ((1 << (hid->BitSize.X - 1)) - 1);
|
||
|
|
||
|
|
||
|
//
|
||
|
// Determine Y axis usage value's bit size.
|
||
|
//
|
||
|
slength = 1;
|
||
|
if (!NT_SUCCESS (status = HidP_GetSpecificValueCaps(
|
||
|
HidP_Input,
|
||
|
HID_USAGE_PAGE_GENERIC,
|
||
|
0,
|
||
|
HID_USAGE_GENERIC_Y,
|
||
|
&valueCaps,
|
||
|
&slength,
|
||
|
preparsedData) ) ) {
|
||
|
goto MouHid_StartDeviceReject;
|
||
|
}
|
||
|
ASSERT (1 == slength);
|
||
|
|
||
|
hid->BitSize.Y = valueCaps.BitSize;
|
||
|
hid->MaxY = valueCaps.PhysicalMax;
|
||
|
hid->MaxY = (hid->MaxY) ? (hid->MaxY) : ((1 << (hid->BitSize.Y - 1)) - 1);
|
||
|
|
||
|
//
|
||
|
// Initialize wheel usage not-detected flag to false (determined later).
|
||
|
//
|
||
|
hid->HasNoWheelUsage = FALSE;
|
||
|
hid->HasNoZUsage = FALSE;
|
||
|
|
||
|
//
|
||
|
// Determine Z axis usage value's bit size (if this is a wheel mouse).
|
||
|
// Note that a Z axis may not exist, so we handle this case differently.
|
||
|
//
|
||
|
|
||
|
slength = 1;
|
||
|
if (NT_SUCCESS (HidP_GetSpecificValueCaps(
|
||
|
HidP_Input,
|
||
|
HID_USAGE_PAGE_GENERIC,
|
||
|
0,
|
||
|
HID_USAGE_GENERIC_WHEEL,
|
||
|
&valueCaps,
|
||
|
&slength,
|
||
|
preparsedData) ) && slength == 1) {
|
||
|
hid->BitSize.Z = valueCaps.BitSize;
|
||
|
Data->Attributes.MouseIdentifier = WHEELMOUSE_HID_HARDWARE;
|
||
|
} else {
|
||
|
// hid->HasNoWheelUsage = TRUE;
|
||
|
|
||
|
slength = 1;
|
||
|
if (NT_SUCCESS (HidP_GetSpecificValueCaps(
|
||
|
HidP_Input,
|
||
|
HID_USAGE_PAGE_GENERIC,
|
||
|
0,
|
||
|
HID_USAGE_GENERIC_Z,
|
||
|
&valueCaps,
|
||
|
&slength,
|
||
|
preparsedData) ) && slength == 1) {
|
||
|
hid->BitSize.Z = valueCaps.BitSize;
|
||
|
Data->Attributes.MouseIdentifier = WHEELMOUSE_HID_HARDWARE;
|
||
|
} else {
|
||
|
// hid->HasNoZUsage = TRUE;
|
||
|
hid->BitSize.Z = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We are done. Return peacefully.
|
||
|
//
|
||
|
return status;
|
||
|
|
||
|
MouHid_StartDeviceReject:
|
||
|
if (preparsedData) {
|
||
|
// no need to set hid->Ppd to NULL becuase we will be freeing it as well
|
||
|
ExFreePool (preparsedData);
|
||
|
}
|
||
|
if (hid) {
|
||
|
if (hid->InputMdl) {
|
||
|
IoFreeMdl (hid->InputMdl);
|
||
|
}
|
||
|
ExFreePool (hid);
|
||
|
Data->HidExtension = NULL;
|
||
|
}
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
MouHid_PnP (
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
The plug and play dispatch routines.
|
||
|
|
||
|
Most of these this filter driver will completely ignore.
|
||
|
In all cases it must pass on the IRP to the lower driver.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceObject - pointer to a device object.
|
||
|
|
||
|
Irp - pointer to an I/O Request Packet.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PDEVICE_EXTENSION data;
|
||
|
PHID_EXTENSION hid;
|
||
|
PIO_STACK_LOCATION stack;
|
||
|
NTSTATUS status;
|
||
|
ULONG i, j;
|
||
|
LONG ioCount;
|
||
|
PDEVICE_EXTENSION * classDataList;
|
||
|
|
||
|
PAGED_CODE ();
|
||
|
|
||
|
data = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
||
|
stack = IoGetCurrentIrpStackLocation (Irp);
|
||
|
hid = data->HidExtension;
|
||
|
|
||
|
status = IoAcquireRemoveLock (&data->RemoveLock, Irp);
|
||
|
if (!NT_SUCCESS (status)) {
|
||
|
//
|
||
|
// Someone gave us a pnp irp after a remove. Unthinkable!
|
||
|
//
|
||
|
ASSERT (FALSE);
|
||
|
Irp->IoStatus.Information = 0;
|
||
|
Irp->IoStatus.Status = status;
|
||
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
Print(DBG_PNP_TRACE, ("PNP: Minor code = %x.", stack->MinorFunction));
|
||
|
|
||
|
switch (stack->MinorFunction) {
|
||
|
case IRP_MN_START_DEVICE:
|
||
|
|
||
|
if (data->Started) {
|
||
|
Print(DBG_PNP_INFO, ("PNP: Device already started." ));
|
||
|
status = STATUS_SUCCESS;
|
||
|
Irp->IoStatus.Status = status;
|
||
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The device is starting.
|
||
|
//
|
||
|
// We cannot touch the device (send it any non pnp irps) until a
|
||
|
// start device has been passed down to the lower drivers.
|
||
|
//
|
||
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
||
|
KeInitializeEvent(&data->StartEvent, NotificationEvent, FALSE);
|
||
|
IoSetCompletionRoutine (Irp,
|
||
|
MouHid_PnPComplete,
|
||
|
data,
|
||
|
TRUE,
|
||
|
TRUE,
|
||
|
TRUE); // No need for Cancel
|
||
|
|
||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
status = IoCallDriver (data->TopOfStack, Irp);
|
||
|
if (STATUS_PENDING == status) {
|
||
|
KeWaitForSingleObject(
|
||
|
&data->StartEvent,
|
||
|
Executive, // Waiting for reason of a driver
|
||
|
KernelMode, // Waiting in kernel mode
|
||
|
FALSE, // No allert
|
||
|
NULL); // No timeout
|
||
|
}
|
||
|
|
||
|
if (NT_SUCCESS (status) && NT_SUCCESS (Irp->IoStatus.Status)) {
|
||
|
//
|
||
|
// As we are successfully now back from our start device
|
||
|
// we can do work.
|
||
|
//
|
||
|
if (!data->Initialized) {
|
||
|
status = MouHid_StartDevice (data);
|
||
|
if (NT_SUCCESS (status)) {
|
||
|
IoWMIRegistrationControl(DeviceObject,
|
||
|
WMIREG_ACTION_REGISTER
|
||
|
);
|
||
|
|
||
|
data->Started = TRUE;
|
||
|
data->Initialized = TRUE;
|
||
|
}
|
||
|
} else {
|
||
|
data->Started = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We must now complete the IRP, since we stopped it in the
|
||
|
// completetion routine with MORE_PROCESSING_REQUIRED.
|
||
|
//
|
||
|
Irp->IoStatus.Status = status;
|
||
|
Irp->IoStatus.Information = 0;
|
||
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
||
|
break;
|
||
|
|
||
|
case IRP_MN_STOP_DEVICE:
|
||
|
//
|
||
|
// After the start IRP has been sent to the lower driver object, the
|
||
|
// bus may NOT send any more IRPS down ``touch'' until another START
|
||
|
// has occured.
|
||
|
// What ever access is required must be done before the Irp is passed
|
||
|
// on.
|
||
|
//
|
||
|
|
||
|
if (data->Started) {
|
||
|
//
|
||
|
// Do what ever
|
||
|
//
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We don't need a completion routine so fire and forget.
|
||
|
//
|
||
|
// Set the current stack location to the next stack location and
|
||
|
// call the next device object.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// Stop Device touching the hardware MouStopDevice(data, TRUE);
|
||
|
//
|
||
|
data->Started = FALSE;
|
||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
IoSkipCurrentIrpStackLocation (Irp);
|
||
|
status = IoCallDriver (data->TopOfStack, Irp);
|
||
|
break;
|
||
|
|
||
|
case IRP_MN_REMOVE_DEVICE:
|
||
|
//
|
||
|
// The PlugPlay system has dictacted the removal of this device. We
|
||
|
// have no choise but to detach and delete the device objecct.
|
||
|
// (If we wanted to express and interest in preventing this removal,
|
||
|
// we should have filtered the query remove and query stop routines.)
|
||
|
//
|
||
|
// Note! we might receive a remove WITHOUT first receiving a stop.
|
||
|
// ASSERT (!usbData->Removed);
|
||
|
Print (DBG_PNP_TRACE, ("enter RemoveDevice \n"));
|
||
|
|
||
|
IoWMIRegistrationControl(data->Self,
|
||
|
WMIREG_ACTION_DEREGISTER
|
||
|
);
|
||
|
|
||
|
if (data->Started) {
|
||
|
// Stop the device without touching the hardware.
|
||
|
// MouStopDevice(data, FALSE);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Here if we had any outstanding requests in a personal queue we should
|
||
|
// complete them all now.
|
||
|
//
|
||
|
// Note, the device could be GONE so we cannot send it any non-
|
||
|
// PNP IRPS.
|
||
|
//
|
||
|
|
||
|
|
||
|
//
|
||
|
// Cancel our read IRP. [DAN]
|
||
|
// Note - waiting is only really necessary on 98, where pnp doesn't
|
||
|
// make sure all handles are closed before sending the remove.
|
||
|
//
|
||
|
data->ShuttingDown = TRUE;
|
||
|
KeWaitForSingleObject (&data->ReadSentEvent,
|
||
|
Executive,
|
||
|
KernelMode,
|
||
|
FALSE,
|
||
|
NULL
|
||
|
);
|
||
|
IoCancelIrp(data->ReadIrp);
|
||
|
|
||
|
//
|
||
|
// Send on the remove IRP
|
||
|
//
|
||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
IoSkipCurrentIrpStackLocation (Irp);
|
||
|
status = IoCallDriver (data->TopOfStack, Irp);
|
||
|
|
||
|
//
|
||
|
// Wait for the remove lock to free.
|
||
|
//
|
||
|
IoReleaseRemoveLockAndWait (&data->RemoveLock, Irp);
|
||
|
|
||
|
//
|
||
|
// Free the associated memory.
|
||
|
//
|
||
|
IoFreeIrp (data->ReadIrp);
|
||
|
|
||
|
if (hid) {
|
||
|
//
|
||
|
// If we are removed without being started then we will have
|
||
|
// no hid extension
|
||
|
//
|
||
|
ExFreePool (hid->Ppd);
|
||
|
IoFreeMdl (hid->InputMdl);
|
||
|
ExFreePool (hid);
|
||
|
}
|
||
|
|
||
|
IoDetachDevice (data->TopOfStack);
|
||
|
IoDeleteDevice (data->Self);
|
||
|
return status;
|
||
|
|
||
|
case IRP_MN_SURPRISE_REMOVAL:
|
||
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||
|
case IRP_MN_QUERY_STOP_DEVICE:
|
||
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
||
|
//
|
||
|
// These IRPs have to have their status changed from
|
||
|
// STATUS_NOT_SUPPORTED b4 passing them down.
|
||
|
//
|
||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
|
||
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
||
|
case IRP_MN_QUERY_INTERFACE:
|
||
|
case IRP_MN_QUERY_CAPABILITIES:
|
||
|
case IRP_MN_QUERY_RESOURCES:
|
||
|
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
|
||
|
case IRP_MN_READ_CONFIG:
|
||
|
case IRP_MN_WRITE_CONFIG:
|
||
|
case IRP_MN_EJECT:
|
||
|
case IRP_MN_SET_LOCK:
|
||
|
case IRP_MN_QUERY_ID:
|
||
|
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
||
|
default:
|
||
|
//
|
||
|
// Here the filter driver might modify the behavior of these IRPS
|
||
|
// Please see PlugPlay documentation for use of these IRPs.
|
||
|
//
|
||
|
IoSkipCurrentIrpStackLocation (Irp);
|
||
|
status = IoCallDriver (data->TopOfStack, Irp);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
IoReleaseRemoveLock (&data->RemoveLock, Irp);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
MouHid_PnPComplete (
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp,
|
||
|
IN PVOID Context
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The pnp IRP is in the process of completing.
|
||
|
signal
|
||
|
|
||
|
Arguments:
|
||
|
Context set to the device object in question.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PIO_STACK_LOCATION stack;
|
||
|
PDEVICE_EXTENSION data;
|
||
|
NTSTATUS status;
|
||
|
|
||
|
UNREFERENCED_PARAMETER (DeviceObject);
|
||
|
|
||
|
status = STATUS_SUCCESS;
|
||
|
data = (PDEVICE_EXTENSION) Context;
|
||
|
stack = IoGetCurrentIrpStackLocation (Irp);
|
||
|
|
||
|
if (Irp->PendingReturned) {
|
||
|
IoMarkIrpPending( Irp );
|
||
|
}
|
||
|
|
||
|
switch (stack->MajorFunction) {
|
||
|
case IRP_MJ_PNP:
|
||
|
|
||
|
switch (stack->MinorFunction) {
|
||
|
case IRP_MN_START_DEVICE:
|
||
|
|
||
|
KeSetEvent (&data->StartEvent, 0, FALSE);
|
||
|
|
||
|
//
|
||
|
// Take the IRP back so that we can continue using it during
|
||
|
// the IRP_MN_START_DEVICE dispatch routine.
|
||
|
// NB: we will have to call IoCompleteRequest
|
||
|
//
|
||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case IRP_MJ_POWER:
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
MouHid_Power (
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp
|
||
|
)
|
||
|
{
|
||
|
PIO_STACK_LOCATION stack;
|
||
|
NTSTATUS status;
|
||
|
PDEVICE_EXTENSION data;
|
||
|
POWER_STATE powerState;
|
||
|
POWER_STATE_TYPE powerType;
|
||
|
|
||
|
Print(DBG_POWER_TRACE, ("Power Enter." ));
|
||
|
|
||
|
data = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
||
|
stack = IoGetCurrentIrpStackLocation (Irp);
|
||
|
powerType = stack->Parameters.Power.Type;
|
||
|
powerState = stack->Parameters.Power.State;
|
||
|
|
||
|
status = IoAcquireRemoveLock (&data->RemoveLock, Irp);
|
||
|
|
||
|
if (!NT_SUCCESS (status)) {
|
||
|
PoStartNextPowerIrp (Irp);
|
||
|
Irp->IoStatus.Status = status;
|
||
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
switch (stack->MinorFunction) {
|
||
|
case IRP_MN_SET_POWER:
|
||
|
Print(DBG_POWER_INFO, ("Power Setting %s state to %d\n",
|
||
|
((powerType == SystemPowerState) ? "System"
|
||
|
: "Device"),
|
||
|
powerState.SystemState));
|
||
|
break;
|
||
|
|
||
|
case IRP_MN_QUERY_POWER:
|
||
|
Print (DBG_POWER_INFO, ("Power query %s status to %d\n",
|
||
|
((powerType == SystemPowerState) ? "System"
|
||
|
: "Device"),
|
||
|
powerState.SystemState));
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Print (DBG_POWER_ERROR, ("Power minor (0x%x) no known\n",
|
||
|
stack->MinorFunction));
|
||
|
}
|
||
|
|
||
|
PoStartNextPowerIrp (Irp);
|
||
|
IoSkipCurrentIrpStackLocation (Irp);
|
||
|
status = PoCallDriver (data->TopOfStack, Irp);
|
||
|
IoReleaseRemoveLock (&data->RemoveLock, Irp);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
|