1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/bluetooth/bluetooth.h>
9 #include <zephyr/bluetooth/conn.h>
10 #include <zephyr/bluetooth/att.h>
11 #include <zephyr/bluetooth/gatt.h>
12 #include <zephyr/logging/log.h>
13 
14 #include "testlib/adv.h"
15 #include "testlib/att_read.h"
16 #include "testlib/att_write.h"
17 #include "testlib/conn.h"
18 #include "testlib/log_utils.h"
19 
20 #include "babblekit/flags.h"
21 #include "babblekit/testcase.h"
22 
23 /* local includes */
24 #include "data.h"
25 
26 LOG_MODULE_REGISTER(peer, LOG_LEVEL_DBG);
27 
28 static DEFINE_FLAG(is_subscribed);
29 static DEFINE_FLAG(got_notification_1);
30 
31 extern unsigned long runtime_log_level;
32 
find_characteristic(struct bt_conn * conn,const struct bt_uuid * svc,const struct bt_uuid * chrc,uint16_t * chrc_value_handle)33 int find_characteristic(struct bt_conn *conn, const struct bt_uuid *svc, const struct bt_uuid *chrc,
34 			uint16_t *chrc_value_handle)
35 {
36 	uint16_t svc_handle;
37 	uint16_t svc_end_handle;
38 	uint16_t chrc_end_handle;
39 	int err;
40 
41 	LOG_DBG("");
42 
43 	err = bt_testlib_gatt_discover_primary(&svc_handle, &svc_end_handle, conn, svc,
44 					       BT_ATT_FIRST_ATTRIBUTE_HANDLE,
45 					       BT_ATT_LAST_ATTRIBUTE_HANDLE);
46 	if (err != 0) {
47 		LOG_ERR("Failed to discover service: %d", err);
48 
49 		return err;
50 	}
51 
52 	LOG_DBG("svc_handle: %u, svc_end_handle: %u", svc_handle, svc_end_handle);
53 
54 	err = bt_testlib_gatt_discover_characteristic(chrc_value_handle, &chrc_end_handle, NULL,
55 						      conn, chrc, (svc_handle + 1), svc_end_handle);
56 	if (err != 0) {
57 		LOG_ERR("Failed to get value handle: %d", err);
58 
59 		return err;
60 	}
61 
62 	LOG_DBG("chrc_value_handle: %u, chrc_end_handle: %u", *chrc_value_handle, chrc_end_handle);
63 
64 	return err;
65 }
66 
received_notification(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)67 static uint8_t received_notification(struct bt_conn *conn, struct bt_gatt_subscribe_params *params,
68 				     const void *data, uint16_t length)
69 {
70 	if (length) {
71 		LOG_INF("RX notification");
72 		LOG_HEXDUMP_DBG(data, length, "payload");
73 		SET_FLAG(got_notification_1);
74 
75 		TEST_ASSERT(length == GATT_PAYLOAD_SIZE, "Unexpected length: %d", length);
76 	}
77 
78 	return BT_GATT_ITER_CONTINUE;
79 }
80 
sub_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_subscribe_params * params)81 static void sub_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_subscribe_params *params)
82 {
83 	TEST_ASSERT(!err, "Subscribe failed (err %d)", err);
84 
85 	TEST_ASSERT(params, "params is NULL");
86 	TEST_ASSERT(params->value, "Host shouldn't know we have unsubscribed");
87 
88 	LOG_DBG("Subscribed to handle 0x%04x", params->value_handle);
89 	SET_FLAG(is_subscribed);
90 }
91 
92 /* Subscription parameters have the same lifetime as a subscription.
93  * That is the backing struct should stay valid until a call to
94  * `bt_gatt_unsubscribe()` is made. Hence the `static`.
95  */
96 static struct bt_gatt_subscribe_params sub_params;
97 
98 /* This is "working memory" used by the `CONFIG_BT_GATT_AUTO_DISCOVER_CCC`
99  * feature. It also has to stay valid until the end of the async call.
100  */
101 static struct bt_gatt_discover_params ccc_disc_params;
102 
subscribe(struct bt_conn * conn,uint16_t handle,bt_gatt_notify_func_t cb)103 static void subscribe(struct bt_conn *conn, uint16_t handle, bt_gatt_notify_func_t cb)
104 {
105 	int err;
106 
107 	/* Subscribe to notifications */
108 	sub_params.notify = cb;
109 	sub_params.subscribe = sub_cb;
110 	sub_params.value = BT_GATT_CCC_INDICATE;
111 	sub_params.value_handle = handle;
112 	sub_params.ccc_handle = BT_GATT_AUTO_DISCOVER_CCC_HANDLE;
113 	sub_params.disc_params = &ccc_disc_params;
114 	sub_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
115 
116 	err = bt_gatt_subscribe(conn, &sub_params);
117 	TEST_ASSERT(!err, "Subscribe failed (err %d)", err);
118 
119 	WAIT_FOR_FLAG(is_subscribed);
120 }
121 
122 static uint16_t g_handle;
123 
test_iteration(void)124 static void test_iteration(void)
125 {
126 	struct bt_conn *conn;
127 	int err;
128 
129 	err = bt_testlib_adv_conn(&conn, BT_ID_DEFAULT, bt_get_name());
130 	TEST_ASSERT(!err, "Failed to start connectable advertising (err %d)", err);
131 
132 	if (g_handle) {
133 		LOG_DBG("Re-use cached characteristic");
134 	} else {
135 		LOG_DBG("Discover test characteristic");
136 		err = find_characteristic(conn, test_service_uuid, test_characteristic_uuid,
137 					  &g_handle);
138 		TEST_ASSERT(!err, "Failed to find characteristic: %d", err);
139 	}
140 
141 	LOG_DBG("Subscribe to test characteristic: handle 0x%04x", g_handle);
142 	subscribe(conn, g_handle, received_notification);
143 
144 	WAIT_FOR_FLAG(got_notification_1);
145 
146 	bt_testlib_wait_disconnected(conn);
147 	bt_testlib_conn_unref(&conn);
148 }
149 
150 /* Read the comments on `entrypoint_dut()` first. */
entrypoint_peer(void)151 void entrypoint_peer(void)
152 {
153 	int err;
154 
155 	/* Mark test as in progress. */
156 	TEST_START("peer");
157 
158 	/* Set the log level given by the `log_level` CLI argument */
159 	bt_testlib_log_level_set("peer", runtime_log_level);
160 
161 	/* Initialize Bluetooth */
162 	err = bt_enable(NULL);
163 	TEST_ASSERT(err == 0, "Can't enable Bluetooth (err %d)", err);
164 
165 	LOG_DBG("Bluetooth initialized");
166 
167 	for (int i = 0; i < TEST_ITERATIONS; i++) {
168 		LOG_INF("## Iteration %d", i);
169 		test_iteration();
170 	}
171 
172 	TEST_PASS_AND_EXIT("peer");
173 }
174