|
3 | 3 | package usbmanager |
4 | 4 |
|
5 | 5 | import ( |
6 | | - "bufio" |
7 | | - "fmt" |
8 | | - "io" |
9 | | - "os" |
10 | | - "path/filepath" |
11 | 6 | "regexp" |
12 | 7 | "strconv" |
13 | 8 | "strings" |
14 | 9 |
|
15 | 10 | "github.com/lf-edge/eve/pkg/pillar/types" |
| 11 | + "github.com/zededa/ghw" |
| 12 | + "github.com/zededa/ghw/pkg/option" |
| 13 | + "github.com/zededa/ghw/pkg/usb" |
16 | 14 | ) |
17 | 15 |
|
18 | 16 | func trimSysPath(path string) string { |
@@ -47,139 +45,73 @@ func extractUSBPort(path string) string { |
47 | 45 | func walkUSBPorts() []*usbdevice { |
48 | 46 | uds := make([]*usbdevice, 0) |
49 | 47 |
|
50 | | - inSysPath := filepath.Join("bus", "usb", "devices") |
51 | | - usbDevicesPath := filepath.Join(sysFSPath, inSysPath) |
52 | | - |
53 | | - files, err := os.ReadDir(usbDevicesPath) |
| 48 | + info, err := ghw.USB(option.WithDisableTools()) |
54 | 49 | if err != nil { |
55 | | - log.Fatal(err) |
| 50 | + log.Warnf("requesting USB info failed: %+v", err) |
| 51 | + return []*usbdevice{} |
56 | 52 | } |
57 | 53 |
|
58 | | - re := regexp.MustCompile(`^\d+-\d+`) |
59 | | - for _, file := range files { |
60 | | - if len(file.Name()) == 0 { |
61 | | - continue |
62 | | - } |
63 | | - |
64 | | - if !re.Match([]byte(file.Name())) { |
| 54 | + for _, dev := range info.Devices { |
| 55 | + ud := ghwUSBDevice2usbdevice(dev) |
| 56 | + if ud == nil { |
65 | 57 | continue |
66 | 58 | } |
67 | 59 |
|
68 | | - relPortPath := filepath.Join(usbDevicesPath, file.Name()) |
69 | | - //fmt.Printf("%s -> %s\n", vals[0], vals[1]) |
70 | | - ud := usbDeviceFromSysPath(relPortPath) |
71 | | - if ud != nil { |
72 | | - uds = append(uds, ud) |
73 | | - } |
| 60 | + uds = append(uds, ud) |
74 | 61 | } |
75 | | - |
76 | 62 | return uds |
77 | 63 | } |
78 | 64 |
|
79 | | -func usbDeviceFromSysPath(relPortPath string) *usbdevice { |
80 | | - portPath, err := os.Readlink(relPortPath) |
| 65 | +func ghwUSBDevice2usbdevice(dev *usb.Device) *usbdevice { |
| 66 | + devnum, err := strconv.ParseUint(dev.Devnum, 10, 16) |
81 | 67 | if err != nil { |
82 | | - fmt.Printf("err: %+v\n", err) |
| 68 | + // ignore hubs, devices with invalid devnum |
83 | 69 | return nil |
84 | 70 | } |
85 | 71 |
|
86 | | - inSysPath := filepath.Join("bus", "usb", "devices") |
87 | | - usbDevicesPath := filepath.Join(sysFSPath, inSysPath) |
88 | | - portPath = filepath.Join(usbDevicesPath, portPath) |
89 | | - |
90 | | - ueventFilePath := filepath.Join(portPath, "uevent") |
91 | | - return ueventFile2usbDevice(ueventFilePath) |
92 | | -} |
93 | | - |
94 | | -func ueventFile2usbDevice(ueventFilePath string) *usbdevice { |
95 | | - ueventFp, err := os.Open(ueventFilePath) |
| 72 | + // continue even if parsing vendor/product fails, because in the case of passthrough |
| 73 | + // by port it still works |
| 74 | + vendorID, err := strconv.ParseUint(dev.VendorID, 16, 32) |
96 | 75 | if err != nil { |
97 | | - return nil |
| 76 | + log.Warnf("could not parse vendor id '%s' as uint32: %+v", dev.VendorID, err) |
98 | 77 | } |
99 | | - defer ueventFp.Close() |
100 | | - |
101 | | - return ueventFile2usbDeviceImpl(ueventFilePath, ueventFp) |
102 | | -} |
103 | | - |
104 | | -func ueventFile2usbDeviceImpl(ueventFilePath string, ueventFp io.Reader) *usbdevice { |
105 | | - |
106 | | - var busnum uint16 |
107 | | - var devnum uint16 |
108 | | - var vendorID uint32 |
109 | | - var productID uint32 |
110 | | - var product string |
111 | | - var devicetype string |
112 | | - |
113 | | - busnumSet := false |
114 | | - devnumSet := false |
115 | | - productSet := false |
116 | | - |
117 | | - sc := bufio.NewScanner(ueventFp) |
118 | | - for sc.Scan() { |
119 | | - vals := strings.SplitN(sc.Text(), "=", 2) |
120 | | - if len(vals) != 2 { |
121 | | - continue |
122 | | - } |
123 | | - |
124 | | - if vals[1] == "" { |
125 | | - continue |
126 | | - } |
127 | | - |
128 | | - if vals[0] == "BUSNUM" { |
129 | | - val64, err := strconv.ParseUint(vals[1], 10, 16) |
130 | | - if err != nil { |
131 | | - log.Warnf("could not parse BUSNUM %+v", vals) |
132 | | - return nil |
133 | | - } |
134 | | - busnum = uint16(val64) |
135 | | - busnumSet = true |
136 | | - } |
137 | | - if vals[0] == "DEVNUM" { |
138 | | - val64, err := strconv.ParseUint(vals[1], 10, 16) |
139 | | - if err != nil { |
140 | | - log.Warnf("could not parse DEVNUM %+v", vals) |
141 | | - return nil |
142 | | - } |
143 | | - devnum = uint16(val64) |
144 | | - devnumSet = true |
145 | | - } |
146 | | - if vals[0] == "PRODUCT" { |
147 | | - product = vals[1] |
148 | | - vendorID, productID = parseProductString(product) |
149 | | - if vendorID != 0 || productID != 0 { |
150 | | - productSet = true |
151 | | - } |
152 | | - } |
153 | | - if vals[0] == "TYPE" { |
154 | | - devicetype = vals[1] |
155 | | - } |
| 78 | + productID, err := strconv.ParseUint(dev.ProductID, 16, 32) |
| 79 | + if err != nil { |
| 80 | + log.Warnf("could not parse product id '%s' as uint32: %+v", dev.ProductID, err) |
156 | 81 | } |
157 | 82 |
|
158 | | - if sc.Err() != nil { |
159 | | - log.Warnf("Parsing of %s failed: %v", ueventFilePath, sc.Err()) |
160 | | - return nil |
| 83 | + var usbControllerPCIAddress string |
| 84 | + if dev.Parent.PCI != nil { |
| 85 | + usbControllerPCIAddress = dev.Parent.PCI.String() |
| 86 | + } |
| 87 | + ud := usbdevice{ |
| 88 | + busnum: dev.Busnum, |
| 89 | + portnum: dev.Port, |
| 90 | + devnum: uint16(devnum), |
| 91 | + vendorID: uint32(vendorID), |
| 92 | + productID: uint32(productID), |
| 93 | + devicetype: dev.Type, |
| 94 | + usbControllerPCIAddress: usbControllerPCIAddress, |
| 95 | + ueventFilePath: dev.UEventFilePath, |
161 | 96 | } |
| 97 | + return &ud |
| 98 | +} |
162 | 99 |
|
163 | | - if !busnumSet || !devnumSet || !productSet { |
| 100 | +func ueventFile2usbDevice(ueventFilePath string) *usbdevice { |
| 101 | + info, err := ghw.USB(option.WithUSBUeventPath(ueventFilePath), option.WithDisableTools()) |
| 102 | + if err != nil { |
| 103 | + log.Warnf("could not retrieve usb device for '%s': %v", ueventFilePath, err) |
164 | 104 | return nil |
165 | 105 | } |
166 | 106 |
|
167 | | - pciAddress := extractPCIaddress(ueventFilePath) |
168 | | - |
169 | | - portnum := extractUSBPort(ueventFilePath) |
170 | | - |
171 | | - ud := usbdevice{ |
172 | | - busnum: busnum, |
173 | | - devnum: devnum, |
174 | | - portnum: portnum, |
175 | | - vendorID: vendorID, |
176 | | - productID: productID, |
177 | | - devicetype: devicetype, |
178 | | - usbControllerPCIAddress: pciAddress, |
179 | | - ueventFilePath: filepath.Clean(ueventFilePath), |
| 107 | + for _, dev := range info.Devices { |
| 108 | + ud := ghwUSBDevice2usbdevice(dev) |
| 109 | + if ud != nil { |
| 110 | + return ud |
| 111 | + } |
180 | 112 | } |
181 | 113 |
|
182 | | - return &ud |
| 114 | + return nil |
183 | 115 | } |
184 | 116 |
|
185 | 117 | func parseProductString(product string) (uint32, uint32) { |
|
0 commit comments