Search code examples
c++clibusb

libusb: error [submit_bulk_transfer] submiturb failed error -1 errno=2 - What am I doing wrong with LibUSB?


Ok, I'm probably doing something dumb, but I can't get libusb to let me transfer data to my device for the life of me.

Code:

#include <iostream>
#include <iomanip>

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>

#include <libusb-1.0/libusb.h>


#define EP_DATA_IN  0x83
#define EP_DATA_OUT 0x02
#define DEVICE_CONFIGURATION 0


int main(int argc, char **argv)
{

    int rc;
    libusb_context *ctx = NULL;
    libusb_device_handle *dev_handle;

    int actual = 0;
    unsigned char *data = new unsigned char[4];
    data[0]='a';data[1]='b';data[2]='c';data[3]='d';

    rc = libusb_init(&ctx);
    if(rc < 0) {
        std::cout << "Init Error " << rc << std::endl;
        return 1;
    }

    libusb_set_debug(ctx, 6);


    dev_handle = libusb_open_device_with_vid_pid(ctx, 0x03eb, 0x2423);
    if (!dev_handle) {
        fprintf(stderr, "Error finding USB device\n");
        return 2;
    }

    if(libusb_kernel_driver_active(dev_handle, DEVICE_CONFIGURATION) == 1) {
        std::cout << "Kernel Driver Active" << std::endl;
        if(libusb_detach_kernel_driver(dev_handle, DEVICE_CONFIGURATION) == 0)
            std::cout << "Kernel Driver Detached!" << std::endl;
    }

    rc = libusb_claim_interface(dev_handle, DEVICE_CONFIGURATION);
    if(rc != 0) {
        std::cout << "Cannot Claim Interface" << std::endl;
        return 3;
    }

    std::cout << "Data->" << data << "<-" << std::endl;
    std::cout << "Writing Data..." << std::endl;

    std::cout << "Trying endpoint " << EP_DATA_OUT << "." << std::endl;
    rc = libusb_bulk_transfer(dev_handle, EP_DATA_OUT, data, sizeof(data), &actual, 100);

    if(rc == 0 && actual == 4)
    {
        std::cout << "Writing Successful!" << std::endl;
    }
    else
    {
        std::cout << "Write Error! Rc: " << rc << " Actual transfered bytes: " << actual << "." << std::endl;
        std::cout << "Error code means: " << libusb_error_name(rc) << std::endl;
    }


    rc = libusb_release_interface(dev_handle, 0);
    if(rc!=0) {
        std::cout << "Cannot Release Interface" << std::endl;
        return 1;
    }

    if (dev_handle)
        libusb_close(dev_handle);

    libusb_exit(ctx);

    return 0;
}

Device in question:

pi@testpi:~$ sudo lsusb -d 03eb: -v

Bus 001 Device 004: ID 03eb:2423 Atmel Corp.
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  idVendor           0x03eb Atmel Corp.
  idProduct          0x2423
  bcdDevice            1.00
  iManufacturer           1 ATMEL ASF
  iProduct                2 Vendor Class Example
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           69
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0xc0
      Self Powered
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass    255 Vendor Specific Subclass
      bInterfaceProtocol    255 Vendor Specific Protocol
      iInterface              0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       1
      bNumEndpoints           6
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass    255 Vendor Specific Subclass
      bInterfaceProtocol    255 Vendor Specific Protocol
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x04  EP 4 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x85  EP 5 IN
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0100  1x 256 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x06  EP 6 OUT
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0100  1x 256 bytes
        bInterval               1
Device Status:     0x0001
  Self Powered

And what I get when I run the code against the device:

pi@testpi:~/BatLogger/Interface/libusb_test$ ./make.sh
libusb: debug [libusb_get_device_list]
libusb: debug [libusb_get_device_descriptor]
libusb: debug [libusb_open] open 1.4
libusb: debug [usbi_add_pollfd] add fd 11 events 4
libusb: debug [libusb_kernel_driver_active] interface 0
libusb: debug [libusb_claim_interface] interface 0
Data->abcd<-
Writing Data...
Trying endpoint 2.
libusb: debug [add_to_flying_list] arm timerfd for timeout in 100ms (first in line)
libusb: debug [submit_bulk_transfer] need 1 urbs for new transfer with length 4
libusb: error [submit_bulk_transfer] submiturb failed error -1 errno=2
libusb: debug [submit_bulk_transfer] first URB failed, easy peasy
libusb: debug [disarm_timerfd]
Write Error! Rc: -1 Actual transfered bytes: 0.
Error code means: LIBUSB_ERROR_IO
libusb: debug [libusb_release_interface] interface 0
libusb: debug [libusb_close]
libusb: debug [usbi_remove_pollfd] remove fd 11
libusb: debug [libusb_exit]
libusb: debug [libusb_exit] destroying default context

As far as I know, I'm doing everything correctly. libusb_claim_interface returns OK, there isn't a pre-existing driver attached to the device since I'm using a custom VID/PID combo, and EP_DATA_OUT is a output endpoint (direction bit is 0, though to whom "out" is with respect to isn't described). Out of irritation, I've also tried every other possible endpoint (0-16, 0-16 | 1 << 7), with the exact same error for all of them.

Is there something silly I'm missing? Do I have to install a kernel module or something to make libusb play nice with me? I'm using libusb-1.0.

The error from the libusb debug message is error -1 errno=2. where errno=2 corresponds to ERNOENT, but the few things I could find about that together with libusb didn't have a decent conclusion about what's actually going on.

