e109d266c1
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>
657 lines
16 KiB
C
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");
|
|
}
|
|
|