This is an automated email from the ASF dual-hosted git repository.

kopyscinski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git

commit f07a77489f4f4b20073b257123ec4fd5123c0b05
Author: Krzysztof Kopyściński <[email protected]>
AuthorDate: Fri Aug 11 14:17:10 2023 +0200

    host/gatt_srv: Implement sending Multiple Handle Notifications
    
    It is mandatory to support sending them if receiving is supported.
    Implemented functionality and added public API.
---
 nimble/host/include/host/ble_gatt.h     |  63 +++++++++++++++
 nimble/host/src/ble_att_clt.c           |  32 ++++++++
 nimble/host/src/ble_att_priv.h          |   1 +
 nimble/host/src/ble_gattc.c             | 133 ++++++++++++++++++++++++++++++++
 nimble/host/syscfg.yml                  |   7 ++
 nimble/include/nimble/nimble_opt_auto.h |   3 +
 6 files changed, 239 insertions(+)

diff --git a/nimble/host/include/host/ble_gatt.h 
b/nimble/host/include/host/ble_gatt.h
index b82ab2f40..a38d3c934 100644
--- a/nimble/host/include/host/ble_gatt.h
+++ b/nimble/host/include/host/ble_gatt.h
@@ -246,6 +246,16 @@ struct ble_gatt_dsc {
     ble_uuid_any_t uuid;
 };
 
+
+/** Represents a handle-value tuple for multiple handle notifications. */
+struct ble_gatt_notif {
+    /** The handle of the GATT characteristic */
+    uint16_t handle;
+
+    /** The buffer with GATT characteristic value */
+    struct os_mbuf *value;
+};
+
 /** Function prototype for the GATT MTU exchange callback. */
 typedef int ble_gatt_mtu_fn(uint16_t conn_handle,
                             const struct ble_gatt_error *error,
@@ -634,6 +644,33 @@ int ble_gattc_write_reliable(uint16_t conn_handle,
 int ble_gatts_notify_custom(uint16_t conn_handle, uint16_t att_handle,
                             struct os_mbuf *om);
 
+/**
+ * Sends a "free-form" multiple handle variable length characteristic
+ * notification. This function consumes supplied mbufs regardless of the
+ * outcome. Notifications are sent in order of supplied entries.
+ * 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.
+ *
+ * If value of characteristic is not specified it will be read from local
+ * GATT database.
+ *
+ * @param conn_handle           The connection over which to execute the
+ *                                  procedure.
+ * @param chr_count             Number of characteristics to notify about.
+ * @param tuples                Handle-value pairs in form of `ble_gatt_notif`
+ *                                  structures.
+ *
+ * @return                      0 on success; nonzero on failure.
+ */
+int ble_gatts_notify_multiple_custom(uint16_t conn_handle,
+                                     uint16_t chr_count,
+                                     struct ble_gatt_notif *tuples);
+
 /**
  * Deprecated. Should not be used. Use ble_gatts_notify_custom instead.
  */
@@ -659,6 +696,32 @@ 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);
 
+/**
+ * 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,
+                              uint8_t num_handles,
+                              const uint16_t *chr_val_handles);
+
 /**
  * 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_att_clt.c b/nimble/host/src/ble_att_clt.c
index 3423e25e8..2b60f8b53 100644
--- a/nimble/host/src/ble_att_clt.c
+++ b/nimble/host/src/ble_att_clt.c
@@ -986,3 +986,35 @@ ble_att_clt_rx_indicate(uint16_t conn_handle, uint16_t 
cid, struct os_mbuf **rxo
 }
 
 #endif
+
+/*****************************************************************************
+ * $multiple handle value notification                                       *
+ *****************************************************************************/
+
+int
+ble_att_clt_tx_notify_mult(uint16_t conn_handle, struct os_mbuf *txom)
+{
+#if !NIMBLE_BLE_ATT_CLT_NOTIFY_MULT
+    return BLE_HS_ENOTSUP;
+#endif
+
+    struct os_mbuf *txom2;
+    uint16_t cid;
+    int rc;
+
+    if (ble_att_cmd_get(BLE_ATT_OP_NOTIFY_MULTI_REQ, 0, &txom2) == NULL) {
+        rc = BLE_HS_ENOMEM;
+        goto err;
+    }
+
+    os_mbuf_concat(txom2, txom);
+
+    cid = ble_eatt_get_available_chan_cid(conn_handle, BLE_GATT_OP_DUMMY);
+    rc = ble_att_tx(conn_handle, cid, txom2);
+    if (cid != BLE_L2CAP_CID_ATT) {
+        ble_eatt_release_chan(conn_handle, BLE_GATT_OP_DUMMY);
+    }
+
+err:
+    return rc;
+}
diff --git a/nimble/host/src/ble_att_priv.h b/nimble/host/src/ble_att_priv.h
index ac65250f9..2bc3da361 100644
--- a/nimble/host/src/ble_att_priv.h
+++ b/nimble/host/src/ble_att_priv.h
@@ -302,6 +302,7 @@ int ble_att_clt_tx_notify(uint16_t conn_handle, uint16_t 
handle,
 int ble_att_clt_tx_indicate(uint16_t conn_handle, uint16_t cid,
                             uint16_t handle, struct os_mbuf *txom);
 int ble_att_clt_rx_indicate(uint16_t conn_handle, uint16_t cid, struct os_mbuf 
**rxom);
+int ble_att_clt_tx_notify_mult(uint16_t conn_handle, struct os_mbuf *txom);
 
 #ifdef __cplusplus
 }
diff --git a/nimble/host/src/ble_gattc.c b/nimble/host/src/ble_gattc.c
index 2d42856b8..d40adfd3c 100644
--- a/nimble/host/src/ble_gattc.c
+++ b/nimble/host/src/ble_gattc.c
@@ -4413,6 +4413,139 @@ ble_gatts_notify(uint16_t conn_handle, uint16_t 
chr_val_handle)
     return rc;
 }
 
