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.
- You have to actually be able to open the device. (more on that later)
- 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.