c6a5caa5eaee795bf266283dba8fcb0c3da6eb8c
[platform/upstream/bluez.git] / emulator / vhci.c
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 /*
3  *
4  *  BlueZ - Bluetooth protocol stack for Linux
5  *
6  *  Copyright (C) 2011-2014  Intel Corporation
7  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
8  *
9  *
10  */
11
12 #ifdef HAVE_CONFIG_H
13 #include <config.h>
14 #endif
15
16 #include <stdio.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/uio.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <dirent.h>
26
27 #include "lib/bluetooth.h"
28 #include "lib/hci.h"
29
30 #include "src/shared/io.h"
31 #include "monitor/bt.h"
32 #include "btdev.h"
33 #include "vhci.h"
34
35 #define DEBUGFS_PATH "/sys/kernel/debug/bluetooth"
36 #define DEVCORE_PATH "/sys/class/devcoredump"
37
38 struct vhci {
39         enum btdev_type type;
40         uint16_t index;
41         struct io *io;
42         struct btdev *btdev;
43 };
44
45 static void vhci_destroy(void *user_data)
46 {
47         struct vhci *vhci = user_data;
48
49         btdev_destroy(vhci->btdev);
50         io_destroy(vhci->io);
51
52         free(vhci);
53 }
54
55 static void vhci_write_callback(const struct iovec *iov, int iovlen,
56                                                         void *user_data)
57 {
58         struct vhci *vhci = user_data;
59         ssize_t written;
60
61         written = io_send(vhci->io, iov, iovlen);
62         if (written < 0)
63                 return;
64 }
65
66 static bool vhci_read_callback(struct io *io, void *user_data)
67 {
68         struct vhci *vhci = user_data;
69         int fd = io_get_fd(vhci->io);
70         unsigned char buf[4096];
71         ssize_t len;
72
73         len = read(fd, buf, sizeof(buf));
74         if (len < 1)
75                 return false;
76
77         switch (buf[0]) {
78         case BT_H4_CMD_PKT:
79         case BT_H4_ACL_PKT:
80         case BT_H4_SCO_PKT:
81         case BT_H4_ISO_PKT:
82                 btdev_receive_h4(vhci->btdev, buf, len);
83                 break;
84         }
85
86         return true;
87 }
88
89 bool vhci_set_debug(struct vhci *vhci, vhci_debug_func_t callback,
90                         void *user_data, vhci_destroy_func_t destroy)
91 {
92         if (!vhci)
93                 return false;
94
95         return btdev_set_debug(vhci->btdev, callback, user_data, destroy);
96 }
97
98 struct vhci_create_req {
99         uint8_t  pkt_type;
100         uint8_t  opcode;
101 } __attribute__((packed));
102
103 struct vhci_create_rsp {
104         uint8_t  pkt_type;
105         uint8_t  opcode;
106         uint16_t index;
107 } __attribute__((packed));
108
109 struct vhci *vhci_open(uint8_t type)
110 {
111         struct vhci *vhci;
112         struct vhci_create_req req;
113         struct vhci_create_rsp rsp;
114         int fd;
115
116         fd = open("/dev/vhci", O_RDWR | O_NONBLOCK);
117         if (fd < 0)
118                 return NULL;
119
120         memset(&req, 0, sizeof(req));
121         req.pkt_type = HCI_VENDOR_PKT;
122
123         switch (type) {
124         case BTDEV_TYPE_AMP:
125                 req.opcode = HCI_AMP;
126                 break;
127         default:
128                 req.opcode = HCI_PRIMARY;
129                 break;
130         }
131
132         if (write(fd, &req, sizeof(req)) < 0) {
133                 close(fd);
134                 return NULL;
135         }
136
137         memset(&rsp, 0, sizeof(rsp));
138
139         if (read(fd, &rsp, sizeof(rsp)) < 0) {
140                 close(fd);
141                 return NULL;
142         }
143
144         vhci = malloc(sizeof(*vhci));
145         if (!vhci) {
146                 close(fd);
147                 return NULL;
148         }
149
150         memset(vhci, 0, sizeof(*vhci));
151         vhci->type = type;
152         vhci->index = rsp.index;
153         vhci->io = io_new(fd);
154
155         io_set_close_on_destroy(vhci->io, true);
156
157         vhci->btdev = btdev_create(type, rsp.index);
158         if (!vhci->btdev) {
159                 vhci_destroy(vhci);
160                 return NULL;
161         }
162
163         btdev_set_send_handler(vhci->btdev, vhci_write_callback, vhci);
164
165         if (!io_set_read_handler(vhci->io, vhci_read_callback, vhci, NULL)) {
166                 vhci_destroy(vhci);
167                 return NULL;
168         }
169
170         return vhci;
171 }
172
173 void vhci_close(struct vhci *vhci)
174 {
175         if (!vhci)
176                 return;
177
178         vhci_destroy(vhci);
179 }
180
181 bool vhci_pause_input(struct vhci *vhci, bool paused)
182 {
183         if (paused)
184                 return io_set_read_handler(vhci->io, NULL, NULL, NULL);
185         else
186                 return io_set_read_handler(vhci->io, vhci_read_callback, vhci,
187                                                                         NULL);
188 }
189
190 struct btdev *vhci_get_btdev(struct vhci *vhci)
191 {
192         if (!vhci)
193                 return NULL;
194
195         return vhci->btdev;
196 }
197
198 static int vhci_debugfs_write(struct vhci *vhci, char *option, const void *data,
199                               size_t len)
200 {
201         char path[64];
202         int fd, err;
203         size_t n;
204
205         if (!vhci)
206                 return -EINVAL;
207
208         memset(path, 0, sizeof(path));
209         sprintf(path, DEBUGFS_PATH "/hci%d/%s", vhci->index, option);
210
211         fd = open(path, O_RDWR);
212         if (fd < 0)
213                 return -errno;
214
215         n = write(fd, data, len);
216         if (n == len)
217                 err = 0;
218         else
219                 err = -errno;
220
221         close(fd);
222
223         return err;
224 }
225
226 int vhci_set_force_suspend(struct vhci *vhci, bool enable)
227 {
228         char val;
229
230         val = (enable) ? 'Y' : 'N';
231
232         return vhci_debugfs_write(vhci, "force_suspend", &val, sizeof(val));
233 }
234
235 int vhci_set_force_wakeup(struct vhci *vhci, bool enable)
236 {
237         char val;
238
239         val = (enable) ? 'Y' : 'N';
240
241         return vhci_debugfs_write(vhci, "force_wakeup", &val, sizeof(val));
242 }
243
244 int vhci_set_msft_opcode(struct vhci *vhci, uint16_t opcode)
245 {
246         int err;
247         char val[7];
248
249         snprintf(val, sizeof(val), "0x%4x", opcode);
250
251         err = vhci_debugfs_write(vhci, "msft_opcode", &val, sizeof(val));
252         if (err)
253                 return err;
254
255         return btdev_set_msft_opcode(vhci->btdev, opcode);
256 }
257
258 int vhci_set_aosp_capable(struct vhci *vhci, bool enable)
259 {
260         char val;
261
262         val = (enable) ? 'Y' : 'N';
263
264         return vhci_debugfs_write(vhci, "aosp_capable", &val, sizeof(val));
265 }
266
267 int vhci_set_emu_opcode(struct vhci *vhci, uint16_t opcode)
268 {
269         return btdev_set_emu_opcode(vhci->btdev, opcode);
270 }
271
272 int vhci_set_force_static_address(struct vhci *vhci, bool enable)
273 {
274         char val;
275
276         val = (enable) ? 'Y' : 'N';
277
278         return vhci_debugfs_write(vhci, "force_static_address", &val,
279                                                         sizeof(val));
280 }
281
282 int vhci_force_devcd(struct vhci *vhci, const void *data, size_t len)
283 {
284         return vhci_debugfs_write(vhci, "force_devcoredump", data, len);
285 }
286
287 int vhci_read_devcd(struct vhci *vhci, void *buf, size_t size)
288 {
289         DIR *dir;
290         struct dirent *entry;
291         char filename[PATH_MAX];
292         int fd;
293         int ret;
294
295         dir = opendir(DEVCORE_PATH);
296         if (dir == NULL)
297                 return -errno;
298
299         while ((entry = readdir(dir)) != NULL) {
300                 if (strstr(entry->d_name, "devcd"))
301                         break;
302         }
303
304         if (entry == NULL) {
305                 ret = -ENOENT;
306                 goto close_dir;
307         }
308
309         sprintf(filename, DEVCORE_PATH "/%s/data", entry->d_name);
310         fd  = open(filename, O_RDWR);
311         if (fd < 0) {
312                 ret = -errno;
313                 goto close_dir;
314         }
315
316         ret = read(fd, buf, size);
317         if (ret < 0) {
318                 ret = -errno;
319                 goto close_file;
320         }
321
322         /* Once the devcoredump is read, write anything to it to mark it for
323          * cleanup.
324          */
325         if (write(fd, "0", 1) < 0) {
326                 ret = -errno;
327                 goto close_file;
328         }
329
330 close_file:
331         close(fd);
332
333 close_dir:
334         closedir(dir);
335
336         return ret;
337 }