Search code examples
macosmacos-catalinadriverkitmacos-system-extension

CopyPipe of DriverKit IOUSBHostInterface fails with kIOReturnError (0xe00002bc)


For my own edification, I'm trying to read some audio data from a USB audio interface using a DriverKit System Extension.

My IOProviderClass is IOUSBHostInterface. I can successfully Open() the interface, but CopyPipe() returns kIOReturnError (0xe00002bc). Why can't I copy the pipe?

To be able to open the interface at all, I had to outmatch AppleUSBAudio so my IOKitPersonalities explicitly match the bConfigurationValue, bInterfaceNumber, idVendor, idProduct, and bcdDevice keys. This list may not be minimal.

In ioreg I can normally see the interfaces (sometimes only my matching one is there, although I think this is a degenerate situation). I see a AppleUserUSBHostHIDDevice child on some of my other interfaces. Could this be the problem? Normally the device has no problem being both USBAudio and HID. I am trying unsuccessfully to out match HID too.


Solution

  • I was passing the wrong endpoint address to CopyPipe().

    To find an endpoint address you need to enumerate through the IOUSBDescriptorHeaders in the IOUSBConfigurationDescriptor and examine the descriptors with bDescriptorType equal to kIOUSBDescriptorTypeEndpoint.

    IOUSBGetNextDescriptor() from USBDriverKit/AppleUSBDescriptorParsing.h is made for this and will save you from having think about pointer manipulation.

    If the endpoint is in a different alternate setting, then you need to switch the interface to that one with SelectAlternateSetting().

    void
    enumerate_configs(const IOUSBConfigurationDescriptor *configDesc) {
        const IOUSBDescriptorHeader *curHeader = NULL;
        
        while ((curHeader = IOUSBGetNextDescriptor(configDesc, curHeader))) {
            switch (curHeader->bDescriptorType) {
                case kIOUSBDescriptorTypeEndpoint: {
                    auto endpoint = (const IOUSBEndpointDescriptor *)curHeader;
                    os_log(OS_LOG_DEFAULT, "Endpoint bLength: %{public}i, bDescriptorType: %i, bEndpointAddress: %i, bmAttributes: 0x%x, wMaxPacketSize: %i, bInterval: %i",
                           endpoint->bLength,
                           endpoint->bDescriptorType,
                           endpoint->bEndpointAddress,  // pass this to CopyPipe()
                           endpoint->bmAttributes,
                           endpoint->wMaxPacketSize,
                           endpoint->bInterval);
                }
                    break;
                default:
                    os_log(OS_LOG_DEFAULT, "some other type: %{public}i", curHeader->bDescriptorType);
                    break;
            }
        }
    }