#include <libkern/OSByteOrder.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOMessage.h>
#include <UserNotification/KUNCUserNotifications.h>
#include <IOKit/usb/IOUSBLog.h>
#include <IOKit/usb/IOUSBInterface.h>
#include <IOKit/usb/IOUSBRootHubDevice.h>
#include <IOKit/usb/IOUSBPipe.h>
#include <IOKit/usb/IOUSBControllerV2.h>
#include "AppleUSBHub.h"
#include "AppleUSBHubPort.h"
#define super IOService
#define self this
static ErrataListEntry errataList[] = {
{0x046a, 0x003, 0x0301, 0x0305, kErrataCaptiveOKBit}, {0x046a, 0x003, 0x0601, 0x0605, kErrataCaptiveOKBit} };
enum
{
kEHCITestMode_Off = 0,
kEHCITestMode_J_State = 1,
kEHCITestMode_K_State = 2,
kEHCITestMode_SE0_NAK = 3,
kEHCITestMode_Packet = 4,
kEHCITestMode_ForceEnable = 5,
kEHCITestMode_Start = 10,
kEHCITestMode_End = 11
};
#define errataListLength (sizeof(errataList)/sizeof(ErrataListEntry))
OSDefineMetaClassAndStructors(AppleUSBHub, IOService)
#define WATCHDOGSECONDS 6
enum
{
kWatchdogTimerPeriod = 1000 * WATCHDOGSECONDS, kDevZeroTimeoutCount = 30 / WATCHDOGSECONDS, kHubDriverRetryCount = 3
};
bool
AppleUSBHub::init( OSDictionary * propTable )
{
if( !super::init(propTable))
return (false);
_numCaptive = 0;
_startupDelay = 0;
_timerSource = NULL;
_gate = NULL;
_portSuspended = false;
_hubHasBeenDisconnected = false;
_hubIsDead = false;
_workThread = NULL;
_resetPortZeroThread = NULL;
_hubDeadCheckThread = NULL;
_busPowerGood = false;
_powerForCaptive = 0;
_numCaptive = 0;
_outstandingIO = 0;
_needToClose = false;
_abortExpected = false;
_devZeroLockedTimeoutCounter = kDevZeroTimeoutCount;
_retryCount = kHubDriverRetryCount;
return(true);
}
bool
AppleUSBHub::start(IOService * provider)
{
IOReturn err = 0;
IOUSBRootHubDevice *rootHub;
OSDictionary *providerDict;
OSNumber * errataProperty;
const IORegistryPlane *usbPlane = NULL;
OSNumber *locationIDProperty;
_inStartMethod = true;
IncrementOutstandingIO();
if( !super::start(provider))
{
goto ErrorExit;
}
_timerSource = IOTimerEventSource::timerEventSource(this, (IOTimerEventSource::Action) TimeoutOccurred);
if ( _timerSource == NULL )
{
USBError(1, "AppleUSBHub::start Couldn't allocate timer event source");
goto ErrorExit;
}
_gate = IOCommandGate::commandGate(this);
if(!_gate)
{
USBError(1, "AppleUSBHub[%p]::start - unable to create command gate", this);
goto ErrorExit;
}
_workLoop = getWorkLoop();
if ( !_workLoop )
{
USBError(1, "AppleUSBHub::start Couldn't get provider's workloop");
goto ErrorExit;
}
_workLoop->retain();
if ( _workLoop->addEventSource( _timerSource ) != kIOReturnSuccess )
{
USBError(1, "AppleUSBHub::start Couldn't add timer event source");
goto ErrorExit;
}
if ( _workLoop->addEventSource( _gate ) != kIOReturnSuccess )
{
USBError(1, "AppleUSBHub::start Couldn't add gate event source");
goto ErrorExit;
}
_device = (IOUSBDevice *) provider;
_address = _device->GetAddress();
_bus = _device->GetBus();
providerDict = (OSDictionary*)getProperty("IOProviderMergeProperties");
if (providerDict)
provider->getPropertyTable()->merge(providerDict);
if (!_device->open(this))
{
USBError(1, "AppleUSBHub::start unable to open provider");
goto ErrorExit;
}
errataProperty = (OSNumber *)getProperty("kStartupDelay");
if ( errataProperty )
{
_startupDelay = errataProperty->unsigned32BitValue();
IOSleep( _startupDelay );
}
_errataBits = GetHubErrataBits();
err = ConfigureHub();
if ( err == kIOReturnSuccess )
{
if (_hsHub)
registerService();
rootHub = OSDynamicCast(IOUSBRootHubDevice, provider);
if (rootHub)
{
_isRootHub = true;
usbPlane = getPlane(kIOUSBPlane);
if (usbPlane)
{
rootHub->attachToParent(getRegistryRoot(), usbPlane);
}
}
_workThread = thread_call_allocate((thread_call_func_t)ProcessStatusChangedEntry, (thread_call_param_t)this);
_resetPortZeroThread = thread_call_allocate((thread_call_func_t)ResetPortZeroEntry, (thread_call_param_t)this);
_hubDeadCheckThread = thread_call_allocate((thread_call_func_t)CheckForDeadHubEntry, (thread_call_param_t)this);
_clearFeatureEndpointHaltThread = thread_call_allocate((thread_call_func_t)ClearFeatureEndpointHaltEntry, (thread_call_param_t)this);
if ( !_workThread || !_resetPortZeroThread || !_hubDeadCheckThread || !_clearFeatureEndpointHaltThread )
{
USBError(1, "AppleUSBHub[%p] could not allocate all thread functions. Aborting start", this);
goto ErrorExit;
}
locationIDProperty = (OSNumber *) provider->getProperty(kUSBDevicePropertyLocationID);
if ( locationIDProperty )
{
_locationID = locationIDProperty->unsigned32BitValue();
}
USBLog(1, "[%p] USB Generic Hub @ %d (0x%lx)", this, _address, _locationID);
_inStartMethod = false;
DecrementOutstandingIO();
err = RearmInterruptRead();
if (err == kIOReturnSuccess)
return true;
}
else
{
USBError(1,"AppleUSBHub[%p]::start Aborting startup: error 0x%x", this, err);
if ( _device && _device->isOpen(this) )
_device->close(this);
stop(provider);
}
ErrorExit:
if ( _timerSource )
{
if ( _workLoop )
_workLoop->removeEventSource(_timerSource);
_timerSource->release();
_timerSource = NULL;
}
if (_gate)
{
if (_workLoop)
_workLoop->removeEventSource(_gate);
_gate->release();
_gate = NULL;
}
if ( _workLoop )
{
_workLoop->release();
_workLoop = NULL;
}
_inStartMethod = false;
DecrementOutstandingIO();
return false;
}
void
AppleUSBHub::stop(IOService * provider)
{
IOUSBControllerV2 *v2Bus;
if (_buffer)
{
_buffer->release();
_buffer = NULL;
}
if (_hsHub)
{
v2Bus = OSDynamicCast(IOUSBControllerV2, _device->GetBus());
if (v2Bus)
v2Bus->RemoveHSHub(_address);
}
if(_hubInterface)
{
_hubInterface->close(this);
_hubInterface->release();
_hubInterface = NULL;
}
if (_timerSource)
{
if ( _workLoop )
_workLoop->removeEventSource(_timerSource);
_timerSource->release();
_timerSource = NULL;
}
if (_gate)
{
if (_workLoop)
_workLoop->removeEventSource(_gate);
_gate->release();
_gate = NULL;
}
if (_workThread)
{
thread_call_cancel(_workThread);
thread_call_free(_workThread);
}
if (_resetPortZeroThread)
{
thread_call_cancel(_resetPortZeroThread);
thread_call_free(_resetPortZeroThread);
}
if (_hubDeadCheckThread)
{
thread_call_cancel(_hubDeadCheckThread);
thread_call_free(_hubDeadCheckThread);
}
if (_clearFeatureEndpointHaltThread)
{
thread_call_cancel(_clearFeatureEndpointHaltThread);
thread_call_free(_clearFeatureEndpointHaltThread);
}
if (_device)
{
_device = 0;
}
super::stop(provider);
}
IOReturn
AppleUSBHub::ConfigureHub()
{
IOReturn err = kIOReturnSuccess;
IOUSBFindInterfaceRequest req;
const IOUSBConfigurationDescriptor *cd;
IOUSBControllerV2 *v2Bus;
_busPowerGood = false;
_powerForCaptive = 0;
_numCaptive = 0;
_retryCount = kHubDriverRetryCount;
if (_device->GetNumConfigurations() < 1)
{
USBError(1,"AppleUSBHub[%p]::ConfigureHub No hub configurations", this);
err = kIOReturnNoResources; goto ErrorExit;
}
cd = _device->GetFullConfigurationDescriptor(0);
if (!cd)
{
USBError(1,"AppleUSBHub[%p]::ConfigureHub No config descriptor", this);
err = kIOUSBConfigNotFound;
goto ErrorExit;
}
err = _device->SetConfiguration(this, cd->bConfigurationValue, false);
if (err)
{
USBError(1,"AppleUSBHub[%p]::ConfigureHub SetConfiguration failed. Error 0x%x", this, err);
goto ErrorExit;
}
if (cd->bmAttributes & kUSBAtrRemoteWakeup)
{
USBLog(3,"AppleUSBHub[%p]::ConfigureHub Setting kUSBFeatureDeviceRemoteWakeup for Hub device (%p)", this, _device);
err = _device->SetFeature(kUSBFeatureDeviceRemoteWakeup);
if ( err)
USBError(1,"AppleUSBHub[%p]::ConfigureHub SetFeature(kUSBFeatureDeviceRemoteWakeup) failed. Error 0x%x", this, err);
}
req.bInterfaceClass = kUSBHubClass;
req.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
req.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
req.bAlternateSetting = kIOUSBFindInterfaceDontCare;
if ((_hubInterface = _device->FindNextInterface(NULL, &req)) == 0)
{
USBError(1,"AppleUSBHub[%p]::ConfigureHub no interface found", this);
err = kIOUSBInterfaceNotFound;
goto ErrorExit;
}
_hubInterface->retain();
_busPowered = (cd->bmAttributes & kUSBAtrBusPowered) ? TRUE : FALSE; _selfPowered = (cd->bmAttributes & kUSBAtrSelfPowered) ? TRUE : FALSE;
if( !(_busPowered || _selfPowered) )
{
USBError(1,"AppleUSBHub[%p]::ConfigureHub illegal device config - no power", this);
err = kIOReturnNoPower; goto ErrorExit;
}
if ( (err = GetHubDescriptor(&_hubDescriptor)) )
{
USBError(1,"AppleUSBHub[%p]::ConfigureHub could not get hub descriptor (0x%x)", this, err);
goto ErrorExit;
}
if(_hubDescriptor.numPorts < 1)
{
USBLog(1,"AppleUSBHub[%p]::ConfigureHub there are no ports on this hub", this);
}
if(_hubDescriptor.numPorts > 7)
{
USBLog(3,"AppleUSBHub[%p]::ConfigureHub there are an awful lot of ports (%d) on this hub", this, _hubDescriptor.numPorts);
}
_readBytes = ((_hubDescriptor.numPorts + 1) / 8) + 1;
_buffer = IOBufferMemoryDescriptor::withCapacity(_readBytes, kIODirectionIn);
if (!_hubInterface->open(this))
{
USBError(1," AppleUSBHub[%p]::ConfigureHub could not open hub interface", this);
err = kIOReturnNotOpen;
goto ErrorExit;
}
_multiTTs = false;
_hsHub = false;
if (_device->GetbcdUSB() >= 0x200)
{
v2Bus = OSDynamicCast(IOUSBControllerV2, _device->GetBus());
if (v2Bus)
{
switch (_device->GetProtocol())
{
case 0:
USBLog(5, "AppleUSBHub[%p]::ConfigureHub - found FS/LS only hub", this);
break;
case 1:
USBLog(5, "AppleUSBHub[%p]::ConfigureHub - found single TT hub", this);
v2Bus->AddHSHub(_address, 0);
_hsHub = true;
break;
case 2:
USBLog(5, "AppleUSBHub[%p]::ConfigureHub - found multi TT hub", this);
_hsHub = true;
if ((err = _hubInterface->SetAlternateInterface(this, 1))) {
USBError(1, "AppleUSBHub[%p]::ConfigureHub - err (%x) setting alt interface", this, err);
v2Bus->AddHSHub(_address, 0);
}
else
v2Bus->AddHSHub(_address, kUSBHSHubFlagsMultiTT);
_multiTTs = true;
break;
default:
USBError(1, "AppleUSBHub[%p]::ConfigureHub - unknown protocol (%d)", this, _device->GetProtocol());
break;
}
}
else
{
USBLog(5, "AppleUSBHub[%p]::ConfigureHub - not on a V2 controller", this);
}
}
IOUSBFindEndpointRequest request;
request.type = kUSBInterrupt;
request.direction = kUSBIn;
_interruptPipe = _hubInterface->FindNextPipe(NULL, &request);
if(!_interruptPipe)
{
USBError(1,"AppleUSBHub[%p]::ConfigureHub could not find interrupt pipe", this);
err = kIOUSBNotEnoughPipesErr; goto ErrorExit;
}
UnpackPortFlags();
CountCaptivePorts();
err = CheckPortPowerRequirements();
if ( err != kIOReturnSuccess )
{
USBError(1,"AppleUSBHub[%p]::ConfigureHub CheckPortPowerRequirements failed with 0x%x", this, err);
goto ErrorExit;
}
err = AllocatePortMemory();
if ( err != kIOReturnSuccess )
{
USBError(1,"AppleUSBHub[%p]::ConfigureHub AllocatePortMemory failed with 0x%x", this, err);
goto ErrorExit;
}
if (_hsHub)
{
if (!_multiTTs)
setProperty("High Speed", (unsigned long long)1, 8); else
setProperty("High Speed", (unsigned long long)_hubDescriptor.numPorts, 8); setProperty("IOUserClientClass", "AppleUSBHSHubUserClient");
}
err = StartPorts();
if ( err != kIOReturnSuccess )
{
USBError(1,"AppleUSBHub[%p]::ConfigureHub StartPorts failed with 0x%x", this, err);
goto ErrorExit;
}
if (_timerSource)
{
_timerSource->setTimeoutMS(kWatchdogTimerPeriod);
}
_hubIsDead = FALSE;
ErrorExit:
return err;
}
void
AppleUSBHub::UnpackPortFlags(void)
{
int i;
int numFlags = ((_hubDescriptor.numPorts + 1) / 8) + 1;
for(i = 0; i < numFlags; i++)
{
_hubDescriptor.pwrCtlPortFlags[i] = _hubDescriptor.removablePortFlags[numFlags+i];
_hubDescriptor.removablePortFlags[numFlags+i] = 0;
}
}
void
AppleUSBHub::CountCaptivePorts(void)
{
int portMask = 2;
int portByte = 0;
int currentPort;
for (currentPort = 1; currentPort <= _hubDescriptor.numPorts; currentPort++)
{
if ((_hubDescriptor.removablePortFlags[portByte] & portMask) != 0)
_numCaptive++;
portMask <<= 1;
if(portMask > 0x80)
{
portMask = 1;
portByte++;
}
}
}
IOReturn
AppleUSBHub::CheckPortPowerRequirements(void)
{
IOReturn err = kIOReturnSuccess;
UInt32 hubPower = _hubDescriptor.hubCurrent/2;
UInt32 busPower = _device->GetBusPowerAvailable();
UInt32 powerAvailForPorts = 0;
UInt32 powerNeededForPorts = 0;
bool startExternal;
do
{
if (hubPower > busPower)
{
USBLog(3, "AppleUSBHub [%p] Hub claims to need more power (%ld > %ld) than available", this, hubPower, busPower);
_busPowerGood = false;
_powerForCaptive = 0;
}
else
{
powerAvailForPorts = busPower - hubPower;
powerNeededForPorts = (_hubDescriptor.numPorts - _numCaptive) * kUSB100mA;
_busPowerGood = (powerAvailForPorts >= powerNeededForPorts);
if(_numCaptive > 0)
{
if(_busPowerGood)
_powerForCaptive =
(powerAvailForPorts - powerNeededForPorts) / _numCaptive;
else
_powerForCaptive = powerAvailForPorts / _numCaptive;
}
if( (_errataBits & kErrataCaptiveOKBit) != 0)
_powerForCaptive = kUSB100mAAvailable;
}
_selfPowerGood = false;
if (_selfPowered)
{
USBStatus status = 0;
IOReturn localErr;
_powerForCaptive = kUSB100mAAvailable;
localErr = _device->GetDeviceStatus(&status);
if ( localErr != kIOReturnSuccess )
{
err = localErr;
break;
}
status = USBToHostWord(status);
_selfPowerGood = ((status & 1) != 0); }
if(_selfPowered && _busPowered)
{
if(_selfPowerGood)
{
USBLog(3,"AppleUSBHub[%p] Hub attached - Self/Bus powered, power supply good", this);
}
else
{
USBLog(3,"AppleUSBHub[%p] Hub attached - Self/Bus powered, no external power", this);
}
}
else
{
if(_selfPowered)
{
if(_selfPowerGood)
{
USBLog(3,"AppleUSBHub[%p] Hub attached - Self powered, power supply good", this);
}
else
{
USBLog(3,"AppleUSBHub[%p] Hub attached - Self powered, no external power", this);
}
}
else
{
USBLog(3,"AppleUSBHub[%p] Hub attached - Bus powered", this);
}
}
startExternal = (_busPowerGood || _selfPowerGood);
if( !startExternal )
{
err = kIOReturnNoPower;
_device->DisplayUserNotification(kUSBNotEnoughPowerNotificationType);
IOLog("USB Low Power Notice: The hub \"%s\" cannot be used because there is not enough power for all its ports\n", _device->getName());
USBLog(1,"AppleUSBHub[%p]: insufficient power to turn on ports", this);
if(!_busPowered)
{
break;
}
}
} while (false);
return err;
}
IOReturn
AppleUSBHub::AllocatePortMemory(void)
{
AppleUSBHubPort *port;
UInt32 power;
UInt32 portMask = 2;
UInt32 portByte = 0;
UInt32 currentPort;
bool captive;
_ports = (AppleUSBHubPort **) IOMalloc(sizeof(AppleUSBHubPort *) * _hubDescriptor.numPorts);
if (!_ports)
return kIOReturnNoMemory;
for (currentPort = 1; currentPort <= _hubDescriptor.numPorts; currentPort++)
{
if ((_hubDescriptor.removablePortFlags[portByte] & portMask) != 0)
{
power = _selfPowerGood ? (UInt32)kUSB500mAAvailable : _powerForCaptive;
captive = true;
}
else
{
power = _selfPowerGood ? kUSB500mAAvailable : kUSB100mAAvailable;
captive = false;
}
port = new AppleUSBHubPort;
if (port->init(self, currentPort, power, captive) != kIOReturnSuccess)
{
port->release();
_ports[currentPort-1] = NULL;
}
else
_ports[currentPort-1] = port;
portMask <<= 1;
if(portMask > 0x80)
{
portMask = 1;
portByte++;
}
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBHub::StartPorts(void)
{
AppleUSBHubPort *port;
int currentPort;
USBLog(5, "AppleUSBHub [%p]: starting ports (%d)", this, _hubDescriptor.numPorts);
for (currentPort = 1; currentPort <= _hubDescriptor.numPorts; currentPort++)
{
port = _ports[currentPort-1];
if (port)
port->start();
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBHub::StopPorts(void)
{
AppleUSBHubPort * port;
AppleUSBHubPort ** cachedPorts;
int currentPort;
USBLog(5, "AppleUSBHub [%p]: stopping ports (%d)", this, _hubDescriptor.numPorts);
if( _ports)
{
cachedPorts = _ports;
_ports = NULL;
for (currentPort = 1; currentPort <= _hubDescriptor.numPorts; currentPort++)
{
port = cachedPorts[currentPort-1];
if (port)
{
cachedPorts[currentPort-1] = NULL;
port->stop();
port->release();
}
}
IOFree(cachedPorts, sizeof(AppleUSBHubPort *) * _hubDescriptor.numPorts);
}
return kIOReturnSuccess;
}
bool
AppleUSBHub::HubStatusChanged(void)
{
IOReturn err = kIOReturnSuccess;
USBLog(6,"+AppleUSBHub[%p]::HubStatusChanged ", this);
do
{
if ((err = GetHubStatus(&_hubStatus)))
{
FatalError(err, "get status (first in hub status change)");
break;
}
_hubStatus.statusFlags = USBToHostWord(_hubStatus.statusFlags);
_hubStatus.changeFlags = USBToHostWord(_hubStatus.changeFlags);
USBLog(3,"AppleUSBHub[%p]: hub status = %x/%x", this, _hubStatus.statusFlags, _hubStatus.changeFlags);
if (_hubStatus.changeFlags & kHubLocalPowerStatusChange)
{
USBLog(3, "AppleUSBHub[%p]: Hub Local Power Status Change detected", this);
if ((err = ClearHubFeature(kUSBHubLocalPowerChangeFeature)))
{
FatalError(err, "clear hub power status feature");
break;
}
if ((err = GetHubStatus(&_hubStatus)))
{
FatalError(err, "get status (second in hub status change)");
break;
}
_hubStatus.statusFlags = USBToHostWord(_hubStatus.statusFlags);
_hubStatus.changeFlags = USBToHostWord(_hubStatus.changeFlags);
USBLog(3,"AppleUSBHub[%p]: hub status after clearing LocalPowerChange = %x/%x", this, _hubStatus.statusFlags, _hubStatus.changeFlags);
}
if (_hubStatus.changeFlags & kHubOverCurrentIndicatorChange)
{
USBLog(3, "AppleUSBHub[%p]: Hub OverCurrent Indicator Change detected", this);
if ((err =
ClearHubFeature(kUSBHubOverCurrentChangeFeature)))
{
FatalError(err, "clear hub over current feature");
break;
}
if ((err = GetHubStatus(&_hubStatus)))
{
FatalError(err, "get status (second in hub status change)");
break;
}
_hubStatus.statusFlags = USBToHostWord(_hubStatus.statusFlags);
_hubStatus.changeFlags = USBToHostWord(_hubStatus.changeFlags);
USBLog(3,"AppleUSBHub[%p]: hub status after clearing HubOvercurrent = %x/%x", this, _hubStatus.statusFlags, _hubStatus.changeFlags);
}
OSBoolean * boolObj = OSDynamicCast( OSBoolean, getProperty("kResetOnPowerStatusChange") );
if ( boolObj && boolObj->isTrue() )
{
if ( _hubStatus.statusFlags & ( kUSBHubLocalPowerChangeFeature || kHubOverCurrentIndicatorChange ) )
{
USBLog(3,"AppleUSBHub[%p]: Hub status was a change to ON (0x%x)", this, _hubStatus.statusFlags);
err = kIOReturnBusy;
}
}
} while(false);
if ( err == kIOReturnSuccess )
{
USBLog(6,"+AppleUSBHub[%p]::HubStatusChanged returning true", this);
return true;
}
else
{
_hubIsDead = TRUE;
retain();
ResetMyPort();
release();
USBLog(6,"-AppleUSBHub[%p]::HubStatusChanged returning false", this);
return false;
}
}
UInt32
AppleUSBHub::GetHubErrataBits()
{
UInt16 vendID, deviceID, revisionID;
ErrataListEntry *entryPtr;
UInt32 i, errata = 0;
vendID = _device->GetVendorID();
deviceID = _device->GetProductID();
revisionID = _device->GetDeviceRelease();
for(i=0, entryPtr = errataList; i < errataListLength; i++, entryPtr++)
{
if (vendID == entryPtr->vendID
&& deviceID == entryPtr->deviceID
&& revisionID >= entryPtr->revisionLo
&& revisionID <= entryPtr->revisionHi)
{
errata |= entryPtr->errata; }
}
return(errata);
}
void
AppleUSBHub::FatalError(IOReturn err, char *str)
{
USBError(1, "AppleUSBHub[%p]::FatalError 0x%x: %s", this, err, str);
}
IOReturn
AppleUSBHub::GetHubDescriptor(IOUSBHubDescriptor *desc)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
if (!desc) return (kIOReturnBadArgument);
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBClass, kUSBDevice);
request.bRequest = kUSBRqGetDescriptor;
request.wValue = (kUSBHubDescriptorType << 8) + 0; request.wIndex = 0;
request.wLength = sizeof(IOUSBHubDescriptor);
request.pData = desc;
err = DoDeviceRequest(&request);
if (err)
{
USBLog(5,"AppleUSBHub[%p]: GetHubDescriptor w/ type = %X returned error: 0x%x", this, kUSBHubDescriptorType, err);
request.wValue = 0;
request.wLength = sizeof(IOUSBHubDescriptor);
err = DoDeviceRequest(&request);
}
if (err)
{
USBLog(3, "AppleUSBHub [%p] GetHubDescriptor error = 0x%x", this, err);
}
return(err);
}
IOReturn
AppleUSBHub::GetHubStatus(IOUSBHubStatus *status)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBClass, kUSBDevice);
request.bRequest = kUSBRqGetStatus;
request.wValue = 0;
request.wIndex = 0;
request.wLength = sizeof(IOUSBHubStatus);
request.pData = status;
err = DoDeviceRequest(&request);
if (err)
{
USBLog(3, "AppleUSBHub [%p] GetHubStatus error = 0x%x", this, err);
}
return(err);
}
IOReturn
AppleUSBHub::GetPortState(UInt8 *state, UInt16 port)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBClass, kUSBOther);
request.bRequest = kUSBRqGetState;
request.wValue = 0;
request.wIndex = port;
request.wLength = sizeof(*state);
request.pData = state;
err = DoDeviceRequest(&request);
if (err)
{
USBLog(3, "AppleUSBHub [%p] GetPortState error = 0x%x", this, err);
}
return(err);
}
IOReturn
AppleUSBHub::ClearHubFeature(UInt16 feature)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBDevice);
request.bRequest = kUSBRqClearFeature;
request.wValue = feature;
request.wIndex = 0;
request.wLength = 0;
request.pData = NULL;
err = DoDeviceRequest(&request);
if (err)
{
USBLog(3, "AppleUSBHub [%p] ClearHubFeature error = 0x%x", this, err);
}
return(err);
}
IOReturn
AppleUSBHub::GetPortStatus(IOUSBHubPortStatus *status, UInt16 port)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
int i = 0;
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBClass, kUSBOther);
request.bRequest = kUSBRqGetStatus;
request.wValue = 0;
request.wIndex = port;
request.wLength = sizeof(IOUSBHubPortStatus);
request.pData = status;
request.wLenDone = 0;
err = DoDeviceRequest(&request);
while ((i++ < 30) && !err && (request.wLenDone != sizeof(IOUSBHubPortStatus)))
{
USBLog(1, "AppleUSBHub[%p]::GetPortStatus - request came back with %d bytes - retrying", this, (int)request.wLenDone);
err = DoDeviceRequest(&request);
}
if (!err && (request.wLenDone != sizeof(IOUSBHubPortStatus)))
{
USBLog(1, "AppleUSBHub[%p]::GetPortStatus - request never returned bytes in %d tries - returning kIOReturnUnderrun", this, i);
err = kIOReturnUnderrun;
}
if (err)
{
USBLog(3, "AppleUSBHub[%p]::GetPortStatus, error (%x) returned from DoDeviceRequest", this, err);
}
if ( err == kIOReturnSuccess)
{
status->statusFlags = USBToHostWord(status->statusFlags);
status->changeFlags = USBToHostWord(status->changeFlags);
USBLog( 6, "AppleUSBHub[%p]::GetPortStatus for port %d, status: 0x%8x, change: 0x%8x - returning kIOReturnSuccess", this, port, status->statusFlags, status->changeFlags);
}
return(err);
}
IOReturn
AppleUSBHub::SetPortFeature(UInt16 feature, UInt16 port)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
USBLog(5, "AppleUSBHub[%p]::SetPortFeature port/feature (%x) - setting", this, (port << 16) | feature);
request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBOther);
request.bRequest = kUSBRqSetFeature;
request.wValue = feature;
request.wIndex = port;
request.wLength = 0;
request.pData = NULL;
err = DoDeviceRequest(&request);
if (err && (err != kIOUSBDeviceNotHighSpeed))
{
USBLog(1, "AppleUSBHub[%p]::SetPortFeature (%d) to port %d got error (%x) from DoDeviceRequest", this, feature, port, err);
}
return(err);
}
IOReturn
AppleUSBHub::ClearPortFeature(UInt16 feature, UInt16 port)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
USBLog(5, "AppleUSBHub[%p]::ClearPortFeature port/feature (%x) - clearing", this, (port << 16) | feature);
request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBOther);
request.bRequest = kUSBRqClearFeature;
request.wValue = feature;
request.wIndex = port;
request.wLength = 0;
request.pData = NULL;
err = DoDeviceRequest(&request);
if (err)
{
USBLog(1, "AppleUSBHub[%p]::ClearPortFeature got error (%x) to DoDeviceRequest", this, err);
}
return(err);
}
IOReturn
AppleUSBHub::DoPortAction(UInt32 type, UInt32 portNumber, UInt32 options )
{
AppleUSBHubPort *port;
IOReturn err = kIOReturnSuccess;
USBLog(5,"+AppleUSBHub[%p]::DoPortAction(0x%lx) for port (%ld), options (0x%lx)", this, type, portNumber, options);
IncrementOutstandingIO();
if ( _portSuspended && _device )
{
USBLog(5,"AppleUSBHub[%p]::DoPortAction(0x%lx) for port (%ld), unsuspending port", this, type, portNumber);
err = _device->SuspendDevice(false);
if ( err != kIOReturnSuccess )
{
USBLog(3,"AppleUSBHub[%p]::DoPortAction unsuspending port returned 0x%x", this, err);
goto ErrorExit;
}
}
if ( _ports == NULL )
{
USBLog(3,"AppleUSBHub[%p]::DoPortAction _ports is NULL!", this);
err = kIOReturnNoDevice;
goto ErrorExit;
}
port = _ports[portNumber - 1];
if (port)
{
port->retain();
switch ( type )
{
case kIOUSBMessageHubSuspendPort:
err = port->SuspendPort( true );
break;
case kIOUSBMessageHubResumePort:
err = port->SuspendPort( false );
break;
case kIOUSBMessageHubReEnumeratePort:
err = port->ReEnumeratePort(options);
break;
case kIOUSBMessageHubResetPort:
err = port->ResetPort();
break;
case kIOUSBMessageHubPortClearTT:
if ( _hsHub )
err = port->ClearTT(_multiTTs, options);
else
err = kIOReturnUnsupported;
break;
}
port->release();
}
ErrorExit:
DecrementOutstandingIO();
USBLog(5,"-AppleUSBHub[%p]::DoPortAction(0x%lx) for port (%ld) returning 0x%x", this, type, portNumber, err);
return err;
}
void
AppleUSBHub::InterruptReadHandlerEntry(OSObject *target, void *param, IOReturn status, UInt32 bufferSizeRemaining)
{
AppleUSBHub * me = OSDynamicCast(AppleUSBHub, target);
if (!me)
return;
me->InterruptReadHandler(status, bufferSizeRemaining);
me->DecrementOutstandingIO();
}
void
AppleUSBHub::InterruptReadHandler(IOReturn status, UInt32 bufferSizeRemaining)
{
bool queueAnother = TRUE;
IOReturn err = kIOReturnSuccess;
switch (status)
{
case kIOReturnOverrun:
USBLog(3, "AppleUSBHub[%p]::InterruptReadHandler kIOReturnOverrun error", this);
if (!isInactive())
{
if ( _interruptPipe )
{
_interruptPipe->ClearStall();
IncrementOutstandingIO();
thread_call_enter(_clearFeatureEndpointHaltThread);
}
}
case kIOReturnSuccess:
_retryCount = kHubDriverRetryCount;
if ( !_hubIsDead )
{
IncrementOutstandingIO();
thread_call_enter(_workThread);
}
queueAnother = FALSE;
break;
case kIOReturnNotResponding:
USBLog(3, "AppleUSBHub[%p]::InterruptReadHandler error kIOReturnNotResponding", this);
if ( _hubHasBeenDisconnected || isInactive() )
{
queueAnother = false;
}
else
{
USBLog(3, "AppleUSBHub[%p]::InterruptReadHandler Checking to see if hub is still connected", this);
CallCheckForDeadHub();
queueAnother = false;
}
break;
case kIOReturnAborted:
if (isInactive() || _hubIsDead || _abortExpected )
{
USBLog(3, "AppleUSBHub[%p]::InterruptReadHandler error kIOReturnAborted (expected)", this);
queueAnother = false;
}
else
{
USBLog(3, "AppleUSBHub[%p]::InterruptReadHandler error kIOReturnAborted. Try again.", this);
}
break;
case kIOReturnUnderrun:
case kIOUSBPipeStalled:
case kIOUSBLinkErr:
case kIOUSBNotSent2Err:
case kIOUSBNotSent1Err:
case kIOUSBBufferUnderrunErr:
case kIOUSBBufferOverrunErr:
case kIOUSBWrongPIDErr:
case kIOUSBPIDCheckErr:
case kIOUSBDataToggleErr:
case kIOUSBBitstufErr:
case kIOUSBCRCErr:
USBLog(3, "AppleUSBHub[%p]::InterruptReadHandler OHCI error (0x%x) reading interrupt pipe", this, status);
if (!isInactive())
{
if ( _interruptPipe )
{
_interruptPipe->ClearStall();
IncrementOutstandingIO();
thread_call_enter(_clearFeatureEndpointHaltThread);
}
}
queueAnother = false;
break;
case kIOUSBHighSpeedSplitError:
default:
USBLog(3,"AppleUSBHub[%p]::InterruptReadHandler error 0x%x reading interrupt pipe", this, status);
if (isInactive())
queueAnother = false;
else
{
if ( _interruptPipe )
_interruptPipe->ClearStall();
}
break;
}
if ( queueAnother )
{
err = RearmInterruptRead();
}
}
void
AppleUSBHub::ResetPortZeroEntry(OSObject *target)
{
AppleUSBHub * me = OSDynamicCast(AppleUSBHub, target);
if (!me)
return;
me->ResetPortZero();
me->DecrementOutstandingIO();
}
void
AppleUSBHub::ResetPortZero()
{
AppleUSBHubPort *port;
UInt32 currentPort;
if( _ports)
for (currentPort = 1; currentPort <= _hubDescriptor.numPorts; currentPort++)
{
port = _ports[currentPort-1];
if (port)
{
Boolean locked;
port->retain();
locked = port->GetDevZeroLock();
if ( locked )
{
if ( ((_timeoutFlag & (1 << (currentPort-1))) != 0) && (_portTimeStamp[currentPort-1] == port->GetPortTimeStamp()) )
{
USBLog(1, "AppleUSBHub[%p]::ResetPortZero: - port %ld - Releasing devZero lock", this, currentPort);
_timeoutFlag &= ~( 1<<(currentPort-1));
port->ReleaseDevZeroLock();
}
}
port->release();
}
}
}
void
AppleUSBHub::ProcessStatusChangedEntry(OSObject *target)
{
AppleUSBHub * me = OSDynamicCast(AppleUSBHub, target);
if (!me)
return;
me->ProcessStatusChanged();
me->DecrementOutstandingIO();
}
void
AppleUSBHub::ProcessStatusChanged()
{
const UInt8 * statusChangedBitmapPtr = 0;
int portMask;
int portByte;
int portIndex;
AppleUSBHubPort *port;
bool portSuccess = false;
bool hubStatusSuccess = true;
if (isInactive() || !_buffer || !_ports)
return;
portMask = 2;
portByte = 0;
statusChangedBitmapPtr = (const UInt8*)_buffer->getBytesNoCopy();
if (statusChangedBitmapPtr == NULL)
{
USBError(1, "AppleUSBHub[%p]::ProcessStatusChanged: No interrupt pipe buffer!", this);
}
else
{
if ( statusChangedBitmapPtr[0] == 0xff)
{
USBLog(5,"AppleUSBHub[%p]::ProcessStatusChanged found (0x%8.8x) in statusChangedBitmap", this, statusChangedBitmapPtr[0]);
}
else
{
if ((statusChangedBitmapPtr[0] & 1) != 0)
{
hubStatusSuccess = HubStatusChanged();
}
if ( hubStatusSuccess )
{
USBLog(5,"AppleUSBHub[%p]::ProcessStatusChanged found (0x%8.8x) in statusChangedBitmap", this, statusChangedBitmapPtr[0]);
for (portIndex = 1; portIndex <= _hubDescriptor.numPorts; portIndex++)
{
if ((statusChangedBitmapPtr[portByte] & portMask) != 0)
{
port = _ports[portIndex-1];
if ( port )
{
USBLog(5,"AppleUSBHub[%p]::ProcessStatusChanged port number %d, calling port->StatusChanged", this, portIndex);
portSuccess = port->StatusChanged();
if (! portSuccess )
{
USBLog(1,"AppleUSBHub[%p]::ProcessStatusChanged port->StatusChanged() returned false", this);
}
}
}
portMask <<= 1;
if (portMask > 0x80)
{
portMask = 1;
portByte++;
}
}
(void) RearmInterruptRead();
}
}
}
}
IOReturn
AppleUSBHub::RearmInterruptRead()
{
IOReturn err = kIOReturnSuccess;
IOUSBCompletion comp;
USBLog(5,"+AppleUSBHub[%p]::RearmInterruptRead", this);
if ( _isRootHub )
{
IOSleep(32);
}
IncrementOutstandingIO();
if ( isInactive() || (_buffer == NULL) || ( _interruptPipe == NULL ) )
{
DecrementOutstandingIO();
return err;
}
comp.target = this;
comp.action = (IOUSBCompletionAction) InterruptReadHandlerEntry;
comp.parameter = NULL;
_buffer->setLength(_readBytes);
if ((err = _interruptPipe->Read(_buffer, &comp)))
{
USBError(1,"AppleUSBHub[%p]::RearmInterruptRead error %x reading interrupt pipe", this, err);
DecrementOutstandingIO();
}
return err;
}
void
AppleUSBHub::PrintHubDescriptor(IOUSBHubDescriptor *desc)
{
int i = 0;
char *characteristics[] =
{ "ppsw", "nosw", "comp", "ppoc", "nooc", 0 };
if (desc->length == 0) return;
IOLog("hub descriptor: (%d bytes)\n", desc->length);
IOLog("\thubType = %d\n", desc->hubType);
IOLog("\tnumPorts = %d\n", desc->numPorts);
IOLog("\tcharacteristics = %x ( ",
USBToHostWord(desc->characteristics));
do
{
if (USBToHostWord(desc->characteristics) & (1 << i))
IOLog("%s ", characteristics[i]);
} while (characteristics[++i]);
IOLog(")\n");
IOLog("\tpowerOnToGood = %d ms\n", desc->powerOnToGood * 2);
IOLog("\thubCurrent = %d\n", desc->hubCurrent);
IOLog("\tremovablePortFlags = %lx %lx\n", (UInt32)&desc->removablePortFlags[1], (UInt32)&desc->removablePortFlags[0]);
IOLog("\tpwrCtlPortFlags = %lx %lx\n", (UInt32)&desc->pwrCtlPortFlags[1], (UInt32)&desc->removablePortFlags[0]);
}
IOReturn
AppleUSBHub::message( UInt32 type, IOService * provider, void * argument )
{
IOReturn err = kIOReturnSuccess;
IOUSBHubPortStatus status;
IOUSBHubPortReEnumerateParam * params ;
IOUSBHubPortClearTTParam * ttParams;
switch ( type )
{
case kIOUSBMessageHubIsDeviceConnected:
if ( isInactive() || _hubIsDead )
{
USBLog(3,"AppleUSBHub[%p] : got kIOUSBMessageHubIsDeviceConnected while isInactive() or _hubIsDead", this);
err = kIOReturnNoDevice;
break;
}
IncrementOutstandingIO(); err = GetPortStatus(&status, * (UInt32 *) argument );
if ( err != kIOReturnSuccess )
{
err = kIOReturnNoDevice;
}
else
{
USBLog(5,"AppleUSBHub[%p]::kIOUSBMessageHubIsDeviceConnected - port %ld - status(%8x)/change(%8x)", this, * (UInt32 *) argument, status.statusFlags, status.changeFlags);
if ( (status.statusFlags & kHubPortConnection) && !(status.changeFlags & kHubPortConnection) )
err = kIOReturnSuccess;
else
err = kIOReturnNoDevice;
}
DecrementOutstandingIO();
break;
case kIOUSBMessageHubSuspendPort:
case kIOUSBMessageHubResumePort:
case kIOUSBMessageHubResetPort:
err = DoPortAction( type, * (UInt32 *) argument, 0 );
break;
case kIOUSBMessageHubPortClearTT:
ttParams = (IOUSBHubPortClearTTParam *) argument;
err = DoPortAction( type, ttParams->portNumber, ttParams->options );
break;
case kIOUSBMessageHubReEnumeratePort:
params = (IOUSBHubPortReEnumerateParam *) argument;
err = DoPortAction( type, params->portNumber, params->options );
break;
case kIOMessageServiceIsTerminated:
USBLog(3,"AppleUSBHub[%p] : Received kIOMessageServiceIsTerminated - ignoring", this);
break;
case kIOUSBMessagePortHasBeenReset:
if ( isInactive() )
{
USBLog(5,"AppleUSBHub[%p] : got kIOUSBMessagePortHasBeenReset while isInactive() or _hubIsDead", this);
err = kIOReturnSuccess;
break;
}
if (!_inStartMethod)
{
_inStartMethod = true;
IncrementOutstandingIO(); USBLog(3, "AppleUSBHub[%p] Received kIOUSBMessagePortHasBeenReset -- reconfiguring hub", this);
if ( _interruptPipe )
{
_interruptPipe->Abort();
_interruptPipe = NULL;
}
if(_hubInterface)
{
_hubInterface->close(this);
_hubInterface->release();
_hubInterface = NULL;
}
err = ConfigureHub();
if ( err )
{
USBLog(3, "AppleUSBHub[%p] Reconfiguring hub returned: 0x%x", this, err);
}
else
{
USBError(1, "[%p] (Reset) USB Generic Hub @ %d (0x%lx)", this, _address, _locationID);
}
_inStartMethod = false;
DecrementOutstandingIO();
if ( err == kIOReturnSuccess)
{
err = RearmInterruptRead();
}
}
USBLog(3, "-AppleUSBHub[%p] Received kIOUSBMessagePortHasBeenReset -- finishded reconfiguring hub", this);
break;
case kIOUSBMessagePortHasBeenResumed:
case kIOUSBMessagePortWasNotSuspended:
USBLog(5, "AppleUSBHub[%p]: received kIOUSBMessagePortHasBeenResumed or kIOUSBMessagePortWasNotSuspended (0x%lx)", this, type);
_portSuspended = false;
_abortExpected = false;
err = RearmInterruptRead();
break;
default:
break;
}
return err;
}
IOReturn
AppleUSBHub::DoDeviceRequest(IOUSBDevRequest *request)
{
IOReturn err;
if ( _device && !_device->isInactive() && _device->isOpen(this))
{
if ( _portSuspended )
{
USBLog(5,"+AppleUSBHub[%p]::DoDeviceRequest, unsuspending port", this);
err = _device->SuspendDevice(false);
if ( err != kIOReturnSuccess )
return err;
}
err = _device->DeviceRequest(request, 5000, 0);
}
else
err = kIOReturnNoDevice;
return err;
}
bool
AppleUSBHub::finalize(IOOptionBits options)
{
return(super::finalize(options));
}
void
AppleUSBHub::TimeoutOccurred(OSObject *owner, IOTimerEventSource *sender)
{
AppleUSBHub *me;
AppleUSBHubPort *port;
UInt32 currentPort;
IOReturn kr = kIOReturnSuccess;
bool checkPorts = false;
me = OSDynamicCast(AppleUSBHub, owner);
if (!me)
return;
me->retain();
if ( --me->_devZeroLockedTimeoutCounter == 0 )
{
checkPorts = true;
me->_devZeroLockedTimeoutCounter = kDevZeroTimeoutCount;
}
if ( checkPorts && me->_ports)
{
for (currentPort = 1; currentPort <= me->_hubDescriptor.numPorts; currentPort++)
{
port = me->_ports[currentPort-1];
if (port)
{
Boolean locked;
port->retain();
locked = port->GetDevZeroLock();
if ( locked )
{
if ( ((me->_timeoutFlag & (1 << (currentPort-1))) != 0) && (me->_portTimeStamp[currentPort-1] == port->GetPortTimeStamp()) )
{
USBError(3,"AppleUSBHub[%p]::TimeoutOccurred error", me);
me->IncrementOutstandingIO();
thread_call_enter(me->_resetPortZeroThread);
}
else
{
me->_timeoutFlag |= (1<<(currentPort-1));
me->_portTimeStamp[currentPort-1] = port->GetPortTimeStamp();
}
}
else
{
me->_timeoutFlag &= ~( 1<<(currentPort-1));
me->_portTimeStamp[currentPort-1] = 0;
}
port->release();
}
}
}
if ( !me->_isRootHub && !me->_portSuspended )
{
if (me->HubAreAllPortsDisconnectedOrSuspended())
{
USBLog(5, "AppleUSBHub[%p](0x%lx)::TimeoutOccurred Suspending our port", me, me->_locationID );
if ( me->_outstandingIO )
{
me->_abortExpected = true;
if ( me->_interruptPipe )
{
kr = me->_interruptPipe->ClearPipeStall(true);
if ( kr != kIOReturnSuccess)
{
USBLog(4, "AppleUSBHub[%p]::TimeoutOccurred _interruptPipe->ClearPipeStall returned 0x%x", me, kr );
}
}
}
else
{
USBLog(1, "AppleUSBHub[%p]::TimeoutOccurred suspending hub device, but no outstandingIO", me );
}
if ( me->_device)
{
kr = me->_device->SuspendDevice(true);
if ( kr == kIOReturnSuccess )
me->_portSuspended = true;
else
USBLog(1, "AppleUSBHub[%p]::TimeoutOccurred SuspendDevice returned 0x%x", me, kr );
}
else
{
USBLog(1, "AppleUSBHub[%p]::TimeoutOccurred _device was NULL", me );
}
}
}
if (me->_timerSource && !me->isInactive() )
{
me->_timerSource->setTimeoutMS(kWatchdogTimerPeriod);
}
me->release();
}
void
AppleUSBHub::CallCheckForDeadHub(void)
{
IncrementOutstandingIO();
thread_call_enter(_hubDeadCheckThread);
}
void
AppleUSBHub::CheckForDeadHubEntry(OSObject *target)
{
AppleUSBHub * me = OSDynamicCast(AppleUSBHub, target);
if (!me)
return;
me->CheckForDeadHub();
me->DecrementOutstandingIO();
}
void
AppleUSBHub::CheckForDeadHub()
{
IOReturn err = kIOReturnSuccess;
if ( _device && !_hubIsDead)
{
err = _device->message(kIOUSBMessageHubIsDeviceConnected, NULL, 0);
if ( kIOReturnSuccess == err)
{
if ( --_retryCount == 0 )
{
_hubIsDead = TRUE;
USBLog(3, "AppleUSBHub[%p]::CheckForDeadHub - Still connected. Resetting port", this);
retain();
ResetMyPort();
release();
}
else
{
USBLog(3, "AppleUSBHub[%p]::CheckForDeadHub - Still connected but retry count (%ld) not reached, clearing stall and retrying", this, _retryCount);
if ( _interruptPipe )
{
_interruptPipe->ClearPipeStall(true);
}
(void) RearmInterruptRead();
}
}
else
{
_hubHasBeenDisconnected = TRUE;
USBLog(3, "AppleUSBHub[%p]::CheckForDeadHub - device has been unplugged", this);
}
}
else
{
USBLog(3,"AppleUSBHub[%p]::CheckForDeadHub -- already resetting hub", this);
}
}
void
AppleUSBHub::ClearFeatureEndpointHaltEntry(OSObject *target)
{
AppleUSBHub * me = OSDynamicCast(AppleUSBHub, target);
if (!me)
return;
me->ClearFeatureEndpointHalt();
me->DecrementOutstandingIO();
}
void
AppleUSBHub::ClearFeatureEndpointHalt( )
{
IOReturn status = kIOReturnSuccess;
IOUSBDevRequest request;
UInt32 retries = 2;
bzero( &request, sizeof(IOUSBDevRequest));
while ( (retries > 0) && (_interruptPipe) )
{
retries--;
request.bmRequestType = USBmakebmRequestType(kUSBNone, kUSBStandard, kUSBEndpoint);
request.bRequest = kUSBRqClearFeature;
request.wValue = kUSBFeatureEndpointStall;
request.wIndex = _interruptPipe->GetEndpointNumber() | 0x80 ; request.wLength = 0;
request.pData = NULL;
status = _device->DeviceRequest(&request, 5000, 0);
if ( status != kIOReturnSuccess )
{
USBLog(3, "AppleUSBHub[%p]::ClearFeatureEndpointHalt - DeviceRequest returned: 0x%x, retries = %ld", this, status, retries);
IOSleep(100);
}
else
break;
}
status = RearmInterruptRead();
}
void
AppleUSBHub::ResetMyPort()
{
willTerminate(this, 0);
if (_timerSource)
{
_timerSource->cancelTimeout();
}
StopPorts();
_device->ResetDevice();
}
bool
AppleUSBHub::requestTerminate( IOService * provider, IOOptionBits options )
{
USBLog(3, "AppleUSBHub[%p]::requestTerminate isInactive = %d", this, isInactive());
return super::requestTerminate(provider, options);
}
bool
AppleUSBHub::willTerminate( IOService * provider, IOOptionBits options )
{
IOReturn err;
int currentPort;
AppleUSBHubPort * port;
USBLog(3, "AppleUSBHub[%p]::willTerminate isInactive = %d", this, isInactive());
if ( _interruptPipe )
{
err = _interruptPipe->Abort();
if ( err != kIOReturnSuccess )
{
USBLog(1, "AppleUSBHub[%p]::willTerminate interruptPipe->Abort returned 0x%x", this, err);
}
}
if ( _ports)
{
for (currentPort = 1; currentPort <= _hubDescriptor.numPorts; currentPort++)
{
port = _ports[currentPort-1];
if (port)
{
if (port->_devZero)
{
USBLog(1, "AppleUSBHub[%p]::StopPorts - port %d had the dev zero lock", this, currentPort);
}
port->willTerminate(this, 0);
}
}
}
if (_timerSource)
{
_timerSource->cancelTimeout();
}
return super::willTerminate(provider, options);
}
bool
AppleUSBHub::didTerminate( IOService * provider, IOOptionBits options, bool * defer )
{
USBLog(3, "AppleUSBHub[%p]::didTerminate isInactive = %d", this, isInactive());
StopPorts();
if (!_outstandingIO)
{
_device->close(this);
}
else
{
_needToClose = true;
}
return super::didTerminate(provider, options, defer);
}
bool
AppleUSBHub::terminate( IOOptionBits options )
{
USBLog(5, "AppleUSBHub[%p]::terminate isInactive = %d", this, isInactive());
return super::terminate(options);
}
void
AppleUSBHub::free( void )
{
USBLog(5, "AppleUSBHub[%p]::free isInactive = %d", this, isInactive());
if (_workLoop)
{
_workLoop->release();
_workLoop = NULL;
}
super::free();
}
bool
AppleUSBHub::terminateClient( IOService * client, IOOptionBits options )
{
USBLog(5, "AppleUSBHub[%p]::terminateClient isInactive = %d", this, isInactive());
return super::terminateClient(client, options);
}
void
AppleUSBHub::DecrementOutstandingIO(void)
{
if (!_gate)
{
USBLog(2, "AppleUSBHub[%p]::DecrementOutstandingIO isInactive = %d, outstandingIO = %ld - no gate", this, isInactive(), _outstandingIO);
if (!--_outstandingIO && _needToClose)
{
USBLog(3, "AppleUSBHub[%p]::DecrementOutstandingIO isInactive = %d, outstandingIO = %ld - closing device", this, isInactive(), _outstandingIO);
_device->close(this);
}
return;
}
_gate->runAction(ChangeOutstandingIO, (void*)-1);
}
void
AppleUSBHub::IncrementOutstandingIO(void)
{
if (!_gate)
{
USBLog(2, "AppleUSBHub[%p]::IncrementOutstandingIO isInactive = %d, outstandingIO = %ld - no gate", this, isInactive(), _outstandingIO);
_outstandingIO++;
return;
}
_gate->runAction(ChangeOutstandingIO, (void*)1);
}
IOReturn
AppleUSBHub::ChangeOutstandingIO(OSObject *target, void *param1, void *param2, void *param3, void *param4)
{
AppleUSBHub *me = OSDynamicCast(AppleUSBHub, target);
UInt32 direction = (UInt32)param1;
if (!me)
{
USBLog(1, "AppleUSBHub::ChangeOutstandingIO - invalid target");
return kIOReturnSuccess;
}
switch (direction)
{
case 1:
me->_outstandingIO++;
break;
case -1:
if (!--me->_outstandingIO && me->_needToClose)
{
USBLog(3, "AppleUSBHub[%p]::ChangeOutstandingIO isInactive = %d, outstandingIO = %ld - closing device", me, me->isInactive(), me->_outstandingIO);
me->_device->close(me);
}
break;
default:
USBLog(1, "AppleUSBHub[%p]::ChangeOutstandingIO - invalid direction", me);
}
return kIOReturnSuccess;
}
bool
AppleUSBHub::MergeDictionaryIntoProvider(IOService * provider, OSDictionary * dictionaryToMerge)
{
const OSSymbol * dictionaryEntry = NULL;
OSCollectionIterator * iter = NULL;
bool result = false;
USBLog(6,"+AppleUSBHub[%p]::MergeDictionary(%p)IntoProvider(%p)", this, dictionaryToMerge, provider);
if (!provider || !dictionaryToMerge)
return false;
iter = OSCollectionIterator::withCollection((OSDictionary *)dictionaryToMerge);
if ( iter != NULL )
{
while ( NULL != (dictionaryEntry = (const OSSymbol *)iter->getNextObject()) )
{
const char * str = NULL;
OSDictionary * sourceDictionary = NULL;
OSDictionary * providerDictionary = NULL;
OSObject * providerProperty = NULL;
str = dictionaryEntry->getCStringNoCopy();
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoProvider merging \"%s\"", this, str);
providerProperty = provider->getProperty(dictionaryEntry);
if ( providerProperty )
{
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoProvider provider already had property %s", this, str);
providerDictionary = OSDynamicCast(OSDictionary, providerProperty);
if ( providerDictionary )
{
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoProvider provider's %s is also a dictionary (%p)", this, str, providerDictionary);
}
}
sourceDictionary = OSDynamicCast(OSDictionary, dictionaryToMerge->getObject(dictionaryEntry));
if ( sourceDictionary )
{
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoProvider source dictionary had %s as a dictionary (%p)", this, str, sourceDictionary);
}
if ( providerDictionary && sourceDictionary )
{
OSDictionary * localCopyOfProvidersDictionary;
UInt32 providerSize;
UInt32 providerSizeAfterMerge;
localCopyOfProvidersDictionary = OSDictionary::withDictionary( providerDictionary, 0);
if ( localCopyOfProvidersDictionary == NULL )
{
USBError(1,"AppleUSBHub[%p]::MergeDictionaryIntoProvider could not copy our provider's dictionary", this);
break;
}
providerSize = providerDictionary->getCapacity();
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoProvider Created a local copy(%p) of dictionary (%p), size %ld", this, localCopyOfProvidersDictionary, providerDictionary, providerSize);
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoProvider need to merge a dictionary (%s)", this, str);
result = MergeDictionaryIntoDictionary( sourceDictionary, localCopyOfProvidersDictionary);
if ( result )
{
providerSizeAfterMerge = providerDictionary->getCapacity();
if ( providerSizeAfterMerge != providerSize )
{
USBError(1,"AppleUSBHub[%p]::MergeDictionaryIntoProvider our provider's dictionary size changed (%ld,%ld)", this, providerSize, providerSizeAfterMerge);
}
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoProvider setting property %s from merged dictionary (%p)", this, str, providerDictionary);
result = provider->setProperty( dictionaryEntry, localCopyOfProvidersDictionary );
if ( !result )
{
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoProvider setProperty %s , returned false", this, str);
break;
}
}
else
{
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoProvider MergeDictionaryIntoDictionary(%p,%p) returned false", this, sourceDictionary, providerDictionary);
break;
}
}
else
{
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoProvider setting property %s", this, str);
result = provider->setProperty(dictionaryEntry, dictionaryToMerge->getObject(dictionaryEntry));
if ( !result )
{
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoProvider setProperty %s, returned false", this, str);
break;
}
}
}
iter->release();
}
USBLog(6,"-AppleUSBHub[%p]::MergeDictionaryIntoProvider(%p, %p) result %d", this, provider, dictionaryToMerge, result);
return result;
}
bool
AppleUSBHub::MergeDictionaryIntoDictionary(OSDictionary * parentSourceDictionary, OSDictionary * parentTargetDictionary)
{
OSCollectionIterator* srcIterator = NULL;
OSSymbol* keyObject = NULL ;
OSObject* targetObject = NULL ;
bool result = false;
USBLog(6,"+AppleUSBHub[%p]::MergeDictionaryIntoDictionary(%p => %p)", this, parentSourceDictionary, parentTargetDictionary);
if (!parentSourceDictionary || !parentTargetDictionary)
return false ;
srcIterator = OSCollectionIterator::withCollection(parentSourceDictionary) ;
while (NULL != (keyObject = OSDynamicCast(OSSymbol, srcIterator->getNextObject())))
{
const char * str;
OSDictionary * childSourceDictionary = NULL;
OSDictionary * childTargetDictionary = NULL;
OSObject * childTargetObject = NULL;
str = keyObject->getCStringNoCopy();
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoDictionary merging \"%s\"", this, str);
childTargetObject = parentTargetDictionary->getObject(keyObject);
if ( childTargetObject )
{
childTargetDictionary = OSDynamicCast(OSDictionary, childTargetObject);
if ( childTargetDictionary )
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoDictionary target object %s is a dictionary (%p)", this, str, childTargetDictionary);
}
childSourceDictionary = OSDynamicCast(OSDictionary, parentSourceDictionary->getObject(keyObject));
if ( childSourceDictionary )
{
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoDictionary source dictionary had %s as a dictionary (%p)", this, str, childSourceDictionary);
}
if ( childTargetDictionary && childSourceDictionary)
{
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoDictionary recursing(%p,%p)", this, childSourceDictionary, childTargetDictionary);
result = MergeDictionaryIntoDictionary(childSourceDictionary, childTargetDictionary) ;
if ( !result )
{
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoDictionary recursing (%p,%p) failed", this, childSourceDictionary, childTargetDictionary);
break;
}
}
else
{
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoDictionary setting object %s into dictionary %p", this, str, parentTargetDictionary);
result = parentTargetDictionary->setObject(keyObject, parentSourceDictionary->getObject(keyObject)) ;
if ( !result )
{
USBLog(6,"AppleUSBHub[%p]::MergeDictionaryIntoDictionary setObject %s, returned false", this, str);
break;
}
}
}
srcIterator->release();
USBLog(6,"-AppleUSBHub[%p]::MergeDictionaryIntoDictionary(%p=>(%p) result %d", this, parentSourceDictionary, parentTargetDictionary, result);
return result;
}
IOReturn
AppleUSBHub::EnterTestMode()
{
IOUSBControllerV2 *con;
int currentPort;
AppleUSBHubPort *port;
if (!_hsHub)
return kIOReturnBadArgument;
if (_isRootHub)
{
con = OSDynamicCast(IOUSBControllerV2, _bus);
if (!con)
return kIOReturnBadArgument;
USBLog(1, "AppleUSBHub[%p]::EnterTestMode - root hub", this);
_inTestMode = true;
return con->SetTestMode(kEHCITestMode_Start, 0);
}
_inTestMode = true;
if ( _ports)
{
USBLog(1, "AppleUSBHub[%p]::EnterTestMode - external hub - suspending ports", this);
for (currentPort = 0; currentPort < _hubDescriptor.numPorts; currentPort++)
{
port = _ports[currentPort];
if (port)
{
port->SuspendPort(true);
}
}
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBHub::LeaveTestMode()
{
IOUSBControllerV2 *con;
if (!_hsHub)
return kIOReturnBadArgument;
if (_isRootHub)
{
con = OSDynamicCast(IOUSBControllerV2, _bus);
if (!con)
return kIOReturnBadArgument;
USBLog(1, "AppleUSBHub[%p]::LeaveTestMode - root hub", this);
return con->SetTestMode(kEHCITestMode_End, 0);
}
USBLog(1, "AppleUSBHub[%p]::LeaveTestMode - external hub", this);
retain();
ResetMyPort();
release();
return kIOReturnSuccess;
}
bool
AppleUSBHub::IsHSRootHub()
{
if (_hsHub && _isRootHub)
return true;
else
return false;
}
IOReturn
AppleUSBHub::PutPortIntoTestMode(UInt32 port, UInt32 mode)
{
IOUSBControllerV2 *con;
IOUSBDevRequest request;
if (!_hsHub || !_inTestMode)
return kIOReturnBadArgument;
if (_isRootHub)
{
con = OSDynamicCast(IOUSBControllerV2, _bus);
if (!con)
return kIOReturnBadArgument;
USBLog(1, "AppleUSBHub[%p]::PutPortIntoTestMode - putting root hub port %ld into mode %lx", this, port, mode);
return con->SetTestMode(mode, port);
}
USBLog(1, "AppleUSBHub[%p]::PutPortIntoTestMode - putting external hub port %ld into mode %lx", this, port, mode);
return SetPortFeature(kUSBHubPortTestFeature, (mode << 8) + port);
}
bool
AppleUSBHub::HubAreAllPortsDisconnectedOrSuspended()
{
UInt32 currentPort;
IOUSBHubPortStatus portStatus;
IOReturn kr;
for (currentPort = 1; currentPort <= _hubDescriptor.numPorts; currentPort++)
{
kr = GetPortStatus( &portStatus, currentPort);
if ( kr == kIOReturnSuccess )
{
if ( (portStatus.statusFlags & kHubPortConnection) && !(portStatus.statusFlags & kHubPortSuspend) )
{
USBLog(6, "AppleUSBHub[%p](0x%lx)::HubAreAllPortsDisconnectedOrSuspended - port %ld enabled and not suspended", this, _locationID, currentPort);
return false;
}
}
else
{
USBLog(1,"AppleUSBHub[%p](0x%lx)::HubAreAllPortsDisconnectedOrSuspended GetPortStatus for port %ld returned 0x%x", this, _locationID, currentPort, kr);
return false;
}
}
USBLog(6, "AppleUSBHub[%p](0x%lx)::HubAreAllPortsDisconnectedOrSuspended - YES THEY ARE", this, _locationID);
return true;
}