I'm using the following code to find the dext service. The driver is enabled in iOS settings prior to launching the app.
io_service_t mService = IO_OBJECT_NULL;
kern_return_t ret = kIOReturnSuccess;
io_iterator_t iterator = IO_OBJECT_NULL;
if (__builtin_available(iOS 15.0, *)) {
ret = IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceNameMatching("MyDriver"), &iterator);
} else {
// Fallback on earlier versions
}
if (ret != kIOReturnSuccess)
{
printf("Unable to find service");
}
while ((mService = IOIteratorNext(iterator)) != IO_OBJECT_NULL)
{
//Only able to find service if launching the app first and then connecting the device
..........
}
I noticed the call IOServiceNameMatching doesn't return the same result for the following workflows:
-
Launch the app first and then connect the device, IOServiceGetMatchingServices can find the service.
-
Connect the device to USB-C port first, then launch the app, the same call can't find a matching service (iterator is null). I would need to disconnect and reconnect the device while the app is running in order to find the matching dext.
Any suggestion on how to find the matching dext service for workflow #2?
Thanks
This is an app we develop for iPadOS so I guess the IORegistryExplorer.app doesn't work in this case.
My actual recommendation is that even if you have no intention of every shipping any product for macOS... you still get your DEXT loading on running on macOS and, at least initially, focus on making everything work in that environment. Practically speaking, DriverKit for iPadOS is a subset of DriverKit for macOS broadly functions in the same way. macOS does introduce some additional complexity and edge cases, but I don't think there's any case where a DEXT will work on macOS but NOT on iPadOS (ignoring DriverKit families that aren't supported on on iPadOS). More to the point, macOS has better tools for investigation and debugging, which is why I'd start with it.
Giving a high level answer on this point:
I’m wondering if anything can be done to get it to work for the workflows I mentioned in #1 and #3 above.
-
I think case #1 is a bug in your code. I'm not sure what exactly that issue is (see below), but this is something that should work.
-
Case #3 depends on exactly what drivers are actually loading when your DEXT isn't present. My guess is that this is either a variant of #1 (so fixing #1 will also resolve #3) or that the system driver can't unload (in which case, the user would need to hot plug the device before you DEXT will match).
Covering a few specific details:
- With the driver up and running, if I disabled and reenabled the driver in iOS settings without disconnecting/reconnecting the device, MyDriver Start() wouldn't get called, and get matching service with IOServiceMatching("IOUserService") wouldn't find it again.
Two things to look at here:
-
Was your driver able to teardown and destroy itself properly? If your DEXT failed to unload cleanly, then it's possible you never removed entirely, which is why you can't reload.
-
If you managed to unload correctly, then it's possible that the system driver (which replace your DEXT) isn't unloading when you reenable your DEXT. The actual behavior here will depend on exactly what driver(s) end up attaching to the device without you DEXT being present, but there are many devices where the expected behavior is that a newly enabled DEXT won't be able to control the device until the device has been hot plugged. Historically, this was actually why so many KEXT installers asked for the system to be rebooted after installation. It wasn't that the driver COULDN'T work without reboot, it was that it was easier to tell the user "reboot after install" instead of trying to explain the nuance of which cases work and which don't.
FYI, both of these are things I would specifically investigate on macOS (NOT iPadOS), primarily because IORegistryExplorer makes it so easy to see exactly what's happening in the driver stack.
- When the USB device is connected before launching the app (with driver already enabled in iOS Settings), I notice my Driver Start() doesn't get called, which results in "MyDriver" not showing up when I try to get matching service with IOServiceMatching("IOUserService").
The first thing I would look at here is to make sure you're CERTAIN that's exactly what's going on. From direct, personal experience, it's very easy to misunderstand exactly what's going on, waste hours investigating the "problem", only to eventually determine that the problem you THOUGHT you had was simply not happening at all. The behavior you're describing above ("DEXT only matches and runs when my app is running") is very odd and not normal system behavior. That means one of two things is most likely going on:
-
(unlikely) You've written your DEXT such that this is in fact "the way it works". (I think this is tricky but possible, however, I think it would be hard to implement by "accident")
-
(more likely) Your DEXT is loading, but you're not able to find or connect to it because of some other issue/factor. (more on that below)
showing up when I try to get matching service with IOServiceMatching("IOUserService").
Frist off, note that you should definitely NOT be using a "generic" name my "MyDriver". I would recommend using the IOKit class naming convention, which is to use our standard reverse DNS/bundle ID sheme, but replacing "." with "_". In concrete terms:
Website-> example.com
BundleID-> com.example.dext.coolusbdevice
Class Name-> com_example_dext_coolusbdevice
More importantly, you should not be matching against "IOUserService", you should be matching against your driver's call name. IOUserService is the general base class used for DEXTs support (it's basically equivalent to IOService), which means you could end up with a bunch of "extra" returns that you don't need or care about. More to the point, in your while loop here:
while ((mService = IOIteratorNext(iterator)) != IO_OBJECT_NULL)
{
//Only able to find "MyDriver" if launching the app first and then connecting the device
ret = IOServiceOpen(mService, mach_task_self_, 0, &mConnection);
..........
}
...what does it do if "IOServiceOpen" returns an error? If you're breaking out of the loop when you get an error, then that might explain both the success and failure. Plugging the device in after your app is running means you're going to be the most "recent" match, which means it's more likely to be the first return* in the io_iterator_t. Having it already plugged in means that other activity can have occurred, so the code above is going to fail on IOServiceOpen.
As a side note, there's a function "KE_PrintIOObject()"in this post that I would recommend copying as a diagnostic aid. It's far more useful on macOS (where the registry is broadly accessible), but it will provide at least some insight into the object(s) the system returned to you.
I have no problem matching on the device using IOServiceMatching("IOUSBDevice") and idVendor + idProduct as keys. It doesn’t matter if the device is connected before or after launching the app, matching on the device always returns the correct value. It is the matching of the driver that I'm having problems with.
Note that if you're able to find your specific accessory, you can try manually walk "up" the registry using IORegistryEntryGetChildEntry/IORegistryEntryGetChildIterator. That might let you "see" what drivers were loading above your device in all of the cases you're looking at.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware