Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions include/zephyr/bluetooth/services/ias.h
Original file line number Diff line number Diff line change
@@ -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_ */
4 changes: 4 additions & 0 deletions include/zephyr/linker/common-rom.ld
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions samples/bluetooth/peripheral/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions samples/bluetooth/peripheral/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <bluetooth/gatt.h>
#include <bluetooth/services/bas.h>
#include <bluetooth/services/hrs.h>
#include <bluetooth/services/ias.h>

#include "cts.h"

Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions subsys/bluetooth/services/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
2 changes: 2 additions & 0 deletions subsys/bluetooth/services/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ rsource "Kconfig.hrs"

rsource "Kconfig.tps"

rsource "Kconfig.ias"

rsource "ots/Kconfig"

endmenu
33 changes: 33 additions & 0 deletions subsys/bluetooth/services/Kconfig.ias
Original file line number Diff line number Diff line change
@@ -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
143 changes: 143 additions & 0 deletions subsys/bluetooth/services/ias.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/** @file
* @brief Immediate Alert Service implementation
*/

/*
* Copyright (c) 2022 Codecoup
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <errno.h>
#include <init.h>
#include <stdint.h>
#include <zephyr.h>
#include <net/buf.h>
#include <logging/log.h>
#include <bluetooth/gatt.h>
#include <bluetooth/conn.h>
#include <bluetooth/services/ias.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

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));