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
VHCIInstrument(int port,const std::string & name)76 VHCIInstrument::VHCIInstrument(int port, const std::string& name)
77 : name_(name), port_{port} {}
78
~VHCIInstrument()79 VHCIInstrument::~VHCIInstrument() {
80 control_write_end_->Write(&kControlExit, sizeof(kControlExit));
81 attach_thread_.join();
82 }
83
Init()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
VerifyPortIsFree() const113 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
TriggerAttach()145 void VHCIInstrument::TriggerAttach() {
146 control_write_end_->Write(&kControlAttach, sizeof(kControlAttach));
147 }
148
TriggerDetach()149 void VHCIInstrument::TriggerDetach() {
150 control_write_end_->Write(&kControlDetach, sizeof(kControlDetach));
151 }
152
AttachThread()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
Detach()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
Attach()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