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