Posted inExperience / Information Technology / Thank You Sir May I Have Another

Qt and USB – Pt. 2

Today’s post will be the first of at least two posts about identifying your USB device. I started by adding the following code.

        out << "ProductID: " << desc.idProduct << endl;

        r0_status = libusb_open(dev, &hHandle);
        if (r0_status != LIBUSB_SUCCESS)
        {
            out << "Error " << r0_status << " opening USB device. Error text: " << libusb_error_name(r0_status) << endl;
        }
        else
        {
            unsigned char serialNumber[255] = { };
            unsigned char vendorName[1024] = { };
            unsigned char productName[1024] = { };

            int textSize = libusb_get_string_descriptor_ascii(hHandle, desc.iSerialNumber, serialNumber, sizeof(serialNumber));
            textSize = libusb_get_string_descriptor_ascii(hHandle, desc.idVendor, vendorName, sizeof(vendorName));
            textSize = libusb_get_string_descriptor_ascii(hHandle, desc.idProduct, productName, sizeof(productName));

            out << "Vendor: " << (char *)vendorName << "  Product: " << (char *)productName << "  Serial Number: " << (char *)serialNumber << endl;
        }

        libusb_config_descriptor *config;

I left the first and last line in place so you would have some idea where in the getDeviceReport() method this code was inserted. Yes, libusb does have methods for obtaining this information but it comes with two major drawbacks.

  1. You have to actually be able to open the device. (more on that later)
  2. It expects the device to be able to tell you.

My rant about security in Linux will come later in this post. First we need to look at two different sets of output. First, from when this program is run within QtCreator as just little ole me.

 

Current device count: 9

Number of possible configurations: 1   Device Class: 9  VendorID: 32903   ProductID: 36
Error -3 opening USB device. Error text: LIBUSB_ERROR_ACCESS
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 9  VendorID: 7531   ProductID: 2
Error -3 opening USB device. Error text: LIBUSB_ERROR_ACCESS
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 0  VendorID: 2362   ProductID: 9488
Error -3 opening USB device. Error text: LIBUSB_ERROR_ACCESS
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 0  VendorID: 1500   ProductID: 43037
Error -3 opening USB device. Error text: LIBUSB_ERROR_ACCESS
Interfaces: 1   Interface Number: 0 | Number of endpoints: 2 | Descriptor Type: 5 | EP Address: 129 | Descriptor Type: 5 | EP Address: 2 | 


Number of possible configurations: 1   Device Class: 9  VendorID: 32903   ProductID: 36
Error -3 opening USB device. Error text: LIBUSB_ERROR_ACCESS
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 9  VendorID: 7531   ProductID: 2
Error -3 opening USB device. Error text: LIBUSB_ERROR_ACCESS
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 9  VendorID: 7531   ProductID: 3
Error -3 opening USB device. Error text: LIBUSB_ERROR_ACCESS
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 0  VendorID: 1309   ProductID: 2
Error -3 opening USB device. Error text: LIBUSB_ERROR_ACCESS
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 9  VendorID: 7531   ProductID: 2
Error -3 opening USB device. Error text: LIBUSB_ERROR_ACCESS
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 

As little ole me, I don’t have access. So, let us look at the output when I sudo this executable from the command line.

Current device count: 9

Number of possible configurations: 1   Device Class: 9  VendorID: 32903   ProductID: 36
Vendor:   Product:   Serial Number: 
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 9  VendorID: 7531   ProductID: 2
Vendor:   Product: EHCI Host Controller  Serial Number: 0000:00:1d.0
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 0  VendorID: 2362   ProductID: 9488
Vendor:   Product:   Serial Number: 
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 0  VendorID: 1500   ProductID: 43037
Vendor:   Product:   Serial Number: AA8BF02FAUF8PRH6
Interfaces: 1   Interface Number: 0 | Number of endpoints: 2 | Descriptor Type: 5 | EP Address: 129 | Descriptor Type: 5 | EP Address: 2 | 


Number of possible configurations: 1   Device Class: 9  VendorID: 32903   ProductID: 36
Vendor:   Product:   Serial Number: 
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 9  VendorID: 7531   ProductID: 2
Vendor:   Product: EHCI Host Controller  Serial Number: 0000:00:1a.0
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 9  VendorID: 7531   ProductID: 3
Vendor:   Product: Linux 4.13.0-41-generic xhci-hcd  Serial Number: 0000:00:14.0
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 0  VendorID: 1309   ProductID: 2
Vendor:   Product: Back-UPS RS 1000G FW:869.L3 .D USB FW:L3   Serial Number: 3B1241X11465  
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 


Number of possible configurations: 1   Device Class: 9  VendorID: 7531   ProductID: 2
Vendor:   Product: xHCI Host Controller  Serial Number: 0000:00:14.0
Interfaces: 1   Interface Number: 0 | Number of endpoints: 1 | Descriptor Type: 5 | EP Address: 129 | 

It managed to find my Back-UPS, not who makes it, but, that device was at least polite enough to say what it was. Sadly, I also had a USB mouse and a Lexar thumb drive plugged in. You don’t have to take my word for it.

