From 3be9980bd623b3d62299b5c8279f13b73595d29c Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 30 Dec 2019 13:18:28 -0800 Subject: [PATCH] Bluetooth: GATT: Remove _peer member from bt_gatt_subscribe_params This further reduce the overhead on each subscription at expense of having a dedicated array to store subscriptions, the code now maintain a separate list for each peer which should also scale better with large number of subscriptions to different peers. Signed-off-by: Luiz Augusto von Dentz --- include/bluetooth/gatt.h | 1 - subsys/bluetooth/host/gatt.c | 181 +++++++++++++++++++++++++---------- 2 files changed, 131 insertions(+), 51 deletions(-) diff --git a/include/bluetooth/gatt.h b/include/bluetooth/gatt.h index 4b9750866ee..7108aebd823 100644 --- a/include/bluetooth/gatt.h +++ b/include/bluetooth/gatt.h @@ -1303,7 +1303,6 @@ enum { /** @brief GATT Subscribe parameters */ struct bt_gatt_subscribe_params { - bt_addr_le_t _peer; /** Notification value callback */ bt_gatt_notify_func_t notify; /** Subscribe value handle */ diff --git a/subsys/bluetooth/host/gatt.c b/subsys/bluetooth/host/gatt.c index a08462b4bf8..e1260484f8b 100644 --- a/subsys/bluetooth/host/gatt.c +++ b/subsys/bluetooth/host/gatt.c @@ -59,7 +59,12 @@ struct ccc_store { }; #if defined(CONFIG_BT_GATT_CLIENT) -static sys_slist_t subscriptions; +struct gatt_sub { + bt_addr_le_t peer; + sys_slist_t list; +}; + +static struct gatt_sub subscriptions[CONFIG_BT_MAX_PAIRED + CONFIG_BT_MAX_CONN]; #endif /* CONFIG_BT_GATT_CLIENT */ static const u16_t gap_appearance = CONFIG_BT_DEVICE_APPEARANCE; @@ -2181,16 +2186,64 @@ bool bt_gatt_is_subscribed(struct bt_conn *conn, } #if defined(CONFIG_BT_GATT_CLIENT) +static struct gatt_sub *gatt_sub_find_free(struct bt_conn *conn, + struct gatt_sub **free_sub) +{ + int i; + + if (free_sub) { + *free_sub = NULL; + } + + for (i = 0; i < ARRAY_SIZE(subscriptions); i++) { + struct gatt_sub *sub = &subscriptions[i]; + + if (!bt_conn_addr_le_cmp(conn, &sub->peer)) { + return sub; + } else if (free_sub && + !bt_addr_le_cmp(BT_ADDR_LE_ANY, &sub->peer)) { + *free_sub = sub; + } + } + + return NULL; +} + +#define gatt_sub_find(_conn) \ + gatt_sub_find_free(_conn, NULL) + +static struct gatt_sub *gatt_sub_add(struct bt_conn *conn) +{ + struct gatt_sub *sub, *free_sub; + + sub = gatt_sub_find_free(conn, &free_sub); + if (sub) { + return sub; + } + + if (free_sub) { + bt_addr_le_copy(&free_sub->peer, &conn->le.dst); + return free_sub; + } + + return NULL; +} + void bt_gatt_notification(struct bt_conn *conn, u16_t handle, const void *data, u16_t length) { struct bt_gatt_subscribe_params *params, *tmp; + struct gatt_sub *sub; BT_DBG("handle 0x%04x length %u", handle, length); - SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subscriptions, params, tmp, node) { - if (bt_conn_addr_le_cmp(conn, ¶ms->_peer) || - handle != params->value_handle) { + sub = gatt_sub_find(conn); + if (!sub) { + return; + } + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&sub->list, params, tmp, node) { + if (handle != params->value_handle) { continue; } @@ -2201,46 +2254,54 @@ void bt_gatt_notification(struct bt_conn *conn, u16_t handle, } } -static void update_subscription(struct bt_conn *conn, - struct bt_gatt_subscribe_params *params) +static void gatt_sub_update(struct bt_conn *conn, struct gatt_sub *sub) { - if (params->_peer.type == BT_ADDR_LE_PUBLIC) { + if (sub->peer.type == BT_ADDR_LE_PUBLIC) { return; } /* Update address */ - bt_addr_le_copy(¶ms->_peer, &conn->le.dst); + bt_addr_le_copy(&sub->peer, &conn->le.dst); } -static void gatt_subscription_remove(struct bt_conn *conn, sys_snode_t *prev, - struct bt_gatt_subscribe_params *params) +static void gatt_sub_remove(struct bt_conn *conn, struct gatt_sub *sub, + sys_snode_t *prev, + struct bt_gatt_subscribe_params *params) { - /* Remove subscription from the list*/ - sys_slist_remove(&subscriptions, prev, ¶ms->node); + if (params) { + /* Remove subscription from the list*/ + sys_slist_remove(&sub->list, prev, ¶ms->node); + /* Notify removal */ + params->notify(conn, params, NULL, 0); + } - params->notify(conn, params, NULL, 0); + if (sys_slist_is_empty(&sub->list)) { + /* Reset address if there are no subscription left */ + bt_addr_le_copy(&sub->peer, BT_ADDR_LE_ANY); + } } static void remove_subscriptions(struct bt_conn *conn) { + struct gatt_sub *sub; struct bt_gatt_subscribe_params *params, *tmp; sys_snode_t *prev = NULL; - /* Lookup existing subscriptions */ - SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subscriptions, params, tmp, node) { - if (bt_conn_addr_le_cmp(conn, ¶ms->_peer)) { - prev = ¶ms->node; - continue; - } + sub = gatt_sub_find(conn); + if (!sub) { + return; + } + /* Lookup existing subscriptions */ + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&sub->list, params, tmp, node) { if (!bt_addr_le_is_bonded(conn->id, &conn->le.dst) || (atomic_test_bit(params->flags, BT_GATT_SUBSCRIBE_FLAG_VOLATILE))) { /* Remove subscription */ params->value = 0U; - gatt_subscription_remove(conn, prev, params); + gatt_sub_remove(conn, sub, prev, params); } else { - update_subscription(conn, params); + gatt_sub_update(conn, sub); prev = ¶ms->node; } } @@ -3410,15 +3471,6 @@ int bt_gatt_write(struct bt_conn *conn, struct bt_gatt_write_params *params) return gatt_send(conn, buf, gatt_write_rsp, params, NULL); } -static void gatt_subscription_add(struct bt_conn *conn, - struct bt_gatt_subscribe_params *params) -{ - bt_addr_le_copy(¶ms->_peer, &conn->le.dst); - - /* Prepend subscription */ - sys_slist_prepend(&subscriptions, ¶ms->node); -} - static void gatt_write_ccc_rsp(struct bt_conn *conn, u8_t err, const void *pdu, u16_t length, void *user_data) @@ -3431,11 +3483,17 @@ static void gatt_write_ccc_rsp(struct bt_conn *conn, u8_t err, /* if write to CCC failed we remove subscription and notify app */ if (err) { + struct gatt_sub *sub; sys_snode_t *node, *tmp, *prev = NULL; - SYS_SLIST_FOR_EACH_NODE_SAFE(&subscriptions, node, tmp) { + sub = gatt_sub_find(conn); + if (!sub) { + return; + } + + SYS_SLIST_FOR_EACH_NODE_SAFE(&sub->list, node, tmp) { if (node == ¶ms->node) { - gatt_subscription_remove(conn, prev, params); + gatt_sub_remove(conn, sub, tmp, params); break; } @@ -3474,6 +3532,7 @@ static int gatt_write_ccc(struct bt_conn *conn, u16_t handle, u16_t value, int bt_gatt_subscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *params) { + struct gatt_sub *sub; struct bt_gatt_subscribe_params *tmp; bool has_subscription = false; @@ -3486,16 +3545,21 @@ int bt_gatt_subscribe(struct bt_conn *conn, return -ENOTCONN; } + sub = gatt_sub_add(conn); + if (!sub) { + return -ENOMEM; + } + /* Lookup existing subscriptions */ - SYS_SLIST_FOR_EACH_CONTAINER(&subscriptions, tmp, node) { + SYS_SLIST_FOR_EACH_CONTAINER(&sub->list, tmp, node) { /* Fail if entry already exists */ if (tmp == params) { + gatt_sub_remove(conn, sub, NULL, NULL); return -EALREADY; } /* Check if another subscription exists */ - if (!bt_conn_addr_le_cmp(conn, &tmp->_peer) && - tmp->value_handle == params->value_handle && + if (tmp->value_handle == params->value_handle && tmp->value >= params->value) { has_subscription = true; } @@ -3508,6 +3572,7 @@ int bt_gatt_subscribe(struct bt_conn *conn, err = gatt_write_ccc(conn, params->ccc_handle, params->value, gatt_write_ccc_rsp, params); if (err) { + gatt_sub_remove(conn, sub, NULL, NULL); return err; } } @@ -3516,7 +3581,7 @@ int bt_gatt_subscribe(struct bt_conn *conn, * Add subscription before write complete as some implementation were * reported to send notification before reply to CCC write. */ - gatt_subscription_add(conn, params); + sys_slist_prepend(&sub->list, ¶ms->node); return 0; } @@ -3524,6 +3589,7 @@ int bt_gatt_subscribe(struct bt_conn *conn, int bt_gatt_unsubscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *params) { + struct gatt_sub *sub; struct bt_gatt_subscribe_params *tmp, *next; bool has_subscription = false, found = false; sys_snode_t *prev = NULL; @@ -3535,12 +3601,17 @@ int bt_gatt_unsubscribe(struct bt_conn *conn, return -ENOTCONN; } + sub = gatt_sub_find(conn); + if (!sub) { + return -EINVAL; + } + /* Lookup existing subscriptions */ - SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subscriptions, tmp, next, node) { + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&sub->list, tmp, next, node) { /* Remove subscription */ if (params == tmp) { found = true; - sys_slist_remove(&subscriptions, prev, &tmp->node); + sys_slist_remove(&sub->list, prev, &tmp->node); /* Attempt to cancel if write is pending */ if (atomic_test_bit(params->flags, BT_GATT_SUBSCRIBE_FLAG_WRITE_PENDING)) { @@ -3552,8 +3623,7 @@ int bt_gatt_unsubscribe(struct bt_conn *conn, } /* Check if there still remains any other subscription */ - if (!bt_conn_addr_le_cmp(conn, &tmp->_peer) && - tmp->value_handle == params->value_handle) { + if (tmp->value_handle == params->value_handle) { has_subscription = true; } } @@ -3581,14 +3651,16 @@ void bt_gatt_cancel(struct bt_conn *conn, void *params) static void add_subscriptions(struct bt_conn *conn) { + struct gatt_sub *sub; struct bt_gatt_subscribe_params *params; - /* Lookup existing subscriptions */ - SYS_SLIST_FOR_EACH_CONTAINER(&subscriptions, params, node) { - if (bt_conn_addr_le_cmp(conn, ¶ms->_peer)) { - continue; - } + sub = gatt_sub_find(conn); + if (!sub) { + return; + } + /* Lookup existing subscriptions */ + SYS_SLIST_FOR_EACH_CONTAINER(&sub->list, params, node) { if (bt_addr_le_is_bonded(conn->id, &conn->le.dst) && !atomic_test_bit(params->flags, BT_GATT_SUBSCRIBE_FLAG_NO_RESUB)) { @@ -4169,16 +4241,25 @@ static int sc_clear_by_addr(u8_t id, const bt_addr_le_t *addr) static void bt_gatt_clear_subscriptions(const bt_addr_le_t *addr) { #if defined(CONFIG_BT_GATT_CLIENT) + struct gatt_sub *sub = NULL; struct bt_gatt_subscribe_params *params, *tmp; sys_snode_t *prev = NULL; + int i; - SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subscriptions, params, tmp, node) { - if (bt_addr_le_cmp(addr, ¶ms->_peer)) { - prev = ¶ms->node; - continue; + for (i = 0; i < ARRAY_SIZE(subscriptions); i++) { + if (!bt_addr_le_cmp(addr, &subscriptions[i].peer)) { + sub = &subscriptions[i]; + break; } + } + + if (!sub) { + return; + } + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&sub->list, params, tmp, node) { params->value = 0U; - gatt_subscription_remove(NULL, prev, params); + gatt_sub_remove(NULL, sub, prev, params); } #endif /* CONFIG_BT_GATT_CLIENT */ }