Merge "adb: use a custom thread to poll for usb devices on mac."
This commit is contained in:
commit
09c5fcf760
2 changed files with 136 additions and 137 deletions
|
|
@ -613,7 +613,6 @@ static void transport_unref(atransport* t) {
|
||||||
t->ref_count--;
|
t->ref_count--;
|
||||||
if (t->ref_count == 0) {
|
if (t->ref_count == 0) {
|
||||||
D("transport: %s unref (kicking and closing)", t->serial);
|
D("transport: %s unref (kicking and closing)", t->serial);
|
||||||
t->Kick();
|
|
||||||
t->close(t);
|
t->close(t);
|
||||||
remove_transport(t);
|
remove_transport(t);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
272
adb/usb_osx.cpp
272
adb/usb_osx.cpp
|
|
@ -29,86 +29,96 @@
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
#include <android-base/stringprintf.h>
|
#include <android-base/stringprintf.h>
|
||||||
|
|
||||||
#include "adb.h"
|
#include "adb.h"
|
||||||
#include "transport.h"
|
#include "transport.h"
|
||||||
|
|
||||||
#define DBG D
|
|
||||||
|
|
||||||
// There's no strerror equivalent for the errors returned by IOKit.
|
|
||||||
// https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Handling_Errors/AH_Handling_Errors.html
|
|
||||||
// Search the web for "IOReturn.h" to find a complete up to date list.
|
|
||||||
|
|
||||||
static IONotificationPortRef notificationPort = 0;
|
|
||||||
static io_iterator_t notificationIterator;
|
|
||||||
|
|
||||||
struct usb_handle
|
struct usb_handle
|
||||||
{
|
{
|
||||||
UInt8 bulkIn;
|
UInt8 bulkIn;
|
||||||
UInt8 bulkOut;
|
UInt8 bulkOut;
|
||||||
IOUSBInterfaceInterface190** interface;
|
IOUSBInterfaceInterface190** interface;
|
||||||
io_object_t usbNotification;
|
|
||||||
unsigned int zero_mask;
|
unsigned int zero_mask;
|
||||||
|
|
||||||
|
// For garbage collecting disconnected devices.
|
||||||
|
bool mark;
|
||||||
|
std::string devpath;
|
||||||
|
std::atomic<bool> dead;
|
||||||
|
|
||||||
|
usb_handle() : bulkIn(0), bulkOut(0), interface(nullptr),
|
||||||
|
zero_mask(0), mark(false), dead(false) {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static CFRunLoopRef currentRunLoop = 0;
|
static std::atomic<bool> usb_inited_flag;
|
||||||
static pthread_mutex_t start_lock;
|
|
||||||
static pthread_cond_t start_cond;
|
|
||||||
|
|
||||||
|
static auto& g_usb_handles_mutex = *new std::mutex();
|
||||||
|
static auto& g_usb_handles = *new std::vector<std::unique_ptr<usb_handle>>();
|
||||||
|
|
||||||
static void AndroidInterfaceAdded(void *refCon, io_iterator_t iterator);
|
static bool IsKnownDevice(const std::string& devpath) {
|
||||||
static void AndroidInterfaceNotify(void *refCon, io_iterator_t iterator,
|
std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
|
||||||
natural_t messageType,
|
for (auto& usb : g_usb_handles) {
|
||||||
void *messageArgument);
|
if (usb->devpath == devpath) {
|
||||||
static usb_handle* CheckInterface(IOUSBInterfaceInterface190 **iface,
|
// Set mark flag to indicate this device is still alive.
|
||||||
UInt16 vendor, UInt16 product);
|
usb->mark = true;
|
||||||
|
return true;
|
||||||
static int
|
}
|
||||||
InitUSB()
|
|
||||||
{
|
|
||||||
CFMutableDictionaryRef matchingDict;
|
|
||||||
CFRunLoopSourceRef runLoopSource;
|
|
||||||
|
|
||||||
//* To set up asynchronous notifications, create a notification port and
|
|
||||||
//* add its run loop event source to the program's run loop
|
|
||||||
notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
|
|
||||||
runLoopSource = IONotificationPortGetRunLoopSource(notificationPort);
|
|
||||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
|
|
||||||
|
|
||||||
//* Create our matching dictionary to find the Android device's
|
|
||||||
//* adb interface
|
|
||||||
//* IOServiceAddMatchingNotification consumes the reference, so we do
|
|
||||||
//* not need to release this
|
|
||||||
matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
|
|
||||||
|
|
||||||
if (!matchingDict) {
|
|
||||||
LOG(ERROR) << "Couldn't create USB matching dictionary.";
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//* We have to get notifications for all potential candidates and test them
|
static void usb_kick_locked(usb_handle* handle);
|
||||||
//* at connection time because the matching rules don't allow for a
|
|
||||||
//* USB interface class of 0xff for class+subclass+protocol matches
|
|
||||||
//* See https://developer.apple.com/library/mac/qa/qa1076/_index.html
|
|
||||||
IOServiceAddMatchingNotification(
|
|
||||||
notificationPort,
|
|
||||||
kIOFirstMatchNotification,
|
|
||||||
matchingDict,
|
|
||||||
AndroidInterfaceAdded,
|
|
||||||
NULL,
|
|
||||||
¬ificationIterator);
|
|
||||||
|
|
||||||
//* Iterate over set of matching interfaces to access already-present
|
static void KickDisconnectedDevices() {
|
||||||
//* devices and to arm the notification
|
std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
|
||||||
AndroidInterfaceAdded(NULL, notificationIterator);
|
for (auto& usb : g_usb_handles) {
|
||||||
|
if (!usb->mark) {
|
||||||
|
usb_kick_locked(usb.get());
|
||||||
|
} else {
|
||||||
|
usb->mark = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
static void AddDevice(std::unique_ptr<usb_handle> handle) {
|
||||||
|
handle->mark = true;
|
||||||
|
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
|
||||||
|
g_usb_handles.push_back(std::move(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AndroidInterfaceAdded(io_iterator_t iterator);
|
||||||
|
static std::unique_ptr<usb_handle> CheckInterface(IOUSBInterfaceInterface190 **iface,
|
||||||
|
UInt16 vendor, UInt16 product);
|
||||||
|
|
||||||
|
static bool FindUSBDevices() {
|
||||||
|
// Create the matching dictionary to find the Android device's adb interface.
|
||||||
|
CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
|
||||||
|
if (!matchingDict) {
|
||||||
|
LOG(ERROR) << "couldn't create USB matching dictionary";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Create an iterator for all I/O Registry objects that match the dictionary.
|
||||||
|
io_iterator_t iter = 0;
|
||||||
|
kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
|
||||||
|
if (kr != KERN_SUCCESS) {
|
||||||
|
LOG(ERROR) << "failed to get matching services";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Iterate over all matching objects.
|
||||||
|
AndroidInterfaceAdded(iter);
|
||||||
|
IOObjectRelease(iter);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
|
AndroidInterfaceAdded(io_iterator_t iterator)
|
||||||
{
|
{
|
||||||
kern_return_t kr;
|
kern_return_t kr;
|
||||||
io_service_t usbDevice;
|
io_service_t usbDevice;
|
||||||
|
|
@ -124,8 +134,7 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
|
||||||
UInt16 product;
|
UInt16 product;
|
||||||
UInt8 serialIndex;
|
UInt8 serialIndex;
|
||||||
char serial[256];
|
char serial[256];
|
||||||
char devpathBuf[64];
|
std::string devpath;
|
||||||
char *devpath = NULL;
|
|
||||||
|
|
||||||
while ((usbInterface = IOIteratorNext(iterator))) {
|
while ((usbInterface = IOIteratorNext(iterator))) {
|
||||||
//* Create an intermediate interface plugin
|
//* Create an intermediate interface plugin
|
||||||
|
|
@ -199,9 +208,11 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
|
||||||
kr = (*dev)->GetDeviceVendor(dev, &vendor);
|
kr = (*dev)->GetDeviceVendor(dev, &vendor);
|
||||||
kr = (*dev)->GetDeviceProduct(dev, &product);
|
kr = (*dev)->GetDeviceProduct(dev, &product);
|
||||||
kr = (*dev)->GetLocationID(dev, &locationId);
|
kr = (*dev)->GetLocationID(dev, &locationId);
|
||||||
if (kr == 0) {
|
if (kr == KERN_SUCCESS) {
|
||||||
snprintf(devpathBuf, sizeof(devpathBuf), "usb:%" PRIu32 "X", locationId);
|
devpath = android::base::StringPrintf("usb:%" PRIu32 "X", locationId);
|
||||||
devpath = devpathBuf;
|
if (IsKnownDevice(devpath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
|
kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
|
||||||
|
|
||||||
|
|
@ -256,49 +267,29 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
|
||||||
|
|
||||||
(*dev)->Release(dev);
|
(*dev)->Release(dev);
|
||||||
|
|
||||||
LOG(INFO) << android::base::StringPrintf("Found vid=%04x pid=%04x serial=%s\n",
|
VLOG(USB) << android::base::StringPrintf("Found vid=%04x pid=%04x serial=%s\n",
|
||||||
vendor, product, serial);
|
vendor, product, serial);
|
||||||
|
if (devpath.empty()) {
|
||||||
usb_handle* handle = CheckInterface((IOUSBInterfaceInterface190**)iface,
|
devpath = serial;
|
||||||
vendor, product);
|
}
|
||||||
if (handle == NULL) {
|
if (IsKnownDevice(devpath)) {
|
||||||
LOG(ERROR) << "Could not find device interface";
|
(*iface)->USBInterfaceClose(iface);
|
||||||
(*iface)->Release(iface);
|
(*iface)->Release(iface);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(DEBUG) << "AndroidDeviceAdded calling register_usb_transport";
|
std::unique_ptr<usb_handle> handle = CheckInterface((IOUSBInterfaceInterface190**)iface,
|
||||||
register_usb_transport(handle, (serial[0] ? serial : NULL), devpath, 1);
|
vendor, product);
|
||||||
|
if (handle == nullptr) {
|
||||||
// Register for an interest notification of this device being removed.
|
LOG(ERROR) << "Could not find device interface";
|
||||||
// Pass the reference to our private data as the refCon for the
|
(*iface)->Release(iface);
|
||||||
// notification.
|
continue;
|
||||||
kr = IOServiceAddInterestNotification(notificationPort,
|
|
||||||
usbInterface,
|
|
||||||
kIOGeneralInterest,
|
|
||||||
AndroidInterfaceNotify,
|
|
||||||
handle,
|
|
||||||
&handle->usbNotification);
|
|
||||||
|
|
||||||
if (kIOReturnSuccess != kr) {
|
|
||||||
LOG(ERROR) << "Unable to create interest notification (" << std::hex << kr << ")";
|
|
||||||
}
|
}
|
||||||
}
|
handle->devpath = devpath;
|
||||||
}
|
usb_handle* handle_p = handle.get();
|
||||||
|
VLOG(USB) << "Add usb device " << serial;
|
||||||
static void
|
AddDevice(std::move(handle));
|
||||||
AndroidInterfaceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
|
register_usb_transport(handle_p, serial, devpath.c_str(), 1);
|
||||||
{
|
|
||||||
usb_handle *handle = (usb_handle *)refCon;
|
|
||||||
|
|
||||||
if (messageType == kIOMessageServiceIsTerminated) {
|
|
||||||
if (!handle) {
|
|
||||||
LOG(ERROR) << "NULL handle";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LOG(DEBUG) << "AndroidInterfaceNotify";
|
|
||||||
IOObjectRelease(handle->usbNotification);
|
|
||||||
usb_kick(handle);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -316,10 +307,10 @@ static bool ClearPipeStallBothEnds(IOUSBInterfaceInterface190** interface, UInt8
|
||||||
|
|
||||||
//* TODO: simplify this further since we only register to get ADB interface
|
//* TODO: simplify this further since we only register to get ADB interface
|
||||||
//* subclass+protocol events
|
//* subclass+protocol events
|
||||||
static usb_handle*
|
static std::unique_ptr<usb_handle>
|
||||||
CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 product)
|
CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 product)
|
||||||
{
|
{
|
||||||
usb_handle* handle;
|
std::unique_ptr<usb_handle> handle;
|
||||||
IOReturn kr;
|
IOReturn kr;
|
||||||
UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
|
UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
|
||||||
UInt8 endpoint;
|
UInt8 endpoint;
|
||||||
|
|
@ -353,8 +344,10 @@ CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 pro
|
||||||
goto err_bad_adb_interface;
|
goto err_bad_adb_interface;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
|
handle.reset(new usb_handle);
|
||||||
if (handle == nullptr) goto err_bad_adb_interface;
|
if (handle == nullptr) {
|
||||||
|
goto err_bad_adb_interface;
|
||||||
|
}
|
||||||
|
|
||||||
//* Iterate over the endpoints for this interface and find the first
|
//* Iterate over the endpoints for this interface and find the first
|
||||||
//* bulk in/out pipes available. These will be our read/write pipes.
|
//* bulk in/out pipes available. These will be our read/write pipes.
|
||||||
|
|
@ -392,39 +385,37 @@ CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 pro
|
||||||
return handle;
|
return handle;
|
||||||
|
|
||||||
err_get_pipe_props:
|
err_get_pipe_props:
|
||||||
free(handle);
|
|
||||||
err_bad_adb_interface:
|
err_bad_adb_interface:
|
||||||
err_get_interface_class:
|
err_get_interface_class:
|
||||||
err_get_num_ep:
|
err_get_num_ep:
|
||||||
(*interface)->USBInterfaceClose(interface);
|
(*interface)->USBInterfaceClose(interface);
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::mutex& operate_device_lock = *new std::mutex();
|
||||||
|
|
||||||
static void RunLoopThread(void* unused) {
|
static void RunLoopThread(void* unused) {
|
||||||
adb_thread_setname("RunLoop");
|
adb_thread_setname("RunLoop");
|
||||||
InitUSB();
|
|
||||||
|
|
||||||
currentRunLoop = CFRunLoopGetCurrent();
|
VLOG(USB) << "RunLoopThread started";
|
||||||
|
while (true) {
|
||||||
// Signal the parent that we are running
|
{
|
||||||
adb_mutex_lock(&start_lock);
|
std::lock_guard<std::mutex> lock_guard(operate_device_lock);
|
||||||
adb_cond_signal(&start_cond);
|
FindUSBDevices();
|
||||||
adb_mutex_unlock(&start_lock);
|
KickDisconnectedDevices();
|
||||||
|
}
|
||||||
CFRunLoopRun();
|
// Signal the parent that we are running
|
||||||
currentRunLoop = 0;
|
usb_inited_flag = true;
|
||||||
|
adb_sleep_ms(1000);
|
||||||
IOObjectRelease(notificationIterator);
|
}
|
||||||
IONotificationPortDestroy(notificationPort);
|
VLOG(USB) << "RunLoopThread done";
|
||||||
|
|
||||||
LOG(DEBUG) << "RunLoopThread done";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_cleanup() {
|
static void usb_cleanup() {
|
||||||
LOG(DEBUG) << "usb_cleanup";
|
VLOG(USB) << "usb_cleanup";
|
||||||
|
// Wait until usb operations in RunLoopThread finish, and prevent further operations.
|
||||||
|
operate_device_lock.lock();
|
||||||
close_usb_devices();
|
close_usb_devices();
|
||||||
if (currentRunLoop)
|
|
||||||
CFRunLoopStop(currentRunLoop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_init() {
|
void usb_init() {
|
||||||
|
|
@ -432,20 +423,16 @@ void usb_init() {
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
atexit(usb_cleanup);
|
atexit(usb_cleanup);
|
||||||
|
|
||||||
adb_mutex_init(&start_lock, NULL);
|
usb_inited_flag = false;
|
||||||
adb_cond_init(&start_cond, NULL);
|
|
||||||
|
|
||||||
if (!adb_thread_create(RunLoopThread, nullptr)) {
|
if (!adb_thread_create(RunLoopThread, nullptr)) {
|
||||||
fatal_errno("cannot create RunLoop thread");
|
fatal_errno("cannot create RunLoop thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for initialization to finish
|
// Wait for initialization to finish
|
||||||
adb_mutex_lock(&start_lock);
|
while (!usb_inited_flag) {
|
||||||
adb_cond_wait(&start_cond, &start_lock);
|
adb_sleep_ms(100);
|
||||||
adb_mutex_unlock(&start_lock);
|
}
|
||||||
|
|
||||||
adb_mutex_destroy(&start_lock);
|
|
||||||
adb_cond_destroy(&start_cond);
|
|
||||||
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
@ -458,7 +445,7 @@ int usb_write(usb_handle *handle, const void *buf, int len)
|
||||||
if (!len)
|
if (!len)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!handle)
|
if (!handle || handle->dead)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (NULL == handle->interface) {
|
if (NULL == handle->interface) {
|
||||||
|
|
@ -499,7 +486,7 @@ int usb_read(usb_handle *handle, void *buf, int len)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handle) {
|
if (!handle || handle->dead) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -532,20 +519,33 @@ int usb_read(usb_handle *handle, void *buf, int len)
|
||||||
|
|
||||||
int usb_close(usb_handle *handle)
|
int usb_close(usb_handle *handle)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
|
||||||
|
for (auto it = g_usb_handles.begin(); it != g_usb_handles.end(); ++it) {
|
||||||
|
if ((*it).get() == handle) {
|
||||||
|
g_usb_handles.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_kick(usb_handle *handle)
|
static void usb_kick_locked(usb_handle *handle)
|
||||||
{
|
{
|
||||||
LOG(INFO) << "Kicking handle";
|
LOG(INFO) << "Kicking handle";
|
||||||
/* release the interface */
|
/* release the interface */
|
||||||
if (!handle)
|
if (!handle)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (handle->interface)
|
if (!handle->dead)
|
||||||
{
|
{
|
||||||
|
handle->dead = true;
|
||||||
(*handle->interface)->USBInterfaceClose(handle->interface);
|
(*handle->interface)->USBInterfaceClose(handle->interface);
|
||||||
(*handle->interface)->Release(handle->interface);
|
(*handle->interface)->Release(handle->interface);
|
||||||
handle->interface = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void usb_kick(usb_handle *handle) {
|
||||||
|
// Use the lock to avoid multiple thread kicking the device at the same time.
|
||||||
|
std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
|
||||||
|
usb_kick_locked(handle);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue