Rust系统级IOKit绑定库IOKit-sys的使用,提供macOS底层硬件和驱动交互能力

Rust系统级IOKit绑定库IOKit-sys的使用,提供macOS底层硬件和驱动交互能力

IOKit-sys包为OS X上的IOKit C库提供了声明和链接。遵循*-sys包的约定,IOKit-sys包没有在原生库之上定义更高级别的抽象。

使用方法

Cargo.toml中添加IOKit-sys作为依赖项:

[dependencies]
IOKit-sys = "0.1"

导入IOKit_sys crate并使用Apple提供的原生IOKit库中定义的函数:

extern crate IOKit_sys as io;

完整示例代码

下面是一个使用IOKit-sys查询系统电源信息的完整示例:

extern crate IOKit_sys as io;

use std::ptr;

fn main() {
    unsafe {
        // 获取IOPowerSources服务的连接
        let power_sources = io::IOServiceGetMatchingService(
            io::kIOMasterPortDefault,
            io::IOServiceMatching(b"IOPMPowerSource\0".as_ptr() as *const _),
        );

        if power_sources != io::kIOReturnSuccess {
            println!("Failed to get power sources service");
            return;
        }

        // 打开服务连接
        let mut conn: io::io_connect_t = 0;
        let result = io::IOServiceOpen(power_sources, io::mach_task_self(), 0, &mut conn);
        if result != io::kIOReturnSuccess {
            println!("Failed to open IOService connection");
            return;
        }

        // 获取电源信息
        let mut output_cnt: u32 = 1;
        let mut output: u32 = 0;
        let result = io::IOConnectCallMethod(
            conn,
            0,  // power source information selector
            ptr::null(),
            0,
            ptr::null(),
            0,
            &mut output,
            &mut output_cnt,
            ptr::null_mut(),
            ptr::null_mut(),
        );

        if result == io::kIOReturnSuccess {
            println!("Current power source: {}", output);
        } else {
            println!("Failed to get power source info");
        }

        // 关闭连接
        io::IOServiceClose(conn);
    }
}

另一个完整示例:获取设备序列号

extern crate IOKit_sys as io;

use std::ffi::CStr;
use std::ptr;

fn main() {
    unsafe {
        // 获取平台专家服务
        let platform_expert = io::IOServiceGetMatchingService(
            io::kIOMasterPortDefault,
            io::IOServiceMatching(b"IOPlatformExpertDevice\0".as_ptr() as *const _),
        );

        if platform_expert.is_null() {
            println!("Failed to get platform expert service");
            return;
        }

        // 获取序列号属性
        let serial_number = io::IORegistryEntryCreateCFProperty(
            platform_expert,
            "IOPlatformSerialNumber".as_ptr() as *const _,
            io::kCFAllocatorDefault,
            0,
        );

        if serial_number.is_null() {
            println!("Failed to get serial number");
            io::IOObjectRelease(platform_expert);
            return;
        }

        // 转换为字符串并打印
        let cstr = CStr::from_ptr(io::CFStringGetCStringPtr(serial_number as _, io::kCFStringEncodingUTF8));
        println!("Device serial number: {:?}", cstr);

        // 释放资源
        io::CFRelease(serial_number);
        io::IOObjectRelease(platform_expert);
    }
}

贡献

如果您发现IOKit-sys缺少某些功能,可以在Github上提交issue或pull request添加所需功能。

代码结构说明:每个IOKit框架的头文件对应一个源文件。例如,src/io_return.rs包含来自IOKit/IOReturn.h的定义。每个文件中的定义顺序与匹配的头文件中的顺序大致相同。每个文件都在crate根目录中重新导出,例如pub use io_return::*

贡献者

  • David Cuddeback (dcuddeback)
  • Dave Hylands (dhylands)
  • forticulous
  • Nicolas Dusart (ndusart)

许可证

版权所有 © 2015 David Cuddeback

根据MIT许可证分发。


1 回复

