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 #include "host/commands/virtual_usb_manager/usbip/client.h"
17
18 #include <arpa/inet.h>
19
20 #include <glog/logging.h>
21 #include <iostream>
22
23 #include "host/commands/virtual_usb_manager/usbip/device.h"
24 #include "host/commands/virtual_usb_manager/usbip/messages.h"
25
26 namespace vadb {
27 namespace usbip {
28
29 // NetToHost and HostToNet are used to reduce risk of copy/paste errors and to
30 // provide uniform method of converting messages between different endian types.
31 namespace {
32
NetToHost(uint32_t t)33 uint32_t NetToHost(uint32_t t) { return ntohl(t); }
34
NetToHost(Command t)35 Command NetToHost(Command t) { return static_cast<Command>(ntohl(t)); }
36
NetToHost(Direction t)37 Direction NetToHost(Direction t) { return static_cast<Direction>(ntohl(t)); }
38
NetToHost(uint16_t t)39 uint32_t NetToHost(uint16_t t) { return ntohs(t); }
40
NetToHost(const CmdHeader & t)41 CmdHeader NetToHost(const CmdHeader& t) {
42 CmdHeader rval = t;
43 rval.command = NetToHost(t.command);
44 rval.seq_num = NetToHost(t.seq_num);
45 rval.bus_num = NetToHost(t.bus_num);
46 rval.dev_num = NetToHost(t.dev_num);
47 rval.direction = NetToHost(t.direction);
48 rval.endpoint = NetToHost(t.endpoint);
49 return rval;
50 }
51
NetToHost(const CmdReqSubmit & t)52 CmdReqSubmit NetToHost(const CmdReqSubmit& t) {
53 CmdReqSubmit rval = t;
54 rval.transfer_flags = NetToHost(t.transfer_flags);
55 rval.transfer_buffer_length = NetToHost(t.transfer_buffer_length);
56 rval.start_frame = NetToHost(t.start_frame);
57 rval.number_of_packets = NetToHost(t.number_of_packets);
58 rval.deadline_interval = NetToHost(t.deadline_interval);
59 return rval;
60 }
61
NetToHost(const CmdReqUnlink & t)62 CmdReqUnlink NetToHost(const CmdReqUnlink& t) {
63 CmdReqUnlink rval = t;
64 rval.seq_num = NetToHost(t.seq_num);
65 return rval;
66 }
67
HostToNet(uint32_t t)68 uint32_t HostToNet(uint32_t t) { return htonl(t); }
69
HostToNet(const Command t)70 Command HostToNet(const Command t) { return static_cast<Command>(htonl(t)); }
71
HostToNet(Direction t)72 Direction HostToNet(Direction t) { return static_cast<Direction>(htonl(t)); }
73
HostToNet(uint16_t t)74 uint16_t HostToNet(uint16_t t) { return htons(t); }
75
HostToNet(const CmdHeader & t)76 CmdHeader HostToNet(const CmdHeader& t) {
77 CmdHeader rval = t;
78 rval.command = HostToNet(t.command);
79 rval.seq_num = HostToNet(t.seq_num);
80 rval.bus_num = HostToNet(t.bus_num);
81 rval.dev_num = HostToNet(t.dev_num);
82 rval.direction = HostToNet(t.direction);
83 rval.endpoint = HostToNet(t.endpoint);
84 return rval;
85 }
86
HostToNet(const CmdRepSubmit & t)87 CmdRepSubmit HostToNet(const CmdRepSubmit& t) {
88 CmdRepSubmit rval = t;
89 rval.status = HostToNet(t.status);
90 rval.actual_length = HostToNet(t.actual_length);
91 rval.start_frame = HostToNet(t.start_frame);
92 rval.number_of_packets = HostToNet(t.number_of_packets);
93 rval.error_count = HostToNet(t.error_count);
94 return rval;
95 }
96
HostToNet(const CmdRepUnlink & t)97 CmdRepUnlink HostToNet(const CmdRepUnlink& t) {
98 CmdRepUnlink rval = t;
99 rval.status = HostToNet(t.status);
100 return rval;
101 }
102
103 // Converts data to network order and sends it to the USB/IP client.
104 // Returns true, if message was sent successfully.
105 template <typename T>
SendUSBIPMsg(const cvd::SharedFD & fd,const T & data)106 bool SendUSBIPMsg(const cvd::SharedFD& fd, const T& data) {
107 T net = HostToNet(data);
108 return fd->Send(&net, sizeof(T), MSG_NOSIGNAL) == sizeof(T);
109 }
110
111 // Receive message from USB/IP client.
112 // After message is received, it's updated to match host endian.
113 // Returns true, if message was received successfully.
114 template <typename T>
RecvUSBIPMsg(const cvd::SharedFD & fd,T * data)115 bool RecvUSBIPMsg(const cvd::SharedFD& fd, T* data) {
116 T net;
117 bool res = fd->Recv(&net, sizeof(T), MSG_NOSIGNAL) == sizeof(T);
118 if (res) {
119 *data = NetToHost(net);
120 }
121 return res;
122 }
123
124 } // namespace
125
BeforeSelect(cvd::SharedFDSet * fd_read) const126 void Client::BeforeSelect(cvd::SharedFDSet* fd_read) const {
127 fd_read->Set(fd_);
128 }
129
AfterSelect(const cvd::SharedFDSet & fd_read)130 bool Client::AfterSelect(const cvd::SharedFDSet& fd_read) {
131 if (fd_read.IsSet(fd_)) return HandleIncomingMessage();
132 return true;
133 }
134
135 // Handle incoming COMMAND.
136 //
137 // Read next CMD from client channel.
138 // Returns false, if connection should be dropped.
HandleIncomingMessage()139 bool Client::HandleIncomingMessage() {
140 CmdHeader hdr;
141 if (!RecvUSBIPMsg(fd_, &hdr)) {
142 LOG(ERROR) << "Could not read command header: " << fd_->StrError();
143 return false;
144 }
145
146 // And the protocol, again.
147 switch (hdr.command) {
148 case kUsbIpCmdReqSubmit:
149 return HandleSubmitCmd(hdr);
150
151 case kUsbIpCmdReqUnlink:
152 return HandleUnlinkCmd(hdr);
153
154 default:
155 LOG(ERROR) << "Unsupported command requested: " << hdr.command;
156 return false;
157 }
158 }
159
160 // Handle incoming SUBMIT COMMAND.
161 //
162 // Execute command on specified USB device.
163 // Returns false, if connection should be dropped.
HandleSubmitCmd(const CmdHeader & cmd)164 bool Client::HandleSubmitCmd(const CmdHeader& cmd) {
165 CmdReqSubmit req;
166 if (!RecvUSBIPMsg(fd_, &req)) {
167 LOG(ERROR) << "Could not read submit command: " << fd_->StrError();
168 return false;
169 }
170
171 uint32_t seq_num = cmd.seq_num;
172
173 // Reserve buffer for data in or out.
174 std::vector<uint8_t> payload;
175 int payload_length = req.transfer_buffer_length;
176 payload.resize(payload_length);
177
178 bool is_host_to_device = cmd.direction == kUsbIpDirectionOut;
179 // Control requests are quite easy to detect; if setup is all '0's, then we're
180 // doing a data transfer, otherwise it's a control transfer.
181 // We only check for cmd and type fields here, as combination 0/0 of these
182 // fields is already invalid (cmd == GET_STATUS, type = WRITE).
183 bool is_control_request = !(req.setup.cmd == 0 && req.setup.type == 0);
184
185 // Find requested device and execute command.
186 auto device = pool_.GetDevice({cmd.bus_num, cmd.dev_num});
187 if (device) {
188 // Read data to be sent to device, if specified.
189 if (is_host_to_device && payload_length) {
190 size_t got = 0;
191 // Make sure we read everything.
192 while (got < payload.size()) {
193 auto read =
194 fd_->Recv(&payload[got], payload.size() - got, MSG_NOSIGNAL);
195 if (fd_->GetErrno() != 0) {
196 LOG(ERROR) << "Client disconnected: " << fd_->StrError();
197 return false;
198 } else if (!read) {
199 LOG(ERROR) << "Short read; client likely disconnected.";
200 return false;
201 }
202 got += read;
203 }
204 }
205
206 // If setup structure of request is initialized then we need to execute
207 // control transfer. Otherwise, this is a plain data exchange.
208 bool send_success = false;
209 if (is_control_request) {
210 send_success = device->handle_control_transfer(
211 req.setup, req.deadline_interval, std::move(payload),
212 [this, seq_num, is_host_to_device](bool is_success,
213 std::vector<uint8_t> data) {
214 HandleAsyncDataReady(seq_num, is_success, is_host_to_device,
215 std::move(data));
216 });
217 } else {
218 send_success = device->handle_data_transfer(
219 cmd.endpoint, is_host_to_device, req.deadline_interval,
220 std::move(payload),
221 [this, seq_num, is_host_to_device](bool is_success,
222 std::vector<uint8_t> data) {
223 HandleAsyncDataReady(seq_num, is_success, is_host_to_device,
224 std::move(data));
225 });
226 }
227
228 // Simply fail if couldn't execute command.
229 if (!send_success) {
230 HandleAsyncDataReady(seq_num, false, is_host_to_device,
231 std::vector<uint8_t>());
232 }
233 }
234 return true;
235 }
236
HandleAsyncDataReady(uint32_t seq_num,bool is_success,bool is_host_to_device,std::vector<uint8_t> data)237 void Client::HandleAsyncDataReady(uint32_t seq_num, bool is_success,
238 bool is_host_to_device,
239 std::vector<uint8_t> data) {
240 // Response template.
241 // - in header, host doesn't care about anything else except for command type
242 // and sequence number.
243 // - in body, report status == !OK unless we completed everything
244 // successfully.
245 CmdHeader rephdr{};
246 rephdr.command = kUsbIpCmdRepSubmit;
247 rephdr.seq_num = seq_num;
248
249 CmdRepSubmit rep{};
250 rep.status = is_success ? 0 : 1;
251 rep.actual_length = data.size();
252
253 // Data out.
254 if (!SendUSBIPMsg(fd_, rephdr)) {
255 LOG(ERROR) << "Failed to send response header: " << fd_->StrError();
256 return;
257 }
258
259 if (!SendUSBIPMsg(fd_, rep)) {
260 LOG(ERROR) << "Failed to send response body: " << fd_->StrError();
261 return;
262 }
263
264 if (!is_host_to_device && data.size() > 0) {
265 if (static_cast<size_t>(
266 fd_->Send(data.data(), data.size(), MSG_NOSIGNAL)) != data.size()) {
267 LOG(ERROR) << "Failed to send response payload: " << fd_->StrError();
268 return;
269 }
270 }
271 }
272
273 // Handle incoming UNLINK COMMAND.
274 //
275 // Unlink removes command specified via seq_num from a list of commands to be
276 // executed.
277 // We don't schedule commands for execution, so technically every UNLINK will
278 // come in late.
279 // Returns false, if connection should be dropped.
HandleUnlinkCmd(const CmdHeader & cmd)280 bool Client::HandleUnlinkCmd(const CmdHeader& cmd) {
281 CmdReqUnlink req;
282 if (!RecvUSBIPMsg(fd_, &req)) {
283 LOG(ERROR) << "Could not read unlink command: " << fd_->StrError();
284 return false;
285 }
286 LOG(INFO) << "Client requested to unlink previously submitted command: "
287 << req.seq_num;
288
289 CmdHeader rephdr{};
290 rephdr.command = kUsbIpCmdRepUnlink;
291 rephdr.seq_num = cmd.seq_num;
292
293 // Technically we do not schedule commands for execution, so we cannot
294 // de-queue commands, either. Indicate this by sending status != ok.
295 CmdRepUnlink rep;
296 rep.status = 1;
297
298 if (!SendUSBIPMsg(fd_, rephdr)) {
299 LOG(ERROR) << "Could not send unlink command header: " << fd_->StrError();
300 return false;
301 }
302
303 if (!SendUSBIPMsg(fd_, rep)) {
304 LOG(ERROR) << "Could not send unlink command data: " << fd_->StrError();
305 return false;
306 }
307 return true;
308 }
309
310 } // namespace usbip
311 } // namespace vadb
312