1 /**
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  */
7 
8 #include <stdint.h>
9 
10 #include <zephyr/kernel.h>
11 
12 #include <zephyr/bluetooth/addr.h>
13 #include <zephyr/bluetooth/conn.h>
14 #include <zephyr/bluetooth/gatt.h>
15 #include <zephyr/bluetooth/uuid.h>
16 #include <zephyr/settings/settings.h>
17 #include <zephyr/bluetooth/bluetooth.h>
18 
19 #include "babblekit/testcase.h"
20 #include "babblekit/flags.h"
21 
22 #include <zephyr/logging/log.h>
23 LOG_MODULE_REGISTER(test_central, LOG_LEVEL_DBG);
24 
25 #include "bs_bt_utils.h"
26 
27 DEFINE_FLAG_STATIC(flag_discovered);
28 DEFINE_FLAG_STATIC(flag_subscribed);
29 DEFINE_FLAG_STATIC(flag_indicated);
30 
31 enum GATT_HANDLES {
32 	SC,
33 	CCC,
34 	NUM_HANDLES,
35 };
36 
37 static uint16_t gatt_handles[NUM_HANDLES] = {0};
38 
39 static struct bt_gatt_subscribe_params subscribe_params;
40 
sc_subscribed(struct bt_conn * conn,uint8_t err,struct bt_gatt_subscribe_params * params)41 static void sc_subscribed(struct bt_conn *conn,
42 			  uint8_t err,
43 			  struct bt_gatt_subscribe_params *params)
44 {
45 	LOG_DBG("subscribed");
46 	SET_FLAG(flag_subscribed);
47 }
48 
sc_indicated(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)49 static uint8_t sc_indicated(struct bt_conn *conn,
50 			    struct bt_gatt_subscribe_params *params,
51 			    const void *data,
52 			    uint16_t length)
53 {
54 	LOG_DBG("indication received");
55 
56 	SET_FLAG(flag_indicated);
57 
58 	return BT_GATT_ITER_CONTINUE;
59 }
60 
subscribe(void)61 static void subscribe(void)
62 {
63 	int err;
64 
65 	subscribe_params.ccc_handle = gatt_handles[CCC];
66 	subscribe_params.value_handle = gatt_handles[SC];
67 	subscribe_params.value = BT_GATT_CCC_INDICATE;
68 	subscribe_params.subscribe = sc_subscribed;
69 	subscribe_params.notify = sc_indicated;
70 
71 	err = bt_gatt_subscribe(get_g_conn(), &subscribe_params);
72 	TEST_ASSERT(!err, "bt_gatt_subscribe failed (%d)", err);
73 
74 	WAIT_FOR_FLAG(flag_subscribed);
75 }
76 
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)77 static uint8_t discover_func(struct bt_conn *conn,
78 			     const struct bt_gatt_attr *attr,
79 			     struct bt_gatt_discover_params *params)
80 {
81 	if (attr == NULL) {
82 		for (size_t i = 0U; i < ARRAY_SIZE(gatt_handles); i++) {
83 			LOG_DBG("handle[%d] = 0x%x", i, gatt_handles[i]);
84 			TEST_ASSERT(gatt_handles[i] != 0, "did not find all handles");
85 		}
86 
87 		(void)memset(params, 0, sizeof(*params));
88 		SET_FLAG(flag_discovered);
89 
90 		return BT_GATT_ITER_STOP;
91 	}
92 
93 	if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) {
94 		const struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;
95 		static const struct bt_uuid_16 ccc_uuid = BT_UUID_INIT_16(BT_UUID_GATT_CCC_VAL);
96 
97 		if (bt_uuid_cmp(chrc->uuid, BT_UUID_GATT_SC) == 0) {
98 			int err;
99 
100 			LOG_DBG("found sc");
101 			gatt_handles[SC] = chrc->value_handle;
102 
103 			params->uuid = &ccc_uuid.uuid;
104 			params->start_handle = attr->handle + 2;
105 			params->type = BT_GATT_DISCOVER_DESCRIPTOR;
106 
107 			err = bt_gatt_discover(conn, params);
108 			TEST_ASSERT(!err, "bt_gatt_discover failed (%d)", err);
109 
110 			return BT_GATT_ITER_STOP;
111 		}
112 
113 	} else if (params->type == BT_GATT_DISCOVER_DESCRIPTOR &&
114 		   bt_uuid_cmp(params->uuid, BT_UUID_GATT_CCC) == 0) {
115 		LOG_DBG("found ccc");
116 		gatt_handles[CCC] = attr->handle;
117 		SET_FLAG(flag_discovered);
118 
119 		return BT_GATT_ITER_STOP;
120 	}
121 
122 	return BT_GATT_ITER_CONTINUE;
123 }
124 
gatt_discover(void)125 static void gatt_discover(void)
126 {
127 	int err;
128 	static struct bt_gatt_discover_params discover_params;
129 
130 	discover_params.uuid = NULL;
131 	discover_params.func = discover_func;
132 	discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
133 	discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
134 	discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
135 
136 	err = bt_gatt_discover(get_g_conn(), &discover_params);
137 	TEST_ASSERT(!err, "bt_gatt_discover failed (%d)", err);
138 
139 	WAIT_FOR_FLAG(flag_discovered);
140 
141 	LOG_DBG("sc handle: %d", gatt_handles[SC]);
142 	LOG_DBG("ccc handle: %d", gatt_handles[CCC]);
143 }
144 
central(void)145 void central(void)
146 {
147 	/*
148 	 * test goal: check that service changed indication is sent on
149 	 * reconnection when the server's GATT database has been updated since
150 	 * last connection
151 	 *
152 	 * the central will connect, bond with the peripheral and then
153 	 * disconnect after doing that, the central will try to connect again,
154 	 * this time it will not elevate the security
155 	 *
156 	 * to pass the test, the central will wait to receive the service
157 	 * changed indication
158 	 */
159 
160 	int err;
161 	struct bt_conn_auth_info_cb bt_conn_auth_info_cb = {
162 		.pairing_failed = pairing_failed,
163 		.pairing_complete = pairing_complete,
164 	};
165 
166 	err = bt_enable(NULL);
167 	TEST_ASSERT(!err, "bt_enable failed (%d)", err);
168 
169 	err = bt_conn_auth_info_cb_register(&bt_conn_auth_info_cb);
170 	TEST_ASSERT(!err, "bt_conn_auth_info_cb_register failed.");
171 
172 	err = settings_load();
173 	TEST_ASSERT(!err, "settings_load failed (%d)", err);
174 
175 	scan_connect_to_first_result();
176 	wait_connected();
177 
178 	set_security(BT_SECURITY_L2);
179 
180 	TAKE_FLAG(flag_pairing_complete);
181 	TAKE_FLAG(flag_bonded);
182 
183 	/* subscribe to the service changed indication */
184 	gatt_discover();
185 	subscribe();
186 
187 	disconnect();
188 	wait_disconnected();
189 	clear_g_conn();
190 
191 	scan_connect_to_first_result();
192 	wait_connected();
193 
194 	/* wait for service change indication */
195 	WAIT_FOR_FLAG(flag_indicated);
196 
197 	TEST_PASS("PASS");
198 }
199