1162 lines
32 KiB
C
1162 lines
32 KiB
C
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
KBDHID.C
|
|
|
|
Abstract:
|
|
|
|
This module contains the init code for the i8042 to hid converter.
|
|
|
|
Note: This is NOT a WDM driver, since it cannot run as a HID mapper on
|
|
Memphis (Memphis requires that the keyboard to HID mapper be a VXD) and
|
|
since it uses Event logs, which are not part of WDM 1.0
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
Nov-96 : created by Kenneth D. Ray
|
|
Jan-97 : Dan Markarian : Made work
|
|
May-97 : Kenneth D. Ray : reconstructed as PnP filter for Keyboard class
|
|
|
|
--*/
|
|
|
|
#include "kbdhid.h"
|
|
#include "hidclass.h"
|
|
|
|
//
|
|
// Use the alloc_text pragma to specify the driver initialization routines
|
|
// (they can be paged out). [DAN]
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,DriverEntry)
|
|
#pragma alloc_text(INIT,KbdHid_GetRegistryParameters)
|
|
#pragma alloc_text(PAGE,KbdHid_UpdateRegistryProblemFlagsCallback)
|
|
#pragma alloc_text(PAGE,KbdHid_Unload)
|
|
#endif
|
|
|
|
GLOBALS Globals;
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Installable driver initialization entry point.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to the driver object
|
|
|
|
RegistryPath - pointer to a unicode string representing the path
|
|
to driver-specific key in the registry
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PUNICODE_STRING registryPath = &Globals.RegistryPath;
|
|
|
|
Print (DBG_SS_TRACE, ("entering DriverEntry\n"));
|
|
Print (DBG_SS_INFO, ("Keyboard to hid mapper\n"));
|
|
|
|
RtlZeroMemory (&Globals, sizeof (GLOBALS));
|
|
|
|
//
|
|
// Need to ensure that the registry path is null-terminated.
|
|
// Allocate pool to hold a null-terminated copy of the path.
|
|
// Safe in paged pool since all registry routines execute at
|
|
// PASSIVE_LEVEL.
|
|
//
|
|
registryPath->MaximumLength = RegistryPath->Length + sizeof(UNICODE_NULL);
|
|
registryPath->Length = RegistryPath->Length;
|
|
registryPath->Buffer = ExAllocatePool(
|
|
PagedPool,
|
|
registryPath->MaximumLength
|
|
);
|
|
|
|
if (!registryPath->Buffer) {
|
|
|
|
Print (DBG_SS_ERROR,
|
|
("Initialize: Couldn't allocate pool for registry path."));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto DriverEntryReject;
|
|
}
|
|
|
|
RtlZeroMemory (registryPath->Buffer, registryPath->MaximumLength);
|
|
|
|
RtlMoveMemory (registryPath->Buffer,
|
|
RegistryPath->Buffer,
|
|
RegistryPath->Length);
|
|
|
|
//
|
|
// Obtain configuration information for this driver.
|
|
//
|
|
status = KbdHid_GetRegistryParameters();
|
|
if (!NT_SUCCESS (status)) {
|
|
goto DriverEntryReject;
|
|
}
|
|
|
|
//
|
|
// Set up the device driver entry points.
|
|
//
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = KbdHid_Create;
|
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = KbdHid_Close;
|
|
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL]=KbdHid_IOCTL;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KbdHid_PassThrough;
|
|
DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = KbdHid_Flush;
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = KbdHid_PnP;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = KbdHid_Power;
|
|
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = KbdHid_SystemControl;
|
|
DriverObject->DriverUnload = KbdHid_Unload;
|
|
DriverObject->DriverExtension->AddDevice = KbdHid_AddDevice;
|
|
|
|
Print (DBG_SS_TRACE, ("exit DriverEntry (0x%x) \n", status));
|
|
|
|
return status;
|
|
|
|
DriverEntryReject:
|
|
|
|
if (registryPath->Buffer) {
|
|
ExFreePool (registryPath->Buffer);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
KbdHid_PassThrough (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Pass the irp on through
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION data;
|
|
|
|
data = DeviceObject->DeviceExtension;
|
|
|
|
status = IoAcquireRemoveLock (&data->RemoveLock, Irp);
|
|
if (!NT_SUCCESS (status)) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = IoCallDriver (data->TopOfStack, Irp);
|
|
IoReleaseRemoveLock (&data->RemoveLock, Irp);
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
KbdHid_Unload(
|
|
IN PDRIVER_OBJECT Driver
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free all the allocated resources, etc.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to a driver object
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (NULL == Driver->DeviceObject);
|
|
|
|
Print (DBG_SS_INFO, ("Unload \n"));
|
|
|
|
//
|
|
// Free resources in device extension.
|
|
//
|
|
ExFreePool (Globals.RegistryPath.Buffer);
|
|
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
KbdHid_Flush(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Respond to flush requests from the mouse class driver. Currently does
|
|
nothing but pass IRP down to next lower driver. This routine expects
|
|
the current IRQL to be < DISPATCH_LEVEL.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION data;
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION stack;
|
|
|
|
Print (DBG_CALL_INFO, ("Flush \n"));
|
|
|
|
TRAP();
|
|
|
|
//
|
|
// Get a pointer to the device extension.
|
|
//
|
|
data = DeviceObject->DeviceExtension;
|
|
|
|
status = IoAcquireRemoveLock (&data->RemoveLock, Irp);
|
|
if (!NT_SUCCESS (status)) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Send the flush request down to the HID class driver, one for each
|
|
// of our mouse device context structures.
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
|
stack = IoGetNextIrpStackLocation (Irp);
|
|
|
|
stack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|
stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_FLUSH_QUEUE;
|
|
|
|
//
|
|
// Fire and forget
|
|
//
|
|
status = IoCallDriver (data->TopOfStack, Irp);
|
|
IoReleaseRemoveLock (&data->RemoveLock, Irp);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
KbdHid_IOCTL (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Respond to queries from the mouse class driver.
|
|
|
|
The IOCTLs for DISABLE, ENABLE, and QUERY_ATTRIBUTES, expect the current
|
|
IRQL to be < DISPATCH_LEVEL.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION stack;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION data;
|
|
ULONG length;
|
|
PKEYBOARD_INDICATOR_TRANSLATION translation;
|
|
BOOLEAN completeIt = TRUE;
|
|
|
|
data = DeviceObject->DeviceExtension;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
stack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
status = IoAcquireRemoveLock (&data->RemoveLock, Irp);
|
|
if (!NT_SUCCESS (status)) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
ASSERT (data->Started ||
|
|
(IOCTL_INTERNAL_KEYBOARD_CONNECT ==
|
|
stack->Parameters.DeviceIoControl.IoControlCode));
|
|
|
|
switch (stack->Parameters.DeviceIoControl.IoControlCode) {
|
|
case IOCTL_INTERNAL_KEYBOARD_CONNECT:
|
|
//
|
|
// Connect a keyboard class device driver to the port driver.
|
|
//
|
|
|
|
Print (DBG_IOCTL_TRACE, ("enter Connect \n"));
|
|
|
|
//
|
|
// Connect a mouse class device driver to the filter driver.
|
|
// Only allow one connection.
|
|
//
|
|
if (NULL != data->ConnectData.ClassService) {
|
|
|
|
Print (DBG_IOCTL_ERROR, ("ERROR: Multiple connects \n"));
|
|
TRAP ();
|
|
status = STATUS_SHARING_VIOLATION;
|
|
break;
|
|
|
|
} else if (stack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CONNECT_DATA)) {
|
|
|
|
Print (DBG_IOCTL_ERROR, ("ERROR: Invalid connect parameter size. \n"));
|
|
TRAP ();
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy the connection parameters to the device extension.
|
|
//
|
|
data->ConnectData = *(PCONNECT_DATA)
|
|
stack->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_INTERNAL_KEYBOARD_DISCONNECT:
|
|
//
|
|
// Disconnect a keyboard class device driver from the port driver.
|
|
//
|
|
Print (DBG_IOCTL_TRACE, ("Disconnect \n"));
|
|
TRAP ();
|
|
|
|
//
|
|
// Not implemented.
|
|
//
|
|
// To implement, code the following:
|
|
// ---------------------------------
|
|
// o ENSURE that we are NOT enabled (mouHidDeviceExt->EnableCount);
|
|
// o If we are, then (a) return STATUS_UNSUCCESSFUL, or
|
|
// (b) disable all devices immediately; see
|
|
// DISABLE IOCTL call for necessary code.
|
|
// o SYNCHRONIZE with the mouse read completion routine (must
|
|
// protect the callback pointer from being dereferenced when
|
|
// it becomes null). Note that no mechanism currently exists
|
|
// for this.
|
|
// o CLEAR the connection parameters in the device extension;
|
|
// ie. mouHidDeviceExt->MouClassObject = NULL;
|
|
// mouHidDeviceExt->MouClassCallback = NULL;
|
|
// o RELEASE the synchronizing lock.
|
|
// o RETURN STATUS_SUCCESS.
|
|
//
|
|
|
|
status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
case IOCTL_INTERNAL_KEYBOARD_ENABLE:
|
|
//
|
|
// Enable keyboard interrupts which really means start the ping pong
|
|
// down to hid class.
|
|
//
|
|
Print (DBG_IOCTL_ERROR, ("ERROR: PnP => use create not enable! \n"));
|
|
status = STATUS_NOT_SUPPORTED;
|
|
|
|
break;
|
|
|
|
case IOCTL_INTERNAL_KEYBOARD_DISABLE:
|
|
//
|
|
// Disable keyboard interrupts which really means stop the ping pongs
|
|
// down to hid class.
|
|
//
|
|
Print (DBG_IOCTL_ERROR, ("ERROR: PnP => use close not Disable! \n"));
|
|
status = STATUS_NOT_SUPPORTED;
|
|
|
|
break;
|
|
|
|
case IOCTL_KEYBOARD_QUERY_ATTRIBUTES:
|
|
//
|
|
// Query the keyboard attributes. First check for adequate buffer
|
|
// length. Then, copy the keyboard attributes from the first device
|
|
// context to the output buffer. [DAN]
|
|
//
|
|
Print (DBG_IOCTL_TRACE, ("Query Attributes \n"));
|
|
|
|
if (stack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(KEYBOARD_ATTRIBUTES)) {
|
|
|
|
Print (DBG_IOCTL_ERROR,
|
|
("ERROR: Query Attributes buffer too small \n"));
|
|
TRAP();
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
|
|
//
|
|
// Copy the keyboard attributes to the buffer.
|
|
//
|
|
|
|
*(PKEYBOARD_ATTRIBUTES) Irp->AssociatedIrp.SystemBuffer =
|
|
data->Attributes;
|
|
|
|
Irp->IoStatus.Information = sizeof (KEYBOARD_ATTRIBUTES);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION:
|
|
//
|
|
// Query the scan code to indicator-light mapping. Validate the
|
|
// parameters, and copy the indicator mapping information from
|
|
// the static translation list to the SystemBuffer.
|
|
//
|
|
Print (DBG_IOCTL_TRACE, ("Query Indicator Translation \n"));
|
|
|
|
TRAP ();
|
|
length = sizeof(KEYBOARD_INDICATOR_TRANSLATION)
|
|
+ (sizeof(INDICATOR_LIST)
|
|
* (HID_KEYBOARD_NUMBER_OF_INDICATORS - 1));
|
|
|
|
if (stack->Parameters.DeviceIoControl.OutputBufferLength < length) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy the indicator mapping information to the system
|
|
// buffer.
|
|
//
|
|
|
|
translation = ((PKEYBOARD_INDICATOR_TRANSLATION)
|
|
Irp->AssociatedIrp.SystemBuffer);
|
|
|
|
translation->NumberOfIndicatorKeys = HID_KEYBOARD_NUMBER_OF_INDICATORS;
|
|
RtlMoveMemory(translation->IndicatorList,
|
|
(PCHAR) IndicatorList,
|
|
length - FIELD_OFFSET (KEYBOARD_INDICATOR_TRANSLATION,
|
|
IndicatorList));
|
|
|
|
Irp->IoStatus.Information = length;
|
|
status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case IOCTL_KEYBOARD_QUERY_INDICATORS:
|
|
//
|
|
// Query the keyboard indicators. Validate the parameters, and
|
|
// copy the indicator information from the device context to
|
|
// the SystemBuffer. [DAN]
|
|
//
|
|
Print (DBG_IOCTL_TRACE, ("Query Indicators \n"));
|
|
|
|
if (stack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(KEYBOARD_INDICATOR_PARAMETERS)) {
|
|
|
|
TRAP ();
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy the keyboard indicators to the buffer.
|
|
//
|
|
//
|
|
// Don't bother to synchronize access to the device context
|
|
// KeyboardIndicators field while copying it. We don't care
|
|
// if another process is setting the LEDs.
|
|
//
|
|
|
|
// Copy the keyboard indicators to the buffer.
|
|
*(PKEYBOARD_INDICATOR_PARAMETERS) Irp->AssociatedIrp.SystemBuffer =
|
|
data->Indicators;
|
|
|
|
Irp->IoStatus.Information = sizeof(KEYBOARD_INDICATOR_PARAMETERS);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_KEYBOARD_SET_INDICATORS:
|
|
//
|
|
// Set the keyboard indicators for all known device contexts. [DAN]
|
|
//
|
|
Print (DBG_IOCTL_TRACE, ("Set Indicators \n"));
|
|
|
|
if (stack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(KEYBOARD_INDICATOR_PARAMETERS)) {
|
|
|
|
Print (DBG_IOCTL_ERROR, ("ERROR: Set Indicators size!\n"));
|
|
TRAP ();
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
status = KbdHid_SetLedIndicators (
|
|
data,
|
|
(PKEYBOARD_INDICATOR_PARAMETERS) Irp->AssociatedIrp.SystemBuffer,
|
|
Irp);
|
|
|
|
completeIt = FALSE;
|
|
|
|
break;
|
|
|
|
case IOCTL_KEYBOARD_QUERY_TYPEMATIC:
|
|
//
|
|
// Query the current keyboard typematic rate and delay. Validate
|
|
// the parameters, and copy the typematic information from the port
|
|
// device extension to the SystemBuffer. [DAN]
|
|
//
|
|
Print (DBG_IOCTL_TRACE, ("Query Typematic \n"));
|
|
|
|
if (stack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)) {
|
|
|
|
Print (DBG_IOCTL_ERROR, ("ERROR: query typematic size!\n"));
|
|
TRAP ();
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy the keyboard typematic info to the buffer.
|
|
//
|
|
|
|
//
|
|
// Don't bother to synchronize access to the device context
|
|
// KeyboardTypematic field while copying it. We don't care
|
|
// if another process is setting the typematic info.
|
|
//
|
|
|
|
*(PKEYBOARD_TYPEMATIC_PARAMETERS) Irp->AssociatedIrp.SystemBuffer =
|
|
data->Typematic;
|
|
|
|
Irp->IoStatus.Information = sizeof(KEYBOARD_TYPEMATIC_PARAMETERS);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_KEYBOARD_SET_TYPEMATIC:
|
|
//
|
|
// Set the keyboard typematic rate and delay for all known device
|
|
// contexts. [DAN]
|
|
//
|
|
Print (DBG_IOCTL_TRACE, ("Set Typematic \n"));
|
|
|
|
if (stack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)) {
|
|
|
|
Print (DBG_IOCTL_ERROR, ("ERROR: Set Typematic size\n"));
|
|
TRAP ();
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
}
|
|
|
|
#define NewTypematic ((PKEYBOARD_TYPEMATIC_PARAMETERS) \
|
|
Irp->AssociatedIrp.SystemBuffer)
|
|
|
|
if ((NewTypematic->Rate == 0) && (NewTypematic->Delay == 0)) {
|
|
break;
|
|
}
|
|
|
|
if ((NewTypematic->Rate < data->Attributes.KeyRepeatMinimum.Rate) ||
|
|
(NewTypematic->Rate > data->Attributes.KeyRepeatMaximum.Rate) ||
|
|
(NewTypematic->Delay < data->Attributes.KeyRepeatMinimum.Delay) ||
|
|
(NewTypematic->Delay > data->Attributes.KeyRepeatMaximum.Delay)) {
|
|
|
|
Print (DBG_IOCTL_ERROR, ("ERROR: Set Typematic range\n"));
|
|
TRAP ();
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
Print (DBG_IOCTL_INFO,
|
|
("Set Typematic Rate: %d Delay: %d\n",
|
|
NewTypematic->Rate,
|
|
NewTypematic->Delay));
|
|
|
|
//
|
|
// Don't bother to synchronize access to the device context
|
|
// KeyboardTypematic field while copying it. We don't care
|
|
// if another thread is reading the typematic info.
|
|
//
|
|
// Note the only danger here is in setting the 64-bit integer
|
|
// "AutoRepeatDelay" in two non-atomic statements. However,
|
|
// we are safe since we never set "HighPart" to anything but
|
|
// -1.
|
|
//
|
|
data->Typematic = *NewTypematic;
|
|
|
|
data->AutoRepeatRate = 1000 / NewTypematic->Rate; // ms
|
|
data->AutoRepeatDelay.LowPart = -NewTypematic->Delay*10000; // 100ns
|
|
data->AutoRepeatDelay.HighPart = -1;
|
|
break;
|
|
|
|
#undef NewTypematic
|
|
|
|
default:
|
|
Print (DBG_IOCTL_ERROR,
|
|
("ERROR: unknown IOCTL: 0x%x \n",
|
|
stack->Parameters.DeviceIoControl.IoControlCode));
|
|
TRAP ();
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
if (completeIt) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
IoReleaseRemoveLock (&data->RemoveLock, Irp);
|
|
|
|
Print (DBG_IOCTL_TRACE, ("IOCTL exit (%x)\n", status));
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
KbdHid_GetRegistryParameters()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves this driver's parameters from the registry,
|
|
including it's base device name.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
|
|
UNICODE_STRING parametersPath = {0,0,0};
|
|
PWSTR path = NULL;
|
|
USHORT queriesPlus1 = 2;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE ();
|
|
|
|
parametersPath.Buffer = NULL;
|
|
|
|
//
|
|
// Registry path is already null-terminated, so just use it as a string.
|
|
//
|
|
path = Globals.RegistryPath.Buffer;
|
|
|
|
//
|
|
// Allocate the Rtl query table.
|
|
//
|
|
parameters = ExAllocatePool(
|
|
PagedPool,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * queriesPlus1);
|
|
|
|
if (!parameters) {
|
|
|
|
Print (DBG_SS_ERROR,
|
|
("Initialize: Couldn't allocate table for Rtl query to parameters for %ws.",
|
|
path));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto KbdHid_GetRegistryParametersExit;
|
|
}
|
|
|
|
RtlZeroMemory(parameters, sizeof(RTL_QUERY_REGISTRY_TABLE) * queriesPlus1);
|
|
|
|
//
|
|
// Form a path to this driver's Parameters subkey.
|
|
//
|
|
parametersPath.MaximumLength = Globals.RegistryPath.Length
|
|
+ sizeof(L"\\Parameters");
|
|
|
|
parametersPath.Buffer = ExAllocatePool(PagedPool,
|
|
parametersPath.MaximumLength);
|
|
|
|
if (!parametersPath.Buffer) {
|
|
|
|
Print (DBG_SS_ERROR,
|
|
("Initialize: Couldn't allocate string for path to parameters for %ws.",
|
|
path));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto KbdHid_GetRegistryParametersExit;
|
|
}
|
|
|
|
//
|
|
// Form the parameters path.
|
|
//
|
|
RtlZeroMemory(parametersPath.Buffer, parametersPath.MaximumLength);
|
|
RtlAppendUnicodeToString(¶metersPath, path);
|
|
RtlAppendUnicodeToString(¶metersPath, L"\\Parameters");
|
|
|
|
#if 0 // We ignore the device name since this is now plug and play
|
|
|
|
//
|
|
// Gather all of the "user specified" information from
|
|
// the registry.
|
|
//
|
|
parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[0].Name = L"KeyboardDeviceBaseName";
|
|
parameters[0].EntryContext = DeviceName;
|
|
parameters[0].DefaultType = REG_SZ;
|
|
parameters[0].DefaultData = defaultUnicodeName.Buffer;
|
|
parameters[0].DefaultLength = 0;
|
|
|
|
|
|
status = RtlQueryRegistryValues (RTL_REGISTRY_ABSOLUTE,
|
|
parametersPath.Buffer,
|
|
parameters,
|
|
NULL,
|
|
NULL);
|
|
#endif
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
Print (DBG_SS_ERROR,
|
|
("ERROR: Initialize: RtlQueryRegistryValues (0x%x).",
|
|
status));
|
|
}
|
|
|
|
|
|
KbdHid_GetRegistryParametersExit:
|
|
//
|
|
// Free the allocated memory before returning.
|
|
//
|
|
if (parametersPath.Buffer)
|
|
ExFreePool(parametersPath.Buffer);
|
|
if (parameters)
|
|
ExFreePool(parameters);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
KbdHid_SetLedIndicatorsComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PIO_STACK_LOCATION stack;
|
|
PDEVICE_EXTENSION data;
|
|
|
|
stack = IoGetCurrentIrpStackLocation (Irp);
|
|
data = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
if (Irp->PendingReturned) {
|
|
IoMarkIrpPending (Irp);
|
|
}
|
|
|
|
IoFreeMdl (Irp->MdlAddress);
|
|
Irp->MdlAddress = (PMDL) stack->Parameters.Others.Argument4;
|
|
|
|
ExFreePool (Context);
|
|
|
|
IoReleaseRemoveLock (&data->RemoveLock, Irp);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
KbdHid_SetLedIndicators (
|
|
PDEVICE_EXTENSION Data,
|
|
PKEYBOARD_INDICATOR_PARAMETERS NewIndicators,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the LED indicators of the supplied keyboard device context.
|
|
|
|
Arguments:
|
|
|
|
Data - Pointer to the driver device extension.
|
|
|
|
Parameters - Pointer to the keyboard indicators to set/unset.
|
|
|
|
Irp - An Irp to use for setting these parameters
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS on success, STATUS_PENDING if operation is still pending,
|
|
or otherwise an NTSTATUS error code on an error.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION nextStack;
|
|
PIO_STACK_LOCATION curStack;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
USAGE usageBuffer [4]; // only 4 known usages hardcoded below
|
|
ULONG usageBufferLen = 0;
|
|
PCHAR outputBuffer = 0;
|
|
PMDL outputMdl = 0;
|
|
PHID_EXTENSION hid = Data->HidExtension;
|
|
|
|
status = IoAcquireRemoveLock (&Data->RemoveLock, Irp);
|
|
if (!NT_SUCCESS (status)) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
if (0 == hid->Caps.OutputReportByteLength) {
|
|
//
|
|
// This device has no LEDs, now while that is strange it is not really
|
|
// an error. HID keyboards can have any LEDs that they want, including
|
|
// none.
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
goto KbdHid_SetIndicatorsReject;
|
|
}
|
|
|
|
outputBuffer = ExAllocatePool (NonPagedPool,
|
|
hid->Caps.OutputReportByteLength);
|
|
|
|
if (NULL == outputBuffer) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto KbdHid_SetIndicatorsReject;
|
|
}
|
|
|
|
outputMdl = IoAllocateMdl (outputBuffer, // The virtual address
|
|
hid->Caps.OutputReportByteLength, // length of the MDL
|
|
FALSE, // No associated IRP -> not secondary
|
|
FALSE, // No quota charge
|
|
0); // No associated IRP
|
|
|
|
if (NULL == outputMdl) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto KbdHid_SetIndicatorsReject;
|
|
}
|
|
|
|
MmBuildMdlForNonPagedPool (outputMdl); // Build this MDL.
|
|
|
|
//
|
|
// Zero the output report packet.
|
|
//
|
|
RtlZeroMemory(outputBuffer, hid->Caps.OutputReportByteLength);
|
|
|
|
//
|
|
// Setup the usage list of LEDs.
|
|
//
|
|
if (NewIndicators->LedFlags & KEYBOARD_KANA_LOCK_ON) {
|
|
usageBuffer[usageBufferLen++] = HID_USAGE_LED_KANA;
|
|
}
|
|
if (NewIndicators->LedFlags & KEYBOARD_CAPS_LOCK_ON) {
|
|
usageBuffer[usageBufferLen++] = HID_USAGE_LED_CAPS_LOCK;
|
|
}
|
|
if (NewIndicators->LedFlags & KEYBOARD_NUM_LOCK_ON) {
|
|
usageBuffer[usageBufferLen++] = HID_USAGE_LED_NUM_LOCK;
|
|
}
|
|
if (NewIndicators->LedFlags & KEYBOARD_SCROLL_LOCK_ON) {
|
|
usageBuffer[usageBufferLen++] = HID_USAGE_LED_SCROLL_LOCK;
|
|
}
|
|
|
|
if (usageBufferLen == 0) {
|
|
ASSERT((NewIndicators->LedFlags & (KEYBOARD_KANA_LOCK_ON |
|
|
KEYBOARD_CAPS_LOCK_ON |
|
|
KEYBOARD_NUM_LOCK_ON |
|
|
KEYBOARD_SCROLL_LOCK_ON)) == 0);
|
|
//
|
|
// In order to fix led setting on a multiple collection keyboard, we
|
|
// have to initialize the output report to make sure we get the correct
|
|
// collection id. This is for the case where we are going from one
|
|
// led on to all leds off. If not initialized, we'll get a report with no
|
|
// collection id at the beginning.
|
|
//
|
|
usageBuffer[0] = HID_USAGE_LED_SCROLL_LOCK; // arbitirary led
|
|
usageBufferLen = 1;
|
|
|
|
HidP_UnsetUsages(HidP_Output,
|
|
HID_USAGE_PAGE_LED,
|
|
0,
|
|
usageBuffer,
|
|
&usageBufferLen,
|
|
hid->Ppd,
|
|
outputBuffer,
|
|
hid->Caps.OutputReportByteLength);
|
|
|
|
}
|
|
else {
|
|
//
|
|
// Set the usages in the output report.
|
|
//
|
|
HidP_SetUsages(HidP_Output,
|
|
HID_USAGE_PAGE_LED,
|
|
0,
|
|
usageBuffer,
|
|
&usageBufferLen,
|
|
hid->Ppd,
|
|
outputBuffer,
|
|
hid->Caps.OutputReportByteLength);
|
|
}
|
|
|
|
//
|
|
// Obtain a pointer to the next IRP stack location.
|
|
//
|
|
nextStack = IoGetNextIrpStackLocation (Irp);
|
|
curStack = IoGetCurrentIrpStackLocation (Irp);
|
|
ASSERT(nextStack != NULL);
|
|
|
|
//
|
|
// Set up our write to HIDCLASS.
|
|
//
|
|
curStack->Parameters.Others.Argument4 = (PVOID) Irp->MdlAddress;
|
|
Irp->MdlAddress = outputMdl;
|
|
|
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
|
nextStack->MajorFunction = IRP_MJ_WRITE;
|
|
nextStack->Parameters.Write.Length = hid->Caps.OutputReportByteLength;
|
|
nextStack->Parameters.Write.Key = 0;
|
|
nextStack->Parameters.Write.ByteOffset.QuadPart = 0;
|
|
|
|
//
|
|
// Hook a completion routine to be called when the request completes.
|
|
//
|
|
|
|
IoSetCompletionRoutine (Irp,
|
|
KbdHid_SetLedIndicatorsComplete,
|
|
outputBuffer,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
//
|
|
// Call the next driver.
|
|
//
|
|
status = IoCallDriver(Data->TopOfStack, Irp);
|
|
|
|
//
|
|
// Return status.
|
|
//
|
|
return status;
|
|
|
|
KbdHid_SetIndicatorsReject:
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
if (NULL != outputBuffer) {
|
|
ExFreePool (outputBuffer);
|
|
}
|
|
if (NULL != outputMdl) {
|
|
IoFreeMdl (outputMdl);
|
|
}
|
|
IoReleaseRemoveLock (&Data->RemoveLock, Irp);
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
KbdHid_UpdateRegistryProblemFlags(
|
|
IN PDEVICE_EXTENSION Data
|
|
)
|
|
/*++
|
|
|
|
[DAN]
|
|
|
|
Routine Description:
|
|
|
|
This routine stores the OR'd ProblemFlags value into the registry.
|
|
It will queue the write to the registry if this routine is not run
|
|
at PASSIVE_LEVEL.
|
|
|
|
Arguments:
|
|
|
|
KbdHidDeviceExt - HID Keyboard Filter Driver device extension.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_WORKITEM item;
|
|
NTSTATUS status;
|
|
|
|
status = IoAcquireRemoveLock (&Data->RemoveLock, KbdHid_UpdateRegistryProblemFlags);
|
|
if (!NT_SUCCESS (status)) {
|
|
//
|
|
// Device has gone away, just silently exit
|
|
//
|
|
return;
|
|
}
|
|
|
|
item = IoAllocateWorkItem (Data->Self);
|
|
|
|
if (item) {
|
|
|
|
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
|
//
|
|
// We are safely at PASSIVE_LEVEL, call callback directly to perform
|
|
// this operation immediately.
|
|
//
|
|
KbdHid_UpdateRegistryProblemFlagsCallback (Data->Self, item);
|
|
|
|
} else {
|
|
//
|
|
// We are not at PASSIVE_LEVEL, so queue a workitem to handle this
|
|
// at a later time.
|
|
//
|
|
IoQueueWorkItem (item,
|
|
KbdHid_UpdateRegistryProblemFlagsCallback,
|
|
DelayedWorkQueue,
|
|
item);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Match the Acquire at the top of the function
|
|
//
|
|
IoReleaseRemoveLock (&Data->RemoveLock, KbdHid_UpdateRegistryProblemFlags);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
KbdHid_UpdateRegistryProblemFlagsCallback (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIO_WORKITEM Item
|
|
)
|
|
/*++
|
|
|
|
[DAN]
|
|
|
|
Routine Description:
|
|
|
|
This routine stores the OR'd ProblemFlags value into the registry. This
|
|
routine must execute at PASSIVE_LEVEL.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION data;
|
|
HANDLE hDevNode;
|
|
UNICODE_STRING strProblemFlags;
|
|
|
|
PAGED_CODE ();
|
|
|
|
data = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
status = IoOpenDeviceRegistryKey (data->PDO,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_ALL,
|
|
&hDevNode);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
RtlInitUnicodeString (&strProblemFlags, L"ProblemFlags");
|
|
|
|
ZwSetValueKey (hDevNode,
|
|
&strProblemFlags,
|
|
0,
|
|
REG_DWORD,
|
|
&data->ProblemFlags,
|
|
sizeof(data->ProblemFlags));
|
|
|
|
ZwClose(hDevNode);
|
|
}
|
|
else {
|
|
Print(DBG_SS_ERROR,
|
|
("UpdateRegistryProblemFlags: failed (%x).\n", status));
|
|
}
|
|
|
|
IoReleaseRemoveLock(&data->RemoveLock, KbdHid_UpdateRegistryProblemFlags);
|
|
IoFreeWorkItem(Item);
|
|
}
|
|
|
|
VOID
|
|
KbdHid_LogError(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN NTSTATUS ErrorCode,
|
|
IN PWSTR ErrorInsertionString OPTIONAL)
|
|
/*++
|
|
|
|
[DAN]
|
|
|
|
Routine Description:
|
|
|
|
Logs an error to the system.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object reporting the error.
|
|
|
|
ErrorCode - Indicates the type of error, system or driver-defined.
|
|
|
|
ErrorInsertionString - Null-terminated Unicode string inserted into error
|
|
description, as defined by error code. Must be no
|
|
no longer than 50 characters.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG errorInsertionStringSize = 0;
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
ULONG errorLogEntrySize; // [including null]
|
|
PWCHAR pWChar;
|
|
|
|
if (ErrorInsertionString) {
|
|
for (pWChar = ErrorInsertionString; *pWChar; pWChar++) {
|
|
errorInsertionStringSize += sizeof(WCHAR);
|
|
}
|
|
errorInsertionStringSize += sizeof(UNICODE_NULL);
|
|
}
|
|
|
|
errorLogEntrySize = sizeof(IO_ERROR_LOG_PACKET) + errorInsertionStringSize;
|
|
|
|
//
|
|
// Log an error.
|
|
//
|
|
if (errorLogEntrySize <= ERROR_LOG_MAXIMUM_SIZE) {
|
|
|
|
errorLogEntry = IoAllocateErrorLogEntry(DriverObject,
|
|
(UCHAR)errorLogEntrySize);
|
|
|
|
if (errorLogEntry != NULL) {
|
|
|
|
RtlZeroMemory(errorLogEntry, errorLogEntrySize);
|
|
|
|
errorLogEntry->ErrorCode = ErrorCode;
|
|
errorLogEntry->FinalStatus = ErrorCode;
|
|
errorLogEntry->NumberOfStrings = (ErrorInsertionString) ? 1 : 0;
|
|
|
|
if (ErrorInsertionString) {
|
|
RtlCopyMemory(errorLogEntry->DumpData,
|
|
ErrorInsertionString,
|
|
errorInsertionStringSize);
|
|
}
|
|
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|