platform_system_core/adb/usb_libusb.c
Scott Anderson e109d266c1 adb: Add ability to specify device path
For manufacturing and testing, there is a need to talk to
whatever device is connected to a given port on the host.  This
change modifies adb's "-s" option to take either a serial
number or a device path.  The device paths of the connected
devices can be listed using "adb devices -l" whose output
will resemble:

    List of devices attached
    016B75D60A00600D	usb:2-5	device
    3031D0B2E71D00EC	usb:1-4.3	device

The second column lists the device paths.  If the -l option is
not given, the output from "adb devices" will be the same as
it used to be (i.e. the paths will not be printed).

The device path can also be obtained with the get-devpath
command:

    $adb -s 3031D0B2E71D00EC get-devpath
    usb:1-4.3

Note that the format of the device paths are platform dependent.
The example above is from Linux.  On OS-X, the paths will be
"usb:" followed by hex digits.  For other platforms, the device
paths will be printed as "????????????" and the -s option will
not be able to select a device until someone implements the
underlying functionality.

Change-Id: I057d5d9f8c5bb72eddf5b8088aae110763f809d7
Signed-off-by: Scott Anderson <saa@android.com>
2012-04-20 11:21:14 -07:00

657 lines
16 KiB
C

/*
* Copyright (C) 2009 bsdroid project
* Alexey Tarasov <tarasov@dodologics.com>
*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <sys/endian.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <err.h>
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <libusb.h>
#include "sysdeps.h"
#define TRACE_TAG TRACE_USB
#include "adb.h"
static adb_mutex_t usb_lock = ADB_MUTEX_INITIALIZER;
static libusb_context *ctx = NULL;
struct usb_handle
{
usb_handle *prev;
usb_handle *next;
libusb_device *dev;
libusb_device_handle *devh;
int interface;
uint8_t dev_bus;
uint8_t dev_addr;
int zero_mask;
unsigned char end_point_address[2];
char serial[128];
adb_cond_t notify;
adb_mutex_t lock;
};
static struct usb_handle handle_list = {
.prev = &handle_list,
.next = &handle_list,
};
void
usb_cleanup()
{
libusb_exit(ctx);
}
void
report_bulk_libusb_error(int r)
{
switch (r) {
case LIBUSB_ERROR_TIMEOUT:
D("Transfer timeout\n");
break;
case LIBUSB_ERROR_PIPE:
D("Control request is not supported\n");
break;
case LIBUSB_ERROR_OVERFLOW:
D("Device offered more data\n");
break;
case LIBUSB_ERROR_NO_DEVICE :
D("Device was disconnected\n");
break;
default:
D("Error %d during transfer\n", r);
break;
};
}
static int
usb_bulk_write(usb_handle *uh, const void *data, int len)
{
int r = 0;
int transferred = 0;
r = libusb_bulk_transfer(uh->devh, uh->end_point_address[1], (void *)data, len,
&transferred, 0);
if (r != 0) {
D("usb_bulk_write(): ");
report_bulk_libusb_error(r);
return r;
}
return (transferred);
}
static int
usb_bulk_read(usb_handle *uh, void *data, int len)
{
int r = 0;
int transferred = 0;
r = libusb_bulk_transfer(uh->devh, uh->end_point_address[0], data, len,
&transferred, 0);
if (r != 0) {
D("usb_bulk_read(): ");
report_bulk_libusb_error(r);
return r;
}
return (transferred);
}
int
usb_write(struct usb_handle *uh, const void *_data, int len)
{
unsigned char *data = (unsigned char*) _data;
int n;
int need_zero = 0;
if (uh->zero_mask == 1) {
if (!(len & uh->zero_mask)) {
need_zero = 1;
}
}
D("usb_write(): %p:%d -> transport %p\n", _data, len, uh);
while (len > 0) {
int xfer = (len > 4096) ? 4096 : len;
n = usb_bulk_write(uh, data, xfer);
if (n != xfer) {
D("usb_write(): failed for transport %p (%d bytes left)\n", uh, len);
return -1;
}
len -= xfer;
data += xfer;
}
if (need_zero){
n = usb_bulk_write(uh, _data, 0);
if (n < 0) {
D("usb_write(): failed to finish operation for transport %p\n", uh);
}
return n;
}
return 0;
}
int
usb_read(struct usb_handle *uh, void *_data, int len)
{
unsigned char *data = (unsigned char*) _data;
int n;
D("usb_read(): %p:%d <- transport %p\n", _data, len, uh);
while (len > 0) {
int xfer = (len > 4096) ? 4096 : len;
n = usb_bulk_read(uh, data, xfer);
if (n != xfer) {
if (n > 0) {
data += n;
len -= n;
continue;
}
D("usb_read(): failed for transport %p (%d bytes left)\n", uh, len);
return -1;
}
len -= xfer;
data += xfer;
}
return 0;
}
int
usb_close(struct usb_handle *h)
{
D("usb_close(): closing transport %p\n", h);
adb_mutex_lock(&usb_lock);
h->next->prev = h->prev;
h->prev->next = h->next;
h->prev = NULL;
h->next = NULL;
libusb_release_interface(h->devh, h->interface);
libusb_close(h->devh);
libusb_unref_device(h->dev);
adb_mutex_unlock(&usb_lock);
free(h);
return (0);
}
void usb_kick(struct usb_handle *h)
{
D("usb_cick(): kicking transport %p\n", h);
adb_mutex_lock(&h->lock);
unregister_usb_transport(h);
adb_mutex_unlock(&h->lock);
h->next->prev = h->prev;
h->prev->next = h->next;
h->prev = NULL;
h->next = NULL;
libusb_release_interface(h->devh, h->interface);
libusb_close(h->devh);
libusb_unref_device(h->dev);
free(h);
}
int
check_usb_interface(libusb_interface *interface,
libusb_device_descriptor *desc,
struct usb_handle *uh)
{
int e;
if (interface->num_altsetting == 0) {
D("check_usb_interface(): No interface settings\n");
return -1;
}
libusb_interface_descriptor *idesc = &interface->altsetting[0];
if (idesc->bNumEndpoints != 2) {
D("check_usb_interface(): Interface have not 2 endpoints, ignoring\n");
return -1;
}
for (e = 0; e < idesc->bNumEndpoints; e++) {
libusb_endpoint_descriptor *edesc = &idesc->endpoint[e];
if (edesc->bmAttributes != LIBUSB_TRANSFER_TYPE_BULK) {
D("check_usb_interface(): Endpoint (%u) is not bulk (%u), ignoring\n",
edesc->bmAttributes, LIBUSB_TRANSFER_TYPE_BULK);
return -1;
}
if (edesc->bEndpointAddress & LIBUSB_ENDPOINT_IN)
uh->end_point_address[0] = edesc->bEndpointAddress;
else
uh->end_point_address[1] = edesc->bEndpointAddress;
/* aproto 01 needs 0 termination */
if (idesc->bInterfaceProtocol == 0x01) {
uh->zero_mask = edesc->wMaxPacketSize - 1;
D("check_usb_interface(): Forced Android interface protocol v.1\n");
}
}
D("check_usb_interface(): Device: %04x:%04x "
"iclass: %x, isclass: %x, iproto: %x ep: %x/%x-> ",
desc->idVendor, desc->idProduct, idesc->bInterfaceClass,
idesc->bInterfaceSubClass, idesc->bInterfaceProtocol,
uh->end_point_address[0], uh->end_point_address[1]);
if (!is_adb_interface(desc->idVendor, desc->idProduct,
idesc->bInterfaceClass, idesc->bInterfaceSubClass,
idesc->bInterfaceProtocol))
{
D("not matches\n");
return -1;
}
D("matches\n");
return 1;
}
int
check_usb_interfaces(libusb_config_descriptor *config,
libusb_device_descriptor *desc, struct usb_handle *uh)
{
int i;
for (i = 0; i < config->bNumInterfaces; ++i) {
if (check_usb_interface(&config->interface[i], desc, uh) != -1) {
/* found some interface and saved information about it */
D("check_usb_interfaces(): Interface %d of %04x:%04x "
"matches Android device\n", i, desc->idVendor,
desc->idProduct);
return i;
}
}
return -1;
}
int
register_device(struct usb_handle *uh, const char *serial)
{
D("register_device(): Registering %p [%s] as USB transport\n",
uh, serial);
struct usb_handle *usb= NULL;
usb = calloc(1, sizeof(struct usb_handle));
memcpy(usb, uh, sizeof(struct usb_handle));
strcpy(usb->serial, uh->serial);
adb_cond_init(&usb->notify, 0);
adb_mutex_init(&usb->lock, 0);
adb_mutex_lock(&usb_lock);
usb->next = &handle_list;
usb->prev = handle_list.prev;
usb->prev->next = usb;
usb->next->prev = usb;
adb_mutex_unlock(&usb_lock);
register_usb_transport(usb, serial, NULL, 1);
return (1);
}
int
already_registered(usb_handle *uh)
{
struct usb_handle *usb= NULL;
int exists = 0;
adb_mutex_lock(&usb_lock);
for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
if ((usb->dev_bus == uh->dev_bus) &&
(usb->dev_addr == uh->dev_addr))
{
exists = 1;
break;
}
}
adb_mutex_unlock(&usb_lock);
return exists;
}
void
check_device(libusb_device *dev)
{
struct usb_handle uh;
int i = 0;
int found = -1;
char serial[256] = {0};
libusb_device_descriptor desc;
libusb_config_descriptor *config = NULL;
int r = libusb_get_device_descriptor(dev, &desc);
if (r != LIBUSB_SUCCESS) {
D("check_device(): Failed to get device descriptor\n");
return;
}
if ((desc.idVendor == 0) && (desc.idProduct == 0))
return;
D("check_device(): Probing usb device %04x:%04x\n",
desc.idVendor, desc.idProduct);
if (!is_adb_interface (desc.idVendor, desc.idProduct,
ADB_CLASS, ADB_SUBCLASS, ADB_PROTOCOL))
{
D("check_device(): Ignored due unknown vendor id\n");
return;
}
uh.dev_bus = libusb_get_bus_number(dev);
uh.dev_addr = libusb_get_device_address(dev);
if (already_registered(&uh)) {
D("check_device(): Device (bus: %d, address: %d) "
"is already registered\n", uh.dev_bus, uh.dev_addr);
return;
}
D("check_device(): Device bus: %d, address: %d\n",
uh.dev_bus, uh.dev_addr);
r = libusb_get_active_config_descriptor(dev, &config);
if (r != 0) {
if (r == LIBUSB_ERROR_NOT_FOUND) {
D("check_device(): Device %4x:%4x is unconfigured\n",
desc.idVendor, desc.idProduct);
return;
}
D("check_device(): Failed to get configuration for %4x:%4x\n",
desc.idVendor, desc.idProduct);
return;
}
if (config == NULL) {
D("check_device(): Sanity check failed after "
"getting active config\n");
return;
}
if (config->interface != NULL) {
found = check_usb_interfaces(config, &desc, &uh);
}
/* not needed anymore */
libusb_free_config_descriptor(config);
r = libusb_open(dev, &uh.devh);
uh.dev = dev;
if (r != 0) {
switch (r) {
case LIBUSB_ERROR_NO_MEM:
D("check_device(): Memory allocation problem\n");
break;
case LIBUSB_ERROR_ACCESS:
D("check_device(): Permissions problem, "
"current user priveleges are messed up?\n");
break;
case LIBUSB_ERROR_NO_DEVICE:
D("check_device(): Device disconected, bad cable?\n");
break;
default:
D("check_device(): libusb triggered error %d\n", r);
}
// skip rest
found = -1;
}
if (found >= 0) {
D("check_device(): Device matches Android interface\n");
// read the device's serial number
memset(serial, 0, sizeof(serial));
uh.interface = found;
r = libusb_claim_interface(uh.devh, uh.interface);
if (r < 0) {
D("check_device(): Failed to claim interface %d\n",
uh.interface);
goto fail;
}
if (desc.iSerialNumber) {
// reading serial
uint16_t buffer[128] = {0};
uint16_t languages[128] = {0};
int languageCount = 0;
memset(languages, 0, sizeof(languages));
r = libusb_control_transfer(uh.devh,
LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_STRING << 8,
0, (uint8_t *)languages, sizeof(languages), 0);
if (r <= 0) {
D("check_device(): Failed to get languages count\n");
goto fail;
}
languageCount = (r - 2) / 2;
for (i = 1; i <= languageCount; ++i) {
memset(buffer, 0, sizeof(buffer));
r = libusb_control_transfer(uh.devh,
LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc.iSerialNumber,
languages[i], (uint8_t *)buffer, sizeof(buffer), 0);
if (r > 0) { /* converting serial */
int j = 0;
r /= 2;
for (j = 1; j < r; ++j)
serial[j - 1] = buffer[j];
serial[j - 1] = '\0';
break; /* languagesCount cycle */
}
}
if (register_device(&uh, serial) == 0) {
D("check_device(): Failed to register device\n");
goto fail_interface;
}
libusb_ref_device(dev);
}
}
return;
fail_interface:
libusb_release_interface(uh.devh, uh.interface);
fail:
libusb_close(uh.devh);
uh.devh = NULL;
}
int
check_device_connected(struct usb_handle *uh)
{
int r = libusb_kernel_driver_active(uh->devh, uh->interface);
if (r == LIBUSB_ERROR_NO_DEVICE)
return 0;
if (r < 0)
return -1;
return 1;
}
void
kick_disconnected()
{
struct usb_handle *usb= NULL;
adb_mutex_lock(&usb_lock);
for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
if (check_device_connected(usb) == 0) {
D("kick_disconnected(): Transport %p is not online anymore\n",
usb);
usb_kick(usb);
}
}
adb_mutex_unlock(&usb_lock);
}
void
scan_usb_devices()
{
D("scan_usb_devices(): started\n");
libusb_device **devs= NULL;
libusb_device *dev= NULL;
ssize_t cnt = libusb_get_device_list(ctx, &devs);
if (cnt < 0) {
D("scan_usb_devices(): Failed to get device list (error: %d)\n",
cnt);
return;
}
int i = 0;
while ((dev = devs[i++]) != NULL) {
check_device(dev);
}
libusb_free_device_list(devs, 1);
}
void *
device_poll_thread(void* unused)
{
D("device_poll_thread(): Created USB scan thread\n");
for (;;) {
sleep(5);
kick_disconnected();
scan_usb_devices();
}
/* never reaching this point */
return (NULL);
}
static void
sigalrm_handler(int signo)
{
/* nothing */
}
void
usb_init()
{
D("usb_init(): started\n");
adb_thread_t tid;
struct sigaction actions;
int r = libusb_init(&ctx);
if (r != LIBUSB_SUCCESS) {
err(EX_IOERR, "Failed to init libusb\n");
}
memset(&actions, 0, sizeof(actions));
sigemptyset(&actions.sa_mask);
actions.sa_flags = 0;
actions.sa_handler = sigalrm_handler;
sigaction(SIGALRM, &actions, NULL);
/* initial device scan */
scan_usb_devices();
/* starting USB event polling thread */
if (adb_thread_create(&tid, device_poll_thread, NULL)) {
err(EX_IOERR, "cannot create USB scan thread\n");
}
D("usb_init(): finished\n");
}