• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <fcntl.h>
17 #include <sys/poll.h>
18 #include <sys/uio.h>
19 #include <termios.h>
20 #include <unistd.h>
21 #include <iomanip>
22 #include <ios>
23 #include <optional>
24 
25 #include <gflags/gflags.h>
26 
27 #include "android-base/logging.h"
28 
29 #include "model/devices/h4_packetizer.h"
30 
31 // Copied from net/bluetooth/hci.h
32 #define HCI_ACLDATA_PKT 0x02
33 #define HCI_SCODATA_PKT 0x03
34 #define HCI_EVENT_PKT 0x04
35 #define HCI_ISODATA_PKT 0x05
36 #define HCI_VENDOR_PKT 0xff
37 #define HCI_MAX_ACL_SIZE 1024
38 #define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4)
39 
40 // Include H4 header byte, and reserve more buffer size in the case of excess
41 // packet.
42 constexpr const size_t kBufferSize = (HCI_MAX_FRAME_SIZE + 1) * 2;
43 
44 constexpr const char* kVhciDev = "/dev/vhci";
45 DEFINE_string(virtio_console_dev, "", "virtio-console device path");
46 
send(int fd_,uint8_t type,const uint8_t * data,size_t length)47 ssize_t send(int fd_, uint8_t type, const uint8_t* data, size_t length) {
48   struct iovec iov[] = {{&type, sizeof(type)},
49                         {const_cast<uint8_t*>(data), length}};
50   ssize_t ret = 0;
51   do {
52     ret = TEMP_FAILURE_RETRY(writev(fd_, iov, sizeof(iov) / sizeof(iov[0])));
53   } while (-1 == ret && EAGAIN == errno);
54   if (ret == -1) {
55     PLOG(ERROR) << "virtio-console to vhci failed";
56   }
57   return ret;
58 }
59 
forward(int from,int to,std::optional<unsigned char> filter_out,unsigned char * buf)60 ssize_t forward(int from, int to, std::optional<unsigned char> filter_out,
61                 unsigned char* buf) {
62   ssize_t count = TEMP_FAILURE_RETRY(read(from, buf, kBufferSize));
63   if (count < 0) {
64     PLOG(ERROR) << "read failed";
65     return count;
66   } else if (count == 0) {
67     return count;
68   }
69   if (filter_out && buf[0] == *filter_out) {
70     LOG(INFO) << "ignore 0x" << std::hex << std::setw(2) << std::setfill('0')
71               << (unsigned)buf[0] << " packet";
72     return 0;
73   }
74   count = TEMP_FAILURE_RETRY(write(to, buf, count));
75   if (count < 0) {
76     PLOG(ERROR) << "write failed, type: 0x" << std::hex << std::setw(2)
77                << std::setfill('0') << (unsigned)buf[0];
78   }
79   return count;
80 }
81 
forward(int from,int to,unsigned char * buf)82 ssize_t forward(int from, int to, unsigned char* buf) {
83   return forward(from, to, std::nullopt, buf);
84 }
85 
setTerminalRaw(int fd_)86 int setTerminalRaw(int fd_) {
87   termios terminal_settings;
88   int rval = tcgetattr(fd_, &terminal_settings);
89   if (rval < 0) {
90     return rval;
91   }
92   cfmakeraw(&terminal_settings);
93   rval = tcsetattr(fd_, TCSANOW, &terminal_settings);
94   return rval;
95 }
96 
main(int argc,char ** argv)97 int main(int argc, char** argv) {
98   gflags::ParseCommandLineFlags(&argc, &argv, true);
99 
100   int vhci_fd = open(kVhciDev, O_RDWR);
101   int virtio_fd = open(FLAGS_virtio_console_dev.c_str(), O_RDWR);
102   setTerminalRaw(virtio_fd);
103 
104   struct pollfd fds[2];
105 
106   fds[0].fd = vhci_fd;
107   fds[0].events = POLLIN;
108   fds[1].fd = virtio_fd;
109   fds[1].events = POLLIN;
110   unsigned char buf[kBufferSize];
111 
112   auto h4 = test_vendor_lib::H4Packetizer(
113       virtio_fd,
114       [](const std::vector<uint8_t>& /* raw_command */) {
115         LOG(ERROR)
116             << "Unexpected command: command pkt shouldn't be sent as response.";
117       },
118       [vhci_fd](const std::vector<uint8_t>& raw_event) {
119         send(vhci_fd, HCI_EVENT_PKT, raw_event.data(), raw_event.size());
120       },
121       [vhci_fd](const std::vector<uint8_t>& raw_acl) {
122         send(vhci_fd, HCI_ACLDATA_PKT, raw_acl.data(), raw_acl.size());
123       },
124       [vhci_fd](const std::vector<uint8_t>& raw_sco) {
125         send(vhci_fd, HCI_SCODATA_PKT, raw_sco.data(), raw_sco.size());
126       },
127       [vhci_fd](const std::vector<uint8_t>& raw_iso) {
128         send(vhci_fd, HCI_ISODATA_PKT, raw_iso.data(), raw_iso.size());
129       },
130       []() { LOG(INFO) << "HCI socket device disconnected"; });
131 
132   while (true) {
133     int ret = TEMP_FAILURE_RETRY(poll(fds, 2, -1));
134     if (ret < 0) {
135       PLOG(ERROR) << "poll failed";
136       continue;
137     }
138     if (fds[0].revents & (POLLIN | POLLERR)) {
139       // TODO(b/182245475) Ignore HCI_VENDOR_PKT
140       // because root-canal cannot handle it.
141       ssize_t c = forward(vhci_fd, virtio_fd, HCI_VENDOR_PKT, buf);
142       if (c < 0) {
143         PLOG(ERROR) << "vhci to virtio-console failed";
144       }
145     }
146 
147     if (fds[1].revents & (POLLIN | POLLERR)) {
148       // 'virtio-console to vhci' depends on H4Packetizer because vhci expects
149       // full packet, but the data from virtio-console could be partial.
150       h4.OnDataReady(virtio_fd);
151     }
152   }
153 }