public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ 0/4] Add 6lowpan-tester
@ 2025-10-05 22:17 Pauli Virtanen
  2025-10-05 22:17 ` [PATCH BlueZ 1/4] bthost: handle L2CAP_DISCONN_RSP and remove l2cap_conns Pauli Virtanen
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: Pauli Virtanen @ 2025-10-05 22:17 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Make a basic tester for the net/bluetooth/6lowpan.c implementation.

This module doesn't look like it's much used and locking around
l2cap_chan / hci_conn there looks suspicious, so probably some tests are
useful.

Here just simple connection / disconnection and packet RX tests are
added.  Some issues appear to be there:

Client Recv Raw - Success
    -> kernel panic (kernel BUG at net/core/skbuff.c:212!)

Client Connect - Disable
    -> lockdep splats

Pauli Virtanen (4):
  bthost: handle L2CAP_DISCONN_RSP and remove l2cap_conns
  bthost: handle L2CAP_FLOW_CONTROL_CREDIT_IND
  tools: add 6lowpan-tester
  doc: enable 6lowpan in tester.config and explain in test-runner.rst

 Makefile.tools         |  12 +-
 doc/test-runner.rst    |  10 +
 doc/tester.config      |   5 +
 emulator/bthost.c      |  93 +++++-
 emulator/bthost.h      |   6 +-
 tools/6lowpan-tester.c | 657 +++++++++++++++++++++++++++++++++++++++++
 tools/l2cap-tester.c   |   3 +-
 7 files changed, 782 insertions(+), 4 deletions(-)
 create mode 100644 tools/6lowpan-tester.c

-- 
2.51.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 1/4] bthost: handle L2CAP_DISCONN_RSP and remove l2cap_conns
  2025-10-05 22:17 [PATCH BlueZ 0/4] Add 6lowpan-tester Pauli Virtanen
@ 2025-10-05 22:17 ` Pauli Virtanen
  2025-10-05 23:42   ` Add 6lowpan-tester bluez.test.bot
  2025-10-05 22:17 ` [PATCH BlueZ 2/4] bthost: handle L2CAP_FLOW_CONTROL_CREDIT_IND Pauli Virtanen
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 9+ messages in thread
From: Pauli Virtanen @ 2025-10-05 22:17 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Handle L2CAP disconnection response. On receiving disconnection request
or response, remove the associated connection.

Change disconnect handler signature to take also the handle and CID.
---
 emulator/bthost.c    | 61 +++++++++++++++++++++++++++++++++++++++++++-
 emulator/bthost.h    |  6 ++++-
 tools/l2cap-tester.c |  3 ++-
 3 files changed, 67 insertions(+), 3 deletions(-)

diff --git a/emulator/bthost.c b/emulator/bthost.c
index c85f751cc..93023331e 100644
--- a/emulator/bthost.c
+++ b/emulator/bthost.c
@@ -403,6 +403,25 @@ static struct l2conn *bthost_add_l2cap_conn(struct bthost *bthost,
 	return l2conn;
 }
 
