extern "C" {
#include <kern/thread_call.h>
}
#include <libkern/OSByteOrder.h>
#include <libkern/c++/OSDictionary.h>
#include <libkern/c++/OSData.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOTimerEventSource.h>
#include <IOKit/usb/IOUSBController.h>
#include <IOKit/usb/IOUSBDevice.h>
#include <IOKit/usb/IOUSBInterface.h>
#include <IOKit/usb/IOUSBLog.h>
#include <IOKit/usb/IOUSBPipe.h>
#include <IOKit/usb/IOUSBRootHubDevice.h>
#include <IOKit/usb/USB.h>
#include <UserNotification/KUNCUserNotifications.h>
#define super IOUSBNub
#define _portNumber _expansionData->_portNumber
#define _doPortResetThread _expansionData->_doPortResetThread
#define _usbPlaneParent _expansionData->_usbPlaneParent
#define _portResetThreadActive _expansionData->_portResetThreadActive
#define _allowConfigValueOfZero _expansionData->_allowConfigValueOfZero
#define _doPortSuspendThread _expansionData->_doPortSuspendThread
#define _portSuspendThreadActive _expansionData->_portSuspendThreadActive
#define _doPortReEnumerateThread _expansionData->_doPortReEnumerateThread
#define _resetInProgress _expansionData->_resetInProgress
#define _portHasBeenReset _expansionData->_portHasBeenReset
#define _deviceterminating _expansionData->_deviceterminating
#define _getConfigLock _expansionData->_getConfigLock
#define _workLoop _expansionData->_workLoop
#define _notifierHandlerTimer _expansionData->_notifierHandlerTimer
#define _notificationType _expansionData->_notificationType
#define _suspendInProgress _expansionData->_suspendInProgress
#define _portHasBeenSuspended _expansionData->_portHasBeenSuspended
#define _addExtraResetTime _expansionData->_addExtraResetTime
#define kNotifyTimerDelay 30000 // in milliseconds = 30 seconds
#define kUserLoginDelay 20000 // in milliseconds = 20 seconds
class IOUSBInterfaceIterator : public OSIterator
{
OSDeclareDefaultStructors(IOUSBInterfaceIterator)
protected:
IOUSBFindInterfaceRequest fRequest;
IOUSBDevice * fDevice;
IOUSBInterface * fCurrent;
virtual void free();
public:
virtual bool init(IOUSBDevice *dev, IOUSBFindInterfaceRequest *reqIn);
virtual void reset();
virtual bool isValid();
virtual OSObject * getNextObject();
};
OSDefineMetaClassAndStructors(IOUSBInterfaceIterator, OSIterator)
bool
IOUSBInterfaceIterator::init(IOUSBDevice *dev, IOUSBFindInterfaceRequest *reqIn)
{
if(!OSIterator::init())
return false;
fDevice = dev;
fDevice->retain();
fRequest = *reqIn;
fCurrent = NULL;
return true;
}
void
IOUSBInterfaceIterator::free()
{
if(fCurrent)
fCurrent->release();
fDevice->release();
OSIterator::free();
}
void
IOUSBInterfaceIterator::reset()
{
if(fCurrent)
fCurrent->release();
fCurrent = NULL;
}
bool
IOUSBInterfaceIterator::isValid()
{
return true;
}
OSObject *
IOUSBInterfaceIterator::getNextObject()
{
IOUSBInterface *next;
next = fDevice->FindNextInterface(fCurrent, &fRequest);
if (next)
next->retain();
if(fCurrent)
fCurrent->release();
fCurrent = next;
return next;
}
OSDefineMetaClassAndStructors(IOUSBDevice, IOUSBNub)
IOUSBDevice *
IOUSBDevice::NewDevice()
{
return new IOUSBDevice;
}
void
IOUSBDevice::SetProperties()
{
char location [8];
const struct IORegistryPlane * usbPlane;
OSNumber * port = (OSNumber *) getProperty("PortNum");
if ( port )
{
_portNumber = port->unsigned32BitValue();
}
usbPlane = getPlane(kIOUSBPlane);
if ( usbPlane )
{
_usbPlaneParent = OSDynamicCast( IOUSBDevice, getParentEntry( usbPlane ));
_usbPlaneParent->retain();
}
if ( _usbPlaneParent )
{
OSNumber * locationID = (OSNumber *) _usbPlaneParent->getProperty(kUSBDevicePropertyLocationID);
if ( locationID )
{
UInt32 childLocationID = GetChildLocationID( locationID->unsigned32BitValue(), _portNumber );
setProperty(kUSBDevicePropertyLocationID, childLocationID,32);
sprintf(location, "%x", (int) childLocationID);
setLocation(location);
}
}
}
UInt32
IOUSBDevice::GetChildLocationID(UInt32 parentLocationID, int port)
{
SInt32 shift;
UInt32 location = parentLocationID;
for ( shift = 20; shift >= 0; shift -= 4)
{
if ((location & (0x0f << shift)) == 0)
{
location |= (port & 0x0f) << shift;
break;
}
}
return location;
}
bool
IOUSBDevice::init(USBDeviceAddress deviceAddress, UInt32 powerAvailable, UInt8 speed, UInt8 maxPacketSize)
{
if(!super::init())
{
USBLog(3,"%s[%p]::init super->init failed", getName(), this);
return false;
}
if (!_expansionData)
{
_expansionData = (ExpansionData *)IOMalloc(sizeof(ExpansionData));
if (!_expansionData)
return false;
bzero(_expansionData, sizeof(ExpansionData));
}
_getConfigLock = IORecursiveLockAlloc();
if (!_getConfigLock)
{
IOFree(_expansionData, sizeof(ExpansionData));
USBError(1, "%s[%p]::init - Error allocating getConfigLock", getName(), this);
return false;
}
_address = deviceAddress;
_descriptor.bMaxPacketSize0 = maxPacketSize;
_busPowerAvailable = powerAvailable;
_speed = speed;
_portResetThreadActive = false;
_allowConfigValueOfZero = false;
_portHasBeenReset = false;
_deviceterminating = false;
USBLog(5,"%s @ %d (%ldmA available, %s speed)", getName(), _address,_busPowerAvailable*2, (_speed == kUSBDeviceSpeedLow) ? "low" : ((_speed == kUSBDeviceSpeedFull) ? "full" : "high") );
return true;
}
bool
IOUSBDevice::finalize(IOOptionBits options)
{
USBLog(5,"%s[%p]::finalize",getName(), this);
if(_pipeZero)
{
_pipeZero->Abort();
_pipeZero->ClosePipe();
_pipeZero->release();
_pipeZero = NULL;
}
_currentConfigValue = 0;
if (_doPortResetThread)
{
thread_call_cancel(_doPortResetThread);
thread_call_free(_doPortResetThread);
}
if (_doPortSuspendThread)
{
thread_call_cancel(_doPortSuspendThread);
thread_call_free(_doPortSuspendThread);
}
if ( _usbPlaneParent )
_usbPlaneParent->release();
return(super::finalize(options));
}
void
IOUSBDevice::free()
{
if(_configList)
{
int i;
for(i=0; i<_descriptor.bNumConfigurations; i++)
if(_configList[i])
{
_configList[i]->release();
_configList[i] = NULL;
}
IODelete(_configList, IOBufferMemoryDescriptor*, _descriptor.bNumConfigurations);
_configList = NULL;
}
_currentConfigValue = 0;
if (_expansionData)
{
if ( _getConfigLock )
IORecursiveLockFree(_getConfigLock);
if (_workLoop)
{
_workLoop->release();
_workLoop = NULL;
}
IOFree(_expansionData, sizeof(ExpansionData));
}
super::free();
}
bool
IOUSBDevice::start( IOService * provider )
{
IOReturn err;
char name[128];
UInt32 delay = 30;
UInt32 retries = 4;
bool allowNumConfigsOfZero = false;
AbsoluteTime currentTime;
UInt64 elapsedTime;
if( !super::start(provider))
return false;
if (_controller == NULL)
return false;
_endpointZero.bLength = sizeof(_endpointZero);
_endpointZero.bDescriptorType = kUSBEndpointDesc;
_endpointZero.bEndpointAddress = 0;
_endpointZero.bmAttributes = kUSBControl;
_endpointZero.wMaxPacketSize = HostToUSBWord(_descriptor.bMaxPacketSize0);
_endpointZero.bInterval = 0;
_pipeZero = IOUSBPipe::ToEndpoint(&_endpointZero, this, _controller);
OSBoolean * boolObj = OSDynamicCast( OSBoolean, getProperty(kAllowNumConfigsOfZero) );
if ( boolObj && boolObj->isTrue() )
allowNumConfigsOfZero = true;
do
{
bzero(&_descriptor,sizeof(_descriptor));
err = GetDeviceDescriptor(&_descriptor, sizeof(_descriptor));
if ( err == kIOReturnOverrun )
{
err = kIOReturnSuccess;
}
if ( (err != kIOReturnSuccess) || (_descriptor.bcdUSB == 0) )
{
USBLog(3, "IOUSBDevice::start, GetDeviceDescriptor -- retrying. err = 0x%x", err);
if ( err == kIOReturnSuccess)
err = kIOUSBPipeStalled;
if ( retries == 2)
delay = 3;
else if ( retries == 1 )
delay = 30;
IOSleep( delay );
retries--;
}
else
{
USBLog(6,"Device Descriptor Dump");
USBLog(6,"\tbLength %d",_descriptor.bLength);
USBLog(6,"\tbDescriptorType %d",_descriptor.bDescriptorType);
USBLog(6,"\tbcdUSB %d", USBToHostWord(_descriptor.bcdUSB));
USBLog(6,"\tbDeviceClass %d", _descriptor.bDeviceClass);
USBLog(6,"\tbDeviceSubClass %d", _descriptor.bDeviceSubClass);
USBLog(6,"\tbDeviceProtocol %d", _descriptor.bDeviceProtocol);
USBLog(6,"\tbMaxPacketSize0 %d", _descriptor.bMaxPacketSize0);
USBLog(6,"\tidVendor %d", USBToHostWord(_descriptor.idVendor));
USBLog(6,"\tidProduct %d", USBToHostWord(_descriptor.idProduct));
USBLog(6,"\tbcdDevice %d", USBToHostWord(_descriptor.bcdDevice));
USBLog(6,"\tiManufacturer %d ", _descriptor.iManufacturer);
USBLog(6,"\tiProduct %d ", _descriptor.iProduct);
USBLog(6,"\tiSerialNumber %d", _descriptor.iSerialNumber);
USBLog(6,"\tbNumConfigurations %d", _descriptor.bNumConfigurations);
}
}
while ( err && retries > 0 );
if ( err )
{
USBLog(3,"%s[%p]::start Couldn't get full device descriptor (0x%x)",getName(),this, err);
return false;
}
if(_descriptor.bNumConfigurations || allowNumConfigsOfZero)
{
_configList = IONew(IOBufferMemoryDescriptor*, _descriptor.bNumConfigurations);
if(!_configList)
return false;
bzero(_configList, sizeof(IOBufferMemoryDescriptor*) * _descriptor.bNumConfigurations);
}
else
{
USBLog(3,"%s[%p]::start USB Device specified bNumConfigurations of 0, which is not legal", getName(), this);
}
if(_descriptor.iProduct)
{
err = GetStringDescriptor(_descriptor.iProduct, name, sizeof(name));
if(err == kIOReturnSuccess)
{
if ( name[0] != 0 )
setName(name);
setProperty("USB Product Name", name);
}
}
else
{
switch ( _descriptor.bDeviceClass )
{
case (kUSBCompositeClass): setName("IOUSBCompositeDevice"); break;
case (kUSBAudioClass): setName("IOUSBAudioDevice"); break;
case (kUSBCommClass): setName("IOUSBCommunicationsDevice"); break;
case (kUSBHIDClass): setName("IOIUSBHIDDevice"); break;
case (kUSBDisplayClass): setName("IOUSBDisplayDevice"); break;
case (kUSBPrintingClass): setName("IOUSBPrintingDevice"); break;
case (kUSBMassStorageClass): setName("IOUSBMassStorageDevice"); break;
case (kUSBHubClass): setName("IOUSBHubDevice"); break;
case (kUSBDataClass): setName("IOUSBDataDevice"); break;
case (kUSBVendorSpecificClass): setName("IOUSBVendorSpecificDevice"); break;
}
}
if(_descriptor.iManufacturer)
{
err = GetStringDescriptor(_descriptor.iManufacturer, name, sizeof(name));
if(err == kIOReturnSuccess)
{
setProperty("USB Vendor Name", name);
}
}
if(_descriptor.iSerialNumber)
{
err = GetStringDescriptor(_descriptor.iSerialNumber, name, sizeof(name));
if(err == kIOReturnSuccess)
{
setProperty("USB Serial Number", name);
}
}
setProperty(kUSBDeviceClass, (unsigned long long)_descriptor.bDeviceClass, (sizeof(_descriptor.bDeviceClass) * 8));
setProperty(kUSBDeviceSubClass, (unsigned long long)_descriptor.bDeviceSubClass, (sizeof(_descriptor.bDeviceSubClass) * 8));
setProperty(kUSBDeviceProtocol, (unsigned long long)_descriptor.bDeviceProtocol, (sizeof(_descriptor.bDeviceProtocol) * 8));
setProperty(kUSBDeviceMaxPacketSize, (unsigned long long)_descriptor.bMaxPacketSize0, (sizeof(_descriptor.bMaxPacketSize0) * 8));
setProperty(kUSBVendorID, (unsigned long long) USBToHostWord(_descriptor.idVendor), (sizeof(_descriptor.idVendor) * 8));
setProperty(kUSBProductID, (unsigned long long) USBToHostWord(_descriptor.idProduct), (sizeof(_descriptor.idProduct) * 8));
setProperty(kUSBDeviceReleaseNumber, (unsigned long long) USBToHostWord(_descriptor.bcdDevice), (sizeof(_descriptor.bcdDevice) * 8));
setProperty(kUSBManufacturerStringIndex, (unsigned long long) _descriptor.iManufacturer, (sizeof(_descriptor.iManufacturer) * 8));
setProperty(kUSBProductStringIndex, (unsigned long long) _descriptor.iProduct, (sizeof(_descriptor.iProduct) * 8));
setProperty(kUSBSerialNumberStringIndex, (unsigned long long) _descriptor.iSerialNumber, (sizeof(_descriptor.iSerialNumber) * 8));
setProperty(kUSBDeviceNumConfigs, (unsigned long long) _descriptor.bNumConfigurations, (sizeof(_descriptor.bNumConfigurations) * 8));
setProperty(kUSBDevicePropertySpeed, (unsigned long long) _speed, (sizeof(_speed) * 8));
setProperty(kUSBDevicePropertyBusPowerAvailable, (unsigned long long) _busPowerAvailable, (sizeof(_busPowerAvailable) * 8));
setProperty(kUSBDevicePropertyAddress, (unsigned long long) _address, (sizeof(_address) * 8));
clock_get_uptime(¤tTime);
absolutetime_to_nanoseconds(currentTime, &elapsedTime);
setProperty("sessionID", elapsedTime, 64);
_doPortResetThread = thread_call_allocate((thread_call_func_t)ProcessPortResetEntry, (thread_call_param_t)this);
_doPortSuspendThread = thread_call_allocate((thread_call_func_t)ProcessPortSuspendEntry, (thread_call_param_t)this);
_doPortReEnumerateThread = thread_call_allocate((thread_call_func_t)ProcessPortReEnumerateEntry, (thread_call_param_t)this);
_interfaceList = NULL;
_currentConfigValue = 0; _numInterfaces = 0;
_notifierHandlerTimer = IOTimerEventSource::timerEventSource(this, (IOTimerEventSource::Action) DisplayUserNotificationForDeviceEntry);
if ( _notifierHandlerTimer == NULL )
{
USBError(1, "%s::start Couldn't allocate timer event source", getName());
goto ErrorExit;
}
_workLoop = getWorkLoop();
if ( !_workLoop )
{
USBError(1, "%s::start Couldn't get provider's workloop", getName());
goto ErrorExit;
}
_workLoop->retain();
if ( _workLoop->addEventSource( _notifierHandlerTimer ) != kIOReturnSuccess )
{
USBError(1, "%s::start Couldn't add timer event source", getName());
goto ErrorExit;
}
return true;
ErrorExit:
if ( _notifierHandlerTimer )
{
if ( _workLoop )
_workLoop->removeEventSource(_notifierHandlerTimer);
_notifierHandlerTimer->release();
_notifierHandlerTimer = NULL;
}
if ( _workLoop != NULL )
{
_workLoop->release();
_workLoop = NULL;
}
return false;
}
bool
IOUSBDevice::attach(IOService *provider)
{
if( !super::attach(provider))
return (false);
if(_controller == NULL)
_controller = OSDynamicCast(IOUSBController, provider);
if(_controller == NULL)
return false;
return true;
}
IOReturn
IOUSBDevice::ResetDevice()
{
UInt32 retries = 0;
if ( _resetInProgress )
{
USBLog(5, "%s[%p] ResetDevice(%d) while in progress", getName(), this, _portNumber );
return kIOReturnNotPermitted;
}
_resetInProgress = true;
if ( isInactive() )
{
USBLog(1, "%s[%p]::ResetDevice - while terminating!", getName(), this);
return kIOReturnNotResponding;
}
retain();
_portHasBeenReset = false;
USBLog(5, "+%s[%p] ResetDevice for port %d", getName(), this, _portNumber );
thread_call_enter( _doPortResetThread );
while ( !_portHasBeenReset && retries < 100 )
{
IOSleep(100);
if ( isInactive() )
{
USBLog(3, "+%s[%p] isInactive() while waiting for reset to finish", getName(), this );
break;
}
retries++;
}
USBLog(5, "-%s[%p] ResetDevice for port %d", getName(), this, _portNumber );
_resetInProgress = false;
release();
return kIOReturnSuccess;
}
const IOUSBConfigurationDescriptor *
IOUSBDevice::FindConfig(UInt8 configValue, UInt8 *configIndex)
{
int i;
const IOUSBConfigurationDescriptor *cd = NULL;
USBLog(6, "%s[%p]:FindConfig (%d)",getName(), this, configValue);
for(i = 0; i < _descriptor.bNumConfigurations; i++)
{
cd = GetFullConfigurationDescriptor(i);
if(!cd)
continue;
if(cd->bConfigurationValue == configValue)
break;
}
if(cd && configIndex)
*configIndex = i;
return cd;
}
static IOUSBDescriptorHeader *
NextDescriptor(const void *desc)
{
const UInt8 *next = (const UInt8 *)desc;
UInt8 length = next[0];
next = &next[length];
return((IOUSBDescriptorHeader *)next);
}
const IOUSBDescriptorHeader*
IOUSBDevice::FindNextDescriptor(const void *cur, UInt8 descType)
{
IOUSBDescriptorHeader *hdr;
UInt8 configIndex;
IOUSBConfigurationDescriptor *curConfDesc;
UInt16 curConfLength;
UInt8 curConfig;
if (!_configList)
return NULL;
if (!_currentConfigValue)
{
GetConfiguration(&curConfig);
if (!_currentConfigValue && !_allowConfigValueOfZero)
return NULL;
}
curConfDesc = (IOUSBConfigurationDescriptor *)FindConfig(_currentConfigValue, &configIndex);
if (!curConfDesc)
return NULL;
if (!_configList[configIndex]) return NULL;
curConfLength = _configList[configIndex]->getLength();
if (!cur)
hdr = (IOUSBDescriptorHeader*)curConfDesc;
else
{
if ((cur < curConfDesc) || (((int)cur - (int)curConfDesc) >= curConfLength))
{
return NULL;
}
hdr = (IOUSBDescriptorHeader *)cur;
}
do
{
IOUSBDescriptorHeader *lasthdr = hdr;
hdr = NextDescriptor(hdr);
if (lasthdr == hdr)
{
return NULL;
}
if(((int)hdr - (int)curConfDesc) >= curConfLength)
{
return NULL;
}
if(descType == 0)
{
return hdr; }
if(hdr->bDescriptorType == descType)
{
return hdr;
}
} while(true);
}
IOReturn
IOUSBDevice::FindNextInterfaceDescriptor(const IOUSBConfigurationDescriptor *configDescIn,
const IOUSBInterfaceDescriptor *intfDesc,
const IOUSBFindInterfaceRequest *request,
IOUSBInterfaceDescriptor **descOut)
{
IOUSBConfigurationDescriptor *configDesc = (IOUSBConfigurationDescriptor *)configDescIn;
IOUSBInterfaceDescriptor *interface, *end;
if (!configDesc && _currentConfigValue)
configDesc = (IOUSBConfigurationDescriptor*)FindConfig(_currentConfigValue, NULL);
if (!configDesc || (configDesc->bDescriptorType != kUSBConfDesc))
return kIOReturnBadArgument;
end = (IOUSBInterfaceDescriptor *)(((UInt8*)configDesc) + USBToHostWord(configDesc->wTotalLength));
if (intfDesc != NULL)
{
if (((void*)intfDesc < (void*)configDesc) || (intfDesc->bDescriptorType != kUSBInterfaceDesc))
return kIOReturnBadArgument;
interface = (IOUSBInterfaceDescriptor *)NextDescriptor(intfDesc);
}
else
interface = (IOUSBInterfaceDescriptor *)NextDescriptor(configDesc);
while (interface < end)
{
if (interface->bDescriptorType == kUSBInterfaceDesc)
{
if (((request->bInterfaceClass == kIOUSBFindInterfaceDontCare) || (request->bInterfaceClass == interface->bInterfaceClass)) &&
((request->bInterfaceSubClass == kIOUSBFindInterfaceDontCare) || (request->bInterfaceSubClass == interface->bInterfaceSubClass)) &&
((request->bInterfaceProtocol == kIOUSBFindInterfaceDontCare) || (request->bInterfaceProtocol == interface->bInterfaceProtocol)) &&
((request->bAlternateSetting == kIOUSBFindInterfaceDontCare) || (request->bAlternateSetting == interface->bAlternateSetting)))
{
*descOut = interface;
return kIOReturnSuccess;
}
}
interface = (IOUSBInterfaceDescriptor *)NextDescriptor(interface);
}
return kIOUSBInterfaceNotFound;
}
IOUSBInterface *
IOUSBDevice::FindNextInterface(IOUSBInterface *current, IOUSBFindInterfaceRequest *request)
{
IOUSBInterfaceDescriptor *id = NULL;
IOUSBInterface *intf = NULL;
if (current)
{
if (!OSDynamicCast(IOUSBInterface, current))
return NULL;
while (true)
{
if (FindNextInterfaceDescriptor(NULL, id, request, &id) != kIOReturnSuccess)
return NULL;
if (GetInterface(id) == current)
break;
}
}
while (!intf)
{
if (FindNextInterfaceDescriptor(NULL, id, request, &id) != kIOReturnSuccess)
return NULL;
intf = GetInterface(id);
}
return intf;
}
OSIterator *
IOUSBDevice::CreateInterfaceIterator(IOUSBFindInterfaceRequest *request)
{
IOUSBInterfaceIterator *iter = new IOUSBInterfaceIterator;
if(!iter)
return NULL;
if(!iter->init(this, request))
{
iter->release();
iter = NULL;
}
return iter;
}
const IOUSBConfigurationDescriptor*
IOUSBDevice::GetFullConfigurationDescriptor(UInt8 index)
{
IOReturn err;
IOBufferMemoryDescriptor * localConfigPointer = NULL;
if (!_configList || (index >= _descriptor.bNumConfigurations))
return NULL;
USBLog(7, "%s[%p]::GetFullConfigurationDescriptor - Index (%x) - about to obtain lock", getName(), this, index);
IORecursiveLockLock(_getConfigLock);
USBLog(7, "%s[%p]::GetFullConfigurationDescriptor - Index (%x) - lock obtained", getName(), this, index);
if(_configList[index] == NULL)
{
int len;
IOUSBConfigurationDescHeader temp;
UInt16 idVendor = USBToHostWord(_descriptor.idVendor);
UInt16 idProduct = USBToHostWord(_descriptor.idProduct);
if ( ((idVendor == 0x3f0) && (idProduct == 0x1001)) || ((idVendor == 0x4c5) && (idProduct == 0x1040)) )
{
USBLog(3, "%s[%p]::GetFullConfigurationDescriptor - assuming config desc length of 39", getName(), this);
len = 39;
}
else
{
temp.bLength = 0;
temp.bDescriptorType = 0;
temp.wTotalLength = 0;
USBLog(5, "%s[%p]::GetFullConfigurationDescriptor - Index (%x) - getting first %d bytes of config descriptor", getName(), this, index, sizeof(temp));
err = GetConfigDescriptor(index, &temp, sizeof(temp));
if ( err != kIOReturnSuccess)
{
IOUSBConfigurationDescriptor confDesc;
bzero( &confDesc, 9);
USBLog(5, "%s[%p]::GetFullConfigurationDescriptor - Index (%x) - Got error (0x%x), trying first %d bytes of config descriptor", getName(), this, index, err, 9);
err = GetConfigDescriptor(index, &confDesc, 9);
if ( (kIOReturnSuccess != err) && ( kIOReturnOverrun != err ) )
{
USBError(1, "%s[%p]::GetFullConfigurationDescriptor - Error (%x) getting first %d bytes of config descriptor", getName(), this, err, 9);
IORecursiveLockUnlock(_getConfigLock);
return NULL;
}
USBError(1, "USB Device %s is violating Section 9.3.5 of the USB Specification -- Error in GetConfigDescriptor( wLength = 4)", getName());
if ( kIOReturnOverrun == err )
{
if ( !((confDesc.bLength == 9) && (confDesc.bDescriptorType == kUSBConfDesc) && (confDesc.wTotalLength != 0)) )
{
USBError(1, "%s[%p]::GetFullConfigurationDescriptor - Overrun error and data returned is not correct (%d, %d, %d)", getName(), this, temp.bLength, temp.bDescriptorType, USBToHostWord(temp.wTotalLength));
IORecursiveLockUnlock(_getConfigLock);
return NULL;
}
USBError(1, "USB Device %s is violating Section 9.3.5 of the USB Specification -- Error in GetConfigDescriptor( wLength = 9)", getName());
}
len = USBToHostWord(confDesc.wTotalLength);
}
else
{
len = USBToHostWord(temp.wTotalLength);
}
}
localConfigPointer = IOBufferMemoryDescriptor::withCapacity(len, kIODirectionIn);
if(!localConfigPointer)
{
USBError(1, "%s[%p]::GetFullConfigurationDescriptor - unable to get memory buffer (capacity requested: %d)", getName(), this, len);
IORecursiveLockUnlock(_getConfigLock);
return NULL;
}
USBLog(5, "%s[%p]::GetFullConfigurationDescriptor - Index (%x) - getting full %d bytes of config descriptor", getName(), this, index, len);
err = GetConfigDescriptor(index, localConfigPointer->getBytesNoCopy(), len);
if (err)
{
USBError(1, "%s[%p]::GetFullConfigurationDescriptor - Error (%x) getting full %d bytes of config descriptor", getName(), this, err, len);
if ( localConfigPointer )
{
localConfigPointer->release();
localConfigPointer = NULL;
}
IORecursiveLockUnlock(_getConfigLock);
return NULL;
}
else
{
if ( _configList[index] != NULL )
localConfigPointer->release();
else
_configList[index] = localConfigPointer;
}
}
USBLog(7, "%s[%p]::GetFullConfigurationDescriptor - Index (%x) - about to release lock", getName(), this, index);
IORecursiveLockUnlock(_getConfigLock);
return (IOUSBConfigurationDescriptor *)_configList[index]->getBytesNoCopy();
}
IOReturn
IOUSBDevice::GetDeviceDescriptor(IOUSBDeviceDescriptor *desc, UInt32 size)
{
IOUSBDevRequest request;
IOReturn err;
USBLog(5, "%s[%p]::GetDeviceDescriptor (size %d)", getName(), this, size);
if (!desc)
return kIOReturnBadArgument;
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
request.bRequest = kUSBRqGetDescriptor;
request.wValue = kUSBDeviceDesc << 8;
request.wIndex = 0;
request.wLength = size;
request.pData = desc;
err = DeviceRequest(&request, 5000, 0);
if (err)
{
USBError(1,"%s[%p]: Error (0x%x) getting device device descriptor", getName(), this, err);
}
return err;
}
IOReturn
IOUSBDevice::GetConfigDescriptor(UInt8 configIndex, void *desc, UInt32 len)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
USBLog(5, "%s[%p]::GetConfigDescriptor (length: %d)", getName(), this, len);
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
request.bRequest = kUSBRqGetDescriptor;
request.wValue = (kUSBConfDesc << 8) + configIndex;
request.wIndex = 0;
request.wLength = len;
request.pData = desc;
err = DeviceRequest(&request, 5000, 0);
if (err)
{
USBError(1,"%s[%p]: Error (0x%x) getting device config descriptor", getName(), this, err);
}
return err;
}
void
IOUSBDevice::TerminateInterfaces()
{
int i;
USBLog(5,"%s[%p]: removing all interfaces and interface drivers",getName(),this);
if (_interfaceList && _numInterfaces)
{
for (i=0; i < _numInterfaces; i++)
{
IOUSBInterface *intf = _interfaceList[i];
if (intf)
{
intf->terminate(kIOServiceSynchronous);
}
}
IODelete(_interfaceList, IOUSBInterface*, _numInterfaces);
_interfaceList = NULL;
_numInterfaces = 0;
}
}
IOReturn
IOUSBDevice::SetConfiguration(IOService *forClient, UInt8 configNumber, bool startMatchingInterfaces)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
const IOUSBConfigurationDescriptor *confDesc;
bool allowConfigValueOfZero = false;
OSBoolean * boolObj = OSDynamicCast( OSBoolean, getProperty(kAllowConfigValueOfZero) );
if ( boolObj && boolObj->isTrue() )
_allowConfigValueOfZero = true;
boolObj = OSDynamicCast( OSBoolean, getProperty("kNeedsExtraResetTime") );
if ( boolObj && boolObj->isTrue() )
{
USBLog(3,"%s[%p]::SetConfiguration kNeedsExtraResetTime is true",getName(), this);
_addExtraResetTime = true;
}
if (!isOpen(forClient))
{
USBLog(3,"%s[%p]::SetConfiguration Error: Client does not have device open",getName(), this);
return kIOReturnExclusiveAccess;
}
confDesc = FindConfig(configNumber);
if ( (configNumber || allowConfigValueOfZero) && !confDesc)
{
USBLog(3,"%s[%p]::SetConfiguration Error: Could not find configuration (%d)",getName(), this, configNumber);
return kIOUSBConfigNotFound;
}
if (confDesc && (confDesc->MaxPower > _busPowerAvailable))
{
DisplayUserNotification(kUSBNotEnoughPowerNotificationType);
USBLog(1,"%s[%p]::SetConfiguration Not enough bus power to configure device",getName(), this);
IOLog("USB Low Power Notice: The device \"%s\" cannot be used because there is not enough power to configure it\n",getName());
return kIOUSBNotEnoughPowerErr;
}
TerminateInterfaces();
USBLog(5,"%s[%p]::SetConfiguration to %d",getName(), this, configNumber);
request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBStandard, kUSBDevice);
request.bRequest = kUSBRqSetConfig;
request.wValue = configNumber;
request.wIndex = 0;
request.wLength = 0;
request.pData = 0;
err = DeviceRequest(&request, 5000, 0);
if (err)
{
USBError(1,"%s[%p]: error setting config. err=0x%x", getName(), this, err);
return err;
}
_currentConfigValue = configNumber;
if (configNumber || _allowConfigValueOfZero)
{
int i;
Boolean gotAlternateSetting = false;
UInt8 altSetting = 0;
if (!confDesc || !confDesc->bNumInterfaces)
{
USBLog(5,"%s[%p]::SetConfiguration No confDesc (%p) or no bNumInterfaces",getName(), this, confDesc);
return kIOReturnNoResources;
}
_interfaceList = IONew(IOUSBInterface*, confDesc->bNumInterfaces);
if (!_interfaceList)
{
USBLog(3,"%s[%p]::SetConfiguration Could not create IOUSBInterface list",getName(), this );
return kIOReturnNoResources;
}
_numInterfaces = confDesc->bNumInterfaces;
const IOUSBInterfaceDescriptor *intfDesc = NULL;
for (i=0; i<_numInterfaces; i++)
{
_interfaceList[i] = NULL;
intfDesc = (IOUSBInterfaceDescriptor *)FindNextDescriptor(intfDesc, kUSBInterfaceDesc);
USBLog(5,"%s[%p]::SetConfiguration Found an interface (%p) ",getName(), this, intfDesc );
while ( gotAlternateSetting && (intfDesc != NULL) && (intfDesc->bAlternateSetting != altSetting) )
{
intfDesc = (IOUSBInterfaceDescriptor *)FindNextDescriptor(intfDesc, kUSBInterfaceDesc);
}
if (intfDesc)
{
if ( !gotAlternateSetting )
{
altSetting = intfDesc->bAlternateSetting;
gotAlternateSetting = true;
}
_interfaceList[i] = IOUSBInterface::withDescriptors(confDesc, intfDesc);
if (_interfaceList[i])
{
USBLog(5,"%s[%p]::SetConfiguration Attaching an interface (%p) ",getName(), this, _interfaceList[i] );
if ( _interfaceList[i]->attach(this) )
{
_interfaceList[i]->release();
if (!_interfaceList[i]->start(this))
{
USBLog(3,"%s[%p]::SetConfiguration Could not start IOUSBInterface (%p)",getName(), this, _interfaceList[i] );
_interfaceList[i]->detach(this);
_interfaceList[i] = NULL;
}
}
else
{
USBLog(3,"%s[%p]::SetConfiguration Attaching an interface (%p) failed",getName(), this, _interfaceList[i] );
return kIOReturnNoResources;
}
}
else
{
USBLog(3,"%s[%p]::SetConfiguration Could not init IOUSBInterface",getName(), this );
return kIOReturnNoMemory;
}
}
else
{
USBLog(3,"%s[%p]: SetConfiguration(%d): could not find interface (%d)", getName(), this, configNumber, i);
}
}
if (startMatchingInterfaces)
{
retain(); for (i=0; i<_numInterfaces; i++)
{
if (_interfaceList[i])
{
IOUSBInterface *theInterface = _interfaceList[i];
USBLog(5,"%s[%p]::SetConfiguration matching to interfaces (%d) ",getName(), this, i );
theInterface->retain();
theInterface->registerService(kIOServiceSynchronous);
theInterface->release();
}
}
release();
}
}
else
{
USBLog(5,"%s[%p]::SetConfiguration Not matching to interfaces: configNumber (%d) is zero and no errata to allow it (%d)",getName(), this, configNumber, _allowConfigValueOfZero);
}
USBLog(5,"%s[%p]::SetConfiguration returning success",getName(), this);
return kIOReturnSuccess;
}
IOReturn
IOUSBDevice::SetFeature(UInt8 feature)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
USBLog(5, "%s[%p]::SetFeature (%d)", getName(), this, feature);
request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBStandard, kUSBDevice);
request.bRequest = kUSBRqSetFeature;
request.wValue = feature;
request.wIndex = 0;
request.wLength = 0;
request.pData = 0;
err = DeviceRequest(&request, 5000, 0);
if (err)
{
USBError(1, "%s[%p]: error setting feature. err=0x%x", getName(), this, err);
}
return(err);
}
IOUSBInterface*
IOUSBDevice::GetInterface(const IOUSBInterfaceDescriptor *intfDesc)
{
int i;
if (!_interfaceList)
return NULL;
for (i=0; i < _numInterfaces; i++)
{
if (!_interfaceList[i])
continue;
if ( (_interfaceList[i]->GetInterfaceNumber() == intfDesc->bInterfaceNumber) &&
(_interfaceList[i]->GetAlternateSetting() == intfDesc->bAlternateSetting) )
return _interfaceList[i];
}
return NULL;
}
IOReturn
IOUSBDevice::GetConfigurationDescriptor(UInt8 configValue, void *data, UInt32 len)
{
unsigned int toCopy;
const IOUSBConfigurationDescriptor *cd;
cd = FindConfig(configValue);
if(!cd)
return kIOUSBConfigNotFound;
toCopy = USBToHostWord(cd->wTotalLength);
if(len < toCopy)
toCopy = len;
bcopy(cd, data, toCopy);
return kIOReturnSuccess;
}
bool
IOUSBDevice::matchPropertyTable(OSDictionary * table, SInt32 *score)
{
bool returnValue = true;
SInt32 propertyScore = *score;
OSString *userClientInitMatchKey;
char logString[256]="";
if ( table == NULL )
{
return false;
}
bool vendorPropertyExists = table->getObject(kUSBVendorID);
bool productPropertyExists = table->getObject(kUSBProductID);
bool deviceReleasePropertyExists = table->getObject(kUSBDeviceReleaseNumber);
bool deviceClassPropertyExists = table->getObject(kUSBDeviceClass);
bool deviceSubClassPropertyExists = table->getObject(kUSBDeviceSubClass);
bool deviceProtocolPropertyExists= table->getObject(kUSBDeviceProtocol);
bool vendorPropertyMatches = USBCompareProperty(table, kUSBVendorID);
bool productPropertyMatches = USBCompareProperty(table, kUSBProductID);
bool deviceReleasePropertyMatches = USBCompareProperty(table, kUSBDeviceReleaseNumber);
bool deviceClassPropertyMatches = USBCompareProperty(table, kUSBDeviceClass);
bool deviceSubClassPropertyMatches = USBCompareProperty(table, kUSBDeviceSubClass);
bool deviceProtocolPropertyMatches= USBCompareProperty(table, kUSBDeviceProtocol);
if (!super::matchPropertyTable(table))
return false;
userClientInitMatchKey = OSDynamicCast(OSString, table->getObject(kIOMatchCategoryKey));
if (userClientInitMatchKey && !strcmp(userClientInitMatchKey->getCStringNoCopy(), "IOUSBUserClientInit"))
{
*score = 9000;
return true;
}
if ( propertyScore >= 10000 )
propertyScore = 9000;
OSNumber * deviceClass = (OSNumber *) getProperty(kUSBDeviceClass);
if ( vendorPropertyMatches && productPropertyMatches && deviceReleasePropertyMatches &&
(!deviceClassPropertyExists || deviceClassPropertyMatches) &&
(!deviceSubClassPropertyExists || deviceSubClassPropertyMatches) &&
(!deviceProtocolPropertyExists || deviceProtocolPropertyMatches) )
{
*score = 100000;
}
else if ( vendorPropertyMatches && productPropertyMatches &&
(!deviceReleasePropertyExists || deviceReleasePropertyMatches) &&
(!deviceClassPropertyExists || deviceClassPropertyMatches) &&
(!deviceSubClassPropertyExists || deviceSubClassPropertyMatches) &&
(!deviceProtocolPropertyExists || deviceProtocolPropertyMatches) )
{
*score = 90000;
}
else if ( deviceClass && deviceClass->unsigned32BitValue() == kUSBVendorSpecificClass )
{
if ( vendorPropertyMatches && deviceSubClassPropertyMatches && deviceProtocolPropertyMatches &&
(!deviceClassPropertyExists || deviceClassPropertyMatches) &&
!deviceReleasePropertyExists && !productPropertyExists )
{
*score = 80000;
}
else if ( vendorPropertyMatches && deviceSubClassPropertyMatches &&
(!deviceClassPropertyExists || deviceClassPropertyMatches) &&
!deviceProtocolPropertyExists && !deviceReleasePropertyExists &&
!productPropertyExists )
{
*score = 70000;
}
else
{
*score = 0;
returnValue = false;
}
}
else if ( deviceClassPropertyMatches && deviceSubClassPropertyMatches && deviceProtocolPropertyMatches &&
!deviceReleasePropertyExists && !vendorPropertyExists && !productPropertyExists )
{
*score = 60000;
}
else if ( deviceClassPropertyMatches && deviceSubClassPropertyMatches &&
!deviceProtocolPropertyExists && !deviceReleasePropertyExists && !vendorPropertyExists &&
!productPropertyExists )
{
*score = 50000;
}
else
{
*score = 0;
returnValue = false;
}
if ( *score != 0 )
*score += propertyScore;
OSString * identifier = OSDynamicCast(OSString, table->getObject("CFBundleIdentifier"));
OSNumber * vendor = (OSNumber *) getProperty(kUSBVendorID);
OSNumber * product = (OSNumber *) getProperty(kUSBProductID);
OSNumber * release = (OSNumber *) getProperty(kUSBDeviceReleaseNumber);
OSNumber * deviceSubClass = (OSNumber *) getProperty(kUSBDeviceSubClass);
OSNumber * protocol = (OSNumber *) getProperty(kUSBDeviceProtocol);
OSNumber * dictVendor = (OSNumber *) table->getObject(kUSBVendorID);
OSNumber * dictProduct = (OSNumber *) table->getObject(kUSBProductID);
OSNumber * dictRelease = (OSNumber *) table->getObject(kUSBDeviceReleaseNumber);
OSNumber * dictDeviceClass = (OSNumber *) table->getObject(kUSBDeviceClass);
OSNumber * dictDeviceSubClass = (OSNumber *) table->getObject(kUSBDeviceSubClass);
OSNumber * dictProtocol = (OSNumber *) table->getObject(kUSBDeviceProtocol);
bool match = false;
if (identifier)
USBLog(5,"Finding device driver for %s, matching personality using %s, score: %ld", getName(), identifier->getCStringNoCopy(), *score);
else
USBLog(6,"Finding device driver for %s, matching user client dictionary, score: %ld", getName(), *score);
if ( vendor && product && release && deviceClass && deviceSubClass && protocol )
{
char tempString[256]="";
sprintf(logString,"\tMatched: ");
if ( vendorPropertyMatches ) { match = true; sprintf(tempString,"idVendor (%d) ", vendor->unsigned32BitValue()); strcat(logString, tempString); }
if ( productPropertyMatches ) { match = true; sprintf(tempString,"idProduct (%d) ", product->unsigned32BitValue()); strcat(logString, tempString);}
if ( deviceReleasePropertyMatches ) { match = true; sprintf(tempString,"bcdDevice (%d) ", release->unsigned32BitValue()); strcat(logString, tempString);}
if ( deviceClassPropertyMatches ) { match = true; sprintf(tempString,"bDeviceClass (%d) ", deviceClass->unsigned32BitValue()); strcat(logString, tempString);}
if ( deviceSubClassPropertyMatches ) { match = true; sprintf(tempString,"bDeviceSubClass (%d) ", deviceSubClass->unsigned32BitValue()); strcat(logString, tempString);}
if ( deviceProtocolPropertyMatches ) { match = true; sprintf(tempString,"bDeviceProtocol (%d) ", protocol->unsigned32BitValue()); strcat(logString, tempString);}
if ( !match ) strcat(logString,"no properties");
USBLog(6,logString);
sprintf(logString,"\tDidn't Match: ");
match = false;
if ( vendorPropertyExists && !vendorPropertyMatches && dictVendor )
{
match = true;
sprintf(tempString,"idVendor (%d,%d) ", dictVendor->unsigned32BitValue(), vendor->unsigned32BitValue());
strcat(logString, tempString);
}
if ( productPropertyExists && !productPropertyMatches && dictProduct)
{
match = true;
sprintf(tempString,"idProduct (%d,%d) ", dictProduct->unsigned32BitValue(), product->unsigned32BitValue());
strcat(logString, tempString);
}
if ( deviceReleasePropertyExists && !deviceReleasePropertyMatches && dictRelease)
{
match = true;
sprintf(tempString,"bcdDevice (%d,%d) ", dictRelease->unsigned32BitValue(), release->unsigned32BitValue());
strcat(logString, tempString);
}
if ( deviceClassPropertyExists && !deviceClassPropertyMatches && dictDeviceClass)
{
match = true;
sprintf(tempString,"bDeviceClass (%d,%d) ", dictDeviceClass->unsigned32BitValue(), deviceClass->unsigned32BitValue());
strcat(logString, tempString);
}
if ( deviceSubClassPropertyExists && !deviceSubClassPropertyMatches && dictDeviceSubClass)
{
match = true;
sprintf(tempString,"bDeviceSubClass (%d,%d) ", dictDeviceSubClass->unsigned32BitValue(), deviceSubClass->unsigned32BitValue());
strcat(logString, tempString);
}
if ( deviceProtocolPropertyExists && !deviceProtocolPropertyMatches && dictProtocol)
{
match = true;
sprintf(tempString,"bDeviceProtocol (%d,%d) ", dictProtocol->unsigned32BitValue(), protocol->unsigned32BitValue());
strcat(logString, tempString);
}
if ( !match ) strcat(logString,"nothing");
USBLog(6,logString);
}
return returnValue;
}
IOReturn
IOUSBDevice::DeviceRequest(IOUSBDevRequest *request, IOUSBCompletion *completion)
{
UInt16 theRequest;
IOReturn err;
UInt16 wValue = request->wValue;
if (_deviceterminating)
{
USBLog(1, "%s[%p]::DeviceRequest - while terminating!", getName(), this);
return kIOReturnNotResponding;
}
theRequest = (request->bRequest << 8) | request->bmRequestType;
if (theRequest == kSetAddress)
{
USBLog(3, "%s[%p]:DeviceRequest ignoring kSetAddress", getName(), this);
return kIOReturnNotPermitted;
}
if ( _pipeZero )
{
err = _pipeZero->ControlRequest(request, completion);
if ( err == kIOReturnSuccess)
{
if ( theRequest == kSetConfiguration)
{
_currentConfigValue = wValue;
}
}
return err;
}
else
return kIOUSBUnknownPipeErr;
}
IOReturn
IOUSBDevice::DeviceRequest(IOUSBDevRequestDesc *request, IOUSBCompletion *completion)
{
UInt16 theRequest;
IOReturn err;
UInt16 wValue = request->wValue;
if (_deviceterminating)
{
USBLog(1, "%s[%p]::DeviceRequest - while terminating!",getName(),this);
return kIOReturnNotResponding;
}
theRequest = (request->bRequest << 8) | request->bmRequestType;
if (theRequest == kSetAddress)
{
USBLog(3, "%s[%p]:DeviceRequest ignoring kSetAddress", getName(), this);
return kIOReturnNotPermitted;
}
if ( _pipeZero )
{
err = _pipeZero->ControlRequest(request, completion);
if ( err == kIOReturnSuccess)
{
if ( theRequest == kSetConfiguration)
{
_currentConfigValue = wValue;
}
}
return err;
}
else
return kIOUSBUnknownPipeErr;
}
IOReturn
IOUSBDevice::GetConfiguration(UInt8 *configNumber)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
USBLog(5, "%s[%p]::GetConfiguration", getName(), this);
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
request.bRequest = kUSBRqGetConfig;
request.wValue = 0;
request.wIndex = 0;
request.wLength = sizeof(*configNumber);
request.pData = configNumber;
err = DeviceRequest(&request, 5000, 0);
if (err)
{
USBError(1,"%s[%p]: error getting config. err=0x%x", getName(), this, err);
}
return(err);
}
IOReturn
IOUSBDevice::GetDeviceStatus(USBStatus *status)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
USBLog(5, "%s[%p]::GetDeviceStatus", getName(), this);
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
request.bRequest = kUSBRqGetStatus;
request.wValue = 0;
request.wIndex = 0;
request.wLength = sizeof(*status);
request.pData = status;
err = DeviceRequest(&request, 5000, 0);
if (err)
USBError(1,"%s[%p]: error getting device status. err=0x%x", getName(), this, err);
return(err);
}
IOReturn
IOUSBDevice::GetStringDescriptor(UInt8 index, char *buf, int maxLen, UInt16 lang)
{
IOReturn err;
UInt8 desc[256]; IOUSBDevRequest request;
int i, len;
if ( maxLen < 2 )
return kIOReturnBadArgument;
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
request.bRequest = kUSBRqGetDescriptor;
request.wValue = (kUSBStringDesc << 8) | index;
request.wIndex = lang;
request.wLength = 2;
bzero(desc, 2);
request.pData = &desc;
err = DeviceRequest(&request, 5000, 0);
if ( (err != kIOReturnSuccess) && (err != kIOReturnOverrun) )
{
USBLog(5,"%s[%p]::GetStringDescriptor reading string length returned error (0x%x) - retrying with max length",getName(), this, err );
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
request.bRequest = kUSBRqGetDescriptor;
request.wValue = (kUSBStringDesc << 8) | index;
request.wIndex = lang;
request.wLength = 256;
bzero(desc, 256);
request.pData = &desc;
err = DeviceRequest(&request, 5000, 0);
if ( (err != kIOReturnSuccess) && (err != kIOReturnUnderrun) )
{
USBLog(3,"%s[%p]::GetStringDescriptor reading string length (256) returned error (0x%x)",getName(), this, err);
return err;
}
}
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
request.bRequest = kUSBRqGetDescriptor;
request.wValue = (kUSBStringDesc << 8) | index;
request.wIndex = lang;
len = desc[0];
if(len == 0)
{
buf[0] = 0;
USBLog(5, "%s[%p]::GetStringDescriptor (%d) Length was zero", getName(), this, index);
return kIOReturnSuccess;
}
if ( desc[1] != kUSBStringDesc )
{
USBLog(3,"%s[%p]::GetStringDescriptor descriptor is not a string (%d kUSBStringDesc)", getName(), this, desc[1] );
return kIOReturnDeviceError;
}
if ( (desc[0] & 1) != 0)
{
USBLog(3,"%s[%p]::GetStringDescriptor descriptor length (%d) is odd, which is illegal", getName(), this, desc[0]);
desc[0] &= 0xfe;
}
request.wLength = len;
bzero(desc, len);
request.pData = &desc;
err = DeviceRequest(&request, 5000, 0);
if (err != kIOReturnSuccess)
{
USBLog(3,"%s[%p]::GetStringDescriptor reading entire string returned error (0x%x)",getName(), this, err);
return err;
}
if ( desc[1] != kUSBStringDesc )
{
USBLog(3,"%s[%p]::GetStringDescriptor descriptor is not a string (%d kUSBStringDesc)", getName(), this, desc[1] );
return kIOReturnDeviceError;
}
if ( (desc[0] & 1) != 0)
{
USBLog(3,"%s[%p]::GetStringDescriptor(2) descriptor length (%d) is odd, which is illegal", getName(), this, desc[0]);
desc[0] &= 0xfe;
}
USBLog(5, "%s[%p]::GetStringDescriptor Got string descriptor %d, length %d, got %d", getName(), this,
index, desc[0], request.wLength);
if ( desc[0] > 1 )
{
len = (desc[0]-2)/2;
if(len > maxLen-1)
len = maxLen-1;
for(i=0; i<len; i++)
buf[i] = desc[2*i+2];
buf[len] = 0;
}
else
buf[0] = 0;
return kIOReturnSuccess;
}
IOReturn
IOUSBDevice::message( UInt32 type, IOService * provider, void * argument )
{
IOReturn err = kIOReturnSuccess;
IOUSBRootHubDevice * rootHub = NULL;
OSIterator * iter;
IOService * client;
UInt32 retries = 100;
IOReturn resetErr = kIOReturnSuccess;
switch ( type )
{
case kIOUSBMessagePortHasBeenReset:
resetErr = * (IOReturn *) argument;
USBLog(4,"%s[%p] received kIOUSBMessagePortHasBeenReset with error = 0x%x",getName(), this, resetErr);
while ( _pipeZero == NULL && retries > 0)
{
retries--;
IOSleep(50);
}
if ( _pipeZero && (resetErr == kIOReturnSuccess) )
{
USBLog(3, "%s[%p] calling messageClients (kIOUSBMessagePortHasBeenReset (%d))", getName(), this, _portNumber );
(void) messageClients(kIOUSBMessagePortHasBeenReset, &_portNumber, sizeof(_portNumber));
}
_portHasBeenReset = true;
break;
case kIOUSBMessageHubIsDeviceConnected:
if ( _usbPlaneParent )
{
_usbPlaneParent->retain();
USBLog(5, "%s at %d: Hub device name is %s at %d", getName(), _address, _usbPlaneParent->getName(), _usbPlaneParent->GetAddress());
rootHub = OSDynamicCast(IOUSBRootHubDevice, _usbPlaneParent);
if ( !rootHub )
{
err = _usbPlaneParent->message(kIOUSBMessageHubIsDeviceConnected, NULL, 0);
}
if ( err == kIOReturnSuccess )
{
iter = _usbPlaneParent->getClientIterator();
while( (client = (IOService *) iter->getNextObject()))
{
IOSleep(1);
err = client->message(kIOUSBMessageHubIsDeviceConnected, this, &_portNumber);
if ( err == kIOReturnUnsupported )
err = kIOReturnSuccess;
else
if ( err != kIOReturnSuccess )
break;
}
iter->release();
}
_usbPlaneParent->release();
}
else
{
err = kIOReturnNoDevice;
}
break;
case kIOUSBMessagePortHasBeenResumed:
USBLog(5,"%s[%p]: kIOUSBMessagePortHasBeenResumed",getName(),this);
_portHasBeenSuspended = true;
messageClients( kIOUSBMessagePortHasBeenResumed, this, _portNumber);
break;
case kIOUSBMessagePortHasBeenSuspended:
USBLog(5,"%s[%p]: kIOUSBMessagePortHasBeenSuspended with error: 0x%x",getName(),this, * (IOReturn *) argument);
_portHasBeenSuspended = true;
messageClients( kIOUSBMessagePortHasBeenSuspended, this, _portNumber);
break;
case kIOMessageServiceIsTerminated:
USBLog(5,"%s[%p]: kIOMessageServiceIsTerminated",getName(),this);
_deviceterminating = true;
break;
case kIOMessageServiceIsSuspended:
case kIOMessageServiceIsResumed:
case kIOMessageServiceIsRequestingClose:
case kIOMessageServiceWasClosed:
case kIOMessageServiceBusyStateChange:
err = kIOReturnUnsupported;
default:
break;
}
return err;
}
void
IOUSBDevice::stop( IOService * provider )
{
USBLog(5, "%s[%p]::stop isInactive = %d", getName(), this, isInactive());
if (_notifierHandlerTimer)
{
if ( _workLoop )
_workLoop->removeEventSource(_notifierHandlerTimer);
_notifierHandlerTimer->release();
_notifierHandlerTimer = NULL;
}
super::stop(provider);
}
bool
IOUSBDevice::willTerminate( IOService * provider, IOOptionBits options )
{
USBLog(3, "%s[%p]::willTerminate isInactive = %d", getName(), this, isInactive());
return super::willTerminate(provider, options);
}
bool
IOUSBDevice::didTerminate( IOService * provider, IOOptionBits options, bool * defer )
{
USBLog(3, "%s[%p]::didTerminate isInactive = %d", getName(), this, isInactive());
return super::didTerminate(provider, options, defer);
}
void
IOUSBDevice::DisplayNotEnoughPowerNotice()
{
KUNCUserNotificationDisplayNotice(
0, 0, "", "", "/System/Library/Extensions/IOUSBFamily.kext", "USB Low Power Header", "USB Low Power Notice", "OK");
return;
}
void
IOUSBDevice::DisplayUserNotificationForDeviceEntry(OSObject *owner, IOTimerEventSource *sender)
{
IOUSBDevice * me = OSDynamicCast(IOUSBDevice, owner);
if (!me)
return;
me->retain();
me->DisplayUserNotificationForDevice();
me->release();
}
void
IOUSBDevice::DisplayUserNotificationForDevice ()
{
kern_return_t notificationError = kIOReturnSuccess;
USBLog(3,"%s[%p]DisplayUserNotificationForDevice notificationType: %d",getName(), this, _notificationType );
switch ( _notificationType )
{
case kUSBNotEnoughPowerNotificationType:
IOLog("USB Notification: The device \"%s\" cannot operate because there is not enough power available\n",getName());
notificationError = KUNCUserNotificationDisplayNotice(
0, 0, "", "", "/System/Library/Extensions/IOUSBFamily.kext", "USB Low Power Header", "USB Low Power Notice", "OK");
break;
case kUSBIndividualOverCurrentNotificationType:
IOLog("USB Notification: The device \"%s\" has caused an overcurrent condition. The port it is attached to has been disabled\n",getName());
notificationError = KUNCUserNotificationDisplayNotice(
0, 0, "", "", "/System/Library/Extensions/IOUSBFamily.kext", "USB OverCurrent Header", "USB Individual OverCurrent Notice", "OK");
break;
case kUSBGangOverCurrentNotificationType:
IOLog("USB Notification: The device \"%s\" has caused an overcurrent condition. The hub it is attached to has been disabled\n",getName());
notificationError = KUNCUserNotificationDisplayNotice(
0, 0, "", "", "/System/Library/Extensions/IOUSBFamily.kext", "USB OverCurrent Header", "USB Gang OverCurrent Notice", "OK");
break;
}
if ( notificationError != kIOReturnSuccess )
{
USBLog(3,"%s[%p]DisplayUserNotificationForDevice returned error 0x%x", getName(), this, notificationError);
_notifierHandlerTimer->setTimeoutMS (kNotifyTimerDelay); }
return;
}
OSMetaClassDefineReservedUsed(IOUSBDevice, 0);
IOReturn
IOUSBDevice::DeviceRequest(IOUSBDevRequest *request, UInt32 noDataTimeout, UInt32 completionTimeout, IOUSBCompletion *completion)
{
UInt16 theRequest;
IOReturn err;
UInt16 wValue = request->wValue;
if (_deviceterminating)
{
USBLog(1, "%s[%p]::DeviceRequest - while terminating!", getName(), this);
return kIOReturnNotResponding;
}
theRequest = (request->bRequest << 8) | request->bmRequestType;
if (theRequest == kSetAddress)
{
USBLog(3, "%s[%p]:DeviceRequest ignoring kSetAddress", getName(), this);
return kIOReturnNotPermitted;
}
if ( _pipeZero )
{
err = _pipeZero->ControlRequest(request, noDataTimeout, completionTimeout, completion);
if ( err == kIOReturnSuccess)
{
if ( theRequest == kSetConfiguration)
{
_currentConfigValue = wValue;
}
}
return err;
}
else
return kIOUSBUnknownPipeErr;
}
OSMetaClassDefineReservedUsed(IOUSBDevice, 1);
IOReturn
IOUSBDevice::DeviceRequest(IOUSBDevRequestDesc *request, UInt32 noDataTimeout, UInt32 completionTimeout, IOUSBCompletion *completion)
{
UInt16 theRequest;
IOReturn err;
UInt16 wValue = request->wValue;
if (_deviceterminating)
{
USBLog(1, "%s[%p]::DeviceRequest - while terminating!", getName(), this);
return kIOReturnNotResponding;
}
theRequest = (request->bRequest << 8) | request->bmRequestType;
if (theRequest == kSetAddress)
{
USBLog(3, "%s[%p]:DeviceRequest ignoring kSetAddress", getName(), this);
return kIOReturnNotPermitted;
}
if ( _pipeZero )
{
err = _pipeZero->ControlRequest(request, noDataTimeout, completionTimeout, completion);
if ( err == kIOReturnSuccess)
{
if ( theRequest == kSetConfiguration)
{
_currentConfigValue = wValue;
}
}
return err;
}
else
return kIOUSBUnknownPipeErr;
}
OSMetaClassDefineReservedUsed(IOUSBDevice, 2);
IOReturn
IOUSBDevice::SuspendDevice( bool suspend )
{
UInt32 retries = 0;
if ( _suspendInProgress )
{
USBLog(5, "%s[%p]::SuspendDevice(%d) while in progress", getName(), this, _portNumber );
return kIOReturnNotPermitted;
}
if ( isInactive() )
{
USBLog(1, "%s[%p]::SuspendDevice - while inactive!", getName(), this);
return kIOReturnNotResponding;
}
_suspendInProgress = true;
retain();
_portHasBeenSuspended = false;
USBLog(5, "+%s[%p]::SuspendDevice for port %d", getName(), this, _portNumber );
thread_call_enter1( _doPortSuspendThread, (thread_call_param_t) suspend );
while ( !_portHasBeenSuspended && retries < 200 )
{
IOSleep(50);
if ( isInactive() )
{
USBLog(3, "+%s[%p]::SuspendDevice isInactive() while waiting for suspend to finish", getName(), this );
break;
}
retries++;
}
USBLog(5, "-%s[%p]::SuspendDevice for port %d", getName(), this, _portNumber );
_suspendInProgress = false;
return kIOReturnSuccess;
}
OSMetaClassDefineReservedUsed(IOUSBDevice, 3);
IOReturn
IOUSBDevice::ReEnumerateDevice( UInt32 options )
{
if (_deviceterminating)
{
USBLog(1, "%s[%p]::ReEnumerateDevice - while terminating!", getName(), this);
return kIOReturnNotResponding;
}
if ( _addExtraResetTime )
{
USBLog(1, "%s[%p]::ReEnumerateDevice - setting extra reset time options!", getName(), this);
options |= (1 << 31 );
}
USBLog(3, "+%s[%p] ReEnumerateDevice for port %d, options 0x%x", getName(), this, _portNumber, options );
retain();
thread_call_enter1( _doPortReEnumerateThread, (thread_call_param_t) options );
USBLog(3, "-%s[%p] ReEnumerateDevice for port %d", getName(), this, _portNumber );
return kIOReturnSuccess;
}
OSMetaClassDefineReservedUsed(IOUSBDevice, 4);
void
IOUSBDevice::DisplayUserNotification(UInt32 notificationType )
{
USBLog(3, "%s[%p] DisplayUserNotification type %ld", getName(), this, notificationType );
_notificationType = notificationType;
DisplayUserNotificationForDeviceEntry(this, NULL );
}
OSMetaClassDefineReservedUnused(IOUSBDevice, 5);
OSMetaClassDefineReservedUnused(IOUSBDevice, 6);
OSMetaClassDefineReservedUnused(IOUSBDevice, 7);
OSMetaClassDefineReservedUnused(IOUSBDevice, 8);
OSMetaClassDefineReservedUnused(IOUSBDevice, 9);
OSMetaClassDefineReservedUnused(IOUSBDevice, 10);
OSMetaClassDefineReservedUnused(IOUSBDevice, 11);
OSMetaClassDefineReservedUnused(IOUSBDevice, 12);
OSMetaClassDefineReservedUnused(IOUSBDevice, 13);
OSMetaClassDefineReservedUnused(IOUSBDevice, 14);
OSMetaClassDefineReservedUnused(IOUSBDevice, 15);
OSMetaClassDefineReservedUnused(IOUSBDevice, 16);
OSMetaClassDefineReservedUnused(IOUSBDevice, 17);
OSMetaClassDefineReservedUnused(IOUSBDevice, 18);
OSMetaClassDefineReservedUnused(IOUSBDevice, 19);
void
IOUSBDevice::ProcessPortResetEntry(OSObject *target)
{
IOUSBDevice * me = OSDynamicCast(IOUSBDevice, target);
if (!me)
return;
me->retain();
me->ProcessPortReset();
me->release();
}
void
IOUSBDevice::ProcessPortReset()
{
IOReturn err = kIOReturnSuccess;
USBLog(5,"+%s[%p]::ProcessPortReset",getName(),this);
_portResetThreadActive = true;
if( _pipeZero)
{
_pipeZero->Abort();
_pipeZero->ClosePipe();
_pipeZero->release();
_pipeZero = NULL;
}
if ( _usbPlaneParent )
{
USBLog(3, "%s[%p] calling messageClients (kIOUSBMessageHubResetPort)", getName(), this);
_usbPlaneParent->retain();
err = _usbPlaneParent->messageClients(kIOUSBMessageHubResetPort, &_portNumber, sizeof(_portNumber));
_usbPlaneParent->release();
}
_pipeZero = IOUSBPipe::ToEndpoint(&_endpointZero, this, _controller);
if (!_pipeZero)
{
USBError(1,"%s[%p]::ProcessPortReset DANGER could not recreate pipe Zero after reset");
err = kIOReturnNoMemory;
}
_currentConfigValue = 0;
_portResetThreadActive = false;
USBLog(5,"-%s[%p]::ProcessPortReset",getName(),this);
}
void
IOUSBDevice::ProcessPortReEnumerateEntry(OSObject *target,thread_call_param_t options)
{
IOUSBDevice * me = OSDynamicCast(IOUSBDevice, target);
if (!me)
return;
me->ProcessPortReEnumerate( (UInt32) options);
me->release();
}
void
IOUSBDevice::ProcessPortReEnumerate(UInt32 options)
{
IOReturn err = kIOReturnSuccess;
IOUSBHubPortReEnumerateParam params;
USBLog(5,"+%s[%p]::ProcessPortReEnumerate",getName(),this);
params.portNumber = _portNumber;
params.options = options;
if( _pipeZero)
{
_pipeZero->Abort();
_pipeZero->ClosePipe();
_pipeZero->release();
_pipeZero = NULL;
}
TerminateInterfaces();
if ( _usbPlaneParent )
{
USBLog(3, "%s[%p] calling messageClients (kIOUSBMessageHubReEnumeratePort)", getName(), this);
_usbPlaneParent->retain();
err = _usbPlaneParent->messageClients(kIOUSBMessageHubReEnumeratePort, ¶ms, sizeof(IOUSBHubPortReEnumerateParam));
_usbPlaneParent->release();
}
USBLog(5,"-%s[%p]::ProcessPortReEnumerate",getName(),this);
}
void
IOUSBDevice::ProcessPortSuspendEntry(OSObject *target, thread_call_param_t suspend)
{
IOUSBDevice * me = OSDynamicCast(IOUSBDevice, target);
if (!me)
return;
me->ProcessPortSuspend( (bool) suspend );
me->release();
}
void
IOUSBDevice::ProcessPortSuspend(bool suspend)
{
IOReturn err = kIOReturnSuccess;
_portSuspendThreadActive = true;
if ( _usbPlaneParent )
{
_usbPlaneParent->retain();
if ( suspend )
err = _usbPlaneParent->messageClients(kIOUSBMessageHubSuspendPort, &_portNumber, sizeof(_portNumber));
else
err = _usbPlaneParent->messageClients(kIOUSBMessageHubResumePort, &_portNumber, sizeof(_portNumber));
_usbPlaneParent->release();
}
_portSuspendThreadActive = false;
}
void
IOUSBDevice::SetPort(void *port)
{
_port = port;
}
USBDeviceAddress
IOUSBDevice::GetAddress(void)
{
return _address;
}
UInt8
IOUSBDevice::GetSpeed(void)
{
return _speed;
}
IOUSBController *
IOUSBDevice::GetBus(void)
{
return _controller;
}
UInt32
IOUSBDevice::GetBusPowerAvailable( void )
{
return _busPowerAvailable;
}
UInt8
IOUSBDevice::GetMaxPacketSize(void)
{
return _descriptor.bMaxPacketSize0;
}
UInt16
IOUSBDevice::GetVendorID(void)
{
return USBToHostWord(_descriptor.idVendor);
}
UInt16
IOUSBDevice::GetProductID(void)
{
return USBToHostWord(_descriptor.idProduct);
}
UInt16
IOUSBDevice::GetDeviceRelease(void)
{
return USBToHostWord(_descriptor.bcdDevice);
}
UInt16
IOUSBDevice::GetbcdUSB(void)
{
return USBToHostWord(_descriptor.bcdUSB);
}
UInt8
IOUSBDevice::GetNumConfigurations(void)
{
return _descriptor.bNumConfigurations;
}
UInt8
IOUSBDevice::GetProtocol(void)
{
return _descriptor.bDeviceProtocol;
}
UInt8
IOUSBDevice::GetManufacturerStringIndex(void )
{
return _descriptor.iManufacturer;
}
UInt8
IOUSBDevice::GetProductStringIndex(void )
{
return _descriptor.iProduct;
}
UInt8
IOUSBDevice::GetSerialNumberStringIndex(void )
{
return _descriptor.iSerialNumber;
}
IOUSBPipe *
IOUSBDevice::GetPipeZero(void)
{
return _pipeZero;
}
IOUSBPipe *
IOUSBDevice::MakePipe(const IOUSBEndpointDescriptor *ep)
{
return IOUSBPipe::ToEndpoint(ep, this, _controller);
}