#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} };
#define errataListLength (sizeof(errataList)/sizeof(ErrataListEntry))
OSDefineMetaClassAndStructors(AppleUSBHub, IOService)
enum
{
kWatchdogTimerPeriod = 30000
};
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;
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, "%s::start Couldn't allocate timer event source", getName());
goto ErrorExit;
}
_gate = IOCommandGate::commandGate(this);
if(!_gate)
{
USBError(1, "%s[%p]::start - unable to create command gate", getName(), this);
goto ErrorExit;
}
_workLoop = getWorkLoop();
if ( !_workLoop )
{
USBError(1, "%s::start Couldn't get provider's workloop", getName());
goto ErrorExit;
}
_workLoop->retain();
if ( _workLoop->addEventSource( _timerSource ) != kIOReturnSuccess )
{
USBError(1, "%s::start Couldn't add timer event source", getName());
goto ErrorExit;
}
if ( _workLoop->addEventSource( _gate ) != kIOReturnSuccess )
{
USBError(1, "%s::start Couldn't add gate event source", getName());
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, "%s::start unable to open provider", getName());
goto ErrorExit;
}
errataProperty = (OSNumber *)getProperty("kStartupDelay");
if ( errataProperty )
{
_startupDelay = errataProperty->unsigned32BitValue();
IOSleep( _startupDelay );
}
_errataBits = GetHubErrataBits();
err = ConfigureHub();
if ( err == kIOReturnSuccess )
{
rootHub = OSDynamicCast(IOUSBRootHubDevice, provider);
if (rootHub)
{
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);
_clearDevZeroLockThread = thread_call_allocate((thread_call_func_t)ClearDevZeroLockForPort, (thread_call_param_t)this);
if ( !_workThread || !_resetPortZeroThread || !_hubDeadCheckThread || !_clearFeatureEndpointHaltThread || !_clearDevZeroLockThread )
{
USBError(1, "%s[%p] could not allocate all thread functions. Aborting start", getName(), this);
goto ErrorExit;
}
locationIDProperty = (OSNumber *) provider->getProperty(kUSBDevicePropertyLocationID);
if ( locationIDProperty )
{
_locationID = locationIDProperty->unsigned32BitValue();
}
USBLog(1, "[%p] USB Generic Hub @ %d (0x%x)", this, _address, _locationID);
_inStartMethod = false;
DecrementOutstandingIO();
err = RearmInterruptRead();
if (err == kIOReturnSuccess)
return true;
}
else
{
USBError(1,"%s[%p]::start Aborting startup: error 0x%x", getName(), 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 != NULL )
{
_workLoop->release();
_workLoop = NULL;
}
_inStartMethod = false;
DecrementOutstandingIO();
return false;
}
void
AppleUSBHub::stop(IOService * provider)
{
if (_buffer)
{
_buffer->release();
_buffer = 0;
}
if(_hubInterface)
{
_hubInterface->close(this);
_hubInterface->release();
_hubInterface = NULL;
}
if (_timerSource)
{
if ( _workLoop )
_workLoop->removeEventSource(_timerSource);
_timerSource->release();
_timerSource = 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 (_clearDevZeroLockThread)
{
thread_call_cancel(_clearDevZeroLockThread);
thread_call_free(_clearDevZeroLockThread);
}
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;
if (_device->GetNumConfigurations() < 1)
{
USBError(1,"%s[%p]::ConfigureHub No hub configurations", getName(), this);
err = kIOReturnNoResources; goto ErrorExit;
}
cd = _device->GetFullConfigurationDescriptor(0);
if (!cd)
{
USBError(1,"%s[%p]::ConfigureHub No config descriptor", getName(), this);
err = kIOUSBConfigNotFound;
goto ErrorExit;
}
err = _device->SetConfiguration(this, cd->bConfigurationValue, false);
if (err)
{
USBError(1,"%s[%p]::ConfigureHub SetConfiguration failed. Error 0x%x", getName(), this, err);
goto ErrorExit;
}
if (cd->bmAttributes & kUSBAtrRemoteWakeup)
{
USBLog(3,"%s[%p]::ConfigureHub Setting kUSBFeatureDeviceRemoteWakeup for Hub device (%p)", getName(), this, _device);
err = _device->SetFeature(kUSBFeatureDeviceRemoteWakeup);
if ( err)
USBError(1,"%s[%p]::ConfigureHub SetFeature(kUSBFeatureDeviceRemoteWakeup) failed. Error 0x%x", getName(), this, err);
}
req.bInterfaceClass = kUSBHubClass;
req.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
req.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
req.bAlternateSetting = kIOUSBFindInterfaceDontCare;
if ((_hubInterface = _device->FindNextInterface(NULL, &req)) == 0)
{
USBError(1,"%s[%p]::ConfigureHub no interface found", getName(), this);
err = kIOUSBInterfaceNotFound;
goto ErrorExit;
}
_hubInterface->retain();
_busPowered = (cd->bmAttributes & kUSBAtrBusPowered) ? TRUE : FALSE; _selfPowered = (cd->bmAttributes & kUSBAtrSelfPowered) ? TRUE : FALSE;
if( !(_busPowered || _selfPowered) )
{
USBError(1,"%s[%p]::ConfigureHub illegal device config - no power", getName(), this);
err = kIOReturnNoPower; goto ErrorExit;
}
if ( (err = GetHubDescriptor(&_hubDescriptor)) )
{
USBError(1,"%s[%p]::ConfigureHub could not get hub descriptor (0x%x)", getName(), this, err);
goto ErrorExit;
}
if(_hubDescriptor.numPorts < 1)
{
USBLog(1,"%s[%p]::ConfigureHub there are no ports on this hub", getName(), this);
}
if(_hubDescriptor.numPorts > 7)
{
USBLog(1,"%s[%p]::ConfigureHub there are an awful lot of ports (%d) on this hub", getName(), this, _hubDescriptor.numPorts);
}
_readBytes = ((_hubDescriptor.numPorts + 1) / 8) + 1;
_buffer = IOBufferMemoryDescriptor::withCapacity(_readBytes, kIODirectionIn);
if (!_hubInterface->open(this))
{
USBError(1," %s[%p]::ConfigureHub could not open hub interface", getName(), 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, "%s[%p]::ConfigureHub - found FS/LS only hub", getName(), this);
break;
case 1:
USBLog(5, "%s[%p]::ConfigureHub - found single TT hub", getName(), this);
v2Bus->AddHSHub(_address, 0);
_hsHub = true;
break;
case 2:
USBLog(5, "%s[%p]::ConfigureHub - found multi TT hub", getName(), this);
_hsHub = true;
if ((err = _hubInterface->SetAlternateInterface(this, 1))) {
USBError(1, "%s[%p]::ConfigureHub - err (%x) setting alt interface", getName(), this, err);
v2Bus->AddHSHub(_address, 0);
}
else
v2Bus->AddHSHub(_address, kUSBHSHubFlagsMultiTT);
_multiTTs = true;
break;
default:
USBError(1, "%s[%p]::ConfigureHub - unknown protocol (%d)", getName(), this, _device->GetProtocol());
break;
}
}
else
{
USBLog(5, "%s[%p]::ConfigureHub - not on a V2 controller", getName(), this);
}
}
IOUSBFindEndpointRequest request;
request.type = kUSBInterrupt;
request.direction = kUSBIn;
_interruptPipe = _hubInterface->FindNextPipe(NULL, &request);
if(!_interruptPipe)
{
USBError(1,"%s[%p]::ConfigureHub could not find interrupt pipe", getName(), this);
err = kIOUSBNotEnoughPipesErr; goto ErrorExit;
}
UnpackPortFlags();
CountCaptivePorts();
err = CheckPortPowerRequirements();
if ( err != kIOReturnSuccess )
{
USBError(1,"%s[%p]::ConfigureHub CheckPortPowerRequirements failed with 0x%x", getName(), this, err);
goto ErrorExit;
}
err = AllocatePortMemory();
if ( err != kIOReturnSuccess )
{
USBError(1,"%s[%p]::ConfigureHub AllocatePortMemory failed with 0x%x", getName(), this, err);
goto ErrorExit;
}
err = StartPorts();
if ( err != kIOReturnSuccess )
{
USBError(1,"%s[%p]::ConfigureHub StartPorts failed with 0x%x", getName(), 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, "%s [%p] Hub claims to need more power (%d > %d) than available", getName(), 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,"%s[%p] Hub attached - Self/Bus powered, power supply good", getName(), this);
}
else
{
USBLog(3,"%s[%p] Hub attached - Self/Bus powered, no external power", getName(), this);
}
}
else
{
if(_selfPowered)
{
if(_selfPowerGood)
{
USBLog(3,"%s[%p] Hub attached - Self powered, power supply good", getName(), this);
}
else
{
USBLog(3,"%s[%p] Hub attached - Self powered, no external power", getName(), this);
}
}
else
{
USBLog(3,"%s[%p] Hub attached - Bus powered", getName(), 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,"%s[%p]: insufficient power to turn on ports", getName(), 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 = _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, "%s [%p]: starting ports (%d)", getName(), 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, "%s [%p]: stopping ports (%d)", getName(), 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;
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,"%s [%p]: hub status = %x/%x", getName(), this, _hubStatus.statusFlags, _hubStatus.changeFlags);
if (_hubStatus.changeFlags & kHubLocalPowerStatusChange)
{
USBLog(3, "%s [%p]: Hub Local Power Status Change detected", getName(), 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);
}
if (_hubStatus.changeFlags & kHubOverCurrentIndicatorChange)
{
USBLog(3, "%s [%p]: Hub OverCurrent Indicator Change detected", getName(), 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);
}
OSBoolean * boolObj = OSDynamicCast( OSBoolean, getProperty("kResetOnPowerStatusChange") );
if ( boolObj && boolObj->isTrue() )
{
err = kIOReturnBusy;
}
} while(false);
if ( err == kIOReturnSuccess )
return true;
else
{
_hubIsDead = TRUE;
retain();
ResetMyPort();
release();
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, "%s[%p]::FatalError 0x%x: %s", getName(), 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,"%s[%p]: GetHubDescriptor w/ type = %X returned error: 0x%x",this, getName(), kUSBHubDescriptorType, err);
request.wValue = 0;
request.wLength = sizeof(IOUSBHubDescriptor);
err = DoDeviceRequest(&request);
}
if (err)
{
USBLog(3, "%s [%p] GetHubDescriptor error = 0x%x", getName(), 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, "%s [%p] GetHubStatus error = 0x%x", getName(), 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, "%s [%p] GetPortState error = 0x%x", getName(), 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, "%s [%p] ClearHubFeature error = 0x%x", getName(), this, err);
}
return(err);
}
IOReturn
AppleUSBHub::GetPortStatus(IOUSBHubPortStatus *status, UInt16 port)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBClass, kUSBOther);
request.bRequest = kUSBRqGetStatus;
request.wValue = 0;
request.wIndex = port;
request.wLength = sizeof(IOUSBHubPortStatus);
request.pData = status;
err = DoDeviceRequest(&request);
if (err)
{
USBLog(3, "%s[%p]::GetPortStatus, error (%x) returned from DoDeviceRequest", getName(), this, err);
}
status->statusFlags = USBToHostWord(status->statusFlags);
status->changeFlags = USBToHostWord(status->changeFlags);
if ( err == kIOReturnSuccess)
{
USBLog( 5, "%s[%p]::GetPortStatus for port %d, status: 0x%8x, change: 0x%8x - returning kIOReturnSuccess", getName(), this, port, status->statusFlags, status->changeFlags);
}
return(err);
}
IOReturn
AppleUSBHub::SetPortFeature(UInt16 feature, UInt16 port)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
USBLog(5, "%s[%p]::SetPortFeature port/feature (%x) - setting", getName(), 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)
{
USBLog(1, "%s[%p]::SetPortFeature got error (%x) to DoDeviceRequest", getName(), this, err);
}
return(err);
}
IOReturn
AppleUSBHub::ClearPortFeature(UInt16 feature, UInt16 port)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
USBLog(5, "%s[%p]::ClearPortFeature port/feature (%x) - clearing", getName(), 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, "%s[%p]::ClearPortFeature got error (%x) to DoDeviceRequest", getName(), this, err);
}
return(err);
}
IOReturn
AppleUSBHub::DoPortAction(UInt32 type, UInt32 portNumber, UInt32 options )
{
AppleUSBHubPort *port;
IOReturn err = kIOReturnSuccess;
USBLog(5,"+%s[%p]::DoPortAction(0x%x) for port (%d), options (0x%x)", getName(), this, type, portNumber, options);
if ( _ports == NULL )
return kIOReturnNoDevice;
port = _ports[portNumber - 1];
if (port)
{
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;
}
}
USBLog(5,"-%s[%p]::DoPortAction(0x%x) for port (%d) returning 0x%x",getName(), 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;
bool timeToGoAway = false;
IOReturn err = kIOReturnSuccess;
switch (status)
{
case kIOReturnOverrun:
USBLog(3, "%s[%p]::InterruptReadHandler kIOReturnOverrun error", getName(), this);
if (!isInactive())
{
_interruptPipe->ClearStall();
IncrementOutstandingIO();
thread_call_enter(_clearFeatureEndpointHaltThread);
}
queueAnother = false;
timeToGoAway = false;
case kIOReturnSuccess:
if ( !_hubIsDead )
{
IncrementOutstandingIO();
thread_call_enter(_workThread);
}
queueAnother = FALSE;
break;
case kIOReturnNotResponding:
USBLog(3, "%s[%p]::InterruptReadHandler error kIOReturnNotResponding", getName(), this);
if ( _hubHasBeenDisconnected || isInactive() )
{
queueAnother = false;
timeToGoAway = true;
}
else
{
USBLog(3, "%s[%p]::InterruptReadHandler Checking to see if hub is still connected", getName(), this);
CallCheckForDeadHub();
queueAnother = false;
}
break;
case kIOReturnAborted:
if (isInactive() || _hubIsDead )
{
USBLog(3, "%s[%p]::InterruptReadHandler error kIOReturnAborted (expected)", getName(), this);
queueAnother = false;
timeToGoAway = true;
}
else
{
USBLog(3, "%s[%p]::InterruptReadHandler error kIOReturnAborted. Try again.", getName(), 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, "%s[%p]::InterruptReadHandler error (0x%x) reading interrupt pipe", getName(), this, status);
if (!isInactive())
{
_interruptPipe->ClearStall();
IncrementOutstandingIO();
thread_call_enter(_clearFeatureEndpointHaltThread);
}
queueAnother = false;
break;
case kIOUSBHighSpeedSplitError:
default:
USBLog(3,"%s[%p]::InterruptReadHandler error 0x%x reading interrupt pipe", getName(), this, status);
if (isInactive())
queueAnother = false;
else
{
_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;
locked = port->GetDevZeroLock();
if ( locked )
{
if ( ((_timeoutFlag & (1 << (currentPort-1))) != 0) && (_portTimeStamp[currentPort-1] == port->GetPortTimeStamp()) )
{
USBLog(1, "%s[%p]::ResetPortZero: - port %d - Releasing devZero lock", getName(), this, currentPort);
_timeoutFlag &= ~( 1<<(currentPort-1));
port->ReleaseDevZeroLock();
}
}
}
}
}
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)
return;
portMask = 2;
portByte = 0;
statusChangedBitmapPtr = (const UInt8*)_buffer->getBytesNoCopy();
if (statusChangedBitmapPtr == NULL)
{
USBError(1, "%s[%p]::ProcessStatusChanged: No interrupt pipe buffer!", getName(), this);
}
else
{
if ( statusChangedBitmapPtr[0] == 0xff)
{
USBLog(5,"%s[%p]::ProcessStatusChanged found (0x%8.8x) in statusChangedBitmap", getName(), this, statusChangedBitmapPtr[0]);
}
else
{
if ((statusChangedBitmapPtr[0] & 1) != 0)
{
hubStatusSuccess = HubStatusChanged();
}
if ( hubStatusSuccess )
{
USBLog(5,"%s[%p]::ProcessStatusChanged found (0x%8.8x) in statusChangedBitmap", getName(), this, statusChangedBitmapPtr[0]);
for (portIndex = 1; portIndex <= _hubDescriptor.numPorts; portIndex++)
{
if ((statusChangedBitmapPtr[portByte] & portMask) != 0)
{
port = _ports[portIndex-1];
USBLog(5,"%s[%p]::ProcessStatusChanged port number %d, calling port(%p)->StatusChanged", getName(), this, portIndex, port);
portSuccess = port->StatusChanged();
if (! portSuccess )
{
USBLog(1,"%s[%p]::ProcessStatusChanged port->StatusChanged() returned false", getName(), this);
}
}
portMask <<= 1;
if (portMask > 0x80)
{
portMask = 1;
portByte++;
}
}
(void) RearmInterruptRead();
}
}
}
}
IOReturn
AppleUSBHub::RearmInterruptRead()
{
IOReturn err = kIOReturnSuccess;
IOUSBCompletion comp;
USBLog(5,"%s[%p]::RearmInterruptRead (%p, %p)", getName(), this, _buffer, _interruptPipe );
IncrementOutstandingIO(); if ( (_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,"%s[%p]::RearmInterruptRead error %x reading interrupt pipe", getName(), 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,"%s[%p] : got kIOUSBMessageHubIsDeviceConnected while isInactive() or _hubIsDead", getName(), this);
err = kIOReturnNoDevice;
break;
}
err = GetPortStatus(&status, * (UInt32 *) argument );
if ( err != kIOReturnSuccess )
{
err = kIOReturnNoDevice;
}
else
{
USBLog(5,"%s[%p]::kIOUSBMessageHubIsDeviceConnected - port %d - status(%8x)/change(%8x)", getName(), this, * (UInt32 *) argument, status.statusFlags, status.changeFlags);
if ( (status.statusFlags & kHubPortConnection) && !(status.changeFlags & kHubPortConnection) )
err = kIOReturnSuccess;
else
err = kIOReturnNoDevice;
}
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,"%s[%p] : Received kIOMessageServiceIsTerminated - ignoring", getName(), this);
break;
case kIOUSBMessagePortHasBeenReset:
if ( isInactive() )
{
USBLog(5,"%s[%p] : got kIOUSBMessagePortHasBeenReset while isInactive() or _hubIsDead", getName(), this);
err = kIOReturnSuccess;
break;
}
if (!_inStartMethod)
{
_inStartMethod = true;
IncrementOutstandingIO(); USBLog(3, "%s[%p] Received kIOUSBMessagePortHasBeenReset -- reconfiguring hub", getName(), this);
if ( _interruptPipe )
{
_interruptPipe->Abort();
_interruptPipe = NULL;
}
if(_hubInterface)
{
_hubInterface->close(this);
_hubInterface->release();
_hubInterface = NULL;
}
err = ConfigureHub();
if ( err )
{
USBLog(3, "%s[%p] Reconfiguring hub returned: 0x%x",getName(), this, err);
}
else
{
USBError(1, "[%p] (Reset) USB Generic Hub @ %d (0x%x)", this, _address, _locationID);
}
_inStartMethod = false;
DecrementOutstandingIO();
if ( err == kIOReturnSuccess)
{
err = RearmInterruptRead();
}
}
break;
case kIOUSBMessagePortHasBeenResumed:
err = kIOReturnSuccess;
break;
default:
break;
}
return err;
}
IOReturn
AppleUSBHub::DoDeviceRequest(IOUSBDevRequest *request)
{
IOReturn err;
if ( _device && !_device->isInactive() && _device->isOpen(this))
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;
me = OSDynamicCast(AppleUSBHub, owner);
if (!me)
return;
if( me->_ports)
for (currentPort = 1; currentPort <= me->_hubDescriptor.numPorts; currentPort++)
{
port = me->_ports[currentPort-1];
if (port)
{
Boolean locked;
locked = port->GetDevZeroLock();
if ( locked )
{
if ( ((me->_timeoutFlag & (1 << (currentPort-1))) != 0) && (me->_portTimeStamp[currentPort-1] == port->GetPortTimeStamp()) )
{
USBError(3,"%s[%p]::TimeoutOccurred error", me->getName(), 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;
}
}
}
if (me->_timerSource && !me->isInactive() )
{
me->_timerSource->setTimeoutMS(kWatchdogTimerPeriod);
}
}
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)
{
_hubIsDead = TRUE;
USBLog(3, "%s[%p]::CheckForDeadHub - Still connected. Resetting port", getName(), this);
retain();
ResetMyPort();
release();
}
else
{
_hubHasBeenDisconnected = TRUE;
USBLog(3, "%s[%p]::CheckForDeadHub - device has been unplugged", getName(), this);
}
}
else
{
USBLog(3,"%s[%p]::CheckForDeadHub -- already resetting hub",getName(), 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 )
{
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, "%s[%p]::ClearFeatureEndpointHalt - DeviceRequest returned: 0x%x, retries = %d", getName(), this, status, retries);
IOSleep(100);
}
else
break;
}
status = RearmInterruptRead();
}
void
AppleUSBHub::ResetMyPort()
{
willTerminate(this, 0);
if ( _interruptPipe )
{
_interruptPipe->Abort();
}
if (_timerSource)
{
_timerSource->cancelTimeout();
}
StopPorts();
_device->ResetDevice();
}
bool
AppleUSBHub::requestTerminate( IOService * provider, IOOptionBits options )
{
USBLog(3, "%s[%p]::requestTerminate isInactive = %d", getName(), this, isInactive());
return super::requestTerminate(provider, options);
}
bool
AppleUSBHub::willTerminate( IOService * provider, IOOptionBits options )
{
int currentPort;
AppleUSBHubPort * port;
IOReturn err;
USBLog(3, "%s[%p]::willTerminate isInactive = %d", getName(), this, isInactive());
if ( _interruptPipe )
{
err = _interruptPipe->Abort();
if ( err != kIOReturnSuccess )
{
USBLog(1, "%s[%p]::willTerminate interruptPipe->Abort returned 0x%x", getName(), this, err);
}
}
if ( _ports)
{
for (currentPort = 1; currentPort <= _hubDescriptor.numPorts; currentPort++)
{
port = _ports[currentPort-1];
if (port)
{
if (port->_devZero)
{
USBLog(1, "%s[%p]::StopPorts - port %d had the dev zero lock", getName(), this, currentPort);
}
thread_call_enter1(_clearDevZeroLockThread, (thread_call_param_t) port);
}
}
}
if (_timerSource)
{
_timerSource->cancelTimeout();
}
return super::willTerminate(provider, options);
}
bool
AppleUSBHub::didTerminate( IOService * provider, IOOptionBits options, bool * defer )
{
USBLog(3, "%s[%p]::didTerminate isInactive = %d", getName(), this, isInactive());
StopPorts();
if (!_outstandingIO)
{
_device->close(this);
}
else
{
_needToClose = true;
}
return super::didTerminate(provider, options, defer);
}
bool
AppleUSBHub::terminate( IOOptionBits options )
{
USBLog(5, "%s[%p]::terminate isInactive = %d", getName(), this, isInactive());
return super::terminate(options);
}
void
AppleUSBHub::free( void )
{
USBLog(5, "%s[%p]::free isInactive = %d", getName(), this, isInactive());
if (_gate)
{
if (_workLoop)
{
(void) _workLoop->removeEventSource(_gate);
_workLoop->release();
_workLoop = NULL;
}
else
{
USBError(1, "%s[%p]::free - have gate, but no valid workloop (%p)!", getName(), this, _workLoop);
}
_gate->release();
_gate = NULL;
}
super::free();
}
bool
AppleUSBHub::terminateClient( IOService * client, IOOptionBits options )
{
USBLog(5, "%s[%p]::terminateClient isInactive = %d", getName(), this, isInactive());
return super::terminateClient(client, options);
}
void
AppleUSBHub::DecrementOutstandingIO(void)
{
if (!_gate)
{
USBLog(2, "%s[%p]::DecrementOutstandingIO isInactive = %d, outstandingIO = %d - no gate", getName(), this, isInactive(), _outstandingIO);
if (!--_outstandingIO && _needToClose)
{
USBLog(3, "%s[%p]::DecrementOutstandingIO isInactive = %d, outstandingIO = %d - closing device", getName(), this, isInactive(), _outstandingIO);
_device->close(this);
}
return;
}
_gate->runAction(ChangeOutstandingIO, (void*)-1);
}
void
AppleUSBHub::IncrementOutstandingIO(void)
{
if (!_gate)
{
USBLog(2, "%s[%p]::IncrementOutstandingIO isInactive = %d, outstandingIO = %d - no gate", getName(), 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, "%s[%p]::ChangeOutstandingIO isInactive = %d, outstandingIO = %d - closing device", me->getName(), me, me->isInactive(), me->_outstandingIO);
me->_device->close(me);
}
break;
default:
USBLog(1, "%s[%p]::ChangeOutstandingIO - invalid direction", me->getName(), me);
}
return kIOReturnSuccess;
}
void
AppleUSBHub::ClearDevZeroLockForPort(OSObject *target, thread_call_param_t thePort)
{
AppleUSBHub * me = OSDynamicCast(AppleUSBHub, target);
AppleUSBHubPort * port = (AppleUSBHubPort *) thePort;
if (!me || !port)
return;
me->retain();
port->willTerminate(me, 0);
me->release();
}
bool
AppleUSBHub::MergeDictionaryIntoProvider(IOService * provider, OSDictionary * dictionaryToMerge)
{
const OSSymbol * dictionaryEntry = NULL;
OSCollectionIterator * iter = NULL;
bool result = false;
USBLog(3,"+%s[%p]::MergeDictionary(%p)IntoProvider(%p)", getName(), 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(3,"%s[%p]::MergeDictionaryIntoProvider merging \"%s\"", getName(), this, str);
providerProperty = provider->getProperty(dictionaryEntry);
if ( providerProperty )
{
USBLog(3,"%s[%p]::MergeDictionaryIntoProvider provider already had property %s", getName(), this, str);
providerDictionary = OSDynamicCast(OSDictionary, providerProperty);
if ( providerDictionary )
{
USBLog(3,"%s[%p]::MergeDictionaryIntoProvider provider's %s is also a dictionary (%p)", getName(), this, str, providerDictionary);
}
}
sourceDictionary = OSDynamicCast(OSDictionary, dictionaryToMerge->getObject(dictionaryEntry));
if ( sourceDictionary )
{
USBLog(3,"%s[%p]::MergeDictionaryIntoProvider source dictionary had %s as a dictionary (%p)", getName(), this, str, sourceDictionary);
}
if ( providerDictionary && sourceDictionary )
{
OSDictionary * localCopyOfProvidersDictionary;
UInt32 providerSize;
UInt32 providerSizeAfterMerge;
localCopyOfProvidersDictionary = OSDictionary::withDictionary( providerDictionary, 0);
if ( localCopyOfProvidersDictionary == NULL )
{
USBError(1,"%s[%p]::MergeDictionaryIntoProvider could not copy our provider's dictionary",getName(), this);
break;
}
providerSize = providerDictionary->getCapacity();
USBLog(3,"%s[%p]::MergeDictionaryIntoProvider Created a local copy(%p) of dictionary (%p), size %d", getName(), this, localCopyOfProvidersDictionary, providerDictionary, providerSize);
USBLog(3,"%s[%p]::MergeDictionaryIntoProvider need to merge a dictionary (%s)", getName(), this, str);
result = MergeDictionaryIntoDictionary( sourceDictionary, localCopyOfProvidersDictionary);
if ( result )
{
providerSizeAfterMerge = providerDictionary->getCapacity();
if ( providerSizeAfterMerge != providerSize )
{
USBError(1,"%s[%p]::MergeDictionaryIntoProvider our provider's dictionary size changed (%d,%d)",getName(), this, providerSize, providerSizeAfterMerge);
}
USBLog(3,"%s[%p]::MergeDictionaryIntoProvider setting property %s from merged dictionary (%p)", getName(), this, str, providerDictionary);
result = provider->setProperty( dictionaryEntry, localCopyOfProvidersDictionary );
if ( !result )
{
USBLog(3,"%s[%p]::MergeDictionaryIntoProvider setProperty %s , returned false", getName(), this, str);
break;
}
}
else
{
USBLog(3,"%s[%p]::MergeDictionaryIntoProvider MergeDictionaryIntoDictionary(%p,%p) returned false", getName(), this, sourceDictionary, providerDictionary);
break;
}
}
else
{
USBLog(3,"%s[%p]::MergeDictionaryIntoProvider setting property %s", getName(), this, str);
result = provider->setProperty(dictionaryEntry, dictionaryToMerge->getObject(dictionaryEntry));
if ( !result )
{
USBLog(3,"%s[%p]::MergeDictionaryIntoProvider setProperty %s, returned false", getName(), this, str);
break;
}
}
}
iter->release();
}
USBLog(3,"-%s[%p]::MergeDictionaryIntoProvider(%p, %p) result %d", getName(), 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(3,"+%s[%p]::MergeDictionaryIntoDictionary(%p => %p)", getName(), 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(3,"%s[%p]::MergeDictionaryIntoDictionary merging \"%s\"", getName(), this, str);
childTargetObject = parentTargetDictionary->getObject(keyObject);
if ( childTargetObject )
{
childTargetDictionary = OSDynamicCast(OSDictionary, childTargetObject);
if ( childTargetDictionary )
USBLog(3,"%s[%p]::MergeDictionaryIntoDictionary target object %s is a dictionary (%p)", getName(), this, str, childTargetDictionary);
}
childSourceDictionary = OSDynamicCast(OSDictionary, parentSourceDictionary->getObject(keyObject));
if ( childSourceDictionary )
{
USBLog(3,"%s[%p]::MergeDictionaryIntoDictionary source dictionary had %s as a dictionary (%p)", getName(), this, str, childSourceDictionary);
}
if ( childTargetDictionary && childSourceDictionary)
{
USBLog(3,"%s[%p]::MergeDictionaryIntoDictionary recursing(%p,%p)", getName(), this, childSourceDictionary, childTargetDictionary);
result = MergeDictionaryIntoDictionary(childSourceDictionary, childTargetDictionary) ;
if ( !result )
{
USBLog(3,"%s[%p]::MergeDictionaryIntoDictionary recursing (%p,%p) failed", getName(), this, childSourceDictionary, childTargetDictionary);
break;
}
}
else
{
USBLog(3,"%s[%p]::MergeDictionaryIntoDictionary setting object %s into dictionary %p", getName(), this, str, parentTargetDictionary);
result = parentTargetDictionary->setObject(keyObject, parentSourceDictionary->getObject(keyObject)) ;
if ( !result )
{
USBLog(3,"%s[%p]::MergeDictionaryIntoDictionary setObject %s, returned false", getName(), this, str);
break;
}
}
}
srcIterator->release();
USBLog(3,"-%s[%p]::MergeDictionaryIntoDictionary(%p=>(%p) result %d", getName(), this, parentSourceDictionary, parentTargetDictionary, result);
return result;
}