AppleUSBEHCI_RootHub.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/usb/USB.h>
#include <IOKit/usb/IOUSBLog.h>
#include <IOKit/usb/IOUSBRootHubDevice.h>
#include "AppleUSBEHCI.h"
#include "USBTracepoints.h"
#ifndef APPLEEHCIROOTHUB_USE_KPRINTF
#define APPLEEHCIROOTHUB_USE_KPRINTF 0
#endif
#if APPLEEHCIROOTHUB_USE_KPRINTF
#undef USBLog
#undef USBError
void kprintf(const char *format, ...)
__attribute__((format(printf, 1, 2)));
#define USBLog( LEVEL, FORMAT, ARGS... ) if ((LEVEL) <= APPLEEHCIROOTHUB_USE_KPRINTF) { kprintf( FORMAT "\n", ## ARGS ) ; }
#define USBError( LEVEL, FORMAT, ARGS... ) { kprintf( FORMAT "\n", ## ARGS ) ; }
#endif
#define super IOUSBControllerV3
IOReturn
AppleUSBEHCI::GetRootHubDeviceDescriptor(IOUSBDeviceDescriptor *desc)
{
IOUSBDeviceDescriptor newDesc =
{
sizeof(IOUSBDeviceDescriptor), kUSBDeviceDesc, HostToUSBWord(kUSBRel20), kUSBHubClass, kUSBHubSubClass, 1, 64, HostToUSBWord(kAppleVendorID), HostToUSBWord(kPrdRootHubAppleE), HostToUSBWord(0x0200), 2, 1, 0, 1 };
if (!desc)
return kIOReturnNoMemory;
bcopy(&newDesc, desc, newDesc.bLength);
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::GetRootHubDescriptor(IOUSBHubDescriptor *desc)
{
IOUSBHubDescriptor hubDesc;
UInt32 HCSParams;
UInt8 pps;
unsigned int i, numBytes;
UInt8 * dstPtr;
UInt32 appleCaptive = 0;
OSNumber * appleCaptiveProperty = NULL;
hubDesc.length = sizeof(IOUSBHubDescriptor);
hubDesc.hubType = kUSBHubDescriptorType;
HCSParams = USBToHostLong(_pEHCICapRegisters->HCSParams);
hubDesc.numPorts = _rootHubNumPorts;
pps = (HCSParams & kEHCIPPCMask) != 0;
hubDesc.characteristics = 0;
hubDesc.characteristics |= (pps ? kPerPortSwitchingBit : 0);
if ( !(hubDesc.characteristics & ( kNoOverCurrentBit | kPerPortOverCurrentBit)) )
{
_gangedOvercurrent = true;
}
hubDesc.characteristics = HostToUSBWord(hubDesc.characteristics);
hubDesc.powerOnToGood = 50; hubDesc.hubCurrent = 0;
numBytes = (hubDesc.numPorts + 1) / 8 + 1;
appleCaptiveProperty = OSDynamicCast(OSNumber, _device->getProperty(kAppleInternalUSBDevice));
if (appleCaptiveProperty)
appleCaptive = appleCaptiveProperty->unsigned32BitValue();
dstPtr = (UInt8 *)&hubDesc.removablePortFlags[0];
for ( i = 0; i < numBytes; i++)
{
*dstPtr++ = (UInt8) (appleCaptive & 0xFF);
appleCaptive >>= 8;
}
for (i=0; i<numBytes; i++)
{
*dstPtr++ = 0xFF;
}
hubDesc.length -= ((sizeof(hubDesc.removablePortFlags) - numBytes) +
(sizeof(hubDesc.pwrCtlPortFlags) - numBytes));
if (!desc)
return kIOReturnNoMemory;
bcopy(&hubDesc, desc, hubDesc.length);
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::GetRootHubConfDescriptor(OSData *desc)
{
IOUSBConfigurationDescriptor confDesc =
{
sizeof(IOUSBConfigurationDescriptor), kUSBConfDesc, HostToUSBWord(sizeof(IOUSBConfigurationDescriptor) +
sizeof(IOUSBInterfaceDescriptor) +
sizeof(IOUSBEndpointDescriptor)), 1, 1, 0, 0x60, 0, };
IOUSBInterfaceDescriptor intfDesc =
{
sizeof(IOUSBInterfaceDescriptor), kUSBInterfaceDesc, 0, 0, 1, kUSBHubClass, kUSBHubSubClass, 1, 0 };
IOUSBEndpointDescriptor endptDesc =
{
sizeof(IOUSBEndpointDescriptor), kUSBEndpointDesc, 0x81, kUSBInterrupt, HostToUSBWord(8), 9, };
if (!desc)
return(kIOReturnNoMemory);
if (!desc->appendBytes(&confDesc, confDesc.bLength))
return(kIOReturnNoMemory);
if (!desc->appendBytes(&intfDesc, intfDesc.bLength))
return(kIOReturnNoMemory);
if (!desc->appendBytes(&endptDesc, endptDesc.bLength))
return(kIOReturnNoMemory);
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::SetRootHubDescriptor(OSData * )
{
USBLog(3,"AppleUSBEHCI[%p]::SetRootHubDescriptor unimplemented", this);
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::GetRootHubStatus(IOUSBHubStatus *status)
{
*(UInt32 *)status = 0;
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::SetRootHubFeature(UInt16 wValue)
{
switch(wValue)
{
case kUSBHubLocalPowerChangeFeature :
USBLog(3,"AppleUSBEHCI[%p]: unimplemented Set Power Change Feature", this);
break;
case kUSBHubOverCurrentChangeFeature :
USBLog(3,"AppleUSBEHCI[%p]: unimplemented Set Overcurrent Change Feature", this);
break;
default:
USBLog(3,"AppleUSBEHCI[%p]: Unknown hub set (%d) in root hub", this, wValue);
break;
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::ClearRootHubFeature(UInt16 wValue)
{
switch(wValue)
{
case kUSBHubLocalPowerChangeFeature :
USBLog(3,"AppleUSBEHCI[%p]: unimplemented Clear Power Change Feature", this);
break;
case kUSBHubOverCurrentChangeFeature :
USBLog(3,"AppleUSBEHCI[%p]: unimplemented Clear Overcurrent Change Feature", this);
break;
default:
USBLog(3,"AppleUSBEHCI[%p]: Unknown hub clear (%d) in root hub", this, wValue);
break;
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::GetRootHubPortStatus(IOUSBHubPortStatus *status, UInt16 port)
{
UInt16 statusFlags, changeFlags;
UInt32 portSC, portSCChange;
if ( _myBusState == kUSBBusStateSuspended )
return kIOReturnNotResponding;
port--;
if (port >= kMaxPorts)
{
USBLog(3, "AppleUSBEHCI[%p]::GetRootHubPortStatus Too many ports specified(%d > %d)", this, port, kMaxPorts);
return kIOReturnBadArgument;
}
portSC = USBToHostLong (_pEHCIRegisters->PortSC[port]);
USBLog(7,"AppleUSBEHCI[%p]::GetRootHubPortStatus for port %d, current: 0x%x, previous: 0x%x", this, port+1, (unsigned int)portSC, (unsigned int)_rhPrevStatus[port] );
statusFlags = 0;
if ( (portSC & kEHCIPortSC_Connect) != 0)
{
statusFlags |= kHubPortConnection;
}
if ( (portSC & kEHCIPortSC_Enabled) != 0)
{
statusFlags |= kHubPortEnabled;
}
if (((portSC & kEHCIPortSC_Suspend) != 0))
{
statusFlags |= kHubPortSuspend;
}
if ( (portSC & kEHCIPortSC_OverCurrent) != 0)
{
statusFlags |= kHubPortOverCurrent;
}
if ( (portSC & kEHCIPortSC_Reset) != 0)
{
statusFlags |= kHubPortBeingReset;
}
if ( (portSC & kEHCIPortSC_Power) != 0)
{
statusFlags |= kHubPortPower;
}
statusFlags |= kHubPortHighSpeed;
status->statusFlags = HostToUSBWord(statusFlags);
portSCChange = (_rhPrevStatus[port] ^ portSC); changeFlags = 0;
if ( (portSC & kEHCIPortSC_ConnectChange) != 0)
{
changeFlags |= kHubPortConnection;
if ((statusFlags & kHubPortConnection) == 0)
{
if ((portSCChange & kEHCIPortSC_Suspend) != 0)
_rhChangeBits[port] |= kHubPortSuspend;
}
}
if ( (portSC & kEHCIPortSC_EnableChange) != 0)
{
changeFlags |= kHubPortEnabled;
}
if (_rhChangeBits[port] & kHubPortSuspend)
{
changeFlags |= kHubPortSuspend;
}
if ( (portSC & kEHCIPortSC_OCChange) != 0)
{
changeFlags |= kHubPortOverCurrent;
}
if ( (portSCChange & kEHCIPortSC_Reset) != 0)
{
_rhChangeBits[port] |= kHubPortBeingReset;
}
if (_rhChangeBits[port] & kHubPortBeingReset)
{
changeFlags |= kHubPortBeingReset;
}
if ( (changeFlags & kHubPortOverCurrent) && (_errataBits & kErrataNeedsOvercurrentDebounce) )
{
UInt32 portSC2;
USBLog(3,"AppleUSBEHCI[%p]::GetRootHubPortStatus Have an overcurrent on port (%d), but need to debounce it", this, port + 1);
IOSleep(10);
portSC2 = USBToHostLong (_pEHCIRegisters->PortSC[port]);
if ( (portSC2 & kEHCIPortSC_OverCurrent) == 0)
{
USBLog(3,"AppleUSBEHCI[%p]::GetRootHubPortStatus Overcurrent on port (%d) has gone away, clearing the change", this, port + 1);
changeFlags &= ~kHubPortOverCurrent;
EHCIRootHubResetOverCurrentChange(port+1);
}
}
status->changeFlags = HostToUSBWord(changeFlags);
_rhPrevStatus[port] = portSC;
USBLog(7,"AppleUSBEHCI[%p]::GetRootHubPortStatus for port %d, status: 0x%x, change: 0x%x, ganged: %d", this, port+1, status->statusFlags, status->changeFlags, _gangedOvercurrent );
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::SetRootHubPortFeature(UInt16 wValue, UInt16 wIndex)
{
IOReturn err;
UInt16 port = wIndex;
switch(wValue)
{
case kUSBHubPortSuspendFeature :
USBLog(5,"AppleUSBEHCI[%p]::SetRootHubPortFeature suspending port %d", this, port);
err = EHCIRootHubPortSuspend(port, true);
break;
case kUSBHubPortResetFeature :
err = EHCIRootHubResetPort(port);
break;
case kUSBHubPortEnableFeature :
err = EHCIRootHubPortEnable(port, true);
break;
case kUSBHubPortPowerFeature :
err = EHCIRootHubPortPower(port, true);
err = EHCIRootHubPower(true);
break;
default:
USBLog(3,"AppleUSBEHCI[%p]::SetRootHubPortFeature unknown wValue %d, wIndex %d", this, wValue, wIndex);
err = kIOReturnUnsupported;
break;
}
return err;
}
IOReturn
AppleUSBEHCI::ClearRootHubPortFeature(UInt16 wValue, UInt16 wIndex)
{
IOReturn err;
UInt16 port = wIndex;
USBLog(4, "AppleUSBEHCI[%p]::ClearRootHubPortFeature - port %d, feature: %d", this, wIndex, wValue);
switch(wValue)
{
case kUSBHubPortEnableFeature :
err = EHCIRootHubPortEnable(port, false);
break;
case kUSBHubPortSuspendFeature :
USBLog(5,"AppleUSBEHCI[%p]::ClearRootHubPortFeature resuming port %d", this, port);
err = EHCIRootHubPortSuspend(port, false);
break;
case kUSBHubPortPowerFeature :
if (_errataBits & kErrataIgnoreRootHubPowerClearFeature)
{
USBLog(5,"AppleUSBEHCI[%p]::ClearRootHubPortFeature (kUSBHubPortPowerFeature) port %d, ignoring per errata bit", this, port);
err = kIOReturnUnsupported;
}
else
{
err = EHCIRootHubPortPower(port, false);
}
break;
case kUSBHubPortConnectionChangeFeature :
err = EHCIRootHubResetChangeConnection(port);
break;
case kUSBHubPortEnableChangeFeature :
err = EHCIRootHubResetEnableChange(port);
break;
case kUSBHubPortSuspendChangeFeature :
err = EHCIRootHubResetSuspendChange(port);
break;
case kUSBHubPortOverCurrentChangeFeature :
err = EHCIRootHubResetOverCurrentChange(port);
break;
case kUSBHubPortResetChangeFeature :
err = EHCIRootHubResetResetChange(port);
break;
default:
USBLog(3,"AppleUSBEHCI[%p]::ClearRootHubPortFeature unknown wValue %d, wIndex %d", this, wValue, wIndex);
err = kIOReturnUnsupported;
break;
}
return err;
}
IOReturn
AppleUSBEHCI::GetRootHubPortState(UInt8 *state, UInt16 port)
{
USBLog(5,"AppleUSBEHCI[%p]::GetRootHubPortState for port %d", this, port);
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::SetHubAddress(UInt16 wValue)
{
_rootHubFuncAddress = wValue;
return kIOReturnSuccess;
}
UInt32
getPortSCForWriting(EHCIRegistersPtr _pEHCIRegisters, short port)
{
return USBToHostLong(_pEHCIRegisters->PortSC[port-1]) &
~(kEHCIPortSC_ConnectChange|kEHCIPortSC_EnableChange|kEHCIPortSC_OCChange);
}
IOReturn
AppleUSBEHCI::EHCIRootHubPower(bool on)
{
on = 0;
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::EHCIRootHubResetChangeConnection(UInt16 port)
{
UInt32 value, newValue, count;
value = getPortSCForWriting(_pEHCIRegisters,port);
value |= kEHCIPortSC_ConnectChange;
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong (value);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
count = 0;
while ((newValue & kEHCIPortSC_ConnectChange) && (count++ < 10))
{
USBError(1, "EHCI driver - Connect Change bit not clearing. Retrying");
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
}
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::EHCIRootHubResetResetChange(UInt16 port)
{
port--;
if (port >= kMaxPorts)
{
USBLog(3, "AppleUSBEHCI[%p]::EHCIRootHubResetResetChange Too many ports specified(%d > %d)", this, port, kMaxPorts);
return kIOReturnBadArgument;
}
_rhChangeBits[port] &= ~kHubPortBeingReset;
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::EHCIRootHubResetSuspendChange(UInt16 port)
{
port--;
if (port >= kMaxPorts)
{
USBLog(3, "AppleUSBEHCI[%p]::EHCIRootHubResetSuspendChange Too many ports specified(%d > %d)", this, port, kMaxPorts);
return kIOReturnBadArgument;
}
_rhChangeBits[port] &= ~kHubPortSuspend;
IOSync();
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::EHCIRootHubResetEnableChange(UInt16 port)
{
UInt32 value, newValue, count;
value = getPortSCForWriting(_pEHCIRegisters,port);
value |= kEHCIPortSC_EnableChange;
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong (value);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
count = 0;
while ((newValue & kEHCIPortSC_EnableChange) && (count++ < 10))
{
USBError(1, "EHCI driver - Enable Change bit not clearing. Retrying");
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
}
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::EHCIRootHubResetOverCurrentChange(UInt16 port)
{
UInt32 value, newValue, count;
value = getPortSCForWriting(_pEHCIRegisters,port);
value |= kEHCIPortSC_OCChange;
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong (value);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
count = 0;
while ((newValue & kEHCIPortSC_OCChange) && (count++ < 10))
{
USBError(1, "EHCI driver - OC Change bit not clearing. Retrying");
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
}
}
return kIOReturnSuccess;
}
void
AppleUSBEHCI::waitForSOF(EHCIRegistersPtr pEHCIRegisters)
{
IOSleep(1);
}
IOReturn
AppleUSBEHCI::EHCIRootHubResetPort (UInt16 port)
{
UInt32 value, newValue;
UInt32 count, resetCount, portSC = 0;
USBLog(5, "AppleUSBEHCI[%p]::EHCIRootHubResetPort begin port %d", this, (int)port);
_UIMDiagnostics.resets++;
value = getPortSCForWriting(_pEHCIRegisters, port);
if ( (value & (kEHCIPortSC_Connect | kEHCIPortSC_Power)) != (kEHCIPortSC_Connect | kEHCIPortSC_Power) )
{
USBLog(1, "AppleUSBEHCI[%p]::EHCIRootHubResetPort - Not resetting port, because device is unplugged or powered off (%x).", this, (unsigned int)value);
USBTrace( kUSBTEHCI, kTPEHCIRootHubResetPort, (uintptr_t)this, port, value, 1);
return kIOReturnNotResponding;
}
if ( ((value & kEHCIPortSC_LineSt) >> kEHCIPortSC_LineStPhase) == kEHCILine_Low)
{
if (!(_errataBits & kErrataDontUseCompanionController))
{
value |= kEHCIPortSC_Owner;
value &= ~kEHCIPortSC_WKDSCNNT_E;
USBLog(5, "AppleUSBEHCI[%p]::EHCIRootHubResetPort: LS device detected (portSC = %p) - writing value (%p) to release the device", this, (void*)portSC, (void*)value);
USBTrace( kUSBTEnumeration, kTPEnumerationLowSpeedDevice, (uintptr_t)this, portSC, 0, 0);
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
IOSleep(1);
if (_errataBits & kErrataNECIncompleteWrite)
{
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
count = 0;
while (!(newValue & kEHCIPortSC_Owner) && (count++ < 10))
{
USBError(1, "EHCI driver - PortSC_Owner (LS device) bit not sticking. Retrying");
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
IOSleep(1);
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
}
}
}
else
{
USBLog(1, "EHCI: Low speed device detected with no companion controller (bad hardware?)");
}
return kIOUSBDeviceNotHighSpeed;
}
value |= kEHCIPortSC_Reset;
value &= ~kEHCIPortSC_Enabled;
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong (value);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
count = 0;
while ((count++ < 10) && (!(newValue & kEHCIPortSC_Reset) || (newValue & kEHCIPortSC_Enabled)))
{
USBError(1, "EHCI driver - Reset and enabled bits not sticking. Retrying");
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
}
}
IOSleep(10);
value = getPortSCForWriting(_pEHCIRegisters, port);
value &= ~kEHCIPortSC_Reset;
count = 0;
do {
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong (value);
IOSync();
IOSleep(2);
resetCount = 0;
do {
IOSleep(1);
portSC = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
} while( (portSC & kEHCIPortSC_Reset) && (resetCount++ < 2001) );
if (!(portSC & kEHCIPortSC_Reset))
{
USBLog(5, "AppleUSBEHCI[%p]::EHCIRootHubResetPort reset took extra %d", this, (int)count);
}
else
USBError(1, "EHCIRootHubResetPort - clear reset didn't take - retrying");
} while ((portSC & kEHCIPortSC_Reset) && (count++ < 10));
IOSleep(1);
if ( portSC != USBToHostLong(_pEHCIRegisters->PortSC[port-1]) )
{
USBLog(1, "AppleUSBEHCI[%p]::EHCIRootHubResetPort- portSC is not equal to value of register! (%p)(%p)", this, (void*)portSC, (void*)USBToHostLong(_pEHCIRegisters->PortSC[port-1]));
USBTrace( kUSBTEHCI, kTPEHCIRootHubResetPort, (uintptr_t)this, portSC, USBToHostLong(_pEHCIRegisters->PortSC[port-1]), 2 );
portSC = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
}
if ( ((portSC & kEHCIPortSC_Reset) != 0) || (count >= 2000) )
{
USBLog(4, "AppleUSBEHCI[%p]::EHCIRootHubResetPort- port slow to come out of reset %d", this, (int)count);
}
if ( (portSC & (kEHCIPortSC_Connect | kEHCIPortSC_Power)) != (kEHCIPortSC_Connect | kEHCIPortSC_Power) )
{
USBLog(1, "AppleUSBEHCI[%p]::EHCIRootHubResetPort - Not resetting port 2, because device is unplugged or powered off (%p).", this, (void*)portSC);
USBTrace( kUSBTEHCI, kTPEHCIRootHubResetPort, (uintptr_t)this, port, portSC, 3 );
return kIOReturnNotResponding;
}
USBLog(5, "AppleUSBEHCI[%p]::EHCIRootHubResetPort - Setting port (%d) reset change bit to 0x%x.", this, (uint32_t)port, (uint32_t)value);
_rhChangeBits[port-1] |= kHubPortBeingReset;
if ( (portSC & kEHCIPortSC_Enabled) == 0)
{
if (!(_errataBits & kErrataDontUseCompanionController))
{
value = getPortSCForWriting(_pEHCIRegisters, port);
value |= kEHCIPortSC_Owner;
value &= ~kEHCIPortSC_WKDSCNNT_E;
USBLog(5, "AppleUSBEHCI[%p]::EHCIRootHubResetPort (port %d): FS device detected (portSC = 0x%x) - writing value (0x%x) to release the device", this, (uint32_t)port, (uint32_t)portSC, (uint32_t)value);
USBTrace( kUSBTEnumeration, kTPEnumerationFullSpeedDevice, (uintptr_t)this, portSC, 0, 0);
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
IOSleep(1);
if (_errataBits & kErrataNECIncompleteWrite)
{
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
count = 0;
while (!(newValue & kEHCIPortSC_Owner) && (count++ < 10))
{
USBError(1, "EHCI driver - PortSC_Owner (FS device) bit not sticking. Retrying");
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
IOSleep(1);
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
}
}
}
else
{
USBLog(1, "EHCI: Full speed device detected with no companion controller - probably a hub which failed to Chirp");
}
return kIOUSBDeviceNotHighSpeed;
}
else
{
waitForSOF(_pEHCIRegisters);
IOSleep(1);
portSC = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
if ( (portSC & kEHCIPortSC_Enabled) == 0)
{
USBLog(3, "AppleUSBEHCI[%p]::EHCIRootHubResetPort *********** Port disabled after 1 frame******* 0x%x", this, (uint32_t)portSC);
}
value = getPortSCForWriting(_pEHCIRegisters, port);
value |= kEHCIPortSC_WKDSCNNT_E;
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
}
USBLog(5, "AppleUSBEHCI[%p]::EHCIRootHubResetPort done", this);
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::EHCIRootHubPortEnable(UInt16 port, bool enable)
{
UInt32 value;
USBLog(5,"AppleUSBEHCI[%p]::EHCIRootHubPortEnable port: %d, on: %d", this, port, enable);
if (enable)
{
USBLog(1,"AppleUSBEHCI[%p]::EHCIRootHubPortEnable enabling port illegal.", this);
USBTrace( kUSBTEHCI, kTPEHCIRootHubPortEnable, (uintptr_t)this, port, enable, kIOReturnUnsupported );
return kIOReturnUnsupported;
}
value = getPortSCForWriting(_pEHCIRegisters, port);
value &= ~kEHCIPortSC_Enabled; _pEHCIRegisters->PortSC[port-1] = HostToUSBLong (value);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue, count;
IOSleep(3);
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
count = 0;
while ((newValue & kEHCIPortSC_Enabled) && (count++ < 10))
{
USBError(1, "EHCI driver - PortEnable did not disable. Retrying");
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
}
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::EHCIRootHubPortSuspend(UInt16 port, bool suspend)
{
UInt32 value, newValue, count;
USBLog(5,"AppleUSBEHCI[%p]::EHCIRootHubPortSuspend port: %d, %s", this, port, suspend ? "SUSPEND" : "RESUME");
if (_rhPortBeingResumed[port-1])
{
if (!suspend)
{
USBLog(3, "AppleUSBEHCI[%p]::EHCIRootHubPortSuspend - resume on port (%d) already being resumed - gracefully ignoring", this, (int)port);
return kIOReturnSuccess;
}
USBLog(1, "AppleUSBEHCI[%p]::EHCIRootHubPortSuspend - trying to suspend port (%d) which is being resumed - UNEXPECTED", this, (int)port);
USBTrace( kUSBTEHCI, kTPEHCIRootHubPortSuspend, (uintptr_t)this, (int)port, suspend, 1 );
}
value = getPortSCForWriting(_pEHCIRegisters, port);
if (suspend)
{
value |= kEHCIPortSC_Suspend;
_rhPrevStatus[port-1] = value;
}
else
{
value |= kEHCIPortSC_Resume; }
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
IOSleep(1); if (_errataBits & kErrataNECIncompleteWrite)
{
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
count = 0;
while (!(newValue & (suspend ? kEHCIPortSC_Suspend : kEHCIPortSC_Resume)) && (count++ < 10))
{
USBError(1, "EHCI driver - PortSuspend did not stick. Retrying");
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
IOSleep(1); }
}
if (!suspend)
{
USBLog(5,"AppleUSBEHCI[%p]::EHCIRootHubPortSuspend - resuming port %d, calling out to timer", this, (int)port);
_rhPortBeingResumed[port-1] = true;
if ( _rhResumePortTimerThread[port-1] == NULL )
{
USBLog(1,"AppleUSBEHCI[%p]::EHCIRootHubPortSuspend - resuming port %d, but callout thread is NULL", this, (int)port);
USBTrace( kUSBTEHCI, kTPEHCIRootHubPortSuspend, (uintptr_t)this, port, 0, 2 );
}
else
thread_call_enter1(_rhResumePortTimerThread[port-1], (void*)port);
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::EHCIRootHubPortPower(UInt16 port, bool on)
{
UInt32 value, newValue, count;
value = getPortSCForWriting(_pEHCIRegisters, port);
if (on)
{
value |= kEHCIPortSC_Power; _pEHCIRegisters->PortSC[port-1] = HostToUSBLong (value);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
count = 0;
while (!(newValue & kEHCIPortSC_Power) && (count++ < 10))
{
USBError(1, "EHCI driver - PortPower bit not sticking. Retrying");
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
}
}
value |= kEHCIPortSC_WKCNNT_E | kEHCIPortSC_WKDSCNNT_E; }
else
{
value &= ~kEHCIPortSC_Power; }
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong (value);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
count = 0;
while ((on ? (!(newValue & kEHCIPortSC_WKCNNT_E) || !(newValue & kEHCIPortSC_WKDSCNNT_E)) : (newValue & kEHCIPortSC_Power)) && (count++ < 10))
{
USBError(1, "EHCI driver - PortPower end bits not sticking. Retrying");
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
}
}
return kIOReturnSuccess;
}
void
AppleUSBEHCI::UIMRootHubStatusChange( bool abort )
{
USBLog(1, "AppleUSBEHCI[%p]::UIMRootHubStatusChange - calling obsolete method UIMRootHubStatusChange(bool)", this);
}
void
AppleUSBEHCI::UIMRootHubStatusChange(void)
{
UInt32 HCSParams;
UInt16 statusChangedBitmap;
IOUSBHubPortStatus portStatus;
UInt32 hubStatus, statusBit, tempStatus;
unsigned int index, port, move;
bool overCurrentReported = false;
IOReturn retStatus;
USBLog(7, "AppleUSBEHCI[%p]::UIMRootHubStatusChange - USBIntr[%p] _myPowerState[%d]", this, (void*)USBToHostLong(_pEHCIRegisters->USBIntr), (int)_myPowerState);
statusChangedBitmap = 0;
statusBit = 1;
if (_controllerAvailable && !_wakingFromHibernation )
{
RHCheckForPortResumes();
if (GetRootHubStatus((IOUSBHubStatus *)&tempStatus) == kIOReturnSuccess)
{
hubStatus = USBToHostLong( tempStatus );
if ((hubStatus & (kHubLocalPowerStatus | kHubOverCurrentIndicator) ) != 0)
statusChangedBitmap |= statusBit;
HCSParams = USBToHostLong(_pEHCICapRegisters->HCSParams);
USBLog(7,"AppleUSBEHCI[%p]::UIMRootHubStatusChange numPorts %d _wakingFromHibernation(%s)", this, _rootHubNumPorts, _wakingFromHibernation ? "true" : "false");
for (port = 1; port <= _rootHubNumPorts; port++)
{
statusBit <<= 1;
retStatus = GetRootHubPortStatus(&portStatus, port);
if (retStatus != kIOReturnSuccess)
{
USBLog(5, "AppleUSBEHCI[%p]::UIMRootHubStatusChange - got status (%p) from GetRootHubPortStatus for port (%d) - skipping", this, (void*)retStatus, (int)port);
continue;
}
portStatus.changeFlags = USBToHostWord(portStatus.changeFlags);
portStatus.statusFlags = USBToHostWord(portStatus.statusFlags);
if ( _gangedOvercurrent && overCurrentReported && ( portStatus.changeFlags & kHubPortOverCurrent) )
{
USBLog(3,"AppleUSBEHCI[%p]::UIMRootHubStatusChange port %d had an overcurrent, but another port already reported it", this, port);
portStatus.changeFlags &= ~kHubPortOverCurrent;
EHCIRootHubResetOverCurrentChange(port);
}
if (portStatus.changeFlags & kHubPortStateChangeMask)
{
USBLog(4,"AppleUSBEHCI[%p]::UIMRootHubStatusChange port %d status(0x%04x) change(0x%04x)", this, port, portStatus.statusFlags, portStatus.changeFlags);
if ( portStatus.changeFlags & kHubPortOverCurrent )
{
USBLog(3,"AppleUSBEHCI[%p]::UIMRootHubStatusChange port %d had an overcurrent", this, port);
overCurrentReported = true;
}
statusChangedBitmap |= statusBit; }
}
}
}
if (statusChangedBitmap)
{
USBLog(3,"AppleUSBEHCI[%p]::UIMRootHubStatusChange got bitmap (%p)", this, (void*)statusChangedBitmap);
}
_rootHubStatusChangedBitmap = statusChangedBitmap;
}
IOReturn
AppleUSBEHCI::SimulateEDDelete (short endpointNumber, short direction)
{
return SimulateEDAbort(endpointNumber, direction);
}
IOReturn
AppleUSBEHCI::SimulateEDAbort (short endpointNumber, short direction)
{
int i;
if (endpointNumber == 1)
{
if (direction != kUSBIn)
{
USBLog(3, "AppleUSBEHCI[%p]::SimulateEDAbort - Root hub wrong direction Int pipe %d", this, direction);
return kIOReturnInternalError;
}
USBLog(5, "AppleUSBEHCI[%p]::SimulateEDAbort Root hub aborting int transactions", this);
RootHubAbortInterruptRead();
}
else
{
USBLog(5, "AppleUSBEHCI[%p]::SimulateEDAbort Root hub aborting control pipe (NOP)", this);
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::GetRootHubStringDescriptor(UInt8 index, OSData *desc)
{
UInt8 productName[] = {
0, kUSBStringDesc, 0x45, 0x00, 0x48, 0x00, 0x43, 0x00, 0x49, 0x00, 0x20, 0x00, 0x52, 0x00, 0x6F, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x20, 0x00, 0x48, 0x00, 0x75, 0x00, 0x62, 0x00, 0x20, 0x00, 0x53, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x75, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, };
UInt8 vendorName[] = {
0, kUSBStringDesc, 0x41, 0x00, 0x70, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x2e, 0x00 };
if ( index > 2 )
return kIOReturnBadArgument;
vendorName[0] = sizeof(vendorName);
productName[0] = sizeof(productName);
if ( index == 1 )
{
if (!desc)
return kIOReturnNoMemory;
if (!desc->appendBytes(&productName, productName[0]))
return kIOReturnNoMemory;
}
if ( index == 2 )
{
if (!desc)
return kIOReturnNoMemory;
if (!desc->appendBytes(&vendorName, vendorName[0]))
return kIOReturnNoMemory;
}
return kIOReturnSuccess;
}
AbsoluteTime
AppleUSBEHCI::LastRootHubPortStatusChanged( bool resetTime )
{
uint64_t currentTime;
if ( resetTime )
{
currentTime = mach_absolute_time();
_lastRootHubStatusChanged = *(AbsoluteTime*) ¤tTime;
}
return _lastRootHubStatusChanged;
}
void
AppleUSBEHCI::GetNumberOfPorts( UInt8 * numPorts )
{
UInt32 descriptorA;
if ( _myBusState == kUSBBusStateReset )
{
*numPorts = 0;
}
else
{
*numPorts = _rootHubNumPorts;
}
}
void
AppleUSBEHCI::RHResumePortTimerEntry(OSObject *target, thread_call_param_t port)
{
AppleUSBEHCI *me = OSDynamicCast(AppleUSBEHCI, target);
if (!me)
return;
me->RHResumePortTimer((uintptr_t)port);
}
void
AppleUSBEHCI::RHResumePortTimer(UInt32 port)
{
if (!_commandGate)
return;
USBLog(5, "AppleUSBEHCI[%p]::RHResumePortTimer - timing the resume for port %d", this, (int)port);
IOSleep(20); USBLog(6, "AppleUSBEHCI[%p]::RHResumePortTimer - Host controller resume about to finish - calling EnsureUsability", this);
EnsureUsability();
_commandGate->runAction(RHResumePortCompletionEntry, (void*)port);
}
IOReturn
AppleUSBEHCI::RHResumePortCompletionEntry(OSObject *target, void *param1, void *param2, void *param3, void *param4)
{
AppleUSBEHCI *me = OSDynamicCast(AppleUSBEHCI, target);
UInt32 port = (uintptr_t)param1;
if (!me)
return kIOReturnInternalError;
return me->RHResumePortCompletion(port);
}
IOReturn
AppleUSBEHCI::RHResumePortCompletion(UInt32 port)
{
UInt32 value;
USBLog(5, "AppleUSBEHCI[%p]::RHResumePortCompletion - finishing resume on port %d", this, (int)port);
if (!_rhPortBeingResumed[port-1])
{
USBLog(1, "AppleUSBEHCI[%p]::RHResumePortCompletion - port %d does not appear to be resuming!", this, (int)port);
USBTrace( kUSBTEHCI, kTPEHCIRHResumePortCompletion, (uintptr_t)this, port, kIOReturnInternalError, 0);
return kIOReturnInternalError;
}
if (!_controllerAvailable)
{
USBLog(5, "AppleUSBEHCI[%p]::RHResumePortCompletion - cannot finish resume on port %d because the controller is unavailable", this, (int)port);
_rhPortBeingResumed[port-1] = false;
return kIOReturnInternalError;
}
value = getPortSCForWriting(_pEHCIRegisters, port);
value &= ~kEHCIPortSC_Resume; _pEHCIRegisters->PortSC[port-1] = HostToUSBLong (value);
IOSync();
IOSleep(2); if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue, count;
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
count = 0;
while ((newValue & kEHCIPortSC_Resume) && (count++ < 10))
{
USBError(1, "EHCI driver - PortSuspend reesume did not clear. Retrying");
_pEHCIRegisters->PortSC[port-1] = HostToUSBLong(value);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->PortSC[port-1]);
IOSleep(3); }
}
_rhPortBeingResumed[port-1] = false;
_rhChangeBits[port-1] |= kHubPortSuspend;
CheckForRootHubChanges();
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::RHCompleteResumeOnAllPorts()
{
UInt32 value;
int i;
UInt32 portIndex;
IOReturn returnValue = kIOReturnSuccess;
UInt32 waitTime = 0;
USBLog(6, "AppleUSBEHCI[%p]::RHResumeAllPorts", this);
if (!_controllerAvailable)
{
for (portIndex = 0; portIndex < _rootHubNumPorts; portIndex++)
{
USBLog(5, "AppleUSBEHCI[%p]::RHResumeAllPorts - cannot finish resume on port %d because the controller is unavailable", this, (int)portIndex+1);
if ( _rhPortBeingResumed[portIndex] )
_rhPortBeingResumed[portIndex] = false;
}
return kIOReturnInternalError;
}
for (portIndex = 0; portIndex < _rootHubNumPorts; portIndex++)
{
UInt32 port = portIndex + 1;
if ( _rhPortBeingResumed[portIndex] )
{
USBLog(5, "AppleUSBEHCI[%p]::RHResumeAllPorts - resuming port %d", this, (int)port);
value = getPortSCForWriting(_pEHCIRegisters, port);
value &= ~kEHCIPortSC_Resume; _pEHCIRegisters->PortSC[portIndex] = HostToUSBLong (value);
IOSync();
waitTime = 2;
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue, count;
newValue = USBToHostLong(_pEHCIRegisters->PortSC[portIndex]);
count = 0;
while ((newValue & kEHCIPortSC_Resume) && (count++ < 10))
{
USBError(1, "EHCI driver - PortSuspend reesume did not clear. Retrying");
_pEHCIRegisters->PortSC[portIndex] = HostToUSBLong(value);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->PortSC[portIndex]);
waitTime = 3; }
}
}
}
if ( waitTime )
IOSleep(waitTime);
for (portIndex = 0; portIndex < _rootHubNumPorts; portIndex++)
{
if ( _rhPortBeingResumed[portIndex] )
{
_rhPortBeingResumed[portIndex] = false;
_rhChangeBits[portIndex] |= kHubPortSuspend; }
}
return kIOReturnSuccess;
}
void
AppleUSBEHCI::RHCheckForPortResumes()
{
int i;
UInt32 port;
for (port = 0; port < _rootHubNumPorts; port++)
{
if (!_rhPortBeingResumed[port])
{
UInt32 portStatus = USBToHostLong(_pEHCIRegisters->PortSC[port]);
if (portStatus & kEHCIPortSC_Resume)
{
USBLog(5, "AppleUSBEHCI[%p]::RHCheckForPortResumes - port %d appears to be resuming from a remote wakeup - spawning thread to resume", this, (int)port+1);
_rhPortBeingResumed[port] = true;
if ( _rhResumePortTimerThread[port] == NULL )
{
USBLog(1,"AppleUSBEHCI[%p]::RHCheckForPortResumes port %d appears to be resuming from a remote wakeup, but the thread callout is NULL!", this, (uint32_t)port+1);
}
else
thread_call_enter1(_rhResumePortTimerThread[port], (void*)(port+1));
}
}
}
}