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