|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
dispatch.c
Abstract
Dispatch routines for the HID class driver.
Author:
Ervin P.
Environment:
Kernel mode only
Revision History:
--*/
#include "pch.h"
#include <poclass.h>
#include <wdmguid.h>
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, HidpCallDriverSynchronous)
#pragma alloc_text(PAGE, HidpIrpMajorPnp)
#pragma alloc_text(PAGE, HidpFdoPnp)
#pragma alloc_text(PAGE, HidpPdoPnp)
#endif
/*
******************************************************************************** * HidpCallDriver ******************************************************************************** * * */ NTSTATUS HidpCallDriver(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp) { PHIDCLASS_DEVICE_EXTENSION hidDeviceExtension; PHIDCLASS_DRIVER_EXTENSION hidDriverExtension; PIO_STACK_LOCATION irpSp; NTSTATUS status; #if DBG
KIRQL saveIrql; #endif
DBGASSERT((Irp->Type == IO_TYPE_IRP), ("Irp->Type != IO_TYPE_IRP, Irp->Type == %x", Irp->Type), TRUE)
/*
* Update the IRP stack to point to the next location. */ Irp->CurrentLocation--;
if (Irp->CurrentLocation <= 0) { KeBugCheckEx( NO_MORE_IRP_STACK_LOCATIONS, (ULONG_PTR) Irp, 0, 0, 0 ); }
irpSp = IoGetNextIrpStackLocation( Irp ); Irp->Tail.Overlay.CurrentStackLocation = irpSp;
//
// Save a pointer to the device object for this request so that it can
// be used later in completion.
//
irpSp->DeviceObject = DeviceObject;
//
// Get a pointer to the class extension and verify it.
//
hidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension; ASSERT(hidDeviceExtension->Signature == HID_DEVICE_EXTENSION_SIG); ASSERT(!hidDeviceExtension->isClientPdo);
//
// Ditto for the driver extension
//
hidDriverExtension = hidDeviceExtension->fdoExt.driverExt; ASSERT( hidDriverExtension->Signature == HID_DRIVER_EXTENSION_SIG );
//
// Invoke the driver at its dispatch routine entry point.
//
#if DBG
saveIrql = KeGetCurrentIrql(); #endif
/*
* Call down to the minidriver */ status = hidDriverExtension->MajorFunction[irpSp->MajorFunction](DeviceObject, Irp);
#if DBG
if (saveIrql != KeGetCurrentIrql()) { DbgPrint( "IO: HidpCallDriver( Driver ext: %x Device object: %x Irp: %x )\n", hidDriverExtension, DeviceObject, Irp ); DbgPrint( " Irql before: %x != After: %x\n", saveIrql, KeGetCurrentIrql() ); DbgBreakPoint(); } #endif
return status; }
/*
******************************************************************************** * HidpSynchronousCallCompletion ******************************************************************************** * * */ NTSTATUS HidpSynchronousCallCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { PKEVENT event = Context;
DBG_COMMON_ENTRY()
KeSetEvent(event, 0, FALSE);
DBG_COMMON_EXIT() return STATUS_MORE_PROCESSING_REQUIRED; }
/*
******************************************************************************** * HidpCallDriverSynchronous ******************************************************************************** * * */ NTSTATUS HidpCallDriverSynchronous(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp) { KEVENT event; NTSTATUS status; static LARGE_INTEGER timeout = {(ULONG) -50000000, 0xFFFFFFFF };
PAGED_CODE();
KeInitializeEvent(&event, NotificationEvent, FALSE);
IoSetCompletionRoutine(Irp, HidpSynchronousCallCompletion, &event, TRUE, TRUE, TRUE);
status = HidpCallDriver(DeviceObject, Irp);
if (STATUS_PENDING == status) { //
// Wait for 5 seconds. If we don't get a response within said amount
// of time, the device is being unresponsive (happens with some UPS').
// At that point, cancel the irp and return STATUS_IO_TIMEOUT.
//
status = KeWaitForSingleObject(&event, Executive, // wait reason
KernelMode, FALSE, // not alertable
&timeout ); // 5 second timeout
if (status == STATUS_TIMEOUT) { #if DBG
LARGE_INTEGER li; KeQueryTickCount(&li); DBGWARN(("Could not cancel irp. Will have to wait. Time %x.",Irp,li)) #endif
DBGWARN(("Device didn't respond for 5 seconds. Cancelling request. Irp %x",Irp)) IoCancelIrp(Irp); KeWaitForSingleObject(&event, Executive, // wait reason
KernelMode, FALSE, // not alertable
NULL ); // no timeout
#if DBG
KeQueryTickCount(&li); DBGWARN(("Irp conpleted. Time %x.",li)) #endif
//
// If we successfully cancelled the irp, then set the status to
// STATUS_IO_TIMEOUT, otherwise, leave the status alone.
//
status = Irp->IoStatus.Status = (Irp->IoStatus.Status == STATUS_CANCELLED) ? STATUS_IO_TIMEOUT : Irp->IoStatus.Status; } else { //
// The minidriver must always return STATUS_PENDING or STATUS_SUCCESS
// (depending on async or sync completion) and set the real status
// in the status block. We're not expecting anything but success from
// KeWaitForSingleObject, either.
//
status = Irp->IoStatus.Status; } }
DBGSUCCESS(status, FALSE)
return status; }
/*
******************************************************************************** * HidpMajorHandler ******************************************************************************** * * Note: this function should not be pageable because * reads can come in at dispatch level. * */ NTSTATUS HidpMajorHandler(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PHIDCLASS_DEVICE_EXTENSION hidClassExtension; PIO_STACK_LOCATION irpSp; NTSTATUS result; UCHAR majorFunction; BOOLEAN isClientPdo;
DBG_COMMON_ENTRY()
hidClassExtension = (PHIDCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension; ASSERT(hidClassExtension->Signature == HID_DEVICE_EXTENSION_SIG);
//
// Get a pointer to the current stack location and dispatch to the
// appropriate routine.
//
irpSp = IoGetCurrentIrpStackLocation(Irp);
/*
* Keep these privately so we still have it after the IRP completes * or after the device extension is freed on a REMOVE_DEVICE */ majorFunction = irpSp->MajorFunction; isClientPdo = hidClassExtension->isClientPdo;
DBG_LOG_IRP_MAJOR(Irp, majorFunction, isClientPdo, FALSE, 0)
switch (majorFunction){
case IRP_MJ_CLOSE: result = HidpIrpMajorClose( hidClassExtension, Irp ); break;
case IRP_MJ_CREATE: result = HidpIrpMajorCreate( hidClassExtension, Irp ); break;
case IRP_MJ_DEVICE_CONTROL: result = HidpIrpMajorDeviceControl( hidClassExtension, Irp ); break;
case IRP_MJ_INTERNAL_DEVICE_CONTROL: result = HidpIrpMajorINTERNALDeviceControl( hidClassExtension, Irp ); break;
case IRP_MJ_PNP: result = HidpIrpMajorPnp( hidClassExtension, Irp ); break;
case IRP_MJ_POWER: result = HidpIrpMajorPower( hidClassExtension, Irp ); break;
case IRP_MJ_READ: result = HidpIrpMajorRead( hidClassExtension, Irp ); break;
case IRP_MJ_WRITE: result = HidpIrpMajorWrite( hidClassExtension, Irp ); break;
case IRP_MJ_SYSTEM_CONTROL: result = HidpIrpMajorSystemControl( hidClassExtension, Irp ); break;
default: result = HidpIrpMajorDefault( hidClassExtension, Irp ); break; }
DBG_LOG_IRP_MAJOR(Irp, majorFunction, isClientPdo, TRUE, result)
DBG_COMMON_EXIT()
return result; }
/*
******************************************************************************** * HidpIrpMajorDefault ******************************************************************************** * * Handle IRPs with un-handled MAJOR function codes * */ NTSTATUS HidpIrpMajorDefault(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp) { NTSTATUS status; PIO_STACK_LOCATION irpSp;
irpSp = IoGetCurrentIrpStackLocation(Irp);
DBGVERBOSE(("Unhandled IRP, MJ function: %x", irpSp->MajorFunction))
if (HidDeviceExtension->isClientPdo){ /*
* This IRP is bound for the collection-PDO. * Return the default status. */ status = Irp->IoStatus.Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } else { /*
* This IRP is bound for the lower device. * Pass it down the stack. */ FDO_EXTENSION *fdoExt = &HidDeviceExtension->fdoExt; IoCopyCurrentIrpStackLocationToNext(Irp); status = HidpCallDriver(fdoExt->fdo, Irp); }
DBGSUCCESS(status, FALSE) return status; }
/*
******************************************************************************** * HidpIrpMajorClose ******************************************************************************** * * Note: this function cannot be pageable because it * acquires a spinlock. * */ NTSTATUS HidpIrpMajorClose(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp) { NTSTATUS result;
ASSERT(HidDeviceExtension->Signature == HID_DEVICE_EXTENSION_SIG);
if (HidDeviceExtension->isClientPdo){
PIO_STACK_LOCATION irpSp; PHIDCLASS_FILE_EXTENSION fileExtension; PFILE_OBJECT fileObject; KIRQL oldIrql; PDO_EXTENSION *pdoExt; FDO_EXTENSION *fdoExt; ULONG openCount;
pdoExt = &HidDeviceExtension->pdoExt; fdoExt = &pdoExt->deviceFdoExt->fdoExt;
ASSERT(fdoExt->openCount > 0);
Irp->IoStatus.Information = 0;
irpSp = IoGetCurrentIrpStackLocation( Irp ); fileObject = irpSp->FileObject; fileExtension = (PHIDCLASS_FILE_EXTENSION)fileObject->FsContext;
openCount = InterlockedDecrement(&fdoExt->openCount);
if (fileExtension){ PHIDCLASS_COLLECTION classCollection;
ASSERT(fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG);
/*
* Get a pointer to the collection that our file extension is queued on. */ classCollection = GetHidclassCollection(fdoExt, pdoExt->collectionNum); if (classCollection){
DBGVERBOSE((" HidpIrpMajorClose: closing collection w/ usagePage=%xh, usage=%xh.", fdoExt->deviceDesc.CollectionDesc[pdoExt->collectionIndex].UsagePage, fdoExt->deviceDesc.CollectionDesc[pdoExt->collectionIndex].Usage))
if (fdoExt->state == DEVICE_STATE_REMOVED){
KeAcquireSpinLock( &classCollection->FileExtensionListSpinLock, &oldIrql ); RemoveEntryList(&fileExtension->FileList); KeReleaseSpinLock( &classCollection->FileExtensionListSpinLock, oldIrql );
if (fileExtension->isSecureOpen) { KeAcquireSpinLock(&classCollection->secureReadLock, &oldIrql);
while(fileExtension->SecureReadMode--) {
classCollection->secureReadMode--;
}
KeReleaseSpinLock(&classCollection->secureReadLock, oldIrql);
}
HidpDestroyFileExtension(classCollection, fileExtension);
classCollection = BAD_POINTER;
/*
* Delete the device-FDO and all collection-PDOs * Don't touch fdoExt after this. */ HidpCleanUpFdo(fdoExt);
result = STATUS_SUCCESS; } else { //
// Destroy the file object and everything on it
//
KeAcquireSpinLock(&classCollection->FileExtensionListSpinLock, &oldIrql);
/*
* Update sharing information: * Decrement open counts and clear any exclusive holds of this file extension * on the device extension. */ ASSERT(pdoExt->openCount > 0); InterlockedDecrement(&pdoExt->openCount); if (fileExtension->accessMask & FILE_READ_DATA){ ASSERT(pdoExt->opensForRead > 0); pdoExt->opensForRead--; } if (fileExtension->accessMask & FILE_WRITE_DATA){ ASSERT(pdoExt->opensForWrite > 0); pdoExt->opensForWrite--; } if (!(fileExtension->shareMask & FILE_SHARE_READ)){ ASSERT(pdoExt->restrictionsForRead > 0); pdoExt->restrictionsForRead--; } if (!(fileExtension->shareMask & FILE_SHARE_WRITE)){ ASSERT(pdoExt->restrictionsForWrite > 0); pdoExt->restrictionsForWrite--; } if (fileExtension->shareMask == 0){ ASSERT(pdoExt->restrictionsForAnyOpen > 0); pdoExt->restrictionsForAnyOpen--; }
RemoveEntryList(&fileExtension->FileList);
KeReleaseSpinLock(&classCollection->FileExtensionListSpinLock, oldIrql);
if (fileExtension->isSecureOpen) { KeAcquireSpinLock(&classCollection->secureReadLock, &oldIrql);
while(fileExtension->SecureReadMode--) {
classCollection->secureReadMode--;
}
KeReleaseSpinLock(&classCollection->secureReadLock, oldIrql);
}
HidpDestroyFileExtension(classCollection, fileExtension);
result = STATUS_SUCCESS; }
} else { result = STATUS_DATA_ERROR; } } else { TRAP; result = STATUS_DEVICE_NOT_CONNECTED; }
DBGVERBOSE((" HidpIrpMajorClose: openCount decremented to %xh/%xh (pdo/fdo).", openCount, fdoExt->openCount)) } else { DBGERR(("IRP_MJ_CLOSE was sent with a device-FDO extension for which an open never succeeded. The OBJDIR test tool does this sometimes. Hit 'g'.")) result = STATUS_INVALID_PARAMETER_1; }
Irp->IoStatus.Status = result; IoCompleteRequest(Irp, IO_NO_INCREMENT);
DBGSUCCESS(result, FALSE) return result; }
/*
******************************************************************************** * HidpIrpMajorCreate ******************************************************************************** * * * Routine Description: * * We connect up to the interrupt for the create/open and initialize * the structures needed to maintain an open for a device. * * Arguments: * * DeviceObject - Pointer to the device object for this device * * Irp - Pointer to the IRP for the current request * * Return Value: * * The function value is the final status of the call * */ NTSTATUS HidpIrpMajorCreate(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp) { NTSTATUS status = STATUS_SUCCESS;
ASSERT(HidDeviceExtension->Signature == HID_DEVICE_EXTENSION_SIG);
if (HidDeviceExtension->isClientPdo){ PDO_EXTENSION *pdoExt = &HidDeviceExtension->pdoExt; FDO_EXTENSION *fdoExt = &pdoExt->deviceFdoExt->fdoExt; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PHIDCLASS_COLLECTION classCollection;
Irp->IoStatus.Information = 0;
classCollection = GetHidclassCollection(fdoExt, pdoExt->collectionNum); if (classCollection){ BOOLEAN sharingOk = TRUE; KIRQL oldIrql; BOOLEAN secureOpen = FALSE;
secureOpen = MyPrivilegeCheck(Irp);
// This is now taken care of by the fact that we don't
// enumerate mouse and keyboard collections as RAW.
KeAcquireSpinLock(&classCollection->FileExtensionListSpinLock, &oldIrql);
/*
* Enforce exclusive-open independently for exclusive-read and exclusive-write. */ ASSERT(irpSp->Parameters.Create.SecurityContext); DBGVERBOSE((" HidpIrpMajorCreate: DesiredAccess = %xh, ShareAccess = %xh.", (ULONG)irpSp->Parameters.Create.SecurityContext->DesiredAccess, (ULONG)irpSp->Parameters.Create.ShareAccess))
DBGASSERT((irpSp->Parameters.Create.SecurityContext->DesiredAccess & (FILE_READ_DATA|FILE_WRITE_DATA)), ("Neither FILE_READ_DATA|FILE_WRITE_DATA requested in HidpIrpMajorCreate. DesiredAccess = %xh.", (ULONG)irpSp->Parameters.Create.SecurityContext->DesiredAccess), FALSE) if (pdoExt->restrictionsForAnyOpen){ /*
* Oops. A previous open requested exclusive access. * Not even a client that requests only ioctl access * (does not request read nor write acess) is * allowed. */ DBGWARN(("HidpIrpMajorCreate failing open: previous open is non-shared (ShareAccess==0).")) sharingOk = FALSE; } else if (pdoExt->openCount && (irpSp->Parameters.Create.ShareAccess == 0)){ /*
* Oops. This open does not allow any sharing * (not even with a client that has neither read nor write access), * but there exists a previous open. */ DBGWARN(("HidpIrpMajorCreate failing open: requesting non-shared (ShareAccess==0) while previous open exists.")) sharingOk = FALSE; } else if ((irpSp->Parameters.Create.SecurityContext->DesiredAccess & FILE_READ_DATA) && pdoExt->restrictionsForRead){ /*
* Oops. A previous open requested exclusive-read access. */ DBGWARN(("HidpIrpMajorCreate failing open: requesting read access while previous open does not share read access.")) sharingOk = FALSE; } else if ((irpSp->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_DATA) && pdoExt->restrictionsForWrite){ /*
* Oops. A previous open requested exclusive-write access. */ DBGWARN(("HidpIrpMajorCreate failing open: requesting write access while previous open does not share write access.")) sharingOk = FALSE; } else if ((pdoExt->opensForRead > 0) && !(irpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ)){
/*
* Oops. The caller is requesting exclusive read access, but the device * is already open for read. */ DBGWARN(("HidpIrpMajorCreate failing open: this open request does not share read access; but collection already open for read.")) sharingOk = FALSE; } else if ((pdoExt->opensForWrite > 0) && !(irpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)){
/*
* Oops. The caller is requesting exclusive write access, but the device * is already open for write. */ DBGWARN(("HidpIrpMajorCreate failing open: this open request does not share write access; but collection already open for write.")) sharingOk = FALSE; }
if (!sharingOk){ DBGWARN(("HidpIrpMajorCreate failing IRP_MJ_CREATE with STATUS_SHARING_VIOLATION.")) status = STATUS_SHARING_VIOLATION; } else { if (irpSp->Parameters.Create.Options & FILE_DIRECTORY_FILE){ /*
* Attempt to open this device as a directory */ status = STATUS_NOT_A_DIRECTORY; } else {
/*
* Make sure the device is started. * If it is temporarily stopped, we also succeed because a stop * is supposed to be transparent to the client. */ if (((fdoExt->state == DEVICE_STATE_START_SUCCESS) || (fdoExt->state == DEVICE_STATE_STOPPING) || (fdoExt->state == DEVICE_STATE_STOPPED)) && ((pdoExt->state == COLLECTION_STATE_RUNNING) || (pdoExt->state == COLLECTION_STATE_STOPPING) || (pdoExt->state == COLLECTION_STATE_STOPPED))){
PHIDCLASS_FILE_EXTENSION fileExtension;
/*
* We have a valid collection. * Allocate a file object extension (which encapsulates an 'open' on the device). */ try {
fileExtension = ALLOCATEQUOTAPOOL(NonPagedPool, sizeof(HIDCLASS_FILE_EXTENSION)); } except (EXCEPTION_EXECUTE_HANDLER) { fileExtension = NULL; status = GetExceptionCode(); } if (fileExtension){ PHIDP_COLLECTION_DESC hidCollectionDesc;
RtlZeroMemory(fileExtension, sizeof(HIDCLASS_FILE_EXTENSION));
fileExtension->CollectionNumber = pdoExt->collectionNum; fileExtension->fdoExt = fdoExt; fileExtension->FileObject = irpSp->FileObject;
fileExtension->isOpportunisticPolledDeviceReader = FALSE; InitializeListHead( &fileExtension->ReportList ); InitializeListHead( &fileExtension->PendingIrpList ); KeInitializeSpinLock( &fileExtension->ListSpinLock ); fileExtension->Closing = FALSE;
//
// Right now we'll set a default maximum input report queue size.
// This can be changed later with an IOCTL.
//
fileExtension->CurrentInputReportQueueSize = 0; fileExtension->MaximumInputReportQueueSize = DEFAULT_INPUT_REPORT_QUEUE_SIZE; fileExtension->insideReadCompleteCount = 0;
//
// Add this file extension to the list of file extensions for this
// collection.
//
InsertHeadList(&classCollection->FileExtensionList, &fileExtension->FileList);
#if DBG
fileExtension->Signature = HIDCLASS_FILE_EXTENSION_SIG; #endif
/*
* Store the file-open attribute flags. */ fileExtension->FileAttributes = irpSp->Parameters.Create.FileAttributes; fileExtension->accessMask = irpSp->Parameters.Create.SecurityContext->DesiredAccess; fileExtension->shareMask = irpSp->Parameters.Create.ShareAccess;
//
// Set up secure read mode
//
fileExtension->SecureReadMode = 0; fileExtension->isSecureOpen = secureOpen; /*
* Store a pointer to our file extension in the file object. */ irpSp->FileObject->FsContext = fileExtension;
//
// KENRAY
// Only drivers can set the FsContext of file
// objects so this is not a security problem.
// However, there is only one file object for the entire
// PDO stack. This means we have to share. You cannot
// have both context pointers. I need one for the
// keyboard and mouse class drivers.
//
// This information need go into the fileExtension.
//
/*
* Increment the device extension's open counts, * and set the exclusive-access fields. */ InterlockedIncrement(&fdoExt->openCount); InterlockedIncrement(&pdoExt->openCount); if (irpSp->Parameters.Create.SecurityContext->DesiredAccess & FILE_READ_DATA){ pdoExt->opensForRead++; } if (irpSp->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_DATA){ pdoExt->opensForWrite++; }
/*
* NOTE: Restrictions are independent of desired access. * For example, a client can do an open-for-read-only * AND prevent other clients from doing an open-for-write * (by not setting the FILE_SHARE_WRITE flag). */ if (!(irpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ)){ pdoExt->restrictionsForRead++; } if (!(irpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)){ pdoExt->restrictionsForWrite++; } if (irpSp->Parameters.Create.ShareAccess == 0){ /*
* ShareAccess==0 means that no other opens of any kind * are allowed. */ pdoExt->restrictionsForAnyOpen++; }
DBGVERBOSE((" HidpIrpMajorCreate: opened collection w/ usagePage=%xh, usage=%xh. openCount incremented to %xh/%xh (pdo/fdo).", fdoExt->deviceDesc.CollectionDesc[pdoExt->collectionIndex].UsagePage, fdoExt->deviceDesc.CollectionDesc[pdoExt->collectionIndex].Usage, pdoExt->openCount, fdoExt->openCount)) } else { status = STATUS_INSUFFICIENT_RESOURCES; } } else { status = STATUS_DEVICE_NOT_CONNECTED; } } }
KeReleaseSpinLock(&classCollection->FileExtensionListSpinLock, oldIrql); } else { DBGERR(("HidpIrpMajorCreate failing -- couldn't find collection")) status = STATUS_DEVICE_NOT_CONNECTED; } } else { /*
* We don't support opens on the device itself, * only on the collections. */ DBGWARN(("HidpIrpMajorCreate failing -- we don't support opens on the device itself; only on collections.")) status = STATUS_UNSUCCESSFUL; }
Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); DBGSUCCESS(status, FALSE) return status; }
/*
******************************************************************************** * HidpIrpMajorDeviceControl ******************************************************************************** * * Note: This function cannot be pageable because IOCTLs * can get sent at DISPATCH_LEVEL. * */ NTSTATUS HidpIrpMajorDeviceControl(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp) { NTSTATUS status; BOOLEAN completeIrpHere = TRUE; PDO_EXTENSION *pdoExt; FDO_EXTENSION *fdoExt; ULONG ioControlCode; KIRQL irql;
PIO_STACK_LOCATION irpSp;
PHIDCLASS_COLLECTION hidCollection; PHIDCLASS_FILE_EXTENSION fileExtension; PFILE_OBJECT fileObject;
if (!HidDeviceExtension->isClientPdo){ ASSERT(HidDeviceExtension->isClientPdo); status = STATUS_INVALID_PARAMETER_1; goto HidpIrpMajorDeviceControlDone; }
pdoExt = &HidDeviceExtension->pdoExt; fdoExt = &pdoExt->deviceFdoExt->fdoExt;
if (fdoExt->state != DEVICE_STATE_START_SUCCESS || pdoExt->state != COLLECTION_STATE_RUNNING) { DBGSTATE (pdoExt->state, COLLECTION_STATE_RUNNING, FALSE) status = STATUS_DEVICE_NOT_CONNECTED; goto HidpIrpMajorDeviceControlDone; }
irpSp = IoGetCurrentIrpStackLocation(Irp); // Keep this privately so we still have it after the IRP is completed.
ioControlCode = irpSp->Parameters.DeviceIoControl.IoControlCode; Irp->IoStatus.Information = 0;
status = HidpCheckIdleState(HidDeviceExtension, Irp); if (status != STATUS_SUCCESS) { completeIrpHere = (status != STATUS_PENDING); goto HidpIrpMajorDeviceControlDone; }
switch (ioControlCode){
case IOCTL_HID_GET_DRIVER_CONFIG: case IOCTL_HID_SET_DRIVER_CONFIG: DBGWARN(("Unsupported ioctl received: %x", ioControlCode)); status = STATUS_NOT_IMPLEMENTED; break;
case IOCTL_HID_GET_COLLECTION_INFORMATION: /*
* This IRP is METHOD_BUFFERED, so the buffer * is in the AssociatedIrp. */ DBGASSERT((Irp->Flags & IRP_BUFFERED_IO), ("Irp->Flags & IRP_BUFFERED_IO Irp->Type != IO_TYPE_IRP, Irp->Type == %x", Irp->Type), FALSE) if (Irp->AssociatedIrp.SystemBuffer){ ULONG bufLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength; status = HidpGetCollectionInformation( fdoExt, pdoExt->collectionNum, Irp->AssociatedIrp.SystemBuffer, &bufLen); Irp->IoStatus.Information = bufLen; } else { status = STATUS_INVALID_PARAMETER; } break;
case IOCTL_HID_GET_COLLECTION_DESCRIPTOR: /*
* This IOCTL is METHOD_NEITHER, so the buffer is in UserBuffer. */ if (Irp->UserBuffer){ __try { ULONG bufLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
if (Irp->RequestorMode != KernelMode){ /*
* Ensure user-mode buffer is legal. */ ProbeForWrite(Irp->UserBuffer, bufLen, sizeof(UCHAR)); }
status = HidpGetCollectionDescriptor( fdoExt, pdoExt->collectionNum, Irp->UserBuffer, &bufLen); Irp->IoStatus.Information = bufLen; } __except(EXCEPTION_EXECUTE_HANDLER) { DBGWARN(("Invalid user mode buffer in IOCTL_HID_DET_COLLECTION_DESCRIPTOR")); status = GetExceptionCode(); } } else { status = STATUS_INVALID_BUFFER_SIZE; } break;
case IOCTL_HID_FLUSH_QUEUE:
//
// Run the list of report descriptors hanging off of this
// file object and free them all.
//
fileObject = irpSp->FileObject; fileExtension = (PHIDCLASS_FILE_EXTENSION)fileObject->FsContext; if(!fileExtension) { DBGWARN(("Attempted to flush queue with no file extension")) status = STATUS_PRIVILEGE_NOT_HELD; break; } ASSERT(fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG); HidpFlushReportQueue(fileExtension); status = STATUS_SUCCESS; break;
case IOCTL_HID_GET_POLL_FREQUENCY_MSEC: hidCollection = GetHidclassCollection(fdoExt, pdoExt->collectionNum); if (hidCollection && hidCollection->hidCollectionInfo.Polled){ /*
* Get the current poll frequency. * This IOCTL is METHOD_BUFFERED, so the result goes in the AssociatedIrp. */ DBGASSERT((Irp->Flags & IRP_BUFFERED_IO), ("Irp->Flags & IRP_BUFFERED_IO Irp->Type != IO_TYPE_IRP, Irp->Type == %x", Irp->Type), FALSE) if (Irp->AssociatedIrp.SystemBuffer && (irpSp->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(ULONG))){
*(ULONG *)Irp->AssociatedIrp.SystemBuffer = hidCollection->PollInterval_msec; Irp->IoStatus.Information = sizeof (ULONG); status = STATUS_SUCCESS; } else { status = STATUS_INVALID_BUFFER_SIZE; } } else { status = STATUS_INVALID_DEVICE_REQUEST; } break;
case IOCTL_HID_SET_POLL_FREQUENCY_MSEC: hidCollection = GetHidclassCollection(fdoExt, pdoExt->collectionNum); if (hidCollection && hidCollection->hidCollectionInfo.Polled){
if (Irp->AssociatedIrp.SystemBuffer && (irpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(ULONG))){
ULONG newPollInterval = *(ULONG *)Irp->AssociatedIrp.SystemBuffer;
fileObject = irpSp->FileObject; fileExtension = (PHIDCLASS_FILE_EXTENSION)fileObject->FsContext; if(!fileExtension) { DBGWARN(("Attempted to set poll frequency with no file extension")) status = STATUS_PRIVILEGE_NOT_HELD; break; } ASSERT(fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG);
if (newPollInterval == 0){ /*
* Poll interval zero means that this client will * be doing irregular, opportunistic reads on the * polled device. We will not change the polling * frequency of the device. But when this client * does a read, we will immediately complete that read * with either the last report for this collection * (if the data is not stale) or by immediately issuing * a new read. */ fileExtension->isOpportunisticPolledDeviceReader = TRUE; } else { /*
* Set the poll frequency AND tell the user what we really set it to * in case it's out of range. */ if (newPollInterval < MIN_POLL_INTERVAL_MSEC){ newPollInterval = MIN_POLL_INTERVAL_MSEC; } else if (newPollInterval > MAX_POLL_INTERVAL_MSEC){ newPollInterval = MAX_POLL_INTERVAL_MSEC; } hidCollection->PollInterval_msec = newPollInterval;
/*
* If this client was an 'opportunistic' reader before, * he's not anymore. */ fileExtension->isOpportunisticPolledDeviceReader = FALSE;
/*
* Stop and re-start the polling loop so that * the new polling interval takes effect right away. */ StopPollingLoop(hidCollection, FALSE); StartPollingLoop(fdoExt, hidCollection, FALSE); }
status = STATUS_SUCCESS; } else { status = STATUS_INVALID_BUFFER_SIZE; } } else { status = STATUS_INVALID_DEVICE_REQUEST; } break;
case IOCTL_HID_GET_FEATURE: case IOCTL_HID_SET_FEATURE: case IOCTL_HID_GET_INPUT_REPORT: case IOCTL_HID_SET_OUTPUT_REPORT: { BOOLEAN sentIrpToMinidriver; status = HidpGetSetReport ( HidDeviceExtension, Irp, irpSp->Parameters.DeviceIoControl.IoControlCode, &sentIrpToMinidriver); /*
* If we just passed this Irp to the minidriver, we don't want to * complete the Irp; we're not even allowed to touch it since it may * have already completed. */ completeIrpHere = !sentIrpToMinidriver; } break;
// NOTE - we currently only support English (langId=0x0409).
// route all collection-PDO string requests to device-FDO.
case IOCTL_HID_GET_MANUFACTURER_STRING: status = HidpGetDeviceString(fdoExt, Irp, HID_STRING_ID_IMANUFACTURER, 0x0409); completeIrpHere = FALSE; break;
case IOCTL_HID_GET_PRODUCT_STRING: status = HidpGetDeviceString(fdoExt, Irp, HID_STRING_ID_IPRODUCT, 0x0409); completeIrpHere = FALSE; break;
case IOCTL_HID_GET_SERIALNUMBER_STRING: status = HidpGetDeviceString(fdoExt, Irp, HID_STRING_ID_ISERIALNUMBER, 0x0409); completeIrpHere = FALSE; break;
case IOCTL_HID_GET_INDEXED_STRING: /*
* This IRP is METHOD_OUT_DIRECT, so the buffer is in the MDL. * The second argument (string index) is in the AssociatedIrp; * the InputBufferLength is the length of this second buffer. */ if (Irp->AssociatedIrp.SystemBuffer && (irpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(ULONG))){
ULONG stringIndex = *(ULONG *)Irp->AssociatedIrp.SystemBuffer; status = HidpGetIndexedString(fdoExt, Irp, stringIndex, 0x409); completeIrpHere = FALSE; } else { status = STATUS_INVALID_PARAMETER; } break;
case IOCTL_HID_GET_MS_GENRE_DESCRIPTOR: /*
* This IRP is METHOD_OUT_DIRECT, so the buffer is in the MDL. */ status = HidpGetMsGenreDescriptor(fdoExt, Irp); completeIrpHere = FALSE; break;
case IOCTL_GET_NUM_DEVICE_INPUT_BUFFERS:
/*
* This IRP is METHOD_BUFFERED, so the buffer * is in the AssociatedIrp.SystemBuffer field. */ DBGASSERT((Irp->Flags & IRP_BUFFERED_IO), ("Irp->Flags & IRP_BUFFERED_IO Irp->Type != IO_TYPE_IRP, Irp->Type == %x", Irp->Type), FALSE) if (Irp->AssociatedIrp.SystemBuffer && (irpSp->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(ULONG))){
fileObject = irpSp->FileObject; fileExtension = (PHIDCLASS_FILE_EXTENSION)fileObject->FsContext; if(!fileExtension) { DBGWARN(("Attempted to get number of input buffers with no file extension")) status = STATUS_PRIVILEGE_NOT_HELD; break; } ASSERT( fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG );
*(ULONG *)Irp->AssociatedIrp.SystemBuffer = fileExtension->MaximumInputReportQueueSize; Irp->IoStatus.Information = sizeof(ULONG); status = STATUS_SUCCESS; } else { status = STATUS_INVALID_PARAMETER; } break;
case IOCTL_SET_NUM_DEVICE_INPUT_BUFFERS:
/*
* This IRP is METHOD_BUFFERED, so the buffer * is in the AssociatedIrp.SystemBuffer field. */ DBGASSERT((Irp->Flags & IRP_BUFFERED_IO), ("Irp->Flags & IRP_BUFFERED_IO Irp->Type != IO_TYPE_IRP, Irp->Type == %x", Irp->Type), FALSE) if (Irp->AssociatedIrp.SystemBuffer && (irpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(ULONG))){
ULONG newValue = *(ULONG *)Irp->AssociatedIrp.SystemBuffer;
fileObject = irpSp->FileObject; fileExtension = (PHIDCLASS_FILE_EXTENSION)fileObject->FsContext; if(!fileExtension) { DBGWARN(("Attempted to set number of input buffers with no file extension")) status = STATUS_PRIVILEGE_NOT_HELD; break; } ASSERT( fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG );
if ((newValue >= MIN_INPUT_REPORT_QUEUE_SIZE) && (newValue <= MAX_INPUT_REPORT_QUEUE_SIZE)){
fileExtension->MaximumInputReportQueueSize = newValue; status = STATUS_SUCCESS; } else { status = STATUS_INVALID_PARAMETER; } } else { status = STATUS_INVALID_PARAMETER; } break;
case IOCTL_GET_PHYSICAL_DESCRIPTOR: status = HidpGetPhysicalDescriptor(HidDeviceExtension, Irp); completeIrpHere = FALSE; break;
case IOCTL_HID_GET_HARDWARE_ID: { PDEVICE_OBJECT pdo = pdoExt->deviceFdoExt->hidExt.PhysicalDeviceObject; ULONG bufLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength; PWSTR hwIdBuf;
hwIdBuf = HidpGetSystemAddressForMdlSafe(Irp->MdlAddress);
if (hwIdBuf && bufLen){ ULONG actualLen;
status = IoGetDeviceProperty( pdo, DevicePropertyHardwareID, bufLen, hwIdBuf, &actualLen); if (NT_SUCCESS(status)){ Irp->IoStatus.Information = (ULONG)actualLen; } } else { status = STATUS_INVALID_USER_BUFFER; } } break;
case IOCTL_GET_SYS_BUTTON_CAPS: hidCollection = GetHidclassCollection(fdoExt, pdoExt->collectionNum); if (hidCollection){ if (irpSp->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(ULONG)){ ULONG buttonCaps;
status = HidP_SysPowerCaps(hidCollection->phidDescriptor, &buttonCaps); if (NT_SUCCESS(status)){ *(PULONG)Irp->AssociatedIrp.SystemBuffer = buttonCaps; Irp->IoStatus.Information = sizeof(ULONG); } } else { status = STATUS_INVALID_BUFFER_SIZE; Irp->IoStatus.Information = sizeof(ULONG); } } else { status = STATUS_DEVICE_NOT_CONNECTED; } break;
case IOCTL_GET_SYS_BUTTON_EVENT:
/*
* Hold onto this IRP and complete it when a power event occurs. */ hidCollection = GetHidclassCollection(fdoExt, pdoExt->collectionNum); if (hidCollection){ status = QueuePowerEventIrp(hidCollection, Irp); if (status == STATUS_PENDING){ completeIrpHere = FALSE; } } else { status = STATUS_DEVICE_NOT_CONNECTED; } break;
case IOCTL_HID_ENABLE_SECURE_READ:
fileObject = irpSp->FileObject; fileExtension = (PHIDCLASS_FILE_EXTENSION)fileObject->FsContext; if(!fileExtension) { DBGWARN(("Attempted to get number of input buffers with no file extension")) status = STATUS_PRIVILEGE_NOT_HELD; break; } ASSERT( fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG );
hidCollection = GetHidclassCollection(fdoExt, pdoExt->collectionNum);
if (!fileExtension->isSecureOpen) {
status = STATUS_PRIVILEGE_NOT_HELD; break;
}
KeAcquireSpinLock(&hidCollection->secureReadLock, &irql); fileExtension->SecureReadMode++; hidCollection->secureReadMode++;
KeReleaseSpinLock(&hidCollection->secureReadLock, irql);
break; case IOCTL_HID_DISABLE_SECURE_READ: fileObject = irpSp->FileObject; fileExtension = (PHIDCLASS_FILE_EXTENSION)fileObject->FsContext; if(!fileExtension) { DBGWARN(("Attempted to get number of input buffers with no file extension")) status = STATUS_PRIVILEGE_NOT_HELD; break; } ASSERT( fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG );
hidCollection = GetHidclassCollection(fdoExt, pdoExt->collectionNum);
if (!fileExtension->isSecureOpen) {
status = STATUS_PRIVILEGE_NOT_HELD; break;
}
KeAcquireSpinLock(&hidCollection->secureReadLock, &irql); if (fileExtension->SecureReadMode > 0) { fileExtension->SecureReadMode--; hidCollection->secureReadMode--; }
KeReleaseSpinLock(&hidCollection->secureReadLock, irql);
break;
default: /*
* 'Fail' the Irp by returning the default status. */ DBGWARN(("Unrecognized ioctl received: %x", ioControlCode)); status = Irp->IoStatus.Status; break; }
DBG_LOG_IOCTL(fdoExt->fdo, ioControlCode, status)
HidpIrpMajorDeviceControlDone:
/*
* If we did not pass the Irp down to a lower driver, complete it here. */ if (completeIrpHere){ Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); }
DBGSUCCESS(status, FALSE)
return status; }
/*
******************************************************************************** * HidpIrpMajorINTERNALDeviceControl ******************************************************************************** * * Note: This function cannot be pageable because IOCTLs * can get sent at DISPATCH_LEVEL. * */ NTSTATUS HidpIrpMajorINTERNALDeviceControl(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp) { NTSTATUS status;
if (HidDeviceExtension->isClientPdo){ PDO_EXTENSION *pdoExt = &HidDeviceExtension->pdoExt; FDO_EXTENSION *fdoExt = &pdoExt->deviceFdoExt->fdoExt;
Irp->IoStatus.Information = 0;
//
// If we ever support any other internal IOCTLs that are real and
// require touching the hardware, then we need to break out the check
// for fdoExt->devicePowerState and enqueue the irp until we get to full
// power
//
if (fdoExt->state == DEVICE_STATE_START_SUCCESS) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
switch (irpSp->Parameters.DeviceIoControl.IoControlCode){
default: /*
* 'Fail' the Irp by returning the default status. */ DBGWARN(("HidpIrpMajorINTERNALDeviceControl - unsupported IOCTL %xh ", (ULONG)irpSp->Parameters.DeviceIoControl.IoControlCode)) status = Irp->IoStatus.Status; break; } } else { status = STATUS_DEVICE_NOT_CONNECTED; } } else { ASSERT(HidDeviceExtension->isClientPdo); status = STATUS_INVALID_PARAMETER_1; }
Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT);
DBGSUCCESS(status, FALSE) return status; }
/*
******************************************************************************** * HidpIrpMajorPnp ******************************************************************************** * * */ NTSTATUS HidpIrpMajorPnp(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp) { NTSTATUS status; PIO_STACK_LOCATION irpSp; BOOLEAN completeIrpHere; BOOLEAN isClientPdo; UCHAR minorFunction;
PAGED_CODE();
irpSp = IoGetCurrentIrpStackLocation(Irp);
/*
* Keep these fields privately so that we have them * after the IRP is completed and in case we delete * the device extension on a REMOVE_DEVICE. */ isClientPdo = HidDeviceExtension->isClientPdo; minorFunction = irpSp->MinorFunction;
DBG_LOG_PNP_IRP(Irp, minorFunction, isClientPdo, FALSE, 0)
if (isClientPdo) { status = HidpPdoPnp(HidDeviceExtension, Irp); } else { status = HidpFdoPnp(HidDeviceExtension, Irp); }
DBG_LOG_PNP_IRP(Irp, minorFunction, isClientPdo, TRUE, status)
return status; }
NTSTATUS HidpPdoPnp( IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp ) { NTSTATUS status = NO_STATUS; PIO_STACK_LOCATION irpSp; FDO_EXTENSION *fdoExt; PDO_EXTENSION *pdoExt; UCHAR minorFunction; BOOLEAN deleteDevice = FALSE;
PAGED_CODE();
irpSp = IoGetCurrentIrpStackLocation(Irp);
/*
* Keep these fields privately so that we have them * after the IRP is completed and in case we delete * the device extension on a REMOVE_DEVICE. */ minorFunction = irpSp->MinorFunction;
DBG_LOG_PNP_IRP(Irp, minorFunction, TRUE, FALSE, 0)
pdoExt = &HidDeviceExtension->pdoExt; fdoExt = &pdoExt->deviceFdoExt->fdoExt;
switch (minorFunction){
case IRP_MN_START_DEVICE: status = HidpStartCollectionPDO(fdoExt, pdoExt, Irp); if (NT_SUCCESS(status) && ISPTR(pdoExt->StatusChangeFn)) { pdoExt->StatusChangeFn(pdoExt->StatusChangeContext, DeviceObjectStarted); } break;
case IRP_MN_QUERY_STOP_DEVICE: DBGSTATE(pdoExt->state, COLLECTION_STATE_RUNNING, FALSE) pdoExt->prevState = pdoExt->state; pdoExt->state = COLLECTION_STATE_STOPPING; status = STATUS_SUCCESS; break;
case IRP_MN_CANCEL_STOP_DEVICE: DBGSTATE(pdoExt->state, COLLECTION_STATE_STOPPING, TRUE) pdoExt->state = pdoExt->prevState; status = STATUS_SUCCESS; break;
case IRP_MN_STOP_DEVICE: DBGSTATE(pdoExt->state, COLLECTION_STATE_STOPPING, TRUE) if (pdoExt->prevState != COLLECTION_STATE_UNINITIALIZED){ /*
* Destroy the symbolic link for this collection. */ HidpCreateSymbolicLink(pdoExt, pdoExt->collectionNum, FALSE, pdoExt->pdo); HidpFreePowerEventIrp(&fdoExt->classCollectionArray[pdoExt->collectionIndex]);
pdoExt->state = COLLECTION_STATE_STOPPED; if (ISPTR(pdoExt->StatusChangeFn)) { pdoExt->StatusChangeFn(pdoExt->StatusChangeContext, DeviceObjectStopped); } }
status = STATUS_SUCCESS; break;
case IRP_MN_SURPRISE_REMOVAL: case IRP_MN_QUERY_REMOVE_DEVICE: DBGASSERT(((pdoExt->state == COLLECTION_STATE_RUNNING) || (pdoExt->state == COLLECTION_STATE_STOPPED)), ("Pdo is neither stopped nor started, but is getting removed, state=%d",pdoExt->state), FALSE)
pdoExt->prevState = pdoExt->state; pdoExt->state = COLLECTION_STATE_REMOVING;
if ((pdoExt->prevState == COLLECTION_STATE_RUNNING)) {
/*
* Remove the symbolic link for this collection-PDO. * * NOTE: Do this BEFORE destroying the collection, because * HidpDestroyCollection() may cause a client driver, * whose pending read IRPs get cancelled when the collection * is destroyed, to try to re-open the device. * Deleting the symbolic link first eliminates this possibility. */ HidpCreateSymbolicLink(pdoExt, pdoExt->collectionNum, FALSE, pdoExt->pdo); }
if ((pdoExt->prevState == COLLECTION_STATE_RUNNING) || (pdoExt->prevState == COLLECTION_STATE_STOPPED)){
/*
* Flush all pending IO and deny any future io by setting * the collection state to removing. * Note: on NT, clients will receive the query remove * first, but surprise removal must deny access to the * device. * * NOTE: There is a hole here that results in a read being * queued even though we've blocked everything. * 1) Get read, check to see that our state is running * or stopped in HidpIrpMajorRead. * 2) Set state to COLLECTION_STATE_REMOVING and complete * all reads here. * 3) Enqueue read in HidpIrpMajorRead. * */ ULONG ctnIndx = pdoExt->collectionIndex; PHIDCLASS_COLLECTION collection = &fdoExt->classCollectionArray[ctnIndx]; LIST_ENTRY dequeue, *entry; PIRP irp;
DBGVERBOSE(("Got QUERY/SURPRISE REMOVE for collection; completing all pending reads. openCount=%d, pendingReads=%d.", pdoExt->openCount, collection->numPendingReads))
CompleteAllPendingReadsForCollection(collection);
DequeueAllPdoPowerDelayedIrps(pdoExt, &dequeue); while (!IsListEmpty(&dequeue)) { entry = RemoveHeadList(&dequeue); irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE; IoCompleteRequest(irp, IO_NO_INCREMENT); } }
status = STATUS_SUCCESS; break;
case IRP_MN_CANCEL_REMOVE_DEVICE: status = STATUS_SUCCESS;
DBGSTATE(pdoExt->state, COLLECTION_STATE_REMOVING, TRUE) pdoExt->state = pdoExt->prevState; if (pdoExt->state == COLLECTION_STATE_RUNNING) { // Re-create the symbolic link, since we're no longer
// deleting the device.
HidpCreateSymbolicLink(pdoExt, pdoExt->collectionNum, TRUE, pdoExt->pdo); } break;
case IRP_MN_REMOVE_DEVICE:
/*
* REMOVE_DEVICE for the device-FDO should come after REMOVE_DEVICE for each * of the collection-PDOs. */ DBGASSERT((pdoExt->state == COLLECTION_STATE_UNINITIALIZED || pdoExt->state == COLLECTION_STATE_REMOVING), ("On pnp remove, collection state is incorrect. Actual: %x", pdoExt->state), TRUE)
HidpRemoveCollection(fdoExt, pdoExt, Irp); if (ISPTR(pdoExt->StatusChangeFn)) { pdoExt->StatusChangeFn(pdoExt->StatusChangeContext, DeviceObjectRemoved); }
if (!fdoExt->presentReported) {
if (ISPTR(pdoExt->name)){ RtlFreeUnicodeString(pdoExt->name); ExFreePool(pdoExt->name); pdoExt->name = BAD_POINTER; } deleteDevice = TRUE;
} status = STATUS_SUCCESS; // Can't fail IRP_MN_REMOVE
break;
case IRP_MN_QUERY_CAPABILITIES: status = HidpQueryCollectionCapabilities(pdoExt, Irp); break;
case IRP_MN_QUERY_DEVICE_RELATIONS: if (irpSp->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation){ /*
* Return a reference to this PDO */ PDEVICE_RELATIONS devRel = ALLOCATEPOOL(PagedPool, sizeof(DEVICE_RELATIONS)); if (devRel){ /*
* Add a reference to the PDO, since CONFIGMG will free it. */ ObReferenceObject(pdoExt->pdo); devRel->Objects[0] = pdoExt->pdo; devRel->Count = 1; Irp->IoStatus.Information = (ULONG_PTR)devRel; status = STATUS_SUCCESS; } else { status = STATUS_INSUFFICIENT_RESOURCES; } } else { /*
* Fail this Irp by returning the default * status (typically STATUS_NOT_SUPPORTED). */ status = Irp->IoStatus.Status; } break;
case IRP_MN_QUERY_ID: status = HidpQueryIdForClientPdo(HidDeviceExtension, Irp); break;
case IRP_MN_QUERY_PNP_DEVICE_STATE: //
// Do not clear any flags that may have been set by drivers above
// the PDO
//
// Irp->IoStatus.Information = 0;
switch (pdoExt->state){ case DEVICE_STATE_START_FAILURE: Irp->IoStatus.Information |= PNP_DEVICE_FAILED; break; case DEVICE_STATE_STOPPED: Irp->IoStatus.Information |= PNP_DEVICE_DISABLED; break; case DEVICE_STATE_REMOVING: case DEVICE_STATE_REMOVED: Irp->IoStatus.Information |= PNP_DEVICE_REMOVED; break; } status = STATUS_SUCCESS; break; case IRP_MN_QUERY_INTERFACE: status = HidpQueryInterface(HidDeviceExtension, Irp); break;
case IRP_MN_QUERY_BUS_INFORMATION: { PPNP_BUS_INFORMATION busInfo = (PPNP_BUS_INFORMATION) ALLOCATEPOOL(NonPagedPool, sizeof(PNP_BUS_INFORMATION)); if (busInfo) { busInfo->BusTypeGuid = GUID_BUS_TYPE_HID; busInfo->LegacyBusType = PNPBus; busInfo->BusNumber = fdoExt->BusNumber; Irp->IoStatus.Information = (ULONG_PTR) busInfo; status = STATUS_SUCCESS; } else { status = STATUS_INSUFFICIENT_RESOURCES; Irp->IoStatus.Information = 0; } } break;
default: /*
* In the default case for the collection-PDOs we complete the IRP * without changing IoStatus.Status; we also return the preset IoStatus.Status. * This allows an upper filter driver to set IoStatus.Status * on the way down. In the absence of a filter driver, * IoStatus.Status will be STATUS_NOT_SUPPORTED. * * In the default case for the FDO we send the Irp on and let * the other drivers in the stack do their thing. */ status = Irp->IoStatus.Status; break; }
/*
* If this is a call for a collection-PDO, we complete it ourselves here. * Otherwise, we pass it to the minidriver stack for more processing. */ ASSERT(status != NO_STATUS); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); if (deleteDevice) { /*
* Delete the client PDO. * Don't touch the pdoExt after doing this. */
ObDereferenceObject(pdoExt->pdo); IoDeleteDevice(pdoExt->pdo);
}
DBG_LOG_PNP_IRP(Irp, minorFunction, TRUE, TRUE, status)
return status; }
NTSTATUS HidpFdoPnp( IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp ) { NTSTATUS status = NO_STATUS; PIO_STACK_LOCATION irpSp; FDO_EXTENSION *fdoExt; BOOLEAN completeIrpHere = FALSE; // general rule
UCHAR minorFunction;
PAGED_CODE();
irpSp = IoGetCurrentIrpStackLocation(Irp);
/*
* Keep these fields privately so that we have them * after the IRP is completed and in case we delete * the device extension on a REMOVE_DEVICE. */ minorFunction = irpSp->MinorFunction;
DBG_LOG_PNP_IRP(Irp, minorFunction, FALSE, FALSE, 0)
fdoExt = &HidDeviceExtension->fdoExt;
switch (minorFunction){
case IRP_MN_START_DEVICE:
status = HidpStartDevice(HidDeviceExtension, Irp); completeIrpHere = TRUE; break;
case IRP_MN_QUERY_STOP_DEVICE: /*
* We will pass this IRP down the driver stack. * However, we need to change the default status * from STATUS_NOT_SUPPORTED to STATUS_SUCCESS. */ Irp->IoStatus.Status = STATUS_SUCCESS;
DBGSTATE(fdoExt->state, DEVICE_STATE_START_SUCCESS, FALSE) fdoExt->prevState = fdoExt->state; fdoExt->state = DEVICE_STATE_STOPPING; break;
case IRP_MN_CANCEL_STOP_DEVICE: /*
* We will pass this IRP down the driver stack. * However, we need to change the default status * from STATUS_NOT_SUPPORTED to STATUS_SUCCESS. */ Irp->IoStatus.Status = STATUS_SUCCESS;
DBGSTATE(fdoExt->state, DEVICE_STATE_STOPPING, TRUE) fdoExt->state = fdoExt->prevState; break;
case IRP_MN_STOP_DEVICE: DBGSTATE(fdoExt->state, DEVICE_STATE_STOPPING, TRUE) if (fdoExt->prevState == DEVICE_STATE_START_SUCCESS){
/*
* While it is stopped, the host controller may not be able * to complete IRPs. So cancel them before sending down the stop. */ CancelAllPingPongIrps(fdoExt); } fdoExt->state = DEVICE_STATE_STOPPED;
IoCopyCurrentIrpStackLocationToNext(Irp); status = HidpCallDriverSynchronous(fdoExt->fdo, Irp);
completeIrpHere = TRUE; break;
case IRP_MN_SURPRISE_REMOVAL: //
// On surprise removal, we should stop accessing the device.
// We do the same steps on IRP_MN_REMOVE_DEVICE for the query
// removal case. Don't bother doing it during query remove,
// itself, because we don't want to have to handle the
// cancel case. NOTE: We only get away with this because all
// of these steps can be repeated without dire consequences.
//
if (ISPTR(fdoExt->waitWakeIrp)){ IoCancelIrp(fdoExt->waitWakeIrp); fdoExt->waitWakeIrp = BAD_POINTER; }
HidpCancelIdleNotification(fdoExt, TRUE);
if (ISPTR(fdoExt->idleNotificationRequest)) { IoFreeIrp(fdoExt->idleNotificationRequest); fdoExt->idleNotificationRequest = BAD_POINTER; }
DestroyPingPongs(fdoExt);
// fall thru to IRP_MN_QUERY_REMOVE_DEVICE
case IRP_MN_QUERY_REMOVE_DEVICE: { PIRP idleIrp;
while (idleIrp = DequeuePowerDelayedIrp(fdoExt)) { idleIrp->IoStatus.Status = STATUS_NO_SUCH_DEVICE; IoCompleteRequest(idleIrp, IO_NO_INCREMENT); } }
/*
* We will pass this IRP down the driver stack. * However, we need to change the default status * from STATUS_NOT_SUPPORTED to STATUS_SUCCESS. */ Irp->IoStatus.Status = STATUS_SUCCESS;
DBGSTATE(fdoExt->state, DEVICE_STATE_START_SUCCESS, FALSE) DBGASSERT((fdoExt->state == DEVICE_STATE_START_SUCCESS || fdoExt->state == DEVICE_STATE_STOPPED), ("Fdo is neither stopped nor started, but is getting removed, state=%d",fdoExt->state), FALSE) fdoExt->prevState = fdoExt->state; fdoExt->state = DEVICE_STATE_REMOVING; break;
case IRP_MN_CANCEL_REMOVE_DEVICE: /*
* We will pass this IRP down the driver stack. * However, we need to change the default status * from STATUS_NOT_SUPPORTED to STATUS_SUCCESS. */ Irp->IoStatus.Status = STATUS_SUCCESS;
DBGSTATE(fdoExt->state, DEVICE_STATE_REMOVING, TRUE) fdoExt->state = fdoExt->prevState; break;
case IRP_MN_REMOVE_DEVICE:
/*
* REMOVE_DEVICE for the device-FDO should come after REMOVE_DEVICE * for each of the collection-PDOs. * Don't touch the device extension after this call. */ DBGASSERT((fdoExt->state == DEVICE_STATE_REMOVING || fdoExt->state == DEVICE_STATE_START_FAILURE || fdoExt->state == DEVICE_STATE_INITIALIZED), ("Incorrect device state: %x", fdoExt->state), TRUE) status = HidpRemoveDevice(fdoExt, Irp); goto HidpFdoPnpDone; break;
case IRP_MN_QUERY_DEVICE_RELATIONS: if (irpSp->Parameters.QueryDeviceRelations.Type == BusRelations){ status = HidpQueryDeviceRelations(HidDeviceExtension, Irp); if (NT_SUCCESS(status)){ /*
* Although we have satisfied this PnP IRP, * we will still pass it down the stack. * First change the default status to our status. */ Irp->IoStatus.Status = status; } else { completeIrpHere = TRUE; } } break;
default: /*
* In the default case for the collection-PDOs we complete the IRP * without changing IoStatus.Status; we also return the preset IoStatus.Status. * This allows an upper filter driver to set IoStatus.Status * on the way down. In the absence of a filter driver, * IoStatus.Status will be STATUS_NOT_SUPPORTED. * * In the default case for the FDO we send the Irp on and let * the other drivers in the stack do their thing. */ if (completeIrpHere){ status = Irp->IoStatus.Status; } break; }
/*
* If this is a call for a collection-PDO, we complete it ourselves here. * Otherwise, we pass it to the minidriver stack for more processing. */ if (completeIrpHere){ ASSERT(status != NO_STATUS); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } else { /*
* Call the minidriver with this Irp. * The rest of our processing will be done in our completion routine. * * Note: Don't touch the Irp after sending it down, since it may * be completed immediately. */ IoCopyCurrentIrpStackLocationToNext(Irp); status = HidpCallDriver(fdoExt->fdo, Irp); }
HidpFdoPnpDone: DBG_LOG_PNP_IRP(Irp, minorFunction, FALSE, TRUE, status)
return status; }
|