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