Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3608 lines
97 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
usbport.c
Abstract:
Port driver for USB host controllers
Environment:
kernel mode only
Notes:
Revision History:
6-20-99 : created
--*/
#include "common.h"
// paged functions
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, USBPORT_QueryCapabilities)
#pragma alloc_text(PAGE, USBPORT_CalculateUsbBandwidth)
#pragma alloc_text(PAGE, USBPORT_ReadWriteConfigSpace)
#pragma alloc_text(PAGE, USBPORT_ReadWriteConfigSpace)
#pragma alloc_text(PAGE, USBPORT_InTextmodeSetup)
#pragma alloc_text(PAGE, USBPORT_IsCompanionController)
#pragma alloc_text(PAGE, USBPORT_GetHcFlavor)
#pragma alloc_text(PAGE, USBPORT_ComputeAllocatedBandwidth)
#endif
// non paged functions
// USBPORT_StopDevice
// USBPORT_StartDevice
// USBPORT_CompleteIrp
// USBPORT_Dispatch
// USBPORT_TrackPendingRequest
// USBPORT_GetConfigValue
// USBPORT_DeferIrpCompletion
// USBPORT_AllocPool
NTSTATUS
DriverEntry(
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
Installable driver initialization entry point.
This entry point is called directly by the I/O system.
Arguments:
DriverObject - pointer to the driver object
RegistryPath - pointer to a unicode string representing the path
to driver-specific key in the registry
Return Value:
NT status code
--*/
{
// BUGBUG
// This function is never called
return STATUS_SUCCESS;
}
NTSTATUS
USBPORT_StopDevice(
PDEVICE_OBJECT FdoDeviceObject,
BOOLEAN HardwarePresent
)
/*++
Routine Description:
Stop the port and miniport
Arguments:
DeviceObject - DeviceObject of the controller to stop
Return Value:
NT status code.
--*/
{
PDEVICE_EXTENSION devExt;
ULONG deviceCount;
BOOLEAN haveWork;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'stpD', 0, 0, HardwarePresent);
DEBUG_BREAK();
USBPORT_FlushCahcedRegistryKeys(FdoDeviceObject);
// the reoot hub and ALL devices should be 'stopped' at
// this point. We should have no device handles in the
// system, if we do these are orphaned and we need to
// remove them now.
deviceCount = USBPORT_GetDeviceCount(FdoDeviceObject);
while (deviceCount != 0 ) {
PUSBD_DEVICE_HANDLE zombieDeviceHandle;
KIRQL irql;
PLIST_ENTRY listEntry;
USBPORT_KdPrint((0, "%d zombie device handles on STOP\n",
deviceCount));
KeAcquireSpinLock(&devExt->Fdo.DevHandleListSpin.sl, &irql);
USBPORT_ASSERT(!IsListEmpty(&devExt->Fdo.DeviceHandleList));
listEntry = devExt->Fdo.DeviceHandleList.Flink;
zombieDeviceHandle = (PUSBD_DEVICE_HANDLE) CONTAINING_RECORD(
listEntry,
struct _USBD_DEVICE_HANDLE,
ListEntry);
ASSERT_DEVICE_HANDLE(zombieDeviceHandle)
KeReleaseSpinLock(&devExt->Fdo.DevHandleListSpin.sl, irql);
USBPORT_KdPrint((0, "deleting zombie handle %x\n",
zombieDeviceHandle));
DEBUG_BREAK();
USBPORT_RemoveDevice(zombieDeviceHandle,
FdoDeviceObject,
0);
deviceCount = USBPORT_GetDeviceCount(FdoDeviceObject);
}
// make sure all lists are empty and all endpoints
// are freed before terminating the worker thread
do {
KIRQL irql;
haveWork = FALSE;
KeAcquireSpinLock(&devExt->Fdo.EpClosedListSpin.sl, &irql);
if (!IsListEmpty(&devExt->Fdo.EpClosedList)) {
haveWork = TRUE;
}
KeReleaseSpinLock(&devExt->Fdo.EpClosedListSpin.sl, irql);
KeAcquireSpinLock(&devExt->Fdo.EndpointListSpin.sl, &irql);
if (!IsListEmpty(&devExt->Fdo.GlobalEndpointList)) {
haveWork = TRUE;
}
KeReleaseSpinLock(&devExt->Fdo.EndpointListSpin.sl, irql);
if (haveWork) {
USBPORT_Wait(FdoDeviceObject, 10);
}
} while (haveWork);
// all of our lists should be empty
USBPORT_ASSERT(IsListEmpty(&devExt->Fdo.EpClosedList) == TRUE);
USBPORT_ASSERT(IsListEmpty(&devExt->Fdo.MapTransferList) == TRUE);
USBPORT_ASSERT(IsListEmpty(&devExt->Fdo.DoneTransferList) == TRUE);
USBPORT_ASSERT(IsListEmpty(&devExt->Fdo.EpStateChangeList) == TRUE);
USBPORT_ASSERT(IsListEmpty(&devExt->Fdo.EpClosedList) == TRUE);
// kill our system thread
USBPORT_TerminateWorkerThread(FdoDeviceObject);
if (devExt->Fdo.MpStateFlags & MP_STATE_STARTED) {
// stop the miniport, disable interrupts
// if hw is not present then it can't be interrupting
// now can it.
if (HardwarePresent) {
MP_DisableInterrupts(FdoDeviceObject, devExt);
}
devExt->Fdo.MpStateFlags &= ~MP_STATE_STARTED;
MP_StopController(devExt, HardwarePresent);
}
// kill our deadman timer
USBPORT_StopDM_Timer(FdoDeviceObject);
// see if we have an interrupt
// if so disconnect it
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_IRQ_CONNECTED)) {
// fortunately this cannot fail
IoDisconnectInterrupt(devExt->Fdo.InterruptObject);
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'IOCd', 0, 0, 0);
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_IRQ_CONNECTED);
}
USBPORT_FreeIrpTable(FdoDeviceObject,
devExt->PendingTransferIrpTable);
USBPORT_FreeIrpTable(FdoDeviceObject,
devExt->ActiveTransferIrpTable);
// free any common buffer we allocated for the miniport
if (devExt->Fdo.ControllerCommonBuffer != NULL) {
USBPORT_HalFreeCommonBuffer(FdoDeviceObject,
devExt->Fdo.ControllerCommonBuffer);
devExt->Fdo.ControllerCommonBuffer = NULL;
}
if (devExt->Fdo.ScratchCommonBuffer != NULL) {
USBPORT_HalFreeCommonBuffer(FdoDeviceObject,
devExt->Fdo.ScratchCommonBuffer);
devExt->Fdo.ScratchCommonBuffer = NULL;
}
if (devExt->Fdo.AdapterObject) {
(devExt->Fdo.AdapterObject->DmaOperations->PutDmaAdapter)
(devExt->Fdo.AdapterObject);
devExt->Fdo.AdapterObject = NULL;
}
// delete the HCD symbolic link
if (TEST_FLAG(devExt->Flags, USBPORT_FLAG_SYM_LINK)) {
USBPORT_SymbolicLink(FALSE,
devExt,
devExt->Fdo.PhysicalDeviceObject,
(LPGUID)&GUID_CLASS_USB_HOST_CONTROLLER);
}
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_LEGACY_SYM_LINK)) {
IoDeleteSymbolicLink(&devExt->Fdo.LegacyLinkUnicodeString);
RtlFreeUnicodeString(&devExt->Fdo.LegacyLinkUnicodeString);
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_LEGACY_SYM_LINK);
}
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_FDO_REGISTERED)) {
if (USBPORT_IS_USB20(devExt)) {
USBPORT_DeregisterUSB2fdo(FdoDeviceObject);
} else {
USBPORT_DeregisterUSB1fdo(FdoDeviceObject);
}
}
// successful stop clears the 'started flag'
CLEAR_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STARTED);
return STATUS_SUCCESS;
}
NTSTATUS
USBPORT_DeferIrpCompletion(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PKEVENT event = Context;
KeSetEvent(event,
1,
FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
USBPORT_QueryCapabilities(
PDEVICE_OBJECT FdoDeviceObject,
PDEVICE_CAPABILITIES DeviceCapabilities
)
/*++
Routine Description:
Arguments:
Return Value:
ntstatus
--*/
{
PIO_STACK_LOCATION nextStack;
PIRP irp;
NTSTATUS ntStatus;
KEVENT event;
PDEVICE_EXTENSION devExt;
PAGED_CODE();
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
// init the caps structure before calldown
RtlZeroMemory(DeviceCapabilities, sizeof(DEVICE_CAPABILITIES));
DeviceCapabilities->Size = sizeof(DEVICE_CAPABILITIES);
DeviceCapabilities->Version = 1;
DeviceCapabilities->Address = -1;
DeviceCapabilities->UINumber = -1;
irp =
IoAllocateIrp(devExt->Fdo.TopOfStackDeviceObject->StackSize, FALSE);
if (!irp) {
USBPORT_KdPrint((1, "failed to allocate Irp\n"));
DEBUG_BREAK();
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
} else {
// All PnP IRP's need the Status field initialized
// to STATUS_NOT_SUPPORTED before calldown
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
nextStack = IoGetNextIrpStackLocation(irp);
USBPORT_ASSERT(nextStack != NULL);
nextStack->MajorFunction = IRP_MJ_PNP;
nextStack->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IoSetCompletionRoutine(irp,
USBPORT_DeferIrpCompletion,
&event,
TRUE,
TRUE,
TRUE);
nextStack->Parameters.DeviceCapabilities.Capabilities =
DeviceCapabilities;
ntStatus = IoCallDriver(devExt->Fdo.TopOfStackDeviceObject,
irp);
if (ntStatus == STATUS_PENDING) {
// wait for irp to complete
KeWaitForSingleObject(
&event,
Suspended,
KernelMode,
FALSE,
NULL);
ntStatus = irp->IoStatus.Status;
}
}
IoFreeIrp(irp);
return ntStatus;
}
NTSTATUS
USBPORT_StartDevice(
PDEVICE_OBJECT FdoDeviceObject,
PHC_RESOURCES HcResources
)
/*++
Routine Description:
Stop the port and miiport
Arguments:
DeviceObject - DeviceObject of the controller to stop
Return Value:
NT status code.
--*/
{
PDEVICE_EXTENSION devExt;
NTSTATUS ntStatus = STATUS_SUCCESS;
USB_MINIPORT_STATUS mpStatus;
DEVICE_DESCRIPTION deviceDescription;
ULONG mpOptionFlags, i, legsup;
ULONG globalDisableSS = 0;
ULONG globalDisableCCDetect = 0;
ULONG enIdleEndpointSupport = 0;
ULONG hactionFlag;
ULONG tmpLength;
BOOLEAN isCC;
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'SRT>', FdoDeviceObject, 0, 0);
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
//
// hardcoded initialization, not failable
//
USBPORT_InitializeSpinLock(&devExt->Fdo.CoreFunctionSpin, 'CRS+', 'CRS-');
USBPORT_InitializeSpinLock(&devExt->Fdo.MapTransferSpin, 'MPS+', 'MPS-');
USBPORT_InitializeSpinLock(&devExt->Fdo.DoneTransferSpin, 'DNS+', 'DNS-');
KeInitializeSpinLock(&devExt->Fdo.EndpointListSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.EpStateChangeListSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.EpClosedListSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.TtEndpointListSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.DevHandleListSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.HcSyncSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.RootHubSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.WorkerThreadSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.PowerSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.DM_TimerSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.PendingIrpSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.WakeIrpSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.IdleIrpSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.BadRequestSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.IsrDpcSpin.sl);
USBPORT_InitializeSpinLock(&devExt->Fdo.ActiveTransferIrpSpin, 'ALS+', 'ALS-');
KeInitializeSpinLock(&devExt->Fdo.PendingTransferIrpSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.StatCounterSpin.sl);
KeInitializeSpinLock(&devExt->Fdo.HcPendingWakeIrpSpin.sl);
devExt->Fdo.LastSystemSleepState = PowerSystemUnspecified;
devExt->Fdo.HcWakeState = HCWAKESTATE_DISARMED;
// this event will be signalled when the wake irp completes
KeInitializeEvent(&devExt->Fdo.HcPendingWakeIrpEvent,
NotificationEvent, TRUE);
devExt->Fdo.BadReqFlushThrottle = 0;
switch(USBPORT_DetectOSVersion(FdoDeviceObject)) {
case Win2K:
// need a long delay for win2k hidclass
USBPORT_KdPrint((0, "We are running on Win2k!\n"));
devExt->Fdo.BadReqFlushThrottle = 5;
globalDisableSS = 1;
}
devExt->PendingTransferIrpTable = NULL;
devExt->ActiveTransferIrpTable = NULL;
// Always start with the default address (0) assigned.
// Address array has one bit for every address 0..127
devExt->Fdo.AddressList[0] = 1;
devExt->Fdo.AddressList[1] =
devExt->Fdo.AddressList[2] =
devExt->Fdo.AddressList[3] = 0;
KeInitializeDpc(&devExt->Fdo.TransferFlushDpc,
USBPORT_TransferFlushDpc,
FdoDeviceObject);
KeInitializeDpc(&devExt->Fdo.SurpriseRemoveDpc,
USBPORT_SurpriseRemoveDpc,
FdoDeviceObject);
KeInitializeDpc(&devExt->Fdo.HcResetDpc,
USBPORT_HcResetDpc,
FdoDeviceObject);
KeInitializeDpc(&devExt->Fdo.HcWakeDpc,
USBPORT_HcWakeDpc,
FdoDeviceObject);
KeInitializeDpc(&devExt->Fdo.IsrDpc,
USBPORT_IsrDpc,
FdoDeviceObject);
devExt->Fdo.DmaBusy = -1;
devExt->Fdo.WorkerDpc = -1;
// set up adapter object
RtlZeroMemory(&deviceDescription, sizeof(deviceDescription));
deviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
deviceDescription.Master = TRUE;
deviceDescription.ScatterGather = TRUE;
deviceDescription.Dma32BitAddresses = TRUE;
deviceDescription.InterfaceType = PCIBus;
deviceDescription.DmaWidth = Width32Bits;
deviceDescription.DmaSpeed = Compatible;
deviceDescription.MaximumLength = (ULONG)-1;
devExt->Fdo.NumberOfMapRegisters = (ULONG)-1;
devExt->Fdo.AdapterObject = NULL;
// miniport common buffer
devExt->Fdo.ControllerCommonBuffer = NULL;
// fetch the global BIOS hacks from the registry
USBPORT_GetDefaultBIOS_X(FdoDeviceObject,
&devExt->Fdo.BiosX,
&globalDisableSS,
&globalDisableCCDetect,
&enIdleEndpointSupport);
if (globalDisableSS) {
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_DISABLE_SS);
}
// check reg for SS flag, note that miniport can
// stil override
if (USBPORT_SelectiveSuspendEnabled(FdoDeviceObject) &&
!globalDisableSS) {
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_RH_CAN_SUSPEND);
}
//
// failable initailization
//
// make sure we got all the resources we need
// note that we check here becuase problems may occurr
// if we attempt to connect the interrupt without all
// the necessary resources
mpOptionFlags = REGISTRATION_PACKET(devExt).OptionFlags;
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'mpOP',
mpOptionFlags, 0, HcResources->Flags);
if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_POLL_CONTROLLER)) {
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_POLL_CONTROLLER);
}
if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_POLL_IN_SUSPEND)) {
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_POLL_IN_SUSPEND);
}
if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_NO_SS)) {
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_RH_CAN_SUSPEND);
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_DISABLE_SS);
}
// make sure we got an IRQ
if ( (mpOptionFlags & USB_MINIPORT_OPT_NEED_IRQ) &&
!(HcResources->Flags & HCR_IRQ)) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'noIQ', 0, 0, 0);
goto USBPORT_StartDevice_Done;
}
if ( (mpOptionFlags & USB_MINIPORT_OPT_NEED_IOPORT) &&
!(HcResources->Flags & HCR_IO_REGS)) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'noRG', 0, 0, 0);
goto USBPORT_StartDevice_Done;
}
if ( (mpOptionFlags & USB_MINIPORT_OPT_NEED_MEMORY) &&
!(HcResources->Flags & HCR_MEM_REGS)) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'noMM', 0, 0, 0);
goto USBPORT_StartDevice_Done;
}
// simulates a failry common scenario where PnP gives us
// the wrong resources.
TEST_PATH(ntStatus, FAILED_NEED_RESOURCE);
if (!NT_SUCCESS(ntStatus)) {
goto USBPORT_StartDevice_Done;
}
if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_NO_PNP_RESOURCES)) {
TEST_TRAP();
devExt->Fdo.PciVendorId = 0;
devExt->Fdo.PciDeviceId = 0;
devExt->Fdo.PciRevisionId = 0;
devExt->Fdo.PciClass = 0;
devExt->Fdo.PciSubClass = 0;
devExt->Fdo.PciProgIf = 0;
} else {
// fetch the Dev Prod and Rev IDs from config space
ULONG ClassCodeRev;
ntStatus = USBPORT_ReadConfigSpace(
FdoDeviceObject,
&devExt->Fdo.PciVendorId,
0,
sizeof(devExt->Fdo.PciVendorId));
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'Gvid',
devExt->Fdo.PciVendorId, 0, ntStatus);
if (!NT_SUCCESS(ntStatus)) {
goto USBPORT_StartDevice_Done;
}
ntStatus = USBPORT_ReadConfigSpace(
FdoDeviceObject,
&devExt->Fdo.PciDeviceId,
2,
sizeof(devExt->Fdo.PciDeviceId));
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'Gdid',
devExt->Fdo.PciDeviceId, 0, ntStatus);
if (!NT_SUCCESS(ntStatus)) {
goto USBPORT_StartDevice_Done;
}
ntStatus = USBPORT_ReadConfigSpace(
FdoDeviceObject,
&ClassCodeRev,
8,
sizeof(ClassCodeRev));
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'Grev',
ClassCodeRev, 0, ntStatus);
if (!NT_SUCCESS(ntStatus)) {
goto USBPORT_StartDevice_Done;
}
devExt->Fdo.PciRevisionId = (UCHAR)ClassCodeRev;
devExt->Fdo.PciClass = (UCHAR)(ClassCodeRev >> 24);
devExt->Fdo.PciSubClass = (UCHAR)(ClassCodeRev >> 16);
devExt->Fdo.PciProgIf = (UCHAR)(ClassCodeRev >> 8);
USBPORT_ASSERT(devExt->Fdo.PciClass == PCI_CLASS_SERIAL_BUS_CTLR);
USBPORT_ASSERT(devExt->Fdo.PciSubClass == PCI_SUBCLASS_SB_USB);
USBPORT_ASSERT(devExt->Fdo.PciProgIf == 0x00 ||
devExt->Fdo.PciProgIf == 0x10 ||
devExt->Fdo.PciProgIf == 0x20);
}
USBPORT_KdPrint((1, "USB Controller VID %x DEV %x REV %x\n",
devExt->Fdo.PciVendorId,
devExt->Fdo.PciDeviceId,
devExt->Fdo.PciRevisionId));
// set the HW flavor so that the miniports and possibly
// the port know what hacks to do to make the controller
// be good.
HcResources->ControllerFlavor =
devExt->Fdo.HcFlavor =
USBPORT_GetHcFlavor(FdoDeviceObject,
devExt->Fdo.PciVendorId,
devExt->Fdo.PciDeviceId,
devExt->Fdo.PciRevisionId);
// check for Fredbhs global idle endpoint support, if the global key is set
// the write the instanced key
USBPORT_KdPrint((1, "Idle Endpoint Support %d%x\n",enIdleEndpointSupport));
USBPORT_SetRegistryKeyValueForPdo(devExt->Fdo.PhysicalDeviceObject,
USBPORT_SW_BRANCH,
REG_DWORD,
EN_IDLE_ENDPOINT_SUPPORT,
sizeof(EN_IDLE_ENDPOINT_SUPPORT),
&enIdleEndpointSupport,
sizeof(enIdleEndpointSupport));
//
// If this is an OHCI or UHCI controller check if it is a companion
// controller.
//
if ((devExt->Fdo.PciClass == PCI_CLASS_SERIAL_BUS_CTLR) &&
(devExt->Fdo.PciSubClass == PCI_SUBCLASS_SB_USB) &&
(devExt->Fdo.PciProgIf < 0x20) &&
NT_SUCCESS(USBPORT_IsCompanionController(FdoDeviceObject, &isCC)))
{
if (isCC)
{
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_IS_CC);
}
else
{
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_IS_CC);
}
}
/* global flag overrides registry settings from inf and
any hard coded detection
*/
if (globalDisableCCDetect) {
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_IS_CC);
}
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_IS_CC))
{
USBPORT_KdPrint((1,"Is CC %04X %04X %02X %02X %02X %02X\n",
devExt->Fdo.PciVendorId,
devExt->Fdo.PciDeviceId,
devExt->Fdo.PciRevisionId,
devExt->Fdo.PciClass,
devExt->Fdo.PciSubClass,
devExt->Fdo.PciProgIf
));
}
else
{
USBPORT_KdPrint((1,"Is not CC %04X %04X %02X %02X %02X %02X\n",
devExt->Fdo.PciVendorId,
devExt->Fdo.PciDeviceId,
devExt->Fdo.PciRevisionId,
devExt->Fdo.PciClass,
devExt->Fdo.PciSubClass,
devExt->Fdo.PciProgIf
));
}
// detect companion controller by registry method
if (USBPORT_GetRegistryKeyValueForPdo(devExt->HcFdoDeviceObject,
devExt->Fdo.PhysicalDeviceObject,
USBPORT_HW_BRANCH,
HACTION_KEY,
sizeof(HACTION_KEY),
&hactionFlag,
sizeof(hactionFlag)) != STATUS_SUCCESS) {
// default is to NO_WAIT on HC if no key is found, UNLESS
// textmode setup is currently running.
//
// note: the goatpack default is to wait
if (USBPORT_InTextmodeSetup())
{
USBPORT_KdPrint((1, "Textmode Setup Detected: hactionFlag=1\n"));
hactionFlag = 1;
}
else
{
USBPORT_KdPrint((1, "Textmode Setup Not Detected: hactionFlag=0\n"));
hactionFlag = 0;
}
}
if (hactionFlag == 0 ) {
USBPORT_KdPrint((1, "Detected HACTION 0 -- OK to enumerate\n"));
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_CC_ENUM_OK);
}
devExt->Fdo.TotalBusBandwidth =
REGISTRATION_PACKET(devExt).BusBandwidth;
// allow a registry override of the total BW for
// this bus
{
ULONG busBandwidth = devExt->Fdo.TotalBusBandwidth;
USBPORT_GetRegistryKeyValueForPdo(devExt->HcFdoDeviceObject,
devExt->Fdo.PhysicalDeviceObject,
USBPORT_SW_BRANCH,
BW_KEY,
sizeof(BW_KEY),
&busBandwidth,
sizeof(busBandwidth));
if (busBandwidth != devExt->Fdo.TotalBusBandwidth) {
USBPORT_KdPrint((0, "Warning: Registry Override of bus bandwidth\n"));
devExt->Fdo.TotalBusBandwidth = busBandwidth;
}
}
// init the bandwidth table
for (i=0; i<USBPORT_MAX_INTEP_POLLING_INTERVAL; i++) {
devExt->Fdo.BandwidthTable[i]
= devExt->Fdo.TotalBusBandwidth -
devExt->Fdo.TotalBusBandwidth/10;
}
// allocate internal irp tracking tables
ALLOC_POOL_Z(devExt->PendingTransferIrpTable, NonPagedPool,
sizeof(USBPORT_IRP_TABLE));
ALLOC_POOL_Z(devExt->ActiveTransferIrpTable, NonPagedPool,
sizeof(USBPORT_IRP_TABLE));
if (devExt->PendingTransferIrpTable == NULL ||
devExt->ActiveTransferIrpTable == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto USBPORT_StartDevice_Done;
}
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'irpT',
devExt->PendingTransferIrpTable,
devExt->ActiveTransferIrpTable, 0);
ntStatus = USBPORT_CreateWorkerThread(FdoDeviceObject);
if (!NT_SUCCESS(ntStatus)) {
goto USBPORT_StartDevice_Done;
}
// query our device caps from the PDO and save them
ntStatus =
USBPORT_QueryCapabilities(FdoDeviceObject,
&devExt->DeviceCapabilities);
if (!NT_SUCCESS(ntStatus)) {
goto USBPORT_StartDevice_Done;
}
ntStatus = IoGetDeviceProperty(devExt->Fdo.PhysicalDeviceObject,
DevicePropertyBusNumber,
sizeof(devExt->Fdo.BusNumber),
&devExt->Fdo.BusNumber,
&tmpLength);
if (!NT_SUCCESS(ntStatus)) {
goto USBPORT_StartDevice_Done;
}
// extract device and function number from the caps
devExt->Fdo.BusDevice = devExt->DeviceCapabilities.Address>>16;
devExt->Fdo.BusFunction = devExt->DeviceCapabilities.Address & 0x0000ffff;
USBPORT_KdPrint((1, "'BUS %x, device %x, function %x\n",
devExt->Fdo.BusNumber, devExt->Fdo.BusDevice, devExt->Fdo.BusFunction));
if (!NT_SUCCESS(ntStatus)) {
goto USBPORT_StartDevice_Done;
}
// this will modify the DeviceCaps reported by the BIOS
USBPORT_ApplyBIOS_X(FdoDeviceObject,
&devExt->DeviceCapabilities,
devExt->Fdo.BiosX);
// got the capabilities, compute the power state table
USBPORT_ComputeHcPowerStates(
FdoDeviceObject,
&devExt->DeviceCapabilities,
&devExt->Fdo.HcPowerStateTbl);
//
// Create our adapter object for a standard USB PCI adapter
//
if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_NO_PNP_RESOURCES)) {
devExt->Fdo.AdapterObject = NULL;
} else {
devExt->Fdo.AdapterObject =
IoGetDmaAdapter(devExt->Fdo.PhysicalDeviceObject,
&deviceDescription,
&devExt->Fdo.NumberOfMapRegisters);
if (devExt->Fdo.AdapterObject == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto USBPORT_StartDevice_Done;
}
}
// see if we have an interrupt
if (HcResources->Flags & HCR_IRQ) {
ntStatus = IoConnectInterrupt(
&devExt->Fdo.InterruptObject,
(PKSERVICE_ROUTINE) USBPORT_InterruptService,
(PVOID) FdoDeviceObject,
(PKSPIN_LOCK)NULL,
HcResources->InterruptVector,
HcResources->InterruptLevel,
HcResources->InterruptLevel,
HcResources->InterruptMode,
HcResources->ShareIRQ,
HcResources->Affinity,
FALSE); // BUGBUG FloatingSave, this is configurable
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'IOCi', 0, 0, ntStatus);
if (NT_SUCCESS(ntStatus)) {
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_IRQ_CONNECTED);
} else {
goto USBPORT_StartDevice_Done;
}
}
if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_NO_PNP_RESOURCES)) {
REGISTRATION_PACKET(devExt).CommonBufferBytes = 0;
}
if (REGISTRATION_PACKET(devExt).CommonBufferBytes) {
PUSBPORT_COMMON_BUFFER commonBuffer;
ULONG bytesNeeded =
REGISTRATION_PACKET(devExt).CommonBufferBytes;
commonBuffer =
USBPORT_HalAllocateCommonBuffer(FdoDeviceObject,
bytesNeeded);
if (commonBuffer == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto USBPORT_StartDevice_Done;
} else {
devExt->Fdo.ControllerCommonBuffer = commonBuffer;
HcResources->CommonBufferVa =
commonBuffer->MiniportVa;
HcResources->CommonBufferPhys =
commonBuffer->MiniportPhys;
}
// allocate some scratch space
bytesNeeded = USB_PAGE_SIZE - sizeof(USBPORT_COMMON_BUFFER);
commonBuffer =
USBPORT_HalAllocateCommonBuffer(FdoDeviceObject,
bytesNeeded);
if (commonBuffer == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto USBPORT_StartDevice_Done;
} else {
devExt->Fdo.ScratchCommonBuffer = commonBuffer;
}
} else {
// no common buffer for this controller
devExt->Fdo.ControllerCommonBuffer = NULL;
}
// zero the controller extension
RtlZeroMemory(devExt->Fdo.MiniportDeviceData,
devExt->Fdo.MiniportDriver->RegistrationPacket.DeviceDataSize);
// attempt to start the miniport
USBPORT_FlushCahcedRegistryKeys(FdoDeviceObject);
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_ON_PNP_THREAD);
MP_StartController(devExt, HcResources, mpStatus);
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_ON_PNP_THREAD);
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'mpST', mpStatus, 0, 0);
if (HcResources->DetectedLegacyBIOS) {
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_LEGACY_BIOS);
legsup = 1;
} else {
legsup = 0;
}
USBPORT_SetRegistryKeyValueForPdo(
devExt->Fdo.PhysicalDeviceObject,
USBPORT_HW_BRANCH,
REG_DWORD,
SYM_LEGSUP_KEY,
sizeof(SYM_LEGSUP_KEY),
&legsup,
sizeof(legsup));
// since common buff signature is at the end this will detect if
// the miniport overwrites the allocated block
#if DBG
if (devExt->Fdo.ControllerCommonBuffer != NULL) {
ASSERT_COMMON_BUFFER(devExt->Fdo.ControllerCommonBuffer);
}
#endif
if (mpStatus == USBMP_STATUS_SUCCESS) {
// controller started, set flag and begin passing
// interrupts to the miniport
SET_FLAG(devExt->Fdo.MpStateFlags, MP_STATE_STARTED);
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'eIRQ', mpStatus, 0, 0);
MP_EnableInterrupts(devExt);
} else {
// error occured, disconnect interrupt now
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_IRQ_CONNECTED)) {
IoDisconnectInterrupt(devExt->Fdo.InterruptObject);
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_IRQ_CONNECTED);
}
// free memory resources
if (devExt->Fdo.ControllerCommonBuffer != NULL) {
USBPORT_HalFreeCommonBuffer(FdoDeviceObject,
devExt->Fdo.ControllerCommonBuffer);
devExt->Fdo.ControllerCommonBuffer = NULL;
}
}
ntStatus = MPSTATUS_TO_NTSTATUS(mpStatus);
// placing the faiure here simulates a failure from
// some point above. We won't start the DM timer unless
// everything succeeds
TEST_PATH(ntStatus, FAILED_USBPORT_START);
// start up the 'DEADMAN' timer
if (NT_SUCCESS(ntStatus)) {
devExt->Fdo.DM_TimerInterval = USBPORT_DM_TIMER_INTERVAL;
USBPORT_StartDM_Timer(FdoDeviceObject,
USBPORT_DM_TIMER_INTERVAL);
}
// create symbolic links for user mode
if (NT_SUCCESS(ntStatus)) {
// create the link based on guid for USB 2.0
ntStatus = USBPORT_CreatePortFdoSymbolicLink(FdoDeviceObject);
}
if (NT_SUCCESS(ntStatus)) {
// create the legacy link only for USB 1.1 controllers
//
// BUGBUG
// Failure to create the legacy link will not keep the
// driver from loading.
// This fails during text mode setup if you still have the
// UHCD loaded, we can remove this when UHCD is completely
// out of the build.
// ntStatus =
USBPORT_CreateLegacyFdoSymbolicLink(FdoDeviceObject);
}
USBPORT_StartDevice_Done:
if (!NT_SUCCESS(ntStatus)) {
// stop_device will(should) cleanup for us
USBPORT_KdPrint((0, "'Start Device Failed (status %08.8x)\n", ntStatus));
DEBUG_BREAK();
// since we won't be marked 'started' call stop here
// to cleanup a failed start
USBPORT_StopDevice(FdoDeviceObject,
TRUE);
} else {
// enable system wake support
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_ENABLE_SYSTEM_WAKE);
}
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'SRT<', ntStatus, 0, 0);
return ntStatus;
}
NTSTATUS
USBPORT_PassIrp(
PDEVICE_OBJECT DeviceObject,
PIO_COMPLETION_ROUTINE CompletionRoutine,
PVOID Context,
BOOLEAN InvokeOnSuccess,
BOOLEAN InvokeOnError,
BOOLEAN InvokeOnCancel,
PIRP Irp
)
/*++
Routine Description:
Passes an irp to the next lower driver,
this is done by the FDO for all PnP Irps
Arguments:
Return Value:
--*/
{
PDEVICE_EXTENSION devExt;
NTSTATUS ntStatus;
GET_DEVICE_EXT(devExt, DeviceObject);
ASSERT_FDOEXT(devExt);
// note that we do not dec the pending request count
// if we set a completion routine.
// We do not want to 'go away' before the completion
// routine is called.
if (CompletionRoutine) {
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
CompletionRoutine,
Context,
InvokeOnSuccess,
InvokeOnError,
InvokeOnCancel);
} else {
IoSkipCurrentIrpStackLocation(Irp);
DECREMENT_PENDING_REQUEST_COUNT(DeviceObject, Irp);
}
ntStatus = IoCallDriver(devExt->Fdo.TopOfStackDeviceObject, Irp);
return ntStatus;
}
VOID
USBPORT_CompleteIrp(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
NTSTATUS ntStatus,
ULONG_PTR Information
)
/*++
Routine Description:
Completes an I/O Request
Arguments:
Return Value:
--*/
{
USBPORT_KdPrint((2, "'USBPORT_CompleteIrp status = %x\n", ntStatus));
DECREMENT_PENDING_REQUEST_COUNT(DeviceObject, Irp);
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = Information;
// LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'irpC', Irp, DeviceObject, Urb);
IoCompleteRequest(Irp,
IO_NO_INCREMENT);
}
NTSTATUS
USBPORT_Dispatch(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
Process the IRPs sent to this device.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Return Value:
NT status code
--*/
{
PIO_STACK_LOCATION irpStack;
NTSTATUS ntStatus = STATUS_SUCCESS;
PDEVICE_EXTENSION devExt;
BOOLEAN forRootHubPdo = FALSE;
KIRQL irql;
USBPORT_KdPrint((2, "'enter USBPORT_Dispatch\n"));
GET_DEVICE_EXT(devExt, DeviceObject);
irpStack = IoGetCurrentIrpStackLocation (Irp);
USBPORT_KdPrint((2, "'USBPORT_Dispatch IRP = %x, stack = %x (func) %x %x\n",
Irp, irpStack, irpStack->MajorFunction, irpStack->MinorFunction));
// figure out if this is the FDO for the HC or the PDO
// for the root hub
if (devExt->Sig == ROOTHUB_DEVICE_EXT_SIG) {
forRootHubPdo = TRUE;
USBPORT_KdPrint((2, "'IRP->PDO\n"));
} else if (devExt->Sig == USBPORT_DEVICE_EXT_SIG) {
forRootHubPdo = FALSE;
USBPORT_KdPrint((2, "'IRP->FDO\n"));
} else {
// this is a bug, bugcheck
USBPORT_ASSERT(FALSE);
}
// *BEGIN SPECIAL CASE
// Before doing anything see if this devobj is 'removed'
// if so do some 'special' handling
KeAcquireSpinLock(&devExt->PendingRequestSpin.sl, &irql);
if (TEST_FLAG(devExt->PnpStateFlags, USBPORT_PNP_REMOVED)) {
// someone has managed to call us after the remove
USBPORT_KdPrint((1, "'(irp after remove) IRP = %x, DO %x MJx%x MNx%x\n",
Irp, DeviceObject, irpStack->MajorFunction,
irpStack->MinorFunction));
KeReleaseSpinLock(&devExt->PendingRequestSpin.sl, irql);
if (forRootHubPdo) {
switch(irpStack->MajorFunction) {
// in the removed state complete all power irps with
// success, this happens if you suspend with the root
// hub disbaled
case IRP_MJ_POWER:
Irp->IoStatus.Status = ntStatus = STATUS_SUCCESS;
PoStartNextPowerIrp(Irp);
IoCompleteRequest(Irp,
IO_NO_INCREMENT);
goto USBPORT_Dispatch_Done;
// since the root hub pdo exists even when the device
// is removed by PnP we still allow irps thru
default:
break;
}
} else {
switch(irpStack->MajorFunction) {
// allow PnP irps even though we are removed
case IRP_MJ_PNP:
break;
case IRP_MJ_POWER:
TEST_TRAP();
Irp->IoStatus.Status = ntStatus = STATUS_DEVICE_REMOVED;
PoStartNextPowerIrp(Irp);
IoCompleteRequest(Irp,
IO_NO_INCREMENT);
goto USBPORT_Dispatch_Done;
default:
Irp->IoStatus.Status = ntStatus = STATUS_DEVICE_REMOVED;
IoCompleteRequest(Irp,
IO_NO_INCREMENT);
goto USBPORT_Dispatch_Done;
}
}
} else {
KeReleaseSpinLock(&devExt->PendingRequestSpin.sl, irql);
}
// *END SPECIAL CASE
INCREMENT_PENDING_REQUEST_COUNT(DeviceObject, Irp);
switch (irpStack->MajorFunction) {
case IRP_MJ_CREATE:
USBPORT_KdPrint((2, "'IRP_MJ_CREATE\n"));
USBPORT_CompleteIrp(DeviceObject, Irp, ntStatus, 0);
break;
case IRP_MJ_CLOSE:
USBPORT_KdPrint((2, "'IRP_MJ_CLOSE\n"));
USBPORT_CompleteIrp(DeviceObject, Irp, ntStatus, 0);
break;
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
USBPORT_KdPrint((2, "'IRP_MJ_NTERNAL_DEVICE_CONTROL\n"));
if (forRootHubPdo) {
ntStatus = USBPORT_PdoInternalDeviceControlIrp(DeviceObject, Irp);
} else {
ntStatus = USBPORT_FdoInternalDeviceControlIrp(DeviceObject, Irp);
}
break;
case IRP_MJ_DEVICE_CONTROL:
USBPORT_KdPrint((2, "'IRP_MJ_DEVICE_CONTROL\n"));
if (forRootHubPdo) {
ntStatus = USBPORT_PdoDeviceControlIrp(DeviceObject, Irp);
} else {
ntStatus = USBPORT_FdoDeviceControlIrp(DeviceObject, Irp);
}
break;
case IRP_MJ_POWER:
if (forRootHubPdo) {
ntStatus = USBPORT_PdoPowerIrp(DeviceObject, Irp);
} else {
ntStatus = USBPORT_FdoPowerIrp(DeviceObject, Irp);
}
break;
case IRP_MJ_PNP:
if (forRootHubPdo) {
ntStatus = USBPORT_PdoPnPIrp(DeviceObject, Irp);
} else {
ntStatus = USBPORT_FdoPnPIrp(DeviceObject, Irp);
}
break;
case IRP_MJ_SYSTEM_CONTROL:
if (forRootHubPdo) {
USBPORT_KdPrint((2, "'IRP_MJ_SYSTEM_CONTROL\n"));
ntStatus = Irp->IoStatus.Status;
USBPORT_CompleteIrp(DeviceObject, Irp, ntStatus, 0);
} else {
USBPORT_KdPrint((2, "'IRP_MJ_SYSTEM_CONTROL\n"));
//
// pass on to our PDO
//
ntStatus =
USBPORT_PassIrp(DeviceObject,
NULL,
NULL,
TRUE,
TRUE,
TRUE,
Irp);
}
break;
default:
USBPORT_KdPrint((2, "'unrecognized IRP_MJ_ function (%x)\n", irpStack->MajorFunction));
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
USBPORT_CompleteIrp(DeviceObject, Irp, ntStatus, 0);
} /* case MJ_FUNCTION */
USBPORT_KdPrint((2, "'exit USBPORT_Dispatch 0x%x\n", ntStatus));
USBPORT_Dispatch_Done:
return ntStatus;
}
VOID
USBPORT_TrackPendingRequest(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
BOOLEAN AddToList
)
/*++
Routine Description:
Keep track of Irps sent to our DevObjs
Arguments:
Return Value:
--*/
{
PDEVICE_EXTENSION devExt;
KIRQL irql;
GET_DEVICE_EXT(devExt, DeviceObject);
if (AddToList) {
KeAcquireSpinLock(&devExt->PendingRequestSpin.sl, &irql);
// -1 to 0 means we are starting, initialize the event
if (devExt->PendingRequestCount == -1) {
KeInitializeEvent(&devExt->PendingRequestEvent,
NotificationEvent,
FALSE);
}
USBPORT_KdPrint((4, "'INC pending Request(%x) %d\n",
devExt, devExt->PendingRequestCount));
devExt->PendingRequestCount++;
// the following is debug only code
#ifdef TRACK_IRPS
if (Irp != NULL) {
PTRACK_IRP trackIrp;
PLIST_ENTRY listEntry;
listEntry = devExt->TrackIrpList.Flink;
while (listEntry != &devExt->TrackIrpList) {
trackIrp = (PTRACK_IRP) CONTAINING_RECORD(
listEntry,
struct _TRACK_IRP,
ListEntry);
if (trackIrp->Irp == Irp) {
USBPORT_KdPrint((0, " IRP %x already pending\n",
Irp));
BUGCHECK();
}
listEntry = trackIrp->ListEntry.Flink;
}
ALLOC_POOL_Z(trackIrp,
NonPagedPool,
sizeof(*trackIrp));
USBPORT_ASSERT(trackIrp != NULL);
if (trackIrp != NULL) {
trackIrp->Irp = Irp;
InsertTailList(&devExt->TrackIrpList,
&trackIrp->ListEntry);
}
}
#endif
KeReleaseSpinLock(&devExt->PendingRequestSpin.sl, irql);
} else {
KeAcquireSpinLock(&devExt->PendingRequestSpin.sl, &irql);
USBPORT_KdPrint((4, "'DEC pending Request(%x) %d\n",
devExt, devExt->PendingRequestCount));
#ifdef TRACK_IRPS
if (Irp != NULL) {
PTRACK_IRP trackIrp;
PLIST_ENTRY listEntry;
listEntry = devExt->TrackIrpList.Flink;
while (listEntry != &devExt->TrackIrpList) {
trackIrp = (PTRACK_IRP) CONTAINING_RECORD(
listEntry,
struct _TRACK_IRP,
ListEntry);
if (trackIrp->Irp == Irp) {
goto found_irp;
}
listEntry = trackIrp->ListEntry.Flink;
}
trackIrp = NULL;
found_irp:
if (trackIrp == NULL) {
USBPORT_KdPrint((0, " Pending IRP %x not found\n",
Irp));
BUGCHECK();
}
RemoveEntryList(&trackIrp->ListEntry);
FREE_POOL(NULL, trackIrp);
}
#endif
devExt->PendingRequestCount--;
// 0 -> -1 indicates we have no more pending io
// signal the event
if (devExt->PendingRequestCount == -1) {
KeSetEvent(&devExt->PendingRequestEvent,
1,
FALSE);
}
KeReleaseSpinLock(&devExt->PendingRequestSpin.sl, irql);
}
}
NTSTATUS
USBPORT_GetConfigValue(
PWSTR ValueName,
ULONG ValueType,
PVOID ValueData,
ULONG ValueLength,
PVOID Context,
PVOID EntryContext
)
/*++
Routine Description:
This routine is a callback routine for RtlQueryRegistryValues
It is called for each entry in the Parameters
node to set the config values. The table is set up
so that this function will be called with correct default
values for keys that are not present.
Arguments:
ValueName - The name of the value (ignored).
ValueType - The type of the value
ValueData - The data for the value.
ValueLength - The length of ValueData.
Context - A pointer to the CONFIG structure.
EntryContext - The index in Config->Parameters to save the value.
Return Value:
--*/
{
NTSTATUS ntStatus = STATUS_SUCCESS;
USBPORT_KdPrint((2, "'Type 0x%x, Length 0x%x\n", ValueType, ValueLength));
switch (ValueType) {
case REG_DWORD:
RtlCopyMemory(EntryContext, ValueData, sizeof(ULONG));
break;
case REG_BINARY:
// BUGBUG we are only set up to read a byte
RtlCopyMemory(EntryContext, ValueData, 1);
break;
default:
ntStatus = STATUS_INVALID_PARAMETER;
}
return ntStatus;
}
VOID
USBPORT_Wait(
PDEVICE_OBJECT FdoDeviceObject,
ULONG Milliseconds
)
/*++
Routine Description:
Synchrounously wait the specified number of miliseconds
Return Value:
None
--*/
{
LONG time;
ULONG timerIncerent;
LARGE_INTEGER time64;
USBPORT_KdPrint((2,"'Wait for %d ms\n", Milliseconds));
//
// work only when LowPart is not overflown.
//
USBPORT_ASSERT(21474 > Milliseconds);
//
// wait MilliSeconds( 10000 100ns unit)
//
timerIncerent = KeQueryTimeIncrement() - 1;
// round up to the next highest timer increment
time = -1 * (MILLISECONDS_TO_100_NS_UNITS(Milliseconds) + timerIncerent);
time64 = RtlConvertLongToLargeInteger(time),
KeDelayExecutionThread(KernelMode, FALSE, &time64);
USBPORT_KdPrint((2,"'Wait done\n"));
return;
}
ULONG
USBPORT_CalculateUsbBandwidth(
PDEVICE_OBJECT FdoDeviceObject,
PHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
Computes the bandwidth that must be reserved for a
given endpoint
Arguments:
Return Value:
banwidth required in bits/ms, returns 0 for bulk
and control endpoints
--*/
{
ULONG bw;
ULONG overhead;
ULONG maxPacketSize;
BOOLEAN lowSpeed = FALSE;
#define USB_ISO_OVERHEAD_BYTES 9
#define USB_INTERRUPT_OVERHEAD_BYTES 13
PAGED_CODE();
ASSERT_ENDPOINT(Endpoint);
maxPacketSize = Endpoint->Parameters.MaxPacketSize;
if (Endpoint->Parameters.DeviceSpeed == LowSpeed) {
lowSpeed = TRUE;
}
//
// control, iso, bulk, interrupt
//
switch(Endpoint->Parameters.TransferType) {
case Bulk:
case Control:
overhead = 0;
break;
case Isochronous:
overhead = USB_ISO_OVERHEAD_BYTES;
break;
case Interrupt:
overhead = USB_INTERRUPT_OVERHEAD_BYTES;
break;
}
//
// Calculate bandwidth for endpoint. We will use the
// approximation: (overhead bytes plus MaxPacket bytes)
// times 8 bits/byte times worst case bitstuffing overhead.
// This gives bit times, for low speed endpoints we multiply
// by 8 again to convert to full speed bits.
//
//
// Figure out how many bits are required for the transfer.
// (multiply by 7/6 because, in the worst case you might
// have a bit-stuff every six bits requiring 7 bit times to
// transmit 6 bits of data.)
//
// overhead(bytes) * maxpacket(bytes/ms) * 8
// (bits/byte) * bitstuff(7/6) = bits/ms
bw = ((overhead+maxPacketSize) * 8 * 7) / 6;
// return zero for control or bulk
if (!overhead) {
bw = 0;
}
if (lowSpeed) {
bw *= 8;
}
return bw;
}
VOID
USBPORT_UpdateAllocatedBw(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PDEVICE_EXTENSION devExt;
ULONG alloced_bw, i, m;
PAGED_CODE();
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
m = 0;
for (i=0; i<USBPORT_MAX_INTEP_POLLING_INTERVAL; i++) {
alloced_bw = devExt->Fdo.TotalBusBandwidth -
devExt->Fdo.BandwidthTable[i];
if (alloced_bw > m) {
m = alloced_bw;
}
}
devExt->Fdo.MaxAllocedBw = m;
m = devExt->Fdo.TotalBusBandwidth;
for (i=0; i<USBPORT_MAX_INTEP_POLLING_INTERVAL; i++) {
alloced_bw = devExt->Fdo.TotalBusBandwidth -
devExt->Fdo.BandwidthTable[i];
if (alloced_bw < m) {
m = alloced_bw;
}
}
devExt->Fdo.MinAllocedBw = m;
if (m == devExt->Fdo.TotalBusBandwidth) {
devExt->Fdo.MinAllocedBw = 0;
}
}
VOID
USBPORT_UpdateAllocatedBwTt(
PTRANSACTION_TRANSLATOR Tt
)
/*++
Routine Description:
This function and the one above it should be merged
Arguments:
Return Value:
--*/
{
ULONG alloced_bw, i, m;
m = 0;
for (i=0; i<USBPORT_MAX_INTEP_POLLING_INTERVAL; i++) {
alloced_bw = Tt->TotalBusBandwidth -
Tt->BandwidthTable[i];
if (alloced_bw > m) {
m = alloced_bw;
}
}
Tt->MaxAllocedBw = m;
m = Tt->TotalBusBandwidth;
for (i=0; i<USBPORT_MAX_INTEP_POLLING_INTERVAL; i++) {
alloced_bw = Tt->TotalBusBandwidth -
Tt->BandwidthTable[i];
if (alloced_bw < m) {
m = alloced_bw;
}
}
Tt->MinAllocedBw = m;
if (m == Tt->TotalBusBandwidth) {
Tt->MinAllocedBw = 0;
}
}
BOOLEAN
USBPORT_AllocateBandwidthUSB11(
PDEVICE_OBJECT FdoDeviceObject,
PHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
Computes the best schedule offset for this endpoint
and allocates the bandwidth.
Offsets in the bw allocation table orrespond to the values
in the following tree structure.
The schedule offset is the best possible posoition for the
endpoint to reside in the schedule based on its period.
for 32 ms or mf ep there are 32 possible positions
for 16 there are 16
for 8 there are 8
...
fr <32> <16> <08> <04> <02> <01>
0 ( 0) -\
( 0)-\
16 ( 1) -/ \
( 0)-\
8 ( 2) -\ / \
( 1)-/ \
24 ( 3) -/ \
(0)-\
4 ( 4) -\ / \
( 2)-\ / \
20 ( 5) -/ \ / \
( 1)-/ \
12 ( 6) -\ / \
( 3)-/ \
28 ( 7) -/ \
(0)-\
2 ( 8) -\ / \
( 4)-\ / \
18 ( 9) -/ \ / \
( 2)-\ / \
10 (10) -\ / \ / \
( 5)-/ \ / \
26 (11) -/ \ / \
(1)-/ \
6 (12) -\ / \
( 6)-\ / \
22 (13) -/ \ / \
( 3)-/ \
14 (14) -\ / \
( 7)-/ \
30 (15) -/ \
(0)
1 (16) -\ /
( 8)-\ /
17 (17) -/ \ /
( 4)-\ /
9 (18) -\ / \ /
( 9)-/ \ /
25 (19) -/ \ /
(2)-\ /
5 (20) -\ / \ /
(10)-\ / \ /
21 (21) -/ \ / \ /
( 5)-/ \ /
13 (22) -\ / \ /
(11)-/ \ /
29 (23) -/ \ /
(1)-/
3 (24) -\ /
(12)-\ /
19 (25) -/ \ /
( 6)-\ /
11 (26) -\ / \ /
(13)-/ \ /
27 (27) -/ \ /
(3)-/
7 (28) -\ /
(14)-\ /
23 (29) -/ \ /
( 7)-/
15 (30) -\ /
(15)-/
31 (32) -/
Allocations:
period.offset table entries
1 0, 1, 2.........31
2.0 0, 1, 2.........15
2.1 16,17,18.........31
4.0 0, 1, 2..........7
4.1 8, 9,10.........15
4.2 16,17,18.........23
4.3 24,25,26.........31
8.0 0, 1, 2, 3
8.1 4, 5, 6, 7
8.2 8, 9,10,11
8.3 12,13,14,15
8.4 16,17,18,19
8.5 20,21,22,23
8.6 24,25,26,27
8.7 28,29,30,31
...
frame nodes visited (period.offset)
0 32.0 16.0 8.0 4.0 2.0
1 32.16 16.8 8.4 4.2 2.1
2 32.8 16.4 8.2 4.1 2.0
3 32.24 16.12 8.6 4.3 2.1
4 32.4 16.2 8.1 4.0 2.0
5 32.20 16.10 8.5 4.2 2.1
6 32.12 16.6 8.3 4.1 2.0
7 32.28 16.14 8.7 4.3 2.1
...
all miniports should maintain a 32 entry table for
interrupt endpoints, the lower 5 bits of the current
frame are used as an index in to this table. see the
above graph for index to frame mappings
Arguments:
Return Value:
FALSE if no bandwidth availble
--*/
{
PDEVICE_EXTENSION devExt;
ULONG period, bandwidth, scheduleOffset, i;
ULONG bestFitBW, min, n;
LONG bestScheduleOffset;
BOOLEAN willFit;
PAGED_CODE();
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
ASSERT_ENDPOINT(Endpoint);
if (Endpoint->Parameters.TransferType == Bulk ||
Endpoint->Parameters.TransferType == Control ||
TEST_FLAG(Endpoint->Flags, EPFLAG_ROOTHUB)) {
// control/bulk come out of our standard 10%
// and root hub endpoints consume no BW
Endpoint->Parameters.ScheduleOffset = 0;
return TRUE;
}
// Iso and interrupt -- iso is just like interrupt with
// a period of 1
// see if we can fit it
scheduleOffset = 0;
period = Endpoint->Parameters.Period;
USBPORT_ASSERT(period != 0);
bandwidth = Endpoint->Parameters.Bandwidth;
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'rqBW', scheduleOffset,
bandwidth, Endpoint);
// The bandwidth table conatins the bw avaialble for all
// possible schedule offsets up to the max polling interval
// we support.
bestFitBW = 0;
bestScheduleOffset = -1;
// scan all possible offsets and select the 'best fit'
// our goal here is to position the ep in the location
// with the most free bw
do {
// assume it will fit
willFit = TRUE;
min = devExt->Fdo.TotalBusBandwidth;
n = USBPORT_MAX_INTEP_POLLING_INTERVAL/period;
for (i=0; i<n; i++) {
if (devExt->Fdo.BandwidthTable[n*scheduleOffset+i] < bandwidth) {
willFit = FALSE;
break;
}
// set min to the lowest free entry for this
// offset
if (min > devExt->Fdo.BandwidthTable[n*scheduleOffset+i]) {
min = devExt->Fdo.BandwidthTable[n*scheduleOffset+i];
}
}
if (willFit && min > bestFitBW) {
// it will fit compare this to the we have found
bestFitBW = min;
bestScheduleOffset = scheduleOffset;
}
scheduleOffset++;
} while (scheduleOffset < period);
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'ckBW', bestScheduleOffset,
bandwidth, period);
if (bestScheduleOffset != -1) {
scheduleOffset = bestScheduleOffset;
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'alBW', scheduleOffset,
bandwidth, period);
// we found an offset that will work for the
// given period, alloc the bw and return the
// offset
Endpoint->Parameters.ScheduleOffset =
scheduleOffset;
n = USBPORT_MAX_INTEP_POLLING_INTERVAL/period;
for (i=0; i<n; i++) {
USBPORT_ASSERT(n*scheduleOffset+i < USBPORT_MAX_INTEP_POLLING_INTERVAL);
USBPORT_ASSERT(devExt->Fdo.BandwidthTable[n*scheduleOffset+i] >= bandwidth);
devExt->Fdo.BandwidthTable[n*scheduleOffset+i] -= bandwidth;
}
// update our bw tracking info
if (Endpoint->Parameters.TransferType == Isochronous) {
devExt->Fdo.AllocedIsoBW += bandwidth;
} else {
USBPORT_GET_BIT_SET(period, n);
USBPORT_ASSERT(n<6);
devExt->Fdo.AllocedInterruptBW[n] += bandwidth;
}
}
USBPORT_UpdateAllocatedBw(FdoDeviceObject);
return bestScheduleOffset == -1 ? FALSE : TRUE;
}
VOID
USBPORT_FreeBandwidthUSB11(
PDEVICE_OBJECT FdoDeviceObject,
PHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
Frees the bw reserved for a give endpoint
Arguments:
Return Value:
FALSE if no bandwidth availble
--*/
{
PDEVICE_EXTENSION devExt;
ULONG period, bandwidth, scheduleOffset, i, n;
PAGED_CODE();
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
ASSERT_ENDPOINT(Endpoint);
if (Endpoint->Parameters.TransferType == Bulk ||
Endpoint->Parameters.TransferType == Control ||
TEST_FLAG(Endpoint->Flags, EPFLAG_ROOTHUB)) {
// these come out of our standard 10%
return;
}
scheduleOffset = Endpoint->Parameters.ScheduleOffset;
bandwidth = Endpoint->Parameters.Bandwidth;
period = Endpoint->Parameters.Period;
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'frBW', scheduleOffset, bandwidth, period);
n = USBPORT_MAX_INTEP_POLLING_INTERVAL/period;
for (i=0; i<n; i++) {
USBPORT_ASSERT(n*scheduleOffset+i < USBPORT_MAX_INTEP_POLLING_INTERVAL);
devExt->Fdo.BandwidthTable[n*scheduleOffset+i] += bandwidth;
}
//for (i=sheduleOffset; i<USBPORT_MAX_INTEP_POLLING_INTERVAL; i+=period) {
// USBPORT_ASSERT(i < USBPORT_MAX_INTEP_POLLING_INTERVAL);
// devExt->Fdo.BandwidthTable[i] += bandwidth;
//}
// update our bw tracking info
if (Endpoint->Parameters.TransferType == Isochronous) {
devExt->Fdo.AllocedIsoBW -= bandwidth;
} else {
USBPORT_GET_BIT_SET(period, n);
USBPORT_ASSERT(n<6);
devExt->Fdo.AllocedInterruptBW[n] -= bandwidth;
}
// update max allocated BW
USBPORT_UpdateAllocatedBw(FdoDeviceObject);
return;
}
NTSTATUS
USBPORT_ReadWriteConfigSpace(
PDEVICE_OBJECT FdoDeviceObject,
BOOLEAN Read,
PVOID Buffer,
ULONG Offset,
ULONG Length
)
/*++
Routine Description:
This routine reads or writes config space.
Arguments:
DeviceObject - .
Read - TRUE if read, FALSE if write.
Buffer - The info to read or write.
Offset - The offset in config space to read or write.
Length - The length to transfer.
Return Value:
NTSTATUS
--*/
{
PIO_STACK_LOCATION nextStack;
PIRP irp;
NTSTATUS ntStatus;
KEVENT event;
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION devExt;
PAGED_CODE();
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
deviceObject = devExt->Fdo.PhysicalDeviceObject;
if (Read) {
RtlZeroMemory(Buffer, Length);
}
irp = IoAllocateIrp(deviceObject->StackSize, FALSE);
if (irp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
// All PnP IRP's need the Status field initialized to STATUS_NOT_SUPPORTED.
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IoSetCompletionRoutine(irp,
USBPORT_DeferIrpCompletion,
&event,
TRUE,
TRUE,
TRUE);
nextStack = IoGetNextIrpStackLocation(irp);
USBPORT_ASSERT(nextStack != NULL);
nextStack->MajorFunction= IRP_MJ_PNP;
nextStack->MinorFunction= Read ? IRP_MN_READ_CONFIG : IRP_MN_WRITE_CONFIG;
nextStack->Parameters.ReadWriteConfig.WhichSpace = PCI_WHICHSPACE_CONFIG;
nextStack->Parameters.ReadWriteConfig.Buffer = Buffer;
nextStack->Parameters.ReadWriteConfig.Offset = Offset;
nextStack->Parameters.ReadWriteConfig.Length = Length;
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'rwC>', 0, 0, 0);
ntStatus = IoCallDriver(deviceObject,
irp);
if (ntStatus == STATUS_PENDING) {
// wait for irp to complete
KeWaitForSingleObject(
&event,
Suspended,
KernelMode,
FALSE,
NULL);
ntStatus = irp->IoStatus.Status;
}
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'rwC<', 0, 0, ntStatus);
IoFreeIrp(irp);
return ntStatus;
}
VOID
USBPORTSVC_Wait(
PDEVICE_DATA DeviceData,
ULONG MillisecondsToWait
)
/*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{
PDEVICE_EXTENSION devExt;
PDEVICE_OBJECT fdoDeviceObject;
DEVEXT_FROM_DEVDATA(devExt, DeviceData);
ASSERT_FDOEXT(devExt);
fdoDeviceObject = devExt->HcFdoDeviceObject;
USBPORT_Wait(fdoDeviceObject, MillisecondsToWait);
}
VOID
USBPORT_InvalidateController(
PDEVICE_OBJECT FdoDeviceObject,
USB_CONTROLLER_STATE ControllerState
)
/*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{
PDEVICE_EXTENSION devExt;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
switch(ControllerState) {
case UsbMpControllerPowerFault:
USBPORT_PowerFault(FdoDeviceObject,
"Miniport Raised Exception");
break;
case UsbMpControllerNeedsHwReset:
USBPORT_KdPrint((1,"'<UsbMpControllerNeedsHwReset>\n"));
if (KeInsertQueueDpc(&devExt->Fdo.HcResetDpc, 0, 0)) {
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_HW_RESET_PENDING);
INCREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL);
}
break;
case UsbMpControllerRemoved:
// set our flag indicating the controller has been
// removed and signal the worker thread.
USBPORT_KdPrint((1,"'<UsbMpControllerRemoved>\n"));
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_CONTROLLER_GONE);
if (KeInsertQueueDpc(&devExt->Fdo.SurpriseRemoveDpc, 0, 0)) {
INCREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL);
}
break;
case UsbMpSimulateInterrupt:
// queue the ISR DPC as if we got a hardware interrupt
KeInsertQueueDpc(&devExt->Fdo.IsrDpc,
NULL,
NULL);
break;
default:
TEST_TRAP();
}
}
VOID
USBPORTSVC_InvalidateController(
PDEVICE_DATA DeviceData,
USB_CONTROLLER_STATE ControllerState
)
/*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{
PDEVICE_EXTENSION devExt;
PDEVICE_OBJECT fdoDeviceObject;
DEVEXT_FROM_DEVDATA(devExt, DeviceData);
ASSERT_FDOEXT(devExt);
fdoDeviceObject = devExt->HcFdoDeviceObject;
USBPORT_InvalidateController(fdoDeviceObject, ControllerState);
}
VOID
USBPORT_HcResetDpc(
PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2
)
/*++
Routine Description:
This routine runs at DISPATCH_LEVEL IRQL.
Arguments:
Dpc - Pointer to the DPC object.
DeferredContext - supplies FdoDeviceObject.
SystemArgument1 - not used.
SystemArgument2 - not used.
Return Value:
None.
--*/
{
PDEVICE_OBJECT fdoDeviceObject = DeferredContext;
PDEVICE_EXTENSION devExt;
GET_DEVICE_EXT(devExt, fdoDeviceObject);
ASSERT_FDOEXT(devExt);
devExt->Fdo.StatHardResetCount++;
// allow Dr. Slick to work his magic on the ohci controller
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_HW_RESET_PENDING);
// core lock is held here so that we don't poll the
// controler endpoints while we reset
MP_ResetController(devExt);
DECREMENT_PENDING_REQUEST_COUNT(fdoDeviceObject, NULL);
}
VOID
USBPORT_SurpriseRemoveDpc(
PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2
)
/*++
Routine Description:
This routine runs at DISPATCH_LEVEL IRQL.
Arguments:
Dpc - Pointer to the DPC object.
DeferredContext - supplies FdoDeviceObject.
SystemArgument1 - not used.
SystemArgument2 - not used.
Return Value:
None.
--*/
{
PDEVICE_OBJECT fdoDeviceObject = DeferredContext;
PDEVICE_EXTENSION devExt;
GET_DEVICE_EXT(devExt, fdoDeviceObject);
ASSERT_FDOEXT(devExt);
USBPORT_NukeAllEndpoints(fdoDeviceObject);
DECREMENT_PENDING_REQUEST_COUNT(fdoDeviceObject, NULL);
}
VOID
USBPORTSVC_BugCheck(
PDEVICE_DATA DeviceData
)
/*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{
PDEVICE_EXTENSION devExt;
PDEVICE_OBJECT fdoDeviceObject;
ULONG vidDev;
ULONG ilog;
PLOG_ENTRY lelog;
DEVEXT_FROM_DEVDATA(devExt, DeviceData);
ASSERT_FDOEXT(devExt);
fdoDeviceObject = devExt->HcFdoDeviceObject;
vidDev = devExt->Fdo.PciVendorId;
vidDev <<=16;
vidDev |= devExt->Fdo.PciDeviceId;
ilog &= devExt->Log.LogSizeMask;
lelog = devExt->Log.LogStart+ilog;
BUGCHECK(USBBUGCODE_MINIPORT_ERROR, vidDev, (ULONG_PTR)lelog, 0);
}
USB_MINIPORT_STATUS
USBPORTSVC_ReadWriteConfigSpace(
PDEVICE_DATA DeviceData,
BOOLEAN Read,
PVOID Buffer,
ULONG Offset,
ULONG Length
)
/*++
Routine Description:
Service Exported to miniports for accessing config
spzce if needed. This service is synchronous and cannot
be called at raised IRQL
USBUHCI uses this service to clear set the PIRQD bit which
enables/disables the controller.
Arguments:
Return Value:
None.
--*/
{
NTSTATUS ntStatus;
PDEVICE_EXTENSION devExt;
PDEVICE_OBJECT fdoDeviceObject;
DEVEXT_FROM_DEVDATA(devExt, DeviceData);
ASSERT_FDOEXT(devExt);
fdoDeviceObject = devExt->HcFdoDeviceObject;
ntStatus = USBPORT_ReadWriteConfigSpace(
fdoDeviceObject,
Read,
Buffer,
Offset,
Length);
return USBPORT_NtStatus_TO_MiniportStatus(ntStatus);
}
BOOLEAN
USBPORT_InTextmodeSetup(
VOID
)
/*++
Routine Description:
Arguments:
None
Return Value:
TRUE if textmode setup is currently running, else FALSE
Notes:
--*/
{
UNICODE_STRING keyName;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE hKey;
BOOLEAN textmodeSetup;
NTSTATUS ntStatus;
PAGED_CODE();
RtlInitUnicodeString(&keyName,
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\setupdd");
InitializeObjectAttributes(&objectAttributes,
&keyName,
OBJ_CASE_INSENSITIVE,
NULL,
(PSECURITY_DESCRIPTOR) NULL);
ntStatus = ZwOpenKey(&hKey,
KEY_READ,
&objectAttributes);
if (!NT_SUCCESS(ntStatus))
{
textmodeSetup = FALSE;
}
else
{
textmodeSetup = TRUE;
ZwClose(hKey);
}
return textmodeSetup;
}
NTSTATUS
USBPORT_IsCompanionController(
PDEVICE_OBJECT FdoDeviceObject,
PBOOLEAN ReturnResult
)
/*++
Routine Description:
Arguments:
FdoDeviceObject - USB 1.0 OHCI or UHCI Host Controller FDO for which
an associated USB 2.0 EHCI Host Controller is searched.
ReturnResult - TRUE if the query is successful and an associated USB 2.0
EHCI Host Controller is found, otherwise FALSE.
Return Value:
Success or failure of the query, not the answer to the query.
Notes:
--*/
{
KEVENT irpCompleted;
PDEVICE_OBJECT targetDevice;
IO_STATUS_BLOCK statusBlock;
PIRP irp;
PIO_STACK_LOCATION irpStack;
PCI_DEVICE_PRESENT_INTERFACE dpInterface;
PCI_DEVICE_PRESENCE_PARAMETERS dpParameters;
BOOLEAN result;
NTSTATUS status;
PAGED_CODE();
//
// Set the default return value;
//
*ReturnResult = FALSE;
//
// Initialize the event we'll wait on
//
KeInitializeEvent(&irpCompleted, SynchronizationEvent, FALSE);
//
// Find out where we are sending the irp
//
targetDevice = IoGetAttachedDeviceReference(FdoDeviceObject);
//
// Get an IRP
//
irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
targetDevice,
NULL, // Buffer
0, // Length
0, // StartingOffset
&irpCompleted,
&statusBlock
);
if (!irp)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto USBPORT_IsCompanionControllerDone;
}
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
irp->IoStatus.Information = 0;
//
// Initialize the stack location
//
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
irpStack->Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_PCI_DEVICE_PRESENT_INTERFACE;
irpStack->Parameters.QueryInterface.Version = PCI_DEVICE_PRESENT_INTERFACE_VERSION;
irpStack->Parameters.QueryInterface.Size = sizeof(PCI_DEVICE_PRESENT_INTERFACE);
irpStack->Parameters.QueryInterface.Interface = (PINTERFACE)&dpInterface;
irpStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;
//
// Call the driver and wait for completion
//
status = IoCallDriver(targetDevice, irp);
if (status == STATUS_PENDING)
{
KeWaitForSingleObject(&irpCompleted, Executive, KernelMode, FALSE, NULL);
status = statusBlock.Status;
}
if (!NT_SUCCESS(status))
{
USBPORT_KdPrint((1,"PCI_DEVICE_PRESENT_INTERFACE query interface failed\n"));
//
// Don't have the interface so don't free it.
//
goto USBPORT_IsCompanionControllerDone;
}
else
{
USBPORT_KdPrint((1,"PCI_DEVICE_PRESENT_INTERFACE query interface succeeded\n"));
}
if (dpInterface.Size < sizeof(PCI_DEVICE_PRESENT_INTERFACE))
{
USBPORT_KdPrint((1,"PCI_DEVICE_PRESENT_INTERFACE old version\n"));
//
// Do have the interface so do free it.
//
goto USBPORT_IsCompanionControllerFreeInterface;
}
//
// Initialize the Device Presence Parameters
//
dpParameters.Size = sizeof(dpParameters);
// We care about the Class / SubClass / Programming Interface
// and the search target Function has to be on the same Bus and
// Device.
dpParameters.Flags = PCI_USE_CLASS_SUBCLASS | PCI_USE_PROGIF |
PCI_USE_LOCAL_BUS | PCI_USE_LOCAL_DEVICE;
dpParameters.VendorID = 0; // We don't care.
dpParameters.DeviceID = 0; // We don't care.
dpParameters.RevisionID = 0; // We don't care.
dpParameters.SubVendorID = 0; // We don't care.
dpParameters.SubSystemID = 0; // We don't care.
dpParameters.BaseClass = PCI_CLASS_SERIAL_BUS_CTLR;
dpParameters.SubClass = PCI_SUBCLASS_SB_USB;
dpParameters.ProgIf = 0x20; // USB 2.0 Programming Interface
//
// Now call the Device Present Interface to see if the specified
// device is present.
//
result = dpInterface.IsDevicePresentEx(dpInterface.Context,
&dpParameters);
if (result)
{
USBPORT_KdPrint((1,"Found EHCI controller for FDO %08X\n", FdoDeviceObject));
}
else
{
USBPORT_KdPrint((1,"Did not find EHCI controller for FDO %08X\n", FdoDeviceObject));
}
//
// Set the return value
//
*ReturnResult = result;
USBPORT_IsCompanionControllerFreeInterface:
//
// We're done with this interface
//
dpInterface.InterfaceDereference(dpInterface.Context);
USBPORT_IsCompanionControllerDone:
//
// We're done with this device stack
//
ObDereferenceObject(targetDevice);
return status;
}
USB_CONTROLLER_FLAVOR
USBPORT_GetHcFlavor(
PDEVICE_OBJECT FdoDeviceObject,
USHORT PciVendorId,
USHORT PciProductId,
UCHAR PciRevision
)
/*++
Routine Description:
See if this is some known broken HW.
We look at the VID,PID and rev in addition to
reg keys.
Arguments:
Return Value:
HC Flavor
--*/
{
USB_CONTROLLER_FLAVOR flavor;
PDEVICE_EXTENSION devExt;
PAGED_CODE();
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
// set a base type from what the miniport told us
switch (REGISTRATION_PACKET(devExt).HciType) {
case USB_OHCI:
flavor = OHCI_Generic;
break;
case USB_UHCI:
flavor = UHCI_Generic;
break;
case USB_EHCI:
flavor = EHCI_Generic;
break;
default:
flavor = USB_HcGeneric;
}
// hardcoded checks of vid pid
// if (PciVendorId == HC_VID_INTEL &&
// PciProductId == 0x7112) {
// flavor = UHCI_Piix4;
// }
if (PciVendorId == HC_VID_OPTI &&
PciProductId == HC_PID_OPTI_HYDRA) {
flavor = OHCI_Hydra;
}
if (PciVendorId == HC_VID_INTEL) {
if (PciProductId == HC_PID_INTEL_ICH2_1) {
flavor = UHCI_Ich2_1;
} else if (PciProductId == HC_PID_INTEL_ICH2_2) {
flavor = UHCI_Ich2_2;
} else if (PciProductId == HC_PID_INTEL_ICH1) {
flavor = UHCI_Ich1;
}
}
if (PciVendorId == HC_VID_VIA &&
PciProductId == HC_PID_VIA) {
flavor = UHCI_VIA + PciRevision;
}
// for now consider rest of UHCI as PIIX4
if (flavor == UHCI_Generic) {
flavor = UHCI_Piix4;
}
if (flavor == EHCI_Generic) {
// check for variations of EHCI controllers
if (PciVendorId == 0x1033) {
flavor = EHCI_NEC;
}
//if (PciProductId == 0x1033) {
// flavor == EHCI_Lucent;
//}
}
// identify NEC companion controllers by VID/PID
if (PciVendorId == HC_VID_NEC_CC &&
PciProductId == HC_PID_NEC_CC &&
PciRevision == HC_REV_NEC_CC) {
// this controller used func 2 for the USB 2 controller
devExt->Fdo.Usb2BusFunction = 2;
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_IS_CC);
}
// identify VIA companion controllers by VID/PID
if (PciVendorId == HC_VID_VIA_CC &&
PciProductId == HC_PID_VIA_CC &&
PciRevision == HC_REV_VIA_CC) {
// this controller used func 2 for the USB 2 controller
devExt->Fdo.Usb2BusFunction = 2;
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_IS_CC);
}
// identify INTEL companion controllers by VID/PID
if (PciVendorId == HC_VID_INTEL_CC &&
(PciProductId == HC_PID_INTEL_CC1 ||
PciProductId == HC_PID_INTEL_CC2 ||
PciProductId == HC_PID_INTEL_CC3)) {
// this controller used func 7 for the USB 2 controller
devExt->Fdo.Usb2BusFunction = 7;
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_IS_CC);
}
// now check the registry
// NOTE: checking the registry last allows this to override
// any setting from the miniport or the port
USBPORT_GetRegistryKeyValueForPdo(devExt->HcFdoDeviceObject,
devExt->Fdo.PhysicalDeviceObject,
USBPORT_SW_BRANCH,
FLAVOR_KEY,
sizeof(FLAVOR_KEY),
&flavor,
sizeof(flavor));
return flavor;
}
ULONG
USBPORT_ComputeTotalBandwidth(
PDEVICE_OBJECT FdoDeviceObject,
PVOID BusContext
)
/*++
Routine Description:
Compute total bandwidth for this virtual bus
Arguments:
Return Value:
bandwidth availble on this bus
--*/
{
PDEVICE_EXTENSION devExt;
ULONG bw;
PAGED_CODE();
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
if (USBPORT_IS_USB20(devExt)) {
PTRANSACTION_TRANSLATOR translator = BusContext;
// bus conetext will be a TT if the query
// is for a 1.1 device atached to a TT or
// the root hub Pdo if it is a native 2.0
// device
if (translator->Sig == SIG_TT) {
bw = translator->TotalBusBandwidth;
} else {
// return bw on or 2.0 bus
bw = devExt->Fdo.TotalBusBandwidth;
}
} else {
bw = devExt->Fdo.TotalBusBandwidth;
}
USBPORT_KdPrint((1,"'Total bus BW %d\n", bw));
return bw;
}
ULONG
USBPORT_ComputeAllocatedBandwidth(
PDEVICE_OBJECT FdoDeviceObject,
PVOID BusContext
)
/*++
Routine Description:
Compute total bandwidth currently allocated
Arguments:
Return Value:
consumed bandwidth in bits/sec
--*/
{
PDEVICE_EXTENSION devExt;
ULONG totalBW, used, i;
PTRANSACTION_TRANSLATOR translator = BusContext;
PAGED_CODE();
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
if (USBPORT_IS_USB20(devExt) &&
translator->Sig == SIG_TT) {
// bus conetext will be a TT if the query
// is for a 1.1 device atached to a TT or
// the root hub Pdo if it is a native 2.0
// device
totalBW = translator->TotalBusBandwidth;
used = 0;
// table contains available bw, total-available = used
for (i=0; i<USBPORT_MAX_INTEP_POLLING_INTERVAL; i++) {
if (totalBW - translator->BandwidthTable[i] > used) {
used = totalBW - translator->BandwidthTable[i];
}
}
} else {
totalBW = devExt->Fdo.TotalBusBandwidth;
used = 0;
// table contains available bw, total-available = used
for (i=0; i<USBPORT_MAX_INTEP_POLLING_INTERVAL; i++) {
if (totalBW - devExt->Fdo.BandwidthTable[i] > used) {
used = totalBW - devExt->Fdo.BandwidthTable[i];
}
}
}
USBPORT_KdPrint((1,"'Bus BW used %d\n", used));
return used;
}
BOOLEAN
USBPORT_SelectiveSuspendEnabled(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
Compute total bandwidth currently allocated
Arguments:
Return Value:
TRUE if selective suspend supported for this HC
--*/
{
PDEVICE_EXTENSION devExt;
ULONG disableSelectiveSuspend = 0;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
USBPORT_GetRegistryKeyValueForPdo(devExt->HcFdoDeviceObject,
devExt->Fdo.PhysicalDeviceObject,
USBPORT_SW_BRANCH,
DISABLE_SS_KEY,
sizeof(DISABLE_SS_KEY),
&disableSelectiveSuspend,
sizeof(disableSelectiveSuspend));
#if DBG
if (disableSelectiveSuspend) {
USBPORT_KdPrint((1,"'<Selective Suspend> Disabled in Registry for HC\n"));
}
#endif
return disableSelectiveSuspend ? FALSE : TRUE;
}
VOID
USBPORT_InitializeSpinLock(
PUSBPORT_SPIN_LOCK SpinLock,
LONG SigA,
LONG SigR
)
{
KeInitializeSpinLock(&(SpinLock->sl));
SpinLock->Check = -1;
SpinLock->SigA = SigA;
SpinLock->SigR = SigR;
}
#if DBG
VOID
USBPORT_DbgAcquireSpinLock(
PDEVICE_OBJECT FdoDeviceObject,
PUSBPORT_SPIN_LOCK SpinLock,
PKIRQL OldIrql
)
{
PDEVICE_EXTENSION devExt;
LONG n;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
KeAcquireSpinLock(&(SpinLock->sl), OldIrql);
n = InterlockedIncrement(&SpinLock->Check);
LOGENTRY(NULL, FdoDeviceObject, LOG_SPIN, SpinLock->SigA, 0, 0, n);
// detect recursively acquired spinlock
USBPORT_ASSERT(n == 0);
}
VOID
USBPORT_DbgReleaseSpinLock(
PDEVICE_OBJECT FdoDeviceObject,
PUSBPORT_SPIN_LOCK SpinLock,
KIRQL NewIrql
)
{
PDEVICE_EXTENSION devExt;
LONG n;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
n = InterlockedDecrement(&SpinLock->Check);
LOGENTRY(NULL, FdoDeviceObject, LOG_SPIN, SpinLock->SigR, 0xFFFFFFFF, 0, n);
USBPORT_ASSERT(n == -1);
KeReleaseSpinLock(&(SpinLock->sl), NewIrql);
}
#endif
VOID
USBPORT_PowerFault(
PDEVICE_OBJECT FdoDeviceObject,
PUCHAR MessageText
)
/*++
Routine Description:
A power fault has occurred, dump information we will need
to debug it.
In the future we may take additional action such as event
logging and disabling the controller.
Arguments:
Return Value:
none.
--*/
{
PDEVICE_EXTENSION devExt;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
// take a power dump,
// lets see the comment police find this one
USBPORT_KdPrint((0, "*** USBPORT POWER FAULT ***\n"));
USBPORT_KdPrint((0, "Warning:\n"));
USBPORT_KdPrint((0, "The USB controller as failed a consistency check\n"));
USBPORT_KdPrint((0, "following an OS power event.\n"));
USBPORT_KdPrint((0, "The controller will not function and the system\n"));
USBPORT_KdPrint((0, "may bugcheck or hang.\n"));
// print the specific cause
USBPORT_KdPrint((0, "CAUSE: <%s>\n", MessageText));
// now dump relevent power information
USBPORT_KdPrint((0, "FdoDeviceObject: %x\n", FdoDeviceObject));
USBPORT_KdPrint((0, "Returning from? (SystemState): "));
switch(devExt->Fdo.LastSystemSleepState) {
case PowerSystemUnspecified:
USBPORT_KdPrint((0, "PowerSystemUnspecified"));
break;
case PowerSystemWorking:
USBPORT_KdPrint((0, "PowerSystemWorking"));
break;
case PowerSystemSleeping1:
USBPORT_KdPrint((0, "PowerSystemSleeping1"));
break;
case PowerSystemSleeping2:
USBPORT_KdPrint((0, "PowerSystemSleeping2"));
break;
case PowerSystemSleeping3:
USBPORT_KdPrint((0, "PowerSystemSleeping3"));
break;
case PowerSystemHibernate:
USBPORT_KdPrint((0, "PowerSystemHibernate"));
break;
case PowerSystemShutdown:
USBPORT_KdPrint((0, "PowerSystemShutdown"));
break;
}
USBPORT_KdPrint((0, "\n"));
#if DBG
// break if we are in debug mode, otherwise this ends up being a warning
DEBUG_BREAK();
#endif
}
NTSTATUS
USBPORT_CreateLegacyFdoSymbolicLink(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
Create the legacy symbolic name \\DosDevices\HCDn where
n = 0...9,A...Z
Many older applications detect the presence of USB by opening
HCDn
Arguments:
Return Value:
none.
--*/
{
PDEVICE_EXTENSION devExt;
WCHAR legacyLinkBuffer[] = L"\\DosDevices\\HCD0";
WCHAR *buffer;
UNICODE_STRING deviceNameUnicodeString;
NTSTATUS ntStatus;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
USBPORT_ASSERT(!TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_LEGACY_SYM_LINK));
ntStatus = USBPORT_MakeHcdDeviceName(&deviceNameUnicodeString,
devExt->Fdo.DeviceNameIdx);
if (NT_SUCCESS(ntStatus)) {
ALLOC_POOL_Z(buffer,
PagedPool,
sizeof(legacyLinkBuffer));
if (buffer != NULL) {
RtlCopyMemory(buffer,
legacyLinkBuffer,
sizeof(legacyLinkBuffer));
USBPORT_ASSERT(devExt->Fdo.DeviceNameIdx < 10);
buffer[15] = (WCHAR)('0'+ devExt->Fdo.DeviceNameIdx);
RtlInitUnicodeString(&devExt->Fdo.LegacyLinkUnicodeString,
buffer);
ntStatus =
IoCreateSymbolicLink(&devExt->Fdo.LegacyLinkUnicodeString,
&deviceNameUnicodeString);
if (!NT_SUCCESS(ntStatus)) {
// free now since we won't be setting our flag
RtlFreeUnicodeString(&devExt->Fdo.LegacyLinkUnicodeString);
}
} else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
RtlFreeUnicodeString(&deviceNameUnicodeString);
}
if (NT_SUCCESS(ntStatus)) {
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_LEGACY_SYM_LINK);
}
return ntStatus;
}
#if 0
VOID
USBPORT_WriteErrorLogEntry(
PDEVICE_OBJECT FdoDeviceObject,
ULONG DumpDataSize,
PULONG DumData
)
/*++
Routine Description:
Arguments:
Return Value:
none
--*/
{
PIO_ERROR_LOG_PACKET errorLogEntry;
ASSERT_PASSIVE();
errorLogEntry = IoAllocateErrorLogEntry(
FdoDeviceObject,
sizeof(IO_ERROR_LOG_PACKET) + DumpDataSize * sizeof(ULONG) +
sizeof(InsertionStrings)
);
if (errorLogEntry != NULL) {
errorLogEntry->ErrorCode = ErrorCode;
errorLogEntry->SequenceNumber = 0;
errorLogEntry->MajorFunctionCode = 0;
errorLogEntry->RetryCount = 0;
errorLogEntry->UniqueErrorValue = 0;
errorLogEntry->FinalStatus = STATUS_SUCCESS;
errorLogEntry->DumpDataSize = 0;
errorLogEntry->DumpData[0] = DumpData;
}
if (InsertionString) {
errorLogEntry->StringOffset =
sizeof(IO_ERROR_LOG_PACKET);
errorLogEntry->NumberOfStrings = 1;
RtlCopyMemory(
((PCHAR)(errorLogEntry) + errorLogEntry->StringOffset),
InsertionString->Buffer,
InsertionString->Length);
}
IoWriteErrorLogEntry(errorLogEntry);
}
#endif
NTSTATUS
USBPORT_GetDefaultBIOS_X(
PDEVICE_OBJECT FdoDeviceObject,
PULONG BiosX,
PULONG GlobalDisableSS,
PULONG GlobalDisableCCDetect,
PULONG EnIdleEndpointSupport
)
/*++
Routine Description:
Read the regkeys defining BIOS specific hacks, these hacks are
applied globaly to all controllers in the system and usualy
involve power management.
Arguments:
DeviceObject - DeviceObject of the controller
Return Value:
NT status code.
--*/
{
PDEVICE_EXTENSION devExt;
#define MAX_HACK_KEYS 5
NTSTATUS ntStatus;
RTL_QUERY_REGISTRY_TABLE QueryTable[MAX_HACK_KEYS];
PWCHAR usb = L"usb";
ULONG k = 0;
PAGED_CODE();
// Set default BIOS hacks here, these can be overriden by
// global reg keys based on a particular BIOS rev.
// set the default behavior then override with the key
*BiosX = BIOS_X_NO_USB_WAKE_S2;
//
// Set up QueryTable to do the following:
//
// bios hacks
QueryTable[k].QueryRoutine = USBPORT_GetConfigValue;
QueryTable[k].Flags = 0;
QueryTable[k].Name = BIOS_X_KEY;
QueryTable[k].EntryContext = BiosX;
QueryTable[k].DefaultType = REG_DWORD;
QueryTable[k].DefaultData = BiosX;
QueryTable[k].DefaultLength = sizeof(*BiosX);
k++;
QueryTable[k].QueryRoutine = USBPORT_GetConfigValue;
QueryTable[k].Flags = 0;
QueryTable[k].Name = G_DISABLE_SS_KEY;
QueryTable[k].EntryContext = GlobalDisableSS;
QueryTable[k].DefaultType = REG_DWORD;
QueryTable[k].DefaultData = GlobalDisableSS;
QueryTable[k].DefaultLength = sizeof(*GlobalDisableSS);
k++;
QueryTable[k].QueryRoutine = USBPORT_GetConfigValue;
QueryTable[k].Flags = 0;
QueryTable[k].Name = G_DISABLE_CC_DETECT_KEY;
QueryTable[k].EntryContext = GlobalDisableCCDetect;
QueryTable[k].DefaultType = REG_DWORD;
QueryTable[k].DefaultData = GlobalDisableCCDetect;
QueryTable[k].DefaultLength = sizeof(*GlobalDisableCCDetect);
k++;
QueryTable[k].QueryRoutine = USBPORT_GetConfigValue;
QueryTable[k].Flags = 0;
QueryTable[k].Name = G_EN_IDLE_ENDPOINT_SUPPORT;
QueryTable[k].EntryContext = EnIdleEndpointSupport;
QueryTable[k].DefaultType = REG_DWORD;
QueryTable[k].DefaultData = EnIdleEndpointSupport;
QueryTable[k].DefaultLength = sizeof(*EnIdleEndpointSupport);
k++;
USBPORT_ASSERT(k < MAX_HACK_KEYS);
// stop
QueryTable[k].QueryRoutine = NULL;
QueryTable[k].Flags = 0;
QueryTable[k].Name = NULL;
ntStatus = RtlQueryRegistryValues(
RTL_REGISTRY_SERVICES,
usb,
QueryTable, // QueryTable
NULL, // Context
NULL); // Environment
#undef MAX_HACK_KEYS
return ntStatus;
}
VOID
USBPORT_ApplyBIOS_X(
PDEVICE_OBJECT FdoDeviceObject,
PDEVICE_CAPABILITIES DeviceCaps,
ULONG BiosX
)
/*++
Routine Description:
Arguments:
DeviceObject - DeviceObject of the controller
Return Value:
--*/
{
PDEVICE_EXTENSION devExt;
ULONG wakeHack;
HC_POWER_STATE_TABLE tmpPowerTable;
PHC_POWER_STATE hcPowerState;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
if (BiosX == 0) {
return;
}
USBPORT_KdPrint((1, "'USB Apply BIOS Hacks\n"));
wakeHack = BiosX;
// change Device Caps to reflect the fact that the controller
// cannot wake from a given S state
USBPORT_ComputeHcPowerStates(
FdoDeviceObject,
DeviceCaps,
&tmpPowerTable);
switch (wakeHack) {
case BIOS_X_NO_USB_WAKE_S4:
// this has never been tested
if (DeviceCaps->SystemWake >= PowerSystemHibernate) {
USBPORT_KdPrint((1, "'USB BIOS X - disable remote wakeup (S4)\n"));
TEST_TRAP();
DeviceCaps->DeviceState[PowerSystemHibernate] = PowerDeviceD3;
DeviceCaps->SystemWake = PowerSystemSleeping3;
}
break;
case BIOS_X_NO_USB_WAKE_S3:
// this has never been tested
if (DeviceCaps->SystemWake >= PowerSystemSleeping3) {
USBPORT_KdPrint((1, "'USB BIOS X - disable remote wakeup (S3)\n"));
TEST_TRAP();
DeviceCaps->DeviceState[PowerSystemHibernate] = PowerDeviceD3;
DeviceCaps->DeviceState[PowerSystemSleeping3] = PowerDeviceD3;
DeviceCaps->SystemWake = PowerSystemSleeping2;
}
break;
case BIOS_X_NO_USB_WAKE_S2:
if (DeviceCaps->SystemWake >= PowerSystemSleeping2) {
USBPORT_KdPrint((1, "'USB BIOS X - disable remote wakeup (S2)\n"));
DeviceCaps->DeviceState[PowerSystemHibernate] = PowerDeviceUnspecified;
DeviceCaps->DeviceState[PowerSystemSleeping3] = PowerDeviceUnspecified;
DeviceCaps->DeviceState[PowerSystemSleeping2] = PowerDeviceUnspecified;
// mimic how we deterine wake for the root hub
hcPowerState = USBPORT_GetHcPowerState(FdoDeviceObject,
&tmpPowerTable,
PowerSystemSleeping1);
if (hcPowerState &&
hcPowerState->Attributes == HcPower_Y_Wakeup_Y) {
DeviceCaps->SystemWake = PowerSystemSleeping1;
} else {
DeviceCaps->SystemWake = PowerSystemUnspecified;
}
}
break;
case BIOS_X_NO_USB_WAKE_S1:
// this has never been tested
if (DeviceCaps->SystemWake >= PowerSystemSleeping1) {
USBPORT_KdPrint((1, "'USB BIOS X - disable remote wakeup (S1)\n"));
TEST_TRAP();
DeviceCaps->DeviceState[PowerSystemHibernate] = PowerDeviceUnspecified;
DeviceCaps->DeviceState[PowerSystemSleeping3] = PowerDeviceUnspecified;
DeviceCaps->DeviceState[PowerSystemSleeping2] = PowerDeviceUnspecified;
DeviceCaps->DeviceState[PowerSystemSleeping1] = PowerDeviceUnspecified;
DeviceCaps->SystemWake = PowerSystemUnspecified;
}
break;
}
}
USBPORT_OS_VERSION
USBPORT_DetectOSVersion(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
Arguments:
DeviceObject - DeviceObject of the controller
Return Value:
--*/
{
PDEVICE_EXTENSION devExt;
USBPORT_OS_VERSION osVersion;
if (IoIsWdmVersionAvailable(1, 0x20)) {
USBPORT_KdPrint((1, "Detected: WinXP\n"));
osVersion = WinXP;
} else if (IoIsWdmVersionAvailable(1, 0x10)) {
USBPORT_KdPrint((1, "Detected: Win2K\n"));
osVersion = Win2K;
} else if (IoIsWdmVersionAvailable(1, 0x05)) {
USBPORT_KdPrint((1, "Detected: WinMe\n"));
osVersion = WinMe;
} else {
// 98 or 98se
USBPORT_KdPrint((1, "Detected: Win98\n"));
osVersion = Win98;
}
return osVersion;
}
VOID
USBPORT_WriteHaction(
PDEVICE_OBJECT Usb2FdoDeviceObject,
ULONG Haction
)
/*++
Routine Description:
specify 'hactions' for the CC to be taken by the
coinstaller
Arguments:
DeviceObject - DeviceObject of the USB 2 controller
Return Value:
--*/
{
PDEVICE_EXTENSION devExt;
PDEVICE_RELATIONS devRelations;
NTSTATUS ntStatus;
ULONG i;
PAGED_CODE();
devRelations =
USBPORT_FindCompanionControllers(Usb2FdoDeviceObject,
FALSE,
FALSE);
for (i=0; devRelations && i< devRelations->Count; i++) {
PDEVICE_OBJECT pdo = devRelations->Objects[i];
ntStatus = USBPORT_SetRegistryKeyValueForPdo(
pdo,
USBPORT_HW_BRANCH,
REG_DWORD,
HACTION_KEY,
sizeof(HACTION_KEY),
&Haction,
sizeof(Haction));
LOGENTRY(NULL, Usb2FdoDeviceObject, LOG_PNP, 'Hact', pdo,
Haction, ntStatus);
}
// thou shall not leak memory
if (devRelations != NULL) {
FREE_POOL(Usb2FdoDeviceObject, devRelations);
}
}