From 3e30c9f70b506958a70c82acb50b155d5a69e060 Mon Sep 17 00:00:00 2001 From: Rahul Tank Date: Fri, 14 Aug 2026 14:28:38 +0530 Subject: [PATCH] fix(nimble): Add separate helper functions to register notification/indication --- nimble/host/include/host/ble_gatt.h | 62 ++++++++++++++++++++-- nimble/host/src/ble_gattc.c | 82 ++++++++++++++++++++--------- 2 files changed, 116 insertions(+), 28 deletions(-) diff --git a/nimble/host/include/host/ble_gatt.h b/nimble/host/include/host/ble_gatt.h index bfa27dfc3..fe72bebaa 100644 --- a/nimble/host/include/host/ble_gatt.h +++ b/nimble/host/include/host/ble_gatt.h @@ -953,15 +953,54 @@ 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). + * Sends a multiple handle variable length characteristic notification. The + * content of the message is read from the specified characteristics. + * Notifications are sent in order of supplied handles. Function tries to + * send minimum amount of PDUs. If PDU can't contain all of the + * characteristic values, multiple notifications are sent. If only one + * handle-value pair fits into PDU, or only one characteristic remains in the + * list, regular characteristic notification is sent. + * + * If GATT client doesn't support receiving multiple handle notifications, + * this will use GATT notification for each characteristic, separately. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param num_handles The number of entries in the "chr_val_handles" + * array. + * @param chr_val_handles Array of attribute handles of the + * characteristics to include in the outgoing + * notification. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gatts_notify_multiple(uint16_t conn_handle, + size_t num_handles, + const uint16_t *chr_val_handles); + +/** + * Unregisters for notifications and indications 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. + * zero to disable both notifications and indications. * * @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. + * @param char_val_handle The value handle of the characteristic to unregister + * from notifications and indications. + * + * @return 0 on success, non-zero error code on failure. + */ +int ble_gattc_unregister_notif_indicate(uint16_t conn_handle, + uint16_t char_val_handle); + +/** + * Deprecated. Use ble_gattc_unregister_notif_indicate instead. + * + * @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 notifications and indications. * * @return 0 on success, non-zero error code on failure. */ @@ -982,6 +1021,21 @@ int ble_gattc_unregister_for_notification(uint16_t conn_handle, uint16_t char_va */ int ble_gattc_register_for_notification(uint16_t conn_handle, uint16_t char_val_handle); +/** + * Registers for indications 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 indications. + * + * @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 indication. + * + * @return 0 on success, non-zero error code on failure. + */ +int ble_gattc_register_for_indication(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 diff --git a/nimble/host/src/ble_gattc.c b/nimble/host/src/ble_gattc.c index 84722621f..bd393fb24 100644 --- a/nimble/host/src/ble_gattc.c +++ b/nimble/host/src/ble_gattc.c @@ -5718,6 +5718,11 @@ ble_gattc_notify_custom(uint16_t conn_handle, uint16_t chr_val_handle, #if MYNEWT_VAL(BLE_GATTC) #if MYNEWT_VAL(BLE_STORE_MAX_CCCDS) +struct ble_gattc_cccd_reg_arg { + bool active; + uint16_t value; +}; + static int ble_gattc_cccd_write_complete_cb(uint16_t conn_handle, const struct ble_gatt_error *error, @@ -5737,44 +5742,47 @@ static int ble_gattc_cccd_register_cb(uint16_t conn_handle, const struct ble_gat void *arg) { int rc = error->status; - bool *cccd_reg_flag = (bool *)arg; + struct ble_gattc_cccd_reg_arg *reg_arg = arg; if (error->status == BLE_HS_EDONE) { /* GATT procedure completed reset active flag */ gatt_proc_active = false; - if (cccd_reg_flag) { - nimble_platform_mem_free(cccd_reg_flag); + if (reg_arg) { + nimble_platform_mem_free(reg_arg); } } else if (error->status == 0) { - if (cccd_reg_flag && *cccd_reg_flag && + if (reg_arg && reg_arg->active && (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}; + reg_arg->active = false; + uint8_t cccd_val[2] = { + reg_arg->value & 0xff, + (reg_arg->value >> 8) & 0xff, + }; - /* 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); + ble_gattc_cccd_write_complete_cb, reg_arg); if (rc != 0) { - BLE_HS_LOG(WARN, "Failed to Register for Notification, status = %d", rc); - rc = 0; + BLE_HS_LOG(WARN, "Failed to register for CCCD, status = %d", rc); + return 0; } } } else { BLE_HS_LOG(WARN, "GATT descriptor discovery failed with status = %d", error->status); gatt_proc_active = false; - if (cccd_reg_flag) { - nimble_platform_mem_free(cccd_reg_flag); + if (reg_arg) { + nimble_platform_mem_free(reg_arg); } } return rc; } -int ble_gattc_register_for_notification(uint16_t conn_handle, uint16_t char_val_handle) +static int +ble_gattc_register_for_cccd(uint16_t conn_handle, uint16_t char_val_handle, + uint16_t cccd_value) { /* Check if a GATT procedure is already active */ if (gatt_proc_active) { @@ -5783,26 +5791,42 @@ int ble_gattc_register_for_notification(uint16_t conn_handle, uint16_t char_val_ return BLE_HS_EBUSY; } - bool *cccd_reg_flag = (bool *)nimble_platform_mem_calloc(1,sizeof(bool)); - if (!cccd_reg_flag) { - BLE_HS_LOG(ERROR, "Failed to allocate memory for CCCD Reg Flag."); + struct ble_gattc_cccd_reg_arg *reg_arg = + nimble_platform_mem_calloc(1, sizeof(*reg_arg)); + if (!reg_arg) { + BLE_HS_LOG(ERROR, "Failed to allocate memory for CCCD registration."); return BLE_HS_ENOMEM; } - *cccd_reg_flag = true; + reg_arg->active = true; + reg_arg->value = cccd_value; /* 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); + ble_gattc_cccd_register_cb, reg_arg); if (rc != 0) { gatt_proc_active = false; - nimble_platform_mem_free(cccd_reg_flag); + nimble_platform_mem_free(reg_arg); } return rc; } +int +ble_gattc_register_for_notification(uint16_t conn_handle, uint16_t char_val_handle) +{ + return ble_gattc_register_for_cccd(conn_handle, char_val_handle, + BLE_GATTS_CLT_CFG_F_NOTIFY); +} + +int +ble_gattc_register_for_indication(uint16_t conn_handle, uint16_t char_val_handle) +{ + return ble_gattc_register_for_cccd(conn_handle, char_val_handle, + BLE_GATTS_CLT_CFG_F_INDICATE); +} + 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) @@ -5824,14 +5848,16 @@ static int ble_gattc_cccd_unregister_cb(uint16_t conn_handle, const struct ble_g /* Disable notification or indication */ uint8_t cccd_val[2] = {0x00, 0x00}; - /* write to CCCD to disable notifications or indication */ + /* Write to CCCD to disable notifications and indications. */ 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); - rc = 0; + BLE_HS_LOG(WARN, + "Failed to unregister for notifications or indications, status = %d", + rc); + return 0; } } } else { @@ -5846,7 +5872,9 @@ static int ble_gattc_cccd_unregister_cb(uint16_t conn_handle, const struct ble_g } -int ble_gattc_unregister_for_notification(uint16_t conn_handle, uint16_t char_val_handle) +int +ble_gattc_unregister_notif_indicate(uint16_t conn_handle, + uint16_t char_val_handle) { if (gatt_proc_active) { BLE_HS_LOG(WARN, "Gatt procedure active; cannot start new process"); @@ -5874,6 +5902,12 @@ int ble_gattc_unregister_for_notification(uint16_t conn_handle, uint16_t char_va return rc; } + +int +ble_gattc_unregister_for_notification(uint16_t conn_handle, uint16_t char_val_handle) +{ + return ble_gattc_unregister_notif_indicate(conn_handle, char_val_handle); +} #endif //MYNEWT_VAL(BLE_STORE_MAX_CCCDS) #endif //MYNEWT_VAL(BLE_GATTC)