From 03250fefce05e8963bd97fcde99012ce97314922 Mon Sep 17 00:00:00 2001 From: NightVsKnight Date: Thu, 20 Oct 2022 01:32:31 -0700 Subject: [PATCH] Add and use usb.ids to show more specific names of USB devices When I run `usbipd list` I see that most [but very interestingly not all] devices have generic not very useful names: ``` C:\path>usbipd.exe list Connected: BUSID VID:PID DEVICE STATE 1-1 048d:8297 USB Input Device Not shared 3-3 0557:2405 USB Input Device Not shared 6-2 0557:2405 USB Input Device Not shared 7-1 046d:085c c922 Pro Stream Webcam, C922 Pro Stream Webcam Not shared 7-2 1b1c:0a14 CORSAIR VOID PRO Wireless Gaming Headset, USB Input Device Not shared 8-1 1b1c:1b8b USB Input Device Shared 8-2 1209:2201 USB Serial Device (COM3), USB Input Device Shared ``` I cannot [easily] tell from this list what devices I want to bind to. Adding and using http://www.linux-usb.org/usb.ids now shows a [I feel] more useful list: ``` C:\path>usbipd.exe list Connected: BUSID VID:PID DEVICE STATE 1-1 048d:8297 Integrated Technology Express, Inc. IT8297 RGB LED Contro... Not shared 3-3 0557:2405 ATEN International Co., Ltd USB Input Device Not shared 6-2 0557:2405 ATEN International Co., Ltd USB Input Device Not shared 7-1 046d:085c Logitech, Inc. C922 Pro Stream Webcam Not shared 7-2 1b1c:0a14 Corsair CORSAIR VOID PRO Wireless Gaming Headset, USB Inp... Not shared 8-1 1b1c:1b8b Corsair USB Input Device Shared 8-2 1209:2201 Generic Dygma Shortcut Keyboard Shared ``` --- Usbipd/CommandHandlers.cs | 8 +- Usbipd/UsbIds.cs | 340 + Usbipd/Usbipd.csproj | 5 +- Usbipd/usb.ids | 25244 ++++++++++++++++++++++++++++++++++++ 4 files changed, 25594 insertions(+), 3 deletions(-) create mode 100644 Usbipd/UsbIds.cs create mode 100644 Usbipd/usb.ids diff --git a/Usbipd/CommandHandlers.cs b/Usbipd/CommandHandlers.cs index 1c3646d..10cc8e1 100644 --- a/Usbipd/CommandHandlers.cs +++ b/Usbipd/CommandHandlers.cs @@ -131,6 +131,8 @@ Task ICommandHandlers.List(IConsole console, CancellationToken cancell foreach (var device in allDevices.Where(d => d.BusId.HasValue).OrderBy(d => d.BusId.GetValueOrDefault())) { Debug.Assert(device.BusId.HasValue); + var hardwareId = device.HardwareId; + string description = UsbIds.GetProductName(hardwareId.Vid, hardwareId.Pid, device.Description); string state; if (device.IPAddress is not null) { @@ -147,7 +149,7 @@ Task ICommandHandlers.List(IConsole console, CancellationToken cancell // NOTE: Strictly speaking, both Bus and Port can be > 99. If you have one of those, you win a prize! console.Write($"{device.BusId.Value,-5} "); console.Write($"{device.HardwareId,-9} "); - console.WriteTruncated(device.Description, 60, true); + console.WriteTruncated(description, 60, true); console.WriteLine($" {state}"); } console.WriteLine(string.Empty); @@ -157,8 +159,10 @@ Task ICommandHandlers.List(IConsole console, CancellationToken cancell foreach (var device in allDevices.Where(d => !d.BusId.HasValue && d.Guid.HasValue).OrderBy(d => d.Guid.GetValueOrDefault())) { Debug.Assert(device.Guid.HasValue); + var hardwareId = device.HardwareId; + string description = UsbIds.GetProductName(hardwareId.Vid, hardwareId.Pid, device.Description); console.Write($"{device.Guid.Value,-36:D} "); - console.WriteTruncated(device.Description, 60, false); + console.WriteTruncated(description, 60, false); console.WriteLine(string.Empty); } console.WriteLine(string.Empty); diff --git a/Usbipd/UsbIds.cs b/Usbipd/UsbIds.cs new file mode 100644 index 0000000..32d58eb --- /dev/null +++ b/Usbipd/UsbIds.cs @@ -0,0 +1,340 @@ +//#define USBIDS_DBG_VERBOSE +#define USBIDS_STOPWATCH + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; + +namespace Usbipd +{ + /// + /// A C# port of https://github.com/cezanne/usbip-win/blob/master/userspace/lib/names.c (GPLv2+) + /// + class UsbIds + { + static UsbIds() + { + Load(); + } + + class UsbVendor + { + public UsbVendor(string name, uint vendorid) + { + this.name = name; + this.vendorid = vendorid; + } + + public string name { get; private set; } + public uint vendorid { get; private set; } + public Dictionary products = new Dictionary(); + } + + class UsbProduct + { + public uint vendorid { get; private set; } + public uint productid { get; private set; } + public string name { get; private set; } + + public UsbProduct(string name, uint vendorid, uint productid) + { + this.name = name; + this.vendorid = vendorid; + this.productid = productid; + } + } + + class UsbClass + { + public UsbClass(string name, uint classid) + { + this.name = name; + this.classid = classid; + } + + public string name { get; private set; } + public uint classid { get; private set; } + public Dictionary subclasses = new Dictionary(); + } + + class UsbSubclass + { + public UsbSubclass(string name, uint classid, uint subclassid) + { + this.name = name; + this.classid = classid; + this.subclassid = subclassid; + } + + public uint classid { get; private set; } + public uint subclassid { get; private set; } + public string name { get; private set; } + public Dictionary protocols = new Dictionary(); + } + + class UsbClassProtocol + { + public UsbClassProtocol(string name, uint classid, uint subclassid, uint protocolid) + { + this.name = name; + this.classid = classid; + this.subclassid = subclassid; + this.protocolid = protocolid; + } + + public uint classid { get; private set; } + public uint subclassid { get; private set; } + public uint protocolid { get; private set; } + public string name { get; private set; } + } + + private static Dictionary vendors = new Dictionary(); + private static Dictionary classes = new Dictionary(); + + private static bool isxdigit(char c) + { + return c >= '0' && c <= '9' || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'); + } + + private static bool isspace(char c) + { + return c == ' '; + } + + private static bool new_vendor(string name, uint vendorid) + { + return vendors.TryAdd(vendorid, new UsbVendor(name, vendorid)); + } + + public static string GetVendorName(uint vendorid) + { + vendors.TryGetValue(vendorid, out var usbvendor); + return usbvendor?.name ?? String.Empty; + } + + private static bool new_product(string name, uint vendorid, uint productid) + { + var usbvendor = vendors[vendorid]; + return usbvendor.products.TryAdd(productid, new UsbProduct(name, vendorid, productid)); + } + + public static string GetProductName(uint vendorid, uint productid, string defaultValue) + { + UsbProduct? usbproduct = null; + if (!vendors.TryGetValue(vendorid, out var usbvendor)) + { + return defaultValue; + } + if (!usbvendor.products.TryGetValue(productid, out usbproduct)) + { + return String.Format("{0} {1}", usbvendor.name, defaultValue); + } + return String.Format("{0} {1}", usbvendor.name, usbproduct.name); + } + + private static bool new_class(string name, uint classid) + { + return classes.TryAdd(classid, new UsbClass(name, classid)); + } + + private static bool new_subclass(string name, uint classid, uint subclassid) + { + var usbclass = classes[classid]; + return usbclass.subclasses.TryAdd(subclassid, new UsbSubclass(name, classid, subclassid)); + } + + private static bool new_protocol(string name, uint classid, uint subclassid, uint protocolid) + { + var usbclass = classes[classid]; + var usbsubclass = usbclass.subclasses[subclassid]; + return usbsubclass.protocols.TryAdd(protocolid, new UsbClassProtocol(name, classid, subclassid, protocolid)); + } + + private static void dbg(string format, params object?[] args) + { + Debug.WriteLine(String.Format(format, args)); + } + + private static uint Load() + { + dbg("+UsbDevice.Load()"); +#if USBIDS_STOPWATCH + var stopwatch = new System.Diagnostics.Stopwatch(); + stopwatch.Start(); +#endif + uint numlines = 0; + var filename = "usb.ids"; + if (File.Exists(filename)) + { + using (var f = File.OpenText(filename)) + { + int lastvendor = -1; + int lastclass = -1; + int lastsubclass = -1; + int lasthut = -1; + int lastlang = -1; + + int numvendors = 0; + int numproducts = 0; + int numclasses = 0; + int numsubclasses = 0; + int numprotocols = 0; + + var patternVendor = @"\A(?'id'[0-9a-f]{4}) +(?'name'.*)\Z"; + var patternProduct = @"\A\t(?'id'[0-9a-f]{4}) +(?'name'.*)\Z"; + var patternClass = @"\AC (?'id'[0-9a-f]{2}) +(?'name'.*)\Z"; + var patternSubclass = @"\A\t(?'id'[0-9a-f]{2}) +(?'name'.*)\Z"; + var patternProtocol = @"\A\t\t(?'id'[0-9a-f]{2}) +(?'name'.*)\Z"; + var patternHidUsage = @"\AHUT (?'id'[0-9a-f]{2}) +(?'name'.*)\Z"; + var patternLang = @"\AL (?'id'[0-9a-f]{4}) +(?'name'.*)\Z"; + Match match; + + string? buf; + while ((buf = f.ReadLine()) != null) + { + numlines++; + + if (buf.Length < 1 || buf[0] == '#') + { + lastvendor = lastclass = lastsubclass = lasthut = lastlang = -1; + continue; + } + + match = Regex.Match(buf, patternVendor); + if (match.Success) + { + var id = Convert.ToUInt32(match.Groups["id"].Value, 16); + var name = match.Groups["name"].Value; +#if USBIDS_DBG_VERBOSE + dbg("{0:D5}: vendor id={1:X4}, name=\"{2}\"", numlines, id, name); +#endif + if (new_vendor(name, id)) + { + ++numvendors; + } + else + { + dbg("Duplicate vendor spec at line {0:D5} vendor {1:X4} {2}", + numlines, id, name); + } + lastvendor = (int)id; + lasthut = lastlang = lastclass = lastsubclass = -1; + continue; + } + match = Regex.Match(buf, patternProduct); + if (match.Success && lastvendor != -1) + { + var id = Convert.ToUInt32(match.Groups["id"].Value, 16); + var name = match.Groups["name"].Value; +#if USBIDS_DBG_VERBOSE + dbg("{0:D5}: product id={1:X4}, name=\"{2}\"", numlines, id, name); +#endif + if (new_product(name, (uint)lastvendor, id)) + { + ++numproducts; + } + else + { + dbg("Duplicate product spec at line {0:D5} product {1:X4}:{2:X4} {3}", + numlines, lastvendor, id, name); + } + continue; + } + match = Regex.Match(buf, patternClass); + if (match.Success) + { + var id = Convert.ToUInt32(match.Groups["id"].Value, 16); + var name = match.Groups["name"].Value; +#if USBIDS_DBG_VERBOSE + dbg("{0:D5}: class id= {1:X2}, name=\"{2}\"", numlines, id, name); +#endif + if (new_class(name, id)) + { + ++numclasses; + } + else + { + dbg("Duplicate class spec at line {0:D5} class {1:X2} {2}", + numlines, id, name); + } + lastclass = (int)id; + lasthut = lastlang = lastvendor = lastsubclass = -1; + continue; + } + match = Regex.Match(buf, patternSubclass); + if (match.Success && lastclass != -1) + { + var id = Convert.ToUInt32(match.Groups["id"].Value, 16); + var name = match.Groups["name"].Value; +#if USBIDS_DBG_VERBOSE + dbg("{0:D5}: subclass id= {1:X2}, name=\"{2}\"", numlines, id, name); +#endif + if (new_subclass(name, (uint)lastclass, id)) + { + ++numsubclasses; + } + else + { + dbg("Duplicate subclass spec at line {0:D5} class {1:X2}:{2:X2} {3}", + numlines, lastclass, id, name); + } + lastsubclass = (int)id; + continue; + } + match = Regex.Match(buf, patternProtocol); + if (match.Success && lastclass != -1 && lastsubclass != -1) + { + var id = Convert.ToUInt32(match.Groups["id"].Value, 16); + var name = match.Groups["name"].Value; +#if USBIDS_DBG_VERBOSE + dbg("{0:D5}: protocol id= {1:X2}, name=\"{2}\"", numlines, id, name); +#endif + if (new_protocol(name, (uint)lastclass, (uint)lastsubclass, id)) + { + ++numprotocols; + } + else + { + dbg("Duplicate protocol spec at line {0:D5} class {1:X2}:{2:X2}:{3:X2} {4}", + numlines, lastclass, lastsubclass, id, name); + } + continue; + } + match = Regex.Match(buf, patternHidUsage); + if (match.Success) + { + /* + * set 1 as pseudo-id to indicate that the parser is + * in a `HUT' section. + */ + lasthut = 1; + lastlang = lastclass = lastvendor = lastsubclass = -1; + continue; + } + match = Regex.Match(buf, patternLang); + if (match.Success) + { + /* + * set 1 as pseudo-id to indicate that the parser is + * in a `L' section. + */ + lastlang = 1; + lasthut = lastclass = lastvendor = lastsubclass = -1; + continue; + } + } // while + dbg($"UsbDevice.Load() DONE: numvendors={numvendors}, numproducts={numproducts}, numclasses={numclasses}, numsubclasses={numsubclasses}, numprotocols={numprotocols}"); + } // using + } // exists +#if USBIDS_STOPWATCH + stopwatch.Stop(); + dbg($"-UsbDevice.Load(); numlines={numlines}; took {stopwatch.ElapsedMilliseconds} ms"); +#else + dbg($"-UsbDevice.Load(); numlines={numlines}"); +#endif + return numlines; + } + } +} diff --git a/Usbipd/Usbipd.csproj b/Usbipd/Usbipd.csproj index 18779e6..36c10fc 100644 --- a/Usbipd/Usbipd.csproj +++ b/Usbipd/Usbipd.csproj @@ -1,4 +1,4 @@ - +