From e1e526dc3c8bbfc74cae6fa72e7465408f1e9ece Mon Sep 17 00:00:00 2001 From: Charles Strahan Date: Sat, 22 Mar 2025 05:17:05 -0500 Subject: [PATCH 1/8] macos: add kernel driver detaching --- Cargo.toml | 1 + src/device.rs | 26 +- src/platform/macos_iokit/device.rs | 194 ++++- src/platform/macos_iokit/iokit.rs | 21 +- src/platform/macos_iokit/iokit_c.rs | 1153 +++++++++++++++++++++++---- src/platform/macos_iokit/mod.rs | 88 ++ 6 files changed, 1328 insertions(+), 155 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 67a0576f..ddfee90a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ windows-sys = { version = "0.59.0", features = ["Win32_Devices_Usb", "Win32_Devi core-foundation = "0.9.3" core-foundation-sys = "0.8.4" io-kit-sys = "0.4.0" +libc = "0.2" [target.'cfg(any(target_os="linux", target_os="android", target_os="windows", target_os="macos"))'.dependencies] blocking ="1.6.1" diff --git a/src/device.rs b/src/device.rs index 9eff6ad8..bcc81667 100644 --- a/src/device.rs +++ b/src/device.rs @@ -97,10 +97,10 @@ impl Device { /// Attach kernel drivers for the specified interface. /// /// ### Platform notes - /// This function can only attach kernel drivers on Linux. Calling on other platforms has + /// This function can only attach kernel drivers on Linux and macOS. Calling on other platforms has /// no effect. pub fn attach_kernel_driver(&self, interface: u8) -> Result<(), Error> { - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "macos"))] self.backend.attach_kernel_driver(interface)?; let _ = interface; @@ -158,6 +158,24 @@ impl Device { self.backend.clone().set_configuration(configuration) } + pub fn set_configuration_detached( + &self, + configuration: u8, + ) -> impl MaybeFuture> { + self.backend + .clone() + .set_configuration_detached(configuration) + } + + pub fn is_kernel_driver_attached_to_interface( + &self, + interface_number: u8, + ) -> impl MaybeFuture> { + self.backend + .clone() + .is_kernel_driver_attached_to_interface(interface_number) + } + /// Request a descriptor from the device. /// /// The `language_id` should be `0` unless you are requesting a string descriptor. @@ -267,6 +285,10 @@ impl Device { self.backend.clone().reset() } + pub fn reset_captured(&self) -> impl MaybeFuture> { + self.backend.clone().reset_detached() + } + /// Synchronously perform a single **IN (device-to-host)** transfer on the default **control** endpoint. /// /// ### Platform-specific notes diff --git a/src/platform/macos_iokit/device.rs b/src/platform/macos_iokit/device.rs index 0591bf45..e7830a51 100644 --- a/src/platform/macos_iokit/device.rs +++ b/src/platform/macos_iokit/device.rs @@ -9,11 +9,16 @@ use std::{ time::Duration, }; +use io_kit_sys::{ + kIOServiceInteractionAllowed, keys::kIOServicePlane, IOObjectRelease, IOObjectRetain, + IORegistryEntryGetChildEntry, +}; use log::{debug, error}; use crate::{ descriptors::{ConfigurationDescriptor, DeviceDescriptor}, maybe_future::blocking::Blocking, + platform::macos_iokit::iokit_c::kUSBReEnumerateCaptureDeviceMask, transfer::{Control, Direction, TransferError, TransferHandle, TransferType}, DeviceInfo, Error, MaybeFuture, Speed, }; @@ -21,7 +26,7 @@ use crate::{ use super::{ enumeration::{device_descriptor_from_fields, service_by_registry_id}, events::{add_event_source, EventRegistration}, - iokit::{call_iokit_function, check_iokit_return}, + iokit::{call_iokit_function, check_iokit_return, ioservice_authorize, IoService}, iokit_c::IOUSBDevRequestTO, iokit_usb::{EndpointInfo, IoKitDevice, IoKitInterface}, status_to_transfer_result, @@ -29,7 +34,9 @@ use super::{ pub(crate) struct MacDevice { _event_registration: EventRegistration, + service: IoService, pub(super) device: IoKitDevice, + device_descriptor: DeviceDescriptor, speed: Option, active_config: AtomicU8, @@ -90,6 +97,7 @@ impl MacDevice { Ok(Arc::new(MacDevice { _event_registration, + service, device, device_descriptor, speed, @@ -156,6 +164,48 @@ impl MacDevice { }) } + pub(crate) fn set_configuration_detached( + self: Arc, + configuration: u8, + ) -> impl MaybeFuture> { + Blocking::new(move || { + self.require_open_exclusive()?; + + let device; + let device_raw; + if security::have_capture_entitlement() { + // Request authorization + check_iokit_return(ioservice_authorize( + &self.service, + kIOServiceInteractionAllowed, + ))?; + + // Need to re-open the device for authorization to take effect + device = IoKitDevice::new(&self.service)?; + device_raw = device.raw; + } else { + if unsafe { libc::geteuid() != 0 } { + return Err(Error::new( + ErrorKind::PermissionDenied, + "Operation not permitted (requires root or com.apple.vm.device-access entitlement)", + )); + } + + device_raw = self.device.raw; + } + + unsafe { + check_iokit_return(call_iokit_function!( + device_raw, + SetConfigurationV2(configuration, false, true) + ))? + } + log::debug!("Set configuration {configuration}"); + self.active_config.store(configuration, Ordering::SeqCst); + Ok(()) + }) + } + pub(crate) fn reset(self: Arc) -> impl MaybeFuture> { Blocking::new(move || { self.require_open_exclusive()?; @@ -168,6 +218,42 @@ impl MacDevice { }) } + pub(crate) fn reset_detached(self: Arc) -> impl MaybeFuture> { + Blocking::new(move || { + self.require_open_exclusive()?; + + let device; + let device_raw; + if security::have_capture_entitlement() { + // Request authorization + check_iokit_return(ioservice_authorize( + &self.service, + kIOServiceInteractionAllowed, + ))?; + + // Need to re-open the device for authorization to take effect + device = IoKitDevice::new(&self.service)?; + device_raw = device.raw; + } else { + if unsafe { libc::geteuid() != 0 } { + return Err(Error::new( + ErrorKind::PermissionDenied, + "Operation not permitted (requires root or com.apple.vm.device-access entitlement)", + )); + } + + device_raw = self.device.raw; + } + + unsafe { + check_iokit_return(call_iokit_function!( + device_raw, + USBDeviceReEnumerate(kUSBReEnumerateCaptureDeviceMask) + )) + } + }) + } + /// SAFETY: `data` must be valid for `len` bytes to read or write, depending on `Direction` unsafe fn control_blocking( &self, @@ -233,6 +319,51 @@ impl MacDevice { TransferHandle::new(super::TransferData::new_control(self.clone())) } + pub(crate) fn is_kernel_driver_attached_to_interface( + self: Arc, + interface_number: u8, + ) -> impl MaybeFuture> { + Blocking::new(move || { + let intf_service = self + .device + .create_interface_iterator()? + .nth(interface_number as usize) + .ok_or(Error::new(ErrorKind::NotFound, "interface not found"))?; + + let mut child = 0; + + unsafe { + IORegistryEntryGetChildEntry( + intf_service.get(), + kIOServicePlane as *mut i8, + &mut child, + ); + + if child != 0 { + IOObjectRelease(child); + Ok(true) + } else { + Ok(false) + } + } + }) + } + + pub(crate) fn attach_kernel_driver( + self: &Arc, + interface_number: u8, + ) -> Result<(), Error> { + let intf_service = self + .device + .create_interface_iterator()? + .nth(interface_number as usize) + .ok_or(Error::new(ErrorKind::NotFound, "interface not found"))?; + + let interface = IoKitInterface::new(intf_service)?; + + unsafe { check_iokit_return(call_iokit_function!(interface.raw, RegisterDriver())) } + } + pub(crate) fn claim_interface( self: Arc, interface_number: u8, @@ -409,3 +540,64 @@ impl Drop for MacInterface { .fetch_sub(1, Ordering::Release); } } + +pub(crate) mod security { + use core_foundation::{ + base::{ + kCFAllocatorDefault, CFAllocatorRef, CFGetTypeID, CFRelease, CFTypeID, CFTypeRef, + TCFType, + }, + boolean::CFBoolean, + error::CFErrorRef, + number::CFBooleanGetValue, + string::{CFString, CFStringRef}, + }; + + #[repr(C)] + #[derive(Debug, Copy, Clone)] + pub struct __SecTask { + _unused: [u8; 0], + } + pub type SecTaskRef = *mut __SecTask; + + #[link(name = "Security", kind = "framework")] + extern "C" { + pub fn SecTaskCreateFromSelf(allocator: CFAllocatorRef) -> SecTaskRef; + pub fn SecTaskCopyValueForEntitlement( + task: SecTaskRef, + entitlement: CFStringRef, + error: *mut CFErrorRef, + ) -> CFTypeRef; + } + + pub(crate) fn have_capture_entitlement() -> bool { + have_entitlement("com.apple.vm.device-access") + } + + pub fn have_entitlement(entitlement: &'static str) -> bool { + unsafe { + let task = SecTaskCreateFromSelf(kCFAllocatorDefault); + if task.is_null() { + return false; + } + + let value = SecTaskCopyValueForEntitlement( + task, + CFString::from_static_string(entitlement).as_concrete_TypeRef(), + std::ptr::null_mut(), + ); + + CFRelease(task.cast()); + + let entitled = !value.is_null() + && CFGetTypeID(value.cast()) == CFBoolean::type_id() + && CFBooleanGetValue(value.cast()); + + if !value.is_null() { + CFRelease(value.cast()); + } + + entitled + } + } +} diff --git a/src/platform/macos_iokit/iokit.rs b/src/platform/macos_iokit/iokit.rs index 30a87794..b751dc36 100644 --- a/src/platform/macos_iokit/iokit.rs +++ b/src/platform/macos_iokit/iokit.rs @@ -4,7 +4,7 @@ //! licensed under MIT OR Apache-2.0. use core_foundation_sys::uuid::CFUUIDBytes; -use io_kit_sys::{ret::IOReturn, IOIteratorNext, IOObjectRelease}; +use io_kit_sys::{ret::IOReturn, IOIteratorNext, IOObjectRelease, IOServiceAuthorize}; use std::io::ErrorKind; use crate::Error; @@ -106,15 +106,23 @@ impl Drop for PluginInterface { /// that we support macOS versions back to 10.7.3, which is currently every version that Rust /// supports. Use this instead of touching the iokit_c structure; this may be bumped to /// (compatible) newer versions of the struct as Rust's support changes. -pub(crate) type UsbDevice = iokit_c::IOUSBDeviceStruct500; -pub(crate) type UsbInterface = iokit_c::IOUSBInterfaceStruct500; +pub(crate) type UsbDevice = iokit_c::IOUSBDeviceStruct650; +pub(crate) type UsbInterface = iokit_c::IOUSBInterfaceStruct700; +// pub(crate) type UsbDevice = iokit_c::IOUSBDeviceStruct500; +// pub(crate) type UsbInterface = iokit_c::IOUSBInterfaceStruct500; pub(crate) fn usb_device_type_id() -> CFUUIDBytes { - unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBDeviceInterfaceID500()) } + // unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBDeviceInterfaceID500()) } + unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBDeviceInterfaceID650()) } } pub(crate) fn usb_interface_type_id() -> CFUUIDBytes { - unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBInterfaceInterfaceID500()) } + // unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBInterfaceInterfaceID500()) } + unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBInterfaceInterfaceID700()) } +} + +pub(crate) fn ioservice_authorize(service: &IoService, options: u32) -> IOReturn { + unsafe { IOServiceAuthorize(service.get(), options) } } pub(crate) fn check_iokit_return(r: IOReturn) -> Result<(), Error> { @@ -127,6 +135,9 @@ pub(crate) fn check_iokit_return(r: IOReturn) -> Result<(), Error> { "could not be opened for exclusive access", )), io_kit_sys::ret::kIOReturnNotFound => Err(Error::new(ErrorKind::NotFound, "not found")), + io_kit_sys::ret::kIOReturnNotPermitted => { + Err(Error::new(ErrorKind::PermissionDenied, "permission denied")) + } _ => Err(Error::from_raw_os_error(r)), } } diff --git a/src/platform/macos_iokit/iokit_c.rs b/src/platform/macos_iokit/iokit_c.rs index 96747b03..c69d99e6 100644 --- a/src/platform/macos_iokit/iokit_c.rs +++ b/src/platform/macos_iokit/iokit_c.rs @@ -22,7 +22,7 @@ use core_foundation_sys::{ use io_kit_sys::{ ret::IOReturn, types::{io_iterator_t, io_service_t, IOByteCount}, - IOAsyncCallback1, + IOAsyncCallback1, IOAsyncCallback2, }; // @@ -61,6 +61,9 @@ pub(crate) const kIOUSBTransactionTimeout: c_int = SYS_IOKIT | SUB_IOKIT_USB | 0 pub(crate) const kIOUSBFindInterfaceDontCare: UInt16 = 0xFFFF; +pub(crate) const kUSBReEnumerateCaptureDeviceBit: u32 = 30; +pub(crate) const kUSBReEnumerateCaptureDeviceMask: u32 = 1 << kUSBReEnumerateCaptureDeviceBit; + // // @@ -79,6 +82,24 @@ pub(crate) type USBDeviceAddress = UInt16; pub(crate) type AbsoluteTime = UnsignedWide; pub(crate) type Boolean = std::os::raw::c_uchar; +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct IOUSBEndpointProperties { + pub bVersion: UInt8, + pub bAlternateSetting: UInt8, + pub bDirection: UInt8, + pub bEndpointNumber: UInt8, + pub bTransferType: UInt8, + pub bUsageType: UInt8, + pub bSyncType: UInt8, + pub bInterval: UInt8, + pub wMaxPacketSize: UInt16, + pub bMaxBurst: UInt8, + pub bMaxStreams: UInt8, + pub bMult: UInt8, + pub wBytesPerInterval: UInt16, +} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct NumVersion { @@ -323,6 +344,30 @@ pub fn kIOUSBDeviceInterfaceID500() -> CFUUIDRef { } } +pub fn kIOUSBDeviceInterfaceID650() -> CFUUIDRef { + unsafe { + CFUUIDGetConstantUUIDWithBytes( + kCFAllocatorSystemDefault, + 0x4A, + 0xAC, + 0x1B, + 0x2E, + 0x24, + 0xC2, + 0x47, + 0x6A, + 0x96, + 0x4D, + 0x91, + 0x33, + 0x35, + 0x34, + 0xF2, + 0xCC, + ) + } +} + pub fn kIOUSBInterfaceInterfaceID500() -> CFUUIDRef { unsafe { CFUUIDGetConstantUUIDWithBytes( @@ -347,6 +392,30 @@ pub fn kIOUSBInterfaceInterfaceID500() -> CFUUIDRef { } } +pub fn kIOUSBInterfaceInterfaceID700() -> CFUUIDRef { + unsafe { + CFUUIDGetConstantUUIDWithBytes( + kCFAllocatorSystemDefault, + 0x17, + 0xF9, + 0xE5, + 0x9C, + 0xB0, + 0xA1, + 0x40, + 0x1D, + 0x9A, + 0xC0, + 0x8D, + 0xE2, + 0x7A, + 0xC6, + 0x04, + 0x7E, + ) + } +} + #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct IOUSBConfigurationDescriptor { @@ -597,7 +666,7 @@ unsafe impl Sync for IOUSBDeviceInterface500 {} #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct IOUSBInterfaceStruct500 { +pub struct IOUSBDeviceStruct650 { pub _reserved: *mut ::std::os::raw::c_void, pub QueryInterface: ::std::option::Option< unsafe extern "C" fn( @@ -612,41 +681,41 @@ pub struct IOUSBInterfaceStruct500 { pub Release: ::std::option::Option< unsafe extern "C" fn(thisPointer: *mut ::std::os::raw::c_void) -> ULONG, >, - pub CreateInterfaceAsyncEventSource: ::std::option::Option< + pub CreateDeviceAsyncEventSource: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, source: *mut CFRunLoopSourceRef, ) -> IOReturn, >, - pub GetInterfaceAsyncEventSource: ::std::option::Option< + pub GetDeviceAsyncEventSource: ::std::option::Option< unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void) -> CFRunLoopSourceRef, >, - pub CreateInterfaceAsyncPort: ::std::option::Option< + pub CreateDeviceAsyncPort: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, port: *mut mach_port_t, ) -> IOReturn, >, - pub GetInterfaceAsyncPort: ::std::option::Option< + pub GetDeviceAsyncPort: ::std::option::Option< unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void) -> mach_port_t, >, - pub USBInterfaceOpen: + pub USBDeviceOpen: ::std::option::Option IOReturn>, - pub USBInterfaceClose: + pub USBDeviceClose: ::std::option::Option IOReturn>, - pub GetInterfaceClass: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, intfClass: *mut UInt8) -> IOReturn, + pub GetDeviceClass: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, devClass: *mut UInt8) -> IOReturn, >, - pub GetInterfaceSubClass: ::std::option::Option< + pub GetDeviceSubClass: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - intfSubClass: *mut UInt8, + devSubClass: *mut UInt8, ) -> IOReturn, >, - pub GetInterfaceProtocol: ::std::option::Option< + pub GetDeviceProtocol: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - intfProtocol: *mut UInt8, + devProtocol: *mut UInt8, ) -> IOReturn, >, pub GetDeviceVendor: ::std::option::Option< @@ -667,26 +736,23 @@ pub struct IOUSBInterfaceStruct500 { devRelNum: *mut UInt16, ) -> IOReturn, >, - pub GetConfigurationValue: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, configVal: *mut UInt8) -> IOReturn, - >, - pub GetInterfaceNumber: ::std::option::Option< + pub GetDeviceAddress: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - intfNumber: *mut UInt8, + addr: *mut USBDeviceAddress, ) -> IOReturn, >, - pub GetAlternateSetting: ::std::option::Option< + pub GetDeviceBusPowerAvailable: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - intfAltSetting: *mut UInt8, + powerAvailable: *mut UInt32, ) -> IOReturn, >, - pub GetNumEndpoints: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - intfNumEndpoints: *mut UInt8, - ) -> IOReturn, + pub GetDeviceSpeed: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, devSpeed: *mut UInt8) -> IOReturn, + >, + pub GetNumberOfConfigurations: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, numConfig: *mut UInt8) -> IOReturn, >, pub GetLocationID: ::std::option::Option< unsafe extern "C" fn( @@ -694,17 +760,18 @@ pub struct IOUSBInterfaceStruct500 { locationID: *mut UInt32, ) -> IOReturn, >, - pub GetDevice: ::std::option::Option< + pub GetConfigurationDescriptorPtr: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - device: *mut io_service_t, + configIndex: UInt8, + desc: *mut IOUSBConfigurationDescriptorPtr, ) -> IOReturn, >, - pub SetAlternateInterface: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - alternateSetting: UInt8, - ) -> IOReturn, + pub GetConfiguration: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, configNum: *mut UInt8) -> IOReturn, + >, + pub SetConfiguration: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, configNum: UInt8) -> IOReturn, >, pub GetBusFrameNumber: ::std::option::Option< unsafe extern "C" fn( @@ -713,185 +780,864 @@ pub struct IOUSBInterfaceStruct500 { atTime: *mut AbsoluteTime, ) -> IOReturn, >, - pub ControlRequest: ::std::option::Option< + pub ResetDevice: + ::std::option::Option IOReturn>, + pub DeviceRequest: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, req: *mut IOUSBDevRequest, ) -> IOReturn, >, - pub ControlRequestAsync: ::std::option::Option< + pub DeviceRequestAsync: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, req: *mut IOUSBDevRequest, callback: IOAsyncCallback1, refCon: *mut ::std::os::raw::c_void, ) -> IOReturn, >, - pub GetPipeProperties: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - direction: *mut UInt8, - number: *mut UInt8, - transferType: *mut UInt8, - maxPacketSize: *mut UInt16, - interval: *mut UInt8, - ) -> IOReturn, - >, - pub GetPipeStatus: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, - >, - pub AbortPipe: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, - >, - pub ResetPipe: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, - >, - pub ClearPipeStall: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, - >, - pub ReadPipe: ::std::option::Option< + pub CreateInterfaceIterator: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: *mut UInt32, + req: *mut IOUSBFindInterfaceRequest, + iter: *mut io_iterator_t, ) -> IOReturn, >, - pub WritePipe: ::std::option::Option< + pub USBDeviceOpenSeize: + ::std::option::Option IOReturn>, + pub DeviceRequestTO: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: UInt32, + req: *mut IOUSBDevRequestTO, ) -> IOReturn, >, - pub ReadPipeAsync: ::std::option::Option< + pub DeviceRequestAsyncTO: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: UInt32, + req: *mut IOUSBDevRequestTO, callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, + refCon: *mut ::std::os::raw::c_void, ) -> IOReturn, >, - pub WritePipeAsync: ::std::option::Option< + pub USBDeviceSuspend: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, suspend: Boolean) -> IOReturn, + >, + pub USBDeviceAbortPipeZero: + ::std::option::Option IOReturn>, + pub USBGetManufacturerStringIndex: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, msi: *mut UInt8) -> IOReturn, + >, + pub USBGetProductStringIndex: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, psi: *mut UInt8) -> IOReturn, + >, + pub USBGetSerialNumberStringIndex: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, snsi: *mut UInt8) -> IOReturn, + >, + pub USBDeviceReEnumerate: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, options: UInt32) -> IOReturn, + >, + pub GetBusMicroFrameNumber: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: UInt32, - callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, + microFrame: *mut UInt64, + atTime: *mut AbsoluteTime, ) -> IOReturn, >, - pub ReadIsochPipeAsync: ::std::option::Option< + pub GetIOUSBLibVersion: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - frameStart: UInt64, - numFrames: UInt32, - frameList: *mut IOUSBIsocFrame, - callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, + ioUSBLibVersion: *mut NumVersion, + usbFamilyVersion: *mut NumVersion, ) -> IOReturn, >, - pub WriteIsochPipeAsync: ::std::option::Option< + pub GetBusFrameNumberWithTime: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - frameStart: UInt64, - numFrames: UInt32, - frameList: *mut IOUSBIsocFrame, - callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, + frame: *mut UInt64, + atTime: *mut AbsoluteTime, ) -> IOReturn, >, - pub ControlRequestTO: ::std::option::Option< + pub GetUSBDeviceInformation: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, info: *mut UInt32) -> IOReturn, + >, + pub RequestExtraPower: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - req: *mut IOUSBDevRequestTO, + type_: UInt32, + requestedPower: UInt32, + powerAvailable: *mut UInt32, ) -> IOReturn, >, - pub ControlRequestAsyncTO: ::std::option::Option< + pub ReturnExtraPower: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - req: *mut IOUSBDevRequestTO, - callback: IOAsyncCallback1, - refCon: *mut ::std::os::raw::c_void, + type_: UInt32, + powerReturned: UInt32, ) -> IOReturn, >, - pub ReadPipeTO: ::std::option::Option< + pub GetExtraPowerAllocated: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: *mut UInt32, - noDataTimeout: UInt32, - completionTimeout: UInt32, + type_: UInt32, + powerAllocated: *mut UInt32, ) -> IOReturn, >, - pub WritePipeTO: ::std::option::Option< + pub GetBandwidthAvailableForDevice: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: UInt32, - noDataTimeout: UInt32, - completionTimeout: UInt32, + bandwidth: *mut UInt32, ) -> IOReturn, >, - pub ReadPipeAsyncTO: ::std::option::Option< + pub SetConfigurationV2: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: UInt32, - noDataTimeout: UInt32, - completionTimeout: UInt32, - callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, + configNum: UInt8, + startInterfaceMatching: bool, + issueRemoteWakeup: bool, ) -> IOReturn, >, - pub WritePipeAsyncTO: ::std::option::Option< + pub RegisterForNotification: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: UInt32, - noDataTimeout: UInt32, - completionTimeout: UInt32, - callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, + notificationMask: UInt64, + callback: IOAsyncCallback2, + refCon: *mut ::std::os::raw::c_void, + pRegistrationToken: *mut UInt64, ) -> IOReturn, >, - pub USBInterfaceGetStringIndex: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, si: *mut UInt8) -> IOReturn, - >, - pub USBInterfaceOpenSeize: - ::std::option::Option IOReturn>, - pub ClearPipeStallBothEnds: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, - >, - pub SetPipePolicy: ::std::option::Option< + pub UnregisterNotification: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - maxPacketSize: UInt16, - maxInterval: UInt8, + registrationToken: UInt64, ) -> IOReturn, >, - pub GetBandwidthAvailable: ::std::option::Option< + pub AcknowledgeNotification: ::std::option::Option< unsafe extern "C" fn( self_: *mut ::std::os::raw::c_void, - bandwidth: *mut UInt32, + notificationToken: UInt64, + ) -> IOReturn, + >, +} +pub type IOUSBDeviceInterface650 = IOUSBDeviceStruct650; + +// Tweak: these are just function pointers to thread-safe functions, +// so add send and sync to the C-type. (Calling these from multiple threads +// may cause odd behavior on the USB bus, though, so we'll still want to wrap the +// device in Mutex somewhere up from here.) +unsafe impl Send for IOUSBDeviceInterface650 {} +unsafe impl Sync for IOUSBDeviceInterface650 {} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct IOUSBInterfaceStruct500 { + pub _reserved: *mut ::std::os::raw::c_void, + pub QueryInterface: ::std::option::Option< + unsafe extern "C" fn( + thisPointer: *mut ::std::os::raw::c_void, + iid: REFIID, + ppv: *mut LPVOID, + ) -> HRESULT, + >, + pub AddRef: ::std::option::Option< + unsafe extern "C" fn(thisPointer: *mut ::std::os::raw::c_void) -> ULONG, + >, + pub Release: ::std::option::Option< + unsafe extern "C" fn(thisPointer: *mut ::std::os::raw::c_void) -> ULONG, + >, + pub CreateInterfaceAsyncEventSource: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + source: *mut CFRunLoopSourceRef, + ) -> IOReturn, + >, + pub GetInterfaceAsyncEventSource: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void) -> CFRunLoopSourceRef, + >, + pub CreateInterfaceAsyncPort: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + port: *mut mach_port_t, + ) -> IOReturn, + >, + pub GetInterfaceAsyncPort: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void) -> mach_port_t, + >, + pub USBInterfaceOpen: + ::std::option::Option IOReturn>, + pub USBInterfaceClose: + ::std::option::Option IOReturn>, + pub GetInterfaceClass: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, intfClass: *mut UInt8) -> IOReturn, + >, + pub GetInterfaceSubClass: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + intfSubClass: *mut UInt8, + ) -> IOReturn, + >, + pub GetInterfaceProtocol: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + intfProtocol: *mut UInt8, + ) -> IOReturn, + >, + pub GetDeviceVendor: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + devVendor: *mut UInt16, + ) -> IOReturn, + >, + pub GetDeviceProduct: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + devProduct: *mut UInt16, + ) -> IOReturn, + >, + pub GetDeviceReleaseNumber: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + devRelNum: *mut UInt16, + ) -> IOReturn, + >, + pub GetConfigurationValue: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, configVal: *mut UInt8) -> IOReturn, + >, + pub GetInterfaceNumber: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + intfNumber: *mut UInt8, + ) -> IOReturn, + >, + pub GetAlternateSetting: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + intfAltSetting: *mut UInt8, + ) -> IOReturn, + >, + pub GetNumEndpoints: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + intfNumEndpoints: *mut UInt8, + ) -> IOReturn, + >, + pub GetLocationID: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + locationID: *mut UInt32, + ) -> IOReturn, + >, + pub GetDevice: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + device: *mut io_service_t, + ) -> IOReturn, + >, + pub SetAlternateInterface: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + alternateSetting: UInt8, + ) -> IOReturn, + >, + pub GetBusFrameNumber: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + frame: *mut UInt64, + atTime: *mut AbsoluteTime, + ) -> IOReturn, + >, + pub ControlRequest: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + req: *mut IOUSBDevRequest, + ) -> IOReturn, + >, + pub ControlRequestAsync: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + req: *mut IOUSBDevRequest, + callback: IOAsyncCallback1, + refCon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub GetPipeProperties: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + direction: *mut UInt8, + number: *mut UInt8, + transferType: *mut UInt8, + maxPacketSize: *mut UInt16, + interval: *mut UInt8, + ) -> IOReturn, + >, + pub GetPipeStatus: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, + >, + pub AbortPipe: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, + >, + pub ResetPipe: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, + >, + pub ClearPipeStall: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, + >, + pub ReadPipe: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: *mut UInt32, + ) -> IOReturn, + >, + pub WritePipe: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + ) -> IOReturn, + >, + pub ReadPipeAsync: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub WritePipeAsync: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub ReadIsochPipeAsync: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + frameStart: UInt64, + numFrames: UInt32, + frameList: *mut IOUSBIsocFrame, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub WriteIsochPipeAsync: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + frameStart: UInt64, + numFrames: UInt32, + frameList: *mut IOUSBIsocFrame, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub ControlRequestTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + req: *mut IOUSBDevRequestTO, + ) -> IOReturn, + >, + pub ControlRequestAsyncTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + req: *mut IOUSBDevRequestTO, + callback: IOAsyncCallback1, + refCon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub ReadPipeTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: *mut UInt32, + noDataTimeout: UInt32, + completionTimeout: UInt32, + ) -> IOReturn, + >, + pub WritePipeTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + noDataTimeout: UInt32, + completionTimeout: UInt32, + ) -> IOReturn, + >, + pub ReadPipeAsyncTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + noDataTimeout: UInt32, + completionTimeout: UInt32, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub WritePipeAsyncTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + noDataTimeout: UInt32, + completionTimeout: UInt32, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub USBInterfaceGetStringIndex: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, si: *mut UInt8) -> IOReturn, + >, + pub USBInterfaceOpenSeize: + ::std::option::Option IOReturn>, + pub ClearPipeStallBothEnds: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, + >, + pub SetPipePolicy: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + maxPacketSize: UInt16, + maxInterval: UInt8, + ) -> IOReturn, + >, + pub GetBandwidthAvailable: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + bandwidth: *mut UInt32, + ) -> IOReturn, + >, + pub GetEndpointProperties: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + alternateSetting: UInt8, + endpointNumber: UInt8, + direction: UInt8, + transferType: *mut UInt8, + maxPacketSize: *mut UInt16, + interval: *mut UInt8, + ) -> IOReturn, + >, + pub LowLatencyReadIsochPipeAsync: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + frameStart: UInt64, + numFrames: UInt32, + updateFrequency: UInt32, + frameList: *mut IOUSBLowLatencyIsocFrame, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub LowLatencyWriteIsochPipeAsync: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + frameStart: UInt64, + numFrames: UInt32, + updateFrequency: UInt32, + frameList: *mut IOUSBLowLatencyIsocFrame, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub LowLatencyCreateBuffer: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + buffer: *mut *mut ::std::os::raw::c_void, + size: IOByteCount, + bufferType: UInt32, + ) -> IOReturn, + >, + pub LowLatencyDestroyBuffer: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + buffer: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub GetBusMicroFrameNumber: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + microFrame: *mut UInt64, + atTime: *mut AbsoluteTime, + ) -> IOReturn, + >, + pub GetFrameListTime: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + microsecondsInFrame: *mut UInt32, + ) -> IOReturn, + >, + pub GetIOUSBLibVersion: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + ioUSBLibVersion: *mut NumVersion, + usbFamilyVersion: *mut NumVersion, + ) -> IOReturn, + >, + pub FindNextAssociatedDescriptor: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + currentDescriptor: *const ::std::os::raw::c_void, + descriptorType: UInt8, + ) -> *mut IOUSBDescriptorHeader, + >, + pub FindNextAltInterface: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + current: *const ::std::os::raw::c_void, + request: *mut IOUSBFindInterfaceRequest, + ) -> *mut IOUSBDescriptorHeader, + >, + pub GetBusFrameNumberWithTime: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + frame: *mut UInt64, + atTime: *mut AbsoluteTime, + ) -> IOReturn, + >, + pub GetPipePropertiesV2: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + direction: *mut UInt8, + number: *mut UInt8, + transferType: *mut UInt8, + maxPacketSize: *mut UInt16, + interval: *mut UInt8, + maxBurst: *mut UInt8, + mult: *mut UInt8, + bytesPerInterval: *mut UInt16, + ) -> IOReturn, + >, +} + +// Tweak: these are just function pointers to thread-safe functions, +// so add send and sync to the C-type. (Calling these from multiple threads +// may cause odd behavior on the USB bus, though, so we'll still want to wrap the +// device in Mutex somewhere up from here.) +unsafe impl Send for IOUSBInterfaceStruct500 {} +unsafe impl Sync for IOUSBInterfaceStruct500 {} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct IOUSBInterfaceStruct700 { + pub _reserved: *mut ::std::os::raw::c_void, + pub QueryInterface: ::std::option::Option< + unsafe extern "C" fn( + thisPointer: *mut ::std::os::raw::c_void, + iid: REFIID, + ppv: *mut LPVOID, + ) -> HRESULT, + >, + pub AddRef: ::std::option::Option< + unsafe extern "C" fn(thisPointer: *mut ::std::os::raw::c_void) -> ULONG, + >, + pub Release: ::std::option::Option< + unsafe extern "C" fn(thisPointer: *mut ::std::os::raw::c_void) -> ULONG, + >, + pub CreateInterfaceAsyncEventSource: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + source: *mut CFRunLoopSourceRef, + ) -> IOReturn, + >, + pub GetInterfaceAsyncEventSource: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void) -> CFRunLoopSourceRef, + >, + pub CreateInterfaceAsyncPort: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + port: *mut mach_port_t, + ) -> IOReturn, + >, + pub GetInterfaceAsyncPort: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void) -> mach_port_t, + >, + pub USBInterfaceOpen: + ::std::option::Option IOReturn>, + pub USBInterfaceClose: + ::std::option::Option IOReturn>, + pub GetInterfaceClass: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, intfClass: *mut UInt8) -> IOReturn, + >, + pub GetInterfaceSubClass: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + intfSubClass: *mut UInt8, + ) -> IOReturn, + >, + pub GetInterfaceProtocol: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + intfProtocol: *mut UInt8, + ) -> IOReturn, + >, + pub GetDeviceVendor: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + devVendor: *mut UInt16, + ) -> IOReturn, + >, + pub GetDeviceProduct: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + devProduct: *mut UInt16, + ) -> IOReturn, + >, + pub GetDeviceReleaseNumber: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + devRelNum: *mut UInt16, + ) -> IOReturn, + >, + pub GetConfigurationValue: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, configVal: *mut UInt8) -> IOReturn, + >, + pub GetInterfaceNumber: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + intfNumber: *mut UInt8, + ) -> IOReturn, + >, + pub GetAlternateSetting: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + intfAltSetting: *mut UInt8, + ) -> IOReturn, + >, + pub GetNumEndpoints: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + intfNumEndpoints: *mut UInt8, + ) -> IOReturn, + >, + pub GetLocationID: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + locationID: *mut UInt32, + ) -> IOReturn, + >, + pub GetDevice: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + device: *mut io_service_t, + ) -> IOReturn, + >, + pub SetAlternateInterface: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + alternateSetting: UInt8, + ) -> IOReturn, + >, + pub GetBusFrameNumber: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + frame: *mut UInt64, + atTime: *mut AbsoluteTime, + ) -> IOReturn, + >, + pub ControlRequest: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + req: *mut IOUSBDevRequest, + ) -> IOReturn, + >, + pub ControlRequestAsync: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + req: *mut IOUSBDevRequest, + callback: IOAsyncCallback1, + refCon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub GetPipeProperties: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + direction: *mut UInt8, + number: *mut UInt8, + transferType: *mut UInt8, + maxPacketSize: *mut UInt16, + interval: *mut UInt8, + ) -> IOReturn, + >, + pub GetPipeStatus: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, + >, + pub AbortPipe: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, + >, + pub ResetPipe: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, + >, + pub ClearPipeStall: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, + >, + pub ReadPipe: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: *mut UInt32, + ) -> IOReturn, + >, + pub WritePipe: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + ) -> IOReturn, + >, + pub ReadPipeAsync: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub WritePipeAsync: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub ReadIsochPipeAsync: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + frameStart: UInt64, + numFrames: UInt32, + frameList: *mut IOUSBIsocFrame, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub WriteIsochPipeAsync: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + frameStart: UInt64, + numFrames: UInt32, + frameList: *mut IOUSBIsocFrame, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub ControlRequestTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + req: *mut IOUSBDevRequestTO, + ) -> IOReturn, + >, + pub ControlRequestAsyncTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + req: *mut IOUSBDevRequestTO, + callback: IOAsyncCallback1, + refCon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub ReadPipeTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: *mut UInt32, + noDataTimeout: UInt32, + completionTimeout: UInt32, + ) -> IOReturn, + >, + pub WritePipeTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + noDataTimeout: UInt32, + completionTimeout: UInt32, + ) -> IOReturn, + >, + pub ReadPipeAsyncTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + noDataTimeout: UInt32, + completionTimeout: UInt32, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub WritePipeAsyncTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + noDataTimeout: UInt32, + completionTimeout: UInt32, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub USBInterfaceGetStringIndex: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, si: *mut UInt8) -> IOReturn, + >, + pub USBInterfaceOpenSeize: + ::std::option::Option IOReturn>, + pub ClearPipeStallBothEnds: ::std::option::Option< + unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, + >, + pub SetPipePolicy: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + maxPacketSize: UInt16, + maxInterval: UInt8, + ) -> IOReturn, + >, + pub GetBandwidthAvailable: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + bandwidth: *mut UInt32, ) -> IOReturn, >, pub GetEndpointProperties: ::std::option::Option< @@ -1000,11 +1746,124 @@ pub struct IOUSBInterfaceStruct500 { bytesPerInterval: *mut UInt16, ) -> IOReturn, >, + pub GetPipePropertiesV3: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + properties: *mut IOUSBEndpointProperties, + ) -> IOReturn, + >, + pub GetEndpointPropertiesV3: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + properties: *mut IOUSBEndpointProperties, + ) -> IOReturn, + >, + pub SupportsStreams: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + supportsStreams: *mut UInt32, + ) -> IOReturn, + >, + pub CreateStreams: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + streamID: UInt32, + ) -> IOReturn, + >, + pub GetConfiguredStreams: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + configuredStreams: *mut UInt32, + ) -> IOReturn, + >, + pub ReadStreamsPipeTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + streamID: UInt32, + buf: *mut ::std::os::raw::c_void, + size: *mut UInt32, + noDataTimeout: UInt32, + completionTimeout: UInt32, + ) -> IOReturn, + >, + pub WriteStreamsPipeTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + streamID: UInt32, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + noDataTimeout: UInt32, + completionTimeout: UInt32, + ) -> IOReturn, + >, + pub ReadStreamsPipeAsyncTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + streamID: UInt32, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + noDataTimeout: UInt32, + completionTimeout: UInt32, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub WriteStreamsPipeAsyncTO: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + streamID: UInt32, + buf: *mut ::std::os::raw::c_void, + size: UInt32, + noDataTimeout: UInt32, + completionTimeout: UInt32, + callback: IOAsyncCallback1, + refcon: *mut ::std::os::raw::c_void, + ) -> IOReturn, + >, + pub AbortStreamsPipe: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + pipeRef: UInt8, + streamID: UInt32, + ) -> IOReturn, + >, + pub RegisterForNotification: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + notificationMask: UInt64, + callback: IOAsyncCallback2, + refCon: *mut ::std::os::raw::c_void, + pRegistrationToken: *mut UInt64, + ) -> IOReturn, + >, + pub UnregisterNotification: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + registrationToken: UInt64, + ) -> IOReturn, + >, + pub AcknowledgeNotification: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + notificationToken: UInt64, + ) -> IOReturn, + >, + pub RegisterDriver: + ::std::option::Option IOReturn>, } +pub type IOUSBInterfaceInterface700 = IOUSBInterfaceStruct700; // Tweak: these are just function pointers to thread-safe functions, // so add send and sync to the C-type. (Calling these from multiple threads // may cause odd behavior on the USB bus, though, so we'll still want to wrap the // device in Mutex somewhere up from here.) -unsafe impl Send for IOUSBInterfaceStruct500 {} -unsafe impl Sync for IOUSBInterfaceStruct500 {} +unsafe impl Send for IOUSBInterfaceInterface700 {} +unsafe impl Sync for IOUSBInterfaceInterface700 {} diff --git a/src/platform/macos_iokit/mod.rs b/src/platform/macos_iokit/mod.rs index 317f8be9..5645fdcb 100644 --- a/src/platform/macos_iokit/mod.rs +++ b/src/platform/macos_iokit/mod.rs @@ -1,5 +1,6 @@ mod transfer; use io_kit_sys::ret::IOReturn; +use once_cell::sync::OnceCell; pub(crate) use transfer::TransferData; mod enumeration; @@ -33,3 +34,90 @@ fn status_to_transfer_result(status: IOReturn) -> Result<(), TransferError> { _ => Err(TransferError::Unknown), } } + +type OsVersion = (u8, u8, u8); + +pub(crate) fn os_version() -> OsVersion { + static VERSION: OnceCell = OnceCell::new(); + *VERSION.get_or_init(|| { + read_osproductversion() + .or_else(|| read_osrelease()) + .unwrap_or((10, 0, 0)) + }) +} + +fn read_osproductversion() -> Option { + unsafe { + let mut buffer: [libc::c_char; 64] = [0; 64]; + let mut len: libc::size_t = buffer.len() - 1; + + let ret = libc::sysctlbyname( + "kern.osproductversion\0".as_ptr() as *const libc::c_char, + buffer.as_mut_ptr() as *mut _, + &mut len, + std::ptr::null_mut(), + 0, + ); + + if ret != 0 { + return None; + } + + let os_product_version_string = std::ffi::CStr::from_ptr(buffer.as_ptr()).to_str().ok()?; + let mut parts = os_product_version_string.split("."); + let major = parts.next().and_then(|s| s.parse().ok())?; + let minor = parts.next().and_then(|s| s.parse().ok())?; + let patch = parts.next().and_then(|s| s.parse().ok())?; + let version = (major, minor, patch); + + return Some(version); + } +} + +fn read_osrelease() -> Option { + unsafe { + let mut buffer: [libc::c_char; 64] = [0; 64]; + let mut len: libc::size_t = buffer.len() - 1; + + let ret = libc::sysctlbyname( + "kern.osrelease\0".as_ptr() as *const libc::c_char, + buffer.as_mut_ptr() as *mut _, + &mut len, + std::ptr::null_mut(), + 0, + ); + + if ret != 0 { + return None; + } + + let os_release_string = std::ffi::CStr::from_ptr(buffer.as_ptr()).to_str().ok()?; + let mut parts = os_release_string.split("."); + let darwin_major: u8 = parts.next().and_then(|s| s.parse().ok())?; + let darwin_minor: u8 = parts.next().and_then(|s| s.parse().ok())?; + + let major; + let minor; + let patch; + if darwin_major == 1 && darwin_minor < 4 { + major = 10; + minor = 0; + patch = 0; + } else if darwin_major < 6 { + major = 10; + minor = 1; + patch = 0; + } else if darwin_major < 20 { + major = 10; + minor = darwin_major - 4; + patch = darwin_minor; + } else { + major = darwin_major - 9; + minor = darwin_minor; + patch = 0; + } + let version = (major, minor, patch); + + return Some(version); + } +} From 315f175bcd541f0cb367d03840817b3f5fd4e859 Mon Sep 17 00:00:00 2001 From: Charles Strahan Date: Mon, 24 Mar 2025 18:18:24 -0500 Subject: [PATCH 2/8] linux: add is_kernel_driver_attached_to_interface --- src/platform/linux_usbfs/usbfs.rs | 74 +++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/src/platform/linux_usbfs/usbfs.rs b/src/platform/linux_usbfs/usbfs.rs index e0e6ea15..0d2688f1 100644 --- a/src/platform/linux_usbfs/usbfs.rs +++ b/src/platform/linux_usbfs/usbfs.rs @@ -8,13 +8,13 @@ use std::ffi::{c_int, c_uchar, c_uint, c_void}; use linux_raw_sys::ioctl::{ USBDEVFS_CLAIMINTERFACE, USBDEVFS_CLEAR_HALT, USBDEVFS_CONNECT, USBDEVFS_CONTROL, - USBDEVFS_DISCARDURB, USBDEVFS_DISCONNECT, USBDEVFS_DISCONNECT_CLAIM, USBDEVFS_GET_SPEED, - USBDEVFS_IOCTL, USBDEVFS_REAPURBNDELAY, USBDEVFS_RELEASEINTERFACE, USBDEVFS_RESET, - USBDEVFS_SETCONFIGURATION, USBDEVFS_SETINTERFACE, USBDEVFS_SUBMITURB, + USBDEVFS_DISCARDURB, USBDEVFS_DISCONNECT, USBDEVFS_DISCONNECT_CLAIM, USBDEVFS_GETDRIVER, + USBDEVFS_GET_SPEED, USBDEVFS_IOCTL, USBDEVFS_REAPURBNDELAY, USBDEVFS_RELEASEINTERFACE, + USBDEVFS_RESET, USBDEVFS_SETCONFIGURATION, USBDEVFS_SETINTERFACE, USBDEVFS_SUBMITURB, }; use rustix::{ fd::AsFd, - io, + io::{self, Errno}, ioctl::{self, Ioctl, IoctlOutput, Opcode}, }; @@ -65,6 +65,35 @@ pub fn detach_and_claim_interface(fd: Fd, interface: u8) -> io::Result } } +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq)] +struct DriverName([u8; Self::MAX_LEN + 1]); + +impl DriverName { + const MAX_LEN: usize = 255; + + fn new() -> Self { + Self([0; Self::MAX_LEN + 1]) + } + + fn from_string(&mut self, driver: &str) { + let bytes = driver.as_bytes(); + let len = cmp::min(bytes.len(), Self::MAX_LEN); + + self.0[..len].copy_from_slice(&bytes[..len]); + self.0[len..].copy_from_slice(&[0u8; Self::MAX_LEN + 1][len..]); + } + + fn as_str(&self) -> &str { + let len = self.driver_len(); + std::str::from_utf8(self.0[..len].as_ref()).unwrap_or("") + } + + fn driver_len(&self) -> usize { + self.0.iter().position(|&b| b == 0).unwrap_or(0) + } +} + #[repr(C)] struct UsbFsIoctl { interface: c_uint, @@ -97,6 +126,43 @@ pub fn attach_kernel_driver(fd: Fd, interface: u8) -> io::Result<()> { } } +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq)] +struct GetDriver { + interface: u32, + driver: DriverName, +} + +impl GetDriver { + fn new() -> Self { + Self { + interface: 0, + driver: DriverName::new(), + } + } + + fn driver(&self) -> &str { + self.driver.as_str() + } +} + +pub fn is_kernel_driver_attached_to_interface(fd: Fd, interface: u8) -> io::Result { + let mut getdrv = UsbfsGetDriver::new(); + getdrv.interface = interface as u32; + + unsafe { + let ctl = ioctl::Updater::<{ USBDEVFS_GETDRIVER as _ }, UsbfsGetDriver>::new(&mut getdrv); + + match ioctl::ioctl(fd, ctl) { + Err(e) if e == Errno::ENODATA => { + return Ok(false); + } + Err(e) => return Err(e), + Ok(_) => Ok(getdrv.driver() != "usbfs"), + } + } +} + #[repr(C)] struct SetAltSetting { interface: c_int, From 42234ad5a6a45dea01724806ded2e3d1d152b32b Mon Sep 17 00:00:00 2001 From: Charles Strahan Date: Mon, 24 Mar 2025 19:46:56 -0500 Subject: [PATCH 3/8] cleanup --- examples/detach.rs | 10 + src/device.rs | 56 ++- src/platform/macos_iokit/device.rs | 11 +- src/platform/macos_iokit/iokit.rs | 8 +- src/platform/macos_iokit/iokit_c.rs | 648 ---------------------------- src/platform/macos_iokit/mod.rs | 88 ---- 6 files changed, 58 insertions(+), 763 deletions(-) diff --git a/examples/detach.rs b/examples/detach.rs index 250dc5de..8236fa8a 100644 --- a/examples/detach.rs +++ b/examples/detach.rs @@ -1,7 +1,17 @@ //! Detach the kernel driver for an FTDI device and then reattach it. + +#[cfg(not(target_os = "linux"))] +fn main() { + println!("This example is only supported on Linux."); +} + +#[cfg(target_os = "linux")] use std::{thread::sleep, time::Duration}; +#[cfg(target_os = "linux")] use nusb::MaybeFuture; + +#[cfg(target_os = "linux")] fn main() { env_logger::init(); let di = nusb::list_devices() diff --git a/src/device.rs b/src/device.rs index bcc81667..dc0d398c 100644 --- a/src/device.rs +++ b/src/device.rs @@ -84,8 +84,8 @@ impl Device { /// Detach kernel drivers for the specified interface. /// /// ### Platform notes - /// This function can only detach kernel drivers on Linux. Calling on other platforms has - /// no effect. + /// This function can only detach kernel drivers on Linux. + #[cfg(target_os = "linux")] pub fn detach_kernel_driver(&self, interface: u8) -> Result<(), Error> { #[cfg(target_os = "linux")] self.backend.detach_kernel_driver(interface)?; @@ -99,6 +99,8 @@ impl Device { /// ### Platform notes /// This function can only attach kernel drivers on Linux and macOS. Calling on other platforms has /// no effect. + /// * macOS: requires either root or com.apple.vm.device-access entitlement + #[cfg(any(target_os = "linux", target_os = "macos"))] pub fn attach_kernel_driver(&self, interface: u8) -> Result<(), Error> { #[cfg(any(target_os = "linux", target_os = "macos"))] self.backend.attach_kernel_driver(interface)?; @@ -107,6 +109,22 @@ impl Device { Ok(()) } + /// Check if a kernel driver is attached to the specified interface. + /// + /// ### Platform-specific details + /// + /// * Only supported on Linux and macOS + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))] + pub fn is_kernel_driver_attached_to_interface( + &self, + interface_number: u8, + ) -> impl MaybeFuture> { + #[cfg(not(target_os = "windows"))] + self.backend + .clone() + .is_kernel_driver_attached_to_interface(interface_number) + } + /// Get the device descriptor. /// /// This returns cached data and does not perform IO. @@ -158,22 +176,23 @@ impl Device { self.backend.clone().set_configuration(configuration) } - pub fn set_configuration_detached( + /// Set the device configuration, with kernel drivers detached. + /// + /// The argument is the desired configuration's `bConfigurationValue` + /// descriptor field from [`Configuration::configuration_value`] or `0` to + /// unconfigure the device. + /// + /// ### Platform-specific notes + /// * Only supported on macOS + /// * macOS: requires either root or com.apple.vm.device-access entitlement + #[cfg(target_os = "macos")] + pub fn set_configuration_captured( &self, configuration: u8, ) -> impl MaybeFuture> { self.backend .clone() - .set_configuration_detached(configuration) - } - - pub fn is_kernel_driver_attached_to_interface( - &self, - interface_number: u8, - ) -> impl MaybeFuture> { - self.backend - .clone() - .is_kernel_driver_attached_to_interface(interface_number) + .set_configuration_captured(configuration) } /// Request a descriptor from the device. @@ -285,8 +304,17 @@ impl Device { self.backend.clone().reset() } + /// Reset the device, forcing it to re-enumerate, with kernel drivers detached. + /// + /// This `Device` will no longer be usable, and you should drop it and call + /// [`super::list_devices`] to find and re-open it again. + /// + /// ### Platform-specific notes + /// * Only supported on macOS + /// * macOS: requires either root or com.apple.vm.device-access entitlement + #[cfg(target_os = "macos")] pub fn reset_captured(&self) -> impl MaybeFuture> { - self.backend.clone().reset_detached() + self.backend.clone().reset_captured() } /// Synchronously perform a single **IN (device-to-host)** transfer on the default **control** endpoint. diff --git a/src/platform/macos_iokit/device.rs b/src/platform/macos_iokit/device.rs index e7830a51..296a6f4a 100644 --- a/src/platform/macos_iokit/device.rs +++ b/src/platform/macos_iokit/device.rs @@ -10,7 +10,7 @@ use std::{ }; use io_kit_sys::{ - kIOServiceInteractionAllowed, keys::kIOServicePlane, IOObjectRelease, IOObjectRetain, + kIOServiceInteractionAllowed, keys::kIOServicePlane, IOObjectRelease, IORegistryEntryGetChildEntry, }; use log::{debug, error}; @@ -164,7 +164,7 @@ impl MacDevice { }) } - pub(crate) fn set_configuration_detached( + pub(crate) fn set_configuration_captured( self: Arc, configuration: u8, ) -> impl MaybeFuture> { @@ -218,7 +218,7 @@ impl MacDevice { }) } - pub(crate) fn reset_detached(self: Arc) -> impl MaybeFuture> { + pub(crate) fn reset_captured(self: Arc) -> impl MaybeFuture> { Blocking::new(move || { self.require_open_exclusive()?; @@ -543,10 +543,7 @@ impl Drop for MacInterface { pub(crate) mod security { use core_foundation::{ - base::{ - kCFAllocatorDefault, CFAllocatorRef, CFGetTypeID, CFRelease, CFTypeID, CFTypeRef, - TCFType, - }, + base::{kCFAllocatorDefault, CFAllocatorRef, CFGetTypeID, CFRelease, CFTypeRef, TCFType}, boolean::CFBoolean, error::CFErrorRef, number::CFBooleanGetValue, diff --git a/src/platform/macos_iokit/iokit.rs b/src/platform/macos_iokit/iokit.rs index b751dc36..0db93ce6 100644 --- a/src/platform/macos_iokit/iokit.rs +++ b/src/platform/macos_iokit/iokit.rs @@ -102,22 +102,18 @@ impl Drop for PluginInterface { } } -/// Alias that select the "version 500" (IOKit 5.0.0) version of UsbDevice, which means -/// that we support macOS versions back to 10.7.3, which is currently every version that Rust +/// Alias that selects the "version 650" version of UsbDevice, and "version 700" of UsbInterface, +/// which means that we support macOS versions back to 10.9, which is currently every version that Rust /// supports. Use this instead of touching the iokit_c structure; this may be bumped to /// (compatible) newer versions of the struct as Rust's support changes. pub(crate) type UsbDevice = iokit_c::IOUSBDeviceStruct650; pub(crate) type UsbInterface = iokit_c::IOUSBInterfaceStruct700; -// pub(crate) type UsbDevice = iokit_c::IOUSBDeviceStruct500; -// pub(crate) type UsbInterface = iokit_c::IOUSBInterfaceStruct500; pub(crate) fn usb_device_type_id() -> CFUUIDBytes { - // unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBDeviceInterfaceID500()) } unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBDeviceInterfaceID650()) } } pub(crate) fn usb_interface_type_id() -> CFUUIDBytes { - // unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBInterfaceInterfaceID500()) } unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBInterfaceInterfaceID700()) } } diff --git a/src/platform/macos_iokit/iokit_c.rs b/src/platform/macos_iokit/iokit_c.rs index c69d99e6..b26204f4 100644 --- a/src/platform/macos_iokit/iokit_c.rs +++ b/src/platform/macos_iokit/iokit_c.rs @@ -430,240 +430,6 @@ pub struct IOUSBConfigurationDescriptor { } pub type IOUSBConfigurationDescriptorPtr = *mut IOUSBConfigurationDescriptor; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct IOUSBDeviceStruct500 { - pub _reserved: *mut ::std::os::raw::c_void, - pub QueryInterface: ::std::option::Option< - unsafe extern "C" fn( - thisPointer: *mut ::std::os::raw::c_void, - iid: REFIID, - ppv: *mut LPVOID, - ) -> HRESULT, - >, - pub AddRef: ::std::option::Option< - unsafe extern "C" fn(thisPointer: *mut ::std::os::raw::c_void) -> ULONG, - >, - pub Release: ::std::option::Option< - unsafe extern "C" fn(thisPointer: *mut ::std::os::raw::c_void) -> ULONG, - >, - pub CreateDeviceAsyncEventSource: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - source: *mut CFRunLoopSourceRef, - ) -> IOReturn, - >, - pub GetDeviceAsyncEventSource: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void) -> CFRunLoopSourceRef, - >, - pub CreateDeviceAsyncPort: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - port: *mut mach_port_t, - ) -> IOReturn, - >, - pub GetDeviceAsyncPort: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void) -> mach_port_t, - >, - pub USBDeviceOpen: - ::std::option::Option IOReturn>, - pub USBDeviceClose: - ::std::option::Option IOReturn>, - pub GetDeviceClass: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, devClass: *mut UInt8) -> IOReturn, - >, - pub GetDeviceSubClass: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - devSubClass: *mut UInt8, - ) -> IOReturn, - >, - pub GetDeviceProtocol: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - devProtocol: *mut UInt8, - ) -> IOReturn, - >, - pub GetDeviceVendor: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - devVendor: *mut UInt16, - ) -> IOReturn, - >, - pub GetDeviceProduct: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - devProduct: *mut UInt16, - ) -> IOReturn, - >, - pub GetDeviceReleaseNumber: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - devRelNum: *mut UInt16, - ) -> IOReturn, - >, - pub GetDeviceAddress: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - addr: *mut USBDeviceAddress, - ) -> IOReturn, - >, - pub GetDeviceBusPowerAvailable: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - powerAvailable: *mut UInt32, - ) -> IOReturn, - >, - pub GetDeviceSpeed: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, devSpeed: *mut UInt8) -> IOReturn, - >, - pub GetNumberOfConfigurations: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, numConfig: *mut UInt8) -> IOReturn, - >, - pub GetLocationID: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - locationID: *mut UInt32, - ) -> IOReturn, - >, - pub GetConfigurationDescriptorPtr: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - configIndex: UInt8, - desc: *mut IOUSBConfigurationDescriptorPtr, - ) -> IOReturn, - >, - pub GetConfiguration: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, configNum: *mut UInt8) -> IOReturn, - >, - pub SetConfiguration: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, configNum: UInt8) -> IOReturn, - >, - pub GetBusFrameNumber: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - frame: *mut UInt64, - atTime: *mut AbsoluteTime, - ) -> IOReturn, - >, - pub ResetDevice: - ::std::option::Option IOReturn>, - pub DeviceRequest: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - req: *mut IOUSBDevRequest, - ) -> IOReturn, - >, - pub DeviceRequestAsync: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - req: *mut IOUSBDevRequest, - callback: IOAsyncCallback1, - refCon: *mut ::std::os::raw::c_void, - ) -> IOReturn, - >, - pub CreateInterfaceIterator: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - req: *mut IOUSBFindInterfaceRequest, - iter: *mut io_iterator_t, - ) -> IOReturn, - >, - pub USBDeviceOpenSeize: - ::std::option::Option IOReturn>, - pub DeviceRequestTO: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - req: *mut IOUSBDevRequestTO, - ) -> IOReturn, - >, - pub DeviceRequestAsyncTO: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - req: *mut IOUSBDevRequestTO, - callback: IOAsyncCallback1, - refCon: *mut ::std::os::raw::c_void, - ) -> IOReturn, - >, - pub USBDeviceSuspend: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, suspend: Boolean) -> IOReturn, - >, - pub USBDeviceAbortPipeZero: - ::std::option::Option IOReturn>, - pub USBGetManufacturerStringIndex: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, msi: *mut UInt8) -> IOReturn, - >, - pub USBGetProductStringIndex: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, psi: *mut UInt8) -> IOReturn, - >, - pub USBGetSerialNumberStringIndex: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, snsi: *mut UInt8) -> IOReturn, - >, - pub USBDeviceReEnumerate: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, options: UInt32) -> IOReturn, - >, - pub GetBusMicroFrameNumber: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - microFrame: *mut UInt64, - atTime: *mut AbsoluteTime, - ) -> IOReturn, - >, - pub GetIOUSBLibVersion: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - ioUSBLibVersion: *mut NumVersion, - usbFamilyVersion: *mut NumVersion, - ) -> IOReturn, - >, - pub GetBusFrameNumberWithTime: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - frame: *mut UInt64, - atTime: *mut AbsoluteTime, - ) -> IOReturn, - >, - pub GetUSBDeviceInformation: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, info: *mut UInt32) -> IOReturn, - >, - pub RequestExtraPower: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - type_: UInt32, - requestedPower: UInt32, - powerAvailable: *mut UInt32, - ) -> IOReturn, - >, - pub ReturnExtraPower: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - type_: UInt32, - powerReturned: UInt32, - ) -> IOReturn, - >, - pub GetExtraPowerAllocated: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - type_: UInt32, - powerAllocated: *mut UInt32, - ) -> IOReturn, - >, - pub GetBandwidthAvailableForDevice: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - bandwidth: *mut UInt32, - ) -> IOReturn, - >, -} -pub type IOUSBDeviceInterface500 = IOUSBDeviceStruct500; - -// Tweak: these are just function pointers to thread-safe functions, -// so add send and sync to the C-type. (Calling these from multiple threads -// may cause odd behavior on the USB bus, though, so we'll still want to wrap the -// device in Mutex somewhere up from here.) -unsafe impl Send for IOUSBDeviceInterface500 {} -unsafe impl Sync for IOUSBDeviceInterface500 {} - #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct IOUSBDeviceStruct650 { @@ -927,420 +693,6 @@ pub type IOUSBDeviceInterface650 = IOUSBDeviceStruct650; unsafe impl Send for IOUSBDeviceInterface650 {} unsafe impl Sync for IOUSBDeviceInterface650 {} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct IOUSBInterfaceStruct500 { - pub _reserved: *mut ::std::os::raw::c_void, - pub QueryInterface: ::std::option::Option< - unsafe extern "C" fn( - thisPointer: *mut ::std::os::raw::c_void, - iid: REFIID, - ppv: *mut LPVOID, - ) -> HRESULT, - >, - pub AddRef: ::std::option::Option< - unsafe extern "C" fn(thisPointer: *mut ::std::os::raw::c_void) -> ULONG, - >, - pub Release: ::std::option::Option< - unsafe extern "C" fn(thisPointer: *mut ::std::os::raw::c_void) -> ULONG, - >, - pub CreateInterfaceAsyncEventSource: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - source: *mut CFRunLoopSourceRef, - ) -> IOReturn, - >, - pub GetInterfaceAsyncEventSource: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void) -> CFRunLoopSourceRef, - >, - pub CreateInterfaceAsyncPort: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - port: *mut mach_port_t, - ) -> IOReturn, - >, - pub GetInterfaceAsyncPort: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void) -> mach_port_t, - >, - pub USBInterfaceOpen: - ::std::option::Option IOReturn>, - pub USBInterfaceClose: - ::std::option::Option IOReturn>, - pub GetInterfaceClass: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, intfClass: *mut UInt8) -> IOReturn, - >, - pub GetInterfaceSubClass: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - intfSubClass: *mut UInt8, - ) -> IOReturn, - >, - pub GetInterfaceProtocol: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - intfProtocol: *mut UInt8, - ) -> IOReturn, - >, - pub GetDeviceVendor: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - devVendor: *mut UInt16, - ) -> IOReturn, - >, - pub GetDeviceProduct: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - devProduct: *mut UInt16, - ) -> IOReturn, - >, - pub GetDeviceReleaseNumber: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - devRelNum: *mut UInt16, - ) -> IOReturn, - >, - pub GetConfigurationValue: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, configVal: *mut UInt8) -> IOReturn, - >, - pub GetInterfaceNumber: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - intfNumber: *mut UInt8, - ) -> IOReturn, - >, - pub GetAlternateSetting: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - intfAltSetting: *mut UInt8, - ) -> IOReturn, - >, - pub GetNumEndpoints: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - intfNumEndpoints: *mut UInt8, - ) -> IOReturn, - >, - pub GetLocationID: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - locationID: *mut UInt32, - ) -> IOReturn, - >, - pub GetDevice: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - device: *mut io_service_t, - ) -> IOReturn, - >, - pub SetAlternateInterface: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - alternateSetting: UInt8, - ) -> IOReturn, - >, - pub GetBusFrameNumber: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - frame: *mut UInt64, - atTime: *mut AbsoluteTime, - ) -> IOReturn, - >, - pub ControlRequest: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - req: *mut IOUSBDevRequest, - ) -> IOReturn, - >, - pub ControlRequestAsync: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - req: *mut IOUSBDevRequest, - callback: IOAsyncCallback1, - refCon: *mut ::std::os::raw::c_void, - ) -> IOReturn, - >, - pub GetPipeProperties: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - direction: *mut UInt8, - number: *mut UInt8, - transferType: *mut UInt8, - maxPacketSize: *mut UInt16, - interval: *mut UInt8, - ) -> IOReturn, - >, - pub GetPipeStatus: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, - >, - pub AbortPipe: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, - >, - pub ResetPipe: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, - >, - pub ClearPipeStall: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, - >, - pub ReadPipe: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: *mut UInt32, - ) -> IOReturn, - >, - pub WritePipe: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: UInt32, - ) -> IOReturn, - >, - pub ReadPipeAsync: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: UInt32, - callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, - ) -> IOReturn, - >, - pub WritePipeAsync: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: UInt32, - callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, - ) -> IOReturn, - >, - pub ReadIsochPipeAsync: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - frameStart: UInt64, - numFrames: UInt32, - frameList: *mut IOUSBIsocFrame, - callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, - ) -> IOReturn, - >, - pub WriteIsochPipeAsync: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - frameStart: UInt64, - numFrames: UInt32, - frameList: *mut IOUSBIsocFrame, - callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, - ) -> IOReturn, - >, - pub ControlRequestTO: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - req: *mut IOUSBDevRequestTO, - ) -> IOReturn, - >, - pub ControlRequestAsyncTO: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - req: *mut IOUSBDevRequestTO, - callback: IOAsyncCallback1, - refCon: *mut ::std::os::raw::c_void, - ) -> IOReturn, - >, - pub ReadPipeTO: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: *mut UInt32, - noDataTimeout: UInt32, - completionTimeout: UInt32, - ) -> IOReturn, - >, - pub WritePipeTO: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: UInt32, - noDataTimeout: UInt32, - completionTimeout: UInt32, - ) -> IOReturn, - >, - pub ReadPipeAsyncTO: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: UInt32, - noDataTimeout: UInt32, - completionTimeout: UInt32, - callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, - ) -> IOReturn, - >, - pub WritePipeAsyncTO: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - size: UInt32, - noDataTimeout: UInt32, - completionTimeout: UInt32, - callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, - ) -> IOReturn, - >, - pub USBInterfaceGetStringIndex: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, si: *mut UInt8) -> IOReturn, - >, - pub USBInterfaceOpenSeize: - ::std::option::Option IOReturn>, - pub ClearPipeStallBothEnds: ::std::option::Option< - unsafe extern "C" fn(self_: *mut ::std::os::raw::c_void, pipeRef: UInt8) -> IOReturn, - >, - pub SetPipePolicy: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - maxPacketSize: UInt16, - maxInterval: UInt8, - ) -> IOReturn, - >, - pub GetBandwidthAvailable: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - bandwidth: *mut UInt32, - ) -> IOReturn, - >, - pub GetEndpointProperties: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - alternateSetting: UInt8, - endpointNumber: UInt8, - direction: UInt8, - transferType: *mut UInt8, - maxPacketSize: *mut UInt16, - interval: *mut UInt8, - ) -> IOReturn, - >, - pub LowLatencyReadIsochPipeAsync: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - frameStart: UInt64, - numFrames: UInt32, - updateFrequency: UInt32, - frameList: *mut IOUSBLowLatencyIsocFrame, - callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, - ) -> IOReturn, - >, - pub LowLatencyWriteIsochPipeAsync: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - buf: *mut ::std::os::raw::c_void, - frameStart: UInt64, - numFrames: UInt32, - updateFrequency: UInt32, - frameList: *mut IOUSBLowLatencyIsocFrame, - callback: IOAsyncCallback1, - refcon: *mut ::std::os::raw::c_void, - ) -> IOReturn, - >, - pub LowLatencyCreateBuffer: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - buffer: *mut *mut ::std::os::raw::c_void, - size: IOByteCount, - bufferType: UInt32, - ) -> IOReturn, - >, - pub LowLatencyDestroyBuffer: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - buffer: *mut ::std::os::raw::c_void, - ) -> IOReturn, - >, - pub GetBusMicroFrameNumber: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - microFrame: *mut UInt64, - atTime: *mut AbsoluteTime, - ) -> IOReturn, - >, - pub GetFrameListTime: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - microsecondsInFrame: *mut UInt32, - ) -> IOReturn, - >, - pub GetIOUSBLibVersion: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - ioUSBLibVersion: *mut NumVersion, - usbFamilyVersion: *mut NumVersion, - ) -> IOReturn, - >, - pub FindNextAssociatedDescriptor: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - currentDescriptor: *const ::std::os::raw::c_void, - descriptorType: UInt8, - ) -> *mut IOUSBDescriptorHeader, - >, - pub FindNextAltInterface: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - current: *const ::std::os::raw::c_void, - request: *mut IOUSBFindInterfaceRequest, - ) -> *mut IOUSBDescriptorHeader, - >, - pub GetBusFrameNumberWithTime: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - frame: *mut UInt64, - atTime: *mut AbsoluteTime, - ) -> IOReturn, - >, - pub GetPipePropertiesV2: ::std::option::Option< - unsafe extern "C" fn( - self_: *mut ::std::os::raw::c_void, - pipeRef: UInt8, - direction: *mut UInt8, - number: *mut UInt8, - transferType: *mut UInt8, - maxPacketSize: *mut UInt16, - interval: *mut UInt8, - maxBurst: *mut UInt8, - mult: *mut UInt8, - bytesPerInterval: *mut UInt16, - ) -> IOReturn, - >, -} - -// Tweak: these are just function pointers to thread-safe functions, -// so add send and sync to the C-type. (Calling these from multiple threads -// may cause odd behavior on the USB bus, though, so we'll still want to wrap the -// device in Mutex somewhere up from here.) -unsafe impl Send for IOUSBInterfaceStruct500 {} -unsafe impl Sync for IOUSBInterfaceStruct500 {} - #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct IOUSBInterfaceStruct700 { diff --git a/src/platform/macos_iokit/mod.rs b/src/platform/macos_iokit/mod.rs index 5645fdcb..317f8be9 100644 --- a/src/platform/macos_iokit/mod.rs +++ b/src/platform/macos_iokit/mod.rs @@ -1,6 +1,5 @@ mod transfer; use io_kit_sys::ret::IOReturn; -use once_cell::sync::OnceCell; pub(crate) use transfer::TransferData; mod enumeration; @@ -34,90 +33,3 @@ fn status_to_transfer_result(status: IOReturn) -> Result<(), TransferError> { _ => Err(TransferError::Unknown), } } - -type OsVersion = (u8, u8, u8); - -pub(crate) fn os_version() -> OsVersion { - static VERSION: OnceCell = OnceCell::new(); - *VERSION.get_or_init(|| { - read_osproductversion() - .or_else(|| read_osrelease()) - .unwrap_or((10, 0, 0)) - }) -} - -fn read_osproductversion() -> Option { - unsafe { - let mut buffer: [libc::c_char; 64] = [0; 64]; - let mut len: libc::size_t = buffer.len() - 1; - - let ret = libc::sysctlbyname( - "kern.osproductversion\0".as_ptr() as *const libc::c_char, - buffer.as_mut_ptr() as *mut _, - &mut len, - std::ptr::null_mut(), - 0, - ); - - if ret != 0 { - return None; - } - - let os_product_version_string = std::ffi::CStr::from_ptr(buffer.as_ptr()).to_str().ok()?; - let mut parts = os_product_version_string.split("."); - let major = parts.next().and_then(|s| s.parse().ok())?; - let minor = parts.next().and_then(|s| s.parse().ok())?; - let patch = parts.next().and_then(|s| s.parse().ok())?; - let version = (major, minor, patch); - - return Some(version); - } -} - -fn read_osrelease() -> Option { - unsafe { - let mut buffer: [libc::c_char; 64] = [0; 64]; - let mut len: libc::size_t = buffer.len() - 1; - - let ret = libc::sysctlbyname( - "kern.osrelease\0".as_ptr() as *const libc::c_char, - buffer.as_mut_ptr() as *mut _, - &mut len, - std::ptr::null_mut(), - 0, - ); - - if ret != 0 { - return None; - } - - let os_release_string = std::ffi::CStr::from_ptr(buffer.as_ptr()).to_str().ok()?; - let mut parts = os_release_string.split("."); - let darwin_major: u8 = parts.next().and_then(|s| s.parse().ok())?; - let darwin_minor: u8 = parts.next().and_then(|s| s.parse().ok())?; - - let major; - let minor; - let patch; - if darwin_major == 1 && darwin_minor < 4 { - major = 10; - minor = 0; - patch = 0; - } else if darwin_major < 6 { - major = 10; - minor = 1; - patch = 0; - } else if darwin_major < 20 { - major = 10; - minor = darwin_major - 4; - patch = darwin_minor; - } else { - major = darwin_major - 9; - minor = darwin_minor; - patch = 0; - } - let version = (major, minor, patch); - - return Some(version); - } -} From b4f84698dc3bdaa575bbae9f105547e7f415f3e9 Mon Sep 17 00:00:00 2001 From: Charles Strahan Date: Mon, 24 Mar 2025 20:02:06 -0500 Subject: [PATCH 4/8] factor out authorization logic --- src/platform/macos_iokit/device.rs | 107 ++++++++++++----------------- 1 file changed, 44 insertions(+), 63 deletions(-) diff --git a/src/platform/macos_iokit/device.rs b/src/platform/macos_iokit/device.rs index 296a6f4a..a67baf09 100644 --- a/src/platform/macos_iokit/device.rs +++ b/src/platform/macos_iokit/device.rs @@ -171,38 +171,16 @@ impl MacDevice { Blocking::new(move || { self.require_open_exclusive()?; - let device; - let device_raw; - if security::have_capture_entitlement() { - // Request authorization - check_iokit_return(ioservice_authorize( - &self.service, - kIOServiceInteractionAllowed, - ))?; - - // Need to re-open the device for authorization to take effect - device = IoKitDevice::new(&self.service)?; - device_raw = device.raw; - } else { - if unsafe { libc::geteuid() != 0 } { - return Err(Error::new( - ErrorKind::PermissionDenied, - "Operation not permitted (requires root or com.apple.vm.device-access entitlement)", - )); - } - - device_raw = self.device.raw; - } - - unsafe { + self.with_capture_authorization(|device| unsafe { check_iokit_return(call_iokit_function!( - device_raw, + device.raw, SetConfigurationV2(configuration, false, true) - ))? - } - log::debug!("Set configuration {configuration}"); - self.active_config.store(configuration, Ordering::SeqCst); - Ok(()) + ))?; + + log::debug!("Set configuration {configuration}"); + self.active_config.store(configuration, Ordering::SeqCst); + Ok(()) + }) }) } @@ -222,35 +200,12 @@ impl MacDevice { Blocking::new(move || { self.require_open_exclusive()?; - let device; - let device_raw; - if security::have_capture_entitlement() { - // Request authorization - check_iokit_return(ioservice_authorize( - &self.service, - kIOServiceInteractionAllowed, - ))?; - - // Need to re-open the device for authorization to take effect - device = IoKitDevice::new(&self.service)?; - device_raw = device.raw; - } else { - if unsafe { libc::geteuid() != 0 } { - return Err(Error::new( - ErrorKind::PermissionDenied, - "Operation not permitted (requires root or com.apple.vm.device-access entitlement)", - )); - } - - device_raw = self.device.raw; - } - - unsafe { + self.with_capture_authorization(|device| unsafe { check_iokit_return(call_iokit_function!( - device_raw, + device.raw, USBDeviceReEnumerate(kUSBReEnumerateCaptureDeviceMask) )) - } + }) }) } @@ -349,19 +304,45 @@ impl MacDevice { }) } + fn with_capture_authorization(&self, f: F) -> Result + where + F: FnOnce(&IoKitDevice) -> Result, + { + if security::have_capture_entitlement() { + // Request authorization + check_iokit_return(ioservice_authorize( + &self.service, + kIOServiceInteractionAllowed, + ))?; + + // Need to re-open the device for authorization to take effect + let device = IoKitDevice::new(&self.service)?; + f(&device) + } else { + if unsafe { libc::geteuid() != 0 } { + return Err(Error::new( + ErrorKind::PermissionDenied, + "Operation not permitted (requires root or com.apple.vm.device-access entitlement)", + )); + } + f(&self.device) + } + } + pub(crate) fn attach_kernel_driver( self: &Arc, interface_number: u8, ) -> Result<(), Error> { - let intf_service = self - .device - .create_interface_iterator()? - .nth(interface_number as usize) - .ok_or(Error::new(ErrorKind::NotFound, "interface not found"))?; + self.with_capture_authorization(|device| unsafe { + let intf_service = device + .create_interface_iterator()? + .nth(interface_number as usize) + .ok_or(Error::new(ErrorKind::NotFound, "interface not found"))?; - let interface = IoKitInterface::new(intf_service)?; + let interface = IoKitInterface::new(intf_service)?; - unsafe { check_iokit_return(call_iokit_function!(interface.raw, RegisterDriver())) } + check_iokit_return(call_iokit_function!(interface.raw, RegisterDriver())) + }) } pub(crate) fn claim_interface( From 85c5d7f8c92ae0c682b252dc985322a430936301 Mon Sep 17 00:00:00 2001 From: Charles Strahan Date: Mon, 24 Mar 2025 20:04:30 -0500 Subject: [PATCH 5/8] linux fixes --- src/platform/linux_usbfs/usbfs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/linux_usbfs/usbfs.rs b/src/platform/linux_usbfs/usbfs.rs index 0d2688f1..8b9ae4e0 100644 --- a/src/platform/linux_usbfs/usbfs.rs +++ b/src/platform/linux_usbfs/usbfs.rs @@ -147,14 +147,14 @@ impl GetDriver { } pub fn is_kernel_driver_attached_to_interface(fd: Fd, interface: u8) -> io::Result { - let mut getdrv = UsbfsGetDriver::new(); + let mut getdrv = GetDriver::new(); getdrv.interface = interface as u32; unsafe { let ctl = ioctl::Updater::<{ USBDEVFS_GETDRIVER as _ }, UsbfsGetDriver>::new(&mut getdrv); match ioctl::ioctl(fd, ctl) { - Err(e) if e == Errno::ENODATA => { + Err(e) if e == Errno::NODATA => { return Ok(false); } Err(e) => return Err(e), From b7dda2e0217699250f21c7571e082090f1400e3c Mon Sep 17 00:00:00 2001 From: Charles Strahan Date: Mon, 24 Mar 2025 20:08:03 -0500 Subject: [PATCH 6/8] linux fixes --- src/platform/linux_usbfs/device.rs | 9 +++++++++ src/platform/linux_usbfs/usbfs.rs | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index e55d2fc4..a96e13ee 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -342,6 +342,15 @@ impl LinuxDevice { usbfs::attach_kernel_driver(&self.fd, interface_number).map_err(|e| e.into()) } + #[cfg(target_os = "linux")] + pub(crate) fn is_kernel_driver_attached_to_interface( + self: &Arc, + interface_number: u8, + ) -> Result<(), Error> { + usbfs::is_kernel_driver_attached_to_interface(&self.fd, interface_number) + .map_err(|e| e.into()) + } + pub(crate) unsafe fn submit_urb(&self, urb: *mut Urb) { let ep = unsafe { (*urb).endpoint }; if let Err(e) = usbfs::submit_urb(&self.fd, urb) { diff --git a/src/platform/linux_usbfs/usbfs.rs b/src/platform/linux_usbfs/usbfs.rs index 8b9ae4e0..b1304449 100644 --- a/src/platform/linux_usbfs/usbfs.rs +++ b/src/platform/linux_usbfs/usbfs.rs @@ -78,7 +78,7 @@ impl DriverName { fn from_string(&mut self, driver: &str) { let bytes = driver.as_bytes(); - let len = cmp::min(bytes.len(), Self::MAX_LEN); + let len = std::cmp::min(bytes.len(), Self::MAX_LEN); self.0[..len].copy_from_slice(&bytes[..len]); self.0[len..].copy_from_slice(&[0u8; Self::MAX_LEN + 1][len..]); @@ -151,7 +151,7 @@ pub fn is_kernel_driver_attached_to_interface(fd: Fd, interface: u8) - getdrv.interface = interface as u32; unsafe { - let ctl = ioctl::Updater::<{ USBDEVFS_GETDRIVER as _ }, UsbfsGetDriver>::new(&mut getdrv); + let ctl = ioctl::Updater::<{ USBDEVFS_GETDRIVER as _ }, GetDriver>::new(&mut getdrv); match ioctl::ioctl(fd, ctl) { Err(e) if e == Errno::NODATA => { From 5df219251154c5d1326be6974aa3b48e46565d35 Mon Sep 17 00:00:00 2001 From: Charles Strahan Date: Mon, 24 Mar 2025 20:11:05 -0500 Subject: [PATCH 7/8] linux fixes --- src/device.rs | 2 +- src/platform/linux_usbfs/device.rs | 2 +- src/platform/macos_iokit/device.rs | 42 ++++++++++++++---------------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/device.rs b/src/device.rs index dc0d398c..a0c61c37 100644 --- a/src/device.rs +++ b/src/device.rs @@ -118,7 +118,7 @@ impl Device { pub fn is_kernel_driver_attached_to_interface( &self, interface_number: u8, - ) -> impl MaybeFuture> { + ) -> Result { #[cfg(not(target_os = "windows"))] self.backend .clone() diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index a96e13ee..6ba1ecef 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -346,7 +346,7 @@ impl LinuxDevice { pub(crate) fn is_kernel_driver_attached_to_interface( self: &Arc, interface_number: u8, - ) -> Result<(), Error> { + ) -> Result { usbfs::is_kernel_driver_attached_to_interface(&self.fd, interface_number) .map_err(|e| e.into()) } diff --git a/src/platform/macos_iokit/device.rs b/src/platform/macos_iokit/device.rs index a67baf09..cdd81374 100644 --- a/src/platform/macos_iokit/device.rs +++ b/src/platform/macos_iokit/device.rs @@ -277,31 +277,29 @@ impl MacDevice { pub(crate) fn is_kernel_driver_attached_to_interface( self: Arc, interface_number: u8, - ) -> impl MaybeFuture> { - Blocking::new(move || { - let intf_service = self - .device - .create_interface_iterator()? - .nth(interface_number as usize) - .ok_or(Error::new(ErrorKind::NotFound, "interface not found"))?; + ) -> Result { + let intf_service = self + .device + .create_interface_iterator()? + .nth(interface_number as usize) + .ok_or(Error::new(ErrorKind::NotFound, "interface not found"))?; - let mut child = 0; + let mut child = 0; - unsafe { - IORegistryEntryGetChildEntry( - intf_service.get(), - kIOServicePlane as *mut i8, - &mut child, - ); - - if child != 0 { - IOObjectRelease(child); - Ok(true) - } else { - Ok(false) - } + unsafe { + IORegistryEntryGetChildEntry( + intf_service.get(), + kIOServicePlane as *mut i8, + &mut child, + ); + + if child != 0 { + IOObjectRelease(child); + Ok(true) + } else { + Ok(false) } - }) + } } fn with_capture_authorization(&self, f: F) -> Result From 8e234a53dc41e1f6ed782dbf8ee0b2bc67339180 Mon Sep 17 00:00:00 2001 From: Charles Strahan Date: Mon, 24 Mar 2025 20:13:18 -0500 Subject: [PATCH 8/8] linux fixes --- src/device.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/device.rs b/src/device.rs index a0c61c37..2f019793 100644 --- a/src/device.rs +++ b/src/device.rs @@ -114,7 +114,7 @@ impl Device { /// ### Platform-specific details /// /// * Only supported on Linux and macOS - #[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "macos"))] pub fn is_kernel_driver_attached_to_interface( &self, interface_number: u8,