+enum {
+ kIOServiceNotificationTypeCount = kIOServiceNotificationTypeLast + 1,
+};
+
+struct IOServiceNotificationDispatchSource_IVars {
+ OSObject * serverName;
+ OSAction * action;
+ IOLock * lock;
+ IONotifier * notifier;
+ OSDictionary * interestNotifiers;
+ OSArray * pending[kIOServiceNotificationTypeCount];
+ bool enable;
+};
+
+kern_return_t
+IMPL(IOServiceNotificationDispatchSource, Create)
+{
+ IOUserServer * us;
+ IOReturn ret;
+ IOServiceNotificationDispatchSource * inst;
+
+ inst = OSTypeAlloc(IOServiceNotificationDispatchSource);
+ if (!inst->init()) {
+ OSSafeReleaseNULL(inst);
+ return kIOReturnNoMemory;
+ }
+
+ us = (typeof(us))thread_iokit_tls_get(0);
+ assert(OSDynamicCast(IOUserServer, us));
+ if (!us) {
+ OSSafeReleaseNULL(inst);
+ return kIOReturnError;
+ }
+ inst->ivars->serverName = us->copyProperty(gIOUserServerNameKey);
+ if (!inst->ivars->serverName) {
+ OSSafeReleaseNULL(inst);
+ return kIOReturnNoMemory;
+ }
+
+ inst->ivars->lock = IOLockAlloc();
+ if (!inst->ivars->lock) {
+ OSSafeReleaseNULL(inst);
+ return kIOReturnNoMemory;
+ }
+ for (uint32_t idx = 0; idx < kIOServiceNotificationTypeCount; idx++) {
+ inst->ivars->pending[idx] = OSArray::withCapacity(4);
+ if (!inst->ivars->pending[idx]) {
+ OSSafeReleaseNULL(inst);
+ return kIOReturnNoMemory;
+ }
+ }
+ inst->ivars->interestNotifiers = OSDictionary::withCapacity(4);
+ if (!inst->ivars->interestNotifiers) {
+ OSSafeReleaseNULL(inst);
+ return kIOReturnNoMemory;
+ }
+
+ inst->ivars->notifier = IOService::addMatchingNotification(gIOMatchedNotification, matching, 0 /*priority*/,
+ ^bool (IOService * newService, IONotifier * notifier) {
+ bool notifyReady = false;
+ IONotifier * interest;
+ OSObject * serverName;
+ bool okToUse;
+
+ serverName = newService->copyProperty(gIOUserServerNameKey);
+ okToUse = (serverName && inst->ivars->serverName->isEqualTo(serverName));
+ OSSafeReleaseNULL(serverName);
+ if (!okToUse) {
+ return false;
+ }
+
+ IOLockLock(inst->ivars->lock);
+ notifyReady = (0 == inst->ivars->pending[kIOServiceNotificationTypeMatched]->getCount());
+ inst->ivars->pending[kIOServiceNotificationTypeMatched]->setObject(newService);
+ IOLockUnlock(inst->ivars->lock);
+
+ interest = newService->registerInterest(gIOGeneralInterest,
+ ^IOReturn (uint32_t messageType, IOService * provider,
+ void * messageArgument, size_t argSize) {
+ IONotifier * interest;
+ bool notifyReady = false;
+
+ switch (messageType) {
+ case kIOMessageServiceIsTerminated:
+ IOLockLock(inst->ivars->lock);
+ notifyReady = (0 == inst->ivars->pending[kIOServiceNotificationTypeTerminated]->getCount());
+ inst->ivars->pending[kIOServiceNotificationTypeTerminated]->setObject(provider);
+ interest = (typeof(interest))inst->ivars->interestNotifiers->getObject((const OSSymbol *) newService);
+ assert(interest);
+ interest->remove();
+ inst->ivars->interestNotifiers->removeObject((const OSSymbol *) newService);
+ IOLockUnlock(inst->ivars->lock);
+ break;
+ default:
+ break;
+ }
+ if (notifyReady && inst->ivars->action) {
+ inst->ServiceNotificationReady(inst->ivars->action);
+ }
+ return kIOReturnSuccess;
+ });
+ if (interest) {
+ IOLockLock(inst->ivars->lock);
+ inst->ivars->interestNotifiers->setObject((const OSSymbol *) newService, interest);
+ IOLockUnlock(inst->ivars->lock);
+ }
+ if (notifyReady) {
+ if (inst->ivars->action) {
+ inst->ServiceNotificationReady(inst->ivars->action);
+ }
+ }
+ return false;
+ });
+
+ if (!inst->ivars->notifier) {
+ OSSafeReleaseNULL(inst);
+ ret = kIOReturnError;
+ }
+
+ *notification = inst;
+ ret = kIOReturnSuccess;
+
+ return ret;
+}
+
+kern_return_t
+IMPL(IOServiceNotificationDispatchSource, CopyNextNotification)
+{
+ IOService * next;
+ uint32_t idx;
+
+ IOLockLock(ivars->lock);
+ for (idx = 0; idx < kIOServiceNotificationTypeCount; idx++) {
+ next = (IOService *) ivars->pending[idx]->getObject(0);
+ if (next) {
+ next->retain();
+ ivars->pending[idx]->removeObject(0);
+ break;
+ }
+ }
+ IOLockUnlock(ivars->lock);
+
+ if (idx == kIOServiceNotificationTypeCount) {
+ idx = kIOServiceNotificationTypeNone;
+ }
+ *type = idx;
+ *service = next;
+ *options = 0;
+
+ return kIOReturnSuccess;
+}
+
+bool
+IOServiceNotificationDispatchSource::init()
+{
+ if (!super::init()) {
+ return false;
+ }
+ ivars = IONewZero(IOServiceNotificationDispatchSource_IVars, 1);
+ if (!ivars) {
+ return false;
+ }
+
+ return true;
+}
+
+void
+IOServiceNotificationDispatchSource::free()
+{
+ if (ivars) {
+ OSSafeReleaseNULL(ivars->serverName);
+ if (ivars->interestNotifiers) {
+ ivars->interestNotifiers->iterateObjects(^bool (const OSSymbol * key, OSObject * object) {
+ IONotifier * interest = (typeof(interest))object;
+ interest->remove();
+ return false;
+ });
+ OSSafeReleaseNULL(ivars->interestNotifiers);
+ }
+ for (uint32_t idx = 0; idx < kIOServiceNotificationTypeCount; idx++) {
+ OSSafeReleaseNULL(ivars->pending[idx]);
+ }
+ if (ivars->lock) {
+ IOLockFree(ivars->lock);
+ ivars->lock = NULL;
+ }
+ if (ivars->notifier) {
+ ivars->notifier->remove();
+ ivars->notifier = NULL;
+ }
+ IOSafeDeleteNULL(ivars, IOServiceNotificationDispatchSource_IVars, 1);
+ }
+
+ super::free();
+}
+
+kern_return_t
+IMPL(IOServiceNotificationDispatchSource, SetHandler)
+{
+ IOReturn ret;
+ bool notifyReady;
+
+ notifyReady = false;
+
+ IOLockLock(ivars->lock);
+ OSSafeReleaseNULL(ivars->action);
+ action->retain();
+ ivars->action = action;
+ if (action) {
+ for (uint32_t idx = 0; idx < kIOServiceNotificationTypeCount; idx++) {
+ notifyReady = (ivars->pending[idx]->getCount());
+ if (notifyReady) {
+ break;
+ }
+ }
+ }
+ IOLockUnlock(ivars->lock);
+
+ if (notifyReady) {
+ ServiceNotificationReady(action);
+ }
+ ret = kIOReturnSuccess;
+
+ return ret;
+}
+
+kern_return_t
+IMPL(IOServiceNotificationDispatchSource, SetEnableWithCompletion)
+{
+ if (enable == ivars->enable) {
+ return kIOReturnSuccess;
+ }
+
+ IOLockLock(ivars->lock);
+ ivars->enable = enable;
+ IOLockUnlock(ivars->lock);
+
+ return kIOReturnSuccess;
+}
+
+kern_return_t
+IMPL(IOServiceNotificationDispatchSource, Cancel)
+{
+ return kIOReturnUnsupported;
+}
+
+kern_return_t
+IMPL(IOServiceNotificationDispatchSource, CheckForWork)
+{
+ return kIOReturnNotReady;
+}
+
+kern_return_t
+IOServiceNotificationDispatchSource::DeliverNotifications(IOServiceNotificationBlock block)
+{
+ return kIOReturnUnsupported;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+