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