1037 lines
27 KiB
C
1037 lines
27 KiB
C
/*--
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
neckbadd.c
|
|
|
|
Abstract:
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "neckbadd.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (INIT, DriverEntry)
|
|
#pragma alloc_text (PAGE, NecKbdCreateClose)
|
|
#pragma alloc_text (PAGE, NecKbdInternIoCtl)
|
|
#pragma alloc_text (PAGE, NecKbdUnload)
|
|
#endif
|
|
|
|
NTSTATUS
|
|
DriverEntry (
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Initialize the entry points of the driver.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG i;
|
|
|
|
UNREFERENCED_PARAMETER (RegistryPath);
|
|
|
|
//
|
|
// Fill in all the dispatch entry points with the pass through function
|
|
// and the explicitly fill in the functions we are going to intercept
|
|
//
|
|
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
|
|
DriverObject->MajorFunction[i] = NecKbdDispatchPassThrough;
|
|
}
|
|
|
|
DriverObject->MajorFunction [IRP_MJ_CREATE] =
|
|
DriverObject->MajorFunction [IRP_MJ_CLOSE] = NecKbdCreateClose;
|
|
DriverObject->MajorFunction [IRP_MJ_PNP] = NecKbdPnP;
|
|
DriverObject->MajorFunction [IRP_MJ_POWER] = NecKbdPower;
|
|
DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = NecKbdInternIoCtl;
|
|
|
|
DriverObject->DriverUnload = NecKbdUnload;
|
|
DriverObject->DriverExtension->AddDevice = NecKbdAddDevice;
|
|
|
|
NecKbdServiceParameters(RegistryPath);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NecKbdAddDevice(
|
|
IN PDRIVER_OBJECT Driver,
|
|
IN PDEVICE_OBJECT PDO
|
|
)
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
IO_ERROR_LOG_PACKET errorLogEntry;
|
|
PDEVICE_OBJECT device;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = IoCreateDevice(Driver, // driver
|
|
sizeof(DEVICE_EXTENSION), // size of extension
|
|
NULL, // device name
|
|
FILE_DEVICE_8042_PORT, // device type
|
|
0, // device characteristics
|
|
FALSE, // exclusive
|
|
&device // new device
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return (status);
|
|
}
|
|
|
|
RtlZeroMemory(device->DeviceExtension, sizeof(DEVICE_EXTENSION));
|
|
|
|
devExt = (PDEVICE_EXTENSION) device->DeviceExtension;
|
|
devExt->TopOfStack = IoAttachDeviceToDeviceStack(device, PDO);
|
|
|
|
if (devExt->TopOfStack == NULL) {
|
|
IoDeleteDevice(device);
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
ASSERT(devExt->TopOfStack);
|
|
|
|
devExt->Self = device;
|
|
devExt->PDO = PDO;
|
|
devExt->DeviceState = PowerDeviceD0;
|
|
|
|
devExt->Removed = FALSE;
|
|
devExt->Started = FALSE;
|
|
|
|
device->Flags |= DO_BUFFERED_IO;
|
|
device->Flags |= DO_POWER_PAGABLE;
|
|
device->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NecKbdComplete(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PKEVENT event;
|
|
PIO_STACK_LOCATION irpStack;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
event = (PKEVENT) Context;
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
if (Irp->PendingReturned) {
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
|
|
//
|
|
// We could switch on the major and minor functions of the IRP to perform
|
|
// different functions, but we know that Context is an event that needs
|
|
// to be set.
|
|
//
|
|
KeSetEvent(event, 0, FALSE);
|
|
|
|
//
|
|
// Allows the caller to use the IRP after it is completed
|
|
//
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NecKbdCreateClose (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Maintain a simple count of the creates and closes sent against this device
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION devExt;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
switch (irpStack->MajorFunction) {
|
|
case IRP_MJ_CREATE:
|
|
|
|
if (NULL == devExt->UpperConnectData.ClassService) {
|
|
//
|
|
// No Connection yet. How can we be enabled?
|
|
//
|
|
status = STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
|
|
break;
|
|
|
|
case IRP_MJ_CLOSE:
|
|
|
|
break;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
//
|
|
// Pass on the create and the close
|
|
//
|
|
return NecKbdDispatchPassThrough(DeviceObject, Irp);
|
|
}
|
|
|
|
NTSTATUS
|
|
NecKbdDispatchPassThrough(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Passes a request on to the lower driver.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Pass the IRP to the target
|
|
//
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
|
|
return IoCallDriver(
|
|
((PDEVICE_EXTENSION) DeviceObject->DeviceExtension)->TopOfStack,
|
|
Irp);
|
|
}
|
|
|
|
NTSTATUS
|
|
NecKbdInternIoCtl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch routine for internal device control requests.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDEVICE_EXTENSION devExt;
|
|
KEVENT event;
|
|
PCONNECT_DATA connectData;
|
|
PKEYBOARD_TYPEMATIC_PARAMETERS TypematicParameters;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Get a pointer to the device extension.
|
|
//
|
|
|
|
devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Initialize the returned Information field.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Get a pointer to the current parameters for this request. The
|
|
// information is contained in the current stack location.
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Case on the device control subfunction that is being performed by the
|
|
// requestor.
|
|
//
|
|
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
//
|
|
// Connect a keyboard class device driver to the port driver.
|
|
//
|
|
|
|
case IOCTL_INTERNAL_KEYBOARD_CONNECT:
|
|
//
|
|
// Only allow a connection if the keyboard hardware is present.
|
|
// Also, only allow one connection.
|
|
//
|
|
if (devExt->UpperConnectData.ClassService != NULL) {
|
|
status = STATUS_SHARING_VIOLATION;
|
|
break;
|
|
}
|
|
else if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CONNECT_DATA)) {
|
|
//
|
|
// invalid buffer
|
|
//
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy the connection parameters to the device extension.
|
|
//
|
|
connectData = ((PCONNECT_DATA)
|
|
(irpStack->Parameters.DeviceIoControl.Type3InputBuffer));
|
|
|
|
devExt->UpperConnectData = *connectData;
|
|
|
|
connectData->ClassDeviceObject = devExt->Self;
|
|
connectData->ClassService = NecKbdServiceCallback;
|
|
|
|
break;
|
|
|
|
//
|
|
// Disconnect a keyboard class device driver from the port driver.
|
|
//
|
|
case IOCTL_INTERNAL_KEYBOARD_DISCONNECT:
|
|
|
|
//
|
|
// Clear the connection parameters in the device extension.
|
|
//
|
|
// devExt->UpperConnectData.ClassDeviceObject = NULL;
|
|
// devExt->UpperConnectData.ClassService = NULL;
|
|
|
|
status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
case IOCTL_KEYBOARD_SET_TYPEMATIC:
|
|
//
|
|
// Might want to capture these in the future
|
|
//
|
|
case IOCTL_KEYBOARD_QUERY_ATTRIBUTES:
|
|
case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION:
|
|
case IOCTL_KEYBOARD_QUERY_INDICATORS:
|
|
case IOCTL_KEYBOARD_SET_INDICATORS:
|
|
case IOCTL_KEYBOARD_QUERY_TYPEMATIC:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
return NecKbdDispatchPassThrough(DeviceObject, Irp);
|
|
}
|
|
|
|
NTSTATUS
|
|
NecKbdPnP(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch routine for plug and play irps
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
PIO_STACK_LOCATION irpStack;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
KIRQL oldIrql;
|
|
KEVENT event;
|
|
|
|
PAGED_CODE();
|
|
|
|
devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
switch (irpStack->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.
|
|
//
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
KeInitializeEvent(&event,
|
|
NotificationEvent,
|
|
FALSE
|
|
);
|
|
|
|
IoSetCompletionRoutine(Irp,
|
|
(PIO_COMPLETION_ROUTINE) NecKbdComplete,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE); // No need for Cancel
|
|
|
|
status = IoCallDriver(devExt->TopOfStack, Irp);
|
|
|
|
if (STATUS_PENDING == status) {
|
|
KeWaitForSingleObject(
|
|
&event,
|
|
Executive, // Waiting for reason of a driver
|
|
KernelMode, // Waiting in kernel mode
|
|
FALSE, // No allert
|
|
NULL); // No timeout
|
|
}
|
|
|
|
if (NT_SUCCESS(status) && NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
//
|
|
// As we are successfully now back from our start device
|
|
// we can do work.
|
|
//
|
|
devExt->Started = TRUE;
|
|
devExt->Removed = FALSE;
|
|
}
|
|
|
|
//
|
|
// 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_REMOVE_DEVICE:
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
IoCallDriver(devExt->TopOfStack, Irp);
|
|
|
|
devExt->Removed = TRUE;
|
|
|
|
IoDetachDevice(devExt->TopOfStack);
|
|
IoDeleteDevice(DeviceObject);
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
|
|
case IRP_MN_STOP_DEVICE:
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
case IRP_MN_QUERY_INTERFACE:
|
|
case IRP_MN_QUERY_CAPABILITIES:
|
|
case IRP_MN_QUERY_DEVICE_TEXT:
|
|
case IRP_MN_QUERY_RESOURCES:
|
|
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
|
|
case IRP_MN_READ_CONFIG:
|
|
case IRP_MN_WRITE_CONFIG:
|
|
case IRP_MN_EJECT:
|
|
case IRP_MN_SET_LOCK:
|
|
case IRP_MN_QUERY_ID:
|
|
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
|
default:
|
|
//
|
|
// Here the filter driver might modify the behavior of these IRPS
|
|
// Please see PlugPlay documentation for use of these IRPs.
|
|
//
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
status = IoCallDriver(devExt->TopOfStack, Irp);
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NecKbdPower(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch routine for power irps Does nothing except
|
|
record the state of the device.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION devExt;
|
|
POWER_STATE powerState;
|
|
POWER_STATE_TYPE powerType;
|
|
|
|
devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
powerType = irpStack->Parameters.Power.Type;
|
|
powerState = irpStack->Parameters.Power.State;
|
|
|
|
switch (irpStack->MinorFunction) {
|
|
case IRP_MN_SET_POWER:
|
|
Print(("NecKbdPower:Power Setting %s state to %d\n",
|
|
((powerType == SystemPowerState) ? "System"
|
|
: "Device"),
|
|
powerState.SystemState));
|
|
if (powerType == DevicePowerState) {
|
|
devExt->DeviceState = powerState.DeviceState;
|
|
}
|
|
|
|
break;
|
|
|
|
case IRP_MN_QUERY_POWER:
|
|
Print(("NecKbdPower:Power query %s status to %d\n",
|
|
((powerType == SystemPowerState) ? "System"
|
|
: "Device"),
|
|
powerState.SystemState));
|
|
break;
|
|
|
|
default:
|
|
Print(("NecKbdPower:Power minor (0x%x) no known\n",
|
|
irpStack->MinorFunction));
|
|
break;
|
|
}
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
PoCallDriver(devExt->TopOfStack, Irp);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
NecKbdServiceCallback(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PKEYBOARD_INPUT_DATA InputDataStart,
|
|
IN PKEYBOARD_INPUT_DATA InputDataEnd,
|
|
IN OUT PULONG InputDataConsumed
|
|
)
|
|
{
|
|
|
|
PDEVICE_EXTENSION devExt;
|
|
PKEYBOARD_INPUT_DATA CurrentInputData,
|
|
CurrentInputDataStart;
|
|
KEYBOARD_INPUT_DATA TempInputData[2];
|
|
|
|
devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
RtlZeroMemory(TempInputData, sizeof(KEYBOARD_INPUT_DATA) * 2);
|
|
|
|
CurrentInputData = CurrentInputDataStart = InputDataStart;
|
|
|
|
while (CurrentInputData < InputDataEnd) {
|
|
|
|
// Print(("NecKbdServiceCallBack: captured scancode: 0x%2x(%2x)\n",
|
|
// CurrentInputData->MakeCode,
|
|
// CurrentInputData->Flags
|
|
// ));
|
|
|
|
if (devExt->KeyStatusFlags & STOP_PREFIX) {
|
|
|
|
if (((CurrentInputData->MakeCode != NUMLOCK_KEY) ||
|
|
((CurrentInputData->Flags & (KEY_E0|KEY_E1)) != 0) ||
|
|
((CurrentInputData->Flags & KEY_BREAK) != (devExt->CachedInputData.Flags & KEY_BREAK)))) {
|
|
|
|
Print(("NecKbdServiceCallBack: clearing prefix of STOP(%s)\n",
|
|
(CurrentInputData->Flags & KEY_BREAK) ? "Break" : "Make"
|
|
));
|
|
|
|
//
|
|
// send cached input data
|
|
//
|
|
CLASSSERVICE_CALLBACK(
|
|
&(devExt->CachedInputData),
|
|
&(devExt->CachedInputData) + 1);
|
|
|
|
devExt->KeyStatusFlags &= ~STOP_PREFIX;
|
|
}
|
|
}
|
|
|
|
switch (CurrentInputData->MakeCode) {
|
|
|
|
case CAPS_KEY:
|
|
case KANA_KEY:
|
|
|
|
if (CurrentInputData->Flags & (KEY_E0|KEY_E1)) {
|
|
break;
|
|
}
|
|
|
|
if (!(CurrentInputData->Flags & KEY_BREAK)) {
|
|
|
|
Print(("NecKbdServiceCallBack: Captured %s (Make)\n",
|
|
((CurrentInputData->MakeCode == CAPS_KEY) ? "CAPS" : "KANA")
|
|
));
|
|
|
|
if (((CurrentInputData->MakeCode == CAPS_KEY)&&(devExt->KeyStatusFlags & CAPS_PRESSING))||
|
|
((CurrentInputData->MakeCode == KANA_KEY)&&(devExt->KeyStatusFlags & KANA_PRESSING))) {
|
|
|
|
//
|
|
// ignore repeated make code
|
|
//
|
|
Print(("NecKbdServiceCallBack: ignoring repeated %s(Break)\n",
|
|
((CurrentInputData->MakeCode == CAPS_KEY) ? "CAPS" : "KANA")
|
|
));
|
|
|
|
} else {
|
|
|
|
if (CurrentInputDataStart <= CurrentInputData) {
|
|
|
|
CLASSSERVICE_CALLBACK(
|
|
CurrentInputDataStart,
|
|
CurrentInputData + 1);
|
|
}
|
|
|
|
//
|
|
// Send break code
|
|
//
|
|
RtlCopyMemory(
|
|
(PCHAR)&(TempInputData[0]),
|
|
(PCHAR)CurrentInputData,
|
|
sizeof(KEYBOARD_INPUT_DATA)
|
|
);
|
|
TempInputData[0].Flags |= KEY_BREAK;
|
|
|
|
Print(("NecKbdServiceCallBack: Sending %s(Break)\n",
|
|
((CurrentInputData->MakeCode == CAPS_KEY) ? "CAPS" : "KANA")
|
|
));
|
|
|
|
CLASSSERVICE_CALLBACK(
|
|
&(TempInputData[0]),
|
|
&(TempInputData[1]));
|
|
}
|
|
|
|
if (CurrentInputData->MakeCode == CAPS_KEY) {
|
|
devExt->KeyStatusFlags |= CAPS_PRESSING;
|
|
} else {
|
|
devExt->KeyStatusFlags |= KANA_PRESSING;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Break generates no scancode.
|
|
//
|
|
Print(("NecKbdServiceCallBack: ignoring %s(Break)\n",
|
|
((CurrentInputData->MakeCode == CAPS_KEY) ? "CAPS" : "KANA")
|
|
));
|
|
|
|
if (CurrentInputDataStart < CurrentInputData) {
|
|
|
|
CLASSSERVICE_CALLBACK(
|
|
CurrentInputDataStart,
|
|
CurrentInputData);
|
|
}
|
|
|
|
if (CurrentInputData->MakeCode == CAPS_KEY) {
|
|
devExt->KeyStatusFlags &= ~CAPS_PRESSING;
|
|
} else {
|
|
devExt->KeyStatusFlags &= ~KANA_PRESSING;
|
|
}
|
|
|
|
}
|
|
CurrentInputDataStart = CurrentInputData + 1;
|
|
|
|
break;
|
|
|
|
case CTRL_KEY:
|
|
|
|
if ((CurrentInputData->Flags & (KEY_E0|KEY_E1)) == KEY_E1) {
|
|
|
|
Print(("NecKbdServiceCallBack: prefix of STOP(%s)\n",
|
|
(CurrentInputData->Flags & KEY_BREAK) ? "Break" : "Make"
|
|
));
|
|
|
|
if (CurrentInputDataStart < CurrentInputData) {
|
|
CLASSSERVICE_CALLBACK(
|
|
CurrentInputDataStart,
|
|
CurrentInputData);
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
(PCHAR)&(devExt->CachedInputData),
|
|
(PCHAR)CurrentInputData,
|
|
sizeof(KEYBOARD_INPUT_DATA)
|
|
);
|
|
|
|
devExt->KeyStatusFlags |= STOP_PREFIX;
|
|
CurrentInputDataStart = CurrentInputData + 1;
|
|
}
|
|
|
|
break;
|
|
|
|
case NUMLOCK_KEY:
|
|
|
|
if ((CurrentInputData->Flags & (KEY_E0|KEY_E1)) == 0) {
|
|
|
|
if (devExt->KeyStatusFlags & STOP_PREFIX) {
|
|
|
|
if ((CurrentInputData->Flags & KEY_BREAK) == (devExt->CachedInputData.Flags & KEY_BREAK)) {
|
|
|
|
//
|
|
// it is STOP key
|
|
//
|
|
Print(("NecKbdServiceCallBack: Captured STOP(%s)\n",
|
|
((CurrentInputData->Flags & KEY_BREAK) ? "Break" : "Make")
|
|
));
|
|
|
|
devExt->KeyStatusFlags &= ~STOP_PREFIX;
|
|
|
|
//
|
|
// make packets for 0x1d
|
|
//
|
|
RtlCopyMemory(
|
|
(PCHAR)&(TempInputData[0]),
|
|
(PCHAR)CurrentInputData,
|
|
sizeof(KEYBOARD_INPUT_DATA)
|
|
);
|
|
TempInputData[0].MakeCode = CTRL_KEY;
|
|
TempInputData[0].Flags &= ~(KEY_E0|KEY_E1);
|
|
|
|
//
|
|
// make packet for 0x46+E0
|
|
//
|
|
RtlCopyMemory(
|
|
(PCHAR)&(TempInputData[1]),
|
|
(PCHAR)CurrentInputData,
|
|
sizeof(KEYBOARD_INPUT_DATA)
|
|
);
|
|
TempInputData[1].MakeCode = STOP_KEY;
|
|
TempInputData[1].Flags |= KEY_E0;
|
|
TempInputData[1].Flags &= ~KEY_E1;
|
|
|
|
//
|
|
// send packets 0x1d, 0x46+E0
|
|
//
|
|
CLASSSERVICE_CALLBACK(
|
|
&(TempInputData[0]),
|
|
&(TempInputData[2]));
|
|
|
|
CurrentInputDataStart = CurrentInputData + 1;
|
|
|
|
} else {
|
|
|
|
//
|
|
// invalid prefix. send it as is.
|
|
//
|
|
Print(("NecKbdServiceCallBack: invalid prefix for STOP(%s)\n",
|
|
((CurrentInputData->Flags & KEY_BREAK) ? "Break" : "Make")
|
|
));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// it is vf3 key. it behaves as F13 or NumLock
|
|
//
|
|
Print(("NecKbdServiceCallBack: Captured vf3(VfKeyEmulation is %s)\n",
|
|
((VfKeyEmulation) ? "On" : "Off")
|
|
));
|
|
|
|
if (!(VfKeyEmulation)) {
|
|
CurrentInputData->MakeCode = VF3_KEY;
|
|
CurrentInputData->Flags &= ~(KEY_E0|KEY_E1);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// ScrollLock can emulate VF4
|
|
//
|
|
case SCROLL_LOCK_KEY:
|
|
|
|
if ((CurrentInputData->Flags & (KEY_E0|KEY_E1)) == 0) {
|
|
|
|
Print(("NecKbdServiceCallBack: Captured vf4(VfKeyEmulation is %s)\n",
|
|
((VfKeyEmulation) ? "On" : "Off")
|
|
));
|
|
|
|
if (!(VfKeyEmulation)) {
|
|
CurrentInputData->MakeCode = VF4_KEY;
|
|
CurrentInputData->Flags &= ~(KEY_E0|KEY_E1);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// hankaku/zenkaku can emulate VF5
|
|
//
|
|
case HANKAKU_ZENKAKU_KEY:
|
|
|
|
if ((CurrentInputData->Flags & (KEY_E0|KEY_E1)) == 0) {
|
|
|
|
Print(("NecKbdServiceCallBack: Captured vf5(VfKeyEmulation is %s)\n",
|
|
((VfKeyEmulation) ? "On" : "Off")
|
|
));
|
|
|
|
if (!(VfKeyEmulation)) {
|
|
CurrentInputData->MakeCode = VF5_KEY;
|
|
CurrentInputData->Flags &= ~(KEY_E0|KEY_E1);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// the others(sent as is)
|
|
//
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
CurrentInputData++;
|
|
|
|
}
|
|
|
|
//
|
|
// flush InputData
|
|
//
|
|
if (CurrentInputDataStart < InputDataEnd) {
|
|
if (devExt->KeyStatusFlags & STOP_PREFIX) {
|
|
CLASSSERVICE_CALLBACK(
|
|
CurrentInputDataStart,
|
|
InputDataEnd - 1);
|
|
} else {
|
|
CLASSSERVICE_CALLBACK(
|
|
CurrentInputDataStart,
|
|
InputDataEnd);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
NecKbdUnload(
|
|
IN PDRIVER_OBJECT Driver
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free all the allocated resources associated with this driver.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to the driver object.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
ASSERT(NULL == Driver->DeviceObject);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
NecKbdServiceParameters(
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves this driver's service parameters information
|
|
from the registry.
|
|
|
|
Arguments:
|
|
|
|
RegistryPath - Pointer to the null-terminated Unicode name of the
|
|
registry path for this driver.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PRTL_QUERY_REGISTRY_TABLE Parameters = NULL;
|
|
PWSTR Path = NULL;
|
|
UNICODE_STRING ParametersPath;
|
|
ULONG QueriedVfKeyEmulation = 0;
|
|
ULONG DefaultVfKeyEmulation = 0;
|
|
USHORT queries = 1;
|
|
|
|
PAGED_CODE();
|
|
|
|
ParametersPath.Buffer = NULL;
|
|
|
|
//
|
|
// Registry path is already null-terminated, so just use it.
|
|
//
|
|
Path = RegistryPath->Buffer;
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Allocate the Rtl query table.
|
|
//
|
|
Parameters = ExAllocatePool(
|
|
PagedPool,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * (queries + 1)
|
|
);
|
|
|
|
if (!Parameters) {
|
|
|
|
Print(("NecKbdServiceParameters: couldn't allocate table for Rtl query to %ws for %ws\n",
|
|
pwParameters,
|
|
Path
|
|
));
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory(
|
|
Parameters,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * (queries + 1)
|
|
);
|
|
|
|
//
|
|
// Form a path to this driver's Parameters subkey.
|
|
//
|
|
RtlInitUnicodeString( &ParametersPath, NULL );
|
|
ParametersPath.MaximumLength = RegistryPath->Length +
|
|
(wcslen(pwParameters) * sizeof(WCHAR) ) + sizeof(UNICODE_NULL);
|
|
|
|
ParametersPath.Buffer = ExAllocatePool(
|
|
PagedPool,
|
|
ParametersPath.MaximumLength
|
|
);
|
|
|
|
if (!ParametersPath.Buffer) {
|
|
|
|
Print(("NecKbdServiceParameters: Couldn't allocate string for path to %ws for %ws\n",
|
|
pwParameters,
|
|
Path
|
|
));
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Form the parameters path.
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
ParametersPath.Buffer,
|
|
ParametersPath.MaximumLength
|
|
);
|
|
RtlAppendUnicodeToString(
|
|
&ParametersPath,
|
|
Path
|
|
);
|
|
RtlAppendUnicodeToString(
|
|
&ParametersPath,
|
|
pwParameters
|
|
);
|
|
|
|
//
|
|
// Gather all of the "user specified" information from
|
|
// the registry.
|
|
//
|
|
Parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
Parameters[0].Name = pwVfKeyEmulation;
|
|
Parameters[0].EntryContext = &QueriedVfKeyEmulation;
|
|
Parameters[0].DefaultType = REG_DWORD;
|
|
Parameters[0].DefaultData = &DefaultVfKeyEmulation;
|
|
Parameters[0].DefaultLength = sizeof(ULONG);
|
|
|
|
Status = RtlQueryRegistryValues(
|
|
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
|
ParametersPath.Buffer,
|
|
Parameters,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
Print(("NecKbdServiceParameters: RtlQueryRegistryValues failed (0x%x)\n", Status));
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// assign driver defaults.
|
|
//
|
|
VfKeyEmulation = (DefaultVfKeyEmulation == 0) ? FALSE : TRUE;
|
|
|
|
} else {
|
|
|
|
VfKeyEmulation = (QueriedVfKeyEmulation == 0) ? FALSE : TRUE;
|
|
|
|
}
|
|
|
|
Print(("NecKbdServiceParameters: VfKeyEmulation is %s\n",
|
|
VfKeyEmulation ? "Enable" : "Disable"));
|
|
|
|
//
|
|
// Free the allocated memory before returning.
|
|
//
|
|
if (ParametersPath.Buffer)
|
|
ExFreePool(ParametersPath.Buffer);
|
|
if (Parameters)
|
|
ExFreePool(Parameters);
|
|
}
|