From a6c1180ea2fe2d49b7cb1dcfd0aa9df6fad95169 Mon Sep 17 00:00:00 2001 From: Roshan Bangar Date: Mon, 30 Oct 2023 14:32:03 +0530 Subject: [PATCH] feature : Gatt Caching --- nimble/host/include/host/ble_att.h | 9 + nimble/host/include/host/ble_gatt.h | 42 + .../gatt/include/services/gatt/ble_svc_gatt.h | 13 + nimble/host/services/gatt/src/ble_svc_gatt.c | 131 +- nimble/host/src/ble_att.c | 1 + nimble/host/src/ble_att_priv.h | 7 + nimble/host/src/ble_att_svr.c | 426 ++++ nimble/host/src/ble_gap.c | 31 + nimble/host/src/ble_gatt_priv.h | 24 + nimble/host/src/ble_gattc.c | 107 + nimble/host/src/ble_gattc_cache.c | 728 +++++++ nimble/host/src/ble_gattc_cache_conn.c | 1794 +++++++++++++++++ nimble/host/src/ble_gattc_cache_priv.h | 184 ++ nimble/host/src/ble_gatts.c | 228 ++- nimble/host/src/ble_hs.c | 5 + nimble/host/src/ble_hs_priv.h | 3 + nimble/host/src/ble_sm_alg.c | 4 +- nimble/host/src/ble_sm_priv.h | 2 + porting/nimble/include/nimble/storage_port.h | 47 + 19 files changed, 3777 insertions(+), 9 deletions(-) create mode 100644 nimble/host/src/ble_gattc_cache.c create mode 100644 nimble/host/src/ble_gattc_cache_conn.c create mode 100644 nimble/host/src/ble_gattc_cache_priv.h create mode 100644 porting/nimble/include/nimble/storage_port.h diff --git a/nimble/host/include/host/ble_att.h b/nimble/host/include/host/ble_att.h index cb98eab47..3899d0028 100644 --- a/nimble/host/include/host/ble_att.h +++ b/nimble/host/include/host/ble_att.h @@ -109,6 +109,8 @@ struct os_mbuf; /**Insufficient Resources to complete the request. */ #define BLE_ATT_ERR_INSUFFICIENT_RES 0x11 +#define BLE_ATT_ERR_DB_OUT_OF_SYNC 0x12 +#define BLE_ATT_ERR_VALUE_NOT_ALLOWED 0x13 /** @} */ @@ -186,6 +188,13 @@ struct os_mbuf; /** Execute Write Response. */ #define BLE_ATT_OP_EXEC_WRITE_RSP 0x19 + +/** Read Multiple Variable Lenght Request */ +#define BLE_ATT_OP_READ_MULT_VAR_REQ 0x20 + +/** Read Multiple Variable Lenght Response */ +#define BLE_ATT_OP_READ_MULT_VAR_RSP 0x21 + /** Notify Request. */ #define BLE_ATT_OP_NOTIFY_REQ 0x1b diff --git a/nimble/host/include/host/ble_gatt.h b/nimble/host/include/host/ble_gatt.h index 41ca814d8..1df1e6963 100644 --- a/nimble/host/include/host/ble_gatt.h +++ b/nimble/host/include/host/ble_gatt.h @@ -1101,6 +1101,48 @@ int ble_gatts_reset(void); */ int ble_gatts_start(void); +/** + * Saves Client Supported Features for specified connection. + * + * @param conn_handle Connection handle identifying connection for + * which Client Supported Features should be saved + * @param om The mbuf chain to set value from. + * + * @return 0 on success; + * BLE_HS_ENOTCONN if no matching connection + * was found + * BLE_HS_EINVAL if supplied buffer is empty or + * if any Client Supported Feature was + * attempted to be disabled. + * A BLE host core return code on unexpected + * error. + * + */ +int ble_gatts_peer_cl_sup_feat_update(uint16_t conn_handle, + struct os_mbuf *om); + +/** + * Gets Client Supported Features for specified connection. + * + * @param conn_handle Connection handle identifying connection for + * which Client Supported Features should be saved + * @param out_supported_feat Client supported features to be returned. + * + * @return 0 on success; + * BLE_HS_ENOTCONN if no matching connection + * was found + * BLE_HS_EINVAL if supplied buffer is empty or + * if any Client Supported Feature was + * attempted to be disabled. + * A BLE host core return code on unexpected + * error. + * + */ +int ble_gatts_peer_cl_sup_feat_get(uint16_t conn_handle, uint8_t *out_supported_feat, uint8_t len); + +#if MYNEWT_VAL(BLE_GATT_CACHING) +int ble_gatts_calculate_hash(uint8_t *out_hash_key); +#endif #ifdef __cplusplus } #endif diff --git a/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h b/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h index 06c44784a..3701121e5 100644 --- a/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h +++ b/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h @@ -21,6 +21,7 @@ #define H_BLE_SVC_GATT_ #include +#include "syscfg/syscfg.h" #ifdef __cplusplus extern "C" { @@ -29,7 +30,19 @@ extern "C" { struct ble_hs_cfg; #define BLE_SVC_GATT_CHR_SERVICE_CHANGED_UUID16 0x2a05 +#define BLE_SVC_GATT_CHR_SERVER_SUPPORTED_FEAT_UUID16 0x2b3a +#define BLE_SVC_GATT_CHR_CLIENT_SUPPORTED_FEAT_UUID16 0x2b29 +#if MYNEWT_VAL(BLE_GATT_CACHING) +#define BLE_SVC_GATT_CHR_DATABASE_HASH_UUID16 0x2b2a + +uint16_t ble_svc_gatt_changed_handle(); +uint16_t ble_svc_gatt_hash_handle(); +uint16_t ble_svc_gatt_csf_handle(); +uint8_t ble_svc_gatt_get_csfs(); +#endif + +uint8_t ble_svc_gatt_get_local_cl_supported_feat(void); void ble_svc_gatt_changed(uint16_t start_handle, uint16_t end_handle); void ble_svc_gatt_init(void); diff --git a/nimble/host/services/gatt/src/ble_svc_gatt.c b/nimble/host/services/gatt/src/ble_svc_gatt.c index 78b4a0683..f32b72944 100644 --- a/nimble/host/services/gatt/src/ble_svc_gatt.c +++ b/nimble/host/services/gatt/src/ble_svc_gatt.c @@ -23,14 +23,38 @@ #include "host/ble_hs.h" #include "services/gatt/ble_svc_gatt.h" +#if MYNEWT_VAL(BLE_GATT_CACHING) +static uint16_t ble_svc_gatt_db_hash_handle; +static uint16_t ble_svc_gatt_client_supp_feature_handle; +#endif + static uint16_t ble_svc_gatt_changed_val_handle; static uint16_t ble_svc_gatt_start_handle; static uint16_t ble_svc_gatt_end_handle; +/* Server supported features */ +#define BLE_SVC_GATT_SRV_SUP_FEAT_EATT_BIT (0x00) + +/* Client supported features */ +#define BLE_SVC_GATT_CLI_SUP_FEAT_ROBUST_CATCHING_BIT (0x00) +#define BLE_SVC_GATT_CLI_SUP_FEAT_EATT_BIT (0x01) +#define BLE_SVC_GATT_CLI_SUP_FEAT_MULT_NTF_BIT (0x02) + +static uint8_t ble_svc_gatt_local_srv_sup_feat = 0; +static uint8_t ble_svc_gatt_local_cl_sup_feat = 0; + static int ble_svc_gatt_access(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); +static int +ble_svc_gatt_srv_sup_feat_access(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static int +ble_svc_gatt_cl_sup_feat_access(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + static const struct ble_gatt_svc_def ble_svc_gatt_defs[] = { { /*** Service: GATT */ @@ -41,7 +65,29 @@ static const struct ble_gatt_svc_def ble_svc_gatt_defs[] = { .access_cb = ble_svc_gatt_access, .val_handle = &ble_svc_gatt_changed_val_handle, .flags = BLE_GATT_CHR_F_INDICATE, - }, { + }, + { + .uuid = BLE_UUID16_DECLARE(BLE_SVC_GATT_CHR_SERVER_SUPPORTED_FEAT_UUID16), + .access_cb = ble_svc_gatt_srv_sup_feat_access, + .flags = BLE_GATT_CHR_F_READ, + }, + { + .uuid = BLE_UUID16_DECLARE(BLE_SVC_GATT_CHR_CLIENT_SUPPORTED_FEAT_UUID16), + .access_cb = ble_svc_gatt_cl_sup_feat_access, +#if MYNEWT_VAL(BLE_GATT_CACHING) + .val_handle = &ble_svc_gatt_client_supp_feature_handle, +#endif + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + }, +#if MYNEWT_VAL(BLE_GATT_CACHING) + { + .uuid = BLE_UUID16_DECLARE(BLE_SVC_GATT_CHR_DATABASE_HASH_UUID16), + .access_cb = ble_svc_gatt_access, + .val_handle = &ble_svc_gatt_db_hash_handle, + .flags = BLE_GATT_CHR_F_READ, + }, +#endif + { 0, /* No more characteristics in this service. */ } }, }, @@ -51,6 +97,41 @@ static const struct ble_gatt_svc_def ble_svc_gatt_defs[] = { }, }; +static int +ble_svc_gatt_srv_sup_feat_access(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + if (ctxt->op != BLE_GATT_ACCESS_OP_READ_CHR) { + return BLE_ATT_ERR_WRITE_NOT_PERMITTED; + } + + os_mbuf_append(ctxt->om, &ble_svc_gatt_local_srv_sup_feat, sizeof(ble_svc_gatt_local_srv_sup_feat)); + + return 0; +} + +static int +ble_svc_gatt_cl_sup_feat_access(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + uint8_t supported_feat; + int rc; + + if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = ble_gatts_peer_cl_sup_feat_get(conn_handle, &supported_feat, 1); + if (rc != 0) { + return BLE_ATT_ERR_UNLIKELY; + } + os_mbuf_append(ctxt->om, &supported_feat, sizeof(supported_feat)); + return 0; + } + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + return ble_gatts_peer_cl_sup_feat_update(conn_handle, ctxt->om); + } + + return 0; +} + static int ble_svc_gatt_access(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) @@ -63,20 +144,53 @@ ble_svc_gatt_access(uint16_t conn_handle, uint16_t attr_handle, * Therefore, this callback should only get called during an attempt to * read the characteristic. */ +#if !MYNEWT_VAL(BLE_GATT_CACHING) assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); - assert(ctxt->chr == &ble_svc_gatt_defs[0].characteristics[0]); u8p = os_mbuf_extend(ctxt->om, 4); if (u8p == NULL) { return BLE_HS_ENOMEM; } + + assert(ctxt->chr == &ble_svc_gatt_defs[0].characteristics[0]); + put_le16(u8p + 0, ble_svc_gatt_start_handle); put_le16(u8p + 2, ble_svc_gatt_end_handle); +#else + int rc; + + if(ctxt->chr == &ble_svc_gatt_defs[0].characteristics[0]){ + u8p = os_mbuf_extend(ctxt->om, 4); + if (u8p == NULL) { + return BLE_HS_ENOMEM; + } + put_le16(u8p + 0, ble_svc_gatt_start_handle); + put_le16(u8p + 2, ble_svc_gatt_end_handle); + } + if (attr_handle == ble_svc_gatt_db_hash_handle) { + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + uint8_t database_hash[16] = {0}; + rc = ble_gatts_calculate_hash(database_hash); + if(rc != 0) { + return BLE_ATT_ERR_UNLIKELY; + } + rc = os_mbuf_append(ctxt->om, database_hash, sizeof(database_hash)); + if(rc != 0) { + return BLE_ATT_ERR_UNLIKELY; + } + } +#endif return 0; } +uint8_t +ble_svc_gatt_get_local_cl_supported_feat(void) +{ + return ble_svc_gatt_local_cl_sup_feat; +} + /** * Indicates a change in attribute assignment to all subscribed peers. * Unconnected bonded peers receive an indication when they next connect. @@ -92,6 +206,19 @@ ble_svc_gatt_changed(uint16_t start_handle, uint16_t end_handle) ble_gatts_chr_updated(ble_svc_gatt_changed_val_handle); } +#if MYNEWT_VAL(BLE_GATT_CACHING) +uint16_t ble_svc_gatt_changed_handle() { + return ble_svc_gatt_changed_val_handle; +} + +uint16_t ble_svc_gatt_hash_handle() { + return ble_svc_gatt_db_hash_handle; +} +uint16_t ble_svc_gatt_csf_handle() { + return ble_svc_gatt_client_supp_feature_handle; +} +#endif + void ble_svc_gatt_init(void) { diff --git a/nimble/host/src/ble_att.c b/nimble/host/src/ble_att.c index 436a55077..f884d3885 100644 --- a/nimble/host/src/ble_att.c +++ b/nimble/host/src/ble_att.c @@ -69,6 +69,7 @@ static const struct ble_att_rx_dispatch_entry ble_att_rx_dispatch[] = { { BLE_ATT_OP_NOTIFY_REQ, ble_att_svr_rx_notify }, { 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_WRITE_CMD, ble_att_svr_rx_write_no_rsp }, }; diff --git a/nimble/host/src/ble_att_priv.h b/nimble/host/src/ble_att_priv.h index 70f2b428f..0e4353224 100644 --- a/nimble/host/src/ble_att_priv.h +++ b/nimble/host/src/ble_att_priv.h @@ -149,6 +149,11 @@ int ble_att_svr_register(const ble_uuid_t *uuid, uint8_t flags, #if MYNEWT_VAL(BLE_DYNAMIC_SERVICE) int ble_att_svr_deregister(uint16_t start_handle, uint16_t end_group_handle); #endif +#if MYNEWT_VAL(BLE_GATT_CACHING) +int ble_att_get_database_size(int *out_size); +int ble_att_fill_database_info(uint8_t *out_data); +#endif + struct ble_att_svr_entry { STAILQ_ENTRY(ble_att_svr_entry) ha_next; @@ -200,6 +205,8 @@ int ble_att_svr_rx_read(uint16_t conn_handle, struct os_mbuf **rxom); int ble_att_svr_rx_read_blob(uint16_t conn_handle, struct os_mbuf **rxom); +int ble_att_svr_rx_read_mult_var(uint16_t conn_handle, + struct os_mbuf **rxom); int ble_att_svr_rx_read_mult(uint16_t conn_handle, struct os_mbuf **rxom); int ble_att_svr_rx_write(uint16_t conn_handle, diff --git a/nimble/host/src/ble_att_svr.c b/nimble/host/src/ble_att_svr.c index da48f8bbf..d167b2adc 100644 --- a/nimble/host/src/ble_att_svr.c +++ b/nimble/host/src/ble_att_svr.c @@ -1308,6 +1308,37 @@ done: return rc; } +#if MYNEWT_VAL(BLE_GATT_CACHING) +static void ble_att_svr_make_conn_aware(uint16_t conn_handle) { + struct ble_hs_conn *conn; + struct ble_hs_conn_addrs addrs; + int i = 0; + + conn = ble_hs_conn_find_assert(conn_handle); + conn->bhc_gatt_svr.aware_state = true; + + ble_hs_conn_addrs(conn, &addrs); + for(i = 0; i < MYNEWT_VAL(BLE_STORE_MAX_BONDS); i++) { + if(memcmp(ble_gatts_conn_aware_states[i].peer_id_addr, + addrs.peer_id_addr.val, sizeof addrs.peer_id_addr.val)) { + ble_gatts_conn_aware_states[i].aware = true; + } + } +} + +static bool ble_att_svr_check_conn_aware(uint16_t conn_handle) { + struct ble_hs_conn *conn; + conn = ble_hs_conn_find_assert(conn_handle); + return conn->bhc_gatt_svr.aware_state; +} + +static uint8_t * ble_att_svr_get_csfs(uint16_t conn_handle) { + struct ble_hs_conn *conn; + conn = ble_hs_conn_find_assert(conn_handle); + return conn->bhc_gatt_svr.peer_cl_sup_feat; +} +#endif + static int ble_att_svr_build_read_type_rsp(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, @@ -1476,6 +1507,15 @@ ble_att_svr_rx_read_type(uint16_t conn_handle, struct os_mbuf **rxom) goto done; } +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_hs_lock(); + ble_att_svr_make_conn_aware(conn_handle); + ble_hs_unlock(); + + if(rc != 0) { + goto done; + } +#endif rc = ble_att_svr_build_read_type_rsp(conn_handle, start_handle, end_handle, &uuid.u, rxom, &txom, &att_err, &err_handle); @@ -1518,6 +1558,20 @@ ble_att_svr_rx_read(uint16_t conn_handle, struct os_mbuf **rxom) err_handle = le16toh(req->barq_handle); +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_hs_lock(); + if((ble_att_svr_get_csfs(conn_handle)[0] & 1) + && ble_svc_gatt_csf_handle() != err_handle ) { + if (!ble_att_svr_check_conn_aware(conn_handle)) { + att_err = BLE_ATT_ERR_DB_OUT_OF_SYNC; + rc = BLE_HS_EREJECT; + ble_att_svr_make_conn_aware(conn_handle); + ble_hs_unlock(); + goto done; + } + } + ble_hs_unlock(); +#endif /* Just reuse the request buffer for the response. */ txom = *rxom; *rxom = NULL; @@ -1568,6 +1622,21 @@ ble_att_svr_rx_read_blob(uint16_t conn_handle, struct os_mbuf **rxom) err_handle = le16toh(req->babq_handle); offset = le16toh(req->babq_offset); +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_hs_lock(); + if((ble_att_svr_get_csfs(conn_handle)[0] & 1) + && ble_svc_gatt_csf_handle() != err_handle ) { + if (!ble_att_svr_check_conn_aware(conn_handle)) { + att_err = BLE_ATT_ERR_DB_OUT_OF_SYNC; + rc = BLE_HS_EREJECT; + ble_att_svr_make_conn_aware(conn_handle); + ble_hs_unlock(); + goto done; + } + } + ble_hs_unlock(); +#endif + /* Just reuse the request buffer for the response. */ txom = *rxom; *rxom = NULL; @@ -1671,13 +1740,156 @@ ble_att_svr_rx_read_mult(uint16_t conn_handle, struct os_mbuf **rxom) err_handle = 0; att_err = 0; +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_hs_lock(); + if((ble_att_svr_get_csfs(conn_handle)[0] & 1) + && ble_svc_gatt_csf_handle() != err_handle ) { + if (!ble_att_svr_check_conn_aware(conn_handle)) { + att_err = BLE_ATT_ERR_DB_OUT_OF_SYNC; + rc = BLE_HS_EREJECT; + ble_att_svr_make_conn_aware(conn_handle); + ble_hs_unlock(); + goto done; + } + } + ble_hs_unlock(); +#endif + rc = ble_att_svr_build_read_mult_rsp(conn_handle, rxom, &txom, &att_err, &err_handle); +#if MYNEWT_VAL(BLE_GATT_CACHING) + done : +#endif return ble_att_svr_tx_rsp(conn_handle, rc, txom, BLE_ATT_OP_READ_MULT_REQ, att_err, err_handle); } +static int +ble_att_svr_build_read_mult_rsp_var(uint16_t conn_handle, + struct os_mbuf **rxom, + struct os_mbuf **out_txom, + uint8_t *att_err, + uint16_t *err_handle) +{ + struct os_mbuf *txom; + uint16_t handle; + uint16_t mtu; + uint16_t tuple_len; + struct os_mbuf *tmp = NULL; + int rc; + + mtu = ble_att_mtu(conn_handle); + + rc = ble_att_svr_pkt(rxom, &txom, att_err); + if (rc != 0) { + *err_handle = 0; + goto done; + } + + if (ble_att_cmd_prepare(BLE_ATT_OP_READ_MULT_VAR_RSP, 0, txom) == NULL) { + *att_err = BLE_ATT_ERR_INSUFFICIENT_RES; + *err_handle = 0; + rc = BLE_HS_ENOMEM; + goto done; + } + + tmp = os_msys_get_pkthdr(2, 0); + + /* Iterate through requested handles, reading the corresponding attribute + * for each. Stop when there are no more handles to process, or the + * response is full. + */ + while (OS_MBUF_PKTLEN(*rxom) >= 2 && OS_MBUF_PKTLEN(txom) < mtu) { + /* Ensure the full 16-bit handle is contiguous at the start of the + * mbuf. + */ + rc = ble_att_svr_pullup_req_base(rxom, 2, att_err); + if (rc != 0) { + *err_handle = 0; + goto done; + } + + /* Extract the 16-bit handle and strip it from the front of the + * mbuf. + */ + handle = get_le16((*rxom)->om_data); + os_mbuf_adj(*rxom, 2); + + rc = ble_att_svr_read_handle(conn_handle, handle, 0, tmp, att_err); + if (rc != 0) { + *err_handle = handle; + goto done; + } + tuple_len = OS_MBUF_PKTLEN(tmp); + rc = os_mbuf_append(txom, &tuple_len, sizeof(tuple_len)); + if (rc != 0) { + *err_handle = handle; + goto done; + } + if (tuple_len != 0) { + rc = os_mbuf_appendfrom(txom, tmp, 0, tuple_len); + if (rc != 0) { + *err_handle = handle; + goto done; + } + os_mbuf_adj(tmp, tuple_len); + } + } + rc = 0; + +done: + + if (tmp) { + os_mbuf_free_chain(tmp); + } + *out_txom = txom; + return rc; +} + +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)) + return BLE_HS_ENOTSUP; +#endif + + struct os_mbuf *txom; + uint16_t err_handle; + uint8_t att_err; + int rc; + + /* Initialize some values in case of early error. */ + txom = NULL; + err_handle = 0; + att_err = 0; + +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_hs_lock(); + if((ble_att_svr_get_csfs(conn_handle)[0] & 1) + && ble_svc_gatt_csf_handle() != err_handle ) { + if (!ble_att_svr_check_conn_aware(conn_handle)) { + att_err = BLE_ATT_ERR_DB_OUT_OF_SYNC; + rc = BLE_HS_EREJECT; + ble_att_svr_make_conn_aware(conn_handle); + ble_hs_unlock(); + goto done; + } + } + ble_hs_unlock(); +#endif + + rc = ble_att_svr_build_read_mult_rsp_var(conn_handle, rxom, &txom, &att_err, + &err_handle); + +#if MYNEWT_VAL(BLE_GATT_CACHING) +done: +#endif + return ble_att_svr_tx_rsp(conn_handle, rc, txom, + BLE_ATT_OP_READ_MULT_VAR_REQ, + att_err, err_handle); +} + static int ble_att_svr_is_valid_read_group_type(const ble_uuid_t *uuid) { @@ -1958,6 +2170,15 @@ ble_att_svr_rx_read_group_type(uint16_t conn_handle, struct os_mbuf **rxom) goto done; } +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_hs_lock(); + ble_att_svr_make_conn_aware(conn_handle); + ble_hs_unlock(); + if(rc != 0) { + goto done; + } +#endif + om_uuid_len = OS_MBUF_PKTHDR(*rxom)->omp_len - sizeof(*req); rc = ble_uuid_init_from_att_mbuf(&uuid, *rxom, sizeof(*req), om_uuid_len); if (rc != 0) { @@ -2046,6 +2267,21 @@ ble_att_svr_rx_write(uint16_t conn_handle, struct os_mbuf **rxom) handle = le16toh(req->bawq_handle); +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_hs_lock(); + if((ble_att_svr_get_csfs(conn_handle)[0] & 1) + && ble_svc_gatt_csf_handle() != handle) { + if (!ble_att_svr_check_conn_aware(conn_handle)) { + att_err = BLE_ATT_ERR_DB_OUT_OF_SYNC; + rc = BLE_HS_EREJECT; + ble_att_svr_make_conn_aware(conn_handle); + ble_hs_unlock(); + goto done; + } + } + ble_hs_unlock(); +#endif + /* Allocate the write response. This must be done prior to processing the * request. See the note at the top of this file for details. */ @@ -2077,6 +2313,16 @@ ble_att_svr_rx_write_no_rsp(uint16_t conn_handle, struct os_mbuf **rxom) return BLE_HS_ENOTSUP; #endif +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_hs_lock(); + if ((ble_att_svr_get_csfs(conn_handle)[0] & 1) && + !ble_att_svr_check_conn_aware(conn_handle)) { + ble_hs_unlock(); + return BLE_HS_EREJECT; + } + ble_hs_unlock(); +#endif + struct ble_att_write_req *req; uint8_t att_err; uint16_t handle; @@ -2388,6 +2634,21 @@ ble_att_svr_rx_prep_write(uint16_t conn_handle, struct os_mbuf **rxom) err_handle = le16toh(req->bapc_handle); +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_hs_lock(); + if((ble_att_svr_get_csfs(conn_handle)[0] & 1) + && ble_svc_gatt_csf_handle() != err_handle ) { + if (!ble_att_svr_check_conn_aware(conn_handle)) { + att_err = BLE_ATT_ERR_DB_OUT_OF_SYNC; + rc = BLE_HS_EREJECT; + ble_att_svr_make_conn_aware(conn_handle); + ble_hs_unlock(); + goto done; + } + } + ble_hs_unlock(); +#endif + attr_entry = ble_att_svr_find_by_handle(le16toh(req->bapc_handle)); /* A prepare write request gets rejected for the following reasons: @@ -2461,6 +2722,21 @@ ble_att_svr_rx_exec_write(uint16_t conn_handle, struct os_mbuf **rxom) txom = NULL; err_handle = 0; +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_hs_lock(); + if((ble_att_svr_get_csfs(conn_handle)[0] & 1) + && ble_svc_gatt_csf_handle() != err_handle ) { + if (!ble_att_svr_check_conn_aware(conn_handle)) { + att_err = BLE_ATT_ERR_DB_OUT_OF_SYNC; + rc = BLE_HS_EREJECT; + ble_att_svr_make_conn_aware(conn_handle); + ble_hs_unlock(); + goto done; + } + } + ble_hs_unlock(); +#endif + rc = ble_att_svr_pullup_req_base(rxom, sizeof(*req), &att_err); if (rc != 0) { flags = 0; @@ -2820,4 +3096,154 @@ ble_att_svr_init(void) return 0; } +/* Defined in Core spec v5.4 VOL 3 Part G 7.3.1 */ +#if MYNEWT_VAL(BLE_GATT_CACHING) +int ble_att_get_database_size(int *out_size) +{ + struct ble_att_svr_entry *entry; + ble_uuid16_t *uuid; + ble_uuid_any_t service_uuid; + uint8_t att_error; + int size = 0; + int rc; + + for (entry = STAILQ_FIRST(&ble_att_svr_list); + entry != NULL; + entry = STAILQ_NEXT(entry, ha_next)) { + uuid = (ble_uuid16_t*)entry->ha_uuid; + + if(uuid->value == BLE_ATT_UUID_PRIMARY_SERVICE || + uuid->value == BLE_ATT_UUID_SECONDARY_SERVICE) { + rc = ble_att_svr_service_uuid(entry, &service_uuid, &att_error); + if(rc != 0) { + return rc; + } + /* handle (2 bytes) + type(2 bytes) + uuid (variable) */ + size += (4 + ble_uuid_length(&service_uuid.u)); + } + else if(uuid->value == BLE_ATT_UUID_INCLUDE) { + rc = ble_att_svr_service_uuid(entry, &service_uuid, &att_error); + if(rc != 0) { + return rc; + } + + /* handle + type + inc_svc_att_handle + end_grp_handle + uuid */ + size += (8 + ble_uuid_length(&service_uuid.u)); + } + else if(uuid->value == BLE_ATT_UUID_CHARACTERISTIC) { + + /* uuid is stored in the value attribute */ + entry = STAILQ_NEXT(entry, ha_next); + /* handle(2 bytes) + type(2 bytes) + properties(1 byte) + + val_handle(2 bytes) + uuid */ + size += (7 + ble_uuid_length(entry->ha_uuid)); + } + else if(uuid->value == 0x2901 || + uuid->value == 0x2902 || + uuid->value == 0x2903 || + uuid->value == 0x2904 || + uuid->value == 0x2905) { + /* handle + type */ + size += 4; + } + else if(uuid->value == 0x2901) { + /* handle + type + value(2 bytes) */ + size += 6; + } + } + *out_size = size; + return 0; +} + +int ble_att_fill_database_info(uint8_t *out_data) +{ + struct ble_att_svr_entry *entry; + ble_uuid16_t *uuid; + ble_uuid_any_t service_uuid; + uint8_t att_error; + uint8_t *data; + uint8_t val[20]; + uint16_t attr_len; + int rc; + + data = out_data; + for (entry = STAILQ_FIRST(&ble_att_svr_list); + entry != NULL; + entry = STAILQ_NEXT(entry, ha_next)) { + uuid = BLE_UUID16(entry->ha_uuid); + if(uuid->value == BLE_ATT_UUID_PRIMARY_SERVICE || + uuid->value == BLE_ATT_UUID_SECONDARY_SERVICE) { + rc = ble_att_svr_service_uuid(entry, &service_uuid, &att_error); + if(rc != 0) { + return rc; + } + /* handle (2 bytes) + type(2 bytes) + uuid (variable) */ + put_le16(data, entry->ha_handle_id); + uuid = BLE_UUID16(entry->ha_uuid); + put_le16(data + 2, uuid->value); + ble_uuid_flat(&service_uuid.u, data + 4); + data += (4 + ble_uuid_length(&service_uuid.u)); + } + else if(uuid->value == BLE_ATT_UUID_INCLUDE) { + rc = ble_att_svr_read_flat(BLE_HS_CONN_HANDLE_NONE, + entry, 0, sizeof(val), val, + &attr_len, &att_error); + if(rc != 0) { + return rc; + } + + /* handle + type + inc_svc_att_handle + end_grp_handle + uuid */ + put_le16(data, entry->ha_handle_id); + uuid = BLE_UUID16(entry->ha_uuid); + put_le16(data + 2, uuid->value); + /* the data is already in LE format */ + memcpy(data + 4, val, attr_len); + data += (4 + attr_len); + } + else if(uuid->value == BLE_ATT_UUID_CHARACTERISTIC) { + + /* handle(2 bytes) + type(2 bytes) + properties(1 byte) + + val_handle(2 bytes) + uuid */ + put_le16(data, entry->ha_handle_id); + uuid = BLE_UUID16(entry->ha_uuid); + put_le16(data + 2, uuid->value); + + rc = ble_att_svr_read_flat(BLE_HS_CONN_HANDLE_NONE, + entry, 0, sizeof(val), val, + &attr_len, &att_error); + if(rc != 0) { + return rc; + } + memcpy(data + 4, val, attr_len); + data += (4 + attr_len); + } + else if(uuid->value == 0x2901 || + uuid->value == 0x2902 || + uuid->value == 0x2903 || + uuid->value == 0x2904 || + uuid->value == 0x2905) { + /* handle + type */ + put_le16(data, entry->ha_handle_id); + uuid = BLE_UUID16(entry->ha_uuid); + put_le16(data + 2, uuid->value); + data += 4; + } + else if(uuid->value == 0x2900) { + /* handle + type + value(2 bytes) */ + put_le16(data, entry->ha_handle_id); + uuid = BLE_UUID16(entry->ha_uuid); + put_le16(data + 2, uuid->value); + rc = ble_att_svr_read_flat(BLE_HS_CONN_HANDLE_NONE, + entry, 0, sizeof(val), val, + &attr_len, &att_error); + if(rc != 0) { + return rc; + } + memcpy(data + 4, val, attr_len); + data += (4 + attr_len); + } + } + return 0; +} +#endif #endif diff --git a/nimble/host/src/ble_gap.c b/nimble/host/src/ble_gap.c index 40b5c6a75..b3fd1851d 100644 --- a/nimble/host/src/ble_gap.c +++ b/nimble/host/src/ble_gap.c @@ -27,6 +27,9 @@ #include "ble_hs_priv.h" #include "ble_gap_priv.h" #include "ble_hs_resolv_priv.h" +#if MYNEWT_VAL(BLE_GATT_CACHING) +#include "ble_gattc_cache_priv.h" +#endif #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) @@ -1378,6 +1381,9 @@ ble_gap_conn_broken(uint16_t conn_handle, int reason) ble_sm_connection_broken(conn_handle); ble_gatts_connection_broken(conn_handle); ble_gattc_connection_broken(conn_handle); +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_gattc_cache_conn_broken(conn_handle); +#endif ble_hs_flow_connection_broken(conn_handle);; ble_hs_atomic_conn_delete(conn_handle); @@ -2094,6 +2100,9 @@ ble_gap_rx_conn_complete(struct ble_gap_conn_complete *evt, uint8_t instance) struct ble_gap_event event; struct ble_hs_conn *conn; int rc; +#if MYNEWT_VAL(BLE_GATT_CACHING) + struct ble_hs_conn_addrs addrs +#endif STATS_INC(ble_gap_stats, rx_conn_complete); @@ -2216,6 +2225,13 @@ ble_gap_rx_conn_complete(struct ble_gap_conn_complete *evt, uint8_t instance) event.connect.conn_handle = evt->connection_handle; event.connect.status = 0; + /* add gatt connection */ +#if MYNEWT_VAL(BLE_GATT_CACHING) + if (evt->role == BLE_HCI_LE_CONN_COMPLETE_ROLE_MASTER) { + ble_hs_conn_addrs(conn, &addrs); + rc = ble_gattc_cache_conn_create(conn->bhc_handle, addrs.peer_id_addr); + } +#endif ble_gap_event_listener_call(&event); ble_gap_call_conn_event_cb(&event, evt->connection_handle); @@ -6859,6 +6875,9 @@ ble_gap_enc_event(uint16_t conn_handle, int status, */ if (security_restored) { ble_gatts_bonding_restored(conn_handle); +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_gattc_cache_conn_bonding_restored(conn_handle); +#endif return; } @@ -6868,6 +6887,9 @@ ble_gap_enc_event(uint16_t conn_handle, int status, */ if (bonded) { ble_gatts_bonding_established(conn_handle); +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_gattc_cache_conn_bonding_established(conn_handle); +#endif } #endif } @@ -6947,7 +6969,16 @@ ble_gap_notify_rx_event(uint16_t conn_handle, uint16_t attr_handle, #if (MYNEWT_VAL(BLE_GATT_NOTIFY) || MYNEWT_VAL(BLE_GATT_INDICATE)) && NIMBLE_BLE_CONNECT struct ble_gap_event event; +#if MYNEWT_VAL(BLE_GATT_CACHING) + uint16_t start_handle; + uint16_t end_handle; + if (attr_handle == ble_gattc_cache_conn_get_svc_changed_handle(conn_handle)) { + start_handle = get_le16(om->om_data); + end_handle = get_le16(om->om_data + 2); + ble_gattc_cache_conn_update(conn_handle, start_handle, end_handle); + } +#endif memset(&event, 0, sizeof event); event.type = BLE_GAP_EVENT_NOTIFY_RX; event.notify_rx.conn_handle = conn_handle; diff --git a/nimble/host/src/ble_gatt_priv.h b/nimble/host/src/ble_gatt_priv.h index 255be751b..84793e122 100644 --- a/nimble/host/src/ble_gatt_priv.h +++ b/nimble/host/src/ble_gatt_priv.h @@ -88,6 +88,7 @@ extern STATS_SECT_DECL(ble_gatts_stats) ble_gatts_stats; #define BLE_GATT_CHR_DECL_SZ_16 5 #define BLE_GATT_CHR_DECL_SZ_128 19 +#define BLE_GATT_CHR_CLI_SUP_FEAT_SZ 1 typedef uint8_t ble_gatts_conn_flags; @@ -98,8 +99,20 @@ struct ble_gatts_conn { struct ble_gatts_clt_cfg *clt_cfgs; #endif int num_clt_cfgs; +#if MYNEWT_VAL(BLE_GATT_CACHING) + bool aware_state; + bool sent_err; +#endif uint16_t indicate_val_handle; + + /** + * For now only 3 bits in one octet are defined, but specification expects + * this service to be variable length with no upper bound. Let's make this + * future proof if more octets might be used. + * (Vol. 3, Part G, 7.2) + */ + uint8_t peer_cl_sup_feat[BLE_GATT_CHR_CLI_SUP_FEAT_SZ]; }; /*** @client. */ @@ -191,10 +204,21 @@ int ble_gatts_clt_cfg_access(uint16_t conn_handle, uint16_t attr_handle, uint8_t op, uint16_t offset, struct os_mbuf **om, void *arg); +#if MYNEWT_VAL(BLE_GATT_CACHING) +struct ble_gatts_aware_state { + uint8_t peer_id_addr[6]; + bool aware; +}; +extern struct ble_gatts_aware_state ble_gatts_conn_aware_states[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; +#endif + /*** @misc. */ int ble_gatts_conn_can_alloc(void); int ble_gatts_conn_init(struct ble_gatts_conn *gatts_conn); int ble_gatts_init(void); +#if MYNEWT_VAL(BLE_GATT_CACHING) +int ble_gattc_cache_conn_init(); +#endif #ifdef __cplusplus } diff --git a/nimble/host/src/ble_gattc.c b/nimble/host/src/ble_gattc.c index 63d967b49..30e5fe8c7 100644 --- a/nimble/host/src/ble_gattc.c +++ b/nimble/host/src/ble_gattc.c @@ -60,6 +60,7 @@ #include "host/ble_uuid.h" #include "host/ble_gap.h" #include "ble_hs_priv.h" +#include "ble_gattc_cache_priv.h" #if NIMBLE_BLE_CONNECT @@ -1535,6 +1536,13 @@ ble_gattc_disc_all_svcs(uint16_t conn_handle, ble_gatt_disc_svc_fn *cb, STATS_INC(ble_gattc_stats, disc_all_svcs); +#if MYNEWT_VAL(BLE_GATT_CACHING) + rc = ble_gattc_cache_conn_search_all_svcs(conn_handle, cb, cb_arg); + if(rc == 0) { + return rc; + } +#endif + proc = ble_gattc_proc_alloc(); if (proc == NULL) { rc = BLE_HS_ENOMEM; @@ -1746,6 +1754,12 @@ ble_gattc_disc_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid, struct ble_gattc_proc *proc; int rc; +#if MYNEWT_VAL(BLE_GATT_CACHING) + rc = ble_gattc_cache_conn_search_svc_by_uuid(conn_handle, uuid, cb, cb_arg); + if(rc == 0) { + return rc; + } +#endif STATS_INC(ble_gattc_stats, disc_svc_uuid); proc = ble_gattc_proc_alloc(); @@ -2290,6 +2304,12 @@ ble_gattc_disc_all_chrs(uint16_t conn_handle, uint16_t start_handle, struct ble_gattc_proc *proc; int rc; +#if MYNEWT_VAL(BLE_GATT_CACHING) + rc = ble_gattc_cache_conn_search_all_chrs(conn_handle, start_handle, end_handle, cb, cb_arg); + if(rc == 0) { + return rc; + } +#endif STATS_INC(ble_gattc_stats, disc_all_chrs); proc = ble_gattc_proc_alloc(); @@ -2529,6 +2549,12 @@ ble_gattc_disc_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle, struct ble_gattc_proc *proc; int rc; +#if MYNEWT_VAL(BLE_GATT_CACHING) + rc = ble_gattc_cache_conn_search_chrs_by_uuid(conn_handle, start_handle, end_handle, uuid, cb, cb_arg); + if(rc == 0) { + return rc; + } +#endif STATS_INC(ble_gattc_stats, disc_chrs_uuid); proc = ble_gattc_proc_alloc(); @@ -2739,6 +2765,12 @@ ble_gattc_disc_all_dscs(uint16_t conn_handle, uint16_t start_handle, struct ble_gattc_proc *proc; int rc; +#if MYNEWT_VAL(BLE_GATT_CACHING) + rc = ble_gattc_cache_conn_search_all_dscs(conn_handle, start_handle, end_handle, cb, cb_arg); + if(rc == 0) { + return rc; + } +#endif STATS_INC(ble_gattc_stats, disc_all_dscs); proc = ble_gattc_proc_alloc(); @@ -2796,6 +2828,11 @@ ble_gattc_read_cb(struct ble_gattc_proc *proc, int status, STATS_INC(ble_gattc_stats, read_fail); } +#if MYNEWT_VAL(BLE_GATT_CACHING) + if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_DB_OUT_OF_SYNC)) { + ble_gattc_cache_conn_update(proc->conn_handle, 0, 0xFFFF); + } +#endif if (proc->read.cb == NULL) { rc = 0; } else { @@ -3035,6 +3072,7 @@ ble_gattc_read_by_uuid(uint16_t conn_handle, uint16_t start_handle, #if !MYNEWT_VAL(BLE_GATT_READ_UUID) return BLE_HS_ENOTSUP; #endif + /* TODO:Roshan */ struct ble_gattc_proc *proc; int rc; @@ -3095,6 +3133,11 @@ ble_gattc_read_long_cb(struct ble_gattc_proc *proc, int status, STATS_INC(ble_gattc_stats, read_long_fail); } +#if MYNEWT_VAL(BLE_GATT_CACHING) + if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_DB_OUT_OF_SYNC)) { + ble_gattc_cache_conn_update(proc->conn_handle, 0, 0xFFFF); + } +#endif if (proc->read_long.cb == NULL) { rc = 0; } else { @@ -3300,6 +3343,11 @@ ble_gattc_read_mult_cb(struct ble_gattc_proc *proc, int status, attr.om = *om; } +#if MYNEWT_VAL(BLE_GATT_CACHING) + if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_DB_OUT_OF_SYNC)) { + ble_gattc_cache_conn_update(proc->conn_handle, 0, 0xFFFF); + } +#endif if (proc->read_mult.cb == NULL) { rc = 0; } else { @@ -3472,6 +3520,11 @@ ble_gattc_write_cb(struct ble_gattc_proc *proc, int status, STATS_INC(ble_gattc_stats, write_fail); } +#if MYNEWT_VAL(BLE_GATT_CACHING) + if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_DB_OUT_OF_SYNC)) { + ble_gattc_cache_conn_update(proc->conn_handle, 0, 0xFFFF); + } +#endif if (proc->write.cb == NULL) { rc = 0; } else { @@ -3596,6 +3649,11 @@ ble_gattc_write_long_cb(struct ble_gattc_proc *proc, int status, STATS_INC(ble_gattc_stats, write_long_fail); } +#if MYNEWT_VAL(BLE_GATT_CACHING) + if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_DB_OUT_OF_SYNC)) { + ble_gattc_cache_conn_update(proc->conn_handle, 0, 0xFFFF); + } +#endif if (proc->write_long.cb == NULL) { rc = 0; } else { @@ -3892,6 +3950,11 @@ ble_gattc_write_reliable_cb(struct ble_gattc_proc *proc, int status, STATS_INC(ble_gattc_stats, write_reliable_fail); } +#if MYNEWT_VAL(BLE_GATT_CACHING) + if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_DB_OUT_OF_SYNC)) { + ble_gattc_cache_conn_update(proc->conn_handle, 0, 0xFFFF); + } +#endif if (proc->write_reliable.cb == NULL) { rc = 0; } else { @@ -4161,6 +4224,18 @@ done: * $notify * *****************************************************************************/ +#if MYNEWT_VAL(BLE_GATT_CACHING) +static int ble_gatts_check_conn_aware(uint16_t conn_handle, bool *aware) { + struct ble_hs_conn *conn; + conn = ble_hs_conn_find(conn_handle); + if(conn == NULL) { + return BLE_HS_ENOTCONN; + } + *aware = conn->bhc_gatt_svr.aware_state; + return 0; +} +#endif + int ble_gatts_notify_custom(uint16_t conn_handle, uint16_t chr_val_handle, struct os_mbuf *txom) @@ -4168,6 +4243,9 @@ ble_gatts_notify_custom(uint16_t conn_handle, uint16_t chr_val_handle, #if !MYNEWT_VAL(BLE_GATT_NOTIFY) return BLE_HS_ENOTSUP; #endif +#if MYNEWT_VAL(BLE_GATT_CACHING) + bool aware; +#endif int rc; @@ -4175,6 +4253,18 @@ ble_gatts_notify_custom(uint16_t conn_handle, uint16_t chr_val_handle, ble_gattc_log_notify(chr_val_handle); +#if MYNEWT_VAL(BLE_GATT_CACHING) + ble_hs_lock(); + rc = ble_gatts_check_conn_aware(conn_handle, &aware); + ble_hs_unlock(); + if(rc != 0) { + goto done; + } + if(!aware) { + rc = BLE_HS_EREJECT; /* TODO correct error code ?*/ + goto done; + } +#endif if (txom == NULL) { /* No custom attribute data; read the value from the specified * attribute. @@ -4335,6 +4425,9 @@ ble_gatts_indicate_custom(uint16_t conn_handle, uint16_t chr_val_handle, struct ble_gattc_proc *proc; struct ble_hs_conn *conn; int rc; +#if MYNEWT_VAL(BLE_GATT_CACHING) + bool aware; +#endif STATS_INC(ble_gattc_stats, indicate); @@ -4350,6 +4443,20 @@ ble_gatts_indicate_custom(uint16_t conn_handle, uint16_t chr_val_handle, ble_gattc_log_indicate(chr_val_handle); +#if MYNEWT_VAL(BLE_GATT_CACHING) + if(chr_val_handle != ble_svc_gatt_changed_handle()) { + ble_hs_lock(); + rc = ble_gatts_check_conn_aware(conn_handle, &aware); + ble_hs_unlock(); + if(rc != 0) { + goto done; + } + if(!aware) { + rc = BLE_HS_EREJECT; + goto done; + } + } +#endif if (txom == NULL) { /* No custom attribute data; read the value from the specified * attribute. diff --git a/nimble/host/src/ble_gattc_cache.c b/nimble/host/src/ble_gattc_cache.c new file mode 100644 index 000000000..7f0bf3108 --- /dev/null +++ b/nimble/host/src/ble_gattc_cache.c @@ -0,0 +1,728 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include "host/ble_hs.h" +#include "ble_hs_conn_priv.h" +#include "ble_hs_priv.h" +#include "ble_gattc_cache_priv.h" + +#include "nimble/storage_port.h" +#include "host/ble_gatt.h" +#include "esp_nimble_mem.h" + +#define GATT_CACHE_PREFIX "gatt_" +#define INVALID_ADDR_NUM 0xff +#define MAX_DEVICE_IN_CACHE 50 +#define MAX_ADDR_LIST_CACHE_BUF 2048 + +#if MYNEWT_VAL(BLE_GATT_CACHING) +static const char *cache_key = "gattc_cache_key"; +static const char *cache_addr = "cache_addr_tab"; +static uint8_t ble_gattc_cache_find_addr(ble_addr_t addr); +static uint8_t ble_gattc_cache_find_hash(uint8_t * hash_key); +static uint8_t svc_end_handle; +struct cache_fn_mapping cache_fn; + +typedef struct { + /*Save the service data in the list according to the address */ + cache_handle_t cache_fp; + bool is_open; + ble_addr_t addr; + uint8_t hash_key[16]; +} cache_addr_info_t; + +typedef struct { + /* Save the address list in the cache */ + cache_handle_t addr_fp; + bool is_open; + uint8_t num_addr; + cache_addr_info_t cache_addr[MAX_DEVICE_IN_CACHE]; +} cache_env_t; + +static cache_env_t *cache_env = NULL; + +static void +print_hash_key(uint8_t * hash_key) +{ + MODLOG_DFLT(DEBUG, "Hash Key : "); + + for (int i = 0; i < 16; i++) { + MODLOG_DFLT(DEBUG, "%x ", hash_key[i]); + } +} + +static void +print_addr(ble_addr_t addr) +{ + MODLOG_DFLT(DEBUG, "Peer address: "); + for (int i = 0; i < 6; i++) { + MODLOG_DFLT(DEBUG, "%d ", addr.val[i]); + } +} + +static void +getFilename(char *buffer, uint8_t * hash) +{ + sprintf(buffer, "%s%02x%02x%02x%02x", GATT_CACHE_PREFIX, + hash[0], hash[1], hash[2], hash[3]); +} + +static int +cacheErase(cache_handle_t handle) +{ + if (cache_fn.erase_all) { + cache_fn.erase_all(handle); + } + return -1; +} + +static int +cacheWrite(cache_handle_t handle, const char * key, const void* value, size_t length) +{ + if (cache_fn.write) { + return cache_fn.write(handle, key, value, length); + } + return -1; +} + +static int +cacheRead(cache_handle_t handle, const char * key, void* out_value, size_t* length) +{ + if (cache_fn.read) { + return cache_fn.read(handle, key, out_value, length); + } + return -1; +} + +static void +cacheClose(ble_addr_t addr) +{ + uint8_t index = 0; + if ((index = ble_gattc_cache_find_addr(addr)) != INVALID_ADDR_NUM) { + if (cache_env->cache_addr[index].is_open) { + if (cache_fn.close) { + cache_fn.close(cache_env->cache_addr[index].cache_fp); + } + cache_env->cache_addr[index].is_open = false; + } + } +} + +static bool +cacheOpen(ble_addr_t addr, bool to_save, uint8_t *index) +{ + char fname[255] = {0}; + int status = -1; + uint8_t hash_key[16] = {0}; + + if ((*index = ble_gattc_cache_find_addr(addr)) != INVALID_ADDR_NUM) { + if (cache_env->cache_addr[*index].is_open) { + return true; + } else { + memcpy(hash_key, cache_env->cache_addr[*index].hash_key, sizeof(uint8_t) * 16); + getFilename(fname, hash_key); + if (cache_fn.open) { + if ((status = cache_fn.open(fname, READWRITE, &cache_env->cache_addr[*index].cache_fp)) == 0) { + /* Set the open flag to TRUE when success to open the hash file. */ + cache_env->cache_addr[*index].is_open = true; + } + } + } + } + return ((status == 0) ? true : false); +} + +void +ble_gattc_cacheReset(ble_addr_t *addr) +{ + uint8_t index = 0; + if (addr == NULL) { + BLE_HS_LOG(ERROR, "%s Cannot reset cache with null addr", __func__); + return; + } + + if ((index = ble_gattc_cache_find_addr(*addr)) != INVALID_ADDR_NUM) { + if (cache_env->cache_addr[index].is_open) { + cacheErase(cache_env->cache_addr[index].cache_fp); + + if (cache_fn.close) { + cache_fn.close(cache_env->cache_addr[index].cache_fp); + } + cache_env->cache_addr[index].is_open = false; + BLE_HS_LOG(DEBUG, "%s erased peer entry from NVS"); + } else { + cacheOpen(*addr, false, &index); + if (index == INVALID_ADDR_NUM) { + BLE_HS_LOG(ERROR, "%s INVALID ADDR NUM", __func__); + return; + } + if (cache_env->cache_addr[index].is_open) { + cacheErase(cache_env->cache_addr[index].cache_fp); + if (cache_fn.close) { + cache_fn.close(cache_env->cache_addr[index].cache_fp); + } + cache_env->cache_addr[index].is_open = false; + BLE_HS_LOG(DEBUG, "%s erased peer entry from NVS"); + } else { + BLE_HS_LOG(ERROR, "%s cacheOpen failed", __func__); + return; + } + } + if (cache_env->num_addr == 0) { + BLE_HS_LOG(ERROR, "%s cache addr list error", __func__); + return; + } + + uint8_t num = cache_env->num_addr; + + /* Delete the server_bda in the addr_info list. */ + for (uint8_t i = index; i < (num - 1); i++) { + memcpy(&cache_env->cache_addr[i], &cache_env->cache_addr[i + 1], + sizeof(cache_addr_info_t)); + } + + /* Reduced the number address counter also */ + cache_env->num_addr--; + + /* Update addr list to storage flash */ + if (cache_env->num_addr > 0) { + uint8_t *p_buf = nimble_platform_mem_malloc(MAX_ADDR_LIST_CACHE_BUF); + if (!p_buf) { + BLE_HS_LOG(ERROR, "%s malloc error", __func__); + return; + } + + uint16_t length = cache_env->num_addr * (sizeof(ble_addr_t) + sizeof(uint8_t) * 16); + + for (uint8_t i = 0; i < cache_env->num_addr; i++) { + + /* Copy the address to the buffer. */ + memcpy((p_buf + i * (sizeof(ble_addr_t) + sizeof(uint8_t) * 16)), + &cache_env->cache_addr[i].addr, sizeof(ble_addr_t)); + + /* Copy the hash key to the buffer.*/ + memcpy(p_buf + i * (sizeof(ble_addr_t) + sizeof(uint8_t) * 16) + sizeof(ble_addr_t), + cache_env->cache_addr[i].hash_key, sizeof(uint8_t) * 16); + } + + if (cache_env->is_open) { + if (cacheWrite(cache_env->addr_fp, cache_key, p_buf, length) != 0) { + BLE_HS_LOG(INFO, "%s, storage set blob failed", __func__); + } + } + nimble_platform_mem_free(p_buf); + + } else { + if (cache_env->is_open) { + cacheErase(cache_env->addr_fp); + if (cache_fn.close) { + cache_fn.close(cache_env->addr_fp); + } + cache_env->is_open = false; + BLE_HS_LOG(DEBUG, "%s erased entire cache from NVS"); + } else { + BLE_HS_LOG(INFO, "cache_env status is error"); + } + } + } +} + +static uint8_t +ble_gattc_cache_find_addr(ble_addr_t addr) +{ + uint8_t addr_index = 0; + uint8_t num = cache_env->num_addr; + cache_addr_info_t *addr_info = &cache_env->cache_addr[0]; + + for (addr_index = 0; addr_index < num; addr_index++, addr_info++) { + if (!memcmp(&addr_info->addr, &addr, sizeof(ble_addr_t))) { + return addr_index; + } + } + + return INVALID_ADDR_NUM; +} + +static uint8_t +ble_gattc_cache_find_hash(uint8_t * hash_key) +{ + uint8_t index = 0; + uint8_t num = cache_env->num_addr; + cache_addr_info_t *addr_info; + for (index = 0; index < num; index++) { + addr_info = &cache_env->cache_addr[index]; + if (memcmp(addr_info->hash_key, hash_key, sizeof(uint8_t) * 16) == 0) { + return index; + } + } + + return INVALID_ADDR_NUM; +} + +static void check_uuid(ble_uuid_any_t *uuid) +{ + ble_uuid16_t u16; + ble_uuid32_t u32; + switch (uuid->u.type) { + case BLE_UUID_TYPE_16: + u16 = *(const ble_uuid16_t *)uuid; + memset(uuid->u128.value, 0, sizeof(uuid->u128.value)); + uuid->u16.u.type = u16.u.type; + uuid->u16.value = u16.value; + break; + case BLE_UUID_TYPE_32: + u32 = *(const ble_uuid32_t *)uuid; + memset(uuid->u128.value, 0, sizeof(uuid->u128.value)); + uuid->u32.u.type = u32.u.type; + uuid->u32.value = u32.value; + break; + case BLE_UUID_TYPE_128: + break; + default: + assert(0); + break; + } +} + +static void +ble_gattc_fill_nv_attr_entry(ble_uuid_any_t uuid, uint16_t s_handle, uint16_t e_handle, + ble_gatt_attr_type attr_type, bool is_primary, + struct ble_gatt_nv_attr *nv_attr, int index, uint8_t properties) +{ + ble_uuid_copy(&nv_attr[index].uuid, &uuid.u); + check_uuid(&nv_attr[index].uuid); + nv_attr[index].s_handle = s_handle; + nv_attr[index].e_handle = e_handle; + nv_attr[index].is_primary = is_primary; + nv_attr[index].attr_type = attr_type; + nv_attr[index].properties = properties; + + if (s_handle >= svc_end_handle) { + svc_end_handle = s_handle; + } + + BLE_HS_LOG(DEBUG, "uuid = %x, s = %d, e = %d, is_primary = %d, attr_type = %d, prop =%d", + uuid.u16.value, s_handle, e_handle, is_primary, attr_type, properties); +} + +static void +ble_gattc_fill_nv_attr(struct ble_gattc_cache_conn *peer, size_t num_attr, struct ble_gatt_nv_attr *nv_attr) +{ + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn_chr *chr; + struct ble_gattc_cache_conn_dsc *dsc; + + int index = 0; + int svc_index = 0; + + SLIST_FOREACH(svc, &peer->svcs, next) { + ble_gattc_fill_nv_attr_entry(svc->svc.uuid, svc->svc.start_handle, svc->svc.end_handle, + BLE_GATT_ATTR_TYPE_SRVC, svc->type == BLE_GATT_SVC_TYPE_PRIMARY, + nv_attr, index, 0); + svc_index = index; + index++; + SLIST_FOREACH(chr, &svc->chrs, next) { + ble_gattc_fill_nv_attr_entry(chr->chr.uuid, chr->chr.val_handle, + chr->chr.def_handle, BLE_GATT_ATTR_TYPE_CHAR, false, + nv_attr, index, chr->chr.properties); + index++; + SLIST_FOREACH(dsc, &chr->dscs, next) { + ble_gattc_fill_nv_attr_entry(dsc->dsc.uuid, dsc->dsc.handle, 0, + BLE_GATT_ATTR_TYPE_CHAR_DESCR, false, nv_attr, index, + 0); + index++; + } + } + + if (svc->svc.end_handle == 65535) { + nv_attr[svc_index].e_handle = svc_end_handle; + } + } +} + +static int +ble_gattc_cache_addr_save(uint8_t *out_index, ble_addr_t addr, uint8_t * hash_key) +{ + int rc; + uint8_t num = ++(cache_env->num_addr); + uint8_t index = 0; + uint8_t insert_ind = 0; + uint8_t i = 0; + uint8_t *p_buf; + + p_buf = nimble_platform_mem_malloc(MAX_ADDR_LIST_CACHE_BUF); + if (p_buf == NULL) { + return BLE_HS_ENOMEM; + } + + /* Check the address list has the same hash key or not */ + if (ble_gattc_cache_find_hash(hash_key) != INVALID_ADDR_NUM) { + BLE_HS_LOG(DEBUG, "Hash key already present in the cache list"); + + if ((index = ble_gattc_cache_find_addr(addr)) != INVALID_ADDR_NUM) { + BLE_HS_LOG(DEBUG, "tBD address already present in the cache list"); + + /* If the bd_addr already in the address list, update the hash key in it. */ + insert_ind = index; + } else { + /* + If the bd_addr isn't in the address list, added the bd_addr to the last of the + address list. + */ + if(num > MYNEWT_VAL(BLE_GATT_CACHING_MAX_CONNS)) { + return BLE_HS_ENOMEM; + } + BLE_HS_LOG(DEBUG, "BD addr not present"); + insert_ind = num - 1; + } + } else { + BLE_HS_LOG(DEBUG, "Hash key not present, saving new data"); + if(num > MYNEWT_VAL(BLE_GATT_CACHING_MAX_CONNS)) { + return BLE_HS_ENOMEM; + } + insert_ind = num - 1; + } + + print_hash_key(hash_key); + print_addr(addr); + + memcpy(cache_env->cache_addr[insert_ind].hash_key, hash_key, sizeof(uint8_t) * 16); + memcpy(&cache_env->cache_addr[insert_ind].addr, &addr, sizeof(ble_addr_t)); + + cache_handle_t *fp = &cache_env->addr_fp; + uint16_t length = num * (sizeof(ble_addr_t) + (sizeof(uint8_t) * 16)); + + for (i = 0; i < num; i++) { + + memcpy(p_buf + i * (sizeof(ble_addr_t) + sizeof(uint8_t) * 16), + &cache_env->cache_addr[i].addr, sizeof(ble_addr_t)); + + memcpy(p_buf + i * (sizeof(ble_addr_t) + sizeof(uint8_t) * 16) + sizeof(ble_addr_t), + cache_env->cache_addr[i].hash_key, sizeof(uint8_t) * 16); + } + + if (cache_env->is_open) { + BLE_HS_LOG(DEBUG, "NVS Opened already"); + + rc = cacheWrite(cache_env->addr_fp, cache_key, p_buf, length); + if (rc != 0) { + BLE_HS_LOG(ERROR, "storage set blob fail, err %d", rc); + } + } else { + rc = cache_fn.open(cache_addr, READWRITE, fp); + + if (rc == 0) { + cache_env->is_open = true; + rc = cacheWrite(cache_env->addr_fp, cache_key, p_buf, length); + + if (rc != 0) { + BLE_HS_LOG(ERROR, "storage set blob fail, err %d", rc); + } + } else { + BLE_HS_LOG(ERROR, "Line = %d, storage flash open fail, err_code = %x", + __LINE__, rc); + } + } + nimble_platform_mem_free(p_buf); + if(out_index) { + *out_index = insert_ind; + } + return rc; +} + +static int +handle_compare(const void *s1, const void *s2) +{ + return ((struct ble_gatt_nv_attr *)s1)->s_handle - ((struct ble_gatt_nv_attr *)s2)->s_handle; +} + +static void +ble_gatts_sort_nv_attr(struct ble_gatt_nv_attr *nv_attr, size_t num_attr) +{ + qsort(nv_attr, (num_attr), sizeof(struct ble_gatt_nv_attr), handle_compare); +} + +void +ble_gattc_cache_save(struct ble_gattc_cache_conn *peer, size_t num_attr) +{ + int rc = 0; + uint8_t hash_key[16] = {0}; + uint8_t index = INVALID_ADDR_NUM; + struct ble_gatt_nv_attr *nv_attr; + + nv_attr = (struct ble_gatt_nv_attr *) nimble_platform_mem_malloc(num_attr * sizeof(ble_gatt_nv_attr)); + if (nv_attr == NULL) { + BLE_HS_LOG(DEBUG, "Failed to allocate memory to nv_attr"); + return; + } + memset(nv_attr, 0, num_attr * sizeof(ble_gatt_nv_attr)); + + ble_gattc_fill_nv_attr(peer, num_attr, nv_attr); + + ble_gatts_sort_nv_attr(nv_attr, num_attr); + + memcpy(hash_key, &peer->database_hash, sizeof(uint8_t) * 16); + + rc = ble_gattc_cache_addr_save(&index, peer->ble_gattc_cache_conn_addr, hash_key); + if(rc != 0) { + /* cannot save address, return */ + BLE_HS_LOG(ERROR, "Failed to save cache %d", rc); + return; + } + + if (cacheOpen(peer->ble_gattc_cache_conn_addr, true, &index)) { + BLE_HS_LOG(DEBUG, "Cache Opened already \n\tWriting cache_fp and cache_key on index = %d", + index); + rc = cacheWrite(cache_env->cache_addr[index].cache_fp, cache_key, nv_attr, + num_attr * sizeof(struct ble_gatt_nv_attr)); + } else { + rc = -1; + } + + BLE_HS_LOG(INFO, "%s() wrote hash_key on index = %d, num_attr = %d, status = %d.", __func__, + index, num_attr, rc); + + nimble_platform_mem_free(nv_attr); + cacheClose(peer->ble_gattc_cache_conn_addr); +} + +static struct ble_gatt_nv_attr * +ble_gattc_cache_load_nv_attr(uint8_t index, int *num_attr) +{ + int rc; + size_t length = 0; + struct ble_gatt_nv_attr *nv_attr; + + cacheRead(cache_env->cache_addr[index].cache_fp, cache_key, NULL, &length); + + *num_attr = length / (sizeof(ble_gatt_nv_attr)); + + nv_attr = (struct ble_gatt_nv_attr *) nimble_platform_mem_malloc((*num_attr) * sizeof(struct ble_gatt_nv_attr)); + if (nv_attr == NULL) { + return NULL; + } + + rc = cacheRead(cache_env->cache_addr[index].cache_fp, cache_key, nv_attr, &length); + + BLE_HS_LOG(INFO, "%s, rc = %d, length = %d index = %d", __func__, rc, length, index); + return nv_attr; +} + +static int +ble_gattc_add_svc_from_cache(ble_addr_t peer_addr, struct ble_gatt_nv_attr nv_attr) +{ + struct ble_gatt_svc *gatt_svc; + + gatt_svc = (struct ble_gatt_svc *)nimble_platform_mem_malloc(sizeof(struct ble_gatt_svc)); + if (gatt_svc == NULL) { + return BLE_HS_ENOMEM; + } + + gatt_svc->start_handle = nv_attr.s_handle; + gatt_svc->end_handle = nv_attr.e_handle; + ble_uuid_copy(&gatt_svc->uuid, &nv_attr.uuid.u); + + return ble_gattc_cache_conn_svc_add(peer_addr, gatt_svc); +} + +static int +ble_gattc_add_inc_from_cache(ble_addr_t peer_addr, struct ble_gatt_nv_attr nv_attr) +{ + struct ble_gatt_svc *gatt_svc; + + gatt_svc = (struct ble_gatt_svc *)nimble_platform_mem_malloc(sizeof(struct ble_gatt_svc)); + if (gatt_svc == NULL) { + return BLE_HS_ENOMEM; + } + + gatt_svc->start_handle = nv_attr.s_handle; + gatt_svc->end_handle = nv_attr.e_handle; + ble_uuid_copy(&gatt_svc->uuid, &nv_attr.uuid.u); + return ble_gattc_cache_conn_inc_add(peer_addr, gatt_svc); +} + +static int +ble_gattc_add_chr_from_cache(ble_addr_t peer_addr, struct ble_gatt_nv_attr nv_attr) +{ + struct ble_gatt_chr *gatt_chr; + gatt_chr = (struct ble_gatt_chr *)nimble_platform_mem_malloc(sizeof(struct ble_gatt_chr)); + if (gatt_chr == NULL) { + return BLE_HS_ENOMEM; + } + + gatt_chr->val_handle = nv_attr.s_handle; + gatt_chr->def_handle = nv_attr.e_handle; + ble_uuid_copy(&gatt_chr->uuid, &nv_attr.uuid.u); + gatt_chr->properties = nv_attr.properties; + + return ble_gattc_cache_conn_chr_add(peer_addr, 0, gatt_chr); +} + +static int +ble_gattc_add_dsc_from_cache(ble_addr_t peer_addr, struct ble_gatt_nv_attr nv_attr) +{ + struct ble_gatt_dsc *gatt_dsc; + gatt_dsc = (struct ble_gatt_dsc *)nimble_platform_mem_malloc(sizeof(struct ble_gatt_dsc)); + if (gatt_dsc == NULL) { + return BLE_HS_ENOMEM; + } + + gatt_dsc->handle = nv_attr.s_handle; + ble_uuid_copy(&gatt_dsc->uuid, &nv_attr.uuid.u); + + return ble_gattc_cache_conn_dsc_add(peer_addr, 0, gatt_dsc); +} + +int +ble_gattc_cache_load(ble_addr_t peer_addr) +{ + uint8_t index = 0; + uint8_t cache_index = 0; + int num_attr = 50; + int rc = 0; + struct ble_gatt_nv_attr *nv_attr; + + if (!cacheOpen(peer_addr, true, &index)) { + BLE_HS_LOG(INFO, "gattc cache open fail"); + return BLE_HS_EINVAL; + } + + if ((nv_attr = ble_gattc_cache_load_nv_attr(index, &num_attr)) == NULL) { + BLE_HS_LOG(INFO, "%s, gattc cache nv_attr load fail", __func__); + return BLE_HS_EINVAL; + } + + for (int i = 0; i < num_attr; i++) { + switch (nv_attr[i].attr_type) { + case BLE_GATT_ATTR_TYPE_SRVC: + if (nv_attr[i].is_primary) { + rc = ble_gattc_add_svc_from_cache(peer_addr, nv_attr[i]); + } else { + rc = ble_gattc_add_inc_from_cache(peer_addr, nv_attr[i]); + } + break; + + case BLE_GATT_ATTR_TYPE_CHAR: + rc = ble_gattc_add_chr_from_cache(peer_addr, nv_attr[i]); + break; + + case BLE_GATT_ATTR_TYPE_CHAR_DESCR: + rc = ble_gattc_add_dsc_from_cache(peer_addr, nv_attr[i]); + break; + + default: + break; + } + } + nimble_platform_mem_free(nv_attr); + cache_index = ble_gattc_cache_find_addr(peer_addr); + ble_gattc_cache_conn_load_hash(cache_env->cache_addr[cache_index].addr, + cache_env->cache_addr[cache_index].hash_key); + return rc; +} + +int +ble_gattc_cache_check_hash(struct ble_gattc_cache_conn *peer, struct os_mbuf *om) +{ + if (peer == NULL || om == NULL) { + BLE_HS_LOG(ERROR, "Check hash failed"); + return -1; + } + if (memcmp(peer->database_hash, om->om_data, om->om_len) == 0) { + return 0; + } + return -1; +} + +int +ble_gattc_cache_init(void *storage_cb) +{ + /* Point where to store data */ + cache_fn = link_storage_fn(storage_cb); + + cache_handle_t fp; + int rc = 0; + uint8_t num_addr; + size_t length = MAX_ADDR_LIST_CACHE_BUF; + + svc_end_handle = 0; + + uint8_t *p_buf = nimble_platform_mem_malloc(MAX_ADDR_LIST_CACHE_BUF); + if (p_buf == NULL) { + BLE_HS_LOG(ERROR, "%s malloc failed!", __func__); + rc = BLE_HS_ENOMEM; + return rc; + } + + cache_env = (cache_env_t *)nimble_platform_mem_malloc(sizeof(cache_env_t)); + if (cache_env == NULL) { + BLE_HS_LOG(ERROR, "%s malloc failed!", __func__); + nimble_platform_mem_free(p_buf); + rc = BLE_HS_ENOMEM; + return rc; + } + + memset(cache_env, 0x0, sizeof(cache_env_t)); + + if (cache_fn.open) { + if ((rc = cache_fn.open(cache_addr, READWRITE, &fp)) == 0) { + cache_env->addr_fp = fp; + cache_env->is_open = true; + + /* Read previously saved blob if available */ + if ((rc = cacheRead(fp, cache_key, p_buf, &length)) != 0) { + if (rc != 0) { + BLE_HS_LOG(DEBUG, "%s, Line = %d, storage flash get blob data fail, err_code = 0x%x", + __func__, __LINE__, rc); + } + nimble_platform_mem_free(p_buf); + return rc; + } + + num_addr = length / (sizeof(ble_addr_t) + sizeof(uint8_t) * 16); + cache_env->num_addr = num_addr; + BLE_HS_LOG(DEBUG, "Number of address loaded = %d", cache_env->num_addr); + + /* Read the address from storage flash to cache address list. */ + for (uint8_t i = 0; i < num_addr; i++) { + + memcpy(&cache_env->cache_addr[i].addr, + p_buf + i * (sizeof(ble_addr_t) + sizeof(uint8_t) * 16), sizeof(ble_addr_t)); + + memcpy(cache_env->cache_addr[i].hash_key, + p_buf + i * (sizeof(ble_addr_t) + sizeof(uint8_t) * 16) + sizeof(ble_addr_t), + sizeof(uint8_t) * 16); + + print_addr(cache_env->cache_addr[i].addr); + print_hash_key(cache_env->cache_addr[i].hash_key); + } + } + } else { + BLE_HS_LOG(ERROR, "%s, Line = %d, storage flash open fail, err_code = %x", __func__, __LINE__, + rc); + nimble_platform_mem_free(p_buf); + return rc; + } + + nimble_platform_mem_free(p_buf); + return 0; +} +#endif /* MYNEWT_VAL(BLE_GATT_CACHING) */ diff --git a/nimble/host/src/ble_gattc_cache_conn.c b/nimble/host/src/ble_gattc_cache_conn.c new file mode 100644 index 000000000..8ec8fdaa9 --- /dev/null +++ b/nimble/host/src/ble_gattc_cache_conn.c @@ -0,0 +1,1794 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include "host/ble_hs.h" +#include "ble_hs_priv.h" +#include "ble_gattc_cache_priv.h" + +#if MYNEWT_VAL(BLE_GATT_CACHING) +/* Gatt Procedure macros */ +#define BLE_GATT_OP_DISC_ALL_SVCS 1 +#define BLE_GATT_OP_DISC_SVC_UUID 2 +#define BLE_GATT_OP_FIND_INC_SVCS 3 +#define BLE_GATT_OP_DISC_ALL_CHRS 4 +#define BLE_GATT_OP_DISC_CHR_UUID 5 +#define BLE_GATT_OP_DISC_ALL_DSCS 6 + +#define CHECK_CACHE_CONN_STATE(cache_state, cb, cb_arg, opcode, \ + s_handle, e_handle, p_uuid) \ + op = &conn->pending_op; \ + switch(cache_state) { \ + case SVC_DISC_IN_PROGRESS: \ + if((void*)ble_gattc_cache_conn_svc_disced == cb) { \ + return BLE_HS_EINVAL; \ + } \ + ble_gattc_cache_conn_fill_op(op, s_handle, e_handle, p_uuid, cb, \ + cb_arg, opcode); \ + return 0; \ + case CHR_DISC_IN_PROGRESS: \ + if((void*)ble_gattc_cache_conn_chr_disced == cb) { \ + return BLE_HS_EINVAL; \ + } \ + ble_gattc_cache_conn_fill_op(op, s_handle, e_handle, p_uuid, cb, \ + cb_arg, opcode); \ + return 0; \ + case INC_DISC_IN_PROGRESS: \ + if((void *)ble_gattc_cache_conn_inc_disced == cb) { \ + return BLE_HS_EINVAL; \ + } \ + ble_gattc_cache_conn_fill_op(op, s_handle, e_handle, p_uuid, cb, \ + cb_arg, opcode); \ + return 0; \ + case DSC_DISC_IN_PROGRESS: \ + if((void*)ble_gattc_cache_conn_dsc_disced == cb) { \ + return BLE_HS_EINVAL; \ + } \ + ble_gattc_cache_conn_fill_op(op, s_handle, e_handle, p_uuid, cb, \ + cb_arg, opcode); \ + return 0; \ + case VERIFY_IN_PROGRESS: \ + ble_gattc_cache_conn_fill_op(op, s_handle, e_handle, p_uuid, cb, \ + cb_arg, opcode); \ + return 0; \ + break; \ + case CACHE_INVALID: \ + ble_gattc_cache_conn_fill_op(op, s_handle, e_handle, p_uuid, cb, \ + cb_arg, opcode); \ + /* start discovery here */ \ + BLE_HS_LOG(INFO, "Cache not valid"); \ + rc = ble_gattc_cache_conn_disc(conn); \ + return rc; \ + case CACHE_VERIFIED: \ + ble_gattc_cache_conn_fill_op(op, s_handle, e_handle, p_uuid, cb, \ + cb_arg, opcode); \ + break; \ + } +#define BLE_SVC_GATT_CHR_SERVICE_CHANGED_UUID16 0x2a05 +static void *ble_gattc_cache_conn_svc_mem; +static struct os_mempool ble_gattc_cache_conn_svc_pool; + +static void *ble_gattc_cache_conn_chr_mem; +static struct os_mempool ble_gattc_cache_conn_chr_pool; + +static void *ble_gattc_cache_conn_dsc_mem; +static struct os_mempool ble_gattc_cache_conn_dsc_pool; + +static void *ble_gattc_cache_conn_mem; +static struct os_mempool ble_gattc_cache_conn_pool; +static SLIST_HEAD(, ble_gattc_cache_conn) ble_gattc_cache_conns; + +static struct ble_gattc_cache_conn_svc * +ble_gattc_cache_conn_svc_find_range(struct ble_gattc_cache_conn *ble_gattc_cache_conn, uint16_t attr_handle); + +static struct ble_gattc_cache_conn_svc * +ble_gattc_cache_conn_svc_find(struct ble_gattc_cache_conn *ble_gattc_cache_conn, uint16_t svc_start_handle, + struct ble_gattc_cache_conn_svc **out_prev); + +int +ble_gattc_cache_conn_svc_is_empty(const struct ble_gattc_cache_conn_svc *svc); + +uint16_t +ble_gattc_cache_conn_chr_end_handle(const struct ble_gattc_cache_conn_svc *svc, const struct ble_gattc_cache_conn_chr *chr); + +int +ble_gattc_cache_conn_chr_is_empty(const struct ble_gattc_cache_conn_svc *svc, const struct ble_gattc_cache_conn_chr *chr); + +static struct ble_gattc_cache_conn_chr * +ble_gattc_cache_conn_chr_find(const struct ble_gattc_cache_conn_svc *svc, uint16_t chr_def_handle, + struct ble_gattc_cache_conn_chr **out_prev); + +static void +ble_gattc_cache_conn_disc_chrs(struct ble_gattc_cache_conn *ble_gattc_cache_conn); + +static void +ble_gattc_cache_conn_disc_incs(struct ble_gattc_cache_conn *ble_gattc_cache_conn); + +static void +ble_gattc_cache_conn_disc_dscs(struct ble_gattc_cache_conn *peer); + +struct ble_gattc_cache_conn * +ble_gattc_cache_conn_find(uint16_t conn_handle) +{ + struct ble_gattc_cache_conn *ble_gattc_cache_conn; + + SLIST_FOREACH(ble_gattc_cache_conn, &ble_gattc_cache_conns, next) { + if (ble_gattc_cache_conn->conn_handle == conn_handle) { + return ble_gattc_cache_conn; + } + } + + return NULL; +} + +struct ble_gattc_cache_conn * +ble_gattc_cache_conn_find_by_addr(ble_addr_t peer_addr) +{ + struct ble_gattc_cache_conn *ble_gattc_cache_conn; + SLIST_FOREACH(ble_gattc_cache_conn, &ble_gattc_cache_conns, next) { + if (memcmp(&ble_gattc_cache_conn->ble_gattc_cache_conn_addr, &peer_addr, sizeof(peer_addr)) == 0) { + return ble_gattc_cache_conn; + } + } + return NULL; +} + +static struct ble_gattc_cache_conn_dsc * +ble_gattc_cache_conn_dsc_find_prev(const struct ble_gattc_cache_conn_chr *chr, uint16_t dsc_handle) +{ + struct ble_gattc_cache_conn_dsc *prev; + struct ble_gattc_cache_conn_dsc *dsc; + + prev = NULL; + SLIST_FOREACH(dsc, &chr->dscs, next) { + if (dsc->dsc.handle >= dsc_handle) { + break; + } + + prev = dsc; + } + + return prev; +} + +static struct ble_gattc_cache_conn_dsc * +ble_gattc_cache_conn_dsc_find(const struct ble_gattc_cache_conn_chr *chr, uint16_t dsc_handle, + struct ble_gattc_cache_conn_dsc **out_prev) +{ + struct ble_gattc_cache_conn_dsc *prev; + struct ble_gattc_cache_conn_dsc *dsc; + + prev = ble_gattc_cache_conn_dsc_find_prev(chr, dsc_handle); + if (prev == NULL) { + dsc = SLIST_FIRST(&chr->dscs); + } else { + dsc = SLIST_NEXT(prev, next); + } + + if (dsc != NULL && dsc->dsc.handle != dsc_handle) { + dsc = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return dsc; +} + +static struct ble_gattc_cache_conn_chr * +ble_gattc_cache_conn_chr_find_range(const struct ble_gattc_cache_conn_svc *svc, uint16_t dsc_handle) +{ + struct ble_gattc_cache_conn_chr *chr; + struct ble_gattc_cache_conn_chr *prev = NULL; + + SLIST_FOREACH(chr, &svc->chrs, next) { + if (chr->chr.val_handle <= (dsc_handle)) { + prev = chr; + } else { + break; + } + } + return prev; +} + +int +ble_gattc_cache_conn_dsc_add(ble_addr_t peer_addr, uint16_t chr_val_handle, + const struct ble_gatt_dsc *gatt_dsc) +{ + struct ble_gattc_cache_conn_dsc *prev; + struct ble_gattc_cache_conn_dsc *dsc; + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn_chr *chr; + struct ble_gattc_cache_conn *peer; + + peer = ble_gattc_cache_conn_find_by_addr(peer_addr); + if (peer == NULL) { + BLE_HS_LOG(ERROR, "Conn not found"); + return BLE_HS_EUNKNOWN; + } + + svc = ble_gattc_cache_conn_svc_find_range(peer, gatt_dsc->handle); + if (svc == NULL) { + /* Can't find service for discovered descriptor; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + if (chr_val_handle == 0) { + chr = ble_gattc_cache_conn_chr_find_range(svc, gatt_dsc->handle); + } else { + chr = ble_gattc_cache_conn_chr_find(svc, chr_val_handle, NULL); + } + + if (chr == NULL) { + /* Can't find characteristic for discovered descriptor; this shouldn't + * happen. + */ + BLE_HS_LOG(ERROR, "Couldn't find characteristc for dsc handle = %d", gatt_dsc->handle); + assert(0); + return BLE_HS_EUNKNOWN; + } + + dsc = ble_gattc_cache_conn_dsc_find(chr, gatt_dsc->handle, &prev); + if (dsc != NULL) { + BLE_HS_LOG(DEBUG, "Descriptor already discovered."); + /* Descriptor already discovered. */ + return 0; + } + + dsc = os_memblock_get(&ble_gattc_cache_conn_dsc_pool); + if (dsc == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(dsc, 0, sizeof * dsc); + + dsc->dsc = *gatt_dsc; + + if (prev == NULL) { + SLIST_INSERT_HEAD(&chr->dscs, dsc, next); + } else { + SLIST_NEXT(prev, next) = dsc; + } + + BLE_HS_LOG(DEBUG, "Descriptor added with handle = %d", dsc->dsc.handle); + + return 0; +} + +uint16_t +ble_gattc_cache_conn_chr_end_handle(const struct ble_gattc_cache_conn_svc *svc, const struct ble_gattc_cache_conn_chr *chr) +{ + const struct ble_gattc_cache_conn_chr *next_chr; + + next_chr = SLIST_NEXT(chr, next); + if (next_chr != NULL) { + return next_chr->chr.def_handle - 1; + } else { + return svc->svc.end_handle; + } +} + +int +ble_gattc_cache_conn_chr_is_empty(const struct ble_gattc_cache_conn_svc *svc, const struct ble_gattc_cache_conn_chr *chr) +{ + return ble_gattc_cache_conn_chr_end_handle(svc, chr) <= chr->chr.val_handle; +} + +static struct ble_gattc_cache_conn_chr * +ble_gattc_cache_conn_chr_find_prev(const struct ble_gattc_cache_conn_svc *svc, uint16_t chr_val_handle) +{ + struct ble_gattc_cache_conn_chr *prev; + struct ble_gattc_cache_conn_chr *chr; + + prev = NULL; + SLIST_FOREACH(chr, &svc->chrs, next) { + if (chr->chr.val_handle >= chr_val_handle) { + break; + } + + prev = chr; + } + + return prev; +} + +static struct ble_gattc_cache_conn_chr * +ble_gattc_cache_conn_chr_find(const struct ble_gattc_cache_conn_svc *svc, uint16_t chr_val_handle, + struct ble_gattc_cache_conn_chr **out_prev) +{ + struct ble_gattc_cache_conn_chr *prev; + struct ble_gattc_cache_conn_chr *chr; + + prev = ble_gattc_cache_conn_chr_find_prev(svc, chr_val_handle); + if (prev == NULL) { + chr = SLIST_FIRST(&svc->chrs); + } else { + chr = SLIST_NEXT(prev, next); + } + + if (chr != NULL && chr->chr.val_handle != chr_val_handle) { + chr = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return chr; +} + +static void +ble_gattc_cache_conn_chr_delete(struct ble_gattc_cache_conn_chr *chr) +{ + struct ble_gattc_cache_conn_dsc *dsc; + + while ((dsc = SLIST_FIRST(&chr->dscs)) != NULL) { + SLIST_REMOVE_HEAD(&chr->dscs, next); + os_memblock_put(&ble_gattc_cache_conn_dsc_pool, dsc); + } + + os_memblock_put(&ble_gattc_cache_conn_chr_pool, chr); +} + +static int +ble_gattc_cache_conn_db_hash_read(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + uint16_t res; + struct ble_gattc_cache_conn *peer; + + peer = arg; + if (error->status != 0) { + res = error->status; + return res; + } + res = ble_hs_mbuf_to_flat(attr->om, peer->database_hash, sizeof(uint8_t) * 16, NULL); + return res; +} + +const struct ble_gattc_cache_conn_svc * +ble_gattc_cache_conn_svc_find_uuid(const struct ble_gattc_cache_conn *ble_gattc_cache_conn, const ble_uuid_t *uuid) +{ + const struct ble_gattc_cache_conn_svc *svc; + + SLIST_FOREACH(svc, &ble_gattc_cache_conn->svcs, next) { + if (ble_uuid_cmp(&svc->svc.uuid.u, uuid) == 0) { + return svc; + } + } + + return NULL; +} + +static const struct ble_gattc_cache_conn_chr * +ble_gattc_cache_conn_chr_find_uuid(const struct ble_gattc_cache_conn *ble_gattc_cache_conn, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid) +{ + const struct ble_gattc_cache_conn_svc *svc; + const struct ble_gattc_cache_conn_chr *chr; + + svc = ble_gattc_cache_conn_svc_find_uuid(ble_gattc_cache_conn, svc_uuid); + if (svc == NULL) { + return NULL; + } + + SLIST_FOREACH(chr, &svc->chrs, next) { + if (ble_uuid_cmp(&chr->chr.uuid.u, chr_uuid) == 0) { + return chr; + } + } + + return NULL; +} + +int +ble_gattc_cache_conn_chr_add(ble_addr_t peer_addr, uint16_t svc_start_handle, + const struct ble_gatt_chr *gatt_chr) +{ + struct ble_gattc_cache_conn_chr *prev; + struct ble_gattc_cache_conn_chr *chr; + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn *peer; + + peer = ble_gattc_cache_conn_find_by_addr(peer_addr); + if (peer == NULL) { + BLE_HS_LOG(ERROR, "Peer not found"); + return BLE_HS_EUNKNOWN; + } + + if (svc_start_handle == 0) { + svc = ble_gattc_cache_conn_svc_find_range(peer, gatt_chr->val_handle); + } else { + svc = ble_gattc_cache_conn_svc_find(peer, svc_start_handle, NULL); + } + + if (svc == NULL) { + /* Can't find service for discovered characteristic; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + chr = ble_gattc_cache_conn_chr_find(svc, gatt_chr->def_handle, &prev); + if (chr != NULL) { + /* Characteristic already discovered. */ + return 0; + } + + chr = os_memblock_get(&ble_gattc_cache_conn_chr_pool); + if (chr == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(chr, 0, sizeof * chr); + + chr->chr = *gatt_chr; + + if (prev == NULL) { + SLIST_INSERT_HEAD(&svc->chrs, chr, next); + } else { + SLIST_NEXT(prev, next) = chr; + } + + /* ble_gattc_db_hash_chr_present(chr->chr.uuid.u16); */ + + BLE_HS_LOG(DEBUG, "Characteristc added with def_handle = %d and val_handle = %d", + chr->chr.def_handle, chr->chr.val_handle); + + return 0; +} + +int +ble_gattc_cache_conn_svc_is_empty(const struct ble_gattc_cache_conn_svc *svc) +{ + return svc->svc.end_handle <= svc->svc.start_handle; +} + +static struct ble_gattc_cache_conn_svc * +ble_gattc_cache_conn_svc_find_prev(struct ble_gattc_cache_conn *peer, uint16_t svc_start_handle) +{ + struct ble_gattc_cache_conn_svc *prev; + struct ble_gattc_cache_conn_svc *svc; + + prev = NULL; + SLIST_FOREACH(svc, &peer->svcs, next) { + if (svc->svc.start_handle >= svc_start_handle) { + break; + } + + prev = svc; + } + + return prev; +} + +static struct ble_gattc_cache_conn_svc * +ble_gattc_cache_conn_svc_find(struct ble_gattc_cache_conn *ble_gattc_cache_conn, uint16_t svc_start_handle, + struct ble_gattc_cache_conn_svc **out_prev) +{ + struct ble_gattc_cache_conn_svc *prev; + struct ble_gattc_cache_conn_svc *svc; + + prev = ble_gattc_cache_conn_svc_find_prev(ble_gattc_cache_conn, svc_start_handle); + if (prev == NULL) { + svc = SLIST_FIRST(&ble_gattc_cache_conn->svcs); + } else { + svc = SLIST_NEXT(prev, next); + } + + if (svc != NULL && svc->svc.start_handle != svc_start_handle) { + svc = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return svc; +} + +static struct ble_gattc_cache_conn_svc * +ble_gattc_cache_conn_svc_find_range(struct ble_gattc_cache_conn *ble_gattc_cache_conn, uint16_t attr_handle) +{ + struct ble_gattc_cache_conn_svc *svc; + + SLIST_FOREACH(svc, &ble_gattc_cache_conn->svcs, next) { + if (svc->svc.start_handle <= attr_handle && + svc->svc.end_handle >= attr_handle) { + + return svc; + } + } + + return NULL; +} + +const struct ble_gattc_cache_conn_dsc * +ble_gattc_cache_conn_dsc_find_uuid(const struct ble_gattc_cache_conn *ble_gattc_cache_conn, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid) +{ + const struct ble_gattc_cache_conn_chr *chr; + const struct ble_gattc_cache_conn_dsc *dsc; + + chr = ble_gattc_cache_conn_chr_find_uuid(ble_gattc_cache_conn, svc_uuid, chr_uuid); + if (chr == NULL) { + return NULL; + } + + SLIST_FOREACH(dsc, &chr->dscs, next) { + if (ble_uuid_cmp(&dsc->dsc.uuid.u, dsc_uuid) == 0) { + return dsc; + } + } + + return NULL; +} + +int +ble_gattc_cache_conn_inc_add(ble_addr_t peer_addr, const struct ble_gatt_svc *gatt_svc) +{ + struct ble_gattc_cache_conn_svc *prev; + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn *peer; + + peer = ble_gattc_cache_conn_find_by_addr(peer_addr); + if (peer == NULL) { + BLE_HS_LOG(ERROR, "Peer not found"); + return BLE_HS_EUNKNOWN; + } + + svc = ble_gattc_cache_conn_svc_find(peer, gatt_svc->start_handle, &prev); + if (svc != NULL) { + /* Inc already discovered. */ + return 0; + } + + svc = os_memblock_get(&ble_gattc_cache_conn_svc_pool); + if (svc == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(svc, 0, sizeof * svc); + + svc->type = BLE_GATT_SVC_TYPE_SECONDARY; + svc->svc = *gatt_svc; + SLIST_INIT(&svc->chrs); + + if (prev == NULL) { + SLIST_INSERT_HEAD(&peer->svcs, svc, next); + } else { + SLIST_INSERT_AFTER(prev, svc, next); + } + + BLE_HS_LOG(DEBUG, "Inc added with start_handle = %d and end_handle = %d", + gatt_svc->start_handle, gatt_svc->end_handle); + + return 0; +} + +int +ble_gattc_cache_conn_svc_add(ble_addr_t peer_addr, const struct ble_gatt_svc *gatt_svc) +{ + struct ble_gattc_cache_conn_svc *prev; + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn *peer; + + peer = ble_gattc_cache_conn_find_by_addr(peer_addr); + if (peer == NULL) { + BLE_HS_LOG(ERROR, "Peer not found"); + return BLE_HS_EUNKNOWN; + } + + svc = ble_gattc_cache_conn_svc_find(peer, gatt_svc->start_handle, &prev); + if (svc != NULL) { + /* Service already discovered. */ + return 0; + } + + svc = os_memblock_get(&ble_gattc_cache_conn_svc_pool); + if (svc == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(svc, 0, sizeof * svc); + + svc->type = BLE_GATT_SVC_TYPE_PRIMARY; + svc->svc = *gatt_svc; + SLIST_INIT(&svc->chrs); + + if (prev == NULL) { + SLIST_INSERT_HEAD(&peer->svcs, svc, next); + } else { + SLIST_INSERT_AFTER(prev, svc, next); + } + + BLE_HS_LOG(DEBUG, "Service added with start_handle = %d and end_handle = %d", + gatt_svc->start_handle, gatt_svc->end_handle); + + return 0; +} + +static void +ble_gattc_cache_conn_svc_delete(struct ble_gattc_cache_conn_svc *svc) +{ + struct ble_gattc_cache_conn_chr *chr; + + while ((chr = SLIST_FIRST(&svc->chrs)) != NULL) { + SLIST_REMOVE_HEAD(&svc->chrs, next); + ble_gattc_cache_conn_chr_delete(chr); + } + + os_memblock_put(&ble_gattc_cache_conn_svc_pool, svc); +} + +size_t +ble_gattc_cache_conn_get_db_size(struct ble_gattc_cache_conn *peer) +{ + if (peer == NULL) { + return 0; + } + + size_t db_size = 0; + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn_chr *chr; + struct ble_gattc_cache_conn_dsc *dsc; + + SLIST_FOREACH(svc, &peer->svcs, next) { + db_size++; + SLIST_FOREACH(chr, &svc->chrs, next) { + db_size++; + SLIST_FOREACH(dsc, &chr->dscs, next) { + db_size++; + } + } + } + + return db_size; +} + +static void +ble_gattc_cache_conn_cache_peer(struct ble_gattc_cache_conn *peer) +{ + /* Cache the discovered peer */ + size_t db_size = ble_gattc_cache_conn_get_db_size(peer); + ble_gattc_cache_save(peer, db_size); +} + +void +ble_gattc_cache_conn_broken(uint16_t conn_handle) +{ + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn *conn; + + conn = ble_gattc_cache_conn_find(conn_handle); + if (conn == NULL) { + return; + } + BLE_HS_DBG_ASSERT(conn != NULL); + + /* clean the cache_conn */ + SLIST_REMOVE(&ble_gattc_cache_conns, conn, ble_gattc_cache_conn, next); + + while ((svc = SLIST_FIRST(&conn->svcs)) != NULL) { + SLIST_REMOVE_HEAD(&conn->svcs, next); + ble_gattc_cache_conn_svc_delete(svc); + + } + os_memblock_put(&ble_gattc_cache_conn_pool, conn); +} + +void +ble_gattc_cache_conn_bonding_established(uint16_t conn_handle) +{ + /* update the address of the peer */ + struct ble_hs_conn *conn; + struct ble_hs_conn_addrs addrs; + struct ble_gattc_cache_conn *peer; + + peer = ble_gattc_cache_conn_find(conn_handle); + if (peer == NULL) { + return; + } + + ble_hs_lock(); + + conn = ble_hs_conn_find(conn_handle); + BLE_HS_DBG_ASSERT(conn != NULL); + ble_hs_conn_addrs(conn, &addrs); + peer->ble_gattc_cache_conn_addr = conn->bhc_peer_addr; + peer->ble_gattc_cache_conn_addr.type = + ble_hs_misc_peer_addr_type_to_id(conn->bhc_peer_addr.type); + + ble_hs_unlock(); +} + +void +ble_gattc_cache_conn_bonding_restored(uint16_t conn_handle) +{ + struct ble_hs_conn *conn; + struct ble_hs_conn_addrs addrs; + struct ble_gattc_cache_conn *peer; + int rc; + + peer = ble_gattc_cache_conn_find(conn_handle); + if (peer == NULL) { + return; + } + + ble_hs_lock(); + + conn = ble_hs_conn_find(conn_handle); + BLE_HS_DBG_ASSERT(conn != NULL); + ble_hs_conn_addrs(conn, &addrs); + peer->ble_gattc_cache_conn_addr = conn->bhc_peer_addr; + peer->ble_gattc_cache_conn_addr.type = + ble_hs_misc_peer_addr_type_to_id(conn->bhc_peer_addr.type); + + ble_hs_unlock(); + /* try to load if not loaded */ + if (peer->cache_state == CACHE_INVALID) { + rc = ble_gattc_cache_load(peer->ble_gattc_cache_conn_addr); + if (rc == 0) { + /* connection is bonded, + so it is safe to set the state to + CACHE_VERIFIED */ + /* if the cache is changed after disconnect + then the indication will be received */ + peer->cache_state = CACHE_VERIFIED; + } + } + if (peer->cache_state == CACHE_LOADED) { + peer->cache_state = CACHE_VERIFIED; + } +} + +static void service_sanity_check(struct ble_gattc_cache_conn_svc_list *svcs) +{ + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn_chr *chr, *prev = NULL; + struct ble_gattc_cache_conn_dsc *dsc; + uint16_t end_handle = 0; + SLIST_FOREACH(svc, svcs, next) { + if (svc->svc.end_handle == 65535) { + SLIST_FOREACH(chr, &svc->chrs, next) { + end_handle = chr->chr.val_handle; + prev = chr; + } + SLIST_FOREACH(dsc, &prev->dscs, next) { + end_handle = dsc->dsc.handle; + } + svc->svc.end_handle = end_handle; + } + } +} + +static void +ble_gattc_cache_conn_disc_complete(struct ble_gattc_cache_conn *peer, int rc) +{ + struct ble_gattc_cache_conn_op *op; + struct ble_hs_conn *hs_conn; + const struct ble_gattc_cache_conn_chr *chr; + bool bonded; + + peer->disc_prev_chr_val = 0; + if (rc == 0) { + /* discovery complete */ + peer->cache_state = CACHE_VERIFIED; + service_sanity_check(&peer->svcs); + + /* cache the database only if the connection + is trusted or database hash exists */ + ble_hs_lock(); + hs_conn = ble_hs_conn_find(peer->conn_handle); + BLE_HS_DBG_ASSERT(hs_conn != NULL); + bonded = hs_conn->bhc_sec_state.bonded; + ble_hs_unlock(); + + chr = ble_gattc_cache_conn_chr_find_uuid(peer, + BLE_UUID16_DECLARE(BLE_GATT_SVC_UUID16), + BLE_UUID16_DECLARE(BLE_SVC_GATT_CHR_DATABASE_HASH_UUID16)); + if (bonded || chr != NULL) { + /* persist the cache */ + ble_gattc_cache_conn_cache_peer(peer); /* TODO */ + } + } else { + peer->cache_state = CACHE_INVALID; + } + /* respond to the pending gatt op */ + op = &peer->pending_op; + if (op->cb) { + switch (op->cb_type) { + case BLE_GATT_OP_DISC_ALL_SVCS : + rc = ble_gattc_cache_conn_search_all_svcs(peer->conn_handle, op->cb, op->cb_arg); + if (rc != 0) { + BLE_HS_LOG(ERROR, "search service failed"); + } + break; + case BLE_GATT_OP_DISC_SVC_UUID : + rc = ble_gattc_cache_conn_search_svc_by_uuid(peer->conn_handle, &op->uuid, op->cb, op->cb_arg); + if (rc != 0) { + BLE_HS_LOG(ERROR, "search service by uuid failed"); + } + break; + case BLE_GATT_OP_FIND_INC_SVCS : + rc = ble_gattc_cache_conn_search_inc_svcs(peer->conn_handle, op->start_handle, op->end_handle, op->cb, op->cb_arg); + if (rc != 0) { + BLE_HS_LOG(ERROR, "search inc failed"); + } + break; + case BLE_GATT_OP_DISC_ALL_CHRS : + rc = ble_gattc_cache_conn_search_all_chrs(peer->conn_handle, op->start_handle, op->end_handle, op->cb, op->cb_arg); + if (rc != 0) { + BLE_HS_LOG(ERROR, "search all chars failed"); + } + break; + case BLE_GATT_OP_DISC_CHR_UUID : + rc = ble_gattc_cache_conn_search_chrs_by_uuid(peer->conn_handle, op->start_handle, op->end_handle, &op->uuid, op->cb, op->cb_arg); + if (rc != 0) { + BLE_HS_LOG(ERROR, "search chars by uuid failed"); + } + break; + case BLE_GATT_OP_DISC_ALL_DSCS : + rc = ble_gattc_cache_conn_search_all_dscs(peer->conn_handle, op->start_handle, op->end_handle, op->cb, op->cb_arg); + if (rc != 0) { + BLE_HS_LOG(ERROR, "search all discs failed"); + } + break; + } + } +} + +static void +ble_gattc_cache_conn_undisc_all(struct ble_gattc_cache_conn *peer) +{ + ble_gattc_cacheReset(&peer->ble_gattc_cache_conn_addr); + + struct ble_gattc_cache_conn_svc *svc; + + while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) { + SLIST_REMOVE_HEAD(&peer->svcs, next); + ble_gattc_cache_conn_svc_delete(svc); + } +} + +static int +ble_gattc_cache_conn_inc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, void *arg) +{ + struct ble_gattc_cache_conn *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = ble_gattc_cache_conn_inc_add(peer->ble_gattc_cache_conn_addr, service); + break; + + case BLE_HS_EDONE: + /* All services discovered; start discovering incs. */ + ble_gattc_cache_conn_disc_chrs(peer); + rc = 0; + break; + + default: + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + ble_gattc_cache_conn_disc_complete(peer, rc); + } + + return rc; +} +static int +ble_gattc_cache_conn_svc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, void *arg) +{ + struct ble_gattc_cache_conn *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = ble_gattc_cache_conn_svc_add(peer->ble_gattc_cache_conn_addr, service); + break; + + case BLE_HS_EDONE: + /* All services discovered; start discovering incs. */ + peer->cur_svc = NULL; + ble_gattc_cache_conn_disc_incs(peer); + rc = 0; + break; + + default: + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + ble_gattc_cache_conn_disc_complete(peer, rc); + } + + return rc; +} + +static int +ble_gattc_cache_conn_disc(struct ble_gattc_cache_conn *peer) +{ + int rc; + + ble_gattc_cache_conn_undisc_all(peer); + + peer->disc_prev_chr_val = 1; + + BLE_HS_LOG(INFO, "Initiating Remote Service Discovery"); + peer->cache_state = SVC_DISC_IN_PROGRESS; + rc = ble_gattc_disc_all_svcs(peer->conn_handle, ble_gattc_cache_conn_svc_disced, peer); + return rc; +} + +static int +ble_gattc_cache_conn_on_read(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + uint16_t res; + + if (error->status != 0) { + res = error->status; + ble_gattc_cache_conn_disc_complete((struct ble_gattc_cache_conn *)arg, res); + return res; + } + + res = ble_gattc_cache_check_hash((struct ble_gattc_cache_conn *)arg, attr->om); + if (res == 0) { + BLE_HS_LOG(INFO, "Hash value up to date, skipping Discovery"); + ble_gattc_cache_conn_disc_complete((struct ble_gattc_cache_conn *)arg, res); + return 0; + } + return 0; + +} + +int +ble_gattc_cache_conn_create(uint16_t conn_handle, ble_addr_t ble_gattc_cache_conn_addr) +{ + struct ble_gattc_cache_conn *cache_conn; + int rc; + + /* Make sure the connection handle is unique. */ + cache_conn = ble_gattc_cache_conn_find(conn_handle); + if (cache_conn != NULL) { + /* peer is present already */ + /* TODO : validate cache somehow */ + return 0; + } + + cache_conn = os_memblock_get(&ble_gattc_cache_conn_pool); + if (cache_conn == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + + memset(cache_conn, 0, sizeof(struct ble_gattc_cache_conn)); + + /* set the conn as dirty initially as the cache is not built */ + cache_conn->cache_state = CACHE_INVALID; + cache_conn->conn_handle = conn_handle; + memcpy(&cache_conn->ble_gattc_cache_conn_addr, &ble_gattc_cache_conn_addr, sizeof(ble_addr_t)); + SLIST_INSERT_HEAD(&ble_gattc_cache_conns, cache_conn, next); + + /* Load cache */ + rc = ble_gattc_cache_load(ble_gattc_cache_conn_addr); + if (rc == 0) { + cache_conn->cache_state = CACHE_LOADED; + } + return 0; +} + +void +ble_gattc_cache_conn_load_hash(ble_addr_t peer_addr, uint8_t *hash_key) +{ + struct ble_gattc_cache_conn *peer; + peer = ble_gattc_cache_conn_find_by_addr(peer_addr); + + if (peer == NULL) { + BLE_HS_LOG(ERROR, "%s peer NULL", __func__); + } else { + BLE_HS_LOG(DEBUG, "Saved hash for peer"); + memcpy(&peer->database_hash, hash_key, sizeof(uint8_t) * 16); + } +} + +static int +ble_gattc_cache_conn_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, + void *arg) +{ + struct ble_gattc_cache_conn *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = ble_gattc_cache_conn_dsc_add(peer->ble_gattc_cache_conn_addr, chr_val_handle, dsc); + break; + + case BLE_HS_EDONE: + /* All descriptors in this characteristic discovered; start discovering + * descriptors in the next characteristic. + */ + if (peer->disc_prev_chr_val > 0) { + ble_gattc_cache_conn_disc_dscs(peer); + } + rc = 0; + break; + + default: + /* Error; abort discovery. */ + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + ble_gattc_cache_conn_disc_complete(peer, rc); + } + + return rc; +} + +static void +ble_gattc_cache_conn_disc_dscs(struct ble_gattc_cache_conn *peer) +{ + struct ble_gattc_cache_conn_chr *chr; + struct ble_gattc_cache_conn_svc *svc; + int rc; + + /* Search through the list of discovered characteristics for the first + * characteristic that contains undiscovered descriptors. Then, discover + * all descriptors belonging to that characteristic. + */ + SLIST_FOREACH(svc, &peer->svcs, next) { + SLIST_FOREACH(chr, &svc->chrs, next) { + if (!ble_gattc_cache_conn_chr_is_empty(svc, chr) && + SLIST_EMPTY(&chr->dscs) && + (peer->disc_prev_chr_val <= chr->chr.def_handle)) { + + peer->cache_state = DSC_DISC_IN_PROGRESS; + rc = ble_gattc_disc_all_dscs(peer->conn_handle, + chr->chr.val_handle, + ble_gattc_cache_conn_chr_end_handle(svc, chr), + ble_gattc_cache_conn_dsc_disced, peer); + if (rc != 0) { + ble_gattc_cache_conn_disc_complete(peer, rc); + } + + peer->disc_prev_chr_val = chr->chr.val_handle; + return; + } + } + } + + /* All descriptors discovered. */ + ble_gattc_cache_conn_disc_complete(peer, 0); +} + +static int +ble_gattc_cache_conn_chr_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) +{ + struct ble_gattc_cache_conn *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = ble_gattc_cache_conn_chr_add(peer->ble_gattc_cache_conn_addr, peer->cur_svc->svc.start_handle, chr); + + if (chr->uuid.u16.value == BLE_GATTC_DATABASE_HASH_UUID128) { + rc = ble_gattc_read(peer->conn_handle, chr->val_handle, + ble_gattc_cache_conn_db_hash_read, peer); + if (rc != 0) { + BLE_HS_LOG(ERROR, "Failed to read Database Hash %d", rc); + } + } + break; + + case BLE_HS_EDONE: + /* All characteristics in this service discovered; start discovering + * characteristics in the next service. + */ + if (peer->disc_prev_chr_val > 0) { + ble_gattc_cache_conn_disc_chrs(peer); + } + rc = 0; + break; + + default: + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + ble_gattc_cache_conn_disc_complete(peer, rc); + } + + return rc; +} + +static void +ble_gattc_cache_conn_disc_chrs(struct ble_gattc_cache_conn *peer) +{ + struct ble_gattc_cache_conn_svc *svc; + int rc; + /* Search through the list of discovered service for the first service that + * contains undiscovered characteristics. Then, discover all + * characteristics belonging to that service. + */ + SLIST_FOREACH(svc, &peer->svcs, next) { + if (!ble_gattc_cache_conn_svc_is_empty(svc) && SLIST_EMPTY(&svc->chrs)) { + peer->cur_svc = svc; + peer->cache_state = CHR_DISC_IN_PROGRESS; + rc = ble_gattc_disc_all_chrs(peer->conn_handle, + svc->svc.start_handle, + svc->svc.end_handle, + ble_gattc_cache_conn_chr_disced, peer); + if (rc != 0) { + ble_gattc_cache_conn_disc_complete(peer, rc); + } + return; + } + } + + /* All characteristics discovered. */ + ble_gattc_cache_conn_disc_dscs(peer); +} + +/* Note : confirm peer->cur_svc is set correctly before calling */ +static void +ble_gattc_cache_conn_disc_incs(struct ble_gattc_cache_conn *peer) +{ + struct ble_gattc_cache_conn_svc *svc; + int rc; + + if (peer->cur_svc == NULL) { + peer->cur_svc = SLIST_FIRST(&peer->svcs); + } else { + peer->cur_svc = SLIST_NEXT(peer->cur_svc, next); + if (peer->cur_svc == NULL) { + if (peer->disc_prev_chr_val > 0) { + ble_gattc_cache_conn_disc_chrs(peer); + } + } + } + peer->cache_state = INC_DISC_IN_PROGRESS; + svc = peer->cur_svc; + rc = ble_gattc_find_inc_svcs(peer->conn_handle, + svc->svc.start_handle, + svc->svc.end_handle, + ble_gattc_cache_conn_inc_disced, peer); + if (rc != 0) { + ble_gattc_cache_conn_disc_complete(peer, rc); + } + return; +} +/* As esp_nimble only supports adding/deleting whole services + * It is safe to assume that the start_handle and end_handle + * spans 1 or more services + */ +void +ble_gattc_cache_conn_update(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) +{ + /* All attributes are being rediscovered + TODO : rediscover only the start_handle to end_handle */ + struct ble_gattc_cache_conn *peer; + int rc; + + peer = ble_gattc_cache_conn_find(conn_handle); + if (peer == NULL) { + BLE_HS_LOG(ERROR, "Cannot find connection with conn_handle %d", conn_handle); + } + + peer->cache_state = CACHE_INVALID; + rc = ble_gattc_cache_conn_disc(peer); + if (rc != 0) { + peer->cache_state = CACHE_INVALID; + } +} + +uint16_t +ble_gattc_cache_conn_get_svc_changed_handle(uint16_t conn_handle) +{ + struct ble_gattc_cache_conn *peer; + const struct ble_gattc_cache_conn_chr *chr; + + peer = ble_gattc_cache_conn_find(conn_handle); + if (peer == NULL) { + BLE_HS_LOG(ERROR, "Cannot find connection with conn_handle %d", conn_handle); + return -1; + } + + /* Check if attr_handle is of service change char */ + chr = ble_gattc_cache_conn_chr_find_uuid(peer, BLE_UUID16_DECLARE(BLE_GATT_SVC_UUID16), + BLE_UUID16_DECLARE(BLE_SVC_GATT_CHR_SERVICE_CHANGED_UUID16)); + + if (chr == NULL) { + BLE_HS_LOG(ERROR, "Cannot find service change characteristic"); + return -1; + } + return chr->chr.val_handle; +} + +void +ble_gattc_cache_conn_free_mem(void) +{ + free(ble_gattc_cache_conn_mem); + ble_gattc_cache_conn_mem = NULL; + + free(ble_gattc_cache_conn_svc_mem); + ble_gattc_cache_conn_svc_mem = NULL; + + free(ble_gattc_cache_conn_chr_mem); + ble_gattc_cache_conn_chr_mem = NULL; + + free(ble_gattc_cache_conn_dsc_mem); + ble_gattc_cache_conn_dsc_mem = NULL; +} + +int +ble_gattc_cache_conn_init() +{ + int rc; + int max_ble_gattc_cache_conns; + int max_svcs; + int max_chrs; + int max_dscs; + void *storage_cb; + + max_ble_gattc_cache_conns = MYNEWT_VAL(BLE_MAX_CONNECTIONS); + max_svcs = (MYNEWT_VAL(BLE_MAX_CONNECTIONS)) * + (MYNEWT_VAL(BLE_GATT_CACHING_MAX_SVCS)); + max_chrs = (MYNEWT_VAL(BLE_MAX_CONNECTIONS)) * + (MYNEWT_VAL(BLE_GATT_CACHING_MAX_CHRS)); + max_dscs = (MYNEWT_VAL(BLE_MAX_CONNECTIONS)) * + (MYNEWT_VAL(BLE_GATT_CACHING_MAX_DSCS)); + /* Free memory first in case this function gets called more than once. */ + ble_gattc_cache_conn_free_mem(); + + ble_gattc_cache_conn_mem = malloc( + OS_MEMPOOL_BYTES(max_ble_gattc_cache_conns, sizeof(struct ble_gattc_cache_conn))); + if (ble_gattc_cache_conn_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&ble_gattc_cache_conn_pool, max_ble_gattc_cache_conns, + sizeof(struct ble_gattc_cache_conn), ble_gattc_cache_conn_mem, + "ble_gattc_cache_conn_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + ble_gattc_cache_conn_svc_mem = malloc( + OS_MEMPOOL_BYTES(max_svcs, sizeof(struct ble_gattc_cache_conn_svc))); + if (ble_gattc_cache_conn_svc_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&ble_gattc_cache_conn_svc_pool, max_svcs, + sizeof(struct ble_gattc_cache_conn_svc), ble_gattc_cache_conn_svc_mem, + "ble_gattc_cache_conn_svc_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + ble_gattc_cache_conn_chr_mem = malloc( + OS_MEMPOOL_BYTES(max_chrs, sizeof(struct ble_gattc_cache_conn_chr))); + if (ble_gattc_cache_conn_chr_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&ble_gattc_cache_conn_chr_pool, max_chrs, + sizeof(struct ble_gattc_cache_conn_chr), ble_gattc_cache_conn_chr_mem, + "ble_gattc_cache_conn_chr_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + ble_gattc_cache_conn_dsc_mem = malloc( + OS_MEMPOOL_BYTES(max_dscs, sizeof(struct ble_gattc_cache_conn_dsc))); + if (ble_gattc_cache_conn_dsc_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&ble_gattc_cache_conn_dsc_pool, max_dscs, + sizeof(struct ble_gattc_cache_conn_dsc), ble_gattc_cache_conn_dsc_mem, + "ble_gattc_cache_conn_dsc_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + storage_cb = NULL; + rc = ble_gattc_cache_init(storage_cb); + return 0; + +err: + ble_gattc_cache_conn_free_mem(); + return rc; +} + +/** + * Returns a pointer to a GATT error object with the specified fields. The + * returned object is statically allocated, so this function is not reentrant. + * This function should only ever be called by the ble_hs task. + */ +static struct ble_gatt_error * +ble_gattc_cache_error(int status, uint16_t att_handle) +{ + static struct ble_gatt_error error; + + /* For consistency, always indicate a handle of 0 on success. */ + if (status == 0 || status == BLE_HS_EDONE) { + att_handle = 0; + } + + error.status = status; + error.att_handle = att_handle; + return &error; +} + +/* gattc discovery apis */ + +static int ble_gattc_cache_conn_verify(struct ble_gattc_cache_conn *conn) +{ + struct ble_hs_conn *gap_conn; + const struct ble_gattc_cache_conn_chr *chr; + int rc; + + if (conn->cache_state == CACHE_VERIFIED) { + return 0; + } + + ble_hs_lock(); + gap_conn = ble_hs_conn_find(conn->conn_handle); + ble_hs_unlock(); + + if (gap_conn == NULL) { + return BLE_HS_ENOTCONN; + } + if (conn->cache_state == CACHE_LOADED) { + if (gap_conn->bhc_sec_state.bonded) { + conn->cache_state = CACHE_VERIFIED; + return 0; + } + chr = ble_gattc_cache_conn_chr_find_uuid(conn, + BLE_UUID16_DECLARE(BLE_GATT_SVC_UUID16), + BLE_UUID16_DECLARE(BLE_SVC_GATT_CHR_DATABASE_HASH_UUID16)); + if (chr == NULL) { + /* no way to verify */ + conn->cache_state = CACHE_INVALID; + return 0; + } + rc = ble_gattc_read(conn->conn_handle, chr->chr.val_handle, + ble_gattc_cache_conn_on_read, conn); + if (rc != 0) { + /* no way to verify */ + conn->cache_state = CACHE_INVALID; + return 0; + } + conn->cache_state = VERIFY_IN_PROGRESS; + return 0; + } + return 0; +} + +static void ble_gattc_cache_search_all_svcs_cb(struct ble_npl_event *ev) +{ + /* return all services */ + struct ble_gattc_cache_conn *conn; + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn_op *op; + int status = 0; + uint16_t conn_handle; + ble_gatt_disc_svc_fn *dcb; + + conn_handle = *(uint16_t*)ble_npl_event_get_arg(ev); + conn = ble_gattc_cache_conn_find(conn_handle); + if (conn == NULL) { + return; + } + op = &conn->pending_op; + dcb = op->cb; + SLIST_FOREACH(svc, &conn->svcs, next) { + if (svc->type == BLE_GATT_SVC_TYPE_PRIMARY) { + dcb(conn->conn_handle, ble_gattc_cache_error(status, 0), &svc->svc, op->cb_arg); + } + } + status = BLE_HS_EDONE; + dcb(conn->conn_handle, ble_gattc_cache_error(status, 0), &svc->svc, op->cb_arg); + return; +} + +static void ble_gattc_cache_conn_fill_op(struct ble_gattc_cache_conn_op *op, + uint16_t start_handle, + uint16_t end_handle, + ble_uuid_t uuid, + void *cb, + void *cb_arg, + uint8_t cb_type) +{ + op->cb = cb; + op->cb_arg = cb_arg; + op->cb_type = cb_type; + op->start_handle = start_handle; + op->end_handle = end_handle; + op->uuid = uuid; +} + +int +ble_gattc_cache_conn_search_all_svcs(uint16_t conn_handle, + ble_gatt_disc_svc_fn *cb, void *cb_arg) +{ + struct ble_gattc_cache_conn *conn; + struct ble_gattc_cache_conn_op *op; + ble_uuid_t uuid = {0}; + int rc; + conn = ble_gattc_cache_conn_find(conn_handle); + if (conn == NULL) { + BLE_HS_LOG(DEBUG, "No connection in the Cache" + "HANDLE=%d\n", + conn_handle); + return BLE_HS_ENOTCONN; + } + + rc = ble_gattc_cache_conn_verify(conn); + if (rc != 0) { + return rc; + } + + CHECK_CACHE_CONN_STATE(conn->cache_state, cb, cb_arg, BLE_GATT_OP_DISC_ALL_SVCS, + 0, 0, uuid); + /* put the event in the queue to mimic the gattc behaviour */ + ble_npl_event_init(&conn->disc_ev, ble_gattc_cache_search_all_svcs_cb, &conn->conn_handle); + ble_npl_eventq_put((struct ble_npl_eventq *)ble_hs_evq_get(), &conn->disc_ev); + return 0; +} + +static void +ble_gattc_cache_conn_search_svc_by_uuid_cb(struct ble_npl_event *ev) +{ + struct ble_gattc_cache_conn *conn; + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn_op *op; + int status = 0; + uint16_t conn_handle; + ble_gatt_disc_svc_fn *dcb; + + conn_handle = *(uint16_t*)ble_npl_event_get_arg(ev); + /* this is to confirm if the connection still exist */ + conn = ble_gattc_cache_conn_find(conn_handle); + if (conn == NULL) { + return; + } + op = &conn->pending_op; + dcb = op->cb; + SLIST_FOREACH(svc, &conn->svcs, next) { + if (svc->type == BLE_GATT_SVC_TYPE_PRIMARY && ble_uuid_cmp(&svc->svc.uuid.u, &op->uuid) == 0) { + dcb(conn_handle, ble_gattc_cache_error(status, 0), &svc->svc, op->cb_arg); + } + } + status = BLE_HS_EDONE; + dcb(conn_handle, ble_gattc_cache_error(status, 0), &svc->svc, op->cb_arg); + return; +} + +int +ble_gattc_cache_conn_search_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid, + ble_gatt_disc_svc_fn *cb, void *cb_arg) +{ + struct ble_gattc_cache_conn *conn; + struct ble_gattc_cache_conn_op *op; + int rc; + + conn = ble_gattc_cache_conn_find(conn_handle); + if (conn == NULL) { + BLE_HS_LOG(DEBUG, "No connection in the Cache" + "HANDLE=%d\n", + conn_handle); + return BLE_HS_ENOTCONN; + } + + rc = ble_gattc_cache_conn_verify(conn); + if (rc != 0) { + return rc; + } + + CHECK_CACHE_CONN_STATE(conn->cache_state, cb, cb_arg, BLE_GATT_OP_DISC_SVC_UUID, + 0, 0, *uuid); + /* put the event in the queue to mimic the gattc behaviour */ + ble_npl_event_init(&conn->disc_ev, ble_gattc_cache_conn_search_svc_by_uuid_cb, &conn->conn_handle); + ble_npl_eventq_put((struct ble_npl_eventq *)ble_hs_evq_get(), &conn->disc_ev); + return 0; +} + +static void +ble_gattc_cache_conn_search_inc_svcs_cb(struct ble_npl_event *ev) +{ + /* return all included services */ + struct ble_gattc_cache_conn *conn; + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn_op *op; + int status = 0; + uint16_t conn_handle; + ble_gatt_disc_svc_fn *dcb; + + conn_handle = *(uint16_t*)ble_npl_event_get_arg(ev); + conn = ble_gattc_cache_conn_find(conn_handle); + if (conn == NULL) { + return; + } + op = &conn->pending_op; + dcb = op->cb; + SLIST_FOREACH(svc, &conn->svcs, next) { + if (svc->type == BLE_GATT_SVC_TYPE_SECONDARY && + (svc->svc.start_handle >= op->start_handle && svc->svc.end_handle <= op->end_handle)) { + dcb(conn->conn_handle, ble_gattc_cache_error(status, 0), &svc->svc, op->cb_arg); + } + } + status = BLE_HS_EDONE; + dcb(conn->conn_handle, ble_gattc_cache_error(status, 0), &svc->svc, op->cb_arg); + return; +} +int +ble_gattc_cache_conn_search_inc_svcs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, + ble_gatt_disc_svc_fn *cb, void *cb_arg) +{ + struct ble_gattc_cache_conn *conn; + struct ble_gattc_cache_conn_op *op; + ble_uuid_t uuid = {0}; + int rc; + + conn = ble_gattc_cache_conn_find(conn_handle); + if (conn == NULL) { + BLE_HS_LOG(DEBUG, "No connection in the Cache" + "HANDLE=%d\n", + conn_handle); + return BLE_HS_ENOTCONN; + } + + rc = ble_gattc_cache_conn_verify(conn); + if (rc != 0) { + return rc; + } + + CHECK_CACHE_CONN_STATE(conn->cache_state, cb, cb_arg, BLE_GATT_OP_DISC_ALL_CHRS, + start_handle, end_handle, uuid); + /* put the event in the queue to mimic the gattc behaviour */ + ble_npl_event_init(&conn->disc_ev, ble_gattc_cache_conn_search_inc_svcs_cb, &conn->conn_handle); + ble_npl_eventq_put((struct ble_npl_eventq *)ble_hs_evq_get(), &conn->disc_ev); + return 0; +} + +static void +ble_gattc_cache_conn_search_all_chrs_cb(struct ble_npl_event *ev) +{ + struct ble_gattc_cache_conn *conn; + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn_chr *chr; + struct ble_gattc_cache_conn_op *op; + int status = 0; + uint16_t conn_handle; + ble_gatt_chr_fn *dcb; + + conn_handle = *(uint16_t*)ble_npl_event_get_arg(ev); + conn = ble_gattc_cache_conn_find(conn_handle); + if (conn == NULL) { + return; + } + op = &conn->pending_op; + dcb = op->cb; + svc = ble_gattc_cache_conn_svc_find_range(conn, op->start_handle); + /* return all chrs */ + SLIST_FOREACH(chr, &svc->chrs, next) { + dcb(conn_handle, ble_gattc_cache_error(status, 0), &chr->chr, op->cb_arg); + } + status = BLE_HS_EDONE; + dcb(conn_handle, ble_gattc_cache_error(status, 0), &chr->chr, op->cb_arg); + return; +} + +int +ble_gattc_cache_conn_search_all_chrs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, ble_gatt_chr_fn *cb, + void *cb_arg) +{ + struct ble_gattc_cache_conn *conn; + struct ble_gattc_cache_conn_op *op; + ble_uuid_t uuid = {0}; + int rc; + + conn = ble_gattc_cache_conn_find(conn_handle); + if (conn == NULL) { + BLE_HS_LOG(DEBUG, "No connection in the Cache" + "HANDLE=%d\n", + conn_handle); + return BLE_HS_ENOTCONN; + } + + rc = ble_gattc_cache_conn_verify(conn); + if (rc != 0) { + return rc; + } + + CHECK_CACHE_CONN_STATE(conn->cache_state, cb, cb_arg, BLE_GATT_OP_DISC_ALL_CHRS, + start_handle, end_handle, uuid); + /* put the event in the queue to mimic the gattc behaviour */ + ble_npl_event_init(&conn->disc_ev, ble_gattc_cache_conn_search_all_chrs_cb, &conn->conn_handle); + ble_npl_eventq_put((struct ble_npl_eventq *)ble_hs_evq_get(), &conn->disc_ev); + return 0; +} + +static void +ble_gattc_cache_conn_search_chrs_by_uuid_cb(struct ble_npl_event *ev) +{ + struct ble_gattc_cache_conn *conn; + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn_chr *chr; + struct ble_gattc_cache_conn_op *op; + int status = 0; + uint16_t conn_handle; + ble_gatt_chr_fn *dcb; + + conn_handle = *(uint16_t*)ble_npl_event_get_arg(ev); + conn = ble_gattc_cache_conn_find(conn_handle); + if (conn == NULL) { + return; + } + op = &conn->pending_op; + dcb = op->cb; + svc = ble_gattc_cache_conn_svc_find_range(conn, op->start_handle); + /* return all chrs */ + SLIST_FOREACH(chr, &svc->chrs, next) { + if (ble_uuid_cmp(&chr->chr.uuid.u, &op->uuid) == 0) { + dcb(conn_handle, ble_gattc_cache_error(status, 0), &chr->chr, op->cb_arg); + } + } + status = BLE_HS_EDONE; + dcb(conn_handle, ble_gattc_cache_error(status, 0), &chr->chr, op->cb_arg); + return; +} + +int +ble_gattc_cache_conn_search_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid, + ble_gatt_chr_fn *cb, void *cb_arg) +{ + struct ble_gattc_cache_conn *conn; + struct ble_gattc_cache_conn_op *op; + int rc; + + conn = ble_gattc_cache_conn_find(conn_handle); + if (conn == NULL) { + BLE_HS_LOG(DEBUG, "No connection in the Cache" + "HANDLE=%d\n", + conn_handle); + return BLE_HS_ENOTCONN; + } + + rc = ble_gattc_cache_conn_verify(conn); + if (rc != 0) { + return rc; + } + + CHECK_CACHE_CONN_STATE(conn->cache_state, cb, cb_arg, BLE_GATT_OP_DISC_CHR_UUID, + start_handle, end_handle, *uuid); + /* put the event in the queue to mimic the gattc behaviour */ + ble_npl_event_init(&conn->disc_ev, ble_gattc_cache_conn_search_chrs_by_uuid_cb, &conn->conn_handle); + ble_npl_eventq_put((struct ble_npl_eventq *)ble_hs_evq_get(), &conn->disc_ev); + return 0; +} + +static void +ble_gattc_cache_conn_search_all_dscs_cb(struct ble_npl_event *ev) +{ + struct ble_gattc_cache_conn *conn; + struct ble_gattc_cache_conn_svc *svc; + struct ble_gattc_cache_conn_chr *chr; + struct ble_gattc_cache_conn_dsc *dsc; + struct ble_gattc_cache_conn_op *op; + int status = 0; + uint16_t conn_handle; + ble_gatt_dsc_fn *dcb; + + conn_handle = *(uint16_t*)ble_npl_event_get_arg(ev); + conn = ble_gattc_cache_conn_find(conn_handle); + if (conn == NULL) { + return; + } + op = &conn->pending_op; + dcb = op->cb; + svc = ble_gattc_cache_conn_svc_find_range(conn, op->start_handle); + chr = ble_gattc_cache_conn_chr_find_range(svc, op->start_handle); + SLIST_FOREACH(dsc, &chr->dscs, next) { + dcb(conn_handle, ble_gattc_cache_error(status, 0), chr->chr.val_handle, &dsc->dsc, op->cb_arg); + } + status = BLE_HS_EDONE; + dcb(conn_handle, ble_gattc_cache_error(status, 0), chr->chr.val_handle, &dsc->dsc, op->cb_arg); + return; +} + +int +ble_gattc_cache_conn_search_all_dscs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, + ble_gatt_dsc_fn *cb, void *cb_arg) +{ + struct ble_gattc_cache_conn *conn; + struct ble_gattc_cache_conn_op *op; + int rc; + ble_uuid_t uuid = {0}; + + conn = ble_gattc_cache_conn_find(conn_handle); + if (conn == NULL) { + BLE_HS_LOG(DEBUG, "No connection in the Cache" + "HANDLE=%d\n", + conn_handle); + return BLE_HS_ENOTCONN; + } + + rc = ble_gattc_cache_conn_verify(conn); + if (rc != 0) { + return rc; + } + + CHECK_CACHE_CONN_STATE(conn->cache_state, cb, cb_arg, BLE_GATT_OP_DISC_ALL_DSCS, + start_handle, end_handle, uuid); + /* put the event in the queue to mimic the gattc behaviour */ + ble_npl_event_init(&conn->disc_ev, ble_gattc_cache_conn_search_all_dscs_cb, &conn->conn_handle); + ble_npl_eventq_put((struct ble_npl_eventq *)ble_hs_evq_get(), &conn->disc_ev); + return 0; +} +#endif diff --git a/nimble/host/src/ble_gattc_cache_priv.h b/nimble/host/src/ble_gattc_cache_priv.h new file mode 100644 index 000000000..85b9c8fb2 --- /dev/null +++ b/nimble/host/src/ble_gattc_cache_priv.h @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_GATTC_CACHE_PRIV_ +#define H_BLE_GATTC_CACHE_PRIV_ + +#include "modlog/modlog.h" +#include "sys/queue.h" +#include "host/ble_gatt.h" +#include "nimble/ble.h" +#include "services/gatt/ble_svc_gatt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MYNEWT_VAL(BLE_GATT_CACHING) +#define BLE_GATTC_DATABASE_HASH_UUID128 0x2b2a +enum { + BLE_GATT_ATTR_TYPE_INCL_SRVC, + BLE_GATT_ATTR_TYPE_CHAR, + BLE_GATT_ATTR_TYPE_CHAR_DESCR, + BLE_GATT_ATTR_TYPE_SRVC +}; +typedef uint8_t ble_gatt_attr_type; + +typedef struct ble_gatt_nv_attr { + uint16_t s_handle; + uint16_t e_handle; /* used for service only */ + ble_gatt_attr_type attr_type; + ble_uuid_any_t uuid; + uint8_t properties; /* used for characteristic only */ + unsigned int is_primary : 1; /* used for service only */ +} ble_gatt_nv_attr; + +/* cache conn */ +/** ble_gattc_cache_conn. */ +struct ble_gattc_cache_conn_dsc { + SLIST_ENTRY(ble_gattc_cache_conn_dsc) next; + struct ble_gatt_dsc dsc; +}; +SLIST_HEAD(ble_gattc_cache_conn_dsc_list, ble_gattc_cache_conn_dsc); + +struct ble_gattc_cache_conn_chr { + SLIST_ENTRY(ble_gattc_cache_conn_chr) next; + struct ble_gatt_chr chr; + + struct ble_gattc_cache_conn_dsc_list dscs; +}; +SLIST_HEAD(ble_gattc_cache_conn_chr_list, ble_gattc_cache_conn_chr); + +struct ble_gattc_cache_conn_svc { + SLIST_ENTRY(ble_gattc_cache_conn_svc) next; + /** + * One of the following: + * o BLE_GATT_SVC_TYPE_PRIMARY - primary service + * o BLE_GATT_SVC_TYPE_SECONDARY - secondary service + */ + uint8_t type; + struct ble_gatt_svc svc; + + struct ble_gattc_cache_conn_chr_list chrs; +}; +SLIST_HEAD(ble_gattc_cache_conn_svc_list, ble_gattc_cache_conn_svc); + +struct ble_gattc_cache_conn; +typedef void ble_gattc_cache_conn_disc_fn(const struct ble_gattc_cache_conn *ble_gattc_cache_conn, int status, void *arg); +enum { + CACHE_INVALID = 0, + CACHE_LOADED, + CACHE_VERIFIED, + SVC_DISC_IN_PROGRESS, + CHR_DISC_IN_PROGRESS, + INC_DISC_IN_PROGRESS, + DSC_DISC_IN_PROGRESS, + VERIFY_IN_PROGRESS +}; +struct ble_gattc_cache_conn_op { + /* cb is used only when the gattc + request comes while the cache is building */ + uint16_t start_handle; + uint16_t end_handle; + ble_uuid_t uuid; + void *cb; + void *cb_arg; + uint8_t cb_type; +}; +struct ble_gattc_cache_conn { + SLIST_ENTRY(ble_gattc_cache_conn) next; + + uint16_t conn_handle; + ble_addr_t ble_gattc_cache_conn_addr; + + uint8_t database_hash[16]; + + /** List of discovered GATT services. */ + struct ble_gattc_cache_conn_svc_list svcs; + + uint8_t cache_state; + /** Keeps track of where we are in the service discovery process. */ + uint16_t disc_prev_chr_val; + struct ble_gattc_cache_conn_svc *cur_svc; + + struct ble_gattc_cache_conn_op pending_op; + /* event to be posted to inform + the application about the discovery results */ + struct ble_npl_event disc_ev; +}; +/* apis from gatt service */ +uint16_t ble_svc_gatt_changed_handle(); +uint16_t ble_svc_gatt_hash_handle(); +uint16_t ble_svc_gatt_csf_handle(); +uint8_t ble_svc_gatt_get_csfs(); + +#if MYNEWT_VAL(BLE_DYNAMIC_SERVICE) +void ble_svc_gatt_changed(uint16_t start_handle, uint16_t end_handle); +#endif +int ble_gattc_cache_conn_add(uint16_t conn_handle, ble_addr_t ble_gattc_cache_conn_addr); +int ble_gattc_cache_conn_svc_add(ble_addr_t peer_addr, const struct ble_gatt_svc *gatt_svc); +int ble_gattc_cache_conn_inc_add(ble_addr_t peer_addr, const struct ble_gatt_svc *gatt_svc); +int ble_gattc_cache_conn_chr_add(ble_addr_t peer_addr, uint16_t svc_start_handle, + const struct ble_gatt_chr *gatt_chr); +int ble_gattc_cache_conn_dsc_add(ble_addr_t peer_addr, uint16_t chr_val_handle, + const struct ble_gatt_dsc *gatt_dsc); +/** + * Loads the cache for the connection given by conn_handle + */ +int ble_gattc_cache_conn_create(uint16_t conn_handle, ble_addr_t ble_gattc_cache_conn_addr); + +void ble_gattc_cache_conn_load_hash(ble_addr_t peer_addr, uint8_t *hash_key); +void ble_gattc_cache_conn_update(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle); +uint16_t ble_gattc_cache_conn_get_svc_changed_handle(uint16_t conn_handle); + +/* cache store */ +void ble_gattc_cache_save(struct ble_gattc_cache_conn *peer, size_t num_attr); +int ble_gattc_cache_init(void *storage_cb); +int ble_gattc_cache_load(ble_addr_t peer_addr); +int ble_gattc_cache_check_hash(struct ble_gattc_cache_conn *peer, struct os_mbuf *om); +void ble_gattc_cacheReset(ble_addr_t *addr); +void ble_gattc_cache_conn_broken(uint16_t conn_handle); +void ble_gattc_cache_conn_bonding_established(uint16_t conn_handle); +void ble_gattc_cache_conn_bonding_restored(uint16_t conn_handle); + +/* cache search */ +int ble_gattc_cache_conn_search_all_svcs(uint16_t conn_handle, + ble_gatt_disc_svc_fn *cb, void *cb_arg); +int ble_gattc_cache_conn_search_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid, + ble_gatt_disc_svc_fn *cb, void *cb_arg); +int ble_gattc_cache_conn_search_inc_svcs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, + ble_gatt_disc_svc_fn *cb, void *cb_arg); +int ble_gattc_cache_conn_search_all_chrs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, ble_gatt_chr_fn *cb, + void *cb_arg); +int ble_gattc_cache_conn_search_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid, + ble_gatt_chr_fn *cb, void *cb_arg); +int +ble_gattc_cache_conn_search_all_dscs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, + ble_gatt_dsc_fn *cb, void *cb_arg); + +#ifdef __cplusplus +} +#endif + +#endif +#endif diff --git a/nimble/host/src/ble_gatts.c b/nimble/host/src/ble_gatts.c index 43467437d..6aef981c0 100644 --- a/nimble/host/src/ble_gatts.c +++ b/nimble/host/src/ble_gatts.c @@ -43,6 +43,12 @@ enum { }; #endif +#if MYNEWT_VAL(BLE_GATT_CACHING) +/* store the aware state only for the bonded peers */ +struct ble_gatts_aware_state ble_gatts_conn_aware_states[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; +/* index of latest bonded peer */ +static int last_conn_aware_state_index; +#endif static const ble_uuid_t *uuid_pri = BLE_UUID16_DECLARE(BLE_ATT_UUID_PRIMARY_SERVICE); static const ble_uuid_t *uuid_sec = @@ -571,6 +577,42 @@ ble_gatts_svc_incs_satisfied(const struct ble_gatt_svc_def *svc) return 1; } + +#if MYNEWT_VAL(BLE_GATT_CACHING) +int +ble_gatts_calculate_hash(uint8_t *out_hash_key) +{ + int size; + int rc; + uint8_t *buf; + uint8_t key[16] = {0}; + + /* data with all zeroes */ + rc = ble_att_get_database_size(&size); + if(rc != 0) { + return rc; + } + buf = nimble_platform_mem_malloc(sizeof(uint8_t) * size); + if(buf == NULL) { + rc = BLE_HS_ENOMEM; + return rc; + } + + rc = ble_att_fill_database_info(buf); + if(rc != 0) { + return rc; + } + + rc = ble_sm_alg_aes_cmac(key, buf, size, out_hash_key); + if(rc != 0) { + return rc; + } + + swap_in_place(out_hash_key, 16); + return 0; +} +#endif + static int ble_gatts_register_inc(struct ble_gatts_svc_entry *entry) { @@ -1285,6 +1327,11 @@ ble_gatts_connection_broken(uint16_t conn_handle) int i; #endif +#if MYNEWT_VAL(BLE_GATT_CACHING) + struct ble_hs_conn_addrs addrs; + int i; +#endif + /* Find the specified connection and extract its CCCD entries. Extracting * the clt_cfg pointer and setting the original to null is done for two * reasons: @@ -1310,6 +1357,19 @@ ble_gatts_connection_broken(uint16_t conn_handle) conn->bhc_gatt_svr.clt_cfgs = NULL; #endif conn->bhc_gatt_svr.num_clt_cfgs = 0; + +#if MYNEWT_VAL(BLE_GATT_CACHING) + /* update bonded peer aware state */ + if(conn->bhc_sec_state.bonded) { + ble_hs_conn_addrs(conn, &addrs); + for(i = 0; i < MYNEWT_VAL(BLE_STORE_MAX_BONDS); i++) { + if(memcmp(ble_gatts_conn_aware_states[i].peer_id_addr, + addrs.peer_id_addr.val, sizeof addrs.peer_id_addr.val)) { + ble_gatts_conn_aware_states[i].aware = conn->bhc_gatt_svr.aware_state; + } + } + } +#endif } ble_hs_unlock(); @@ -1744,6 +1804,11 @@ ble_gatts_rx_indicate_ack(uint16_t conn_handle, uint16_t chr_val_handle) #endif int persist; int rc; +#if MYNEWT_VAL(BLE_GATT_CACHING) + uint16_t svc_change_handle; + + svc_change_handle = ble_svc_gatt_changed_handle(); +#endif #if MYNEWT_VAL(BLE_DYNAMIC_SERVICE) clt_cfg = ble_gatts_clt_cfg_find(&ble_gatts_clt_cfgs, @@ -1782,6 +1847,11 @@ ble_gatts_rx_indicate_ack(uint16_t conn_handle, uint16_t chr_val_handle) * been modified since we sent the indication, there is no indication * pending. */ +#if MYNEWT_VAL(BLE_GATT_CACHING) + if(chr_val_handle == svc_change_handle) { + conn->bhc_gatt_svr.aware_state = true; + } +#endif #if MYNEWT_VAL(BLE_DYNAMIC_SERVICE) clt_cfg = ble_gatts_clt_cfg_find(&conn->bhc_gatt_svr.clt_cfgs, chr_val_handle); BLE_HS_DBG_ASSERT(clt_cfg != NULL); @@ -1943,6 +2013,92 @@ ble_gatts_chr_updated(uint16_t chr_val_handle) } } +int +ble_gatts_peer_cl_sup_feat_get(uint16_t conn_handle, uint8_t *out_supported_feat, uint8_t len) +{ + struct ble_hs_conn *conn; + int rc = 0; + + if (out_supported_feat == NULL) { + return BLE_HS_EINVAL; + } + + ble_hs_lock(); + conn = ble_hs_conn_find(conn_handle); + if (conn == NULL) { + rc = BLE_HS_ENOTCONN; + goto done; + } + + if (BLE_GATT_CHR_CLI_SUP_FEAT_SZ < len) { + len = BLE_GATT_CHR_CLI_SUP_FEAT_SZ; + } + + memcpy(out_supported_feat, conn->bhc_gatt_svr.peer_cl_sup_feat, + sizeof(uint8_t) * len); + +done: + ble_hs_unlock(); + return rc; +} + +int +ble_gatts_peer_cl_sup_feat_update(uint16_t conn_handle, struct os_mbuf *om) +{ + struct ble_hs_conn *conn; + uint8_t feat[BLE_GATT_CHR_CLI_SUP_FEAT_SZ] = {}; + uint16_t len; + int rc = 0; + int i; + int rfu_mask = 7; + + BLE_HS_LOG(DEBUG, ""); + + if (!om) { + return BLE_HS_EINVAL; + } + + /* RFU bits are ignored so we can skip any bytes larger than supported */ + len = os_mbuf_len(om); + if (len > BLE_GATT_CHR_CLI_SUP_FEAT_SZ) { + len = BLE_GATT_CHR_CLI_SUP_FEAT_SZ; + } + + if (os_mbuf_copydata(om, 0, len, feat) < 0) { + return BLE_ATT_ERR_UNLIKELY; + } + + /* clear RFU bits */ + for (i = 0; i < BLE_GATT_CHR_CLI_SUP_FEAT_SZ; i++) { + feat[i] &= (BLE_GATT_CHR_CLI_SUP_FEAT_MASK >> (8 * i)); + } + + ble_hs_lock(); + conn = ble_hs_conn_find(conn_handle); + if (conn == NULL) { + rc = BLE_HS_ENOTCONN; + goto done; + } + + /** + * Disabling already enabled features is not permitted + * (Vol. 3, Part F, 3.3.3) + */ + for (i = 0; i < BLE_GATT_CHR_CLI_SUP_FEAT_SZ; i++) { + if ((conn->bhc_gatt_svr.peer_cl_sup_feat[i] & feat[i]) != + conn->bhc_gatt_svr.peer_cl_sup_feat[i]) { + rc = BLE_ATT_ERR_VALUE_NOT_ALLOWED; + goto done; + } + } + + memcpy(conn->bhc_gatt_svr.peer_cl_sup_feat, feat, BLE_GATT_CHR_CLI_SUP_FEAT_SZ); + +done: + ble_hs_unlock(); + return rc; +} + /** * Sends notifications or indications for the specified characteristic to all * connected devices. The bluetooth spec does not allow more than one @@ -2058,6 +2214,10 @@ ble_gatts_bonding_established(uint16_t conn_handle) #if !MYNEWT_VAL(BLE_DYNAMIC_SERVICE) int i; #endif +#if MYNEWT_VAL(BLE_GATT_CACHING) + struct ble_hs_conn_addrs addrs; + int new_idx; +#endif ble_hs_lock(); @@ -2092,6 +2252,19 @@ ble_gatts_bonding_established(uint16_t conn_handle) } } +#if MYNEWT_VAL(BLE_GATT_CACHING) + /* store the bonded peer aware_state + if space not available delete the + oldest bond */ + ble_hs_conn_addrs(conn, &addrs); + new_idx = (last_conn_aware_state_index + 1) % + MYNEWT_VAL(BLE_STORE_MAX_BONDS); + memset(&ble_gatts_conn_aware_states[new_idx], 0, + sizeof(struct ble_gatts_aware_state)); + memcpy(ble_gatts_conn_aware_states[new_idx].peer_id_addr, + addrs.peer_id_addr.val, sizeof addrs.peer_id_addr.val); + last_conn_aware_state_index = new_idx; +#endif ble_hs_unlock(); } @@ -2112,6 +2285,10 @@ ble_gatts_bonding_restored(uint16_t conn_handle) struct ble_hs_conn *conn; uint8_t att_op; int rc; +#if MYNEWT_VAL(BLE_GATT_CACHING) + struct ble_hs_conn_addrs addrs; + int i; +#endif ble_hs_lock(); @@ -2125,6 +2302,16 @@ ble_gatts_bonding_restored(uint16_t conn_handle) cccd_key.chr_val_handle = 0; cccd_key.idx = 0; +#if MYNEWT_VAL(BLE_GATT_CACHING) + /* update the aware state of the client */ + ble_hs_conn_addrs(conn, &addrs); + for(i = 0; i < MYNEWT_VAL(BLE_STORE_MAX_BONDS); i++) { + if(memcmp(ble_gatts_conn_aware_states[i].peer_id_addr, + addrs.peer_id_addr.val, sizeof addrs.peer_id_addr.val)) { + conn->bhc_gatt_svr.aware_state = ble_gatts_conn_aware_states[i].aware; + } + } +#endif ble_hs_unlock(); while (1) { @@ -2393,12 +2580,20 @@ static void ble_gatts_remove_clt_cfg(struct ble_gatts_clt_cfg_list *clt_cfgs, ui } } +#if MYNEWT_VAL(BLE_GATT_CACHING) +static int +ble_gatts_conn_unaware(struct ble_hs_conn *conn, void *arg) { + conn->bhc_gatt_svr.aware_state = false; + return 0; +} +#endif + /* takes two arguments arg[0] : added/removed arg[1] : affected chr_val_handle arg[2] : allowed_flags */ -static int update_conn_clt_cfg(struct ble_hs_conn *conn, void *arg) { +static int ble_gatts_update_conn_clt_cfg(struct ble_hs_conn *conn, void *arg) { uint16_t action = ((uint16_t *) arg)[0]; uint16_t chr_val_handle = ((uint16_t *) arg)[1]; uint16_t allowed_flags; @@ -2474,10 +2669,9 @@ int ble_gatts_add_dynamic_svcs(const struct ble_gatt_svc_def *svcs) { arg[0] = CONN_CLT_CFG_ADD; arg[1] = ha->ha_handle_id + 1; arg[2] = allowed_flags; - ble_hs_conn_foreach(update_conn_clt_cfg, arg); + ble_hs_conn_foreach(ble_gatts_update_conn_clt_cfg, arg); } } - /* send service change indication */ i = 0; entry = ble_gatts_find_svc_entry(&svcs[i]); start_handle = entry->handle; @@ -2486,6 +2680,15 @@ int ble_gatts_add_dynamic_svcs(const struct ble_gatt_svc_def *svcs) { } entry = ble_gatts_find_svc_entry(&svcs[i - 1]); end_handle = entry->end_group_handle; +#if MYNEWT_VAL(BLE_GATT_CACHING) + /* make all bonded connections unaware */ + for(i = 0; i < MYNEWT_VAL(BLE_STORE_MAX_BONDS); i++) { + ble_gatts_conn_aware_states[i].aware = false; + } + ble_hs_conn_foreach(ble_gatts_conn_unaware, NULL); +#endif + + /* send service change indication */ ble_svc_gatt_changed(start_handle, end_handle); done: ble_hs_unlock(); @@ -2538,6 +2741,10 @@ int ble_gatts_delete_svc(const ble_uuid_t *uuid) { ble_uuid16_t uuid_chr = BLE_UUID16_INIT(BLE_ATT_UUID_CHARACTERISTIC); struct ble_att_svr_entry *ha; uint16_t start_handle, end_handle; +#if MYNEWT_VAL(BLE_GATT_CACHING) + int i; +#endif + /* Update the cache. and connections*/ ble_hs_lock(); entry = ble_gatts_find_svc_entry_by_uuid(uuid); @@ -2560,7 +2767,7 @@ int ble_gatts_delete_svc(const ble_uuid_t *uuid) { /* update connections */ arg[0] = CONN_CLT_CFG_REMOVE; arg[1] = chr_val_handle; - ble_hs_conn_foreach(update_conn_clt_cfg, arg); + ble_hs_conn_foreach(ble_gatts_update_conn_clt_cfg, arg); } } /* keep the start handle and end handle before deleting the service */ @@ -2573,6 +2780,14 @@ int ble_gatts_delete_svc(const ble_uuid_t *uuid) { done: if (rc == 0) { rc = ble_gatts_remove_svc_entry(uuid); +#if MYNEWT_VAL(BLE_GATT_CACHING) + /* make all bonded connections them unaware */ + for(i = 0; i < MYNEWT_VAL(BLE_STORE_MAX_BONDS); i++) { + ble_gatts_conn_aware_states[i].aware = false; + } + ble_hs_conn_foreach(ble_gatts_conn_unaware, NULL); +#endif + /* send service change indication */ ble_svc_gatt_changed(start_handle, end_handle); } @@ -2848,7 +3063,10 @@ ble_gatts_init(void) #if MYNEWT_VAL(BLE_DYNAMIC_SERVICE) STAILQ_INIT(&ble_gatts_svc_entries); #endif +#if MYNEWT_VAL(BLE_GATT_CACHING) + memset(ble_gatts_conn_aware_states, 0, sizeof ble_gatts_conn_aware_states); + last_conn_aware_state_index = 0; +#endif return 0; - } diff --git a/nimble/host/src/ble_hs.c b/nimble/host/src/ble_hs.c index 5be54d7a1..286625830 100644 --- a/nimble/host/src/ble_hs.c +++ b/nimble/host/src/ble_hs.c @@ -775,6 +775,11 @@ ble_hs_init(void) rc = ble_gattc_init(); SYSINIT_PANIC_ASSERT(rc == 0); +#if MYNEWT_VAL(BLE_GATT_CACHING) + rc = ble_gattc_cache_conn_init(); + SYSINIT_PANIC_ASSERT(rc == 0); +#endif + rc = ble_gatts_init(); SYSINIT_PANIC_ASSERT(rc == 0); #endif diff --git a/nimble/host/src/ble_hs_priv.h b/nimble/host/src/ble_hs_priv.h index 27dc1fd8c..c5781ccc9 100644 --- a/nimble/host/src/ble_hs_priv.h +++ b/nimble/host/src/ble_hs_priv.h @@ -44,6 +44,9 @@ #include "host/ble_hs.h" #include "nimble/nimble_opt.h" #include "stats/stats.h" +#if MYNEWT_VAL(BLE_GATT_CACHING) +#include "ble_gattc_cache_priv.h" +#endif #ifdef __cplusplus extern "C" { #endif diff --git a/nimble/host/src/ble_sm_alg.c b/nimble/host/src/ble_sm_alg.c index 4e9218dae..e0dc71c5d 100644 --- a/nimble/host/src/ble_sm_alg.c +++ b/nimble/host/src/ble_sm_alg.c @@ -260,7 +260,7 @@ ble_sm_alg_log_buf(const char *name, const uint8_t *buf, int len) */ #if MYNEWT_VAL(BLE_CRYPTO_STACK_MBEDTLS) -static int +int ble_sm_alg_aes_cmac(const uint8_t *key, const uint8_t *in, size_t len, uint8_t *out) { @@ -298,7 +298,7 @@ exit: } #else -static int +int ble_sm_alg_aes_cmac(const uint8_t *key, const uint8_t *in, size_t len, uint8_t *out) { diff --git a/nimble/host/src/ble_sm_priv.h b/nimble/host/src/ble_sm_priv.h index b6a356126..d0b97cb39 100644 --- a/nimble/host/src/ble_sm_priv.h +++ b/nimble/host/src/ble_sm_priv.h @@ -424,6 +424,8 @@ int ble_sm_init(void); struct ble_l2cap_chan *ble_sm_create_chan(uint16_t handle); void *ble_sm_cmd_get(uint8_t opcode, size_t len, struct os_mbuf **txom); int ble_sm_tx(uint16_t conn_handle, struct os_mbuf *txom); +int ble_sm_alg_aes_cmac(const uint8_t *key, const uint8_t *in, size_t len, uint8_t *out); + #ifdef __cplusplus } diff --git a/porting/nimble/include/nimble/storage_port.h b/porting/nimble/include/nimble/storage_port.h new file mode 100644 index 000000000..0c935fde3 --- /dev/null +++ b/porting/nimble/include/nimble/storage_port.h @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _STORAGE_PORT_H +#define _STORAGE_PORT_H + +#include + +typedef enum { + READONLY, + READWRITE +} open_mode_t; + +typedef uint32_t cache_handle_t; +typedef int (*open_cache)(const char *namespace_name, open_mode_t open_mode, cache_handle_t *out_handle); +typedef void (*close_cache)(cache_handle_t handle); +typedef int (*erase_all_cache)(cache_handle_t handle); +typedef int (*write_cache)(cache_handle_t handle, const char *key, const void* value, size_t length); +typedef int (*read_cache)(cache_handle_t handle, const char *key, void* out_value, size_t *length); + +struct cache_fn_mapping { + open_cache open; + close_cache close; + erase_all_cache erase_all; + write_cache write; + read_cache read; +}; + +struct cache_fn_mapping link_storage_fn(void *storage_cb); + +#endif