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 int signed_value;
134 
read_signed(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)135 static ssize_t read_signed(struct bt_conn *conn, const struct bt_gatt_attr *attr,
136 			   void *buf, uint16_t len, uint16_t offset)
137 {
138 	const char *value = attr->user_data;
139 
140 	return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
141 				 sizeof(signed_value));
142 }
143 
write_signed(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)144 static ssize_t write_signed(struct bt_conn *conn, const struct bt_gatt_attr *attr,
145 			    const void *buf, uint16_t len, uint16_t offset,
146 			    uint8_t flags)
147 {
148 	uint8_t *value = attr->user_data;
149 
150 	if (offset + len > sizeof(signed_value)) {
151 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
152 	}
153 
154 	memcpy(value + offset, buf, len);
155 
156 	return len;
157 }
158 
159 static const struct bt_uuid_128 vnd_signed_uuid = BT_UUID_INIT_128(
160 	BT_UUID_128_ENCODE(0x13345678, 0x1234, 0x5678, 0x1334, 0x56789abcdef3));
161 
162 static const struct bt_uuid_128 vnd_write_cmd_uuid = BT_UUID_INIT_128(
163 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef4));
164 
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)165 static ssize_t write_without_rsp_vnd(struct bt_conn *conn,
166 				     const struct bt_gatt_attr *attr,
167 				     const void *buf, uint16_t len, uint16_t offset,
168 				     uint8_t flags)
169 {
170 	uint8_t *value = attr->user_data;
171 
172 	if (!(flags & BT_GATT_WRITE_FLAG_CMD)) {
173 		/* Write Request received. Reject it since this Characteristic
174 		 * only accepts Write Without Response.
175 		 */
176 		return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
177 	}
178 
179 	if (offset + len > VND_MAX_LEN) {
180 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
181 	}
182 
183 	memcpy(value + offset, buf, len);
184 	value[offset + len] = 0;
185 
186 	return len;
187 }
188 
189 /* Vendor Primary Service Declaration */
190 BT_GATT_SERVICE_DEFINE(vnd_svc,
191 	BT_GATT_PRIMARY_SERVICE(&vnd_uuid),
192 	BT_GATT_CHARACTERISTIC(&vnd_enc_uuid.uuid,
193 			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE |
194 			       BT_GATT_CHRC_INDICATE,
195 			       BT_GATT_PERM_READ_ENCRYPT |
196 			       BT_GATT_PERM_WRITE_ENCRYPT,
197 			       read_vnd, write_vnd, vnd_value),
198 	BT_GATT_CCC(vnd_ccc_cfg_changed,
199 		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_ENCRYPT),
200 	BT_GATT_CHARACTERISTIC(&vnd_auth_uuid.uuid,
201 			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
202 			       BT_GATT_PERM_READ_AUTHEN |
203 			       BT_GATT_PERM_WRITE_AUTHEN,
204 			       read_vnd, write_vnd, vnd_auth_value),
205 	BT_GATT_CHARACTERISTIC(&vnd_long_uuid.uuid, BT_GATT_CHRC_READ |
206 			       BT_GATT_CHRC_WRITE | BT_GATT_CHRC_EXT_PROP,
207 			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE |
208 			       BT_GATT_PERM_PREPARE_WRITE,
209 			       read_vnd, write_long_vnd, &vnd_long_value),
210 	BT_GATT_CEP(&vnd_long_cep),
211 	BT_GATT_CHARACTERISTIC(&vnd_signed_uuid.uuid, BT_GATT_CHRC_READ |
212 			       BT_GATT_CHRC_WRITE | BT_GATT_CHRC_AUTH,
213 			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
214 			       read_signed, write_signed, &signed_value),
215 	BT_GATT_CHARACTERISTIC(&vnd_write_cmd_uuid.uuid,
216 			       BT_GATT_CHRC_WRITE_WITHOUT_RESP,
217 			       BT_GATT_PERM_WRITE, NULL,
218 			       write_without_rsp_vnd, &vnd_wwr_value),
219 );
220 
221 static const struct bt_data ad[] = {
222 	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
223 	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
224 		      BT_UUID_16_ENCODE(BT_UUID_HRS_VAL),
225 		      BT_UUID_16_ENCODE(BT_UUID_BAS_VAL),
226 		      BT_UUID_16_ENCODE(BT_UUID_CTS_VAL)),
227 	BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_CUSTOM_SERVICE_VAL),
228 };
229 
230 static const struct bt_data sd[] = {
231 	BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
232 };
233 
mtu_updated(struct bt_conn * conn,uint16_t tx,uint16_t rx)234 void mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
235 {
236 	printk("Updated MTU: TX: %d RX: %d bytes\n", tx, rx);
237 }
238 
239 static struct bt_gatt_cb gatt_callbacks = {
240 	.att_mtu_updated = mtu_updated
241 };
242 
connected(struct bt_conn * conn,uint8_t err)243 static void connected(struct bt_conn *conn, uint8_t err)
244 {
245 	if (err) {
246 		printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err));
247 	} else {
248 		printk("Connected\n");
249 	}
250 }
251 
disconnected(struct bt_conn * conn,uint8_t reason)252 static void disconnected(struct bt_conn *conn, uint8_t reason)
253 {
254 	printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
255 }
256 
alert_stop(void)257 static void alert_stop(void)
258 {
259 	printk("Alert stopped\n");
260 }
261 
alert_start(void)262 static void alert_start(void)
263 {
264 	printk("Mild alert started\n");
265 }
266 
alert_high_start(void)267 static void alert_high_start(void)
268 {
269 	printk("High alert started\n");
270 }
271 
272 BT_CONN_CB_DEFINE(conn_callbacks) = {
273 	.connected = connected,
274 	.disconnected = disconnected,
275 };
276 
277 BT_IAS_CB_DEFINE(ias_callbacks) = {
278 	.no_alert = alert_stop,
279 	.mild_alert = alert_start,
280 	.high_alert = alert_high_start,
281 };
282 
bt_ready(void)283 static void bt_ready(void)
284 {
285 	int err;
286 
287 	printk("Bluetooth initialized\n");
288 
289 	if (IS_ENABLED(CONFIG_SETTINGS)) {
290 		settings_load();
291 	}
292 
293 	err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
294 	if (err) {
295 		printk("Advertising failed to start (err %d)\n", err);
296 		return;
297 	}
298 
299 	printk("Advertising successfully started\n");
300 }
301 
auth_passkey_display(struct bt_conn * conn,unsigned int passkey)302 static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
303 {
304 	char addr[BT_ADDR_LE_STR_LEN];
305 
306 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
307 
308 	printk("Passkey for %s: %06u\n", addr, passkey);
309 }
310 
auth_cancel(struct bt_conn * conn)311 static void auth_cancel(struct bt_conn *conn)
312 {
313 	char addr[BT_ADDR_LE_STR_LEN];
314 
315 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
316 
317 	printk("Pairing cancelled: %s\n", addr);
318 }
319 
320 static struct bt_conn_auth_cb auth_cb_display = {
321 	.passkey_display = auth_passkey_display,
322 	.passkey_entry = NULL,
323 	.cancel = auth_cancel,
324 };
325 
bas_notify(void)326 static void bas_notify(void)
327 {
328 	uint8_t battery_level = bt_bas_get_battery_level();
329 
330 	battery_level--;
331 
332 	if (!battery_level) {
333 		battery_level = 100U;
334 	}
335 
336 	bt_bas_set_battery_level(battery_level);
337 }
338 
339 static uint8_t bt_heartrate = BT_HR_HEARTRATE_DEFAULT_MIN;
340 
hrs_notify(void)341 static void hrs_notify(void)
342 {
343 	/* Heartrate measurements simulation */
344 	bt_heartrate++;
345 	if (bt_heartrate == BT_HR_HEARTRATE_DEFAULT_MAX) {
346 		bt_heartrate = BT_HR_HEARTRATE_DEFAULT_MIN;
347 	}
348 
349 	bt_hrs_notify(bt_heartrate);
350 }
351 
352 /**
353  * variable to hold reference milliseconds to epoch when device booted
354  * this is only for demo purpose, for more precise synchronization please
355  * review clock_settime API implementation.
356  */
357 static int64_t unix_ms_ref;
358 static bool cts_notification_enabled;
359 
bt_cts_notification_changed(bool enabled)360 void bt_cts_notification_changed(bool enabled)
361 {
362 	cts_notification_enabled = enabled;
363 }
364 
bt_cts_cts_time_write(struct bt_cts_time_format * cts_time)365 int bt_cts_cts_time_write(struct bt_cts_time_format *cts_time)
366 {
367 	int err;
368 	int64_t unix_ms;
369 
370 	if (IS_ENABLED(CONFIG_BT_CTS_HELPER_API)) {
371 		err = bt_cts_time_to_unix_ms(cts_time, &unix_ms);
372 		if (err) {
373 			return err;
374 		}
375 	} else {
376 		return -ENOTSUP;
377 	}
378 
379 	/* recalculate reference value */
380 	unix_ms_ref = unix_ms - k_uptime_get();
381 	return 0;
382 }
383 
bt_cts_fill_current_cts_time(struct bt_cts_time_format * cts_time)384 int bt_cts_fill_current_cts_time(struct bt_cts_time_format *cts_time)
385 {
386 	int64_t unix_ms = unix_ms_ref + k_uptime_get();
387 
388 	if (IS_ENABLED(CONFIG_BT_CTS_HELPER_API)) {
389 		return bt_cts_time_from_unix_ms(cts_time, unix_ms);
390 	} else {
391 		return -ENOTSUP;
392 	}
393 }
394 
395 const struct bt_cts_cb cts_cb = {
396 	.notification_changed = bt_cts_notification_changed,
397 	.cts_time_write = bt_cts_cts_time_write,
398 	.fill_current_cts_time = bt_cts_fill_current_cts_time,
399 };
400 
bt_hrs_ctrl_point_write(uint8_t request)401 static int bt_hrs_ctrl_point_write(uint8_t request)
402 {
403 	printk("HRS Control point request: %d\n", request);
404 	if (request != BT_HRS_CONTROL_POINT_RESET_ENERGY_EXPANDED_REQ) {
405 		return -ENOTSUP;
406 	}
407 
408 	bt_heartrate = BT_HR_HEARTRATE_DEFAULT_MIN;
409 	return 0;
410 }
411 
412 static struct bt_hrs_cb hrs_cb = {
413 	.ctrl_point_write = bt_hrs_ctrl_point_write,
414 };
415 
main(void)416 int main(void)
417 {
418 	struct bt_gatt_attr *vnd_ind_attr;
419 	char str[BT_UUID_STR_LEN];
420 	int err;
421 
422 	err = bt_enable(NULL);
423 	if (err) {
424 		printk("Bluetooth init failed (err %d)\n", err);
425 		return 0;
426 	}
427 
428 	bt_ready();
429 	bt_cts_init(&cts_cb);
430 	bt_hrs_cb_register(&hrs_cb);
431 
432 	bt_gatt_cb_register(&gatt_callbacks);
433 	bt_conn_auth_cb_register(&auth_cb_display);
434 
435 	vnd_ind_attr = bt_gatt_find_by_uuid(vnd_svc.attrs, vnd_svc.attr_count,
436 					    &vnd_enc_uuid.uuid);
437 	bt_uuid_to_str(&vnd_enc_uuid.uuid, str, sizeof(str));
438 	printk("Indicate VND attr %p (UUID %s)\n", vnd_ind_attr, str);
439 
440 	/* Implement notification. At the moment there is no suitable way
441 	 * of starting delayed work so we do it here
442 	 */
443 	while (1) {
444 		k_sleep(K_SECONDS(1));
445 
446 		/* Current time update notification example
447 		 * For testing purposes, we send a manual update notification every second.
448 		 * In production `bt_cts_send_notification` should only be used when time is changed
449 		 */
450 		if (cts_notification_enabled) {
451 			bt_cts_send_notification(BT_CTS_UPDATE_REASON_MANUAL);
452 		}
453 		/* Heartrate measurements simulation */
454 		hrs_notify();
455 
456 		/* Battery level simulation */
457 		bas_notify();
458 
459 		/* Vendor indication simulation */
460 		if (simulate_vnd && vnd_ind_attr) {
461 			if (indicating) {
462 				continue;
463 			}
464 
465 			ind_params.attr = vnd_ind_attr;
466 			ind_params.func = indicate_cb;
467 			ind_params.destroy = indicate_destroy;
468 			ind_params.data = &indicating;
469 			ind_params.len = sizeof(indicating);
470 
471 			if (bt_gatt_indicate(NULL, &ind_params) == 0) {
472 				indicating = 1U;
473 			}
474 		}
475 	}
476 	return 0;
477 }
478