1 /*
2 * Copyright (c) 2020 Bose Corporation
3 * Copyright (c) 2020 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/kernel.h>
9 #include <zephyr/sys/byteorder.h>
10 #include <zephyr/sys/check.h>
11
12 #include <zephyr/device.h>
13 #include <zephyr/init.h>
14
15 #include <zephyr/bluetooth/bluetooth.h>
16 #include <zephyr/bluetooth/conn.h>
17 #include <zephyr/bluetooth/gatt.h>
18 #include <zephyr/bluetooth/audio/aics.h>
19
20 #include "aics_internal.h"
21 #include "audio_internal.h"
22
23 #define LOG_LEVEL CONFIG_BT_AICS_LOG_LEVEL
24 #include <zephyr/logging/log.h>
25 LOG_MODULE_REGISTER(bt_aics);
26
27 #define VALID_AICS_OPCODE(opcode) \
28 ((opcode) >= BT_AICS_OPCODE_SET_GAIN && (opcode) <= BT_AICS_OPCODE_SET_AUTO)
29
30 #define AICS_CP_LEN 0x02
31 #define AICS_CP_SET_GAIN_LEN 0x03
32
33
34 static ssize_t write_description(struct bt_conn *conn,
35 const struct bt_gatt_attr *attr,
36 const void *buf, uint16_t len, uint16_t offset,
37 uint8_t flags);
38
39 static ssize_t write_aics_control(struct bt_conn *conn,
40 const struct bt_gatt_attr *attr,
41 const void *buf, uint16_t len,
42 uint16_t offset, uint8_t flags);
43
44 #if defined(CONFIG_BT_AICS)
45 static void aics_state_cfg_changed(const struct bt_gatt_attr *attr,
46 uint16_t value);
47 static ssize_t read_aics_state(struct bt_conn *conn,
48 const struct bt_gatt_attr *attr,
49 void *buf, uint16_t len, uint16_t offset);
50 static ssize_t read_aics_gain_settings(struct bt_conn *conn,
51 const struct bt_gatt_attr *attr,
52 void *buf, uint16_t len,
53 uint16_t offset);
54 static ssize_t read_type(struct bt_conn *conn, const struct bt_gatt_attr *attr,
55 void *buf, uint16_t len, uint16_t offset);
56 static void aics_input_status_cfg_changed(const struct bt_gatt_attr *attr,
57 uint16_t value);
58 static ssize_t read_input_status(struct bt_conn *conn,
59 const struct bt_gatt_attr *attr,
60 void *buf, uint16_t len, uint16_t offset);
61 static void aics_description_cfg_changed(const struct bt_gatt_attr *attr,
62 uint16_t value);
63 static ssize_t read_description(struct bt_conn *conn,
64 const struct bt_gatt_attr *attr, void *buf,
65 uint16_t len, uint16_t offset);
66
67 #define BT_AICS_SERVICE_DEFINITION(_aics) { \
68 BT_GATT_SECONDARY_SERVICE(BT_UUID_AICS), \
69 BT_AUDIO_CHRC(BT_UUID_AICS_STATE, \
70 BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
71 BT_GATT_PERM_READ_ENCRYPT, \
72 read_aics_state, NULL, &_aics), \
73 BT_AUDIO_CCC(aics_state_cfg_changed), \
74 BT_AUDIO_CHRC(BT_UUID_AICS_GAIN_SETTINGS, \
75 BT_GATT_CHRC_READ, \
76 BT_GATT_PERM_READ_ENCRYPT, \
77 read_aics_gain_settings, NULL, &_aics), \
78 BT_AUDIO_CHRC(BT_UUID_AICS_INPUT_TYPE, \
79 BT_GATT_CHRC_READ, \
80 BT_GATT_PERM_READ_ENCRYPT, \
81 read_type, NULL, &_aics), \
82 BT_AUDIO_CHRC(BT_UUID_AICS_INPUT_STATUS, \
83 BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
84 BT_GATT_PERM_READ_ENCRYPT, \
85 read_input_status, NULL, &_aics), \
86 BT_AUDIO_CCC(aics_input_status_cfg_changed), \
87 BT_AUDIO_CHRC(BT_UUID_AICS_CONTROL, \
88 BT_GATT_CHRC_WRITE, \
89 BT_GATT_PERM_WRITE_ENCRYPT, \
90 NULL, write_aics_control, &_aics), \
91 BT_AUDIO_CHRC(BT_UUID_AICS_DESCRIPTION, \
92 BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
93 BT_GATT_PERM_READ_ENCRYPT, \
94 read_description, write_description, &_aics), \
95 BT_AUDIO_CCC(aics_description_cfg_changed) \
96 }
97
98
99 static struct bt_aics aics_insts[CONFIG_BT_AICS_MAX_INSTANCE_COUNT];
100 static uint32_t instance_cnt;
101 BT_GATT_SERVICE_INSTANCE_DEFINE(aics_service_list, aics_insts,
102 CONFIG_BT_AICS_MAX_INSTANCE_COUNT,
103 BT_AICS_SERVICE_DEFINITION);
104
aics_state_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)105 static void aics_state_cfg_changed(const struct bt_gatt_attr *attr,
106 uint16_t value)
107 {
108 LOG_DBG("value 0x%04x", value);
109 }
110
read_aics_state(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)111 static ssize_t read_aics_state(struct bt_conn *conn,
112 const struct bt_gatt_attr *attr, void *buf,
113 uint16_t len, uint16_t offset)
114 {
115 struct bt_aics *inst = BT_AUDIO_CHRC_USER_DATA(attr);
116
117 LOG_DBG("gain %d, mute %u, gain_mode %u, counter %u", inst->srv.state.gain,
118 inst->srv.state.mute, inst->srv.state.gain_mode, inst->srv.state.change_counter);
119
120 return bt_gatt_attr_read(conn, attr, buf, len, offset, &inst->srv.state,
121 sizeof(inst->srv.state));
122 }
123
read_aics_gain_settings(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)124 static ssize_t read_aics_gain_settings(struct bt_conn *conn,
125 const struct bt_gatt_attr *attr,
126 void *buf, uint16_t len, uint16_t offset)
127 {
128 struct bt_aics *inst = BT_AUDIO_CHRC_USER_DATA(attr);
129
130 LOG_DBG("units %u, min %d, max %d", inst->srv.gain_settings.units,
131 inst->srv.gain_settings.minimum, inst->srv.gain_settings.maximum);
132
133 return bt_gatt_attr_read(conn, attr, buf, len, offset,
134 &inst->srv.gain_settings,
135 sizeof(inst->srv.gain_settings));
136 }
137
read_type(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)138 static ssize_t read_type(struct bt_conn *conn, const struct bt_gatt_attr *attr,
139 void *buf, uint16_t len, uint16_t offset)
140 {
141 struct bt_aics *inst = BT_AUDIO_CHRC_USER_DATA(attr);
142
143 LOG_DBG("%u", inst->srv.type);
144
145 return bt_gatt_attr_read(conn, attr, buf, len, offset, &inst->srv.type,
146 sizeof(inst->srv.type));
147 }
148
aics_input_status_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)149 static void aics_input_status_cfg_changed(const struct bt_gatt_attr *attr,
150 uint16_t value)
151 {
152 LOG_DBG("value 0x%04x", value);
153 }
154
read_input_status(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)155 static ssize_t read_input_status(struct bt_conn *conn,
156 const struct bt_gatt_attr *attr, void *buf,
157 uint16_t len, uint16_t offset)
158 {
159 struct bt_aics *inst = BT_AUDIO_CHRC_USER_DATA(attr);
160
161 LOG_DBG("%u", inst->srv.status);
162
163 return bt_gatt_attr_read(conn, attr, buf, len, offset, &inst->srv.status,
164 sizeof(inst->srv.status));
165 }
166
aics_notify_str(enum bt_aics_notify notify)167 static const char *aics_notify_str(enum bt_aics_notify notify)
168 {
169 switch (notify) {
170 case AICS_NOTIFY_STATE:
171 return "state";
172 case AICS_NOTIFY_DESCRIPTION:
173 return "description";
174 case AICS_NOTIFY_STATUS:
175 return "status";
176 default:
177 return "unknown";
178 }
179 }
180
notify_work_reschedule(struct bt_aics * inst,enum bt_aics_notify notify,k_timeout_t delay)181 static void notify_work_reschedule(struct bt_aics *inst, enum bt_aics_notify notify,
182 k_timeout_t delay)
183 {
184 int err;
185
186 atomic_set_bit(inst->srv.notify, notify);
187
188 err = k_work_reschedule(&inst->srv.notify_work, K_NO_WAIT);
189 if (err < 0) {
190 LOG_ERR("Failed to reschedule %s notification err %d",
191 aics_notify_str(notify), err);
192 }
193 }
194
notify(struct bt_aics * inst,enum bt_aics_notify notify,const struct bt_uuid * uuid,const void * data,uint16_t len)195 static void notify(struct bt_aics *inst, enum bt_aics_notify notify, const struct bt_uuid *uuid,
196 const void *data, uint16_t len)
197 {
198 int err;
199
200 err = bt_gatt_notify_uuid(NULL, uuid, inst->srv.service_p->attrs, data, len);
201 if (err == -ENOMEM) {
202 notify_work_reschedule(inst, notify, K_USEC(BT_AUDIO_NOTIFY_RETRY_DELAY_US));
203 } else if (err < 0 && err != -ENOTCONN) {
204 LOG_ERR("Notify %s err %d", aics_notify_str(notify), err);
205 }
206 }
207
notify_work_handler(struct k_work * work)208 static void notify_work_handler(struct k_work *work)
209 {
210 struct k_work_delayable *d_work = k_work_delayable_from_work(work);
211 struct bt_aics *inst = CONTAINER_OF(d_work, struct bt_aics, srv.notify_work);
212
213 if (atomic_test_and_clear_bit(inst->srv.notify, AICS_NOTIFY_STATE)) {
214 notify(inst, AICS_NOTIFY_STATE, BT_UUID_AICS_STATE, &inst->srv.state,
215 sizeof(inst->srv.state));
216 }
217
218 if (atomic_test_and_clear_bit(inst->srv.notify, AICS_NOTIFY_DESCRIPTION)) {
219 notify(inst, AICS_NOTIFY_DESCRIPTION, BT_UUID_AICS_DESCRIPTION,
220 &inst->srv.description, strlen(inst->srv.description));
221 }
222
223 if (atomic_test_and_clear_bit(inst->srv.notify, AICS_NOTIFY_STATUS)) {
224 notify(inst, AICS_NOTIFY_STATUS, BT_UUID_AICS_INPUT_STATUS, &inst->srv.status,
225 sizeof(inst->srv.status));
226 }
227 }
228
value_changed(struct bt_aics * inst,enum bt_aics_notify notify)229 static void value_changed(struct bt_aics *inst, enum bt_aics_notify notify)
230 {
231 notify_work_reschedule(inst, notify, K_NO_WAIT);
232 }
233 #else
234 #define value_changed(...)
235 #endif /* CONFIG_BT_AICS */
236
write_aics_control(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)237 static ssize_t write_aics_control(struct bt_conn *conn,
238 const struct bt_gatt_attr *attr,
239 const void *buf, uint16_t len,
240 uint16_t offset, uint8_t flags)
241 {
242 struct bt_aics *inst = BT_AUDIO_CHRC_USER_DATA(attr);
243 const struct bt_aics_gain_control *cp = buf;
244 bool notify = false;
245
246 if (offset) {
247 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
248 }
249
250 if (!len || !buf) {
251 return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
252 }
253
254 /* Check opcode before length */
255 if (!VALID_AICS_OPCODE(cp->cp.opcode)) {
256 LOG_DBG("Invalid opcode %u", cp->cp.opcode);
257 return BT_GATT_ERR(BT_AICS_ERR_OP_NOT_SUPPORTED);
258 }
259
260 if ((len < AICS_CP_LEN) ||
261 (len == AICS_CP_SET_GAIN_LEN && cp->cp.opcode != BT_AICS_OPCODE_SET_GAIN) ||
262 (len > AICS_CP_SET_GAIN_LEN)) {
263 return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
264 }
265
266 LOG_DBG("Opcode %u, counter %u", cp->cp.opcode, cp->cp.counter);
267 if (cp->cp.counter != inst->srv.state.change_counter) {
268 return BT_GATT_ERR(BT_AICS_ERR_INVALID_COUNTER);
269 }
270
271 switch (cp->cp.opcode) {
272 case BT_AICS_OPCODE_SET_GAIN:
273 LOG_DBG("Set gain %d", cp->gain_setting);
274 if (cp->gain_setting < inst->srv.gain_settings.minimum ||
275 cp->gain_setting > inst->srv.gain_settings.maximum) {
276 return BT_GATT_ERR(BT_AICS_ERR_OUT_OF_RANGE);
277 }
278 if (BT_AICS_INPUT_MODE_SETTABLE(inst->srv.state.gain_mode) &&
279 inst->srv.state.gain != cp->gain_setting) {
280 inst->srv.state.gain = cp->gain_setting;
281 notify = true;
282 }
283 break;
284 case BT_AICS_OPCODE_UNMUTE:
285 LOG_DBG("Unmute");
286 if (inst->srv.state.mute == BT_AICS_STATE_MUTE_DISABLED) {
287 return BT_GATT_ERR(BT_AICS_ERR_MUTE_DISABLED);
288 }
289 if (inst->srv.state.mute != BT_AICS_STATE_UNMUTED) {
290 inst->srv.state.mute = BT_AICS_STATE_UNMUTED;
291 notify = true;
292 }
293 break;
294 case BT_AICS_OPCODE_MUTE:
295 LOG_DBG("Mute");
296 if (inst->srv.state.mute == BT_AICS_STATE_MUTE_DISABLED) {
297 return BT_GATT_ERR(BT_AICS_ERR_MUTE_DISABLED);
298 }
299 if (inst->srv.state.mute != BT_AICS_STATE_MUTED) {
300 inst->srv.state.mute = BT_AICS_STATE_MUTED;
301 notify = true;
302 }
303 break;
304 case BT_AICS_OPCODE_SET_MANUAL:
305 LOG_DBG("Set manual mode");
306 if (BT_AICS_INPUT_MODE_IMMUTABLE(inst->srv.state.gain_mode)) {
307 return BT_GATT_ERR(BT_AICS_ERR_GAIN_MODE_NOT_ALLOWED);
308 }
309 if (inst->srv.state.gain_mode != BT_AICS_MODE_MANUAL) {
310 inst->srv.state.gain_mode = BT_AICS_MODE_MANUAL;
311 notify = true;
312 }
313 break;
314 case BT_AICS_OPCODE_SET_AUTO:
315 LOG_DBG("Set automatic mode");
316 if (BT_AICS_INPUT_MODE_IMMUTABLE(inst->srv.state.gain_mode)) {
317 return BT_GATT_ERR(BT_AICS_ERR_GAIN_MODE_NOT_ALLOWED);
318 }
319 if (inst->srv.state.gain_mode != BT_AICS_MODE_AUTO) {
320 inst->srv.state.gain_mode = BT_AICS_MODE_AUTO;
321 notify = true;
322 }
323 break;
324 default:
325 return BT_GATT_ERR(BT_AICS_ERR_OP_NOT_SUPPORTED);
326 }
327
328 if (notify) {
329 inst->srv.state.change_counter++;
330
331 LOG_DBG("New state: gain %d, mute %u, gain_mode %u, counter %u",
332 inst->srv.state.gain, inst->srv.state.mute, inst->srv.state.gain_mode,
333 inst->srv.state.change_counter);
334
335 value_changed(inst, AICS_NOTIFY_STATE);
336
337 if (inst->srv.cb && inst->srv.cb->state) {
338 inst->srv.cb->state(inst, 0, inst->srv.state.gain,
339 inst->srv.state.mute,
340 inst->srv.state.gain_mode);
341 } else {
342 LOG_DBG("Callback not registered for instance %p", inst);
343 }
344 }
345
346 return len;
347 }
348
349 #if defined(CONFIG_BT_AICS)
aics_description_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)350 static void aics_description_cfg_changed(const struct bt_gatt_attr *attr,
351 uint16_t value)
352 {
353 LOG_DBG("value 0x%04x", value);
354 }
355 #endif /* CONFIG_BT_AICS */
356
write_description(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)357 static ssize_t write_description(struct bt_conn *conn,
358 const struct bt_gatt_attr *attr,
359 const void *buf, uint16_t len, uint16_t offset,
360 uint8_t flags)
361 {
362 struct bt_aics *inst = BT_AUDIO_CHRC_USER_DATA(attr);
363
364 if (len >= sizeof(inst->srv.description)) {
365 LOG_DBG("Output desc was clipped from length %u to %zu", len,
366 sizeof(inst->srv.description) - 1);
367 /* We just clip the string value if it's too long */
368 len = (uint16_t)sizeof(inst->srv.description) - 1;
369 }
370
371 if (memcmp(buf, inst->srv.description, len)) {
372 memcpy(inst->srv.description, buf, len);
373 inst->srv.description[len] = '\0';
374
375 value_changed(inst, AICS_NOTIFY_DESCRIPTION);
376
377 if (inst->srv.cb && inst->srv.cb->description) {
378 inst->srv.cb->description(inst, 0,
379 inst->srv.description);
380 } else {
381 LOG_DBG("Callback not registered for instance %p", inst);
382 }
383 }
384
385 LOG_DBG("%s", inst->srv.description);
386
387 return len;
388 }
389
aics_write(struct bt_aics * inst,ssize_t (* write)(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags),const void * buf,uint16_t len)390 static int aics_write(struct bt_aics *inst,
391 ssize_t (*write)(struct bt_conn *conn,
392 const struct bt_gatt_attr *attr,
393 const void *buf, uint16_t len,
394 uint16_t offset, uint8_t flags),
395 const void *buf, uint16_t len)
396 {
397 struct bt_audio_attr_user_data user_data = {
398 .user_data = inst,
399 };
400 struct bt_gatt_attr attr = {
401 .user_data = &user_data,
402 };
403 int err;
404
405 err = write(NULL, &attr, buf, len, 0, 0);
406 if (err < 0) {
407 return err;
408 }
409
410 return 0;
411 }
412
413 #if defined(CONFIG_BT_AICS)
read_description(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)414 static ssize_t read_description(struct bt_conn *conn,
415 const struct bt_gatt_attr *attr, void *buf,
416 uint16_t len, uint16_t offset)
417 {
418 struct bt_aics *inst = BT_AUDIO_CHRC_USER_DATA(attr);
419
420 LOG_DBG("%s", inst->srv.description);
421
422 return bt_gatt_attr_read(conn, attr, buf, len, offset,
423 &inst->srv.description, strlen(inst->srv.description));
424 }
425
426 /************************ PUBLIC API ************************/
bt_aics_svc_decl_get(struct bt_aics * aics)427 void *bt_aics_svc_decl_get(struct bt_aics *aics)
428 {
429 CHECKIF(!aics) {
430 LOG_DBG("NULL instance");
431 return NULL;
432 }
433
434 return aics->srv.service_p->attrs;
435 }
436
prepare_aics_instances(void)437 static void prepare_aics_instances(void)
438 {
439 for (int i = 0; i < ARRAY_SIZE(aics_insts); i++) {
440 aics_insts[i].srv.service_p = &aics_service_list[i];
441 }
442 }
443
bt_aics_register(struct bt_aics * aics,struct bt_aics_register_param * param)444 int bt_aics_register(struct bt_aics *aics, struct bt_aics_register_param *param)
445 {
446 int err;
447 static bool instance_prepared;
448
449 CHECKIF(!aics) {
450 LOG_DBG("NULL aics pointer");
451 return -ENOTCONN;
452 }
453
454 CHECKIF(!param) {
455 LOG_DBG("NULL param");
456 return -EINVAL;
457 }
458
459 if (!instance_prepared) {
460 prepare_aics_instances();
461 instance_prepared = true;
462 }
463
464 CHECKIF(aics->srv.initialized) {
465 return -EALREADY;
466 }
467
468 CHECKIF(param->mute > BT_AICS_STATE_MUTE_DISABLED) {
469 LOG_DBG("Invalid AICS mute value: %u", param->mute);
470 return -EINVAL;
471 }
472
473 CHECKIF(param->gain_mode > BT_AICS_MODE_AUTO) {
474 LOG_DBG("Invalid AICS mode value: %u", param->gain_mode);
475 return -EINVAL;
476 }
477
478 CHECKIF(param->type > BT_AICS_INPUT_TYPE_STREAMING) {
479 LOG_DBG("Invalid AICS input type value: %u", param->type);
480 return -EINVAL;
481 }
482
483 CHECKIF(param->units == 0) {
484 LOG_DBG("AICS units value shall not be 0");
485 return -EINVAL;
486 }
487
488 CHECKIF(!(param->min_gain <= param->max_gain)) {
489 LOG_DBG("AICS min gain (%d) shall be lower than or equal to max gain (%d)",
490 param->min_gain, param->max_gain);
491 return -EINVAL;
492 }
493
494 CHECKIF(param->gain < param->min_gain || param->gain > param->max_gain) {
495 LOG_DBG("AICS gain (%d) shall be not lower than min gain (%d) "
496 "or higher than max gain (%d)",
497 param->gain, param->min_gain, param->max_gain);
498 return -EINVAL;
499 }
500
501 aics->srv.state.gain = param->gain;
502 aics->srv.state.mute = param->mute;
503 aics->srv.state.gain_mode = param->gain_mode;
504 aics->srv.gain_settings.units = param->units;
505 aics->srv.gain_settings.minimum = param->min_gain;
506 aics->srv.gain_settings.maximum = param->max_gain;
507 aics->srv.type = param->type;
508 aics->srv.status = param->status ? BT_AICS_STATUS_ACTIVE : BT_AICS_STATUS_INACTIVE;
509 aics->srv.cb = param->cb;
510
511 atomic_clear(aics->srv.notify);
512 k_work_init_delayable(&aics->srv.notify_work, notify_work_handler);
513
514 if (param->description) {
515 strncpy(aics->srv.description, param->description,
516 sizeof(aics->srv.description) - 1);
517 /* strncpy may not always null-terminate */
518 aics->srv.description[sizeof(aics->srv.description) - 1] = '\0';
519 if (IS_ENABLED(CONFIG_BT_AICS_LOG_LEVEL_DBG) &&
520 strcmp(aics->srv.description, param->description)) {
521 LOG_DBG("Input desc clipped to %s", aics->srv.description);
522 }
523 }
524
525 /* Iterate over the attributes in AICS (starting from i = 1 to skip the
526 * service declaration) to find the BT_UUID_AICS_DESCRIPTION and update
527 * the characteristic value (at [i]), update that with the write
528 * permission and callback, and also update the characteristic
529 * declaration (always found at [i - 1]) with the
530 * BT_GATT_CHRC_WRITE_WITHOUT_RESP property.
531 */
532 if (param->desc_writable) {
533 for (int i = 1; i < aics->srv.service_p->attr_count; i++) {
534 struct bt_gatt_attr *attr;
535
536 attr = &aics->srv.service_p->attrs[i];
537
538 if (!bt_uuid_cmp(attr->uuid, BT_UUID_AICS_DESCRIPTION)) {
539 /* Update attr and chrc to be writable */
540 struct bt_gatt_chrc *chrc;
541
542 chrc = aics->srv.service_p->attrs[i - 1].user_data;
543 attr->perm |= BT_GATT_PERM_WRITE_ENCRYPT;
544 chrc->properties |= BT_GATT_CHRC_WRITE_WITHOUT_RESP;
545
546 break;
547 }
548 }
549 }
550
551 err = bt_gatt_service_register(aics->srv.service_p);
552 if (err) {
553 LOG_DBG("Could not register AICS service");
554 return err;
555 }
556
557 aics->srv.initialized = true;
558
559 return 0;
560 }
561
bt_aics_free_instance_get(void)562 struct bt_aics *bt_aics_free_instance_get(void)
563 {
564 if (instance_cnt >= CONFIG_BT_AICS_MAX_INSTANCE_COUNT) {
565 return NULL;
566 }
567
568 return (struct bt_aics *)&aics_insts[instance_cnt++];
569 }
570
571 /****************************** PUBLIC API ******************************/
bt_aics_deactivate(struct bt_aics * inst)572 int bt_aics_deactivate(struct bt_aics *inst)
573 {
574 CHECKIF(!inst) {
575 LOG_DBG("NULL instance");
576 return -EINVAL;
577 }
578
579 if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && inst->client_instance) {
580 return -ENOTSUP;
581 }
582
583 if (inst->srv.status == BT_AICS_STATUS_ACTIVE) {
584 inst->srv.status = BT_AICS_STATUS_INACTIVE;
585 LOG_DBG("Instance %p: Status was set to inactive", inst);
586
587 value_changed(inst, AICS_NOTIFY_STATUS);
588
589 if (inst->srv.cb && inst->srv.cb->status) {
590 inst->srv.cb->status(inst, 0, inst->srv.status);
591 } else {
592 LOG_DBG("Callback not registered for instance %p", inst);
593 }
594 }
595
596 return 0;
597 }
598
bt_aics_activate(struct bt_aics * inst)599 int bt_aics_activate(struct bt_aics *inst)
600 {
601 CHECKIF(!inst) {
602 LOG_DBG("NULL instance");
603 return -EINVAL;
604 }
605
606 if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && inst->client_instance) {
607 return -ENOTSUP;
608 }
609
610 if (inst->srv.status == BT_AICS_STATUS_INACTIVE) {
611 inst->srv.status = BT_AICS_STATUS_ACTIVE;
612 LOG_DBG("Instance %p: Status was set to active", inst);
613
614 value_changed(inst, AICS_NOTIFY_STATUS);
615
616 if (inst->srv.cb && inst->srv.cb->status) {
617 inst->srv.cb->status(inst, 0, inst->srv.status);
618 } else {
619 LOG_DBG("Callback not registered for instance %p", inst);
620 }
621 }
622
623 return 0;
624 }
625
626 #endif /* CONFIG_BT_AICS */
bt_aics_gain_set_manual_only(struct bt_aics * inst)627 int bt_aics_gain_set_manual_only(struct bt_aics *inst)
628 {
629 CHECKIF(!inst) {
630 LOG_DBG("NULL instance");
631 return -EINVAL;
632 }
633
634 inst->srv.state.gain_mode = BT_AICS_MODE_MANUAL_ONLY;
635
636 value_changed(inst, AICS_NOTIFY_STATE);
637
638 return 0;
639 }
640
bt_aics_gain_set_auto_only(struct bt_aics * inst)641 int bt_aics_gain_set_auto_only(struct bt_aics *inst)
642 {
643 CHECKIF(!inst) {
644 LOG_DBG("NULL instance");
645 return -EINVAL;
646 }
647
648 inst->srv.state.gain_mode = BT_AICS_MODE_AUTO_ONLY;
649
650 value_changed(inst, AICS_NOTIFY_STATE);
651
652 return 0;
653 }
654
bt_aics_state_get(struct bt_aics * inst)655 int bt_aics_state_get(struct bt_aics *inst)
656 {
657 CHECKIF(!inst) {
658 LOG_DBG("NULL instance");
659 return -EINVAL;
660 }
661
662 if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && inst->client_instance) {
663 return bt_aics_client_state_get(inst);
664 } else if (IS_ENABLED(CONFIG_BT_AICS) && !inst->client_instance) {
665 if (inst->srv.cb && inst->srv.cb->state) {
666 inst->srv.cb->state(inst, 0, inst->srv.state.gain,
667 inst->srv.state.mute,
668 inst->srv.state.gain_mode);
669 } else {
670 LOG_DBG("Callback not registered for instance %p", inst);
671 }
672 return 0;
673 }
674
675 return -ENOTSUP;
676 }
677
bt_aics_gain_setting_get(struct bt_aics * inst)678 int bt_aics_gain_setting_get(struct bt_aics *inst)
679 {
680 CHECKIF(!inst) {
681 LOG_DBG("NULL instance");
682 return -EINVAL;
683 }
684
685 if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && inst->client_instance) {
686 return bt_aics_client_gain_setting_get(inst);
687 } else if (IS_ENABLED(CONFIG_BT_AICS) && !inst->client_instance) {
688 if (inst->srv.cb && inst->srv.cb->gain_setting) {
689 inst->srv.cb->gain_setting(inst, 0,
690 inst->srv.gain_settings.units,
691 inst->srv.gain_settings.minimum,
692 inst->srv.gain_settings.maximum);
693 } else {
694 LOG_DBG("Callback not registered for instance %p", inst);
695 }
696 return 0;
697 }
698
699 return -ENOTSUP;
700 }
701
bt_aics_type_get(struct bt_aics * inst)702 int bt_aics_type_get(struct bt_aics *inst)
703 {
704 CHECKIF(!inst) {
705 LOG_DBG("NULL instance");
706 return -EINVAL;
707 }
708
709 if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && inst->client_instance) {
710 return bt_aics_client_type_get(inst);
711 } else if (IS_ENABLED(CONFIG_BT_AICS) && !inst->client_instance) {
712 if (inst->srv.cb && inst->srv.cb->type) {
713 inst->srv.cb->type(inst, 0, inst->srv.type);
714 } else {
715 LOG_DBG("Callback not registered for instance %p", inst);
716 }
717 return 0;
718 }
719
720 return -ENOTSUP;
721 }
722
bt_aics_status_get(struct bt_aics * inst)723 int bt_aics_status_get(struct bt_aics *inst)
724 {
725 CHECKIF(!inst) {
726 LOG_DBG("NULL instance");
727 return -EINVAL;
728 }
729
730 if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && inst->client_instance) {
731 return bt_aics_client_status_get(inst);
732 } else if (IS_ENABLED(CONFIG_BT_AICS) && !inst->client_instance) {
733 if (inst->srv.cb && inst->srv.cb->status) {
734 inst->srv.cb->status(inst, 0, inst->srv.status);
735 } else {
736 LOG_DBG("Callback not registered for instance %p", inst);
737 }
738 return 0;
739 }
740
741 return -ENOTSUP;
742 }
743
bt_aics_disable_mute(struct bt_aics * inst)744 int bt_aics_disable_mute(struct bt_aics *inst)
745 {
746 CHECKIF(!inst) {
747 LOG_DBG("NULL instance");
748 return -EINVAL;
749 }
750
751 inst->srv.state.mute = BT_AICS_STATE_MUTE_DISABLED;
752
753 value_changed(inst, AICS_NOTIFY_STATE);
754
755 return 0;
756 }
757
bt_aics_unmute(struct bt_aics * inst)758 int bt_aics_unmute(struct bt_aics *inst)
759 {
760 CHECKIF(!inst) {
761 LOG_DBG("NULL instance");
762 return -EINVAL;
763 }
764
765 if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && inst->client_instance) {
766 return bt_aics_client_unmute(inst);
767 } else if (IS_ENABLED(CONFIG_BT_AICS) && !inst->client_instance) {
768 struct bt_aics_control cp;
769
770 cp.opcode = BT_AICS_OPCODE_UNMUTE;
771 cp.counter = inst->srv.state.change_counter;
772
773 return aics_write(inst, write_aics_control, &cp, sizeof(cp));
774 }
775
776 return -ENOTSUP;
777 }
778
bt_aics_mute(struct bt_aics * inst)779 int bt_aics_mute(struct bt_aics *inst)
780 {
781 CHECKIF(!inst) {
782 LOG_DBG("NULL instance");
783 return -EINVAL;
784 }
785
786 if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && inst->client_instance) {
787 return bt_aics_client_mute(inst);
788 } else if (IS_ENABLED(CONFIG_BT_AICS) && !inst->client_instance) {
789 struct bt_aics_control cp;
790
791 cp.opcode = BT_AICS_OPCODE_MUTE;
792 cp.counter = inst->srv.state.change_counter;
793
794 return aics_write(inst, write_aics_control, &cp, sizeof(cp));
795 }
796
797 return -ENOTSUP;
798 }
799
bt_aics_manual_gain_set(struct bt_aics * inst)800 int bt_aics_manual_gain_set(struct bt_aics *inst)
801 {
802 CHECKIF(!inst) {
803 LOG_DBG("NULL instance");
804 return -EINVAL;
805 }
806
807 if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && inst->client_instance) {
808 return bt_aics_client_manual_gain_set(inst);
809 } else if (IS_ENABLED(CONFIG_BT_AICS) && !inst->client_instance) {
810 struct bt_aics_control cp;
811
812 cp.opcode = BT_AICS_OPCODE_SET_MANUAL;
813 cp.counter = inst->srv.state.change_counter;
814
815 return aics_write(inst, write_aics_control, &cp, sizeof(cp));
816 }
817
818 return -ENOTSUP;
819 }
820
bt_aics_automatic_gain_set(struct bt_aics * inst)821 int bt_aics_automatic_gain_set(struct bt_aics *inst)
822 {
823 CHECKIF(!inst) {
824 LOG_DBG("NULL instance");
825 return -EINVAL;
826 }
827
828 if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && inst->client_instance) {
829 return bt_aics_client_automatic_gain_set(inst);
830 } else if (IS_ENABLED(CONFIG_BT_AICS) && !inst->client_instance) {
831 struct bt_aics_control cp;
832
833 cp.opcode = BT_AICS_OPCODE_SET_AUTO;
834 cp.counter = inst->srv.state.change_counter;
835
836 return aics_write(inst, write_aics_control, &cp, sizeof(cp));
837 }
838
839 return -ENOTSUP;
840 }
841
bt_aics_gain_set(struct bt_aics * inst,int8_t gain)842 int bt_aics_gain_set(struct bt_aics *inst, int8_t gain)
843 {
844 CHECKIF(!inst) {
845 LOG_DBG("NULL instance");
846 return -EINVAL;
847 }
848
849 if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && inst->client_instance) {
850 return bt_aics_client_gain_set(inst, gain);
851 } else if (IS_ENABLED(CONFIG_BT_AICS) && !inst->client_instance) {
852 struct bt_aics_gain_control cp;
853
854 cp.cp.opcode = BT_AICS_OPCODE_SET_GAIN;
855 cp.cp.counter = inst->srv.state.change_counter;
856 cp.gain_setting = gain;
857
858 return aics_write(inst, write_aics_control, &cp, sizeof(cp));
859 }
860
861 return -ENOTSUP;
862 }
863
bt_aics_description_get(struct bt_aics * inst)864 int bt_aics_description_get(struct bt_aics *inst)
865 {
866 CHECKIF(!inst) {
867 LOG_DBG("NULL instance");
868 return -EINVAL;
869 }
870
871 if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && inst->client_instance) {
872 return bt_aics_client_description_get(inst);
873 } else if (IS_ENABLED(CONFIG_BT_AICS) && !inst->client_instance) {
874 if (inst->srv.cb && inst->srv.cb->description) {
875 inst->srv.cb->description(inst, 0,
876 inst->srv.description);
877 } else {
878 LOG_DBG("Callback not registered for instance %p", inst);
879 }
880 return 0;
881 }
882
883 return -ENOTSUP;
884 }
885
bt_aics_description_set(struct bt_aics * inst,const char * description)886 int bt_aics_description_set(struct bt_aics *inst, const char *description)
887 {
888 CHECKIF(!inst) {
889 LOG_DBG("NULL instance");
890 return -EINVAL;
891 }
892
893 CHECKIF(!description) {
894 LOG_DBG("NULL description");
895 return -EINVAL;
896 }
897
898 if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && inst->client_instance) {
899 return bt_aics_client_description_set(inst, description);
900 } else if (IS_ENABLED(CONFIG_BT_AICS) && !inst->client_instance) {
901 return aics_write(inst, write_description, description, strlen(description));
902 }
903
904 return -ENOTSUP;
905 }
906