diff --git a/src/gatt-database.c b/src/gatt-database.c
index 42ceefa..7594ee3 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
#define GATT_MANAGER_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHRC_IFACE "org.bluez.GattCharacteristic1"
+#define GATT_DESC_IFACE "org.bluez.GattDescriptor1"
#define UUID_GAP 0x1800
#define UUID_GATT 0x1801
struct gatt_db_attribute *attrib;
uint16_t attr_cnt;
struct queue *chrcs;
+ struct queue *descs;
};
struct external_chrc {
struct external_service *service;
+ char *path;
GDBusProxy *proxy;
uint8_t props;
uint8_t ext_props;
unsigned int ntfy_cnt;
};
+struct external_desc {
+ struct external_service *service;
+ char *chrc_path;
+ GDBusProxy *proxy;
+ struct gatt_db_attribute *attrib;
+ bool handled;
+};
+
struct pending_op {
struct external_chrc *chrc;
unsigned int id;
queue_destroy(chrc->pending_reads, cancel_pending_read);
queue_destroy(chrc->pending_writes, cancel_pending_write);
+ g_free(chrc->path);
+
g_dbus_proxy_set_property_watch(chrc->proxy, NULL, NULL);
g_dbus_proxy_unref(chrc->proxy);
free(chrc);
}
+static void desc_free(void *data)
+{
+ struct external_desc *desc = data;
+
+ g_dbus_proxy_unref(desc->proxy);
+ g_free(desc->chrc_path);
+
+ free(desc);
+}
+
static void service_free(void *data)
{
struct external_service *service = data;
queue_destroy(service->chrcs, chrc_free);
+ queue_destroy(service->descs, desc_free);
gatt_db_remove_service(service->database->db, service->attrib);
}
static struct external_chrc *chrc_create(struct external_service *service,
- GDBusProxy *proxy)
+ GDBusProxy *proxy,
+ const char *path)
{
struct external_chrc *chrc;
return NULL;
}
+ chrc->path = g_strdup(path);
+ if (!chrc->path) {
+ queue_destroy(chrc->pending_reads, NULL);
+ queue_destroy(chrc->pending_writes, NULL);
+ free(chrc);
+ return NULL;
+ }
+
chrc->service = service;
chrc->proxy = g_dbus_proxy_ref(proxy);
return chrc;
}
+static struct external_desc *desc_create(struct external_service *service,
+ GDBusProxy *proxy,
+ const char *chrc_path)
+{
+ struct external_desc *desc;
+
+ desc = new0(struct external_desc, 1);
+ if (!desc)
+ return NULL;
+
+ desc->chrc_path = g_strdup(chrc_path);
+ if (!desc->chrc_path) {
+ free(desc);
+ return NULL;
+ }
+
+ desc->service = service;
+ desc->proxy = g_dbus_proxy_ref(proxy);
+
+ return desc;
+}
+
static bool incr_attr_count(struct external_service *service, uint16_t incr)
{
if (service->attr_cnt > UINT16_MAX - incr)
return true;
}
-static bool parse_service(GDBusProxy *proxy, struct external_service *service)
+static bool parse_path(GDBusProxy *proxy, const char *name, const char **path)
{
DBusMessageIter iter;
- const char *service_path;
- if (!g_dbus_proxy_get_property(proxy, "Service", &iter))
+ if (!g_dbus_proxy_get_property(proxy, name, &iter))
return false;
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
return false;
- dbus_message_iter_get_basic(&iter, &service_path);
+ dbus_message_iter_get_basic(&iter, path);
+
+ return true;
+}
+
+static bool check_service_path(GDBusProxy *proxy,
+ struct external_service *service)
+{
+ const char *service_path;
+
+ if (!parse_path(proxy, "Service", &service_path))
+ return false;
return g_strcmp0(service_path, service->path) == 0;
}
{
struct external_service *service = user_data;
const char *iface, *path;
- struct external_chrc *chrc;
if (service->failed || service->attrib)
return;
if (!g_str_has_prefix(path, service->path))
return;
- /* TODO: Handle descriptors here */
-
if (g_strcmp0(iface, GATT_SERVICE_IFACE) == 0) {
if (service->proxy)
return;
service->proxy = g_dbus_proxy_ref(proxy);
} else if (g_strcmp0(iface, GATT_CHRC_IFACE) == 0) {
+ struct external_chrc *chrc;
+
if (g_strcmp0(path, service->path) == 0) {
error("Characteristic path same as service path");
service->failed = true;
return;
}
- chrc = chrc_create(service, proxy);
+ chrc = chrc_create(service, proxy, path);
if (!chrc) {
service->failed = true;
return;
}
queue_push_tail(service->chrcs, chrc);
- } else
+ } else if (g_strcmp0(iface, GATT_DESC_IFACE) == 0) {
+ struct external_desc *desc;
+ const char *chrc_path;
+
+ if (!parse_path(proxy, "Characteristic", &chrc_path)) {
+ error("Failed to obtain characteristic path for "
+ "descriptor");
+ service->failed = true;
+ return;
+ }
+
+ desc = desc_create(service, proxy, chrc_path);
+ if (!desc) {
+ service->failed = true;
+ return;
+ }
+
+ /* Add 1 for the descriptor attribute */
+ if (!incr_attr_count(service, 1)) {
+ error("Failed to increment attribute count");
+ service->failed = true;
+ return;
+ }
+
+ queue_push_tail(service->descs, desc);
+ } else {
+ DBG("Ignoring unrelated interface: %s", iface);
return;
+ }
DBG("Object added to service - path: %s, iface: %s", path, iface);
}
return true;
}
+static bool database_add_desc(struct external_service *service,
+ struct external_desc *desc)
+{
+ bt_uuid_t uuid;
+
+ if (!parse_uuid(desc->proxy, &uuid)) {
+ error("Failed to read \"UUID\" property of descriptor");
+ return false;
+ }
+
+ /*
+ * TODO: Set read/write callbacks and property set permissions based on
+ * a D-Bus property of the external descriptor.
+ */
+ desc->attrib = gatt_db_service_add_descriptor(service->attrib,
+ &uuid, 0, NULL,
+ NULL, NULL);
+
+ if (!desc->attrib) {
+ error("Failed to create descriptor entry in database");
+ return false;
+ }
+
+ desc->handled = true;
+
+ return true;
+}
+
static bool database_add_chrc(struct external_service *service,
struct external_chrc *chrc)
{
bt_uuid_t uuid;
uint32_t perm;
+ const struct queue_entry *entry;
if (!parse_uuid(chrc->proxy, &uuid)) {
error("Failed to read \"UUID\" property of characteristic");
return false;
}
- if (!parse_service(chrc->proxy, service)) {
+ if (!check_service_path(chrc->proxy, service)) {
error("Invalid service path for characteristic");
return false;
}
if (!database_add_cep(service, chrc))
return false;
+ /* Handle the descriptors that belong to this characteristic. */
+ entry = queue_get_entries(service->descs);
+ while (entry) {
+ struct external_desc *desc = entry->data;
+
+ if (desc->handled || g_strcmp0(desc->chrc_path, chrc->path))
+ continue;
+
+ if (!database_add_desc(service, desc)) {
+ chrc->attrib = NULL;
+ error("Failed to create descriptor entry");
+ return false;
+ }
+
+ entry = entry->next;
+ }
+
return true;
}
+static bool match_desc_unhandled(const void *a, const void *b)
+{
+ const struct external_desc *desc = a;
+
+ return !desc->handled;
+}
+
static bool create_service_entry(struct external_service *service)
{
bt_uuid_t uuid;
if (!database_add_chrc(service, chrc)) {
error("Failed to add characteristic");
- gatt_db_remove_service(service->database->db,
- service->attrib);
- service->attrib = NULL;
- return false;
+ goto fail;
}
entry = entry->next;
}
+ /* If there are any unhandled descriptors, return an error */
+ if (queue_find(service->descs, match_desc_unhandled, NULL)) {
+ error("Found descriptor with no matching characteristic!");
+ goto fail;
+ }
+
gatt_db_service_set_active(service->attrib, true);
return true;
+
+fail:
+ gatt_db_remove_service(service->database->db, service->attrib);
+ service->attrib = NULL;
+
+ return false;
}
static void client_ready_cb(GDBusClient *client, void *user_data)
if (!service->chrcs)
goto fail;
+ service->descs = queue_new();
+ if (!service->descs)
+ goto fail;
+
service->reg = dbus_message_ref(msg);
g_dbus_client_set_disconnect_watch(service->client,