+void
+IOService::publishResource( const char * key, OSObject * value )
+{
+ const OSSymbol * sym;
+
+ if ((sym = OSSymbol::withCString( key))) {
+ publishResource( sym, value);
+ sym->release();
+ }
+}
+
+void
+IOService::publishResource( const OSSymbol * key, OSObject * value )
+{
+ if (NULL == value) {
+ value = (OSObject *) gIOServiceKey;
+ }
+
+ gIOResources->setProperty( key, value);
+
+ if (IORecursiveLockHaveLock( gNotificationLock)) {
+ return;
+ }
+
+ gIOResourceGenerationCount++;
+ gIOResources->registerService();
+}
+
+void
+IOService::publishUserResource( const OSSymbol * key, OSObject * value )
+{
+ if (NULL == value) {
+ value = (OSObject *) gIOServiceKey;
+ }
+
+ gIOUserResources->setProperty( key, value);
+
+ if (IORecursiveLockHaveLock( gNotificationLock)) {
+ return;
+ }
+
+ gIOResourceGenerationCount++;
+ gIOUserResources->registerService();
+}
+
+bool
+IOService::addNeededResource( const char * key )
+{
+ OSObject * resourcesProp;
+ OSSet * set;
+ OSString * newKey;
+ bool ret;
+
+ resourcesProp = copyProperty( gIOResourceMatchKey );
+ if (!resourcesProp) {
+ return false;
+ }
+
+ newKey = OSString::withCString( key );
+ if (!newKey) {
+ resourcesProp->release();
+ return false;
+ }
+
+ set = OSDynamicCast( OSSet, resourcesProp );
+ if (!set) {
+ set = OSSet::withCapacity( 1 );
+ if (set) {
+ set->setObject( resourcesProp );
+ }
+ } else {
+ set->retain();
+ }
+
+ set->setObject( newKey );
+ newKey->release();
+ ret = setProperty( gIOResourceMatchKey, set );
+ set->release();
+ resourcesProp->release();
+
+ return ret;
+}
+
+bool
+IOService::checkResource( OSObject * matching )
+{
+ OSString * str;
+ OSDictionary * table;
+
+ if ((str = OSDynamicCast( OSString, matching ))) {
+ if (gIOResources->getProperty( str )) {
+ return true;
+ }
+ }
+
+ if (str) {
+ table = resourceMatching( str );
+ } else if ((table = OSDynamicCast( OSDictionary, matching ))) {
+ table->retain();
+ } else {
+ IOLog("%s: Can't match using: %s\n", getName(),
+ matching->getMetaClass()->getClassName());
+ /* false would stall forever */
+ return true;
+ }
+
+ if (gIOKitDebug & kIOLogConfig) {
+ LOG("config(%p): stalling %s\n", IOSERVICE_OBFUSCATE(IOThreadSelf()), getName());
+ }
+
+ waitForService( table );
+
+ if (gIOKitDebug & kIOLogConfig) {
+ LOG("config(%p): waking\n", IOSERVICE_OBFUSCATE(IOThreadSelf()));
+ }
+
+ return true;
+}
+
+bool
+IOService::checkResources( void )
+{
+ OSObject * resourcesProp;
+ OSSet * set;
+ OSIterator * iter;
+ bool ok;
+
+ resourcesProp = copyProperty( gIOResourceMatchKey );
+ if (NULL == resourcesProp) {
+ return true;
+ }
+
+ if ((set = OSDynamicCast( OSSet, resourcesProp ))) {
+ iter = OSCollectionIterator::withCollection( set );
+ ok = (NULL != iter);
+ while (ok && (resourcesProp = iter->getNextObject())) {
+ ok = checkResource( resourcesProp );
+ }
+ if (iter) {
+ iter->release();
+ }
+ } else {
+ ok = checkResource( resourcesProp );
+ }
+
+ OSSafeReleaseNULL(resourcesProp);
+
+ return ok;
+}
+
+
+void
+_IOConfigThread::configThread( int configThreadId )
+{
+ _IOConfigThread * inst;
+
+ do {
+ if (!(inst = new _IOConfigThread)) {
+ continue;
+ }
+ if (!inst->init()) {
+ continue;
+ }
+ thread_t thread;
+ if (KERN_SUCCESS != kernel_thread_start(&_IOConfigThread::main, inst, &thread)) {
+ continue;
+ }
+
+ char threadName[MAXTHREADNAMESIZE];
+ snprintf(threadName, sizeof(threadName), "IOConfigThread_%d", configThreadId);
+ thread_set_thread_name(thread, threadName);
+ thread_deallocate(thread);
+
+ return;
+ } while (false);
+
+ if (inst) {
+ inst->release();
+ }
+
+ return;
+}
+
+void
+IOService::doServiceMatch( IOOptionBits options )
+{
+ _IOServiceNotifier * notify;
+ OSIterator * iter;
+ OSOrderedSet * matches;
+ OSArray * resourceKeys = NULL;
+ SInt32 catalogGeneration;
+ bool keepGuessing = true;
+ bool reRegistered = true;
+ bool didRegister;
+ OSArray * notifiers[2] = {NULL};
+
+// job->nub->deliverNotification( gIOPublishNotification,
+// kIOServiceRegisteredState, 0xffffffff );
+
+ while (keepGuessing) {
+ matches = gIOCatalogue->findDrivers( this, &catalogGeneration );
+ // the matches list should always be created by findDrivers()
+ if (matches) {
+ lockForArbitration();
+ if (0 == (__state[0] & kIOServiceFirstPublishState)) {
+ getMetaClass()->addInstance(this);
+ notifiers[0] = copyNotifiers(gIOFirstPublishNotification,
+ kIOServiceFirstPublishState, 0xffffffff );
+ }
+ LOCKREADNOTIFY();
+ __state[1] &= ~kIOServiceNeedConfigState;
+ __state[1] |= kIOServiceConfigState | kIOServiceConfigRunning;
+ didRegister = (0 == (kIOServiceRegisteredState & __state[0]));
+ __state[0] |= kIOServiceRegisteredState;
+
+ keepGuessing &= (0 == (__state[0] & kIOServiceInactiveState));
+ if (reRegistered && keepGuessing) {
+ iter = OSCollectionIterator::withCollection((OSOrderedSet *)
+ gNotifications->getObject( gIOPublishNotification ));
+ if (iter) {
+ while ((notify = (_IOServiceNotifier *)
+ iter->getNextObject())) {
+ if (matchPassive(notify->matching, 0)
+ && (kIOServiceNotifyEnable & notify->state)) {
+ matches->setObject( notify );
+ }
+ }
+ iter->release();
+ }
+ }
+
+ UNLOCKNOTIFY();
+ unlockForArbitration();
+ invokeNotifiers(¬ifiers[0]);
+
+ if (keepGuessing && matches->getCount() && (kIOReturnSuccess == getResources())) {
+ if ((this == gIOResources) || (this == gIOUserResources)) {
+ if (resourceKeys) {
+ resourceKeys->release();
+ }
+ resourceKeys = copyPropertyKeys();
+ }
+ probeCandidates( matches );
+ } else {
+ matches->release();
+ }
+ }
+
+ lockForArbitration();
+ reRegistered = (0 != (__state[1] & kIOServiceNeedConfigState));
+ keepGuessing =
+ (reRegistered || (catalogGeneration !=
+ gIOCatalogue->getGenerationCount()))
+ && (0 == (__state[0] & kIOServiceInactiveState));
+
+ if (keepGuessing) {
+ unlockForArbitration();
+ }
+ }
+
+ if ((0 == (__state[0] & kIOServiceInactiveState))
+ && (0 == (__state[1] & kIOServiceModuleStallState))) {
+ if (resourceKeys) {
+ setProperty(gIOResourceMatchedKey, resourceKeys);
+ }
+
+ notifiers[0] = copyNotifiers(gIOMatchedNotification,
+ kIOServiceMatchedState, 0xffffffff);
+ if (0 == (__state[0] & kIOServiceFirstMatchState)) {
+ notifiers[1] = copyNotifiers(gIOFirstMatchNotification,
+ kIOServiceFirstMatchState, 0xffffffff);
+ }
+ }
+
+ __state[1] &= ~kIOServiceConfigRunning;
+ unlockForArbitration();
+
+ if (resourceKeys) {
+ resourceKeys->release();
+ }
+
+ invokeNotifiers(¬ifiers[0]);
+ invokeNotifiers(¬ifiers[1]);
+
+ lockForArbitration();
+ __state[1] &= ~kIOServiceConfigState;
+ scheduleTerminatePhase2();
+
+ _adjustBusy( -1 );
+ unlockForArbitration();
+}
+
+UInt32
+IOService::_adjustBusy( SInt32 delta )
+{
+ IOService * next;
+ UInt32 count;
+ UInt32 result;
+ bool wasQuiet, nowQuiet, needWake;
+
+ next = this;
+ result = __state[1] & kIOServiceBusyStateMask;
+
+ if (delta) {
+ do {
+ if (next != this) {
+ next->lockForArbitration();
+ }
+ count = next->__state[1] & kIOServiceBusyStateMask;
+ wasQuiet = (0 == count);
+ if (((delta < 0) && wasQuiet) || ((delta > 0) && (kIOServiceBusyMax == count))) {
+ OSReportWithBacktrace("%s: bad busy count (%d,%d)\n", next->getName(), count, delta);
+ } else {
+ count += delta;
+ }
+ next->__state[1] = (next->__state[1] & ~kIOServiceBusyStateMask) | count;
+ nowQuiet = (0 == count);
+ needWake = (0 != (kIOServiceBusyWaiterState & next->__state[1]));
+
+ if (needWake) {
+ next->__state[1] &= ~kIOServiceBusyWaiterState;
+ IOLockLock( gIOServiceBusyLock );
+ thread_wakeup((event_t) next);
+ IOLockUnlock( gIOServiceBusyLock );
+ }
+ if (next != this) {
+ next->unlockForArbitration();
+ }
+
+ if ((wasQuiet || nowQuiet)) {
+ uint64_t regID = next->getRegistryEntryID();
+ IOServiceTrace(
+ ((wasQuiet /*nowBusy*/) ? IOSERVICE_BUSY : IOSERVICE_NONBUSY),
+ (uintptr_t) regID,
+ (uintptr_t) (regID >> 32),
+ (uintptr_t) next,
+ 0);
+
+ if (wasQuiet) {
+ next->__timeBusy = mach_absolute_time();
+ } else {
+ next->__accumBusy += mach_absolute_time() - next->__timeBusy;
+ next->__timeBusy = 0;
+ }
+
+ MessageClientsContext context;
+
+ context.service = next;
+ context.type = kIOMessageServiceBusyStateChange;
+ context.argument = (void *) wasQuiet; /*nowBusy*/
+ context.argSize = 0;
+
+ applyToInterestNotifiers( next, gIOBusyInterest,
+ &messageClientsApplier, &context );
+
+#if !NO_KEXTD
+ if (nowQuiet && (next == gIOServiceRoot)) {
+ OSKext::considerUnloads();
+ IOServiceTrace(IOSERVICE_REGISTRY_QUIET, 0, 0, 0, 0);
+ }
+#endif
+ }
+
+ delta = nowQuiet ? -1 : +1;
+ } while ((wasQuiet || nowQuiet) && (next = next->getProvider()));
+ }
+
+ return result;
+}
+
+void
+IOService::adjustBusy( SInt32 delta )
+{
+ lockForArbitration();
+ _adjustBusy( delta );
+ unlockForArbitration();
+}
+
+uint64_t
+IOService::getAccumulatedBusyTime( void )
+{
+ uint64_t accumBusy = __accumBusy;
+ uint64_t timeBusy = __timeBusy;
+ uint64_t nano;
+
+ do{
+ accumBusy = __accumBusy;
+ timeBusy = __timeBusy;
+ if (timeBusy) {
+ accumBusy += mach_absolute_time() - timeBusy;
+ }
+ }while (timeBusy != __timeBusy);
+
+ absolutetime_to_nanoseconds(*(AbsoluteTime *)&accumBusy, &nano);
+
+ return nano;
+}
+
+UInt32
+IOService::getBusyState( void )
+{
+ return __state[1] & kIOServiceBusyStateMask;
+}
+
+IOReturn
+IOService::waitForState( UInt32 mask, UInt32 value,
+ mach_timespec_t * timeout )
+{
+ panic("waitForState");
+ return kIOReturnUnsupported;
+}
+
+IOReturn
+IOService::waitForState( UInt32 mask, UInt32 value,
+ uint64_t timeout )
+{
+ bool wait;
+ int waitResult = THREAD_AWAKENED;
+ bool computeDeadline = true;
+ AbsoluteTime abstime;
+
+ do {
+ lockForArbitration();
+ IOLockLock( gIOServiceBusyLock );
+ wait = (value != (__state[1] & mask));
+ if (wait) {
+ __state[1] |= kIOServiceBusyWaiterState;
+ unlockForArbitration();
+ if (timeout != UINT64_MAX) {
+ if (computeDeadline) {
+ AbsoluteTime nsinterval;
+ nanoseconds_to_absolutetime(timeout, &nsinterval );
+ clock_absolutetime_interval_to_deadline(nsinterval, &abstime);
+ computeDeadline = false;
+ }
+ assert_wait_deadline((event_t)this, THREAD_UNINT, __OSAbsoluteTime(abstime));
+ } else {
+ assert_wait((event_t)this, THREAD_UNINT );
+ }
+ } else {
+ unlockForArbitration();
+ }
+ IOLockUnlock( gIOServiceBusyLock );
+ if (wait) {
+ waitResult = thread_block(THREAD_CONTINUE_NULL);
+ }
+ } while (wait && (waitResult != THREAD_TIMED_OUT));
+
+ if (waitResult == THREAD_TIMED_OUT) {
+ return kIOReturnTimeout;
+ } else {
+ return kIOReturnSuccess;
+ }
+}
+
+IOReturn
+IOService::waitQuiet( uint64_t timeout )
+{
+ IOReturn ret;
+ uint32_t loops;
+ char * string = NULL;
+ char * panicString = NULL;
+ size_t len;
+ size_t panicStringLen;
+ uint64_t time;
+ uint64_t nano;
+ bool kextdWait;
+ bool dopanic;
+
+#if KASAN
+ /*
+ * On kasan kernels, everything takes longer, so double the number of
+ * timeout extensions. This should help with issues like 41259215
+ * where WindowServer was timing out waiting for kextd to get all the
+ * kasan kexts loaded and started.
+ */
+ enum { kTimeoutExtensions = 8 };
+#else
+ enum { kTimeoutExtensions = 4 };
+#endif
+
+ time = mach_absolute_time();
+ kextdWait = false;
+ for (loops = 0; loops < kTimeoutExtensions; loops++) {
+ ret = waitForState( kIOServiceBusyStateMask, 0, timeout );
+
+ if (loops && (kIOReturnSuccess == ret)) {
+ time = mach_absolute_time() - time;
+ absolutetime_to_nanoseconds(*(AbsoluteTime *)&time, &nano);
+ IOLog("busy extended ok[%d], (%llds, %llds)\n",
+ loops, timeout / 1000000000ULL, nano / 1000000000ULL);
+ break;
+ } else if (kIOReturnTimeout != ret) {
+ break;
+ } else if (timeout < (4100ull * NSEC_PER_SEC)) {
+ break;
+ }
+
+ {
+ IORegistryIterator * iter;
+ OSOrderedSet * set;
+ OSOrderedSet * leaves;
+ IOService * next;
+ IOService * nextParent;
+ char * s;
+ size_t l;
+
+ len = 256;
+ panicStringLen = 256;
+ if (!string) {
+ string = IONew(char, len);
+ }
+ if (!panicString) {
+ panicString = IONew(char, panicStringLen);
+ }
+ set = NULL;
+ kextdWait = OSKext::isWaitingKextd();
+ iter = IORegistryIterator::iterateOver(this, gIOServicePlane, kIORegistryIterateRecursively);
+ leaves = OSOrderedSet::withCapacity(4);
+ if (iter) {
+ set = iter->iterateAll();
+ }
+ if (string && panicString && leaves && set) {
+ string[0] = panicString[0] = 0;
+ set->setObject(this);
+ while ((next = (IOService *) set->getLastObject())) {
+ if (next->getBusyState()) {
+ if (kIOServiceModuleStallState & next->__state[1]) {
+ kextdWait = true;
+ }
+ leaves->setObject(next);
+ nextParent = next;
+ while ((nextParent = nextParent->getProvider())) {
+ set->removeObject(nextParent);
+ leaves->removeObject(nextParent);
+ }
+ }
+ set->removeObject(next);
+ }
+ s = string;
+ while ((next = (IOService *) leaves->getLastObject())) {
+ l = snprintf(s, len, "%s'%s'", ((s == string) ? "" : ", "), next->getName());
+ if (l >= len) {
+ break;
+ }
+ s += l;
+ len -= l;
+ leaves->removeObject(next);
+ }
+ }
+ OSSafeReleaseNULL(leaves);
+ OSSafeReleaseNULL(set);
+ OSSafeReleaseNULL(iter);
+ }
+
+ dopanic = ((loops >= (kTimeoutExtensions - 1)) && (kIOWaitQuietPanics & gIOKitDebug));
+ snprintf(panicString, panicStringLen,
+ "%s[%d], (%llds): %s",
+ kextdWait ? "kextd stall" : "busy timeout",
+ loops, timeout / 1000000000ULL,
+ string ? string : "");
+ IOLog("%s\n", panicString);
+ if (dopanic) {
+ panic("%s", panicString);
+ } else if (!loops) {
+ getPMRootDomain()->startSpinDump(1);
+ }
+ }
+
+ if (string) {
+ IODelete(string, char, 256);
+ }
+ if (panicString) {
+ IODelete(panicString, char, panicStringLen);
+ }
+
+ return ret;
+}
+
+IOReturn
+IOService::waitQuiet( mach_timespec_t * timeout )
+{
+ uint64_t timeoutNS;
+
+ if (timeout) {
+ timeoutNS = timeout->tv_sec;
+ timeoutNS *= kSecondScale;
+ timeoutNS += timeout->tv_nsec;
+ } else {
+ timeoutNS = UINT64_MAX;
+ }
+
+ return waitQuiet(timeoutNS);
+}
+
+bool
+IOService::serializeProperties( OSSerialize * s ) const
+{
+#if 0
+ ((IOService *)this)->setProperty(((IOService *)this)->__state,
+ sizeof(__state), "__state");
+#endif
+ return super::serializeProperties(s);
+}
+
+
+void
+_IOConfigThread::main(void * arg, wait_result_t result)
+{
+ _IOConfigThread * self = (_IOConfigThread *) arg;
+ _IOServiceJob * job;
+ IOService * nub;
+ bool alive = true;
+ kern_return_t kr;
+ thread_precedence_policy_data_t precedence = { -1 };
+
+ kr = thread_policy_set(current_thread(),
+ THREAD_PRECEDENCE_POLICY,
+ (thread_policy_t) &precedence,
+ THREAD_PRECEDENCE_POLICY_COUNT);
+ if (KERN_SUCCESS != kr) {
+ IOLog("thread_policy_set(%d)\n", kr);
+ }
+
+ do {
+// randomDelay();
+
+ semaphore_wait( gJobsSemaphore );
+
+ IOTakeLock( gJobsLock );
+ job = (_IOServiceJob *) gJobs->getFirstObject();
+ job->retain();
+ gJobs->removeObject(job);
+ if (job) {
+ gOutstandingJobs--;
+// gNumConfigThreads--; // we're out of service
+ gNumWaitingThreads--; // we're out of service
+ }
+ IOUnlock( gJobsLock );
+
+ if (job) {
+ nub = job->nub;
+
+ if (gIOKitDebug & kIOLogConfig) {
+ LOG("config(%p): starting on %s, %d\n",
+ IOSERVICE_OBFUSCATE(IOThreadSelf()), job->nub->getName(), job->type);
+ }
+
+ switch (job->type) {
+ case kMatchNubJob:
+ nub->doServiceMatch( job->options );
+ break;
+
+ default:
+ LOG("config(%p): strange type (%d)\n",
+ IOSERVICE_OBFUSCATE(IOThreadSelf()), job->type );
+ break;
+ }
+
+ nub->release();
+ job->release();
+
+ IOTakeLock( gJobsLock );
+ alive = (gOutstandingJobs > gNumWaitingThreads);
+ if (alive) {
+ gNumWaitingThreads++; // back in service
+ }
+// gNumConfigThreads++;
+ else {
+ if (0 == --gNumConfigThreads) {
+// IOLog("MATCH IDLE\n");
+ IOLockWakeup( gJobsLock, (event_t) &gNumConfigThreads, /* one-thread */ false );
+ }
+ }
+ IOUnlock( gJobsLock );
+ }
+ } while (alive);
+
+ if (gIOKitDebug & kIOLogConfig) {
+ LOG("config(%p): terminating\n", IOSERVICE_OBFUSCATE(IOThreadSelf()));
+ }
+
+ self->release();
+}
+
+IOReturn
+IOService::waitMatchIdle( UInt32 msToWait )
+{
+ bool wait;
+ int waitResult = THREAD_AWAKENED;
+ bool computeDeadline = true;
+ AbsoluteTime deadline;
+
+ IOLockLock( gJobsLock );
+ do {
+ wait = (0 != gNumConfigThreads);
+ if (wait) {
+ if (msToWait) {
+ if (computeDeadline) {
+ clock_interval_to_deadline(
+ msToWait, kMillisecondScale, &deadline );
+ computeDeadline = false;
+ }
+ waitResult = IOLockSleepDeadline( gJobsLock, &gNumConfigThreads,
+ deadline, THREAD_UNINT );
+ } else {
+ waitResult = IOLockSleep( gJobsLock, &gNumConfigThreads,
+ THREAD_UNINT );
+ }
+ }
+ } while (wait && (waitResult != THREAD_TIMED_OUT));
+ IOLockUnlock( gJobsLock );
+
+ if (waitResult == THREAD_TIMED_OUT) {
+ return kIOReturnTimeout;
+ } else {
+ return kIOReturnSuccess;
+ }
+}
+
+void
+IOService::cpusRunning(void)
+{
+ gCPUsRunning = true;
+}
+
+void
+_IOServiceJob::pingConfig( _IOServiceJob * job )
+{
+ int count;
+ bool create;
+
+ assert( job );
+
+ IOTakeLock( gJobsLock );
+
+ gOutstandingJobs++;
+ gJobs->setLastObject( job );
+
+ count = gNumWaitingThreads;
+// if( gNumConfigThreads) count++;// assume we're called from a config thread
+
+ create = ((gOutstandingJobs > count)
+ && ((gNumConfigThreads < kMaxConfigThreads)
+ || (job->nub == gIOResources)
+ || !gCPUsRunning));
+ if (create) {
+ gNumConfigThreads++;
+ gNumWaitingThreads++;
+ }
+
+ IOUnlock( gJobsLock );
+
+ job->release();
+
+ if (create) {
+ if (gIOKitDebug & kIOLogConfig) {
+ LOG("config(%d): creating\n", gNumConfigThreads - 1);
+ }
+ _IOConfigThread::configThread(gNumConfigThreads - 1);
+ }
+
+ semaphore_signal( gJobsSemaphore );
+}
+
+struct IOServiceMatchContext {
+ OSDictionary * table;
+ OSObject * result;
+ uint32_t options;
+ uint32_t state;
+ uint32_t count;
+ uint32_t done;
+};
+
+bool
+IOService::instanceMatch(const OSObject * entry, void * context)
+{
+ IOServiceMatchContext * ctx = (typeof(ctx))context;
+ IOService * service = (typeof(service))entry;
+ OSDictionary * table = ctx->table;
+ uint32_t options = ctx->options;
+ uint32_t state = ctx->state;
+ uint32_t done;
+ bool match;
+
+ done = 0;
+ do{
+ match = ((state == (state & service->__state[0]))
+ && (0 == (service->__state[0] & kIOServiceInactiveState)));
+ if (!match) {
+ break;
+ }
+ ctx->count += table->getCount();
+ match = service->matchInternal(table, options, &done);
+ ctx->done += done;
+ }while (false);
+ if (!match) {
+ return false;
+ }
+
+ if ((kIONotifyOnce & options) && (ctx->done == ctx->count)) {
+ service->retain();
+ ctx->result = service;
+ return true;
+ } else if (!ctx->result) {
+ ctx->result = OSSet::withObjects((const OSObject **) &service, 1, 1);
+ } else {
+ ((OSSet *)ctx->result)->setObject(service);
+ }
+ return false;
+}
+
+// internal - call with gNotificationLock
+OSObject *
+IOService::copyExistingServices( OSDictionary * matching,
+ IOOptionBits inState, IOOptionBits options )
+{
+ OSObject * current = NULL;
+ OSIterator * iter;
+ IOService * service;
+ OSObject * obj;
+ OSString * str;
+
+ if (!matching) {
+ return NULL;
+ }
+
+#if MATCH_DEBUG
+ OSSerialize * s = OSSerialize::withCapacity(128);
+ matching->serialize(s);
+#endif
+
+ if ((obj = matching->getObject(gIOProviderClassKey))
+ && gIOResourcesKey
+ && gIOResourcesKey->isEqualTo(obj)
+ && (service = gIOResources)) {
+ if ((inState == (service->__state[0] & inState))
+ && (0 == (service->__state[0] & kIOServiceInactiveState))
+ && service->matchPassive(matching, options)) {
+ if (options & kIONotifyOnce) {
+ service->retain();
+ current = service;
+ } else {
+ current = OSSet::withObjects((const OSObject **) &service, 1, 1 );
+ }
+ }
+ } else {
+ IOServiceMatchContext ctx;
+ ctx.table = matching;
+ ctx.state = inState;
+ ctx.count = 0;
+ ctx.done = 0;
+ ctx.options = options;
+ ctx.result = NULL;
+
+ if ((str = OSDynamicCast(OSString, obj))) {
+ const OSSymbol * sym = OSSymbol::withString(str);
+ OSMetaClass::applyToInstancesOfClassName(sym, instanceMatch, &ctx);
+ sym->release();
+ } else {
+ IOService::gMetaClass.applyToInstances(instanceMatch, &ctx);
+ }
+
+
+ current = ctx.result;
+
+ options |= kIOServiceInternalDone | kIOServiceClassDone;
+ if (current && (ctx.done != ctx.count)) {
+ OSSet *
+ source = OSDynamicCast(OSSet, current);
+ current = NULL;
+ while ((service = (IOService *) source->getAnyObject())) {
+ if (service->matchPassive(matching, options)) {
+ if (options & kIONotifyOnce) {
+ service->retain();
+ current = service;
+ break;
+ }
+ if (current) {
+ ((OSSet *)current)->setObject( service );
+ } else {
+ current = OSSet::withObjects(
+ (const OSObject **) &service, 1, 1 );
+ }
+ }
+ source->removeObject(service);
+ }
+ source->release();
+ }
+ }
+
+#if MATCH_DEBUG
+ {
+ OSObject * _current = 0;
+
+ iter = IORegistryIterator::iterateOver( gIOServicePlane,
+ kIORegistryIterateRecursively );
+ if (iter) {
+ do {
+ iter->reset();
+ while ((service = (IOService *) iter->getNextObject())) {
+ if ((inState == (service->__state[0] & inState))
+ && (0 == (service->__state[0] & kIOServiceInactiveState))
+ && service->matchPassive(matching, 0)) {
+ if (options & kIONotifyOnce) {
+ service->retain();
+ _current = service;
+ break;
+ }
+ if (_current) {
+ ((OSSet *)_current)->setObject( service );
+ } else {
+ _current = OSSet::withObjects(
+ (const OSObject **) &service, 1, 1 );
+ }
+ }
+ }
+ } while (!service && !iter->isValid());
+ iter->release();
+ }
+
+ if (((current != 0) != (_current != 0))
+ || (current && _current && !current->isEqualTo(_current))) {
+ OSSerialize * s1 = OSSerialize::withCapacity(128);
+ OSSerialize * s2 = OSSerialize::withCapacity(128);
+ current->serialize(s1);
+ _current->serialize(s2);
+ kprintf("**mismatch** %p %p\n%s\n%s\n%s\n", IOSERVICE_OBFUSCATE(current),
+ IOSERVICE_OBFUSCATE(_current), s->text(), s1->text(), s2->text());
+ s1->release();
+ s2->release();
+ }
+
+ if (_current) {
+ _current->release();
+ }
+ }
+
+ s->release();
+#endif
+
+ if (current && (0 == (options & (kIONotifyOnce | kIOServiceExistingSet)))) {
+ iter = OSCollectionIterator::withCollection((OSSet *)current );
+ current->release();
+ current = iter;
+ }
+
+ return current;
+}
+
+// public version
+OSIterator *
+IOService::getMatchingServices( OSDictionary * matching )
+{
+ OSIterator * iter;
+
+ // is a lock even needed?
+ LOCKWRITENOTIFY();
+
+ iter = (OSIterator *) copyExistingServices( matching,
+ kIOServiceMatchedState );
+
+ UNLOCKNOTIFY();
+
+ return iter;
+}
+
+IOService *
+IOService::copyMatchingService( OSDictionary * matching )
+{
+ IOService * service;
+
+ // is a lock even needed?
+ LOCKWRITENOTIFY();
+
+ service = (IOService *) copyExistingServices( matching,
+ kIOServiceMatchedState, kIONotifyOnce );
+
+ UNLOCKNOTIFY();
+
+ return service;
+}
+
+struct _IOServiceMatchingNotificationHandlerRef {
+ IOServiceNotificationHandler handler;
+ void * ref;
+};
+
+static bool
+_IOServiceMatchingNotificationHandler( void * target, void * refCon,
+ IOService * newService,
+ IONotifier * notifier )
+{
+ return (*((_IOServiceNotifier *) notifier)->compatHandler)(target, refCon, newService);
+}
+
+// internal - call with gNotificationLock
+IONotifier *
+IOService::setNotification(
+ const OSSymbol * type, OSDictionary * matching,
+ IOServiceMatchingNotificationHandler handler, void * target, void * ref,
+ SInt32 priority )
+{
+ _IOServiceNotifier * notify = NULL;
+ OSOrderedSet * set;
+
+ if (!matching) {
+ return NULL;
+ }
+
+ notify = new _IOServiceNotifier;
+ if (notify && !notify->init()) {
+ notify->release();
+ notify = NULL;
+ }
+
+ if (notify) {
+ notify->handler = handler;
+ notify->target = target;
+ notify->type = type;
+ notify->matching = matching;
+ matching->retain();
+ if (handler == &_IOServiceMatchingNotificationHandler) {
+ notify->compatHandler = ((_IOServiceMatchingNotificationHandlerRef *)ref)->handler;
+ notify->ref = ((_IOServiceMatchingNotificationHandlerRef *)ref)->ref;
+ } else {
+ notify->ref = ref;
+ }
+ notify->priority = priority;
+ notify->state = kIOServiceNotifyEnable;
+ queue_init( ¬ify->handlerInvocations );
+
+ ////// queue
+
+ if (NULL == (set = (OSOrderedSet *) gNotifications->getObject( type ))) {
+ set = OSOrderedSet::withCapacity( 1,
+ IONotifyOrdering, NULL );
+ if (set) {
+ gNotifications->setObject( type, set );
+ set->release();
+ }
+ }
+ notify->whence = set;
+ if (set) {
+ set->setObject( notify );
+ }
+ }
+
+ return notify;
+}
+
+// internal - call with gNotificationLock
+IONotifier *
+IOService::doInstallNotification(
+ const OSSymbol * type, OSDictionary * matching,
+ IOServiceMatchingNotificationHandler handler,
+ void * target, void * ref,
+ SInt32 priority, OSIterator ** existing )
+{
+ OSIterator * exist;
+ IONotifier * notify;
+ IOOptionBits inState;
+
+ if (!matching) {
+ return NULL;
+ }
+
+ if (type == gIOPublishNotification) {
+ inState = kIOServiceRegisteredState;
+ } else if (type == gIOFirstPublishNotification) {
+ inState = kIOServiceFirstPublishState;
+ } else if (type == gIOMatchedNotification) {
+ inState = kIOServiceMatchedState;
+ } else if (type == gIOFirstMatchNotification) {
+ inState = kIOServiceFirstMatchState;
+ } else if ((type == gIOTerminatedNotification) || (type == gIOWillTerminateNotification)) {
+ inState = 0;
+ } else {
+ return NULL;
+ }
+
+ notify = setNotification( type, matching, handler, target, ref, priority );
+
+ if (inState) {
+ // get the current set
+ exist = (OSIterator *) copyExistingServices( matching, inState );
+ } else {
+ exist = NULL;
+ }
+
+ *existing = exist;
+
+ return notify;
+}
+
+#if !defined(__LP64__)
+IONotifier *
+IOService::installNotification(const OSSymbol * type, OSDictionary * matching,
+ IOServiceNotificationHandler handler,
+ void * target, void * refCon,
+ SInt32 priority, OSIterator ** existing )
+{
+ IONotifier * result;
+ _IOServiceMatchingNotificationHandlerRef ref;
+ ref.handler = handler;
+ ref.ref = refCon;
+
+ result = (_IOServiceNotifier *) installNotification( type, matching,
+ &_IOServiceMatchingNotificationHandler,
+ target, &ref, priority, existing );
+ if (result) {
+ matching->release();
+ }
+
+ return result;
+}
+#endif /* !defined(__LP64__) */
+
+
+IONotifier *
+IOService::installNotification(
+ const OSSymbol * type, OSDictionary * matching,
+ IOServiceMatchingNotificationHandler handler,
+ void * target, void * ref,
+ SInt32 priority, OSIterator ** existing )
+{
+ IONotifier * notify;
+
+ LOCKWRITENOTIFY();
+
+ notify = doInstallNotification( type, matching, handler, target, ref,
+ priority, existing );
+
+ // in case handler remove()s
+ if (notify) {
+ notify->retain();
+ }
+
+ UNLOCKNOTIFY();
+
+ return notify;
+}
+
+IONotifier *
+IOService::addNotification(
+ const OSSymbol * type, OSDictionary * matching,
+ IOServiceNotificationHandler handler,
+ void * target, void * refCon,
+ SInt32 priority )
+{
+ IONotifier * result;
+ _IOServiceMatchingNotificationHandlerRef ref;
+
+ ref.handler = handler;
+ ref.ref = refCon;
+
+ result = addMatchingNotification(type, matching, &_IOServiceMatchingNotificationHandler,
+ target, &ref, priority);
+
+ if (result) {
+ matching->release();
+ }
+
+ return result;
+}
+
+IONotifier *
+IOService::addMatchingNotification(
+ const OSSymbol * type, OSDictionary * matching,
+ IOServiceMatchingNotificationHandler handler,
+ void * target, void * ref,
+ SInt32 priority )
+{
+ OSIterator * existing = NULL;
+ IONotifier * ret;
+ _IOServiceNotifier * notify;
+ IOService * next;
+
+ ret = notify = (_IOServiceNotifier *) installNotification( type, matching,
+ handler, target, ref, priority, &existing );
+ if (!ret) {
+ return NULL;
+ }
+
+ // send notifications for existing set
+ if (existing) {
+ while ((next = (IOService *) existing->getNextObject())) {
+ if (0 == (next->__state[0] & kIOServiceInactiveState)) {
+ next->invokeNotifier( notify );
+ }
+ }
+ existing->release();
+ }
+
+ LOCKWRITENOTIFY();
+ bool removed = (NULL == notify->whence);
+ notify->release();
+ if (removed) {
+ ret = gIOServiceNullNotifier;
+ }
+ UNLOCKNOTIFY();
+
+ return ret;
+}
+
+static bool
+IOServiceMatchingNotificationHandlerToBlock( void * target __unused, void * refCon,
+ IOService * newService,
+ IONotifier * notifier )
+{
+ return ((IOServiceMatchingNotificationHandlerBlock) refCon)(newService, notifier);
+}
+
+IONotifier *
+IOService::addMatchingNotification(
+ const OSSymbol * type, OSDictionary * matching,
+ SInt32 priority,
+ IOServiceMatchingNotificationHandlerBlock handler)
+{
+ IONotifier * notify;
+ void * block;
+
+ block = Block_copy(handler);
+ if (!block) {
+ return NULL;
+ }
+
+ notify = addMatchingNotification(type, matching,
+ &IOServiceMatchingNotificationHandlerToBlock, NULL, block, priority);
+
+ if (!notify) {
+ Block_release(block);
+ }
+
+ return notify;
+}
+
+
+bool
+IOService::syncNotificationHandler(
+ void * /* target */, void * ref,
+ IOService * newService,
+ IONotifier * notifier )
+{
+ LOCKWRITENOTIFY();
+ if (!*((IOService **) ref)) {
+ newService->retain();
+ (*(IOService **) ref) = newService;
+ WAKEUPNOTIFY(ref);
+ }
+ UNLOCKNOTIFY();
+
+ return false;
+}
+
+IOService *
+IOService::waitForMatchingService( OSDictionary * matching,
+ uint64_t timeout)
+{
+ IONotifier * notify = NULL;
+ // priority doesn't help us much since we need a thread wakeup
+ SInt32 priority = 0;
+ IOService * result;
+
+ if (!matching) {
+ return NULL;
+ }
+
+ result = NULL;
+
+ LOCKWRITENOTIFY();
+ do{
+ result = (IOService *) copyExistingServices( matching,
+ kIOServiceMatchedState, kIONotifyOnce );
+ if (result) {
+ break;
+ }
+ notify = IOService::setNotification( gIOMatchedNotification, matching,
+ &IOService::syncNotificationHandler, (void *) NULL,
+ &result, priority );
+ if (!notify) {
+ break;
+ }
+ if (UINT64_MAX != timeout) {
+ AbsoluteTime deadline;
+ nanoseconds_to_absolutetime(timeout, &deadline);
+ clock_absolutetime_interval_to_deadline(deadline, &deadline);
+ SLEEPNOTIFYTO(&result, deadline);
+ } else {
+ SLEEPNOTIFY(&result);
+ }
+ }while (false);
+
+ UNLOCKNOTIFY();
+
+ if (notify) {
+ notify->remove(); // dequeues
+ }
+ return result;
+}
+
+IOService *
+IOService::waitForService( OSDictionary * matching,
+ mach_timespec_t * timeout )
+{
+ IOService * result;
+ uint64_t timeoutNS;
+
+ if (timeout) {
+ timeoutNS = timeout->tv_sec;
+ timeoutNS *= kSecondScale;
+ timeoutNS += timeout->tv_nsec;
+ } else {
+ timeoutNS = UINT64_MAX;
+ }
+
+ result = waitForMatchingService(matching, timeoutNS);
+
+ matching->release();
+ if (result) {
+ result->release();
+ }
+
+ return result;
+}
+
+__dead2
+void
+IOService::deliverNotification( const OSSymbol * type,
+ IOOptionBits orNewState, IOOptionBits andNewState )
+{
+ panic("deliverNotification");
+}
+
+OSArray *
+IOService::copyNotifiers(const OSSymbol * type,
+ IOOptionBits orNewState, IOOptionBits andNewState )
+{
+ _IOServiceNotifier * notify;
+ OSIterator * iter;
+ OSArray * willSend = NULL;
+
+ lockForArbitration();
+
+ if ((0 == (__state[0] & kIOServiceInactiveState))
+ || (type == gIOTerminatedNotification)
+ || (type == gIOWillTerminateNotification)) {
+ LOCKREADNOTIFY();
+
+ iter = OSCollectionIterator::withCollection((OSOrderedSet *)
+ gNotifications->getObject( type ));
+
+ if (iter) {
+ while ((notify = (_IOServiceNotifier *) iter->getNextObject())) {
+ if (matchPassive(notify->matching, 0)
+ && (kIOServiceNotifyEnable & notify->state)) {
+ if (NULL == willSend) {
+ willSend = OSArray::withCapacity(8);
+ }
+ if (willSend) {
+ willSend->setObject( notify );
+ }
+ }
+ }
+ iter->release();
+ }
+ __state[0] = (__state[0] | orNewState) & andNewState;
+ UNLOCKNOTIFY();
+ }
+
+ unlockForArbitration();
+
+ return willSend;
+}
+
+IOOptionBits
+IOService::getState( void ) const
+{
+ return __state[0];
+}
+
+/*
+ * Helpers to make matching objects for simple cases
+ */
+
+OSDictionary *
+IOService::serviceMatching( const OSString * name,
+ OSDictionary * table )
+{
+ const OSString * str;
+
+ str = OSSymbol::withString(name);
+ if (!str) {
+ return NULL;
+ }
+
+ if (!table) {
+ table = OSDictionary::withCapacity( 2 );
+ }
+ if (table) {
+ table->setObject(gIOProviderClassKey, (OSObject *)str );
+ }
+ str->release();
+
+ return table;
+}
+
+OSDictionary *
+IOService::serviceMatching( const char * name,
+ OSDictionary * table )
+{
+ const OSString * str;
+
+ str = OSSymbol::withCString( name );
+ if (!str) {
+ return NULL;
+ }
+
+ table = serviceMatching( str, table );
+ str->release();
+ return table;
+}
+
+OSDictionary *
+IOService::nameMatching( const OSString * name,
+ OSDictionary * table )
+{
+ if (!table) {
+ table = OSDictionary::withCapacity( 2 );
+ }
+ if (table) {
+ table->setObject( gIONameMatchKey, (OSObject *)name );
+ }
+
+ return table;
+}
+
+OSDictionary *
+IOService::nameMatching( const char * name,
+ OSDictionary * table )
+{
+ const OSString * str;
+
+ str = OSSymbol::withCString( name );
+ if (!str) {
+ return NULL;
+ }
+
+ table = nameMatching( str, table );
+ str->release();
+ return table;
+}
+
+OSDictionary *
+IOService::resourceMatching( const OSString * str,
+ OSDictionary * table )
+{
+ table = serviceMatching( gIOResourcesKey, table );
+ if (table) {
+ table->setObject( gIOResourceMatchKey, (OSObject *) str );
+ }
+
+ return table;
+}
+
+OSDictionary *
+IOService::resourceMatching( const char * name,
+ OSDictionary * table )
+{
+ const OSSymbol * str;
+
+ str = OSSymbol::withCString( name );
+ if (!str) {
+ return NULL;
+ }
+
+ table = resourceMatching( str, table );
+ str->release();
+
+ return table;
+}
+
+OSDictionary *
+IOService::propertyMatching( const OSSymbol * key, const OSObject * value,
+ OSDictionary * table )
+{
+ OSDictionary * properties;
+
+ properties = OSDictionary::withCapacity( 2 );
+ if (!properties) {
+ return NULL;
+ }
+ properties->setObject( key, value );
+
+ if (!table) {
+ table = OSDictionary::withCapacity( 2 );
+ }
+ if (table) {
+ table->setObject( gIOPropertyMatchKey, properties );
+ }
+
+ properties->release();
+
+ return table;
+}
+
+OSDictionary *
+IOService::registryEntryIDMatching( uint64_t entryID,
+ OSDictionary * table )
+{
+ OSNumber * num;
+
+ num = OSNumber::withNumber( entryID, 64 );
+ if (!num) {
+ return NULL;
+ }
+
+ if (!table) {
+ table = OSDictionary::withCapacity( 2 );
+ }
+ if (table) {
+ table->setObject( gIORegistryEntryIDKey, num );
+ }
+
+ if (num) {
+ num->release();
+ }
+
+ return table;
+}
+
+
+/*
+ * _IOServiceNotifier
+ */
+
+// wait for all threads, other than the current one,
+// to exit the handler
+
+void
+_IOServiceNotifier::wait()
+{
+ _IOServiceNotifierInvocation * next;
+ bool doWait;
+
+ do {
+ doWait = false;
+ queue_iterate( &handlerInvocations, next,
+ _IOServiceNotifierInvocation *, link) {
+ if (next->thread != current_thread()) {
+ doWait = true;
+ break;
+ }
+ }
+ if (doWait) {
+ state |= kIOServiceNotifyWaiter;
+ SLEEPNOTIFY(this);
+ }
+ } while (doWait);
+}
+
+void
+_IOServiceNotifier::free()
+{
+ assert( queue_empty( &handlerInvocations ));
+
+ if (handler == &IOServiceMatchingNotificationHandlerToBlock) {
+ Block_release(ref);
+ }
+
+ OSObject::free();
+}
+
+void
+_IOServiceNotifier::remove()
+{
+ LOCKWRITENOTIFY();
+
+ if (whence) {
+ whence->removeObject((OSObject *) this );
+ whence = NULL;
+ }
+ if (matching) {
+ matching->release();
+ matching = NULL;
+ }
+
+ state &= ~kIOServiceNotifyEnable;
+
+ wait();
+
+ UNLOCKNOTIFY();
+
+ release();
+}
+
+bool
+_IOServiceNotifier::disable()
+{
+ bool ret;
+
+ LOCKWRITENOTIFY();
+
+ ret = (0 != (kIOServiceNotifyEnable & state));
+ state &= ~kIOServiceNotifyEnable;
+ if (ret) {
+ wait();
+ }
+
+ UNLOCKNOTIFY();
+
+ return ret;
+}
+
+void
+_IOServiceNotifier::enable( bool was )
+{
+ LOCKWRITENOTIFY();
+ if (was) {
+ state |= kIOServiceNotifyEnable;
+ } else {
+ state &= ~kIOServiceNotifyEnable;
+ }
+ UNLOCKNOTIFY();
+}
+
+
+/*
+ * _IOServiceNullNotifier
+ */
+
+void
+_IOServiceNullNotifier::taggedRetain(const void *tag) const
+{
+}
+void
+_IOServiceNullNotifier::taggedRelease(const void *tag, const int when) const
+{
+}
+void
+_IOServiceNullNotifier::free()
+{
+}
+void
+_IOServiceNullNotifier::wait()
+{
+}
+void
+_IOServiceNullNotifier::remove()
+{
+}
+void
+_IOServiceNullNotifier::enable(bool was)
+{
+}
+bool
+_IOServiceNullNotifier::disable()
+{
+ return false;
+}
+
+/*
+ * IOResources
+ */
+
+IOService *
+IOResources::resources( void )
+{
+ IOResources * inst;
+
+ inst = new IOResources;
+ if (inst && !inst->init()) {
+ inst->release();
+ inst = NULL;
+ }
+
+ return inst;
+}
+
+bool
+IOResources::init( OSDictionary * dictionary )
+{
+ // Do super init first
+ if (!IOService::init()) {
+ return false;
+ }
+
+ // Allow PAL layer to publish a value
+ const char *property_name;
+ int property_value;
+
+ pal_get_resource_property( &property_name, &property_value );
+
+ if (property_name) {
+ OSNumber *num;
+ const OSSymbol * sym;
+
+ if ((num = OSNumber::withNumber(property_value, 32)) != NULL) {
+ if ((sym = OSSymbol::withCString( property_name)) != NULL) {
+ this->setProperty( sym, num );
+ sym->release();
+ }
+ num->release();
+ }
+ }
+
+ return true;
+}
+
+IOReturn
+IOResources::newUserClient(task_t owningTask, void * securityID,
+ UInt32 type, OSDictionary * properties,
+ IOUserClient ** handler)
+{
+ return kIOReturnUnsupported;