1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> |
4 | * 2005-2007 Takahiro Hirofuchi |
5 | * Copyright (C) 2015-2016 Samsung Electronics |
6 | * Igor Kotrasinski <i.kotrasinsk@samsung.com> |
7 | * Krzysztof Opasiak <k.opasiak@samsung.com> |
8 | */ |
9 | |
10 | #include <sys/types.h> |
11 | #include <libudev.h> |
12 | |
13 | #include <errno.h> |
14 | #include <stdbool.h> |
15 | #include <stdint.h> |
16 | #include <stdio.h> |
17 | #include <stdlib.h> |
18 | #include <string.h> |
19 | |
20 | #include <getopt.h> |
21 | #include <netdb.h> |
22 | #include <unistd.h> |
23 | |
24 | #include <dirent.h> |
25 | |
26 | #include <linux/usb/ch9.h> |
27 | |
28 | #include "usbip_common.h" |
29 | #include "usbip_network.h" |
30 | #include "usbip.h" |
31 | |
32 | static const char usbip_list_usage_string[] = |
33 | "usbip list [-p|--parsable] <args>\n" |
34 | " -p, --parsable Parsable list format\n" |
35 | " -r, --remote=<host> List the exportable USB devices on <host>\n" |
36 | " -l, --local List the local USB devices\n" |
37 | " -d, --device List the local USB gadgets bound to usbip-vudc\n" ; |
38 | |
39 | void usbip_list_usage(void) |
40 | { |
41 | printf("usage: %s" , usbip_list_usage_string); |
42 | } |
43 | |
44 | static int get_exported_devices(char *host, int sockfd) |
45 | { |
46 | char product_name[100]; |
47 | char class_name[100]; |
48 | struct op_devlist_reply reply; |
49 | uint16_t code = OP_REP_DEVLIST; |
50 | struct usbip_usb_device udev; |
51 | struct usbip_usb_interface uintf; |
52 | unsigned int i; |
53 | int rc, j; |
54 | int status; |
55 | |
56 | rc = usbip_net_send_op_common(sockfd, OP_REQ_DEVLIST, status: 0); |
57 | if (rc < 0) { |
58 | dbg("usbip_net_send_op_common failed" ); |
59 | return -1; |
60 | } |
61 | |
62 | rc = usbip_net_recv_op_common(sockfd, code: &code, status: &status); |
63 | if (rc < 0) { |
64 | err("Exported Device List Request failed - %s\n" , |
65 | usbip_op_common_status_string(status)); |
66 | return -1; |
67 | } |
68 | |
69 | memset(&reply, 0, sizeof(reply)); |
70 | rc = usbip_net_recv(sockfd, buff: &reply, bufflen: sizeof(reply)); |
71 | if (rc < 0) { |
72 | dbg("usbip_net_recv_op_devlist failed" ); |
73 | return -1; |
74 | } |
75 | PACK_OP_DEVLIST_REPLY(0, &reply); |
76 | dbg("exportable devices: %d\n" , reply.ndev); |
77 | |
78 | if (reply.ndev == 0) { |
79 | info("no exportable devices found on %s" , host); |
80 | return 0; |
81 | } |
82 | |
83 | printf("Exportable USB devices\n" ); |
84 | printf("======================\n" ); |
85 | printf(" - %s\n" , host); |
86 | |
87 | for (i = 0; i < reply.ndev; i++) { |
88 | memset(&udev, 0, sizeof(udev)); |
89 | rc = usbip_net_recv(sockfd, buff: &udev, bufflen: sizeof(udev)); |
90 | if (rc < 0) { |
91 | dbg("usbip_net_recv failed: usbip_usb_device[%d]" , i); |
92 | return -1; |
93 | } |
94 | usbip_net_pack_usb_device(pack: 0, udev: &udev); |
95 | |
96 | usbip_names_get_product(product_name, sizeof(product_name), |
97 | udev.idVendor, udev.idProduct); |
98 | usbip_names_get_class(class_name, sizeof(class_name), |
99 | udev.bDeviceClass, udev.bDeviceSubClass, |
100 | udev.bDeviceProtocol); |
101 | printf("%11s: %s\n" , udev.busid, product_name); |
102 | printf("%11s: %s\n" , "" , udev.path); |
103 | printf("%11s: %s\n" , "" , class_name); |
104 | |
105 | for (j = 0; j < udev.bNumInterfaces; j++) { |
106 | rc = usbip_net_recv(sockfd, buff: &uintf, bufflen: sizeof(uintf)); |
107 | if (rc < 0) { |
108 | err("usbip_net_recv failed: usbip_usb_intf[%d]" , |
109 | j); |
110 | |
111 | return -1; |
112 | } |
113 | usbip_net_pack_usb_interface(pack: 0, uinf: &uintf); |
114 | |
115 | usbip_names_get_class(class_name, sizeof(class_name), |
116 | uintf.bInterfaceClass, |
117 | uintf.bInterfaceSubClass, |
118 | uintf.bInterfaceProtocol); |
119 | printf("%11s: %2d - %s\n" , "" , j, class_name); |
120 | } |
121 | |
122 | printf("\n" ); |
123 | } |
124 | |
125 | return 0; |
126 | } |
127 | |
128 | static int list_exported_devices(char *host) |
129 | { |
130 | int rc; |
131 | int sockfd; |
132 | |
133 | sockfd = usbip_net_tcp_connect(hostname: host, port: usbip_port_string); |
134 | if (sockfd < 0) { |
135 | err("could not connect to %s:%s: %s" , host, |
136 | usbip_port_string, gai_strerror(sockfd)); |
137 | return -1; |
138 | } |
139 | dbg("connected to %s:%s" , host, usbip_port_string); |
140 | |
141 | rc = get_exported_devices(host, sockfd); |
142 | if (rc < 0) { |
143 | err("failed to get device list from %s" , host); |
144 | return -1; |
145 | } |
146 | |
147 | close(sockfd); |
148 | |
149 | return 0; |
150 | } |
151 | |
152 | static void print_device(const char *busid, const char *vendor, |
153 | const char *product, bool parsable) |
154 | { |
155 | if (parsable) |
156 | printf("busid=%s#usbid=%.4s:%.4s#" , busid, vendor, product); |
157 | else |
158 | printf(" - busid %s (%.4s:%.4s)\n" , busid, vendor, product); |
159 | } |
160 | |
161 | static void print_product_name(char *product_name, bool parsable) |
162 | { |
163 | if (!parsable) |
164 | printf(" %s\n" , product_name); |
165 | } |
166 | |
167 | static int list_devices(bool parsable) |
168 | { |
169 | struct udev *udev; |
170 | struct udev_enumerate *enumerate; |
171 | struct udev_list_entry *devices, *dev_list_entry; |
172 | struct udev_device *dev; |
173 | const char *path; |
174 | const char *idVendor; |
175 | const char *idProduct; |
176 | const char *bConfValue; |
177 | const char *bNumIntfs; |
178 | const char *busid; |
179 | char product_name[128]; |
180 | int ret = -1; |
181 | const char *devpath; |
182 | |
183 | /* Create libudev context. */ |
184 | udev = udev_new(); |
185 | |
186 | /* Create libudev device enumeration. */ |
187 | enumerate = udev_enumerate_new(udev); |
188 | |
189 | /* Take only USB devices that are not hubs and do not have |
190 | * the bInterfaceNumber attribute, i.e. are not interfaces. |
191 | */ |
192 | udev_enumerate_add_match_subsystem(enumerate, "usb" ); |
193 | udev_enumerate_add_nomatch_sysattr(enumerate, "bDeviceClass" , "09" ); |
194 | udev_enumerate_add_nomatch_sysattr(enumerate, "bInterfaceNumber" , NULL); |
195 | udev_enumerate_scan_devices(enumerate); |
196 | |
197 | devices = udev_enumerate_get_list_entry(enumerate); |
198 | |
199 | /* Show information about each device. */ |
200 | udev_list_entry_foreach(dev_list_entry, devices) { |
201 | path = udev_list_entry_get_name(dev_list_entry); |
202 | dev = udev_device_new_from_syspath(udev, path); |
203 | |
204 | /* Ignore devices attached to vhci_hcd */ |
205 | devpath = udev_device_get_devpath(dev); |
206 | if (strstr(devpath, USBIP_VHCI_DRV_NAME)) { |
207 | dbg("Skip the device %s already attached to %s\n" , |
208 | devpath, USBIP_VHCI_DRV_NAME); |
209 | continue; |
210 | } |
211 | |
212 | /* Get device information. */ |
213 | idVendor = udev_device_get_sysattr_value(dev, "idVendor" ); |
214 | idProduct = udev_device_get_sysattr_value(dev, "idProduct" ); |
215 | bConfValue = udev_device_get_sysattr_value(dev, |
216 | "bConfigurationValue" ); |
217 | bNumIntfs = udev_device_get_sysattr_value(dev, |
218 | "bNumInterfaces" ); |
219 | busid = udev_device_get_sysname(dev); |
220 | if (!idVendor || !idProduct || !bConfValue || !bNumIntfs) { |
221 | err("problem getting device attributes: %s" , |
222 | strerror(errno)); |
223 | goto err_out; |
224 | } |
225 | |
226 | /* Get product name. */ |
227 | usbip_names_get_product(product_name, sizeof(product_name), |
228 | strtol(idVendor, NULL, 16), |
229 | strtol(idProduct, NULL, 16)); |
230 | |
231 | /* Print information. */ |
232 | print_device(busid, vendor: idVendor, product: idProduct, parsable); |
233 | print_product_name(product_name, parsable); |
234 | |
235 | printf("\n" ); |
236 | |
237 | udev_device_unref(dev); |
238 | } |
239 | |
240 | ret = 0; |
241 | |
242 | err_out: |
243 | udev_enumerate_unref(enumerate); |
244 | udev_unref(udev); |
245 | |
246 | return ret; |
247 | } |
248 | |
249 | static int list_gadget_devices(bool parsable) |
250 | { |
251 | int ret = -1; |
252 | struct udev *udev; |
253 | struct udev_enumerate *enumerate; |
254 | struct udev_list_entry *devices, *dev_list_entry; |
255 | struct udev_device *dev; |
256 | const char *path; |
257 | const char *driver; |
258 | |
259 | const struct usb_device_descriptor *d_desc; |
260 | const char *descriptors; |
261 | char product_name[128]; |
262 | |
263 | uint16_t idVendor; |
264 | char idVendor_buf[8]; |
265 | uint16_t idProduct; |
266 | char idProduct_buf[8]; |
267 | const char *busid; |
268 | |
269 | udev = udev_new(); |
270 | enumerate = udev_enumerate_new(udev); |
271 | |
272 | udev_enumerate_add_match_subsystem(enumerate, "platform" ); |
273 | |
274 | udev_enumerate_scan_devices(enumerate); |
275 | devices = udev_enumerate_get_list_entry(enumerate); |
276 | |
277 | udev_list_entry_foreach(dev_list_entry, devices) { |
278 | path = udev_list_entry_get_name(dev_list_entry); |
279 | dev = udev_device_new_from_syspath(udev, path); |
280 | |
281 | driver = udev_device_get_driver(dev); |
282 | /* We only have mechanism to enumerate gadgets bound to vudc */ |
283 | if (driver == NULL || strcmp(driver, USBIP_DEVICE_DRV_NAME)) |
284 | continue; |
285 | |
286 | /* Get device information. */ |
287 | descriptors = udev_device_get_sysattr_value(dev, |
288 | VUDC_DEVICE_DESCR_FILE); |
289 | |
290 | if (!descriptors) { |
291 | err("problem getting device attributes: %s" , |
292 | strerror(errno)); |
293 | goto err_out; |
294 | } |
295 | |
296 | d_desc = (const struct usb_device_descriptor *) descriptors; |
297 | |
298 | idVendor = le16toh(d_desc->idVendor); |
299 | sprintf(idVendor_buf, "0x%4x" , idVendor); |
300 | idProduct = le16toh(d_desc->idProduct); |
301 | sprintf(idProduct_buf, "0x%4x" , idVendor); |
302 | busid = udev_device_get_sysname(dev); |
303 | |
304 | /* Get product name. */ |
305 | usbip_names_get_product(product_name, sizeof(product_name), |
306 | le16toh(idVendor), |
307 | le16toh(idProduct)); |
308 | |
309 | /* Print information. */ |
310 | print_device(busid, vendor: idVendor_buf, product: idProduct_buf, parsable); |
311 | print_product_name(product_name, parsable); |
312 | |
313 | printf("\n" ); |
314 | |
315 | udev_device_unref(dev); |
316 | } |
317 | ret = 0; |
318 | |
319 | err_out: |
320 | udev_enumerate_unref(enumerate); |
321 | udev_unref(udev); |
322 | |
323 | return ret; |
324 | } |
325 | |
326 | int usbip_list(int argc, char *argv[]) |
327 | { |
328 | static const struct option opts[] = { |
329 | { "parsable" , no_argument, NULL, 'p' }, |
330 | { "remote" , required_argument, NULL, 'r' }, |
331 | { "local" , no_argument, NULL, 'l' }, |
332 | { "device" , no_argument, NULL, 'd' }, |
333 | { NULL, 0, NULL, 0 } |
334 | }; |
335 | |
336 | bool parsable = false; |
337 | int opt; |
338 | int ret = -1; |
339 | |
340 | if (usbip_names_init(USBIDS_FILE)) |
341 | err("failed to open %s" , USBIDS_FILE); |
342 | |
343 | for (;;) { |
344 | opt = getopt_long(argc, argv, "pr:ld" , opts, NULL); |
345 | |
346 | if (opt == -1) |
347 | break; |
348 | |
349 | switch (opt) { |
350 | case 'p': |
351 | parsable = true; |
352 | break; |
353 | case 'r': |
354 | ret = list_exported_devices(host: optarg); |
355 | goto out; |
356 | case 'l': |
357 | ret = list_devices(parsable); |
358 | goto out; |
359 | case 'd': |
360 | ret = list_gadget_devices(parsable); |
361 | goto out; |
362 | default: |
363 | goto err_out; |
364 | } |
365 | } |
366 | |
367 | err_out: |
368 | usbip_list_usage(); |
369 | out: |
370 | usbip_names_free(); |
371 | |
372 | return ret; |
373 | } |
374 | |