Qt and USB – Pt. 1

By | May 3, 2018

Recently I’ve had some discussions on the qt-interest mailing list about Qt and USB and why it is not integrated in the package. Many of the kids there aren’t old enough to shave so they don’t remember this _exact_ same journey with serial ports. Couldn’t be done in a cross platform blah blah blah. Then there were various “unofficial” Qt based serial port classes. Then we had “almost” official stuff showing up in the Qt Playground. Eventually it all lead to the QSerialPort class. So much for that “can’t be done in a cross platform blah blah blah.”

Adding insult to injury, there already is a cross platform C library for generic USB access. It is called libusb. It is OpenSource with a list of platforms almost as long as the Qt list of supported platforms. So, instead creating a QUsb wrapper class for this library, each project rolls their own. No, they can never get rolled back in. The process of trying to submit a user contribution and push it all the way through to inclusion is akin to rolling naked twelve miles through broken glass. Takes about as long too. So, rather than getting something included and maintained as part of the library we will once again head down a broken and disjointed path. (Yes, I’m one of those people who was using all of those different, incompatible serial port classes and then had to rewrite everything once again when QSerialPort was officially adopted. They didn’t even keep the class name from the playground!)

I had to pack up my Raspberry Pi to clean off a table for an upcoming project. Will be quite some time before I can test this code on Raspberry Pi. I have been applying all updates, but, this machine is otherwise close to where we left off with the Raspberry Pi experiments.

As to the install of libusb development files, that took me two attempts.

You can visit the site and read through the doc, but, there is a parting of the ways between 0.1-4 and the 1.0 and forward stuff. Everyone wants to get the 1.0 and newer stuff for new development.

One must start out with baby steps. Given that mantra, my sample application doesn’t do much.

It’s just a main window with a text browser. The text browser gave me some place to dump what would otherwise be standard output. I’m even going to be honest here and tell you much of the code to retrieve that information was stolen/ported from examples found at dreamincode.net. One thing I want to do for the next baby step is find a method of translating VendorID and ProductID values into some human usable text. There appears to be at least one OpenSource effort, but I haven’t had time to drill down into that project. Been helping with corn planting season so you all can eat. There are no “amber waves of grain” until someone puts the seed in the ground so keep that in mind the next time you and your can’t-look-up-from-the-idiot-phone friends are bitching about the farm bill.

White 2-195 – My most favorite tractor ever – not me in image though

I know you are going to steal this code so let me say this.

You are not allowed to change the class name!

 

#ifndef LOGIKALUSB_H
#define LOGIKALUSB_H

#include
 #include

class LogikalUSB : public QObject
 {
 Q_OBJECT
 public:
 explicit LogikalUSB(QObject *parent = nullptr);
 ~LogikalUSB();

size_t deviceCount() { return dev_count;}
 void getDeviceReport(QString *txt);
 libusb_device *getDevice(unsigned int sub);
 libusb_device_descriptor *getDeviceDescriptor(int sub);

signals:

public slots:

private:
 libusb_device **devs = nullptr; // pointer to pointer of device used to retrieve list
 libusb_context *ctx = nullptr; // libusb session
 size_t dev_count = 0; // number of entries in list
 };

#endif // LOGIKALUSB_H

Like I said, this is baby steps. I am just creating a wrapper class around libusb.

 

#include "logikalusb.h"

#include 
#include 

LogikalUSB::LogikalUSB(QObject *parent) : QObject(parent)
{
    int r0_status;              // return values


    r0_status = libusb_init(&ctx);
    if (r0_status < 0)
    {
        // TODO:: need an error log util to call
        std::cout << "Init Error " << r0_status << std::endl;
        return;
    }

    libusb_set_debug(ctx, 3);   // verbosity level

    int x = libusb_get_device_list(ctx, &devs);
    if (x < 0)
    {
        std::cout << "Get Device Error " << x << std::endl;
        dev_count = 0;
    }
    else
    {
        dev_count = (unsigned int) x;
    }

}

LogikalUSB::~LogikalUSB()
{
    if (devs)
    {
        libusb_free_device_list(devs, 1);   // free the list, unref the devices
    }

    if (ctx)
    {
        libusb_exit(ctx);
    }
}

