Skip to content

Instantly share code, notes, and snippets.

@gvanem
Last active December 5, 2017 11:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gvanem/8928b4262d158ff46d3d to your computer and use it in GitHub Desktop.
Save gvanem/8928b4262d158ff46d3d to your computer and use it in GitHub Desktop.
USB enumerator using libusb and the Python-script make-usb.py. This script generates usb_vendors.c.
#!/usr/bin/env python
#
# make-usb.py : a modified Python-script from Wireshark.
# http://www.wireshark.org/
# Creates the examples/usb_vendors.c file containing vendor and
# product ids. It use the databases at
# http://www.linux-usb.org/usb.ids
#
# Changed by G. Vanem <gvanem@yahoo.no> 2012.
#
import sys, re
if sys.version_info[0] < 3:
import urllib
else:
import urllib.request, urllib.error, urllib.parse
MODE_IDLE = 0
MODE_VENDOR_PRODUCT = 1
mode = MODE_IDLE
URL = 'http://www.linux-usb.org/usb.ids'
#
# Grab the URL from linux-usb.org
#
if sys.version_info[0] < 3:
response = urllib.urlopen (URL)
else:
response = urllib.request.urlopen (URL)
lines = response.read().splitlines()
vendors = dict()
products = dict()
date = None
for line in lines:
line = line.rstrip()
#
# grab the first "# Date: " in header and print as a C-comment and value.
#
if date == None and line.startswith("# Date: "):
date = line[len("# Date: "):].strip()
if line == "# Vendors, devices and interfaces. Please keep sorted.":
mode = MODE_VENDOR_PRODUCT
continue
elif line == "# List of known device classes, subclasses and protocols":
mode = MODE_IDLE
continue
if mode == MODE_VENDOR_PRODUCT:
if re.match ("^[0-9a-f]{4}", line):
last_vendor = line[:4]
vendors [last_vendor] = re.sub ("\"", "\\\"", re.sub("\?+", "?", repr(line[4:].strip())[1:-1].replace("\\", "\\\\")))
elif re.match ("^\t[0-9a-f]{4}", line):
line = line.strip()
product = "%s%s" % (last_vendor, line[:4])
products [product] = re.sub ("\"", "\\\"", re.sub("\?+", "?", repr(line[4:].strip())[1:-1].replace("\\", "\\\\")))
print (r"""/*
* USB vendor id and product ids
* This file was generated by running python ./examples/make-usb.py
* Don't change it directly.
*
* Copyright 2012, Michal Labedzki for Tieto Corporation
*
* Other values imported from libghoto2/camlibs/ptp2/library.c, music-players.h
*
* Copyright (C) 2001-2005 Mariusz Woloszyn <emsi@ipartners.pl>
* Copyright (C) 2003-2013 Marcus Meissner <marcus@jet.franken.de>
* Copyright (C) 2005 Hubert Figuiere <hfiguiere@teaser.fr>
* Copyright (C) 2009 Axel Waggershauser <awagger@web.de>
* Copyright (C) 2005-2007 Richard A. Low <richard@wentnet.com>
* Copyright (C) 2005-2012 Linus Walleij <triad@df.lth.se>
* Copyright (C) 2007 Ted Bullock
* Copyright (C) 2012 Sony Mobile Communications AB
*
* $Id: make-usb.py 51113 2013-08-02 02:09:11Z gerald $
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include "usb_enum.h"
/* struct value_string {
* unsigned long val; << values below; hi-word is VID, lo-word is PID.
* const char *string; << the description.
* };
*/
""")
print (r"""
/*
* usb.ids obtained from %s at date:
*/
const char *usb_ids_date = """ % URL),
if date:
print ("\"%s\";\n" % date)
else:
print ("NULL;\n")
num_vendors = 0
print ("const struct value_string usb_vendors_vals[] = {")
for v in sorted(vendors):
print (" { 0x%s, \"%s\" }," % (v, vendors[v]))
num_vendors += 1
print (" { 0, NULL }\n};\n")
num_products = 0
print ("const struct value_string usb_products_vals[] = {")
for p in sorted(products):
print (" { 0x%s, \"%s\" }," % (p, products[p]))
num_products += 1
print (" { 0, NULL }\n};\n")
print ("unsigned num_usb_products = %u;" % num_products)
print ("unsigned num_usb_vendors = %u;" % num_vendors)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <getopt.h>
#include "libusb.h"
#include "usb_enum.h"
#ifdef _MSC_VER
#define putenv _putenv
#if (_MSC_VER < 1900)
#define snprintf _snprintf
#endif
#endif
#ifdef USE_HID_API
#define _DEBUG
#include "../windows/hid.c" /* Relative to '$(HID_API_ROOT)/hidapi' */
void print_hid_info (const char *indent, uint16_t vid, uint16_t pid);
#endif
/*
* At high verbose-level, we must flush both stdout and stderr
* to get a synced output.
*/
static int flush_each_line = 0;
#define FLUSH() do { \
if (flush_each_line) { \
fflush (stdout); \
fflush (stderr); \
} \
} while (0)
void Fatal (const char *func, int err)
{
FLUSH();
if (err)
fprintf (stderr, "%s(): %s\n", func, libusb_strerror(err));
else fprintf (stderr, "%s\n", func);
exit (1);
}
/*
* The generated arrays 'usb_vendors_vals[]' and 'usb_products_vals[]' are
* sorted on 'val'. So we break the loops below early when there can be
* no match.
*/
const char *find_vendor_name (uint16_t vendor)
{
const struct value_string *list = usb_vendors_vals;
for ( ; list->string && vendor >= list->val; list++)
{
if (list->val == vendor)
return (list->string);
}
return (NULL);
}
const char *find_product_name (uint16_t vendor, uint16_t product)
{
const struct value_string *list = usb_products_vals;
uint32_t val = (vendor << 16) + product;
for ( ; list->string && vendor >= (list->val >> 16); list++)
{
if (list->val == val)
return (list->string);
}
return (NULL);
}
int device_compare (const struct libusb_device_descriptor *d1,
const struct libusb_device_descriptor *d2)
{
if (d1->idVendor != d2->idVendor)
return (int) (d1->idVendor - d2->idVendor);
return (int) (d1->idProduct - d2->idProduct);
}
int sort_list (struct libusb_device_descriptor *dd, size_t num)
{
typedef int (*CmpFunc) (const void *, const void *);
qsort (dd, num, sizeof(*dd), (CmpFunc)device_compare);
return (num);
}
void print_libusb_details (const char *indent,
libusb_context *ctx,
const struct libusb_device_descriptor *dd)
{
libusb_device_handle *handle;
libusb_device *dev;
uint8_t bus, port_path[8];
int i, r;
char string[128];
uint8_t string_index[3]; /* indexes of the string descriptors */
const char *speed_name[5] = { "Unknown",
"1.5 Mbit/s (USB LowSpeed)",
"12 Mbit/s (USB FullSpeed)",
"480 Mbit/s (USB HighSpeed)",
"5000 Mbit/s (USB SuperSpeed)"
};
uint16_t vid = dd->idVendor;
uint16_t pid = dd->idProduct;
printf ("%sOpening device %04X:%04X...%c", indent, vid, pid, flush_each_line ? '\n' :' ');
FLUSH();
handle = libusb_open_device_with_vid_pid (ctx, vid, pid);
FLUSH();
if (handle == NULL)
{
puts (" Failed.\n");
return;
}
dev = libusb_get_device (handle);
bus = libusb_get_bus_number (dev);
r = libusb_get_port_numbers (dev, port_path, sizeof(port_path));
if (r > 0)
{
FLUSH();
printf ("\n%sDevice properties:\n", indent);
printf ("%s bus number: %d\n", indent, bus);
printf ("%s port path: %d", indent, port_path[0]);
for (i = 1; i < r; i++)
printf ("->%d", port_path[i]);
printf (" (from root hub)\n");
}
else if (r < 0)
{
printf ("\n%sError:%s\n", indent, libusb_strerror((enum libusb_error)r));
}
FLUSH();
r = libusb_get_device_speed (dev);
FLUSH();
if (r < 0 || r > 4)
r = 0;
printf ("%s speed: %s\n", indent, speed_name[r]);
printf ("\n%sReading string descriptors:\n", indent);
for (i = 0; i < 3; i++)
{
if (string_index[i] == 0)
continue;
FLUSH();
if (libusb_get_string_descriptor_ascii(handle, string_index[i], (unsigned char*)string, sizeof(string)) >= 0)
{
FLUSH();
printf ("%s String (0x%02X): \"%s\"\n", indent, string_index[i], string);
}
}
FLUSH();
libusb_close (handle);
puts ("");
FLUSH();
}
void print_libusb_descriptor (const char *indent, const struct libusb_device_descriptor *dd)
{
printf ("%sDescr-type:%02Xh, BCD:%02Xh, Class:%02Xh, Sub-class:%02Xh, Proto:%02Xh\n",
indent,
dd->bDescriptorType, dd->bcdUSB, dd->bDeviceClass,
dd->bDeviceSubClass, dd->bDeviceProtocol);
FLUSH();
}
void print_vendor_prod_names (int index, int vendor, int product, uint8_t bus, uint8_t addr)
{
const char *vendor_name, *product_name;
FLUSH();
printf ("%2d: VID:%04X PID:%04X (BUS:%02Xh, ADDR:%02Xh) -> ",
index, vendor, product, bus, addr);
vendor_name = find_vendor_name (vendor);
product_name = find_product_name (vendor, product);
printf ("%-30s %s\n",
vendor_name ? vendor_name : "<not found>",
product_name ? product_name : "<not found>");
FLUSH();
}
const char *heading = " # Vendor Product (BUS Address) Vendor name "
"Product name\n"
"------------------------------------------------------"
"---------------------------------------------------------";
#if 0
struct hid_device_info {
/** Platform-specific device path */
char *path;
/** Device Vendor ID */
unsigned short vendor_id;
/** Device Product ID */
unsigned short product_id;
/** Serial Number */
wchar_t *serial_number;
/** Device Release Number in binary-coded decimal,
also known as Device Version Number */
unsigned short release_number;
/** Manufacturer String */
wchar_t *manufacturer_string;
/** Product string */
wchar_t *product_string;
/** Usage Page for this Device/Interface
(Windows/Mac only). */
unsigned short usage_page;
/** Usage for this Device/Interface
(Windows/Mac only).*/
unsigned short usage;
/** The USB interface which this logical device
represents. Valid on both Linux implementations
in all cases, and valid on the Windows implementation
only if the device contains more than one interface. */
int interface_number;
/** Pointer to the next device */
struct hid_device_info *next;
};
#endif
void list_devices_using_hidapi (int verbose)
{
#ifndef USE_HID_API
Fatal ("'USE_HID_API' not compiled into program.\n", 0);
#else
const struct hid_device_info *cur, *hid_info = hid_enumerate (0, 0);
int i;
if (!hid_info)
{
printf ("hid_init() failed\n");
FLUSH();
}
/* to-do: sort this linked list on VID/PID like in sort_list().
*/
puts (heading);
for (i = 0, cur = hid_info; cur; cur = cur->next, i++)
{
print_vendor_prod_names (i, cur->vendor_id, cur->product_id, 0, 0);
if (verbose >= 1)
print_hid_info (" ", cur->vendor_id, cur->product_id);
#if 0
if (verbose >= 2)
{
print_hid_info ("path: %s, S/N: %s", cur->path, cur->serial_number);
}
#endif
}
hid_exit();
#endif
}
void list_devices_using_libusb (int verbose)
{
libusb_context *ctx;
libusb_device **list;
struct libusb_device_descriptor *dd_list;
int i, j, rc, count;
rc = libusb_init (&ctx);
if (rc != 0)
Fatal ("libusb_init", rc);
libusb_set_debug (NULL, verbose); /* Cannot fail */
rc = libusb_get_device_list (ctx, &list);
if (rc < 0)
{
libusb_exit (ctx);
Fatal ("libusb_get_device_list", rc);
}
count = rc;
dd_list = alloca (count * sizeof(*dd_list));
for (i = j = 0; i < count; i++)
{
if (libusb_get_device_descriptor(list[i], dd_list+j) != 0)
printf ("libusb_get_device_descriptor (list[%d]) failed!!\n", i);
else j++;
FLUSH();
}
count = sort_list (dd_list, j);
puts (heading);
FLUSH();
for (i = 0; i < count; i++)
{
struct libusb_device_descriptor *dd = dd_list + i;
int bus = libusb_get_bus_number ((libusb_device*)dd);
int addr = libusb_get_device_address ((libusb_device*)dd);
print_vendor_prod_names (i, dd->idVendor, dd->idProduct, bus, addr);
if (verbose >= 2)
print_libusb_descriptor (" ", dd);
if (verbose >= 3)
print_libusb_details (" ", ctx, dd);
}
libusb_free_device_list (list, 1);
libusb_exit (ctx);
}
void list_devices (int verbose, BOOL use_HID_API)
{
if (verbose >= 4)
{
putenv ("LIBUSB_DEBUG=4");
flush_each_line = 1;
}
if (use_HID_API)
list_devices_using_hidapi (verbose);
else list_devices_using_libusb (verbose);
}
void show_help (void)
{
puts ("usb_enum Usage:\n"
" -v: Increase verbose mode. E.g. use '-vvv' for level 3 verbosity.\n"
" -H: Check only HID [1] devices using HID-API [2]\n"
" Otherwise use libusb [3]");
puts (" Footnotes:\n"
" [1] https://en.wikipedia.org/wiki/USB_human_interface_device_class\n"
" [2] https://github.com/signal11/hidapi\n"
" [3] http://libusb.info/");
exit (0);
}
int main (int argc, char **argv)
{
int opt, verbose = 0, use_hid_api = 0;
while ((opt = getopt(argc, argv, "vhH")) != EOF)
{
if (opt == 'v')
verbose++;
else if (opt == 'h')
show_help();
else if (opt == 'H')
use_hid_api = 1;
}
if (usb_ids_date)
printf ("Using USB VID/PIDs obtained from http://www.linux-usb.org/usb.ids at %s.\n\n",
usb_ids_date);
list_devices (verbose, use_hid_api);
return (0);
}
#ifdef USE_HID_API
/*
* Adapted from:
* https://raw.githubusercontent.com/signal11/hidapi/master/hidtest/hidtest.cpp
*
* But HidAPI seems buggy...
*/
#define MAX_STR 255
#define PRINTF(...) do { \
printf (indent); \
printf (__VA_ARGS__); \
FLUSH(); \
} while (0)
void print_hid_info (const char *indent, uint16_t vid, uint16_t pid)
{
int res, i;
uint8_t buf [256];
wchar_t wstr [MAX_STR];
hid_device *handle;
printf ("%sInfo from HidAPI:\n", indent);
/* Open the device using the VID, PID,
* and optionally the Serial number.
*/
handle = hid_open (vid, pid, NULL);
if (!handle)
{
PRINTF ("Unable to open device. VID:%04X PID:%04X\n\n", vid, pid);
return;
}
/* Read the Manufacturer String.
*/
wstr[0] = 0x0000;
res = hid_get_manufacturer_string (handle, wstr, MAX_STR);
if (res < 0)
PRINTF ("Unable to read manufacturer string: %ls\n", hid_error(handle));
PRINTF ("Manufacturer String: %ls\n", wstr);
/* Read the Product String.
*/
wstr[0] = 0x0000;
res = hid_get_product_string (handle, wstr, MAX_STR);
if (res < 0)
PRINTF ("Unable to read product string\n: %ls\n", hid_error(handle));
PRINTF ("Product String: %ls\n", wstr);
/* Read the Serial Number String.
*/
wstr[0] = 0x0000;
res = hid_get_serial_number_string (handle, wstr, MAX_STR);
if (res < 0)
PRINTF ("Unable to read serial number string: %ls\n", hid_error(handle));
PRINTF ("Serial Number String: (%d) %ls\n", wstr[0], wstr);
/* Read Indexed String 1.
*/
wstr[0] = 0x0000;
res = hid_get_indexed_string(handle, 1, wstr, MAX_STR);
if (res < 0)
PRINTF ("Unable to read indexed string 1: %ls\n", hid_error(handle));
PRINTF ("Indexed String 1: %ls\n", wstr);
/* Set the hid_read() function to be non-blocking.
*/
hid_set_nonblocking (handle, 1);
/* Try to read from the device. There should be no
* data here, but execution should not block.
*/
res = hid_read (handle, buf, 17);
/* Send a Feature Report to the device.
*/
buf[0] = 0x2;
buf[1] = 0xA0;
buf[2] = 0x0A;
buf[3] = 0x00;
buf[4] = 0x00;
res = hid_send_feature_report (handle, buf, 17);
if (res < 0)
PRINTF ("Unable to send a feature report: %ls\n", hid_error(handle));
memset (buf, 0, sizeof(buf));
/* Read a Feature Report from the device.
*/
buf[0] = 0x2;
res = hid_get_feature_report (handle, buf, sizeof(buf));
if (res < 0)
{
PRINTF ("Unable to get a feature report.\n%ls\n", hid_error(handle));
}
else
{
/* Print out the returned buffer.
*/
PRINTF ("Feature Report\n ");
for (i = 0; i < res; i++)
printf ("%02hhx ", buf[i]);
printf ("\n");
}
memset (buf,0,sizeof(buf));
/* Toggle LED (cmd 0x80). The first byte is the report number (0x1).
*/
buf[0] = 0x1;
buf[1] = 0x80;
res = hid_write(handle, buf, 17);
if (res < 0)
PRINTF ("Unable to write(): %ls\n", hid_error(handle));
/* Request state (cmd 0x81). The first byte is the report number (0x1).
*/
buf[0] = 0x1;
buf[1] = 0x81;
hid_write (handle, buf, 17);
if (res < 0)
PRINTF ("Unable to write(): %ls\n", hid_error(handle));
/* Read requested state. hid_read() has been set to be
* non-blocking by the call to hid_set_nonblocking() above.
* This loop demonstrates the non-blocking nature of hid_read().
*/
res = 0;
while (res == 0)
{
res = hid_read (handle, buf, sizeof(buf));
if (res == 0)
PRINTF ("waiting...\n");
if (res < 0)
printf ("Unable to read()\n");
Sleep (500);
}
PRINTF ("Data read:\n ");
/* Print out the returned buffer.
*/
for (i = 0; i < res; i++)
PRINTF ("%02hhx ", buf[i]);
printf ("\n");
hid_close (handle);
}
#endif
gist.github.com doesn't seem to allow .exe-files to be added. So the usb_enum.exe is here:
http://www.watt-32.net/USB/usb_enum.exe
Along with a Makefile:
http://www.watt-32.net/USB/Makefile.Windows
to show how it's created.
#ifndef _USB_ENUM_H
#define _USB_ENUM_H
struct value_string {
unsigned long val;
const char *string;
};
/* These arrays are generated by the examples/make-usb.py script.
*/
extern const struct value_string usb_vendors_vals[];
extern const struct value_string usb_products_vals[];
extern unsigned num_usb_products;
extern unsigned num_usb_vendors;
extern const char *usb_ids_date;
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment