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