blob: 1084236ede07e6e4357f606e4b65bb66af411251 [file] [log] [blame]
Mike Lockwood30ff2c72010-05-09 16:23:47 -04001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <unistd.h>
20#include <string.h>
21
22#include <sys/ioctl.h>
23#include <sys/types.h>
24#include <sys/time.h>
25#include <sys/inotify.h>
26#include <dirent.h>
27#include <fcntl.h>
28#include <errno.h>
29#include <ctype.h>
30#include <pthread.h>
31
32#include <linux/usbdevice_fs.h>
33#include <linux/version.h>
34#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
35#include <linux/usb/ch9.h>
36#else
37#include <linux/usb_ch9.h>
38#endif
39#include <asm/byteorder.h>
40
41#include "usbhost/usbhost.h"
42
43#define USB_FS_DIR "/dev/bus/usb"
44
45#if 0
46#define D printf
47#else
48#define D(...)
49#endif
50
Mike Lockwood6ac3aa12010-05-25 08:10:02 -040051struct usb_host_context {
52 usb_device_added_cb added_cb;
53 usb_device_removed_cb removed_cb;
54 void *client_data;
55};
56
Mike Lockwood30ff2c72010-05-09 16:23:47 -040057struct usb_device {
58 char dev_name[64];
59 unsigned char desc[256];
60 int desc_length;
61 int fd;
62 int writeable;
63};
64
65struct usb_endpoint
66{
67 struct usb_device *dev;
68 struct usb_endpoint_descriptor desc;
69 struct usbdevfs_urb urb;
70};
71
Mike Lockwood30ff2c72010-05-09 16:23:47 -040072static inline int badname(const char *name)
73{
74 while(*name) {
75 if(!isdigit(*name++)) return 1;
76 }
77 return 0;
78}
79
Mike Lockwood6ac3aa12010-05-25 08:10:02 -040080static void find_existing_devices(struct usb_host_context *context)
Mike Lockwood30ff2c72010-05-09 16:23:47 -040081{
82 char busname[32], devname[32];
83 DIR *busdir , *devdir ;
84 struct dirent *de;
85
86 busdir = opendir(USB_FS_DIR);
87 if(busdir == 0) return;
88
89 while((de = readdir(busdir)) != 0) {
90 if(badname(de->d_name)) continue;
91
92 snprintf(busname, sizeof busname, "%s/%s", USB_FS_DIR, de->d_name);
93 devdir = opendir(busname);
94 if(devdir == 0) continue;
95
96 while((de = readdir(devdir))) {
97 if(badname(de->d_name)) continue;
98
99 snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name);
Mike Lockwood6ac3aa12010-05-25 08:10:02 -0400100 context->added_cb(devname, context->client_data);
Mike Lockwood30ff2c72010-05-09 16:23:47 -0400101 } // end of devdir while
102 closedir(devdir);
103 } //end of busdir while
104 closedir(busdir);
105}
106
Mike Lockwood6ac3aa12010-05-25 08:10:02 -0400107static void* device_discovery_thread(void *client_data)
Mike Lockwood30ff2c72010-05-09 16:23:47 -0400108{
Mike Lockwood6ac3aa12010-05-25 08:10:02 -0400109 struct usb_host_context *context = (struct usb_host_context *)client_data;
Mike Lockwood30ff2c72010-05-09 16:23:47 -0400110 struct inotify_event* event;
111 char event_buf[512];
112 char path[100];
113 int i, fd, ret;
114 int wd, wds[10];
115 int wd_count = sizeof(wds) / sizeof(wds[0]);
116
117 D("Created device discovery thread\n");
118
119 fd = inotify_init();
120 if (fd < 0) {
121 fprintf(stderr, "inotify_init failed\n");
122 return NULL;
123 }
124
125 /* watch for files added and deleted within USB_FS_DIR */
126 memset(wds, 0, sizeof(wds));
127 /* watch the root for new subdirectories */
128 wds[0] = inotify_add_watch(fd, USB_FS_DIR, IN_CREATE | IN_DELETE);
129 if (wds[0] < 0) {
130 fprintf(stderr, "inotify_add_watch failed\n");
131 return NULL;
132 }
133
134 /* watch existing subdirectories of USB_FS_DIR */
135 for (i = 1; i < wd_count; i++) {
136 snprintf(path, sizeof(path), "%s/%03d", USB_FS_DIR, i);
137 ret = inotify_add_watch(fd, path, IN_CREATE | IN_DELETE);
138 if (ret > 0)
139 wds[i] = ret;
140 }
141
142 /* check for existing devices first, after we have inotify set up */
Mike Lockwood6ac3aa12010-05-25 08:10:02 -0400143 find_existing_devices(context);
Mike Lockwood30ff2c72010-05-09 16:23:47 -0400144
145 while (1) {
146 ret = read(fd, event_buf, sizeof(event_buf));
147 if (ret >= (int)sizeof(struct inotify_event)) {
148 event = (struct inotify_event *)event_buf;
149 wd = event->wd;
150 if (wd == wds[0]) {
151 i = atoi(event->name);
152 snprintf(path, sizeof(path), "%s/%s", USB_FS_DIR, event->name);
153 D("new subdirectory %s: index: %d\n", path, i);
154 if (i > 0 && i < wd_count) {
155 ret = inotify_add_watch(fd, path, IN_CREATE | IN_DELETE);
156 if (ret > 0)
157 wds[i] = ret;
158 }
159 } else {
160 for (i = 1; i < wd_count; i++) {
161 if (wd == wds[i]) {
162 snprintf(path, sizeof(path), "%s/%03d/%s", USB_FS_DIR, i, event->name);
163 if (event->mask == IN_CREATE) {
164 D("new device %s\n", path);
Mike Lockwood6ac3aa12010-05-25 08:10:02 -0400165 context->added_cb(path, context->client_data);
Mike Lockwood30ff2c72010-05-09 16:23:47 -0400166 } else if (event->mask == IN_DELETE) {
167 D("gone device %s\n", path);
Mike Lockwood6ac3aa12010-05-25 08:10:02 -0400168 context->removed_cb(path, context->client_data);
Mike Lockwood30ff2c72010-05-09 16:23:47 -0400169 }
170 }
171 }
172 }
173 }
174 }
175 return NULL;
176}
177
Mike Lockwood6ac3aa12010-05-25 08:10:02 -0400178int usb_host_init(usb_device_added_cb added_cb, usb_device_removed_cb removed_cb, void *client_data)
Mike Lockwood30ff2c72010-05-09 16:23:47 -0400179{
Mike Lockwood6ac3aa12010-05-25 08:10:02 -0400180 struct usb_host_context *context;
Mike Lockwood30ff2c72010-05-09 16:23:47 -0400181 pthread_t tid;
Mike Lockwood6ac3aa12010-05-25 08:10:02 -0400182 pthread_attr_t attr;
Mike Lockwood30ff2c72010-05-09 16:23:47 -0400183
Mike Lockwood6ac3aa12010-05-25 08:10:02 -0400184 if (!added_cb || !removed_cb)
185 return -EINVAL;
Mike Lockwood30ff2c72010-05-09 16:23:47 -0400186
Mike Lockwood6ac3aa12010-05-25 08:10:02 -0400187 context = calloc(1, sizeof(struct usb_host_context));
188 context->added_cb = added_cb;
189 context->removed_cb = removed_cb;
190 context->client_data = client_data;
Mike Lockwood30ff2c72010-05-09 16:23:47 -0400191
Mike Lockwood6ac3aa12010-05-25 08:10:02 -0400192 pthread_attr_init(&attr);
193 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
194 return pthread_create(&tid, &attr, device_discovery_thread, context);
Mike Lockwood30ff2c72010-05-09 16:23:47 -0400195}
196
197struct usb_device *usb_device_open(const char *dev_name)
198{
199 struct usb_device *device = calloc(1, sizeof(struct usb_device));
200 int fd, length, did_retry = 0;
201
202 strcpy(device->dev_name, dev_name);
203 device->writeable = 1;
204
205retry:
206 fd = open(dev_name, O_RDWR);
207 if (fd < 0) {
208 /* if we fail, see if have read-only access */
209 fd = open(dev_name, O_RDONLY);
210 if (fd < 0 && errno == EACCES && !did_retry) {
211 /* work around race condition between inotify and permissions management */
212 sleep(1);
213 did_retry = 1;
214 goto retry;
215 }
216
217 if (fd < 0) goto fail;
218 device->writeable = 0;
219 D("[ usb open read-only %s fd = %d]\n", dev_name, fd);
220 }
221
222 length = read(fd, device->desc, sizeof(device->desc));
223 if (length < 0)
224 goto fail;
225
226 device->fd = fd;
227 device->desc_length = length;
228 return device;
229fail:
230 close(fd);
231 free(device);
232 return NULL;
233}
234
235void usb_device_close(struct usb_device *device)
236{
237 close(device->fd);
238 free(device);
239}
240
241const char* usb_device_get_name(struct usb_device *device)
242{
243 return device->dev_name;
244}
245
246uint16_t usb_device_get_vendor_id(struct usb_device *device)
247{
248 struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc;
249 return __le16_to_cpu(desc->idVendor);
250}
251
252uint16_t usb_device_get_product_id(struct usb_device *device)
253{
254 struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc;
255 return __le16_to_cpu(desc->idProduct);
256}
257
258char* usb_device_get_string(struct usb_device *device, int id)
259{
260 char string[256];
261 struct usbdevfs_ctrltransfer ctrl;
262 __u16 buffer[128];
263 __u16 languages[128];
264 int i, result;
265 int languageCount = 0;
266
267 string[0] = 0;
268
269 // reading the string requires read/write permission
270 if (!device->writeable) {
271 int fd = open(device->dev_name, O_RDWR);
272 if (fd > 0) {
273 close(device->fd);
274 device->fd = fd;
275 device->writeable = 1;
276 } else {
277 return NULL;
278 }
279 }
280
281 memset(languages, 0, sizeof(languages));
282 memset(&ctrl, 0, sizeof(ctrl));
283
284 // read list of supported languages
285 ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
286 ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
287 ctrl.wValue = (USB_DT_STRING << 8) | 0;
288 ctrl.wIndex = 0;
289 ctrl.wLength = sizeof(languages);
290 ctrl.data = languages;
291
292 result = ioctl(device->fd, USBDEVFS_CONTROL, &ctrl);
293 if (result > 0)
294 languageCount = (result - 2) / 2;
295
296 for (i = 1; i <= languageCount; i++) {
297 memset(buffer, 0, sizeof(buffer));
298 memset(&ctrl, 0, sizeof(ctrl));
299
300 ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
301 ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
302 ctrl.wValue = (USB_DT_STRING << 8) | id;
303 ctrl.wIndex = languages[i];
304 ctrl.wLength = sizeof(buffer);
305 ctrl.data = buffer;
306
307 result = ioctl(device->fd, USBDEVFS_CONTROL, &ctrl);
308 if (result > 0) {
309 int i;
310 // skip first word, and copy the rest to the string, changing shorts to bytes.
311 result /= 2;
312 for (i = 1; i < result; i++)
313 string[i - 1] = buffer[i];
314 string[i - 1] = 0;
315 return strdup(string);
316 }
317 }
318
319 return NULL;
320}
321
322char* usb_device_get_manufacturer_name(struct usb_device *device)
323{
324 struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
325
326 if (desc->iManufacturer)
327 return usb_device_get_string(device, desc->iManufacturer);
328 else
329 return NULL;
330}
331
332char* usb_device_get_product_name(struct usb_device *device)
333{
334 struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
335
336 if (desc->iProduct)
337 return usb_device_get_string(device, desc->iProduct);
338 else
339 return NULL;
340}
341
342char* usb_device_get_serial(struct usb_device *device)
343{
344 struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
345
346 if (desc->iSerialNumber)
347 return usb_device_get_string(device, desc->iSerialNumber);
348 else
349 return NULL;
350}
351
352int usb_device_is_writeable(struct usb_device *device)
353{
354 return device->writeable;
355}
356
357void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter)
358{
359 iter->config = device->desc;
360 iter->config_end = device->desc + device->desc_length;
361 iter->curr_desc = device->desc;
362}
363
364struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter)
365{
366 struct usb_descriptor_header* next;
367 if (iter->curr_desc >= iter->config_end)
368 return NULL;
369 next = (struct usb_descriptor_header*)iter->curr_desc;
370 iter->curr_desc += next->bLength;
371 return next;
372}
373
374int usb_device_claim_interface(struct usb_device *device, unsigned int interface)
375{
376 return ioctl(device->fd, USBDEVFS_CLAIMINTERFACE, &interface);
377}
378
379int usb_device_release_interface(struct usb_device *device, unsigned int interface)
380{
381 return ioctl(device->fd, USBDEVFS_RELEASEINTERFACE, &interface);
382}
383
384struct usb_endpoint *usb_endpoint_open(struct usb_device *dev,
385 const struct usb_endpoint_descriptor *desc)
386{
387 struct usb_endpoint *ep = calloc(1, sizeof(struct usb_endpoint));
388 memcpy(&ep->desc, desc, sizeof(ep->desc));
389 ep->dev = dev;
390 return ep;
391}
392
393void usb_endpoint_close(struct usb_endpoint *ep)
394{
395 // cancel IO here?
396 free(ep);
397}
398
399int usb_endpoint_queue(struct usb_endpoint *ep, void *data, int len)
400{
401 struct usbdevfs_urb *urb = &ep->urb;
402 int res;
403
404 D("usb_endpoint_queue\n");
405 memset(urb, 0, sizeof(*urb));
406 urb->type = USBDEVFS_URB_TYPE_BULK;
407 urb->endpoint = ep->desc.bEndpointAddress;
408 urb->status = -1;
409 urb->buffer = data;
410 urb->buffer_length = len;
411
412 do {
413 res = ioctl(ep->dev->fd, USBDEVFS_SUBMITURB, urb);
414 } while((res < 0) && (errno == EINTR));
415
416 return res;
417}
418
419int usb_endpoint_wait(struct usb_device *dev, int *out_ep_num)
420{
421 struct usbdevfs_urb *out = NULL;
422 int res;
423
424 while (1) {
425 res = ioctl(dev->fd, USBDEVFS_REAPURB, &out);
426 D("USBDEVFS_REAPURB returned %d\n", res);
427 if (res < 0) {
428 if(errno == EINTR) {
429 continue;
430 }
431 D("[ reap urb - error ]\n");
432 *out_ep_num = -1;
433 } else {
434 D("[ urb @%p status = %d, actual = %d ]\n",
435 out, out->status, out->actual_length);
436 res = out->actual_length;
437 *out_ep_num = out->endpoint;
438 }
439 break;
440 }
441 return res;
442}
443
444int usb_endpoint_cancel(struct usb_endpoint *ep)
445{
446 return ioctl(ep->dev->fd, USBDEVFS_DISCARDURB, &ep->urb);
447}
448
Mike Lockwood5e567cb2010-05-12 08:53:49 -0400449struct usb_device *usb_endpoint_get_device(struct usb_endpoint *ep)
450{
451 return ep->dev;
452}
453
Mike Lockwood30ff2c72010-05-09 16:23:47 -0400454int usb_endpoint_number(struct usb_endpoint *ep)
455{
456 return ep->desc.bEndpointAddress;
457}
458
459int usb_endpoint_max_packet(struct usb_endpoint *ep)
460{
461 return __le16_to_cpu(ep->desc.wMaxPacketSize);
462}
463