From 4664785eba751660c41772885ab01fdf87f3f104 Mon Sep 17 00:00:00 2001 From: MaIII Date: Tue, 26 May 2026 08:10:14 +0700 Subject: [PATCH] enumerator: normalize Linux USB VID/PID to uppercase PortDetails.VID/PID were reported in different cases depending on the platform: the darwin backend formats them with %04X (usb_darwin.go) and the Windows backend extracts them from device IDs that use uppercase hex (usb_windows.go), but the Linux backend copied idVendor/idProduct straight from sysfs, which stores them lowercase. Code that compares or displays VID/PID across platforms (or matches against an uppercase table) got inconsistent results. Uppercase the VID and PID read from sysfs with strings.ToUpper, matching the darwin and Windows backends. The serial number's case is left untouched (it is not hex and may be case-significant). TestParseUSBSysFSVIDPIDUppercase writes lowercase idVendor=10c4/idProduct=ea60 plus a mixed-case serial into a temp sysfs-like dir and asserts parseUSBSysFS yields VID=10C4 PID=EA60 while preserving the serial's case. --- enumerator/usb_linux.go | 8 ++++++-- enumerator/usb_linux_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/enumerator/usb_linux.go b/enumerator/usb_linux.go index b52e388..00c1bd4 100644 --- a/enumerator/usb_linux.go +++ b/enumerator/usb_linux.go @@ -12,6 +12,7 @@ import ( "io" "os" "path/filepath" + "strings" "go.bug.st/serial" ) @@ -91,8 +92,11 @@ func parseUSBSysFS(usbDevicePath string, details *PortDetails) error { } details.IsUSB = true - details.VID = vid - details.PID = pid + // sysfs reports idVendor/idProduct in lowercase hex; the darwin and Windows + // backends report them uppercase. Normalize to uppercase so VID/PID are + // consistent across platforms. + details.VID = strings.ToUpper(vid) + details.PID = strings.ToUpper(pid) details.SerialNumber = serial details.Configuration = configuration details.Manufacturer = manufacturer diff --git a/enumerator/usb_linux_test.go b/enumerator/usb_linux_test.go index 57f7133..3de1496 100644 --- a/enumerator/usb_linux_test.go +++ b/enumerator/usb_linux_test.go @@ -101,3 +101,31 @@ func TestParseUSBSysFSEmptySerial(t *testing.T) { t.Fatalf("Product = %q, want %q", details.Product, "Arduino Uno") } } + +// TestParseUSBSysFSVIDPIDUppercase checks that VID/PID read from sysfs (which +// stores them lowercase) are normalized to uppercase, matching the darwin and +// Windows backends, while the serial number's case is preserved. +func TestParseUSBSysFSVIDPIDUppercase(t *testing.T) { + dir := t.TempDir() + write := func(name, content string) { + if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil { + t.Fatal(err) + } + } + write("idVendor", "10c4\n") // CP2102, lowercase in sysfs + write("idProduct", "ea60\n") + write("serial", "abcDEF0123\n") // mixed case must be preserved + write("manufacturer", "Silicon Labs\n") + write("product", "CP2102 USB to UART\n") + + details := &PortDetails{} + if err := parseUSBSysFS(dir, details); err != nil { + t.Fatalf("parseUSBSysFS returned error %v", err) + } + if details.VID != "10C4" || details.PID != "EA60" { + t.Fatalf("got VID=%q PID=%q, want 10C4/EA60 (uppercase)", details.VID, details.PID) + } + if details.SerialNumber != "abcDEF0123" { + t.Fatalf("SerialNumber = %q, want case preserved", details.SerialNumber) + } +}