zephyr/subsys/bluetooth/services/bas/bas_bls.c
Nithin Ramesh Myliattil b717488be2 bluetooth: BAS: add battery critical status char to bas service
Added the battery critical status char to bas service
as per bas_1.1 spec. Updated BSIM test for BAS service
to test the INDs of BAS critical characteristic.

Signed-off-by: Nithin Ramesh Myliattil <niym@demant.com>
2024-10-22 14:06:35 +02:00

272 lines
8.8 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2024 Demant A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/sys/byteorder.h>
#include <zephyr/bluetooth/services/bas.h>
#include <zephyr/bluetooth/gatt.h>
#include "bas_internal.h"
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(bas, CONFIG_BT_BAS_LOG_LEVEL);
/* The battery level status of a battery. */
static struct bt_bas_bls bls;
#define BT_BAS_IDX_BATT_LVL_STATUS_CHAR_VAL 6
/* Notify/Indicate all connections */
static struct bt_gatt_indicate_params ind_params;
/*
* Bitfield structure: Power State
*
* - Bits 0: Battery Present
* - Bits 12: Wired External Power Source Connected
* - Bits 34: Wireless External Power Source Connected
* - Bits 56: Battery Charge State
* - Bits 78: Battery Charge Level
* - Bits 911: Charging Type
* - Bits 1214: Charging Fault Reason
* - Bit 15: RFU
*
* For detailed specification, refer to:
* https://bitbucket.org/bluetooth-SIG/public/src/main/gss/
* org.bluetooth.characteristic.battery_level_status.yaml
*/
#define BATTERY_SHIFT 0
#define WIRED_POWER_SHIFT 1
#define WIRELESS_POWER_SHIFT 3
#define BATTERY_CHARGE_STATE_SHIFT 5
#define BATTERY_CHARGE_LEVEL_SHIFT 7
#define BATTERY_CHARGE_TYPE_SHIFT 9
#define CHARGING_FAULT_SHIFT 12
#define BATTERY_MASK (BIT_MASK(1) << BATTERY_SHIFT)
#define WIRED_POWER_MASK (BIT_MASK(2) << WIRED_POWER_SHIFT)
#define WIRELESS_POWER_MASK (BIT_MASK(2) << WIRELESS_POWER_SHIFT)
#define BATTERY_CHARGE_STATE_MASK (BIT_MASK(2) << BATTERY_CHARGE_STATE_SHIFT)
#define BATTERY_CHARGE_LEVEL_MASK (BIT_MASK(2) << BATTERY_CHARGE_LEVEL_SHIFT)
#define BATTERY_CHARGE_TYPE_MASK (BIT_MASK(3) << BATTERY_CHARGE_TYPE_SHIFT)
#define CHARGING_FAULT_MASK (BIT_MASK(3) << CHARGING_FAULT_SHIFT)
/*
* Bitfield structure: Additional Status
*
* - Bits 01: Service Required
* - Bit 2: Battery Fault
* - Bits 37: Reserved
*/
#define SERVICE_REQUIRED_SHIFT 0
#define BATTERY_FAULT_SHIFT 2
#define SERVICE_REQUIRED_MASK (BIT_MASK(2) << SERVICE_REQUIRED_SHIFT)
#define BATTERY_FAULT_MASK (BIT_MASK(1) << BATTERY_FAULT_SHIFT)
void bt_bas_bls_init(void)
{
LOG_DBG("Initialise BAS Battery Level Status Module");
bls.flags = 0;
bls.power_state = 0;
#if defined(CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT)
/* Set identifier flag */
bls.flags |= BT_BAS_BLS_FLAG_IDENTIFIER_PRESENT;
bls.identifier = 0;
#endif /* CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT */
#if defined(CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT)
/* Set battery level flag */
bls.flags |= BT_BAS_BLS_FLAG_BATTERY_LEVEL_PRESENT;
bls.battery_level = bt_bas_get_battery_level();
#endif /* CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT */
#if defined(CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT)
/* Set additional status flag */
bls.flags |= BT_BAS_BLS_FLAG_ADDITIONAL_STATUS_PRESENT;
bls.additional_status = 0;
#endif /* CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT */
}
static void indicate_cb(struct bt_conn *conn, struct bt_gatt_indicate_params *params, uint8_t err)
{
if (err != 0) {
LOG_DBG("Indication failed with error %d\n", err);
} else {
LOG_DBG("Indication sent successfully\n");
}
}
static void bt_bas_bls_update_battery_level_status(void)
{
int err;
const struct bt_gatt_attr *attr = bt_bas_get_bas_attr(BT_BAS_IDX_BATT_LVL_STATUS_CHAR_VAL);
if (attr) {
const struct bt_bas_bls le_battery_level_status = {
.flags = bls.flags,
.power_state = sys_cpu_to_le16(bls.power_state),
#if defined(CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT)
.identifier = sys_cpu_to_le16(bls.identifier),
#endif
#if defined(CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT)
.battery_level = bls.battery_level,
#endif
#if defined(CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT)
.additional_status = bls.additional_status,
#endif
};
/* Indicate all connections */
ind_params.attr = attr;
ind_params.data = &le_battery_level_status;
ind_params.len = sizeof(le_battery_level_status);
ind_params.func = indicate_cb;
err = bt_gatt_indicate(NULL, &ind_params);
if (err) {
LOG_DBG("Failed to send ind to all connections (err %d)\n", err);
}
/* Notify all connections */
err = bt_gatt_notify(NULL, attr, &le_battery_level_status,
sizeof(le_battery_level_status));
if (err) {
LOG_DBG("Failed to send ntf to all connections (err %d)\n", err);
}
}
}
ssize_t bt_bas_bls_read_blvl_status(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
const struct bt_bas_bls le_battery_level_status = {
.flags = bls.flags,
.power_state = sys_cpu_to_le16(bls.power_state),
#if defined(CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT)
.identifier = sys_cpu_to_le16(bls.identifier),
#endif
#if defined(CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT)
.battery_level = bls.battery_level,
#endif
#if defined(CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT)
.additional_status = bls.additional_status,
#endif
};
return bt_gatt_attr_read(conn, attr, buf, len, offset, &le_battery_level_status,
sizeof(le_battery_level_status));
}
void bt_bas_bls_set_battery_present(enum bt_bas_bls_battery_present present)
{
bls.power_state &= ~BATTERY_MASK;
bls.power_state |= (present << BATTERY_SHIFT) & BATTERY_MASK;
bt_bas_bls_update_battery_level_status();
}
void bt_bas_bls_set_wired_external_power_source(enum bt_bas_bls_wired_power_source source)
{
bls.power_state &= ~WIRED_POWER_MASK;
bls.power_state |= (source << WIRED_POWER_SHIFT) & WIRED_POWER_MASK;
bt_bas_bls_update_battery_level_status();
}
void bt_bas_bls_set_wireless_external_power_source(enum bt_bas_bls_wireless_power_source source)
{
bls.power_state &= ~WIRELESS_POWER_MASK;
bls.power_state |= (source << WIRELESS_POWER_SHIFT) & WIRELESS_POWER_MASK;
bt_bas_bls_update_battery_level_status();
}
void bt_bas_bls_set_battery_charge_state(enum bt_bas_bls_battery_charge_state state)
{
bls.power_state &= ~BATTERY_CHARGE_STATE_MASK;
bls.power_state |= (state << BATTERY_CHARGE_STATE_SHIFT) & BATTERY_CHARGE_STATE_MASK;
bt_bas_bls_update_battery_level_status();
}
void bt_bas_bls_set_battery_charge_level(enum bt_bas_bls_battery_charge_level level)
{
bls.power_state &= ~BATTERY_CHARGE_LEVEL_MASK;
bls.power_state |= (level << BATTERY_CHARGE_LEVEL_SHIFT) & BATTERY_CHARGE_LEVEL_MASK;
bt_bas_bls_update_battery_level_status();
if (IS_ENABLED(CONFIG_BT_BAS_BCS)) {
/*
* Set the BCS Critical Power State bit as per BAS 1.1 specification
* section 3.4.1.1: The BCS Critical Power State bit should be set to true if the
* Battery Charge Level is set to Critical in the Power State field.
*/
if (level == BT_BAS_BLS_CHARGE_LEVEL_CRITICAL) {
bt_bas_bcs_set_battery_critical_state(true);
return;
} else if (level != BT_BAS_BLS_CHARGE_LEVEL_UNKNOWN) {
bt_bas_bcs_set_battery_critical_state(false);
}
}
}
void bt_bas_bls_set_battery_charge_type(enum bt_bas_bls_battery_charge_type type)
{
bls.power_state &= ~BATTERY_CHARGE_TYPE_MASK;
bls.power_state |= (type << BATTERY_CHARGE_TYPE_SHIFT) & BATTERY_CHARGE_TYPE_MASK;
bt_bas_bls_update_battery_level_status();
}
void bt_bas_bls_set_charging_fault_reason(enum bt_bas_bls_charging_fault_reason reason)
{
bls.power_state &= ~CHARGING_FAULT_MASK;
bls.power_state |= (reason << CHARGING_FAULT_SHIFT) & CHARGING_FAULT_MASK;
bt_bas_bls_update_battery_level_status();
}
#if defined(CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT)
void bt_bas_bls_set_battery_level(uint8_t level)
{
bls.battery_level = level;
bt_bas_bls_update_battery_level_status();
}
#endif /* CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT */
#if defined(CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT)
void bt_bas_bls_set_identifier(uint16_t identifier)
{
bls.identifier = sys_cpu_to_le16(identifier);
bt_bas_bls_update_battery_level_status();
}
#endif /* CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT */
#if defined(CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT)
void bt_bas_bls_set_service_required(enum bt_bas_bls_service_required value)
{
bls.additional_status &= ~SERVICE_REQUIRED_MASK;
bls.additional_status |= (value << SERVICE_REQUIRED_SHIFT) & SERVICE_REQUIRED_MASK;
bt_bas_bls_update_battery_level_status();
if (IS_ENABLED(CONFIG_BT_BAS_BCS)) {
/*
* Set the BCS Immediate Service Required bit as per BAS 1.1 specification
* section 3.4.1.1: The BCS Immediate Service Required bit should be set to true if
* the service Required bit is set to true in the Additional Status field.
*/
if (value == BT_BAS_BLS_SERVICE_REQUIRED_TRUE) {
bt_bas_bcs_set_immediate_service_required(true);
return;
} else if (value != BT_BAS_BLS_SERVICE_REQUIRED_UNKNOWN) {
bt_bas_bcs_set_immediate_service_required(false);
}
}
}
void bt_bas_bls_set_battery_fault(enum bt_bas_bls_battery_fault value)
{
bls.additional_status &= ~BATTERY_FAULT_MASK;
bls.additional_status |= (value << BATTERY_FAULT_SHIFT) & BATTERY_FAULT_MASK;
bt_bas_bls_update_battery_level_status();
}
#endif /* CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT */