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