[PATCH BlueZ 4/7] shared/gatt-client: Handle incoming not/ind PDUs.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



bt_gatt_client now registers an incoming PDU handler for notification and
indication PDUs. The PDU is parsed and routed to the notify handler registered
with bt_gatt_client_register_notify for the corresponding characteristic value
handle. A confirmation PDU is sent automatically for received indications.

This patch removes the bt_gatt_register function from shared/gatt-helpers.
---
 src/shared/gatt-client.c  | 201 ++++++++++++++++++++++++++++++++++------------
 src/shared/gatt-helpers.c |  67 ----------------
 src/shared/gatt-helpers.h |   9 ---
 3 files changed, 150 insertions(+), 127 deletions(-)

diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index dfd37f8..74fe9e5 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -88,6 +88,9 @@ struct bt_gatt_client {
 	/* List of registered notification/indication callbacks */
 	struct queue *notify_list;
 	int next_reg_id;
+	unsigned int not_id, ind_id;
+	bool in_notify;
+	bool need_notify_cleanup;
 };
 
 static bool gatt_client_add_service(struct bt_gatt_client *client,
@@ -569,6 +572,126 @@ static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
 	return true;
 }
 
+struct notify_data {
+	struct bt_gatt_client *client;
+	bool removed;
+	unsigned int id;
+	int ref_count;
+	struct chrc_data *chrc;
+	bt_gatt_client_notify_id_callback_t callback;
+	bt_gatt_client_notify_callback_t notify;
+	void *user_data;
+	bt_gatt_client_destroy_func_t destroy;
+};
+
+static struct notify_data *notify_data_ref(struct notify_data *notd)
+{
+	__sync_fetch_and_add(&notd->ref_count, 1);
+
+	return notd;
+}
+
+static void notify_data_unref(void *data)
+{
+	struct notify_data *notd = data;
+
+	if (__sync_sub_and_fetch(&notd->ref_count, 1))
+		return;
+
+	if (notd->destroy)
+		notd->destroy(notd->user_data);
+
+	free(notd);
+}
+
+static bool match_notify_data_id(const void *a, const void *b)
+{
+	const struct notify_data *notd = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return notd->id == id;
+}
+
+static bool match_notify_data_removed(const void *a, const void *b)
+{
+	const struct notify_data *notd = a;
+
+	return notd->removed;
+}
+
+struct pdu_data {
+	const void *pdu;
+	uint16_t length;
+};
+
+static bool notify_data_write_ccc(struct notify_data *notd, bool enable);
+
+static void complete_unregister_notify(void *data)
+{
+	struct notify_data *notd = data;
+
+	if (__sync_sub_and_fetch(&notd->chrc->not_ref_count, 1)) {
+		notify_data_unref(notd);
+		return;
+	}
+
+	notify_data_write_ccc(notd, false);
+}
+
+static void notify_handler(void *data, void *user_data)
+{
+	struct notify_data *notd = data;
+	struct pdu_data *pdu_data = user_data;
+	uint16_t value_handle;
+	const uint8_t *value = NULL;
+
+	if (notd->removed)
+		return;
+
+	value_handle = get_le16(pdu_data->pdu);
+
+	if (notd->chrc->chrc.value_handle != value_handle)
+		return;
+
+	if (pdu_data->length > 2)
+		value = pdu_data->pdu + 2;
+
+	if (notd->notify)
+		notd->notify(value_handle, value, pdu_data->length - 2,
+							notd->user_data);
+}
+
+static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length,
+								void *user_data)
+{
+	struct bt_gatt_client *client = user_data;
+	struct pdu_data pdu_data;
+
+	bt_gatt_client_ref(client);
+
+	client->in_notify = true;
+
+	memset(&pdu_data, 0, sizeof(pdu_data));
+	pdu_data.pdu = pdu;
+	pdu_data.length = length;
+
+	queue_foreach(client->notify_list, notify_handler, &pdu_data);
+
+	client->in_notify = false;
+
+	if (client->need_notify_cleanup) {
+		queue_remove_all(client->notify_list, match_notify_data_removed,
+					NULL, complete_unregister_notify);
+		client->need_notify_cleanup = false;
+	}
+
+	if (opcode == BT_ATT_OP_HANDLE_VAL_IND)
+		bt_att_send(client->att, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0,
+							NULL, NULL, NULL);
+
+	bt_gatt_client_unref(client);
+}
+
 struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
 {
 	struct bt_gatt_client *client;
@@ -593,6 +716,23 @@ struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
 		return NULL;
 	}
 
+	client->not_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_NOT,
+						notify_cb, client, NULL);
+	if (!client->not_id) {
+		queue_destroy(client->long_write_queue, NULL);
+		free(client);
+		return NULL;
+	}
+
+	client->ind_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_IND,
+						notify_cb, client, NULL);
+	if (!client->ind_id) {
+		bt_att_unregister(att, client->not_id);
+		queue_destroy(client->long_write_queue, NULL);
+		free(client);
+		return NULL;
+	}
+
 	client->att = bt_att_ref(att);
 
 	gatt_client_init(client, mtu);
@@ -626,8 +766,12 @@ void bt_gatt_client_unref(struct bt_gatt_client *client)
 	if (client->debug_destroy)
 		client->debug_destroy(client->debug_data);
 
+	bt_att_unregister(client->att, client->not_id);
+	bt_att_unregister(client->att, client->ind_id);
+
 	queue_destroy(client->long_write_queue, long_write_op_unref);
 	queue_destroy(client->notify_list, notify_data_unref);
+
 	bt_att_unref(client->att);
 	free(client);
 }
@@ -1449,45 +1593,6 @@ bool bt_gatt_client_write_long_value(struct bt_gatt_client *client,
 	return true;
 }
 
