USB Forwarding utility

This utility will provide USB forwarding while we work on VSoC implementation

BUG=64121751
Change-Id: I17159053ea971a6e1f69cd6af8e8e280b8337447
diff --git a/guest/usbforward/Android.mk b/guest/usbforward/Android.mk
new file mode 100644
index 0000000..77b14ed
--- /dev/null
+++ b/guest/usbforward/Android.mk
@@ -0,0 +1,37 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_MODULE := usbforward
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := \
+    main.cpp \
+    usb_server.cpp
+
+LOCAL_C_INCLUDES := \
+    device/google/cuttlefish_common \
+    device/google/cuttlefish_kernel
+
+LOCAL_SHARED_LIBRARIES := \
+    libcuttlefish_auto_resources \
+    libcuttlefish_fs \
+    libusb \
+    libbase \
+    liblog
+
+LOCAL_MULTILIB := first
+LOCAL_VENDOR_MODULE := true
+include $(BUILD_EXECUTABLE)
+
diff --git a/guest/usbforward/BUILD b/guest/usbforward/BUILD
new file mode 100644
index 0000000..91d968b
--- /dev/null
+++ b/guest/usbforward/BUILD
@@ -0,0 +1,7 @@
+cc_library(
+    name = "protocol",
+    hdrs = [
+        "protocol.h"
+    ],
+    visibility = ["//visibility:public"],
+)
\ No newline at end of file
diff --git a/guest/usbforward/main.cpp b/guest/usbforward/main.cpp
new file mode 100644
index 0000000..3f05812
--- /dev/null
+++ b/guest/usbforward/main.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "UsbForward"
+
+#include <cutils/log.h>
+#include <stdio.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "guest/usbforward/usb_server.h"
+
+int main(int argc, char* argv[]) {
+  if (argc == 1) {
+    printf("Usage: %s <virtio_channel>\n", argv[0]);
+    return 1;
+  }
+
+  avd::SharedFD fd =
+      avd::SharedFD::Open(argv[1], O_RDWR | O_NOCTTY);
+  if (!fd->IsOpen()) {
+    ALOGE("Could not open %s: %s", argv[1], fd->StrError());
+    return 1;
+  }
+
+  USBServer server(fd);
+  if (server.Init()) {
+    server.Serve();
+  }
+  ALOGE("Terminated.");
+  return 1;
+}
diff --git a/guest/usbforward/protocol.h b/guest/usbforward/protocol.h
new file mode 100644
index 0000000..68c514e
--- /dev/null
+++ b/guest/usbforward/protocol.h
@@ -0,0 +1,93 @@
+/*
+ * 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 <stdint.h>
+
+// Commands that can be executed over serial port.
+// Use magic value to avoid accidental interpretation of commonly seen numbers.
+enum : uint32_t {
+  // Get device list.
+  // Response format:
+  // - int32_t(num_devices)
+  // - num_devices times:
+  //   - DeviceInfo{}
+  //   - DeviceInfo.num_interfaces times:
+  //     - InterfaceInfo{}
+  CmdDeviceList = 0xcfad0001,
+
+  // Attach specified device.
+  // Request format:
+  // - AttachRequest{}
+  // Response format:
+  // - status (0 = success).
+  CmdAttach,
+
+  // Execute command on attached USB device.
+  // Request format:
+  // - ExecuteRequest{}
+  // - if transfer direction is host -> device
+  //   - uint8_t[ExecuteRequest.length]
+  // Response format:
+  // - int32_t(status)
+  // - if transfer direction is device -> host
+  //   - int32_t(actual length)
+  //   - uint8_t[actual length] bytes
+  CmdExecute
+};
+
+// DeviceInfo describes individual USB device that was found attached to the
+// bus.
+struct DeviceInfo {
+  uint16_t vendor_id;
+  uint16_t product_id;
+  uint16_t dev_version;
+  uint8_t dev_class;
+  uint8_t dev_subclass;
+  uint8_t dev_protocol;
+  uint8_t bus_id;
+  uint8_t dev_id;
+  uint8_t speed;
+  uint8_t num_configurations;
+  uint8_t num_interfaces;
+  uint8_t cur_configuration;
+} __attribute__((packed));
+
+// InterfaceInfo describes individual interface attached to a USB device.
+struct InterfaceInfo {
+  uint8_t if_class;
+  uint8_t if_subclass;
+  uint8_t if_protocol;
+  uint8_t if_reserved;
+} __attribute__((packed));
+
+// AttachRequest specifies which device on which bus needs to be attached.
+struct AttachRequest {
+  uint8_t bus_id;
+  uint8_t dev_id;
+} __attribute__((packed));
+
+// ExecuteRequest specifies target bus and device along with USB request.
+struct ExecuteRequest {
+  uint8_t bus_id;
+  uint8_t dev_id;
+  uint8_t type;
+  uint8_t cmd;
+  uint16_t value;
+  uint16_t index;
+  uint16_t length;
+  uint32_t timeout;
+} __attribute__((packed));
diff --git a/guest/usbforward/usb_server.cpp b/guest/usbforward/usb_server.cpp
new file mode 100644
index 0000000..50af2f7
--- /dev/null
+++ b/guest/usbforward/usb_server.cpp
@@ -0,0 +1,240 @@
+/*
+ * 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 "guest/usbforward/usb_server.h"
+
+#include <string>
+#include <vector>
+#include <strings.h>
+#include <cutils/log.h>
+#include <libusb/libusb.h>
+#include "common/libs/fs/shared_select.h"
+#include "guest/usbforward/protocol.h"
+
+namespace {
+void GetDeviceInfo(libusb_device* dev, DeviceInfo* info,
+                   std::vector<InterfaceInfo>* ifaces) {
+  libusb_device_descriptor desc;
+  libusb_config_descriptor* conf;
+
+  memset(info, 0, sizeof(*info));
+
+  int res = libusb_get_device_descriptor(dev, &desc);
+  if (res < 0) {
+    // This shouldn't really happen.
+    ALOGE("libusb_get_device_descriptor failed %d", res);
+    return;
+  }
+
+  res = libusb_get_active_config_descriptor(dev, &conf);
+  if (res < 0) {
+    // This shouldn't really happen.
+    ALOGE("libusb_get_active_config_descriptor failed %d", res);
+    libusb_free_config_descriptor(conf);
+    return;
+  }
+
+  info->vendor_id = desc.idVendor;
+  info->product_id = desc.idProduct;
+  info->dev_version = desc.bcdDevice;
+  info->dev_class = desc.bDeviceClass;
+  info->dev_subclass = desc.bDeviceSubClass;
+  info->dev_protocol = desc.bDeviceProtocol;
+  info->speed = libusb_get_device_speed(dev);
+  info->num_configurations = desc.bNumConfigurations;
+  info->num_interfaces = conf->bNumInterfaces;
+  info->cur_configuration = conf->bConfigurationValue;
+  info->bus_id = libusb_get_bus_number(dev);
+  info->dev_id = libusb_get_device_address(dev);
+
+  if (ifaces != nullptr) {
+    for (int ifidx = 0; ifidx < conf->bNumInterfaces; ++ifidx) {
+      const libusb_interface& iface = conf->interface[ifidx];
+      for (int altidx = 0; altidx < iface.num_altsetting; ++altidx) {
+        const libusb_interface_descriptor& alt = iface.altsetting[altidx];
+        ifaces->push_back(InterfaceInfo{alt.bInterfaceClass,
+                                        alt.bInterfaceSubClass,
+                                        alt.bInterfaceProtocol, 0});
+      }
+    }
+  }
+  libusb_free_config_descriptor(conf);
+}
+
+uint16_t MakeDeviceKey(uint8_t bus_id, uint8_t dev_id) {
+  return bus_id << 8 | dev_id;
+}
+}  // anonymous namespace
+
+USBServer::~USBServer() {
+  for (const auto& dev : attached_devices_) {
+    libusb_close(dev.second);
+  }
+
+  for (const auto& dev : devices_) {
+    libusb_unref_device(dev.second);
+  }
+}
+
+bool USBServer::Init() {
+  auto res = libusb_init(nullptr);
+  if (res < 0) {
+    // res is LIBUSB_ERROR in this context.
+    ALOGE("libusb_init failed %d", res);
+    return false;
+  }
+
+  libusb_device** devices;
+  int32_t cnt = libusb_get_device_list(nullptr, &devices);
+  if (cnt <= 0) {
+    ALOGE("libusb_get_device_list failed %d", cnt);
+    return false;
+  }
+
+  for (int index = 0; index < cnt; index++) {
+    auto key = MakeDeviceKey(libusb_get_bus_number(devices[index]),
+                             libusb_get_device_address(devices[index]));
+
+    libusb_ref_device(devices[index]);
+    devices_[key] = devices[index];
+  }
+
+  return true;
+}
+
+void USBServer::HandleDeviceList() {
+  // Iterate all devices and send structure for every found device.
+  DeviceInfo info;
+  int32_t size = devices_.size();
+
+  // Write header: number of devices.
+  fd_->Write(&size, sizeof(size));
+
+  for (const auto& dev : devices_) {
+    std::vector<InterfaceInfo> ifaces;
+    GetDeviceInfo(dev.second, &info, &ifaces);
+    fd_->Write(&info, sizeof(info));
+    fd_->Write(ifaces.data(), ifaces.size() * sizeof(InterfaceInfo));
+  }
+}
+
+void USBServer::HandleAttach() {
+  AttachRequest req;
+  // If disconnected prematurely, don't send response.
+  if (fd_->Read(&req, sizeof(req)) != sizeof(req)) return;
+  // To simplify our lives, let's use status similar to USB/IP.
+  int32_t status = 1;
+
+  // Force nul-terminate path.
+  const auto key = MakeDeviceKey(req.bus_id, req.dev_id);
+  auto iter = devices_.find(key);
+  if (iter == devices_.end()) {
+    ALOGE("No device found for %x-%x", req.bus_id, req.dev_id);
+    fd_->Write(&status, sizeof(status));
+    return;
+  }
+
+  libusb_device_handle* handle;
+  auto res = libusb_open(iter->second, &handle);
+  if (res < 0) {
+    ALOGE("libusb_open failed %d", res);
+    fd_->Write(&status, sizeof(status));
+    return;
+  }
+
+  if (handle) attached_devices_[key] = handle;
+  // Indicate failure if we don't have the handle.
+  status = (handle == nullptr);
+  fd_->Write(&status, sizeof(status));
+}
+
+void USBServer::HandleExecute() {
+  ExecuteRequest req;
+  // If disconnected prematurely, don't send response.
+  if (fd_->Read(&req, sizeof(req)) != sizeof(req)) return;
+
+  std::vector<uint8_t> data;
+  data.resize(req.length);
+
+  bool req_out = ((req.type & 0x80) == 0);
+
+  if (req_out && req.length) {
+    // If disconnected prematurely, don't send response.
+    if (fd_->Read(data.data(), req.length) != req.length) return;
+  }
+
+  int32_t status = 1;
+  int32_t len = 0;
+  auto handle_iter =
+      attached_devices_.find(MakeDeviceKey(req.bus_id, req.dev_id));
+
+  if (handle_iter != attached_devices_.end()) {
+    // Now that we read the whole request we are good to check if device's
+    len = libusb_control_transfer(handle_iter->second, req.type, req.cmd,
+                                  req.value, req.index, data.data(), req.length,
+                                  req.timeout);
+
+    status = (len < 0);
+    if (status) {
+      ALOGE("USB request failed %d", len);
+    }
+  }
+
+  fd_->Write(&status, sizeof(status));
+
+  if (!status && !req_out) {
+    fd_->Write(&len, sizeof(len));
+    if (len > 0) {
+      fd_->Write(data.data(), len);
+    }
+  }
+}
+
+void USBServer::Serve() {
+  avd::SharedFDSet rset;
+  while (true) {
+    rset.Zero();
+    rset.Set(fd_);
+    int ret = avd::Select(&rset, nullptr, nullptr, nullptr);
+
+    if (ret < 0) continue;
+
+    if (rset.IsSet(fd_)) {
+      uint32_t cmd;
+      char data;
+      if (fd_->Read(&cmd, sizeof(cmd)) < int(sizeof(cmd))) {
+        ALOGE("Could not read data from input stream: %s", fd_->StrError());
+        continue;
+      }
+
+      switch (cmd) {
+        case CmdDeviceList:
+          HandleDeviceList();
+          break;
+
+        case CmdAttach:
+          HandleAttach();
+
+        case CmdExecute:
+          HandleExecute();
+          break;
+
+        default:
+          ALOGE("Discarding unknown command %08x", cmd);
+      }
+    }
+  }
+}
diff --git a/guest/usbforward/usb_server.h b/guest/usbforward/usb_server.h
new file mode 100644
index 0000000..030613b
--- /dev/null
+++ b/guest/usbforward/usb_server.h
@@ -0,0 +1,60 @@
+/*
+ * 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
+
+#define LOG_TAG "UsbForward"
+
+#include <map>
+#include <string>
+#include <libusb/libusb.h>
+
+#include "common/libs/fs/shared_fd.h"
+
+// USBServer exposes access to USB devices over pipe (virtio channel etc).
+// Usage:
+//
+//     avd::SharedFD pipe = avd::SharedFD::Open(pipe_path, O_RDWR);
+//     USBServer server(pipe);
+//     CHECK(server.Init());
+//     server.Serve();
+class USBServer final {
+ public:
+  USBServer(const avd::SharedFD& fd) : fd_{fd} {}
+  ~USBServer();
+
+  // Populate list of USB devices.
+  bool Init();
+
+  // Serve incoming USB requests.
+  void Serve();
+
+ private:
+  // Handle CmdDeviceList request.
+  void HandleDeviceList();
+
+  // Handle CmdAttach request.
+  void HandleAttach();
+
+  // Handle CmdExecute request.
+  void HandleExecute();
+
+  avd::SharedFD fd_;
+  std::map<uint16_t, libusb_device*> devices_;
+  std::map<uint16_t, libusb_device_handle*> attached_devices_;
+
+  USBServer(const USBServer& other) = delete;
+  USBServer& operator=(const USBServer& other) = delete;
+};