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/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 9eff6ad8..2f019793 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)?; @@ -97,16 +97,34 @@ 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. + /// * 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(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "macos"))] self.backend.attach_kernel_driver(interface)?; let _ = interface; 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"))] + pub fn is_kernel_driver_attached_to_interface( + &self, + interface_number: u8, + ) -> Result { + #[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,6 +176,25 @@ impl Device { self.backend.clone().set_configuration(configuration) } + /// 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_captured(configuration) + } + /// Request a descriptor from the device. /// /// The `language_id` should be `0` unless you are requesting a string descriptor. @@ -267,6 +304,19 @@ 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_captured() + } + /// Synchronously perform a single **IN (device-to-host)** transfer on the default **control** endpoint. /// /// ### Platform-specific notes diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index e55d2fc4..6ba1ecef 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 { + 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 e0e6ea15..b1304449 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 = 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..]); + } + + 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 = GetDriver::new(); + getdrv.interface = interface as u32; + + unsafe { + let ctl = ioctl::Updater::<{ USBDEVFS_GETDRIVER as _ }, GetDriver>::new(&mut getdrv); + + match ioctl::ioctl(fd, ctl) { + Err(e) if e == Errno::NODATA => { + return Ok(false); + } + Err(e) => return Err(e), + Ok(_) => Ok(getdrv.driver() != "usbfs"), + } + } +} + #[repr(C)] struct SetAltSetting { interface: c_int, diff --git a/src/platform/macos_iokit/device.rs b/src/platform/macos_iokit/device.rs index 0591bf45..cdd81374 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, + 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,26 @@ impl MacDevice { }) } + pub(crate) fn set_configuration_captured( + self: Arc, + configuration: u8, + ) -> impl MaybeFuture> { + Blocking::new(move || { + self.require_open_exclusive()?; + + self.with_capture_authorization(|device| 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 +196,19 @@ impl MacDevice { }) } + pub(crate) fn reset_captured(self: Arc) -> impl MaybeFuture> { + Blocking::new(move || { + self.require_open_exclusive()?; + + self.with_capture_authorization(|device| 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 +274,75 @@ impl MacDevice { TransferHandle::new(super::TransferData::new_control(self.clone())) } + pub(crate) fn is_kernel_driver_attached_to_interface( + self: Arc, + interface_number: u8, + ) -> 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; + + 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 + 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> { + 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)?; + + check_iokit_return(call_iokit_function!(interface.raw, RegisterDriver())) + }) + } + pub(crate) fn claim_interface( self: Arc, interface_number: u8, @@ -409,3 +519,61 @@ impl Drop for MacInterface { .fetch_sub(1, Ordering::Release); } } + +pub(crate) mod security { + use core_foundation::{ + base::{kCFAllocatorDefault, CFAllocatorRef, CFGetTypeID, CFRelease, 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..0db93ce6 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; @@ -102,19 +102,23 @@ 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::IOUSBDeviceStruct500; -pub(crate) type UsbInterface = iokit_c::IOUSBInterfaceStruct500; +pub(crate) type UsbDevice = iokit_c::IOUSBDeviceStruct650; +pub(crate) type UsbInterface = iokit_c::IOUSBInterfaceStruct700; 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()) } +} + +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 +131,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..b26204f4 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 { @@ -363,7 +432,7 @@ pub type IOUSBConfigurationDescriptorPtr = *mut IOUSBConfigurationDescriptor; #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct IOUSBDeviceStruct500 { +pub struct IOUSBDeviceStruct650 { pub _reserved: *mut ::std::os::raw::c_void, pub QueryInterface: ::std::option::Option< unsafe extern "C" fn( @@ -585,19 +654,48 @@ pub struct IOUSBDeviceStruct500 { bandwidth: *mut UInt32, ) -> IOReturn, >, + pub SetConfigurationV2: ::std::option::Option< + unsafe extern "C" fn( + self_: *mut ::std::os::raw::c_void, + configNum: UInt8, + startInterfaceMatching: bool, + issueRemoteWakeup: bool, + ) -> 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 type IOUSBDeviceInterface500 = IOUSBDeviceStruct500; +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 IOUSBDeviceInterface500 {} -unsafe impl Sync for IOUSBDeviceInterface500 {} +unsafe impl Send for IOUSBDeviceInterface650 {} +unsafe impl Sync for IOUSBDeviceInterface650 {} #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct IOUSBInterfaceStruct500 { +pub struct IOUSBInterfaceStruct700 { pub _reserved: *mut ::std::os::raw::c_void, pub QueryInterface: ::std::option::Option< unsafe extern "C" fn( @@ -1000,11 +1098,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 {}