diff mbox series

[BlueZ,v2] mgmt-tester: Ensure rfkilling HCI disconnects devices

Message ID 20240206101415.46505-1-verdre@v0yd.nl (mailing list archive)
State New, archived
Headers show
Series [BlueZ,v2] mgmt-tester: Ensure rfkilling HCI disconnects devices | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
tedd_an/CheckPatch success CheckPatch PASS
tedd_an/GitLint fail WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search 20: B1 Line exceeds max length (82>80): "v1: https://lore.kernel.org/linux-bluetooth/20240128210547.16141-1-verdre@v0yd.nl/"
tedd_an/BuildEll success Build ELL PASS
tedd_an/BluezMake success Bluez Make PASS
tedd_an/MakeCheck success Bluez Make Check PASS
tedd_an/MakeDistcheck success Make Distcheck PASS
tedd_an/CheckValgrind success Check Valgrind PASS
tedd_an/CheckSmatch success CheckSparse PASS
tedd_an/bluezmakeextell success Make External ELL PASS
tedd_an/IncrementalBuild success Incremental Build PASS
tedd_an/ScanBuild success Scan Build PASS

Commit Message

Jonas Dreßler Feb. 6, 2024, 10:14 a.m. UTC
The kernel was recently changed to introduce disconnecting of connected
BT devices before the adapter gets rfkilled (see kernel commit
"Bluetooth: Disconnect connected devices before rfkilling adapter").

Add a test to mgmt-tester to ensure that this logic works. It uses
async io to write to the rfkill device and then looks if the HCI
Disconnect command gets sent for the connected device.

Use of async io is necessary because the rfkill write would block our
thread until the disconnect and adapter shutdown is finished. To finish
this disconnect though, the HCI emulator needs to respond to the "HCI
Disconnect" command. Because the emulator runs in the our mgmt-tester
thread too, it would be blocked by the write and couldn't reply,
resulting in a timeout and test failure.

---

v1: https://lore.kernel.org/linux-bluetooth/20240128210547.16141-1-verdre@v0yd.nl/
v2: added -lrt to link to real-time library for aio_* functions

---
 Makefile.tools      |   2 +-
 tools/mgmt-tester.c | 153 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 154 insertions(+), 1 deletion(-)

Comments

bluez.test.bot@gmail.com Feb. 6, 2024, 11:30 a.m. UTC | #1
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=823512

---Test result---

Test Summary:
CheckPatch                    PASS      0.53 seconds
GitLint                       FAIL      0.49 seconds
BuildEll                      PASS      24.02 seconds
BluezMake                     PASS      713.96 seconds
MakeCheck                     PASS      11.79 seconds
MakeDistcheck                 PASS      161.79 seconds
CheckValgrind                 PASS      225.85 seconds
CheckSmatch                   PASS      326.21 seconds
bluezmakeextell               PASS      106.89 seconds
IncrementalBuild              PASS      663.62 seconds
ScanBuild                     PASS      919.83 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,v2] mgmt-tester: Ensure rfkilling HCI disconnects devices

WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
20: B1 Line exceeds max length (82>80): "v1: https://lore.kernel.org/linux-bluetooth/20240128210547.16141-1-verdre@v0yd.nl/"


---
Regards,
Linux Bluetooth
diff mbox series

Patch

diff --git a/Makefile.tools b/Makefile.tools
index 044342e29..51ce103ea 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -124,7 +124,7 @@  tools_mgmt_tester_SOURCES = tools/mgmt-tester.c monitor/bt.h \
 				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-glib.la $(GLIB_LIBS) -lrt
 
 tools_mesh_tester_SOURCES = tools/mesh-tester.c monitor/bt.h \
 				emulator/hciemu.h emulator/hciemu.c \
diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c
index 7dfd1b0c7..60b5a305f 100644
--- a/tools/mgmt-tester.c
+++ b/tools/mgmt-tester.c
@@ -21,6 +21,7 @@ 
 #include <fcntl.h>
 #include <errno.h>
 #include <unistd.h>
+#include <aio.h>
 
 #include <glib.h>
 
@@ -12760,6 +12761,149 @@  static void test_hci_devcd(const void *test_data)
 	tester_wait(test->dump_data->timeout + 1, verify_devcd, NULL);
 }
 
