2020-11-07 07:04:08

by Stotland, Inga

[permalink] [raw]
Subject: [RFC PATCH BlueZ 00/10] Convert tools to use ELL library

This patch set is a part of ongoing effort to remove GLib dependencies
from BlueZ in favor of using ELL library.

bluez/tools subdirectory is chosen as a first candidate since
it provides standalone functionality making it easier to adress the
issues discovered during the conversion process.

The patch set is a snapshot of *some* tools converted to use ELL as well
as ELL based versions of emulator/hciemu-ell.c and shared/tester-ell.c.

Inga Stotland (10):
shared/tester-ell: Create ell-based version of tester code
emulator/hciemu: Create ELL based version of hciemu
tools/gap-tester: Convert to use ELL library
tools/sco-tester: Convert to use ELL library
tools/userchan-tester: Convert to use ELL library
tools/smp-tester: Convert to use ELL library
tools/bnep-tester: Convert to use ELL library
tools/l2cap-tester: Convert to use ELL library
tools/mgmt-tester: Convert to use ELL library
tools/rfcomm-tester: Convert to use ELL library

Makefile.am | 8 +-
Makefile.tools | 34 +-
emulator/hciemu-ell.c | 564 ++++++++++++++++++++++++++
src/shared/tester-ell.c | 875 ++++++++++++++++++++++++++++++++++++++++
tools/bnep-tester.c | 9 +-
tools/gap-tester.c | 87 ++--
tools/l2cap-tester.c | 256 ++++++------
tools/mgmt-tester.c | 5 +-
tools/rfcomm-tester.c | 92 +++--
tools/sco-tester.c | 33 +-
tools/smp-tester.c | 24 +-
tools/userchan-tester.c | 2 -
12 files changed, 1715 insertions(+), 274 deletions(-)
create mode 100644 emulator/hciemu-ell.c
create mode 100644 src/shared/tester-ell.c

--
2.26.2


2020-11-07 07:04:08

by Stotland, Inga

[permalink] [raw]
Subject: [RFC PATCH BlueZ 04/10] tools/sco-tester: Convert to use ELL library

This reworks the source code to use ELL primitives and removes
dependecies on GLib.
---
Makefile.tools | 4 ++--
tools/sco-tester.c | 33 ++++++++++++++-------------------
2 files changed, 16 insertions(+), 21 deletions(-)

diff --git a/Makefile.tools b/Makefile.tools
index 259427443..15613a5c9 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -154,12 +154,12 @@ tools_gap_tester_LDADD = lib/libbluetooth-internal.la \
src/libshared-ell.la $(ell_ldadd)

tools_sco_tester_SOURCES = tools/sco-tester.c monitor/bt.h \
- emulator/hciemu.h emulator/hciemu.c \
+ emulator/hciemu.h emulator/hciemu-ell.c \
emulator/btdev.h emulator/btdev.c \
emulator/bthost.h emulator/bthost.c \
emulator/smp.c
tools_sco_tester_LDADD = lib/libbluetooth-internal.la \
- src/libshared-glib.la $(GLIB_LIBS)
+ src/libshared-ell.la $(ell_ldadd)

tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h
tools_hci_tester_LDADD = src/libshared-glib.la $(GLIB_LIBS)
diff --git a/tools/sco-tester.c b/tools/sco-tester.c
index 2b8dc0d4a..c3c43a1c3 100644
--- a/tools/sco-tester.c
+++ b/tools/sco-tester.c
@@ -17,7 +17,7 @@
#include <errno.h>
#include <stdbool.h>

-#include <glib.h>
+#include <ell/ell.h>

#include "lib/bluetooth.h"
#include "lib/sco.h"
@@ -36,7 +36,7 @@ struct test_data {
uint16_t mgmt_index;
struct hciemu *hciemu;
enum hciemu_type hciemu_type;
- unsigned int io_id;
+ struct l_io *io;
bool disable_esco;
};

@@ -196,20 +196,21 @@ static void test_data_free(void *test_data)
{
struct test_data *data = test_data;

- if (data->io_id > 0)
- g_source_remove(data->io_id);
+ if (data->io) {
+ l_io_destroy(data->io);
+ data->io = NULL;
+ }

- free(data);
+ l_free(data);
}

#define test_sco_full(name, data, setup, func, _disable_esco) \
do { \
struct test_data *user; \
- user = malloc(sizeof(struct test_data)); \
+ user = l_new(struct test_data, 1); \
if (!user) \
break; \
user->hciemu_type = HCIEMU_TYPE_BREDRLE; \
- user->io_id = 0; \
user->test_data = data; \
user->disable_esco = _disable_esco; \
tester_add_full(name, data, \
@@ -474,17 +475,14 @@ static int connect_sco_sock(struct test_data *data, int sk)
return 0;
}

-static gboolean sco_connect_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
+static bool sco_connect_cb(struct l_io *io, void *user_data)
{
struct test_data *data = tester_get_data();
const struct sco_client_data *scodata = data->test_data;
int err, sk_err, sk;
socklen_t len = sizeof(sk_err);

- data->io_id = 0;
-
- sk = g_io_channel_unix_get_fd(io);
+ sk = l_io_get_fd(io);

if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
err = -errno;
@@ -501,13 +499,12 @@ static gboolean sco_connect_cb(GIOChannel *io, GIOCondition cond,
else
tester_test_passed();

- return FALSE;
+ return false;
}

static void test_connect(const void *test_data)
{
struct test_data *data = tester_get_data();
- GIOChannel *io;
int sk;

sk = create_sco_sock(data);
@@ -522,12 +519,10 @@ static void test_connect(const void *test_data)
return;
}

- io = g_io_channel_unix_new(sk);
- g_io_channel_set_close_on_unref(io, TRUE);
-
- data->io_id = g_io_add_watch(io, G_IO_OUT, sco_connect_cb, NULL);
+ data->io = l_io_new(sk);
+ l_io_set_close_on_destroy(data->io, true);

- g_io_channel_unref(io);
+ l_io_set_write_handler(data->io, sco_connect_cb, NULL, NULL);

tester_print("Connect in progress");
}
--
2.26.2

2020-11-07 07:04:08

by Stotland, Inga

[permalink] [raw]
Subject: [RFC PATCH BlueZ 03/10] tools/gap-tester: Convert to use ELL library

This reworks the source code to use ELL primitives and removes
dependecies on GLib.
---
Makefile.tools | 6 ++--
tools/gap-tester.c | 87 ++++++++++++++++++++++++++--------------------
2 files changed, 51 insertions(+), 42 deletions(-)

diff --git a/Makefile.tools b/Makefile.tools
index d5fdf2d89..259427443 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -146,14 +146,12 @@ tools_smp_tester_LDADD = lib/libbluetooth-internal.la \
src/libshared-glib.la $(GLIB_LIBS)

tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \
- emulator/hciemu.h emulator/hciemu.c \
+ emulator/hciemu.h emulator/hciemu-ell.c \
emulator/btdev.h emulator/btdev.c \
emulator/bthost.h emulator/bthost.c \
emulator/smp.c
tools_gap_tester_LDADD = lib/libbluetooth-internal.la \
- gdbus/libgdbus-internal.la \
- src/libshared-glib.la \
- $(GLIB_LIBS) $(DBUS_LIBS)
+ src/libshared-ell.la $(ell_ldadd)

tools_sco_tester_SOURCES = tools/sco-tester.c monitor/bt.h \
emulator/hciemu.h emulator/hciemu.c \
diff --git a/tools/gap-tester.c b/tools/gap-tester.c
index 942c37d27..2797a60a8 100644
--- a/tools/gap-tester.c
+++ b/tools/gap-tester.c
@@ -12,60 +12,72 @@
#include <config.h>
#endif

-#include "gdbus/gdbus.h"
+#include <ell/ell.h>

#include "src/shared/tester.h"
#include "emulator/hciemu.h"

-static DBusConnection *dbus_conn = NULL;
-static GDBusClient *dbus_client = NULL;
-static GDBusProxy *adapter_proxy = NULL;
+static struct l_dbus *dbus_conn;
+struct l_dbus_client *dbus_client;
+struct l_dbus_proxy *adapter_proxy;

-static struct hciemu *hciemu_stack = NULL;
+static struct hciemu *hciemu_stack;

-static void connect_handler(DBusConnection *connection, void *user_data)
+static void connect_handler(struct l_dbus *connection, void *user_data)
{
tester_print("Connected to daemon");

hciemu_stack = hciemu_new(HCIEMU_TYPE_BREDRLE);
}

-static void disconnect_handler(DBusConnection *connection, void *user_data)
+static void destroy_client(void *data)
{
- tester_print("Disconnected from daemon");
+ l_dbus_client_destroy(dbus_client);
+ dbus_client = NULL;
+}

- dbus_connection_unref(dbus_conn);
+static void destroy_conn(void *data)
+{
+ l_dbus_destroy(dbus_conn);
dbus_conn = NULL;
+}

- tester_teardown_complete();
+static void service_disconnect_handler(struct l_dbus *connection,
+ void *user_data)
+{
+ tester_print("Daemon disconnected");
}

-static gboolean compare_string_property(GDBusProxy *proxy, const char *name,
- const char *value)
+static void client_destroy_handler(void *user_data)
{
- DBusMessageIter iter;
- const char *str;
+ tester_print("Disconnected from daemon");

- if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE)
- return FALSE;
+ if (dbus_conn)
+ l_idle_oneshot(destroy_conn, NULL, NULL);

- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
- return FALSE;
+ tester_teardown_complete();
+}

