fix(nimble): Added missing api in nimble which is present in bluedroid

This commit is contained in:
Astha Verma
2025-08-19 12:19:41 +05:30
parent 550ce6a646
commit db2f236ec1
8 changed files with 507 additions and 2 deletions
+36
View File
@@ -171,6 +171,7 @@ struct hci_conn_update;
#define BLE_GAP_EVENT_PER_SUBEV_DATA_REQ 40
#define BLE_GAP_EVENT_PER_SUBEV_RESP 41
#define BLE_GAP_EVENT_PERIODIC_TRANSFER_V2 42
#define BLE_GAP_EVENT_CACHE_ASSOC 43
/* ISO events */
#define BLE_GAP_EVENT_CIS_ESTAB 43
@@ -876,6 +877,41 @@ struct ble_gap_event {
uint16_t conn_handle;
} passkey;
#if MYNEWT_VAL(BLE_GATT_CACHING_ASSOC_ENABLE)
/**
* Represents an attempt to associate the cached attribute data of a peer
* device with the data already stored on the host. This is typically done
* after a connection is established to avoid full service discovery if
* valid cached data is available.
*
* If the association is successful, `cache_state` will be set to
* CACHE_LOADED, indicating that the cached attributes can be used.
* If the association fails, `cache_state` will be set to CACHE_INVALID,
* meaning the cache is unusable and full discovery may be required.
*
* Valid for the following event types:
* o BLE_GAP_EVENT_CACHE_ASSOC
*/
struct {
/**
* Indicates the result of the cache association attempt;
* o 0: association successful;
* o BLE host error code: association failed for the specified reason.
*/
int status;
/** The handle of the relevant connection. */
uint16_t conn_handle;
/**
* Represents the state of the cache:
* o CACHE_LOADED : Cache successfully loaded
* o CACHE_INVALID : Cache invalid or unavailable
*/
uint8_t cache_state;
} cache_assoc;
#endif
/**
* Represents a received ATT notification or indication.
*
+30
View File
@@ -949,6 +949,36 @@ int ble_gatts_notify(uint16_t conn_handle, uint16_t chr_val_handle);
*/
int ble_gattc_notify(uint16_t conn_handle, uint16_t chr_val_handle);
/**
* Unregisters for notifications on a specified characteristic by writing to its
* Client Characteristic Configuration Descriptor (CCCD).
*
* This function initiates discovery of all descriptors associated with the given
* characteristic value handle (`char_val_handle`). If a CCCD is found, it writes
* a value to disable notifications.
*
* @param conn_handle The handle of the connection with the peer device.
* @param char_val_handle The value handle of the characteristic to unregister from notification.
*
* @return 0 on success, non-zero error code on failure.
*/
int ble_gattc_unregister_for_notification(uint16_t conn_handle, uint16_t char_val_handle);
/**
* Registers for notifications on a specified characteristic by writing to its
* Client Characteristic Configuration Descriptor (CCCD).
*
* This function initiates discovery of all descriptors associated with the given
* characteristic value handle (`char_val_handle`). If a CCCD is found, it writes
* the appropriate value to enable notifications.
*
* @param conn_handle The handle of the connection with the peer device.
* @param char_val_handle The value handle of the characteristic to register for notification.
*
* @return 0 on success, non-zero error code on failure.
*/
int ble_gattc_register_for_notification(uint16_t conn_handle, uint16_t char_val_handle);
/**
* Sends a "free-form" characteristic indication. The provided mbuf contains
* the indication payload. This function consumes the supplied mbuf regardless
+20
View File
@@ -8885,6 +8885,26 @@ ble_gap_pairing_complete_event(uint16_t conn_handle, int status)
ble_gap_call_conn_event_cb(&event, conn_handle);
#endif
}
/*****************************************************************************
* $cache *
*****************************************************************************/
#if MYNEWT_VAL(BLE_GATT_CACHING_ASSOC_ENABLE)
void
ble_gap_assoc_event(uint16_t conn_handle, int status, uint8_t cache_state)
{
#if NIMBLE_BLE_CONNECT
struct ble_gap_event event;
memset(&event, 0, sizeof event);
event.type = BLE_GAP_EVENT_CACHE_ASSOC;
event.cache_assoc.conn_handle = conn_handle;
event.cache_assoc.status = status;
event.cache_assoc.cache_state = cache_state;
ble_gap_call_conn_event_cb(&event, conn_handle);
#endif
}
#endif
/*****************************************************************************
* $rssi *
+3
View File
@@ -159,6 +159,9 @@ void ble_gap_enc_event(uint16_t conn_handle, int status,
int security_restored, int bonded);
void ble_gap_passkey_event(uint16_t conn_handle,
struct ble_gap_passkey_params *passkey_params);
#if MYNEWT_VAL(BLE_GATT_CACHING_ASSOC_ENABLE)
void ble_gap_assoc_event(uint16_t conn_handle, int status, uint8_t cache_state);
#endif
void ble_gap_notify_rx_event(uint16_t conn_handle, uint16_t attr_handle,
struct os_mbuf *om, int is_indication);
void ble_gap_notify_tx_event(int status, uint16_t conn_handle,
+154
View File
@@ -269,6 +269,7 @@ static ble_gattc_err_fn ble_gattc_read_mult_var_err;
static ble_gattc_err_fn ble_gattc_write_err;
static ble_gattc_err_fn ble_gattc_write_long_err;
static ble_gattc_err_fn ble_gattc_write_reliable_err;
static bool gatt_proc_active = false;
#endif
#if MYNEWT_VAL(BLE_GATTS)
@@ -5344,6 +5345,159 @@ ble_gattc_notify_custom(uint16_t conn_handle, uint16_t chr_val_handle,
{
return ble_gatts_notify_custom(conn_handle, chr_val_handle, txom);
}
static int
ble_gattc_cccd_write_complete_cb(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg)
{
BLE_HS_LOG(INFO,
"CCCD write : "
"status=%d conn_handle=%d attr_handle=%d",
error->status, conn_handle, attr->handle);
return 0;
}
static int ble_gattc_cccd_register_cb(uint16_t conn_handle, const struct ble_gatt_error *error,
uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
void *arg)
{
int rc = error->status;
bool *cccd_reg_flag = (bool *)arg;
if (error->status == BLE_HS_EDONE) {
/* GATT procedure completed reset active flag */
gatt_proc_active = false;
if (cccd_reg_flag) {
free(cccd_reg_flag);
}
} else if (error->status == 0) {
if (cccd_reg_flag && *cccd_reg_flag &&
(ble_uuid_cmp(&dsc->uuid.u, BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16)) == 0)) {
/* Prevent duplicate CCCD writes */
*cccd_reg_flag = false;
/* Enable notification */
uint8_t cccd_val[2] = {0x01, 0x00};
/* write to CCCD to enable notifications */
rc = ble_gattc_write_flat(conn_handle,
dsc->handle,
cccd_val, sizeof(cccd_val),
ble_gattc_cccd_write_complete_cb, NULL);
if (rc != 0) {
BLE_HS_LOG(WARN, "Failed to Register for Notification, status = %d", rc);
}
}
} else {
BLE_HS_LOG(WARN, "GATT descriptor discovery failed with status = %d", error->status);
gatt_proc_active = false;
if (cccd_reg_flag) {
free(cccd_reg_flag);
}
}
return rc;
}
int ble_gattc_register_for_notification(uint16_t conn_handle, uint16_t char_val_handle)
{
/* Check if a GATT procedure is already active */
if (gatt_proc_active) {
BLE_HS_LOG(WARN, "Gatt procedure active; cannot start new process.");
return BLE_HS_EBUSY;
}
bool *cccd_reg_flag = (bool *)malloc(sizeof(bool));
if (!cccd_reg_flag) {
BLE_HS_LOG(ERROR, "Failed to allocate memory for CCCD Reg Flag.");
return BLE_HS_ENOMEM;
}
*cccd_reg_flag = true;
/* Mark that a GATT procedure is now active */
gatt_proc_active = true;
int rc = ble_gattc_disc_all_dscs(conn_handle, char_val_handle, 0xffff,
ble_gattc_cccd_register_cb, cccd_reg_flag);
if (rc != 0) {
gatt_proc_active = false;
free(cccd_reg_flag);
}
return rc;
}
static int ble_gattc_cccd_unregister_cb(uint16_t conn_handle, const struct ble_gatt_error *error,
uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
void *arg)
{
int rc = error->status;
bool *cccd_unreg_flag = (bool *)arg;
if (error->status == BLE_HS_EDONE) {
/* GATT procedure completed reset active flag */
gatt_proc_active = false;
if (cccd_unreg_flag) {
free(cccd_unreg_flag);
}
} else if (error->status == 0) {
if (cccd_unreg_flag && *cccd_unreg_flag &&
(ble_uuid_cmp(&dsc->uuid.u, BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16)) == 0)) {
/* Prevent duplicate CCCD writes */
*cccd_unreg_flag = false;
/* Disable notification or indication */
uint8_t cccd_val[2] = {0x00, 0x00};
/* write to CCCD to disable notifications or indication */
rc = ble_gattc_write_flat(conn_handle,
dsc->handle,
cccd_val, sizeof(cccd_val),
ble_gattc_cccd_write_complete_cb, NULL);
if (rc != 0) {
BLE_HS_LOG(WARN, "Failed to Unregister for Notification, status = %d", rc);
}
}
} else {
BLE_HS_LOG(WARN, "GATT descriptor discovery failed with status = %d", error->status);
gatt_proc_active = false;
if (cccd_unreg_flag) {
free(cccd_unreg_flag);
}
}
return rc;
}
int ble_gattc_unregister_for_notification(uint16_t conn_handle, uint16_t char_val_handle)
{
if (gatt_proc_active) {
BLE_HS_LOG(WARN, "Gatt procedure active; cannot start new process");
return BLE_HS_EBUSY;
}
int rc;
bool *cccd_unreg_flag = (bool *)malloc(sizeof(bool));
if (!cccd_unreg_flag) {
BLE_HS_LOG(ERROR, "Failed to allocate memory for CCCD Reg Flag");
return BLE_HS_ENOMEM;
}
*cccd_unreg_flag = true;
gatt_proc_active = true;
rc = ble_gattc_disc_all_dscs(conn_handle, char_val_handle, 0xFFFF,
ble_gattc_cccd_unregister_cb, cccd_unreg_flag);
if (rc != 0) {
gatt_proc_active = false;
free(cccd_unreg_flag);
}
return rc;
}
#endif
int
+109 -2
View File
@@ -261,6 +261,30 @@ ble_gattc_cache_find_addr(ble_addr_t addr)
return INVALID_ADDR_NUM;
}
void ble_gattc_cache_get_addr_list(ble_addr_t *addr_list, uint8_t *out_num)
{
uint8_t num = cache_env->num_addr;
if (addr_list == NULL || out_num == NULL) {
BLE_HS_LOG(WARN, "Invalid input to ble_gattc_cache_get_addr_list.");
return;
}
for (uint8_t i = 0; i < num; i++) {
memcpy(&addr_list[i], &cache_env->cache_addr[i].addr, sizeof(ble_addr_t));
}
*out_num = num;
BLE_HS_LOG(DEBUG, "Total %d addresses in cache (each %u bytes):", num, (unsigned int)sizeof(ble_addr_t));
for (uint8_t i = 0; i < num; i++) {
ble_addr_t *addr = &addr_list[i];
BLE_HS_LOG(DEBUG, "[%d] Addr: %02x:%02x:%02x:%02x:%02x:%02x, type: %d\n", i,
addr->val[5], addr->val[4], addr->val[3],
addr->val[2], addr->val[1], addr->val[0], addr->type);
}
}
static uint8_t
ble_gattc_cache_find_hash(uint8_t * hash_key)
{
@@ -330,7 +354,7 @@ static void
ble_gattc_fill_nv_attr(struct ble_gattc_cache_conn *peer, size_t num_attr, struct ble_gatt_nv_attr *nv_attr)
{
struct ble_gattc_cache_conn_svc *svc;
#if MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES)
#if MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES)
struct ble_gattc_cache_conn_incl_svc *included_svc;
#endif
struct ble_gattc_cache_conn_chr *chr;
@@ -626,6 +650,89 @@ ble_gattc_add_dsc_from_cache(ble_addr_t peer_addr, struct ble_gatt_nv_attr nv_at
nimble_platform_mem_free(gatt_dsc);
return rc;
}
#if MYNEWT_VAL(BLE_GATT_CACHING_ASSOC_ENABLE)
int
ble_gattc_cache_assoc_load(ble_addr_t src_addr, uint8_t src_index, ble_addr_t assoc_addr)
{
int rc = 0;
int num_attr = 50;
struct ble_gatt_nv_attr *nv_attr;
if (!cacheOpen(src_addr, true, &src_index)) {
BLE_HS_LOG(INFO, "gattc cache open fail for src addr");
return BLE_HS_EINVAL;
}
/* Load the GATT attributes stored at src_index */
nv_attr = ble_gattc_cache_load_nv_attr(src_index, &num_attr);
if (nv_attr == NULL) {
BLE_HS_LOG(ERROR, "Failed to load nv_attr from source index %d", src_index);
return BLE_HS_EINVAL;
}
/* Load each attribute from the source into the associated address */
for (int i = 0; i < num_attr; i++) {
switch (nv_attr[i].attr_type) {
case BLE_GATT_ATTR_TYPE_SRVC:
rc = ble_gattc_add_svc_from_cache(assoc_addr, nv_attr[i]);
break;
case BLE_GATT_ATTR_TYPE_CHAR:
rc = ble_gattc_add_chr_from_cache(assoc_addr, nv_attr[i]);
break;
case BLE_GATT_ATTR_TYPE_CHAR_DESCR:
rc = ble_gattc_add_dsc_from_cache(assoc_addr, nv_attr[i]);
break;
#if MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES)
case BLE_GATT_ATTR_TYPE_INCL_SRVC:
rc = ble_gattc_add_inc_from_cache(assoc_addr, nv_attr[i]);
break;
#endif
default:
break;
}
}
nimble_platform_mem_free(nv_attr);
/* Copy the database hash from src_addr entry and associate it with assoc_addr */
ble_gattc_cache_conn_load_hash(assoc_addr,
cache_env->cache_addr[src_index].hash_key);
BLE_HS_LOG(DEBUG, "Successfully associated cache from src_addr to assoc_addr.");
return 0;
}
int
ble_gattc_cache_find_source(struct ble_gattc_cache_conn *cache_conn, uint8_t *database_hash)
{
uint8_t addr_index = 0;
uint8_t num = cache_env->num_addr;
cache_addr_info_t *addr_info = &cache_env->cache_addr[0];
int rc = ESP_FAIL;
/* Iterate through all cached addresses to find a matching database hash */
for (addr_index = 0; addr_index < num; addr_index++, addr_info++) {
/* Compare stored hash with the provided database_hash */
if (!memcmp(&addr_info->hash_key, database_hash, sizeof(uint8_t) * 16)) {
rc = ble_gattc_cache_assoc_load(addr_info->addr, addr_index, cache_conn->ble_gattc_cache_conn_addr);
if (rc == 0) {
cache_conn->cache_state = CACHE_LOADED;
cache_conn->assoc_success = 1;
BLE_HS_LOG(INFO, "Cache successfully loaded from src");
break;
}
}
}
ble_gap_assoc_event(cache_conn->conn_handle, rc, cache_conn->cache_state);
return rc;
}
#endif
int
ble_gattc_cache_load(ble_addr_t peer_addr)
@@ -660,7 +767,7 @@ ble_gattc_cache_load(ble_addr_t peer_addr)
rc = ble_gattc_add_dsc_from_cache(peer_addr, nv_attr[i]);
break;
#if MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES)
#if MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES)
case BLE_GATT_ATTR_TYPE_INCL_SRVC:
rc = ble_gattc_add_inc_from_cache(peer_addr, nv_attr[i]);
break;
+142
View File
@@ -1723,6 +1723,9 @@ ble_gattc_cache_conn_create(uint16_t conn_handle, ble_addr_t ble_gattc_cache_con
/* set the conn as dirty initially as the cache is not built */
cache_conn->cache_state = CACHE_INVALID;
cache_conn->conn_handle = conn_handle;
#if MYNEWT_VAL(BLE_GATT_CACHING_ASSOC_ENABLE)
cache_conn->assoc_success = 0;
#endif
memcpy(&cache_conn->ble_gattc_cache_conn_addr, &ble_gattc_cache_conn_addr, sizeof(ble_addr_t));
SLIST_INSERT_HEAD(&ble_gattc_cache_conns, cache_conn, next);
@@ -1731,6 +1734,7 @@ ble_gattc_cache_conn_create(uint16_t conn_handle, ble_addr_t ble_gattc_cache_con
if (rc == 0) {
cache_conn->cache_state = CACHE_LOADED;
}
return 0;
}
@@ -1962,6 +1966,136 @@ ble_gattc_cache_conn_update(uint16_t conn_handle, uint16_t start_handle, uint16_
}
#endif
int ble_gattc_cache_refresh(ble_addr_t peer_addr)
{
struct ble_hs_conn *hs_conn;
struct ble_gattc_cache_conn *conn;
int rc;
hs_conn = ble_hs_conn_find_by_addr(&peer_addr);
if (hs_conn == NULL) {
BLE_HS_LOG(ERROR, "GATT cache refresh: connection not found for peer: %02x:%02x:%02x:%02x:%02x:%02x\n",
peer_addr.val[0], peer_addr.val[1], peer_addr.val[2],
peer_addr.val[3], peer_addr.val[4], peer_addr.val[5]);
return BLE_HS_ENOTCONN;
}
/* Lookup cache connection entry */
conn = ble_gattc_cache_conn_find_by_addr(peer_addr);
if (conn == NULL) {
BLE_HS_LOG(WARN, "GATT cache refresh: no cache entry found for peer.");
return BLE_HS_EUNKNOWN;
}
ble_gattc_cacheReset(&peer_addr);
/* Invalidate and restart discovery*/
rc = ble_gattc_cache_conn_disc(conn);
return rc;
}
#if MYNEWT_VAL(BLE_GATT_CACHING_ASSOC_ENABLE)
static int
ble_gattc_cache_conn_assoc_on_read(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg)
{
uint16_t res;
uint8_t database_hash[16];
int rc;
if (error->status == BLE_HS_EDONE) {
/* Ignore Read by UUID follow-up callback */
return 0;
}
if (error->status != 0) {
res = error->status;
return res;
}
rc = os_mbuf_copydata(attr->om, 0, sizeof(database_hash), database_hash);
if (rc != 0) {
BLE_HS_LOG(WARN, "Failed to copy database hash from attr->om (rc=%d)", rc);
return rc;
}
rc = ble_gattc_cache_find_source((struct ble_gattc_cache_conn *)arg, database_hash);
return rc;
}
int ble_gattc_cache_assoc(ble_addr_t peer_addr)
{
int rc;
struct ble_gattc_cache_conn *cache_conn;
cache_conn = ble_gattc_cache_conn_find_by_addr(peer_addr);
if (cache_conn == NULL) {
BLE_HS_LOG(WARN, "No cache entry found for peer.");
return BLE_HS_EUNKNOWN;
}
if (cache_conn->cache_state == CACHE_LOADED) {
BLE_HS_LOG(INFO, "Cache already loaded for conn_handle=%d; "
"cache state=%d. Skipping association.",
cache_conn->conn_handle, cache_conn->cache_state);
ble_gap_assoc_event(cache_conn->conn_handle, 0, cache_conn->cache_state);
}
if (cache_conn->cache_state == CACHE_INVALID) {
rc = ble_gattc_read_by_uuid(cache_conn->conn_handle, 0x0001, 0xFFFF,
BLE_UUID16_DECLARE(BLE_SVC_GATT_CHR_DATABASE_HASH_UUID16),
ble_gattc_cache_conn_assoc_on_read, cache_conn);
if (rc != 0) {
cache_conn->cache_state = CACHE_INVALID;
return rc;
}
}
return 0;
}
#endif
int ble_gattc_cache_clean(ble_addr_t peer_addr)
{
struct ble_hs_conn *hs_conn;
struct ble_gattc_cache_conn *conn;
// Check if the device is connected
hs_conn = ble_hs_conn_find_by_addr(&peer_addr);
if (hs_conn == NULL) {
BLE_HS_LOG(WARN, "GATT cache clean: No active connection found for peer:"
" %02x:%02x:%02x:%02x:%02x:%02x, Attempting cache clean anyway.",
peer_addr.val[0], peer_addr.val[1], peer_addr.val[2],
peer_addr.val[3], peer_addr.val[4], peer_addr.val[5]);
ble_gattc_cacheReset(&peer_addr);
return 0;
}
// Find cached GATT services for this peer
conn = ble_gattc_cache_conn_find_by_addr(peer_addr);
if (conn == NULL) {
BLE_HS_LOG(WARN, "GATT cache clean: no cache entry found for peer.");
return BLE_HS_EUNKNOWN;
}
// Reset any existing discovery flags/cache markers
ble_gattc_cacheReset(&peer_addr);
conn->cache_state = CACHE_INVALID;
BLE_HS_LOG(INFO, "GATT cache cleaned for peer: %02x:%02x:%02x:%02x:%02x:%02x",
peer_addr.val[0], peer_addr.val[1], peer_addr.val[2],
peer_addr.val[3], peer_addr.val[4], peer_addr.val[5]);
return 0;
}
uint16_t
ble_gattc_cache_conn_get_svc_changed_handle(uint16_t conn_handle)
{
@@ -2143,6 +2277,14 @@ static int ble_gattc_cache_conn_verify(struct ble_gattc_cache_conn *conn)
{
struct ble_hs_conn *gap_conn;
int rc;
#if MYNEWT_VAL(BLE_GATT_CACHING_ASSOC_ENABLE)
if (conn->assoc_success && conn->cache_state == CACHE_LOADED) {
conn->cache_state = CACHE_VERIFIED;
BLE_HS_LOG(INFO, "Associate complete, skipping Discovery");
ble_gattc_cache_conn_disc_complete(conn, 0);
return 0;
}
#endif
if (conn->cache_state == CACHE_VERIFIED) {
return 0;
+13
View File
@@ -134,6 +134,11 @@ struct ble_gattc_cache_conn {
/* event to be posted to inform
the application about the discovery results */
struct ble_npl_event disc_ev;
#if MYNEWT_VAL(BLE_GATT_CACHING_ASSOC_ENABLE)
/** Indicates whether association was successful (0 = fail, 1 = success) */
uint8_t assoc_success;
#endif
};
/** Enumerates types of GATT database attributes */
@@ -287,6 +292,14 @@ void ble_gattc_cache_conn_broken(uint16_t conn_handle);
void ble_gattc_cache_conn_bonding_established(uint16_t conn_handle);
void ble_gattc_cache_conn_bonding_restored(uint16_t conn_handle);
void ble_gattc_cache_get_addr_list(ble_addr_t *addr_list, uint8_t *out_num);
int ble_gattc_cache_refresh(ble_addr_t peer_addr);
int ble_gattc_cache_clean(ble_addr_t peer_addr);
#if MYNEWT_VAL(BLE_GATT_CACHING_ASSOC_ENABLE)
int ble_gattc_cache_assoc(ble_addr_t peer_addr);
int ble_gattc_cache_find_source(struct ble_gattc_cache_conn *cache_conn, uint8_t *database_hash);
int ble_gattc_cache_assoc_load(ble_addr_t src_addr, uint8_t src_index, ble_addr_t assoc_addr);
#endif
/* cache search */
int ble_gattc_cache_conn_search_all_svcs(uint16_t conn_handle,
ble_gatt_disc_svc_fn *cb, void *cb_arg);