-struct notify_data {
-	struct bt_gatt_client *client;
-	unsigned int id;
-	int ref_count;
-	struct chrc_data *chrc;
-	bt_gatt_client_notify_id_callback_t callback;
-	bt_gatt_client_notify_callback_t notify;
-	void *user_data;
-	bt_gatt_client_destroy_func_t destroy;
-};
-
-static struct notify_data *notify_data_ref(struct notify_data *notd)
-{
-	__sync_fetch_and_add(&notd->ref_count, 1);
-
-	return notd;
-}
-
-static void notify_data_unref(void *data)
-{
-	struct notify_data *notd = data;
-
-	if (__sync_sub_and_fetch(&notd->ref_count, 1))
-		return;
-
-	if (notd->destroy)
-		notd->destroy(notd->user_data);
-
-	free(notd);
-}
-
-static bool match_notify_data_id(const void *a, const void *b)
-{
-	const struct notify_data *notd = a;
-	unsigned int id = PTR_TO_UINT(b);
-
-	return notd->id == id;
-}
-
 static void complete_notify_request(void *data)
 {
 	struct notify_data *notd = data;
@@ -1508,8 +1613,6 @@ static void complete_notify_request(void *data)
 	notd->callback(notd->id, 0, notd->user_data);
 }
 
-static bool notify_data_write_ccc(struct notify_data *notd, bool enable);
-
 static void enable_ccc_callback(uint8_t opcode, const void *pdu,
 					uint16_t length, void *user_data)
 {
@@ -1688,23 +1791,19 @@ bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client,
 
 	notd = queue_find(client->notify_list, match_notify_data_id,
 							UINT_TO_PTR(id));
-	if (!notd)
+	if (!notd || notd->removed)
 		return false;
 
 	assert(notd->chrc->not_ref_count > 0);
 	assert(!notd->chrc->ccc_write_id);
 
-	/* TODO: Delay destruction/removal if we're in the middle of processing
-	 * a notification.
-	 */
-	queue_remove(client->notify_list, notd);
-
-	if (__sync_sub_and_fetch(&notd->chrc->not_ref_count, 1)) {
-		notify_data_unref(notd);
+	if (!client->in_notify) {
+		queue_remove(client->notify_list, notd);
+		complete_unregister_notify(notd);
 		return true;
 	}
 
-	notify_data_write_ccc(notd, false);
-
+	notd->removed = true;
+	client->need_notify_cleanup = true;
 	return true;
 }
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index ede6a67..54876bc 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -911,70 +911,3 @@ bool bt_gatt_discover_descriptors(struct bt_att *att,
 
 	return true;
 }
-
-struct notify_data {
-	struct bt_att *att;
-	bt_gatt_notify_callback_t callback;
-	void *user_data;
-	bt_gatt_destroy_func_t destroy;
-};
-
-static void notify_data_destroy(void *data)
-{
-	struct notify_data *notd = data;
-
-	if (notd->destroy)
-		notd->destroy(notd->user_data);
-
-	free(notd);
-}
-
-static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length,
-								void *user_data)
-{
-	struct notify_data *data = user_data;
-	uint16_t value_handle;
-	const uint8_t *value = NULL;
-
-	value_handle = get_le16(pdu);
-
-	if (length > 2)
-		value = pdu + 2;
-
-	if (data->callback)
-		data->callback(value_handle, value, length - 2, data->user_data);
-
-	if (opcode == BT_ATT_OP_HANDLE_VAL_IND)
-		bt_att_send(data->att, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0,
-							NULL, NULL, NULL);
-}
-
-unsigned int bt_gatt_register(struct bt_att *att, bool indications,
-					bt_gatt_notify_callback_t callback,
-					void *user_data,
-					bt_gatt_destroy_func_t destroy)
-{
-	struct notify_data *data;
-	uint8_t opcode;
-	unsigned int id;
-
-	if (!att)
-		return 0;
-
-	data = new0(struct notify_data, 1);
-	if (!data)
-		return 0;
-
-	data->att = att;
-	data->callback = callback;
-	data->user_data = user_data;
-	data->destroy = destroy;
-
-	opcode = indications ? BT_ATT_OP_HANDLE_VAL_IND : BT_ATT_OP_HANDLE_VAL_NOT;
-
-	id = bt_att_register(att, opcode, notify_cb, data, notify_data_destroy);
-	if (!id)
-		free(data);
-
-	return id;
-}
diff --git a/src/shared/gatt-helpers.h b/src/shared/gatt-helpers.h
index 75ad4b0..c4a6578 100644
--- a/src/shared/gatt-helpers.h
+++ b/src/shared/gatt-helpers.h
@@ -58,10 +58,6 @@ typedef void (*bt_gatt_discovery_callback_t)(bool success, uint8_t att_ecode,
 						struct bt_gatt_result *result,
 						void *user_data);
 
-typedef void (*bt_gatt_notify_callback_t)(uint16_t value_handle,
-					const uint8_t *value, uint16_t length,
-					void *user_data);
-
 bool bt_gatt_exchange_mtu(struct bt_att *att, uint16_t client_rx_mtu,
 					bt_gatt_result_callback_t callback,
 					void *user_data,
@@ -87,8 +83,3 @@ bool bt_gatt_discover_descriptors(struct bt_att *att,
 					bt_gatt_discovery_callback_t callback,
 					void *user_data,
 					bt_gatt_destroy_func_t destroy);
-
-unsigned int bt_gatt_register(struct bt_att *att, bool indications,
-					bt_gatt_notify_callback_t callback,
-					void *user_data,
-					bt_gatt_destroy_func_t destroy);
-- 
2.1.0.rc2.206.gedb03e5

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux