blob: eb10118bf3ec784c9f6eef2faa3dbda8a2e56b32 [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001/*
2 * Copyright (C) 2007 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
Mike Lockwoodd7249c42010-05-07 09:52:32 -040022#include <usbhost/usbhost.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080023#include <linux/usbdevice_fs.h>
24#include <linux/version.h>
25#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
26#include <linux/usb/ch9.h>
27#else
28#include <linux/usb_ch9.h>
29#endif
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080030
31#include "sysdeps.h"
32
33#define TRACE_TAG TRACE_USB
34#include "adb.h"
35
36
37/* usb scan debugging is waaaay too verbose */
38#define DBGX(x...)
39
40static adb_mutex_t usb_lock = ADB_MUTEX_INITIALIZER;
41
42struct usb_handle
43{
44 usb_handle *prev;
45 usb_handle *next;
46
Mike Lockwoodd7249c42010-05-07 09:52:32 -040047 struct usb_device *device;
48 struct usb_endpoint *ep_in;
49 struct usb_endpoint *ep_out;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080050
Mike Lockwoodd7249c42010-05-07 09:52:32 -040051 adb_cond_t notify_in;
52 adb_cond_t notify_out;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080053 adb_mutex_t lock;
54
Mike Lockwoodd7249c42010-05-07 09:52:32 -040055 int read_result, write_result;
56 int zero_mask;
57 int dead;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080058
Mike Lockwoodd7249c42010-05-07 09:52:32 -040059 // Thread ID for our reaper thread
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080060 pthread_t reaper_thread;
61};
62
63static usb_handle handle_list = {
64 .prev = &handle_list,
65 .next = &handle_list,
66};
67
68static int known_device(const char *dev_name)
69{
70 usb_handle *usb;
71
72 adb_mutex_lock(&usb_lock);
Mike Lockwoodd7249c42010-05-07 09:52:32 -040073 for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
74 if (!strcmp(usb_device_get_name(usb->device), dev_name)) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080075 adb_mutex_unlock(&usb_lock);
76 return 1;
77 }
78 }
79 adb_mutex_unlock(&usb_lock);
80 return 0;
81}
82
Mike Lockwoodd7249c42010-05-07 09:52:32 -040083static void kick_disconnected_device(const char *devname)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080084{
85 usb_handle *usb;
86
87 adb_mutex_lock(&usb_lock);
Mike Lockwoodd7249c42010-05-07 09:52:32 -040088 /* kick the device if it is in our list */
89 for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
90 if (!strcmp(devname, usb_device_get_name(usb->device)))
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080091 usb_kick(usb);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080092 }
93 adb_mutex_unlock(&usb_lock);
94
95}
96
Mike Lockwoodd7249c42010-05-07 09:52:32 -040097static void* reaper_thread(void* arg)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080098{
Mike Lockwoodd7249c42010-05-07 09:52:32 -040099 struct usb_handle* h = (struct usb_handle *)arg;
100 int ep_in = usb_endpoint_number(h->ep_in);
101 int ep_out = usb_endpoint_number(h->ep_out);
102 int reaped_ep, res;
103
104 while (1) {
105 D("[ reap urb - wait ]\n");
106 adb_mutex_unlock(&h->lock);
107 res = usb_endpoint_wait(h->device, &reaped_ep);
108 adb_mutex_lock(&h->lock);
109 if(h->dead) {
110 res = -1;
111 break;
112 }
113
114 D("[ reaped ep %d ret = %d ]\n", reaped_ep, res);
115
116 if (reaped_ep == ep_in) {
117 D("[ reap urb - IN complete ]\n");
118 h->read_result = res;
119 adb_cond_broadcast(&h->notify_in);
120 }
121 if (reaped_ep == ep_out) {
122 D("[ reap urb - OUT compelete ]\n");
123 h->write_result = res;
124 adb_cond_broadcast(&h->notify_out);
125 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800126 }
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400127
128 return NULL;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800129}
130
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400131static void register_device(struct usb_device *device, int interface,
132 struct usb_endpoint *ep_in, struct usb_endpoint *ep_out)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800133{
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400134 usb_handle* usb = 0;
135 int ret = 0;
136 int writeable;
137 char *serial;
138 pthread_attr_t attr;
139 const char* dev_name = usb_device_get_name(device);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800140
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400141 /* Since Linux will not reassign the device ID (and dev_name)
142 ** as long as the device is open, we can add to the list here
143 ** once we open it and remove from the list when we're finally
144 ** closed and everything will work out fine.
145 **
146 ** If we have a usb_handle on the list 'o handles with a matching
147 ** name, we have no further work to do.
148 */
149 adb_mutex_lock(&usb_lock);
150 for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
151 if (!strcmp(usb_device_get_name(usb->device), dev_name)) {
152 adb_mutex_unlock(&usb_lock);
153 return;
154 }
155 }
156 adb_mutex_unlock(&usb_lock);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800157
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400158 usb = calloc(1, sizeof(usb_handle));
159 adb_cond_init(&usb->notify_in, 0);
160 adb_cond_init(&usb->notify_out, 0);
161 adb_mutex_init(&usb->lock, 0);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800162
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400163 usb->device = device;
164 usb->ep_in = ep_in;
165 usb->ep_out = ep_out;
166 usb->zero_mask = usb_endpoint_max_packet(usb->ep_out) - 1;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800167
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400168 D("[ usb open %s ]\n", dev_name);
169 writeable = usb_device_is_writeable(device);
170 if (writeable) {
171 ret = usb_device_claim_interface(device, interface);
172 if(ret != 0) goto fail;
173 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800174
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400175 /* add to the end of the active handles */
176 adb_mutex_lock(&usb_lock);
177 usb->next = &handle_list;
178 usb->prev = handle_list.prev;
179 usb->prev->next = usb;
180 usb->next->prev = usb;
181 adb_mutex_unlock(&usb_lock);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800182
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400183 pthread_attr_init(&attr);
184 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
185 pthread_create(&usb->reaper_thread, &attr, reaper_thread, usb);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800186
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400187 serial = usb_device_get_serial(device);
188 register_usb_transport(usb, serial, writeable);
189 if (serial)
190 free(serial);
191 return;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800192
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400193fail:
194 D("[ usb open %s error=%d, err_str = %s]\n",
195 dev_name, errno, strerror(errno));
196 if (usb->ep_in)
197 usb_endpoint_close(usb->ep_in);
198 if (usb->ep_out)
199 usb_endpoint_close(usb->ep_out);
200 if(device) {
201 usb_device_close(device);
202 }
203 free(usb);
204}
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800205
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400206static void check_usb_device(const char *devname) {
207 struct usb_device *device;
208 struct usb_descriptor_iter iter;
209 struct usb_descriptor_header* header;
210 struct usb_interface_descriptor* interface;
211 struct usb_endpoint_descriptor *ep1, *ep2;
212 struct usb_endpoint *ep_in = NULL, *ep_out = NULL;
213 uint16_t vid, pid;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800214
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400215 if(known_device(devname)) {
216 DBGX("skipping %s\n", devname);
217 return;
218 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800219
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400220 device = usb_device_open(devname);
221 if (!device) return;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800222
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400223 vid = usb_device_get_vendor_id(device);
224 pid = usb_device_get_product_id(device);
225 DBGX("[ %s is V:%04x P:%04x ]\n", devname, vid, pid);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800226
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400227 // loop through all the descriptors and look for the ADB interface
228 usb_descriptor_iter_init(device, &iter);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800229
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400230 while ((header = usb_descriptor_iter_next(&iter)) != NULL) {
231 if (header->bDescriptorType == USB_DT_INTERFACE) {
232 interface = (struct usb_interface_descriptor *)header;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800233
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400234 DBGX("bInterfaceClass: %d, bInterfaceSubClass: %d,"
235 "bInterfaceProtocol: %d, bNumEndpoints: %d\n",
236 interface->bInterfaceClass, interface->bInterfaceSubClass,
237 interface->bInterfaceProtocol, interface->bNumEndpoints);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800238
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400239 if (interface->bNumEndpoints == 2 &&
240 is_adb_interface(vid, pid, interface->bInterfaceClass,
241 interface->bInterfaceSubClass, interface->bInterfaceProtocol)) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800242
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400243 DBGX("looking for bulk endpoints\n");
244 // looks like ADB...
245 ep1 = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
246 ep2 = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
Mike Lockwood07e8f7e2010-01-17 00:52:27 -0500247
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400248 if (!ep1 || !ep2 ||
249 ep1->bDescriptorType != USB_DT_ENDPOINT ||
250 ep2->bDescriptorType != USB_DT_ENDPOINT) {
251 D("endpoints not found\n");
252 continue;
Mike Lockwood07e8f7e2010-01-17 00:52:27 -0500253 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800254
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400255 // both endpoints should be bulk
256 if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
257 ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
258 D("bulk endpoints not found\n");
259 continue;
260 }
261
262 // we have a match. now we just need to figure out which is in and which is out.
263 if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
264 ep_in = usb_endpoint_open(device, ep1);
265 ep_out = usb_endpoint_open(device, ep2);
266 } else {
267 ep_in = usb_endpoint_open(device, ep2);
268 ep_out = usb_endpoint_open(device, ep1);
269 }
270
271 register_device(device, interface->bInterfaceNumber, ep_in, ep_out);
272 // so we don't free it at the bottom
273 device = NULL;
274 break;
275 }
276 }
277 } // end of while
278
279 if (device)
280 usb_device_close(device);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800281}
282
283void usb_cleanup()
284{
285}
286
287static int usb_bulk_write(usb_handle *h, const void *data, int len)
288{
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400289 struct usb_endpoint *ep = h->ep_out;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800290 int res;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800291
292 D("++ write ++\n");
293
294 adb_mutex_lock(&h->lock);
295 if(h->dead) {
296 res = -1;
297 goto fail;
298 }
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400299 res = usb_endpoint_queue(ep, (void *)data, len);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800300 if(res < 0) {
301 goto fail;
302 }
303
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400304 res = pthread_cond_wait(&h->notify_out, &h->lock);
305 if (!res)
306 res = h->write_result;
307
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800308fail:
309 adb_mutex_unlock(&h->lock);
310 D("-- write --\n");
311 return res;
312}
313
314static int usb_bulk_read(usb_handle *h, void *data, int len)
315{
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400316 struct usb_endpoint *ep = h->ep_in;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800317 int res;
318
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800319 adb_mutex_lock(&h->lock);
320 if(h->dead) {
321 res = -1;
322 goto fail;
323 }
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400324 res = usb_endpoint_queue(ep, data, len);
325 if (res < 0) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800326 goto fail;
327 }
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400328 res = pthread_cond_wait(&h->notify_in, &h->lock);
329 if (!res)
330 res = h->read_result;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800331
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800332fail:
333 adb_mutex_unlock(&h->lock);
334 return res;
335}
336
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800337int usb_write(usb_handle *h, const void *_data, int len)
338{
339 unsigned char *data = (unsigned char*) _data;
340 int n;
341 int need_zero = 0;
342
343 if(h->zero_mask) {
344 /* if we need 0-markers and our transfer
345 ** is an even multiple of the packet size,
346 ** we make note of it
347 */
348 if(!(len & h->zero_mask)) {
349 need_zero = 1;
350 }
351 }
352
353 while(len > 0) {
354 int xfer = (len > 4096) ? 4096 : len;
355
356 n = usb_bulk_write(h, data, xfer);
357 if(n != xfer) {
358 D("ERROR: n = %d, errno = %d (%s)\n",
359 n, errno, strerror(errno));
360 return -1;
361 }
362
363 len -= xfer;
364 data += xfer;
365 }
366
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400367 if(need_zero) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800368 n = usb_bulk_write(h, _data, 0);
369 return n;
370 }
371
372 return 0;
373}
374
375int usb_read(usb_handle *h, void *_data, int len)
376{
377 unsigned char *data = (unsigned char*) _data;
378 int n;
379
380 D("++ usb_read ++\n");
381 while(len > 0) {
382 int xfer = (len > 4096) ? 4096 : len;
383
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800384 n = usb_bulk_read(h, data, xfer);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800385 if(n != xfer) {
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400386 if(errno == ETIMEDOUT && h->device) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800387 D("[ timeout ]\n");
388 if(n > 0){
389 data += n;
390 len -= n;
391 }
392 continue;
393 }
394 D("ERROR: n = %d, errno = %d (%s)\n",
395 n, errno, strerror(errno));
396 return -1;
397 }
398
399 len -= xfer;
400 data += xfer;
401 }
402
403 D("-- usb_read --\n");
404 return 0;
405}
406
407void usb_kick(usb_handle *h)
408{
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400409 D("[ kicking %p (fd = %s) ]\n", h, usb_device_get_name(h->device));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800410 adb_mutex_lock(&h->lock);
411 if(h->dead == 0) {
412 h->dead = 1;
413
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400414 if (usb_device_is_writeable(h->device)) {
Mike Lockwood0927bf92009-08-08 12:37:44 -0400415 /* HACK ALERT!
416 ** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB).
417 ** This is a workaround for that problem.
418 */
419 if (h->reaper_thread) {
420 pthread_kill(h->reaper_thread, SIGALRM);
421 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800422
Mike Lockwood0927bf92009-08-08 12:37:44 -0400423 /* cancel any pending transactions
424 ** these will quietly fail if the txns are not active,
425 ** but this ensures that a reader blocked on REAPURB
426 ** will get unblocked
427 */
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400428 usb_endpoint_cancel(h->ep_in);
429 usb_endpoint_cancel(h->ep_out);
430 adb_cond_broadcast(&h->notify_in);
431 adb_cond_broadcast(&h->notify_out);
Mike Lockwood0927bf92009-08-08 12:37:44 -0400432 } else {
433 unregister_usb_transport(h);
434 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800435 }
436 adb_mutex_unlock(&h->lock);
437}
438
439int usb_close(usb_handle *h)
440{
441 D("[ usb close ... ]\n");
442 adb_mutex_lock(&usb_lock);
443 h->next->prev = h->prev;
444 h->prev->next = h->next;
445 h->prev = 0;
446 h->next = 0;
447
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400448 usb_device_close(h->device);
449 D("[ usb closed %p ]\n", h);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800450 adb_mutex_unlock(&usb_lock);
451
452 free(h);
453 return 0;
454}
455
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800456static void sigalrm_handler(int signo)
457{
458 // don't need to do anything here
459}
460
461void usb_init()
462{
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800463 struct sigaction actions;
464
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400465 if (usb_host_init(check_usb_device, kick_disconnected_device))
466 fatal_errno("usb_host_init failed\n");
467
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800468 memset(&actions, 0, sizeof(actions));
469 sigemptyset(&actions.sa_mask);
470 actions.sa_flags = 0;
471 actions.sa_handler = sigalrm_handler;
472 sigaction(SIGALRM,& actions, NULL);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800473}
474