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>
This commit is contained in:
Nithin Ramesh Myliattil 2024-10-17 09:14:27 +02:00 committed by Alberto Escolar
commit b717488be2
10 changed files with 334 additions and 37 deletions

View file

@ -6,9 +6,10 @@ CONFIG_BT_SMP=y
CONFIG_BT_BAS=y
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_DEVICE_NAME="bsim_bas"
CONFIG_BT_ATT_TX_COUNT=5
CONFIG_BT_ATT_TX_COUNT=10
CONFIG_BT_BAS_BLS=y
CONFIG_BT_BAS_BCS=y
CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT=y
CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT=y
CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT=y

View file

@ -43,6 +43,7 @@ static struct bt_gatt_discover_params discover_params;
static struct bt_gatt_subscribe_params battery_level_notify_params;
static struct bt_gatt_subscribe_params battery_level_status_sub_params;
static struct bt_gatt_subscribe_params battery_critical_status_sub_params;
/*
* Battery Service test:
@ -58,6 +59,56 @@ static struct bt_gatt_subscribe_params battery_level_status_sub_params;
static DEFINE_FLAG(notification_count_reached);
static DEFINE_FLAG(indication_count_reached);
static DEFINE_FLAG(bcs_char_read);
/* Callback for handling Battery Critical Status Read Response */
static uint8_t battery_critical_status_read_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params, const void *data,
uint16_t length)
{
TEST_ASSERT(err == 0, "Failed to read Battery critical status (err %d)", err);
TEST_ASSERT(length > 0, "No data is sent");
if (data) {
uint8_t status_byte = *(uint8_t *)data;
printk("[READ] BAS Critical Status:\n");
printk("Battery state: %s\n",
(status_byte & BT_BAS_BCS_BATTERY_CRITICAL_STATE) ? "Critical" : "Normal");
printk("Immediate service: %s\n",
(status_byte & BT_BAS_BCS_IMMEDIATE_SERVICE_REQUIRED) ? "Required"
: "Not Required");
}
SET_FLAG(bcs_char_read);
return BT_GATT_ITER_STOP;
}
static struct bt_gatt_read_params read_bcs_params = {
.func = battery_critical_status_read_cb,
.by_uuid.uuid = BT_UUID_BAS_BATTERY_CRIT_STATUS,
.by_uuid.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE,
.by_uuid.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE,
};
/* Callback for handling Battery Level Read Response */
static uint8_t battery_level_read_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params, const void *data,
uint16_t length)
{
TEST_ASSERT(err == 0, "Failed to read Battery Level (err %d)", err);
if (data) {
LOG_DBG("[READ] BAS Battery Level: %d%%\n", *(const uint8_t *)data);
}
return BT_GATT_ITER_STOP;
}
static struct bt_gatt_read_params read_blvl_params = {
.func = battery_level_read_cb,
.by_uuid.uuid = BT_UUID_BAS_BATTERY_LEVEL_STATUS,
.by_uuid.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE,
.by_uuid.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE,
};
extern enum bst_result_t bst_result;
@ -91,19 +142,6 @@ static uint8_t battery_level_notify_cb(struct bt_conn *conn,
return BT_GATT_ITER_CONTINUE;
}
/* Callback for handling Battery Level Read Response */
static uint8_t battery_level_read_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params, const void *data,
uint16_t length)
{
TEST_ASSERT(err == 0, "Failed to read Battery Level (err %d)", err);
if (data) {
LOG_DBG("[READ] BAS Battery Level: %d%%\n", *(const uint8_t *)data);
}
return BT_GATT_ITER_STOP;
}
static bool parse_battery_level_status(const uint8_t *data, uint16_t length)
{
/* Check minimum length for parsing flags and power state */
@ -311,6 +349,25 @@ static bool parse_battery_level_status(const uint8_t *data, uint16_t length)
return true;
}
static unsigned char battery_critical_status_indicate_cb(struct bt_conn *conn,
struct bt_gatt_subscribe_params *params,
const void *data, uint16_t length)
{
if (!data) {
LOG_INF("BAS critical status indication disabled\n");
} else {
uint8_t status_byte = ((uint8_t *)data)[0];
printk("[INDICATION] BAS Critical Status:\n");
printk("Battery state: %s\n",
(status_byte & BT_BAS_BCS_BATTERY_CRITICAL_STATE) ? "Critical" : "Normal");
printk("Immediate service: %s\n",
(status_byte & BT_BAS_BCS_IMMEDIATE_SERVICE_REQUIRED) ? "Required"
: "Not Required");
}
return BT_GATT_ITER_CONTINUE;
}
static unsigned char battery_level_status_indicate_cb(struct bt_conn *conn,
struct bt_gatt_subscribe_params *params,
const void *data, uint16_t length)
@ -367,27 +424,13 @@ static uint8_t battery_level_status_notify_cb(struct bt_conn *conn,
return BT_GATT_ITER_CONTINUE;
}
static void read_battery_level(const struct bt_gatt_attr *attr)
{
/* Read the battery level after subscribing */
static struct bt_gatt_read_params read_params;
read_params.func = battery_level_read_cb;
read_params.handle_count = 1;
read_params.single.handle = bt_gatt_attr_get_handle(attr);
read_params.single.offset = 0;
bt_gatt_read(default_conn, &read_params);
}
static void subscribe_battery_level(const struct bt_gatt_attr *attr)
{
int err;
struct bt_gatt_attr *ccc_attr = bt_gatt_find_by_uuid(attr, 0, BT_UUID_GATT_CCC);
battery_level_notify_params = (struct bt_gatt_subscribe_params){
/* In Zephyr, it is common practice for the CCC handle
* to be positioned two handles after the characteristic handle.
*/
.ccc_handle = bt_gatt_attr_get_handle(attr) + 2,
.ccc_handle = bt_gatt_attr_get_handle(ccc_attr),
.value_handle = bt_gatt_attr_value_handle(attr),
.value = BT_GATT_CCC_NOTIFY,
.notify = battery_level_notify_cb,
@ -399,26 +442,53 @@ static void subscribe_battery_level(const struct bt_gatt_attr *attr)
} else {
LOG_DBG("Battery level [SUBSCRIBED]\n");
}
read_battery_level(attr);
err = bt_gatt_read(default_conn, &read_blvl_params);
if (err != 0) {
TEST_FAIL("Battery Level Read failed (err %d)\n", err);
}
}
static void subscribe_battery_critical_status(const struct bt_gatt_attr *attr)
{
int err;
struct bt_gatt_attr *ccc_attr = bt_gatt_find_by_uuid(attr, 0, BT_UUID_GATT_CCC);
battery_critical_status_sub_params = (struct bt_gatt_subscribe_params){
.ccc_handle = bt_gatt_attr_get_handle(ccc_attr),
.value_handle = bt_gatt_attr_value_handle(attr),
.value = BT_GATT_CCC_INDICATE,
.notify = battery_critical_status_indicate_cb,
};
err = bt_gatt_subscribe(default_conn, &battery_critical_status_sub_params);
if (err && err != -EALREADY) {
TEST_FAIL("Subscribe failed (err %d)\n", err);
} else {
LOG_DBG("Battery critical status [SUBSCRIBED]\n");
}
err = bt_gatt_read(default_conn, &read_bcs_params);
if (err != 0) {
TEST_FAIL("Battery Critical Status Read failed (err %d)\n", err);
}
}
static void subscribe_battery_level_status(const struct bt_gatt_attr *attr)
{
int err;
struct bt_gatt_attr *ccc_attr = bt_gatt_find_by_uuid(attr, 0, BT_UUID_GATT_CCC);
if (get_device_nbr() == 1) { /* One device for Indication */
battery_level_status_sub_params = (struct bt_gatt_subscribe_params){
/* In Zephyr, it is common practice for the CCC handle
* to be positioned two handles after the characteristic handle.
*/
.ccc_handle = bt_gatt_attr_get_handle(attr) + 2,
.ccc_handle = bt_gatt_attr_get_handle(ccc_attr),
.value_handle = bt_gatt_attr_value_handle(attr),
.value = BT_GATT_CCC_INDICATE,
.notify = battery_level_status_indicate_cb,
};
} else { /* Other device for Notification */
battery_level_status_sub_params = (struct bt_gatt_subscribe_params){
.ccc_handle = bt_gatt_attr_get_handle(attr) + 2,
.ccc_handle = bt_gatt_attr_get_handle(ccc_attr),
.value_handle = bt_gatt_attr_value_handle(attr),
.value = BT_GATT_CCC_NOTIFY,
.notify = battery_level_status_notify_cb,
@ -474,6 +544,19 @@ static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *at
} else if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_BAS_BATTERY_LEVEL_STATUS)) {
LOG_DBG("Subscribe Batterry Level Status Char\n");
subscribe_battery_level_status(attr);
memcpy(&uuid, BT_UUID_BAS_BATTERY_CRIT_STATUS, sizeof(uuid));
discover_params.uuid = &uuid.uuid;
discover_params.start_handle = attr->handle + 1;
discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
err = bt_gatt_discover(conn, &discover_params);
if (err) {
TEST_FAIL("Discover failed (err %d)\n", err);
}
} else if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_BAS_BATTERY_CRIT_STATUS)) {
LOG_DBG("Subscribe Batterry Critical Status Char\n");
subscribe_battery_critical_status(attr);
}
return BT_GATT_ITER_STOP;
}
@ -531,12 +614,26 @@ static void test_bas_central_main(void)
WAIT_FOR_FLAG(notification_count_reached);
LOG_INF("Notification Count Reached!");
}
/* bk_sync_send only works between two devices in a simulation, with IDs 0 and 1. */
if (get_device_nbr() == 1) {
bk_sync_send();
}
printk("Read BCS once peripheral sets BLS Addl Status Service Required Flag to false\n");
UNSET_FLAG(bcs_char_read);
err = bt_gatt_read(default_conn, &read_bcs_params);
if (err != 0) {
TEST_FAIL("Battery Critical Status Read failed (err %d)\n", err);
}
WAIT_FOR_FLAG(bcs_char_read);
if (get_device_nbr() == 1) {
bk_sync_send();
}
bst_result = Passed;
TEST_PASS("Central Test Passed");
}

View file

@ -160,6 +160,14 @@ static void test_bas_peripheral_main(void)
/* Main thread waits for the sync signal from other device */
bk_sync_wait();
/*
* Once BLS Additional status service required flag is set to false,
* BCS Immediate service flag is also set to false. BCS char is
* read from central.
*/
bt_bas_bls_set_service_required(BT_BAS_BLS_SERVICE_REQUIRED_FALSE);
bk_sync_wait();
bst_result = Passed;
TEST_PASS_AND_EXIT("Peripheral Test Passed");
}