Commit | Line | Data |
---|---|---|
e9837bbb | 1 | /* |
2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | |
3 | * 2005-2007 Takahiro Hirofuchi | |
4 | * | |
5 | * This program is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include <sys/types.h> | |
20 | #include <sysfs/libsysfs.h> | |
21 | ||
22 | #include <errno.h> | |
23 | #include <stdbool.h> | |
24 | #include <stdint.h> | |
25 | #include <stdio.h> | |
74ce259c | 26 | #include <stdlib.h> |
e9837bbb | 27 | #include <string.h> |
28 | ||
e9837bbb | 29 | #include <getopt.h> |
30 | #include <netdb.h> | |
e9837bbb | 31 | #include <unistd.h> |
32 | ||
33 | #include "usbip_common.h" | |
34 | #include "usbip_network.h" | |
e9837bbb | 35 | #include "usbip.h" |
36 | ||
37 | static const char usbip_list_usage_string[] = | |
38 | "usbip list [-p|--parsable] <args>\n" | |
39 | " -p, --parsable Parsable list format\n" | |
2435ab14 | 40 | " -r, --remote=<host> List the exportable USB devices on <host>\n" |
e9837bbb | 41 | " -l, --local List the local USB devices\n"; |
42 | ||
43 | void usbip_list_usage(void) | |
44 | { | |
45 | printf("usage: %s", usbip_list_usage_string); | |
46 | } | |
47 | ||
b9d65b1d | 48 | static int get_exported_devices(char *host, int sockfd) |
e9837bbb | 49 | { |
11095664 | 50 | char product_name[100]; |
51 | char class_name[100]; | |
622dde81 | 52 | struct op_devlist_reply reply; |
e9837bbb | 53 | uint16_t code = OP_REP_DEVLIST; |
11095664 | 54 | struct usbip_usb_device udev; |
55 | struct usbip_usb_interface uintf; | |
56 | unsigned int i; | |
57 | int j, rc; | |
58 | ||
3c6e9e8f | 59 | rc = usbip_net_send_op_common(sockfd, OP_REQ_DEVLIST, 0); |
11095664 | 60 | if (rc < 0) { |
3c6e9e8f | 61 | dbg("usbip_net_send_op_common failed"); |
e9837bbb | 62 | return -1; |
63 | } | |
64 | ||
3c6e9e8f | 65 | rc = usbip_net_recv_op_common(sockfd, &code); |
11095664 | 66 | if (rc < 0) { |
3c6e9e8f | 67 | dbg("usbip_net_recv_op_common failed"); |
e9837bbb | 68 | return -1; |
69 | } | |
70 | ||
622dde81 | 71 | memset(&reply, 0, sizeof(reply)); |
3c6e9e8f | 72 | rc = usbip_net_recv(sockfd, &reply, sizeof(reply)); |
11095664 | 73 | if (rc < 0) { |
3c6e9e8f | 74 | dbg("usbip_net_recv_op_devlist failed"); |
e9837bbb | 75 | return -1; |
76 | } | |
622dde81 | 77 | PACK_OP_DEVLIST_REPLY(0, &reply); |
78 | dbg("exportable devices: %d\n", reply.ndev); | |
e9837bbb | 79 | |
b9d65b1d | 80 | if (reply.ndev == 0) { |
81 | info("no exportable devices found on %s", host); | |
82 | return 0; | |
83 | } | |
84 | ||
85 | printf("Exportable USB devices\n"); | |
86 | printf("======================\n"); | |
87 | printf(" - %s\n", host); | |
88 | ||
622dde81 | 89 | for (i = 0; i < reply.ndev; i++) { |
e9837bbb | 90 | memset(&udev, 0, sizeof(udev)); |
3c6e9e8f | 91 | rc = usbip_net_recv(sockfd, &udev, sizeof(udev)); |
11095664 | 92 | if (rc < 0) { |
3c6e9e8f | 93 | dbg("usbip_net_recv failed: usbip_usb_device[%d]", i); |
e9837bbb | 94 | return -1; |
95 | } | |
3c6e9e8f | 96 | usbip_net_pack_usb_device(0, &udev); |
e9837bbb | 97 | |
98 | usbip_names_get_product(product_name, sizeof(product_name), | |
99 | udev.idVendor, udev.idProduct); | |
100 | usbip_names_get_class(class_name, sizeof(class_name), | |
101 | udev.bDeviceClass, udev.bDeviceSubClass, | |
102 | udev.bDeviceProtocol); | |
fcedd169 | 103 | printf("%11s: %s\n", udev.busid, product_name); |
104 | printf("%11s: %s\n", "", udev.path); | |
105 | printf("%11s: %s\n", "", class_name); | |
e9837bbb | 106 | |
11095664 | 107 | for (j = 0; j < udev.bNumInterfaces; j++) { |
3c6e9e8f | 108 | rc = usbip_net_recv(sockfd, &uintf, sizeof(uintf)); |
11095664 | 109 | if (rc < 0) { |
3c6e9e8f | 110 | dbg("usbip_net_recv failed: usbip_usb_intf[%d]", |
111 | j); | |
112 | ||
e9837bbb | 113 | return -1; |
114 | } | |
3c6e9e8f | 115 | usbip_net_pack_usb_interface(0, &uintf); |
e9837bbb | 116 | |
e9837bbb | 117 | usbip_names_get_class(class_name, sizeof(class_name), |
11095664 | 118 | uintf.bInterfaceClass, |
119 | uintf.bInterfaceSubClass, | |
120 | uintf.bInterfaceProtocol); | |
fcedd169 | 121 | printf("%11s: %2d - %s\n", "", j, class_name); |
e9837bbb | 122 | } |
e9837bbb | 123 | printf("\n"); |
124 | } | |
125 | ||
11095664 | 126 | return 0; |
e9837bbb | 127 | } |
128 | ||
11095664 | 129 | static int list_exported_devices(char *host) |
e9837bbb | 130 | { |
11095664 | 131 | int rc; |
e9837bbb | 132 | int sockfd; |
133 | ||
7182f8f8 | 134 | sockfd = usbip_net_tcp_connect(host, usbip_port_string); |
e9837bbb | 135 | if (sockfd < 0) { |
622dde81 | 136 | err("could not connect to %s:%s: %s", host, |
7182f8f8 | 137 | usbip_port_string, gai_strerror(sockfd)); |
e9837bbb | 138 | return -1; |
139 | } | |
7182f8f8 | 140 | dbg("connected to %s:%s", host, usbip_port_string); |
622dde81 | 141 | |
b9d65b1d | 142 | rc = get_exported_devices(host, sockfd); |
11095664 | 143 | if (rc < 0) { |
622dde81 | 144 | err("failed to get device list from %s", host); |
e9837bbb | 145 | return -1; |
146 | } | |
147 | ||
148 | close(sockfd); | |
11095664 | 149 | |
e9837bbb | 150 | return 0; |
151 | } | |
152 | ||
74ce259c | 153 | static void print_device(char *busid, char *vendor, char *product, |
154 | bool parsable) | |
e9837bbb | 155 | { |
74ce259c | 156 | if (parsable) |
157 | printf("busid=%s#usbid=%.4s:%.4s#", busid, vendor, product); | |
158 | else | |
159 | printf(" - busid %s (%.4s:%.4s)\n", busid, vendor, product); | |
e9837bbb | 160 | } |
161 | ||
70e90fb5 KK |
162 | static void print_product_name(char *product_name, bool parsable) |
163 | { | |
164 | if (!parsable) | |
165 | printf(" %s\n", product_name); | |
166 | } | |
167 | ||
74ce259c | 168 | static void print_interface(char *busid, char *driver, bool parsable) |
e9837bbb | 169 | { |
74ce259c | 170 | if (parsable) |
171 | printf("%s=%s#", busid, driver); | |
172 | else | |
173 | printf("%9s%s -> %s\n", "", busid, driver); | |
e9837bbb | 174 | } |
175 | ||
74ce259c | 176 | static int is_device(void *x) |
e9837bbb | 177 | { |
74ce259c | 178 | struct sysfs_attribute *devpath; |
179 | struct sysfs_device *dev = x; | |
180 | int ret = 0; | |
e9837bbb | 181 | |
74ce259c | 182 | devpath = sysfs_get_device_attr(dev, "devpath"); |
183 | if (devpath && *devpath->value != '0') | |
184 | ret = 1; | |
e9837bbb | 185 | |
74ce259c | 186 | return ret; |
187 | } | |
e9837bbb | 188 | |
74ce259c | 189 | static int devcmp(void *a, void *b) |
190 | { | |
191 | return strcmp(a, b); | |
192 | } | |
e9837bbb | 193 | |
74ce259c | 194 | static int list_devices(bool parsable) |
195 | { | |
196 | char bus_type[] = "usb"; | |
197 | char busid[SYSFS_BUS_ID_SIZE]; | |
70e90fb5 | 198 | char product_name[128]; |
74ce259c | 199 | struct sysfs_bus *ubus; |
200 | struct sysfs_device *dev; | |
201 | struct sysfs_device *intf; | |
202 | struct sysfs_attribute *idVendor; | |
203 | struct sysfs_attribute *idProduct; | |
204 | struct sysfs_attribute *bConfValue; | |
205 | struct sysfs_attribute *bNumIntfs; | |
206 | struct dlist *devlist; | |
207 | int i; | |
208 | int ret = -1; | |
e9837bbb | 209 | |
74ce259c | 210 | ubus = sysfs_open_bus(bus_type); |
211 | if (!ubus) { | |
622dde81 | 212 | err("could not open %s bus: %s", bus_type, strerror(errno)); |
74ce259c | 213 | return -1; |
214 | } | |
e9837bbb | 215 | |
74ce259c | 216 | devlist = sysfs_get_bus_devices(ubus); |
217 | if (!devlist) { | |
622dde81 | 218 | err("could not get %s bus devices: %s", bus_type, |
219 | strerror(errno)); | |
74ce259c | 220 | goto err_out; |
221 | } | |
e9837bbb | 222 | |
74ce259c | 223 | /* remove interfaces and root hubs from device list */ |
224 | dlist_filter_sort(devlist, is_device, devcmp); | |
e9837bbb | 225 | |
74ce259c | 226 | if (!parsable) { |
227 | printf("Local USB devices\n"); | |
228 | printf("=================\n"); | |
229 | } | |
230 | dlist_for_each_data(devlist, dev, struct sysfs_device) { | |
231 | idVendor = sysfs_get_device_attr(dev, "idVendor"); | |
232 | idProduct = sysfs_get_device_attr(dev, "idProduct"); | |
233 | bConfValue = sysfs_get_device_attr(dev, "bConfigurationValue"); | |
234 | bNumIntfs = sysfs_get_device_attr(dev, "bNumInterfaces"); | |
622dde81 | 235 | if (!idVendor || !idProduct || !bConfValue || !bNumIntfs) { |
236 | err("problem getting device attributes: %s", | |
237 | strerror(errno)); | |
74ce259c | 238 | goto err_out; |
622dde81 | 239 | } |
e9837bbb | 240 | |
70e90fb5 KK |
241 | /* get product name */ |
242 | usbip_names_get_product(product_name, sizeof(product_name), | |
243 | strtol(idVendor->value, NULL, 16), | |
244 | strtol(idProduct->value, NULL, 16)); | |
74ce259c | 245 | print_device(dev->bus_id, idVendor->value, idProduct->value, |
246 | parsable); | |
70e90fb5 | 247 | print_product_name(product_name, parsable); |
74ce259c | 248 | |
249 | for (i = 0; i < atoi(bNumIntfs->value); i++) { | |
250 | snprintf(busid, sizeof(busid), "%s:%.1s.%d", | |
251 | dev->bus_id, bConfValue->value, i); | |
252 | intf = sysfs_open_device(bus_type, busid); | |
622dde81 | 253 | if (!intf) { |
254 | err("could not open device interface: %s", | |
255 | strerror(errno)); | |
74ce259c | 256 | goto err_out; |
622dde81 | 257 | } |
74ce259c | 258 | print_interface(busid, intf->driver_name, parsable); |
259 | sysfs_close_device(intf); | |
e9837bbb | 260 | } |
74ce259c | 261 | printf("\n"); |
e9837bbb | 262 | } |
263 | ||
74ce259c | 264 | ret = 0; |
e9837bbb | 265 | |
74ce259c | 266 | err_out: |
267 | sysfs_close_bus(ubus); | |
268 | ||
269 | return ret; | |
e9837bbb | 270 | } |
271 | ||
272 | int usbip_list(int argc, char *argv[]) | |
273 | { | |
274 | static const struct option opts[] = { | |
622dde81 | 275 | { "parsable", no_argument, NULL, 'p' }, |
276 | { "remote", required_argument, NULL, 'r' }, | |
277 | { "local", no_argument, NULL, 'l' }, | |
278 | { NULL, 0, NULL, 0 } | |
e9837bbb | 279 | }; |
622dde81 | 280 | |
74ce259c | 281 | bool parsable = false; |
e9837bbb | 282 | int opt; |
283 | int ret = -1; | |
284 | ||
285 | if (usbip_names_init(USBIDS_FILE)) | |
74ce259c | 286 | err("failed to open %s", USBIDS_FILE); |
e9837bbb | 287 | |
288 | for (;;) { | |
289 | opt = getopt_long(argc, argv, "pr:l", opts, NULL); | |
290 | ||
291 | if (opt == -1) | |
292 | break; | |
293 | ||
294 | switch (opt) { | |
295 | case 'p': | |
74ce259c | 296 | parsable = true; |
e9837bbb | 297 | break; |
298 | case 'r': | |
11095664 | 299 | ret = list_exported_devices(optarg); |
e9837bbb | 300 | goto out; |
301 | case 'l': | |
74ce259c | 302 | ret = list_devices(parsable); |
e9837bbb | 303 | goto out; |
304 | default: | |
305 | goto err_out; | |
306 | } | |
307 | } | |
308 | ||
309 | err_out: | |
310 | usbip_list_usage(); | |
311 | out: | |
312 | usbip_names_free(); | |
313 | ||
314 | return ret; | |
315 | } |