cusbdriverwebcamlibusb

Why does SET_INTERFACE on my Logitech QuickCam via libusb give a "LIBUSB_ERROR_PIPE", but the official program can do the same thing without issue?


As a project to learn libusb, I am writing a driver for my old Logitech QuickCam Chat from 2006. To clarify, the camera quality is awful and I have no intention of using this for anything other than proof of concept.

Because Logitech doesn't provide the USB protocols for their old webcams, I am forced to use Wireshark and USBPcap to reverse engineer. The inspiration for this is this article also made by someone whose USB device did not officially support Linux. As the Webcam driver is old, I made a Windows 7 VM in VirtualBox and installed the driver from the disc I still have, and setup Wireshark with USBPcap for monitoring USB traffic.

My process is as simple as this: open Wireshark, connect the camera and forward via USB, and then open the official QuickCam program that came with the drivers, and then stop monitoring.

When the camera first opens, the light on the QuickCam turns green to indicate that it is recording. I have figured out this is probably caused by the SET_INTERFACE command which lets the camera know it is now in use. This is because when I close the program the light goes off and SET_INTERFACE appears once again in the logs.

First SET_INTERFACE when camera program opens and light goes green:

First SET_INTERFACE when camera program opens and light goes green

Second SET_INTERFACE when camera program is closed and light turns off:

Second SET_INTERFACE when camera program is closed and light turns off

Notice the bAlternateSetting switches between 6 (to turn it on) and 0 (to turn it off).

Yet somehow in my basic C program, which for now I just intend to turn the light on, running this command gives a code -9, which is LIBUSB_ERROR_PIPE.

#include <libusb-1.0/libusb.h>

#include <stdio.h>

int main() {
        libusb_context *ctx;
        libusb_init_context(&ctx, NULL, 0); // Initialise context
//      libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);

        libusb_device_handle *device_handle = libusb_open_device_with_vid_pid(ctx, 0x046d, 0x092e); // Get device handle
        libusb_reset_device(device_handle); // Reset device

        libusb_device *device = libusb_get_device(device_handle);

        // Get device descriptor
        struct libusb_device_descriptor desc; 
        int rc = libusb_get_device_descriptor(device, &desc);

        if (rc != 0) { // If error
                printf("%s\n", libusb_error_name(rc)); // Report error
                return rc; // Halt program returning error code
        }

        // If generic kernel driver is active then deactivate for this device
        if (libusb_kernel_driver_active(device_handle, 0) == 1) {
                rc = libusb_detach_kernel_driver(device_handle, 0);
                if (rc != 0) { // If it failed report error and terminate
                        printf("%s\n", libusb_error_name(rc));
                        return rc;
                }
        }

        rc = libusb_claim_interface(device_handle, 0); // Claim interface 0

        if (rc != 0) {
                printf("%s\n", libusb_error_name(rc));
                return rc;
        }

        // Attempt to set interface to 6
        int bytes = libusb_control_transfer(device_handle, 0x00, 0x0b, 6, 0, NULL, 0, 0);

        printf("Result of SET_INTERFACE: %s\n", libusb_error_name(bytes));

        libusb_release_interface(device_handle, 0); // Release interface
        libusb_close(device_handle); // Release device
        libusb_exit(ctx); // End context

        return 0;
}

OUTPUT:

Result of SET_INTERFACE: LIBUSB_ERROR_PIPE

The problematic line is int bytes = libusb_control_transfer(device_handle, 0x00, 0x0b, 6, 0, NULL, 0, 0);

0x00 is the bRequestType indicating the request is going from the host (my computer) to the device (the webcam). 0x0b is hexadecimal for 11 which is the number for SET_INTERFACE. Despite it being unlikely, I also did try 0x11 and got the same result.

6 is the bAlternateSetting which according to USB in a NutShell is to be given through the bValue parameter. 0 is the interface gathered both from previous investigation by libusb and also from Wireshark and is given as the index.

There is no data to receive by the host as it is sending the request to the device. So data is NULL. This is also stated in USB in a NutShell. Henceforth, wLength is 0.

Upon researching, I learned the LIBUSB_ERROR_PIPE occurs when the request is not supported by the device. However, that is impossible, as Wireshark explicitly shows that very request being performed by the QuickCam program and it works as the light turns green.

I have ran both with and without sudo so it is not permissions related, and I detached the camera from the VM after I was done monitoring it.

Could anyone explain why I am having this issue and how to solve it?


Solution

  • Libusb documentation says to use libusb_set_interface_alt_setting instead of formulating your own control transfer to do it:

    https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html#ga4858ad4f0f58fd1dc0afaead1fe6479a