Code is built g++ -std=c++11 -Wall -lrt -lusb-1.0 main.cpp -o main.bin, though the fact that I'm using C++ is probably not relevant to the issue, since I'm not using one of the C++ libusb wrappers.


Solution

  • Ok, so I figured out the issue.

    Basically, apparently, for ~reasons~, the endpoints for my device are attached to configuration 0, alternate setting 1.

    I'm not sure how, or if it's even possible to determine this from the output of lsusb, but I had a bit of scripting that I had used for a different device written against PyUSB, so I had a poke around with that, and it told me:

    1 pi@testpi:~/BatLogger/Interface/libusb_test$ sudo python3 test.py
    INFO:Main.Gui:Device: DEVICE ID 03eb:2423 on Bus 001 Address 004 =================
     bLength                :   0x12 (18 bytes)
     bDescriptorType        :    0x1 Device
     bcdUSB                 :  0x200 USB 2.0
     bDeviceClass           :    0x0 Specified at interface
     bDeviceSubClass        :    0x0
     bDeviceProtocol        :    0x0
     bMaxPacketSize0        :   0x40 (64 bytes)
     idVendor               : 0x03eb
     idProduct              : 0x2423
     bcdDevice              :  0x100 Device 1.0
     iManufacturer          :    0x1 ATMEL ASF
     iProduct               :    0x2 Vendor Class Example
     iSerialNumber          :    0x0
     bNumConfigurations     :    0x1
      CONFIGURATION 1: 100 mA ==================================
       bLength              :    0x9 (9 bytes)
       bDescriptorType      :    0x2 Configuration
       wTotalLength         :   0x45 (69 bytes)
       bNumInterfaces       :    0x1
       bConfigurationValue  :    0x1
       iConfiguration       :    0x0
       bmAttributes         :   0xc0 Self Powered
       bMaxPower            :   0x32 (100 mA)
        INTERFACE 0: Vendor Specific ===========================
         bLength            :    0x9 (9 bytes)
         bDescriptorType    :    0x4 Interface
         bInterfaceNumber   :    0x0
         bAlternateSetting  :    0x0
         bNumEndpoints      :    0x0
         bInterfaceClass    :   0xff Vendor Specific
         bInterfaceSubClass :   0xff
         bInterfaceProtocol :   0xff
         iInterface         :    0x0
        INTERFACE 0, 1: Vendor Specific ========================
         bLength            :    0x9 (9 bytes)
         bDescriptorType    :    0x4 Interface
         bInterfaceNumber   :    0x0
         bAlternateSetting  :    0x1
         bNumEndpoints      :    0x6
         bInterfaceClass    :   0xff Vendor Specific
         bInterfaceSubClass :   0xff
         bInterfaceProtocol :   0xff
         iInterface         :    0x0
          ENDPOINT 0x81: Interrupt IN ==========================
           bLength          :    0x7 (7 bytes)
           bDescriptorType  :    0x5 Endpoint
           bEndpointAddress :   0x81 IN
           bmAttributes     :    0x3 Interrupt
           wMaxPacketSize   :   0x40 (64 bytes)
           bInterval        :    0x1
          ENDPOINT 0x2: Interrupt OUT ==========================
           bLength          :    0x7 (7 bytes)
           bDescriptorType  :    0x5 Endpoint
           bEndpointAddress :    0x2 OUT
           bmAttributes     :    0x3 Interrupt
           wMaxPacketSize   :   0x40 (64 bytes)
           bInterval        :    0x1
          ENDPOINT 0x83: Bulk IN ===============================
           bLength          :    0x7 (7 bytes)
           bDescriptorType  :    0x5 Endpoint
           bEndpointAddress :   0x83 IN
           bmAttributes     :    0x2 Bulk
           wMaxPacketSize   :   0x40 (64 bytes)
           bInterval        :    0x0
          ENDPOINT 0x4: Bulk OUT ===============================
           bLength          :    0x7 (7 bytes)
           bDescriptorType  :    0x5 Endpoint
           bEndpointAddress :    0x4 OUT
           bmAttributes     :    0x2 Bulk
           wMaxPacketSize   :   0x40 (64 bytes)
           bInterval        :    0x0
          ENDPOINT 0x85: Isochronous IN ========================
           bLength          :    0x7 (7 bytes)
           bDescriptorType  :    0x5 Endpoint
           bEndpointAddress :   0x85 IN
           bmAttributes     :    0x1 Isochronous
           wMaxPacketSize   :  0x100 (256 bytes)
           bInterval        :    0x1
          ENDPOINT 0x6: Isochronous OUT ========================
           bLength          :    0x7 (7 bytes)
           bDescriptorType  :    0x5 Endpoint
           bEndpointAddress :    0x6 OUT
           bmAttributes     :    0x1 Isochronous
           wMaxPacketSize   :  0x100 (256 bytes)
           bInterval        :    0x1
    

    The critical thing being that there are no endpoints under INTERFACE 0:, but there are endpoints under INTERFACE 0, 1:. This was enough to go on to figure out that there were more then one version of INTERFACE 0, and with that, it was pretty easy to figure out I needed to call libusb_set_interface_alt_setting() to select the right alternate configuration thingie.

    Basically, I wound up adding

    rc = libusb_set_interface_alt_setting(dev_handle, DEVICE_CONFIGURATION, 1);
    if(rc != 0) {
        std::cout << "Cannot configure alternate setting" << std::endl;
        return 3;
    }
    

    after the libusb_claim_interface() call in my C(++) code, and I can now write to the device.