Conversation
|
The issue is that for since time ResetDevice has been a noop in macOS. If I remember correctly we even confirmed that with a wireshark. Do we know if that has changed? If not we may need to figure out how to make this work with reenumerate. |
|
|
||
| if (dpriv->capture_count > 0) { | ||
| /* we have to use ResetDevice as USBDeviceReEnumerate() loses the authorization for capture */ | ||
| kresult = (*(dpriv->device))->ResetDevice (dpriv->device); |
There was a problem hiding this comment.
This is ok for now but if possible it would be better if we can be consistent and always re-enumerate the device. This ensures that the device is actually reset. I am surprised we can't just specify capture again on reset but I have to assume you have already tried this. We may want to call this out somewhere in the docs so that users are aware that reset may not actually reset when capturing a device.
There was a problem hiding this comment.
Yup I tried that the main problem is if you need authorization (it’s not running as root and it shows the user a pop-up to approve the device), it will need it again after calling re-enumerate (with or without the release flag). This means the user needs to click “approve” after every re-enum.
Perhaps someone with a usb sniffer can check the behaviour of the reset call again?
|
LGTM. Please squash into a minimal set of commits and we will merge. |
Can we wait for someone to test USB reset with a sniffer? |
USBDeviceReEnumerate() does not return an error code (bug?) so if it fails we could be stuck waiting forever. Set a sane timeout to 10s.
Since it calls USBDeviceReEnumerate() and not USBDeviceReset(), we name it accordingly.
When libusb_detach_kernel_driver() is called on the first interface, we use the capture re-enumerate APIs to force kernel drivers to detach. On subsequent calls, a counter is incremented to keep track of the number of detach calls. When libusb_attach_kernel_driver() is called for the same number of times, then we re-enumerate (reset) and let kernel drivers re-attach. darwin_kernel_driver_active() is changed to attempt to claim an interface and return 1 only if a kIOReturnExclusiveAccess status is returned. The old implementation which looks for a CFBundleID in the IORegistryEntry does not seem to work in all cases. darwin_reset_device() is aware of the capture status and will re-set twice if capture mode is enabled: once to do a USB reset and again to re-capture.
To use USBDeviceReEnumerate with kUSBReEnumerateCaptureDeviceMask your app either needs to be running as root OR have the 'com.apple.vm.device-access' entitlement AND have the user authorization requested via IOServiceAuthorize(). We can use the capture re-enumerate APIs if either 1) the process is running as root or 2) the 'com.apple.vm.device-access' entitlement is used AND IOServiceAuthorize() is called. We assume that if the entitlement is not there then we are running as root--if this is not true, then darwin_detach_kernel_driver will fail anyways. The authorization status is cached in the device's start() so we have to stop() and start() the device by destroying the plugin and recreating it again.
The InterfaceVersion is what we want and there is no reason to also check that the InterfaceVersion matches the macOS version it was introduced in. This check inadvertently disables features for other Apple platforms that use IOKit.
|
I use libusb with a computer emulator called QEMU. I was able to test out the changes in the above patches. They do work. I was able to listen to MP3 files in several of my VM's using a real attached USB sound card. Good job everyone. |
|
@hjelmn was USB reset tested? I’m still having some issues that I think might be related to reset but I don’t have any sniffer to test. I’ve been locally experimenting with a patch that implements the reenumeration but the kernel drivers always reattach as soon as you re-enumerate and then you can re-capture but there is a period of time when the kernel drivers take control and can possibly modify the device state. I’m still not sure how to do a “clean” reset while keeping capture state. |
I would like to get these changes up in a 1.0.25 RC so a wider audience can start testing. I don't have the equipment to test that reset works as expected. If there is a known issue we can add a known issues list while a fix is developed. Given how capture works I don't think we can easily fix it if it is broken. |
|
I tested this with various devices (gamepads, mouse, kbd, camera, USB Disk, ...) and it works like a charm! Thanks a lot. |
|
This pull request seems to cause regression as mentioned in #945 . |
|
Ref: Device Capture Discussion |
|
Ref: Property List Key: com.apple.vm.device-access Discussion |
|
https://developer.apple.com/documentation/bundleresources/entitlements Discussion You configure entitlements for your app by declaring capabilities for a target in Xcode. Xcode records capabilities that you add in a property list file with the .entitlements extension. You can also edit the entitlements file directly. When code signing your app, Xcode combines the entitlements file, information from your developer account, and other project information to apply a final set of entitlements to your app. |
|
@osy Could you provide more details how you modify hidtest (HIDAPI test application) to use libusb and use this new feature under macOS? Thanks. |
|
Yes latest git head is good. |
|
And hidtest works as well. |
|
And it is also okay with USB Composite Devices. |
|
@hjelmn @osy We may need to improve the documentation here. The other thing is to document how to get the entitlement (I am not using Xcode so I have not tried it myself). |
|
There actually isn’t a documented way. I had to manually contact Apple to get them. They are included with the VM entitlements and there’s no signup form or anything. You just have to purchase an Apple Developer membership and contact their technical support. |
I see. Thanks. Maybe I will just mention this in the wiki. |
|
@osy I think the Developer provisioning profile process should be similar to the following. Is that correct? |
|
You need |
A bit of mentioning in Apple websites. But I agree it is not well documented. https://developer.apple.com/documentation/bundleresources/entitlements |
|
Improved the error message a bit: |
|
Yes, the new warning and info message are good. |
It's took me a while for reading history above, and a little bit confused. Is there any solution? macOS12 has released its official version, the libuvc still can't access camera anyhow. I did use the com.apple.vm.device-access in the entitlements. |
@llinshenzhen |
|
This is probably not a good place to continue the discussion as it has already been closed. |
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 kextunloador 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.The original implementation relied on a new option but that has been superseded by a new implementation which uses the
libusb_detach_kernel_driver()andlibusb_set_auto_detach_kernel_driver()APIs. However there are a few caveats.First, the
libusb_detach_kernel_driverAPIs expect a single USB interface to be detachable. On macOS, we can only "capture" an entire USB device (and force all interfaces to be released by any existing driver). To keep compatibility with existing code, we count the number of (successful) calls tolibusb_detach_kernel_driverand when that number is balanced bylibusb_attach_kernel_driver, the entire device is re-enumerated and the original kernel driver is allowed to re-attach.Second,
libusb_detach_kernel_driveris expected to be called afterlibusb_open(this doesn't seem to be documented but I see that is the usage from existing code). This is problematic because the effect ofUSBDeviceReEnumeratewithkUSBReEnumerateCaptureDeviceMaskdoesn't seem to take place until afterUSBDeviceOpen. So we calldarwin_restore_stateto close and re-open the device.Third,
libusb_reset_deviceusesUSBDeviceReEnumeratewhich will release the capture flag and allow the driver to re-claim the interfaces. I'm not sure what the reasoning for avoidingResetDevice(I see a comment "from macOS 10.11 ResetDevice no longer does anything so just use USBDeviceReEnumerate" but it seems to work fine in 10.15...). If we are currently capturing a USB device, thenResetDeviceis used instead ofUSBDeviceReEnumerate.kUSBReEnumerateCaptureDeviceMaskrequires either the application to be running as root OR the application has the entitlementcom.apple.vm.device-accessAND the user has accepted the authorization for the capture withIOServiceAuthorize. To get the second part to work, we detect when the entitlementcom.apple.vm.device-accessis available and then callIOServiceAuthorizeindarwin_detach_kernel_driverbeforedarwin_reenumerate_device. However, as the authorization decision is cached in the plugin's start() call, it is necessary to stop and start the USB plugin again.I've only tested this with hidtest (modified to use libusb on OSX) and with libusbredir (in spice-gtk). More testing is needed specifically with calling libusb_detach_kernel_driver on multiple interfaces. On calling libusb_detach_kernel_driver before libusb_open or libusb_attach_kernel_driver after libusb_close (I'm not sure if this should be a supported use case but the APIs don't prescribe the usage order...). I'm also not sure why
ResetDevicewas avoided so there may be subtle issues indarwin_reset_device. Finally, there should be some testing with multiple calls to libusb_open/libusb_close.