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 <assert.h>
21*042d53a7SEvalZero #include <string.h>
22*042d53a7SEvalZero #include "host/ble_hs.h"
23*042d53a7SEvalZero #include "blecent.h"
24*042d53a7SEvalZero
25*042d53a7SEvalZero static void *peer_svc_mem;
26*042d53a7SEvalZero static struct os_mempool peer_svc_pool;
27*042d53a7SEvalZero
28*042d53a7SEvalZero static void *peer_chr_mem;
29*042d53a7SEvalZero static struct os_mempool peer_chr_pool;
30*042d53a7SEvalZero
31*042d53a7SEvalZero static void *peer_dsc_mem;
32*042d53a7SEvalZero static struct os_mempool peer_dsc_pool;
33*042d53a7SEvalZero
34*042d53a7SEvalZero static void *peer_mem;
35*042d53a7SEvalZero static struct os_mempool peer_pool;
36*042d53a7SEvalZero static SLIST_HEAD(, peer) peers;
37*042d53a7SEvalZero
38*042d53a7SEvalZero static struct peer_svc *
39*042d53a7SEvalZero peer_svc_find_range(struct peer *peer, uint16_t attr_handle);
40*042d53a7SEvalZero static struct peer_svc *
41*042d53a7SEvalZero peer_svc_find(struct peer *peer, uint16_t svc_start_handle,
42*042d53a7SEvalZero struct peer_svc **out_prev);
43*042d53a7SEvalZero int
44*042d53a7SEvalZero peer_svc_is_empty(const struct peer_svc *svc);
45*042d53a7SEvalZero
46*042d53a7SEvalZero uint16_t
47*042d53a7SEvalZero chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr);
48*042d53a7SEvalZero int
49*042d53a7SEvalZero chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr);
50*042d53a7SEvalZero static struct peer_chr *
51*042d53a7SEvalZero peer_chr_find(const struct peer_svc *svc, uint16_t chr_def_handle,
52*042d53a7SEvalZero struct peer_chr **out_prev);
53*042d53a7SEvalZero static void
54*042d53a7SEvalZero peer_disc_chrs(struct peer *peer);
55*042d53a7SEvalZero
56*042d53a7SEvalZero static int
57*042d53a7SEvalZero peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
58*042d53a7SEvalZero uint16_t chr_def_handle, const struct ble_gatt_dsc *dsc,
59*042d53a7SEvalZero void *arg);
60*042d53a7SEvalZero
61*042d53a7SEvalZero static struct peer *
peer_find(uint16_t conn_handle)62*042d53a7SEvalZero peer_find(uint16_t conn_handle)
63*042d53a7SEvalZero {
64*042d53a7SEvalZero struct peer *peer;
65*042d53a7SEvalZero
66*042d53a7SEvalZero SLIST_FOREACH(peer, &peers, next) {
67*042d53a7SEvalZero if (peer->conn_handle == conn_handle) {
68*042d53a7SEvalZero return peer;
69*042d53a7SEvalZero }
70*042d53a7SEvalZero }
71*042d53a7SEvalZero
72*042d53a7SEvalZero return NULL;
73*042d53a7SEvalZero }
74*042d53a7SEvalZero
75*042d53a7SEvalZero static void
peer_disc_complete(struct peer * peer,int rc)76*042d53a7SEvalZero peer_disc_complete(struct peer *peer, int rc)
77*042d53a7SEvalZero {
78*042d53a7SEvalZero peer->disc_prev_chr_val = 0;
79*042d53a7SEvalZero
80*042d53a7SEvalZero /* Notify caller that discovery has completed. */
81*042d53a7SEvalZero if (peer->disc_cb != NULL) {
82*042d53a7SEvalZero peer->disc_cb(peer, rc, peer->disc_cb_arg);
83*042d53a7SEvalZero }
84*042d53a7SEvalZero }
85*042d53a7SEvalZero
86*042d53a7SEvalZero static struct peer_dsc *
peer_dsc_find_prev(const struct peer_chr * chr,uint16_t dsc_handle)87*042d53a7SEvalZero peer_dsc_find_prev(const struct peer_chr *chr, uint16_t dsc_handle)
88*042d53a7SEvalZero {
89*042d53a7SEvalZero struct peer_dsc *prev;
90*042d53a7SEvalZero struct peer_dsc *dsc;
91*042d53a7SEvalZero
92*042d53a7SEvalZero prev = NULL;
93*042d53a7SEvalZero SLIST_FOREACH(dsc, &chr->dscs, next) {
94*042d53a7SEvalZero if (dsc->dsc.handle >= dsc_handle) {
95*042d53a7SEvalZero break;
96*042d53a7SEvalZero }
97*042d53a7SEvalZero
98*042d53a7SEvalZero prev = dsc;
99*042d53a7SEvalZero }
100*042d53a7SEvalZero
101*042d53a7SEvalZero return prev;
102*042d53a7SEvalZero }
103*042d53a7SEvalZero
104*042d53a7SEvalZero static struct peer_dsc *
peer_dsc_find(const struct peer_chr * chr,uint16_t dsc_handle,struct peer_dsc ** out_prev)105*042d53a7SEvalZero peer_dsc_find(const struct peer_chr *chr, uint16_t dsc_handle,
106*042d53a7SEvalZero struct peer_dsc **out_prev)
107*042d53a7SEvalZero {
108*042d53a7SEvalZero struct peer_dsc *prev;
109*042d53a7SEvalZero struct peer_dsc *dsc;
110*042d53a7SEvalZero
111*042d53a7SEvalZero prev = peer_dsc_find_prev(chr, dsc_handle);
112*042d53a7SEvalZero if (prev == NULL) {
113*042d53a7SEvalZero dsc = SLIST_FIRST(&chr->dscs);
114*042d53a7SEvalZero } else {
115*042d53a7SEvalZero dsc = SLIST_NEXT(prev, next);
116*042d53a7SEvalZero }
117*042d53a7SEvalZero
118*042d53a7SEvalZero if (dsc != NULL && dsc->dsc.handle != dsc_handle) {
119*042d53a7SEvalZero dsc = NULL;
120*042d53a7SEvalZero }
121*042d53a7SEvalZero
122*042d53a7SEvalZero if (out_prev != NULL) {
123*042d53a7SEvalZero *out_prev = prev;
124*042d53a7SEvalZero }
125*042d53a7SEvalZero return dsc;
126*042d53a7SEvalZero }
127*042d53a7SEvalZero
128*042d53a7SEvalZero static int
peer_dsc_add(struct peer * peer,uint16_t chr_val_handle,const struct ble_gatt_dsc * gatt_dsc)129*042d53a7SEvalZero peer_dsc_add(struct peer *peer, uint16_t chr_val_handle,
130*042d53a7SEvalZero const struct ble_gatt_dsc *gatt_dsc)
131*042d53a7SEvalZero {
132*042d53a7SEvalZero struct peer_dsc *prev;
133*042d53a7SEvalZero struct peer_dsc *dsc;
134*042d53a7SEvalZero struct peer_svc *svc;
135*042d53a7SEvalZero struct peer_chr *chr;
136*042d53a7SEvalZero
137*042d53a7SEvalZero svc = peer_svc_find_range(peer, chr_val_handle);
138*042d53a7SEvalZero if (svc == NULL) {
139*042d53a7SEvalZero /* Can't find service for discovered descriptor; this shouldn't
140*042d53a7SEvalZero * happen.
141*042d53a7SEvalZero */
142*042d53a7SEvalZero assert(0);
143*042d53a7SEvalZero return BLE_HS_EUNKNOWN;
144*042d53a7SEvalZero }
145*042d53a7SEvalZero
146*042d53a7SEvalZero chr = peer_chr_find(svc, chr_val_handle, NULL);
147*042d53a7SEvalZero if (chr == NULL) {
148*042d53a7SEvalZero /* Can't find characteristic for discovered descriptor; this shouldn't
149*042d53a7SEvalZero * happen.
150*042d53a7SEvalZero */
151*042d53a7SEvalZero assert(0);
152*042d53a7SEvalZero return BLE_HS_EUNKNOWN;
153*042d53a7SEvalZero }
154*042d53a7SEvalZero
155*042d53a7SEvalZero dsc = peer_dsc_find(chr, gatt_dsc->handle, &prev);
156*042d53a7SEvalZero if (dsc != NULL) {
157*042d53a7SEvalZero /* Descriptor already discovered. */
158*042d53a7SEvalZero return 0;
159*042d53a7SEvalZero }
160*042d53a7SEvalZero
161*042d53a7SEvalZero dsc = os_memblock_get(&peer_dsc_pool);
162*042d53a7SEvalZero if (dsc == NULL) {
163*042d53a7SEvalZero /* Out of memory. */
164*042d53a7SEvalZero return BLE_HS_ENOMEM;
165*042d53a7SEvalZero }
166*042d53a7SEvalZero memset(dsc, 0, sizeof *dsc);
167*042d53a7SEvalZero
168*042d53a7SEvalZero dsc->dsc = *gatt_dsc;
169*042d53a7SEvalZero
170*042d53a7SEvalZero if (prev == NULL) {
171*042d53a7SEvalZero SLIST_INSERT_HEAD(&chr->dscs, dsc, next);
172*042d53a7SEvalZero } else {
173*042d53a7SEvalZero SLIST_NEXT(prev, next) = dsc;
174*042d53a7SEvalZero }
175*042d53a7SEvalZero
176*042d53a7SEvalZero return 0;
177*042d53a7SEvalZero }
178*042d53a7SEvalZero
179*042d53a7SEvalZero static void
peer_disc_dscs(struct peer * peer)180*042d53a7SEvalZero peer_disc_dscs(struct peer *peer)
181*042d53a7SEvalZero {
182*042d53a7SEvalZero struct peer_chr *chr;
183*042d53a7SEvalZero struct peer_svc *svc;
184*042d53a7SEvalZero int rc;
185*042d53a7SEvalZero
186*042d53a7SEvalZero /* Search through the list of discovered characteristics for the first
187*042d53a7SEvalZero * characteristic that contains undiscovered descriptors. Then, discover
188*042d53a7SEvalZero * all descriptors belonging to that characteristic.
189*042d53a7SEvalZero */
190*042d53a7SEvalZero SLIST_FOREACH(svc, &peer->svcs, next) {
191*042d53a7SEvalZero SLIST_FOREACH(chr, &svc->chrs, next) {
192*042d53a7SEvalZero if (!chr_is_empty(svc, chr) &&
193*042d53a7SEvalZero SLIST_EMPTY(&chr->dscs) &&
194*042d53a7SEvalZero peer->disc_prev_chr_val <= chr->chr.def_handle) {
195*042d53a7SEvalZero
196*042d53a7SEvalZero rc = ble_gattc_disc_all_dscs(peer->conn_handle,
197*042d53a7SEvalZero chr->chr.val_handle,
198*042d53a7SEvalZero chr_end_handle(svc, chr),
199*042d53a7SEvalZero peer_dsc_disced, peer);
200*042d53a7SEvalZero if (rc != 0) {
201*042d53a7SEvalZero peer_disc_complete(peer, rc);
202*042d53a7SEvalZero }
203*042d53a7SEvalZero
204*042d53a7SEvalZero peer->disc_prev_chr_val = chr->chr.val_handle;
205*042d53a7SEvalZero return;
206*042d53a7SEvalZero }
207*042d53a7SEvalZero }
208*042d53a7SEvalZero }
209*042d53a7SEvalZero
210*042d53a7SEvalZero /* All descriptors discovered. */
211*042d53a7SEvalZero peer_disc_complete(peer, 0);
212*042d53a7SEvalZero }
213*042d53a7SEvalZero
214*042d53a7SEvalZero static int
peer_dsc_disced(uint16_t conn_handle,const struct ble_gatt_error * error,uint16_t chr_val_handle,const struct ble_gatt_dsc * dsc,void * arg)215*042d53a7SEvalZero peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
216*042d53a7SEvalZero uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
217*042d53a7SEvalZero void *arg)
218*042d53a7SEvalZero {
219*042d53a7SEvalZero struct peer *peer;
220*042d53a7SEvalZero int rc;
221*042d53a7SEvalZero
222*042d53a7SEvalZero peer = arg;
223*042d53a7SEvalZero assert(peer->conn_handle == conn_handle);
224*042d53a7SEvalZero
225*042d53a7SEvalZero switch (error->status) {
226*042d53a7SEvalZero case 0:
227*042d53a7SEvalZero rc = peer_dsc_add(peer, chr_val_handle, dsc);
228*042d53a7SEvalZero break;
229*042d53a7SEvalZero
230*042d53a7SEvalZero case BLE_HS_EDONE:
231*042d53a7SEvalZero /* All descriptors in this characteristic discovered; start discovering
232*042d53a7SEvalZero * descriptors in the next characteristic.
233*042d53a7SEvalZero */
234*042d53a7SEvalZero if (peer->disc_prev_chr_val > 0) {
235*042d53a7SEvalZero peer_disc_dscs(peer);
236*042d53a7SEvalZero }
237*042d53a7SEvalZero rc = 0;
238*042d53a7SEvalZero break;
239*042d53a7SEvalZero
240*042d53a7SEvalZero default:
241*042d53a7SEvalZero /* Error; abort discovery. */
242*042d53a7SEvalZero rc = error->status;
243*042d53a7SEvalZero break;
244*042d53a7SEvalZero }
245*042d53a7SEvalZero
246*042d53a7SEvalZero if (rc != 0) {
247*042d53a7SEvalZero /* Error; abort discovery. */
248*042d53a7SEvalZero peer_disc_complete(peer, rc);
249*042d53a7SEvalZero }
250*042d53a7SEvalZero
251*042d53a7SEvalZero return rc;
252*042d53a7SEvalZero }
253*042d53a7SEvalZero
254*042d53a7SEvalZero uint16_t
chr_end_handle(const struct peer_svc * svc,const struct peer_chr * chr)255*042d53a7SEvalZero chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr)
256*042d53a7SEvalZero {
257*042d53a7SEvalZero const struct peer_chr *next_chr;
258*042d53a7SEvalZero
259*042d53a7SEvalZero next_chr = SLIST_NEXT(chr, next);
260*042d53a7SEvalZero if (next_chr != NULL) {
261*042d53a7SEvalZero return next_chr->chr.def_handle - 1;
262*042d53a7SEvalZero } else {
263*042d53a7SEvalZero return svc->svc.end_handle;
264*042d53a7SEvalZero }
265*042d53a7SEvalZero }
266*042d53a7SEvalZero
267*042d53a7SEvalZero int
chr_is_empty(const struct peer_svc * svc,const struct peer_chr * chr)268*042d53a7SEvalZero chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr)
269*042d53a7SEvalZero {
270*042d53a7SEvalZero return chr_end_handle(svc, chr) <= chr->chr.val_handle;
271*042d53a7SEvalZero }
272*042d53a7SEvalZero
273*042d53a7SEvalZero static struct peer_chr *
peer_chr_find_prev(const struct peer_svc * svc,uint16_t chr_val_handle)274*042d53a7SEvalZero peer_chr_find_prev(const struct peer_svc *svc, uint16_t chr_val_handle)
275*042d53a7SEvalZero {
276*042d53a7SEvalZero struct peer_chr *prev;
277*042d53a7SEvalZero struct peer_chr *chr;
278*042d53a7SEvalZero
279*042d53a7SEvalZero prev = NULL;
280*042d53a7SEvalZero SLIST_FOREACH(chr, &svc->chrs, next) {
281*042d53a7SEvalZero if (chr->chr.val_handle >= chr_val_handle) {
282*042d53a7SEvalZero break;
283*042d53a7SEvalZero }
284*042d53a7SEvalZero
285*042d53a7SEvalZero prev = chr;
286*042d53a7SEvalZero }
287*042d53a7SEvalZero
288*042d53a7SEvalZero return prev;
289*042d53a7SEvalZero }
290*042d53a7SEvalZero
291*042d53a7SEvalZero static struct peer_chr *
peer_chr_find(const struct peer_svc * svc,uint16_t chr_val_handle,struct peer_chr ** out_prev)292*042d53a7SEvalZero peer_chr_find(const struct peer_svc *svc, uint16_t chr_val_handle,
293*042d53a7SEvalZero struct peer_chr **out_prev)
294*042d53a7SEvalZero {
295*042d53a7SEvalZero struct peer_chr *prev;
296*042d53a7SEvalZero struct peer_chr *chr;
297*042d53a7SEvalZero
298*042d53a7SEvalZero prev = peer_chr_find_prev(svc, chr_val_handle);
299*042d53a7SEvalZero if (prev == NULL) {
300*042d53a7SEvalZero chr = SLIST_FIRST(&svc->chrs);
301*042d53a7SEvalZero } else {
302*042d53a7SEvalZero chr = SLIST_NEXT(prev, next);
303*042d53a7SEvalZero }
304*042d53a7SEvalZero
305*042d53a7SEvalZero if (chr != NULL && chr->chr.val_handle != chr_val_handle) {
306*042d53a7SEvalZero chr = NULL;
307*042d53a7SEvalZero }
308*042d53a7SEvalZero
309*042d53a7SEvalZero if (out_prev != NULL) {
310*042d53a7SEvalZero *out_prev = prev;
311*042d53a7SEvalZero }
312*042d53a7SEvalZero return chr;
313*042d53a7SEvalZero }
314*042d53a7SEvalZero
315*042d53a7SEvalZero static void
peer_chr_delete(struct peer_chr * chr)316*042d53a7SEvalZero peer_chr_delete(struct peer_chr *chr)
317*042d53a7SEvalZero {
318*042d53a7SEvalZero struct peer_dsc *dsc;
319*042d53a7SEvalZero
320*042d53a7SEvalZero while ((dsc = SLIST_FIRST(&chr->dscs)) != NULL) {
321*042d53a7SEvalZero SLIST_REMOVE_HEAD(&chr->dscs, next);
322*042d53a7SEvalZero os_memblock_put(&peer_dsc_pool, dsc);
323*042d53a7SEvalZero }
324*042d53a7SEvalZero
325*042d53a7SEvalZero os_memblock_put(&peer_chr_pool, chr);
326*042d53a7SEvalZero }
327*042d53a7SEvalZero
328*042d53a7SEvalZero static int
peer_chr_add(struct peer * peer,uint16_t svc_start_handle,const struct ble_gatt_chr * gatt_chr)329*042d53a7SEvalZero peer_chr_add(struct peer *peer, uint16_t svc_start_handle,
330*042d53a7SEvalZero const struct ble_gatt_chr *gatt_chr)
331*042d53a7SEvalZero {
332*042d53a7SEvalZero struct peer_chr *prev;
333*042d53a7SEvalZero struct peer_chr *chr;
334*042d53a7SEvalZero struct peer_svc *svc;
335*042d53a7SEvalZero
336*042d53a7SEvalZero svc = peer_svc_find(peer, svc_start_handle, NULL);
337*042d53a7SEvalZero if (svc == NULL) {
338*042d53a7SEvalZero /* Can't find service for discovered characteristic; this shouldn't
339*042d53a7SEvalZero * happen.
340*042d53a7SEvalZero */
341*042d53a7SEvalZero assert(0);
342*042d53a7SEvalZero return BLE_HS_EUNKNOWN;
343*042d53a7SEvalZero }
344*042d53a7SEvalZero
345*042d53a7SEvalZero chr = peer_chr_find(svc, gatt_chr->def_handle, &prev);
346*042d53a7SEvalZero if (chr != NULL) {
347*042d53a7SEvalZero /* Characteristic already discovered. */
348*042d53a7SEvalZero return 0;
349*042d53a7SEvalZero }
350*042d53a7SEvalZero
351*042d53a7SEvalZero chr = os_memblock_get(&peer_chr_pool);
352*042d53a7SEvalZero if (chr == NULL) {
353*042d53a7SEvalZero /* Out of memory. */
354*042d53a7SEvalZero return BLE_HS_ENOMEM;
355*042d53a7SEvalZero }
356*042d53a7SEvalZero memset(chr, 0, sizeof *chr);
357*042d53a7SEvalZero
358*042d53a7SEvalZero chr->chr = *gatt_chr;
359*042d53a7SEvalZero
360*042d53a7SEvalZero if (prev == NULL) {
361*042d53a7SEvalZero SLIST_INSERT_HEAD(&svc->chrs, chr, next);
362*042d53a7SEvalZero } else {
363*042d53a7SEvalZero SLIST_NEXT(prev, next) = chr;
364*042d53a7SEvalZero }
365*042d53a7SEvalZero
366*042d53a7SEvalZero return 0;
367*042d53a7SEvalZero }
368*042d53a7SEvalZero
369*042d53a7SEvalZero static int
peer_chr_disced(uint16_t conn_handle,const struct ble_gatt_error * error,const struct ble_gatt_chr * chr,void * arg)370*042d53a7SEvalZero peer_chr_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
371*042d53a7SEvalZero const struct ble_gatt_chr *chr, void *arg)
372*042d53a7SEvalZero {
373*042d53a7SEvalZero struct peer *peer;
374*042d53a7SEvalZero int rc;
375*042d53a7SEvalZero
376*042d53a7SEvalZero peer = arg;
377*042d53a7SEvalZero assert(peer->conn_handle == conn_handle);
378*042d53a7SEvalZero
379*042d53a7SEvalZero switch (error->status) {
380*042d53a7SEvalZero case 0:
381*042d53a7SEvalZero rc = peer_chr_add(peer, peer->cur_svc->svc.start_handle, chr);
382*042d53a7SEvalZero break;
383*042d53a7SEvalZero
384*042d53a7SEvalZero case BLE_HS_EDONE:
385*042d53a7SEvalZero /* All characteristics in this service discovered; start discovering
386*042d53a7SEvalZero * characteristics in the next service.
387*042d53a7SEvalZero */
388*042d53a7SEvalZero if (peer->disc_prev_chr_val > 0) {
389*042d53a7SEvalZero peer_disc_chrs(peer);
390*042d53a7SEvalZero }
391*042d53a7SEvalZero rc = 0;
392*042d53a7SEvalZero break;
393*042d53a7SEvalZero
394*042d53a7SEvalZero default:
395*042d53a7SEvalZero rc = error->status;
396*042d53a7SEvalZero break;
397*042d53a7SEvalZero }
398*042d53a7SEvalZero
399*042d53a7SEvalZero if (rc != 0) {
400*042d53a7SEvalZero /* Error; abort discovery. */
401*042d53a7SEvalZero peer_disc_complete(peer, rc);
402*042d53a7SEvalZero }
403*042d53a7SEvalZero
404*042d53a7SEvalZero return rc;
405*042d53a7SEvalZero }
406*042d53a7SEvalZero
407*042d53a7SEvalZero static void
peer_disc_chrs(struct peer * peer)408*042d53a7SEvalZero peer_disc_chrs(struct peer *peer)
409*042d53a7SEvalZero {
410*042d53a7SEvalZero struct peer_svc *svc;
411*042d53a7SEvalZero int rc;
412*042d53a7SEvalZero
413*042d53a7SEvalZero /* Search through the list of discovered service for the first service that
414*042d53a7SEvalZero * contains undiscovered characteristics. Then, discover all
415*042d53a7SEvalZero * characteristics belonging to that service.
416*042d53a7SEvalZero */
417*042d53a7SEvalZero SLIST_FOREACH(svc, &peer->svcs, next) {
418*042d53a7SEvalZero if (!peer_svc_is_empty(svc) && SLIST_EMPTY(&svc->chrs)) {
419*042d53a7SEvalZero peer->cur_svc = svc;
420*042d53a7SEvalZero rc = ble_gattc_disc_all_chrs(peer->conn_handle,
421*042d53a7SEvalZero svc->svc.start_handle,
422*042d53a7SEvalZero svc->svc.end_handle,
423*042d53a7SEvalZero peer_chr_disced, peer);
424*042d53a7SEvalZero if (rc != 0) {
425*042d53a7SEvalZero peer_disc_complete(peer, rc);
426*042d53a7SEvalZero }
427*042d53a7SEvalZero return;
428*042d53a7SEvalZero }
429*042d53a7SEvalZero }
430*042d53a7SEvalZero
431*042d53a7SEvalZero /* All characteristics discovered. */
432*042d53a7SEvalZero peer_disc_dscs(peer);
433*042d53a7SEvalZero }
434*042d53a7SEvalZero
435*042d53a7SEvalZero int
peer_svc_is_empty(const struct peer_svc * svc)436*042d53a7SEvalZero peer_svc_is_empty(const struct peer_svc *svc)
437*042d53a7SEvalZero {
438*042d53a7SEvalZero return svc->svc.end_handle <= svc->svc.start_handle;
439*042d53a7SEvalZero }
440*042d53a7SEvalZero
441*042d53a7SEvalZero static struct peer_svc *
peer_svc_find_prev(struct peer * peer,uint16_t svc_start_handle)442*042d53a7SEvalZero peer_svc_find_prev(struct peer *peer, uint16_t svc_start_handle)
443*042d53a7SEvalZero {
444*042d53a7SEvalZero struct peer_svc *prev;
445*042d53a7SEvalZero struct peer_svc *svc;
446*042d53a7SEvalZero
447*042d53a7SEvalZero prev = NULL;
448*042d53a7SEvalZero SLIST_FOREACH(svc, &peer->svcs, next) {
449*042d53a7SEvalZero if (svc->svc.start_handle >= svc_start_handle) {
450*042d53a7SEvalZero break;
451*042d53a7SEvalZero }
452*042d53a7SEvalZero
453*042d53a7SEvalZero prev = svc;
454*042d53a7SEvalZero }
455*042d53a7SEvalZero
456*042d53a7SEvalZero return prev;
457*042d53a7SEvalZero }
458*042d53a7SEvalZero
459*042d53a7SEvalZero static struct peer_svc *
peer_svc_find(struct peer * peer,uint16_t svc_start_handle,struct peer_svc ** out_prev)460*042d53a7SEvalZero peer_svc_find(struct peer *peer, uint16_t svc_start_handle,
461*042d53a7SEvalZero struct peer_svc **out_prev)
462*042d53a7SEvalZero {
463*042d53a7SEvalZero struct peer_svc *prev;
464*042d53a7SEvalZero struct peer_svc *svc;
465*042d53a7SEvalZero
466*042d53a7SEvalZero prev = peer_svc_find_prev(peer, svc_start_handle);
467*042d53a7SEvalZero if (prev == NULL) {
468*042d53a7SEvalZero svc = SLIST_FIRST(&peer->svcs);
469*042d53a7SEvalZero } else {
470*042d53a7SEvalZero svc = SLIST_NEXT(prev, next);
471*042d53a7SEvalZero }
472*042d53a7SEvalZero
473*042d53a7SEvalZero if (svc != NULL && svc->svc.start_handle != svc_start_handle) {
474*042d53a7SEvalZero svc = NULL;
475*042d53a7SEvalZero }
476*042d53a7SEvalZero
477*042d53a7SEvalZero if (out_prev != NULL) {
478*042d53a7SEvalZero *out_prev = prev;
479*042d53a7SEvalZero }
480*042d53a7SEvalZero return svc;
481*042d53a7SEvalZero }
482*042d53a7SEvalZero
483*042d53a7SEvalZero static struct peer_svc *
peer_svc_find_range(struct peer * peer,uint16_t attr_handle)484*042d53a7SEvalZero peer_svc_find_range(struct peer *peer, uint16_t attr_handle)
485*042d53a7SEvalZero {
486*042d53a7SEvalZero struct peer_svc *svc;
487*042d53a7SEvalZero
488*042d53a7SEvalZero SLIST_FOREACH(svc, &peer->svcs, next) {
489*042d53a7SEvalZero if (svc->svc.start_handle <= attr_handle &&
490*042d53a7SEvalZero svc->svc.end_handle >= attr_handle) {
491*042d53a7SEvalZero
492*042d53a7SEvalZero return svc;
493*042d53a7SEvalZero }
494*042d53a7SEvalZero }
495*042d53a7SEvalZero
496*042d53a7SEvalZero return NULL;
497*042d53a7SEvalZero }
498*042d53a7SEvalZero
499*042d53a7SEvalZero const struct peer_svc *
peer_svc_find_uuid(const struct peer * peer,const ble_uuid_t * uuid)500*042d53a7SEvalZero peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid)
501*042d53a7SEvalZero {
502*042d53a7SEvalZero const struct peer_svc *svc;
503*042d53a7SEvalZero
504*042d53a7SEvalZero SLIST_FOREACH(svc, &peer->svcs, next) {
505*042d53a7SEvalZero if (ble_uuid_cmp(&svc->svc.uuid.u, uuid) == 0) {
506*042d53a7SEvalZero return svc;
507*042d53a7SEvalZero }
508*042d53a7SEvalZero }
509*042d53a7SEvalZero
510*042d53a7SEvalZero return NULL;
511*042d53a7SEvalZero }
512*042d53a7SEvalZero
513*042d53a7SEvalZero const struct peer_chr *
peer_chr_find_uuid(const struct peer * peer,const ble_uuid_t * svc_uuid,const ble_uuid_t * chr_uuid)514*042d53a7SEvalZero peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
515*042d53a7SEvalZero const ble_uuid_t *chr_uuid)
516*042d53a7SEvalZero {
517*042d53a7SEvalZero const struct peer_svc *svc;
518*042d53a7SEvalZero const struct peer_chr *chr;
519*042d53a7SEvalZero
520*042d53a7SEvalZero svc = peer_svc_find_uuid(peer, svc_uuid);
521*042d53a7SEvalZero if (svc == NULL) {
522*042d53a7SEvalZero return NULL;
523*042d53a7SEvalZero }
524*042d53a7SEvalZero
525*042d53a7SEvalZero SLIST_FOREACH(chr, &svc->chrs, next) {
526*042d53a7SEvalZero if (ble_uuid_cmp(&chr->chr.uuid.u, chr_uuid) == 0) {
527*042d53a7SEvalZero return chr;
528*042d53a7SEvalZero }
529*042d53a7SEvalZero }
530*042d53a7SEvalZero
531*042d53a7SEvalZero return NULL;
532*042d53a7SEvalZero }
533*042d53a7SEvalZero
534*042d53a7SEvalZero const struct peer_dsc *
peer_dsc_find_uuid(const struct peer * peer,const ble_uuid_t * svc_uuid,const ble_uuid_t * chr_uuid,const ble_uuid_t * dsc_uuid)535*042d53a7SEvalZero peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
536*042d53a7SEvalZero const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid)
537*042d53a7SEvalZero {
538*042d53a7SEvalZero const struct peer_chr *chr;
539*042d53a7SEvalZero const struct peer_dsc *dsc;
540*042d53a7SEvalZero
541*042d53a7SEvalZero chr = peer_chr_find_uuid(peer, svc_uuid, chr_uuid);
542*042d53a7SEvalZero if (chr == NULL) {
543*042d53a7SEvalZero return NULL;
544*042d53a7SEvalZero }
545*042d53a7SEvalZero
546*042d53a7SEvalZero SLIST_FOREACH(dsc, &chr->dscs, next) {
547*042d53a7SEvalZero if (ble_uuid_cmp(&dsc->dsc.uuid.u, dsc_uuid) == 0) {
548*042d53a7SEvalZero return dsc;
549*042d53a7SEvalZero }
550*042d53a7SEvalZero }
551*042d53a7SEvalZero
552*042d53a7SEvalZero return NULL;
553*042d53a7SEvalZero }
554*042d53a7SEvalZero
555*042d53a7SEvalZero static int
peer_svc_add(struct peer * peer,const struct ble_gatt_svc * gatt_svc)556*042d53a7SEvalZero peer_svc_add(struct peer *peer, const struct ble_gatt_svc *gatt_svc)
557*042d53a7SEvalZero {
558*042d53a7SEvalZero struct peer_svc *prev;
559*042d53a7SEvalZero struct peer_svc *svc;
560*042d53a7SEvalZero
561*042d53a7SEvalZero svc = peer_svc_find(peer, gatt_svc->start_handle, &prev);
562*042d53a7SEvalZero if (svc != NULL) {
563*042d53a7SEvalZero /* Service already discovered. */
564*042d53a7SEvalZero return 0;
565*042d53a7SEvalZero }
566*042d53a7SEvalZero
567*042d53a7SEvalZero svc = os_memblock_get(&peer_svc_pool);
568*042d53a7SEvalZero if (svc == NULL) {
569*042d53a7SEvalZero /* Out of memory. */
570*042d53a7SEvalZero return BLE_HS_ENOMEM;
571*042d53a7SEvalZero }
572*042d53a7SEvalZero memset(svc, 0, sizeof *svc);
573*042d53a7SEvalZero
574*042d53a7SEvalZero svc->svc = *gatt_svc;
575*042d53a7SEvalZero SLIST_INIT(&svc->chrs);
576*042d53a7SEvalZero
577*042d53a7SEvalZero if (prev == NULL) {
578*042d53a7SEvalZero SLIST_INSERT_HEAD(&peer->svcs, svc, next);
579*042d53a7SEvalZero } else {
580*042d53a7SEvalZero SLIST_INSERT_AFTER(prev, svc, next);
581*042d53a7SEvalZero }
582*042d53a7SEvalZero
583*042d53a7SEvalZero return 0;
584*042d53a7SEvalZero }
585*042d53a7SEvalZero
586*042d53a7SEvalZero static void
peer_svc_delete(struct peer_svc * svc)587*042d53a7SEvalZero peer_svc_delete(struct peer_svc *svc)
588*042d53a7SEvalZero {
589*042d53a7SEvalZero struct peer_chr *chr;
590*042d53a7SEvalZero
591*042d53a7SEvalZero while ((chr = SLIST_FIRST(&svc->chrs)) != NULL) {
592*042d53a7SEvalZero SLIST_REMOVE_HEAD(&svc->chrs, next);
593*042d53a7SEvalZero peer_chr_delete(chr);
594*042d53a7SEvalZero }
595*042d53a7SEvalZero
596*042d53a7SEvalZero os_memblock_put(&peer_svc_pool, svc);
597*042d53a7SEvalZero }
598*042d53a7SEvalZero
599*042d53a7SEvalZero static int
peer_svc_disced(uint16_t conn_handle,const struct ble_gatt_error * error,const struct ble_gatt_svc * service,void * arg)600*042d53a7SEvalZero peer_svc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
601*042d53a7SEvalZero const struct ble_gatt_svc *service, void *arg)
602*042d53a7SEvalZero {
603*042d53a7SEvalZero struct peer *peer;
604*042d53a7SEvalZero int rc;
605*042d53a7SEvalZero
606*042d53a7SEvalZero peer = arg;
607*042d53a7SEvalZero assert(peer->conn_handle == conn_handle);
608*042d53a7SEvalZero
609*042d53a7SEvalZero switch (error->status) {
610*042d53a7SEvalZero case 0:
611*042d53a7SEvalZero rc = peer_svc_add(peer, service);
612*042d53a7SEvalZero break;
613*042d53a7SEvalZero
614*042d53a7SEvalZero case BLE_HS_EDONE:
615*042d53a7SEvalZero /* All services discovered; start discovering characteristics. */
616*042d53a7SEvalZero if (peer->disc_prev_chr_val > 0) {
617*042d53a7SEvalZero peer_disc_chrs(peer);
618*042d53a7SEvalZero }
619*042d53a7SEvalZero rc = 0;
620*042d53a7SEvalZero break;
621*042d53a7SEvalZero
622*042d53a7SEvalZero default:
623*042d53a7SEvalZero rc = error->status;
624*042d53a7SEvalZero break;
625*042d53a7SEvalZero }
626*042d53a7SEvalZero
627*042d53a7SEvalZero if (rc != 0) {
628*042d53a7SEvalZero /* Error; abort discovery. */
629*042d53a7SEvalZero peer_disc_complete(peer, rc);
630*042d53a7SEvalZero }
631*042d53a7SEvalZero
632*042d53a7SEvalZero return rc;
633*042d53a7SEvalZero }
634*042d53a7SEvalZero
635*042d53a7SEvalZero
636*042d53a7SEvalZero int
peer_disc_all(uint16_t conn_handle,peer_disc_fn * disc_cb,void * disc_cb_arg)637*042d53a7SEvalZero peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb, void *disc_cb_arg)
638*042d53a7SEvalZero {
639*042d53a7SEvalZero struct peer_svc *svc;
640*042d53a7SEvalZero struct peer *peer;
641*042d53a7SEvalZero int rc;
642*042d53a7SEvalZero
643*042d53a7SEvalZero peer = peer_find(conn_handle);
644*042d53a7SEvalZero if (peer == NULL) {
645*042d53a7SEvalZero return BLE_HS_ENOTCONN;
646*042d53a7SEvalZero }
647*042d53a7SEvalZero
648*042d53a7SEvalZero /* Undiscover everything first. */
649*042d53a7SEvalZero while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) {
650*042d53a7SEvalZero SLIST_REMOVE_HEAD(&peer->svcs, next);
651*042d53a7SEvalZero peer_svc_delete(svc);
652*042d53a7SEvalZero }
653*042d53a7SEvalZero
654*042d53a7SEvalZero peer->disc_prev_chr_val = 1;
655*042d53a7SEvalZero peer->disc_cb = disc_cb;
656*042d53a7SEvalZero peer->disc_cb_arg = disc_cb_arg;
657*042d53a7SEvalZero
658*042d53a7SEvalZero rc = ble_gattc_disc_all_svcs(conn_handle, peer_svc_disced, peer);
659*042d53a7SEvalZero if (rc != 0) {
660*042d53a7SEvalZero return rc;
661*042d53a7SEvalZero }
662*042d53a7SEvalZero
663*042d53a7SEvalZero return 0;
664*042d53a7SEvalZero }
665*042d53a7SEvalZero
666*042d53a7SEvalZero int
peer_delete(uint16_t conn_handle)667*042d53a7SEvalZero peer_delete(uint16_t conn_handle)
668*042d53a7SEvalZero {
669*042d53a7SEvalZero struct peer_svc *svc;
670*042d53a7SEvalZero struct peer *peer;
671*042d53a7SEvalZero int rc;
672*042d53a7SEvalZero
673*042d53a7SEvalZero peer = peer_find(conn_handle);
674*042d53a7SEvalZero if (peer == NULL) {
675*042d53a7SEvalZero return BLE_HS_ENOTCONN;
676*042d53a7SEvalZero }
677*042d53a7SEvalZero
678*042d53a7SEvalZero SLIST_REMOVE(&peers, peer, peer, next);
679*042d53a7SEvalZero
680*042d53a7SEvalZero while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) {
681*042d53a7SEvalZero SLIST_REMOVE_HEAD(&peer->svcs, next);
682*042d53a7SEvalZero peer_svc_delete(svc);
683*042d53a7SEvalZero }
684*042d53a7SEvalZero
685*042d53a7SEvalZero rc = os_memblock_put(&peer_pool, peer);
686*042d53a7SEvalZero if (rc != 0) {
687*042d53a7SEvalZero return BLE_HS_EOS;
688*042d53a7SEvalZero }
689*042d53a7SEvalZero
690*042d53a7SEvalZero return 0;
691*042d53a7SEvalZero }
692*042d53a7SEvalZero
693*042d53a7SEvalZero int
peer_add(uint16_t conn_handle)694*042d53a7SEvalZero peer_add(uint16_t conn_handle)
695*042d53a7SEvalZero {
696*042d53a7SEvalZero struct peer *peer;
697*042d53a7SEvalZero
698*042d53a7SEvalZero /* Make sure the connection handle is unique. */
699*042d53a7SEvalZero peer = peer_find(conn_handle);
700*042d53a7SEvalZero if (peer != NULL) {
701*042d53a7SEvalZero return BLE_HS_EALREADY;
702*042d53a7SEvalZero }
703*042d53a7SEvalZero
704*042d53a7SEvalZero peer = os_memblock_get(&peer_pool);
705*042d53a7SEvalZero if (peer == NULL) {
706*042d53a7SEvalZero /* Out of memory. */
707*042d53a7SEvalZero return BLE_HS_ENOMEM;
708*042d53a7SEvalZero }
709*042d53a7SEvalZero
710*042d53a7SEvalZero memset(peer, 0, sizeof *peer);
711*042d53a7SEvalZero peer->conn_handle = conn_handle;
712*042d53a7SEvalZero
713*042d53a7SEvalZero SLIST_INSERT_HEAD(&peers, peer, next);
714*042d53a7SEvalZero
715*042d53a7SEvalZero return 0;
716*042d53a7SEvalZero }
717*042d53a7SEvalZero
718*042d53a7SEvalZero static void
peer_free_mem(void)719*042d53a7SEvalZero peer_free_mem(void)
720*042d53a7SEvalZero {
721*042d53a7SEvalZero free(peer_mem);
722*042d53a7SEvalZero peer_mem = NULL;
723*042d53a7SEvalZero
724*042d53a7SEvalZero free(peer_svc_mem);
725*042d53a7SEvalZero peer_svc_mem = NULL;
726*042d53a7SEvalZero
727*042d53a7SEvalZero free(peer_chr_mem);
728*042d53a7SEvalZero peer_chr_mem = NULL;
729*042d53a7SEvalZero
730*042d53a7SEvalZero free(peer_dsc_mem);
731*042d53a7SEvalZero peer_dsc_mem = NULL;
732*042d53a7SEvalZero }
733*042d53a7SEvalZero
734*042d53a7SEvalZero int
peer_init(int max_peers,int max_svcs,int max_chrs,int max_dscs)735*042d53a7SEvalZero peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs)
736*042d53a7SEvalZero {
737*042d53a7SEvalZero int rc;
738*042d53a7SEvalZero
739*042d53a7SEvalZero /* Free memory first in case this function gets called more than once. */
740*042d53a7SEvalZero peer_free_mem();
741*042d53a7SEvalZero
742*042d53a7SEvalZero peer_mem = malloc(
743*042d53a7SEvalZero OS_MEMPOOL_BYTES(max_peers, sizeof (struct peer)));
744*042d53a7SEvalZero if (peer_mem == NULL) {
745*042d53a7SEvalZero rc = BLE_HS_ENOMEM;
746*042d53a7SEvalZero goto err;
747*042d53a7SEvalZero }
748*042d53a7SEvalZero
749*042d53a7SEvalZero rc = os_mempool_init(&peer_pool, max_peers,
750*042d53a7SEvalZero sizeof (struct peer), peer_mem,
751*042d53a7SEvalZero "peer_pool");
752*042d53a7SEvalZero if (rc != 0) {
753*042d53a7SEvalZero rc = BLE_HS_EOS;
754*042d53a7SEvalZero goto err;
755*042d53a7SEvalZero }
756*042d53a7SEvalZero
757*042d53a7SEvalZero peer_svc_mem = malloc(
758*042d53a7SEvalZero OS_MEMPOOL_BYTES(max_svcs, sizeof (struct peer_svc)));
759*042d53a7SEvalZero if (peer_svc_mem == NULL) {
760*042d53a7SEvalZero rc = BLE_HS_ENOMEM;
761*042d53a7SEvalZero goto err;
762*042d53a7SEvalZero }
763*042d53a7SEvalZero
764*042d53a7SEvalZero rc = os_mempool_init(&peer_svc_pool, max_svcs,
765*042d53a7SEvalZero sizeof (struct peer_svc), peer_svc_mem,
766*042d53a7SEvalZero "peer_svc_pool");
767*042d53a7SEvalZero if (rc != 0) {
768*042d53a7SEvalZero rc = BLE_HS_EOS;
769*042d53a7SEvalZero goto err;
770*042d53a7SEvalZero }
771*042d53a7SEvalZero
772*042d53a7SEvalZero peer_chr_mem = malloc(
773*042d53a7SEvalZero OS_MEMPOOL_BYTES(max_chrs, sizeof (struct peer_chr)));
774*042d53a7SEvalZero if (peer_chr_mem == NULL) {
775*042d53a7SEvalZero rc = BLE_HS_ENOMEM;
776*042d53a7SEvalZero goto err;
777*042d53a7SEvalZero }
778*042d53a7SEvalZero
779*042d53a7SEvalZero rc = os_mempool_init(&peer_chr_pool, max_chrs,
780*042d53a7SEvalZero sizeof (struct peer_chr), peer_chr_mem,
781*042d53a7SEvalZero "peer_chr_pool");
782*042d53a7SEvalZero if (rc != 0) {
783*042d53a7SEvalZero rc = BLE_HS_EOS;
784*042d53a7SEvalZero goto err;
785*042d53a7SEvalZero }
786*042d53a7SEvalZero
787*042d53a7SEvalZero peer_dsc_mem = malloc(
788*042d53a7SEvalZero OS_MEMPOOL_BYTES(max_dscs, sizeof (struct peer_dsc)));
789*042d53a7SEvalZero if (peer_dsc_mem == NULL) {
790*042d53a7SEvalZero rc = BLE_HS_ENOMEM;
791*042d53a7SEvalZero goto err;
792*042d53a7SEvalZero }
793*042d53a7SEvalZero
794*042d53a7SEvalZero rc = os_mempool_init(&peer_dsc_pool, max_dscs,
795*042d53a7SEvalZero sizeof (struct peer_dsc), peer_dsc_mem,
796*042d53a7SEvalZero "peer_dsc_pool");
797*042d53a7SEvalZero if (rc != 0) {
798*042d53a7SEvalZero rc = BLE_HS_EOS;
799*042d53a7SEvalZero goto err;
800*042d53a7SEvalZero }
801*042d53a7SEvalZero
802*042d53a7SEvalZero return 0;
803*042d53a7SEvalZero
804*042d53a7SEvalZero err:
805*042d53a7SEvalZero peer_free_mem();
806*042d53a7SEvalZero return rc;
807*042d53a7SEvalZero }
808