+static void btconn_detach_l2cap_conn(struct btconn *conn, struct l2conn *l2conn)
+{
+	struct l2conn *c;
+
+	if (conn->l2conns == l2conn) {
+		conn->l2conns = l2conn->next;
+		l2conn->next = NULL;
+		return;
+	}
+
+	for (c = conn->l2conns; c != NULL; c = c->next) {
+		if (c->next == l2conn) {
+			c->next = l2conn->next;
+			l2conn->next = NULL;
+			return;
+		}
+	}
+}
+
 static struct rcconn *bthost_add_rfcomm_conn(struct bthost *bthost,
 						struct btconn *conn,
 						struct l2conn *l2conn,
@@ -2142,11 +2161,41 @@ static bool l2cap_disconn_req(struct bthost *bthost, struct btconn *conn,
 	if (!l2conn)
 		return true;
 
+	btconn_detach_l2cap_conn(conn, l2conn);
+
 	cb_data = bthost_find_l2cap_cb_by_psm(bthost, l2conn->psm);
 
 	if (cb_data && cb_data->disconn_func)
-		cb_data->disconn_func(cb_data->user_data);
+		cb_data->disconn_func(conn->handle, l2conn->dcid,
+							cb_data->user_data);
 
+	l2conn_free(l2conn);
+	return true;
+}
+
+static bool l2cap_disconn_rsp(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_disconn_rsp *rsp = data;
+	struct l2cap_conn_cb_data *cb_data;
+	struct l2conn *l2conn;
+
+	if (len < sizeof(*rsp))
+		return false;
+
+	l2conn = btconn_find_l2cap_conn_by_scid(conn, rsp->dcid);
+	if (!l2conn)
+		return true;
+
+	btconn_detach_l2cap_conn(conn, l2conn);
+
+	cb_data = bthost_find_l2cap_cb_by_psm(bthost, l2conn->psm);
+
+	if (cb_data && cb_data->disconn_func)
+		cb_data->disconn_func(conn->handle, l2conn->dcid,
+							cb_data->user_data);
+
+	l2conn_free(l2conn);
 	return true;
 }
 
@@ -2302,6 +2351,11 @@ static void l2cap_sig(struct bthost *bthost, struct btconn *conn,
 						data + sizeof(*hdr), hdr_len);
 		break;
 
+	case BT_L2CAP_PDU_DISCONN_RSP:
+		ret = l2cap_disconn_rsp(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
 	case BT_L2CAP_PDU_INFO_REQ:
 		ret = l2cap_info_req(bthost, conn, hdr->ident,
 						data + sizeof(*hdr), hdr_len);
@@ -2536,6 +2590,11 @@ static void l2cap_le_sig(struct bthost *bthost, struct btconn *conn,
 						data + sizeof(*hdr), hdr_len);
 		break;
 
+	case BT_L2CAP_PDU_DISCONN_RSP:
+		ret = l2cap_disconn_rsp(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
 	case BT_L2CAP_PDU_CONN_PARAM_REQ:
 		ret = l2cap_conn_param_req(bthost, conn, hdr->ident,
 						data + sizeof(*hdr), hdr_len);
diff --git a/emulator/bthost.h b/emulator/bthost.h
index 743615838..d60111d25 100644
--- a/emulator/bthost.h
+++ b/emulator/bthost.h
@@ -99,6 +99,9 @@ void bthost_send_iso(struct bthost *bthost, uint16_t handle, bool ts,
 			uint16_t sn, uint32_t timestamp, uint8_t pkt_status,
 			const struct iovec *iov, int iovcnt);
 
+void bthost_disconnect_cid(struct bthost *bthost, uint16_t handle,
+								uint16_t cid);
+
 typedef void (*bthost_l2cap_rsp_cb) (uint8_t code, const void *data,
 						uint16_t len, void *user_data);
 
@@ -145,7 +148,8 @@ void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle,
 							const uint8_t ltk[16]);
 typedef void (*bthost_l2cap_connect_cb) (uint16_t handle, uint16_t cid,
 							void *user_data);
-typedef void (*bthost_l2cap_disconnect_cb) (void *user_data);
+typedef void (*bthost_l2cap_disconnect_cb) (uint16_t handle, uint16_t cid,
+							void *user_data);
 
 void bthost_add_l2cap_server(struct bthost *bthost, uint16_t psm,
 				bthost_l2cap_connect_cb func,
diff --git a/tools/l2cap-tester.c b/tools/l2cap-tester.c
index 208772527..a9ab8b051 100644
--- a/tools/l2cap-tester.c
+++ b/tools/l2cap-tester.c
@@ -1726,7 +1726,8 @@ static void client_l2cap_connect_cb(uint16_t handle, uint16_t cid,
 	data->handle = handle;
 }
 
-static void client_l2cap_disconnect_cb(void *user_data)
+static void client_l2cap_disconnect_cb(uint16_t handle, uint16_t cid,
+							void *user_data)
 {
 	struct test_data *data = user_data;
 
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 2/4] bthost: handle L2CAP_FLOW_CONTROL_CREDIT_IND
  2025-10-05 22:17 [PATCH BlueZ 0/4] Add 6lowpan-tester Pauli Virtanen
  2025-10-05 22:17 ` [PATCH BlueZ 1/4] bthost: handle L2CAP_DISCONN_RSP and remove l2cap_conns Pauli Virtanen
@ 2025-10-05 22:17 ` Pauli Virtanen
  2025-10-05 22:17 ` [PATCH BlueZ 3/4] tools: add 6lowpan-tester Pauli Virtanen
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Pauli Virtanen @ 2025-10-05 22:17 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Parse L2CAP_FLOW_CONTROL_CREDIT_IND and track credits, instead of
sending reject response.  Nothing is done with the credit counts
currently.
---
 emulator/bthost.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/emulator/bthost.c b/emulator/bthost.c
index 93023331e..b00f8f2b6 100644
--- a/emulator/bthost.c
+++ b/emulator/bthost.c
@@ -184,6 +184,8 @@ struct l2conn {
 	uint16_t scid;
 	uint16_t dcid;
 	uint16_t psm;
+	uint16_t rx_credits;
+	uint16_t tx_credits;
 	enum l2cap_mode mode;
 	uint16_t data_len;
 	uint16_t recv_len;
@@ -2459,6 +2461,8 @@ static bool l2cap_le_conn_req(struct bthost *bthost, struct btconn *conn,
 							le16_to_cpu(req->scid),
 							le16_to_cpu(psm));
 		l2conn->mode = L2CAP_MODE_LE_CRED;
+		l2conn->rx_credits = le16_to_cpu(rsp.credits);
+		l2conn->tx_credits = le16_to_cpu(req->credits);
 
 		if (cb_data && l2conn->psm == cb_data->psm && cb_data->func)
 			cb_data->func(conn->handle, l2conn->dcid,
@@ -2476,10 +2480,13 @@ static bool l2cap_le_conn_rsp(struct bthost *bthost, struct btconn *conn,
 
 	if (len < sizeof(*rsp))
 		return false;
+
 	/* TODO add L2CAP connection before with proper PSM */
 	l2conn = bthost_add_l2cap_conn(bthost, conn, 0,
 						le16_to_cpu(rsp->dcid), 0);
 	l2conn->mode = L2CAP_MODE_LE_CRED;
+	l2conn->rx_credits = 1;
+	l2conn->tx_credits = le16_to_cpu(rsp->credits);
 
 	return true;
 }
@@ -2544,12 +2551,32 @@ static bool l2cap_ecred_conn_rsp(struct bthost *bthost, struct btconn *conn,
 		l2conn = bthost_add_l2cap_conn(bthost, conn, 0,
 				      le16_to_cpu(rsp->scid[i]), 0);
 		l2conn->mode = L2CAP_MODE_LE_ENH_CRED;
+		l2conn->tx_credits = rsp->pdu->credits;
+		l2conn->rx_credits = 1;
 	}
 
 
 	return true;
 }
 
+static bool l2cap_le_flowctl_creds(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_le_flowctl_creds *ind = data;
+	struct l2conn *l2conn;
+
+	if (len < sizeof(*ind))
+		return false;
+
+	l2conn = btconn_find_l2cap_conn_by_dcid(conn, ind->cid);
+	if (!l2conn)
+		return true;
+
+	l2conn->tx_credits += ind->credits;
+
+	return true;
+}
+
 static bool l2cap_le_rsp(uint8_t code)
 {
 	switch (code) {
@@ -2624,6 +2651,11 @@ static void l2cap_le_sig(struct bthost *bthost, struct btconn *conn,
 						data + sizeof(*hdr), hdr_len);
 		break;
 
+	case BT_L2CAP_PDU_LE_FLOWCTL_CREDS:
+		ret = l2cap_le_flowctl_creds(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
 	default:
 		bthost_debug(bthost, "Unknown L2CAP code 0x%02x", hdr->code);
 		ret = false;
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 3/4] tools: add 6lowpan-tester
  2025-10-05 22:17 [PATCH BlueZ 0/4] Add 6lowpan-tester Pauli Virtanen
  2025-10-05 22:17 ` [PATCH BlueZ 1/4] bthost: handle L2CAP_DISCONN_RSP and remove l2cap_conns Pauli Virtanen
  2025-10-05 22:17 ` [PATCH BlueZ 2/4] bthost: handle L2CAP_FLOW_CONTROL_CREDIT_IND Pauli Virtanen
@ 2025-10-05 22:17 ` Pauli Virtanen
  2025-10-13 17:20   ` Luiz Augusto von Dentz
  2025-10-05 22:17 ` [PATCH BlueZ 4/4] doc: enable 6lowpan in tester.config and explain in test-runner.rst Pauli Virtanen
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 9+ messages in thread
From: Pauli Virtanen @ 2025-10-05 22:17 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Add 6lowpan smoke testing. It only exercises the kernel 6lowpan L2CAP
connection handling, ipv6 testing is mostly out of scope for the tester.

Add tests:

Basic Framework - Success
Client Connect - Terminate
Client Connect - Disable
Client Connect - Disconnect
Client Recv Dgram - Success
Client Recv Raw - Success
---
 Makefile.tools         |  12 +-
 tools/6lowpan-tester.c | 657 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 668 insertions(+), 1 deletion(-)
 create mode 100644 tools/6lowpan-tester.c

diff --git a/Makefile.tools b/Makefile.tools
index e60c31b1d..561b03d0b 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -87,7 +87,8 @@ noinst_PROGRAMS += emulator/btvirt emulator/b1ee emulator/hfp \
 					tools/smp-tester tools/hci-tester \
 					tools/rfcomm-tester tools/bnep-tester \
 					tools/userchan-tester tools/iso-tester \
-					tools/mesh-tester tools/ioctl-tester
+					tools/mesh-tester tools/ioctl-tester \
+					tools/6lowpan-tester
 
 emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
 				emulator/serial.h emulator/serial.c \
@@ -221,6 +222,15 @@ tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \
 				emulator/smp.c
 tools_ioctl_tester_LDADD = lib/libbluetooth-internal.la \
 				src/libshared-glib.la $(GLIB_LIBS)
+
+tools_6lowpan_tester_SOURCES = tools/6lowpan-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/vhci.h emulator/vhci.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_6lowpan_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la $(GLIB_LIBS)
 endif
 
 if TOOLS
diff --git a/tools/6lowpan-tester.c b/tools/6lowpan-tester.c
new file mode 100644
index 000000000..89ed9277e
--- /dev/null
+++ b/tools/6lowpan-tester.c
@@ -0,0 +1,657 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <net/if.h>
+#include <linux/if_packet.h>
+#include <sys/ioctl.h>
+
+#include <glib.h>
+
+#include "bluetooth/bluetooth.h"
+#include "bluetooth/mgmt.h"
+
+#include "monitor/bt.h"
+#include "emulator/bthost.h"
+#include "emulator/hciemu.h"
+
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/util.h"
+
+#include "tester.h"
+
+
+#define L2CAP_PSM_IPSP		0x0023 /* 6LoWPAN */
+
+struct test_data {
+	const void *test_data;
+	struct mgmt *mgmt;
+	uint16_t mgmt_index;
+	struct hciemu *hciemu;
+	uint16_t handle;
+	uint16_t dcid;
+	unsigned int io_id;
+	int packet_fd;
+};
+
+struct client_data {
+	/* Skip test by default if set */
+	const char *skip_by_default_reason;
+
+	/* Send disconnect command after L2CAP connection */
+	bool disconnect;
+
+	/* Terminate L2CAP connection immediately */
+	bool terminate_l2cap;
+
+	/* Disable 6lowpan immediately on L2CAP connection */
+	bool disable_on_connect;
+
+	/* Data to send to kernel client after L2CAP connect */
+	const void *send_data;
+	uint16_t send_data_len;
+
+	/* Interface listener socket type, SOCK_RAW / DGRAM */
+	int sk_type;
+};
+
+static void print_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_print("%s%s", prefix, str);
+}
+
+static void read_info_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct mgmt_rp_read_info *rp = param;
+	char addr[18];
+	uint16_t manufacturer;
+	uint32_t supported_settings, current_settings;
+
+	tester_print("Read Info callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+	manufacturer = btohs(rp->manufacturer);
+	supported_settings = btohl(rp->supported_settings);
+	current_settings = btohl(rp->current_settings);
+
+	tester_print("  Address: %s", addr);
+	tester_print("  Version: 0x%02x", rp->version);
+	tester_print("  Manufacturer: 0x%04x", manufacturer);
+	tester_print("  Supported settings: 0x%08x", supported_settings);
+	tester_print("  Current settings: 0x%08x", current_settings);
+	tester_print("  Class: 0x%02x%02x%02x",
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+	tester_print("  Name: %s", rp->name);
+	tester_print("  Short name: %s", rp->short_name);
+
+	if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Added callback");
+	tester_print("  Index: 0x%04x", index);
+
+	data->mgmt_index = index;
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+					read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Removed callback");
+	tester_print("  Index: 0x%04x", index);
+
+	if (index != data->mgmt_index)
+		return;
+
+	mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+	mgmt_unref(data->mgmt);
+	data->mgmt = NULL;
+
+	tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Read Index List callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					index_added_callback, NULL, NULL);
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+					index_removed_callback, NULL, NULL);
+
+	data->hciemu = hciemu_new(HCIEMU_TYPE_LE);
+	if (!data->hciemu) {
+		tester_warn("Failed to setup HCI emulation");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (tester_use_debug())
+		hciemu_set_debug(data->hciemu, print_debug, "hciemu: ", NULL);
+
+	tester_print("New hciemu instance created");
+}
+
+static int write_6lowpan(const char *filename, const char *fmt, ...)
+{
+	va_list ap;
+	char path[PATH_MAX];
+	char cmd[512];
+	int fd, ret, len;
+
+	va_start(ap, fmt);
+	len = vsnprintf(cmd, sizeof(cmd), fmt, ap);
+	va_end(ap);
+
+	if (len < 0 || (size_t)len >= sizeof(cmd))
+		return -ENOSPC;
+
+	tester_debug("%s < %s", filename, cmd);
+
+	snprintf(path, sizeof(path), "/sys/kernel/debug/bluetooth/%s", filename);
+
+	fd = open(path, O_WRONLY);
+	if (fd < 0)
+		return -EIO;
+
+	ret = write(fd, cmd, len);
+	if (ret == len)
+		tester_print("%s: OK", filename);
+	else
+		tester_warn("%s: %m", filename);
+
+	close(fd);
+	return ret == len ? 0 : -EIO;
+}
+
+static void test_pre_setup(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct client_data *cdata = data->test_data;
+	int ret;
+
+	if (cdata && cdata->skip_by_default_reason) {
+		tester_warn("Skip test: %s", cdata->skip_by_default_reason);
+		if (tester_pre_setup_skip_by_default())
+			return;
+	}
+
+	ret = write_6lowpan("6lowpan_enable", "1");
+	if (ret < 0) {
+		tester_warn("Failed to enable 6lowpan");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	data->mgmt = mgmt_new_default();
+	if (!data->mgmt) {
+		tester_warn("Failed to setup management interface");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (tester_use_debug())
+		mgmt_set_debug(data->mgmt, print_debug, "mgmt: ", NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
+					read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	int ret;
+
+	if (data->io_id > 0) {
+		g_source_remove(data->io_id);
+		data->io_id = 0;
+	}
+
+	if (data->packet_fd > 0) {
+		close(data->packet_fd);
+		data->packet_fd = -1;
+	}
+
+	ret = write_6lowpan("6lowpan_enable", "0");
+	if (ret < 0) {
+		tester_warn("Failed to disable 6lowpan");
+		tester_post_teardown_failed();
+		return;
+	}
+
+	hciemu_unref(data->hciemu);
+	data->hciemu = NULL;
+}
+
+static void test_data_free(void *test_data)
+{
+	struct test_data *data = test_data;
+
+	free(data);
+}
+
+#define test_6lowpan_full(name, data, setup, func) \
+	do { \
+		struct test_data *user; \
+		user = calloc(1, sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->test_data = data; \
+		tester_add_full(name, data, \
+				test_pre_setup, setup, func, NULL, \
+				test_post_teardown, 5, user, test_data_free); \
+	} while (0)
+
+#define test_6lowpan(name, data, setup, func) \
+	test_6lowpan_full(name, data, setup, func)
+
+static const struct client_data client_connect_terminate = {
+	.terminate_l2cap = true,
+};
+
+static const struct client_data client_connect_disable = {
+	.disable_on_connect = true,
+};
+
+static const struct client_data client_connect_disconnect = {
+	.disconnect = true,
+};
+
+static const uint8_t dgram_data[64+1] = {
+	0x41, /* LOWPAN_DISPATCH_IPV6 */
+	0xde, 0xad, 0xbe, 0xef /* some payload, not actually ipv6 */
+};
+
+static const struct client_data client_recv_dgram = {
+	.send_data = dgram_data,
+	.send_data_len = sizeof(dgram_data),
+	.sk_type = SOCK_DGRAM,
+	.disconnect = true,
+};
+
+static const struct client_data client_recv_raw = {
+	.send_data = dgram_data,
+	.send_data_len = sizeof(dgram_data),
+	.sk_type = SOCK_RAW,
+	.disconnect = true,
+	.skip_by_default_reason = "kernel BUG at net/core/skbuff.c:212"
+};
+
+static void client_cmd_complete(uint16_t opcode, uint8_t status,
+					const void *param, uint8_t len,
+					void *user_data)
+{
+	switch (opcode) {
+	case BT_HCI_CMD_LE_SET_ADV_ENABLE:
+		tester_print("Client set adv enable status 0x%02x", status);
+		break;
+	default:
+		return;
+	}
+
+	if (status)
+		tester_setup_failed();
+	else
+		tester_setup_complete();
+}
+
+static void setup_powered_client_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Controller powered on");
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_cmd_complete_cb(bthost, client_cmd_complete, user_data);
+	bthost_set_adv_enable(bthost, 0x01);
+}
+
+static void setup_powered_common(void)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_BONDABLE, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+}
+
+static void setup_powered_client(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Powering on controller");
+
+	setup_powered_common();
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+				sizeof(param), param,
+				setup_powered_client_callback, NULL, NULL);
+
+	tester_print("OK");
+}
+
+static void test_framework(const void *test_data)
+{
+	tester_test_passed();
+}
+
+static int open_iface(const char *ifname, int sk_type)
+{
+	int sk = -1;
+	struct sockaddr_ll sa;
+	struct ifreq ifr;
+	struct packet_mreq mr;
+	int err;
+	socklen_t len;
+
+	/* Open socket that receives all rx/tx on interface */
+
+	if (!sk_type)
+		sk_type = SOCK_RAW;
+
+	sk = socket(PF_PACKET, sk_type, htons(ETH_P_ALL));
+	if (sk < 0) {
+		tester_print("socket: %m");
+		goto error;
+	}
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, ifname, IF_NAMESIZE);
+	if (ioctl(sk, SIOCGIFINDEX, &ifr) == -1) {
+		tester_print("SIOCGIFINDEX: %s: %m", ifname);
+                goto error;
+        }
+
+	sa.sll_family = AF_PACKET;
+	sa.sll_ifindex = ifr.ifr_ifindex;
+	sa.sll_protocol = 0;
+	if (bind(sk, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
+		tester_print("bind: %m");
+		goto error;
+	}
+
+	memset(&mr, 0, sizeof(mr));
+	mr.mr_ifindex = ifr.ifr_ifindex;
+	mr.mr_type = PACKET_MR_PROMISC;
+	if (setsockopt(sk, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
+						&mr, sizeof(mr)) < 0) {
+		tester_print("PACKET_ADD_MEMBERSHIP: %m");
+		goto error;
+	}
+
+	len = sizeof(err);
+	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+		tester_print("getsockopt: %m");
+                goto error;
+	}
+
+	if (err) {
+		tester_print("SO_ERROR: %d", err);
+		goto error;
+	}
+
+	return sk;
+
+error:
+	if (sk >= 0)
+		close(sk);
+	return -EIO;
+}
+
+static gboolean client_do_disconnect(gpointer user_data)
+{
+	struct test_data *data = user_data;
+	const uint8_t *client_bdaddr;
+	char addr[18];
+
+	data->io_id = 0;
+
+	client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
+	ba2str((void *) client_bdaddr, addr);
+
+	/* XXX: sic - 6lowpan connect takes BDADDR_LE_* but disconnect takes
+	 * XXX: ADDR_LE_DEV_* address type value.
+	 * XXX: This is probably a bug in 6lowpan.c
+	 */
+	if (write_6lowpan("6lowpan_control", "disconnect %s 0", addr))
+		tester_test_failed();
+
+	return FALSE;
+}
+
+static gboolean recv_iface_packet(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct test_data *data = user_data;
+	const struct client_data *cdata = data->test_data;
+	uint8_t buf[256];
+	int fd;
+	ssize_t ret;
+	int phy_hdr_size = (cdata->sk_type == SOCK_DGRAM) ? 1 : 0;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+		return FALSE;
+
+	fd = g_io_channel_unix_get_fd(io);
+	ret = recv(fd, buf, sizeof(buf), 0);
+	if (ret < 0) {
+		tester_print("recv failed");
+		tester_test_failed();
+		return FALSE;
+	}
+
+	tester_print("Recv %d bytes", (int)ret);
+
+	if (ret != cdata->send_data_len - phy_hdr_size)
+		return TRUE;
+	if (memcmp(buf, cdata->send_data + phy_hdr_size, ret))
+		return TRUE;
+
+	tester_print("Received sent packet");
+
+	if (cdata->disconnect)
+		return client_do_disconnect(user_data);
+
+	tester_test_passed();
+	return FALSE;
+}
+
+static gboolean client_open_iface(gpointer user_data)
+{
+	struct test_data *data = user_data;
+	const struct client_data *cdata = data->test_data;
+
+	data->io_id = 0;
+
+	data->packet_fd = open_iface("bt0", cdata->sk_type);
+	if (data->packet_fd < 0) {
+		tester_print("Wait for interface...");
+		data->io_id = g_timeout_add(500, client_open_iface, data);
+		return FALSE;
+	}
+
+	if (cdata->send_data) {
+		struct bthost *bthost;
+		GIOChannel *io;
+
+		bthost = hciemu_client_get_host(data->hciemu);
+
+		io = g_io_channel_unix_new(data->packet_fd);
+		data->io_id = g_io_add_watch(io,
+					G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					recv_iface_packet, data);
+		g_io_channel_unref(io);
+
+		tester_debug("Send %u+1 bytes", cdata->send_data_len - 1);
+		bthost_send_cid(bthost, data->handle, data->dcid, cdata->send_data,
+				cdata->send_data_len);
+	} else if (cdata->disconnect) {
+		data->io_id = g_idle_add(client_do_disconnect, data);
+	}
+
+	return FALSE;
+}
+
+static void client_l2cap_connect_cb(uint16_t handle, uint16_t cid,
+							void *user_data)
+{
+	struct test_data *data = user_data;
+	const struct client_data *cdata = data->test_data;
+
+	tester_debug("Client connect CID 0x%04x handle 0x%04x", cid, handle);
+
+	data->handle = handle;
+	data->dcid = cid;
+
+	if (cdata->terminate_l2cap) {
+		struct bthost *bthost;
+		struct bt_l2cap_pdu_disconn_req req;
+
+		bthost = hciemu_client_get_host(data->hciemu);
+
+		memset(&req, 0, sizeof(req));
+		req.scid = cpu_to_le16(cid);
+		req.dcid = cpu_to_le16(cid);
+
+		bthost_l2cap_req(bthost, handle, BT_L2CAP_PDU_DISCONN_REQ,
+				&req, sizeof(req), NULL, NULL);
+	}
+
+	/* Wait until kernel handles L2CAP connect RSP */
+	if (cdata->send_data || cdata->disconnect)
+		data->io_id = g_idle_add(client_open_iface, data);
+
+	if (cdata->disable_on_connect) {
+		if (write_6lowpan("6lowpan_enable", "0"))
+			tester_test_failed();
+		else
+			tester_test_passed();
+	}
+}
+
+static void client_l2cap_disconnect_cb(uint16_t handle, uint16_t cid,
+							void *user_data)
+{
+	struct test_data *data = user_data;
+	const struct client_data *cdata = data->test_data;
+
+	tester_debug("Client disconnect CID 0x%04x handle 0x%04x", cid, handle);
+
+	if ((cdata->terminate_l2cap || cdata->disconnect) &&
+				handle == data->handle && cid == data->dcid)
+		tester_test_passed();
+}
+
+static void test_connect(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	const uint8_t *client_bdaddr;
+	char addr[18];
+
+	bthost_add_l2cap_server_custom(bthost, L2CAP_PSM_IPSP,
+				1280, 1280, 1,
+				client_l2cap_connect_cb,
+				client_l2cap_disconnect_cb,
+				data);
+
+	client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
+	ba2str((void *) client_bdaddr, addr);
+
+	if (write_6lowpan("6lowpan_control", "connect %s 1", addr))
+		tester_test_failed();
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	test_6lowpan("Basic Framework - Success", NULL, setup_powered_client,
+							test_framework);
+
+	test_6lowpan("Client Connect - Terminate", &client_connect_terminate,
+							setup_powered_client,
+							test_connect);
+
+	test_6lowpan("Client Connect - Disable", &client_connect_disable,
+							setup_powered_client,
+							test_connect);
+
+	test_6lowpan("Client Connect - Disconnect", &client_connect_disconnect,
+							setup_powered_client,
+							test_connect);
+
+	test_6lowpan("Client Recv Dgram - Success", &client_recv_dgram,
+							setup_powered_client,
+							test_connect);
+
+	test_6lowpan("Client Recv Raw - Success", &client_recv_raw,
+							setup_powered_client,
+							test_connect);
+
+	return tester_run();
+}
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 4/4] doc: enable 6lowpan in tester.config and explain in test-runner.rst
  2025-10-05 22:17 [PATCH BlueZ 0/4] Add 6lowpan-tester Pauli Virtanen
                   ` (2 preceding siblings ...)
  2025-10-05 22:17 ` [PATCH BlueZ 3/4] tools: add 6lowpan-tester Pauli Virtanen
@ 2025-10-05 22:17 ` Pauli Virtanen
  2025-10-13 17:40 ` [PATCH BlueZ 0/4] Add 6lowpan-tester patchwork-bot+bluetooth
  2025-10-13 19:00 ` patchwork-bot+bluetooth
  5 siblings, 0 replies; 9+ messages in thread
From: Pauli Virtanen @ 2025-10-05 22:17 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Now that there is a 6lowpan-tester, enable requirements in tester
config, and explain what is needed in test-runner.rst
---
 doc/test-runner.rst | 10 ++++++++++
 doc/tester.config   |  5 +++++
 2 files changed, 15 insertions(+)

diff --git a/doc/test-runner.rst b/doc/test-runner.rst
index cb56d5b94..d3e5f2984 100644
--- a/doc/test-runner.rst
+++ b/doc/test-runner.rst
@@ -92,6 +92,16 @@ Bluetooth
 
 	CONFIG_UHID=y
 
+For 6lowpan-tester, the following are required:
+
+.. code-block::
+
+   CONFIG_6LOWPAN=y
+   CONFIG_6LOWPAN_DEBUGFS=y
+   CONFIG_BT_6LOWPAN=y
+   CONFIG_PACKET=y
+
+
 Lock debugging
 --------------
 
diff --git a/doc/tester.config b/doc/tester.config
index 24a0ca1eb..4ee306405 100644
--- a/doc/tester.config
+++ b/doc/tester.config
@@ -60,6 +60,11 @@ CONFIG_USB_XHCI_PLATFORM=y
 
 CONFIG_SERIAL_DEV_BUS=y
 
+CONFIG_6LOWPAN=y
+CONFIG_6LOWPAN_DEBUGFS=y
+CONFIG_BT_6LOWPAN=y
+CONFIG_PACKET=y
+
 #
 # Bluetooth device drivers
 #
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* RE: Add 6lowpan-tester
  2025-10-05 22:17 ` [PATCH BlueZ 1/4] bthost: handle L2CAP_DISCONN_RSP and remove l2cap_conns Pauli Virtanen
@ 2025-10-05 23:42   ` bluez.test.bot
  0 siblings, 0 replies; 9+ messages in thread
From: bluez.test.bot @ 2025-10-05 23:42 UTC (permalink / raw)
  To: linux-bluetooth, pav

[-- Attachment #1: Type: text/plain, Size: 2014 bytes --]

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

---Test result---

Test Summary:
CheckPatch                    PENDING   0.23 seconds
GitLint                       PENDING   0.34 seconds
BuildEll                      PASS      20.02 seconds
BluezMake                     PASS      2682.42 seconds
MakeCheck                     PASS      20.07 seconds
MakeDistcheck                 PASS      185.23 seconds
CheckValgrind                 PASS      237.40 seconds
CheckSmatch                   WARNING   308.46 seconds
bluezmakeextell               PASS      128.98 seconds
IncrementalBuild              PENDING   0.29 seconds
ScanBuild                     PASS      921.00 seconds

Details
##############################
Test: CheckPatch - PENDING
Desc: Run checkpatch.pl script
Output:

##############################
Test: GitLint - PENDING
Desc: Run gitlint
Output:

##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
emulator/bthost.c:699:28: warning: Variable length array is used.emulator/bthost.c:700:32: warning: Variable length array is used.emulator/bthost.c:917:28: warning: Variable length array is used.emulator/bthost.c:951:28: warning: Variable length array is used.emulator/bthost.c:952:32: warning: Variable length array is used.emulator/bthost.c:699:28: warning: Variable length array is used.emulator/bthost.c:700:32: warning: Variable length array is used.emulator/bthost.c:917:28: warning: Variable length array is used.emulator/bthost.c:951:28: warning: Variable length array is used.emulator/bthost.c:952:32: warning: Variable length array is used.
##############################
Test: IncrementalBuild - PENDING
Desc: Incremental build with the patches in the series
Output:



---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH BlueZ 3/4] tools: add 6lowpan-tester
  2025-10-05 22:17 ` [PATCH BlueZ 3/4] tools: add 6lowpan-tester Pauli Virtanen
@ 2025-10-13 17:20   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2025-10-13 17:20 UTC (permalink / raw)
  To: Pauli Virtanen; +Cc: linux-bluetooth

Hi Pauli,

On Sun, Oct 5, 2025 at 6:18 PM Pauli Virtanen <pav@iki.fi> wrote:
>
> Add 6lowpan smoke testing. It only exercises the kernel 6lowpan L2CAP
> connection handling, ipv6 testing is mostly out of scope for the tester.
>
> Add tests:
>
> Basic Framework - Success
> Client Connect - Terminate
> Client Connect - Disable
> Client Connect - Disconnect
> Client Recv Dgram - Success
> Client Recv Raw - Success
> ---
>  Makefile.tools         |  12 +-
>  tools/6lowpan-tester.c | 657 +++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 668 insertions(+), 1 deletion(-)
>  create mode 100644 tools/6lowpan-tester.c
>
> diff --git a/Makefile.tools b/Makefile.tools
> index e60c31b1d..561b03d0b 100644
> --- a/Makefile.tools
> +++ b/Makefile.tools
> @@ -87,7 +87,8 @@ noinst_PROGRAMS += emulator/btvirt emulator/b1ee emulator/hfp \
>                                         tools/smp-tester tools/hci-tester \
>                                         tools/rfcomm-tester tools/bnep-tester \
>                                         tools/userchan-tester tools/iso-tester \
> -                                       tools/mesh-tester tools/ioctl-tester
> +                                       tools/mesh-tester tools/ioctl-tester \
> +                                       tools/6lowpan-tester
>
>  emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
>                                 emulator/serial.h emulator/serial.c \
> @@ -221,6 +222,15 @@ tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \
>                                 emulator/smp.c
>  tools_ioctl_tester_LDADD = lib/libbluetooth-internal.la \
>                                 src/libshared-glib.la $(GLIB_LIBS)
> +
> +tools_6lowpan_tester_SOURCES = tools/6lowpan-tester.c monitor/bt.h \
> +                               emulator/hciemu.h emulator/hciemu.c \
> +                               emulator/vhci.h emulator/vhci.c \
> +                               emulator/btdev.h emulator/btdev.c \
> +                               emulator/bthost.h emulator/bthost.c \
> +                               emulator/smp.c
> +tools_6lowpan_tester_LDADD = lib/libbluetooth-internal.la \
> +                               src/libshared-glib.la $(GLIB_LIBS)
>  endif
>
>  if TOOLS
> diff --git a/tools/6lowpan-tester.c b/tools/6lowpan-tester.c
> new file mode 100644
> index 000000000..89ed9277e
> --- /dev/null
> +++ b/tools/6lowpan-tester.c
> @@ -0,0 +1,657 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + *
> + *  BlueZ - Bluetooth protocol stack for Linux
> + *
> + *  Copyright (C) 2013  Intel Corporation. All rights reserved.
> + *
> + *
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <stdbool.h>
> +#include <fcntl.h>
> +#include <signal.h>
> +
> +#include <net/if.h>
> +#include <linux/if_packet.h>
> +#include <sys/ioctl.h>
> +
> +#include <glib.h>
> +
> +#include "bluetooth/bluetooth.h"
> +#include "bluetooth/mgmt.h"
> +
> +#include "monitor/bt.h"
> +#include "emulator/bthost.h"
> +#include "emulator/hciemu.h"
> +
> +#include "src/shared/tester.h"
> +#include "src/shared/mgmt.h"
> +#include "src/shared/util.h"
> +
> +#include "tester.h"
> +
> +
> +#define L2CAP_PSM_IPSP         0x0023 /* 6LoWPAN */
> +
> +struct test_data {
> +       const void *test_data;
> +       struct mgmt *mgmt;
> +       uint16_t mgmt_index;
> +       struct hciemu *hciemu;
> +       uint16_t handle;
> +       uint16_t dcid;
> +       unsigned int io_id;
> +       int packet_fd;
> +};
> +
> +struct client_data {
> +       /* Skip test by default if set */
> +       const char *skip_by_default_reason;
> +
> +       /* Send disconnect command after L2CAP connection */
> +       bool disconnect;
> +
> +       /* Terminate L2CAP connection immediately */
> +       bool terminate_l2cap;
> +
> +       /* Disable 6lowpan immediately on L2CAP connection */
> +       bool disable_on_connect;
> +
> +       /* Data to send to kernel client after L2CAP connect */
> +       const void *send_data;
> +       uint16_t send_data_len;
> +
> +       /* Interface listener socket type, SOCK_RAW / DGRAM */
> +       int sk_type;
> +};
> +
> +static void print_debug(const char *str, void *user_data)
> +{
> +       const char *prefix = user_data;
> +
> +       tester_print("%s%s", prefix, str);
> +}
> +
> +static void read_info_callback(uint8_t status, uint16_t length,
> +                                       const void *param, void *user_data)
> +{
> +       struct test_data *data = tester_get_data();
> +       const struct mgmt_rp_read_info *rp = param;
> +       char addr[18];
> +       uint16_t manufacturer;
> +       uint32_t supported_settings, current_settings;
> +
> +       tester_print("Read Info callback");
> +       tester_print("  Status: 0x%02x", status);
> +
> +       if (status || !param) {
> +               tester_pre_setup_failed();
> +               return;
> +       }
> +
> +       ba2str(&rp->bdaddr, addr);
> +       manufacturer = btohs(rp->manufacturer);
> +       supported_settings = btohl(rp->supported_settings);
> +       current_settings = btohl(rp->current_settings);
> +
> +       tester_print("  Address: %s", addr);
> +       tester_print("  Version: 0x%02x", rp->version);
> +       tester_print("  Manufacturer: 0x%04x", manufacturer);
> +       tester_print("  Supported settings: 0x%08x", supported_settings);
> +       tester_print("  Current settings: 0x%08x", current_settings);
> +       tester_print("  Class: 0x%02x%02x%02x",
> +                       rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
> +       tester_print("  Name: %s", rp->name);
> +       tester_print("  Short name: %s", rp->short_name);
> +
> +       if (strcmp(hciemu_get_address(data->hciemu), addr)) {
> +               tester_pre_setup_failed();
> +               return;
> +       }
> +
> +       tester_pre_setup_complete();
> +}
> +
> +static void index_added_callback(uint16_t index, uint16_t length,
> +                                       const void *param, void *user_data)
> +{
> +       struct test_data *data = tester_get_data();
> +
> +       tester_print("Index Added callback");
> +       tester_print("  Index: 0x%04x", index);
> +
> +       data->mgmt_index = index;
> +
> +       mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
> +                                       read_info_callback, NULL, NULL);
> +}
> +
> +static void index_removed_callback(uint16_t index, uint16_t length,
> +                                       const void *param, void *user_data)
> +{
> +       struct test_data *data = tester_get_data();
> +
> +       tester_print("Index Removed callback");
> +       tester_print("  Index: 0x%04x", index);
> +
> +       if (index != data->mgmt_index)
> +               return;
> +
> +       mgmt_unregister_index(data->mgmt, data->mgmt_index);
> +
> +       mgmt_unref(data->mgmt);
> +       data->mgmt = NULL;
> +
> +       tester_post_teardown_complete();
> +}
> +
> +static void read_index_list_callback(uint8_t status, uint16_t length,
> +                                       const void *param, void *user_data)
> +{
> +       struct test_data *data = tester_get_data();
> +
> +       tester_print("Read Index List callback");
> +       tester_print("  Status: 0x%02x", status);
> +
> +       if (status || !param) {
> +               tester_pre_setup_failed();
> +               return;
> +       }
> +
> +       mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
> +                                       index_added_callback, NULL, NULL);
> +
> +       mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
> +                                       index_removed_callback, NULL, NULL);
> +
> +       data->hciemu = hciemu_new(HCIEMU_TYPE_LE);
> +       if (!data->hciemu) {
> +               tester_warn("Failed to setup HCI emulation");
> +               tester_pre_setup_failed();
> +               return;
> +       }
> +
> +       if (tester_use_debug())
> +               hciemu_set_debug(data->hciemu, print_debug, "hciemu: ", NULL);
> +
> +       tester_print("New hciemu instance created");
> +}
> +
> +static int write_6lowpan(const char *filename, const char *fmt, ...)
> +{
> +       va_list ap;
> +       char path[PATH_MAX];
> +       char cmd[512];
> +       int fd, ret, len;
> +
> +       va_start(ap, fmt);
> +       len = vsnprintf(cmd, sizeof(cmd), fmt, ap);
> +       va_end(ap);
> +
> +       if (len < 0 || (size_t)len >= sizeof(cmd))
> +               return -ENOSPC;
> +
> +       tester_debug("%s < %s", filename, cmd);
> +
> +       snprintf(path, sizeof(path), "/sys/kernel/debug/bluetooth/%s", filename);
> +
> +       fd = open(path, O_WRONLY);
> +       if (fd < 0)
> +               return -EIO;
> +
> +       ret = write(fd, cmd, len);
> +       if (ret == len)
> +               tester_print("%s: OK", filename);
> +       else
> +               tester_warn("%s: %m", filename);
> +
> +       close(fd);
> +       return ret == len ? 0 : -EIO;
> +}
> +
> +static void test_pre_setup(const void *test_data)
> +{
> +       struct test_data *data = tester_get_data();
> +       const struct client_data *cdata = data->test_data;
> +       int ret;
> +
> +       if (cdata && cdata->skip_by_default_reason) {
> +               tester_warn("Skip test: %s", cdata->skip_by_default_reason);
> +               if (tester_pre_setup_skip_by_default())
> +                       return;
> +       }
> +
> +       ret = write_6lowpan("6lowpan_enable", "1");
> +       if (ret < 0) {
> +               tester_warn("Failed to enable 6lowpan");
> +               tester_pre_setup_failed();
> +               return;
> +       }
> +
> +       data->mgmt = mgmt_new_default();
> +       if (!data->mgmt) {
> +               tester_warn("Failed to setup management interface");
> +               tester_pre_setup_failed();
> +               return;
> +       }
> +
> +       if (tester_use_debug())
> +               mgmt_set_debug(data->mgmt, print_debug, "mgmt: ", NULL);
> +
> +       mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
> +                                       read_index_list_callback, NULL, NULL);
> +}
> +
> +static void test_post_teardown(const void *test_data)
> +{
> +       struct test_data *data = tester_get_data();
> +       int ret;
> +
> +       if (data->io_id > 0) {
> +               g_source_remove(data->io_id);
> +               data->io_id = 0;
> +       }
> +
> +       if (data->packet_fd > 0) {
> +               close(data->packet_fd);
> +               data->packet_fd = -1;
> +       }
> +
> +       ret = write_6lowpan("6lowpan_enable", "0");
> +       if (ret < 0) {
> +               tester_warn("Failed to disable 6lowpan");
> +               tester_post_teardown_failed();
> +               return;
> +       }
> +
> +       hciemu_unref(data->hciemu);
> +       data->hciemu = NULL;
> +}
> +
> +static void test_data_free(void *test_data)
> +{
> +       struct test_data *data = test_data;
> +
> +       free(data);
> +}
> +
> +#define test_6lowpan_full(name, data, setup, func) \
> +       do { \
> +               struct test_data *user; \
> +               user = calloc(1, sizeof(struct test_data)); \
> +               if (!user) \
> +                       break; \
> +               user->test_data = data; \
> +               tester_add_full(name, data, \
> +                               test_pre_setup, setup, func, NULL, \
> +                               test_post_teardown, 5, user, test_data_free); \
> +       } while (0)
> +
> +#define test_6lowpan(name, data, setup, func) \
> +       test_6lowpan_full(name, data, setup, func)
> +
> +static const struct client_data client_connect_terminate = {
> +       .terminate_l2cap = true,
> +};
> +
> +static const struct client_data client_connect_disable = {
> +       .disable_on_connect = true,
> +};
> +
> +static const struct client_data client_connect_disconnect = {
> +       .disconnect = true,
> +};
> +
> +static const uint8_t dgram_data[64+1] = {
> +       0x41, /* LOWPAN_DISPATCH_IPV6 */
> +       0xde, 0xad, 0xbe, 0xef /* some payload, not actually ipv6 */
> +};
> +
> +static const struct client_data client_recv_dgram = {
> +       .send_data = dgram_data,
> +       .send_data_len = sizeof(dgram_data),
> +       .sk_type = SOCK_DGRAM,
> +       .disconnect = true,
> +};
> +
> +static const struct client_data client_recv_raw = {
> +       .send_data = dgram_data,
> +       .send_data_len = sizeof(dgram_data),
> +       .sk_type = SOCK_RAW,
> +       .disconnect = true,
> +       .skip_by_default_reason = "kernel BUG at net/core/skbuff.c:212"
> +};
> +
> +static void client_cmd_complete(uint16_t opcode, uint8_t status,
> +                                       const void *param, uint8_t len,
> +                                       void *user_data)
> +{
> +       switch (opcode) {
> +       case BT_HCI_CMD_LE_SET_ADV_ENABLE:
> +               tester_print("Client set adv enable status 0x%02x", status);
> +               break;
> +       default:
> +               return;
> +       }
> +
> +       if (status)
> +               tester_setup_failed();
> +       else
> +               tester_setup_complete();
> +}
> +
> +static void setup_powered_client_callback(uint8_t status, uint16_t length,
> +                                       const void *param, void *user_data)
> +{
> +       struct test_data *data = tester_get_data();
> +       struct bthost *bthost;
> +
> +       if (status != MGMT_STATUS_SUCCESS) {
> +               tester_setup_failed();
> +               return;
> +       }
> +
> +       tester_print("Controller powered on");
> +
> +       bthost = hciemu_client_get_host(data->hciemu);
> +       bthost_set_cmd_complete_cb(bthost, client_cmd_complete, user_data);
> +       bthost_set_adv_enable(bthost, 0x01);
> +}
> +
> +static void setup_powered_common(void)
> +{
> +       struct test_data *data = tester_get_data();
> +       unsigned char param[] = { 0x01 };
> +
> +       mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
> +                               sizeof(param), param, NULL, NULL, NULL);
> +
> +       mgmt_send(data->mgmt, MGMT_OP_SET_BONDABLE, data->mgmt_index,
> +                               sizeof(param), param, NULL, NULL, NULL);
> +}
> +
> +static void setup_powered_client(const void *test_data)
> +{
> +       struct test_data *data = tester_get_data();
> +       unsigned char param[] = { 0x01 };
> +
> +       tester_print("Powering on controller");
> +
> +       setup_powered_common();
> +
> +       mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
> +                               sizeof(param), param,
> +                               setup_powered_client_callback, NULL, NULL);
> +
> +       tester_print("OK");
> +}
> +
> +static void test_framework(const void *test_data)
> +{
> +       tester_test_passed();
> +}
> +
> +static int open_iface(const char *ifname, int sk_type)
> +{
> +       int sk = -1;
> +       struct sockaddr_ll sa;
> +       struct ifreq ifr;
> +       struct packet_mreq mr;
> +       int err;
> +       socklen_t len;
> +
> +       /* Open socket that receives all rx/tx on interface */
> +
> +       if (!sk_type)
> +               sk_type = SOCK_RAW;
> +
> +       sk = socket(PF_PACKET, sk_type, htons(ETH_P_ALL));
> +       if (sk < 0) {
> +               tester_print("socket: %m");
> +               goto error;
> +       }
> +
> +       memset(&ifr, 0, sizeof(ifr));
> +       strncpy(ifr.ifr_name, ifname, IF_NAMESIZE);
> +       if (ioctl(sk, SIOCGIFINDEX, &ifr) == -1) {
> +               tester_print("SIOCGIFINDEX: %s: %m", ifname);
> +                goto error;
> +        }
> +
> +       sa.sll_family = AF_PACKET;
> +       sa.sll_ifindex = ifr.ifr_ifindex;
> +       sa.sll_protocol = 0;
> +       if (bind(sk, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
> +               tester_print("bind: %m");
> +               goto error;
> +       }
> +
> +       memset(&mr, 0, sizeof(mr));
> +       mr.mr_ifindex = ifr.ifr_ifindex;
> +       mr.mr_type = PACKET_MR_PROMISC;
> +       if (setsockopt(sk, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
> +                                               &mr, sizeof(mr)) < 0) {
> +               tester_print("PACKET_ADD_MEMBERSHIP: %m");
> +               goto error;
> +       }
> +
> +       len = sizeof(err);
> +       if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
> +               tester_print("getsockopt: %m");
> +                goto error;
> +       }
> +
> +       if (err) {
> +               tester_print("SO_ERROR: %d", err);
> +               goto error;
> +       }
> +
> +       return sk;
> +
> +error:
> +       if (sk >= 0)
> +               close(sk);
> +       return -EIO;
> +}
> +
> +static gboolean client_do_disconnect(gpointer user_data)
> +{
> +       struct test_data *data = user_data;
> +       const uint8_t *client_bdaddr;
> +       char addr[18];
> +
> +       data->io_id = 0;
> +
> +       client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
> +       ba2str((void *) client_bdaddr, addr);
> +
> +       /* XXX: sic - 6lowpan connect takes BDADDR_LE_* but disconnect takes
> +        * XXX: ADDR_LE_DEV_* address type value.
> +        * XXX: This is probably a bug in 6lowpan.c
> +        */
> +       if (write_6lowpan("6lowpan_control", "disconnect %s 0", addr))
> +               tester_test_failed();
> +
> +       return FALSE;
> +}
> +
> +static gboolean recv_iface_packet(GIOChannel *io, GIOCondition cond,
> +                                                       gpointer user_data)
> +{
> +       struct test_data *data = user_data;
> +       const struct client_data *cdata = data->test_data;
> +       uint8_t buf[256];
> +       int fd;
> +       ssize_t ret;
> +       int phy_hdr_size = (cdata->sk_type == SOCK_DGRAM) ? 1 : 0;
> +
> +       if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
> +               return FALSE;
> +
> +       fd = g_io_channel_unix_get_fd(io);
> +       ret = recv(fd, buf, sizeof(buf), 0);
> +       if (ret < 0) {
> +               tester_print("recv failed");
> +               tester_test_failed();
> +               return FALSE;
> +       }
> +
> +       tester_print("Recv %d bytes", (int)ret);
> +
> +       if (ret != cdata->send_data_len - phy_hdr_size)
> +               return TRUE;
> +       if (memcmp(buf, cdata->send_data + phy_hdr_size, ret))
> +               return TRUE;
> +
> +       tester_print("Received sent packet");
> +
> +       if (cdata->disconnect)
> +               return client_do_disconnect(user_data);
> +
> +       tester_test_passed();
> +       return FALSE;
> +}
> +
> +static gboolean client_open_iface(gpointer user_data)
> +{
> +       struct test_data *data = user_data;
> +       const struct client_data *cdata = data->test_data;
> +
> +       data->io_id = 0;
> +
> +       data->packet_fd = open_iface("bt0", cdata->sk_type);
> +       if (data->packet_fd < 0) {
> +               tester_print("Wait for interface...");
> +               data->io_id = g_timeout_add(500, client_open_iface, data);
> +               return FALSE;
> +       }
> +
> +       if (cdata->send_data) {
> +               struct bthost *bthost;
> +               GIOChannel *io;
> +
> +               bthost = hciemu_client_get_host(data->hciemu);
> +
> +               io = g_io_channel_unix_new(data->packet_fd);
> +               data->io_id = g_io_add_watch(io,
> +                                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
> +                                       recv_iface_packet, data);
> +               g_io_channel_unref(io);
> +
> +               tester_debug("Send %u+1 bytes", cdata->send_data_len - 1);
> +               bthost_send_cid(bthost, data->handle, data->dcid, cdata->send_data,
> +                               cdata->send_data_len);
> +       } else if (cdata->disconnect) {
> +               data->io_id = g_idle_add(client_do_disconnect, data);
> +       }
> +
> +       return FALSE;
> +}
> +
> +static void client_l2cap_connect_cb(uint16_t handle, uint16_t cid,
> +                                                       void *user_data)
> +{
> +       struct test_data *data = user_data;
> +       const struct client_data *cdata = data->test_data;
> +
> +       tester_debug("Client connect CID 0x%04x handle 0x%04x", cid, handle);
> +
> +       data->handle = handle;
> +       data->dcid = cid;
> +
> +       if (cdata->terminate_l2cap) {
> +               struct bthost *bthost;
> +               struct bt_l2cap_pdu_disconn_req req;
> +
> +               bthost = hciemu_client_get_host(data->hciemu);
> +
> +               memset(&req, 0, sizeof(req));
> +               req.scid = cpu_to_le16(cid);
> +               req.dcid = cpu_to_le16(cid);
> +
> +               bthost_l2cap_req(bthost, handle, BT_L2CAP_PDU_DISCONN_REQ,
> +                               &req, sizeof(req), NULL, NULL);
> +       }
> +
> +       /* Wait until kernel handles L2CAP connect RSP */
> +       if (cdata->send_data || cdata->disconnect)
> +               data->io_id = g_idle_add(client_open_iface, data);
> +
> +       if (cdata->disable_on_connect) {
> +               if (write_6lowpan("6lowpan_enable", "0"))
> +                       tester_test_failed();
> +               else
> +                       tester_test_passed();
> +       }
> +}
> +
> +static void client_l2cap_disconnect_cb(uint16_t handle, uint16_t cid,
> +                                                       void *user_data)
> +{
> +       struct test_data *data = user_data;
> +       const struct client_data *cdata = data->test_data;
> +
> +       tester_debug("Client disconnect CID 0x%04x handle 0x%04x", cid, handle);
> +
> +       if ((cdata->terminate_l2cap || cdata->disconnect) &&
> +                               handle == data->handle && cid == data->dcid)
> +               tester_test_passed();
> +}
> +
> +static void test_connect(const void *test_data)
> +{
> +       struct test_data *data = tester_get_data();
> +       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
> +       const uint8_t *client_bdaddr;
> +       char addr[18];
> +
> +       bthost_add_l2cap_server_custom(bthost, L2CAP_PSM_IPSP,
> +                               1280, 1280, 1,
> +                               client_l2cap_connect_cb,
> +                               client_l2cap_disconnect_cb,
> +                               data);
> +
> +       client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
> +       ba2str((void *) client_bdaddr, addr);
> +
> +       if (write_6lowpan("6lowpan_control", "connect %s 1", addr))
> +               tester_test_failed();
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +       tester_init(&argc, &argv);
> +
> +       test_6lowpan("Basic Framework - Success", NULL, setup_powered_client,
> +                                                       test_framework);
> +
> +       test_6lowpan("Client Connect - Terminate", &client_connect_terminate,
> +                                                       setup_powered_client,
> +                                                       test_connect);
> +
> +       test_6lowpan("Client Connect - Disable", &client_connect_disable,
> +                                                       setup_powered_client,
> +                                                       test_connect);
> +
> +       test_6lowpan("Client Connect - Disconnect", &client_connect_disconnect,
> +                                                       setup_powered_client,
> +                                                       test_connect);
> +
> +       test_6lowpan("Client Recv Dgram - Success", &client_recv_dgram,
> +                                                       setup_powered_client,
> +                                                       test_connect);
> +
> +       test_6lowpan("Client Recv Raw - Success", &client_recv_raw,
> +                                                       setup_powered_client,
> +                                                       test_connect);
> +
> +       return tester_run();
> +}
> --
> 2.51.0

