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.
This commit is contained in:
Krzysztof Kopyściński
2024-02-13 15:45:50 +01:00
committed by Krzysztof Kopyściński
parent 4340db0c5c
commit f07a77489f
6 changed files with 239 additions and 0 deletions
+63
View File
@@ -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
+32
View File
@@ -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;
}
+1
View File
@@ -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
}
+133
View File
@@ -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.
*/
+7
View File
@@ -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: >
+3
View File
@@ -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