roland@roland-I5-HP-Compaq-8300-Elite-SFF-PC:~$ lsusb
Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 004: ID 093a:2510 Pixart Imaging, Inc. Optical Mouse
Bus 001 Device 003: ID 05dc:a81d Lexar Media, Inc. 
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 002: ID 051d:0002 American Power Conversion Uninterruptible Power Supply
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
roland@roland-I5-HP-Compaq-8300-Elite-SFF-PC:~$ 

Now, lsusb ultimately uses the usbutils package which provides names_vendor(), names_product() and a host of other handy functions. Sadly this is not cross platform. I really wish our cross platform usb library (libusb) would import one tiny piece of usbutils. The list. Thankfully, it exists on-line. We can kind-of-sort-of make a cross platform Qt friendly method of using this information. When running on Linux there is a 99% chance the file usb.ids is local to our machine. We can also add the fallback capability of reaching out to the Web and pulling the most current version down . . . assuming we have an Internet connection.

I want to cut and paste a tiny snippet of this file so you have a frame of reference for our ongoing discussion.

 

# Vendors, devices and interfaces. Please keep sorted.

# Syntax:
# vendor  vendor_name
#	device  device_name				<-- single tab
#		interface  interface_name		<-- two tabs

0001  Fry's Electronics
	7778  Counterfeit flash drive [Kingston]
0002  Ingram
0003  Club Mac
0004  Nebraska Furniture Mart
0011  Unknown
	7788  counterfeit flash drive
0053  Planex
	5301  GW-US54ZGL 802.11bg
0078  Microntek
	0006  Joystick
0079  DragonRise Inc.
	0006  PC TWIN SHOCK Gamepad
	0011  Gamepad
	1800  Mayflash Wii U Pro Game Controller Adapter [DirectInput]
	181b  Venom Arcade Joystick
	1843  Mayflash GameCube Controller Adapter
	1844  Mayflash GameCube Controller

 

This is a tab and space delimited text file. When we directly pull it down from the Web we will most likely also get a wee bit of HTML. What really turned me off about usbutils is looking at some of the code to fetch the names. This entire file gets parsed into C structures which use a hash for access. I understand, they were working with C and took the minimalist approach, but, what we have here is known as a control break report. Our job is to parse the report in order to get the data in a usable format. Had the usbutils routines parsed this into a pair of indexed files either in memory or written to /tmp, I would have been more inclined to replicate their works.

If you wish to read up on indexed files with multi-typed records and segmented keys, feel free to find a copy of this book and read it. You may wish to believe “Oh, I only write Qt code on Linux/Windows, I don’t need to know any of that stuff.” Well young grasshopper, you really do need to know that stuff so you can understand why much of the stuff done in this world is shit.

Take a good look at the output again.

The text file master approach got more vendor names correct, but, take a good look at the UPS line. It didn’t get the model correct. Any “list” approach is going to be a “much of the time” solution. What I mean by that is “much of the time it will have something you can use, just don’t expect it to be too useful.” Even if a list were to contain absolutely perfect information for every device on it, there is still going to be a lag between a new device coming out and its information getting on the list.

Relying on a vendor to actually store all needed information in the device __and__ respond to a query with said information is, well, on a good day, fool-hardy. Electronics are in a race to the bottom. People buy the cheapest piece of shit and expect it to work like the high end stuff. Everyone is cutting corners and taking short cuts. I’m not surprised this generic mouse I got from Staples didn’t bother to identify itself, but, Lexar is a really big name in thumb drives. Usually their drives come pre-loaded with tons of crap software so a person has to know how to format a thumb drive before they can use it. I was shocked to see it didn’t bother to answer the query.

So, as time allows, the next step is to come up with our own Qt based class to obtain vendor and device name information. While I would like it to be stand alone, I haven’t yet noodled on it enough to make the function work. The ideal functionality would be to have the class use both the list _and_ the query. That means a coupling though.

Kids today would also try to come up with a QObject based class to hold both vendorID and ProductID with comparison methods so it could be used as the key for a QMap. The proper method of handling this kind of data is with a database. Either in memory or in a temp directory. A simple SQLite thing.

Hopefully you spend some time looking at that list. It contains many thing one could bump into. To start with it isn’t a single list.

 

# Syntax:
# C class  class_name
#	subclass  subclass_name			<-- single tab
#		protocol  protocol_name		<-- two tabs

C 00  (Defined at Interface level)
C 01  Audio
	01  Control Device
	02  Streaming
	03  MIDI Streaming
C 02  Communications
	01  Direct Line
	02  Abstract (modem)
		00  None
		01  AT-commands (v.25ter)
		02  AT-commands (PCCA101)
		03  AT-commands (PCCA101 + wakeup)
		04  AT-commands (GSM)
		05  AT-commands (3G)
		06  AT-commands (CDMA)
		fe  Defined by command set descriptor
		ff  Vendor Specific (MSFT RNDIS?)

...

# List of Audio Class Terminal Types

# Syntax:
# AT terminal_type  terminal_type_name

AT 0100  USB Undefined
AT 0101  USB Streaming
AT 01ff  USB Vendor Specific
AT 0200  Input Undefined
AT 0201  Microphone
AT 0202  Desktop Microphone

 

It goes on and on.

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.