This one has quite a few problems:

Applying: tools: add 6lowpan-tester
WARNING:LONG_LINE: line length of 81 exceeds 80 columns
#240: FILE: tools/6lowpan-tester.c:204:
+    snprintf(path, sizeof(path), "/sys/kernel/debug/bluetooth/%s", filename);

WARNING:STRNCPY: Prefer strscpy, strscpy_pad, or __nonstring over
strncpy - see: https://github.com/KSPP/linux/issues/90
#459: FILE: tools/6lowpan-tester.c:423:
+    strncpy(ifr.ifr_name, ifname, IF_NAMESIZE);

ERROR:CODE_INDENT: code indent should use tabs where possible
#462: FILE: tools/6lowpan-tester.c:426:
+                goto error;$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#462: FILE: tools/6lowpan-tester.c:426:
+                goto error;$

ERROR:CODE_INDENT: code indent should use tabs where possible
#463: FILE: tools/6lowpan-tester.c:427:
+        }$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#463: FILE: tools/6lowpan-tester.c:427:
+        }$

ERROR:CODE_INDENT: code indent should use tabs where possible
#485: FILE: tools/6lowpan-tester.c:449:
+                goto error;$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#485: FILE: tools/6lowpan-tester.c:449:
+                goto error;$

WARNING:LONG_LINE: line length of 82 exceeds 80 columns
#581: FILE: tools/6lowpan-tester.c:545:
+                    G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,

