darwin: Retry IOCreatePlugInInterfaceForService on failure
authorJuan Cruz Viotti <juan@balena.io>
Wed, 20 Mar 2019 11:34:41 +0000 (11:34 +0000)
committerNathan Hjelm <hjelmn@me.com>
Fri, 5 Apr 2019 15:28:52 +0000 (09:28 -0600)
We have a case where a device might not be detected by libusb if we plug
it after initializing the library. We were able to reproduce this issue
pretty consistently on a Mac Mini and several MacBook Pros running very
recent macOS versions (Mojave) using the Balena Fin board
(https://www.balena.io/fin/). I don't know if this happens with other
USB devices.

Enabling debug output revealed the following:

```
[ 7.901582] [00004f07] libusb: debug [darwin_get_cached_device] finding cached device for sessionID 0x2a9b5279f04
[ 7.901764] [00004f07] libusb: debug [darwin_get_cached_device] matching sessionID 0x2a9b5279f04 against cached device with sessionID 0x17dbd029b6a
[ 7.901798] [00004f07] libusb: debug [darwin_get_cached_device] matching sessionID 0x2a9b5279f04 against cached device with sessionID 0x1688763269f
[ 7.901818] [00004f07] libusb: debug [darwin_get_cached_device] matching sessionID 0x2a9b5279f04 against cached device with sessionID 0x4c0aefaa
[ 7.901831] [00004f07] libusb: debug [darwin_get_cached_device] matching sessionID 0x2a9b5279f04 against cached device with sessionID 0x0
[ 7.901845] [00004f07] libusb: debug [darwin_get_cached_device] caching new device with sessionID 0x2a9b5279f04
[ 7.903377] [00004f07] libusb: debug [darwin_device_from_service] could not set up plugin for service: out of resources
[ 8.029152] [00000307] libusb: debug [libusb_get_device_list]
```

The "out of resources" error comes from
`IOCreatePlugInInterfaceForService` which will apparently return this
error if the `Start` method of the interface is not success (see
https://github.com/opensource-apple/IOKitUser/blob/b80a5cbc0ebfb5c4954ef6d757918db0e4dc4b7f/IOCFPlugIn.c#L232).

Retrying `IOCreatePlugInInterfaceForService` makes it work for me.

Closes #542

Signed-off-by: Juan Cruz Viotti <juan@balena.io>
Signed-off-by: Nathan Hjelm <hjelmn@me.com>
libusb/os/darwin_usb.c
libusb/version_nano.h

index dd391db..f6e853b 100644 (file)
@@ -297,10 +297,24 @@ static usb_device_t **darwin_device_from_service (io_service_t service)
   usb_device_t **device;
   IOReturn kresult;
   SInt32 score;
+  const int max_retries = 5;
+
+  /* The IOCreatePlugInInterfaceForService function might consistently return
+     an "out of resources" error with certain USB devices the first time we run 
+     it. The reason is still unclear, but retrying fixes the problem */
+  for (int count = 0; count < max_retries; count++) {
+    kresult = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID,
+                                                kIOCFPlugInInterfaceID, &plugInInterface,
+                                                &score);
+    if (kIOReturnSuccess == kresult && plugInInterface) {
+      break;
+    }
 
-  kresult = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID,
-                                              kIOCFPlugInInterfaceID, &plugInInterface,
-                                              &score);
+    usbi_dbg ("set up plugin for service retry: %s", darwin_error_str (kresult));
+
+    /* sleep for a little while before trying again */
+    nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 1000}, NULL);
+  }
 
   if (kIOReturnSuccess != kresult || !plugInInterface) {
     usbi_dbg ("could not set up plugin for service: %s", darwin_error_str (kresult));
index ae13822..e542188 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 11361
+#define LIBUSB_NANO 11362