140 lines
3.3 KiB
C
140 lines
3.3 KiB
C
/** @file
|
|
* @brief Immediate Alert Service implementation
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2022 Codecoup
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <zephyr/init.h>
|
|
#include <stdint.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/net_buf.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/bluetooth/gatt.h>
|
|
#include <zephyr/bluetooth/conn.h>
|
|
#include <zephyr/bluetooth/services/ias.h>
|
|
#include <zephyr/sys/iterable_sections.h>
|
|
|
|
#define LOG_LEVEL CONFIG_BT_IAS_LOG_LEVEL
|
|
LOG_MODULE_REGISTER(ias);
|
|
|
|
#define BT_IAS_ALERT_LVL_LEN 1
|
|
|
|
#if defined(CONFIG_BT_IAS_SEC_AUTH)
|
|
#define IAS_ALERT_LEVEL_PERM BT_GATT_PERM_WRITE_AUTHEN
|
|
#elif defined(CONFIG_BT_IAS_SEC_ENC)
|
|
#define IAS_ALERT_LEVEL_PERM BT_GATT_PERM_WRITE_ENCRYPT
|
|
#else
|
|
#define IAS_ALERT_LEVEL_PERM BT_GATT_PERM_WRITE
|
|
#endif
|
|
|
|
struct alerting_device {
|
|
enum bt_ias_alert_lvl alert_level;
|
|
};
|
|
|
|
static struct alerting_device devices[CONFIG_BT_MAX_CONN];
|
|
static enum bt_ias_alert_lvl curr_lvl;
|
|
|
|
static void set_alert_level(void)
|
|
{
|
|
enum bt_ias_alert_lvl alert_level = BT_IAS_ALERT_LVL_NO_ALERT;
|
|
|
|
/* Set the alert_level as the highest requested by any device */
|
|
for (int i = 0; i < CONFIG_BT_MAX_CONN; i++) {
|
|
if (alert_level < devices[i].alert_level) {
|
|
alert_level = devices[i].alert_level;
|
|
}
|
|
}
|
|
|
|
if (curr_lvl == alert_level) {
|
|
return;
|
|
}
|
|
|
|
curr_lvl = alert_level;
|
|
|
|
if (alert_level == BT_IAS_ALERT_LVL_HIGH_ALERT) {
|
|
STRUCT_SECTION_FOREACH(bt_ias_cb, cb) {
|
|
if (cb->high_alert) {
|
|
cb->high_alert();
|
|
}
|
|
}
|
|
LOG_DBG("High alert");
|
|
} else if (alert_level == BT_IAS_ALERT_LVL_MILD_ALERT) {
|
|
STRUCT_SECTION_FOREACH(bt_ias_cb, cb) {
|
|
if (cb->mild_alert) {
|
|
cb->mild_alert();
|
|
}
|
|
}
|
|
LOG_DBG("Mild alert");
|
|
} else {
|
|
STRUCT_SECTION_FOREACH(bt_ias_cb, cb) {
|
|
if (cb->no_alert) {
|
|
cb->no_alert();
|
|
}
|
|
}
|
|
LOG_DBG("No alert");
|
|
}
|
|
}
|
|
|
|
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
|
{
|
|
devices[bt_conn_index(conn)].alert_level = BT_IAS_ALERT_LVL_NO_ALERT;
|
|
set_alert_level();
|
|
}
|
|
|
|
int bt_ias_local_alert_stop(void)
|
|
{
|
|
if (curr_lvl == BT_IAS_ALERT_LVL_NO_ALERT) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
for (int idx = 0; idx < CONFIG_BT_MAX_CONN; idx++) {
|
|
devices[idx].alert_level = BT_IAS_ALERT_LVL_NO_ALERT;
|
|
}
|
|
curr_lvl = BT_IAS_ALERT_LVL_NO_ALERT;
|
|
set_alert_level();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t 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)
|
|
{
|
|
struct net_buf_simple data;
|
|
enum bt_ias_alert_lvl alert_val;
|
|
|
|
if (offset > 0) {
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
|
}
|
|
|
|
if (len != BT_IAS_ALERT_LVL_LEN) {
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
|
}
|
|
|
|
net_buf_simple_init_with_data(&data, (void *)buf, len);
|
|
alert_val = net_buf_simple_pull_u8(&data);
|
|
devices[bt_conn_index(conn)].alert_level = alert_val;
|
|
|
|
if (alert_val < BT_IAS_ALERT_LVL_NO_ALERT || alert_val > BT_IAS_ALERT_LVL_HIGH_ALERT) {
|
|
return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
|
|
}
|
|
set_alert_level();
|
|
|
|
return len;
|
|
}
|
|
|
|
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
|
.disconnected = disconnected,
|
|
};
|
|
|
|
/* Immediate Alert Service Declaration */
|
|
BT_GATT_SERVICE_DEFINE(ias_svc,
|
|
BT_GATT_PRIMARY_SERVICE(BT_UUID_IAS),
|
|
BT_GATT_CHARACTERISTIC(BT_UUID_ALERT_LEVEL, BT_GATT_CHRC_WRITE_WITHOUT_RESP,
|
|
IAS_ALERT_LEVEL_PERM, NULL,
|
|
bt_ias_write_alert_lvl, NULL));
|