Merge changes I8e8e0963,I3c714f63,Id157412e,Ib7c26fbd

* changes:
  adb: libusb: recognize devices with multiple interfaces.
  adb: libusb: wait for devices to become accessible.
  adb: libusb: switch to hotplug for device detection.
  adb: silence noise.
This commit is contained in:
Josh Gao 2017-05-15 20:45:04 +00:00 committed by Gerrit Code Review
commit 55ddb99527
2 changed files with 78 additions and 43 deletions

View file

@ -1241,10 +1241,7 @@ void update_transport_status() {
return true; return true;
}); });
D("update_transport_status: transports_ready = %s", result ? "true" : "false");
bool ready; bool ready;
{ {
std::lock_guard<std::mutex> lock(init_mutex); std::lock_guard<std::mutex> lock(init_mutex);
transports_ready = result; transports_ready = result;
@ -1252,14 +1249,11 @@ void update_transport_status() {
} }
if (ready) { if (ready) {
D("update_transport_status: notifying");
init_cv.notify_all(); init_cv.notify_all();
} }
} }
void adb_notify_device_scan_complete() { void adb_notify_device_scan_complete() {
D("device scan complete");
{ {
std::lock_guard<std::mutex> lock(init_mutex); std::lock_guard<std::mutex> lock(init_mutex);
device_scan_complete = true; device_scan_complete = true;

View file

@ -151,10 +151,7 @@ struct usb_handle : public ::usb_handle {
static auto& usb_handles = *new std::unordered_map<std::string, std::unique_ptr<usb_handle>>(); static auto& usb_handles = *new std::unordered_map<std::string, std::unique_ptr<usb_handle>>();
static auto& usb_handles_mutex = *new std::mutex(); static auto& usb_handles_mutex = *new std::mutex();
static std::thread* device_poll_thread = nullptr; static libusb_hotplug_callback_handle hotplug_handle;
static bool terminate_device_poll_thread = false;
static auto& device_poll_mutex = *new std::mutex();
static auto& device_poll_cv = *new std::condition_variable();
static std::string get_device_address(libusb_device* device) { static std::string get_device_address(libusb_device* device) {
return StringPrintf("usb:%d:%d", libusb_get_bus_number(device), return StringPrintf("usb:%d:%d", libusb_get_bus_number(device),
@ -175,6 +172,17 @@ static std::string get_device_serial_path(libusb_device* device) {
path += "/serial"; path += "/serial";
return path; return path;
} }
static std::string get_device_dev_path(libusb_device* device) {
uint8_t ports[7];
int port_count = libusb_get_port_numbers(device, ports, 7);
if (port_count < 0) return "";
return StringPrintf("/dev/bus/usb/%03u/%03u", libusb_get_bus_number(device), ports[0]);
}
static bool is_device_accessible(libusb_device* device) {
return access(get_device_dev_path(device).c_str(), R_OK | W_OK) == 0;
}
#endif #endif
static bool endpoint_is_output(uint8_t endpoint) { static bool endpoint_is_output(uint8_t endpoint) {
@ -229,7 +237,7 @@ static void process_device(libusb_device* device) {
// TODO: Is this assumption valid? // TODO: Is this assumption valid?
LOG(VERBOSE) << "skipping interface with incorrect num_altsetting at " << device_address LOG(VERBOSE) << "skipping interface with incorrect num_altsetting at " << device_address
<< " (interface " << interface_num << ")"; << " (interface " << interface_num << ")";
return; continue;
} }
const libusb_interface_descriptor& interface_desc = interface.altsetting[0]; const libusb_interface_descriptor& interface_desc = interface.altsetting[0];
@ -237,7 +245,7 @@ static void process_device(libusb_device* device) {
interface_desc.bInterfaceProtocol)) { interface_desc.bInterfaceProtocol)) {
LOG(VERBOSE) << "skipping non-adb interface at " << device_address << " (interface " LOG(VERBOSE) << "skipping non-adb interface at " << device_address << " (interface "
<< interface_num << ")"; << interface_num << ")";
return; continue;
} }
LOG(VERBOSE) << "found potential adb interface at " << device_address << " (interface " LOG(VERBOSE) << "found potential adb interface at " << device_address << " (interface "
@ -253,7 +261,7 @@ static void process_device(libusb_device* device) {
const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK; const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK;
if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) { if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) {
return; continue;
} }
if (endpoint_is_output(endpoint_addr) && !found_out) { if (endpoint_is_output(endpoint_addr) && !found_out) {
@ -371,33 +379,62 @@ static void process_device(libusb_device* device) {
} }
register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), writable); register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), writable);
LOG(INFO) << "registered new usb device '" << device_serial << "'"; LOG(INFO) << "registered new usb device '" << device_serial << "'";
} }
static void poll_for_devices() { static std::atomic<int> connecting_devices(0);
libusb_device** list;
adb_thread_setname("device poll");
while (true) {
const ssize_t device_count = libusb_get_device_list(nullptr, &list);
LOG(VERBOSE) << "found " << device_count << " attached devices"; static void device_connected(libusb_device* device) {
#if defined(__linux__)
for (ssize_t i = 0; i < device_count; ++i) { // Android's host linux libusb uses netlink instead of udev for device hotplug notification,
process_device(list[i]); // which means we can get hotplug notifications before udev has updated ownership/perms on the
// device. Since we're not going to be able to link against the system's libudev any time soon,
// hack around this by checking for accessibility in a loop.
++connecting_devices;
auto thread = std::thread([device]() {
std::string device_path = get_device_dev_path(device);
auto start = std::chrono::steady_clock::now();
while (std::chrono::steady_clock::now() - start < 500ms) {
if (is_device_accessible(device)) {
break;
}
std::this_thread::sleep_for(10ms);
} }
libusb_free_device_list(list, 1); process_device(device);
--connecting_devices;
});
thread.detach();
#else
process_device(device);
#endif
}
adb_notify_device_scan_complete(); static void device_disconnected(libusb_device* device) {
std::string device_address = get_device_address(device);
std::unique_lock<std::mutex> lock(device_poll_mutex); LOG(INFO) << "device disconnected: " << device_address;
if (device_poll_cv.wait_for(lock, 500ms, []() { return terminate_device_poll_thread; })) { std::unique_lock<std::mutex> lock(usb_handles_mutex);
return; auto it = usb_handles.find(device_address);
if (it != usb_handles.end()) {
if (!it->second->device_handle) {
// If the handle is null, we were never able to open the device.
unregister_usb_transport(it->second.get());
} }
usb_handles.erase(it);
} }
} }
static int hotplug_callback(libusb_context*, libusb_device* device, libusb_hotplug_event event,
void*) {
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
device_connected(device);
} else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
device_disconnected(device);
}
return 0;
}
void usb_init() { void usb_init() {
LOG(DEBUG) << "initializing libusb..."; LOG(DEBUG) << "initializing libusb...";
int rc = libusb_init(nullptr); int rc = libusb_init(nullptr);
@ -405,6 +442,24 @@ void usb_init() {
LOG(FATAL) << "failed to initialize libusb: " << libusb_error_name(rc); LOG(FATAL) << "failed to initialize libusb: " << libusb_error_name(rc);
} }
// Register the hotplug callback.
rc = libusb_hotplug_register_callback(
nullptr, static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
LIBUSB_CLASS_PER_INTERFACE, hotplug_callback, nullptr, &hotplug_handle);
if (rc != LIBUSB_SUCCESS) {
LOG(FATAL) << "failed to register libusb hotplug callback";
}
// Wait for all of the connecting devices to finish.
while (connecting_devices != 0) {
std::this_thread::sleep_for(10ms);
}
adb_notify_device_scan_complete();
// Spawn a thread for libusb_handle_events. // Spawn a thread for libusb_handle_events.
std::thread([]() { std::thread([]() {
adb_thread_setname("libusb"); adb_thread_setname("libusb");
@ -412,24 +467,10 @@ void usb_init() {
libusb_handle_events(nullptr); libusb_handle_events(nullptr);
} }
}).detach(); }).detach();
// Spawn a thread to do device enumeration.
// TODO: Use libusb_hotplug_* instead?
std::unique_lock<std::mutex> lock(device_poll_mutex);
device_poll_thread = new std::thread(poll_for_devices);
} }
void usb_cleanup() { void usb_cleanup() {
{ libusb_hotplug_deregister_callback(nullptr, hotplug_handle);
std::unique_lock<std::mutex> lock(device_poll_mutex);
terminate_device_poll_thread = true;
if (!device_poll_thread) {
return;
}
}
device_poll_cv.notify_all();
device_poll_thread->join();
} }
// Dispatch a libusb transfer, unlock |device_lock|, and then wait for the result. // Dispatch a libusb transfer, unlock |device_lock|, and then wait for the result.