[PATCH BlueZ 10/18] core: gatt: Make CCC addition API public

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

 



This patch adds the btd_gatt_database_add_ccc function to the
database's public API. The signature has been extended to accept
a callback that gets invoked to notify the upper layer when a CCC write
is performed. The result is cached by the database on a per-device basis
while the callback is invoked for all writes from all devices.
---
 src/gatt-database.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++------
 src/gatt-database.h |  11 ++++++
 2 files changed, 109 insertions(+), 12 deletions(-)

diff --git a/src/gatt-database.c b/src/gatt-database.c
index e876141..ebfcf2d 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -59,6 +59,7 @@ struct btd_gatt_database {
 	uint32_t gap_handle;
 	uint32_t gatt_handle;
 	struct queue *device_states;
+	struct queue *ccc_callbacks;
 	struct gatt_db_attribute *svc_chngd;
 	struct gatt_db_attribute *svc_chngd_ccc;
 };
@@ -74,11 +75,48 @@ struct ccc_state {
 	uint8_t value[2];
 };
 
+struct ccc_cb_data {
+	uint16_t handle;
+	btd_gatt_database_ccc_write_t callback;
+	btd_gatt_database_destroy_t destroy;
+	void *user_data;
+};
+
 struct device_info {
 	bdaddr_t bdaddr;
 	uint8_t bdaddr_type;
 };
 
+static void ccc_cb_free(void *data)
+{
+	struct ccc_cb_data *ccc_cb = data;
+
+	if (ccc_cb->destroy)
+		ccc_cb->destroy(ccc_cb->user_data);
+
+	free(ccc_cb);
+}
+
+static bool ccc_cb_match_service(const void *data, const void *match_data)
+{
+	const struct ccc_cb_data *ccc_cb = data;
+	const struct gatt_db_attribute *attrib = match_data;
+	uint16_t start, end;
+
+	if (!gatt_db_attribute_get_service_handles(attrib, &start, &end))
+		return false;
+
+	return ccc_cb->handle >= start && ccc_cb->handle <= end;
+}
+
+static bool ccc_cb_match_handle(const void *data, const void *match_data)
+{
+	const struct ccc_cb_data *ccc_cb = data;
+	uint16_t handle = PTR_TO_UINT(match_data);
+
+	return ccc_cb->handle == handle;
+}
+
 static bool dev_state_match(const void *a, const void *b)
 {
 	const struct device_state *dev_state = a;
@@ -218,6 +256,9 @@ static void gatt_database_free(void *data)
 
 	/* TODO: Persistently store CCC states before freeing them */
 	queue_destroy(database->device_states, device_state_free);
+	queue_destroy(database->ccc_callbacks, ccc_cb_free);
+	database->device_states = NULL;
+	database->ccc_callbacks = NULL;
 	gatt_db_unregister(database->db, database->db_id);
 	gatt_db_unref(database->db);
 	btd_adapter_unref(database->adapter);
@@ -510,6 +551,7 @@ static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
 {
 	struct btd_gatt_database *database = user_data;
 	struct ccc_state *ccc;
+	struct ccc_cb_data *ccc_cb;
 	uint16_t handle;
 	uint8_t ecode = 0;
 	bdaddr_t bdaddr;
@@ -540,22 +582,38 @@ static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
 		goto done;
 	}
 
-	/*
-	 * TODO: Perform this after checking with a callback to the upper
-	 * layer.
-	 */
-	ccc->value[0] = value[0];
-	ccc->value[1] = value[1];
+	ccc_cb = queue_find(database->ccc_callbacks, ccc_cb_match_handle,
+			UINT_TO_PTR(gatt_db_attribute_get_handle(attrib)));
+	if (!ccc_cb) {
+		ecode = BT_ATT_ERROR_UNLIKELY;
+		goto done;
+	}
+
+	/* If value is identical, then just succeed */
+	if (ccc->value[0] == value[0] && ccc->value[1] == value[1])
+		goto done;
+
+	if (ccc_cb->callback)
+		ecode = ccc_cb->callback(get_le16(value), ccc_cb->user_data);
+
+	if (!ecode) {
+		ccc->value[0] = value[0];
+		ccc->value[1] = value[1];
+	}
 
 done:
 	gatt_db_attribute_write_result(attrib, id, ecode);
 }
 
