/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) #include #else #include #endif #include #include #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; }