macosusbiokit

Unable to access USB device with IOKit/iousblib


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"
          }

Solution

  • 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.)