From: Arman Uguray <armansito@chromium.org>
To: linux-bluetooth@vger.kernel.org
Cc: Arman Uguray <armansito@chromium.org>
Subject: [PATCH BlueZ 4/8] shared/gatt-client: Use gatt_db in bt_gatt_register_notify
Date: Fri, 28 Nov 2014 09:49:18 -0800 [thread overview]
Message-ID: <1417196962-3876-5-git-send-email-armansito@chromium.org> (raw)
In-Reply-To: <1417196962-3876-1-git-send-email-armansito@chromium.org>
This patch rewrites the notification/indication logic in gatt-client to
use the internal gatt-db.
---
src/shared/gatt-client.c | 203 ++++++++++++++++++++++++++++++++++-------------
1 file changed, 147 insertions(+), 56 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 2dc6735..a26f43a 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -45,27 +45,6 @@
#define GATT_SVC_UUID 0x1801
#define SVC_CHNGD_UUID 0x2a05
-struct chrc_data {
- /* The public characteristic entry. */
- bt_gatt_characteristic_t chrc_external;
-
- /* The private entries. */
- uint16_t ccc_handle;
- int notify_count; /* Reference count of registered notify callbacks */
-
- /* Internal non-const pointer to the descriptor array. We use this
- * internally to modify/free the array, while we expose it externally
- * using the const pointer "descs" field in bt_gatt_characteristic_t.
- */
- bt_gatt_descriptor_t *descs;
-
- /* Pending calls to register_notify are queued here so that they can be
- * processed after a write that modifies the CCC descriptor.
- */
- struct queue *reg_notify_queue;
- unsigned int ccc_write_id;
-};
-
struct bt_gatt_client {
struct bt_att *att;
int ref_count;
@@ -97,6 +76,7 @@ struct bt_gatt_client {
/* List of registered disconnect/notification/indication callbacks */
struct queue *notify_list;
+ struct queue *notify_chrcs;
int next_reg_id;
unsigned int disc_id, notify_id, ind_id;
bool in_notify;
@@ -111,13 +91,26 @@ struct bt_gatt_client {
bool in_svc_chngd;
};
+struct notify_chrc {
+ uint16_t value_handle;
+ uint16_t ccc_handle;
+ uint16_t properties;
+ int notify_count; /* Reference count of registered notify callbacks */
+
+ /* Pending calls to register_notify are queued here so that they can be
+ * processed after a write that modifies the CCC descriptor.
+ */
+ struct queue *reg_notify_queue;
+ unsigned int ccc_write_id;
+};
+
struct notify_data {
struct bt_gatt_client *client;
bool removed;
bool invalid;
unsigned int id;
int ref_count;
- struct chrc_data *chrc;
+ struct notify_chrc *chrc;
bt_gatt_client_notify_id_callback_t callback;
bt_gatt_client_notify_callback_t notify;
void *user_data;
@@ -144,6 +137,76 @@ static void notify_data_unref(void *data)
free(notify_data);
}
+static void find_ccc(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gatt_db_attribute **ccc_ptr = user_data;
+ bt_uuid_t uuid;
+
+ if (*ccc_ptr)
+ return;
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+ if (bt_uuid_cmp(&uuid, gatt_db_attribute_get_type(attr)))
+ return;
+
+ *ccc_ptr = attr;
+}
+
+static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
+ uint16_t value_handle)
+{
+ struct gatt_db_attribute *attr, *ccc;
+ struct notify_chrc *chrc;
+ bt_uuid_t uuid;
+ uint8_t properties;
+
+ /* Check that chrc_value_handle belongs to a known characteristic */
+ attr = gatt_db_get_attribute(client->db, value_handle - 1);
+ if (!attr)
+ return NULL;
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ if (bt_uuid_cmp(&uuid, gatt_db_attribute_get_type(attr)))
+ return NULL;
+
+ if (!gatt_db_attribute_get_char_data(attr, NULL, NULL,
+ &properties, NULL))
+ return NULL;
+
+ /* Find the CCC characteristic */
+ ccc = NULL;
+ gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
+ if (!ccc)
+ return NULL;
+
+ chrc = new0(struct notify_chrc, 1);
+ if (!chrc)
+ return NULL;
+
+ chrc->reg_notify_queue = queue_new();
+ if (!chrc->reg_notify_queue) {
+ free(chrc);
+ return NULL;
+ }
+
+ chrc->value_handle = value_handle;
+ chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
+ chrc->properties = properties;
+
+ queue_push_tail(client->notify_chrcs, chrc);
+
+ return chrc;
+}
+
+static void notify_chrc_free(void *data)
+{
+ struct notify_chrc *chrc = data;
+
+ queue_destroy(chrc->reg_notify_queue, notify_data_unref);
+ free(chrc);
+}
+
static bool match_notify_data_id(const void *a, const void *b)
{
const struct notify_data *notify_data = a;
@@ -174,7 +237,7 @@ struct handle_range {
static bool match_notify_data_handle_range(const void *a, const void *b)
{
const struct notify_data *notify_data = a;
- bt_gatt_characteristic_t *chrc = ¬ify_data->chrc->chrc_external;
+ struct notify_chrc *chrc = notify_data->chrc;
const struct handle_range *range = b;
return chrc->value_handle >= range->start &&
@@ -184,7 +247,7 @@ static bool match_notify_data_handle_range(const void *a, const void *b)
static void mark_notify_data_invalid_if_in_range(void *data, void *user_data)
{
struct notify_data *notify_data = data;
- bt_gatt_characteristic_t *chrc = ¬ify_data->chrc->chrc_external;
+ struct notify_chrc *chrc = notify_data->chrc;
struct handle_range *range = user_data;
if (chrc->value_handle >= range->start &&
@@ -192,6 +255,15 @@ static void mark_notify_data_invalid_if_in_range(void *data, void *user_data)
notify_data->invalid = true;
}
+static bool match_notify_chrc_handle_range(const void *a, const void *b)
+{
+ const struct notify_chrc *chrc = a;
+ const struct handle_range *range = b;
+
+ return chrc->value_handle >= range->start &&
+ chrc->value_handle <= range->end;
+}
+
static void gatt_client_remove_all_notify_in_range(
struct bt_gatt_client *client,
uint16_t start_handle, uint16_t end_handle)
@@ -213,6 +285,19 @@ static void gatt_client_remove_all_notify_in_range(
&range, notify_data_unref);
}
+static void gatt_client_remove_notify_chrcs_in_range(
+ struct bt_gatt_client *client,
+ uint16_t start_handle, uint16_t end_handle)
+{
+ struct handle_range range;
+
+ range.start = start_handle;
+ range.end = end_handle;
+
+ queue_remove_all(client->notify_chrcs, match_notify_chrc_handle_range,
+ &range, notify_chrc_free);
+}
+
struct discovery_op;
typedef void (*discovery_op_complete_func_t)(struct discovery_op *op,
@@ -988,6 +1073,8 @@ static void process_service_changed(struct bt_gatt_client *client,
/* Invalidate and remove all effected notify callbacks */
gatt_client_remove_all_notify_in_range(client, start_handle,
end_handle);
+ gatt_client_remove_notify_chrcs_in_range(client, start_handle,
+ end_handle);
/* Remove all services that overlap the modified range since we'll
* rediscover them
@@ -1206,12 +1293,10 @@ static bool notify_data_write_ccc(struct notify_data *notify_data, bool enable,
/* Try to enable notifications and/or indications based on
* whatever the characteristic supports.
*/
- if (notify_data->chrc->chrc_external.properties &
- BT_GATT_CHRC_PROP_NOTIFY)
+ if (notify_data->chrc->properties & BT_GATT_CHRC_PROP_NOTIFY)
pdu[2] = 0x01;
- if (notify_data->chrc->chrc_external.properties &
- BT_GATT_CHRC_PROP_INDICATE)
+ if (notify_data->chrc->properties & BT_GATT_CHRC_PROP_INDICATE)
pdu[2] |= 0x02;
if (!pdu[2])
@@ -1322,7 +1407,7 @@ static void notify_handler(void *data, void *user_data)
value_handle = get_le16(pdu_data->pdu);
- if (notify_data->chrc->chrc_external.value_handle != value_handle)
+ if (notify_data->chrc->value_handle != value_handle)
return;
if (pdu_data->length > 2)
@@ -1389,6 +1474,7 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
queue_destroy(client->svc_chngd_queue, free);
queue_destroy(client->long_write_queue, long_write_op_unref);
queue_destroy(client->notify_list, notify_data_unref);
+ queue_destroy(client->notify_chrcs, notify_chrc_free);
free(client);
}
@@ -1438,6 +1524,10 @@ struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
if (!client->notify_list)
goto fail;
+ client->notify_chrcs = queue_new();
+ if (!client->notify_chrcs)
+ goto fail;
+
client->notify_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_NOT,
notify_cb, client, NULL);
if (!client->notify_id)
@@ -2379,6 +2469,14 @@ bool bt_gatt_client_write_long_value(struct bt_gatt_client *client,
return true;
}
+static bool match_notify_chrc_value_handle(const void *a, const void *b)
+{
+ const struct notify_chrc *chrc = a;
+ uint16_t value_handle = PTR_TO_UINT(b);
+
+ return chrc->value_handle == value_handle;
+}
+
bool bt_gatt_client_register_notify(struct bt_gatt_client *client,
uint16_t chrc_value_handle,
bt_gatt_client_notify_id_callback_t callback,
@@ -2387,40 +2485,31 @@ bool bt_gatt_client_register_notify(struct bt_gatt_client *client,
bt_gatt_client_destroy_func_t destroy)
{
struct notify_data *notify_data;
- struct service_list *svc_data = NULL;
- struct chrc_data *chrc = NULL;
- struct bt_gatt_service_iter iter;
- const bt_gatt_service_t *service;
+ struct notify_chrc *chrc = NULL;
- if (!client || !chrc_value_handle || !callback)
+ if (!client || !client->db || !chrc_value_handle || !callback)
return false;
if (!bt_gatt_client_is_ready(client) || client->in_svc_chngd)
return false;
- /* Check that chrc_value_handle belongs to a known characteristic */
- if (!bt_gatt_service_iter_init(&iter, client))
- return false;
+ /* Check if a characteristic ref count has been started already */
+ chrc = queue_find(client->notify_chrcs, match_notify_chrc_value_handle,
+ UINT_TO_PTR(chrc_value_handle));
- while (bt_gatt_service_iter_next(&iter, &service)) {
- if (chrc_value_handle >= service->start_handle &&
- chrc_value_handle <= service->end_handle) {
- svc_data = (void *) service;
- break;
- }
- }
-
- if (!svc_data)
- return false;
+ if (!chrc) {
+ /*
+ * Create an entry if the characteristic is known and has a CCC
+ * descriptor.
+ */
+ chrc = notify_chrc_create(client, chrc_value_handle);
+ if (!chrc)
+ return false;
- /*
- * TODO: Lookup characteristic and CCC in database. Add entries for each
- * characteristic to a list on demand.
- */
- return false;
+ }
- /* Check that the characteristic supports notifications/indications */
- if (!chrc || !chrc->ccc_handle || chrc->notify_count == INT_MAX)
+ /* Fail if we've hit the maximum allowed notify sessions */
+ if (chrc->notify_count == INT_MAX)
return false;
notify_data = new0(struct notify_data, 1);
@@ -2435,7 +2524,8 @@ bool bt_gatt_client_register_notify(struct bt_gatt_client *client,
notify_data->user_data = user_data;
notify_data->destroy = destroy;
- /* If a write to the CCC descriptor is in progress, then queue this
+ /*
+ * If a write to the CCC descriptor is in progress, then queue this
* request.
*/
if (chrc->ccc_write_id) {
@@ -2443,7 +2533,8 @@ bool bt_gatt_client_register_notify(struct bt_gatt_client *client,
return true;
}
- /* If the ref count is not zero, then notifications are already enabled.
+ /*
+ * If the ref count is not zero, then notifications are already enabled.
*/
if (chrc->notify_count > 0) {
complete_notify_request(notify_data);
--
2.2.0.rc0.207.ga3a616c
next prev parent reply other threads:[~2014-11-28 17:49 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-11-28 17:49 [PATCH BlueZ 0/8] shared/gatt: Use gatt-db for client role Arman Uguray
2014-11-28 17:49 ` [PATCH BlueZ 1/8] shared/gatt-db: Fix bug in maximum handle check Arman Uguray
2014-11-28 17:49 ` [PATCH BlueZ 2/8] shared/gatt-db: Add helper to get service handles Arman Uguray
2014-11-28 17:49 ` [PATCH BlueZ 3/8] shared/gatt-client: Store services in gatt_db Arman Uguray
2014-12-01 9:45 ` Luiz Augusto von Dentz
2014-12-01 14:50 ` Arman Uguray
2014-12-01 16:02 ` Luiz Augusto von Dentz
2014-12-01 16:37 ` Arman Uguray
2014-12-01 17:19 ` Marcel Holtmann
2014-12-01 17:57 ` Arman Uguray
2014-12-02 10:46 ` Luiz Augusto von Dentz
2014-12-02 15:12 ` Luiz Augusto von Dentz
2014-12-02 19:35 ` Arman Uguray
2014-12-03 0:15 ` Arman Uguray
2014-11-28 17:49 ` Arman Uguray [this message]
2014-11-28 17:49 ` [PATCH BlueZ 5/8] tools/btgatt-server: Add the "services" command Arman Uguray
2014-11-28 17:49 ` [PATCH BlueZ 6/8] tools/btgatt-client: Use gatt-db instead of iterators Arman Uguray
2014-11-28 17:49 ` [PATCH BlueZ 7/8] unit/test-gatt: Use gatt-db for CLIENT tests Arman Uguray
2014-11-28 17:49 ` [PATCH BlueZ 8/8] shared/gatt-client: Remove GATT structs and iterators Arman Uguray
2014-12-01 9:49 ` [PATCH BlueZ 0/8] shared/gatt: Use gatt-db for client role Luiz Augusto von Dentz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1417196962-3876-5-git-send-email-armansito@chromium.org \
--to=armansito@chromium.org \
--cc=linux-bluetooth@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).