- dbus_message_iter_get_basic(&iter, &str);
+static bool compare_string_property(struct l_dbus_proxy *proxy,
+ const char *name, const char *value)
+{
+ const char *str;

- return g_str_equal(str, value);
+ if (!l_dbus_proxy_get_property(proxy, name, "s", &str))
+ return false;
+
+ return !strcmp(str, value);
}

-static void proxy_added(GDBusProxy *proxy, void *user_data)
+static void proxy_added(struct l_dbus_proxy *proxy, void *user_data)
{
const char *interface;

- interface = g_dbus_proxy_get_interface(proxy);
+ interface = l_dbus_proxy_get_interface(proxy);

- if (g_str_equal(interface, "org.bluez.Adapter1") == TRUE) {
+ if (!strcmp(interface, "org.bluez.Adapter1")) {
if (compare_string_property(proxy, "Address",
- hciemu_get_address(hciemu_stack)) == TRUE) {
+ hciemu_get_address(hciemu_stack))) {
adapter_proxy = proxy;
tester_print("Found adapter");

@@ -74,35 +86,34 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
}
}

-static void proxy_removed(GDBusProxy *proxy, void *user_data)
+static void proxy_removed(struct l_dbus_proxy *proxy, void *user_data)
{
const char *interface;

- interface = g_dbus_proxy_get_interface(proxy);
+ interface = l_dbus_proxy_get_interface(proxy);

- if (g_str_equal(interface, "org.bluez.Adapter1") == TRUE) {
+ if (!strcmp(interface, "org.bluez.Adapter1")) {
if (adapter_proxy == proxy) {
adapter_proxy = NULL;
tester_print("Adapter removed");
-
- g_dbus_client_unref(dbus_client);
- dbus_client = NULL;
+ l_idle_oneshot(destroy_client, NULL, NULL);
}
}
}

static void test_setup(const void *test_data)
{
- dbus_conn = g_dbus_setup_private(DBUS_BUS_SYSTEM, NULL, NULL);
-
- dbus_client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+ dbus_conn = l_dbus_new_default(L_DBUS_SYSTEM_BUS);

- g_dbus_client_set_connect_watch(dbus_client, connect_handler, NULL);
- g_dbus_client_set_disconnect_watch(dbus_client,
- disconnect_handler, NULL);
+ dbus_client = l_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+ l_dbus_client_set_connect_handler(dbus_client, connect_handler, NULL,
+ NULL);
+ l_dbus_client_set_disconnect_handler(dbus_client,
+ service_disconnect_handler,
+ NULL, client_destroy_handler);

- g_dbus_client_set_proxy_handlers(dbus_client, proxy_added,
- proxy_removed, NULL, NULL);
+ l_dbus_client_set_proxy_handlers(dbus_client, proxy_added,
+ proxy_removed, NULL, NULL, NULL);
}

static void test_run(const void *test_data)
--
2.26.2

2020-11-07 07:04:08

by Stotland, Inga

[permalink] [raw]
Subject: [RFC PATCH BlueZ 06/10] tools/smp-tester: Convert to use ELL library

This reworks the source code to use ELL primitives and removes
dependecies on GLib.
---
Makefile.tools | 4 ++--
tools/smp-tester.c | 24 +++++++++---------------
2 files changed, 11 insertions(+), 17 deletions(-)

diff --git a/Makefile.tools b/Makefile.tools
index 7c29e115b..83c30252d 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -138,12 +138,12 @@ tools_bnep_tester_LDADD = lib/libbluetooth-internal.la \
src/libshared-glib.la $(GLIB_LIBS)

tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \
- emulator/hciemu.h emulator/hciemu.c \
+ emulator/hciemu.h emulator/hciemu-ell.c \
emulator/btdev.h emulator/btdev.c \
emulator/bthost.h emulator/bthost.c \
emulator/smp.c
tools_smp_tester_LDADD = lib/libbluetooth-internal.la \
- src/libshared-glib.la $(GLIB_LIBS)
+ src/libshared-ell.la $(ell_ldadd)

tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \
emulator/hciemu.h emulator/hciemu-ell.c \
diff --git a/tools/smp-tester.c b/tools/smp-tester.c
index 7823b6b17..b7843c73d 100644
--- a/tools/smp-tester.c
+++ b/tools/smp-tester.c
@@ -18,7 +18,7 @@
#include <stdbool.h>
#include <sys/socket.h>

-#include <glib.h>
+#include <ell/ell.h>

#include "lib/bluetooth.h"
#include "lib/hci.h"
@@ -41,7 +41,6 @@ struct test_data {
uint16_t mgmt_index;
struct hciemu *hciemu;
enum hciemu_type hciemu_type;
- unsigned int io_id;
uint8_t ia[6];
uint8_t ia_type;
uint8_t ra[6];
@@ -223,11 +222,6 @@ static void test_post_teardown(const void *test_data)
{
struct test_data *data = tester_get_data();

- if (data->io_id > 0) {
- g_source_remove(data->io_id);
- data->io_id = 0;
- }
-
if (data->crypto) {
bt_crypto_unref(data->crypto);
data->crypto = NULL;
@@ -287,7 +281,7 @@ static const struct smp_req_rsp nval_req_1[] = {

static const struct smp_data smp_server_nval_req_1_test = {
.req = nval_req_1,
- .req_count = G_N_ELEMENTS(nval_req_1),
+ .req_count = L_ARRAY_SIZE(nval_req_1),
};

static const uint8_t smp_nval_req_2[7] = { 0x01 };
@@ -300,7 +294,7 @@ static const struct smp_req_rsp srv_nval_req_1[] = {

static const struct smp_data smp_server_nval_req_2_test = {
.req = srv_nval_req_1,
- .req_count = G_N_ELEMENTS(srv_nval_req_1),
+ .req_count = L_ARRAY_SIZE(srv_nval_req_1),
};

static const uint8_t smp_nval_req_3[] = { 0x01, 0xff };
@@ -313,7 +307,7 @@ static const struct smp_req_rsp srv_nval_req_2[] = {

static const struct smp_data smp_server_nval_req_3_test = {
.req = srv_nval_req_2,
- .req_count = G_N_ELEMENTS(srv_nval_req_2),
+ .req_count = L_ARRAY_SIZE(srv_nval_req_2),
};

static const uint8_t smp_basic_req_1[] = { 0x01, /* Pairing Request */
@@ -347,7 +341,7 @@ static const struct smp_req_rsp srv_basic_req_1[] = {

static const struct smp_data smp_server_basic_req_1_test = {
.req = srv_basic_req_1,
- .req_count = G_N_ELEMENTS(srv_basic_req_1),
+ .req_count = L_ARRAY_SIZE(srv_basic_req_1),
};

static const struct smp_req_rsp cli_basic_req_1[] = {
@@ -361,7 +355,7 @@ static const struct smp_req_rsp cli_basic_req_1[] = {

static const struct smp_data smp_client_basic_req_1_test = {
.req = cli_basic_req_1,
- .req_count = G_N_ELEMENTS(cli_basic_req_1),
+ .req_count = L_ARRAY_SIZE(cli_basic_req_1),
};

static const uint8_t smp_basic_req_2[] = { 0x01, /* Pairing Request */
@@ -384,7 +378,7 @@ static const struct smp_req_rsp cli_basic_req_2[] = {

static const struct smp_data smp_client_basic_req_2_test = {
.req = cli_basic_req_2,
- .req_count = G_N_ELEMENTS(cli_basic_req_1),
+ .req_count = L_ARRAY_SIZE(cli_basic_req_1),
.mitm = true,
};

@@ -423,7 +417,7 @@ static const struct smp_req_rsp cli_sc_req_1[] = {

static const struct smp_data smp_client_sc_req_1_test = {
.req = cli_sc_req_1,
- .req_count = G_N_ELEMENTS(cli_sc_req_1),
+ .req_count = L_ARRAY_SIZE(cli_sc_req_1),
.sc = true,
};

@@ -449,7 +443,7 @@ static const struct smp_req_rsp cli_sc_req_2[] = {

static const struct smp_data smp_client_sc_req_2_test = {
.req = cli_sc_req_2,
- .req_count = G_N_ELEMENTS(cli_sc_req_2),
+ .req_count = L_ARRAY_SIZE(cli_sc_req_2),
.sc = true,
};

--
2.26.2

2020-11-07 07:04:09

by Stotland, Inga

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

This adds a separate implementtion of hciemu code, hciemu-ell.c,
that uses ELL library primitives.
---
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

2020-11-07 07:04:09

by Stotland, Inga

[permalink] [raw]
Subject: [RFC PATCH BlueZ 01/10] shared/tester-ell: Create ell-based version of tester code

Create a version of tester that uses ell primitives instead of glib:
tester-ell.c. This source is included to generate lishared-ell library.
The original tester.c is built as part of libshared-glib library.
---
Makefile.am | 8 +-
src/shared/tester-ell.c | 875 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 880 insertions(+), 3 deletions(-)
create mode 100644 src/shared/tester-ell.c

diff --git a/Makefile.am b/Makefile.am
index 69b95828f..952b9cc5b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -191,7 +191,7 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
src/shared/crypto.h src/shared/crypto.c \
src/shared/ecc.h src/shared/ecc.c \
src/shared/ringbuf.h src/shared/ringbuf.c \
- src/shared/tester.h src/shared/tester.c \
+ src/shared/tester.h\
src/shared/hci.h src/shared/hci.c \
src/shared/hci-crypto.h src/shared/hci-crypto.c \
src/shared/hfp.h src/shared/hfp.c \
@@ -218,7 +218,8 @@ src_libshared_glib_la_SOURCES = $(shared_sources) \
src/shared/timeout-glib.c \
src/shared/mainloop-glib.c \
src/shared/mainloop-notify.h \
- src/shared/mainloop-notify.c
+ src/shared/mainloop-notify.c \
+ src/shared/tester.c

src_libshared_mainloop_la_SOURCES = $(shared_sources) \
src/shared/io-mainloop.c \
@@ -232,7 +233,8 @@ src_libshared_ell_la_SOURCES = $(shared_sources) \
src/shared/io-ell.c \
src/shared/timeout-ell.c \
src/shared/mainloop.h \
- src/shared/mainloop-ell.c
+ src/shared/mainloop-ell.c \
+ src/shared/tester-ell.c
endif

attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \
diff --git a/src/shared/tester-ell.c b/src/shared/tester-ell.c
new file mode 100644
index 000000000..4d7b794b9
--- /dev/null
+++ b/src/shared/tester-ell.c
@@ -0,0 +1,875 @@
+// 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 <getopt.h>
+#include <stdio.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <ell/ell.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include "src/shared/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+#include "src/shared/log.h"
+
+#define COLOR_OFF "\x1B[0m"
+#define COLOR_BLACK "\x1B[0;30m"
+#define COLOR_RED "\x1B[0;31m"
+#define COLOR_GREEN "\x1B[0;32m"
+#define COLOR_YELLOW "\x1B[0;33m"
+#define COLOR_BLUE "\x1B[0;34m"
+#define COLOR_MAGENTA "\x1B[0;35m"
+#define COLOR_CYAN "\x1B[0;36m"
+#define COLOR_WHITE "\x1B[0;37m"
+#define COLOR_HIGHLIGHT "\x1B[1;39m"
+
+#define print_text(color, fmt, args...) \
+ tester_log(color fmt COLOR_OFF, ## args)
+
+#define print_summary(label, color, value, fmt, args...) \
+ tester_log("%-52s " color "%-10s" COLOR_OFF fmt, \
+ label, value, ## args)
+
+#define print_progress(name, color, fmt, args...) \
+ tester_log(COLOR_HIGHLIGHT "%s" COLOR_OFF " - " \
+ color fmt COLOR_OFF, name, ## args)
+
+enum test_result {
+ TEST_RESULT_NOT_RUN,
+ TEST_RESULT_PASSED,
+ TEST_RESULT_FAILED,
+ TEST_RESULT_TIMED_OUT,
+};
+
+enum test_stage {
+ TEST_STAGE_INVALID,
+ TEST_STAGE_PRE_SETUP,
+ TEST_STAGE_SETUP,
+ TEST_STAGE_RUN,
+ TEST_STAGE_TEARDOWN,
+ TEST_STAGE_POST_TEARDOWN,
+};
+
+struct test_case {
+ char *name;
+ enum test_result result;
+ enum test_stage stage;
+ const void *test_data;
+ tester_data_func_t pre_setup_func;
+ tester_data_func_t setup_func;
+ tester_data_func_t test_func;
+ tester_data_func_t teardown_func;
+ tester_data_func_t post_teardown_func;
+ double start_time;
+ double end_time;
+ unsigned int timeout;
+ struct l_timeout *run_timer;
+ tester_destroy_func_t destroy;
+ void *user_data;
+ bool teardown;
+};
+
+static char *tester_name;
+
+static struct l_queue *test_list;
+static const struct l_queue_entry *test_entry;
+static struct timeval tester_start;
+
+static bool option_quiet;
+static bool option_debug;
+static bool option_monitor;
+static bool option_list;
+static const char *option_prefix;
+
+struct monitor_hdr {
+ uint16_t opcode;
+ uint16_t index;
+ uint16_t len;
+ uint8_t priority;
+ uint8_t ident_len;
+} __attribute__((packed));
+
+struct monitor_l2cap_hdr {
+ uint16_t cid;
+ uint16_t psm;
+} __attribute__((packed));
+
+static void test_destroy(void *data)
+{
+ struct test_case *test = data;
+
+ l_timeout_remove(test->run_timer);
+
+ if (test->destroy)
+ test->destroy(test->user_data);
+
+ l_free(test->name);
+ l_free(test);
+}
+
+static void tester_vprintf(const char *format, va_list ap)
+{
+ if (tester_use_quiet())
+ return;
+
+ printf(" %s", COLOR_WHITE);
+ vprintf(format, ap);
+ printf("%s\n", COLOR_OFF);
+}
+
+static void tester_log(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vprintf(format, ap);
+ printf("\n");
+ va_end(ap);
+
+ va_start(ap, format);
+ bt_log_vprintf(HCI_DEV_NONE, tester_name, LOG_INFO, format, ap);
+ va_end(ap);
+}
+
+void tester_print(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ tester_vprintf(format, ap);
+ va_end(ap);
+
+ va_start(ap, format);
+ bt_log_vprintf(HCI_DEV_NONE, tester_name, LOG_INFO, format, ap);
+ va_end(ap);
+}
+
+void tester_debug(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ tester_vprintf(format, ap);
+ va_end(ap);
+
+ va_start(ap, format);
+ bt_log_vprintf(HCI_DEV_NONE, tester_name, LOG_DEBUG, format, ap);
+ va_end(ap);
+}
+
+void tester_warn(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ tester_vprintf(format, ap);
+ va_end(ap);
+
+ va_start(ap, format);
+ bt_log_vprintf(HCI_DEV_NONE, tester_name, LOG_WARNING, format, ap);
+ va_end(ap);
+}
+
+static void monitor_debug(const char *str, void *user_data)
+{
+ const char *label = user_data;
+
+ tester_debug("%s: %s", label, str);
+}
+
+static void monitor_log(char dir, uint16_t cid, uint16_t psm, const void *data,
+ size_t len)
+{
+ struct iovec iov[3];
+ struct monitor_l2cap_hdr hdr;
+ uint8_t term = 0x00;
+ char label[16];
+
+ if (snprintf(label, sizeof(label), "%c %s", dir, tester_name) < 0)
+ return;
+
+ hdr.cid = cpu_to_le16(cid);
+ hdr.psm = cpu_to_le16(psm);
+
+ iov[0].iov_base = &hdr;
+ iov[0].iov_len = sizeof(hdr);
+
+ iov[1].iov_base = (void *) data;
+ iov[1].iov_len = len;
+
+ /* Kernel won't forward if data is no NULL terminated */
+ iov[2].iov_base = &term;
+ iov[2].iov_len = sizeof(term);
+
+ bt_log_sendmsg(HCI_DEV_NONE, label, LOG_INFO, iov, 3);
+}
+
+void tester_monitor(char dir, uint16_t cid, uint16_t psm, const void *data,
+ size_t len)
+{
+ monitor_log(dir, cid, psm, data, len);
+
+ if (!tester_use_debug())
+ return;
+
+ util_hexdump(dir, data, len, monitor_debug, (void *) tester_name);
+}
+
+static void default_pre_setup(const void *test_data)
+{
+ tester_pre_setup_complete();
+}
+
+static void default_setup(const void *test_data)
+{
+ tester_setup_complete();
+}
+
+static void default_teardown(const void *test_data)
+{
+ tester_teardown_complete();
+}
+
+static void default_post_teardown(const void *test_data)
+{
+ tester_post_teardown_complete();
+}
+
+void tester_add_full(const char *name, const void *test_data,
+ tester_data_func_t pre_setup_func,
+ tester_data_func_t setup_func,
+ tester_data_func_t test_func,
+ tester_data_func_t teardown_func,
+ tester_data_func_t post_teardown_func,
+ unsigned int timeout,
+ void *user_data, tester_destroy_func_t destroy)
+{
+ struct test_case *test;
+
+ if (!test_func)
+ return;
+
+ if (option_prefix && !l_str_has_prefix(name, option_prefix)) {
+ if (destroy)
+ destroy(user_data);
+ return;
+ }
+
+ if (option_list) {
+ tester_log("%s", name);
+ if (destroy)
+ destroy(user_data);
+ return;
+ }
+
+ test = l_new(struct test_case, 1);
+ test->name = l_strdup(name);
+ test->result = TEST_RESULT_NOT_RUN;
+ test->stage = TEST_STAGE_INVALID;
+
+ test->test_data = test_data;
+
+ if (pre_setup_func)
+ test->pre_setup_func = pre_setup_func;
+ else
+ test->pre_setup_func = default_pre_setup;
+
+ if (setup_func)
+ test->setup_func = setup_func;
+ else
+ test->setup_func = default_setup;
+
+ test->test_func = test_func;
+
+ if (teardown_func)
+ test->teardown_func = teardown_func;
+ else
+ test->teardown_func = default_teardown;
+
+ if (post_teardown_func)
+ test->post_teardown_func = post_teardown_func;
+ else
+ test->post_teardown_func = default_post_teardown;
+
+ test->timeout = timeout;
+
+ test->destroy = destroy;
+ test->user_data = user_data;
+
+ l_queue_push_tail(test_list, test);
+}
+
+void tester_add(const char *name, const void *test_data,
+ tester_data_func_t setup_func,
+ tester_data_func_t test_func,
+ tester_data_func_t teardown_func)
+{
+ tester_add_full(name, test_data, NULL, setup_func, test_func,
+ teardown_func, NULL, 0, NULL, NULL);
+}
+
+void *tester_get_data(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return NULL;
+
+ test = test_entry->data;
+
+ return test->user_data;
+}
+
+static double get_elapsed_time(struct timeval *base)
+{
+ static struct timeval now, elapsed;
+
+ gettimeofday(&now, NULL);
+ timersub(&now, base, &elapsed);
+
+ return elapsed.tv_sec + ((double) elapsed.tv_usec) / 1000000;
+}
+
+static int tester_summarize(void)
+{
+ unsigned int not_run = 0, passed = 0, failed = 0;
+ double execution_time;
+ const struct l_queue_entry *entry;
+
+ tester_log("");
+ print_text(COLOR_HIGHLIGHT, "");
+ print_text(COLOR_HIGHLIGHT, "Test Summary");
+ print_text(COLOR_HIGHLIGHT, "------------");
+
+ entry = l_queue_get_entries(test_list);
+
+ for (; entry; entry = entry->next) {
+ struct test_case *test = entry->data;
+ double exec_time;
+
+ exec_time = test->end_time - test->start_time;
+
+ switch (test->result) {
+ case TEST_RESULT_NOT_RUN:
+ print_summary(test->name, COLOR_YELLOW, "Not Run", "");
+ not_run++;
+ break;
+ case TEST_RESULT_PASSED:
+ print_summary(test->name, COLOR_GREEN, "Passed",
+ "%8.3f seconds", exec_time);
+ passed++;
+ break;
+ case TEST_RESULT_FAILED:
+ print_summary(test->name, COLOR_RED, "Failed",
+ "%8.3f seconds", exec_time);
+ failed++;
+ break;
+ case TEST_RESULT_TIMED_OUT:
+ print_summary(test->name, COLOR_RED, "Timed out",
+ "%8.3f seconds", exec_time);
+ failed++;
+ break;
+ }
+ }
+
+ tester_log("Total: %d, "
+ COLOR_GREEN "Passed: %d (%.1f%%)" COLOR_OFF ", "
+ COLOR_RED "Failed: %d" COLOR_OFF ", "
+ COLOR_YELLOW "Not Run: %d" COLOR_OFF,
+ not_run + passed + failed, passed,
+ (not_run + passed + failed) ?
+ (float) passed * 100 / (not_run + passed + failed) : 0,
+ failed, not_run);
+
+ execution_time = get_elapsed_time(&tester_start);
+ tester_log("Overall execution time: %.3g seconds", execution_time);
+
+ return failed;
+}
+
+static void teardown_callback(void *user_data)
+{
+ struct test_case *test = user_data;
+
+ test->stage = TEST_STAGE_TEARDOWN;
+ test->teardown = false;
+
+ print_progress(test->name, COLOR_MAGENTA, "teardown");
+ test->teardown_func(test->test_data);
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_DO_ADDED_LEAK_CHECK;
+#endif
+}
+
+static void test_timeout(struct l_timeout *timer, void *user_data)
+{
+ struct test_case *test = user_data;
+
+ l_timeout_remove(timer);
+ test->run_timer = NULL;
+
+ test->result = TEST_RESULT_TIMED_OUT;
+ print_progress(test->name, COLOR_RED, "test timed out");
+
+ l_idle_oneshot(teardown_callback, test, NULL);
+}
+
+static void next_test_case(void)
+{
+ struct test_case *test;
+
+ if (test_entry)
+ test_entry = test_entry->next;
+ else
+ test_entry = l_queue_get_entries(test_list);
+
+ if (!test_entry) {
+ mainloop_quit();
+ return;
+ }
+
+ test = test_entry->data;
+
+ tester_log("");
+ print_progress(test->name, COLOR_BLACK, "init");
+
+ test->start_time = get_elapsed_time(&tester_start);
+
+ if (test->timeout > 0)
+ test->run_timer = l_timeout_create(test->timeout, test_timeout,
+ test, NULL);
+
+ test->stage = TEST_STAGE_PRE_SETUP;
+
+ test->pre_setup_func(test->test_data);
+}
+
+static void setup_callback(void *user_data)
+{
+ struct test_case *test = user_data;
+
+ test->stage = TEST_STAGE_SETUP;
+
+ print_progress(test->name, COLOR_BLUE, "setup");
+ test->setup_func(test->test_data);
+}
+
+static void run_callback(void *user_data)
+{
+ struct test_case *test = user_data;
+
+ test->stage = TEST_STAGE_RUN;
+
+ print_progress(test->name, COLOR_BLACK, "run");
+ test->test_func(test->test_data);
+}
+
+static void done_callback(void *user_data)
+{
+ struct test_case *test = user_data;
+
+ test->end_time = get_elapsed_time(&tester_start);
+
+ print_progress(test->name, COLOR_BLACK, "done");
+ next_test_case();
+}
+
+void tester_pre_setup_complete(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != TEST_STAGE_PRE_SETUP)
+ return;
+
+ l_idle_oneshot(setup_callback, test, NULL);
+}
+
+void tester_pre_setup_failed(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != TEST_STAGE_PRE_SETUP)
+ return;
+
+ print_progress(test->name, COLOR_RED, "pre setup failed");
+
+ l_idle_oneshot(done_callback, test, NULL);
+}
+
+void tester_setup_complete(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != TEST_STAGE_SETUP)
+ return;
+
+ print_progress(test->name, COLOR_BLUE, "setup complete");
+
+ l_idle_oneshot(run_callback, test, NULL);
+}
+
+void tester_setup_failed(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != TEST_STAGE_SETUP)
+ return;
+
+ test->stage = TEST_STAGE_POST_TEARDOWN;
+
+ l_timeout_remove(test->run_timer);
+ test->run_timer = NULL;
+
+ print_progress(test->name, COLOR_RED, "setup failed");
+ print_progress(test->name, COLOR_MAGENTA, "teardown");
+
+ test->post_teardown_func(test->test_data);
+}
+
+static void test_result(enum test_result result)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != TEST_STAGE_RUN)
+ return;
+
+ l_timeout_remove(test->run_timer);
+ test->run_timer = NULL;
+
+ test->result = result;
+ switch (result) {
+ case TEST_RESULT_PASSED:
+ print_progress(test->name, COLOR_GREEN, "test passed");
+ break;
+ case TEST_RESULT_FAILED:
+ print_progress(test->name, COLOR_RED, "test failed");
+ break;
+ case TEST_RESULT_NOT_RUN:
+ print_progress(test->name, COLOR_YELLOW, "test not run");
+ break;
+ case TEST_RESULT_TIMED_OUT:
+ print_progress(test->name, COLOR_RED, "test timed out");
+ break;
+ }
+
+ if (test->teardown)
+ return;
+
+ test->teardown = true;
+
+ l_idle_oneshot(teardown_callback, test, NULL);
+}
+
+void tester_test_passed(void)
+{
+ test_result(TEST_RESULT_PASSED);
+}
+
+void tester_test_failed(void)
+{
+ test_result(TEST_RESULT_FAILED);
+}
+
+void tester_test_abort(void)
+{
+ test_result(TEST_RESULT_NOT_RUN);
+}
+
+void tester_teardown_complete(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != TEST_STAGE_TEARDOWN)
+ return;
+
+ test->stage = TEST_STAGE_POST_TEARDOWN;
+
+ test->post_teardown_func(test->test_data);
+}
+
+void tester_teardown_failed(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != TEST_STAGE_TEARDOWN)
+ return;
+
+ test->stage = TEST_STAGE_POST_TEARDOWN;
+
+ tester_post_teardown_failed();
+}
+
+void tester_post_teardown_complete(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != TEST_STAGE_POST_TEARDOWN)
+ return;
+
+ print_progress(test->name, COLOR_MAGENTA, "teardown complete");
+
+ l_idle_oneshot(done_callback, test, NULL);
+}
+
+void tester_post_teardown_failed(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != TEST_STAGE_POST_TEARDOWN)
+ return;
+
+ print_progress(test->name, COLOR_RED, "teardown failed");
+
+ l_idle_oneshot(done_callback, test, NULL);
+}
+
+static void start_tester(void *user_data)
+{
+ gettimeofday(&tester_start, NULL);
+ next_test_case();
+}
+
+struct wait_data {
+ unsigned int seconds;
+ struct test_case *test;
+ tester_wait_func_t func;
+ void *user_data;
+};
+
+static void wait_callback(struct l_timeout *timer, void *user_data)
+{
+ struct wait_data *wait = user_data;
+ struct test_case *test = wait->test;
+
+ wait->seconds--;
+
+ if (wait->seconds > 0) {
+ print_progress(test->name, COLOR_BLACK, "%u seconds left",
+ wait->seconds);
+ return;
+ }
+
+ print_progress(test->name, COLOR_BLACK, "waiting done");
+
+ wait->func(wait->user_data);
+
+ free(wait);
+
+ l_timeout_remove(timer);
+}
+
+void tester_wait(unsigned int seconds, tester_wait_func_t func,
+ void *user_data)
+{
+ struct test_case *test;
+ struct wait_data *wait;
+
+ if (!func || seconds < 1)
+ return;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ wait = new0(struct wait_data, 1);
+ wait->seconds = seconds;
+ wait->test = test;
+ wait->func = func;
+ wait->user_data = user_data;
+
+ l_timeout_create(1000, wait_callback, wait, NULL);
+
+ print_progress(test->name, COLOR_BLACK, "waiting %u seconds", seconds);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+ static bool terminated = false;
+
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ if (!terminated)
+ mainloop_quit();
+
+ terminated = true;
+ break;
+ }
+}
+
+bool tester_use_quiet(void)
+{
+ return option_quiet;
+}
+
+bool tester_use_debug(void)
+{
+ return option_debug;
+}
+
+static const struct option options[] = {
+ { "version", no_argument, NULL, 'v' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "debug", no_argument, NULL, 'd' },
+ { "monitor", no_argument, NULL, 'm' },
+ { "list", no_argument, NULL, 'l' },
+ { "prefix", required_argument, NULL, 'p' },
+ { }
+};
+
+static void usage(void)
+{
+ fprintf(stderr,
+ "Usage:\n"
+ "\%s [options]\n", tester_name);
+ fprintf(stderr,
+ "Options:\n"
+ "\t--version Show version information and exit\n"
+ "\t--quiet Run tests without logging\n"
+ "\t--debug Run tests with debug output\n"
+ "\t--monitor Enable monitor output\n"
+ "\t--list Only list the tests to be run\n"
+ "\t--prefix Run tests matching provided prefix\n");
+}
+
+void tester_init(int *argc, char ***argv)
+{
+ tester_name = strrchr(*argv[0], '/');
+ if (!tester_name)
+ tester_name = strdup(*argv[0]);
+ else
+ tester_name = strdup(++tester_name);
+
+ for (;;) {
+ int opt;
+
+ opt = getopt_long(*argc, *argv, "p:vqdml", options, NULL);
+ if (opt < 0)
+ break;
+
+ switch (opt) {
+ case 'v':
+ printf("%s\n", VERSION);
+ exit(EXIT_SUCCESS);
+ case 'q':
+ option_quiet = true;
+ break;
+ case 'd':
+ option_debug = true;
+ break;
+ case 'm':
+ option_monitor = true;
+ break;
+ case 'l':
+ option_list = true;
+ break;
+ case 'p':
+ option_prefix = optarg;
+ break;
+ default:
+ usage();
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ mainloop_init();
+
+ test_list = l_queue_new();
+}
+
+int tester_run(void)
+{
+ int ret;
+
+ if (option_list) {
+ mainloop_quit();
+ return EXIT_SUCCESS;
+ }
+
+ l_idle_oneshot(start_tester, NULL, NULL);
+
+ mainloop_run_with_signal(signal_callback, NULL);
+
+ ret = tester_summarize();
+
+ l_queue_destroy(test_list, test_destroy);
+
+ if (option_monitor)
+ bt_log_close();
+
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
--
2.26.2

2020-11-07 07:07:11

by Stotland, Inga

[permalink] [raw]
Subject: [RFC PATCH BlueZ 07/10] tools/bnep-tester: Convert to use ELL library

This reworks the source code to use ELL primitives and removes
dependecies on GLib.
---
Makefile.tools | 4 ++--
tools/bnep-tester.c | 9 +--------
2 files changed, 3 insertions(+), 10 deletions(-)

diff --git a/Makefile.tools b/Makefile.tools
index 83c30252d..8c79a528d 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -130,12 +130,12 @@ tools_rfcomm_tester_LDADD = lib/libbluetooth-internal.la \
src/libshared-glib.la $(GLIB_LIBS)

tools_bnep_tester_SOURCES = tools/bnep-tester.c monitor/bt.h \
- emulator/hciemu.h emulator/hciemu.c \
+ emulator/hciemu.h emulator/hciemu-ell.c \
emulator/btdev.h emulator/btdev.c \
emulator/bthost.h emulator/bthost.c \
emulator/smp.c
tools_bnep_tester_LDADD = lib/libbluetooth-internal.la \
- src/libshared-glib.la $(GLIB_LIBS)
+ src/libshared-ell.la $(ell_ldadd)

tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \
emulator/hciemu.h emulator/hciemu-ell.c \
diff --git a/tools/bnep-tester.c b/tools/bnep-tester.c
index 5e4d7fb6d..dab913862 100644
--- a/tools/bnep-tester.c
+++ b/tools/bnep-tester.c
@@ -18,7 +18,7 @@
#include <errno.h>
#include <stdbool.h>

-#include <glib.h>
+#include <ell/ell.h>

#include "lib/bluetooth.h"
#include "lib/bnep.h"
@@ -37,7 +37,6 @@ struct test_data {
struct hciemu *hciemu;
enum hciemu_type hciemu_type;
const void *test_data;
- unsigned int io_id;
uint16_t conn_handle;
};

@@ -192,11 +191,6 @@ static void test_post_teardown(const void *test_data)
{
struct test_data *data = tester_get_data();

- if (data->io_id > 0) {
- g_source_remove(data->io_id);
- data->io_id = 0;
- }
-
hciemu_unref(data->hciemu);
data->hciemu = NULL;
}
@@ -282,7 +276,6 @@ static void test_basic(const void *test_data)
break; \
user->hciemu_type = HCIEMU_TYPE_BREDR; \
user->test_data = data; \
- user->io_id = 0; \
tester_add_full(name, data, \
test_pre_setup, setup, func, NULL, \
test_post_teardown, 2, user, test_data_free); \
--
2.26.2

2020-11-07 07:07:12

by Stotland, Inga

[permalink] [raw]
Subject: [RFC PATCH BlueZ 09/10] tools/mgmt-tester: Convert to use ELL library

This reworks the source code to use ELL primitives and removes
dependecies on GLib.
---
Makefile.tools | 4 ++--
tools/mgmt-tester.c | 5 +++--
2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/Makefile.tools b/Makefile.tools
index 03264cff9..e65547c3f 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -106,12 +106,12 @@ tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h
tools_3dsp_LDADD = src/libshared-mainloop.la

tools_mgmt_tester_SOURCES = tools/mgmt-tester.c monitor/bt.h \
- emulator/hciemu.h emulator/hciemu.c \
+ emulator/hciemu.h emulator/hciemu-ell.c \
emulator/btdev.h emulator/btdev.c \
emulator/bthost.h emulator/bthost.c \
emulator/smp.c
tools_mgmt_tester_LDADD = lib/libbluetooth-internal.la \
- src/libshared-glib.la $(GLIB_LIBS)
+ src/libshared-ell.la $(ell_ldadd)

tools_l2cap_tester_SOURCES = tools/l2cap-tester.c monitor/bt.h \
emulator/hciemu.h emulator/hciemu-ell.c \
diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c
index d8554411f..75f6457d2 100644
--- a/tools/mgmt-tester.c
+++ b/tools/mgmt-tester.c
@@ -18,7 +18,7 @@
#include <errno.h>
#include <unistd.h>

-#include <glib.h>
+#include <ell/ell.h>

#include "lib/bluetooth.h"
#include "lib/hci.h"
@@ -130,7 +130,8 @@ static void read_info_callback(uint8_t status, uint16_t length,
tester_print(" Name: %s", rp->name);
tester_print(" Short name: %s", rp->short_name);

- if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+ if (!hciemu_get_address(data->hciemu) ||
+ (strcmp(hciemu_get_address(data->hciemu), addr))) {
tester_pre_setup_failed();
return;
}
--
2.26.2

2020-11-07 07:09:01

by Stotland, Inga

[permalink] [raw]
Subject: [RFC PATCH BlueZ 10/10] tools/rfcomm-tester: Convert to use ELL library

This reworks the source code to use ELL primitives and removes
dependecies on GLib.
---
Makefile.tools | 4 +-
tools/rfcomm-tester.c | 92 ++++++++++++++++++++++++-------------------
2 files changed, 53 insertions(+), 43 deletions(-)

diff --git a/Makefile.tools b/Makefile.tools
index e65547c3f..2f86c839c 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -122,12 +122,12 @@ tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la \
src/libshared-ell.la $(ell_ldadd)

tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \
- emulator/hciemu.h emulator/hciemu.c \
+ emulator/hciemu.h emulator/hciemu-ell.c \
emulator/btdev.h emulator/btdev.c \
emulator/bthost.h emulator/bthost.c \
emulator/smp.c
tools_rfcomm_tester_LDADD = lib/libbluetooth-internal.la \
- src/libshared-glib.la $(GLIB_LIBS)
+ src/libshared-ell.la $(ell_ldadd)

tools_bnep_tester_SOURCES = tools/bnep-tester.c monitor/bt.h \
emulator/hciemu.h emulator/hciemu-ell.c \
diff --git a/tools/rfcomm-tester.c b/tools/rfcomm-tester.c
index 9bae5b9d5..516dd3f82 100644
--- a/tools/rfcomm-tester.c
+++ b/tools/rfcomm-tester.c
@@ -18,7 +18,7 @@
#include <errno.h>
#include <stdbool.h>

-#include <glib.h>
+#include <ell/ell.h>

#include "lib/bluetooth.h"
#include "lib/rfcomm.h"
@@ -35,9 +35,9 @@ struct test_data {
struct mgmt *mgmt;
uint16_t mgmt_index;
struct hciemu *hciemu;
+ struct l_io *io;
enum hciemu_type hciemu_type;
const void *test_data;
- unsigned int io_id;
uint16_t conn_handle;
};

@@ -192,9 +192,9 @@ static void test_post_teardown(const void *test_data)
{
struct test_data *data = tester_get_data();

- if (data->io_id > 0) {
- g_source_remove(data->io_id);
- data->io_id = 0;
+ if (data->io) {
+ l_io_destroy(data->io);
+ data->io = NULL;
}

hciemu_unref(data->hciemu);
@@ -395,8 +395,7 @@ static int connect_rfcomm_sock(int sk, const bdaddr_t *bdaddr, uint8_t channel)
return 0;
}

-static gboolean client_received_data(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
+static bool client_received_data(struct l_io *io, void *user_data)
{
struct test_data *data = tester_get_data();
const struct rfcomm_client_data *cli = data->test_data;
@@ -404,7 +403,7 @@ static gboolean client_received_data(GIOChannel *io, GIOCondition cond,
ssize_t ret;
char buf[248];

- sk = g_io_channel_unix_get_fd(io);
+ sk = l_io_get_fd(io);

ret = read(sk, buf, cli->data_len);
if (cli->data_len != ret) {
@@ -420,19 +419,38 @@ static gboolean client_received_data(GIOChannel *io, GIOCondition cond,
return false;
}

-static gboolean rc_connect_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
+static void rc_disconnect_cb(struct l_io *io, void *user_data)
{
struct test_data *data = tester_get_data();
const struct rfcomm_client_data *cli = data->test_data;
socklen_t len = sizeof(int);
int sk, err, sk_err;

- tester_print("Connected");
+ tester_print("Disconnected");
+
+ sk = l_io_get_fd(io);
+
+ if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+ err = -errno;
+ else
+ err = -sk_err;
+
+ if (cli->expected_connect_err && err == cli->expected_connect_err)
+ tester_test_passed();
+ else
+ tester_test_failed();
+}

- data->io_id = 0;
+static bool rc_connect_cb(struct l_io *io, void *user_data)
+{
+ struct test_data *data = tester_get_data();
+ const struct rfcomm_client_data *cli = data->test_data;
+ socklen_t len = sizeof(int);
+ int sk, err, sk_err;
+
+ tester_print("Connected");

- sk = g_io_channel_unix_get_fd(io);
+ sk = l_io_get_fd(io);

if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
err = -errno;
@@ -458,7 +476,7 @@ static gboolean rc_connect_cb(GIOChannel *io, GIOCondition cond,

return false;
} else if (cli->read_data) {
- g_io_add_watch(io, G_IO_IN, client_received_data, NULL);
+ l_io_set_read_handler(io, client_received_data, NULL, NULL);
bthost_send_rfcomm_data(hciemu_client_get_host(data->hciemu),
data->conn_handle,
cli->client_channel,
@@ -535,7 +553,6 @@ static void test_connect(const void *test_data)
struct bthost *bthost = hciemu_client_get_host(data->hciemu);
const struct rfcomm_client_data *cli = data->test_data;
const uint8_t *client_addr, *master_addr;
- GIOChannel *io;
int sk;

bthost_add_l2cap_server(bthost, 0x0003, NULL, NULL, NULL);
@@ -554,18 +571,19 @@ static void test_connect(const void *test_data)
return;
}

- io = g_io_channel_unix_new(sk);
- g_io_channel_set_close_on_unref(io, TRUE);
-
- data->io_id = g_io_add_watch(io, G_IO_OUT, rc_connect_cb, NULL);
+ data->io = l_io_new(sk);
+ l_io_set_close_on_destroy(data->io, true);
+ l_io_set_disconnect_handler(data->io, rc_disconnect_cb, NULL, NULL);

- g_io_channel_unref(io);
+ if (!l_io_set_write_handler(data->io, rc_connect_cb, NULL, NULL)) {
+ tester_test_failed();
+ return;
+ }

tester_print("Connect in progress %d", sk);
}

-static gboolean server_received_data(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
+static bool server_received_data(struct l_io *io, void *user_data)
{
struct test_data *data = tester_get_data();
const struct rfcomm_server_data *srv = data->test_data;
@@ -573,7 +591,7 @@ static gboolean server_received_data(GIOChannel *io, GIOCondition cond,
ssize_t ret;
int sk;

- sk = g_io_channel_unix_get_fd(io);
+ sk = l_io_get_fd(io);

ret = read(sk, buf, srv->data_len);
if (ret != srv->data_len) {
@@ -589,16 +607,14 @@ static gboolean server_received_data(GIOChannel *io, GIOCondition cond,
return false;
}

-static gboolean rfcomm_listen_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
+static bool rfcomm_listen_cb(struct l_io *io, void *user_data)
{
struct test_data *data = tester_get_data();
const struct rfcomm_server_data *srv = data->test_data;
int sk, new_sk;

- data->io_id = 0;

- sk = g_io_channel_unix_get_fd(io);
+ sk = l_io_get_fd(io);

new_sk = accept(sk, NULL, NULL);
if (new_sk < 0) {
@@ -616,15 +632,12 @@ static gboolean rfcomm_listen_cb(GIOChannel *io, GIOCondition cond,
close(new_sk);
return false;
} else if (srv->read_data) {
- GIOChannel *new_io;
-
- new_io = g_io_channel_unix_new(new_sk);
- g_io_channel_set_close_on_unref(new_io, TRUE);
+ struct l_io *new_io;

- data->io_id = g_io_add_watch(new_io, G_IO_IN,
- server_received_data, NULL);
+ new_io = l_io_new(new_sk);
+ l_io_set_close_on_destroy(new_io, true);
+ l_io_set_read_handler(new_io, server_received_data, NULL, NULL);

- g_io_channel_unref(new_io);
return false;
}

@@ -677,7 +690,6 @@ static void test_server(const void *test_data)
const struct rfcomm_server_data *srv = data->test_data;
const uint8_t *master_addr;
struct bthost *bthost;
- GIOChannel *io;
int sk;

master_addr = hciemu_get_master_bdaddr(data->hciemu);
@@ -696,11 +708,10 @@ static void test_server(const void *test_data)
return;
}

- io = g_io_channel_unix_new(sk);
- g_io_channel_set_close_on_unref(io, TRUE);
+ data->io = l_io_new(sk);
+ l_io_set_close_on_destroy(data->io, true);

- data->io_id = g_io_add_watch(io, G_IO_IN, rfcomm_listen_cb, NULL);
- g_io_channel_unref(io);
+ l_io_set_read_handler(data->io, rfcomm_listen_cb, NULL, NULL);

tester_print("Listening for connections");

@@ -713,12 +724,11 @@ static void test_server(const void *test_data)
#define test_rfcomm(name, data, setup, func) \
do { \
struct test_data *user; \
- user = malloc(sizeof(struct test_data)); \
+ user = l_new(struct test_data, 1); \
if (!user) \
break; \
user->hciemu_type = HCIEMU_TYPE_BREDR; \
user->test_data = data; \
- user->io_id = 0; \
tester_add_full(name, data, \
test_pre_setup, setup, func, NULL, \
test_post_teardown, 2, user, test_data_free); \
--
2.26.2

2020-11-07 07:09:01

by Stotland, Inga

[permalink] [raw]
Subject: [RFC PATCH BlueZ 08/10] tools/l2cap-tester: Convert to use ELL library

This reworks the source code to use ELL primitives and removes
dependecies on GLib.
---
Makefile.tools | 4 +-
tools/l2cap-tester.c | 256 +++++++++++++++++++++----------------------
2 files changed, 130 insertions(+), 130 deletions(-)

diff --git a/Makefile.tools b/Makefile.tools
index 8c79a528d..03264cff9 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -114,12 +114,12 @@ tools_mgmt_tester_LDADD = lib/libbluetooth-internal.la \
src/libshared-glib.la $(GLIB_LIBS)

tools_l2cap_tester_SOURCES = tools/l2cap-tester.c monitor/bt.h \
- emulator/hciemu.h emulator/hciemu.c \
+ emulator/hciemu.h emulator/hciemu-ell.c \
emulator/btdev.h emulator/btdev.c \
emulator/bthost.h emulator/bthost.c \
emulator/smp.c
tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la \
- src/libshared-glib.la $(GLIB_LIBS)
+ src/libshared-ell.la $(ell_ldadd)

tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \
emulator/hciemu.h emulator/hciemu.c \
diff --git a/tools/l2cap-tester.c b/tools/l2cap-tester.c
index ff641ba1d..6f55cf6f2 100644
--- a/tools/l2cap-tester.c
+++ b/tools/l2cap-tester.c
@@ -4,6 +4,7 @@
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2013 Intel Corporation. All rights reserved.
+
*
*
*/
@@ -17,7 +18,7 @@
#include <errno.h>
#include <stdbool.h>

-#include <glib.h>
+#include <ell/ell.h>

#include "lib/bluetooth.h"
#include "lib/l2cap.h"
@@ -35,8 +36,9 @@ struct test_data {
struct mgmt *mgmt;
uint16_t mgmt_index;
struct hciemu *hciemu;
+ struct l_io *io;
+ struct l_io *io2;
enum hciemu_type hciemu_type;
- unsigned int io_id;
uint16_t handle;
uint16_t scid;
uint16_t dcid;
@@ -218,9 +220,14 @@ static void test_post_teardown(const void *test_data)
{
struct test_data *data = tester_get_data();

- if (data->io_id > 0) {
- g_source_remove(data->io_id);
- data->io_id = 0;
+ if (data->io) {
+ l_io_destroy(data->io);
+ data->io = NULL;
+ }
+
+ if (data->io2) {
+ l_io_destroy(data->io2);
+ data->io2 = NULL;
}

hciemu_unref(data->hciemu);
@@ -230,18 +237,16 @@ static void test_post_teardown(const void *test_data)
static void test_data_free(void *test_data)
{
struct test_data *data = test_data;
-
- free(data);
+ l_free(data);
}

#define test_l2cap_bredr(name, data, setup, func) \
do { \
struct test_data *user; \
- user = malloc(sizeof(struct test_data)); \
+ user = l_new(struct test_data, 1); \
if (!user) \
break; \
user->hciemu_type = HCIEMU_TYPE_BREDR; \
- user->io_id = 0; \
user->test_data = data; \
tester_add_full(name, data, \
test_pre_setup, setup, func, NULL, \
@@ -251,11 +256,10 @@ static void test_data_free(void *test_data)
#define test_l2cap_le(name, data, setup, func) \
do { \
struct test_data *user; \
- user = malloc(sizeof(struct test_data)); \
+ user = l_new(struct test_data, 1); \
if (!user) \
break; \
user->hciemu_type = HCIEMU_TYPE_LE; \
- user->io_id = 0; \
user->test_data = data; \
tester_add_full(name, data, \
test_pre_setup, setup, func, NULL, \
@@ -886,19 +890,18 @@ static void test_basic(const void *test_data)
tester_test_passed();
}

-static gboolean client_received_data(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
+static bool client_received_data(struct l_io *io, void *user_data)
{
struct test_data *data = tester_get_data();
const struct l2cap_data *l2data = data->test_data;
char buf[1024];
int sk;

- sk = g_io_channel_unix_get_fd(io);
+ sk = l_io_get_fd(io);
if (read(sk, buf, l2data->data_len) != l2data->data_len) {
tester_warn("Unable to read %u bytes", l2data->data_len);
tester_test_failed();
- return FALSE;
+ return false;
}

if (memcmp(buf, l2data->read_data, l2data->data_len))
@@ -906,22 +909,22 @@ static gboolean client_received_data(GIOChannel *io, GIOCondition cond,
else
tester_test_passed();

- return FALSE;
+ return true;
}

-static gboolean server_received_data(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
+static bool server_received_data(struct l_io *io, void *user_data)
{
struct test_data *data = tester_get_data();
const struct l2cap_data *l2data = data->test_data;
char buf[1024];
int sk;

- sk = g_io_channel_unix_get_fd(io);
+ sk = l_io_get_fd(io);
if (read(sk, buf, l2data->data_len) != l2data->data_len) {
tester_warn("Unable to read %u bytes", l2data->data_len);
tester_test_failed();
- return FALSE;
+ l_io_destroy(io);
+ return false;
}

if (memcmp(buf, l2data->read_data, l2data->data_len))
@@ -929,7 +932,9 @@ static gboolean server_received_data(GIOChannel *io, GIOCondition cond,
else
tester_test_passed();

- return FALSE;
+ l_io_destroy(io);
+
+ return true;
}

static void bthost_received_data(const void *buf, uint16_t len,
@@ -966,27 +971,6 @@ static void server_bthost_received_data(const void *buf, uint16_t len,
tester_test_passed();
}

-static gboolean socket_closed_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
-{
- struct test_data *data = tester_get_data();
- const struct l2cap_data *l2data = data->test_data;
-
- if (l2data->shut_sock_wr) {
- /* if socket is closed using SHUT_WR, L2CAP disconnection
- * response must be received first before G_IO_HUP event.
- */
- if (data->host_disconnected)
- tester_test_passed();
- else {
- tester_warn("G_IO_HUP received before receiving L2CAP disconnection");
- tester_test_failed();
- }
- }
-
- return FALSE;
-}
-
static bool check_mtu(struct test_data *data, int sk)
{
const struct l2cap_data *l2data = data->test_data;
@@ -1029,17 +1013,52 @@ static bool check_mtu(struct test_data *data, int sk)
return true;
}

-static gboolean l2cap_connect_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
+static void l2cap_disconnect_cb(struct l_io *io, void *user_data)
{
struct test_data *data = tester_get_data();
const struct l2cap_data *l2data = data->test_data;
int err, sk_err, sk;
socklen_t len = sizeof(sk_err);

- data->io_id = 0;
+ tester_print("Disconnect callback");
+ if (l2data->shut_sock_wr) {
+ /* if socket is closed using SHUT_WR, L2CAP disconnection
+ * response must be received first before EPOLLHUP event.
+ */
+ if (data->host_disconnected)
+ tester_test_passed();
+ else {
+ tester_warn("HUP received before L2CAP disconnection");
+ tester_test_failed();
+ }
+
+ return;
+ }
+
+ sk = l_io_get_fd(io);

- sk = g_io_channel_unix_get_fd(io);
+ if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+ err = -errno;
+ else
+ err = -sk_err;
+
+ if (l2data->expect_err) {
+
+ if (-err == l2data->expect_err)
+ tester_test_passed();
+ else
+ tester_test_failed();
+ }
+}
+
+static bool l2cap_connect_cb(struct l_io *io, void *user_data)
+{
+ struct test_data *data = tester_get_data();
+ const struct l2cap_data *l2data = data->test_data;
+ int err, sk_err, sk;
+ socklen_t len = sizeof(sk_err);
+
+ sk = l_io_get_fd(io);

if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
err = -errno;
@@ -1055,19 +1074,19 @@ static gboolean l2cap_connect_cb(GIOChannel *io, GIOCondition cond,

if (!check_mtu(data, sk)) {
tester_test_failed();
- return FALSE;
+ return false;
}

if (l2data->read_data) {
struct bthost *bthost;

bthost = hciemu_client_get_host(data->hciemu);
- g_io_add_watch(io, G_IO_IN, client_received_data, NULL);
+ l_io_set_read_handler(io, client_received_data, NULL, NULL);

bthost_send_cid(bthost, data->handle, data->dcid,
l2data->read_data, l2data->data_len);

- return FALSE;
+ return false;
} else if (l2data->write_data) {
struct bthost *bthost;
ssize_t ret;
@@ -1082,12 +1101,11 @@ static gboolean l2cap_connect_cb(GIOChannel *io, GIOCondition cond,
tester_test_failed();
}

- return FALSE;
+ return false;
} else if (l2data->shut_sock_wr) {
- g_io_add_watch(io, G_IO_HUP, socket_closed_cb, NULL);
shutdown(sk, SHUT_WR);

- return FALSE;
+ return false;
}

failed:
@@ -1096,7 +1114,7 @@ failed:
else
tester_test_passed();

- return FALSE;
+ return false;
}

static int create_l2cap_sock(struct test_data *data, uint16_t psm,
@@ -1281,7 +1299,6 @@ static void test_connect(const void *test_data)
{
struct test_data *data = tester_get_data();
const struct l2cap_data *l2data = data->test_data;
- GIOChannel *io;
int sk;

if (l2data->server_psm) {
@@ -1321,12 +1338,12 @@ static void test_connect(const void *test_data)
return;
}

- io = g_io_channel_unix_new(sk);
- g_io_channel_set_close_on_unref(io, TRUE);
+ data->io = l_io_new(sk);

- data->io_id = g_io_add_watch(io, G_IO_OUT, l2cap_connect_cb, NULL);
+ l_io_set_close_on_destroy(data->io, true);

- g_io_channel_unref(io);
+ l_io_set_disconnect_handler(data->io, l2cap_disconnect_cb, NULL, NULL);
+ l_io_set_write_handler(data->io, l2cap_connect_cb, NULL, NULL);

tester_print("Connect in progress");
}
@@ -1353,12 +1370,12 @@ static void test_connect_reject(const void *test_data)
close(sk);
}

-static int connect_socket(const uint8_t *client_bdaddr, GIOFunc connect_cb,
- bool defer)
+static struct l_io *connect_socket(const uint8_t *client_bdaddr,
+ l_io_write_cb_t connect_cb, bool defer)
{
struct test_data *data = tester_get_data();
const struct l2cap_data *l2data = data->test_data;
- GIOChannel *io;
+ struct l_io *io;
int sk;

sk = create_l2cap_sock(data, 0, l2data->cid, l2data->sec_level,
@@ -1369,7 +1386,7 @@ static int connect_socket(const uint8_t *client_bdaddr, GIOFunc connect_cb,
tester_test_abort();
else
tester_test_failed();
- return -1;
+ return NULL;
}

if (defer) {
@@ -1380,7 +1397,7 @@ static int connect_socket(const uint8_t *client_bdaddr, GIOFunc connect_cb,
tester_print("Can't enable deferred setup: %s (%d)",
strerror(errno), errno);
tester_test_failed();
- return -1;
+ return NULL;
}
}

@@ -1389,25 +1406,20 @@ static int connect_socket(const uint8_t *client_bdaddr, GIOFunc connect_cb,
tester_print("Error in connect_l2cap_sock");
close(sk);
tester_test_failed();
- return -1;
+ return NULL;
}

- if (connect_cb) {
- io = g_io_channel_unix_new(sk);
- g_io_channel_set_close_on_unref(io, TRUE);
-
- data->io_id = g_io_add_watch(io, G_IO_OUT, connect_cb, NULL);
-
- g_io_channel_unref(io);
- }
+ io = l_io_new(sk);
+ l_io_set_close_on_destroy(io, true);
+ l_io_set_write_handler(io, connect_cb, NULL, NULL);

tester_print("Connect in progress, sk = %d %s", sk,
defer ? "(deferred)" : "");

- return sk;
+ return io;
}

-static gboolean test_close_socket_1_part_3(gpointer arg)
+static void test_close_socket_1_part_3(void *arg)
{
struct test_data *data = tester_get_data();

@@ -1416,20 +1428,19 @@ static gboolean test_close_socket_1_part_3(gpointer arg)
if (data->sk != -1) {
tester_print("Error - scan was not enabled yet");
tester_test_failed();
- return FALSE;
+ return;
}

if (hciemu_get_master_le_scan_enable(data->hciemu)) {
tester_print("Delayed check whether scann is off failed");
tester_test_failed();
- return FALSE;
+ return;
}

tester_test_passed();
- return FALSE;
}

-static gboolean test_close_socket_1_part_2(gpointer args)
+static void test_close_socket_1_part_2(void *args)
{
struct test_data *data = tester_get_data();
int sk = data->sk;
@@ -1443,7 +1454,7 @@ static gboolean test_close_socket_1_part_2(gpointer args)
if (!hciemu_get_master_le_scan_enable(data->hciemu)) {
tester_print("Error - should be still scanning");
tester_test_failed();
- return FALSE;
+ return;
}

/* Calling close() should remove device from whitelist, and stop
@@ -1452,15 +1463,14 @@ static gboolean test_close_socket_1_part_2(gpointer args)
if (close(sk) < 0) {
tester_print("Error when closing socket");
tester_test_failed();
- return FALSE;
+ return;
}

data->sk = -1;
/* tester_test_passed will be called when scan is stopped. */
- return FALSE;
}

-static gboolean test_close_socket_2_part_3(gpointer arg)
+static void test_close_socket_2_part_3(void *arg)
{
struct test_data *data = tester_get_data();
int sk = data->sk;
@@ -1470,7 +1480,7 @@ static gboolean test_close_socket_2_part_3(gpointer arg)
if (hciemu_get_master_le_scan_enable(data->hciemu)) {
tester_print("Error - should no longer scan");
tester_test_failed();
- return FALSE;
+ return;
}

/* Calling close() should eventually cause CMD_LE_CREATE_CONN_CANCEL */
@@ -1478,11 +1488,10 @@ static gboolean test_close_socket_2_part_3(gpointer arg)
if (err < 0) {
tester_print("Error when closing socket");
tester_test_failed();
- return FALSE;
+ return;
}

/* CMD_LE_CREATE_CONN_CANCEL will trigger test pass. */
- return FALSE;
}

static bool test_close_socket_cc_hook(const void *data, uint16_t len,
@@ -1491,7 +1500,7 @@ static bool test_close_socket_cc_hook(const void *data, uint16_t len,
return false;
}

-static gboolean test_close_socket_2_part_2(gpointer arg)
+static void test_close_socket_2_part_2(void *arg)
{
struct test_data *data = tester_get_data();
struct bthost *bthost = hciemu_client_get_host(data->hciemu);
@@ -1507,7 +1516,6 @@ static gboolean test_close_socket_2_part_2(gpointer arg)
*/
bthost_set_adv_enable(bthost, 0x01);
bthost_set_adv_enable(bthost, 0x00);
- return FALSE;
}

static void test_close_socket_scan_enabled(void)
@@ -1516,9 +1524,9 @@ static void test_close_socket_scan_enabled(void)
const struct l2cap_data *l2data = data->test_data;

if (l2data == &le_client_close_socket_test_1)
- g_idle_add(test_close_socket_1_part_2, NULL);
+ l_idle_oneshot(test_close_socket_1_part_2, NULL, NULL);
else if (l2data == &le_client_close_socket_test_2)
- g_idle_add(test_close_socket_2_part_2, NULL);
+ l_idle_oneshot(test_close_socket_2_part_2, NULL, NULL);
}

static void test_close_socket_scan_disabled(void)
@@ -1527,9 +1535,9 @@ static void test_close_socket_scan_disabled(void)
const struct l2cap_data *l2data = data->test_data;

if (l2data == &le_client_close_socket_test_1)
- g_idle_add(test_close_socket_1_part_3, NULL);
+ l_idle_oneshot(test_close_socket_1_part_3, NULL, NULL);
else if (l2data == &le_client_close_socket_test_2)
- g_idle_add(test_close_socket_2_part_3, NULL);
+ l_idle_oneshot(test_close_socket_2_part_3, NULL, NULL);
}

static void test_close_socket_conn_cancel(void)
@@ -1571,21 +1579,19 @@ static void test_close_socket(const void *test_data)
else
client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);

- data->sk = connect_socket(client_bdaddr, NULL, false);
+ data->io = connect_socket(client_bdaddr, NULL, false);
+ data->sk = l_io_get_fd(data->io);
}

static uint8_t test_2_connect_cb_cnt;
-static gboolean test_2_connect_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
+static bool test_2_connect_cb(struct l_io *io, void *user_data)
{
struct test_data *data = tester_get_data();
const struct l2cap_data *l2data = data->test_data;
int err, sk_err, sk;
socklen_t len = sizeof(sk_err);

- data->io_id = 0;
-
- sk = g_io_channel_unix_get_fd(io);
+ sk = l_io_get_fd(io);

if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
err = -errno;
@@ -1595,7 +1601,7 @@ static gboolean test_2_connect_cb(GIOChannel *io, GIOCondition cond,
if (err < 0) {
tester_warn("Connect failed: %s (%d)", strerror(-err), -err);
tester_test_failed();
- return FALSE;
+ return false;
}

tester_print("Successfully connected");
@@ -1612,16 +1618,15 @@ static gboolean test_2_connect_cb(GIOChannel *io, GIOCondition cond,
tester_test_passed();
}

- return FALSE;
+ return true;
}

-static gboolean enable_advertising(gpointer args)
+static void enable_advertising(void *args)
{
struct test_data *data = tester_get_data();
struct bthost *bthost = hciemu_client_get_host(data->hciemu);

bthost_set_adv_enable(bthost, 0x01);
- return FALSE;
}

static void test_connect_2_part_2(void)
@@ -1631,14 +1636,15 @@ static void test_connect_2_part_2(void)
const uint8_t *client_bdaddr;

client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
- data->sk2 = connect_socket(client_bdaddr, test_2_connect_cb, false);
+ data->io2 = connect_socket(client_bdaddr, test_2_connect_cb, false);
+ data->sk2 = l_io_get_fd(data->io2);

if (l2data->close_1) {
tester_print("Closing first socket! %d", data->sk);
close(data->sk);
}

- g_idle_add(enable_advertising, NULL);
+ l_idle_oneshot(enable_advertising, NULL, NULL);
}

static uint8_t test_scan_enable_counter;
@@ -1654,7 +1660,7 @@ static void test_connect_2_router(uint16_t opcode, const void *param,
if (test_scan_enable_counter == 1)
test_connect_2_part_2();
else if (test_scan_enable_counter == 2)
- g_idle_add(enable_advertising, NULL);
+ l_idle_oneshot(enable_advertising, NULL, NULL);
}
}

@@ -1683,50 +1689,47 @@ static void test_connect_2(const void *test_data)

client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
if (l2data->close_1)
- data->sk = connect_socket(client_bdaddr, NULL, defer);
+ data->io = connect_socket(client_bdaddr, NULL, defer);
else
- data->sk = connect_socket(client_bdaddr, test_2_connect_cb,
+ data->io = connect_socket(client_bdaddr, test_2_connect_cb,
defer);
+ data->sk = l_io_get_fd(data->io);
}

-static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
+static bool l2cap_listen_cb(struct l_io *io, void *user_data)
{
struct test_data *data = tester_get_data();
const struct l2cap_data *l2data = data->test_data;
int sk, new_sk;

- data->io_id = 0;
-
- sk = g_io_channel_unix_get_fd(io);
+ sk = l_io_get_fd(io);

new_sk = accept(sk, NULL, NULL);
if (new_sk < 0) {
tester_warn("accept failed: %s (%u)", strerror(errno), errno);
tester_test_failed();
- return FALSE;
+ return false;
}

if (!check_mtu(data, new_sk)) {
tester_test_failed();
- return FALSE;
+ return false;
}

if (l2data->read_data) {
struct bthost *bthost;
- GIOChannel *new_io;
+ struct l_io *new_io;
+
+ new_io = l_io_new(new_sk);

- new_io = g_io_channel_unix_new(new_sk);
- g_io_channel_set_close_on_unref(new_io, TRUE);
+ l_io_set_close_on_destroy(new_io, true);

bthost = hciemu_client_get_host(data->hciemu);
- g_io_add_watch(new_io, G_IO_IN, server_received_data, NULL);
+ l_io_set_read_handler(new_io, server_received_data, NULL, NULL);
bthost_send_cid(bthost, data->handle, data->dcid,
l2data->read_data, l2data->data_len);

- g_io_channel_unref(new_io);
-
- return FALSE;
+ return false;
} else if (l2data->write_data) {
struct bthost *bthost;
ssize_t ret;
@@ -1743,7 +1746,7 @@ static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond,
tester_test_failed();
}

- return FALSE;
+ return false;
}

tester_print("Successfully connected");
@@ -1752,7 +1755,7 @@ static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond,

tester_test_passed();

- return FALSE;
+ return false;
}

static void client_l2cap_rsp(uint8_t code, const void *data, uint16_t len,
@@ -1839,7 +1842,6 @@ static void test_server(const void *test_data)
const uint8_t *master_bdaddr;
uint8_t addr_type;
struct bthost *bthost;
- GIOChannel *io;
int sk;

if (l2data->server_psm || l2data->cid) {
@@ -1859,12 +1861,10 @@ static void test_server(const void *test_data)
return;
}

- io = g_io_channel_unix_new(sk);
- g_io_channel_set_close_on_unref(io, TRUE);
+ data->io = l_io_new(sk);

- data->io_id = g_io_add_watch(io, G_IO_IN, l2cap_listen_cb,
- NULL);
- g_io_channel_unref(io);
+ l_io_set_close_on_destroy(data->io, true);
+ l_io_set_read_handler(data->io, l2cap_listen_cb, NULL, NULL);

tester_print("Listening for connections");
}
--
2.26.2

2020-11-07 07:37:08

by bluez.test.bot

[permalink] [raw]
Subject: RE: Convert tools to use ELL library

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=379479

---Test result---

##############################
Test: CheckPatch - FAIL
Output:
shared/tester-ell: Create ell-based version of tester code
ERROR:INITIALISED_STATIC: do not initialise statics to false
#809: FILE: src/shared/tester-ell.c:761:
+ static bool terminated = false;

- total: 1 errors, 0 warnings, 901 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.

"[PATCH] shared/tester-ell: Create ell-based version of tester code" has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.


##############################
Test: CheckGitLint - PASS

##############################
Test: CheckBuild - PASS

##############################
Test: MakeCheck - PASS



---
Regards,
Linux Bluetooth

2020-11-09 18:05:59

by Luiz Augusto von Dentz

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

Hi Inga,

On Fri, Nov 6, 2020 at 11:06 PM Inga Stotland <[email protected]> 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

2020-11-10 22:11:29

by Luiz Augusto von Dentz

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

Hi Inga,

On Tue, Nov 10, 2020 at 1:55 PM Stotland, Inga <[email protected]> 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 <
>
> [email protected]
>
> > 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