|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
util.c
Abstract
Internal utility functions for the HID class driver.
Authors:
Ervin P.
Environment:
Kernel mode only
Revision History:
--*/
#include "pch.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, HidpAddDevice)
#pragma alloc_text(PAGE, HidpDriverUnload)
#pragma alloc_text(PAGE, HidpGetDeviceDescriptor)
#pragma alloc_text(PAGE, HidpQueryDeviceCapabilities)
#pragma alloc_text(PAGE, HidpQueryIdForClientPdo)
#pragma alloc_text(PAGE, SubstituteBusNames)
#pragma alloc_text(PAGE, BuildCompatibleID)
#pragma alloc_text(PAGE, HidpQueryCollectionCapabilities)
#pragma alloc_text(PAGE, HidpCreateClientPDOs)
#pragma alloc_text(PAGE, MakeClientPDOName)
#pragma alloc_text(PAGE, HidpCreateSymbolicLink)
#pragma alloc_text(PAGE, HidpQueryInterface)
#endif
/*
******************************************************************************** * HidpCopyInputReportToUser ******************************************************************************** * * Copy a read report into a user's buffer. * * Note: ReportData is already "cooked" (already has report-id byte at start of report). * */ NTSTATUS HidpCopyInputReportToUser( IN PHIDCLASS_FILE_EXTENSION FileExtension, IN PUCHAR ReportData, IN OUT PULONG UserBufferLen, OUT PUCHAR UserBuffer ) { NTSTATUS result = STATUS_DEVICE_DATA_ERROR; ULONG reportId; PHIDP_REPORT_IDS reportIdentifier; FDO_EXTENSION *fdoExtension = FileExtension->fdoExt;
RUNNING_DISPATCH();
ASSERT(fdoExtension->deviceDesc.CollectionDescLength > 0);
reportId = (ULONG)*ReportData;
reportIdentifier = GetReportIdentifier(fdoExtension, reportId); if (reportIdentifier){ PHIDP_COLLECTION_DESC collectionDesc; PHIDCLASS_COLLECTION hidpCollection;
collectionDesc = GetCollectionDesc(fdoExtension, reportIdentifier->CollectionNumber); hidpCollection = GetHidclassCollection(fdoExtension, reportIdentifier->CollectionNumber);
if (collectionDesc && hidpCollection){ ULONG reportLength = collectionDesc->InputLength;
if (*UserBufferLen >= reportLength){ RtlCopyMemory(UserBuffer, ReportData, reportLength); result = STATUS_SUCCESS; } else { result = STATUS_INVALID_BUFFER_SIZE; }
/*
* Return the actual length of the report (whether we copied or not). */ *UserBufferLen = reportLength; } }
ASSERT((result == STATUS_SUCCESS) || (result == STATUS_INVALID_BUFFER_SIZE)); return result; }
/*
******************************************************************************** * HidpAddDevice ******************************************************************************** * * Routine Description: * * This routine is called by configmgr when a new PDO is dected. * It creates an Functional Device Object (FDO) and attaches it to the * PDO. * * Arguments: * * DriverObject - pointer to the minidriver's driver object. * * PhysicalDeviceObject - pointer to the PDO that the minidriver got in it's * AddDevice() routine. * * Return Value: * * Standard NT return value. * */ NTSTATUS HidpAddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject) { PHIDCLASS_DRIVER_EXTENSION hidDriverExtension; PHIDCLASS_DEVICE_EXTENSION hidClassExtension; NTSTATUS status; UNICODE_STRING uPdoName; PWSTR wPdoName; PVOID miniDeviceExtension; ULONG totalExtensionSize; ULONG thisHidId; PDEVICE_OBJECT functionalDeviceObject;
PAGED_CODE();
DBG_COMMON_ENTRY()
DBG_RECORD_DEVOBJ(PhysicalDeviceObject, "minidrvr PDO")
//
// Get a pointer to our per-driver extension, make sure it's one of ours.
//
hidDriverExtension = RefDriverExt(DriverObject); if (hidDriverExtension){
ASSERT(DriverObject == hidDriverExtension->MinidriverObject);
//
// Construct a name for the FDO. The only requirement, really, is
// that it's unique. For now we'll call them "_HIDx", where 'x' is some
// unique number.
//
/*
* PDO name has form "\Device\_HIDx". */ wPdoName = ALLOCATEPOOL(NonPagedPool, sizeof(L"\\Device\\_HID00000000")); if (wPdoName){
//
// Get the current value of NextHidId and increment it. Since
// InterlockedIncrement() returns the incremented value, subtract one to
// get the pre-increment value of NextHidId;
//
thisHidId = InterlockedIncrement(&HidpNextHidNumber) - 1; StringCbPrintfW(wPdoName, sizeof(L"\\Device\\_HID00000000"), L"\\Device\\_HID%08x", thisHidId); RtlInitUnicodeString(&uPdoName, wPdoName);
//
// We've got a counted-string version of the device object name. Calculate
// the total size of the device extension and create the FDO.
//
totalExtensionSize = sizeof(HIDCLASS_DEVICE_EXTENSION) + hidDriverExtension->DeviceExtensionSize;
status = IoCreateDevice( DriverObject, // driver object
totalExtensionSize, // extension size
&uPdoName, // name of the FDO
FILE_DEVICE_UNKNOWN, //
0, // DeviceCharacteristics
FALSE, // not exclusive
&functionalDeviceObject );
if (NT_SUCCESS(status)){
DBG_RECORD_DEVOBJ(functionalDeviceObject, "device FDO")
ObReferenceObject(functionalDeviceObject);
ASSERT(DriverObject->DeviceObject == functionalDeviceObject); ASSERT(functionalDeviceObject->DriverObject == DriverObject);
//
// We've created the device object. Fill in the minidriver's extension
// pointer and attach this FDO to the PDO.
//
hidClassExtension = functionalDeviceObject->DeviceExtension; RtlZeroMemory(hidClassExtension, totalExtensionSize);
hidClassExtension->isClientPdo = FALSE;
//
// Assign the name of the minidriver's PDO to our FDO.
//
hidClassExtension->fdoExt.name = uPdoName;
//
// The minidriver extension lives in the device extension and starts
// immediately after our HIDCLASS_DEVICE_EXTENSION structure. Note
// that the first structure in the HIDCLASS_DEVICE_EXTENSION is the
// public HID_DEVICE_EXTENSION structure, which is where the pointer
// to the minidriver's per-device extension area lives.
//
miniDeviceExtension = (PVOID)(hidClassExtension + 1); hidClassExtension->hidExt.MiniDeviceExtension = miniDeviceExtension;
//
// Get a pointer to the physical device object passed in. This device
// object should already have the DO_DEVICE_INITIALIZING flag cleared.
//
ASSERT( (PhysicalDeviceObject->Flags & DO_DEVICE_INITIALIZING) == 0 );
//
// Attach the FDO to the PDO, storing the device object at the top of the
// stack in our device extension.
//
hidClassExtension->hidExt.NextDeviceObject = IoAttachDeviceToDeviceStack( functionalDeviceObject, PhysicalDeviceObject );
ASSERT(DriverObject->DeviceObject == functionalDeviceObject); ASSERT(functionalDeviceObject->DriverObject == DriverObject);
//
// The functional device requires two stack locations: one for the class
// driver, and one for the minidriver.
//
functionalDeviceObject->StackSize++;
//
// We'll need a pointer to the physical device object as well for PnP
// purposes. Note that it's a virtual certainty that NextDeviceObject
// and PhysicalDeviceObject are identical.
//
hidClassExtension->hidExt.PhysicalDeviceObject = PhysicalDeviceObject; hidClassExtension->Signature = HID_DEVICE_EXTENSION_SIG; hidClassExtension->fdoExt.fdo = functionalDeviceObject; hidClassExtension->fdoExt.driverExt = hidDriverExtension; hidClassExtension->fdoExt.outstandingRequests = 0; hidClassExtension->fdoExt.openCount = 0; hidClassExtension->fdoExt.state = DEVICE_STATE_INITIALIZED; hidClassExtension->fdoExt.isPresent = TRUE; KeInitializeSpinLock(&hidClassExtension->fdoExt.presentSpinLock); hidClassExtension->fdoExt.presentReported = TRUE;
//
// Selective suspend portion.
//
hidClassExtension->fdoExt.idleState = IdleDisabled; hidClassExtension->fdoExt.idleTimeoutValue = BAD_POINTER; KeInitializeSpinLock(&hidClassExtension->fdoExt.idleNotificationSpinLock); KeInitializeEvent(&hidClassExtension->fdoExt.idleDoneEvent, NotificationEvent, TRUE); hidClassExtension->fdoExt.idleNotificationRequest = BAD_POINTER; hidClassExtension->fdoExt.idleCallbackInfo.IdleCallback = HidpIdleNotificationCallback; hidClassExtension->fdoExt.idleCallbackInfo.IdleContext = (PVOID) hidClassExtension;
hidClassExtension->fdoExt.systemPowerState = PowerSystemWorking; hidClassExtension->fdoExt.devicePowerState = PowerDeviceD0;
hidClassExtension->fdoExt.waitWakeIrp = BAD_POINTER; KeInitializeSpinLock(&hidClassExtension->fdoExt.waitWakeSpinLock); hidClassExtension->fdoExt.isWaitWakePending = FALSE;
InitializeListHead(&hidClassExtension->fdoExt.collectionWaitWakeIrpQueue); KeInitializeSpinLock(&hidClassExtension->fdoExt.collectionWaitWakeIrpQueueSpinLock);
InitializeListHead(&hidClassExtension->fdoExt.collectionPowerDelayedIrpQueue); KeInitializeSpinLock(&hidClassExtension->fdoExt.collectionPowerDelayedIrpQueueSpinLock); hidClassExtension->fdoExt.numPendingPowerDelayedIrps = 0;
hidClassExtension->fdoExt.BusNumber = thisHidId;
#if DBG
InitFdoExtDebugInfo(hidClassExtension); #endif
EnqueueFdoExt(&hidClassExtension->fdoExt);
/*
* Indicate that this device object does direct I/O. * * Set the flag that causes the IO subsystem to decrement the device * object's reference count *before* sending down IRP_MJ_CLOSEs. We * need this because we delete the device object on the last close. */ functionalDeviceObject->Flags |= DO_DIRECT_IO;
/*
* The DO_POWER_PAGABLE bit of a device object * indicates to the kernel that the power-handling * code of the corresponding driver is pageable, and * so must be called at IRQL 0. * As a filter driver, we do not want to change the power * behavior of the driver stack in any way; therefore, * we copy this bit from the lower device object. */ functionalDeviceObject->Flags |= (PhysicalDeviceObject->Flags & DO_POWER_PAGABLE);
/*
* Must clear the initializing flag after initialization complete. */ functionalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
//
// Since we have NOT seen a start device, we CANNOT send any non
// pnp irps to the device yet. We need to do that in the start
// device requests.
//
//
// Call the minidriver to let it do any extension initialization
//
status = hidDriverExtension->AddDevice(DriverObject, functionalDeviceObject);
if (!NT_SUCCESS(status)) { DequeueFdoExt(&hidClassExtension->fdoExt); IoDetachDevice(hidClassExtension->hidExt.NextDeviceObject); ObDereferenceObject(functionalDeviceObject); IoDeleteDevice(functionalDeviceObject); ExFreePool( wPdoName ); } } else { DBGWARN(("IoCreateDevice failed with status: %x", status)); ExFreePool( wPdoName ); } } else { DBGWARN(("Pool allocation for fdo name failed.")); status = STATUS_INSUFFICIENT_RESOURCES; }
if (!NT_SUCCESS(status)){ DerefDriverExt(DriverObject); } } else { ASSERT(hidDriverExtension); status = STATUS_DEVICE_CONFIGURATION_ERROR; }
DBGSUCCESS(status, TRUE) DBG_COMMON_EXIT() return status; }
/*
******************************************************************************** * HidpDriverUnload ******************************************************************************** * * */ VOID HidpDriverUnload(IN struct _DRIVER_OBJECT *minidriverObject) { PHIDCLASS_DRIVER_EXTENSION hidDriverExt;
PAGED_CODE();
DBG_COMMON_ENTRY()
/*
* This extra de-reference will cause our hidDriverExtension's * reference count to eventually go to -1; at that time, we'll * dequeue it. */ hidDriverExt = DerefDriverExt(minidriverObject); ASSERT(hidDriverExt);
/*
* Chain the unload call to the minidriver. */ hidDriverExt->DriverUnload(minidriverObject);
DBG_COMMON_EXIT() }
NTSTATUS GetHIDRawReportDescriptor(FDO_EXTENSION *fdoExt, PIRP irp, ULONG descriptorLen) { NTSTATUS status;
if (descriptorLen){ PUCHAR rawReportDescriptor = ALLOCATEPOOL(NonPagedPool, descriptorLen);
if (rawReportDescriptor){ const ULONG retries = 3; ULONG i;
for (i = 0; i < retries; i++){ PIO_STACK_LOCATION irpSp;
irp->UserBuffer = rawReportDescriptor; irpSp = IoGetNextIrpStackLocation(irp);
ASSERT(irpSp->Parameters.DeviceIoControl.InputBufferLength == 0); ASSERT(irpSp->Parameters.DeviceIoControl.Type3InputBuffer == NULL); irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; irpSp->Parameters.DeviceIoControl.OutputBufferLength = descriptorLen; irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_GET_REPORT_DESCRIPTOR;
//
// Call the minidriver to get the report descriptor.
//
status = HidpCallDriverSynchronous(fdoExt->fdo, irp); if (NT_SUCCESS(status)){ if (irp->IoStatus.Information == descriptorLen){ fdoExt->rawReportDescriptionLength = descriptorLen; fdoExt->rawReportDescription = rawReportDescriptor; break; } else { DBGWARN(("GetHIDRawReportDescriptor (attempt #%d) returned %xh/%xh bytes", i, irp->IoStatus.Information, descriptorLen)) status = STATUS_DEVICE_DATA_ERROR; } } else { DBGWARN(("GetHIDRawReportDescriptor (attempt #%d) failed with status %xh.", i, status)) } }
if (!NT_SUCCESS(status)){ DBGWARN(("GetHIDRawReportDescriptor failed %d times.", retries)) ExFreePool(rawReportDescriptor); }
} else { DBGWARN(("alloc failed in GetHIDRawReportDescriptor")) status = STATUS_INSUFFICIENT_RESOURCES; } } else { DBGWARN(("GetHIDRawReportDescriptor: descriptorLen is zero.")) status = STATUS_DEVICE_DATA_ERROR; }
DBGSUCCESS(status, FALSE) return status; }
/*
******************************************************************************** * HidpGetDeviceDescriptor ******************************************************************************** * * */ NTSTATUS HidpGetDeviceDescriptor(FDO_EXTENSION *fdoExtension) { PIRP irp; PIO_STACK_LOCATION irpSp; NTSTATUS status; PHID_DESCRIPTOR hidDescriptor; ULONG rawReportDescriptorLength;
PAGED_CODE();
/*
* Retrieve: * * 1. Device descriptor (fixed portion) * 2. Device attributes * 3. Report descriptor */
hidDescriptor = &fdoExtension->hidDescriptor;
irp = IoAllocateIrp(fdoExtension->fdo->StackSize, FALSE); if (irp){ irpSp = IoGetNextIrpStackLocation(irp); irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_GET_DEVICE_DESCRIPTOR;
/*
* This IOCTL uses buffering type METHOD_NEITHER, so * the buffer is simply passed in irp->UserBuffer. */ irp->UserBuffer = hidDescriptor; irpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(HID_DESCRIPTOR); irpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
status = HidpCallDriverSynchronous(fdoExtension->fdo, irp); DBGASSERT((status == STATUS_SUCCESS), ("STATUS_SUCCESS not returned, %x returned",status), TRUE)
if (status == STATUS_SUCCESS){
if (irp->IoStatus.Information == sizeof(HID_DESCRIPTOR)){
irpSp = IoGetNextIrpStackLocation(irp);
ASSERT(irpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL); ASSERT(irpSp->Parameters.DeviceIoControl.InputBufferLength == 0); ASSERT(!irpSp->Parameters.DeviceIoControl.Type3InputBuffer);
irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_GET_DEVICE_ATTRIBUTES;
irp->UserBuffer = &fdoExtension->hidDeviceAttributes; irpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(HID_DEVICE_ATTRIBUTES);
status = HidpCallDriverSynchronous(fdoExtension->fdo, irp); DBGASSERT((status == STATUS_SUCCESS), ("STATUS_SUCCESS not returned, %x returned",status), TRUE)
if (NT_SUCCESS (status)) { /*
* We've got a hid descriptor, now we need to read the report descriptor. * * Find the descriptor describing the report. */ rawReportDescriptorLength = 0;
/*
* We currently don't support Physical descriptors, and have never * seen a device that implements them, so we only get the report descriptor * which MUST be the first descriptor. If physical descriptors are * to be supported, must realloc the hidDescriptor to hold the data * for the other descriptors and iterate over the descritpors. */ if (hidDescriptor->DescriptorList[0].bReportType == HID_REPORT_DESCRIPTOR_TYPE){ rawReportDescriptorLength = (ULONG)hidDescriptor->DescriptorList[0].wReportLength; status = GetHIDRawReportDescriptor(fdoExtension, irp, rawReportDescriptorLength); } else { status = STATUS_DEVICE_DATA_ERROR; } } else { status = STATUS_DEVICE_DATA_ERROR; } } else { status = STATUS_DEVICE_DATA_ERROR; } } else { status = STATUS_DEVICE_DATA_ERROR; }
IoFreeIrp(irp); } else { status = STATUS_INSUFFICIENT_RESOURCES; }
DBGSUCCESS(status, FALSE) return status; }
/*
******************************************************************************** * HidpCreateSymbolicLink ******************************************************************************** * * */ NTSTATUS HidpCreateSymbolicLink( IN PDO_EXTENSION *pdoExt, IN ULONG collectionNum, IN BOOLEAN Create, IN PDEVICE_OBJECT Pdo ) { NTSTATUS status; PHIDCLASS_COLLECTION classCollection;
PAGED_CODE();
classCollection = GetHidclassCollection(&pdoExt->deviceFdoExt->fdoExt, collectionNum); if (classCollection){ //
// We've got a collection. Figure out what it is and create a symbolic
// link for it. For now we assign the "input" guid to all hid devices.
// The reference string is simply the collection number, zero-padded
// to eight digits.
//
if (Create){
/*
* Mark the PDO as initialized */ Pdo->Flags |= DO_DIRECT_IO; Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
/*
* Create the symbolic link */ status = IoRegisterDeviceInterface( Pdo, (LPGUID)&GUID_CLASS_INPUT, NULL, &classCollection->SymbolicLinkName ); if (NT_SUCCESS(status)){
/*
* Now set the symbolic link for the association and store it.. */ ASSERT(ISPTR(pdoExt->name));
status = IoSetDeviceInterfaceState(&classCollection->SymbolicLinkName, TRUE); } } else {
/*
* Disable the symbolic link */ if (ISPTR(classCollection->SymbolicLinkName.Buffer)){ status = IoSetDeviceInterfaceState(&classCollection->SymbolicLinkName, FALSE); ExFreePool( classCollection->SymbolicLinkName.Buffer ); classCollection->SymbolicLinkName.Buffer = BAD_POINTER; } else { status = STATUS_SUCCESS; } } } else { status = STATUS_DEVICE_CONFIGURATION_ERROR; }
DBGSUCCESS(status, TRUE) return status; }
/*
******************************************************************************** * EnqueueInterruptReport ******************************************************************************** * * */ VOID EnqueueInterruptReport(PHIDCLASS_FILE_EXTENSION fileExtension, PHIDCLASS_REPORT report) { PHIDCLASS_REPORT reportToDrop = NULL;
RUNNING_DISPATCH();
/*
* If the queue is full, drop the oldest report. */ if (fileExtension->CurrentInputReportQueueSize >= fileExtension->MaximumInputReportQueueSize){ PLIST_ENTRY listEntry;
#if DBG
if (fileExtension->dbgNumReportsDroppedSinceLastRead++ == 0){ DBGWARN(("HIDCLASS dropping input reports because report queue (size %xh) is full ...", fileExtension->MaximumInputReportQueueSize)) DBGASSERT((fileExtension->CurrentInputReportQueueSize == fileExtension->MaximumInputReportQueueSize), ("Current report queue size (%xh) is greater than maximum (%xh)", fileExtension->CurrentInputReportQueueSize, fileExtension->MaximumInputReportQueueSize), FALSE); } #endif
ASSERT(!IsListEmpty(&fileExtension->ReportList));
listEntry = RemoveHeadList(&fileExtension->ReportList); reportToDrop = CONTAINING_RECORD(listEntry, HIDCLASS_REPORT, ListEntry); fileExtension->CurrentInputReportQueueSize--; }
/*
* Now queue the current report */ InsertTailList(&fileExtension->ReportList, &report->ListEntry); fileExtension->CurrentInputReportQueueSize++;
/*
* We don't have to be running < DPC_LEVEL to release reports since they * are allocated using NonPagePool. */ if (reportToDrop){ ExFreePool(reportToDrop); }
}
/*
******************************************************************************** * DequeueInterruptReport ******************************************************************************** * * Return the next interrupt report in the queue. * If maxLen is not -1, then only return the report if it is <= maxlen. */ PHIDCLASS_REPORT DequeueInterruptReport(PHIDCLASS_FILE_EXTENSION fileExtension, LONG maxLen) { PHIDCLASS_REPORT report;
RUNNING_DISPATCH();
if (IsListEmpty(&fileExtension->ReportList)){ report = NULL; } else { PLIST_ENTRY listEntry = RemoveHeadList(&fileExtension->ReportList); report = CONTAINING_RECORD(listEntry, HIDCLASS_REPORT, ListEntry);
if ((maxLen > 0) && (report->reportLength > (ULONG)maxLen)){ /*
* This report is too big for the caller. * So put the report back in the queue and return NULL. */ InsertHeadList(&fileExtension->ReportList, &report->ListEntry); report = NULL; } else { InitializeListHead(&report->ListEntry); ASSERT(fileExtension->CurrentInputReportQueueSize > 0); fileExtension->CurrentInputReportQueueSize--;
#if DBG
if (fileExtension->dbgNumReportsDroppedSinceLastRead > 0){ DBGWARN(("... successful read(/flush) after %d reports were dropped.", fileExtension->dbgNumReportsDroppedSinceLastRead)); fileExtension->dbgNumReportsDroppedSinceLastRead = 0; } #endif
} }
return report; }
/*
******************************************************************************** * HidpDestroyFileExtension ******************************************************************************** * * */ VOID HidpDestroyFileExtension(PHIDCLASS_COLLECTION collection, PHIDCLASS_FILE_EXTENSION FileExtension) { PFILE_OBJECT fileObject;
//
// Flush all of the pending reports on the file extension
//
HidpFlushReportQueue(FileExtension);
/*
* Fail all the pending reads * (it would be nice if apps always cancelled all their reads * before closing the device, but this is not always the case). */ CompleteAllPendingReadsForFileExtension(collection, FileExtension);
//
// Indicate in the file object that this file extension has gone away.
//
fileObject = FileExtension->FileObject; #if DBG
fileObject->FsContext = NULL; #endif
//
// Free our extension
//
#if DBG
FileExtension->Signature = ~HIDCLASS_FILE_EXTENSION_SIG; #endif
ExFreePool( FileExtension ); }
/*
******************************************************************************** * HidpFlushReportQueue ******************************************************************************** * * */ VOID HidpFlushReportQueue(IN PHIDCLASS_FILE_EXTENSION fileExtension) { PHIDCLASS_REPORT report; KIRQL oldIrql;
LockFileExtension(fileExtension, &oldIrql); while (report = DequeueInterruptReport(fileExtension, -1)){ //
// Ok to call this at DISPATCH_LEVEL, since report is NonPagedPool
//
ExFreePool(report); } UnlockFileExtension(fileExtension, oldIrql); }
/*
******************************************************************************** * HidpGetCollectionInformation ******************************************************************************** * * */ NTSTATUS HidpGetCollectionInformation( IN FDO_EXTENSION *fdoExtension, IN ULONG collectionNumber, IN PVOID Buffer, IN OUT PULONG BufferSize ) { HID_COLLECTION_INFORMATION hidCollectionInfo; PHIDP_COLLECTION_DESC hidCollectionDesc; ULONG bytesToCopy; NTSTATUS status;
/*
* Get a pointer to the appropriate collection descriptor. */ hidCollectionDesc = GetCollectionDesc(fdoExtension, collectionNumber); if (hidCollectionDesc){ //
// Fill in hidCollectionInfo
//
hidCollectionInfo.DescriptorSize = hidCollectionDesc->PreparsedDataLength;
hidCollectionInfo.Polled = fdoExtension->driverExt->DevicesArePolled;
hidCollectionInfo.VendorID = fdoExtension->hidDeviceAttributes.VendorID; hidCollectionInfo.ProductID = fdoExtension->hidDeviceAttributes.ProductID; hidCollectionInfo.VersionNumber = fdoExtension->hidDeviceAttributes.VersionNumber;
//
// Copy as much of hidCollectionInfo as will fit in the output buffer.
//
if (*BufferSize < sizeof( HID_COLLECTION_INFORMATION)){ /*
* The user's buffer is not big enough. * We'll return the size that the buffer needs to be. * Must return this with a real error code (not a warning) * so that IO post-processing does not copy into (and past) * the user's buffer. */ bytesToCopy = *BufferSize; status = STATUS_INVALID_BUFFER_SIZE; } else { bytesToCopy = sizeof( HID_COLLECTION_INFORMATION ); status = STATUS_SUCCESS; }
RtlCopyMemory(Buffer, &hidCollectionInfo, bytesToCopy); *BufferSize = sizeof (HID_COLLECTION_INFORMATION); } else { status = STATUS_DATA_ERROR; }
DBGSUCCESS(status, FALSE) return status; }
/*
******************************************************************************** * HidpGetCollectionDescriptor ******************************************************************************** * * */ NTSTATUS HidpGetCollectionDescriptor( IN FDO_EXTENSION *fdoExtension, IN ULONG collectionId, IN PVOID Buffer, IN OUT PULONG BufferSize) { PHIDP_COLLECTION_DESC hidCollectionDesc; ULONG bytesToCopy; NTSTATUS status;
hidCollectionDesc = GetCollectionDesc(fdoExtension, collectionId); if (hidCollectionDesc){
/*
* Copy as much of the preparsed data as will fit in the output buffer. */ if (*BufferSize < hidCollectionDesc->PreparsedDataLength){ /*
* The user's buffer is not big enough for all the * preparsed data. * We'll return the size that the buffer needs to be. * Must return this with a real error code (not a warning) * so that IO post-processing does not copy into (and past) * the user's buffer. */ bytesToCopy = *BufferSize; status = STATUS_INVALID_BUFFER_SIZE; } else { bytesToCopy = hidCollectionDesc->PreparsedDataLength; status = STATUS_SUCCESS; }
RtlCopyMemory(Buffer, hidCollectionDesc->PreparsedData, bytesToCopy); *BufferSize = hidCollectionDesc->PreparsedDataLength; } else { status = STATUS_DATA_ERROR; }
DBGSUCCESS(status, FALSE) return status; }
/*
******************************************************************************** * GetReportIdentifier ******************************************************************************** * * */ PHIDP_REPORT_IDS GetReportIdentifier(FDO_EXTENSION *fdoExtension, ULONG reportId) { PHIDP_REPORT_IDS result = NULL; PHIDP_DEVICE_DESC deviceDesc = &fdoExtension->deviceDesc; ULONG i;
if (deviceDesc->ReportIDs){ for (i = 0; i < deviceDesc->ReportIDsLength; i++){ if (deviceDesc->ReportIDs[i].ReportID == reportId){ result = &deviceDesc->ReportIDs[i]; break; } } }
DBGASSERT(result, ("Bogus report identifier requested %d", reportId), FALSE)
return result; }
/*
******************************************************************************** * GetCollectionDesc ******************************************************************************** * * */ PHIDP_COLLECTION_DESC GetCollectionDesc(FDO_EXTENSION *fdoExtension, ULONG collectionId) { PHIDP_COLLECTION_DESC result = NULL; PHIDP_DEVICE_DESC deviceDesc = &fdoExtension->deviceDesc; ULONG i;
if (deviceDesc->CollectionDesc){ for (i = 0; i < deviceDesc->CollectionDescLength; i++){ if (deviceDesc->CollectionDesc[i].CollectionNumber == collectionId){ result = &deviceDesc->CollectionDesc[i]; break; } } }
ASSERT(result); return result; }
/*
******************************************************************************** * GetHidclassCollection ******************************************************************************** * */ PHIDCLASS_COLLECTION GetHidclassCollection(FDO_EXTENSION *fdoExtension, ULONG collectionId) { PHIDCLASS_COLLECTION result = NULL; PHIDP_DEVICE_DESC deviceDesc = &fdoExtension->deviceDesc; ULONG i;
if (ISPTR(fdoExtension->classCollectionArray)){ for (i = 0; i < deviceDesc->CollectionDescLength; i++){ if (fdoExtension->classCollectionArray[i].CollectionNumber == collectionId){ result = &fdoExtension->classCollectionArray[i]; break; } } }
return result; }
/*
******************************************************************************** * MakeClientPDOName ******************************************************************************** * * */ PUNICODE_STRING MakeClientPDOName(PUNICODE_STRING fdoName, ULONG collectionId) { PUNICODE_STRING uPdoName;
PAGED_CODE();
uPdoName = (PUNICODE_STRING)ALLOCATEPOOL(NonPagedPool, sizeof(UNICODE_STRING)); if (uPdoName){ PWSTR wPdoName;
wPdoName = (PWSTR)ALLOCATEPOOL( PagedPool, fdoName->Length+sizeof(L"#COLLECTION0000000x")); if (wPdoName){ StringCchPrintfW(wPdoName, fdoName->Length+sizeof(L"#COLLECTION0000000x"), L"%s#COLLECTION%08x", fdoName->Buffer, collectionId); RtlInitUnicodeString(uPdoName, wPdoName); } else { ExFreePool(uPdoName); uPdoName = NULL; } }
return uPdoName; }
/*
******************************************************************************** * HidpCreateClientPDOs ******************************************************************************** * * */ NTSTATUS HidpCreateClientPDOs(PHIDCLASS_DEVICE_EXTENSION hidClassExtension) { NTSTATUS ntStatus = STATUS_SUCCESS; PHIDCLASS_DRIVER_EXTENSION hidDriverExtension; FDO_EXTENSION *fdoExt;
PAGED_CODE();
ASSERT(!hidClassExtension->isClientPdo);
fdoExt = &hidClassExtension->fdoExt;
hidDriverExtension = RefDriverExt(fdoExt->driverExt->MinidriverObject); if (hidDriverExtension){
/*
* We will create one PDO for each collection on this device. */ ULONG numPDOs = fdoExt->deviceDesc.CollectionDescLength;
if (numPDOs){
if (!ISPTR(fdoExt->deviceRelations)) { fdoExt->deviceRelations = (PDEVICE_RELATIONS) ALLOCATEPOOL(NonPagedPool, sizeof(DEVICE_RELATIONS) + (numPDOs*sizeof(PDEVICE_OBJECT))); }
if (fdoExt->deviceRelations){
if (!ISPTR(fdoExt->collectionPdoExtensions)) { fdoExt->collectionPdoExtensions = ALLOCATEPOOL(NonPagedPool, numPDOs*sizeof(PHIDCLASS_DEVICE_EXTENSION));
}
if (fdoExt->collectionPdoExtensions){
ULONG i;
fdoExt->deviceRelations->Count = numPDOs;
for (i = 0; i < numPDOs; i++){ PUNICODE_STRING uPdoName; ULONG totalExtensionSize; ULONG collectionNum = fdoExt->deviceDesc.CollectionDesc[i].CollectionNumber; PDEVICE_OBJECT newClientPdo;
/*
* Construct a name for the PDO we're about to create. */ uPdoName = MakeClientPDOName(&fdoExt->name, collectionNum); if (uPdoName){ /*
* We use the same device extension for the client PDOs as for our FDO. */ totalExtensionSize = sizeof(HIDCLASS_DEVICE_EXTENSION) + hidDriverExtension->DeviceExtensionSize;
/*
* Create a PDO to represent this collection. * Since hidclass is not a real driver, it does not have a driver object; * so just use the minidriver's driver object. * * NOTE - newClientPdo->NextDevice will point to this minidriver's NextDevice */ ntStatus = IoCreateDevice( hidDriverExtension->MinidriverObject, // driver object
totalExtensionSize, // extension size
NULL, // name of the PDO
FILE_DEVICE_UNKNOWN, // Device type
FILE_AUTOGENERATED_DEVICE_NAME, // DeviceCharacteristics
FALSE, // not exclusive
&newClientPdo); if (NT_SUCCESS(ntStatus)){ PHIDCLASS_DEVICE_EXTENSION clientPdoExtension = newClientPdo->DeviceExtension; USHORT usagePage = fdoExt->deviceDesc.CollectionDesc[i].UsagePage; USHORT usage = fdoExt->deviceDesc.CollectionDesc[i].Usage;
DBG_RECORD_DEVOBJ(newClientPdo, "cltn PDO")
ObReferenceObject(newClientPdo);
/*
* We may pass Irps from the upper stack to the lower stack, * so make sure there are enough stack locations for the IRPs * we pass down. */ newClientPdo->StackSize = fdoExt->fdo->StackSize+1;
/*
* Initialize the PDO's extension */ RtlZeroMemory(clientPdoExtension, totalExtensionSize);
clientPdoExtension->hidExt = hidClassExtension->hidExt; clientPdoExtension->isClientPdo = TRUE; clientPdoExtension->Signature = HID_DEVICE_EXTENSION_SIG;
clientPdoExtension->pdoExt.collectionNum = collectionNum; clientPdoExtension->pdoExt.collectionIndex = i; clientPdoExtension->pdoExt.pdo = newClientPdo; clientPdoExtension->pdoExt.state = COLLECTION_STATE_UNINITIALIZED; clientPdoExtension->pdoExt.deviceFdoExt = hidClassExtension; clientPdoExtension->pdoExt.StatusChangeFn = BAD_POINTER;
clientPdoExtension->pdoExt.name = uPdoName;
clientPdoExtension->pdoExt.devicePowerState = PowerDeviceD0; clientPdoExtension->pdoExt.systemPowerState = fdoExt->systemPowerState; clientPdoExtension->pdoExt.MouseOrKeyboard = ((usagePage == HID_USAGE_PAGE_GENERIC) && ((usage == HID_USAGE_GENERIC_POINTER) || (usage == HID_USAGE_GENERIC_MOUSE) || (usage == HID_USAGE_GENERIC_KEYBOARD) || (usage == HID_USAGE_GENERIC_KEYPAD)));
IoInitializeRemoveLock (&clientPdoExtension->pdoExt.removeLock, HIDCLASS_POOL_TAG, 0, 10); KeInitializeSpinLock (&clientPdoExtension->pdoExt.remoteWakeSpinLock); clientPdoExtension->pdoExt.remoteWakeIrp = NULL;
/*
* Store a pointer to the new PDO in the FDO extension's deviceRelations array. */ fdoExt->deviceRelations->Objects[i] = newClientPdo;
/*
* Store a pointer to the PDO's extension. */ fdoExt->collectionPdoExtensions[i] = clientPdoExtension;
newClientPdo->Flags |= DO_POWER_PAGABLE; newClientPdo->Flags &= ~DO_DEVICE_INITIALIZING; } else { break; } } else { ntStatus = STATUS_NO_MEMORY; } }
if (!NT_SUCCESS(ntStatus)){ ExFreePool(fdoExt->collectionPdoExtensions); fdoExt->collectionPdoExtensions = BAD_POINTER; } } else { ntStatus = STATUS_NO_MEMORY; }
if (!NT_SUCCESS(ntStatus)){ ExFreePool(fdoExt->deviceRelations); fdoExt->deviceRelations = BAD_POINTER; } } else { ntStatus = STATUS_NO_MEMORY; } } else { ASSERT(numPDOs); ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR; } DerefDriverExt(fdoExt->driverExt->MinidriverObject); } else { ASSERT(hidDriverExtension); ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR; }
DBGSUCCESS(ntStatus, TRUE) return ntStatus; }
/*
******************************************************************************** * MemDup ******************************************************************************** * * Return a fresh copy of the argument. * */ PVOID MemDup(POOL_TYPE PoolType, PVOID dataPtr, ULONG length) { PVOID newPtr;
newPtr = (PVOID)ALLOCATEPOOL(PoolType, length); if (newPtr){ RtlCopyMemory(newPtr, dataPtr, length); }
ASSERT(newPtr); return newPtr; }
/*
******************************************************************************** * WStrLen ******************************************************************************** * */ ULONG WStrLen(PWCHAR str) { ULONG result = 0;
while (*str++ != UNICODE_NULL){ result++; }
return result; }
/*
******************************************************************************** * WStrCpy ******************************************************************************** * */ ULONG WStrCpy(PWCHAR dest, PWCHAR src) { ULONG result = 0;
while (*dest++ = *src++){ result++; }
return result; }
BOOLEAN WStrCompareN(PWCHAR str1, PWCHAR str2, ULONG maxChars) { while ((maxChars > 0) && *str1 && (*str1 == *str2)){ maxChars--; str1++; str2++; }
return (BOOLEAN)((maxChars == 0) || (!*str1 && !*str2)); }
/*
******************************************************************************** * HidpNumberToString ******************************************************************************** * */ void HidpNumberToString(PWCHAR String, USHORT Number, USHORT stringLen) { const static WCHAR map[] = L"0123456789ABCDEF"; LONG i = 0; ULONG nibble = 0;
ASSERT(stringLen);
for (i = stringLen-1; i >= 0; i--) { String[i] = map[Number & 0x0F]; Number >>= 4; } }
/*
******************************************************************************** * CopyDeviceRelations ******************************************************************************** * * */ PDEVICE_RELATIONS CopyDeviceRelations(PDEVICE_RELATIONS deviceRelations) { PDEVICE_RELATIONS newDeviceRelations;
if (deviceRelations){ ULONG size = sizeof(DEVICE_RELATIONS) + (deviceRelations->Count*sizeof(PDEVICE_OBJECT)); newDeviceRelations = MemDup(PagedPool, deviceRelations, size); } else { newDeviceRelations = NULL; }
return newDeviceRelations; }
/*
******************************************************************************** * HidpQueryDeviceRelations ******************************************************************************** * * */ NTSTATUS HidpQueryDeviceRelations(IN PHIDCLASS_DEVICE_EXTENSION hidClassExtension, IN OUT PIRP Irp) { PIO_STACK_LOCATION ioStack; NTSTATUS ntStatus = STATUS_SUCCESS; KIRQL irql;
ASSERT(!hidClassExtension->isClientPdo);
ioStack = IoGetCurrentIrpStackLocation(Irp);
if (ioStack->Parameters.QueryDeviceRelations.Type == BusRelations) {
if (ISPTR(hidClassExtension->fdoExt.deviceRelations)){ /*
* Don't call HidpCreateClientPDOs again if it's * already been called for this device. */ ntStatus = STATUS_SUCCESS; } else {
ntStatus = HidpCreateClientPDOs(hidClassExtension); }
if (NT_SUCCESS(ntStatus)){ ULONG i;
KeAcquireSpinLock(&hidClassExtension->fdoExt.presentSpinLock, &irql);
/*
* NTKERN expects a new pointer each time it calls QUERY_DEVICE_RELATIONS; * it then FREES THE POINTER. * So we have to return a new pointer each time, whether or not we actually * created our copy of the device relations for this call. */
if (!hidClassExtension->fdoExt.isPresent) { hidClassExtension->fdoExt.presentReported = FALSE;
KeReleaseSpinLock(&hidClassExtension->fdoExt.presentSpinLock, irql);
Irp->IoStatus.Information = (ULONG_PTR)ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS)); if (Irp->IoStatus.Information) { PHIDCLASS_DEVICE_EXTENSION clientPdoExtension; if (ISPTR(hidClassExtension->fdoExt.deviceRelations)) { ExFreePool(hidClassExtension->fdoExt.deviceRelations); }
hidClassExtension->fdoExt.deviceRelations = BAD_POINTER; ((PDEVICE_RELATIONS) (Irp->IoStatus.Information))->Count = 0; ((PDEVICE_RELATIONS) (Irp->IoStatus.Information))->Objects[0] = NULL; } } else { hidClassExtension->fdoExt.presentReported = TRUE; KeReleaseSpinLock(&hidClassExtension->fdoExt.presentSpinLock, irql);
Irp->IoStatus.Information = (ULONG_PTR)CopyDeviceRelations(hidClassExtension->fdoExt.deviceRelations);
if (Irp->IoStatus.Information) {
/*
* PnP dereferences each device object * in the device relations list after each call. * So for each call, add an extra reference. */ for (i = 0; i < hidClassExtension->fdoExt.deviceRelations->Count; i++){ ObReferenceObject(hidClassExtension->fdoExt.deviceRelations->Objects[i]); hidClassExtension->fdoExt.deviceRelations->Objects[i]->Flags &= ~DO_DEVICE_INITIALIZING; }
} }
if (!Irp->IoStatus.Information){ ntStatus = STATUS_INSUFFICIENT_RESOURCES; } }
DBGSUCCESS(ntStatus, TRUE) } else { /*
* We don't support this option, so just maintain * the current status (do not return STATUS_NOT_SUPPORTED). */ ntStatus = Irp->IoStatus.Status; }
return ntStatus; }
/*
******************************************************************************** * HidpQueryCollectionCapabilities ******************************************************************************** * * */ NTSTATUS HidpQueryCollectionCapabilities( PDO_EXTENSION *pdoExt, IN OUT PIRP Irp) { PDEVICE_CAPABILITIES deviceCapabilities; PIO_STACK_LOCATION ioStack; FDO_EXTENSION *fdoExt; NTSTATUS status;
PAGED_CODE();
ASSERT(pdoExt->deviceFdoExt->Signature == HID_DEVICE_EXTENSION_SIG); fdoExt = &pdoExt->deviceFdoExt->fdoExt; ioStack = IoGetCurrentIrpStackLocation(Irp);
deviceCapabilities = ioStack->Parameters.DeviceCapabilities.Capabilities; if (deviceCapabilities){
/*
* Set all fields for the collection-PDO as for the device-FDO * by default. */ *deviceCapabilities = fdoExt->deviceCapabilities;
/*
* Now override the fields we care about. */ deviceCapabilities->LockSupported = FALSE; deviceCapabilities->EjectSupported = FALSE; deviceCapabilities->Removable = FALSE; deviceCapabilities->DockDevice = FALSE; deviceCapabilities->UniqueID = FALSE; deviceCapabilities->SilentInstall = TRUE;
/*
* This field is very important; * it causes HIDCLASS to get the START_DEVICE IRP immediately, * if the device is not a keyboard or mouse. */ deviceCapabilities->RawDeviceOK = !pdoExt->MouseOrKeyboard;
/*
* This bit indicates that the device may be removed on NT * without running the 'hot-unplug' utility. */ deviceCapabilities->SurpriseRemovalOK = TRUE;
DBGVERBOSE(("WAKE info: sysWake=%d devWake=%d; wake from D0=%d D1=%d D2=%d D3=%d.", deviceCapabilities->SystemWake, deviceCapabilities->DeviceWake, (ULONG)deviceCapabilities->WakeFromD0, (ULONG)deviceCapabilities->WakeFromD1, (ULONG)deviceCapabilities->WakeFromD2, (ULONG)deviceCapabilities->WakeFromD3))
status = STATUS_SUCCESS; } else { status = STATUS_DEVICE_CONFIGURATION_ERROR; }
Irp->IoStatus.Information = (ULONG_PTR)deviceCapabilities;
DBGSUCCESS(status, TRUE) return status; }
/*
******************************************************************************** * BuildCompatibleID ******************************************************************************** * * Return a multi-string consisting of compatibility id's for this device * in increasingly generic order (ending with HID_GENERIC_DEVICE). * * author: kenray * */ PWSTR BuildCompatibleID(PHIDCLASS_DEVICE_EXTENSION hidClassExtension) { USHORT usage, usagePage; ULONG spLength; ULONG totLength; ULONG i; PWSTR specificId = NULL; PWSTR compatIdList; PWSTR genericId; FDO_EXTENSION *fdoExt;
PAGED_CODE();
ASSERT(hidClassExtension->isClientPdo); fdoExt = &hidClassExtension->pdoExt.deviceFdoExt->fdoExt;
ASSERT(ISPTR(fdoExt->deviceDesc.CollectionDesc));
i = hidClassExtension->pdoExt.collectionIndex; usagePage = fdoExt->deviceDesc.CollectionDesc[i].UsagePage; usage = fdoExt->deviceDesc.CollectionDesc[i].Usage;
switch (usagePage) { case HID_USAGE_PAGE_GENERIC: switch (usage) { case HID_USAGE_GENERIC_POINTER: case HID_USAGE_GENERIC_MOUSE: specificId = HIDCLASS_SYSTEM_MOUSE; break; case HID_USAGE_GENERIC_KEYBOARD: case HID_USAGE_GENERIC_KEYPAD: specificId = HIDCLASS_SYSTEM_KEYBOARD; break; case HID_USAGE_GENERIC_JOYSTICK: case HID_USAGE_GENERIC_GAMEPAD: specificId = HIDCLASS_SYSTEM_GAMING_DEVICE; break; case HID_USAGE_GENERIC_SYSTEM_CTL: specificId = HIDCLASS_SYSTEM_CONTROL; break; } break;
case HID_USAGE_PAGE_CONSUMER: specificId = HIDCLASS_SYSTEM_CONSUMER_DEVICE; break;
default: break; }
spLength = (specificId) ? (WStrLen(specificId)+1) : 0;
totLength = spLength + HIDCLASS_COMPATIBLE_ID_GENERIC_LENGTH + HIDCLASS_COMPATIBLE_ID_STANDARD_LENGTH + 1;
compatIdList = ALLOCATEPOOL(NonPagedPool, totLength * sizeof(WCHAR)); if (compatIdList) {
RtlZeroMemory (compatIdList, totLength * sizeof(WCHAR)); if (specificId) { RtlCopyMemory (compatIdList, specificId, spLength * sizeof (WCHAR)); }
genericId = compatIdList + spLength; totLength = HIDCLASS_COMPATIBLE_ID_GENERIC_LENGTH; RtlCopyMemory (genericId, HIDCLASS_COMPATIBLE_ID_GENERIC_NAME, totLength*sizeof(WCHAR));
HidpNumberToString (genericId + HIDCLASS_COMPATIBLE_ID_PAGE_OFFSET, usagePage, 4);
HidpNumberToString (genericId + HIDCLASS_COMPATIBLE_ID_USAGE_OFFSET, usage, 4);
RtlCopyMemory (genericId + totLength, HIDCLASS_COMPATIBLE_ID_STANDARD_NAME, HIDCLASS_COMPATIBLE_ID_STANDARD_LENGTH * sizeof (WCHAR)); }
return compatIdList; }
/*
******************************************************************************** * SubstituteBusNames ******************************************************************************** * * oldIDs is a multi-String of hardware IDs. * * 1. Return a new string with each "<busName>\" prefix replaced by "HID\". * * 2. If the device has multiple collections, append "&Colxx" to each id. * */ PWCHAR SubstituteBusNames(PWCHAR oldIDs, FDO_EXTENSION *fdoExt, PDO_EXTENSION *pdoExt) { ULONG newIdLen; PWCHAR id, newIDs; ULONG numCollections; WCHAR colNumStr[] = L"&Colxx";
PAGED_CODE();
numCollections = fdoExt->deviceDesc.CollectionDescLength; ASSERT(numCollections > 0);
for (id = oldIDs, newIdLen = 0; *id; ){ ULONG thisIdLen = WStrLen(id);
/*
* This is a little sloppy because we're actually going to chop * off the other bus name; but better this than walking each string. */ newIdLen += thisIdLen + 1 + sizeof("HID\\");
if (numCollections > 1){ newIdLen += sizeof(colNumStr)/sizeof(WCHAR); }
id += thisIdLen + 1; }
/*
* Add one for the extra NULL at the end of the multi-string. */ newIdLen++;
newIDs = ALLOCATEPOOL(NonPagedPool, newIdLen*sizeof(WCHAR)); if (newIDs){ ULONG oldIdOff, newIdOff;
/*
* Copy each string in the multi-string, replacing the bus name. */ for (oldIdOff = newIdOff = 0; oldIDs[oldIdOff]; ){ ULONG thisIdLen = WStrLen(oldIDs+oldIdOff); ULONG devIdOff;
/*
* Copy the new bus name to the new string. */ newIdOff += WStrCpy(newIDs+newIdOff, L"HID\\");
/*
* Go past the old bus name in the old string. */ for (devIdOff = 0; oldIDs[oldIdOff+devIdOff]; devIdOff++){ if (oldIDs[oldIdOff+devIdOff] == L'\\'){ break; } }
/*
* Copy the rest of this device id. */ if (oldIDs[oldIdOff+devIdOff] == L'\\'){ devIdOff++; } else { /*
* Strange -- no bus name in hardware id. * Just copy the entire id. */ devIdOff = 0; } newIdOff += WStrCpy(newIDs+newIdOff, oldIDs+oldIdOff+devIdOff);
if (numCollections > 1){ /*
* If there is more than one collection, * then also append the collection number. */ HidpNumberToString(colNumStr+4, (USHORT)pdoExt->collectionNum, 2); newIdOff += WStrCpy(newIDs+newIdOff, colNumStr); }
/*
* Go past the single string terminator. */ newIdOff++;
oldIdOff += thisIdLen + 1; }
/*
* Add extra NULL to terminate multi-string. */ newIDs[newIdOff] = UNICODE_NULL; }
return newIDs; }
NTSTATUS HidpQueryInterface( IN PHIDCLASS_DEVICE_EXTENSION hidClassExtension, IN OUT PIRP Irp ) { PIO_STACK_LOCATION irpSp;
PAGED_CODE();
ASSERT(hidClassExtension->isClientPdo); irpSp = IoGetCurrentIrpStackLocation(Irp);
if (RtlEqualMemory(irpSp->Parameters.QueryInterface.InterfaceType, &GUID_HID_INTERFACE_NOTIFY, sizeof(GUID))) { PDO_EXTENSION *pdoExt; PHID_INTERFACE_NOTIFY_PNP notify;
notify = (PHID_INTERFACE_NOTIFY_PNP) irpSp->Parameters.QueryInterface.Interface; if (notify->Size != sizeof(HID_INTERFACE_NOTIFY_PNP) || notify->Version < 1 || notify->StatusChangeFn == NULL) { //
// return STATUS_UNSUPPORTED probably
//
return Irp->IoStatus.Status; }
pdoExt = &hidClassExtension->pdoExt;
pdoExt->StatusChangeFn = notify->StatusChangeFn; pdoExt->StatusChangeContext = notify->CallbackContext; return STATUS_SUCCESS; } else if (RtlEqualMemory(irpSp->Parameters.QueryInterface.InterfaceType, &GUID_HID_INTERFACE_HIDPARSE, sizeof(GUID))) { //
// Required for Generic Input, to remove the direct link
// b/w win32k and hidparse.
//
PHID_INTERFACE_HIDPARSE hidparse;
hidparse = (PHID_INTERFACE_HIDPARSE) irpSp->Parameters.QueryInterface.Interface; if (hidparse->Size != sizeof(HID_INTERFACE_HIDPARSE) || hidparse->Version < 1) { //
// return STATUS_UNSUPPORTED probably
//
return Irp->IoStatus.Status; } hidparse->HidpGetCaps = HidP_GetCaps; return STATUS_SUCCESS; }
//
// return STATUS_UNSUPPORTED probably
//
return Irp->IoStatus.Status; }
/*
******************************************************************************** * HidpQueryIdForClientPdo ******************************************************************************** * * * */ NTSTATUS HidpQueryIdForClientPdo ( IN PHIDCLASS_DEVICE_EXTENSION hidClassExtension, IN OUT PIRP Irp ) { PIO_STACK_LOCATION irpSp; NTSTATUS status; PDO_EXTENSION *pdoExt; FDO_EXTENSION *fdoExt;
PAGED_CODE();
ASSERT(hidClassExtension->isClientPdo); pdoExt = &hidClassExtension->pdoExt; fdoExt = &pdoExt->deviceFdoExt->fdoExt;
irpSp = IoGetCurrentIrpStackLocation( Irp );
switch (irpSp->Parameters.QueryId.IdType) {
case BusQueryHardwareIDs:
/*
* Call down to get a multi-string of hardware ids for the PDO. */ IoCopyCurrentIrpStackLocationToNext(Irp); status = HidpCallDriverSynchronous(fdoExt->fdo, Irp); if (NT_SUCCESS(status)){ PWCHAR oldIDs, newIDs; /*
* Replace the bus names in the current hardware IDs list with "HID\". */ oldIDs = (PWCHAR)Irp->IoStatus.Information; Irp->IoStatus.Information = (ULONG_PTR)BAD_POINTER; newIDs = SubstituteBusNames(oldIDs, fdoExt, pdoExt); ExFreePool(oldIDs);
if (newIDs){
/*
* Now append the compatible ids to the end of the HardwareIDs list. */ PWCHAR compatIDs = BuildCompatibleID(hidClassExtension); if (compatIDs){ ULONG basicIDsLen, compatIDsLen; PWCHAR allHwIDs;
/*
* Find the lengths of the id multi-strings (not counting the extra NULL at end). */ for (basicIDsLen = 0; newIDs[basicIDsLen]; basicIDsLen += WStrLen(newIDs+basicIDsLen)+1); for (compatIDsLen = 0; compatIDs[compatIDsLen]; compatIDsLen += WStrLen(compatIDs+compatIDsLen)+1);
allHwIDs = ALLOCATEPOOL(PagedPool, (basicIDsLen+compatIDsLen+1)*sizeof(WCHAR)); if (allHwIDs){ RtlCopyMemory(allHwIDs, newIDs, basicIDsLen*sizeof(WCHAR)); RtlCopyMemory( allHwIDs+basicIDsLen, compatIDs, (compatIDsLen+1)*sizeof(WCHAR));
Irp->IoStatus.Information = (ULONG_PTR)allHwIDs; } else { status = STATUS_INSUFFICIENT_RESOURCES; }
ExFreePool(compatIDs); } else { status = STATUS_INSUFFICIENT_RESOURCES; }
ExFreePool(newIDs); } else { status = STATUS_INSUFFICIENT_RESOURCES; } }
DBGSUCCESS(status, TRUE) break;
case BusQueryDeviceID: /*
* Call down to get a the device id for the device's PDO. */ IoCopyCurrentIrpStackLocationToNext(Irp); status = HidpCallDriverSynchronous(fdoExt->fdo, Irp); if (NT_SUCCESS(status)){ PWCHAR oldId, newId, tmpId;
/*
* Replace the bus name (e.g. "USB\") with "HID\" in the device name. */
/*
* First make this string into a multi-string. */ oldId = (PWCHAR)Irp->IoStatus.Information; tmpId = ALLOCATEPOOL(PagedPool, (WStrLen(oldId)+2)*sizeof(WCHAR)); if (tmpId){ ULONG len = WStrCpy(tmpId, oldId);
/*
* Add the extra NULL to terminate the multi-string. */ tmpId[len+1] = UNICODE_NULL;
/*
* Change the bus name to "HID\" */ newId = SubstituteBusNames(tmpId, fdoExt, pdoExt); if (newId){ Irp->IoStatus.Information = (ULONG_PTR)newId; } else { status = STATUS_DEVICE_DATA_ERROR; Irp->IoStatus.Information = (ULONG_PTR)BAD_POINTER; }
ExFreePool(tmpId); } else { status = STATUS_DEVICE_DATA_ERROR; Irp->IoStatus.Information = (ULONG_PTR)BAD_POINTER; } ExFreePool(oldId); }
DBGSUCCESS(status, TRUE) break;
case BusQueryInstanceID:
/*
* Produce an instance-id for this collection-PDO. * * Note: NTKERN frees the returned pointer, so we must provide a fresh pointer. */ { PWSTR instanceId = MemDup(PagedPool, L"0000", sizeof(L"0000")); if (instanceId){ ULONG i;
/*
* Find this collection-PDO in the device-relations array * and make the id be the PDO's index within that array. */ for (i = 0; i < fdoExt->deviceRelations->Count; i++){ if (fdoExt->deviceRelations->Objects[i] == pdoExt->pdo){ StringCbPrintfW(instanceId, sizeof(L"0000"), L"%04x", i); break; } } ASSERT(i < fdoExt->deviceRelations->Count);
Irp->IoStatus.Information = (ULONG_PTR)instanceId; status = STATUS_SUCCESS; } else { status = STATUS_INSUFFICIENT_RESOURCES; } }
DBGSUCCESS(status, TRUE) break;
case BusQueryCompatibleIDs:
// we now return the compatible id's at the end of HardwareIDs
// so that there is no UI on plug-in for a compatible-id match
// for a class-PDO.
// Irp->IoStatus.Information = (ULONG)BuildCompatibleID(hidClassExtension);
Irp->IoStatus.Information = (ULONG_PTR)ALLOCATEPOOL(PagedPool, sizeof(L"\0")); if (Irp->IoStatus.Information) { *(ULONG *)Irp->IoStatus.Information = 0; // double unicode-NULL.
status = STATUS_SUCCESS; } else { status = STATUS_INSUFFICIENT_RESOURCES; ASSERT(0); } break;
default: /*
* Do not return STATUS_NOT_SUPPORTED; * keep the default status * (this allows filter drivers to work). */ status = Irp->IoStatus.Status; break; }
return status; }
/*
******************************************************************************** * AllClientPDOsInitialized ******************************************************************************** * * */ BOOLEAN AllClientPDOsInitialized(FDO_EXTENSION *fdoExtension, BOOLEAN initialized) { BOOLEAN result = TRUE; ULONG i;
if (ISPTR(fdoExtension->deviceRelations)){ for (i = 0; i < fdoExtension->deviceRelations->Count; i++){ PDEVICE_OBJECT pdo = fdoExtension->deviceRelations->Objects[i]; PHIDCLASS_DEVICE_EXTENSION pdoDevExt = pdo->DeviceExtension; PDO_EXTENSION *pdoExt = &pdoDevExt->pdoExt;
/*
* Trick: compare !-results so that all TRUE values are equal */ if (!initialized == !(pdoExt->state == COLLECTION_STATE_UNINITIALIZED)){ DBGVERBOSE(("AllClientPDOsInitialized is returning FALSE for pdo %x, state = %d", pdo, pdoExt->state)) result = FALSE; break; } } } else { result = !initialized; }
return result; }
/*
******************************************************************************** * AnyClientPDOsInitialized ******************************************************************************** * * */ BOOLEAN AnyClientPDOsInitialized(FDO_EXTENSION *fdoExtension, BOOLEAN initialized) { BOOLEAN result = TRUE; ULONG i;
if (ISPTR(fdoExtension->deviceRelations)){ for (i = 0; i < fdoExtension->deviceRelations->Count; i++){ PDEVICE_OBJECT pdo = fdoExtension->deviceRelations->Objects[i]; PHIDCLASS_DEVICE_EXTENSION pdoDevExt = pdo->DeviceExtension; PDO_EXTENSION *pdoExt = &pdoDevExt->pdoExt;
if (!initialized != !(pdoExt->state == COLLECTION_STATE_UNINITIALIZED)){ result = TRUE; break; } } } else { result = !initialized; }
return result; }
/*
******************************************************************************** * HidpDeleteDeviceObjects ******************************************************************************** * * Delete the device-FDO and collection-PDO's IF POSSIBLE. * (must wait for REMOVE_DEVICE completion AND the IRP_MJ_CLOSE. * Otherwise, return FALSE and we'll try again later. * * */ BOOLEAN HidpDeleteDeviceObjects(FDO_EXTENSION *fdoExt) { ULONG i;
/*
* Do this switch-a-roo to thwart re-entrancy problems. */ PDEVICE_OBJECT objToDelete = fdoExt->fdo; fdoExt->fdo = BAD_POINTER;
if (ISPTR(fdoExt->deviceRelations)){
for (i = 0; i < fdoExt->deviceRelations->Count; i++){ PDO_EXTENSION *pdoExt = &fdoExt->collectionPdoExtensions[i]->pdoExt;
ASSERT(ISPTR(fdoExt->deviceRelations->Objects[i]));
if (ISPTR(pdoExt->name)){ RtlFreeUnicodeString(pdoExt->name); ExFreePool(pdoExt->name); pdoExt->name = BAD_POINTER; }
/*
* Delete the client PDO. * Don't touch the pdoExt after doing this. */ ObDereferenceObject(fdoExt->deviceRelations->Objects[i]); IoDeleteDevice(fdoExt->deviceRelations->Objects[i]); }
ExFreePool(fdoExt->deviceRelations); } fdoExt->deviceRelations = BAD_POINTER;
if (ISPTR(fdoExt->collectionPdoExtensions)){ ExFreePool(fdoExt->collectionPdoExtensions); } fdoExt->collectionPdoExtensions = BAD_POINTER;
ObDereferenceObject(objToDelete); IoDeleteDevice(objToDelete);
return TRUE; }
/*
******************************************************************************** * HidpQueryDeviceCapabilities ******************************************************************************** * * * */ NTSTATUS HidpQueryDeviceCapabilities( IN PDEVICE_OBJECT PdoDeviceObject, IN PDEVICE_CAPABILITIES DeviceCapabilities) { PIRP irp; NTSTATUS status;
PAGED_CODE();
irp = IoAllocateIrp(PdoDeviceObject->StackSize, FALSE); if (irp) { PIO_STACK_LOCATION nextStack; KEVENT event;
nextStack = IoGetNextIrpStackLocation(irp); ASSERT(nextStack);
nextStack->MajorFunction= IRP_MJ_PNP; nextStack->MinorFunction= IRP_MN_QUERY_CAPABILITIES; irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IoSetCompletionRoutine(irp, HidpQueryCapsCompletion, &event, TRUE, TRUE, TRUE);
RtlZeroMemory(DeviceCapabilities, sizeof(DEVICE_CAPABILITIES));
/*
* Caller needs to initialize some fields */ DeviceCapabilities->Size = sizeof(DEVICE_CAPABILITIES); DeviceCapabilities->Version = 1; DeviceCapabilities->Address = -1; DeviceCapabilities->UINumber = -1;
nextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;
status = IoCallDriver(PdoDeviceObject, irp);
if (status == STATUS_PENDING) { KeWaitForSingleObject( &event, Suspended, KernelMode, FALSE, NULL); }
/*
* Note: we still own the IRP after the IoCallDriver() call * because the completion routine returned * STATUS_MORE_PROCESSING_REQUIRED. */ status = irp->IoStatus.Status;
IoFreeIrp(irp);
} else { status = STATUS_INSUFFICIENT_RESOURCES; }
return status; }
/*
******************************************************************************** * CheckReportPowerEvent ******************************************************************************** * * Check whether the read report includes a power event. * If it does, notify the system by completing the saved power-event Irp. * * Note: report should point to a "cooked" report with the report-id byte * included at the beginning of the report, whether or not the device * included the report id. * */ VOID CheckReportPowerEvent( FDO_EXTENSION *fdoExt, PHIDCLASS_COLLECTION collection, PUCHAR report, ULONG reportLen) { ULONG powerMask; NTSTATUS status;
ASSERT(ISPTR(fdoExt->collectionPdoExtensions));
status = HidP_SysPowerEvent( report, (USHORT)reportLen, collection->phidDescriptor, &powerMask); if (NT_SUCCESS(status)){
if (powerMask){ /*
* This report contains a power event! */
PIRP irpToComplete = NULL; KIRQL oldIrql;
KeAcquireSpinLock(&collection->powerEventSpinLock, &oldIrql);
/*
* We should have gotten a IOCTL_GET_SYS_BUTTON_EVENT earlier and queued * an IRP to return now. */ if (ISPTR(collection->powerEventIrp)){ PDRIVER_CANCEL oldCancelRoutine;
/*
* "Dequeue" the power event IRP. */ irpToComplete = collection->powerEventIrp;
oldCancelRoutine = IoSetCancelRoutine(irpToComplete, NULL); if (oldCancelRoutine){ ASSERT(oldCancelRoutine == PowerEventCancelRoutine); } else { /*
* This IRP was cancelled and the cancel routine WAS called. * The cancel routine will complete this IRP * as soon as we drop the spinlock, so don't touch the IRP. */ ASSERT(irpToComplete->Cancel); irpToComplete = NULL; }
collection->powerEventIrp = BAD_POINTER; } else { TRAP; }
KeReleaseSpinLock(&collection->powerEventSpinLock, oldIrql);
/*
* If completing the IRP, * do so after releasing all spinlocks. */ if (irpToComplete){ /*
* Complete the IRP with the power mask. * */ ASSERT(irpToComplete->AssociatedIrp.SystemBuffer); *(PULONG)irpToComplete->AssociatedIrp.SystemBuffer = powerMask; irpToComplete->IoStatus.Information = sizeof(ULONG); irpToComplete->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(irpToComplete, IO_NO_INCREMENT); } } }
}
LONG WStrNCmpI(PWCHAR s1, PWCHAR s2, ULONG n) { ULONG result;
while (n && *s1 && *s2 && ((*s1|0x20) == (*s2|0x20))){ s1++, s2++; n--; }
if (n){ result = ((*s1|0x20) > (*s2|0x20)) ? 1 : ((*s1|0x20) < (*s2|0x20)) ? -1 : 0; } else { result = 0; }
return result; }
ULONG LAtoX(PWCHAR wHexString) /*++
Routine Description:
Convert a hex string (without the '0x' prefix) to a ULONG.
Arguments:
wHexString - null-terminated wide-char hex string (with no "0x" prefix)
Return Value:
ULONG value
--*/ { ULONG i, result = 0;
for (i = 0; wHexString[i]; i++){ if ((wHexString[i] >= L'0') && (wHexString[i] <= L'9')){ result *= 0x10; result += (wHexString[i] - L'0'); } else if ((wHexString[i] >= L'a') && (wHexString[i] <= L'f')){ result *= 0x10; result += (wHexString[i] - L'a' + 0x0a); } else if ((wHexString[i] >= L'A') && (wHexString[i] <= L'F')){ result *= 0x10; result += (wHexString[i] - L'A' + 0x0a); } else { ASSERT(0); break; } }
return result; }
ULONG WStrNCpy(PWCHAR dest, PWCHAR src, ULONG n) { ULONG result = 0;
while (n && (*dest++ = *src++)){ result++; n--; }
return result; }
NTSTATUS OpenSubkey( OUT PHANDLE Handle, IN HANDLE BaseHandle, IN PUNICODE_STRING KeyName, IN ACCESS_MASK DesiredAccess ) { OBJECT_ATTRIBUTES objectAttributes; NTSTATUS status;
PAGED_CODE();
InitializeObjectAttributes( &objectAttributes, KeyName, OBJ_CASE_INSENSITIVE, BaseHandle, (PSECURITY_DESCRIPTOR) NULL );
status = ZwOpenKey(Handle, DesiredAccess, &objectAttributes);
return status; }
PVOID HidpGetSystemAddressForMdlSafe(PMDL MdlAddress) { PVOID buf = NULL; /*
* Can't call MmGetSystemAddressForMdlSafe in a WDM driver, * so set the MDL_MAPPING_CAN_FAIL bit and check the result * of the mapping. */ if (MdlAddress) { MdlAddress->MdlFlags |= MDL_MAPPING_CAN_FAIL; buf = MmGetSystemAddressForMdl(MdlAddress); MdlAddress->MdlFlags &= (~MDL_MAPPING_CAN_FAIL); } else { DBGASSERT(MdlAddress, ("MdlAddress passed into GetSystemAddress is NULL"), FALSE) } return buf; }
|