From 83ec366e5d384fddc255397a00fc5d71923c5c3b Mon Sep 17 00:00:00 2001 From: Szymon Czapracki Date: Tue, 22 Feb 2022 17:26:08 +0100 Subject: [PATCH] Bluetooth: Immediate Alert Service This commits adds IAS for zephyr bluetooth. Signed-off-by: Szymon Czapracki --- include/zephyr/bluetooth/services/ias.h | 71 +++++++++++ include/zephyr/linker/common-rom.ld | 4 + samples/bluetooth/peripheral/prj.conf | 1 + samples/bluetooth/peripheral/src/main.c | 22 ++++ subsys/bluetooth/services/CMakeLists.txt | 2 + subsys/bluetooth/services/Kconfig | 2 + subsys/bluetooth/services/Kconfig.ias | 33 ++++++ subsys/bluetooth/services/ias.c | 143 +++++++++++++++++++++++ 8 files changed, 278 insertions(+) create mode 100644 include/zephyr/bluetooth/services/ias.h create mode 100644 subsys/bluetooth/services/Kconfig.ias create mode 100644 subsys/bluetooth/services/ias.c diff --git a/include/zephyr/bluetooth/services/ias.h b/include/zephyr/bluetooth/services/ias.h new file mode 100644 index 0000000000000..9e06e985474eb --- /dev/null +++ b/include/zephyr/bluetooth/services/ias.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 Codecoup + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_SERVICES_IAS_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_SERVICES_IAS_H_ + +/** + * @brief Immediate Alert Service (IAS) + * @defgroup bt_ias Immediate Alert Service (IAS) + * @ingroup bluetooth + * @{ + * + * [Experimental] Users should note that the APIs can change + * as a part of ongoing development. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Immediate Alert Service callback structure. */ +struct bt_ias_cb { + /** + * @brief Callback function to stop alert. + * + * This callback is called when peer commands to disable alert. + */ + void (*no_alert)(void); + + /** + * @brief Callback function for alert level value. + * + * This callback is called when peer commands to alert. + */ + void (*mild_alert)(void); + + /** + * @brief Callback function for alert level value. + * + * This callback is called when peer commands to alert in the strongest possible way. + */ + void (*high_alert)(void); +}; + +/** @brief Method for stopping alert locally + * + * @return Zero in case of success and error code in case of error. + */ +int bt_ias_local_alert_stop(void); + +/** @def BT_IAS_CB_DEFINE + * + * @brief Register a callback structure for immediate alert events. + * + * @param _name Name of callback structure. + */ +#define BT_IAS_CB_DEFINE(_name) \ + static const STRUCT_SECTION_ITERABLE(bt_ias_cb, _CONCAT(bt_ias_cb_, _name)) + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_SERVICES_IAS_H_ */ diff --git a/include/zephyr/linker/common-rom.ld b/include/zephyr/linker/common-rom.ld index cf4ff7b273b85..051bc056e1658 100644 --- a/include/zephyr/linker/common-rom.ld +++ b/include/zephyr/linker/common-rom.ld @@ -136,6 +136,10 @@ ITERABLE_SECTION_ROM(bt_mesh_lpn_cb, 4) #endif +#if defined(CONFIG_BT_IAS) + ITERABLE_SECTION_ROM(bt_ias_cb, 4) +#endif + #if defined(CONFIG_BT_MESH_GATT_PROXY) ITERABLE_SECTION_ROM(bt_mesh_proxy_cb, 4) #endif diff --git a/samples/bluetooth/peripheral/prj.conf b/samples/bluetooth/peripheral/prj.conf index d9637e997223f..549a30da79f8d 100644 --- a/samples/bluetooth/peripheral/prj.conf +++ b/samples/bluetooth/peripheral/prj.conf @@ -10,6 +10,7 @@ CONFIG_BT_DIS=y CONFIG_BT_ATT_PREPARE_COUNT=5 CONFIG_BT_BAS=y CONFIG_BT_HRS=y +CONFIG_BT_IAS=y CONFIG_BT_PRIVACY=y CONFIG_BT_DEVICE_NAME="Zephyr Peripheral Sample Long Name" CONFIG_BT_DEVICE_APPEARANCE=833 diff --git a/samples/bluetooth/peripheral/src/main.c b/samples/bluetooth/peripheral/src/main.c index 35ed3ed52a2e1..d0a2c924fa1c9 100644 --- a/samples/bluetooth/peripheral/src/main.c +++ b/samples/bluetooth/peripheral/src/main.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "cts.h" @@ -248,11 +249,32 @@ static void disconnected(struct bt_conn *conn, uint8_t reason) printk("Disconnected (reason 0x%02x)\n", reason); } +static void alert_stop(void) +{ + printk("Alert stopped\n"); +} + +static void alert_start(void) +{ + printk("Mild alert started\n"); +} + +static void alert_high_start(void) +{ + printk("High alert started\n"); +} + BT_CONN_CB_DEFINE(conn_callbacks) = { .connected = connected, .disconnected = disconnected, }; +BT_IAS_CB_DEFINE(ias_callbacks) = { + .no_alert = alert_stop, + .mild_alert = alert_start, + .high_alert = alert_high_start, +}; + static void bt_ready(void) { int err; diff --git a/subsys/bluetooth/services/CMakeLists.txt b/subsys/bluetooth/services/CMakeLists.txt index 0732f2bfaf2c3..a9bbccfcec323 100644 --- a/subsys/bluetooth/services/CMakeLists.txt +++ b/subsys/bluetooth/services/CMakeLists.txt @@ -9,6 +9,8 @@ zephyr_sources_ifdef(CONFIG_BT_HRS hrs.c) zephyr_sources_ifdef(CONFIG_BT_TPS tps.c) +zephyr_sources_ifdef(CONFIG_BT_IAS ias.c) + if(CONFIG_BT_OTS OR CONFIG_BT_OTS_CLIENT) add_subdirectory(ots) endif() diff --git a/subsys/bluetooth/services/Kconfig b/subsys/bluetooth/services/Kconfig index d898e2a496a3b..0555aa2a94230 100644 --- a/subsys/bluetooth/services/Kconfig +++ b/subsys/bluetooth/services/Kconfig @@ -14,6 +14,8 @@ rsource "Kconfig.hrs" rsource "Kconfig.tps" +rsource "Kconfig.ias" + rsource "ots/Kconfig" endmenu diff --git a/subsys/bluetooth/services/Kconfig.ias b/subsys/bluetooth/services/Kconfig.ias new file mode 100644 index 0000000000000..93e044642f583 --- /dev/null +++ b/subsys/bluetooth/services/Kconfig.ias @@ -0,0 +1,33 @@ +# Bluetooth GATT Immediate Alert Service + +# Copyright (c) 2022 Codecoup +# SPDX-License-Identifier: Apache-2.0 + +menuconfig BT_IAS + bool "Support for GATT Immediate Alert Service [EXPERIMENTAL]" + select EXPERIMENTAL + +if BT_IAS + +choice BT_IAS_SEC_CHOICE + prompt "Default permissions used for IAS characteristics" + default BT_IAS_SEC_NONE + help + Default write permissions for IAS characteristic attributes + +config BT_IAS_SEC_NONE + bool "No security required" + +config BT_IAS_SEC_ENC + bool "Require encryption for write access" + +config BT_IAS_SEC_AUTH + bool "Require encryption and authentication for write access" + +endchoice #BT_IAS_SEC_CHOICE + +module = BT_IAS +module-str = IAS +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" + +endif # BT_IAS diff --git a/subsys/bluetooth/services/ias.c b/subsys/bluetooth/services/ias.c new file mode 100644 index 0000000000000..9a6f702be8e0a --- /dev/null +++ b/subsys/bluetooth/services/ias.c @@ -0,0 +1,143 @@ +/** @file + * @brief Immediate Alert Service implementation + */ + +/* + * Copyright (c) 2022 Codecoup + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +enum bt_ias_alert_lvl { + BT_IAS_ALERT_LVL_NO_ALERT, + BT_IAS_ALERT_LVL_MILD_ALERT, + BT_IAS_ALERT_LVL_HIGH_ALERT, +}; + +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; + + alert_level = devices[0].alert_level; + for (int i = 1; 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; + } + + 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"); + } + curr_lvl = alert_level; +} + +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));