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 /**
49  * @brief GATT ATTR Error that should be returned in case
50  * HRS Control point request is not supported.
51  */
52 #define BT_HRS_ATT_ERR_CONTROL_POINT_NOT_SUPPORTED 0x80
53 
54 #define HRS_GATT_PERM_DEFAULT (						\
55 	CONFIG_BT_HRS_DEFAULT_PERM_RW_AUTHEN ?				\
56 	(BT_GATT_PERM_READ_AUTHEN | BT_GATT_PERM_WRITE_AUTHEN) :	\
57 	CONFIG_BT_HRS_DEFAULT_PERM_RW_ENCRYPT ?				\
58 	(BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT) :	\
59 	(BT_GATT_PERM_READ | BT_GATT_PERM_WRITE))			\
60 
61 static uint8_t hrs_blsc;
62 static sys_slist_t hrs_cbs = SYS_SLIST_STATIC_INIT(&hrs_cbs);
63 
hrmc_ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)64 static void hrmc_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
65 {
66 	ARG_UNUSED(attr);
67 
68 	struct bt_hrs_cb *listener;
69 
70 	bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
71 
72 	LOG_INF("HRS notifications %s", notif_enabled ? "enabled" : "disabled");
73 
74 	SYS_SLIST_FOR_EACH_CONTAINER(&hrs_cbs, listener, _node) {
75 		if (listener->ntf_changed) {
76 			listener->ntf_changed(notif_enabled);
77 		}
78 	}
79 }
80 
read_blsc(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)81 static ssize_t read_blsc(struct bt_conn *conn, const struct bt_gatt_attr *attr,
82 			 void *buf, uint16_t len, uint16_t offset)
83 {
84 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &hrs_blsc,
85 				 sizeof(hrs_blsc));
86 }
87 
ctrl_point_write(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)88 static ssize_t ctrl_point_write(struct bt_conn *conn, const struct bt_gatt_attr *attr,
89 				const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
90 {
91 	int err = -ENOTSUP;
92 	struct bt_hrs_cb *listener;
93 
94 	LOG_INF("HRS CTRL Point Written %d", len);
95 
96 	SYS_SLIST_FOR_EACH_CONTAINER(&hrs_cbs, listener, _node) {
97 		if (listener->ctrl_point_write) {
98 			err = listener->ctrl_point_write(*((uint8_t *)buf));
99 			/* If we get an error other than ENOTSUP then immediately
100 			 * break the loop and return a generic gatt error, assuming this
101 			 * listener supports this request code, but failed to serve it
102 			 */
103 			if ((err != 0) && (err != -ENOTSUP)) {
104 				return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
105 			}
106 		}
107 	}
108 	if (err) {
109 		return BT_GATT_ERR(BT_HRS_ATT_ERR_CONTROL_POINT_NOT_SUPPORTED);
110 	} else {
111 		return len;
112 	}
113 }
114 
115 /* Heart Rate Service Declaration */
116 BT_GATT_SERVICE_DEFINE(hrs_svc,
117 	BT_GATT_PRIMARY_SERVICE(BT_UUID_HRS),
118 	BT_GATT_CHARACTERISTIC(BT_UUID_HRS_MEASUREMENT, BT_GATT_CHRC_NOTIFY,
119 			       BT_GATT_PERM_NONE, NULL, NULL, NULL),
120 	BT_GATT_CCC(hrmc_ccc_cfg_changed,
121 		    HRS_GATT_PERM_DEFAULT),
122 	BT_GATT_CHARACTERISTIC(BT_UUID_HRS_BODY_SENSOR, BT_GATT_CHRC_READ,
123 			       HRS_GATT_PERM_DEFAULT & GATT_PERM_READ_MASK,
124 			       read_blsc, NULL, NULL),
125 	BT_GATT_CHARACTERISTIC(BT_UUID_HRS_CONTROL_POINT, BT_GATT_CHRC_WRITE,
126 			       HRS_GATT_PERM_DEFAULT & GATT_PERM_WRITE_MASK,
127 			       NULL, ctrl_point_write, NULL),
128 );
129 
hrs_init(void)130 static int hrs_init(void)
131 {
132 
133 	hrs_blsc = 0x01;
134 
135 	return 0;
136 }
137 
bt_hrs_cb_register(struct bt_hrs_cb * cb)138 int bt_hrs_cb_register(struct bt_hrs_cb *cb)
139 {
140 	CHECKIF(cb == NULL) {
141 		return -EINVAL;
142 	}
143 
144 	sys_slist_append(&hrs_cbs, &cb->_node);
145 
146 	return 0;
147 }
148 
bt_hrs_cb_unregister(struct bt_hrs_cb * cb)149 int bt_hrs_cb_unregister(struct bt_hrs_cb *cb)
150 {
151 	CHECKIF(cb == NULL) {
152 		return -EINVAL;
153 	}
154 
155 	if (!sys_slist_find_and_remove(&hrs_cbs, &cb->_node)) {
156 		return -ENOENT;
157 	}
158 
159 	return 0;
160 }
161 
bt_hrs_notify(uint16_t heartrate)162 int bt_hrs_notify(uint16_t heartrate)
163 {
164 	int rc;
165 	static uint8_t hrm[2];
166 
167 	hrm[0] = 0x06; /* uint8, sensor contact */
168 	hrm[1] = heartrate;
169 
170 	rc = bt_gatt_notify(NULL, &hrs_svc.attrs[1], &hrm, sizeof(hrm));
171 
172 	return rc == -ENOTCONN ? 0 : rc;
173 }
174 
175 SYS_INIT(hrs_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
176