1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2005-2007 Takahiro Hirofuchi |
4 | */ |
5 | |
6 | #include <libudev.h> |
7 | #include "usbip_common.h" |
8 | #include "names.h" |
9 | |
10 | #undef PROGNAME |
11 | #define PROGNAME "libusbip" |
12 | |
13 | int usbip_use_syslog; |
14 | int usbip_use_stderr; |
15 | int usbip_use_debug; |
16 | |
17 | extern struct udev *udev_context; |
18 | |
19 | struct speed_string { |
20 | int num; |
21 | char *speed; |
22 | char *desc; |
23 | }; |
24 | |
25 | static const struct speed_string speed_strings[] = { |
26 | { USB_SPEED_UNKNOWN, "unknown" , "Unknown Speed" }, |
27 | { USB_SPEED_LOW, "1.5" , "Low Speed(1.5Mbps)" }, |
28 | { USB_SPEED_FULL, "12" , "Full Speed(12Mbps)" }, |
29 | { USB_SPEED_HIGH, "480" , "High Speed(480Mbps)" }, |
30 | { USB_SPEED_WIRELESS, "53.3-480" , "Wireless" }, |
31 | { USB_SPEED_SUPER, "5000" , "Super Speed(5000Mbps)" }, |
32 | { 0, NULL, NULL } |
33 | }; |
34 | |
35 | struct portst_string { |
36 | int num; |
37 | char *desc; |
38 | }; |
39 | |
40 | static struct portst_string portst_strings[] = { |
41 | { SDEV_ST_AVAILABLE, "Device Available" }, |
42 | { SDEV_ST_USED, "Device in Use" }, |
43 | { SDEV_ST_ERROR, "Device Error" }, |
44 | { VDEV_ST_NULL, "Port Available" }, |
45 | { VDEV_ST_NOTASSIGNED, "Port Initializing" }, |
46 | { VDEV_ST_USED, "Port in Use" }, |
47 | { VDEV_ST_ERROR, "Port Error" }, |
48 | { 0, NULL} |
49 | }; |
50 | |
51 | const char *usbip_status_string(int32_t status) |
52 | { |
53 | for (int i = 0; portst_strings[i].desc != NULL; i++) |
54 | if (portst_strings[i].num == status) |
55 | return portst_strings[i].desc; |
56 | |
57 | return "Unknown Status" ; |
58 | } |
59 | |
60 | const char *usbip_speed_string(int num) |
61 | { |
62 | for (int i = 0; speed_strings[i].speed != NULL; i++) |
63 | if (speed_strings[i].num == num) |
64 | return speed_strings[i].desc; |
65 | |
66 | return "Unknown Speed" ; |
67 | } |
68 | |
69 | struct op_common_status_string { |
70 | int num; |
71 | char *desc; |
72 | }; |
73 | |
74 | static struct op_common_status_string op_common_status_strings[] = { |
75 | { ST_OK, "Request Completed Successfully" }, |
76 | { ST_NA, "Request Failed" }, |
77 | { ST_DEV_BUSY, "Device busy (exported)" }, |
78 | { ST_DEV_ERR, "Device in error state" }, |
79 | { ST_NODEV, "Device not found" }, |
80 | { ST_ERROR, "Unexpected response" }, |
81 | { 0, NULL} |
82 | }; |
83 | |
84 | const char *usbip_op_common_status_string(int status) |
85 | { |
86 | for (int i = 0; op_common_status_strings[i].desc != NULL; i++) |
87 | if (op_common_status_strings[i].num == status) |
88 | return op_common_status_strings[i].desc; |
89 | |
90 | return "Unknown Op Common Status" ; |
91 | } |
92 | |
93 | #define DBG_UDEV_INTEGER(name)\ |
94 | dbg("%-20s = %x", to_string(name), (int) udev->name) |
95 | |
96 | #define DBG_UINF_INTEGER(name)\ |
97 | dbg("%-20s = %x", to_string(name), (int) uinf->name) |
98 | |
99 | void dump_usb_interface(struct usbip_usb_interface *uinf) |
100 | { |
101 | char buff[100]; |
102 | |
103 | usbip_names_get_class(buff, size: sizeof(buff), |
104 | class: uinf->bInterfaceClass, |
105 | subclass: uinf->bInterfaceSubClass, |
106 | protocol: uinf->bInterfaceProtocol); |
107 | dbg("%-20s = %s" , "Interface(C/SC/P)" , buff); |
108 | } |
109 | |
110 | void dump_usb_device(struct usbip_usb_device *udev) |
111 | { |
112 | char buff[100]; |
113 | |
114 | dbg("%-20s = %s" , "path" , udev->path); |
115 | dbg("%-20s = %s" , "busid" , udev->busid); |
116 | |
117 | usbip_names_get_class(buff, size: sizeof(buff), |
118 | class: udev->bDeviceClass, |
119 | subclass: udev->bDeviceSubClass, |
120 | protocol: udev->bDeviceProtocol); |
121 | dbg("%-20s = %s" , "Device(C/SC/P)" , buff); |
122 | |
123 | DBG_UDEV_INTEGER(bcdDevice); |
124 | |
125 | usbip_names_get_product(buff, size: sizeof(buff), |
126 | vendor: udev->idVendor, |
127 | product: udev->idProduct); |
128 | dbg("%-20s = %s" , "Vendor/Product" , buff); |
129 | |
130 | DBG_UDEV_INTEGER(bNumConfigurations); |
131 | DBG_UDEV_INTEGER(bNumInterfaces); |
132 | |
133 | dbg("%-20s = %s" , "speed" , |
134 | usbip_speed_string(udev->speed)); |
135 | |
136 | DBG_UDEV_INTEGER(busnum); |
137 | DBG_UDEV_INTEGER(devnum); |
138 | } |
139 | |
140 | |
141 | int read_attr_value(struct udev_device *dev, const char *name, |
142 | const char *format) |
143 | { |
144 | const char *attr; |
145 | int num = 0; |
146 | int ret; |
147 | |
148 | attr = udev_device_get_sysattr_value(dev, name); |
149 | if (!attr) { |
150 | err("udev_device_get_sysattr_value failed" ); |
151 | goto err; |
152 | } |
153 | |
154 | /* The client chooses the device configuration |
155 | * when attaching it so right after being bound |
156 | * to usbip-host on the server the device will |
157 | * have no configuration. |
158 | * Therefore, attributes such as bConfigurationValue |
159 | * and bNumInterfaces will not exist and sscanf will |
160 | * fail. Check for these cases and don't treat them |
161 | * as errors. |
162 | */ |
163 | |
164 | ret = sscanf(attr, format, &num); |
165 | if (ret < 1) { |
166 | if (strcmp(name, "bConfigurationValue" ) && |
167 | strcmp(name, "bNumInterfaces" )) { |
168 | err("sscanf failed for attribute %s" , name); |
169 | goto err; |
170 | } |
171 | } |
172 | |
173 | err: |
174 | |
175 | return num; |
176 | } |
177 | |
178 | |
179 | int read_attr_speed(struct udev_device *dev) |
180 | { |
181 | const char *speed; |
182 | |
183 | speed = udev_device_get_sysattr_value(dev, "speed" ); |
184 | if (!speed) { |
185 | err("udev_device_get_sysattr_value failed" ); |
186 | goto err; |
187 | } |
188 | |
189 | for (int i = 0; speed_strings[i].speed != NULL; i++) { |
190 | if (!strcmp(speed, speed_strings[i].speed)) |
191 | return speed_strings[i].num; |
192 | } |
193 | |
194 | err: |
195 | |
196 | return USB_SPEED_UNKNOWN; |
197 | } |
198 | |
199 | #define READ_ATTR(object, type, dev, name, format) \ |
200 | do { \ |
201 | (object)->name = (type) read_attr_value(dev, to_string(name), \ |
202 | format); \ |
203 | } while (0) |
204 | |
205 | |
206 | int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev) |
207 | { |
208 | uint32_t busnum, devnum; |
209 | const char *path, *name; |
210 | |
211 | READ_ATTR(udev, uint8_t, sdev, bDeviceClass, "%02x\n" ); |
212 | READ_ATTR(udev, uint8_t, sdev, bDeviceSubClass, "%02x\n" ); |
213 | READ_ATTR(udev, uint8_t, sdev, bDeviceProtocol, "%02x\n" ); |
214 | |
215 | READ_ATTR(udev, uint16_t, sdev, idVendor, "%04x\n" ); |
216 | READ_ATTR(udev, uint16_t, sdev, idProduct, "%04x\n" ); |
217 | READ_ATTR(udev, uint16_t, sdev, bcdDevice, "%04x\n" ); |
218 | |
219 | READ_ATTR(udev, uint8_t, sdev, bConfigurationValue, "%02x\n" ); |
220 | READ_ATTR(udev, uint8_t, sdev, bNumConfigurations, "%02x\n" ); |
221 | READ_ATTR(udev, uint8_t, sdev, bNumInterfaces, "%02x\n" ); |
222 | |
223 | READ_ATTR(udev, uint8_t, sdev, devnum, "%d\n" ); |
224 | udev->speed = read_attr_speed(dev: sdev); |
225 | |
226 | path = udev_device_get_syspath(sdev); |
227 | name = udev_device_get_sysname(sdev); |
228 | |
229 | strncpy(udev->path, path, SYSFS_PATH_MAX - 1); |
230 | udev->path[SYSFS_PATH_MAX - 1] = '\0'; |
231 | strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE - 1); |
232 | udev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0'; |
233 | |
234 | sscanf(name, "%u-%u" , &busnum, &devnum); |
235 | udev->busnum = busnum; |
236 | |
237 | return 0; |
238 | } |
239 | |
240 | int read_usb_interface(struct usbip_usb_device *udev, int i, |
241 | struct usbip_usb_interface *uinf) |
242 | { |
243 | char busid[SYSFS_BUS_ID_SIZE]; |
244 | int size; |
245 | struct udev_device *sif; |
246 | |
247 | size = snprintf(busid, sizeof(busid), "%s:%d.%d" , |
248 | udev->busid, udev->bConfigurationValue, i); |
249 | if (size < 0 || (unsigned int)size >= sizeof(busid)) { |
250 | err("busid length %i >= %lu or < 0" , size, |
251 | (long unsigned)sizeof(busid)); |
252 | return -1; |
253 | } |
254 | |
255 | sif = udev_device_new_from_subsystem_sysname(udev_context, "usb" , busid); |
256 | if (!sif) { |
257 | err("udev_device_new_from_subsystem_sysname %s failed" , busid); |
258 | return -1; |
259 | } |
260 | |
261 | READ_ATTR(uinf, uint8_t, sif, bInterfaceClass, "%02x\n" ); |
262 | READ_ATTR(uinf, uint8_t, sif, bInterfaceSubClass, "%02x\n" ); |
263 | READ_ATTR(uinf, uint8_t, sif, bInterfaceProtocol, "%02x\n" ); |
264 | |
265 | return 0; |
266 | } |
267 | |
268 | int usbip_names_init(char *f) |
269 | { |
270 | return names_init(n: f); |
271 | } |
272 | |
273 | void usbip_names_free(void) |
274 | { |
275 | names_free(); |
276 | } |
277 | |
278 | void usbip_names_get_product(char *buff, size_t size, uint16_t vendor, |
279 | uint16_t product) |
280 | { |
281 | const char *prod, *vend; |
282 | |
283 | prod = names_product(vendorid: vendor, productid: product); |
284 | if (!prod) |
285 | prod = "unknown product" ; |
286 | |
287 | |
288 | vend = names_vendor(vendorid: vendor); |
289 | if (!vend) |
290 | vend = "unknown vendor" ; |
291 | |
292 | snprintf(buff, size, "%s : %s (%04x:%04x)" , vend, prod, vendor, product); |
293 | } |
294 | |
295 | void usbip_names_get_class(char *buff, size_t size, uint8_t class, |
296 | uint8_t subclass, uint8_t protocol) |
297 | { |
298 | const char *c, *s, *p; |
299 | |
300 | if (class == 0 && subclass == 0 && protocol == 0) { |
301 | snprintf(buff, size, "(Defined at Interface level) (%02x/%02x/%02x)" , class, subclass, protocol); |
302 | return; |
303 | } |
304 | |
305 | p = names_protocol(classid: class, subclassid: subclass, protocolid: protocol); |
306 | if (!p) |
307 | p = "unknown protocol" ; |
308 | |
309 | s = names_subclass(classid: class, subclassid: subclass); |
310 | if (!s) |
311 | s = "unknown subclass" ; |
312 | |
313 | c = names_class(classid: class); |
314 | if (!c) |
315 | c = "unknown class" ; |
316 | |
317 | snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)" , c, s, p, class, subclass, protocol); |
318 | } |
319 | |