1 /** @file
2 * @brief Immediate Alert Service implementation
3 */
4
5 /*
6 * Copyright (c) 2022 Codecoup
7 *
8 * SPDX-License-Identifier: Apache-2.0
9 */
10
11 #include <errno.h>
12 #include <zephyr/init.h>
13 #include <stdint.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/net_buf.h>
16 #include <zephyr/logging/log.h>
17 #include <zephyr/bluetooth/gatt.h>
18 #include <zephyr/bluetooth/conn.h>
19 #include <zephyr/bluetooth/services/ias.h>
20 #include <zephyr/sys/iterable_sections.h>
21
22 #define LOG_LEVEL CONFIG_BT_IAS_LOG_LEVEL
23 LOG_MODULE_REGISTER(ias);
24
25 #define BT_IAS_ALERT_LVL_LEN 1
26
27 #if defined(CONFIG_BT_IAS_SEC_AUTH)
28 #define IAS_ALERT_LEVEL_PERM BT_GATT_PERM_WRITE_AUTHEN
29 #elif defined(CONFIG_BT_IAS_SEC_ENC)
30 #define IAS_ALERT_LEVEL_PERM BT_GATT_PERM_WRITE_ENCRYPT
31 #else
32 #define IAS_ALERT_LEVEL_PERM BT_GATT_PERM_WRITE
33 #endif
34
35 struct alerting_device {
36 enum bt_ias_alert_lvl alert_level;
37 };
38
39 static struct alerting_device devices[CONFIG_BT_MAX_CONN];
40 static enum bt_ias_alert_lvl curr_lvl;
41
set_alert_level(void)42 static void set_alert_level(void)
43 {
44 enum bt_ias_alert_lvl alert_level = BT_IAS_ALERT_LVL_NO_ALERT;
45
46 /* Set the alert_level as the highest requested by any device */
47 for (int i = 0; i < CONFIG_BT_MAX_CONN; i++) {
48 if (alert_level < devices[i].alert_level) {
49 alert_level = devices[i].alert_level;
50 }
51 }
52
53 if (curr_lvl == alert_level) {
54 return;
55 }
56
57 curr_lvl = alert_level;
58
59 if (alert_level == BT_IAS_ALERT_LVL_HIGH_ALERT) {
60 STRUCT_SECTION_FOREACH(bt_ias_cb, cb) {
61 if (cb->high_alert) {
62 cb->high_alert();
63 }
64 }
65 LOG_DBG("High alert");
66 } else if (alert_level == BT_IAS_ALERT_LVL_MILD_ALERT) {
67 STRUCT_SECTION_FOREACH(bt_ias_cb, cb) {
68 if (cb->mild_alert) {
69 cb->mild_alert();
70 }
71 }
72 LOG_DBG("Mild alert");
73 } else {
74 STRUCT_SECTION_FOREACH(bt_ias_cb, cb) {
75 if (cb->no_alert) {
76 cb->no_alert();
77 }
78 }
79 LOG_DBG("No alert");
80 }
81 }
82
disconnected(struct bt_conn * conn,uint8_t reason)83 static void disconnected(struct bt_conn *conn, uint8_t reason)
84 {
85 devices[bt_conn_index(conn)].alert_level = BT_IAS_ALERT_LVL_NO_ALERT;
86 set_alert_level();
87 }
88
bt_ias_local_alert_stop(void)89 int bt_ias_local_alert_stop(void)
90 {
91 if (curr_lvl == BT_IAS_ALERT_LVL_NO_ALERT) {
92 return -EALREADY;
93 }
94
95 for (int idx = 0; idx < CONFIG_BT_MAX_CONN; idx++) {
96 devices[idx].alert_level = BT_IAS_ALERT_LVL_NO_ALERT;
97 }
98 curr_lvl = BT_IAS_ALERT_LVL_NO_ALERT;
99 set_alert_level();
100
101 return 0;
102 }
103
bt_ias_write_alert_lvl(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)104 static ssize_t bt_ias_write_alert_lvl(struct bt_conn *conn, const struct bt_gatt_attr *attr,
105 const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
106 {
107 struct net_buf_simple data;
108 enum bt_ias_alert_lvl alert_val;
109
110 if (offset > 0) {
111 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
112 }
113
114 if (len != BT_IAS_ALERT_LVL_LEN) {
115 return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
116 }
117
118 net_buf_simple_init_with_data(&data, (void *)buf, len);
119 alert_val = net_buf_simple_pull_u8(&data);
120 devices[bt_conn_index(conn)].alert_level = alert_val;
121
122 if (alert_val < BT_IAS_ALERT_LVL_NO_ALERT || alert_val > BT_IAS_ALERT_LVL_HIGH_ALERT) {
123 return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
124 }
125 set_alert_level();
126
127 return len;
128 }
129
130 BT_CONN_CB_DEFINE(conn_callbacks) = {
131 .disconnected = disconnected,
132 };
133
134 /* Immediate Alert Service Declaration */
135 BT_GATT_SERVICE_DEFINE(ias_svc,
136 BT_GATT_PRIMARY_SERVICE(BT_UUID_IAS),
137 BT_GATT_CHARACTERISTIC(BT_UUID_ALERT_LEVEL, BT_GATT_CHRC_WRITE_WITHOUT_RESP,
138 IAS_ALERT_LEVEL_PERM, NULL,
139 bt_ias_write_alert_lvl, NULL));
140