From 2795cb8849a1a59132a0e97ff5777eff91bdb003 Mon Sep 17 00:00:00 2001 From: Sumeet Singh Date: Wed, 14 Feb 2024 17:24:09 +0530 Subject: [PATCH] feat(nimble): read multiple variable length characteristics --- nimble/host/include/host/ble_gatt.h | 15 ++ nimble/host/src/ble_att.c | 1 + nimble/host/src/ble_att_clt.c | 21 ++- nimble/host/src/ble_att_priv.h | 3 +- nimble/host/src/ble_att_svr.c | 2 +- nimble/host/src/ble_gatt_priv.h | 2 +- nimble/host/src/ble_gattc.c | 137 ++++++++++++++++-- nimble/host/syscfg.yml | 5 + nimble/host/test/src/ble_att_clt_test.c | 4 +- nimble/include/nimble/nimble_opt_auto.h | 4 + .../examples/linux/include/syscfg/syscfg.h | 4 + .../linux_blemesh/include/syscfg/syscfg.h | 4 + .../examples/nuttx/include/syscfg/syscfg.h | 4 + porting/nimble/include/syscfg/syscfg.h | 4 + porting/npl/riot/include/syscfg/syscfg.h | 4 + 15 files changed, 190 insertions(+), 24 deletions(-) diff --git a/nimble/host/include/host/ble_gatt.h b/nimble/host/include/host/ble_gatt.h index 8684f16fa..c2facf299 100644 --- a/nimble/host/include/host/ble_gatt.h +++ b/nimble/host/include/host/ble_gatt.h @@ -468,6 +468,17 @@ typedef int ble_gatt_attr_fn(uint16_t conn_handle, struct ble_gatt_attr *attr, void *arg); +/** + * The host will free the attribute mbuf automatically after the callback is + * executed. The application can take ownership of the mbuf and prevent it + * from being freed by assigning NULL to attr->om. + */ +typedef int ble_gatt_attr_mult_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attrs, + uint8_t num_attrs, + void *arg); + /** * The host will free the attribute mbufs automatically after the callback is * executed. The application can take ownership of the mbufs and prevent them @@ -690,6 +701,10 @@ int ble_gattc_read_mult(uint16_t conn_handle, const uint16_t *handles, uint8_t num_handles, ble_gatt_attr_fn *cb, void *cb_arg); +int ble_gattc_read_mult_var(uint16_t conn_handle, const uint16_t *handles, + uint8_t num_handles, ble_gatt_attr_mult_fn *cb, + void *cb_arg); + /** * Initiates GATT procedure: Write Without Response. This function consumes * the supplied mbuf regardless of the outcome. diff --git a/nimble/host/src/ble_att.c b/nimble/host/src/ble_att.c index 3f1ec9a4a..91e770808 100644 --- a/nimble/host/src/ble_att.c +++ b/nimble/host/src/ble_att.c @@ -70,6 +70,7 @@ static const struct ble_att_rx_dispatch_entry ble_att_rx_dispatch[] = { { BLE_ATT_OP_INDICATE_REQ, ble_att_svr_rx_indicate }, { BLE_ATT_OP_INDICATE_RSP, ble_att_clt_rx_indicate }, { BLE_ATT_OP_READ_MULT_VAR_REQ, ble_att_svr_rx_read_mult_var }, + { BLE_ATT_OP_READ_MULT_VAR_RSP, ble_att_clt_rx_read_mult_var }, { BLE_ATT_OP_WRITE_CMD, ble_att_svr_rx_write_no_rsp }, { BLE_ATT_OP_SIGNED_WRITE_CMD, ble_att_svr_rx_signed_write }, }; diff --git a/nimble/host/src/ble_att_clt.c b/nimble/host/src/ble_att_clt.c index d83137768..2b62e27b8 100644 --- a/nimble/host/src/ble_att_clt.c +++ b/nimble/host/src/ble_att_clt.c @@ -537,7 +537,7 @@ ble_att_clt_rx_read_blob(uint16_t conn_handle, struct os_mbuf **rxom) *****************************************************************************/ int ble_att_clt_tx_read_mult(uint16_t conn_handle, const uint16_t *handles, - int num_handles) + int num_handles, bool variable) { #if !NIMBLE_BLE_ATT_CLT_READ_MULT return BLE_HS_ENOTSUP; @@ -546,12 +546,15 @@ ble_att_clt_tx_read_mult(uint16_t conn_handle, const uint16_t *handles, struct ble_att_read_mult_req *req; struct os_mbuf *txom; int i; + uint8_t op; if (num_handles < 1) { return BLE_HS_EINVAL; } - req = ble_att_cmd_get(BLE_ATT_OP_READ_MULT_REQ, + op = variable ? BLE_ATT_OP_READ_MULT_VAR_REQ : BLE_ATT_OP_READ_MULT_REQ; + + req = ble_att_cmd_get(op, sizeof(req->handles[0]) * num_handles, &txom); if (req == NULL) { @@ -573,7 +576,19 @@ ble_att_clt_rx_read_mult(uint16_t conn_handle, struct os_mbuf **rxom) #endif /* Pass the Attribute Value field to GATT. */ - ble_gattc_rx_read_mult_rsp(conn_handle, 0, rxom); + ble_gattc_rx_read_mult_rsp(conn_handle, 0, rxom, false); + return 0; +} + +int +ble_att_clt_rx_read_mult_var(uint16_t conn_handle, struct os_mbuf **rxom) +{ +#if !NIMBLE_BLE_ATT_CLT_READ_MULT_VAR + return BLE_HS_ENOTSUP; +#endif + + /* Pass the Attribute Value field to GATT. */ + ble_gattc_rx_read_mult_rsp(conn_handle, 0, rxom, true); return 0; } diff --git a/nimble/host/src/ble_att_priv.h b/nimble/host/src/ble_att_priv.h index 4b0bda179..0e723de92 100644 --- a/nimble/host/src/ble_att_priv.h +++ b/nimble/host/src/ble_att_priv.h @@ -273,8 +273,9 @@ int ble_att_clt_tx_read_blob(uint16_t conn_handle, uint16_t handle, uint16_t offset); int ble_att_clt_rx_read_blob(uint16_t conn_handle, struct os_mbuf **rxom); int ble_att_clt_tx_read_mult(uint16_t conn_handle, - const uint16_t *handles, int num_handles); + const uint16_t *handles, int num_handles, bool variable); int ble_att_clt_rx_read_mult(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_clt_rx_read_mult_var(uint16_t conn_handle, struct os_mbuf **rxom); int ble_att_clt_tx_read_type(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, const ble_uuid_t *uuid); int ble_att_clt_rx_read_type(uint16_t conn_handle, struct os_mbuf **rxom); diff --git a/nimble/host/src/ble_att_svr.c b/nimble/host/src/ble_att_svr.c index 7ff3b5569..3d600b831 100644 --- a/nimble/host/src/ble_att_svr.c +++ b/nimble/host/src/ble_att_svr.c @@ -1858,7 +1858,7 @@ done: int ble_att_svr_rx_read_mult_var(uint16_t conn_handle, struct os_mbuf **rxom) { -#if (!MYNEWT_VAL(BLE_ATT_SVR_READ_MULT) || (MYNEWT_VAL(BLE_VERSION) < 52)) +#if !MYNEWT_VAL(BLE_ATT_SVR_READ_MULT) return BLE_HS_ENOTSUP; #endif diff --git a/nimble/host/src/ble_gatt_priv.h b/nimble/host/src/ble_gatt_priv.h index d4c2bdd24..34c10ed16 100644 --- a/nimble/host/src/ble_gatt_priv.h +++ b/nimble/host/src/ble_gatt_priv.h @@ -137,7 +137,7 @@ void ble_gattc_rx_read_rsp(uint16_t conn_handle, int status, void ble_gattc_rx_read_blob_rsp(uint16_t conn_handle, int status, struct os_mbuf **rxom); void ble_gattc_rx_read_mult_rsp(uint16_t conn_handle, int status, - struct os_mbuf **rxom); + struct os_mbuf **rxom, bool variable); void ble_gattc_rx_read_group_type_adata( uint16_t conn_handle, struct ble_att_read_group_type_adata *adata); void ble_gattc_rx_read_group_type_complete(uint16_t conn_handle, int rc); diff --git a/nimble/host/src/ble_gattc.c b/nimble/host/src/ble_gattc.c index 6b157b969..3b9a5dfcf 100644 --- a/nimble/host/src/ble_gattc.c +++ b/nimble/host/src/ble_gattc.c @@ -94,11 +94,12 @@ #define BLE_GATT_OP_READ_UUID 8 #define BLE_GATT_OP_READ_LONG 9 #define BLE_GATT_OP_READ_MULT 10 -#define BLE_GATT_OP_WRITE 11 -#define BLE_GATT_OP_WRITE_LONG 12 -#define BLE_GATT_OP_WRITE_RELIABLE 13 -#define BLE_GATT_OP_INDICATE 14 -#define BLE_GATT_OP_CNT 15 +#define BLE_GATT_OP_READ_MULT_VAR 11 +#define BLE_GATT_OP_WRITE 12 +#define BLE_GATT_OP_WRITE_LONG 13 +#define BLE_GATT_OP_WRITE_RELIABLE 14 +#define BLE_GATT_OP_INDICATE 15 +#define BLE_GATT_OP_CNT 16 /** Procedure stalled due to resource exhaustion. */ #define BLE_GATTC_PROC_F_STALLED 0x01 @@ -189,7 +190,9 @@ struct ble_gattc_proc { struct { uint16_t handles[MYNEWT_VAL(BLE_GATT_READ_MAX_ATTRS)]; uint8_t num_handles; + bool variable; ble_gatt_attr_fn *cb; + ble_gatt_attr_mult_fn *cb_mult; void *cb_arg; } read_mult; @@ -586,7 +589,7 @@ ble_gattc_log_read_long(struct ble_gattc_proc *proc) } static void -ble_gattc_log_read_mult(const uint16_t *handles, uint8_t num_handles) +ble_gattc_log_read_mult(const uint16_t *handles, uint8_t num_handles, bool variable) { int i; @@ -3322,6 +3325,73 @@ done: * $read multiple * *****************************************************************************/ +static int +ble_gattc_read_mult_cb_var(struct ble_gattc_proc *proc, int status, + uint16_t att_handle, struct os_mbuf **om) +{ + struct ble_gatt_attr attr[proc->read_mult.num_handles]; + int rc; + int i; + uint16_t attr_len; + + if (proc->read_mult.cb_mult == NULL) { + return 0; + } + + memset(attr, 0, sizeof(*attr)); + + for (i = 0; i < proc->read_mult.num_handles; i++) { + attr[i].handle = proc->read_mult.handles[i]; + attr[i].offset = 0; + if (om == NULL || OS_MBUF_PKTLEN(*om) == 0) { + continue; + } + + *om = os_mbuf_pullup(*om, 2); + assert(*om); + + attr_len = get_le16((*om)->om_data); + + os_mbuf_adj(*om, 2); + + if (attr_len > BLE_ATT_ATTR_MAX_LEN) { + /*TODO Figure out what to do here */ + break; + } + + attr[i].om = os_msys_get_pkthdr(attr_len, 0); + if (!attr[i].om) { + /*TODO Figure out what to do here */ + break; + } + + rc = os_mbuf_appendfrom(attr[i].om, *om, 0, attr_len); + if (rc) { + /*TODO Figure out what to do here */ + break; + } + + os_mbuf_adj(*om, attr_len); + } + + /*FIXME Testing assert */ + assert(i == proc->read_mult.num_handles); + + proc->read_mult.cb_mult(proc->conn_handle, + ble_gattc_error(status, att_handle), &attr[0], + i, + proc->read_mult.cb_arg); + + for (i = 0; i < proc->read_mult.num_handles; i++) { + if (attr[i].om != NULL) { + os_mbuf_free_chain(attr[i].om); + } + } + + return 0; +} + + /** * Calls a read-multiple-characteristics proc's callback with the specified * parameters. If the proc has no callback, this function is a no-op. @@ -3344,6 +3414,10 @@ ble_gattc_read_mult_cb(struct ble_gattc_proc *proc, int status, STATS_INC(ble_gattc_stats, read_mult_fail); } + if (proc->read_mult.variable) { + return ble_gattc_read_mult_cb_var(proc, status, att_handle, om); + } + attr.handle = 0; attr.offset = 0; if (om == NULL) { @@ -3400,7 +3474,7 @@ ble_gattc_read_mult_tx(struct ble_gattc_proc *proc) int rc; rc = ble_att_clt_tx_read_mult(proc->conn_handle, proc->read_mult.handles, - proc->read_mult.num_handles); + proc->read_mult.num_handles, proc->read_mult.variable); if (rc != 0) { return rc; } @@ -3409,10 +3483,11 @@ ble_gattc_read_mult_tx(struct ble_gattc_proc *proc) } -int -ble_gattc_read_mult(uint16_t conn_handle, const uint16_t *handles, - uint8_t num_handles, ble_gatt_attr_fn *cb, - void *cb_arg) +static int +ble_gattc_read_mult_internal(uint16_t conn_handle, const uint16_t *handles, + uint8_t num_handles, bool variable, ble_gatt_attr_fn *cb, + ble_gatt_attr_mult_fn *cb_mult, + void *cb_arg) { #if !MYNEWT_VAL(BLE_GATT_READ_MULT) return BLE_HS_ENOTSUP; @@ -3436,14 +3511,20 @@ ble_gattc_read_mult(uint16_t conn_handle, const uint16_t *handles, goto done; } - proc->op = BLE_GATT_OP_READ_MULT; + if (variable) { + proc->op = BLE_GATT_OP_READ_MULT_VAR; + } else { + proc->op = BLE_GATT_OP_READ_MULT; + } proc->conn_handle = conn_handle; memcpy(proc->read_mult.handles, handles, num_handles * sizeof *handles); proc->read_mult.num_handles = num_handles; + proc->read_mult.variable = variable; proc->read_mult.cb = cb; + proc->read_mult.cb_mult = cb_mult; proc->read_mult.cb_arg = cb_arg; - ble_gattc_log_read_mult(handles, num_handles); + ble_gattc_log_read_mult(handles, num_handles, variable); rc = ble_gattc_read_mult_tx(proc); if (rc != 0) { goto done; @@ -3458,6 +3539,28 @@ done: return rc; } +int +ble_gattc_read_mult(uint16_t conn_handle, const uint16_t *handles, + uint8_t num_handles, ble_gatt_attr_fn *cb, + void *cb_arg) +{ + return ble_gattc_read_mult_internal(conn_handle, handles, + num_handles, false, cb, NULL, cb_arg); +} + +int +ble_gattc_read_mult_var(uint16_t conn_handle, const uint16_t *handles, + uint8_t num_handles, ble_gatt_attr_mult_fn *cb, + void *cb_arg) +{ +#if MYNEWT_VAL(BLE_GATT_READ_MULT_VAR) + return ble_gattc_read_mult_internal(conn_handle, handles, num_handles, + true, NULL, cb, cb_arg); +#else + return BLE_HS_ENOTSUP; +#endif +} + /***************************************************************************** * $write no response * *****************************************************************************/ @@ -4875,16 +4978,18 @@ ble_gattc_rx_read_blob_rsp(uint16_t conn_handle, int status, */ void ble_gattc_rx_read_mult_rsp(uint16_t conn_handle, int status, - struct os_mbuf **om) + struct os_mbuf **om, bool variable) { #if !NIMBLE_BLE_ATT_CLT_READ_MULT return; #endif struct ble_gattc_proc *proc; + uint8_t op; - proc = ble_gattc_extract_first_by_conn_op(conn_handle, - BLE_GATT_OP_READ_MULT); + op = variable ? BLE_GATT_OP_READ_MULT_VAR : BLE_GATT_OP_READ_MULT; + + proc = ble_gattc_extract_first_by_conn_op(conn_handle, op); if (proc != NULL) { ble_gattc_read_mult_cb(proc, status, 0, om); ble_gattc_process_status(proc, BLE_HS_EDONE); diff --git a/nimble/host/syscfg.yml b/nimble/host/syscfg.yml index cf35be243..eb1abdf41 100644 --- a/nimble/host/syscfg.yml +++ b/nimble/host/syscfg.yml @@ -244,6 +244,11 @@ syscfg.defs: Enables the Read Multiple Characteristic Values GATT procedure. (0/1) value: MYNEWT_VAL_BLE_ROLE_CENTRAL + BLE_GATT_READ_MULT_VAR: + description: > + Enables the Read Multiple Variable Characteristic Values GATT procedure. + (0/1) + value: MYNEWT_VAL_BLE_ROLE_CENTRAL BLE_GATT_WRITE_NO_RSP: description: > Enables the Write Without Response GATT procedure. (0/1) diff --git a/nimble/host/test/src/ble_att_clt_test.c b/nimble/host/test/src/ble_att_clt_test.c index 787d4bca3..0b6d55d31 100644 --- a/nimble/host/test/src/ble_att_clt_test.c +++ b/nimble/host/test/src/ble_att_clt_test.c @@ -380,7 +380,7 @@ TEST_CASE_SELF(ble_att_clt_test_tx_read_mult) conn_handle = ble_att_clt_test_misc_init(); /*** Success. */ - rc = ble_att_clt_tx_read_mult(conn_handle, ((uint16_t[]){ 1, 2 }), 2); + rc = ble_att_clt_tx_read_mult(conn_handle, ((uint16_t[]){ 1, 2 }), 2, false); TEST_ASSERT(rc == 0); om = ble_hs_test_util_prev_tx_dequeue_pullup(); @@ -392,7 +392,7 @@ TEST_CASE_SELF(ble_att_clt_test_tx_read_mult) TEST_ASSERT(get_le16(om->om_data + BLE_ATT_READ_MULT_REQ_BASE_SZ + 2) == 2); /*** Error: no handles. */ - rc = ble_att_clt_tx_read_mult(conn_handle, NULL, 0); + rc = ble_att_clt_tx_read_mult(conn_handle, NULL, 0, false); TEST_ASSERT(rc == BLE_HS_EINVAL); ble_hs_test_util_assert_mbufs_freed(NULL); diff --git a/nimble/include/nimble/nimble_opt_auto.h b/nimble/include/nimble/nimble_opt_auto.h index b9faee755..6326abf54 100644 --- a/nimble/include/nimble/nimble_opt_auto.h +++ b/nimble/include/nimble/nimble_opt_auto.h @@ -77,6 +77,10 @@ extern "C" { #define NIMBLE_BLE_ATT_CLT_READ_MULT \ (MYNEWT_VAL(BLE_GATT_READ_MULT)) +#undef NIMBLE_BLE_ATT_CLT_READ_MULT_VAR +#define NIMBLE_BLE_ATT_CLT_READ_MULT_VAR \ + (MYNEWT_VAL(BLE_GATT_READ_MULT_VAR)) + #undef NIMBLE_BLE_ATT_CLT_READ_GROUP_TYPE #define NIMBLE_BLE_ATT_CLT_READ_GROUP_TYPE \ (MYNEWT_VAL(BLE_GATT_DISC_ALL_SVCS)) diff --git a/porting/examples/linux/include/syscfg/syscfg.h b/porting/examples/linux/include/syscfg/syscfg.h index cbcb719c0..d089d7e60 100644 --- a/porting/examples/linux/include/syscfg/syscfg.h +++ b/porting/examples/linux/include/syscfg/syscfg.h @@ -631,6 +631,10 @@ #define MYNEWT_VAL_BLE_GATT_READ_MULT (MYNEWT_VAL_BLE_ROLE_CENTRAL) #endif +#ifndef MYNEWT_VAL_BLE_GATT_READ_MULT_VAR +#define MYNEWT_VAL_BLE_GATT_READ_MULT_VAR (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + #ifndef MYNEWT_VAL_BLE_GATT_READ_UUID #define MYNEWT_VAL_BLE_GATT_READ_UUID (MYNEWT_VAL_BLE_ROLE_CENTRAL) #endif diff --git a/porting/examples/linux_blemesh/include/syscfg/syscfg.h b/porting/examples/linux_blemesh/include/syscfg/syscfg.h index 1dfe0d2e4..1df8abaf8 100644 --- a/porting/examples/linux_blemesh/include/syscfg/syscfg.h +++ b/porting/examples/linux_blemesh/include/syscfg/syscfg.h @@ -632,6 +632,10 @@ #define MYNEWT_VAL_BLE_GATT_READ_MULT (MYNEWT_VAL_BLE_ROLE_CENTRAL) #endif +#ifndef MYNEWT_VAL_BLE_GATT_READ_MULT_VAR +#define MYNEWT_VAL_BLE_GATT_READ_MULT_VAR (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + #ifndef MYNEWT_VAL_BLE_GATT_READ_UUID #define MYNEWT_VAL_BLE_GATT_READ_UUID (MYNEWT_VAL_BLE_ROLE_CENTRAL) #endif diff --git a/porting/examples/nuttx/include/syscfg/syscfg.h b/porting/examples/nuttx/include/syscfg/syscfg.h index 55d38b66c..51f8c44bf 100644 --- a/porting/examples/nuttx/include/syscfg/syscfg.h +++ b/porting/examples/nuttx/include/syscfg/syscfg.h @@ -631,6 +631,10 @@ #define MYNEWT_VAL_BLE_GATT_READ_MULT (MYNEWT_VAL_BLE_ROLE_CENTRAL) #endif +#ifndef MYNEWT_VAL_BLE_GATT_READ_MULT_VAR +#define MYNEWT_VAL_BLE_GATT_READ_MULT_VAR (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + #ifndef MYNEWT_VAL_BLE_GATT_READ_UUID #define MYNEWT_VAL_BLE_GATT_READ_UUID (MYNEWT_VAL_BLE_ROLE_CENTRAL) #endif diff --git a/porting/nimble/include/syscfg/syscfg.h b/porting/nimble/include/syscfg/syscfg.h index 75a5e9ab1..cc8a60b37 100644 --- a/porting/nimble/include/syscfg/syscfg.h +++ b/porting/nimble/include/syscfg/syscfg.h @@ -634,6 +634,10 @@ #define MYNEWT_VAL_BLE_GATT_READ_MULT (MYNEWT_VAL_BLE_ROLE_CENTRAL) #endif +#ifndef MYNEWT_VAL_BLE_GATT_READ_MULT_VAR +#define MYNEWT_VAL_BLE_GATT_READ_MULT_VAR (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + #ifndef MYNEWT_VAL_BLE_GATT_READ_UUID #define MYNEWT_VAL_BLE_GATT_READ_UUID (MYNEWT_VAL_BLE_ROLE_CENTRAL) #endif diff --git a/porting/npl/riot/include/syscfg/syscfg.h b/porting/npl/riot/include/syscfg/syscfg.h index f44120796..c90322eaa 100644 --- a/porting/npl/riot/include/syscfg/syscfg.h +++ b/porting/npl/riot/include/syscfg/syscfg.h @@ -1502,6 +1502,10 @@ #define MYNEWT_VAL_BLE_GATT_READ_MULT (MYNEWT_VAL_BLE_ROLE_CENTRAL) #endif +#ifndef MYNEWT_VAL_BLE_GATT_READ_MULT_VAR +#define MYNEWT_VAL_BLE_GATT_READ_MULT_VAR (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + #ifndef MYNEWT_VAL_BLE_GATT_READ_UUID #define MYNEWT_VAL_BLE_GATT_READ_UUID (MYNEWT_VAL_BLE_ROLE_CENTRAL) #endif