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