Skip to content

Commit

Permalink
Added fixes from haidragon/hUsbDk for hug removal, daynix#115 by remo…
Browse files Browse the repository at this point in the history
…ving D0 power setting code, daynix#103 by adding more USB3 hub idetifiers, daynix#105 with counting fixes from kvojacheck.

Hopefully fixed remaining daynix#124 issues by adding device configuration and hub port reset before attempting port CYCLE for devices with no function driver. Cleaned up no-function driver devices handling so that the RawFilter doesn't affect devices mis-classified on first plugin, as well as ensuring function driver install proceeds correctly after device has been used without a function driver.
  • Loading branch information
gwgill committed Nov 10, 2023
1 parent a28e3c8 commit 0969455
Show file tree
Hide file tree
Showing 14 changed files with 542 additions and 101 deletions.
316 changes: 295 additions & 21 deletions UsbDk/ControlDevice.cpp

Large diffs are not rendered by default.

16 changes: 12 additions & 4 deletions UsbDk/ControlDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ class CUsbDkFDriverRule : public CAllocatable < USBDK_NON_PAGED_POOL, 'FDRR' >
: m_VidPid(VidPid)
, m_PortHub(PortHub)
{
m_KeyName.Swap(KeyName);
m_KeyName.Transfer(KeyName);
}

bool Match(const ULONG VidPid, const ULONG PortHub) const
Expand Down Expand Up @@ -301,15 +301,15 @@ class CUsbDkControlDevice : private CWdfControlDevice, public CAllocatable<USBDK
{ return ReloadPersistentHideRules(); }

bool EnumerateDevices(USB_DK_DEVICE_INFO *outBuff, size_t numberAllocatedDevices, size_t &numberExistingDevices);
NTSTATUS ResetUsbDevice(const USB_DK_DEVICE_ID &DeviceId, bool ForceD0);
NTSTATUS ResetUsbDevice(const USB_DK_DEVICE_ID &DeviceId);
NTSTATUS AddRedirect(const USB_DK_DEVICE_ID &DeviceId, HANDLE RequestorProcess, PHANDLE ObjectHandle);

NTSTATUS AddHideRule(const USB_DK_HIDE_RULE &UsbDkRule);
NTSTATUS AddPersistentHideRule(const USB_DK_HIDE_RULE &UsbDkRule);

void ClearHideRules();

NTSTATUS RemoveRedirect(const USB_DK_DEVICE_ID &DeviceId);
NTSTATUS RemoveRedirect(const USB_DK_DEVICE_ID &DeviceId, ULONG pid);
NTSTATUS GetConfigurationDescriptor(const USB_DK_CONFIG_DESCRIPTOR_REQUEST &Request,
PUSB_CONFIGURATION_DESCRIPTOR Descriptor,
size_t *OutputBuffLen);
Expand Down Expand Up @@ -347,11 +347,13 @@ class CUsbDkControlDevice : private CWdfControlDevice, public CAllocatable<USBDK
}

bool NotifyRedirectorAttached(CRegText *DeviceID, CRegText *InstanceID, CUsbDkFilterDevice *RedirectorDevice);
bool NotifyRedirectorRemovalStarted(const USB_DK_DEVICE_ID &ID);
bool NotifyRedirectorRemovalStarted(const USB_DK_DEVICE_ID &ID, ULONG pid);
bool WaitForDetachment(const USB_DK_DEVICE_ID &ID);

NTSTATUS ReloadHasDriverList();

CUsbDkChildDevice *GetChildByPDO(const PDEVICE_OBJECT PDO);

private:
NTSTATUS ReloadPersistentHideRules();

Expand Down Expand Up @@ -389,6 +391,10 @@ class CUsbDkControlDevice : private CWdfControlDevice, public CAllocatable<USBDK
template <typename TFunctor>
bool EnumUsbDevicesByID(const USB_DK_DEVICE_ID &ID, TFunctor Functor);
PDEVICE_OBJECT GetPDOByDeviceID(const USB_DK_DEVICE_ID &DeviceID);
CUsbDkChildDevice *GetChildByDeviceID(const USB_DK_DEVICE_ID &DeviceID);

