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