英文:
Unable to access USB device with IOKit/iousblib
问题
我正在尝试对USB设备(繁忙指示灯设备)进行实验。为了做到这一点,我正在使用IOKit来帮助我访问该设备。我对与USB设备交互的完整细节不太熟悉,已经使用了许多资源来帮助我,并找到了以下步骤:
- 使用IOServiceMatching根据productId和vendorID搜索我的设备。
- 使用io_iterator_t对象找到我的设备。
- 创建设备驱动程序的实例(例如IOUSBDeviceInterface300)。
- 创建IOUSBInterfaceInterface类的实例,以便我可以读取/发送数据到我的设备。
我了解通常情况下,我正在尝试创建一个内核扩展程序。也就是说,在用户空间中创建内核类型对象,以便我可以确保我的代码不会在内核级别引发问题。
我一直在使用Ole Henry Halvorsen/Douglas Clarke编写的《OS X和iOS内核编程》第15章。
我的代码如下,并且我的错误发生在带有语句“ERROR OCCURS ON THE NEXT LINE”的注释之后。
我得到的错误如下:
e00002c2
使用调试器,我可以深入挖掘并看到其他错误,例如以下错误:
Exception: EXC_BAD_ACCESS (code=257, address=0x3cb0b1bb)
我可以看到这指的是我的代码访问了要么不存在的内存,要么我没有权限访问的内存或资源。
我还对授权进行了一些研究,但不确定如何启用访问,以便我的代码可以访问这个设备。
我正在寻求帮助,以了解为什么我会收到这个访问错误,以及如何设置授权以允许访问这个设备?
#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;
// 创建一个匹配IOUSBDevice的字典
matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName);
if (matchingDictionary == NULL) goto bail;
// 将USB供应商ID添加到匹配字典
numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idVendor);
if (numberRef == NULL) goto bail;
CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBVendorID), numberRef);
CFRelease(numberRef);
// 将USB产品ID添加到匹配字典
numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idProduct);
if (numberRef == NULL) goto bail;
CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBProductID), numberRef);
CFRelease(numberRef);
// 成功 - 返回字典给调用者
return matchingDictionary;
bail:
// 失败 - 释放资源并返回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); // 注意:在代码中,这被写为“LPVOIDE”而没有星号
err2 = (*usbDevice)->USBDeviceOpen(usbDevice);
printf("打开usbDevice时的错误代码:%d\n", err2);
UInt8 stringIndex;
err2 = (*usbDevice)->USBGetManufacturerStringIndex(usbDevice, &stringIndex);
printf("访问制造商字符串索引时的错误代码:%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));
// 错误发生在下一行,即“(*usbDevice)->DeviceRequest(&usbDevice, &devRequest)”。
err2 = (*usbDevice)->DeviceRequest(&usbDevice, &devRequest);
printf("进行设备请求时的错误代码:%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("打开usbinterface时的错误代码:%x", err);
return usbInterface;
}
void MyFindMatchingDevices (CFDictionaryRef matchingDictionary) {
io_iterator_t iterator = 0;
io_service_t usbDeviceRef;
kern_return_t err;
//
<details>
<summary>英文:</summary>
I am trying to experiment with a USB device (busy-light device). In order to do this, I am using the IOKit to help me access the device. I am not as familiar with the full nuances of interfacing with USB devices and have used a number of sources to help and found the following steps:
1. Use the IOServiceMatching to search for my device using the productId and vendorID
2. Use the io_iterator_t object to find my device
3. Create an instance of the device driver (e.g. IOUSBDeviceInterface300)
4. Create an instance of the IOUSBInterfaceInterface class so that I can read/send data to my device
I understand that in general, I am trying to create a kernel extension. That is create the kernel type objects in user space such that I can ensure my code doesn't create issues at the kernel level.
I have been using "OS X and iOS Kernel Programming" by Ole Henry Halvorsen/Douglas Clarke chapter 15.
My code is listed below and my error occurs after the comment with the statement "ERROR OCCURS ON THE NEXT LINE"
The error that I am getting is as follows:
e00002c2
Using the debugger, I can dig deep and see other errors like the following:
Exception: EXC_BAD_ACCESS (code=257, address=0x3cb0b1bb)
I can see that this refers to my code accessing either memory that doesn't exist or memory or resources that I don't have permissions for.
I also did some research on entitlements but I am not sure how enable access such that my code can get access to this device.
I am looking for help on understanding why I am getting this access error and also how to set the entitlements to allow access to this device?
#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;
}
When I run
ioreg -p IOUSB -w0 -l
I can see the following:
+-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"
}
</details>
# 答案1
**得分**: 1
我明白,通常情况下,我正在尝试创建一个内核扩展。
一点也不是。`IOKit/IOUSBLib.h` 定义了与用户空间 IOCFPlugin 库的接口,该库*提供访问*内核 I/O Kit 对象。在任何时候,您都不需要为此创建内核扩展。(而且,USB 内核扩展在现代 macOS 上不会再加载。)
我收到的错误如下所示:
e00002c2
[这是 `kIOReturnBadArgument`](https://github.com/apple-oss-distributions/xnu/blob/aca3beaa3dfbd42498b42c5e5ce20a938e6554e5/iokit/IOKit/IOReturn.h#L147),考虑使用一个[辅助库函数](https://gitlab.com/pmdj/kextgizmos/-/blob/main/ioreturn_strings.cpp#L88)将这些代码转换回可读的人类字符串以进行诊断。
现在,到有问题的代码行:
```cpp
err2 = (*usbDevice)->DeviceRequest(&usbDevice, &devRequest);
// 问题在这里-----------------^
问题在于 IOUSBDeviceInterface300
(以及类似的接口)方法期望将 COM 接口指针作为第一个参数,而不是包含 COM 接口指针的变量的指针。移除 &
符号,应该可以正常工作。(请注意,我尚未彻底审查您的其余代码。)
英文:
> I understand that in general, I am trying to create a kernel extension.
Not at all. IOKit/IOUSBLib.h
defines the interface to a user space IOCFPlugin library which provides access to kernel I/O Kit objects. At no point do you need to create a kernel extension for this. (And anyway, USB kernel extensions won't load anymore on modern macOS.)
> The error that I am getting is as follows:
>
>
> e00002c2
>
This is kIOReturnBadArgument
, consider using a helper library function that will turn the codes back into human readable strings for diagnostics.
Now, to the problematic line:
err2 = (*usbDevice)->DeviceRequest(&usbDevice, &devRequest);
// Problem is here-----------------^
The issue is that the IOUSBDeviceInterface300
(and friends) methods expect the COM interface pointer as the first argument, not a pointer to a variable containing the COM interface pointer. Remove the &
ampersand, and it should work. (Note that I haven't thoroughly reviewed the rest of your code however.)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论