AppleUSBUHCI_RootHub.cpp [plain text]
#include <IOKit/usb/IOUSBLog.h>
#include "AppleUSBUHCI.h"
#ifndef APPLEUHCIROOTHUB_USE_KPRINTF
#define APPLEUHCIROOTHUB_USE_KPRINTF 0
#endif
#if APPLEUHCIROOTHUB_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,
kPrdRootHubApple = 0x8005,
kUHCIRootHubPollingInterval = 32 };
#pragma mark Public root hub methods
IOReturn
AppleUSBUHCI::GetRootHubDeviceDescriptor(IOUSBDeviceDescriptor *desc)
{
IOUSBDeviceDescriptor newDesc = {
sizeof(IOUSBDeviceDescriptor), kUSBDeviceDesc, USB_CONSTANT16(kUSBRel10), kUSBHubClass, kUSBHubSubClass, 0, 8, USB_CONSTANT16(kAppleVendorID), USB_CONSTANT16(kPrdRootHubApple), USB_CONSTANT16(0x0110), 2, 1, 0, 1 };
if (!desc)
return(kIOReturnNoMemory);
bcopy(&newDesc, desc, newDesc.bLength);
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::GetRootHubDescriptor(IOUSBHubDescriptor *desc)
{
IOUSBHubDescriptor hubDesc;
if (!desc)
return(kIOReturnNoMemory);
hubDesc.length = sizeof(IOUSBHubDescriptor);
hubDesc.hubType = kUSBHubDescriptorType;
hubDesc.numPorts = kUHCI_NUM_PORTS;
hubDesc.powerOnToGood = 50;
hubDesc.hubCurrent = 0;
hubDesc.characteristics = HostToUSBWord(kNoPowerSwitchingBit | kNoOverCurrentBit);
assert(kUHCI_NUM_PORTS < 8);
hubDesc.removablePortFlags[0] = 0;
hubDesc.removablePortFlags[1] = 0xFF;
hubDesc.length -= (sizeof(hubDesc.removablePortFlags) - 1 +
sizeof(hubDesc.pwrCtlPortFlags) - 1);
bcopy(&hubDesc, desc, hubDesc.length);
return(kIOReturnSuccess);
}
IOReturn
AppleUSBUHCI::SetRootHubDescriptor(OSData *buffer)
{
USBLog(3,"%s[%p]: unimplemented set root hub descriptor", getName(), this);
return(kIOReturnSuccess);
}
IOReturn
AppleUSBUHCI::GetRootHubConfDescriptor(OSData *desc)
{
IOUSBConfigurationDescriptor confDesc =
{
sizeof(IOUSBConfigurationDescriptor), kUSBConfDesc, USB_CONSTANT16(sizeof(IOUSBConfigurationDescriptor) +
sizeof(IOUSBInterfaceDescriptor) +
sizeof(IOUSBEndpointDescriptor)), 1, 1, 0, 0x60, 0, };
IOUSBInterfaceDescriptor intfDesc =
{
sizeof(IOUSBInterfaceDescriptor), kUSBInterfaceDesc, 0, 0, 1, kUSBHubClass, kUSBHubSubClass, 0, 0 };
IOUSBEndpointDescriptor endptDesc =
{
sizeof(IOUSBEndpointDescriptor), kUSBEndpointDesc, 0x81, kUSBInterrupt, HostToUSBWord(8), kUHCIRootHubPollingInterval, };
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
AppleUSBUHCI::SetRootHubFeature(UInt16 wValue)
{
switch(wValue)
{
case kUSBHubLocalPowerChangeFeature :
USBLog(3,"%s[%p]: unimplemented Set Power Change Feature", getName(), this);
break;
case kUSBHubOverCurrentChangeFeature :
USBLog(3,"%s[%p]: unimplemented Set Overcurrent Change Feature", getName(), this);
break;
default:
USBLog(3,"%s[%p]: Unknown set hub feature (%d) in root hub", getName(), this, wValue);
break;
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::ClearRootHubFeature(UInt16 wValue)
{
switch(wValue)
{
case kUSBHubLocalPowerChangeFeature :
USBLog(3,"%s[%p]: unimplemented Clear Power Change Feature", getName(), this);
break;
case kUSBHubOverCurrentChangeFeature :
USBLog(3,"%s[%p]: unimplemented Clear Overcurrent Change Feature", getName(), this);
break;
default:
USBLog(3,"%s[%p]: Unknown hub clear (%d) in root hub", getName(), this, wValue);
break;
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::GetRootHubStatus(IOUSBHubStatus *status)
{
USBLog(5, "%s[%p]::GetRootHubStatus", getName(), this);
status->statusFlags = 0;
status->changeFlags = 0;
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::GetRootHubPortStatus(IOUSBHubPortStatus *status, UInt16 port)
{
UInt16 p_status;
UInt16 r_status, r_change;
USBLog(5, "AppleUSBUHCI[%p]::GetRootHubPortStatus %d", this, port);
RHDumpPortStatus(port);
if (_uhciBusState == kUHCIBusStateSuspended)
{
return kIOReturnNotResponding;
}
port--; if (port >= kUHCI_NUM_PORTS)
{
return kIOReturnBadArgument;
}
p_status = ReadPortStatus(port);
if ((p_status & kUHCI_PORTSC_SUSPEND) && !(p_status & kUHCI_PORTSC_CCS))
{
USBLog(7, "AppleUSBUHCI[%p]::GetRootHubPortStatus - clearing suspend on disconnected port status[%p]", this, (void*)p_status);
p_status &= kUHCI_PORTSC_MASK; p_status &= ~kUHCI_PORTSC_SUSPEND; WritePortStatus(port, p_status); p_status = ReadPortStatus(port); USBLog(7, "AppleUSBUHCI[%p]::GetRootHubPortStatus - new port status[%p]", this, (void*)p_status);
}
r_status = kHubPortPower;
if (p_status & kUHCI_PORTSC_SUSPEND)
r_status |= kHubPortSuspend;
if (p_status & kUHCI_PORTSC_RESET)
r_status |= kHubPortBeingReset;
if (p_status & kUHCI_PORTSC_LS)
r_status |= kHubPortLowSpeed;
if (p_status & kUHCI_PORTSC_PED)
r_status |= kHubPortEnabled;
if (p_status & kUHCI_PORTSC_CCS)
r_status |= kHubPortConnection;
status->statusFlags = HostToUSBWord(r_status);
r_change = r_status ^ _lastPortStatus[port];
if (p_status & kUHCI_PORTSC_PEDC)
{
r_change |= kHubPortEnabled;
} else
{
r_change &= ~kHubPortEnabled;
}
if (p_status & kUHCI_PORTSC_CSC)
{
r_change |= kHubPortConnection;
} else
{
r_change &= ~kHubPortConnection;
}
if ((_lastPortStatus[port] & kHubPortSuspend) &&
!(r_status & kHubPortSuspend))
{
USBLog(5, "%s[%p]: Turning on suspend change bit", getName(), this);
_portSuspendChange[port] = true;
}
if (_portSuspendChange[port])
{
r_change |= kHubPortSuspend;
} else {
r_change &= ~kHubPortSuspend;
}
if (_portWasReset[port])
{
r_change |= kHubPortBeingReset;
}
status->changeFlags = HostToUSBWord(r_change);
USBLog(5, "%s[%p]: returned status is (%x,%x)", getName(), this, r_status, r_change);
RHDumpHubPortStatus(status);
_lastPortStatus[port] = r_status;
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::SetRootHubPortFeature(UInt16 wValue, UInt16 port)
{
IOReturn result = kIOReturnSuccess;
USBLog(5, "%s[%p]::SetRootHubPortFeature %d %d", getName(), this, wValue, port);
if ( _idleSuspend )
{
USBLog(3, "AppleUSBUHCI[%p]::SetRootHubPortFeature - in _idleSuspend - restarting bus", this);
setPowerState(kUHCIPowerLevelRunning, this);
}
switch(wValue)
{
case kUSBHubPortSuspendFeature :
result = RHSuspendPort(port, true);
break;
case kUSBHubPortResetFeature :
result = RHResetPort(port);
break;
case kUSBHubPortEnableFeature :
RHEnablePort(port, true);
result = kIOReturnSuccess;
break;
case kUSBHubPortPowerFeature :
result = kIOReturnSuccess;
break;
default:
USBLog(5, "%s[%p]: unknown feature %d", getName(), this, wValue);
break;
}
return result;
}
IOReturn
AppleUSBUHCI::ClearRootHubPortFeature(UInt16 wValue, UInt16 port)
{
UInt16 value;
USBLog(5, "%s[%p]::ClearRootHubPortFeature %d %d", getName(), this, wValue, port);
if ( _idleSuspend )
{
USBLog(3, "AppleUSBUHCI[%p]::ClearRootHubPortFeature - in _idleSuspend - restarting bus", this);
setPowerState(kUHCIPowerLevelRunning, this);
}
switch(wValue)
{
case kUSBHubPortEnableFeature :
USBLog(5, "%s[%p]: Clear port enable", getName(), this);
RHEnablePort(port, false);
break;
case kUSBHubPortConnectionChangeFeature :
USBLog(5, "%s[%p]: Clear connection change", getName(), this);
value = ReadPortStatus(port-1) & kUHCI_PORTSC_MASK;
WritePortStatus(port-1, value | kUHCI_PORTSC_CSC);
break;
case kUSBHubPortEnableChangeFeature :
USBLog(5, "%s[%p]: Clear port enable change", getName(), this);
value = ReadPortStatus(port-1) & kUHCI_PORTSC_MASK;
WritePortStatus(port-1, value | kUHCI_PORTSC_PEDC);
break;
case kUSBHubPortResetChangeFeature :
USBLog(5, "%s[%p]: Clear port reset change", getName(), this);
_portWasReset[port-1] = false;
break;
case kUSBHubPortSuspendFeature :
RHSuspendPort(port, false);
break;
case kUSBHubPortSuspendChangeFeature :
USBLog(5, "%s[%p]: Clear port suspend change", getName(), this);
_portSuspendChange[port-1] = false;
break;
#if 0
case kUSBHubPortOverCurrentChangeFeature :
RHResetOverCurrentChange(port);
break;
case kUSBHubPortPowerFeature :
break;
#endif
default:
USBLog(5,"%s[%p]: clear unknown feature %d", getName(), this, wValue);
break;
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::GetRootHubPortState(UInt8 *state, UInt16 port)
{
USBLog(5,"%s[%p]::GetRootHubPortState", getName(), this);
return(kIOReturnSuccess);
}
IOReturn
AppleUSBUHCI::SetHubAddress(UInt16 functionNumber)
{
USBLog(5, "%s[%p]::SetHubAddress %d", getName(), this, functionNumber);
_rootFunctionNumber = functionNumber;
return kIOReturnSuccess;
}
void
AppleUSBUHCI::UIMRootHubStatusChange(void)
{
UIMRootHubStatusChange(false);
}
void
AppleUSBUHCI::UIMRootHubStatusChange(bool abort)
{
UInt8 bitmap, bit;
unsigned int i, index, move;
IOUSBHubPortStatus portStatus;
struct InterruptTransaction last;
USBLog(5, "%s[%p]::UIMRootHubStatusChange (Abort: %d, _uhciAvailable: %d)", getName(), this, abort, _uhciAvailable);
if (_uhciAvailable)
{
_rhTimer->cancelTimeout();
}
if (_outstandingTrans[0].completion.action == NULL)
{
USBLog(3, "AppleUSBUHCI[%p]::UIMRootHubStatusChange - no one listening to our changes", this);
return; }
if (!abort && _uhciAvailable)
{
if (_uhciBusState == kUHCIBusStateSuspended)
{
USBLog(3, "AppleUSBUHCI[%p]::UIMRootHubStatusChange - turning back on", this);
setPowerState(kUHCIPowerLevelRunning, this);
}
assert(kUHCI_NUM_PORTS < 8);
bitmap = 0;
bit = 0x2;
for (i=1; i <= kUHCI_NUM_PORTS; i++)
{
GetRootHubPortStatus(&portStatus, i);
USBLog(5, "AppleUSBUHCI[%p]::UIMRootHubStatusChange Port %d hub flags:", this, i);
RHDumpHubPortStatus(&portStatus);
if (portStatus.changeFlags != 0)
{
AbsoluteTime currentTime;
UInt64 elapsedTime;
bitmap |= bit;
clock_get_uptime(¤tTime);
SUB_ABSOLUTETIME(¤tTime, &_portRecoveryTime[i-1] );
absolutetime_to_nanoseconds(currentTime, &elapsedTime);
elapsedTime /= 1000000000;
if ( _previousPortRecoveryAttempted[i-1] && (elapsedTime >= kUHCITimeoutForPortRecovery) )
{
USBLog(2, "AppleUSBUHCI[%p]::UIMRootHubStatusChange Forgetting about our portRecovery state since the last change occurred %qd seconds ago", this, elapsedTime);
_previousPortRecoveryAttempted[i-1] = false;
}
if ( !_previousPortRecoveryAttempted[i-1] )
{
USBLog(7, "AppleUSBUHCI[%p]::UIMRootHubStatusChange Port %d had a change: 0x%x", this, i, portStatus.changeFlags);
if ( (portStatus.changeFlags & kHubPortEnabled) and (portStatus.statusFlags & kHubPortConnection) and (portStatus.statusFlags & kHubPortPower) and !(portStatus.statusFlags & kHubPortEnabled) )
{
_previousPortRecoveryAttempted[i-1] = true;
clock_get_uptime(&_portRecoveryTime[i-1]);
USBLog(1, "AppleUSBUHCI[%p]::UIMRootHubStatusChange Port %d attempting to enable a disabled port to work around a fickle UHCI controller", this, i);
RHEnablePort(i, true);
bitmap &= ~bit;
}
}
else
{
if ( (portStatus.changeFlags & kHubPortEnabled) and (portStatus.statusFlags & kHubPortConnection) and (portStatus.statusFlags & kHubPortPower) and (portStatus.statusFlags & kHubPortEnabled) )
{
USBLog(2, "AppleUSBUHCI[%p]::UIMRootHubStatusChange Port %d had a change but it's just the port enabled notification", this, i);
}
else
{
USBLog(2, "AppleUSBUHCI[%p]::UIMRootHubStatusChange Port %d had a change but last time we attempted a recovery, so not attempting again", this, i);
_previousPortRecoveryAttempted[i-1] = false;
}
}
}
bit <<= 1;
}
USBLog(5, "AppleUSBUHCI[%p]::UIMRootHubStatusChange RH status bitmap = %x", this, bitmap);
}
else
{
USBLog(7, "AppleUSBUHCI[%p]::UIMRootHubStatusChange - no work to (abort: %d, available: %d)", this, abort, _uhciAvailable);
}
if ( (abort || (bitmap != 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(&_rhChangeTime);
move = last.bufLen;
if (move > sizeof(bitmap))
move = sizeof(bitmap);
last.buf->writeBytes(0, &bitmap, 1);
IOUnlock(_intLock);
Complete(last.completion, (abort ? kIOReturnAborted : kIOReturnSuccess), last.bufLen - move);
}
if ( !abort && _uhciAvailable && ((_outstandingTrans[0].completion.action != NULL) || (bitmap == 0)) )
{
USBLog(2, "AppleUSBUHCI[%p]::UIMRootHubStatusChange - starting rhTimer", this);
_rhTimer->setTimeoutMS(_rootHubPollingRate);
}
}
IOReturn
AppleUSBUHCI::GetRootHubStringDescriptor(UInt8 index, OSData *desc)
{
UInt8 productName[] = {
0, kUSBStringDesc, 0x55, 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;
}
#pragma mark Internal root hub methods
AbsoluteTime
AppleUSBUHCI::RHLastPortStatusChanged()
{
return _rhChangeTime;
}
bool
AppleUSBUHCI::RHAreAllPortsDisconnectedOrSuspended( void )
{
int i;
UInt16 status;
bool result = true;
for (i=0; i<kUHCI_NUM_PORTS; i++)
{
status = ReadPortStatus(i);
if ((status & kUHCI_PORTSC_CCS) && !(status & kUHCI_PORTSC_SUSPEND))
{
result = false;
break;
}
}
if ( result )
{
if ( _controlBulkTransactionsOut != 0 )
{
USBLog(2, "AppleUSBUHCI[%p]::RHAreAllPortsDisconnectedOrSuspended everything disconnected, but %ld control/bulk transactions are pending. ", this, _controlBulkTransactionsOut);
result = false;
}
}
USBLog(result ? 2 : 6, "AppleUSBUHCI[%p]::RHAreAllPortsDisconnectedOrSuspended returns %d", this, result);
return result;
}
IOReturn
AppleUSBUHCI::RHDeleteEndpoint (short endpointNumber, short direction)
{
return RHAbortEndpoint(endpointNumber, direction);
}
IOReturn
AppleUSBUHCI::RHAbortEndpoint (short endpointNumber, short direction)
{
if (endpointNumber == 1)
{
if(direction != kUSBIn)
{
USBLog(5, "%s[%p]::RHAbortEndpoint - Root hub wrong direction Int pipe %d", getName(), this, direction);
return kIOReturnBadArgument;
}
USBLog(3, "AppleUSBUHCI[%p]::RHAbortEndpoint - cancelling rhTimer", this);
_rootHubPollingRate = 0;
_rhTimer->cancelTimeout();
USBLog(3, "AppleUSBUHCI[%p]::RHAbortEndpoint - Interrupt pipe - noting status change", this);
UIMRootHubStatusChange(true);
} else
{
USBLog(3, "AppleUSBUHCI[%p]::RHAbortEndpoint - Control pipe - noting status change", this);
UIMRootHubStatusChange(false);
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::RHCreateInterruptEndpoint(short endpointNumber,
UInt8 direction,
short speed,
UInt16 maxPacketSize,
short pollingRate)
{
USBLog(3, "%s[%p]::RHCreateInterruptEndpoint rate %d", getName(), this, pollingRate);
_rootHubPollingRate = pollingRate;
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::RHCreateInterruptTransfer(IOUSBCommand* command)
{
int index;
AbsoluteTime lastRootHubChangeTime, currentTime;
UInt64 elapsedTime = 0;
USBLog(5, "AppleUSBUHCI[%p]::RHCreateInterruptTransfer", this);
if (_rootHubPollingRate == 0)
{
USBLog(2, "AppleUSBUHCI[%p]::RHCreateInterruptTransfer, pollingRate is 0!", this);
return kIOReturnBadArgument;
}
IOTakeLock(_intLock);
for (index = 0; index < kMaxOutstandingTrans; index++)
{
if (_outstandingTrans[index].completion.action == NULL)
{
_outstandingTrans[index].buf = command->GetBuffer();
_outstandingTrans[index].bufLen = command->GetReqCount();
_outstandingTrans[index].completion = command->GetUSLCompletion();
IOUnlock(_intLock);
lastRootHubChangeTime = RHLastPortStatusChanged();
clock_get_uptime( ¤tTime );
SUB_ABSOLUTETIME(¤tTime, &lastRootHubChangeTime );
absolutetime_to_nanoseconds(currentTime, &elapsedTime);
elapsedTime /= 1000000;
if ( elapsedTime < kUHCIRootHubPollingInterval )
{
USBLog(5, "AppleUSBUHCI[%p]::SimulateRootHubInt Last change was %qd ms ago. IOSleep'ing for %qd ms, _workLoop->inGate = %d", this, elapsedTime, kUHCIRootHubPollingInterval - elapsedTime, _workLoop->inGate() );
IOSleep( kUHCIRootHubPollingInterval - elapsedTime );
}
UIMRootHubStatusChange(false);
return kIOReturnSuccess;
}
}
IOUnlock(_intLock); Complete(command->GetUSLCompletion(), -1, command->GetReqCount());
return kIOReturnSuccess;
}
void
AppleUSBUHCI::RHCheckStatus()
{
int i;
UInt16 status;
bool change = false;
for (i=0; i<kUHCI_NUM_PORTS; i++)
{
status = ReadPortStatus(i);
if (status & kUHCI_PORTSC_RD)
{
USBLog(3, "AppleUSBUHCI[%p]::RHCheckStatus - resume detected on port %d, resuming", this, i+1);
if ( _idleSuspend )
{
USBLog(3, "AppleUSBUHCI[%p]::RHCheckStatus - restarting bus before resuming port", this);
setPowerState(kUHCIPowerLevelRunning, this);
}
RHSuspendPort(i+1, false);
change = true;
} else if (status & (kUHCI_PORTSC_PEDC|kUHCI_PORTSC_CSC|kUHCI_PORTSC_RD))
{
USBLog(3,"AppleUSBUHCI[%p]::RHCheckStatus Status changed on port %d", this, i+1);
change = true;
} else if (_portWasReset[i])
{
change = true;
}
}
if (change)
{
USBLog(3, "AppleUSBUHCI[%p]::RHCheckStatus - Status change for root hub", this);
if ( _idleSuspend )
{
USBLog(3, "AppleUSBUHCI[%p]::RHCheckStatus - port change detect interrupt while in idlesuspend - restarting bus", this);
setPowerState(kUHCIPowerLevelRunning, this);
}
UIMRootHubStatusChange(false);
}
}
void
AppleUSBUHCI::RHTimerFired(OSObject *owner, IOTimerEventSource *sender)
{
AppleUSBUHCI *myself;
UInt32 period;
myself = OSDynamicCast(AppleUSBUHCI, owner);
if (!myself || myself->isInactive() || !myself->_uhciAvailable)
return;
myself->RHCheckStatus();
if (myself->_rootHubPollingRate)
{
myself->_rhTimer->setTimeoutMS(myself->_rootHubPollingRate);
}
}
void
AppleUSBUHCI::RHEnablePort(int port, bool enable)
{
UInt16 value;
port--; value = ReadPortStatus(port) & kUHCI_PORTSC_MASK;
USBLog(3, "%s[%p]::RHEnablePort port: %d enable: %d PortSC: 0x%x", getName(), this, port+1, enable, value);
USBLog(2, "AppleUSBUHCI[%p]::RHEnablePort (CMD:%p STS:%p INTR:%p PORTSC1:%p PORTSC2:%p FRBASEADDR:%p FRNUM:%p, SOFMOD:%p, ConfigCMD:%p)", this, (void*)ioRead16(kUHCI_CMD), (void*)ioRead16(kUHCI_STS), (void*)ioRead16(kUHCI_INTR), (void*)ioRead16(kUHCI_PORTSC1), (void*)ioRead16(kUHCI_PORTSC2), (void*)ioRead32(kUHCI_FRBASEADDR),
(void*)ioRead32(kUHCI_FRNUM), (void*)ioRead32(kUHCI_SOFMOD), (void*)_device->configRead16(kIOPCIConfigCommand));
if (enable)
{
value |= kUHCI_PORTSC_PED;
} else
{
value &= ~kUHCI_PORTSC_PED;
}
WritePortStatus(port, value);
}
IOReturn
AppleUSBUHCI::RHSuspendPort(int port, bool suspended)
{
UInt16 cmd, value;
USBLog(3, "AppleUSBUHCI[%p]::RHSuspendPort %d (%s)", this, port, suspended ? "SUSPEND" : "RESUME");
port--;
cmd = ioRead16(kUHCI_CMD);
value = ReadPortStatus(port) & kUHCI_PORTSC_MASK;
if (suspended)
{
value |= kUHCI_PORTSC_SUSPEND;
value &= ~kUHCI_PORTSC_RD;
} else
{
if (cmd & kUHCI_CMD_EGSM)
{
USBError(1, "AppleUSBUHCI[%p]: attempt to resume during global suspend", this);
return kIOReturnError;
}
value |= (kUHCI_PORTSC_SUSPEND | kUHCI_PORTSC_RD);
}
value |= kUHCI_PORTSC_PED;
USBLog(7, "AppleUSBUHCI[%p]: writing 0x%x to port control", this, value);
WritePortStatus(port, value);
if (suspended)
{
IOSleep(3);
} else
{
int i;
IOSleep(20);
value = ReadPortStatus(port) & kUHCI_PORTSC_MASK;
value &= ~(kUHCI_PORTSC_RD | kUHCI_PORTSC_SUSPEND);
USBLog(7, "AppleUSBUHCI[%p]: de-asserting resume signal 0x%x", this, value);
WritePortStatus(port, value);
for (i=0; i<10; i++)
{
IOSleep(1);
if ((ReadPortStatus(port) & kUHCI_PORTSC_RD) == 0)
{
break;
}
}
USBLog(7, "AppleUSBUHCI[%p]: EOP finished", this);
}
USBLog(5, "AppleUSBUHCI[%p]::RHSuspendPort %d (%s) calling UIMRootHubStatusChange", this, port+1, suspended ? "SUSPEND" : "RESUME");
UIMRootHubStatusChange();
USBLog(5, "AppleUSBUHCI[%p]::RHSuspendPort %d (%s) DONE", this, port+1, suspended ? "SUSPEND" : "RESUME");
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::RHResetPort(int port)
{
UInt16 value;
int i;
USBLog(3, "%s[%p]::RHResetPort %d", getName(), this, port);
port--;
value = ReadPortStatus(port) & kUHCI_PORTSC_MASK;
WritePortStatus(port, value | kUHCI_PORTSC_RESET);
IOSleep(50);
value = ReadPortStatus(port) & kUHCI_PORTSC_MASK;
WritePortStatus(port, value & ~kUHCI_PORTSC_RESET);
IODelay(10);
value = ReadPortStatus(port) & kUHCI_PORTSC_MASK;
WritePortStatus(port, value | kUHCI_PORTSC_PED);
for (i=10; i>0; i--)
{
IOSleep(10);
value = ReadPortStatus(port);
if ((value & kUHCI_PORTSC_CCS) == 0)
{
return kIOReturnNotResponding;
break;
}
if (value & (kUHCI_PORTSC_PEDC | kUHCI_PORTSC_CSC))
{
WritePortStatus(port, (value & kUHCI_PORTSC_MASK) | (kUHCI_PORTSC_PEDC | kUHCI_PORTSC_CSC));
continue;
}
if (value & kUHCI_PORTSC_PED)
{
break;
}
}
if (i == 0)
{
USBLog(5, "%s[%p]: reset port FAILED", getName(), this);
return kIOReturnNotResponding;
}
_portWasReset[port] = true;
USBLog(5, "%s[%p]: reset port succeeded", getName(), this);
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::RHHoldPortReset(int port)
{
UInt16 value;
int i;
USBLog(1, "%s[%p]::RHHoldPortReset %d", getName(), this, port);
port--;
value = ReadPortStatus(port) & kUHCI_PORTSC_MASK;
WritePortStatus(port, value | kUHCI_PORTSC_RESET);
return kIOReturnSuccess;
}
void
AppleUSBUHCI::RHDumpPortStatus(int port)
{
UInt16 value;
char buf[64];
static struct {
UInt16 mask;
char *string;
} strings[] = {
{kUHCI_PORTSC_SUSPEND, "SUSPEND "},
{kUHCI_PORTSC_RESET, "RESET "},
{kUHCI_PORTSC_LS, "LS "},
{kUHCI_PORTSC_RD, "RD "},
{kUHCI_PORTSC_LINE0, "LINE0 "},
{kUHCI_PORTSC_LINE1, "LINE1 "},
{kUHCI_PORTSC_PEDC, "PEDC "},
{kUHCI_PORTSC_PED, "PED "},
{kUHCI_PORTSC_CSC, "CSC "},
{kUHCI_PORTSC_CCS, "CCS"},
{0,0}
};
int i;
port--; buf[0] = '\0';
value = ReadPortStatus(port);
for (i=0; strings[i].string != 0; i++)
{
if ((value & strings[i].mask) != 0)
{
strcat(buf, strings[i].string);
}
}
USBLog(5, "%s[%p]: Port %d: status %x %s", getName(), this, port+1, value, buf);
}
void
AppleUSBUHCI::RHDumpHubPortStatus(IOUSBHubPortStatus *status)
{
UInt16 value;
char buf[128];
static struct {
UInt16 mask;
char *string;
} strings[] = {
{kHubPortConnection, "kHubPortConnection "},
{kHubPortEnabled, "kHubPortEnabled "},
{kHubPortSuspend, "kHubPortSuspend "},
{kHubPortOverCurrent,"kHubPortOverCurrent "},
{kHubPortBeingReset, "kHubPortBeingReset "},
{kHubPortPower, "kHubPortPower "},
{kHubPortLowSpeed, "kHubPortLowSpeed "},
{kHubPortHighSpeed, "kHubPortHighSpeed "},
{kHubPortTestMode, "kHubPortTestMode "},
{kHubPortIndicator, "kHubPortIndicator "},
{0,0}
};
int i;
buf[0] = '\0';
value = USBToHostWord(status->statusFlags);
for (i=0; strings[i].string != 0; i++)
{
if ((value & strings[i].mask) != 0)
{
strcat(buf, strings[i].string);
}
}
USBLog(5, "%s[%p]: Hub port status: %s", getName(), this, buf);
buf[0] = '\0';
value = USBToHostWord(status->changeFlags);
for (i=0; strings[i].string != 0; i++)
{
if ((value & strings[i].mask) != 0)
{
strcat(buf, strings[i].string);
}
}
USBLog(5, "%s[%p]: Hub port change: %s", getName(), this, buf);
}