1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <errno.h> 18 #include <string.h> 19 20 #include <arpa/inet.h> 21 #include <netdb.h> 22 #include <sys/socket.h> 23 24 #include <glog/logging.h> 25 #include <fstream> 26 #include <limits> 27 #include <sstream> 28 #include "common/libs/fs/shared_select.h" 29 30 #include "common/libs/fs/shared_fd.h" 31 #include "host/commands/virtual_usb_manager/usbip/vhci_instrument.h" 32 33 namespace vadb { 34 namespace usbip { 35 namespace { 36 // Device ID is specified as a concatenated pair of BUS and DEVICE id. 37 // Since we only export one device and our server doesn't care much about 38 // its number, we use the default value of BUS=1 and DEVICE=1. 39 // This can be set to something else and should still work, as long as 40 // numbers are valid in USB sense. 41 constexpr uint32_t kDefaultDeviceID = (1 << 16) | 1; 42 43 // Request Highspeed configuration. Superspeed isn't supported by vhci. 44 // Supported configurations are: 45 // 4 -> wireless 46 // 3 -> highspeed 47 // 2 -> full speed 48 // 1 -> low speed 49 // Please refer to the Kernel source tree in the following locations: 50 // include/uapi/linux/usb/ch9.h 51 // drivers/usb/usbip/vhci_sysfs.c 52 constexpr uint32_t kDefaultDeviceSpeed = 3; 53 54 // Subsystem and device type where VHCI driver is located. 55 const char* const kVHCIPlatformPaths[] = { 56 "/sys/devices/platform/vhci_hcd", 57 "/sys/devices/platform/vhci_hcd.1", 58 }; 59 60 // Control messages. 61 // Attach tells thread to attach remote device. 62 // Detach tells thread to detach remote device. 63 using ControlMsgType = uint8_t; 64 constexpr ControlMsgType kControlAttach = 'A'; 65 constexpr ControlMsgType kControlDetach = 'D'; 66 constexpr ControlMsgType kControlExit = 'E'; 67 68 // Used with EPOLL as epoll_data to determine event type. 69 enum EpollEventType { 70 kControlEvent, 71 kVHCIEvent, 72 }; 73 74 } // anonymous namespace 75 76 VHCIInstrument::VHCIInstrument(int port, const std::string& name) 77 : name_(name), port_{port} {} 78 79 VHCIInstrument::~VHCIInstrument() { 80 control_write_end_->Write(&kControlExit, sizeof(kControlExit)); 81 attach_thread_.join(); 82 } 83 84 bool VHCIInstrument::Init() { 85 cvd::SharedFD::Pipe(&control_read_end_, &control_write_end_); 86 87 struct stat buf; 88 for (const auto* path : kVHCIPlatformPaths) { 89 if (stat(path, &buf) == 0) { 90 syspath_ = path; 91 break; 92 } 93 } 94 95 if (syspath_.empty()) { 96 LOG(ERROR) << "VHCI not available. Is the driver loaded?"; 97 LOG(ERROR) << "Try: sudo modprobe vhci_hcd"; 98 LOG(ERROR) << "The driver is part of linux-image-extra-`uname -r` package"; 99 return false; 100 } 101 102 if (!VerifyPortIsFree()) { 103 LOG(ERROR) << "Trying to use VHCI port " << port_ << " but it is already in" 104 << " use."; 105 return false; 106 } 107 108 LOG(INFO) << "Using VHCI port " << port_; 109 attach_thread_ = std::thread([this] { AttachThread(); }); 110 return true; 111 } 112 113 bool VHCIInstrument::VerifyPortIsFree() const { 114 std::ifstream status_file(syspath_ + "/status"); 115 116 if (!status_file.good()) { 117 LOG(ERROR) << "Could not open usb-ip status file."; 118 return false; 119 } 120 121 // Skip past the header line. 122 status_file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 123 124 while (true) { 125 // Port status values deducted from /sys/devices/platform/vhci_hcd/status 126 // kVHCIPortFree indicates the port is not currently in use. 127 constexpr static int kVHCIStatusPortFree = 4; 128 129 int port{}; 130 int status{}; 131 status_file >> port >> status; 132 if (!status_file.good()) { 133 break; 134 } 135 136 status_file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 137 if (port_ == port) { 138 return status == kVHCIStatusPortFree; 139 } 140 } 141 LOG(ERROR) << "Couldn't find status for VHCI port " << port_; 142 return false; 143 } 144 145 void VHCIInstrument::TriggerAttach() { 146 control_write_end_->Write(&kControlAttach, sizeof(kControlAttach)); 147 } 148 149 void VHCIInstrument::TriggerDetach() { 150 control_write_end_->Write(&kControlDetach, sizeof(kControlDetach)); 151 } 152 153 void VHCIInstrument::AttachThread() { 154 cvd::SharedFD epoll = cvd::SharedFD::Epoll(); 155 // Trigger attach upon start. 156 bool want_attach = true; 157 // Operation is pending on read. 158 bool is_pending = false; 159 160 epoll_event control_event; 161 control_event.events = EPOLLIN; 162 control_event.data.u64 = kControlEvent; 163 epoll_event vhci_event; 164 vhci_event.events = EPOLLRDHUP | EPOLLONESHOT; 165 vhci_event.data.u64 = kVHCIEvent; 166 167 epoll->EpollCtl(EPOLL_CTL_ADD, control_read_end_, &control_event); 168 while (true) { 169 if (vhci_socket_->IsOpen()) { 170 epoll->EpollCtl(EPOLL_CTL_ADD, vhci_socket_, &vhci_event); 171 } 172 173 epoll_event found_event{}; 174 ControlMsgType request_type; 175 176 if (epoll->EpollWait(&found_event, 1, 1000)) { 177 switch (found_event.data.u64) { 178 case kControlEvent: 179 control_read_end_->Read(&request_type, sizeof(request_type)); 180 is_pending = true; 181 want_attach = request_type == kControlAttach; 182 LOG(INFO) << (want_attach ? "Attach" : "Detach") << " triggered."; 183 break; 184 case kVHCIEvent: 185 vhci_socket_ = cvd::SharedFD(); 186 // Only re-establish VHCI if it was already established before. 187 is_pending = want_attach; 188 // Do not immediately fall into attach cycle. It will likely complete 189 // before VHCI finishes deregistering this callback. 190 continue; 191 } 192 } 193 194 // Make an attempt to re-attach. If successful, clear pending attach flag. 195 if (is_pending) { 196 if (want_attach && Attach()) { 197 is_pending = false; 198 } else if (!want_attach && Detach()) { 199 is_pending = false; 200 } else { 201 LOG(INFO) << (want_attach ? "Attach" : "Detach") << " unsuccessful. " 202 << "Will re-try."; 203 sleep(1); 204 } 205 } 206 } 207 } 208 209 bool VHCIInstrument::Detach() { 210 std::stringstream result; 211 result << port_; 212 std::ofstream detach(syspath_ + "/detach"); 213 214 if (!detach.is_open()) { 215 LOG(WARNING) << "Could not open VHCI detach file."; 216 return false; 217 } 218 detach << result.str(); 219 return detach.rdstate() == std::ios_base::goodbit; 220 } 221 222 bool VHCIInstrument::Attach() { 223 if (!vhci_socket_->IsOpen()) { 224 vhci_socket_ = 225 cvd::SharedFD::SocketLocalClient(name_.c_str(), true, SOCK_STREAM); 226 if (!vhci_socket_->IsOpen()) return false; 227 } 228 229 int sys_fd = vhci_socket_->UNMANAGED_Dup(); 230 bool success = false; 231 232 { 233 std::stringstream result; 234 result << port_ << ' ' << sys_fd << ' ' << kDefaultDeviceID << ' ' 235 << kDefaultDeviceSpeed; 236 std::string path = syspath_ + "/attach"; 237 std::ofstream attach(path); 238 239 if (!attach.is_open()) { 240 LOG(WARNING) << "Could not open VHCI attach file " << path << " (" 241 << strerror(errno) << ")"; 242 close(sys_fd); 243 return false; 244 } 245 attach << result.str(); 246 247 // It is unclear whether duplicate FD should remain open or not. There are 248 // cases supporting both assumptions, likely related to kernel version. 249 // Kernel 4.10 is having problems communicating with USB/IP server if the 250 // socket is closed after it's passed to kernel. It is a clear indication 251 // that the kernel requires the socket to be kept open. 252 success = attach.rdstate() == std::ios_base::goodbit; 253 // Make sure everything was written and flushed. This happens when we close 254 // the ofstream attach. 255 } 256 257 close(sys_fd); 258 return success; 259 } 260 261 } // namespace usbip 262 } // namespace vadb 263