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