Search code examples
c++cusblibusblibusb-1.0

LibUSB C++ Format of USB transfer differs


I've been using stack overflow for a long time now, and most of the problems a solution is already available. It is the first time that I actually couldn't figure it out with the web. I hope someone has the answer to the following problem.

Introduction I am currently working on a project which should be capable of executing a command and act upon its response. This project runs on a debian based system in a c++ console application. In order to be able to perform such commands I tried using the LibUSB library.

The problem Whenever packets are being sent it does not return a valid response as described in the documentation of the hardware. a default tool is available which triggers a callibration command, I sniffed these packets with Wireshark, but the structure of the OUT interrupt calls of the callibration tool differs from the LibUSB generated one, thus (I think) causing the command to not be executed.

The documentation provides one of the following commands, which should run a diagnostics check that returns 5 bytes of data.

[0] Header: 0x02   
[1] Command: 0x4C  
[2] Byte to send: 0x02 (N bytes to send, Argument + data size)   
[3] Argument: 0x09  
[4] Data: 0x00

The response should have the following format:

[0] Header: 0x02  
[1] Command: 0x4C  
[2] Byte to send: 0x03 (N bytes to send, Argument + data size)  
[3] Argument: 0x09  
[4] Processing result: D-1  
[5] Diagnostic result: D-2

D-1: either 0x01: Normal or 0x00 Error D-2: either 0x00: Normal or not 0x00, linked error code.

Things tried so far

  • Transfer types:
    • Synchronous:
    • Libusb_bulk_transfer
    • Libusb_control_transfer
    • libusb_interrupt_transfer
    • Asynchronous:
    • Libusb_fill_bulk_transfer
    • Libusb_fill_control_transfer
    • Libusb_fill_interrupt_transfer

I tried both async as synchronous implementations for the LibUSB library. The control transfer I tried randomly switching the variables after the most logical ways of filling them had ran out, without success, as to be expected. Since the results found in the packet sniffing clearly indicated INTERRUPT calls being made.

