windows-nt/Source/XPSP1/NT/drivers/wdm/usb/driver/hub.pnp/parent.c
2020-09-26 16:20:57 +08:00

3501 lines
104 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
PARENT.C
Abstract:
This module contains code that manages composite devices on USB.
Author:
jdunn
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include <wdm.h>
#ifdef WMI_SUPPORT
#include <wmilib.h>
#endif /* WMI_SUPPORT */
#include <wdmguid.h>
#include "usbhub.h"
#define COMP_RESET_TIMEOUT 3000 // Timeout in ms (3 sec)
#ifdef PAGE_CODE
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, USBH_ParentFdoStopDevice)
#pragma alloc_text(PAGE, USBH_ParentFdoRemoveDevice)
#pragma alloc_text(PAGE, UsbhParentFdoCleanup)
#pragma alloc_text(PAGE, USBH_ParentQueryBusRelations)
#pragma alloc_text(PAGE, USBH_ParentFdoStartDevice)
#pragma alloc_text(PAGE, USBH_FunctionPdoQueryId)
#pragma alloc_text(PAGE, USBH_FunctionPdoQueryDeviceText)
#pragma alloc_text(PAGE, USBH_FunctionPdoPnP)
#pragma alloc_text(PAGE, USBH_ParentCreateFunctionList)
#endif
#endif
VOID
UsbhParentFdoCleanup(
IN PDEVICE_EXTENSION_PARENT DeviceExtensionParent
)
/* ++
*
* Description:
*
* This routine is called to shut down the hub.
*
* Argument:
*
* Return:
*
* STATUS_SUCCESS
*
* -- */
{
PDEVICE_OBJECT deviceObject;
PSINGLE_LIST_ENTRY listEntry;
ULONG i;
PDEVICE_EXTENSION_FUNCTION deviceExtensionFunction;
KIRQL irql;
PIRP wWIrp;
USBH_KdPrint((2,"'UsbhParentFdoCleanup Fdo extension %x\n",
DeviceExtensionParent));
LOGENTRY(LOG_PNP, "pfdc", DeviceExtensionParent,
DeviceExtensionParent->PendingWakeIrp,
0);
//
// dump our wake request
//
IoAcquireCancelSpinLock(&irql);
if (DeviceExtensionParent->PendingWakeIrp) {
USBH_ASSERT(DeviceExtensionParent->ParentFlags & HUBFLAG_PENDING_WAKE_IRP);
wWIrp = DeviceExtensionParent->PendingWakeIrp;
IoSetCancelRoutine(wWIrp, NULL);
DeviceExtensionParent->PendingWakeIrp = NULL;
IoReleaseCancelSpinLock(irql);
IoCancelIrp(wWIrp);
} else {
IoReleaseCancelSpinLock(irql);
}
if (DeviceExtensionParent->ConfigurationDescriptor) {
UsbhExFreePool(DeviceExtensionParent->ConfigurationDescriptor);
DeviceExtensionParent->ConfigurationDescriptor = NULL;
}
USBH_ParentCompleteFunctionWakeIrps (DeviceExtensionParent,
STATUS_DELETE_PENDING);
do {
listEntry = PopEntryList(&DeviceExtensionParent->FunctionList);
LOGENTRY(LOG_PNP, "dFU1", 0, listEntry, 0);
if (listEntry != NULL) {
deviceExtensionFunction =
CONTAINING_RECORD(listEntry,
DEVICE_EXTENSION_FUNCTION,
ListEntry);
ASSERT_FUNCTION(deviceExtensionFunction);
LOGENTRY(LOG_PNP, "dFUN", deviceExtensionFunction, 0, 0);
for (i=0; i< deviceExtensionFunction->InterfaceCount; i++) {
LOGENTRY(LOG_PNP, "dFUi", deviceExtensionFunction,
deviceExtensionFunction->FunctionInterfaceList[i].InterfaceInformation,
0);
UsbhExFreePool(deviceExtensionFunction->FunctionInterfaceList[i].InterfaceInformation);
}
//
// Sometimes the FunctionPhysicalDeviceObject == deviceExtensionFunction.
// In other words the device object about to be deleted is the
// same one being used. So do not use the extions after it has been
// deleted.
//
deviceObject = deviceExtensionFunction->FunctionPhysicalDeviceObject;
deviceExtensionFunction->FunctionPhysicalDeviceObject = NULL;
LOGENTRY(LOG_PNP, "dFUo", deviceExtensionFunction,
deviceObject,
0);
IoDeleteDevice(deviceObject);
}
} while (listEntry != NULL);
DeviceExtensionParent->NeedCleanup = FALSE;
USBH_KdPrint((2,"'UsbhParentFdoCleanup done Fdo extension %x\n",
DeviceExtensionParent));
return;
}
NTSTATUS
USBH_ParentFdoRemoveDevice(
IN PDEVICE_EXTENSION_PARENT DeviceExtensionParent,
IN PIRP Irp
)
/* ++
*
* Description:
*
* Argument:
*
* Return:
*
* STATUS_SUCCESS
*
* -- */
{
PDEVICE_OBJECT deviceObject;
NTSTATUS ntStatus;
PAGED_CODE();
deviceObject = DeviceExtensionParent->FunctionalDeviceObject;
USBH_KdPrint((2,"'ParentFdoRemoveDevice Fdo %x\n", deviceObject));
DeviceExtensionParent->ParentFlags |= HUBFLAG_DEVICE_STOPPING;
//
// see if we need cleanup
//
if (DeviceExtensionParent->NeedCleanup) {
UsbhParentFdoCleanup(DeviceExtensionParent);
}
#ifdef WMI_SUPPORT
// de-register with WMI
IoWMIRegistrationControl(deviceObject,
WMIREG_ACTION_DEREGISTER);
#endif
//
// And we need to pass this message on to lower level driver
//
// IrpAssert: Set IRP status before passing on.
Irp->IoStatus.Status = STATUS_SUCCESS;
ntStatus = USBH_PassIrp(Irp, DeviceExtensionParent->TopOfStackDeviceObject);
//
// Detach FDO from PDO
//
IoDetachDevice(DeviceExtensionParent->TopOfStackDeviceObject);
// delete FDO of the parent
IoDeleteDevice(deviceObject);
return ntStatus;
}
NTSTATUS
USBH_ParentCreateFunctionList(
IN PDEVICE_EXTENSION_PARENT DeviceExtensionParent,
IN PUSBD_INTERFACE_LIST_ENTRY InterfaceList,
IN PURB Urb
)
/* ++
*
* Description:
*
* Argument:
*
* Return:
*
* STATUS_SUCCESS
*
* -- */
{
PDEVICE_OBJECT deviceObject;
PUSBD_INTERFACE_LIST_ENTRY interfaceList, tmp, baseInterface;
PDEVICE_EXTENSION_FUNCTION deviceExtensionFunction;
ULONG nameIndex = 0, numberOfInterfacesThisFunction, k;
NTSTATUS ntStatus = STATUS_SUCCESS;
PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor;
UNICODE_STRING uniqueIdUnicodeString;
PAGED_CODE();
DeviceExtensionParent->FunctionCount = 0;
tmp = interfaceList = InterfaceList;
DeviceExtensionParent->FunctionList.Next = NULL;
configurationDescriptor = DeviceExtensionParent->ConfigurationDescriptor;
for (;;) {
nameIndex = 0;
if (interfaceList->InterfaceDescriptor) {
//
// interfaceList contains all the interfaces on the device
// in sequential order.
//
//
// We will create nodes based on the following criteria:
//
// For each interface create one function (node)
//
//
// For Each group of class/subclass interface create one
// node iff the class is audio
//
// This means:
// **
// Class=1
// subclass=0
// Class=1
// subclass=0
// creates 2 nodes
//
// ** we will only do this for the audio class
// **
// Class=1
// subclass=0
// Class=2
// subclass=1
// creates 2 nodes
//
// **
// Class=1
// subclass=0
// Class=1
// subclass=1
// creates 1 node
//
// Create the node to represent this device
//
do {
if (NT_SUCCESS(ntStatus)) {
ntStatus = IoCreateDevice(UsbhDriverObject, // Driver Object
sizeof(DEVICE_EXTENSION_FUNCTION), // Device Extension size
NULL, // Device name
FILE_DEVICE_UNKNOWN, // Device Type
// should look device
// class
FILE_AUTOGENERATED_DEVICE_NAME,// Device Chars
FALSE, // Exclusive
&deviceObject); // Bus Device Object
}
nameIndex++;
} while (ntStatus == STATUS_OBJECT_NAME_COLLISION);
if (!NT_SUCCESS(ntStatus)) {
USBH_KdTrap(("IoCreateDevice for function fail\n"));
USBH_ASSERT(deviceObject == NULL);
deviceExtensionFunction = NULL;
// bail on whole node
break;
}
deviceObject->StackSize =
DeviceExtensionParent->TopOfStackDeviceObject->StackSize + 1;
USBH_KdPrint((2,"'CreateFunctionPdo StackSize=%d\n", deviceObject->StackSize));
deviceExtensionFunction =
(PDEVICE_EXTENSION_FUNCTION) deviceObject->DeviceExtension;
RtlFillMemory(deviceExtensionFunction,
sizeof(PDEVICE_EXTENSION_FUNCTION),
0);
//
// initialize this function extension
//
deviceExtensionFunction->ConfigurationHandle =
Urb->UrbSelectConfiguration.ConfigurationHandle;
deviceExtensionFunction->FunctionPhysicalDeviceObject =
deviceObject;
deviceExtensionFunction->ExtensionType =
EXTENSION_TYPE_FUNCTION;
deviceExtensionFunction->DeviceExtensionParent =
DeviceExtensionParent;
//
// remember the base interface for this function
//
baseInterface = interfaceList;
USBH_KdPrint((2,"baseInterface = %x config descr = %x\n",
baseInterface, configurationDescriptor));
//
// now compile the group of interfaces that will make up
// this function.
//
{
PUSBD_INTERFACE_LIST_ENTRY interface;
interface = interfaceList;
interface++;
numberOfInterfacesThisFunction = 1;
while (interface->InterfaceDescriptor) {
if ((interface->InterfaceDescriptor->bInterfaceClass !=
baseInterface->InterfaceDescriptor->bInterfaceClass) ||
(interface->InterfaceDescriptor->bInterfaceSubClass ==
baseInterface->InterfaceDescriptor->bInterfaceSubClass) ||
(interface->InterfaceDescriptor->bInterfaceClass !=
USB_DEVICE_CLASS_AUDIO)) {
break;
}
numberOfInterfacesThisFunction++;
interface++;
}
USBH_ASSERT(numberOfInterfacesThisFunction <=
USBH_MAX_FUNCTION_INTERFACES);
}
//
// now we know how many interfaces we are dealing with
//
deviceExtensionFunction->InterfaceCount = 0;
for (k=0; k< numberOfInterfacesThisFunction; k++) {
PFUNCTION_INTERFACE functionInterface;
functionInterface =
&deviceExtensionFunction->FunctionInterfaceList[deviceExtensionFunction->InterfaceCount];
if (functionInterface->InterfaceInformation =
UsbhExAllocatePool(NonPagedPool,
interfaceList->Interface->Length)) {
RtlCopyMemory(functionInterface->InterfaceInformation,
interfaceList->Interface,
interfaceList->Interface->Length);
functionInterface->InterfaceDescriptor
= interfaceList->InterfaceDescriptor;
//
// calculate the length of this interface now
//
// the length of the descriptor is the difference
// between the start of this interface and the
// start of the next one.
//
{
PUCHAR start, end;
PUSBD_INTERFACE_LIST_ENTRY tmp;
tmp = interfaceList;
tmp++;
end = (PUCHAR) configurationDescriptor;
end += configurationDescriptor->wTotalLength;
start = (PUCHAR) functionInterface->InterfaceDescriptor;
if (tmp->InterfaceDescriptor) {
end = (PUCHAR) tmp->InterfaceDescriptor;
}
USBH_ASSERT(end > start);
functionInterface->InterfaceDescriptorLength =
(ULONG)(end - start);
}
USBH_KdPrint((2,"functionInterface = %x\n",
functionInterface));
deviceExtensionFunction->InterfaceCount++;
} else {
USBH_KdTrap(("failure to create function interface\n"));
}
interfaceList++;
}
//
// use the interface number from our 'base' interface
// for the unique id
//
RtlInitUnicodeString(&uniqueIdUnicodeString,
&deviceExtensionFunction->UniqueIdString[0]);
uniqueIdUnicodeString.MaximumLength =
sizeof(deviceExtensionFunction->UniqueIdString);
ntStatus = RtlIntegerToUnicodeString(
(ULONG) baseInterface->InterfaceDescriptor->bInterfaceNumber,
10,
&uniqueIdUnicodeString);
//
// add this function to the list
//
DeviceExtensionParent->FunctionCount++;
PushEntryList(&DeviceExtensionParent->FunctionList,
&deviceExtensionFunction->ListEntry);
USBH_KdPrint((2,"deviceExtensionFunction = %x\n", deviceExtensionFunction));
deviceObject->Flags |= DO_POWER_PAGABLE;
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
} else {
// end of interface list
break;
}
} /* for */
return STATUS_SUCCESS;
}
NTSTATUS
USBH_ParentFdoStopDevice(
IN PDEVICE_EXTENSION_PARENT DeviceExtensionParent,
IN PIRP Irp
)
/* ++
*
* Description:
*
* Argument:
*
* Return:
*
* STATUS_SUCCESS
*
* -- */
{
PDEVICE_OBJECT deviceObject;
NTSTATUS ntStatus;
PAGED_CODE();
deviceObject = DeviceExtensionParent->FunctionalDeviceObject;
USBH_KdPrint((2,"'ParentFdoStopDevice Fdo %x\n", deviceObject));
//
// set the device to the unconfigured state
//
ntStatus = USBH_CloseConfiguration((PDEVICE_EXTENSION_FDO) DeviceExtensionParent);
//
// And we need to pass this message on to lower level driver
//
ntStatus = USBH_PassIrp(Irp, DeviceExtensionParent->TopOfStackDeviceObject);
return ntStatus;
}
NTSTATUS
USBH_ParentFdoStartDevice(
IN OUT PDEVICE_EXTENSION_PARENT DeviceExtensionParent,
IN PIRP Irp,
IN BOOLEAN NewList
)
/* ++ Description:
*
* Argument:
*
* Return:
*
* STATUS_SUCCESS - if successful STATUS_UNSUCCESSFUL - otherwise
*
* -- */
{
NTSTATUS ntStatus;
PURB urb = NULL;
PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor;
PUSBD_INTERFACE_LIST_ENTRY interfaceList, tmp;
LONG numberOfInterfaces, interfaceNumber, i;
PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor;
ULONG nameIndex = 0;
DEVICE_CAPABILITIES deviceCapabilities;
PAGED_CODE();
USBH_KdPrint((2,"'Enter Parent StartDevice\n"));
USBH_ASSERT(EXTENSION_TYPE_PARENT == DeviceExtensionParent->ExtensionType);
KeInitializeEvent(&DeviceExtensionParent->PnpStartEvent, NotificationEvent, FALSE);
USBH_KdPrint((2,"'Set PnPIrp Completion Routine\n"));
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
USBH_PnPIrp_Complete,
// always pass FDO to completion routine
DeviceExtensionParent,
TRUE,
TRUE,
TRUE);
IoCallDriver(DeviceExtensionParent->TopOfStackDeviceObject,
Irp);
KeWaitForSingleObject(&DeviceExtensionParent->PnpStartEvent,
Suspended,
KernelMode,
FALSE,
NULL);
DeviceExtensionParent->NeedCleanup = FALSE;
// WARN STARTS OF OLD GENERIC PARENT
UsbhWarning(NULL,
"This device is using obsolete USB Generic Parent!\nPlease fix your INF file.\n",
TRUE);
// ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
// goto USBH_ParentFdoStartDevice_Done;
// END WARN STARTS OF OLD GENERIC PARENT
//
// configure the device
//
// Initialize DeviceCapabilities structure in case USBH_QueryCapabilities
// is unsuccessful.
RtlZeroMemory(&deviceCapabilities, sizeof(DEVICE_CAPABILITIES));
USBH_QueryCapabilities(DeviceExtensionParent->TopOfStackDeviceObject,
&deviceCapabilities);
//
// save the system state mapping
//
RtlCopyMemory(&DeviceExtensionParent->DeviceState[0],
&deviceCapabilities.DeviceState[0],
sizeof(DeviceExtensionParent->DeviceState));
// always enabled for wakeup
DeviceExtensionParent->ParentFlags |= HUBFLAG_ENABLED_FOR_WAKEUP;
DeviceExtensionParent->DeviceWake = deviceCapabilities.DeviceWake;
DeviceExtensionParent->SystemWake = deviceCapabilities.SystemWake;
DeviceExtensionParent->CurrentPowerState = PowerDeviceD0;
KeInitializeSemaphore(&DeviceExtensionParent->ParentMutex, 1, 1);
ntStatus = USBH_GetDeviceDescriptor(DeviceExtensionParent->FunctionalDeviceObject,
&DeviceExtensionParent->DeviceDescriptor);
if (!NT_SUCCESS(ntStatus)) {
goto USBH_ParentFdoStartDevice_Done;
}
if (NewList) {
ntStatus =
USBH_GetConfigurationDescriptor(DeviceExtensionParent->FunctionalDeviceObject,
&configurationDescriptor);
} else {
//
// use the old config descriptor if this is a re-start
// the reason is that our interface structures in the function
// extension point in to this same buffer.
configurationDescriptor =
DeviceExtensionParent->ConfigurationDescriptor;
}
if (!NT_SUCCESS(ntStatus)) {
goto USBH_ParentFdoStartDevice_Done;
}
DeviceExtensionParent->ConfigurationDescriptor =
configurationDescriptor;
// we will likely define some registry keys to guide us
// in the configuration of the device -- the default will
// be to select the first congiguration and the first
// alternate interface for each interface.
//
USBH_KdPrint((2,"' Parent StartDevice cd = %x\n",
configurationDescriptor));
DeviceExtensionParent->CurrentConfig =
configurationDescriptor->bConfigurationValue;
//
// Build an interface list structure, this is an array
// of strucutres for each interface on the device.
// We keep a pointer to the interface descriptor for each interface
// within the configuration descriptor
//
numberOfInterfaces = configurationDescriptor->bNumInterfaces;
tmp = interfaceList =
UsbhExAllocatePool(PagedPool, sizeof(USBD_INTERFACE_LIST_ENTRY) *
(numberOfInterfaces+1));
if (tmp == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto USBH_ParentFdoStartDevice_Done;
}
//
// just grab the first alt setting we find for each interface
//
i = interfaceNumber = 0;
while (i< numberOfInterfaces) {
interfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
configurationDescriptor,
configurationDescriptor,
interfaceNumber,
0, // assume alt setting zero here
-1,
-1,
-1);
if (interfaceDescriptor) {
interfaceList->InterfaceDescriptor =
interfaceDescriptor;
interfaceList++;
i++;
}
interfaceNumber++;
}
//
// terminate the list
//
interfaceList->InterfaceDescriptor = NULL;
urb = USBD_CreateConfigurationRequestEx(configurationDescriptor,
tmp);
if (urb) {
ntStatus = USBH_FdoSyncSubmitUrb(DeviceExtensionParent->FunctionalDeviceObject, urb);
if (NT_SUCCESS(ntStatus)) {
if (NewList) {
//
// first time create our function list
//
ntStatus = USBH_ParentCreateFunctionList(
DeviceExtensionParent,
tmp,
urb);
} else {
//
// update our function list with the new handles
//
PDEVICE_OBJECT deviceObject;
PSINGLE_LIST_ENTRY listEntry;
SINGLE_LIST_ENTRY tempList;
ULONG i;
PDEVICE_EXTENSION_FUNCTION deviceExtensionFunction;
USBH_KdBreak(("re-init function list %x\n",
DeviceExtensionParent));
deviceObject = DeviceExtensionParent->FunctionalDeviceObject;
tempList.Next = NULL;
//
// process all entries in the function list
//
do {
listEntry = PopEntryList(&DeviceExtensionParent->FunctionList);
if (listEntry != NULL) {
PushEntryList(&tempList, listEntry);
deviceExtensionFunction =
CONTAINING_RECORD(listEntry,
DEVICE_EXTENSION_FUNCTION,
ListEntry);
USBH_KdPrint((2,"'re-init function %x\n",
deviceExtensionFunction));
deviceExtensionFunction->ConfigurationHandle =
urb->UrbSelectConfiguration.ConfigurationHandle;
for (i=0; i< deviceExtensionFunction->InterfaceCount; i++) {
//
// now we need to find the matching interface
// information from the new configuration request
// and attach it to the function
{
PUSBD_INTERFACE_INFORMATION interfaceInformation;
interfaceInformation =
deviceExtensionFunction->FunctionInterfaceList[i].InterfaceInformation;
interfaceList = tmp;
while (interfaceList->InterfaceDescriptor) {
PFUNCTION_INTERFACE functionInterface;
functionInterface =
&deviceExtensionFunction->FunctionInterfaceList[i];
if (interfaceList->InterfaceDescriptor->bInterfaceNumber
== interfaceInformation->InterfaceNumber) {
USBH_KdPrint((2,
"'re-init matched interface %d %x %x\n",
interfaceInformation->InterfaceNumber,
interfaceList,
interfaceInformation));
if (interfaceList->InterfaceDescriptor->bAlternateSetting !=
interfaceInformation->AlternateSetting) {
USBH_KdPrint((2,
"'re-init no match alt interface %d %x %x\n",
interfaceInformation->InterfaceNumber,
interfaceList,
interfaceInformation));
// we have a different alt setting
// switch our info to match the new
// setting
UsbhExFreePool(interfaceInformation);
interfaceInformation =
functionInterface ->InterfaceInformation =
UsbhExAllocatePool(NonPagedPool,
interfaceList->Interface->Length);
if (interfaceInformation) {
RtlCopyMemory(interfaceInformation,
interfaceList->Interface,
interfaceList->Interface->Length);
functionInterface->InterfaceDescriptor =
interfaceList->InterfaceDescriptor;
}
} else {
USBH_KdPrint((2,
"'re-init matched alt interface %d %x %x\n",
interfaceInformation->InterfaceNumber,
interfaceList,
interfaceInformation));
USBH_ASSERT(interfaceList->Interface->Length ==
interfaceInformation->Length);
RtlCopyMemory(interfaceInformation,
interfaceList->Interface,
interfaceList->Interface->Length);
}
break;
}
interfaceList++;
}
}
}
}
} while (listEntry != NULL);
// now put the entries back
do {
listEntry = PopEntryList(&tempList);
if (listEntry != NULL) {
PushEntryList(&DeviceExtensionParent->FunctionList, listEntry);
}
} while (listEntry != NULL);
}
}
ExFreePool(urb);
//
// Tell the OS that this PDO can have kids.
//
//
// Workaround for PnP bug #406381 - RC3SS: Bluescreen failure when
// installing/deinstalling communication ports
//
//===== Assigned by santoshj on 09/23/99 10:27:20 to kenray =====
// This is a race condition between IopInitializeSystemDrivers and
// IoInvalidateDeviceRelations. The real fix is too big a change at this
// stage of the product and has potential of exposing other problems. This
// problem can be solved if USBHUB does not invalidate device relations on
// every start which is redundant anyway (and also exposes this bug).
//
// USBH_IoInvalidateDeviceRelations(DeviceExtensionParent->PhysicalDeviceObject,
// BusRelations);
DeviceExtensionParent->NeedCleanup = TRUE;
} else {
// failed to allocate URB
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
UsbhExFreePool(tmp);
USBH_ParentFdoStartDevice_Done:
//
// complete the start Irp now since we pended it with
// our completion handler.
//
USBH_CompleteIrp(Irp, ntStatus);
return ntStatus;
}
NTSTATUS
USBH_ParentQueryBusRelations(
IN PDEVICE_EXTENSION_PARENT DeviceExtensionParent,
IN PIRP Irp
)
/* ++
*
* Description:
*
* This function responds to Bus_Reference_Next_Device, Bus_Query_Bus_Check,
* //Bus_Query_Id: Bus_Id, HardwareIDs, CompatibleIDs and InstanceID.
*
* Arguments:
*
* Return:
*
* NtStatus
*
* -- */
{
PIO_STACK_LOCATION ioStack;
NTSTATUS ntStatus = STATUS_SUCCESS;
PDEVICE_RELATIONS deviceRelations;
PDEVICE_OBJECT deviceObject;
PSINGLE_LIST_ENTRY listEntry;
PDEVICE_EXTENSION_FUNCTION deviceExtensionFunction;
PAGED_CODE();
USBH_KdPrint((1, "'Query Bus Relations (PAR) %x\n",
DeviceExtensionParent->PhysicalDeviceObject));
//
// Get a pointer to the current location in the Irp. This is where
// the function codes and parameters are located.
//
ioStack = IoGetCurrentIrpStackLocation(Irp);
USBH_KdPrint((2,"'QueryBusRelations (parent) ext = %x\n", DeviceExtensionParent));
USBH_KdPrint((2,"'QueryBusRelations (parent) %x\n", ioStack->Parameters.QueryDeviceRelations.Type));
USBH_ASSERT(ioStack->Parameters.QueryDeviceRelations.Type == BusRelations);
USBH_KdPrint((2,"'ParentQueryBusRelations enumerate device\n"));
//
// It should be Function device object.
//
USBH_ASSERT(EXTENSION_TYPE_PARENT == DeviceExtensionParent->ExtensionType);
//
// Must use ExAllocatePool directly here because the OS
// will free the buffer
//
deviceRelations = ExAllocatePoolWithTag(PagedPool, sizeof(*deviceRelations) +
(DeviceExtensionParent->FunctionCount - 1) * sizeof(PDEVICE_OBJECT),
USBHUB_HEAP_TAG);
if (deviceRelations == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto USBH_ParentQueryBusRelations_Done;
}
deviceRelations->Count = 0;
//
// Functions on a composite device are always present
// we just need to return the PDO
//
listEntry = DeviceExtensionParent->FunctionList.Next;
while (listEntry) {
deviceExtensionFunction =
CONTAINING_RECORD(listEntry,
DEVICE_EXTENSION_FUNCTION,
ListEntry);
USBH_KdPrint((2,"'deviceExtensionFunction = %x\n", deviceExtensionFunction));
deviceObject = deviceExtensionFunction->FunctionPhysicalDeviceObject;
ObReferenceObject(deviceObject);
deviceObject->Flags |= DO_POWER_PAGABLE;
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
deviceRelations->Objects[deviceRelations->Count] = deviceObject;
deviceRelations->Count++;
listEntry = listEntry->Next;
}
USBH_ParentQueryBusRelations_Done:
Irp->IoStatus.Information=(ULONG_PTR) deviceRelations;
Irp->IoStatus.Status = STATUS_SUCCESS;
USBH_KdPrint((1, "'Query Bus Relations (PAR) %x pass on\n",
DeviceExtensionParent->PhysicalDeviceObject));
ntStatus = USBH_PassIrp(Irp,
DeviceExtensionParent->TopOfStackDeviceObject);
return ntStatus;
}
NTSTATUS
USBH_FunctionPdoQueryId(
IN PDEVICE_EXTENSION_FUNCTION DeviceExtensionFunction,
IN PIRP Irp
)
/* ++
*
* Description:
*
* This function responds to IRP_MJ_PNP, IRP_MN_QUERY_ID.
*
* Arguments:
*
* DeviceExtensionPort - should be the PDO we created for the port device Irp
* - the Irp
*
* Return:
*
* NtStatus
*
* -- */
{
PIO_STACK_LOCATION ioStack;
PDEVICE_EXTENSION_PARENT deviceExtensionParent;
PDEVICE_EXTENSION_PORT deviceExtensionPort;
PDEVICE_EXTENSION_HUB deviceExtensionHub;
#ifdef USB2
// ULONG diagnosticFlags;
#else
PUSBD_EXTENSION deviceExtensionUsbd;
#endif
USHORT idVendor;
USHORT idProduct;
LONG miId;
NTSTATUS ntStatus = STATUS_SUCCESS;
BOOLEAN diagnosticMode;
PAGED_CODE();
deviceExtensionParent = DeviceExtensionFunction->DeviceExtensionParent;
ioStack = IoGetCurrentIrpStackLocation(Irp);
USBH_KdPrint((2,"'IRP_MN_QUERY_ID function Pdo extension=%x\n", DeviceExtensionFunction));
//
// It should be physical device object.
//
USBH_ASSERT(EXTENSION_TYPE_FUNCTION == DeviceExtensionFunction->ExtensionType);
// It might not be too clean to reach into the RootHubPdo USBD extension,
// but there doesn't seem to be any other easy way to determine if diag
// mode is on. If diagnostic mode is on, report the VID & PID as 0xFFFF
// so that the diagnostic driver gets loaded for each interface of the
// device.
//
deviceExtensionPort = (PDEVICE_EXTENSION_PORT)deviceExtensionParent->PhysicalDeviceObject->DeviceExtension;
deviceExtensionHub = deviceExtensionPort->DeviceExtensionHub;
#ifdef USB2
// diagnosticFlags = USBD_GetHackFlags(deviceExtensionHub);
// diagnosticMode = (BOOLEAN)(USBD_DEVHACK_SET_DIAG_ID & diagnosticFlags);
diagnosticMode = FALSE;
#else
deviceExtensionUsbd = ((PUSBD_EXTENSION)deviceExtensionHub->RootHubPdo->DeviceExtension)->TrueDeviceExtension;
diagnosticMode = deviceExtensionUsbd->DiagnosticMode;
#endif
if (diagnosticMode)
{
idVendor = 0xFFFF;
idProduct = 0xFFFF;
miId = -1;
}
else
{
idVendor = deviceExtensionParent->DeviceDescriptor.idVendor;
idProduct = deviceExtensionParent->DeviceDescriptor.idProduct;
miId = DeviceExtensionFunction->FunctionInterfaceList[0].InterfaceInformation->InterfaceNumber;
}
switch (ioStack->Parameters.QueryId.IdType) {
case BusQueryDeviceID:
Irp->IoStatus.Information =
(ULONG_PTR)
USBH_BuildDeviceID(idVendor,
idProduct,
miId,
FALSE);
break;
case BusQueryHardwareIDs:
Irp->IoStatus.Information =
(ULONG_PTR)
USBH_BuildHardwareIDs(idVendor,
idProduct,
deviceExtensionParent->DeviceDescriptor.bcdDevice,
miId,
FALSE);
break;
case BusQueryCompatibleIDs:
//
// always use first interface
//
Irp->IoStatus.Information =
(ULONG_PTR) USBH_BuildCompatibleIDs(
"",
"",
DeviceExtensionFunction->FunctionInterfaceList[0].InterfaceInformation->Class,
DeviceExtensionFunction->FunctionInterfaceList[0].InterfaceInformation->SubClass,
DeviceExtensionFunction->FunctionInterfaceList[0].InterfaceInformation->Protocol,
FALSE,
FALSE);
break;
case BusQueryInstanceID:
Irp->IoStatus.Information =
(ULONG_PTR) USBH_BuildInstanceID(&DeviceExtensionFunction->UniqueIdString[0],
sizeof(DeviceExtensionFunction->UniqueIdString));
break;
default:
USBH_KdBreak(("PdoBusExtension Unknown BusQueryId\n"));
// IrpAssert: Must not change Irp->IoStatus.Status for bogus IdTypes,
// so return original status here.
return Irp->IoStatus.Status;
}
if (Irp->IoStatus.Information == 0) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
return ntStatus;
}
NTSTATUS
USBH_FunctionPdoQueryDeviceText(
IN PDEVICE_EXTENSION_FUNCTION DeviceExtensionFunction,
IN PIRP Irp
)
/* ++
*
* Description:
*
* This routine is called by PnP via (IRP_MJ_PNP, IRP_MN_QUERY_CAPABILITIES).
* Supposedly, this is a message forwarded by port device Fdo.
*
* Argument:
*
* DeviceExtensionPort - This is a a Pdo extension we created for the port
* device. Irp - the request
*
* Return:
*
* STATUS_SUCCESS
*
*
* -- */
{
PDEVICE_OBJECT deviceObject;
PIO_STACK_LOCATION ioStack;
PDEVICE_EXTENSION_PARENT deviceExtensionParent;
PDEVICE_EXTENSION_PORT deviceExtensionPort;
PDEVICE_EXTENSION_HUB deviceExtensionHub;
DEVICE_TEXT_TYPE deviceTextType;
LANGID languageId;
NTSTATUS ntStatus = STATUS_SUCCESS;
PUSB_STRING_DESCRIPTOR usbString;
PWCHAR deviceText;
PAGED_CODE();
deviceExtensionParent = DeviceExtensionFunction->DeviceExtensionParent;
deviceExtensionPort = (PDEVICE_EXTENSION_PORT)deviceExtensionParent->PhysicalDeviceObject->DeviceExtension;
deviceObject = deviceExtensionPort->PortPhysicalDeviceObject;
ioStack = IoGetCurrentIrpStackLocation(Irp);
deviceExtensionHub = deviceExtensionPort->DeviceExtensionHub;
deviceTextType = ioStack->
Parameters.QueryDeviceText.DeviceTextType;
// Validate DeviceTextType for IrpAssert
if (deviceTextType != DeviceTextDescription &&
deviceTextType != DeviceTextLocationInformation) {
USBH_KdPrint((2, "'PdoQueryDeviceText called with bogus DeviceTextType\n"));
//
// return the original status passed to us
//
ntStatus = Irp->IoStatus.Status;
goto USBH_FunctionPdoQueryDeviceTextDone;
}
// we don't care about the hiword
//languageId = (USHORT) (ioStack->Parameters.QueryDeviceText.LocaleId >>16);
// always specify english for now.
languageId = 0x0409;
USBH_KdPrint((2,"'PdoQueryDeviceText Pdo %x type = %x, lang = %x locale %x\n",
deviceObject, deviceTextType, languageId, ioStack->Parameters.QueryDeviceText.LocaleId));
//
// see if the device supports strings, for non complient device mode
// we won't even try
//
if (deviceExtensionPort->DeviceData == NULL ||
deviceExtensionPort->DeviceDescriptor.iProduct == 0 ||
(deviceExtensionPort->DeviceHackFlags & USBD_DEVHACK_DISABLE_SN) ||
(deviceExtensionPort->PortPdoFlags & PORTPDO_DEVICE_ENUM_ERROR)) {
// string descriptor
USBH_KdBreak(("no product string\n", deviceObject));
ntStatus = STATUS_NOT_SUPPORTED;
}
if (NT_SUCCESS(ntStatus)) {
usbString = UsbhExAllocatePool(NonPagedPool, MAXIMUM_USB_STRING_LENGTH);
if (usbString) {
ntStatus = USBH_CheckDeviceLanguage(deviceObject,
languageId);
if (NT_SUCCESS(ntStatus)) {
//
// device supports are language, get the string
//
ntStatus = USBH_SyncGetStringDescriptor(deviceObject,
deviceExtensionPort->DeviceDescriptor.iProduct, //index
languageId, //langid
usbString,
MAXIMUM_USB_STRING_LENGTH,
NULL,
TRUE);
if (NT_SUCCESS(ntStatus) &&
usbString->bLength <= sizeof(UNICODE_NULL)) {
ntStatus = STATUS_UNSUCCESSFUL;
}
if (NT_SUCCESS(ntStatus)) {
//
// return the string
//
//
// must use stock alloc function because the caller frees the
// buffer
//
// note: the descriptor header is the same size as
// a unicode NULL so we don't have to adjust the size
//
deviceText = ExAllocatePoolWithTag(PagedPool, usbString->bLength, USBHUB_HEAP_TAG);
if (deviceText) {
RtlZeroMemory(deviceText, usbString->bLength);
RtlCopyMemory(deviceText, &usbString->bString[0],
usbString->bLength - sizeof(UNICODE_NULL));
Irp->IoStatus.Information = (ULONG_PTR) deviceText;
USBH_KdBreak(("Returning Device Text %x\n", deviceText));
} else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
}
UsbhExFreePool(usbString);
} else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
USBH_FunctionPdoQueryDeviceTextDone:
return ntStatus;
}
NTSTATUS
USBH_FunctionPdoPnP(
IN PDEVICE_EXTENSION_FUNCTION DeviceExtensionFunction,
IN PIRP Irp,
IN UCHAR MinorFunction,
IN OUT PBOOLEAN IrpNeedsCompletion
)
/* ++
*
* Description:
*
* This function responds to IoControl PnPPower for the PDO. This function is
* synchronous.
*
* Arguments:
*
* DeviceExtensionPort - the PDO extension Irp - the request packet
* uchMinorFunction - the minor function of the PnP Power request.
*
* Return:
*
* NTSTATUS
*
* -- */
{
NTSTATUS ntStatus;
#if DBG
PDEVICE_OBJECT deviceObject = DeviceExtensionFunction->FunctionPhysicalDeviceObject;
#endif
PDEVICE_EXTENSION_PARENT deviceExtensionParent;
PIO_STACK_LOCATION irpStack;
PAGED_CODE();
*IrpNeedsCompletion = TRUE;
deviceExtensionParent = DeviceExtensionFunction->DeviceExtensionParent;
irpStack = IoGetCurrentIrpStackLocation(Irp);
USBH_KdPrint((2,"'PnP Power Pdo %x minor %x\n", deviceObject, MinorFunction));
switch (MinorFunction) {
case IRP_MN_QUERY_STOP_DEVICE:
case IRP_MN_CANCEL_STOP_DEVICE:
case IRP_MN_QUERY_REMOVE_DEVICE:
case IRP_MN_CANCEL_REMOVE_DEVICE:
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
// Ken says take this out
// case IRP_MN_SURPRISE_REMOVAL:
ntStatus = STATUS_SUCCESS;
break;
case IRP_MN_START_DEVICE:
USBH_KdPrint((1,
"'Starting composite PDO %x\n",
DeviceExtensionFunction->FunctionPhysicalDeviceObject));
ntStatus = STATUS_SUCCESS;
break;
case IRP_MN_STOP_DEVICE:
USBH_KdPrint((1,
"'Stopping composite PDO %x\n",
DeviceExtensionFunction->FunctionPhysicalDeviceObject));
ntStatus = STATUS_SUCCESS;
break;
case IRP_MN_REMOVE_DEVICE:
USBH_KdPrint((1,
"'Removing composite PDO %x\n",
DeviceExtensionFunction->FunctionPhysicalDeviceObject));
ntStatus = STATUS_SUCCESS;
break;
case IRP_MN_QUERY_CAPABILITIES:
{
PDEVICE_CAPABILITIES deviceCapabilities;
PIO_STACK_LOCATION ioStack;
USBH_KdPrint((2,"'IRP_MN_QUERY_CAPABILITIES Function Pdo %x\n", deviceObject));
ntStatus = STATUS_SUCCESS;
ioStack = IoGetCurrentIrpStackLocation(Irp);
deviceCapabilities = ioStack->
Parameters.DeviceCapabilities.Capabilities;
//
// clone the capabilities for the parent
//
//
// fill in the the device state capabilities from the
// table we saved from the pdo.
//
RtlCopyMemory(&deviceCapabilities->DeviceState[0],
&deviceExtensionParent->DeviceState[0],
sizeof(deviceExtensionParent->DeviceState));
//
// clone the device wake capabilities for children
// from the parent.
//
deviceCapabilities->DeviceWake =
deviceExtensionParent->DeviceWake;
deviceCapabilities->SystemWake =
deviceExtensionParent->SystemWake;
//
// we will need to modify these based on information
// returned in the power descriptor
//
deviceCapabilities->Removable = FALSE;
deviceCapabilities->UniqueID = FALSE;
// SurpriseRemovalOK is FALSE by default, and some clients (NDIS)
// set it to true on the way down, in accordance with the DDK.
// deviceCapabilities->SurpriseRemovalOK = FALSE;
deviceCapabilities->RawDeviceOK = FALSE;
}
break;
case IRP_MN_QUERY_ID:
USBH_KdPrint((2,"'IRP_MN_QUERY_ID Pdo %x\n", deviceObject));
ntStatus = USBH_FunctionPdoQueryId(DeviceExtensionFunction, Irp);
break;
case IRP_MN_QUERY_DEVICE_TEXT:
USBH_KdPrint((2,"'IRP_MN_QUERY_DEVICE_TEXT Pdo %x\n", deviceObject));
ntStatus = USBH_FunctionPdoQueryDeviceText(DeviceExtensionFunction, Irp);
break;
case IRP_MN_QUERY_DEVICE_RELATIONS:
// this is a leaf node, we return the status passed
// to us unless it is a call to TargetRelations
if (irpStack->Parameters.QueryDeviceRelations.Type ==
TargetDeviceRelation) {
PDEVICE_RELATIONS deviceRelations = NULL;
deviceRelations = ExAllocatePoolWithTag(PagedPool,
sizeof(*deviceRelations), USBHUB_HEAP_TAG);
if (deviceRelations == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
} else {
ObReferenceObject(
DeviceExtensionFunction->FunctionPhysicalDeviceObject);
deviceRelations->Count = 1;
deviceRelations->Objects[0] =
DeviceExtensionFunction->FunctionPhysicalDeviceObject;
ntStatus = STATUS_SUCCESS;
}
USBH_KdPrint((1, "'Query Target Relations (FUN) PDO %x complt\n",
DeviceExtensionFunction->FunctionPhysicalDeviceObject));
Irp->IoStatus.Information=(ULONG_PTR) deviceRelations;
} else {
ntStatus = Irp->IoStatus.Status;
}
break;
case IRP_MN_QUERY_INTERFACE:
USBH_KdPrint((1,"'IRP_MN_QUERY_INTERFACE, xface type: %x\n",
irpStack->Parameters.QueryInterface.InterfaceType));
// Pass this on to the parent.
ntStatus = USBH_PassIrp(Irp, deviceExtensionParent->FunctionalDeviceObject);
*IrpNeedsCompletion = FALSE;
break;
default:
USBH_KdBreak(("PdoPnP unknown (%d) PnP message Pdo %x\n",
MinorFunction, deviceObject));
ntStatus = Irp->IoStatus.Status;
}
USBH_KdPrint((2,"'FunctionPdoPnP exit %x\n", ntStatus));
return ntStatus;
}
VOID
USBH_ParentWaitWakeCancel(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
Return Value:
NT status code.
--*/
{
PDEVICE_EXTENSION_HEADER devExtHeader;
PDEVICE_EXTENSION_FUNCTION function;
PDEVICE_EXTENSION_PARENT parent;
NTSTATUS ntStatus = STATUS_CANCELLED;
LONG pendingChildWWs;
PIRP parentWaitWake = NULL;
USBH_KdPrint((1,"'Function WaitWake Irp %x cancelled\n", Irp));
USBH_ASSERT(DeviceObject);
devExtHeader = (PDEVICE_EXTENSION_HEADER) DeviceObject->DeviceExtension;
USBH_ASSERT(devExtHeader->ExtensionType == EXTENSION_TYPE_FUNCTION);
function = (PDEVICE_EXTENSION_FUNCTION) devExtHeader;
parent = function->DeviceExtensionParent;
if (Irp != function->WaitWakeIrp) {
//
// Nothing to do
// This Irp has already been taken care of.
// We are in the process of completing this IRP in
// USBH_ParentCompleteFunctionWakeIrps.
//
IoReleaseCancelSpinLock(Irp->CancelIrql);
} else {
function->WaitWakeIrp = NULL;
IoSetCancelRoutine(Irp, NULL);
pendingChildWWs = InterlockedDecrement (&parent->NumberFunctionWakeIrps);
parentWaitWake = parent->PendingWakeIrp;
if (0 == pendingChildWWs) {
// Set PendingWakeIrp to NULL since we cancel it below.
parent->PendingWakeIrp = NULL;
parent->ParentFlags &= ~HUBFLAG_PENDING_WAKE_IRP;
}
IoReleaseCancelSpinLock(Irp->CancelIrql);
PoStartNextPowerIrp(Irp);
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
//
// If there are no more outstanding WW irps, we need to cancel the WW
// to our parent.
//
if (0 == pendingChildWWs) {
IoCancelIrp (parentWaitWake);
} else {
ASSERT (0 < pendingChildWWs);
}
}
}
NTSTATUS
USBH_FunctionPdoPower(
IN PDEVICE_EXTENSION_FUNCTION DeviceExtensionFunction,
IN PIRP Irp,
IN UCHAR MinorFunction
)
/* ++
*
* Description:
*
* This function responds to IoControl Power for the PDO. This function is
* synchronous.
*
* Arguments:
*
* DeviceExtensionPort - the PDO extension Irp - the request packet
* uchMinorFunction - the minor function of the PnP Power request.
*
* Return:
*
* NTSTATUS
*
* -- */
{
NTSTATUS ntStatus;
#if DBG
PDEVICE_OBJECT deviceObject = DeviceExtensionFunction->FunctionPhysicalDeviceObject;
#endif
PIO_STACK_LOCATION irpStack;
USHORT feature;
KIRQL irql;
PIRP wWIrp;
PIRP parentWaitWake;
LONG pendingFunctionWWs;
PDEVICE_EXTENSION_PARENT deviceExtensionParent;
PDRIVER_CANCEL oldCancel;
irpStack = IoGetCurrentIrpStackLocation(Irp);
USBH_KdPrint((2,"'Power Pdo %x minor %x\n", deviceObject, MinorFunction));
deviceExtensionParent = DeviceExtensionFunction->DeviceExtensionParent;
switch (MinorFunction) {
case IRP_MN_SET_POWER:
USBH_KdPrint((2,"'IRP_MN_SET_POWER\n"));
//
// we just return success here, pnp will make sure
// all children have entred the low power state
// before putting the parent in a low power state
//
//
// send the setpower feature request here if the device
// wants it
//
ntStatus = STATUS_SUCCESS;
switch (irpStack->Parameters.Power.Type) {
case SystemPowerState:
USBH_KdPrint(
(1, "'IRP_MJ_POWER PA pdo(%x) MN_SET_POWER(SystemPowerState) complt\n",
DeviceExtensionFunction->FunctionPhysicalDeviceObject));
ntStatus = STATUS_SUCCESS;
break;
case DevicePowerState:
ntStatus = STATUS_SUCCESS;
USBH_KdPrint(
(1, "'IRP_MJ_POWER PA pdo(%x) MN_SET_POWER(DevicePowerState) complt\n",
DeviceExtensionFunction->FunctionPhysicalDeviceObject));
break;
} // switch irpStack->Parameters.Power.Type
break; //IRP_MN_SET_POWER
case IRP_MN_QUERY_POWER:
ntStatus = STATUS_SUCCESS;
USBH_KdPrint(
(1, "'IRP_MJ_POWER PA pdo(%x) MN_QUERY_POWER, status = %x complt\n",
DeviceExtensionFunction->FunctionPhysicalDeviceObject, ntStatus));
break;
case IRP_MN_WAIT_WAKE:
USBH_KdPrint(
(1, "'enabling remote wakeup for USB child PDO (%x)\n",
DeviceExtensionFunction->FunctionPhysicalDeviceObject));
if (deviceExtensionParent->CurrentPowerState != PowerDeviceD0 ||
deviceExtensionParent->ParentFlags & HUBFLAG_DEVICE_STOPPING) {
LOGENTRY(LOG_PNP, "!WWp", deviceExtensionParent, 0, 0);
UsbhWarning(NULL,
"Client driver should not be submitting WW IRPs at this time.\n",
TRUE);
ntStatus = STATUS_INVALID_DEVICE_STATE;
break;
}
IoAcquireCancelSpinLock(&irql);
if (DeviceExtensionFunction->WaitWakeIrp != NULL) {
ntStatus = STATUS_DEVICE_BUSY;
IoReleaseCancelSpinLock(irql);
} else {
// set a cancel routine
oldCancel = IoSetCancelRoutine(Irp, USBH_ParentWaitWakeCancel);
USBH_ASSERT (NULL == oldCancel);
if (Irp->Cancel) {
TEST_TRAP();
IoSetCancelRoutine (Irp, NULL);
IoReleaseCancelSpinLock(irql);
ntStatus = STATUS_CANCELLED;
} else {
// flag this device as "enabled for wakeup"
DeviceExtensionFunction->WaitWakeIrp = Irp;
pendingFunctionWWs =
InterlockedIncrement (&deviceExtensionParent->NumberFunctionWakeIrps);
IoMarkIrpPending(Irp);
IoReleaseCancelSpinLock(irql);
//
// now we must enable the parent PDO for wakeup
//
if (1 == pendingFunctionWWs) {
// What if this fails?
ntStatus = USBH_ParentSubmitWaitWakeIrp(deviceExtensionParent);
} else {
ntStatus = STATUS_PENDING;
}
ntStatus = STATUS_PENDING;
goto USBH_FunctionPdoPower_Done;
}
}
break;
default:
USBH_KdBreak(("PdoPnP unknown (%d) PnP message Pdo %x\n",
MinorFunction, deviceObject));
//
// return the original status passed to us
//
ntStatus = Irp->IoStatus.Status;
}
USBH_KdPrint((2,"'FunctionPdoPower exit %x\n", ntStatus));
PoStartNextPowerIrp(Irp);
Irp->IoStatus.Status = ntStatus;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
USBH_FunctionPdoPower_Done:
return ntStatus;
}
NTSTATUS
USBH_ParentQCapsComplete(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
Called when lower device completes Q_CAPS.
This gives us a chance to mark the device as SurpriseRemovalOK.
Arguments:
DeviceObject - a pointer to the device object
Irp - a pointer to the irp
Context - NULL ptr
Return Value:
STATUS_SUCCESS
--*/
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_CAPABILITIES pDevCaps = irpStack->Parameters.DeviceCapabilities.Capabilities;
NTSTATUS ntStatus;
USBH_KdPrint((1, "'USBH_ParentQCapsComplete\n"));
ntStatus = Irp->IoStatus.Status;
if (Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
//
// Set SurpriseRemoval flag to TRUE
//
pDevCaps->SurpriseRemovalOK = TRUE;
return ntStatus;
}
NTSTATUS
USBH_ParentPnP(
IN PDEVICE_EXTENSION_PARENT DeviceExtensionParent,
IN PIRP Irp,
IN UCHAR MinorFunction
)
/* ++
*
* Description:
*
* This function responds to IoControl PnP for the FDO. This function is
* synchronous.
*
* Arguments:
*
* DeviceExtensionParent - the FDO extension pIrp - the request packet
* MinorFunction - the minor function of the PnP Power request.
*
* Return:
*
* NTSTATUS
*
* -- */
{
NTSTATUS ntStatus;
PDEVICE_OBJECT deviceObject;
PIO_STACK_LOCATION irpStack;
irpStack = IoGetCurrentIrpStackLocation(Irp);
deviceObject = DeviceExtensionParent->FunctionalDeviceObject;
USBH_KdPrint((2,"'PnP Fdo %x minor %x\n", deviceObject, MinorFunction));
switch (MinorFunction) {
case IRP_MN_START_DEVICE:
USBH_KdBreak(("'IRP_MN_START_DEVICE Parent Fdo %x\n", deviceObject));
// we get here as a result of re-start.
// note: our parent hub already checked to see if the device is the same.
Irp->IoStatus.Status = STATUS_SUCCESS;
ntStatus = USBH_ParentFdoStartDevice(DeviceExtensionParent, Irp, FALSE);
break;
case IRP_MN_STOP_DEVICE:
USBH_KdPrint((2,"'IRP_MN_STOP_DEVICE Fdo %x\n", deviceObject));
Irp->IoStatus.Status = STATUS_SUCCESS;
ntStatus = USBH_ParentFdoStopDevice(DeviceExtensionParent, Irp);
break;
case IRP_MN_REMOVE_DEVICE:
USBH_KdPrint((2,"'IRP_MN_REMOVE_DEVICE Fdo %x\n", deviceObject));
Irp->IoStatus.Status = STATUS_SUCCESS;
ntStatus = USBH_ParentFdoRemoveDevice(DeviceExtensionParent, Irp);
break;
//
// This one should be passed down. Let the default case handle it.
//
// case IRP_MN_QUERY_PNP_DEVICE_STATE:
// USBH_KdPrint((2,"IRP_MN_QUERY_PNP_DEVICE_STATE Pdo %x\n", deviceObject));
// ntStatus = STATUS_SUCCESS;
// break;
case IRP_MN_QUERY_DEVICE_RELATIONS:
switch (irpStack->Parameters.QueryDeviceRelations.Type) {
case BusRelations:
ntStatus = USBH_ParentQueryBusRelations(DeviceExtensionParent, Irp);
break;
case TargetDeviceRelation:
//
// this one gets passed on
//
USBH_KdPrint((1, "'Query Relations, TargetRelations (PAR) %x\n",
DeviceExtensionParent->PhysicalDeviceObject));
ntStatus = USBH_PassIrp(Irp,
DeviceExtensionParent->TopOfStackDeviceObject);
break;
default:
USBH_KdPrint((1, "'Query Relations (?) (PAR) %x pass on\n",
DeviceExtensionParent->PhysicalDeviceObject));
ntStatus = USBH_PassIrp(Irp,
DeviceExtensionParent->TopOfStackDeviceObject);
}
break;
case IRP_MN_QUERY_CAPABILITIES:
USBH_KdPrint((1, "'Query Capabilities (PAR) %x\n",
DeviceExtensionParent->PhysicalDeviceObject));
IoCopyCurrentIrpStackLocationToNext(Irp);
// Set up a completion routine to handle marking the IRP.
IoSetCompletionRoutine(Irp,
USBH_ParentQCapsComplete,
DeviceExtensionParent,
TRUE,
TRUE,
TRUE);
// Now pass down the IRP.
ntStatus = IoCallDriver(DeviceExtensionParent->TopOfStackDeviceObject, Irp);
break;
case IRP_MN_QUERY_STOP_DEVICE:
case IRP_MN_CANCEL_STOP_DEVICE:
case IRP_MN_QUERY_REMOVE_DEVICE:
case IRP_MN_CANCEL_REMOVE_DEVICE:
// Ken says take this out
// case IRP_MN_SURPRISE_REMOVAL:
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
// IrpAssert: Must set IRP status before passing IRP down.
Irp->IoStatus.Status = STATUS_SUCCESS;
// fall through
//
// Pass it down to Pdo to handle all other MN functions
//
default:
USBH_KdPrint((2,"'Query/Cancel/Power request on parent fdo %x %x\n",
deviceObject, MinorFunction));
ntStatus = USBH_PassIrp(Irp,
DeviceExtensionParent->TopOfStackDeviceObject);
break;
}
USBH_KdPrint((2,"'ParentPnP exit %x\n", ntStatus));
return ntStatus;
}
NTSTATUS
USBH_ParentPower(
IN PDEVICE_EXTENSION_PARENT DeviceExtensionParent,
IN PIRP Irp,
IN UCHAR MinorFunction
)
/* ++
*
* Description:
*
* This function responds to IoControl Power for the FDO. This function is
* synchronous.
*
* Arguments:
*
* DeviceExtensionParent - the FDO extension pIrp - the request packet
* MinorFunction - the minor function of the PnP Power request.
*
* Return:
*
* NTSTATUS
*
* -- */
{
NTSTATUS ntStatus;
PDEVICE_OBJECT deviceObject;
PIO_STACK_LOCATION irpStack;
irpStack = IoGetCurrentIrpStackLocation(Irp);
deviceObject = DeviceExtensionParent->FunctionalDeviceObject;
USBH_KdPrint((2,"'Power Fdo %x minor %x\n", deviceObject, MinorFunction));
switch (MinorFunction) {
case IRP_MN_QUERY_POWER:
USBH_KdPrint(
(1, "'IRP_MJ_POWER PA fdo(%x) MN_QUERY_POWER\n",
DeviceExtensionParent->FunctionalDeviceObject));
IoCopyCurrentIrpStackLocationToNext(Irp);
PoStartNextPowerIrp(Irp);
//
// must pass this on to our PDO
//
ntStatus = PoCallDriver(DeviceExtensionParent->TopOfStackDeviceObject,
Irp);
break;
case IRP_MN_SET_POWER:
USBH_KdPrint(
(1, "'IRP_MJ_POWER PA fdo(%x) MN_QUERY_POWER\n",
DeviceExtensionParent->FunctionalDeviceObject));
switch (irpStack->Parameters.Power.Type) {
case SystemPowerState:
{
POWER_STATE powerState;
USBH_KdPrint(
(1, "IRP_MJ_POWER PA fdo(%x) MN_SET_POWER(SystemPowerState)\n",
DeviceExtensionParent->FunctionalDeviceObject));
if (irpStack->Parameters.Power.State.SystemState ==
PowerSystemWorking) {
powerState.DeviceState = PowerDeviceD0;
} else if (DeviceExtensionParent->ParentFlags &
HUBFLAG_ENABLED_FOR_WAKEUP) {
//
// based on the system power state
// request a setting to the appropriate
// Dx state.
//
powerState.DeviceState =
DeviceExtensionParent->DeviceState[irpStack->Parameters.Power.State.SystemState];
//
// These tables should have already been fixed up by the root hub
// (usbd.sys) to not contain an entry of unspecified.
//
ASSERT (PowerDeviceUnspecified != powerState.DeviceState);
USBH_KdPrint((2,"'Parent System state maps to device state 0x%x\n",
powerState.DeviceState));
} else {
TEST_TRAP();
powerState.DeviceState = PowerDeviceD3;
} // irpStack->Parameters.Power.State.SystemState
//
// only make the request if it is for a differnt power
// state then the one we are in.
//
if (powerState.DeviceState !=
DeviceExtensionParent->CurrentPowerState) {
DeviceExtensionParent->PowerIrp = Irp;
ntStatus = PoRequestPowerIrp(DeviceExtensionParent->PhysicalDeviceObject,
IRP_MN_SET_POWER,
powerState,
USBH_FdoDeferPoRequestCompletion,
DeviceExtensionParent,
NULL);
} else {
IoCopyCurrentIrpStackLocationToNext(Irp);
PoStartNextPowerIrp(Irp);
ntStatus = PoCallDriver(DeviceExtensionParent->TopOfStackDeviceObject,
Irp);
}
}
break;
case DevicePowerState:
USBH_KdPrint(
(1, "IRP_MJ_POWER PA fdo(%x) MN_SET_POWER(DevicePowerState)\n",
DeviceExtensionParent->FunctionalDeviceObject));
DeviceExtensionParent->CurrentPowerState =
irpStack->Parameters.Power.State.DeviceState;
LOGENTRY(LOG_PNP, "prD>", DeviceExtensionParent, DeviceExtensionParent->CurrentPowerState , 0);
//
// all of our pdos need to be at or below the
// expected D-state
//
IoCopyCurrentIrpStackLocationToNext(Irp);
PoStartNextPowerIrp(Irp);
ntStatus = PoCallDriver(DeviceExtensionParent->TopOfStackDeviceObject,
Irp);
USBH_KdPrint((2,"'Parent Device Power State PoCallDriver() = %x\n",
ntStatus));
break;
}
break; // MN_SET_POWER
default:
USBH_KdPrint((2,"'Power request on parent not handled, fdo %x %x\n",
deviceObject, MinorFunction));
IoCopyCurrentIrpStackLocationToNext(Irp);
PoStartNextPowerIrp(Irp);
ntStatus = PoCallDriver(DeviceExtensionParent->TopOfStackDeviceObject,
Irp);
break;
}
USBH_KdPrint((2,"'ParentPnP exit %x\n", ntStatus));
return ntStatus;
}
NTSTATUS
USBH_ParentDispatch(
IN PDEVICE_EXTENSION_PARENT DeviceExtensionParent,
IN PIRP Irp
)
/* ++
*
* Description:
*
* Handles calls to a FDO associated with a composite device
*
* Arguments:
*
* Return:
*
* NTSTATUS
*
* -- */
{
NTSTATUS ntStatus = STATUS_SUCCESS;
PIO_STACK_LOCATION ioStackLocation; // our stack location
PDEVICE_OBJECT deviceObject;
USBH_KdPrint((2,"'FdoDispatch DeviceExtension %x Irp %x\n",
DeviceExtensionParent, Irp));
deviceObject = DeviceExtensionParent->FunctionalDeviceObject;
//
// Get a pointer to IoStackLocation so we can retrieve parameters.
//
ioStackLocation = IoGetCurrentIrpStackLocation(Irp);
//
// the called functions will complete the irp if necessary
//
switch (ioStackLocation->MajorFunction) {
case IRP_MJ_CREATE:
USBH_KdPrint((2,"'IRP_MJ_CREATE\n"));
USBH_CompleteIrp(Irp, STATUS_SUCCESS);
break;
case IRP_MJ_CLOSE:
USBH_KdPrint((2,"'IRP_MJ_CLOSE\n"));
USBH_CompleteIrp(Irp, STATUS_SUCCESS);
break;
case IRP_MJ_DEVICE_CONTROL:
//
// Note: if we ever do find a reason to handle this, be sure to
// not forward IOCTL_KS_PROPERTY / KSPROPSETID_DrmAudioStream /
// KSPROPERTY_DRMAUDIOSTREAM_SETCONTENTID to next driver! Otherwise
// this might not be DRM compliant.
//
USBH_KdPrint((2,"'IRP_MJ_DEVICE_CONTROL\n"));
UsbhWarning(NULL,"Should not be hitting this code\n", FALSE);
ntStatus = STATUS_UNSUCCESSFUL;
USBH_CompleteIrp(Irp, ntStatus);
break;
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
USBH_KdPrint((2,"'InternlDeviceControl IOCTL unknown pass on\n"));
UsbhWarning(NULL,"Should not be hitting this code\n", FALSE);
ntStatus = STATUS_UNSUCCESSFUL;
USBH_CompleteIrp(Irp, ntStatus);
break;
case IRP_MJ_PNP:
USBH_KdPrint((2,"'IRP_MJ_PNP\n"));
ntStatus = USBH_ParentPnP(DeviceExtensionParent, Irp, ioStackLocation->MinorFunction);
break;
case IRP_MJ_POWER:
USBH_KdPrint((2,"'IRP_MJ_POWER\n"));
ntStatus = USBH_ParentPower(DeviceExtensionParent, Irp, ioStackLocation->MinorFunction);
break;
case IRP_MJ_SYSTEM_CONTROL:
USBH_KdPrint((2,"'IRP_MJ_SYSTEM_CONTROL\n"));
#ifdef WMI_SUPPORT
ntStatus =
USBH_SystemControl ((PDEVICE_EXTENSION_FDO) DeviceExtensionParent, Irp);
#else
ntStatus = USBH_PassIrp(Irp,
DeviceExtensionParent->TopOfStackDeviceObject);
#endif
break;
default:
//
// Unknown Irp -- complete with error
//
USBH_KdBreak(("Unknown Irp for Fdo %x Irp_Mj %x\n",
deviceObject, ioStackLocation->MajorFunction));
ntStatus = STATUS_NOT_IMPLEMENTED;
USBH_CompleteIrp(Irp, ntStatus);
break;
}
USBH_KdPrint((2,"' exit USBH_ParentDispatch Object %x Status %x\n",
deviceObject, ntStatus));
//
// always return a status code
//
return ntStatus;
}
NTSTATUS
USBH_FunctionUrbFilter(
IN PDEVICE_EXTENSION_FUNCTION DeviceExtensionFunction,
IN PIRP Irp
)
/*
* Description:
*
* Arguments:
*
* Return:
*
* NTSTATUS
*
* -- */
{
NTSTATUS ntStatus;
PIO_STACK_LOCATION ioStackLocation; // our stack location
PDEVICE_EXTENSION_PARENT deviceExtensionParent;
PURB urb;
USHORT function;
USBH_KdPrint((2,"'USBH_FunctionUrbFilter DeviceExtension %x Irp %x\n",
DeviceExtensionFunction, Irp));
deviceExtensionParent = DeviceExtensionFunction->DeviceExtensionParent;
LOGENTRY(LOG_PNP, "fURB", DeviceExtensionFunction, deviceExtensionParent,
deviceExtensionParent->ParentFlags);
ioStackLocation = IoGetCurrentIrpStackLocation(Irp);
urb = ioStackLocation->Parameters.Others.Argument1;
// check the command code code the URB
function = urb->UrbHeader.Function;
if (deviceExtensionParent->CurrentPowerState !=
PowerDeviceD0) {
// the child devices should not be passing in urbs
// unless th eparent is in D0
UsbhWarning(NULL,
"Parent Not in D0.\n",
TRUE);
}
switch(function) {
case URB_FUNCTION_SELECT_CONFIGURATION:
{
//
// if the requested config matches the current config
// then go ahead and return the current interface
// information for all interfaces requested.
//
PUSBD_INTERFACE_INFORMATION interface;
if (urb->UrbSelectConfiguration.ConfigurationDescriptor == NULL) {
USBH_KdBreak(("closing config on a composite device\n"));
//
// closing the configuration,
// just return success.
//
urb->UrbHeader.Status = USBD_STATUS_SUCCESS;
ntStatus = STATUS_SUCCESS;
} else {
ULONG i;
//
// Normally the URB will contain only one interface.
// the special case is audio which may contain two
// so we have to have a check to handle this.
//
interface = &urb->UrbSelectConfiguration.Interface;
USBH_FunctionUrbFilter_Next:
USBH_KdPrint((2,"'interface = %x\n",
interface));
//
// should validate the requested interface against the
// current config.
//
USBH_KdBreak(("need some validation here!\n"));
USBH_ASSERT(urb->UrbSelectConfiguration.ConfigurationDescriptor->bConfigurationValue
== deviceExtensionParent->CurrentConfig);
// find the interface we are interested in
for (i=0; i< DeviceExtensionFunction->InterfaceCount; i++) {
PFUNCTION_INTERFACE functionInterface;
functionInterface =
&DeviceExtensionFunction->FunctionInterfaceList[i];
USBH_KdPrint((2,"'functionInterface = %x, %x\n",
functionInterface, functionInterface->InterfaceInformation));
if (functionInterface->InterfaceInformation->InterfaceNumber ==
interface->InterfaceNumber) {
break;
}
}
if (i < DeviceExtensionFunction->InterfaceCount) {
PFUNCTION_INTERFACE functionInterface;
functionInterface =
&DeviceExtensionFunction->FunctionInterfaceList[i];
if (functionInterface->InterfaceInformation->AlternateSetting !=
interface->AlternateSetting) {
PURB iUrb;
NTSTATUS localStatus;
PUSBD_INTERFACE_INFORMATION localInterface;
USHORT siz;
// client is requesting a different alternate setting
// we need to do a select_interface
siz =
(USHORT)(GET_SELECT_INTERFACE_REQUEST_SIZE(interface->NumberOfPipes));
iUrb = UsbhExAllocatePool(NonPagedPool, siz);
if (iUrb) {
localInterface = &iUrb->UrbSelectInterface.Interface;
iUrb->UrbSelectInterface.Hdr.Function =
URB_FUNCTION_SELECT_INTERFACE;
iUrb->UrbSelectInterface.Hdr.Length = siz;
iUrb->UrbSelectInterface.ConfigurationHandle =
DeviceExtensionFunction->ConfigurationHandle;
USBH_KdPrint((2,"'localInterface = %x\n",
localInterface));
RtlCopyMemory(localInterface,
interface,
interface->Length);
localStatus = USBH_SyncSubmitUrb(
deviceExtensionParent->TopOfStackDeviceObject,
iUrb);
UsbhExFreePool(functionInterface->InterfaceInformation);
functionInterface->InterfaceInformation =
UsbhExAllocatePool(NonPagedPool,
interface->Length);
RtlCopyMemory(functionInterface->InterfaceInformation,
localInterface,
localInterface->Length);
UsbhExFreePool(iUrb);
iUrb = NULL;
}
}
USBH_ASSERT(interface->Length ==
functionInterface->InterfaceInformation->Length);
RtlCopyMemory(interface,
functionInterface->InterfaceInformation,
functionInterface->InterfaceInformation->Length);
urb->UrbSelectConfiguration.ConfigurationHandle =
DeviceExtensionFunction->ConfigurationHandle;
urb->UrbHeader.Status = USBD_STATUS_SUCCESS;
ntStatus = STATUS_SUCCESS;
} else {
ntStatus = STATUS_INVALID_PARAMETER;
}
//
// check for multiple interfaces e.g. audio
//
if (DeviceExtensionFunction->InterfaceCount > 1) {
interface = (PUSBD_INTERFACE_INFORMATION)
(((PUCHAR) interface) + interface->Length);
if ((PUCHAR)interface < (((PUCHAR) urb) +
urb->UrbSelectConfiguration.Hdr.Length)) {
goto USBH_FunctionUrbFilter_Next;
}
}
}
USBH_CompleteIrp(Irp, ntStatus);
}
break;
case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
{
PUCHAR userBuffer = NULL;
ULONG bytesReturned;
//
// if we requesting the configuration descriptor then we
// will return it based on the information in our extension.
//
if (urb->UrbControlDescriptorRequest.DescriptorType ==
USB_CONFIGURATION_DESCRIPTOR_TYPE) {
if (urb->UrbControlDescriptorRequest.TransferBufferMDL) {
ntStatus = STATUS_INVALID_PARAMETER;
} else {
userBuffer =
urb->UrbControlDescriptorRequest.TransferBuffer;
ntStatus = USBH_BuildFunctionConfigurationDescriptor(
DeviceExtensionFunction,
userBuffer,
urb->UrbControlDescriptorRequest.TransferBufferLength,
&bytesReturned);
urb->UrbControlDescriptorRequest.TransferBufferLength =
bytesReturned;
urb->UrbHeader.Status = USBD_STATUS_SUCCESS;
}
USBH_CompleteIrp(Irp, ntStatus);
} else {
ntStatus = USBH_PassIrp(Irp,
deviceExtensionParent->TopOfStackDeviceObject);
}
}
break;
default:
//
// forward the request to the parents PDO
//
ntStatus = USBH_PassIrp(Irp,
deviceExtensionParent->TopOfStackDeviceObject);
break;
}
return ntStatus;
}
VOID
USBH_CancelAllIrpsInList(
IN PDEVICE_EXTENSION_PARENT DeviceExtensionParent
)
/*
* Description:
*
* This function walks the list of devices and cancels all the queued
* ResetIrps in the list.
*
* Arguments:
*
* Return:
*
*
* -- */
{
PSINGLE_LIST_ENTRY listEntry;
PDEVICE_EXTENSION_FUNCTION deviceExtensionFunction;
listEntry = DeviceExtensionParent->FunctionList.Next;
while (listEntry) {
deviceExtensionFunction =
CONTAINING_RECORD(listEntry,
DEVICE_EXTENSION_FUNCTION,
ListEntry);
ASSERT_FUNCTION(deviceExtensionFunction);
if (deviceExtensionFunction->ResetIrp) {
USBH_CompleteIrp(deviceExtensionFunction->ResetIrp, STATUS_UNSUCCESSFUL);
deviceExtensionFunction->ResetIrp = NULL;
}
listEntry = listEntry->Next;
}
}
VOID
USBH_CompResetTimeoutWorker(
IN PVOID Context)
/* ++
*
* Description:
*
* Work item scheduled to handle a composite reset timeout.
*
*
* Arguments:
*
* Return:
*
* -- */
{
PUSBH_COMP_RESET_TIMEOUT_WORK_ITEM workItemCompResetTimeout;
PDEVICE_EXTENSION_PARENT deviceExtensionParent;
workItemCompResetTimeout = Context;
deviceExtensionParent = workItemCompResetTimeout->DeviceExtensionParent;
USBH_KdPrint((2,"'CompReset timeout\n"));
LOGENTRY(LOG_PNP, "CRTO", deviceExtensionParent, 0, 0);
USBH_KdPrint((2,"'*** (CRTW) WAIT parent mutex %x\n", deviceExtensionParent));
KeWaitForSingleObject(&deviceExtensionParent->ParentMutex,
Executive,
KernelMode,
FALSE,
NULL);
USBH_KdPrint((2,"'*** (CRTW) WAIT parent mutex done %x\n", deviceExtensionParent));
USBH_CancelAllIrpsInList(deviceExtensionParent);
USBH_KdPrint((2,"'*** (CRTW) RELEASE parent mutex %x\n", deviceExtensionParent));
KeReleaseSemaphore(&deviceExtensionParent->ParentMutex,
LOW_REALTIME_PRIORITY,
1,
FALSE);
UsbhExFreePool(workItemCompResetTimeout);
}
VOID
USBH_CompResetTimeoutDPC(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
This routine runs at DISPATCH_LEVEL IRQL.
Arguments:
Dpc - Pointer to the DPC object.
DeferredContext -
SystemArgument1 - not used.
SystemArgument2 - not used.
Return Value:
None.
--*/
{
PCOMP_RESET_TIMEOUT_CONTEXT compResetTimeoutContext = DeferredContext;
PDEVICE_EXTENSION_PARENT deviceExtensionParent =
compResetTimeoutContext->DeviceExtensionParent;
BOOLEAN cancelFlag;
PUSBH_COMP_RESET_TIMEOUT_WORK_ITEM workItemCompResetTimeout;
USBH_KdPrint((2,"'COMP_RESET_TIMEOUT\n"));
// Take SpinLock here so that main routine won't write CancelFlag
// in the timeout context while we free the timeout context.
KeAcquireSpinLockAtDpcLevel(&deviceExtensionParent->ParentSpinLock);
cancelFlag = compResetTimeoutContext->CancelFlag;
deviceExtensionParent->CompResetTimeoutContext = NULL;
KeReleaseSpinLockFromDpcLevel(&deviceExtensionParent->ParentSpinLock);
UsbhExFreePool(compResetTimeoutContext);
if (!cancelFlag) {
//
// Schedule a work item to process this.
//
workItemCompResetTimeout = UsbhExAllocatePool(NonPagedPool,
sizeof(USBH_COMP_RESET_TIMEOUT_WORK_ITEM));
if (workItemCompResetTimeout) {
workItemCompResetTimeout->DeviceExtensionParent = deviceExtensionParent;
ExInitializeWorkItem(&workItemCompResetTimeout->WorkQueueItem,
USBH_CompResetTimeoutWorker,
workItemCompResetTimeout);
LOGENTRY(LOG_PNP, "crER", deviceExtensionParent,
&workItemCompResetTimeout->WorkQueueItem, 0);
ExQueueWorkItem(&workItemCompResetTimeout->WorkQueueItem,
DelayedWorkQueue);
// The WorkItem is freed by USBH_CompResetTimeoutWorker()
// Don't try to access the WorkItem after it is queued.
}
}
}
BOOLEAN
USBH_ListReadyForReset(
IN PDEVICE_EXTENSION_PARENT DeviceExtensionParent
)
/*
* Description:
*
* This function walks the list of devices to see if we are ready
* to do the actual reset.
*
* Arguments:
*
* Return:
*
* TRUE if we're ready, FALSE if we're not.
*
* -- */
{
PSINGLE_LIST_ENTRY listEntry;
PDEVICE_EXTENSION_FUNCTION deviceExtensionFunction;
listEntry = DeviceExtensionParent->FunctionList.Next;
while (listEntry) {
deviceExtensionFunction =
CONTAINING_RECORD(listEntry,
DEVICE_EXTENSION_FUNCTION,
ListEntry);
ASSERT_FUNCTION(deviceExtensionFunction);
if (!deviceExtensionFunction->ResetIrp)
return FALSE;
listEntry = listEntry->Next;
}
return TRUE;
}
NTSTATUS
USBH_ResetParentPort(
IN PDEVICE_EXTENSION_PARENT DeviceExtensionParent
)
/*++
Routine Description:
Calls the parent device to reset its port.
Arguments:
Return Value:
NTSTATUS
--*/
{
NTSTATUS ntStatus, status = STATUS_SUCCESS;
PIRP irp;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
USBH_KdPrint((2,"'CompReset parent port\n"));
LOGENTRY(LOG_PNP, "CRPP", DeviceExtensionParent, 0, 0);
//
// issue a synchronous request
//
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(
IOCTL_INTERNAL_USB_RESET_PORT,
DeviceExtensionParent->TopOfStackDeviceObject,
NULL,
0,
NULL,
0,
TRUE, /* INTERNAL */
&event,
&ioStatus);
if (irp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Call the class driver to perform the operation. If the returned status
// is PENDING, wait for the request to complete.
//
ntStatus = IoCallDriver(DeviceExtensionParent->TopOfStackDeviceObject,
irp);
if (ntStatus == STATUS_PENDING) {
status = KeWaitForSingleObject(
&event,
Suspended,
KernelMode,
FALSE,
NULL);
} else {
ioStatus.Status = ntStatus;
}
ntStatus = ioStatus.Status;
return ntStatus;
}
VOID
USBH_CompositeResetPortWorker(
IN PVOID Context)
/* ++
*
* Description:
*
* Work item scheduled to process a composite port reset.
*
*
* Arguments:
*
* Return:
*
* -- */
{
PUSBH_COMP_RESET_WORK_ITEM workItemCompReset;
PSINGLE_LIST_ENTRY listEntry;
PDEVICE_EXTENSION_PARENT deviceExtensionParent;
PDEVICE_EXTENSION_FUNCTION deviceExtensionFunction;
USBH_KdPrint((2,"'Composite Reset Executing!\n"));
workItemCompReset = Context;
deviceExtensionParent = workItemCompReset->DeviceExtensionParent;
LOGENTRY(LOG_PNP, "CRW_", deviceExtensionParent, 0, 0);
// Send reset to parent (IoCallDriver)
USBH_ResetParentPort(deviceExtensionParent);
// Now, complete all Irps in list and set the Irps in the list to NULL.
USBH_KdPrint((2,"'*** (CRW) WAIT parent mutex %x\n", deviceExtensionParent));
KeWaitForSingleObject(&deviceExtensionParent->ParentMutex,
Executive,
KernelMode,
FALSE,
NULL);
USBH_KdPrint((2,"'*** (CRW) WAIT parent mutex done %x\n", deviceExtensionParent));
listEntry = deviceExtensionParent->FunctionList.Next;
while (listEntry) {
deviceExtensionFunction =
CONTAINING_RECORD(listEntry,
DEVICE_EXTENSION_FUNCTION,
ListEntry);
ASSERT_FUNCTION(deviceExtensionFunction);
// Although ResetIrp should usually be set here, we check anyway in
// case it had already been completed in USBH_CompleteAllIrpsInList.
//
if (deviceExtensionFunction->ResetIrp) {
USBH_CompleteIrp(deviceExtensionFunction->ResetIrp, STATUS_SUCCESS);
deviceExtensionFunction->ResetIrp = NULL;
}
listEntry = listEntry->Next;
}
USBH_KdPrint((2,"'*** (CRW) RELEASE parent mutex %x\n", deviceExtensionParent));
KeReleaseSemaphore(&deviceExtensionParent->ParentMutex,
LOW_REALTIME_PRIORITY,
1,
FALSE);
UsbhExFreePool(workItemCompReset);
}
NTSTATUS
USBH_FunctionPdoDispatch(
IN PDEVICE_EXTENSION_FUNCTION DeviceExtensionFunction,
IN PIRP Irp
)
/*
* Description:
*
* This function handles calls to PDOs we have created
* since we are the bottom driver for the PDO it is up
* to us to complete the irp -- with one exception.
*
* api calls to the USB stack are forwarded directly
* to the PDO for the root hub which is owned by the USB
* HC.
*
* Arguments:
*
* Return:
*
* NTSTATUS
*
* -- */
{
NTSTATUS ntStatus = STATUS_SUCCESS;
PIO_STACK_LOCATION ioStackLocation; // our stack location
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION_PARENT deviceExtensionParent;
PCOMP_RESET_TIMEOUT_CONTEXT compResetTimeoutContext = NULL;
LARGE_INTEGER dueTime;
KIRQL irql;
BOOLEAN bCompleteIrp;
USBH_KdPrint((2,"'FunctionPdoDispatch DeviceExtension %x Irp %x\n",
DeviceExtensionFunction, Irp));
deviceObject = DeviceExtensionFunction->FunctionPhysicalDeviceObject;
deviceExtensionParent = DeviceExtensionFunction->DeviceExtensionParent;
//
// Get a pointer to IoStackLocation so we can retrieve parameters.
//
ioStackLocation = IoGetCurrentIrpStackLocation(Irp);
switch (ioStackLocation->MajorFunction) {
case IRP_MJ_CREATE:
USBH_KdPrint((2,"'PARENT PDO IRP_MJ_CREATE\n"));
ntStatus = STATUS_SUCCESS;
USBH_CompleteIrp(Irp, ntStatus);
break;
case IRP_MJ_CLOSE:
USBH_KdPrint((2,"'PARENT PDO IRP_MJ_CLOSE\n"));
ntStatus = STATUS_SUCCESS;
USBH_CompleteIrp(Irp, ntStatus);
break;
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
{
ULONG ioControlCode;
USBH_KdPrint((2,"'Internal Device Control\n"));
if (deviceExtensionParent->ParentFlags & HUBFLAG_DEVICE_STOPPING) {
UsbhWarning(NULL,
"Client Device Driver is sending requests to a device that has been removed.\n",
FALSE);
ntStatus = STATUS_DEVICE_REMOVED;
USBH_CompleteIrp(Irp, ntStatus);
break;
}
ioControlCode = ioStackLocation->Parameters.DeviceIoControl.IoControlCode;
switch (ioControlCode) {
case IOCTL_INTERNAL_USB_GET_PORT_STATUS:
USBH_KdPrint((2,"'Composite GetPortStatus, pass on\n"));
ntStatus = USBH_PassIrp(Irp, deviceExtensionParent->TopOfStackDeviceObject);
break;
case IOCTL_INTERNAL_USB_RESET_PORT:
LOGENTRY(LOG_PNP, "fRES", deviceExtensionParent, 0, 0);
USBH_KdPrint((2,"'Composite Reset Requested\n"));
if (deviceExtensionParent->CurrentPowerState !=
PowerDeviceD0) {
// the child devices should not be resetting
// unless the parent is in D0
UsbhWarning(NULL,
"Parent Not in D0.\n",
TRUE);
}
if (DeviceExtensionFunction->ResetIrp) {
ntStatus = STATUS_UNSUCCESSFUL;
USBH_CompleteIrp(Irp, ntStatus);
} else {
ntStatus = STATUS_PENDING;
USBH_KdPrint((2,"'***WAIT parent mutex %x\n", deviceExtensionParent));
KeWaitForSingleObject(&deviceExtensionParent->ParentMutex,
Executive,
KernelMode,
FALSE,
NULL);
USBH_KdPrint((2,"'***WAIT parent mutex done %x\n", deviceExtensionParent));
DeviceExtensionFunction->ResetIrp = Irp;
if (USBH_ListReadyForReset(deviceExtensionParent)) {
PUSBH_COMP_RESET_WORK_ITEM workItemCompReset;
//
// "Cancel" watchdog timer.
//
// Take SpinLock here so that DPC routine won't free
// the timeout context while we write the CancelFlag
// in the timeout context.
//
KeAcquireSpinLock(&deviceExtensionParent->ParentSpinLock,
&irql);
if (deviceExtensionParent->CompResetTimeoutContext) {
compResetTimeoutContext = deviceExtensionParent->CompResetTimeoutContext;
compResetTimeoutContext->CancelFlag = TRUE;
if (KeCancelTimer(&compResetTimeoutContext->TimeoutTimer)) {
//
// We cancelled the timer before it could run. Free the context.
//
deviceExtensionParent->CompResetTimeoutContext = NULL;
UsbhExFreePool(compResetTimeoutContext);
}
}
KeReleaseSpinLock(&deviceExtensionParent->ParentSpinLock,
irql);
//
// Schedule a work item to process this reset.
//
workItemCompReset = UsbhExAllocatePool(NonPagedPool,
sizeof(USBH_COMP_RESET_WORK_ITEM));
USBH_ASSERT(workItemCompReset);
if (workItemCompReset) {
workItemCompReset->DeviceExtensionParent = deviceExtensionParent;
ExInitializeWorkItem(&workItemCompReset->WorkQueueItem,
USBH_CompositeResetPortWorker,
workItemCompReset);
LOGENTRY(LOG_PNP, "rCMP", deviceExtensionParent,
&workItemCompReset->WorkQueueItem, 0);
ExQueueWorkItem(&workItemCompReset->WorkQueueItem,
DelayedWorkQueue);
// The WorkItem is freed by USBH_CompositeResetPortWorker()
// Don't try to access the WorkItem after it is queued.
} else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
} else if (!deviceExtensionParent->CompResetTimeoutContext) {
// Start watchdog timer if not already started.
//
// When timer expires, timer routine should
// complete all Irps in the list with an error
// and clear the Irps in the list.
USBH_KdPrint((2,"'Start composite port reset timeout\n"));
compResetTimeoutContext = UsbhExAllocatePool(NonPagedPool,
sizeof(*compResetTimeoutContext));
USBH_ASSERT(compResetTimeoutContext);
if (compResetTimeoutContext) {
compResetTimeoutContext->CancelFlag = FALSE;
// Maintain links between the device extension and the
// timeout context.
deviceExtensionParent->CompResetTimeoutContext = compResetTimeoutContext;
compResetTimeoutContext->DeviceExtensionParent = deviceExtensionParent;
KeInitializeTimer(&compResetTimeoutContext->TimeoutTimer);
KeInitializeDpc(&compResetTimeoutContext->TimeoutDpc,
USBH_CompResetTimeoutDPC,
compResetTimeoutContext);
dueTime.QuadPart = -10000 * COMP_RESET_TIMEOUT;
KeSetTimer(&compResetTimeoutContext->TimeoutTimer,
dueTime,
&compResetTimeoutContext->TimeoutDpc);
} else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
if (ntStatus == STATUS_PENDING) {
IoMarkIrpPending(Irp);
} else {
USBH_CompleteIrp(Irp, ntStatus);
}
USBH_KdPrint((2,"'***RELEASE parent mutex %x\n", deviceExtensionParent));
KeReleaseSemaphore(&deviceExtensionParent->ParentMutex,
LOW_REALTIME_PRIORITY,
1,
FALSE);
}
break;
case IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO:
TEST_TRAP(); //shouldn't see this
break;
case IOCTL_INTERNAL_USB_SUBMIT_URB:
ntStatus = USBH_FunctionUrbFilter(DeviceExtensionFunction, Irp);
break;
case IOCTL_INTERNAL_USB_GET_BUS_INFO:
// this api returns some BW info that drivers
// may need -- pass it on
ntStatus = USBH_PassIrp(Irp, deviceExtensionParent->TopOfStackDeviceObject);
break;
default:
USBH_KdPrint((2,"'InternalDeviceControl IOCTL unknown pass on\n"));
ntStatus = STATUS_INVALID_PARAMETER;
USBH_CompleteIrp(Irp, ntStatus);
}
break;
}
case IRP_MJ_PNP:
USBH_KdPrint((2,"'IRP_MJ_PNP\n"));
ntStatus = USBH_FunctionPdoPnP(DeviceExtensionFunction, Irp,
ioStackLocation->MinorFunction, &bCompleteIrp);
if (bCompleteIrp) {
USBH_CompleteIrp(Irp, ntStatus);
}
break;
case IRP_MJ_POWER:
USBH_KdPrint((2,"'IRP_MJ_POWER\n"));
ntStatus = USBH_FunctionPdoPower(DeviceExtensionFunction, Irp, ioStackLocation->MinorFunction);
break;
case IRP_MJ_SYSTEM_CONTROL:
USBH_KdPrint((2,"'IRP_MJ_SYSTEM_CONTROL\n"));
ntStatus = STATUS_NOT_SUPPORTED;
USBH_CompleteIrp(Irp, ntStatus);
break;
case IRP_MJ_DEVICE_CONTROL:
//
// Note: if we ever do find a reason to handle this, be sure to
// not forward IOCTL_KS_PROPERTY / KSPROPSETID_DrmAudioStream /
// KSPROPERTY_DRMAUDIOSTREAM_SETCONTENTID to next driver! Otherwise
// this might not be DRM compliant.
//
USBH_KdBreak(("Unhandled IRP_MJ_DEVICE_CONTROL for Pdo %x Irp_Mj %x\n",
deviceObject, ioStackLocation->MajorFunction));
ntStatus = STATUS_INVALID_PARAMETER;
USBH_CompleteIrp(Irp, ntStatus);
break;
default:
// Unknown Irp, shouldn't be here.
USBH_KdBreak(("Unhandled Irp for Pdo %x Irp_Mj %x\n",
deviceObject, ioStackLocation->MajorFunction));
ntStatus = STATUS_INVALID_PARAMETER;
USBH_CompleteIrp(Irp, ntStatus);
break;
}
USBH_KdPrint((2,"' exit USBH_FunctionPdoDispatch Object %x -- Status %x\n",
deviceObject, ntStatus));
return ntStatus;
}
NTSTATUS
USBH_BuildFunctionConfigurationDescriptor(
IN PDEVICE_EXTENSION_FUNCTION DeviceExtensionFunction,
IN OUT PUCHAR Buffer,
IN ULONG BufferLength,
OUT PULONG BytesReturned
)
/*
* Description:
*
* This function creates a configuration descriptor (with all interface &
* endpoints) for a give function.
*
* Arguments:
*
* Buffer - buffer to put descriptor in
*
* BufferLength - max size of this buffer.
*
* Return:
*
* NTSTATUS
*
* -- */
{
NTSTATUS ntStatus = STATUS_SUCCESS;
PDEVICE_EXTENSION_PARENT deviceExtensionParent;
PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor;
PVOID scratch;
ULONG length, i;
PUCHAR pch;
USBH_KdPrint((2,"'USBH_BuildFunctionConfigurationDescriptor\n"));
deviceExtensionParent = DeviceExtensionFunction->DeviceExtensionParent;
//
// scratch area to build descriptor in
//
*BytesReturned = 0;
configurationDescriptor = deviceExtensionParent->ConfigurationDescriptor;
if (!configurationDescriptor || !configurationDescriptor->wTotalLength) {
return STATUS_INVALID_PARAMETER;
}
scratch = UsbhExAllocatePool(PagedPool, configurationDescriptor->
wTotalLength);
if (scratch) {
configurationDescriptor = scratch;
pch = scratch;
length = sizeof(USB_CONFIGURATION_DESCRIPTOR);
RtlCopyMemory(pch,
deviceExtensionParent->ConfigurationDescriptor,
length);
pch+=length;
//
// now copy the interfaces
//
for (i=0; i< DeviceExtensionFunction->InterfaceCount; i++) {
PFUNCTION_INTERFACE functionInterface;
functionInterface =
&DeviceExtensionFunction->FunctionInterfaceList[i];
RtlCopyMemory(pch,
functionInterface->InterfaceDescriptor,
functionInterface->InterfaceDescriptorLength);
pch+=functionInterface->InterfaceDescriptorLength;
length+=functionInterface->InterfaceDescriptorLength;
}
configurationDescriptor->bNumInterfaces = (UCHAR) DeviceExtensionFunction->InterfaceCount;
configurationDescriptor->wTotalLength = (USHORT) length;
//
// now copy what we can in to the user buffer
//
if (BufferLength >= configurationDescriptor->wTotalLength) {
*BytesReturned = configurationDescriptor->wTotalLength;
} else {
*BytesReturned = BufferLength;
}
RtlCopyMemory(Buffer,
scratch,
*BytesReturned);
USBH_KdBreak(("'USBH_BuildFunctionConfigurationDescriptor, buffer = %x scratch = %x\n",
Buffer, scratch));
UsbhExFreePool(scratch);
} else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
return ntStatus;
}
VOID
USBH_ParentCompleteFunctionWakeIrps(
IN PDEVICE_EXTENSION_PARENT DeviceExtensionParent,
IN NTSTATUS NtStatus
)
/*++
Routine Description:
Called when a wake irp completes for a hub
Propagates the wake irp completion to all the function (children).
Arguments:
DeviceObject - Pointer to the device object for the class device.
Return Value:
The function value is the final status from the operation.
--*/
{
PDEVICE_EXTENSION_FUNCTION deviceExtensionFunction;
PSINGLE_LIST_ENTRY listEntry;
PIRP irp;
KIRQL irql;
LONG pendingFunctionWWs;
ULONG i;
PIRP irpArray[128]; // Limited to 127 functions in the list.
LOGENTRY(LOG_PNP, "fWWc", DeviceExtensionParent, NtStatus, 0);
//
// Here we are walking the list of child PDOs, which should never change.
// (The number of interfaces on a USB device is fixed so long as the parent
// is here the number of children stay constant.)
//
// Therefore we need no protection for parent->FunctionList.
//
// Wrongo! The list may not change, but the WW IRPs attributed to the
// list can, so we must take the spinlock here.
IoAcquireCancelSpinLock(&irql);
listEntry = DeviceExtensionParent->FunctionList.Next;
i = 0;
while (listEntry) {
deviceExtensionFunction =
CONTAINING_RECORD(listEntry,
DEVICE_EXTENSION_FUNCTION,
ListEntry);
irp = deviceExtensionFunction->WaitWakeIrp;
deviceExtensionFunction->WaitWakeIrp = NULL;
if (irp) {
IoSetCancelRoutine(irp, NULL);
pendingFunctionWWs =
InterlockedDecrement(&DeviceExtensionParent->NumberFunctionWakeIrps);
if (0 == pendingFunctionWWs) {
LOGENTRY(LOG_PNP, "fWWx", DeviceExtensionParent,
DeviceExtensionParent->PendingWakeIrp, 0);
DeviceExtensionParent->PendingWakeIrp = NULL;
DeviceExtensionParent->ParentFlags &= ~HUBFLAG_PENDING_WAKE_IRP;
}
irpArray[i++] = irp;
}
listEntry = listEntry->Next;
}
irpArray[i] = NULL; // Terminate array
IoReleaseCancelSpinLock(irql);
USBH_ASSERT(DeviceExtensionParent->PendingWakeIrp == NULL);
// Ok, we have queued all the function wake IRPs and have released the
// cancel spinlock. Let's complete all the IRPs.
i = 0;
while (irpArray[i]) {
USBH_KdPrint((1,"'completing function WaitWake irp(%x) for PARENT VID %x, PID %x\n\n",
NtStatus,
DeviceExtensionParent->DeviceDescriptor.idVendor, \
DeviceExtensionParent->DeviceDescriptor.idProduct));
irpArray[i]->IoStatus.Status = NtStatus;
PoStartNextPowerIrp(irpArray[i]);
IoCompleteRequest(irpArray[i], IO_NO_INCREMENT);
i++;
}
}
NTSTATUS
USBH_ParentPoRequestD0Completion(
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PVOID Context,
IN PIO_STATUS_BLOCK IoStatus
)
/*++
Routine Description:
Called when a wake irp completes for a hub
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.
--*/
{
NTSTATUS ntStatus;
PDEVICE_EXTENSION_PARENT deviceExtensionParent = Context;
ntStatus = IoStatus->Status;
USBH_KdPrint((1,"'WaitWake D0 completion(%x) for PARENT VID %x, PID %x\n",
ntStatus,
deviceExtensionParent->DeviceDescriptor.idVendor, \
deviceExtensionParent->DeviceDescriptor.idProduct));
LOGENTRY(LOG_PNP, "pWD0", deviceExtensionParent,
deviceExtensionParent->PendingWakeIrp,
0);
//
// Device has been powered on. Now we must complete the function that
// caused the parent to awake.
//
// Since of course we cannot tell them apart we must complete all function
// WW Irps.
//
USBH_ParentCompleteFunctionWakeIrps(deviceExtensionParent, STATUS_SUCCESS);
return ntStatus;
}
NTSTATUS
USBH_ParentWaitWakeIrpCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PVOID Context,
IN PIO_STATUS_BLOCK IoStatus
)
/*++
Routine Description:
Called when a wake irp completes for a composite device
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.
--*/
{
NTSTATUS ntStatus;
PDEVICE_EXTENSION_PARENT deviceExtensionParent = Context;
POWER_STATE powerState;
ntStatus = IoStatus->Status;
USBH_KdPrint((1,"'WaitWake completion(%x) for PARENT VID %x, PID %x\n",
ntStatus,
deviceExtensionParent->DeviceDescriptor.idVendor, \
deviceExtensionParent->DeviceDescriptor.idProduct));
LOGENTRY(LOG_PNP, "pWWc", deviceExtensionParent,
ntStatus,
0);
// first we power our device back on
if (NT_SUCCESS(ntStatus)) {
powerState.DeviceState = PowerDeviceD0;
PoRequestPowerIrp(deviceExtensionParent->PhysicalDeviceObject,
IRP_MN_SET_POWER,
powerState,
USBH_ParentPoRequestD0Completion,
deviceExtensionParent,
NULL);
// USBH_ParentPoRequestD0Completion must complete the
// wake irp
ntStatus = STATUS_SUCCESS;
} else {
// complete the child wake requests with an error
USBH_ParentCompleteFunctionWakeIrps(deviceExtensionParent,
ntStatus);
}
return ntStatus;
}
NTSTATUS
USBH_ParentSubmitWaitWakeIrp(
IN PDEVICE_EXTENSION_PARENT DeviceExtensionParent
)
/*++
Routine Description:
called when a child Pdo is enabled for wakeup, this
function allocates a wait wake irp and passes it to
the parents PDO.
Arguments:
Return Value:
--*/
{
PIRP irp;
NTSTATUS ntStatus;
POWER_STATE powerState;
USBH_ASSERT (NULL == DeviceExtensionParent->PendingWakeIrp);
LOGENTRY(LOG_PNP, "prWI", DeviceExtensionParent,
0,
0);
USBH_ASSERT(DeviceExtensionParent->PendingWakeIrp == NULL);
DeviceExtensionParent->ParentFlags |= HUBFLAG_PENDING_WAKE_IRP;
powerState.DeviceState = DeviceExtensionParent->SystemWake;
ntStatus = PoRequestPowerIrp(DeviceExtensionParent->PhysicalDeviceObject,
IRP_MN_WAIT_WAKE,
powerState,
USBH_ParentWaitWakeIrpCompletion,
DeviceExtensionParent,
&irp);
if (ntStatus == STATUS_PENDING) {
if (DeviceExtensionParent->ParentFlags & HUBFLAG_PENDING_WAKE_IRP) {
DeviceExtensionParent->PendingWakeIrp = irp;
}
}
USBH_KdPrint((2,
"'ntStatus from PoRequestPowerIrp for wait_wake to parent PDO = 0x%x\n", ntStatus));
return ntStatus;
}