+/*
+ * Helpers for matching dictionaries.
+ * Keys existing in matching are checked in properties.
+ * Keys may be a string or OSCollection of IOStrings
+ */
+
+bool
+IOService::compareProperty( OSDictionary * matching,
+ const char * key )
+{
+ OSObject * value;
+ OSObject * prop;
+ bool ok;
+
+ value = matching->getObject( key );
+ if (value) {
+ prop = copyProperty(key);
+ ok = value->isEqualTo(prop);
+ if (prop) {
+ prop->release();
+ }
+ } else {
+ ok = true;
+ }
+
+ return ok;
+}
+
+
+bool
+IOService::compareProperty( OSDictionary * matching,
+ const OSString * key )
+{
+ OSObject * value;
+ OSObject * prop;
+ bool ok;
+
+ value = matching->getObject( key );
+ if (value) {
+ prop = copyProperty(key);
+ ok = value->isEqualTo(prop);
+ if (prop) {
+ prop->release();
+ }
+ } else {
+ ok = true;
+ }
+
+ return ok;
+}
+
+#ifndef __clang_analyzer__
+// Implementation of this function is hidden from the static analyzer.
+// The analyzer was worried about this function's confusing contract over
+// the 'keys' parameter. The contract is to either release it or not release it
+// depending on whether 'matching' is non-null. Such contracts are discouraged
+// but changing it now would break compatibility.
+bool
+IOService::compareProperties( OSDictionary * matching,
+ OSCollection * keys )
+{
+ OSCollectionIterator * iter;
+ const OSString * key;
+ bool ok = true;
+
+ if (!matching || !keys) {
+ return false;
+ }
+
+ iter = OSCollectionIterator::withCollection( keys );
+
+ if (iter) {
+ while (ok && (key = OSDynamicCast( OSString, iter->getNextObject()))) {
+ ok = compareProperty( matching, key );
+ }
+
+ iter->release();
+ }
+ keys->release(); // !! consume a ref !!
+
+ return ok;
+}
+#endif // __clang_analyzer__
+
+/* Helper to add a location matching dict to the table */
+
+OSDictionary *
+IOService::addLocation( OSDictionary * table )
+{
+ OSDictionary * dict;
+
+ if (!table) {
+ return NULL;
+ }
+
+ dict = OSDictionary::withCapacity( 1 );
+ if (dict) {
+ bool ok = table->setObject( gIOLocationMatchKey, dict );
+ dict->release();
+ if (!ok) {
+ dict = NULL;
+ }
+ }
+
+ return dict;
+}
+
+/*
+ * Go looking for a provider to match a location dict.
+ */
+
+IOService *
+IOService::matchLocation( IOService * /* client */ )
+{
+ IOService * parent;
+
+ parent = getProvider();
+
+ if (parent) {
+ parent = parent->matchLocation( this );
+ }
+
+ return parent;
+}
+
+bool
+IOService::matchInternal(OSDictionary * table, uint32_t options, uint32_t * did)
+{
+ OSString * matched;
+ OSObject * obj;
+ OSString * str;
+ OSDictionary * matchProps;
+ IORegistryEntry * entry;
+ OSNumber * num;
+ bool match = true;
+ bool changesOK = (0 != (kIOServiceChangesOK & options));
+ uint32_t count;
+ uint32_t done;
+
+ do{
+ count = table->getCount();
+ done = 0;
+ matchProps = NULL;
+
+ if (table->getObject(gIOCompatibilityMatchKey)) {
+ done++;
+ obj = copyProperty(gIOCompatibilityPropertiesKey);
+ matchProps = OSDynamicCast(OSDictionary, obj);
+ if (!matchProps) {
+ OSSafeReleaseNULL(obj);
+ }
+ }
+
+ str = OSDynamicCast(OSString, table->getObject(gIOProviderClassKey));
+ if (str) {
+ done++;
+ if (matchProps && (obj = matchProps->getObject(gIOClassKey))) {
+ match = str->isEqualTo(obj);
+ } else {
+ match = ((kIOServiceClassDone & options) || (NULL != metaCast(str)));
+ }
+
+#if MATCH_DEBUG
+ match = (0 != metaCast( str ));
+ if ((kIOServiceClassDone & options) && !match) {
+ panic("classDone");
+ }
+#endif
+ if ((!match) || (done == count)) {
+ break;
+ }
+ }
+
+ obj = table->getObject( gIONameMatchKey );
+ if (obj) {
+ done++;
+ match = compareNames( obj, changesOK ? &matched : NULL );
+ if (!match) {
+ break;
+ }
+ if (changesOK && matched) {
+ // leave a hint as to which name matched
+ table->setObject( gIONameMatchedKey, matched );
+ matched->release();
+ }
+ if (done == count) {
+ break;
+ }
+ }
+
+ str = OSDynamicCast( OSString, table->getObject( gIOLocationMatchKey ));
+ if (str) {
+ const OSSymbol * sym;
+ done++;
+ match = false;
+ sym = copyLocation();
+ if (sym) {
+ match = sym->isEqualTo( str );
+ sym->release();
+ }
+ if ((!match) || (done == count)) {
+ break;
+ }
+ }
+
+ obj = table->getObject( gIOPropertyMatchKey );
+ if (obj) {
+ OSDictionary * nextDict;
+ OSIterator * iter;
+ done++;
+ match = false;
+ if (!matchProps) {
+ matchProps = dictionaryWithProperties();
+ }
+ if (matchProps) {
+ nextDict = OSDynamicCast( OSDictionary, obj);
+ if (nextDict) {
+ iter = NULL;
+ } else {
+ iter = OSCollectionIterator::withCollection(
+ OSDynamicCast(OSCollection, obj));
+ }
+
+ while (nextDict
+ || (iter && (NULL != (nextDict = OSDynamicCast(OSDictionary,
+ iter->getNextObject()))))) {
+ match = matchProps->isEqualTo( nextDict, nextDict);
+ if (match) {
+ break;
+ }
+ nextDict = NULL;
+ }
+ if (iter) {
+ iter->release();
+ }
+ }
+ if ((!match) || (done == count)) {
+ break;
+ }
+ }
+
+ obj = table->getObject( gIOPropertyExistsMatchKey );
+ if (obj) {
+ OSString * nextKey;
+ OSIterator * iter;
+ done++;
+ match = false;
+ if (!matchProps) {
+ matchProps = dictionaryWithProperties();
+ }
+ if (matchProps) {
+ nextKey = OSDynamicCast( OSString, obj);
+ if (nextKey) {
+ iter = NULL;
+ } else {
+ iter = OSCollectionIterator::withCollection(
+ OSDynamicCast(OSCollection, obj));
+ }
+
+ while (nextKey
+ || (iter && (NULL != (nextKey = OSDynamicCast(OSString,
+ iter->getNextObject()))))) {
+ match = (NULL != matchProps->getObject(nextKey));
+ if (match) {
+ break;
+ }
+ nextKey = NULL;
+ }
+ if (iter) {
+ iter->release();
+ }
+ }
+ if ((!match) || (done == count)) {
+ break;
+ }
+ }
+
+ str = OSDynamicCast( OSString, table->getObject( gIOPathMatchKey ));
+ if (str) {
+ done++;
+ entry = IORegistryEntry::fromPath( str->getCStringNoCopy());
+ match = (this == entry);
+ if (entry) {
+ entry->release();
+ }
+ if (!match && matchProps && (obj = matchProps->getObject(gIOPathKey))) {
+ match = str->isEqualTo(obj);
+ }
+ if ((!match) || (done == count)) {
+ break;
+ }
+ }
+
+ num = OSDynamicCast( OSNumber, table->getObject( gIORegistryEntryIDKey ));
+ if (num) {
+ done++;
+ match = (getRegistryEntryID() == num->unsigned64BitValue());
+ if ((!match) || (done == count)) {
+ break;
+ }
+ }
+
+ num = OSDynamicCast( OSNumber, table->getObject( gIOMatchedServiceCountKey ));
+ if (num) {
+ OSIterator * iter;
+ IOService * service = NULL;
+ UInt32 serviceCount = 0;
+
+ done++;
+ iter = getClientIterator();
+ if (iter) {
+ while ((service = (IOService *) iter->getNextObject())) {
+ if (kIOServiceInactiveState & service->__state[0]) {
+ continue;
+ }
+ if (NULL == service->getProperty( gIOMatchCategoryKey )) {
+ continue;
+ }
+ ++serviceCount;
+ }
+ iter->release();
+ }
+ match = (serviceCount == num->unsigned32BitValue());
+ if ((!match) || (done == count)) {
+ break;
+ }
+ }
+
+#define propMatch(key) \
+ obj = table->getObject(key); \
+ if (obj) \
+ { \
+ OSObject * prop; \
+ done++; \
+ prop = copyProperty(key); \
+ match = obj->isEqualTo(prop); \
+ if (prop) prop->release(); \
+ if ((!match) || (done == count)) break; \
+ }
+ propMatch(gIOBSDNameKey)
+ propMatch(gIOBSDMajorKey)
+ propMatch(gIOBSDMinorKey)
+ propMatch(gIOBSDUnitKey)
+#undef propMatch
+ }while (false);
+
+ OSSafeReleaseNULL(matchProps);
+
+ if (did) {
+ *did = done;
+ }
+ return match;
+}
+
+bool
+IOService::passiveMatch( OSDictionary * table, bool changesOK )
+{
+ return matchPassive(table, changesOK ? kIOServiceChangesOK : 0);
+}
+
+bool
+IOService::matchPassive(OSDictionary * table, uint32_t options)
+{
+ IOService * where;
+ OSDictionary * nextTable;
+ SInt32 score;
+ OSNumber * newPri;
+ bool match = true;
+ bool matchParent = false;
+ uint32_t count;
+ uint32_t done;
+
+ assert( table );
+
+#if defined(XNU_TARGET_OS_OSX)
+ OSArray* aliasServiceRegIds = NULL;
+ IOService* foundAlternateService = NULL;
+#endif /* defined(XNU_TARGET_OS_OSX) */
+
+#if MATCH_DEBUG
+ OSDictionary * root = table;
+#endif
+
+ where = this;
+ do{
+ do{
+ count = table->getCount();
+ if (!(kIOServiceInternalDone & options)) {
+ match = where->matchInternal(table, options, &done);
+ // don't call family if we've done all the entries in the table
+ if ((!match) || (done == count)) {
+ break;
+ }
+ }
+
+ // pass in score from property table
+ score = IOServiceObjectOrder( table, (void *) gIOProbeScoreKey);
+
+ // do family specific matching
+ match = where->matchPropertyTable( table, &score );
+
+ if (!match) {
+#if IOMATCHDEBUG
+ if (kIOLogMatch & getDebugFlags( table )) {
+ LOG("%s: family specific matching fails\n", where->getName());
+ }
+#endif
+ break;
+ }
+
+ if (kIOServiceChangesOK & options) {
+ // save the score
+ newPri = OSNumber::withNumber( score, 32 );
+ if (newPri) {
+ table->setObject( gIOProbeScoreKey, newPri );
+ newPri->release();
+ }
+ }
+
+ options = 0;
+ matchParent = false;
+
+ nextTable = OSDynamicCast(OSDictionary,
+ table->getObject( gIOParentMatchKey ));
+ if (nextTable) {
+ // look for a matching entry anywhere up to root
+ match = false;
+ matchParent = true;
+ table = nextTable;
+ break;
+ }
+
+ table = OSDynamicCast(OSDictionary,
+ table->getObject( gIOLocationMatchKey ));
+ if (table) {
+ // look for a matching entry at matchLocation()
+ match = false;
+ where = where->getProvider();
+ if (where && (where = where->matchLocation(where))) {
+ continue;
+ }
+ }
+ break;
+ }while (true);
+
+ if (match == true) {
+ break;
+ }
+
+ if (matchParent == true) {
+#if defined(XNU_TARGET_OS_OSX)
+ // check if service has an alias to search its other "parents" if a parent match isn't found
+ OSObject * prop = where->copyProperty(gIOServiceLegacyMatchingRegistryIDKey);
+ OSNumber * alternateRegistryID = OSDynamicCast(OSNumber, prop);
+ if (alternateRegistryID != NULL) {
+ if (aliasServiceRegIds == NULL) {
+ aliasServiceRegIds = OSArray::withCapacity(sizeof(alternateRegistryID));
+ }
+ aliasServiceRegIds->setObject(alternateRegistryID);
+ }
+ OSSafeReleaseNULL(prop);
+#endif /* defined(XNU_TARGET_OS_OSX) */
+ } else {
+ break;
+ }
+
+ where = where->getProvider();
+#if defined(XNU_TARGET_OS_OSX)
+ if (where == NULL) {
+ // there were no matching parent services, check to see if there are aliased services that have a matching parent
+ if (aliasServiceRegIds != NULL) {
+ unsigned int numAliasedServices = aliasServiceRegIds->getCount();
+ if (numAliasedServices != 0) {
+ OSNumber* alternateRegistryID = OSDynamicCast(OSNumber, aliasServiceRegIds->getObject(numAliasedServices - 1));
+ if (alternateRegistryID != NULL) {
+ OSDictionary* alternateMatchingDict = IOService::registryEntryIDMatching(alternateRegistryID->unsigned64BitValue());
+ aliasServiceRegIds->removeObject(numAliasedServices - 1);
+ if (alternateMatchingDict != NULL) {
+ OSSafeReleaseNULL(foundAlternateService);
+ foundAlternateService = IOService::copyMatchingService(alternateMatchingDict);
+ alternateMatchingDict->release();
+ if (foundAlternateService != NULL) {
+ where = foundAlternateService;
+ }
+ }
+ }
+ }
+ }
+ }
+#endif /* defined(XNU_TARGET_OS_OSX) */
+ }while (where != NULL);
+
+#if defined(XNU_TARGET_OS_OSX)
+ OSSafeReleaseNULL(foundAlternateService);
+ OSSafeReleaseNULL(aliasServiceRegIds);
+#endif /* defined(XNU_TARGET_OS_OSX) */
+
+#if MATCH_DEBUG
+ if (where != this) {
+ OSSerialize * s = OSSerialize::withCapacity(128);
+ root->serialize(s);
+ kprintf("parent match 0x%llx, %d,\n%s\n", getRegistryEntryID(), match, s->text());
+ s->release();
+ }
+#endif
+
+ return match;
+}
+
+
+IOReturn
+IOService::newUserClient( task_t owningTask, void * securityID,
+ UInt32 type, OSDictionary * properties,
+ IOUserClient ** handler )
+{
+ const OSSymbol *userClientClass = NULL;
+ IOUserClient *client;
+ OSObject *prop;
+ OSObject *temp;
+
+ if (reserved && reserved->uvars && reserved->uvars->userServer) {
+ return reserved->uvars->userServer->serviceNewUserClient(this, owningTask, securityID, type, properties, handler);
+ }
+
+ if (kIOReturnSuccess == newUserClient( owningTask, securityID, type, handler )) {
+ return kIOReturnSuccess;
+ }
+
+ // First try my own properties for a user client class name
+ prop = copyProperty(gIOUserClientClassKey);
+ if (prop) {
+ if (OSDynamicCast(OSSymbol, prop)) {
+ userClientClass = (const OSSymbol *) prop;
+ } else if (OSDynamicCast(OSString, prop)) {
+ userClientClass = OSSymbol::withString((OSString *) prop);
+ if (userClientClass) {
+ setProperty(gIOUserClientClassKey,
+ (OSObject *) userClientClass);
+ }
+ }
+ }
+
+ // Didn't find one so lets just bomb out now without further ado.
+ if (!userClientClass) {
+ OSSafeReleaseNULL(prop);
+ return kIOReturnUnsupported;
+ }
+
+ // This reference is consumed by the IOServiceOpen call
+ temp = OSMetaClass::allocClassWithName(userClientClass);
+ OSSafeReleaseNULL(prop);
+ if (!temp) {
+ return kIOReturnNoMemory;
+ }
+
+ if (OSDynamicCast(IOUserClient, temp)) {
+ client = (IOUserClient *) temp;
+ } else {
+ temp->release();
+ return kIOReturnUnsupported;
+ }
+
+ if (!client->initWithTask(owningTask, securityID, type, properties)) {
+ client->release();
+ return kIOReturnBadArgument;
+ }
+
+ if (!client->attach(this)) {
+ client->release();
+ return kIOReturnUnsupported;
+ }
+
+ if (!client->start(this)) {
+ client->detach(this);
+ client->release();
+ return kIOReturnUnsupported;
+ }
+
+ *handler = client;
+ return kIOReturnSuccess;
+}
+
+IOReturn
+IOService::newUserClient( task_t owningTask, void * securityID,
+ UInt32 type, OSDictionary * properties,
+ OSSharedPtr<IOUserClient>& handler )
+{
+ IOUserClient* handlerRaw = NULL;
+ IOReturn result = newUserClient(owningTask, securityID, type, properties, &handlerRaw);
+ handler.reset(handlerRaw, OSNoRetain);
+ return result;
+}
+
+IOReturn
+IOService::newUserClient( task_t owningTask, void * securityID,
+ UInt32 type, IOUserClient ** handler )
+{
+ return kIOReturnUnsupported;
+}
+
+IOReturn
+IOService::newUserClient( task_t owningTask, void * securityID,
+ UInt32 type, OSSharedPtr<IOUserClient>& handler )
+{
+ IOUserClient* handlerRaw = nullptr;
+ IOReturn result = IOService::newUserClient(owningTask, securityID, type, &handlerRaw);
+ handler.reset(handlerRaw, OSNoRetain);
+ return result;
+}
+
+
+IOReturn
+IOService::requestProbe( IOOptionBits options )
+{
+ return kIOReturnUnsupported;
+}
+
+bool
+IOService::hasUserServer() const
+{
+ return reserved && reserved->uvars && reserved->uvars->userServer;
+}
+
+/*
+ * Convert an IOReturn to text. Subclasses which add additional
+ * IOReturn's should override this method and call
+ * super::stringFromReturn if the desired value is not found.
+ */
+
+const char *
+IOService::stringFromReturn( IOReturn rtn )
+{
+ static const IONamedValue IOReturn_values[] = {
+ {kIOReturnSuccess, "success" },
+ {kIOReturnError, "general error" },
+ {kIOReturnNoMemory, "memory allocation error" },
+ {kIOReturnNoResources, "resource shortage" },
+ {kIOReturnIPCError, "Mach IPC failure" },
+ {kIOReturnNoDevice, "no such device" },
+ {kIOReturnNotPrivileged, "privilege violation" },
+ {kIOReturnBadArgument, "invalid argument" },
+ {kIOReturnLockedRead, "device is read locked" },
+ {kIOReturnLockedWrite, "device is write locked" },
+ {kIOReturnExclusiveAccess, "device is exclusive access" },
+ {kIOReturnBadMessageID, "bad IPC message ID" },
+ {kIOReturnUnsupported, "unsupported function" },
+ {kIOReturnVMError, "virtual memory error" },
+ {kIOReturnInternalError, "internal driver error" },
+ {kIOReturnIOError, "I/O error" },
+ {kIOReturnCannotLock, "cannot acquire lock" },
+ {kIOReturnNotOpen, "device is not open" },
+ {kIOReturnNotReadable, "device is not readable" },
+ {kIOReturnNotWritable, "device is not writeable" },
+ {kIOReturnNotAligned, "alignment error" },
+ {kIOReturnBadMedia, "media error" },
+ {kIOReturnStillOpen, "device is still open" },
+ {kIOReturnRLDError, "rld failure" },
+ {kIOReturnDMAError, "DMA failure" },
+ {kIOReturnBusy, "device is busy" },
+ {kIOReturnTimeout, "I/O timeout" },
+ {kIOReturnOffline, "device is offline" },
+ {kIOReturnNotReady, "device is not ready" },
+ {kIOReturnNotAttached, "device/channel is not attached" },
+ {kIOReturnNoChannels, "no DMA channels available" },
+ {kIOReturnNoSpace, "no space for data" },
+ {kIOReturnPortExists, "device port already exists" },
+ {kIOReturnCannotWire, "cannot wire physical memory" },
+ {kIOReturnNoInterrupt, "no interrupt attached" },
+ {kIOReturnNoFrames, "no DMA frames enqueued" },
+ {kIOReturnMessageTooLarge, "message is too large" },
+ {kIOReturnNotPermitted, "operation is not permitted" },
+ {kIOReturnNoPower, "device is without power" },
+ {kIOReturnNoMedia, "media is not present" },
+ {kIOReturnUnformattedMedia, "media is not formatted" },
+ {kIOReturnUnsupportedMode, "unsupported mode" },
+ {kIOReturnUnderrun, "data underrun" },
+ {kIOReturnOverrun, "data overrun" },
+ {kIOReturnDeviceError, "device error" },
+ {kIOReturnNoCompletion, "no completion routine" },
+ {kIOReturnAborted, "operation was aborted" },
+ {kIOReturnNoBandwidth, "bus bandwidth would be exceeded" },
+ {kIOReturnNotResponding, "device is not responding" },
+ {kIOReturnInvalid, "unanticipated driver error" },
+ {0, NULL }
+ };
+
+ return IOFindNameForValue(rtn, IOReturn_values);
+}
+
+/*
+ * Convert an IOReturn to an errno.
+ */
+int
+IOService::errnoFromReturn( IOReturn rtn )
+{
+ if (unix_err(err_get_code(rtn)) == rtn) {
+ return err_get_code(rtn);
+ }
+
+ switch (rtn) {
+ // (obvious match)
+ case kIOReturnSuccess:
+ return 0;
+ case kIOReturnNoMemory:
+ return ENOMEM;
+ case kIOReturnNoDevice:
+ return ENXIO;
+ case kIOReturnVMError:
+ return EFAULT;
+ case kIOReturnNotPermitted:
+ return EPERM;
+ case kIOReturnNotPrivileged:
+ return EACCES;
+ case kIOReturnIOError:
+ return EIO;
+ case kIOReturnNotWritable:
+ return EROFS;
+ case kIOReturnBadArgument:
+ return EINVAL;
+ case kIOReturnUnsupported:
+ return ENOTSUP;
+ case kIOReturnBusy:
+ return EBUSY;
+ case kIOReturnNoPower:
+ return EPWROFF;
+ case kIOReturnDeviceError:
+ return EDEVERR;
+ case kIOReturnTimeout:
+ return ETIMEDOUT;
+ case kIOReturnMessageTooLarge:
+ return EMSGSIZE;
+ case kIOReturnNoSpace:
+ return ENOSPC;
+ case kIOReturnCannotLock:
+ return ENOLCK;
+
+ // (best match)
+ case kIOReturnBadMessageID:
+ case kIOReturnNoCompletion:
+ case kIOReturnNotAligned:
+ return EINVAL;
+ case kIOReturnNotReady:
+ return EBUSY;
+ case kIOReturnRLDError:
+ return EBADMACHO;
+ case kIOReturnPortExists:
+ case kIOReturnStillOpen:
+ return EEXIST;
+ case kIOReturnExclusiveAccess:
+ case kIOReturnLockedRead:
+ case kIOReturnLockedWrite:
+ case kIOReturnNotOpen:
+ case kIOReturnNotReadable:
+ return EACCES;
+ case kIOReturnCannotWire:
+ case kIOReturnNoResources:
+ return ENOMEM;
+ case kIOReturnAborted:
+ case kIOReturnOffline:
+ case kIOReturnNotResponding:
+ return EBUSY;
+ case kIOReturnBadMedia:
+ case kIOReturnNoMedia:
+ case kIOReturnNotAttached:
+ case kIOReturnUnformattedMedia:
+ return ENXIO; // (media error)
+ case kIOReturnDMAError:
+ case kIOReturnOverrun:
+ case kIOReturnUnderrun:
+ return EIO; // (transfer error)
+ case kIOReturnNoBandwidth:
+ case kIOReturnNoChannels:
+ case kIOReturnNoFrames:
+ case kIOReturnNoInterrupt:
+ return EIO; // (hardware error)
+ case kIOReturnError:
+ case kIOReturnInternalError:
+ case kIOReturnInvalid:
+ return EIO; // (generic error)
+ case kIOReturnIPCError:
+ return EIO; // (ipc error)
+ default:
+ return EIO; // (all other errors)
+ }
+}
+
+IOReturn
+IOService::message( UInt32 type, IOService * provider,
+ void * argument )
+{
+ /*
+ * Generic entry point for calls from the provider. A return value of
+ * kIOReturnSuccess indicates that the message was received, and where
+ * applicable, that it was successful.
+ */
+
+ return kIOReturnUnsupported;
+}
+
+/*
+ * Device memory
+ */
+
+IOItemCount
+IOService::getDeviceMemoryCount( void )
+{
+ OSArray * array;
+ IOItemCount count;
+
+ array = OSDynamicCast( OSArray, getProperty( gIODeviceMemoryKey));
+ if (array) {
+ count = array->getCount();
+ } else {
+ count = 0;
+ }
+
+ return count;
+}
+
+IODeviceMemory *
+IOService::getDeviceMemoryWithIndex( unsigned int index )
+{
+ OSArray * array;
+ IODeviceMemory * range;
+
+ array = OSDynamicCast( OSArray, getProperty( gIODeviceMemoryKey));
+ if (array) {
+ range = (IODeviceMemory *) array->getObject( index );
+ } else {
+ range = NULL;
+ }
+
+ return range;
+}
+
+IOMemoryMap *
+IOService::mapDeviceMemoryWithIndex( unsigned int index,
+ IOOptionBits options )
+{
+ IODeviceMemory * range;
+ IOMemoryMap * map;
+
+ range = getDeviceMemoryWithIndex( index );
+ if (range) {
+ map = range->map( options );
+ } else {
+ map = NULL;
+ }
+
+ return map;
+}
+
+OSArray *
+IOService::getDeviceMemory( void )
+{
+ return OSDynamicCast( OSArray, getProperty( gIODeviceMemoryKey));
+}
+
+
+void
+IOService::setDeviceMemory( OSArray * array )
+{
+ setProperty( gIODeviceMemoryKey, array);
+}
+
+static void
+requireMaxCpuDelay(IOService * service, UInt32 ns, UInt32 delayType)
+{
+ static const UInt kNoReplace = -1U; // Must be an illegal index
+ UInt replace = kNoReplace;
+ bool setCpuDelay = false;
+
+ IORecursiveLockLock(sCpuDelayLock);
+
+ UInt count = sCpuDelayData->getLength() / sizeof(CpuDelayEntry);
+ CpuDelayEntry *entries = (CpuDelayEntry *) sCpuDelayData->getBytesNoCopy();
+ IOService * holder = NULL;
+
+ if (ns) {
+ const CpuDelayEntry ne = {service, ns, delayType};
+ holder = service;
+ // Set maximum delay.
+ for (UInt i = 0; i < count; i++) {
+ IOService *thisService = entries[i].fService;
+ bool sameType = (delayType == entries[i].fDelayType);
+ if ((service == thisService) && sameType) {
+ replace = i;
+ } else if (!thisService) {
+ if (kNoReplace == replace) {
+ replace = i;
+ }
+ } else if (sameType) {
+ const UInt32 thisMax = entries[i].fMaxDelay;
+ if (thisMax < ns) {
+ ns = thisMax;
+ holder = thisService;
+ }
+ }
+ }
+
+ setCpuDelay = true;
+ if (kNoReplace == replace) {
+ sCpuDelayData->appendBytes(&ne, sizeof(ne));
+ } else {
+ entries[replace] = ne;
+ }
+ } else {
+ ns = -1U; // Set to max unsigned, i.e. no restriction
+
+ for (UInt i = 0; i < count; i++) {
+ // Clear a maximum delay.
+ IOService *thisService = entries[i].fService;
+ if (thisService && (delayType == entries[i].fDelayType)) {
+ UInt32 thisMax = entries[i].fMaxDelay;
+ if (service == thisService) {
+ replace = i;
+ } else if (thisMax < ns) {
+ ns = thisMax;
+ holder = thisService;
+ }
+ }
+ }
+
+ // Check if entry found
+ if (kNoReplace != replace) {
+ entries[replace].fService = NULL; // Null the entry
+ setCpuDelay = true;
+ }
+ }
+
+ if (setCpuDelay) {
+ if (holder && debug_boot_arg) {
+ strlcpy(sCPULatencyHolderName[delayType], holder->getName(), sizeof(sCPULatencyHolderName[delayType]));
+ }
+
+ // Must be safe to call from locked context
+ if (delayType == kCpuDelayBusStall) {
+#if defined(__x86_64__)
+ ml_set_maxbusdelay(ns);
+#endif /* defined(__x86_64__) */
+ }
+#if defined(__x86_64__)
+ else if (delayType == kCpuDelayInterrupt) {
+ ml_set_maxintdelay(ns);
+ }
+#endif /* defined(__x86_64__) */
+ sCPULatencyHolder[delayType]->setValue(holder ? holder->getRegistryEntryID() : 0);
+ sCPULatencySet[delayType]->setValue(ns);
+
+ OSArray * handlers = sCpuLatencyHandlers[delayType];
+ IOService * target;
+ if (handlers) {
+ for (unsigned int idx = 0;
+ (target = (IOService *) handlers->getObject(idx));
+ idx++) {
+ target->callPlatformFunction(sCPULatencyFunctionName[delayType], false,
+ (void *) (uintptr_t) ns, holder,
+ NULL, NULL);
+ }
+ }
+ }
+
+ IORecursiveLockUnlock(sCpuDelayLock);
+}
+
+static IOReturn
+setLatencyHandler(UInt32 delayType, IOService * target, bool enable)
+{
+ IOReturn result = kIOReturnNotFound;
+ OSArray * array;
+ unsigned int idx;
+
+ IORecursiveLockLock(sCpuDelayLock);
+
+ do{
+ if (enable && !sCpuLatencyHandlers[delayType]) {
+ sCpuLatencyHandlers[delayType] = OSArray::withCapacity(4);
+ }
+ array = sCpuLatencyHandlers[delayType];
+ if (!array) {
+ break;
+ }
+ idx = array->getNextIndexOfObject(target, 0);
+ if (!enable) {
+ if (-1U != idx) {
+ array->removeObject(idx);
+ result = kIOReturnSuccess;
+ }
+ } else {
+ if (-1U != idx) {
+ result = kIOReturnExclusiveAccess;
+ break;
+ }
+ array->setObject(target);
+
+ UInt count = sCpuDelayData->getLength() / sizeof(CpuDelayEntry);
+ CpuDelayEntry *entries = (CpuDelayEntry *) sCpuDelayData->getBytesNoCopy();
+ UInt32 ns = -1U; // Set to max unsigned, i.e. no restriction
+ IOService * holder = NULL;
+
+ for (UInt i = 0; i < count; i++) {
+ if (entries[i].fService
+ && (delayType == entries[i].fDelayType)
+ && (entries[i].fMaxDelay < ns)) {
+ ns = entries[i].fMaxDelay;
+ holder = entries[i].fService;
+ }
+ }
+ target->callPlatformFunction(sCPULatencyFunctionName[delayType], false,
+ (void *) (uintptr_t) ns, holder,
+ NULL, NULL);
+ result = kIOReturnSuccess;
+ }
+ }while (false);
+
+ IORecursiveLockUnlock(sCpuDelayLock);
+
+ return result;
+}
+
+IOReturn
+IOService::requireMaxBusStall(UInt32 ns)
+{
+#if !defined(__x86_64__)
+ switch (ns) {
+ case kIOMaxBusStall40usec:
+ case kIOMaxBusStall30usec:
+ case kIOMaxBusStall25usec:
+ case kIOMaxBusStall20usec:
+ case kIOMaxBusStall10usec:
+ case kIOMaxBusStall5usec:
+ case kIOMaxBusStallNone:
+ break;
+ default:
+ return kIOReturnBadArgument;
+ }
+#endif /* !defined(__x86_64__) */
+ requireMaxCpuDelay(this, ns, kCpuDelayBusStall);
+ return kIOReturnSuccess;
+}
+
+IOReturn
+IOService::requireMaxInterruptDelay(uint32_t ns)
+{
+#if defined(__x86_64__)
+ requireMaxCpuDelay(this, ns, kCpuDelayInterrupt);
+ return kIOReturnSuccess;
+#else /* defined(__x86_64__) */
+ return kIOReturnUnsupported;
+#endif /* defined(__x86_64__) */
+}
+
+/*
+ * Device interrupts
+ */
+
+IOReturn
+IOService::resolveInterrupt(IOService *nub, int source)
+{
+ IOInterruptController *interruptController;
+ OSArray *array;
+ OSData *data;
+ OSSymbol *interruptControllerName;
+ unsigned int numSources;
+ IOInterruptSource *interruptSources;
+
+ // Get the parents list from the nub.
+ array = OSDynamicCast(OSArray, nub->getProperty(gIOInterruptControllersKey));
+ if (array == NULL) {
+ return kIOReturnNoResources;
+ }
+
+ // Allocate space for the IOInterruptSources if needed... then return early.
+ if (nub->_interruptSources == NULL) {
+ numSources = array->getCount();
+ interruptSources = (IOInterruptSource *)IOMalloc(
+ numSources * sizeofAllIOInterruptSource);
+ if (interruptSources == NULL) {
+ return kIOReturnNoMemory;
+ }
+
+ bzero(interruptSources, numSources * sizeofAllIOInterruptSource);
+
+ nub->_numInterruptSources = numSources;
+ nub->_interruptSources = interruptSources;
+ return kIOReturnSuccess;
+ }
+
+ interruptControllerName = OSDynamicCast(OSSymbol, array->getObject(source));
+ if (interruptControllerName == NULL) {
+ return kIOReturnNoResources;
+ }
+
+ interruptController = getPlatform()->lookUpInterruptController(interruptControllerName);
+ if (interruptController == NULL) {
+ return kIOReturnNoResources;
+ }
+
+ // Get the interrupt numbers from the nub.
+ array = OSDynamicCast(OSArray, nub->getProperty(gIOInterruptSpecifiersKey));
+ if (array == NULL) {
+ return kIOReturnNoResources;
+ }
+ data = OSDynamicCast(OSData, array->getObject(source));
+ if (data == NULL) {
+ return kIOReturnNoResources;
+ }
+
+ // Set the interruptController and interruptSource in the nub's table.
+ interruptSources = nub->_interruptSources;
+ interruptSources[source].interruptController = interruptController;
+ interruptSources[source].vectorData = data;
+
+ return kIOReturnSuccess;
+}
+
+IOReturn
+IOService::lookupInterrupt(int source, bool resolve, IOInterruptController **interruptController)
+{
+ IOReturn ret;
+
+ /* Make sure the _interruptSources are set */
+ if (_interruptSources == NULL) {
+ ret = resolveInterrupt(this, source);
+ if (ret != kIOReturnSuccess) {
+ return ret;
+ }
+ }
+
+ /* Make sure the local source number is valid */
+ if ((source < 0) || (source >= _numInterruptSources)) {
+ return kIOReturnNoInterrupt;
+ }
+
+ /* Look up the contoller for the local source */
+ *interruptController = _interruptSources[source].interruptController;
+
+ if (*interruptController == NULL) {
+ if (!resolve) {
+ return kIOReturnNoInterrupt;
+ }
+
+ /* Try to resolve the interrupt */
+ ret = resolveInterrupt(this, source);
+ if (ret != kIOReturnSuccess) {
+ return ret;
+ }
+
+ *interruptController = _interruptSources[source].interruptController;
+ }
+
+ return kIOReturnSuccess;
+}
+
+IOReturn
+IOService::registerInterrupt(int source, OSObject *target,
+ IOInterruptAction handler,
+ void *refCon)
+{
+ IOInterruptController *interruptController;
+ IOReturn ret;
+
+ ret = lookupInterrupt(source, true, &interruptController);
+ if (ret != kIOReturnSuccess) {
+ return ret;
+ }
+
+ /* Register the source */
+ return interruptController->registerInterrupt(this, source, target,
+ (IOInterruptHandler)handler,
+ refCon);
+}
+
+static void
+IOServiceInterruptActionToBlock( OSObject * target, void * refCon,
+ IOService * nub, int source )
+{
+ ((IOInterruptActionBlock)(refCon))(nub, source);
+}
+
+IOReturn
+IOService::registerInterruptBlock(int source, OSObject *target,
+ IOInterruptActionBlock handler)
+{
+ IOReturn ret;
+ void * block;
+
+ block = Block_copy(handler);
+ if (!block) {
+ return kIOReturnNoMemory;
+ }
+
+ ret = registerInterrupt(source, target, &IOServiceInterruptActionToBlock, block);
+ if (kIOReturnSuccess != ret) {
+ Block_release(block);
+ return ret;
+ }
+ _interruptSourcesPrivate(this)[source].vectorBlock = block;
+
+ return ret;
+}
+
+IOReturn
+IOService::unregisterInterrupt(int source)
+{
+ IOReturn ret;
+ IOInterruptController *interruptController;
+ void *block;
+
+ ret = lookupInterrupt(source, false, &interruptController);
+ if (ret != kIOReturnSuccess) {
+ return ret;
+ }
+
+ /* Unregister the source */
+ block = _interruptSourcesPrivate(this)[source].vectorBlock;
+ ret = interruptController->unregisterInterrupt(this, source);
+ if ((kIOReturnSuccess == ret) && (block = _interruptSourcesPrivate(this)[source].vectorBlock)) {
+ _interruptSourcesPrivate(this)[source].vectorBlock = NULL;
+ Block_release(block);
+ }
+
+ return ret;
+}
+
+IOReturn
+IOService::addInterruptStatistics(IOInterruptAccountingData * statistics, int source)
+{
+ IOReportLegend * legend = NULL;
+ IOInterruptAccountingData * oldValue = NULL;
+ IOInterruptAccountingReporter * newArray = NULL;
+ char subgroupName[64];
+ int newArraySize = 0;
+ int i = 0;
+
+ if (source < 0) {
+ return kIOReturnBadArgument;
+ }
+
+ /*
+ * We support statistics on a maximum of 256 interrupts per nub; if a nub
+ * has more than 256 interrupt specifiers associated with it, and tries
+ * to register a high interrupt index with interrupt accounting, panic.
+ * Having more than 256 interrupts associated with a single nub is
+ * probably a sign that something fishy is going on.
+ */
+ if (source > IA_INDEX_MAX) {
+ panic("addInterruptStatistics called for an excessively large index (%d)", source);
+ }
+
+ /*
+ * TODO: This is ugly (wrapping a lock around an allocation). I'm only
+ * leaving it as is because the likelihood of contention where we are
+ * actually growing the array is minimal (we would realistically need
+ * to be starting a driver for the first time, with an IOReporting
+ * client already in place). Nonetheless, cleanup that can be done
+ * to adhere to best practices; it'll make the code more complicated,
+ * unfortunately.
+ */
+ IOLockLock(reserved->interruptStatisticsLock);
+
+ /*
+ * Lazily allocate the statistics array.
+ */
+ if (!reserved->interruptStatisticsArray) {
+ reserved->interruptStatisticsArray = IONew(IOInterruptAccountingReporter, 1);
+ assert(reserved->interruptStatisticsArray);
+ reserved->interruptStatisticsArrayCount = 1;
+ bzero(reserved->interruptStatisticsArray, sizeof(*reserved->interruptStatisticsArray));
+ }
+
+ if (source >= reserved->interruptStatisticsArrayCount) {
+ /*
+ * We're still within the range of supported indices, but we are out
+ * of space in the current array. Do a nasty realloc (because
+ * IORealloc isn't a thing) here. We'll double the size with each
+ * reallocation.
+ *
+ * Yes, the "next power of 2" could be more efficient; but this will
+ * be invoked incredibly rarely. Who cares.
+ */
+ newArraySize = (reserved->interruptStatisticsArrayCount << 1);
+
+ while (newArraySize <= source) {
+ newArraySize = (newArraySize << 1);
+ }
+ newArray = IONew(IOInterruptAccountingReporter, newArraySize);
+
+ assert(newArray);
+
+ /*
+ * TODO: This even zeroes the memory it is about to overwrite.
+ * Shameful; fix it. Not particularly high impact, however.
+ */
+ bzero(newArray, newArraySize * sizeof(*newArray));
+ memcpy(newArray, reserved->interruptStatisticsArray, reserved->interruptStatisticsArrayCount * sizeof(*newArray));
+ IODelete(reserved->interruptStatisticsArray, IOInterruptAccountingReporter, reserved->interruptStatisticsArrayCount);
+ reserved->interruptStatisticsArray = newArray;
+ reserved->interruptStatisticsArrayCount = newArraySize;
+ }
+
+ if (!reserved->interruptStatisticsArray[source].reporter) {
+ /*
+ * We don't have a reporter associated with this index yet, so we
+ * need to create one.
+ */
+ /*
+ * TODO: Some statistics do in fact have common units (time); should this be
+ * split into separate reporters to communicate this?
+ */
+ reserved->interruptStatisticsArray[source].reporter = IOSimpleReporter::with(this, kIOReportCategoryPower, kIOReportUnitNone);
+
+ /*
+ * Each statistic is given an identifier based on the interrupt index (which
+ * should be unique relative to any single nub) and the statistic involved.
+ * We should now have a sane (small and positive) index, so start
+ * constructing the channels for statistics.
+ */
+ for (i = 0; i < IA_NUM_INTERRUPT_ACCOUNTING_STATISTICS; i++) {
+ /*
+ * TODO: Currently, this does not add channels for disabled statistics.
+ * Will this be confusing for clients? If so, we should just add the
+ * channels; we can avoid updating the channels even if they exist.
+ */
+ if (IA_GET_STATISTIC_ENABLED(i)) {
+ reserved->interruptStatisticsArray[source].reporter->addChannel(IA_GET_CHANNEL_ID(source, i), kInterruptAccountingStatisticNameArray[i]);
+ }
+ }
+
+ /*
+ * We now need to add the legend for this reporter to the registry.
+ */
+ OSObject * prop = copyProperty(kIOReportLegendKey);
+ legend = IOReportLegend::with(OSDynamicCast(OSArray, prop));
+ OSSafeReleaseNULL(prop);
+
+ /*
+ * Note that while we compose the subgroup name, we do not need to
+ * manage its lifecycle (the reporter will handle this).
+ */
+ snprintf(subgroupName, sizeof(subgroupName), "%s %d", getName(), source);
+ subgroupName[sizeof(subgroupName) - 1] = 0;
+ legend->addReporterLegend(reserved->interruptStatisticsArray[source].reporter, kInterruptAccountingGroupName, subgroupName);
+ setProperty(kIOReportLegendKey, legend->getLegend());
+ legend->release();
+
+ /*
+ * TODO: Is this a good idea? Probably not; my assumption is it opts
+ * all entities who register interrupts into public disclosure of all
+ * IOReporting channels. Unfortunately, this appears to be as fine
+ * grain as it gets.
+ */
+ setProperty(kIOReportLegendPublicKey, true);
+ }
+
+ /*
+ * Don't stomp existing entries. If we are about to, panic; this
+ * probably means we failed to tear down our old interrupt source
+ * correctly.
+ */
+ oldValue = reserved->interruptStatisticsArray[source].statistics;
+
+ if (oldValue) {
+ panic("addInterruptStatistics call for index %d would have clobbered existing statistics", source);
+ }
+
+ reserved->interruptStatisticsArray[source].statistics = statistics;
+
+ /*
+ * Inherit the reporter values for each statistic. The target may
+ * be torn down as part of the runtime of the service (especially
+ * for sleep/wake), so we inherit in order to avoid having values
+ * reset for no apparent reason. Our statistics are ultimately
+ * tied to the index and the sevice, not to an individual target,
+ * so we should maintain them accordingly.
+ */
+ interruptAccountingDataInheritChannels(reserved->interruptStatisticsArray[source].statistics, reserved->interruptStatisticsArray[source].reporter);
+
+ IOLockUnlock(reserved->interruptStatisticsLock);
+
+ return kIOReturnSuccess;
+}
+
+IOReturn
+IOService::removeInterruptStatistics(int source)
+{
+ IOInterruptAccountingData * value = NULL;
+
+ if (source < 0) {
+ return kIOReturnBadArgument;
+ }
+
+ IOLockLock(reserved->interruptStatisticsLock);
+
+ /*
+ * We dynamically grow the statistics array, so an excessively
+ * large index value has NEVER been registered. This either
+ * means our cap on the array size is too small (unlikely), or
+ * that we have been passed a corrupt index (this must be passed
+ * the plain index into the interrupt specifier list).
+ */
+ if (source >= reserved->interruptStatisticsArrayCount) {
+ panic("removeInterruptStatistics called for index %d, which was never registered", source);
+ }
+
+ assert(reserved->interruptStatisticsArray);
+
+ /*
+ * If there is no existing entry, we are most likely trying to
+ * free an interrupt owner twice, or we have corrupted the
+ * index value.
+ */
+ value = reserved->interruptStatisticsArray[source].statistics;
+
+ if (!value) {
+ panic("removeInterruptStatistics called for empty index %d", source);
+ }
+
+ /*
+ * We update the statistics, so that any delta with the reporter
+ * state is not lost.
+ */
+ interruptAccountingDataUpdateChannels(reserved->interruptStatisticsArray[source].statistics, reserved->interruptStatisticsArray[source].reporter);
+ reserved->interruptStatisticsArray[source].statistics = NULL;
+ IOLockUnlock(reserved->interruptStatisticsLock);
+
+ return kIOReturnSuccess;
+}
+
+IOReturn
+IOService::getInterruptType(int source, int *interruptType)
+{
+ IOInterruptController *interruptController;
+ IOReturn ret;
+
+ ret = lookupInterrupt(source, true, &interruptController);
+ if (ret != kIOReturnSuccess) {
+ return ret;
+ }
+
+ /* Return the type */
+ return interruptController->getInterruptType(this, source, interruptType);
+}
+
+IOReturn
+IOService::enableInterrupt(int source)
+{
+ IOInterruptController *interruptController;
+ IOReturn ret;
+
+ ret = lookupInterrupt(source, false, &interruptController);
+ if (ret != kIOReturnSuccess) {
+ return ret;
+ }
+
+ /* Enable the source */
+ return interruptController->enableInterrupt(this, source);
+}
+
+IOReturn
+IOService::disableInterrupt(int source)
+{
+ IOInterruptController *interruptController;
+ IOReturn ret;
+
+ ret = lookupInterrupt(source, false, &interruptController);
+ if (ret != kIOReturnSuccess) {
+ return ret;
+ }
+
+ /* Disable the source */
+ return interruptController->disableInterrupt(this, source);
+}
+
+IOReturn
+IOService::causeInterrupt(int source)
+{
+ IOInterruptController *interruptController;
+ IOReturn ret;
+
+ ret = lookupInterrupt(source, false, &interruptController);
+ if (ret != kIOReturnSuccess) {
+ return ret;
+ }
+
+ /* Cause an interrupt for the source */
+ return interruptController->causeInterrupt(this, source);
+}
+
+IOReturn
+IOService::configureReport(IOReportChannelList *channelList,
+ IOReportConfigureAction action,
+ void *result,
+ void *destination)
+{
+ unsigned cnt;
+
+ for (cnt = 0; cnt < channelList->nchannels; cnt++) {
+ if (channelList->channels[cnt].channel_id == kPMPowerStatesChID) {
+ if (pwrMgt) {
+ configurePowerStatesReport(action, result);
+ } else {
+ return kIOReturnUnsupported;
+ }
+ } else if (channelList->channels[cnt].channel_id == kPMCurrStateChID) {
+ if (pwrMgt) {
+ configureSimplePowerReport(action, result);
+ } else {
+ return kIOReturnUnsupported;
+ }
+ }
+ }
+
+ IOLockLock(reserved->interruptStatisticsLock);
+
+ /* The array count is signed (because the interrupt indices are signed), hence the cast */
+ for (cnt = 0; cnt < (unsigned) reserved->interruptStatisticsArrayCount; cnt++) {
+ if (reserved->interruptStatisticsArray[cnt].reporter) {
+ /*
+ * If the reporter is currently associated with the statistics
+ * for an event source, we may need to update the reporter.
+ */
+ if (reserved->interruptStatisticsArray[cnt].statistics) {
+ interruptAccountingDataUpdateChannels(reserved->interruptStatisticsArray[cnt].statistics, reserved->interruptStatisticsArray[cnt].reporter);
+ }
+
+ reserved->interruptStatisticsArray[cnt].reporter->configureReport(channelList, action, result, destination);
+ }
+ }
+
+ IOLockUnlock(reserved->interruptStatisticsLock);
+
+ return kIOReturnSuccess;
+}
+
+IOReturn
+IOService::updateReport(IOReportChannelList *channelList,
+ IOReportUpdateAction action,
+ void *result,
+ void *destination)
+{
+ unsigned cnt;
+
+ for (cnt = 0; cnt < channelList->nchannels; cnt++) {
+ if (channelList->channels[cnt].channel_id == kPMPowerStatesChID) {
+ if (pwrMgt) {
+ updatePowerStatesReport(action, result, destination);
+ } else {
+ return kIOReturnUnsupported;
+ }
+ } else if (channelList->channels[cnt].channel_id == kPMCurrStateChID) {
+ if (pwrMgt) {
+ updateSimplePowerReport(action, result, destination);
+ } else {
+ return kIOReturnUnsupported;
+ }
+ }
+ }
+
+ IOLockLock(reserved->interruptStatisticsLock);
+
+ /* The array count is signed (because the interrupt indices are signed), hence the cast */
+ for (cnt = 0; cnt < (unsigned) reserved->interruptStatisticsArrayCount; cnt++) {
+ if (reserved->interruptStatisticsArray[cnt].reporter) {
+ /*
+ * If the reporter is currently associated with the statistics
+ * for an event source, we need to update the reporter.
+ */
+ if (reserved->interruptStatisticsArray[cnt].statistics) {
+ interruptAccountingDataUpdateChannels(reserved->interruptStatisticsArray[cnt].statistics, reserved->interruptStatisticsArray[cnt].reporter);
+ }
+
+ reserved->interruptStatisticsArray[cnt].reporter->updateReport(channelList, action, result, destination);
+ }
+ }
+
+ IOLockUnlock(reserved->interruptStatisticsLock);
+
+ return kIOReturnSuccess;
+}
+
+uint64_t
+IOService::getAuthorizationID( void )
+{
+ return reserved->authorizationID;
+}
+
+IOReturn
+IOService::setAuthorizationID( uint64_t authorizationID )
+{
+ OSObject * entitlement;
+ IOReturn status;
+
+ entitlement = IOUserClient::copyClientEntitlement( current_task(), "com.apple.private.iokit.IOServiceSetAuthorizationID" );
+
+ if (entitlement) {
+ if (entitlement == kOSBooleanTrue) {
+ reserved->authorizationID = authorizationID;
+
+ status = kIOReturnSuccess;
+ } else {
+ status = kIOReturnNotPrivileged;
+ }
+
+ entitlement->release();
+ } else {
+ status = kIOReturnNotPrivileged;
+ }
+
+ return status;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+