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"
#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) <= 5) { kprintf( FORMAT "\n", ## ARGS ) ; }
#define USBError( LEVEL, FORMAT, ARGS... ) { kprintf( FORMAT "\n", ## ARGS ) ; }
#endif
enum {
kAppleVendorID = 0x05AC,
kPrdRootHubAppleE = 0x8006,
kEHCIRootHubPollingInterval = 32 };
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;
int i, numBytes;
UInt8 *dstPtr;
hubDesc.length = sizeof(IOUSBHubDescriptor);
hubDesc.hubType = kUSBHubDescriptorType;
HCSParams = USBToHostLong(_pEHCICapRegisters->HCSParams);
hubDesc.numPorts = HCSParams & kEHCINumPortsMask;
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;
dstPtr = (UInt8 *)&hubDesc.removablePortFlags[0];
for (i=0; i<numBytes; i++) {
*dstPtr++ = 0;
}
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), 12, };
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 portFlags;
UInt32 portSC, portSCChange;
if ( _ehciBusState == kEHCIBusStateSuspended )
return kIOReturnNotResponding;
if (port < 1 || port > 15)
return(kIOReturnBadArgument);
port--;
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] );
portFlags = 0;
if( (portSC & kEHCIPortSC_Connect) != 0)
{
portFlags |= kHubPortConnection;
}
if( (portSC & kEHCIPortSC_Enabled) != 0)
{
portFlags |= kHubPortEnabled;
}
if( (portSC & kEHCIPortSC_Suspend) != 0)
{
portFlags |= kHubPortSuspend;
}
if( (portSC & kEHCIPortSC_OverCurrent) != 0)
{
portFlags |= kHubPortOverCurrent;
}
if( (portSC & kEHCIPortSC_Reset) != 0)
{
portFlags |= kHubPortBeingReset;
}
if( (portSC & kEHCIPortSC_Power) != 0)
{
portFlags |= kHubPortPower;
}
portFlags |= kHubPortHighSpeed;
status->statusFlags = HostToUSBWord(portFlags);
portSCChange = (_rhPrevStatus[port] ^ portSC); portFlags = 0;
if( (portSC & kEHCIPortSC_ConnectChange) != 0)
{
portFlags |= kHubPortConnection;
}
if( (portSC & kEHCIPortSC_EnableChange) != 0)
{
portFlags |= kHubPortEnabled;
}
if( (portSCChange & (kEHCIPortSC_Suspend | kEHCIPortSC_Resume)) != 0)
{
_rhChangeBits[port] |= kHubPortSuspend;
}
if(_rhChangeBits[port] & kHubPortSuspend)
{
portFlags |= kHubPortSuspend;
}
if( (portSC & kEHCIPortSC_OCChange) != 0)
{
portFlags |= kHubPortOverCurrent;
}
if( (portSCChange & kEHCIPortSC_Reset) != 0)
{
_rhChangeBits[port] |= kHubPortBeingReset;
}
if(_rhChangeBits[port] & kHubPortBeingReset)
{
portFlags |= kHubPortBeingReset;
}
if ( (portFlags & 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);
portFlags &= ~kHubPortOverCurrent;
EHCIRootHubResetOverCurrentChange(port+1);
}
}
status->changeFlags = HostToUSBWord(portFlags);
_rhPrevStatus[port] = portSC;
USBLog(6,"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;
if ( _idleSuspend )
{
USBLog(4, "AppleUSBEHCI[%p]::SetRootHubPortFeature - in _idleSuspend - restarting", this);
setPowerState(kEHCISetPowerLevelRunning, this);
}
switch(wValue)
{
case kUSBHubPortSuspendFeature :
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);
if ( _idleSuspend )
{
USBLog(4, "AppleUSBEHCI[%p]::ClearRootHubPortFeature - in _idleSuspend - restarting", this);
setPowerState(kEHCISetPowerLevelRunning, this);
}
switch(wValue)
{
case kUSBHubPortEnableFeature :
err = EHCIRootHubPortEnable(port, false);
break;
case kUSBHubPortSuspendFeature :
err = EHCIRootHubPortSuspend(port, false);
break;
case kUSBHubPortPowerFeature :
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", this);
value = getPortSCForWriting(_pEHCIRegisters, port);
if( (value & (kEHCIPortSC_Connect | kEHCIPortSC_Power)) != (kEHCIPortSC_Connect | kEHCIPortSC_Power) )
{
USBLog(1, "AppleUSBEHCI[%p]::EHCIRootHubResetPort - Not resetting port, beacuse device is unplugged of powered off (%x).", this, (unsigned int)value);
return kIOReturnNotResponding;
}
if( ((value & kEHCIPortSC_LineSt) >> kEHCIPortSC_LineStPhase) == kEHCILine_Low)
{
value |= kEHCIPortSC_Owner;
USBLog(5, "AppleUSBEHCI[%p]::EHCIRootHubResetPort: LS device detected (portSC = %p) - writing value (%p) to release the device", this, (void*)portSC, (void*)value);
_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]);
}
}
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]));
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, beacuse device is unplugged of powered off (%p).", this, (void*)portSC);
return kIOReturnNotResponding;
}
USBLog(5, "AppleUSBEHCI[%p]::EHCIRootHubResetPort - Setting port reset change bit to 0x%lx.", this, value);
_rhChangeBits[port-1] |= kHubPortBeingReset;
if( (portSC & kEHCIPortSC_Enabled) == 0)
{
value = getPortSCForWriting(_pEHCIRegisters, port);
value |= kEHCIPortSC_Owner;
USBLog(5, "AppleUSBEHCI[%p]::EHCIRootHubResetPort: FS device detected (portSC = 0x%lx) - writing value (0x%lx) to release the device", this, portSC, value);
_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]);
}
}
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%lx", this, portSC);
}
}
USBLog(5, "AppleUSBEHCI[%p]::EHCIRootHubResetPort done", this);
USBLog(6, "AppleUSBEHCI[%p]::EHCIRootHubResetPort - Call UIMRootHubStatusChange", this);
UIMRootHubStatusChange();
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);
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");
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)
{
IOSleep(20);
value = getPortSCForWriting(_pEHCIRegisters, port);
value &= ~kEHCIPortSC_Resume; _pEHCIRegisters->PortSC[port-1] = HostToUSBLong (value);
IOSync();
IOSleep(3); if (_errataBits & kErrataNECIncompleteWrite)
{
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); }
}
UIMRootHubStatusChange();
}
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(void)
{
UIMRootHubStatusChange(false);
}
OSMetaClassDefineReservedUsed(IOUSBController, 10);
void
AppleUSBEHCI::UIMRootHubStatusChange(bool abort)
{
UInt8 numPorts = 0;
UInt32 HCSParams;
UInt16 statusChangedBitmap;
IOUSBHubPortStatus portStatus;
UInt32 hubStatus, statusBit, tempStatus;
unsigned int index, port, move;
struct EHCIRHInterruptTransaction last;
bool overCurrentReported = false;
if (_ehciAvailable)
{
_pEHCIRegisters->USBIntr = _pEHCIRegisters->USBIntr & ~HostToUSBLong(kEHCIPortChangeIntBit);
IOSync();
}
statusChangedBitmap = 0;
statusBit = 1;
if (!abort && _ehciAvailable && !_wakingFromHibernation && (GetRootHubStatus((IOUSBHubStatus *)&tempStatus) == kIOReturnSuccess))
{
hubStatus = USBToHostLong( tempStatus );
if ((hubStatus & (kHubLocalPowerStatus | kHubOverCurrentIndicator) ) != 0)
statusChangedBitmap |= statusBit;
HCSParams = USBToHostLong(_pEHCICapRegisters->HCSParams);
numPorts = HCSParams & kEHCINumPortsMask;
USBLog(5,"AppleUSBEHCI[%p]::UIMRootHubStatusChange numPorts %d _wakingFromHibernation(%s)", this, numPorts, _wakingFromHibernation ? "true" : "false");
for (port = 1; port <= numPorts; port++)
{
statusBit <<= 1;
GetRootHubPortStatus(&portStatus, port);
portStatus.changeFlags = USBToHostWord(portStatus.changeFlags);
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%x", this, port, portStatus.changeFlags);
if ( portStatus.changeFlags & kHubPortOverCurrent )
{
USBLog(3,"AppleUSBEHCI[%p]::UIMRootHubStatusChange port %d had an overcurrent", this, port);
overCurrentReported = true;
}
statusChangedBitmap |= statusBit; }
}
}
if ( (abort || (statusChangedBitmap != 0)) && (_outstandingTrans[0].completion.action != NULL) )
{
last = _outstandingTrans[0];
IOTakeLock(_intLock);
for (index = 1; index < kMaxOutstandingTrans ; index++)
{
_outstandingTrans[index-1] = _outstandingTrans[index];
if (_outstandingTrans[index].completion.action == NULL)
break;
}
clock_get_uptime( &_lastRootHubStatusChanged );
move = last.bufLen;
if (move > sizeof(statusChangedBitmap))
move = sizeof(statusChangedBitmap);
if (numPorts < 8)
move = 1;
statusChangedBitmap = HostToUSBWord(statusChangedBitmap);
last.buf->writeBytes(0,&statusChangedBitmap, move);
IOUnlock(_intLock);
Complete(last.completion, (abort ? kIOReturnAborted : kIOReturnSuccess), last.bufLen - move);
}
if ( !abort && _ehciAvailable && ((_outstandingTrans[0].completion.action != NULL) || (statusChangedBitmap == 0)) )
{
_pEHCIRegisters->USBIntr = _pEHCIRegisters->USBIntr | HostToUSBLong(kEHCIPortChangeIntBit);
IOSync();
}
}
void
AppleUSBEHCI::SimulateRootHubInt(UInt8 endpoint,
IOMemoryDescriptor * buf,
UInt32 bufLen,
IOUSBCompletion completion)
{
int index;
AbsoluteTime lastRootHubChangeTime, currentTime;
UInt64 elapsedTime = 0;
if (endpoint != 1)
{
Complete(completion, -1, bufLen);
return;
}
IOTakeLock(_intLock);
for (index = 0; index < kMaxOutstandingTrans; index++)
{
if (_outstandingTrans[index].completion.action == NULL)
{
_outstandingTrans[index].buf = buf;
_outstandingTrans[index].bufLen = bufLen;
_outstandingTrans[index].completion = completion;
IOUnlock(_intLock);
lastRootHubChangeTime = LastRootHubPortStatusChanged( false );
clock_get_uptime( ¤tTime );
SUB_ABSOLUTETIME(¤tTime, &lastRootHubChangeTime );
absolutetime_to_nanoseconds(currentTime, &elapsedTime);
elapsedTime /= 1000000;
if ( elapsedTime < kEHCIRootHubPollingInterval )
{
USBLog(5, "AppleUSBEHCI[%p]::SimulateRootHubInt Last change was %qd ms ago. IOSleep'ing for %qd ms, inGate: %d", this, elapsedTime, kEHCIRootHubPollingInterval - elapsedTime, _workLoop->inGate() );
IOSleep( kEHCIRootHubPollingInterval - elapsedTime );
}
UIMRootHubStatusChange(false);
return;
}
}
IOUnlock(_intLock); Complete(completion, -1, bufLen); }
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(-1);
}
USBLog(5, "AppleUSBEHCI[%p]::SimulateEDAbort Root hub aborting int transactions", this);
for( i=0; i < kMaxOutstandingTrans; i++)
{
UIMRootHubStatusChange(true);
}
}
else
{
USBLog(5, "AppleUSBEHCI[%p]::SimulateEDAbort Root hub aborting control pipe", this);
UIMRootHubStatusChange(false);
}
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, 0x43, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x75, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2c, 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 )
{
if ( resetTime )
clock_get_uptime(&_lastRootHubStatusChanged);
return _lastRootHubStatusChanged;
}
void
AppleUSBEHCI::GetNumberOfPorts( UInt8 * numPorts )
{
UInt32 descriptorA;
if ( _ehciBusState == kEHCIBusStateOff )
{
*numPorts = 0;
}
else
{
*numPorts = USBToHostLong(_pEHCICapRegisters->HCSParams) & kEHCINumPortsMask;
}
}
bool
AppleUSBEHCI::RootHubAreAllPortsDisconnectedOrSuspended( )
{
UInt8 numPorts = 0;
UInt32 portStat;
int i;
bool result = true;
GetNumberOfPorts( &numPorts );
if ( numPorts == 0 )
return false;
for (i=0; i < numPorts; i++)
{
portStat = USBToHostLong(_pEHCIRegisters->PortSC[i]);
if ((portStat & kEHCIPortSC_Enabled) && !(portStat & kEHCIPortSC_Suspend))
{
USBLog(6, "AppleUSBEHCI[%p]::RootHubAreAllPortsDisconnectedOrSuspended - port %d enabled and not suspended", this, i+1);
result = false;
}
if ((portStat & kEHCIPortSC_Connect) && !(portStat & kEHCIPortSC_Suspend))
{
USBLog(6, "AppleUSBEHCI[%p]::RootHubAreAllPortsDisconnectedOrSuspended - port %d CONNECTED, NOT ENABLED and not suspended - we used to allow this but no more", this, i+1);
USBLog(7, "AppleUSBEHCI::RHAPDOS: USBCMD(%p) USBSTS(%p) USBIntr(%p) PortSC(%p %p %p %p %p)",
(void*)USBToHostLong(_pEHCIRegisters->USBCMD),
(void*)USBToHostLong(_pEHCIRegisters->USBSTS),
(void*)USBToHostLong(_pEHCIRegisters->USBIntr),
(void*)USBToHostLong(_pEHCIRegisters->PortSC[0]),
(void*)USBToHostLong(_pEHCIRegisters->PortSC[1]),
(void*)USBToHostLong(_pEHCIRegisters->PortSC[2]),
(void*)USBToHostLong(_pEHCIRegisters->PortSC[3]),
(void*)USBToHostLong(_pEHCIRegisters->PortSC[4]));
return false;
}
}
if ( result )
{
if ( _controlBulkTransactionsOut != 0 )
{
USBLog(2, "AppleUSBEHCI[%p]::RHAreAllPortsDisconnectedOrSuspended everything disconnected, but %ld control/bulk transactions are pending. ", this, _controlBulkTransactionsOut);
result = false;
}
}
USBLog(6, "AppleUSBEHCI[%p]::RootHubAreAllPortsDisconnectedOrSuspended - YES THEY ARE", this);
return result;
}
void
AppleUSBEHCI::PortDetectInterruptThreadEntry(OSObject *target)
{
AppleUSBEHCI * me = OSDynamicCast(AppleUSBEHCI, target);
if (!me )
return;
me->retain();
me->PortDetectInterruptThread();
me->release();
}
void
AppleUSBEHCI::PortDetectInterruptThread()
{
UInt8 numPorts = 0;
UInt32 hcsParams;
UInt32 portSC = 0;
unsigned int port;
hcsParams = USBToHostLong(_pEHCICapRegisters->HCSParams);
numPorts = hcsParams & kEHCINumPortsMask;
USBLog(5,"AppleUSBEHCI[%p]::PortDetectInterruptThread numPorts %d", this, numPorts);
for (port = 1; port <= numPorts; port++)
{
portSC = USBToHostLong (_pEHCIRegisters->PortSC[port-1]);
if (portSC & kEHCIPortSC_Resume)
{
USBLog(3, "AppleUSBUHCI[%p]::PortDetectInterruptThread - resume detected on port %d, resuming", this, port);
if ( _idleSuspend )
{
USBLog(3, "AppleUSBUHCI[%p]::PortDetectInterruptThread - restarting bus before resuming port", this);
setPowerState(kEHCISetPowerLevelRunning, this);
}
EHCIRootHubPortSuspend(port, false);
}
}
if ( _idleSuspend )
{
USBLog(2, "AppleUSBEHCI[%p]::PortDetectInterruptThread - port change detect interrupt while in idlesuspend - restarting bus", this);
setPowerState(kEHCISetPowerLevelRunning, this);
}
UIMRootHubStatusChange();
}
void
AppleUSBEHCI::RootHubCreationEntry(OSObject *target)
{
AppleUSBEHCI * me = OSDynamicCast(AppleUSBEHCI, target);
if (!me )
return;
me->retain();
me->RootHubCreation();
me->release();
}
void
AppleUSBEHCI::RootHubCreation()
{
IOReturn err;
USBLog(1,"AppleUSBEHCI[%p]::RootHubCreation - Need to recreate root hub on bus %ld", this, _busNumber);
USBLog(2,"AppleUSBEHCI[%p]::RootHubCreation - powering up hardware", this);
UIMInitializeForPowerUp();
_ehciBusState = kEHCIBusStateRunning;
_ehciAvailable = true; _wakingFromHibernation = false;
if ( _rootHubDevice == NULL )
{
err = CreateRootHubDevice( _device, &_rootHubDevice );
USBLog(1,"AppleUSBEHCI[%p]::RootHubCreation - done creating root hub", this);
if ( err != kIOReturnSuccess )
{
USBError(1,"AppleUSBEHCI[%p]::RootHubCreation - Could not create root hub device upon wakeup (%x)!", this, err);
}
else
{
_rootHubDevice->registerService(kIOServiceRequired | kIOServiceSynchronous);
LastRootHubPortStatusChanged(true);
}
}
_companionWakeHoldoff = false;
return;
}