From 8032fe905f4b8645424d2eaa2144ea376f09d982 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Mon, 11 May 2026 11:00:52 +0200 Subject: [PATCH] nimble/host: Rework ble_gatts_notify_multiple_custom Simplify ble_gatts_notify_multiple_custom implementation: - reject if peer doesn't support it (app shall check it) - fail if doesn't git in single ATT packet - fix missing cleanups on error - fix missing endianess convertions ble_gatts_notify_multiple() is now very thin wrapper on top of ble_gatts_notify_multiple_custom(). --- nimble/host/include/host/ble_gatt.h | 6 -- nimble/host/src/ble_att_clt.c | 3 + nimble/host/src/ble_gattc.c | 130 +++++++++++----------------- 3 files changed, 53 insertions(+), 86 deletions(-) diff --git a/nimble/host/include/host/ble_gatt.h b/nimble/host/include/host/ble_gatt.h index e74ba9e1a1..df9c36bbeb 100644 --- a/nimble/host/include/host/ble_gatt.h +++ b/nimble/host/include/host/ble_gatt.h @@ -692,9 +692,6 @@ int ble_gatts_notify_custom(uint16_t conn_handle, uint16_t att_handle, * 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. - * * If value of characteristic is not specified it will be read from local * GATT database. * @@ -733,9 +730,6 @@ int ble_gatts_notify(uint16_t conn_handle, uint16_t chr_val_handle); * 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" diff --git a/nimble/host/src/ble_att_clt.c b/nimble/host/src/ble_att_clt.c index ddd7fc8a9d..43cb0610ed 100644 --- a/nimble/host/src/ble_att_clt.c +++ b/nimble/host/src/ble_att_clt.c @@ -1022,6 +1022,9 @@ ble_att_clt_tx_notify_mult(uint16_t conn_handle, struct os_mbuf *txom) ble_eatt_release_chan(conn_handle, BLE_GATT_OP_DUMMY); } + return rc; + err: + os_mbuf_free_chain(txom); return rc; } diff --git a/nimble/host/src/ble_gattc.c b/nimble/host/src/ble_gattc.c index 0418a405b6..0e3553ed4a 100644 --- a/nimble/host/src/ble_gattc.c +++ b/nimble/host/src/ble_gattc.c @@ -4424,85 +4424,78 @@ ble_gatts_notify_multiple_custom(uint16_t conn_handle, #endif int rc; - size_t i = 0; - uint16_t cur_chr_cnt = 0; - /* mtu = MTU - 1 octet (OP code) */ - uint16_t mtu = ble_att_mtu(conn_handle) - 1; + size_t i; struct os_mbuf *txom; - struct ble_hs_conn *conn; + uint16_t le16; + uint8_t supported; + uint16_t total = 1; /* opcode */ - txom = ble_hs_mbuf_att_pkt(); - if (txom == NULL) { - return BLE_HS_ENOMEM; + /* validate handles sanity */ + for (i = 0; i < chr_count; i++) { + if (tuples->handle == 0) { + rc = BLE_HS_EINVAL; + goto err; + } } - conn = ble_hs_conn_find(conn_handle); - if (conn == NULL) { - return BLE_HS_ENOTCONN; + if (chr_count < 2) { + rc = BLE_HS_EINVAL; + goto err; + } + + rc = ble_gatts_peer_cl_sup_feat_get(conn_handle, &supported, sizeof(supported)); + if (rc) { + goto err; + } + + if ((supported & 0x04) == 0) { + rc = BLE_HS_EREJECT; + goto err; } /* Read missing values */ for (i = 0; i < chr_count; i++) { - if (tuples->handle == 0) { - rc = BLE_HS_EINVAL; - goto done; - } if (tuples[i].value == NULL) { rc = ble_att_svr_read_local(tuples[i].handle, &tuples[i].value); if (rc != 0) { - goto done; + goto err; } } } - /* If peer does not support fall back to multiple single value - * Notifications */ - if ((conn->bhc_gatt_svr.peer_cl_sup_feat[0] & 0x04) == 0) { - for (i = 0; i < chr_count; i++) { - rc = ble_att_clt_tx_notify(conn_handle, tuples[chr_count].handle, - tuples[chr_count].value); - if (rc != 0) { - goto done; - } - } + /* check if this fits into MTU */ + for (i = 0; i < chr_count; i++) { + total += sizeof(uint16_t); /* handle */ + total += sizeof(uint16_t); /* length */ + total += os_mbuf_len(tuples[i].value); } - for (i = 0; i < chr_count; i++) { - if (txom->om_len + tuples[i].value->om_len > mtu && cur_chr_cnt < 2) { - rc = ble_att_clt_tx_notify(conn_handle, tuples[i].handle, - tuples[i].value); - if (rc != 0) { - goto done; - } - continue; - } else if (txom->om_len + tuples[i].value->om_len > mtu) { - rc = ble_att_clt_tx_notify_mult(conn_handle, txom); - if (rc != 0) { - goto done; - } - cur_chr_cnt = 0; - /* buffer was consumed, allocate new one */ - txom = ble_hs_mbuf_att_pkt(); - if (txom == NULL) { - return BLE_HS_ENOMEM; - } - } + if (total > ble_att_mtu(conn_handle)) { + rc = BLE_HS_EMSGSIZE; + goto err; + } - os_mbuf_append(txom, &tuples[i].handle, sizeof(uint16_t)); - os_mbuf_append(txom, &tuples[i].value->om_len, - sizeof(uint16_t)); + txom = ble_hs_mbuf_att_pkt(); + if (txom == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + for (i = 0; i < chr_count; i++) { + le16 = htole16(tuples[i].handle); + os_mbuf_append(txom, &le16, sizeof(uint16_t)); + le16 = htole16(tuples[i].value->om_len); + os_mbuf_append(txom, &le16, sizeof(uint16_t)); os_mbuf_concat(txom, tuples[i].value); - cur_chr_cnt++; } - if (cur_chr_cnt == 1) { - rc = ble_att_clt_tx_notify(conn_handle, tuples[chr_count].handle, - tuples[chr_count].value); - } else { - rc = ble_att_clt_tx_notify_mult(conn_handle, txom); + return ble_att_clt_tx_notify_mult(conn_handle, txom); + +err: + for (i = 0; i < chr_count; i++) { + os_mbuf_free_chain(tuples[i].value); } -done: return rc; } @@ -4514,38 +4507,15 @@ ble_gatts_notify_multiple(uint16_t conn_handle, #if !MYNEWT_VAL(BLE_GATT_NOTIFY_MULTIPLE) return BLE_HS_ENOTSUP; #endif - int rc; size_t i; struct ble_gatt_notif tuples[num_handles]; - struct ble_hs_conn *conn; - - BLE_HS_LOG_DEBUG("conn_handle %d\n", conn_handle); - conn = ble_hs_conn_find(conn_handle); - if (conn == NULL) { - return BLE_HS_ENOTCONN; - } - - /** Skip sending to client that doesn't support this feature */ - BLE_HS_LOG_DEBUG("ble_gatts_notify_multiple: peer_cl_sup_feat %d\n", - conn->bhc_gatt_svr.peer_cl_sup_feat[0]); - if ((conn->bhc_gatt_svr.peer_cl_sup_feat[0] & 0x04) == 0) { - for (i = 0; i < num_handles; i++) { - rc = ble_gatts_notify(conn_handle, chr_val_handles[i]); - if (rc != 0) { - return rc; - } - } - return 0; - } for (i = 0; i < num_handles; i++) { tuples[i].handle = chr_val_handles[i]; tuples[i].value = NULL; - BLE_HS_LOG(DEBUG, "handle 0x%02x\n", tuples[i].handle); } - rc = ble_gatts_notify_multiple_custom(conn_handle, num_handles, tuples); - return rc; + return ble_gatts_notify_multiple_custom(conn_handle, num_handles, tuples); } /*****************************************************************************