168 lines
3.4 KiB
Go
168 lines
3.4 KiB
Go
package pciids
|
|
|
|
import (
|
|
"bufio"
|
|
_ "embed"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
var (
|
|
//go:embed pci.ids
|
|
embeddedPCIIDs string
|
|
|
|
loadOnce sync.Once
|
|
vendors map[int]string
|
|
devices map[string]string
|
|
)
|
|
|
|
// VendorName returns vendor name by PCI Vendor ID
|
|
func VendorName(vendorID int) string {
|
|
loadPCIIDs()
|
|
if name, ok := vendors[vendorID]; ok {
|
|
return name
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// DeviceName returns device name by Vendor ID and Device ID
|
|
func DeviceName(vendorID, deviceID int) string {
|
|
loadPCIIDs()
|
|
key := fmt.Sprintf("%04x:%04x", vendorID, deviceID)
|
|
if name, ok := devices[key]; ok {
|
|
return name
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// DeviceInfo returns both vendor and device name
|
|
func DeviceInfo(vendorID, deviceID int) (vendor, device string) {
|
|
vendor = VendorName(vendorID)
|
|
device = DeviceName(vendorID, deviceID)
|
|
return
|
|
}
|
|
|
|
// VendorNameFromString tries to parse vendor ID from string (hex) and return name
|
|
func VendorNameFromString(s string) string {
|
|
s = strings.TrimSpace(s)
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
|
|
// Try to parse as hex (with or without 0x prefix)
|
|
s = strings.TrimPrefix(strings.ToLower(s), "0x")
|
|
|
|
var id int
|
|
for _, c := range s {
|
|
if c >= '0' && c <= '9' {
|
|
id = id*16 + int(c-'0')
|
|
} else if c >= 'a' && c <= 'f' {
|
|
id = id*16 + int(c-'a'+10)
|
|
} else {
|
|
return ""
|
|
}
|
|
}
|
|
|
|
return VendorName(id)
|
|
}
|
|
|
|
func loadPCIIDs() {
|
|
loadOnce.Do(func() {
|
|
vendors = make(map[int]string)
|
|
devices = make(map[string]string)
|
|
|
|
parsePCIIDs(strings.NewReader(embeddedPCIIDs), vendors, devices)
|
|
|
|
for _, path := range candidatePCIIDsPaths() {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
parsePCIIDs(f, vendors, devices)
|
|
_ = f.Close()
|
|
}
|
|
})
|
|
}
|
|
|
|
func candidatePCIIDsPaths() []string {
|
|
paths := []string{
|
|
"pci.ids",
|
|
"/usr/share/hwdata/pci.ids",
|
|
"/usr/share/misc/pci.ids",
|
|
"/opt/homebrew/share/pciids/pci.ids",
|
|
}
|
|
|
|
// Env paths have highest priority, so they are applied last.
|
|
if env := strings.TrimSpace(os.Getenv("LOGPILE_PCI_IDS_PATH")); env != "" {
|
|
for _, p := range strings.Split(env, string(os.PathListSeparator)) {
|
|
p = strings.TrimSpace(p)
|
|
if p != "" {
|
|
paths = append(paths, p)
|
|
}
|
|
}
|
|
}
|
|
return paths
|
|
}
|
|
|
|
func parsePCIIDs(r interface{ Read([]byte) (int, error) }, outVendors map[int]string, outDevices map[string]string) {
|
|
scanner := bufio.NewScanner(r)
|
|
currentVendor := -1
|
|
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
|
|
// Subdevice line (tab-tab) - ignored for now
|
|
if strings.HasPrefix(line, "\t\t") {
|
|
continue
|
|
}
|
|
|
|
// Device line
|
|
if strings.HasPrefix(line, "\t") {
|
|
if currentVendor < 0 {
|
|
continue
|
|
}
|
|
trimmed := strings.TrimLeft(line, "\t")
|
|
fields := strings.Fields(trimmed)
|
|
if len(fields) < 2 {
|
|
continue
|
|
}
|
|
deviceID, err := strconv.ParseInt(fields[0], 16, 32)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
name := strings.TrimSpace(trimmed[len(fields[0]):])
|
|
if name == "" {
|
|
continue
|
|
}
|
|
key := fmt.Sprintf("%04x:%04x", currentVendor, int(deviceID))
|
|
outDevices[key] = name
|
|
continue
|
|
}
|
|
|
|
// Vendor line
|
|
fields := strings.Fields(line)
|
|
if len(fields) < 2 {
|
|
currentVendor = -1
|
|
continue
|
|
}
|
|
vendorID, err := strconv.ParseInt(fields[0], 16, 32)
|
|
if err != nil {
|
|
currentVendor = -1
|
|
continue
|
|
}
|
|
name := strings.TrimSpace(line[len(fields[0]):])
|
|
if name == "" {
|
|
currentVendor = -1
|
|
continue
|
|
}
|
|
currentVendor = int(vendorID)
|
|
outVendors[currentVendor] = name
|
|
}
|
|
}
|