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) + } +}