1 /*  Bluetooth AICS client */
2 
3 /*
4  * Copyright (c) 2020 Bose Corporation
5  * Copyright (c) 2020 Nordic Semiconductor ASA
6  *
7  * SPDX-License-Identifier: Apache-2.0
8  */
9 
10 #include <errno.h>
11 #include <stdbool.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <string.h>
15 
16 #include <zephyr/autoconf.h>
17 #include <zephyr/bluetooth/audio/aics.h>
18 #include <zephyr/bluetooth/att.h>
19 #include <zephyr/bluetooth/bluetooth.h>
20 #include <zephyr/bluetooth/l2cap.h>
21 #include <zephyr/bluetooth/conn.h>
22 #include <zephyr/bluetooth/gatt.h>
23 #include <zephyr/bluetooth/uuid.h>
24 #include <zephyr/device.h>
25 #include <zephyr/kernel.h>
26 #include <zephyr/logging/log.h>
27 #include <zephyr/init.h>
28 #include <zephyr/sys/__assert.h>
29 #include <zephyr/sys/atomic.h>
30 #include <zephyr/sys/check.h>
31 #include <zephyr/sys/util.h>
32 #include <zephyr/types.h>
33 
34 #include "aics_internal.h"
35 #include "common/bt_str.h"
36 
37 LOG_MODULE_REGISTER(bt_aics_client, CONFIG_BT_AICS_CLIENT_LOG_LEVEL);
38 
39 static struct bt_aics aics_insts[CONFIG_BT_MAX_CONN * CONFIG_BT_AICS_CLIENT_MAX_INSTANCE_COUNT];
40 
41 static int aics_client_common_control(uint8_t opcode, struct bt_aics *inst);
42 
lookup_aics_by_handle(struct bt_conn * conn,uint16_t handle)43 static struct bt_aics *lookup_aics_by_handle(struct bt_conn *conn, uint16_t handle)
44 {
45 	for (int i = 0; i < ARRAY_SIZE(aics_insts); i++) {
46 		if (aics_insts[i].cli.conn == conn &&
47 		    atomic_test_bit(aics_insts[i].cli.flags, BT_AICS_CLIENT_FLAG_ACTIVE) &&
48 		    aics_insts[i].cli.start_handle <= handle &&
49 		    aics_insts[i].cli.end_handle >= handle) {
50 			return &aics_insts[i];
51 		}
52 	}
53 
54 	LOG_DBG("Could not find AICS instance with handle 0x%04x", handle);
55 
56 	return NULL;
57 }
58 
aics_client_notify_handler(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)59 uint8_t aics_client_notify_handler(struct bt_conn *conn, struct bt_gatt_subscribe_params *params,
60 				   const void *data, uint16_t length)
61 {
62 	uint16_t handle = params->value_handle;
63 	struct bt_aics *inst;
64 	const struct bt_aics_state *state;
65 	const uint8_t *status;
66 
67 	if (conn == NULL) {
68 		return BT_GATT_ITER_CONTINUE;
69 	}
70 
71 	inst = lookup_aics_by_handle(conn, handle);
72 
73 	if (!inst) {
74 		LOG_DBG("Instance not found");
75 		return BT_GATT_ITER_STOP;
76 	}
77 
78 	if (!data || !length) {
79 		return BT_GATT_ITER_CONTINUE;
80 	}
81 
82 	if (handle == inst->cli.state_handle) {
83 		if (length == sizeof(*state)) {
84 			state = (const struct bt_aics_state *)data;
85 			LOG_DBG("Inst %p: Gain %d, mute %u, gain_mode %u, counter %u", inst,
86 				state->gain, state->mute, state->gain_mode, state->change_counter);
87 
88 			inst->cli.change_counter = state->change_counter;
89 
90 			if (inst->cli.cb && inst->cli.cb->state) {
91 				inst->cli.cb->state(inst, 0, state->gain,
92 						    state->mute,
93 						    state->gain_mode);
94 			}
95 		}
96 	} else if (handle == inst->cli.status_handle) {
97 		if (length == sizeof(*status)) {
98 			status = (const uint8_t *)data;
99 			LOG_DBG("Inst %p: Status %u", inst, *status);
100 			if (inst->cli.cb && inst->cli.cb->status) {
101 				inst->cli.cb->status(inst, 0, *status);
102 			}
103 		}
104 	} else if (handle == inst->cli.desc_handle) {
105 		char desc[MIN(BT_L2CAP_RX_MTU, BT_ATT_MAX_ATTRIBUTE_LEN) + 1];
106 
107 		/* Truncate if too large */
108 		if (length > sizeof(desc) - 1) {
109 			LOG_DBG("Description truncated from %u to %zu octets", length,
110 				sizeof(desc) - 1);
111 		}
112 		length = MIN(sizeof(desc) - 1, length);
113 
114 		memcpy(desc, data, length);
115 		desc[length] = '\0';
116 		LOG_DBG("Inst %p: Input description: %s", inst, desc);
117 		if (inst->cli.cb && inst->cli.cb->description) {
118 			inst->cli.cb->description(inst, 0, desc);
119 		}
120 	} else {
121 		LOG_DBG("Receive notification on unexpected handle 0x%04X", handle);
122 	}
123 
124 	return BT_GATT_ITER_CONTINUE;
125 }
126 
aics_client_read_state_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)127 static uint8_t aics_client_read_state_cb(struct bt_conn *conn, uint8_t err,
128 					 struct bt_gatt_read_params *params,
129 					 const void *data, uint16_t length)
130 {
131 	int cb_err = err;
132 	struct bt_aics *inst = lookup_aics_by_handle(conn, params->single.handle);
133 	const struct bt_aics_state *state = (const struct bt_aics_state *)data;
134 
135 	memset(params, 0, sizeof(*params));
136 
137 	if (!inst) {
138 		LOG_DBG("Instance not found");
139 		return BT_GATT_ITER_STOP;
140 	}
141 
142 	LOG_DBG("Inst %p: err: 0x%02X", inst, err);
143 	atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
144 
145 	if (cb_err) {
146 		LOG_DBG("State read failed: %d", err);
147 		if (inst->cli.cb && inst->cli.cb->state) {
148 			inst->cli.cb->state(inst, cb_err, 0, 0, 0);
149 		}
150 		return BT_GATT_ITER_STOP;
151 	}
152 
153 	if (data) {
154 		if (length == sizeof(*state)) {
155 			LOG_DBG("Gain %d, mute %u, gain_mode %u, counter %u", state->gain,
156 				state->mute, state->gain_mode, state->change_counter);
157 
158 			inst->cli.change_counter = state->change_counter;
159 		} else {
160 			LOG_DBG("Invalid length %u (expected %zu)", length, sizeof(*state));
161 			cb_err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
162 		}
163 	} else {
164 		LOG_DBG("Invalid state");
165 		cb_err = BT_ATT_ERR_UNLIKELY;
166 	}
167 
168 	if (inst->cli.cb && inst->cli.cb->state) {
169 		inst->cli.cb->state(inst, cb_err, state->gain,
170 				    state->mute, state->gain_mode);
171 	}
172 
173 	return BT_GATT_ITER_STOP;
174 }
175 
aics_client_read_gain_settings_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)176 static uint8_t aics_client_read_gain_settings_cb(struct bt_conn *conn, uint8_t err,
177 						 struct bt_gatt_read_params *params,
178 						 const void *data, uint16_t length)
179 {
180 	int cb_err = err;
181 	struct bt_aics *inst = lookup_aics_by_handle(conn, params->single.handle);
182 	const struct bt_aics_gain_settings *gain_settings =
183 		(const struct bt_aics_gain_settings *)data;
184 
185 	memset(params, 0, sizeof(*params));
186 
187 	if (!inst) {
188 		LOG_DBG("Instance not found");
189 		return BT_GATT_ITER_STOP;
190 	}
191 
192 	LOG_DBG("Inst %p: err: 0x%02X", inst, err);
193 	atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
194 
195 	if (cb_err) {
196 		LOG_DBG("Gain settings read failed: %d", err);
197 		if (inst->cli.cb && inst->cli.cb->gain_setting) {
198 			inst->cli.cb->gain_setting(inst, cb_err, 0, 0, 0);
199 		}
200 		return BT_GATT_ITER_STOP;
201 	}
202 
203 	if (data) {
204 		if (length == sizeof(*gain_settings)) {
205 			LOG_DBG("Units %u, Max %d, Min %d", gain_settings->units,
206 				gain_settings->maximum, gain_settings->minimum);
207 		} else {
208 			LOG_DBG("Invalid length %u (expected %zu)", length, sizeof(*gain_settings));
209 			cb_err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
210 		}
211 	} else {
212 		LOG_DBG("Invalid gain settings");
213 		cb_err = BT_ATT_ERR_UNLIKELY;
214 	}
215 
216 	if (inst->cli.cb && inst->cli.cb->gain_setting) {
217 		inst->cli.cb->gain_setting(inst, cb_err, gain_settings->units,
218 					   gain_settings->minimum,
219 					   gain_settings->maximum);
220 	}
221 
222 	return BT_GATT_ITER_STOP;
223 }
224 
aics_client_read_type_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)225 static uint8_t aics_client_read_type_cb(struct bt_conn *conn, uint8_t err,
226 					struct bt_gatt_read_params *params,
227 					const void *data, uint16_t length)
228 {
229 	int cb_err = err;
230 	const uint8_t *type = (const uint8_t *)data;
231 	struct bt_aics *inst = lookup_aics_by_handle(conn, params->single.handle);
232 
233 	memset(params, 0, sizeof(*params));
234 
235 	if (!inst) {
236 		LOG_DBG("Instance not found");
237 		return BT_GATT_ITER_STOP;
238 	}
239 
240 	LOG_DBG("Inst %p: err: 0x%02X", inst, err);
241 	atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
242 
243 	if (cb_err) {
244 		LOG_DBG("Type read failed: %d", err);
245 		if (inst->cli.cb && inst->cli.cb->type) {
246 			inst->cli.cb->type(inst, cb_err, 0);
247 		}
248 		return BT_GATT_ITER_STOP;
249 	}
250 
251 	if (data) {
252 		if (length == sizeof(*type)) {
253 			LOG_DBG("Type %u", *type);
254 		} else {
255 			LOG_DBG("Invalid length %u (expected %zu)", length, sizeof(*type));
256 			cb_err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
257 		}
258 	} else {
259 		LOG_DBG("Invalid type");
260 		cb_err = BT_ATT_ERR_UNLIKELY;
261 	}
262 
263 	if (inst->cli.cb && inst->cli.cb->type) {
264 		inst->cli.cb->type(inst, cb_err, *type);
265 	}
266 
267 	return BT_GATT_ITER_STOP;
268 }
269 
aics_client_read_status_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)270 static uint8_t aics_client_read_status_cb(struct bt_conn *conn, uint8_t err,
271 					  struct bt_gatt_read_params *params,
272 					  const void *data, uint16_t length)
273 {
274 	int cb_err = err;
275 	const uint8_t *status = (const uint8_t *)data;
276 	struct bt_aics *inst = lookup_aics_by_handle(conn, params->single.handle);
277 
278 	memset(params, 0, sizeof(*params));
279 
280 	if (!inst) {
281 		LOG_DBG("Instance not found");
282 		return BT_GATT_ITER_STOP;
283 	}
284 
285 	LOG_DBG("Inst %p: err: 0x%02X", inst, err);
286 	atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
287 
288 	if (cb_err) {
289 		LOG_DBG("Status read failed: %d", err);
290 		if (inst->cli.cb && inst->cli.cb->status) {
291 			inst->cli.cb->status(inst, cb_err, 0);
292 		}
293 		return BT_GATT_ITER_STOP;
294 	}
295 
296 	if (data) {
297 		if (length == sizeof(*status)) {
298 			LOG_DBG("Status %u", *status);
299 		} else {
300 			LOG_DBG("Invalid length %u (expected %zu)", length, sizeof(*status));
301 			cb_err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
302 		}
303 	} else {
304 		LOG_DBG("Invalid status");
305 		cb_err = BT_ATT_ERR_UNLIKELY;
306 	}
307 
308 	if (inst->cli.cb && inst->cli.cb->status) {
309 		inst->cli.cb->status(inst, cb_err, *status);
310 	}
311 
312 	return BT_GATT_ITER_STOP;
313 }
314 
aics_cp_notify_app(struct bt_aics * inst,uint8_t err)315 static void aics_cp_notify_app(struct bt_aics *inst, uint8_t err)
316 {
317 	if (!inst->cli.cb) {
318 		return;
319 	}
320 
321 	switch (inst->cli.cp_val.cp.opcode) {
322 	case BT_AICS_OPCODE_SET_GAIN:
323 		if (inst->cli.cb->set_gain) {
324 			inst->cli.cb->set_gain(inst, err);
325 		}
326 		break;
327 	case BT_AICS_OPCODE_UNMUTE:
328 		if (inst->cli.cb->unmute) {
329 			inst->cli.cb->unmute(inst, err);
330 		}
331 		break;
332 	case BT_AICS_OPCODE_MUTE:
333 		if (inst->cli.cb->mute) {
334 			inst->cli.cb->mute(inst, err);
335 		}
336 		break;
337 	case BT_AICS_OPCODE_SET_MANUAL:
338 		if (inst->cli.cb->set_manual_mode) {
339 			inst->cli.cb->set_manual_mode(inst, err);
340 		}
341 		break;
342 	case BT_AICS_OPCODE_SET_AUTO:
343 		if (inst->cli.cb->set_auto_mode) {
344 			inst->cli.cb->set_auto_mode(inst, err);
345 		}
346 		break;
347 	default:
348 		LOG_DBG("Unknown opcode 0x%02x", inst->cli.cp_val.cp.opcode);
349 		break;
350 	}
351 }
352 
internal_read_state_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)353 static uint8_t internal_read_state_cb(struct bt_conn *conn, uint8_t err,
354 				      struct bt_gatt_read_params *params,
355 				      const void *data, uint16_t length)
356 {
357 	int cb_err = err;
358 	struct bt_aics *inst = lookup_aics_by_handle(conn, params->single.handle);
359 	const struct bt_aics_state *state = (const struct bt_aics_state *)data;
360 
361 	memset(params, 0, sizeof(*params));
362 
363 	if (!inst) {
364 		LOG_ERR("Instance not found");
365 		return BT_GATT_ITER_STOP;
366 	}
367 
368 	if (err) {
369 		LOG_WRN("State read failed: %d", err);
370 	} else if (data) {
371 		if (length == sizeof(*state)) {
372 			int write_err;
373 
374 			LOG_DBG("Gain %d, mute %u, gain_mode %u, counter %u", state->gain,
375 				state->mute, state->gain_mode, state->change_counter);
376 			inst->cli.change_counter = state->change_counter;
377 
378 			/* clear busy flag to reuse function */
379 			atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
380 
381 			if (inst->cli.cp_val.cp.opcode == BT_AICS_OPCODE_SET_GAIN) {
382 				write_err = bt_aics_client_gain_set(inst,
383 								    inst->cli.cp_val.gain_setting);
384 			} else {
385 				write_err = aics_client_common_control(inst->cli.cp_val.cp.opcode,
386 								       inst);
387 			}
388 
389 			if (write_err) {
390 				cb_err = BT_ATT_ERR_UNLIKELY;
391 			}
392 		} else {
393 			LOG_DBG("Invalid length %u (expected %zu)", length, sizeof(*state));
394 			cb_err = BT_ATT_ERR_UNLIKELY;
395 		}
396 	} else {
397 		/* Since we return BT_GATT_ITER_STOP this should never happen */
398 		__ASSERT(false, "Unexpected NULL data without error");
399 	}
400 
401 	if (cb_err) {
402 		atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
403 		aics_cp_notify_app(inst, cb_err);
404 	}
405 
406 	return BT_GATT_ITER_STOP;
407 }
408 
409 
aics_client_write_aics_cp_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)410 static void aics_client_write_aics_cp_cb(struct bt_conn *conn, uint8_t err,
411 					 struct bt_gatt_write_params *params)
412 {
413 	int cb_err = err;
414 	struct bt_aics *inst = lookup_aics_by_handle(conn, params->handle);
415 
416 	memset(params, 0, sizeof(*params));
417 
418 	if (!inst) {
419 		LOG_DBG("Instance not found");
420 		return;
421 	}
422 
423 	LOG_DBG("Inst %p: err: %d", inst, cb_err);
424 
425 	/* If the change counter is out of data when a write was attempted from
426 	 * the application, we automatically initiate a read to get the newest
427 	 * state and try again. Once the change counter has been read, we
428 	 * restart the applications write request. If it fails
429 	 * the second time, we return an error to the application.
430 	 */
431 	if (cb_err == BT_AICS_ERR_INVALID_COUNTER &&
432 	    atomic_test_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_CP_RETRIED)) {
433 		cb_err = BT_ATT_ERR_UNLIKELY;
434 	} else if (cb_err == BT_AICS_ERR_INVALID_COUNTER && inst->cli.state_handle) {
435 		inst->cli.read_params.func = internal_read_state_cb;
436 		inst->cli.read_params.handle_count = 1;
437 		inst->cli.read_params.single.handle = inst->cli.state_handle;
438 		inst->cli.read_params.single.offset = 0U;
439 
440 		atomic_set_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_CP_RETRIED);
441 
442 		cb_err = bt_gatt_read(conn, &inst->cli.read_params);
443 		if (cb_err != 0) {
444 			LOG_WRN("Could not read state: %d", cb_err);
445 		} else {
446 			/* Wait for read callback */
447 			return;
448 		}
449 	} else {
450 		/* Write failed, fallthrough and notify application */
451 	}
452 
453 	atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_CP_RETRIED);
454 	atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
455 
456 	aics_cp_notify_app(inst, cb_err);
457 }
458 
aics_client_common_control(uint8_t opcode,struct bt_aics * inst)459 static int aics_client_common_control(uint8_t opcode, struct bt_aics *inst)
460 {
461 	int err;
462 
463 	CHECKIF(!inst) {
464 		LOG_DBG("NULL instance");
465 		return -EINVAL;
466 	}
467 
468 	CHECKIF(!inst->client_instance) {
469 		LOG_DBG("Not a client instance instance");
470 		return -EINVAL;
471 	}
472 
473 	CHECKIF(inst->cli.conn == NULL) {
474 		LOG_DBG("NULL conn");
475 		return -EINVAL;
476 	}
477 
478 	if (!inst->cli.control_handle) {
479 		LOG_DBG("Handle not set for opcode %u", opcode);
480 		return -EINVAL;
481 	} else if (atomic_test_and_set_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY)) {
482 		return -EBUSY;
483 	}
484 
485 	inst->cli.cp_val.cp.opcode = opcode;
486 	inst->cli.cp_val.cp.counter = inst->cli.change_counter;
487 
488 	inst->cli.write_params.offset = 0;
489 	inst->cli.write_params.data = &inst->cli.cp_val.cp;
490 	inst->cli.write_params.length = sizeof(inst->cli.cp_val.cp);
491 	inst->cli.write_params.handle = inst->cli.control_handle;
492 	inst->cli.write_params.func = aics_client_write_aics_cp_cb;
493 
494 	err = bt_gatt_write(inst->cli.conn, &inst->cli.write_params);
495 	if (err != 0) {
496 		atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
497 	}
498 
499 	return err;
500 }
501 
aics_client_read_desc_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)502 static uint8_t aics_client_read_desc_cb(struct bt_conn *conn, uint8_t err,
503 					struct bt_gatt_read_params *params,
504 					const void *data, uint16_t length)
505 {
506 	int cb_err = err;
507 	char desc[MIN(BT_L2CAP_RX_MTU, BT_ATT_MAX_ATTRIBUTE_LEN) + 1];
508 	struct bt_aics *inst = lookup_aics_by_handle(conn, params->single.handle);
509 
510 	memset(params, 0, sizeof(*params));
511 
512 	if (!inst) {
513 		LOG_DBG("Instance not found");
514 		return BT_GATT_ITER_STOP;
515 	}
516 
517 	atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
518 
519 	if (cb_err) {
520 		LOG_DBG("Description read failed: %d", err);
521 		if (inst->cli.cb && inst->cli.cb->description) {
522 			inst->cli.cb->description(inst, cb_err, NULL);
523 		}
524 		return BT_GATT_ITER_STOP;
525 	}
526 
527 	if (data) {
528 		LOG_HEXDUMP_DBG(data, length, "Input description read");
529 
530 		/* Truncate if too large */
531 		if (length > sizeof(desc) - 1) {
532 			LOG_DBG("Description truncated from %u to %zu octets", length,
533 				sizeof(desc) - 1);
534 		}
535 		length = MIN(sizeof(desc) - 1, length);
536 
537 		/* TODO: Handle long reads */
538 
539 		memcpy(desc, data, length);
540 	}
541 
542 	desc[length] = '\0';
543 	LOG_DBG("Input description: %s", desc);
544 
545 	if (inst->cli.cb && inst->cli.cb->description) {
546 		inst->cli.cb->description(inst, cb_err, desc);
547 	}
548 
549 	return BT_GATT_ITER_STOP;
550 }
551 
valid_inst_discovered(struct bt_aics * inst)552 static bool valid_inst_discovered(struct bt_aics *inst)
553 {
554 	return inst->cli.state_handle &&
555 	       inst->cli.gain_handle &&
556 	       inst->cli.type_handle &&
557 	       inst->cli.status_handle &&
558 	       inst->cli.control_handle &&
559 	       inst->cli.desc_handle;
560 }
561 
store_attr_handle_and_subscribe(struct bt_aics_client * client_inst,struct bt_conn * conn,const struct bt_gatt_chrc * chrc)562 static int store_attr_handle_and_subscribe(struct bt_aics_client *client_inst, struct bt_conn *conn,
563 					   const struct bt_gatt_chrc *chrc)
564 {
565 	struct bt_gatt_subscribe_params *sub_params = NULL;
566 
567 	if (bt_uuid_cmp(chrc->uuid, BT_UUID_AICS_STATE) == 0U) {
568 		LOG_DBG("Audio Input state");
569 		client_inst->state_handle = chrc->value_handle;
570 		sub_params = &client_inst->state_sub_params;
571 	} else if (bt_uuid_cmp(chrc->uuid, BT_UUID_AICS_GAIN_SETTINGS) == 0U) {
572 		LOG_DBG("Gain settings");
573 		client_inst->gain_handle = chrc->value_handle;
574 	} else if (bt_uuid_cmp(chrc->uuid, BT_UUID_AICS_INPUT_TYPE) == 0U) {
575 		LOG_DBG("Input type");
576 		client_inst->type_handle = chrc->value_handle;
577 	} else if (bt_uuid_cmp(chrc->uuid, BT_UUID_AICS_INPUT_STATUS) == 0U) {
578 		LOG_DBG("Input status");
579 		client_inst->status_handle = chrc->value_handle;
580 		sub_params = &client_inst->status_sub_params;
581 	} else if (bt_uuid_cmp(chrc->uuid, BT_UUID_AICS_CONTROL) == 0U) {
582 		LOG_DBG("Control point");
583 		client_inst->control_handle = chrc->value_handle;
584 	} else if (bt_uuid_cmp(chrc->uuid, BT_UUID_AICS_DESCRIPTION) == 0U) {
585 		LOG_DBG("Description");
586 		client_inst->desc_handle = chrc->value_handle;
587 		if ((chrc->properties & BT_GATT_CHRC_NOTIFY) != 0U) {
588 			sub_params = &client_inst->desc_sub_params;
589 		}
590 
591 		if ((chrc->properties & BT_GATT_CHRC_WRITE_WITHOUT_RESP) != 0U) {
592 			atomic_set_bit(client_inst->flags, BT_AICS_CLIENT_FLAG_DESC_WRITABLE);
593 		}
594 	} else {
595 		__ASSERT(false, "Unexpected UUID %s discovered", bt_uuid_str(chrc->uuid));
596 	}
597 
598 	if (sub_params != NULL) {
599 		int err;
600 
601 		sub_params->value = BT_GATT_CCC_NOTIFY;
602 		sub_params->value_handle = chrc->value_handle;
603 		/*
604 		 * TODO: Don't assume that CCC is at value handle + 1;
605 		 * do proper discovery;
606 		 */
607 		sub_params->ccc_handle = chrc->value_handle + 1;
608 		sub_params->notify = aics_client_notify_handler;
609 		atomic_set_bit(sub_params->flags, BT_GATT_SUBSCRIBE_FLAG_VOLATILE);
610 
611 		err = bt_gatt_subscribe(conn, sub_params);
612 		if (err != 0 && err != -EALREADY) {
613 			LOG_ERR("Failed to subscribe: %d", err);
614 
615 			return err;
616 		}
617 	}
618 
619 	return 0;
620 }
621 
aics_discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)622 static uint8_t aics_discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
623 				  struct bt_gatt_discover_params *params)
624 {
625 	struct bt_aics_client *client_inst =
626 		CONTAINER_OF(params, struct bt_aics_client, discover_params);
627 	struct bt_aics *inst = CONTAINER_OF(client_inst, struct bt_aics, cli);
628 	const struct bt_gatt_chrc *chrc;
629 	int err;
630 
631 	if (!attr) {
632 		LOG_DBG("Discovery complete for AICS %p", inst);
633 
634 		memset(params, 0, sizeof(*params));
635 
636 		atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
637 
638 		if (inst->cli.cb && inst->cli.cb->discover) {
639 			err = valid_inst_discovered(inst) ? 0 : -ENOENT;
640 
641 			inst->cli.cb->discover(inst, err);
642 		}
643 
644 		return BT_GATT_ITER_STOP;
645 	}
646 
647 	LOG_DBG("[ATTRIBUTE] handle 0x%04X", attr->handle);
648 
649 	__ASSERT_NO_MSG(params->type == BT_GATT_DISCOVER_CHARACTERISTIC);
650 
651 	chrc = (const struct bt_gatt_chrc *)attr->user_data;
652 	if (inst->cli.start_handle == 0U) { /* if start handle is unset */
653 		inst->cli.start_handle = chrc->value_handle;
654 	}
655 	inst->cli.end_handle = chrc->value_handle;
656 
657 	err = store_attr_handle_and_subscribe(client_inst, conn, chrc);
658 	if (err != 0) {
659 		if (client_inst->cb && client_inst->cb->discover) {
660 			client_inst->cb->discover(inst, err);
661 		}
662 
663 		return BT_GATT_ITER_STOP;
664 	}
665 
666 	return BT_GATT_ITER_CONTINUE;
667 }
668 
aics_client_reset(struct bt_aics * inst)669 static void aics_client_reset(struct bt_aics *inst)
670 {
671 	inst->cli.change_counter = 0;
672 	inst->cli.gain_mode = 0;
673 	inst->cli.start_handle = 0;
674 	inst->cli.end_handle = 0;
675 	inst->cli.state_handle = 0;
676 	inst->cli.gain_handle = 0;
677 	inst->cli.type_handle = 0;
678 	inst->cli.status_handle = 0;
679 	inst->cli.control_handle = 0;
680 	inst->cli.desc_handle = 0;
681 
682 	atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_DESC_WRITABLE);
683 	atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_CP_RETRIED);
684 
685 	if (inst->cli.conn != NULL) {
686 		struct bt_conn *conn = inst->cli.conn;
687 
688 		bt_conn_unref(conn);
689 		inst->cli.conn = NULL;
690 	}
691 }
692 
disconnected(struct bt_conn * conn,uint8_t reason)693 static void disconnected(struct bt_conn *conn, uint8_t reason)
694 {
695 	for (size_t i = 0; i < ARRAY_SIZE(aics_insts); i++) {
696 		if (aics_insts[i].cli.conn == conn) {
697 			aics_client_reset(&aics_insts[i]);
698 		}
699 	}
700 }
701 
702 BT_CONN_CB_DEFINE(conn_callbacks) = {
703 	.disconnected = disconnected,
704 };
705 
bt_aics_discover(struct bt_conn * conn,struct bt_aics * inst,const struct bt_aics_discover_param * param)706 int bt_aics_discover(struct bt_conn *conn, struct bt_aics *inst,
707 		     const struct bt_aics_discover_param *param)
708 {
709 	int err = 0;
710 
711 	CHECKIF(!inst || !conn || !param) {
712 		LOG_DBG("%s cannot be NULL", inst == NULL   ? "inst"
713 					     : conn == NULL ? "conn"
714 							    : "param");
715 		return -EINVAL;
716 	}
717 
718 	CHECKIF(param->end_handle <= param->start_handle) {
719 		LOG_DBG("start_handle (%u) shall be less than end_handle (%u)", param->start_handle,
720 			param->end_handle);
721 		return -EINVAL;
722 	}
723 
724 	CHECKIF(!atomic_test_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_ACTIVE)) {
725 		LOG_DBG("Inactive instance");
726 		return -EINVAL;
727 	}
728 
729 	if (atomic_test_and_set_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY)) {
730 		LOG_DBG("Instance is busy");
731 		return -EBUSY;
732 	}
733 
734 	aics_client_reset(inst);
735 
736 	(void)memset(&inst->cli.discover_params, 0, sizeof(inst->cli.discover_params));
737 
738 	inst->cli.discover_params.start_handle = param->start_handle;
739 	inst->cli.discover_params.end_handle = param->end_handle;
740 	inst->cli.discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
741 	inst->cli.discover_params.func = aics_discover_func;
742 
743 	err = bt_gatt_discover(conn, &inst->cli.discover_params);
744 	if (err != 0) {
745 		atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
746 		LOG_DBG("Discover failed (err %d)", err);
747 	} else {
748 		inst->cli.conn = bt_conn_ref(conn);
749 	}
750 
751 	return err;
752 }
753 
bt_aics_client_free_instance_get(void)754 struct bt_aics *bt_aics_client_free_instance_get(void)
755 {
756 	for (int i = 0; i < ARRAY_SIZE(aics_insts); i++) {
757 		if (!atomic_test_and_set_bit(aics_insts[i].cli.flags, BT_AICS_CLIENT_FLAG_ACTIVE)) {
758 			aics_insts[i].client_instance = true;
759 			return &aics_insts[i];
760 		}
761 	}
762 
763 	return NULL;
764 }
765 
bt_aics_client_conn_get(const struct bt_aics * aics,struct bt_conn ** conn)766 int bt_aics_client_conn_get(const struct bt_aics *aics, struct bt_conn **conn)
767 {
768 	CHECKIF(aics == NULL) {
769 		LOG_DBG("NULL aics pointer");
770 		return -EINVAL;
771 	}
772 
773 	if (!aics->client_instance) {
774 		LOG_DBG("aics pointer shall be client instance");
775 		return -EINVAL;
776 	}
777 
778 	if (aics->cli.conn == NULL) {
779 		LOG_DBG("aics pointer not associated with a connection. "
780 		       "Do discovery first");
781 		return -ENOTCONN;
782 	}
783 
784 	*conn = aics->cli.conn;
785 	return 0;
786 }
787 
bt_aics_client_state_get(struct bt_aics * inst)788 int bt_aics_client_state_get(struct bt_aics *inst)
789 {
790 	int err;
791 
792 	CHECKIF(!inst) {
793 		LOG_DBG("NULL instance");
794 		return -EINVAL;
795 	}
796 
797 	CHECKIF(!inst->client_instance) {
798 		LOG_DBG("Not a client instance instance");
799 		return -EINVAL;
800 	}
801 
802 	CHECKIF(inst->cli.conn == NULL) {
803 		LOG_DBG("NULL conn");
804 		return -EINVAL;
805 	}
806 
807 	if (!inst->cli.state_handle) {
808 		LOG_DBG("Handle not set");
809 		return -EINVAL;
810 	} else if (atomic_test_and_set_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY)) {
811 		return -EBUSY;
812 	}
813 
814 	inst->cli.read_params.func = aics_client_read_state_cb;
815 	inst->cli.read_params.handle_count = 1;
816 	inst->cli.read_params.single.handle = inst->cli.state_handle;
817 
818 	err = bt_gatt_read(inst->cli.conn, &inst->cli.read_params);
819 	if (err != 0) {
820 		atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
821 	}
822 
823 	return err;
824 }
825 
bt_aics_client_gain_setting_get(struct bt_aics * inst)826 int bt_aics_client_gain_setting_get(struct bt_aics *inst)
827 {
828 	int err;
829 
830 	CHECKIF(!inst) {
831 		LOG_DBG("NULL instance");
832 		return -EINVAL;
833 	}
834 
835 	CHECKIF(!inst->client_instance) {
836 		LOG_DBG("Not a client instance instance");
837 		return -EINVAL;
838 	}
839 
840 	CHECKIF(inst->cli.conn == NULL) {
841 		LOG_DBG("NULL conn");
842 		return -EINVAL;
843 	}
844 
845 	if (!inst->cli.gain_handle) {
846 		LOG_DBG("Handle not set");
847 		return -EINVAL;
848 	} else if (atomic_test_and_set_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY)) {
849 		return -EBUSY;
850 	}
851 
852 	inst->cli.read_params.func = aics_client_read_gain_settings_cb;
853 	inst->cli.read_params.handle_count = 1;
854 	inst->cli.read_params.single.handle = inst->cli.gain_handle;
855 
856 	err = bt_gatt_read(inst->cli.conn, &inst->cli.read_params);
857 	if (err != 0) {
858 		atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
859 	}
860 
861 	return err;
862 }
863 
bt_aics_client_type_get(struct bt_aics * inst)864 int bt_aics_client_type_get(struct bt_aics *inst)
865 {
866 	int err;
867 
868 	CHECKIF(!inst) {
869 		LOG_DBG("NULL instance");
870 		return -EINVAL;
871 	}
872 
873 	CHECKIF(!inst->client_instance) {
874 		LOG_DBG("Not a client instance instance");
875 		return -EINVAL;
876 	}
877 
878 	CHECKIF(inst->cli.conn == NULL) {
879 		LOG_DBG("NULL conn");
880 		return -EINVAL;
881 	}
882 
883 	if (!inst->cli.type_handle) {
884 		LOG_DBG("Handle not set");
885 		return -EINVAL;
886 	} else if (atomic_test_and_set_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY)) {
887 		return -EBUSY;
888 	}
889 
890 	inst->cli.read_params.func = aics_client_read_type_cb;
891 	inst->cli.read_params.handle_count = 1;
892 	inst->cli.read_params.single.handle = inst->cli.type_handle;
893 
894 	err = bt_gatt_read(inst->cli.conn, &inst->cli.read_params);
895 	if (err != 0) {
896 		atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
897 	}
898 
899 	return err;
900 }
901 
bt_aics_client_status_get(struct bt_aics * inst)902 int bt_aics_client_status_get(struct bt_aics *inst)
903 {
904 	int err;
905 
906 	CHECKIF(!inst) {
907 		LOG_DBG("NULL instance");
908 		return -EINVAL;
909 	}
910 
911 	CHECKIF(!inst->client_instance) {
912 		LOG_DBG("Not a client instance instance");
913 		return -EINVAL;
914 	}
915 
916 	CHECKIF(inst->cli.conn == NULL) {
917 		LOG_DBG("NULL conn");
918 		return -EINVAL;
919 	}
920 
921 	if (!inst->cli.status_handle) {
922 		LOG_DBG("Handle not set");
923 		return -EINVAL;
924 	} else if (atomic_test_and_set_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY)) {
925 		return -EBUSY;
926 	}
927 
928 	inst->cli.read_params.func = aics_client_read_status_cb;
929 	inst->cli.read_params.handle_count = 1;
930 	inst->cli.read_params.single.handle = inst->cli.status_handle;
931 
932 	err = bt_gatt_read(inst->cli.conn, &inst->cli.read_params);
933 	if (err != 0) {
934 		atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
935 	}
936 
937 	return err;
938 }
939 
bt_aics_client_unmute(struct bt_aics * inst)940 int bt_aics_client_unmute(struct bt_aics *inst)
941 {
942 	return aics_client_common_control(BT_AICS_OPCODE_UNMUTE, inst);
943 }
944 
bt_aics_client_mute(struct bt_aics * inst)945 int bt_aics_client_mute(struct bt_aics *inst)
946 {
947 	return aics_client_common_control(BT_AICS_OPCODE_MUTE, inst);
948 }
949 
bt_aics_client_manual_gain_set(struct bt_aics * inst)950 int bt_aics_client_manual_gain_set(struct bt_aics *inst)
951 {
952 	return aics_client_common_control(BT_AICS_OPCODE_SET_MANUAL, inst);
953 }
954 
bt_aics_client_automatic_gain_set(struct bt_aics * inst)955 int bt_aics_client_automatic_gain_set(struct bt_aics *inst)
956 {
957 	return aics_client_common_control(BT_AICS_OPCODE_SET_AUTO, inst);
958 }
959 
bt_aics_client_gain_set(struct bt_aics * inst,int8_t gain)960 int bt_aics_client_gain_set(struct bt_aics *inst, int8_t gain)
961 {
962 	int err;
963 
964 	CHECKIF(!inst) {
965 		LOG_DBG("NULL instance");
966 		return -EINVAL;
967 	}
968 
969 	CHECKIF(!inst->client_instance) {
970 		LOG_DBG("Not a client instance instance");
971 		return -EINVAL;
972 	}
973 
974 	CHECKIF(inst->cli.conn == NULL) {
975 		LOG_DBG("NULL conn");
976 		return -EINVAL;
977 	}
978 
979 	if (!inst->cli.control_handle) {
980 		LOG_DBG("Handle not set");
981 		return -EINVAL;
982 	} else if (atomic_test_and_set_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY)) {
983 		return -EBUSY;
984 	}
985 
986 	inst->cli.cp_val.cp.opcode = BT_AICS_OPCODE_SET_GAIN;
987 	inst->cli.cp_val.cp.counter = inst->cli.change_counter;
988 	inst->cli.cp_val.gain_setting = gain;
989 
990 	inst->cli.write_params.data = &inst->cli.cp_val;
991 	inst->cli.write_params.length = sizeof(inst->cli.cp_val);
992 	inst->cli.write_params.handle = inst->cli.control_handle;
993 	inst->cli.write_params.func = aics_client_write_aics_cp_cb;
994 
995 	err = bt_gatt_write(inst->cli.conn, &inst->cli.write_params);
996 	if (err != 0) {
997 		atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
998 	}
999 
1000 	return err;
1001 }
1002 
bt_aics_client_description_get(struct bt_aics * inst)1003 int bt_aics_client_description_get(struct bt_aics *inst)
1004 {
1005 	int err;
1006 
1007 	CHECKIF(!inst) {
1008 		LOG_DBG("NULL instance");
1009 		return -EINVAL;
1010 	}
1011 
1012 	CHECKIF(!inst->client_instance) {
1013 		LOG_DBG("Not a client instance instance");
1014 		return -EINVAL;
1015 	}
1016 
1017 	CHECKIF(inst->cli.conn == NULL) {
1018 		LOG_DBG("NULL conn");
1019 		return -EINVAL;
1020 	}
1021 
1022 	if (!inst->cli.desc_handle) {
1023 		LOG_DBG("Handle not set");
1024 		return -EINVAL;
1025 	} else if (atomic_test_and_set_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY)) {
1026 		return -EBUSY;
1027 	}
1028 
1029 	inst->cli.read_params.func = aics_client_read_desc_cb;
1030 	inst->cli.read_params.handle_count = 1;
1031 	inst->cli.read_params.single.handle = inst->cli.desc_handle;
1032 
1033 	err = bt_gatt_read(inst->cli.conn, &inst->cli.read_params);
1034 	if (err != 0) {
1035 		atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
1036 	}
1037 
1038 	return err;
1039 }
1040 
bt_aics_client_description_set(struct bt_aics * inst,const char * description)1041 int bt_aics_client_description_set(struct bt_aics *inst,
1042 				   const char *description)
1043 {
1044 	int err;
1045 
1046 	CHECKIF(!inst) {
1047 		LOG_DBG("NULL instance");
1048 		return -EINVAL;
1049 	}
1050 
1051 	CHECKIF(!inst->client_instance) {
1052 		LOG_DBG("Not a client instance instance");
1053 		return -EINVAL;
1054 	}
1055 
1056 	CHECKIF(inst->cli.conn == NULL) {
1057 		LOG_DBG("NULL conn");
1058 		return -EINVAL;
1059 	}
1060 
1061 	if (!inst->cli.desc_handle) {
1062 		LOG_DBG("Handle not set");
1063 		return -EINVAL;
1064 	} else if (!atomic_test_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_DESC_WRITABLE)) {
1065 		LOG_DBG("Description is not writable on peer service instance");
1066 		return -EPERM;
1067 	} else if (atomic_test_and_set_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY)) {
1068 		return -EBUSY;
1069 	}
1070 
1071 	err = bt_gatt_write_without_response(inst->cli.conn, inst->cli.desc_handle, description,
1072 					     strlen(description), false);
1073 	if (err != 0) {
1074 		atomic_clear_bit(inst->cli.flags, BT_AICS_CLIENT_FLAG_BUSY);
1075 	}
1076 
1077 	return err;
1078 }
1079 
bt_aics_client_cb_register(struct bt_aics * inst,struct bt_aics_cb * cb)1080 void bt_aics_client_cb_register(struct bt_aics *inst, struct bt_aics_cb *cb)
1081 {
1082 	CHECKIF(!inst) {
1083 		LOG_DBG("inst cannot be NULL");
1084 		return;
1085 	}
1086 
1087 	inst->cli.cb = cb;
1088 }
1089