1 /* main.c - Application main entry point */
2 
3 /*
4  * Copyright (c) 2015-2016 Intel Corporation
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include <zephyr/types.h>
10 #include <stddef.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <zephyr/sys/printk.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/kernel.h>
16 
17 #include <zephyr/settings/settings.h>
18 
19 #include <zephyr/bluetooth/bluetooth.h>
20 #include <zephyr/bluetooth/hci.h>
21 #include <zephyr/bluetooth/conn.h>
22 #include <zephyr/bluetooth/uuid.h>
23 #include <zephyr/bluetooth/gatt.h>
24 #include <zephyr/bluetooth/services/bas.h>
25 #include <zephyr/bluetooth/services/cts.h>
26 #include <zephyr/bluetooth/services/hrs.h>
27 #include <zephyr/bluetooth/services/ias.h>
28 
29 /* Custom Service Variables */
30 #define BT_UUID_CUSTOM_SERVICE_VAL \
31 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0)
32 
33 static const struct bt_uuid_128 vnd_uuid = BT_UUID_INIT_128(
34 	BT_UUID_CUSTOM_SERVICE_VAL);
35 
36 static const struct bt_uuid_128 vnd_enc_uuid = BT_UUID_INIT_128(
37 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef1));
38 
39 static const struct bt_uuid_128 vnd_auth_uuid = BT_UUID_INIT_128(
40 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef2));
41 
42 #define VND_MAX_LEN 20
43 #define BT_HR_HEARTRATE_DEFAULT_MIN 90U
44 #define BT_HR_HEARTRATE_DEFAULT_MAX 160U
45 
46 static uint8_t vnd_value[VND_MAX_LEN + 1] = { 'V', 'e', 'n', 'd', 'o', 'r'};
47 static uint8_t vnd_auth_value[VND_MAX_LEN + 1] = { 'V', 'e', 'n', 'd', 'o', 'r'};
48 static uint8_t vnd_wwr_value[VND_MAX_LEN + 1] = { 'V', 'e', 'n', 'd', 'o', 'r' };
49 
read_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)50 static ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
51 			void *buf, uint16_t len, uint16_t offset)
52 {
53 	const char *value = attr->user_data;
54 
55 	return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
56 				 strlen(value));
57 }
58 
write_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)59 static ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
60 			 const void *buf, uint16_t len, uint16_t offset,
61 			 uint8_t flags)
62 {
63 	uint8_t *value = attr->user_data;
64 
65 	if (offset + len > VND_MAX_LEN) {
66 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
67 	}
68 
69 	memcpy(value + offset, buf, len);
70 	value[offset + len] = 0;
71 
72 	return len;
73 }
74 
75 static uint8_t simulate_vnd;
76 static uint8_t indicating;
77 static struct bt_gatt_indicate_params ind_params;
78 
vnd_ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)79 static void vnd_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
80 {
81 	simulate_vnd = (value == BT_GATT_CCC_INDICATE) ? 1 : 0;
82 }
83 
indicate_cb(struct bt_conn * conn,struct bt_gatt_indicate_params * params,uint8_t err)84 static void indicate_cb(struct bt_conn *conn,
85 			struct bt_gatt_indicate_params *params, uint8_t err)
86 {
87 	printk("Indication %s\n", err != 0U ? "fail" : "success");
88 }
89 
indicate_destroy(struct bt_gatt_indicate_params * params)90 static void indicate_destroy(struct bt_gatt_indicate_params *params)
91 {
92 	printk("Indication complete\n");
93 	indicating = 0U;
94 }
95 
96 #define VND_LONG_MAX_LEN 74
97 static uint8_t vnd_long_value[VND_LONG_MAX_LEN + 1] = {
98 		  'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '1',
99 		  'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '2',
100 		  'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '3',
101 		  'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '4',
102 		  'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '5',
103 		  'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '6',
104 		  '.', ' ' };
105 
write_long_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)106 static ssize_t write_long_vnd(struct bt_conn *conn,
107 			      const struct bt_gatt_attr *attr, const void *buf,
108 			      uint16_t len, uint16_t offset, uint8_t flags)
109 {
110 	uint8_t *value = attr->user_data;
111 
112 	if (flags & BT_GATT_WRITE_FLAG_PREPARE) {
113 		return 0;
114 	}
115 
116 	if (offset + len > VND_LONG_MAX_LEN) {
117 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
118 	}
119 
120 	memcpy(value + offset, buf, len);
121 	value[offset + len] = 0;
122 
123 	return len;
124 }
125 
126 static const struct bt_uuid_128 vnd_long_uuid = BT_UUID_INIT_128(
127 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef3));
128 
129 static struct bt_gatt_cep vnd_long_cep = {
130 	.properties = BT_GATT_CEP_RELIABLE_WRITE,
131 };
132 
133 static const struct bt_uuid_128 vnd_write_cmd_uuid = BT_UUID_INIT_128(
134 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef4));
135 
write_without_rsp_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)136 static ssize_t write_without_rsp_vnd(struct bt_conn *conn,
137 				     const struct bt_gatt_attr *attr,
138 				     const void *buf, uint16_t len, uint16_t offset,
139 				     uint8_t flags)
140 {
141 	uint8_t *value = attr->user_data;
142 
143 	if (!(flags & BT_GATT_WRITE_FLAG_CMD)) {
144 		/* Write Request received. Reject it since this Characteristic
145 		 * only accepts Write Without Response.
146 		 */
147 		return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
148 	}
149 
150 	if (offset + len > VND_MAX_LEN) {
151 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
152 	}
153 
154 	memcpy(value + offset, buf, len);
155 	value[offset + len] = 0;
156 
157 	return len;
158 }
159 
160 /* Vendor Primary Service Declaration */
161 BT_GATT_SERVICE_DEFINE(vnd_svc,
162 	BT_GATT_PRIMARY_SERVICE(&vnd_uuid),
163 	BT_GATT_CHARACTERISTIC(&vnd_enc_uuid.uuid,
164 			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE |
165 			       BT_GATT_CHRC_INDICATE,
166 			       BT_GATT_PERM_READ_ENCRYPT |
167 			       BT_GATT_PERM_WRITE_ENCRYPT,
168 			       read_vnd, write_vnd, vnd_value),
169 	BT_GATT_CCC(vnd_ccc_cfg_changed,
170 		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_ENCRYPT),
171 	BT_GATT_CHARACTERISTIC(&vnd_auth_uuid.uuid,
172 			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
173 			       BT_GATT_PERM_READ_AUTHEN |
174 			       BT_GATT_PERM_WRITE_AUTHEN,
175 			       read_vnd, write_vnd, vnd_auth_value),
176 	BT_GATT_CHARACTERISTIC(&vnd_long_uuid.uuid, BT_GATT_CHRC_READ |
177 			       BT_GATT_CHRC_WRITE | BT_GATT_CHRC_EXT_PROP,
178 			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE |
179 			       BT_GATT_PERM_PREPARE_WRITE,
180 			       read_vnd, write_long_vnd, &vnd_long_value),
181 	BT_GATT_CEP(&vnd_long_cep),
182 	BT_GATT_CHARACTERISTIC(&vnd_write_cmd_uuid.uuid,
183 			       BT_GATT_CHRC_WRITE_WITHOUT_RESP,
184 			       BT_GATT_PERM_WRITE, NULL,
185 			       write_without_rsp_vnd, &vnd_wwr_value),
186 );
187 
188 static const struct bt_data ad[] = {
189 	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
190 	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
191 		      BT_UUID_16_ENCODE(BT_UUID_HRS_VAL),
192 		      BT_UUID_16_ENCODE(BT_UUID_BAS_VAL),
193 		      BT_UUID_16_ENCODE(BT_UUID_CTS_VAL)),
194 	BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_CUSTOM_SERVICE_VAL),
195 };
196 
197 static const struct bt_data sd[] = {
198 	BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
199 };
200 
mtu_updated(struct bt_conn * conn,uint16_t tx,uint16_t rx)201 void mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
202 {
203 	printk("Updated MTU: TX: %d RX: %d bytes\n", tx, rx);
204 }
205 
206 static struct bt_gatt_cb gatt_callbacks = {
207 	.att_mtu_updated = mtu_updated
208 };
209 
connected(struct bt_conn * conn,uint8_t err)210 static void connected(struct bt_conn *conn, uint8_t err)
211 {
212 	if (err) {
213 		printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err));
214 	} else {
215 		printk("Connected\n");
216 	}
217 }
218 
disconnected(struct bt_conn * conn,uint8_t reason)219 static void disconnected(struct bt_conn *conn, uint8_t reason)
220 {
221 	printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
222 }
223 
alert_stop(void)224 static void alert_stop(void)
225 {
226 	printk("Alert stopped\n");
227 }
228 
alert_start(void)229 static void alert_start(void)
230 {
231 	printk("Mild alert started\n");
232 }
233 
alert_high_start(void)234 static void alert_high_start(void)
235 {
236 	printk("High alert started\n");
237 }
238 
239 BT_CONN_CB_DEFINE(conn_callbacks) = {
240 	.connected = connected,
241 	.disconnected = disconnected,
242 };
243 
244 BT_IAS_CB_DEFINE(ias_callbacks) = {
245 	.no_alert = alert_stop,
246 	.mild_alert = alert_start,
247 	.high_alert = alert_high_start,
248 };
249 
bt_ready(void)250 static void bt_ready(void)
251 {
252 	int err;
253 
254 	printk("Bluetooth initialized\n");
255 
256 	if (IS_ENABLED(CONFIG_SETTINGS)) {
257 		settings_load();
258 	}
259 
260 	err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
261 	if (err) {
262 		printk("Advertising failed to start (err %d)\n", err);
263 		return;
264 	}
265 
266 	printk("Advertising successfully started\n");
267 }
268 
auth_passkey_display(struct bt_conn * conn,unsigned int passkey)269 static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
270 {
271 	char addr[BT_ADDR_LE_STR_LEN];
272 
273 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
274 
275 	printk("Passkey for %s: %06u\n", addr, passkey);
276 }
277 
auth_cancel(struct bt_conn * conn)278 static void auth_cancel(struct bt_conn *conn)
279 {
280 	char addr[BT_ADDR_LE_STR_LEN];
281 
282 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
283 
284 	printk("Pairing cancelled: %s\n", addr);
285 }
286 
287 static struct bt_conn_auth_cb auth_cb_display = {
288 	.passkey_display = auth_passkey_display,
289 	.passkey_entry = NULL,
290 	.cancel = auth_cancel,
291 };
292 
bas_notify(void)293 static void bas_notify(void)
294 {
295 	uint8_t battery_level = bt_bas_get_battery_level();
296 
297 	battery_level--;
298 
299 	if (!battery_level) {
300 		battery_level = 100U;
301 	}
302 
303 	bt_bas_set_battery_level(battery_level);
304 }
305 
306 static uint8_t bt_heartrate = BT_HR_HEARTRATE_DEFAULT_MIN;
307 
hrs_notify(void)308 static void hrs_notify(void)
309 {
310 	/* Heartrate measurements simulation */
311 	bt_heartrate++;
312 	if (bt_heartrate == BT_HR_HEARTRATE_DEFAULT_MAX) {
313 		bt_heartrate = BT_HR_HEARTRATE_DEFAULT_MIN;
314 	}
315 
316 	bt_hrs_notify(bt_heartrate);
317 }
318 
319 /**
320  * variable to hold reference milliseconds to epoch when device booted
321  * this is only for demo purpose, for more precise synchronization please
322  * review clock_settime API implementation.
323  */
324 static int64_t unix_ms_ref;
325 static bool cts_notification_enabled;
326 
bt_cts_notification_changed(bool enabled)327 void bt_cts_notification_changed(bool enabled)
328 {
329 	cts_notification_enabled = enabled;
330 }
331 
bt_cts_cts_time_write(struct bt_cts_time_format * cts_time)332 int bt_cts_cts_time_write(struct bt_cts_time_format *cts_time)
333 {
334 	int err;
335 	int64_t unix_ms;
336 
337 	if (IS_ENABLED(CONFIG_BT_CTS_HELPER_API)) {
338 		err = bt_cts_time_to_unix_ms(cts_time, &unix_ms);
339 		if (err) {
340 			return err;
341 		}
342 	} else {
343 		return -ENOTSUP;
344 	}
345 
346 	/* recalculate reference value */
347 	unix_ms_ref = unix_ms - k_uptime_get();
348 	return 0;
349 }
350 
bt_cts_fill_current_cts_time(struct bt_cts_time_format * cts_time)351 int bt_cts_fill_current_cts_time(struct bt_cts_time_format *cts_time)
352 {
353 	int64_t unix_ms = unix_ms_ref + k_uptime_get();
354 
355 	if (IS_ENABLED(CONFIG_BT_CTS_HELPER_API)) {
356 		return bt_cts_time_from_unix_ms(cts_time, unix_ms);
357 	} else {
358 		return -ENOTSUP;
359 	}
360 }
361 
362 const struct bt_cts_cb cts_cb = {
363 	.notification_changed = bt_cts_notification_changed,
364 	.cts_time_write = bt_cts_cts_time_write,
365 	.fill_current_cts_time = bt_cts_fill_current_cts_time,
366 };
367 
bt_hrs_ctrl_point_write(uint8_t request)368 static int bt_hrs_ctrl_point_write(uint8_t request)
369 {
370 	printk("HRS Control point request: %d\n", request);
371 	if (request != BT_HRS_CONTROL_POINT_RESET_ENERGY_EXPANDED_REQ) {
372 		return -ENOTSUP;
373 	}
374 
375 	bt_heartrate = BT_HR_HEARTRATE_DEFAULT_MIN;
376 	return 0;
377 }
378 
379 static struct bt_hrs_cb hrs_cb = {
380 	.ctrl_point_write = bt_hrs_ctrl_point_write,
381 };
382 
main(void)383 int main(void)
384 {
385 	struct bt_gatt_attr *vnd_ind_attr;
386 	char str[BT_UUID_STR_LEN];
387 	int err;
388 
389 	err = bt_enable(NULL);
390 	if (err) {
391 		printk("Bluetooth init failed (err %d)\n", err);
392 		return 0;
393 	}
394 
395 	bt_ready();
396 	bt_cts_init(&cts_cb);
397 	bt_hrs_cb_register(&hrs_cb);
398 
399 	bt_gatt_cb_register(&gatt_callbacks);
400 	bt_conn_auth_cb_register(&auth_cb_display);
401 
402 	vnd_ind_attr = bt_gatt_find_by_uuid(vnd_svc.attrs, vnd_svc.attr_count,
403 					    &vnd_enc_uuid.uuid);
404 	bt_uuid_to_str(&vnd_enc_uuid.uuid, str, sizeof(str));
405 	printk("Indicate VND attr %p (UUID %s)\n", vnd_ind_attr, str);
406 
407 	/* Implement notification. At the moment there is no suitable way
408 	 * of starting delayed work so we do it here
409 	 */
410 	while (1) {
411 		k_sleep(K_SECONDS(1));
412 
413 		/* Current time update notification example
414 		 * For testing purposes, we send a manual update notification every second.
415 		 * In production `bt_cts_send_notification` should only be used when time is changed
416 		 */
417 		if (cts_notification_enabled) {
418 			bt_cts_send_notification(BT_CTS_UPDATE_REASON_MANUAL);
419 		}
420 		/* Heartrate measurements simulation */
421 		hrs_notify();
422 
423 		/* Battery level simulation */
424 		bas_notify();
425 
426 		/* Vendor indication simulation */
427 		if (simulate_vnd && vnd_ind_attr) {
428 			if (indicating) {
429 				continue;
430 			}
431 
432 			ind_params.attr = vnd_ind_attr;
433 			ind_params.func = indicate_cb;
434 			ind_params.destroy = indicate_destroy;
435 			ind_params.data = &indicating;
436 			ind_params.len = sizeof(indicating);
437 
438 			if (bt_gatt_indicate(NULL, &ind_params) == 0) {
439 				indicating = 1U;
440 			}
441 		}
442 	}
443 	return 0;
444 }
445