WARNING:LONG_LINE: line length of 83 exceeds 80 columns
#586: FILE: tools/6lowpan-tester.c:550:
+        bthost_send_cid(bthost, data->handle, data->dcid, cdata->send_data,


Only the second I think is not really valid since that appears to be a
kernel only function not available in userspace.

-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH BlueZ 0/4] Add 6lowpan-tester
  2025-10-05 22:17 [PATCH BlueZ 0/4] Add 6lowpan-tester Pauli Virtanen
                   ` (3 preceding siblings ...)
  2025-10-05 22:17 ` [PATCH BlueZ 4/4] doc: enable 6lowpan in tester.config and explain in test-runner.rst Pauli Virtanen
@ 2025-10-13 17:40 ` patchwork-bot+bluetooth
  2025-10-13 19:00 ` patchwork-bot+bluetooth
  5 siblings, 0 replies; 9+ messages in thread
From: patchwork-bot+bluetooth @ 2025-10-13 17:40 UTC (permalink / raw)
  To: Pauli Virtanen; +Cc: linux-bluetooth

Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Mon,  6 Oct 2025 01:17:38 +0300 you wrote:
> Make a basic tester for the net/bluetooth/6lowpan.c implementation.
> 
> This module doesn't look like it's much used and locking around
> l2cap_chan / hci_conn there looks suspicious, so probably some tests are
> useful.
> 
> Here just simple connection / disconnection and packet RX tests are
> added.  Some issues appear to be there:
> 
> [...]