Rust系统级IOKit绑定库IOKit-sys使用指南

IOKit-sys是Rust语言的系统级绑定库,提供了与macOS IOKit框架交互的能力,允许开发者访问macOS底层硬件和驱动功能。

基本介绍

IOKit是macOS的核心框架,用于与设备驱动程序和硬件交互。IOKit-sys提供了对IOKit的Rust绑定,使Rust开发者能够:

  • 发现和枚举设备
  • 与设备驱动程序通信
  • 访问硬件属性和功能
  • 监控设备状态变化

使用方法

1. 添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
IOKit-sys = "0.1"

2. 基本使用示例

extern crate IOKit_sys as io;

use std::ptr;

fn main() {
    unsafe {
        // 获取IOKit主端口
        let mut master_port: io::mach_port_t = 0;
        let kr = io::IOMasterPort(io::kIOMasterPortDefault, &mut master_port);
        
        if kr != io::KERN_SUCCESS {
            eprintln!("Failed to get IOKit master port");
            return;
        }

        // 创建匹配字典
        let matching_dict = io::IOServiceMatching(b"IOUSBDevice\0".as_ptr() as *const _);
        
        // 获取设备迭代器
        let mut iter: io::io_iterator_t = 0;
        let kr = io::IOServiceGetMatchingServices(master_port, matching_dict, &mut iter);
        
        if kr != io::KERN_SUCCESS {
            eprintln!("Failed to get matching services");
            return;
        }

        // 遍历USB设备
        let mut device = io::IOIteratorNext(iter);
        while device != 0 {
            let mut name: [u8; 128] = [0; 128];
            let kr = io::IORegistryEntryGetName(device, name.as_mut_ptr() as *mut _);
            
            if kr == io::KERN_SUCCESS {
                let name_str = std::ffi::CStr::from_ptr(name.as_ptr() as *const _)
                    .to_string_lossy();
                println!("Found device: {}", name_str);
            }
            
            io::IOObjectRelease(device);
            device = io::IOIteratorNext(iter);
        }
        
        io::IOObjectRelease(iter);
    }
}

3. 读取设备属性

unsafe fn read_device_property(device: io::io_object_t, property_name: &str) {
    let prop_name = std::ffi::CString::new(property_name).unwrap();
    let mut value: io::CFTypeRef = ptr::null_mut();
    
    let kr = io::IORegistryEntryCreateCFProperty(
        device,
        prop_name.as_ptr(),
        io::kCFAllocatorDefault,
        0,
        &mut value
    );
    
    if kr == io::KERN_SUCCESS && !value.is_null() {
        // 处理属性值
        println!("Property {} found", property_name);
        io::CFRelease(value);
    }
}

4. 监控设备通知

unsafe fn setup_device_notification(notify_port: io::mach_port_t, matching_dict: io::CFDictionaryRef) {
    let mut notification: io::io_object_t = 0;
    let kr = io::IOServiceAddMatchingNotification(
        notify_port,
        io::kIOFirstMatchNotification,
        matching_dict,
        Some(device_added_callback),
        ptr::null_mut(),
        &mut notification
    );
    
    if kr == io::KERN_SUCCESS {
        // 触发初始回调
        device_added_callback(ptr::null_mut(), notification);
    }
}

extern "C" fn device_added_callback(refcon: *mut std::ffi::c_void, iterator: io::io_iterator_t) {
    unsafe {
        let mut device = io::IOIteratorNext(iterator);
        while device != 0 {
            println!("New device detected!");
            io::IOObjectRelease(device);
            device = io::IOIteratorNext(iterator);
        }
    }
}

完整示例代码

下面是一个完整的USB设备枚举和监控示例:

extern crate IOKit_sys as io;
extern crate libc;

use std::ptr;
use std::ffi::CString;
use std::thread;
use std::time::Duration;

// USB设备结构体
struct USBDevice {
    name: String,
    vendor_id: Option<u32>,
    product_id: Option<u32>,
}

