aboutsummaryrefslogtreecommitdiff
path: root/packages/numap/internal/hwids/hwids.go
diff options
context:
space:
mode:
Diffstat (limited to 'packages/numap/internal/hwids/hwids.go')
-rw-r--r--packages/numap/internal/hwids/hwids.go148
1 files changed, 148 insertions, 0 deletions
diff --git a/packages/numap/internal/hwids/hwids.go b/packages/numap/internal/hwids/hwids.go
new file mode 100644
index 0000000..6aa9d8a
--- /dev/null
+++ b/packages/numap/internal/hwids/hwids.go
@@ -0,0 +1,148 @@
+package hwids
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strings"
+)
+
+var pciPath = []string{
+ "/usr/share/hwdata/pci.ids",
+ "/usr/share/misc/pci.ids",
+}
+
+type PCIType int
+
+const (
+ PCIVendor PCIType = iota
+ PCIDevice
+ PCISubsystem
+)
+
+type PciDevices map[uint16][]PciDevice
+
+// PciDevice represents a PCI device
+type PciDevice struct {
+ Type PCIType
+ Vendor, Device uint16
+ SubVendor, SubDevice uint16
+ VendorName, DeviceName string
+ SubName string
+}
+
+// Load load the hardware database for PCI devices and return a map of
+// vendor -> list of devices.
+func Load() (PciDevices, error) {
+ // if the environment variable HWDATAPATH is set, we add it to the
+ // list of paths we check for the hardware database.
+ extraPath := os.Getenv("HWDATA")
+ if extraPath != "" {
+ pciPath = append(pciPath, extraPath)
+ }
+
+ for _, f := range pciPath {
+ fh, err := os.Open(f)
+ if err != nil {
+ continue
+ }
+ defer fh.Close()
+ return parse(fh)
+ }
+ return PciDevices{}, fmt.Errorf("hwids: could not find a pci.ids file")
+}
+
+func parse(f *os.File) (PciDevices, error) {
+ devices := make(PciDevices)
+
+ s := bufio.NewScanner(f)
+
+ // this is to keep track of the current device. The format of the
+ // file is as follow:
+ // vendor vendor_name
+ // device device_name <-- single tab
+ // subvendor subdevice subsystem_name <-- two tabs
+ // the variable is to keep track of the current vendor / device
+ cur := PciDevice{}
+
+ for s.Scan() {
+ l := s.Text()
+ // skip empty lines or lines that are a comment
+ if len(l) == 0 || l[0] == '#' {
+ continue
+ }
+ // lines starting with a C are the classes definitions, and
+ // they are at the end of the file, which means we're done
+ // parsing the devices
+ if l[0] == 'C' {
+ break
+ }
+
+ parts := strings.SplitN(l, " ", 2)
+ if len(parts) != 2 {
+ return devices, fmt.Errorf("hwids: malformed PCI ID line (missing ID separator): %s", l)
+ }
+
+ ids, name := parts[0], parts[1]
+ if len(ids) < 2 || len(name) == 0 {
+ return devices, fmt.Errorf("hwids: malformed PCI ID line (empty ID or name): %s", l)
+ }
+
+ cur.Type = PCIVendor
+
+ if ids[0] == '\t' {
+ if ids[1] == '\t' {
+ cur.Type = PCISubsystem
+ } else {
+ cur.Type = PCIDevice
+ }
+ }
+
+ var err error
+ switch cur.Type {
+ case PCIVendor:
+ _, err = fmt.Sscanf(ids, "%x", &cur.Vendor)
+ cur.VendorName = name
+ case PCIDevice:
+ _, err = fmt.Sscanf(ids, "%x", &cur.Device)
+ cur.DeviceName = name
+ case PCISubsystem:
+ _, err = fmt.Sscanf(ids, "%x %x", &cur.SubVendor, &cur.SubDevice)
+ cur.SubName = name
+ }
+
+ if err != nil {
+ return devices, fmt.Errorf("hwids: malformed PCI ID line: %s: %v", l, err)
+ }
+
+ // This is to reset the state when we are moving to a
+ // different vendor or device
+ switch cur.Type {
+ case PCIVendor:
+ cur.Device = 0
+ cur.DeviceName = ""
+ fallthrough
+ case PCIDevice:
+ cur.SubVendor = 0
+ cur.SubDevice = 0
+ cur.SubName = ""
+ }
+
+ _, ok := devices[cur.Vendor]
+ if ok {
+ _devices := devices[cur.Vendor]
+ _devices = append(_devices, cur)
+ devices[cur.Vendor] = _devices
+
+ } else {
+ _devices := []PciDevice{cur}
+ devices[cur.Vendor] = _devices
+ }
+ }
+
+ if err := s.Err(); err != nil {
+ return devices, fmt.Errorf("hwids: failed to read PCI ID line: %v", err)
+ }
+
+ return devices, nil
+}