template <typename TFunctor>
bool EnumUsbDevicesByPDO(const PDEVICE_OBJECT PDO, TFunctor Functor);

bool UsbDeviceExists(const USB_DK_DEVICE_ID &ID);

Expand All @@ -401,6 +407,8 @@ class CUsbDkControlDevice : private CWdfControlDevice, public CAllocatable<USBDK
USB_CONFIGURATION_DESCRIPTOR &Descriptor,
size_t Length);

NTSTATUS SetUsbConfiguration(CUsbDkChildDevice *Child, UCHAR configuration);

static void IoInCallerContext(WDFDEVICE Device, WDFREQUEST Request);

friend class CUsbDkControlDeviceInit;
Expand Down
67 changes: 25 additions & 42 deletions UsbDk/DeviceAccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,25 +221,6 @@ bool CWdmDeviceAccess::QueryPowerData(CM_POWER_DATA& powerData)
#endif
}

#if 0
static void PowerRequestCompletion(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ UCHAR MinorFunction,
_In_ POWER_STATE PowerState,
_In_opt_ PVOID Context,
_In_ PIO_STATUS_BLOCK IoStatus
)
{
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(MinorFunction);
UNREFERENCED_PARAMETER(PowerState);
UNREFERENCED_PARAMETER(IoStatus);
CWdmEvent *pev = (CWdmEvent *)Context;
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVACCESS, "%!FUNC! -> D%d", PowerState.DeviceState - 1);
pev->Set();
}
#endif

PWCHAR CWdmDeviceAccess::MakeNonPagedDuplicate(BUS_QUERY_ID_TYPE idType, PWCHAR idData)
{
auto bufferLength = GetIdBufferLength(idType, idData);
Expand Down Expand Up @@ -305,44 +286,46 @@ NTSTATUS CWdmDeviceAccess::QueryForInterface(const GUID &guid, __out INTERFACE &
return status;
}

NTSTATUS CWdmUsbDeviceAccess::Reset(bool ForceD0)
NTSTATUS CWdmUsbDeviceAccess::Reset()
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVACCESS, "%!FUNC! About to send IOCTL_INTERNAL_USB_RESET_PORT to PDO 0x%p",m_DevObj);

CIoControlIrp Irp;
#if 0 // #115 reports that this can cause a WDF_VIOLATION (10d) error code with some devices.
CM_POWER_DATA powerData;
if (ForceD0 && QueryPowerData(powerData) && powerData.PD_MostRecentPowerState != PowerDeviceD0)
{
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVACCESS, "%!FUNC! device power state D%d", powerData.PD_MostRecentPowerState - 1);
POWER_STATE PowerState;
CWdmEvent Event;
PowerState.DeviceState = PowerDeviceD0;
auto status = PoRequestPowerIrp(m_DevObj, IRP_MN_SET_POWER, PowerState, PowerRequestCompletion, &Event, NULL);
if (NT_SUCCESS(status))
{
Event.Wait();
}
auto status = Irp.Create(m_DevObj, IOCTL_INTERNAL_USB_RESET_PORT);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVACCESS, "%!FUNC! Creation IOCTL_INTERNAL_USB_RESET_PORT %!STATUS! n", status);
return status;
}
#else
ForceD0;
#endif

TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVACCESS, "%!FUNC! About to sed IOCTL_INTERNAL_USB_CYCLE_PORT");
status = Irp.SendSynchronously();
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVACCESS, "%!FUNC! Execute PORT %!STATUS!", status);
}

auto status = Irp.Create(m_DevObj, IOCTL_INTERNAL_USB_CYCLE_PORT);
return status;
}

NTSTATUS CWdmUsbDeviceAccess::Cycle()
{
/* (Attempting a IRP_MN_SET_POWER can cause a WDF_VIOLATION if we're not the Power Manager.) */

TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVACCESS, "%!FUNC! About to send IOCTL_INTERNAL_USB_CYCLE_PORT to PDO 0x%p",m_DevObj);

