1843 lines
55 KiB
C
1843 lines
55 KiB
C
/*++
|
|
|
|
Copyright (c) 1997-1998 Microsoft Corporation, All Rights Reserved
|
|
|
|
Module Name:
|
|
|
|
pnp.c
|
|
|
|
Abstract:
|
|
|
|
This module contains general PnP and Power code for the i8042prt Driver.
|
|
|
|
Environment:
|
|
|
|
Kernel mode.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include "i8042prt.h"
|
|
#include "i8042log.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, I8xAddDevice)
|
|
#pragma alloc_text(PAGE, I8xFilterResourceRequirements)
|
|
#pragma alloc_text(PAGE, I8xFindPortCallout)
|
|
#pragma alloc_text(PAGE, I8xManuallyRemoveDevice)
|
|
#pragma alloc_text(PAGE, I8xPnP)
|
|
#pragma alloc_text(PAGE, I8xPower)
|
|
#pragma alloc_text(PAGE, I8xRegisterDeviceInterface)
|
|
#pragma alloc_text(PAGE, I8xRemovePort)
|
|
#pragma alloc_text(PAGE, I8xSendIrpSynchronously)
|
|
#endif
|
|
|
|
NTSTATUS
|
|
I8xAddDevice (
|
|
IN PDRIVER_OBJECT Driver,
|
|
IN PDEVICE_OBJECT PDO
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a device to the stack and sets up the appropriate flags and
|
|
device extension for the newly created device.
|
|
|
|
Arguments:
|
|
|
|
Driver - The driver object
|
|
PDO - the device that we are attaching ourselves on top of
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS result code.
|
|
|
|
--*/
|
|
{
|
|
PCOMMON_DATA commonData;
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
PDEVICE_OBJECT device;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG maxSize;
|
|
|
|
PAGED_CODE();
|
|
|
|
Print(DBG_PNP_TRACE, ("enter Add Device \n"));
|
|
|
|
maxSize = sizeof(PORT_KEYBOARD_EXTENSION) > sizeof(PORT_MOUSE_EXTENSION) ?
|
|
sizeof(PORT_KEYBOARD_EXTENSION) :
|
|
sizeof(PORT_MOUSE_EXTENSION);
|
|
|
|
status = IoCreateDevice(Driver, // driver
|
|
maxSize, // size of extension
|
|
NULL, // device name
|
|
FILE_DEVICE_8042_PORT, // device type ?? unknown at this time!!!
|
|
0, // device characteristics
|
|
FALSE, // exclusive
|
|
&device // new device
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return (status);
|
|
}
|
|
|
|
RtlZeroMemory(device->DeviceExtension, maxSize);
|
|
|
|
commonData = GET_COMMON_DATA(device->DeviceExtension);
|
|
commonData->TopOfStack = IoAttachDeviceToDeviceStack(device, PDO);
|
|
|
|
if (commonData->TopOfStack == NULL) {
|
|
//
|
|
// Not good; in only extreme cases will this fail
|
|
//
|
|
errorLogEntry = (PIO_ERROR_LOG_PACKET)
|
|
IoAllocateErrorLogEntry(Driver, (UCHAR)sizeof(IO_ERROR_LOG_PACKET));
|
|
if (errorLogEntry) {
|
|
errorLogEntry->ErrorCode = I8042_ATTACH_DEVICE_FAILED;
|
|
errorLogEntry->DumpDataSize = 0;
|
|
errorLogEntry->SequenceNumber = 0;
|
|
errorLogEntry->MajorFunctionCode = 0;
|
|
errorLogEntry->IoControlCode = 0;
|
|
errorLogEntry->RetryCount = 0;
|
|
errorLogEntry->UniqueErrorValue = 0;
|
|
errorLogEntry->FinalStatus = STATUS_DEVICE_NOT_CONNECTED;
|
|
|
|
IoWriteErrorLogEntry (errorLogEntry);
|
|
}
|
|
|
|
IoDeleteDevice (device);
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
ASSERT(commonData->TopOfStack);
|
|
|
|
commonData->Self = device;
|
|
commonData->PDO = PDO;
|
|
commonData->PowerState = PowerDeviceD0;
|
|
|
|
KeInitializeSpinLock(&commonData->InterruptSpinLock);
|
|
|
|
//
|
|
// Initialize the data consumption timer
|
|
//
|
|
KeInitializeTimer(&commonData->DataConsumptionTimer);
|
|
|
|
//
|
|
// Initialize the port DPC queue to log overrun and internal
|
|
// device errors.
|
|
//
|
|
KeInitializeDpc(
|
|
&commonData->ErrorLogDpc,
|
|
(PKDEFERRED_ROUTINE) I8042ErrorLogDpc,
|
|
device
|
|
);
|
|
|
|
//
|
|
// Initialize the device completion DPC for requests that exceed the
|
|
// maximum number of retries.
|
|
//
|
|
KeInitializeDpc(
|
|
&commonData->RetriesExceededDpc,
|
|
(PKDEFERRED_ROUTINE) I8042RetriesExceededDpc,
|
|
device
|
|
);
|
|
|
|
//
|
|
// Initialize the device completion DPC for requests that have timed out
|
|
//
|
|
KeInitializeDpc(
|
|
&commonData->TimeOutDpc,
|
|
(PKDEFERRED_ROUTINE) I8042TimeOutDpc,
|
|
device
|
|
);
|
|
|
|
//
|
|
// Initialize the port completion DPC object in the device extension.
|
|
// This DPC routine handles the completion of successful set requests.
|
|
//
|
|
IoInitializeDpcRequest(device, I8042CompletionDpc);
|
|
|
|
IoInitializeRemoveLock(&commonData->RemoveLock,
|
|
I8042_POOL_TAG,
|
|
0,
|
|
0);
|
|
|
|
device->Flags |= DO_BUFFERED_IO;
|
|
device->Flags |= DO_POWER_PAGABLE;
|
|
device->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
Print(DBG_PNP_TRACE, ("Add Device (0x%x)\n", status));
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xSendIrpSynchronously (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN BOOLEAN Strict
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generic routine to send an irp DeviceObject and wait for its return up the
|
|
device stack.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object to which we want to send the Irp
|
|
|
|
Irp - The Irp we want to send
|
|
|
|
Return Value:
|
|
|
|
return code from the Irp
|
|
--*/
|
|
{
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeInitializeEvent(&event,
|
|
SynchronizationEvent,
|
|
FALSE
|
|
);
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
|
|
IoSetCompletionRoutine(Irp,
|
|
I8xPnPComplete,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
status = IoCallDriver(DeviceObject, Irp);
|
|
|
|
//
|
|
// Wait for lower drivers to be done with the Irp
|
|
//
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
if (!Strict &&
|
|
(status == STATUS_NOT_SUPPORTED ||
|
|
status == STATUS_INVALID_DEVICE_REQUEST)) {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xPnPComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PKEVENT Event
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completion routine for all PnP IRPs
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the DeviceObject
|
|
|
|
Irp - Pointer to the request packet
|
|
|
|
Event - The event to set once processing is complete
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED
|
|
|
|
--*/
|
|
{
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
UNREFERENCED_PARAMETER (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 bubble up the pending bit.
|
|
//
|
|
// if (Irp->PendingReturned) {
|
|
// IoMarkIrpPending(Irp);
|
|
// }
|
|
//
|
|
|
|
KeSetEvent(Event, 0, FALSE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xPnP (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the dispatch routine for PnP requests
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object
|
|
|
|
Irp - Pointer to the request packet
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESSFUL if successful,
|
|
an valid NTSTATUS error code otherwise
|
|
|
|
--*/
|
|
{
|
|
PPORT_KEYBOARD_EXTENSION kbExtension;
|
|
PPORT_MOUSE_EXTENSION mouseExtension;
|
|
PCOMMON_DATA commonData;
|
|
PIO_STACK_LOCATION stack;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
KIRQL oldIrql;
|
|
|
|
PAGED_CODE();
|
|
|
|
commonData = GET_COMMON_DATA(DeviceObject->DeviceExtension);
|
|
stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
status = IoAcquireRemoveLock(&commonData->RemoveLock, Irp);
|
|
if (!NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return status;
|
|
}
|
|
|
|
Print(DBG_PNP_TRACE,
|
|
("I8xPnP (%s), enter (min func=0x%x)\n",
|
|
commonData->IsKeyboard ? "kb" : "mou",
|
|
(ULONG) stack->MinorFunction
|
|
));
|
|
|
|
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 = I8xSendIrpSynchronously(commonData->TopOfStack, 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.
|
|
|
|
ExAcquireFastMutexUnsafe(&Globals.DispatchMutex);
|
|
|
|
if (commonData->Started) {
|
|
Print(DBG_PNP_ERROR,
|
|
("received 1+ starts on %s\n",
|
|
commonData->IsKeyboard ? "kb" : "mouse"
|
|
));
|
|
}
|
|
else {
|
|
//
|
|
// commonData->IsKeyboard is set during
|
|
// IOCTL_INTERNAL_KEYBOARD_CONNECT to TRUE and
|
|
// IOCTL_INTERNAL_MOUSE_CONNECT to FALSE
|
|
//
|
|
if (IS_KEYBOARD(commonData)) {
|
|
status = I8xKeyboardStartDevice(
|
|
(PPORT_KEYBOARD_EXTENSION) DeviceObject->DeviceExtension,
|
|
stack->Parameters.StartDevice.AllocatedResourcesTranslated
|
|
);
|
|
}
|
|
else {
|
|
status = I8xMouseStartDevice(
|
|
(PPORT_MOUSE_EXTENSION) DeviceObject->DeviceExtension,
|
|
stack->Parameters.StartDevice.AllocatedResourcesTranslated
|
|
);
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
InterlockedIncrement(&Globals.StartedDevices);
|
|
commonData->Started = TRUE;
|
|
}
|
|
}
|
|
|
|
ExReleaseFastMutexUnsafe(&Globals.DispatchMutex);
|
|
}
|
|
|
|
//
|
|
// 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_FILTER_RESOURCE_REQUIREMENTS:
|
|
//
|
|
// The general rule of thumb for handling this minor code is this:
|
|
// add resources when the irp is going down the stack and
|
|
// remove resources when the irp is coming back up the stack
|
|
//
|
|
// The irp has the original resources on the way down.
|
|
//
|
|
status = I8xSendIrpSynchronously(commonData->TopOfStack, Irp, FALSE);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
status = I8xFilterResourceRequirements(DeviceObject,
|
|
Irp
|
|
);
|
|
}
|
|
else {
|
|
Print(DBG_PNP_ERROR,
|
|
("error pending filter res req event (0x%x)\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
//
|
|
// Irp->IoStatus.Information will contain the new i/o resource
|
|
// requirements list so leave it alone
|
|
//
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
break;
|
|
|
|
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
|
|
|
status = I8xSendIrpSynchronously(commonData->TopOfStack, Irp, FALSE);
|
|
if (NT_SUCCESS(status)) {
|
|
(PNP_DEVICE_STATE) Irp->IoStatus.Information |=
|
|
commonData->PnpDeviceState;
|
|
}
|
|
else {
|
|
Print(DBG_PNP_ERROR,
|
|
("error pending query pnp device state event (0x%x)\n",
|
|
status
|
|
));
|
|
|
|
}
|
|
|
|
//
|
|
// Irp->IoStatus.Information will contain the new i/o resource
|
|
// requirements list so leave it alone
|
|
//
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
break;
|
|
|
|
//
|
|
// Don't let either of the requests succeed, otherwise the kb/mouse
|
|
// might be rendered useless.
|
|
//
|
|
// NOTE: this behavior is particular to i8042prt. Any other driver,
|
|
// especially any other keyboard or port driver, should
|
|
// succeed the query remove or stop. i8042prt has this different
|
|
// behavior because of the shared I/O ports but independent interrupts.
|
|
//
|
|
// FURTHERMORE, if you allow the query to succeed, it should be sent
|
|
// down the stack (see sermouse.sys for an example of how to do this)
|
|
//
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
status = (MANUALLY_REMOVED(commonData) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
|
|
|
|
//
|
|
// If we succeed the irp, we must send it down the stack
|
|
//
|
|
if (NT_SUCCESS(status)) {
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
status = IoCallDriver(commonData->TopOfStack, Irp);
|
|
}
|
|
else {
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
break;
|
|
|
|
//
|
|
// PnP rules dictate we send the IRP down to the PDO first
|
|
//
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
status = I8xSendIrpSynchronously(commonData->TopOfStack, Irp, FALSE);
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
break;
|
|
|
|
// case IRP_MN_SURPRISE_REMOVAL:
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
Print(DBG_PNP_INFO,
|
|
("(surprise) remove device (0x%x function 0x%x)\n",
|
|
commonData->Self,
|
|
(ULONG) stack->MinorFunction));
|
|
|
|
if (commonData->Initialized) {
|
|
IoWMIRegistrationControl(commonData->Self,
|
|
WMIREG_ACTION_DEREGISTER
|
|
);
|
|
}
|
|
|
|
if (commonData->Started) {
|
|
InterlockedDecrement(&Globals.StartedDevices);
|
|
}
|
|
|
|
//
|
|
// Wait for any pending I/O to drain
|
|
//
|
|
IoReleaseRemoveLockAndWait(&commonData->RemoveLock, Irp);
|
|
|
|
ExAcquireFastMutexUnsafe(&Globals.DispatchMutex);
|
|
if (IS_KEYBOARD(commonData)) {
|
|
I8xKeyboardRemoveDevice(DeviceObject);
|
|
}
|
|
else {
|
|
I8xMouseRemoveDevice(DeviceObject);
|
|
}
|
|
ExReleaseFastMutexUnsafe(&Globals.DispatchMutex);
|
|
|
|
//
|
|
// Set these flags so that when a surprise remove is sent, it will be
|
|
// handled just like a remove, and when the remove comes, no other
|
|
// removal type actions will occur.
|
|
//
|
|
commonData->Started = FALSE;
|
|
commonData->Initialized = FALSE;
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
status = IoCallDriver(commonData->TopOfStack, Irp);
|
|
|
|
IoDetachDevice(commonData->TopOfStack);
|
|
IoDeleteDevice(DeviceObject);
|
|
|
|
return status;
|
|
|
|
case IRP_MN_QUERY_CAPABILITIES:
|
|
|
|
//
|
|
// Change the device caps to not allow wait wake requests on level
|
|
// triggered interrupts for mice because when an errant mouse movement
|
|
// occurs while we are going to sleep, the interrupt will remain
|
|
// triggered indefinitely.
|
|
//
|
|
// If the mouse does not have a level triggered interrupt, just let the
|
|
// irp go by...
|
|
//
|
|
if (commonData->Started &&
|
|
IS_MOUSE(commonData) && IS_LEVEL_TRIGGERED(commonData)) {
|
|
|
|
Print(DBG_PNP_NOISE, ("query caps, mouse is level triggered\n"));
|
|
|
|
status = I8xSendIrpSynchronously(commonData->TopOfStack, Irp, TRUE);
|
|
if (NT_SUCCESS(status) && NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
PDEVICE_CAPABILITIES devCaps;
|
|
|
|
Print(DBG_PNP_INFO, ("query caps, removing wake caps\n"));
|
|
|
|
stack = IoGetCurrentIrpStackLocation(Irp);
|
|
devCaps = stack->Parameters.DeviceCapabilities.Capabilities;
|
|
|
|
ASSERT(devCaps);
|
|
|
|
if (devCaps) {
|
|
Print(DBG_PNP_NOISE,
|
|
("old DeviceWake was D%d and SystemWake was S%d.\n",
|
|
devCaps->DeviceWake-1, devCaps->SystemWake-1
|
|
)) ;
|
|
|
|
devCaps->DeviceWake = PowerDeviceUnspecified;
|
|
devCaps->SystemWake = PowerSystemUnspecified;
|
|
}
|
|
}
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
case IRP_MN_QUERY_INTERFACE:
|
|
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:
|
|
default:
|
|
//
|
|
// Here the driver below i8042prt might modify the behavior of these IRPS
|
|
// Please see PlugPlay documentation for use of these IRPs.
|
|
//
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
status = IoCallDriver(commonData->TopOfStack, Irp);
|
|
break;
|
|
}
|
|
|
|
Print(DBG_PNP_TRACE,
|
|
("I8xPnP (%s) exit (status=0x%x)\n",
|
|
commonData->IsKeyboard ? "kb" : "mou",
|
|
status
|
|
));
|
|
|
|
IoReleaseRemoveLock(&commonData->RemoveLock, Irp);
|
|
|
|
return status;
|
|
}
|
|
|
|
LONG
|
|
I8xManuallyRemoveDevice(
|
|
PCOMMON_DATA CommonData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Invalidates CommonData->PDO's device state and sets the manually removed
|
|
flag
|
|
|
|
Arguments:
|
|
|
|
CommonData - represent either the keyboard or mouse
|
|
|
|
Return Value:
|
|
|
|
new device count for that particular type of device
|
|
|
|
--*/
|
|
{
|
|
LONG deviceCount;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (IS_KEYBOARD(CommonData)) {
|
|
|
|
deviceCount = InterlockedDecrement(&Globals.AddedKeyboards);
|
|
if (deviceCount < 1) {
|
|
Print(DBG_PNP_INFO, ("clear kb (manually remove)\n"));
|
|
CLEAR_KEYBOARD_PRESENT();
|
|
}
|
|
|
|
} else {
|
|
|
|
deviceCount = InterlockedDecrement(&Globals.AddedMice);
|
|
if (deviceCount < 1) {
|
|
Print(DBG_PNP_INFO, ("clear mou (manually remove)\n"));
|
|
CLEAR_MOUSE_PRESENT();
|
|
}
|
|
|
|
}
|
|
|
|
CommonData->PnpDeviceState |= PNP_DEVICE_REMOVED | PNP_DEVICE_DONT_DISPLAY_IN_UI;
|
|
IoInvalidateDeviceState(CommonData->PDO);
|
|
|
|
return deviceCount;
|
|
}
|
|
|
|
#define PhysAddrCmp(a,b) ( (a).LowPart == (b).LowPart && (a).HighPart == (b).HighPart )
|
|
|
|
BOOLEAN
|
|
I8xRemovePort(
|
|
IN PIO_RESOURCE_DESCRIPTOR ResDesc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If the physical address contained in the ResDesc is not in the list of
|
|
previously seen physicall addresses, it is placed within the list.
|
|
|
|
Arguments:
|
|
|
|
ResDesc - contains the physical address
|
|
|
|
Return Value:
|
|
|
|
TRUE - if the physical address was found in the list
|
|
FALSE - if the physical address was not found in the list (and thus inserted
|
|
into it)
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
PHYSICAL_ADDRESS address;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (Globals.ControllerData->KnownPortsCount == -1) {
|
|
return FALSE;
|
|
}
|
|
|
|
address = ResDesc->u.Port.MinimumAddress;
|
|
for (i = 0; i < Globals.ControllerData->KnownPortsCount; i++) {
|
|
if (PhysAddrCmp(address, Globals.ControllerData->KnownPorts[i])) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (Globals.ControllerData->KnownPortsCount < MaximumPortCount) {
|
|
Globals.ControllerData->KnownPorts[
|
|
Globals.ControllerData->KnownPortsCount++] = address;
|
|
}
|
|
|
|
Print(DBG_PNP_INFO,
|
|
("Saw port [0x%08x %08x] - [0x%08x %08x]\n",
|
|
address.HighPart,
|
|
address.LowPart,
|
|
ResDesc->u.Port.MaximumAddress.HighPart,
|
|
ResDesc->u.Port.MaximumAddress.LowPart
|
|
));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xFindPortCallout(
|
|
IN PVOID Context,
|
|
IN PUNICODE_STRING PathName,
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG BusNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
|
|
IN CONFIGURATION_TYPE ControllerType,
|
|
IN ULONG ControllerNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
|
|
IN CONFIGURATION_TYPE PeripheralType,
|
|
IN ULONG PeripheralNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the callout routine sent as a parameter to
|
|
IoQueryDeviceDescription. It grabs the keyboard controller and
|
|
peripheral configuration information.
|
|
|
|
Arguments:
|
|
|
|
Context - Context parameter that was passed in by the routine
|
|
that called IoQueryDeviceDescription.
|
|
|
|
PathName - The full pathname for the registry key.
|
|
|
|
BusType - Bus interface type (Isa, Eisa, Mca, etc.).
|
|
|
|
BusNumber - The bus sub-key (0, 1, etc.).
|
|
|
|
BusInformation - Pointer to the array of pointers to the full value
|
|
information for the bus.
|
|
|
|
ControllerType - The controller type (should be KeyboardController).
|
|
|
|
ControllerNumber - The controller sub-key (0, 1, etc.).
|
|
|
|
ControllerInformation - Pointer to the array of pointers to the full
|
|
value information for the controller key.
|
|
|
|
PeripheralType - The peripheral type (should be KeyboardPeripheral).
|
|
|
|
PeripheralNumber - The peripheral sub-key.
|
|
|
|
PeripheralInformation - Pointer to the array of pointers to the full
|
|
value information for the peripheral key.
|
|
|
|
|
|
Return Value:
|
|
|
|
None. If successful, will have the following side-effects:
|
|
|
|
- Sets DeviceObject->DeviceExtension->HardwarePresent.
|
|
- Sets configuration fields in
|
|
DeviceObject->DeviceExtension->Configuration.
|
|
|
|
--*/
|
|
{
|
|
PUCHAR controllerData;
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
ULONG i,
|
|
listCount,
|
|
portCount = 0;
|
|
PIO_RESOURCE_LIST pResList = (PIO_RESOURCE_LIST) Context;
|
|
PIO_RESOURCE_DESCRIPTOR pResDesc;
|
|
PKEY_VALUE_FULL_INFORMATION controllerInfo = NULL;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceDescriptor;
|
|
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER(PathName);
|
|
UNREFERENCED_PARAMETER(BusType);
|
|
UNREFERENCED_PARAMETER(BusNumber);
|
|
UNREFERENCED_PARAMETER(BusInformation);
|
|
UNREFERENCED_PARAMETER(ControllerType);
|
|
UNREFERENCED_PARAMETER(ControllerNumber);
|
|
UNREFERENCED_PARAMETER(PeripheralType);
|
|
UNREFERENCED_PARAMETER(PeripheralNumber);
|
|
UNREFERENCED_PARAMETER(PeripheralInformation);
|
|
|
|
pResDesc = pResList->Descriptors + pResList->Count;
|
|
controllerInfo = ControllerInformation[IoQueryDeviceConfigurationData];
|
|
|
|
Print(DBG_PNP_TRACE, ("I8xFindPortCallout enter\n"));
|
|
|
|
if (controllerInfo->DataLength != 0) {
|
|
controllerData = ((PUCHAR) controllerInfo) + controllerInfo->DataOffset;
|
|
controllerData += FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR,
|
|
PartialResourceList);
|
|
|
|
listCount = ((PCM_PARTIAL_RESOURCE_LIST) controllerData)->Count;
|
|
|
|
resourceDescriptor =
|
|
((PCM_PARTIAL_RESOURCE_LIST) controllerData)->PartialDescriptors;
|
|
|
|
for (i = 0; i < listCount; i++, resourceDescriptor++) {
|
|
switch(resourceDescriptor->Type) {
|
|
case CmResourceTypePort:
|
|
|
|
if (portCount < 2) {
|
|
|
|
Print(DBG_PNP_INFO,
|
|
("found port [0x%x 0x%x] with length %d\n",
|
|
resourceDescriptor->u.Port.Start.HighPart,
|
|
resourceDescriptor->u.Port.Start.LowPart,
|
|
resourceDescriptor->u.Port.Length
|
|
));
|
|
|
|
pResDesc->Type = resourceDescriptor->Type;
|
|
pResDesc->Flags = resourceDescriptor->Flags;
|
|
pResDesc->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
|
|
pResDesc->u.Port.Alignment = 1;
|
|
pResDesc->u.Port.Length =
|
|
resourceDescriptor->u.Port.Length;
|
|
pResDesc->u.Port.MinimumAddress.QuadPart =
|
|
resourceDescriptor->u.Port.Start.QuadPart;
|
|
pResDesc->u.Port.MaximumAddress.QuadPart =
|
|
pResDesc->u.Port.MinimumAddress.QuadPart +
|
|
pResDesc->u.Port.Length - 1;
|
|
|
|
pResList->Count++;
|
|
|
|
//
|
|
// We want to record the ports we stole from the kb as seen
|
|
// so that if the keyboard is started later, we can trim
|
|
// its resources and not have a resource conflict...
|
|
//
|
|
// ...we are getting too smart for ourselves here :]
|
|
//
|
|
I8xRemovePort(pResDesc);
|
|
pResDesc++;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
default:
|
|
Print(DBG_PNP_NOISE, ("type 0x%x found\n",
|
|
(LONG) resourceDescriptor->Type));
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
Print(DBG_PNP_TRACE, ("I8xFindPortCallout exit (0x%x)\n", status));
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xFilterResourceRequirements(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Iterates through the resource requirements list contained in the IRP and removes
|
|
any duplicate requests for I/O ports. (This is a common problem on the Alphas.)
|
|
|
|
No removal is performed if more than one resource requirements list is present.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - A pointer to the device object
|
|
|
|
Irp - A pointer to the request packet which contains the resource req. list.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_RESOURCE_REQUIREMENTS_LIST pReqList = NULL,
|
|
pNewReqList = NULL;
|
|
PIO_RESOURCE_LIST pResList = NULL,
|
|
pNewResList = NULL;
|
|
PIO_RESOURCE_DESCRIPTOR pResDesc = NULL,
|
|
pNewResDesc = NULL;
|
|
ULONG i = 0, j = 0,
|
|
removeCount,
|
|
reqCount,
|
|
size;
|
|
BOOLEAN foundInt = FALSE,
|
|
foundPorts = FALSE;
|
|
|
|
PIO_STACK_LOCATION stack;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DeviceObject);
|
|
ASSERT(DeviceObject->DeviceExtension);
|
|
|
|
Print(DBG_PNP_NOISE,
|
|
("Received IRP_MN_FILTER_RESOURCE_REQUIREMENTS for %s\n",
|
|
(GET_COMMON_DATA(DeviceObject->DeviceExtension))->IsKeyboard ? "kb" : "mouse"
|
|
));
|
|
|
|
stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// The list can be in either the information field, or in the current
|
|
// stack location. The Information field has a higher precedence over
|
|
// the stack location.
|
|
//
|
|
if (Irp->IoStatus.Information == 0) {
|
|
pReqList =
|
|
stack->Parameters.FilterResourceRequirements.IoResourceRequirementList;
|
|
Irp->IoStatus.Information = (ULONG_PTR) pReqList;
|
|
}
|
|
else {
|
|
pReqList = (PIO_RESOURCE_REQUIREMENTS_LIST) Irp->IoStatus.Information;
|
|
}
|
|
|
|
if (!pReqList) {
|
|
//
|
|
// Not much can be done here except return
|
|
//
|
|
Print(DBG_PNP_MASK & ~ DBG_PNP_TRACE,
|
|
("(%s) NULL resource list in I8xFilterResourceRequirements\n",
|
|
(GET_COMMON_DATA(DeviceObject->DeviceExtension))->IsKeyboard ?
|
|
"kb" : "mou"
|
|
));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ASSERT(Irp->IoStatus.Information != 0);
|
|
ASSERT(pReqList != 0);
|
|
|
|
reqCount = pReqList->AlternativeLists;
|
|
|
|
//
|
|
// Only one AlternativeList is supported. If there is more than one list,
|
|
// then there is now way of knowing which list will be chosen. Also, if
|
|
// there are multiple lists, then chances are that a list with no i/o port
|
|
// conflicts will be chosen.
|
|
//
|
|
if (reqCount > 1) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pResList = pReqList->List;
|
|
removeCount = 0;
|
|
|
|
for (j = 0; j < pResList->Count; j++) {
|
|
pResDesc = &pResList->Descriptors[j];
|
|
switch (pResDesc->Type) {
|
|
case CmResourceTypePort:
|
|
Print(DBG_PNP_INFO,
|
|
("option = 0x%x, flags = 0x%x\n",
|
|
(LONG) pResDesc->Option,
|
|
(LONG) pResDesc->Flags
|
|
));
|
|
|
|
if (I8xRemovePort(pResDesc)) {
|
|
//
|
|
// Increment the remove count and tag this resource as
|
|
// one that we don't want to copy to the new list
|
|
//
|
|
removeCount++;
|
|
pResDesc->Type = I8X_REMOVE_RESOURCE;
|
|
}
|
|
|
|
foundPorts = TRUE;
|
|
break;
|
|
|
|
case CmResourceTypeInterrupt:
|
|
if (Globals.ControllerData->Configuration.SharedInterrupts) {
|
|
if (pResDesc->ShareDisposition != CmResourceShareShared) {
|
|
Print(DBG_PNP_INFO, ("forcing non shared int to shared\n"));
|
|
}
|
|
pResDesc->ShareDisposition = CmResourceShareShared;
|
|
}
|
|
|
|
foundInt = TRUE;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (removeCount) {
|
|
size = pReqList->ListSize;
|
|
|
|
//
|
|
// One element of the array is already allocated (via the struct
|
|
// definition) so make sure that we are allocating at least that
|
|
// much memory.
|
|
//
|
|
|
|
ASSERT(pResList->Count >= removeCount);
|
|
if (pResList->Count > 1) {
|
|
size -= removeCount * sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
}
|
|
|
|
pNewReqList =
|
|
(PIO_RESOURCE_REQUIREMENTS_LIST) ExAllocatePool(PagedPool, size);
|
|
|
|
if (!pNewReqList) {
|
|
//
|
|
// This is not good, but the system doesn't really need to know about
|
|
// this, so just fix up our munging and return the original list
|
|
//
|
|
pReqList = stack->Parameters.FilterResourceRequirements.IoResourceRequirementList;
|
|
reqCount = pReqList->AlternativeLists;
|
|
removeCount = 0;
|
|
|
|
for (i = 0; i < reqCount; i++) {
|
|
pResList = &pReqList->List[i];
|
|
|
|
for (j = 0; j < pResList->Count; j++) {
|
|
pResDesc = &pResList->Descriptors[j];
|
|
if (pResDesc->Type == I8X_REMOVE_RESOURCE) {
|
|
pResDesc->Type = CmResourceTypePort;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Clear out the newly allocated list
|
|
//
|
|
RtlZeroMemory(pNewReqList,
|
|
size
|
|
);
|
|
|
|
//
|
|
// Copy the list header information except for the IO resource list
|
|
// itself
|
|
//
|
|
RtlCopyMemory(pNewReqList,
|
|
pReqList,
|
|
sizeof(IO_RESOURCE_REQUIREMENTS_LIST) -
|
|
sizeof(IO_RESOURCE_LIST)
|
|
);
|
|
pNewReqList->ListSize = size;
|
|
|
|
pResList = pReqList->List;
|
|
pNewResList = pNewReqList->List;
|
|
|
|
//
|
|
// Copy the list header information except for the IO resource
|
|
// descriptor list itself
|
|
//
|
|
RtlCopyMemory(pNewResList,
|
|
pResList,
|
|
sizeof(IO_RESOURCE_LIST) -
|
|
sizeof(IO_RESOURCE_DESCRIPTOR)
|
|
);
|
|
|
|
pNewResList->Count = 0;
|
|
pNewResDesc = pNewResList->Descriptors;
|
|
|
|
for (j = 0; j < pResList->Count; j++) {
|
|
pResDesc = &pResList->Descriptors[j];
|
|
if (pResDesc->Type != I8X_REMOVE_RESOURCE) {
|
|
//
|
|
// Keep this resource, so copy it into the new list and
|
|
// incement the count and the location for the next
|
|
// IO resource descriptor
|
|
//
|
|
*pNewResDesc = *pResDesc;
|
|
pNewResDesc++;
|
|
pNewResList->Count++;
|
|
|
|
Print(DBG_PNP_INFO,
|
|
("List #%d, Descriptor #%d ... keeping res type %d\n",
|
|
i, j,
|
|
(ULONG) pResDesc->Type
|
|
));
|
|
}
|
|
else {
|
|
//
|
|
// Decrement the remove count so we can assert it is
|
|
// zero once we are done
|
|
//
|
|
Print(DBG_PNP_INFO,
|
|
("Removing port [0x%08x %08x] - [0x%#08x %08x]\n",
|
|
pResDesc->u.Port.MinimumAddress.HighPart,
|
|
pResDesc->u.Port.MinimumAddress.LowPart,
|
|
pResDesc->u.Port.MaximumAddress.HighPart,
|
|
pResDesc->u.Port.MaximumAddress.LowPart
|
|
));
|
|
removeCount--;
|
|
}
|
|
}
|
|
|
|
ASSERT(removeCount == 0);
|
|
|
|
//
|
|
// There have been bugs where the old list was being used. Zero it out to
|
|
// make sure that no conflicts arise. (Not to mention the fact that some
|
|
// other code is accessing freed memory
|
|
//
|
|
RtlZeroMemory(pReqList,
|
|
pReqList->ListSize
|
|
);
|
|
|
|
//
|
|
// Free the old list and place the new one in its place
|
|
//
|
|
ExFreePool(pReqList);
|
|
stack->Parameters.FilterResourceRequirements.IoResourceRequirementList =
|
|
pNewReqList;
|
|
Irp->IoStatus.Information = (ULONG_PTR) pNewReqList;
|
|
}
|
|
else if (!KEYBOARD_PRESENT() && !foundPorts && foundInt) {
|
|
INTERFACE_TYPE interfaceType;
|
|
NTSTATUS status;
|
|
ULONG prevCount;
|
|
CONFIGURATION_TYPE controllerType = KeyboardController;
|
|
CONFIGURATION_TYPE peripheralType = KeyboardPeripheral;
|
|
|
|
ASSERT( MOUSE_PRESENT() );
|
|
|
|
Print(DBG_PNP_INFO, ("Adding ports to res list!\n"));
|
|
|
|
//
|
|
// We will now yank the resources from the keyboard to start the mouse
|
|
// solo
|
|
//
|
|
size = pReqList->ListSize + 2 * sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
pNewReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)
|
|
ExAllocatePool(
|
|
PagedPool,
|
|
size
|
|
);
|
|
|
|
if (!pNewReqList) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Clear out the newly allocated list
|
|
//
|
|
RtlZeroMemory(pNewReqList,
|
|
size
|
|
);
|
|
|
|
//
|
|
// Copy the entire old list
|
|
//
|
|
RtlCopyMemory(pNewReqList,
|
|
pReqList,
|
|
pReqList->ListSize
|
|
);
|
|
|
|
pResList = pReqList->List;
|
|
pNewResList = pNewReqList->List;
|
|
|
|
prevCount = pNewResList->Count;
|
|
for (i = 0; i < MaximumInterfaceType; i++) {
|
|
|
|
//
|
|
// Get the registry information for this device.
|
|
//
|
|
interfaceType = i;
|
|
status = IoQueryDeviceDescription(
|
|
&interfaceType,
|
|
NULL,
|
|
&controllerType,
|
|
NULL,
|
|
&peripheralType,
|
|
NULL,
|
|
I8xFindPortCallout,
|
|
(PVOID) pNewResList
|
|
);
|
|
|
|
if (NT_SUCCESS(status) || prevCount != pNewResList->Count) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status) || prevCount != pNewResList->Count) {
|
|
pNewReqList->ListSize = size - (2 - (pNewResList->Count - prevCount));
|
|
|
|
//
|
|
// Free the old list and place the new one in its place
|
|
//
|
|
ExFreePool(pReqList);
|
|
stack->Parameters.FilterResourceRequirements.IoResourceRequirementList =
|
|
pNewReqList;
|
|
Irp->IoStatus.Information = (ULONG_PTR) pNewReqList;
|
|
}
|
|
else {
|
|
ExFreePool(pNewReqList);
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
I8xRegisterDeviceInterface(
|
|
PDEVICE_OBJECT PDO,
|
|
CONST GUID * Guid,
|
|
PUNICODE_STRING SymbolicName
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = IoRegisterDeviceInterface(
|
|
PDO,
|
|
Guid,
|
|
NULL,
|
|
SymbolicName
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
status = IoSetDeviceInterfaceState(SymbolicName,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
I8xSetPowerFlag(
|
|
IN ULONG Flag,
|
|
IN BOOLEAN Set
|
|
)
|
|
{
|
|
KIRQL irql;
|
|
|
|
KeAcquireSpinLock(&Globals.ControllerData->PowerSpinLock, &irql);
|
|
if (Set) {
|
|
Globals.PowerFlags |= Flag;
|
|
}
|
|
else {
|
|
Globals.PowerFlags &= ~Flag;
|
|
}
|
|
KeReleaseSpinLock(&Globals.ControllerData->PowerSpinLock, irql);
|
|
}
|
|
|
|
BOOLEAN
|
|
I8xCheckPowerFlag(
|
|
ULONG Flag
|
|
)
|
|
{
|
|
KIRQL irql;
|
|
BOOLEAN rVal = FALSE;
|
|
|
|
KeAcquireSpinLock(&Globals.ControllerData->PowerSpinLock, &irql);
|
|
if (Globals.PowerFlags & Flag) {
|
|
rVal = TRUE;
|
|
}
|
|
KeReleaseSpinLock(&Globals.ControllerData->PowerSpinLock, irql);
|
|
|
|
return rVal;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xPower (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the dispatch routine for power requests.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESSFUL if successful,
|
|
an valid NTSTATUS error code otherwise
|
|
|
|
--*/
|
|
{
|
|
PCOMMON_DATA commonData;
|
|
PIO_STACK_LOCATION stack;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
commonData = GET_COMMON_DATA(DeviceObject->DeviceExtension);
|
|
|
|
stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
Print(DBG_POWER_TRACE,
|
|
("Power (%s), enter\n",
|
|
commonData->IsKeyboard ? "keyboard" :
|
|
"mouse"
|
|
));
|
|
|
|
//
|
|
// A power irp can be sent to the device before we have been started or
|
|
// initialized. Since the code below relies on StartDevice() to have
|
|
// executed, just fire and forget the irp
|
|
//
|
|
if (!commonData->Started || !commonData->Initialized) {
|
|
PoStartNextPowerIrp(Irp);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return PoCallDriver(commonData->TopOfStack, Irp);
|
|
}
|
|
|
|
switch(stack->MinorFunction) {
|
|
case IRP_MN_WAIT_WAKE:
|
|
Print(DBG_POWER_NOISE, ("Got IRP_MN_WAIT_WAKE\n" ));
|
|
|
|
//
|
|
// Fail all wait wake requests on level triggered interrupts for mice
|
|
// because when an errant mouse movement occurs while we are going to
|
|
// sleep, it will keep the interrupt triggered indefinitely.
|
|
//
|
|
// We should not even get into this situation because the caps of the
|
|
// mouse should have been altered to not report wait wake
|
|
//
|
|
if (IS_MOUSE(commonData) && IS_LEVEL_TRIGGERED(commonData)) {
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
status = Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
Print(DBG_POWER_INFO | DBG_POWER_ERROR,
|
|
("failing a wait wake request on a level triggered mouse\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
break;
|
|
|
|
case IRP_MN_POWER_SEQUENCE:
|
|
Print(DBG_POWER_NOISE, ("Got IRP_MN_POWER_SEQUENCE\n" ));
|
|
break;
|
|
|
|
case IRP_MN_SET_POWER:
|
|
Print(DBG_POWER_NOISE, ("Got IRP_MN_SET_POWER\n" ));
|
|
|
|
//
|
|
// Don't handle anything but DevicePowerState changes
|
|
//
|
|
if (stack->Parameters.Power.Type != DevicePowerState) {
|
|
commonData->SystemState = stack->Parameters.Power.State.SystemState;
|
|
|
|
Print(DBG_POWER_INFO, ("system power irp, S%d\n", commonData->SystemState-1));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check for no change in state, and if none, do nothing. This state
|
|
// can occur when the device is armed for wake. We will get a D0 in
|
|
// response to the WW irp completing and then another D0 corresponding
|
|
// to the S0 irp sent to the stack.
|
|
//
|
|
if (stack->Parameters.Power.State.DeviceState ==
|
|
commonData->PowerState) {
|
|
Print(DBG_POWER_INFO,
|
|
("no change in state (PowerDeviceD%d)\n",
|
|
commonData->PowerState-1
|
|
));
|
|
break;
|
|
}
|
|
|
|
switch (stack->Parameters.Power.State.DeviceState) {
|
|
case PowerDeviceD0:
|
|
Print(DBG_POWER_INFO, ("Powering up to PowerDeviceD0\n"));
|
|
|
|
IoAcquireRemoveLock(&commonData->RemoveLock, Irp);
|
|
|
|
if (IS_KEYBOARD(commonData)) {
|
|
I8xSetPowerFlag(KBD_POWERED_UP_STARTED, TRUE);
|
|
}
|
|
else {
|
|
I8xSetPowerFlag(MOU_POWERED_UP_STARTED, TRUE);
|
|
}
|
|
|
|
//
|
|
// PoSetPowerState will be called in I8xReinitalizeHardware for each
|
|
// device once all the devices have powered back up
|
|
//
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp,
|
|
I8xPowerUpToD0Complete,
|
|
NULL,
|
|
TRUE, // on success
|
|
TRUE, // on error
|
|
TRUE // on cancel
|
|
);
|
|
|
|
//
|
|
// PoStartNextPowerIrp() gets called when the irp gets completed
|
|
// in either the completion routine or the resulting work item
|
|
//
|
|
// It is OK to call PoCallDriver and return pending b/c we are
|
|
// pending the irp in the completion routine and we may change
|
|
// the completion status if we can't alloc pool. If we return the
|
|
// value from PoCallDriver, we are tied to that status value on the
|
|
// way back up.
|
|
//
|
|
IoMarkIrpPending(Irp);
|
|
PoCallDriver(commonData->TopOfStack, Irp);
|
|
return STATUS_PENDING;
|
|
|
|
case PowerDeviceD1:
|
|
case PowerDeviceD2:
|
|
case PowerDeviceD3:
|
|
Print(DBG_POWER_INFO,
|
|
("Powering down to PowerDeviceD%d\n",
|
|
stack->Parameters.Power.State.DeviceState-1
|
|
));
|
|
|
|
//
|
|
// If WORK_ITEM_QUEUED is set, that means that a work item is
|
|
// either queued to be run, or running now so we don't want to yank
|
|
// any devices underneath from the work item
|
|
//
|
|
if (I8xCheckPowerFlag(WORK_ITEM_QUEUED)) {
|
|
Print(DBG_POWER_INFO | DBG_POWER_ERROR,
|
|
("denying power down request because work item is running\n"
|
|
));
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
status = Irp->IoStatus.Status = STATUS_POWER_STATE_INVALID;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return status;
|
|
}
|
|
|
|
if (IS_KEYBOARD(commonData)) {
|
|
I8xSetPowerFlag(KBD_POWERED_DOWN, TRUE);
|
|
}
|
|
else {
|
|
I8xSetPowerFlag(MOU_POWERED_DOWN, TRUE);
|
|
}
|
|
|
|
PoSetPowerState(DeviceObject,
|
|
stack->Parameters.Power.Type,
|
|
stack->Parameters.Power.State
|
|
);
|
|
|
|
//
|
|
// Disconnect level triggered interupts on mice when we go into
|
|
// low power so errant mouse movement doesn't leave the interrupt
|
|
// signalled for long periods of time
|
|
//
|
|
if (IS_MOUSE(commonData) && IS_LEVEL_TRIGGERED(commonData)) {
|
|
PKINTERRUPT interrupt = commonData->InterruptObject;
|
|
|
|
Print(DBG_POWER_NOISE,
|
|
("disconnecting interrupt on level triggered mouse\n")
|
|
);
|
|
|
|
commonData->InterruptObject = NULL;
|
|
if (interrupt) {
|
|
IoDisconnectInterrupt(interrupt);
|
|
}
|
|
}
|
|
|
|
commonData->PowerState = stack->Parameters.Power.State.DeviceState;
|
|
commonData->ShutdownType = stack->Parameters.Power.ShutdownType;
|
|
|
|
//
|
|
// For what we are doing, we don't need a completion routine
|
|
// since we don't race on the power requests.
|
|
//
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
PoStartNextPowerIrp(Irp);
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return PoCallDriver(commonData->TopOfStack, Irp);
|
|
|
|
default:
|
|
Print(DBG_POWER_INFO, ("unknown state\n"));
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_QUERY_POWER:
|
|
Print(DBG_POWER_NOISE, ("Got IRP_MN_QUERY_POWER\n" ));
|
|
break;
|
|
|
|
default:
|
|
Print(DBG_POWER_NOISE,
|
|
("Got unhandled minor function (%d)\n",
|
|
stack->MinorFunction
|
|
));
|
|
break;
|
|
}
|
|
|
|
Print(DBG_POWER_TRACE, ("Power, exit\n"));
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return PoCallDriver(commonData->TopOfStack, Irp);
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xPowerUpToD0Complete(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reinitializes the i8042 haardware after any type of hibernation/sleep.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object
|
|
|
|
Irp - Pointer to the request
|
|
|
|
Context - Context passed in from the funciton that set the completion
|
|
routine. UNUSED.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESSFUL if successful,
|
|
an valid NTSTATUS error code otherwise
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PCOMMON_DATA commonData;
|
|
PPOWER_UP_WORK_ITEM item;
|
|
KIRQL irql;
|
|
UCHAR poweredDownDevices = 0,
|
|
poweredUpDevices = 0,
|
|
failedDevices = 0;
|
|
BOOLEAN queueItem = FALSE,
|
|
clearFlags = FALSE,
|
|
failMouIrp = FALSE;
|
|
PIRP mouIrp = NULL,
|
|
kbdIrp = NULL;
|
|
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
commonData = GET_COMMON_DATA(DeviceObject->DeviceExtension);
|
|
|
|
Print(DBG_POWER_TRACE,
|
|
("PowerUpToD0Complete (%s), Enter\n",
|
|
commonData->IsKeyboard ? "kb" : "mouse"
|
|
));
|
|
|
|
|
|
//
|
|
// We can use a regular work item because we have a non completed power irp
|
|
// which has an outstanding reference to this stack.
|
|
//
|
|
item = (PPOWER_UP_WORK_ITEM) ExAllocatePool(NonPagedPool,
|
|
sizeof(POWER_UP_WORK_ITEM));
|
|
|
|
KeAcquireSpinLock(&Globals.ControllerData->PowerSpinLock, &irql);
|
|
|
|
Print(DBG_POWER_TRACE,
|
|
("Power up to D0 completion enter, power flags 0x%x\n",
|
|
Globals.PowerFlags));
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
commonData->OutstandingPowerIrp = Irp;
|
|
status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
if (IS_KEYBOARD(commonData)) {
|
|
KEYBOARD_POWERED_UP_SUCCESSFULLY();
|
|
}
|
|
else {
|
|
MOUSE_POWERED_UP_SUCCESSFULLY();
|
|
}
|
|
}
|
|
else {
|
|
if (IS_KEYBOARD(commonData)) {
|
|
KEYBOARD_POWERED_UP_FAILURE();
|
|
}
|
|
else {
|
|
MOUSE_POWERED_UP_FAILURE();
|
|
}
|
|
}
|
|
|
|
if (KEYBOARD_POWERED_DOWN_SUCCESS()) {
|
|
Print(DBG_POWER_NOISE, ("--kbd powered down successfully\n"));
|
|
poweredDownDevices++;
|
|
}
|
|
if (MOUSE_POWERED_DOWN_SUCCESS()) {
|
|
Print(DBG_POWER_NOISE, ("--mou powered down successfully\n"));
|
|
poweredDownDevices++;
|
|
}
|
|
|
|
if (KEYBOARD_POWERED_UP_SUCCESS()) {
|
|
Print(DBG_POWER_NOISE, ("++kbd powered up successfully\n"));
|
|
poweredUpDevices++;
|
|
}
|
|
if (MOUSE_POWERED_UP_SUCCESS()) {
|
|
Print(DBG_POWER_NOISE, ("++mou powered up successfully\n"));
|
|
poweredUpDevices++;
|
|
}
|
|
|
|
if (KEYBOARD_POWERED_UP_FAILED()) {
|
|
Print(DBG_POWER_NOISE|DBG_POWER_ERROR, (">>kbd powered down failed\n"));
|
|
failedDevices++;
|
|
}
|
|
if (MOUSE_POWERED_UP_FAILED()) {
|
|
Print(DBG_POWER_NOISE|DBG_POWER_ERROR, (">>mou powered down failed\n"));
|
|
failedDevices++;
|
|
}
|
|
|
|
Print(DBG_POWER_INFO,
|
|
("up %d, down %d, failed %d, flags 0x%x\n",
|
|
(ULONG) poweredUpDevices,
|
|
(ULONG) poweredDownDevices,
|
|
(ULONG) failedDevices,
|
|
Globals.PowerFlags));
|
|
|
|
if ((poweredUpDevices + failedDevices) == poweredDownDevices) {
|
|
if (poweredUpDevices > 0) {
|
|
//
|
|
// The ports are associated with the keyboard. If it has failed to
|
|
// power up while the mouse succeeded, we still need to fail the
|
|
// mouse b/c there is no hardware to talk to
|
|
//
|
|
if (failedDevices > 0 && KEYBOARD_POWERED_UP_FAILED()) {
|
|
ASSERT(MOUSE_POWERED_UP_SUCCESS());
|
|
ASSERT(Globals.KeyboardExtension->OutstandingPowerIrp == NULL);
|
|
|
|
mouIrp = Globals.MouseExtension->OutstandingPowerIrp;
|
|
Globals.MouseExtension->OutstandingPowerIrp = NULL;
|
|
Globals.PowerFlags &= ~MOU_POWER_FLAGS;
|
|
clearFlags = TRUE;
|
|
|
|
if (mouIrp != Irp) {
|
|
//
|
|
// we have queued the irp, complete it later in this
|
|
// function under a special case
|
|
//
|
|
failMouIrp = TRUE;
|
|
}
|
|
else {
|
|
//
|
|
// The mouse irp is the current irp. We have already
|
|
// completed the kbd irp in our previous processing. Set
|
|
// the irp status to some unsuccessful value so that we will
|
|
// call PoStartNextPowerIrp later in this function. Also
|
|
// set status to != STATUS_MORE_PROCESSING_REQUIRED so the
|
|
// irp will be completed when the function exits.
|
|
//
|
|
status = mouIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
}
|
|
else {
|
|
Print(DBG_POWER_INFO, ("at least one device powered up!\n"));
|
|
queueItem = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
Print(DBG_POWER_INFO,
|
|
("all devices failed power up, 0x%x\n",
|
|
Globals.PowerFlags));
|
|
|
|
clearFlags = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// the other device is still powered down, wait for it to power back
|
|
// up before processing power states
|
|
//
|
|
Print(DBG_POWER_INFO,
|
|
("queueing, waiting for 2nd dev obj to power cycle\n"));
|
|
}
|
|
|
|
if (queueItem || clearFlags) {
|
|
//
|
|
// Extract the irp from each successfully started device and clear the
|
|
// associated power flags for the device
|
|
//
|
|
if (MOUSE_POWERED_UP_SUCCESS()) {
|
|
mouIrp = Globals.MouseExtension->OutstandingPowerIrp;
|
|
Globals.MouseExtension->OutstandingPowerIrp = NULL;
|
|
|
|
ASSERT(!TEST_PWR_FLAGS(MOU_POWERED_UP_FAILURE));
|
|
Globals.PowerFlags &= ~MOU_POWER_FLAGS;
|
|
}
|
|
else {
|
|
Globals.PowerFlags &= ~(MOU_POWERED_UP_FAILURE);
|
|
}
|
|
|
|
if (KEYBOARD_POWERED_UP_SUCCESS()) {
|
|
kbdIrp = Globals.KeyboardExtension->OutstandingPowerIrp;
|
|
Globals.KeyboardExtension->OutstandingPowerIrp = NULL;
|
|
|
|
ASSERT(!TEST_PWR_FLAGS(KBD_POWERED_UP_FAILURE));
|
|
Globals.PowerFlags &= ~(KBD_POWER_FLAGS);
|
|
}
|
|
else {
|
|
Globals.PowerFlags &= ~(KBD_POWERED_UP_FAILURE);
|
|
}
|
|
|
|
//
|
|
// Mark that the work item is queued. This is used to make sure that 2
|
|
// work items are not queued concucrrently
|
|
//
|
|
if (item && queueItem) {
|
|
Print(DBG_POWER_INFO, ("setting work item queued flag\n"));
|
|
|
|
Globals.PowerFlags |= WORK_ITEM_QUEUED;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&Globals.ControllerData->PowerSpinLock, irql);
|
|
|
|
if (queueItem) {
|
|
if (item == NULL) {
|
|
//
|
|
// complete any queued power irps
|
|
//
|
|
Print(DBG_POWER_INFO | DBG_POWER_ERROR,
|
|
("failed to alloc work item\n"));
|
|
|
|
//
|
|
// what about PoSetPowerState?
|
|
//
|
|
if (mouIrp != NULL) {
|
|
Print(DBG_POWER_ERROR | DBG_POWER_INFO,
|
|
("completing mouse power irp 0x%x", mouIrp));
|
|
|
|
mouIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
mouIrp->IoStatus.Information = 0x0;
|
|
|
|
PoStartNextPowerIrp(mouIrp);
|
|
IoCompleteRequest(mouIrp, IO_NO_INCREMENT);
|
|
IoReleaseRemoveLock(&Globals.MouseExtension->RemoveLock,
|
|
mouIrp);
|
|
mouIrp = NULL;
|
|
}
|
|
|
|
if (kbdIrp != NULL) {
|
|
Print(DBG_POWER_ERROR | DBG_POWER_INFO,
|
|
("completing kbd power irp 0x%x", kbdIrp));
|
|
|
|
kbdIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
kbdIrp->IoStatus.Information = 0x0;
|
|
|
|
PoStartNextPowerIrp(kbdIrp);
|
|
IoCompleteRequest(kbdIrp, IO_NO_INCREMENT);
|
|
IoReleaseRemoveLock(&Globals.KeyboardExtension->RemoveLock,
|
|
kbdIrp);
|
|
kbdIrp = NULL;
|
|
}
|
|
|
|
//
|
|
// The passed in Irp has just been completed; by returning more
|
|
// processing required, it will not be double completed
|
|
//
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else {
|
|
RtlZeroMemory(item, sizeof(*item));
|
|
|
|
if (MOUSE_STARTED()) {
|
|
SET_RECORD_STATE(Globals.MouseExtension,
|
|
RECORD_RESUME_FROM_POWER);
|
|
}
|
|
|
|
Print(DBG_POWER_INFO, ("queueing work item for init\n"));
|
|
|
|
item->KeyboardPowerIrp = kbdIrp;
|
|
item->MousePowerIrp = mouIrp;
|
|
|
|
ExInitializeWorkItem(&item->Item, I8xReinitializeHardware, item);
|
|
ExQueueWorkItem(&item->Item, DelayedWorkQueue);
|
|
}
|
|
}
|
|
else if (item != NULL) {
|
|
Print(DBG_POWER_NOISE,("freeing unused item %p\n", item));
|
|
ExFreePool(item);
|
|
item = NULL;
|
|
}
|
|
|
|
if (failMouIrp) {
|
|
Print(DBG_POWER_INFO | DBG_POWER_ERROR,
|
|
("failing successful mouse irp %p because kbd failed power up\n",
|
|
mouIrp));
|
|
|
|
PoStartNextPowerIrp(mouIrp);
|
|
IoCompleteRequest(mouIrp, IO_NO_INCREMENT);
|
|
IoReleaseRemoveLock(&Globals.MouseExtension->RemoveLock, mouIrp);
|
|
mouIrp = NULL;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
Print(DBG_POWER_INFO | DBG_POWER_ERROR,
|
|
("irp %p failed, starting next\n", Irp));
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
Irp = NULL;
|
|
ASSERT(status != STATUS_MORE_PROCESSING_REQUIRED);
|
|
}
|
|
|
|
return status;
|
|
}
|