How to create IOUSBDeviceInterface without iterating IORegistry

Viewed 33

In a project involving USB devices with a custom protocol, I can successfully enumerate the connected USB devices and work with the desired one:

io_iterator_t iter = 0;

// Get the IO registry which has the system information for connected hardware
io_registry_entry_t entry = IORegistryGetRootEntry(kIOMasterPortDefault);
if (entry == 0)
    throw usb_error("internal error (failed to get IO registry root entry)", 0);

// Get an iterator for the USB plane
kern_return_t kret = IORegistryEntryCreateIterator(entry, kIOUSBPlane, kIORegistryIterateRecursively, &iter);
if (kret != KERN_SUCCESS || iter == 0)
    throw usb_error("internal error (failed to create device iterator)", 0);

// Walk the iterator
io_service_t service = 0;
while ((service = IOIteratorNext(iter)) != 0) {

    IOUSBDeviceInterface** dev = iokit_helper::get_interface<IOUSBDeviceInterface>(service, kIOUSBDeviceUserClientTypeID, kIOUSBDeviceInterfaceID);

    IOObjectRelease(service);
    if (dev == nullptr)
        continue;
    
    ... do something with dev ...
    
    (*dev)->Release(dev);
}

IOObjectRelease(iter);

Since the code will eventually be run as a JNI library within a Java VM, I don't want to keep references to IOUSBDeviceInterface for a long period as it will be difficult to ensure with the JVM and its garbage collection that they are released in time. And for most of these USB devices, only metadata (PID, VID, serial etc.) is relevant. They are not opened for communication. So I'd rather release IOUSBDeviceInterface immediately and reopen them when they are actually used.

Is there a way to get a device path or other identifier of the USB device and then directly create a IOUSBDeviceInterface without enumerating all USB devices in the IO registry?

Or if the IO registry must be used always: What's the best identifier of the USB device so the iterator only returns this single USB device?

1 Answers

Given a entry/service from IOIteratorNext, IORegistryEntryGetPath does the trick:

io_string_t path;
int res = IORegistryEntryGetPath(service, kIOServicePlane, path);

It returns a path like:

IOService:/AppleARMPE/arm-io/AppleT600xIO/usb-drd0@2280000/AppleT6000USBXHCI@00000000/usb-drd0-port-hs@00100000/A39F3CD99B172BC@00100000

Using this path, the USB device can be retrieved from the IO registry:

io_service_t service = IORegistryEntryFromPath(kIOMasterPortDefault, path);
if (service == MACH_PORT_NULL)
    ...error handling... 

IOUSBDeviceInterface** dev = iokit_helper::get_interface<IOUSBDeviceInterface>(service, kIOUSBDeviceUserClientTypeID, kIOUSBDeviceInterfaceID);
if (dev == nullptr)
    ...error handling...;

IOReturn ret = (*dev)->USBDeviceOpen(dev);
Related