This patchset adds the following features:
* register external characteristics
* basic read or write external characteristics
Usage of glib is being avoided inside src/gatt.c. Properties/flags is not
implemented yet.
How to test:
It is possible to test the implemented features using a modified
gatttool version. Unix socket is used as transport to allow basic
ATT operations:
git://git.infradead.org/users/cktakahasi/bluez.git gatt-api-forupstream-group2-patchv0-testing
$gatttool -I -L
[LOCAL][LE]> connect
Local connection
Connection successful
[LOCAL][LE]> primary
attr handle: 0x0001, end grp handle: 0x0003 uuid: 000034fb-0000-1000-8000-00805f9b34fb
[LOCAL][LE]> characteristics
handle: 0x0002, char properties: 0x00, char value handle: 0x0003, uuid: 00002a06-0000-1000-8000-00805f9b34fb
[LOCAL][LE]> char-read-hnd 0x0003
Characteristic value/descriptor: 00
[LOCAL][LE]> char-write-req 0x0003 02
Characteristic value was written successfully
[LOCAL][LE]> char-read-hnd 0x0003
Characteristic value/descriptor: 02
Alvaro Silva (3):
tools: Add Alert Level characteristic to gatt-service
tools: Add reading Value property of gatt-service
tools: Add setting Value property of gatt-service
Andre Guedes (2):
gatt: Add function to create constant attributes
gatt: Add helper for creating GATT characteristics
Claudio Takahasi (11):
gatt: Add characteristic to the database
gatt: Add hash table of GDBusProxy objects
gatt: Add read callback to btd_gatt_add_char helper
gatt: Assign read callback for external services
gatt: Add helper for reading characteristics
gatt: Add write callback to btd_gatt_add_char helper
gdbus: Add g_dbus_proxy_set_property_array
gatt: Assign write callback for external services
gatt: Add result callback for Write Request
gatt: Add Write Request handling for GDBusProxy
tools: Emit property changed when Value changes
gdbus/client.c | 85 +++++++++++++++++++++++
gdbus/gdbus.h | 5 ++
src/gatt-dbus.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++++--
src/gatt.c | 138 +++++++++++++++++++++++++++++++++++--
src/gatt.h | 52 ++++++++++++++
tools/gatt-service.c | 157 ++++++++++++++++++++++++++++++++++++++----
6 files changed, 604 insertions(+), 22 deletions(-)
--
1.8.3.1
Hi Claudio,
On Mon, Mar 24, 2014, Claudio Takahasi wrote:
> Remaining patches of "Add basic GATT characteristics" patchset.
>
> This patchset adds the following features:
> * basic read or write external characteristics
>
> TODO (missing features of this patchset and upstream code):
> * Properties/flags: operations and security
> * 32-bit UUIDs
> * Remove/free services
> * multiple services provided by the same client
> * remove att_put_* att_get_* helpers
>
> Changes from v8 to v7:
> * fixed gatt.h write callback description
>
> Changes from v7 to v6:
> * coding style: !foo and boolean
>
> Changes from v6 to v5:
> * Use size_t instead of signed integer for
> g_dbus_proxy_set_property_array
> * Use negative value to denote an error (-ernno)
>
> Changes from v5 to v4:
> * avoid usage of uint128_t in the header util.h
>
> Changes from v4 to v3:
> * keep LE <-> CPU helpers in lib/bluetooth.h (patches removing these
> helpers are being shifted to next patchset)
>
> Changes from v3 to v2:
> * return put_uuid() definition to src/gatt.c
> * add put_le16(), put_le32(), put_le128() to src/shared/util.h
>
> Changes from v2 to v1:
> * move put_uuid() and put_le16() to src/shared/util.h
> * check malloc0() and new0() return value
>
> Changes from v1 to v0:
> * Use LE <-> CPU helpers defined in src/shared/util.h
> * remove patch "gatt: Add helper for reading characteristics" from
> this set.
>
> Alvaro Silva (3):
> tools: Add Alert Level characteristic to gatt-service
> tools: Add reading Value property of gatt-service
> tools: Add setting Value property of gatt-service
>
> Claudio Takahasi (5):
> gatt: Add write callback to btd_gatt_add_char helper
> gatt: Assign write callback for external services
> gatt: Add result callback for Write Request
> gatt: Add Write Request handling for GDBusProxy
> tools: Emit property changed when Value changes
>
> src/gatt-dbus.c | 84 +++++++++++++++++++++++++++-
> src/gatt.c | 7 ++-
> src/gatt.h | 28 +++++++++-
> tools/gatt-service.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++----
> 4 files changed, 256 insertions(+), 17 deletions(-)
All patches in this set have been applied. Thanks.
Johan
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding the callback to
allow reading the Value property. At the moment, it is a generic
callback and it doesn't consider characteristic value property
restrictions.
---
tools/gatt-service.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 53 insertions(+), 6 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 56d6e86..a5a64b5 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -46,18 +46,44 @@
static GMainLoop *main_loop;
static GSList *services;
+struct characteristic {
+ char *uuid;
+ uint8_t *value;
+ int vlen;
+};
+
static gboolean chr_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
- const char *uuid = user_data;
+ struct characteristic *chr = user_data;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid);
+
+ return TRUE;
+}
+
+static gboolean chr_get_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+
+ printf("Get(\"Value\")\n");
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &chr->value, chr->vlen);
+
+ dbus_message_iter_close_container(iter, &array);
return TRUE;
}
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
+ { "Value", "ay", chr_get_value, NULL, NULL },
{ }
};
@@ -100,18 +126,35 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static void chr_iface_destroy(gpointer user_data)
+{
+ struct characteristic *chr = user_data;
+
+ g_free(chr->uuid);
+ g_free(chr->value);
+ g_free(chr);
+}
+
static gboolean register_characteristic(DBusConnection *conn, const char *uuid,
+ const uint8_t *value, int vlen,
const char *service_path)
{
+ struct characteristic *chr;
static int id = 1;
char *path;
gboolean ret = TRUE;
path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+ chr = g_new0(struct characteristic, 1);
+
+ chr->uuid = g_strdup(uuid);
+ chr->value = g_memdup(value, vlen);
+ chr->vlen = vlen;
+
if (!g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
- g_strdup(uuid), g_free)) {
+ chr, chr_iface_destroy)) {
printf("Couldn't register characteristic interface\n");
ret = FALSE;
}
@@ -141,14 +184,18 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ uint8_t level = 0;
service_path = register_service(conn, IAS_UUID);
if (!service_path)
return;
- /* Add Alert Level Characteristic to Immediate Alert Service */
- if (!register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
- service_path)) {
+ /* Add Alert Level Characteristic to Immediate Alert Service
+ * According to the IAS SPEC, reading <<Alert level>> is not allowed.
+ * "Value" is readable for testing purpose only.
+ */
+ if (!register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ sizeof(level), service_path)) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
g_dbus_unregister_interface(conn, service_path,
GATT_SERVICE_IFACE);
--
1.8.3.1
This patch extends gatt-service tool, emitting PropertiesChanged for
"Value" when a new value is set. This flow represents the handling of
ATT Write Command or Write Request which is translated to a GDBusProxy
set property. PropertiesChanged will be tracked by the characteristic
GDBusProxy in order to detect "Value" changes.
---
tools/gatt-service.c | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 6ccaeb5..1e369d7 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -47,9 +47,11 @@
static GMainLoop *main_loop;
static GSList *services;
+static DBusConnection *connection;
struct characteristic {
char *uuid;
+ char *path;
uint8_t *value;
int vlen;
};
@@ -110,6 +112,8 @@ static void chr_set_value(const GDBusPropertyTable *property,
chr->vlen = len;
g_dbus_pending_property_success(id);
+ g_dbus_emit_property_changed(connection, chr->path,
+ GATT_CHR_IFACE, "Value");
}
static const GDBusPropertyTable chr_properties[] = {
@@ -163,6 +167,7 @@ static void chr_iface_destroy(gpointer user_data)
g_free(chr->uuid);
g_free(chr->value);
+ g_free(chr->path);
g_free(chr);
}
@@ -182,6 +187,7 @@ static gboolean register_characteristic(DBusConnection *conn, const char *uuid,
chr->uuid = g_strdup(uuid);
chr->value = g_memdup(value, vlen);
chr->vlen = vlen;
+ chr->path = path;
if (!g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
@@ -190,18 +196,16 @@ static gboolean register_characteristic(DBusConnection *conn, const char *uuid,
ret = FALSE;
}
- g_free(path);
-
return ret;
}
-static char *register_service(DBusConnection *conn, const char *uuid)
+static char *register_service(const char *uuid)
{
static int id = 1;
char *path;
path = g_strdup_printf("/service%d", id++);
- if (g_dbus_register_interface(conn, path, GATT_SERVICE_IFACE,
+ if (g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
NULL, NULL, service_properties,
g_strdup(uuid), g_free) == FALSE) {
printf("Couldn't register service interface\n");
@@ -212,12 +216,12 @@ static char *register_service(DBusConnection *conn, const char *uuid)
return path;
}
-static void create_services(DBusConnection *conn)
+static void create_services()
{
char *service_path;
uint8_t level = 0;
- service_path = register_service(conn, IAS_UUID);
+ service_path = register_service(IAS_UUID);
if (!service_path)
return;
@@ -225,10 +229,10 @@ static void create_services(DBusConnection *conn)
* According to the IAS SPEC, reading <<Alert level>> is not allowed.
* "Value" is readable for testing purpose only.
*/
- if (!register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ if (!register_characteristic(connection, ALERT_LEVEL_CHR_UUID, &level,
sizeof(level), service_path)) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
- g_dbus_unregister_interface(conn, service_path,
+ g_dbus_unregister_interface(connection, service_path,
GATT_SERVICE_IFACE);
g_free(service_path);
return;
@@ -369,25 +373,24 @@ static guint setup_signalfd(void)
int main(int argc, char *argv[])
{
GDBusClient *client;
- DBusConnection *dbus_conn;
guint signal;
signal = setup_signalfd();
if (signal == 0)
return -errno;
- dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
main_loop = g_main_loop_new(NULL, FALSE);
- g_dbus_attach_object_manager(dbus_conn);
+ g_dbus_attach_object_manager(connection);
printf("gatt-service unique name: %s\n",
- dbus_bus_get_unique_name(dbus_conn));
+ dbus_bus_get_unique_name(connection));
- create_services(dbus_conn);
+ create_services();
- client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+ client = g_dbus_client_new(connection, "org.bluez", "/org/bluez");
g_dbus_client_set_connect_watch(client, connect_handler, NULL);
@@ -398,7 +401,7 @@ int main(int argc, char *argv[])
g_source_remove(signal);
g_slist_free_full(services, g_free);
- dbus_connection_unref(dbus_conn);
+ dbus_connection_unref(connection);
return 0;
}
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding a generic callback
to allow set the characteristic Value. It doesn't check for
characteristic properties yet.
---
tools/gatt-service.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index a5a64b5..6ccaeb5 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -35,6 +35,8 @@
#include <dbus/dbus.h>
#include <gdbus/gdbus.h>
+#include "src/error.h"
+
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
@@ -81,9 +83,38 @@ static gboolean chr_get_value(const GDBusPropertyTable *property,
return TRUE;
}
+static void chr_set_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+ uint8_t *value;
+ int len;
+
+ printf("Set('Value', ...)\n");
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ printf("Invalid value for Set('Value'...)\n");
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ g_free(chr->value);
+ chr->value = g_memdup(value, len);
+ chr->vlen = len;
+
+ g_dbus_pending_property_success(id);
+}
+
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
- { "Value", "ay", chr_get_value, NULL, NULL },
+ { "Value", "ay", chr_get_value, chr_set_value, NULL },
{ }
};
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch registers the Alert Level characteristic object related to
Immediate Alert Service. It adds the characteristic UUID property only.
---
tools/gatt-service.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index ef479ed..56d6e86 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -37,13 +37,30 @@
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
+#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
/* Immediate Alert Service UUID */
#define IAS_UUID "00001802-0000-1000-8000-00805f9b34fb"
+#define ALERT_LEVEL_CHR_UUID "00002a06-0000-1000-8000-00805f9b34fb"
static GMainLoop *main_loop;
static GSList *services;
+static gboolean chr_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *uuid = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable chr_properties[] = {
+ { "UUID", "s", chr_get_uuid },
+ { }
+};
+
static gboolean service_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
@@ -83,6 +100,27 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static gboolean register_characteristic(DBusConnection *conn, const char *uuid,
+ const char *service_path)
+{
+ static int id = 1;
+ char *path;
+ gboolean ret = TRUE;
+
+ path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+
+ if (!g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
+ NULL, NULL, chr_properties,
+ g_strdup(uuid), g_free)) {
+ printf("Couldn't register characteristic interface\n");
+ ret = FALSE;
+ }
+
+ g_free(path);
+
+ return ret;
+}
+
static char *register_service(DBusConnection *conn, const char *uuid)
{
static int id = 1;
@@ -105,9 +143,20 @@ static void create_services(DBusConnection *conn)
char *service_path;
service_path = register_service(conn, IAS_UUID);
+ if (!service_path)
+ return;
- services = g_slist_prepend(services, service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service */
+ if (!register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
+ service_path)) {
+ printf("Couldn't register Alert Level characteristic (IAS)\n");
+ g_dbus_unregister_interface(conn, service_path,
+ GATT_SERVICE_IFACE);
+ g_free(service_path);
+ return;
+ }
+ services = g_slist_prepend(services, service_path);
printf("Registered service: %s\n", service_path);
}
--
1.8.3.1
When the characteristic requires Write Request, the ATT response should
be sent after receiving the reply for the Set method call.
---
src/gatt-dbus.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 61 insertions(+), 6 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 9ab7b8b..1a1c6cc 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,11 @@ struct external_app {
unsigned int watch;
};
+struct proxy_write_data {
+ btd_attr_write_result_t result_cb;
+ void *user_data;
+};
+
/*
* Attribute to Proxy hash table. Used to map incoming
* ATT operations to its external characteristic proxy.
@@ -181,8 +186,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
static void proxy_write_reply(const DBusError *derr, void *user_data)
{
- if (derr)
- DBG("Write reply: %s", derr->message);
+ struct proxy_write_data *wdata = user_data;
+ int err = 0;
+
+ /*
+ * Security requirements shall be handled by the core. If external
+ * applications returns an error, the reasons will be restricted to
+ * invalid argument or application specific errors.
+ */
+
+ if (!dbus_error_is_set(derr))
+ goto done;
+
+ DBG("Write reply: %s", derr->message);
+
+ if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
+ err = -ETIMEDOUT;
+ else if (dbus_error_has_name(derr, ERROR_INTERFACE ".InvalidArguments"))
+ err = -EINVAL;
+ else
+ err = -EPROTO;
+
+done:
+ if (wdata && wdata->result_cb)
+ wdata->result_cb(err, wdata->user_data);
}
static void proxy_write_cb(struct btd_attribute *attr,
@@ -193,16 +220,44 @@ static void proxy_write_cb(struct btd_attribute *attr,
GDBusProxy *proxy;
proxy = g_hash_table_lookup(proxy_hash, attr);
- if (!proxy)
- /* FIXME: Attribute not found */
+ if (!proxy) {
+ result(-ENOENT, user_data);
return;
+ }
- g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ /*
+ * "result" callback defines if the core wants to receive the
+ * operation result, allowing to select ATT Write Request or Write
+ * Command. Descriptors requires Write Request operation. For
+ * Characteristics, the implementation will define which operations
+ * are allowed based on the properties/flags.
+ * TODO: Write Long Characteristics/Descriptors.
+ */
+
+ if (result) {
+ struct proxy_write_data *wdata;
+
+ wdata = g_new0(struct proxy_write_data, 1);
+ wdata->result_cb = result;
+ wdata->user_data = user_data;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ wdata, g_free);
+ } else {
+ /*
+ * Caller is not interested in the Set method call result.
+ * This flow implements the ATT Write Command scenario, where
+ * the remote doesn't receive ATT response.
+ */
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
value, len, proxy_write_reply,
NULL, NULL);
+ }
- DBG("Server: Write characteristic callback %s",
+ DBG("Server: Write attribute callback %s",
g_dbus_proxy_get_path(proxy));
+
}
static int register_external_service(const struct external_app *eapp,
--
1.8.3.1
This patch adds a callback to allow the service implementation to inform
the core (ATT layer) the result of the write operation. Used to handle
ATT Write Request procedure, which requires ATT response.
---
src/gatt-dbus.c | 4 +++-
src/gatt.h | 16 +++++++++++++++-
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index cdb46c4..9ab7b8b 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -186,7 +186,9 @@ static void proxy_write_reply(const DBusError *derr, void *user_data)
}
static void proxy_write_cb(struct btd_attribute *attr,
- const uint8_t *value, size_t len)
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data)
{
GDBusProxy *proxy;
diff --git a/src/gatt.h b/src/gatt.h
index d5dc7e6..8137e81 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -42,15 +42,29 @@ typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
btd_attr_read_result_t result,
void *user_data);
+
+/*
+ * Write operation result callback. Called from the service implementation
+ * informing the core (ATT layer) the result of the write operation. It is used
+ * to manage Write Request operations.
+ * @err: error in -errno format.
+ * @user_data: user_data passed in btd_attr_write_t callback.
+ */
+typedef void (*btd_attr_write_result_t) (int err, void *user_data);
/*
* Service implementation callback passed to core (ATT layer). It manages write
* operations received from remote devices.
* @attr: reference of the attribute to be changed.
* @value: new attribute value.
* @len: length of value.
+ * @result: callback called from the service implementation informing the
+ * result of the write operation.
+ * @user_data: user_data passed in btd_attr_write_result_t callback.
*/
typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
- const uint8_t *value, size_t len);
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
--
1.8.3.1
Remaining patches of "Add basic GATT characteristics" patchset.
This patchset adds the following features:
* basic read or write external characteristics
TODO (missing features of this patchset and upstream code):
* Properties/flags: operations and security
* 32-bit UUIDs
* Remove/free services
* multiple services provided by the same client
* remove att_put_* att_get_* helpers
Changes from v8 to v7:
* fixed gatt.h write callback description
Changes from v7 to v6:
* coding style: !foo and boolean
Changes from v6 to v5:
* Use size_t instead of signed integer for
g_dbus_proxy_set_property_array
* Use negative value to denote an error (-ernno)
Changes from v5 to v4:
* avoid usage of uint128_t in the header util.h
Changes from v4 to v3:
* keep LE <-> CPU helpers in lib/bluetooth.h (patches removing these
helpers are being shifted to next patchset)
Changes from v3 to v2:
* return put_uuid() definition to src/gatt.c
* add put_le16(), put_le32(), put_le128() to src/shared/util.h
Changes from v2 to v1:
* move put_uuid() and put_le16() to src/shared/util.h
* check malloc0() and new0() return value
Changes from v1 to v0:
* Use LE <-> CPU helpers defined in src/shared/util.h
* remove patch "gatt: Add helper for reading characteristics" from
this set.
Alvaro Silva (3):
tools: Add Alert Level characteristic to gatt-service
tools: Add reading Value property of gatt-service
tools: Add setting Value property of gatt-service
Claudio Takahasi (5):
gatt: Add write callback to btd_gatt_add_char helper
gatt: Assign write callback for external services
gatt: Add result callback for Write Request
gatt: Add Write Request handling for GDBusProxy
tools: Emit property changed when Value changes
src/gatt-dbus.c | 84 +++++++++++++++++++++++++++-
src/gatt.c | 7 ++-
src/gatt.h | 28 +++++++++-
tools/gatt-service.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++----
4 files changed, 256 insertions(+), 17 deletions(-)
--
1.8.3.1
This patch adds the callback for writing the external characteristic
value. Internally, GDBusProxy implementation calls Set method of the
"Value" property. At the moment, potential errors returned by the
external service implementation are being ignored.
The ATT operation (Write Command or Request) is not exposed to the
external service implementation. GATT/GDBusProxy handles possible
DBus errors returned, and notify the core using the callbacks informed.
Write Command doesn't have callback since it doesn't return ATT errors.
---
src/gatt-dbus.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index ef137a8..cdb46c4 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -179,6 +179,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
result(0, value, len, user_data);
}
+static void proxy_write_reply(const DBusError *derr, void *user_data)
+{
+ if (derr)
+ DBG("Write reply: %s", derr->message);
+}
+
+static void proxy_write_cb(struct btd_attribute *attr,
+ const uint8_t *value, size_t len)
+{
+ GDBusProxy *proxy;
+
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (!proxy)
+ /* FIXME: Attribute not found */
+ return;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ NULL, NULL);
+
+ DBG("Server: Write characteristic callback %s",
+ g_dbus_proxy_get_path(proxy));
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -238,7 +262,8 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb,
+ proxy_write_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This patch adds a function callback for write operations. When a remote
device writes to a given attribute, the core calls the specified write
callback informing the attribute server the new characteristic value.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 ++++---
src/gatt.h | 14 +++++++++++++-
3 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 4418353..ef137a8 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -238,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 76bbe20..45b5e26 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -46,6 +46,7 @@ struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
btd_attr_read_t read_cb;
+ btd_attr_write_t write_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -133,7 +134,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb)
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -192,8 +194,7 @@ struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
char_value->type = *uuid;
char_value->read_cb = read_cb;
-
- /* TODO: Write callbacks */
+ char_value->write_cb = write_cb;
if (local_database_add(next_handle, char_value) < 0)
/* TODO: remove declaration */
diff --git a/src/gatt.h b/src/gatt.h
index daa8d54..d5dc7e6 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -42,6 +42,15 @@ typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
btd_attr_read_result_t result,
void *user_data);
+/*
+ * Service implementation callback passed to core (ATT layer). It manages write
+ * operations received from remote devices.
+ * @attr: reference of the attribute to be changed.
+ * @value: new attribute value.
+ * @len: length of value.
+ */
+typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
+ const uint8_t *value, size_t len);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
@@ -57,10 +66,13 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
* @read_cb: Callback used to provide the characteristic value.
+ * @write_cb: Callback called to notify the implementation that a new value
+ * is available.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb);
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb);
--
1.8.3.1
Hi Johan:
On Mon, Mar 24, 2014 at 5:58 AM, Johan Hedberg <[email protected]> wrote:
> Hi Claudio,
>
> On Fri, Mar 21, 2014, Claudio Takahasi wrote:
>> This patch adds a function callback for write operations. When a remote
>> device writes to a given attribute, the core calls the specified write
>> callback informing the attribute server the new characteristic value.
>> ---
>> src/gatt-dbus.c | 2 +-
>> src/gatt.c | 7 ++++---
>> src/gatt.h | 13 ++++++++++++-
>> 3 files changed, 17 insertions(+), 5 deletions(-)
>
> I've applied the first patch but your change to the header file in this
> patch is strange:
>
>> +/*
>> + * Callbacks from this type are called once the value from the attribute was
>> + * written.
>> + * @err: error in -errno format.
>> + * @user_data: user_data passed in btd_attr_write_t callback
>> + */
>> +typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
>> + const uint8_t *value, size_t len);
>
> There's neither an err parameter nor a user_data parameter in this
> callback, so I'm wondering what your code comment refers to? :)
>
> Johan
It belongs to another patch that introduces Write Request handling:
asynchronous support.
It is a leftover issue of my approach of re-creating all the patches
based on the code that I have.
I will send another patchset moving this comment to the right patch.
Regards,
Claudio
Hi Claudio,
On Fri, Mar 21, 2014, Claudio Takahasi wrote:
> This patch adds a new gdbus utility function to allow setting a property
> of fixed, and non-fixed values array.
> ---
> gdbus/client.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> gdbus/gdbus.h | 5 ++++
> 2 files changed, 90 insertions(+)
This patch has been applied. Thanks.
Johan
Hi Claudio,
On Fri, Mar 21, 2014, Claudio Takahasi wrote:
> This patch adds a function callback for write operations. When a remote
> device writes to a given attribute, the core calls the specified write
> callback informing the attribute server the new characteristic value.
> ---
> src/gatt-dbus.c | 2 +-
> src/gatt.c | 7 ++++---
> src/gatt.h | 13 ++++++++++++-
> 3 files changed, 17 insertions(+), 5 deletions(-)
I've applied the first patch but your change to the header file in this
patch is strange:
> +/*
> + * Callbacks from this type are called once the value from the attribute was
> + * written.
> + * @err: error in -errno format.
> + * @user_data: user_data passed in btd_attr_write_t callback
> + */
> +typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
> + const uint8_t *value, size_t len);
There's neither an err parameter nor a user_data parameter in this
callback, so I'm wondering what your code comment refers to? :)
Johan
This patch extends gatt-service tool, emitting PropertiesChanged for
"Value" when a new value is set. This flow represents the handling of
ATT Write Command or Write Request which is translated to a GDBusProxy
set property. PropertiesChanged will be tracked by the characteristic
GDBusProxy in order to detect "Value" changes.
---
tools/gatt-service.c | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index f01fd2a..255f3bb 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -47,9 +47,11 @@
static GMainLoop *main_loop;
static GSList *services;
+static DBusConnection *connection;
struct characteristic {
char *uuid;
+ char *path;
uint8_t *value;
int vlen;
};
@@ -110,6 +112,8 @@ static void chr_set_value(const GDBusPropertyTable *property,
chr->vlen = len;
g_dbus_pending_property_success(id);
+ g_dbus_emit_property_changed(connection, chr->path,
+ GATT_CHR_IFACE, "Value");
}
static const GDBusPropertyTable chr_properties[] = {
@@ -163,6 +167,7 @@ static void chr_iface_destroy(gpointer user_data)
g_free(chr->uuid);
g_free(chr->value);
+ g_free(chr->path);
g_free(chr);
}
@@ -182,6 +187,7 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
chr->uuid = g_strdup(uuid);
chr->value = g_memdup(value, vlen);
chr->vlen = vlen;
+ chr->path = path;
if (!g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
@@ -190,18 +196,16 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
ret = -EIO;
}
- g_free(path);
-
return ret;
}
-static char *register_service(DBusConnection *conn, const char *uuid)
+static char *register_service(const char *uuid)
{
static int id = 1;
char *path;
path = g_strdup_printf("/service%d", id++);
- if (g_dbus_register_interface(conn, path, GATT_SERVICE_IFACE,
+ if (g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
NULL, NULL, service_properties,
g_strdup(uuid), g_free) == FALSE) {
printf("Couldn't register service interface\n");
@@ -212,13 +216,13 @@ static char *register_service(DBusConnection *conn, const char *uuid)
return path;
}
-static void create_services(DBusConnection *conn)
+static void create_services()
{
char *service_path;
uint8_t level = 0;
int ret;
- service_path = register_service(conn, IAS_UUID);
+ service_path = register_service(IAS_UUID);
if (!service_path)
return;
@@ -226,11 +230,11 @@ static void create_services(DBusConnection *conn)
* According to the IAS SPEC, reading <<Alert level>> is not alloweed.
* "Value" is readable for testing purpose only.
*/
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ ret = register_characteristic(connection, ALERT_LEVEL_CHR_UUID, &level,
sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
- g_dbus_unregister_interface(conn, service_path,
+ g_dbus_unregister_interface(connection, service_path,
GATT_SERVICE_IFACE);
g_free(service_path);
return;
@@ -371,25 +375,24 @@ static guint setup_signalfd(void)
int main(int argc, char *argv[])
{
GDBusClient *client;
- DBusConnection *dbus_conn;
guint signal;
signal = setup_signalfd();
if (signal == 0)
return -errno;
- dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
main_loop = g_main_loop_new(NULL, FALSE);
- g_dbus_attach_object_manager(dbus_conn);
+ g_dbus_attach_object_manager(connection);
printf("gatt-service unique name: %s\n",
- dbus_bus_get_unique_name(dbus_conn));
+ dbus_bus_get_unique_name(connection));
- create_services(dbus_conn);
+ create_services();
- client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+ client = g_dbus_client_new(connection, "org.bluez", "/org/bluez");
g_dbus_client_set_connect_watch(client, connect_handler, NULL);
@@ -400,7 +403,7 @@ int main(int argc, char *argv[])
g_source_remove(signal);
g_slist_free_full(services, g_free);
- dbus_connection_unref(dbus_conn);
+ dbus_connection_unref(connection);
return 0;
}
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding a generic callback
to allow set the characteristic Value. It doesn't check for
characteristic properties yet.
---
tools/gatt-service.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index aef1291..f01fd2a 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -35,6 +35,8 @@
#include <dbus/dbus.h>
#include <gdbus/gdbus.h>
+#include "src/error.h"
+
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
@@ -81,9 +83,38 @@ static gboolean chr_get_value(const GDBusPropertyTable *property,
return TRUE;
}
+static void chr_set_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+ uint8_t *value;
+ int len;
+
+ printf("Set('Value', ...)\n");
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ printf("Invalid value for Set('Value'...)\n");
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ g_free(chr->value);
+ chr->value = g_memdup(value, len);
+ chr->vlen = len;
+
+ g_dbus_pending_property_success(id);
+}
+
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
- { "Value", "ay", chr_get_value, NULL, NULL },
+ { "Value", "ay", chr_get_value, chr_set_value, NULL },
{ }
};
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding the callback to
allow reading the Value property. At the moment, it is a generic
callback and it doesn't consider characteristic value property
restrictions.
---
tools/gatt-service.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 53 insertions(+), 6 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 6ae642c..aef1291 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -46,18 +46,44 @@
static GMainLoop *main_loop;
static GSList *services;
+struct characteristic {
+ char *uuid;
+ uint8_t *value;
+ int vlen;
+};
+
static gboolean chr_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
- const char *uuid = user_data;
+ struct characteristic *chr = user_data;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid);
+
+ return TRUE;
+}
+
+static gboolean chr_get_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+
+ printf("Get(\"Value\")\n");
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &chr->value, chr->vlen);
+
+ dbus_message_iter_close_container(iter, &array);
return TRUE;
}
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
+ { "Value", "ay", chr_get_value, NULL, NULL },
{ }
};
@@ -100,18 +126,35 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static void chr_iface_destroy(gpointer user_data)
+{
+ struct characteristic *chr = user_data;
+
+ g_free(chr->uuid);
+ g_free(chr->value);
+ g_free(chr);
+}
+
static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const uint8_t *value, int vlen,
const char *service_path)
{
+ struct characteristic *chr;
static int id = 1;
char *path;
int ret = 0;
path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+ chr = g_new0(struct characteristic, 1);
+
+ chr->uuid = g_strdup(uuid);
+ chr->value = g_memdup(value, vlen);
+ chr->vlen = vlen;
+
if (!g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
- g_strdup(uuid), g_free)) {
+ chr, chr_iface_destroy)) {
printf("Couldn't register characteristic interface\n");
ret = -EIO;
}
@@ -141,15 +184,19 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ uint8_t level = 0;
int ret;
service_path = register_service(conn, IAS_UUID);
if (!service_path)
return;
- /* Add Alert Level Characteristic to Immediate Alert Service */
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
- service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service
+ * According to the IAS SPEC, reading <<Alert level>> is not alloweed.
+ * "Value" is readable for testing purpose only.
+ */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
g_dbus_unregister_interface(conn, service_path,
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch registers the Alert Level characteristic object related to
Immediate Alert Service. It adds the characteristic UUID property only.
---
tools/gatt-service.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index cba0ff3..6ae642c 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -37,13 +37,30 @@
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
+#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
/* Immediate Alert Service UUID */
#define IAS_UUID "00001802-0000-1000-8000-00805f9b34fb"
+#define ALERT_LEVEL_CHR_UUID "00002a06-0000-1000-8000-00805f9b34fb"
static GMainLoop *main_loop;
static GSList *services;
+static gboolean chr_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *uuid = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable chr_properties[] = {
+ { "UUID", "s", chr_get_uuid },
+ { }
+};
+
static gboolean service_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
@@ -83,6 +100,27 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const char *service_path)
+{
+ static int id = 1;
+ char *path;
+ int ret = 0;
+
+ path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+
+ if (!g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
+ NULL, NULL, chr_properties,
+ g_strdup(uuid), g_free)) {
+ printf("Couldn't register characteristic interface\n");
+ ret = -EIO;
+ }
+
+ g_free(path);
+
+ return ret;
+}
+
static char *register_service(DBusConnection *conn, const char *uuid)
{
static int id = 1;
@@ -103,11 +141,24 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ int ret;
service_path = register_service(conn, IAS_UUID);
+ if (!service_path)
+ return;
- services = g_slist_prepend(services, service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
+ service_path);
+ if (ret < 0) {
+ printf("Couldn't register Alert Level characteristic (IAS)\n");
+ g_dbus_unregister_interface(conn, service_path,
+ GATT_SERVICE_IFACE);
+ g_free(service_path);
+ return;
+ }
+ services = g_slist_prepend(services, service_path);
printf("Registered service: %s\n", service_path);
}
--
1.8.3.1
When the characteristic requires Write Request, the ATT response should
be sent after receiving the reply for the Set method call.
---
src/gatt-dbus.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 61 insertions(+), 6 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 916f727..3c3b54c 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,11 @@ struct external_app {
unsigned int watch;
};
+struct proxy_write_data {
+ btd_attr_write_result_t result_cb;
+ void *user_data;
+};
+
/*
* Attribute to Proxy hash table. Used to map incoming
* ATT operations to its external characteristic proxy.
@@ -181,8 +186,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
static void proxy_write_reply(const DBusError *derr, void *user_data)
{
- if (derr)
- DBG("Write reply: %s", derr->message);
+ struct proxy_write_data *wdata = user_data;
+ int err = 0;
+
+ /*
+ * Security requirements shall be handled by the core. If external
+ * applications returns an error, the reasons will be restricted to
+ * invalid argument or application specific errors.
+ */
+
+ if (!dbus_error_is_set(derr))
+ goto done;
+
+ DBG("Write reply: %s", derr->message);
+
+ if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
+ err = -ETIMEDOUT;
+ else if (dbus_error_has_name(derr, ERROR_INTERFACE ".InvalidArguments"))
+ err = -EINVAL;
+ else
+ err = -EPROTO;
+
+done:
+ if (wdata && wdata->result_cb)
+ wdata->result_cb(err, wdata->user_data);
}
static void proxy_write_cb(struct btd_attribute *attr,
@@ -193,16 +220,44 @@ static void proxy_write_cb(struct btd_attribute *attr,
GDBusProxy *proxy;
proxy = g_hash_table_lookup(proxy_hash, attr);
- if (!proxy)
- /* FIXME: Attribute not found */
+ if (!proxy) {
+ result(-ENOENT, user_data);
return;
+ }
- g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ /*
+ * "result" callback defines if the core wants to receive the
+ * operation result, allowing to select ATT Write Request or Write
+ * Command. Descriptors requires Write Request operation. For
+ * Characteristics, the implementation will define which operations
+ * are allowed based on the properties/flags.
+ * TODO: Write Long Characteristics/Descriptors.
+ */
+
+ if (result) {
+ struct proxy_write_data *wdata;
+
+ wdata = g_new0(struct proxy_write_data, 1);
+ wdata->result_cb = result;
+ wdata->user_data = user_data;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ wdata, g_free);
+ } else {
+ /*
+ * Caller is not interested in the Set method call result.
+ * This flow implements the ATT Write Command scenario, where
+ * the remote doesn't receive ATT response.
+ */
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
value, len, proxy_write_reply,
NULL, NULL);
+ }
- DBG("Server: Write characteristic callback %s",
+ DBG("Server: Write attribute callback %s",
g_dbus_proxy_get_path(proxy));
+
}
static int register_external_service(const struct external_app *eapp,
--
1.8.3.1
This patch adds an extra callback to inform the result of the write
operation on an external entity to allow the core to reply properly
for the ATT request.
---
src/gatt-dbus.c | 4 +++-
src/gatt.h | 5 ++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 5dbd82e..916f727 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -186,7 +186,9 @@ static void proxy_write_reply(const DBusError *derr, void *user_data)
}
static void proxy_write_cb(struct btd_attribute *attr,
- const uint8_t *value, size_t len)
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data)
{
GDBusProxy *proxy;
diff --git a/src/gatt.h b/src/gatt.h
index 41fb3e5..447a380 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -48,8 +48,11 @@ typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
* @err: error in -errno format.
* @user_data: user_data passed in btd_attr_write_t callback
*/
+typedef void (*btd_attr_write_result_t) (int err, void *user_data);
typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
- const uint8_t *value, size_t len);
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
--
1.8.3.1
This patch adds the callback for writing the external characteristic
value. Internally, GDBusProxy implementation calls Set method of the
"Value" property. At the moment, potential errors returned by the
external service implementation are being ignored.
The ATT operation (Write Command or Request) is not exposed to the
external service implementation. GATT/GDBusProxy handles possible
DBus errors returned, and notify the core using the callbacks informed.
Write Command doesn't have callback since it doesn't return ATT errors.
---
src/gatt-dbus.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 629e3c3..5dbd82e 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -179,6 +179,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
result(0, value, len, user_data);
}
+static void proxy_write_reply(const DBusError *derr, void *user_data)
+{
+ if (derr)
+ DBG("Write reply: %s", derr->message);
+}
+
+static void proxy_write_cb(struct btd_attribute *attr,
+ const uint8_t *value, size_t len)
+{
+ GDBusProxy *proxy;
+
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (!proxy)
+ /* FIXME: Attribute not found */
+ return;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ NULL, NULL);
+
+ DBG("Server: Write characteristic callback %s",
+ g_dbus_proxy_get_path(proxy));
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -238,7 +262,8 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb,
+ proxy_write_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This patch adds a new gdbus utility function to allow setting a property
of fixed, and non-fixed values array.
---
gdbus/client.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdbus/gdbus.h | 5 ++++
2 files changed, 90 insertions(+)
diff --git a/gdbus/client.c b/gdbus/client.c
index 5193b6c..8f4fe4e 100644
--- a/gdbus/client.c
+++ b/gdbus/client.c
@@ -727,6 +727,91 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
return TRUE;
}
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ size_t size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy)
+{
+ struct set_property_data *data;
+ GDBusClient *client;
+ DBusMessage *msg;
+ DBusMessageIter iter, variant, array;
+ DBusPendingCall *call;
+ char array_sig[3];
+ char type_sig[2];
+
+ if (!proxy || !name || !value)
+ return FALSE;
+
+ if (!dbus_type_is_basic(type))
+ return FALSE;
+
+ client = proxy->client;
+ if (!client)
+ return FALSE;
+
+ data = g_try_new0(struct set_property_data, 1);
+ if (!data)
+ return FALSE;
+
+ data->function = function;
+ data->user_data = user_data;
+ data->destroy = destroy;
+
+ msg = dbus_message_new_method_call(client->service_name,
+ proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set");
+ if (!msg) {
+ g_free(data);
+ return FALSE;
+ }
+
+ array_sig[0] = DBUS_TYPE_ARRAY;
+ array_sig[1] = (char) type;
+ array_sig[2] = '\0';
+
+ type_sig[0] = (char) type;
+ type_sig[1] = '\0';
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &proxy->interface);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ array_sig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ type_sig, &array);
+
+ if (dbus_type_is_fixed(type))
+ dbus_message_iter_append_fixed_array(&array, type,
+ &value, size);
+ else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+ size_t i;
+ const char **str = (const char **) value;
+
+ for (i = 0; i < size; i++)
+ dbus_message_iter_append_basic(&array, type, &str[i]);
+ }
+
+ dbus_message_iter_close_container(&variant, &array);
+ dbus_message_iter_close_container(&iter, &variant);
+
+ if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+ &call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ g_free(data);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
struct method_call_data {
GDBusReturnFunction function;
void *user_data;
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 8ada200..551c306 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -329,6 +329,11 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
GDBusResultFunction function, void *user_data,
GDBusDestroyFunction destroy);
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ size_t size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy);
+
typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data);
typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data);
--
1.8.3.1
This patch adds a function callback for write operations. When a remote
device writes to a given attribute, the core calls the specified write
callback informing the attribute server the new characteristic value.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 ++++---
src/gatt.h | 13 ++++++++++++-
3 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index a5a862b..629e3c3 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -238,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 76bbe20..45b5e26 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -46,6 +46,7 @@ struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
btd_attr_read_t read_cb;
+ btd_attr_write_t write_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -133,7 +134,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb)
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -192,8 +194,7 @@ struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
char_value->type = *uuid;
char_value->read_cb = read_cb;
-
- /* TODO: Write callbacks */
+ char_value->write_cb = write_cb;
if (local_database_add(next_handle, char_value) < 0)
/* TODO: remove declaration */
diff --git a/src/gatt.h b/src/gatt.h
index daa8d54..41fb3e5 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -42,6 +42,14 @@ typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
btd_attr_read_result_t result,
void *user_data);
+/*
+ * Callbacks from this type are called once the value from the attribute was
+ * written.
+ * @err: error in -errno format.
+ * @user_data: user_data passed in btd_attr_write_t callback
+ */
+typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
+ const uint8_t *value, size_t len);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
@@ -57,10 +65,13 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
* @read_cb: Callback used to provide the characteristic value.
+ * @write_cb: Callback called to notify the implementation that a new value
+ * is available.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb);
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb);
--
1.8.3.1
This patch adds the callback for reading the external characteristic
Value. Internally, GDBusProxy implementation tracks all properties Value
changes consequently Value can be ready directly from the proxy without
additional method calls.
---
src/gatt-dbus.c | 42 +++++++++++++++++++++++++++++++++++++++++-
src/gatt.h | 2 +-
2 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 6c4cdf7..a5a862b 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -139,6 +139,46 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
eapp->proxies = g_slist_remove(eapp->proxies, proxy);
}
+static void proxy_read_cb(struct btd_attribute *attr,
+ btd_attr_read_result_t result, void *user_data)
+{
+ DBusMessageIter iter, array;
+ GDBusProxy *proxy;
+ uint8_t *value;
+ int len;
+
+ /*
+ * Remote device is trying to read the informed attribute,
+ * "Value" should be read from the proxy. GDBusProxy tracks
+ * properties changes automatically, it is not necessary to
+ * get the value directly from the GATT server.
+ */
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (!proxy) {
+ result(-ENOENT, NULL, 0, user_data);
+ return;
+ }
+
+ if (!g_dbus_proxy_get_property(proxy, "Value", &iter)) {
+ /* Unusual situation, read property will checked earlier */
+ result(-EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ DBG("External service inconsistent!");
+ result(-EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ DBG("attribute: %p read %d bytes", attr, len);
+
+ result(0, value, len, user_data);
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -198,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.h b/src/gatt.h
index e446fa0..daa8d54 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -32,7 +32,7 @@ void gatt_cleanup(void);
* ready to be read from the service implementation. Result callback is
* the asynchronous function that should be used to inform the caller
* the read value.
- * @err: error in errno format.
+ * @err: error in -errno format.
* @value: pointer to value
* @len: length of value
* @user_data: user_data passed in btd_attr_read_t callback
--
1.8.3.1
Remaining patches of "GATT basic characteristics" patchset.
This patchset adds the following features:
* basic read or write external characteristics
TODO (missing features of this patchset and upstream code):
* Properties/flags: operations and security
* 32-bit UUIDs
* Remove/free services
* multiple services provided by the same client
* remove att_put_* att_get_* helpers
Changes from v7 to v6:
* coding style: !foo and boolean
Changes from v6 to v5:
* Use size_t instead of signed integer for
g_dbus_proxy_set_property_array
* Use negative value to denote an error (-ernno)
Changes from v5 to v4:
* avoid usage of uint128_t in the header util.h
Changes from v4 to v3:
* keep LE <-> CPU helpers in lib/bluetooth.h (patches removing these
helpers are being shifted to next patchset)
Changes from v3 to v2:
* return put_uuid() definition to src/gatt.c
* add put_le16(), put_le32(), put_le128() to src/shared/util.h
Changes from v2 to v1:
* move put_uuid() and put_le16() to src/shared/util.h
* check malloc0() and new0() return value
Changes from v1 to v0:
* Use LE <-> CPU helpers defined in src/shared/util.h
* remove patch "gatt: Add helper for reading characteristics" from
this set.
Alvaro Silva (3):
tools: Add Alert Level characteristic to gatt-service
tools: Add reading Value property of gatt-service
tools: Add setting Value property of gatt-service
Claudio Takahasi (7):
gatt: Assign read callback for external services
gatt: Add write callback to btd_gatt_add_char helper
gdbus: Add g_dbus_proxy_set_property_array
gatt: Assign write callback for external services
gatt: Add result callback for Write Request
gatt: Add Write Request handling for GDBusProxy
tools: Emit property changed when Value changes
gdbus/client.c | 85 ++++++++++++++++++++++++++++
gdbus/gdbus.h | 5 ++
src/gatt-dbus.c | 124 +++++++++++++++++++++++++++++++++++++++-
src/gatt.c | 7 ++-
src/gatt.h | 18 +++++-
tools/gatt-service.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++----
6 files changed, 377 insertions(+), 18 deletions(-)
--
1.8.3.1
This patch extends gatt-service tool, emitting PropertiesChanged for
"Value" when a new value is set. This flow represents the handling of
ATT Write Command or Write Request which is translated to a GDBusProxy
set property. PropertiesChanged will be tracked by the characteristic
GDBusProxy in order to detect "Value" changes.
---
tools/gatt-service.c | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index ad5399b..8dbecce 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -47,9 +47,11 @@
static GMainLoop *main_loop;
static GSList *services;
+static DBusConnection *connection;
struct characteristic {
char *uuid;
+ char *path;
uint8_t *value;
int vlen;
};
@@ -110,6 +112,8 @@ static void chr_set_value(const GDBusPropertyTable *property,
chr->vlen = len;
g_dbus_pending_property_success(id);
+ g_dbus_emit_property_changed(connection, chr->path,
+ GATT_CHR_IFACE, "Value");
}
static const GDBusPropertyTable chr_properties[] = {
@@ -163,6 +167,7 @@ static void chr_iface_destroy(gpointer user_data)
g_free(chr->uuid);
g_free(chr->value);
+ g_free(chr->path);
g_free(chr);
}
@@ -182,6 +187,7 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
chr->uuid = g_strdup(uuid);
chr->value = g_memdup(value, vlen);
chr->vlen = vlen;
+ chr->path = path;
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
@@ -190,18 +196,16 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
ret = -EIO;
}
- g_free(path);
-
return ret;
}
-static char *register_service(DBusConnection *conn, const char *uuid)
+static char *register_service(const char *uuid)
{
static int id = 1;
char *path;
path = g_strdup_printf("/service%d", id++);
- if (g_dbus_register_interface(conn, path, GATT_SERVICE_IFACE,
+ if (g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
NULL, NULL, service_properties,
g_strdup(uuid), g_free) == FALSE) {
printf("Couldn't register service interface\n");
@@ -212,13 +216,13 @@ static char *register_service(DBusConnection *conn, const char *uuid)
return path;
}
-static void create_services(DBusConnection *conn)
+static void create_services()
{
char *service_path;
uint8_t level = 0;
int ret;
- service_path = register_service(conn, IAS_UUID);
+ service_path = register_service(IAS_UUID);
if (service_path == NULL)
return;
@@ -226,11 +230,11 @@ static void create_services(DBusConnection *conn)
* According to the IAS SPEC, reading <<Alert level>> is not alloweed.
* "Value" is readable for testing purpose only.
*/
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ ret = register_characteristic(connection, ALERT_LEVEL_CHR_UUID, &level,
sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
- g_dbus_unregister_interface(conn, service_path,
+ g_dbus_unregister_interface(connection, service_path,
GATT_SERVICE_IFACE);
g_free(service_path);
return;
@@ -371,25 +375,24 @@ static guint setup_signalfd(void)
int main(int argc, char *argv[])
{
GDBusClient *client;
- DBusConnection *dbus_conn;
guint signal;
signal = setup_signalfd();
if (signal == 0)
return -errno;
- dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
main_loop = g_main_loop_new(NULL, FALSE);
- g_dbus_attach_object_manager(dbus_conn);
+ g_dbus_attach_object_manager(connection);
printf("gatt-service unique name: %s\n",
- dbus_bus_get_unique_name(dbus_conn));
+ dbus_bus_get_unique_name(connection));
- create_services(dbus_conn);
+ create_services();
- client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+ client = g_dbus_client_new(connection, "org.bluez", "/org/bluez");
g_dbus_client_set_connect_watch(client, connect_handler, NULL);
@@ -400,7 +403,7 @@ int main(int argc, char *argv[])
g_source_remove(signal);
g_slist_free_full(services, g_free);
- dbus_connection_unref(dbus_conn);
+ dbus_connection_unref(connection);
return 0;
}
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding a generic callback
to allow set the characteristic Value. It doesn't check for
characteristic properties yet.
---
tools/gatt-service.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 97dc8d4..ad5399b 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -35,6 +35,8 @@
#include <dbus/dbus.h>
#include <gdbus/gdbus.h>
+#include "src/error.h"
+
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
@@ -81,9 +83,38 @@ static gboolean chr_get_value(const GDBusPropertyTable *property,
return TRUE;
}
+static void chr_set_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+ uint8_t *value;
+ int len;
+
+ printf("Set('Value', ...)\n");
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ printf("Invalid value for Set('Value'...)\n");
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ g_free(chr->value);
+ chr->value = g_memdup(value, len);
+ chr->vlen = len;
+
+ g_dbus_pending_property_success(id);
+}
+
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
- { "Value", "ay", chr_get_value, NULL, NULL },
+ { "Value", "ay", chr_get_value, chr_set_value, NULL },
{ }
};
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding the callback to
allow reading the Value property. At the moment, it is a generic
callback and it doesn't consider characteristic value property
restrictions.
---
tools/gatt-service.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 53 insertions(+), 6 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index d1606ad..97dc8d4 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -46,18 +46,44 @@
static GMainLoop *main_loop;
static GSList *services;
+struct characteristic {
+ char *uuid;
+ uint8_t *value;
+ int vlen;
+};
+
static gboolean chr_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
- const char *uuid = user_data;
+ struct characteristic *chr = user_data;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid);
+
+ return TRUE;
+}
+
+static gboolean chr_get_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+
+ printf("Get(\"Value\")\n");
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &chr->value, chr->vlen);
+
+ dbus_message_iter_close_container(iter, &array);
return TRUE;
}
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
+ { "Value", "ay", chr_get_value, NULL, NULL },
{ }
};
@@ -100,18 +126,35 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static void chr_iface_destroy(gpointer user_data)
+{
+ struct characteristic *chr = user_data;
+
+ g_free(chr->uuid);
+ g_free(chr->value);
+ g_free(chr);
+}
+
static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const uint8_t *value, int vlen,
const char *service_path)
{
+ struct characteristic *chr;
static int id = 1;
char *path;
int ret = 0;
path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+ chr = g_new0(struct characteristic, 1);
+
+ chr->uuid = g_strdup(uuid);
+ chr->value = g_memdup(value, vlen);
+ chr->vlen = vlen;
+
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
- g_strdup(uuid), g_free) == FALSE) {
+ chr, chr_iface_destroy) == FALSE) {
printf("Couldn't register characteristic interface\n");
ret = -EIO;
}
@@ -141,15 +184,19 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ uint8_t level = 0;
int ret;
service_path = register_service(conn, IAS_UUID);
if (service_path == NULL)
return;
- /* Add Alert Level Characteristic to Immediate Alert Service */
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
- service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service
+ * According to the IAS SPEC, reading <<Alert level>> is not alloweed.
+ * "Value" is readable for testing purpose only.
+ */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
g_dbus_unregister_interface(conn, service_path,
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch registers the Alert Level characteristic object related to
Immediate Alert Service. It adds the characteristic UUID property only.
---
tools/gatt-service.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index cba0ff3..d1606ad 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -37,13 +37,30 @@
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
+#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
/* Immediate Alert Service UUID */
#define IAS_UUID "00001802-0000-1000-8000-00805f9b34fb"
+#define ALERT_LEVEL_CHR_UUID "00002a06-0000-1000-8000-00805f9b34fb"
static GMainLoop *main_loop;
static GSList *services;
+static gboolean chr_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *uuid = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable chr_properties[] = {
+ { "UUID", "s", chr_get_uuid },
+ { }
+};
+
static gboolean service_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
@@ -83,6 +100,27 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const char *service_path)
+{
+ static int id = 1;
+ char *path;
+ int ret = 0;
+
+ path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+
+ if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
+ NULL, NULL, chr_properties,
+ g_strdup(uuid), g_free) == FALSE) {
+ printf("Couldn't register characteristic interface\n");
+ ret = -EIO;
+ }
+
+ g_free(path);
+
+ return ret;
+}
+
static char *register_service(DBusConnection *conn, const char *uuid)
{
static int id = 1;
@@ -103,11 +141,24 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ int ret;
service_path = register_service(conn, IAS_UUID);
+ if (service_path == NULL)
+ return;
- services = g_slist_prepend(services, service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
+ service_path);
+ if (ret < 0) {
+ printf("Couldn't register Alert Level characteristic (IAS)\n");
+ g_dbus_unregister_interface(conn, service_path,
+ GATT_SERVICE_IFACE);
+ g_free(service_path);
+ return;
+ }
+ services = g_slist_prepend(services, service_path);
printf("Registered service: %s\n", service_path);
}
--
1.8.3.1
When the characteristic requires Write Request, the ATT response should
be sent after receiving the reply for the Set method call.
---
src/gatt-dbus.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 61 insertions(+), 6 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 6483d76..830eaf8 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,11 @@ struct external_app {
unsigned int watch;
};
+struct proxy_write_data {
+ btd_attr_write_result_t result_cb;
+ void *user_data;
+};
+
/*
* Attribute to Proxy hash table. Used to map incoming
* ATT operations to its external characteristic proxy.
@@ -181,8 +186,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
static void proxy_write_reply(const DBusError *derr, void *user_data)
{
- if (derr)
- DBG("Write reply: %s", derr->message);
+ struct proxy_write_data *wdata = user_data;
+ int err = 0;
+
+ /*
+ * Security requirements shall be handled by the core. If external
+ * applications returns an error, the reasons will be restricted to
+ * invalid argument or application specific errors.
+ */
+
+ if (dbus_error_is_set(derr) == FALSE)
+ goto done;
+
+ DBG("Write reply: %s", derr->message);
+
+ if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
+ err = -ETIMEDOUT;
+ else if (dbus_error_has_name(derr, ERROR_INTERFACE ".InvalidArguments"))
+ err = -EINVAL;
+ else
+ err = -EPROTO;
+
+done:
+ if (wdata && wdata->result_cb)
+ wdata->result_cb(err, wdata->user_data);
}
static void proxy_write_cb(struct btd_attribute *attr,
@@ -193,16 +220,44 @@ static void proxy_write_cb(struct btd_attribute *attr,
GDBusProxy *proxy;
proxy = g_hash_table_lookup(proxy_hash, attr);
- if (proxy == NULL)
- /* FIXME: Attribute not found */
+ if (proxy == NULL) {
+ result(-ENOENT, user_data);
return;
+ }
- g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ /*
+ * "result" callback defines if the core wants to receive the
+ * operation result, allowing to select ATT Write Request or Write
+ * Command. Descriptors requires Write Request operation. For
+ * Characteristics, the implementation will define which operations
+ * are allowed based on the properties/flags.
+ * TODO: Write Long Characteristics/Descriptors.
+ */
+
+ if (result) {
+ struct proxy_write_data *wdata;
+
+ wdata = g_new0(struct proxy_write_data, 1);
+ wdata->result_cb = result;
+ wdata->user_data = user_data;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ wdata, g_free);
+ } else {
+ /*
+ * Caller is not interested in the Set method call result.
+ * This flow implements the ATT Write Command scenario, where
+ * the remote doesn't receive ATT response.
+ */
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
value, len, proxy_write_reply,
NULL, NULL);
+ }
- DBG("Server: Write characteristic callback %s",
+ DBG("Server: Write attribute callback %s",
g_dbus_proxy_get_path(proxy));
+
}
static int register_external_service(const struct external_app *eapp,
--
1.8.3.1
This patch adds an extra callback to inform the result of the write
operation on an external entity to allow the core to reply properly
for the ATT request.
---
src/gatt-dbus.c | 4 +++-
src/gatt.h | 5 ++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 04c2b0d..6483d76 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -186,7 +186,9 @@ static void proxy_write_reply(const DBusError *derr, void *user_data)
}
static void proxy_write_cb(struct btd_attribute *attr,
- const uint8_t *value, size_t len)
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data)
{
GDBusProxy *proxy;
diff --git a/src/gatt.h b/src/gatt.h
index 41fb3e5..447a380 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -48,8 +48,11 @@ typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
* @err: error in -errno format.
* @user_data: user_data passed in btd_attr_write_t callback
*/
+typedef void (*btd_attr_write_result_t) (int err, void *user_data);
typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
- const uint8_t *value, size_t len);
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
--
1.8.3.1
This patch adds the callback for writing the external characteristic
value. Internally, GDBusProxy implementation calls Set method of the
"Value" property. At the moment, potential errors returned by the
external service implementation are being ignored.
The ATT operation (Write Command or Request) is not exposed to the
external service implementation. GATT/GDBusProxy handles possible
DBus errors returned, and notify the core using the callbacks informed.
Write Command doesn't have callback since it doesn't return ATT errors.
---
src/gatt-dbus.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 1a10338..04c2b0d 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -179,6 +179,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
result(0, value, len, user_data);
}
+static void proxy_write_reply(const DBusError *derr, void *user_data)
+{
+ if (derr)
+ DBG("Write reply: %s", derr->message);
+}
+
+static void proxy_write_cb(struct btd_attribute *attr,
+ const uint8_t *value, size_t len)
+{
+ GDBusProxy *proxy;
+
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL)
+ /* FIXME: Attribute not found */
+ return;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ NULL, NULL);
+
+ DBG("Server: Write characteristic callback %s",
+ g_dbus_proxy_get_path(proxy));
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -238,7 +262,8 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb,
+ proxy_write_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This patch adds a new gdbus utility function to allow setting a property
of fixed, and non-fixed values array.
---
gdbus/client.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdbus/gdbus.h | 5 ++++
2 files changed, 90 insertions(+)
diff --git a/gdbus/client.c b/gdbus/client.c
index 5193b6c..0f87635 100644
--- a/gdbus/client.c
+++ b/gdbus/client.c
@@ -727,6 +727,91 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
return TRUE;
}
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ size_t size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy)
+{
+ struct set_property_data *data;
+ GDBusClient *client;
+ DBusMessage *msg;
+ DBusMessageIter iter, variant, array;
+ DBusPendingCall *call;
+ char array_sig[3];
+ char type_sig[2];
+
+ if (proxy == NULL || name == NULL || value == NULL)
+ return FALSE;
+
+ if (dbus_type_is_basic(type) == FALSE)
+ return FALSE;
+
+ client = proxy->client;
+ if (client == NULL)
+ return FALSE;
+
+ data = g_try_new0(struct set_property_data, 1);
+ if (data == NULL)
+ return FALSE;
+
+ data->function = function;
+ data->user_data = user_data;
+ data->destroy = destroy;
+
+ msg = dbus_message_new_method_call(client->service_name,
+ proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set");
+ if (msg == NULL) {
+ g_free(data);
+ return FALSE;
+ }
+
+ array_sig[0] = DBUS_TYPE_ARRAY;
+ array_sig[1] = (char) type;
+ array_sig[2] = '\0';
+
+ type_sig[0] = (char) type;
+ type_sig[1] = '\0';
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &proxy->interface);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ array_sig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ type_sig, &array);
+
+ if (dbus_type_is_fixed(type) == TRUE)
+ dbus_message_iter_append_fixed_array(&array, type,
+ &value, size);
+ else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+ size_t i;
+ const char **str = (const char **) value;
+
+ for (i = 0; i < size; i++)
+ dbus_message_iter_append_basic(&array, type, &str[i]);
+ }
+
+ dbus_message_iter_close_container(&variant, &array);
+ dbus_message_iter_close_container(&iter, &variant);
+
+ if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+ &call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ g_free(data);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
struct method_call_data {
GDBusReturnFunction function;
void *user_data;
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 8ada200..551c306 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -329,6 +329,11 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
GDBusResultFunction function, void *user_data,
GDBusDestroyFunction destroy);
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ size_t size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy);
+
typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data);
typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data);
--
1.8.3.1
This patch adds a function callback for write operations. When a remote
device writes to a given attribute, the core calls the specified write
callback informing the attribute server the new characteristic value.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 ++++---
src/gatt.h | 13 ++++++++++++-
3 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 701fc5d..1a10338 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -238,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 76bbe20..45b5e26 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -46,6 +46,7 @@ struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
btd_attr_read_t read_cb;
+ btd_attr_write_t write_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -133,7 +134,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb)
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -192,8 +194,7 @@ struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
char_value->type = *uuid;
char_value->read_cb = read_cb;
-
- /* TODO: Write callbacks */
+ char_value->write_cb = write_cb;
if (local_database_add(next_handle, char_value) < 0)
/* TODO: remove declaration */
diff --git a/src/gatt.h b/src/gatt.h
index daa8d54..41fb3e5 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -42,6 +42,14 @@ typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
btd_attr_read_result_t result,
void *user_data);
+/*
+ * Callbacks from this type are called once the value from the attribute was
+ * written.
+ * @err: error in -errno format.
+ * @user_data: user_data passed in btd_attr_write_t callback
+ */
+typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
+ const uint8_t *value, size_t len);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
@@ -57,10 +65,13 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
* @read_cb: Callback used to provide the characteristic value.
+ * @write_cb: Callback called to notify the implementation that a new value
+ * is available.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb);
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb);
--
1.8.3.1
This patch adds the callback for reading the external characteristic
Value. Internally, GDBusProxy implementation tracks all properties Value
changes consequently Value can be ready directly from the proxy without
additional method calls.
---
src/gatt-dbus.c | 42 +++++++++++++++++++++++++++++++++++++++++-
src/gatt.h | 2 +-
2 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 275b263..701fc5d 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -139,6 +139,46 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
eapp->proxies = g_slist_remove(eapp->proxies, proxy);
}
+static void proxy_read_cb(struct btd_attribute *attr,
+ btd_attr_read_result_t result, void *user_data)
+{
+ DBusMessageIter iter, array;
+ GDBusProxy *proxy;
+ uint8_t *value;
+ int len;
+
+ /*
+ * Remote device is trying to read the informed attribute,
+ * "Value" should be read from the proxy. GDBusProxy tracks
+ * properties changes automatically, it is not necessary to
+ * get the value directly from the GATT server.
+ */
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL) {
+ result(-ENOENT, NULL, 0, user_data);
+ return;
+ }
+
+ if (!g_dbus_proxy_get_property(proxy, "Value", &iter)) {
+ /* Unusual situation, read property will checked earlier */
+ result(-EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ DBG("External service inconsistent!");
+ result(-EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ DBG("attribute: %p read %d bytes", attr, len);
+
+ result(0, value, len, user_data);
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -198,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.h b/src/gatt.h
index e446fa0..daa8d54 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -32,7 +32,7 @@ void gatt_cleanup(void);
* ready to be read from the service implementation. Result callback is
* the asynchronous function that should be used to inform the caller
* the read value.
- * @err: error in errno format.
+ * @err: error in -errno format.
* @value: pointer to value
* @len: length of value
* @user_data: user_data passed in btd_attr_read_t callback
--
1.8.3.1
Remaining patches of "GATT basic characteristics" patchset.
This patchset adds the following features:
* basic read or write external characteristics
TODO (missing features of this patchset and upstream code):
* Properties/flags: operations and security
* 32-bit UUIDs
* Remove/free services
* multiple services provided by the same client
* remove att_put_* att_get_* helpers
Changes from v6 to v5:
* Use size_t instead of signed integer for
g_dbus_proxy_set_property_array
* Use negative value to denote an error (-ernno)
Changes from v5 to v4:
* avoid usage of uint128_t in the header util.h
Changes from v4 to v3:
* keep LE <-> CPU helpers in lib/bluetooth.h (patches removing these
helpers are being shifted to next patchset)
Changes from v3 to v2:
* return put_uuid() definition to src/gatt.c
* add put_le16(), put_le32(), put_le128() to src/shared/util.h
Changes from v2 to v1:
* move put_uuid() and put_le16() to src/shared/util.h
* check malloc0() and new0() return value
Changes from v1 to v0:
* Use LE <-> CPU helpers defined in src/shared/util.h
* remove patch "gatt: Add helper for reading characteristics" from
this set.
Alvaro Silva (3):
tools: Add Alert Level characteristic to gatt-service
tools: Add reading Value property of gatt-service
tools: Add setting Value property of gatt-service
Claudio Takahasi (7):
gatt: Assign read callback for external services
gatt: Add write callback to btd_gatt_add_char helper
gdbus: Add g_dbus_proxy_set_property_array
gatt: Assign write callback for external services
gatt: Add result callback for Write Request
gatt: Add Write Request handling for GDBusProxy
tools: Emit property changed when Value changes
gdbus/client.c | 85 ++++++++++++++++++++++++++++
gdbus/gdbus.h | 5 ++
src/gatt-dbus.c | 124 +++++++++++++++++++++++++++++++++++++++-
src/gatt.c | 7 ++-
src/gatt.h | 18 +++++-
tools/gatt-service.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++----
6 files changed, 377 insertions(+), 18 deletions(-)
--
1.8.3.1
Hi Johan:
On Fri, Mar 21, 2014 at 5:49 AM, Johan Hedberg <[email protected]> wrote:
> Hi Claudio,
>
> On Tue, Mar 18, 2014, Claudio Takahasi wrote:
>> This patch adds a new gdbus utility function to allow setting a property
>> of fixed, and non-fixed values array.
>> ---
>> gdbus/client.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> gdbus/gdbus.h | 5 ++++
>> 2 files changed, 90 insertions(+)
>>
>> diff --git a/gdbus/client.c b/gdbus/client.c
>> index 5193b6c..6fea3d9 100644
>> --- a/gdbus/client.c
>> +++ b/gdbus/client.c
>> @@ -727,6 +727,91 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
>> return TRUE;
>> }
>>
>> +gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
>> + const char *name, int type, const void *value,
>> + int size, GDBusResultFunction function,
>> + void *user_data, GDBusDestroyFunction destroy)
>
> Would size_t make more sense for the size parameter? Can it be negative
> (in which case maybe ssize_t)?
>
> Johan
Indeed, it doesn't make to use signed.
I will fix it.
Regards,
Claudio
Hi Claudio,
On Tue, Mar 18, 2014, Claudio Takahasi wrote:
> This patch adds a new gdbus utility function to allow setting a property
> of fixed, and non-fixed values array.
> ---
> gdbus/client.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> gdbus/gdbus.h | 5 ++++
> 2 files changed, 90 insertions(+)
>
> diff --git a/gdbus/client.c b/gdbus/client.c
> index 5193b6c..6fea3d9 100644
> --- a/gdbus/client.c
> +++ b/gdbus/client.c
> @@ -727,6 +727,91 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
> return TRUE;
> }
>
> +gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
> + const char *name, int type, const void *value,
> + int size, GDBusResultFunction function,
> + void *user_data, GDBusDestroyFunction destroy)
Would size_t make more sense for the size parameter? Can it be negative
(in which case maybe ssize_t)?
Johan
Hi Claudio,
On Tue, Mar 18, 2014, Claudio Takahasi wrote:
> This patch adds the callback for reading the external characteristic
> Value. Internally, GDBusProxy implementation tracks all properties Value
> changes consequently Value can be ready directly from the proxy without
> additional method calls.
> ---
> src/gatt-dbus.c | 42 +++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 41 insertions(+), 1 deletion(-)
The first 6 patches have been applied. However:
> +static void proxy_read_cb(struct btd_attribute *attr,
> + btd_attr_read_result_t result, void *user_data)
> +{
> + DBusMessageIter iter, array;
> + GDBusProxy *proxy;
> + uint8_t *value;
> + int len;
> +
> + /*
> + * Remote device is trying to read the informed attribute,
> + * "Value" should be read from the proxy. GDBusProxy tracks
> + * properties changes automatically, it is not necessary to
> + * get the value directly from the GATT server.
> + */
> + proxy = g_hash_table_lookup(proxy_hash, attr);
> + if (proxy == NULL) {
> + result(ENOENT, NULL, 0, user_data);
> + return;
> + }
> +
> + if (!g_dbus_proxy_get_property(proxy, "Value", &iter)) {
> + /* Unusual situation, read property will checked earlier */
> + result(EPERM, NULL, 0, user_data);
> + return;
> + }
> +
> + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
> + DBG("External service inconsistent!");
> + result(EPERM, NULL, 0, user_data);
> + return;
> + }
Whenever we have "int err" variables a negative value denotes an error.
Let's keep this convention consistent throughout the code base.
Johan
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding a generic callback
to allow set the characteristic Value. It doesn't check for
characteristic properties yet.
---
tools/gatt-service.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 97dc8d4..ad5399b 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -35,6 +35,8 @@
#include <dbus/dbus.h>
#include <gdbus/gdbus.h>
+#include "src/error.h"
+
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
@@ -81,9 +83,38 @@ static gboolean chr_get_value(const GDBusPropertyTable *property,
return TRUE;
}
+static void chr_set_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+ uint8_t *value;
+ int len;
+
+ printf("Set('Value', ...)\n");
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ printf("Invalid value for Set('Value'...)\n");
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ g_free(chr->value);
+ chr->value = g_memdup(value, len);
+ chr->vlen = len;
+
+ g_dbus_pending_property_success(id);
+}
+
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
- { "Value", "ay", chr_get_value, NULL, NULL },
+ { "Value", "ay", chr_get_value, chr_set_value, NULL },
{ }
};
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch registers the Alert Level characteristic object related to
Immediate Alert Service. It adds the characteristic UUID property only.
---
tools/gatt-service.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index cba0ff3..d1606ad 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -37,13 +37,30 @@
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
+#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
/* Immediate Alert Service UUID */
#define IAS_UUID "00001802-0000-1000-8000-00805f9b34fb"
+#define ALERT_LEVEL_CHR_UUID "00002a06-0000-1000-8000-00805f9b34fb"
static GMainLoop *main_loop;
static GSList *services;
+static gboolean chr_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *uuid = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable chr_properties[] = {
+ { "UUID", "s", chr_get_uuid },
+ { }
+};
+
static gboolean service_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
@@ -83,6 +100,27 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const char *service_path)
+{
+ static int id = 1;
+ char *path;
+ int ret = 0;
+
+ path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+
+ if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
+ NULL, NULL, chr_properties,
+ g_strdup(uuid), g_free) == FALSE) {
+ printf("Couldn't register characteristic interface\n");
+ ret = -EIO;
+ }
+
+ g_free(path);
+
+ return ret;
+}
+
static char *register_service(DBusConnection *conn, const char *uuid)
{
static int id = 1;
@@ -103,11 +141,24 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ int ret;
service_path = register_service(conn, IAS_UUID);
+ if (service_path == NULL)
+ return;
- services = g_slist_prepend(services, service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
+ service_path);
+ if (ret < 0) {
+ printf("Couldn't register Alert Level characteristic (IAS)\n");
+ g_dbus_unregister_interface(conn, service_path,
+ GATT_SERVICE_IFACE);
+ g_free(service_path);
+ return;
+ }
+ services = g_slist_prepend(services, service_path);
printf("Registered service: %s\n", service_path);
}
--
1.8.3.1
This patch extends gatt-service tool, emitting PropertiesChanged for
"Value" when a new value is set. This flow represents the handling of
ATT Write Command or Write Request which is translated to a GDBusProxy
set property. PropertiesChanged will be tracked by the characteristic
GDBusProxy in order to detect "Value" changes.
---
tools/gatt-service.c | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index ad5399b..8dbecce 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -47,9 +47,11 @@
static GMainLoop *main_loop;
static GSList *services;
+static DBusConnection *connection;
struct characteristic {
char *uuid;
+ char *path;
uint8_t *value;
int vlen;
};
@@ -110,6 +112,8 @@ static void chr_set_value(const GDBusPropertyTable *property,
chr->vlen = len;
g_dbus_pending_property_success(id);
+ g_dbus_emit_property_changed(connection, chr->path,
+ GATT_CHR_IFACE, "Value");
}
static const GDBusPropertyTable chr_properties[] = {
@@ -163,6 +167,7 @@ static void chr_iface_destroy(gpointer user_data)
g_free(chr->uuid);
g_free(chr->value);
+ g_free(chr->path);
g_free(chr);
}
@@ -182,6 +187,7 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
chr->uuid = g_strdup(uuid);
chr->value = g_memdup(value, vlen);
chr->vlen = vlen;
+ chr->path = path;
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
@@ -190,18 +196,16 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
ret = -EIO;
}
- g_free(path);
-
return ret;
}
-static char *register_service(DBusConnection *conn, const char *uuid)
+static char *register_service(const char *uuid)
{
static int id = 1;
char *path;
path = g_strdup_printf("/service%d", id++);
- if (g_dbus_register_interface(conn, path, GATT_SERVICE_IFACE,
+ if (g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
NULL, NULL, service_properties,
g_strdup(uuid), g_free) == FALSE) {
printf("Couldn't register service interface\n");
@@ -212,13 +216,13 @@ static char *register_service(DBusConnection *conn, const char *uuid)
return path;
}
-static void create_services(DBusConnection *conn)
+static void create_services()
{
char *service_path;
uint8_t level = 0;
int ret;
- service_path = register_service(conn, IAS_UUID);
+ service_path = register_service(IAS_UUID);
if (service_path == NULL)
return;
@@ -226,11 +230,11 @@ static void create_services(DBusConnection *conn)
* According to the IAS SPEC, reading <<Alert level>> is not alloweed.
* "Value" is readable for testing purpose only.
*/
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ ret = register_characteristic(connection, ALERT_LEVEL_CHR_UUID, &level,
sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
- g_dbus_unregister_interface(conn, service_path,
+ g_dbus_unregister_interface(connection, service_path,
GATT_SERVICE_IFACE);
g_free(service_path);
return;
@@ -371,25 +375,24 @@ static guint setup_signalfd(void)
int main(int argc, char *argv[])
{
GDBusClient *client;
- DBusConnection *dbus_conn;
guint signal;
signal = setup_signalfd();
if (signal == 0)
return -errno;
- dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
main_loop = g_main_loop_new(NULL, FALSE);
- g_dbus_attach_object_manager(dbus_conn);
+ g_dbus_attach_object_manager(connection);
printf("gatt-service unique name: %s\n",
- dbus_bus_get_unique_name(dbus_conn));
+ dbus_bus_get_unique_name(connection));
- create_services(dbus_conn);
+ create_services();
- client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+ client = g_dbus_client_new(connection, "org.bluez", "/org/bluez");
g_dbus_client_set_connect_watch(client, connect_handler, NULL);
@@ -400,7 +403,7 @@ int main(int argc, char *argv[])
g_source_remove(signal);
g_slist_free_full(services, g_free);
- dbus_connection_unref(dbus_conn);
+ dbus_connection_unref(connection);
return 0;
}
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding the callback to
allow reading the Value property. At the moment, it is a generic
callback and it doesn't consider characteristic value property
restrictions.
---
tools/gatt-service.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 53 insertions(+), 6 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index d1606ad..97dc8d4 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -46,18 +46,44 @@
static GMainLoop *main_loop;
static GSList *services;
+struct characteristic {
+ char *uuid;
+ uint8_t *value;
+ int vlen;
+};
+
static gboolean chr_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
- const char *uuid = user_data;
+ struct characteristic *chr = user_data;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid);
+
+ return TRUE;
+}
+
+static gboolean chr_get_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+
+ printf("Get(\"Value\")\n");
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &chr->value, chr->vlen);
+
+ dbus_message_iter_close_container(iter, &array);
return TRUE;
}
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
+ { "Value", "ay", chr_get_value, NULL, NULL },
{ }
};
@@ -100,18 +126,35 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static void chr_iface_destroy(gpointer user_data)
+{
+ struct characteristic *chr = user_data;
+
+ g_free(chr->uuid);
+ g_free(chr->value);
+ g_free(chr);
+}
+
static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const uint8_t *value, int vlen,
const char *service_path)
{
+ struct characteristic *chr;
static int id = 1;
char *path;
int ret = 0;
path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+ chr = g_new0(struct characteristic, 1);
+
+ chr->uuid = g_strdup(uuid);
+ chr->value = g_memdup(value, vlen);
+ chr->vlen = vlen;
+
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
- g_strdup(uuid), g_free) == FALSE) {
+ chr, chr_iface_destroy) == FALSE) {
printf("Couldn't register characteristic interface\n");
ret = -EIO;
}
@@ -141,15 +184,19 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ uint8_t level = 0;
int ret;
service_path = register_service(conn, IAS_UUID);
if (service_path == NULL)
return;
- /* Add Alert Level Characteristic to Immediate Alert Service */
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
- service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service
+ * According to the IAS SPEC, reading <<Alert level>> is not alloweed.
+ * "Value" is readable for testing purpose only.
+ */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
g_dbus_unregister_interface(conn, service_path,
--
1.8.3.1
This patch adds the callback for writing the external characteristic
value. Internally, GDBusProxy implementation calls Set method of the
"Value" property. At the moment, potential errors returned by the
external service implementation are being ignored.
The ATT operation (Write Command or Request) is not exposed to the
external service implementation. GATT/GDBusProxy handles possible
DBus errors returned, and notify the core using the callbacks informed.
Write Command doesn't have callback since it doesn't return ATT errors.
---
src/gatt-dbus.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index a02d565..7e81dc6 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -179,6 +179,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
result(0, value, len, user_data);
}
+static void proxy_write_reply(const DBusError *derr, void *user_data)
+{
+ if (derr)
+ DBG("Write reply: %s", derr->message);
+}
+
+static void proxy_write_cb(struct btd_attribute *attr,
+ const uint8_t *value, size_t len)
+{
+ GDBusProxy *proxy;
+
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL)
+ /* FIXME: Attribute not found */
+ return;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ NULL, NULL);
+
+ DBG("Server: Write characteristic callback %s",
+ g_dbus_proxy_get_path(proxy));
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -238,7 +262,8 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb,
+ proxy_write_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
When the characteristic requires Write Request, the ATT response should
be sent after receiving the reply for the Set method call.
---
src/gatt-dbus.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 61 insertions(+), 6 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index d703355..2412e2d 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,11 @@ struct external_app {
unsigned int watch;
};
+struct proxy_write_data {
+ btd_attr_write_result_t result_cb;
+ void *user_data;
+};
+
/*
* Attribute to Proxy hash table. Used to map incoming
* ATT operations to its external characteristic proxy.
@@ -181,8 +186,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
static void proxy_write_reply(const DBusError *derr, void *user_data)
{
- if (derr)
- DBG("Write reply: %s", derr->message);
+ struct proxy_write_data *wdata = user_data;
+ int err = 0;
+
+ /*
+ * Security requirements shall be handled by the core. If external
+ * applications returns an error, the reasons will be restricted to
+ * invalid argument or application specific errors.
+ */
+
+ if (dbus_error_is_set(derr) == FALSE)
+ goto done;
+
+ DBG("Write reply: %s", derr->message);
+
+ if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
+ err = ETIMEDOUT;
+ else if (dbus_error_has_name(derr, ERROR_INTERFACE ".InvalidArguments"))
+ err = EINVAL;
+ else
+ err = EPROTO;
+
+done:
+ if (wdata && wdata->result_cb)
+ wdata->result_cb(err, wdata->user_data);
}
static void proxy_write_cb(struct btd_attribute *attr,
@@ -193,16 +220,44 @@ static void proxy_write_cb(struct btd_attribute *attr,
GDBusProxy *proxy;
proxy = g_hash_table_lookup(proxy_hash, attr);
- if (proxy == NULL)
- /* FIXME: Attribute not found */
+ if (proxy == NULL) {
+ result(ENOENT, user_data);
return;
+ }
- g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ /*
+ * "result" callback defines if the core wants to receive the
+ * operation result, allowing to select ATT Write Request or Write
+ * Command. Descriptors requires Write Request operation. For
+ * Characteristics, the implementation will define which operations
+ * are allowed based on the properties/flags.
+ * TODO: Write Long Characteristics/Descriptors.
+ */
+
+ if (result) {
+ struct proxy_write_data *wdata;
+
+ wdata = g_new0(struct proxy_write_data, 1);
+ wdata->result_cb = result;
+ wdata->user_data = user_data;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ wdata, g_free);
+ } else {
+ /*
+ * Caller is not interested in the Set method call result.
+ * This flow implements the ATT Write Command scenario, where
+ * the remote doesn't receive ATT response.
+ */
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
value, len, proxy_write_reply,
NULL, NULL);
+ }
- DBG("Server: Write characteristic callback %s",
+ DBG("Server: Write attribute callback %s",
g_dbus_proxy_get_path(proxy));
+
}
static int register_external_service(const struct external_app *eapp,
--
1.8.3.1
This patch adds a new gdbus utility function to allow setting a property
of fixed, and non-fixed values array.
---
gdbus/client.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdbus/gdbus.h | 5 ++++
2 files changed, 90 insertions(+)
diff --git a/gdbus/client.c b/gdbus/client.c
index 5193b6c..6fea3d9 100644
--- a/gdbus/client.c
+++ b/gdbus/client.c
@@ -727,6 +727,91 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
return TRUE;
}
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ int size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy)
+{
+ struct set_property_data *data;
+ GDBusClient *client;
+ DBusMessage *msg;
+ DBusMessageIter iter, variant, array;
+ DBusPendingCall *call;
+ char array_sig[3];
+ char type_sig[2];
+
+ if (proxy == NULL || name == NULL || value == NULL)
+ return FALSE;
+
+ if (dbus_type_is_basic(type) == FALSE)
+ return FALSE;
+
+ client = proxy->client;
+ if (client == NULL)
+ return FALSE;
+
+ data = g_try_new0(struct set_property_data, 1);
+ if (data == NULL)
+ return FALSE;
+
+ data->function = function;
+ data->user_data = user_data;
+ data->destroy = destroy;
+
+ msg = dbus_message_new_method_call(client->service_name,
+ proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set");
+ if (msg == NULL) {
+ g_free(data);
+ return FALSE;
+ }
+
+ array_sig[0] = DBUS_TYPE_ARRAY;
+ array_sig[1] = (char) type;
+ array_sig[2] = '\0';
+
+ type_sig[0] = (char) type;
+ type_sig[1] = '\0';
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &proxy->interface);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ array_sig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ type_sig, &array);
+
+ if (dbus_type_is_fixed(type) == TRUE)
+ dbus_message_iter_append_fixed_array(&array, type,
+ &value, size);
+ else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+ int i;
+ const char **str = (const char **) value;
+
+ for (i = 0; i < size; i++)
+ dbus_message_iter_append_basic(&array, type, &str[i]);
+ }
+
+ dbus_message_iter_close_container(&variant, &array);
+ dbus_message_iter_close_container(&iter, &variant);
+
+ if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+ &call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ g_free(data);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
struct method_call_data {
GDBusReturnFunction function;
void *user_data;
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 8ada200..40a5abe 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -329,6 +329,11 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
GDBusResultFunction function, void *user_data,
GDBusDestroyFunction destroy);
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ int size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy);
+
typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data);
typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data);
--
1.8.3.1
This patch adds a function callback for write operations. When a remote
device writes to a given attribute, the core calls the specified write
callback informing the attribute server the new characteristic value.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 ++++---
src/gatt.h | 13 ++++++++++++-
3 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index fff2d3a..a02d565 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -238,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 76bbe20..45b5e26 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -46,6 +46,7 @@ struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
btd_attr_read_t read_cb;
+ btd_attr_write_t write_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -133,7 +134,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb)
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -192,8 +194,7 @@ struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
char_value->type = *uuid;
char_value->read_cb = read_cb;
-
- /* TODO: Write callbacks */
+ char_value->write_cb = write_cb;
if (local_database_add(next_handle, char_value) < 0)
/* TODO: remove declaration */
diff --git a/src/gatt.h b/src/gatt.h
index e446fa0..f4eebe5 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -42,6 +42,14 @@ typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
btd_attr_read_result_t result,
void *user_data);
+/*
+ * Callbacks from this type are called once the value from the attribute was
+ * written.
+ * @err: error in errno format.
+ * @user_data: user_data passed in btd_attr_write_t callback
+ */
+typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
+ const uint8_t *value, size_t len);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
@@ -57,10 +65,13 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
* @read_cb: Callback used to provide the characteristic value.
+ * @write_cb: Callback called to notify the implementation that a new value
+ * is available.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb);
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb);
--
1.8.3.1
This patch adds an extra callback to inform the result of the write
operation on an external entity to allow the core to reply properly
for the ATT request.
---
src/gatt-dbus.c | 4 +++-
src/gatt.h | 5 ++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 7e81dc6..d703355 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -186,7 +186,9 @@ static void proxy_write_reply(const DBusError *derr, void *user_data)
}
static void proxy_write_cb(struct btd_attribute *attr,
- const uint8_t *value, size_t len)
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data)
{
GDBusProxy *proxy;
diff --git a/src/gatt.h b/src/gatt.h
index f4eebe5..ed5fe80 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -48,8 +48,11 @@ typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
* @err: error in errno format.
* @user_data: user_data passed in btd_attr_write_t callback
*/
+typedef void (*btd_attr_write_result_t) (int err, void *user_data);
typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
- const uint8_t *value, size_t len);
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
--
1.8.3.1
This patch adds the callback for reading the external characteristic
Value. Internally, GDBusProxy implementation tracks all properties Value
changes consequently Value can be ready directly from the proxy without
additional method calls.
---
src/gatt-dbus.c | 42 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 275b263..fff2d3a 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -139,6 +139,46 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
eapp->proxies = g_slist_remove(eapp->proxies, proxy);
}
+static void proxy_read_cb(struct btd_attribute *attr,
+ btd_attr_read_result_t result, void *user_data)
+{
+ DBusMessageIter iter, array;
+ GDBusProxy *proxy;
+ uint8_t *value;
+ int len;
+
+ /*
+ * Remote device is trying to read the informed attribute,
+ * "Value" should be read from the proxy. GDBusProxy tracks
+ * properties changes automatically, it is not necessary to
+ * get the value directly from the GATT server.
+ */
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL) {
+ result(ENOENT, NULL, 0, user_data);
+ return;
+ }
+
+ if (!g_dbus_proxy_get_property(proxy, "Value", &iter)) {
+ /* Unusual situation, read property will checked earlier */
+ result(EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ DBG("External service inconsistent!");
+ result(EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ DBG("attribute: %p read %d bytes", attr, len);
+
+ result(0, value, len, user_data);
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -198,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This patch adds a function callback for read operations. When a remote
device wants to reads a given attribute, the core calls the specified
read callback to get the value from the external services.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 +++++--
src/gatt.h | 20 +++++++++++++++++++-
3 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index f60260e..275b263 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -198,7 +198,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00);
+ attr = btd_gatt_add_char(&uuid, 0x00, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 34f9eea..76bbe20 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -45,6 +45,7 @@ static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
+ btd_attr_read_t read_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -131,7 +132,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
}
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
- uint8_t properties)
+ uint8_t properties,
+ btd_attr_read_t read_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -189,8 +191,9 @@ struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
*/
char_value->type = *uuid;
+ char_value->read_cb = read_cb;
- /* TODO: Read & Write callbacks */
+ /* TODO: Write callbacks */
if (local_database_add(next_handle, char_value) < 0)
/* TODO: remove declaration */
diff --git a/src/gatt.h b/src/gatt.h
index d7acc5b..e446fa0 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -27,6 +27,22 @@ void gatt_init(void);
void gatt_cleanup(void);
+/*
+ * Callbacks of this type are called once the value from the attribute is
+ * ready to be read from the service implementation. Result callback is
+ * the asynchronous function that should be used to inform the caller
+ * the read value.
+ * @err: error in errno format.
+ * @value: pointer to value
+ * @len: length of value
+ * @user_data: user_data passed in btd_attr_read_t callback
+ */
+typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
+ void *user_data);
+typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
+ btd_attr_read_result_t result,
+ void *user_data);
+
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
*
@@ -40,9 +56,11 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* to local attribute database.
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
+ * @read_cb: Callback used to provide the characteristic value.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
- uint8_t properties);
+ uint8_t properties,
+ btd_attr_read_t read_cb);
--
1.8.3.1
From: Andre Guedes <[email protected]>
This patch adds a helper function to create attribute with static
values. It is intended to be used to create GATT services, and
characteristic declarations.
---
src/gatt.c | 48 ++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 42 insertions(+), 6 deletions(-)
diff --git a/src/gatt.c b/src/gatt.c
index f7b74d6..d3a8062 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -30,6 +30,7 @@
#include "log.h"
#include "lib/uuid.h"
#include "attrib/att.h"
+#include "src/shared/util.h"
#include "gatt-dbus.h"
#include "gatt.h"
@@ -48,6 +49,39 @@ struct btd_attribute {
static GList *local_attribute_db;
static uint16_t next_handle = 0x0001;
+static inline void put_uuid_le(const bt_uuid_t *src, void *dst)
+{
+ if (src->type == BT_UUID16)
+ put_le16(src->value.u16, dst);
+ else if (src->type == BT_UUID32)
+ put_le32(src->value.u32, dst);
+ else
+ /* Convert from 128-bit BE to LE */
+ bswap_128(&src->value.u128, dst);
+}
+
+/*
+ * Helper function to create new attributes containing constant/static values.
+ * eg: declaration of services/characteristics, and characteristics with
+ * fixed values.
+ */
+static struct btd_attribute *new_const_attribute(const bt_uuid_t *type,
+ const uint8_t *value,
+ uint16_t len)
+{
+ struct btd_attribute *attr;
+
+ attr = malloc0(sizeof(struct btd_attribute) + len);
+ if (attr == NULL)
+ return NULL;
+
+ attr->type = *type;
+ memcpy(&attr->value, value, len);
+ attr->value_len = len;
+
+ return attr;
+}
+
static int local_database_add(uint16_t handle, struct btd_attribute *attr)
{
attr->handle = handle;
@@ -59,9 +93,9 @@ static int local_database_add(uint16_t handle, struct btd_attribute *attr)
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
{
+ struct btd_attribute *attr;
uint16_t len = bt_uuid_len(uuid);
- struct btd_attribute *attr = g_malloc0(sizeof(struct btd_attribute) +
- len);
+ uint8_t value[len];
/*
* Service DECLARATION
@@ -75,13 +109,15 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
* (2) - 2 or 16 octets: Service UUID
*/
- attr->type = primary_uuid;
+ /* Set attribute value */
+ put_uuid_le(uuid, value);
- att_put_uuid(*uuid, attr->value);
- attr->value_len = len;
+ attr = new_const_attribute(&primary_uuid, value, len);
+ if (attr == NULL)
+ return NULL;
if (local_database_add(next_handle, attr) < 0) {
- g_free(attr);
+ free(attr);
return NULL;
}
--
1.8.3.1
Initial support for GATT characteristics. This patch adds the
characteristic declaration attribute to the GATT local database based
on the fetched GDBusProxy objects.
---
src/gatt-dbus.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 3fbff87..9f94c4f 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -163,6 +163,44 @@ static int register_external_service(const struct external_app *eapp,
return 0;
}
+static int register_external_characteristics(GSList *proxies)
+
+{
+ GSList *list;
+
+ for (list = proxies; list; list = g_slist_next(proxies)) {
+ DBusMessageIter iter;
+ const char *str, *path;
+ bt_uuid_t uuid;
+ GDBusProxy *proxy = list->data;
+
+ if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+ return -EINVAL;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&iter, &str);
+
+ if (bt_string_to_uuid(&uuid, str) < 0)
+ return -EINVAL;
+
+ /*
+ * TODO: Missing Flags/property
+ * Add properties according to Core SPEC 4.1 page 2183.
+ * Reference table 3.5: Characteristic Properties bit field.
+ */
+
+ if (btd_gatt_add_char(&uuid, 0x00) == NULL)
+ return -EINVAL;
+
+ path = g_dbus_proxy_get_path(proxy);
+ DBG("Added GATT CHR: %s (%s)", path, str);
+ }
+
+ return 0;
+}
+
static void client_ready(GDBusClient *client, void *user_data)
{
struct external_app *eapp = user_data;
@@ -177,6 +215,9 @@ static void client_ready(GDBusClient *client, void *user_data)
if (register_external_service(eapp, proxy) < 0)
goto fail;
+ if (register_external_characteristics(g_slist_next(eapp->proxies)) < 0)
+ goto fail;
+
DBG("Added GATT service %s", eapp->path);
reply = dbus_message_new_method_return(eapp->reg);
@@ -186,7 +227,7 @@ fail:
error("Could not register external service: %s", eapp->path);
reply = btd_error_invalid_args(eapp->reg);
- /* TODO: missing eapp cleanup */
+ /* TODO: missing eapp/database cleanup */
reply:
dbus_message_unref(eapp->reg);
--
1.8.3.1
From: Andre Guedes <[email protected]>
This patch adds btd_gatt_add_char() helper. It creates and adds the
Characteristic declaration, and Characteristic value attributes to the
local attribute database.
---
src/gatt.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/gatt.h | 12 +++++++++
2 files changed, 100 insertions(+)
diff --git a/src/gatt.c b/src/gatt.c
index d3a8062..34f9eea 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -39,6 +39,9 @@
static const bt_uuid_t primary_uuid = { .type = BT_UUID16,
.value.u16 = GATT_PRIM_SVC_UUID };
+static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
+ .value.u16 = GATT_CHARAC_UUID };
+
struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
@@ -127,6 +130,91 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
return attr;
}
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+ uint8_t properties)
+{
+ struct btd_attribute *char_decl, *char_value = NULL;
+
+ /* Attribute value length */
+ uint16_t len = 1 + 2 + bt_uuid_len(uuid);
+ uint8_t value[len];
+
+ /*
+ * Characteristic DECLARATION
+ *
+ * TYPE ATTRIBUTE VALUE
+ * +-------+---------------------------------+
+ * |0x2803 | 0xXX 0xYYYY 0xZZZZ... |
+ * | (1) | (2) (3) (4) |
+ * +------+----------------------------------+
+ * (1) - 2 octets: Characteristic declaration UUID
+ * (2) - 1 octet : Properties
+ * (3) - 2 octets: Handle of the characteristic Value
+ * (4) - 2 or 16 octets: Characteristic UUID
+ */
+
+ value[0] = properties;
+
+ /*
+ * Since we don't know yet the characteristic value attribute
+ * handle, we skip and set it later.
+ */
+
+ put_uuid_le(uuid, &value[3]);
+
+ char_decl = new_const_attribute(&chr_uuid, value, len);
+ if (char_decl == NULL)
+ goto fail;
+
+ char_value = new0(struct btd_attribute, 1);
+ if (char_value == NULL)
+ goto fail;
+
+ if (local_database_add(next_handle, char_decl) < 0)
+ goto fail;
+
+ next_handle = next_handle + 1;
+
+ /*
+ * Characteristic VALUE
+ *
+ * TYPE ATTRIBUTE VALUE
+ * +----------+---------------------------------+
+ * |0xZZZZ... | 0x... |
+ * | (1) | (2) |
+ * +----------+---------------------------------+
+ * (1) - 2 or 16 octets: Characteristic UUID
+ * (2) - N octets: Value is read dynamically from the service
+ * implementation (external entity).
+ */
+
+ char_value->type = *uuid;
+
+ /* TODO: Read & Write callbacks */
+
+ if (local_database_add(next_handle, char_value) < 0)
+ /* TODO: remove declaration */
+ goto fail;
+
+ next_handle = next_handle + 1;
+
+ /*
+ * Update characteristic value handle in characteristic declaration
+ * attribute. For local attributes, we can assume that the handle
+ * representing the characteristic value will get the next available
+ * handle. However, for remote attribute this assumption is not valid.
+ */
+ put_le16(char_value->handle, &char_decl->value[1]);
+
+ return char_value;
+
+fail:
+ free(char_decl);
+ free(char_value);
+
+ return NULL;
+}
+
void gatt_init(void)
{
DBG("Starting GATT server");
diff --git a/src/gatt.h b/src/gatt.h
index 8dd1312..d7acc5b 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -34,3 +34,15 @@ void gatt_cleanup(void);
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
+
+/*
+ * btd_gatt_add_char - Add a characteristic (declaration and value attributes)
+ * to local attribute database.
+ * @uuid: Characteristic UUID (16-bits or 128-bits).
+ * @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
+ *
+ * Returns a reference to characteristic value attribute. In case of error,
+ * NULL is returned.
+ */
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+ uint8_t properties);
--
1.8.3.1
This commits adds a hash table to map attributes into GDBusProxy,
allowing to call remote objects for reading and writing values.
---
src/gatt-dbus.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 9f94c4f..f60260e 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,12 @@ struct external_app {
unsigned int watch;
};
+/*
+ * Attribute to Proxy hash table. Used to map incoming
+ * ATT operations to its external characteristic proxy.
+ */
+static GHashTable *proxy_hash;
+
static GSList *external_apps;
static int external_app_path_cmp(gconstpointer a, gconstpointer b)
@@ -172,6 +178,7 @@ static int register_external_characteristics(GSList *proxies)
DBusMessageIter iter;
const char *str, *path;
bt_uuid_t uuid;
+ struct btd_attribute *attr;
GDBusProxy *proxy = list->data;
if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
@@ -191,11 +198,14 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- if (btd_gatt_add_char(&uuid, 0x00) == NULL)
+ attr = btd_gatt_add_char(&uuid, 0x00);
+ if (attr == NULL)
return -EINVAL;
path = g_dbus_proxy_get_path(proxy);
DBG("Added GATT CHR: %s (%s)", path, str);
+
+ g_hash_table_insert(proxy_hash, attr, g_dbus_proxy_ref(proxy));
}
return 0;
@@ -318,13 +328,22 @@ static const GDBusMethodTable methods[] = {
gboolean gatt_dbus_manager_register(void)
{
- return g_dbus_register_interface(btd_get_dbus_connection(),
- "/org/bluez", GATT_MGR_IFACE,
- methods, NULL, NULL, NULL, NULL);
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ "/org/bluez", GATT_MGR_IFACE,
+ methods, NULL, NULL, NULL, NULL) == FALSE)
+ return FALSE;
+
+ proxy_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) g_dbus_proxy_unref);
+
+ return TRUE;
}
void gatt_dbus_manager_unregister(void)
{
+ g_hash_table_destroy(proxy_hash);
+ proxy_hash = NULL;
+
g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez",
GATT_MGR_IFACE);
}
--
1.8.3.1
This patchset adds the following features:
* register external characteristics
* basic read or write external characteristics
Usage of glib is being avoided inside src/gatt.c. Remaining
put/get little-endian/big-endian helpers will be added in the
next patchset.
No matter the system, bt_uuid_t must use big-endian byte order (similar
to human-readable format). bt_string_to_uuid() & bt_uuid_to_string() are
still changing byte order, this issue will be also fixed in the next
patchsets.
TODO (missing features of this patchset and upstream code):
* Properties/flags: operations and security
* 32-bit UUIDs
* Remove/free services
* multiple services provided by the same client
* remove att_put_* att_get_* helpers
Changes from v4 to v5:
* avoid usage of uint128_t in the header util.h
Changes from v3 to v4:
* keep LE <-> CPU helpers in lib/bluetooth.h (patches removing these
helpers are being shifted to next patchset)
Changes from v2 to v3:
* return put_uuid() definition to src/gatt.c
* add put_le16(), put_le32(), put_le128() to src/shared/util.h
Changes from v1 to v2:
* move put_uuid() and put_le16() to src/shared/util.h
* check malloc0() and new0() return value
Changes from v0 to v1:
* Use LE <-> CPU helpers defined in src/shared/util.h
* remove patch "gatt: Add helper for reading characteristics" from
this set.
Alvaro Silva (3):
tools: Add Alert Level characteristic to gatt-service
tools: Add reading Value property of gatt-service
tools: Add setting Value property of gatt-service
Andre Guedes (2):
gatt: Add function to create constant attributes
gatt: Add helper for creating GATT characteristics
Claudio Takahasi (11):
shared: Add bswap_128()
gatt: Add characteristic to the database
gatt: Add hash table of GDBusProxy objects
gatt: Add read callback to btd_gatt_add_char helper
gatt: Assign read callback for external services
gatt: Add write callback to btd_gatt_add_char helper
gdbus: Add g_dbus_proxy_set_property_array
gatt: Assign write callback for external services
gatt: Add result callback for Write Request
gatt: Add Write Request handling for GDBusProxy
tools: Emit property changed when Value changes
gdbus/client.c | 85 +++++++++++++++++++++++
gdbus/gdbus.h | 5 ++
src/gatt-dbus.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++--
src/gatt.c | 140 +++++++++++++++++++++++++++++++++++--
src/gatt.h | 44 ++++++++++++
src/shared/util.h | 10 +++
tools/gatt-service.c | 156 ++++++++++++++++++++++++++++++++++++++----
7 files changed, 608 insertions(+), 22 deletions(-)
--
1.8.3.1
Adds a new helper to swap 128-bit values. It is intended to be used for
GATT 128-bit UUID handling, converting 128-bit UUID from big-endian to
little-endian (or the opposite).
No matter the system, bt_uuid_t should always store 128-bit UUID value
using big-endian format (similar to human-readable format).
---
src/shared/util.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/shared/util.h b/src/shared/util.h
index cc2dbcd..f9170bb 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -91,6 +91,16 @@ void util_debug(util_debug_func_t function, void *user_data,
void util_hexdump(const char dir, const unsigned char *buf, size_t len,
util_debug_func_t function, void *user_data);
+static inline void bswap_128(const void *src, void *dst)
+{
+ const uint8_t *s = src;
+ uint8_t *d = dst;
+ int i;
+
+ for (i = 0; i < 16; i++)
+ d[15 - i] = s[i];
+}
+
static inline void put_le16(uint16_t val, void *dst)
{
put_unaligned(cpu_to_le16(val), (uint16_t *) dst);
--
1.8.3.1
Hi Claudio,
On Fri, Mar 14, 2014, Claudio Takahasi wrote:
> Add helper to put uint128 values to the given pointer using little-endian
> representation. This helper is only a wrapper of cpu_to_le128().
> ---
> src/shared/util.h | 23 +++++++++++++++++++++++
> 1 file changed, 23 insertions(+)
I've applied patches 1-4, but had to stop with this one.
> diff --git a/src/shared/util.h b/src/shared/util.h
> index cc2dbcd..f3db8fb 100644
> --- a/src/shared/util.h
> +++ b/src/shared/util.h
> @@ -26,6 +26,8 @@
> #include <alloca.h>
> #include <byteswap.h>
>
> +#include <bluetooth/bluetooth.h>
I don't think we want shared/util.h (an LGPL header) depending on
lib/bluetooth.h (a GPL header).
> +static inline void cpu_to_le128(const uint128_t *src, uint128_t *dst)
> +{
> + int i;
> +
> + for (i = 0; i < 16; i++)
> + dst->data[15 - i] = src->data[i];
> +}
> +
> +#else
> +
> +static inline void cpu_to_le128(const uint128_t *src, uint128_t *dst)
> +{
> + *dst = *src;
> +}
What would probably work is to completely avoid the uint128_t type and
instead do something like:
static inline void cpu_to_le128(const void *src, void *dst)
{
const uint8_t *src_data = src;
uint8_t *dst_data = dst;
for (...)
...
}
That way you could still pass uint128_t pointers to the function and it
would still work (and it would also work with any other 128-bit type we
come up with).
This all said, are you sure this is the correct way of converting a UUID
from host endianness to LE? Looking at the UUID specification[1], mainly
section 12.1, it seems the various components should be converted
independently. So basically what you have is a generic 128-bit byte
order converter, but it's not necessarily usable for UUIDs (which seems
to be the only/main thing you're using it for).
Johan
[1] http://www.itu.int/ITU-T/studygroups/com17/oid/X.667-E.pdf
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding a generic callback
to allow set the characteristic Value. It doesn't check for
characteristic properties yet.
---
tools/gatt-service.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 97dc8d4..ad5399b 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -35,6 +35,8 @@
#include <dbus/dbus.h>
#include <gdbus/gdbus.h>
+#include "src/error.h"
+
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
@@ -81,9 +83,38 @@ static gboolean chr_get_value(const GDBusPropertyTable *property,
return TRUE;
}
+static void chr_set_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+ uint8_t *value;
+ int len;
+
+ printf("Set('Value', ...)\n");
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ printf("Invalid value for Set('Value'...)\n");
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ g_free(chr->value);
+ chr->value = g_memdup(value, len);
+ chr->vlen = len;
+
+ g_dbus_pending_property_success(id);
+}
+
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
- { "Value", "ay", chr_get_value, NULL, NULL },
+ { "Value", "ay", chr_get_value, chr_set_value, NULL },
{ }
};
--
1.8.3.1
This patch extends gatt-service tool, emitting PropertiesChanged for
"Value" when a new value is set. This flow represents the handling of
ATT Write Command or Write Request which is translated to a GDBusProxy
set property. PropertiesChanged will be tracked by the characteristic
GDBusProxy in order to detect "Value" changes.
---
tools/gatt-service.c | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index ad5399b..8dbecce 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -47,9 +47,11 @@
static GMainLoop *main_loop;
static GSList *services;
+static DBusConnection *connection;
struct characteristic {
char *uuid;
+ char *path;
uint8_t *value;
int vlen;
};
@@ -110,6 +112,8 @@ static void chr_set_value(const GDBusPropertyTable *property,
chr->vlen = len;
g_dbus_pending_property_success(id);
+ g_dbus_emit_property_changed(connection, chr->path,
+ GATT_CHR_IFACE, "Value");
}
static const GDBusPropertyTable chr_properties[] = {
@@ -163,6 +167,7 @@ static void chr_iface_destroy(gpointer user_data)
g_free(chr->uuid);
g_free(chr->value);
+ g_free(chr->path);
g_free(chr);
}
@@ -182,6 +187,7 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
chr->uuid = g_strdup(uuid);
chr->value = g_memdup(value, vlen);
chr->vlen = vlen;
+ chr->path = path;
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
@@ -190,18 +196,16 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
ret = -EIO;
}
- g_free(path);
-
return ret;
}
-static char *register_service(DBusConnection *conn, const char *uuid)
+static char *register_service(const char *uuid)
{
static int id = 1;
char *path;
path = g_strdup_printf("/service%d", id++);
- if (g_dbus_register_interface(conn, path, GATT_SERVICE_IFACE,
+ if (g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
NULL, NULL, service_properties,
g_strdup(uuid), g_free) == FALSE) {
printf("Couldn't register service interface\n");
@@ -212,13 +216,13 @@ static char *register_service(DBusConnection *conn, const char *uuid)
return path;
}
-static void create_services(DBusConnection *conn)
+static void create_services()
{
char *service_path;
uint8_t level = 0;
int ret;
- service_path = register_service(conn, IAS_UUID);
+ service_path = register_service(IAS_UUID);
if (service_path == NULL)
return;
@@ -226,11 +230,11 @@ static void create_services(DBusConnection *conn)
* According to the IAS SPEC, reading <<Alert level>> is not alloweed.
* "Value" is readable for testing purpose only.
*/
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ ret = register_characteristic(connection, ALERT_LEVEL_CHR_UUID, &level,
sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
- g_dbus_unregister_interface(conn, service_path,
+ g_dbus_unregister_interface(connection, service_path,
GATT_SERVICE_IFACE);
g_free(service_path);
return;
@@ -371,25 +375,24 @@ static guint setup_signalfd(void)
int main(int argc, char *argv[])
{
GDBusClient *client;
- DBusConnection *dbus_conn;
guint signal;
signal = setup_signalfd();
if (signal == 0)
return -errno;
- dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
main_loop = g_main_loop_new(NULL, FALSE);
- g_dbus_attach_object_manager(dbus_conn);
+ g_dbus_attach_object_manager(connection);
printf("gatt-service unique name: %s\n",
- dbus_bus_get_unique_name(dbus_conn));
+ dbus_bus_get_unique_name(connection));
- create_services(dbus_conn);
+ create_services();
- client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+ client = g_dbus_client_new(connection, "org.bluez", "/org/bluez");
g_dbus_client_set_connect_watch(client, connect_handler, NULL);
@@ -400,7 +403,7 @@ int main(int argc, char *argv[])
g_source_remove(signal);
g_slist_free_full(services, g_free);
- dbus_connection_unref(dbus_conn);
+ dbus_connection_unref(connection);
return 0;
}
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch registers the Alert Level characteristic object related to
Immediate Alert Service. It adds the characteristic UUID property only.
---
tools/gatt-service.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index cba0ff3..d1606ad 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -37,13 +37,30 @@
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
+#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
/* Immediate Alert Service UUID */
#define IAS_UUID "00001802-0000-1000-8000-00805f9b34fb"
+#define ALERT_LEVEL_CHR_UUID "00002a06-0000-1000-8000-00805f9b34fb"
static GMainLoop *main_loop;
static GSList *services;
+static gboolean chr_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *uuid = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable chr_properties[] = {
+ { "UUID", "s", chr_get_uuid },
+ { }
+};
+
static gboolean service_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
@@ -83,6 +100,27 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const char *service_path)
+{
+ static int id = 1;
+ char *path;
+ int ret = 0;
+
+ path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+
+ if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
+ NULL, NULL, chr_properties,
+ g_strdup(uuid), g_free) == FALSE) {
+ printf("Couldn't register characteristic interface\n");
+ ret = -EIO;
+ }
+
+ g_free(path);
+
+ return ret;
+}
+
static char *register_service(DBusConnection *conn, const char *uuid)
{
static int id = 1;
@@ -103,11 +141,24 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ int ret;
service_path = register_service(conn, IAS_UUID);
+ if (service_path == NULL)
+ return;
- services = g_slist_prepend(services, service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
+ service_path);
+ if (ret < 0) {
+ printf("Couldn't register Alert Level characteristic (IAS)\n");
+ g_dbus_unregister_interface(conn, service_path,
+ GATT_SERVICE_IFACE);
+ g_free(service_path);
+ return;
+ }
+ services = g_slist_prepend(services, service_path);
printf("Registered service: %s\n", service_path);
}
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding the callback to
allow reading the Value property. At the moment, it is a generic
callback and it doesn't consider characteristic value property
restrictions.
---
tools/gatt-service.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 53 insertions(+), 6 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index d1606ad..97dc8d4 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -46,18 +46,44 @@
static GMainLoop *main_loop;
static GSList *services;
+struct characteristic {
+ char *uuid;
+ uint8_t *value;
+ int vlen;
+};
+
static gboolean chr_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
- const char *uuid = user_data;
+ struct characteristic *chr = user_data;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid);
+
+ return TRUE;
+}
+
+static gboolean chr_get_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+
+ printf("Get(\"Value\")\n");
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &chr->value, chr->vlen);
+
+ dbus_message_iter_close_container(iter, &array);
return TRUE;
}
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
+ { "Value", "ay", chr_get_value, NULL, NULL },
{ }
};
@@ -100,18 +126,35 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static void chr_iface_destroy(gpointer user_data)
+{
+ struct characteristic *chr = user_data;
+
+ g_free(chr->uuid);
+ g_free(chr->value);
+ g_free(chr);
+}
+
static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const uint8_t *value, int vlen,
const char *service_path)
{
+ struct characteristic *chr;
static int id = 1;
char *path;
int ret = 0;
path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+ chr = g_new0(struct characteristic, 1);
+
+ chr->uuid = g_strdup(uuid);
+ chr->value = g_memdup(value, vlen);
+ chr->vlen = vlen;
+
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
- g_strdup(uuid), g_free) == FALSE) {
+ chr, chr_iface_destroy) == FALSE) {
printf("Couldn't register characteristic interface\n");
ret = -EIO;
}
@@ -141,15 +184,19 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ uint8_t level = 0;
int ret;
service_path = register_service(conn, IAS_UUID);
if (service_path == NULL)
return;
- /* Add Alert Level Characteristic to Immediate Alert Service */
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
- service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service
+ * According to the IAS SPEC, reading <<Alert level>> is not alloweed.
+ * "Value" is readable for testing purpose only.
+ */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
g_dbus_unregister_interface(conn, service_path,
--
1.8.3.1
When the characteristic requires Write Request, the ATT response should
be sent after receiving the reply for the Set method call.
---
src/gatt-dbus.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 61 insertions(+), 6 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index d703355..2412e2d 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,11 @@ struct external_app {
unsigned int watch;
};
+struct proxy_write_data {
+ btd_attr_write_result_t result_cb;
+ void *user_data;
+};
+
/*
* Attribute to Proxy hash table. Used to map incoming
* ATT operations to its external characteristic proxy.
@@ -181,8 +186,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
static void proxy_write_reply(const DBusError *derr, void *user_data)
{
- if (derr)
- DBG("Write reply: %s", derr->message);
+ struct proxy_write_data *wdata = user_data;
+ int err = 0;
+
+ /*
+ * Security requirements shall be handled by the core. If external
+ * applications returns an error, the reasons will be restricted to
+ * invalid argument or application specific errors.
+ */
+
+ if (dbus_error_is_set(derr) == FALSE)
+ goto done;
+
+ DBG("Write reply: %s", derr->message);
+
+ if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
+ err = ETIMEDOUT;
+ else if (dbus_error_has_name(derr, ERROR_INTERFACE ".InvalidArguments"))
+ err = EINVAL;
+ else
+ err = EPROTO;
+
+done:
+ if (wdata && wdata->result_cb)
+ wdata->result_cb(err, wdata->user_data);
}
static void proxy_write_cb(struct btd_attribute *attr,
@@ -193,16 +220,44 @@ static void proxy_write_cb(struct btd_attribute *attr,
GDBusProxy *proxy;
proxy = g_hash_table_lookup(proxy_hash, attr);
- if (proxy == NULL)
- /* FIXME: Attribute not found */
+ if (proxy == NULL) {
+ result(ENOENT, user_data);
return;
+ }
- g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ /*
+ * "result" callback defines if the core wants to receive the
+ * operation result, allowing to select ATT Write Request or Write
+ * Command. Descriptors requires Write Request operation. For
+ * Characteristics, the implementation will define which operations
+ * are allowed based on the properties/flags.
+ * TODO: Write Long Characteristics/Descriptors.
+ */
+
+ if (result) {
+ struct proxy_write_data *wdata;
+
+ wdata = g_new0(struct proxy_write_data, 1);
+ wdata->result_cb = result;
+ wdata->user_data = user_data;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ wdata, g_free);
+ } else {
+ /*
+ * Caller is not interested in the Set method call result.
+ * This flow implements the ATT Write Command scenario, where
+ * the remote doesn't receive ATT response.
+ */
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
value, len, proxy_write_reply,
NULL, NULL);
+ }
- DBG("Server: Write characteristic callback %s",
+ DBG("Server: Write attribute callback %s",
g_dbus_proxy_get_path(proxy));
+
}
static int register_external_service(const struct external_app *eapp,
--
1.8.3.1
This patch adds an extra callback to inform the result of the write
operation on an external entity to allow the core to reply properly
for the ATT request.
---
src/gatt-dbus.c | 4 +++-
src/gatt.h | 5 ++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 7e81dc6..d703355 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -186,7 +186,9 @@ static void proxy_write_reply(const DBusError *derr, void *user_data)
}
static void proxy_write_cb(struct btd_attribute *attr,
- const uint8_t *value, size_t len)
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data)
{
GDBusProxy *proxy;
diff --git a/src/gatt.h b/src/gatt.h
index f4eebe5..ed5fe80 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -48,8 +48,11 @@ typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
* @err: error in errno format.
* @user_data: user_data passed in btd_attr_write_t callback
*/
+typedef void (*btd_attr_write_result_t) (int err, void *user_data);
typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
- const uint8_t *value, size_t len);
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
--
1.8.3.1
This patch adds the callback for writing the external characteristic
value. Internally, GDBusProxy implementation calls Set method of the
"Value" property. At the moment, potential errors returned by the
external service implementation are being ignored.
The ATT operation (Write Command or Request) is not exposed to the
external service implementation. GATT/GDBusProxy handles possible
DBus errors returned, and notify the core using the callbacks informed.
Write Command doesn't have callback since it doesn't return ATT errors.
---
src/gatt-dbus.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index a02d565..7e81dc6 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -179,6 +179,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
result(0, value, len, user_data);
}
+static void proxy_write_reply(const DBusError *derr, void *user_data)
+{
+ if (derr)
+ DBG("Write reply: %s", derr->message);
+}
+
+static void proxy_write_cb(struct btd_attribute *attr,
+ const uint8_t *value, size_t len)
+{
+ GDBusProxy *proxy;
+
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL)
+ /* FIXME: Attribute not found */
+ return;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ NULL, NULL);
+
+ DBG("Server: Write characteristic callback %s",
+ g_dbus_proxy_get_path(proxy));
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -238,7 +262,8 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb,
+ proxy_write_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This patch adds a new gdbus utility function to allow setting a property
of fixed, and non-fixed values array.
---
gdbus/client.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdbus/gdbus.h | 5 ++++
2 files changed, 90 insertions(+)
diff --git a/gdbus/client.c b/gdbus/client.c
index 5193b6c..6fea3d9 100644
--- a/gdbus/client.c
+++ b/gdbus/client.c
@@ -727,6 +727,91 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
return TRUE;
}
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ int size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy)
+{
+ struct set_property_data *data;
+ GDBusClient *client;
+ DBusMessage *msg;
+ DBusMessageIter iter, variant, array;
+ DBusPendingCall *call;
+ char array_sig[3];
+ char type_sig[2];
+
+ if (proxy == NULL || name == NULL || value == NULL)
+ return FALSE;
+
+ if (dbus_type_is_basic(type) == FALSE)
+ return FALSE;
+
+ client = proxy->client;
+ if (client == NULL)
+ return FALSE;
+
+ data = g_try_new0(struct set_property_data, 1);
+ if (data == NULL)
+ return FALSE;
+
+ data->function = function;
+ data->user_data = user_data;
+ data->destroy = destroy;
+
+ msg = dbus_message_new_method_call(client->service_name,
+ proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set");
+ if (msg == NULL) {
+ g_free(data);
+ return FALSE;
+ }
+
+ array_sig[0] = DBUS_TYPE_ARRAY;
+ array_sig[1] = (char) type;
+ array_sig[2] = '\0';
+
+ type_sig[0] = (char) type;
+ type_sig[1] = '\0';
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &proxy->interface);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ array_sig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ type_sig, &array);
+
+ if (dbus_type_is_fixed(type) == TRUE)
+ dbus_message_iter_append_fixed_array(&array, type,
+ &value, size);
+ else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+ int i;
+ const char **str = (const char **) value;
+
+ for (i = 0; i < size; i++)
+ dbus_message_iter_append_basic(&array, type, &str[i]);
+ }
+
+ dbus_message_iter_close_container(&variant, &array);
+ dbus_message_iter_close_container(&iter, &variant);
+
+ if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+ &call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ g_free(data);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
struct method_call_data {
GDBusReturnFunction function;
void *user_data;
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 8ada200..40a5abe 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -329,6 +329,11 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
GDBusResultFunction function, void *user_data,
GDBusDestroyFunction destroy);
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ int size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy);
+
typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data);
typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data);
--
1.8.3.1
This patch adds the callback for reading the external characteristic
Value. Internally, GDBusProxy implementation tracks all properties Value
changes consequently Value can be ready directly from the proxy without
additional method calls.
---
src/gatt-dbus.c | 42 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 275b263..fff2d3a 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -139,6 +139,46 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
eapp->proxies = g_slist_remove(eapp->proxies, proxy);
}
+static void proxy_read_cb(struct btd_attribute *attr,
+ btd_attr_read_result_t result, void *user_data)
+{
+ DBusMessageIter iter, array;
+ GDBusProxy *proxy;
+ uint8_t *value;
+ int len;
+
+ /*
+ * Remote device is trying to read the informed attribute,
+ * "Value" should be read from the proxy. GDBusProxy tracks
+ * properties changes automatically, it is not necessary to
+ * get the value directly from the GATT server.
+ */
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL) {
+ result(ENOENT, NULL, 0, user_data);
+ return;
+ }
+
+ if (!g_dbus_proxy_get_property(proxy, "Value", &iter)) {
+ /* Unusual situation, read property will checked earlier */
+ result(EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ DBG("External service inconsistent!");
+ result(EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ DBG("attribute: %p read %d bytes", attr, len);
+
+ result(0, value, len, user_data);
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -198,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This patch adds a function callback for write operations. When a remote
device writes to a given attribute, the core calls the specified write
callback informing the attribute server the new characteristic value.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 ++++---
src/gatt.h | 13 ++++++++++++-
3 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index fff2d3a..a02d565 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -238,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 7d063a3..abf0c34 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -46,6 +46,7 @@ struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
btd_attr_read_t read_cb;
+ btd_attr_write_t write_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -132,7 +133,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb)
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -191,8 +193,7 @@ struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
char_value->type = *uuid;
char_value->read_cb = read_cb;
-
- /* TODO: Write callbacks */
+ char_value->write_cb = write_cb;
if (local_database_add(next_handle, char_value) < 0)
/* TODO: remove declaration */
diff --git a/src/gatt.h b/src/gatt.h
index e446fa0..f4eebe5 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -42,6 +42,14 @@ typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
btd_attr_read_result_t result,
void *user_data);
+/*
+ * Callbacks from this type are called once the value from the attribute was
+ * written.
+ * @err: error in errno format.
+ * @user_data: user_data passed in btd_attr_write_t callback
+ */
+typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
+ const uint8_t *value, size_t len);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
@@ -57,10 +65,13 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
* @read_cb: Callback used to provide the characteristic value.
+ * @write_cb: Callback called to notify the implementation that a new value
+ * is available.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb);
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb);
--
1.8.3.1
This commits adds a hash table to map attributes into GDBusProxy,
allowing to call remote objects for reading and writing values.
---
src/gatt-dbus.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 9f94c4f..f60260e 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,12 @@ struct external_app {
unsigned int watch;
};
+/*
+ * Attribute to Proxy hash table. Used to map incoming
+ * ATT operations to its external characteristic proxy.
+ */
+static GHashTable *proxy_hash;
+
static GSList *external_apps;
static int external_app_path_cmp(gconstpointer a, gconstpointer b)
@@ -172,6 +178,7 @@ static int register_external_characteristics(GSList *proxies)
DBusMessageIter iter;
const char *str, *path;
bt_uuid_t uuid;
+ struct btd_attribute *attr;
GDBusProxy *proxy = list->data;
if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
@@ -191,11 +198,14 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- if (btd_gatt_add_char(&uuid, 0x00) == NULL)
+ attr = btd_gatt_add_char(&uuid, 0x00);
+ if (attr == NULL)
return -EINVAL;
path = g_dbus_proxy_get_path(proxy);
DBG("Added GATT CHR: %s (%s)", path, str);
+
+ g_hash_table_insert(proxy_hash, attr, g_dbus_proxy_ref(proxy));
}
return 0;
@@ -318,13 +328,22 @@ static const GDBusMethodTable methods[] = {
gboolean gatt_dbus_manager_register(void)
{
- return g_dbus_register_interface(btd_get_dbus_connection(),
- "/org/bluez", GATT_MGR_IFACE,
- methods, NULL, NULL, NULL, NULL);
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ "/org/bluez", GATT_MGR_IFACE,
+ methods, NULL, NULL, NULL, NULL) == FALSE)
+ return FALSE;
+
+ proxy_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) g_dbus_proxy_unref);
+
+ return TRUE;
}
void gatt_dbus_manager_unregister(void)
{
+ g_hash_table_destroy(proxy_hash);
+ proxy_hash = NULL;
+
g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez",
GATT_MGR_IFACE);
}
--
1.8.3.1
Initial support for GATT characteristics. This patch adds the
characteristic declaration attribute to the GATT local database based
on the fetched GDBusProxy objects.
---
src/gatt-dbus.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 3fbff87..9f94c4f 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -163,6 +163,44 @@ static int register_external_service(const struct external_app *eapp,
return 0;
}
+static int register_external_characteristics(GSList *proxies)
+
+{
+ GSList *list;
+
+ for (list = proxies; list; list = g_slist_next(proxies)) {
+ DBusMessageIter iter;
+ const char *str, *path;
+ bt_uuid_t uuid;
+ GDBusProxy *proxy = list->data;
+
+ if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+ return -EINVAL;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&iter, &str);
+
+ if (bt_string_to_uuid(&uuid, str) < 0)
+ return -EINVAL;
+
+ /*
+ * TODO: Missing Flags/property
+ * Add properties according to Core SPEC 4.1 page 2183.
+ * Reference table 3.5: Characteristic Properties bit field.
+ */
+
+ if (btd_gatt_add_char(&uuid, 0x00) == NULL)
+ return -EINVAL;
+
+ path = g_dbus_proxy_get_path(proxy);
+ DBG("Added GATT CHR: %s (%s)", path, str);
+ }
+
+ return 0;
+}
+
static void client_ready(GDBusClient *client, void *user_data)
{
struct external_app *eapp = user_data;
@@ -177,6 +215,9 @@ static void client_ready(GDBusClient *client, void *user_data)
if (register_external_service(eapp, proxy) < 0)
goto fail;
+ if (register_external_characteristics(g_slist_next(eapp->proxies)) < 0)
+ goto fail;
+
DBG("Added GATT service %s", eapp->path);
reply = dbus_message_new_method_return(eapp->reg);
@@ -186,7 +227,7 @@ fail:
error("Could not register external service: %s", eapp->path);
reply = btd_error_invalid_args(eapp->reg);
- /* TODO: missing eapp cleanup */
+ /* TODO: missing eapp/database cleanup */
reply:
dbus_message_unref(eapp->reg);
--
1.8.3.1
This patch adds a function callback for read operations. When a remote
device wants to reads a given attribute, the core calls the specified
read callback to get the value from the external services.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 +++++--
src/gatt.h | 20 +++++++++++++++++++-
3 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index f60260e..275b263 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -198,7 +198,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00);
+ attr = btd_gatt_add_char(&uuid, 0x00, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 062460c..7d063a3 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -45,6 +45,7 @@ static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
+ btd_attr_read_t read_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -130,7 +131,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
}
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
- uint8_t properties)
+ uint8_t properties,
+ btd_attr_read_t read_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -188,8 +190,9 @@ struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
*/
char_value->type = *uuid;
+ char_value->read_cb = read_cb;
- /* TODO: Read & Write callbacks */
+ /* TODO: Write callbacks */
if (local_database_add(next_handle, char_value) < 0)
/* TODO: remove declaration */
diff --git a/src/gatt.h b/src/gatt.h
index d7acc5b..e446fa0 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -27,6 +27,22 @@ void gatt_init(void);
void gatt_cleanup(void);
+/*
+ * Callbacks of this type are called once the value from the attribute is
+ * ready to be read from the service implementation. Result callback is
+ * the asynchronous function that should be used to inform the caller
+ * the read value.
+ * @err: error in errno format.
+ * @value: pointer to value
+ * @len: length of value
+ * @user_data: user_data passed in btd_attr_read_t callback
+ */
+typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
+ void *user_data);
+typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
+ btd_attr_read_result_t result,
+ void *user_data);
+
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
*
@@ -40,9 +56,11 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* to local attribute database.
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
+ * @read_cb: Callback used to provide the characteristic value.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
- uint8_t properties);
+ uint8_t properties,
+ btd_attr_read_t read_cb);
--
1.8.3.1
From: Andre Guedes <[email protected]>
This patch adds btd_gatt_add_char() helper. It creates and adds the
Characteristic declaration, and Characteristic value attributes to the
local attribute database.
---
src/gatt.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/gatt.h | 12 +++++++++
2 files changed, 100 insertions(+)
diff --git a/src/gatt.c b/src/gatt.c
index 56a1db1..062460c 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -39,6 +39,9 @@
static const bt_uuid_t primary_uuid = { .type = BT_UUID16,
.value.u16 = GATT_PRIM_SVC_UUID };
+static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
+ .value.u16 = GATT_CHARAC_UUID };
+
struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
@@ -126,6 +129,91 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
return attr;
}
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+ uint8_t properties)
+{
+ struct btd_attribute *char_decl, *char_value = NULL;
+
+ /* Attribute value length */
+ uint16_t len = 1 + 2 + bt_uuid_len(uuid);
+ uint8_t value[len];
+
+ /*
+ * Characteristic DECLARATION
+ *
+ * TYPE ATTRIBUTE VALUE
+ * +-------+---------------------------------+
+ * |0x2803 | 0xXX 0xYYYY 0xZZZZ... |
+ * | (1) | (2) (3) (4) |
+ * +------+----------------------------------+
+ * (1) - 2 octets: Characteristic declaration UUID
+ * (2) - 1 octet : Properties
+ * (3) - 2 octets: Handle of the characteristic Value
+ * (4) - 2 or 16 octets: Characteristic UUID
+ */
+
+ value[0] = properties;
+
+ /*
+ * Since we don't know yet the characteristic value attribute
+ * handle, we skip and set it later.
+ */
+
+ put_uuid(uuid, &value[3]);
+
+ char_decl = new_const_attribute(&chr_uuid, value, len);
+ if (char_decl == NULL)
+ goto fail;
+
+ char_value = new0(struct btd_attribute, 1);
+ if (char_value == NULL)
+ goto fail;
+
+ if (local_database_add(next_handle, char_decl) < 0)
+ goto fail;
+
+ next_handle = next_handle + 1;
+
+ /*
+ * Characteristic VALUE
+ *
+ * TYPE ATTRIBUTE VALUE
+ * +----------+---------------------------------+
+ * |0xZZZZ... | 0x... |
+ * | (1) | (2) |
+ * +----------+---------------------------------+
+ * (1) - 2 or 16 octets: Characteristic UUID
+ * (2) - N octets: Value is read dynamically from the service
+ * implementation (external entity).
+ */
+
+ char_value->type = *uuid;
+
+ /* TODO: Read & Write callbacks */
+
+ if (local_database_add(next_handle, char_value) < 0)
+ /* TODO: remove declaration */
+ goto fail;
+
+ next_handle = next_handle + 1;
+
+ /*
+ * Update characteristic value handle in characteristic declaration
+ * attribute. For local attributes, we can assume that the handle
+ * representing the characteristic value will get the next available
+ * handle. However, for remote attribute this assumption is not valid.
+ */
+ put_le16(char_value->handle, &char_decl->value[1]);
+
+ return char_value;
+
+fail:
+ free(char_decl);
+ free(char_value);
+
+ return NULL;
+}
+
void gatt_init(void)
{
DBG("Starting GATT server");
diff --git a/src/gatt.h b/src/gatt.h
index 8dd1312..d7acc5b 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -34,3 +34,15 @@ void gatt_cleanup(void);
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
+
+/*
+ * btd_gatt_add_char - Add a characteristic (declaration and value attributes)
+ * to local attribute database.
+ * @uuid: Characteristic UUID (16-bits or 128-bits).
+ * @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
+ *
+ * Returns a reference to characteristic value attribute. In case of error,
+ * NULL is returned.
+ */
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+ uint8_t properties);
--
1.8.3.1
From: Andre Guedes <[email protected]>
This patch adds a helper function to create attribute with static
values. It is intended to be used to create GATT services, and
characteristic declarations.
---
src/gatt.c | 47 +++++++++++++++++++++++++++++++++++++++++------
1 file changed, 41 insertions(+), 6 deletions(-)
diff --git a/src/gatt.c b/src/gatt.c
index f7b74d6..56a1db1 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -30,6 +30,7 @@
#include "log.h"
#include "lib/uuid.h"
#include "attrib/att.h"
+#include "src/shared/util.h"
#include "gatt-dbus.h"
#include "gatt.h"
@@ -48,6 +49,38 @@ struct btd_attribute {
static GList *local_attribute_db;
static uint16_t next_handle = 0x0001;
+static inline void put_uuid(const bt_uuid_t *src, void *dst)
+{
+ if (src->type == BT_UUID16)
+ put_le16(src->value.u16, dst);
+ else if (src->type == BT_UUID32)
+ put_le32(src->value.u32, dst);
+ else
+ put_le128(&(src->value.u128), dst);
+}
+
+/*
+ * Helper function to create new attributes containing constant/static values.
+ * eg: declaration of services/characteristics, and characteristics with
+ * fixed values.
+ */
+static struct btd_attribute *new_const_attribute(const bt_uuid_t *type,
+ const uint8_t *value,
+ uint16_t len)
+{
+ struct btd_attribute *attr;
+
+ attr = malloc0(sizeof(struct btd_attribute) + len);
+ if (attr == NULL)
+ return NULL;
+
+ attr->type = *type;
+ memcpy(&attr->value, value, len);
+ attr->value_len = len;
+
+ return attr;
+}
+
static int local_database_add(uint16_t handle, struct btd_attribute *attr)
{
attr->handle = handle;
@@ -59,9 +92,9 @@ static int local_database_add(uint16_t handle, struct btd_attribute *attr)
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
{
+ struct btd_attribute *attr;
uint16_t len = bt_uuid_len(uuid);
- struct btd_attribute *attr = g_malloc0(sizeof(struct btd_attribute) +
- len);
+ uint8_t value[len];
/*
* Service DECLARATION
@@ -75,13 +108,15 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
* (2) - 2 or 16 octets: Service UUID
*/
- attr->type = primary_uuid;
+ /* Set attribute value */
+ put_uuid(uuid, value);
- att_put_uuid(*uuid, attr->value);
- attr->value_len = len;
+ attr = new_const_attribute(&primary_uuid, value, len);
+ if (attr == NULL)
+ return NULL;
if (local_database_add(next_handle, attr) < 0) {
- g_free(attr);
+ free(attr);
return NULL;
}
--
1.8.3.1
---
tools/l2test.c | 2 +-
tools/rctest.c | 2 +-
tools/scotest.c | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/l2test.c b/tools/l2test.c
index 8e62227..a9fc642 100644
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -978,7 +978,7 @@ static void do_send(int sk)
seq = 0;
while ((num_frames == -1) || (num_frames-- > 0)) {
- bt_put_le32(seq, buf);
+ put_le32(seq, buf);
put_le16(data_size, buf + 4);
seq++;
diff --git a/tools/rctest.c b/tools/rctest.c
index 98fe792..2c7e45b 100644
--- a/tools/rctest.c
+++ b/tools/rctest.c
@@ -569,7 +569,7 @@ static void do_send(int sk)
seq = 0;
while ((num_frames == -1) || (num_frames-- > 0)) {
- bt_put_le32(seq, buf);
+ put_le32(seq, buf);
put_le16(data_size, buf + 4);
seq++;
diff --git a/tools/scotest.c b/tools/scotest.c
index 13b9602..e5530d9 100644
--- a/tools/scotest.c
+++ b/tools/scotest.c
@@ -347,7 +347,7 @@ static void send_mode(char *svr)
seq = 0;
while (1) {
- bt_put_le32(seq, buf);
+ put_le32(seq, buf);
put_le16(data_size, buf + 4);
seq++;
--
1.8.3.1
Add helper to put uint32 values to the given pointer using
little-endian representation.
---
src/shared/util.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/shared/util.h b/src/shared/util.h
index e0a61c6..cc2dbcd 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -95,3 +95,8 @@ static inline void put_le16(uint16_t val, void *dst)
{
put_unaligned(cpu_to_le16(val), (uint16_t *) dst);
}
+
+static inline void put_le32(uint32_t val, void *dst)
+{
+ put_unaligned(cpu_to_le32(val), (uint32_t *) dst);
+}
--
1.8.3.1
---
android/hidhost.c | 3 ++-
src/eir.c | 3 ++-
tools/l2test.c | 4 +++-
tools/rctest.c | 4 +++-
tools/scotest.c | 4 +++-
5 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/android/hidhost.c b/android/hidhost.c
index 4226f69..81e7eb8 100644
--- a/android/hidhost.c
+++ b/android/hidhost.c
@@ -38,6 +38,7 @@
#include "lib/sdp.h"
#include "lib/sdp_lib.h"
#include "src/shared/mgmt.h"
+#include "src/shared/util.h"
#include "src/sdp-client.h"
#include "src/uuid-helper.h"
#include "profiles/input/uhid_copy.h"
@@ -1066,7 +1067,7 @@ static void bt_hid_get_report(const void *buf, uint16_t len)
if (cmd->buf_size > 0) {
req[0] = req[0] | HID_GET_REPORT_SIZE_FIELD;
- bt_put_le16(cmd->buf_size, &req[2]);
+ put_le16(cmd->buf_size, &req[2]);
}
fd = g_io_channel_unix_get_fd(dev->ctrl_io);
diff --git a/src/eir.c b/src/eir.c
index cc1c5dd..be83718 100644
--- a/src/eir.c
+++ b/src/eir.c
@@ -37,6 +37,7 @@
#include <bluetooth/hci.h>
#include <bluetooth/sdp.h>
+#include "src/shared/util.h"
#include "uuid-helper.h"
#include "eir.h"
@@ -473,7 +474,7 @@ int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod,
eir_total_len += eir_optional_len;
/* store total length */
- bt_put_le16(eir_total_len, data);
+ put_le16(eir_total_len, data);
return eir_total_len;
}
diff --git a/tools/l2test.c b/tools/l2test.c
index 0993f74..8e62227 100644
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -47,6 +47,8 @@
#include <bluetooth/hci_lib.h>
#include <bluetooth/l2cap.h>
+#include "src/shared/util.h"
+
#define NIBBLE_TO_ASCII(c) ((c) < 0x0a ? (c) + 0x30 : (c) + 0x57)
#define BREDR_DEFAULT_PSM 0x1011
@@ -977,7 +979,7 @@ static void do_send(int sk)
seq = 0;
while ((num_frames == -1) || (num_frames-- > 0)) {
bt_put_le32(seq, buf);
- bt_put_le16(data_size, buf + 4);
+ put_le16(data_size, buf + 4);
seq++;
diff --git a/tools/rctest.c b/tools/rctest.c
index 9281392..98fe792 100644
--- a/tools/rctest.c
+++ b/tools/rctest.c
@@ -47,6 +47,8 @@
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include "src/shared/util.h"
+
/* Test modes */
enum {
SEND,
@@ -568,7 +570,7 @@ static void do_send(int sk)
seq = 0;
while ((num_frames == -1) || (num_frames-- > 0)) {
bt_put_le32(seq, buf);
- bt_put_le16(data_size, buf + 4);
+ put_le16(data_size, buf + 4);
seq++;
diff --git a/tools/scotest.c b/tools/scotest.c
index 227287d..13b9602 100644
--- a/tools/scotest.c
+++ b/tools/scotest.c
@@ -40,6 +40,8 @@
#include <bluetooth/bluetooth.h>
#include <bluetooth/sco.h>
+#include "src/shared/util.h"
+
/* Test modes */
enum {
SEND,
@@ -346,7 +348,7 @@ static void send_mode(char *svr)
seq = 0;
while (1) {
bt_put_le32(seq, buf);
- bt_put_le16(data_size, buf + 4);
+ put_le16(data_size, buf + 4);
seq++;
--
1.8.3.1
Add helper to put uint128 values to the given pointer using little-endian
representation. This helper is only a wrapper of cpu_to_le128().
---
src/shared/util.h | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/src/shared/util.h b/src/shared/util.h
index cc2dbcd..f3db8fb 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -26,6 +26,8 @@
#include <alloca.h>
#include <byteswap.h>
+#include <bluetooth/bluetooth.h>
+
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define le16_to_cpu(val) (val)
#define le32_to_cpu(val) (val)
@@ -56,6 +58,27 @@
#error "Unknown byte order"
#endif
+#if __BYTE_ORDER == __BIG_ENDIAN
+
+static inline void cpu_to_le128(const uint128_t *src, uint128_t *dst)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ dst->data[15 - i] = src->data[i];
+}
+
+#else
+
+static inline void cpu_to_le128(const uint128_t *src, uint128_t *dst)
+{
+ *dst = *src;
+}
+
+#endif
+
+#define put_le128(val, dst) cpu_to_le128(val, dst)
+
#define get_unaligned(ptr) \
({ \
struct __attribute__((packed)) { \
--
1.8.3.1
Add helper to put uint16 values to the given pointer using little-endian
representation.
---
src/shared/util.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/shared/util.h b/src/shared/util.h
index d7d4a94..e0a61c6 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -90,3 +90,8 @@ void util_debug(util_debug_func_t function, void *user_data,
void util_hexdump(const char dir, const unsigned char *buf, size_t len,
util_debug_func_t function, void *user_data);
+
+static inline void put_le16(uint16_t val, void *dst)
+{
+ put_unaligned(cpu_to_le16(val), (uint16_t *) dst);
+}
--
1.8.3.1
This patchset adds the following features:
* register external characteristics
* basic read or write external characteristics
Usage of glib is being avoided inside src/gatt.c. Remaining
put/get little-endian/big-endian helpers will be added in the
next patchset.
TODO (missing features of this patchset and upstream code):
* Properties/flags: operations and security
* 32-bit UUIDs
* Remove/free services
* multiple services provided by the same client
* remove att_put_* att_get_* helpers
Changes from v3 to v4:
* keep LE <-> CPU helpers in lib/bluetooth.h (patches removing these
helpers are being shifted to next patchset)
Changes from v2 to v3:
* return put_uuid() definition to src/gatt.c
* add put_le16(), put_le32(), put_le128() to src/shared/util.h
Changes from v1 to v2:
* move put_uuid() and put_le16() to src/shared/util.h
* check malloc0() and new0() return value
Changes from v0 to v1:
* Use LE <-> CPU helpers defined in src/shared/util.h
* remove patch "gatt: Add helper for reading characteristics" from
this set.
How to test:
It is possible to test the implemented features using a modified
gatttool version. Unix socket is used as transport to allow basic
ATT operations:
git://git.infradead.org/users/cktakahasi/bluez.git gatt-api-forupstream-group2-patchv3-testing
$gatttool -I -L
[LOCAL][LE]> connect
Local connection
Connection successful
[LOCAL][LE]> primary
attr handle: 0x0001, end grp handle: 0x0003 uuid: 000034fb-0000-1000-8000-00805f9b34fb
[LOCAL][LE]> characteristics
handle: 0x0002, char properties: 0x00, char value handle: 0x0003, uuid: 00002a06-0000-1000-8000-00805f9b34fb
[LOCAL][LE]> char-read-hnd 0x0003
Characteristic value/descriptor: 00
[LOCAL][LE]> char-write-req 0x0003 02
Characteristic value was written successfully
[LOCAL][LE]> char-read-hnd 0x0003
Characteristic value/descriptor: 02
Alvaro Silva (3):
tools: Add Alert Level characteristic to gatt-service
tools: Add reading Value property of gatt-service
tools: Add setting Value property of gatt-service
Andre Guedes (2):
gatt: Add function to create constant attributes
gatt: Add helper for creating GATT characteristics
Claudio Takahasi (15):
shared: Add put_le16()
Replace bt_put_le16() by put_le16()
shared: Add put_le32()
Replace bt_put_le32() by put_le32()
shared: Add put_le128()
gatt: Add characteristic to the database
gatt: Add hash table of GDBusProxy objects
gatt: Add read callback to btd_gatt_add_char helper
gatt: Assign read callback for external services
gatt: Add write callback to btd_gatt_add_char helper
gdbus: Add g_dbus_proxy_set_property_array
gatt: Assign write callback for external services
gatt: Add result callback for Write Request
gatt: Add Write Request handling for GDBusProxy
tools: Emit property changed when Value changes
android/hidhost.c | 3 +-
gdbus/client.c | 85 +++++++++++++++++++++++
gdbus/gdbus.h | 5 ++
src/eir.c | 3 +-
src/gatt-dbus.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++--
src/gatt.c | 139 +++++++++++++++++++++++++++++++++++--
src/gatt.h | 44 ++++++++++++
src/shared/util.h | 33 +++++++++
tools/gatt-service.c | 156 ++++++++++++++++++++++++++++++++++++++----
tools/l2test.c | 6 +-
tools/rctest.c | 6 +-
tools/scotest.c | 6 +-
12 files changed, 646 insertions(+), 30 deletions(-)
--
1.8.3.1
Hi Claudio,
On Thu, Mar 13, 2014, Claudio Takahasi wrote:
> ---
> android/hidhost.c | 3 ++-
> lib/bluetooth.h | 10 ----------
> src/eir.c | 3 ++-
> tools/l2test.c | 4 +++-
> tools/rctest.c | 4 +++-
> tools/scotest.c | 4 +++-
> 6 files changed, 13 insertions(+), 15 deletions(-)
This would have to be split into two patches: one to convert all users
and another for removing from lib. This is particularly important since
I'm not sure Marcel is ok with removing symbols from the library (even
though it now days defaults to not being installed by default), i.e. if
it's not ok you wouldn't have to resend but I could just skip the
removal patches.
Same goes for any other patches you might have that mix lib removals
with other changes.
Johan
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding a generic callback
to allow set the characteristic Value. It doesn't check for
characteristic properties yet.
---
tools/gatt-service.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 97dc8d4..ad5399b 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -35,6 +35,8 @@
#include <dbus/dbus.h>
#include <gdbus/gdbus.h>
+#include "src/error.h"
+
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
@@ -81,9 +83,38 @@ static gboolean chr_get_value(const GDBusPropertyTable *property,
return TRUE;
}
+static void chr_set_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+ uint8_t *value;
+ int len;
+
+ printf("Set('Value', ...)\n");
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ printf("Invalid value for Set('Value'...)\n");
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ g_free(chr->value);
+ chr->value = g_memdup(value, len);
+ chr->vlen = len;
+
+ g_dbus_pending_property_success(id);
+}
+
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
- { "Value", "ay", chr_get_value, NULL, NULL },
+ { "Value", "ay", chr_get_value, chr_set_value, NULL },
{ }
};
--
1.8.3.1
This patch extends gatt-service tool, emitting PropertiesChanged for
"Value" when a new value is set. This flow represents the handling of
ATT Write Command or Write Request which is translated to a GDBusProxy
set property. PropertiesChanged will be tracked by the characteristic
GDBusProxy in order to detect "Value" changes.
---
tools/gatt-service.c | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index ad5399b..8dbecce 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -47,9 +47,11 @@
static GMainLoop *main_loop;
static GSList *services;
+static DBusConnection *connection;
struct characteristic {
char *uuid;
+ char *path;
uint8_t *value;
int vlen;
};
@@ -110,6 +112,8 @@ static void chr_set_value(const GDBusPropertyTable *property,
chr->vlen = len;
g_dbus_pending_property_success(id);
+ g_dbus_emit_property_changed(connection, chr->path,
+ GATT_CHR_IFACE, "Value");
}
static const GDBusPropertyTable chr_properties[] = {
@@ -163,6 +167,7 @@ static void chr_iface_destroy(gpointer user_data)
g_free(chr->uuid);
g_free(chr->value);
+ g_free(chr->path);
g_free(chr);
}
@@ -182,6 +187,7 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
chr->uuid = g_strdup(uuid);
chr->value = g_memdup(value, vlen);
chr->vlen = vlen;
+ chr->path = path;
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
@@ -190,18 +196,16 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
ret = -EIO;
}
- g_free(path);
-
return ret;
}
-static char *register_service(DBusConnection *conn, const char *uuid)
+static char *register_service(const char *uuid)
{
static int id = 1;
char *path;
path = g_strdup_printf("/service%d", id++);
- if (g_dbus_register_interface(conn, path, GATT_SERVICE_IFACE,
+ if (g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
NULL, NULL, service_properties,
g_strdup(uuid), g_free) == FALSE) {
printf("Couldn't register service interface\n");
@@ -212,13 +216,13 @@ static char *register_service(DBusConnection *conn, const char *uuid)
return path;
}
-static void create_services(DBusConnection *conn)
+static void create_services()
{
char *service_path;
uint8_t level = 0;
int ret;
- service_path = register_service(conn, IAS_UUID);
+ service_path = register_service(IAS_UUID);
if (service_path == NULL)
return;
@@ -226,11 +230,11 @@ static void create_services(DBusConnection *conn)
* According to the IAS SPEC, reading <<Alert level>> is not alloweed.
* "Value" is readable for testing purpose only.
*/
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ ret = register_characteristic(connection, ALERT_LEVEL_CHR_UUID, &level,
sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
- g_dbus_unregister_interface(conn, service_path,
+ g_dbus_unregister_interface(connection, service_path,
GATT_SERVICE_IFACE);
g_free(service_path);
return;
@@ -371,25 +375,24 @@ static guint setup_signalfd(void)
int main(int argc, char *argv[])
{
GDBusClient *client;
- DBusConnection *dbus_conn;
guint signal;
signal = setup_signalfd();
if (signal == 0)
return -errno;
- dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
main_loop = g_main_loop_new(NULL, FALSE);
- g_dbus_attach_object_manager(dbus_conn);
+ g_dbus_attach_object_manager(connection);
printf("gatt-service unique name: %s\n",
- dbus_bus_get_unique_name(dbus_conn));
+ dbus_bus_get_unique_name(connection));
- create_services(dbus_conn);
+ create_services();
- client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+ client = g_dbus_client_new(connection, "org.bluez", "/org/bluez");
g_dbus_client_set_connect_watch(client, connect_handler, NULL);
@@ -400,7 +403,7 @@ int main(int argc, char *argv[])
g_source_remove(signal);
g_slist_free_full(services, g_free);
- dbus_connection_unref(dbus_conn);
+ dbus_connection_unref(connection);
return 0;
}
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding the callback to
allow reading the Value property. At the moment, it is a generic
callback and it doesn't consider characteristic value property
restrictions.
---
tools/gatt-service.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 53 insertions(+), 6 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index d1606ad..97dc8d4 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -46,18 +46,44 @@
static GMainLoop *main_loop;
static GSList *services;
+struct characteristic {
+ char *uuid;
+ uint8_t *value;
+ int vlen;
+};
+
static gboolean chr_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
- const char *uuid = user_data;
+ struct characteristic *chr = user_data;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid);
+
+ return TRUE;
+}
+
+static gboolean chr_get_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+
+ printf("Get(\"Value\")\n");
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &chr->value, chr->vlen);
+
+ dbus_message_iter_close_container(iter, &array);
return TRUE;
}
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
+ { "Value", "ay", chr_get_value, NULL, NULL },
{ }
};
@@ -100,18 +126,35 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static void chr_iface_destroy(gpointer user_data)
+{
+ struct characteristic *chr = user_data;
+
+ g_free(chr->uuid);
+ g_free(chr->value);
+ g_free(chr);
+}
+
static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const uint8_t *value, int vlen,
const char *service_path)
{
+ struct characteristic *chr;
static int id = 1;
char *path;
int ret = 0;
path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+ chr = g_new0(struct characteristic, 1);
+
+ chr->uuid = g_strdup(uuid);
+ chr->value = g_memdup(value, vlen);
+ chr->vlen = vlen;
+
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
- g_strdup(uuid), g_free) == FALSE) {
+ chr, chr_iface_destroy) == FALSE) {
printf("Couldn't register characteristic interface\n");
ret = -EIO;
}
@@ -141,15 +184,19 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ uint8_t level = 0;
int ret;
service_path = register_service(conn, IAS_UUID);
if (service_path == NULL)
return;
- /* Add Alert Level Characteristic to Immediate Alert Service */
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
- service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service
+ * According to the IAS SPEC, reading <<Alert level>> is not alloweed.
+ * "Value" is readable for testing purpose only.
+ */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
g_dbus_unregister_interface(conn, service_path,
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch registers the Alert Level characteristic object related to
Immediate Alert Service. It adds the characteristic UUID property only.
---
tools/gatt-service.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index cba0ff3..d1606ad 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -37,13 +37,30 @@
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
+#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
/* Immediate Alert Service UUID */
#define IAS_UUID "00001802-0000-1000-8000-00805f9b34fb"
+#define ALERT_LEVEL_CHR_UUID "00002a06-0000-1000-8000-00805f9b34fb"
static GMainLoop *main_loop;
static GSList *services;
+static gboolean chr_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *uuid = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable chr_properties[] = {
+ { "UUID", "s", chr_get_uuid },
+ { }
+};
+
static gboolean service_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
@@ -83,6 +100,27 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const char *service_path)
+{
+ static int id = 1;
+ char *path;
+ int ret = 0;
+
+ path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+
+ if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
+ NULL, NULL, chr_properties,
+ g_strdup(uuid), g_free) == FALSE) {
+ printf("Couldn't register characteristic interface\n");
+ ret = -EIO;
+ }
+
+ g_free(path);
+
+ return ret;
+}
+
static char *register_service(DBusConnection *conn, const char *uuid)
{
static int id = 1;
@@ -103,11 +141,24 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ int ret;
service_path = register_service(conn, IAS_UUID);
+ if (service_path == NULL)
+ return;
- services = g_slist_prepend(services, service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
+ service_path);
+ if (ret < 0) {
+ printf("Couldn't register Alert Level characteristic (IAS)\n");
+ g_dbus_unregister_interface(conn, service_path,
+ GATT_SERVICE_IFACE);
+ g_free(service_path);
+ return;
+ }
+ services = g_slist_prepend(services, service_path);
printf("Registered service: %s\n", service_path);
}
--
1.8.3.1
When the characteristic requires Write Request, the ATT response should
be sent after receiving the reply for the Set method call.
---
src/gatt-dbus.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 61 insertions(+), 6 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index d703355..2412e2d 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,11 @@ struct external_app {
unsigned int watch;
};
+struct proxy_write_data {
+ btd_attr_write_result_t result_cb;
+ void *user_data;
+};
+
/*
* Attribute to Proxy hash table. Used to map incoming
* ATT operations to its external characteristic proxy.
@@ -181,8 +186,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
static void proxy_write_reply(const DBusError *derr, void *user_data)
{
- if (derr)
- DBG("Write reply: %s", derr->message);
+ struct proxy_write_data *wdata = user_data;
+ int err = 0;
+
+ /*
+ * Security requirements shall be handled by the core. If external
+ * applications returns an error, the reasons will be restricted to
+ * invalid argument or application specific errors.
+ */
+
+ if (dbus_error_is_set(derr) == FALSE)
+ goto done;
+
+ DBG("Write reply: %s", derr->message);
+
+ if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
+ err = ETIMEDOUT;
+ else if (dbus_error_has_name(derr, ERROR_INTERFACE ".InvalidArguments"))
+ err = EINVAL;
+ else
+ err = EPROTO;
+
+done:
+ if (wdata && wdata->result_cb)
+ wdata->result_cb(err, wdata->user_data);
}
static void proxy_write_cb(struct btd_attribute *attr,
@@ -193,16 +220,44 @@ static void proxy_write_cb(struct btd_attribute *attr,
GDBusProxy *proxy;
proxy = g_hash_table_lookup(proxy_hash, attr);
- if (proxy == NULL)
- /* FIXME: Attribute not found */
+ if (proxy == NULL) {
+ result(ENOENT, user_data);
return;
+ }
- g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ /*
+ * "result" callback defines if the core wants to receive the
+ * operation result, allowing to select ATT Write Request or Write
+ * Command. Descriptors requires Write Request operation. For
+ * Characteristics, the implementation will define which operations
+ * are allowed based on the properties/flags.
+ * TODO: Write Long Characteristics/Descriptors.
+ */
+
+ if (result) {
+ struct proxy_write_data *wdata;
+
+ wdata = g_new0(struct proxy_write_data, 1);
+ wdata->result_cb = result;
+ wdata->user_data = user_data;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ wdata, g_free);
+ } else {
+ /*
+ * Caller is not interested in the Set method call result.
+ * This flow implements the ATT Write Command scenario, where
+ * the remote doesn't receive ATT response.
+ */
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
value, len, proxy_write_reply,
NULL, NULL);
+ }
- DBG("Server: Write characteristic callback %s",
+ DBG("Server: Write attribute callback %s",
g_dbus_proxy_get_path(proxy));
+
}
static int register_external_service(const struct external_app *eapp,
--
1.8.3.1
This patch adds an extra callback to inform the result of the write
operation on an external entity to allow the core to reply properly
for the ATT request.
---
src/gatt-dbus.c | 4 +++-
src/gatt.h | 5 ++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 7e81dc6..d703355 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -186,7 +186,9 @@ static void proxy_write_reply(const DBusError *derr, void *user_data)
}
static void proxy_write_cb(struct btd_attribute *attr,
- const uint8_t *value, size_t len)
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data)
{
GDBusProxy *proxy;
diff --git a/src/gatt.h b/src/gatt.h
index f4eebe5..ed5fe80 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -48,8 +48,11 @@ typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
* @err: error in errno format.
* @user_data: user_data passed in btd_attr_write_t callback
*/
+typedef void (*btd_attr_write_result_t) (int err, void *user_data);
typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
- const uint8_t *value, size_t len);
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
--
1.8.3.1
This patch adds the callback for writing the external characteristic
value. Internally, GDBusProxy implementation calls Set method of the
"Value" property. At the moment, potential errors returned by the
external service implementation are being ignored.
The ATT operation (Write Command or Request) is not exposed to the
external service implementation. GATT/GDBusProxy handles possible
DBus errors returned, and notify the core using the callbacks informed.
Write Command doesn't have callback since it doesn't return ATT errors.
---
src/gatt-dbus.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index a02d565..7e81dc6 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -179,6 +179,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
result(0, value, len, user_data);
}
+static void proxy_write_reply(const DBusError *derr, void *user_data)
+{
+ if (derr)
+ DBG("Write reply: %s", derr->message);
+}
+
+static void proxy_write_cb(struct btd_attribute *attr,
+ const uint8_t *value, size_t len)
+{
+ GDBusProxy *proxy;
+
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL)
+ /* FIXME: Attribute not found */
+ return;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ NULL, NULL);
+
+ DBG("Server: Write characteristic callback %s",
+ g_dbus_proxy_get_path(proxy));
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -238,7 +262,8 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb,
+ proxy_write_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This patch adds a new gdbus utility function to allow setting a property
of fixed, and non-fixed values array.
---
gdbus/client.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdbus/gdbus.h | 5 ++++
2 files changed, 90 insertions(+)
diff --git a/gdbus/client.c b/gdbus/client.c
index 5193b6c..6fea3d9 100644
--- a/gdbus/client.c
+++ b/gdbus/client.c
@@ -727,6 +727,91 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
return TRUE;
}
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ int size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy)
+{
+ struct set_property_data *data;
+ GDBusClient *client;
+ DBusMessage *msg;
+ DBusMessageIter iter, variant, array;
+ DBusPendingCall *call;
+ char array_sig[3];
+ char type_sig[2];
+
+ if (proxy == NULL || name == NULL || value == NULL)
+ return FALSE;
+
+ if (dbus_type_is_basic(type) == FALSE)
+ return FALSE;
+
+ client = proxy->client;
+ if (client == NULL)
+ return FALSE;
+
+ data = g_try_new0(struct set_property_data, 1);
+ if (data == NULL)
+ return FALSE;
+
+ data->function = function;
+ data->user_data = user_data;
+ data->destroy = destroy;
+
+ msg = dbus_message_new_method_call(client->service_name,
+ proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set");
+ if (msg == NULL) {
+ g_free(data);
+ return FALSE;
+ }
+
+ array_sig[0] = DBUS_TYPE_ARRAY;
+ array_sig[1] = (char) type;
+ array_sig[2] = '\0';
+
+ type_sig[0] = (char) type;
+ type_sig[1] = '\0';
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &proxy->interface);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ array_sig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ type_sig, &array);
+
+ if (dbus_type_is_fixed(type) == TRUE)
+ dbus_message_iter_append_fixed_array(&array, type,
+ &value, size);
+ else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+ int i;
+ const char **str = (const char **) value;
+
+ for (i = 0; i < size; i++)
+ dbus_message_iter_append_basic(&array, type, &str[i]);
+ }
+
+ dbus_message_iter_close_container(&variant, &array);
+ dbus_message_iter_close_container(&iter, &variant);
+
+ if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+ &call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ g_free(data);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
struct method_call_data {
GDBusReturnFunction function;
void *user_data;
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 8ada200..40a5abe 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -329,6 +329,11 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
GDBusResultFunction function, void *user_data,
GDBusDestroyFunction destroy);
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ int size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy);
+
typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data);
typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data);
--
1.8.3.1
This patch adds a function callback for write operations. When a remote
device writes to a given attribute, the core calls the specified write
callback informing the attribute server the new characteristic value.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 ++++---
src/gatt.h | 13 ++++++++++++-
3 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index fff2d3a..a02d565 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -238,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 7d063a3..abf0c34 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -46,6 +46,7 @@ struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
btd_attr_read_t read_cb;
+ btd_attr_write_t write_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -132,7 +133,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb)
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -191,8 +193,7 @@ struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
char_value->type = *uuid;
char_value->read_cb = read_cb;
-
- /* TODO: Write callbacks */
+ char_value->write_cb = write_cb;
if (local_database_add(next_handle, char_value) < 0)
/* TODO: remove declaration */
diff --git a/src/gatt.h b/src/gatt.h
index e446fa0..f4eebe5 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -42,6 +42,14 @@ typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
btd_attr_read_result_t result,
void *user_data);
+/*
+ * Callbacks from this type are called once the value from the attribute was
+ * written.
+ * @err: error in errno format.
+ * @user_data: user_data passed in btd_attr_write_t callback
+ */
+typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
+ const uint8_t *value, size_t len);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
@@ -57,10 +65,13 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
* @read_cb: Callback used to provide the characteristic value.
+ * @write_cb: Callback called to notify the implementation that a new value
+ * is available.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb);
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb);
--
1.8.3.1
This patch adds the callback for reading the external characteristic
Value. Internally, GDBusProxy implementation tracks all properties Value
changes consequently Value can be ready directly from the proxy without
additional method calls.
---
src/gatt-dbus.c | 42 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 275b263..fff2d3a 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -139,6 +139,46 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
eapp->proxies = g_slist_remove(eapp->proxies, proxy);
}
+static void proxy_read_cb(struct btd_attribute *attr,
+ btd_attr_read_result_t result, void *user_data)
+{
+ DBusMessageIter iter, array;
+ GDBusProxy *proxy;
+ uint8_t *value;
+ int len;
+
+ /*
+ * Remote device is trying to read the informed attribute,
+ * "Value" should be read from the proxy. GDBusProxy tracks
+ * properties changes automatically, it is not necessary to
+ * get the value directly from the GATT server.
+ */
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL) {
+ result(ENOENT, NULL, 0, user_data);
+ return;
+ }
+
+ if (!g_dbus_proxy_get_property(proxy, "Value", &iter)) {
+ /* Unusual situation, read property will checked earlier */
+ result(EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ DBG("External service inconsistent!");
+ result(EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ DBG("attribute: %p read %d bytes", attr, len);
+
+ result(0, value, len, user_data);
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -198,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This patch adds a function callback for read operations. When a remote
device wants to reads a given attribute, the core calls the specified
read callback to get the value from the external services.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 +++++--
src/gatt.h | 20 +++++++++++++++++++-
3 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index f60260e..275b263 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -198,7 +198,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00);
+ attr = btd_gatt_add_char(&uuid, 0x00, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 062460c..7d063a3 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -45,6 +45,7 @@ static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
+ btd_attr_read_t read_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -130,7 +131,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
}
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
- uint8_t properties)
+ uint8_t properties,
+ btd_attr_read_t read_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -188,8 +190,9 @@ struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
*/
char_value->type = *uuid;
+ char_value->read_cb = read_cb;
- /* TODO: Read & Write callbacks */
+ /* TODO: Write callbacks */
if (local_database_add(next_handle, char_value) < 0)
/* TODO: remove declaration */
diff --git a/src/gatt.h b/src/gatt.h
index d7acc5b..e446fa0 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -27,6 +27,22 @@ void gatt_init(void);
void gatt_cleanup(void);
+/*
+ * Callbacks of this type are called once the value from the attribute is
+ * ready to be read from the service implementation. Result callback is
+ * the asynchronous function that should be used to inform the caller
+ * the read value.
+ * @err: error in errno format.
+ * @value: pointer to value
+ * @len: length of value
+ * @user_data: user_data passed in btd_attr_read_t callback
+ */
+typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
+ void *user_data);
+typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
+ btd_attr_read_result_t result,
+ void *user_data);
+
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
*
@@ -40,9 +56,11 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* to local attribute database.
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
+ * @read_cb: Callback used to provide the characteristic value.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
- uint8_t properties);
+ uint8_t properties,
+ btd_attr_read_t read_cb);
--
1.8.3.1
This commits adds a hash table to map attributes into GDBusProxy,
allowing to call remote objects for reading and writing values.
---
src/gatt-dbus.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 9f94c4f..f60260e 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,12 @@ struct external_app {
unsigned int watch;
};
+/*
+ * Attribute to Proxy hash table. Used to map incoming
+ * ATT operations to its external characteristic proxy.
+ */
+static GHashTable *proxy_hash;
+
static GSList *external_apps;
static int external_app_path_cmp(gconstpointer a, gconstpointer b)
@@ -172,6 +178,7 @@ static int register_external_characteristics(GSList *proxies)
DBusMessageIter iter;
const char *str, *path;
bt_uuid_t uuid;
+ struct btd_attribute *attr;
GDBusProxy *proxy = list->data;
if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
@@ -191,11 +198,14 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- if (btd_gatt_add_char(&uuid, 0x00) == NULL)
+ attr = btd_gatt_add_char(&uuid, 0x00);
+ if (attr == NULL)
return -EINVAL;
path = g_dbus_proxy_get_path(proxy);
DBG("Added GATT CHR: %s (%s)", path, str);
+
+ g_hash_table_insert(proxy_hash, attr, g_dbus_proxy_ref(proxy));
}
return 0;
@@ -318,13 +328,22 @@ static const GDBusMethodTable methods[] = {
gboolean gatt_dbus_manager_register(void)
{
- return g_dbus_register_interface(btd_get_dbus_connection(),
- "/org/bluez", GATT_MGR_IFACE,
- methods, NULL, NULL, NULL, NULL);
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ "/org/bluez", GATT_MGR_IFACE,
+ methods, NULL, NULL, NULL, NULL) == FALSE)
+ return FALSE;
+
+ proxy_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) g_dbus_proxy_unref);
+
+ return TRUE;
}
void gatt_dbus_manager_unregister(void)
{
+ g_hash_table_destroy(proxy_hash);
+ proxy_hash = NULL;
+
g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez",
GATT_MGR_IFACE);
}
--
1.8.3.1
From: Andre Guedes <[email protected]>
This patch adds a helper function to create attribute with static
values. It is intended to be used to create GATT services, and
characteristic declarations.
---
src/gatt.c | 47 +++++++++++++++++++++++++++++++++++++++++------
1 file changed, 41 insertions(+), 6 deletions(-)
diff --git a/src/gatt.c b/src/gatt.c
index f7b74d6..56a1db1 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -30,6 +30,7 @@
#include "log.h"
#include "lib/uuid.h"
#include "attrib/att.h"
+#include "src/shared/util.h"
#include "gatt-dbus.h"
#include "gatt.h"
@@ -48,6 +49,38 @@ struct btd_attribute {
static GList *local_attribute_db;
static uint16_t next_handle = 0x0001;
+static inline void put_uuid(const bt_uuid_t *src, void *dst)
+{
+ if (src->type == BT_UUID16)
+ put_le16(src->value.u16, dst);
+ else if (src->type == BT_UUID32)
+ put_le32(src->value.u32, dst);
+ else
+ put_le128(&(src->value.u128), dst);
+}
+
+/*
+ * Helper function to create new attributes containing constant/static values.
+ * eg: declaration of services/characteristics, and characteristics with
+ * fixed values.
+ */
+static struct btd_attribute *new_const_attribute(const bt_uuid_t *type,
+ const uint8_t *value,
+ uint16_t len)
+{
+ struct btd_attribute *attr;
+
+ attr = malloc0(sizeof(struct btd_attribute) + len);
+ if (attr == NULL)
+ return NULL;
+
+ attr->type = *type;
+ memcpy(&attr->value, value, len);
+ attr->value_len = len;
+
+ return attr;
+}
+
static int local_database_add(uint16_t handle, struct btd_attribute *attr)
{
attr->handle = handle;
@@ -59,9 +92,9 @@ static int local_database_add(uint16_t handle, struct btd_attribute *attr)
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
{
+ struct btd_attribute *attr;
uint16_t len = bt_uuid_len(uuid);
- struct btd_attribute *attr = g_malloc0(sizeof(struct btd_attribute) +
- len);
+ uint8_t value[len];
/*
* Service DECLARATION
@@ -75,13 +108,15 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
* (2) - 2 or 16 octets: Service UUID
*/
- attr->type = primary_uuid;
+ /* Set attribute value */
+ put_uuid(uuid, value);
- att_put_uuid(*uuid, attr->value);
- attr->value_len = len;
+ attr = new_const_attribute(&primary_uuid, value, len);
+ if (attr == NULL)
+ return NULL;
if (local_database_add(next_handle, attr) < 0) {
- g_free(attr);
+ free(attr);
return NULL;
}
--
1.8.3.1
Initial support for GATT characteristics. This patch adds the
characteristic declaration attribute to the GATT local database based
on the fetched GDBusProxy objects.
---
src/gatt-dbus.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 3fbff87..9f94c4f 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -163,6 +163,44 @@ static int register_external_service(const struct external_app *eapp,
return 0;
}
+static int register_external_characteristics(GSList *proxies)
+
+{
+ GSList *list;
+
+ for (list = proxies; list; list = g_slist_next(proxies)) {
+ DBusMessageIter iter;
+ const char *str, *path;
+ bt_uuid_t uuid;
+ GDBusProxy *proxy = list->data;
+
+ if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+ return -EINVAL;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&iter, &str);
+
+ if (bt_string_to_uuid(&uuid, str) < 0)
+ return -EINVAL;
+
+ /*
+ * TODO: Missing Flags/property
+ * Add properties according to Core SPEC 4.1 page 2183.
+ * Reference table 3.5: Characteristic Properties bit field.
+ */
+
+ if (btd_gatt_add_char(&uuid, 0x00) == NULL)
+ return -EINVAL;
+
+ path = g_dbus_proxy_get_path(proxy);
+ DBG("Added GATT CHR: %s (%s)", path, str);
+ }
+
+ return 0;
+}
+
static void client_ready(GDBusClient *client, void *user_data)
{
struct external_app *eapp = user_data;
@@ -177,6 +215,9 @@ static void client_ready(GDBusClient *client, void *user_data)
if (register_external_service(eapp, proxy) < 0)
goto fail;
+ if (register_external_characteristics(g_slist_next(eapp->proxies)) < 0)
+ goto fail;
+
DBG("Added GATT service %s", eapp->path);
reply = dbus_message_new_method_return(eapp->reg);
@@ -186,7 +227,7 @@ fail:
error("Could not register external service: %s", eapp->path);
reply = btd_error_invalid_args(eapp->reg);
- /* TODO: missing eapp cleanup */
+ /* TODO: missing eapp/database cleanup */
reply:
dbus_message_unref(eapp->reg);
--
1.8.3.1
From: Andre Guedes <[email protected]>
This patch adds btd_gatt_add_char() helper. It creates and adds the
Characteristic declaration, and Characteristic value attributes to the
local attribute database.
---
src/gatt.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/gatt.h | 12 +++++++++
2 files changed, 100 insertions(+)
diff --git a/src/gatt.c b/src/gatt.c
index 56a1db1..062460c 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -39,6 +39,9 @@
static const bt_uuid_t primary_uuid = { .type = BT_UUID16,
.value.u16 = GATT_PRIM_SVC_UUID };
+static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
+ .value.u16 = GATT_CHARAC_UUID };
+
struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
@@ -126,6 +129,91 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
return attr;
}
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+ uint8_t properties)
+{
+ struct btd_attribute *char_decl, *char_value = NULL;
+
+ /* Attribute value length */
+ uint16_t len = 1 + 2 + bt_uuid_len(uuid);
+ uint8_t value[len];
+
+ /*
+ * Characteristic DECLARATION
+ *
+ * TYPE ATTRIBUTE VALUE
+ * +-------+---------------------------------+
+ * |0x2803 | 0xXX 0xYYYY 0xZZZZ... |
+ * | (1) | (2) (3) (4) |
+ * +------+----------------------------------+
+ * (1) - 2 octets: Characteristic declaration UUID
+ * (2) - 1 octet : Properties
+ * (3) - 2 octets: Handle of the characteristic Value
+ * (4) - 2 or 16 octets: Characteristic UUID
+ */
+
+ value[0] = properties;
+
+ /*
+ * Since we don't know yet the characteristic value attribute
+ * handle, we skip and set it later.
+ */
+
+ put_uuid(uuid, &value[3]);
+
+ char_decl = new_const_attribute(&chr_uuid, value, len);
+ if (char_decl == NULL)
+ goto fail;
+
+ char_value = new0(struct btd_attribute, 1);
+ if (char_value == NULL)
+ goto fail;
+
+ if (local_database_add(next_handle, char_decl) < 0)
+ goto fail;
+
+ next_handle = next_handle + 1;
+
+ /*
+ * Characteristic VALUE
+ *
+ * TYPE ATTRIBUTE VALUE
+ * +----------+---------------------------------+
+ * |0xZZZZ... | 0x... |
+ * | (1) | (2) |
+ * +----------+---------------------------------+
+ * (1) - 2 or 16 octets: Characteristic UUID
+ * (2) - N octets: Value is read dynamically from the service
+ * implementation (external entity).
+ */
+
+ char_value->type = *uuid;
+
+ /* TODO: Read & Write callbacks */
+
+ if (local_database_add(next_handle, char_value) < 0)
+ /* TODO: remove declaration */
+ goto fail;
+
+ next_handle = next_handle + 1;
+
+ /*
+ * Update characteristic value handle in characteristic declaration
+ * attribute. For local attributes, we can assume that the handle
+ * representing the characteristic value will get the next available
+ * handle. However, for remote attribute this assumption is not valid.
+ */
+ put_le16(char_value->handle, &char_decl->value[1]);
+
+ return char_value;
+
+fail:
+ free(char_decl);
+ free(char_value);
+
+ return NULL;
+}
+
void gatt_init(void)
{
DBG("Starting GATT server");
diff --git a/src/gatt.h b/src/gatt.h
index 8dd1312..d7acc5b 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -34,3 +34,15 @@ void gatt_cleanup(void);
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
+
+/*
+ * btd_gatt_add_char - Add a characteristic (declaration and value attributes)
+ * to local attribute database.
+ * @uuid: Characteristic UUID (16-bits or 128-bits).
+ * @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
+ *
+ * Returns a reference to characteristic value attribute. In case of error,
+ * NULL is returned.
+ */
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+ uint8_t properties);
--
1.8.3.1
Add helper to put uint128 values to the given pointer using little-endian
representation. This helper is only a wrapper of cpu_to_le128().
---
src/shared/util.h | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/src/shared/util.h b/src/shared/util.h
index cc2dbcd..f3db8fb 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -26,6 +26,8 @@
#include <alloca.h>
#include <byteswap.h>
+#include <bluetooth/bluetooth.h>
+
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define le16_to_cpu(val) (val)
#define le32_to_cpu(val) (val)
@@ -56,6 +58,27 @@
#error "Unknown byte order"
#endif
+#if __BYTE_ORDER == __BIG_ENDIAN
+
+static inline void cpu_to_le128(const uint128_t *src, uint128_t *dst)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ dst->data[15 - i] = src->data[i];
+}
+
+#else
+
+static inline void cpu_to_le128(const uint128_t *src, uint128_t *dst)
+{
+ *dst = *src;
+}
+
+#endif
+
+#define put_le128(val, dst) cpu_to_le128(val, dst)
+
#define get_unaligned(ptr) \
({ \
struct __attribute__((packed)) { \
--
1.8.3.1
---
android/hidhost.c | 3 ++-
lib/bluetooth.h | 10 ----------
src/eir.c | 3 ++-
tools/l2test.c | 4 +++-
tools/rctest.c | 4 +++-
tools/scotest.c | 4 +++-
6 files changed, 13 insertions(+), 15 deletions(-)
diff --git a/android/hidhost.c b/android/hidhost.c
index 4226f69..81e7eb8 100644
--- a/android/hidhost.c
+++ b/android/hidhost.c
@@ -38,6 +38,7 @@
#include "lib/sdp.h"
#include "lib/sdp_lib.h"
#include "src/shared/mgmt.h"
+#include "src/shared/util.h"
#include "src/sdp-client.h"
#include "src/uuid-helper.h"
#include "profiles/input/uhid_copy.h"
@@ -1066,7 +1067,7 @@ static void bt_hid_get_report(const void *buf, uint16_t len)
if (cmd->buf_size > 0) {
req[0] = req[0] | HID_GET_REPORT_SIZE_FIELD;
- bt_put_le16(cmd->buf_size, &req[2]);
+ put_le16(cmd->buf_size, &req[2]);
}
fd = g_io_channel_unix_get_fd(dev->ctrl_io);
diff --git a/lib/bluetooth.h b/lib/bluetooth.h
index 61c1f9a..8865c00 100644
--- a/lib/bluetooth.h
+++ b/lib/bluetooth.h
@@ -222,11 +222,6 @@ static inline void bt_put_be32(uint32_t val, const void *ptr)
bt_put_unaligned(bswap_32(val), (uint32_t *) ptr);
}
-static inline void bt_put_le16(uint16_t val, const void *ptr)
-{
- bt_put_unaligned(val, (uint16_t *) ptr);
-}
-
static inline void bt_put_be16(uint16_t val, const void *ptr)
{
bt_put_unaligned(bswap_16(val), (uint16_t *) ptr);
@@ -283,11 +278,6 @@ static inline void bt_put_be32(uint32_t val, const void *ptr)
bt_put_unaligned(val, (uint32_t *) ptr);
}
-static inline void bt_put_le16(uint16_t val, const void *ptr)
-{
- bt_put_unaligned(bswap_16(val), (uint16_t *) ptr);
-}
-
static inline void bt_put_be16(uint16_t val, const void *ptr)
{
bt_put_unaligned(val, (uint16_t *) ptr);
diff --git a/src/eir.c b/src/eir.c
index cc1c5dd..be83718 100644
--- a/src/eir.c
+++ b/src/eir.c
@@ -37,6 +37,7 @@
#include <bluetooth/hci.h>
#include <bluetooth/sdp.h>
+#include "src/shared/util.h"
#include "uuid-helper.h"
#include "eir.h"
@@ -473,7 +474,7 @@ int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod,
eir_total_len += eir_optional_len;
/* store total length */
- bt_put_le16(eir_total_len, data);
+ put_le16(eir_total_len, data);
return eir_total_len;
}
diff --git a/tools/l2test.c b/tools/l2test.c
index 0993f74..8e62227 100644
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -47,6 +47,8 @@
#include <bluetooth/hci_lib.h>
#include <bluetooth/l2cap.h>
+#include "src/shared/util.h"
+
#define NIBBLE_TO_ASCII(c) ((c) < 0x0a ? (c) + 0x30 : (c) + 0x57)
#define BREDR_DEFAULT_PSM 0x1011
@@ -977,7 +979,7 @@ static void do_send(int sk)
seq = 0;
while ((num_frames == -1) || (num_frames-- > 0)) {
bt_put_le32(seq, buf);
- bt_put_le16(data_size, buf + 4);
+ put_le16(data_size, buf + 4);
seq++;
diff --git a/tools/rctest.c b/tools/rctest.c
index 9281392..98fe792 100644
--- a/tools/rctest.c
+++ b/tools/rctest.c
@@ -47,6 +47,8 @@
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include "src/shared/util.h"
+
/* Test modes */
enum {
SEND,
@@ -568,7 +570,7 @@ static void do_send(int sk)
seq = 0;
while ((num_frames == -1) || (num_frames-- > 0)) {
bt_put_le32(seq, buf);
- bt_put_le16(data_size, buf + 4);
+ put_le16(data_size, buf + 4);
seq++;
diff --git a/tools/scotest.c b/tools/scotest.c
index 227287d..13b9602 100644
--- a/tools/scotest.c
+++ b/tools/scotest.c
@@ -40,6 +40,8 @@
#include <bluetooth/bluetooth.h>
#include <bluetooth/sco.h>
+#include "src/shared/util.h"
+
/* Test modes */
enum {
SEND,
@@ -346,7 +348,7 @@ static void send_mode(char *svr)
seq = 0;
while (1) {
bt_put_le32(seq, buf);
- bt_put_le16(data_size, buf + 4);
+ put_le16(data_size, buf + 4);
seq++;
--
1.8.3.1
Add helper to put uint32 values to the given pointer using
little-endian representation.
---
src/shared/util.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/shared/util.h b/src/shared/util.h
index e0a61c6..cc2dbcd 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -95,3 +95,8 @@ static inline void put_le16(uint16_t val, void *dst)
{
put_unaligned(cpu_to_le16(val), (uint16_t *) dst);
}
+
+static inline void put_le32(uint32_t val, void *dst)
+{
+ put_unaligned(cpu_to_le32(val), (uint32_t *) dst);
+}
--
1.8.3.1
---
lib/bluetooth.h | 10 ----------
tools/l2test.c | 2 +-
tools/rctest.c | 2 +-
tools/scotest.c | 2 +-
4 files changed, 3 insertions(+), 13 deletions(-)
diff --git a/lib/bluetooth.h b/lib/bluetooth.h
index 8865c00..cb6f563 100644
--- a/lib/bluetooth.h
+++ b/lib/bluetooth.h
@@ -212,11 +212,6 @@ static inline void bt_put_be64(uint64_t val, const void *ptr)
bt_put_unaligned(bswap_64(val), (uint64_t *) ptr);
}
-static inline void bt_put_le32(uint32_t val, const void *ptr)
-{
- bt_put_unaligned(val, (uint32_t *) ptr);
-}
-
static inline void bt_put_be32(uint32_t val, const void *ptr)
{
bt_put_unaligned(bswap_32(val), (uint32_t *) ptr);
@@ -268,11 +263,6 @@ static inline void bt_put_be64(uint64_t val, const void *ptr)
bt_put_unaligned(val, (uint64_t *) ptr);
}
-static inline void bt_put_le32(uint32_t val, const void *ptr)
-{
- bt_put_unaligned(bswap_32(val), (uint32_t *) ptr);
-}
-
static inline void bt_put_be32(uint32_t val, const void *ptr)
{
bt_put_unaligned(val, (uint32_t *) ptr);
diff --git a/tools/l2test.c b/tools/l2test.c
index 8e62227..a9fc642 100644
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -978,7 +978,7 @@ static void do_send(int sk)
seq = 0;
while ((num_frames == -1) || (num_frames-- > 0)) {
- bt_put_le32(seq, buf);
+ put_le32(seq, buf);
put_le16(data_size, buf + 4);
seq++;
diff --git a/tools/rctest.c b/tools/rctest.c
index 98fe792..2c7e45b 100644
--- a/tools/rctest.c
+++ b/tools/rctest.c
@@ -569,7 +569,7 @@ static void do_send(int sk)
seq = 0;
while ((num_frames == -1) || (num_frames-- > 0)) {
- bt_put_le32(seq, buf);
+ put_le32(seq, buf);
put_le16(data_size, buf + 4);
seq++;
diff --git a/tools/scotest.c b/tools/scotest.c
index 13b9602..e5530d9 100644
--- a/tools/scotest.c
+++ b/tools/scotest.c
@@ -347,7 +347,7 @@ static void send_mode(char *svr)
seq = 0;
while (1) {
- bt_put_le32(seq, buf);
+ put_le32(seq, buf);
put_le16(data_size, buf + 4);
seq++;
--
1.8.3.1
Add helper to put uint16 values to the given pointer using little-endian
representation.
---
src/shared/util.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/shared/util.h b/src/shared/util.h
index d7d4a94..e0a61c6 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -90,3 +90,8 @@ void util_debug(util_debug_func_t function, void *user_data,
void util_hexdump(const char dir, const unsigned char *buf, size_t len,
util_debug_func_t function, void *user_data);
+
+static inline void put_le16(uint16_t val, void *dst)
+{
+ put_unaligned(cpu_to_le16(val), (uint16_t *) dst);
+}
--
1.8.3.1
This patchset adds the following features:
* register external characteristics
* basic read or write external characteristics
Usage of glib is being avoided inside src/gatt.c. Remaining
put/get little-endian/big-endian helpers will be added in the
next patchset.
TODO (missing features of this patchset and upstream code):
* Properties/flags: operations and security
* 32-bit UUIDs
* Remove/free services
* multiple services provided by the same client
* remove att_put_* att_get_* helpers
Changes from v2 to v3:
* return put_uuid() definition to src/gatt.c
* add put_le16(), put_le32(), put_le128() to src/shared/util.h
Changes from v1 to v2:
* move put_uuid() and put_le16() to src/shared/util.h
* check malloc0() and new0() return value
Changes from v0 to v1:
* Use LE <-> CPU helpers defined in src/shared/util.h
* remove patch "gatt: Add helper for reading characteristics" from
this set.
How to test:
It is possible to test the implemented features using a modified
gatttool version. Unix socket is used as transport to allow basic
ATT operations:
git://git.infradead.org/users/cktakahasi/bluez.git gatt-api-forupstream-group2-patchv3-testing
$gatttool -I -L
[LOCAL][LE]> connect
Local connection
Connection successful
[LOCAL][LE]> primary
attr handle: 0x0001, end grp handle: 0x0003 uuid: 000034fb-0000-1000-8000-00805f9b34fb
[LOCAL][LE]> characteristics
handle: 0x0002, char properties: 0x00, char value handle: 0x0003, uuid: 00002a06-0000-1000-8000-00805f9b34fb
[LOCAL][LE]> char-read-hnd 0x0003
Characteristic value/descriptor: 00
[LOCAL][LE]> char-write-req 0x0003 02
Characteristic value was written successfully
[LOCAL][LE]> char-read-hnd 0x0003
Characteristic value/descriptor: 02
Alvaro Silva (3):
tools: Add Alert Level characteristic to gatt-service
tools: Add reading Value property of gatt-service
tools: Add setting Value property of gatt-service
Andre Guedes (2):
gatt: Add function to create constant attributes
gatt: Add helper for creating GATT characteristics
Claudio Takahasi (15):
shared: Add put_le16()
lib: Remove bt_put_le16()
shared: Add put_le32()
lib: Remove bt_put_le32()
shared: Add put_le128()
gatt: Add characteristic to the database
gatt: Add hash table of GDBusProxy objects
gatt: Add read callback to btd_gatt_add_char helper
gatt: Assign read callback for external services
gatt: Add write callback to btd_gatt_add_char helper
gdbus: Add g_dbus_proxy_set_property_array
gatt: Assign write callback for external services
gatt: Add result callback for Write Request
gatt: Add Write Request handling for GDBusProxy
tools: Emit property changed when Value changes
android/hidhost.c | 3 +-
gdbus/client.c | 85 +++++++++++++++++++++++
gdbus/gdbus.h | 5 ++
lib/bluetooth.h | 20 ------
src/eir.c | 3 +-
src/gatt-dbus.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++--
src/gatt.c | 139 +++++++++++++++++++++++++++++++++++--
src/gatt.h | 44 ++++++++++++
src/shared/util.h | 33 +++++++++
tools/gatt-service.c | 156 ++++++++++++++++++++++++++++++++++++++----
tools/l2test.c | 6 +-
tools/rctest.c | 6 +-
tools/scotest.c | 6 +-
13 files changed, 646 insertions(+), 50 deletions(-)
--
1.8.3.1
This patch extends gatt-service tool, emitting PropertiesChanged for
"Value" when a new value is set. This flow represents the handling of
ATT Write Command or Write Request which is translated to a GDBusProxy
set property. PropertiesChanged will be tracked by the characteristic
GDBusProxy in order to detect "Value" changes.
---
tools/gatt-service.c | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index ad5399b..8dbecce 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -47,9 +47,11 @@
static GMainLoop *main_loop;
static GSList *services;
+static DBusConnection *connection;
struct characteristic {
char *uuid;
+ char *path;
uint8_t *value;
int vlen;
};
@@ -110,6 +112,8 @@ static void chr_set_value(const GDBusPropertyTable *property,
chr->vlen = len;
g_dbus_pending_property_success(id);
+ g_dbus_emit_property_changed(connection, chr->path,
+ GATT_CHR_IFACE, "Value");
}
static const GDBusPropertyTable chr_properties[] = {
@@ -163,6 +167,7 @@ static void chr_iface_destroy(gpointer user_data)
g_free(chr->uuid);
g_free(chr->value);
+ g_free(chr->path);
g_free(chr);
}
@@ -182,6 +187,7 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
chr->uuid = g_strdup(uuid);
chr->value = g_memdup(value, vlen);
chr->vlen = vlen;
+ chr->path = path;
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
@@ -190,18 +196,16 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
ret = -EIO;
}
- g_free(path);
-
return ret;
}
-static char *register_service(DBusConnection *conn, const char *uuid)
+static char *register_service(const char *uuid)
{
static int id = 1;
char *path;
path = g_strdup_printf("/service%d", id++);
- if (g_dbus_register_interface(conn, path, GATT_SERVICE_IFACE,
+ if (g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
NULL, NULL, service_properties,
g_strdup(uuid), g_free) == FALSE) {
printf("Couldn't register service interface\n");
@@ -212,13 +216,13 @@ static char *register_service(DBusConnection *conn, const char *uuid)
return path;
}
-static void create_services(DBusConnection *conn)
+static void create_services()
{
char *service_path;
uint8_t level = 0;
int ret;
- service_path = register_service(conn, IAS_UUID);
+ service_path = register_service(IAS_UUID);
if (service_path == NULL)
return;
@@ -226,11 +230,11 @@ static void create_services(DBusConnection *conn)
* According to the IAS SPEC, reading <<Alert level>> is not alloweed.
* "Value" is readable for testing purpose only.
*/
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ ret = register_characteristic(connection, ALERT_LEVEL_CHR_UUID, &level,
sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
- g_dbus_unregister_interface(conn, service_path,
+ g_dbus_unregister_interface(connection, service_path,
GATT_SERVICE_IFACE);
g_free(service_path);
return;
@@ -371,25 +375,24 @@ static guint setup_signalfd(void)
int main(int argc, char *argv[])
{
GDBusClient *client;
- DBusConnection *dbus_conn;
guint signal;
signal = setup_signalfd();
if (signal == 0)
return -errno;
- dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
main_loop = g_main_loop_new(NULL, FALSE);
- g_dbus_attach_object_manager(dbus_conn);
+ g_dbus_attach_object_manager(connection);
printf("gatt-service unique name: %s\n",
- dbus_bus_get_unique_name(dbus_conn));
+ dbus_bus_get_unique_name(connection));
- create_services(dbus_conn);
+ create_services();
- client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+ client = g_dbus_client_new(connection, "org.bluez", "/org/bluez");
g_dbus_client_set_connect_watch(client, connect_handler, NULL);
@@ -400,7 +403,7 @@ int main(int argc, char *argv[])
g_source_remove(signal);
g_slist_free_full(services, g_free);
- dbus_connection_unref(dbus_conn);
+ dbus_connection_unref(connection);
return 0;
}
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch registers the Alert Level characteristic object related to
Immediate Alert Service. It adds the characteristic UUID property only.
---
tools/gatt-service.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index cba0ff3..d1606ad 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -37,13 +37,30 @@
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
+#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
/* Immediate Alert Service UUID */
#define IAS_UUID "00001802-0000-1000-8000-00805f9b34fb"
+#define ALERT_LEVEL_CHR_UUID "00002a06-0000-1000-8000-00805f9b34fb"
static GMainLoop *main_loop;
static GSList *services;
+static gboolean chr_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *uuid = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable chr_properties[] = {
+ { "UUID", "s", chr_get_uuid },
+ { }
+};
+
static gboolean service_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
@@ -83,6 +100,27 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const char *service_path)
+{
+ static int id = 1;
+ char *path;
+ int ret = 0;
+
+ path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+
+ if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
+ NULL, NULL, chr_properties,
+ g_strdup(uuid), g_free) == FALSE) {
+ printf("Couldn't register characteristic interface\n");
+ ret = -EIO;
+ }
+
+ g_free(path);
+
+ return ret;
+}
+
static char *register_service(DBusConnection *conn, const char *uuid)
{
static int id = 1;
@@ -103,11 +141,24 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ int ret;
service_path = register_service(conn, IAS_UUID);
+ if (service_path == NULL)
+ return;
- services = g_slist_prepend(services, service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
+ service_path);
+ if (ret < 0) {
+ printf("Couldn't register Alert Level characteristic (IAS)\n");
+ g_dbus_unregister_interface(conn, service_path,
+ GATT_SERVICE_IFACE);
+ g_free(service_path);
+ return;
+ }
+ services = g_slist_prepend(services, service_path);
printf("Registered service: %s\n", service_path);
}
--
1.8.3.1
This patch adds a new gdbus utility function to allow setting a property
of fixed, and non-fixed values array.
---
gdbus/client.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdbus/gdbus.h | 5 ++++
2 files changed, 90 insertions(+)
diff --git a/gdbus/client.c b/gdbus/client.c
index 5193b6c..6fea3d9 100644
--- a/gdbus/client.c
+++ b/gdbus/client.c
@@ -727,6 +727,91 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
return TRUE;
}
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ int size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy)
+{
+ struct set_property_data *data;
+ GDBusClient *client;
+ DBusMessage *msg;
+ DBusMessageIter iter, variant, array;
+ DBusPendingCall *call;
+ char array_sig[3];
+ char type_sig[2];
+
+ if (proxy == NULL || name == NULL || value == NULL)
+ return FALSE;
+
+ if (dbus_type_is_basic(type) == FALSE)
+ return FALSE;
+
+ client = proxy->client;
+ if (client == NULL)
+ return FALSE;
+
+ data = g_try_new0(struct set_property_data, 1);
+ if (data == NULL)
+ return FALSE;
+
+ data->function = function;
+ data->user_data = user_data;
+ data->destroy = destroy;
+
+ msg = dbus_message_new_method_call(client->service_name,
+ proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set");
+ if (msg == NULL) {
+ g_free(data);
+ return FALSE;
+ }
+
+ array_sig[0] = DBUS_TYPE_ARRAY;
+ array_sig[1] = (char) type;
+ array_sig[2] = '\0';
+
+ type_sig[0] = (char) type;
+ type_sig[1] = '\0';
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &proxy->interface);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ array_sig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ type_sig, &array);
+
+ if (dbus_type_is_fixed(type) == TRUE)
+ dbus_message_iter_append_fixed_array(&array, type,
+ &value, size);
+ else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+ int i;
+ const char **str = (const char **) value;
+
+ for (i = 0; i < size; i++)
+ dbus_message_iter_append_basic(&array, type, &str[i]);
+ }
+
+ dbus_message_iter_close_container(&variant, &array);
+ dbus_message_iter_close_container(&iter, &variant);
+
+ if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+ &call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ g_free(data);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
struct method_call_data {
GDBusReturnFunction function;
void *user_data;
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 8ada200..40a5abe 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -329,6 +329,11 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
GDBusResultFunction function, void *user_data,
GDBusDestroyFunction destroy);
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ int size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy);
+
typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data);
typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data);
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding a generic callback
to allow set the characteristic Value. It doesn't check for
characteristic properties yet.
---
tools/gatt-service.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 97dc8d4..ad5399b 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -35,6 +35,8 @@
#include <dbus/dbus.h>
#include <gdbus/gdbus.h>
+#include "src/error.h"
+
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
@@ -81,9 +83,38 @@ static gboolean chr_get_value(const GDBusPropertyTable *property,
return TRUE;
}
+static void chr_set_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+ uint8_t *value;
+ int len;
+
+ printf("Set('Value', ...)\n");
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ printf("Invalid value for Set('Value'...)\n");
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ g_free(chr->value);
+ chr->value = g_memdup(value, len);
+ chr->vlen = len;
+
+ g_dbus_pending_property_success(id);
+}
+
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
- { "Value", "ay", chr_get_value, NULL, NULL },
+ { "Value", "ay", chr_get_value, chr_set_value, NULL },
{ }
};
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding the callback to
allow reading the Value property. At the moment, it is a generic
callback and it doesn't consider characteristic value property
restrictions.
---
tools/gatt-service.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 53 insertions(+), 6 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index d1606ad..97dc8d4 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -46,18 +46,44 @@
static GMainLoop *main_loop;
static GSList *services;
+struct characteristic {
+ char *uuid;
+ uint8_t *value;
+ int vlen;
+};
+
static gboolean chr_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
- const char *uuid = user_data;
+ struct characteristic *chr = user_data;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid);
+
+ return TRUE;
+}
+
+static gboolean chr_get_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+
+ printf("Get(\"Value\")\n");
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &chr->value, chr->vlen);
+
+ dbus_message_iter_close_container(iter, &array);
return TRUE;
}
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
+ { "Value", "ay", chr_get_value, NULL, NULL },
{ }
};
@@ -100,18 +126,35 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static void chr_iface_destroy(gpointer user_data)
+{
+ struct characteristic *chr = user_data;
+
+ g_free(chr->uuid);
+ g_free(chr->value);
+ g_free(chr);
+}
+
static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const uint8_t *value, int vlen,
const char *service_path)
{
+ struct characteristic *chr;
static int id = 1;
char *path;
int ret = 0;
path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+ chr = g_new0(struct characteristic, 1);
+
+ chr->uuid = g_strdup(uuid);
+ chr->value = g_memdup(value, vlen);
+ chr->vlen = vlen;
+
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
- g_strdup(uuid), g_free) == FALSE) {
+ chr, chr_iface_destroy) == FALSE) {
printf("Couldn't register characteristic interface\n");
ret = -EIO;
}
@@ -141,15 +184,19 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ uint8_t level = 0;
int ret;
service_path = register_service(conn, IAS_UUID);
if (service_path == NULL)
return;
- /* Add Alert Level Characteristic to Immediate Alert Service */
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
- service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service
+ * According to the IAS SPEC, reading <<Alert level>> is not alloweed.
+ * "Value" is readable for testing purpose only.
+ */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
g_dbus_unregister_interface(conn, service_path,
--
1.8.3.1
When the characteristic requires Write Request, the ATT response should
be sent after receiving the reply for the Set method call.
---
src/gatt-dbus.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 61 insertions(+), 6 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index d703355..2412e2d 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,11 @@ struct external_app {
unsigned int watch;
};
+struct proxy_write_data {
+ btd_attr_write_result_t result_cb;
+ void *user_data;
+};
+
/*
* Attribute to Proxy hash table. Used to map incoming
* ATT operations to its external characteristic proxy.
@@ -181,8 +186,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
static void proxy_write_reply(const DBusError *derr, void *user_data)
{
- if (derr)
- DBG("Write reply: %s", derr->message);
+ struct proxy_write_data *wdata = user_data;
+ int err = 0;
+
+ /*
+ * Security requirements shall be handled by the core. If external
+ * applications returns an error, the reasons will be restricted to
+ * invalid argument or application specific errors.
+ */
+
+ if (dbus_error_is_set(derr) == FALSE)
+ goto done;
+
+ DBG("Write reply: %s", derr->message);
+
+ if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
+ err = ETIMEDOUT;
+ else if (dbus_error_has_name(derr, ERROR_INTERFACE ".InvalidArguments"))
+ err = EINVAL;
+ else
+ err = EPROTO;
+
+done:
+ if (wdata && wdata->result_cb)
+ wdata->result_cb(err, wdata->user_data);
}
static void proxy_write_cb(struct btd_attribute *attr,
@@ -193,16 +220,44 @@ static void proxy_write_cb(struct btd_attribute *attr,
GDBusProxy *proxy;
proxy = g_hash_table_lookup(proxy_hash, attr);
- if (proxy == NULL)
- /* FIXME: Attribute not found */
+ if (proxy == NULL) {
+ result(ENOENT, user_data);
return;
+ }
- g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ /*
+ * "result" callback defines if the core wants to receive the
+ * operation result, allowing to select ATT Write Request or Write
+ * Command. Descriptors requires Write Request operation. For
+ * Characteristics, the implementation will define which operations
+ * are allowed based on the properties/flags.
+ * TODO: Write Long Characteristics/Descriptors.
+ */
+
+ if (result) {
+ struct proxy_write_data *wdata;
+
+ wdata = g_new0(struct proxy_write_data, 1);
+ wdata->result_cb = result;
+ wdata->user_data = user_data;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ wdata, g_free);
+ } else {
+ /*
+ * Caller is not interested in the Set method call result.
+ * This flow implements the ATT Write Command scenario, where
+ * the remote doesn't receive ATT response.
+ */
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
value, len, proxy_write_reply,
NULL, NULL);
+ }
- DBG("Server: Write characteristic callback %s",
+ DBG("Server: Write attribute callback %s",
g_dbus_proxy_get_path(proxy));
+
}
static int register_external_service(const struct external_app *eapp,
--
1.8.3.1
This patch adds an extra callback to inform the result of the write
operation on an external entity to allow the core to reply properly
for the ATT request.
---
src/gatt-dbus.c | 4 +++-
src/gatt.h | 5 ++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 7e81dc6..d703355 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -186,7 +186,9 @@ static void proxy_write_reply(const DBusError *derr, void *user_data)
}
static void proxy_write_cb(struct btd_attribute *attr,
- const uint8_t *value, size_t len)
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data)
{
GDBusProxy *proxy;
diff --git a/src/gatt.h b/src/gatt.h
index f4eebe5..ed5fe80 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -48,8 +48,11 @@ typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
* @err: error in errno format.
* @user_data: user_data passed in btd_attr_write_t callback
*/
+typedef void (*btd_attr_write_result_t) (int err, void *user_data);
typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
- const uint8_t *value, size_t len);
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
--
1.8.3.1
This patch adds the callback for writing the external characteristic
value. Internally, GDBusProxy implementation calls Set method of the
"Value" property. At the moment, potential errors returned by the
external service implementation are being ignored.
The ATT operation (Write Command or Request) is not exposed to the
external service implementation. GATT/GDBusProxy handles possible
DBus errors returned, and notify the core using the callbacks informed.
Write Command doesn't have callback since it doesn't return ATT errors.
---
src/gatt-dbus.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index a02d565..7e81dc6 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -179,6 +179,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
result(0, value, len, user_data);
}
+static void proxy_write_reply(const DBusError *derr, void *user_data)
+{
+ if (derr)
+ DBG("Write reply: %s", derr->message);
+}
+
+static void proxy_write_cb(struct btd_attribute *attr,
+ const uint8_t *value, size_t len)
+{
+ GDBusProxy *proxy;
+
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL)
+ /* FIXME: Attribute not found */
+ return;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ NULL, NULL);
+
+ DBG("Server: Write characteristic callback %s",
+ g_dbus_proxy_get_path(proxy));
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -238,7 +262,8 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb,
+ proxy_write_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This patch adds a function callback for read operations. When a remote
device wants to reads a given attribute, the core calls the specified
read callback to get the value from the external services.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 +++++--
src/gatt.h | 20 +++++++++++++++++++-
3 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index f60260e..275b263 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -198,7 +198,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00);
+ attr = btd_gatt_add_char(&uuid, 0x00, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 41d03cd..cc4f327 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -45,6 +45,7 @@ static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
+ btd_attr_read_t read_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -120,7 +121,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
}
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
- uint8_t properties)
+ uint8_t properties,
+ btd_attr_read_t read_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -178,8 +180,9 @@ struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
*/
char_value->type = *uuid;
+ char_value->read_cb = read_cb;
- /* TODO: Read & Write callbacks */
+ /* TODO: Write callbacks */
if (local_database_add(next_handle, char_value) < 0)
/* TODO: remove declaration */
diff --git a/src/gatt.h b/src/gatt.h
index d7acc5b..e446fa0 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -27,6 +27,22 @@ void gatt_init(void);
void gatt_cleanup(void);
+/*
+ * Callbacks of this type are called once the value from the attribute is
+ * ready to be read from the service implementation. Result callback is
+ * the asynchronous function that should be used to inform the caller
+ * the read value.
+ * @err: error in errno format.
+ * @value: pointer to value
+ * @len: length of value
+ * @user_data: user_data passed in btd_attr_read_t callback
+ */
+typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
+ void *user_data);
+typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
+ btd_attr_read_result_t result,
+ void *user_data);
+
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
*
@@ -40,9 +56,11 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* to local attribute database.
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
+ * @read_cb: Callback used to provide the characteristic value.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
- uint8_t properties);
+ uint8_t properties,
+ btd_attr_read_t read_cb);
--
1.8.3.1
This patch adds a function callback for write operations. When a remote
device writes to a given attribute, the core calls the specified write
callback informing the attribute server the new characteristic value.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 ++++---
src/gatt.h | 13 ++++++++++++-
3 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index fff2d3a..a02d565 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -238,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index cc4f327..5d2c3d1 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -46,6 +46,7 @@ struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
btd_attr_read_t read_cb;
+ btd_attr_write_t write_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -122,7 +123,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb)
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -181,8 +183,7 @@ struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
char_value->type = *uuid;
char_value->read_cb = read_cb;
-
- /* TODO: Write callbacks */
+ char_value->write_cb = write_cb;
if (local_database_add(next_handle, char_value) < 0)
/* TODO: remove declaration */
diff --git a/src/gatt.h b/src/gatt.h
index e446fa0..f4eebe5 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -42,6 +42,14 @@ typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
btd_attr_read_result_t result,
void *user_data);
+/*
+ * Callbacks from this type are called once the value from the attribute was
+ * written.
+ * @err: error in errno format.
+ * @user_data: user_data passed in btd_attr_write_t callback
+ */
+typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
+ const uint8_t *value, size_t len);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
@@ -57,10 +65,13 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
* @read_cb: Callback used to provide the characteristic value.
+ * @write_cb: Callback called to notify the implementation that a new value
+ * is available.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb);
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb);
--
1.8.3.1
This patch adds the callback for reading the external characteristic
Value. Internally, GDBusProxy implementation tracks all properties Value
changes consequently Value can be ready directly from the proxy without
additional method calls.
---
src/gatt-dbus.c | 42 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 275b263..fff2d3a 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -139,6 +139,46 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
eapp->proxies = g_slist_remove(eapp->proxies, proxy);
}
+static void proxy_read_cb(struct btd_attribute *attr,
+ btd_attr_read_result_t result, void *user_data)
+{
+ DBusMessageIter iter, array;
+ GDBusProxy *proxy;
+ uint8_t *value;
+ int len;
+
+ /*
+ * Remote device is trying to read the informed attribute,
+ * "Value" should be read from the proxy. GDBusProxy tracks
+ * properties changes automatically, it is not necessary to
+ * get the value directly from the GATT server.
+ */
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL) {
+ result(ENOENT, NULL, 0, user_data);
+ return;
+ }
+
+ if (!g_dbus_proxy_get_property(proxy, "Value", &iter)) {
+ /* Unusual situation, read property will checked earlier */
+ result(EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ DBG("External service inconsistent!");
+ result(EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ DBG("attribute: %p read %d bytes", attr, len);
+
+ result(0, value, len, user_data);
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -198,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This commits adds a hash table to map attributes into GDBusProxy,
allowing to call remote objects for reading and writing values.
---
src/gatt-dbus.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 9f94c4f..f60260e 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,12 @@ struct external_app {
unsigned int watch;
};
+/*
+ * Attribute to Proxy hash table. Used to map incoming
+ * ATT operations to its external characteristic proxy.
+ */
+static GHashTable *proxy_hash;
+
static GSList *external_apps;
static int external_app_path_cmp(gconstpointer a, gconstpointer b)
@@ -172,6 +178,7 @@ static int register_external_characteristics(GSList *proxies)
DBusMessageIter iter;
const char *str, *path;
bt_uuid_t uuid;
+ struct btd_attribute *attr;
GDBusProxy *proxy = list->data;
if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
@@ -191,11 +198,14 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- if (btd_gatt_add_char(&uuid, 0x00) == NULL)
+ attr = btd_gatt_add_char(&uuid, 0x00);
+ if (attr == NULL)
return -EINVAL;
path = g_dbus_proxy_get_path(proxy);
DBG("Added GATT CHR: %s (%s)", path, str);
+
+ g_hash_table_insert(proxy_hash, attr, g_dbus_proxy_ref(proxy));
}
return 0;
@@ -318,13 +328,22 @@ static const GDBusMethodTable methods[] = {
gboolean gatt_dbus_manager_register(void)
{
- return g_dbus_register_interface(btd_get_dbus_connection(),
- "/org/bluez", GATT_MGR_IFACE,
- methods, NULL, NULL, NULL, NULL);
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ "/org/bluez", GATT_MGR_IFACE,
+ methods, NULL, NULL, NULL, NULL) == FALSE)
+ return FALSE;
+
+ proxy_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) g_dbus_proxy_unref);
+
+ return TRUE;
}
void gatt_dbus_manager_unregister(void)
{
+ g_hash_table_destroy(proxy_hash);
+ proxy_hash = NULL;
+
g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez",
GATT_MGR_IFACE);
}
--
1.8.3.1
From: Andre Guedes <[email protected]>
This patch adds btd_gatt_add_char() helper. It creates and adds the
Characteristic declaration, and Characteristic value attributes to the
local attribute database.
---
src/gatt.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/gatt.h | 12 +++++++++
2 files changed, 100 insertions(+)
diff --git a/src/gatt.c b/src/gatt.c
index a4d1665..41d03cd 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -39,6 +39,9 @@
static const bt_uuid_t primary_uuid = { .type = BT_UUID16,
.value.u16 = GATT_PRIM_SVC_UUID };
+static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
+ .value.u16 = GATT_CHARAC_UUID };
+
struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
@@ -116,6 +119,91 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
return attr;
}
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+ uint8_t properties)
+{
+ struct btd_attribute *char_decl, *char_value = NULL;
+
+ /* Attribute value length */
+ uint16_t len = 1 + 2 + bt_uuid_len(uuid);
+ uint8_t value[len];
+
+ /*
+ * Characteristic DECLARATION
+ *
+ * TYPE ATTRIBUTE VALUE
+ * +-------+---------------------------------+
+ * |0x2803 | 0xXX 0xYYYY 0xZZZZ... |
+ * | (1) | (2) (3) (4) |
+ * +------+----------------------------------+
+ * (1) - 2 octets: Characteristic declaration UUID
+ * (2) - 1 octet : Properties
+ * (3) - 2 octets: Handle of the characteristic Value
+ * (4) - 2 or 16 octets: Characteristic UUID
+ */
+
+ value[0] = properties;
+
+ /*
+ * Since we don't know yet the characteristic value attribute
+ * handle, we skip and set it later.
+ */
+
+ put_uuid(uuid, &value[3]);
+
+ char_decl = new_const_attribute(&chr_uuid, value, len);
+ if (char_decl == NULL)
+ goto fail;
+
+ char_value = new0(struct btd_attribute, 1);
+ if (char_value == NULL)
+ goto fail;
+
+ if (local_database_add(next_handle, char_decl) < 0)
+ goto fail;
+
+ next_handle = next_handle + 1;
+
+ /*
+ * Characteristic VALUE
+ *
+ * TYPE ATTRIBUTE VALUE
+ * +----------+---------------------------------+
+ * |0xZZZZ... | 0x... |
+ * | (1) | (2) |
+ * +----------+---------------------------------+
+ * (1) - 2 or 16 octets: Characteristic UUID
+ * (2) - N octets: Value is read dynamically from the service
+ * implementation (external entity).
+ */
+
+ char_value->type = *uuid;
+
+ /* TODO: Read & Write callbacks */
+
+ if (local_database_add(next_handle, char_value) < 0)
+ /* TODO: remove declaration */
+ goto fail;
+
+ next_handle = next_handle + 1;
+
+ /*
+ * Update characteristic value handle in characteristic declaration
+ * attribute. For local attributes, we can assume that the handle
+ * representing the characteristic value will get the next available
+ * handle. However, for remote attribute this assumption is not valid.
+ */
+ put_le16(char_value->handle, &char_decl->value[1]);
+
+ return char_value;
+
+fail:
+ free(char_decl);
+ free(char_value);
+
+ return NULL;
+}
+
void gatt_init(void)
{
DBG("Starting GATT server");
diff --git a/src/gatt.h b/src/gatt.h
index 8dd1312..d7acc5b 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -34,3 +34,15 @@ void gatt_cleanup(void);
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
+
+/*
+ * btd_gatt_add_char - Add a characteristic (declaration and value attributes)
+ * to local attribute database.
+ * @uuid: Characteristic UUID (16-bits or 128-bits).
+ * @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
+ *
+ * Returns a reference to characteristic value attribute. In case of error,
+ * NULL is returned.
+ */
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+ uint8_t properties);
--
1.8.3.1
Add helper to put uint16 values to the given buffer using little-endian
representation.
---
src/shared/util.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/shared/util.h b/src/shared/util.h
index f614c99..399f678 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -93,6 +93,11 @@ void util_debug(util_debug_func_t function, void *user_data,
void util_hexdump(const char dir, const unsigned char *buf, size_t len,
util_debug_func_t function, void *user_data);
+static inline void put_le16(uint16_t val, void *dst)
+{
+ put_unaligned(cpu_to_le16(val), (uint16_t *) dst);
+}
+
static inline void put_uuid(const bt_uuid_t *src, void *dst)
{
if (src->type == BT_UUID16)
--
1.8.3.1
From: Andre Guedes <[email protected]>
This patch adds a helper function to create attribute with static
values. It is intended to be used to create GATT services, and
characteristic declarations.
---
src/gatt.c | 37 +++++++++++++++++++++++++++++++------
1 file changed, 31 insertions(+), 6 deletions(-)
diff --git a/src/gatt.c b/src/gatt.c
index f7b74d6..a4d1665 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -30,6 +30,7 @@
#include "log.h"
#include "lib/uuid.h"
#include "attrib/att.h"
+#include "src/shared/util.h"
#include "gatt-dbus.h"
#include "gatt.h"
@@ -48,6 +49,28 @@ struct btd_attribute {
static GList *local_attribute_db;
static uint16_t next_handle = 0x0001;
+/*
+ * Helper function to create new attributes containing constant/static values.
+ * eg: declaration of services/characteristics, and characteristics with
+ * fixed values.
+ */
+static struct btd_attribute *new_const_attribute(const bt_uuid_t *type,
+ const uint8_t *value,
+ uint16_t len)
+{
+ struct btd_attribute *attr;
+
+ attr = malloc0(sizeof(struct btd_attribute) + len);
+ if (attr == NULL)
+ return NULL;
+
+ attr->type = *type;
+ memcpy(&attr->value, value, len);
+ attr->value_len = len;
+
+ return attr;
+}
+
static int local_database_add(uint16_t handle, struct btd_attribute *attr)
{
attr->handle = handle;
@@ -59,9 +82,9 @@ static int local_database_add(uint16_t handle, struct btd_attribute *attr)
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
{
+ struct btd_attribute *attr;
uint16_t len = bt_uuid_len(uuid);
- struct btd_attribute *attr = g_malloc0(sizeof(struct btd_attribute) +
- len);
+ uint8_t value[len];
/*
* Service DECLARATION
@@ -75,13 +98,15 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
* (2) - 2 or 16 octets: Service UUID
*/
- attr->type = primary_uuid;
+ /* Set attribute value */
+ put_uuid(uuid, value);
- att_put_uuid(*uuid, attr->value);
- attr->value_len = len;
+ attr = new_const_attribute(&primary_uuid, value, len);
+ if (attr == NULL)
+ return NULL;
if (local_database_add(next_handle, attr) < 0) {
- g_free(attr);
+ free(attr);
return NULL;
}
--
1.8.3.1
Initial support for GATT characteristics. This patch adds the
characteristic declaration attribute to the GATT local database based
on the fetched GDBusProxy objects.
---
src/gatt-dbus.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 3fbff87..9f94c4f 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -163,6 +163,44 @@ static int register_external_service(const struct external_app *eapp,
return 0;
}
+static int register_external_characteristics(GSList *proxies)
+
+{
+ GSList *list;
+
+ for (list = proxies; list; list = g_slist_next(proxies)) {
+ DBusMessageIter iter;
+ const char *str, *path;
+ bt_uuid_t uuid;
+ GDBusProxy *proxy = list->data;
+
+ if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+ return -EINVAL;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&iter, &str);
+
+ if (bt_string_to_uuid(&uuid, str) < 0)
+ return -EINVAL;
+
+ /*
+ * TODO: Missing Flags/property
+ * Add properties according to Core SPEC 4.1 page 2183.
+ * Reference table 3.5: Characteristic Properties bit field.
+ */
+
+ if (btd_gatt_add_char(&uuid, 0x00) == NULL)
+ return -EINVAL;
+
+ path = g_dbus_proxy_get_path(proxy);
+ DBG("Added GATT CHR: %s (%s)", path, str);
+ }
+
+ return 0;
+}
+
static void client_ready(GDBusClient *client, void *user_data)
{
struct external_app *eapp = user_data;
@@ -177,6 +215,9 @@ static void client_ready(GDBusClient *client, void *user_data)
if (register_external_service(eapp, proxy) < 0)
goto fail;
+ if (register_external_characteristics(g_slist_next(eapp->proxies)) < 0)
+ goto fail;
+
DBG("Added GATT service %s", eapp->path);
reply = dbus_message_new_method_return(eapp->reg);
@@ -186,7 +227,7 @@ fail:
error("Could not register external service: %s", eapp->path);
reply = btd_error_invalid_args(eapp->reg);
- /* TODO: missing eapp cleanup */
+ /* TODO: missing eapp/database cleanup */
reply:
dbus_message_unref(eapp->reg);
--
1.8.3.1
This patchset adds the following features:
* register external characteristics
* basic read or write external characteristics
Usage of glib is being avoided inside src/gatt.c.
TODO (missing features of this patchset and upstream code):
* Properties/flags: operations and security
* 32-bit UUIDs
* Remove/free services
* multiple services provided by the same client
Changes from v1 to v2:
* move put_uuid() and put_le16() to src/shared/util.h
* check malloc0() and new0() return value
Changes from v0 to v1:
* Use LE <-> CPU helpers defined in src/shared/util.h
* remove patch "gatt: Add helper for reading characteristics" from
this set.
How to test:
It is possible to test the implemented features using a modified
gatttool version. Unix socket is used as transport to allow basic
ATT operations:
git://git.infradead.org/users/cktakahasi/bluez.git gatt-api-forupstream-group2-patchv2-testing
$gatttool -I -L
[LOCAL][LE]> connect
Local connection
Connection successful
[LOCAL][LE]> primary
attr handle: 0x0001, end grp handle: 0x0003 uuid: 000034fb-0000-1000-8000-00805f9b34fb
[LOCAL][LE]> characteristics
handle: 0x0002, char properties: 0x00, char value handle: 0x0003, uuid: 00002a06-0000-1000-8000-00805f9b34fb
[LOCAL][LE]> char-read-hnd 0x0003
Characteristic value/descriptor: 00
[LOCAL][LE]> char-write-req 0x0003 02
Characteristic value was written successfully
[LOCAL][LE]> char-read-hnd 0x0003
Characteristic value/descriptor: 02
Alvaro Silva (3):
tools: Add Alert Level characteristic to gatt-service
tools: Add reading Value property of gatt-service
tools: Add setting Value property of gatt-service
Andre Guedes (2):
gatt: Add function to create constant attributes
gatt: Add helper for creating GATT characteristics
Claudio Takahasi (12):
shared: Add put_uuid()
shared: Add put_le16()
gatt: Add characteristic to the database
gatt: Add hash table of GDBusProxy objects
gatt: Add read callback to btd_gatt_add_char helper
gatt: Assign read callback for external services
gatt: Add write callback to btd_gatt_add_char helper
gdbus: Add g_dbus_proxy_set_property_array
gatt: Assign write callback for external services
gatt: Add result callback for Write Request
gatt: Add Write Request handling for GDBusProxy
tools: Emit property changed when Value changes
gdbus/client.c | 85 +++++++++++++++++++++++
gdbus/gdbus.h | 5 ++
src/gatt-dbus.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++--
src/gatt.c | 129 ++++++++++++++++++++++++++++++++--
src/gatt.h | 44 ++++++++++++
src/shared/util.h | 17 +++++
tools/gatt-service.c | 156 ++++++++++++++++++++++++++++++++++++++----
7 files changed, 604 insertions(+), 22 deletions(-)
--
1.8.3.1
Add helper to put 16/32/128-bit UUID values to the given buffer using
little-endian representation.
---
src/shared/util.h | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/shared/util.h b/src/shared/util.h
index d7d4a94..f614c99 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -26,6 +26,8 @@
#include <alloca.h>
#include <byteswap.h>
+#include "lib/uuid.h"
+
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define le16_to_cpu(val) (val)
#define le32_to_cpu(val) (val)
@@ -90,3 +92,13 @@ void util_debug(util_debug_func_t function, void *user_data,
void util_hexdump(const char dir, const unsigned char *buf, size_t len,
util_debug_func_t function, void *user_data);
+
+static inline void put_uuid(const bt_uuid_t *src, void *dst)
+{
+ if (src->type == BT_UUID16)
+ put_unaligned(cpu_to_le16(src->value.u16), (uint16_t *) dst);
+ else if (src->type == BT_UUID32)
+ put_unaligned(cpu_to_le32(src->value.u32), (uint32_t *) dst);
+ else
+ htob128(&(src->value.u128), (uint128_t *) dst);
+}
--
1.8.3.1
Hi Johan,
On Thu, Mar 13, 2014 at 5:30 AM, Johan Hedberg <[email protected]> wrote:
> Hi Claudio,
>
> On Wed, Mar 12, 2014, Claudio Takahasi wrote:
>> +static inline void put_uuid(const bt_uuid_t *src, void *dst)
>> +{
>> + if (src->type == BT_UUID16)
>> + put_unaligned(cpu_to_le16(src->value.u16), (uint16_t *) dst);
>
> I thought you were going to add helpers like put_le16() to shared/util.h
> first?
My understanding of our discussion in the IRC was that put_uuid()
could be implemented inside src/gatt.c for now.
Anyway, I will send a new patchset moving put_uuid() and put_le16() to
src/shared/util.h
Regarding the remaining put_leX(), get_leX() (and related helpers) we
can start moving to src/shared/util.h after this patchset.
>
>> +static struct btd_attribute *new_const_attribute(const bt_uuid_t *type,
>> + const uint8_t *value,
>> + uint16_t len)
>> +{
>> + struct btd_attribute *attr = malloc0(sizeof(struct btd_attribute) +
>> + len);
>
> First of all I think it'd be better (coding style consistency-wise) to
> do the allocation separately from the declaration of the variable.
> Secondly, malloc0 can return NULL (unlike g_malloc0) so you'd need to
> check for that.
OK. I will fix it.
Claudio.
Hi Claudio,
On Wed, Mar 12, 2014, Claudio Takahasi wrote:
> --- a/src/gatt.c
> +++ b/src/gatt.c
> @@ -40,6 +40,9 @@
> static const bt_uuid_t primary_uuid = { .type = BT_UUID16,
> .value.u16 = GATT_PRIM_SVC_UUID };
>
> +static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
> + .value.u16 = GATT_CHARAC_UUID };
> +
> struct btd_attribute {
> uint16_t handle;
> bt_uuid_t type;
> @@ -50,6 +53,11 @@ struct btd_attribute {
> static GList *local_attribute_db;
> static uint16_t next_handle = 0x0001;
>
> +static inline void put_le16(uint16_t val, void *dst)
> +{
> + put_unaligned(cpu_to_le16(val), (uint16_t *) dst);
> +}
This helper should go to shared/util.h
Johan
Hi Claudio,
On Wed, Mar 12, 2014, Claudio Takahasi wrote:
> +static inline void put_uuid(const bt_uuid_t *src, void *dst)
> +{
> + if (src->type == BT_UUID16)
> + put_unaligned(cpu_to_le16(src->value.u16), (uint16_t *) dst);
I thought you were going to add helpers like put_le16() to shared/util.h
first?
> +static struct btd_attribute *new_const_attribute(const bt_uuid_t *type,
> + const uint8_t *value,
> + uint16_t len)
> +{
> + struct btd_attribute *attr = malloc0(sizeof(struct btd_attribute) +
> + len);
First of all I think it'd be better (coding style consistency-wise) to
do the allocation separately from the declaration of the variable.
Secondly, malloc0 can return NULL (unlike g_malloc0) so you'd need to
check for that.
Johan
This patch extends gatt-service tool, emitting PropertiesChanged for
"Value" when a new value is set. This flow represents the handling of
ATT Write Command or Write Request which is translated to a GDBusProxy
set property. PropertiesChanged will be tracked by the characteristic
GDBusProxy in order to detect "Value" changes.
---
tools/gatt-service.c | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index ad5399b..8dbecce 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -47,9 +47,11 @@
static GMainLoop *main_loop;
static GSList *services;
+static DBusConnection *connection;
struct characteristic {
char *uuid;
+ char *path;
uint8_t *value;
int vlen;
};
@@ -110,6 +112,8 @@ static void chr_set_value(const GDBusPropertyTable *property,
chr->vlen = len;
g_dbus_pending_property_success(id);
+ g_dbus_emit_property_changed(connection, chr->path,
+ GATT_CHR_IFACE, "Value");
}
static const GDBusPropertyTable chr_properties[] = {
@@ -163,6 +167,7 @@ static void chr_iface_destroy(gpointer user_data)
g_free(chr->uuid);
g_free(chr->value);
+ g_free(chr->path);
g_free(chr);
}
@@ -182,6 +187,7 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
chr->uuid = g_strdup(uuid);
chr->value = g_memdup(value, vlen);
chr->vlen = vlen;
+ chr->path = path;
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
@@ -190,18 +196,16 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
ret = -EIO;
}
- g_free(path);
-
return ret;
}
-static char *register_service(DBusConnection *conn, const char *uuid)
+static char *register_service(const char *uuid)
{
static int id = 1;
char *path;
path = g_strdup_printf("/service%d", id++);
- if (g_dbus_register_interface(conn, path, GATT_SERVICE_IFACE,
+ if (g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
NULL, NULL, service_properties,
g_strdup(uuid), g_free) == FALSE) {
printf("Couldn't register service interface\n");
@@ -212,13 +216,13 @@ static char *register_service(DBusConnection *conn, const char *uuid)
return path;
}
-static void create_services(DBusConnection *conn)
+static void create_services()
{
char *service_path;
uint8_t level = 0;
int ret;
- service_path = register_service(conn, IAS_UUID);
+ service_path = register_service(IAS_UUID);
if (service_path == NULL)
return;
@@ -226,11 +230,11 @@ static void create_services(DBusConnection *conn)
* According to the IAS SPEC, reading <<Alert level>> is not alloweed.
* "Value" is readable for testing purpose only.
*/
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ ret = register_characteristic(connection, ALERT_LEVEL_CHR_UUID, &level,
sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
- g_dbus_unregister_interface(conn, service_path,
+ g_dbus_unregister_interface(connection, service_path,
GATT_SERVICE_IFACE);
g_free(service_path);
return;
@@ -371,25 +375,24 @@ static guint setup_signalfd(void)
int main(int argc, char *argv[])
{
GDBusClient *client;
- DBusConnection *dbus_conn;
guint signal;
signal = setup_signalfd();
if (signal == 0)
return -errno;
- dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
main_loop = g_main_loop_new(NULL, FALSE);
- g_dbus_attach_object_manager(dbus_conn);
+ g_dbus_attach_object_manager(connection);
printf("gatt-service unique name: %s\n",
- dbus_bus_get_unique_name(dbus_conn));
+ dbus_bus_get_unique_name(connection));
- create_services(dbus_conn);
+ create_services();
- client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+ client = g_dbus_client_new(connection, "org.bluez", "/org/bluez");
g_dbus_client_set_connect_watch(client, connect_handler, NULL);
@@ -400,7 +403,7 @@ int main(int argc, char *argv[])
g_source_remove(signal);
g_slist_free_full(services, g_free);
- dbus_connection_unref(dbus_conn);
+ dbus_connection_unref(connection);
return 0;
}
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding a generic callback
to allow set the characteristic Value. It doesn't check for
characteristic properties yet.
---
tools/gatt-service.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 97dc8d4..ad5399b 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -35,6 +35,8 @@
#include <dbus/dbus.h>
#include <gdbus/gdbus.h>
+#include "src/error.h"
+
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
@@ -81,9 +83,38 @@ static gboolean chr_get_value(const GDBusPropertyTable *property,
return TRUE;
}
+static void chr_set_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+ uint8_t *value;
+ int len;
+
+ printf("Set('Value', ...)\n");
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ printf("Invalid value for Set('Value'...)\n");
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ g_free(chr->value);
+ chr->value = g_memdup(value, len);
+ chr->vlen = len;
+
+ g_dbus_pending_property_success(id);
+}
+
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
- { "Value", "ay", chr_get_value, NULL, NULL },
+ { "Value", "ay", chr_get_value, chr_set_value, NULL },
{ }
};
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding the callback to
allow reading the Value property. At the moment, it is a generic
callback and it doesn't consider characteristic value property
restrictions.
---
tools/gatt-service.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 53 insertions(+), 6 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index d1606ad..97dc8d4 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -46,18 +46,44 @@
static GMainLoop *main_loop;
static GSList *services;
+struct characteristic {
+ char *uuid;
+ uint8_t *value;
+ int vlen;
+};
+
static gboolean chr_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
- const char *uuid = user_data;
+ struct characteristic *chr = user_data;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid);
+
+ return TRUE;
+}
+
+static gboolean chr_get_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+
+ printf("Get(\"Value\")\n");
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &chr->value, chr->vlen);
+
+ dbus_message_iter_close_container(iter, &array);
return TRUE;
}
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
+ { "Value", "ay", chr_get_value, NULL, NULL },
{ }
};
@@ -100,18 +126,35 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static void chr_iface_destroy(gpointer user_data)
+{
+ struct characteristic *chr = user_data;
+
+ g_free(chr->uuid);
+ g_free(chr->value);
+ g_free(chr);
+}
+
static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const uint8_t *value, int vlen,
const char *service_path)
{
+ struct characteristic *chr;
static int id = 1;
char *path;
int ret = 0;
path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+ chr = g_new0(struct characteristic, 1);
+
+ chr->uuid = g_strdup(uuid);
+ chr->value = g_memdup(value, vlen);
+ chr->vlen = vlen;
+
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
- g_strdup(uuid), g_free) == FALSE) {
+ chr, chr_iface_destroy) == FALSE) {
printf("Couldn't register characteristic interface\n");
ret = -EIO;
}
@@ -141,15 +184,19 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ uint8_t level = 0;
int ret;
service_path = register_service(conn, IAS_UUID);
if (service_path == NULL)
return;
- /* Add Alert Level Characteristic to Immediate Alert Service */
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
- service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service
+ * According to the IAS SPEC, reading <<Alert level>> is not alloweed.
+ * "Value" is readable for testing purpose only.
+ */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
g_dbus_unregister_interface(conn, service_path,
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch registers the Alert Level characteristic object related to
Immediate Alert Service. It adds the characteristic UUID property only.
---
tools/gatt-service.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index cba0ff3..d1606ad 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -37,13 +37,30 @@
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
+#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
/* Immediate Alert Service UUID */
#define IAS_UUID "00001802-0000-1000-8000-00805f9b34fb"
+#define ALERT_LEVEL_CHR_UUID "00002a06-0000-1000-8000-00805f9b34fb"
static GMainLoop *main_loop;
static GSList *services;
+static gboolean chr_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *uuid = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable chr_properties[] = {
+ { "UUID", "s", chr_get_uuid },
+ { }
+};
+
static gboolean service_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
@@ -83,6 +100,27 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const char *service_path)
+{
+ static int id = 1;
+ char *path;
+ int ret = 0;
+
+ path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+
+ if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
+ NULL, NULL, chr_properties,
+ g_strdup(uuid), g_free) == FALSE) {
+ printf("Couldn't register characteristic interface\n");
+ ret = -EIO;
+ }
+
+ g_free(path);
+
+ return ret;
+}
+
static char *register_service(DBusConnection *conn, const char *uuid)
{
static int id = 1;
@@ -103,11 +141,24 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ int ret;
service_path = register_service(conn, IAS_UUID);
+ if (service_path == NULL)
+ return;
- services = g_slist_prepend(services, service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
+ service_path);
+ if (ret < 0) {
+ printf("Couldn't register Alert Level characteristic (IAS)\n");
+ g_dbus_unregister_interface(conn, service_path,
+ GATT_SERVICE_IFACE);
+ g_free(service_path);
+ return;
+ }
+ services = g_slist_prepend(services, service_path);
printf("Registered service: %s\n", service_path);
}
--
1.8.3.1
When the characteristic requires Write Request, the ATT response should
be sent after receiving the reply for the Set method call.
---
src/gatt-dbus.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 61 insertions(+), 6 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index d703355..2412e2d 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,11 @@ struct external_app {
unsigned int watch;
};
+struct proxy_write_data {
+ btd_attr_write_result_t result_cb;
+ void *user_data;
+};
+
/*
* Attribute to Proxy hash table. Used to map incoming
* ATT operations to its external characteristic proxy.
@@ -181,8 +186,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
static void proxy_write_reply(const DBusError *derr, void *user_data)
{
- if (derr)
- DBG("Write reply: %s", derr->message);
+ struct proxy_write_data *wdata = user_data;
+ int err = 0;
+
+ /*
+ * Security requirements shall be handled by the core. If external
+ * applications returns an error, the reasons will be restricted to
+ * invalid argument or application specific errors.
+ */
+
+ if (dbus_error_is_set(derr) == FALSE)
+ goto done;
+
+ DBG("Write reply: %s", derr->message);
+
+ if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
+ err = ETIMEDOUT;
+ else if (dbus_error_has_name(derr, ERROR_INTERFACE ".InvalidArguments"))
+ err = EINVAL;
+ else
+ err = EPROTO;
+
+done:
+ if (wdata && wdata->result_cb)
+ wdata->result_cb(err, wdata->user_data);
}
static void proxy_write_cb(struct btd_attribute *attr,
@@ -193,16 +220,44 @@ static void proxy_write_cb(struct btd_attribute *attr,
GDBusProxy *proxy;
proxy = g_hash_table_lookup(proxy_hash, attr);
- if (proxy == NULL)
- /* FIXME: Attribute not found */
+ if (proxy == NULL) {
+ result(ENOENT, user_data);
return;
+ }
- g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ /*
+ * "result" callback defines if the core wants to receive the
+ * operation result, allowing to select ATT Write Request or Write
+ * Command. Descriptors requires Write Request operation. For
+ * Characteristics, the implementation will define which operations
+ * are allowed based on the properties/flags.
+ * TODO: Write Long Characteristics/Descriptors.
+ */
+
+ if (result) {
+ struct proxy_write_data *wdata;
+
+ wdata = g_new0(struct proxy_write_data, 1);
+ wdata->result_cb = result;
+ wdata->user_data = user_data;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ wdata, g_free);
+ } else {
+ /*
+ * Caller is not interested in the Set method call result.
+ * This flow implements the ATT Write Command scenario, where
+ * the remote doesn't receive ATT response.
+ */
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
value, len, proxy_write_reply,
NULL, NULL);
+ }
- DBG("Server: Write characteristic callback %s",
+ DBG("Server: Write attribute callback %s",
g_dbus_proxy_get_path(proxy));
+
}
static int register_external_service(const struct external_app *eapp,
--
1.8.3.1
This patch adds an extra callback to inform the result of the write
operation on an external entity to allow the core to reply properly
for the ATT request.
---
src/gatt-dbus.c | 4 +++-
src/gatt.h | 5 ++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 7e81dc6..d703355 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -186,7 +186,9 @@ static void proxy_write_reply(const DBusError *derr, void *user_data)
}
static void proxy_write_cb(struct btd_attribute *attr,
- const uint8_t *value, size_t len)
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data)
{
GDBusProxy *proxy;
diff --git a/src/gatt.h b/src/gatt.h
index f4eebe5..ed5fe80 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -48,8 +48,11 @@ typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
* @err: error in errno format.
* @user_data: user_data passed in btd_attr_write_t callback
*/
+typedef void (*btd_attr_write_result_t) (int err, void *user_data);
typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
- const uint8_t *value, size_t len);
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
--
1.8.3.1
This patch adds the callback for writing the external characteristic
value. Internally, GDBusProxy implementation calls Set method of the
"Value" property. At the moment, potential errors returned by the
external service implementation are being ignored.
The ATT operation (Write Command or Request) is not exposed to the
external service implementation. GATT/GDBusProxy handles possible
DBus errors returned, and notify the core using the callbacks informed.
Write Command doesn't have callback since it doesn't return ATT errors.
---
src/gatt-dbus.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index a02d565..7e81dc6 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -179,6 +179,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
result(0, value, len, user_data);
}
+static void proxy_write_reply(const DBusError *derr, void *user_data)
+{
+ if (derr)
+ DBG("Write reply: %s", derr->message);
+}
+
+static void proxy_write_cb(struct btd_attribute *attr,
+ const uint8_t *value, size_t len)
+{
+ GDBusProxy *proxy;
+
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL)
+ /* FIXME: Attribute not found */
+ return;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ NULL, NULL);
+
+ DBG("Server: Write characteristic callback %s",
+ g_dbus_proxy_get_path(proxy));
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -238,7 +262,8 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb,
+ proxy_write_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This patch adds a new gdbus utility function to allow setting a property
of fixed, and non-fixed values array.
---
gdbus/client.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdbus/gdbus.h | 5 ++++
2 files changed, 90 insertions(+)
diff --git a/gdbus/client.c b/gdbus/client.c
index 5193b6c..6fea3d9 100644
--- a/gdbus/client.c
+++ b/gdbus/client.c
@@ -727,6 +727,91 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
return TRUE;
}
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ int size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy)
+{
+ struct set_property_data *data;
+ GDBusClient *client;
+ DBusMessage *msg;
+ DBusMessageIter iter, variant, array;
+ DBusPendingCall *call;
+ char array_sig[3];
+ char type_sig[2];
+
+ if (proxy == NULL || name == NULL || value == NULL)
+ return FALSE;
+
+ if (dbus_type_is_basic(type) == FALSE)
+ return FALSE;
+
+ client = proxy->client;
+ if (client == NULL)
+ return FALSE;
+
+ data = g_try_new0(struct set_property_data, 1);
+ if (data == NULL)
+ return FALSE;
+
+ data->function = function;
+ data->user_data = user_data;
+ data->destroy = destroy;
+
+ msg = dbus_message_new_method_call(client->service_name,
+ proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set");
+ if (msg == NULL) {
+ g_free(data);
+ return FALSE;
+ }
+
+ array_sig[0] = DBUS_TYPE_ARRAY;
+ array_sig[1] = (char) type;
+ array_sig[2] = '\0';
+
+ type_sig[0] = (char) type;
+ type_sig[1] = '\0';
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &proxy->interface);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ array_sig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ type_sig, &array);
+
+ if (dbus_type_is_fixed(type) == TRUE)
+ dbus_message_iter_append_fixed_array(&array, type,
+ &value, size);
+ else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+ int i;
+ const char **str = (const char **) value;
+
+ for (i = 0; i < size; i++)
+ dbus_message_iter_append_basic(&array, type, &str[i]);
+ }
+
+ dbus_message_iter_close_container(&variant, &array);
+ dbus_message_iter_close_container(&iter, &variant);
+
+ if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+ &call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ g_free(data);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
struct method_call_data {
GDBusReturnFunction function;
void *user_data;
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 8ada200..40a5abe 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -329,6 +329,11 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
GDBusResultFunction function, void *user_data,
GDBusDestroyFunction destroy);
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ int size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy);
+
typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data);
typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data);
--
1.8.3.1
This patch adds a function callback for write operations. When a remote
device writes to a given attribute, the core calls the specified write
callback informing the attribute server the new characteristic value.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 ++++---
src/gatt.h | 13 ++++++++++++-
3 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index fff2d3a..a02d565 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -238,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 634e23a..f7f7f2e 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -47,6 +47,7 @@ struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
btd_attr_read_t read_cb;
+ btd_attr_write_t write_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -131,7 +132,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb)
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -184,8 +186,7 @@ struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
char_value = new0(struct btd_attribute, 1);
char_value->type = *uuid;
char_value->read_cb = read_cb;
-
- /* TODO: Write callbacks */
+ char_value->write_cb = write_cb;
if (local_database_add(next_handle, char_value) < 0)
goto fail;
diff --git a/src/gatt.h b/src/gatt.h
index e446fa0..f4eebe5 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -42,6 +42,14 @@ typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
btd_attr_read_result_t result,
void *user_data);
+/*
+ * Callbacks from this type are called once the value from the attribute was
+ * written.
+ * @err: error in errno format.
+ * @user_data: user_data passed in btd_attr_write_t callback
+ */
+typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
+ const uint8_t *value, size_t len);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
@@ -57,10 +65,13 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
* @read_cb: Callback used to provide the characteristic value.
+ * @write_cb: Callback called to notify the implementation that a new value
+ * is available.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
uint8_t properties,
- btd_attr_read_t read_cb);
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb);
--
1.8.3.1
This patch adds the callback for reading the external characteristic
Value. Internally, GDBusProxy implementation tracks all properties Value
changes consequently Value can be ready directly from the proxy without
additional method calls.
---
src/gatt-dbus.c | 42 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 275b263..fff2d3a 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -139,6 +139,46 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
eapp->proxies = g_slist_remove(eapp->proxies, proxy);
}
+static void proxy_read_cb(struct btd_attribute *attr,
+ btd_attr_read_result_t result, void *user_data)
+{
+ DBusMessageIter iter, array;
+ GDBusProxy *proxy;
+ uint8_t *value;
+ int len;
+
+ /*
+ * Remote device is trying to read the informed attribute,
+ * "Value" should be read from the proxy. GDBusProxy tracks
+ * properties changes automatically, it is not necessary to
+ * get the value directly from the GATT server.
+ */
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL) {
+ result(ENOENT, NULL, 0, user_data);
+ return;
+ }
+
+ if (!g_dbus_proxy_get_property(proxy, "Value", &iter)) {
+ /* Unusual situation, read property will checked earlier */
+ result(EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ DBG("External service inconsistent!");
+ result(EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ DBG("attribute: %p read %d bytes", attr, len);
+
+ result(0, value, len, user_data);
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -198,7 +238,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This patch adds a function callback for read operations. When a remote
device wants to reads a given attribute, the core calls the specified
read callback to get the value from the external services.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 +++++--
src/gatt.h | 20 +++++++++++++++++++-
3 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index f60260e..275b263 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -198,7 +198,7 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00);
+ attr = btd_gatt_add_char(&uuid, 0x00, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 669c716..634e23a 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -46,6 +46,7 @@ static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
+ btd_attr_read_t read_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -129,7 +130,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
}
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
- uint8_t properties)
+ uint8_t properties,
+ btd_attr_read_t read_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -181,8 +183,9 @@ struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
char_value = new0(struct btd_attribute, 1);
char_value->type = *uuid;
+ char_value->read_cb = read_cb;
- /* TODO: Read & Write callbacks */
+ /* TODO: Write callbacks */
if (local_database_add(next_handle, char_value) < 0)
goto fail;
diff --git a/src/gatt.h b/src/gatt.h
index d7acc5b..e446fa0 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -27,6 +27,22 @@ void gatt_init(void);
void gatt_cleanup(void);
+/*
+ * Callbacks of this type are called once the value from the attribute is
+ * ready to be read from the service implementation. Result callback is
+ * the asynchronous function that should be used to inform the caller
+ * the read value.
+ * @err: error in errno format.
+ * @value: pointer to value
+ * @len: length of value
+ * @user_data: user_data passed in btd_attr_read_t callback
+ */
+typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
+ void *user_data);
+typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
+ btd_attr_read_result_t result,
+ void *user_data);
+
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
*
@@ -40,9 +56,11 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* to local attribute database.
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
+ * @read_cb: Callback used to provide the characteristic value.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
- uint8_t properties);
+ uint8_t properties,
+ btd_attr_read_t read_cb);
--
1.8.3.1
This commits adds a hash table to map attributes into GDBusProxy,
allowing to call remote objects for reading and writing values.
---
src/gatt-dbus.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 9f94c4f..f60260e 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,12 @@ struct external_app {
unsigned int watch;
};
+/*
+ * Attribute to Proxy hash table. Used to map incoming
+ * ATT operations to its external characteristic proxy.
+ */
+static GHashTable *proxy_hash;
+
static GSList *external_apps;
static int external_app_path_cmp(gconstpointer a, gconstpointer b)
@@ -172,6 +178,7 @@ static int register_external_characteristics(GSList *proxies)
DBusMessageIter iter;
const char *str, *path;
bt_uuid_t uuid;
+ struct btd_attribute *attr;
GDBusProxy *proxy = list->data;
if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
@@ -191,11 +198,14 @@ static int register_external_characteristics(GSList *proxies)
* Reference table 3.5: Characteristic Properties bit field.
*/
- if (btd_gatt_add_char(&uuid, 0x00) == NULL)
+ attr = btd_gatt_add_char(&uuid, 0x00);
+ if (attr == NULL)
return -EINVAL;
path = g_dbus_proxy_get_path(proxy);
DBG("Added GATT CHR: %s (%s)", path, str);
+
+ g_hash_table_insert(proxy_hash, attr, g_dbus_proxy_ref(proxy));
}
return 0;
@@ -318,13 +328,22 @@ static const GDBusMethodTable methods[] = {
gboolean gatt_dbus_manager_register(void)
{
- return g_dbus_register_interface(btd_get_dbus_connection(),
- "/org/bluez", GATT_MGR_IFACE,
- methods, NULL, NULL, NULL, NULL);
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ "/org/bluez", GATT_MGR_IFACE,
+ methods, NULL, NULL, NULL, NULL) == FALSE)
+ return FALSE;
+
+ proxy_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) g_dbus_proxy_unref);
+
+ return TRUE;
}
void gatt_dbus_manager_unregister(void)
{
+ g_hash_table_destroy(proxy_hash);
+ proxy_hash = NULL;
+
g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez",
GATT_MGR_IFACE);
}
--
1.8.3.1
From: Andre Guedes <[email protected]>
This patch adds btd_gatt_add_char() helper. It creates and adds the
Characteristic declaration, and Characteristic value attributes to the
local attribute database.
---
src/gatt.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/gatt.h | 12 +++++++++
2 files changed, 98 insertions(+)
diff --git a/src/gatt.c b/src/gatt.c
index eed4fae..669c716 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -40,6 +40,9 @@
static const bt_uuid_t primary_uuid = { .type = BT_UUID16,
.value.u16 = GATT_PRIM_SVC_UUID };
+static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
+ .value.u16 = GATT_CHARAC_UUID };
+
struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
@@ -50,6 +53,11 @@ struct btd_attribute {
static GList *local_attribute_db;
static uint16_t next_handle = 0x0001;
+static inline void put_le16(uint16_t val, void *dst)
+{
+ put_unaligned(cpu_to_le16(val), (uint16_t *) dst);
+}
+
static inline void put_uuid(const bt_uuid_t *src, void *dst)
{
if (src->type == BT_UUID16)
@@ -120,6 +128,84 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
return attr;
}
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+ uint8_t properties)
+{
+ struct btd_attribute *char_decl, *char_value = NULL;
+
+ /* Attribute value length */
+ uint16_t len = 1 + 2 + bt_uuid_len(uuid);
+ uint8_t value[len];
+
+ /*
+ * Characteristic DECLARATION
+ *
+ * TYPE ATTRIBUTE VALUE
+ * +-------+---------------------------------+
+ * |0x2803 | 0xXX 0xYYYY 0xZZZZ... |
+ * | (1) | (2) (3) (4) |
+ * +------+----------------------------------+
+ * (1) - 2 octets: Characteristic declaration UUID
+ * (2) - 1 octet : Properties
+ * (3) - 2 octets: Handle of the characteristic Value
+ * (4) - 2 or 16 octets: Characteristic UUID
+ */
+
+ value[0] = properties;
+
+ /*
+ * Since we don't know yet the characteristic value attribute
+ * handle, we skip and set it later.
+ */
+
+ put_uuid(uuid, &value[3]);
+
+ char_decl = new_const_attribute(&chr_uuid, value, len);
+ if (local_database_add(next_handle, char_decl) < 0)
+ goto fail;
+
+ next_handle = next_handle + 1;
+
+ /*
+ * Characteristic VALUE
+ *
+ * TYPE ATTRIBUTE VALUE
+ * +----------+---------------------------------+
+ * |0xZZZZ... | 0x... |
+ * | (1) | (2) |
+ * +----------+---------------------------------+
+ * (1) - 2 or 16 octets: Characteristic UUID
+ * (2) - N octets: Value is read dynamically from the service
+ * implementation (external entity).
+ */
+
+ char_value = new0(struct btd_attribute, 1);
+ char_value->type = *uuid;
+
+ /* TODO: Read & Write callbacks */
+
+ if (local_database_add(next_handle, char_value) < 0)
+ goto fail;
+
+ next_handle = next_handle + 1;
+
+ /*
+ * Update characteristic value handle in characteristic declaration
+ * attribute. For local attributes, we can assume that the handle
+ * representing the characteristic value will get the next available
+ * handle. However, for remote attribute this assumption is not valid.
+ */
+ put_le16(char_value->handle, &char_decl->value[1]);
+
+ return char_value;
+
+fail:
+ free(char_decl);
+ free(char_value);
+
+ return NULL;
+}
+
void gatt_init(void)
{
DBG("Starting GATT server");
diff --git a/src/gatt.h b/src/gatt.h
index 8dd1312..d7acc5b 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -34,3 +34,15 @@ void gatt_cleanup(void);
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
+
+/*
+ * btd_gatt_add_char - Add a characteristic (declaration and value attributes)
+ * to local attribute database.
+ * @uuid: Characteristic UUID (16-bits or 128-bits).
+ * @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
+ *
+ * Returns a reference to characteristic value attribute. In case of error,
+ * NULL is returned.
+ */
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+ uint8_t properties);
--
1.8.3.1
Initial support for GATT characteristics. This patch adds the
characteristic declaration attribute to the GATT local database based
on the fetched GDBusProxy objects.
---
src/gatt-dbus.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 3fbff87..9f94c4f 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -163,6 +163,44 @@ static int register_external_service(const struct external_app *eapp,
return 0;
}
+static int register_external_characteristics(GSList *proxies)
+
+{
+ GSList *list;
+
+ for (list = proxies; list; list = g_slist_next(proxies)) {
+ DBusMessageIter iter;
+ const char *str, *path;
+ bt_uuid_t uuid;
+ GDBusProxy *proxy = list->data;
+
+ if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+ return -EINVAL;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&iter, &str);
+
+ if (bt_string_to_uuid(&uuid, str) < 0)
+ return -EINVAL;
+
+ /*
+ * TODO: Missing Flags/property
+ * Add properties according to Core SPEC 4.1 page 2183.
+ * Reference table 3.5: Characteristic Properties bit field.
+ */
+
+ if (btd_gatt_add_char(&uuid, 0x00) == NULL)
+ return -EINVAL;
+
+ path = g_dbus_proxy_get_path(proxy);
+ DBG("Added GATT CHR: %s (%s)", path, str);
+ }
+
+ return 0;
+}
+
static void client_ready(GDBusClient *client, void *user_data)
{
struct external_app *eapp = user_data;
@@ -177,6 +215,9 @@ static void client_ready(GDBusClient *client, void *user_data)
if (register_external_service(eapp, proxy) < 0)
goto fail;
+ if (register_external_characteristics(g_slist_next(eapp->proxies)) < 0)
+ goto fail;
+
DBG("Added GATT service %s", eapp->path);
reply = dbus_message_new_method_return(eapp->reg);
@@ -186,7 +227,7 @@ fail:
error("Could not register external service: %s", eapp->path);
reply = btd_error_invalid_args(eapp->reg);
- /* TODO: missing eapp cleanup */
+ /* TODO: missing eapp/database cleanup */
reply:
dbus_message_unref(eapp->reg);
--
1.8.3.1
From: Andre Guedes <[email protected]>
This patch adds a helper function to create attribute with static
values. It is intended to be used to create GATT services, and
characteristic declarations.
---
src/gatt.c | 41 +++++++++++++++++++++++++++++++++++------
1 file changed, 35 insertions(+), 6 deletions(-)
diff --git a/src/gatt.c b/src/gatt.c
index f7b74d6..eed4fae 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -26,10 +26,12 @@
#endif
#include <glib.h>
+#include <bluetooth/bluetooth.h>
#include "log.h"
#include "lib/uuid.h"
#include "attrib/att.h"
+#include "src/shared/util.h"
#include "gatt-dbus.h"
#include "gatt.h"
@@ -48,6 +50,33 @@ struct btd_attribute {
static GList *local_attribute_db;
static uint16_t next_handle = 0x0001;
+static inline void put_uuid(const bt_uuid_t *src, void *dst)
+{
+ if (src->type == BT_UUID16)
+ put_unaligned(cpu_to_le16(src->value.u16), (uint16_t *) dst);
+ else
+ htob128(&(src->value.u128), (uint128_t *) dst);
+}
+
+/*
+ * Helper function to create new attributes containing constant/static values.
+ * eg: declaration of services/characteristics, and characteristics with
+ * fixed values.
+ */
+static struct btd_attribute *new_const_attribute(const bt_uuid_t *type,
+ const uint8_t *value,
+ uint16_t len)
+{
+ struct btd_attribute *attr = malloc0(sizeof(struct btd_attribute) +
+ len);
+
+ attr->type = *type;
+ memcpy(&attr->value, value, len);
+ attr->value_len = len;
+
+ return attr;
+}
+
static int local_database_add(uint16_t handle, struct btd_attribute *attr)
{
attr->handle = handle;
@@ -59,9 +88,9 @@ static int local_database_add(uint16_t handle, struct btd_attribute *attr)
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
{
+ struct btd_attribute *attr;
uint16_t len = bt_uuid_len(uuid);
- struct btd_attribute *attr = g_malloc0(sizeof(struct btd_attribute) +
- len);
+ uint8_t value[len];
/*
* Service DECLARATION
@@ -75,13 +104,13 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
* (2) - 2 or 16 octets: Service UUID
*/
- attr->type = primary_uuid;
+ /* Set attribute value */
+ put_uuid(uuid, value);
- att_put_uuid(*uuid, attr->value);
- attr->value_len = len;
+ attr = new_const_attribute(&primary_uuid, value, len);
if (local_database_add(next_handle, attr) < 0) {
- g_free(attr);
+ free(attr);
return NULL;
}
--
1.8.3.1
This patchset adds the following features:
* register external characteristics
* basic read or write external characteristics
Usage of glib is being avoided inside src/gatt.c. Properties/flags and
32-bit UUIDs are not implemented yet.
Changes from v0 to v1:
* Use LE <-> CPU helpers defined in src/shared/util.h
* remove patch "gatt: Add helper for reading characteristics" from
this set.
How to test:
It is possible to test the implemented features using a modified
gatttool version. Unix socket is used as transport to allow basic
ATT operations:
git://git.infradead.org/users/cktakahasi/bluez.git gatt-api-forupstream-group2-patchv1-testing
$gatttool -I -L
[LOCAL][LE]> connect
Local connection
Connection successful
[LOCAL][LE]> primary
attr handle: 0x0001, end grp handle: 0x0003 uuid: 000034fb-0000-1000-8000-00805f9b34fb
[LOCAL][LE]> characteristics
handle: 0x0002, char properties: 0x00, char value handle: 0x0003, uuid: 00002a06-0000-1000-8000-00805f9b34fb
[LOCAL][LE]> char-read-hnd 0x0003
Characteristic value/descriptor: 00
[LOCAL][LE]> char-write-req 0x0003 02
Characteristic value was written successfully
[LOCAL][LE]> char-read-hnd 0x0003
Characteristic value/descriptor: 02
Alvaro Silva (3):
tools: Add Alert Level characteristic to gatt-service
tools: Add reading Value property of gatt-service
tools: Add setting Value property of gatt-service
Andre Guedes (2):
gatt: Add function to create constant attributes
gatt: Add helper for creating GATT characteristics
Claudio Takahasi (10):
gatt: Add characteristic to the database
gatt: Add hash table of GDBusProxy objects
gatt: Add read callback to btd_gatt_add_char helper
gatt: Assign read callback for external services
gatt: Add write callback to btd_gatt_add_char helper
gdbus: Add g_dbus_proxy_set_property_array
gatt: Assign write callback for external services
gatt: Add result callback for Write Request
gatt: Add Write Request handling for GDBusProxy
tools: Emit property changed when Value changes
gdbus/client.c | 85 +++++++++++++++++++++++
gdbus/gdbus.h | 5 ++
src/gatt-dbus.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++--
src/gatt.c | 131 +++++++++++++++++++++++++++++++++--
src/gatt.h | 44 ++++++++++++
tools/gatt-service.c | 156 ++++++++++++++++++++++++++++++++++++++----
6 files changed, 589 insertions(+), 22 deletions(-)
--
1.8.3.1
Initial support for GATT characteristics. This patch adds the
characteristic declaration attribute to the GATT local database based
on the fetched GDBusProxy objects.
---
src/gatt-dbus.c | 38 +++++++++++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 3fbff87..8cf222d 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -163,6 +163,39 @@ static int register_external_service(const struct external_app *eapp,
return 0;
}
+static int register_external_characteristics(GSList *proxies)
+
+{
+ GSList *list;
+
+ for (list = proxies; list; list = g_slist_next(proxies)) {
+ DBusMessageIter iter;
+ const char *str, *path;
+ bt_uuid_t uuid;
+ GDBusProxy *proxy = list->data;
+
+ if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+ return -EINVAL;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&iter, &str);
+
+ if (bt_string_to_uuid(&uuid, str) < 0)
+ return -EINVAL;
+
+ /* TODO: Missing Flags/property */
+ if (btd_gatt_add_char(&uuid, 0x00) == NULL)
+ return -EINVAL;
+
+ path = g_dbus_proxy_get_path(proxy);
+ DBG("Added GATT CHR: %s (%s)", path, str);
+ }
+
+ return 0;
+}
+
static void client_ready(GDBusClient *client, void *user_data)
{
struct external_app *eapp = user_data;
@@ -177,6 +210,9 @@ static void client_ready(GDBusClient *client, void *user_data)
if (register_external_service(eapp, proxy) < 0)
goto fail;
+ if (register_external_characteristics(g_slist_next(eapp->proxies)) < 0)
+ goto fail;
+
DBG("Added GATT service %s", eapp->path);
reply = dbus_message_new_method_return(eapp->reg);
@@ -186,7 +222,7 @@ fail:
error("Could not register external service: %s", eapp->path);
reply = btd_error_invalid_args(eapp->reg);
- /* TODO: missing eapp cleanup */
+ /* TODO: missing eapp/database cleanup */
reply:
dbus_message_unref(eapp->reg);
--
1.8.3.1
This patch extends gatt-service tool, emitting PropertiesChanged for
"Value" when a new value is set. This flow represents the handling of
ATT Write Command or Write Request which is translated to a GDBusProxy
set property. PropertiesChanged will be tracked by the characteristic
GDBusProxy in order to detect "Value" changes.
---
tools/gatt-service.c | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 481495c..5878dc3 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -47,9 +47,11 @@
static GMainLoop *main_loop;
static GSList *services;
+static DBusConnection *connection;
struct characteristic {
char *uuid;
+ char *path;
uint8_t *value;
int vlen;
};
@@ -110,6 +112,8 @@ static void chr_set_value(const GDBusPropertyTable *property,
chr->vlen = len;
g_dbus_pending_property_success(id);
+ g_dbus_emit_property_changed(connection, chr->path,
+ GATT_CHR_IFACE, "Value");
}
static const GDBusPropertyTable chr_properties[] = {
@@ -163,6 +167,7 @@ static void chr_iface_destroy(gpointer user_data)
g_free(chr->uuid);
g_free(chr->value);
+ g_free(chr->path);
g_free(chr);
}
@@ -183,6 +188,7 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
chr->value = g_memdup(value, vlen);
chr->vlen = vlen;
+ chr->path = path;
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
@@ -191,18 +197,16 @@ static int register_characteristic(DBusConnection *conn, const char *uuid,
ret = -EIO;
}
- g_free(path);
-
return ret;
}
-static char *register_service(DBusConnection *conn, const char *uuid)
+static char *register_service(const char *uuid)
{
static int id = 1;
char *path;
path = g_strdup_printf("/service%d", id++);
- if (g_dbus_register_interface(conn, path, GATT_SERVICE_IFACE,
+ if (g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
NULL, NULL, service_properties,
g_strdup(uuid), g_free) == FALSE) {
printf("Couldn't register service interface\n");
@@ -213,13 +217,13 @@ static char *register_service(DBusConnection *conn, const char *uuid)
return path;
}
-static void create_services(DBusConnection *conn)
+static void create_services()
{
char *service_path;
uint8_t level = 0;
int ret;
- service_path = register_service(conn, IAS_UUID);
+ service_path = register_service(IAS_UUID);
if (service_path == NULL)
return;
@@ -227,11 +231,11 @@ static void create_services(DBusConnection *conn)
* According to the IAS SPEC, reading <<Alert level>> is not alloweed.
* "Value" is readable for testing purpose only.
*/
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ ret = register_characteristic(connection, ALERT_LEVEL_CHR_UUID, &level,
sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
- g_dbus_unregister_interface(conn, service_path,
+ g_dbus_unregister_interface(connection, service_path,
GATT_SERVICE_IFACE);
g_free(service_path);
return;
@@ -372,25 +376,24 @@ static guint setup_signalfd(void)
int main(int argc, char *argv[])
{
GDBusClient *client;
- DBusConnection *dbus_conn;
guint signal;
signal = setup_signalfd();
if (signal == 0)
return -errno;
- dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
main_loop = g_main_loop_new(NULL, FALSE);
- g_dbus_attach_object_manager(dbus_conn);
+ g_dbus_attach_object_manager(connection);
printf("gatt-service unique name: %s\n",
- dbus_bus_get_unique_name(dbus_conn));
+ dbus_bus_get_unique_name(connection));
- create_services(dbus_conn);
+ create_services();
- client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+ client = g_dbus_client_new(connection, "org.bluez", "/org/bluez");
g_dbus_client_set_connect_watch(client, connect_handler, NULL);
@@ -401,7 +404,7 @@ int main(int argc, char *argv[])
g_source_remove(signal);
g_slist_free_full(services, g_free);
- dbus_connection_unref(dbus_conn);
+ dbus_connection_unref(connection);
return 0;
}
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding a generic callback
to allow set the characteristic Value. It doesn't check for
characteristic properties yet.
---
tools/gatt-service.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 25528ca..481495c 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -35,6 +35,8 @@
#include <dbus/dbus.h>
#include <gdbus/gdbus.h>
+#include "src/error.h"
+
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
@@ -81,9 +83,38 @@ static gboolean chr_get_value(const GDBusPropertyTable *property,
return TRUE;
}
+static void chr_set_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+ uint8_t *value;
+ int len;
+
+ printf("Set('Value', ...)\n");
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ printf("Invalid value for Set('Value'...)\n");
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ g_free(chr->value);
+ chr->value = g_memdup(value, len);
+ chr->vlen = len;
+
+ g_dbus_pending_property_success(id);
+}
+
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
- { "Value", "ay", chr_get_value, NULL, NULL },
+ { "Value", "ay", chr_get_value, chr_set_value, NULL },
{ }
};
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch extends the gatt-service.c example adding the callback to
allow reading the Value property. At the moment, it is a generic
callback and it doesn't consider characteristic value property
restrictions.
---
tools/gatt-service.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 54 insertions(+), 6 deletions(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index d1606ad..25528ca 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -46,18 +46,44 @@
static GMainLoop *main_loop;
static GSList *services;
+struct characteristic {
+ char *uuid;
+ uint8_t *value;
+ int vlen;
+};
+
static gboolean chr_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
- const char *uuid = user_data;
+ struct characteristic *chr = user_data;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid);
+
+ return TRUE;
+}
+
+static gboolean chr_get_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct characteristic *chr = user_data;
+ DBusMessageIter array;
+
+ printf("Get(\"Value\")\n");
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &chr->value, chr->vlen);
+
+ dbus_message_iter_close_container(iter, &array);
return TRUE;
}
static const GDBusPropertyTable chr_properties[] = {
{ "UUID", "s", chr_get_uuid },
+ { "Value", "ay", chr_get_value, NULL, NULL },
{ }
};
@@ -100,18 +126,36 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static void chr_iface_destroy(gpointer user_data)
+{
+ struct characteristic *chr = user_data;
+
+ g_free(chr->uuid);
+ g_free(chr->value);
+ g_free(chr);
+}
+
static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const uint8_t *value, int vlen,
const char *service_path)
{
+ struct characteristic *chr;
static int id = 1;
char *path;
int ret = 0;
path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+ chr = g_new0(struct characteristic, 1);
+
+ chr->uuid = g_strdup(uuid);
+
+ chr->value = g_memdup(value, vlen);
+ chr->vlen = vlen;
+
if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
NULL, NULL, chr_properties,
- g_strdup(uuid), g_free) == FALSE) {
+ chr, chr_iface_destroy) == FALSE) {
printf("Couldn't register characteristic interface\n");
ret = -EIO;
}
@@ -141,15 +185,19 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ uint8_t level = 0;
int ret;
service_path = register_service(conn, IAS_UUID);
if (service_path == NULL)
return;
- /* Add Alert Level Characteristic to Immediate Alert Service */
- ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
- service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service
+ * According to the IAS SPEC, reading <<Alert level>> is not alloweed.
+ * "Value" is readable for testing purpose only.
+ */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID, &level,
+ sizeof(level), service_path);
if (ret < 0) {
printf("Couldn't register Alert Level characteristic (IAS)\n");
g_dbus_unregister_interface(conn, service_path,
--
1.8.3.1
From: Alvaro Silva <[email protected]>
This patch registers the Alert Level characteristic object related to
Immediate Alert Service. It adds the characteristic UUID property only.
---
tools/gatt-service.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index cba0ff3..d1606ad 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -37,13 +37,30 @@
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
+#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
/* Immediate Alert Service UUID */
#define IAS_UUID "00001802-0000-1000-8000-00805f9b34fb"
+#define ALERT_LEVEL_CHR_UUID "00002a06-0000-1000-8000-00805f9b34fb"
static GMainLoop *main_loop;
static GSList *services;
+static gboolean chr_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *uuid = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable chr_properties[] = {
+ { "UUID", "s", chr_get_uuid },
+ { }
+};
+
static gboolean service_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
@@ -83,6 +100,27 @@ static const GDBusPropertyTable service_properties[] = {
{ }
};
+static int register_characteristic(DBusConnection *conn, const char *uuid,
+ const char *service_path)
+{
+ static int id = 1;
+ char *path;
+ int ret = 0;
+
+ path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+
+ if (g_dbus_register_interface(conn, path, GATT_CHR_IFACE,
+ NULL, NULL, chr_properties,
+ g_strdup(uuid), g_free) == FALSE) {
+ printf("Couldn't register characteristic interface\n");
+ ret = -EIO;
+ }
+
+ g_free(path);
+
+ return ret;
+}
+
static char *register_service(DBusConnection *conn, const char *uuid)
{
static int id = 1;
@@ -103,11 +141,24 @@ static char *register_service(DBusConnection *conn, const char *uuid)
static void create_services(DBusConnection *conn)
{
char *service_path;
+ int ret;
service_path = register_service(conn, IAS_UUID);
+ if (service_path == NULL)
+ return;
- services = g_slist_prepend(services, service_path);
+ /* Add Alert Level Characteristic to Immediate Alert Service */
+ ret = register_characteristic(conn, ALERT_LEVEL_CHR_UUID,
+ service_path);
+ if (ret < 0) {
+ printf("Couldn't register Alert Level characteristic (IAS)\n");
+ g_dbus_unregister_interface(conn, service_path,
+ GATT_SERVICE_IFACE);
+ g_free(service_path);
+ return;
+ }
+ services = g_slist_prepend(services, service_path);
printf("Registered service: %s\n", service_path);
}
--
1.8.3.1
When the characteristic requires Write Request, the ATT response should
be sent after receiving the reply for the Set method call.
---
src/gatt-dbus.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 61 insertions(+), 6 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 7bc19c8..c5f1597 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,11 @@ struct external_app {
unsigned int watch;
};
+struct proxy_write_data {
+ btd_attr_write_result_t result_cb;
+ void *user_data;
+};
+
/*
* Attribute to Proxy hash table. Used to map incoming
* ATT operations to its external characteristic proxy.
@@ -181,8 +186,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
static void proxy_write_reply(const DBusError *derr, void *user_data)
{
- if (derr)
- DBG("Write reply: %s", derr->message);
+ struct proxy_write_data *wdata = user_data;
+ int err = 0;
+
+ /*
+ * Security requirements shall be handled by the core. If external
+ * applications returns an error, the reasons will be restricted to
+ * invalid argument or application specific errors.
+ */
+
+ if (dbus_error_is_set(derr) == FALSE)
+ goto done;
+
+ DBG("Write reply: %s", derr->message);
+
+ if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
+ err = ETIMEDOUT;
+ else if (dbus_error_has_name(derr, ERROR_INTERFACE ".InvalidArguments"))
+ err = EINVAL;
+ else
+ err = EPROTO;
+
+done:
+ if (wdata && wdata->result_cb)
+ wdata->result_cb(err, wdata->user_data);
}
static void proxy_write_cb(struct btd_attribute *attr,
@@ -193,16 +220,44 @@ static void proxy_write_cb(struct btd_attribute *attr,
GDBusProxy *proxy;
proxy = g_hash_table_lookup(proxy_hash, attr);
- if (proxy == NULL)
- /* FIXME: Attribute not found */
+ if (proxy == NULL) {
+ result(ENOENT, user_data);
return;
+ }
- g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ /*
+ * "result" callback defines if the core wants to receive the
+ * operation result, allowing to select ATT Write Request or Write
+ * Command. Descriptors requires Write Request operation. For
+ * Characteristics, the implementation will define which operations
+ * are allowed based on the properties/flags.
+ * TODO: Write Long Characteristics/Descriptors.
+ */
+
+ if (result) {
+ struct proxy_write_data *wdata;
+
+ wdata = g_new0(struct proxy_write_data, 1);
+ wdata->result_cb = result;
+ wdata->user_data = user_data;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ wdata, g_free);
+ } else {
+ /*
+ * Caller is not interested in the Set method call result.
+ * This flow implements the ATT Write Command scenario, where
+ * the remote doesn't receive ATT response.
+ */
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
value, len, proxy_write_reply,
NULL, NULL);
+ }
- DBG("Server: Write characteristic callback %s",
+ DBG("Server: Write attribute callback %s",
g_dbus_proxy_get_path(proxy));
+
}
static int register_external_service(const struct external_app *eapp,
--
1.8.3.1
This patch adds an extra callback to inform the result of the write
operation on an external entity to allow the core to reply properly
for the ATT request.
---
src/gatt-dbus.c | 4 +++-
src/gatt.h | 5 ++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index d71d993..7bc19c8 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -186,7 +186,9 @@ static void proxy_write_reply(const DBusError *derr, void *user_data)
}
static void proxy_write_cb(struct btd_attribute *attr,
- const uint8_t *value, size_t len)
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data)
{
GDBusProxy *proxy;
diff --git a/src/gatt.h b/src/gatt.h
index f6b4aca..296258e 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -48,8 +48,11 @@ typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
* @err: error in errno format.
* @user_data: user_data passed in btd_attr_write_t callback
*/
+typedef void (*btd_attr_write_result_t) (int err, void *user_data);
typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
- const uint8_t *value, size_t len);
+ const uint8_t *value, size_t len,
+ btd_attr_write_result_t result,
+ void *user_data);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
--
1.8.3.1
This patch adds the callback for writing the external characteristic
value. Internally, DBusProxy implementation calls the Set method of the
"Value" property. At the moment, potential errors returned by the
external service implementation are being ignored.
The ATT operation (Write Command or Request) is not exposed to the service
implementation. Internally, gatt-dbus will handle possible returned errors,
and notify the core based on the callbacks informed. Write Command
doesn't have callback since it doesn't return ATT errors.
---
src/gatt-dbus.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 10d682d..d71d993 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -179,6 +179,30 @@ static void proxy_read_cb(struct btd_attribute *attr,
result(0, value, len, user_data);
}
+static void proxy_write_reply(const DBusError *derr, void *user_data)
+{
+ if (derr)
+ DBG("Write reply: %s", derr->message);
+}
+
+static void proxy_write_cb(struct btd_attribute *attr,
+ const uint8_t *value, size_t len)
+{
+ GDBusProxy *proxy;
+
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL)
+ /* FIXME: Attribute not found */
+ return;
+
+ g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+ value, len, proxy_write_reply,
+ NULL, NULL);
+
+ DBG("Server: Write characteristic callback %s",
+ g_dbus_proxy_get_path(proxy));
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -237,7 +261,8 @@ static int register_external_characteristics(GSList *proxies)
* Add properties according to Core SPEC 4.1 page 2183.
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb,
+ proxy_write_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This patch adds a new gdbus utility function to allow setting a property
of fixed, and non-fixed values array.
---
gdbus/client.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdbus/gdbus.h | 5 ++++
2 files changed, 90 insertions(+)
diff --git a/gdbus/client.c b/gdbus/client.c
index 5193b6c..6fea3d9 100644
--- a/gdbus/client.c
+++ b/gdbus/client.c
@@ -727,6 +727,91 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
return TRUE;
}
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ int size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy)
+{
+ struct set_property_data *data;
+ GDBusClient *client;
+ DBusMessage *msg;
+ DBusMessageIter iter, variant, array;
+ DBusPendingCall *call;
+ char array_sig[3];
+ char type_sig[2];
+
+ if (proxy == NULL || name == NULL || value == NULL)
+ return FALSE;
+
+ if (dbus_type_is_basic(type) == FALSE)
+ return FALSE;
+
+ client = proxy->client;
+ if (client == NULL)
+ return FALSE;
+
+ data = g_try_new0(struct set_property_data, 1);
+ if (data == NULL)
+ return FALSE;
+
+ data->function = function;
+ data->user_data = user_data;
+ data->destroy = destroy;
+
+ msg = dbus_message_new_method_call(client->service_name,
+ proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set");
+ if (msg == NULL) {
+ g_free(data);
+ return FALSE;
+ }
+
+ array_sig[0] = DBUS_TYPE_ARRAY;
+ array_sig[1] = (char) type;
+ array_sig[2] = '\0';
+
+ type_sig[0] = (char) type;
+ type_sig[1] = '\0';
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &proxy->interface);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ array_sig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ type_sig, &array);
+
+ if (dbus_type_is_fixed(type) == TRUE)
+ dbus_message_iter_append_fixed_array(&array, type,
+ &value, size);
+ else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+ int i;
+ const char **str = (const char **) value;
+
+ for (i = 0; i < size; i++)
+ dbus_message_iter_append_basic(&array, type, &str[i]);
+ }
+
+ dbus_message_iter_close_container(&variant, &array);
+ dbus_message_iter_close_container(&iter, &variant);
+
+ if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+ &call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ g_free(data);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
struct method_call_data {
GDBusReturnFunction function;
void *user_data;
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 8ada200..40a5abe 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -329,6 +329,11 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
GDBusResultFunction function, void *user_data,
GDBusDestroyFunction destroy);
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ int size, GDBusResultFunction function,
+ void *user_data, GDBusDestroyFunction destroy);
+
typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data);
typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data);
--
1.8.3.1
This patch adds a function callback for write operations. When a remote
device writes to a given attribute, the core calls the specified write
callback informing the attribute server the new characteristic value.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 ++++---
src/gatt.h | 14 +++++++++++++-
3 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 28604ea..10d682d 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -237,7 +237,7 @@ static int register_external_characteristics(GSList *proxies)
* Add properties according to Core SPEC 4.1 page 2183.
* Reference table 3.5: Characteristic Properties bit field.
*/
- attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 0137123..04b537e 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -47,6 +47,7 @@ struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
btd_attr_read_t read_cb;
+ btd_attr_write_t write_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -117,7 +118,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
}
struct btd_attribute *btd_gatt_add_char(bt_uuid_t *uuid, uint8_t properties,
- btd_attr_read_t read_cb)
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -170,8 +172,7 @@ struct btd_attribute *btd_gatt_add_char(bt_uuid_t *uuid, uint8_t properties,
char_value = new0(struct btd_attribute, 1);
memcpy(&char_value->type, uuid, sizeof(char_value->type));
char_value->read_cb = read_cb;
-
- /* TODO: Write callbacks */
+ char_value->write_cb = write_cb;
if (local_database_add(next_handle, char_value) < 0)
goto fail;
diff --git a/src/gatt.h b/src/gatt.h
index 4363769..f6b4aca 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -42,6 +42,14 @@ typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
btd_attr_read_result_t result,
void *user_data);
+/*
+ * Callbacks from this type are called once the value from the attribute was
+ * written.
+ * @err: error in errno format.
+ * @user_data: user_data passed in btd_attr_write_t callback
+ */
+typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
+ const uint8_t *value, size_t len);
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
@@ -56,12 +64,16 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* to local attribute database.
* @uuid: Characteristic UUID (16-bits or 128-bits).
* @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
+ * @read_cb: Callback used to provide the characteristic value.
+ * @write_cb: Callback called to notify the implementation that a new value
+ * is available.
*
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_char(bt_uuid_t *uuid, uint8_t properties,
- btd_attr_read_t read_cb);
+ btd_attr_read_t read_cb,
+ btd_attr_write_t write_cb);
/* btd_gatt_read_attribute - Read the value of an attribute.
* @attr: Attribute to be read.
--
1.8.3.1
This patch adds the btd_gatt_read_attribute() helper. It reads the
attribute value based on its read callback function. The callback can
the an abstraction for reading an external object proxy or a remote
attribute, acting as a helper function for server and client
implementations.
---
src/gatt.c | 22 ++++++++++++++++++++++
src/gatt.h | 9 +++++++++
2 files changed, 31 insertions(+)
diff --git a/src/gatt.c b/src/gatt.c
index 059e085..0137123 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -25,6 +25,7 @@
#include <config.h>
#endif
+#include <errno.h>
#include <glib.h>
#include "log.h"
@@ -194,6 +195,27 @@ fail:
return NULL;
}
+void btd_gatt_read_attribute(struct btd_attribute *attr,
+ btd_attr_read_result_t result,
+ void *user_data)
+{
+ /*
+ * When read_cb is available, it means that the attribute value
+ * is dynamic, and its value must be read from the external
+ * implementation. If "value_len" is set, the attribute value is
+ * constant. Additional checking are performed by the attribute server
+ * when the ATT Read request arrives based on the characteristic
+ * properties. At this point, properties bitmask doesn't need to be
+ * checked.
+ */
+ if (attr->read_cb)
+ attr->read_cb(attr, result, user_data);
+ else if (attr->value_len > 0)
+ result(0, attr->value, attr->value_len, user_data);
+ else
+ result(EPERM, NULL, 0, user_data);
+}
+
void gatt_init(void)
{
DBG("Starting GATT server");
diff --git a/src/gatt.h b/src/gatt.h
index 0021076..4363769 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -62,3 +62,12 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
*/
struct btd_attribute *btd_gatt_add_char(bt_uuid_t *uuid, uint8_t properties,
btd_attr_read_t read_cb);
+
+/* btd_gatt_read_attribute - Read the value of an attribute.
+ * @attr: Attribute to be read.
+ * @result: Callback function to be called with the result.
+ * @user_data: Data to be passed to the result callback function.
+ */
+void btd_gatt_read_attribute(struct btd_attribute *attr,
+ btd_attr_read_result_t result,
+ void *user_data);
--
1.8.3.1
This patch adds the callback for reading the external characteristic
Value. Internally, GDBusProxy implementation tracks all properties Value
changes consequently Value can be ready directly from the proxy without
additional method calls.
---
src/gatt-dbus.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 46 insertions(+), 2 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index a904a97..28604ea 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -139,6 +139,46 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
eapp->proxies = g_slist_remove(eapp->proxies, proxy);
}
+static void proxy_read_cb(struct btd_attribute *attr,
+ btd_attr_read_result_t result, void *user_data)
+{
+ DBusMessageIter iter, array;
+ GDBusProxy *proxy;
+ uint8_t *value;
+ int len;
+
+ /*
+ * Remote device is trying to read the informed attribute,
+ * "Value" should be read from the proxy. GDBusProxy tracks
+ * properties changes automatically, it is not necessary to
+ * get the value directly from the GATT server.
+ */
+ proxy = g_hash_table_lookup(proxy_hash, attr);
+ if (proxy == NULL) {
+ result(ENOENT, NULL, 0, user_data);
+ return;
+ }
+
+ if (!g_dbus_proxy_get_property(proxy, "Value", &iter)) {
+ /* Unusual situation, read property will checked earlier */
+ result(EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ DBG("External service inconsistent!");
+ result(EPERM, NULL, 0, user_data);
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ DBG("attribute: %p read %d bytes", attr, len);
+
+ result(0, value, len, user_data);
+}
+
static int register_external_service(const struct external_app *eapp,
GDBusProxy *proxy)
{
@@ -192,8 +232,12 @@ static int register_external_characteristics(GSList *proxies)
if (bt_string_to_uuid(&uuid, str) < 0)
return -EINVAL;
- /* TODO: Missing Flags/property */
- attr = btd_gatt_add_char(&uuid, 0x00, NULL);
+ /*
+ * TODO: Missing Flags/property
+ * Add properties according to Core SPEC 4.1 page 2183.
+ * Reference table 3.5: Characteristic Properties bit field.
+ */
+ attr = btd_gatt_add_char(&uuid, 0x00, proxy_read_cb);
if (attr == NULL)
return -EINVAL;
--
1.8.3.1
This patch adds a function callback for read operations. When a remote
device wants to reads a given attribute, the core calls the specified
read callback to get the value from the external services.
---
src/gatt-dbus.c | 2 +-
src/gatt.c | 7 +++++--
src/gatt.h | 19 ++++++++++++++++++-
3 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 014b8d6..a904a97 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -193,7 +193,7 @@ static int register_external_characteristics(GSList *proxies)
return -EINVAL;
/* TODO: Missing Flags/property */
- attr = btd_gatt_add_char(&uuid, 0x00);
+ attr = btd_gatt_add_char(&uuid, 0x00, NULL);
if (attr == NULL)
return -EINVAL;
diff --git a/src/gatt.c b/src/gatt.c
index 7f7af80..059e085 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -45,6 +45,7 @@ static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
+ btd_attr_read_t read_cb;
uint16_t value_len;
uint8_t value[0];
};
@@ -114,7 +115,8 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
return attr;
}
-struct btd_attribute *btd_gatt_add_char(bt_uuid_t *uuid, uint8_t properties)
+struct btd_attribute *btd_gatt_add_char(bt_uuid_t *uuid, uint8_t properties,
+ btd_attr_read_t read_cb)
{
struct btd_attribute *char_decl, *char_value = NULL;
@@ -166,8 +168,9 @@ struct btd_attribute *btd_gatt_add_char(bt_uuid_t *uuid, uint8_t properties)
char_value = new0(struct btd_attribute, 1);
memcpy(&char_value->type, uuid, sizeof(char_value->type));
+ char_value->read_cb = read_cb;
- /* TODO: Read & Write callbacks */
+ /* TODO: Write callbacks */
if (local_database_add(next_handle, char_value) < 0)
goto fail;
diff --git a/src/gatt.h b/src/gatt.h
index 7df82cf..0021076 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -27,6 +27,22 @@ void gatt_init(void);
void gatt_cleanup(void);
+/*
+ * Callbacks of this type are called once the value from the attribute is
+ * ready to be read from the service implementation. Result callback is
+ * the asynchronous function that should be used to inform the caller
+ * the read value.
+ * @err: error in errno format.
+ * @value: pointer to value
+ * @len: length of value
+ * @user_data: user_data passed in btd_attr_read_t callback
+ */
+typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
+ void *user_data);
+typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
+ btd_attr_read_result_t result,
+ void *user_data);
+
/* btd_gatt_add_service - Add a service declaration to local attribute database.
* @uuid: Service UUID.
*
@@ -44,4 +60,5 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
* Returns a reference to characteristic value attribute. In case of error,
* NULL is returned.
*/
-struct btd_attribute *btd_gatt_add_char(bt_uuid_t *uuid, uint8_t properties);
+struct btd_attribute *btd_gatt_add_char(bt_uuid_t *uuid, uint8_t properties,
+ btd_attr_read_t read_cb);
--
1.8.3.1
This commits adds a hash table to map attributes into GDBusProxy,
allowing to call remote objects for reading and writing values.
---
src/gatt-dbus.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
index 8cf222d..014b8d6 100644
--- a/src/gatt-dbus.c
+++ b/src/gatt-dbus.c
@@ -56,6 +56,12 @@ struct external_app {
unsigned int watch;
};
+/*
+ * Attribute to Proxy hash table. Used to map incoming
+ * ATT operations to its external characteristic proxy.
+ */
+static GHashTable *proxy_hash;
+
static GSList *external_apps;
static int external_app_path_cmp(gconstpointer a, gconstpointer b)
@@ -172,6 +178,7 @@ static int register_external_characteristics(GSList *proxies)
DBusMessageIter iter;
const char *str, *path;
bt_uuid_t uuid;
+ struct btd_attribute *attr;
GDBusProxy *proxy = list->data;
if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
@@ -186,11 +193,14 @@ static int register_external_characteristics(GSList *proxies)
return -EINVAL;
/* TODO: Missing Flags/property */
- if (btd_gatt_add_char(&uuid, 0x00) == NULL)
+ attr = btd_gatt_add_char(&uuid, 0x00);
+ if (attr == NULL)
return -EINVAL;
path = g_dbus_proxy_get_path(proxy);
DBG("Added GATT CHR: %s (%s)", path, str);
+
+ g_hash_table_insert(proxy_hash, attr, g_dbus_proxy_ref(proxy));
}
return 0;
@@ -313,13 +323,22 @@ static const GDBusMethodTable methods[] = {
gboolean gatt_dbus_manager_register(void)
{
- return g_dbus_register_interface(btd_get_dbus_connection(),
- "/org/bluez", GATT_MGR_IFACE,
- methods, NULL, NULL, NULL, NULL);
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ "/org/bluez", GATT_MGR_IFACE,
+ methods, NULL, NULL, NULL, NULL) == FALSE)
+ return FALSE;
+
+ proxy_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) g_dbus_proxy_unref);
+
+ return TRUE;
}
void gatt_dbus_manager_unregister(void)
{
+ g_hash_table_destroy(proxy_hash);
+ proxy_hash = NULL;
+
g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez",
GATT_MGR_IFACE);
}
--
1.8.3.1
From: Andre Guedes <[email protected]>
This patch adds btd_gatt_add_char() helper. It creates and adds the
Characteristic declaration, and Characteristic value attributes to the
local attribute database.
---
src/gatt.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/gatt.h | 11 +++++++++
2 files changed, 91 insertions(+)
diff --git a/src/gatt.c b/src/gatt.c
index 7882a91..7f7af80 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -39,6 +39,9 @@
static const bt_uuid_t primary_uuid = { .type = BT_UUID16,
.value.u16 = GATT_PRIM_SVC_UUID };
+static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
+ .value.u16 = GATT_CHARAC_UUID };
+
struct btd_attribute {
uint16_t handle;
bt_uuid_t type;
@@ -111,6 +114,83 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
return attr;
}
+struct btd_attribute *btd_gatt_add_char(bt_uuid_t *uuid, uint8_t properties)
+{
+ struct btd_attribute *char_decl, *char_value = NULL;
+
+ /* Attribute value length */
+ uint16_t len = 1 + 2 + bt_uuid_len(uuid);
+ uint8_t value[len];
+
+ /*
+ * Characteristic DECLARATION
+ *
+ * TYPE ATTRIBUTE VALUE
+ * +-------+---------------------------------+
+ * |0x2803 | 0xXX 0xYYYY 0xZZZZ... |
+ * | (1) | (2) (3) (4) |
+ * +------+----------------------------------+
+ * (1) - 2 octets: Characteristic declaration UUID
+ * (2) - 1 octet : Properties
+ * (3) - 2 octets: Handle of the characteristic Value
+ * (4) - 2 or 16 octets: Characteristic UUID
+ */
+
+ value[0] = properties;
+
+ /*
+ * Since we don't know yet the characteristic value attribute
+ * handle, we skip and set it later.
+ */
+
+ att_put_uuid(*uuid, &value[3]);
+
+ char_decl = new_const_attribute(&chr_uuid, value, len);
+ if (local_database_add(next_handle, char_decl) < 0)
+ goto fail;
+
+ next_handle = next_handle + 1;
+
+ /*
+ * Characteristic VALUE
+ *
+ * TYPE ATTRIBUTE VALUE
+ * +----------+---------------------------------+
+ * |0xZZZZ... | 0x... |
+ * | (1) | (2) |
+ * +----------+---------------------------------+
+ * (1) - 2 or 16 octets: Characteristic UUID
+ * (2) - N octets: Value is read dynamically from the service
+ * implementation (external entity).
+ */
+
+ char_value = new0(struct btd_attribute, 1);
+ memcpy(&char_value->type, uuid, sizeof(char_value->type));
+
+ /* TODO: Read & Write callbacks */
+
+ if (local_database_add(next_handle, char_value) < 0)
+ goto fail;
+
+ next_handle = next_handle + 1;
+
+ /*
+ * Update characteristic value handle in characteristic declaration
+ * attribute. For local attributes, we can assume that the handle
+ * representing the characteristic value will get the next available
+ * handle. However, for remote attribute this assumption is not valid.
+ */
+ att_put_u16(char_value->handle, &char_decl->value[1]);
+
+ return char_value;
+
+fail:
+ free(char_decl);
+ free(char_value);
+
+ return NULL;
+}
+
void gatt_init(void)
{
DBG("Starting GATT server");
diff --git a/src/gatt.h b/src/gatt.h
index 8dd1312..7df82cf 100644
--- a/src/gatt.h
+++ b/src/gatt.h
@@ -34,3 +34,14 @@ void gatt_cleanup(void);
* NULL is returned.
*/
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
+
+/*
+ * btd_gatt_add_char - Add a characteristic (declaration and value attributes)
+ * to local attribute database.
+ * @uuid: Characteristic UUID (16-bits or 128-bits).
+ * @properties: Characteristic properties. See Core SPEC 4.1 page 2183.
+ *
+ * Returns a reference to characteristic value attribute. In case of error,
+ * NULL is returned.
+ */
+struct btd_attribute *btd_gatt_add_char(bt_uuid_t *uuid, uint8_t properties);
--
1.8.3.1
From: Andre Guedes <[email protected]>
This patch adds a helper function to create attribute with static
values. It is intended to be used to create GATT services, and
characteristic declarations.
---
src/gatt.c | 32 ++++++++++++++++++++++++++------
1 file changed, 26 insertions(+), 6 deletions(-)
diff --git a/src/gatt.c b/src/gatt.c
index f7b74d6..7882a91 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -30,6 +30,7 @@
#include "log.h"
#include "lib/uuid.h"
#include "attrib/att.h"
+#include "src/shared/util.h"
#include "gatt-dbus.h"
#include "gatt.h"
@@ -48,6 +49,25 @@ struct btd_attribute {
static GList *local_attribute_db;
static uint16_t next_handle = 0x0001;
+/*
+ * Helper function to create new attributes containing constant/static values.
+ * eg: declaration of services/characteristics, and characteristics with
+ * fixed values.
+ */
+static struct btd_attribute *new_const_attribute(const bt_uuid_t *type,
+ const uint8_t *value,
+ uint16_t len)
+{
+ struct btd_attribute *attr = malloc0(sizeof(struct btd_attribute) +
+ len);
+
+ memcpy(&attr->type, type, sizeof(*type));
+ memcpy(&attr->value, value, len);
+ attr->value_len = len;
+
+ return attr;
+}
+
static int local_database_add(uint16_t handle, struct btd_attribute *attr)
{
attr->handle = handle;
@@ -59,9 +79,9 @@ static int local_database_add(uint16_t handle, struct btd_attribute *attr)
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
{
+ struct btd_attribute *attr;
uint16_t len = bt_uuid_len(uuid);
- struct btd_attribute *attr = g_malloc0(sizeof(struct btd_attribute) +
- len);
+ uint8_t value[len];
/*
* Service DECLARATION
@@ -75,13 +95,13 @@ struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
* (2) - 2 or 16 octets: Service UUID
*/
- attr->type = primary_uuid;
+ /* Set attribute value */
+ att_put_uuid(*uuid, value);
- att_put_uuid(*uuid, attr->value);
- attr->value_len = len;
+ attr = new_const_attribute(&primary_uuid, value, len);
if (local_database_add(next_handle, attr) < 0) {
- g_free(attr);
+ free(attr);
return NULL;
}
--
1.8.3.1