You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用IOKit/IOUSBLib无法访问USB设备的问题求助

BUSYLIGHT OMEGA USB设备IOKit访问错误分析与权限配置解决方法

问题描述

我正在调试BUSYLIGHT OMEGA USB设备,采用IOKit框架实现访问。参考资料整理出以下步骤:

  • 用IOServiceMatching结合vendorID和productID搜索设备
  • 通过io_iterator_t查找目标设备
  • 创建IOUSBDeviceInterface300设备驱动实例
  • 创建IOUSBInterfaceInterface实例以读写设备数据

知晓这是在用户空间创建类内核对象以规避内核级问题,参考《OS X and iOS Kernel Programming》第15章编写代码。代码执行到标注“ERROR OCCURS ON THE NEXT LINE”的DeviceRequest调用时出错,错误码为e00002c2,调试时还出现EXC_BAD_ACCESS(code=257)内存访问错误,推测是权限或非法内存访问问题。研究过权限授权(entitlements)但不知如何配置以获取设备访问权限,现寻求错误原因分析及权限配置方法。

附完整代码

#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/usb/USBSpec.h>

CFDictionaryRef MyCreateUSBMatchingDictionary(SInt32 idVendor, SInt32 idProduct) {
    CFMutableDictionaryRef matchingDictionary = NULL;
    CFNumberRef numberRef;

    // Create a matching dictionary for IOUSBDevice
    matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName);
    if (matchingDictionary == NULL) goto bail;

    // Add the USB Vendor ID to the matching dictionary
    numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idVendor);
    if (numberRef == NULL) goto bail;
    CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBVendorID), numberRef);
    CFRelease(numberRef);

    // Add the USB Product ID to the matching dictionary
    numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idProduct);
    if (numberRef == NULL) goto bail;
    CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBProductID), numberRef);
    CFRelease(numberRef);


    // Success - return the dictionary to the caller
    return matchingDictionary;

    bail:
    // Failure - release resources and return NULL
    if (matchingDictionary != NULL)
        CFRelease(matchingDictionary);
    return NULL;
}

IOUSBDeviceInterface300** MyStartDriver(io_service_t usbDeviceRef) {
    SInt32 score;
    IOCFPlugInInterface** plugin;
    IOUSBDeviceInterface300** usbDevice = NULL;
    kern_return_t err1, err2;

    err1 = IOCreatePlugInInterfaceForService(usbDeviceRef, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin,
                                             &score);
    if (err1 == 0) {
        err1 = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID300),
                                         (LPVOID*)&usbDevice);  // NOTE: In the code this is stated as "LPVOIDE" without the astrix
        err2 = (*usbDevice)->USBDeviceOpen(usbDevice);
        printf("Error code when opening the usbDevice: %d\n", err2);

        UInt8 stringIndex;
        err2 = (*usbDevice)->USBGetManufacturerStringIndex(usbDevice, &stringIndex);
        printf("Error code when accessing the mfg string index: %d\n", err2);

        IOUSBDevRequest devRequest;
        UInt8 buffer[256];
        devRequest.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
        devRequest.bRequest = kUSBRqGetDescriptor;
        devRequest.wValue = (kUSBStringDesc << 8) | stringIndex;
        devRequest.wIndex = 0x409;
        devRequest.wLength = sizeof(buffer);
        devRequest.pData = &buffer[0];
        bzero(&buffer[0], sizeof(buffer));
        // ERROR OCCURS ON THE NEXT LINE i.e. "(*usbDevice)->DeviceRequest(&usbDevice, &devRequest)"
        err2 = (*usbDevice)->DeviceRequest(&usbDevice, &devRequest);
        printf("Error code when making device request: %x", err2);

        IODestroyPlugInInterface(plugin);
    }
    return usbDevice;
}

IOUSBDeviceInterface300 ** MyCreateInterfaceClass(io_service_t usbInterfaceRef) {
    SInt32 score;
    IOCFPlugInInterface** plugin;
    IOUSBDeviceInterface300** usbInterface = NULL;
    kern_return_t err;

    err = IOCreatePlugInInterfaceForService(usbInterfaceRef, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID,
                                            &plugin, &score);
    if (err == 0) {
        err = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID300),
                                        (LPVOID*)&usbInterface);
        IODestroyPlugInInterface(plugin);
    }
    printf("Error code when opening the usbinterface: %x", err);

    return usbInterface;
}

void MyFindMatchingDevices (CFDictionaryRef matchingDictionary) {
    io_iterator_t iterator = 0;
    io_service_t usbDeviceRef;
    kern_return_t err;

    // Find all kernel objects that match the directory
    err = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &iterator);

    if (err == 0) {
        // Iterate over all matching kernel objects
        while ((usbDeviceRef = IOIteratorNext(iterator)) !=0 ) {
            IOUSBDeviceInterface300** usbDevice;
            IOUSBDeviceInterface300** usbInterface;

            // Create a driver for this device instance
            usbDevice = MyStartDriver(usbDeviceRef);
            //usbInterface = MyCreateInterfaceClass(usbDeviceRef);
            IOObjectRelease(iterator);
        }

    }
}

