USBIP classes
Change-Id: I4256e666e48e77e04c82aa67d7d4b6b59d6e20cb
(cherry picked from commit 0bb107f3853d88748ad59b435cef85dacd94ad26)
diff --git a/vadb/usbip/BUILD b/vadb/usbip/BUILD
new file mode 100644
index 0000000..29109b9
--- /dev/null
+++ b/vadb/usbip/BUILD
@@ -0,0 +1,27 @@
+cc_library(
+ name = "usbip_lib",
+ srcs = [
+ "client.cpp",
+ "client.h",
+ "device.h",
+ "device_pool.cpp",
+ "device_pool.h",
+ "messages.cpp",
+ "messages.h",
+ "server.cpp",
+ "server.h",
+ ],
+ hdrs = [
+ "client.h",
+ "device.h",
+ "device_pool.h",
+ "messages.h",
+ "server.h",
+ ],
+ deps = [
+ "@cuttlefish//common/libs/fs",
+ "@gflags_repo//:gflags",
+ "@glog_repo//:glog",
+ ],
+)
+
diff --git a/vadb/usbip/README.md b/vadb/usbip/README.md
new file mode 100644
index 0000000..e652352
--- /dev/null
+++ b/vadb/usbip/README.md
@@ -0,0 +1,36 @@
+# USB/IP server library
+
+This folder contains set of classes and structures that constitute basic USB/IP
+server.
+
+Protocol used in this library is defined as part of
+[Linux kernel documentation](https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt).
+
+## Structure
+
+### [`vadb::usbip::Device`](./device.h)[](#Device)
+
+Structure describing individual device accessible over USB/IP protocol.
+
+### [`vadb::usbip::DevicePool`](./device_pool.h)[](#DevicePool)
+
+DevicePool holds a set of [Devices](#Device) that can be enumerated and
+accessed by clients of this Server.
+
+### [`vadb::usbip::Server`](./server.h)
+
+Purpose of this class is to start a new listening socket and accept incoming
+USB/IP connections & requests.
+
+### [`vadb::usbip::Client`](./client.h)
+
+Client class represents individual USB/IP connection. Client enables remote
+USB/IP client to enumerate and access devices registered in
+[DevicePool](#DevicePool).
+
+### [`USB/IP Messages`](./messages.h)
+
+This file contains structures and enum values defined by the USB/IP protocol.
+All definitions found there have been collected from
+[Linux kernel documentation](https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt)
+.
diff --git a/vadb/usbip/client.cpp b/vadb/usbip/client.cpp
new file mode 100644
index 0000000..c483f42
--- /dev/null
+++ b/vadb/usbip/client.cpp
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "host/vadb/usbip/client.h"
+
+#include <glog/logging.h>
+#include <iostream>
+
+#include "host/vadb/usbip/device.h"
+#include "host/vadb/usbip/messages.h"
+
+namespace vadb {
+namespace usbip {
+namespace {
+// Parse BUI ID (typically in form #-#) and extract root hub and bus.
+// We use these values as synonyms to bus and device numbers internally.
+// Returns false, if extracting BusDevNumber was unsuccessful.
+bool ParseBusID(const OpReqRepBusId& busid, DevicePool::BusDevNumber* dn) {
+ return sscanf(busid, "%hu-%hu", &dn->bus_number, &dn->dev_number) == 2;
+}
+
+// Build USBIP Device Report.
+void BuildDeviceNode(DevicePool::BusDevNumber dn, const Device& dd,
+ OpRepDeviceInfo* node) {
+ memset(node, 0, sizeof(*node));
+
+ snprintf(node->usb_path, sizeof(node->usb_path),
+ "/sys/devices/usb/vhci/%hu-%hu", dn.bus_number, dn.dev_number);
+ snprintf(node->bus_id, sizeof(node->bus_id), "%hu-%hu", dn.bus_number,
+ dn.dev_number);
+
+ node->bus_num = dn.bus_number;
+ node->dev_num = dn.dev_number;
+
+ // TODO(ender): How is this defined?
+ node->speed = 2;
+
+ node->id_vendor = dd.vendor_id;
+ node->id_product = dd.product_id;
+ node->bcd_device = dd.dev_version;
+ node->device_class = dd.dev_class;
+ node->device_subclass = dd.dev_subclass;
+ node->device_protocol = dd.dev_protocol;
+ node->configuration_value = dd.configuration_number;
+ node->num_configurations = dd.configurations_count;
+ node->num_interfaces = dd.interfaces.size();
+}
+} // namespace
+
+// Handle incoming USB/IP message.
+// USB/IP messages have two forms:
+// - OPs (OPERATIONs) - are executed only before remote device is attached,
+// - CMDs (COMMANDs) - are executed only after remote device is attached.
+// The two types of commands are incompatible with one another, so it's
+// impossible to tell which is being parsed, unless you know the state of this
+// connection.
+//
+// Returns false, if connection should be dropped.
+bool Client::HandleIncomingMessage() {
+ return attached_ ? HandleCommand() : HandleOperation();
+}
+
+// Handle incoming OPERATION.
+//
+// Reads next OP from client channel.
+// Returns false, if connection should be dropped.
+bool Client::HandleOperation() {
+ OpHeader hdr;
+ if (!RecvUSBIPMsg(fd_, &hdr)) {
+ LOG(ERROR) << "Could not read operation header: " << fd_->StrError();
+ return false;
+ }
+
+ if (hdr.status != 0) {
+ // This really shouldn't happen unless we're already reading random bytes.
+ LOG(ERROR) << "Unexpected request status: " << hdr.status;
+ return false;
+ }
+
+ // USB/IP version is client-driven. Client requires server to support the
+ // version reported by client, so we need to cache it somehow.
+ if (!proto_version_) {
+ proto_version_ = hdr.version;
+ if ((proto_version_ < kMinVersion) || (proto_version_ > kMaxVersion)) {
+ LOG(ERROR) << "Unsupported USB/IP protocol version: " << proto_version_
+ << ", want: [" << kMinVersion << "-" << kMaxVersion << "].";
+ return false;
+ }
+ } else {
+ // Now that we cache client version, we can use it to verify if we're not
+ // reading random data bytes again. Sort-of like a MAGIC word.
+ if (proto_version_ != hdr.version) {
+ LOG(ERROR) << "Inconsistent USB/IP version support reported by client; "
+ << "I've seen " << proto_version_
+ << ", and now i see: " << hdr.version
+ << ". Client is not sane. Disconnecting.";
+ return false;
+ }
+ }
+
+ // Protocol itself. Behold.
+ switch (hdr.command) {
+ case kUsbIpOpReqDevList:
+ return HandleListOp();
+
+ case kUsbIpOpReqImport:
+ return HandleImportOp();
+
+ default:
+ LOG(WARNING) << "Ignoring unknown command: " << hdr.command;
+ // Drop connection at this point. Client may attempt to send some request
+ // data after header, and we risk interpreting this as another OP.
+ return false;
+ }
+}
+
+// Handle incoming DEVICE LIST OPERATION.
+//
+// Send list of (virtual) devices attached to this USB/IP server.
+// Returns false, if connection should be dropped.
+bool Client::HandleListOp() {
+ LOG(INFO) << "Client requests device list";
+ // NOTE: Device list Request is currently empty. Do not attempt to read.
+
+ // Send command header
+ OpHeader op{};
+ op.version = proto_version_;
+ op.command = kUsbIpOpRepDevList;
+ op.status = 0;
+ if (!SendUSBIPMsg(fd_, op)) {
+ LOG(ERROR) << "Could not send device list header: " << fd_->StrError();
+ return false;
+ }
+
+ // Send devlist header
+ OpRepDeviceListInfo rep{};
+ rep.num_exported_devices = pool_.Size();
+ if (!SendUSBIPMsg(fd_, rep)) {
+ LOG(ERROR) << "Could not send device list header: " << fd_->StrError();
+ return false;
+ }
+
+ // Send device reports.
+ for (const auto& pair : pool_) {
+ OpRepDeviceInfo device;
+ BuildDeviceNode(pair.first, *pair.second, &device);
+ if (!SendUSBIPMsg(fd_, device)) {
+ LOG(ERROR) << "Could not send device list node: " << fd_->StrError();
+ return false;
+ }
+
+ OpRepInterfaceInfo repif;
+ // Interfaces are ligth. Copying value is easier than dereferencing
+ // reference.
+ for (auto iface : pair.second->interfaces) {
+ repif.iface_class = iface.iface_class;
+ repif.iface_subclass = iface.iface_subclass;
+ repif.iface_protocol = iface.iface_protocol;
+ if (!SendUSBIPMsg(fd_, repif)) {
+ LOG(ERROR) << "Could not send device list interface: "
+ << fd_->StrError();
+ return false;
+ }
+ }
+ }
+
+ LOG(INFO) << "Device list sent.";
+ return true;
+}
+
+// Handle incoming IMPORT OPERATION.
+//
+// Attach device to remote host. Flip internal state machine to start processing
+// COMMANDs.
+// Returns false, if connection should be dropped.
+bool Client::HandleImportOp() {
+ // Request contains BUS ID
+ OpReqRepBusId req;
+ if (!RecvUSBIPMsg(fd_, &req)) {
+ LOG(ERROR) << "Could not read op import data: " << fd_->StrError();
+ return false;
+ }
+ LOG(INFO) << "Client requests device import for bus" << req;
+
+ // Craft response header.
+ OpHeader op{};
+ op.version = proto_version_;
+ op.command = kUsbIpOpRepImport;
+ op.status = 0;
+
+ Device* device = nullptr;
+ DevicePool::BusDevNumber dn;
+
+ // Find requested device.
+ if (ParseBusID(req, &dn)) {
+ device = pool_.GetDevice(dn);
+ if (!device) {
+ op.status = 1;
+ LOG(ERROR) << "Import failed; No device registered on bus " << req;
+ }
+ } else {
+ LOG(ERROR) << "Could not parse BUS ID: " << req;
+ op.status = 1;
+ }
+
+ // Craft response data, if device was found.
+ OpRepDeviceInfo rep{};
+ if (device) {
+ BuildDeviceNode(dn, *device, &rep);
+ }
+
+ // Send response header.
+ if (!SendUSBIPMsg(fd_, op)) {
+ LOG(ERROR) << "Could not send import header: " << fd_->StrError();
+ return false;
+ }
+
+ // Send response data, if header indicates success.
+ if (!op.status) {
+ if (!SendUSBIPMsg(fd_, rep)) {
+ LOG(ERROR) << "Could not send import body: " << fd_->StrError();
+ return false;
+ }
+ attached_ = true;
+ LOG(INFO) << "Virtual USB attach successful.";
+ }
+
+ return true;
+}
+
+// Handle incoming COMMAND.
+//
+// Read next CMD from client channel.
+// Returns false, if connection should be dropped.
+bool Client::HandleCommand() {
+ CmdHeader hdr;
+ if (!RecvUSBIPMsg(fd_, &hdr)) {
+ LOG(ERROR) << "Could not read command header: " << fd_->StrError();
+ return false;
+ }
+
+ // And the protocol, again.
+ switch (hdr.command) {
+ case kUsbIpCmdReqSubmit:
+ HandleSubmitCmd(hdr);
+ break;
+
+ case kUsbIpCmdReqUnlink:
+ HandleUnlinkCmd(hdr);
+ break;
+
+ default:
+ LOG(ERROR) << "Unsupported command requested: " << hdr.command;
+ return false;
+ }
+ return true;
+}
+
+// Handle incoming SUBMIT COMMAND.
+//
+// Execute command on specified USB device.
+// Returns false, if connection should be dropped.
+bool Client::HandleSubmitCmd(const CmdHeader& cmd) {
+ CmdReqSubmit req;
+ if (!RecvUSBIPMsg(fd_, &req)) {
+ LOG(ERROR) << "Could not read submit command: " << fd_->StrError();
+ return false;
+ }
+
+ // Response template.
+ // - in header, host doesn't care about anything else except for command type
+ // and sequence number.
+ // - in body, report status == !OK unless we completed everything
+ // successfully.
+ CmdHeader rephdr{};
+ rephdr.command = kUsbIpCmdRepSubmit;
+ rephdr.seq_num = cmd.seq_num;
+ CmdRepSubmit rep{};
+ rep.status = 1;
+
+ std::vector<uint8_t> payload_in;
+ std::vector<uint8_t> payload_out;
+ int payload_length = req.transfer_buffer_length;
+
+ // Find requested device and execute command.
+ auto device = pool_.GetDevice({cmd.bus_num, cmd.dev_num});
+ if (device) {
+ // Read DATA_IN, if specified.
+ if (cmd.direction == kUsbIpDirectionOut && payload_length) {
+ LOG(INFO) << "Reading payload (" << payload_length << " bytes).";
+ payload_in.resize(payload_length);
+ auto read = fd_->Recv(payload_in.data(), payload_in.size(), MSG_NOSIGNAL);
+ if (read != payload_in.size()) {
+ LOG(ERROR) << "Short read while receiving payload; want="
+ << payload_in.size() << ", got=" << read
+ << ", err: " << fd_->StrError();
+ return false;
+ }
+ }
+
+ // Indicate execution result. Status is 0 when everything is OK.
+ rep.status = !device->handle_request(req.setup, payload_in, &payload_out);
+
+ // Trim / expand output payload length. Client will expect this number of
+ // bytes and may just freeze trying to read what it just requested.
+ if (cmd.direction == kUsbIpDirectionIn) {
+ // Erase output payload buffer if status is not OK.
+ payload_out.resize(rep.status ? 0 : payload_length);
+ }
+ }
+
+ rep.actual_length = payload_out.size();
+
+ // Data out.
+ if (!SendUSBIPMsg(fd_, rephdr)) {
+ LOG(ERROR) << "Failed to send response header: " << fd_->StrError();
+ return false;
+ }
+
+ if (!SendUSBIPMsg(fd_, rep)) {
+ LOG(ERROR) << "Failed to send response body: " << fd_->StrError();
+ return false;
+ }
+
+ if (payload_out.size()) {
+ if (fd_->Send(payload_out.data(), payload_out.size(), MSG_NOSIGNAL) !=
+ payload_out.size()) {
+ LOG(ERROR) << "Failed to send response payload: " << fd_->StrError();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Handle incoming UNLINK COMMAND.
+//
+// Unlink removes command specified via seq_num from a list of commands to be
+// executed.
+// We don't schedule commands for execution, so technically every UNLINK will
+// come in late.
+// Returns false, if connection should be dropped.
+bool Client::HandleUnlinkCmd(const CmdHeader& cmd) {
+ CmdReqUnlink req;
+ if (!RecvUSBIPMsg(fd_, &req)) {
+ LOG(ERROR) << "Could not read unlink command: " << fd_->StrError();
+ return false;
+ }
+ LOG(INFO) << "Client requested to unlink previously submitted command: "
+ << req.seq_num;
+
+ CmdHeader rephdr{};
+ rephdr.command = kUsbIpCmdRepUnlink;
+ rephdr.seq_num = cmd.seq_num;
+
+ // Technically we do not schedule commands for execution, so we cannot
+ // de-queue commands, either. Indicate this by sending status != ok.
+ CmdRepUnlink rep;
+ rep.status = 1;
+
+ if (!SendUSBIPMsg(fd_, rephdr)) {
+ LOG(ERROR) << "Could not send unlink command header: " << fd_->StrError();
+ return false;
+ }
+
+ if (!SendUSBIPMsg(fd_, rep)) {
+ LOG(ERROR) << "Could not send unlink command data: " << fd_->StrError();
+ return false;
+ }
+ return true;
+}
+
+} // namespace usbip
+} // namespace vadb
diff --git a/vadb/usbip/client.h b/vadb/usbip/client.h
new file mode 100644
index 0000000..572d7c2
--- /dev/null
+++ b/vadb/usbip/client.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/vadb/usbip/device_pool.h"
+#include "host/vadb/usbip/messages.h"
+
+namespace vadb {
+namespace usbip {
+
+// Represents USB/IP client, or individual connection to our USB/IP server.
+// Multiple clients are allowed, even if practically we anticipate only one
+// connection at the time.
+class Client final {
+ public:
+ Client(const DevicePool& pool, const avd::SharedFD& fd)
+ : pool_(pool), fd_(fd) {}
+
+ ~Client() {}
+
+ // Respond to message from remote client.
+ // Returns false, if client violated protocol or disconnected, indicating,
+ // that this instance should no longer be used.
+ bool HandleIncomingMessage();
+
+ const avd::SharedFD& fd() const { return fd_; }
+
+ private:
+ // Process messages that are valid only while client is detached.
+ // Returns false, if conversation was unsuccessful.
+ bool HandleOperation();
+
+ // Process messages that are valid only while client is attached.
+ // Returns false, if connection should be dropped.
+ bool HandleCommand();
+
+ // List remote USB devices.
+ // Returns false, if connection should be dropped.
+ bool HandleListOp();
+
+ // Attach virtual USB devices to remote host.
+ // Returns false, if connection should be dropped.
+ bool HandleImportOp();
+
+ // Execute command on USB device.
+ // Returns false, if connection should be dropped.
+ bool HandleSubmitCmd(const CmdHeader& hdr);
+
+ // Unlink previously submitted message from device queue.
+ // Returns false, if connection should be dropped.
+ bool HandleUnlinkCmd(const CmdHeader& hdr);
+
+ const DevicePool& pool_;
+ avd::SharedFD fd_;
+
+ // True, if client requested USB device attach.
+ bool attached_ = false;
+ uint16_t proto_version_ = 0;
+
+ Client(const Client&) = delete;
+ Client& operator=(const Client&) = delete;
+};
+
+} // namespace usbip
+} // namespace vadb
diff --git a/vadb/usbip/device.h b/vadb/usbip/device.h
new file mode 100644
index 0000000..47ba0c1
--- /dev/null
+++ b/vadb/usbip/device.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <cstdint>
+#include <functional>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "host/vadb/usbip/messages.h"
+
+namespace vadb {
+namespace usbip {
+
+// The device descriptor of a USB device represents a USB device that is
+// available for import.
+class Device {
+ public:
+ // Interface provides minimal description of device's interface.
+ struct Interface {
+ uint8_t iface_class;
+ uint8_t iface_subclass;
+ uint8_t iface_protocol;
+ };
+
+ // vendor_id and product_id identify device manufacturer and type.
+ // dev_version describes device version (as BCD).
+ uint16_t vendor_id;
+ uint16_t product_id;
+ uint16_t dev_version;
+
+ // Class, Subclass and Protocol define device type.
+ uint8_t dev_class;
+ uint8_t dev_subclass;
+ uint8_t dev_protocol;
+
+ // ConfigurationsCount and ConfigurationNumber describe total number of device
+ // configurations and currently activated device configuration.
+ size_t configurations_count;
+ size_t configuration_number;
+
+ // Interfaces returns a collection of device interfaces.
+ std::vector<Interface> interfaces;
+
+ // Device request dispatcher.
+ std::function<bool(const CmdRequest& request,
+ const std::vector<uint8_t>& data_in,
+ std::vector<uint8_t>* data_out)>
+ handle_request;
+};
+
+} // namespace usbip
+} // namespace vadb
diff --git a/vadb/usbip/device_pool.cpp b/vadb/usbip/device_pool.cpp
new file mode 100644
index 0000000..be026d1
--- /dev/null
+++ b/vadb/usbip/device_pool.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/vadb/usbip/device_pool.h"
+
+#include <glog/logging.h>
+
+namespace vadb {
+namespace usbip {
+
+void DevicePool::AddDevice(BusDevNumber bdn, std::unique_ptr<Device> device) {
+ LOG_IF(FATAL, devices_.find(bdn) != devices_.end())
+ << "Node already defined for bus=" << bdn.bus_number
+ << ", dev=" << bdn.dev_number;
+ devices_[bdn] = std::move(device);
+}
+
+Device* DevicePool::GetDevice(BusDevNumber bus_id) const {
+ auto iter = devices_.find(bus_id);
+ if (iter == devices_.end()) return nullptr;
+ return iter->second.get();
+}
+
+} // namespace usbip
+} // namespace vadb
diff --git a/vadb/usbip/device_pool.h b/vadb/usbip/device_pool.h
new file mode 100644
index 0000000..4a7c637
--- /dev/null
+++ b/vadb/usbip/device_pool.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <map>
+#include <string>
+
+#include "host/vadb/usbip/device.h"
+
+namespace vadb {
+namespace usbip {
+// Container for all virtual USB/IP devices.
+// Stores devices by virtual BUS ID.
+class DevicePool {
+ public:
+ // BusDevNumber is a pair uniquely identifying bus and device.
+ struct BusDevNumber {
+ uint16_t bus_number;
+ uint16_t dev_number;
+
+ bool operator<(BusDevNumber other) const {
+ return (bus_number << 16 | dev_number) <
+ (other.bus_number << 16 | other.dev_number);
+ }
+ };
+
+ // Internal container type.
+ using MapType = std::map<BusDevNumber, std::unique_ptr<Device>>;
+
+ DevicePool() = default;
+ virtual ~DevicePool() = default;
+
+ // Add new device associated with virtual BUS ID.
+ void AddDevice(BusDevNumber bus_id, std::unique_ptr<Device> device);
+
+ // Get device associated with supplied virtual bus/device number.
+ Device* GetDevice(BusDevNumber bus_dev_num) const;
+
+ // Get total number of USB/IP devices.
+ size_t Size() const { return devices_.size(); }
+
+ MapType::const_iterator begin() const { return devices_.cbegin(); }
+ MapType::const_iterator end() const { return devices_.cend(); }
+
+ private:
+ MapType devices_;
+
+ DevicePool(const DevicePool&) = delete;
+ DevicePool& operator=(const DevicePool&) = delete;
+};
+
+} // namespace usbip
+} // namespace vadb
diff --git a/vadb/usbip/messages.cpp b/vadb/usbip/messages.cpp
new file mode 100644
index 0000000..962905c
--- /dev/null
+++ b/vadb/usbip/messages.cpp
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "host/vadb/usbip/messages.h"
+
+#include <netinet/in.h>
+#include <iostream>
+
+#include <glog/logging.h>
+
+namespace vadb {
+namespace usbip {
+namespace {
+// Basic sanity checking.
+// We're using CmdHeader + CmdReq/Rep in case any of the fields is moved between
+// structures.
+constexpr int kUsbIpCmdLength = 48;
+
+static_assert(sizeof(CmdHeader) + sizeof(CmdReqSubmit) == kUsbIpCmdLength,
+ "USB/IP command + header must be exactly 48 bytes.");
+static_assert(sizeof(CmdHeader) + sizeof(CmdRepSubmit) == kUsbIpCmdLength,
+ "USB/IP command + header must be exactly 48 bytes.");
+static_assert(sizeof(CmdHeader) + sizeof(CmdReqUnlink) == kUsbIpCmdLength,
+ "USB/IP command + header must be exactly 48 bytes.");
+static_assert(sizeof(CmdHeader) + sizeof(CmdRepUnlink) == kUsbIpCmdLength,
+ "USB/IP command + header must be exactly 48 bytes.");
+} // namespace
+
+// NetToHost and HostToNet are used to reduce risk of copy/paste errors and to
+// provide uniform method of converting messages between different endian types.
+namespace internal {
+
+template <>
+void NetToHost(uint32_t* t) {
+ *t = ntohl(*t);
+}
+
+template <>
+void NetToHost(Command* t) {
+ *t = static_cast<Command>(ntohl(*t));
+}
+
+template <>
+void NetToHost(Direction* t) {
+ *t = static_cast<Direction>(ntohl(*t));
+}
+
+template <>
+void NetToHost(uint16_t* t) {
+ *t = ntohs(*t);
+}
+
+template <>
+void NetToHost(Operation* t) {
+ *t = static_cast<Operation>(ntohs(*t));
+}
+
+template <>
+void NetToHost(CmdHeader* t) {
+ NetToHost(&t->command);
+ NetToHost(&t->seq_num);
+ NetToHost(&t->bus_num);
+ NetToHost(&t->dev_num);
+ NetToHost(&t->direction);
+ NetToHost(&t->endpoint);
+}
+
+template <>
+void NetToHost(CmdReqSubmit* t) {
+ NetToHost(&t->transfer_flags);
+ NetToHost(&t->transfer_buffer_length);
+ NetToHost(&t->start_frame);
+ NetToHost(&t->number_of_packets);
+ NetToHost(&t->deadline_interval);
+}
+
+template <>
+void NetToHost(OpHeader* t) {
+ NetToHost(&t->version);
+ NetToHost(&t->command);
+ NetToHost(&t->status);
+}
+
+template <>
+void NetToHost(OpReqRepBusId* t) {}
+
+template <>
+void NetToHost(CmdReqUnlink* t) {
+ NetToHost(&t->seq_num);
+}
+
+template <>
+void HostToNet(uint32_t* t) {
+ *t = htonl(*t);
+}
+
+template <>
+void HostToNet(Command* t) {
+ *t = static_cast<Command>(htonl(*t));
+}
+
+template <>
+void HostToNet(Direction* t) {
+ *t = static_cast<Direction>(htonl(*t));
+}
+
+template <>
+void HostToNet(uint16_t* t) {
+ *t = htons(*t);
+}
+
+template <>
+void HostToNet(Operation* t) {
+ *t = static_cast<Operation>(htons(*t));
+}
+
+template <>
+void HostToNet(CmdHeader* t) {
+ HostToNet(&t->command);
+ HostToNet(&t->seq_num);
+ HostToNet(&t->bus_num);
+ HostToNet(&t->dev_num);
+ HostToNet(&t->direction);
+ HostToNet(&t->endpoint);
+}
+
+template <>
+void HostToNet(CmdRepSubmit* t) {
+ HostToNet(&t->status);
+ HostToNet(&t->actual_length);
+ HostToNet(&t->start_frame);
+ HostToNet(&t->number_of_packets);
+ HostToNet(&t->error_count);
+}
+
+template <>
+void HostToNet(OpHeader* t) {
+ HostToNet(&t->version);
+ HostToNet(&t->command);
+ HostToNet(&t->status);
+}
+
+template <>
+void HostToNet(OpRepDeviceListInfo* t) {
+ HostToNet(&t->num_exported_devices);
+}
+
+template <>
+void HostToNet(OpRepDeviceInfo* t) {
+ HostToNet(&t->bus_num);
+ HostToNet(&t->dev_num);
+ HostToNet(&t->speed);
+
+ // Note: The following should not be rotated when exporting host USB devices.
+ // We only rotate these here because we are using native endian everywhere.
+ HostToNet(&t->id_vendor);
+ HostToNet(&t->id_product);
+ HostToNet(&t->bcd_device);
+}
+
+template <>
+void HostToNet(CmdRepUnlink* t) {
+ HostToNet(&t->status);
+}
+
+template <>
+void HostToNet(OpRepInterfaceInfo* t) {}
+
+} // namespace internal
+
+// Output and diagnostic functionality.
+std::ostream& operator<<(std::ostream& out, Operation op) {
+ switch (op) {
+ case kUsbIpOpReqDevList:
+ out << "OpReqDevList";
+ break;
+ case kUsbIpOpRepDevList:
+ out << "OpRepDevList";
+ break;
+ case kUsbIpOpReqImport:
+ out << "OpReqImport";
+ break;
+ case kUsbIpOpRepImport:
+ out << "OpRepImport";
+ break;
+ default:
+ out << "UNKNOWN (" << int(op) << ")";
+ break;
+ }
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const CmdHeader& header) {
+ out << "CmdHeader\n";
+ out << "\t\tcmd:\t" << header.command << '\n';
+ out << "\t\tseq#:\t" << header.seq_num << '\n';
+ out << "\t\tbus#:\t0x" << header.bus_num << '\n';
+ out << "\t\tdev#:\t0x" << header.dev_num << '\n';
+ out << "\t\tdir:\t" << (header.direction ? "in" : "out") << '\n';
+ out << "\t\tendpt:\t" << header.endpoint << "\n";
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const CmdRequest& setup) {
+ out << "Request\n";
+ out << "\t\t\ttype:\t" << std::hex << int(setup.type) << '\n';
+ out << "\t\t\treq:\t" << int(setup.cmd) << std::dec << '\n';
+ out << "\t\t\tval:\t" << setup.value << '\n';
+ out << "\t\t\tidx:\t" << setup.index << '\n';
+ out << "\t\t\tlen:\t" << setup.length << '\n';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const CmdReqSubmit& submit) {
+ out << "CmdReqSubmit\n";
+ out << "\t\ttr_flg:\t" << std::hex << submit.transfer_flags << std::dec
+ << '\n';
+ out << "\t\ttr_len:\t" << submit.transfer_buffer_length << '\n';
+ out << "\t\tstart:\t" << submit.start_frame << '\n';
+ out << "\t\tpktcnt:\t" << submit.number_of_packets << '\n';
+ out << "\t\tttl:\t" << submit.deadline_interval << '\n';
+ out << "\t\tsetup:\t" << submit.setup << '\n';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const CmdRepSubmit& submit) {
+ out << "CmdRepSubmit\n";
+ out << "\t\tstatus:\t" << submit.status << '\n';
+ out << "\t\tlen:\t" << submit.actual_length << '\n';
+ out << "\t\tstart:\t" << submit.start_frame << '\n';
+ out << "\t\tpktcnt:\t" << submit.number_of_packets << '\n';
+ out << "\t\terrors:\t" << submit.error_count << '\n';
+ out << "\t\tsetup:\t" << submit.setup << '\n';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const CmdReqUnlink& unlink) {
+ out << "CmdReqUnlink\n";
+ out << "\t\tseq#:\t" << unlink.seq_num << '\n';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const CmdRepUnlink& unlink) {
+ out << "CmdRepUnlink\n";
+ out << "\t\tstatus:\t" << unlink.status << '\n';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const OpHeader& header) {
+ out << "OpHeader\n";
+ out << "\t\tvrsn:\t" << std::hex << header.version << '\n';
+ out << "\t\tcmd:\t" << header.command << '\n';
+ out << "\t\tstatus:\t" << header.status << std::dec << '\n';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const OpRepDeviceInfo& import) {
+ out << "OpRepDeviceInfo\n";
+ out << "\t\tsysfs:\t" << import.usb_path << '\n';
+ out << "\t\tbusid:\t" << import.bus_id << '\n';
+ out << "\t\tbus#:\t" << import.bus_num << '\n';
+ out << "\t\tdev#:\t" << import.dev_num << '\n';
+ out << "\t\tspeed:\t" << import.speed << '\n';
+ out << "\t\tvendor:\t" << std::hex << import.id_vendor << std::dec << '\n';
+ out << "\t\tprodct:\t" << std::hex << import.id_product << std::dec << '\n';
+ out << "\t\trel:\t" << std::hex << import.bcd_device << std::dec << '\n';
+ out << "\t\tcls:\t" << int(import.device_class) << '\n';
+ out << "\t\tsubcls:\t" << int(import.device_subclass) << '\n';
+ out << "\t\tproto:\t" << int(import.device_protocol) << '\n';
+ out << "\t\tcfg#:\t" << int(import.configuration_value) << '\n';
+ out << "\t\tcfgs#:\t" << int(import.num_configurations) << '\n';
+ out << "\t\tifs#:\t" << int(import.num_interfaces) << '\n';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const OpRepDeviceListInfo& list) {
+ out << "OpRepDeviceListInfo\n";
+ out << "\t\tcount:\t" << list.num_exported_devices << '\n';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const OpRepInterfaceInfo& i) {
+ out << "OpRepDevListIface\n";
+ out << "\t\tcls:\t" << int(i.iface_class) << '\n';
+ out << "\t\tsubcls:\t" << int(i.iface_subclass) << '\n';
+ out << "\t\tproto:\t" << int(i.iface_protocol) << '\n';
+ return out;
+}
+
+} // namespace usbip
+} // namespace vadb
diff --git a/vadb/usbip/messages.h b/vadb/usbip/messages.h
new file mode 100644
index 0000000..7137940
--- /dev/null
+++ b/vadb/usbip/messages.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <glog/logging.h>
+#include <stdint.h>
+
+#include "common/libs/fs/shared_fd.h"
+
+// Requests and constants below are defined in kernel documentation file:
+// https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt
+namespace vadb {
+namespace usbip {
+namespace internal {
+// Rotate endianness of the data to match protocol.
+template <typename T>
+void HostToNet(T* data);
+template <typename T>
+void NetToHost(T* data);
+} // namespace internal
+
+// This is the range of USB/IP versions in which we should be safe to operate.
+// USB/IP expects (and the expectation is strong) that the version reported by
+// server is *same* as version reported by client, so we have to mock this for
+// every client.
+// TODO(ender): find if this is documented anywhere.
+constexpr int kMinVersion = 0x100; // 1.0.0
+constexpr int kMaxVersion = 0x111; // 1.1?.1?
+
+// Send message to USB/IP client.
+// Accept data by value and modify it to match net endian locally.
+// Returns true, if message was sent successfully.
+template <typename T>
+bool SendUSBIPMsg(const avd::SharedFD& fd, T data) {
+ VLOG(2) << "Sending " << sizeof(T) << " bytes...";
+ VLOG(1) << data;
+ internal::HostToNet(&data);
+ return fd->Send(&data, sizeof(T), MSG_NOSIGNAL) == sizeof(T);
+}
+
+// Receive message from USB/IP client.
+// After message is received, it's updated to match host endian.
+// Returns true, if message was received successfully.
+template <typename T>
+bool RecvUSBIPMsg(const avd::SharedFD& fd, T* data) {
+ VLOG(2) << "Reading " << sizeof(T) << " bytes...";
+ bool res = fd->Recv(data, sizeof(T), MSG_NOSIGNAL) == sizeof(T);
+ if (res) {
+ internal::NetToHost(data);
+ VLOG(1) << *data;
+ }
+ return res;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// OPERATIONS
+////////////////////////////////////////////////////////////////////////////////
+
+// Operation numbers. Operations are valid only when USB device is detached.
+enum Operation : uint16_t {
+ // Request import (~attach) USB device. Request data format:
+ // - OpReqRepBusId - describing BUS ID.
+ kUsbIpOpReqImport = 0x8003,
+
+ // Import (~attach) response. Response format:
+ // - OpRepDeviceInfo - USBIP device descriptor.
+ kUsbIpOpRepImport = 3,
+
+ // Request list available devices. No request data.
+ kUsbIpOpReqDevList = 0x8005,
+
+ // Device list response. Response format:
+ // - OpRepDeviceListInfo - specifies number of device list reports that follow
+ // (n),
+ // - n * OpRepDeviceInfo - USBIP device descriptor, including # interfaces
+ // (m),
+ // - m * OpRepInterfaceInfo - for every OpRepDeviceInfo, list of interfaces.
+ kUsbIpOpRepDevList = 5,
+};
+
+// Header precedes all OPERATION requests and responses.
+// Header does NOT precede COMMAND requests and responses.
+struct OpHeader {
+ uint16_t version; // BCD. Server must obey client, not the other way around.
+ Operation command; // Request or response type.
+ uint32_t status; // Status; 0 = ok, 1 = error.
+};
+
+// OPERATION request/response body is essentially several structures glued
+// together. Because half of these messages are nested arrays of arbitrary
+// lengths each, we can't define a single structure explaining full request or
+// response body. Instead we will define components that, when combined,
+// constitute this body.
+//
+// OpReqRepBusId functions both as a device info field and request body.
+using OpReqRepBusId = char[32];
+
+// OpRepDeviceListInfo is a header preceding an array of OpRepDeviceInfo devices
+// offered by this server.
+struct OpRepDeviceListInfo {
+ uint32_t num_exported_devices;
+} __attribute__((packed));
+
+// OpRepDeviceInfo is used both as a partial response to OpReqDeviceList and
+// OpReqImport. Depending on operation type it may or may not be followed by an
+// array of OpRepInterfaceInfo interfaces this device exports.
+struct OpRepDeviceInfo {
+ char usb_path[256];
+ OpReqRepBusId bus_id;
+ uint32_t bus_num;
+ uint32_t dev_num;
+ uint32_t speed;
+ uint16_t id_vendor;
+ uint16_t id_product;
+ uint16_t bcd_device;
+ uint8_t device_class;
+ uint8_t device_subclass;
+ uint8_t device_protocol;
+ uint8_t configuration_value;
+ uint8_t num_configurations;
+ uint8_t num_interfaces;
+} __attribute__((packed));
+
+// OpRepInterfaceInfo lists interface details of a particular USB device.
+struct OpRepInterfaceInfo {
+ uint8_t iface_class;
+ uint8_t iface_subclass;
+ uint8_t iface_protocol;
+ uint8_t reserved;
+} __attribute__((packed));
+
+////////////////////////////////////////////////////////////////////////////////
+// COMMANDS
+////////////////////////////////////////////////////////////////////////////////
+
+// Command numbers. Commands are valid only once USB device is attached.
+enum Command : uint32_t {
+ kUsbIpCmdReqSubmit = 1, // Submit request
+ kUsbIpCmdReqUnlink = 2, // Unlink request
+ kUsbIpCmdRepSubmit = 3, // Submit response
+ kUsbIpCmdRepUnlink = 4, // Unlink response
+};
+
+// Direction of data flow.
+enum Direction : uint32_t {
+ kUsbIpDirectionOut = 0,
+ kUsbIpDirectionIn = 1,
+};
+
+// Setup structure is explained in great detail here:
+// - http://www.beyondlogic.org/usbnutshell/usb6.shtml
+// - http://www.usbmadesimple.co.uk/ums_4.htm
+struct CmdRequest {
+ uint8_t type;
+ uint8_t cmd;
+ uint16_t value;
+ uint16_t index;
+ uint16_t length;
+} __attribute__((packed));
+
+// CmdHeader precedes any command request or response body.
+struct CmdHeader {
+ Command command;
+ uint32_t seq_num;
+ uint16_t bus_num;
+ uint16_t dev_num;
+ Direction direction;
+ uint32_t endpoint; // valid values: 0-15
+} __attribute__((packed));
+
+// Command data for submitting an USB request.
+struct CmdReqSubmit {
+ uint32_t transfer_flags;
+ uint32_t transfer_buffer_length;
+ uint32_t start_frame;
+ uint32_t number_of_packets;
+ uint32_t deadline_interval;
+ CmdRequest setup;
+} __attribute__((packed));
+
+// Command response for submitting an USB request.
+struct CmdRepSubmit {
+ uint32_t status; // 0 = success.
+ uint32_t actual_length;
+ uint32_t start_frame;
+ uint32_t number_of_packets;
+ uint32_t error_count;
+ CmdRequest setup;
+} __attribute__((packed));
+
+// Unlink USB request.
+struct CmdReqUnlink {
+ uint32_t seq_num;
+ uint32_t reserved[6];
+} __attribute__((packed));
+
+// Unlink USB response.
+struct CmdRepUnlink {
+ uint32_t status;
+ uint32_t reserved[6];
+} __attribute__((packed));
+
+// Diagnostics.
+std::ostream& operator<<(std::ostream& out, Operation op);
+std::ostream& operator<<(std::ostream& out, const OpHeader& header);
+std::ostream& operator<<(std::ostream& out, const OpRepDeviceInfo& data);
+std::ostream& operator<<(std::ostream& out, const OpRepDeviceListInfo& list);
+std::ostream& operator<<(std::ostream& out, const OpRepInterfaceInfo& i);
+std::ostream& operator<<(std::ostream& out, const CmdHeader& header);
+std::ostream& operator<<(std::ostream& out, const CmdReqSubmit& data);
+std::ostream& operator<<(std::ostream& out, const CmdRepSubmit& data);
+std::ostream& operator<<(std::ostream& out, const CmdReqUnlink& data);
+std::ostream& operator<<(std::ostream& out, const CmdRepUnlink& data);
+
+} // namespace usbip
+} // namespace vadb
diff --git a/vadb/usbip/server.cpp b/vadb/usbip/server.cpp
new file mode 100644
index 0000000..fb83922
--- /dev/null
+++ b/vadb/usbip/server.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/vadb/usbip/server.h"
+
+#include <glog/logging.h>
+#include <netinet/in.h>
+
+#include "common/libs/fs/shared_select.h"
+
+using avd::SharedFD;
+
+namespace vadb {
+namespace usbip {
+namespace {
+// USB-IP server port. USBIP will attempt to connect to this server to attach
+// new virtual USB devices to host.
+static constexpr int kServerPort = 3240;
+} // namespace
+
+Server::Server(const DevicePool &devices) : device_pool_(devices) {}
+
+bool Server::Init() { return CreateServerSocket(); }
+
+// Open new listening server socket.
+// Returns false, if listening socket could not be created.
+bool Server::CreateServerSocket() {
+ LOG(INFO) << "Starting server socket on port " << kServerPort;
+ server_ = SharedFD::Socket(PF_INET6, SOCK_STREAM, 0);
+ if (!server_->IsOpen()) {
+ LOG(ERROR) << "Could not create socket: " << server_->StrError();
+ return false;
+ }
+
+ int n = 1;
+ if (server_->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1) {
+ LOG(ERROR) << "SetSockOpt failed " << server_->StrError();
+ return false;
+ }
+
+ struct sockaddr_in6 addr = {
+ AF_INET6, htons(kServerPort), 0, in6addr_loopback, 0,
+ };
+
+ if (server_->Bind((struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ LOG(ERROR) << "Could not bind socket: " << server_->StrError();
+ return false;
+ }
+
+ if (server_->Listen(1) == -1) {
+ LOG(ERROR) << "Could not start listening: " << server_->StrError();
+ return false;
+ }
+
+ return true;
+}
+
+// Serve incoming USB/IP connections.
+void Server::Serve() {
+ LOG(INFO) << "Serving USB/IP connections.";
+ while (true) {
+ avd::SharedFDSet fd_read;
+ fd_read.Set(server_);
+ for (const auto &client : clients_) fd_read.Set(client.fd());
+
+ int ret = avd::Select(&fd_read, nullptr, nullptr, nullptr);
+ if (ret <= 0) continue;
+
+ if (fd_read.IsSet(server_)) HandleIncomingConnection();
+
+ for (auto iter = clients_.begin(); iter != clients_.end();) {
+ if (fd_read.IsSet(iter->fd())) {
+ // If client conversation failed, hang up.
+ if (!iter->HandleIncomingMessage()) {
+ iter = clients_.erase(iter);
+ continue;
+ }
+ }
+ ++iter;
+ }
+ }
+ LOG(INFO) << "Server exiting.";
+}
+
+// Accept new USB/IP connection. Add it to client pool.
+void Server::HandleIncomingConnection() {
+ SharedFD client = SharedFD::Accept(*server_, nullptr, nullptr);
+ if (!client->IsOpen()) {
+ LOG(ERROR) << "Client connection failed: " << client->StrError();
+ return;
+ }
+
+ clients_.emplace_back(device_pool_, client);
+}
+} // namespace usbip
+} // namespace vadb
diff --git a/vadb/usbip/server.h b/vadb/usbip/server.h
new file mode 100644
index 0000000..2e8223f
--- /dev/null
+++ b/vadb/usbip/server.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <list>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/vadb/usbip/device_pool.h"
+#include "host/vadb/usbip/client.h"
+
+namespace vadb {
+namespace usbip {
+
+class Server final {
+ public:
+ Server(const DevicePool& device_pool);
+ ~Server() = default;
+
+ // Initialize this instance of Server.
+ // Returns true, if initialization was successful.
+ bool Init();
+
+ // Main server loop. Handles all incoming connections as well as client data
+ // exchange.
+ void Serve();
+
+ private:
+ // Create USBIP server socket.
+ // Returns true, if socket was successfully created.
+ bool CreateServerSocket();
+
+ // Handle new client connection.
+ // New clients will be appended to clients_ list.
+ void HandleIncomingConnection();
+
+ avd::SharedFD server_;
+ std::list<Client> clients_;
+ const DevicePool& device_pool_;
+
+ Server(const Server&) = delete;
+ Server& operator=(const Server&) = delete;
+};
+
+} // namespace usbip
+} // namespace vadb