-
Notifications
You must be signed in to change notification settings - Fork 8.4k
Bluetooth: Host: Fix L2CAP EINPROGRESS refs #76835
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
21d1bc7
185c5f2
82231e5
31fc3a4
e8b9c00
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| /* Copyright (c) 2024 Nordic Semiconductor ASA | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| /** @brief Internal testing interfaces for Bluetooth | ||
| * @file | ||
| * @internal | ||
| * | ||
| * The interfaces in this file are internal and not stable. | ||
| */ | ||
|
|
||
| #ifndef ZEPHYR_INCLUDE_BLUETOOTH_TESTING_H_ | ||
| #define ZEPHYR_INCLUDE_BLUETOOTH_TESTING_H_ | ||
|
|
||
| #include <zephyr/net_buf.h> | ||
|
|
||
| /** @brief Hook for `acl_in_pool.destroy` | ||
| * | ||
| * Weak-function interface. The user can simply define this | ||
| * function, and it will automatically become the event | ||
| * listener. | ||
| * | ||
| * @kconfig_dep{CONFIG_BT_TESTING} | ||
| */ | ||
| void bt_testing_trace_event_acl_pool_destroy(struct net_buf *buf); | ||
|
|
||
| #endif /* ZEPHYR_INCLUDE_BLUETOOTH_TESTING_H_ */ |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |||||
| #include <zephyr/net_buf.h> | ||||||
| #include <zephyr/sys/atomic.h> | ||||||
| #include <zephyr/sys/check.h> | ||||||
| #include <zephyr/sys/util_macro.h> | ||||||
| #include <zephyr/sys/util.h> | ||||||
| #include <zephyr/sys/slist.h> | ||||||
| #include <zephyr/sys/byteorder.h> | ||||||
|
|
@@ -28,6 +29,7 @@ | |||||
| #include <zephyr/bluetooth/l2cap.h> | ||||||
| #include <zephyr/bluetooth/hci.h> | ||||||
| #include <zephyr/bluetooth/hci_vs.h> | ||||||
| #include <zephyr/bluetooth/testing.h> | ||||||
| #if DT_HAS_CHOSEN(zephyr_bt_hci) | ||||||
| #include <zephyr/drivers/bluetooth.h> | ||||||
| #else | ||||||
|
|
@@ -263,13 +265,23 @@ void bt_send_one_host_num_completed_packets(uint16_t handle) | |||||
| BT_ASSERT_MSG(err == 0, "Unable to send Host NCP (err %d)", err); | ||||||
| } | ||||||
|
|
||||||
| #if defined(CONFIG_BT_TESTING) | ||||||
| __weak void bt_testing_trace_event_acl_pool_destroy(struct net_buf *buf) | ||||||
|
||||||
| /* Defined in hci_core.c */ | |
| extern k_tid_t bt_testing_tx_tid_get(void); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to expose test functions in a public header file?
Honestly not sure what the best practices is for testing in Zephyr/C when it comes to things like this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like that concern should be solvable with proper documentation. These APIs either way require explicitly enabling CONFIG_BT_TESTING before they can be used. We could additionally output a build warning (if we don't already) that the configuration should not be used in production.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alwa-nordic this conversation is still unresolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I welcome the "public" test header. Should I add it?
Should its doxygen say @file @internal? That will prevent anything from that file from rendering in the public documentation, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a fan of public testing headers. If this is for internal testing, then I think such headers are better placed in a non-public place. Placing a testing.h makes more sense if it's a testing API that is designed to be used for users (maybe that is the intention?).
We actually recently moved the Mesh testing.h header file from the public include directory to internal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it help to create a include/zephyr/bluetoooth/internal directory for these, if the concern is that someone might think they're intended for general app use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main issue with putting them e.g. somewhere under subsys/bluetooth/ is the then you need to play manual tricks with include lookup paths etc. An alternative idea that comes to mind is to have some CONFIG_BT_PRIVATE_INCLUDES option which adds subsys/bluetooth/ to the lookup path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Semantically it's odd to have an internal directory in the public directory :D
FYI I'm not blocking these changes as I don't have a solution/alternative, but just wanted to voice my concerns about putting functions that aren't meant to be used by applications in the same location as our APIs. We don't want it to be perceived as a test framework/interface of any sort, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main issue with putting them e.g. somewhere under
subsys/bluetooth/is the then you need to play manual tricks with include lookup paths etc. An alternative idea that comes to mind is to have someCONFIG_BT_PRIVATE_INCLUDESoption which addssubsys/bluetooth/to the lookup path.
True to some extent. subsys/bluetooth is already in the include path many places (or can be added as ${ZEPHYR_BASE}/subsys/bluetooth/), so if we have a subsys/bluetooth/host/testing.h file it's a matter of #include <host/testing.h>
We already include header files from the subsys/bluetooth/host directory in both unit and BSIM testing.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| /* Copyright (c) 2024 Nordic Semiconductor ASA | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #ifndef ZEPHYR_TESTS_BLUETOOTH_COMMON_TESTLIB_INCLUDE_TESTLIB_ADDR_H_ | ||
| #define ZEPHYR_TESTS_BLUETOOTH_COMMON_TESTLIB_INCLUDE_TESTLIB_ADDR_H_ | ||
|
|
||
| #include <stdint.h> | ||
|
|
||
| #include <zephyr/bluetooth/addr.h> | ||
| #include <zephyr/bluetooth/byteorder.h> | ||
|
|
||
| /** Bluetooth LE static random address */ | ||
| #define BT_TESTLIB_ADDR_LE_RANDOM_C0_00_00_00_00_(last) \ | ||
| ((bt_addr_le_t){ \ | ||
| .type = BT_ADDR_LE_RANDOM, \ | ||
| .a = {{last, 0x00, 0x00, 0x00, 0x00, 0xc0}}, \ | ||
| }) | ||
|
|
||
| #endif /* ZEPHYR_TESTS_BLUETOOTH_COMMON_TESTLIB_INCLUDE_TESTLIB_ADDR_H_ */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| cmake_minimum_required(VERSION 3.20.0) | ||
|
|
||
| find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) | ||
|
|
||
| project(test_l2cap_einprogress) | ||
|
|
||
| add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/common/testlib testlib) | ||
| target_link_libraries(app PRIVATE testlib) | ||
|
|
||
| add_subdirectory(${ZEPHYR_BASE}/tests/bsim/babblekit babblekit) | ||
| target_link_libraries(app PRIVATE babblekit) | ||
|
|
||
| zephyr_include_directories( | ||
| ${BSIM_COMPONENTS_PATH}/libUtilv1/src/ | ||
| ${BSIM_COMPONENTS_PATH}/libPhyComv1/src/ | ||
| ) | ||
|
|
||
| target_sources(app PRIVATE | ||
| src/main.c | ||
| src/dut.c | ||
| src/tester.c | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| #!/usr/bin/env bash | ||
| # Copyright 2023 Nordic Semiconductor ASA | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| set -eu | ||
| : "${ZEPHYR_BASE:?ZEPHYR_BASE must be defined}" | ||
|
|
||
| INCR_BUILD=1 | ||
Thalley marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| source ${ZEPHYR_BASE}/tests/bsim/compile.source | ||
|
|
||
| app="$(guess_test_relpath)" compile | ||
|
|
||
| wait_for_background_jobs | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| CONFIG_LOG=y | ||
| CONFIG_ASSERT=y | ||
| CONFIG_THREAD_NAME=y | ||
| CONFIG_LOG_THREAD_ID_PREFIX=y | ||
| CONFIG_ARCH_POSIX_TRAP_ON_FATAL=y | ||
| CONFIG_BT_TESTING=y | ||
|
|
||
| CONFIG_BT_HCI_ACL_FLOW_CONTROL=y | ||
|
|
||
| CONFIG_BT=y | ||
| CONFIG_BT_CENTRAL=y | ||
| CONFIG_BT_PERIPHERAL=y | ||
|
|
||
| # Dependency of testlib/adv and testlib/scan. | ||
| CONFIG_BT_EXT_ADV=y | ||
|
|
||
| # Dynamic channel depends on SMP | ||
| CONFIG_BT_SMP=y | ||
| CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y | ||
|
|
||
| # Disable auto-initiated procedures so they don't | ||
| # mess with the test's execution. | ||
| CONFIG_BT_AUTO_PHY_UPDATE=n | ||
| CONFIG_BT_AUTO_DATA_LEN_UPDATE=n | ||
| CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=n |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| /* Copyright (c) 2024 Nordic Semiconductor ASA | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #ifndef ZEPHYR_TESTS_BSIM_BLUETOOTH_HOST_L2CAP_EINPROGRESS_SRC_DATA_H_ | ||
| #define ZEPHYR_TESTS_BSIM_BLUETOOTH_HOST_L2CAP_EINPROGRESS_SRC_DATA_H_ | ||
|
|
||
| #define TEST_DATA_L2CAP_PSM 0x0080 | ||
| #define TEST_DATA_DUT_ADDR BT_TESTLIB_ADDR_LE_RANDOM_C0_00_00_00_00_(0x01) | ||
|
|
||
| #endif /* ZEPHYR_TESTS_BSIM_BLUETOOTH_HOST_L2CAP_EINPROGRESS_SRC_DATA_H_ */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| /* Copyright (c) 2024 Nordic Semiconductor ASA | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #include <zephyr/kernel.h> | ||
| #include <zephyr/bluetooth/bluetooth.h> | ||
| #include <zephyr/bluetooth/conn.h> | ||
| #include <zephyr/bluetooth/l2cap.h> | ||
| #include <zephyr/bluetooth/testing.h> | ||
| #include <zephyr/logging/log.h> | ||
| #include <zephyr/net_buf.h> | ||
| #include <zephyr/sys/__assert.h> | ||
| #include <zephyr/sys/atomic.h> | ||
| #include <zephyr/sys/util_macro.h> | ||
|
|
||
| #include <testlib/addr.h> | ||
| #include <testlib/adv.h> | ||
| #include <testlib/conn.h> | ||
| #include <testlib/scan.h> | ||
|
|
||
| #include <babblekit/flags.h> | ||
| #include <babblekit/testcase.h> | ||
|
|
||
| #include "data.h" | ||
|
|
||
| LOG_MODULE_REGISTER(dut, LOG_LEVEL_INF); | ||
|
|
||
| /** Here we keep track of the reference count in the test | ||
| * application. This allows us to notice if the stack has freed | ||
| * references that were ours. | ||
| */ | ||
| static atomic_t acl_pool_refs_held[CONFIG_BT_BUF_ACL_RX_COUNT]; | ||
|
|
||
| BUILD_ASSERT(IS_ENABLED(CONFIG_BT_TESTING)); | ||
| BUILD_ASSERT(IS_ENABLED(CONFIG_BT_HCI_ACL_FLOW_CONTROL)); | ||
| void bt_testing_trace_event_acl_pool_destroy(struct net_buf *destroyed_buf) | ||
| { | ||
| int buf_id = net_buf_id(destroyed_buf); | ||
|
|
||
| __ASSERT_NO_MSG(0 <= buf_id && buf_id < ARRAY_SIZE(acl_pool_refs_held)); | ||
| TEST_ASSERT(acl_pool_refs_held[buf_id] == 0, | ||
| "ACL buf was destroyed while tester still held a reference"); | ||
| } | ||
|
|
||
| static void acl_pool_refs_held_add(struct net_buf *buf) | ||
| { | ||
| int buf_id = net_buf_id(buf); | ||
|
|
||
| __ASSERT_NO_MSG(0 <= buf_id && buf_id < CONFIG_BT_BUF_ACL_RX_COUNT); | ||
| atomic_inc(&acl_pool_refs_held[buf_id]); | ||
| } | ||
|
|
||
| static void acl_pool_refs_held_remove(struct net_buf *buf) | ||
| { | ||
| int buf_id = net_buf_id(buf); | ||
|
|
||
| __ASSERT_NO_MSG(0 <= buf_id && buf_id < ARRAY_SIZE(acl_pool_refs_held)); | ||
| atomic_val_t old = atomic_dec(&acl_pool_refs_held[buf_id]); | ||
|
|
||
| __ASSERT(old != 0, "Tester error: releasing a reference that was not held"); | ||
| } | ||
|
|
||
| struct k_fifo ack_todo; | ||
|
|
||
| static int dut_chan_recv_cb(struct bt_l2cap_chan *chan, struct net_buf *buf) | ||
| { | ||
| /* Move buf. Ownership is ours if we return -EINPROGRESS. */ | ||
| acl_pool_refs_held_add(buf); | ||
| k_fifo_put(&ack_todo, buf); | ||
|
|
||
| return -EINPROGRESS; | ||
| } | ||
|
|
||
| static const struct bt_l2cap_chan_ops ops = { | ||
| .recv = dut_chan_recv_cb, | ||
| }; | ||
|
|
||
| static struct bt_l2cap_le_chan le_chan = { | ||
| .chan.ops = &ops, | ||
| }; | ||
|
|
||
| static int dut_server_accept_cb(struct bt_conn *conn, struct bt_l2cap_server *server, | ||
| struct bt_l2cap_chan **chan) | ||
| { | ||
| *chan = &le_chan.chan; | ||
| return 0; | ||
| } | ||
|
|
||
| static struct bt_l2cap_server test_l2cap_server = { | ||
| .accept = dut_server_accept_cb, | ||
| .psm = TEST_DATA_L2CAP_PSM, | ||
| }; | ||
|
|
||
| void entrypoint_dut(void) | ||
| { | ||
| struct net_buf *ack_buf; | ||
| struct bt_conn *conn = NULL; | ||
| int err; | ||
|
|
||
| TEST_START("dut"); | ||
|
|
||
| k_fifo_init(&ack_todo); | ||
|
|
||
| err = bt_id_create(&TEST_DATA_DUT_ADDR, NULL); | ||
| __ASSERT_NO_MSG(!err); | ||
|
|
||
| err = bt_enable(NULL); | ||
| __ASSERT_NO_MSG(!err); | ||
|
|
||
| err = bt_l2cap_server_register(&test_l2cap_server); | ||
| __ASSERT_NO_MSG(!err); | ||
|
|
||
| err = bt_testlib_adv_conn(&conn, BT_ID_DEFAULT, NULL); | ||
| __ASSERT_NO_MSG(!err); | ||
|
|
||
| ack_buf = k_fifo_get(&ack_todo, K_FOREVER); | ||
| __ASSERT_NO_MSG(ack_buf); | ||
|
|
||
| acl_pool_refs_held_remove(ack_buf); | ||
| err = bt_l2cap_chan_recv_complete(&le_chan.chan, ack_buf); | ||
| TEST_ASSERT(!err); | ||
|
|
||
| TEST_PASS_AND_EXIT("dut"); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.