+int
+ble_gatts_notify_multiple_custom(uint16_t conn_handle,
+                                 uint16_t chr_count,
+                                 struct ble_gatt_notif *tuples)
+{
+#if !MYNEWT_VAL(BLE_GATT_NOTIFY_MULTIPLE)
+    return BLE_HS_ENOTSUP;
+#endif
+
+    int rc;
+    int i = 0;
+    uint16_t cur_chr_cnt = 0;
+    /* mtu = MTU - 1 octet (OP code) */
+    uint16_t mtu = ble_att_mtu(conn_handle) - 1;
+    struct os_mbuf *txom;
+    struct ble_hs_conn *conn;
+
+    txom = ble_hs_mbuf_att_pkt();
+    if (txom == NULL) {
+        return BLE_HS_ENOMEM;
+    }
+
+    conn = ble_hs_conn_find(conn_handle);
+    if (conn == NULL) {
+        return ENOTCONN;
+    }
+
+    /* 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;
+            }
+        }
+    }
+
+    /* 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;
+            }
+        }
+    }
+
+    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;
+            }
+        }
+
+        os_mbuf_append(txom, &tuples[i].handle, sizeof(uint16_t));
+        os_mbuf_append(txom, &tuples[i].value->om_len,
+                       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);
+    }
+
+done:
+    return rc;
+}
+
+int
+ble_gatts_notify_multiple(uint16_t conn_handle,
+                          uint8_t num_handles,
+                          const uint16_t *chr_val_handles)
+{
+#if !MYNEWT_VAL(BLE_GATT_NOTIFY_MULTIPLE)
+    return BLE_HS_ENOTSUP;
+#endif
+    int rc, 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 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;
+}
+
 /**
  * Deprecated. Should not be used. Use ble_gatts_notify instead.
  */
diff --git a/nimble/host/syscfg.yml b/nimble/host/syscfg.yml
index 558e31e8d..79a027d4b 100644
--- a/nimble/host/syscfg.yml
+++ b/nimble/host/syscfg.yml
@@ -282,6 +282,12 @@ syscfg.defs:
         description: >
             Enables sending and receiving of GATT indications. (0/1)
         value: 1
+    BLE_GATT_NOTIFY_MULTIPLE:
+        description: >
+            Enables sending and receiving of GATT multiple handle 
notifications. (0/1)
+        value: (MYNEWT_VAL_BLE_VERSION >= 52)
+        restrictions:
+            - '(BLE_VERSION >= 52) if 1'
 
     # GATT options.
     BLE_GATT_READ_MAX_ATTRS:
@@ -309,6 +315,7 @@ syscfg.defs:
         description: >
             Number of CoC channels allocated to EATT
         value: 0
+        restrictions: BLE_GATT_NOTIFY_MULTIPLE
 
     BLE_EATT_MTU:
         description: >
diff --git a/nimble/include/nimble/nimble_opt_auto.h 
b/nimble/include/nimble/nimble_opt_auto.h
index 55f621991..4663ae8c7 100644
--- a/nimble/include/nimble/nimble_opt_auto.h
+++ b/nimble/include/nimble/nimble_opt_auto.h
@@ -109,6 +109,9 @@ extern "C" {
 #define NIMBLE_BLE_ATT_CLT_INDICATE             \
     (MYNEWT_VAL(BLE_GATT_INDICATE))
 
+#undef NIMBLE_BLE_ATT_CLT_NOTIFY_MULT
+#define NIMBLE_BLE_ATT_CLT_NOTIFY_MULT          \
+    (MYNEWT_VAL(BLE_GATT_NOTIFY_MULTIPLE))
 /** Security manager settings. */
 
 #undef NIMBLE_BLE_SM

Reply via email to