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