+static const uint8_t rfkill_hci_disconnect_device[] = {
+	0x2a, 0x00, /* handle */
+	0x15, /* reason: Power Off */
+};
+
+static const struct generic_data rfkill_disconnect_devices = {
+	.setup_settings = settings_powered_connectable_bondable,
+	.expect_hci_command = BT_HCI_CMD_DISCONNECT,
+	.expect_hci_param = rfkill_hci_disconnect_device,
+	.expect_hci_len = sizeof(rfkill_hci_disconnect_device),
+};
+
+enum rfkill_type {
+	RFKILL_TYPE_ALL = 0,
+	RFKILL_TYPE_WLAN,
+	RFKILL_TYPE_BLUETOOTH,
+	RFKILL_TYPE_UWB,
+	RFKILL_TYPE_WIMAX,
+	RFKILL_TYPE_WWAN,
+};
+
+enum rfkill_operation {
+	RFKILL_OP_ADD = 0,
+	RFKILL_OP_DEL,
+	RFKILL_OP_CHANGE,
+	RFKILL_OP_CHANGE_ALL,
+};
+
+struct rfkill_event {
+	uint32_t idx;
+	uint8_t type;
+	uint8_t op;
+	uint8_t soft;
+	uint8_t hard;
+};
+#define RFKILL_EVENT_SIZE_V1    8
+
+static void trigger_rfkill(void *user_data)
+{
+	struct aiocb *op = user_data;
+	int fd;
+	int latest_rfkill_idx;
+	struct rfkill_event write_event;
+
+	tester_print("Blocking hci device via rfkill");
+
+	fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+	if (fd < 0) {
+		tester_warn("Failed to open rfkill control device: %s",
+				strerror(errno));
+		tester_test_failed();
+		free(op);
+		return;
+	}
+
+	latest_rfkill_idx = -1;
+	while (1) {
+		struct rfkill_event event = { 0 };
+		ssize_t len;
+
+		len = read(fd, &event, sizeof(event));
+		if (len < RFKILL_EVENT_SIZE_V1)
+			break;
+
+		if (event.type == RFKILL_TYPE_BLUETOOTH)
+			latest_rfkill_idx = event.idx;
+	}
+
+	if (latest_rfkill_idx < 0) {
+		tester_warn("No rfkill device to block found");
+		tester_test_failed();
+		close(fd);
+		free(op);
+		return;
+	}
+
+	write_event.idx = latest_rfkill_idx;
+	write_event.op = RFKILL_OP_CHANGE;
+	write_event.soft = true;
+
+	op->aio_fildes = fd;
+	op->aio_buf = &write_event;
+	op->aio_nbytes = sizeof(write_event);
+
+	if (aio_write(op) != 0) {
+		tester_warn("Failed to write to rfkill control device: %s",
+				strerror(errno));
+		tester_test_failed();
+		close(fd);
+		free(op);
+	}
+}
+
+static void finish_aio_write(void *user_data)
+{
+	struct aiocb *op = user_data;
+	struct test_data *data = tester_get_data();
+	int err;
+	size_t ret;
+
+	err = aio_error(op);
+	ret = aio_return(op);
+	if (err != 0) {
+		tester_warn("aio_error() return error: %s", strerror(err));
+		tester_test_failed();
+	} else if (ret != op->aio_nbytes) {
+		tester_warn("aio_return() returned write failed");
+		tester_test_failed();
+	}
+
+	close(op->aio_fildes);
+	free(op);
+	test_condition_complete(data);
+}
+
+static void test_disconnect_on_rfkill(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost;
+	const uint8_t *central_bdaddr;
+	uint8_t addr_type;
+	struct aiocb *op = new0(struct aiocb, 1);
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	central_bdaddr = hciemu_get_central_bdaddr(data->hciemu);
+	if (!central_bdaddr) {
+		tester_warn("No central bdaddr");
+		tester_test_failed();
+		return;
+	}
+
+	addr_type = data->hciemu_type == HCIEMU_TYPE_BREDRLE
+		? BDADDR_BREDR : BDADDR_LE_PUBLIC;
+
+	bthost_hci_connect(bthost, central_bdaddr, addr_type);
+
+	test_command_generic(test_data);
+
+	tester_wait(1, trigger_rfkill, (void *)op);
+	tester_wait(2, finish_aio_write, (void *)op);
+	test_add_condition(data);
+}
+
 int main(int argc, char *argv[])
 {
 	tester_init(&argc, &argv);
@@ -14925,5 +15069,14 @@  int main(int argc, char *argv[])
 	test_bredrle_full("HCI Devcoredump - Dump Timeout", &dump_timeout, NULL,
 				test_hci_devcd, 3);
 
+	/* Rfkill
+	 * Setup: Connect to device
+	 * Run: Block hci device via rfkill
+	 * Expect: Disconnect HCI command sent to device
+	 */
+	test_bredrle("Rfkill - disconnect devices",
+			&rfkill_disconnect_devices, NULL,
+			test_disconnect_on_rfkill);
+
 	return tester_run();
 }