1 /*
2 * Copyright (c) 2022 Codecoup
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <errno.h>
7 #include <stdbool.h>
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <string.h>
11
12 #include <zephyr/autoconf.h>
13 #include <zephyr/bluetooth/att.h>
14 #include <zephyr/bluetooth/audio/has.h>
15 #include <zephyr/bluetooth/bluetooth.h>
16 #include <zephyr/bluetooth/conn.h>
17 #include <zephyr/bluetooth/gatt.h>
18 #include <zephyr/bluetooth/uuid.h>
19 #include <zephyr/kernel.h>
20 #include <zephyr/logging/log.h>
21 #include <zephyr/net/buf.h>
22 #include <zephyr/sys/atomic.h>
23 #include <zephyr/sys/check.h>
24 #include <zephyr/sys/util.h>
25 #include <zephyr/sys/util_macro.h>
26
27 #include "has_internal.h"
28
29 LOG_MODULE_REGISTER(bt_has_client, CONFIG_BT_HAS_CLIENT_LOG_LEVEL);
30
31 #define HAS_INST(_has) CONTAINER_OF(_has, struct bt_has_client, has)
32 #define HANDLE_IS_VALID(handle) ((handle) != 0x0000)
33 static struct bt_has_client clients[CONFIG_BT_MAX_CONN];
34 static const struct bt_has_client_cb *client_cb;
35
inst_by_conn(struct bt_conn * conn)36 static struct bt_has_client *inst_by_conn(struct bt_conn *conn)
37 {
38 struct bt_has_client *inst = &clients[bt_conn_index(conn)];
39
40 if (inst->conn == conn) {
41 return inst;
42 }
43
44 return NULL;
45 }
46
inst_cleanup(struct bt_has_client * inst)47 static void inst_cleanup(struct bt_has_client *inst)
48 {
49 bt_conn_unref(inst->conn);
50
51 (void)memset(inst, 0, sizeof(*inst));
52 }
53
get_capabilities(const struct bt_has_client * inst)54 static enum bt_has_capabilities get_capabilities(const struct bt_has_client *inst)
55 {
56 enum bt_has_capabilities caps = 0;
57
58 /* The Control Point support is optional, as the server might have no presets support */
59 if (HANDLE_IS_VALID(inst->control_point_subscription.value_handle)) {
60 caps |= BT_HAS_PRESET_SUPPORT;
61 }
62
63 return caps;
64 }
65
handle_read_preset_rsp(struct bt_has_client * inst,struct net_buf_simple * buf)66 static void handle_read_preset_rsp(struct bt_has_client *inst, struct net_buf_simple *buf)
67 {
68 const struct bt_has_cp_read_preset_rsp *pdu;
69 struct bt_has_preset_record record;
70 char name[BT_HAS_PRESET_NAME_MAX + 1]; /* + 1 byte for null-terminator */
71 size_t name_len;
72
73 LOG_DBG("conn %p buf %p", (void *)inst->conn, buf);
74
75 if (buf->len < sizeof(*pdu)) {
76 LOG_ERR("malformed PDU");
77 return;
78 }
79
80 pdu = net_buf_simple_pull_mem(buf, sizeof(*pdu));
81
82 if (pdu->is_last > BT_HAS_IS_LAST) {
83 LOG_WRN("unexpected is_last value 0x%02x", pdu->is_last);
84 }
85
86 record.index = pdu->index;
87 record.properties = pdu->properties;
88 record.name = name;
89
90 name_len = buf->len + 1; /* + 1 byte for NULL terminator */
91 if (name_len > ARRAY_SIZE(name)) {
92 LOG_WRN("name is too long (%zu > %u)", buf->len, BT_HAS_PRESET_NAME_MAX);
93
94 name_len = ARRAY_SIZE(name);
95 }
96
97 utf8_lcpy(name, pdu->name, name_len);
98
99 client_cb->preset_read_rsp(&inst->has, 0, &record, !!pdu->is_last);
100 }
101
handle_generic_update(struct bt_has_client * inst,struct net_buf_simple * buf,bool is_last)102 static void handle_generic_update(struct bt_has_client *inst, struct net_buf_simple *buf,
103 bool is_last)
104 {
105 const struct bt_has_cp_generic_update *pdu;
106 struct bt_has_preset_record record;
107 char name[BT_HAS_PRESET_NAME_MAX + 1]; /* + 1 byte for null-terminator */
108 size_t name_len;
109
110 if (buf->len < sizeof(*pdu)) {
111 LOG_ERR("malformed PDU");
112 return;
113 }
114
115 pdu = net_buf_simple_pull_mem(buf, sizeof(*pdu));
116
117 record.index = pdu->index;
118 record.properties = pdu->properties;
119 record.name = name;
120
121 name_len = buf->len + 1; /* + 1 byte for NULL terminator */
122 if (name_len > ARRAY_SIZE(name)) {
123 LOG_WRN("name is too long (%zu > %u)", buf->len, BT_HAS_PRESET_NAME_MAX);
124
125 name_len = ARRAY_SIZE(name);
126 }
127
128 utf8_lcpy(name, pdu->name, name_len);
129
130 client_cb->preset_update(&inst->has, pdu->prev_index, &record, is_last);
131 }
132
handle_preset_deleted(struct bt_has_client * inst,struct net_buf_simple * buf,bool is_last)133 static void handle_preset_deleted(struct bt_has_client *inst, struct net_buf_simple *buf,
134 bool is_last)
135 {
136 if (buf->len < sizeof(uint8_t)) {
137 LOG_ERR("malformed PDU");
138 return;
139 }
140
141 client_cb->preset_deleted(&inst->has, net_buf_simple_pull_u8(buf), is_last);
142 }
143
handle_preset_availability(struct bt_has_client * inst,struct net_buf_simple * buf,bool available,bool is_last)144 static void handle_preset_availability(struct bt_has_client *inst, struct net_buf_simple *buf,
145 bool available, bool is_last)
146 {
147 if (buf->len < sizeof(uint8_t)) {
148 LOG_ERR("malformed PDU");
149 return;
150 }
151
152 client_cb->preset_availability(&inst->has, net_buf_simple_pull_u8(buf), available,
153 is_last);
154 }
155
handle_preset_changed(struct bt_has_client * inst,struct net_buf_simple * buf)156 static void handle_preset_changed(struct bt_has_client *inst, struct net_buf_simple *buf)
157 {
158 const struct bt_has_cp_preset_changed *pdu;
159
160 LOG_DBG("conn %p buf %p", (void *)inst->conn, buf);
161
162 if (buf->len < sizeof(*pdu)) {
163 LOG_ERR("malformed PDU");
164 return;
165 }
166
167 pdu = net_buf_simple_pull_mem(buf, sizeof(*pdu));
168
169 if (pdu->is_last > BT_HAS_IS_LAST) {
170 LOG_WRN("unexpected is_last 0x%02x", pdu->is_last);
171 }
172
173 switch (pdu->change_id) {
174 case BT_HAS_CHANGE_ID_GENERIC_UPDATE:
175 if (client_cb->preset_update) {
176 handle_generic_update(inst, buf, !!pdu->is_last);
177 }
178 break;
179 case BT_HAS_CHANGE_ID_PRESET_DELETED:
180 if (client_cb->preset_deleted) {
181 handle_preset_deleted(inst, buf, !!pdu->is_last);
182 }
183 break;
184 case BT_HAS_CHANGE_ID_PRESET_AVAILABLE:
185 if (client_cb->preset_availability) {
186 handle_preset_availability(inst, buf, !!pdu->is_last, true);
187 }
188 return;
189 case BT_HAS_CHANGE_ID_PRESET_UNAVAILABLE:
190 if (client_cb->preset_availability) {
191 handle_preset_availability(inst, buf, !!pdu->is_last, false);
192 }
193 return;
194 default:
195 LOG_WRN("unknown change_id 0x%02x", pdu->change_id);
196 }
197 }
198
control_point_notify_cb(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t len)199 static uint8_t control_point_notify_cb(struct bt_conn *conn,
200 struct bt_gatt_subscribe_params *params, const void *data,
201 uint16_t len)
202 {
203 struct bt_has_client *inst = CONTAINER_OF(params, struct bt_has_client,
204 control_point_subscription);
205 const struct bt_has_cp_hdr *hdr;
206 struct net_buf_simple buf;
207
208 LOG_DBG("conn %p params %p data %p len %u", (void *)conn, params, data, len);
209
210 if (!conn) { /* Unpaired, continue receiving notifications */
211 return BT_GATT_ITER_CONTINUE;
212 }
213
214 if (!data) { /* Unsubscribed */
215 params->value_handle = 0u;
216
217 return BT_GATT_ITER_STOP;
218 }
219
220 if (len < sizeof(*hdr)) { /* Ignore malformed notification */
221 return BT_GATT_ITER_CONTINUE;
222 }
223
224 net_buf_simple_init_with_data(&buf, (void *)data, len);
225
226 hdr = net_buf_simple_pull_mem(&buf, sizeof(*hdr));
227
228 switch (hdr->opcode) {
229 case BT_HAS_OP_READ_PRESET_RSP:
230 handle_read_preset_rsp(inst, &buf);
231 break;
232 case BT_HAS_OP_PRESET_CHANGED:
233 handle_preset_changed(inst, &buf);
234 break;
235 };
236
237 return BT_GATT_ITER_CONTINUE;
238 }
239
discover_complete(struct bt_has_client * inst)240 static void discover_complete(struct bt_has_client *inst)
241 {
242 LOG_DBG("conn %p", (void *)inst->conn);
243
244 atomic_clear_bit(inst->flags, HAS_CLIENT_DISCOVER_IN_PROGRESS);
245
246 client_cb->discover(inst->conn, 0, &inst->has,
247 inst->has.features & BT_HAS_FEAT_HEARING_AID_TYPE_MASK,
248 get_capabilities(inst));
249
250 /* If Active Preset Index supported, notify it's value */
251 if (client_cb->preset_switch &&
252 HANDLE_IS_VALID(inst->active_index_subscription.value_handle)) {
253 client_cb->preset_switch(&inst->has, 0, inst->has.active_index);
254 }
255 }
256
discover_failed(struct bt_conn * conn,int err)257 static void discover_failed(struct bt_conn *conn, int err)
258 {
259 LOG_DBG("conn %p", (void *)conn);
260
261 client_cb->discover(conn, err, NULL, 0, 0);
262 }
263
cp_write(struct bt_has_client * inst,struct net_buf_simple * buf,bt_gatt_write_func_t func)264 static int cp_write(struct bt_has_client *inst, struct net_buf_simple *buf,
265 bt_gatt_write_func_t func)
266 {
267 const uint16_t value_handle = inst->control_point_subscription.value_handle;
268
269 if (!HANDLE_IS_VALID(value_handle)) {
270 return -ENOTSUP;
271 }
272
273 inst->params.write.func = func;
274 inst->params.write.handle = value_handle;
275 inst->params.write.offset = 0U;
276 inst->params.write.data = buf->data;
277 inst->params.write.length = buf->len;
278
279 return bt_gatt_write(inst->conn, &inst->params.write);
280 }
281
read_presets_req_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)282 static void read_presets_req_cb(struct bt_conn *conn, uint8_t err,
283 struct bt_gatt_write_params *params)
284 {
285 struct bt_has_client *inst = CONTAINER_OF(params, struct bt_has_client, params.write);
286
287 LOG_DBG("conn %p err 0x%02x param %p", (void *)conn, err, params);
288
289 atomic_clear_bit(inst->flags, HAS_CLIENT_CP_OPERATION_IN_PROGRESS);
290
291 if (err) {
292 client_cb->preset_read_rsp(&inst->has, err, NULL, true);
293 }
294 }
295
read_presets_req(struct bt_has_client * inst,uint8_t start_index,uint8_t num_presets)296 static int read_presets_req(struct bt_has_client *inst, uint8_t start_index, uint8_t num_presets)
297 {
298 struct bt_has_cp_hdr *hdr;
299 struct bt_has_cp_read_presets_req *req;
300
301 NET_BUF_SIMPLE_DEFINE(buf, sizeof(*hdr) + sizeof(*req));
302
303 LOG_DBG("conn %p start_index 0x%02x num_presets %d", (void *)inst->conn, start_index,
304 num_presets);
305
306 hdr = net_buf_simple_add(&buf, sizeof(*hdr));
307 hdr->opcode = BT_HAS_OP_READ_PRESET_REQ;
308 req = net_buf_simple_add(&buf, sizeof(*req));
309 req->start_index = start_index;
310 req->num_presets = num_presets;
311
312 return cp_write(inst, &buf, read_presets_req_cb);
313 }
314
set_active_preset_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)315 static void set_active_preset_cb(struct bt_conn *conn, uint8_t err,
316 struct bt_gatt_write_params *params)
317 {
318 struct bt_has_client *inst = CONTAINER_OF(params, struct bt_has_client, params.write);
319
320 LOG_DBG("conn %p err 0x%02x param %p", (void *)conn, err, params);
321
322 atomic_clear_bit(inst->flags, HAS_CLIENT_CP_OPERATION_IN_PROGRESS);
323
324 if (err) {
325 client_cb->preset_switch(&inst->has, err, inst->has.active_index);
326 }
327 }
328
preset_set(struct bt_has_client * inst,uint8_t opcode,uint8_t index)329 static int preset_set(struct bt_has_client *inst, uint8_t opcode, uint8_t index)
330 {
331 struct bt_has_cp_hdr *hdr;
332 struct bt_has_cp_set_active_preset *req;
333
334 NET_BUF_SIMPLE_DEFINE(buf, sizeof(*hdr) + sizeof(*req));
335
336 LOG_DBG("conn %p opcode 0x%02x index 0x%02x", (void *)inst->conn, opcode, index);
337
338 hdr = net_buf_simple_add(&buf, sizeof(*hdr));
339 hdr->opcode = opcode;
340 req = net_buf_simple_add(&buf, sizeof(*req));
341 req->index = index;
342
343 return cp_write(inst, &buf, set_active_preset_cb);
344 }
345
preset_set_next_or_prev(struct bt_has_client * inst,uint8_t opcode)346 static int preset_set_next_or_prev(struct bt_has_client *inst, uint8_t opcode)
347 {
348 struct bt_has_cp_hdr *hdr;
349
350 NET_BUF_SIMPLE_DEFINE(buf, sizeof(*hdr));
351
352 LOG_DBG("conn %p opcode 0x%02x", (void *)inst->conn, opcode);
353
354 hdr = net_buf_simple_add(&buf, sizeof(*hdr));
355 hdr->opcode = opcode;
356
357 return cp_write(inst, &buf, set_active_preset_cb);
358 }
359
active_index_update(struct bt_has_client * inst,const void * data,uint16_t len)360 static uint8_t active_index_update(struct bt_has_client *inst, const void *data, uint16_t len)
361 {
362 struct net_buf_simple buf;
363 const uint8_t prev = inst->has.active_index;
364
365 net_buf_simple_init_with_data(&buf, (void *)data, len);
366
367 inst->has.active_index = net_buf_simple_pull_u8(&buf);
368
369 LOG_DBG("conn %p index 0x%02x", (void *)inst->conn, inst->has.active_index);
370
371 return prev;
372 }
373
active_preset_notify_cb(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t len)374 static uint8_t active_preset_notify_cb(struct bt_conn *conn,
375 struct bt_gatt_subscribe_params *params, const void *data,
376 uint16_t len)
377 {
378 struct bt_has_client *inst = CONTAINER_OF(params, struct bt_has_client,
379 active_index_subscription);
380 uint8_t prev;
381
382 LOG_DBG("conn %p params %p data %p len %u", (void *)conn, params, data, len);
383
384 if (!conn) {
385 /* Unpaired, stop receiving notifications from device */
386 return BT_GATT_ITER_STOP;
387 }
388
389 if (!data) {
390 /* Unsubscribed */
391 params->value_handle = 0u;
392
393 return BT_GATT_ITER_STOP;
394 }
395
396 if (len == 0) {
397 /* Ignore empty notification */
398 return BT_GATT_ITER_CONTINUE;
399 }
400
401 prev = active_index_update(inst, data, len);
402
403 if (atomic_test_bit(inst->flags, HAS_CLIENT_DISCOVER_IN_PROGRESS)) {
404 /* Got notification during discovery process, postpone the active_index callback
405 * until discovery is complete.
406 */
407 return BT_GATT_ITER_CONTINUE;
408 }
409
410 if (client_cb && client_cb->preset_switch && inst->has.active_index != prev) {
411 client_cb->preset_switch(&inst->has, 0, inst->has.active_index);
412 }
413
414 return BT_GATT_ITER_CONTINUE;
415 }
416
active_index_subscribe_cb(struct bt_conn * conn,uint8_t att_err,struct bt_gatt_subscribe_params * params)417 static void active_index_subscribe_cb(struct bt_conn *conn, uint8_t att_err,
418 struct bt_gatt_subscribe_params *params)
419 {
420 struct bt_has_client *inst = CONTAINER_OF(params, struct bt_has_client,
421 active_index_subscription);
422
423 LOG_DBG("conn %p att_err 0x%02x params %p", (void *)inst->conn, att_err, params);
424
425 if (att_err != BT_ATT_ERR_SUCCESS) {
426 /* Cleanup instance so that it can be reused */
427 inst_cleanup(inst);
428
429 discover_failed(conn, att_err);
430 } else {
431 discover_complete(inst);
432 }
433 }
434
active_index_subscribe(struct bt_has_client * inst,uint16_t value_handle)435 static int active_index_subscribe(struct bt_has_client *inst, uint16_t value_handle)
436 {
437 int err;
438
439 LOG_DBG("conn %p handle 0x%04x", (void *)inst->conn, value_handle);
440
441 inst->active_index_subscription.notify = active_preset_notify_cb;
442 inst->active_index_subscription.subscribe = active_index_subscribe_cb;
443 inst->active_index_subscription.value_handle = value_handle;
444 inst->active_index_subscription.ccc_handle = 0x0000;
445 inst->active_index_subscription.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
446 inst->active_index_subscription.disc_params = &inst->params.discover;
447 inst->active_index_subscription.value = BT_GATT_CCC_NOTIFY;
448 atomic_set_bit(inst->active_index_subscription.flags, BT_GATT_SUBSCRIBE_FLAG_VOLATILE);
449
450 err = bt_gatt_subscribe(inst->conn, &inst->active_index_subscription);
451 if (err != 0 && err != -EALREADY) {
452 return err;
453 }
454
455 return 0;
456 }
457
active_index_read_cb(struct bt_conn * conn,uint8_t att_err,struct bt_gatt_read_params * params,const void * data,uint16_t len)458 static uint8_t active_index_read_cb(struct bt_conn *conn, uint8_t att_err,
459 struct bt_gatt_read_params *params, const void *data,
460 uint16_t len)
461 {
462 struct bt_has_client *inst = CONTAINER_OF(params, struct bt_has_client, params.read);
463 int err = att_err;
464
465 LOG_DBG("conn %p att_err 0x%02x params %p data %p len %u", (void *)conn, att_err, params,
466 data, len);
467
468 if (att_err != BT_ATT_ERR_SUCCESS || len == 0) {
469 goto fail;
470 }
471
472 active_index_update(inst, data, len);
473
474 err = active_index_subscribe(inst, params->by_uuid.start_handle);
475 if (err) {
476 LOG_ERR("Subscribe failed (err %d)", err);
477 goto fail;
478 }
479
480 return BT_GATT_ITER_STOP;
481
482 fail:
483 /* Cleanup instance so that it can be reused */
484 inst_cleanup(inst);
485
486 discover_failed(conn, err);
487
488 return BT_GATT_ITER_STOP;
489 }
490
active_index_read(struct bt_has_client * inst)491 static int active_index_read(struct bt_has_client *inst)
492 {
493 LOG_DBG("conn %p", (void *)inst->conn);
494
495 (void)memset(&inst->params.read, 0, sizeof(inst->params.read));
496
497 (void)memcpy(&inst->params.uuid, BT_UUID_HAS_ACTIVE_PRESET_INDEX,
498 sizeof(inst->params.uuid));
499 inst->params.read.func = active_index_read_cb;
500 inst->params.read.handle_count = 0u;
501 inst->params.read.by_uuid.uuid = &inst->params.uuid.uuid;
502 inst->params.read.by_uuid.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
503 inst->params.read.by_uuid.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
504
505 return bt_gatt_read(inst->conn, &inst->params.read);
506 }
507
control_point_subscribe_cb(struct bt_conn * conn,uint8_t att_err,struct bt_gatt_subscribe_params * params)508 static void control_point_subscribe_cb(struct bt_conn *conn, uint8_t att_err,
509 struct bt_gatt_subscribe_params *params)
510 {
511 struct bt_has_client *inst = CONTAINER_OF(params, struct bt_has_client,
512 control_point_subscription);
513 int err = att_err;
514
515 LOG_DBG("conn %p att_err 0x%02x", (void *)inst->conn, att_err);
516
517 if (att_err != BT_ATT_ERR_SUCCESS) {
518 goto fail;
519 }
520
521 err = active_index_read(inst);
522 if (err) {
523 LOG_ERR("Active Preset Index read failed (err %d)", err);
524 goto fail;
525 }
526
527 return;
528
529 fail:
530 /* Cleanup instance so that it can be reused */
531 inst_cleanup(inst);
532
533 discover_failed(conn, err);
534 }
535
control_point_subscribe(struct bt_has_client * inst,uint16_t value_handle,uint8_t properties)536 static int control_point_subscribe(struct bt_has_client *inst, uint16_t value_handle,
537 uint8_t properties)
538 {
539 int err;
540
541 LOG_DBG("conn %p handle 0x%04x", (void *)inst->conn, value_handle);
542
543 inst->control_point_subscription.notify = control_point_notify_cb;
544 inst->control_point_subscription.subscribe = control_point_subscribe_cb;
545 inst->control_point_subscription.value_handle = value_handle;
546 inst->control_point_subscription.ccc_handle = 0x0000;
547 inst->control_point_subscription.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
548 inst->control_point_subscription.disc_params = &inst->params.discover;
549 atomic_set_bit(inst->control_point_subscription.flags, BT_GATT_SUBSCRIBE_FLAG_VOLATILE);
550
551 if (IS_ENABLED(CONFIG_BT_EATT) && properties & BT_GATT_CHRC_NOTIFY) {
552 inst->control_point_subscription.value = BT_GATT_CCC_INDICATE | BT_GATT_CCC_NOTIFY;
553 } else {
554 inst->control_point_subscription.value = BT_GATT_CCC_INDICATE;
555 }
556
557 err = bt_gatt_subscribe(inst->conn, &inst->control_point_subscription);
558 if (err != 0 && err != -EALREADY) {
559 return err;
560 }
561
562 return 0;
563 }
564
control_point_discover_cb(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)565 static uint8_t control_point_discover_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
566 struct bt_gatt_discover_params *params)
567 {
568 struct bt_has_client *inst = CONTAINER_OF(params, struct bt_has_client, params.discover);
569 const struct bt_gatt_chrc *chrc;
570 int err;
571
572 LOG_DBG("conn %p attr %p params %p", (void *)conn, attr, params);
573
574 if (!attr) {
575 LOG_INF("Control Point not found");
576 discover_complete(inst);
577 return BT_GATT_ITER_STOP;
578 }
579
580 chrc = attr->user_data;
581
582 err = control_point_subscribe(inst, chrc->value_handle, chrc->properties);
583 if (err) {
584 LOG_ERR("Subscribe failed (err %d)", err);
585
586 /* Cleanup instance so that it can be reused */
587 inst_cleanup(inst);
588
589 discover_failed(conn, err);
590 }
591
592 return BT_GATT_ITER_STOP;
593 }
594
control_point_discover(struct bt_has_client * inst)595 static int control_point_discover(struct bt_has_client *inst)
596 {
597 LOG_DBG("conn %p", (void *)inst->conn);
598
599 (void)memset(&inst->params.discover, 0, sizeof(inst->params.discover));
600
601 (void)memcpy(&inst->params.uuid, BT_UUID_HAS_PRESET_CONTROL_POINT,
602 sizeof(inst->params.uuid));
603 inst->params.discover.uuid = &inst->params.uuid.uuid;
604 inst->params.discover.func = control_point_discover_cb;
605 inst->params.discover.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
606 inst->params.discover.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
607 inst->params.discover.type = BT_GATT_DISCOVER_CHARACTERISTIC;
608
609 return bt_gatt_discover(inst->conn, &inst->params.discover);
610 }
611
features_update(struct bt_has_client * inst,const void * data,uint16_t len)612 static void features_update(struct bt_has_client *inst, const void *data, uint16_t len)
613 {
614 struct net_buf_simple buf;
615
616 net_buf_simple_init_with_data(&buf, (void *)data, len);
617
618 inst->has.features = net_buf_simple_pull_u8(&buf);
619
620 LOG_DBG("conn %p features 0x%02x", (void *)inst->conn, inst->has.features);
621 }
622
features_read_cb(struct bt_conn * conn,uint8_t att_err,struct bt_gatt_read_params * params,const void * data,uint16_t len)623 static uint8_t features_read_cb(struct bt_conn *conn, uint8_t att_err,
624 struct bt_gatt_read_params *params, const void *data, uint16_t len)
625 {
626 struct bt_has_client *inst = CONTAINER_OF(params, struct bt_has_client, params.read);
627 int err = att_err;
628
629 LOG_DBG("conn %p att_err 0x%02x params %p data %p len %u", (void *)conn, att_err, params,
630 data, len);
631
632 if (att_err != BT_ATT_ERR_SUCCESS || len == 0) {
633 goto fail;
634 }
635
636 features_update(inst, data, len);
637
638 if (!client_cb->preset_switch) {
639 /* Complete the discovery if client is not interested in active preset changes */
640 discover_complete(inst);
641 return BT_GATT_ITER_STOP;
642 }
643
644 err = control_point_discover(inst);
645 if (err) {
646 LOG_ERR("Control Point discover failed (err %d)", err);
647 goto fail;
648 }
649
650 return BT_GATT_ITER_STOP;
651
652 fail:
653 /* Cleanup instance so that it can be reused */
654 inst_cleanup(inst);
655
656 discover_failed(conn, err);
657
658 return BT_GATT_ITER_STOP;
659 }
660
features_read(struct bt_has_client * inst,uint16_t value_handle)661 static int features_read(struct bt_has_client *inst, uint16_t value_handle)
662 {
663 LOG_DBG("conn %p handle 0x%04x", (void *)inst->conn, value_handle);
664
665 (void)memset(&inst->params.read, 0, sizeof(inst->params.read));
666
667 inst->params.read.func = features_read_cb;
668 inst->params.read.handle_count = 1u;
669 inst->params.read.single.handle = value_handle;
670 inst->params.read.single.offset = 0u;
671
672 return bt_gatt_read(inst->conn, &inst->params.read);
673 }
674
features_subscribe_cb(struct bt_conn * conn,uint8_t att_err,struct bt_gatt_subscribe_params * params)675 static void features_subscribe_cb(struct bt_conn *conn, uint8_t att_err,
676 struct bt_gatt_subscribe_params *params)
677 {
678 struct bt_has_client *inst = CONTAINER_OF(params, struct bt_has_client,
679 features_subscription);
680 int err = att_err;
681
682 LOG_DBG("conn %p att_err 0x%02x params %p", (void *)conn, att_err, params);
683
684 if (att_err != BT_ATT_ERR_SUCCESS) {
685 goto fail;
686 }
687
688 err = features_read(inst, inst->features_subscription.value_handle);
689 if (err) {
690 LOG_ERR("Read failed (err %d)", err);
691 goto fail;
692 }
693
694 return;
695
696 fail:
697 /* Cleanup instance so that it can be reused */
698 inst_cleanup(inst);
699
700 discover_failed(conn, err);
701 }
702
features_notify_cb(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t len)703 static uint8_t features_notify_cb(struct bt_conn *conn, struct bt_gatt_subscribe_params *params,
704 const void *data, uint16_t len)
705 {
706 struct bt_has_client *inst = CONTAINER_OF(params, struct bt_has_client,
707 features_subscription);
708
709 LOG_DBG("conn %p params %p data %p len %u", (void *)conn, params, data, len);
710
711 if (!conn) {
712 /* Unpaired, stop receiving notifications from device */
713 return BT_GATT_ITER_STOP;
714 }
715
716 if (!data) {
717 /* Unsubscribed */
718 params->value_handle = 0u;
719
720 return BT_GATT_ITER_STOP;
721 }
722
723 if (len == 0) {
724 /* Ignore empty notification */
725 return BT_GATT_ITER_CONTINUE;
726 }
727
728 features_update(inst, data, len);
729
730 return BT_GATT_ITER_CONTINUE;
731 }
732
features_subscribe(struct bt_has_client * inst,uint16_t value_handle)733 static int features_subscribe(struct bt_has_client *inst, uint16_t value_handle)
734 {
735 int err;
736
737 LOG_DBG("conn %p handle 0x%04x", (void *)inst->conn, value_handle);
738
739 inst->features_subscription.notify = features_notify_cb;
740 inst->features_subscription.subscribe = features_subscribe_cb;
741 inst->features_subscription.value_handle = value_handle;
742 inst->features_subscription.ccc_handle = 0x0000;
743 inst->features_subscription.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
744 inst->features_subscription.disc_params = &inst->params.discover;
745 inst->features_subscription.value = BT_GATT_CCC_NOTIFY;
746 atomic_set_bit(inst->features_subscription.flags, BT_GATT_SUBSCRIBE_FLAG_VOLATILE);
747
748 err = bt_gatt_subscribe(inst->conn, &inst->features_subscription);
749 if (err != 0 && err != -EALREADY) {
750 return err;
751 }
752
753 return 0;
754 }
755
features_discover_cb(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)756 static uint8_t features_discover_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
757 struct bt_gatt_discover_params *params)
758 {
759 struct bt_has_client *inst = CONTAINER_OF(params, struct bt_has_client, params.discover);
760 const struct bt_gatt_chrc *chrc;
761 int err;
762
763 LOG_DBG("conn %p attr %p params %p", (void *)conn, attr, params);
764
765 if (!attr) {
766 err = -ENOENT;
767 goto fail;
768 }
769
770 chrc = attr->user_data;
771
772 /* Subscribe first if notifications are supported, otherwise read the features */
773 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
774 err = features_subscribe(inst, chrc->value_handle);
775 if (err) {
776 LOG_ERR("Subscribe failed (err %d)", err);
777 goto fail;
778 }
779 } else {
780 err = features_read(inst, chrc->value_handle);
781 if (err) {
782 LOG_ERR("Read failed (err %d)", err);
783 goto fail;
784 }
785 }
786
787 return BT_GATT_ITER_STOP;
788
789 fail:
790 /* Cleanup instance so that it can be reused */
791 inst_cleanup(inst);
792
793 discover_failed(conn, err);
794
795 return BT_GATT_ITER_STOP;
796 }
797
features_discover(struct bt_has_client * inst)798 static int features_discover(struct bt_has_client *inst)
799 {
800 LOG_DBG("conn %p", (void *)inst->conn);
801
802 (void)memset(&inst->params.discover, 0, sizeof(inst->params.discover));
803
804 (void)memcpy(&inst->params.uuid, BT_UUID_HAS_HEARING_AID_FEATURES,
805 sizeof(inst->params.uuid));
806 inst->params.discover.uuid = &inst->params.uuid.uuid;
807 inst->params.discover.func = features_discover_cb;
808 inst->params.discover.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
809 inst->params.discover.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
810 inst->params.discover.type = BT_GATT_DISCOVER_CHARACTERISTIC;
811
812 return bt_gatt_discover(inst->conn, &inst->params.discover);
813 }
814
bt_has_client_cb_register(const struct bt_has_client_cb * cb)815 int bt_has_client_cb_register(const struct bt_has_client_cb *cb)
816 {
817 CHECKIF(!cb) {
818 return -EINVAL;
819 }
820
821 CHECKIF(client_cb) {
822 return -EALREADY;
823 }
824
825 client_cb = cb;
826
827 return 0;
828 }
829
830 /* Hearing Access Service discovery
831 *
832 * This will initiate a discover procedure. The procedure will do the following sequence:
833 * 1) HAS related characteristic discovery
834 * 2) CCC subscription
835 * 3) Hearing Aid Features and Active Preset Index characteristic read
836 * 5) When everything above have been completed, the callback is called
837 */
bt_has_client_discover(struct bt_conn * conn)838 int bt_has_client_discover(struct bt_conn *conn)
839 {
840 struct bt_has_client *inst;
841 int err;
842
843 LOG_DBG("conn %p", (void *)conn);
844
845 CHECKIF(!conn || !client_cb || !client_cb->discover) {
846 return -EINVAL;
847 }
848
849 inst = &clients[bt_conn_index(conn)];
850
851 if (atomic_test_bit(inst->flags, HAS_CLIENT_CP_OPERATION_IN_PROGRESS) ||
852 atomic_test_and_set_bit(inst->flags, HAS_CLIENT_DISCOVER_IN_PROGRESS)) {
853 return -EBUSY;
854 }
855
856 if (inst->conn) {
857 return -EALREADY;
858 }
859
860 inst->conn = bt_conn_ref(conn);
861
862 err = features_discover(inst);
863 if (err) {
864 atomic_clear_bit(inst->flags, HAS_CLIENT_DISCOVER_IN_PROGRESS);
865 }
866
867 return err;
868 }
869
bt_has_client_conn_get(const struct bt_has * has,struct bt_conn ** conn)870 int bt_has_client_conn_get(const struct bt_has *has, struct bt_conn **conn)
871 {
872 struct bt_has_client *inst = HAS_INST(has);
873
874 *conn = bt_conn_ref(inst->conn);
875
876 return 0;
877 }
878
bt_has_client_presets_read(struct bt_has * has,uint8_t start_index,uint8_t count)879 int bt_has_client_presets_read(struct bt_has *has, uint8_t start_index, uint8_t count)
880 {
881 struct bt_has_client *inst = HAS_INST(has);
882 int err;
883
884 LOG_DBG("conn %p start_index 0x%02x count %d", (void *)inst->conn, start_index, count);
885
886 if (!inst->conn) {
887 return -ENOTCONN;
888 }
889
890 if (atomic_test_bit(inst->flags, HAS_CLIENT_DISCOVER_IN_PROGRESS) ||
891 atomic_test_and_set_bit(inst->flags, HAS_CLIENT_CP_OPERATION_IN_PROGRESS)) {
892 return -EBUSY;
893 }
894
895 CHECKIF(start_index == BT_HAS_PRESET_INDEX_NONE) {
896 return -EINVAL;
897 }
898
899 CHECKIF(count == 0u) {
900 return -EINVAL;
901 }
902
903 err = read_presets_req(inst, start_index, count);
904 if (err) {
905 atomic_clear_bit(inst->flags, HAS_CLIENT_CP_OPERATION_IN_PROGRESS);
906 }
907
908 return err;
909 }
910
bt_has_client_preset_set(struct bt_has * has,uint8_t index,bool sync)911 int bt_has_client_preset_set(struct bt_has *has, uint8_t index, bool sync)
912 {
913 struct bt_has_client *inst = HAS_INST(has);
914 uint8_t opcode;
915
916 LOG_DBG("conn %p index 0x%02x", (void *)inst->conn, index);
917
918 if (!inst->conn) {
919 return -ENOTCONN;
920 }
921
922 CHECKIF(index == BT_HAS_PRESET_INDEX_NONE) {
923 return -EINVAL;
924 }
925
926 if (sync && (inst->has.features & BT_HAS_FEAT_PRESET_SYNC_SUPP) == 0) {
927 return -EOPNOTSUPP;
928 }
929
930 if (atomic_test_bit(inst->flags, HAS_CLIENT_DISCOVER_IN_PROGRESS) ||
931 atomic_test_and_set_bit(inst->flags, HAS_CLIENT_CP_OPERATION_IN_PROGRESS)) {
932 return -EBUSY;
933 }
934
935 opcode = sync ? BT_HAS_OP_SET_ACTIVE_PRESET_SYNC : BT_HAS_OP_SET_ACTIVE_PRESET;
936
937 return preset_set(inst, opcode, index);
938 }
939
bt_has_client_preset_next(struct bt_has * has,bool sync)940 int bt_has_client_preset_next(struct bt_has *has, bool sync)
941 {
942 struct bt_has_client *inst = HAS_INST(has);
943 uint8_t opcode;
944
945 LOG_DBG("conn %p sync %d", (void *)inst->conn, sync);
946
947 if (!inst->conn) {
948 return -ENOTCONN;
949 }
950
951 if (sync && (inst->has.features & BT_HAS_FEAT_PRESET_SYNC_SUPP) == 0) {
952 return -EOPNOTSUPP;
953 }
954
955 if (atomic_test_bit(inst->flags, HAS_CLIENT_DISCOVER_IN_PROGRESS) ||
956 atomic_test_and_set_bit(inst->flags, HAS_CLIENT_CP_OPERATION_IN_PROGRESS)) {
957 return -EBUSY;
958 }
959
960 opcode = sync ? BT_HAS_OP_SET_NEXT_PRESET_SYNC : BT_HAS_OP_SET_NEXT_PRESET;
961
962 return preset_set_next_or_prev(inst, opcode);
963 }
964
bt_has_client_preset_prev(struct bt_has * has,bool sync)965 int bt_has_client_preset_prev(struct bt_has *has, bool sync)
966 {
967 struct bt_has_client *inst = HAS_INST(has);
968 uint8_t opcode;
969
970 LOG_DBG("conn %p sync %d", (void *)inst->conn, sync);
971
972 if (!inst->conn) {
973 return -ENOTCONN;
974 }
975
976 if (sync && (inst->has.features & BT_HAS_FEAT_PRESET_SYNC_SUPP) == 0) {
977 return -EOPNOTSUPP;
978 }
979
980 if (atomic_test_bit(inst->flags, HAS_CLIENT_DISCOVER_IN_PROGRESS) ||
981 atomic_test_and_set_bit(inst->flags, HAS_CLIENT_CP_OPERATION_IN_PROGRESS)) {
982 return -EBUSY;
983 }
984
985 opcode = sync ? BT_HAS_OP_SET_PREV_PRESET_SYNC : BT_HAS_OP_SET_PREV_PRESET;
986
987 return preset_set_next_or_prev(inst, opcode);
988 }
989
disconnected(struct bt_conn * conn,uint8_t reason)990 static void disconnected(struct bt_conn *conn, uint8_t reason)
991 {
992 struct bt_has_client *inst = inst_by_conn(conn);
993
994 if (!inst) {
995 return;
996 }
997
998 if (atomic_test_bit(inst->flags, HAS_CLIENT_DISCOVER_IN_PROGRESS)) {
999 discover_failed(conn, -ECONNABORTED);
1000 }
1001
1002 inst_cleanup(inst);
1003 }
1004
1005 BT_CONN_CB_DEFINE(conn_cb) = {
1006 .disconnected = disconnected,
1007 };
1008