blob: 31fe6f67c96baa830027ae3c6b2052a2c56d6e0d [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 Lockwood36b99e42010-05-25 08:11:17 -040082static void kick_disconnected_device(const char *devname, void *client_data)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080083{
Mike Lockwooda78012a2010-06-14 12:08:38 -070084 usb_handle *h;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080085
86 adb_mutex_lock(&usb_lock);
Mike Lockwoodd7249c42010-05-07 09:52:32 -040087 /* kick the device if it is in our list */
Mike Lockwooda78012a2010-06-14 12:08:38 -070088 for (h = handle_list.next; h != &handle_list; h = h->next) {
89 if (!strcmp(devname, usb_device_get_name(h->device))) {
90 D("[ kicking %p (fd = %s) ]\n", h, usb_device_get_name(h->device));
91 adb_mutex_lock(&h->lock);
92 if(h->dead == 0) {
93 h->dead = 1;
94
95 if (usb_device_is_writeable(h->device)) {
96 /* HACK ALERT!
97 ** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB).
98 ** This is a workaround for that problem.
99 */
100 if (h->reaper_thread) {
101 pthread_kill(h->reaper_thread, SIGALRM);
102 }
103
104 /* cancel any pending transactions
105 ** these will quietly fail if the txns are not active,
106 ** but this ensures that a reader blocked on REAPURB
107 ** will get unblocked
108 */
109 usb_endpoint_cancel(h->ep_in);
110 usb_endpoint_cancel(h->ep_out);
111 adb_cond_broadcast(&h->notify_in);
112 adb_cond_broadcast(&h->notify_out);
113 } else {
114 unregister_usb_transport(h);
115 }
116 }
117 adb_mutex_unlock(&h->lock);
118 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800119 }
120 adb_mutex_unlock(&usb_lock);
121
122}
123
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400124static void* reaper_thread(void* arg)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800125{
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400126 struct usb_handle* h = (struct usb_handle *)arg;
127 int ep_in = usb_endpoint_number(h->ep_in);
128 int ep_out = usb_endpoint_number(h->ep_out);
129 int reaped_ep, res;
130
131 while (1) {
132 D("[ reap urb - wait ]\n");
133 adb_mutex_unlock(&h->lock);
134 res = usb_endpoint_wait(h->device, &reaped_ep);
135 adb_mutex_lock(&h->lock);
136 if(h->dead) {
137 res = -1;
138 break;
139 }
140
141 D("[ reaped ep %d ret = %d ]\n", reaped_ep, res);
142
143 if (reaped_ep == ep_in) {
144 D("[ reap urb - IN complete ]\n");
145 h->read_result = res;
146 adb_cond_broadcast(&h->notify_in);
147 }
148 if (reaped_ep == ep_out) {
149 D("[ reap urb - OUT compelete ]\n");
150 h->write_result = res;
151 adb_cond_broadcast(&h->notify_out);
152 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800153 }
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400154
155 return NULL;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800156}
157
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400158static void register_device(struct usb_device *device, int interface,
159 struct usb_endpoint *ep_in, struct usb_endpoint *ep_out)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800160{
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400161 usb_handle* usb = 0;
162 int ret = 0;
163 int writeable;
164 char *serial;
165 pthread_attr_t attr;
166 const char* dev_name = usb_device_get_name(device);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800167
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400168 /* Since Linux will not reassign the device ID (and dev_name)
169 ** as long as the device is open, we can add to the list here
170 ** once we open it and remove from the list when we're finally
171 ** closed and everything will work out fine.
172 **
173 ** If we have a usb_handle on the list 'o handles with a matching
174 ** name, we have no further work to do.
175 */
176 adb_mutex_lock(&usb_lock);
177 for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
178 if (!strcmp(usb_device_get_name(usb->device), dev_name)) {
179 adb_mutex_unlock(&usb_lock);
180 return;
181 }
182 }
183 adb_mutex_unlock(&usb_lock);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800184
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400185 usb = calloc(1, sizeof(usb_handle));
186 adb_cond_init(&usb->notify_in, 0);
187 adb_cond_init(&usb->notify_out, 0);
188 adb_mutex_init(&usb->lock, 0);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800189
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400190 usb->device = device;
191 usb->ep_in = ep_in;
192 usb->ep_out = ep_out;
193 usb->zero_mask = usb_endpoint_max_packet(usb->ep_out) - 1;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800194
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400195 D("[ usb open %s ]\n", dev_name);
196 writeable = usb_device_is_writeable(device);
197 if (writeable) {
198 ret = usb_device_claim_interface(device, interface);
199 if(ret != 0) goto fail;
200 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800201
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400202 /* add to the end of the active handles */
203 adb_mutex_lock(&usb_lock);
204 usb->next = &handle_list;
205 usb->prev = handle_list.prev;
206 usb->prev->next = usb;
207 usb->next->prev = usb;
208 adb_mutex_unlock(&usb_lock);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800209
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400210 pthread_attr_init(&attr);
211 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
212 pthread_create(&usb->reaper_thread, &attr, reaper_thread, usb);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800213
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400214 serial = usb_device_get_serial(device);
215 register_usb_transport(usb, serial, writeable);
216 if (serial)
217 free(serial);
218 return;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800219
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400220fail:
221 D("[ usb open %s error=%d, err_str = %s]\n",
222 dev_name, errno, strerror(errno));
223 if (usb->ep_in)
224 usb_endpoint_close(usb->ep_in);
225 if (usb->ep_out)
226 usb_endpoint_close(usb->ep_out);
227 if(device) {
228 usb_device_close(device);
229 }
230 free(usb);
231}
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800232
Mike Lockwood36b99e42010-05-25 08:11:17 -0400233static void check_usb_device(const char *devname, void *client_data) {
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400234 struct usb_device *device;
235 struct usb_descriptor_iter iter;
236 struct usb_descriptor_header* header;
237 struct usb_interface_descriptor* interface;
238 struct usb_endpoint_descriptor *ep1, *ep2;
239 struct usb_endpoint *ep_in = NULL, *ep_out = NULL;
240 uint16_t vid, pid;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800241
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400242 if(known_device(devname)) {
243 DBGX("skipping %s\n", devname);
244 return;
245 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800246
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400247 device = usb_device_open(devname);
248 if (!device) return;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800249
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400250 vid = usb_device_get_vendor_id(device);
251 pid = usb_device_get_product_id(device);
252 DBGX("[ %s is V:%04x P:%04x ]\n", devname, vid, pid);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800253
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400254 // loop through all the descriptors and look for the ADB interface
255 usb_descriptor_iter_init(device, &iter);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800256
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400257 while ((header = usb_descriptor_iter_next(&iter)) != NULL) {
258 if (header->bDescriptorType == USB_DT_INTERFACE) {
259 interface = (struct usb_interface_descriptor *)header;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800260
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400261 DBGX("bInterfaceClass: %d, bInterfaceSubClass: %d,"
262 "bInterfaceProtocol: %d, bNumEndpoints: %d\n",
263 interface->bInterfaceClass, interface->bInterfaceSubClass,
264 interface->bInterfaceProtocol, interface->bNumEndpoints);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800265
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400266 if (interface->bNumEndpoints == 2 &&
267 is_adb_interface(vid, pid, interface->bInterfaceClass,
268 interface->bInterfaceSubClass, interface->bInterfaceProtocol)) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800269
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400270 DBGX("looking for bulk endpoints\n");
271 // looks like ADB...
272 ep1 = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
273 ep2 = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
Mike Lockwood07e8f7e2010-01-17 00:52:27 -0500274
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400275 if (!ep1 || !ep2 ||
276 ep1->bDescriptorType != USB_DT_ENDPOINT ||
277 ep2->bDescriptorType != USB_DT_ENDPOINT) {
278 D("endpoints not found\n");
279 continue;
Mike Lockwood07e8f7e2010-01-17 00:52:27 -0500280 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800281
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400282 // both endpoints should be bulk
283 if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
284 ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
285 D("bulk endpoints not found\n");
286 continue;
287 }
288
289 // we have a match. now we just need to figure out which is in and which is out.
290 if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
291 ep_in = usb_endpoint_open(device, ep1);
292 ep_out = usb_endpoint_open(device, ep2);
293 } else {
294 ep_in = usb_endpoint_open(device, ep2);
295 ep_out = usb_endpoint_open(device, ep1);
296 }
297
298 register_device(device, interface->bInterfaceNumber, ep_in, ep_out);
299 // so we don't free it at the bottom
300 device = NULL;
301 break;
302 }
303 }
304 } // end of while
305
306 if (device)
307 usb_device_close(device);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800308}
309
310void usb_cleanup()
311{
312}
313
314static int usb_bulk_write(usb_handle *h, const void *data, int len)
315{
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400316 struct usb_endpoint *ep = h->ep_out;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800317 int res;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800318
319 D("++ write ++\n");
320
321 adb_mutex_lock(&h->lock);
322 if(h->dead) {
323 res = -1;
324 goto fail;
325 }
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400326 res = usb_endpoint_queue(ep, (void *)data, len);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800327 if(res < 0) {
328 goto fail;
329 }
330
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400331 res = pthread_cond_wait(&h->notify_out, &h->lock);
332 if (!res)
333 res = h->write_result;
334
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800335fail:
336 adb_mutex_unlock(&h->lock);
337 D("-- write --\n");
338 return res;
339}
340
341static int usb_bulk_read(usb_handle *h, void *data, int len)
342{
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400343 struct usb_endpoint *ep = h->ep_in;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800344 int res;
345
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800346 adb_mutex_lock(&h->lock);
347 if(h->dead) {
348 res = -1;
349 goto fail;
350 }
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400351 res = usb_endpoint_queue(ep, data, len);
352 if (res < 0) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800353 goto fail;
354 }
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400355 res = pthread_cond_wait(&h->notify_in, &h->lock);
356 if (!res)
357 res = h->read_result;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800358
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800359fail:
360 adb_mutex_unlock(&h->lock);
361 return res;
362}
363
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800364int usb_write(usb_handle *h, const void *_data, int len)
365{
366 unsigned char *data = (unsigned char*) _data;
367 int n;
368 int need_zero = 0;
369
370 if(h->zero_mask) {
371 /* if we need 0-markers and our transfer
372 ** is an even multiple of the packet size,
373 ** we make note of it
374 */
375 if(!(len & h->zero_mask)) {
376 need_zero = 1;
377 }
378 }
379
380 while(len > 0) {
381 int xfer = (len > 4096) ? 4096 : len;
382
383 n = usb_bulk_write(h, data, xfer);
384 if(n != xfer) {
385 D("ERROR: n = %d, errno = %d (%s)\n",
386 n, errno, strerror(errno));
387 return -1;
388 }
389
390 len -= xfer;
391 data += xfer;
392 }
393
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400394 if(need_zero) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800395 n = usb_bulk_write(h, _data, 0);
396 return n;
397 }
398
399 return 0;
400}
401
402int usb_read(usb_handle *h, void *_data, int len)
403{
404 unsigned char *data = (unsigned char*) _data;
405 int n;
406
407 D("++ usb_read ++\n");
408 while(len > 0) {
409 int xfer = (len > 4096) ? 4096 : len;
410
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800411 n = usb_bulk_read(h, data, xfer);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800412 if(n != xfer) {
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400413 if(errno == ETIMEDOUT && h->device) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800414 D("[ timeout ]\n");
415 if(n > 0){
416 data += n;
417 len -= n;
418 }
419 continue;
420 }
421 D("ERROR: n = %d, errno = %d (%s)\n",
422 n, errno, strerror(errno));
423 return -1;
424 }
425
426 len -= xfer;
427 data += xfer;
428 }
429
430 D("-- usb_read --\n");
431 return 0;
432}
433
434void usb_kick(usb_handle *h)
435{
Mike Lockwooda78012a2010-06-14 12:08:38 -0700436 // do nothing here. we kick in kick_disconnected_devices instead.
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800437}
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 Lockwood36b99e42010-05-25 08:11:17 -0400465 if (usb_host_init(check_usb_device, kick_disconnected_device, NULL))
Mike Lockwoodd7249c42010-05-07 09:52:32 -0400466 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