diff --git a/src/gatt-database.c b/src/gatt-database.c
index fa10ce8..db21d49 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
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;
struct queue *services;
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;
/* TODO: Persistently store CCC states before freeing them */
queue_destroy(database->device_states, device_state_free);
queue_destroy(database->services, service_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);
{
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;
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)
+service_add_ccc(struct gatt_db_attribute *service,
+ struct btd_gatt_database *database,
+ btd_gatt_database_ccc_write_t write_callback,
+ void *user_data,
+ btd_gatt_database_destroy_t destroy)
+{
+ struct gatt_db_attribute *ccc;
+ struct ccc_cb_data *ccc_cb;
+ bt_uuid_t uuid;
+
+ 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);
+ 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;
+}
+
+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;
- bt_uuid_t uuid;
if (!database || !service_handle)
return NULL;
return NULL;
}
- bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
- return gatt_db_service_add_descriptor(service, &uuid,
- BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
- gatt_ccc_read_cb, gatt_ccc_write_cb, database);
+ return service_add_ccc(service, database, write_callback, user_data,
+ destroy);
}
static void populate_gatt_service(struct btd_gatt_database *database)
{
bt_uuid_t uuid;
struct gatt_db_attribute *service;
- uint16_t start_handle;
/* Add the GATT service */
bt_uuid16_create(&uuid, UUID_GATT);
database->gatt_handle = database_add_record(database, UUID_GATT,
service,
"Generic Attribute Profile");
- gatt_db_attribute_get_service_handles(service, &start_handle, NULL);
bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
database->svc_chngd = gatt_db_service_add_characteristic(service, &uuid,
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 = service_add_ccc(service, database, NULL, NULL,
+ NULL);
gatt_db_service_set_active(service, true);
}
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);
}
static bool match_service_path(const void *a, const void *b)
if (!database->services)
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
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);