@@ -19,36 +19,112 @@ package types
1919import (
2020 "fmt"
2121 "strconv"
22+ "strings"
2223)
2324
2425// DeviceID represents a GPU Device ID as read from a GPUs PCIe config space.
25- type DeviceID uint32
26+ type DeviceID struct {
27+ Device uint16
28+ Vendor uint16
29+ SubsystemDevice uint16
30+ SubsystemVendor uint16
31+ HasSubsystem bool
32+ }
2633
2734// NewDeviceID constructs a new 'DeviceID' from the device and vendor values pulled from a GPUs PCIe config space.
2835func NewDeviceID (device , vendor uint16 ) DeviceID {
29- return DeviceID ((uint32 (device ) << 16 ) | uint32 (vendor ))
36+ return DeviceID {
37+ Device : device ,
38+ Vendor : vendor ,
39+ }
40+ }
41+
42+ // NewDeviceIDWithSubsystem constructs a new 'DeviceID' with subsystem values.
43+ func NewDeviceIDWithSubsystem (device , vendor , subDevice , subVendor uint16 ) DeviceID {
44+ return DeviceID {
45+ Device : device ,
46+ Vendor : vendor ,
47+ SubsystemDevice : subDevice ,
48+ SubsystemVendor : subVendor ,
49+ HasSubsystem : true ,
50+ }
3051}
3152
3253// NewDeviceIDFromString constructs a 'DeviceID' from its string representation.
3354func NewDeviceIDFromString (str string ) (DeviceID , error ) {
34- deviceID , err := strconv .ParseInt (str , 0 , 32 )
55+ parts := strings .Split (str , ":" )
56+ if len (parts ) > 2 {
57+ return DeviceID {}, fmt .Errorf (
58+ "invalid DeviceID format '%v': expected '<devicevendor>' or '<devicevendor>:<subdevicevendor>'" ,
59+ str ,
60+ )
61+ }
62+
63+ deviceIDRaw , err := strconv .ParseUint (parts [0 ], 0 , 32 )
3564 if err != nil {
36- return 0 , fmt .Errorf ("unable to create DeviceID from string '%v': %v" , str , err )
65+ return DeviceID {}, fmt .Errorf ("unable to create DeviceID from string '%v': %v" , str , err )
66+ }
67+
68+ device , vendor := splitRawDeviceID (deviceIDRaw )
69+
70+ deviceID := DeviceID {
71+ Device : device ,
72+ Vendor : vendor ,
73+ }
74+
75+ if len (parts ) == 2 {
76+ subIDRaw , err := strconv .ParseUint (parts [1 ], 0 , 32 )
77+ if err != nil {
78+ return DeviceID {}, fmt .Errorf ("unable to create Subsystem from string '%v': %v" , str , err )
79+ }
80+
81+ subsystemDevice , subsystemVendor := splitRawDeviceID (subIDRaw )
82+
83+ deviceID .SubsystemDevice = subsystemDevice
84+ deviceID .SubsystemVendor = subsystemVendor
85+ deviceID .HasSubsystem = true
3786 }
38- return DeviceID (deviceID ), nil
87+
88+ return deviceID , nil
3989}
4090
4191// String returns a 'DeviceID' as a string.
4292func (d DeviceID ) String () string {
43- return fmt .Sprintf ("0x%X" , uint32 (d ))
93+ primary := fmt .Sprintf ("0x%04X%04X" , d .Device , d .Vendor )
94+ if d .HasSubsystem {
95+ return fmt .Sprintf ("%s:0x%04X%04X" , primary , d .SubsystemDevice , d .SubsystemVendor )
96+ }
97+ return primary
4498}
4599
46100// GetVendor returns the 'vendor' portion of a 'DeviceID'.
47101func (d DeviceID ) GetVendor () uint16 {
48- return uint16 ( d )
102+ return d . Vendor
49103}
50104
51105// GetDevice returns the 'device' portion of a 'DeviceID'.
52106func (d DeviceID ) GetDevice () uint16 {
53- return uint16 (d >> 16 )
107+ return d .Device
108+ }
109+
110+ // Matches checks if a hardware GPU matches the DeviceID filter.
111+ // If the filter has a subsystem defined, it requires an exact match on all 4 components.
112+ // Otherwise, it only matches on the primary device and vendor IDs.
113+ func (filter DeviceID ) Matches (hardware DeviceID ) bool {
114+ if filter .Device != hardware .Device || filter .Vendor != hardware .Vendor {
115+ return false
116+ }
117+
118+ if filter .HasSubsystem {
119+ if filter .SubsystemDevice != hardware .SubsystemDevice || filter .SubsystemVendor != hardware .SubsystemVendor {
120+ return false
121+ }
122+ }
123+
124+ return true
125+ }
126+
127+ func splitRawDeviceID (raw uint64 ) (uint16 , uint16 ) {
128+ // nolint:gosec // raw is parsed as a 32-bit value, so both halves fit in a uint16
129+ return uint16 (raw >> 16 ), uint16 (raw & 0xFFFF )
54130}
0 commit comments