1 /* Copyright (c) 2024 Nordic Semiconductor ASA
2 * SPDX-License-Identifier: Apache-2.0
3 */
4
5 #include <zephyr/kernel.h>
6 #include <zephyr/bluetooth/bluetooth.h>
7 #include <zephyr/bluetooth/conn.h>
8 #include <zephyr/bluetooth/l2cap.h>
9 #include <zephyr/bluetooth/testing.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/net_buf.h>
12 #include <zephyr/sys/__assert.h>
13 #include <zephyr/sys/atomic.h>
14 #include <zephyr/sys/util_macro.h>
15
16 #include <testlib/addr.h>
17 #include <testlib/adv.h>
18 #include <testlib/conn.h>
19 #include <testlib/scan.h>
20
21 #include <babblekit/flags.h>
22 #include <babblekit/testcase.h>
23
24 #include "data.h"
25
26 LOG_MODULE_REGISTER(dut, LOG_LEVEL_INF);
27
28 /** Here we keep track of the reference count in the test
29 * application. This allows us to notice if the stack has freed
30 * references that were ours.
31 */
32 static atomic_t acl_pool_refs_held[BT_BUF_ACL_RX_COUNT];
33
34 BUILD_ASSERT(IS_ENABLED(CONFIG_BT_TESTING));
35 BUILD_ASSERT(IS_ENABLED(CONFIG_BT_HCI_ACL_FLOW_CONTROL));
bt_testing_trace_event_acl_pool_destroy(struct net_buf * destroyed_buf)36 void bt_testing_trace_event_acl_pool_destroy(struct net_buf *destroyed_buf)
37 {
38 int buf_id = net_buf_id(destroyed_buf);
39
40 __ASSERT_NO_MSG(0 <= buf_id && buf_id < ARRAY_SIZE(acl_pool_refs_held));
41 TEST_ASSERT(acl_pool_refs_held[buf_id] == 0,
42 "ACL buf was destroyed while tester still held a reference");
43 }
44
acl_pool_refs_held_add(struct net_buf * buf)45 static void acl_pool_refs_held_add(struct net_buf *buf)
46 {
47 int buf_id = net_buf_id(buf);
48
49 __ASSERT_NO_MSG(0 <= buf_id && buf_id < BT_BUF_ACL_RX_COUNT);
50 atomic_inc(&acl_pool_refs_held[buf_id]);
51 }
52
acl_pool_refs_held_remove(struct net_buf * buf)53 static void acl_pool_refs_held_remove(struct net_buf *buf)
54 {
55 int buf_id = net_buf_id(buf);
56
57 __ASSERT_NO_MSG(0 <= buf_id && buf_id < ARRAY_SIZE(acl_pool_refs_held));
58 atomic_val_t old = atomic_dec(&acl_pool_refs_held[buf_id]);
59
60 __ASSERT(old != 0, "Tester error: releasing a reference that was not held");
61 }
62
63 struct k_fifo ack_todo;
64
dut_chan_recv_cb(struct bt_l2cap_chan * chan,struct net_buf * buf)65 static int dut_chan_recv_cb(struct bt_l2cap_chan *chan, struct net_buf *buf)
66 {
67 /* Move buf. Ownership is ours if we return -EINPROGRESS. */
68 acl_pool_refs_held_add(buf);
69 k_fifo_put(&ack_todo, buf);
70
71 return -EINPROGRESS;
72 }
73
74 static const struct bt_l2cap_chan_ops ops = {
75 .recv = dut_chan_recv_cb,
76 };
77
78 static struct bt_l2cap_le_chan le_chan = {
79 .chan.ops = &ops,
80 };
81
dut_server_accept_cb(struct bt_conn * conn,struct bt_l2cap_server * server,struct bt_l2cap_chan ** chan)82 static int dut_server_accept_cb(struct bt_conn *conn, struct bt_l2cap_server *server,
83 struct bt_l2cap_chan **chan)
84 {
85 *chan = &le_chan.chan;
86 return 0;
87 }
88
89 static struct bt_l2cap_server test_l2cap_server = {
90 .accept = dut_server_accept_cb,
91 .psm = TEST_DATA_L2CAP_PSM,
92 };
93
entrypoint_dut(void)94 void entrypoint_dut(void)
95 {
96 struct net_buf *ack_buf;
97 struct bt_conn *conn = NULL;
98 int err;
99
100 TEST_START("dut");
101
102 k_fifo_init(&ack_todo);
103
104 err = bt_id_create(&TEST_DATA_DUT_ADDR, NULL);
105 __ASSERT_NO_MSG(!err);
106
107 err = bt_enable(NULL);
108 __ASSERT_NO_MSG(!err);
109
110 err = bt_l2cap_server_register(&test_l2cap_server);
111 __ASSERT_NO_MSG(!err);
112
113 err = bt_testlib_adv_conn(&conn, BT_ID_DEFAULT, NULL);
114 __ASSERT_NO_MSG(!err);
115
116 ack_buf = k_fifo_get(&ack_todo, K_FOREVER);
117 __ASSERT_NO_MSG(ack_buf);
118
119 acl_pool_refs_held_remove(ack_buf);
120 err = bt_l2cap_chan_recv_complete(&le_chan.chan, ack_buf);
121 TEST_ASSERT(!err);
122
123 TEST_PASS_AND_EXIT("dut");
124 }
125