windows-nt/Source/XPSP1/NT/drivers/wdm/input/tabletpc/mutohpen/pnp.c
2020-09-26 16:20:57 +08:00

623 lines
20 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
pnp.c
Abstract: This module contains code to handle PnP and Power IRPs.
Environment:
Kernel mode
Author:
Michael Tsang (MikeTs) 13-Mar-2000
Revision History:
--*/
#include "pch.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, HpenPnp)
#pragma alloc_text(PAGE, HpenPower)
#pragma alloc_text(PAGE, InitDevice)
#pragma alloc_text(PAGE, RemoveDevice)
#pragma alloc_text(PAGE, SendSyncIrp)
#endif
/*****************************************************************************
*
* @doc EXTERNAL
*
* @func NTSTATUS | HpenPnp |
* Plug and Play dispatch routine for this driver.
*
* @parm IN PDEVICE_OBJECT | DevObj | Pointer to the device object.
* @parm IN PIRP | Irp | Pointer to an I/O request packet.
*
* @rvalue SUCCESS | returns STATUS_SUCCESS
* @rvalue FAILURE | returns NT status code
*
*****************************************************************************/
NTSTATUS EXTERNAL
HpenPnp(
IN PDEVICE_OBJECT DevObj,
IN PIRP Irp
)
{
PROCNAME("HpenPnp")
NTSTATUS status;
PIO_STACK_LOCATION irpsp;
PDEVICE_EXTENSION devext;
PAGED_CODE();
irpsp = IoGetCurrentIrpStackLocation(Irp);
ENTER(1, ("(DevObj=%p,Irp=%p,IrpSp=%p,Minor=%s)\n",
DevObj, Irp, irpsp,
LookupName(irpsp->MinorFunction, PnPMinorFnNames)));
devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj);
status = IoAcquireRemoveLock(&devext->RemoveLock, Irp);
if (!NT_SUCCESS(status))
{
//
// Someone sent us another plug and play IRP after removed
//
ERRPRINT(("received PnP IRP after device was removed\n"));
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
else
{
BOOLEAN fSkipIt = FALSE;
switch (irpsp->MinorFunction)
{
case IRP_MN_START_DEVICE:
ASSERT(!(devext->dwfHPen & HPENF_DEVICE_STARTED));
//
// Forward the IRP down the stack
//
status = SendSyncIrp(devext->SerialDevObj, Irp, TRUE);
if (NT_SUCCESS(status))
{
status = InitDevice(DevObj, Irp);
if (NT_SUCCESS(status))
{
devext->dwfHPen |= HPENF_DEVICE_STARTED;
}
}
else
{
ERRPRINT(("failed to forward start IRP (status=%x)\n",
status));
}
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
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. Whatever access is
// required must be done before Irp passed on.
//
if (devext->dwfHPen & HPENF_DEVICE_STARTED)
{
devext->dwfHPen &= ~HPENF_DEVICE_STARTED;
//
// I don't need to cancel any pending read IRPs since
// those IRPs originated from hidclass and presumably
// hidclass will cancel them.
//
}
//
// 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.
//
fSkipIt = TRUE;
Irp->IoStatus.Status = STATUS_SUCCESS;
break;
case IRP_MN_REMOVE_DEVICE:
case IRP_MN_SURPRISE_REMOVAL:
//
// The PlugPlay system has detected the removal of this device.
// We have no choice but to detach and delete the device object.
// (If we wanted to express an interest in preventing this
// removal, we should have filtered the query remove and query
// stop routines.)
// Note: we might receive a remove WITHOUT first receiving a
// stop.
//
//
// Make sure we do not allow more IRPs to start touching the
// device.
//
devext->dwfHPen &= ~HPENF_DEVICE_STARTED;
devext->dwfHPen |= HPENF_DEVICE_REMOVED;
RemoveDevice(DevObj, Irp);
//
// Send on the remove IRP
//
fSkipIt = TRUE;
Irp->IoStatus.Status = STATUS_SUCCESS;
break;
case IRP_MN_QUERY_CAPABILITIES:
status = SendSyncIrp(GET_NEXT_DEVICE_OBJECT(DevObj), Irp, TRUE);
if (NT_SUCCESS(status))
{
PDEVICE_CAPABILITIES devcaps;
devcaps = irpsp->Parameters.DeviceCapabilities.Capabilities;
if (devcaps != NULL)
{
SYSTEM_POWER_STATE i;
//
// This device is built-in to the system, so it should
// be impossible to surprise remove this device, but
// we will handle it anyway.
//
devcaps->SurpriseRemovalOK = TRUE;
//
// While the underlying serial bus might be able to
// wake the machine from low power (via wake on ring),
// the tablet cannot.
//
devcaps->SystemWake = PowerSystemUnspecified;
devcaps->DeviceWake = PowerDeviceUnspecified;
devcaps->WakeFromD0 =
devcaps->WakeFromD1 =
devcaps->WakeFromD2 =
devcaps->WakeFromD3 = FALSE;
devcaps->DeviceState[PowerSystemWorking] =
PowerDeviceD0;
for (i = PowerSystemSleeping1;
i < PowerSystemMaximum;
i++)
{
devcaps->DeviceState[i] = PowerDeviceD3;
}
}
}
IoCompleteRequest(Irp, IO_NO_INCREMENT);
break;
default:
fSkipIt = TRUE;
break;
}
if (fSkipIt)
{
IoSkipCurrentIrpStackLocation(Irp);
ENTER(2, (".IoCallDriver(DevObj=%p,Irp=%p)\n",
GET_NEXT_DEVICE_OBJECT(DevObj), Irp));
status = IoCallDriver(GET_NEXT_DEVICE_OBJECT(DevObj), Irp);
EXIT(2, (".IoCallDriver=%x\n", status));
}
if (irpsp->MinorFunction == IRP_MN_REMOVE_DEVICE)
{
//
// Wait for the remove lock to free.
//
IoReleaseRemoveLockAndWait(&devext->RemoveLock, Irp);
IoFreeWorkItem(devext->ReadWorkItem[0].WorkItem);
IoFreeWorkItem(devext->ReadWorkItem[1].WorkItem);
}
else
{
IoReleaseRemoveLock(&devext->RemoveLock, Irp);
}
}
EXIT(1, ("=%x\n", status));
return status;
} //HpenPnp
/*****************************************************************************
*
* @doc EXTERNAL
*
* @func NTSTATUS | HpenPower | The power dispatch routine for this driver.
*
* @parm IN PDEVICE_OBJECT | DevObj | Points to the device object.
* @parm IN PIRP | Irp | Points to an I/O request packet.
*
* @rvalue SUCCESS | returns STATUS_SUCCESS
* @rvalue FAILURE | returns NT status code
*
*****************************************************************************/
NTSTATUS EXTERNAL
HpenPower(
IN PDEVICE_OBJECT DevObj,
IN PIRP Irp
)
{
PROCNAME("HpenPower")
NTSTATUS status;
PDEVICE_EXTENSION devext;
PAGED_CODE();
ENTER(1, ("(DevObj=%p,Irp=%p,Minor=%s)\n",
DevObj, Irp,
LookupName(IoGetCurrentIrpStackLocation(Irp)->MinorFunction,
PowerMinorFnNames)));
devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj);
status = IoAcquireRemoveLock(&devext->RemoveLock, Irp);
if (!NT_SUCCESS(status))
{
//
// Someone sent us another power IRP after removed
//
ERRPRINT(("received Power IRP after device was removed\n"));
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
else
{
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(Irp);
POWER_STATE_TYPE PowerType = irpsp->Parameters.Power.Type;
POWER_STATE NewPowerState = irpsp->Parameters.Power.State;
BOOLEAN fSkipCalldown = FALSE;
switch (irpsp->MinorFunction)
{
case IRP_MN_SET_POWER:
//
// We only handle DevicePowerState IRPs that change
// power states.
//
if ((PowerType == DevicePowerState) &&
(NewPowerState.DeviceState != devext->PowerState))
{
DBGPRINT(1, ("power state change (%s->%s)\n",
LookupName(devext->PowerState,
PowerStateNames),
LookupName(NewPowerState.DeviceState,
PowerStateNames)));
switch (NewPowerState.DeviceState)
{
case PowerDeviceD0:
//
// Transitioning from a low D state to D0.
//
status = SendSyncIrp(GET_NEXT_DEVICE_OBJECT(DevObj),
Irp,
TRUE);
if (NT_SUCCESS(status))
{
PoSetPowerState(DevObj,
PowerType,
NewPowerState);
OemWakeupDevice(devext);
devext->PowerState = NewPowerState.DeviceState;
devext->dwfHPen &= ~HPENF_TABLET_STANDBY;
}
Irp->IoStatus.Status = status;
IoReleaseRemoveLock(&devext->RemoveLock, Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
fSkipCalldown = TRUE;
break;
case PowerDeviceD1:
case PowerDeviceD2:
case PowerDeviceD3:
//BUGBUG: OemStandbyDevice(devext);
PoSetPowerState(DevObj, PowerType, NewPowerState);
devext->PowerState = NewPowerState.DeviceState;
//BUGBUG: devext->dwfHPen |= HPENF_TABLET_STANDBY;
IoReleaseRemoveLock(&devext->RemoveLock, Irp);
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation(Irp);
ENTER(2, (".PoCallDriver(DevObj=%p,Irp=%p)\n",
GET_NEXT_DEVICE_OBJECT(DevObj), Irp));
status = PoCallDriver(
GET_NEXT_DEVICE_OBJECT(DevObj),
Irp);
EXIT(2, (".PoCallDriver=%x\n", status));
fSkipCalldown = TRUE;
break;
}
}
break;
case IRP_MN_WAIT_WAKE:
case IRP_MN_QUERY_POWER:
break;
default:
ERRPRINT(("unsupported power IRP (%s)\n",
LookupName(irpsp->MinorFunction, PowerMinorFnNames)));
break;
}
if (!fSkipCalldown)
{
IoSkipCurrentIrpStackLocation(Irp);
ENTER(2, (".PoCallDriver(DevObj=%p,Irp=%p)\n",
GET_NEXT_DEVICE_OBJECT(DevObj), Irp));
status = PoCallDriver(GET_NEXT_DEVICE_OBJECT(DevObj), Irp);
EXIT(2, (".PoCallDriver=%x\n", status));
IoReleaseRemoveLock(&devext->RemoveLock, Irp);
}
}
EXIT(1, ("=%x\n", status));
return status;
} //HpenPower
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | InitDevice |
* Get the device information and attempt to initialize a
* configuration for a device. If we cannot identify this as a
* valid HID device or configure the device, our start device
* function is failed.
*
* @parm IN PDEVICE_OBJECT | DevObj | Points to the device object.
* @parm IN PIRP | Irp | Points to an I/O request packet.
*
* @rvalue SUCCESS | returns STATUS_SUCCESS
* @rvalue FAILURE | returns NT status code
*
*****************************************************************************/
NTSTATUS INTERNAL
InitDevice(
IN PDEVICE_OBJECT DevObj,
IN PIRP Irp
)
{
PROCNAME("InitDevice")
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION devext;
PAGED_CODE();
ENTER(2, ("(DevObj=%p,Irp=%p)\n", DevObj, Irp));
devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj);
if (!(devext->dwfHPen & HPENF_SERIAL_OPENED))
{
//
// If a create hasn't been sent down the stack, send one now.
// The serial port driver requires a create before it will accept
// any reads or ioctls.
//
PIO_STACK_LOCATION irpspNext = IoGetNextIrpStackLocation(Irp);
NTSTATUS PrevStatus = Irp->IoStatus.Status;
ULONG_PTR PrevInfo = Irp->IoStatus.Information;
RtlZeroMemory(irpspNext, sizeof(*irpspNext));
irpspNext->MajorFunction = IRP_MJ_CREATE;
status = SendSyncIrp(GET_NEXT_DEVICE_OBJECT(DevObj), Irp, FALSE);
if (NT_SUCCESS(status))
{
devext->dwfHPen |= HPENF_SERIAL_OPENED;
Irp->IoStatus.Status = PrevStatus;
Irp->IoStatus.Information = PrevInfo;
}
else
{
ERRPRINT(("failed to send CREATE IRP to serial port (status=%x)\n",
status));
}
}
if (NT_SUCCESS(status))
{
status = OemInitSerialPort(devext);
if (NT_SUCCESS(status))
{
status = OemInitDevice(devext);
}
}
EXIT(2, ("=%x\n", status));
return status;
} //InitDevice
/*****************************************************************************
*
* @doc INTERNAL
*
* @func VOID | RemoveDevice | FDO Remove routine
*
* @parm IN PDEVICE_OBJECT | DevObj | Points to the device object.
* @parm IN PIRP | Irp | Points to an I/O request packet.
*
*****************************************************************************/
VOID INTERNAL
RemoveDevice(
PDEVICE_OBJECT DevObj,
PIRP Irp
)
{
PROCNAME("RemoveDevice")
PDEVICE_EXTENSION devext;
PAGED_CODE();
ENTER(2, ("(DevObj=%p,Irp=%p)\n", DevObj, Irp));
devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj);
ASSERT(devext->dwfHPen & HPENF_DEVICE_REMOVED);
if (devext->dwfHPen & HPENF_SERIAL_OPENED)
{
PIO_STACK_LOCATION irpspNext;
OemRemoveDevice(devext);
irpspNext = IoGetNextIrpStackLocation(Irp);
RtlZeroMemory(irpspNext, sizeof(*irpspNext));
irpspNext->MajorFunction = IRP_MJ_CLEANUP;
SendSyncIrp(GET_NEXT_DEVICE_OBJECT(DevObj), Irp, FALSE);
irpspNext = IoGetNextIrpStackLocation(Irp);
RtlZeroMemory(irpspNext, sizeof(*irpspNext));
irpspNext->MajorFunction = IRP_MJ_CLOSE;
SendSyncIrp(GET_NEXT_DEVICE_OBJECT(DevObj), Irp, FALSE);
devext->dwfHPen &= ~HPENF_SERIAL_OPENED;
}
#ifdef DEBUG
ExAcquireFastMutex(&gmutexDevExtList);
RemoveEntryList(&devext->List);
ExReleaseFastMutex (&gmutexDevExtList);
#endif
EXIT(2, ("!\n"));
return;
} //RemoveDevice
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | SendSyncIrp |
* Send an IRP synchronously down the stack.
*
* @parm IN PDEVICE_OBJECT | DevObj | Points to the device object.
* @parm IN PIRP | Irp | Points to the IRP.
* @parm IN BOOLEAN | fCopyToNext | if TRUE, copy the irpsp to next location.
*
* @rvalue SUCCESS | returns STATUS_SUCCESS
* @rvalue FAILURE | returns NT status code
*
*****************************************************************************/
NTSTATUS INTERNAL
SendSyncIrp(
IN PDEVICE_OBJECT DevObj,
IN PIRP Irp,
IN BOOLEAN fCopyToNext
)
{
PROCNAME("SendSyncIrp")
NTSTATUS status;
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(Irp);
KEVENT event;
PAGED_CODE();
ENTER(2, ("(DevObj=%p,Irp=%p,fCopyToNext=%x,MajorFunc=%s)\n",
DevObj, Irp, fCopyToNext,
LookupName(irpsp->MajorFunction, MajorFnNames)));
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
if (fCopyToNext)
{
IoCopyCurrentIrpStackLocationToNext(Irp);
}
IoSetCompletionRoutine(Irp, IrpCompletion, &event, TRUE, TRUE, TRUE);
if (irpsp->MajorFunction == IRP_MJ_POWER)
{
ENTER(2, (".PoCallDriver(DevObj=%p,Irp=%p)\n", DevObj, Irp));
status = PoCallDriver(DevObj, Irp);
EXIT(2, (".IoCallDriver=%x\n", status));
}
else
{
ENTER(2, (".IoCallDriver(DevObj=%p,Irp=%p)\n", DevObj, Irp));
status = IoCallDriver(DevObj, Irp);
EXIT(2, (".IoCallDriver=%x\n", status));
}
if (status == STATUS_PENDING)
{
status = KeWaitForSingleObject(&event,
Executive,
KernelMode,
FALSE,
NULL);
}
if (NT_SUCCESS(status))
{
status = Irp->IoStatus.Status;
}
EXIT(2, ("=%x\n", status));
return status;
} //SendSyncIrp
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | IrpCompletion | Completion routine for all IRPs.
*
* @parm IN PDEVICE_OBJECT | DevObj | Points to the device object.
* @parm IN PIRP | Irp | Points to an I/O request packet.
* @parm IN PKEVENT | Event | Points to the event to notify.
*
* @rvalue STATUS_MORE_PROCESSING_REQUIRED | We want the IRP back
*
*****************************************************************************/
NTSTATUS INTERNAL
IrpCompletion(
IN PDEVICE_OBJECT DevObj,
IN PIRP Irp,
IN PKEVENT Event
)
{
PROCNAME("IrpCompletion")
ENTER(2, ("(DevObj=%p,Irp=%p,Event=%p)\n", DevObj, Irp, Event));
UNREFERENCED_PARAMETER(DevObj);
KeSetEvent(Event, 0, FALSE);
/*
* If the lower driver returned PENDING, mark our stack location as
* pending also. This prevents the IRP's thread from being freed if
* the client's call returns pending.
*/
if (Irp->PendingReturned)
{
IoMarkIrpPending(Irp);
}
EXIT(2, ("=%x\n", STATUS_MORE_PROCESSING_REQUIRED));
return STATUS_MORE_PROCESSING_REQUIRED;
} //IrpCompletion