CIoControlIrp Irp;
auto status = Irp.Create(m_DevObj, IOCTL_INTERNAL_USB_CYCLE_PORT);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVACCESS, "%!FUNC! Error %!STATUS! during IOCTL IRP creation", status);
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVACCESS, "%!FUNC! Create IOCTL_INTERNAL_USB_CYCLE_PORT %!STATUS!", status);
return status;
}

status = Irp.SendSynchronously();

if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVACCESS, "%!FUNC! Send IOCTL IRP Error %!STATUS!", status);
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVACCESS, "%!FUNC! Execute IOCTL_INTERNAL_USB_CYCLE_PORT %!STATUS!", status);
}

return status;
}

Expand Down
3 changes: 2 additions & 1 deletion UsbDk/DeviceAccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ class CWdmUsbDeviceAccess : public CWdmDeviceAccess
: CWdmDeviceAccess(WdmDevice)
{ }

NTSTATUS Reset(bool ForceD0);
NTSTATUS Reset();
NTSTATUS Cycle();
NTSTATUS GetDeviceDescriptor(USB_DEVICE_DESCRIPTOR &Descriptor);
NTSTATUS GetConfigurationDescriptor(UCHAR Index, USB_CONFIGURATION_DESCRIPTOR &Descriptor, size_t Length);

Expand Down
137 changes: 123 additions & 14 deletions UsbDk/FilterDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ NTSTATUS CUsbDkFilterDeviceInit::Configure(ULONG InstanceNumber)
[](_In_ WDFFILEOBJECT FileObject)
{
WDFDEVICE Device = WdfFileObjectGetDevice(FileObject);
Strategy(Device)->OnClose();
ULONG pid = (ULONG)(ULONG_PTR)PsGetCurrentProcessId();
Strategy(Device)->OnClose(pid);
},
WDF_NO_EVENT_CALLBACK);

Expand Down Expand Up @@ -245,6 +246,22 @@ class CNonPagedDeviceRelations : public CDeviceRelations
NTSTATUS CUsbDkHubFilterStrategy::PNPPreProcess(PIRP Irp)
{
auto irpStack = IoGetCurrentIrpStackLocation(Irp);
/* GWG
IRP_MN_START_DEVICE 0x1
IRP_MN_REMOVE_DEVICE 0x2
IRP_MN_QUERY_DEVICE_RELATIONS 0x7
IRP_MN_QUERY_INTERFACE 0x8
IRP_MN_QUERY_CAPABILITIES 0x9
IRP_MN_QUERY_RESOURCES 0xa
IRP_MN_QUERY_RESOURCE_REQUIREMENTS 0xb
IRP_MN_QUERY_DEVICE_TEXT 0xc
IRP_MN_QUERY_ID 0x13
IRP_MN_QUERY_PNP_DEVICE_STATE 0x14
IRP_MN_QUERY_BUS_INFORMATION 0x15
IRP_MN_SURPRISE_REMOVAL 0x17
IRP_MN_DEVICE_ENUMERATED 0x19
*/
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_HIDER, "%!FUNC! Irp 0x%x, PDO 0x%p",irpStack->MinorFunction, irpStack->DeviceObject);

if ((irpStack->MinorFunction == IRP_MN_QUERY_DEVICE_RELATIONS) &&
(BusRelations == irpStack->Parameters.QueryDeviceRelations.Type))
Expand All @@ -271,14 +288,12 @@ NTSTATUS CUsbDkHubFilterStrategy::PNPPreProcess(PIRP Irp)
});
}

#if 0 // From haidragon/hUsbDk
/* Remove all the hubs children if it is unplugged. */
if (irpStack->MinorFunction == IRP_MN_SURPRISE_REMOVAL)
|| irpStack->MinorFunction == IRP_MN_REMOVE_DEVICE)
/* (From haidragon/hUsbDk) */
/* Remove all the hubs children if it is unplugged. */
if (irpStack->MinorFunction == IRP_MN_REMOVE_DEVICE)
{
// Need to create DropAllDevices() method ?
DropAllDevices();
}
#endif

