blob: 4b5edb6488b4cdd728acae2d142c25eba8f8e42c [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/version.h>
24#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
25#include <linux/usb/ch9.h>
26#else
27#include <linux/usb_ch9.h>
28#endif
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080029
30#include "sysdeps.h"
31
32#define TRACE_TAG TRACE_USB
33#include "adb.h"
34
35
36/* usb scan debugging is waaaay too verbose */
37#define DBGX(x...)
38
39static adb_mutex_t usb_lock = ADB_MUTEX_INITIALIZER;
40
41struct usb_handle
42{
43 usb_handle *prev;
44 usb_handle *next;
45
Mike Lockwoodd7249c42010-05-07 09:52:32 -040046 struct usb_device *device;
47 struct usb_endpoint *ep_in;
48 struct usb_endpoint *ep_out;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080049
Mike Lockwoodd7249c42010-05-07 09:52:32 -040050 adb_cond_t notify_in;
51 adb_cond_t notify_out;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080052 adb_mutex_t lock;
53
Mike Lockwoodd7249c42010-05-07 09:52:32 -040054 int read_result, write_result;
55 int zero_mask;
56 int dead;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080057
Mike Lockwoodd7249c42010-05-07 09:52:32 -040058 // Thread ID for our reaper thread
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080059 pthread_t reaper_thread;
60};
61
62static usb_handle handle_list = {
63 .prev = &handle_list,
64 .next = &handle_list,
65};
66
67static int known_device(const char *dev_name)
68{
69 usb_handle *usb;
70
71 adb_mutex_lock(&usb_lock);
Mike Lockwoodd7249c42010-05-07 09:52:32 -040072 for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
73 if (!strcmp(usb_device_get_name(usb->device), dev_name)) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080074 adb_mutex_unlock(&usb_lock);
75 return 1;
76 }
77 }
78 adb_mutex_unlock(&usb_lock);
79 return 0;
80}
81
Mike Lockwoodd7249c42010-05-07 09:52:32 -040082static void kick_disconnected_device(const char *devname)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080083{
84 usb_handle *usb;
85
86 adb_mutex_lock(&usb_lock);
Mike Lockwoodd7249c42010-05-07 09:52:32 -040087 /* kick the device if it is in our list */
88 for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
89 if (!strcmp(devname, usb_device_get_name(usb->device)))
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080090 usb_kick(usb);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080091 }
92 adb_mutex_unlock(&usb_lock);
93
94}
95
Mike Lockwoodd7249c42010-05-07 09:52:32 -040096static void* reaper_thread(void* arg)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080097{
Mike Lockwoodd7249c42010-05-07 09:52:32 -040098 struct usb_handle* h = (struct usb_handle *)arg;
99 int ep_in = usb_endpoint_number(h->ep_in);
100 int ep_out = usb_endpoint_number(h->ep_out);
101 int reaped_ep, res;
102
103 while (1) {
104 D("[ reap urb - wait ]\n");
105 adb_mutex_unlock(&h->lock);
106 res = usb_endpoint_wait(h->device, &reaped_ep);
107 adb_mutex_lock(&h->lock);
108 if(h->dead) {
109 res = -1;
110 break;
111 }
112
113 D("[ reaped ep %d ret = %d ]\n", reaped_ep, res);
114
115 if (reaped_ep == ep_in) {
116 D("[ reap urb - IN complete ]\n");
117 h->read_result = res;
118 adb_cond_broadcast(&h->notify_in);
119 }
120 if (reaped_ep == ep_out) {
121 D("[ reap urb - OUT compelete ]\n");
122 h->write_result = res;
123 adb_cond_broadcast(&h->notify_out);
124 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800125 }
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400126
127 return NULL;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800128}
129
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400130static void register_device(struct usb_device *device, int interface,
131 struct usb_endpoint *ep_in, struct usb_endpoint *ep_out)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800132{
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400133 usb_handle* usb = 0;
134 int ret = 0;
135 int writeable;
136 char *serial;
137 pthread_attr_t attr;
138 const char* dev_name = usb_device_get_name(device);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800139
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400140 /* Since Linux will not reassign the device ID (and dev_name)
141 ** as long as the device is open, we can add to the list here
142 ** once we open it and remove from the list when we're finally
143 ** closed and everything will work out fine.
144 **
145 ** If we have a usb_handle on the list 'o handles with a matching
146 ** name, we have no further work to do.
147 */
148 adb_mutex_lock(&usb_lock);
149 for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
150 if (!strcmp(usb_device_get_name(usb->device), dev_name)) {
151 adb_mutex_unlock(&usb_lock);
152 return;
153 }
154 }
155 adb_mutex_unlock(&usb_lock);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800156
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400157 usb = calloc(1, sizeof(usb_handle));
158 adb_cond_init(&usb->notify_in, 0);
159 adb_cond_init(&usb->notify_out, 0);
160 adb_mutex_init(&usb->lock, 0);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800161
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400162 usb->device = device;
163 usb->ep_in = ep_in;
164 usb->ep_out = ep_out;
165 usb->zero_mask = usb_endpoint_max_packet(usb->ep_out) - 1;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800166
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400167 D("[ usb open %s ]\n", dev_name);
168 writeable = usb_device_is_writeable(device);
169 if (writeable) {
170 ret = usb_device_claim_interface(device, interface);
171 if(ret != 0) goto fail;
172 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800173
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400174 /* add to the end of the active handles */
175 adb_mutex_lock(&usb_lock);
176 usb->next = &handle_list;
177 usb->prev = handle_list.prev;
178 usb->prev->next = usb;
179 usb->next->prev = usb;
180 adb_mutex_unlock(&usb_lock);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800181
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400182 pthread_attr_init(&attr);
183 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
184 pthread_create(&usb->reaper_thread, &attr, reaper_thread, usb);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800185
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400186 serial = usb_device_get_serial(device);
187 register_usb_transport(usb, serial, writeable);
188 if (serial)
189 free(serial);
190 return;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800191
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400192fail:
193 D("[ usb open %s error=%d, err_str = %s]\n",
194 dev_name, errno, strerror(errno));
195 if (usb->ep_in)
196 usb_endpoint_close(usb->ep_in);
197 if (usb->ep_out)
198 usb_endpoint_close(usb->ep_out);
199 if(device) {
200 usb_device_close(device);
201 }
202 free(usb);
203}
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800204
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400205static void check_usb_device(const char *devname) {
206 struct usb_device *device;
207 struct usb_descriptor_iter iter;
208 struct usb_descriptor_header* header;
209 struct usb_interface_descriptor* interface;
210 struct usb_endpoint_descriptor *ep1, *ep2;
211 struct usb_endpoint *ep_in = NULL, *ep_out = NULL;
212 uint16_t vid, pid;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800213
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400214 if(known_device(devname)) {
215 DBGX("skipping %s\n", devname);
216 return;
217 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800218
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400219 device = usb_device_open(devname);
220 if (!device) return;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800221
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400222 vid = usb_device_get_vendor_id(device);
223 pid = usb_device_get_product_id(device);
224 DBGX("[ %s is V:%04x P:%04x ]\n", devname, vid, pid);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800225
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400226 // loop through all the descriptors and look for the ADB interface
227 usb_descriptor_iter_init(device, &iter);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800228
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400229 while ((header = usb_descriptor_iter_next(&iter)) != NULL) {
230 if (header->bDescriptorType == USB_DT_INTERFACE) {
231 interface = (struct usb_interface_descriptor *)header;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800232
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400233 DBGX("bInterfaceClass: %d, bInterfaceSubClass: %d,"
234 "bInterfaceProtocol: %d, bNumEndpoints: %d\n",
235 interface->bInterfaceClass, interface->bInterfaceSubClass,
236 interface->bInterfaceProtocol, interface->bNumEndpoints);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800237
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400238 if (interface->bNumEndpoints == 2 &&
239 is_adb_interface(vid, pid, interface->bInterfaceClass,
240 interface->bInterfaceSubClass, interface->bInterfaceProtocol)) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800241
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400242 DBGX("looking for bulk endpoints\n");
243 // looks like ADB...
244 ep1 = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
245 ep2 = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
Mike Lockwood07e8f7e2010-01-17 00:52:27 -0500246
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400247 if (!ep1 || !ep2 ||
248 ep1->bDescriptorType != USB_DT_ENDPOINT ||
249 ep2->bDescriptorType != USB_DT_ENDPOINT) {
250 D("endpoints not found\n");
251 continue;
Mike Lockwood07e8f7e2010-01-17 00:52:27 -0500252 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800253
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400254 // both endpoints should be bulk
255 if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
256 ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
257 D("bulk endpoints not found\n");
258 continue;
259 }
260
261 // we have a match. now we just need to figure out which is in and which is out.
262 if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
263 ep_in = usb_endpoint_open(device, ep1);
264 ep_out = usb_endpoint_open(device, ep2);
265 } else {
266 ep_in = usb_endpoint_open(device, ep2);
267 ep_out = usb_endpoint_open(device, ep1);
268 }
269
270 register_device(device, interface->bInterfaceNumber, ep_in, ep_out);
271 // so we don't free it at the bottom
272 device = NULL;
273 break;
274 }
275 }
276 } // end of while
277
278 if (device)
279 usb_device_close(device);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800280}
281
282void usb_cleanup()
283{
284}
285
286static int usb_bulk_write(usb_handle *h, const void *data, int len)
287{
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400288 struct usb_endpoint *ep = h->ep_out;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800289 int res;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800290
291 D("++ write ++\n");
292
293 adb_mutex_lock(&h->lock);
294 if(h->dead) {
295 res = -1;
296 goto fail;
297 }
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400298 res = usb_endpoint_queue(ep, (void *)data, len);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800299 if(res < 0) {
300 goto fail;
301 }
302
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400303 res = pthread_cond_wait(&h->notify_out, &h->lock);
304 if (!res)
305 res = h->write_result;
306
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800307fail:
308 adb_mutex_unlock(&h->lock);
309 D("-- write --\n");
310 return res;
311}
312
313static int usb_bulk_read(usb_handle *h, void *data, int len)
314{
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400315 struct usb_endpoint *ep = h->ep_in;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800316 int res;
317
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800318 adb_mutex_lock(&h->lock);
319 if(h->dead) {
320 res = -1;
321 goto fail;
322 }
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400323 res = usb_endpoint_queue(ep, data, len);
324 if (res < 0) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800325 goto fail;
326 }
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400327 res = pthread_cond_wait(&h->notify_in, &h->lock);
328 if (!res)
329 res = h->read_result;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800330
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800331fail:
332 adb_mutex_unlock(&h->lock);
333 return res;
334}
335
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800336int usb_write(usb_handle *h, const void *_data, int len)
337{
338 unsigned char *data = (unsigned char*) _data;
339 int n;
340 int need_zero = 0;
341
342 if(h->zero_mask) {
343 /* if we need 0-markers and our transfer
344 ** is an even multiple of the packet size,
345 ** we make note of it
346 */
347 if(!(len & h->zero_mask)) {
348 need_zero = 1;
349 }
350 }
351
352 while(len > 0) {
353 int xfer = (len > 4096) ? 4096 : len;
354
355 n = usb_bulk_write(h, data, xfer);
356 if(n != xfer) {
357 D("ERROR: n = %d, errno = %d (%s)\n",
358 n, errno, strerror(errno));
359 return -1;
360 }
361
362 len -= xfer;
363 data += xfer;
364 }
365
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400366 if(need_zero) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800367 n = usb_bulk_write(h, _data, 0);
368 return n;
369 }
370
371 return 0;
372}
373
374int usb_read(usb_handle *h, void *_data, int len)
375{
376 unsigned char *data = (unsigned char*) _data;
377 int n;
378
379 D("++ usb_read ++\n");
380 while(len > 0) {
381 int xfer = (len > 4096) ? 4096 : len;
382
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800383 n = usb_bulk_read(h, data, xfer);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800384 if(n != xfer) {
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400385 if(errno == ETIMEDOUT && h->device) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800386 D("[ timeout ]\n");
387 if(n > 0){
388 data += n;
389 len -= n;
390 }
391 continue;
392 }
393 D("ERROR: n = %d, errno = %d (%s)\n",
394 n, errno, strerror(errno));
395 return -1;
396 }
397
398 len -= xfer;
399 data += xfer;
400 }
401
402 D("-- usb_read --\n");
403 return 0;
404}
405
406void usb_kick(usb_handle *h)
407{
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400408 D("[ kicking %p (fd = %s) ]\n", h, usb_device_get_name(h->device));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800409 adb_mutex_lock(&h->lock);
410 if(h->dead == 0) {
411 h->dead = 1;
412
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400413 if (usb_device_is_writeable(h->device)) {
Mike Lockwood0927bf92009-08-08 12:37:44 -0400414 /* HACK ALERT!
415 ** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB).
416 ** This is a workaround for that problem.
417 */
418 if (h->reaper_thread) {
419 pthread_kill(h->reaper_thread, SIGALRM);
420 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800421
Mike Lockwood0927bf92009-08-08 12:37:44 -0400422 /* cancel any pending transactions
423 ** these will quietly fail if the txns are not active,
424 ** but this ensures that a reader blocked on REAPURB
425 ** will get unblocked
426 */
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400427 usb_endpoint_cancel(h->ep_in);
428 usb_endpoint_cancel(h->ep_out);
429 adb_cond_broadcast(&h->notify_in);
430 adb_cond_broadcast(&h->notify_out);
Mike Lockwood0927bf92009-08-08 12:37:44 -0400431 } else {
432 unregister_usb_transport(h);
433 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800434 }
435 adb_mutex_unlock(&h->lock);
436}
437
438int usb_close(usb_handle *h)
439{
440 D("[ usb close ... ]\n");
441 adb_mutex_lock(&usb_lock);
442 h->next->prev = h->prev;
443 h->prev->next = h->next;
444 h->prev = 0;
445 h->next = 0;
446
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400447 usb_device_close(h->device);
448 D("[ usb closed %p ]\n", h);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800449 adb_mutex_unlock(&usb_lock);
450
451 free(h);
452 return 0;
453}
454
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800455static void sigalrm_handler(int signo)
456{
457 // don't need to do anything here
458}
459
460void usb_init()
461{
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800462 struct sigaction actions;
463
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400464 if (usb_host_init(check_usb_device, kick_disconnected_device))
465 fatal_errno("usb_host_init failed\n");
466
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800467 memset(&actions, 0, sizeof(actions));
468 sigemptyset(&actions.sa_mask);
469 actions.sa_flags = 0;
470 actions.sa_handler = sigalrm_handler;
471 sigaction(SIGALRM,& actions, NULL);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800472}
473