Home | History | Annotate | Download | only in usbip
      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 
     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 
     33 uint32_t NetToHost(uint32_t t) { return ntohl(t); }
     34 
     35 Command NetToHost(Command t) { return static_cast<Command>(ntohl(t)); }
     36 
     37 Direction NetToHost(Direction t) { return static_cast<Direction>(ntohl(t)); }
     38 
     39 uint32_t NetToHost(uint16_t t) { return ntohs(t); }
     40 
     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 
     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 
     62 CmdReqUnlink NetToHost(const CmdReqUnlink& t) {
     63   CmdReqUnlink rval = t;
     64   rval.seq_num = NetToHost(t.seq_num);
     65   return rval;
     66 }
     67 
     68 uint32_t HostToNet(uint32_t t) { return htonl(t); }
     69 
     70 Command HostToNet(const Command t) { return static_cast<Command>(htonl(t)); }
     71 
     72 Direction HostToNet(Direction t) { return static_cast<Direction>(htonl(t)); }
     73 
     74 uint16_t HostToNet(uint16_t t) { return htons(t); }
     75 
     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 
     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 
     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>
    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>
    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 
    126 void Client::BeforeSelect(cvd::SharedFDSet* fd_read) const {
    127   fd_read->Set(fd_);
    128 }
    129 
    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.
    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.
    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 
    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.
    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