Re: [RFC PATCH BlueZ 02/10] emulator/hciemu: Create ELL based version of hciemu

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.

> ---
>  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



[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux