Hi Inga, On Tue, Nov 10, 2020 at 1:55 PM Stotland, Inga <inga.stotland@xxxxxxxxx> wrote: > > Hi Luiz, > > On Mon, 2020-11-09 at 10:04 -0800, Luiz Augusto von Dentz wrote: > > Hi Inga, > > > On Fri, Nov 6, 2020 at 11:06 PM Inga Stotland < > > inga.stotland@xxxxxxxxx > > > wrote: > > > This adds a separate implementtion of hciemu code, hciemu-ell.c, > > that uses ELL library primitives. > > > I wonder if this should really be separated like this or we just make > > use of struct io instead of l_io, that way we don't need to keep > > duplicating things on the emulator, the other option would be to drop > > entirely the glib version but I guess you haven't done that because > > there are quite a few dependencies to get rid in order to run the > > tester with ell. > > > I would've made one version of hciemu.c with the unified io, but there's a reference to g_idel_add that has equivalent in ELL, but not the internal bluez libs. > > Also, hciemu is linked with android test build (android/Makefile.am) and android is a completely different animal: from what I can see it relies heavily on GLib > > and I wouldn't know how to test the android stuff once I make the changes. So I chose to err on the side of caution and create a duplicate. Im planning to remove android actually, just need to move a few things around since some unit test uses the android version. > > > > > > --- > > emulator/hciemu-ell.c | 564 ++++++++++++++++++++++++++++++++++++++++++ > > 1 file changed, 564 insertions(+) > > create mode 100644 emulator/hciemu-ell.c > > > diff --git a/emulator/hciemu-ell.c b/emulator/hciemu-ell.c > > new file mode 100644 > > index 000000000..40342e99b > > --- /dev/null > > +++ b/emulator/hciemu-ell.c > > @@ -0,0 +1,564 @@ > > +// SPDX-License-Identifier: LGPL-2.1-or-later > > +/* > > + * > > + * BlueZ - Bluetooth protocol stack for Linux > > + * > > + * Copyright (C) 2012-2014, 2020 Intel Corporation. All rights reserved. > > + * > > + * > > + */ > > + > > +#ifdef HAVE_CONFIG_H > > +#include <config.h> > > +#endif > > + > > +#define _GNU_SOURCE > > +#include <stdio.h> > > +#include <fcntl.h> > > +#include <unistd.h> > > +#include <stdlib.h> > > +#include <string.h> > > +#include <stdbool.h> > > +#include <errno.h> > > +#include <sys/socket.h> > > + > > +#include <ell/ell.h> > > + > > +#include "lib/bluetooth.h" > > +#include "lib/hci.h" > > + > > +#include "monitor/bt.h" > > +#include "emulator/btdev.h" > > +#include "emulator/bthost.h" > > +#include "src/shared/util.h" > > +#include "src/shared/queue.h" > > +#include "emulator/hciemu.h" > > + > > +struct hciemu { > > + int ref_count; > > + enum btdev_type btdev_type; > > + struct bthost *host_stack; > > + struct btdev *master_dev; > > + struct btdev *client_dev; > > + struct l_io *host_io; > > + struct l_io *master_io; > > + struct l_io *client_io; > > + struct queue *post_command_hooks; > > + char bdaddr_str[18]; > > + > > + hciemu_debug_func_t debug_callback; > > + hciemu_destroy_func_t debug_destroy; > > + void *debug_data; > > +}; > > + > > +struct hciemu_command_hook { > > + hciemu_command_func_t function; > > + void *user_data; > > +}; > > + > > +static void destroy_command_hook(void *data) > > +{ > > + struct hciemu_command_hook *hook = data; > > + > > + free(hook); > > +} > > + > > +struct run_data { > > + uint16_t opcode; > > + const void *data; > > + uint8_t len; > > +}; > > + > > +static void run_command_hook(void *data, void *user_data) > > +{ > > + struct hciemu_command_hook *hook = data; > > + struct run_data *run_data = user_data; > > + > > + if (hook->function) > > + hook->function(run_data->opcode, run_data->data, > > + run_data->len, hook->user_data); > > +} > > + > > +static void master_command_callback(uint16_t opcode, > > + const void *data, uint8_t len, > > + btdev_callback callback, void *user_data) > > +{ > > + struct hciemu *hciemu = user_data; > > + struct run_data run_data = { .opcode = opcode, > > + .data = data, .len = len }; > > + > > + btdev_command_default(callback); > > + > > + queue_foreach(hciemu->post_command_hooks, run_command_hook, &run_data); > > +} > > + > > +static void client_command_callback(uint16_t opcode, > > + const void *data, uint8_t len, > > + btdev_callback callback, void *user_data) > > +{ > > + btdev_command_default(callback); > > +} > > + > > +static void writev_callback(const struct iovec *iov, int iovlen, > > + void *user_data) > > +{ > > + struct l_io *io = user_data; > > + ssize_t written; > > + int fd; > > + > > + fd = l_io_get_fd(io); > > + > > + written = writev(fd, iov, iovlen); > > + if (written < 0) > > + return; > > +} > > + > > +static bool receive_bthost(struct l_io *io, void *user_data) > > +{ > > + struct bthost *bthost = user_data; > > + unsigned char buf[4096]; > > + ssize_t len; > > + int fd; > > + > > + fd = l_io_get_fd(io); > > + > > + len = read(fd, buf, sizeof(buf)); > > + if (len < 0) > > + return false; > > + > > + bthost_receive_h4(bthost, buf, len); > > + > > + return true; > > +} > > + > > +static struct l_io *create_io_bthost(int fd, struct bthost *bthost) > > +{ > > + struct l_io *io; > > + > > + io = l_io_new(fd); > > + > > + l_io_set_close_on_destroy(io, true); > > + > > + bthost_set_send_handler(bthost, writev_callback, io); > > + > > + l_io_set_read_handler(io, receive_bthost, bthost, NULL); > > + > > + return io; > > +} > > + > > +static bool receive_btdev(struct l_io *io, void *user_data) > > + > > +{ > > + struct btdev *btdev = user_data; > > + unsigned char buf[4096]; > > + ssize_t len; > > + int fd; > > + > > + fd = l_io_get_fd(io); > > + > > + len = read(fd, buf, sizeof(buf)); > > + if (len < 0) { > > + if (errno == EAGAIN || errno == EINTR) > > + return true; > > + > > + return false; > > + } > > + > > + if (len < 1) > > + return false; > > + > > + switch (buf[0]) { > > + case BT_H4_CMD_PKT: > > + case BT_H4_ACL_PKT: > > + case BT_H4_SCO_PKT: > > + btdev_receive_h4(btdev, buf, len); > > + break; > > + } > > + > > + return true; > > +} > > + > > +static struct l_io *create_io_btdev(int fd, struct btdev *btdev) > > +{ > > + struct l_io *io; > > + > > + io = l_io_new(fd); > > + > > + l_io_set_close_on_destroy(io, true); > > + > > + btdev_set_send_handler(btdev, writev_callback, io); > > + > > + l_io_set_read_handler(io, receive_btdev, btdev, NULL); > > + > > + return io; > > +} > > + > > +static bool create_vhci(struct hciemu *hciemu) > > +{ > > + struct btdev *btdev; > > + uint8_t create_req[2]; > > + ssize_t written; > > + int fd; > > + > > + btdev = btdev_create(hciemu->btdev_type, 0x00); > > + if (!btdev) > > + return false; > > + > > + btdev_set_command_handler(btdev, master_command_callback, hciemu); > > + > > + fd = open("/dev/vhci", O_RDWR | O_NONBLOCK | O_CLOEXEC); > > + if (fd < 0) { > > + perror("Opening /dev/vhci failed"); > > + btdev_destroy(btdev); > > + return false; > > + } > > + > > + create_req[0] = HCI_VENDOR_PKT; > > + create_req[1] = HCI_PRIMARY; > > + > > + written = write(fd, create_req, sizeof(create_req)); > > + if (written < 0) { > > + close(fd); > > + btdev_destroy(btdev); > > + return false; > > + } > > + > > + hciemu->master_dev = btdev; > > + > > + hciemu->master_io = create_io_btdev(fd, btdev); > > + > > + return true; > > +} > > + > > +struct bthost *hciemu_client_get_host(struct hciemu *hciemu) > > +{ > > + if (!hciemu) > > + return NULL; > > + > > + return hciemu->host_stack; > > +} > > + > > +static bool create_stack(struct hciemu *hciemu) > > +{ > > + struct btdev *btdev; > > + struct bthost *bthost; > > + int sv[2]; > > + > > + btdev = btdev_create(hciemu->btdev_type, 0x00); > > + if (!btdev) > > + return false; > > + > > + bthost = bthost_create(); > > + if (!bthost) { > > + btdev_destroy(btdev); > > + return false; > > + } > > + > > + btdev_set_command_handler(btdev, client_command_callback, hciemu); > > + > > + if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, > > + 0, sv) < 0) { > > + bthost_destroy(bthost); > > + btdev_destroy(btdev); > > + return false; > > + } > > + > > + hciemu->client_dev = btdev; > > + hciemu->host_stack = bthost; > > + > > + hciemu->client_io = create_io_btdev(sv[0], btdev); > > + hciemu->host_io = create_io_bthost(sv[1], bthost); > > + > > + return true; > > +} > > + > > +static void start_stack(void *user_data) > > +{ > > + struct hciemu *hciemu = user_data; > > + > > + bthost_start(hciemu->host_stack); > > +} > > + > > +struct hciemu *hciemu_new(enum hciemu_type type) > > +{ > > + struct hciemu *hciemu; > > + > > + hciemu = new0(struct hciemu, 1); > > + if (!hciemu) > > + return NULL; > > + > > + switch (type) { > > + case HCIEMU_TYPE_BREDRLE: > > + hciemu->btdev_type = BTDEV_TYPE_BREDRLE; > > + break; > > + case HCIEMU_TYPE_BREDR: > > + hciemu->btdev_type = BTDEV_TYPE_BREDR; > > + break; > > + case HCIEMU_TYPE_LE: > > + hciemu->btdev_type = BTDEV_TYPE_LE; > > + break; > > + case HCIEMU_TYPE_LEGACY: > > + hciemu->btdev_type = BTDEV_TYPE_BREDR20; > > + break; > > + case HCIEMU_TYPE_BREDRLE50: > > + hciemu->btdev_type = BTDEV_TYPE_BREDRLE50; > > + break; > > + case HCIEMU_TYPE_BREDRLE52: > > + hciemu->btdev_type = BTDEV_TYPE_BREDRLE52; > > + break; > > + default: > > + return NULL; > > + } > > + > > + hciemu->post_command_hooks = queue_new(); > > + if (!hciemu->post_command_hooks) { > > + free(hciemu); > > + return NULL; > > + } > > + > > + if (!create_vhci(hciemu)) { > > + queue_destroy(hciemu->post_command_hooks, NULL); > > + free(hciemu); > > + return NULL; > > + } > > + > > + if (!create_stack(hciemu)) { > > + l_io_destroy(hciemu->master_io); > > + btdev_destroy(hciemu->master_dev); > > + queue_destroy(hciemu->post_command_hooks, NULL); > > + free(hciemu); > > + return NULL; > > + } > > + > > + l_idle_oneshot(start_stack, hciemu, NULL); > > + > > + return hciemu_ref(hciemu); > > +} > > + > > +struct hciemu *hciemu_ref(struct hciemu *hciemu) > > +{ > > + if (!hciemu) > > + return NULL; > > + > > + __sync_fetch_and_add(&hciemu->ref_count, 1); > > + > > + return hciemu; > > +} > > + > > +void hciemu_unref(struct hciemu *hciemu) > > +{ > > + if (!hciemu) > > + return; > > + > > + if (__sync_sub_and_fetch(&hciemu->ref_count, 1)) > > + return; > > + > > + queue_destroy(hciemu->post_command_hooks, destroy_command_hook); > > + > > + l_io_destroy(hciemu->host_io); > > + l_io_destroy(hciemu->client_io); > > + l_io_destroy(hciemu->master_io); > > + > > + bthost_destroy(hciemu->host_stack); > > + btdev_destroy(hciemu->client_dev); > > + btdev_destroy(hciemu->master_dev); > > + > > + free(hciemu); > > +} > > + > > +static void bthost_debug(const char *str, void *user_data) > > +{ > > + struct hciemu *hciemu = user_data; > > + > > + util_debug(hciemu->debug_callback, hciemu->debug_data, > > + "bthost: %s", str); > > +} > > + > > +static void btdev_master_debug(const char *str, void *user_data) > > +{ > > + struct hciemu *hciemu = user_data; > > + > > + util_debug(hciemu->debug_callback, hciemu->debug_data, > > + "btdev: %s", str); > > +} > > + > > +static void btdev_client_debug(const char *str, void *user_data) > > +{ > > + struct hciemu *hciemu = user_data; > > + > > + util_debug(hciemu->debug_callback, hciemu->debug_data, > > + "btdev[bthost]: %s", str); > > +} > > + > > +bool hciemu_set_debug(struct hciemu *hciemu, hciemu_debug_func_t callback, > > + void *user_data, hciemu_destroy_func_t destroy) > > +{ > > + if (!hciemu) > > + return false; > > + > > + if (hciemu->debug_destroy) > > + hciemu->debug_destroy(hciemu->debug_data); > > + > > + hciemu->debug_callback = callback; > > + hciemu->debug_destroy = destroy; > > + hciemu->debug_data = user_data; > > + > > + btdev_set_debug(hciemu->master_dev, btdev_master_debug, hciemu, NULL); > > + btdev_set_debug(hciemu->client_dev, btdev_client_debug, hciemu, NULL); > > + bthost_set_debug(hciemu->host_stack, bthost_debug, hciemu, NULL); > > + > > + return true; > > +} > > + > > +const char *hciemu_get_address(struct hciemu *hciemu) > > +{ > > + const uint8_t *addr; > > + > > + if (!hciemu || !hciemu->master_dev) > > + return NULL; > > + > > + addr = btdev_get_bdaddr(hciemu->master_dev); > > + sprintf(hciemu->bdaddr_str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", > > + addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); > > + return hciemu->bdaddr_str; > > +} > > + > > +uint8_t *hciemu_get_features(struct hciemu *hciemu) > > +{ > > + if (!hciemu || !hciemu->master_dev) > > + return NULL; > > + > > + return btdev_get_features(hciemu->master_dev); > > +} > > + > > +const uint8_t *hciemu_get_master_bdaddr(struct hciemu *hciemu) > > +{ > > + if (!hciemu || !hciemu->master_dev) > > + return NULL; > > + > > + return btdev_get_bdaddr(hciemu->master_dev); > > +} > > + > > +const uint8_t *hciemu_get_client_bdaddr(struct hciemu *hciemu) > > +{ > > + if (!hciemu || !hciemu->client_dev) > > + return NULL; > > + > > + return btdev_get_bdaddr(hciemu->client_dev); > > +} > > + > > +uint8_t hciemu_get_master_scan_enable(struct hciemu *hciemu) > > +{ > > + if (!hciemu || !hciemu->master_dev) > > + return 0; > > + > > + return btdev_get_scan_enable(hciemu->master_dev); > > +} > > + > > +uint8_t hciemu_get_master_le_scan_enable(struct hciemu *hciemu) > > +{ > > + if (!hciemu || !hciemu->master_dev) > > + return 0; > > + > > + return btdev_get_le_scan_enable(hciemu->master_dev); > > +} > > + > > +void hciemu_set_master_le_states(struct hciemu *hciemu, > > + const uint8_t *le_states) > > +{ > > + if (!hciemu || !hciemu->master_dev) > > + return; > > + > > + btdev_set_le_states(hciemu->master_dev, le_states); > > +} > > + > > +bool hciemu_add_master_post_command_hook(struct hciemu *hciemu, > > + hciemu_command_func_t function, void *user_data) > > +{ > > + struct hciemu_command_hook *hook; > > + > > + if (!hciemu) > > + return false; > > + > > + hook = new0(struct hciemu_command_hook, 1); > > + if (!hook) > > + return false; > > + > > + hook->function = function; > > + hook->user_data = user_data; > > + > > + if (!queue_push_tail(hciemu->post_command_hooks, hook)) { > > + free(hook); > > + return false; > > + } > > + > > + return true; > > +} > > + > > +bool hciemu_clear_master_post_command_hooks(struct hciemu *hciemu) > > +{ > > + if (!hciemu) > > + return false; > > + > > + queue_remove_all(hciemu->post_command_hooks, > > + NULL, NULL, destroy_command_hook); > > + return true; > > +} > > + > > +int hciemu_add_hook(struct hciemu *hciemu, enum hciemu_hook_type type, > > + uint16_t opcode, hciemu_hook_func_t function, > > + void *user_data) > > +{ > > + enum btdev_hook_type hook_type; > > + > > + if (!hciemu) > > + return -1; > > + > > + switch (type) { > > + case HCIEMU_HOOK_PRE_CMD: > > + hook_type = BTDEV_HOOK_PRE_CMD; > > + break; > > + case HCIEMU_HOOK_POST_CMD: > > + hook_type = BTDEV_HOOK_POST_CMD; > > + break; > > + case HCIEMU_HOOK_PRE_EVT: > > + hook_type = BTDEV_HOOK_PRE_EVT; > > + break; > > + case HCIEMU_HOOK_POST_EVT: > > + hook_type = BTDEV_HOOK_POST_EVT; > > + break; > > + default: > > + return -1; > > + } > > + > > + return btdev_add_hook(hciemu->master_dev, hook_type, opcode, function, > > + user_data); > > +} > > + > > +bool hciemu_del_hook(struct hciemu *hciemu, enum hciemu_hook_type type, > > + uint16_t opcode) > > +{ > > + enum btdev_hook_type hook_type; > > + > > + if (!hciemu) > > + return false; > > + > > + switch (type) { > > + case HCIEMU_HOOK_PRE_CMD: > > + hook_type = BTDEV_HOOK_PRE_CMD; > > + break; > > + case HCIEMU_HOOK_POST_CMD: > > + hook_type = BTDEV_HOOK_POST_CMD; > > + break; > > + case HCIEMU_HOOK_PRE_EVT: > > + hook_type = BTDEV_HOOK_PRE_EVT; > > + break; > > + case HCIEMU_HOOK_POST_EVT: > > + hook_type = BTDEV_HOOK_POST_EVT; > > + break; > > + default: > > + return false; > > + } > > + > > + return btdev_del_hook(hciemu->master_dev, hook_type, opcode); > > +} > > -- > > 2.26.2 > > > > -- Luiz Augusto von Dentz