diff options
Diffstat (limited to 'tools/usb/usbip/libsrc/vhci_driver.c')
-rw-r--r-- | tools/usb/usbip/libsrc/vhci_driver.c | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c new file mode 100644 index 000000000000..ad9204773533 --- /dev/null +++ b/tools/usb/usbip/libsrc/vhci_driver.c | |||
@@ -0,0 +1,411 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005-2007 Takahiro Hirofuchi | ||
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 "sysfs_utils.h" | ||
11 | |||
12 | #undef PROGNAME | ||
13 | #define PROGNAME "libusbip" | ||
14 | |||
15 | struct usbip_vhci_driver *vhci_driver; | ||
16 | struct udev *udev_context; | ||
17 | |||
18 | static struct usbip_imported_device * | ||
19 | imported_device_init(struct usbip_imported_device *idev, char *busid) | ||
20 | { | ||
21 | struct udev_device *sudev; | ||
22 | |||
23 | sudev = udev_device_new_from_subsystem_sysname(udev_context, | ||
24 | "usb", busid); | ||
25 | if (!sudev) { | ||
26 | dbg("udev_device_new_from_subsystem_sysname failed: %s", busid); | ||
27 | goto err; | ||
28 | } | ||
29 | read_usb_device(sudev, &idev->udev); | ||
30 | udev_device_unref(sudev); | ||
31 | |||
32 | return idev; | ||
33 | |||
34 | err: | ||
35 | return NULL; | ||
36 | } | ||
37 | |||
38 | |||
39 | |||
40 | static int parse_status(const char *value) | ||
41 | { | ||
42 | int ret = 0; | ||
43 | char *c; | ||
44 | |||
45 | |||
46 | for (int i = 0; i < vhci_driver->nports; i++) | ||
47 | memset(&vhci_driver->idev[i], 0, sizeof(vhci_driver->idev[i])); | ||
48 | |||
49 | |||
50 | /* skip a header line */ | ||
51 | c = strchr(value, '\n'); | ||
52 | if (!c) | ||
53 | return -1; | ||
54 | c++; | ||
55 | |||
56 | while (*c != '\0') { | ||
57 | int port, status, speed, devid; | ||
58 | unsigned long socket; | ||
59 | char lbusid[SYSFS_BUS_ID_SIZE]; | ||
60 | |||
61 | ret = sscanf(c, "%d %d %d %x %lx %31s\n", | ||
62 | &port, &status, &speed, | ||
63 | &devid, &socket, lbusid); | ||
64 | |||
65 | if (ret < 5) { | ||
66 | dbg("sscanf failed: %d", ret); | ||
67 | BUG(); | ||
68 | } | ||
69 | |||
70 | dbg("port %d status %d speed %d devid %x", | ||
71 | port, status, speed, devid); | ||
72 | dbg("socket %lx lbusid %s", socket, lbusid); | ||
73 | |||
74 | |||
75 | /* if a device is connected, look at it */ | ||
76 | { | ||
77 | struct usbip_imported_device *idev = &vhci_driver->idev[port]; | ||
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 | |||
98 | /* go to the next line */ | ||
99 | c = strchr(c, '\n'); | ||
100 | if (!c) | ||
101 | break; | ||
102 | c++; | ||
103 | } | ||
104 | |||
105 | dbg("exit"); | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static int refresh_imported_device_list(void) | ||
111 | { | ||
112 | const char *attr_status; | ||
113 | |||
114 | attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device, | ||
115 | "status"); | ||
116 | if (!attr_status) { | ||
117 | err("udev_device_get_sysattr_value failed"); | ||
118 | return -1; | ||
119 | } | ||
120 | |||
121 | return parse_status(attr_status); | ||
122 | } | ||
123 | |||
124 | static int get_nports(void) | ||
125 | { | ||
126 | char *c; | ||
127 | int nports = 0; | ||
128 | const char *attr_status; | ||
129 | |||
130 | attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device, | ||
131 | "status"); | ||
132 | if (!attr_status) { | ||
133 | err("udev_device_get_sysattr_value failed"); | ||
134 | return -1; | ||
135 | } | ||
136 | |||
137 | /* skip a header line */ | ||
138 | c = strchr(attr_status, '\n'); | ||
139 | if (!c) | ||
140 | return 0; | ||
141 | c++; | ||
142 | |||
143 | while (*c != '\0') { | ||
144 | /* go to the next line */ | ||
145 | c = strchr(c, '\n'); | ||
146 | if (!c) | ||
147 | return nports; | ||
148 | c++; | ||
149 | nports += 1; | ||
150 | } | ||
151 | |||
152 | return nports; | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * Read the given port's record. | ||
157 | * | ||
158 | * To avoid buffer overflow we will read the entire line and | ||
159 | * validate each part's size. The initial buffer is padded by 4 to | ||
160 | * accommodate the 2 spaces, 1 newline and an additional character | ||
161 | * which is needed to properly validate the 3rd part without it being | ||
162 | * truncated to an acceptable length. | ||
163 | */ | ||
164 | static int read_record(int rhport, char *host, unsigned long host_len, | ||
165 | char *port, unsigned long port_len, char *busid) | ||
166 | { | ||
167 | int part; | ||
168 | FILE *file; | ||
169 | char path[PATH_MAX+1]; | ||
170 | char *buffer, *start, *end; | ||
171 | char delim[] = {' ', ' ', '\n'}; | ||
172 | int max_len[] = {(int)host_len, (int)port_len, SYSFS_BUS_ID_SIZE}; | ||
173 | size_t buffer_len = host_len + port_len + SYSFS_BUS_ID_SIZE + 4; | ||
174 | |||
175 | buffer = malloc(buffer_len); | ||
176 | if (!buffer) | ||
177 | return -1; | ||
178 | |||
179 | snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport); | ||
180 | |||
181 | file = fopen(path, "r"); | ||
182 | if (!file) { | ||
183 | err("fopen"); | ||
184 | free(buffer); | ||
185 | return -1; | ||
186 | } | ||
187 | |||
188 | if (fgets(buffer, buffer_len, file) == NULL) { | ||
189 | err("fgets"); | ||
190 | free(buffer); | ||
191 | fclose(file); | ||
192 | return -1; | ||
193 | } | ||
194 | fclose(file); | ||
195 | |||
196 | /* validate the length of each of the 3 parts */ | ||
197 | start = buffer; | ||
198 | for (part = 0; part < 3; part++) { | ||
199 | end = strchr(start, delim[part]); | ||
200 | if (end == NULL || (end - start) > max_len[part]) { | ||
201 | free(buffer); | ||
202 | return -1; | ||
203 | } | ||
204 | start = end + 1; | ||
205 | } | ||
206 | |||
207 | if (sscanf(buffer, "%s %s %s\n", host, port, busid) != 3) { | ||
208 | err("sscanf"); | ||
209 | free(buffer); | ||
210 | return -1; | ||
211 | } | ||
212 | |||
213 | free(buffer); | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | /* ---------------------------------------------------------------------- */ | ||
219 | |||
220 | int usbip_vhci_driver_open(void) | ||
221 | { | ||
222 | udev_context = udev_new(); | ||
223 | if (!udev_context) { | ||
224 | err("udev_new failed"); | ||
225 | return -1; | ||
226 | } | ||
227 | |||
228 | vhci_driver = calloc(1, sizeof(struct usbip_vhci_driver)); | ||
229 | |||
230 | /* will be freed in usbip_driver_close() */ | ||
231 | vhci_driver->hc_device = | ||
232 | udev_device_new_from_subsystem_sysname(udev_context, | ||
233 | USBIP_VHCI_BUS_TYPE, | ||
234 | USBIP_VHCI_DRV_NAME); | ||
235 | if (!vhci_driver->hc_device) { | ||
236 | err("udev_device_new_from_subsystem_sysname failed"); | ||
237 | goto err; | ||
238 | } | ||
239 | |||
240 | vhci_driver->nports = get_nports(); | ||
241 | |||
242 | dbg("available ports: %d", vhci_driver->nports); | ||
243 | |||
244 | if (refresh_imported_device_list()) | ||
245 | goto err; | ||
246 | |||
247 | return 0; | ||
248 | |||
249 | err: | ||
250 | udev_device_unref(vhci_driver->hc_device); | ||
251 | |||
252 | if (vhci_driver) | ||
253 | free(vhci_driver); | ||
254 | |||
255 | vhci_driver = NULL; | ||
256 | |||
257 | udev_unref(udev_context); | ||
258 | |||
259 | return -1; | ||
260 | } | ||
261 | |||
262 | |||
263 | void usbip_vhci_driver_close(void) | ||
264 | { | ||
265 | if (!vhci_driver) | ||
266 | return; | ||
267 | |||
268 | udev_device_unref(vhci_driver->hc_device); | ||
269 | |||
270 | free(vhci_driver); | ||
271 | |||
272 | vhci_driver = NULL; | ||
273 | |||
274 | udev_unref(udev_context); | ||
275 | } | ||
276 | |||
277 | |||
278 | int usbip_vhci_refresh_device_list(void) | ||
279 | { | ||
280 | |||
281 | if (refresh_imported_device_list()) | ||
282 | goto err; | ||
283 | |||
284 | return 0; | ||
285 | err: | ||
286 | dbg("failed to refresh device list"); | ||
287 | return -1; | ||
288 | } | ||
289 | |||
290 | |||
291 | int usbip_vhci_get_free_port(void) | ||
292 | { | ||
293 | for (int i = 0; i < vhci_driver->nports; i++) { | ||
294 | if (vhci_driver->idev[i].status == VDEV_ST_NULL) | ||
295 | return i; | ||
296 | } | ||
297 | |||
298 | return -1; | ||
299 | } | ||
300 | |||
301 | int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid, | ||
302 | uint32_t speed) { | ||
303 | char buff[200]; /* what size should be ? */ | ||
304 | char attach_attr_path[SYSFS_PATH_MAX]; | ||
305 | char attr_attach[] = "attach"; | ||
306 | const char *path; | ||
307 | int ret; | ||
308 | |||
309 | snprintf(buff, sizeof(buff), "%u %d %u %u", | ||
310 | port, sockfd, devid, speed); | ||
311 | dbg("writing: %s", buff); | ||
312 | |||
313 | path = udev_device_get_syspath(vhci_driver->hc_device); | ||
314 | snprintf(attach_attr_path, sizeof(attach_attr_path), "%s/%s", | ||
315 | path, attr_attach); | ||
316 | dbg("attach attribute path: %s", attach_attr_path); | ||
317 | |||
318 | ret = write_sysfs_attribute(attach_attr_path, buff, strlen(buff)); | ||
319 | if (ret < 0) { | ||
320 | dbg("write_sysfs_attribute failed"); | ||
321 | return -1; | ||
322 | } | ||
323 | |||
324 | dbg("attached port: %d", port); | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static unsigned long get_devid(uint8_t busnum, uint8_t devnum) | ||
330 | { | ||
331 | return (busnum << 16) | devnum; | ||
332 | } | ||
333 | |||
334 | /* will be removed */ | ||
335 | int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum, | ||
336 | uint8_t devnum, uint32_t speed) | ||
337 | { | ||
338 | int devid = get_devid(busnum, devnum); | ||
339 | |||
340 | return usbip_vhci_attach_device2(port, sockfd, devid, speed); | ||
341 | } | ||
342 | |||
343 | int usbip_vhci_detach_device(uint8_t port) | ||
344 | { | ||
345 | char detach_attr_path[SYSFS_PATH_MAX]; | ||
346 | char attr_detach[] = "detach"; | ||
347 | char buff[200]; /* what size should be ? */ | ||
348 | const char *path; | ||
349 | int ret; | ||
350 | |||
351 | snprintf(buff, sizeof(buff), "%u", port); | ||
352 | dbg("writing: %s", buff); | ||
353 | |||
354 | path = udev_device_get_syspath(vhci_driver->hc_device); | ||
355 | snprintf(detach_attr_path, sizeof(detach_attr_path), "%s/%s", | ||
356 | path, attr_detach); | ||
357 | dbg("detach attribute path: %s", detach_attr_path); | ||
358 | |||
359 | ret = write_sysfs_attribute(detach_attr_path, buff, strlen(buff)); | ||
360 | if (ret < 0) { | ||
361 | dbg("write_sysfs_attribute failed"); | ||
362 | return -1; | ||
363 | } | ||
364 | |||
365 | dbg("detached port: %d", port); | ||
366 | |||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev) | ||
371 | { | ||
372 | char product_name[100]; | ||
373 | char host[NI_MAXHOST] = "unknown host"; | ||
374 | char serv[NI_MAXSERV] = "unknown port"; | ||
375 | char remote_busid[SYSFS_BUS_ID_SIZE]; | ||
376 | int ret; | ||
377 | int read_record_error = 0; | ||
378 | |||
379 | if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED) | ||
380 | return 0; | ||
381 | |||
382 | ret = read_record(idev->port, host, sizeof(host), serv, sizeof(serv), | ||
383 | remote_busid); | ||
384 | if (ret) { | ||
385 | err("read_record"); | ||
386 | read_record_error = 1; | ||
387 | } | ||
388 | |||
389 | printf("Port %02d: <%s> at %s\n", idev->port, | ||
390 | usbip_status_string(idev->status), | ||
391 | usbip_speed_string(idev->udev.speed)); | ||
392 | |||
393 | usbip_names_get_product(product_name, sizeof(product_name), | ||
394 | idev->udev.idVendor, idev->udev.idProduct); | ||
395 | |||
396 | printf(" %s\n", product_name); | ||
397 | |||
398 | if (!read_record_error) { | ||
399 | printf("%10s -> usbip://%s:%s/%s\n", idev->udev.busid, | ||
400 | host, serv, remote_busid); | ||
401 | printf("%10s -> remote bus/dev %03d/%03d\n", " ", | ||
402 | idev->busnum, idev->devnum); | ||
403 | } else { | ||
404 | printf("%10s -> unknown host, remote port and remote busid\n", | ||
405 | idev->udev.busid); | ||
406 | printf("%10s -> remote bus/dev %03d/%03d\n", " ", | ||
407 | idev->busnum, idev->devnum); | ||
408 | } | ||
409 | |||
410 | return 0; | ||
411 | } | ||