caudiousblibusblibusb-1.0

LIBUSB_ERROR_PIPE when trying to control volume using libusb


I am trying to write a small C program to read and adjust the volume of a USB DAC (Apple USB-C to 3.5mm Dongle). I have been using this document as a reference, but have encountered an issue that I cannot seem to work around. I am receiving a LIBUSB_ERROR_PIPE response for my requests.

libusb_control_transfer(handle, 0b10100001, 0x81, 0x0200, 0x0200, mesg, 2, 500); is the line I am using, the bmRequestType, bRequest comes from the document on page 68, wValue is 0x02 (VOLUME_CONTROL) with a lower 0 byte, wIndex comes from sniffing wireshark during a volume change (I have a feeling this is where I am failing, however I do not understand how the Terminal ID is sourced), mesg is 2 0x00 bytes, and wLength is 2. The full program is below.

#include <assert.h>
#include <libusb-1.0/libusb.h>

int main(int argc, char **argv) {
    libusb_context *context;
    libusb_device_handle *handle;
    libusb_device *device;
    
    libusb_device **devs;
    ssize_t num_devs;
    
    int i;
    
    struct libusb_device_descriptor desc;
    unsigned char buffer[256];
    
    assert(!libusb_init(&context));
    
    num_devs = libusb_get_device_list(context, &devs);
    if (num_devs < 0) {
        fprintf(stderr, "Error getting device list\n");
        libusb_exit(context);
        return 1;
    }
    
    for (i = 0; i < num_devs; ++i) {
        libusb_device *dev = devs[i];
        struct libusb_device_descriptor desc;
        int r = libusb_get_device_descriptor(dev, &desc);
        if (r < 0) {
            fprintf(stderr, "Failed to get device descriptor\n");
            continue;
        }
        if(desc.idVendor == 0x05ac && desc.idProduct == 0x110a){
            device = dev;
        }
    }
    
    assert(!libusb_get_device_descriptor(device, &desc));
    printf("Vendor ID: %04x\n", desc.idVendor);
    printf("Product ID: %04x\n", desc.idProduct);

    assert(libusb_open(device, &handle) == 0);
    device = libusb_get_device(handle);
    
    assert(libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, buffer, 256) >= 0);
    printf("Manufacturer: %s\n", buffer);
    assert(libusb_get_string_descriptor_ascii(handle, desc.iProduct, buffer, 256) >= 0);
    printf("Product: %s\n", buffer);
    if (libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, buffer, 256) >= 0)
        printf("Serial No: %s\n", buffer);
    libusb_detach_kernel_driver(handle, 0);
    libusb_claim_interface(handle, 0);
    
    int result = libusb_control_transfer(handle, 0b10100001, 0x81, 0x0200, 0x0200, mesg, 2, 500);
    printf(libusb_error_name(result));
    printf("%d", result);
    printf("\n");
    
    libusb_release_interface(handle, 0);
    libusb_attach_kernel_driver(handle, 0);
    libusb_exit(context);
}

Solution

  • I think your bRequest should be 0x1 instead of 0x81:

    int result = libusb_control_transfer(handle, 0b10100001 /* bmRequest */, 0x1 /* bRequest */, 0x0200 /* wValue */, 0x0200 /* wIndex */, mesg, 2 /* wLength */, 500);
    

    I can't quote UAC 1.0 at you right now, but it's what I see in wireshark and your code succeeds, resulting in a volume that matches what I see in Audio MIDI Setup.app.