3501 lines
104 KiB
C
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;
|
|
}
|
|
|