libusb_device *LogikalUSB::getDevice(unsigned int sub)
{
    libusb_device *retVal = nullptr;

    if (sub < dev_count)
    {
        retVal = devs[sub];
    }

    return retVal;
}

void LogikalUSB::getDeviceReport(QString *txt)
{
    QTextStream out(txt);

    for (size_t y=0; y < dev_count; y++)
    {
        libusb_device *dev = devs[y];
        libusb_device_descriptor desc;
        int r0_status = libusb_get_device_descriptor(dev, &desc);
        if (r0_status < 0)
        {
            std::cout << "Get Device Descriptor Error " << r0_status << std::endl;
            continue;
        }

        out << "Number of possible configurations: " << (int)desc.bNumConfigurations << "   ";
        out << "Device Class: " << (int)desc.bDeviceClass<<"  ";
        out << "VendorID: " << desc.idVendor << "   ";
        out << "ProductID: " << desc.idProduct << endl;

        libusb_config_descriptor *config;
        libusb_get_config_descriptor(dev, 0, &config);

        out << "Interfaces: " << (int)config->bNumInterfaces << "   ";

        const libusb_interface *inter;
        const libusb_interface_descriptor *interdesc;
        const libusb_endpoint_descriptor *epdesc;

        for(int i=0; i < (int)config->bNumInterfaces; i++)
        {
            inter = &config->interface[i];
            for(int j=0; jnum_altsetting; j++)
            {
                interdesc = &inter->altsetting[j];
                out << "Interface Number: " << (int)interdesc->bInterfaceNumber << " | ";
                out << "Number of endpoints: "<< (int)interdesc->bNumEndpoints << " | ";

                for(int k=0; k<(int)interdesc->bNumEndpoints; k++)
                {
                    epdesc = &interdesc->endpoint[k];
                    out << "Descriptor Type: " << (int)epdesc->bDescriptorType << " | ";
                    out << "EP Address: " << (int)epdesc->bEndpointAddress << " | ";
                }
            }
        }

        out << endl << endl << endl;
    }
}

 

We had to have a class destructor here because of the dynamic allocation. One of my big regrets here is the API is obviously set up for pre-C11 standards, making us keep a count and utilize older style for() loops.

The biggest benefit this USB library brings to the table is “user mode.” I just ran this as myself. I didn’t create any udev rules or have to jump through any other hoops as of yet. This was a major pain in the early iterations for the various serial port classes. You almost always had to be root. Truth be told, I am just reading right now. I haven’t chosen two ports to use for direct communication between two instances of this application. Ultimately  would like to find a place to set up my Raspberry Pi with a long enough cable, cross compiling for target, then running.

That reminds me. I need to email a client of mine. That is the last place I saw my 20 or so foot long USB cable.

I will work on this project as long as rain and time allow. Very soon I will be starting a new contract so we are talking hours to devote to this, not weeks or months. I really do want to get these ID values translating to human consumable strings. That will make the evolution of this experiment much cleaner. If we can simply and easily get real names we can have a pick list.

 

Category: Information Technology Raspberry Pi Tags: , , , , ,

About seasoned_geek

Roland Hughes started his IT career in the early 1980s. He quickly became a consultant and president of Logikal Solutions, a software consulting firm specializing in OpenVMS application and C++/Qt touchscreen/embedded Linux development. Early in his career he became involved in what is now called cross platform development. Given the dearth of useful books on the subject he ventured into the world of professional author in 1995 writing the first of the "Zinc It!" book series for John Gordon Burke Publisher, Inc. A decade later he released a massive (nearly 800 pages) tome "The Minimum You Need to Know to Be an OpenVMS Application Developer" which tried to encapsulate the essential skills gained over what was nearly a 20 year career at that point. From there "The Minimum You Need to Know" book series was born. Three years later he wrote his first novel "Infinite Exposure" which got much notice from people involved in the banking and financial security worlds. Some of the attacks predicted in that book have since come to pass. While it was not originally intended to be a trilogy, it became the first book of "The Earth That Was" trilogy: Infinite Exposure Lesedi - The Greatest Lie Ever Told John Smith - Last Known Survivor of the Microsoft Wars When he is not consulting Roland Hughes posts about technology and sometimes politics on his blog. He also has regularly scheduled Sunday posts appearing on the Interesting Authors blog.