mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6561 lines
194 KiB
6561 lines
194 KiB
/*++
|
|
|
|
Copyright (c) 1990, 1991, 1992, 1993, 1994 - 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
kbdclass.c
|
|
|
|
Abstract:
|
|
|
|
Keyboard class driver.
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Notes:
|
|
|
|
NOTES: (Future/outstanding issues)
|
|
|
|
- Consolidate common code into a function, where appropriate.
|
|
|
|
@@BEGIN_DDKSPLIT
|
|
|
|
Revision History:
|
|
|
|
May 1997 Kenneth D. Ray: liberal additions of plug and play
|
|
|
|
@@END_DDKSPLIT
|
|
--*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <ntddk.h>
|
|
//@@BEGIN_DDKSPLIT
|
|
#include <ntpoapi.h>
|
|
//@@END_DDKSPLIT
|
|
#include <hidclass.h>
|
|
|
|
#include <initguid.h>
|
|
#include <kbdmou.h>
|
|
#include <kbdlog.h>
|
|
#include "kbdclass.h"
|
|
#include <poclass.h>
|
|
#include <wmistr.h>
|
|
|
|
#define INITGUID
|
|
#include "wdmguid.h"
|
|
|
|
GLOBALS Globals;
|
|
|
|
//@@BEGIN_DDKSPLIT
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
ZwPowerInformation(
|
|
IN POWER_INFORMATION_LEVEL InformationLevel,
|
|
IN PVOID InputBuffer OPTIONAL,
|
|
IN ULONG InputBufferLength,
|
|
OUT PVOID OutputBuffer OPTIONAL,
|
|
IN ULONG OutputBufferLength
|
|
);
|
|
//@@END_DDKSPLIT
|
|
|
|
//
|
|
// Use the alloc_text pragma to specify the driver initialization routines
|
|
// (they can be paged out).
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,DriverEntry)
|
|
#pragma alloc_text(INIT,KbdConfiguration)
|
|
#pragma alloc_text(PAGE,KeyboardClassPassThrough)
|
|
#pragma alloc_text(PAGE,KeyboardQueryDeviceKey)
|
|
#pragma alloc_text(PAGE,KbdDeterminePortsServiced)
|
|
#pragma alloc_text(PAGE,KbdDeviceMapQueryCallback)
|
|
#pragma alloc_text(PAGE,KbdSendConnectRequest)
|
|
#pragma alloc_text(PAGE,KeyboardAddDevice)
|
|
#pragma alloc_text(PAGE,KeyboardAddDeviceEx)
|
|
#pragma alloc_text(PAGE,KeyboardClassDeviceControl)
|
|
#pragma alloc_text(PAGE,KeyboardSendIrpSynchronously)
|
|
#pragma alloc_text(PAGE,KbdCreateClassObject)
|
|
#pragma alloc_text(PAGE,KeyboardClassFindMorePorts)
|
|
#pragma alloc_text(PAGE,KeyboardClassGetWaitWakeEnableState)
|
|
#pragma alloc_text(PAGE,KeyboardClassEnableGlobalPort)
|
|
#pragma alloc_text(PAGE,KeyboardClassPlugPlayNotification)
|
|
#pragma alloc_text(PAGE,KeyboardClassSystemControl)
|
|
#pragma alloc_text(PAGE,KeyboardClassSetWmiDataItem)
|
|
#pragma alloc_text(PAGE,KeyboardClassSetWmiDataBlock)
|
|
#pragma alloc_text(PAGE,KeyboardClassQueryWmiDataBlock)
|
|
#pragma alloc_text(PAGE,KeyboardClassQueryWmiRegInfo)
|
|
|
|
#pragma alloc_text(PAGE,KeyboardClassPower)
|
|
#pragma alloc_text(PAGE,KeyboardClassCreateWaitWakeIrpWorker)
|
|
#pragma alloc_text(PAGE,KeyboardClassCreateWaitWakeIrp)
|
|
// #pragma alloc_text(PAGE,KeyboardToggleWaitWakeWorker)
|
|
#pragma alloc_text(PAGE,KeyboardClassUnload)
|
|
#endif
|
|
|
|
#define WMI_CLASS_DRIVER_INFORMATION 0
|
|
#define WMI_WAIT_WAKE 1
|
|
|
|
GUID KeyboardClassGuid = MSKeyboard_ClassInformationGuid;
|
|
|
|
WMIGUIDREGINFO KeyboardClassWmiGuidList[] =
|
|
{
|
|
{
|
|
&KeyboardClassGuid,
|
|
1,
|
|
0 // Keyboard class driver information
|
|
},
|
|
{
|
|
&GUID_POWER_DEVICE_WAKE_ENABLE,
|
|
1,
|
|
0 // wait wake
|
|
}
|
|
};
|
|
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the keyboard class driver.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by system.
|
|
|
|
RegistryPath - Pointer to the Unicode name of the registry path
|
|
for this driver.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status from the initialization operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = NULL;
|
|
PDEVICE_OBJECT classDeviceObject = NULL;
|
|
ULONG dumpCount = 0;
|
|
ULONG dumpData[DUMP_COUNT];
|
|
ULONG i;
|
|
ULONG numPorts;
|
|
ULONG uniqueErrorValue;
|
|
UNICODE_STRING basePortName;
|
|
UNICODE_STRING fullPortName;
|
|
WCHAR basePortBuffer[NAME_MAX];
|
|
PWCHAR fullClassName = NULL;
|
|
PFILE_OBJECT file;
|
|
PLIST_ENTRY entry;
|
|
|
|
KbdPrint((1,"\n\nKBDCLASS-KeyboardClassInitialize: enter\n"));
|
|
|
|
//
|
|
// Zero-initialize various structures.
|
|
//
|
|
RtlZeroMemory(&Globals, sizeof(GLOBALS));
|
|
|
|
Globals.Debug = DEFAULT_DEBUG_LEVEL;
|
|
|
|
InitializeListHead (&Globals.LegacyDeviceList);
|
|
|
|
fullPortName.MaximumLength = 0;
|
|
|
|
ExInitializeFastMutex (&Globals.Mutex);
|
|
Globals.BaseClassName.Buffer = Globals.BaseClassBuffer;
|
|
Globals.BaseClassName.Length = 0;
|
|
Globals.BaseClassName.MaximumLength = NAME_MAX * sizeof(WCHAR);
|
|
|
|
RtlZeroMemory(basePortBuffer, NAME_MAX * sizeof(WCHAR));
|
|
basePortName.Buffer = basePortBuffer;
|
|
basePortName.Length = 0;
|
|
basePortName.MaximumLength = NAME_MAX * sizeof(WCHAR);
|
|
|
|
//
|
|
// Need to ensure that the registry path is null-terminated.
|
|
// Allocate pool to hold a null-terminated copy of the path.
|
|
//
|
|
|
|
Globals.RegistryPath.Length = RegistryPath->Length;
|
|
Globals.RegistryPath.MaximumLength = RegistryPath->Length
|
|
+ sizeof (UNICODE_NULL);
|
|
|
|
Globals.RegistryPath.Buffer = ExAllocatePool(
|
|
NonPagedPool,
|
|
Globals.RegistryPath.MaximumLength);
|
|
|
|
if (!Globals.RegistryPath.Buffer) {
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KeyboardClassInitialize: Couldn't allocate pool for registry path\n"
|
|
));
|
|
|
|
dumpData[0] = (ULONG) RegistryPath->Length + sizeof(UNICODE_NULL);
|
|
|
|
KeyboardClassLogError (DriverObject,
|
|
KBDCLASS_INSUFFICIENT_RESOURCES,
|
|
KEYBOARD_ERROR_VALUE_BASE + 2,
|
|
STATUS_UNSUCCESSFUL,
|
|
1,
|
|
dumpData,
|
|
0);
|
|
|
|
goto KeyboardClassInitializeExit;
|
|
}
|
|
|
|
RtlMoveMemory(Globals.RegistryPath.Buffer,
|
|
RegistryPath->Buffer,
|
|
RegistryPath->Length);
|
|
Globals.RegistryPath.Buffer [RegistryPath->Length / sizeof (WCHAR)] = L'\0';
|
|
|
|
//
|
|
// Get the configuration information for this driver.
|
|
//
|
|
|
|
KbdConfiguration();
|
|
|
|
//
|
|
// If there is only one class device object then create it as the grand
|
|
// master device object. Otherwise let all the FDOs also double as the
|
|
// class DO.
|
|
//
|
|
if (!Globals.ConnectOneClassToOnePort) {
|
|
status = KbdCreateClassObject (DriverObject,
|
|
&Globals.InitExtension,
|
|
&classDeviceObject,
|
|
&fullClassName,
|
|
TRUE);
|
|
if (!NT_SUCCESS (status)) {
|
|
// ISSUE: should log an error that we could not create a GM
|
|
goto KeyboardClassInitializeExit;
|
|
}
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION)classDeviceObject->DeviceExtension;
|
|
Globals.GrandMaster = deviceExtension;
|
|
deviceExtension->PnP = FALSE;
|
|
KeyboardAddDeviceEx (deviceExtension, fullClassName, NULL);
|
|
|
|
ASSERT (NULL != fullClassName);
|
|
ExFreePool (fullClassName);
|
|
fullClassName = NULL;
|
|
|
|
classDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
}
|
|
|
|
//
|
|
// Set up the base device name for the associated port device.
|
|
// It is the same as the base class name, with "Class" replaced
|
|
// by "Port".
|
|
//
|
|
RtlCopyUnicodeString(&basePortName, &Globals.BaseClassName);
|
|
basePortName.Length -= (sizeof(L"Class") - sizeof(UNICODE_NULL));
|
|
RtlAppendUnicodeToString(&basePortName, L"Port");
|
|
|
|
//
|
|
// Determine how many (static) ports this class driver is to service.
|
|
//
|
|
//
|
|
// If this returns zero, then all ports will be dynamically PnP added later
|
|
//
|
|
KbdDeterminePortsServiced(&basePortName, &numPorts);
|
|
|
|
ASSERT (numPorts <= MAXIMUM_PORTS_SERVICED);
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KeyboardClassInitialize: Will service %d port devices\n",
|
|
numPorts
|
|
));
|
|
|
|
//
|
|
// Set up space for the class's full device object name.
|
|
//
|
|
RtlInitUnicodeString(&fullPortName, NULL);
|
|
|
|
fullPortName.MaximumLength = sizeof(L"\\Device\\")
|
|
+ basePortName.Length
|
|
+ sizeof (UNICODE_NULL);
|
|
|
|
fullPortName.Buffer = ExAllocatePool(PagedPool,
|
|
fullPortName.MaximumLength);
|
|
|
|
if (!fullPortName.Buffer) {
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KeyboardClassInitialize: Couldn't allocate string for device object name\n"
|
|
));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
dumpData[0] = (ULONG) fullPortName.MaximumLength;
|
|
|
|
KeyboardClassLogError (DriverObject,
|
|
KBDCLASS_INSUFFICIENT_RESOURCES,
|
|
KEYBOARD_ERROR_VALUE_BASE + 6,
|
|
status,
|
|
1,
|
|
dumpData,
|
|
0);
|
|
|
|
goto KeyboardClassInitializeExit;
|
|
|
|
}
|
|
|
|
RtlZeroMemory(fullPortName.Buffer, fullPortName.MaximumLength);
|
|
RtlAppendUnicodeToString(&fullPortName, L"\\Device\\");
|
|
RtlAppendUnicodeToString(&fullPortName, basePortName.Buffer);
|
|
RtlAppendUnicodeToString(&fullPortName, L"0");
|
|
|
|
//
|
|
// Set up the class device object(s) to handle the associated
|
|
// port devices.
|
|
//
|
|
for (i = 0; (i < Globals.PortsServiced) && (i < numPorts); i++) {
|
|
|
|
//
|
|
// Append the suffix to the device object name string. E.g., turn
|
|
// \Device\KeyboardClass into \Device\KeyboardClass0. Then attempt
|
|
// to create the device object. If the device object already
|
|
// exists increment the suffix and try again.
|
|
//
|
|
|
|
fullPortName.Buffer[(fullPortName.Length / sizeof(WCHAR)) - 1]
|
|
= L'0' + (WCHAR) i;
|
|
|
|
//
|
|
// Create the class device object.
|
|
//
|
|
status = KbdCreateClassObject (DriverObject,
|
|
&Globals.InitExtension,
|
|
&classDeviceObject,
|
|
&fullClassName,
|
|
TRUE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
KeyboardClassLogError (DriverEntry,
|
|
KBDCLASS_INSUFFICIENT_RESOURCES,
|
|
KEYBOARD_ERROR_VALUE_BASE + 8,
|
|
status,
|
|
0,
|
|
NULL,
|
|
0);
|
|
continue;
|
|
}
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION)classDeviceObject->DeviceExtension;
|
|
deviceExtension->PnP = FALSE;
|
|
|
|
classDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
//
|
|
// Connect to the port device.
|
|
//
|
|
status = IoGetDeviceObjectPointer (&fullPortName,
|
|
FILE_READ_ATTRIBUTES,
|
|
&file,
|
|
&deviceExtension->TopPort);
|
|
|
|
//
|
|
// In case of failure, just delete the device and continue
|
|
//
|
|
if (!NT_SUCCESS(status)) {
|
|
// ISSUE: log error
|
|
KeyboardClassDeleteLegacyDevice (deviceExtension);
|
|
continue;
|
|
}
|
|
|
|
classDeviceObject->StackSize = 1 + deviceExtension->TopPort->StackSize;
|
|
status = KeyboardAddDeviceEx (deviceExtension, fullClassName, file);
|
|
|
|
if (fullClassName) {
|
|
ExFreePool (fullClassName);
|
|
fullClassName = NULL;
|
|
}
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
if (Globals.GrandMaster == NULL) {
|
|
if (deviceExtension->File) {
|
|
file = deviceExtension->File;
|
|
deviceExtension->File = NULL;
|
|
}
|
|
}
|
|
else {
|
|
PPORT port;
|
|
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
|
|
file = Globals.AssocClassList[deviceExtension->UnitId].File;
|
|
Globals.AssocClassList[deviceExtension->UnitId].File = NULL;
|
|
Globals.AssocClassList[deviceExtension->UnitId].Free = TRUE;
|
|
Globals.AssocClassList[deviceExtension->UnitId].Port = NULL;
|
|
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
}
|
|
|
|
if (file) {
|
|
ObDereferenceObject (file);
|
|
}
|
|
|
|
KeyboardClassDeleteLegacyDevice (deviceExtension);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Store this device object in a linked list regardless if we are in
|
|
// grand master mode or not
|
|
//
|
|
InsertTailList (&Globals.LegacyDeviceList, &deviceExtension->Link);
|
|
} // for
|
|
|
|
//
|
|
// If we had any failures creating legacy device objects, we must still
|
|
// succeed b/c we need to service pnp ports later on
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Count the number of legacy device ports we created
|
|
//
|
|
for (entry = Globals.LegacyDeviceList.Flink;
|
|
entry != &Globals.LegacyDeviceList;
|
|
entry = entry->Flink) {
|
|
Globals.NumberLegacyPorts++;
|
|
}
|
|
|
|
KeyboardClassInitializeExit:
|
|
|
|
//
|
|
// Free the unicode strings.
|
|
//
|
|
if (fullPortName.MaximumLength != 0){
|
|
ExFreePool (fullPortName.Buffer);
|
|
}
|
|
|
|
if (fullClassName) {
|
|
ExFreePool (fullClassName);
|
|
}
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
|
|
IoRegisterDriverReinitialization(DriverObject,
|
|
KeyboardClassFindMorePorts,
|
|
NULL);
|
|
|
|
//
|
|
// Set up the device driver entry points.
|
|
//
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = KeyboardClassCreate;
|
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = KeyboardClassClose;
|
|
DriverObject->MajorFunction[IRP_MJ_READ] = KeyboardClassRead;
|
|
DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = KeyboardClassFlush;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KeyboardClassDeviceControl;
|
|
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
|
|
KeyboardClassPassThrough;
|
|
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = KeyboardClassCleanup;
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = KeyboardPnP;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = KeyboardClassPower;
|
|
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = KeyboardClassSystemControl;
|
|
DriverObject->DriverExtension->AddDevice = KeyboardAddDevice;
|
|
|
|
// DriverObject->DriverUnload = KeyboardClassUnload;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
//
|
|
// Clean up all the pool we created and delete the GM if it exists
|
|
//
|
|
if (Globals.RegistryPath.Buffer != NULL) {
|
|
ExFreePool (Globals.RegistryPath.Buffer);
|
|
Globals.RegistryPath.Buffer = NULL;
|
|
}
|
|
|
|
if (Globals.AssocClassList) {
|
|
ExFreePool (Globals.AssocClassList);
|
|
Globals.AssocClassList = NULL;
|
|
}
|
|
|
|
if (Globals.GrandMaster) {
|
|
KeyboardClassDeleteLegacyDevice(Globals.GrandMaster);
|
|
Globals.GrandMaster = NULL;
|
|
}
|
|
}
|
|
|
|
KbdPrint((1,"KBDCLASS-KeyboardClassInitialize: exit\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassPassThrough(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Passes a request on to the lower driver.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Pass the IRP to the target
|
|
//
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
return IoCallDriver (
|
|
((PDEVICE_EXTENSION) DeviceObject->DeviceExtension)->TopPort,
|
|
Irp);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
KeyboardQueryDeviceKey (
|
|
IN HANDLE Handle,
|
|
IN PWCHAR ValueNameString,
|
|
OUT PVOID Data,
|
|
IN ULONG DataLength
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING valueName;
|
|
ULONG length;
|
|
PKEY_VALUE_FULL_INFORMATION fullInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
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
|
|
KeyboardAddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
)
|
|
/*++
|
|
Description:
|
|
The plug play entry point "AddDevice"
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT fdo;
|
|
PDEVICE_EXTENSION port;
|
|
PWCHAR fullClassName = NULL;
|
|
POWER_STATE state;
|
|
HANDLE hService, hParameters;
|
|
ULONG tmp;
|
|
OBJECT_ATTRIBUTES oa;
|
|
|
|
PAGED_CODE ();
|
|
|
|
status = KbdCreateClassObject (DriverObject,
|
|
&Globals.InitExtension,
|
|
&fdo,
|
|
&fullClassName,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
return status;
|
|
}
|
|
|
|
port = (PDEVICE_EXTENSION) fdo->DeviceExtension;
|
|
port->TopPort = IoAttachDeviceToDeviceStack (fdo, PhysicalDeviceObject);
|
|
|
|
if (port->TopPort == NULL) {
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
|
|
//
|
|
// Not good; in only extreme cases will this fail
|
|
//
|
|
errorLogEntry = (PIO_ERROR_LOG_PACKET)
|
|
IoAllocateErrorLogEntry (DriverObject,
|
|
(UCHAR) sizeof(IO_ERROR_LOG_PACKET));
|
|
|
|
if (errorLogEntry) {
|
|
errorLogEntry->ErrorCode = KBDCLASS_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 (fdo);
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
port->PDO = PhysicalDeviceObject;
|
|
port->PnP = TRUE;
|
|
port->Started = FALSE;
|
|
port->DeviceState = PowerDeviceD0;
|
|
port->SystemState = PowerSystemWorking;
|
|
|
|
state.DeviceState = PowerDeviceD0;
|
|
PoSetPowerState (fdo, DevicePowerState, state);
|
|
|
|
port->MinDeviceWakeState = PowerDeviceUnspecified;
|
|
port->MinSystemWakeState = PowerSystemUnspecified;
|
|
port->WaitWakeEnabled = FALSE;
|
|
port->AllowDisable = FALSE;
|
|
|
|
InitializeObjectAttributes (&oa,
|
|
&Globals.RegistryPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
(PSECURITY_DESCRIPTOR) NULL);
|
|
|
|
status = ZwOpenKey (&hService, KEY_ALL_ACCESS, &oa);
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
UNICODE_STRING parameters;
|
|
|
|
RtlInitUnicodeString(¶meters, L"Parameters");
|
|
InitializeObjectAttributes (&oa,
|
|
¶meters,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hService,
|
|
(PSECURITY_DESCRIPTOR) NULL);
|
|
|
|
status = ZwOpenKey (&hParameters, KEY_ALL_ACCESS, &oa);
|
|
if (NT_SUCCESS (status)) {
|
|
status = KeyboardQueryDeviceKey (hParameters,
|
|
KEYBOARD_ALLOW_DISABLE,
|
|
&tmp,
|
|
sizeof (tmp));
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
port->AllowDisable = (tmp ? TRUE : FALSE);
|
|
}
|
|
|
|
ZwClose (hParameters);
|
|
}
|
|
|
|
ZwClose (hService);
|
|
}
|
|
|
|
fdo->Flags |= DO_POWER_PAGABLE;
|
|
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
status = IoRegisterDeviceInterface (PhysicalDeviceObject,
|
|
(LPGUID)&GUID_CLASS_KEYBOARD,
|
|
NULL,
|
|
&port->SymbolicLinkName );
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
IoDetachDevice (port->TopPort);
|
|
port->TopPort = NULL;
|
|
IoDeleteDevice (fdo);
|
|
} else {
|
|
status = KeyboardAddDeviceEx (port, fullClassName, NULL);
|
|
}
|
|
|
|
if (fullClassName) {
|
|
ExFreePool(fullClassName);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
KeyboardClassGetWaitWakeEnableState(
|
|
IN PDEVICE_EXTENSION Data
|
|
)
|
|
{
|
|
HANDLE hKey;
|
|
NTSTATUS status;
|
|
ULONG tmp;
|
|
BOOLEAN wwEnableFound;
|
|
|
|
hKey = NULL;
|
|
wwEnableFound = FALSE;
|
|
|
|
status = IoOpenDeviceRegistryKey (Data->PDO,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_ALL,
|
|
&hKey);
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
status = KeyboardQueryDeviceKey (hKey,
|
|
KEYBOARD_WAIT_WAKE_ENABLE,
|
|
&tmp,
|
|
sizeof (tmp));
|
|
if (NT_SUCCESS (status)) {
|
|
wwEnableFound = TRUE;
|
|
Data->WaitWakeEnabled = (tmp ? TRUE : FALSE);
|
|
}
|
|
ZwClose (hKey);
|
|
hKey = NULL;
|
|
}
|
|
|
|
//@@BEGIN_DDKSPLIT
|
|
if (wwEnableFound == FALSE && Data->WaitWakeEnabled == FALSE) {
|
|
RTL_OSVERSIONINFOEXW osVerInfo;
|
|
ULONGLONG mask = 0;
|
|
|
|
//
|
|
// Only auto enable wait wake on workstation installs (pro and personal)
|
|
//
|
|
RtlZeroMemory(&osVerInfo, sizeof(osVerInfo));
|
|
osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);
|
|
osVerInfo.wProductType = VER_NT_WORKSTATION;
|
|
|
|
VER_SET_CONDITION(mask, VER_PRODUCT_TYPE, VER_EQUAL);
|
|
|
|
if (NT_SUCCESS(RtlVerifyVersionInfo(&osVerInfo,
|
|
VER_PRODUCT_TYPE,
|
|
mask))) {
|
|
SYSTEM_POWER_CAPABILITIES sysPowerCaps;
|
|
|
|
RtlZeroMemory(&sysPowerCaps, sizeof(sysPowerCaps));
|
|
|
|
status = ZwPowerInformation (SystemPowerCapabilities,
|
|
NULL,
|
|
0,
|
|
&sysPowerCaps,
|
|
sizeof(SYSTEM_POWER_CAPABILITIES));
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
SYSTEM_POWER_STATE maxSysWake;
|
|
|
|
//
|
|
// Get the deepest sleep state the machine is capable of
|
|
//
|
|
if (sysPowerCaps.SystemS3) {
|
|
maxSysWake = PowerSystemSleeping3;
|
|
}
|
|
else if (sysPowerCaps.SystemS2) {
|
|
maxSysWake = PowerSystemSleeping2;
|
|
}
|
|
else if (sysPowerCaps.SystemS1) {
|
|
maxSysWake = PowerSystemSleeping1;
|
|
}
|
|
else {
|
|
maxSysWake = PowerSystemUnspecified;
|
|
}
|
|
|
|
//
|
|
// See if the system wake state for the device is as deep (or
|
|
// deeper) than the deepest system sleep state. This will
|
|
// prevent us from auto enabling wake and then only allowing the
|
|
// machine to go into S1 instead of S3 (which is the case on a
|
|
// lot of laptops).
|
|
//
|
|
if (Data->MinSystemWakeState >= maxSysWake) {
|
|
//
|
|
// Success!
|
|
//
|
|
Data->WaitWakeEnabled = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//@@END_DDKSPLIT
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardAddDeviceEx(
|
|
IN PDEVICE_EXTENSION ClassData,
|
|
IN PWCHAR FullClassName,
|
|
IN PFILE_OBJECT File
|
|
)
|
|
/*++ Description:
|
|
*
|
|
* Called whenever the Keyboard Class driver is loaded to control a device.
|
|
*
|
|
* Two possible reasons.
|
|
* 1) Plug and Play found a PNP enumerated keyboard port.
|
|
* 2) Driver Entry found this device via old crusty legacy reasons.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return:
|
|
*
|
|
* STATUS_SUCCESS - if successful STATUS_UNSUCCESSFUL - otherwise
|
|
*
|
|
* --*/
|
|
{
|
|
NTSTATUS errorCode = STATUS_SUCCESS;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION trueClassData;
|
|
PPORT classDataList;
|
|
ULONG uniqueErrorValue = 0;
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
ULONG dumpCount = 0;
|
|
ULONG dumpData[DUMP_COUNT];
|
|
ULONG i;
|
|
|
|
PAGED_CODE ();
|
|
|
|
KeInitializeSpinLock (&ClassData->WaitWakeSpinLock);
|
|
|
|
if (Globals.ConnectOneClassToOnePort) {
|
|
|
|
ASSERT (NULL == Globals.GrandMaster);
|
|
trueClassData = ClassData;
|
|
|
|
} else {
|
|
trueClassData = Globals.GrandMaster;
|
|
}
|
|
ClassData->TrueClassDevice = trueClassData->Self;
|
|
|
|
if ((Globals.GrandMaster != ClassData) &&
|
|
(Globals.GrandMaster == trueClassData)) {
|
|
//
|
|
// We have a grand master, and are adding a port device object.
|
|
//
|
|
|
|
//
|
|
// Connect to port device.
|
|
//
|
|
status = KbdSendConnectRequest(ClassData, KeyboardClassServiceCallback);
|
|
|
|
//
|
|
// Link this class device object in the list of class devices object
|
|
// associated with the true class device object
|
|
//
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
|
|
for (i=0; i < Globals.NumAssocClass; i++) {
|
|
if (Globals.AssocClassList[i].Free) {
|
|
Globals.AssocClassList[i].Free = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == Globals.NumAssocClass) {
|
|
classDataList = ExAllocatePool (
|
|
NonPagedPool,
|
|
(Globals.NumAssocClass + 1) * sizeof (PORT));
|
|
|
|
if (NULL == classDataList) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
// ISSUE: log error
|
|
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
goto KeyboardAddDeviceExReject;
|
|
}
|
|
|
|
RtlZeroMemory (classDataList,
|
|
(Globals.NumAssocClass + 1) * sizeof (PORT));
|
|
|
|
if (0 != Globals.NumAssocClass) {
|
|
RtlCopyMemory (classDataList,
|
|
Globals.AssocClassList,
|
|
Globals.NumAssocClass * sizeof (PORT));
|
|
|
|
ExFreePool (Globals.AssocClassList);
|
|
}
|
|
Globals.AssocClassList = classDataList;
|
|
Globals.NumAssocClass++;
|
|
}
|
|
|
|
ClassData->UnitId = i;
|
|
Globals.AssocClassList [i].Port = ClassData;
|
|
Globals.AssocClassList [i].File = File;
|
|
|
|
trueClassData->Self->StackSize =
|
|
MAX (trueClassData->Self->StackSize, ClassData->Self->StackSize);
|
|
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
} else if ((Globals.GrandMaster != ClassData) &&
|
|
(ClassData == trueClassData)) {
|
|
|
|
//
|
|
// Connect to port device.
|
|
//
|
|
status = KbdSendConnectRequest(ClassData, KeyboardClassServiceCallback);
|
|
ASSERT (STATUS_SUCCESS == status);
|
|
}
|
|
|
|
if (ClassData == trueClassData) {
|
|
|
|
ASSERT (NULL != FullClassName);
|
|
|
|
//
|
|
// Load the device map information into the registry so
|
|
// that setup can determine which keyboard class driver is active.
|
|
//
|
|
|
|
status = RtlWriteRegistryValue(
|
|
RTL_REGISTRY_DEVICEMAP,
|
|
Globals.BaseClassName.Buffer, // key name
|
|
FullClassName, // value name
|
|
REG_SZ,
|
|
Globals.RegistryPath.Buffer, // The value
|
|
Globals.RegistryPath.Length + sizeof(UNICODE_NULL));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KeyboardClassInitialize: Could not store %ws in DeviceMap\n",
|
|
FullClassName));
|
|
|
|
KeyboardClassLogError (ClassData,
|
|
KBDCLASS_NO_DEVICEMAP_CREATED,
|
|
KEYBOARD_ERROR_VALUE_BASE + 14,
|
|
status,
|
|
0,
|
|
NULL,
|
|
0);
|
|
} else {
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KeyboardClassInitialize: Stored %ws in DeviceMap\n",
|
|
FullClassName));
|
|
|
|
}
|
|
}
|
|
|
|
return status;
|
|
|
|
KeyboardAddDeviceExReject:
|
|
|
|
//
|
|
// Some part of the initialization failed. Log an error, and
|
|
// clean up the resources for the failed part of the initialization.
|
|
//
|
|
if (errorCode != STATUS_SUCCESS) {
|
|
|
|
errorLogEntry = (PIO_ERROR_LOG_PACKET)
|
|
IoAllocateErrorLogEntry(
|
|
trueClassData->Self,
|
|
(UCHAR) (sizeof(IO_ERROR_LOG_PACKET)
|
|
+ (dumpCount * sizeof(ULONG)))
|
|
);
|
|
|
|
if (errorLogEntry != NULL) {
|
|
|
|
errorLogEntry->ErrorCode = errorCode;
|
|
errorLogEntry->DumpDataSize = (USHORT) (dumpCount * sizeof (ULONG));
|
|
errorLogEntry->SequenceNumber = 0;
|
|
errorLogEntry->MajorFunctionCode = 0;
|
|
errorLogEntry->IoControlCode = 0;
|
|
errorLogEntry->RetryCount = 0;
|
|
errorLogEntry->UniqueErrorValue = uniqueErrorValue;
|
|
errorLogEntry->FinalStatus = status;
|
|
for (i = 0; i < dumpCount; i++)
|
|
errorLogEntry->DumpData[i] = dumpData[i];
|
|
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
}
|
|
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
KeyboardClassCancel(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the class cancellation routine. It is
|
|
called from the I/O system when a request is cancelled. Read requests
|
|
are currently the only cancellable requests.
|
|
|
|
N.B. The cancel spinlock is already held upon entry to this routine.
|
|
Also, there is no ISR to synchronize with.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
Irp - Pointer to the request packet to be cancelled.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
KIRQL irql;
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Release the global cancel spinlock.
|
|
// Do this while not holding any other spinlocks so that we exit at the
|
|
// right IRQL.
|
|
//
|
|
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
|
|
|
//
|
|
// Dequeue and complete the IRP. The enqueue and dequeue functions
|
|
// synchronize properly so that if this cancel routine is called,
|
|
// the dequeue is safe and only the cancel routine will complete the IRP.
|
|
//
|
|
KeAcquireSpinLock(&deviceExtension->SpinLock, &irql);
|
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, irql);
|
|
|
|
//
|
|
// Complete the IRP. This is a call outside the driver, so all spinlocks
|
|
// must be released by this point.
|
|
//
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
//
|
|
// Remove the lock we took in the read handler
|
|
//
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
}
|
|
|
|
VOID
|
|
KeyboardClassCleanupQueue (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PFILE_OBJECT FileObject
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This does the work of MouseClassCleanup so that we can also do that work
|
|
during remove device for when the grand master isn't enabled.
|
|
|
|
|
|
--*/
|
|
{
|
|
PIRP irp;
|
|
LIST_ENTRY listHead, *entry;
|
|
KIRQL irql;
|
|
|
|
InitializeListHead(&listHead);
|
|
|
|
KeAcquireSpinLock(&DeviceExtension->SpinLock, &irql);
|
|
|
|
do {
|
|
irp = KeyboardClassDequeueReadByFileObject(DeviceExtension, FileObject);
|
|
if (irp) {
|
|
irp->IoStatus.Status = STATUS_CANCELLED;
|
|
irp->IoStatus.Information = 0;
|
|
|
|
InsertTailList (&listHead, &irp->Tail.Overlay.ListEntry);
|
|
}
|
|
} while (irp != NULL);
|
|
|
|
KeReleaseSpinLock(&DeviceExtension->SpinLock, irql);
|
|
|
|
//
|
|
// Complete these irps outside of the spin lock
|
|
//
|
|
while (! IsListEmpty (&listHead)) {
|
|
entry = RemoveHeadList (&listHead);
|
|
irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
IoCompleteRequest (irp, IO_NO_INCREMENT);
|
|
IoReleaseRemoveLock (&DeviceExtension->RemoveLock, irp);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
KeyboardClassCleanup(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch routine for cleanup requests.
|
|
All requests queued to the keyboard class device (on behalf of
|
|
the thread for whom the cleanup request was generated) are
|
|
completed with STATUS_CANCELLED.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassCleanup: enter\n"));
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Get a pointer to the current stack location for this request.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// If the file object is the FileTrustedForRead, then the cleanup
|
|
// request is being executed by the trusted subsystem. Since the
|
|
// trusted subsystem is the only one with sufficient privilege to make
|
|
// Read requests to the driver, and since only Read requests get queued
|
|
// to the device queue, a cleanup request from the trusted subsystem is
|
|
// handled by cancelling all queued requests.
|
|
//
|
|
// If not, there is no cleanup work to perform
|
|
// (only read requests can be cancelled).
|
|
//
|
|
|
|
if (IS_TRUSTED_FILE_FOR_READ (irpSp->FileObject)) {
|
|
KeyboardClassCleanupQueue (DeviceObject, deviceExtension, irpSp->FileObject);
|
|
}
|
|
|
|
//
|
|
// Complete the cleanup request with STATUS_SUCCESS.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassCleanup: exit\n"));
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
KeyboardClassDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch routine for device control requests.
|
|
All device control subfunctions are passed, asynchronously, to the
|
|
connected port driver for processing and completion.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION stack;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PDEVICE_EXTENSION port;
|
|
BOOLEAN loopit = FALSE;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PKEYBOARD_INDICATOR_PARAMETERS param;
|
|
ULONG unitId;
|
|
ULONG ioctl;
|
|
ULONG i;
|
|
PKBD_CALL_ALL_PORTS callAll;
|
|
|
|
PAGED_CODE ();
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassDeviceControl: enter\n"));
|
|
|
|
//
|
|
// Get a pointer to the device extension.
|
|
//
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Get a pointer to the current parameters for this request. The
|
|
// information is contained in the current stack location.
|
|
//
|
|
|
|
stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp);
|
|
if (!NT_SUCCESS (status)) {
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Check for adequate input buffer length. The input buffer
|
|
// should, at a minimum, contain the unit ID specifying one of
|
|
// the connected port devices. If there is no input buffer (i.e.,
|
|
// the input buffer length is zero), then we assume the unit ID
|
|
// is zero (for backwards compatibility).
|
|
//
|
|
|
|
unitId = 0;
|
|
switch (ioctl = stack->Parameters.DeviceIoControl.IoControlCode) {
|
|
case IOCTL_KEYBOARD_SET_INDICATORS:
|
|
if (stack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof (KEYBOARD_INDICATOR_PARAMETERS)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
goto KeyboardClassDeviceControlReject;
|
|
}
|
|
|
|
deviceExtension->IndicatorParameters
|
|
= *(PKEYBOARD_INDICATOR_PARAMETERS)Irp->AssociatedIrp.SystemBuffer;
|
|
// Fall through
|
|
case IOCTL_KEYBOARD_SET_TYPEMATIC:
|
|
if (Globals.SendOutputToAllPorts) {
|
|
loopit = TRUE;
|
|
}
|
|
// Fall through
|
|
case IOCTL_KEYBOARD_QUERY_ATTRIBUTES:
|
|
case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION:
|
|
case IOCTL_KEYBOARD_QUERY_INDICATORS:
|
|
case IOCTL_KEYBOARD_QUERY_TYPEMATIC:
|
|
case IOCTL_KEYBOARD_QUERY_IME_STATUS:
|
|
case IOCTL_KEYBOARD_SET_IME_STATUS:
|
|
|
|
if (stack->Parameters.DeviceIoControl.InputBufferLength == 0) {
|
|
unitId = 0;
|
|
} else if (stack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(KEYBOARD_UNIT_ID_PARAMETER)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
goto KeyboardClassDeviceControlReject;
|
|
|
|
} else {
|
|
unitId = ((PKEYBOARD_UNIT_ID_PARAMETER)
|
|
Irp->AssociatedIrp.SystemBuffer)->UnitId;
|
|
}
|
|
|
|
if (deviceExtension->Self != deviceExtension->TrueClassDevice) {
|
|
status = STATUS_NOT_SUPPORTED;
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
goto KeyboardClassDeviceControlReject;
|
|
|
|
} else if (deviceExtension == Globals.GrandMaster) {
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
if (Globals.NumAssocClass <= unitId) {
|
|
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
goto KeyboardClassDeviceControlReject;
|
|
}
|
|
if (0 < Globals.NumAssocClass) {
|
|
if (!PORT_WORKING (&Globals.AssocClassList [unitId])) {
|
|
unitId = 0;
|
|
}
|
|
while (Globals.NumAssocClass > unitId &&
|
|
!PORT_WORKING (&Globals.AssocClassList [unitId])) {
|
|
unitId++;
|
|
}
|
|
}
|
|
if (Globals.NumAssocClass <= unitId) {
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
goto KeyboardClassDeviceControlReject;
|
|
}
|
|
port = Globals.AssocClassList [unitId].Port;
|
|
stack->FileObject = Globals.AssocClassList[unitId].File;
|
|
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
} else {
|
|
loopit = FALSE;
|
|
port = deviceExtension;
|
|
}
|
|
|
|
//
|
|
// Pass the device control request on to the port driver,
|
|
// asynchronously. Get the next IRP stack location and copy the
|
|
// input parameters to the next stack location. Change the major
|
|
// function to internal device control.
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
|
(IoGetNextIrpStackLocation (Irp))->MajorFunction =
|
|
IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
|
|
if (loopit) {
|
|
//
|
|
// Inc the lock one more time until this looping is done.
|
|
// Since we are allready holding this semiphore, it should not
|
|
// have triggered on us.
|
|
//
|
|
status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp);
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
//
|
|
// Prepare to call multiple ports
|
|
// Make a copy of the port array.
|
|
//
|
|
// If someone yanks the keyboard, while the caps lock is
|
|
// going we could be in trouble.
|
|
//
|
|
// We should therefore take out remove locks on each and every
|
|
// port device object so that it won't.
|
|
//
|
|
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
callAll = ExAllocatePool (NonPagedPool,
|
|
sizeof (KBD_CALL_ALL_PORTS) +
|
|
(sizeof (PORT) * Globals.NumAssocClass));
|
|
|
|
if (callAll) {
|
|
callAll->Len = Globals.NumAssocClass;
|
|
callAll->Current = 0;
|
|
for (i = 0; i < Globals.NumAssocClass; i++) {
|
|
|
|
callAll->Port[i] = Globals.AssocClassList[i];
|
|
|
|
if (PORT_WORKING (&callAll->Port[i])) {
|
|
status = IoAcquireRemoveLock (
|
|
&(callAll->Port[i].Port)->RemoveLock,
|
|
Irp);
|
|
ASSERT (NT_SUCCESS (status));
|
|
}
|
|
}
|
|
status = KeyboardCallAllPorts (DeviceObject, Irp, callAll);
|
|
|
|
} else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
}
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
|
|
} else {
|
|
status = IoCallDriver(port->TopPort, Irp);
|
|
}
|
|
break;
|
|
|
|
case IOCTL_GET_SYS_BUTTON_CAPS:
|
|
case IOCTL_GET_SYS_BUTTON_EVENT:
|
|
case IOCTL_HID_GET_DRIVER_CONFIG:
|
|
case IOCTL_HID_SET_DRIVER_CONFIG:
|
|
case IOCTL_HID_GET_POLL_FREQUENCY_MSEC:
|
|
case IOCTL_HID_SET_POLL_FREQUENCY_MSEC:
|
|
case IOCTL_GET_NUM_DEVICE_INPUT_BUFFERS:
|
|
case IOCTL_SET_NUM_DEVICE_INPUT_BUFFERS:
|
|
case IOCTL_HID_GET_COLLECTION_INFORMATION:
|
|
case IOCTL_HID_GET_COLLECTION_DESCRIPTOR:
|
|
case IOCTL_HID_FLUSH_QUEUE:
|
|
case IOCTL_HID_SET_FEATURE:
|
|
case IOCTL_HID_GET_FEATURE:
|
|
case IOCTL_GET_PHYSICAL_DESCRIPTOR:
|
|
case IOCTL_HID_GET_HARDWARE_ID:
|
|
case IOCTL_HID_GET_MANUFACTURER_STRING:
|
|
case IOCTL_HID_GET_PRODUCT_STRING:
|
|
case IOCTL_HID_GET_SERIALNUMBER_STRING:
|
|
case IOCTL_HID_GET_INDEXED_STRING:
|
|
if (deviceExtension->PnP && (deviceExtension != Globals.GrandMaster)) {
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = IoCallDriver (deviceExtension->TopPort, Irp);
|
|
break;
|
|
}
|
|
default:
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
break;
|
|
}
|
|
|
|
KeyboardClassDeviceControlReject:
|
|
|
|
IoReleaseRemoveLock (&deviceExtension->RemoveLock, Irp);
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassDeviceControl: exit\n"));
|
|
|
|
return(status);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardCallAllPorts (
|
|
PDEVICE_OBJECT Device,
|
|
PIRP Irp,
|
|
PKBD_CALL_ALL_PORTS CallAll
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Bounce this Irp to all the ports associated with the given device extension.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION nextSp;
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION port;
|
|
BOOLEAN firstTime;
|
|
|
|
firstTime = CallAll->Current == 0;
|
|
|
|
ASSERT (Globals.GrandMaster->Self == Device);
|
|
|
|
nextSp = IoGetNextIrpStackLocation (Irp);
|
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
|
nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
|
|
while ((CallAll->Current < CallAll->Len) &&
|
|
(!PORT_WORKING (&CallAll->Port[CallAll->Current]))) {
|
|
CallAll->Current++;
|
|
}
|
|
if (CallAll->Current < CallAll->Len) {
|
|
|
|
port = CallAll->Port [CallAll->Current].Port;
|
|
nextSp->FileObject = CallAll->Port [CallAll->Current].File;
|
|
|
|
CallAll->Current++;
|
|
|
|
IoSetCompletionRoutine (Irp,
|
|
&KeyboardCallAllPorts,
|
|
CallAll,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
status = IoCallDriver (port->TopPort, Irp);
|
|
IoReleaseRemoveLock (&port->RemoveLock, Irp);
|
|
|
|
} else {
|
|
//
|
|
// We are done so let this Irp complete normally
|
|
//
|
|
ASSERT (Globals.GrandMaster == Device->DeviceExtension);
|
|
|
|
if (Irp->PendingReturned) {
|
|
IoMarkIrpPending (Irp);
|
|
}
|
|
|
|
IoReleaseRemoveLock (&Globals.GrandMaster->RemoveLock, Irp);
|
|
ExFreePool (CallAll);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (firstTime) {
|
|
//
|
|
// Here we are not completing an IRP but sending it down for the first
|
|
// time.
|
|
//
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Since we bounced the Irp another time we must stop the completion on
|
|
// this particular trip.
|
|
//
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
KeyboardClassFlush(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch routine for flush requests. The class
|
|
input data queue is reinitialized.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassFlush: enter\n"));
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
if (deviceExtension->Self != deviceExtension->TrueClassDevice) {
|
|
status = STATUS_NOT_SUPPORTED;
|
|
|
|
} else if (!IS_TRUSTED_FILE_FOR_READ (irpSp->FileObject)) {
|
|
status = STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
//
|
|
// Initialize keyboard class input data queue.
|
|
//
|
|
KbdInitializeDataQueue((PVOID)deviceExtension);
|
|
}
|
|
|
|
//
|
|
// Complete the request and return status.
|
|
//
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassFlush: exit\n"));
|
|
|
|
return(status);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
KbdSyncComplete (
|
|
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;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
stack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
//
|
|
// Since this completion routines sole purpose in life is to synchronize
|
|
// Irp, we know that unless something else happens that the IoCallDriver
|
|
// will unwind AFTER the we have complete this Irp. Therefore we should
|
|
// NOT buble up the pending bit.
|
|
//
|
|
// if (Irp->PendingReturned) {
|
|
// IoMarkIrpPending( Irp );
|
|
// }
|
|
//
|
|
|
|
KeSetEvent ((PKEVENT) Context, 0, FALSE);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardSendIrpSynchronously (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN BOOLEAN CopyToNext
|
|
)
|
|
{
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE ();
|
|
|
|
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
|
|
|
|
if (CopyToNext) {
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
}
|
|
|
|
IoSetCompletionRoutine(Irp,
|
|
KbdSyncComplete,
|
|
&event,
|
|
TRUE, // on success
|
|
TRUE, // on error
|
|
TRUE // on cancel
|
|
);
|
|
|
|
IoCallDriver(DeviceObject, Irp);
|
|
|
|
//
|
|
// Wait for lower drivers to be done with the Irp
|
|
//
|
|
KeWaitForSingleObject(&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
status = Irp->IoStatus.Status;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
KeyboardClassCreate (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch routine for create/open and close requests.
|
|
Open/close requests are completed here.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PPORT port;
|
|
KIRQL oldIrql;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG i;
|
|
LUID priv;
|
|
KEVENT event;
|
|
BOOLEAN someEnableDisableSucceeded = FALSE;
|
|
BOOLEAN enabled;
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassCreate: enter\n"));
|
|
|
|
//
|
|
// Get a pointer to the device extension.
|
|
//
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Get a pointer to the current parameters for this request. The
|
|
// information is contained in the current stack location.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
ASSERT (IRP_MJ_CREATE == irpSp->MajorFunction);
|
|
|
|
//
|
|
// We do not allow user mode opens for read. This includes services (who
|
|
// have the TCB privilege).
|
|
//
|
|
if (Irp->RequestorMode == UserMode &&
|
|
(irpSp->Parameters.Create.SecurityContext->DesiredAccess & FILE_READ_DATA)
|
|
//@@BEGIN_DDKSPLIT
|
|
&& ((irpSp->Parameters.Create.Options & FILE_DIRECTORY_FILE) == 0)
|
|
//@@END_DDKSPLIT
|
|
) {
|
|
status = STATUS_ACCESS_DENIED;
|
|
goto KeyboardClassCreateEnd;
|
|
}
|
|
|
|
status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp);
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
goto KeyboardClassCreateEnd;
|
|
}
|
|
|
|
if ((deviceExtension->PnP) && (!deviceExtension->Started)) {
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-Create: failed create because PnP and Not started\n"
|
|
));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
IoReleaseRemoveLock (&deviceExtension->RemoveLock, Irp);
|
|
goto KeyboardClassCreateEnd;
|
|
}
|
|
|
|
//@@BEGIN_DDKSPLIT
|
|
//
|
|
// Clear the FILE_DIRECTORY_FILE bit because the lower bus driver might
|
|
// fail the open if it is set
|
|
//
|
|
irpSp->Parameters.Create.Options &= ~FILE_DIRECTORY_FILE;
|
|
//@@END_DDKSPLIT
|
|
|
|
//
|
|
// For the create/open operation, send a KEYBOARD_ENABLE internal
|
|
// device control request to the port driver to enable interrupts.
|
|
//
|
|
|
|
if (deviceExtension->Self == deviceExtension->TrueClassDevice) {
|
|
//
|
|
// The real keyboard is being opened. This either represents the
|
|
// Grand Master, if one exists, or the individual keyboards objects,
|
|
// if all for one is not set. (IE "KeyboardClassX")
|
|
//
|
|
// First, if the requestor is the trusted subsystem (the single
|
|
// reader), reset the the cleanup indicator and place a pointer
|
|
// to the file object which this class driver uses
|
|
// to determine if the requestor has sufficient
|
|
// privilege to perform the read operation).
|
|
//
|
|
|
|
priv = RtlConvertLongToLuid(SE_TCB_PRIVILEGE);
|
|
|
|
if (SeSinglePrivilegeCheck(priv, Irp->RequestorMode)) {
|
|
|
|
KeAcquireSpinLock(&deviceExtension->SpinLock, &oldIrql);
|
|
|
|
ASSERT (!IS_TRUSTED_FILE_FOR_READ (irpSp->FileObject));
|
|
SET_TRUSTED_FILE_FOR_READ (irpSp->FileObject);
|
|
deviceExtension->TrustedSubsystemCount++;
|
|
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, oldIrql);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Pass on enables for opens to the true class device
|
|
//
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
if ((Globals.GrandMaster == deviceExtension) && (1 == ++Globals.Opens)) {
|
|
|
|
for (i = 0; i < Globals.NumAssocClass; i++) {
|
|
port = &Globals.AssocClassList[i];
|
|
|
|
if (port->Free) {
|
|
continue;
|
|
}
|
|
|
|
enabled = port->Enabled;
|
|
port->Enabled = TRUE;
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
if (!enabled) {
|
|
status = KbdEnableDisablePort(TRUE,
|
|
Irp,
|
|
port->Port,
|
|
&port->File);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
KbdPrint((0,
|
|
"KBDCLASS-KeyboardClassOpenClose: Could not enable/disable interrupts for port device object @ 0x%x\n",
|
|
port->Port->TopPort));
|
|
|
|
KeyboardClassLogError (DeviceObject,
|
|
KBDCLASS_PORT_INTERRUPTS_NOT_ENABLED,
|
|
KEYBOARD_ERROR_VALUE_BASE + 120,
|
|
status,
|
|
0,
|
|
NULL,
|
|
irpSp->MajorFunction);
|
|
|
|
port->Enabled = FALSE;
|
|
}
|
|
else {
|
|
someEnableDisableSucceeded = TRUE;
|
|
}
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
}
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
} else if (Globals.GrandMaster != deviceExtension) {
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
if (deviceExtension->TrueClassDevice == DeviceObject) {
|
|
//
|
|
// An open to the true class Device => enable the one and only port
|
|
//
|
|
|
|
status = KbdEnableDisablePort (TRUE,
|
|
Irp,
|
|
deviceExtension,
|
|
&irpSp->FileObject);
|
|
|
|
} else {
|
|
//
|
|
// A subordinant FDO. They are not their own TrueClassDeviceObject.
|
|
// Therefore pass the create straight on through.
|
|
//
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = IoCallDriver (deviceExtension->TopPort, Irp);
|
|
IoReleaseRemoveLock (&deviceExtension->RemoveLock, Irp);
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
KbdPrint((0,
|
|
"KBDCLASS-KeyboardClassOpenClose: Create failed (0x%x) port device object @ 0x%x\n",
|
|
status, deviceExtension->TopPort));
|
|
#if 0
|
|
//
|
|
// Log an error.
|
|
//
|
|
// Do not log an error for a failed open on a PNP device, esp HID
|
|
// devices which can be easily opened from user mode
|
|
//
|
|
KeyboardClassLogError (DeviceObject,
|
|
KBDCLASS_PORT_INTERRUPTS_NOT_ENABLED,
|
|
KEYBOARD_ERROR_VALUE_BASE + 120,
|
|
status,
|
|
0,
|
|
NULL,
|
|
irpSp->MajorFunction);
|
|
#endif
|
|
}
|
|
else {
|
|
someEnableDisableSucceeded = TRUE;
|
|
}
|
|
} else {
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
}
|
|
|
|
//
|
|
// Complete the request and return status.
|
|
//
|
|
// NOTE: We complete the request successfully if any one of the
|
|
// connected port devices successfully handled the request.
|
|
// The RIT only knows about one pointing device.
|
|
//
|
|
|
|
if (someEnableDisableSucceeded) {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
IoReleaseRemoveLock (&deviceExtension->RemoveLock, Irp);
|
|
|
|
KeyboardClassCreateEnd:
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassOpenClose: exit\n"));
|
|
return(status);
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassClose (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch routine for create/open and close requests.
|
|
Open/close requests are completed here.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PPORT port;
|
|
KIRQL oldIrql;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG i;
|
|
LUID priv;
|
|
KEVENT event;
|
|
PFILE_OBJECT file;
|
|
BOOLEAN someEnableDisableSucceeded = FALSE;
|
|
BOOLEAN enabled;
|
|
PVOID notifyHandle;
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassClose: enter\n"));
|
|
|
|
//
|
|
// Get a pointer to the device extension.
|
|
//
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Get a pointer to the current parameters for this request. The
|
|
// information is contained in the current stack location.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Let the close go through even if the device is removed
|
|
// AKA do not call KbdIncIoCount
|
|
//
|
|
|
|
//
|
|
// For the create/open operation, send a KEYBOARD_ENABLE internal
|
|
// device control request to the port driver to enable interrupts.
|
|
//
|
|
|
|
ASSERT (IRP_MJ_CLOSE == irpSp->MajorFunction);
|
|
|
|
if (deviceExtension->Self == deviceExtension->TrueClassDevice) {
|
|
|
|
KeAcquireSpinLock(&deviceExtension->SpinLock, &oldIrql);
|
|
if (IS_TRUSTED_FILE_FOR_READ (irpSp->FileObject)) {
|
|
ASSERT(0 < deviceExtension->TrustedSubsystemCount);
|
|
deviceExtension->TrustedSubsystemCount--;
|
|
CLEAR_TRUSTED_FILE_FOR_READ (irpSp->FileObject);
|
|
}
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, oldIrql);
|
|
}
|
|
|
|
//
|
|
// Pass on enables for closes to the true class device
|
|
//
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
if ((Globals.GrandMaster == deviceExtension) && (0 == --Globals.Opens)) {
|
|
|
|
for (i = 0; i < Globals.NumAssocClass; i++) {
|
|
port = &Globals.AssocClassList[i];
|
|
|
|
if (port->Free) {
|
|
continue;
|
|
}
|
|
|
|
enabled = port->Enabled;
|
|
port->Enabled = FALSE;
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
if (enabled) {
|
|
notifyHandle = InterlockedExchangePointer (
|
|
&port->Port->TargetNotifyHandle,
|
|
NULL);
|
|
|
|
if (NULL != notifyHandle) {
|
|
IoUnregisterPlugPlayNotification (notifyHandle);
|
|
}
|
|
status = KbdEnableDisablePort(FALSE,
|
|
Irp,
|
|
port->Port,
|
|
&port->File);
|
|
} else {
|
|
ASSERT (NULL == port->Port->TargetNotifyHandle);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
KbdPrint((0,
|
|
"KBDCLASS-KeyboardClassOpenClose: Could not enable/disable interrupts for port device object @ 0x%x\n",
|
|
port->Port->TopPort));
|
|
|
|
//
|
|
// Log an error.
|
|
//
|
|
KeyboardClassLogError (DeviceObject,
|
|
KBDCLASS_PORT_INTERRUPTS_NOT_DISABLED,
|
|
KEYBOARD_ERROR_VALUE_BASE + 120,
|
|
status,
|
|
0,
|
|
NULL,
|
|
0);
|
|
}
|
|
else {
|
|
someEnableDisableSucceeded = TRUE;
|
|
}
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
}
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
} else if (Globals.GrandMaster != deviceExtension) {
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
if (deviceExtension->TrueClassDevice == DeviceObject) {
|
|
//
|
|
// A close to the true class Device => disable the one and only port
|
|
//
|
|
|
|
status = KbdEnableDisablePort (FALSE,
|
|
Irp,
|
|
deviceExtension,
|
|
&irpSp->FileObject);
|
|
|
|
} else {
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = IoCallDriver (deviceExtension->TopPort, Irp);
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
KbdPrint((0,
|
|
"KBDCLASS-KeyboardClassOpenClose: Could not enable/disable interrupts for port device object @ 0x%x\n",
|
|
deviceExtension->TopPort));
|
|
|
|
//
|
|
// Log an error.
|
|
//
|
|
KeyboardClassLogError (DeviceObject,
|
|
KBDCLASS_PORT_INTERRUPTS_NOT_DISABLED,
|
|
KEYBOARD_ERROR_VALUE_BASE + 120,
|
|
status,
|
|
0,
|
|
NULL,
|
|
irpSp->MajorFunction);
|
|
}
|
|
else {
|
|
someEnableDisableSucceeded = TRUE;
|
|
}
|
|
} else {
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
}
|
|
|
|
//
|
|
// Complete the request and return status.
|
|
//
|
|
// NOTE: We complete the request successfully if any one of the
|
|
// connected port devices successfully handled the request.
|
|
// The RIT only knows about one pointing device.
|
|
//
|
|
|
|
if (someEnableDisableSucceeded) {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassOpenClose: exit\n"));
|
|
return(status);
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassReadCopyData(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Copies data as much from the internal queue to the irp as possible.
|
|
|
|
Assumptions:
|
|
DeviceExtension->SpinLock is already held (so no further synch
|
|
is required).
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PCHAR destination;
|
|
ULONG bytesInQueue;
|
|
ULONG bytesToMove;
|
|
ULONG moveSize;
|
|
|
|
//
|
|
// Bump the error log sequence number.
|
|
//
|
|
DeviceExtension->SequenceNumber += 1;
|
|
|
|
ASSERT (DeviceExtension->InputCount != 0);
|
|
|
|
//
|
|
// Copy as much of the input data as possible from the class input
|
|
// data queue to the SystemBuffer to satisfy the read. It may be
|
|
// necessary to copy the data in two chunks (i.e., if the circular
|
|
// queue wraps).
|
|
//
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// BytesToMove <- MIN(Number of filled bytes in class input data queue,
|
|
// Requested read length).
|
|
//
|
|
bytesInQueue = DeviceExtension->InputCount *
|
|
sizeof(KEYBOARD_INPUT_DATA);
|
|
bytesToMove = irpSp->Parameters.Read.Length;
|
|
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassReadCopyData: queue size 0x%lx, read length 0x%lx\n",
|
|
bytesInQueue,
|
|
bytesToMove
|
|
));
|
|
|
|
bytesToMove = (bytesInQueue < bytesToMove) ?
|
|
bytesInQueue:bytesToMove;
|
|
|
|
//
|
|
// MoveSize <- MIN(Number of bytes to be moved from the class queue,
|
|
// Number of bytes to end of class input data queue).
|
|
//
|
|
bytesInQueue = (ULONG)(((PCHAR) DeviceExtension->InputData +
|
|
DeviceExtension->KeyboardAttributes.InputDataQueueLength) -
|
|
(PCHAR) DeviceExtension->DataOut);
|
|
moveSize = (bytesToMove < bytesInQueue) ?
|
|
bytesToMove:bytesInQueue;
|
|
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassReadCopyData: bytes to end of queue 0x%lx\n",
|
|
bytesInQueue
|
|
));
|
|
|
|
//
|
|
// Move bytes from the class input data queue to SystemBuffer, until
|
|
// the request is satisfied or we wrap the class input data buffer.
|
|
//
|
|
destination = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassReadCopyData: number of bytes in first move 0x%lx\n",
|
|
moveSize
|
|
));
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassReadCopyData: move bytes from 0x%lx to 0x%lx\n",
|
|
(PCHAR) DeviceExtension->DataOut,
|
|
destination
|
|
));
|
|
|
|
RtlMoveMemory(
|
|
destination,
|
|
(PCHAR) DeviceExtension->DataOut,
|
|
moveSize
|
|
);
|
|
destination += moveSize;
|
|
|
|
//
|
|
// If the data wraps in the class input data buffer, copy the rest
|
|
// of the data from the start of the input data queue
|
|
// buffer through the end of the queued data.
|
|
//
|
|
if ((bytesToMove - moveSize) > 0) {
|
|
//
|
|
// MoveSize <- Remaining number bytes to move.
|
|
//
|
|
moveSize = bytesToMove - moveSize;
|
|
|
|
//
|
|
// Move the bytes from the class input data queue to SystemBuffer.
|
|
//
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassReadCopyData: number of bytes in second move 0x%lx\n",
|
|
moveSize
|
|
));
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassReadCopyData: move bytes from 0x%lx to 0x%lx\n",
|
|
(PCHAR) DeviceExtension->InputData,
|
|
destination
|
|
));
|
|
|
|
RtlMoveMemory(
|
|
destination,
|
|
(PCHAR) DeviceExtension->InputData,
|
|
moveSize
|
|
);
|
|
|
|
//
|
|
// Update the class input data queue removal pointer.
|
|
//
|
|
DeviceExtension->DataOut = (PKEYBOARD_INPUT_DATA)
|
|
(((PCHAR) DeviceExtension->InputData) + moveSize);
|
|
}
|
|
else {
|
|
//
|
|
// Update the input data queue removal pointer.
|
|
//
|
|
DeviceExtension->DataOut = (PKEYBOARD_INPUT_DATA)
|
|
(((PCHAR) DeviceExtension->DataOut) + moveSize);
|
|
}
|
|
|
|
//
|
|
// Update the class input data queue InputCount.
|
|
//
|
|
DeviceExtension->InputCount -=
|
|
(bytesToMove / sizeof(KEYBOARD_INPUT_DATA));
|
|
|
|
if (DeviceExtension->InputCount == 0) {
|
|
//
|
|
// Reset the flag that determines whether it is time to log
|
|
// queue overflow errors. We don't want to log errors too often.
|
|
// Instead, log an error on the first overflow that occurs after
|
|
// the ring buffer has been emptied, and then stop logging errors
|
|
// until it gets cleared out and overflows again.
|
|
//
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KeyboardClassCopyReadData: Okay to log overflow\n"
|
|
));
|
|
|
|
DeviceExtension->OkayToLogOverflow = TRUE;
|
|
}
|
|
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassCopyReadData: new DataIn 0x%lx, DataOut 0x%lx\n",
|
|
DeviceExtension->DataIn,
|
|
DeviceExtension->DataOut
|
|
));
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassCopyReadData: new InputCount %ld\n",
|
|
DeviceExtension->InputCount
|
|
));
|
|
|
|
//
|
|
// Record how many bytes we have satisfied
|
|
//
|
|
Irp->IoStatus.Information = bytesToMove;
|
|
irpSp->Parameters.Read.Length = bytesToMove;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassHandleRead(
|
|
PDEVICE_EXTENSION DeviceExtension,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If there is queued data, the Irp will be completed immediately. If there is
|
|
no data to report, queue the irp.
|
|
|
|
--*/
|
|
{
|
|
PDRIVER_CANCEL oldCancelRoutine;
|
|
NTSTATUS status = STATUS_PENDING;
|
|
KIRQL irql;
|
|
BOOLEAN completeIrp = FALSE;
|
|
|
|
KeAcquireSpinLock(&DeviceExtension->SpinLock, &irql);
|
|
|
|
if (DeviceExtension->InputCount == 0) {
|
|
//
|
|
// Easy case to handle, just enqueue the irp
|
|
//
|
|
InsertTailList (&DeviceExtension->ReadQueue, &Irp->Tail.Overlay.ListEntry);
|
|
IoMarkIrpPending (Irp);
|
|
|
|
//
|
|
// Must set a cancel routine before checking the Cancel flag.
|
|
//
|
|
oldCancelRoutine = IoSetCancelRoutine (Irp, KeyboardClassCancel);
|
|
ASSERT (!oldCancelRoutine);
|
|
|
|
if (Irp->Cancel) {
|
|
//
|
|
// The IRP was cancelled. Check whether or not the cancel
|
|
// routine was called.
|
|
//
|
|
oldCancelRoutine = IoSetCancelRoutine (Irp, NULL);
|
|
if (oldCancelRoutine) {
|
|
//
|
|
// The cancel routine was NOT called so dequeue the IRP now and
|
|
// complete it after releasing the spinlock.
|
|
//
|
|
RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
|
|
status = Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
}
|
|
else {
|
|
//
|
|
// The cancel routine WAS called.
|
|
//
|
|
// As soon as we drop the spinlock it will dequeue and complete
|
|
// the IRP. So leave the IRP in the queue and otherwise don't
|
|
// touch it. Return pending since we're not completing the IRP
|
|
// here.
|
|
//
|
|
;
|
|
}
|
|
}
|
|
|
|
if (status != STATUS_PENDING){
|
|
completeIrp = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// If we have outstanding input to report, our queue better be empty!
|
|
//
|
|
ASSERT (IsListEmpty (&DeviceExtension->ReadQueue));
|
|
|
|
status = KeyboardClassReadCopyData (DeviceExtension, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
completeIrp = TRUE;
|
|
}
|
|
|
|
KeReleaseSpinLock (&DeviceExtension->SpinLock, irql);
|
|
|
|
if (completeIrp) {
|
|
IoReleaseRemoveLock (&DeviceExtension->RemoveLock, Irp);
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassRead(
|
|
IN PDEVICE_OBJECT Device,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch routine for read requests. Valid read
|
|
requests are either marked pending if no data has been queued or completed
|
|
immediatedly with available data.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassRead: enter\n"));
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Validate the read request parameters. The read length should be an
|
|
// integral number of KEYBOARD_INPUT_DATA structures.
|
|
//
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) Device->DeviceExtension;
|
|
if (irpSp->Parameters.Read.Length == 0) {
|
|
status = STATUS_SUCCESS;
|
|
} else if (irpSp->Parameters.Read.Length % sizeof(KEYBOARD_INPUT_DATA)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else if (deviceExtension->SurpriseRemoved) {
|
|
status = STATUS_DEVICE_NOT_CONNECTED;
|
|
} else if (IS_TRUSTED_FILE_FOR_READ (irpSp->FileObject)) {
|
|
//
|
|
// If the file object's FsContext is non-null, then we've already
|
|
// done the Read privilege check once before for this thread. Skip
|
|
// the privilege check.
|
|
//
|
|
|
|
status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp);
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
status = STATUS_PENDING;
|
|
}
|
|
} else {
|
|
//
|
|
// We only allow a trusted subsystem with the appropriate privilege
|
|
// level to execute a Read call.
|
|
//
|
|
|
|
status = STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
|
|
//
|
|
// If status is pending, mark the packet pending and start the packet
|
|
// in a cancellable state. Otherwise, complete the request.
|
|
//
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
if (status == STATUS_PENDING) {
|
|
return KeyboardClassHandleRead(deviceExtension, Irp);
|
|
}
|
|
else {
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassRead: exit\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
PIRP
|
|
KeyboardClassDequeueRead(
|
|
IN PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Dequeues the next available read irp regardless of FileObject
|
|
|
|
Assumptions:
|
|
DeviceExtension->SpinLock is already held (so no further sync is required).
|
|
|
|
--*/
|
|
{
|
|
PIRP nextIrp = NULL;
|
|
KIRQL oldIrql;
|
|
|
|
while (!nextIrp && !IsListEmpty (&DeviceExtension->ReadQueue)){
|
|
PDRIVER_CANCEL oldCancelRoutine;
|
|
PLIST_ENTRY listEntry = RemoveHeadList (&DeviceExtension->ReadQueue);
|
|
|
|
//
|
|
// Get the next IRP off the queue and clear the cancel routine
|
|
//
|
|
nextIrp = CONTAINING_RECORD (listEntry, IRP, Tail.Overlay.ListEntry);
|
|
oldCancelRoutine = IoSetCancelRoutine (nextIrp, NULL);
|
|
|
|
//
|
|
// IoCancelIrp() could have just been called on this IRP.
|
|
// What we're interested in is not whether IoCancelIrp() was called
|
|
// (ie, nextIrp->Cancel is set), but whether IoCancelIrp() called (or
|
|
// is about to call) our cancel routine. To check that, check the result
|
|
// of the test-and-set macro IoSetCancelRoutine.
|
|
//
|
|
if (oldCancelRoutine) {
|
|
//
|
|
// Cancel routine not called for this IRP. Return this IRP.
|
|
//
|
|
ASSERT (oldCancelRoutine == KeyboardClassCancel);
|
|
}
|
|
else {
|
|
//
|
|
// This IRP was just cancelled and the cancel routine was (or will
|
|
// be) called. The cancel routine will complete this IRP as soon as
|
|
// we drop the spinlock. So don't do anything with the IRP.
|
|
//
|
|
// Also, the cancel routine will try to dequeue the IRP, so make the
|
|
// IRP's listEntry point to itself.
|
|
//
|
|
ASSERT (nextIrp->Cancel);
|
|
InitializeListHead (&nextIrp->Tail.Overlay.ListEntry);
|
|
nextIrp = NULL;
|
|
}
|
|
}
|
|
|
|
return nextIrp;
|
|
}
|
|
|
|
PIRP
|
|
KeyboardClassDequeueReadByFileObject(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PFILE_OBJECT FileObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Dequeues the next available read with a matching FileObject
|
|
|
|
Assumptions:
|
|
DeviceExtension->SpinLock is already held (so no further sync is required).
|
|
|
|
--*/
|
|
{
|
|
PIRP irp = NULL;
|
|
PLIST_ENTRY entry;
|
|
PIO_STACK_LOCATION stack;
|
|
PDRIVER_CANCEL oldCancelRoutine;
|
|
KIRQL oldIrql;
|
|
|
|
if (FileObject == NULL) {
|
|
return KeyboardClassDequeueRead (DeviceExtension);
|
|
}
|
|
|
|
for (entry = DeviceExtension->ReadQueue.Flink;
|
|
entry != &DeviceExtension->ReadQueue;
|
|
entry = entry->Flink) {
|
|
|
|
irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry);
|
|
stack = IoGetCurrentIrpStackLocation (irp);
|
|
if (stack->FileObject == FileObject) {
|
|
RemoveEntryList (entry);
|
|
|
|
oldCancelRoutine = IoSetCancelRoutine (irp, NULL);
|
|
|
|
//
|
|
// IoCancelIrp() could have just been called on this IRP.
|
|
// What we're interested in is not whether IoCancelIrp() was called
|
|
// (ie, nextIrp->Cancel is set), but whether IoCancelIrp() called (or
|
|
// is about to call) our cancel routine. To check that, check the result
|
|
// of the test-and-set macro IoSetCancelRoutine.
|
|
//
|
|
if (oldCancelRoutine) {
|
|
//
|
|
// Cancel routine not called for this IRP. Return this IRP.
|
|
//
|
|
return irp;
|
|
}
|
|
else {
|
|
//
|
|
// This IRP was just cancelled and the cancel routine was (or will
|
|
// be) called. The cancel routine will complete this IRP as soon as
|
|
// we drop the spinlock. So don't do anything with the IRP.
|
|
//
|
|
// Also, the cancel routine will try to dequeue the IRP, so make the
|
|
// IRP's listEntry point to itself.
|
|
//
|
|
ASSERT (irp->Cancel);
|
|
InitializeListHead (&irp->Tail.Overlay.ListEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
KeyboardClassServiceCallback(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PKEYBOARD_INPUT_DATA InputDataStart,
|
|
IN PKEYBOARD_INPUT_DATA InputDataEnd,
|
|
IN OUT PULONG InputDataConsumed
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the class service callback routine. It is
|
|
called from the port driver's interrupt service DPC. If there is an
|
|
outstanding read request, the request is satisfied from the port input
|
|
data queue. Unsolicited keyboard input is moved from the port input
|
|
|
|
data queue to the class input data queue.
|
|
|
|
N.B. This routine is entered at DISPATCH_LEVEL IRQL from the port
|
|
driver's ISR DPC routine.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
InputDataStart - Pointer to the start of the data in the port input
|
|
data queue.
|
|
|
|
InputDataEnd - Points one input data structure past the end of the
|
|
valid port input data.
|
|
|
|
InputDataConsumed - Pointer to storage in which the number of input
|
|
data structures consumed by this call is returned.
|
|
|
|
NOTE: Could pull the duplicate code out into a called procedure.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PIO_STACK_LOCATION irpSp;
|
|
LIST_ENTRY listHead;
|
|
PIRP irp;
|
|
ULONG bytesInQueue;
|
|
ULONG bytesToMove;
|
|
ULONG moveSize;
|
|
ULONG dumpData[2];
|
|
BOOLEAN logOverflow;
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassServiceCallback: enter\n"));
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
bytesInQueue = (ULONG)((PCHAR) InputDataEnd - (PCHAR) InputDataStart);
|
|
moveSize = 0;
|
|
*InputDataConsumed = 0;
|
|
|
|
logOverflow = FALSE;
|
|
|
|
//
|
|
// Notify system that human input has occured
|
|
//
|
|
|
|
PoSetSystemState (ES_USER_PRESENT);
|
|
|
|
//
|
|
// N.B. We can use KeAcquireSpinLockAtDpcLevel, instead of
|
|
// KeAcquireSpinLock, because this routine is already running
|
|
// at DISPATCH_IRQL.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel (&deviceExtension->SpinLock);
|
|
|
|
InitializeListHead (&listHead);
|
|
irp = KeyboardClassDequeueRead (deviceExtension);
|
|
if (irp) {
|
|
//
|
|
// An outstanding read request exists.
|
|
//
|
|
// Copy as much of the input data possible from the port input
|
|
// data queue to the SystemBuffer to satisfy the read.
|
|
//
|
|
irpSp = IoGetCurrentIrpStackLocation (irp);
|
|
bytesToMove = irpSp->Parameters.Read.Length;
|
|
moveSize = (bytesInQueue < bytesToMove) ?
|
|
bytesInQueue:bytesToMove;
|
|
*InputDataConsumed += (moveSize / sizeof(KEYBOARD_INPUT_DATA));
|
|
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: port queue length 0x%lx, read length 0x%lx\n",
|
|
bytesInQueue,
|
|
bytesToMove
|
|
));
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: number of bytes to move from port to SystemBuffer 0x%lx\n",
|
|
moveSize
|
|
));
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: move bytes from 0x%lx to 0x%lx\n",
|
|
(PCHAR) InputDataStart,
|
|
irp->AssociatedIrp.SystemBuffer
|
|
));
|
|
|
|
RtlMoveMemory(
|
|
irp->AssociatedIrp.SystemBuffer,
|
|
(PCHAR) InputDataStart,
|
|
moveSize
|
|
);
|
|
|
|
//
|
|
// Set the flag so that we start the next packet and complete
|
|
// this read request (with STATUS_SUCCESS) prior to return.
|
|
//
|
|
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
irp->IoStatus.Information = moveSize;
|
|
irpSp->Parameters.Read.Length = moveSize;
|
|
|
|
InsertTailList (&listHead, &irp->Tail.Overlay.ListEntry);
|
|
}
|
|
|
|
//
|
|
// If there is still data in the port input data queue, move it to the class
|
|
// input data queue.
|
|
//
|
|
InputDataStart = (PKEYBOARD_INPUT_DATA) ((PCHAR) InputDataStart + moveSize);
|
|
moveSize = bytesInQueue - moveSize;
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: bytes remaining after move to SystemBuffer 0x%lx\n",
|
|
moveSize
|
|
));
|
|
|
|
if (moveSize > 0) {
|
|
|
|
//
|
|
// Move the remaining data from the port input data queue to
|
|
// the class input data queue. The move will happen in two
|
|
// parts in the case where the class input data buffer wraps.
|
|
//
|
|
|
|
bytesInQueue =
|
|
deviceExtension->KeyboardAttributes.InputDataQueueLength -
|
|
(deviceExtension->InputCount * sizeof(KEYBOARD_INPUT_DATA));
|
|
bytesToMove = moveSize;
|
|
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: unused bytes in class queue 0x%lx, remaining bytes in port queue 0x%lx\n",
|
|
bytesInQueue,
|
|
bytesToMove
|
|
));
|
|
|
|
#if ALLOW_OVERFLOW
|
|
#else
|
|
if (bytesInQueue == 0) {
|
|
|
|
//
|
|
// Refuse to move any bytes that would cause a class input data
|
|
// queue overflow. Just drop the bytes on the floor, and
|
|
// log an overrun error.
|
|
//
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KeyboardClassServiceCallback: Class input data queue OVERRUN\n"
|
|
));
|
|
|
|
if (deviceExtension->OkayToLogOverflow) {
|
|
//
|
|
// Allocate and report the error log entry outside of any locks
|
|
// we are currently holding
|
|
//
|
|
logOverflow = TRUE;
|
|
dumpData[0] = bytesToMove;
|
|
dumpData[1] =
|
|
deviceExtension->KeyboardAttributes.InputDataQueueLength;
|
|
|
|
deviceExtension->OkayToLogOverflow = FALSE;
|
|
}
|
|
|
|
} else {
|
|
#endif
|
|
|
|
//
|
|
// There is room in the class input data queue, so move
|
|
// the remaining port input data to it.
|
|
//
|
|
// BytesToMove <- MIN(Number of unused bytes in class input data
|
|
// queue, Number of bytes remaining in port
|
|
// input queue).
|
|
// This is the total number of bytes that actually will move from
|
|
// the port input data queue to the class input data queue.
|
|
//
|
|
|
|
#if ALLOW_OVERFLOW
|
|
bytesInQueue = deviceExtension->KeyboardAttributes.InputDataQueueLength;
|
|
#endif
|
|
bytesToMove = (bytesInQueue < bytesToMove) ?
|
|
bytesInQueue:bytesToMove;
|
|
|
|
//
|
|
// BytesInQueue <- Number of unused bytes from insertion pointer to
|
|
// the end of the class input data queue (i.e., until the buffer
|
|
// wraps).
|
|
//
|
|
|
|
bytesInQueue = (ULONG)(((PCHAR) deviceExtension->InputData +
|
|
deviceExtension->KeyboardAttributes.InputDataQueueLength) -
|
|
(PCHAR) deviceExtension->DataIn);
|
|
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: total number of bytes to move to class queue 0x%lx\n",
|
|
bytesToMove
|
|
));
|
|
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: number of bytes to end of class buffer 0x%lx\n",
|
|
bytesInQueue
|
|
));
|
|
|
|
//
|
|
// MoveSize <- Number of bytes to handle in the first move.
|
|
//
|
|
|
|
moveSize = (bytesToMove < bytesInQueue) ?
|
|
bytesToMove:bytesInQueue;
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: number of bytes in first move to class 0x%lx\n",
|
|
moveSize
|
|
));
|
|
|
|
//
|
|
// Do the move from the port data queue to the class data queue.
|
|
//
|
|
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: move bytes from 0x%lx to 0x%lx\n",
|
|
(PCHAR) InputDataStart,
|
|
(PCHAR) deviceExtension->DataIn
|
|
));
|
|
|
|
RtlMoveMemory(
|
|
(PCHAR) deviceExtension->DataIn,
|
|
(PCHAR) InputDataStart,
|
|
moveSize
|
|
);
|
|
|
|
//
|
|
// Increment the port data queue pointer and the class input
|
|
// data queue insertion pointer. Wrap the insertion pointer,
|
|
// if necessary.
|
|
//
|
|
|
|
InputDataStart = (PKEYBOARD_INPUT_DATA)
|
|
(((PCHAR) InputDataStart) + moveSize);
|
|
deviceExtension->DataIn = (PKEYBOARD_INPUT_DATA)
|
|
(((PCHAR) deviceExtension->DataIn) + moveSize);
|
|
if ((PCHAR) deviceExtension->DataIn >=
|
|
((PCHAR) deviceExtension->InputData +
|
|
deviceExtension->KeyboardAttributes.InputDataQueueLength)) {
|
|
deviceExtension->DataIn = deviceExtension->InputData;
|
|
}
|
|
|
|
if ((bytesToMove - moveSize) > 0) {
|
|
|
|
//
|
|
// Special case. The data must wrap in the class input data buffer.
|
|
// Copy the rest of the port input data into the beginning of the
|
|
// class input data queue.
|
|
//
|
|
|
|
//
|
|
// MoveSize <- Number of bytes to handle in the second move.
|
|
//
|
|
|
|
moveSize = bytesToMove - moveSize;
|
|
|
|
//
|
|
// Do the move from the port data queue to the class data queue.
|
|
//
|
|
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: number of bytes in second move to class 0x%lx\n",
|
|
moveSize
|
|
));
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: move bytes from 0x%lx to 0x%lx\n",
|
|
(PCHAR) InputDataStart,
|
|
(PCHAR) deviceExtension->DataIn
|
|
));
|
|
|
|
RtlMoveMemory(
|
|
(PCHAR) deviceExtension->DataIn,
|
|
(PCHAR) InputDataStart,
|
|
moveSize
|
|
);
|
|
|
|
//
|
|
// Update the class input data queue insertion pointer.
|
|
//
|
|
|
|
deviceExtension->DataIn = (PKEYBOARD_INPUT_DATA)
|
|
(((PCHAR) deviceExtension->DataIn) + moveSize);
|
|
}
|
|
|
|
//
|
|
// Update the input data queue counter.
|
|
//
|
|
|
|
deviceExtension->InputCount +=
|
|
(bytesToMove / sizeof(KEYBOARD_INPUT_DATA));
|
|
*InputDataConsumed += (bytesToMove / sizeof(KEYBOARD_INPUT_DATA));
|
|
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: changed InputCount to %ld entries in the class queue\n",
|
|
deviceExtension->InputCount
|
|
));
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: DataIn 0x%lx, DataOut 0x%lx\n",
|
|
deviceExtension->DataIn,
|
|
deviceExtension->DataOut
|
|
));
|
|
KbdPrint((
|
|
3,
|
|
"KBDCLASS-KeyboardClassServiceCallback: Input data items consumed = %d\n",
|
|
*InputDataConsumed
|
|
));
|
|
#if ALLOW_OVERFLOW
|
|
#else
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// If we still have data in our internal queue, fulfill any outstanding
|
|
// reads now until either we run out of data or outstanding reads
|
|
//
|
|
while (deviceExtension->InputCount > 0 &&
|
|
(irp = KeyboardClassDequeueRead (deviceExtension)) != NULL) {
|
|
irp->IoStatus.Status = KeyboardClassReadCopyData (deviceExtension, irp);
|
|
InsertTailList (&listHead, &irp->Tail.Overlay.ListEntry);
|
|
}
|
|
|
|
//
|
|
// Release the class input data queue spinlock.
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel (&deviceExtension->SpinLock);
|
|
|
|
if (logOverflow) {
|
|
KeyboardClassLogError (DeviceObject,
|
|
KBDCLASS_KBD_BUFFER_OVERFLOW,
|
|
KEYBOARD_ERROR_VALUE_BASE + 210,
|
|
0,
|
|
2,
|
|
dumpData,
|
|
0);
|
|
}
|
|
|
|
//
|
|
// Complete all the read requests we have fulfilled outside of the spin lock
|
|
//
|
|
while (! IsListEmpty (&listHead)) {
|
|
PLIST_ENTRY entry = RemoveHeadList (&listHead);
|
|
|
|
irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry);
|
|
ASSERT (NT_SUCCESS (irp->IoStatus.Status) &&
|
|
irp->IoStatus.Status != STATUS_PENDING);
|
|
IoCompleteRequest (irp, IO_KEYBOARD_INCREMENT);
|
|
|
|
IoReleaseRemoveLock (&deviceExtension->RemoveLock, irp);
|
|
}
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassServiceCallback: exit\n"));
|
|
}
|
|
|
|
VOID
|
|
KeyboardClassUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the class driver unload routine.
|
|
|
|
NOTE: Not currently implemented.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY entry;
|
|
PDEVICE_EXTENSION data;
|
|
PPORT port;
|
|
PIRP irp;
|
|
|
|
UNREFERENCED_PARAMETER(DriverObject);
|
|
|
|
PAGED_CODE ();
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassUnload: enter\n"));
|
|
|
|
//
|
|
// Delete all of our legacy devices
|
|
//
|
|
for (entry = Globals.LegacyDeviceList.Flink;
|
|
entry != &Globals.LegacyDeviceList;
|
|
/* advance to next before deleting the devobj */) {
|
|
|
|
BOOLEAN enabled = FALSE;
|
|
PFILE_OBJECT file = NULL;
|
|
|
|
data = CONTAINING_RECORD (entry, DEVICE_EXTENSION, Link);
|
|
ASSERT (data->PnP == FALSE);
|
|
|
|
if (Globals.GrandMaster) {
|
|
port = &Globals.AssocClassList[data->UnitId];
|
|
ASSERT (port->Port == data);
|
|
|
|
enabled = port->Enabled;
|
|
file = port->File;
|
|
|
|
port->Enabled = FALSE;
|
|
port->File = NULL;
|
|
port->Free = TRUE;
|
|
}
|
|
else {
|
|
enabled = data->Enabled;
|
|
file = data->File;
|
|
ASSERT (data->File);
|
|
data->Enabled = FALSE;
|
|
}
|
|
|
|
if (enabled) {
|
|
irp = IoAllocateIrp(data->TopPort->StackSize+1, FALSE);
|
|
if (irp) {
|
|
KbdEnableDisablePort (FALSE, irp, data, &file);
|
|
IoFreeIrp (irp);
|
|
}
|
|
}
|
|
|
|
//
|
|
// This file object represents the open we performed on the legacy
|
|
// port device object. It does NOT represent the open that the RIT
|
|
// performed on our DO.
|
|
//
|
|
if (file) {
|
|
ObDereferenceObject(file);
|
|
}
|
|
|
|
|
|
//
|
|
// Clean out the queue only if there is no GM
|
|
//
|
|
if (Globals.GrandMaster == NULL) {
|
|
KeyboardClassCleanupQueue (data->Self, data, NULL);
|
|
}
|
|
|
|
RemoveEntryList (&data->Link);
|
|
entry = entry->Flink;
|
|
|
|
KeyboardClassDeleteLegacyDevice (data);
|
|
}
|
|
|
|
//
|
|
// Delete the grandmaster if it exists
|
|
//
|
|
if (Globals.GrandMaster) {
|
|
data = Globals.GrandMaster;
|
|
Globals.GrandMaster = NULL;
|
|
|
|
KeyboardClassCleanupQueue (data->Self, data, NULL);
|
|
KeyboardClassDeleteLegacyDevice (data);
|
|
}
|
|
|
|
ExFreePool(Globals.RegistryPath.Buffer);
|
|
if (Globals.AssocClassList) {
|
|
#if DBG
|
|
ULONG i;
|
|
|
|
for (i = 0; i < Globals.NumAssocClass; i++) {
|
|
ASSERT (Globals.AssocClassList[i].Free == TRUE);
|
|
ASSERT (Globals.AssocClassList[i].Enabled == FALSE);
|
|
ASSERT (Globals.AssocClassList[i].File == NULL);
|
|
}
|
|
#endif
|
|
|
|
ExFreePool(Globals.AssocClassList);
|
|
}
|
|
|
|
KbdPrint((2,"KBDCLASS-KeyboardClassUnload: exit\n"));
|
|
}
|
|
|
|
VOID
|
|
KbdConfiguration()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine stores the configuration information for this device.
|
|
|
|
Return Value:
|
|
|
|
None. As a side-effect, sets fields in
|
|
DeviceExtension->KeyboardAttributes.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
|
|
ULONG defaultDataQueueSize = DATA_QUEUE_SIZE;
|
|
ULONG defaultMaximumPortsServiced = 1;
|
|
ULONG defaultConnectMultiplePorts = 1;
|
|
ULONG defaultSendOutputToAllPorts = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
UNICODE_STRING parametersPath;
|
|
UNICODE_STRING defaultUnicodeName;
|
|
PWSTR path = NULL;
|
|
USHORT queriesPlusOne = 6;
|
|
|
|
PAGED_CODE ();
|
|
|
|
parametersPath.Buffer = NULL;
|
|
|
|
//
|
|
// Registry path is already null-terminated, so just use it.
|
|
//
|
|
|
|
path = Globals.RegistryPath.Buffer;
|
|
|
|
//
|
|
// Allocate the Rtl query table.
|
|
//
|
|
|
|
parameters = ExAllocatePool(
|
|
PagedPool,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * queriesPlusOne
|
|
);
|
|
|
|
if (!parameters) {
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdConfiguration: Couldn't allocate table for Rtl query to parameters for %ws\n",
|
|
path
|
|
));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory(
|
|
parameters,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * queriesPlusOne
|
|
);
|
|
|
|
//
|
|
// Form a path to this driver's Parameters subkey.
|
|
//
|
|
|
|
RtlInitUnicodeString(
|
|
¶metersPath,
|
|
NULL
|
|
);
|
|
|
|
parametersPath.MaximumLength = Globals.RegistryPath.Length +
|
|
sizeof(L"\\Parameters");
|
|
|
|
parametersPath.Buffer = ExAllocatePool(
|
|
PagedPool,
|
|
parametersPath.MaximumLength
|
|
);
|
|
|
|
if (!parametersPath.Buffer) {
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdConfiguration: Couldn't allocate string for path to parameters for %ws\n",
|
|
path
|
|
));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Form the parameters path.
|
|
//
|
|
|
|
RtlZeroMemory(parametersPath.Buffer, parametersPath.MaximumLength);
|
|
RtlAppendUnicodeToString(¶metersPath, path);
|
|
RtlAppendUnicodeToString(¶metersPath, L"\\Parameters");
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdConfiguration: parameters path is %ws\n",
|
|
parametersPath.Buffer
|
|
));
|
|
|
|
//
|
|
// Form the default keyboard class device name, in case it is not
|
|
// specified in the registry.
|
|
//
|
|
|
|
RtlInitUnicodeString(
|
|
&defaultUnicodeName,
|
|
DD_KEYBOARD_CLASS_BASE_NAME_U
|
|
);
|
|
|
|
//
|
|
// Gather all of the "user specified" information from
|
|
// the registry.
|
|
//
|
|
|
|
parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[0].Name = L"KeyboardDataQueueSize";
|
|
parameters[0].EntryContext =
|
|
&Globals.InitExtension.KeyboardAttributes.InputDataQueueLength;
|
|
parameters[0].DefaultType = REG_DWORD;
|
|
parameters[0].DefaultData = &defaultDataQueueSize;
|
|
parameters[0].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[1].Name = L"MaximumPortsServiced";
|
|
parameters[1].EntryContext = &Globals.PortsServiced;
|
|
parameters[1].DefaultType = REG_DWORD;
|
|
parameters[1].DefaultData = &defaultMaximumPortsServiced;
|
|
parameters[1].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[2].Name = L"KeyboardDeviceBaseName";
|
|
parameters[2].EntryContext = &Globals.BaseClassName;
|
|
parameters[2].DefaultType = REG_SZ;
|
|
parameters[2].DefaultData = defaultUnicodeName.Buffer;
|
|
parameters[2].DefaultLength = 0;
|
|
|
|
//
|
|
// Using this parameter in an inverted fashion, registry key is
|
|
// backwards from global variable. (Note comment below).
|
|
//
|
|
parameters[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[3].Name = L"ConnectMultiplePorts";
|
|
parameters[3].EntryContext = &Globals.ConnectOneClassToOnePort;
|
|
parameters[3].DefaultType = REG_DWORD;
|
|
parameters[3].DefaultData = &defaultConnectMultiplePorts;
|
|
parameters[3].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[4].Name = L"SendOutputToAllPorts";
|
|
parameters[4].EntryContext =
|
|
&Globals.SendOutputToAllPorts;
|
|
parameters[4].DefaultType = REG_DWORD;
|
|
parameters[4].DefaultData = &defaultSendOutputToAllPorts;
|
|
parameters[4].DefaultLength = sizeof(ULONG);
|
|
|
|
status = RtlQueryRegistryValues(
|
|
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
|
parametersPath.Buffer,
|
|
parameters,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdConfiguration: RtlQueryRegistryValues failed with 0x%x\n",
|
|
status
|
|
));
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Go ahead and assign driver defaults.
|
|
//
|
|
|
|
Globals.InitExtension.KeyboardAttributes.InputDataQueueLength =
|
|
defaultDataQueueSize;
|
|
Globals.PortsServiced = defaultMaximumPortsServiced;
|
|
Globals.ConnectOneClassToOnePort = defaultConnectMultiplePorts;
|
|
Globals.SendOutputToAllPorts = defaultSendOutputToAllPorts;
|
|
RtlCopyUnicodeString(&Globals.BaseClassName, &defaultUnicodeName);
|
|
}
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdConfiguration: Keyboard class base name = %ws\n",
|
|
Globals.BaseClassName.Buffer
|
|
));
|
|
|
|
if (Globals.InitExtension.KeyboardAttributes.InputDataQueueLength == 0) {
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdConfiguration: overriding KeyboardInputDataQueueLength = 0x%x\n",
|
|
Globals.InitExtension.KeyboardAttributes.InputDataQueueLength
|
|
));
|
|
|
|
Globals.InitExtension.KeyboardAttributes.InputDataQueueLength =
|
|
defaultDataQueueSize;
|
|
}
|
|
|
|
Globals.InitExtension.KeyboardAttributes.InputDataQueueLength *=
|
|
sizeof(KEYBOARD_INPUT_DATA);
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdConfiguration: KeyboardInputDataQueueLength = 0x%x\n",
|
|
Globals.InitExtension.KeyboardAttributes.InputDataQueueLength
|
|
));
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdConfiguration: MaximumPortsServiced = %d\n",
|
|
Globals.PortsServiced
|
|
));
|
|
|
|
//
|
|
// Invert the flag that specifies the type of class/port connections.
|
|
// We used it in the RtlQuery call in an inverted fashion.
|
|
//
|
|
|
|
Globals.ConnectOneClassToOnePort = !Globals.ConnectOneClassToOnePort;
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdConfiguration: Connection Type = %d\n",
|
|
Globals.ConnectOneClassToOnePort
|
|
));
|
|
|
|
//
|
|
// Free the allocated memory before returning.
|
|
//
|
|
|
|
if (parametersPath.Buffer)
|
|
ExFreePool(parametersPath.Buffer);
|
|
if (parameters)
|
|
ExFreePool(parameters);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
KbdCreateClassObject(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_EXTENSION TmpDeviceExtension,
|
|
OUT PDEVICE_OBJECT * ClassDeviceObject,
|
|
OUT PWCHAR * FullDeviceName,
|
|
IN BOOLEAN Legacy
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates the keyboard class device object.
|
|
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by system.
|
|
|
|
TmpDeviceExtension - Pointer to the template device extension.
|
|
|
|
FullDeviceName - Pointer to the Unicode string that is the full path name
|
|
for the class device object.
|
|
|
|
ClassDeviceObject - Pointer to a pointer to the class device object.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status from the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
ULONG uniqueErrorValue;
|
|
PDEVICE_EXTENSION deviceExtension = NULL;
|
|
NTSTATUS errorCode = STATUS_SUCCESS;
|
|
UNICODE_STRING fullClassName = {0,0,0};
|
|
ULONG dumpCount = 0;
|
|
ULONG dumpData[DUMP_COUNT];
|
|
ULONG i;
|
|
WCHAR nameIndex;
|
|
|
|
PAGED_CODE ();
|
|
|
|
KbdPrint((1,"\n\nKBDCLASS-KbdCreateClassObject: enter\n"));
|
|
|
|
//
|
|
// Create a non-exclusive device object for the keyboard class device.
|
|
//
|
|
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
|
|
//
|
|
// Make sure ClassDeviceObject isn't pointing to a random pointer value
|
|
//
|
|
*ClassDeviceObject = NULL;
|
|
|
|
if (NULL == Globals.GrandMaster) {
|
|
//
|
|
// Create a legacy name for this DO.
|
|
//
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
//
|
|
// Set up space for the class's full device object name.
|
|
//
|
|
fullClassName.MaximumLength = sizeof(L"\\Device\\") +
|
|
+ Globals.BaseClassName.Length
|
|
+ sizeof(L"0");
|
|
|
|
if (Globals.ConnectOneClassToOnePort && Legacy) {
|
|
fullClassName.MaximumLength += sizeof(L"Legacy");
|
|
}
|
|
|
|
fullClassName.Buffer = ExAllocatePool(PagedPool,
|
|
fullClassName.MaximumLength);
|
|
|
|
if (!fullClassName.Buffer) {
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KbdCLASS-KeyboardClassInitialize: Couldn't allocate string for device object name\n"
|
|
));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
errorCode = KBDCLASS_INSUFFICIENT_RESOURCES;
|
|
uniqueErrorValue = KEYBOARD_ERROR_VALUE_BASE + 6;
|
|
dumpData[0] = (ULONG) fullClassName.MaximumLength;
|
|
dumpCount = 1;
|
|
goto KbdCreateClassObjectExit;
|
|
}
|
|
|
|
RtlZeroMemory(fullClassName.Buffer, fullClassName.MaximumLength);
|
|
RtlAppendUnicodeToString(&fullClassName, L"\\Device\\");
|
|
RtlAppendUnicodeToString(&fullClassName, Globals.BaseClassName.Buffer);
|
|
|
|
if (Globals.ConnectOneClassToOnePort && Legacy) {
|
|
RtlAppendUnicodeToString(&fullClassName, L"Legacy");
|
|
}
|
|
|
|
RtlAppendUnicodeToString(&fullClassName, L"0");
|
|
|
|
//
|
|
// Using the base name start trying to create device names until
|
|
// one succeeds. Everytime start over at 0 to eliminate gaps.
|
|
//
|
|
nameIndex = 0;
|
|
|
|
do {
|
|
fullClassName.Buffer [ (fullClassName.Length / sizeof (WCHAR)) - 1]
|
|
= L'0' + nameIndex++;
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdCreateClassObject: Creating device object named %ws\n",
|
|
fullClassName.Buffer
|
|
));
|
|
|
|
status = IoCreateDevice(DriverObject,
|
|
sizeof (DEVICE_EXTENSION),
|
|
&fullClassName,
|
|
FILE_DEVICE_KEYBOARD,
|
|
0,
|
|
FALSE,
|
|
ClassDeviceObject);
|
|
|
|
} while (STATUS_OBJECT_NAME_COLLISION == status);
|
|
|
|
*FullDeviceName = fullClassName.Buffer;
|
|
|
|
} else {
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
status = IoCreateDevice(DriverObject,
|
|
sizeof(DEVICE_EXTENSION),
|
|
NULL, // no name for this FDO
|
|
FILE_DEVICE_KEYBOARD,
|
|
0,
|
|
FALSE,
|
|
ClassDeviceObject);
|
|
*FullDeviceName = NULL;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdCreateClassObject: Could not create class device object = %ws\n",
|
|
fullClassName.Buffer
|
|
));
|
|
|
|
errorCode = KBDCLASS_COULD_NOT_CREATE_DEVICE;
|
|
uniqueErrorValue = KEYBOARD_ERROR_VALUE_BASE + 6;
|
|
dumpData[0] = (ULONG) fullClassName.MaximumLength;
|
|
dumpCount = 1;
|
|
goto KbdCreateClassObjectExit;
|
|
}
|
|
|
|
//
|
|
// Do buffered I/O. I.e., the I/O system will copy to/from user data
|
|
// from/to a system buffer.
|
|
//
|
|
|
|
(*ClassDeviceObject)->Flags |= DO_BUFFERED_IO;
|
|
deviceExtension =
|
|
(PDEVICE_EXTENSION)(*ClassDeviceObject)->DeviceExtension;
|
|
*deviceExtension = *TmpDeviceExtension;
|
|
|
|
deviceExtension->Self = *ClassDeviceObject;
|
|
IoInitializeRemoveLock (&deviceExtension->RemoveLock, KEYBOARD_POOL_TAG, 0, 0);
|
|
|
|
//
|
|
// Initialize spin lock for critical sections.
|
|
//
|
|
KeInitializeSpinLock (&deviceExtension->SpinLock);
|
|
|
|
//
|
|
// Initialize the read queue
|
|
//
|
|
InitializeListHead (&deviceExtension->ReadQueue);
|
|
|
|
//
|
|
// No trusted subsystem has sent us an open yet.
|
|
//
|
|
|
|
deviceExtension->TrustedSubsystemCount = 0;
|
|
|
|
//
|
|
// Allocate the ring buffer for the keyboard class input data.
|
|
//
|
|
|
|
deviceExtension->InputData =
|
|
ExAllocatePool(
|
|
NonPagedPool,
|
|
deviceExtension->KeyboardAttributes.InputDataQueueLength
|
|
);
|
|
|
|
if (!deviceExtension->InputData) {
|
|
|
|
//
|
|
// Could not allocate memory for the keyboard class data queue.
|
|
//
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdCreateClassObject: Could not allocate input data queue for %ws\n",
|
|
FullDeviceName
|
|
));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
//
|
|
// Log an error.
|
|
//
|
|
|
|
errorCode = KBDCLASS_NO_BUFFER_ALLOCATED;
|
|
uniqueErrorValue = KEYBOARD_ERROR_VALUE_BASE + 20;
|
|
goto KbdCreateClassObjectExit;
|
|
}
|
|
|
|
//
|
|
// Initialize keyboard class input data queue.
|
|
//
|
|
|
|
KbdInitializeDataQueue((PVOID)deviceExtension);
|
|
|
|
KbdCreateClassObjectExit:
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
|
|
//
|
|
// Some part of the initialization failed. Log an error, and
|
|
// clean up the resources for the failed part of the initialization.
|
|
//
|
|
RtlFreeUnicodeString (&fullClassName);
|
|
*FullDeviceName = NULL;
|
|
|
|
if (errorCode != STATUS_SUCCESS) {
|
|
KeyboardClassLogError (
|
|
(*ClassDeviceObject == NULL) ?
|
|
(PVOID) DriverObject : (PVOID) *ClassDeviceObject,
|
|
errorCode,
|
|
uniqueErrorValue,
|
|
status,
|
|
dumpCount,
|
|
dumpData,
|
|
0);
|
|
}
|
|
|
|
if ((deviceExtension) && (deviceExtension->InputData)) {
|
|
ExFreePool (deviceExtension->InputData);
|
|
deviceExtension->InputData = NULL;
|
|
}
|
|
if (*ClassDeviceObject) {
|
|
IoDeleteDevice(*ClassDeviceObject);
|
|
*ClassDeviceObject = NULL;
|
|
}
|
|
}
|
|
|
|
KbdPrint((1,"KBDCLASS-KbdCreateClassObject: exit\n"));
|
|
|
|
return(status);
|
|
}
|
|
|
|
#if DBG
|
|
VOID
|
|
KbdDebugPrint(
|
|
ULONG DebugPrintLevel,
|
|
PCCHAR DebugMessage,
|
|
...
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Debug print routine.
|
|
|
|
Arguments:
|
|
|
|
Debug print level between 0 and 3, with 3 being the most verbose.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, DebugMessage);
|
|
|
|
if (DebugPrintLevel <= Globals.Debug) {
|
|
|
|
char buffer[256];
|
|
|
|
(VOID) vsprintf(buffer, DebugMessage, ap);
|
|
|
|
DbgPrint(buffer);
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
KbdDeterminePortsServiced(
|
|
IN PUNICODE_STRING BasePortName,
|
|
IN OUT PULONG NumberPortsServiced
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads the DEVICEMAP portion of the registry to determine
|
|
how many ports the class driver is to service. Depending on the
|
|
value of DeviceExtension->ConnectOneClassToOnePort, the class driver
|
|
will eventually create one device object per port device serviced, or
|
|
one class device object that connects to multiple port device objects.
|
|
|
|
Assumptions:
|
|
|
|
1. If the base device name for the class driver is "KeyboardClass",
|
|
^^^^^
|
|
then the port drivers it can service are found under the
|
|
"KeyboardPort" subkey in the DEVICEMAP portion of the registry.
|
|
^^^^
|
|
|
|
2. The port device objects are created with suffixes in strictly
|
|
ascending order, starting with suffix 0. E.g.,
|
|
\Device\KeyboardPort0 indicates the first keyboard port device,
|
|
\Device\KeyboardPort1 the second, and so on. There are no gaps
|
|
in the list.
|
|
|
|
3. If ConnectOneClassToOnePort is non-zero, there is a 1:1
|
|
correspondence between class device objects and port device
|
|
objects. I.e., \Device\KeyboardClass0 will connect to
|
|
\Device\KeyboardPort0, \Device\KeyboardClass1 to
|
|
\Device\KeyboardPort1, and so on.
|
|
|
|
4. If ConnectOneClassToOnePort is zero, there is a 1:many
|
|
correspondence between class device objects and port device
|
|
objects. I.e., \Device\KeyboardClass0 will connect to
|
|
\Device\KeyboardPort0, and \Device\KeyboardPort1, and so on.
|
|
|
|
|
|
Note that for Product 1, the Raw Input Thread (Windows USER) will
|
|
only deign to open and read from one keyboard device. Hence, it is
|
|
safe to make simplifying assumptions because the driver is basically
|
|
providing much more functionality than the RIT will use.
|
|
|
|
Arguments:
|
|
|
|
BasePortName - Pointer to the Unicode string that is the base path name
|
|
for the port device.
|
|
|
|
NumberPortsServiced - Pointer to storage that will receive the
|
|
number of ports this class driver should service.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status from the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PRTL_QUERY_REGISTRY_TABLE registryTable = NULL;
|
|
USHORT queriesPlusOne = 2;
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Initialize the result.
|
|
//
|
|
|
|
*NumberPortsServiced = 0;
|
|
|
|
//
|
|
// Allocate the Rtl query table.
|
|
//
|
|
|
|
registryTable = ExAllocatePool(
|
|
PagedPool,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * queriesPlusOne
|
|
);
|
|
|
|
if (!registryTable) {
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdDeterminePortsServiced: Couldn't allocate table for Rtl query\n"
|
|
));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory(
|
|
registryTable,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * queriesPlusOne
|
|
);
|
|
|
|
//
|
|
// Set things up so that KbdDeviceMapQueryCallback will be
|
|
// called once for every value in the keyboard port section
|
|
// of the registry's hardware devicemap.
|
|
//
|
|
|
|
registryTable[0].QueryRoutine = KbdDeviceMapQueryCallback;
|
|
registryTable[0].Name = NULL;
|
|
|
|
status = RtlQueryRegistryValues(
|
|
RTL_REGISTRY_DEVICEMAP | RTL_REGISTRY_OPTIONAL,
|
|
BasePortName->Buffer,
|
|
registryTable,
|
|
NumberPortsServiced,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KbdDeterminePortsServiced: RtlQueryRegistryValues failed with 0x%x\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
ExFreePool(registryTable);
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
NTSTATUS
|
|
KbdDeviceMapQueryCallback(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the callout routine specified in a call to
|
|
RtlQueryRegistryValues. It increments the value pointed
|
|
to by the Context parameter.
|
|
|
|
Arguments:
|
|
|
|
ValueName - Unused.
|
|
|
|
ValueType - Unused.
|
|
|
|
ValueData - Unused.
|
|
|
|
ValueLength - Unused.
|
|
|
|
Context - Pointer to a count of the number of times this
|
|
routine has been called. This is the number of ports
|
|
the class driver needs to service.
|
|
|
|
EntryContext - Unused.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status from the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
*(PULONG)Context += 1;
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
NTSTATUS
|
|
KbdEnableDisablePort(
|
|
IN BOOLEAN EnableFlag,
|
|
IN PIRP Irp,
|
|
IN PDEVICE_EXTENSION Port,
|
|
IN PFILE_OBJECT * File
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends an enable or a disable request to the port driver.
|
|
The legacy port drivers require an enable or disable ioctl, while the
|
|
plug and play drivers require merely a create.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
EnableFlag - If TRUE, send an ENABLE request; otherwise, send DISABLE.
|
|
|
|
PortIndex - Index into the PortDeviceObjectList[] for the current
|
|
enable/disable request.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
IO_STATUS_BLOCK ioStatus;
|
|
UNICODE_STRING name = {0,0,0};
|
|
PDEVICE_OBJECT device = NULL;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PWCHAR buffer = NULL;
|
|
ULONG bufferLength = 0;
|
|
PIO_STACK_LOCATION stack;
|
|
|
|
PAGED_CODE ();
|
|
|
|
KbdPrint((2,"KBDCLASS-KbdEnableDisablePort: enter\n"));
|
|
|
|
//
|
|
// Create notification event object to be used to signal the
|
|
// request completion.
|
|
//
|
|
|
|
if ((Port->TrueClassDevice == Port->Self) && (Port->PnP)) {
|
|
|
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
|
stack = IoGetNextIrpStackLocation (Irp);
|
|
|
|
if (EnableFlag) {
|
|
//
|
|
// Since there is no grand master there could not have been a
|
|
// create file against the FDO before it was started. Therefore
|
|
// the only time we would enable is during a create and not a
|
|
// start as we might with another FDO attached to an already open
|
|
// grand master.
|
|
//
|
|
ASSERT (IRP_MJ_CREATE == stack->MajorFunction);
|
|
|
|
} else {
|
|
if (IRP_MJ_CLOSE != stack->MajorFunction) {
|
|
//
|
|
// We are disabling. This could be because the device was
|
|
// closed, or because the device was removed out from
|
|
// underneath us.
|
|
//
|
|
ASSERT (IRP_MJ_PNP == stack->MajorFunction);
|
|
ASSERT ((IRP_MN_REMOVE_DEVICE == stack->MinorFunction) ||
|
|
(IRP_MN_STOP_DEVICE == stack->MinorFunction));
|
|
stack->MajorFunction = IRP_MJ_CLOSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Either way we need only pass the Irp down without mucking with the
|
|
// file object.
|
|
//
|
|
status = KeyboardSendIrpSynchronously (Port->TopPort, Irp, FALSE);
|
|
|
|
} else if (!Port->PnP) {
|
|
Port->Enabled = EnableFlag;
|
|
|
|
//
|
|
// We have here an old style Port Object. Therefore we send it the
|
|
// old style internal IOCTLs of ENABLE and DISABLE, and not the new
|
|
// style of passing on a create and close.
|
|
//
|
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
|
stack = IoGetNextIrpStackLocation (Irp);
|
|
|
|
stack->Parameters.DeviceIoControl.OutputBufferLength = 0;
|
|
stack->Parameters.DeviceIoControl.InputBufferLength = 0;
|
|
stack->Parameters.DeviceIoControl.IoControlCode
|
|
= EnableFlag ? IOCTL_INTERNAL_KEYBOARD_ENABLE
|
|
: IOCTL_INTERNAL_KEYBOARD_DISABLE;
|
|
stack->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
|
|
stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
|
|
status = KeyboardSendIrpSynchronously (Port->TopPort, Irp, FALSE);
|
|
|
|
} else {
|
|
//
|
|
// We are dealing with a plug and play port and we have a Grand
|
|
// Master.
|
|
//
|
|
ASSERT (Port->TrueClassDevice == Globals.GrandMaster->Self);
|
|
|
|
//
|
|
// Therefore we need to substitute the given file object for a new
|
|
// one for use with each individual ports.
|
|
// For enable, we need to create this file object against the given
|
|
// port and then hand it back in the File parameter, or for disable,
|
|
// deref the File parameter and free that file object.
|
|
//
|
|
// Of course, there must be storage for a file pointer pointed to by
|
|
// the File parameter.
|
|
//
|
|
ASSERT (NULL != File);
|
|
|
|
if (EnableFlag) {
|
|
|
|
ASSERT (NULL == *File);
|
|
|
|
//
|
|
// The following long list of rigamaroll translates into
|
|
// sending the lower driver a create file IRP and creating a
|
|
// NEW file object disjoint from the one given us in our create
|
|
// file routine.
|
|
//
|
|
// Normally we would just pass down the Create IRP we were
|
|
// given, but since we do not have a one to one correspondance of
|
|
// top device objects and port device objects.
|
|
// This means we need more file objects: one for each of the
|
|
// miriad of lower DOs.
|
|
//
|
|
|
|
bufferLength = 0;
|
|
status = IoGetDeviceProperty (
|
|
Port->PDO,
|
|
DevicePropertyPhysicalDeviceObjectName,
|
|
bufferLength,
|
|
buffer,
|
|
&bufferLength);
|
|
ASSERT (STATUS_BUFFER_TOO_SMALL == status);
|
|
|
|
buffer = ExAllocatePool (PagedPool, bufferLength);
|
|
|
|
if (NULL == buffer) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
status = IoGetDeviceProperty (
|
|
Port->PDO,
|
|
DevicePropertyPhysicalDeviceObjectName,
|
|
bufferLength,
|
|
buffer,
|
|
&bufferLength);
|
|
|
|
name.MaximumLength = (USHORT) bufferLength;
|
|
name.Length = (USHORT) bufferLength - sizeof (UNICODE_NULL);
|
|
name.Buffer = buffer;
|
|
|
|
status = IoGetDeviceObjectPointer (&name,
|
|
FILE_ALL_ACCESS,
|
|
File,
|
|
&device);
|
|
ExFreePool (buffer);
|
|
//
|
|
// Note, that this create will first go to ourselves since we
|
|
// are attached to this PDO stack. Therefore two things are
|
|
// noteworthy. This driver will receive another Create IRP
|
|
// (with a different file object) (not to the grand master but
|
|
// to one of the subordenant FDO's). The device object returned
|
|
// will be the subordenant FDO, which in this case is the "self"
|
|
// device object of this Port.
|
|
//
|
|
if (NT_SUCCESS (status)) {
|
|
PVOID tmpBuffer;
|
|
|
|
ASSERT (device == Port->Self);
|
|
|
|
if (NULL != Irp) {
|
|
//
|
|
// Set the indicators for this port device object.
|
|
// NB: The Grandmaster's device extension is initialized to
|
|
// zero, and the flags for indicator lights are flags, so
|
|
// this means that unless the RIUT has set the flags that
|
|
// IndicatorParameters will have no lights set.
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
|
stack = IoGetNextIrpStackLocation (Irp);
|
|
|
|
stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
stack->Parameters.DeviceIoControl.OutputBufferLength = 0;
|
|
stack->Parameters.DeviceIoControl.InputBufferLength =
|
|
sizeof (KEYBOARD_INDICATOR_PARAMETERS);
|
|
stack->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_KEYBOARD_SET_INDICATORS;
|
|
stack->FileObject = *File;
|
|
|
|
tmpBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
Irp->AssociatedIrp.SystemBuffer =
|
|
& Globals.GrandMaster->IndicatorParameters;
|
|
|
|
status = KeyboardSendIrpSynchronously (Port->TopPort, Irp, FALSE);
|
|
|
|
Irp->AssociatedIrp.SystemBuffer = tmpBuffer;
|
|
}
|
|
|
|
//
|
|
// Register for Target device removal events
|
|
//
|
|
ASSERT (NULL == Port->TargetNotifyHandle);
|
|
status = IoRegisterPlugPlayNotification (
|
|
EventCategoryTargetDeviceChange,
|
|
0, // No flags
|
|
*File,
|
|
Port->Self->DriverObject,
|
|
KeyboardClassPlugPlayNotification,
|
|
Port,
|
|
&Port->TargetNotifyHandle);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Getting rid of the handle is easy. Just deref the file.
|
|
//
|
|
ObDereferenceObject (*File);
|
|
*File = NULL;
|
|
}
|
|
|
|
}
|
|
KbdPrint((2,"KBDCLASS-KbdEnableDisablePort: exit\n"));
|
|
|
|
return (status);
|
|
}
|
|
|
|
VOID
|
|
KbdInitializeDataQueue (
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the input data queue. IRQL is raised to
|
|
DISPATCH_LEVEL to synchronize with StartIo, and the device object
|
|
spinlock is acquired.
|
|
|
|
Arguments:
|
|
|
|
Context - Supplies a pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
KbdPrint((3,"KBDCLASS-KbdInitializeDataQueue: enter\n"));
|
|
|
|
//
|
|
// Get address of device extension.
|
|
//
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION)Context;
|
|
|
|
//
|
|
// Acquire the spinlock to protect the input data
|
|
// queue and associated pointers.
|
|
//
|
|
|
|
KeAcquireSpinLock(&deviceExtension->SpinLock, &oldIrql);
|
|
|
|
//
|
|
// Initialize the input data queue.
|
|
//
|
|
|
|
deviceExtension->InputCount = 0;
|
|
deviceExtension->DataIn = deviceExtension->InputData;
|
|
deviceExtension->DataOut = deviceExtension->InputData;
|
|
|
|
deviceExtension->OkayToLogOverflow = TRUE;
|
|
|
|
//
|
|
// Release the spinlock and lower IRQL.
|
|
//
|
|
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, oldIrql);
|
|
|
|
KbdPrint((3,"KBDCLASS-KbdInitializeDataQueue: exit\n"));
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
KbdSendConnectRequest(
|
|
IN PDEVICE_EXTENSION ClassData,
|
|
IN PVOID ServiceCallback
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends a connect request to the port driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
ServiceCallback - Pointer to the class service callback routine.
|
|
|
|
PortIndex - The index into the PortDeviceObjectList[] for the current
|
|
connect request.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS status;
|
|
KEVENT event;
|
|
CONNECT_DATA connectData;
|
|
|
|
PAGED_CODE ();
|
|
|
|
KbdPrint((2,"KBDCLASS-KbdSendConnectRequest: enter\n"));
|
|
|
|
//
|
|
// Create notification event object to be used to signal the
|
|
// request completion.
|
|
//
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// Build the synchronous request to be sent to the port driver
|
|
// to perform the request. Allocate an IRP to issue the port internal
|
|
// device control connect call. The connect parameters are passed in
|
|
// the input buffer, and the keyboard attributes are copied back
|
|
// from the port driver directly into the class device extension.
|
|
//
|
|
|
|
connectData.ClassDeviceObject = ClassData->TrueClassDevice;
|
|
connectData.ClassService = ServiceCallback;
|
|
|
|
irp = IoBuildDeviceIoControlRequest(
|
|
IOCTL_INTERNAL_KEYBOARD_CONNECT,
|
|
ClassData->TopPort,
|
|
&connectData,
|
|
sizeof(CONNECT_DATA),
|
|
NULL,
|
|
0,
|
|
TRUE,
|
|
&event,
|
|
&ioStatus
|
|
);
|
|
|
|
//
|
|
// Call the port driver to perform the operation. If the returned status
|
|
// is PENDING, wait for the request to complete.
|
|
//
|
|
|
|
status = IoCallDriver(ClassData->TopPort, irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject(
|
|
&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
status = irp->IoStatus.Status;
|
|
} else {
|
|
|
|
//
|
|
// Ensure that the proper status value gets picked up.
|
|
//
|
|
|
|
ioStatus.Status = status;
|
|
}
|
|
|
|
KbdPrint((2,"KBDCLASS-KbdSendConnectRequest: exit\n"));
|
|
|
|
return(ioStatus.Status);
|
|
|
|
} // end KbdSendConnectRequest()
|
|
|
|
void
|
|
KeyboardClassRemoveDevice(
|
|
IN PDEVICE_EXTENSION Data
|
|
)
|
|
{
|
|
PFILE_OBJECT * file;
|
|
PPORT port;
|
|
PIRP waitWakeIrp;
|
|
PVOID notifyHandle;
|
|
BOOLEAN enabled;
|
|
|
|
//
|
|
// If this is a surprise remove or we got a remove w/out a surprise remove,
|
|
// then we need to clean up
|
|
//
|
|
waitWakeIrp = (PIRP)
|
|
InterlockedExchangePointer(&Data->WaitWakeIrp, NULL);
|
|
|
|
if (waitWakeIrp) {
|
|
IoCancelIrp(waitWakeIrp);
|
|
}
|
|
|
|
IoWMIRegistrationControl (Data->Self, WMIREG_ACTION_DEREGISTER);
|
|
|
|
if (Data->Started) {
|
|
//
|
|
// Stop the device without touching the hardware.
|
|
// MouStopDevice(Data, FALSE);
|
|
//
|
|
// NB sending down the enable disable does NOT touch the hardware
|
|
// it instead sends down a close file.
|
|
//
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
if (Globals.GrandMaster) {
|
|
if (0 < Globals.Opens) {
|
|
port = &Globals.AssocClassList[Data->UnitId];
|
|
ASSERT (port->Port == Data);
|
|
file = &(port->File);
|
|
enabled = port->Enabled;
|
|
port->Enabled = FALSE;
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
//
|
|
// ASSERT (NULL == Data->notifyHandle);
|
|
//
|
|
// If we have a grand master, that means we did the
|
|
// creation locally and registered for notification.
|
|
// we should have closed the file during
|
|
// TARGET_DEVICE_QUERY_REMOVE, but we will have not
|
|
// gotten rid of the notify handle.
|
|
//
|
|
// Of course if we receive a surprise removal then
|
|
// we should not have received the query cancel.
|
|
// In which case we should have received a
|
|
// TARGET_DEVICE_REMOVE_COMPLETE where we would have
|
|
// both closed the file and removed cleared the
|
|
// notify handle
|
|
//
|
|
// Either way the file should be closed by now.
|
|
//
|
|
ASSERT (!enabled);
|
|
// if (enabled) {
|
|
// status = MouEnableDisablePort (FALSE, Irp, Data, file);
|
|
// ASSERTMSG ("Could not close open port", NT_SUCCESS(status));
|
|
// }
|
|
|
|
notifyHandle = InterlockedExchangePointer (
|
|
&Data->TargetNotifyHandle,
|
|
NULL);
|
|
|
|
if (NULL != notifyHandle) {
|
|
IoUnregisterPlugPlayNotification (notifyHandle);
|
|
}
|
|
}
|
|
else {
|
|
ASSERT (!Globals.AssocClassList[Data->UnitId].Enabled);
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
}
|
|
}
|
|
else {
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
ASSERT (Data->TrueClassDevice == Data->Self);
|
|
ASSERT (Globals.ConnectOneClassToOnePort);
|
|
|
|
if (!Data->SurpriseRemoved) {
|
|
//
|
|
// If add device fails, then the buffer will be NULL
|
|
//
|
|
if (Data->SymbolicLinkName.Buffer != NULL) {
|
|
IoSetDeviceInterfaceState (&Data->SymbolicLinkName, FALSE);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Always drain the queue, no matter if we have received both a surprise
|
|
// remove and a remove
|
|
//
|
|
if (Data->PnP) {
|
|
//
|
|
// Empty the device I/O Queue
|
|
//
|
|
KeyboardClassCleanupQueue (Data->Self, Data, NULL);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardPnP (
|
|
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;
|
|
PDEVICE_EXTENSION trueClassData;
|
|
PIO_STACK_LOCATION stack;
|
|
NTSTATUS status, startStatus;
|
|
ULONG i;
|
|
PFILE_OBJECT * file;
|
|
UINT_PTR startInformation;
|
|
DEVICE_CAPABILITIES devCaps;
|
|
BOOLEAN enabled;
|
|
PPORT port;
|
|
PVOID notifyHandle;
|
|
KIRQL oldIrql;
|
|
KIRQL cancelIrql;
|
|
|
|
data = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
stack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
if(!data->PnP) {
|
|
//
|
|
// This irp was sent to the control device object, which knows not
|
|
// how to deal with this IRP. It is therefore an error.
|
|
//
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
trueClassData = (PDEVICE_EXTENSION) data->TrueClassDevice->DeviceExtension;
|
|
switch (stack->MinorFunction) {
|
|
case IRP_MN_START_DEVICE:
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
status = KeyboardSendIrpSynchronously (data->TopPort, Irp, TRUE);
|
|
|
|
if (NT_SUCCESS (status) && NT_SUCCESS (Irp->IoStatus.Status)) {
|
|
//
|
|
// As we are successfully now back from our start device
|
|
// we can do work.
|
|
//
|
|
// Get the caps of the device. Save off pertinent information
|
|
// before mucking about w/the irp
|
|
//
|
|
startStatus = Irp->IoStatus.Status;
|
|
startInformation = Irp->IoStatus.Information;
|
|
|
|
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
RtlZeroMemory(&devCaps, sizeof (DEVICE_CAPABILITIES));
|
|
devCaps.Size = sizeof (DEVICE_CAPABILITIES);
|
|
devCaps.Version = 1;
|
|
devCaps.Address = devCaps.UINumber = (ULONG)-1;
|
|
|
|
stack = IoGetNextIrpStackLocation (Irp);
|
|
stack->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
|
|
stack->Parameters.DeviceCapabilities.Capabilities = &devCaps;
|
|
|
|
status = KeyboardSendIrpSynchronously (data->TopPort, Irp, FALSE);
|
|
|
|
if (NT_SUCCESS (status) && NT_SUCCESS (Irp->IoStatus.Status)) {
|
|
data->MinDeviceWakeState = devCaps.DeviceWake;
|
|
data->MinSystemWakeState = devCaps.SystemWake;
|
|
|
|
RtlCopyMemory (data->SystemToDeviceState,
|
|
devCaps.DeviceState,
|
|
sizeof(DEVICE_POWER_STATE) * PowerSystemHibernate);
|
|
} else {
|
|
ASSERTMSG ("Get Device caps Failed!\n", status);
|
|
}
|
|
|
|
//
|
|
// Set everything back to the way it was and continue with the start
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Status = startStatus;
|
|
Irp->IoStatus.Information = startInformation;
|
|
|
|
data->Started = TRUE;
|
|
|
|
if (WAITWAKE_SUPPORTED (data)) {
|
|
//
|
|
// register for the wait wake guid as well
|
|
//
|
|
data->WmiLibInfo.GuidCount = sizeof (KeyboardClassWmiGuidList) /
|
|
sizeof (WMIGUIDREGINFO);
|
|
ASSERT (2 == data->WmiLibInfo.GuidCount);
|
|
|
|
//
|
|
// See if the user has enabled wait wake for the device
|
|
//
|
|
KeyboardClassGetWaitWakeEnableState (data);
|
|
}
|
|
else {
|
|
data->WmiLibInfo.GuidCount = (sizeof (KeyboardClassWmiGuidList) /
|
|
sizeof (WMIGUIDREGINFO)) - 1;
|
|
ASSERT (1 == data->WmiLibInfo.GuidCount);
|
|
}
|
|
data->WmiLibInfo.GuidList = KeyboardClassWmiGuidList;
|
|
data->WmiLibInfo.QueryWmiRegInfo = KeyboardClassQueryWmiRegInfo;
|
|
data->WmiLibInfo.QueryWmiDataBlock = KeyboardClassQueryWmiDataBlock;
|
|
data->WmiLibInfo.SetWmiDataBlock = KeyboardClassSetWmiDataBlock;
|
|
data->WmiLibInfo.SetWmiDataItem = KeyboardClassSetWmiDataItem;
|
|
data->WmiLibInfo.ExecuteWmiMethod = NULL;
|
|
data->WmiLibInfo.WmiFunctionControl = NULL;
|
|
|
|
IoWMIRegistrationControl(data->Self,
|
|
WMIREG_ACTION_REGISTER
|
|
);
|
|
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
if (Globals.GrandMaster) {
|
|
if (0 < Globals.Opens) {
|
|
port = &Globals.AssocClassList[data->UnitId];
|
|
ASSERT (port->Port == data);
|
|
file = &port->File;
|
|
enabled = port->Enabled;
|
|
port->Enabled = TRUE;
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
if (!enabled) {
|
|
status = KbdEnableDisablePort (TRUE, Irp, data, file);
|
|
if (!NT_SUCCESS (status)) {
|
|
port->Enabled = FALSE;
|
|
// ASSERT (Globals.AssocClassList[data->UnitId].Enabled);
|
|
} else {
|
|
//
|
|
// This is not the first kb to start, make sure its
|
|
// lights are set according to the indicators on the
|
|
// other kbs
|
|
//
|
|
PVOID startBuffer;
|
|
|
|
stack = IoGetNextIrpStackLocation (Irp);
|
|
stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
stack->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_KEYBOARD_SET_INDICATORS;
|
|
|
|
stack->FileObject = *file;
|
|
stack->Parameters.DeviceIoControl.OutputBufferLength = 0;
|
|
stack->Parameters.DeviceIoControl.InputBufferLength =
|
|
sizeof (KEYBOARD_INDICATOR_PARAMETERS);
|
|
|
|
startStatus = Irp->IoStatus.Status;
|
|
startInformation = Irp->IoStatus.Information;
|
|
startBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->AssociatedIrp.SystemBuffer =
|
|
&Globals.GrandMaster->IndicatorParameters;
|
|
|
|
status =
|
|
KeyboardSendIrpSynchronously (data->TopPort,
|
|
Irp,
|
|
FALSE);
|
|
|
|
//
|
|
// We don't care if we succeeded or not...
|
|
// set everything back to the way it was and
|
|
// continue with the start
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Status = startStatus;
|
|
Irp->IoStatus.Information = startInformation;
|
|
Irp->AssociatedIrp.SystemBuffer = startBuffer;
|
|
}
|
|
}
|
|
} else {
|
|
ASSERT (!Globals.AssocClassList[data->UnitId].Enabled);
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
}
|
|
} else {
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
ASSERT (data->Self == data->TrueClassDevice);
|
|
status=IoSetDeviceInterfaceState(&data->SymbolicLinkName, TRUE);
|
|
}
|
|
|
|
//
|
|
// Start up the Wait Wake Engine if required.
|
|
//
|
|
if (SHOULD_SEND_WAITWAKE (data)) {
|
|
KeyboardClassCreateWaitWakeIrp (data);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
//
|
|
// Do what ever
|
|
//
|
|
|
|
//
|
|
// Stop Device touching the hardware KbdStopDevice(data, TRUE);
|
|
//
|
|
if (data->Started) {
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
if (Globals.GrandMaster) {
|
|
if (0 < Globals.Opens) {
|
|
port = &Globals.AssocClassList[data->UnitId];
|
|
ASSERT (port->Port == data);
|
|
file = &(port->File);
|
|
enabled = port->Enabled;
|
|
port->Enabled = FALSE;
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
if (enabled) {
|
|
notifyHandle = InterlockedExchangePointer (
|
|
&data->TargetNotifyHandle,
|
|
NULL);
|
|
|
|
if (NULL != notifyHandle) {
|
|
IoUnregisterPlugPlayNotification (notifyHandle);
|
|
}
|
|
|
|
status = KbdEnableDisablePort (FALSE, Irp, data, file);
|
|
ASSERTMSG ("Could not close open port", NT_SUCCESS(status));
|
|
} else {
|
|
ASSERT (NULL == data->TargetNotifyHandle);
|
|
}
|
|
} else {
|
|
ASSERT (!Globals.AssocClassList[data->UnitId].Enabled);
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
}
|
|
} else {
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
}
|
|
}
|
|
|
|
data->Started = FALSE;
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = IoCallDriver (data->TopPort, Irp);
|
|
break;
|
|
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
//
|
|
// The PlugPlay system has dictacted the removal of this device.
|
|
//
|
|
data->SurpriseRemoved = TRUE;
|
|
|
|
//
|
|
// If add device fails, then the buffer will be NULL
|
|
//
|
|
if (data->SymbolicLinkName.Buffer != NULL) {
|
|
IoSetDeviceInterfaceState (&data->SymbolicLinkName, FALSE);
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = IoCallDriver (data->TopPort, 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.)
|
|
//
|
|
KeyboardClassRemoveDevice (data);
|
|
|
|
//
|
|
// Here if we had any outstanding requests in a personal queue we should
|
|
// complete them all now.
|
|
//
|
|
// Note, the device is guarenteed stopped, so we cannot send it any non-
|
|
// PNP IRPS.
|
|
//
|
|
|
|
//
|
|
// Send on the remove IRP
|
|
//
|
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
|
status = IoCallDriver (data->TopPort, Irp);
|
|
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
if (Globals.GrandMaster) {
|
|
ASSERT (Globals.GrandMaster->Self == data->TrueClassDevice);
|
|
//
|
|
// We must remove ourself from the Assoc List
|
|
//
|
|
|
|
if (1 < Globals.NumAssocClass) {
|
|
ASSERT (Globals.AssocClassList[data->UnitId].Port == data);
|
|
|
|
Globals.AssocClassList[data->UnitId].Free = TRUE;
|
|
Globals.AssocClassList[data->UnitId].File = NULL;
|
|
Globals.AssocClassList[data->UnitId].Port = NULL;
|
|
|
|
} else {
|
|
ASSERT (1 == Globals.NumAssocClass);
|
|
Globals.NumAssocClass = 0;
|
|
ExFreePool (Globals.AssocClassList);
|
|
Globals.AssocClassList = NULL;
|
|
}
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
} else {
|
|
//
|
|
// We are removing the one and only port associated with this class
|
|
// device object.
|
|
//
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
ASSERT (data->TrueClassDevice == data->Self);
|
|
ASSERT (Globals.ConnectOneClassToOnePort);
|
|
}
|
|
|
|
IoReleaseRemoveLockAndWait (&data->RemoveLock, Irp);
|
|
|
|
IoDetachDevice (data->TopPort);
|
|
|
|
//
|
|
// Clean up memory
|
|
//
|
|
RtlFreeUnicodeString (&data->SymbolicLinkName);
|
|
ExFreePool (data->InputData);
|
|
IoDeleteDevice (data->Self);
|
|
|
|
return status;
|
|
|
|
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
|
|
|
//
|
|
// Set the not disableable bit on the way down so that the stack below
|
|
// has a chance to clear it
|
|
//
|
|
if (data->AllowDisable == FALSE) {
|
|
|
|
(PNP_DEVICE_STATE) Irp->IoStatus.Information |=
|
|
PNP_DEVICE_NOT_DISABLEABLE;
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// drop through to the default case
|
|
// || ||
|
|
// \/ \/
|
|
//
|
|
}
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
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:
|
|
default:
|
|
//
|
|
// Here the filter driver might modify the behavior of these IRPS
|
|
// Please see PlugPlay documentation for use of these IRPs.
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
|
status = IoCallDriver (data->TopPort, Irp);
|
|
break;
|
|
}
|
|
|
|
IoReleaseRemoveLock (&data->RemoveLock, Irp);
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
KeyboardClassLogError(
|
|
PVOID Object,
|
|
ULONG ErrorCode,
|
|
ULONG UniqueErrorValue,
|
|
NTSTATUS FinalStatus,
|
|
ULONG DumpCount,
|
|
ULONG *DumpData,
|
|
UCHAR MajorFunction
|
|
)
|
|
{
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
ULONG i;
|
|
|
|
errorLogEntry = (PIO_ERROR_LOG_PACKET)
|
|
IoAllocateErrorLogEntry(
|
|
Object,
|
|
(UCHAR) (sizeof(IO_ERROR_LOG_PACKET) + (DumpCount * sizeof(ULONG)))
|
|
);
|
|
|
|
if (errorLogEntry != NULL) {
|
|
|
|
errorLogEntry->ErrorCode = ErrorCode;
|
|
errorLogEntry->DumpDataSize = (USHORT) (DumpCount * sizeof(ULONG));
|
|
errorLogEntry->SequenceNumber = 0;
|
|
errorLogEntry->MajorFunctionCode = MajorFunction;
|
|
errorLogEntry->IoControlCode = 0;
|
|
errorLogEntry->RetryCount = 0;
|
|
errorLogEntry->UniqueErrorValue = UniqueErrorValue;
|
|
errorLogEntry->FinalStatus = FinalStatus;
|
|
for (i = 0; i < DumpCount; i++)
|
|
errorLogEntry->DumpData[i] = DumpData[i];
|
|
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
KeyboardClassFindMorePorts (
|
|
PDRIVER_OBJECT DriverObject,
|
|
PVOID Context,
|
|
ULONG Count
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called from
|
|
serviced by the boot device drivers and then called again by the
|
|
IO system to find disk devices serviced by nonboot device drivers.
|
|
|
|
Arguments:
|
|
|
|
DriverObject
|
|
Context -
|
|
Count - Used to determine if this is the first or second time called.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION deviceExtension = NULL;
|
|
PDEVICE_OBJECT classDeviceObject = NULL;
|
|
ULONG dumpData[DUMP_COUNT];
|
|
ULONG i;
|
|
ULONG numPorts;
|
|
ULONG successfulCreates;
|
|
UNICODE_STRING basePortName;
|
|
UNICODE_STRING fullPortName;
|
|
WCHAR basePortBuffer[NAME_MAX];
|
|
PWCHAR fullClassName = NULL;
|
|
PFILE_OBJECT file;
|
|
|
|
PAGED_CODE ();
|
|
|
|
fullPortName.MaximumLength = 0;
|
|
|
|
RtlZeroMemory(basePortBuffer, NAME_MAX * sizeof(WCHAR));
|
|
basePortName.Buffer = basePortBuffer;
|
|
basePortName.Length = 0;
|
|
basePortName.MaximumLength = NAME_MAX * sizeof(WCHAR);
|
|
|
|
//
|
|
// Set up the base device name for the associated port device.
|
|
// It is the same as the base class name, with "Class" replaced
|
|
// by "Port".
|
|
//
|
|
RtlCopyUnicodeString(&basePortName, &Globals.BaseClassName);
|
|
basePortName.Length -= (sizeof(L"Class") - sizeof(UNICODE_NULL));
|
|
RtlAppendUnicodeToString(&basePortName, L"Port");
|
|
|
|
//
|
|
// Set up space for the full device object name for the ports.
|
|
//
|
|
RtlInitUnicodeString(&fullPortName, NULL);
|
|
|
|
fullPortName.MaximumLength = sizeof(L"\\Device\\")
|
|
+ basePortName.Length
|
|
+ sizeof (UNICODE_NULL);
|
|
|
|
fullPortName.Buffer = ExAllocatePool(PagedPool,
|
|
fullPortName.MaximumLength);
|
|
|
|
if (!fullPortName.Buffer) {
|
|
|
|
KbdPrint((
|
|
1,
|
|
"KBDCLASS-KeyboardClassInitialize: Couldn't allocate string for port device object name\n"
|
|
));
|
|
|
|
dumpData[0] = (ULONG) fullPortName.MaximumLength;
|
|
KeyboardClassLogError (DriverObject,
|
|
KBDCLASS_INSUFFICIENT_RESOURCES,
|
|
KEYBOARD_ERROR_VALUE_BASE + 8,
|
|
STATUS_UNSUCCESSFUL,
|
|
1,
|
|
dumpData,
|
|
0);
|
|
|
|
goto KeyboardFindMorePortsExit;
|
|
|
|
}
|
|
|
|
RtlZeroMemory(fullPortName.Buffer, fullPortName.MaximumLength);
|
|
RtlAppendUnicodeToString(&fullPortName, L"\\Device\\");
|
|
RtlAppendUnicodeToString(&fullPortName, basePortName.Buffer);
|
|
RtlAppendUnicodeToString(&fullPortName, L"0");
|
|
|
|
KbdDeterminePortsServiced(&basePortName, &numPorts);
|
|
|
|
//
|
|
// Set up the class device object(s) to handle the associated
|
|
// port devices.
|
|
//
|
|
|
|
for (i = Globals.NumberLegacyPorts, successfulCreates = 0;
|
|
((i < Globals.PortsServiced) && (i < numPorts));
|
|
i++) {
|
|
|
|
//
|
|
// Append the suffix to the device object name string. E.g., turn
|
|
// \Device\PointerClass into \Device\PointerClass0. Then attempt
|
|
// to create the device object. If the device object already
|
|
// exists increment the suffix and try again.
|
|
//
|
|
|
|
fullPortName.Buffer[(fullPortName.Length / sizeof(WCHAR)) - 1]
|
|
= L'0' + (WCHAR) i;
|
|
|
|
//
|
|
// Create the class device object.
|
|
//
|
|
status = KbdCreateClassObject (DriverObject,
|
|
&Globals.InitExtension,
|
|
&classDeviceObject,
|
|
&fullClassName,
|
|
TRUE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
KeyboardClassLogError (DriverObject,
|
|
KBDCLASS_INSUFFICIENT_RESOURCES,
|
|
KEYBOARD_ERROR_VALUE_BASE + 8,
|
|
status,
|
|
0,
|
|
NULL,
|
|
0);
|
|
continue;
|
|
}
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION)classDeviceObject->DeviceExtension;
|
|
deviceExtension->PnP = FALSE;
|
|
|
|
//
|
|
// Connect to the port device.
|
|
//
|
|
status = IoGetDeviceObjectPointer (&fullPortName,
|
|
FILE_READ_ATTRIBUTES,
|
|
&file,
|
|
&deviceExtension->TopPort);
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
// ISSUE: log error
|
|
KeyboardClassDeleteLegacyDevice (deviceExtension);
|
|
continue;
|
|
}
|
|
|
|
classDeviceObject->StackSize = 1 + deviceExtension->TopPort->StackSize;
|
|
status = KeyboardAddDeviceEx (deviceExtension, fullClassName, file);
|
|
classDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
if (fullClassName) {
|
|
ExFreePool (fullClassName);
|
|
fullClassName = NULL;
|
|
}
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
if (Globals.GrandMaster == NULL) {
|
|
if (deviceExtension->File) {
|
|
file = deviceExtension->File;
|
|
deviceExtension->File = NULL;
|
|
}
|
|
}
|
|
else {
|
|
PPORT port;
|
|
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
|
|
file = Globals.AssocClassList[deviceExtension->UnitId].File;
|
|
Globals.AssocClassList[deviceExtension->UnitId].File = NULL;
|
|
Globals.AssocClassList[deviceExtension->UnitId].Free = TRUE;
|
|
Globals.AssocClassList[deviceExtension->UnitId].Port = NULL;
|
|
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
}
|
|
|
|
if (file) {
|
|
ObDereferenceObject (file);
|
|
}
|
|
|
|
KeyboardClassDeleteLegacyDevice (deviceExtension);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We want to only add it to our list if everything went alright
|
|
//
|
|
InsertTailList (&Globals.LegacyDeviceList, &deviceExtension->Link);
|
|
successfulCreates++;
|
|
} // for
|
|
Globals.NumberLegacyPorts = i;
|
|
|
|
KeyboardFindMorePortsExit:
|
|
|
|
//
|
|
// Free the unicode strings.
|
|
//
|
|
|
|
if (fullPortName.MaximumLength != 0) {
|
|
ExFreePool(fullPortName.Buffer);
|
|
}
|
|
|
|
if (fullClassName) {
|
|
ExFreePool (fullClassName);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassEnableGlobalPort(
|
|
IN PDEVICE_EXTENSION Port,
|
|
IN BOOLEAN Enabled
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PPORT globalPort = NULL;
|
|
BOOLEAN enabled, testVal;
|
|
ULONG i;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ExAcquireFastMutex (&Globals.Mutex);
|
|
if (0 < Globals.Opens) {
|
|
for (i = 0; i < Globals.NumAssocClass; i++) {
|
|
if (! Globals.AssocClassList [i].Free) {
|
|
if (Globals.AssocClassList[i].Port == Port) {
|
|
globalPort = &Globals.AssocClassList [i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
ASSERTMSG ("What shall I do now?\n", (NULL != globalPort));
|
|
|
|
//
|
|
// This should never happen, globalPort should be in our list
|
|
//
|
|
if (globalPort == NULL) {
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
enabled = globalPort->Enabled;
|
|
globalPort->Enabled = Enabled;
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
|
|
//
|
|
// Check to see if the port should change state. If so, send the new state
|
|
// down the stack
|
|
//
|
|
if (Enabled != enabled) {
|
|
status = KbdEnableDisablePort (Enabled,
|
|
NULL,
|
|
Port,
|
|
&globalPort->File);
|
|
}
|
|
} else {
|
|
ExReleaseFastMutex (&Globals.Mutex);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassPlugPlayNotification(
|
|
IN PTARGET_DEVICE_REMOVAL_NOTIFICATION NotificationStructure,
|
|
IN PDEVICE_EXTENSION Port
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called as the result of recieving PlugPlay Notifications
|
|
as registered by the previous call to IoRegisterPlugPlayNotification.
|
|
|
|
Currently this should only occur for Target Device Notifications
|
|
|
|
Arguments:
|
|
|
|
NotificationStructure - what happened.
|
|
Port - The Fdo to which things happened.
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PVOID notify = NULL;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (Globals.GrandMaster->Self == Port->TrueClassDevice);
|
|
|
|
if (IsEqualGUID ((LPGUID) &(NotificationStructure->Event),
|
|
(LPGUID) &GUID_TARGET_DEVICE_QUERY_REMOVE)) {
|
|
|
|
//
|
|
// Our port device object will soon be receiving a query remove.
|
|
// Before that query remove will actually be sent to the device object
|
|
// stack itself the plug and play subsystem will send those registered
|
|
// for target device notification the message first.
|
|
//
|
|
|
|
//
|
|
// What we should do is now close the handle.
|
|
// Because if we do not the query remove will fail before it ever
|
|
// gets to the IRP_MJ_PNP IRP_MN_QUERY_REMOVE stage, as the PlugPlay
|
|
// system fails before it is sent based on there being an open handle
|
|
// to the device.
|
|
//
|
|
// DbgPrint ("Keyboard QUERY Remove\n");
|
|
// DbgBreakPoint();
|
|
|
|
status = KeyboardClassEnableGlobalPort (Port, FALSE);
|
|
|
|
} else if(IsEqualGUID ((LPGUID)&(NotificationStructure->Event),
|
|
(LPGUID)&GUID_TARGET_DEVICE_REMOVE_COMPLETE)) {
|
|
|
|
//
|
|
// Here the device has disappeared.
|
|
//
|
|
// DbgPrint ("Keyboard Remove Complete\n");
|
|
// DbgBreakPoint();
|
|
|
|
notify = InterlockedExchangePointer (&Port->TargetNotifyHandle,
|
|
NULL);
|
|
|
|
if (NULL != notify) {
|
|
//
|
|
// Deregister
|
|
//
|
|
IoUnregisterPlugPlayNotification (notify);
|
|
|
|
status = KeyboardClassEnableGlobalPort (Port, FALSE);
|
|
}
|
|
|
|
} else if(IsEqualGUID ((LPGUID)&(NotificationStructure->Event),
|
|
(LPGUID)&GUID_TARGET_DEVICE_REMOVE_CANCELLED)) {
|
|
|
|
//
|
|
// The query remove has been revoked.
|
|
// Reopen the device.
|
|
//
|
|
// DbgPrint ("Keyboard Remove Complete\n");
|
|
// DbgBreakPoint();
|
|
|
|
notify = InterlockedExchangePointer (&Port->TargetNotifyHandle,
|
|
NULL);
|
|
|
|
if (NULL != notify) {
|
|
//
|
|
// Deregister
|
|
//
|
|
IoUnregisterPlugPlayNotification (notify);
|
|
|
|
//
|
|
// If the notify handle is no longer around then this device must
|
|
// have been removed, so there is no point trying to create again.
|
|
//
|
|
status = KeyboardClassEnableGlobalPort (Port, TRUE);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
KeyboardClassPoRequestComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR MinorFunction,
|
|
IN POWER_STATE PowerState,
|
|
IN PVOID Context,
|
|
IN PIO_STATUS_BLOCK IoStatus
|
|
);
|
|
|
|
NTSTATUS
|
|
KeyboardClassPowerComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
KeyboardClassWWPowerUpComplete(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR MinorFunction,
|
|
IN POWER_STATE PowerState,
|
|
IN PVOID Context,
|
|
IN PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Catch the Wait wake Irp on its way back.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION data = Context;
|
|
POWER_STATE powerState;
|
|
NTSTATUS status;
|
|
PKEYBOARD_WORK_ITEM_DATA itemData;
|
|
|
|
ASSERT (MinorFunction == IRP_MN_SET_POWER);
|
|
|
|
if (data->WaitWakeEnabled) {
|
|
//
|
|
// We cannot call CreateWaitWake from this completion routine,
|
|
// as it is a paged function.
|
|
//
|
|
itemData = (PKEYBOARD_WORK_ITEM_DATA)
|
|
ExAllocatePool (NonPagedPool, sizeof (KEYBOARD_WORK_ITEM_DATA));
|
|
|
|
if (NULL != itemData) {
|
|
itemData->Item = IoAllocateWorkItem(data->Self);
|
|
if (itemData->Item == NULL) {
|
|
ExFreePool(itemData);
|
|
goto CreateWaitWakeWorkerError;
|
|
}
|
|
|
|
itemData->Data = data;
|
|
itemData->Irp = NULL;
|
|
status = IoAcquireRemoveLock (&data->RemoveLock, itemData);
|
|
if (NT_SUCCESS(status)) {
|
|
IoQueueWorkItem (itemData->Item,
|
|
KeyboardClassCreateWaitWakeIrpWorker,
|
|
DelayedWorkQueue,
|
|
itemData);
|
|
}
|
|
else {
|
|
//
|
|
// The device has been removed
|
|
//
|
|
IoFreeWorkItem (itemData->Item);
|
|
ExFreePool (itemData);
|
|
}
|
|
} else {
|
|
CreateWaitWakeWorkerError:
|
|
//
|
|
// Well, we dropped the WaitWake.
|
|
//
|
|
// Print a warning to the debugger, and log an error.
|
|
//
|
|
DbgPrint ("KbdClass: WARNING: Failed alloc pool -> no WW Irp\n");
|
|
|
|
KeyboardClassLogError (data->Self,
|
|
KBDCLASS_NO_RESOURCES_FOR_WAITWAKE,
|
|
2,
|
|
STATUS_INSUFFICIENT_RESOURCES,
|
|
0,
|
|
NULL,
|
|
0);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
KeyboardClassWaitWakeComplete(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR MinorFunction,
|
|
IN POWER_STATE PowerState,
|
|
IN PVOID Context,
|
|
IN PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Catch the Wait wake Irp on its way back.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION data = Context;
|
|
POWER_STATE powerState;
|
|
NTSTATUS status;
|
|
PKEYBOARD_WORK_ITEM_DATA itemData;
|
|
|
|
ASSERT (MinorFunction == IRP_MN_WAIT_WAKE);
|
|
//
|
|
// PowerState.SystemState is undefined when the WW irp has been completed
|
|
//
|
|
// ASSERT (PowerState.SystemState == PowerSystemWorking);
|
|
|
|
if (InterlockedExchangePointer(&data->ExtraWaitWakeIrp, NULL)) {
|
|
ASSERT(IoStatus->Status == STATUS_INVALID_DEVICE_STATE);
|
|
} else {
|
|
InterlockedExchangePointer(&data->WaitWakeIrp, NULL);
|
|
}
|
|
|
|
switch (IoStatus->Status) {
|
|
case STATUS_SUCCESS:
|
|
KbdPrint((1, "KbdClass: Wake irp was completed succeSSfully.\n"));
|
|
|
|
//
|
|
// We need to request a set power to power up the device.
|
|
//
|
|
powerState.DeviceState = PowerDeviceD0;
|
|
status = PoRequestPowerIrp(
|
|
data->PDO,
|
|
IRP_MN_SET_POWER,
|
|
powerState,
|
|
KeyboardClassWWPowerUpComplete,
|
|
Context,
|
|
NULL);
|
|
|
|
//
|
|
// We do not notify the system that a user is present because:
|
|
// 1 Win9x doesn't do this and we must maintain compatibility with it
|
|
// 2 The USB PIX4 motherboards sends a wait wake event every time the
|
|
// machine wakes up, no matter if this device woke the machine or not
|
|
//
|
|
// If we incorrectly notify the system a user is present, the following
|
|
// will occur:
|
|
// 1 The monitor will be turned on
|
|
// 2 We will prevent the machine from transitioning from standby
|
|
// (to PowerSystemWorking) to hibernate
|
|
//
|
|
// If a user is truly present, we will receive input in the service
|
|
// callback and we will notify the system at that time.
|
|
//
|
|
// PoSetSystemState (ES_USER_PRESENT);
|
|
|
|
// fall through to the break
|
|
|
|
//
|
|
// We get a remove. We will not (obviously) send another wait wake
|
|
//
|
|
case STATUS_CANCELLED:
|
|
|
|
//
|
|
// This status code will be returned if the device is put into a power state
|
|
// in which we cannot wake the machine (hibernate is a good example). When
|
|
// the device power state is returned to D0, we will attempt to rearm wait wake
|
|
//
|
|
case STATUS_POWER_STATE_INVALID:
|
|
case STATUS_ACPI_POWER_REQUEST_FAILED:
|
|
|
|
//
|
|
// We failed the Irp because we already had one queued, or a lower driver in
|
|
// the stack failed it. Either way, don't do anything.
|
|
//
|
|
case STATUS_INVALID_DEVICE_STATE:
|
|
|
|
//
|
|
// Somehow someway we got two WWs down to the lower stack.
|
|
// Let's just don't worry about it.
|
|
//
|
|
case STATUS_DEVICE_BUSY:
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Something went wrong, disable the wait wake.
|
|
//
|
|
KdPrint(("KBDCLASS: wait wake irp failed with %x\n", IoStatus->Status));
|
|
KeyboardToggleWaitWake (data, FALSE);
|
|
}
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
KeyboardClassCheckWaitWakeEnabled(
|
|
IN PDEVICE_EXTENSION Data
|
|
)
|
|
{
|
|
KIRQL irql;
|
|
BOOLEAN enabled;
|
|
|
|
KeAcquireSpinLock (&Data->WaitWakeSpinLock, &irql);
|
|
enabled = Data->WaitWakeEnabled;
|
|
KeReleaseSpinLock (&Data->WaitWakeSpinLock, irql);
|
|
|
|
return enabled;
|
|
}
|
|
|
|
void
|
|
KeyboardClassCreateWaitWakeIrpWorker (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PKEYBOARD_WORK_ITEM_DATA ItemData
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
KeyboardClassCreateWaitWakeIrp (ItemData->Data);
|
|
IoReleaseRemoveLock (&ItemData->Data->RemoveLock, ItemData);
|
|
IoFreeWorkItem(ItemData->Item);
|
|
ExFreePool (ItemData);
|
|
}
|
|
|
|
BOOLEAN
|
|
KeyboardClassCreateWaitWakeIrp (
|
|
IN PDEVICE_EXTENSION Data
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Catch the Wait wake Irp on its way back.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
POWER_STATE powerState;
|
|
BOOLEAN success = TRUE;
|
|
NTSTATUS status;
|
|
PIRP waitWakeIrp;
|
|
|
|
PAGED_CODE ();
|
|
|
|
powerState.SystemState = Data->MinSystemWakeState;
|
|
status = PoRequestPowerIrp (Data->PDO,
|
|
IRP_MN_WAIT_WAKE,
|
|
powerState,
|
|
KeyboardClassWaitWakeComplete,
|
|
Data,
|
|
NULL);
|
|
|
|
if (status != STATUS_PENDING) {
|
|
success = FALSE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
VOID
|
|
KeyboardToggleWaitWakeWorker(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
PKEYBOARD_WORK_ITEM_DATA ItemData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION data;
|
|
PIRP waitWakeIrp = NULL;
|
|
KIRQL irql;
|
|
BOOLEAN wwState = ItemData->WaitWakeState ? TRUE : FALSE;
|
|
BOOLEAN toggled = FALSE;
|
|
|
|
//
|
|
// Can't be paged b/c we are using spin locks
|
|
//
|
|
// PAGED_CODE ();
|
|
|
|
data = ItemData->Data;
|
|
|
|
KeAcquireSpinLock (&data->WaitWakeSpinLock, &irql);
|
|
|
|
if (wwState != data->WaitWakeEnabled) {
|
|
toggled = TRUE;
|
|
if (data->WaitWakeEnabled) {
|
|
waitWakeIrp = (PIRP)
|
|
InterlockedExchangePointer (&data->WaitWakeIrp, NULL);
|
|
}
|
|
|
|
data->WaitWakeEnabled = wwState;
|
|
}
|
|
|
|
KeReleaseSpinLock (&data->WaitWakeSpinLock, irql);
|
|
|
|
if (toggled) {
|
|
UNICODE_STRING strEnable;
|
|
HANDLE devInstRegKey;
|
|
ULONG tmp = wwState;
|
|
|
|
//
|
|
// write the value out to the registry
|
|
//
|
|
if ((NT_SUCCESS(IoOpenDeviceRegistryKey (data->PDO,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_ALL,
|
|
&devInstRegKey)))) {
|
|
RtlInitUnicodeString (&strEnable, KEYBOARD_WAIT_WAKE_ENABLE);
|
|
|
|
ZwSetValueKey (devInstRegKey,
|
|
&strEnable,
|
|
0,
|
|
REG_DWORD,
|
|
&tmp,
|
|
sizeof(tmp));
|
|
|
|
ZwClose (devInstRegKey);
|
|
}
|
|
}
|
|
|
|
if (toggled && wwState) {
|
|
//
|
|
// wwState is our new state, so WW was just turned on
|
|
//
|
|
KeyboardClassCreateWaitWakeIrp (data);
|
|
}
|
|
|
|
//
|
|
// If we have an IRP, then WW has been toggled off, otherwise, if toggled is
|
|
// TRUE, we need to save this in the reg and, perhaps, send down a new WW irp
|
|
//
|
|
if (waitWakeIrp) {
|
|
IoCancelIrp (waitWakeIrp);
|
|
}
|
|
|
|
IoReleaseRemoveLock (&data->RemoveLock, KeyboardToggleWaitWakeWorker);
|
|
IoFreeWorkItem (ItemData->Item);
|
|
ExFreePool (ItemData);
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardToggleWaitWake(
|
|
PDEVICE_EXTENSION Data,
|
|
BOOLEAN WaitWakeState
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PKEYBOARD_WORK_ITEM_DATA itemData;
|
|
|
|
status = IoAcquireRemoveLock (&Data->RemoveLock, KeyboardToggleWaitWakeWorker);
|
|
if (!NT_SUCCESS (status)) {
|
|
//
|
|
// Device has gone away, just silently exit
|
|
//
|
|
return status;
|
|
}
|
|
|
|
itemData = (PKEYBOARD_WORK_ITEM_DATA)
|
|
ExAllocatePool(NonPagedPool, sizeof(KEYBOARD_WORK_ITEM_DATA));
|
|
if (itemData) {
|
|
itemData->Item = IoAllocateWorkItem(Data->Self);
|
|
if (itemData->Item == NULL) {
|
|
IoReleaseRemoveLock (&Data->RemoveLock, KeyboardToggleWaitWakeWorker);
|
|
}
|
|
else {
|
|
itemData->Data = Data;
|
|
itemData->WaitWakeState = WaitWakeState;
|
|
|
|
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
|
//
|
|
// We are safely at PASSIVE_LEVEL, call callback directly to perform
|
|
// this operation immediately.
|
|
//
|
|
KeyboardToggleWaitWakeWorker (Data->Self, itemData);
|
|
|
|
} else {
|
|
//
|
|
// We are not at PASSIVE_LEVEL, so queue a workitem to handle this
|
|
// at a later time.
|
|
//
|
|
IoQueueWorkItem (itemData->Item,
|
|
KeyboardToggleWaitWakeWorker,
|
|
DelayedWorkQueue,
|
|
itemData);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
IoReleaseRemoveLock (&Data->RemoveLock, KeyboardToggleWaitWakeWorker);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassPower (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The power dispatch routine.
|
|
|
|
In all cases it must call PoStartNextPowerIrp
|
|
In all cases (except failure) 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
|
|
|
|
--*/
|
|
{
|
|
POWER_STATE_TYPE powerType;
|
|
PIO_STACK_LOCATION stack;
|
|
PDEVICE_EXTENSION data;
|
|
NTSTATUS status;
|
|
POWER_STATE powerState;
|
|
BOOLEAN hookit = FALSE;
|
|
BOOLEAN pendit = FALSE;
|
|
|
|
PAGED_CODE ();
|
|
|
|
data = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
stack = IoGetCurrentIrpStackLocation (Irp);
|
|
powerType = stack->Parameters.Power.Type;
|
|
powerState = stack->Parameters.Power.State;
|
|
|
|
if (data == Globals.GrandMaster) {
|
|
//
|
|
// We should never get a power irp to the grand master.
|
|
//
|
|
ASSERT (data != Globals.GrandMaster);
|
|
PoStartNextPowerIrp (Irp);
|
|
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
} else if (!data->PnP) {
|
|
//
|
|
// We should never get a power irp to a non PnP device object.
|
|
//
|
|
ASSERT (data->PnP);
|
|
PoStartNextPowerIrp (Irp);
|
|
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
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:
|
|
KbdPrint((2,"KBDCLASS-PnP Setting %s state to %d\n",
|
|
((powerType == SystemPowerState) ? "System" : "Device"),
|
|
powerState.SystemState));
|
|
|
|
switch (powerType) {
|
|
case DevicePowerState:
|
|
status = Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
if (data->DeviceState < powerState.DeviceState) {
|
|
//
|
|
// Powering down
|
|
//
|
|
PoSetPowerState (data->Self, powerType, powerState);
|
|
data->DeviceState = powerState.DeviceState;
|
|
}
|
|
else if (powerState.DeviceState < data->DeviceState) {
|
|
//
|
|
// Powering Up
|
|
//
|
|
hookit = TRUE;
|
|
} // else { no change }.
|
|
break;
|
|
|
|
case SystemPowerState:
|
|
|
|
if (data->SystemState < powerState.SystemState) {
|
|
//
|
|
// Powering down
|
|
//
|
|
status = IoAcquireRemoveLock (&data->RemoveLock, Irp);
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// This should never happen b/c we successfully acquired
|
|
// the lock already, but we must handle this case
|
|
//
|
|
// The S irp will completed with the value in status
|
|
//
|
|
break;
|
|
}
|
|
|
|
if (WAITWAKE_ON (data) &&
|
|
powerState.SystemState < PowerSystemHibernate) {
|
|
ASSERT (powerState.SystemState >= PowerSystemWorking &&
|
|
powerState.SystemState < PowerSystemHibernate);
|
|
|
|
powerState.DeviceState =
|
|
data->SystemToDeviceState[powerState.SystemState];
|
|
}
|
|
else {
|
|
powerState.DeviceState = PowerDeviceD3;
|
|
}
|
|
|
|
IoMarkIrpPending(Irp);
|
|
status = PoRequestPowerIrp (data->Self,
|
|
IRP_MN_SET_POWER,
|
|
powerState,
|
|
KeyboardClassPoRequestComplete,
|
|
Irp,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// Failure...release the inner reference we just took
|
|
//
|
|
IoReleaseRemoveLock (&data->RemoveLock, Irp);
|
|
|
|
//
|
|
// Propagate the failure back to the S irp
|
|
//
|
|
PoStartNextPowerIrp (Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
//
|
|
// Release the outer reference (top of the function)
|
|
//
|
|
IoReleaseRemoveLock (&data->RemoveLock, Irp);
|
|
|
|
//
|
|
// Must return status pending b/c we marked the irp pending
|
|
// so we special case the return here and avoid overly
|
|
// complex processing at the end of the function.
|
|
//
|
|
return STATUS_PENDING;
|
|
}
|
|
else {
|
|
pendit = TRUE;
|
|
}
|
|
}
|
|
else if (powerState.SystemState < data->SystemState) {
|
|
//
|
|
// Powering Up
|
|
//
|
|
hookit = TRUE;
|
|
status = Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
//
|
|
// No change, but we want to make sure a wait wake irp is sent.
|
|
//
|
|
if (powerState.SystemState == PowerSystemWorking &&
|
|
SHOULD_SEND_WAITWAKE (data)) {
|
|
KeyboardClassCreateWaitWakeIrp (data);
|
|
}
|
|
status = Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case IRP_MN_QUERY_POWER:
|
|
ASSERT (SystemPowerState == powerType);
|
|
|
|
//
|
|
// Fail the query if we can't wake the machine. We do, however, want to
|
|
// let hibernate succeed no matter what (besides, it is doubtful that a
|
|
// keyboard can wait wake the machine out of S4).
|
|
//
|
|
if (powerState.SystemState < PowerSystemHibernate &&
|
|
powerState.SystemState > data->MinSystemWakeState &&
|
|
WAITWAKE_ON(data)) {
|
|
status = STATUS_POWER_STATE_INVALID;
|
|
}
|
|
else {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
break;
|
|
|
|
case IRP_MN_WAIT_WAKE:
|
|
if (InterlockedCompareExchangePointer(&data->WaitWakeIrp,
|
|
Irp,
|
|
NULL) != NULL) {
|
|
/* When powering up with WW being completed at same time, there
|
|
is a race condition between PoReq completion for S Irp and
|
|
completion of WW irp. Steps to repro this:
|
|
|
|
S irp completes and does PoReq of D irp with completion
|
|
routine MouseClassPoRequestComplete
|
|
WW Irp completion fires and the following happens:
|
|
set data->WaitWakeIrp to NULL
|
|
PoReq D irp with completion routine MouseClassWWPowerUpComplete
|
|
|
|
MouseClassPoRequestComplete fires first and sees no WW queued,
|
|
so it queues one.
|
|
MouseClassWWPowerUpComplete fires second and tries to queue
|
|
WW. When the WW arrives in mouclass, it sees there's one
|
|
queued already, so it fails it with invalid device state.
|
|
The completion routine, MouseClassWaitWakeComplete, fires
|
|
and it deletes the irp from the device extension.
|
|
|
|
This results in the appearance of wake being disabled,
|
|
even though the first irp is still queued.
|
|
*/
|
|
|
|
InterlockedExchangePointer(&data->ExtraWaitWakeIrp, Irp);
|
|
status = STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
else {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
Irp->IoStatus.Status = status;
|
|
PoStartNextPowerIrp (Irp);
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
|
|
} else if (hookit) {
|
|
status = IoAcquireRemoveLock (&data->RemoveLock, Irp);
|
|
ASSERT (STATUS_SUCCESS == status);
|
|
IoCopyCurrentIrpStackLocationToNext (Irp);
|
|
|
|
IoSetCompletionRoutine (Irp,
|
|
KeyboardClassPowerComplete,
|
|
NULL,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
IoMarkIrpPending(Irp);
|
|
PoCallDriver (data->TopPort, Irp);
|
|
|
|
//
|
|
// We are returning pending instead of the result from PoCallDriver because:
|
|
// 1 we are changing the status in the completion routine
|
|
// 2 we will not be completing this irp in the completion routine
|
|
//
|
|
status = STATUS_PENDING;
|
|
}
|
|
else if (pendit) {
|
|
status = STATUS_PENDING;
|
|
} else {
|
|
PoStartNextPowerIrp (Irp);
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = PoCallDriver (data->TopPort, Irp);
|
|
}
|
|
|
|
IoReleaseRemoveLock (&data->RemoveLock, Irp);
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
KeyboardClassPoRequestComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR MinorFunction,
|
|
IN POWER_STATE D_PowerState,
|
|
IN PIRP S_Irp, // The S irp that caused us to request the power.
|
|
IN PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
{
|
|
PDEVICE_EXTENSION data;
|
|
PKEYBOARD_WORK_ITEM_DATA itemData;
|
|
|
|
data = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// If the S_Irp is present, we are powering down. We do not pass the S_Irp
|
|
// as a parameter to PoRequestPowerIrp when we are powering up
|
|
//
|
|
if (ARGUMENT_PRESENT(S_Irp)) {
|
|
POWER_STATE powerState;
|
|
|
|
//
|
|
// Powering Down
|
|
//
|
|
powerState = IoGetCurrentIrpStackLocation(S_Irp)->Parameters.Power.State;
|
|
PoSetPowerState (data->Self, SystemPowerState, powerState);
|
|
data->SystemState = powerState.SystemState;
|
|
|
|
PoStartNextPowerIrp (S_Irp);
|
|
IoSkipCurrentIrpStackLocation (S_Irp);
|
|
PoCallDriver (data->TopPort, S_Irp);
|
|
|
|
//
|
|
// Finally, release the lock we acquired based on this irp
|
|
//
|
|
IoReleaseRemoveLock (&data->RemoveLock, S_Irp);
|
|
}
|
|
else {
|
|
//
|
|
// Powering Up
|
|
//
|
|
|
|
//
|
|
// We have come back to the PowerSystemWorking state and the device is
|
|
// fully powered. If we can (and should), send a wait wake irp down
|
|
// the stack. This is necessary because we might have gone into a power
|
|
// state where the wait wake irp was invalid.
|
|
//
|
|
ASSERT(data->SystemState == PowerSystemWorking);
|
|
|
|
if (SHOULD_SEND_WAITWAKE (data)) {
|
|
//
|
|
// We cannot call CreateWaitWake from this completion routine,
|
|
// as it is a paged function.
|
|
//
|
|
itemData = (PKEYBOARD_WORK_ITEM_DATA)
|
|
ExAllocatePool (NonPagedPool, sizeof (KEYBOARD_WORK_ITEM_DATA));
|
|
|
|
if (NULL != itemData) {
|
|
NTSTATUS status;
|
|
|
|
itemData->Item = IoAllocateWorkItem (data->Self);
|
|
if (itemData->Item == NULL) {
|
|
ExFreePool (itemData);
|
|
goto CreateWaitWakeWorkerError;
|
|
}
|
|
|
|
itemData->Data = data;
|
|
itemData->Irp = NULL;
|
|
status = IoAcquireRemoveLock (&data->RemoveLock, itemData);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
IoQueueWorkItem (itemData->Item,
|
|
KeyboardClassCreateWaitWakeIrpWorker,
|
|
DelayedWorkQueue,
|
|
itemData);
|
|
}
|
|
else {
|
|
IoFreeWorkItem (itemData->Item);
|
|
ExFreePool (itemData);
|
|
goto CreateWaitWakeWorkerError;
|
|
}
|
|
} else {
|
|
CreateWaitWakeWorkerError:
|
|
//
|
|
// Well, we dropped the WaitWake.
|
|
//
|
|
// Print a warning to the debugger, and log an error.
|
|
//
|
|
DbgPrint ("KbdClass: WARNING: Failed alloc pool -> no WW Irp\n");
|
|
|
|
KeyboardClassLogError (data->Self,
|
|
KBDCLASS_NO_RESOURCES_FOR_WAITWAKE,
|
|
1,
|
|
STATUS_INSUFFICIENT_RESOURCES,
|
|
0,
|
|
NULL,
|
|
0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassSetLedsComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PDEVICE_EXTENSION data;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
data = (PDEVICE_EXTENSION) Context;
|
|
|
|
IoReleaseRemoveLock (&data->RemoveLock, Irp);
|
|
IoFreeIrp (Irp);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassPowerComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
POWER_STATE powerState;
|
|
POWER_STATE_TYPE powerType;
|
|
PIO_STACK_LOCATION stack, next;
|
|
PIRP irpLeds;
|
|
PDEVICE_EXTENSION data;
|
|
IO_STATUS_BLOCK block;
|
|
PFILE_OBJECT file;
|
|
PKEYBOARD_INDICATOR_PARAMETERS params;
|
|
|
|
UNREFERENCED_PARAMETER (Context);
|
|
|
|
data = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
stack = IoGetCurrentIrpStackLocation (Irp);
|
|
next = IoGetNextIrpStackLocation (Irp);
|
|
powerType = stack->Parameters.Power.Type;
|
|
powerState = stack->Parameters.Power.State;
|
|
|
|
ASSERT (data != Globals.GrandMaster);
|
|
ASSERT (data->PnP);
|
|
|
|
switch (stack->MinorFunction) {
|
|
case IRP_MN_SET_POWER:
|
|
switch (powerType) {
|
|
case DevicePowerState:
|
|
ASSERT (powerState.DeviceState < data->DeviceState);
|
|
//
|
|
// Powering up
|
|
//
|
|
PoSetPowerState (data->Self, powerType, powerState);
|
|
data->DeviceState = powerState.DeviceState;
|
|
|
|
irpLeds = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (irpLeds) {
|
|
status = IoAcquireRemoveLock(&data->RemoveLock, irpLeds);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Set the keyboard Indicators.
|
|
//
|
|
if (Globals.GrandMaster) {
|
|
params = &Globals.GrandMaster->IndicatorParameters;
|
|
file = Globals.AssocClassList[data->UnitId].File;
|
|
} else {
|
|
params = &data->IndicatorParameters;
|
|
file = stack->FileObject;
|
|
}
|
|
|
|
//
|
|
// This is a completion routine. We could be at DISPATCH_LEVEL
|
|
// Therefore we must bounce the IRP
|
|
//
|
|
next = IoGetNextIrpStackLocation(irpLeds);
|
|
|
|
next->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
next->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_KEYBOARD_SET_INDICATORS;
|
|
next->Parameters.DeviceIoControl.InputBufferLength =
|
|
sizeof (KEYBOARD_INDICATOR_PARAMETERS);
|
|
next->Parameters.DeviceIoControl.OutputBufferLength = 0;
|
|
next->FileObject = file;
|
|
|
|
IoSetCompletionRoutine (irpLeds,
|
|
KeyboardClassSetLedsComplete,
|
|
data,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
irpLeds->AssociatedIrp.SystemBuffer = params;
|
|
|
|
IoCallDriver (data->TopPort, irpLeds);
|
|
}
|
|
else {
|
|
IoFreeIrp (irpLeds);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case SystemPowerState:
|
|
ASSERT (powerState.SystemState < data->SystemState);
|
|
//
|
|
// Powering up
|
|
//
|
|
// Save the system state before we overwrite it
|
|
//
|
|
PoSetPowerState (data->Self, powerType, powerState);
|
|
data->SystemState = powerState.SystemState;
|
|
powerState.DeviceState = PowerDeviceD0;
|
|
|
|
status = PoRequestPowerIrp (data->Self,
|
|
IRP_MN_SET_POWER,
|
|
powerState,
|
|
KeyboardClassPoRequestComplete,
|
|
NULL,
|
|
NULL);
|
|
|
|
//
|
|
// Propagate the error if one occurred
|
|
//
|
|
if (!NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Status = status;
|
|
}
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT (0xBADBAD == stack->MinorFunction);
|
|
break;
|
|
}
|
|
|
|
PoStartNextPowerIrp (Irp);
|
|
IoReleaseRemoveLock (&data->RemoveLock, Irp);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// WMI System Call back functions
|
|
//
|
|
NTSTATUS
|
|
KeyboardClassSystemControl (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
Routine Description
|
|
|
|
We have just received a System Control IRP.
|
|
|
|
Assume that this is a WMI IRP and
|
|
call into the WMI system library and let it handle this IRP for us.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
NTSTATUS status;
|
|
SYSCTL_IRP_DISPOSITION disposition;
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp);
|
|
if (!NT_SUCCESS (status)) {
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
status = WmiSystemControl(&deviceExtension->WmiLibInfo,
|
|
DeviceObject,
|
|
Irp,
|
|
&disposition);
|
|
switch(disposition) {
|
|
case IrpProcessed:
|
|
//
|
|
// This irp has been processed and may be completed or pending.
|
|
//
|
|
break;
|
|
|
|
case IrpNotCompleted:
|
|
//
|
|
// This irp has not been completed, but has been fully processed.
|
|
// we will complete it now
|
|
//
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
break;
|
|
|
|
case IrpForward:
|
|
case IrpNotWmi:
|
|
//
|
|
// This irp is either not a WMI irp or is a WMI irp targetted
|
|
// at a device lower in the stack.
|
|
//
|
|
status = KeyboardClassPassThrough(DeviceObject, Irp);
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// We really should never get here, but if we do just forward....
|
|
//
|
|
ASSERT(FALSE);
|
|
status = KeyboardClassPassThrough(DeviceObject, Irp);
|
|
break;
|
|
}
|
|
|
|
IoReleaseRemoveLock (&deviceExtension->RemoveLock, Irp);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassSetWmiDataItem(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN ULONG InstanceIndex,
|
|
IN ULONG DataItemId,
|
|
IN ULONG BufferSize,
|
|
IN PUCHAR Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback into the driver to set for the contents of
|
|
a data block. When the driver has finished filling the data block it
|
|
must call ClassWmiCompleteRequest to complete the irp. The driver can
|
|
return STATUS_PENDING if the irp cannot be completed immediately.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject is the device whose data block is being queried
|
|
|
|
Irp is the Irp that makes this request
|
|
|
|
GuidIndex is the index into the list of guids provided when the
|
|
device registered
|
|
|
|
InstanceIndex is the index that denotes which instance of the data block
|
|
is being queried.
|
|
|
|
DataItemId has the id of the data item being set
|
|
|
|
BufferSize has the size of the data item passed
|
|
|
|
Buffer has the new values for the data item
|
|
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION data;
|
|
NTSTATUS status;
|
|
ULONG size = 0;
|
|
|
|
PAGED_CODE ();
|
|
|
|
data = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
switch(GuidIndex) {
|
|
case WMI_CLASS_DRIVER_INFORMATION:
|
|
status = STATUS_WMI_READ_ONLY;
|
|
break;
|
|
|
|
case WMI_WAIT_WAKE:
|
|
|
|
size = sizeof(BOOLEAN);
|
|
|
|
if (BufferSize < size) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
} else if ((1 != DataItemId) || (0 != InstanceIndex)) {
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
status = KeyboardToggleWaitWake (data, *(PBOOLEAN) Buffer);
|
|
break;
|
|
|
|
default:
|
|
status = STATUS_WMI_GUID_NOT_FOUND;
|
|
}
|
|
|
|
status = WmiCompleteRequest (DeviceObject,
|
|
Irp,
|
|
status,
|
|
size,
|
|
IO_NO_INCREMENT);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassSetWmiDataBlock(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN ULONG InstanceIndex,
|
|
IN ULONG BufferSize,
|
|
IN PUCHAR Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback into the driver to set the contents of
|
|
a data block. When the driver has finished filling the data block it
|
|
must call ClassWmiCompleteRequest to complete the irp. The driver can
|
|
return STATUS_PENDING if the irp cannot be completed immediately.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject is the device whose data block is being queried
|
|
|
|
Irp is the Irp that makes this request
|
|
|
|
GuidIndex is the index into the list of guids provided when the
|
|
device registered
|
|
|
|
InstanceIndex is the index that denotes which instance of the data block
|
|
is being queried.
|
|
|
|
BufferSize has the size of the data block passed
|
|
|
|
Buffer has the new values for the data block
|
|
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION data;
|
|
NTSTATUS status;
|
|
ULONG size = 0;
|
|
|
|
PAGED_CODE ();
|
|
|
|
data = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
switch(GuidIndex) {
|
|
case WMI_CLASS_DRIVER_INFORMATION:
|
|
status = STATUS_WMI_READ_ONLY;
|
|
break;
|
|
|
|
case WMI_WAIT_WAKE: {
|
|
size = sizeof(BOOLEAN);
|
|
|
|
if (BufferSize < size) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
} else if (0 != InstanceIndex) {
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
status = KeyboardToggleWaitWake (data, * (PBOOLEAN) Buffer);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
status = STATUS_WMI_GUID_NOT_FOUND;
|
|
}
|
|
|
|
status = WmiCompleteRequest (DeviceObject,
|
|
Irp,
|
|
status,
|
|
size,
|
|
IO_NO_INCREMENT);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassQueryWmiDataBlock(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN ULONG InstanceIndex,
|
|
IN ULONG InstanceCount,
|
|
IN OUT PULONG InstanceLengthArray,
|
|
IN ULONG OutBufferSize,
|
|
OUT PUCHAR Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback into the driver to query for the contents of
|
|
a data block. When the driver has finished filling the data block it
|
|
must call ClassWmiCompleteRequest to complete the irp. The driver can
|
|
return STATUS_PENDING if the irp cannot be completed immediately.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject is the device whose data block is being queried
|
|
|
|
Irp is the Irp that makes this request
|
|
|
|
GuidIndex is the index into the list of guids provided when the
|
|
device registered
|
|
|
|
InstanceIndex is the index that denotes which instance of the data block
|
|
is being queried.
|
|
|
|
InstanceCount is the number of instnaces expected to be returned for
|
|
the data block.
|
|
|
|
InstanceLengthArray is a pointer to an array of ULONG that returns the
|
|
lengths of each instance of the data block. If this is NULL then
|
|
there was not enough space in the output buffer to fufill the request
|
|
so the irp should be completed with the buffer needed.
|
|
|
|
BufferAvail on has the maximum size available to write the data
|
|
block.
|
|
|
|
Buffer on return is filled with the returned data block
|
|
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION data;
|
|
NTSTATUS status;
|
|
ULONG size = 0;
|
|
PMSKeyboard_ClassInformation classInformation;
|
|
|
|
PAGED_CODE ();
|
|
|
|
data = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
switch (GuidIndex) {
|
|
case WMI_CLASS_DRIVER_INFORMATION:
|
|
//
|
|
// Only registers 1 instance for this guid
|
|
//
|
|
if ((0 != InstanceIndex) || (1 != InstanceCount)) {
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
size = sizeof (MSKeyboard_ClassInformation);
|
|
|
|
if (OutBufferSize < size) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
classInformation = (PMSKeyboard_ClassInformation)Buffer;
|
|
classInformation->DeviceId = (ULONGLONG) DeviceObject;
|
|
*InstanceLengthArray = size;
|
|
status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case WMI_WAIT_WAKE:
|
|
//
|
|
// Only registers 1 instance for this guid
|
|
//
|
|
if ((0 != InstanceIndex) || (1 != InstanceCount)) {
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
size = sizeof(BOOLEAN);
|
|
|
|
if (OutBufferSize < size) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
*(PBOOLEAN) Buffer = (WAITWAKE_ON (data) ? TRUE : FALSE );
|
|
*InstanceLengthArray = size;
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
status = STATUS_WMI_GUID_NOT_FOUND;
|
|
}
|
|
|
|
status = WmiCompleteRequest (DeviceObject,
|
|
Irp,
|
|
status,
|
|
size,
|
|
IO_NO_INCREMENT);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
KeyboardClassQueryWmiRegInfo(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
OUT ULONG *RegFlags,
|
|
OUT PUNICODE_STRING InstanceName,
|
|
OUT PUNICODE_STRING *RegistryPath,
|
|
OUT PUNICODE_STRING MofResourceName,
|
|
OUT PDEVICE_OBJECT *Pdo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback into the driver to retrieve information about
|
|
the guids being registered.
|
|
|
|
Implementations of this routine may be in paged memory
|
|
|
|
Arguments:
|
|
|
|
DeviceObject is the device whose registration information is needed
|
|
|
|
*RegFlags returns with a set of flags that describe all of the guids being
|
|
registered for this device. If the device wants enable and disable
|
|
collection callbacks before receiving queries for the registered
|
|
guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the
|
|
returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case
|
|
the instance name is determined from the PDO associated with the
|
|
device object. Note that the PDO must have an associated devnode. If
|
|
WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique
|
|
name for the device. These flags are ORed into the flags specified
|
|
by the GUIDREGINFO for each guid.
|
|
|
|
InstanceName returns with the instance name for the guids if
|
|
WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The
|
|
caller will call ExFreePool with the buffer returned.
|
|
|
|
*RegistryPath returns with the registry path of the driver. This is
|
|
required
|
|
|
|
*MofResourceName returns with the name of the MOF resource attached to
|
|
the binary file. If the driver does not have a mof resource attached
|
|
then this can be returned as NULL.
|
|
|
|
*Pdo returns with the device object for the PDO associated with this
|
|
device if the WMIREG_FLAG_INSTANCE_PDO flag is retured in
|
|
*RegFlags.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
PAGED_CODE ();
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
*RegFlags = WMIREG_FLAG_INSTANCE_PDO;
|
|
*RegistryPath = &Globals.RegistryPath;
|
|
*Pdo = deviceExtension->PDO;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|