fn main() {
    // 1. 枚举所有USB设备
    let usb_devices = enumerate_usb_devices().unwrap_or_else(|e| {
        eprintln!("Error enumerating USB devices: {}", e);
        Vec::new()
    });

    println!("Found {} USB devices:", usb_devices.len());
    for (i, device) in usb_devices.iter().enumerate() {
        println!("{}. {}", i + 1, device.name);
        if let Some(vid) = device.vendor_id {
            println!("   Vendor ID: {:04x}", vid);
        }
        if let Some(pid) = device.product_id {
            println!("   Product ID: {:04x}", pid);
        }
    }

    // 2. 设置USB设备插拔通知
    if let Err(e) = setup_usb_notifications() {
        eprintln!("Failed to setup USB notifications: {}", e);
    }
}

// 枚举所有USB设备
fn enumerate_usb_devices() -> Result<Vec<USBDevice>, String> {
    unsafe {
        let mut devices = Vec::new();
        let mut master_port: io::mach_port_t = 0;
        
        // 获取IOKit主端口
        let kr = io::IOMasterPort(io::kIOMasterPortDefault, &mut master_port);
        if kr != io::KERN_SUCCESS {
            return Err("Failed to get IOKit master port".to_string());
        }

        // 创建USB设备匹配字典
        let matching_dict = io::IOServiceMatching(b"IOUSBDevice\0".as_ptr() as *const _);
        if matching_dict.is_null() {
            return Err("Failed to create matching dictionary".to_string());
        }

        // 获取设备迭代器
        let mut iter: io::io_iterator_t = 0;
        let kr = io::IOServiceGetMatchingServices(master_port, matching_dict, &mut iter);
        if kr != io::KERN_SUCCESS {
            return Err("Failed to get matching services".to_string());
        }

        // 遍历USB设备
        let mut device = io::IOIteratorNext(iter);
        while device != 0 {
            // 获取设备名称
            let mut name_buf: [u8; 128] = [0; 128];
            let mut usb_device = USBDevice {
                name: String::new(),
                vendor_id: None,
                product_id: None,
            };

            if io::IORegistryEntryGetName(device, name_buf.as_mut_ptr() as *mut _) == io::KERN_SUCCESS {
                usb_device.name = std::ffi::CStr::from_ptr(name_buf.as_ptr() as *const _)
                    .to_string_lossy()
                    .into_owned();
            }

            // 获取厂商ID和产品ID
            usb_device.vendor_id = get_property_uint32(device, "idVendor");
            usb_device.product_id = get_property_uint32(device, "idProduct");

            devices.push(usb_device);
            io::IOObjectRelease(device);
            device = io::IOIteratorNext(iter);
        }
        
        io::IOObjectRelease(iter);
        Ok(devices)
    }
}

// 获取设备的32位无符号整数属性
unsafe fn get_property_uint32(device: io::io_object_t, property_name: &str) -> Option<u32> {
    let prop_name = CString::new(property_name).unwrap();
    let mut value: io::CFTypeRef = ptr::null_mut();
    
    let kr = io::IORegistryEntryCreateCFProperty(
        device,
        prop_name.as_ptr(),
        io::kCFAllocatorDefault,
        0,
        &mut value
    );
    
    if kr == io::KERN_SUCCESS && !value.is_null() {
        let number = value as io::CFNumberRef;
        let mut num: u32 = 0;
        if io::CFNumberGetValue(number, io::kCFNumberSInt32Type, &mut num as *mut _ as *mut _) != 0 {
            io::CFRelease(value);
            return Some(num);
        }
        io::CFRelease(value);
    }
    
    None
}

