blob: 60de53c3fb6919e123aeedc8fffd175b9c720784 [file] [log] [blame]
/*
* 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.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <ctype.h>
#include <linux/usbdevice_fs.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
#include <linux/usb/ch9.h>
#else
#include <linux/usb_ch9.h>
#endif
#include <asm/byteorder.h>
#include <cutils/fdevent.h>
#include "adb.h"
#define TRACE_USB 0
#if TRACE_USB
#define DBG1(x...) fprintf(stderr, x)
#define DBG(x...) fprintf(stderr, x)
#else
#define DBG(x...)
#define DBG1(x...)
#endif
struct usb_handle
{
struct usb_handle *next;
char fname[32];
int desc;
unsigned char ep_in;
unsigned char ep_out;
unsigned int interface;
};
static struct usb_handle *g_first_usb_device;
static struct usb_handle *g_last_usb_device;
static void new_device(char *dev_name, unsigned char ep_in, unsigned char ep_out, unsigned int interface)
{
struct usb_handle* usb;
DBG("New device being added %s \n", dev_name);
usb = (struct usb_handle *)calloc(1, sizeof(struct usb_handle));
strcpy(usb->fname, dev_name);
usb->ep_in = ep_in;
usb->ep_out = ep_out;
usb->interface = interface;
usb->next = NULL;
if(g_last_usb_device)
g_last_usb_device->next = usb;
else
g_first_usb_device = usb;
g_last_usb_device = usb;
}
static inline int badname(const char *name)
{
if(!isdigit(name[0])) return 1;
if(!isdigit(name[1])) return 1;
if(!isdigit(name[2])) return 1;
if(name[3] != 0) return 1;
return 0;
}
static int find_usb_devices(const char *base, unsigned vendor, unsigned product1, unsigned product2,
unsigned ifclass, unsigned ifsubclass,
unsigned ifprotocol, unsigned numendpoints)
{
char busname[32], devname[32];
unsigned char local_ep_in, local_ep_out;
DIR *busdir , *devdir ;
struct dirent *de;
int fd ;
int ret_val = -1;
int found_device = 0;
busdir = opendir(base);
if(busdir == 0) return 0;
while((de = readdir(busdir)) != 0) {
if(badname(de->d_name)) continue;
snprintf(busname, sizeof busname, "%s/%s", base, de->d_name);
devdir = opendir(busname);
if(devdir == 0) continue;
DBG("[ scanning %s ]\n", busname);
while((de = readdir(devdir))) {
if(badname(de->d_name)) continue;
snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name);
DBG("[ scanning %s ]\n", devname);
fd = open(devname, O_RDWR);
if(fd < 0) {
continue;
} else {
unsigned char devdesc[256];
unsigned char* bufptr = devdesc;
struct usb_device_descriptor* device;
struct usb_config_descriptor* config;
struct usb_interface_descriptor* interface;
struct usb_endpoint_descriptor *ep1, *ep2;
unsigned vid, pid;
int i, interfaces;
size_t desclength = read(fd, devdesc, sizeof(devdesc));
// should have device and configuration descriptors, and atleast two endpoints
if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
DBG("desclength %d is too small\n", desclength);
close(fd);
continue;
}
device = (struct usb_device_descriptor*)bufptr;
bufptr += USB_DT_DEVICE_SIZE;
if(device->bLength == USB_DT_DEVICE_SIZE && device->bDescriptorType == USB_DT_DEVICE) {
vid = __le16_to_cpu(device->idVendor);
pid = __le16_to_cpu(device->idProduct);
pid = devdesc[10] | (devdesc[11] << 8);
DBG("[ %s is V:%04x P:%04x ]\n", devname, vid, pid);
if((vendor == vid) && (product1 == pid || product2 == pid)){
// should have config descriptor next
config = (struct usb_config_descriptor *)bufptr;
bufptr += USB_DT_CONFIG_SIZE;
if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
DBG("usb_config_descriptor not found\n");
close(fd);
continue;
}
// loop through all the interfaces and look for the ADB interface
interfaces = config->bNumInterfaces;
for (i = 0; i < interfaces; i++) {
if (bufptr + USB_DT_ENDPOINT_SIZE > devdesc + desclength)
break;
interface = (struct usb_interface_descriptor *)bufptr;
bufptr += USB_DT_INTERFACE_SIZE;
if (interface->bLength != USB_DT_INTERFACE_SIZE ||
interface->bDescriptorType != USB_DT_INTERFACE) {
DBG("usb_interface_descriptor not found\n");
break;
}
DBG("bInterfaceClass: %d, bInterfaceSubClass: %d,\
bInterfaceProtocol: %d, bNumEndpoints: %d\n",
interface->bInterfaceClass, interface->bInterfaceSubClass,
interface->bInterfaceProtocol, interface->bNumEndpoints);
// Sooner bootloader has zero for bInterfaceClass, while adb has USB_CLASS_CDC_DATA
if (interface->bInterfaceClass == ifclass &&
interface->bInterfaceSubClass == ifsubclass &&
interface->bInterfaceProtocol == ifprotocol &&
interface->bNumEndpoints == numendpoints) {
DBG("looking for bulk endpoints\n");
// looks like ADB...
ep1 = (struct usb_endpoint_descriptor *)bufptr;
bufptr += USB_DT_ENDPOINT_SIZE;
ep2 = (struct usb_endpoint_descriptor *)bufptr;
bufptr += USB_DT_ENDPOINT_SIZE;
if (bufptr > devdesc + desclength ||
ep1->bLength != USB_DT_ENDPOINT_SIZE ||
ep1->bDescriptorType != USB_DT_ENDPOINT ||
ep2->bLength != USB_DT_ENDPOINT_SIZE ||
ep2->bDescriptorType != USB_DT_ENDPOINT) {
DBG("endpoints not found\n");
break;
}
// both endpoints should be bulk
if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
DBG("bulk endpoints not found\n");
continue;
}
// we have a match. now we just need to figure out which is in and which is out.
if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
local_ep_in = ep1->bEndpointAddress;
local_ep_out = ep2->bEndpointAddress;
} else {
local_ep_in = ep2->bEndpointAddress;
local_ep_out = ep1->bEndpointAddress;
}
new_device(devname, local_ep_in, local_ep_out, i);
found_device = 1;
close(fd);
} else {
// skip to next interface
bufptr += (interface->bNumEndpoints * USB_DT_ENDPOINT_SIZE);
}
} // end of for
} //end of productid if
}
close(fd);
} // end of if
} // end of devdir while
closedir(devdir);
} //end of busdir while
closedir(busdir);
return found_device;
}
static void find_devices(unsigned vendor, unsigned product1, unsigned product2)
{
// don't scan /proc/bus/usb if we find something in /dev/bus/usb, to avoid duplication of devices.
if (!find_usb_devices("/dev/bus/usb", vendor, product1, product2, USB_CLASS_VENDOR_SPEC, 1, 0, 2)) {
find_usb_devices("/proc/bus/usb", vendor, product1, product2, USB_CLASS_VENDOR_SPEC, 1, 0, 2);
}
}
void usb_open_device(struct usb_handle *h)
{
int n = 0;
h->desc = open(h->fname, O_RDWR);
//DBG("[ usb open %s fd = %d]\n", h->fname, h->desc);
n = ioctl(h->desc, USBDEVFS_CLAIMINTERFACE, &h->interface);
if(n != 0) goto fail;
// t->usb_is_open = 1;
return;
fail:
DBG("[ usb open %s error=%d, err_str = %s]\n",
h->fname, errno, strerror(errno));
if(h->desc >= 0) {
close(h->desc);
h->desc = -1;
}
// t->usb_is_open = 0;
}
int usb_write(struct usb_handle *h, const void *_data, int len)
{
unsigned char *data = (unsigned char*) _data;
struct usbdevfs_bulktransfer bulk;
int n;
while(len >= 0) {
int xfer = (len > 4096) ? 4096 : len;
bulk.ep = h->ep_out;
bulk.len = xfer;
bulk.data = data;
bulk.timeout = 500 + xfer * 8;
bulk.timeout *= 10;
n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
if(n != xfer) {
DBG("ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
if(len == 0)
break;
len -= xfer;
data += xfer;
if(len == 0)
break;
}
return 0;
}
int usb_read(struct usb_handle *h, void *_data, int len)
{
unsigned char *data_start = (unsigned char*) _data;
unsigned char *data = (unsigned char*) _data;
struct usbdevfs_bulktransfer bulk;
int n;
while(len > 0) {
int xfer = (len > 4096) ? 4096 : len;
bulk.ep = h->ep_in;
bulk.len = xfer;
bulk.data = data;
// adjust timeout based on the data we're transferring,
// otherwise the timeout interrupts us partway through
// and we get out of sync...
bulk.timeout = 500 + xfer * 8;
bulk.timeout = 500 + xfer / 128;
// bulk.timeout *= 10;
DBG1("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
DBG1("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname);
if(n < 0) {
if((errno == ETIMEDOUT) && (h->desc != -1)) {
DBG("[ timeout ]\n");
if(n > 0){
data += n;
len -= n;
}
continue;
}
DBG1("ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
len -= n;
data += n;
if(n != xfer)
break;
}
return data - data_start;
}
void usb_kick(struct usb_handle *h)
{
close(h->desc);
h->desc = -1;
}
int usb_close(struct usb_handle *h)
{
close(h->desc);
h->desc = -1;
return 0;
}
void list_devices()
{
int i = 0;
struct usb_handle *h = g_first_usb_device;
while(h) {
printf("%d: %s\n", i, h->fname);
i++;
h = h->next;
}
}
int main(int argc, char **argv)
{
char buffer[4096/*-64*/];
int len;
int c;
char *arg;
int device_index = 0;
struct usb_handle *h;
int i;
find_devices(VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER, PRODUCT_ID_SOONER_COMP);
while(1) {
c = getopt(argc, argv, "d:l");
if (c == EOF)
break;
switch(c) {
case 'd':
device_index = strtol(optarg, NULL, 0);
break;
case 'l':
list_devices();
return 0;
case '?':
fprintf(stderr, "%s: invalid option -%c\n",
argv[0], optopt);
return 1;
}
}
argc -= optind - 1;
argv += optind - 1;
h = g_first_usb_device;
i = device_index;
while(i-- > 0 && h) {
h = h->next;
}
if(h == NULL) {
fprintf(stderr, "no device %d\n", device_index);
return 1;
}
usb_open_device(h);
if(g_first_usb_device->desc < 0) {
fprintf(stderr, "could not open device (%s), %s\n", h->fname, strerror(errno));
return 1;
}
len = 0;
if(argc == 1) {
char *line = NULL;
size_t line_size = 0;
while((len = getline(&line, &line_size, stdin)) >= 0) {
//if(len > 0 && line[len - 1] == '\n')
// len--;
usb_write(h, line, len);
while(1) {
len = usb_read(h, buffer, sizeof(buffer));
if(len < 0)
break;
write(STDOUT_FILENO, buffer, len);
if(len < (int)sizeof(buffer))
break;
}
}
return 0;
}
while(argc > 1) {
argc--;
argv++;
arg = *argv;
while(arg) {
if(*arg)
buffer[len++] = *arg++;
else {
arg = NULL;
if(argc > 1)
buffer[len++] = ' ';
else
break;
}
if(len == sizeof(buffer)) {
usb_write(h, buffer, len);
len = 0;
}
}
}
usb_write(h, buffer, len);
while(1) {
len = usb_read(h, buffer, sizeof(buffer));
if(len < 0)
break;
write(STDOUT_FILENO, buffer, len);
if(len < (int)sizeof(buffer))
break;
}
return 0;
}