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/libs/usbip/client.h" 17 18#include <arpa/inet.h> 19 20#include <glog/logging.h> 21#include <iostream> 22 23#include "host/libs/usbip/device.h" 24#include "host/libs/usbip/messages.h" 25 26namespace vadb { 27namespace 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. 31namespace { 32 33uint32_t NetToHost(uint32_t t) { return ntohl(t); } 34 35Command NetToHost(Command t) { return static_cast<Command>(ntohl(t)); } 36 37Direction NetToHost(Direction t) { return static_cast<Direction>(ntohl(t)); } 38 39uint32_t NetToHost(uint16_t t) { return ntohs(t); } 40 41CmdHeader 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 52CmdReqSubmit 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 62CmdReqUnlink NetToHost(const CmdReqUnlink& t) { 63 CmdReqUnlink rval = t; 64 rval.seq_num = NetToHost(t.seq_num); 65 return rval; 66} 67 68uint32_t HostToNet(uint32_t t) { return htonl(t); } 69 70Command HostToNet(const Command t) { return static_cast<Command>(htonl(t)); } 71 72Direction HostToNet(Direction t) { return static_cast<Direction>(htonl(t)); } 73 74uint16_t HostToNet(uint16_t t) { return htons(t); } 75 76CmdHeader 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 87CmdRepSubmit 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 97CmdRepUnlink 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. 105template <typename T> 106bool 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. 114template <typename T> 115bool 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 126void Client::BeforeSelect(cvd::SharedFDSet* fd_read) const { 127 fd_read->Set(fd_); 128} 129 130bool 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. 139bool 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. 164bool 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 237void 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. 280bool 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