summaryrefslogtreecommitdiff
path: root/guest/commands/usbforward/usb_server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'guest/commands/usbforward/usb_server.cpp')
-rw-r--r--guest/commands/usbforward/usb_server.cpp405
1 files changed, 0 insertions, 405 deletions
diff --git a/guest/commands/usbforward/usb_server.cpp b/guest/commands/usbforward/usb_server.cpp
deleted file mode 100644
index e8f55257..00000000
--- a/guest/commands/usbforward/usb_server.cpp
+++ /dev/null
@@ -1,405 +0,0 @@
-/*
- * 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.
- */
-// #undef NDEBUG
-
-#include "guest/commands/usbforward/usb_server.h"
-
-#include <string>
-#include <vector>
-#include <strings.h>
-#include <log/log.h>
-#include <libusb/libusb.h>
-#include "common/libs/fs/shared_select.h"
-#include "common/libs/usbforward/protocol.h"
-#include "guest/commands/usbforward/transport_request.h"
-
-namespace usb_forward {
-namespace {
-// USBServer exports device kExportedVendorID:kExportedProductID to the server.
-// We will not support exporting multiple USB devices as there's no practical
-// need for this.
-constexpr uint16_t kExportedVendorID = 0x18d1;
-constexpr uint16_t kExportedProductID = 0x4ee7;
-
-// Use default BUS and DEVICE IDs so that it's easier to attach over USB/IP.
-constexpr uint8_t kDefaultBusID = 1;
-constexpr uint8_t kDefaultDevID = 1;
-
-std::shared_ptr<libusb_device_handle> GetDevice() {
- std::shared_ptr<libusb_device_handle> res(
- libusb_open_device_with_vid_pid(nullptr, kExportedVendorID,
- kExportedProductID),
- [](libusb_device_handle* h) {
- // Apparently, deleter is called even on an uninitialized shared_ptr.
- if (h != nullptr) {
- libusb_release_interface(h, 0);
- libusb_close(h);
- }
- });
-
- if (res) libusb_claim_interface(res.get(), 0);
-
- return res;
-}
-
-} // anonymous namespace
-
-bool USBServer::GetDeviceInfo(
- DeviceInfo* info, std::vector<InterfaceInfo>* ifaces) {
- if (!handle_) return false;
-
- // This function does not modify the reference count of the returned device,
- // so do not feel compelled to unreference it when you are done.
- libusb_device* dev = libusb_get_device(handle_.get());
-
- 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 false;
- }
-
- 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 false;
- }
-
- 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 = kDefaultBusID;
- info->dev_id = kDefaultDevID;
-
- 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);
- return true;
-}
-
-USBServer::USBServer(const cvd::SharedFD& fd)
- : fd_{fd},
- device_event_fd_{cvd::SharedFD::Event(0, 0)},
- thread_event_fd_{cvd::SharedFD::Event(0, 0)} {}
-
-void USBServer::HandleDeviceList(uint32_t tag) {
- // Iterate all devices and send structure for every found device.
- // Write header: number of devices.
- DeviceInfo info;
- std::vector<InterfaceInfo> ifaces;
- bool found = GetDeviceInfo(&info, &ifaces);
-
- cvd::LockGuard<cvd::Mutex> lock(write_mutex_);
- ResponseHeader rsp{StatusSuccess, tag};
- fd_->Write(&rsp, sizeof(rsp));
- if (found) {
- uint32_t cnt = 1;
- fd_->Write(&cnt, sizeof(cnt));
- fd_->Write(&info, sizeof(info));
- fd_->Write(ifaces.data(), ifaces.size() * sizeof(InterfaceInfo));
- } else {
- // No devices.
- uint32_t cnt = 0;
- fd_->Write(&cnt, sizeof(cnt));
- }
-}
-
-void USBServer::HandleAttach(uint32_t tag) {
- // We read the request, but it no longer plays any significant role here.
- AttachRequest req;
- if (fd_->Read(&req, sizeof(req)) != sizeof(req)) return;
-
- cvd::LockGuard<cvd::Mutex> lock(write_mutex_);
- ResponseHeader rsp{handle_ ? StatusSuccess : StatusFailure, tag};
- fd_->Write(&rsp, sizeof(rsp));
-}
-
-void USBServer::HandleHeartbeat(uint32_t tag) {
- cvd::LockGuard<cvd::Mutex> lock(write_mutex_);
- ResponseHeader rsp{handle_ ? StatusSuccess : StatusFailure, tag};
- fd_->Write(&rsp, sizeof(rsp));
-}
-
-void USBServer::HandleControlTransfer(uint32_t tag) {
- ControlTransfer req;
- // If disconnected prematurely, don't send response.
- if (fd_->Read(&req, sizeof(req)) != sizeof(req)) return;
-
- // Technically speaking this isn't endpoint, but names, masks, values and
- // meaning here is exactly same.
- bool is_data_in =
- ((req.type & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN);
-
- std::unique_ptr<TransportRequest> treq(new TransportRequest(
- handle_,
- [this, is_data_in, tag](bool is_success, const uint8_t* data,
- int32_t length) {
- OnTransferComplete(tag, is_data_in, is_success, data, length);
- },
- req));
-
- if (!is_data_in && req.length) {
- // If disconnected prematurely, don't send response.
- int32_t got = 0;
- while (got < req.length) {
- auto read = fd_->Read(&treq->Buffer()[got], req.length - got);
- if (fd_->GetErrno() != 0) {
- ALOGE("Failed to read from client: %s", fd_->StrError());
- return;
- } else if (read == 0) {
- ALOGE("Failed to read from client: short read");
- return;
- }
- got += read;
- }
- }
-
- // At this point we store transport request internally until it completes.
- TransportRequest* treq_ptr = treq.get();
- {
- cvd::LockGuard<cvd::Mutex> lock(requests_mutex_);
- requests_in_flight_[tag] = std::move(treq);
- }
-
- if (!treq_ptr->Submit()) {
- OnTransferComplete(tag, is_data_in, false, nullptr, 0);
- }
-}
-
-void USBServer::HandleDataTransfer(uint32_t tag) {
- DataTransfer req;
- // If disconnected prematurely, don't send response.
- if (fd_->Read(&req, sizeof(req)) != sizeof(req)) return;
-
- bool is_data_in = !req.is_host_to_device;
-
- std::unique_ptr<TransportRequest> treq(new TransportRequest(
- handle_,
- [this, is_data_in, tag](bool is_success, const uint8_t* data,
- int32_t length) {
- OnTransferComplete(tag, is_data_in, is_success, data, length);
- },
- req));
-
- if (!is_data_in && req.length) {
- // If disconnected prematurely, don't send response.
- int32_t got = 0;
- while (got < req.length) {
- auto read = fd_->Read(&treq->Buffer()[got], req.length - got);
- if (fd_->GetErrno() != 0) {
- ALOGE("Failed to read from client: %s", fd_->StrError());
- return;
- } else if (read == 0) {
- ALOGE("Failed to read from client: short read");
- return;
- }
- got += read;
- }
- }
-
- // At this point we store transport request internally until it completes.
- TransportRequest* treq_ptr = treq.get();
- {
- cvd::LockGuard<cvd::Mutex> lock(requests_mutex_);
- requests_in_flight_[tag] = std::move(treq);
- }
-
- if (!treq_ptr->Submit()) {
- OnTransferComplete(tag, is_data_in, false, nullptr, 0);
- }
-}
-
-void USBServer::OnTransferComplete(uint32_t tag, bool is_data_in,
- bool is_success, const uint8_t* buffer,
- int32_t actual_length) {
- ResponseHeader rsp{is_success ? StatusSuccess : StatusFailure, tag};
-
- cvd::LockGuard<cvd::Mutex> lock(write_mutex_);
- fd_->Write(&rsp, sizeof(rsp));
- if (is_success && is_data_in) {
- fd_->Write(&actual_length, sizeof(actual_length));
- if (actual_length > 0) {
- // NOTE: don't use buffer_ here directly, as libusb uses first few bytes
- // to store control data there.
- int32_t sent = 0;
- while (sent < actual_length) {
- int packet_size = fd_->Write(&buffer[sent], actual_length - sent);
- sent += packet_size;
- ALOGV("Sending response, %d / %d bytes sent", sent, actual_length);
- if (fd_->GetErrno() != 0) {
- ALOGE("Send failed: %s", fd_->StrError());
- return;
- }
- }
- }
- }
-
- {
- cvd::LockGuard<cvd::Mutex> lock(requests_mutex_);
- requests_in_flight_.erase(tag);
- }
-}
-
-int USBServer::HandleDeviceEvent(libusb_context*, libusb_device*,
- libusb_hotplug_event, void* self_raw) {
- auto self = reinterpret_cast<USBServer*>(self_raw);
- int64_t dummy = 1;
- self->device_event_fd_->Write(&dummy, sizeof(dummy));
- return 0;
-}
-
-void* USBServer::ProcessLibUSBRequests(void* self_raw) {
- USBServer* self = reinterpret_cast<USBServer*>(self_raw);
- ALOGI("Starting hotplug thread.");
-
- cvd::SharedFDSet rset;
- while (true) {
- // Do not wait if there's no event.
- timeval select_timeout{0, 0};
- rset.Zero();
- rset.Set(self->thread_event_fd_);
- int ret = cvd::Select(&rset, nullptr, nullptr, &select_timeout);
- if (ret > 0) break;
-
- timeval libusb_timeout{1, 0};
- libusb_handle_events_timeout_completed(nullptr, &libusb_timeout, nullptr);
- }
-
- int64_t dummy;
- self->thread_event_fd_->Read(&dummy, sizeof(dummy));
- ALOGI("Shutting down hotplug thread.");
- return nullptr;
-}
-
-void USBServer::InitLibUSB() {
- if (libusb_init(nullptr) != 0) return;
- libusb_hotplug_register_callback(
- nullptr,
- libusb_hotplug_event(LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
- libusb_hotplug_flag(0), kExportedVendorID, kExportedProductID,
- LIBUSB_HOTPLUG_MATCH_ANY, &USBServer::HandleDeviceEvent, this,
- &hotplug_handle_);
- handle_ = GetDevice();
- libusb_thread_.reset(new cvd::ScopedThread(&ProcessLibUSBRequests, this));
-}
-
-void USBServer::ExitLibUSB() {
- if (!libusb_thread_) return;
- libusb_hotplug_deregister_callback(nullptr, hotplug_handle_);
- int64_t dummy = 1;
- thread_event_fd_->Write(&dummy, sizeof(dummy));
- libusb_thread_.reset();
- handle_.reset();
- libusb_exit(nullptr);
-}
-
-void USBServer::Serve() {
- cvd::SharedFDSet rset;
- while (true) {
- timeval retry_timeout{1, 0};
- timeval* select_timeout = nullptr;
- if (!handle_) {
- select_timeout = &retry_timeout;
- }
-
- rset.Zero();
- rset.Set(fd_);
- rset.Set(device_event_fd_);
- int ret = cvd::Select(&rset, nullptr, nullptr, select_timeout);
-
- // device_event_fd_ is reset each time libusb notices device has re-appeared
- // or is gone. In both cases, the existing handle is no longer valid.
- if (rset.IsSet(device_event_fd_)) {
- int64_t dummy;
- device_event_fd_->Read(&dummy, sizeof(dummy));
- handle_.reset();
- }
-
- if (!handle_) {
- ExitLibUSB();
- InitLibUSB();
- if (handle_) {
- ALOGI("Device present.");
- }
- }
-
- if (ret < 0) continue;
-
- if (rset.IsSet(fd_)) {
- RequestHeader req;
- if (fd_->Read(&req, sizeof(req)) != sizeof(req)) {
- // There's nobody on the other side.
- sleep(3);
- continue;
- }
-
- switch (req.command) {
- case CmdDeviceList:
- ALOGV("Processing DeviceList command, tag=%d", req.tag);
- HandleDeviceList(req.tag);
- break;
-
- case CmdAttach:
- ALOGV("Processing Attach command, tag=%d", req.tag);
- HandleAttach(req.tag);
- break;
-
- case CmdControlTransfer:
- ALOGV("Processing ControlTransfer command, tag=%d", req.tag);
- HandleControlTransfer(req.tag);
- break;
-
- case CmdDataTransfer:
- ALOGV("Processing DataTransfer command, tag=%d", req.tag);
- HandleDataTransfer(req.tag);
- break;
-
- case CmdHeartbeat:
- ALOGV("Processing Heartbeat command, tag=%d", req.tag);
- HandleHeartbeat(req.tag);
- break;
-
- default:
- ALOGE("Discarding unknown command %08x, tag=%d", req.command,
- req.tag);
- }
- }
- }
-}
-
-} // namespace usb_forward