mirror of
https://github.com/reactos/reactos.git
synced 2025-03-10 10:14:44 +00:00

Not all files are included, but these are necessary to compile cdrom driver. So far it can only be statically linked with drivers, a proper implementation requires wdfldr helper driver
1926 lines
46 KiB
C++
1926 lines
46 KiB
C++
/*++
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
FxPkgPdo.cpp
|
|
|
|
Abstract:
|
|
|
|
This module implements the Pnp package for Pdo devices.
|
|
|
|
Author:
|
|
|
|
|
|
|
|
Environment:
|
|
|
|
Both kernel and user mode
|
|
|
|
Revision History:
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
#include "pnppriv.hpp"
|
|
#include <wdmguid.h>
|
|
|
|
// Tracing support
|
|
#if defined(EVENT_TRACING)
|
|
extern "C" {
|
|
#include "FxPkgPdo.tmh"
|
|
}
|
|
#endif
|
|
|
|
const PFN_PNP_POWER_CALLBACK FxPkgPdo::m_PdoPnpFunctionTable[IRP_MN_SURPRISE_REMOVAL + 1] =
|
|
{
|
|
_PnpStartDevice, // IRP_MN_START_DEVICE
|
|
_PnpQueryRemoveDevice, // IRP_MN_QUERY_REMOVE_DEVICE
|
|
_PnpRemoveDevice, // IRP_MN_REMOVE_DEVICE
|
|
_PnpCancelRemoveDevice, // IRP_MN_CANCEL_REMOVE_DEVICE
|
|
_PnpStopDevice, // IRP_MN_STOP_DEVICE
|
|
_PnpQueryStopDevice, // IRP_MN_QUERY_STOP_DEVICE
|
|
_PnpCancelStopDevice, // IRP_MN_CANCEL_STOP_DEVICE
|
|
|
|
_PnpQueryDeviceRelations, // IRP_MN_QUERY_DEVICE_RELATIONS
|
|
_PnpQueryInterface, // IRP_MN_QUERY_INTERFACE
|
|
_PnpQueryCapabilities, // IRP_MN_QUERY_CAPABILITIES
|
|
_PnpQueryResources, // IRP_MN_QUERY_RESOURCES
|
|
_PnpQueryResourceRequirements, // IRP_MN_QUERY_RESOURCE_REQUIREMENTS
|
|
_PnpQueryDeviceText, // IRP_MN_QUERY_DEVICE_TEXT
|
|
_PnpFilterResourceRequirements, // IRP_MN_FILTER_RESOURCE_REQUIREMENTS
|
|
|
|
_PnpCompleteIrp, // 0x0E
|
|
|
|
_PnpCompleteIrp, // IRP_MN_READ_CONFIG
|
|
_PnpCompleteIrp, // IRP_MN_WRITE_CONFIG
|
|
_PnpEject, // IRP_MN_EJECT
|
|
_PnpSetLock, // IRP_MN_SET_LOCK
|
|
_PnpQueryId, // IRP_MN_QUERY_ID
|
|
_PnpQueryPnpDeviceState, // IRP_MN_QUERY_PNP_DEVICE_STATE
|
|
_PnpQueryBusInformation, // IRP_MN_QUERY_BUS_INFORMATION
|
|
_PnpDeviceUsageNotification, // IRP_MN_DEVICE_USAGE_NOTIFICATION
|
|
_PnpSurpriseRemoval, // IRP_MN_SURPRISE_REMOVAL
|
|
};
|
|
|
|
const PFN_PNP_POWER_CALLBACK FxPkgPdo::m_PdoPowerFunctionTable[IRP_MN_QUERY_POWER + 1] =
|
|
{
|
|
_DispatchWaitWake, // IRP_MN_WAIT_WAKE
|
|
_DispatchPowerSequence, // IRP_MN_POWER_SEQUENCE
|
|
_DispatchSetPower, // IRP_MN_SET_POWER
|
|
_DispatchQueryPower, // IRP_MN_QUERY_POWER
|
|
};
|
|
|
|
//#if defined(ALLOC_PRAGMA)
|
|
//#pragma code_seg("PAGE")
|
|
//#endif
|
|
|
|
FxPkgPdo::FxPkgPdo(
|
|
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
|
|
__in CfxDevice *Device
|
|
) :
|
|
FxPkgPnp(FxDriverGlobals, Device, FX_TYPE_PACKAGE_PDO)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the constructor for the FxPkgPdo. Don't do any initialization
|
|
that might fail here.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Returns:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
m_DeviceTextHead.Next = NULL;
|
|
|
|
m_DeviceID = NULL;
|
|
m_InstanceID = NULL;
|
|
m_HardwareIDs = NULL;
|
|
m_CompatibleIDs = NULL;
|
|
m_ContainerID = NULL;
|
|
m_IDsAllocation = NULL;
|
|
|
|
m_RawOK = FALSE;
|
|
m_Static = FALSE;
|
|
m_AddedToStaticList = FALSE;
|
|
|
|
//
|
|
// By default the PDO is the owner of wait wake irps (only case where this
|
|
// wouldn't be the case is for a bus filter to be sitting above us).
|
|
//
|
|
m_SharedPower.m_WaitWakeOwner = TRUE;
|
|
|
|
m_Description = NULL;
|
|
m_OwningChildList = NULL;
|
|
|
|
m_EjectionDeviceList = NULL;
|
|
m_DeviceEjectProcessed = NULL;
|
|
|
|
m_CanBeDeleted = FALSE;
|
|
m_EnableWakeAtBusInvoked = FALSE;
|
|
}
|
|
|
|
FxPkgPdo::~FxPkgPdo(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the destructor for the FxPkgPdo
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Returns:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If IoCreateDevice fails on a dynamically created PDO, m_Description will
|
|
// be != NULL b/c we will not go through the pnp state machine in its entirety
|
|
// for cleanup. FxChildList will need a valid m_Description to cleanup upon
|
|
// failure from EvtChildListDeviceCrate, so we just leave m_Description alone
|
|
// here if != NULL.
|
|
//
|
|
// ASSERT(m_Description == NULL);
|
|
|
|
FxDeviceText::_CleanupList(&m_DeviceTextHead);
|
|
|
|
//
|
|
// This will free the underlying memory for m_DeviceID, m_InstanceID,
|
|
// m_HardwareIDs, m_CompatibleIDs and m_ContainerID
|
|
//
|
|
if (m_IDsAllocation != NULL) {
|
|
FxPoolFree(m_IDsAllocation);
|
|
m_IDsAllocation = NULL;
|
|
}
|
|
|
|
if (m_OwningChildList != NULL) {
|
|
m_OwningChildList->RELEASE(this);
|
|
}
|
|
|
|
if (m_EjectionDeviceList != NULL) {
|
|
delete m_EjectionDeviceList;
|
|
m_EjectionDeviceList = NULL;
|
|
}
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::Initialize(
|
|
__in PWDFDEVICE_INIT DeviceInit
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Do initialization that might fail here.
|
|
|
|
Arguemnts:
|
|
|
|
DeviceInit - the structure the driver passed in with initialization data
|
|
|
|
Returns:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
size_t cbLength, cbStrLength;
|
|
PWSTR pCur;
|
|
PdoInit* pPdo;
|
|
|
|
status = FxPkgPnp::Initialize(DeviceInit);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
cbLength = 0;
|
|
|
|
//
|
|
// Compute the total number of bytes required for all strings and then
|
|
// make one allocation and remember pointers w/in the string so we can
|
|
// retrieve them individually later.
|
|
//
|
|
pPdo = &DeviceInit->Pdo;
|
|
cbLength += FxCalculateTotalStringSize(&pPdo->HardwareIDs);
|
|
cbLength += FxCalculateTotalStringSize(&pPdo->CompatibleIDs);
|
|
|
|
if (pPdo->DeviceID != NULL) {
|
|
cbLength += pPdo->DeviceID->ByteLength(TRUE);
|
|
}
|
|
if (pPdo->InstanceID != NULL) {
|
|
cbLength += pPdo->InstanceID->ByteLength(TRUE);
|
|
}
|
|
if (pPdo->ContainerID != NULL) {
|
|
cbLength += pPdo->ContainerID->ByteLength(TRUE);
|
|
}
|
|
|
|
m_IDsAllocation = (PWSTR) FxPoolAllocate(GetDriverGlobals(),
|
|
PagedPool,
|
|
cbLength);
|
|
|
|
if (m_IDsAllocation == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"DeviceInit %p could not allocate string for device IDs "
|
|
"(length %I64d), %!STATUS!", DeviceInit, cbLength, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
pCur = m_IDsAllocation;
|
|
|
|
m_HardwareIDs = pCur;
|
|
pCur = FxCopyMultiSz(m_HardwareIDs, &pPdo->HardwareIDs);
|
|
|
|
m_CompatibleIDs = pCur;
|
|
pCur = FxCopyMultiSz(m_CompatibleIDs, &pPdo->CompatibleIDs);
|
|
|
|
if (pPdo->DeviceID != NULL) {
|
|
m_DeviceID = pCur;
|
|
|
|
//
|
|
// Copy the bytes and then null terminate the buffer
|
|
//
|
|
cbStrLength = pPdo->DeviceID->ByteLength(FALSE);
|
|
|
|
RtlCopyMemory(m_DeviceID,
|
|
pPdo->DeviceID->Buffer(),
|
|
cbStrLength);
|
|
|
|
m_DeviceID[cbStrLength / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
pCur = (PWSTR) WDF_PTR_ADD_OFFSET(m_DeviceID,
|
|
cbStrLength + sizeof(UNICODE_NULL));
|
|
}
|
|
|
|
if (pPdo->InstanceID != NULL) {
|
|
m_InstanceID = pCur;
|
|
|
|
//
|
|
// Copy the bytes and then null terminate the buffer
|
|
//
|
|
cbStrLength = pPdo->InstanceID->ByteLength(FALSE);
|
|
|
|
RtlCopyMemory(m_InstanceID,
|
|
pPdo->InstanceID->Buffer(),
|
|
cbStrLength);
|
|
|
|
m_InstanceID[cbStrLength / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
pCur = (PWSTR) WDF_PTR_ADD_OFFSET(m_InstanceID,
|
|
cbStrLength + sizeof(UNICODE_NULL));
|
|
}
|
|
|
|
if (pPdo->ContainerID != NULL) {
|
|
m_ContainerID = pCur;
|
|
|
|
//
|
|
// Copy the bytes and then null terminate the buffer
|
|
//
|
|
cbStrLength = pPdo->ContainerID->ByteLength(FALSE);
|
|
|
|
RtlCopyMemory(m_ContainerID,
|
|
pPdo->ContainerID->Buffer(),
|
|
cbStrLength);
|
|
|
|
m_ContainerID[cbStrLength / sizeof(WCHAR)] = UNICODE_NULL;
|
|
}
|
|
|
|
m_Static = pPdo->Static;
|
|
|
|
if (m_Static) {
|
|
//
|
|
// Statically enumerated children do not support reenumeration requests
|
|
// from the stack on top of them.
|
|
//
|
|
|
|
//
|
|
// The only way we can have static children is if an FDO enumerates them.
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
Mx::MxAssert(m_Device->m_ParentDevice->IsFdo());
|
|
m_OwningChildList = m_Device->m_ParentDevice->GetFdoPkg()->m_StaticDeviceList;
|
|
|
|
m_OwningChildList->ADDREF(this);
|
|
}
|
|
else {
|
|
m_Description = pPdo->DescriptionEntry;
|
|
|
|
m_OwningChildList = m_Description->GetParentList();
|
|
m_OwningChildList->ADDREF(this);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
FxPkgPdo::FinishInitialize(
|
|
__in PWDFDEVICE_INIT DeviceInit
|
|
)
|
|
{
|
|
PdoInit* pdoInit;
|
|
|
|
pdoInit = &DeviceInit->Pdo;
|
|
|
|
m_DefaultLocale = pdoInit->DefaultLocale;
|
|
m_DeviceTextHead.Next = pdoInit->DeviceText.Next;
|
|
pdoInit->DeviceText.Next = NULL;
|
|
|
|
//
|
|
// Important to do this last since this will cause a pnp state machine
|
|
// transition
|
|
//
|
|
FxPkgPnp::FinishInitialize(DeviceInit); // __super call
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::SendIrpSynchronously(
|
|
__in FxIrp* Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Virtual override for synchronously sending a request down the stack and
|
|
catching it on the way back up. For PDOs, we are the bottom, so this is a
|
|
no-op.
|
|
|
|
Arguments:
|
|
Irp - The request
|
|
|
|
Return Value:
|
|
Status in the Irp
|
|
|
|
--*/
|
|
{
|
|
return Irp->GetStatus();
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::FireAndForgetIrp(
|
|
__inout FxIrp *Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Virtual override for sending a request down the stack and forgetting about
|
|
it. Since we are the bottom of the stack, just complete the request.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
status = Irp->GetStatus();
|
|
|
|
if (Irp->GetMajorFunction() == IRP_MJ_POWER) {
|
|
return CompletePowerRequest(Irp, status);
|
|
}
|
|
else {
|
|
return CompletePnpRequest(Irp, status);
|
|
}
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::_PnpCompleteIrp(
|
|
__in FxPkgPnp* This,
|
|
__inout FxIrp *Irp
|
|
)
|
|
{
|
|
return ((FxPkgPdo*) This)->CompletePnpRequest(Irp, Irp->GetStatus());
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::_PnpQueryDeviceRelations(
|
|
__in FxPkgPnp* This,
|
|
__inout FxIrp *Irp
|
|
)
|
|
{
|
|
return ((FxPkgPdo*) This)->PnpQueryDeviceRelations(Irp);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::PnpQueryDeviceRelations(
|
|
__inout FxIrp *Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method is called in response to a PnP QDR. PDOs handle Ejection
|
|
Relations and Target Relations.
|
|
|
|
Arguments:
|
|
|
|
Irp - a pointer to the FxIrp
|
|
|
|
Returns:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_RELATIONS pDeviceRelations;
|
|
NTSTATUS status;
|
|
DEVICE_RELATION_TYPE type;
|
|
PFX_DRIVER_GLOBALS pFxDriverGlobals;
|
|
|
|
status = Irp->GetStatus();
|
|
pFxDriverGlobals = GetDriverGlobals();
|
|
|
|
type = Irp->GetParameterQDRType();
|
|
switch (type) {
|
|
case BusRelations:
|
|
status = HandleQueryBusRelations(Irp);
|
|
break;
|
|
|
|
case EjectionRelations:
|
|
case RemovalRelations:
|
|
status = HandleQueryDeviceRelations(
|
|
Irp,
|
|
(type == RemovalRelations) ? m_RemovalDeviceList
|
|
: m_EjectionDeviceList);
|
|
|
|
//
|
|
// STATUS_NOT_SUPPORTED is a special value. It means that
|
|
// HandleQueryDeviceRelations did not modify the irp at all and it
|
|
// should be sent off as is.
|
|
//
|
|
if (status == STATUS_NOT_SUPPORTED) {
|
|
//
|
|
// Complete the request with the status it was received with
|
|
//
|
|
status = Irp->GetStatus();
|
|
}
|
|
break;
|
|
|
|
case TargetDeviceRelation:
|
|
pDeviceRelations = (PDEVICE_RELATIONS) MxMemory::MxAllocatePoolWithTag(
|
|
PagedPool, sizeof(DEVICE_RELATIONS), pFxDriverGlobals->Tag);
|
|
|
|
if (pDeviceRelations != NULL) {
|
|
PDEVICE_OBJECT pDeviceObject;
|
|
|
|
pDeviceObject = reinterpret_cast<PDEVICE_OBJECT> (m_Device->GetDeviceObject());
|
|
|
|
Mx::MxReferenceObject(pDeviceObject);
|
|
|
|
pDeviceRelations->Count = 1;
|
|
pDeviceRelations->Objects[0] = pDeviceObject;
|
|
|
|
Irp->SetInformation((ULONG_PTR) pDeviceRelations);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
Irp->SetInformation(NULL);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"WDFDEVICE %p failing TargetDeviceRelations, %!STATUS!",
|
|
m_Device->GetHandle(), status);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return CompletePnpRequest(Irp, status);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::_PnpQueryInterface(
|
|
IN FxPkgPnp* This,
|
|
IN FxIrp *Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Query interface handler for the PDO
|
|
|
|
Arguments:
|
|
This - the package
|
|
|
|
Irp - the QI request
|
|
|
|
Return Value:
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN completeIrp;
|
|
|
|
status = ((FxPkgPdo*) This)->HandleQueryInterface(Irp, &completeIrp);
|
|
|
|
return ((FxPkgPdo*) This)->CompletePnpRequest(Irp, status);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::_PnpQueryCapabilities(
|
|
__inout FxPkgPnp* This,
|
|
__inout FxIrp *Irp
|
|
)
|
|
{
|
|
return ((FxPkgPdo*) This)->PnpQueryCapabilities(Irp);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::PnpQueryCapabilities(
|
|
__inout FxIrp *Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method is invoked in response to a Pnp QueryCapabilities IRP.
|
|
|
|
Arguments:
|
|
|
|
Irp - a pointer to the FxIrp
|
|
|
|
Returns:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_CAPABILITIES pDeviceCapabilities;
|
|
STACK_DEVICE_CAPABILITIES parentStackCapabilities = {0};
|
|
NTSTATUS status;
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
pDeviceCapabilities = Irp->GetParameterDeviceCapabilities();
|
|
|
|
//
|
|
// Confirm this is a valid DeviceCapabilities structure.
|
|
//
|
|
ASSERT(pDeviceCapabilities->Size >= sizeof(DEVICE_CAPABILITIES));
|
|
ASSERT(pDeviceCapabilities->Version == 1);
|
|
|
|
if ((pDeviceCapabilities->Version == 1) &&
|
|
(pDeviceCapabilities->Size >= sizeof(DEVICE_CAPABILITIES))) {
|
|
|
|
//
|
|
// Since query caps must be sent to the parent stack until it reaches
|
|
// the root, we can quickly run out of stack space. If that happens,
|
|
// then move to a work item to get a fresh stack with plenty of stack
|
|
// space.
|
|
//
|
|
if (Mx::MxHasEnoughRemainingThreadStack() == FALSE) {
|
|
MxWorkItem workItem;
|
|
|
|
status = workItem.Allocate(m_Device->GetDeviceObject());
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Store off the work item so we can free it in the worker routine
|
|
//
|
|
Irp->SetContext(0, (PVOID)workItem.GetWorkItem());
|
|
|
|
//
|
|
// Mark the irp as pending because it will be completed in
|
|
// another thread
|
|
//
|
|
Irp->MarkIrpPending();
|
|
|
|
//
|
|
// Kick off to another thread
|
|
//
|
|
workItem.Enqueue(_QueryCapsWorkItem, Irp->GetIrp());
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
else {
|
|
//
|
|
// Not enough for a work item, return error
|
|
//
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
else {
|
|
MxDeviceObject parentDeviceObject;
|
|
|
|
parentDeviceObject.SetObject(
|
|
m_Device->m_ParentDevice->GetDeviceObject());
|
|
status = GetStackCapabilities(
|
|
GetDriverGlobals(),
|
|
&parentDeviceObject,
|
|
NULL, // D3ColdInterface
|
|
&parentStackCapabilities);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
#pragma prefast(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY, "prefast is confused")
|
|
HandleQueryCapabilities(pDeviceCapabilities,
|
|
&parentStackCapabilities.DeviceCaps);
|
|
|
|
//
|
|
// The check above does not guarantee STATUS_SUCCESS explicitly
|
|
// (ie the verifier can change the value to something other then
|
|
// STATUS_SUCCESS) so set it here
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return CompletePnpRequest(Irp, status);
|
|
}
|
|
|
|
VOID
|
|
FxPkgPdo::HandleQueryCapabilities(
|
|
__inout PDEVICE_CAPABILITIES ReportedCaps,
|
|
__in_bcount(ParentCaps->size) PDEVICE_CAPABILITIES ParentCaps
|
|
)
|
|
{
|
|
LONG pnpCaps;
|
|
ULONG i;
|
|
|
|
//
|
|
// PowerSystemUnspecified is reserved for system use as per the DDK
|
|
//
|
|
for (i = PowerSystemWorking; i < PowerSystemMaximum; i++) {
|
|
DEVICE_POWER_STATE state;
|
|
|
|
state = _GetPowerCapState(i, m_PowerCaps.States);
|
|
|
|
if (state == PowerDeviceMaximum) {
|
|
//
|
|
// PDO did not specify any value, use parent's cap
|
|
//
|
|
#pragma prefast(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY, "Esp:675")
|
|
ReportedCaps->DeviceState[i] = ParentCaps->DeviceState[i];
|
|
}
|
|
else {
|
|
//
|
|
// Use PDO's reported value
|
|
//
|
|
ReportedCaps->DeviceState[i] = state;
|
|
}
|
|
}
|
|
|
|
pnpCaps = GetPnpCapsInternal();
|
|
|
|
//
|
|
// Appropriately fill the DeviceCapabilities structure.
|
|
//
|
|
SET_PNP_CAP(pnpCaps, ReportedCaps, LockSupported);
|
|
SET_PNP_CAP(pnpCaps, ReportedCaps, EjectSupported);
|
|
SET_PNP_CAP(pnpCaps, ReportedCaps, Removable);
|
|
SET_PNP_CAP(pnpCaps, ReportedCaps, DockDevice);
|
|
SET_PNP_CAP(pnpCaps, ReportedCaps, UniqueID);
|
|
|
|
if ((pnpCaps & FxPnpCapSilentInstallMask) != FxPnpCapSilentInstallUseDefault) {
|
|
SET_PNP_CAP(pnpCaps, ReportedCaps, SilentInstall);
|
|
}
|
|
else if (m_RawOK) {
|
|
//
|
|
// By default, we report raw devices as silent install devices
|
|
// because if they are raw, they don't need any further
|
|
// installation.
|
|
//
|
|
ReportedCaps->SilentInstall = TRUE;
|
|
}
|
|
|
|
SET_PNP_CAP(pnpCaps, ReportedCaps, SurpriseRemovalOK);
|
|
SET_PNP_CAP(pnpCaps, ReportedCaps, HardwareDisabled);
|
|
SET_PNP_CAP(pnpCaps, ReportedCaps, NoDisplayInUI);
|
|
|
|
SET_POWER_CAP(m_PowerCaps.Caps , ReportedCaps, WakeFromD0);
|
|
SET_POWER_CAP(m_PowerCaps.Caps , ReportedCaps, WakeFromD1);
|
|
SET_POWER_CAP(m_PowerCaps.Caps , ReportedCaps, WakeFromD2);
|
|
SET_POWER_CAP(m_PowerCaps.Caps , ReportedCaps, WakeFromD3);
|
|
SET_POWER_CAP(m_PowerCaps.Caps , ReportedCaps, DeviceD1);
|
|
SET_POWER_CAP(m_PowerCaps.Caps , ReportedCaps, DeviceD2);
|
|
|
|
if (m_RawOK) {
|
|
ReportedCaps->RawDeviceOK = TRUE;
|
|
}
|
|
|
|
ReportedCaps->UINumber = m_PnpCapsUINumber;
|
|
ReportedCaps->Address = m_PnpCapsAddress;
|
|
|
|
if (m_PowerCaps.SystemWake != PowerSystemMaximum) {
|
|
ReportedCaps->SystemWake = (SYSTEM_POWER_STATE) m_PowerCaps.SystemWake;
|
|
}
|
|
else {
|
|
ReportedCaps->SystemWake = ParentCaps->SystemWake;
|
|
}
|
|
|
|
//
|
|
// Set the least-powered device state from which the device can
|
|
// wake the system.
|
|
//
|
|
if (m_PowerCaps.DeviceWake != PowerDeviceMaximum) {
|
|
ReportedCaps->DeviceWake = (DEVICE_POWER_STATE) m_PowerCaps.DeviceWake;
|
|
}
|
|
else {
|
|
ReportedCaps->DeviceWake = ParentCaps->DeviceWake;
|
|
}
|
|
|
|
//
|
|
// Set the Device wake up latencies.
|
|
//
|
|
if (m_PowerCaps.D1Latency != (ULONG) -1) {
|
|
ReportedCaps->D1Latency = m_PowerCaps.D1Latency;
|
|
}
|
|
else {
|
|
ReportedCaps->D1Latency = 0;
|
|
}
|
|
|
|
if (m_PowerCaps.D2Latency != (ULONG) -1) {
|
|
ReportedCaps->D2Latency = m_PowerCaps.D2Latency;
|
|
}
|
|
else {
|
|
ReportedCaps->D2Latency = 0;
|
|
}
|
|
|
|
if (m_PowerCaps.D3Latency != (ULONG) -1) {
|
|
ReportedCaps->D3Latency = m_PowerCaps.D3Latency;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FxPkgPdo::_QueryCapsWorkItem(
|
|
__in MdDeviceObject DeviceObject,
|
|
__in PVOID Context
|
|
)
|
|
{
|
|
STACK_DEVICE_CAPABILITIES parentCapabilities;
|
|
MdWorkItem pItem;
|
|
FxPkgPdo* pPkgPdo;
|
|
FxIrp irp;
|
|
NTSTATUS status;
|
|
MxDeviceObject parentDeviceObject;
|
|
|
|
irp.SetIrp((MdIrp)Context);
|
|
pItem = (MdWorkItem) irp.GetContext(0);
|
|
|
|
pPkgPdo = FxDevice::GetFxDevice(DeviceObject)->GetPdoPkg();
|
|
|
|
parentDeviceObject.SetObject(
|
|
pPkgPdo->m_Device->m_ParentDevice->GetDeviceObject());
|
|
|
|
status = GetStackCapabilities(
|
|
pPkgPdo->m_Device->GetDriverGlobals(),
|
|
&parentDeviceObject,
|
|
NULL, // D3ColdInterface
|
|
&parentCapabilities);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
#pragma prefast(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY, "prefast is confused")
|
|
pPkgPdo->HandleQueryCapabilities(
|
|
irp.GetParameterDeviceCapabilities(),
|
|
&parentCapabilities.DeviceCaps
|
|
);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
pPkgPdo->CompletePnpRequest(&irp, status);
|
|
|
|
MxWorkItem::_Free(pItem);
|
|
}
|
|
|
|
FxDeviceText *
|
|
FindObjectForGivenLocale(
|
|
__in PSINGLE_LIST_ENTRY Head,
|
|
__in LCID LocaleId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
|
|
{
|
|
PSINGLE_LIST_ENTRY ple;
|
|
|
|
for (ple = Head->Next; ple != NULL; ple = ple->Next) {
|
|
FxDeviceText *pDeviceText;
|
|
|
|
pDeviceText= FxDeviceText::_FromEntry(ple);
|
|
|
|
if (pDeviceText->m_LocaleId == LocaleId) {
|
|
//
|
|
// We found our object!
|
|
//
|
|
return pDeviceText;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::_PnpQueryDeviceText(
|
|
__inout FxPkgPnp* This,
|
|
__inout FxIrp *Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method is invoked in response to a Pnp QueryDeviceText IRP.
|
|
We return the decription or the location.
|
|
|
|
Arguments:
|
|
|
|
Irp - a pointer to the FxIrp
|
|
|
|
Returns:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
FxPkgPdo* pThis;
|
|
LCID localeId;
|
|
FxDeviceText *pDeviceTextObject;
|
|
NTSTATUS status;
|
|
PFX_DRIVER_GLOBALS pFxDriverGlobals;
|
|
|
|
pThis = (FxPkgPdo*) This;
|
|
pFxDriverGlobals = pThis->GetDriverGlobals();
|
|
|
|
localeId = Irp->GetParameterQueryDeviceTextLocaleId();
|
|
status = Irp->GetStatus();
|
|
|
|
//
|
|
// The PDO package maintains a collection of "DeviceText" objects. We
|
|
// will look up the item in the collection with the "appropriate" locale.
|
|
//
|
|
// If no entries are found in the collection for the given locale, then
|
|
// we will use the "default locale" property of the PDO and use the
|
|
// entry for the "default locale".
|
|
//
|
|
|
|
//
|
|
// Try to find the FxDeviceText object for the given locale.
|
|
//
|
|
pDeviceTextObject = FindObjectForGivenLocale(
|
|
&pThis->m_DeviceTextHead, localeId);
|
|
|
|
if (pDeviceTextObject == NULL) {
|
|
pDeviceTextObject = FindObjectForGivenLocale(
|
|
&pThis->m_DeviceTextHead, pThis->m_DefaultLocale);
|
|
}
|
|
|
|
if (pDeviceTextObject != NULL) {
|
|
PWCHAR pInformation;
|
|
|
|
pInformation = NULL;
|
|
|
|
switch (Irp->GetParameterQueryDeviceTextType()) {
|
|
case DeviceTextDescription:
|
|
pInformation = pDeviceTextObject->m_Description;
|
|
break;
|
|
|
|
case DeviceTextLocationInformation:
|
|
pInformation = pDeviceTextObject->m_LocationInformation;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Information should now point to a valid unicode string.
|
|
//
|
|
if (pInformation != NULL) {
|
|
PWCHAR pBuffer;
|
|
size_t length;
|
|
|
|
length = (wcslen(pInformation) + 1) * sizeof(WCHAR);
|
|
|
|
//
|
|
// Make sure the information field of the IRP isn't already set.
|
|
//
|
|
ASSERT(Irp->GetInformation() == NULL);
|
|
|
|
pBuffer = (PWCHAR) MxMemory::MxAllocatePoolWithTag(
|
|
PagedPool, length, pFxDriverGlobals->Tag);
|
|
|
|
if (pBuffer != NULL) {
|
|
RtlCopyMemory(pBuffer, pInformation, length);
|
|
Irp->SetInformation((ULONG_PTR) pBuffer);
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
DoTraceLevelMessage(
|
|
pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"WDFDEVICE %p failing Query Device Text, type %d, %!STATUS!",
|
|
pThis->m_Device->GetHandle(),
|
|
Irp->GetParameterQueryDeviceTextType(), status);
|
|
}
|
|
}
|
|
}
|
|
|
|
return pThis->CompletePnpRequest(Irp, status);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::_PnpEject(
|
|
__inout FxPkgPnp* This,
|
|
__inout FxIrp *Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Ejection is handled by the PnP state machine. Handle it synchronously.
|
|
Don't pend it since PnP manager does not serilaize it with other state
|
|
changing pnp irps if handled asynchronously.
|
|
|
|
Arguments:
|
|
|
|
This - the package
|
|
|
|
Irp - the request
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
MxEvent event;
|
|
FxPkgPdo* pdoPkg;
|
|
NTSTATUS status;
|
|
|
|
pdoPkg = (FxPkgPdo*)This;
|
|
|
|
//
|
|
// This will make sure no new state changing pnp irps arrive while
|
|
// we are still processing this one. Also, note that irp is not being
|
|
// marked pending.
|
|
//
|
|
pdoPkg->SetPendingPnpIrp(Irp, FALSE);
|
|
|
|
status = event.Initialize(SynchronizationEvent, FALSE);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
DoTraceLevelMessage(
|
|
pdoPkg->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"Event allocation failed while processing eject for WDFDEVICE %p,"
|
|
" %!STATUS!",
|
|
pdoPkg->m_Device->GetHandle(), status);
|
|
}
|
|
else {
|
|
ASSERT(pdoPkg->m_DeviceEjectProcessed == NULL);
|
|
pdoPkg->m_DeviceEjectProcessed = event.GetSelfPointer();
|
|
|
|
//
|
|
// let state machine process eject
|
|
//
|
|
pdoPkg->PnpProcessEvent(PnpEventEject);
|
|
|
|
//
|
|
// No need to wait in a critical region because we are in the context of a
|
|
// pnp request which is in the system context.
|
|
//
|
|
event.WaitFor(Executive, KernelMode, FALSE, NULL);
|
|
|
|
pdoPkg->m_DeviceEjectProcessed = NULL;
|
|
|
|
status = Irp->GetStatus();
|
|
}
|
|
|
|
//
|
|
// complete request
|
|
//
|
|
|
|
pdoPkg->ClearPendingPnpIrp();
|
|
pdoPkg->CompletePnpRequest(Irp, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
WDF_DEVICE_PNP_STATE
|
|
FxPkgPdo::PnpEventEjectHardwareOverload(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements the EjectHardware state. This
|
|
function overloads the base PnP State machine handler. Its
|
|
job is to call EvtDeviceEject. If that succeeds, then we
|
|
transition immediately to EjectedWaitingForRemove. If not,
|
|
then to EjectFailed.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
WDF_DEVICE_PNP_STATE state;
|
|
|
|
status = m_DeviceEject.Invoke(m_Device->GetHandle());
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Upon a successful eject, mark the child as missing so that when we
|
|
// get another QDR, it is not re-reported.
|
|
//
|
|
FxChildList* pList;
|
|
MxEvent* event;
|
|
|
|
pList = m_Description->GetParentList();
|
|
|
|
status = pList->UpdateAsMissing(m_Description->GetId());
|
|
if (NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
"PDO WDFDEVICE %p !devobj %p marked missing as a result of eject",
|
|
m_Device->GetHandle(), m_Device->GetDeviceObject());
|
|
}
|
|
else {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"Failed to mark PDO WDFDEVICE %p !devobj %p missing after eject %!STATUS!",
|
|
m_Device->GetHandle(), m_Device->GetDeviceObject(),
|
|
status);
|
|
}
|
|
|
|
//
|
|
// We must wait for any pending scans to finish so that the previous
|
|
// update as missing is enacted into the list and reported to the
|
|
// OS. Otherwise, if we don't wait we could be in the middle of a
|
|
// scan, complete the eject, report the child again and it will be
|
|
// reenumerated.
|
|
//
|
|
event = pList->GetScanEvent();
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
"waiting on event %p for device to finish scanning",
|
|
&event);
|
|
|
|
//
|
|
// No need to wait in a crtical region because we are in the context of a
|
|
// pnp request which is in the system context.
|
|
//
|
|
event->WaitFor(Executive, KernelMode, FALSE, NULL);
|
|
|
|
//
|
|
// Change the state.
|
|
//
|
|
state = WdfDevStatePnpEjectedWaitingForRemove;
|
|
}
|
|
else {
|
|
state = WdfDevStatePnpEjectFailed;
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"Eject failed since driver's EvtDeviceEject returned %!STATUS!", status);
|
|
|
|
if (status == STATUS_NOT_SUPPORTED) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE,
|
|
"EvtDeviceEject returned an invalid status STATUS_NOT_SUPPORTED");
|
|
|
|
if (GetDriverGlobals()->IsVerificationEnabled(1, 11, OkForDownLevel)) {
|
|
FxVerifierDbgBreakPoint(GetDriverGlobals());
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// set irp status
|
|
//
|
|
SetPendingPnpIrpStatus(status);
|
|
|
|
//
|
|
// Pnp dispatch routine is waiting on this event, and it will complete
|
|
// the Eject irp
|
|
//
|
|
m_DeviceEjectProcessed->Set();
|
|
|
|
return state;
|
|
}
|
|
|
|
WDF_DEVICE_PNP_STATE
|
|
FxPkgPdo::PnpEventCheckForDevicePresenceOverload(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements the CheckForDevicePresence state. This
|
|
function overloads the base PnP State machine handler. It's
|
|
job is to figure out whether the removed device is actually
|
|
still attached. It then changes state based on that result.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
if (m_Description != NULL) {
|
|
if (m_Description->IsDeviceRemoved()) {
|
|
//
|
|
// The description freed itself now that the device has been reported
|
|
// missing.
|
|
//
|
|
return WdfDevStatePnpPdoRemoved;
|
|
}
|
|
else {
|
|
//
|
|
// Device was not reported as missing, keep it alive
|
|
//
|
|
return WdfDevStatePnpRemovedPdoWait;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Only static children can get this far without having an m_Description
|
|
//
|
|
ASSERT(m_Static);
|
|
|
|
//
|
|
// The description freed itself now that the device has been reported
|
|
// missing.
|
|
//
|
|
return WdfDevStatePnpPdoRemoved;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_PNP_STATE
|
|
FxPkgPdo::PnpEventPdoRemovedOverload(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements the Removed state. This
|
|
function overloads the base PnP State machine handler.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
m_CanBeDeleted = TRUE;
|
|
|
|
//
|
|
// Unconditionally delete the symbolic link now (vs calling
|
|
// DeleteSymbolicLinkOverload()) because we know that the device has been
|
|
// reported as missing.
|
|
//
|
|
m_Device->DeleteSymbolicLink();
|
|
|
|
//
|
|
// Do that which all device stacks need to do upon removal.
|
|
//
|
|
PnpEventRemovedCommonCode();
|
|
|
|
//
|
|
// m_Device is Release()'ed in FxPkgPnp::PnpEventFinal
|
|
//
|
|
if (m_Description != NULL) {
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
"Removing entry reference %p on FxPkgPnp %p",
|
|
m_Description, this);
|
|
|
|
m_Description->ProcessDeviceRemoved();
|
|
m_Description = NULL;
|
|
}
|
|
|
|
return WdfDevStatePnpFinal;
|
|
}
|
|
|
|
WDF_DEVICE_PNP_STATE
|
|
FxPkgPdo::PnpGetPostRemoveState(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// Transition to the check for device presence state.
|
|
//
|
|
return WdfDevStatePnpCheckForDevicePresence;
|
|
}
|
|
|
|
WDF_DEVICE_PNP_STATE
|
|
FxPkgPdo::PnpEventFdoRemovedOverload(
|
|
VOID
|
|
)
|
|
{
|
|
ASSERT(!"This should only be implemented for FDOs.");
|
|
|
|
//
|
|
// Do something safe. Act like the device is not present.
|
|
//
|
|
return WdfDevStatePnpFinal;
|
|
}
|
|
|
|
VOID
|
|
FxPkgPdo::PnpEventSurpriseRemovePendingOverload(
|
|
VOID
|
|
)
|
|
{
|
|
if (m_Description != NULL) {
|
|
m_Description->DeviceSurpriseRemoved();
|
|
}
|
|
|
|
FxPkgPnp::PnpEventSurpriseRemovePendingOverload();
|
|
}
|
|
|
|
BOOLEAN
|
|
FxPkgPdo::PnpSendStartDeviceDownTheStackOverload(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Process the start irp on the way down the stack during a start.
|
|
|
|
Since the PDO is the bottom, just move to the new state where we
|
|
are processing the irp up the stack.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
TRUE, the completion of the start irp down the stack was synchronous
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We are successful so far, indicate this in the irp.
|
|
//
|
|
SetPendingPnpIrpStatus(STATUS_SUCCESS);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::_PnpSetLock(
|
|
__inout FxPkgPnp* This,
|
|
__inout FxIrp *Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Set lock
|
|
|
|
Arguments:
|
|
This - the package
|
|
|
|
Irp - the irp
|
|
|
|
Return Value:
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN lock;
|
|
|
|
lock = Irp->GetParameterSetLockLock();
|
|
|
|
status = ((FxPkgPdo*) This)->m_DeviceSetLock.Invoke(
|
|
((FxPkgPdo*) This)->m_Device->GetHandle(), lock);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
Irp->SetInformation(NULL);
|
|
}
|
|
|
|
return ((FxPkgPdo*) This)->CompletePnpRequest(Irp, status);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::_PnpQueryId(
|
|
__inout FxPkgPnp* This,
|
|
__inout FxIrp *Irp
|
|
)
|
|
{
|
|
FxPkgPdo* pThis;
|
|
NTSTATUS status;
|
|
PWCHAR pBuffer;
|
|
PCWSTR pSrc;
|
|
size_t cbLength;
|
|
PFX_DRIVER_GLOBALS pFxDriverGlobals;
|
|
BUS_QUERY_ID_TYPE queryIdType;
|
|
|
|
pThis = (FxPkgPdo*) This;
|
|
pFxDriverGlobals = pThis->GetDriverGlobals();
|
|
status = Irp->GetStatus();
|
|
cbLength = 0;
|
|
|
|
queryIdType = Irp->GetParameterQueryIdType();
|
|
|
|
switch (queryIdType) {
|
|
case BusQueryDeviceID:
|
|
case BusQueryInstanceID:
|
|
case BusQueryContainerID:
|
|
if (queryIdType == BusQueryDeviceID) {
|
|
pSrc = pThis->m_DeviceID;
|
|
}
|
|
else if (queryIdType == BusQueryInstanceID) {
|
|
pSrc = pThis->m_InstanceID;
|
|
}
|
|
else {
|
|
pSrc = pThis->m_ContainerID;
|
|
}
|
|
|
|
if (pSrc != NULL) {
|
|
cbLength = (wcslen(pSrc) + 1) * sizeof(WCHAR);
|
|
|
|
pBuffer = (PWCHAR) MxMemory::MxAllocatePoolWithTag(
|
|
PagedPool, cbLength, pFxDriverGlobals->Tag);
|
|
}
|
|
else {
|
|
status = Irp->GetStatus();
|
|
break;
|
|
}
|
|
|
|
if (pBuffer != NULL) {
|
|
|
|
//
|
|
// This will copy the NULL terminator too
|
|
//
|
|
RtlCopyMemory(pBuffer, pSrc, cbLength);
|
|
Irp->SetInformation((ULONG_PTR) pBuffer);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
break;
|
|
|
|
case BusQueryHardwareIDs:
|
|
case BusQueryCompatibleIDs:
|
|
if (queryIdType == BusQueryHardwareIDs) {
|
|
pSrc = pThis->m_HardwareIDs;
|
|
}
|
|
else {
|
|
pSrc = pThis->m_CompatibleIDs;
|
|
}
|
|
|
|
if (pSrc != NULL) {
|
|
cbLength = FxCalculateTotalMultiSzStringSize(pSrc);
|
|
}
|
|
else {
|
|
//
|
|
// Must return an empty list
|
|
//
|
|
cbLength = 2 * sizeof(UNICODE_NULL);
|
|
}
|
|
|
|
pBuffer = (PWCHAR) MxMemory::MxAllocatePoolWithTag(
|
|
PagedPool, cbLength, pFxDriverGlobals->Tag);
|
|
|
|
if (pBuffer != NULL) {
|
|
if (pSrc != NULL) {
|
|
RtlCopyMemory(pBuffer, pSrc, cbLength);
|
|
}
|
|
else {
|
|
RtlZeroMemory(pBuffer, cbLength);
|
|
}
|
|
|
|
Irp->SetInformation((ULONG_PTR) pBuffer);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
Irp->SetInformation(NULL);
|
|
|
|
if (status == STATUS_NOT_SUPPORTED) {
|
|
DoTraceLevelMessage(
|
|
pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"WDFDEVICE %p does not have a string for PnP query IdType "
|
|
"%!BUS_QUERY_ID_TYPE!, %!STATUS!",
|
|
pThis->m_Device->GetHandle(),
|
|
queryIdType, status);
|
|
}
|
|
else {
|
|
DoTraceLevelMessage(
|
|
pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"WDFDEVICE %p could not alloc string for PnP query IdType "
|
|
"%!BUS_QUERY_ID_TYPE!, %!STATUS!",
|
|
pThis->m_Device->GetHandle(),
|
|
queryIdType, status);
|
|
}
|
|
}
|
|
|
|
return ((FxPkgPdo*) pThis)->CompletePnpRequest(Irp, status);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::_PnpQueryPnpDeviceState(
|
|
__inout FxPkgPnp* This,
|
|
__inout FxIrp *Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
indicates the current device state
|
|
|
|
Arguments:
|
|
This - the package
|
|
|
|
Irp - the request
|
|
|
|
Return Value:
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PNP_DEVICE_STATE pnpDeviceState;
|
|
PFX_DRIVER_GLOBALS FxDriverGlobals = This->GetDriverGlobals();
|
|
|
|
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
"Entering QueryPnpDeviceState handler");
|
|
|
|
pnpDeviceState = ((FxPkgPdo*) This)->HandleQueryPnpDeviceState(
|
|
(PNP_DEVICE_STATE) Irp->GetInformation());
|
|
|
|
Irp->SetInformation((ULONG_PTR) pnpDeviceState);
|
|
|
|
DoTraceLevelMessage(
|
|
FxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
"WDFDEVICE 0x%p !devobj 0x%p returning PNP_DEVICE_STATE 0x%d IRP 0x%p",
|
|
This->GetDevice()->GetHandle(),
|
|
This->GetDevice()->GetDeviceObject(),
|
|
pnpDeviceState,
|
|
Irp->GetIrp());
|
|
|
|
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
"Exiting QueryPnpDeviceState handler");
|
|
|
|
return ((FxPkgPdo*) This)->CompletePnpRequest(Irp, STATUS_SUCCESS);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::_PnpQueryBusInformation(
|
|
__inout FxPkgPnp* This,
|
|
__inout FxIrp *Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Returns the bus information for this child
|
|
|
|
Arguments:
|
|
This - the package
|
|
|
|
Irp - the request
|
|
|
|
Return Value:
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
FxPkgPdo* pThis;
|
|
NTSTATUS status;
|
|
|
|
pThis = (FxPkgPdo*) This;
|
|
|
|
status = pThis->m_Device->m_ParentDevice->m_PkgPnp->
|
|
HandleQueryBusInformation(Irp);
|
|
|
|
return pThis->CompletePnpRequest(Irp, status);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::_PnpSurpriseRemoval(
|
|
__inout FxPkgPnp* This,
|
|
__inout FxIrp *Irp
|
|
)
|
|
{
|
|
FxPkgPdo* pThis;
|
|
|
|
pThis = (FxPkgPdo*) This;
|
|
|
|
pThis->m_Description->DeviceSurpriseRemoved();
|
|
|
|
return pThis->PnpSurpriseRemoval(Irp);
|
|
}
|
|
|
|
VOID
|
|
FxPkgPdo::RegisterCallbacks(
|
|
__in PWDF_PDO_EVENT_CALLBACKS DispatchTable
|
|
)
|
|
{
|
|
m_DeviceResourcesQuery.m_Method = DispatchTable->EvtDeviceResourcesQuery;
|
|
m_DeviceResourceRequirementsQuery.m_Method = DispatchTable->EvtDeviceResourceRequirementsQuery;
|
|
m_DeviceEject.m_Method = DispatchTable->EvtDeviceEject;
|
|
m_DeviceSetLock.m_Method = DispatchTable->EvtDeviceSetLock;
|
|
|
|
m_DeviceEnableWakeAtBus.m_Method = DispatchTable->EvtDeviceEnableWakeAtBus;
|
|
m_DeviceDisableWakeAtBus.m_Method = DispatchTable->EvtDeviceDisableWakeAtBus;
|
|
|
|
//
|
|
// this callback was added in V1.11
|
|
//
|
|
if (DispatchTable->Size > sizeof(WDF_PDO_EVENT_CALLBACKS_V1_9)) {
|
|
m_DeviceReportedMissing.m_Method = DispatchTable->EvtDeviceReportedMissing;
|
|
}
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::AskParentToRemoveAndReenumerate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine asks the PDO to ask its parent bus driver to Surprise-Remove
|
|
and re-enumerate the PDO. This will be done only at the point of
|
|
catastrophic software failure, and occasionally after catastrophic hardware
|
|
failure.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
status
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Static children do not support reenumeration.
|
|
//
|
|
if (m_Description != NULL && m_Static == FALSE) {
|
|
m_Description->GetParentList()->ReenumerateEntry(m_Description);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
|
|
VOID
|
|
FxPkgPdo::DeleteSymbolicLinkOverload(
|
|
__in BOOLEAN GracefulRemove
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Role specific virtual function which determines if the symbolic link for a
|
|
device should be deleted.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if (GracefulRemove) {
|
|
//
|
|
// We will remove the symbolic link when we determine if the PDO was
|
|
// reported missing or not.
|
|
//
|
|
return;
|
|
}
|
|
else if (m_Description->IsDeviceReportedMissing()) {
|
|
//
|
|
// Surprise removed and we have reported the PDO as missing
|
|
//
|
|
|
|
m_Device->DeleteSymbolicLink();
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
FxPkgPdo::_RemoveAndReenumerateSelf(
|
|
__in PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine is passed out to higher-level drivers as part of the
|
|
re-enumeration interface.
|
|
|
|
Arguments:
|
|
This
|
|
|
|
Return Value:
|
|
void
|
|
|
|
--*/
|
|
{
|
|
((FxPkgPdo*) Context)->AskParentToRemoveAndReenumerate();
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::HandleQueryInterfaceForReenumerate(
|
|
__in FxIrp* Irp,
|
|
__out PBOOLEAN CompleteRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Handles a query interface on the PDO for the self reenumeration interface.
|
|
|
|
Arguments:
|
|
Irp - the request containing the QI
|
|
|
|
CompleteRequest - whether the caller should complete the request when this
|
|
call returns
|
|
|
|
Return Value:
|
|
status to complete the irp with
|
|
|
|
--*/
|
|
{
|
|
PREENUMERATE_SELF_INTERFACE_STANDARD pInterface;
|
|
NTSTATUS status;
|
|
|
|
*CompleteRequest = TRUE;
|
|
|
|
if (m_Static) {
|
|
//
|
|
// Return the embedded status in the irp since this is a statically
|
|
// enumerated child. Only dynamically enuemrated child support self
|
|
// reenumeration.
|
|
//
|
|
return Irp->GetStatus();
|
|
}
|
|
|
|
if (Irp->GetParameterQueryInterfaceVersion() == 1 &&
|
|
Irp->GetParameterQueryInterfaceSize() >= sizeof(*pInterface)) {
|
|
|
|
pInterface = (PREENUMERATE_SELF_INTERFACE_STANDARD)
|
|
Irp->GetParameterQueryInterfaceInterface();
|
|
|
|
//
|
|
// Expose the interface to the requesting driver.
|
|
//
|
|
pInterface->Version = 1;
|
|
pInterface->Size = sizeof(*pInterface);
|
|
pInterface->Context = this;
|
|
pInterface->InterfaceReference = FxDevice::_InterfaceReferenceNoOp;
|
|
pInterface->InterfaceDereference = FxDevice::_InterfaceDereferenceNoOp;
|
|
pInterface->SurpriseRemoveAndReenumerateSelf = &FxPkgPdo::_RemoveAndReenumerateSelf;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Caller assumes a reference has been taken.
|
|
//
|
|
pInterface->InterfaceReference(pInterface->Context);
|
|
}
|
|
else {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::ProcessRemoveDeviceOverload(
|
|
__inout FxIrp* Irp
|
|
)
|
|
{
|
|
if (m_CanBeDeleted) {
|
|
//
|
|
// After this is called, any irp dispatched to FxDevice::DispatchWithLock
|
|
// will fail with STATUS_INVALID_DEVICE_REQUEST.
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Mx::MxReleaseRemoveLockAndWait(
|
|
m_Device->GetRemoveLock(),
|
|
Irp->GetIrp()
|
|
);
|
|
|
|
//
|
|
// Cleanup the state machines and release the power thread.
|
|
//
|
|
CleanupStateMachines(TRUE);
|
|
|
|
//
|
|
// Detach and delete the device object.
|
|
//
|
|
DeleteDevice();
|
|
|
|
//
|
|
// Can't call CompletePnpRequest because we just released the tag in
|
|
// IoReleaseRemoveLockAndWait above.
|
|
//
|
|
Irp->CompleteRequest(IO_NO_INCREMENT);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
//
|
|
// This was a PDO which was not reported missing, so do not free the
|
|
// memory and clear out our stack local address.
|
|
//
|
|
m_DeviceRemoveProcessed = NULL;
|
|
return CompletePnpRequest(Irp, Irp->GetStatus());
|
|
}
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::QueryForPowerThread(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Since the PDO is the lowest device in the stack, it does not have to send
|
|
a query down the stack. Rather, it just creates the thread and returns.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
return CreatePowerThread();
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPkgPdo::AddEjectionDevice(
|
|
__in MdDeviceObject DependentDevice
|
|
)
|
|
{
|
|
FxRelatedDevice* pRelated;
|
|
NTSTATUS status;
|
|
|
|
if (m_EjectionDeviceList == NULL) {
|
|
KIRQL irql;
|
|
|
|
Lock(&irql);
|
|
if (m_EjectionDeviceList == NULL) {
|
|
m_EjectionDeviceList = new (GetDriverGlobals()) FxRelatedDeviceList();
|
|
|
|
if (m_EjectionDeviceList != NULL) {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"Could not allocate ejection device list for PDO WDFDEVICE %p",
|
|
|
|
m_Device->GetHandle());
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// another thread allocated the list already
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
Unlock(irql);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
pRelated = new(GetDriverGlobals())
|
|
FxRelatedDevice(DependentDevice, GetDriverGlobals());
|
|
|
|
if (pRelated == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
status = m_EjectionDeviceList->Add(GetDriverGlobals(), pRelated);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// EjectRelations are queried automatically by PnP when the device is
|
|
// going to be ejected. No need to tell pnp that the list changed
|
|
// until it needs to query for it.
|
|
//
|
|
DO_NOTHING();
|
|
}
|
|
else {
|
|
pRelated->DeleteFromFailedCreate();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
FxPkgPdo::RemoveEjectionDevice(
|
|
__in MdDeviceObject DependentDevice
|
|
)
|
|
{
|
|
if (m_EjectionDeviceList != NULL) {
|
|
m_EjectionDeviceList->Remove(GetDriverGlobals(), DependentDevice);
|
|
}
|
|
|
|
//
|
|
// EjectRelations are queried automatically by PnP when the device is
|
|
// going to be ejected. No need to tell pnp that the list changed
|
|
// until it needs to query for it.
|
|
//
|
|
}
|
|
|
|
VOID
|
|
FxPkgPdo::ClearEjectionDevicesList(
|
|
VOID
|
|
)
|
|
{
|
|
FxRelatedDevice* pEntry;
|
|
|
|
if (m_EjectionDeviceList != NULL) {
|
|
m_EjectionDeviceList->LockForEnum(GetDriverGlobals());
|
|
while ((pEntry = m_EjectionDeviceList->GetNextEntry(NULL)) != NULL) {
|
|
m_EjectionDeviceList->Remove(GetDriverGlobals(),
|
|
pEntry->GetDevice());
|
|
}
|
|
m_EjectionDeviceList->UnlockFromEnum(GetDriverGlobals());
|
|
}
|
|
|
|
//
|
|
// EjectRelations are queried automatically by PnP when the device is
|
|
// going to be ejected. No need to tell pnp that the list changed
|
|
// until it needs to query for it.
|
|
//
|
|
}
|