1 /** @file
2  *  @brief HRS Service sample
3  */
4 
5 /*
6  * Copyright (c) 2016 Intel Corporation
7  *
8  * SPDX-License-Identifier: Apache-2.0
9  */
10 
11 #include <zephyr/types.h>
12 #include <stddef.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/init.h>
17 #include <zephyr/sys/check.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/hrs.h>
25 
26 #define LOG_LEVEL CONFIG_BT_HRS_LOG_LEVEL
27 #include <zephyr/logging/log.h>
28 LOG_MODULE_REGISTER(hrs);
29 
30 
31 #define GATT_PERM_READ_MASK     (BT_GATT_PERM_READ | \
32 				 BT_GATT_PERM_READ_ENCRYPT | \
33 				 BT_GATT_PERM_READ_AUTHEN)
34 #define GATT_PERM_WRITE_MASK    (BT_GATT_PERM_WRITE | \
35 				 BT_GATT_PERM_WRITE_ENCRYPT | \
36 				 BT_GATT_PERM_WRITE_AUTHEN)
37 
38 #ifndef CONFIG_BT_HRS_DEFAULT_PERM_RW_AUTHEN
39 #define CONFIG_BT_HRS_DEFAULT_PERM_RW_AUTHEN 0
40 #endif
41 #ifndef CONFIG_BT_HRS_DEFAULT_PERM_RW_ENCRYPT
42 #define CONFIG_BT_HRS_DEFAULT_PERM_RW_ENCRYPT 0
43 #endif
44 #ifndef CONFIG_BT_HRS_DEFAULT_PERM_RW
45 #define CONFIG_BT_HRS_DEFAULT_PERM_RW 0
46 #endif
47 
48 #define HRS_GATT_PERM_DEFAULT (						\
49 	CONFIG_BT_HRS_DEFAULT_PERM_RW_AUTHEN ?				\
50 	(BT_GATT_PERM_READ_AUTHEN | BT_GATT_PERM_WRITE_AUTHEN) :	\
51 	CONFIG_BT_HRS_DEFAULT_PERM_RW_ENCRYPT ?				\
52 	(BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT) :	\
53 	(BT_GATT_PERM_READ | BT_GATT_PERM_WRITE))			\
54 
55 static uint8_t hrs_blsc;
56 static sys_slist_t hrs_cbs = SYS_SLIST_STATIC_INIT(&hrs_cbs);
57 
hrmc_ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)58 static void hrmc_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
59 {
60 	ARG_UNUSED(attr);
61 
62 	struct bt_hrs_cb *listener;
63 
64 	bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
65 
66 	LOG_INF("HRS notifications %s", notif_enabled ? "enabled" : "disabled");
67 
68 	SYS_SLIST_FOR_EACH_CONTAINER(&hrs_cbs, listener, _node) {
69 		if (listener->ntf_changed) {
70 			listener->ntf_changed(notif_enabled);
71 		}
72 	}
73 }
74 
read_blsc(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)75 static ssize_t read_blsc(struct bt_conn *conn, const struct bt_gatt_attr *attr,
76 			 void *buf, uint16_t len, uint16_t offset)
77 {
78 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &hrs_blsc,
79 				 sizeof(hrs_blsc));
80 }
81 
82 /* Heart Rate Service Declaration */
83 BT_GATT_SERVICE_DEFINE(hrs_svc,
84 	BT_GATT_PRIMARY_SERVICE(BT_UUID_HRS),
85 	BT_GATT_CHARACTERISTIC(BT_UUID_HRS_MEASUREMENT, BT_GATT_CHRC_NOTIFY,
86 			       BT_GATT_PERM_NONE, NULL, NULL, NULL),
87 	BT_GATT_CCC(hrmc_ccc_cfg_changed,
88 		    HRS_GATT_PERM_DEFAULT),
89 	BT_GATT_CHARACTERISTIC(BT_UUID_HRS_BODY_SENSOR, BT_GATT_CHRC_READ,
90 			       HRS_GATT_PERM_DEFAULT & GATT_PERM_READ_MASK,
91 			       read_blsc, NULL, NULL),
92 	BT_GATT_CHARACTERISTIC(BT_UUID_HRS_CONTROL_POINT, BT_GATT_CHRC_WRITE,
93 			       HRS_GATT_PERM_DEFAULT & GATT_PERM_WRITE_MASK,
94 			       NULL, NULL, NULL),
95 );
96 
hrs_init(void)97 static int hrs_init(void)
98 {
99 
100 	hrs_blsc = 0x01;
101 
102 	return 0;
103 }
104 
bt_hrs_cb_register(struct bt_hrs_cb * cb)105 int bt_hrs_cb_register(struct bt_hrs_cb *cb)
106 {
107 	CHECKIF(cb == NULL) {
108 		return -EINVAL;
109 	}
110 
111 	sys_slist_append(&hrs_cbs, &cb->_node);
112 
113 	return 0;
114 }
115 
bt_hrs_cb_unregister(struct bt_hrs_cb * cb)116 int bt_hrs_cb_unregister(struct bt_hrs_cb *cb)
117 {
118 	CHECKIF(cb == NULL) {
119 		return -EINVAL;
120 	}
121 
122 	if (!sys_slist_find_and_remove(&hrs_cbs, &cb->_node)) {
123 		return -ENOENT;
124 	}
125 
126 	return 0;
127 }
128 
bt_hrs_notify(uint16_t heartrate)129 int bt_hrs_notify(uint16_t heartrate)
130 {
131 	int rc;
132 	static uint8_t hrm[2];
133 
134 	hrm[0] = 0x06; /* uint8, sensor contact */
135 	hrm[1] = heartrate;
136 
137 	rc = bt_gatt_notify(NULL, &hrs_svc.attrs[1], &hrm, sizeof(hrm));
138 
139 	return rc == -ENOTCONN ? 0 : rc;
140 }
141 
142 SYS_INIT(hrs_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
143