windows-nt/Source/XPSP1/NT/drivers/input/mouser/detect.c
2020-09-26 16:20:57 +08:00

392 lines
11 KiB
C

/*++
Copyright (c) 1997-1998 Microsoft Corporation, All Rights Reserved
Module Name:
detect.c
Abstract:
Detection of surprise removal of the mouse.
Environment:
Kernel mode only.
Notes:
Revision History:
--*/
#include "stdarg.h"
#include "stdio.h"
#include "string.h"
#include "ntddk.h"
#include "mouser.h"
#include "sermlog.h"
#include "debug.h"
VOID
SerialMouseSerialMaskEventWorker(
PDEVICE_OBJECT DeviceObject,
PIO_WORKITEM Item
);
NTSTATUS
SerialMouseSerialMaskEventComplete (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
SerialMouseSendWaitMaskIrp(
IN PDEVICE_EXTENSION DeviceExtension
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, SerialMouseSerialMaskEventWorker)
#pragma alloc_text(PAGE, SerialMouseStartDetection)
#pragma alloc_text(PAGE, SerialMouseStopDetection)
#pragma alloc_text(PAGE, SerialMouseSendWaitMaskIrp)
#endif
NTSTATUS
SerialMouseSendWaitMaskIrp(
IN PDEVICE_EXTENSION DeviceExtension
)
{
PIRP irp;
PIO_STACK_LOCATION next;
NTSTATUS status;
PAGED_CODE();
irp = DeviceExtension->DetectionIrp;
DeviceExtension->SerialEventBits = 0x0;
//
// Will be released in the completion routine
//
status = IoAcquireRemoveLock (&DeviceExtension->RemoveLock, irp);
if (!NT_SUCCESS(status)) {
return status;
}
IoReuseIrp(irp, STATUS_SUCCESS);
next = IoGetNextIrpStackLocation(irp);
next->MajorFunction = IRP_MJ_DEVICE_CONTROL;
next->Parameters.DeviceIoControl.IoControlCode = IOCTL_SERIAL_WAIT_ON_MASK;
next->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ULONG);
irp->AssociatedIrp.SystemBuffer = &DeviceExtension->SerialEventBits;
//
// Hook a completion routine for when the device completes.
//
IoSetCompletionRoutine(irp,
SerialMouseSerialMaskEventComplete,
DeviceExtension,
TRUE,
TRUE,
TRUE);
return IoCallDriver(DeviceExtension->TopOfStack, irp);
}
VOID
SerialMouseStartDetection(
PDEVICE_EXTENSION DeviceExtension
)
/*++
Routine Description
This will cancel any previous set on the timer and queue the timer for
DetectionTimeout # of seconds and repeatedly trigger the timer every
DetectionTimeout # of seconds.
Arguments:
DeviceExtension - pointer to the device extension
Return Value:
None
--*/
{
IO_STATUS_BLOCK iosb;
KEVENT event;
ULONG waitMask, bits = 0x0;
NTSTATUS status;
PDEVICE_OBJECT self;
ULONG statusBits[] = {
SERIAL_DSR_STATE,
SERIAL_CTS_STATE,
0x0
};
ULONG eventBits[] = {
SERIAL_EV_DSR,
SERIAL_EV_CTS,
};
int i;
PAGED_CODE();
//
// Check to see if removal detection was turned off in the registry
//
if (DeviceExtension->WaitEventMask == 0xffffffff) {
DeviceExtension->DetectionSupported = FALSE;
return;
}
KeInitializeEvent(&event, NotificationEvent, FALSE);
if (!DeviceExtension->WaitEventMask) {
status = SerialMouseIoSyncIoctlEx(IOCTL_SERIAL_GET_MODEMSTATUS,
DeviceExtension->TopOfStack,
&event,
&iosb,
NULL,
0,
&bits,
sizeof(ULONG)
);
Print(DeviceExtension, DBG_SS_NOISE,
("get modem status, NTSTATUS = 0x%x, bits = 0x%x\n",
status, bits));
if (!NT_SUCCESS(status) || !bits) {
Print(DeviceExtension, DBG_SS_ERROR,
("modem status failed, status = 0x%x, bits are 0x%x\n",
status, bits));
DeviceExtension->ModemStatusBits = 0x0;
DeviceExtension->DetectionSupported = FALSE;
return;
}
DeviceExtension->ModemStatusBits = bits;
for (i = 0, waitMask = 0x0; statusBits[i] != 0x0; i++) {
if (bits & statusBits[i]) {
waitMask |= eventBits[i];
}
}
Print(DeviceExtension, DBG_SS_NOISE,
("event wait bits are 0x%x\n", waitMask));
}
else {
waitMask = DeviceExtension->WaitEventMask;
}
status = SerialMouseIoSyncIoctlEx(IOCTL_SERIAL_SET_WAIT_MASK,
DeviceExtension->TopOfStack,
&event,
&iosb,
&waitMask,
sizeof(ULONG),
NULL,
0);
if (!NT_SUCCESS(status)) {
Print(DeviceExtension, DBG_SS_ERROR,
("set mask failed, status = 0x%x\n", status));
DeviceExtension->DetectionSupported = FALSE;
return;
}
self = DeviceExtension->Self;
if (!DeviceExtension->DetectionIrp) {
if (!(DeviceExtension->DetectionIrp =
IoAllocateIrp(self->StackSize, FALSE))) {
DeviceExtension->DetectionSupported = FALSE;
return;
}
}
status = SerialMouseSendWaitMaskIrp(DeviceExtension);
Print(DeviceExtension, DBG_SS_NOISE, ("set wait event status = 0x%x\n", status));
if (NT_SUCCESS(status)) {
DeviceExtension->DetectionSupported = TRUE;
}
else {
IoCancelIrp(DeviceExtension->DetectionIrp);
DeviceExtension->DetectionSupported = FALSE;
}
}
VOID
SerialMouseStopDetection(
PDEVICE_EXTENSION DeviceExtension
)
{
PAGED_CODE();
if (!DeviceExtension->DetectionSupported) {
return;
}
if (!DeviceExtension->RemovalDetected) {
IoCancelIrp(DeviceExtension->DetectionIrp);
}
}
NTSTATUS
SerialMouseSerialMaskEventComplete (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) Context;
PIO_WORKITEM item;
NTSTATUS status;
BOOLEAN killMouse = FALSE;
//
// DeviceObject is NULL b/c this driver was the one that allocated and sent
// the irp, we must use deviceExtension->Self instead.
//
UNREFERENCED_PARAMETER(DeviceObject);
if (!deviceExtension->Removed && !deviceExtension->SurpriseRemoved) {
item = IoAllocateWorkItem(deviceExtension->Self);
if (!item) {
//
// Well, we can't allocate the work item, so lets invalidate our device
// state and hope everything gets torn down.
//
killMouse = TRUE;
}
else {
status = IoAcquireRemoveLock(&deviceExtension->RemoveLock, item);
if (NT_SUCCESS(status)) {
IoQueueWorkItem (item,
SerialMouseSerialMaskEventWorker,
DelayedWorkQueue,
item);
}
else {
killMouse = TRUE;
}
}
}
if (killMouse) {
deviceExtension->RemovalDetected = TRUE;
IoInvalidateDeviceState(deviceExtension->PDO);
}
IoReleaseRemoveLock (&deviceExtension->RemoveLock,
deviceExtension->DetectionIrp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID
SerialMouseSerialMaskEventWorker(
PDEVICE_OBJECT DeviceObject,
PIO_WORKITEM Item
)
{
IO_STATUS_BLOCK iosb;
PIRP irp;
KEVENT event;
NTSTATUS status;
ULONG bits;
BOOLEAN removeSelf = FALSE;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PAGED_CODE();
irp = deviceExtension->DetectionIrp;
KeInitializeEvent(&event, NotificationEvent, FALSE);
switch (irp->IoStatus.Status) {
case STATUS_SUCCESS:
Print(deviceExtension, DBG_SS_NOISE,
("SerialEventBits are 0x%x\n", deviceExtension->SerialEventBits));
bits = 0x0;
status = SerialMouseIoSyncIoctlEx(IOCTL_SERIAL_GET_MODEMSTATUS,
deviceExtension->TopOfStack,
&event,
&iosb,
NULL,
0,
&bits,
sizeof(ULONG)
);
Print(deviceExtension, DBG_SS_NOISE,
("get modem status, NTSTATUS = 0x%x, bits = 0x%x, MSB = 0x%x\n",
status, bits, deviceExtension->ModemStatusBits));
//
// Make sure that the lines truly changed
//
if (deviceExtension->ModemStatusBits == bits) {
//
// Resend the detection irp
//
SerialMouseSendWaitMaskIrp(deviceExtension);
}
else {
//
// The lines have changed, it is a hot removal
//
Print(deviceExtension, DBG_SS_NOISE, ("device hot removed!\n"));
SerialMouseIoSyncInternalIoctl(IOCTL_INTERNAL_SERENUM_REMOVE_SELF,
deviceExtension->TopOfStack,
&event,
&iosb);
deviceExtension->RemovalDetected = TRUE;
}
break;
case STATUS_CANCELLED:
//
// We get here if the user manually removes the device (ie through the
// device manager) and we send the clean up irp down the stack
//
Print(deviceExtension, DBG_SS_NOISE, ("wait cancelled!\n"));
if (deviceExtension->PowerState != PowerDeviceD0 &&
!deviceExtension->PoweringDown) {
deviceExtension->RemovalDetected = TRUE;
}
break;
default:
Print(deviceExtension, DBG_SS_ERROR,
("unknown status in mask event (0x%x)\n",
irp->IoStatus.Status));
TRAP();
}
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Item);
IoFreeWorkItem(Item);
}