Interfaces: The hardware has two interfaces. Interface 0 which contains OUT 0x02 and IN 0x81, and interface 1 which contains OUT 0x04 and IN 0x83. The sniffing of the USB interrupt call to the device triggered by the tooling provided that interface 1 is being used for the diagnostics command. (Also tried interface 0 with both IN and OUT, couldn't get it to work.

Packet sniffing with Wireshark

Results of the packet sniffing Request and response generated with the tooling: IMG: Interrupt OUT (I marked the bit where to command is actually provided) IMG: Interrupt IN response This code actually works and returns the, expected, dataset in its data slot. (as described above, the return format is correct, 0x01 and 0x00).

Request and response generated with the LibUSB using code: IMG: Interrupt OUT IMG: Interrupt IN response

Yes, I also tried setting the buffer to a size of 64, the max buffer size for the hardware. Sadly didn't work. As seen clearly, both requests differ a lot, do I use the wrong transfer method? Is it another supported format in which you can send commands?

Used Code snippet: The code snippet is a bit outdated, I tried re-writing / editing it several times, the last implementations being used from online examples.

#define USB_VENDOR_ID       <VENDOR_ID>/* USB vendor ID used by the device
                                         * 0x0483 is STMs ID
                                         */
#define USB_PRODUCT_ID      <PRODUCT_ID>      /* USB product ID used by the device */
#define USB_ENDPOINT_IN     (LIBUSB_ENDPOINT_IN  | 0x83)   /* endpoint address */
#define USB_ENDPOINT_OUT    (LIBUSB_ENDPOINT_OUT | 0x04)   /* endpoint address */
#define USB_TIMEOUT         3000        /* Connection timeout (in ms) */
#define INTERFACE_NO        1

static libusb_context *ctx = NULL;
static libusb_device_handle *handle;

static uint8_t receiveBuf[64];
uint8_t transferBuf[64];

uint16_t counter=0;


int main(int argc, char **argv) {
    libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
    libusb_device_handle *dev_handle; //a device handle
    libusb_context *ctx = NULL; //a libusb session
    int r; //for return values
    ssize_t cnt; //holding number of devices in list
    r = libusb_init(&ctx); //initialize the library for the session we just declared
    if(r < 0) {
        qDebug()<<"Init Error "<<r<<endl; //there was an error
        return 1;
    }
    libusb_set_debug(ctx, 4); //set verbosity level to 3, as suggested in the documentation

    cnt = libusb_get_device_list(ctx, &devs); //get the list of devices
    if(cnt < 0) {
        qDebug()<<"Get Device Error"<<endl; //there was an error
        return 1;
    }
    qDebug()<<cnt<<" Devices in list."<<endl;

    dev_handle = libusb_open_device_with_vid_pid(ctx, 0x0AFA, 0x7D3); //these are vendorID and productID I found for my usb device
    if(dev_handle == NULL)
        qDebug()<<"Cannot open device"<<endl;
    else
        qDebug()<<"Device Opened"<<endl;
    libusb_free_device_list(devs, 1); //free the list, unref the devices in it

    unsigned char *data = new unsigned char[5] { 0x02, 0x4C, 0x02, 0x09, 0 }; //data to write
    data[0]= 0x02;data[1]= 0x4C;data[2]=0x02;data[3]=0x09; data[4]= 0; //some dummy values

    int actual; //used to find out how many bytes were written
    if(libusb_kernel_driver_active(dev_handle, INTERFACE_NO) == 1) { //find out if kernel driver is attached
        qDebug()<<"Kernel Driver Active"<<endl;
        if(libusb_detach_kernel_driver(dev_handle, INTERFACE_NO) == 0) //detach it
            qDebug()<<"Kernel Driver Detached!"<<endl;
    }
    r = libusb_claim_interface(dev_handle, INTERFACE_NO); //claim interface 0 (the first) of device (mine had jsut 1)
    if(r < 0) {
        qDebug()<<"Cannot Claim Interface"<<endl;
        return 1;
    }
    qDebug()<<"Claimed Interface"<<endl;

    for(int i = 0; i != sizeof(data); i++) {
        fprintf(stderr, "[%d] - %02x\n", i, data[i]);
    }
    qDebug()<<"Writing Data..."<<endl;
    r = libusb_bulk_transfer(dev_handle, (USB_ENDPOINT_OUT | LIBUSB_ENDPOINT_OUT), data, sizeof(data), &actual, 0); //my device's out endpoint was 2, found with trial- the device had 2 endpoints: 2 and 129
    if(r == 0 && actual == sizeof(data)) //we wrote the 4 bytes successfully
        qDebug()<<"Writing Successful!"<<endl;
    else
        qDebug()<<"Write Error"<<endl;
        fprintf(stderr, "Error Writing: %s", libusb_strerror(static_cast<libusb_error>(r)));

    r = libusb_release_interface(dev_handle, INTERFACE_NO); //release the claimed interface
    if(r!=0) {
        qDebug()<<"Cannot Release Interface"<<endl;
        return 1;
    }
    qDebug()<<"Released Interface"<<endl;

    libusb_close(dev_handle); //close the device we opened
    libusb_exit(ctx); //needs to be called to end the

    delete[] data; //delete the allocated memory for data
    return 0;
}

I hope I that there's someone out there capable and willing to help me out here, because I've been working on this for three days straight and still haven't gotten a logical solution to this problem.

Thanks in advance!

~ Mark


Solution

  • Thanks for your response! I currently found a solution to the problem! It had nothing to do with using both C / C++. Sorry for the code being a bit messy. I wrote it several times so tidiness wasn't my priority, though I will keep it in mind for a possible future post on StackOverflow. Even though solved I added results of sniffing both packets going IN and OUT, hoping it may help others with a possible same issue.

    Well, what was the problem?
    So, the capture of the tool indicated the last 64 bit being the payload of the request and its data, this is for both OUT and IN. (As to be seen in the images now actually provided) and as I said before, I tried allocating arrays with a size of 64 and setting the first few slots with the data necessary for the operation. As for the other slots, they were filled with the leftovers sitting at those allocated memory addresses.

    What did I do to fix it
    So, what I did was the following. After initializing an array and assigning it a size of 64 I set all of the allocated slots to 0 with the memset command, so the array would be completely cleared of left-over data. This left me with a clean array in which I could set the variables necessary for the command I wanted to send. (See the following snippet)

    // Initialize array of 64 bytes.
        uint8_t *data = new uint8_t[64];
        memset(data, 0x00, 64);
        data[0] = 0x02; data[1] = 0x4C; data[2] = 0x01; data[3] = 0x17;
    

    I tidied up the code a bit to provide better readability, here is the code I used which works! Hopefully others find this information useful.

    //*** DEPENDENCIES *************************************************************
    // QT
    #include <QCoreApplication>
    #include <QtCore/QDebug>
    // Others
    #include <libusb.h>
    #include <iostream>
    
    
    //*** VARIABLES ****************************************************************
    #define USB_VENDOR_ID      <VENDOR_ID_GOES_HERE>
    #define USB_PRODUCT_ID     <PRODUCT_ID_GOES_HERE>
    #define USB_ENDPOINT_OUT    0x04
    #define USB_ENDPOINT_IN     0x83
    
    #define INTERFACE_NO        0x01
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        libusb_device *dev;
        libusb_device_handle *dev_handle;
        libusb_context *ctx = NULL;
    
    
        //*** INITIALIZATION *******************************************************
        uint r = libusb_init(&ctx);
        // Check if initiated succesfully
        if ( r < 0 )                            { qDebug() << "Init error."; return 1; }
        libusb_set_debug(ctx, 4);
    
        dev_handle = libusb_open_device_with_vid_pid(ctx, USB_VENDOR_ID, USB_PRODUCT_ID);
    
        if (dev_handle == NULL) { qDebug() << "Could not open device."; return 1;}
        qDebug() << "Device opened succesfully!";
    
    
        // Check if kernel driver, detach
        if(libusb_kernel_driver_active(dev_handle, INTERFACE_NO) == 1) {
                qDebug() << "Kernel Driver Active";
                if(libusb_detach_kernel_driver(dev_handle, INTERFACE_NO) == 0) {
                   qDebug() << "Kernel Driver Detached";
            }
        }
    
        // Claim interface
        r = libusb_claim_interface(dev_handle, INTERFACE_NO);
        if ( r < 0 ) {
            qDebug() << "Could not claim interface.";
            return 1;
        }
        qDebug() << "Interface claimed.";
    
    
        //*** EXECUTION OF USB TRANSFERS *******************************************
    
        // Prepare command
        int actual_written;
        // Initialize array of 64 bytes.
        uint8_t *data = new uint8_t[64];
        memset(data, 0x00, 64);
        data[0] = 0x02; data[1] = 0x4C; data[2] = 0x01; data[3] = 0x17;
    
        qDebug() << "================= OUT ==============================";
        //*** ATTEMPT TO WRITE COMMAND *********************************************
        r = libusb_bulk_transfer(dev_handle,
                                 USB_ENDPOINT_OUT,
                                 data, 64,
                                 &actual_written,
                                 10000);
    
        qDebug() << "OUT status: " << libusb_strerror(static_cast<libusb_error>(r));
        if (r == 0 && actual_written == 64) {
            qDebug() << "Succesfully written!";
        } else {
            qDebug() << "||" << r << "||"<< actual_written << "||"
                     << "Could not write.";
        }
        qDebug() << "================== IN ===============================";
    
    
        //*** ATTEMPT TO READ FEEDBACK *********************************************
        // Initialize array of 64 bytes.
        uint8_t *feedback = new uint8_t[64];
        memset(feedback, 0x00, 64);
    
        int actual_received;
        r = libusb_bulk_transfer(
                    dev_handle,
                    USB_ENDPOINT_IN,
                    feedback,
                    64,
                    &actual_received,
                    0);
    
        qDebug() << "IN status: " << libusb_strerror(static_cast<libusb_error>(r));
       if(r == 0 && actual_received == 64) {
                qDebug("\nRetrieval successful!");
                qDebug("\nSent %d bytes with string: %s\n", actual_received, feedback);
        } else {
           qDebug() << actual_received << "||" <<feedback << "||"
                    << "Could not read incoming data. ||";
       }
    
        for( int m = 0; m < 64; m++)
        {
            fprintf(stderr, "[%d] - %02x\n", m, feedback[m]);
        }
    
        if (feedback[4] != 0x01) {
            qDebug() << "Unsuccesful offset adjustment.";
            return -1;
        }
    
        // Further code should go here.
    
        //*** FREEING USB **********************************************************
        // Releasing interface
        r = libusb_release_interface(dev_handle, INTERFACE_NO);
        if ( r < 0 )        { qDebug() << "Could not release interface."; return 1; }
        qDebug() << "Interface released.";
    
        libusb_close(dev_handle);
        libusb_exit(ctx);
        delete[] data;
        delete[] feedback;
    
    
        qDebug() << "End of main";
        return 0;
    }
    

    Thomas and David, thanks a lot!

    ~ Mark