1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 
9 #include <zephyr/bluetooth/bluetooth.h>
10 #include <zephyr/bluetooth/conn.h>
11 #include <zephyr/bluetooth/att.h>
12 #include <zephyr/bluetooth/gatt.h>
13 
14 #include <zephyr/settings/settings.h>
15 
16 #include <zephyr/logging/log.h>
17 
18 #include "testlib/adv.h"
19 #include "testlib/att_read.h"
20 #include "testlib/conn.h"
21 
22 #include "babblekit/flags.h"
23 #include "babblekit/sync.h"
24 #include "babblekit/testcase.h"
25 
26 #include "common.h"
27 
28 LOG_MODULE_REGISTER(client, LOG_LEVEL_DBG);
29 
30 DEFINE_FLAG_STATIC(client_security_changed_flag);
31 DEFINE_FLAG_STATIC(client_is_subscribed_flag);
32 
33 /* Subscription parameters have the same lifetime as a subscription.
34  * That is the backing struct should stay valid until a call to
35  * `bt_gatt_unsubscribe()` is made. Hence the `static`.
36  */
37 static struct bt_gatt_subscribe_params sub_params;
38 
39 /* This is "working memory" used by the `CONFIG_BT_GATT_AUTO_DISCOVER_CCC`
40  * feature. It also has to stay valid until the end of the async call.
41  */
42 static struct bt_gatt_discover_params ccc_disc_params;
43 
44 static struct bt_conn_cb client_conn_cb;
45 
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)46 static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
47 {
48 	char addr_str[BT_ADDR_LE_STR_LEN];
49 
50 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
51 
52 	TEST_ASSERT(err == 0, "Security update failed: %s level %u err %d", addr_str, level, err);
53 
54 	LOG_DBG("Security changed: %s level %u", addr_str, level);
55 	SET_FLAG(client_security_changed_flag);
56 }
57 
find_characteristic(struct bt_conn * conn,const struct bt_uuid * svc,const struct bt_uuid * chrc,uint16_t * chrc_value_handle)58 void find_characteristic(struct bt_conn *conn,
59 			const struct bt_uuid *svc,
60 			const struct bt_uuid *chrc,
61 			uint16_t *chrc_value_handle)
62 {
63 	int err;
64 	uint16_t svc_handle;
65 	uint16_t svc_end_handle;
66 	uint16_t chrc_end_handle;
67 
68 	err = bt_testlib_gatt_discover_primary(&svc_handle, &svc_end_handle, conn, svc,
69 					       BT_ATT_FIRST_ATTRIBUTE_HANDLE,
70 					       BT_ATT_LAST_ATTRIBUTE_HANDLE);
71 	TEST_ASSERT(err == 0, "Failed to discover service: %d", err);
72 
73 	LOG_DBG("svc_handle: %u, svc_end_handle: %u", svc_handle, svc_end_handle);
74 
75 	err = bt_testlib_gatt_discover_characteristic(chrc_value_handle, &chrc_end_handle,
76 						      NULL, conn, chrc, (svc_handle + 1),
77 						      svc_end_handle);
78 	TEST_ASSERT(err == 0, "Failed to get value handle: %d", err);
79 
80 	LOG_DBG("chrc_value_handle: %u, chrc_end_handle: %u", *chrc_value_handle, chrc_end_handle);
81 }
82 
received_notification(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)83 static uint8_t received_notification(struct bt_conn *conn,
84 				     struct bt_gatt_subscribe_params *params,
85 				     const void *data,
86 				     uint16_t length)
87 {
88 	if (length) {
89 		LOG_INF("RX notification");
90 		LOG_HEXDUMP_DBG(data, length, "payload");
91 	}
92 
93 	return BT_GATT_ITER_CONTINUE;
94 }
95 
sub_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_subscribe_params * params)96 static void sub_cb(struct bt_conn *conn,
97 		   uint8_t err,
98 		   struct bt_gatt_subscribe_params *params)
99 {
100 	TEST_ASSERT(!err, "Subscribe failed (err %d)", err);
101 
102 	TEST_ASSERT(params, "params is NULL");
103 	TEST_ASSERT(params->value, "Host shouldn't know we have unsubscribed");
104 
105 	LOG_DBG("Subscribed to handle 0x%04x", params->value_handle);
106 	SET_FLAG(client_is_subscribed_flag);
107 }
108 
109 
110 
subscribe(struct bt_conn * conn,uint16_t handle,bt_gatt_notify_func_t cb)111 static void subscribe(struct bt_conn *conn,
112 		      uint16_t handle,
113 		      bt_gatt_notify_func_t cb)
114 {
115 	int err;
116 
117 	/* Subscribe to notifications */
118 	sub_params.notify = cb;
119 	sub_params.subscribe = sub_cb;
120 	sub_params.value = BT_GATT_CCC_NOTIFY;
121 	sub_params.value_handle = handle;
122 	sub_params.ccc_handle = BT_GATT_AUTO_DISCOVER_CCC_HANDLE;
123 	sub_params.disc_params = &ccc_disc_params;
124 	sub_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
125 
126 	err = bt_gatt_subscribe(conn, &sub_params);
127 	TEST_ASSERT(!err, "Subscribe failed (err %d)", err);
128 
129 	WAIT_FOR_FLAG(client_is_subscribed_flag);
130 }
131 
client_procedure(void)132 void client_procedure(void)
133 {
134 	int err;
135 	struct bt_conn *conn;
136 	uint16_t handle;
137 
138 	TEST_START("client");
139 
140 	err = bt_enable(NULL);
141 	TEST_ASSERT(err == 0, "Cannot enable Bluetooth (err %d)", err);
142 
143 	LOG_DBG("Bluetooth initialized");
144 
145 	err = settings_load();
146 	TEST_ASSERT(err == 0, "Failed to load settings (err %d)", err);
147 
148 	err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
149 	TEST_ASSERT(err == 0, "Failed to unpair (err %d)", err);
150 
151 	client_conn_cb.connected = NULL;
152 	client_conn_cb.disconnected = NULL;
153 	client_conn_cb.security_changed = security_changed;
154 	client_conn_cb.identity_resolved = NULL;
155 
156 	bt_conn_cb_register(&client_conn_cb);
157 	TEST_ASSERT(err == 0, "Failed to set server conn callbacks (err %d)", err);
158 
159 	err = bt_testlib_adv_conn(&conn, BT_ID_DEFAULT, ADVERTISER_NAME);
160 	TEST_ASSERT(err == 0, "Failed to start connectable advertising (err %d)", err);
161 
162 	WAIT_FOR_FLAG(client_security_changed_flag);
163 
164 	find_characteristic(conn, test_service_uuid, test_characteristic_uuid, &handle);
165 
166 	subscribe(conn, handle, received_notification);
167 
168 	TEST_PASS("client");
169 }
170