return CUsbDkFilterStrategy::PNPPreProcess(Irp);
}
Expand All @@ -298,7 +313,31 @@ void CUsbDkHubFilterStrategy::DropRemovedDevices(const CDeviceRelations &Relatio
});
ToBeDeleted.ForEach([this](CUsbDkChildDevice *Device) -> bool
{
Device->MarkRawDeviceToReinstall();
/* If the device is ReallyRaw, make it re-install on next plug */
if (Device->IfReallyRaw())
Device->MarkRawDeviceToReinstall();
m_ControlDevice->NotifyRedirectionRemoved(*Device);
return true;
});
}

void CUsbDkHubFilterStrategy::DropAllDevices()
{
//Child device must be deleted on PASSIVE_LEVEL
//So we put those to non-locked list and let its destructor do the job
CWdmList<CUsbDkChildDevice, CRawAccess, CNonCountingObject> ToBeDeleted;
Children().ForEachDetached([&ToBeDeleted](CUsbDkChildDevice *Child) -> bool
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_FILTERDEVICE, "%!FUNC! Deleting child object:");
Child->Dump();
ToBeDeleted.PushBack(Child);
return true;
});
ToBeDeleted.ForEach([this](CUsbDkChildDevice *Device) -> bool
{
/* If the device is ReallyRaw, make it re-install on next plug */
if (Device->IfReallyRaw())
Device->MarkRawDeviceToReinstall();
m_ControlDevice->NotifyRedirectionRemoved(*Device);
return true;
});
Expand Down Expand Up @@ -335,7 +374,6 @@ void CUsbDkHubFilterStrategy::RegisterNewChild(PDEVICE_OBJECT PDO)
// device. Sending USB requests to it may be problematic as sending URB
// is just internal device control with trivial IOCTL code.
// Before trying to initialize it as USB device we check it is really one.

CObjHolder<CRegText> DevID;
CObjHolder<CRegText> InstanceID;
CObjHolder<CRegText> LocationID;
Expand Down Expand Up @@ -415,6 +453,8 @@ void CUsbDkHubFilterStrategy::RegisterNewChild(PDEVICE_OBJECT PDO)
InstanceID.detach();
LocationID.detach();

TraceEvents(TRACE_LEVEL_ERROR, TRACE_FILTERDEVICE,
"%!FUNC! Adding child 0x%p PDO 0x%p", Device, PDO);
Children().PushBack(Device);

ApplyRedirectionPolicy(*Device);
Expand Down Expand Up @@ -523,7 +563,7 @@ NTSTATUS CUsbDkFilterDevice::AttachToStack(WDFDRIVER Driver)
return status;
}

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Attached");
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! Attached, PDO 0x%p",WdmObject());
return STATUS_SUCCESS;
}

Expand All @@ -550,6 +590,19 @@ NTSTATUS CUsbDkFilterDevice::DefineStrategy()
return status;
}

/* Tell the Hidden or Raw filter strategy what its child device PDO is */
PDEVICE_OBJECT dpdo = IoGetLowerDeviceObject(WdmObject());
while (dpdo != nullptr) {
PDEVICE_OBJECT ndpdo = IoGetLowerDeviceObject(dpdo);
if (ndpdo == nullptr)
{
m_Strategy->SetMyDevPDO(dpdo); /* (Keeps reference until destructed) */
break;
}
ObDereferenceObject(dpdo);
dpdo = ndpdo;
}

return STATUS_SUCCESS;
}

