• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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