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(®ister_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(®ister_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(®ister_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