/*++ Copyright (c) 1999 Microsoft Corporation Module Name: roothub.c Abstract: miniport root hub Environment: kernel mode only Notes: THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. Copyright (c) 1999 Microsoft Corporation. All Rights Reserved. Revision History: 2-19-99 : created, jdunn implements the following miniport functions: MINIPORT_RH_GetStatus MINIPORT_RH_GetPortStatus MINIPORT_RH_GethubStatus MINIPORT_RH_SetFeaturePortReset MINIPORT_RH_SetFeaturePortSuspend MINIPORT_RH_SetFeaturePortPower MINIPORT_RH_ClearFeaturePortEnable MINIPORT_RH_ClearFeaturePortSuspend MINIPORT_RH_ClearFeaturePortPower MINIPORT_RH_ClearFeaturePortConnectChange MINIPORT_RH_ClearFeaturePortResetChange MINIPORT_RH_ClearFeaturePortEnableChange MINIPORT_RH_ClearFeaturePortSuspendChange MINIPORT_RH_ClearFeaturePortOvercurrentChange --*/ #include "common.h" VOID OHCI_RH_GetRootHubData( PDEVICE_DATA DeviceData, PROOTHUB_DATA HubData ) /*++ return info about the root hub --*/ { HC_RH_DESCRIPTOR_A descrA; PHC_OPERATIONAL_REGISTER hc; hc = DeviceData->HC; descrA.ul = OHCI_ReadRhDescriptorA(DeviceData); OHCI_ASSERT(DeviceData, (descrA.ul) && (!(descrA.ul & HcDescA_RESERVED))); HubData->NumberOfPorts = descrA.s.NumberDownstreamPorts; DeviceData->NumberOfPorts = HubData->NumberOfPorts; HubData->HubCharacteristics.us = (USHORT)descrA.s.HubChars; HubData->PowerOnToPowerGood = descrA.s.PowerOnToPowerGoodTime; // This may upset the stopwatch fanatics, but it appears that a minimum // delay is necessary here in some cases. One example being resuming from // hibernation on a 7800 with a USB IntelliMouse Explorer attached. // (The delay happens in the hub driver after powering on each port). // HubData->PowerOnToPowerGood = max(descrA.s.PowerOnToPowerGoodTime, 25); // OHCI controllers generally use the 1.0 USB spec. // HubChars were revised in 1.1 so we need to do some // mapping. // We will assume it is gang switched unless the port // power switching bit is set HubData->HubCharacteristics.PowerSwitchType = USBPORT_RH_POWER_SWITCH_GANG; if (descrA.ul & HcDescA_PowerSwitchingModePort) { HubData->HubCharacteristics.PowerSwitchType = USBPORT_RH_POWER_SWITCH_PORT; } // this value is the current consumed by the hub // brains, for the embedded hub this doesn't make // much sense. // so we report zero. // HubData->HubControlCurrent = 0; } USB_MINIPORT_STATUS OHCI_RH_GetStatus( PDEVICE_DATA DeviceData, PUSHORT Status ) /*++ get the device status --*/ { // the root hub is self powered *Status = USB_GETSTATUS_SELF_POWERED; return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_ClearFeaturePortEnable ( PDEVICE_DATA DeviceData, USHORT PortNumber ) { PHC_OPERATIONAL_REGISTER hc; hc = DeviceData->HC; WRITE_REGISTER_ULONG(&hc->HcRhPortStatus[PortNumber-1], HcRhPS_ClearPortEnable); return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_ClearFeaturePortPower ( PDEVICE_DATA DeviceData, USHORT PortNumber ) { PHC_OPERATIONAL_REGISTER hc; hc = DeviceData->HC; WRITE_REGISTER_ULONG(&hc->HcRhPortStatus[PortNumber-1], HcRhPS_ClearPortPower); return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_GetPortStatus( PDEVICE_DATA DeviceData, USHORT PortNumber, PRH_PORT_STATUS portStatus ) /*++ get the status of a partuclar port --*/ { PHC_OPERATIONAL_REGISTER hc; PULONG pulRegister; ULONG statusAsUlong; ULONG i; hc = DeviceData->HC; // hw array is zero based // pulRegister = &hc->HcRhPortStatus[PortNumber-1]; // // by coincedence rhStatus register defined in OHCI is an // exact match of the RH_PORT_STATUS define in the USB core // spec. // // If this register reads as all zero or any of the reserved bits are set // then try reading the register again. This is a workaround for some // early revs of the AMD K7 chipset, which can sometimes return bogus values // if the root hub registers are read while the host controller is // performing PCI bus master ED & TD reads. // for (i = 0; i < 10; i++) { statusAsUlong = READ_REGISTER_ULONG(pulRegister); if ((statusAsUlong) && (!(statusAsUlong & HcRhPS_RESERVED))) { break; } else { KeStallExecutionProcessor(5); } } portStatus->ul = statusAsUlong; return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_SetFeaturePortReset( PDEVICE_DATA DeviceData, USHORT PortNumber ) /*++ Put a port in reset --*/ { PHC_OPERATIONAL_REGISTER hc; hc = DeviceData->HC; WRITE_REGISTER_ULONG(&hc->HcRhPortStatus[PortNumber-1], HcRhPS_SetPortReset); return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_SetFeaturePortSuspend( PDEVICE_DATA DeviceData, USHORT PortNumber ) /*++ Put a port in suspend --*/ { PHC_OPERATIONAL_REGISTER hc; hc = DeviceData->HC; WRITE_REGISTER_ULONG(&hc->HcRhPortStatus[PortNumber-1], HcRhPS_SetPortSuspend); return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_ClearFeaturePortSuspend( PDEVICE_DATA DeviceData, USHORT PortNumber ) /*++ --*/ { PHC_OPERATIONAL_REGISTER hc; hc = DeviceData->HC; WRITE_REGISTER_ULONG(&hc->HcRhPortStatus[PortNumber-1], HcRhPS_ClearPortSuspend); return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_ClearFeaturePortSuspendChange( PDEVICE_DATA DeviceData, USHORT PortNumber ) /*++ --*/ { PHC_OPERATIONAL_REGISTER hc; hc = DeviceData->HC; WRITE_REGISTER_ULONG(&hc->HcRhPortStatus[PortNumber-1], HcRhPS_ClearPortSuspendStatusChange); return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_ClearFeaturePortOvercurrentChange( PDEVICE_DATA DeviceData, USHORT PortNumber ) /*++ --*/ { PHC_OPERATIONAL_REGISTER hc; hc = DeviceData->HC; if (PortNumber == 0) { WRITE_REGISTER_ULONG(&hc->HcRhStatus, HcRhS_ClearOverCurrentIndicatorChange); } else { WRITE_REGISTER_ULONG(&hc->HcRhPortStatus[PortNumber-1], HcRhPS_ClearPortOverCurrentChange); } return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_SetFeaturePortPower( PDEVICE_DATA DeviceData, USHORT PortNumber ) /*++ --*/ { PHC_OPERATIONAL_REGISTER hc; hc = DeviceData->HC; WRITE_REGISTER_ULONG(&hc->HcRhPortStatus[PortNumber-1], HcRhPS_SetPortPower); return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_SetFeaturePortEnable( PDEVICE_DATA DeviceData, USHORT PortNumber ) /*++ --*/ { PHC_OPERATIONAL_REGISTER hc; hc = DeviceData->HC; WRITE_REGISTER_ULONG(&hc->HcRhPortStatus[PortNumber-1], HcRhPS_SetPortEnable); return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_ClearFeaturePortConnectChange( PDEVICE_DATA DeviceData, USHORT PortNumber ) /*++ --*/ { PHC_OPERATIONAL_REGISTER hc; hc = DeviceData->HC; WRITE_REGISTER_ULONG(&hc->HcRhPortStatus[PortNumber-1], HcRhPS_ClearConnectStatusChange); return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_ClearFeaturePortEnableChange( PDEVICE_DATA DeviceData, USHORT PortNumber ) /*++ --*/ { PHC_OPERATIONAL_REGISTER hc; hc = DeviceData->HC; WRITE_REGISTER_ULONG(&hc->HcRhPortStatus[PortNumber-1], HcRhPS_ClearPortEnableStatusChange); return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_GetHubStatus( PDEVICE_DATA DeviceData, PRH_HUB_STATUS HubStatus ) { PHC_OPERATIONAL_REGISTER hc; ULONG statusAsUlong; hc = DeviceData->HC; // we will never report a localpower change HubStatus->LocalPowerLost = 0; HubStatus->LocalPowerChange = 0; // see if we should reort an overcurrent condition // statusAsUlong = READ_REGISTER_ULONG(&hc->HcRhStatus); HubStatus->OverCurrent = (statusAsUlong & HcRhS_OverCurrentIndicator) ? 1: 0; HubStatus->OverCurrentChange = (statusAsUlong & HcRhS_OverCurrentIndicatorChange) ? 1: 0; return USBMP_STATUS_SUCCESS; } USB_MINIPORT_STATUS OHCI_RH_ClearFeaturePortResetChange( PDEVICE_DATA DeviceData, USHORT PortNumber ) /*++ --*/ { PHC_OPERATIONAL_REGISTER hc; hc = DeviceData->HC; WRITE_REGISTER_ULONG(&hc->HcRhPortStatus[PortNumber-1], HcRhPS_ClearPortResetStatusChange); return USBMP_STATUS_SUCCESS; }