/*++ Copyright (c) 1996 Microsoft Corporation Module Name: device.c Abstract Resource management routines for devices and collections Author: ervinp Environment: Kernel mode only Revision History: --*/ #include "pch.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, HidpStartDevice) #pragma alloc_text(PAGE, HidpStartCollectionPDO) #pragma alloc_text(PAGE, AllocDeviceResources) #pragma alloc_text(PAGE, FreeDeviceResources) #pragma alloc_text(PAGE, AllocCollectionResources) #pragma alloc_text(PAGE, FreeCollectionResources) #pragma alloc_text(PAGE, InitializeCollection) #pragma alloc_text(PAGE, HidpCleanUpFdo) #pragma alloc_text(PAGE, HidpRemoveDevice) #pragma alloc_text(PAGE, HidpRemoveCollection) #endif /* ******************************************************************************** * AllocDeviceResources ******************************************************************************** * * */ NTSTATUS AllocDeviceResources(FDO_EXTENSION *fdoExt) { ULONG numCollections; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); /* * This will allocate fdoExt->rawReportDescription */ status = HidpGetDeviceDescriptor(fdoExt); if (NT_SUCCESS(status)){ /* * Ask HIDPARSE to fill in the HIDP_DEVICE_DESC for this device. */ status = HidP_GetCollectionDescription( fdoExt->rawReportDescription, fdoExt->rawReportDescriptionLength, NonPagedPool, &fdoExt->deviceDesc); if (NT_SUCCESS(status)){ fdoExt->devDescInitialized = TRUE; numCollections = fdoExt->deviceDesc.CollectionDescLength; ASSERT(numCollections); fdoExt->classCollectionArray = ALLOCATEPOOL(NonPagedPool, numCollections*sizeof(HIDCLASS_COLLECTION)); if (!fdoExt->classCollectionArray){ fdoExt->classCollectionArray = BAD_POINTER; status = STATUS_INSUFFICIENT_RESOURCES; } else { RtlZeroMemory(fdoExt->classCollectionArray, numCollections*sizeof(HIDCLASS_COLLECTION)); } } } else { fdoExt->rawReportDescription = BAD_POINTER; } DBGSUCCESS(status, FALSE) return status; } /* ******************************************************************************** * FreeDeviceResources ******************************************************************************** * * */ VOID FreeDeviceResources(FDO_EXTENSION *fdoExt) { ULONG i; PAGED_CODE(); for (i = 0; i < fdoExt->deviceDesc.CollectionDescLength; i++) { FreeCollectionResources(fdoExt, fdoExt->classCollectionArray[i].CollectionNumber); } /* * Free the stuff returned by HIDPARSE's HidP_GetCollectionDescription. */ if (fdoExt->devDescInitialized){ HidP_FreeCollectionDescription(&fdoExt->deviceDesc); #if DBG fdoExt->deviceDesc.CollectionDesc = BAD_POINTER; fdoExt->deviceDesc.ReportIDs = BAD_POINTER; #endif } fdoExt->deviceDesc.CollectionDescLength = 0; /* * Free the raw report descriptor allocated during START_DEVICE by HidpGetDeviceDescriptor(). */ if (ISPTR(fdoExt->rawReportDescription)){ ExFreePool(fdoExt->rawReportDescription); } fdoExt->rawReportDescription = BAD_POINTER; if (ISPTR(fdoExt->classCollectionArray)){ ExFreePool(fdoExt->classCollectionArray); } fdoExt->classCollectionArray = BAD_POINTER; } /* ******************************************************************************** * AllocCollectionResources ******************************************************************************** * * */ NTSTATUS AllocCollectionResources(FDO_EXTENSION *fdoExt, ULONG collectionNum) { PHIDCLASS_COLLECTION collection; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); collection = GetHidclassCollection(fdoExt, collectionNum); if (collection){ ULONG descriptorLen; descriptorLen = collection->hidCollectionInfo.DescriptorSize; if (descriptorLen){ collection->phidDescriptor = ALLOCATEPOOL(NonPagedPool, descriptorLen); if (collection->phidDescriptor){ status = HidpGetCollectionDescriptor( fdoExt, collection->CollectionNumber, collection->phidDescriptor, &descriptorLen); } else { collection->phidDescriptor = BAD_POINTER; status = STATUS_INSUFFICIENT_RESOURCES; } if (NT_SUCCESS(status)){ ULONG i = collection->CollectionIndex; ULONG inputLength; ASSERT(fdoExt->devDescInitialized); inputLength = fdoExt->deviceDesc.CollectionDesc[i].InputLength; if (inputLength){ if (collection->hidCollectionInfo.Polled){ collection->cookedInterruptReportBuf = BAD_POINTER; } else { collection->cookedInterruptReportBuf = ALLOCATEPOOL(NonPagedPool, inputLength); if (!collection->cookedInterruptReportBuf){ status = STATUS_INSUFFICIENT_RESOURCES; } } fdoExt->isOutputOnlyDevice = FALSE; } else { /* * This is an output-only device (e.g. USB monitor) */ DBGINFO(("Zero input length -> output-only device.")) collection->cookedInterruptReportBuf = BAD_POINTER; } } } else { ASSERT(descriptorLen > 0); status = STATUS_DEVICE_CONFIGURATION_ERROR; } } else { status = STATUS_DEVICE_DATA_ERROR; } DBGSUCCESS(status, TRUE) return status; } /* ******************************************************************************** * FreeCollectionResources ******************************************************************************** * * */ VOID FreeCollectionResources(FDO_EXTENSION *fdoExt, ULONG collectionNum) { PHIDCLASS_COLLECTION collection; PAGED_CODE(); collection = GetHidclassCollection(fdoExt, collectionNum); if (collection){ if (collection->hidCollectionInfo.Polled){ if (ISPTR(collection->savedPolledReportBuf)){ ExFreePool(collection->savedPolledReportBuf); } collection->savedPolledReportBuf = BAD_POINTER; } else { if (ISPTR(collection->cookedInterruptReportBuf)){ ExFreePool(collection->cookedInterruptReportBuf); } else { // this is an output-only collection } } collection->cookedInterruptReportBuf = BAD_POINTER; if (ISPTR(collection->phidDescriptor)){ ExFreePool(collection->phidDescriptor); } collection->phidDescriptor = BAD_POINTER; } else { TRAP; } } /* ******************************************************************************** * InitializeCollection ******************************************************************************** * * */ NTSTATUS InitializeCollection(FDO_EXTENSION *fdoExt, ULONG collectionIndex) { PHIDCLASS_COLLECTION collection; ULONG descriptorBufLen; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); ASSERT(ISPTR(fdoExt->classCollectionArray)); collection = &fdoExt->classCollectionArray[collectionIndex]; RtlZeroMemory(collection, sizeof(HIDCLASS_COLLECTION)); ASSERT(fdoExt->devDescInitialized); collection->CollectionNumber = fdoExt->deviceDesc.CollectionDesc[collectionIndex].CollectionNumber; collection->CollectionIndex = collectionIndex; InitializeListHead(&collection->FileExtensionList); KeInitializeSpinLock(&collection->FileExtensionListSpinLock); KeInitializeSpinLock(&collection->powerEventSpinLock); KeInitializeSpinLock(&collection->secureReadLock); collection->secureReadMode = 0; descriptorBufLen = sizeof(HID_COLLECTION_INFORMATION); status = HidpGetCollectionInformation( fdoExt, collection->CollectionNumber, &collection->hidCollectionInfo, &descriptorBufLen); DBGSUCCESS(status, TRUE) return status; } void HidpGetRemoteWakeEnableState( PDO_EXTENSION *pdoExt ) { HANDLE hKey; NTSTATUS status; ULONG tmp; BOOLEAN wwEnableFound; hKey = NULL; wwEnableFound = FALSE; status = IoOpenDeviceRegistryKey (pdoExt->pdo, PLUGPLAY_REGKEY_DEVICE, STANDARD_RIGHTS_ALL, &hKey); if (NT_SUCCESS (status)) { UNICODE_STRING valueName; ULONG length; ULONG value = 0; PKEY_VALUE_FULL_INFORMATION fullInfo; PAGED_CODE(); RtlInitUnicodeString (&valueName, HIDCLASS_REMOTE_WAKE_ENABLE); length = sizeof (KEY_VALUE_FULL_INFORMATION) + valueName.MaximumLength + sizeof(value); fullInfo = ExAllocatePool (PagedPool, length); if (fullInfo) { status = ZwQueryValueKey (hKey, &valueName, KeyValueFullInformation, fullInfo, length, &length); if (NT_SUCCESS (status)) { DBGASSERT (sizeof(value) == fullInfo->DataLength, ("Value data wrong length for REmote wake reg value."), TRUE); RtlCopyMemory (&value, ((PUCHAR) fullInfo) + fullInfo->DataOffset, fullInfo->DataLength); pdoExt->remoteWakeEnabled = (value ? TRUE : FALSE); } ExFreePool (fullInfo); } ZwClose (hKey); hKey = NULL; } } WMIGUIDREGINFO HidClassWmiGuidList = { &GUID_POWER_DEVICE_WAKE_ENABLE, 1, 0 // wait wake }; WMIGUIDREGINFO HidClassFdoWmiGuidList = { &GUID_POWER_DEVICE_ENABLE, 1, 0 }; /* ******************************************************************************** * HidpStartCollectionPDO ******************************************************************************** * * */ NTSTATUS HidpStartCollectionPDO(FDO_EXTENSION *fdoExt, PDO_EXTENSION *pdoExt, PIRP Irp) { NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); /* * Initialize the collection only if it's not already initialized. * This is so we don't destroy the FileExtensionList after a STOP/START. */ if (pdoExt->state == COLLECTION_STATE_UNINITIALIZED){ pdoExt->state = COLLECTION_STATE_INITIALIZED; } if (NT_SUCCESS(status)){ PHIDCLASS_COLLECTION collection = GetHidclassCollection(fdoExt, pdoExt->collectionNum); if (collection){ /* * If all collection PDOs for this device FDO are initialized, * figure out the maximum report size and finish starting the device. */ if (AnyClientPDOsInitialized(fdoExt, TRUE)){ DBGSTATE(fdoExt->state, DEVICE_STATE_START_SUCCESS, FALSE) /* * If this is a polled collection, * start the background polling loop FOR EACH COLLECTION. * Otherwise, if it's an ordinary interrupt collection, * start the ping-pong IRPs for it. */ if (collection->hidCollectionInfo.Polled){ if (HidpSetMaxReportSize(fdoExt)){ ULONG i; for (i = 0; i < fdoExt->deviceDesc.CollectionDescLength; i++){ PHIDCLASS_COLLECTION ctn; ctn = &fdoExt->classCollectionArray[i]; /* * If one of the collections is polled, they * should ALL be polled. */ ASSERT(ctn->hidCollectionInfo.Polled); ctn->PollInterval_msec = DEFAULT_POLL_INTERVAL_MSEC; /* * Allocate the buffer for saving the polled device's * last report. Allocate one more byte than the max * report size for the device in case we have to * prepend a report id byte. */ ctn->savedPolledReportBuf = ALLOCATEPOOL(NonPagedPool, fdoExt->maxReportSize+1); if (ctn->savedPolledReportBuf){ ctn->polledDataIsStale = TRUE; StartPollingLoop(fdoExt, ctn, TRUE); status = STATUS_SUCCESS; } else { ASSERT(ctn->savedPolledReportBuf); status = STATUS_INSUFFICIENT_RESOURCES; } } } } else if (fdoExt->isOutputOnlyDevice){ /* * Don't start ping-pong IRPs. */ } else { status = HidpStartAllPingPongs(fdoExt); } } if (NT_SUCCESS(status)) { pdoExt->state = COLLECTION_STATE_RUNNING; #if DBG collection->Signature = HIDCLASS_COLLECTION_SIG; #endif /* * Create the 'file-name' used by clients to open this device. */ if (!pdoExt->MouseOrKeyboard) { HidpCreateSymbolicLink(pdoExt, pdoExt->collectionNum, TRUE, pdoExt->pdo); } if (!pdoExt->MouseOrKeyboard && WAITWAKE_SUPPORTED(fdoExt)) { // // register for the wait wake guid as well // pdoExt->WmiLibInfo.GuidCount = sizeof (HidClassWmiGuidList) / sizeof (WMIGUIDREGINFO); ASSERT (1 == pdoExt->WmiLibInfo.GuidCount); // // See if the user has enabled remote wake for the device // PRIOR to registering with WMI. // HidpGetRemoteWakeEnableState(pdoExt); pdoExt->WmiLibInfo.GuidList = &HidClassWmiGuidList; pdoExt->WmiLibInfo.QueryWmiRegInfo = HidpQueryWmiRegInfo; pdoExt->WmiLibInfo.QueryWmiDataBlock = HidpQueryWmiDataBlock; pdoExt->WmiLibInfo.SetWmiDataBlock = HidpSetWmiDataBlock; pdoExt->WmiLibInfo.SetWmiDataItem = HidpSetWmiDataItem; pdoExt->WmiLibInfo.ExecuteWmiMethod = NULL; pdoExt->WmiLibInfo.WmiFunctionControl = NULL; IoWMIRegistrationControl(pdoExt->pdo, WMIREG_ACTION_REGISTER); if (SHOULD_SEND_WAITWAKE(pdoExt)) { HidpCreateRemoteWakeIrp(pdoExt); } } if (AllClientPDOsInitialized(fdoExt, TRUE)){ HidpStartIdleTimeout(fdoExt, TRUE); } } } else { status = STATUS_DEVICE_DATA_ERROR; } } DBGSUCCESS(status, FALSE) return status; } /* ******************************************************************************** * HidpStartDevice ******************************************************************************** * * */ NTSTATUS HidpStartDevice(PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, PIRP Irp) { FDO_EXTENSION *fdoExt; enum deviceState previousState; NTSTATUS status; ULONG i; PAGED_CODE(); ASSERT(!HidDeviceExtension->isClientPdo); fdoExt = &HidDeviceExtension->fdoExt; previousState = fdoExt->state; fdoExt->state = DEVICE_STATE_STARTING; /* * Get the power-state conversion table */ status = HidpQueryDeviceCapabilities( HidDeviceExtension->hidExt.PhysicalDeviceObject, &fdoExt->deviceCapabilities); if (NT_SUCCESS(status)){ /* * Alert the rest of the driver stack that the device is starting. */ IoCopyCurrentIrpStackLocationToNext(Irp); status = HidpCallDriverSynchronous(fdoExt->fdo, Irp); if (NT_SUCCESS(status)){ /* * If we're just resuming from STOP, * there's nothing else to do; * otherwise, need to call down the USB stack * for some info and allocate some resources. */ if (previousState == DEVICE_STATE_INITIALIZED){ status = AllocDeviceResources(fdoExt); if (NT_SUCCESS(status)){ /* * Assume this is an output-only device until we start * a collection-pdo which handles inputs. * Only set fdoExt->isOutputOnlyDevice on the first start * not on a subsequent start following a stop. */ fdoExt->isOutputOnlyDevice = TRUE; /* * Initialize WMI stuff */ fdoExt->WmiLibInfo.GuidCount = sizeof(HidClassFdoWmiGuidList) / sizeof (WMIGUIDREGINFO); fdoExt->WmiLibInfo.GuidList = &HidClassFdoWmiGuidList; fdoExt->WmiLibInfo.QueryWmiRegInfo = HidpQueryWmiRegInfo; fdoExt->WmiLibInfo.QueryWmiDataBlock = HidpQueryWmiDataBlock; fdoExt->WmiLibInfo.SetWmiDataBlock = HidpSetWmiDataBlock; fdoExt->WmiLibInfo.SetWmiDataItem = HidpSetWmiDataItem; fdoExt->WmiLibInfo.ExecuteWmiMethod = NULL; fdoExt->WmiLibInfo.WmiFunctionControl = NULL; /* * Allocate all the collection resources before allocating * the pingpong irps, so that we can set a maximum report * size. */ for (i = 0; i < fdoExt->deviceDesc.CollectionDescLength; i++) { // If one of these fails, we will clean up properly // in the remove routine, so there's no need to // bother cleaning up here. status = InitializeCollection(fdoExt, i); if (!NT_SUCCESS(status)){ break; } status = AllocCollectionResources(fdoExt, fdoExt->deviceDesc.CollectionDesc[i].CollectionNumber); if (!NT_SUCCESS(status)){ break; } } /* * We need ot allocate the pingpongs in the fdo start * routine due to race conditions introduced by selective * suspend. */ if (!fdoExt->isOutputOnlyDevice && !fdoExt->driverExt->DevicesArePolled) { status = HidpReallocPingPongIrps(fdoExt, MIN_PINGPONG_IRPS); } if (NT_SUCCESS(status)){ /* * We will have to create an array of PDOs, one for each device class. * The following call will cause NTKERN to call us back with * IRP_MN_QUERY_DEVICE_RELATIONS and initialize its collection-PDOs. */ IoInvalidateDeviceRelations(HidDeviceExtension->hidExt.PhysicalDeviceObject, BusRelations); } } } else if (previousState == DEVICE_STATE_STOPPED){ // // Any request that comes in when we are in low power will be // dealt with at that time // DBGSTATE(fdoExt->prevState, DEVICE_STATE_START_SUCCESS, TRUE) } else { TRAP; status = STATUS_DEVICE_CONFIGURATION_ERROR; } } } if (NT_SUCCESS(status)){ fdoExt->state = DEVICE_STATE_START_SUCCESS; #if DBG { ULONG i; // Win98 doesn't have good debug extensions DBGVERBOSE(("Started fdoExt %ph with %d collections: ", fdoExt, fdoExt->deviceDesc.CollectionDescLength)) for (i = 0; i < fdoExt->deviceDesc.CollectionDescLength; i++){ DBGVERBOSE((" - collection #%d: (in=%xh,out=%xh,feature=%xh) usagePage %xh, usage %xh ", fdoExt->deviceDesc.CollectionDesc[i].CollectionNumber, fdoExt->deviceDesc.CollectionDesc[i].InputLength, fdoExt->deviceDesc.CollectionDesc[i].OutputLength, fdoExt->deviceDesc.CollectionDesc[i].FeatureLength, fdoExt->deviceDesc.CollectionDesc[i].UsagePage, fdoExt->deviceDesc.CollectionDesc[i].Usage)) } } #endif } else { fdoExt->state = DEVICE_STATE_START_FAILURE; } DBGSUCCESS(status, FALSE) return status; } VOID HidpCleanUpFdo(FDO_EXTENSION *fdoExt) { PAGED_CODE(); if (fdoExt->openCount == 0){ /* * This is the last CLOSE on an alreay-removed device. * * Free resources and the FDO name * (wPdoName that was allocated in HidpAddDevice); * */ DequeueFdoExt(fdoExt); FreeDeviceResources(fdoExt); RtlFreeUnicodeString(&fdoExt->name); IoWMIRegistrationControl(fdoExt->fdo, WMIREG_ACTION_DEREGISTER); /* * Delete the device-FDO and all collection-PDOs * Don't touch fdoExt after this. */ HidpDeleteDeviceObjects(fdoExt); } } /* ******************************************************************************** * HidpRemoveDevice ******************************************************************************** * */ NTSTATUS HidpRemoveDevice(FDO_EXTENSION *fdoExt, IN PIRP Irp) { BOOLEAN proceedWithRemove; NTSTATUS status; PIRP IdleIrp; PAGED_CODE(); /* * All collection-PDOs should have been removed by now, * but we want to verify this. * Only allow removal of this device-FDO if all the * collection-PDOs are removed * (or if they never got created in the first place). */ if (fdoExt->prevState == DEVICE_STATE_START_FAILURE){ proceedWithRemove = TRUE; } else if (fdoExt->prevState == DEVICE_STATE_STOPPED){ /* * If a device fails to initialize, it may get * STOP_DEVICE before being removed, so we want to * go ahead and remove it without calling * AllClientPDOsInitialized, which accesses some * data which may not have been initialized. * In this case we're never checking for the * case that the device was initialized successfully, * then stopped, and then removed without its * collection-PDOs being removed; but this is an * illegal case, so we'll just punt on it. */ proceedWithRemove = TRUE; } else if (AllClientPDOsInitialized(fdoExt, FALSE)){ proceedWithRemove = TRUE; } else { /* * This shouldn't happen -- all the collection-PDOs * should have been removed before the device-FDO. */ DBGERR(("State of fdo %x state is %d",fdoExt->fdo,fdoExt->state)) TRAP; proceedWithRemove = FALSE; } if (proceedWithRemove){ PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension = CONTAINING_RECORD(fdoExt, HIDCLASS_DEVICE_EXTENSION, fdoExt); DBGASSERT((fdoExt->state == DEVICE_STATE_REMOVING || fdoExt->state == DEVICE_STATE_INITIALIZED || fdoExt->state == DEVICE_STATE_START_FAILURE), ("Device is in incorrect state: %x", fdoExt->state), TRUE) 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; } while (IdleIrp = DequeuePowerDelayedIrp(fdoExt)) { IdleIrp->IoStatus.Status = STATUS_NO_SUCH_DEVICE; IoCompleteRequest(IdleIrp, IO_NO_INCREMENT); } DestroyPingPongs(fdoExt); /* * Note: THE ORDER OF THESE ACTIONS IS VERY CRITICAL */ Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation (Irp); status = HidpCallDriver(fdoExt->fdo, Irp); fdoExt->state = DEVICE_STATE_REMOVED; DerefDriverExt(fdoExt->driverExt->MinidriverObject); fdoExt->driverExt = BAD_POINTER; /* * After Detach we can no longer send IRPS to this device * object as it will be GONE! */ IoDetachDevice(HidDeviceExtension->hidExt.NextDeviceObject); /* * If all client handles on this device have been closed, * destroy the objects and our context for it; * otherwise, we'll do this when the last client closes * their handle. * * On NT we can only get here if all our creates have been closed, so * this is unnecessary, but on Win9x, a remove can be sent with valid * opens against the stack. * * Don't touch fdoExt after this. */ HidpCleanUpFdo(fdoExt); } else { status = STATUS_DEVICE_CONFIGURATION_ERROR; } DBGSUCCESS(status, FALSE) return status; } /* ******************************************************************************** * HidpRemoveCollection ******************************************************************************** * */ VOID HidpRemoveCollection(FDO_EXTENSION *fdoExt, PDO_EXTENSION *pdoExt, IN PIRP Irp) { PAGED_CODE(); // // This pdo is no longer available as it has been removed. // It should still be returned for each Query Device Relations // IRPS to the HID bus, but it itself should respond to all // IRPS with STATUS_DELETE_PENDING. // if (pdoExt->prevState == COLLECTION_STATE_UNINITIALIZED || // for started pdos pdoExt->state == COLLECTION_STATE_UNINITIALIZED){ // For unstarted pdos pdoExt->state = COLLECTION_STATE_UNINITIALIZED; DBGVERBOSE(("HidpRemoveCollection: collection uninitialized.")) } else { ULONG ctnIndx = pdoExt->collectionIndex; PHIDCLASS_COLLECTION collection = &fdoExt->classCollectionArray[ctnIndx]; ULONG numReportIDs = fdoExt->deviceDesc.ReportIDsLength; PIRP remoteWakeIrp; if (!pdoExt->MouseOrKeyboard && WAITWAKE_SUPPORTED(fdoExt)) { // // Unregister for remote wakeup. // IoWMIRegistrationControl (pdoExt->pdo, WMIREG_ACTION_DEREGISTER); } remoteWakeIrp = (PIRP) InterlockedExchangePointer(&pdoExt->remoteWakeIrp, NULL); if (remoteWakeIrp) { IoCancelIrp(remoteWakeIrp); } pdoExt->state = COLLECTION_STATE_UNINITIALIZED; /* * Destroy this collection. * This will also abort all pending reads on this collection-PDO. */ HidpDestroyCollection(fdoExt, collection); } DBGVERBOSE(("HidpRemoveCollection: removed pdo %ph (refCount=%xh)", pdoExt->pdo, (ULONG)(*(((PUCHAR)pdoExt->pdo)-0x18)))) }