Windows-Server-2003/termsrv/drivers/termdd/ptdrvpnp.c

803 lines
21 KiB
C

/*++
Copyright (c) 1997-1999 Microsoft Corporation, All Rights Reserved
Module Name:
ptdrvpnp.c
Abstract:
This module contains general PnP code for the RDP remote port driver.
Environment:
Kernel mode.
Revision History:
02/12/99 - Initial Revision based on pnpi8042 driver
--*/
#include <precomp.h>
#pragma hdrstop
#include "ptdrvcom.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PtAddDevice)
#pragma alloc_text(PAGE, PtManuallyRemoveDevice)
#pragma alloc_text(PAGE, PtPnP)
//#pragma alloc_text(PAGE, PtPower)
#pragma alloc_text(PAGE, PtSendIrpSynchronously)
#endif
NTSTATUS
PtAddDevice (
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;
IO_ERROR_LOG_PACKET errorLogEntry;
PDEVICE_OBJECT device;
NTSTATUS status = STATUS_SUCCESS;
ULONG maxSize;
UNICODE_STRING fullRDPName;
UNICODE_STRING baseRDPName;
UNICODE_STRING deviceNameSuffix;
PAGED_CODE();
Print(DBG_PNP_TRACE, ("enter Add Device: %ld \n", Globals.ulDeviceNumber));
//
// Initialize the various unicode structures for forming the device name.
//
if (Globals.ulDeviceNumber == 0)
RtlInitUnicodeString(&fullRDPName, RDP_CONSOLE_BASE_NAME0);
else
RtlInitUnicodeString(&fullRDPName, RDP_CONSOLE_BASE_NAME1);
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!!!
FILE_DEVICE_SECURE_OPEN,// device characteristics
FALSE, // exclusive
&device // new device
);
if (!NT_SUCCESS(status)) {
Print(DBG_SS_TRACE, ("Add Device failed! (0x%x) \n", status));
return status;
}
Globals.ulDeviceNumber++;
RtlZeroMemory(device->DeviceExtension, maxSize);
//
// Set up the device type
//
*((ULONG *)(device->DeviceExtension)) = DEV_TYPE_PORT;
commonData = GET_COMMON_DATA(device->DeviceExtension);
RtlInitUnicodeString(&commonData->DeviceName, fullRDPName.Buffer);
commonData->TopOfStack = IoAttachDeviceToDeviceStack(device, PDO);
ASSERT(commonData->TopOfStack);
commonData->Self = device;
commonData->PDO = PDO;
device->Flags |= DO_BUFFERED_IO;
device->Flags &= ~DO_DEVICE_INITIALIZING;
Print(DBG_PNP_TRACE, ("Add Device (0x%x)\n", status));
return status;
}
NTSTATUS
PtSendIrpSynchronously (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
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,
PtPnPComplete,
&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;
}
return status;
}
NTSTATUS
PtPnPComplete (
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_SUCCESSFUL if successful,
an valid NTSTATUS error code otherwise
--*/
{
PIO_STACK_LOCATION stack;
NTSTATUS status;
UNREFERENCED_PARAMETER (DeviceObject);
status = STATUS_SUCCESS;
stack = IoGetCurrentIrpStackLocation(Irp);
if (Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
KeSetEvent(Event, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
PtPnP (
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);
Print(DBG_PNP_TRACE,
("PtPnP (%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 = PtSendIrpSynchronously(commonData->TopOfStack, Irp);
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 (commonData->IsKeyboard) {
status = PtKeyboardStartDevice(
(PPORT_KEYBOARD_EXTENSION) DeviceObject->DeviceExtension,
stack->Parameters.StartDevice.AllocatedResourcesTranslated
);
}
else {
status = PtMouseStartDevice(
(PPORT_MOUSE_EXTENSION) DeviceObject->DeviceExtension,
stack->Parameters.StartDevice.AllocatedResourcesTranslated
);
}
if (NT_SUCCESS(status)) {
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_QUERY_PNP_DEVICE_STATE:
status = PtSendIrpSynchronously(commonData->TopOfStack, Irp);
//
// If the lower filter does not support this Irp, this is
// OK, we can ignore this error
//
if (status == STATUS_NOT_SUPPORTED ||
status == STATUS_INVALID_DEVICE_REQUEST) {
status = STATUS_SUCCESS;
}
//
// do stuff here...
//
if (NT_SUCCESS(status)) {
if (commonData->ManuallyRemoved &&
!(commonData->IsKeyboard ? KEYBOARD_PRESENT():MOUSE_PRESENT())) {
commonData->Started = FALSE;
(PNP_DEVICE_STATE) Irp->IoStatus.Information |=
(PNP_DEVICE_REMOVED | PNP_DEVICE_DONT_DISPLAY_IN_UI);
}
//
// In all cases this device must be disableable
//
(PNP_DEVICE_STATE) Irp->IoStatus.Information &= ~PNP_DEVICE_NOT_DISABLEABLE;
//
// Don't show it in the device manager
//
(PNP_DEVICE_STATE) Irp->IoStatus.Information |= PNP_DEVICE_DONT_DISPLAY_IN_UI;
} 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 = (commonData->ManuallyRemoved ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
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 = PtSendIrpSynchronously(commonData->TopOfStack, Irp);
//
// If the lower filter does not support this Irp, this is
// OK, we can ignore this error
//
if (status == STATUS_NOT_SUPPORTED ||
status == STATUS_INVALID_DEVICE_REQUEST) {
status = STATUS_SUCCESS;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
break;
case IRP_MN_REMOVE_DEVICE:
Print(DBG_PNP_TRACE, ("remove device\n"));
if (commonData->Started && !commonData->ManuallyRemoved) {
//
// This should never happen. The only way we can get a remove is if
// a start has failed.
//
// NOTE: Again, this should never happen for i8042prt, but any
// other input port driver should allow itself to be removed
// (see sermouse.sys on how to do this correctly)
//
Print(DBG_PNP_ERROR, ("Cannot remove a started device!!!\n"));
ASSERT(FALSE);
}
if (commonData->Initialized) {
IoWMIRegistrationControl(commonData->Self,
WMIREG_ACTION_DEREGISTER
);
}
ExAcquireFastMutexUnsafe(&Globals.DispatchMutex);
if (commonData->IsKeyboard) {
PtKeyboardRemoveDevice(DeviceObject);
}
ExReleaseFastMutexUnsafe(&Globals.DispatchMutex);
//
// Nothing has been allocated or connected
//
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation(Irp);
status = IoCallDriver(commonData->TopOfStack, Irp);
IoDetachDevice(commonData->TopOfStack);
IoDeleteDevice(DeviceObject);
break;
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_FILTER_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,
("PtPnP (%s) exit (status=0x%x)\n",
commonData->IsKeyboard ? "kb" : "mou",
status
));
return status;
}
LONG
PtManuallyRemoveDevice(
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 (CommonData->IsKeyboard) {
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->ManuallyRemoved = TRUE;
IoInvalidateDeviceState(CommonData->PDO);
return deviceCount;
}
NTSTATUS
PtPower (
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"
));
switch(stack->MinorFunction) {
case IRP_MN_WAIT_WAKE:
Print(DBG_POWER_NOISE, ("Got IRP_MN_WAIT_WAKE\n" ));
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) {
Print(DBG_POWER_TRACE, ("not a device power irp\n"));
break;
}
//
// Check for no change in state, and if none, do nothing
//
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_TRACE, ("Powering up to PowerDeviceD0\n"));
commonData->IsKeyboard ? KEYBOARD_POWERED_UP_STARTED()
: MOUSE_POWERED_UP_STARTED();
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
PtPowerUpToD0Complete,
NULL,
TRUE, // on success
TRUE, // on error
TRUE // on cancel
);
//
// PoStartNextPowerIrp() gets called when the irp gets completed
//
IoMarkIrpPending(Irp);
PoCallDriver(commonData->TopOfStack, Irp);
return STATUS_PENDING;
case PowerDeviceD1:
case PowerDeviceD2:
case PowerDeviceD3:
Print(DBG_POWER_TRACE,
("Powering down to PowerDeviceD%d\n",
stack->Parameters.Power.State.DeviceState-1
));
PoSetPowerState(DeviceObject,
stack->Parameters.Power.Type,
stack->Parameters.Power.State
);
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;
IoCopyCurrentIrpStackLocationToNext(Irp); // skip ?
PoStartNextPowerIrp(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
PtPowerUpToD0Complete(
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;
PCOMMON_DATA commonData;
PIO_STACK_LOCATION stack;
KIRQL irql;
UNREFERENCED_PARAMETER(Context);
commonData = GET_COMMON_DATA(DeviceObject->DeviceExtension);
Print(DBG_POWER_TRACE,
("PowerUpToD0Complete (%s), Enter\n",
commonData->IsKeyboard ? "kb" : "mouse"
));
KeAcquireSpinLock(&Globals.ControllerData->PowerUpSpinLock, &irql);
if (NT_SUCCESS(Irp->IoStatus.Status)) {
commonData->IsKeyboard ? KEYBOARD_POWERED_UP_SUCCESSFULLY()
: MOUSE_POWERED_UP_SUCCESSFULLY();
status = STATUS_MORE_PROCESSING_REQUIRED;
}
else {
commonData->IsKeyboard ? KEYBOARD_POWERED_UP_FAILED()
: MOUSE_POWERED_UP_FAILED();
status = Irp->IoStatus.Status;
#if DBG
if (commonData->IsKeyboard) {
ASSERT(KEYBOARD_POWERED_UP_FAILED());
}
else {
ASSERT(MOUSE_POWERED_UP_FAILED());
}
#endif // DBG
}
KeReleaseSpinLock(&Globals.ControllerData->PowerUpSpinLock, irql);
if (NT_SUCCESS(status)) {
Print(DBG_SS_NOISE, ("reinit, status == 0x%x\n", status));
stack = IoGetCurrentIrpStackLocation(Irp);
ASSERT(stack->Parameters.Power.State.DeviceState == PowerDeviceD0);
commonData->PowerState = stack->Parameters.Power.State.DeviceState;
commonData->ShutdownType = PowerActionNone;
PoSetPowerState(commonData->Self,
stack->Parameters.Power.Type,
stack->Parameters.Power.State
);
}
//
// Complete the irp
//
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
PoStartNextPowerIrp(Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
//
// Reset PoweredDevices so that we can keep track of the powered device
// the next time the machine is power managed off.
//
CLEAR_POWERUP_FLAGS();
return status;
}