(ctx context.Context)
| 33 | ) |
| 34 | |
| 35 | func (i *Info) load(ctx context.Context) error { |
| 36 | // In Linux, each graphics card is listed under the /sys/class/drm |
| 37 | // directory as a symbolic link named "cardN", where N is a zero-based |
| 38 | // index of the card in the system. "DRM" stands for Direct Rendering |
| 39 | // Manager and is the Linux subsystem that is responsible for graphics I/O |
| 40 | // |
| 41 | // Each card may have multiple symbolic |
| 42 | // links in this directory representing the interfaces from the graphics |
| 43 | // card over a particular wire protocol (HDMI, DisplayPort, etc). These |
| 44 | // symbolic links are named cardN-<INTERFACE_TYPE>-<DISPLAY_ID>. For |
| 45 | // instance, on one of my local workstations with an NVIDIA GTX 1050ti |
| 46 | // graphics card with one HDMI, one DisplayPort, and one DVI interface to |
| 47 | // the card, I see the following in /sys/class/drm: |
| 48 | // |
| 49 | // $ ll /sys/class/drm/ |
| 50 | // total 0 |
| 51 | // drwxr-xr-x 2 root root 0 Jul 16 11:50 ./ |
| 52 | // drwxr-xr-x 75 root root 0 Jul 16 11:50 ../ |
| 53 | // lrwxrwxrwx 1 root root 0 Jul 16 11:50 card0 -> ../../devices/pci0000:00/0000:00:03.0/0000:03:00.0/drm/card0/ |
| 54 | // lrwxrwxrwx 1 root root 0 Jul 16 11:50 card0-DP-1 -> ../../devices/pci0000:00/0000:00:03.0/0000:03:00.0/drm/card0/card0-DP-1/ |
| 55 | // lrwxrwxrwx 1 root root 0 Jul 16 11:50 card0-DVI-D-1 -> ../../devices/pci0000:00/0000:00:03.0/0000:03:00.0/drm/card0/card0-DVI-D-1/ |
| 56 | // lrwxrwxrwx 1 root root 0 Jul 16 11:50 card0-HDMI-A-1 -> ../../devices/pci0000:00/0000:00:03.0/0000:03:00.0/drm/card0/card0-HDMI-A-1/ |
| 57 | // |
| 58 | // In this routine, we are only interested in the first link (card0), which |
| 59 | // we follow to gather information about the actual device from the PCI |
| 60 | // subsystem (we query the modalias file of the PCI device's sysfs |
| 61 | // directory using the `ghw.PCIInfo.GetDevice()` function. |
| 62 | pci, err := pci.New(ctx) |
| 63 | if err != nil { |
| 64 | return fmt.Errorf("failed to initialize PCI device database: %w", err) |
| 65 | } |
| 66 | paths := linuxpath.New(ctx) |
| 67 | links, err := os.ReadDir(paths.SysClassDRM) |
| 68 | if err != nil { |
| 69 | log.Warn(ctx, warnNoSysClassDRM) |
| 70 | return nil |
| 71 | } |
| 72 | cards := make([]*GraphicsCard, 0) |
| 73 | for _, link := range links { |
| 74 | lname := link.Name() |
| 75 | if !strings.HasPrefix(lname, "card") { |
| 76 | continue |
| 77 | } |
| 78 | if strings.ContainsRune(lname, '-') { |
| 79 | continue |
| 80 | } |
| 81 | // Grab the card's zero-based integer index |
| 82 | lnameBytes := []byte(lname) |
| 83 | cardIdx, err := strconv.Atoi(string(lnameBytes[4:])) |
| 84 | if err != nil { |
| 85 | cardIdx = -1 |
| 86 | } |
| 87 | |
| 88 | // Calculate the card's PCI address by looking at the symbolic link's |
| 89 | // target |
| 90 | lpath := filepath.Join(paths.SysClassDRM, lname) |
| 91 | dest, err := os.Readlink(lpath) |
| 92 | if err != nil { |
nothing calls this directly
no test coverage detected