Allow VirtualADB control VHCI state.
This change makes VirtualUSB more robust by detecting cases where device
is gone. The effect is that ADB will re-attach to device after it comes
back online, instead of reporting it as 'offline'.
Change-Id: Ib37a5273043ba42c47c8bf4a1f52f15152d05af7
(cherry picked from commit f4f38a878415365bf4d554641e880cd37935bb68)
diff --git a/ivserver/main.cc b/ivserver/main.cc
index 1a6dd3d..75ce28e 100644
--- a/ivserver/main.cc
+++ b/ivserver/main.cc
@@ -65,7 +65,7 @@
google::InstallFailureSignalHandler();
google::ParseCommandLineFlags(&argc, &argv, true);
- vadb::VirtualADBServer adb(FLAGS_usbsocket);
+ vadb::VirtualADBServer adb(FLAGS_usbsocket, FLAGS_usbipsocket);
CHECK(adb.Init());
vadb::usbip::Server usbip_server(FLAGS_usbipsocket, adb.Pool());
CHECK(usbip_server.Init()) << "Could not start usb/ip server";
diff --git a/vadb/main.cpp b/vadb/main.cpp
index 2aa5d8f..2e4c2cb 100644
--- a/vadb/main.cpp
+++ b/vadb/main.cpp
@@ -30,7 +30,7 @@
google::InitGoogleLogging(argv[0]);
google::ParseCommandLineFlags(&argc, &argv, true);
- vadb::VirtualADBServer adb(FLAGS_socket);
+ vadb::VirtualADBServer adb(FLAGS_socket, FLAGS_usbip_socket_name);
CHECK(adb.Init());
vadb::usbip::Server s(FLAGS_usbip_socket_name, adb.Pool());
diff --git a/vadb/usbip/server.cpp b/vadb/usbip/server.cpp
index 9f53a95..ceb70e8 100644
--- a/vadb/usbip/server.cpp
+++ b/vadb/usbip/server.cpp
@@ -25,9 +25,9 @@
namespace vadb {
namespace usbip {
Server::Server(const std::string& name, const DevicePool& devices)
- : name_{name}, device_pool_{devices}, vhci_{name} {}
+ : name_{name}, device_pool_{devices} {}
-bool Server::Init() { return CreateServerSocket() && vhci_.Init(); }
+bool Server::Init() { return CreateServerSocket(); }
// Open new listening server socket.
// Returns false, if listening socket could not be created.
@@ -54,7 +54,6 @@
if (!iter->AfterSelect(fd_read)) {
// If client conversation failed, hang up.
iter = clients_.erase(iter);
- vhci_.TriggerAttach();
continue;
}
++iter;
diff --git a/vadb/usbip/server.h b/vadb/usbip/server.h
index 3602732..06b5e30 100644
--- a/vadb/usbip/server.h
+++ b/vadb/usbip/server.h
@@ -21,7 +21,6 @@
#include "common/libs/fs/shared_fd.h"
#include "host/vadb/usbip/client.h"
#include "host/vadb/usbip/device_pool.h"
-#include "host/vadb/usbip/vhci_instrument.h"
namespace vadb {
namespace usbip {
@@ -57,7 +56,6 @@
std::list<Client> clients_;
const DevicePool& device_pool_;
- VHCIInstrument vhci_;
Server(const Server&) = delete;
Server& operator=(const Server&) = delete;
diff --git a/vadb/usbip/vhci_instrument.cpp b/vadb/usbip/vhci_instrument.cpp
index e1e3725..a3744eb 100644
--- a/vadb/usbip/vhci_instrument.cpp
+++ b/vadb/usbip/vhci_instrument.cpp
@@ -18,9 +18,9 @@
#include <netdb.h>
#include <sys/socket.h>
+#include <glog/logging.h>
#include <fstream>
#include <sstream>
-#include <glog/logging.h>
#include "common/libs/fs/shared_select.h"
#include "common/libs/fs/shared_fd.h"
@@ -42,6 +42,13 @@
constexpr char kVHCISubsystem[] = "platform";
constexpr char kVHCIDevType[] = "vhci_hcd";
+// Control messages.
+// Attach tells thread to attach remote device.
+// Detach tells thread to detach remote device.
+using ControlMsgType = uint8_t;
+constexpr ControlMsgType kControlAttach = 'A';
+constexpr ControlMsgType kControlDetach = 'D';
+
// Port status values deducted from /sys/devices/platform/vhci_hcd/status
enum {
// kVHCIPortFree indicates the port is not currently in use.
@@ -55,15 +62,18 @@
[](udev_device* device) { udev_device_unref(device); }),
name_(name) {}
+VHCIInstrument::~VHCIInstrument() {
+ if (sys_fd_ > 0) close(sys_fd_);
+}
+
bool VHCIInstrument::Init() {
- wake_event_ = avd::SharedFD::Event(0, 0);
+ avd::SharedFD::Pipe(&control_read_end_, &control_write_end_);
udev_.reset(udev_new());
CHECK(udev_) << "Could not create libudev context.";
- vhci_device_.reset(udev_device_new_from_subsystem_sysname(udev_.get(),
- kVHCISubsystem,
- kVHCIDevType));
+ vhci_device_.reset(udev_device_new_from_subsystem_sysname(
+ udev_.get(), kVHCISubsystem, kVHCIDevType));
if (!vhci_device_) {
LOG(ERROR) << "VHCI not available. Is the driver loaded?";
LOG(ERROR) << "Try: sudo modprobe vhci_hcd";
@@ -80,8 +90,7 @@
return false;
}
- attach_thread_.reset(new std::thread([this]() {
- AttachThread(); }));
+ attach_thread_.reset(new std::thread([this]() { AttachThread(); }));
return true;
}
@@ -112,8 +121,11 @@
}
void VHCIInstrument::TriggerAttach() {
- uint64_t count = 1;
- wake_event_->Write(&count, sizeof(count));
+ control_write_end_->Write(&kControlAttach, sizeof(kControlAttach));
+}
+
+void VHCIInstrument::TriggerDetach() {
+ control_write_end_->Write(&kControlDetach, sizeof(kControlDetach));
}
void VHCIInstrument::AttachThread() {
@@ -123,42 +135,73 @@
timeval period = {1, 0};
// Trigger attach upon start.
bool want_attach = true;
+ // Indicate running operation on start.
+ bool is_pending = true;
while (true) {
rset.Zero();
- rset.Set(wake_event_);
+ rset.Set(control_read_end_);
// Wait until poked.
if (0 != avd::Select(&rset, nullptr, nullptr,
- (want_attach ? &period : nullptr))) {
- uint64_t ignore;
- wake_event_->Read(&ignore, sizeof(ignore));
- LOG(INFO) << "Attach triggered.";
- want_attach = true;
+ (is_pending ? &period : nullptr))) {
+ ControlMsgType request_type;
+ control_read_end_->Read(&request_type, sizeof(request_type));
+ is_pending = true;
+ want_attach = request_type == kControlAttach;
+ LOG(INFO) << (want_attach ? "Attach" : "Detach") << " triggered.";
}
// Make an attempt to re-attach. If successful, clear pending attach flag.
- if (Attach()) {
- want_attach = false;
- } else {
- LOG(WARNING) << "Attach failed.";
- sleep(1);
+ if (is_pending) {
+ if (want_attach && Attach()) {
+ is_pending = false;
+ } else if (!want_attach && Detach()) {
+ is_pending = false;
+ } else {
+ LOG(INFO) << (want_attach ? "Attach" : "Detach") << " unsuccessful. "
+ << "Will re-try.";
+ sleep(1);
+ }
}
}
}
+bool VHCIInstrument::Detach() {
+ // sys_fd_ is the descriptor we supplied to the system to allow it to talk to
+ // (remote) USB device. By closing this descriptor we effectively force close
+ // connection to remote USB device.
+ if (sys_fd_ > 0) {
+ close(sys_fd_);
+ sys_fd_ = -1;
+ }
+
+ std::stringstream result;
+ result << port_;
+ std::ofstream detach(syspath_ + "/detach");
+
+ if (!detach.is_open()) {
+ LOG(WARNING) << "Could not open VHCI detach file.";
+ return false;
+ }
+ detach << result.str();
+ return detach.rdstate() == std::ios_base::goodbit;
+}
+
bool VHCIInstrument::Attach() {
avd::SharedFD socket =
avd::SharedFD::SocketLocalClient(name_.c_str(), true, SOCK_STREAM);
if (!socket->IsOpen()) return false;
- int dup_fd = socket->UNMANAGED_Dup();
+ sys_fd_ = socket->UNMANAGED_Dup();
std::stringstream result;
- result << port_ << ' ' << dup_fd << ' ' << kDefaultDeviceID << ' ' <<
- kDefaultDeviceSpeed;
+ result << port_ << ' ' << sys_fd_ << ' ' << kDefaultDeviceID << ' '
+ << kDefaultDeviceSpeed;
std::ofstream attach(syspath_ + "/attach");
if (!attach.is_open()) {
LOG(WARNING) << "Could not open VHCI attach file.";
+ close(sys_fd_);
+ sys_fd_ = -1;
return false;
}
attach << result.str();
@@ -168,7 +211,12 @@
// Kernel 4.10 is having problems communicating with USB/IP server if the
// socket is closed after it's passed to kernel. It is a clear indication that
// the kernel requires the socket to be kept open.
- return attach.rdstate() == std::ios_base::goodbit;
+ bool success = attach.rdstate() == std::ios_base::goodbit;
+ if (!success) {
+ close(sys_fd_);
+ sys_fd_ = -1;
+ }
+ return success;
}
} // namespace usbip
diff --git a/vadb/usbip/vhci_instrument.h b/vadb/usbip/vhci_instrument.h
index 107e140..f669fc0 100644
--- a/vadb/usbip/vhci_instrument.h
+++ b/vadb/usbip/vhci_instrument.h
@@ -29,7 +29,7 @@
class VHCIInstrument {
public:
VHCIInstrument(const std::string& name);
- virtual ~VHCIInstrument() = default;
+ virtual ~VHCIInstrument();
// Init opens vhci-hcd driver and allocates port to which remote USB device
// will be attached.
@@ -41,11 +41,18 @@
// device.
void TriggerAttach();
+ // TriggerDetach tells underlying thread to disconnect remote USB device.
+ void TriggerDetach();
+
private:
// Attach makes an attempt to configure VHCI to enable virtual USB device.
// Returns true, if configuration attempt was successful.
bool Attach();
+ // Detach disconnects virtual USB device.
+ // Returns true, if attempt was successful.
+ bool Detach();
+
// AttachThread is a background thread that responds to configuration
// requests.
void AttachThread();
@@ -57,8 +64,12 @@
std::string name_;
std::unique_ptr<std::thread> attach_thread_;
std::string syspath_;
- avd::SharedFD wake_event_;
+ avd::SharedFD control_write_end_;
+ avd::SharedFD control_read_end_;
int port_;
+ // Raw FD used by system to access remote USB device.
+ // This FD must remain valid until no longer needed.
+ int sys_fd_ = -1;
VHCIInstrument(const VHCIInstrument& other) = delete;
VHCIInstrument& operator=(const VHCIInstrument& other) = delete;
diff --git a/vadb/virtual_adb_client.cpp b/vadb/virtual_adb_client.cpp
index 440db10..b165287 100644
--- a/vadb/virtual_adb_client.cpp
+++ b/vadb/virtual_adb_client.cpp
@@ -28,8 +28,10 @@
constexpr int kHeartbeatTimeoutSeconds = 3;
} // namespace
-VirtualADBClient::VirtualADBClient(usbip::DevicePool* pool, avd::SharedFD fd)
- : pool_(pool), fd_(fd) {
+VirtualADBClient::VirtualADBClient(usbip::DevicePool* pool, avd::SharedFD fd,
+ const std::string& usbip_socket_name)
+ : pool_{pool}, fd_{fd}, vhci_{usbip_socket_name} {
+ CHECK(vhci_.Init());
timer_ = avd::SharedFD::TimerFD(CLOCK_MONOTONIC, 0);
SendHeartbeat();
}
@@ -119,7 +121,7 @@
bool VirtualADBClient::SendHeartbeat() {
VLOG(1) << "Sending heartbeat...";
- struct itimerspec spec{};
+ struct itimerspec spec {};
spec.it_value.tv_sec = kHeartbeatTimeoutSeconds;
timer_->TimerSet(0, &spec, nullptr);
@@ -134,7 +136,9 @@
if (is_ready && !is_remote_server_ready_) {
LOG(INFO) << "Remote server is now ready.";
PopulateRemoteDevices();
+ vhci_.TriggerAttach();
} else if (is_remote_server_ready_ && !is_ready) {
+ vhci_.TriggerDetach();
LOG(WARNING) << "Remote server connection lost.";
// It makes perfect sense to cancel all outstanding USB requests, as device
// is not going to answer any of these anyway.
diff --git a/vadb/virtual_adb_client.h b/vadb/virtual_adb_client.h
index 2ade53e..740df21 100644
--- a/vadb/virtual_adb_client.h
+++ b/vadb/virtual_adb_client.h
@@ -24,6 +24,7 @@
#include "host/vadb/usbip/device.h"
#include "host/vadb/usbip/device_pool.h"
#include "host/vadb/usbip/messages.h"
+#include "host/vadb/usbip/vhci_instrument.h"
namespace vadb {
// VirtualADBClient is a companion class for USBForwarder, running on
@@ -34,7 +35,8 @@
// remote USB devices possible with help of USB/IP protocol.
class VirtualADBClient {
public:
- VirtualADBClient(usbip::DevicePool* pool, avd::SharedFD fd);
+ VirtualADBClient(usbip::DevicePool* pool, avd::SharedFD fd,
+ const std::string& usbip_socket_name);
virtual ~VirtualADBClient() = default;
@@ -90,6 +92,7 @@
usbip::DevicePool* pool_;
avd::SharedFD fd_;
avd::SharedFD timer_;
+ usbip::VHCIInstrument vhci_;
bool is_remote_server_ready_ = false;
uint32_t tag_ = 0;
diff --git a/vadb/virtual_adb_server.cpp b/vadb/virtual_adb_server.cpp
index 8d331e7..bf39bb3 100644
--- a/vadb/virtual_adb_server.cpp
+++ b/vadb/virtual_adb_server.cpp
@@ -58,7 +58,7 @@
return;
}
- clients_.emplace_back(&pool_, client);
+ clients_.emplace_back(&pool_, client, usbip_name_);
}
} // namespace vadb
diff --git a/vadb/virtual_adb_server.h b/vadb/virtual_adb_server.h
index b1f82fb..91f6611 100644
--- a/vadb/virtual_adb_server.h
+++ b/vadb/virtual_adb_server.h
@@ -26,7 +26,10 @@
// VirtualADBServer manages incoming VirtualUSB/ADB connections from QEmu.
class VirtualADBServer {
public:
- VirtualADBServer(const std::string& name) : name_(name) {}
+ VirtualADBServer(const std::string& usb_socket_name,
+ const std::string& usbip_socket_name)
+ : name_(usb_socket_name), usbip_name_(usbip_socket_name) {}
+
~VirtualADBServer() = default;
// Initialize this instance of Server.
@@ -49,6 +52,7 @@
usbip::DevicePool pool_;
std::string name_;
+ std::string usbip_name_;
avd::SharedFD server_;
std::list<VirtualADBClient> clients_;