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
This commit is contained in:
Aaron Wisner 2018-07-26 10:56:09 -05:00
parent 28fb130cbb
commit acf78d462f
4 changed files with 78 additions and 20 deletions

View file

@ -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);

View file

@ -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<usb_handle> handle) : handle_(std::move(handle)) {}
explicit LinuxUsbTransport(std::unique_ptr<usb_handle> 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<usb_handle> 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<usb_handle> 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

View file

@ -65,17 +65,21 @@ struct usb_handle
unsigned int zero_mask;
};
class OsxUsbTransport : public Transport {
class OsxUsbTransport : public UsbTransport {
public:
OsxUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
// A timeout of 0 is blocking
OsxUsbTransport(std::unique_ptr<usb_handle> 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<usb_handle> 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<usb_handle>* 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<usb_handle> 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;

View file

@ -66,7 +66,7 @@ struct usb_handle {
std::string interface_name;
};
class WindowsUsbTransport : public Transport {
class WindowsUsbTransport : public UsbTransport {
public:
WindowsUsbTransport(std::unique_ptr<usb_handle> 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<usb_handle> 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<usb_handle> 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<usb_handle> handle = find_usb_device(callback);
return handle ? new WindowsUsbTransport(std::move(handle)) : nullptr;
}