bfe67480f4b8224f59e83496992834322a92975c
[pandora-kernel.git] / drivers / staging / usbip / userspace / libsrc / stub_driver.c
1 /*
2  * Copyright (C) 2005-2007 Takahiro Hirofuchi
3  */
4
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8
9 #include "usbip.h"
10
11 static const char *usbip_stub_driver_name = "usbip";
12
13
14 struct usbip_stub_driver *stub_driver;
15
16 static struct sysfs_driver *open_sysfs_stub_driver(void)
17 {
18         int ret;
19
20         char sysfs_mntpath[SYSFS_PATH_MAX];
21         char stub_driver_path[SYSFS_PATH_MAX];
22         struct sysfs_driver *stub_driver;
23
24
25         ret = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX);
26         if (ret < 0) {
27                 err("sysfs must be mounted");
28                 return NULL;
29         }
30
31         snprintf(stub_driver_path, SYSFS_PATH_MAX, "%s/%s/usb/%s/%s",
32                         sysfs_mntpath, SYSFS_BUS_NAME, SYSFS_DRIVERS_NAME,
33                         usbip_stub_driver_name);
34
35         stub_driver = sysfs_open_driver_path(stub_driver_path);
36         if (!stub_driver) {
37                 err("usbip_common_mod.ko and usbip.ko must be loaded");
38                 return NULL;
39         }
40
41         return stub_driver;
42 }
43
44
45 #define SYSFS_OPEN_RETRIES 100
46
47 /* only the first interface value is true! */
48 static int32_t read_attr_usbip_status(struct usb_device *udev)
49 {
50         char attrpath[SYSFS_PATH_MAX];
51         struct sysfs_attribute *attr;
52         int value = 0;
53         int  ret;
54         struct stat s;
55         int retries = SYSFS_OPEN_RETRIES;
56
57         /* This access is racy!
58          *
59          * Just after detach, our driver removes the sysfs
60          * files and recreates them.
61          *
62          * We may try and fail to open the usbip_status of
63          * an exported device in the (short) window where
64          * it has been removed and not yet recreated.
65          *
66          * This is a bug in the interface. Nothing we can do
67          * except work around it here by polling for the sysfs
68          * usbip_status to reappear.
69          */
70
71         snprintf(attrpath, SYSFS_PATH_MAX, "%s/%s:%d.%d/usbip_status",
72                         udev->path, udev->busid,
73                         udev->bConfigurationValue,
74                         0);
75
76         while (retries > 0) {
77                 if (stat(attrpath, &s) == 0)
78                         break;
79
80                 if (errno != ENOENT) {
81                         err("error stat'ing %s", attrpath);
82                         return -1;
83                 }
84
85                 usleep(10000); /* 10ms */
86                 retries--;
87         }
88
89         if (retries == 0)
90                 err("usbip_status not ready after %d retries", 
91                         SYSFS_OPEN_RETRIES);
92         else if (retries < SYSFS_OPEN_RETRIES)
93                 info("warning: usbip_status ready after %d retries",
94                          SYSFS_OPEN_RETRIES - retries);
95
96         attr = sysfs_open_attribute(attrpath);
97         if (!attr) {
98                 err("open %s", attrpath);
99                 return -1;
100         }
101
102         ret = sysfs_read_attribute(attr);
103         if (ret) {
104                 err("read %s", attrpath);
105                 sysfs_close_attribute(attr);
106                 return -1;
107         }
108
109         value = atoi(attr->value);
110
111         sysfs_close_attribute(attr);
112
113         return value;
114 }
115
116
117 static void usbip_exported_device_delete(void *dev)
118 {
119         struct usbip_exported_device *edev =
120                 (struct usbip_exported_device *) dev;
121
122         sysfs_close_device(edev->sudev);
123         free(dev);
124 }
125
126
127 static struct usbip_exported_device *usbip_exported_device_new(char *sdevpath)
128 {
129         struct usbip_exported_device *edev = NULL;
130
131         edev = (struct usbip_exported_device *) calloc(1, sizeof(*edev));
132         if (!edev) {
133                 err("alloc device");
134                 return NULL;
135         }
136
137         edev->sudev = sysfs_open_device_path(sdevpath);
138         if (!edev->sudev) {
139                 err("open %s", sdevpath);
140                 goto err;
141         }
142
143         read_usb_device(edev->sudev, &edev->udev);
144
145         edev->status = read_attr_usbip_status(&edev->udev);
146         if (edev->status < 0)
147                 goto err;
148
149         /* reallocate buffer to include usb interface data */
150         size_t size = sizeof(*edev) + edev->udev.bNumInterfaces * sizeof(struct usb_interface);
151         edev = (struct usbip_exported_device *) realloc(edev, size);
152         if (!edev) {
153                 err("alloc device");
154                 goto err;
155         }
156
157         for (int i=0; i < edev->udev.bNumInterfaces; i++)
158                 read_usb_interface(&edev->udev, i, &edev->uinf[i]);
159
160         return edev;
161
162 err:
163         if (edev && edev->sudev)
164                 sysfs_close_device(edev->sudev);
165         if (edev)
166                 free(edev);
167         return NULL;
168 }
169
170
171 static int check_new(struct dlist *dlist, struct sysfs_device *target)
172 {
173         struct sysfs_device *dev;
174
175         dlist_for_each_data(dlist, dev, struct sysfs_device) {
176                 if (!strncmp(dev->bus_id, target->bus_id, SYSFS_BUS_ID_SIZE))
177                         /* found. not new */
178                         return 0;
179         }
180
181         return 1;
182 }
183
184 static void delete_nothing(void *dev)
185 {
186         /* do not delete anything. but, its container will be deleted. */
187 }
188
189 static int refresh_exported_devices(void)
190 {
191         struct sysfs_device     *suinf;  /* sysfs_device of usb_interface */
192         struct dlist            *suinf_list;
193
194         struct sysfs_device     *sudev;  /* sysfs_device of usb_device */
195         struct dlist            *sudev_list;
196
197
198         sudev_list = dlist_new_with_delete(sizeof(struct sysfs_device), delete_nothing);
199
200         suinf_list = sysfs_get_driver_devices(stub_driver->sysfs_driver);
201         if (!suinf_list) {
202                 printf("Bind usbip.ko to a usb device to be exportable!\n");
203                 goto bye;
204         }
205
206         /* collect unique USB devices (not interfaces) */
207         dlist_for_each_data(suinf_list, suinf, struct sysfs_device) {
208
209                 /* get usb device of this usb interface */
210                 sudev = sysfs_get_device_parent(suinf);
211                 if (!sudev) {
212                         err("get parent dev of %s", suinf->name);
213                         continue;
214                 }
215
216                 if (check_new(sudev_list, sudev)) {
217                         dlist_unshift(sudev_list, sudev);
218                 }
219         }
220
221         dlist_for_each_data(sudev_list, sudev, struct sysfs_device) {
222                 struct usbip_exported_device *edev;
223
224                 edev = usbip_exported_device_new(sudev->path);
225                 if (!edev) {
226                         err("usbip_exported_device new");
227                         continue;
228                 }
229
230                 dlist_unshift(stub_driver->edev_list, (void *) edev);
231                 stub_driver->ndevs++;
232         }
233
234
235         dlist_destroy(sudev_list);
236
237 bye:
238
239         return 0;
240 }
241
242 int usbip_stub_refresh_device_list(void)
243 {
244         int ret;
245
246         if (stub_driver->edev_list)
247                 dlist_destroy(stub_driver->edev_list);
248
249         stub_driver->ndevs = 0;
250
251         stub_driver->edev_list = dlist_new_with_delete(sizeof(struct usbip_exported_device),
252                         usbip_exported_device_delete);
253         if (!stub_driver->edev_list) {
254                 err("alloc dlist");
255                 return -1;
256         }
257
258         ret = refresh_exported_devices();
259         if (ret < 0)
260                 return ret;
261
262         return 0;
263 }
264
265 int usbip_stub_driver_open(void)
266 {
267         int ret;
268
269
270         stub_driver = (struct usbip_stub_driver *) calloc(1, sizeof(*stub_driver));
271         if (!stub_driver) {
272                 err("alloc stub_driver");
273                 return -1;
274         }
275
276         stub_driver->ndevs = 0;
277
278         stub_driver->edev_list = dlist_new_with_delete(sizeof(struct usbip_exported_device),
279                         usbip_exported_device_delete);
280         if (!stub_driver->edev_list) {
281                 err("alloc dlist");
282                 goto err;
283         }
284
285         stub_driver->sysfs_driver = open_sysfs_stub_driver();
286         if (!stub_driver->sysfs_driver)
287                 goto err;
288
289         ret = refresh_exported_devices();
290         if (ret < 0)
291                 goto err;
292
293         return 0;
294
295
296 err:
297         if (stub_driver->sysfs_driver)
298                 sysfs_close_driver(stub_driver->sysfs_driver);
299         if (stub_driver->edev_list)
300                 dlist_destroy(stub_driver->edev_list);
301         free(stub_driver);
302
303         stub_driver = NULL;
304         return -1;
305 }
306
307
308 void usbip_stub_driver_close(void)
309 {
310         if (!stub_driver)
311                 return;
312
313         if (stub_driver->edev_list)
314                 dlist_destroy(stub_driver->edev_list);
315         if (stub_driver->sysfs_driver)
316                 sysfs_close_driver(stub_driver->sysfs_driver);
317         free(stub_driver);
318
319         stub_driver = NULL;
320 }
321
322 int usbip_stub_export_device(struct usbip_exported_device *edev, int sockfd)
323 {
324         char attrpath[SYSFS_PATH_MAX];
325         struct sysfs_attribute *attr;
326         char sockfd_buff[30];
327         int ret;
328
329
330         if (edev->status != SDEV_ST_AVAILABLE) {
331                 info("device not available, %s", edev->udev.busid);
332                 switch( edev->status ) {
333                         case SDEV_ST_ERROR:
334                                 info("     status SDEV_ST_ERROR");
335                                 break;
336                         case SDEV_ST_USED:
337                                 info("     status SDEV_ST_USED");
338                                 break;
339                         default:
340                                 info("     status unknown: 0x%x", edev->status);
341                 }
342                 return -1;
343         }
344
345         /* only the first interface is true */
346         snprintf(attrpath, sizeof(attrpath), "%s/%s:%d.%d/%s",
347                         edev->udev.path,
348                         edev->udev.busid,
349                         edev->udev.bConfigurationValue, 0,
350                         "usbip_sockfd");
351
352         attr = sysfs_open_attribute(attrpath);
353         if (!attr) {
354                 err("open %s", attrpath);
355                 return -1;
356         }
357
358         snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd);
359
360         dbg("write: %s", sockfd_buff);
361
362         ret = sysfs_write_attribute(attr, sockfd_buff, strlen(sockfd_buff));
363         if (ret < 0) {
364                 err("write sockfd %s to %s", sockfd_buff, attrpath);
365                 goto err_write_sockfd;
366         }
367
368         info("connect %s", edev->udev.busid);
369
370 err_write_sockfd:
371         sysfs_close_attribute(attr);
372
373         return ret;
374 }
375
376 struct usbip_exported_device *usbip_stub_get_device(int num)
377 {
378         struct usbip_exported_device *edev;
379         struct dlist            *dlist = stub_driver->edev_list;
380         int count = 0;
381
382         dlist_for_each_data(dlist, edev, struct usbip_exported_device) {
383                 if (num == count)
384                         return edev;
385                 else
386                         count++ ;
387         }
388
389         return NULL;
390 }