1 /*
2  * Copyright (c) 2024 Demant A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/sys/byteorder.h>
8 #include <zephyr/bluetooth/services/bas.h>
9 #include <zephyr/bluetooth/gatt.h>
10 #include "bas_internal.h"
11 
12 #include <zephyr/logging/log.h>
13 LOG_MODULE_DECLARE(bas, CONFIG_BT_BAS_LOG_LEVEL);
14 
15 /* The battery level status of a battery. */
16 static struct bt_bas_bls bls;
17 
18 #define BT_BAS_IDX_BATT_LVL_STATUS_CHAR_VAL 6
19 
20 /* Notify/Indicate all connections */
21 static struct bt_gatt_indicate_params ind_params;
22 
23 /*
24  * Bitfield structure: Power State
25  *
26  * - Bits 0: Battery Present
27  * - Bits 1–2: Wired External Power Source Connected
28  * - Bits 3–4: Wireless External Power Source Connected
29  * - Bits 5–6: Battery Charge State
30  * - Bits 7–8: Battery Charge Level
31  * - Bits 9–11: Charging Type
32  * - Bits 12–14: Charging Fault Reason
33  * - Bit 15: RFU
34  *
35  * For detailed specification, refer to:
36  * https://bitbucket.org/bluetooth-SIG/public/src/main/gss/
37  * org.bluetooth.characteristic.battery_level_status.yaml
38  */
39 
40 #define BATTERY_SHIFT              0
41 #define WIRED_POWER_SHIFT          1
42 #define WIRELESS_POWER_SHIFT       3
43 #define BATTERY_CHARGE_STATE_SHIFT 5
44 #define BATTERY_CHARGE_LEVEL_SHIFT 7
45 #define BATTERY_CHARGE_TYPE_SHIFT  9
46 #define CHARGING_FAULT_SHIFT       12
47 
48 #define BATTERY_MASK              (BIT_MASK(1) << BATTERY_SHIFT)
49 #define WIRED_POWER_MASK          (BIT_MASK(2) << WIRED_POWER_SHIFT)
50 #define WIRELESS_POWER_MASK       (BIT_MASK(2) << WIRELESS_POWER_SHIFT)
51 #define BATTERY_CHARGE_STATE_MASK (BIT_MASK(2) << BATTERY_CHARGE_STATE_SHIFT)
52 #define BATTERY_CHARGE_LEVEL_MASK (BIT_MASK(2) << BATTERY_CHARGE_LEVEL_SHIFT)
53 #define BATTERY_CHARGE_TYPE_MASK  (BIT_MASK(3) << BATTERY_CHARGE_TYPE_SHIFT)
54 #define CHARGING_FAULT_MASK       (BIT_MASK(3) << CHARGING_FAULT_SHIFT)
55 
56 /*
57  * Bitfield structure: Additional Status
58  *
59  * - Bits 0–1: Service Required
60  * - Bit 2: Battery Fault
61  * - Bits 3–7: Reserved
62  */
63 #define SERVICE_REQUIRED_SHIFT 0
64 #define BATTERY_FAULT_SHIFT    2
65 
66 #define SERVICE_REQUIRED_MASK (BIT_MASK(2) << SERVICE_REQUIRED_SHIFT)
67 #define BATTERY_FAULT_MASK    (BIT_MASK(1) << BATTERY_FAULT_SHIFT)
68 
bt_bas_bls_init(void)69 void bt_bas_bls_init(void)
70 {
71 	LOG_DBG("Initialise BAS Battery Level Status Module");
72 
73 	bls.flags = 0;
74 	bls.power_state = 0;
75 
76 #if defined(CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT)
77 	/* Set identifier flag */
78 	bls.flags |= BT_BAS_BLS_FLAG_IDENTIFIER_PRESENT;
79 	bls.identifier = 0;
80 #endif /* CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT */
81 
82 #if defined(CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT)
83 	/* Set battery level flag */
84 	bls.flags |= BT_BAS_BLS_FLAG_BATTERY_LEVEL_PRESENT;
85 	bls.battery_level = bt_bas_get_battery_level();
86 #endif /* CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT */
87 
88 #if defined(CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT)
89 	/* Set additional status flag */
90 	bls.flags |= BT_BAS_BLS_FLAG_ADDITIONAL_STATUS_PRESENT;
91 	bls.additional_status = 0;
92 #endif /* CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT */
93 }
94 
indicate_cb(struct bt_conn * conn,struct bt_gatt_indicate_params * params,uint8_t err)95 static void indicate_cb(struct bt_conn *conn, struct bt_gatt_indicate_params *params, uint8_t err)
96 {
97 	if (err != 0) {
98 		LOG_DBG("Indication failed with error %d\n", err);
99 	} else {
100 		LOG_DBG("Indication sent successfully\n");
101 	}
102 }
103 
bt_bas_bls_update_battery_level_status(void)104 static void bt_bas_bls_update_battery_level_status(void)
105 {
106 	int err;
107 	const struct bt_gatt_attr *attr = bt_bas_get_bas_attr(BT_BAS_IDX_BATT_LVL_STATUS_CHAR_VAL);
108 
109 	if (attr) {
110 		const struct bt_bas_bls le_battery_level_status = {
111 			.flags = bls.flags,
112 			.power_state = sys_cpu_to_le16(bls.power_state),
113 #if defined(CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT)
114 			.identifier = sys_cpu_to_le16(bls.identifier),
115 #endif
116 #if defined(CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT)
117 			.battery_level = bls.battery_level,
118 #endif
119 #if defined(CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT)
120 			.additional_status = bls.additional_status,
121 #endif
122 		};
123 
124 		/* Indicate all connections */
125 		ind_params.attr = attr;
126 		ind_params.data = &le_battery_level_status;
127 		ind_params.len = sizeof(le_battery_level_status);
128 		ind_params.func = indicate_cb;
129 		err = bt_gatt_indicate(NULL, &ind_params);
130 		if (err) {
131 			LOG_DBG("Failed to send ind to all connections (err %d)\n", err);
132 		}
133 
134 		/* Notify all connections */
135 		err = bt_gatt_notify(NULL, attr, &le_battery_level_status,
136 				     sizeof(le_battery_level_status));
137 		if (err) {
138 			LOG_DBG("Failed to send ntf to all connections (err %d)\n", err);
139 		}
140 	}
141 }
142 
bt_bas_bls_read_blvl_status(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)143 ssize_t bt_bas_bls_read_blvl_status(struct bt_conn *conn, const struct bt_gatt_attr *attr,
144 				    void *buf, uint16_t len, uint16_t offset)
145 {
146 	const struct bt_bas_bls le_battery_level_status = {
147 		.flags = bls.flags,
148 		.power_state = sys_cpu_to_le16(bls.power_state),
149 #if defined(CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT)
150 		.identifier = sys_cpu_to_le16(bls.identifier),
151 #endif
152 #if defined(CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT)
153 		.battery_level = bls.battery_level,
154 #endif
155 #if defined(CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT)
156 		.additional_status = bls.additional_status,
157 #endif
158 	};
159 
160 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &le_battery_level_status,
161 				 sizeof(le_battery_level_status));
162 }
163 
bt_bas_bls_set_battery_present(enum bt_bas_bls_battery_present present)164 void bt_bas_bls_set_battery_present(enum bt_bas_bls_battery_present present)
165 {
166 	bls.power_state &= ~BATTERY_MASK;
167 	bls.power_state |= (present << BATTERY_SHIFT) & BATTERY_MASK;
168 	bt_bas_bls_update_battery_level_status();
169 }
170 
bt_bas_bls_set_wired_external_power_source(enum bt_bas_bls_wired_power_source source)171 void bt_bas_bls_set_wired_external_power_source(enum bt_bas_bls_wired_power_source source)
172 {
173 	bls.power_state &= ~WIRED_POWER_MASK;
174 	bls.power_state |= (source << WIRED_POWER_SHIFT) & WIRED_POWER_MASK;
175 	bt_bas_bls_update_battery_level_status();
176 }
177 
bt_bas_bls_set_wireless_external_power_source(enum bt_bas_bls_wireless_power_source source)178 void bt_bas_bls_set_wireless_external_power_source(enum bt_bas_bls_wireless_power_source source)
179 {
180 	bls.power_state &= ~WIRELESS_POWER_MASK;
181 	bls.power_state |= (source << WIRELESS_POWER_SHIFT) & WIRELESS_POWER_MASK;
182 	bt_bas_bls_update_battery_level_status();
183 }
184 
bt_bas_bls_set_battery_charge_state(enum bt_bas_bls_battery_charge_state state)185 void bt_bas_bls_set_battery_charge_state(enum bt_bas_bls_battery_charge_state state)
186 {
187 	bls.power_state &= ~BATTERY_CHARGE_STATE_MASK;
188 	bls.power_state |= (state << BATTERY_CHARGE_STATE_SHIFT) & BATTERY_CHARGE_STATE_MASK;
189 	bt_bas_bls_update_battery_level_status();
190 }
191 
bt_bas_bls_set_battery_charge_level(enum bt_bas_bls_battery_charge_level level)192 void bt_bas_bls_set_battery_charge_level(enum bt_bas_bls_battery_charge_level level)
193 {
194 	bls.power_state &= ~BATTERY_CHARGE_LEVEL_MASK;
195 	bls.power_state |= (level << BATTERY_CHARGE_LEVEL_SHIFT) & BATTERY_CHARGE_LEVEL_MASK;
196 	bt_bas_bls_update_battery_level_status();
197 
198 	if (IS_ENABLED(CONFIG_BT_BAS_BCS)) {
199 		/*
200 		 * Set the BCS Critical Power State bit as per BAS 1.1 specification
201 		 * section 3.4.1.1: The BCS Critical Power State bit should be set to true if the
202 		 * Battery Charge Level is set to Critical in the Power State field.
203 		 */
204 		if (level == BT_BAS_BLS_CHARGE_LEVEL_CRITICAL) {
205 			bt_bas_bcs_set_battery_critical_state(true);
206 			return;
207 		} else if (level != BT_BAS_BLS_CHARGE_LEVEL_UNKNOWN) {
208 			bt_bas_bcs_set_battery_critical_state(false);
209 		}
210 	}
211 }
212 
bt_bas_bls_set_battery_charge_type(enum bt_bas_bls_battery_charge_type type)213 void bt_bas_bls_set_battery_charge_type(enum bt_bas_bls_battery_charge_type type)
214 {
215 	bls.power_state &= ~BATTERY_CHARGE_TYPE_MASK;
216 	bls.power_state |= (type << BATTERY_CHARGE_TYPE_SHIFT) & BATTERY_CHARGE_TYPE_MASK;
217 	bt_bas_bls_update_battery_level_status();
218 }
219 
bt_bas_bls_set_charging_fault_reason(enum bt_bas_bls_charging_fault_reason reason)220 void bt_bas_bls_set_charging_fault_reason(enum bt_bas_bls_charging_fault_reason reason)
221 {
222 	bls.power_state &= ~CHARGING_FAULT_MASK;
223 	bls.power_state |= (reason << CHARGING_FAULT_SHIFT) & CHARGING_FAULT_MASK;
224 	bt_bas_bls_update_battery_level_status();
225 }
226 
227 #if defined(CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT)
bt_bas_bls_set_battery_level(uint8_t level)228 void bt_bas_bls_set_battery_level(uint8_t level)
229 {
230 	bls.battery_level = level;
231 	bt_bas_bls_update_battery_level_status();
232 }
233 #endif /* CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT */
234 
235 #if defined(CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT)
bt_bas_bls_set_identifier(uint16_t identifier)236 void bt_bas_bls_set_identifier(uint16_t identifier)
237 {
238 	bls.identifier = sys_cpu_to_le16(identifier);
239 	bt_bas_bls_update_battery_level_status();
240 }
241 #endif /* CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT */
242 
243 #if defined(CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT)
bt_bas_bls_set_service_required(enum bt_bas_bls_service_required value)244 void bt_bas_bls_set_service_required(enum bt_bas_bls_service_required value)
245 {
246 	bls.additional_status &= ~SERVICE_REQUIRED_MASK;
247 	bls.additional_status |= (value << SERVICE_REQUIRED_SHIFT) & SERVICE_REQUIRED_MASK;
248 	bt_bas_bls_update_battery_level_status();
249 
250 	if (IS_ENABLED(CONFIG_BT_BAS_BCS)) {
251 		/*
252 		 * Set the BCS Immediate Service Required bit as per BAS 1.1 specification
253 		 * section 3.4.1.1: The BCS Immediate Service Required bit should be set to true if
254 		 * the service Required bit is set to true in the Additional Status field.
255 		 */
256 		if (value == BT_BAS_BLS_SERVICE_REQUIRED_TRUE) {
257 			bt_bas_bcs_set_immediate_service_required(true);
258 			return;
259 		} else if (value != BT_BAS_BLS_SERVICE_REQUIRED_UNKNOWN) {
260 			bt_bas_bcs_set_immediate_service_required(false);
261 		}
262 	}
263 }
264 
bt_bas_bls_set_battery_fault(enum bt_bas_bls_battery_fault value)265 void bt_bas_bls_set_battery_fault(enum bt_bas_bls_battery_fault value)
266 {
267 	bls.additional_status &= ~BATTERY_FAULT_MASK;
268 	bls.additional_status |= (value << BATTERY_FAULT_SHIFT) & BATTERY_FAULT_MASK;
269 	bt_bas_bls_update_battery_level_status();
270 }
271 #endif /* CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT */
272