blob: 9df09adbdb972e18bae85cbbdefeb1dad2f97388 [file] [log] [blame]
Greg Hartman92045f62017-10-31 17:12:24 -07001/*
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>
Ryan Hainingdd0dcfe2018-05-16 13:55:20 -070026#include <limits>
Greg Hartman92045f62017-10-31 17:12:24 -070027#include <sstream>
28#include "common/libs/fs/shared_select.h"
29
30#include "common/libs/fs/shared_fd.h"
Jorge E. Moreirad8430662018-07-22 23:52:33 -070031#include "host/commands/virtual_usb_manager/usbip/vhci_instrument.h"
Greg Hartman92045f62017-10-31 17:12:24 -070032
33namespace vadb {
34namespace usbip {
35namespace {
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.
41constexpr 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
52constexpr uint32_t kDefaultDeviceSpeed = 3;
53
54// Subsystem and device type where VHCI driver is located.
Tomasz Wiszkowskie18ee642017-10-31 12:53:42 -070055const char* const kVHCIPlatformPaths[] = {
Ryan Hainingdd0dcfe2018-05-16 13:55:20 -070056 "/sys/devices/platform/vhci_hcd",
57 "/sys/devices/platform/vhci_hcd.1",
Tomasz Wiszkowskie18ee642017-10-31 12:53:42 -070058};
Greg Hartman92045f62017-10-31 17:12:24 -070059
60// Control messages.
61// Attach tells thread to attach remote device.
62// Detach tells thread to detach remote device.
63using ControlMsgType = uint8_t;
64constexpr ControlMsgType kControlAttach = 'A';
65constexpr ControlMsgType kControlDetach = 'D';
66constexpr ControlMsgType kControlExit = 'E';
67
68// Used with EPOLL as epoll_data to determine event type.
69enum EpollEventType {
70 kControlEvent,
71 kVHCIEvent,
72};
73
Greg Hartman92045f62017-10-31 17:12:24 -070074} // anonymous namespace
75
Ryan Hainingdd0dcfe2018-05-16 13:55:20 -070076VHCIInstrument::VHCIInstrument(int port, const std::string& name)
77 : name_(name), port_{port} {}
Greg Hartman92045f62017-10-31 17:12:24 -070078
79VHCIInstrument::~VHCIInstrument() {
80 control_write_end_->Write(&kControlExit, sizeof(kControlExit));
Ryan Hainingb8afc412018-04-07 09:49:28 -070081 attach_thread_.join();
Greg Hartman92045f62017-10-31 17:12:24 -070082}
83
84bool VHCIInstrument::Init() {
Greg Hartman153b1062017-11-11 12:09:21 -080085 cvd::SharedFD::Pipe(&control_read_end_, &control_write_end_);
Greg Hartman92045f62017-10-31 17:12:24 -070086
Tomasz Wiszkowskie18ee642017-10-31 12:53:42 -070087 struct stat buf;
88 for (const auto* path : kVHCIPlatformPaths) {
89 if (stat(path, &buf) == 0) {
90 syspath_ = path;
91 break;
92 }
93 }
Greg Hartman92045f62017-10-31 17:12:24 -070094
Tomasz Wiszkowskie18ee642017-10-31 12:53:42 -070095 if (syspath_.empty()) {
Greg Hartman92045f62017-10-31 17:12:24 -070096 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
Ryan Hainingdd0dcfe2018-05-16 13:55:20 -0700102 if (!VerifyPortIsFree()) {
103 LOG(ERROR) << "Trying to use VHCI port " << port_ << " but it is already in"
104 << " use.";
Greg Hartman92045f62017-10-31 17:12:24 -0700105 return false;
106 }
107
Ryan Hainingdd0dcfe2018-05-16 13:55:20 -0700108 LOG(INFO) << "Using VHCI port " << port_;
Ryan Hainingb8afc412018-04-07 09:49:28 -0700109 attach_thread_ = std::thread([this] { AttachThread(); });
Greg Hartman92045f62017-10-31 17:12:24 -0700110 return true;
111}
112
Ryan Hainingdd0dcfe2018-05-16 13:55:20 -0700113bool VHCIInstrument::VerifyPortIsFree() const {
114 std::ifstream status_file(syspath_ + "/status");
Greg Hartman92045f62017-10-31 17:12:24 -0700115
Ryan Hainingdd0dcfe2018-05-16 13:55:20 -0700116 if (!status_file.good()) {
Greg Hartman92045f62017-10-31 17:12:24 -0700117 LOG(ERROR) << "Could not open usb-ip status file.";
118 return false;
119 }
120
121 // Skip past the header line.
Ryan Hainingdd0dcfe2018-05-16 13:55:20 -0700122 status_file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Greg Hartman92045f62017-10-31 17:12:24 -0700123
Ryan Hainingdd0dcfe2018-05-16 13:55:20 -0700124 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;
Greg Hartman92045f62017-10-31 17:12:24 -0700139 }
140 }
Ryan Hainingdd0dcfe2018-05-16 13:55:20 -0700141 LOG(ERROR) << "Couldn't find status for VHCI port " << port_;
Greg Hartman92045f62017-10-31 17:12:24 -0700142 return false;
143}
144
145void VHCIInstrument::TriggerAttach() {
146 control_write_end_->Write(&kControlAttach, sizeof(kControlAttach));
147}
148
149void VHCIInstrument::TriggerDetach() {
150 control_write_end_->Write(&kControlDetach, sizeof(kControlDetach));
151}
152
153void VHCIInstrument::AttachThread() {
Greg Hartman153b1062017-11-11 12:09:21 -0800154 cvd::SharedFD epoll = cvd::SharedFD::Epoll();
Greg Hartman92045f62017-10-31 17:12:24 -0700155 // 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:
Greg Hartman153b1062017-11-11 12:09:21 -0800185 vhci_socket_ = cvd::SharedFD();
Greg Hartman92045f62017-10-31 17:12:24 -0700186 // 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
209bool 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
222bool VHCIInstrument::Attach() {
223 if (!vhci_socket_->IsOpen()) {
224 vhci_socket_ =
Greg Hartman153b1062017-11-11 12:09:21 -0800225 cvd::SharedFD::SocketLocalClient(name_.c_str(), true, SOCK_STREAM);
Greg Hartman92045f62017-10-31 17:12:24 -0700226 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
Ryan Hainingdd0dcfe2018-05-16 13:55:20 -0700250 // 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.
Greg Hartman92045f62017-10-31 17:12:24 -0700252 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