int main() {
    CFDictionaryRef myDict = MyCreateUSBMatchingDictionary(10171, 15311);
    if (myDict == NULL) {
        printf("Could not find matching dictionary item -- stopping");
        exit(0);
    }
    MyFindMatchingDevices(myDict);
    return 0;
}

ioreg设备信息

执行ioreg -p IOUSB -w0 -l得到设备信息:

+-o BUSYLIGHT OMEGA@00110000  <class IOUSBHostDevice, id 0x10008951b, registered, matched, active, busy 0 (38 ms), retain 31>
          {
            "sessionID" = 23078049098302
            "USBSpeed" = 1
            "idProduct" = 15311
            "iManufacturer" = 1
            "bDeviceClass" = 0
            "IOPowerManagement" = {"PowerOverrideOn"=Yes,"DevicePowerState"=2,"CurrentPowerState"=2,"CapabilityFlags"=32768,"MaxPowerState"=2,"DriverPowerState"=0}
            "bcdDevice" = 256
            "bMaxPacketSize0" = 8
            "iProduct" = 2
            "iSerialNumber" = 3
            "bNumConfigurations" = 1
            "UsbDeviceSignature" = <bb27cf3b000139323230393246463030313032344646303346464646464632373032464646463330303246464646000000030000>
            "USB Product Name" = "BUSYLIGHT OMEGA"
            "locationID" = 1114112
            "bDeviceSubClass" = 0
            "bcdUSB" = 512
            "kUSBSerialNumberString" = "922092FF001024FF03FFFFFF2702FFFF3002FFFF"
            "USB Address" = 4
            "IOCFPlugInTypes" = {"9dc7b780-9ec0-11d4-a54f-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
            "kUSBCurrentConfiguration" = 1
            "bDeviceProtocol" = 0
            "USBPortType" = 0
            "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
            "USB Vendor Name" = "PLENOM A/S"
            "Device Speed" = 1
            "idVendor" = 10171
            "kUSBProductString" = "BUSYLIGHT OMEGA"
            "USB Serial Number" = "922092FF001024FF03FFFFFF2702FFFF3002FFFF"
            "IOGeneralInterest" = "IOCommand is not serializable"
            "kUSBAddress" = 4
            "kUSBVendorString" = "PLENOM A/S"
          }

错误原因分析

1. 内存访问错误的直接原因

代码中DeviceRequest调用的参数错误:

err2 = (*usbDevice)->DeviceRequest(&usbDevice, &devRequest);

IOUSBDeviceInterface的DeviceRequest方法第一个参数应为接口指针本身,而非指针的地址。正确调用方式:

err2 = (*usbDevice)->DeviceRequest(usbDevice, &devRequest);

传递&usbDevice会导致函数访问非法内存地址,直接触发EXC_BAD_ACCESS(code=257)错误。

2. 权限错误的深层原因

错误码e00002c2对应kIOReturnNotAuthorized,说明应用无足够权限访问USB设备。从ioreg输出的IOServiceDEXTEntitlements字段可知,设备需要特定权限,且macOS对用户空间USB访问有严格管控,必须通过正确的entitlements配置获取授权。

权限配置方法

1. 创建Entitlements文件

在Xcode项目中添加新的Property List文件(命名为MyApp.entitlements),添加以下内容:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.device.usb</key>
    <array>
        <dict>
            <key>idVendor</key>
            <integer>10171</integer>
            <key>idProduct</key>
            <integer>15311</integer>
        </dict>
    </array>
</dict>
</plist>

该权限允许应用访问指定vendorID和productID的USB设备。

2. 配置Xcode项目

  • 打开项目的Signing & Capabilities标签页
  • 点击+ Capability,搜索并添加USB权限
  • 在USB权限模块中,点击+按钮,输入设备的vendorID(10171)和productID(15311)
  • 确保项目的Code Signing Entitlements指向创建的MyApp.entitlements文件

3. 代码额外修正点

  • MyFindMatchingDevices函数中,IOObjectRelease(iterator)需放在循环外,否则第一次循环后迭代器被释放,后续循环出错:
void MyFindMatchingDevices (CFDictionaryRef matchingDictionary) {
    io_iterator_t iterator = 0;
    io_service_t usbDeviceRef;
    kern_return_t err;

    err = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &iterator);

    if (err == 0) {
        while ((usbDeviceRef = IOIteratorNext(iterator)) !=0 ) {
            IOUSBDeviceInterface300** usbDevice;
            usbDevice = MyStartDriver(usbDeviceRef);
            // 使用完设备接口后释放资源
            if (usbDevice) {
                (*usbDevice)->USBDeviceClose(usbDevice);
                IOObjectRelease(*usbDevice);
            }
            IOObjectRelease(usbDeviceRef); // 释放单个设备引用
        }
        IOObjectRelease(iterator); // 循环结束后释放迭代器
    }
}
  • 使用完IOUSBDeviceInterface后,必须调用USBDeviceClose和IOObjectRelease释放资源,避免内存泄漏。

内容的提问来源于stack exchange,提问作者dshenoy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 22:44:57