nusb/src/platform/macos_iokit/iokit.rs
2025-05-24 14:56:33 -06:00

131 lines
3.8 KiB
Rust

//! Utilities for using IOKit APIs.
//!
//! Based on Kate Temkin's [usrs](https://github.com/ktemkin/usrs)
//! licensed under MIT OR Apache-2.0.
use core_foundation_sys::uuid::CFUUIDBytes;
use io_kit_sys::{ret::IOReturn, IOIteratorNext, IOObjectRelease};
use std::io::ErrorKind;
use crate::Error;
use super::iokit_c::{self, CFUUIDGetUUIDBytes, IOCFPlugInInterface};
pub(crate) struct IoObject(u32);
impl IoObject {
// Safety: `handle` must be an IOObject handle. Ownership is transferred.
pub unsafe fn new(handle: u32) -> IoObject {
IoObject(handle)
}
pub fn get(&self) -> u32 {
self.0
}
}
impl Drop for IoObject {
fn drop(&mut self) {
unsafe {
IOObjectRelease(self.0);
}
}
}
pub(crate) struct IoService(IoObject);
impl IoService {
// Safety: `handle` must be an IOService handle. Ownership is transferred.
pub unsafe fn new(handle: u32) -> IoService {
IoService(IoObject(handle))
}
pub fn get(&self) -> u32 {
self.0 .0
}
}
pub(crate) struct IoServiceIterator(IoObject);
impl IoServiceIterator {
// Safety: `handle` must be an IoIterator of IoService. Ownership is transferred.
pub unsafe fn new(handle: u32) -> IoServiceIterator {
IoServiceIterator(IoObject::new(handle))
}
}
impl Iterator for IoServiceIterator {
type Item = IoService;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let handle = IOIteratorNext(self.0.get());
if handle != 0 {
Some(IoService::new(handle))
} else {
None
}
}
}
}
/// Helper for calling IOKit function pointers.
macro_rules! call_iokit_function {
($ptr:expr, $function:ident($($args:expr),*)) => {{
use std::ffi::c_void;
let func = (**$ptr).$function.expect("function pointer from IOKit was null");
func($ptr as *mut c_void, $($args),*)
}};
}
pub(crate) use call_iokit_function;
/// Wrapper around a **IOCFPluginInterface that automatically releases it.
#[derive(Debug)]
pub(crate) struct PluginInterface {
interface: *mut *mut IOCFPlugInInterface,
}
impl PluginInterface {
pub(crate) fn new(interface: *mut *mut IOCFPlugInInterface) -> Self {
Self { interface }
}
/// Fetches the inner pointer for passing to IOKit functions.
pub(crate) fn get(&self) -> *mut *mut IOCFPlugInInterface {
self.interface
}
}
impl Drop for PluginInterface {
fn drop(&mut self) {
unsafe {
call_iokit_function!(self.interface, Release());
}
}
}
/// 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
/// 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) fn usb_device_type_id() -> CFUUIDBytes {
unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBDeviceInterfaceID500()) }
}
pub(crate) fn usb_interface_type_id() -> CFUUIDBytes {
unsafe { CFUUIDGetUUIDBytes(iokit_c::kIOUSBInterfaceInterfaceID500()) }
}
pub(crate) fn check_iokit_return(r: IOReturn) -> Result<(), Error> {
#[allow(non_upper_case_globals)]
#[deny(unreachable_patterns)]
match r {
io_kit_sys::ret::kIOReturnSuccess => Ok(()),
io_kit_sys::ret::kIOReturnExclusiveAccess => {
Err(Error::other("could not be opened for exclusive access"))
}
io_kit_sys::ret::kIOReturnNotFound => Err(Error::new(ErrorKind::NotFound, "not found")),
_ => Err(Error::from_raw_os_error(r)),
}
}