// 设置USB设备插拔通知
fn setup_usb_notifications() -> Result<(), String> {
    unsafe {
        let mut master_port: io::mach_port_t = 0;
        let kr = io::IOMasterPort(io::kIOMasterPortDefault, &mut master_port);
        if kr != io::KERN_SUCCESS {
            return Err("Failed to get IOKit master port".to_string());
        }

        // 创建通知端口
        let notify_port = io::IONotificationPortCreate(master_port);
        if notify_port.is_null() {
            return Err("Failed to create notification port".to_string());
        }

        // 将通知端口添加到运行循环
        let run_loop_src = io::IONotificationPortGetRunLoopSource(notify_port);
        let run_loop = io::CFRunLoopGetCurrent();
        io::CFRunLoopAddSource(run_loop, run_loop_src, io::kCFRunLoopDefaultMode);

        // 创建USB设备匹配字典
        let matching_dict = io::IOServiceMatching(b"IOUSBDevice\0".as_ptr() as *const _);
        if matching_dict.is_null() {
            return Err("Failed to create matching dictionary".to_string());
        }

        // 添加设备添加通知
        let mut added_iter: io::io_iterator_t = 0;
        let kr = io::IOServiceAddMatchingNotification(
            notify_port,
            io::kIOFirstMatchNotification,
            matching_dict,
            Some(device_notification_callback),
            ptr::null_mut(),
            &mut added_iter
        );

        if kr != io::KERN_SUCCESS {
            return Err("Failed to add matching notification".to_string());
        }

        // 触发初始回调
        device_notification_callback(ptr::null_mut(), added_iter);

        // 添加设备移除通知
        let matching_dict = io::IOServiceMatching(b"IOUSBDevice\0".as_ptr() as *const _);
        let mut removed_iter: io::io_iterator_t = 0;
        let kr = io::IOServiceAddMatchingNotification(
            notify_port,
            io::kIOTerminatedNotification,
            matching_dict,
            Some(device_notification_callback),
            ptr::null_mut(),
            &mut removed_iter
        );

        if kr != io::KERN_SUCCESS {
            return Err("Failed to add terminated notification".to_string());
        }

        // 触发初始回调
        device_notification_callback(ptr::null_mut(), removed_iter);

        println!("Monitoring USB devices. Press Ctrl+C to exit...");
        
        // 运行事件循环
        io::CFRunLoopRun();

        Ok(())
    }
}

// 设备通知回调函数
extern "C" fn device_notification_callback(refcon: *mut libc::c_void, iterator: io::io_iterator_t) {
    unsafe {
        let mut device = io::IOIteratorNext(iterator);
        while device != 0 {
            let mut name_buf: [u8; 128] = [0; 128];
            if io::IORegistryEntryGetName(device, name_buf.as_mut_ptr() as *mut _) == io::KERN_SUCCESS {
                let name = std::ffi::CStr::from_ptr(name_buf.as_ptr() as *const _)
                    .to_string_lossy();
                
                // 检查是设备连接还是断开
                let mut is_terminated = 0;
                io::IOObjectIsClass(device, b"IOUSBDevice\0".as_ptr() as *const _, &mut is_terminated);
                
                if is_terminated != 0 {
                    println!("USB device disconnected: {}", name);
                } else {
                    println!("USB device connected: {}", name);
                }
            }
            
            io::IOObjectRelease(device);
            device = io::IOIteratorNext(iterator);
        }
    }
}

注意事项

  1. 安全性:IOKit-sys大部分操作都是unsafe的,需要开发者自行确保内存和线程安全。

  2. 错误处理:所有IOKit函数都返回kern_return_t,应检查返回值是否为KERN_SUCCESS。

  3. 资源管理:使用IOObjectRelease释放获取的对象,避免内存泄漏。

  4. 权限:某些操作可能需要root权限或特定的entitlements。

  5. 线程:IOKit操作通常应在主线程执行。

高级功能

  • 直接内存访问:通过IOConnectCallMethod与内核扩展通信
  • 电源管理:监控和处理设备电源状态变化
  • 热插拔检测:实时检测设备连接/断开事件

IOKit-sys为Rust开发者提供了强大的macOS硬件交互能力,但需要谨慎使用以避免系统稳定性问题。

回到顶部