From 0927bf9690127bc45cf8837a1467759e9720399a Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sat, 8 Aug 2009 12:37:44 -0400 Subject: adb: On Linux, detect USB devices for which adb does not have permissions to communicate with. adb devices will now list devices without adequate file system permissions in /dev/bus/usb as: List of devices attached ???????????? no permissions Signed-off-by: Mike Lockwood --- adb/adb.h | 11 +-- adb/transport.c | 82 ++++++++++++++-------- adb/transport_usb.c | 4 +- adb/usb_linux.c | 181 ++++++++++++++++++++++++++----------------------- adb/usb_linux_client.c | 2 +- 5 files changed, 159 insertions(+), 121 deletions(-) (limited to 'adb') diff --git a/adb/adb.h b/adb/adb.h index 8d57bf2..4789edf 100644 --- a/adb/adb.h +++ b/adb/adb.h @@ -33,7 +33,7 @@ #define ADB_VERSION_MAJOR 1 // Used for help/version information #define ADB_VERSION_MINOR 0 // Used for help/version information -#define ADB_SERVER_VERSION 22 // Increment this when we want to force users to start a new adb server +#define ADB_SERVER_VERSION 23 // Increment this when we want to force users to start a new adb server typedef struct amessage amessage; typedef struct apacket apacket; @@ -263,14 +263,17 @@ void kick_transport( atransport* t ); /* initialize a transport object's func pointers and state */ int init_socket_transport(atransport *t, int s, int port); -void init_usb_transport(atransport *t, usb_handle *usb); +void init_usb_transport(atransport *t, usb_handle *usb, int state); /* for MacOS X cleanup */ void close_usb_devices(); /* cause new transports to be init'd and added to the list */ void register_socket_transport(int s, const char *serial, int port); -void register_usb_transport(usb_handle *h, const char *serial); +void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable); + +/* this should only be used for transports with connection_state == CS_NOPERM */ +void unregister_usb_transport(usb_handle *usb); int service_to_fd(const char *name); #if ADB_HOST @@ -384,7 +387,7 @@ int connection_state(atransport *t); #define CS_DEVICE 2 #define CS_HOST 3 #define CS_RECOVERY 4 -#define CS_ERROR 5 +#define CS_NOPERM 5 /* Insufficient permissions to communicate with the device */ extern int HOST; diff --git a/adb/transport.c b/adb/transport.c index c76f1a5..831b6fc 100644 --- a/adb/transport.c +++ b/adb/transport.c @@ -584,17 +584,36 @@ static void transport_registration_func(int _fd, unsigned ev, void *data) return; } + /* don't create transport threads for inaccessible devices */ + if (t->connection_state != CS_NOPERM) { /* initial references are the two threads */ - t->ref_count = 2; + t->ref_count = 2; - if(adb_socketpair(s)) { - fatal_errno("cannot open transport socketpair"); - } + if(adb_socketpair(s)) { + fatal_errno("cannot open transport socketpair"); + } + + D("transport: %p (%d,%d) starting\n", t, s[0], s[1]); + + t->transport_socket = s[0]; + t->fd = s[1]; - D("transport: %p (%d,%d) starting\n", t, s[0], s[1]); + D("transport: %p install %d\n", t, t->transport_socket ); + fdevent_install(&(t->transport_fde), + t->transport_socket, + transport_socket_events, + t); + + fdevent_set(&(t->transport_fde), FDE_READ); + + if(adb_thread_create(&input_thread_ptr, input_thread, t)){ + fatal_errno("cannot create input thread"); + } - t->transport_socket = s[0]; - t->fd = s[1]; + if(adb_thread_create(&output_thread_ptr, output_thread, t)){ + fatal_errno("cannot create output thread"); + } + } /* put us on the master device list */ adb_mutex_lock(&transport_lock); @@ -604,22 +623,6 @@ static void transport_registration_func(int _fd, unsigned ev, void *data) t->prev->next = t; adb_mutex_unlock(&transport_lock); - D("transport: %p install %d\n", t, t->transport_socket ); - fdevent_install(&(t->transport_fde), - t->transport_socket, - transport_socket_events, - t); - - fdevent_set(&(t->transport_fde), FDE_READ); - - if(adb_thread_create(&input_thread_ptr, input_thread, t)){ - fatal_errno("cannot create input thread"); - } - - if(adb_thread_create(&output_thread_ptr, output_thread, t)){ - fatal_errno("cannot create output thread"); - } - t->disconnects.next = t->disconnects.prev = &t->disconnects; update_transports(); @@ -717,6 +720,8 @@ retry: adb_mutex_lock(&transport_lock); for (t = transport_list.next; t != &transport_list; t = t->next) { + if (t->connection_state == CS_NOPERM) continue; + /* check for matching serial number */ if (serial) { if (t->serial && !strcmp(serial, t->serial)) { @@ -763,6 +768,11 @@ retry: *error_out = "device offline"; result = NULL; } + if (result && result->connection_state == CS_NOPERM) { + if (error_out) + *error_out = "no permissions for device"; + result = NULL; + } /* check for required connection state */ if (result && state != CS_ANY && result->connection_state != state) { @@ -793,6 +803,7 @@ static const char *statename(atransport *t) case CS_DEVICE: return "device"; case CS_HOST: return "host"; case CS_RECOVERY: return "recovery"; + case CS_NOPERM: return "no permissions"; default: return "unknown"; } } @@ -807,9 +818,10 @@ int list_transports(char *buf, size_t bufsize) /* XXX OVERRUN PROBLEMS XXX */ adb_mutex_lock(&transport_lock); for(t = transport_list.next; t != &transport_list; t = t->next) { - len = snprintf(p, end - p, "%s\t%s\n", - t->serial ? t->serial : "", - statename(t)); + const char* serial = t->serial; + if (!serial || !serial[0]) + serial = "????????????"; + len = snprintf(p, end - p, "%s\t%s\n", serial, statename(t)); if (p + len >= end) { /* discard last line if buffer is too short */ @@ -854,18 +866,32 @@ void register_socket_transport(int s, const char *serial, int port) register_transport(t); } -void register_usb_transport(usb_handle *usb, const char *serial) +void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable) { atransport *t = calloc(1, sizeof(atransport)); D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb, serial ? serial : ""); - init_usb_transport(t, usb); + init_usb_transport(t, usb, (writeable ? CS_OFFLINE : CS_NOPERM)); if(serial) { t->serial = strdup(serial); } register_transport(t); } +/* this should only be used for transports with connection_state == CS_NOPERM */ +void unregister_usb_transport(usb_handle *usb) +{ + atransport *t; + adb_mutex_lock(&transport_lock); + for(t = transport_list.next; t != &transport_list; t = t->next) { + if (t->usb == usb && t->connection_state == CS_NOPERM) { + t->next->prev = t->prev; + t->prev->next = t->next; + break; + } + } + adb_mutex_unlock(&transport_lock); +} #undef TRACE_TAG #define TRACE_TAG TRACE_RWX diff --git a/adb/transport_usb.c b/adb/transport_usb.c index 3737c5c..2584163 100644 --- a/adb/transport_usb.c +++ b/adb/transport_usb.c @@ -110,7 +110,7 @@ static void remote_kick(atransport *t) usb_kick(t->usb); } -void init_usb_transport(atransport *t, usb_handle *h) +void init_usb_transport(atransport *t, usb_handle *h, int state) { D("transport: usb\n"); t->close = remote_close; @@ -118,7 +118,7 @@ void init_usb_transport(atransport *t, usb_handle *h) t->read_from_remote = remote_read; t->write_to_remote = remote_write; t->sync_token = 1; - t->connection_state = CS_OFFLINE; + t->connection_state = state; t->type = kTransportUsb; t->usb = h; diff --git a/adb/usb_linux.c b/adb/usb_linux.c index 537122d..cf78d80 100644 --- a/adb/usb_linux.c +++ b/adb/usb_linux.c @@ -57,6 +57,7 @@ struct usb_handle unsigned char ep_out; unsigned zero_mask; + unsigned writeable; struct usbdevfs_urb urb_in; struct usbdevfs_urb urb_out; @@ -115,7 +116,7 @@ static void kick_disconnected_devices() } static void register_device(const char *dev_name, unsigned char ep_in, unsigned char ep_out, - int ifc, const char *serial, unsigned zero_mask); + int ifc, int serial_index, unsigned zero_mask); static inline int badname(const char *name) { @@ -125,19 +126,18 @@ static inline int badname(const char *name) return 0; } -static int find_usb_device(const char *base, - void (*register_device_callback) (const char *, unsigned char, unsigned char, int, const char *, unsigned)) +static void find_usb_device(const char *base, + void (*register_device_callback) + (const char *, unsigned char, unsigned char, int, int, unsigned)) { char busname[32], devname[32]; unsigned char local_ep_in, local_ep_out; DIR *busdir , *devdir ; struct dirent *de; int fd ; - int found_device = 0; - char serial[256]; busdir = opendir(base); - if(busdir == 0) return 0; + if(busdir == 0) return; while((de = readdir(busdir)) != 0) { if(badname(de->d_name)) continue; @@ -168,7 +168,7 @@ static int find_usb_device(const char *base, } // DBGX("[ scanning %s ]\n", devname); - if((fd = unix_open(devname, O_RDWR)) < 0) { + if((fd = unix_open(devname, O_RDONLY)) < 0) { continue; } @@ -263,59 +263,9 @@ static int find_usb_device(const char *base, local_ep_out = ep1->bEndpointAddress; } - // read the device's serial number - serial[0] = 0; - memset(serial, 0, sizeof(serial)); - if (device->iSerialNumber) { - struct usbdevfs_ctrltransfer ctrl; - __u16 buffer[128]; - __u16 languages[128]; - int i, result; - int languageCount = 0; - - memset(languages, 0, sizeof(languages)); - memset(&ctrl, 0, sizeof(ctrl)); - - // read list of supported languages - ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; - ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; - ctrl.wValue = (USB_DT_STRING << 8) | 0; - ctrl.wIndex = 0; - ctrl.wLength = sizeof(languages); - ctrl.data = languages; - - result = ioctl(fd, USBDEVFS_CONTROL, &ctrl); - if (result > 0) - languageCount = (result - 2) / 2; - - for (i = 1; i <= languageCount; i++) { - memset(buffer, 0, sizeof(buffer)); - memset(&ctrl, 0, sizeof(ctrl)); - - ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; - ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; - ctrl.wValue = (USB_DT_STRING << 8) | device->iSerialNumber; - ctrl.wIndex = languages[i]; - ctrl.wLength = sizeof(buffer); - ctrl.data = buffer; - - result = ioctl(fd, USBDEVFS_CONTROL, &ctrl); - if (result > 0) { - int i; - // skip first word, and copy the rest to the serial string, changing shorts to bytes. - result /= 2; - for (i = 1; i < result; i++) - serial[i - 1] = buffer[i]; - serial[i - 1] = 0; - break; - } - } - } - register_device_callback(devname, local_ep_in, local_ep_out, - interface->bInterfaceNumber, serial, zero_mask); + interface->bInterfaceNumber, device->iSerialNumber, zero_mask); - found_device = 1; break; } else { // seek next interface descriptor @@ -332,8 +282,6 @@ static int find_usb_device(const char *base, closedir(devdir); } //end of busdir while closedir(busdir); - - return found_device; } void usb_cleanup() @@ -537,26 +485,30 @@ void usb_kick(usb_handle *h) if(h->dead == 0) { h->dead = 1; - /* HACK ALERT! - ** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB). - ** This is a workaround for that problem. - */ - if (h->reaper_thread) { - pthread_kill(h->reaper_thread, SIGALRM); - } + if (h->writeable) { + /* HACK ALERT! + ** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB). + ** This is a workaround for that problem. + */ + if (h->reaper_thread) { + pthread_kill(h->reaper_thread, SIGALRM); + } - /* cancel any pending transactions - ** these will quietly fail if the txns are not active, - ** but this ensures that a reader blocked on REAPURB - ** will get unblocked - */ - ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_in); - ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_out); - h->urb_in.status = -ENODEV; - h->urb_out.status = -ENODEV; - h->urb_in_busy = 0; - h->urb_out_busy = 0; - adb_cond_broadcast(&h->notify); + /* cancel any pending transactions + ** these will quietly fail if the txns are not active, + ** but this ensures that a reader blocked on REAPURB + ** will get unblocked + */ + ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_in); + ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_out); + h->urb_in.status = -ENODEV; + h->urb_out.status = -ENODEV; + h->urb_in_busy = 0; + h->urb_out_busy = 0; + adb_cond_broadcast(&h->notify); + } else { + unregister_usb_transport(h); + } } adb_mutex_unlock(&h->lock); } @@ -580,11 +532,11 @@ int usb_close(usb_handle *h) static void register_device(const char *dev_name, unsigned char ep_in, unsigned char ep_out, - int interface, - const char *serial, unsigned zero_mask) + int interface, int serial_index, unsigned zero_mask) { usb_handle* usb = 0; int n = 0; + char serial[256]; /* Since Linux will not reassign the device ID (and dev_name) ** as long as the device is open, we can add to the list here @@ -610,6 +562,7 @@ static void register_device(const char *dev_name, usb->ep_in = ep_in; usb->ep_out = ep_out; usb->zero_mask = zero_mask; + usb->writeable = 1; adb_cond_init(&usb->notify, 0); adb_mutex_init(&usb->lock, 0); @@ -618,10 +571,66 @@ static void register_device(const char *dev_name, usb->reaper_thread = 0; usb->desc = unix_open(usb->fname, O_RDWR); - if(usb->desc < 0) goto fail; - D("[ usb open %s fd = %d]\n", usb->fname, usb->desc); - n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface); - if(n != 0) goto fail; + if(usb->desc < 0) { + /* if we fail, see if have read-only access */ + usb->desc = unix_open(usb->fname, O_RDONLY); + if(usb->desc < 0) goto fail; + usb->writeable = 0; + D("[ usb open read-only %s fd = %d]\n", usb->fname, usb->desc); + } else { + D("[ usb open %s fd = %d]\n", usb->fname, usb->desc); + n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface); + if(n != 0) goto fail; + } + + /* read the device's serial number */ + serial[0] = 0; + memset(serial, 0, sizeof(serial)); + if (serial_index) { + struct usbdevfs_ctrltransfer ctrl; + __u16 buffer[128]; + __u16 languages[128]; + int i, result; + int languageCount = 0; + + memset(languages, 0, sizeof(languages)); + memset(&ctrl, 0, sizeof(ctrl)); + + // read list of supported languages + ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; + ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; + ctrl.wValue = (USB_DT_STRING << 8) | 0; + ctrl.wIndex = 0; + ctrl.wLength = sizeof(languages); + ctrl.data = languages; + + result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl); + if (result > 0) + languageCount = (result - 2) / 2; + + for (i = 1; i <= languageCount; i++) { + memset(buffer, 0, sizeof(buffer)); + memset(&ctrl, 0, sizeof(ctrl)); + + ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; + ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; + ctrl.wValue = (USB_DT_STRING << 8) | serial_index; + ctrl.wIndex = languages[i]; + ctrl.wLength = sizeof(buffer); + ctrl.data = buffer; + + result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl); + if (result > 0) { + int i; + // skip first word, and copy the rest to the serial string, changing shorts to bytes. + result /= 2; + for (i = 1; i < result; i++) + serial[i - 1] = buffer[i]; + serial[i - 1] = 0; + break; + } + } + } /* add to the end of the active handles */ adb_mutex_lock(&usb_lock); @@ -631,7 +640,7 @@ static void register_device(const char *dev_name, usb->next->prev = usb; adb_mutex_unlock(&usb_lock); - register_usb_transport(usb, serial); + register_usb_transport(usb, serial, usb->writeable); return; fail: diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c index 530bd04..0a21c6f 100644 --- a/adb/usb_linux_client.c +++ b/adb/usb_linux_client.c @@ -72,7 +72,7 @@ static void *usb_open_thread(void *x) usb->fd = fd; D("[ usb_thread - registering device ]\n"); - register_usb_transport(usb, 0); + register_usb_transport(usb, 0, 1); } // never gets here -- cgit v1.1