diff options
Diffstat (limited to 'guest/commands/usbforward/usb_server.cpp')
-rw-r--r-- | guest/commands/usbforward/usb_server.cpp | 405 |
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 |