Here is the summary with links:
  - [BlueZ,1/4] bthost: handle L2CAP_DISCONN_RSP and remove l2cap_conns
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=05f3aab743f0
  - [BlueZ,2/4] bthost: handle L2CAP_FLOW_CONTROL_CREDIT_IND
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=3e17f888c840
  - [BlueZ,3/4] tools: add 6lowpan-tester
    (no matching commit)
  - [BlueZ,4/4] doc: enable 6lowpan in tester.config and explain in test-runner.rst
    (no matching commit)

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH BlueZ 0/4] Add 6lowpan-tester
  2025-10-05 22:17 [PATCH BlueZ 0/4] Add 6lowpan-tester Pauli Virtanen
                   ` (4 preceding siblings ...)
  2025-10-13 17:40 ` [PATCH BlueZ 0/4] Add 6lowpan-tester patchwork-bot+bluetooth
@ 2025-10-13 19:00 ` patchwork-bot+bluetooth
  5 siblings, 0 replies; 9+ messages in thread
From: patchwork-bot+bluetooth @ 2025-10-13 19:00 UTC (permalink / raw)
  To: Pauli Virtanen; +Cc: linux-bluetooth

Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Mon,  6 Oct 2025 01:17:38 +0300 you wrote:
> Make a basic tester for the net/bluetooth/6lowpan.c implementation.
> 
> This module doesn't look like it's much used and locking around
> l2cap_chan / hci_conn there looks suspicious, so probably some tests are
> useful.
> 
> Here just simple connection / disconnection and packet RX tests are
> added.  Some issues appear to be there:
> 
> [...]

Here is the summary with links:
  - [BlueZ,1/4] bthost: handle L2CAP_DISCONN_RSP and remove l2cap_conns
    (no matching commit)
  - [BlueZ,2/4] bthost: handle L2CAP_FLOW_CONTROL_CREDIT_IND
    (no matching commit)
  - [BlueZ,3/4] tools: add 6lowpan-tester
    (no matching commit)
  - [BlueZ,4/4] doc: enable 6lowpan in tester.config and explain in test-runner.rst
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=2db2d3593799

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2025-10-13 19:00 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-05 22:17 [PATCH BlueZ 0/4] Add 6lowpan-tester Pauli Virtanen
2025-10-05 22:17 ` [PATCH BlueZ 1/4] bthost: handle L2CAP_DISCONN_RSP and remove l2cap_conns Pauli Virtanen
2025-10-05 23:42   ` Add 6lowpan-tester bluez.test.bot
2025-10-05 22:17 ` [PATCH BlueZ 2/4] bthost: handle L2CAP_FLOW_CONTROL_CREDIT_IND Pauli Virtanen
2025-10-05 22:17 ` [PATCH BlueZ 3/4] tools: add 6lowpan-tester Pauli Virtanen
2025-10-13 17:20   ` Luiz Augusto von Dentz
2025-10-05 22:17 ` [PATCH BlueZ 4/4] doc: enable 6lowpan in tester.config and explain in test-runner.rst Pauli Virtanen
2025-10-13 17:40 ` [PATCH BlueZ 0/4] Add 6lowpan-tester patchwork-bot+bluetooth
2025-10-13 19:00 ` patchwork-bot+bluetooth

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox