2014-03-14 23:30:09

by Arman Uguray

[permalink] [raw]
Subject: [RFC BlueZ v1 0/2] Initial D-Bus GATT client API implementation.

Reroll 1: Mostly fixed style issues. Additional changes from v0:
- Added the EXPERIMENTAL flag to properties in GattService1 and
GattCharacteristic1.
- Removed the change in src/device.c to when a connect call initiates
GATT service discovery.
- Removed the logic that skips GAP and GATT when registering the D-Bus
services.

Arman Uguray (2):
gatt-dbus: Add remote GATT service objects.
gatt-dbus: Add remote GATT characteristic objects.

src/device.c | 35 +++++++
src/gatt-dbus.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
src/gatt-dbus.h | 24 +++++
3 files changed, 339 insertions(+), 3 deletions(-)

--
1.9.0.279.gdc9e3eb



2014-03-14 23:30:11

by Arman Uguray

[permalink] [raw]
Subject: [RFC BlueZ v1 2/2] gatt-dbus: Add remote GATT characteristic objects.

This patch adds remote GATT characteristic objects to the D-Bus API. The
API only exposes the UUID; the characteristic value and permissions have
not yet been implemented.
---
src/gatt-dbus.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 191 insertions(+), 4 deletions(-)

diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 3937e17..0c89873 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -52,8 +52,24 @@

struct btd_gatt_dbus_service {
bt_uuid_t uuid;
+ struct att_range range;
+ GAttrib *attrib;
+ guint attioid;
+ guint request;
+
struct btd_device *device;
char *path;
+ GSList *characteristics;
+};
+
+struct gatt_dbus_characteristic {
+ bt_uuid_t uuid;
+ uint16_t handle;
+ uint16_t value_handle;
+ uint8_t properties;
+
+ struct btd_gatt_dbus_service *service;
+ char *path;
};

struct external_app {
@@ -491,20 +507,186 @@ static gboolean service_property_get_uuid(const GDBusPropertyTable *property,
return TRUE;
}

+static gboolean characteristic_property_get_uuid(
+ const GDBusPropertyTable *table,
+ DBusMessageIter *iter, void *data)
+{
+ char uuid[MAX_LEN_UUID_STR + 1];
+ const char *ptr = uuid;
+ struct gatt_dbus_characteristic *chr = data;
+
+ bt_uuid_to_string(&chr->uuid, uuid, sizeof(uuid));
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
+
+ return TRUE;
+}
+
+static gboolean characteristic_property_get_service(
+ const GDBusPropertyTable *table,
+ DBusMessageIter *iter, void *data)
+{
+ struct gatt_dbus_characteristic *chr = data;
+ const char *str = chr->service->path;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str);
+
+ return TRUE;
+}
+
static const GDBusPropertyTable service_properties[] = {
{ "UUID", "s", service_property_get_uuid, NULL, NULL,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{}
};

+static const GDBusPropertyTable characteristic_properties[] = {
+ { "UUID", "s", characteristic_property_get_uuid, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Service", "o", characteristic_property_get_service, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ {}
+};
+
+static void unregister_characteristic(gpointer user_data)
+{
+ struct gatt_dbus_characteristic *chr = user_data;
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ chr->path, GATT_CHR_IFACE);
+}
+
+static void attio_cleanup(struct btd_gatt_dbus_service *service)
+{
+ GAttrib *attrib = service->attrib;
+
+ if (!service->attrib)
+ return;
+
+ service->attrib = NULL;
+
+ if (service->request) {
+ g_attrib_cancel(attrib, service->request);
+ service->request = 0;
+ }
+
+ g_attrib_unref(attrib);
+}
+
static void service_free(gpointer user_data)
{
struct btd_gatt_dbus_service *service = user_data;

+ g_slist_free_full(service->characteristics, unregister_characteristic);
+
+ if (service->attioid)
+ btd_device_remove_attio_callback(service->device,
+ service->attioid);
+
+ attio_cleanup(service);
g_free(service->path);
g_free(service);
}

+static void characteristic_free(gpointer user_data)
+{
+ struct gatt_dbus_characteristic *characteristic = user_data;
+
+ g_free(characteristic->path);
+ g_free(characteristic);
+}
+
+static struct gatt_dbus_characteristic *characteristic_create(
+ struct btd_gatt_dbus_service *service,
+ struct gatt_char *chr)
+{
+ struct gatt_dbus_characteristic *characteristic;
+ bt_uuid_t uuid;
+
+ DBG("GATT characteristic UUID: %s", chr->uuid);
+
+ characteristic = g_try_new0(struct gatt_dbus_characteristic, 1);
+ if (characteristic == NULL)
+ return NULL;
+
+ characteristic->path = g_strdup_printf("%s/char%04X", service->path,
+ chr->handle);
+ characteristic->service = service;
+ characteristic->handle = chr->handle;
+ characteristic->value_handle = chr->value_handle;
+ characteristic->properties = chr->properties;
+
+ if (bt_string_to_uuid(&uuid, chr->uuid)) {
+ error("Characteristic has invalid UUID: %s", chr->uuid);
+ goto fail;
+ }
+
+ bt_uuid_to_uuid128(&uuid, &characteristic->uuid);
+
+ DBG("Creating GATT characteristic %s", characteristic->path);
+
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ characteristic->path,
+ GATT_CHR_IFACE, NULL, NULL,
+ characteristic_properties,
+ characteristic,
+ characteristic_free == FALSE)) {
+ error("Unable to register GATT characteristic: %s", chr->uuid);
+ goto fail;
+ }
+
+ return characteristic;
+
+fail:
+ characteristic_free(characteristic);
+ return NULL;
+}
+
+static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
+{
+ struct btd_gatt_dbus_service *service = user_data;
+
+ service->request = 0;
+
+ if (status) {
+ error("Characteristic discovery failed.");
+ return;
+ }
+
+ for (; chars; chars = chars->next) {
+ struct gatt_char *chr = chars->data;
+ struct gatt_dbus_characteristic *dbus_chr =
+ characteristic_create(service, chr);
+
+ if (dbus_chr == NULL) {
+ error("Failed to register GATT characteristic: %s",
+ chr->uuid);
+ continue;
+ }
+
+ service->characteristics = g_slist_append(
+ service->characteristics, dbus_chr);
+ }
+
+ attio_cleanup(service);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct btd_gatt_dbus_service *service = user_data;
+
+ service->attrib = g_attrib_ref(attrib);
+ service->request = gatt_discover_char(service->attrib,
+ service->range.start,
+ service->range.end,
+ NULL, discover_char_cb,
+ service);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct btd_gatt_dbus_service *service = user_data;
+ attio_cleanup(service);
+}
+
struct btd_gatt_dbus_service *btd_gatt_dbus_service_register(
struct btd_device *device,
struct gatt_primary *primary)
@@ -521,6 +703,8 @@ struct btd_gatt_dbus_service *btd_gatt_dbus_service_register(

service->path = g_strdup_printf("%s/service%04X", device_path,
primary->range.start);
+ service->device = device;
+ service->range = primary->range;

if (bt_string_to_uuid(&uuid, primary->uuid)) {
error("Primary has invalid UUID: %s", primary->uuid);
@@ -531,19 +715,22 @@ struct btd_gatt_dbus_service *btd_gatt_dbus_service_register(

DBG("Creating GATT service %s", service->path);

- if (g_dbus_register_interface(btd_get_dbus_connection(),
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
service->path, GATT_SERVICE_IFACE,
NULL, NULL, service_properties,
service,
- service_free) == FALSE) {
+ service_free)) {
char device_addr[18];
ba2str(device_get_address(device), device_addr);
error("Unable to register GATT service: UUID: %s, device: %s",
- primary->uuid, device_addr);
+ primary->uuid, device_addr);
goto fail;
}

- service->device = device;
+ service->attioid = service->attioid = btd_device_add_attio_callback(
+ service->device, attio_connected_cb,
+ attio_disconnected_cb, service);
+
return service;

fail:
--
1.9.0.279.gdc9e3eb


2014-03-14 23:30:10

by Arman Uguray

[permalink] [raw]
Subject: [RFC BlueZ v1 1/2] gatt-dbus: Add remote GATT service objects.

This patch adds the initial GATT client D-Bus API support for remote primary
GATT services. Characteristics, descriptors, and included services are not yet
handled.
---
src/device.c | 35 +++++++++++++++++++++
src/gatt-dbus.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
src/gatt-dbus.h | 24 +++++++++++++++
3 files changed, 150 insertions(+), 3 deletions(-)

diff --git a/src/device.c b/src/device.c
index e624445..ce895b2 100644
--- a/src/device.c
+++ b/src/device.c
@@ -61,10 +61,12 @@
#include "uuid-helper.h"
#include "sdp-client.h"
#include "attrib/gatt.h"
+#include "gatt-dbus.h"
#include "agent.h"
#include "textfile.h"
#include "storage.h"
#include "attrib-server.h"
+#include "gatt.h"

#define IO_CAPABILITY_NOINPUTNOOUTPUT 0x03

@@ -186,6 +188,7 @@ struct btd_device {
GSList *uuids;
GSList *primaries; /* List of primary services */
GSList *services; /* List of btd_service */
+ GSList *gatt_services; /* List of GATT DBus Services */
GSList *pending; /* Pending services */
GSList *watches; /* List of disconnect_data */
gboolean temporary;
@@ -511,11 +514,18 @@ static void svc_dev_remove(gpointer user_data)
g_free(cb);
}

+static void gatt_dbus_service_free(gpointer user_data)
+{
+ struct btd_gatt_dbus_service *service = user_data;
+ btd_gatt_dbus_service_unregister(service);
+}
+
static void device_free(gpointer user_data)
{
struct btd_device *device = user_data;

g_slist_free_full(device->uuids, g_free);
+ g_slist_free_full(device->gatt_services, gatt_dbus_service_free);
g_slist_free_full(device->primaries, g_free);
g_slist_free_full(device->attios, g_free);
g_slist_free_full(device->attios_offline, g_free);
@@ -3317,6 +3327,29 @@ done:
return FALSE;
}

+static void expose_btd_dbus_gatt_services(struct btd_device *device)
+{
+ GSList *l;
+
+ /* Clear current list of GATT services. */
+ g_slist_free_full(device->gatt_services, gatt_dbus_service_free);
+ device->gatt_services = NULL;
+
+ for (l = device->primaries; l; l = g_slist_next(l)) {
+ struct gatt_primary *prim = l->data;
+ struct btd_gatt_dbus_service *service;
+
+ service = btd_gatt_dbus_service_register(device, prim);
+ if (service == NULL) {
+ error("Failed to register a GATT D-Bus service.");
+ continue;
+ }
+
+ device->gatt_services = g_slist_append(device->gatt_services,
+ service);
+ }
+}
+
static void register_all_services(struct browse_req *req, GSList *services)
{
struct btd_device *device = req->device;
@@ -3329,6 +3362,8 @@ static void register_all_services(struct browse_req *req, GSList *services)

device_register_primaries(device, g_slist_copy(services), -1);

+ expose_btd_dbus_gatt_services(device);
+
device_probe_profiles(device, req->profiles_added);

if (device->attios == NULL && device->attios_offline == NULL)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index c5f1597..3937e17 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -37,6 +37,9 @@
#include "lib/uuid.h"
#include "dbus-common.h"
#include "log.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"

#include "error.h"
#include "gatt.h"
@@ -47,6 +50,12 @@
#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
#define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1"

+struct btd_gatt_dbus_service {
+ bt_uuid_t uuid;
+ struct btd_device *device;
+ char *path;
+};
+
struct external_app {
char *owner;
char *path;
@@ -436,7 +445,7 @@ static DBusMessage *unregister_service(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}

-static const GDBusMethodTable methods[] = {
+static const GDBusMethodTable manager_methods[] = {
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterService",
GDBUS_ARGS({ "service", "o"},
{ "options", "a{sv}"}),
@@ -450,8 +459,8 @@ static const GDBusMethodTable methods[] = {
gboolean gatt_dbus_manager_register(void)
{
if (g_dbus_register_interface(btd_get_dbus_connection(),
- "/org/bluez", GATT_MGR_IFACE,
- methods, NULL, NULL, NULL, NULL) == FALSE)
+ "/org/bluez", GATT_MGR_IFACE, manager_methods,
+ NULL, NULL, NULL, NULL) == FALSE)
return FALSE;

proxy_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
@@ -468,3 +477,82 @@ void gatt_dbus_manager_unregister(void)
g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez",
GATT_MGR_IFACE);
}
+
+static gboolean service_property_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ char uuid[MAX_LEN_UUID_STR + 1];
+ const char *ptr = uuid;
+ struct btd_gatt_dbus_service *service = data;
+
+ bt_uuid_to_string(&service->uuid, uuid, sizeof(uuid));
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable service_properties[] = {
+ { "UUID", "s", service_property_get_uuid, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ {}
+};
+
+static void service_free(gpointer user_data)
+{
+ struct btd_gatt_dbus_service *service = user_data;
+
+ g_free(service->path);
+ g_free(service);
+}
+
+struct btd_gatt_dbus_service *btd_gatt_dbus_service_register(
+ struct btd_device *device,
+ struct gatt_primary *primary)
+{
+ struct btd_gatt_dbus_service *service;
+ bt_uuid_t uuid;
+ const char *device_path = device_get_path(device);
+
+ DBG("GATT service UUID: %s", primary->uuid);
+
+ service = g_try_new0(struct btd_gatt_dbus_service, 1);
+ if (service == NULL)
+ return NULL;
+
+ service->path = g_strdup_printf("%s/service%04X", device_path,
+ primary->range.start);
+
+ if (bt_string_to_uuid(&uuid, primary->uuid)) {
+ error("Primary has invalid UUID: %s", primary->uuid);
+ goto fail;
+ }
+
+ bt_uuid_to_uuid128(&uuid, &service->uuid);
+
+ DBG("Creating GATT service %s", service->path);
+
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ service->path, GATT_SERVICE_IFACE,
+ NULL, NULL, service_properties,
+ service,
+ service_free) == FALSE) {
+ char device_addr[18];
+ ba2str(device_get_address(device), device_addr);
+ error("Unable to register GATT service: UUID: %s, device: %s",
+ primary->uuid, device_addr);
+ goto fail;
+ }
+
+ service->device = device;
+ return service;
+
+fail:
+ service_free(service);
+ return NULL;
+}
+
+void btd_gatt_dbus_service_unregister(struct btd_gatt_dbus_service *service)
+{
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ service->path, GATT_SERVICE_IFACE);
+}
diff --git a/src/gatt-dbus.h b/src/gatt-dbus.h
index 310cfa9..c09b7fb 100644
--- a/src/gatt-dbus.h
+++ b/src/gatt-dbus.h
@@ -21,5 +21,29 @@
*
*/

+struct btd_gatt_dbus_service;
+
gboolean gatt_dbus_manager_register(void);
void gatt_dbus_manager_unregister(void);
+
+/*
+ * btd_gatt_dbus_service_create - Create a GATT service that represents a remote
+ * primary GATT service and expose it via D-Bus.
+ *
+ * @device: The remote device that hosts the GATT service.
+ * @primary: The primary GATT service.
+ *
+ * Returns a reference to the GATT service object. In case of error, NULL is
+ * returned.
+ */
+struct btd_gatt_dbus_service *btd_gatt_dbus_service_register(
+ struct btd_device *device,
+ struct gatt_primary *primary);
+
+/*
+ * btd_gatt_dbus_service_free - Unregister a GATT service as a D-Bus object and
+ * free up its memory.
+ *
+ * @service: The GATT service object to remove.
+ */
+void btd_gatt_dbus_service_unregister(struct btd_gatt_dbus_service *service);
--
1.9.0.279.gdc9e3eb