After going back and forth on this unsuccessfully, I finally found the answer in the OS X and iOS Kernel Programming book (page 95, listing 5-15).
The trick is to use a IONotificationPortRef
along with IONotificationPortSetDispatchQueue
to set the target dispatch queue. Then to actually have the callback dispatched to that queue, set up an io_async_ref64_t
and use it. Here's an outline of what the code would look like:
// Create a notification port for IOKit service callbacks
IONotificationPortRef notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
// Run notification callbacks on the desired dispatch queue
IONotificationPortSetDispatchQueue(self.notificationPort, dispatch_get_main_queue());
io_async_ref64_t asyncRef;
asyncRef[kIOAsyncCalloutFuncIndex] = (uint64_t)callback;
asyncRef[kIOAsyncCalloutRefconIndex] = (uint64_t)(__bridge void *)self;
uint32_t cmd = 0xCAFE; // Method as defined by the service
kern_return_t error = IOConnectCallAsyncScalarMethod(connection, cmd, IONotificationPortGetMachPort(notificationPort), asyncRef, kIOAsyncCalloutCount, NULL, 0, NULL, NULL);
callback
should have this signature: void commandReadyCallback(void *context, IOReturn result)
. (AKA. IOAsyncCallback0
)
I hope this helps some poor soul in the future.