1 /*
2  * Copyright (c) 2024 Demant A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/bluetooth/services/bas.h>
8 #include <zephyr/bluetooth/gatt.h>
9 #include "bas_internal.h"
10 
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_DECLARE(bas, CONFIG_BT_BAS_LOG_LEVEL);
13 
14 #define BATTERY_CRITICAL_STATUS_CHAR_IDX 9
15 
16 static uint8_t battery_critical_status;
17 
bt_bas_bcs_ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)18 void bt_bas_bcs_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
19 {
20 	ARG_UNUSED(attr);
21 
22 	bool ind_enabled = (value == BT_GATT_CCC_INDICATE);
23 
24 	LOG_DBG("BAS Critical status Indication %s", ind_enabled ? "enabled" : "disabled");
25 }
26 
bt_bas_bcs_read_critical_status(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)27 ssize_t bt_bas_bcs_read_critical_status(struct bt_conn *conn, const struct bt_gatt_attr *attr,
28 					void *buf, uint16_t len, uint16_t offset)
29 {
30 
31 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &battery_critical_status,
32 				 sizeof(uint8_t));
33 }
34 
bcs_indicate_cb(struct bt_conn * conn,struct bt_gatt_indicate_params * params,uint8_t err)35 static void bcs_indicate_cb(struct bt_conn *conn, struct bt_gatt_indicate_params *params,
36 			    uint8_t err)
37 {
38 	if (err != 0) {
39 		LOG_DBG("BCS Indication failed with error %u\n", err);
40 	} else {
41 		LOG_DBG("BCS Indication sent successfully\n");
42 	}
43 }
44 
bt_bas_bcs_update_battery_critical_status(void)45 static void bt_bas_bcs_update_battery_critical_status(void)
46 {
47 	/* Indicate all connections */
48 	const struct bt_gatt_attr *attr = bt_bas_get_bas_attr(BATTERY_CRITICAL_STATUS_CHAR_IDX);
49 
50 	if (attr) {
51 		uint8_t err;
52 		/* Indicate battery critical status to all connections */
53 		static struct bt_gatt_indicate_params bcs_ind_params;
54 
55 		bcs_ind_params.attr = attr;
56 		bcs_ind_params.data = &battery_critical_status;
57 		bcs_ind_params.len = sizeof(battery_critical_status);
58 		bcs_ind_params.func = bcs_indicate_cb;
59 		err = bt_gatt_indicate(NULL, &bcs_ind_params);
60 		if (err && err != -ENOTCONN) {
61 			LOG_DBG("Failed to send critical status ind to all conns (err %d)\n", err);
62 		}
63 	}
64 }
65 
bt_bas_bcs_set_battery_critical_state(bool critical_state)66 void bt_bas_bcs_set_battery_critical_state(bool critical_state)
67 {
68 	bool current_state = (battery_critical_status & BT_BAS_BCS_BATTERY_CRITICAL_STATE) != 0;
69 
70 	if (current_state == critical_state) {
71 		LOG_DBG("Already battery_critical_state is %d\n", critical_state);
72 		return;
73 	}
74 
75 	if (critical_state) {
76 		battery_critical_status |= BT_BAS_BCS_BATTERY_CRITICAL_STATE;
77 	} else {
78 		battery_critical_status &= ~BT_BAS_BCS_BATTERY_CRITICAL_STATE;
79 	}
80 	bt_bas_bcs_update_battery_critical_status();
81 }
82 
bt_bas_bcs_set_immediate_service_required(bool service_required)83 void bt_bas_bcs_set_immediate_service_required(bool service_required)
84 {
85 	bool current_state = (battery_critical_status & BT_BAS_BCS_IMMEDIATE_SERVICE_REQUIRED) != 0;
86 
87 	if (current_state == service_required) {
88 		LOG_DBG("Already immediate_service_required is %d\n", service_required);
89 		return;
90 	}
91 
92 	if (service_required) {
93 		battery_critical_status |= BT_BAS_BCS_IMMEDIATE_SERVICE_REQUIRED;
94 	} else {
95 		battery_critical_status &= ~BT_BAS_BCS_IMMEDIATE_SERVICE_REQUIRED;
96 	}
97 	bt_bas_bcs_update_battery_critical_status();
98 }
99