You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2775 lines
75 KiB
2775 lines
75 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pnp.c
|
|
|
|
Abstract:
|
|
|
|
Port driver for USB host controllers
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
6-20-99 : created
|
|
|
|
--*/
|
|
|
|
#include "common.h"
|
|
|
|
// paged functions
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, USBPORT_CreateDeviceObject)
|
|
#pragma alloc_text(PAGE, USBPORT_DeferredStartDevice)
|
|
#pragma alloc_text(PAGE, USBPORT_SymbolicLink)
|
|
#pragma alloc_text(PAGE, USBPORT_GetRegistryKeyValueForPdo)
|
|
#pragma alloc_text(PAGE, USBPORT_SetRegistryKeyValueForPdo)
|
|
#pragma alloc_text(PAGE, USBPORT_MakeRootHubPdoName)
|
|
#pragma alloc_text(PAGE, USBPORT_MakeHcdDeviceName)
|
|
#pragma alloc_text(PAGE, USBPORT_CreateRootHubPdo)
|
|
#pragma alloc_text(PAGE, USBPORT_GetIdString)
|
|
#pragma alloc_text(PAGE, USBPORTSVC_GetMiniportRegistryKeyValue)
|
|
#pragma alloc_text(PAGE, USBPORT_CreatePortFdoSymbolicLink)
|
|
#endif
|
|
|
|
// non paged functions
|
|
//USBPORT_FindMiniport
|
|
//USBPORT_Unload
|
|
//USBPORT_PnPAddDevice
|
|
//USBPORT_GetResources
|
|
//USBPORT_FdoStart_Complete
|
|
//USBPORT_FdoPnPIrp
|
|
//USBPORT_PdoPnPIrp
|
|
|
|
// globals
|
|
|
|
LIST_ENTRY USBPORT_MiniportDriverList;
|
|
USBPORT_SPIN_LOCK USBPORT_GlobalsSpinLock;
|
|
BOOLEAN USBPORT_GlobalInitialized = FALSE;
|
|
LIST_ENTRY USBPORT_USB2fdoList;
|
|
LIST_ENTRY USBPORT_USB1fdoList;
|
|
|
|
ULONG USB2LIB_HcContextSize;
|
|
ULONG USB2LIB_EndpointContextSize;
|
|
ULONG USB2LIB_TtContextSize;
|
|
|
|
/*
|
|
*/
|
|
#define USBPORT_DUMMY_USBD_EXT_SIZE 512
|
|
PUCHAR USBPORT_DummyUsbdExtension = NULL;
|
|
|
|
#if DBG
|
|
ULONG USBPORT_GlobalAllocedPagedPool;
|
|
ULONG USBPORT_GlobalAllocedNonPagedPool;
|
|
#endif
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
USBPORTSVC_GetMiniportRegistryKeyValue(
|
|
PDEVICE_DATA DeviceData,
|
|
BOOLEAN SoftwareBranch,
|
|
PWCHAR KeyNameString,
|
|
ULONG KeyNameStringLength,
|
|
PVOID Data,
|
|
ULONG DataLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get a registry parameter from either the hardware
|
|
or software branch of the registry given the PDO
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEVEXT_FROM_DEVDATA(devExt, DeviceData);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
|
|
ntStatus = USBPORT_GetCachedRegistryKeyValueForPdo(
|
|
devExt->HcFdoDeviceObject,
|
|
devExt->Fdo.PhysicalDeviceObject,
|
|
SoftwareBranch,
|
|
KeyNameString,
|
|
KeyNameStringLength,
|
|
Data,
|
|
DataLength);
|
|
|
|
return USBPORT_NtStatus_TO_MiniportStatus(ntStatus);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_GetRegistryKeyValueForPdo(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PDEVICE_OBJECT PhysicalDeviceObject,
|
|
BOOLEAN SoftwareBranch,
|
|
PWCHAR KeyNameString,
|
|
ULONG KeyNameStringLength,
|
|
PVOID Data,
|
|
ULONG DataLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get a registry parameter from either the hardware
|
|
or software branch of the registry given the PDO
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
UNICODE_STRING keyNameUnicodeString;
|
|
ULONG length;
|
|
PKEY_VALUE_FULL_INFORMATION fullInfo;
|
|
HANDLE handle;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (SoftwareBranch) {
|
|
ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject,
|
|
PLUGPLAY_REGKEY_DRIVER,
|
|
STANDARD_RIGHTS_ALL,
|
|
&handle);
|
|
} else {
|
|
ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_ALL,
|
|
&handle);
|
|
}
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
RtlInitUnicodeString(&keyNameUnicodeString, KeyNameString);
|
|
|
|
length = sizeof(KEY_VALUE_FULL_INFORMATION) +
|
|
KeyNameStringLength + DataLength;
|
|
|
|
ALLOC_POOL_Z(fullInfo, PagedPool, length);
|
|
|
|
USBPORT_KdPrint((2,"' GetRegistryKeyValueForPdo buffer = 0x%x\n", fullInfo));
|
|
|
|
if (fullInfo) {
|
|
ntStatus = ZwQueryValueKey(handle,
|
|
&keyNameUnicodeString,
|
|
KeyValueFullInformation,
|
|
fullInfo,
|
|
length,
|
|
&length);
|
|
|
|
if (NT_SUCCESS(ntStatus)){
|
|
USBPORT_ASSERT(DataLength == fullInfo->DataLength);
|
|
RtlCopyMemory(Data, ((PUCHAR) fullInfo) + fullInfo->DataOffset, DataLength);
|
|
}
|
|
|
|
FREE_POOL(FdoDeviceObject, fullInfo);
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_SetRegistryKeyValueForPdo(
|
|
PDEVICE_OBJECT PhysicalDeviceObject,
|
|
BOOLEAN SoftwareBranch,
|
|
ULONG Type,
|
|
PWCHAR KeyNameString,
|
|
ULONG KeyNameStringLength,
|
|
PVOID Data,
|
|
ULONG DataLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
UNICODE_STRING keyNameUnicodeString;
|
|
HANDLE handle;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (SoftwareBranch) {
|
|
ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject,
|
|
PLUGPLAY_REGKEY_DRIVER,
|
|
STANDARD_RIGHTS_ALL,
|
|
&handle);
|
|
} else {
|
|
ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_ALL,
|
|
&handle);
|
|
}
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
RtlInitUnicodeString(&keyNameUnicodeString, KeyNameString);
|
|
|
|
ntStatus = ZwSetValueKey(handle,
|
|
&keyNameUnicodeString,
|
|
0,
|
|
Type,
|
|
Data,
|
|
DataLength);
|
|
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_SymbolicLink(
|
|
BOOLEAN CreateFlag,
|
|
PDEVICE_EXTENSION DevExt,
|
|
PDEVICE_OBJECT PhysicalDeviceObject,
|
|
LPGUID Guid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
create a symbolic link for a given GUID class and
|
|
PhysicalDeviceObject
|
|
|
|
We also write the name to the hw branch of the registry
|
|
to make it easy to find for a particular instance
|
|
of controller.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject of the controller to stop
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (CreateFlag) {
|
|
|
|
/*
|
|
* Create the symbolic link
|
|
*/
|
|
|
|
USBPORT_ASSERT(!TEST_FLAG(DevExt->Flags, USBPORT_FLAG_SYM_LINK));
|
|
|
|
ntStatus = IoRegisterDeviceInterface(
|
|
PhysicalDeviceObject,
|
|
Guid,
|
|
NULL,
|
|
&DevExt->SymbolicLinkName);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
/*
|
|
* Now set the symbolic link for the association and
|
|
* store it..
|
|
*/
|
|
|
|
// successfully alloced a link
|
|
// set the flag so we will free it
|
|
SET_FLAG(DevExt->Flags, USBPORT_FLAG_SYM_LINK);
|
|
|
|
// write it to the registry -- this is for comaptibilty
|
|
// with older OS versions
|
|
|
|
ntStatus = USBPORT_SetRegistryKeyValueForPdo(
|
|
PhysicalDeviceObject,
|
|
USBPORT_HW_BRANCH,
|
|
REG_SZ,
|
|
SYM_LINK_KEY,
|
|
sizeof(SYM_LINK_KEY),
|
|
&DevExt->SymbolicLinkName.Buffer[0],
|
|
DevExt->SymbolicLinkName.Length);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
ntStatus =
|
|
IoSetDeviceInterfaceState(&DevExt->SymbolicLinkName,
|
|
TRUE);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
USBPORT_ASSERT(TEST_FLAG(DevExt->Flags, USBPORT_FLAG_SYM_LINK));
|
|
|
|
/*
|
|
* Disable the symbolic link
|
|
*/
|
|
ntStatus = IoSetDeviceInterfaceState(
|
|
&DevExt->SymbolicLinkName, FALSE);
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
RtlFreeUnicodeString(&DevExt->SymbolicLinkName);
|
|
CLEAR_FLAG(DevExt->Flags, USBPORT_FLAG_SYM_LINK);
|
|
} else {
|
|
DEBUG_BREAK();
|
|
}
|
|
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
PUSBPORT_MINIPORT_DRIVER
|
|
USBPORT_FindMiniport(
|
|
PDRIVER_OBJECT DriverObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find a miniport given a DriverObject
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to a driver object
|
|
|
|
Return Value:
|
|
|
|
pointer to miniport or NULL
|
|
|
|
--*/
|
|
{
|
|
KIRQL irql;
|
|
PUSBPORT_MINIPORT_DRIVER found = NULL;
|
|
PUSBPORT_MINIPORT_DRIVER miniportDriver;
|
|
PLIST_ENTRY listEntry;
|
|
|
|
KeAcquireSpinLock(&USBPORT_GlobalsSpinLock.sl, &irql);
|
|
|
|
listEntry = &USBPORT_MiniportDriverList;
|
|
if (!IsListEmpty(listEntry)) {
|
|
listEntry = USBPORT_MiniportDriverList.Flink;
|
|
}
|
|
// LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'FIl+', listEntry,
|
|
// &USBPORT_MiniportDriverList, 0);
|
|
|
|
while (listEntry != &USBPORT_MiniportDriverList) {
|
|
|
|
miniportDriver = (PUSBPORT_MINIPORT_DRIVER)
|
|
CONTAINING_RECORD(listEntry,
|
|
struct _USBPORT_MINIPORT_DRIVER,
|
|
ListEntry);
|
|
|
|
if (miniportDriver->DriverObject == DriverObject) {
|
|
found = miniportDriver;
|
|
break;
|
|
}
|
|
|
|
// next entry
|
|
listEntry = miniportDriver->ListEntry.Flink;
|
|
}
|
|
|
|
KeReleaseSpinLock(&USBPORT_GlobalsSpinLock.sl, irql);
|
|
|
|
// LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'Fmpd', found, 0, 0);
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
VOID
|
|
USBPORT_Unload(
|
|
PDRIVER_OBJECT DriverObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free globally allocated miniport structure used to
|
|
track this particular miniport driver.
|
|
|
|
note: OS won't unload unless this is the last instance
|
|
of the miniport
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to a driver object
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
KIRQL irql;
|
|
PUSBPORT_MINIPORT_DRIVER miniportDriver;
|
|
|
|
// find the miniport driver data
|
|
|
|
miniportDriver = USBPORT_FindMiniport(DriverObject);
|
|
|
|
// we had better find it! If we don't we screwed up
|
|
// the system will crash
|
|
USBPORT_ASSERT(miniportDriver != NULL);
|
|
if (miniportDriver == NULL) {
|
|
BUGCHECK(USBBUGCODE_INTERNAL_ERROR, 0, 0, 0);
|
|
// prefix happy
|
|
return;
|
|
}
|
|
|
|
// the miniport should not need to do anything.
|
|
// But just in case/ we will call them if they
|
|
// indicated an unload routine in the DriverObject.
|
|
|
|
USBPORT_KdPrint((1, "'unloading USB miniport\n"));
|
|
|
|
if (miniportDriver->MiniportUnload != NULL) {
|
|
miniportDriver->MiniportUnload(DriverObject);
|
|
}
|
|
|
|
USBPORT_InterlockedRemoveEntryList(&miniportDriver->ListEntry,
|
|
&USBPORT_GlobalsSpinLock.sl);
|
|
|
|
FREE_POOL(NULL, miniportDriver);
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_MakeHcdDeviceName(
|
|
PUNICODE_STRING DeviceNameUnicodeString,
|
|
ULONG Idx
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function generates the name used for the FDO. The
|
|
name format is USBFDO-n where nnn is 0 - 65535.
|
|
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
ULONG bit, i;
|
|
PWCHAR deviceNameBuffer;
|
|
WCHAR nameBuffer[] = L"\\Device\\USBFDO-";
|
|
NTSTATUS ntStatus;
|
|
UNICODE_STRING tmpUnicodeString;
|
|
WCHAR tmpBuffer[16];
|
|
|
|
PAGED_CODE();
|
|
|
|
// enough for 3 digits and NULL
|
|
tmpUnicodeString.Buffer = tmpBuffer;
|
|
tmpUnicodeString.MaximumLength = sizeof(tmpBuffer);
|
|
tmpUnicodeString.Length = 0;
|
|
|
|
ntStatus = RtlIntegerToUnicodeString(Idx,
|
|
10,
|
|
&tmpUnicodeString);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
USHORT siz;
|
|
|
|
siz = sizeof(nameBuffer)+tmpUnicodeString.Length;
|
|
|
|
// we can't log this alloc because the device object
|
|
// has not been created yet
|
|
ALLOC_POOL_Z(deviceNameBuffer, PagedPool, siz);
|
|
|
|
if (deviceNameBuffer != NULL) {
|
|
|
|
RtlCopyMemory(deviceNameBuffer, nameBuffer, sizeof(nameBuffer));
|
|
RtlInitUnicodeString(DeviceNameUnicodeString,
|
|
deviceNameBuffer);
|
|
DeviceNameUnicodeString->MaximumLength = siz;
|
|
|
|
ntStatus = RtlAppendUnicodeStringToString(
|
|
DeviceNameUnicodeString,
|
|
&tmpUnicodeString);
|
|
|
|
} else {
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_MakeRootHubPdoName(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PUNICODE_STRING PdoNameUnicodeString,
|
|
ULONG Index
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This service Creates a name for a PDO created by the HUB
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PWCHAR nameBuffer = NULL;
|
|
WCHAR rootName[] = L"\\Device\\USBPDO-";
|
|
UNICODE_STRING idUnicodeString;
|
|
WCHAR buffer[32];
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
USHORT length;
|
|
BOOLEAN haveString = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
length = sizeof(buffer)+sizeof(rootName);
|
|
|
|
// os frees this when the unicode string is 'freed'
|
|
ALLOC_POOL_OSOWNED(nameBuffer, PagedPool, length);
|
|
|
|
if (nameBuffer) {
|
|
RtlCopyMemory(nameBuffer, rootName, sizeof(rootName));
|
|
|
|
RtlInitUnicodeString(PdoNameUnicodeString,
|
|
nameBuffer);
|
|
PdoNameUnicodeString->MaximumLength =
|
|
length;
|
|
haveString = TRUE; // we have a string now
|
|
|
|
RtlInitUnicodeString(&idUnicodeString,
|
|
&buffer[0]);
|
|
idUnicodeString.MaximumLength =
|
|
sizeof(buffer);
|
|
|
|
ntStatus = RtlIntegerToUnicodeString(
|
|
Index,
|
|
10,
|
|
&idUnicodeString);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
ntStatus = RtlAppendUnicodeStringToString(PdoNameUnicodeString,
|
|
&idUnicodeString);
|
|
}
|
|
|
|
USBPORT_KdPrint((3, "'USBPORT_MakeNodeName string = %x\n",
|
|
PdoNameUnicodeString));
|
|
|
|
} else {
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (!NT_SUCCESS(ntStatus) && haveString) {
|
|
RtlFreeUnicodeString(PdoNameUnicodeString);
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_PnPAddDevice(
|
|
PDRIVER_OBJECT DriverObject,
|
|
PDEVICE_OBJECT PhysicalDeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to create a new instance of a USB host
|
|
controller. This is where we create our deviceObject.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to the driver object for this instance of HCD
|
|
|
|
PhysicalDeviceObject - pointer to a device object created by the bus
|
|
|
|
Return Value:
|
|
|
|
NT STATUS CODE
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
PDEVICE_OBJECT deviceObject = NULL;
|
|
PDEVICE_EXTENSION devExt;
|
|
UNICODE_STRING deviceNameUnicodeString;
|
|
ULONG deviceNameIdx;
|
|
PUSBPORT_MINIPORT_DRIVER miniportDriver;
|
|
|
|
// since we raise IRQL in this function it cannot be pagable
|
|
|
|
// find the driver
|
|
miniportDriver = USBPORT_FindMiniport(DriverObject);
|
|
|
|
USBPORT_ASSERT(miniportDriver != NULL);
|
|
|
|
//
|
|
// generate a device name
|
|
//
|
|
|
|
deviceNameIdx = 0;
|
|
|
|
do {
|
|
|
|
ntStatus = USBPORT_MakeHcdDeviceName(&deviceNameUnicodeString,
|
|
deviceNameIdx);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
ntStatus = USBPORT_CreateDeviceObject(DriverObject,
|
|
miniportDriver,
|
|
&deviceObject,
|
|
&deviceNameUnicodeString);
|
|
|
|
RtlFreeUnicodeString(&deviceNameUnicodeString);
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
//preserve idx
|
|
break;
|
|
}
|
|
}
|
|
|
|
deviceNameIdx++;
|
|
|
|
} while (ntStatus == STATUS_OBJECT_NAME_COLLISION);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
GET_DEVICE_EXT(devExt, deviceObject);
|
|
|
|
// BUGBUG OS should zero this
|
|
RtlZeroMemory(devExt, sizeof(DEVICE_EXTENSION));
|
|
|
|
devExt->DummyUsbdExtension = USBPORT_DummyUsbdExtension;
|
|
devExt->Sig = USBPORT_DEVICE_EXT_SIG;
|
|
devExt->HcFdoDeviceObject = deviceObject;
|
|
devExt->Fdo.PhysicalDeviceObject = PhysicalDeviceObject;
|
|
devExt->Fdo.DeviceNameIdx = deviceNameIdx;
|
|
devExt->Fdo.MiniportDriver = miniportDriver;
|
|
devExt->Fdo.MiniportDeviceData = &devExt->Fdo.MiniportExtension[0];
|
|
|
|
if (USBPORT_IS_USB20(devExt)) {
|
|
PUCHAR pch;
|
|
|
|
pch = (PUCHAR) &devExt->Fdo.MiniportExtension[0];
|
|
devExt->Fdo.Usb2LibHcContext = (PVOID) (pch +
|
|
devExt->Fdo.MiniportDriver->RegistrationPacket.DeviceDataSize);
|
|
|
|
USB2LIB_InitController(devExt->Fdo.Usb2LibHcContext);
|
|
} else {
|
|
devExt->Fdo.Usb2LibHcContext = USB_BAD_PTR;
|
|
}
|
|
|
|
INITIALIZE_PENDING_REQUEST_COUNTER(devExt);
|
|
|
|
// inc once for the add
|
|
// transition to -1 means we have no pending requests
|
|
INCREMENT_PENDING_REQUEST_COUNT(deviceObject, NULL);
|
|
#if DBG
|
|
USBPORT_LogAlloc(&devExt->Log, 16);
|
|
#else
|
|
USBPORT_LogAlloc(&devExt->Log, 8);
|
|
#endif
|
|
// init the log spinlock here
|
|
KeInitializeSpinLock(&devExt->Fdo.LogSpinLock.sl);
|
|
|
|
//#if DBG
|
|
// USBPORT_LogAlloc(&devExt->TransferLog, 4);
|
|
// USBPORT_LogAlloc(&devExt->EnumLog, 4);
|
|
//#endif
|
|
USBPORT_KdPrint((1, "'**USBPORT DEVICE OBJECT** (fdo) = %x, ext = %x\n",
|
|
deviceObject, devExt));
|
|
|
|
|
|
KeInitializeSemaphore(&devExt->Fdo.DeviceLock, 1, 1);
|
|
KeInitializeSemaphore(&devExt->Fdo.CcLock, 1, 1);
|
|
InitializeListHead(&devExt->Fdo.DeviceHandleList);
|
|
InitializeListHead(&devExt->Fdo.MapTransferList);
|
|
InitializeListHead(&devExt->Fdo.DoneTransferList);
|
|
InitializeListHead(&devExt->Fdo.GlobalEndpointList);
|
|
InitializeListHead(&devExt->Fdo.AttendEndpointList);
|
|
InitializeListHead(&devExt->Fdo.EpStateChangeList);
|
|
InitializeListHead(&devExt->Fdo.EpClosedList);
|
|
InitializeListHead(&devExt->Fdo.BadRequestList);
|
|
InitializeListHead(&devExt->Fdo.RegistryCache);
|
|
devExt->Fdo.BadRequestFlush = 0;
|
|
|
|
//
|
|
// we need to handle a seemingly random set of requests
|
|
// to start/stop/remove power up, down etc in order to
|
|
// handle this we keep a set of PNP state flags
|
|
|
|
// not removed, not started, not stopped
|
|
devExt->PnpStateFlags = 0;
|
|
// until we get a start we will consider ourselves OFF
|
|
devExt->CurrentDevicePowerState = PowerDeviceD3;
|
|
|
|
devExt->Fdo.MpStateFlags = 0;
|
|
|
|
// attach to top of PnP stack
|
|
devExt->Fdo.TopOfStackDeviceObject =
|
|
IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
|
|
|
|
devExt->Fdo.PendingRhCallback = 1;
|
|
//
|
|
// Indicate that the device object is ready for requests.
|
|
//
|
|
|
|
if (!USBPORT_IS_USB20(devExt)) {
|
|
deviceObject->Flags |= DO_POWER_PAGABLE;
|
|
}
|
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
}
|
|
|
|
USBPORT_KdPrint((2, "'exit USBPORT_PnPAddDevice (%x)\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_CreateDeviceObject(
|
|
PDRIVER_OBJECT DriverObject,
|
|
PUSBPORT_MINIPORT_DRIVER MiniportDriver,
|
|
PDEVICE_OBJECT *DeviceObject,
|
|
PUNICODE_STRING DeviceNameUnicodeString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to create a new instance of a USB host
|
|
controller.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to the driver object for USBD.
|
|
|
|
*DeviceObject - ptr to DeviceObject ptr to be filled
|
|
in with the device object we create.
|
|
|
|
DeviceNameUnicodeString - optional pointer to a device
|
|
name for this FDO, can be NULL
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
PDEVICE_EXTENSION devExt;
|
|
ULONG extensionSize;
|
|
|
|
PAGED_CODE();
|
|
|
|
USBPORT_KdPrint((2, "'enter USBPORT_CreateDeviceObject\n"));
|
|
|
|
extensionSize = sizeof(DEVICE_EXTENSION)+
|
|
MiniportDriver->RegistrationPacket.DeviceDataSize +
|
|
USB2LIB_HcContextSize;
|
|
|
|
ntStatus = IoCreateDevice(DriverObject,
|
|
extensionSize,
|
|
DeviceNameUnicodeString, // Name
|
|
FILE_DEVICE_CONTROLLER,
|
|
0,
|
|
FALSE, //NOT Exclusive
|
|
DeviceObject);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
devExt = (PDEVICE_EXTENSION) ((*DeviceObject)->DeviceExtension);
|
|
|
|
USBPORT_KdPrint((2, "'USBPORT_CreateDeviceObject: device object %x device extension = %x\n",
|
|
*DeviceObject, devExt));
|
|
|
|
} else if (*DeviceObject) {
|
|
IoDeleteDevice(*DeviceObject);
|
|
}
|
|
|
|
USBPORT_KdPrint((2, "'exit USBPORT_CreateDeviceObject (%x)\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_GetResources(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PCM_RESOURCE_LIST ResourceList,
|
|
PHC_RESOURCES HcResources
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
ResourceList - Resources for this controller.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
NTSTATUS ntStatus;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR interrupt;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR memory;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR ioport;
|
|
PHYSICAL_ADDRESS cardAddress;
|
|
ULONG addressSpace;
|
|
PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR fullResourceDescriptor;
|
|
ULONG mpOptionFlags;
|
|
PDEVICE_EXTENSION devExt;
|
|
|
|
USBPORT_KdPrint((2, "'enter USBPORT_GetResources\n"));
|
|
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
mpOptionFlags = REGISTRATION_PACKET(devExt).OptionFlags;
|
|
|
|
// assume success
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
// init the resource list
|
|
RtlZeroMemory(HcResources, sizeof(*HcResources));
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'GRES', 0, 0, ResourceList);
|
|
|
|
if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_NO_PNP_RESOURCES)) {
|
|
TEST_TRAP();
|
|
// no resources, bail with success
|
|
return ntStatus;
|
|
}
|
|
|
|
if (ResourceList == NULL) {
|
|
USBPORT_KdPrint((1, "'no resources, failing start.\n"));
|
|
ntStatus = STATUS_NONE_MAPPED;
|
|
goto USBPORT_GetResources_Done;
|
|
}
|
|
|
|
fullResourceDescriptor = &ResourceList->List[0];
|
|
PartialResourceList = &fullResourceDescriptor->PartialResourceList;
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'gres',
|
|
PartialResourceList->Count,
|
|
0,
|
|
PartialResourceList);
|
|
|
|
interrupt = NULL;
|
|
memory = NULL;
|
|
ioport = NULL;
|
|
|
|
for (i = 0; i < PartialResourceList->Count; i++) {
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'resT', i,
|
|
PartialResourceList->PartialDescriptors[i].Type, 0);
|
|
|
|
switch (PartialResourceList->PartialDescriptors[i].Type) {
|
|
case CmResourceTypeInterrupt:
|
|
if (interrupt == NULL) {
|
|
interrupt = &PartialResourceList->PartialDescriptors[i];
|
|
}
|
|
break;
|
|
|
|
case CmResourceTypeMemory:
|
|
if (memory == NULL) {
|
|
memory = &PartialResourceList->PartialDescriptors[i];
|
|
}
|
|
break;
|
|
|
|
case CmResourceTypePort:
|
|
if (ioport == NULL) {
|
|
ioport = &PartialResourceList->PartialDescriptors[i];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// only map resources this miniport actually needs
|
|
|
|
if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_NEED_IOPORT) &&
|
|
ioport != NULL &&
|
|
NT_SUCCESS(ntStatus)) {
|
|
//
|
|
// Set up AddressSpace to be of type Port I/O
|
|
//
|
|
|
|
USBPORT_KdPrint((1, "'Port Resources Found @ %x'%x, %d Ports Available \n",
|
|
ioport->u.Port.Start.HighPart,
|
|
ioport->u.Port.Start.LowPart,
|
|
ioport->u.Port.Length));
|
|
|
|
addressSpace =
|
|
(ioport->Flags & CM_RESOURCE_PORT_IO) == CM_RESOURCE_PORT_IO? 1:0;
|
|
|
|
cardAddress=ioport->u.Port.Start;
|
|
|
|
if (!addressSpace) {
|
|
// HcResources->Flags |= MAP_REGISTERS;
|
|
HcResources->DeviceRegisters =
|
|
MmMapIoSpace(
|
|
cardAddress,
|
|
ioport->u.Port.Length,
|
|
FALSE);
|
|
|
|
HcResources->DeviceRegistersLength =
|
|
ioport->u.Port.Length;
|
|
} else {
|
|
// HcResources->Flags &= MAP_REGISTERS;
|
|
HcResources->DeviceRegisters =
|
|
(PVOID)(ULONG_PTR)cardAddress.QuadPart;
|
|
HcResources->DeviceRegistersLength =
|
|
ioport->u.Port.Length;
|
|
}
|
|
|
|
//
|
|
// see if we successfully mapped the IO regs
|
|
//
|
|
|
|
if (HcResources->DeviceRegisters == NULL) {
|
|
USBPORT_KdPrint((1, "'Couldn't map the device(port) registers. \n"));
|
|
ntStatus = STATUS_NONE_MAPPED;
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'Fmio', 0, 0, ntStatus);
|
|
|
|
} else {
|
|
USBPORT_KdPrint((2, "'Mapped device(port) registers to 0x%x.\n",
|
|
HcResources->DeviceRegisters));
|
|
HcResources->Flags |= HCR_IO_REGS;
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_NEED_MEMORY) &&
|
|
memory != NULL &&
|
|
NT_SUCCESS(ntStatus)) {
|
|
//
|
|
// Set up AddressSpace to be of type Memory mapped I/O
|
|
//
|
|
|
|
USBPORT_KdPrint((1,
|
|
"'Memory Resources Found @ %x'%x, Length = %x\n",
|
|
memory->u.Memory.Start.HighPart,
|
|
memory->u.Memory.Start.LowPart,
|
|
memory->u.Memory.Length));
|
|
|
|
addressSpace = 0;
|
|
HcResources->DeviceRegistersLength =
|
|
memory->u.Memory.Length;
|
|
|
|
cardAddress = memory->u.Memory.Start;
|
|
|
|
HcResources->DeviceRegisters =
|
|
MmMapIoSpace(cardAddress,
|
|
HcResources->DeviceRegistersLength,
|
|
FALSE);
|
|
|
|
if (HcResources->DeviceRegisters == NULL) {
|
|
USBPORT_KdPrint((1, "'Couldn't map the device(memory) registers. \n"));
|
|
ntStatus = STATUS_NONE_MAPPED;
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'Fmmr', 0, 0, ntStatus);
|
|
|
|
} else {
|
|
USBPORT_KdPrint((2, "'Mapped device(memory) registers to 0x%x.\n",
|
|
HcResources->DeviceRegisters));
|
|
HcResources->Flags |= HCR_MEM_REGS;
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_NEED_IRQ) &&
|
|
interrupt != NULL &&
|
|
NT_SUCCESS(ntStatus)) {
|
|
//
|
|
// Get Vector, level, and affinity information for this interrupt.
|
|
//
|
|
|
|
USBPORT_KdPrint((1, "'Interrupt Resources Found! Level = %x Vector = %x\n",
|
|
interrupt->u.Interrupt.Level,
|
|
interrupt->u.Interrupt.Vector
|
|
));
|
|
|
|
HcResources->Flags |= HCR_IRQ;
|
|
|
|
//
|
|
// Set up our interrupt.
|
|
//
|
|
|
|
USBPORT_KdPrint((2, "'requesting interrupt vector %x level %x\n",
|
|
interrupt->u.Interrupt.Level,
|
|
interrupt->u.Interrupt.Vector));
|
|
|
|
HcResources->InterruptLevel=(KIRQL)interrupt->u.Interrupt.Level;
|
|
HcResources->InterruptVector=interrupt->u.Interrupt.Vector;
|
|
HcResources->Affinity=interrupt->u.Interrupt.Affinity;
|
|
|
|
//
|
|
// Initialize the interrupt object for the controller.
|
|
//
|
|
|
|
HcResources->InterruptObject = NULL;
|
|
HcResources->ShareIRQ =
|
|
interrupt->ShareDisposition == CmResourceShareShared ? TRUE : FALSE;
|
|
HcResources->InterruptMode =
|
|
interrupt->Flags == CM_RESOURCE_INTERRUPT_LATCHED ?
|
|
Latched :
|
|
LevelSensitive;
|
|
|
|
#ifdef DEBUG
|
|
USBPORT_KdPrint((2, "'interrupt->ShareDisposition %x\n", interrupt->ShareDisposition));
|
|
if (!HcResources->ShareIRQ) {
|
|
TEST_TRAP();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
USBPORT_GetResources_Done:
|
|
|
|
TEST_PATH(ntStatus, FAILED_GETRESOURCES);
|
|
|
|
USBPORT_KdPrint((2, "'exit USBPORT_GetResources (%x)\n", ntStatus));
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'GRSd', 0, 0, ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_FdoStart_Complete(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the port driver completes an IRP.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for the class device.
|
|
|
|
Irp - Irp completed.
|
|
|
|
Context - Driver defined context.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
PKEVENT event = Context;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
USBPORT_ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);
|
|
USBPORT_ASSERT(irpStack->MinorFunction == IRP_MN_START_DEVICE);
|
|
|
|
// signal the start device dispatch to finsh
|
|
KeSetEvent(event,
|
|
1,
|
|
FALSE);
|
|
|
|
// defer completion
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_FdoPnPIrp(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the PNP IRPs sent to the FDO for the host
|
|
controller.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to a hcd device object (FDO)
|
|
|
|
Irp - pointer to an I/O Request Packet
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
|
|
PIO_STACK_LOCATION irpStack;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION devExt;
|
|
BOOLEAN hardwarePresent = TRUE;
|
|
|
|
USBPORT_KdPrint((2, "'IRP_MJ_PNP %x\n", FdoDeviceObject));
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
USBPORT_ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'fPnP', irpStack->MinorFunction, 0, 0);
|
|
|
|
switch (irpStack->MinorFunction) {
|
|
|
|
case IRP_MN_START_DEVICE:
|
|
{
|
|
KEVENT pnpStartEvent;
|
|
|
|
KeInitializeEvent(&pnpStartEvent,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
// pass on to host controllers PDO
|
|
ntStatus =
|
|
USBPORT_PassIrp(FdoDeviceObject,
|
|
USBPORT_FdoStart_Complete,
|
|
&pnpStartEvent,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE,
|
|
Irp);
|
|
|
|
if (ntStatus == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(
|
|
&pnpStartEvent,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
ntStatus = Irp->IoStatus.Status;
|
|
}
|
|
|
|
TEST_PATH(ntStatus, FAILED_LOWER_START);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
//
|
|
// irp completed succesfully by lower
|
|
// drivers, start usbport and miniport
|
|
//
|
|
|
|
ntStatus =
|
|
USBPORT_DeferredStartDevice(
|
|
FdoDeviceObject,
|
|
Irp);
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
USBPORT_KdPrint((1, "'miniport failed start %x\n", ntStatus));
|
|
DEBUG_BREAK();
|
|
}
|
|
#endif
|
|
}
|
|
#if DBG
|
|
else {
|
|
USBPORT_KdPrint((1, "'lower drivers failed start %x\n", ntStatus));
|
|
DEBUG_BREAK();
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// we must complete this irp since we defrerred completion
|
|
// with the completion routine.
|
|
//
|
|
|
|
USBPORT_CompleteIrp(FdoDeviceObject,
|
|
Irp,
|
|
ntStatus,
|
|
0);
|
|
|
|
goto USBPORT_ProcessPnPIrp_Done;
|
|
|
|
}
|
|
break;
|
|
|
|
//
|
|
// STOP & REMOVE messages unload the driver
|
|
// when we get a STOP message it is still possible
|
|
// touch the hardware, when we get a REMOVE message
|
|
// we have to assume that the hardware is gone.
|
|
//
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
|
|
// check our state and take appropriate action
|
|
if (TEST_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STARTED)) {
|
|
// device is started, stop it now
|
|
ntStatus = USBPORT_StopDevice(FdoDeviceObject,
|
|
hardwarePresent);
|
|
|
|
// not started flag, note: not started is not the
|
|
// same as stopped
|
|
CLEAR_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STARTED);
|
|
SET_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STOPPED);
|
|
}
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
// bugbug what is our state if stop fails?
|
|
TEST_TRAP();
|
|
}
|
|
|
|
// PnP commandment: Thou shalt not fail stop.
|
|
Irp->IoStatus.Status =
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'STOP', 0,
|
|
devExt->PnpStateFlags, ntStatus);
|
|
// Pass on to PDO
|
|
break;
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
|
|
{
|
|
|
|
PDEVICE_RELATIONS deviceRelations;
|
|
|
|
USBPORT_KdPrint((1,
|
|
"'IRP_MN_QUERY_DEVICE_RELATIONS %x %x\n",
|
|
FdoDeviceObject,
|
|
irpStack->Parameters.QueryDeviceRelations.Type));
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
switch(irpStack->Parameters.QueryDeviceRelations.Type) {
|
|
case BusRelations:
|
|
|
|
// query relations.
|
|
// we report only one child, the root hub
|
|
|
|
// assume success
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
ALLOC_POOL_OSOWNED(deviceRelations,
|
|
PagedPool,
|
|
sizeof(*deviceRelations));
|
|
|
|
if (!deviceRelations) {
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
// Complete the Irp now with failure, don't pass it down.
|
|
//
|
|
USBPORT_CompleteIrp(FdoDeviceObject,
|
|
Irp,
|
|
ntStatus,
|
|
0);
|
|
|
|
goto USBPORT_ProcessPnPIrp_Done;
|
|
}
|
|
|
|
if (devExt->Fdo.RootHubPdo == NULL) {
|
|
// we either have not created it or the current one
|
|
// has been removed by the OS.
|
|
|
|
// create a new root hub
|
|
ntStatus =
|
|
USBPORT_CreateRootHubPdo(FdoDeviceObject,
|
|
&devExt->Fdo.RootHubPdo);
|
|
|
|
}
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
PDEVICE_EXTENSION rhDevExt;
|
|
KIRQL irql;
|
|
|
|
GET_DEVICE_EXT(rhDevExt, devExt->Fdo.RootHubPdo);
|
|
ASSERT_PDOEXT(rhDevExt);
|
|
|
|
deviceRelations->Count=1;
|
|
deviceRelations->Objects[0] =
|
|
devExt->Fdo.RootHubPdo;
|
|
ObReferenceObject(devExt->Fdo.RootHubPdo);
|
|
Irp->IoStatus.Information=(ULONG_PTR)deviceRelations;
|
|
|
|
// report the same PDO every time ie the PDO is never
|
|
// truely remove until the controller is removed
|
|
|
|
} else {
|
|
FREE_POOL(FdoDeviceObject, deviceRelations);
|
|
deviceRelations = NULL;
|
|
// free the device object if we
|
|
// created one
|
|
TEST_TRAP();
|
|
}
|
|
|
|
Irp->IoStatus.Status = ntStatus;
|
|
|
|
USBPORT_KdPrint((1,
|
|
"'IRP_MN_QUERY_DEVICE_RELATIONS %x BusRelations\n",
|
|
FdoDeviceObject));
|
|
|
|
break;
|
|
|
|
case TargetDeviceRelation:
|
|
|
|
//
|
|
// this one gets passed on
|
|
//
|
|
|
|
USBPORT_KdPrint((1,
|
|
" IRP_MN_QUERY_DEVICE_RELATIONS %x, TargetDeviceRelation\n",
|
|
FdoDeviceObject));
|
|
break;
|
|
|
|
case RemovalRelations:
|
|
|
|
// assume success
|
|
ntStatus = STATUS_SUCCESS;
|
|
deviceRelations = NULL;
|
|
|
|
if (USBPORT_IS_USB20(devExt)) {
|
|
|
|
deviceRelations =
|
|
USBPORT_FindCompanionControllers(FdoDeviceObject,
|
|
TRUE,
|
|
FALSE);
|
|
|
|
if (!deviceRelations) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
// Complete the Irp now with failure, don't pass it down.
|
|
//
|
|
USBPORT_CompleteIrp(FdoDeviceObject,
|
|
Irp,
|
|
ntStatus,
|
|
0);
|
|
|
|
goto USBPORT_ProcessPnPIrp_Done;
|
|
}
|
|
}
|
|
|
|
Irp->IoStatus.Information=(ULONG_PTR)deviceRelations;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
|
|
USBPORT_KdPrint((1,
|
|
"'IRP_MN_QUERY_DEVICE_RELATIONS %x RemovalRelations\n",
|
|
FdoDeviceObject));
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// some other kind of relations
|
|
// pass this on
|
|
//
|
|
USBPORT_KdPrint((1,
|
|
"'IRP_MN_QUERY_DEVICE_RELATIONS %x, other relations\n",
|
|
FdoDeviceObject));
|
|
|
|
|
|
} /* case irpStack->Parameters.QueryDeviceRelations.Type */
|
|
|
|
}
|
|
break; /* IRP_MN_QUERY_DEVICE_RELATIONS */
|
|
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
|
|
// hardware is gone
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'hcSR', 0, ntStatus, 0);
|
|
|
|
USBPORT_KdPrint((1, " HC FDO (%x) surprise removed\n",
|
|
FdoDeviceObject));
|
|
DEBUG_BREAK();
|
|
|
|
if (TEST_FLAG(devExt->PnpStateFlags, USBPORT_PNP_REMOVED)) {
|
|
|
|
// it would be odd to get a surprise remove when
|
|
// we are already removed but it would not 'surprise'
|
|
// me if Win2k did this under some cirumstance
|
|
TEST_TRAP();
|
|
|
|
ntStatus =
|
|
USBPORT_PassIrp(FdoDeviceObject,
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE,
|
|
Irp);
|
|
|
|
goto USBPORT_ProcessPnPIrp_Done;
|
|
|
|
}
|
|
|
|
// see if we have an interrupt
|
|
// if so disconnect it
|
|
// **
|
|
// DDK implies that that interrupt resources bust be
|
|
// freed on surprise remove and the PCI driver depends
|
|
// on this.
|
|
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_IRQ_CONNECTED)) {
|
|
|
|
// fortunately this cannot fail
|
|
IoDisconnectInterrupt(devExt->Fdo.InterruptObject);
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'IOCd', 0, 0, 0);
|
|
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_IRQ_CONNECTED);
|
|
}
|
|
|
|
USBPORT_InvalidateController(FdoDeviceObject,
|
|
UsbMpControllerRemoved);
|
|
|
|
break;
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
|
|
{
|
|
PDEVICE_OBJECT rootHubPdo;
|
|
KIRQL irql;
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'hcRM', 0, ntStatus, 0);
|
|
|
|
USBPORT_KdPrint((1, " HC FDO (%x) is being removed\n",
|
|
FdoDeviceObject));
|
|
|
|
// this device is now 'REMOVED'
|
|
|
|
KeAcquireSpinLock(&devExt->PendingRequestSpin.sl, &irql);
|
|
USBPORT_ASSERT(!TEST_FLAG(devExt->PnpStateFlags, USBPORT_PNP_REMOVED));
|
|
SET_FLAG(devExt->PnpStateFlags, USBPORT_PNP_REMOVED);
|
|
KeReleaseSpinLock(&devExt->PendingRequestSpin.sl, irql);
|
|
|
|
// if we are started AND
|
|
// we haven't been stopped yet then stop now.
|
|
if (TEST_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STARTED) &&
|
|
!TEST_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STOPPED)) {
|
|
NTSTATUS status;
|
|
|
|
status = USBPORT_StopDevice(FdoDeviceObject,
|
|
hardwarePresent);
|
|
|
|
SET_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STOPPED);
|
|
}
|
|
|
|
//
|
|
// pass on to our PDO
|
|
//
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
ntStatus =
|
|
USBPORT_PassIrp(FdoDeviceObject,
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE,
|
|
Irp);
|
|
|
|
// bugbug
|
|
// Flush any requests that are still queued in our driver
|
|
|
|
|
|
// This DEC matches the INC in our add device,
|
|
// this is our last reference and this will cause the
|
|
// transition 0 -> -1 when all irps pending complete
|
|
//
|
|
// after this wait we consider it safe to 'unload'
|
|
DECREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL);
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'watP', 0, 0, FdoDeviceObject);
|
|
KeWaitForSingleObject(&devExt->PendingRequestEvent,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'waPD', 0, 0, FdoDeviceObject);
|
|
|
|
// last chance to debug with the log
|
|
DEBUG_BREAK();
|
|
USBPORT_LogFree(FdoDeviceObject, &devExt->Log);
|
|
USBPORT_LogFree(FdoDeviceObject, &devExt->TransferLog);
|
|
USBPORT_LogFree(FdoDeviceObject, &devExt->EnumLog);
|
|
|
|
|
|
//
|
|
// important to detach FDO from PDO after we pass the irp on
|
|
//
|
|
|
|
IoDetachDevice(devExt->Fdo.TopOfStackDeviceObject);
|
|
|
|
//
|
|
// Delete the device object we created for this controller
|
|
//
|
|
|
|
rootHubPdo = devExt->Fdo.RootHubPdo;
|
|
SET_FLAG(devExt->PnpStateFlags, USBPORT_PNP_DELETED);
|
|
USBPORT_KdPrint((1, "'Deleting HC FDO (%x) now.\n",
|
|
FdoDeviceObject));
|
|
IoDeleteDevice(FdoDeviceObject);
|
|
|
|
// HC is FDO gone so root hub is gone.
|
|
//
|
|
// note: in some cases we may not have a root hub
|
|
// PDO since we create it in response to a QBR.
|
|
if (rootHubPdo != NULL) {
|
|
PDEVICE_EXTENSION rhDevExt;
|
|
|
|
GET_DEVICE_EXT(rhDevExt, rootHubPdo);
|
|
ASSERT_PDOEXT(rhDevExt);
|
|
|
|
SET_FLAG(rhDevExt->PnpStateFlags, USBPORT_PNP_DELETED);
|
|
USBPORT_KdPrint((1, "'Deleting root hub PDO (%x) now.\n",
|
|
rootHubPdo));
|
|
IoDeleteDevice(rootHubPdo);
|
|
}
|
|
|
|
goto USBPORT_ProcessPnPIrp_Done;
|
|
|
|
}
|
|
break;
|
|
|
|
// Quoting from the book of PNP
|
|
//
|
|
// 'The FDO must either fail the IRP or set the
|
|
// IRP's status if it is not going change the IRP's status
|
|
// using a completion routine.'
|
|
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
Irp->IoStatus.Status =
|
|
ntStatus = STATUS_SUCCESS;
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'cstp', 0,
|
|
devExt->PnpStateFlags, ntStatus);
|
|
break;
|
|
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
Irp->IoStatus.Status =
|
|
ntStatus = STATUS_SUCCESS;
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'qstp', 0,
|
|
devExt->PnpStateFlags, ntStatus);
|
|
break;
|
|
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
Irp->IoStatus.Status =
|
|
ntStatus = STATUS_SUCCESS;
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'crmv', 0,
|
|
devExt->PnpStateFlags, ntStatus);
|
|
break;
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
|
|
// BUGBUG reverse this in cance query remove?
|
|
if (USBPORT_IS_USB20(devExt)) {
|
|
// make a note on the CCs for this USB 2
|
|
// master controller
|
|
USBPORT_WriteHaction(FdoDeviceObject,
|
|
2);
|
|
}
|
|
|
|
Irp->IoStatus.Status =
|
|
ntStatus = STATUS_SUCCESS;
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'qrmv', 0,
|
|
devExt->PnpStateFlags, ntStatus);
|
|
break;
|
|
|
|
//
|
|
// All other PnP messages passed on to our PDO
|
|
//
|
|
|
|
default:
|
|
USBPORT_ASSERT(devExt->Fdo.TopOfStackDeviceObject != NULL);
|
|
USBPORT_KdPrint((2, "'UNKNOWN PNP MESSAGE (%x)\n", irpStack->MinorFunction));
|
|
|
|
//
|
|
// All unahndled PnP messages are passed on to the PDO
|
|
//
|
|
|
|
} /* case PNP minor function */
|
|
|
|
//
|
|
// pass on to our PDO
|
|
//
|
|
ntStatus =
|
|
USBPORT_PassIrp(FdoDeviceObject,
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE,
|
|
Irp);
|
|
|
|
USBPORT_ProcessPnPIrp_Done:
|
|
|
|
// DO NOT touch the Irp from this point on
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_DeferredStartDevice(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called as a result of MN_START_DEVICE,
|
|
it is called after successful completion of the START
|
|
irp by the lower drivers.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT Status code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDEVICE_EXTENSION devExt;
|
|
|
|
PAGED_CODE();
|
|
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
ntStatus = USBPORT_GetResources(FdoDeviceObject,
|
|
irpStack->Parameters.StartDevice.AllocatedResourcesTranslated,
|
|
&devExt->Fdo.HcResources);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
// got resources, start the port driver,
|
|
// connect the interrupt and start miniport.
|
|
ntStatus = USBPORT_StartDevice(FdoDeviceObject,
|
|
&devExt->Fdo.HcResources);
|
|
}
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
CLEAR_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STOPPED);
|
|
SET_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STARTED);
|
|
// consider ourselves powered
|
|
//
|
|
// Are we powered if we fail start?
|
|
// PnP sure thinks we are becuse the OS sends power
|
|
// irps. Since we handle this bogus case (ie have hit it)
|
|
// for the OS we just set ourselves to D0 here.
|
|
|
|
devExt->CurrentDevicePowerState = PowerDeviceD0;
|
|
|
|
if (USBPORT_IS_USB20(devExt)) {
|
|
USBPORT_RegisterUSB2fdo(FdoDeviceObject);
|
|
|
|
// for some reason we only do this fot XPSP1
|
|
// this is really only for the WU install
|
|
// if (USBPORT_IS_USB20(devExt)) {
|
|
// // set the default haction to wait (1) on
|
|
// // successful start
|
|
// USBPORT_WriteHaction(FdoDeviceObject,
|
|
// 1);
|
|
// }
|
|
|
|
|
|
} else {
|
|
USBPORT_RegisterUSB1fdo(FdoDeviceObject);
|
|
}
|
|
|
|
} else {
|
|
SET_FLAG(devExt->PnpStateFlags, USBPORT_PNP_START_FAILED);
|
|
}
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'dfST', 0, 0, ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
PWCHAR
|
|
USB_MakeId(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PWCHAR IdString,
|
|
PWCHAR Buffer,
|
|
PULONG Length,
|
|
USHORT NullCount,
|
|
USHORT Digits,
|
|
USHORT HexId
|
|
)
|
|
/*
|
|
given a wide Id string like "FOOnnnn\0"
|
|
add the HexId value to nnnn as hex
|
|
this string is appended to the buffer passed in
|
|
|
|
eg
|
|
in : FOOnnnn\0 , 0x123A
|
|
out : FOO123A\0
|
|
*/
|
|
{
|
|
#define NIBBLE_TO_HEX( byte ) ((WCHAR)Nibble[byte])
|
|
CONST UCHAR Nibble[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
|
|
'B', 'C', 'D', 'E', 'F'};
|
|
|
|
PWCHAR tmp, id;
|
|
PUCHAR p;
|
|
SIZE_T siz, idLen;
|
|
|
|
idLen = wcslen(IdString)*sizeof(WCHAR);
|
|
siz = idLen+(USHORT)*Length+(NullCount*sizeof(WCHAR));
|
|
|
|
ALLOC_POOL_OSOWNED(tmp, PagedPool, siz);
|
|
|
|
if (tmp == NULL) {
|
|
*Length = 0;
|
|
} else {
|
|
// this takes care of the nulls
|
|
RtlCopyMemory(tmp, Buffer, *Length);
|
|
p = (PUCHAR) tmp;
|
|
p += *Length;
|
|
RtlCopyMemory(p, IdString, idLen);
|
|
id = (PWCHAR) p;
|
|
*Length = siz;
|
|
|
|
// now convert the vaules
|
|
while (*id != (WCHAR)'n' && Digits) {
|
|
id++;
|
|
}
|
|
|
|
switch(Digits) {
|
|
case 2:
|
|
*(id) = NIBBLE_TO_HEX((HexId >> 4) & 0x000f);
|
|
*(id+1) = NIBBLE_TO_HEX(HexId & 0x000f);
|
|
break;
|
|
case 4:
|
|
*(id) = NIBBLE_TO_HEX(HexId >> 12);
|
|
*(id+1) = NIBBLE_TO_HEX((HexId >> 8) & 0x000f);
|
|
*(id+2) = NIBBLE_TO_HEX((HexId >> 4) & 0x000f);
|
|
*(id+3) = NIBBLE_TO_HEX(HexId & 0x000f);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Buffer != NULL) {
|
|
FREE_POOL(FdoDeviceObject, Buffer);
|
|
}
|
|
|
|
return tmp;
|
|
#undef NIBBLE_TO_HEX
|
|
}
|
|
|
|
|
|
|
|
PWCHAR
|
|
USBPORT_GetIdString(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
USHORT Vid,
|
|
USHORT Pid,
|
|
USHORT Rev
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Make an id string for PnP
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NT Status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWCHAR id;
|
|
ULONG length;
|
|
PDEVICE_EXTENSION devExt;
|
|
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
// we need to generate the following series of strings
|
|
|
|
// USB\\ROOT_HUB&VIDnnnn&PIDnnnn&REVnnnn\0
|
|
// USB\\ROOT_HUB&VIDnnnn&PIDnnnn\0
|
|
// USB\\ROOT_HUB\0\0
|
|
|
|
|
|
// allocate space for the three id plus a NULL
|
|
id = NULL;
|
|
length = 0;
|
|
|
|
// USB\\ROOT_HUB&VIDnnnn&PIDnnnn&REVnnnn\0
|
|
if (USBPORT_IS_USB20(devExt)) {
|
|
id = USB_MakeId(FdoDeviceObject,
|
|
L"USB\\ROOT_HUB20&VIDnnnn\0",
|
|
id,
|
|
&length,
|
|
0,
|
|
4, // four digits
|
|
Vid);
|
|
} else {
|
|
id = USB_MakeId(FdoDeviceObject,
|
|
L"USB\\ROOT_HUB&VIDnnnn\0",
|
|
id,
|
|
&length,
|
|
0,
|
|
4, // four digits
|
|
Vid);
|
|
}
|
|
|
|
id = USB_MakeId(FdoDeviceObject,
|
|
L"&PIDnnnn\0",
|
|
id,
|
|
&length,
|
|
0,
|
|
4, // four digits
|
|
Pid);
|
|
|
|
id = USB_MakeId(FdoDeviceObject,
|
|
L"&REVnnnn\0",
|
|
id,
|
|
&length,
|
|
1, // add a NULL
|
|
4, // four digits
|
|
Rev);
|
|
|
|
// USB\\ROOT_HUB&VIDnnnn&PIDnnnn\0
|
|
if (USBPORT_IS_USB20(devExt)) {
|
|
id = USB_MakeId(FdoDeviceObject,
|
|
L"USB\\ROOT_HUB20&VIDnnnn\0",
|
|
id,
|
|
&length,
|
|
0,
|
|
4, // four digits
|
|
Vid);
|
|
} else {
|
|
id = USB_MakeId(FdoDeviceObject,
|
|
L"USB\\ROOT_HUB&VIDnnnn\0",
|
|
id,
|
|
&length,
|
|
0,
|
|
4, // four digits
|
|
Vid);
|
|
}
|
|
|
|
id = USB_MakeId(FdoDeviceObject,
|
|
L"&PIDnnnn\0",
|
|
id,
|
|
&length,
|
|
1,
|
|
4, // four digits
|
|
Pid);
|
|
|
|
// USB\\ROOT_HUB\0\0
|
|
if (USBPORT_IS_USB20(devExt)) {
|
|
id = USB_MakeId(FdoDeviceObject,
|
|
L"USB\\ROOT_HUB20\0",
|
|
id,
|
|
&length,
|
|
2, // double null
|
|
0, // no digits
|
|
0);
|
|
} else {
|
|
id = USB_MakeId(FdoDeviceObject,
|
|
L"USB\\ROOT_HUB\0",
|
|
id,
|
|
&length,
|
|
2, // double null
|
|
0, // no digits
|
|
0);
|
|
}
|
|
|
|
return(id);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_PdoPnPIrp(
|
|
PDEVICE_OBJECT PdoDeviceObject,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Disptach routine for PnP Irps sent to the PDO for the root hub.
|
|
|
|
NOTE:
|
|
irps sent to the PDO are always completed by the bus driver
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pdo for the root hub
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDEVICE_CAPABILITIES DeviceCapabilities;
|
|
NTSTATUS ntStatus;
|
|
PDEVICE_EXTENSION rhDevExt;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
// return no infornation by default
|
|
ULONG_PTR information;
|
|
|
|
GET_DEVICE_EXT(rhDevExt, PdoDeviceObject);
|
|
ASSERT_PDOEXT(rhDevExt);
|
|
|
|
fdoDeviceObject = rhDevExt->HcFdoDeviceObject;
|
|
//GET_DEVICE_EXT(devExt, fdoDeviceObject);
|
|
//ASSERT_FDOEXT(devExt);
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
// don't stomp the current value unless we
|
|
// have to.
|
|
information = Irp->IoStatus.Information;
|
|
|
|
USBPORT_ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);
|
|
|
|
// PNP messages for the PDO created for the root hub
|
|
|
|
switch (irpStack->MinorFunction) {
|
|
case IRP_MN_START_DEVICE:
|
|
{
|
|
KIRQL irql;
|
|
|
|
USBPORT_KdPrint((1, " Starting Root hub PDO %x\n",
|
|
PdoDeviceObject));
|
|
DEBUG_BREAK();
|
|
INCREMENT_PENDING_REQUEST_COUNT(PdoDeviceObject, NULL);
|
|
|
|
// first create the 'Device'
|
|
ntStatus = USBPORT_RootHub_CreateDevice(fdoDeviceObject,
|
|
PdoDeviceObject);
|
|
|
|
//
|
|
// create a symbolic link for the root hub PDO
|
|
// USBUI uses this link to talk to the hub
|
|
//
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
ntStatus = USBPORT_SymbolicLink(TRUE,
|
|
rhDevExt,
|
|
PdoDeviceObject,
|
|
(LPGUID)&GUID_CLASS_USBHUB);
|
|
}
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
// erases remove and stop flags
|
|
rhDevExt->PnpStateFlags = USBPORT_PNP_STARTED;
|
|
// consider ourselves powered when started
|
|
rhDevExt->CurrentDevicePowerState = PowerDeviceD0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
KIRQL irql;
|
|
|
|
USBPORT_KdPrint((1, " Root Hub PDO (%x) is being removed\n",
|
|
PdoDeviceObject));
|
|
|
|
LOGENTRY(NULL, fdoDeviceObject, LOG_PNP, 'rhRM', 0, 0, 0);
|
|
|
|
GET_DEVICE_EXT(devExt, rhDevExt->HcFdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
// stop if necessary
|
|
USBPORT_StopRootHubPdo(fdoDeviceObject,
|
|
PdoDeviceObject);
|
|
|
|
|
|
// when is a remove not a remove? when PnP sends it.
|
|
// this flag will be reset when the root hub pdo is
|
|
// started
|
|
SET_FLAG(rhDevExt->PnpStateFlags, USBPORT_PNP_REMOVED);
|
|
|
|
|
|
// since the PnP convention is for the PDO to exist
|
|
// as long as the physical device exists we do not
|
|
// delete the root hub PDO until the controller is
|
|
// removed.
|
|
|
|
// we will call this off just to gixe us a defined state
|
|
rhDevExt->CurrentDevicePowerState = PowerDeviceD3;
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
|
|
// note: since OS PnP will STOP things that are not STARTED
|
|
// we maintain two separate flags for this.
|
|
//
|
|
// the state machine looks like this:
|
|
//
|
|
//
|
|
// / Started \
|
|
// stop = = stopped
|
|
// \ Not Started /
|
|
|
|
|
|
USBPORT_KdPrint((1, " Root Hub PDO %x is being stopped\n",
|
|
PdoDeviceObject));
|
|
|
|
USBPORT_StopRootHubPdo(fdoDeviceObject,
|
|
PdoDeviceObject);
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_QUERY_CAPABILITIES:
|
|
|
|
//
|
|
// Handle query caps for the root hub PDO
|
|
//
|
|
|
|
USBPORT_KdPrint((1, "'IRP_MN_QUERY_CAPABILITIES (rh PDO)\n"));
|
|
|
|
//
|
|
// Get the packet.
|
|
//
|
|
DeviceCapabilities =
|
|
irpStack->Parameters.DeviceCapabilities.Capabilities;
|
|
|
|
//
|
|
// The power state capabilities for the root
|
|
// hub are based on those of the host controller.
|
|
//
|
|
// We then modify them based on the power rules of
|
|
// USB
|
|
//
|
|
|
|
RtlCopyMemory(DeviceCapabilities,
|
|
&rhDevExt->DeviceCapabilities,
|
|
sizeof(*DeviceCapabilities));
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case IRP_MN_QUERY_ID:
|
|
|
|
USBPORT_KdPrint((3, "'IOCTL_BUS_QUERY_ID\n"));
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
switch (irpStack->Parameters.QueryId.IdType) {
|
|
|
|
case BusQueryDeviceID:
|
|
|
|
// return the 'generic' root hub ID
|
|
{
|
|
PWCHAR deviceId;
|
|
WCHAR rootHubDeviceId[] = L"USB\\ROOT_HUB\0";
|
|
WCHAR rootHubDeviceId_20[] = L"USB\\ROOT_HUB20\0";
|
|
PWCHAR id;
|
|
ULONG siz;
|
|
PDEVICE_EXTENSION devExt;
|
|
|
|
GET_DEVICE_EXT(devExt, fdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
id = &rootHubDeviceId[0];
|
|
siz = sizeof(rootHubDeviceId);
|
|
if (USBPORT_IS_USB20(devExt)) {
|
|
id = &rootHubDeviceId_20[0];
|
|
siz = sizeof(rootHubDeviceId_20);
|
|
}
|
|
|
|
ALLOC_POOL_OSOWNED(deviceId,
|
|
PagedPool,
|
|
siz);
|
|
|
|
if (deviceId) {
|
|
RtlCopyMemory(deviceId,
|
|
id,
|
|
siz);
|
|
}
|
|
// device id for root hub is USB\ROOT_HUB
|
|
information = (ULONG_PTR) deviceId;
|
|
}
|
|
LOGENTRY(NULL, fdoDeviceObject, LOG_PNP, 'DVid', information, 0, 0);
|
|
|
|
break;
|
|
|
|
case BusQueryHardwareIDs:
|
|
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
|
|
//
|
|
// generate hardware id for root hub
|
|
//
|
|
// A host controllers root hub VID,PID,REV is derived
|
|
// from the controllers PCI VID,DEV,REV that is:
|
|
// root hub VID = hc VID (vendor id)
|
|
// root hub PID = hc DEV (device id)
|
|
// root hub REV = hc REV (revision id)
|
|
//
|
|
// this allows filter drivers to be loaded on
|
|
// specific root hub instances.
|
|
|
|
// for HW IDs we generate:
|
|
// USB\PORT_ROOT_HUB&VIDnnnn&PIDnnnn&REVnnnn
|
|
// USB\PORT_ROOT_HUB&VIDnnnn&PIDnnnn
|
|
// USB\PORT_ROOT_HUB
|
|
//
|
|
GET_DEVICE_EXT(devExt, fdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
information =
|
|
(ULONG_PTR) USBPORT_GetIdString(
|
|
fdoDeviceObject,
|
|
devExt->Fdo.PciVendorId,
|
|
devExt->Fdo.PciDeviceId,
|
|
(USHORT) devExt->Fdo.PciRevisionId);
|
|
|
|
LOGENTRY(NULL, fdoDeviceObject, LOG_PNP, 'HWid', information, 0, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BusQueryCompatibleIDs:
|
|
information = 0;
|
|
break;
|
|
|
|
case BusQueryInstanceID:
|
|
//
|
|
// The root HUB is instanced solely by the controller's id.
|
|
// Hence the UniqueDeviceId above.
|
|
//
|
|
information = 0;
|
|
break;
|
|
|
|
default:
|
|
ntStatus = Irp->IoStatus.Status;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_QUERY_BUS_INFORMATION:
|
|
{
|
|
// return the standard USB GUID
|
|
PPNP_BUS_INFORMATION busInfo;
|
|
|
|
ALLOC_POOL_OSOWNED(busInfo, PagedPool,
|
|
sizeof(PNP_BUS_INFORMATION));
|
|
|
|
if (busInfo == NULL) {
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
} else {
|
|
busInfo->BusTypeGuid = GUID_BUS_TYPE_USB;
|
|
busInfo->LegacyBusType = PNPBus;
|
|
busInfo->BusNumber = 0;
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
information = (ULONG_PTR) busInfo;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
|
|
USBPORT_KdPrint((1," IRP_MN_QUERY_DEVICE_RELATIONS (PDO) %x %x\n",
|
|
PdoDeviceObject,
|
|
irpStack->Parameters.QueryDeviceRelations.Type));
|
|
|
|
if (irpStack->Parameters.QueryDeviceRelations.Type ==
|
|
TargetDeviceRelation) {
|
|
|
|
PDEVICE_RELATIONS deviceRelations = NULL;
|
|
|
|
ALLOC_POOL_OSOWNED(deviceRelations, PagedPool, sizeof(*deviceRelations));
|
|
|
|
if (deviceRelations == NULL) {
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
} else {
|
|
// return a reference to ourselves
|
|
deviceRelations->Count = 1;
|
|
ObReferenceObject(PdoDeviceObject);
|
|
deviceRelations->Objects[0] =
|
|
PdoDeviceObject;
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
USBPORT_KdPrint((1, " TargetDeviceRelation to Root Hub PDO - complt\n"));
|
|
|
|
information = (ULONG_PTR) deviceRelations;
|
|
|
|
} else {
|
|
ntStatus = Irp->IoStatus.Status;
|
|
information = Irp->IoStatus.Information;
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_QUERY_INTERFACE:
|
|
|
|
USBPORT_KdPrint((1," IRP_MN_QUERY_INTERFACE (PDO) %x\n",
|
|
PdoDeviceObject));
|
|
|
|
ntStatus =
|
|
USBPORT_GetBusInterface(fdoDeviceObject,
|
|
PdoDeviceObject,
|
|
Irp);
|
|
|
|
break;
|
|
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
|
|
USBPORT_KdPrint((1," IRP_MN_SURPRISE_REMOVAL (PDO) %x\n",
|
|
PdoDeviceObject));
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// default behavior for an unhandled PnP irp is to return the
|
|
// status currently in the irp
|
|
|
|
USBPORT_KdPrint((1, " PnP IOCTL(%d) to root hub PDO not handled\n",
|
|
irpStack->MinorFunction));
|
|
|
|
ntStatus = Irp->IoStatus.Status;
|
|
|
|
} /* switch, PNP minor function */
|
|
|
|
USBPORT_CompleteIrp(PdoDeviceObject,
|
|
Irp,
|
|
ntStatus,
|
|
information);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_CreateRootHubPdo(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PDEVICE_OBJECT *RootHubPdo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempt to create the root hub
|
|
|
|
Arguments:
|
|
|
|
*RootHubPdo set to NULL if unsuccessful
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
ULONG index = 0;
|
|
UNICODE_STRING rootHubPdoUnicodeString;
|
|
PDEVICE_EXTENSION rhDevExt, devExt;
|
|
PDEVICE_OBJECT deviceObject = NULL;
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
// those who wear priestly robes say we must do this
|
|
|
|
do {
|
|
ntStatus =
|
|
USBPORT_MakeRootHubPdoName(FdoDeviceObject,
|
|
&rootHubPdoUnicodeString,
|
|
index);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
ntStatus =
|
|
IoCreateDevice(devExt->Fdo.MiniportDriver->DriverObject,
|
|
sizeof(DEVICE_EXTENSION),
|
|
&rootHubPdoUnicodeString,
|
|
FILE_DEVICE_BUS_EXTENDER,
|
|
0,
|
|
FALSE,
|
|
&deviceObject);
|
|
|
|
index++;
|
|
|
|
// delete the usbicode string we used for the
|
|
// device name -- we don't need it anymore
|
|
RtlFreeUnicodeString(&rootHubPdoUnicodeString);
|
|
}
|
|
|
|
} while (ntStatus == STATUS_OBJECT_NAME_COLLISION);
|
|
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
if (deviceObject != NULL) {
|
|
|
|
rhDevExt = deviceObject->DeviceExtension;
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'rPDO', deviceObject, rhDevExt, 0);
|
|
|
|
rhDevExt->DummyUsbdExtension = USBPORT_DummyUsbdExtension;
|
|
rhDevExt->Sig = ROOTHUB_DEVICE_EXT_SIG;
|
|
|
|
INITIALIZE_PENDING_REQUEST_COUNTER(rhDevExt);
|
|
|
|
// transition to -1 means we have no pending requests
|
|
INCREMENT_PENDING_REQUEST_COUNT(deviceObject, NULL);
|
|
|
|
// point to our creator
|
|
rhDevExt->HcFdoDeviceObject = FdoDeviceObject;
|
|
|
|
// initialize root hub extension
|
|
USBPORT_ComputeRootHubDeviceCaps(FdoDeviceObject,
|
|
deviceObject);
|
|
|
|
// initialize object
|
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
deviceObject->Flags |= DO_POWER_PAGABLE;
|
|
deviceObject->StackSize = FdoDeviceObject->StackSize;
|
|
|
|
} else {
|
|
TEST_TRAP();
|
|
// sucess but no devobj?
|
|
// we will return an error
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
*RootHubPdo = deviceObject;
|
|
} else {
|
|
*RootHubPdo = NULL;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_CreatePortFdoSymbolicLink(
|
|
PDEVICE_OBJECT FdoDeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempt to create a symbolic link for the HC. We use the
|
|
PnP APIs to generate a name based on the USBPORT Host
|
|
Controller Class GUID defined in USB.H
|
|
|
|
Arguments:
|
|
|
|
*RootHubPdo set to NULL if unsuccessful
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
|
|
ntStatus = USBPORT_SymbolicLink(TRUE,
|
|
devExt,
|
|
devExt->Fdo.PhysicalDeviceObject,
|
|
(LPGUID)&GUID_CLASS_USB_HOST_CONTROLLER);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
USBPORT_StopRootHubPdo(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PDEVICE_OBJECT PdoDeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempt to STOP the root hub
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION rhDevExt, devExt;
|
|
|
|
GET_DEVICE_EXT(rhDevExt, PdoDeviceObject);
|
|
ASSERT_PDOEXT(rhDevExt);
|
|
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
// disable the root hub notification interrupt
|
|
// we won't need it while we are stopped
|
|
MPRH_DisableIrq(devExt);
|
|
|
|
// at this point no new notifications can come in for
|
|
// the root hub
|
|
|
|
// remove any start callback notifications
|
|
rhDevExt->Pdo.HubInitCallback = NULL;
|
|
rhDevExt->Pdo.HubInitContext = NULL;
|
|
|
|
// remove the root hub 'device' the root hub PDO
|
|
// will remain
|
|
|
|
if (TEST_FLAG(rhDevExt->PnpStateFlags, USBPORT_PNP_STARTED)) {
|
|
USBPORT_RootHub_RemoveDevice(FdoDeviceObject,
|
|
PdoDeviceObject);
|
|
|
|
// stopped = NOT started
|
|
CLEAR_FLAG(rhDevExt->PnpStateFlags, USBPORT_PNP_STARTED);
|
|
}
|
|
|
|
if (TEST_FLAG(rhDevExt->Flags, USBPORT_FLAG_SYM_LINK)) {
|
|
USBPORT_SymbolicLink(FALSE,
|
|
rhDevExt,
|
|
PdoDeviceObject,
|
|
(LPGUID)&GUID_CLASS_USBHUB);
|
|
}
|
|
|
|
SET_FLAG(rhDevExt->PnpStateFlags, USBPORT_PNP_STOPPED);
|
|
|
|
// resume the controller if it is 'suspended'
|
|
USBPORT_ResumeController(FdoDeviceObject);
|
|
|
|
}
|
|
|
|
/*
|
|
Registry Key cache for miniports. Since the miniports cannot read
|
|
the registry from the another thread other than the PNP thread we cache
|
|
the reg values read from PNP start.
|
|
|
|
Miniports re-read the registry on a re-start.
|
|
|
|
*/
|
|
|
|
PUSBPORT_REG_CACHE_ENTRY
|
|
USBPORT_GetCahceEntry(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
BOOLEAN SoftwareBranch,
|
|
PWCHAR KeyNameString,
|
|
ULONG KeyNameStringLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fetches a registry key value from the cache if there
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
returns cached entry or NULL if not found
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY listEntry;
|
|
PDEVICE_EXTENSION devExt;
|
|
PUSBPORT_REG_CACHE_ENTRY regEntry;
|
|
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
// walk the list
|
|
GET_HEAD_LIST(devExt->Fdo.RegistryCache, listEntry);
|
|
|
|
while (listEntry &&
|
|
listEntry != &devExt->Fdo.RegistryCache) {
|
|
|
|
regEntry = (PUSBPORT_REG_CACHE_ENTRY) CONTAINING_RECORD(
|
|
listEntry,
|
|
struct _USBPORT_REG_CACHE_ENTRY,
|
|
RegLink);
|
|
|
|
ASSERT_REG_CACHE(regEntry);
|
|
|
|
if (KeyNameStringLength == regEntry->KeyNameStringLength &&
|
|
SoftwareBranch == regEntry->SoftwareBranch &&
|
|
RtlCompareMemory(regEntry->KeyNameString,
|
|
KeyNameString,
|
|
KeyNameStringLength)) {
|
|
USBPORT_KdPrint((1, " reg entry found in cache\n"));
|
|
return regEntry;
|
|
}
|
|
|
|
listEntry = regEntry->RegLink.Flink;
|
|
}
|
|
|
|
USBPORT_KdPrint((1, " reg entry not in cache\n"));
|
|
return NULL;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_AddCahcedRegistryKey(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
BOOLEAN SoftwareBranch,
|
|
PWCHAR KeyNameString,
|
|
ULONG KeyNameStringLength,
|
|
PVOID Data,
|
|
ULONG DataLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a reg key value to the cache
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
returns STATUS_SUCCESS if the value was added to our cache
|
|
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
PUSBPORT_REG_CACHE_ENTRY regEntry;
|
|
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
|
|
ALLOC_POOL_Z(regEntry, PagedPool,
|
|
sizeof(*regEntry)+KeyNameStringLength);
|
|
|
|
if (regEntry != NULL) {
|
|
ALLOC_POOL_Z(regEntry->Data, PagedPool, DataLength);
|
|
if (regEntry->Data != NULL) {
|
|
regEntry->Sig = SIG_REG_CACHE;
|
|
regEntry->SoftwareBranch = SoftwareBranch;
|
|
regEntry->KeyNameStringLength;
|
|
regEntry->DataLength = DataLength;
|
|
RtlCopyMemory(regEntry->Data, (PUCHAR)Data, DataLength);
|
|
RtlCopyMemory(®Entry->KeyNameString[0], KeyNameString,
|
|
KeyNameStringLength);
|
|
InsertTailList(&devExt->Fdo.RegistryCache, ®Entry->RegLink);
|
|
|
|
USBPORT_KdPrint((1, " adding cache reg entry %x\n", regEntry));
|
|
|
|
return STATUS_SUCCESS;
|
|
} else {
|
|
FREE_POOL(FdoDeviceObject, regEntry);
|
|
}
|
|
}
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
USBPORT_GetCachedRegistryKeyValueForPdo(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PDEVICE_OBJECT PhysicalDeviceObject,
|
|
BOOLEAN SoftwareBranch,
|
|
PWCHAR KeyNameString,
|
|
ULONG KeyNameStringLength,
|
|
PVOID Data,
|
|
ULONG DataLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fetches a registry key value from the cache since we cannot read
|
|
the registry on a thread other than the PNP,POWER thread
|
|
|
|
We cache entries between PNP start and STOP
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
returns STATUS_SUCCESS if the value is found in the cache
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
PUSBPORT_REG_CACHE_ENTRY regEntry;
|
|
NTSTATUS ntStatus;
|
|
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
USBPORT_KdPrint((1, " USBPORT_GetCahcedRegistryKeyValueForPDO\n"));
|
|
|
|
// read from the registry if we can
|
|
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_ON_PNP_THREAD)) {
|
|
|
|
ntStatus = USBPORT_GetRegistryKeyValueForPdo(FdoDeviceObject,
|
|
PhysicalDeviceObject,
|
|
SoftwareBranch,
|
|
KeyNameString,
|
|
KeyNameStringLength,
|
|
Data,
|
|
DataLength);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
// cache it, if this fails we just return the result
|
|
// of the read
|
|
USBPORT_AddCahcedRegistryKey(
|
|
FdoDeviceObject,
|
|
SoftwareBranch,
|
|
KeyNameString,
|
|
KeyNameStringLength,
|
|
Data,
|
|
DataLength);
|
|
}
|
|
|
|
return ntStatus;
|
|
|
|
}
|
|
|
|
// just read from the cache
|
|
|
|
regEntry = USBPORT_GetCahceEntry(FdoDeviceObject,
|
|
SoftwareBranch,
|
|
KeyNameString,
|
|
KeyNameStringLength);
|
|
if (regEntry != NULL) {
|
|
if (regEntry->DataLength <= DataLength) {
|
|
RtlCopyMemory(Data, regEntry->Data, regEntry->DataLength);
|
|
ntStatus = STATUS_SUCCESS;
|
|
TEST_TRAP();
|
|
} else {
|
|
ntStatus = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
} else {
|
|
ntStatus = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
return ntStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
USBPORT_FlushCahcedRegistryKeys(
|
|
PDEVICE_OBJECT FdoDeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Flushes cache. Removes all cached registry keys.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
PUSBPORT_REG_CACHE_ENTRY regEntry;
|
|
PLIST_ENTRY listEntry;
|
|
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
while (!IsListEmpty(&devExt->Fdo.RegistryCache)) {
|
|
listEntry = RemoveHeadList(&devExt->Fdo.RegistryCache);
|
|
|
|
regEntry = (PUSBPORT_REG_CACHE_ENTRY) CONTAINING_RECORD(
|
|
listEntry,
|
|
struct _USBPORT_REG_CACHE_ENTRY,
|
|
RegLink);
|
|
|
|
ASSERT_REG_CACHE(regEntry);
|
|
|
|
FREE_POOL(FdoDeviceObject, regEntry->Data);
|
|
FREE_POOL(FdoDeviceObject, regEntry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|