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