|
|
/*++
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; }
|