Expand Down Expand Up @@ -602,6 +655,8 @@ bool CUsbDkFilterDevice::CStrategist::SelectStrategy(PDEVICE_OBJECT DevObj)
{
PAGED_CODE();

TraceEvents(TRACE_LEVEL_ERROR, TRACE_FILTERDEVICE, "%!FUNC! for PDO 0x%p",DevObj);

// Get device ID
CObjHolder<CRegText> DevID;
if (!UsbDkGetWdmDeviceIdentity(DevObj, &DevID, nullptr, nullptr))
Expand Down Expand Up @@ -661,6 +716,15 @@ bool CUsbDkFilterDevice::CStrategist::SelectStrategy(PDEVICE_OBJECT DevObj)
// we are dealing with USB hub and WDF attached us to its stack
// automatically because UsbDk is registered in PNP manager as
// USB hubs filter

/* OR we failed to query the device descriptor, and really shouldn't treat it as a HUB! */
/* (Failure to query the device descriptor appears to return VID_0000&PID_0002) */
if (DevID->Match(L"USB\\VID_0000&PID_0002"))
{
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! No cached device descriptor, and bad VID/PI, No strategy assigned");
return false;
}

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "%!FUNC! No cached device descriptor, assigning hub strategy");
m_Strategy->Delete();
m_Strategy = &m_HubStrategy;
Expand Down Expand Up @@ -830,16 +894,60 @@ void CUsbDkChildDevice::DetermineDeviceClasses()
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FILTERDEVICE, "Class mask %08X", m_ClassMaskForExtHider);
}

/* We can call this once the Raw Filtered device is real to see if our */
/* guess as to whether the device has a function driver is corrrect. */
bool CUsbDkChildDevice::IfReallyRaw() {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_FILTERDEVICE, "%!FUNC! m_IsProbablyRaw %d, m_HaveCheckedIfReallyRaw %d",m_IsProbablyRaw,m_HaveCheckedIfReallyRaw);

/* We only have to make this check once */
if (!m_IsProbablyRaw || m_HaveCheckedIfReallyRaw)
{
m_HaveCheckedIfReallyRaw = true;

TraceEvents(TRACE_LEVEL_ERROR, TRACE_FILTERDEVICE, "%!FUNC! returning %d",m_IsReallyRaw);
return m_IsReallyRaw;
}

m_HaveCheckedIfReallyRaw = true;

CRegKey regkey;

auto status = regkey.Open(*m_HwKeyPath);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_CONTROLDEVICE,
"%!FUNC! Failed to open Key '%wZ' registry key",m_HwKeyPath);
return m_IsReallyRaw; /* Hmm. */
}
CStringHolder DriverNameHolder;
status = DriverNameHolder.Attach(TEXT("Driver"));
ASSERT(NT_SUCCESS(status));

CWdmMemoryBuffer Buffer;
status = regkey.QueryValueInfo(*DriverNameHolder, KeyValuePartialInformation, Buffer);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_CONTROLDEVICE,
"%!FUNC! Failed to read value '%wZ\\Driver' (status %!STATUS!)",
m_HwKeyPath, status);
m_IsReallyRaw = true;
} else {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_CONTROLDEVICE,
"%!FUNC! As able to read value '%wZ\\Driver'", m_HwKeyPath);
m_IsReallyRaw = false;
}

TraceEvents(TRACE_LEVEL_ERROR, TRACE_FILTERDEVICE, "%!FUNC! returning %d",m_IsReallyRaw);
return m_IsReallyRaw;
}

#ifndef CONFIGFLAG_REINSTALL /* This is in um/RegStr.h */
# define CONFIGFLAG_REINSTALL 0x00000020 // Redo install
#endif

void CUsbDkChildDevice::MarkRawDeviceToReinstall()
{
if (!m_SetReinstall)
return;

TraceEvents(TRACE_LEVEL_ERROR, TRACE_FILTERDEVICE, "%!FUNC! Found m_SetReinstall flag "
TraceEvents(TRACE_LEVEL_ERROR, TRACE_FILTERDEVICE, "%!FUNC! setting reinstall flag "
"on Child PDO 0x%p",PDO());
CRegKey regkey;

Expand Down Expand Up @@ -883,3 +991,4 @@ void CUsbDkChildDevice::MarkRawDeviceToReinstall()
m_HwKeyPath, status);
}
}

Loading

0 comments on commit 0969455

Please sign in to comment.