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:
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"
}
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.)