1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <stddef.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include "nimble/ble.h"
24 #include "host/ble_uuid.h"
25 #include "host/ble_store.h"
26 #include "ble_hs_priv.h"
27
28 #define BLE_GATTS_INCLUDE_SZ 6
29 #define BLE_GATTS_CHR_MAX_SZ 19
30
31 static const ble_uuid_t *uuid_pri =
32 BLE_UUID16_DECLARE(BLE_ATT_UUID_PRIMARY_SERVICE);
33 static const ble_uuid_t *uuid_sec =
34 BLE_UUID16_DECLARE(BLE_ATT_UUID_SECONDARY_SERVICE);
35 static const ble_uuid_t *uuid_inc =
36 BLE_UUID16_DECLARE(BLE_ATT_UUID_INCLUDE);
37 static const ble_uuid_t *uuid_chr =
38 BLE_UUID16_DECLARE(BLE_ATT_UUID_CHARACTERISTIC);
39 static const ble_uuid_t *uuid_ccc =
40 BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16);
41
42 static const struct ble_gatt_svc_def **ble_gatts_svc_defs;
43 static int ble_gatts_num_svc_defs;
44
45 struct ble_gatts_svc_entry {
46 const struct ble_gatt_svc_def *svc;
47 uint16_t handle; /* 0 means unregistered. */
48 uint16_t end_group_handle; /* 0xffff means unset. */
49 };
50
51 static struct ble_gatts_svc_entry *ble_gatts_svc_entries;
52 static uint16_t ble_gatts_num_svc_entries;
53
54 static os_membuf_t *ble_gatts_clt_cfg_mem;
55 static struct os_mempool ble_gatts_clt_cfg_pool;
56
57 struct ble_gatts_clt_cfg {
58 uint16_t chr_val_handle;
59 uint8_t flags;
60 uint8_t allowed;
61 };
62
63 /** A cached array of handles for the configurable characteristics. */
64 static struct ble_gatts_clt_cfg *ble_gatts_clt_cfgs;
65 static int ble_gatts_num_cfgable_chrs;
66
67 STATS_SECT_DECL(ble_gatts_stats) ble_gatts_stats;
68 STATS_NAME_START(ble_gatts_stats)
STATS_NAME(ble_gatts_stats,svcs)69 STATS_NAME(ble_gatts_stats, svcs)
70 STATS_NAME(ble_gatts_stats, chrs)
71 STATS_NAME(ble_gatts_stats, dscs)
72 STATS_NAME(ble_gatts_stats, svc_def_reads)
73 STATS_NAME(ble_gatts_stats, svc_inc_reads)
74 STATS_NAME(ble_gatts_stats, chr_def_reads)
75 STATS_NAME(ble_gatts_stats, chr_val_reads)
76 STATS_NAME(ble_gatts_stats, chr_val_writes)
77 STATS_NAME(ble_gatts_stats, dsc_reads)
78 STATS_NAME(ble_gatts_stats, dsc_writes)
79 STATS_NAME_END(ble_gatts_stats)
80
81 static int
82 ble_gatts_svc_access(uint16_t conn_handle, uint16_t attr_handle,
83 uint8_t op, uint16_t offset, struct os_mbuf **om,
84 void *arg)
85 {
86 const struct ble_gatt_svc_def *svc;
87 uint8_t *buf;
88
89 STATS_INC(ble_gatts_stats, svc_def_reads);
90
91 BLE_HS_DBG_ASSERT(op == BLE_ATT_ACCESS_OP_READ);
92
93 svc = arg;
94
95 buf = os_mbuf_extend(*om, ble_uuid_length(svc->uuid));
96 if (buf == NULL) {
97 return BLE_ATT_ERR_INSUFFICIENT_RES;
98 }
99
100 ble_uuid_flat(svc->uuid, buf);
101
102 return 0;
103 }
104
105 static int
ble_gatts_inc_access(uint16_t conn_handle,uint16_t attr_handle,uint8_t op,uint16_t offset,struct os_mbuf ** om,void * arg)106 ble_gatts_inc_access(uint16_t conn_handle, uint16_t attr_handle,
107 uint8_t op, uint16_t offset, struct os_mbuf **om,
108 void *arg)
109 {
110 const struct ble_gatts_svc_entry *entry;
111 uint16_t uuid16;
112 uint8_t *buf;
113
114 STATS_INC(ble_gatts_stats, svc_inc_reads);
115
116 BLE_HS_DBG_ASSERT(op == BLE_ATT_ACCESS_OP_READ);
117
118 entry = arg;
119
120 buf = os_mbuf_extend(*om, 4);
121 if (buf == NULL) {
122 return BLE_ATT_ERR_INSUFFICIENT_RES;
123 }
124 put_le16(buf + 0, entry->handle);
125 put_le16(buf + 2, entry->end_group_handle);
126
127 /* Only include the service UUID if it has a 16-bit representation. */
128 uuid16 = ble_uuid_u16(entry->svc->uuid);
129 if (uuid16 != 0) {
130 buf = os_mbuf_extend(*om, 2);
131 if (buf == NULL) {
132 return BLE_ATT_ERR_INSUFFICIENT_RES;
133 }
134 put_le16(buf, uuid16);
135 }
136
137 return 0;
138 }
139
140 static uint16_t
ble_gatts_chr_clt_cfg_allowed(const struct ble_gatt_chr_def * chr)141 ble_gatts_chr_clt_cfg_allowed(const struct ble_gatt_chr_def *chr)
142 {
143 uint16_t flags;
144
145 flags = 0;
146 if (chr->flags & BLE_GATT_CHR_F_NOTIFY) {
147 flags |= BLE_GATTS_CLT_CFG_F_NOTIFY;
148 }
149 if (chr->flags & BLE_GATT_CHR_F_INDICATE) {
150 flags |= BLE_GATTS_CLT_CFG_F_INDICATE;
151 }
152
153 return flags;
154 }
155
156 static uint8_t
ble_gatts_att_flags_from_chr_flags(ble_gatt_chr_flags chr_flags)157 ble_gatts_att_flags_from_chr_flags(ble_gatt_chr_flags chr_flags)
158 {
159 uint8_t att_flags;
160
161 att_flags = 0;
162 if (chr_flags & BLE_GATT_CHR_F_READ) {
163 att_flags |= BLE_ATT_F_READ;
164 }
165 if (chr_flags & (BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_WRITE)) {
166 att_flags |= BLE_ATT_F_WRITE;
167 }
168 if (chr_flags & BLE_GATT_CHR_F_READ_ENC) {
169 att_flags |= BLE_ATT_F_READ_ENC;
170 }
171 if (chr_flags & BLE_GATT_CHR_F_READ_AUTHEN) {
172 att_flags |= BLE_ATT_F_READ_AUTHEN;
173 }
174 if (chr_flags & BLE_GATT_CHR_F_READ_AUTHOR) {
175 att_flags |= BLE_ATT_F_READ_AUTHOR;
176 }
177 if (chr_flags & BLE_GATT_CHR_F_WRITE_ENC) {
178 att_flags |= BLE_ATT_F_WRITE_ENC;
179 }
180 if (chr_flags & BLE_GATT_CHR_F_WRITE_AUTHEN) {
181 att_flags |= BLE_ATT_F_WRITE_AUTHEN;
182 }
183 if (chr_flags & BLE_GATT_CHR_F_WRITE_AUTHOR) {
184 att_flags |= BLE_ATT_F_WRITE_AUTHOR;
185 }
186
187 return att_flags;
188 }
189
190 static uint8_t
ble_gatts_chr_properties(const struct ble_gatt_chr_def * chr)191 ble_gatts_chr_properties(const struct ble_gatt_chr_def *chr)
192 {
193 uint8_t properties;
194
195 properties = 0;
196
197 if (chr->flags & BLE_GATT_CHR_F_BROADCAST) {
198 properties |= BLE_GATT_CHR_PROP_BROADCAST;
199 }
200 if (chr->flags & BLE_GATT_CHR_F_READ) {
201 properties |= BLE_GATT_CHR_PROP_READ;
202 }
203 if (chr->flags & BLE_GATT_CHR_F_WRITE_NO_RSP) {
204 properties |= BLE_GATT_CHR_PROP_WRITE_NO_RSP;
205 }
206 if (chr->flags & BLE_GATT_CHR_F_WRITE) {
207 properties |= BLE_GATT_CHR_PROP_WRITE;
208 }
209 if (chr->flags & BLE_GATT_CHR_F_NOTIFY) {
210 properties |= BLE_GATT_CHR_PROP_NOTIFY;
211 }
212 if (chr->flags & BLE_GATT_CHR_F_INDICATE) {
213 properties |= BLE_GATT_CHR_PROP_INDICATE;
214 }
215 if (chr->flags & BLE_GATT_CHR_F_AUTH_SIGN_WRITE) {
216 properties |= BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE;
217 }
218 if (chr->flags &
219 (BLE_GATT_CHR_F_RELIABLE_WRITE | BLE_GATT_CHR_F_AUX_WRITE)) {
220
221 properties |= BLE_GATT_CHR_PROP_EXTENDED;
222 }
223
224 return properties;
225 }
226
227 static int
ble_gatts_chr_def_access(uint16_t conn_handle,uint16_t attr_handle,uint8_t op,uint16_t offset,struct os_mbuf ** om,void * arg)228 ble_gatts_chr_def_access(uint16_t conn_handle, uint16_t attr_handle,
229 uint8_t op, uint16_t offset, struct os_mbuf **om,
230 void *arg)
231 {
232 const struct ble_gatt_chr_def *chr;
233 uint8_t *buf;
234
235 STATS_INC(ble_gatts_stats, chr_def_reads);
236
237 BLE_HS_DBG_ASSERT(op == BLE_ATT_ACCESS_OP_READ);
238
239 chr = arg;
240
241 buf = os_mbuf_extend(*om, 3);
242 if (buf == NULL) {
243 return BLE_ATT_ERR_INSUFFICIENT_RES;
244 }
245
246 buf[0] = ble_gatts_chr_properties(chr);
247
248 /* The value attribute is always immediately after the declaration. */
249 put_le16(buf + 1, attr_handle + 1);
250
251 buf = os_mbuf_extend(*om, ble_uuid_length(chr->uuid));
252 if (buf == NULL) {
253 return BLE_ATT_ERR_INSUFFICIENT_RES;
254 }
255
256 ble_uuid_flat(chr->uuid, buf);
257
258 return 0;
259 }
260
261 static int
ble_gatts_chr_is_sane(const struct ble_gatt_chr_def * chr)262 ble_gatts_chr_is_sane(const struct ble_gatt_chr_def *chr)
263 {
264 if (chr->uuid == NULL) {
265 return 0;
266 }
267
268 if (chr->access_cb == NULL) {
269 return 0;
270 }
271
272 /* XXX: Check properties. */
273
274 return 1;
275 }
276
277 static uint8_t
ble_gatts_chr_op(uint8_t att_op)278 ble_gatts_chr_op(uint8_t att_op)
279 {
280 switch (att_op) {
281 case BLE_ATT_ACCESS_OP_READ:
282 return BLE_GATT_ACCESS_OP_READ_CHR;
283
284 case BLE_ATT_ACCESS_OP_WRITE:
285 return BLE_GATT_ACCESS_OP_WRITE_CHR;
286
287 default:
288 BLE_HS_DBG_ASSERT(0);
289 return BLE_GATT_ACCESS_OP_READ_CHR;
290 }
291 }
292
293 static void
ble_gatts_chr_inc_val_stat(uint8_t gatt_op)294 ble_gatts_chr_inc_val_stat(uint8_t gatt_op)
295 {
296 switch (gatt_op) {
297 case BLE_GATT_ACCESS_OP_READ_CHR:
298 STATS_INC(ble_gatts_stats, chr_val_reads);
299 break;
300
301 case BLE_GATT_ACCESS_OP_WRITE_CHR:
302 STATS_INC(ble_gatts_stats, chr_val_writes);
303 break;
304
305 default:
306 break;
307 }
308 }
309
310 /**
311 * Indicates whether the set of registered services can be modified. The
312 * service set is mutable if:
313 * o No peers are connected, and
314 * o No GAP operations are active (advertise, discover, or connect).
315 *
316 * @return true if the GATT service set can be modified;
317 * false otherwise.
318 */
319 static bool
ble_gatts_mutable(void)320 ble_gatts_mutable(void)
321 {
322 /* Ensure no active GAP procedures. */
323 if (ble_gap_adv_active() ||
324 ble_gap_disc_active() ||
325 ble_gap_conn_active()) {
326
327 return false;
328 }
329
330 /* Ensure no established connections. */
331 if (ble_hs_conn_first() != NULL) {
332 return false;
333 }
334
335 return true;
336 }
337
338 static int
ble_gatts_val_access(uint16_t conn_handle,uint16_t attr_handle,uint16_t offset,struct ble_gatt_access_ctxt * gatt_ctxt,struct os_mbuf ** om,ble_gatt_access_fn * access_cb,void * cb_arg)339 ble_gatts_val_access(uint16_t conn_handle, uint16_t attr_handle,
340 uint16_t offset, struct ble_gatt_access_ctxt *gatt_ctxt,
341 struct os_mbuf **om, ble_gatt_access_fn *access_cb,
342 void *cb_arg)
343 {
344 uint16_t initial_len;
345 int attr_len;
346 int new_om;
347 int rc;
348
349 switch (gatt_ctxt->op) {
350 case BLE_GATT_ACCESS_OP_READ_CHR:
351 case BLE_GATT_ACCESS_OP_READ_DSC:
352 /* A characteristic value is being read.
353 *
354 * If the read specifies an offset of 0:
355 * just append the characteristic value directly onto the response
356 * mbuf.
357 *
358 * Else:
359 * allocate a new mbuf to hold the characteristic data, then append
360 * the requested portion onto the response mbuf.
361 */
362 if (offset == 0) {
363 new_om = 0;
364 gatt_ctxt->om = *om;
365 } else {
366 new_om = 1;
367 gatt_ctxt->om = os_msys_get_pkthdr(0, 0);
368 if (gatt_ctxt->om == NULL) {
369 return BLE_ATT_ERR_INSUFFICIENT_RES;
370 }
371 }
372
373 initial_len = OS_MBUF_PKTLEN(gatt_ctxt->om);
374 rc = access_cb(conn_handle, attr_handle, gatt_ctxt, cb_arg);
375 if (rc == 0) {
376 attr_len = OS_MBUF_PKTLEN(gatt_ctxt->om) - initial_len - offset;
377 if (attr_len >= 0) {
378 if (new_om) {
379 os_mbuf_appendfrom(*om, gatt_ctxt->om, offset, attr_len);
380 }
381 } else {
382 rc = BLE_ATT_ERR_INVALID_OFFSET;
383 }
384 }
385
386 if (new_om) {
387 os_mbuf_free_chain(gatt_ctxt->om);
388 }
389 return rc;
390
391 case BLE_GATT_ACCESS_OP_WRITE_CHR:
392 case BLE_GATT_ACCESS_OP_WRITE_DSC:
393 gatt_ctxt->om = *om;
394 rc = access_cb(conn_handle, attr_handle, gatt_ctxt, cb_arg);
395 *om = gatt_ctxt->om;
396 return rc;
397
398 default:
399 BLE_HS_DBG_ASSERT(0);
400 return BLE_ATT_ERR_UNLIKELY;
401 }
402 }
403
404 static int
ble_gatts_chr_val_access(uint16_t conn_handle,uint16_t attr_handle,uint8_t att_op,uint16_t offset,struct os_mbuf ** om,void * arg)405 ble_gatts_chr_val_access(uint16_t conn_handle, uint16_t attr_handle,
406 uint8_t att_op, uint16_t offset,
407 struct os_mbuf **om, void *arg)
408 {
409 const struct ble_gatt_chr_def *chr_def;
410 struct ble_gatt_access_ctxt gatt_ctxt;
411 int rc;
412
413 chr_def = arg;
414 BLE_HS_DBG_ASSERT(chr_def != NULL && chr_def->access_cb != NULL);
415
416 gatt_ctxt.op = ble_gatts_chr_op(att_op);
417 gatt_ctxt.chr = chr_def;
418
419 ble_gatts_chr_inc_val_stat(gatt_ctxt.op);
420 rc = ble_gatts_val_access(conn_handle, attr_handle, offset, &gatt_ctxt, om,
421 chr_def->access_cb, chr_def->arg);
422
423 return rc;
424 }
425
426 static int
ble_gatts_find_svc_entry_idx(const struct ble_gatt_svc_def * svc)427 ble_gatts_find_svc_entry_idx(const struct ble_gatt_svc_def *svc)
428 {
429 int i;
430
431 for (i = 0; i < ble_gatts_num_svc_entries; i++) {
432 if (ble_gatts_svc_entries[i].svc == svc) {
433 return i;
434 }
435 }
436
437 return -1;
438 }
439
440 static int
ble_gatts_svc_incs_satisfied(const struct ble_gatt_svc_def * svc)441 ble_gatts_svc_incs_satisfied(const struct ble_gatt_svc_def *svc)
442 {
443 int idx;
444 int i;
445
446 if (svc->includes == NULL) {
447 /* No included services. */
448 return 1;
449 }
450
451 for (i = 0; svc->includes[i] != NULL; i++) {
452 idx = ble_gatts_find_svc_entry_idx(svc->includes[i]);
453 if (idx == -1 || ble_gatts_svc_entries[idx].handle == 0) {
454 return 0;
455 }
456 }
457
458 return 1;
459 }
460
461 static int
ble_gatts_register_inc(struct ble_gatts_svc_entry * entry)462 ble_gatts_register_inc(struct ble_gatts_svc_entry *entry)
463 {
464 uint16_t handle;
465 int rc;
466
467 BLE_HS_DBG_ASSERT(entry->handle != 0);
468 BLE_HS_DBG_ASSERT(entry->end_group_handle != 0xffff);
469
470 rc = ble_att_svr_register(uuid_inc, BLE_ATT_F_READ, 0, &handle,
471 ble_gatts_inc_access, entry);
472 if (rc != 0) {
473 return rc;
474 }
475
476 return 0;
477 }
478
479 static uint8_t
ble_gatts_dsc_op(uint8_t att_op)480 ble_gatts_dsc_op(uint8_t att_op)
481 {
482 switch (att_op) {
483 case BLE_ATT_ACCESS_OP_READ:
484 return BLE_GATT_ACCESS_OP_READ_DSC;
485
486 case BLE_ATT_ACCESS_OP_WRITE:
487 return BLE_GATT_ACCESS_OP_WRITE_DSC;
488
489 default:
490 BLE_HS_DBG_ASSERT(0);
491 return BLE_GATT_ACCESS_OP_READ_DSC;
492 }
493 }
494
495 static void
ble_gatts_dsc_inc_stat(uint8_t gatt_op)496 ble_gatts_dsc_inc_stat(uint8_t gatt_op)
497 {
498 switch (gatt_op) {
499 case BLE_GATT_ACCESS_OP_READ_DSC:
500 STATS_INC(ble_gatts_stats, dsc_reads);
501 break;
502
503 case BLE_GATT_ACCESS_OP_WRITE_DSC:
504 STATS_INC(ble_gatts_stats, dsc_writes);
505 break;
506
507 default:
508 break;
509 }
510 }
511
512 static int
ble_gatts_dsc_access(uint16_t conn_handle,uint16_t attr_handle,uint8_t att_op,uint16_t offset,struct os_mbuf ** om,void * arg)513 ble_gatts_dsc_access(uint16_t conn_handle, uint16_t attr_handle,
514 uint8_t att_op, uint16_t offset, struct os_mbuf **om,
515 void *arg)
516 {
517 const struct ble_gatt_dsc_def *dsc_def;
518 struct ble_gatt_access_ctxt gatt_ctxt;
519 int rc;
520
521 dsc_def = arg;
522 BLE_HS_DBG_ASSERT(dsc_def != NULL && dsc_def->access_cb != NULL);
523
524 gatt_ctxt.op = ble_gatts_dsc_op(att_op);
525 gatt_ctxt.dsc = dsc_def;
526
527 ble_gatts_dsc_inc_stat(gatt_ctxt.op);
528 rc = ble_gatts_val_access(conn_handle, attr_handle, offset, &gatt_ctxt, om,
529 dsc_def->access_cb, dsc_def->arg);
530
531 return rc;
532 }
533
534 static int
ble_gatts_dsc_is_sane(const struct ble_gatt_dsc_def * dsc)535 ble_gatts_dsc_is_sane(const struct ble_gatt_dsc_def *dsc)
536 {
537 if (dsc->uuid == NULL) {
538 return 0;
539 }
540
541 if (dsc->access_cb == NULL) {
542 return 0;
543 }
544
545 return 1;
546 }
547
548 static int
ble_gatts_register_dsc(const struct ble_gatt_svc_def * svc,const struct ble_gatt_chr_def * chr,const struct ble_gatt_dsc_def * dsc,uint16_t chr_def_handle,ble_gatt_register_fn * register_cb,void * cb_arg)549 ble_gatts_register_dsc(const struct ble_gatt_svc_def *svc,
550 const struct ble_gatt_chr_def *chr,
551 const struct ble_gatt_dsc_def *dsc,
552 uint16_t chr_def_handle,
553 ble_gatt_register_fn *register_cb, void *cb_arg)
554 {
555 struct ble_gatt_register_ctxt register_ctxt;
556 uint16_t dsc_handle;
557 int rc;
558
559 if (!ble_gatts_dsc_is_sane(dsc)) {
560 return BLE_HS_EINVAL;
561 }
562
563 rc = ble_att_svr_register(dsc->uuid, dsc->att_flags, dsc->min_key_size,
564 &dsc_handle, ble_gatts_dsc_access, (void *)dsc);
565 if (rc != 0) {
566 return rc;
567 }
568
569 if (register_cb != NULL) {
570 register_ctxt.op = BLE_GATT_REGISTER_OP_DSC;
571 register_ctxt.dsc.handle = dsc_handle;
572 register_ctxt.dsc.svc_def = svc;
573 register_ctxt.dsc.chr_def = chr;
574 register_ctxt.dsc.dsc_def = dsc;
575 register_cb(®ister_ctxt, cb_arg);
576 }
577
578 STATS_INC(ble_gatts_stats, dscs);
579
580 return 0;
581
582 }
583
584 static int
ble_gatts_clt_cfg_find_idx(struct ble_gatts_clt_cfg * cfgs,uint16_t chr_val_handle)585 ble_gatts_clt_cfg_find_idx(struct ble_gatts_clt_cfg *cfgs,
586 uint16_t chr_val_handle)
587 {
588 struct ble_gatts_clt_cfg *cfg;
589 int i;
590
591 for (i = 0; i < ble_gatts_num_cfgable_chrs; i++) {
592 cfg = cfgs + i;
593 if (cfg->chr_val_handle == chr_val_handle) {
594 return i;
595 }
596 }
597
598 return -1;
599 }
600
601 static struct ble_gatts_clt_cfg *
ble_gatts_clt_cfg_find(struct ble_gatts_clt_cfg * cfgs,uint16_t chr_val_handle)602 ble_gatts_clt_cfg_find(struct ble_gatts_clt_cfg *cfgs,
603 uint16_t chr_val_handle)
604 {
605 int idx;
606
607 idx = ble_gatts_clt_cfg_find_idx(cfgs, chr_val_handle);
608 if (idx == -1) {
609 return NULL;
610 } else {
611 return cfgs + idx;
612 }
613 }
614
615 static void
ble_gatts_subscribe_event(uint16_t conn_handle,uint16_t attr_handle,uint8_t reason,uint8_t prev_flags,uint8_t cur_flags)616 ble_gatts_subscribe_event(uint16_t conn_handle, uint16_t attr_handle,
617 uint8_t reason,
618 uint8_t prev_flags, uint8_t cur_flags)
619 {
620 if ((prev_flags ^ cur_flags) & ~BLE_GATTS_CLT_CFG_F_RESERVED) {
621 ble_gap_subscribe_event(conn_handle,
622 attr_handle,
623 reason,
624 prev_flags & BLE_GATTS_CLT_CFG_F_NOTIFY,
625 cur_flags & BLE_GATTS_CLT_CFG_F_NOTIFY,
626 prev_flags & BLE_GATTS_CLT_CFG_F_INDICATE,
627 cur_flags & BLE_GATTS_CLT_CFG_F_INDICATE);
628 }
629 }
630
631 /**
632 * Performs a read or write access on a client characteritic configuration
633 * descriptor (CCCD).
634 *
635 * @param conn The connection of the peer doing the accessing.
636 * @apram attr_handle The handle of the CCCD.
637 * @param att_op The ATT operation being performed (read or
638 * write).
639 * @param ctxt Communication channel between this function and
640 * the caller within the nimble stack.
641 * Semantics depends on the operation being
642 * performed.
643 * @param out_cccd If the CCCD should be persisted as a result of
644 * the access, the data-to-be-persisted gets
645 * written here. If no persistence is
646 * necessary, out_cccd->chr_val_handle is set
647 * to 0.
648 *
649 * @return 0 on success; nonzero on failure.
650 */
651 static int
ble_gatts_clt_cfg_access_locked(struct ble_hs_conn * conn,uint16_t attr_handle,uint8_t att_op,uint16_t offset,struct os_mbuf * om,struct ble_store_value_cccd * out_cccd,uint8_t * out_prev_clt_cfg_flags,uint8_t * out_cur_clt_cfg_flags)652 ble_gatts_clt_cfg_access_locked(struct ble_hs_conn *conn, uint16_t attr_handle,
653 uint8_t att_op, uint16_t offset,
654 struct os_mbuf *om,
655 struct ble_store_value_cccd *out_cccd,
656 uint8_t *out_prev_clt_cfg_flags,
657 uint8_t *out_cur_clt_cfg_flags)
658 {
659 struct ble_gatts_clt_cfg *clt_cfg;
660 uint16_t chr_val_handle;
661 uint16_t flags;
662 uint8_t gatt_op;
663 uint8_t *buf;
664
665 /* Assume nothing needs to be persisted. */
666 out_cccd->chr_val_handle = 0;
667
668 /* We always register the client characteristics descriptor with handle
669 * (chr_val + 1).
670 */
671 chr_val_handle = attr_handle - 1;
672 if (chr_val_handle > attr_handle) {
673 /* Attribute handle wrapped somehow. */
674 return BLE_ATT_ERR_UNLIKELY;
675 }
676
677 clt_cfg = ble_gatts_clt_cfg_find(conn->bhc_gatt_svr.clt_cfgs,
678 chr_val_handle);
679 if (clt_cfg == NULL) {
680 return BLE_ATT_ERR_UNLIKELY;
681 }
682
683 /* Assume no change in flags. */
684 *out_prev_clt_cfg_flags = clt_cfg->flags;
685 *out_cur_clt_cfg_flags = clt_cfg->flags;
686
687 gatt_op = ble_gatts_dsc_op(att_op);
688 ble_gatts_dsc_inc_stat(gatt_op);
689
690 switch (gatt_op) {
691 case BLE_GATT_ACCESS_OP_READ_DSC:
692 STATS_INC(ble_gatts_stats, dsc_reads);
693 buf = os_mbuf_extend(om, 2);
694 if (buf == NULL) {
695 return BLE_ATT_ERR_INSUFFICIENT_RES;
696 }
697 put_le16(buf, clt_cfg->flags & ~BLE_GATTS_CLT_CFG_F_RESERVED);
698 break;
699
700 case BLE_GATT_ACCESS_OP_WRITE_DSC:
701 STATS_INC(ble_gatts_stats, dsc_writes);
702 if (OS_MBUF_PKTLEN(om) != 2) {
703 return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
704 }
705
706 om = os_mbuf_pullup(om, 2);
707 BLE_HS_DBG_ASSERT(om != NULL);
708
709 flags = get_le16(om->om_data);
710 if ((flags & ~clt_cfg->allowed) != 0) {
711 return BLE_ATT_ERR_REQ_NOT_SUPPORTED;
712 }
713
714 if (clt_cfg->flags != flags) {
715 clt_cfg->flags = flags;
716 *out_cur_clt_cfg_flags = flags;
717
718 /* Successful writes get persisted for bonded connections. */
719 if (conn->bhc_sec_state.bonded) {
720 out_cccd->peer_addr = conn->bhc_peer_addr;
721 out_cccd->chr_val_handle = chr_val_handle;
722 out_cccd->flags = clt_cfg->flags;
723 out_cccd->value_changed = 0;
724 }
725 }
726 break;
727
728 default:
729 BLE_HS_DBG_ASSERT(0);
730 return BLE_ATT_ERR_UNLIKELY;
731 }
732
733 return 0;
734 }
735
736 static int
ble_gatts_clt_cfg_access(uint16_t conn_handle,uint16_t attr_handle,uint8_t op,uint16_t offset,struct os_mbuf ** om,void * arg)737 ble_gatts_clt_cfg_access(uint16_t conn_handle, uint16_t attr_handle,
738 uint8_t op, uint16_t offset, struct os_mbuf **om,
739 void *arg)
740 {
741 struct ble_store_value_cccd cccd_value;
742 struct ble_store_key_cccd cccd_key;
743 struct ble_hs_conn *conn;
744 uint16_t chr_val_handle;
745 uint8_t prev_flags;
746 uint8_t cur_flags;
747 int rc;
748
749 ble_hs_lock();
750
751 conn = ble_hs_conn_find(conn_handle);
752 if (conn == NULL) {
753 rc = BLE_ATT_ERR_UNLIKELY;
754 } else {
755 rc = ble_gatts_clt_cfg_access_locked(conn, attr_handle, op, offset,
756 *om, &cccd_value, &prev_flags,
757 &cur_flags);
758 }
759
760 ble_hs_unlock();
761
762 if (rc != 0) {
763 return rc;
764 }
765
766 /* The value attribute is always immediately after the declaration. */
767 chr_val_handle = attr_handle - 1;
768
769 /* Tell the application if the peer changed its subscription state. */
770 ble_gatts_subscribe_event(conn_handle, chr_val_handle,
771 BLE_GAP_SUBSCRIBE_REASON_WRITE,
772 prev_flags, cur_flags);
773
774 /* Persist the CCCD if required. */
775 if (cccd_value.chr_val_handle != 0) {
776 if (cccd_value.flags == 0) {
777 ble_store_key_from_value_cccd(&cccd_key, &cccd_value);
778 rc = ble_store_delete_cccd(&cccd_key);
779 } else {
780 rc = ble_store_write_cccd(&cccd_value);
781 }
782 }
783
784 return rc;
785 }
786
787 static int
ble_gatts_register_clt_cfg_dsc(uint16_t * att_handle)788 ble_gatts_register_clt_cfg_dsc(uint16_t *att_handle)
789 {
790 int rc;
791
792 rc = ble_att_svr_register(uuid_ccc, BLE_ATT_F_READ | BLE_ATT_F_WRITE, 0,
793 att_handle, ble_gatts_clt_cfg_access, NULL);
794 if (rc != 0) {
795 return rc;
796 }
797
798 STATS_INC(ble_gatts_stats, dscs);
799
800 return 0;
801 }
802
803 static int
ble_gatts_register_chr(const struct ble_gatt_svc_def * svc,const struct ble_gatt_chr_def * chr,ble_gatt_register_fn * register_cb,void * cb_arg)804 ble_gatts_register_chr(const struct ble_gatt_svc_def *svc,
805 const struct ble_gatt_chr_def *chr,
806 ble_gatt_register_fn *register_cb, void *cb_arg)
807 {
808 struct ble_gatt_register_ctxt register_ctxt;
809 struct ble_gatt_dsc_def *dsc;
810 uint16_t def_handle;
811 uint16_t val_handle;
812 uint16_t dsc_handle;
813 uint8_t att_flags;
814 int rc;
815
816 if (!ble_gatts_chr_is_sane(chr)) {
817 return BLE_HS_EINVAL;
818 }
819
820 if (ble_gatts_chr_clt_cfg_allowed(chr) != 0) {
821 if (ble_gatts_num_cfgable_chrs > ble_hs_max_client_configs) {
822 return BLE_HS_ENOMEM;
823 }
824 ble_gatts_num_cfgable_chrs++;
825 }
826
827 /* Register characteristic definition attribute (cast away const on
828 * callback arg).
829 */
830 rc = ble_att_svr_register(uuid_chr, BLE_ATT_F_READ, 0, &def_handle,
831 ble_gatts_chr_def_access, (void *)chr);
832 if (rc != 0) {
833 return rc;
834 }
835
836 /* Register characteristic value attribute (cast away const on callback
837 * arg).
838 */
839 att_flags = ble_gatts_att_flags_from_chr_flags(chr->flags);
840 rc = ble_att_svr_register(chr->uuid, att_flags, chr->min_key_size,
841 &val_handle, ble_gatts_chr_val_access,
842 (void *)chr);
843 if (rc != 0) {
844 return rc;
845 }
846 BLE_HS_DBG_ASSERT(val_handle == def_handle + 1);
847
848 if (chr->val_handle != NULL) {
849 *chr->val_handle = val_handle;
850 }
851
852 if (register_cb != NULL) {
853 register_ctxt.op = BLE_GATT_REGISTER_OP_CHR;
854 register_ctxt.chr.def_handle = def_handle;
855 register_ctxt.chr.val_handle = val_handle;
856 register_ctxt.chr.svc_def = svc;
857 register_ctxt.chr.chr_def = chr;
858 register_cb(®ister_ctxt, cb_arg);
859 }
860
861 if (ble_gatts_chr_clt_cfg_allowed(chr) != 0) {
862 rc = ble_gatts_register_clt_cfg_dsc(&dsc_handle);
863 if (rc != 0) {
864 return rc;
865 }
866 BLE_HS_DBG_ASSERT(dsc_handle == def_handle + 2);
867 }
868
869 /* Register each descriptor. */
870 if (chr->descriptors != NULL) {
871 for (dsc = chr->descriptors; dsc->uuid != NULL; dsc++) {
872 rc = ble_gatts_register_dsc(svc, chr, dsc, def_handle, register_cb,
873 cb_arg);
874 if (rc != 0) {
875 return rc;
876 }
877 }
878 }
879
880 STATS_INC(ble_gatts_stats, chrs);
881
882 return 0;
883 }
884
885 static int
ble_gatts_svc_type_to_uuid(uint8_t svc_type,const ble_uuid_t ** uuid)886 ble_gatts_svc_type_to_uuid(uint8_t svc_type, const ble_uuid_t **uuid)
887 {
888 switch (svc_type) {
889 case BLE_GATT_SVC_TYPE_PRIMARY:
890 *uuid = uuid_pri;
891 return 0;
892
893 case BLE_GATT_SVC_TYPE_SECONDARY:
894 *uuid = uuid_sec;
895 return 0;
896
897 default:
898 return BLE_HS_EINVAL;
899 }
900 }
901
902 static int
ble_gatts_svc_is_sane(const struct ble_gatt_svc_def * svc)903 ble_gatts_svc_is_sane(const struct ble_gatt_svc_def *svc)
904 {
905 if (svc->type != BLE_GATT_SVC_TYPE_PRIMARY &&
906 svc->type != BLE_GATT_SVC_TYPE_SECONDARY) {
907
908 return 0;
909 }
910
911 if (svc->uuid == NULL) {
912 return 0;
913 }
914
915 return 1;
916 }
917
918 static int
ble_gatts_register_svc(const struct ble_gatt_svc_def * svc,uint16_t * out_handle,ble_gatt_register_fn * register_cb,void * cb_arg)919 ble_gatts_register_svc(const struct ble_gatt_svc_def *svc,
920 uint16_t *out_handle,
921 ble_gatt_register_fn *register_cb, void *cb_arg)
922 {
923 const struct ble_gatt_chr_def *chr;
924 struct ble_gatt_register_ctxt register_ctxt;
925 const ble_uuid_t *uuid;
926 int idx;
927 int rc;
928 int i;
929
930 if (!ble_gatts_svc_incs_satisfied(svc)) {
931 return BLE_HS_EAGAIN;
932 }
933
934 if (!ble_gatts_svc_is_sane(svc)) {
935 return BLE_HS_EINVAL;
936 }
937
938 /* Prevent spurious maybe-uninitialized gcc warning. */
939 uuid = NULL;
940
941 rc = ble_gatts_svc_type_to_uuid(svc->type, &uuid);
942 BLE_HS_DBG_ASSERT_EVAL(rc == 0);
943
944 /* Register service definition attribute (cast away const on callback
945 * arg).
946 */
947 rc = ble_att_svr_register(uuid, BLE_ATT_F_READ, 0, out_handle,
948 ble_gatts_svc_access, (void *)svc);
949 if (rc != 0) {
950 return rc;
951 }
952
953 if (register_cb != NULL) {
954 register_ctxt.op = BLE_GATT_REGISTER_OP_SVC;
955 register_ctxt.svc.handle = *out_handle;
956 register_ctxt.svc.svc_def = svc;
957 register_cb(®ister_ctxt, cb_arg);
958 }
959
960 /* Register each include. */
961 if (svc->includes != NULL) {
962 for (i = 0; svc->includes[i] != NULL; i++) {
963 idx = ble_gatts_find_svc_entry_idx(svc->includes[i]);
964 BLE_HS_DBG_ASSERT_EVAL(idx != -1);
965
966 rc = ble_gatts_register_inc(ble_gatts_svc_entries + idx);
967 if (rc != 0) {
968 return rc;
969 }
970 }
971 }
972
973 /* Register each characteristic. */
974 if (svc->characteristics != NULL) {
975 for (chr = svc->characteristics; chr->uuid != NULL; chr++) {
976 rc = ble_gatts_register_chr(svc, chr, register_cb, cb_arg);
977 if (rc != 0) {
978 return rc;
979 }
980 }
981 }
982
983 STATS_INC(ble_gatts_stats, svcs);
984
985 return 0;
986 }
987
988 static int
ble_gatts_register_round(int * out_num_registered,ble_gatt_register_fn * cb,void * cb_arg)989 ble_gatts_register_round(int *out_num_registered, ble_gatt_register_fn *cb,
990 void *cb_arg)
991 {
992 struct ble_gatts_svc_entry *entry;
993 uint16_t handle;
994 int rc;
995 int i;
996
997 *out_num_registered = 0;
998 for (i = 0; i < ble_gatts_num_svc_entries; i++) {
999 entry = ble_gatts_svc_entries + i;
1000
1001 if (entry->handle == 0) {
1002 rc = ble_gatts_register_svc(entry->svc, &handle, cb, cb_arg);
1003 switch (rc) {
1004 case 0:
1005 /* Service successfully registered. */
1006 entry->handle = handle;
1007 entry->end_group_handle = ble_att_svr_prev_handle();
1008 (*out_num_registered)++;
1009 break;
1010
1011 case BLE_HS_EAGAIN:
1012 /* Service could not be registered due to unsatisfied includes.
1013 * Try again on the next iteration.
1014 */
1015 break;
1016
1017 default:
1018 return rc;
1019 }
1020 }
1021 }
1022
1023 if (*out_num_registered == 0) {
1024 /* There is a circular dependency. */
1025 return BLE_HS_EINVAL;
1026 }
1027
1028 return 0;
1029 }
1030
1031 /**
1032 * Registers a set of services, characteristics, and descriptors to be accessed
1033 * by GATT clients.
1034 *
1035 * @param svcs A table of the service definitions to be
1036 * registered.
1037 * @param cb The function to call for each service,
1038 * characteristic, and descriptor that gets
1039 * registered.
1040 * @param cb_arg The optional argument to pass to the callback
1041 * function.
1042 *
1043 * @return 0 on success;
1044 * BLE_HS_ENOMEM if registration failed due to
1045 * resource exhaustion;
1046 * BLE_HS_EINVAL if the service definition table
1047 * contains an invalid element.
1048 */
1049 int
ble_gatts_register_svcs(const struct ble_gatt_svc_def * svcs,ble_gatt_register_fn * cb,void * cb_arg)1050 ble_gatts_register_svcs(const struct ble_gatt_svc_def *svcs,
1051 ble_gatt_register_fn *cb, void *cb_arg)
1052 {
1053 int total_registered;
1054 int cur_registered;
1055 int num_svcs;
1056 int idx;
1057 int rc;
1058 int i;
1059
1060 for (i = 0; svcs[i].type != BLE_GATT_SVC_TYPE_END; i++) {
1061 idx = ble_gatts_num_svc_entries + i;
1062 if (idx >= ble_hs_max_services) {
1063 return BLE_HS_ENOMEM;
1064 }
1065
1066 ble_gatts_svc_entries[idx].svc = svcs + i;
1067 ble_gatts_svc_entries[idx].handle = 0;
1068 ble_gatts_svc_entries[idx].end_group_handle = 0xffff;
1069 }
1070 num_svcs = i;
1071 ble_gatts_num_svc_entries += num_svcs;
1072
1073 total_registered = 0;
1074 while (total_registered < num_svcs) {
1075 rc = ble_gatts_register_round(&cur_registered, cb, cb_arg);
1076 if (rc != 0) {
1077 return rc;
1078 }
1079 total_registered += cur_registered;
1080 }
1081
1082 return 0;
1083 }
1084
1085 static int
ble_gatts_clt_cfg_size(void)1086 ble_gatts_clt_cfg_size(void)
1087 {
1088 return ble_gatts_num_cfgable_chrs * sizeof (struct ble_gatts_clt_cfg);
1089 }
1090
1091 /**
1092 * Handles GATT server clean up for a terminated connection:
1093 * o Informs the application that the peer is no longer subscribed to any
1094 * characteristic updates.
1095 * o Frees GATT server resources consumed by the connection (CCCDs).
1096 */
1097 void
ble_gatts_connection_broken(uint16_t conn_handle)1098 ble_gatts_connection_broken(uint16_t conn_handle)
1099 {
1100 struct ble_gatts_clt_cfg *clt_cfgs;
1101 struct ble_hs_conn *conn;
1102 int num_clt_cfgs;
1103 int rc;
1104 int i;
1105
1106 /* Find the specified connection and extract its CCCD entries. Extracting
1107 * the clt_cfg pointer and setting the original to null is done for two
1108 * reasons:
1109 * 1. So that the CCCD entries can be safely processed after unlocking
1110 * the mutex.
1111 * 2. To ensure a subsequent indicate procedure for this peer is not
1112 * attempted, as the connection is about to be terminated. This
1113 * avoids a spurious notify-tx GAP event callback to the
1114 * application. By setting the clt_cfg pointer to null, it is
1115 * assured that the connection has no pending indications to send.
1116 */
1117 ble_hs_lock();
1118 conn = ble_hs_conn_find(conn_handle);
1119 if (conn != NULL) {
1120 clt_cfgs = conn->bhc_gatt_svr.clt_cfgs;
1121 num_clt_cfgs = conn->bhc_gatt_svr.num_clt_cfgs;
1122
1123 conn->bhc_gatt_svr.clt_cfgs = NULL;
1124 conn->bhc_gatt_svr.num_clt_cfgs = 0;
1125 }
1126 ble_hs_unlock();
1127
1128 if (conn == NULL) {
1129 return;
1130 }
1131
1132 /* If there is an indicate procedure in progress for this connection,
1133 * inform the application that it has failed.
1134 */
1135 ble_gatts_indicate_fail_notconn(conn_handle);
1136
1137 /* Now that the mutex is unlocked, inform the application that the peer is
1138 * no longer subscribed to any characteristic updates.
1139 */
1140 if (clt_cfgs != NULL) {
1141 for (i = 0; i < num_clt_cfgs; i++) {
1142 ble_gatts_subscribe_event(conn_handle, clt_cfgs[i].chr_val_handle,
1143 BLE_GAP_SUBSCRIBE_REASON_TERM,
1144 clt_cfgs[i].flags, 0);
1145 }
1146
1147 rc = os_memblock_put(&ble_gatts_clt_cfg_pool, clt_cfgs);
1148 BLE_HS_DBG_ASSERT_EVAL(rc == 0);
1149 }
1150 }
1151
1152 static void
ble_gatts_free_svc_defs(void)1153 ble_gatts_free_svc_defs(void)
1154 {
1155 free(ble_gatts_svc_defs);
1156 ble_gatts_svc_defs = NULL;
1157 ble_gatts_num_svc_defs = 0;
1158 }
1159
1160 static void
ble_gatts_free_mem(void)1161 ble_gatts_free_mem(void)
1162 {
1163 free(ble_gatts_clt_cfg_mem);
1164 ble_gatts_clt_cfg_mem = NULL;
1165
1166 free(ble_gatts_svc_entries);
1167 ble_gatts_svc_entries = NULL;
1168 }
1169
1170 int
ble_gatts_start(void)1171 ble_gatts_start(void)
1172 {
1173 struct ble_att_svr_entry *ha;
1174 struct ble_gatt_chr_def *chr;
1175 uint16_t allowed_flags;
1176 ble_uuid16_t uuid = BLE_UUID16_INIT(BLE_ATT_UUID_CHARACTERISTIC);
1177 int num_elems;
1178 int idx;
1179 int rc;
1180 int i;
1181
1182 ble_hs_lock();
1183 if (!ble_gatts_mutable()) {
1184 rc = BLE_HS_EBUSY;
1185 goto done;
1186 }
1187
1188 ble_gatts_free_mem();
1189
1190 rc = ble_att_svr_start();
1191 if (rc != 0) {
1192 goto done;
1193 }
1194
1195 if (ble_hs_max_client_configs > 0) {
1196 ble_gatts_clt_cfg_mem = malloc(
1197 OS_MEMPOOL_BYTES(ble_hs_max_client_configs,
1198 sizeof (struct ble_gatts_clt_cfg)));
1199 if (ble_gatts_clt_cfg_mem == NULL) {
1200 rc = BLE_HS_ENOMEM;
1201 goto done;
1202 }
1203 }
1204
1205 if (ble_hs_max_services > 0) {
1206 ble_gatts_svc_entries =
1207 malloc(ble_hs_max_services * sizeof *ble_gatts_svc_entries);
1208 if (ble_gatts_svc_entries == NULL) {
1209 rc = BLE_HS_ENOMEM;
1210 goto done;
1211 }
1212 }
1213
1214
1215 ble_gatts_num_svc_entries = 0;
1216 for (i = 0; i < ble_gatts_num_svc_defs; i++) {
1217 rc = ble_gatts_register_svcs(ble_gatts_svc_defs[i],
1218 ble_hs_cfg.gatts_register_cb,
1219 ble_hs_cfg.gatts_register_arg);
1220 if (rc != 0) {
1221 goto done;
1222 }
1223 }
1224 ble_gatts_free_svc_defs();
1225
1226 if (ble_gatts_num_cfgable_chrs == 0) {
1227 rc = 0;
1228 goto done;
1229 }
1230
1231 /* Initialize client-configuration memory pool. */
1232 num_elems = ble_hs_max_client_configs / ble_gatts_num_cfgable_chrs;
1233 rc = os_mempool_init(&ble_gatts_clt_cfg_pool, num_elems,
1234 ble_gatts_clt_cfg_size(), ble_gatts_clt_cfg_mem,
1235 "ble_gatts_clt_cfg_pool");
1236 if (rc != 0) {
1237 rc = BLE_HS_EOS;
1238 goto done;
1239 }
1240
1241 /* Allocate the cached array of handles for the configuration
1242 * characteristics.
1243 */
1244 ble_gatts_clt_cfgs = os_memblock_get(&ble_gatts_clt_cfg_pool);
1245 if (ble_gatts_clt_cfgs == NULL) {
1246 rc = BLE_HS_ENOMEM;
1247 goto done;
1248 }
1249
1250 /* Fill the cache. */
1251 idx = 0;
1252 ha = NULL;
1253 while ((ha = ble_att_svr_find_by_uuid(ha, &uuid.u, 0xffff)) != NULL) {
1254 chr = ha->ha_cb_arg;
1255 allowed_flags = ble_gatts_chr_clt_cfg_allowed(chr);
1256 if (allowed_flags != 0) {
1257 BLE_HS_DBG_ASSERT_EVAL(idx < ble_gatts_num_cfgable_chrs);
1258
1259 ble_gatts_clt_cfgs[idx].chr_val_handle = ha->ha_handle_id + 1;
1260 ble_gatts_clt_cfgs[idx].allowed = allowed_flags;
1261 ble_gatts_clt_cfgs[idx].flags = 0;
1262 idx++;
1263 }
1264 }
1265
1266 done:
1267 if (rc != 0) {
1268 ble_gatts_free_mem();
1269 ble_gatts_free_svc_defs();
1270 }
1271
1272 ble_hs_unlock();
1273 return rc;
1274 }
1275
1276 int
ble_gatts_conn_can_alloc(void)1277 ble_gatts_conn_can_alloc(void)
1278 {
1279 return ble_gatts_num_cfgable_chrs == 0 ||
1280 ble_gatts_clt_cfg_pool.mp_num_free > 0;
1281 }
1282
1283 int
ble_gatts_conn_init(struct ble_gatts_conn * gatts_conn)1284 ble_gatts_conn_init(struct ble_gatts_conn *gatts_conn)
1285 {
1286 if (ble_gatts_num_cfgable_chrs > 0) {
1287 gatts_conn->clt_cfgs = os_memblock_get(&ble_gatts_clt_cfg_pool);
1288 if (gatts_conn->clt_cfgs == NULL) {
1289 return BLE_HS_ENOMEM;
1290 }
1291
1292 /* Initialize the client configuration with a copy of the cache. */
1293 memcpy(gatts_conn->clt_cfgs, ble_gatts_clt_cfgs,
1294 ble_gatts_clt_cfg_size());
1295 gatts_conn->num_clt_cfgs = ble_gatts_num_cfgable_chrs;
1296 } else {
1297 gatts_conn->clt_cfgs = NULL;
1298 gatts_conn->num_clt_cfgs = 0;
1299 }
1300
1301 return 0;
1302 }
1303
1304
1305 /**
1306 * Schedules a notification or indication for the specified peer-CCCD pair. If
1307 * the update should be sent immediately, it is indicated in the return code.
1308 *
1309 * @param conn The connection to schedule the update for.
1310 * @param clt_cfg The client config entry corresponding to the
1311 * peer and affected characteristic.
1312 *
1313 * @return The att_op of the update to send immediately,
1314 * if any. 0 if nothing should get sent.
1315 */
1316 static uint8_t
ble_gatts_schedule_update(struct ble_hs_conn * conn,struct ble_gatts_clt_cfg * clt_cfg)1317 ble_gatts_schedule_update(struct ble_hs_conn *conn,
1318 struct ble_gatts_clt_cfg *clt_cfg)
1319 {
1320 uint8_t att_op;
1321
1322 if (!(clt_cfg->flags & BLE_GATTS_CLT_CFG_F_MODIFIED)) {
1323 /* Characteristic not modified. Nothing to send. */
1324 att_op = 0;
1325 } else if (clt_cfg->flags & BLE_GATTS_CLT_CFG_F_NOTIFY) {
1326 /* Notifications always get sent immediately. */
1327 att_op = BLE_ATT_OP_NOTIFY_REQ;
1328 } else if (clt_cfg->flags & BLE_GATTS_CLT_CFG_F_INDICATE) {
1329 /* Only one outstanding indication per peer is allowed. If we
1330 * are still awaiting an ack, mark this CCCD as updated so that
1331 * we know to send the indication upon receiving the expected ack.
1332 * If there isn't an outstanding indication, send this one now.
1333 */
1334 if (conn->bhc_gatt_svr.indicate_val_handle != 0) {
1335 att_op = 0;
1336 } else {
1337 att_op = BLE_ATT_OP_INDICATE_REQ;
1338 }
1339 } else {
1340 /* Peer isn't subscribed to notifications or indications. Nothing to
1341 * send.
1342 */
1343 att_op = 0;
1344 }
1345
1346 /* If we will be sending an update, clear the modified flag so that we
1347 * don't double-send.
1348 */
1349 if (att_op != 0) {
1350 clt_cfg->flags &= ~BLE_GATTS_CLT_CFG_F_MODIFIED;
1351 }
1352
1353 return att_op;
1354 }
1355
1356 int
ble_gatts_send_next_indicate(uint16_t conn_handle)1357 ble_gatts_send_next_indicate(uint16_t conn_handle)
1358 {
1359 struct ble_gatts_clt_cfg *clt_cfg;
1360 struct ble_hs_conn *conn;
1361 uint16_t chr_val_handle;
1362 int rc;
1363 int i;
1364
1365 /* Assume no pending indications. */
1366 chr_val_handle = 0;
1367
1368 ble_hs_lock();
1369
1370 conn = ble_hs_conn_find(conn_handle);
1371 if (conn != NULL) {
1372 for (i = 0; i < conn->bhc_gatt_svr.num_clt_cfgs; i++) {
1373 clt_cfg = conn->bhc_gatt_svr.clt_cfgs + i;
1374 if (clt_cfg->flags & BLE_GATTS_CLT_CFG_F_MODIFIED) {
1375 BLE_HS_DBG_ASSERT(clt_cfg->flags &
1376 BLE_GATTS_CLT_CFG_F_INDICATE);
1377
1378 chr_val_handle = clt_cfg->chr_val_handle;
1379
1380 /* Clear pending flag in anticipation of indication tx. */
1381 clt_cfg->flags &= ~BLE_GATTS_CLT_CFG_F_MODIFIED;
1382 break;
1383 }
1384 }
1385 }
1386
1387 ble_hs_unlock();
1388
1389 if (conn == NULL) {
1390 return BLE_HS_ENOTCONN;
1391 }
1392
1393 if (chr_val_handle == 0) {
1394 return BLE_HS_ENOENT;
1395 }
1396
1397 rc = ble_gattc_indicate(conn_handle, chr_val_handle);
1398 if (rc != 0) {
1399 return rc;
1400 }
1401
1402 return 0;
1403 }
1404
1405 int
ble_gatts_rx_indicate_ack(uint16_t conn_handle,uint16_t chr_val_handle)1406 ble_gatts_rx_indicate_ack(uint16_t conn_handle, uint16_t chr_val_handle)
1407 {
1408 struct ble_store_value_cccd cccd_value;
1409 struct ble_gatts_clt_cfg *clt_cfg;
1410 struct ble_hs_conn *conn;
1411 int clt_cfg_idx;
1412 int persist;
1413 int rc;
1414
1415 clt_cfg_idx = ble_gatts_clt_cfg_find_idx(ble_gatts_clt_cfgs,
1416 chr_val_handle);
1417 if (clt_cfg_idx == -1) {
1418 /* This characteristic does not have a CCCD. */
1419 return BLE_HS_ENOENT;
1420 }
1421
1422 clt_cfg = ble_gatts_clt_cfgs + clt_cfg_idx;
1423 if (!(clt_cfg->allowed & BLE_GATTS_CLT_CFG_F_INDICATE)) {
1424 /* This characteristic does not allow indications. */
1425 return BLE_HS_ENOENT;
1426 }
1427
1428 ble_hs_lock();
1429
1430 conn = ble_hs_conn_find(conn_handle);
1431 BLE_HS_DBG_ASSERT(conn != NULL);
1432 if (conn->bhc_gatt_svr.indicate_val_handle == chr_val_handle) {
1433 /* This acknowledgement is expected. */
1434 rc = 0;
1435
1436 /* Mark that there is no longer an outstanding txed indicate. */
1437 conn->bhc_gatt_svr.indicate_val_handle = 0;
1438
1439 /* Determine if we need to persist that there is no pending indication
1440 * for this peer-characteristic pair. If the characteristic has not
1441 * been modified since we sent the indication, there is no indication
1442 * pending.
1443 */
1444 BLE_HS_DBG_ASSERT(conn->bhc_gatt_svr.num_clt_cfgs > clt_cfg_idx);
1445 clt_cfg = conn->bhc_gatt_svr.clt_cfgs + clt_cfg_idx;
1446 BLE_HS_DBG_ASSERT(clt_cfg->chr_val_handle == chr_val_handle);
1447
1448 persist = conn->bhc_sec_state.bonded &&
1449 !(clt_cfg->flags & BLE_GATTS_CLT_CFG_F_MODIFIED);
1450 if (persist) {
1451 cccd_value.peer_addr = conn->bhc_peer_addr;
1452 cccd_value.chr_val_handle = chr_val_handle;
1453 cccd_value.flags = clt_cfg->flags;
1454 cccd_value.value_changed = 0;
1455 }
1456 } else {
1457 /* This acknowledgement doesn't correspond to the outstanding
1458 * indication; ignore it.
1459 */
1460 rc = BLE_HS_ENOENT;
1461 }
1462
1463 ble_hs_unlock();
1464
1465 if (rc != 0) {
1466 return rc;
1467 }
1468
1469 if (persist) {
1470 rc = ble_store_write_cccd(&cccd_value);
1471 if (rc != 0) {
1472 /* XXX: How should this error get reported? */
1473 }
1474 }
1475
1476 return 0;
1477 }
1478
1479 void
ble_gatts_chr_updated(uint16_t chr_val_handle)1480 ble_gatts_chr_updated(uint16_t chr_val_handle)
1481 {
1482 struct ble_store_value_cccd cccd_value;
1483 struct ble_store_key_cccd cccd_key;
1484 struct ble_gatts_clt_cfg *clt_cfg;
1485 struct ble_hs_conn *conn;
1486 int new_notifications = 0;
1487 int clt_cfg_idx;
1488 int persist;
1489 int rc;
1490 int i;
1491
1492 /* Determine if notifications or indications are allowed for this
1493 * characteristic. If not, return immediately.
1494 */
1495 clt_cfg_idx = ble_gatts_clt_cfg_find_idx(ble_gatts_clt_cfgs,
1496 chr_val_handle);
1497 if (clt_cfg_idx == -1) {
1498 return;
1499 }
1500
1501 /*** Send notifications and indications to connected devices. */
1502
1503 ble_hs_lock();
1504 for (i = 0; ; i++) {
1505 /* XXX: This is inefficient when there are a lot of connections.
1506 * Consider using a "foreach" function to walk the connection list.
1507 */
1508 conn = ble_hs_conn_find_by_idx(i);
1509 if (conn == NULL) {
1510 break;
1511 }
1512
1513 BLE_HS_DBG_ASSERT_EVAL(conn->bhc_gatt_svr.num_clt_cfgs >
1514 clt_cfg_idx);
1515 clt_cfg = conn->bhc_gatt_svr.clt_cfgs + clt_cfg_idx;
1516 BLE_HS_DBG_ASSERT_EVAL(clt_cfg->chr_val_handle == chr_val_handle);
1517
1518 /* Mark the CCCD entry as modified. */
1519 clt_cfg->flags |= BLE_GATTS_CLT_CFG_F_MODIFIED;
1520 new_notifications = 1;
1521 }
1522 ble_hs_unlock();
1523
1524 if (new_notifications) {
1525 ble_hs_notifications_sched();
1526 }
1527
1528 /*** Persist updated flag for unconnected and not-yet-bonded devices. */
1529
1530 /* Retrieve each record corresponding to the modified characteristic. */
1531 cccd_key.peer_addr = *BLE_ADDR_ANY;
1532 cccd_key.chr_val_handle = chr_val_handle;
1533 cccd_key.idx = 0;
1534
1535 while (1) {
1536 rc = ble_store_read_cccd(&cccd_key, &cccd_value);
1537 if (rc != 0) {
1538 /* Read error or no more CCCD records. */
1539 break;
1540 }
1541
1542 /* Determine if this record needs to be rewritten. */
1543 ble_hs_lock();
1544 conn = ble_hs_conn_find_by_addr(&cccd_key.peer_addr);
1545
1546 if (conn == NULL) {
1547 /* Device isn't connected; persist the changed flag so that an
1548 * update can be sent when the device reconnects and rebonds.
1549 */
1550 persist = 1;
1551 } else if (cccd_value.flags & BLE_GATTS_CLT_CFG_F_INDICATE) {
1552 /* Indication for a connected device; record that the
1553 * characteristic has changed until we receive the ack.
1554 */
1555 persist = 1;
1556 } else {
1557 /* Notification for a connected device; we already sent it so there
1558 * is no need to persist.
1559 */
1560 persist = 0;
1561 }
1562
1563 ble_hs_unlock();
1564
1565 /* Only persist if the value changed flag wasn't already sent (i.e.,
1566 * don't overwrite with identical data).
1567 */
1568 if (persist && !cccd_value.value_changed) {
1569 cccd_value.value_changed = 1;
1570 ble_store_write_cccd(&cccd_value);
1571 }
1572
1573 /* Read the next matching record. */
1574 cccd_key.idx++;
1575 }
1576 }
1577
1578 /**
1579 * Sends notifications or indications for the specified characteristic to all
1580 * connected devices. The bluetooth spec does not allow more than one
1581 * concurrent indication for a single peer, so this function will hold off on
1582 * sending such indications.
1583 */
1584 static void
ble_gatts_tx_notifications_one_chr(uint16_t chr_val_handle)1585 ble_gatts_tx_notifications_one_chr(uint16_t chr_val_handle)
1586 {
1587 struct ble_gatts_clt_cfg *clt_cfg;
1588 struct ble_hs_conn *conn;
1589 uint16_t conn_handle;
1590 uint8_t att_op;
1591 int clt_cfg_idx;
1592 int i;
1593
1594 /* Determine if notifications / indications are enabled for this
1595 * characteristic.
1596 */
1597 clt_cfg_idx = ble_gatts_clt_cfg_find_idx(ble_gatts_clt_cfgs,
1598 chr_val_handle);
1599 if (clt_cfg_idx == -1) {
1600 return;
1601 }
1602
1603 for (i = 0; ; i++) {
1604 ble_hs_lock();
1605
1606 conn = ble_hs_conn_find_by_idx(i);
1607 if (conn != NULL) {
1608 BLE_HS_DBG_ASSERT_EVAL(conn->bhc_gatt_svr.num_clt_cfgs >
1609 clt_cfg_idx);
1610 clt_cfg = conn->bhc_gatt_svr.clt_cfgs + clt_cfg_idx;
1611 BLE_HS_DBG_ASSERT_EVAL(clt_cfg->chr_val_handle == chr_val_handle);
1612
1613 /* Determine what type of command should get sent, if any. */
1614 att_op = ble_gatts_schedule_update(conn, clt_cfg);
1615 conn_handle = conn->bhc_handle;
1616 } else {
1617 /* Silence some spurious gcc warnings. */
1618 att_op = 0;
1619 conn_handle = BLE_HS_CONN_HANDLE_NONE;
1620 }
1621 ble_hs_unlock();
1622
1623 if (conn == NULL) {
1624 /* No more connected devices. */
1625 break;
1626 }
1627
1628 switch (att_op) {
1629 case 0:
1630 break;
1631
1632 case BLE_ATT_OP_NOTIFY_REQ:
1633 ble_gattc_notify(conn_handle, chr_val_handle);
1634 break;
1635
1636 case BLE_ATT_OP_INDICATE_REQ:
1637 ble_gattc_indicate(conn_handle, chr_val_handle);
1638 break;
1639
1640 default:
1641 BLE_HS_DBG_ASSERT(0);
1642 break;
1643 }
1644 }
1645 }
1646
1647 /**
1648 * Sends all pending notifications and indications. The bluetooth spec does
1649 * not allow more than one concurrent indication for a single peer, so this
1650 * function will hold off on sending such indications.
1651 */
1652 void
ble_gatts_tx_notifications(void)1653 ble_gatts_tx_notifications(void)
1654 {
1655 uint16_t chr_val_handle;
1656 int i;
1657
1658 for (i = 0; i < ble_gatts_num_cfgable_chrs; i++) {
1659 chr_val_handle = ble_gatts_clt_cfgs[i].chr_val_handle;
1660 ble_gatts_tx_notifications_one_chr(chr_val_handle);
1661 }
1662 }
1663
1664 /**
1665 * Called when bonding has been restored via the encryption procedure. This
1666 * function:
1667 * o Restores persisted CCCD entries for the connected peer.
1668 * o Sends all pending notifications to the connected peer.
1669 * o Sends up to one pending indication to the connected peer; schedules
1670 * any remaining pending indications.
1671 */
1672 void
ble_gatts_bonding_restored(uint16_t conn_handle)1673 ble_gatts_bonding_restored(uint16_t conn_handle)
1674 {
1675 struct ble_store_value_cccd cccd_value;
1676 struct ble_store_key_cccd cccd_key;
1677 struct ble_gatts_clt_cfg *clt_cfg;
1678 struct ble_hs_conn *conn;
1679 uint8_t att_op;
1680 int rc;
1681
1682 ble_hs_lock();
1683
1684 conn = ble_hs_conn_find(conn_handle);
1685 BLE_HS_DBG_ASSERT(conn != NULL);
1686 BLE_HS_DBG_ASSERT(conn->bhc_sec_state.bonded);
1687
1688 cccd_key.peer_addr = conn->bhc_peer_addr;
1689 cccd_key.chr_val_handle = 0;
1690 cccd_key.idx = 0;
1691
1692 ble_hs_unlock();
1693
1694 while (1) {
1695 rc = ble_store_read_cccd(&cccd_key, &cccd_value);
1696 if (rc != 0) {
1697 break;
1698 }
1699
1700 /* Assume no notification or indication will get sent. */
1701 att_op = 0;
1702
1703 ble_hs_lock();
1704
1705 conn = ble_hs_conn_find(conn_handle);
1706 BLE_HS_DBG_ASSERT(conn != NULL);
1707
1708 clt_cfg = ble_gatts_clt_cfg_find(conn->bhc_gatt_svr.clt_cfgs,
1709 cccd_value.chr_val_handle);
1710 if (clt_cfg != NULL) {
1711 clt_cfg->flags = cccd_value.flags;
1712
1713 if (cccd_value.value_changed) {
1714 /* The characteristic's value changed while the device was
1715 * disconnected or unbonded. Schedule the notification or
1716 * indication now.
1717 */
1718 clt_cfg->flags |= BLE_GATTS_CLT_CFG_F_MODIFIED;
1719 att_op = ble_gatts_schedule_update(conn, clt_cfg);
1720 }
1721 }
1722
1723 ble_hs_unlock();
1724
1725 /* Tell the application if the peer changed its subscription state
1726 * when it was restored from persistence.
1727 */
1728 ble_gatts_subscribe_event(conn_handle, cccd_value.chr_val_handle,
1729 BLE_GAP_SUBSCRIBE_REASON_RESTORE,
1730 0, cccd_value.flags);
1731
1732 switch (att_op) {
1733 case 0:
1734 break;
1735
1736 case BLE_ATT_OP_NOTIFY_REQ:
1737 rc = ble_gattc_notify(conn_handle, cccd_value.chr_val_handle);
1738 if (rc == 0) {
1739 cccd_value.value_changed = 0;
1740 ble_store_write_cccd(&cccd_value);
1741 }
1742 break;
1743
1744 case BLE_ATT_OP_INDICATE_REQ:
1745 ble_gattc_indicate(conn_handle, cccd_value.chr_val_handle);
1746 break;
1747
1748 default:
1749 BLE_HS_DBG_ASSERT(0);
1750 break;
1751 }
1752
1753 cccd_key.idx++;
1754 }
1755 }
1756
1757 static struct ble_gatts_svc_entry *
ble_gatts_find_svc_entry(const ble_uuid_t * uuid)1758 ble_gatts_find_svc_entry(const ble_uuid_t *uuid)
1759 {
1760 struct ble_gatts_svc_entry *entry;
1761 int i;
1762
1763 for (i = 0; i < ble_gatts_num_svc_entries; i++) {
1764 entry = ble_gatts_svc_entries + i;
1765 if (ble_uuid_cmp(uuid, entry->svc->uuid) == 0) {
1766 return entry;
1767 }
1768 }
1769
1770 return NULL;
1771 }
1772
1773 static int
ble_gatts_find_svc_chr_attr(const ble_uuid_t * svc_uuid,const ble_uuid_t * chr_uuid,struct ble_gatts_svc_entry ** out_svc_entry,struct ble_att_svr_entry ** out_att_chr)1774 ble_gatts_find_svc_chr_attr(const ble_uuid_t *svc_uuid,
1775 const ble_uuid_t *chr_uuid,
1776 struct ble_gatts_svc_entry **out_svc_entry,
1777 struct ble_att_svr_entry **out_att_chr)
1778 {
1779 struct ble_gatts_svc_entry *svc_entry;
1780 struct ble_att_svr_entry *att_svc;
1781 struct ble_att_svr_entry *next;
1782 struct ble_att_svr_entry *cur;
1783
1784 svc_entry = ble_gatts_find_svc_entry(svc_uuid);
1785 if (svc_entry == NULL) {
1786 return BLE_HS_ENOENT;
1787 }
1788
1789 att_svc = ble_att_svr_find_by_handle(svc_entry->handle);
1790 if (att_svc == NULL) {
1791 return BLE_HS_EUNKNOWN;
1792 }
1793
1794 cur = STAILQ_NEXT(att_svc, ha_next);
1795 while (1) {
1796 if (cur == NULL) {
1797 /* Reached end of attribute list without a match. */
1798 return BLE_HS_ENOENT;
1799 }
1800 next = STAILQ_NEXT(cur, ha_next);
1801
1802 if (cur->ha_handle_id == svc_entry->end_group_handle) {
1803 /* Reached end of service without a match. */
1804 return BLE_HS_ENOENT;
1805 }
1806
1807 if (ble_uuid_u16(cur->ha_uuid) == BLE_ATT_UUID_CHARACTERISTIC &&
1808 next != NULL &&
1809 ble_uuid_cmp(next->ha_uuid, chr_uuid) == 0) {
1810
1811 if (out_svc_entry != NULL) {
1812 *out_svc_entry = svc_entry;
1813 }
1814 if (out_att_chr != NULL) {
1815 *out_att_chr = next;
1816 }
1817 return 0;
1818 }
1819
1820 cur = next;
1821 }
1822 }
1823
1824 int
ble_gatts_find_svc(const ble_uuid_t * uuid,uint16_t * out_handle)1825 ble_gatts_find_svc(const ble_uuid_t *uuid, uint16_t *out_handle)
1826 {
1827 struct ble_gatts_svc_entry *entry;
1828
1829 entry = ble_gatts_find_svc_entry(uuid);
1830 if (entry == NULL) {
1831 return BLE_HS_ENOENT;
1832 }
1833
1834 if (out_handle != NULL) {
1835 *out_handle = entry->handle;
1836 }
1837 return 0;
1838 }
1839
1840 int
ble_gatts_find_chr(const ble_uuid_t * svc_uuid,const ble_uuid_t * chr_uuid,uint16_t * out_def_handle,uint16_t * out_val_handle)1841 ble_gatts_find_chr(const ble_uuid_t *svc_uuid, const ble_uuid_t *chr_uuid,
1842 uint16_t *out_def_handle, uint16_t *out_val_handle)
1843 {
1844 struct ble_att_svr_entry *att_chr;
1845 int rc;
1846
1847 rc = ble_gatts_find_svc_chr_attr(svc_uuid, chr_uuid, NULL, &att_chr);
1848 if (rc != 0) {
1849 return rc;
1850 }
1851
1852 if (out_def_handle) {
1853 *out_def_handle = att_chr->ha_handle_id - 1;
1854 }
1855 if (out_val_handle) {
1856 *out_val_handle = att_chr->ha_handle_id;
1857 }
1858 return 0;
1859 }
1860
1861 int
ble_gatts_find_dsc(const ble_uuid_t * svc_uuid,const ble_uuid_t * chr_uuid,const ble_uuid_t * dsc_uuid,uint16_t * out_handle)1862 ble_gatts_find_dsc(const ble_uuid_t *svc_uuid, const ble_uuid_t *chr_uuid,
1863 const ble_uuid_t *dsc_uuid, uint16_t *out_handle)
1864 {
1865 struct ble_gatts_svc_entry *svc_entry;
1866 struct ble_att_svr_entry *att_chr;
1867 struct ble_att_svr_entry *cur;
1868 uint16_t uuid16;
1869 int rc;
1870
1871 rc = ble_gatts_find_svc_chr_attr(svc_uuid, chr_uuid, &svc_entry,
1872 &att_chr);
1873 if (rc != 0) {
1874 return rc;
1875 }
1876
1877 cur = STAILQ_NEXT(att_chr, ha_next);
1878 while (1) {
1879 if (cur == NULL) {
1880 /* Reached end of attribute list without a match. */
1881 return BLE_HS_ENOENT;
1882 }
1883
1884 if (cur->ha_handle_id > svc_entry->end_group_handle) {
1885 /* Reached end of service without a match. */
1886 return BLE_HS_ENOENT;
1887 }
1888
1889 uuid16 = ble_uuid_u16(cur->ha_uuid);
1890 if (uuid16 == BLE_ATT_UUID_CHARACTERISTIC) {
1891 /* Reached end of characteristic without a match. */
1892 return BLE_HS_ENOENT;
1893 }
1894
1895 if (ble_uuid_cmp(cur->ha_uuid, dsc_uuid) == 0) {
1896 if (out_handle != NULL) {
1897 *out_handle = cur->ha_handle_id;
1898 return 0;
1899 }
1900 }
1901 cur = STAILQ_NEXT(cur, ha_next);
1902 }
1903 }
1904
1905 int
ble_gatts_add_svcs(const struct ble_gatt_svc_def * svcs)1906 ble_gatts_add_svcs(const struct ble_gatt_svc_def *svcs)
1907 {
1908 void *p;
1909 int rc;
1910
1911 ble_hs_lock();
1912 if (!ble_gatts_mutable()) {
1913 rc = BLE_HS_EBUSY;
1914 goto done;
1915 }
1916
1917 p = realloc(ble_gatts_svc_defs,
1918 (ble_gatts_num_svc_defs + 1) * sizeof *ble_gatts_svc_defs);
1919 if (p == NULL) {
1920 rc = BLE_HS_ENOMEM;
1921 goto done;
1922 }
1923
1924 ble_gatts_svc_defs = p;
1925 ble_gatts_svc_defs[ble_gatts_num_svc_defs] = svcs;
1926 ble_gatts_num_svc_defs++;
1927
1928 rc = 0;
1929
1930 done:
1931 ble_hs_unlock();
1932 return rc;
1933 }
1934
1935 int
ble_gatts_svc_set_visibility(uint16_t handle,int visible)1936 ble_gatts_svc_set_visibility(uint16_t handle, int visible)
1937 {
1938 int i;
1939
1940 for (i = 0; i < ble_gatts_num_svc_entries; i++) {
1941 struct ble_gatts_svc_entry *entry = &ble_gatts_svc_entries[i];
1942
1943 if (entry->handle == handle) {
1944 if (visible) {
1945 ble_att_svr_restore_range(entry->handle, entry->end_group_handle);
1946 } else {
1947 ble_att_svr_hide_range(entry->handle, entry->end_group_handle);
1948 }
1949 return 0;
1950 }
1951 }
1952
1953 return BLE_HS_ENOENT;
1954 }
1955
1956 /**
1957 * Accumulates counts of each resource type required by the specified service
1958 * definition array. This function is generally used to calculate some host
1959 * configuration values prior to initialization. This function adds the counts
1960 * to the appropriate fields in the supplied ble_gatt_resources object without
1961 * clearing them first, so it can be called repeatedly with different inputs to
1962 * calculate totals. Be sure to zero the resource struct prior to the first
1963 * call to this function.
1964 *
1965 * @param svcs The service array containing the resource
1966 * definitions to be counted.
1967 * @param res The resource counts are accumulated in this
1968 * struct.
1969 *
1970 * @return 0 on success;
1971 * BLE_HS_EINVAL if the svcs array contains an
1972 * invalid resource definition.
1973 */
1974 static int
ble_gatts_count_resources(const struct ble_gatt_svc_def * svcs,struct ble_gatt_resources * res)1975 ble_gatts_count_resources(const struct ble_gatt_svc_def *svcs,
1976 struct ble_gatt_resources *res)
1977 {
1978 const struct ble_gatt_svc_def *svc;
1979 const struct ble_gatt_chr_def *chr;
1980 int s;
1981 int i;
1982 int c;
1983 int d;
1984
1985 for (s = 0; svcs[s].type != BLE_GATT_SVC_TYPE_END; s++) {
1986 svc = svcs + s;
1987
1988 if (!ble_gatts_svc_is_sane(svc)) {
1989 BLE_HS_DBG_ASSERT(0);
1990 return BLE_HS_EINVAL;
1991 }
1992
1993 /* Each service requires:
1994 * o 1 service
1995 * o 1 attribute
1996 */
1997 res->svcs++;
1998 res->attrs++;
1999
2000 if (svc->includes != NULL) {
2001 for (i = 0; svc->includes[i] != NULL; i++) {
2002 /* Each include requires:
2003 * o 1 include
2004 * o 1 attribute
2005 */
2006 res->incs++;
2007 res->attrs++;
2008 }
2009 }
2010
2011 if (svc->characteristics != NULL) {
2012 for (c = 0; svc->characteristics[c].uuid != NULL; c++) {
2013 chr = svc->characteristics + c;
2014
2015 if (!ble_gatts_chr_is_sane(chr)) {
2016 BLE_HS_DBG_ASSERT(0);
2017 return BLE_HS_EINVAL;
2018 }
2019
2020 /* Each characteristic requires:
2021 * o 1 characteristic
2022 * o 2 attributes
2023 */
2024 res->chrs++;
2025 res->attrs += 2;
2026
2027 /* If the characteristic permits notifications or indications,
2028 * it has a CCCD.
2029 */
2030 if (chr->flags & BLE_GATT_CHR_F_NOTIFY ||
2031 chr->flags & BLE_GATT_CHR_F_INDICATE) {
2032
2033 /* Each CCCD requires:
2034 * o 1 descriptor
2035 * o 1 CCCD
2036 * o 1 attribute
2037 */
2038 res->dscs++;
2039 res->cccds++;
2040 res->attrs++;
2041 }
2042
2043 if (chr->descriptors != NULL) {
2044 for (d = 0; chr->descriptors[d].uuid != NULL; d++) {
2045 if (!ble_gatts_dsc_is_sane(chr->descriptors + d)) {
2046 BLE_HS_DBG_ASSERT(0);
2047 return BLE_HS_EINVAL;
2048 }
2049
2050 /* Each descriptor requires:
2051 * o 1 descriptor
2052 * o 1 attribute
2053 */
2054 res->dscs++;
2055 res->attrs++;
2056 }
2057 }
2058 }
2059 }
2060 }
2061
2062 return 0;
2063 }
2064 int
ble_gatts_count_cfg(const struct ble_gatt_svc_def * defs)2065 ble_gatts_count_cfg(const struct ble_gatt_svc_def *defs)
2066 {
2067 struct ble_gatt_resources res = { 0 };
2068 int rc;
2069
2070 rc = ble_gatts_count_resources(defs, &res);
2071 if (rc != 0) {
2072 return rc;
2073 }
2074
2075 ble_hs_max_services += res.svcs;
2076 ble_hs_max_attrs += res.attrs;
2077
2078 /* Reserve an extra CCCD for the cache. */
2079 ble_hs_max_client_configs +=
2080 res.cccds * (MYNEWT_VAL(BLE_MAX_CONNECTIONS) + 1);
2081
2082 return 0;
2083 }
2084
2085 void
ble_gatts_lcl_svc_foreach(ble_gatt_svc_foreach_fn cb)2086 ble_gatts_lcl_svc_foreach(ble_gatt_svc_foreach_fn cb)
2087 {
2088 int i;
2089
2090 for (i = 0; i < ble_gatts_num_svc_entries; i++) {
2091 cb(ble_gatts_svc_entries[i].svc,
2092 ble_gatts_svc_entries[i].handle,
2093 ble_gatts_svc_entries[i].end_group_handle);
2094 }
2095 }
2096
2097 int
ble_gatts_reset(void)2098 ble_gatts_reset(void)
2099 {
2100 int rc;
2101
2102 ble_hs_lock();
2103
2104 if (!ble_gatts_mutable()) {
2105 rc = BLE_HS_EBUSY;
2106 } else {
2107 /* Unregister all ATT attributes. */
2108 ble_att_svr_reset();
2109 ble_gatts_num_cfgable_chrs = 0;
2110 rc = 0;
2111
2112 /* Note: gatts memory gets freed on next call to ble_gatts_start(). */
2113 }
2114
2115 ble_hs_unlock();
2116
2117 return rc;
2118 }
2119
2120 int
ble_gatts_init(void)2121 ble_gatts_init(void)
2122 {
2123 int rc;
2124
2125 ble_gatts_num_cfgable_chrs = 0;
2126 ble_gatts_clt_cfgs = NULL;
2127
2128 rc = stats_init_and_reg(
2129 STATS_HDR(ble_gatts_stats), STATS_SIZE_INIT_PARMS(ble_gatts_stats,
2130 STATS_SIZE_32), STATS_NAME_INIT_PARMS(ble_gatts_stats), "ble_gatts");
2131 if (rc != 0) {
2132 return BLE_HS_EOS;
2133 }
2134
2135 return 0;
2136
2137 }
2138