-static struct gatt_db_attribute *
-gatt_database_add_ccc(struct btd_gatt_database *database,
-							uint16_t service_handle)
+struct gatt_db_attribute *
+btd_gatt_database_add_ccc(struct btd_gatt_database *database,
+				uint16_t service_handle,
+				btd_gatt_database_ccc_write_t write_callback,
+				void *user_data,
+				btd_gatt_database_destroy_t destroy)
 {
-	struct gatt_db_attribute *service;
+	struct gatt_db_attribute *service, *ccc;
+	struct ccc_cb_data *ccc_cb;
 	bt_uuid_t uuid;
 
 	if (!database || !service_handle)
@@ -567,10 +625,30 @@ gatt_database_add_ccc(struct btd_gatt_database *database,
 		return NULL;
 	}
 
+	ccc_cb = new0(struct ccc_cb_data, 1);
+	if (!ccc_cb) {
+		error("Could not allocate memory for callback data");
+		return NULL;
+	}
+
 	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
-	return gatt_db_service_add_descriptor(service, &uuid,
+	ccc = gatt_db_service_add_descriptor(service, &uuid,
 				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
 				gatt_ccc_read_cb, gatt_ccc_write_cb, database);
+	if (!ccc) {
+		error("Failed to create CCC entry in database");
+		free(ccc_cb);
+		return NULL;
+	}
+
+	ccc_cb->handle = gatt_db_attribute_get_handle(ccc);
+	ccc_cb->callback = write_callback;
+	ccc_cb->destroy = destroy;
+	ccc_cb->user_data = user_data;
+
+	queue_push_tail(database->ccc_callbacks, ccc_cb);
+
+	return ccc;
 }
 
 static void populate_gatt_service(struct btd_gatt_database *database)
@@ -592,7 +670,9 @@ static void populate_gatt_service(struct btd_gatt_database *database)
 				BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_INDICATE,
 				NULL, NULL, database);
 
-	database->svc_chngd_ccc = gatt_database_add_ccc(database, start_handle);
+	database->svc_chngd_ccc = btd_gatt_database_add_ccc(database,
+							start_handle,
+							NULL, NULL, NULL);
 
 	gatt_db_service_set_active(service, true);
 }
@@ -744,6 +824,8 @@ static void gatt_db_service_removed(struct gatt_db_attribute *attrib,
 	send_service_changed(database, attrib);
 
 	queue_foreach(database->device_states, remove_device_ccc, attrib);
+	queue_remove_all(database->ccc_callbacks, ccc_cb_match_service, attrib,
+								ccc_cb_free);
 }
 
 struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
@@ -768,6 +850,10 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
 	if (!database->device_states)
 		goto fail;
 
+	database->ccc_callbacks = queue_new();
+	if (!database->ccc_callbacks)
+		goto fail;
+
 	database->db_id = gatt_db_register(database->db, gatt_db_service_added,
 							gatt_db_service_removed,
 							database, NULL);
diff --git a/src/gatt-database.h b/src/gatt-database.h
index 0d9106b..163b601 100644
--- a/src/gatt-database.h
+++ b/src/gatt-database.h
@@ -23,3 +23,14 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter);
 void btd_gatt_database_destroy(struct btd_gatt_database *database);
 
 struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database);
+
+typedef uint8_t (*btd_gatt_database_ccc_write_t) (uint16_t value,
+							void *user_data);
+typedef void (*btd_gatt_database_destroy_t) (void *data);
+
+struct gatt_db_attribute *
+btd_gatt_database_add_ccc(struct btd_gatt_database *database,
+				uint16_t service_handle,
+				btd_gatt_database_ccc_write_t write_callback,
+				void *user_data,
+				btd_gatt_database_destroy_t destroy);
-- 
2.2.0.rc0.207.ga3a616c

--
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