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 "hci/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_fd,int to_fd,unsigned char * buf)60 ssize_t forward(int from_fd, int to_fd, unsigned char* buf) {
61   ssize_t count = TEMP_FAILURE_RETRY(read(from_fd, buf, kBufferSize));
62   if (count < 0) {
63     PLOG(ERROR) << "read failed";
64     return count;
65   }
66   if (count == 0) {
67     return count;
68   }
69   // TODO(b/182245475) Ignore HCI_VENDOR_PKT
70   // because root-canal cannot handle it.
71   if (buf[0] == HCI_VENDOR_PKT) {
72     LOG(INFO) << "Ignoring VENDOR packet";
73     return 0;
74   }
75   count = TEMP_FAILURE_RETRY(write(to_fd, buf, count));
76   if (count < 0) {
77     PLOG(ERROR) << "write failed, type: 0x" << std::hex << std::setw(2)
78                << std::setfill('0') << (unsigned)buf[0];
79   }
80   return count;
81 }
82 
setTerminalRaw(int fd_)83 int setTerminalRaw(int fd_) {
84   termios terminal_settings;
85   int rval = tcgetattr(fd_, &terminal_settings);
86   if (rval < 0) {
87     return rval;
88   }
89   cfmakeraw(&terminal_settings);
90   rval = tcsetattr(fd_, TCSANOW, &terminal_settings);
91   return rval;
92 }
93 
main(int argc,char ** argv)94 int main(int argc, char** argv) {
95   gflags::ParseCommandLineFlags(&argc, &argv, true);
96 
97   int vhci_fd = open(kVhciDev, O_RDWR);
98   if (vhci_fd < 0) {
99     PLOG(ERROR) << "Unable to open " << kVhciDev;
100   }
101   int virtio_fd = open(FLAGS_virtio_console_dev.c_str(), O_RDWR);
102   if (virtio_fd < 0) {
103     PLOG(ERROR) << "Unable to open " << FLAGS_virtio_console_dev;
104   }
105   int set_result = setTerminalRaw(virtio_fd);
106   if (set_result < 0) {
107     PLOG(ERROR) << "setTerminalRaw failed " << FLAGS_virtio_console_dev;
108   }
109 
110   struct pollfd fds[2];
111 
112   fds[0].fd = vhci_fd;
113   fds[0].events = POLLIN;
114   fds[1].fd = virtio_fd;
115   fds[1].events = POLLIN;
116   unsigned char buf[kBufferSize];
117 
118   auto h4 = rootcanal::H4Packetizer(
119       virtio_fd,
120       [](const std::vector<uint8_t>& /* raw_command */) {
121         LOG(ERROR)
122             << "Unexpected command: command pkt shouldn't be sent as response.";
123       },
124       [vhci_fd](const std::vector<uint8_t>& raw_event) {
125         send(vhci_fd, HCI_EVENT_PKT, raw_event.data(), raw_event.size());
126       },
127       [vhci_fd](const std::vector<uint8_t>& raw_acl) {
128         send(vhci_fd, HCI_ACLDATA_PKT, raw_acl.data(), raw_acl.size());
129       },
130       [vhci_fd](const std::vector<uint8_t>& raw_sco) {
131         send(vhci_fd, HCI_SCODATA_PKT, raw_sco.data(), raw_sco.size());
132       },
133       [vhci_fd](const std::vector<uint8_t>& raw_iso) {
134         send(vhci_fd, HCI_ISODATA_PKT, raw_iso.data(), raw_iso.size());
135       },
136       []() { LOG(INFO) << "HCI socket device disconnected"; });
137 
138   // Flag to drop any data left in the virtio-console buffer
139   // before any command is received from vhci: corrupted data
140   // could be left there from android restarting.
141   bool before_first_command = true;
142 
143   while (true) {
144     int ret = TEMP_FAILURE_RETRY(poll(fds, 2, -1));
145     if (ret < 0) {
146       PLOG(ERROR) << "poll failed";
147       continue;
148     }
149     if (fds[1].revents & POLLHUP) {
150       LOG(ERROR) << "PollHUP";
151       usleep(50 * 1000);
152       continue;
153     }
154     if (fds[1].revents & (POLLIN | POLLERR)) {
155       if (before_first_command) {
156         // Drop any data left in the virtio-console from a previous reset.
157         ssize_t bytes = TEMP_FAILURE_RETRY(read(virtio_fd, buf, kBufferSize));
158         if (bytes < 0) {
159           LOG(ERROR) << "virtio_fd ready, but read failed " << strerror(errno);
160           continue;
161         }
162         if (bytes == 1) {
163           LOG(INFO) << "Discarding 1 byte from virtio_fd: "
164                     << "0x" << std::hex << (unsigned)buf[0];
165         } else {
166           LOG(INFO) << "Discarding " << bytes << " bytes from virtio_fd: "
167                     << "0x" << std::hex << (unsigned)buf[0] << " .. 0x"
168                     << std::hex << (unsigned)buf[bytes - 1];
169         }
170         continue;
171       }
172       // 'virtio-console to vhci' depends on H4Packetizer because vhci expects
173       // full packet, but the data from virtio-console could be partial.
174       h4.OnDataReady(virtio_fd);
175     }
176     if (fds[0].revents & (POLLIN | POLLERR)) {
177       ssize_t transferred = forward(vhci_fd, virtio_fd, buf);
178       if (transferred > 0 && before_first_command) {
179         LOG(INFO) << "Received first command from VHCI device; enabling reads "
180                      "from virtio";
181         before_first_command = false;
182       }
183     }
184   }
185 }
186