-
Notifications
You must be signed in to change notification settings - Fork 2k
macOS: implement device capture to force unload of KEXT drivers #906
Description
Pull request https://github.com/libusb/libusb/pull/893 has been closed but the idea seems to be pretty interesting.
From the author osy:
It's a well known issue on macOS that you cannot use libusb to attach to an interface already claimed by an KEXT for exclusive access. The common workaround is to do sudo kextunload or inject a codeless KEXT. Both require admin permissions, and does not work for all devices on latest macOS. Additionally, SIP has to be disabled to inject a codeless KEXT, making it a pain to work with USB devices.
This patch uses the kUSBReEnumerateCaptureDeviceBit introduced back in macOS 10.10 which will tell the kernel to unload all other drivers for you without needing any dirty hacks.
Unfortunately, a side effect is that it will reset (simulate unplug-replug) the device to do this and so I didn't want to make it the default behaviour of libusb_open as that may, for example, cause all USB devices to be reset if the application decides to read the product string of every device attached. I can't find a good way to change this without touching the API specs, so I introduced a new option LIBUSB_OPTION_FORCE_CAPTURE_OPEN that lets you toggle the "capture" behaviour.
Also, the way libusb_close works makes it a bit awkward to implement the "release" of the captured device because just closing the handle does not tell the KEXTs to start matching again and calling USBDeviceReEnumerate with kUSBReEnumerateReleaseDeviceBit will also cause the device to be reset which then makes the next call of libusb_open fetch a stale cached object and fail. However, calling darwin_reset_device to address the stale cache issue will cause the device to be opened again, which causes another "capture" and then the OS will never get the device back. So the workaround of this is as follows:
- When
darwin_closeis called, if the device is captured, thendarwin_reset_devicewill first be called. darwin_reset_device_capturewill callUSBDeviceReEnumerateand release the capture.darwin_restore_statecallsdarwin_openindicating that capture should NOT be used.darwin_opensucceeds and the device is re-opened without the capture bit.- Call returns to
darwin_closewho callsUSBDeviceCloseon the non-captured device handle.