1
2
3
4
5#include "usbip_common.h"
6#include "vhci_driver.h"
7#include <limits.h>
8#include <netdb.h>
9#include <libudev.h>
10#include <dirent.h>
11#include "sysfs_utils.h"
12
13#undef PROGNAME
14#define PROGNAME "libusbip"
15
16struct usbip_vhci_driver *vhci_driver;
17struct udev *udev_context;
18
19static struct usbip_imported_device *
20imported_device_init(struct usbip_imported_device *idev, char *busid)
21{
22 struct udev_device *sudev;
23
24 sudev = udev_device_new_from_subsystem_sysname(udev_context,
25 "usb", busid);
26 if (!sudev) {
27 dbg("udev_device_new_from_subsystem_sysname failed: %s", busid);
28 goto err;
29 }
30 read_usb_device(sudev, &idev->udev);
31 udev_device_unref(sudev);
32
33 return idev;
34
35err:
36 return NULL;
37}
38
39static int parse_status(const char *value)
40{
41 int ret = 0;
42 char *c;
43
44
45 c = strchr(value, '\n');
46 if (!c)
47 return -1;
48 c++;
49
50 while (*c != '\0') {
51 int port, status, speed, devid;
52 unsigned long socket;
53 char lbusid[SYSFS_BUS_ID_SIZE];
54 struct usbip_imported_device *idev;
55 char hub[3];
56
57 ret = sscanf(c, "%2s %d %d %d %x %lx %31s\n",
58 hub, &port, &status, &speed,
59 &devid, &socket, lbusid);
60
61 if (ret < 5) {
62 dbg("sscanf failed: %d", ret);
63 BUG();
64 }
65
66 dbg("hub %s port %d status %d speed %d devid %x",
67 hub, port, status, speed, devid);
68 dbg("socket %lx lbusid %s", socket, lbusid);
69
70
71 idev = &vhci_driver->idev[port];
72 memset(idev, 0, sizeof(*idev));
73
74 if (strncmp("hs", hub, 2) == 0)
75 idev->hub = HUB_SPEED_HIGH;
76 else
77 idev->hub = HUB_SPEED_SUPER;
78
79 idev->port = port;
80 idev->status = status;
81
82 idev->devid = devid;
83
84 idev->busnum = (devid >> 16);
85 idev->devnum = (devid & 0x0000ffff);
86
87 if (idev->status != VDEV_ST_NULL
88 && idev->status != VDEV_ST_NOTASSIGNED) {
89 idev = imported_device_init(idev, lbusid);
90 if (!idev) {
91 dbg("imported_device_init failed");
92 return -1;
93 }
94 }
95
96
97 c = strchr(c, '\n');
98 if (!c)
99 break;
100 c++;
101 }
102
103 dbg("exit");
104
105 return 0;
106}
107
108#define MAX_STATUS_NAME 16
109
110static int refresh_imported_device_list(void)
111{
112 const char *attr_status;
113 char status[MAX_STATUS_NAME+1] = "status";
114 int i, ret;
115
116 for (i = 0; i < vhci_driver->ncontrollers; i++) {
117 if (i > 0)
118 snprintf(status, sizeof(status), "status.%d", i);
119
120 attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device,
121 status);
122 if (!attr_status) {
123 err("udev_device_get_sysattr_value failed");
124 return -1;
125 }
126
127 dbg("controller %d", i);
128
129 ret = parse_status(attr_status);
130 if (ret != 0)
131 return ret;
132 }
133
134 return 0;
135}
136
137static int get_nports(void)
138{
139 const char *attr_nports;
140
141 attr_nports = udev_device_get_sysattr_value(vhci_driver->hc_device, "nports");
142 if (!attr_nports) {
143 err("udev_device_get_sysattr_value nports failed");
144 return -1;
145 }
146
147 return (int)strtoul(attr_nports, NULL, 10);
148}
149
150static int vhci_hcd_filter(const struct dirent *dirent)
151{
152 return strcmp(dirent->d_name, "vhci_hcd") >= 0;
153}
154
155static int get_ncontrollers(void)
156{
157 struct dirent **namelist;
158 struct udev_device *platform;
159 int n;
160
161 platform = udev_device_get_parent(vhci_driver->hc_device);
162 if (platform == NULL)
163 return -1;
164
165 n = scandir(udev_device_get_syspath(platform), &namelist, vhci_hcd_filter, NULL);
166 if (n < 0)
167 err("scandir failed");
168 else {
169 for (int i = 0; i < n; i++)
170 free(namelist[i]);
171 free(namelist);
172 }
173
174 return n;
175}
176
177
178
179
180
181
182
183
184
185
186static int read_record(int rhport, char *host, unsigned long host_len,
187 char *port, unsigned long port_len, char *busid)
188{
189 int part;
190 FILE *file;
191 char path[PATH_MAX+1];
192 char *buffer, *start, *end;
193 char delim[] = {' ', ' ', '\n'};
194 int max_len[] = {(int)host_len, (int)port_len, SYSFS_BUS_ID_SIZE};
195 size_t buffer_len = host_len + port_len + SYSFS_BUS_ID_SIZE + 4;
196
197 buffer = malloc(buffer_len);
198 if (!buffer)
199 return -1;
200
201 snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
202
203 file = fopen(path, "r");
204 if (!file) {
205 err("fopen");
206 free(buffer);
207 return -1;
208 }
209
210 if (fgets(buffer, buffer_len, file) == NULL) {
211 err("fgets");
212 free(buffer);
213 fclose(file);
214 return -1;
215 }
216 fclose(file);
217
218
219 start = buffer;
220 for (part = 0; part < 3; part++) {
221 end = strchr(start, delim[part]);
222 if (end == NULL || (end - start) > max_len[part]) {
223 free(buffer);
224 return -1;
225 }
226 start = end + 1;
227 }
228
229 if (sscanf(buffer, "%s %s %s\n", host, port, busid) != 3) {
230 err("sscanf");
231 free(buffer);
232 return -1;
233 }
234
235 free(buffer);
236
237 return 0;
238}
239
240
241
242int usbip_vhci_driver_open(void)
243{
244 udev_context = udev_new();
245 if (!udev_context) {
246 err("udev_new failed");
247 return -1;
248 }
249
250 vhci_driver = calloc(1, sizeof(struct usbip_vhci_driver));
251
252
253 vhci_driver->hc_device =
254 udev_device_new_from_subsystem_sysname(udev_context,
255 USBIP_VHCI_BUS_TYPE,
256 USBIP_VHCI_DEVICE_NAME);
257 if (!vhci_driver->hc_device) {
258 err("udev_device_new_from_subsystem_sysname failed");
259 goto err;
260 }
261
262 vhci_driver->nports = get_nports();
263 dbg("available ports: %d", vhci_driver->nports);
264
265 if (vhci_driver->nports <= 0) {
266 err("no available ports");
267 goto err;
268 } else if (vhci_driver->nports > MAXNPORT) {
269 err("port number exceeds %d", MAXNPORT);
270 goto err;
271 }
272
273 vhci_driver->ncontrollers = get_ncontrollers();
274 dbg("available controllers: %d", vhci_driver->ncontrollers);
275
276 if (vhci_driver->ncontrollers <=0) {
277 err("no available usb controllers");
278 goto err;
279 }
280
281 if (refresh_imported_device_list())
282 goto err;
283
284 return 0;
285
286err:
287 udev_device_unref(vhci_driver->hc_device);
288
289 if (vhci_driver)
290 free(vhci_driver);
291
292 vhci_driver = NULL;
293
294 udev_unref(udev_context);
295
296 return -1;
297}
298
299
300void usbip_vhci_driver_close(void)
301{
302 if (!vhci_driver)
303 return;
304
305 udev_device_unref(vhci_driver->hc_device);
306
307 free(vhci_driver);
308
309 vhci_driver = NULL;
310
311 udev_unref(udev_context);
312}
313
314
315int usbip_vhci_refresh_device_list(void)
316{
317
318 if (refresh_imported_device_list())
319 goto err;
320
321 return 0;
322err:
323 dbg("failed to refresh device list");
324 return -1;
325}
326
327
328int usbip_vhci_get_free_port(uint32_t speed)
329{
330 for (int i = 0; i < vhci_driver->nports; i++) {
331 if (speed == USB_SPEED_SUPER &&
332 vhci_driver->idev[i].hub != HUB_SPEED_SUPER)
333 continue;
334
335 if (vhci_driver->idev[i].status == VDEV_ST_NULL)
336 return vhci_driver->idev[i].port;
337 }
338
339 return -1;
340}
341
342int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
343 uint32_t speed) {
344 char buff[200];
345 char attach_attr_path[SYSFS_PATH_MAX];
346 char attr_attach[] = "attach";
347 const char *path;
348 int ret;
349
350 snprintf(buff, sizeof(buff), "%u %d %u %u",
351 port, sockfd, devid, speed);
352 dbg("writing: %s", buff);
353
354 path = udev_device_get_syspath(vhci_driver->hc_device);
355 snprintf(attach_attr_path, sizeof(attach_attr_path), "%s/%s",
356 path, attr_attach);
357 dbg("attach attribute path: %s", attach_attr_path);
358
359 ret = write_sysfs_attribute(attach_attr_path, buff, strlen(buff));
360 if (ret < 0) {
361 dbg("write_sysfs_attribute failed");
362 return -1;
363 }
364
365 dbg("attached port: %d", port);
366
367 return 0;
368}
369
370static unsigned long get_devid(uint8_t busnum, uint8_t devnum)
371{
372 return (busnum << 16) | devnum;
373}
374
375
376int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
377 uint8_t devnum, uint32_t speed)
378{
379 int devid = get_devid(busnum, devnum);
380
381 return usbip_vhci_attach_device2(port, sockfd, devid, speed);
382}
383
384int usbip_vhci_detach_device(uint8_t port)
385{
386 char detach_attr_path[SYSFS_PATH_MAX];
387 char attr_detach[] = "detach";
388 char buff[200];
389 const char *path;
390 int ret;
391
392 snprintf(buff, sizeof(buff), "%u", port);
393 dbg("writing: %s", buff);
394
395 path = udev_device_get_syspath(vhci_driver->hc_device);
396 snprintf(detach_attr_path, sizeof(detach_attr_path), "%s/%s",
397 path, attr_detach);
398 dbg("detach attribute path: %s", detach_attr_path);
399
400 ret = write_sysfs_attribute(detach_attr_path, buff, strlen(buff));
401 if (ret < 0) {
402 dbg("write_sysfs_attribute failed");
403 return -1;
404 }
405
406 dbg("detached port: %d", port);
407
408 return 0;
409}
410
411int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev)
412{
413 char product_name[100];
414 char host[NI_MAXHOST] = "unknown host";
415 char serv[NI_MAXSERV] = "unknown port";
416 char remote_busid[SYSFS_BUS_ID_SIZE];
417 int ret;
418 int read_record_error = 0;
419
420 if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED)
421 return 0;
422
423 ret = read_record(idev->port, host, sizeof(host), serv, sizeof(serv),
424 remote_busid);
425 if (ret) {
426 err("read_record");
427 read_record_error = 1;
428 }
429
430 printf("Port %02d: <%s> at %s\n", idev->port,
431 usbip_status_string(idev->status),
432 usbip_speed_string(idev->udev.speed));
433
434 usbip_names_get_product(product_name, sizeof(product_name),
435 idev->udev.idVendor, idev->udev.idProduct);
436
437 printf(" %s\n", product_name);
438
439 if (!read_record_error) {
440 printf("%10s -> usbip://%s:%s/%s\n", idev->udev.busid,
441 host, serv, remote_busid);
442 printf("%10s -> remote bus/dev %03d/%03d\n", " ",
443 idev->busnum, idev->devnum);
444 } else {
445 printf("%10s -> unknown host, remote port and remote busid\n",
446 idev->udev.busid);
447 printf("%10s -> remote bus/dev %03d/%03d\n", " ",
448 idev->busnum, idev->devnum);
449 }
450
451 return 0;
452}
453