From 993bd86ee1c56e9584a809f483ed84c7d51e81b6 Mon Sep 17 00:00:00 2001 From: Astha Verma Date: Mon, 14 Apr 2025 11:55:11 +0530 Subject: [PATCH] fix(nimble): Added api's for fetching gatt cache data and for discovering included service --- components/bt/host/nimble/Kconfig.in | 19 ++ components/bt/host/nimble/nimble | 2 +- .../host/nimble/port/include/esp_nimble_cfg.h | 24 ++ examples/bluetooth/nimble/blecent/main/main.c | 9 +- .../common/nimble_central_utils/esp_central.h | 18 +- .../nimble/common/nimble_central_utils/peer.c | 235 ++++++++++++++++++ 6 files changed, 303 insertions(+), 4 deletions(-) diff --git a/components/bt/host/nimble/Kconfig.in b/components/bt/host/nimble/Kconfig.in index c0ea8e9f11..320960f64b 100644 --- a/components/bt/host/nimble/Kconfig.in +++ b/components/bt/host/nimble/Kconfig.in @@ -708,6 +708,19 @@ menuconfig BT_NIMBLE_GATT_CACHING select BT_NIMBLE_DYNAMIC_SERVICE help Enable GATT caching +config BT_NIMBLE_GATT_CACHING_INCLUDE_SERVICES + bool "Include services in GATT caching" + depends on BT_NIMBLE_GATT_CACHING + default n + help + Enable this option to include *included services* (e.g., services referenced by other services) + in the GATT database cache. Disabling this will skip caching of included service entries. +config BT_NIMBLE_INCL_SVC_DISCOVERY + bool "Enable Included service discovery" + default y if BT_NIMBLE_GATT_CACHING_INCLUDE_SERVICES + default n + help + Enable this option to start discovery for included service. config BT_NIMBLE_GATT_CACHING_MAX_CONNS int "Maximum connections to be cached" depends on BT_NIMBLE_GATT_CACHING @@ -720,6 +733,12 @@ config BT_NIMBLE_GATT_CACHING_MAX_SVCS default 64 help Set this option to set the upper limit on number of services per connection to be cached. +config BT_NIMBLE_GATT_CACHING_MAX_INCL_SVCS + int "Maximum number of included services per connection" + depends on BT_NIMBLE_GATT_CACHING + default 64 + help + Set this option to set the upper limit on number of included services per connection to be cached. config BT_NIMBLE_GATT_CACHING_MAX_CHRS int "Maximum number of characteristics per connection" depends on BT_NIMBLE_GATT_CACHING diff --git a/components/bt/host/nimble/nimble b/components/bt/host/nimble/nimble index 7c18dde8d7..92b8a227f0 160000 --- a/components/bt/host/nimble/nimble +++ b/components/bt/host/nimble/nimble @@ -1 +1 @@ -Subproject commit 7c18dde8d7d8edd05087330347a84c5e43bd29e3 +Subproject commit 92b8a227f0401ba37770e21b26ea5bb33baad4e1 diff --git a/components/bt/host/nimble/port/include/esp_nimble_cfg.h b/components/bt/host/nimble/port/include/esp_nimble_cfg.h index 138b2460f2..90025e49a5 100644 --- a/components/bt/host/nimble/port/include/esp_nimble_cfg.h +++ b/components/bt/host/nimble/port/include/esp_nimble_cfg.h @@ -129,11 +129,27 @@ #define MYNEWT_VAL_BLE_MAX_PERIODIC_ADVERTISER_LIST (CONFIG_BT_NIMBLE_MAX_PERIODIC_ADVERTISER_LIST) #endif +#ifndef MYNEWT_VAL_BLE_INCL_SVC_DISCOVERY +#ifdef CONFIG_BT_NIMBLE_INCL_SVC_DISCOVERY +#define MYNEWT_VAL_BLE_INCL_SVC_DISCOVERY (CONFIG_BT_NIMBLE_INCL_SVC_DISCOVERY) +#else +#define MYNEWT_VAL_BLE_INCL_SVC_DISCOVERY (0) +#endif +#endif + #ifndef CONFIG_BT_NIMBLE_GATT_CACHING #define MYNEWT_VAL_BLE_GATT_CACHING (0) #else #define MYNEWT_VAL_BLE_GATT_CACHING (CONFIG_BT_NIMBLE_GATT_CACHING) +#ifndef MYNEWT_VAL_BLE_GATT_CACHING_INCLUDE_SERVICES +#ifdef CONFIG_BT_NIMBLE_GATT_CACHING_INCLUDE_SERVICES +#define MYNEWT_VAL_BLE_GATT_CACHING_INCLUDE_SERVICES (CONFIG_BT_NIMBLE_GATT_CACHING_INCLUDE_SERVICES) +#else +#define MYNEWT_VAL_BLE_GATT_CACHING_INCLUDE_SERVICES (0) +#endif +#endif + #ifdef CONFIG_BT_NIMBLE_GATT_CACHING_MAX_CONNS #define MYNEWT_VAL_BLE_GATT_CACHING_MAX_CONNS (CONFIG_BT_NIMBLE_GATT_CACHING_MAX_CONNS) #else @@ -146,6 +162,14 @@ #define MYNEWT_VAL_BLE_GATT_CACHING_MAX_SVCS (0) #endif +#ifndef MYNEWT_VAL_BLE_GATT_CACHING_MAX_INCL_SVCS +#ifdef CONFIG_BT_NIMBLE_GATT_CACHING_MAX_INCL_SVCS +#define MYNEWT_VAL_BLE_GATT_CACHING_MAX_INCL_SVCS (CONFIG_BT_NIMBLE_GATT_CACHING_MAX_INCL_SVCS) +#else +#define MYNEWT_VAL_BLE_GATT_CACHING_MAX_INCL_SVCS (0) +#endif +#endif + #ifdef CONFIG_BT_NIMBLE_GATT_CACHING_MAX_CHRS #define MYNEWT_VAL_BLE_GATT_CACHING_MAX_CHRS (CONFIG_BT_NIMBLE_GATT_CACHING_MAX_CHRS) #else diff --git a/examples/bluetooth/nimble/blecent/main/main.c b/examples/bluetooth/nimble/blecent/main/main.c index e2c37b1ffe..f050b61908 100644 --- a/examples/bluetooth/nimble/blecent/main/main.c +++ b/examples/bluetooth/nimble/blecent/main/main.c @@ -27,6 +27,9 @@ #include "console/console.h" #include "services/gap/ble_svc_gap.h" #include "blecent.h" +#if MYNEWT_VAL(BLE_GATT_CACHING) +#include "host/ble_esp_gattc_cache.h" +#endif #if CONFIG_EXAMPLE_USE_CI_ADDRESS #ifdef CONFIG_IDF_TARGET_ESP32 @@ -1052,9 +1055,13 @@ app_main(void) ble_hs_cfg.store_status_cb = ble_store_util_status_rr; /* Initialize data structures to track connected peers. */ +#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) + rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64, 64); + assert(rc == 0); +#else rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); assert(rc == 0); - +#endif /* Set the default device name. */ rc = ble_svc_gap_device_name_set("nimble-blecent"); assert(rc == 0); diff --git a/examples/bluetooth/nimble/common/nimble_central_utils/esp_central.h b/examples/bluetooth/nimble/common/nimble_central_utils/esp_central.h index 396d416ea7..45fbb6ab0e 100644 --- a/examples/bluetooth/nimble/common/nimble_central_utils/esp_central.h +++ b/examples/bluetooth/nimble/common/nimble_central_utils/esp_central.h @@ -38,14 +38,24 @@ struct peer_chr { struct peer_dsc_list dscs; }; SLIST_HEAD(peer_chr_list, peer_chr); +SLIST_HEAD(peer_svc_list, peer_svc); + +#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) +struct peer_incl_svc { + SLIST_ENTRY(peer_incl_svc) next; + struct ble_gatt_incl_svc svc; +}; +SLIST_HEAD(peer_incl_svc_list, peer_incl_svc); +#endif struct peer_svc { SLIST_ENTRY(peer_svc) next; struct ble_gatt_svc svc; - +#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) + struct peer_incl_svc_list incl_svc; +#endif struct peer_chr_list chrs; }; -SLIST_HEAD(peer_svc_list, peer_svc); struct peer; typedef void peer_disc_fn(const struct peer *peer, int status, void *arg); @@ -95,7 +105,11 @@ const struct peer_svc * peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid); int peer_delete(uint16_t conn_handle); int peer_add(uint16_t conn_handle); +#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) +int peer_init(int max_peers, int max_svcs, int max_incl_svcs, int max_chrs, int max_dscs); +#else int peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs); +#endif struct peer * peer_find(uint16_t conn_handle); #if MYNEWT_VAL(ENC_ADV_DATA) diff --git a/examples/bluetooth/nimble/common/nimble_central_utils/peer.c b/examples/bluetooth/nimble/common/nimble_central_utils/peer.c index 87bcf094a8..5635b26b4f 100644 --- a/examples/bluetooth/nimble/common/nimble_central_utils/peer.c +++ b/examples/bluetooth/nimble/common/nimble_central_utils/peer.c @@ -12,6 +12,10 @@ static void *peer_svc_mem; static struct os_mempool peer_svc_pool; +#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) +static void *peer_incl_svc_mem; +static struct os_mempool peer_incl_svc_pool; +#endif static void *peer_chr_mem; static struct os_mempool peer_chr_pool; @@ -44,6 +48,13 @@ 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); +#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) +static int +peer_inc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_incl_svc *service, void *arg); +static void +peer_disc_incs(struct peer *peer); +#endif struct peer * peer_find(uint16_t conn_handle) @@ -419,6 +430,189 @@ peer_disc_chrs(struct peer *peer) peer_disc_dscs(peer); } +#if (MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES)) +static struct peer_incl_svc * +peer_incl_svc_find_prev(const struct peer_svc *svc, uint16_t incl_svc_handle) +{ + struct peer_incl_svc *prev; + struct peer_incl_svc *incl_svc; + + prev = NULL; + SLIST_FOREACH(incl_svc, &svc->incl_svc, next) { + if (incl_svc->svc.handle >= incl_svc_handle) { + break; + } + + prev = incl_svc; + } + + return prev; +} + +static struct peer_incl_svc * +peer_incl_svc_find(const struct peer_svc *svc, uint16_t incl_svc_handle, + struct peer_incl_svc **out_prev) +{ + struct peer_incl_svc *prev; + struct peer_incl_svc *incl_svc; + + prev = peer_incl_svc_find_prev(svc, incl_svc_handle); + if (prev == NULL) { + incl_svc = SLIST_FIRST(&svc->incl_svc); + } else { + incl_svc = SLIST_NEXT(prev, next); + } + + if (incl_svc != NULL && incl_svc->svc.handle != incl_svc_handle) { + incl_svc = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return incl_svc; +} + +static void +peer_incl_svc_delete(struct peer_incl_svc *incl_svc) +{ + os_memblock_put(&peer_incl_svc_pool, incl_svc); +} + +static int +peer_inc_add(struct peer *peer, uint16_t svc_start_handle, + const struct ble_gatt_incl_svc *gatt_incl_svc) +{ + struct peer_incl_svc *incl_svc; + struct peer_incl_svc *incl_svc_prev; + struct peer_svc *cur_svc; + struct peer_svc *svc; + struct peer_svc *prev; + + svc = peer_svc_find(peer, gatt_incl_svc->start_handle, &prev); + + if (!svc) { + /* secondary service */ + svc = os_memblock_get(&peer_svc_pool); + if (svc == NULL) { + /* out of memory */ + return BLE_HS_ENOMEM; + } + + memset(svc, 0, sizeof *svc); + svc->svc.start_handle = gatt_incl_svc->start_handle; + svc->svc.end_handle = gatt_incl_svc->end_handle; + memcpy(&svc->svc.uuid, &gatt_incl_svc->uuid, sizeof(ble_uuid_any_t)); + + SLIST_INIT(&svc->chrs); + SLIST_INIT(&svc->incl_svc); + + if (prev == NULL) { + SLIST_INSERT_HEAD(&peer->svcs, svc, next); + } else { + SLIST_INSERT_AFTER(prev, svc, next); + } + } + + /* Including the services into inlucding list */ + + cur_svc = peer_svc_find_range(peer, gatt_incl_svc->handle); + + if (cur_svc == NULL) { + /* Can't find service for discovered included service; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + incl_svc = peer_incl_svc_find(cur_svc, gatt_incl_svc->handle, &incl_svc_prev); + if (incl_svc != NULL) { + /* Already discovered */ + return 0; + } + + incl_svc = os_memblock_get(&peer_incl_svc_pool); + if (incl_svc == NULL) { + return BLE_HS_ENOMEM; + } + + incl_svc->svc = *gatt_incl_svc; + + if (incl_svc_prev == NULL) { + SLIST_INSERT_HEAD(&cur_svc->incl_svc, incl_svc, next); + } else { + SLIST_INSERT_AFTER(incl_svc_prev, incl_svc, next); + } + + BLE_HS_LOG(DEBUG, "Inc Service added with handle = %d", gatt_incl_svc->handle); + + return 0; +} + + +static int +peer_inc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_incl_svc *service, void *arg) +{ + struct peer *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = peer_inc_add(peer, peer->cur_svc->svc.start_handle, service); + break; + + case BLE_HS_EDONE: + peer_disc_incs(peer); + rc = 0; + break; + + default: + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + peer_disc_complete(peer, rc); + } + + return rc; +} + +static void +peer_disc_incs(struct peer *peer) +{ + struct peer_svc *svc; + int rc; + + if (peer->cur_svc == NULL) { + peer->cur_svc = SLIST_FIRST(&peer->svcs); + } else { + peer->cur_svc = SLIST_NEXT(peer->cur_svc, next); + if (peer->cur_svc == NULL) { + if (peer->disc_prev_chr_val > 0) { + peer_disc_chrs(peer); + return; + } + } + } + + svc = peer->cur_svc; + rc = ble_gattc_find_inc_svcs(peer->conn_handle, + svc->svc.start_handle, + svc->svc.end_handle, + peer_inc_disced, peer); + if (rc != 0) { + peer_disc_chrs(peer); + } +} +#endif + int peer_svc_is_empty(const struct peer_svc *svc) { @@ -575,6 +769,14 @@ peer_svc_delete(struct peer_svc *svc) { struct peer_chr *chr; +#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) + struct peer_incl_svc *incl_svc; + + while ((incl_svc = SLIST_FIRST(&svc->incl_svc)) != NULL) { + SLIST_REMOVE_HEAD(&svc->incl_svc, next); + peer_incl_svc_delete(incl_svc); + } +#endif while ((chr = SLIST_FIRST(&svc->chrs)) != NULL) { SLIST_REMOVE_HEAD(&svc->chrs, next); peer_chr_delete(chr); @@ -599,10 +801,16 @@ peer_svc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, break; case BLE_HS_EDONE: + /* All services discovered; start discovering incs.*/ +#if (MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES)) + peer->cur_svc = NULL; + peer_disc_incs(peer); +#else /* All services discovered; start discovering characteristics. */ if (peer->disc_prev_chr_val > 0) { peer_disc_chrs(peer); } +#endif rc = 0; break; @@ -773,6 +981,10 @@ peer_free_mem(void) free(peer_svc_mem); peer_svc_mem = NULL; +#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) + free(peer_incl_svc_mem); + peer_incl_svc_mem = NULL; +#endif free(peer_chr_mem); peer_chr_mem = NULL; @@ -780,8 +992,13 @@ peer_free_mem(void) peer_dsc_mem = NULL; } +#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) +int +peer_init(int max_peers, int max_svcs, int max_incl_svcs, int max_chrs, int max_dscs) +#else int peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs) +#endif { int rc; @@ -818,8 +1035,26 @@ peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs) goto err; } +#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) + peer_incl_svc_mem = malloc( + OS_MEMPOOL_BYTES(max_incl_svcs, sizeof(struct peer_incl_svc))); + + if (peer_incl_svc_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_incl_svc_pool, max_incl_svcs, + sizeof(struct peer_incl_svc), peer_incl_svc_mem, + "peer_incl_svc_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } +#endif peer_chr_mem = malloc( OS_MEMPOOL_BYTES(max_chrs, sizeof (struct peer_chr))); + if (peer_chr_mem == NULL) { rc = BLE_HS_ENOMEM; goto err;