From acf78d462f556bef83dfe9a36db8201e552711a8 Mon Sep 17 00:00:00 2001 From: Aaron Wisner Date: Thu, 26 Jul 2018 10:56:09 -0500 Subject: [PATCH] Add support for fastboot transport timeouts and USB Reset() on linux USB Reset() allows simulating unplugging and replugging device. Test: build and run fastboot on mac 10.13.3 Test: glinux build and run fastboot Change-Id: Id924d063e549a4cca9dda03afd8f8fe266f6d2ab --- fastboot/usb.h | 10 ++++++++- fastboot/usb_linux.cpp | 30 ++++++++++++++++++-------- fastboot/usb_osx.cpp | 46 ++++++++++++++++++++++++++++++++++------ fastboot/usb_windows.cpp | 12 ++++++++--- 4 files changed, 78 insertions(+), 20 deletions(-) diff --git a/fastboot/usb.h b/fastboot/usb.h index 5b44468bb..7ca44c414 100644 --- a/fastboot/usb.h +++ b/fastboot/usb.h @@ -52,6 +52,14 @@ struct usb_ifc_info { char device_path[256]; }; +class UsbTransport : public Transport { + // Resets the underlying transport. Returns 0 on success. + // This effectively simulates unplugging and replugging + public: + virtual int Reset() = 0; +}; + typedef int (*ifc_match_func)(usb_ifc_info *ifc); -Transport* usb_open(ifc_match_func callback); +// 0 is non blocking +UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms = 0); diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp index 386dd300c..9b779ddea 100644 --- a/fastboot/usb_linux.cpp +++ b/fastboot/usb_linux.cpp @@ -52,7 +52,7 @@ using namespace std::chrono_literals; -#define MAX_RETRIES 5 +#define MAX_RETRIES 2 /* Timeout in seconds for usb_wait_for_disconnect. * It doesn't usually take long for a device to disconnect (almost always @@ -91,18 +91,21 @@ struct usb_handle unsigned char ep_out; }; -class LinuxUsbTransport : public Transport { +class LinuxUsbTransport : public UsbTransport { public: - explicit LinuxUsbTransport(std::unique_ptr handle) : handle_(std::move(handle)) {} + explicit LinuxUsbTransport(std::unique_ptr handle, uint32_t ms_timeout = 0) + : handle_(std::move(handle)), ms_timeout_(ms_timeout) {} ~LinuxUsbTransport() override = default; ssize_t Read(void* data, size_t len) override; ssize_t Write(const void* data, size_t len) override; int Close() override; + int Reset() override; int WaitForDisconnect() override; private: std::unique_ptr handle_; + const uint32_t ms_timeout_; DISALLOW_COPY_AND_ASSIGN(LinuxUsbTransport); }; @@ -402,7 +405,7 @@ ssize_t LinuxUsbTransport::Write(const void* _data, size_t len) bulk.ep = handle_->ep_out; bulk.len = xfer; bulk.data = data; - bulk.timeout = 0; + bulk.timeout = ms_timeout_; n = ioctl(handle_->desc, USBDEVFS_BULK, &bulk); if(n != xfer) { @@ -436,7 +439,7 @@ ssize_t LinuxUsbTransport::Read(void* _data, size_t len) bulk.ep = handle_->ep_in; bulk.len = xfer; bulk.data = data; - bulk.timeout = 0; + bulk.timeout = ms_timeout_; retry = 0; do { @@ -447,7 +450,7 @@ ssize_t LinuxUsbTransport::Read(void* _data, size_t len) if (n < 0) { DBG1("ERROR: n = %d, errno = %d (%s)\n",n, errno, strerror(errno)); if (++retry > MAX_RETRIES) return -1; - std::this_thread::sleep_for(1s); + std::this_thread::sleep_for(100ms); } } while (n < 0); @@ -477,10 +480,19 @@ int LinuxUsbTransport::Close() return 0; } -Transport* usb_open(ifc_match_func callback) -{ +int LinuxUsbTransport::Reset() { + int ret = 0; + // We reset the USB connection + if ((ret = ioctl(handle_->desc, USBDEVFS_RESET, 0))) { + return ret; + } + + return 0; +} + +UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) { std::unique_ptr handle = find_usb_device("/sys/bus/usb/devices", callback); - return handle ? new LinuxUsbTransport(std::move(handle)) : nullptr; + return handle ? new LinuxUsbTransport(std::move(handle), timeout_ms) : nullptr; } /* Wait for the system to notice the device is gone, so that a subsequent diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp index e95b04936..4d48f6e26 100644 --- a/fastboot/usb_osx.cpp +++ b/fastboot/usb_osx.cpp @@ -65,17 +65,21 @@ struct usb_handle unsigned int zero_mask; }; -class OsxUsbTransport : public Transport { +class OsxUsbTransport : public UsbTransport { public: - OsxUsbTransport(std::unique_ptr handle) : handle_(std::move(handle)) {} + // A timeout of 0 is blocking + OsxUsbTransport(std::unique_ptr handle, uint32_t ms_timeout = 0) + : handle_(std::move(handle)), ms_timeout_(ms_timeout) {} ~OsxUsbTransport() override = default; ssize_t Read(void* data, size_t len) override; ssize_t Write(const void* data, size_t len) override; int Close() override; + int Reset() override; private: std::unique_ptr handle_; + const uint32_t ms_timeout_; DISALLOW_COPY_AND_ASSIGN(OsxUsbTransport); }; @@ -456,7 +460,7 @@ static int init_usb(ifc_match_func callback, std::unique_ptr* handle * Definitions of this file's public functions. */ -Transport* usb_open(ifc_match_func callback) { +UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) { std::unique_ptr handle; if (init_usb(callback, &handle) < 0) { @@ -464,7 +468,7 @@ Transport* usb_open(ifc_match_func callback) { return nullptr; } - return new OsxUsbTransport(std::move(handle)); + return new OsxUsbTransport(std::move(handle), timeout_ms); } int OsxUsbTransport::Close() { @@ -472,6 +476,19 @@ int OsxUsbTransport::Close() { return 0; } +/* + TODO: this SHOULD be easy to do with ResetDevice() from IOUSBDeviceInterface. + However to perform operations that manipulate the state of the device, you must + claim ownership of the device with USBDeviceOpenSeize(). However, this operation + always fails with kIOReturnExclusiveAccess. + It seems that the kext com.apple.driver.usb.AppleUSBHostCompositeDevice + always loads and claims ownership of the device and refuses to give it up. +*/ +int OsxUsbTransport::Reset() { + ERR("USB reset is currently unsupported on osx\n"); + return -1; +} + ssize_t OsxUsbTransport::Read(void* data, size_t len) { IOReturn result; UInt32 numBytes = len; @@ -494,7 +511,14 @@ ssize_t OsxUsbTransport::Read(void* data, size_t len) { return -1; } - result = (*handle_->interface)->ReadPipe(handle_->interface, handle_->bulkIn, data, &numBytes); + if (!ms_timeout_) { + result = (*handle_->interface) + ->ReadPipe(handle_->interface, handle_->bulkIn, data, &numBytes); + } else { + result = (*handle_->interface) + ->ReadPipeTO(handle_->interface, handle_->bulkIn, data, &numBytes, + ms_timeout_, ms_timeout_); + } if (result == 0) { return (int) numBytes; @@ -541,8 +565,16 @@ ssize_t OsxUsbTransport::Write(const void* data, size_t len) { int lenToSend = lenRemaining > maxLenToSend ? maxLenToSend : lenRemaining; - result = (*handle_->interface)->WritePipe( - handle_->interface, handle_->bulkOut, (void *)data, lenToSend); + if (!ms_timeout_) { // blocking + result = (*handle_->interface) + ->WritePipe(handle_->interface, handle_->bulkOut, (void*)data, + lenToSend); + } else { + result = (*handle_->interface) + ->WritePipeTO(handle_->interface, handle_->bulkOut, (void*)data, + lenToSend, ms_timeout_, ms_timeout_); + } + if (result != 0) break; lenRemaining -= lenToSend; diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp index 0e5fba125..8c60a7173 100644 --- a/fastboot/usb_windows.cpp +++ b/fastboot/usb_windows.cpp @@ -66,7 +66,7 @@ struct usb_handle { std::string interface_name; }; -class WindowsUsbTransport : public Transport { +class WindowsUsbTransport : public UsbTransport { public: WindowsUsbTransport(std::unique_ptr handle) : handle_(std::move(handle)) {} ~WindowsUsbTransport() override = default; @@ -74,6 +74,7 @@ class WindowsUsbTransport : public Transport { ssize_t Read(void* data, size_t len) override; ssize_t Write(const void* data, size_t len) override; int Close() override; + int Reset() override; private: std::unique_ptr handle_; @@ -261,6 +262,12 @@ int WindowsUsbTransport::Close() { return 0; } +int WindowsUsbTransport::Reset() { + DBG("usb_reset currently unsupported\n\n"); + // TODO, this is a bit complicated since it is using ADB + return -1; +} + int recognized_device(usb_handle* handle, ifc_match_func callback) { struct usb_ifc_info info; USB_DEVICE_DESCRIPTOR device_desc; @@ -366,8 +373,7 @@ static std::unique_ptr find_usb_device(ifc_match_func callback) { return handle; } -Transport* usb_open(ifc_match_func callback) -{ +UsbTransport* usb_open(ifc_match_func callback, uint32_t) { std::unique_ptr handle = find_usb_device(callback); return handle ? new WindowsUsbTransport(std::move(handle)) : nullptr; }