1 /*
2 * Copyright (c) 2022 Codecoup
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdlib.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/sys/check.h>
10
11 #include <zephyr/device.h>
12
13 #include <zephyr/bluetooth/bluetooth.h>
14 #include <zephyr/bluetooth/gatt.h>
15 #include <zephyr/bluetooth/audio/audio.h>
16 #include <zephyr/bluetooth/audio/pacs.h>
17 #include <zephyr/bluetooth/audio/has.h>
18 #include <zephyr/sys/check.h>
19
20 #include "../bluetooth/host/conn_internal.h"
21 #include "../bluetooth/host/hci_core.h"
22 #include "audio_internal.h"
23 #include "has_internal.h"
24
25 #include <zephyr/logging/log.h>
26
27 LOG_MODULE_REGISTER(bt_has, CONFIG_BT_HAS_LOG_LEVEL);
28
29 /* The service allows operations with paired devices only.
30 * For now, the context is kept for connected devices only, thus the number of contexts is
31 * equal to maximum number of simultaneous connections to paired devices.
32 */
33 #define BT_HAS_MAX_CONN MIN(CONFIG_BT_MAX_CONN, CONFIG_BT_MAX_PAIRED)
34
35 #define BITS_CHANGED(_new_value, _old_value) ((_new_value) ^ (_old_value))
36 #define FEATURE_DEVICE_TYPE_UNCHANGED(_new_value) \
37 !BITS_CHANGED(_new_value, (has.features & BT_HAS_FEAT_HEARING_AID_TYPE_MASK))
38 #define FEATURE_SYNC_SUPPORT_UNCHANGED(_new_value) \
39 !BITS_CHANGED(_new_value, ((has.features & BT_HAS_FEAT_PRESET_SYNC_SUPP) != 0 ? 1 : 0))
40 #define FEATURE_IND_PRESETS_UNCHANGED(_new_value) \
41 !BITS_CHANGED(_new_value, ((has.features & BT_HAS_FEAT_INDEPENDENT_PRESETS) != 0 ? 1 : 0))
42
43 static struct bt_has has;
44
45 #if defined(CONFIG_BT_HAS_ACTIVE_PRESET_INDEX)
active_preset_index_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)46 static void active_preset_index_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
47 {
48 LOG_DBG("attr %p value 0x%04x", attr, value);
49 }
50 #endif /* CONFIG_BT_HAS_ACTIVE_PRESET_INDEX */
51
52 #if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
53 struct has_client;
54
55 /* Active preset notification work */
56 static void active_preset_work_process(struct k_work *work);
57 static K_WORK_DEFINE(active_preset_work, active_preset_work_process);
58
59 static void process_control_point_work(struct k_work *work);
60 static void read_presets_req_free(struct has_client *client);
61 static ssize_t write_control_point(struct bt_conn *conn, const struct bt_gatt_attr *attr,
62 const void *data, uint16_t len, uint16_t offset, uint8_t flags);
63
preset_cp_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)64 static void preset_cp_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
65 {
66 LOG_DBG("attr %p value 0x%04x", attr, value);
67 }
68
read_active_preset_index(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)69 static ssize_t read_active_preset_index(struct bt_conn *conn, const struct bt_gatt_attr *attr,
70 void *buf, uint16_t len, uint16_t offset)
71 {
72 LOG_DBG("conn %p attr %p offset %d", (void *)conn, attr, offset);
73
74 if (offset > sizeof(has.active_index)) {
75 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
76 }
77
78 return bt_gatt_attr_read(conn, attr, buf, len, offset, &has.active_index,
79 sizeof(has.active_index));
80 }
81 #endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
82
83 #if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
features_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)84 static void features_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
85 {
86 LOG_DBG("attr %p value 0x%04x", attr, value);
87 }
88 #endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
89
read_features(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)90 static ssize_t read_features(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
91 uint16_t len, uint16_t offset)
92 {
93 LOG_DBG("conn %p attr %p offset %d", (void *)conn, attr, offset);
94
95 if (offset > sizeof(has.features)) {
96 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
97 }
98
99 return bt_gatt_attr_read(conn, attr, buf, len, offset, &has.features,
100 sizeof(has.features));
101 }
102
103 #if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
104 #define BT_HAS_CHR_FEATURES \
105 BT_AUDIO_CHRC(BT_UUID_HAS_HEARING_AID_FEATURES, \
106 BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
107 BT_GATT_PERM_READ_ENCRYPT, \
108 read_features, NULL, NULL), \
109 BT_AUDIO_CCC(features_cfg_changed),
110 #else
111 #define BT_HAS_CHR_FEATURES \
112 BT_AUDIO_CHRC(BT_UUID_HAS_HEARING_AID_FEATURES, \
113 BT_GATT_CHRC_READ, \
114 BT_GATT_PERM_READ_ENCRYPT, \
115 read_features, NULL, NULL),
116 #endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
117
118
119
120 #if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
121 #if defined(CONFIG_BT_HAS_PRESET_CONTROL_POINT_NOTIFIABLE)
122 #define BT_HAS_CHR_PRESET_CONTROL_POINT \
123 BT_AUDIO_CHRC(BT_UUID_HAS_PRESET_CONTROL_POINT, \
124 BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE | BT_GATT_CHRC_NOTIFY, \
125 BT_GATT_PERM_WRITE_ENCRYPT, \
126 NULL, write_control_point, NULL), \
127 BT_AUDIO_CCC(preset_cp_cfg_changed),
128 #else
129 #define BT_HAS_CHR_PRESET_CONTROL_POINT \
130 BT_AUDIO_CHRC(BT_UUID_HAS_PRESET_CONTROL_POINT, \
131 BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE, \
132 BT_GATT_PERM_WRITE_ENCRYPT, \
133 NULL, write_control_point, NULL), \
134 BT_AUDIO_CCC(preset_cp_cfg_changed),
135 #endif /* CONFIG_BT_HAS_PRESET_CONTROL_POINT_NOTIFIABLE */
136 #else
137 #define BT_HAS_CHR_PRESET_CONTROL_POINT
138 #endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
139
140 #if defined(CONFIG_BT_HAS_ACTIVE_PRESET_INDEX)
141 #define BT_HAS_CHR_ACTIVE_PRESET_INDEX \
142 BT_AUDIO_CHRC(BT_UUID_HAS_ACTIVE_PRESET_INDEX, \
143 BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
144 BT_GATT_PERM_READ_ENCRYPT, \
145 read_active_preset_index, NULL, NULL), \
146 BT_AUDIO_CCC(active_preset_index_cfg_changed)
147 #else
148 #define BT_HAS_CHR_ACTIVE_PRESET_INDEX
149 #endif /* CONFIG_BT_HAS_ACTIVE_PRESET_INDEX */
150
151 /* Hearing Access Service GATT Attributes */
152 static struct bt_gatt_attr has_attrs[] = {
153 BT_GATT_PRIMARY_SERVICE(BT_UUID_HAS),
154 BT_HAS_CHR_FEATURES
155 BT_HAS_CHR_PRESET_CONTROL_POINT
156 BT_HAS_CHR_ACTIVE_PRESET_INDEX
157 };
158
159 static struct bt_gatt_service has_svc;
160
161 #if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
162 /* Features notification work */
163 static void features_work_process(struct k_work *work);
164 static K_WORK_DEFINE(features_work, features_work_process);
165
166 #define FEATURES_ATTR &has_attrs[2]
167 #if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
168 #define PRESET_CONTROL_POINT_ATTR &has_attrs[5]
169 #define ACTIVE_PRESET_INDEX_ATTR &has_attrs[8]
170 #endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
171 #else
172 #if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
173 #define PRESET_CONTROL_POINT_ATTR &has_attrs[4]
174 #define ACTIVE_PRESET_INDEX_ATTR &has_attrs[7]
175 #endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
176 #endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
177
178 #if defined(CONFIG_BT_HAS_PRESET_SUPPORT) || defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
179 enum {
180 FLAG_ACTIVE_INDEX_CHANGED,
181 FLAG_CONTROL_POINT_NOTIFY,
182 FLAG_FEATURES_CHANGED,
183 FLAG_NUM,
184 };
185
186 static struct has_client {
187 struct bt_conn *conn;
188 #if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
189 union {
190 struct bt_gatt_indicate_params ind;
191 #if defined(CONFIG_BT_HAS_PRESET_CONTROL_POINT_NOTIFIABLE)
192 struct bt_gatt_notify_params ntf;
193 #endif /* CONFIG_BT_HAS_PRESET_CONTROL_POINT_NOTIFIABLE */
194 } params;
195
196 uint8_t preset_changed_index_next;
197 struct bt_has_cp_read_presets_req read_presets_req;
198 struct k_work control_point_work;
199 #endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
200 ATOMIC_DEFINE(flags, FLAG_NUM);
201 } has_client_list[BT_HAS_MAX_CONN];
202
client_get_or_new(struct bt_conn * conn)203 static struct has_client *client_get_or_new(struct bt_conn *conn)
204 {
205 struct has_client *client = NULL;
206
207 for (size_t i = 0; i < ARRAY_SIZE(has_client_list); i++) {
208 if (conn == has_client_list[i].conn) {
209 return &has_client_list[i];
210 }
211
212 /* first free slot */
213 if (!client && !has_client_list[i].conn) {
214 client = &has_client_list[i];
215 }
216 }
217
218 __ASSERT(client, "failed to get client for conn %p", (void *)conn);
219
220 client->conn = bt_conn_ref(conn);
221
222 #if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
223 k_work_init(&client->control_point_work, process_control_point_work);
224 #endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
225
226 return client;
227 }
228
client_free(struct has_client * client)229 static void client_free(struct has_client *client)
230 {
231 #if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
232 (void)k_work_cancel(&client->control_point_work);
233 read_presets_req_free(client);
234 #endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
235
236 atomic_clear_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY);
237 atomic_clear_bit(client->flags, FLAG_ACTIVE_INDEX_CHANGED);
238 atomic_clear_bit(client->flags, FLAG_FEATURES_CHANGED);
239
240 bt_conn_unref(client->conn);
241
242 client->conn = NULL;
243 }
244
client_get(struct bt_conn * conn)245 static struct has_client *client_get(struct bt_conn *conn)
246 {
247 for (size_t i = 0; i < ARRAY_SIZE(has_client_list); i++) {
248 if (conn == has_client_list[i].conn) {
249 return &has_client_list[i];
250 }
251 }
252
253 return NULL;
254 }
255
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)256 static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
257 {
258 struct has_client *client;
259
260 LOG_DBG("conn %p level %d err %d", (void *)conn, level, err);
261
262 if (err != BT_SECURITY_ERR_SUCCESS) {
263 return;
264 }
265
266 client = client_get_or_new(conn);
267 if (unlikely(!client)) {
268 LOG_ERR("Failed to allocate client");
269 return;
270 }
271
272 if (!bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
273 return;
274 }
275
276 #if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
277 /* Notify after reconnection */
278 if (atomic_test_and_clear_bit(client->flags, FLAG_ACTIVE_INDEX_CHANGED)) {
279 /* Emit active preset notification */
280 k_work_submit(&active_preset_work);
281 }
282
283 if (atomic_test_and_clear_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY)) {
284 /* Emit preset changed notifications */
285 k_work_submit(&client->control_point_work);
286 }
287 #endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
288
289 #if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
290 if (atomic_test_and_clear_bit(client->flags, FLAG_FEATURES_CHANGED)) {
291 /* Emit preset changed notifications */
292 k_work_submit(&features_work);
293 }
294 #endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
295 }
296
connected(struct bt_conn * conn,uint8_t err)297 static void connected(struct bt_conn *conn, uint8_t err)
298 {
299 struct has_client *client;
300
301 LOG_DBG("conn %p err %d", conn, err);
302
303 if (err != 0 || !bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
304 return;
305 }
306
307 client = client_get_or_new(conn);
308 if (unlikely(!client)) {
309 LOG_ERR("Failed to allocate client");
310 return;
311 }
312 }
313
disconnected(struct bt_conn * conn,uint8_t reason)314 static void disconnected(struct bt_conn *conn, uint8_t reason)
315 {
316 struct has_client *client;
317
318 LOG_DBG("conn %p reason %d", (void *)conn, reason);
319
320 client = client_get(conn);
321 if (client) {
322 client_free(client);
323 }
324 }
325
326 BT_CONN_CB_DEFINE(conn_cb) = {
327 .connected = connected,
328 .disconnected = disconnected,
329 .security_changed = security_changed,
330 };
331 #endif /* CONFIG_BT_HAS_PRESET_SUPPORT || CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
332
333 #if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
334 /* HAS internal preset representation */
335 static struct has_preset {
336 uint8_t index;
337 enum bt_has_properties properties;
338 #if defined(CONFIG_BT_HAS_PRESET_NAME_DYNAMIC)
339 char name[BT_HAS_PRESET_NAME_MAX + 1]; /* +1 byte for NULL-terminator */
340 #else
341 const char *name;
342 #endif /* CONFIG_BT_HAS_PRESET_NAME_DYNAMIC */
343 const struct bt_has_preset_ops *ops;
344 } has_preset_list[CONFIG_BT_HAS_PRESET_COUNT];
345
346 /* Number of registered presets */
347 static uint8_t has_preset_num;
348
read_presets_req_pending_cp(const struct has_client * client)349 static bool read_presets_req_pending_cp(const struct has_client *client)
350 {
351 return client->read_presets_req.num_presets > 0;
352 }
353
read_presets_req_free(struct has_client * client)354 static void read_presets_req_free(struct has_client *client)
355 {
356 client->read_presets_req.num_presets = 0;
357 }
358
359 typedef uint8_t (*preset_func_t)(const struct has_preset *preset, void *user_data);
360
preset_foreach(uint8_t start_index,uint8_t end_index,preset_func_t func,void * user_data)361 static void preset_foreach(uint8_t start_index, uint8_t end_index, preset_func_t func,
362 void *user_data)
363 {
364 for (size_t i = 0; i < ARRAY_SIZE(has_preset_list); i++) {
365 const struct has_preset *preset = &has_preset_list[i];
366
367 if (preset->index < start_index) {
368 continue;
369 }
370
371 if (preset->index > end_index) {
372 return;
373 }
374
375 if (func(preset, user_data) == BT_HAS_PRESET_ITER_STOP) {
376 return;
377 }
378 }
379 }
380
preset_found(const struct has_preset * preset,void * user_data)381 static uint8_t preset_found(const struct has_preset *preset, void *user_data)
382 {
383 const struct has_preset **found = user_data;
384
385 *found = preset;
386
387 return BT_HAS_PRESET_ITER_STOP;
388 }
389
preset_index_compare(const void * p1,const void * p2)390 static int preset_index_compare(const void *p1, const void *p2)
391 {
392 const struct has_preset *preset_1 = p1;
393 const struct has_preset *preset_2 = p2;
394
395 if (preset_1->index == BT_HAS_PRESET_INDEX_NONE) {
396 return 1;
397 }
398
399 if (preset_2->index == BT_HAS_PRESET_INDEX_NONE) {
400 return -1;
401 }
402
403 return preset_1->index - preset_2->index;
404 }
405
preset_alloc(uint8_t index,enum bt_has_properties properties,const char * name,const struct bt_has_preset_ops * ops)406 static struct has_preset *preset_alloc(uint8_t index, enum bt_has_properties properties,
407 const char *name, const struct bt_has_preset_ops *ops)
408 {
409 struct has_preset *preset = NULL;
410
411 if (has_preset_num < ARRAY_SIZE(has_preset_list)) {
412 preset = &has_preset_list[has_preset_num];
413 preset->index = index;
414 preset->properties = properties;
415 #if defined(CONFIG_BT_HAS_PRESET_NAME_DYNAMIC)
416 utf8_lcpy(preset->name, name, ARRAY_SIZE(preset->name));
417 #else
418 preset->name = name;
419 #endif /* CONFIG_BT_HAS_PRESET_NAME_DYNAMIC */
420 preset->ops = ops;
421
422 has_preset_num++;
423
424 /* sort the presets in index ascending order */
425 qsort(has_preset_list, has_preset_num, sizeof(*preset), preset_index_compare);
426 }
427
428 return preset;
429 }
430
preset_free(struct has_preset * preset)431 static void preset_free(struct has_preset *preset)
432 {
433 preset->index = BT_HAS_PRESET_INDEX_NONE;
434
435 /* sort the presets in index ascending order */
436 if (has_preset_num > 1) {
437 qsort(has_preset_list, has_preset_num, sizeof(*preset), preset_index_compare);
438 }
439
440 has_preset_num--;
441 }
442
control_point_ntf_complete(struct bt_conn * conn,void * user_data)443 static void control_point_ntf_complete(struct bt_conn *conn, void *user_data)
444 {
445 struct has_client *client = client_get(conn);
446
447 LOG_DBG("conn %p", (void *)conn);
448
449 /* Resubmit if needed */
450 if (client != NULL &&
451 (read_presets_req_pending_cp(client) ||
452 atomic_test_and_clear_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY))) {
453 k_work_submit(&client->control_point_work);
454 }
455 }
456
control_point_ind_complete(struct bt_conn * conn,struct bt_gatt_indicate_params * params,uint8_t err)457 static void control_point_ind_complete(struct bt_conn *conn,
458 struct bt_gatt_indicate_params *params,
459 uint8_t err)
460 {
461 if (err) {
462 /* TODO: Handle error somehow */
463 LOG_ERR("conn %p err 0x%02x", (void *)conn, err);
464 }
465
466 control_point_ntf_complete(conn, NULL);
467 }
468
control_point_send(struct has_client * client,struct net_buf_simple * buf)469 static int control_point_send(struct has_client *client, struct net_buf_simple *buf)
470 {
471 #if defined(CONFIG_BT_HAS_PRESET_CONTROL_POINT_NOTIFIABLE)
472 if (bt_eatt_count(client->conn) > 0 &&
473 bt_gatt_is_subscribed(client->conn, PRESET_CONTROL_POINT_ATTR, BT_GATT_CCC_NOTIFY)) {
474 client->params.ntf.attr = PRESET_CONTROL_POINT_ATTR;
475 client->params.ntf.func = control_point_ntf_complete;
476 client->params.ntf.data = buf->data;
477 client->params.ntf.len = buf->len;
478
479 return bt_gatt_notify_cb(client->conn, &client->params.ntf);
480 }
481 #endif /* CONFIG_BT_HAS_PRESET_CONTROL_POINT_NOTIFIABLE */
482
483 if (bt_gatt_is_subscribed(client->conn, PRESET_CONTROL_POINT_ATTR, BT_GATT_CCC_INDICATE)) {
484 client->params.ind.attr = PRESET_CONTROL_POINT_ATTR;
485 client->params.ind.func = control_point_ind_complete;
486 client->params.ind.destroy = NULL;
487 client->params.ind.data = buf->data;
488 client->params.ind.len = buf->len;
489
490 return bt_gatt_indicate(client->conn, &client->params.ind);
491 }
492
493 return -ECANCELED;
494 }
495
control_point_send_all(struct net_buf_simple * buf)496 static int control_point_send_all(struct net_buf_simple *buf)
497 {
498 int result = 0;
499
500 for (size_t i = 0; i < ARRAY_SIZE(has_client_list); i++) {
501 struct has_client *client = &has_client_list[i];
502 int err;
503
504 if (!client->conn) {
505 /* Mark preset changed operation as pending */
506 atomic_set_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY);
507 /* For simplicity we simply start with the first index,
508 * rather than keeping detailed logs of which clients
509 * have knowledge of which presets
510 */
511 client->preset_changed_index_next = BT_HAS_PRESET_INDEX_FIRST;
512 continue;
513 }
514
515 if (!bt_gatt_is_subscribed(client->conn, PRESET_CONTROL_POINT_ATTR,
516 BT_GATT_CCC_NOTIFY | BT_GATT_CCC_INDICATE)) {
517 continue;
518 }
519
520 err = control_point_send(client, buf);
521 if (err) {
522 result = err;
523 /* continue anyway */
524 }
525 }
526
527 return result;
528 }
529
bt_has_cp_read_preset_rsp(struct has_client * client,const struct has_preset * preset,bool is_last)530 static int bt_has_cp_read_preset_rsp(struct has_client *client, const struct has_preset *preset,
531 bool is_last)
532 {
533 struct bt_has_cp_hdr *hdr;
534 struct bt_has_cp_read_preset_rsp *rsp;
535
536 NET_BUF_SIMPLE_DEFINE(buf, sizeof(*hdr) + sizeof(*rsp) + BT_HAS_PRESET_NAME_MAX);
537
538 LOG_DBG("conn %p preset %p is_last 0x%02x", (void *)client->conn, preset, is_last);
539
540 hdr = net_buf_simple_add(&buf, sizeof(*hdr));
541 hdr->opcode = BT_HAS_OP_READ_PRESET_RSP;
542 rsp = net_buf_simple_add(&buf, sizeof(*rsp));
543 rsp->is_last = is_last ? 0x01 : 0x00;
544 rsp->index = preset->index;
545 rsp->properties = preset->properties;
546 net_buf_simple_add_mem(&buf, preset->name, strlen(preset->name));
547
548 return control_point_send(client, &buf);
549 }
550
get_prev_preset_index(const struct has_preset * preset)551 static uint8_t get_prev_preset_index(const struct has_preset *preset)
552 {
553 const struct has_preset *prev = NULL;
554
555 for (size_t i = 0; i < ARRAY_SIZE(has_preset_list); i++) {
556 const struct has_preset *tmp = &has_preset_list[i];
557
558 if (tmp->index == BT_HAS_PRESET_INDEX_NONE || tmp == preset) {
559 break;
560 }
561
562 prev = tmp;
563 }
564
565 return prev ? prev->index : BT_HAS_PRESET_INDEX_NONE;
566 }
567
preset_changed_prepare(struct net_buf_simple * buf,uint8_t change_id,uint8_t is_last)568 static void preset_changed_prepare(struct net_buf_simple *buf, uint8_t change_id, uint8_t is_last)
569 {
570 struct bt_has_cp_hdr *hdr;
571 struct bt_has_cp_preset_changed *preset_changed;
572
573 hdr = net_buf_simple_add(buf, sizeof(*hdr));
574 hdr->opcode = BT_HAS_OP_PRESET_CHANGED;
575 preset_changed = net_buf_simple_add(buf, sizeof(*preset_changed));
576 preset_changed->change_id = change_id;
577 preset_changed->is_last = is_last;
578 }
579
bt_has_cp_generic_update(struct has_client * client,const struct has_preset * preset,uint8_t is_last)580 static int bt_has_cp_generic_update(struct has_client *client, const struct has_preset *preset,
581 uint8_t is_last)
582 {
583 struct bt_has_cp_generic_update *generic_update;
584
585 NET_BUF_SIMPLE_DEFINE(buf, sizeof(struct bt_has_cp_hdr) +
586 sizeof(struct bt_has_cp_preset_changed) +
587 sizeof(struct bt_has_cp_generic_update) + BT_HAS_PRESET_NAME_MAX);
588
589 preset_changed_prepare(&buf, BT_HAS_CHANGE_ID_GENERIC_UPDATE, is_last);
590
591 generic_update = net_buf_simple_add(&buf, sizeof(*generic_update));
592 generic_update->prev_index = get_prev_preset_index(preset);
593 generic_update->index = preset->index;
594 generic_update->properties = preset->properties;
595 net_buf_simple_add_mem(&buf, preset->name, strlen(preset->name));
596
597 if (client) {
598 return control_point_send(client, &buf);
599 } else {
600 return control_point_send_all(&buf);
601 }
602 }
603
process_control_point_work(struct k_work * work)604 static void process_control_point_work(struct k_work *work)
605 {
606 struct has_client *client = CONTAINER_OF(work, struct has_client, control_point_work);
607 int err;
608
609 if (!client->conn) {
610 return;
611 }
612
613 if (read_presets_req_pending_cp(client)) {
614 const struct has_preset *preset = NULL;
615 bool is_last = true;
616
617 preset_foreach(client->read_presets_req.start_index, BT_HAS_PRESET_INDEX_LAST,
618 preset_found, &preset);
619
620 if (unlikely(preset == NULL)) {
621 (void)bt_has_cp_read_preset_rsp(client, NULL, 0x01);
622
623 return;
624 }
625
626 if (client->read_presets_req.num_presets > 1) {
627 const struct has_preset *next = NULL;
628
629 preset_foreach(preset->index + 1, BT_HAS_PRESET_INDEX_LAST,
630 preset_found, &next);
631
632 is_last = next == NULL;
633
634 }
635
636 err = bt_has_cp_read_preset_rsp(client, preset, is_last);
637 if (err) {
638 LOG_ERR("bt_has_cp_read_preset_rsp failed (err %d)", err);
639 }
640
641 if (err || is_last) {
642 read_presets_req_free(client);
643 } else {
644 client->read_presets_req.start_index = preset->index + 1;
645 client->read_presets_req.num_presets--;
646 }
647 } else if (atomic_test_and_clear_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY)) {
648 const struct has_preset *preset = NULL;
649 const struct has_preset *next = NULL;
650 bool is_last = true;
651
652 preset_foreach(client->preset_changed_index_next,
653 BT_HAS_PRESET_INDEX_LAST, preset_found, &preset);
654
655 if (preset == NULL) {
656 return;
657 }
658
659 preset_foreach(preset->index + 1, BT_HAS_PRESET_INDEX_LAST,
660 preset_found, &next);
661
662 is_last = next == NULL;
663
664 err = bt_has_cp_generic_update(client, preset, is_last);
665 if (err) {
666 LOG_ERR("bt_has_cp_read_preset_rsp failed (err %d)", err);
667 }
668
669 if (err || is_last) {
670 atomic_clear_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY);
671 } else {
672 client->preset_changed_index_next = preset->index + 1;
673 }
674 }
675 }
676
handle_read_preset_req(struct bt_conn * conn,struct net_buf_simple * buf)677 static uint8_t handle_read_preset_req(struct bt_conn *conn, struct net_buf_simple *buf)
678 {
679 const struct bt_has_cp_read_presets_req *req;
680 const struct has_preset *preset = NULL;
681 struct has_client *client;
682
683 if (buf->len < sizeof(*req)) {
684 return BT_HAS_ERR_INVALID_PARAM_LEN;
685 }
686
687 /* As per HAS_d1.0r00 Client Characteristic Configuration Descriptor Improperly Configured
688 * shall be returned if client writes Read Presets Request but is not registered for
689 * indications.
690 */
691 if (!bt_gatt_is_subscribed(conn, PRESET_CONTROL_POINT_ATTR, BT_GATT_CCC_INDICATE)) {
692 return BT_ATT_ERR_CCC_IMPROPER_CONF;
693 }
694
695 client = client_get(conn);
696 if (!client) {
697 return BT_ATT_ERR_UNLIKELY;
698 }
699
700 req = net_buf_simple_pull_mem(buf, sizeof(*req));
701
702 LOG_DBG("start_index %d num_presets %d", req->start_index, req->num_presets);
703
704 /* Abort if there is no preset in requested index range */
705 preset_foreach(req->start_index, BT_HAS_PRESET_INDEX_LAST, preset_found, &preset);
706
707 if (preset == NULL) {
708 return BT_ATT_ERR_OUT_OF_RANGE;
709 }
710
711 /* Reject if already in progress */
712 if (read_presets_req_pending_cp(client)) {
713 return BT_HAS_ERR_OPERATION_NOT_POSSIBLE;
714 }
715
716 /* Store the request */
717 client->read_presets_req.start_index = req->start_index;
718 client->read_presets_req.num_presets = req->num_presets;
719
720 k_work_submit(&client->control_point_work);
721
722 return 0;
723 }
724
set_preset_name(uint8_t index,const char * name,size_t len)725 static int set_preset_name(uint8_t index, const char *name, size_t len)
726 {
727 struct has_preset *preset = NULL;
728
729 LOG_DBG("index %d name_len %zu", index, len);
730
731 if (len < BT_HAS_PRESET_NAME_MIN || len > BT_HAS_PRESET_NAME_MAX) {
732 return -EINVAL;
733 }
734
735 /* Abort if there is no preset in requested index range */
736 preset_foreach(index, BT_HAS_PRESET_INDEX_LAST, preset_found, &preset);
737
738 if (preset == NULL) {
739 return -ENOENT;
740 }
741
742 if (!(preset->properties & BT_HAS_PROP_WRITABLE)) {
743 return -EPERM;
744 }
745
746 IF_ENABLED(CONFIG_BT_HAS_PRESET_NAME_DYNAMIC, (
747 __ASSERT(len < ARRAY_SIZE(preset->name), "No space for name");
748
749 (void)memcpy(preset->name, name, len);
750
751 /* NULL-terminate string */
752 preset->name[len] = '\0';
753
754 /* Properly truncate a NULL-terminated UTF-8 string */
755 utf8_trunc(preset->name);
756 ));
757
758 if (preset->ops->name_changed) {
759 preset->ops->name_changed(index, preset->name);
760 }
761
762 return bt_has_cp_generic_update(NULL, preset, BT_HAS_IS_LAST);
763 }
764
handle_write_preset_name(struct bt_conn * conn,struct net_buf_simple * buf)765 static uint8_t handle_write_preset_name(struct bt_conn *conn, struct net_buf_simple *buf)
766 {
767 const struct bt_has_cp_write_preset_name *req;
768 struct has_client *client;
769 int err;
770
771 if (buf->len < sizeof(*req)) {
772 return BT_HAS_ERR_INVALID_PARAM_LEN;
773 }
774
775 /* As per HAS_v1.0 Client Characteristic Configuration Descriptor Improperly Configured
776 * shall be returned if client writes Write Preset Name opcode but is not registered for
777 * indications.
778 */
779 if (!bt_gatt_is_subscribed(conn, PRESET_CONTROL_POINT_ATTR, BT_GATT_CCC_INDICATE)) {
780 return BT_ATT_ERR_CCC_IMPROPER_CONF;
781 }
782
783 client = client_get(conn);
784 if (!client) {
785 return BT_ATT_ERR_UNLIKELY;
786 }
787
788 req = net_buf_simple_pull_mem(buf, sizeof(*req));
789
790 err = set_preset_name(req->index, req->name, buf->len);
791 if (err == -EINVAL) {
792 return BT_HAS_ERR_INVALID_PARAM_LEN;
793 } else if (err == -ENOENT) {
794 return BT_ATT_ERR_OUT_OF_RANGE;
795 } else if (err == -EPERM) {
796 return BT_HAS_ERR_WRITE_NAME_NOT_ALLOWED;
797 } else if (err) {
798 return BT_ATT_ERR_UNLIKELY;
799 }
800
801 return BT_ATT_ERR_SUCCESS;
802 }
803
active_preset_work_process(struct k_work * work)804 static void active_preset_work_process(struct k_work *work)
805 {
806 const uint8_t active_index = bt_has_preset_active_get();
807
808 for (size_t i = 0U; i < ARRAY_SIZE(has_client_list); i++) {
809 struct has_client *client = &has_client_list[i];
810 int err;
811
812 if (client->conn == NULL) {
813 /* mark to notify on reconnect */
814 atomic_set_bit(client->flags, FLAG_ACTIVE_INDEX_CHANGED);
815 continue;
816 } else if (atomic_test_and_clear_bit(client->flags, FLAG_ACTIVE_INDEX_CHANGED)) {
817 err = bt_gatt_notify(client->conn, ACTIVE_PRESET_INDEX_ATTR, &active_index,
818 sizeof(active_index));
819 if (err != 0) {
820 LOG_DBG("failed to notify active_index for %p: %d", client->conn,
821 err);
822 }
823 }
824 }
825 }
826
preset_active_set(uint8_t index)827 static void preset_active_set(uint8_t index)
828 {
829 if (index != has.active_index) {
830 has.active_index = index;
831
832 for (size_t i = 0U; i < ARRAY_SIZE(has_client_list); i++) {
833 struct has_client *client = &has_client_list[i];
834 /* mark to notify */
835 atomic_set_bit(client->flags, FLAG_ACTIVE_INDEX_CHANGED);
836 }
837
838 /* Emit active preset notification */
839 k_work_submit(&active_preset_work);
840 }
841 }
842
preset_select(const struct has_preset * preset,bool sync)843 static uint8_t preset_select(const struct has_preset *preset, bool sync)
844 {
845 const int err = preset->ops->select(preset->index, sync);
846
847 if (err == -EINPROGRESS) {
848 /* User has to confirm once the requested preset becomes active by
849 * calling bt_has_preset_active_set.
850 */
851 return 0;
852 }
853
854 if (err == -EBUSY) {
855 return BT_HAS_ERR_OPERATION_NOT_POSSIBLE;
856 }
857
858 if (err) {
859 return BT_ATT_ERR_UNLIKELY;
860 }
861
862 preset_active_set(preset->index);
863
864 return 0;
865 }
866
handle_set_active_preset(struct net_buf_simple * buf,bool sync)867 static uint8_t handle_set_active_preset(struct net_buf_simple *buf, bool sync)
868 {
869 const struct bt_has_cp_set_active_preset *pdu;
870 const struct has_preset *preset = NULL;
871
872 if (buf->len < sizeof(*pdu)) {
873 return BT_HAS_ERR_INVALID_PARAM_LEN;
874 }
875
876 pdu = net_buf_simple_pull_mem(buf, sizeof(*pdu));
877
878 preset_foreach(pdu->index, pdu->index, preset_found, &preset);
879 if (preset == NULL) {
880 return BT_ATT_ERR_OUT_OF_RANGE;
881 }
882
883 if (!(preset->properties & BT_HAS_PROP_AVAILABLE)) {
884 return BT_HAS_ERR_OPERATION_NOT_POSSIBLE;
885 }
886
887 return preset_select(preset, sync);
888 }
889
handle_set_next_preset(bool sync)890 static uint8_t handle_set_next_preset(bool sync)
891 {
892 const struct has_preset *next_avail = NULL;
893 const struct has_preset *first_avail = NULL;
894
895 for (size_t i = 0; i < has_preset_num; i++) {
896 const struct has_preset *tmp = &has_preset_list[i];
897
898 if (tmp->index == BT_HAS_PRESET_INDEX_NONE) {
899 break;
900 }
901
902 if (!(tmp->properties & BT_HAS_PROP_AVAILABLE)) {
903 continue;
904 }
905
906 if (tmp->index < has.active_index && !first_avail) {
907 first_avail = tmp;
908 continue;
909 }
910
911 if (tmp->index > has.active_index) {
912 next_avail = tmp;
913 break;
914 }
915 }
916
917 if (next_avail) {
918 return preset_select(next_avail, sync);
919 }
920
921 if (first_avail) {
922 return preset_select(first_avail, sync);
923 }
924
925 return BT_HAS_ERR_OPERATION_NOT_POSSIBLE;
926 }
927
handle_set_prev_preset(bool sync)928 static uint8_t handle_set_prev_preset(bool sync)
929 {
930 const struct has_preset *prev_available = NULL;
931 const struct has_preset *last_available = NULL;
932
933 for (size_t i = 0; i < ARRAY_SIZE(has_preset_list); i++) {
934 const struct has_preset *tmp = &has_preset_list[i];
935
936 if (tmp->index == BT_HAS_PRESET_INDEX_NONE) {
937 break;
938 }
939
940 if (!(tmp->properties & BT_HAS_PROP_AVAILABLE)) {
941 continue;
942 }
943
944 if (tmp->index < has.active_index) {
945 prev_available = tmp;
946 continue;
947 }
948
949 if (prev_available) {
950 break;
951 }
952
953 if (tmp->index > has.active_index) {
954 last_available = tmp;
955 continue;
956 }
957 }
958
959 if (prev_available) {
960 return preset_select(prev_available, sync);
961 }
962
963 if (last_available) {
964 return preset_select(last_available, sync);
965 }
966
967 return BT_HAS_ERR_OPERATION_NOT_POSSIBLE;
968 }
969
handle_control_point_op(struct bt_conn * conn,struct net_buf_simple * buf)970 static uint8_t handle_control_point_op(struct bt_conn *conn, struct net_buf_simple *buf)
971 {
972 const struct bt_has_cp_hdr *hdr;
973
974 hdr = net_buf_simple_pull_mem(buf, sizeof(*hdr));
975
976 LOG_DBG("conn %p opcode %s (0x%02x)", (void *)conn, bt_has_op_str(hdr->opcode),
977 hdr->opcode);
978
979 switch (hdr->opcode) {
980 case BT_HAS_OP_READ_PRESET_REQ:
981 return handle_read_preset_req(conn, buf);
982 case BT_HAS_OP_WRITE_PRESET_NAME:
983 if (IS_ENABLED(CONFIG_BT_HAS_PRESET_NAME_DYNAMIC)) {
984 return handle_write_preset_name(conn, buf);
985 }
986 break;
987 case BT_HAS_OP_SET_ACTIVE_PRESET:
988 return handle_set_active_preset(buf, false);
989 case BT_HAS_OP_SET_NEXT_PRESET:
990 return handle_set_next_preset(false);
991 case BT_HAS_OP_SET_PREV_PRESET:
992 return handle_set_prev_preset(false);
993 case BT_HAS_OP_SET_ACTIVE_PRESET_SYNC:
994 if ((has.features & BT_HAS_FEAT_PRESET_SYNC_SUPP) != 0) {
995 return handle_set_active_preset(buf, true);
996 } else {
997 return BT_HAS_ERR_PRESET_SYNC_NOT_SUPP;
998 }
999 case BT_HAS_OP_SET_NEXT_PRESET_SYNC:
1000 if ((has.features & BT_HAS_FEAT_PRESET_SYNC_SUPP) != 0) {
1001 return handle_set_next_preset(true);
1002 } else {
1003 return BT_HAS_ERR_PRESET_SYNC_NOT_SUPP;
1004 }
1005 case BT_HAS_OP_SET_PREV_PRESET_SYNC:
1006 if ((has.features & BT_HAS_FEAT_PRESET_SYNC_SUPP) != 0) {
1007 return handle_set_prev_preset(true);
1008 } else {
1009 return BT_HAS_ERR_PRESET_SYNC_NOT_SUPP;
1010 }
1011 };
1012
1013 return BT_HAS_ERR_INVALID_OPCODE;
1014 }
1015
write_control_point(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * data,uint16_t len,uint16_t offset,uint8_t flags)1016 static ssize_t write_control_point(struct bt_conn *conn, const struct bt_gatt_attr *attr,
1017 const void *data, uint16_t len, uint16_t offset, uint8_t flags)
1018 {
1019 struct net_buf_simple buf;
1020 uint8_t err;
1021
1022 LOG_DBG("conn %p attr %p data %p len %d offset %d flags 0x%02x", (void *)conn, attr, data,
1023 len, offset, flags);
1024
1025 if (offset > 0) {
1026 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
1027 }
1028
1029 if (len == 0) {
1030 return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
1031 }
1032
1033 net_buf_simple_init_with_data(&buf, (void *)data, len);
1034
1035 err = handle_control_point_op(conn, &buf);
1036 if (err) {
1037 LOG_WRN("err 0x%02x", err);
1038 return BT_GATT_ERR(err);
1039 }
1040
1041 return len;
1042 }
1043
bt_has_preset_register(const struct bt_has_preset_register_param * param)1044 int bt_has_preset_register(const struct bt_has_preset_register_param *param)
1045 {
1046 struct has_preset *preset = NULL;
1047 size_t name_len;
1048
1049 CHECKIF(param == NULL) {
1050 LOG_ERR("param is NULL");
1051 return -EINVAL;
1052 }
1053
1054 CHECKIF(param->index == BT_HAS_PRESET_INDEX_NONE) {
1055 LOG_ERR("param->index is invalid");
1056 return -EINVAL;
1057 }
1058
1059 CHECKIF(param->name == NULL) {
1060 LOG_ERR("param->name is NULL");
1061 return -EINVAL;
1062 }
1063
1064 name_len = strlen(param->name);
1065
1066 CHECKIF(name_len < BT_HAS_PRESET_NAME_MIN) {
1067 LOG_ERR("param->name is too short (%zu < %u)", name_len, BT_HAS_PRESET_NAME_MIN);
1068 return -EINVAL;
1069 }
1070
1071 CHECKIF(name_len > BT_HAS_PRESET_NAME_MAX) {
1072 LOG_WRN("param->name is too long (%zu > %u)", name_len, BT_HAS_PRESET_NAME_MAX);
1073 }
1074
1075 CHECKIF(param->ops == NULL) {
1076 LOG_ERR("param->ops is NULL");
1077 return -EINVAL;
1078 }
1079
1080 CHECKIF(param->ops->select == NULL) {
1081 LOG_ERR("param->ops->select is NULL");
1082 return -EINVAL;
1083 }
1084
1085 preset_foreach(param->index, param->index, preset_found, &preset);
1086 if (preset != NULL) {
1087 return -EALREADY;
1088 }
1089
1090 preset = preset_alloc(param->index, param->properties, param->name, param->ops);
1091 if (preset == NULL) {
1092 return -ENOMEM;
1093 }
1094
1095 return bt_has_cp_generic_update(NULL, preset, BT_HAS_IS_LAST);
1096 }
1097
bt_has_preset_unregister(uint8_t index)1098 int bt_has_preset_unregister(uint8_t index)
1099 {
1100 struct has_preset *preset = NULL;
1101
1102 NET_BUF_SIMPLE_DEFINE(buf, sizeof(struct bt_has_cp_hdr) +
1103 sizeof(struct bt_has_cp_preset_changed) + sizeof(uint8_t));
1104
1105 CHECKIF(index == BT_HAS_PRESET_INDEX_NONE) {
1106 LOG_ERR("index is invalid");
1107 return -EINVAL;
1108 }
1109
1110 preset_foreach(index, index, preset_found, &preset);
1111 if (preset == NULL) {
1112 return -ENOENT;
1113 }
1114
1115 preset_changed_prepare(&buf, BT_HAS_CHANGE_ID_PRESET_DELETED, BT_HAS_IS_LAST);
1116 net_buf_simple_add_u8(&buf, preset->index);
1117
1118 preset_free(preset);
1119
1120 return control_point_send_all(&buf);
1121 }
1122
bt_has_preset_available(uint8_t index)1123 int bt_has_preset_available(uint8_t index)
1124 {
1125 struct has_preset *preset = NULL;
1126
1127 CHECKIF(index == BT_HAS_PRESET_INDEX_NONE) {
1128 LOG_ERR("index is invalid");
1129 return -EINVAL;
1130 }
1131
1132 preset_foreach(index, index, preset_found, &preset);
1133 if (preset == NULL) {
1134 return -ENOENT;
1135 }
1136
1137 /* toggle property bit if needed */
1138 if (!(preset->properties & BT_HAS_PROP_AVAILABLE)) {
1139 NET_BUF_SIMPLE_DEFINE(buf, sizeof(struct bt_has_cp_hdr) +
1140 sizeof(struct bt_has_cp_preset_changed) + sizeof(uint8_t));
1141
1142 preset->properties ^= BT_HAS_PROP_AVAILABLE;
1143
1144 preset_changed_prepare(&buf, BT_HAS_CHANGE_ID_PRESET_AVAILABLE, BT_HAS_IS_LAST);
1145 net_buf_simple_add_u8(&buf, preset->index);
1146
1147 return control_point_send_all(&buf);
1148 }
1149
1150 return 0;
1151 }
1152
bt_has_preset_unavailable(uint8_t index)1153 int bt_has_preset_unavailable(uint8_t index)
1154 {
1155 struct has_preset *preset = NULL;
1156
1157 CHECKIF(index == BT_HAS_PRESET_INDEX_NONE) {
1158 LOG_ERR("index is invalid");
1159 return -EINVAL;
1160 }
1161
1162 preset_foreach(index, index, preset_found, &preset);
1163 if (preset == NULL) {
1164 return -ENOENT;
1165 }
1166
1167 /* toggle property bit if needed */
1168 if (preset->properties & BT_HAS_PROP_AVAILABLE) {
1169 NET_BUF_SIMPLE_DEFINE(buf, sizeof(struct bt_has_cp_hdr) +
1170 sizeof(struct bt_has_cp_preset_changed) + sizeof(uint8_t));
1171
1172 preset->properties ^= BT_HAS_PROP_AVAILABLE;
1173
1174 preset_changed_prepare(&buf, BT_HAS_CHANGE_ID_PRESET_UNAVAILABLE, BT_HAS_IS_LAST);
1175 net_buf_simple_add_u8(&buf, preset->index);
1176
1177 return control_point_send_all(&buf);
1178 }
1179
1180 return 0;
1181 }
1182
1183 struct bt_has_preset_foreach_data {
1184 bt_has_preset_func_t func;
1185 void *user_data;
1186 };
1187
bt_has_preset_foreach_func(const struct has_preset * preset,void * user_data)1188 static uint8_t bt_has_preset_foreach_func(const struct has_preset *preset, void *user_data)
1189 {
1190 const struct bt_has_preset_foreach_data *data = user_data;
1191
1192 return data->func(preset->index, preset->properties, preset->name, data->user_data);
1193 }
1194
bt_has_preset_foreach(uint8_t index,bt_has_preset_func_t func,void * user_data)1195 void bt_has_preset_foreach(uint8_t index, bt_has_preset_func_t func, void *user_data)
1196 {
1197 uint8_t start_index, end_index;
1198 struct bt_has_preset_foreach_data data = {
1199 .func = func,
1200 .user_data = user_data,
1201 };
1202
1203 if (index == BT_HAS_PRESET_INDEX_NONE) {
1204 start_index = BT_HAS_PRESET_INDEX_FIRST;
1205 end_index = BT_HAS_PRESET_INDEX_LAST;
1206 } else {
1207 start_index = end_index = index;
1208 }
1209
1210 preset_foreach(start_index, end_index, bt_has_preset_foreach_func, &data);
1211 }
1212
bt_has_preset_active_set(uint8_t index)1213 int bt_has_preset_active_set(uint8_t index)
1214 {
1215 if (index != BT_HAS_PRESET_INDEX_NONE) {
1216 struct has_preset *preset = NULL;
1217
1218 preset_foreach(index, index, preset_found, &preset);
1219 if (preset == NULL) {
1220 return -ENOENT;
1221 }
1222
1223 if (!(preset->properties & BT_HAS_PROP_AVAILABLE)) {
1224 return -EINVAL;
1225 }
1226 }
1227
1228 preset_active_set(index);
1229
1230 return 0;
1231 }
1232
bt_has_preset_active_get(void)1233 uint8_t bt_has_preset_active_get(void)
1234 {
1235 return has.active_index;
1236 }
1237
bt_has_preset_name_change(uint8_t index,const char * name)1238 int bt_has_preset_name_change(uint8_t index, const char *name)
1239 {
1240 CHECKIF(name == NULL) {
1241 return -EINVAL;
1242 }
1243
1244 if (IS_ENABLED(CONFIG_BT_HAS_PRESET_NAME_DYNAMIC)) {
1245 return set_preset_name(index, name, strlen(name));
1246 } else {
1247 return -EOPNOTSUPP;
1248 }
1249 }
1250 #endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
1251
has_features_register(const struct bt_has_features_param * features)1252 static int has_features_register(const struct bt_has_features_param *features)
1253 {
1254 /* Initialize the supported features characteristic value */
1255 has.features = features->type;
1256
1257 if (IS_ENABLED(CONFIG_BT_HAS_PRESET_SUPPORT)) {
1258 has.features |= BT_HAS_FEAT_DYNAMIC_PRESETS;
1259
1260 if (features->preset_sync_support) {
1261 if (features->type != BT_HAS_HEARING_AID_TYPE_BINAURAL) {
1262 LOG_DBG("Preset sync support only available "
1263 "for binaural hearing aid type");
1264 return -EINVAL;
1265 }
1266
1267 has.features |= BT_HAS_FEAT_PRESET_SYNC_SUPP;
1268 }
1269
1270 if (features->independent_presets) {
1271 if (features->type != BT_HAS_HEARING_AID_TYPE_BINAURAL) {
1272 LOG_DBG("Independent presets only available "
1273 "for binaural hearing aid type");
1274 return -EINVAL;
1275 }
1276
1277 has.features |= BT_HAS_FEAT_INDEPENDENT_PRESETS;
1278 }
1279 }
1280
1281 if (IS_ENABLED(CONFIG_BT_HAS_PRESET_NAME_DYNAMIC)) {
1282 has.features |= BT_HAS_FEAT_WRITABLE_PRESETS_SUPP;
1283 }
1284
1285 return 0;
1286 }
1287
1288 #if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
features_work_process(struct k_work * work)1289 static void features_work_process(struct k_work *work)
1290 {
1291 for (size_t i = 0U; i < ARRAY_SIZE(has_client_list); i++) {
1292 struct has_client *client = &has_client_list[i];
1293 int err;
1294
1295 if (client->conn == NULL) {
1296 /* mark to notify on reconnect */
1297 atomic_set_bit(client->flags, FLAG_FEATURES_CHANGED);
1298 continue;
1299 } else if (atomic_test_and_clear_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY)) {
1300 err = bt_gatt_notify(client->conn, FEATURES_ATTR, &has.features,
1301 sizeof(has.features));
1302 if (err != 0) {
1303 LOG_DBG("failed to notify features for %p: %d", client->conn, err);
1304 }
1305 }
1306 }
1307 }
1308
bt_has_features_set(const struct bt_has_features_param * features)1309 int bt_has_features_set(const struct bt_has_features_param *features)
1310 {
1311 uint8_t tmp_features;
1312 int err;
1313
1314 if (!has.registered) {
1315 return -ENOTSUP;
1316 }
1317
1318 /* Check whether any features will change, otherwise we don't want to notify clients */
1319 if (FEATURE_DEVICE_TYPE_UNCHANGED(features->type) &&
1320 FEATURE_SYNC_SUPPORT_UNCHANGED(features->preset_sync_support) &&
1321 FEATURE_IND_PRESETS_UNCHANGED(features->independent_presets)) {
1322 return 0;
1323 }
1324
1325 tmp_features = has.features;
1326
1327 err = has_features_register(features);
1328 if (err != 0) {
1329 LOG_DBG("Failed to register features");
1330 return err;
1331 }
1332
1333 bool tmp_pending_ntf_features[ARRAY_SIZE(has_client_list)];
1334
1335 for (size_t i = 0U; i < ARRAY_SIZE(has_client_list); i++) {
1336 struct has_client *client = &has_client_list[i];
1337 /* save old state */
1338 tmp_pending_ntf_features[i] = atomic_test_bit(client->flags, FLAG_FEATURES_CHANGED);
1339 /* mark to notify */
1340 atomic_set_bit(client->flags, FLAG_FEATURES_CHANGED);
1341 }
1342
1343 err = k_work_submit(&features_work);
1344 if (err < 0) {
1345 /* restore old values */
1346 for (size_t i = 0U; i < ARRAY_SIZE(has_client_list); i++) {
1347 struct has_client *client = &has_client_list[i];
1348
1349 atomic_set_bit_to(client->flags, FLAG_FEATURES_CHANGED,
1350 tmp_pending_ntf_features[i]);
1351 }
1352 has.features = tmp_features;
1353
1354 return err;
1355 }
1356
1357 return 0;
1358 }
1359 #endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
1360
bt_has_register(const struct bt_has_features_param * features)1361 int bt_has_register(const struct bt_has_features_param *features)
1362 {
1363 int err;
1364
1365 LOG_DBG("features %p", features);
1366
1367 CHECKIF(!features) {
1368 LOG_DBG("NULL params pointer");
1369 return -EINVAL;
1370 }
1371
1372 if (has.registered) {
1373 return -EALREADY;
1374 }
1375
1376 err = has_features_register(features);
1377 if (err != 0) {
1378 LOG_DBG("HAS service failed to register features: %d", err);
1379 return err;
1380 }
1381
1382 has_svc = (struct bt_gatt_service)BT_GATT_SERVICE(has_attrs);
1383 err = bt_gatt_service_register(&has_svc);
1384 if (err != 0) {
1385 LOG_DBG("HAS service register failed: %d", err);
1386 return err;
1387 }
1388
1389 has.registered = true;
1390
1391 return 0;
1392 }
1393