xref: /nrf52832-nimble/packages/NimBLE-latest/apps/blecent/src/peer.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
1*042d53a7SEvalZero /*
2*042d53a7SEvalZero  * Licensed to the Apache Software Foundation (ASF) under one
3*042d53a7SEvalZero  * or more contributor license agreements.  See the NOTICE file
4*042d53a7SEvalZero  * distributed with this work for additional information
5*042d53a7SEvalZero  * regarding copyright ownership.  The ASF licenses this file
6*042d53a7SEvalZero  * to you under the Apache License, Version 2.0 (the
7*042d53a7SEvalZero  * "License"); you may not use this file except in compliance
8*042d53a7SEvalZero  * with the License.  You may obtain a copy of the License at
9*042d53a7SEvalZero  *
10*042d53a7SEvalZero  *  http://www.apache.org/licenses/LICENSE-2.0
11*042d53a7SEvalZero  *
12*042d53a7SEvalZero  * Unless required by applicable law or agreed to in writing,
13*042d53a7SEvalZero  * software distributed under the License is distributed on an
14*042d53a7SEvalZero  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15*042d53a7SEvalZero  * KIND, either express or implied.  See the License for the
16*042d53a7SEvalZero  * specific language governing permissions and limitations
17*042d53a7SEvalZero  * under the License.
18*042d53a7SEvalZero  */
19*042d53a7SEvalZero 
20*042d53a7SEvalZero #include <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