Jonas Due Vesterheden
Jonas Due Vesterheden

Reputation: 502

How to use `IOConnectCallAsyncScalarMethod` with a GCD dispatch queue

I am working on some code that uses IOConnectCallAsyncScalarMethod() to get callbacks from a DriverKit extension. The setup is quite heavy, involving spawning a thread, manually creating a CFMachPortRef, adding its CFRunLoopSourceRef to a CFRunLoop and then pumping that run loop.

In order to simplify this code and reduce the risk of race conditions, I would like to get the IOKit callback on a dispatch queue instead. Is there any way to achieve this?

Upvotes: 2

Views: 220

Answers (1)

Jonas Due Vesterheden
Jonas Due Vesterheden

Reputation: 502

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.

Upvotes: 3

Related Questions