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