Commit | Line | Data |
---|---|---|
0945b4fe TH |
1 | /* |
2 | * Copyright (C) 2005-2007 Takahiro Hirofuchi | |
3 | */ | |
4 | ||
099f79fa | 5 | #include "usbip_common.h" |
6 | #include "vhci_driver.h" | |
0945b4fe | 7 | |
f2fb62b3 | 8 | #undef PROGNAME |
9 | #define PROGNAME "libusbip" | |
10 | ||
0945b4fe TH |
11 | struct usbip_vhci_driver *vhci_driver; |
12 | ||
13 | static struct usbip_imported_device *imported_device_init(struct usbip_imported_device *idev, char *busid) | |
14 | { | |
15 | struct sysfs_device *sudev; | |
16 | ||
17 | sudev = sysfs_open_device("usb", busid); | |
18 | if (!sudev) { | |
25567a39 | 19 | dbg("sysfs_open_device failed: %s", busid); |
0945b4fe TH |
20 | goto err; |
21 | } | |
22 | read_usb_device(sudev, &idev->udev); | |
23 | sysfs_close_device(sudev); | |
24 | ||
25 | /* add class devices of this imported device */ | |
7e485ee7 | 26 | struct usbip_class_device *cdev; |
27 | dlist_for_each_data(vhci_driver->cdev_list, cdev, | |
28 | struct usbip_class_device) { | |
29 | if (!strncmp(cdev->dev_path, idev->udev.path, | |
30 | strlen(idev->udev.path))) { | |
31 | struct usbip_class_device *new_cdev; | |
0945b4fe TH |
32 | |
33 | /* alloc and copy because dlist is linked from only one list */ | |
34 | new_cdev = calloc(1, sizeof(*new_cdev)); | |
35 | if (!new_cdev) | |
36 | goto err; | |
37 | ||
38 | memcpy(new_cdev, cdev, sizeof(*new_cdev)); | |
71bd5b76 | 39 | dlist_unshift(idev->cdev_list, (void *) new_cdev); |
0945b4fe TH |
40 | } |
41 | } | |
42 | ||
43 | return idev; | |
44 | ||
45 | err: | |
46 | return NULL; | |
47 | } | |
48 | ||
49 | ||
50 | ||
51 | static int parse_status(char *value) | |
52 | { | |
53 | int ret = 0; | |
54 | char *c; | |
55 | ||
56 | ||
57 | for (int i = 0; i < vhci_driver->nports; i++) | |
950a4cd8 | 58 | memset(&vhci_driver->idev[i], 0, sizeof(vhci_driver->idev[i])); |
0945b4fe TH |
59 | |
60 | ||
61 | /* skip a header line */ | |
2f5c638c CH |
62 | c = strchr(value, '\n'); |
63 | if (!c) | |
64 | return -1; | |
65 | c++; | |
0945b4fe TH |
66 | |
67 | while (*c != '\0') { | |
68 | int port, status, speed, devid; | |
69 | unsigned long socket; | |
70 | char lbusid[SYSFS_BUS_ID_SIZE]; | |
71 | ||
72 | ret = sscanf(c, "%d %d %d %x %lx %s\n", | |
73 | &port, &status, &speed, | |
74 | &devid, &socket, lbusid); | |
75 | ||
76 | if (ret < 5) { | |
25567a39 | 77 | dbg("sscanf failed: %d", ret); |
0945b4fe TH |
78 | BUG(); |
79 | } | |
80 | ||
81 | dbg("port %d status %d speed %d devid %x", | |
82 | port, status, speed, devid); | |
83 | dbg("socket %lx lbusid %s", socket, lbusid); | |
84 | ||
85 | ||
86 | /* if a device is connected, look at it */ | |
87 | { | |
88 | struct usbip_imported_device *idev = &vhci_driver->idev[port]; | |
89 | ||
90 | idev->port = port; | |
91 | idev->status = status; | |
92 | ||
93 | idev->devid = devid; | |
94 | ||
95 | idev->busnum = (devid >> 16); | |
96 | idev->devnum = (devid & 0x0000ffff); | |
97 | ||
7e485ee7 | 98 | idev->cdev_list = dlist_new(sizeof(struct usbip_class_device)); |
0945b4fe | 99 | if (!idev->cdev_list) { |
25567a39 | 100 | dbg("dlist_new failed"); |
0945b4fe TH |
101 | return -1; |
102 | } | |
103 | ||
104 | if (idev->status != VDEV_ST_NULL && idev->status != VDEV_ST_NOTASSIGNED) { | |
105 | idev = imported_device_init(idev, lbusid); | |
106 | if (!idev) { | |
25567a39 | 107 | dbg("imported_device_init failed"); |
0945b4fe TH |
108 | return -1; |
109 | } | |
110 | } | |
111 | } | |
112 | ||
113 | ||
114 | /* go to the next line */ | |
2f5c638c CH |
115 | c = strchr(c, '\n'); |
116 | if (!c) | |
117 | break; | |
118 | c++; | |
0945b4fe TH |
119 | } |
120 | ||
121 | dbg("exit"); | |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
126 | ||
127 | static int check_usbip_device(struct sysfs_class_device *cdev) | |
128 | { | |
7e485ee7 | 129 | char class_path[SYSFS_PATH_MAX]; /* /sys/class/video4linux/video0/device */ |
130 | char dev_path[SYSFS_PATH_MAX]; /* /sys/devices/platform/vhci_hcd/usb6/6-1:1.1 */ | |
0945b4fe | 131 | int ret; |
7e485ee7 | 132 | struct usbip_class_device *usbip_cdev; |
0945b4fe | 133 | |
7e485ee7 | 134 | snprintf(class_path, sizeof(class_path), "%s/device", cdev->path); |
0945b4fe | 135 | |
7e485ee7 | 136 | ret = sysfs_get_link(class_path, dev_path, sizeof(dev_path)); |
137 | if (ret == 0) { | |
138 | if (!strncmp(dev_path, vhci_driver->hc_device->path, | |
139 | strlen(vhci_driver->hc_device->path))) { | |
0945b4fe | 140 | /* found usbip device */ |
7e485ee7 | 141 | usbip_cdev = calloc(1, sizeof(*usbip_cdev)); |
93583548 | 142 | if (!usbip_cdev) { |
25567a39 | 143 | dbg("calloc failed"); |
0945b4fe TH |
144 | return -1; |
145 | } | |
7e485ee7 | 146 | dlist_unshift(vhci_driver->cdev_list, usbip_cdev); |
147 | strncpy(usbip_cdev->class_path, class_path, | |
148 | sizeof(usbip_cdev->class_path)); | |
149 | strncpy(usbip_cdev->dev_path, dev_path, | |
150 | sizeof(usbip_cdev->dev_path)); | |
25567a39 | 151 | dbg("found: %s %s", class_path, dev_path); |
0945b4fe TH |
152 | } |
153 | } | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
158 | ||
159 | static int search_class_for_usbip_device(char *cname) | |
160 | { | |
161 | struct sysfs_class *class; | |
162 | struct dlist *cdev_list; | |
163 | struct sysfs_class_device *cdev; | |
164 | int ret = 0; | |
165 | ||
166 | class = sysfs_open_class(cname); | |
167 | if (!class) { | |
25567a39 | 168 | dbg("sysfs_open_class failed"); |
0945b4fe TH |
169 | return -1; |
170 | } | |
171 | ||
25567a39 | 172 | dbg("class: %s", class->name); |
0945b4fe TH |
173 | |
174 | cdev_list = sysfs_get_class_devices(class); | |
175 | if (!cdev_list) | |
176 | /* nothing */ | |
177 | goto out; | |
178 | ||
179 | dlist_for_each_data(cdev_list, cdev, struct sysfs_class_device) { | |
25567a39 | 180 | dbg("cdev: %s", cdev->name); |
0945b4fe TH |
181 | ret = check_usbip_device(cdev); |
182 | if (ret < 0) | |
183 | goto out; | |
184 | } | |
185 | ||
186 | out: | |
187 | sysfs_close_class(class); | |
188 | ||
189 | return ret; | |
190 | } | |
191 | ||
192 | ||
193 | static int refresh_class_device_list(void) | |
194 | { | |
195 | int ret; | |
196 | struct dlist *cname_list; | |
197 | char *cname; | |
58058422 | 198 | char sysfs_mntpath[SYSFS_PATH_MAX]; |
199 | char class_path[SYSFS_PATH_MAX]; | |
200 | ||
201 | ret = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX); | |
202 | if (ret < 0) { | |
25567a39 | 203 | dbg("sysfs_get_mnt_path failed"); |
58058422 | 204 | return -1; |
205 | } | |
206 | ||
207 | snprintf(class_path, sizeof(class_path), "%s/%s", sysfs_mntpath, | |
208 | SYSFS_CLASS_NAME); | |
0945b4fe TH |
209 | |
210 | /* search under /sys/class */ | |
58058422 | 211 | cname_list = sysfs_open_directory_list(class_path); |
0945b4fe | 212 | if (!cname_list) { |
25567a39 | 213 | dbg("sysfs_open_directory failed"); |
0945b4fe TH |
214 | return -1; |
215 | } | |
216 | ||
217 | dlist_for_each_data(cname_list, cname, char) { | |
218 | ret = search_class_for_usbip_device(cname); | |
219 | if (ret < 0) { | |
220 | sysfs_close_list(cname_list); | |
221 | return -1; | |
222 | } | |
223 | } | |
224 | ||
225 | sysfs_close_list(cname_list); | |
226 | ||
227 | /* seach under /sys/block */ | |
228 | ret = search_class_for_usbip_device(SYSFS_BLOCK_NAME); | |
229 | if (ret < 0) | |
230 | return -1; | |
231 | ||
232 | return 0; | |
233 | } | |
234 | ||
235 | ||
236 | static int refresh_imported_device_list(void) | |
237 | { | |
238 | struct sysfs_attribute *attr_status; | |
239 | ||
240 | ||
241 | attr_status = sysfs_get_device_attr(vhci_driver->hc_device, "status"); | |
242 | if (!attr_status) { | |
25567a39 | 243 | dbg("sysfs_get_device_attr(\"status\") failed: %s", |
244 | vhci_driver->hc_device->name); | |
0945b4fe TH |
245 | return -1; |
246 | } | |
247 | ||
25567a39 | 248 | dbg("name: %s path: %s len: %d method: %d value: %s", |
249 | attr_status->name, attr_status->path, attr_status->len, | |
250 | attr_status->method, attr_status->value); | |
0945b4fe TH |
251 | |
252 | return parse_status(attr_status->value); | |
253 | } | |
254 | ||
255 | static int get_nports(void) | |
256 | { | |
25567a39 | 257 | char *c; |
0945b4fe TH |
258 | int nports = 0; |
259 | struct sysfs_attribute *attr_status; | |
260 | ||
261 | attr_status = sysfs_get_device_attr(vhci_driver->hc_device, "status"); | |
262 | if (!attr_status) { | |
25567a39 | 263 | dbg("sysfs_get_device_attr(\"status\") failed: %s", |
264 | vhci_driver->hc_device->name); | |
0945b4fe TH |
265 | return -1; |
266 | } | |
267 | ||
25567a39 | 268 | dbg("name: %s path: %s len: %d method: %d value: %s", |
269 | attr_status->name, attr_status->path, attr_status->len, | |
270 | attr_status->method, attr_status->value); | |
0945b4fe | 271 | |
25567a39 | 272 | /* skip a header line */ |
2f5c638c CH |
273 | c = strchr(attr_status->value, '\n'); |
274 | if (!c) | |
275 | return 0; | |
276 | c++; | |
0945b4fe | 277 | |
25567a39 | 278 | while (*c != '\0') { |
279 | /* go to the next line */ | |
2f5c638c CH |
280 | c = strchr(c, '\n'); |
281 | if (!c) | |
282 | return nports; | |
283 | c++; | |
25567a39 | 284 | nports += 1; |
0945b4fe TH |
285 | } |
286 | ||
287 | return nports; | |
288 | } | |
289 | ||
290 | static int get_hc_busid(char *sysfs_mntpath, char *hc_busid) | |
291 | { | |
cbb86718 KK |
292 | struct sysfs_driver *sdriver; |
293 | char sdriver_path[SYSFS_PATH_MAX]; | |
0945b4fe TH |
294 | |
295 | struct sysfs_device *hc_dev; | |
296 | struct dlist *hc_devs; | |
297 | ||
298 | int found = 0; | |
299 | ||
cbb86718 KK |
300 | snprintf(sdriver_path, SYSFS_PATH_MAX, "%s/%s/%s/%s/%s", sysfs_mntpath, |
301 | SYSFS_BUS_NAME, USBIP_VHCI_BUS_TYPE, SYSFS_DRIVERS_NAME, | |
302 | USBIP_VHCI_DRV_NAME); | |
0945b4fe | 303 | |
cbb86718 KK |
304 | sdriver = sysfs_open_driver_path(sdriver_path); |
305 | if (!sdriver) { | |
25567a39 | 306 | dbg("sysfs_open_driver_path failed: %s", sdriver_path); |
cbb86718 | 307 | dbg("make sure " USBIP_CORE_MOD_NAME ".ko and " |
25567a39 | 308 | USBIP_VHCI_DRV_NAME ".ko are loaded!"); |
cbb86718 KK |
309 | return -1; |
310 | } | |
0945b4fe TH |
311 | |
312 | hc_devs = sysfs_get_driver_devices(sdriver); | |
313 | if (!hc_devs) { | |
25567a39 | 314 | dbg("sysfs_get_driver failed"); |
0945b4fe TH |
315 | goto err; |
316 | } | |
317 | ||
318 | /* assume only one vhci_hcd */ | |
319 | dlist_for_each_data(hc_devs, hc_dev, struct sysfs_device) { | |
320 | strncpy(hc_busid, hc_dev->bus_id, SYSFS_BUS_ID_SIZE); | |
321 | found = 1; | |
322 | } | |
323 | ||
324 | err: | |
325 | sysfs_close_driver(sdriver); | |
326 | ||
327 | if (found) | |
328 | return 0; | |
329 | ||
25567a39 | 330 | dbg("%s not found", hc_busid); |
0945b4fe TH |
331 | return -1; |
332 | } | |
333 | ||
334 | ||
335 | /* ---------------------------------------------------------------------- */ | |
336 | ||
337 | int usbip_vhci_driver_open(void) | |
338 | { | |
339 | int ret; | |
340 | char hc_busid[SYSFS_BUS_ID_SIZE]; | |
341 | ||
342 | vhci_driver = (struct usbip_vhci_driver *) calloc(1, sizeof(*vhci_driver)); | |
343 | if (!vhci_driver) { | |
25567a39 | 344 | dbg("calloc failed"); |
0945b4fe TH |
345 | return -1; |
346 | } | |
347 | ||
348 | ret = sysfs_get_mnt_path(vhci_driver->sysfs_mntpath, SYSFS_PATH_MAX); | |
349 | if (ret < 0) { | |
25567a39 | 350 | dbg("sysfs_get_mnt_path failed"); |
0945b4fe TH |
351 | goto err; |
352 | } | |
353 | ||
354 | ret = get_hc_busid(vhci_driver->sysfs_mntpath, hc_busid); | |
355 | if (ret < 0) | |
356 | goto err; | |
357 | ||
358 | /* will be freed in usbip_driver_close() */ | |
58058422 | 359 | vhci_driver->hc_device = sysfs_open_device(USBIP_VHCI_BUS_TYPE, |
360 | hc_busid); | |
0945b4fe | 361 | if (!vhci_driver->hc_device) { |
25567a39 | 362 | dbg("sysfs_open_device failed"); |
0945b4fe TH |
363 | goto err; |
364 | } | |
365 | ||
366 | vhci_driver->nports = get_nports(); | |
367 | ||
25567a39 | 368 | dbg("available ports: %d", vhci_driver->nports); |
0945b4fe | 369 | |
7e485ee7 | 370 | vhci_driver->cdev_list = dlist_new(sizeof(struct usbip_class_device)); |
0945b4fe TH |
371 | if (!vhci_driver->cdev_list) |
372 | goto err; | |
373 | ||
374 | if (refresh_class_device_list()) | |
375 | goto err; | |
376 | ||
377 | if (refresh_imported_device_list()) | |
378 | goto err; | |
379 | ||
380 | ||
381 | return 0; | |
382 | ||
383 | ||
384 | err: | |
385 | if (vhci_driver->cdev_list) | |
386 | dlist_destroy(vhci_driver->cdev_list); | |
387 | if (vhci_driver->hc_device) | |
388 | sysfs_close_device(vhci_driver->hc_device); | |
389 | if (vhci_driver) | |
390 | free(vhci_driver); | |
391 | ||
392 | vhci_driver = NULL; | |
393 | return -1; | |
394 | } | |
395 | ||
396 | ||
397 | void usbip_vhci_driver_close() | |
398 | { | |
399 | if (!vhci_driver) | |
400 | return; | |
401 | ||
402 | if (vhci_driver->cdev_list) | |
403 | dlist_destroy(vhci_driver->cdev_list); | |
404 | ||
405 | for (int i = 0; i < vhci_driver->nports; i++) { | |
406 | if (vhci_driver->idev[i].cdev_list) | |
407 | dlist_destroy(vhci_driver->idev[i].cdev_list); | |
408 | } | |
409 | ||
410 | if (vhci_driver->hc_device) | |
411 | sysfs_close_device(vhci_driver->hc_device); | |
412 | free(vhci_driver); | |
413 | ||
414 | vhci_driver = NULL; | |
415 | } | |
416 | ||
417 | ||
418 | int usbip_vhci_refresh_device_list(void) | |
419 | { | |
420 | if (vhci_driver->cdev_list) | |
421 | dlist_destroy(vhci_driver->cdev_list); | |
422 | ||
423 | ||
424 | for (int i = 0; i < vhci_driver->nports; i++) { | |
425 | if (vhci_driver->idev[i].cdev_list) | |
426 | dlist_destroy(vhci_driver->idev[i].cdev_list); | |
427 | } | |
428 | ||
7e485ee7 | 429 | vhci_driver->cdev_list = dlist_new(sizeof(struct usbip_class_device)); |
0945b4fe TH |
430 | if (!vhci_driver->cdev_list) |
431 | goto err; | |
432 | ||
433 | if (refresh_class_device_list()) | |
434 | goto err; | |
435 | ||
436 | if (refresh_imported_device_list()) | |
437 | goto err; | |
438 | ||
439 | return 0; | |
440 | err: | |
441 | if (vhci_driver->cdev_list) | |
442 | dlist_destroy(vhci_driver->cdev_list); | |
443 | ||
444 | for (int i = 0; i < vhci_driver->nports; i++) { | |
445 | if (vhci_driver->idev[i].cdev_list) | |
446 | dlist_destroy(vhci_driver->idev[i].cdev_list); | |
447 | } | |
448 | ||
25567a39 | 449 | dbg("failed to refresh device list"); |
0945b4fe TH |
450 | return -1; |
451 | } | |
452 | ||
453 | ||
454 | int usbip_vhci_get_free_port(void) | |
455 | { | |
456 | for (int i = 0; i < vhci_driver->nports; i++) { | |
457 | if (vhci_driver->idev[i].status == VDEV_ST_NULL) | |
458 | return i; | |
459 | } | |
460 | ||
461 | return -1; | |
462 | } | |
463 | ||
464 | int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid, | |
465 | uint32_t speed) { | |
466 | struct sysfs_attribute *attr_attach; | |
467 | char buff[200]; /* what size should be ? */ | |
468 | int ret; | |
469 | ||
470 | attr_attach = sysfs_get_device_attr(vhci_driver->hc_device, "attach"); | |
471 | if (!attr_attach) { | |
25567a39 | 472 | dbg("sysfs_get_device_attr(\"attach\") failed: %s", |
473 | vhci_driver->hc_device->name); | |
0945b4fe TH |
474 | return -1; |
475 | } | |
476 | ||
477 | snprintf(buff, sizeof(buff), "%u %u %u %u", | |
478 | port, sockfd, devid, speed); | |
479 | dbg("writing: %s", buff); | |
480 | ||
481 | ret = sysfs_write_attribute(attr_attach, buff, strlen(buff)); | |
482 | if (ret < 0) { | |
25567a39 | 483 | dbg("sysfs_write_attribute failed"); |
0945b4fe TH |
484 | return -1; |
485 | } | |
486 | ||
25567a39 | 487 | dbg("attached port: %d", port); |
0945b4fe TH |
488 | |
489 | return 0; | |
490 | } | |
491 | ||
492 | static unsigned long get_devid(uint8_t busnum, uint8_t devnum) | |
493 | { | |
494 | return (busnum << 16) | devnum; | |
495 | } | |
496 | ||
497 | /* will be removed */ | |
498 | int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum, | |
499 | uint8_t devnum, uint32_t speed) | |
500 | { | |
501 | int devid = get_devid(busnum, devnum); | |
502 | ||
503 | return usbip_vhci_attach_device2(port, sockfd, devid, speed); | |
504 | } | |
505 | ||
506 | int usbip_vhci_detach_device(uint8_t port) | |
507 | { | |
508 | struct sysfs_attribute *attr_detach; | |
509 | char buff[200]; /* what size should be ? */ | |
510 | int ret; | |
511 | ||
512 | attr_detach = sysfs_get_device_attr(vhci_driver->hc_device, "detach"); | |
513 | if (!attr_detach) { | |
25567a39 | 514 | dbg("sysfs_get_device_attr(\"detach\") failed: %s", |
515 | vhci_driver->hc_device->name); | |
0945b4fe TH |
516 | return -1; |
517 | } | |
518 | ||
519 | snprintf(buff, sizeof(buff), "%u", port); | |
0945b4fe TH |
520 | dbg("writing: %s", buff); |
521 | ||
522 | ret = sysfs_write_attribute(attr_detach, buff, strlen(buff)); | |
523 | if (ret < 0) { | |
25567a39 | 524 | dbg("sysfs_write_attribute failed"); |
0945b4fe TH |
525 | return -1; |
526 | } | |
527 | ||
25567a39 | 528 | dbg("detached port: %d", port); |
0945b4fe TH |
529 | |
530 | return 0; | |
531 | } |