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 }