This patch set includes patches that rewrite the local GATT server using
shared/gatt. This in effect invalidates the existing src/attrib-server in
favor of a new src/gatt-server.
Arman Uguray (13):
shared/att: Add bt_att_get|set_bdaddr functions
shared/gatt: Add bdaddr_type param to gatt_db APIs
core: Introduce btd_gatt_server.
core: Attach gatt-server to bt_att
core: gatt: Add GATT/GAP services to local db
core: Add GATT UUIDs to Adapter1.UUIDs
core: Support per-client CCC state
core: Setup added/removed handlers in GATT server
core: Add Service Changed characteristic
core: device: Add getter for GATT server
core: gatt-server: Send "Service Changed"
core: adapter: Send UUIDs changed for GATT services
shared/gatt: Don't incorrectly terminate discovery
Makefile.am | 1 +
android/gatt.c | 32 +-
src/adapter.c | 58 +++-
src/device.c | 53 +++-
src/device.h | 1 +
src/gatt-client.c | 20 +-
src/gatt-server.c | 764 ++++++++++++++++++++++++++++++++++++++++++++++
src/gatt-server.h | 29 ++
src/main.c | 3 +
src/shared/att.c | 28 ++
src/shared/att.h | 3 +
src/shared/gatt-client.c | 6 +-
src/shared/gatt-db.c | 6 +-
src/shared/gatt-db.h | 4 +
src/shared/gatt-helpers.c | 3 +-
src/shared/gatt-server.c | 62 +++-
tools/btgatt-server.c | 22 +-
unit/test-gatt.c | 7 +-
18 files changed, 1027 insertions(+), 75 deletions(-)
create mode 100644 src/gatt-server.c
create mode 100644 src/gatt-server.h
--
2.2.0.rc0.207.ga3a616c
Hi Arman,
On Wed, Feb 11, 2015 at 6:54 AM, Arman Uguray <[email protected]> wrote:
> This patch introduces src/gatt-server.* that handles incoming ATT
> connections, manages per-adapter shared/gatt-db instances, and routes
> connections to the corresponding device object. This is the layer that
> will handle all the CCC management and Service Changed handling.
> ---
> Makefile.am | 1 +
> src/adapter.c | 8 ++-
> src/gatt-server.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> src/gatt-server.h | 26 +++++++
> src/main.c | 3 +
> 5 files changed, 239 insertions(+), 2 deletions(-)
> create mode 100644 src/gatt-server.c
> create mode 100644 src/gatt-server.h
>
> diff --git a/Makefile.am b/Makefile.am
> index 60811f1..3c64426 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -167,6 +167,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
> src/sdpd-server.c src/sdpd-request.c \
> src/sdpd-service.c src/sdpd-database.c \
> src/attrib-server.h src/attrib-server.c \
> + src/gatt-server.h src/gatt-server.c \
> src/sdp-xml.h src/sdp-xml.c \
> src/sdp-client.h src/sdp-client.c \
> src/textfile.h src/textfile.c \
> diff --git a/src/adapter.c b/src/adapter.c
> index 1839286..db359ad 100644
> --- a/src/adapter.c
> +++ b/src/adapter.c
> @@ -68,6 +68,7 @@
> #include "attrib/att.h"
> #include "attrib/gatt.h"
> #include "attrib-server.h"
> +#include "gatt-server.h"
> #include "eir.h"
>
> #define ADAPTER_INTERFACE "org.bluez.Adapter1"
> @@ -302,6 +303,7 @@ static void dev_class_changed_callback(uint16_t index, uint16_t length,
> appearance[1] = rp->val[1] & 0x1f; /* removes service class */
> appearance[2] = rp->val[2];
>
> + /* TODO: Do this through btd_gatt_server instead */
> attrib_gap_set(adapter, GATT_CHARAC_APPEARANCE, appearance, 2);
> }
>
> @@ -4014,6 +4016,7 @@ static void convert_sdp_entry(char *key, char *value, void *user_data)
> if (record_has_uuid(rec, att_uuid))
> goto failed;
>
> + /* TODO: Do this through btd_gatt_server */
> if (!gatt_parse_record(rec, &uuid, &psm, &start, &end))
> goto failed;
>
> @@ -4548,7 +4551,7 @@ static void adapter_remove(struct btd_adapter *adapter)
> adapter->devices = NULL;
>
> unload_drivers(adapter);
> - btd_adapter_gatt_server_stop(adapter);
> + btd_gatt_server_unregister_adapter(adapter);
>
> g_slist_free(adapter->pin_callbacks);
> adapter->pin_callbacks = NULL;
> @@ -6590,7 +6593,8 @@ static int adapter_register(struct btd_adapter *adapter)
> agent_unref(agent);
> }
>
> - btd_adapter_gatt_server_start(adapter);
> + if (!btd_gatt_server_register_adapter(adapter))
> + error("Failed to register adapter with GATT server");
>
> load_config(adapter);
> fix_storage(adapter);
> diff --git a/src/gatt-server.c b/src/gatt-server.c
> new file mode 100644
> index 0000000..977a057
> --- /dev/null
> +++ b/src/gatt-server.c
> @@ -0,0 +1,203 @@
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2015 Google Inc.
> + *
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <stdint.h>
> +#include <stdlib.h>
> +
> +#include "lib/uuid.h"
> +#include "btio/btio.h"
> +#include "src/shared/util.h"
> +#include "src/shared/queue.h"
> +#include "src/shared/gatt-db.h"
> +#include "log.h"
> +#include "adapter.h"
> +#include "device.h"
> +#include "gatt-server.h"
> +
> +#ifndef ATT_CID
> +#define ATT_CID 4
> +#endif
> +
> +static struct queue *servers = NULL;
> +
> +struct btd_gatt_server {
> + struct btd_adapter *adapter;
> + struct gatt_db *db;
> + GIOChannel *le_io;
> +};
> +
> +static void gatt_server_free(void *data)
> +{
> + struct btd_gatt_server *server = data;
> +
> + if (server->le_io) {
> + g_io_channel_shutdown(server->le_io, FALSE, NULL);
> + g_io_channel_unref(server->le_io);
> + }
> +
> + gatt_db_unref(server->db);
> + btd_adapter_unref(server->adapter);
> + free(server);
> +}
> +
> +bool btd_gatt_server_init(void)
> +{
> +
> + info("Initializing GATT server");
> +
> + servers = queue_new();
> + if (!servers) {
> + error("Failed to set up local GATT server");
> + return false;
> + }
> +
> + return true;
> +}
> +
> +void btd_gatt_server_cleanup(void)
> +{
> + info("Cleaning up GATT server");
> +
> + queue_destroy(servers, gatt_server_free);
> + servers = NULL;
> +}
> +
> +static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
> +{
> + struct btd_adapter *adapter;
> + struct btd_device *device;
> + uint8_t dst_type;
> + bdaddr_t src, dst;
> +
> + DBG("New incoming LE ATT connection");
> +
> + if (gerr) {
> + error("%s", gerr->message);
> + return;
> + }
> +
> + bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src,
> + BT_IO_OPT_DEST_BDADDR, &dst,
> + BT_IO_OPT_DEST_TYPE, &dst_type,
> + BT_IO_OPT_INVALID);
> + if (gerr) {
> + error("bt_io_get: %s", gerr->message);
> + g_error_free(gerr);
> + return;
> + }
> +
> + adapter = adapter_find(&src);
> + if (!adapter)
> + return;
> +
> + device = btd_adapter_get_device(adapter, &dst, dst_type);
> + if (!device)
> + return;
> +
> + device_attach_att(device, io);
> +}
> +
> +static bool match_adapter(const void *a, const void *b)
> +{
> + const struct btd_gatt_server *server = a;
> + const struct btd_adapter *adapter = b;
> +
> + return server->adapter == adapter;
> +}
> +
> +bool btd_gatt_server_register_adapter(struct btd_adapter *adapter)
> +{
> + struct btd_gatt_server *server;
> + GError *gerr = NULL;
> + const bdaddr_t *addr;
> +
> + if (!adapter)
> + return false;
> +
> + if (!servers) {
> + error("GATT server not initialized");
> + return false;
> + }
> +
> + if (queue_find(servers, match_adapter, adapter)) {
> + error("Adapter already registered with GATT server");
> + return false;
> + }
> +
> + server = new0(struct btd_gatt_server, 1);
> + if (!server)
> + return false;
> +
> + server->adapter = btd_adapter_ref(adapter);
> + server->db = gatt_db_new();
> + if (!server->db)
> + goto fail;
> +
> + addr = btd_adapter_get_address(adapter);
> + server->le_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
> + BT_IO_OPT_SOURCE_BDADDR, addr,
> + BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
> + BT_IO_OPT_CID, ATT_CID,
> + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
> + BT_IO_OPT_INVALID);
> + if (!server->le_io) {
> + error("Failed to start listening: %s", gerr->message);
> + g_error_free(gerr);
> + goto fail;
> + }
> +
> + queue_push_tail(servers, server);
> +
> + /* TODO: Set up GAP/GATT services */
> +
> + return true;
> +
> +fail:
> + gatt_server_free(server);
> +
> + return false;
> +}
> +
> +void btd_gatt_server_unregister_adapter(struct btd_adapter *adapter)
> +{
> + if (!adapter || !servers)
> + return;
> +
> + queue_remove_all(servers, match_adapter, adapter, gatt_server_free);
> +}
> +
> +struct gatt_db *btd_gatt_server_get_db(struct btd_adapter *adapter)
> +{
> + struct btd_gatt_server *server;
> +
> + if (!servers) {
> + error("GATT server not initialized");
> + return false;
> + }
> +
> + server = queue_find(servers, match_adapter, adapter);
> + if (!server)
> + return false;
> +
> + return server->db;
> +}
> diff --git a/src/gatt-server.h b/src/gatt-server.h
> new file mode 100644
> index 0000000..f97ad05
> --- /dev/null
> +++ b/src/gatt-server.h
> @@ -0,0 +1,26 @@
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2015 Google Inc.
> + *
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +bool btd_gatt_server_init(void);
> +void btd_gatt_server_cleanup(void);
> +
> +bool btd_gatt_server_register_adapter(struct btd_adapter *adapter);
> +void btd_gatt_server_unregister_adapter(struct btd_adapter *adapter);
> +
> +struct gatt_db *btd_gatt_server_get_db(struct btd_adapter *adapter);
Lets please not commit the same mistakes as in btd_gatt_client, so
please use another filename and lets avoid circular dependency as it
makes the code pretty hard to follow, not to mention it is hard to
decouple if we ever need to do that, for these reasons I think it is
better to use gatt-dbus.c since anyway it was create to deal with GATT
server.
> diff --git a/src/main.c b/src/main.c
> index 061060d..510388a 100644
> --- a/src/main.c
> +++ b/src/main.c
> @@ -56,6 +56,7 @@
> #include "agent.h"
> #include "profile.h"
> #include "gatt.h"
> +#include "gatt-server.h"
> #include "systemd.h"
>
> #define BLUEZ_NAME "org.bluez"
> @@ -579,6 +580,7 @@ int main(int argc, char *argv[])
> g_dbus_set_flags(gdbus_flags);
>
> gatt_init();
> + btd_gatt_server_init();
>
> if (adapter_init() < 0) {
> error("Adapter handling initialization failed");
> @@ -642,6 +644,7 @@ int main(int argc, char *argv[])
>
> adapter_cleanup();
>
> + btd_gatt_server_cleanup();
> gatt_cleanup();
>
> rfkill_exit();
> --
> 2.2.0.rc0.207.ga3a616c
>
--
Luiz Augusto von Dentz
Hi Arman,
On Wed, Feb 11, 2015 at 6:54 AM, Arman Uguray <[email protected]> wrote:
> Added functions to get and set bdaddr_t* and bdaddr_type on a bt_att
> structure.
> ---
> src/shared/att.c | 28 ++++++++++++++++++++++++++++
> src/shared/att.h | 3 +++
> src/shared/gatt-client.c | 3 ++-
> src/shared/gatt-helpers.c | 3 ++-
> 4 files changed, 35 insertions(+), 2 deletions(-)
>
> diff --git a/src/shared/att.c b/src/shared/att.c
> index a98909e..5b04c1f 100644
> --- a/src/shared/att.c
> +++ b/src/shared/att.c
> @@ -76,6 +76,9 @@ struct bt_att {
> unsigned int next_send_id; /* IDs for "send" ops */
> unsigned int next_reg_id; /* IDs for registered callbacks */
>
> + bdaddr_t bdaddr; /* BDADDR assigned to this transport */
> + uint8_t bdaddr_type;
>
> bt_att_timeout_func_t timeout_callback;
> bt_att_destroy_func_t timeout_destroy;
> void *timeout_data;
> @@ -889,6 +892,31 @@ bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close)
> return io_set_close_on_destroy(att->io, do_close);
> }
>
> +bool bt_att_get_bdaddr(struct bt_att *att, bdaddr_t *bdaddr, uint8_t *type)
> +{
> + if (!att)
> + return false;
> +
> + if (bdaddr)
> + *bdaddr = att->bdaddr;
> +
> + if (type)
> + *type = att->bdaddr_type;
> +
> + return true;
> +}
> +
> +bool bt_att_set_bdaddr(struct bt_att *att, bdaddr_t *addr, uint8_t type)
> +{
> + if (!att || !addr)
> + return false;
> +
> + bacpy(&att->bdaddr, addr);
> + att->bdaddr_type = type;
> +
> + return true;
> +}
> +
> bool bt_att_set_debug(struct bt_att *att, bt_att_debug_func_t callback,
> void *user_data, bt_att_destroy_func_t destroy)
> {
> diff --git a/src/shared/att.h b/src/shared/att.h
> index cd00a1e..48241fc 100644
> --- a/src/shared/att.h
> +++ b/src/shared/att.h
> @@ -35,6 +35,9 @@ void bt_att_unref(struct bt_att *att);
>
> bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close);
>
> +bool bt_att_get_bdaddr(struct bt_att *att, bdaddr_t *bdaddr, uint8_t *type);
> +bool bt_att_set_bdaddr(struct bt_att *att, bdaddr_t *addr, uint8_t type);
These will make caller depend on bluetooth.h which we probably don't
want, can't we figure internally if what type of fd that is, perhaps
with getpeername or getsockopt?
> typedef void (*bt_att_response_func_t)(uint8_t opcode, const void *pdu,
> uint16_t length, void *user_data);
> typedef void (*bt_att_notify_func_t)(uint8_t opcode, const void *pdu,
> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
> index d0fc054..0c8fa13 100644
> --- a/src/shared/gatt-client.c
> +++ b/src/shared/gatt-client.c
> @@ -21,8 +21,9 @@
> *
> */
>
> -#include "src/shared/att.h"
> #include "lib/uuid.h"
> +#include "lib/bluetooth.h"
> +#include "src/shared/att.h"
> #include "src/shared/gatt-helpers.h"
> #include "src/shared/util.h"
> #include "src/shared/queue.h"
> diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
> index a33f960..b469116 100644
> --- a/src/shared/gatt-helpers.c
> +++ b/src/shared/gatt-helpers.c
> @@ -26,9 +26,10 @@
> #include <config.h>
> #endif
>
> +#include "lib/uuid.h"
> +#include "lib/bluetooth.h"
> #include "src/shared/queue.h"
> #include "src/shared/att.h"
> -#include "lib/uuid.h"
> #include "src/shared/gatt-helpers.h"
> #include "src/shared/util.h"
>
> --
> 2.2.0.rc0.207.ga3a616c
>
--
Luiz Augusto von Dentz
bt_gatt_client terminates discovery if no primary services are found
within the given range. This behavior is incorrect, as the given
handle range may contain secondary services and those should be
discovered regardless.
---
src/shared/gatt-client.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 0c8fa13..c3130e5 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -906,7 +906,7 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
util_debug(client->debug_callback, client->debug_data,
"Primary service discovery failed."
" ATT ECODE: 0x%02x", att_ecode);
- goto done;
+ goto secondary;
}
if (!result || !bt_gatt_iter_init(&iter, result)) {
@@ -939,6 +939,7 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
queue_push_tail(op->pending_svcs, attr);
}
+secondary:
/* Discover secondary services */
if (bt_gatt_discover_secondary_services(client->att, NULL,
op->start, op->end,
--
2.2.0.rc0.207.ga3a616c
btd_adapter now sends a PropertiesChanged signal for the "UUIDs"
property when its associated gatt_db is modified.
---
src/adapter.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/src/adapter.c b/src/adapter.c
index beb9fda..ae74fa9 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -225,6 +225,8 @@ struct btd_adapter {
unsigned int pair_device_id;
guint pair_device_timeout;
+ unsigned int db_id; /* Service event handler for GATT db */
+
bool is_default; /* true if adapter is default one */
};
@@ -4554,6 +4556,7 @@ static struct btd_adapter *btd_adapter_new(uint16_t index)
static void adapter_remove(struct btd_adapter *adapter)
{
GSList *l;
+ struct gatt_db *db;
DBG("Removing adapter %s", adapter->path);
@@ -4579,6 +4582,11 @@ static void adapter_remove(struct btd_adapter *adapter)
adapter->devices = NULL;
unload_drivers(adapter);
+
+ db = btd_gatt_server_get_db(adapter);
+ gatt_db_unregister(db, adapter->db_id);
+ adapter->db_id = 0;
+
btd_gatt_server_unregister_adapter(adapter);
g_slist_free(adapter->pin_callbacks);
@@ -6588,9 +6596,18 @@ static int set_did(struct btd_adapter *adapter, uint16_t vendor,
return -EIO;
}
+static void services_modified(struct gatt_db_attribute *attrib, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "UUIDs");
+}
+
static int adapter_register(struct btd_adapter *adapter)
{
struct agent *agent;
+ struct gatt_db *db;
if (powering_down)
return -EBUSY;
@@ -6624,6 +6641,11 @@ static int adapter_register(struct btd_adapter *adapter)
if (!btd_gatt_server_register_adapter(adapter))
error("Failed to register adapter with GATT server");
+ db = btd_gatt_server_get_db(adapter);
+ adapter->db_id = gatt_db_register(db, services_modified,
+ services_modified,
+ adapter, NULL);
+
load_config(adapter);
fix_storage(adapter);
load_drivers(adapter);
--
2.2.0.rc0.207.ga3a616c
With this patch, the local GATT server sends out "Service Changed"
indications to devices that have configured the corresponding CCC
descriptor, when a local attribute database is modified.
---
src/gatt-server.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 112 insertions(+), 4 deletions(-)
diff --git a/src/gatt-server.c b/src/gatt-server.c
index 9af58a0..73a4ef3 100644
--- a/src/gatt-server.c
+++ b/src/gatt-server.c
@@ -29,7 +29,8 @@
#include "src/shared/util.h"
#include "src/shared/queue.h"
#include "src/shared/gatt-db.h"
-#include "src/shared/att-types.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-server.h"
#include "log.h"
#include "adapter.h"
#include "device.h"
@@ -420,10 +421,117 @@ static void register_core_services(struct btd_gatt_server *server)
populate_gatt_service(server);
}
+struct not_data {
+ struct btd_gatt_server *server;
+ uint16_t handle, ccc_handle;
+ const uint8_t *value;
+ uint16_t len;
+ bool indicate;
+};
+
+static void conf_cb(void *user_data)
+{
+ DBG("GATT server received confirmation");
+}
+
+static void send_notification_to_device(void *data, void *user_data)
+{
+ struct device_state *device_state = data;
+ struct not_data *not_data = user_data;
+ struct ccc_state *ccc;
+ struct btd_device *device;
+
+ ccc = find_ccc_state(device_state, not_data->ccc_handle);
+ if (!ccc)
+ return;
+
+ if (!ccc->value[0] || (not_data->indicate && !(ccc->value[0] & 0x02)))
+ return;
+
+ device = btd_adapter_get_device(not_data->server->adapter,
+ &device_state->bdaddr,
+ device_state->bdaddr_type);
+ if (!device)
+ return;
+
+ /*
+ * TODO: If the device is not connected but bonded, send the
+ * notification/indication when it becomes connected.
+ */
+
+ if (!not_data->indicate) {
+ DBG("GATT server sending notification");
+ bt_gatt_server_send_notification(
+ btd_device_get_gatt_server(device),
+ not_data->handle, not_data->value,
+ not_data->len);
+ return;
+ }
+
+ DBG("GATT server sending indication");
+ bt_gatt_server_send_indication(btd_device_get_gatt_server(device),
+ not_data->handle,
+ not_data->value,
+ not_data->len, conf_cb,
+ NULL, NULL);
+}
+
+static void send_notification_to_devices(struct btd_gatt_server *server,
+ uint16_t handle, const uint8_t *value,
+ uint16_t len, uint16_t ccc_handle,
+ bool indicate)
+{
+ struct not_data not_data;
+
+ memset(¬_data, 0, sizeof(not_data));
+
+ not_data.server = server;
+ not_data.handle = handle;
+ not_data.ccc_handle = ccc_handle;
+ not_data.value = value;
+ not_data.len = len;
+ not_data.indicate = indicate;
+
+ queue_foreach(server->device_states, send_notification_to_device,
+ ¬_data);
+}
+
+static void send_service_changed(struct btd_gatt_server *server,
+ struct gatt_db_attribute *attrib)
+{
+ uint16_t start, end;
+ uint8_t value[4];
+ uint16_t handle, ccc_handle;
+
+ if (!gatt_db_attribute_get_service_handles(attrib, &start, &end)) {
+ error("Failed to obtain changed service handles");
+ return;
+ }
+
+ handle = gatt_db_attribute_get_handle(server->svc_chngd);
+ ccc_handle = gatt_db_attribute_get_handle(server->svc_chngd_ccc);
+
+ if (!handle || !ccc_handle) {
+ error("Failed to obtain handles for \"Service Changed\""
+ " characteristic");
+ return;
+ }
+
+ put_le16(start, value);
+ put_le16(end, value + 2);
+
+ send_notification_to_devices(server, handle, value, sizeof(value),
+ ccc_handle, true);
+}
+
static void gatt_db_service_added(struct gatt_db_attribute *attrib,
void *user_data)
{
- /* TODO: Send out service changed signal */
+ struct btd_gatt_server *server = user_data;
+
+ DBG("GATT Service added to local database");
+
+ send_service_changed(server, attrib);
}
static bool ccc_match_service(const void *data, const void *match_data)
@@ -452,9 +560,9 @@ static void gatt_db_service_removed(struct gatt_db_attribute *attrib,
DBG("Local GATT service removed");
- queue_foreach(server->device_states, remove_device_ccc, attrib);
+ send_service_changed(server, attrib);
- /* TODO: Send out service changed signal */
+ queue_foreach(server->device_states, remove_device_ccc, attrib);
}
bool btd_gatt_server_register_adapter(struct btd_adapter *adapter)
--
2.2.0.rc0.207.ga3a616c
Added btd_device_get_gatt_server function.
---
src/device.c | 8 ++++++++
src/device.h | 1 +
2 files changed, 9 insertions(+)
diff --git a/src/device.c b/src/device.c
index b8b89cd..55db667 100644
--- a/src/device.c
+++ b/src/device.c
@@ -5093,6 +5093,14 @@ struct bt_gatt_client *btd_device_get_gatt_client(struct btd_device *device)
return device->client;
}
+struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device)
+{
+ if (!device)
+ return NULL;
+
+ return device->server;
+}
+
void btd_device_gatt_set_service_changed(struct btd_device *device,
uint16_t start, uint16_t end)
{
diff --git a/src/device.h b/src/device.h
index a7fefee..8edd0df 100644
--- a/src/device.h
+++ b/src/device.h
@@ -69,6 +69,7 @@ struct gatt_primary *btd_device_get_primary(struct btd_device *device,
GSList *btd_device_get_primaries(struct btd_device *device);
struct gatt_db *btd_device_get_gatt_db(struct btd_device *device);
struct bt_gatt_client *btd_device_get_gatt_client(struct btd_device *device);
+struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device);
void btd_device_gatt_set_service_changed(struct btd_device *device,
uint16_t start, uint16_t end);
bool device_attach_att(struct btd_device *dev, GIOChannel *io);
--
2.2.0.rc0.207.ga3a616c
This patch exports the "Service Changed" characteristic and its CCC
descriptor in btd_gatt_server.
---
src/gatt-server.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/src/gatt-server.c b/src/gatt-server.c
index 22faece..9af58a0 100644
--- a/src/gatt-server.c
+++ b/src/gatt-server.c
@@ -50,6 +50,10 @@ struct btd_gatt_server {
unsigned int db_id;
GIOChannel *le_io;
struct queue *device_states;
+ struct gatt_db_attribute *svc_chngd;
+ struct gatt_db_attribute *svc_chngd_ccc;
+ uint16_t svc_chngd_start;
+ uint16_t svc_chngd_end;
};
struct device_state {
@@ -355,16 +359,57 @@ static void populate_gap_service(struct btd_gatt_server *server)
gatt_db_service_set_active(service, true);
}
+static void gatt_svc_chngd_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type, void *user_data)
+{
+ struct btd_gatt_server *server = user_data;
+ uint8_t ecode = 0;
+ size_t len;
+ const uint8_t *value = NULL;
+ uint8_t bytes[4];
+
+ if (server->svc_chngd_start && server->svc_chngd_end) {
+ memset(bytes, 0, sizeof(bytes));
+ put_le16(server->svc_chngd_start, bytes);
+ put_le16(server->svc_chngd_end, bytes + 2);
+ len = 4;
+ } else {
+ len = 0;
+ }
+
+ if (offset > len) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ len -= offset;
+ value = len ? &bytes[offset] : NULL;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, ecode, value, len);
+}
+
static void populate_gatt_service(struct btd_gatt_server *server)
{
bt_uuid_t uuid;
struct gatt_db_attribute *service;
+ uint16_t start_handle;
/* Add the GATT service */
bt_uuid16_create(&uuid, UUID_GATT);
- service = gatt_db_add_service(server->db, &uuid, true, 1);
+ service = gatt_db_add_service(server->db, &uuid, true, 4);
+ gatt_db_attribute_get_service_handles(service, &start_handle, NULL);
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
+ server->svc_chngd = gatt_db_service_add_characteristic(service, &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_INDICATE,
+ gatt_svc_chngd_read_cb, NULL, server);
- /* TODO: Add "Service Changed" characteristic and handle CCC */
+ server->svc_chngd_ccc = btd_gatt_server_add_ccc(server->adapter,
+ start_handle);
gatt_db_service_set_active(service, true);
}
--
2.2.0.rc0.207.ga3a616c
Registered service added/removed handlers in btd_gatt_server.
---
src/gatt-server.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/src/gatt-server.c b/src/gatt-server.c
index f9ff92d..22faece 100644
--- a/src/gatt-server.c
+++ b/src/gatt-server.c
@@ -47,6 +47,7 @@ static struct queue *servers = NULL;
struct btd_gatt_server {
struct btd_adapter *adapter;
struct gatt_db *db;
+ unsigned int db_id;
GIOChannel *le_io;
struct queue *device_states;
};
@@ -194,6 +195,7 @@ static void gatt_server_free(void *data)
/* TODO: Persistently store CCC states before freeing them */
queue_destroy(server->device_states, device_state_free);
+ gatt_db_unregister(server->db, server->db_id);
gatt_db_unref(server->db);
btd_adapter_unref(server->adapter);
free(server);
@@ -373,6 +375,43 @@ static void register_core_services(struct btd_gatt_server *server)
populate_gatt_service(server);
}
+static void gatt_db_service_added(struct gatt_db_attribute *attrib,
+ void *user_data)
+{
+ /* TODO: Send out service changed signal */
+}
+
+static bool ccc_match_service(const void *data, const void *match_data)
+{
+ const struct ccc_state *ccc = data;
+ const struct gatt_db_attribute *attrib = match_data;
+ uint16_t start, end;
+
+ if (!gatt_db_attribute_get_service_handles(attrib, &start, &end))
+ return false;
+
+ return ccc->handle >= start && ccc->handle <= end;
+}
+
+static void remove_device_ccc(void *data, void *user_data)
+{
+ struct device_state *state = data;
+
+ queue_remove_all(state->ccc_states, ccc_match_service, user_data, free);
+}
+
+static void gatt_db_service_removed(struct gatt_db_attribute *attrib,
+ void *user_data)
+{
+ struct btd_gatt_server *server = user_data;
+
+ DBG("Local GATT service removed");
+
+ queue_foreach(server->device_states, remove_device_ccc, attrib);
+
+ /* TODO: Send out service changed signal */
+}
+
bool btd_gatt_server_register_adapter(struct btd_adapter *adapter)
{
struct btd_gatt_server *server;
@@ -405,6 +444,12 @@ bool btd_gatt_server_register_adapter(struct btd_adapter *adapter)
if (!server->device_states)
goto fail;
+ server->db_id = gatt_db_register(server->db, gatt_db_service_added,
+ gatt_db_service_removed,
+ server, NULL);
+ if (!server->db_id)
+ goto fail;
+
addr = btd_adapter_get_address(adapter);
server->le_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR, addr,
--
2.2.0.rc0.207.ga3a616c
Added support to btd_gatt_server to track the states of CCC descriptors
on a per-client device basis. Added a new API for creating a CCC entry
for a given GATT service.
---
src/gatt-server.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
src/gatt-server.h | 3 +
2 files changed, 258 insertions(+), 5 deletions(-)
diff --git a/src/gatt-server.c b/src/gatt-server.c
index 14e335e..f9ff92d 100644
--- a/src/gatt-server.c
+++ b/src/gatt-server.c
@@ -48,8 +48,141 @@ struct btd_gatt_server {
struct btd_adapter *adapter;
struct gatt_db *db;
GIOChannel *le_io;
+ struct queue *device_states;
};
+struct device_state {
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ struct queue *ccc_states;
+};
+
+struct ccc_state {
+ uint16_t handle;
+ uint8_t value[2];
+};
+
+struct device_info {
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+};
+
+static bool dev_state_match(const void *a, const void *b)
+{
+ const struct device_state *dev_state = a;
+ const struct device_info *dev_info = b;
+
+ return bacmp(&dev_state->bdaddr, &dev_info->bdaddr) == 0 &&
+ dev_state->bdaddr_type == dev_info->bdaddr_type;
+}
+
+static struct device_state *find_device_state(struct btd_gatt_server *server,
+ bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+{
+ struct device_info dev_info;
+
+ memset(&dev_info, 0, sizeof(dev_info));
+
+ bacpy(&dev_info.bdaddr, bdaddr);
+ dev_info.bdaddr_type = bdaddr_type;
+
+ return queue_find(server->device_states, dev_state_match, &dev_info);
+}
+
+static bool ccc_state_match(const void *a, const void *b)
+{
+ const struct ccc_state *ccc = a;
+ uint16_t handle = PTR_TO_UINT(b);
+
+ return ccc->handle == handle;
+}
+
+static struct ccc_state *find_ccc_state(struct device_state *dev_state,
+ uint16_t handle)
+{
+ return queue_find(dev_state->ccc_states, ccc_state_match,
+ UINT_TO_PTR(handle));
+}
+
+static struct device_state *device_state_create(bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+{
+ struct device_state *dev_state;
+
+ dev_state = new0(struct device_state, 1);
+ if (!dev_state)
+ return NULL;
+
+ dev_state->ccc_states = queue_new();
+ if (!dev_state->ccc_states) {
+ free(dev_state);
+ return NULL;
+ }
+
+ bacpy(&dev_state->bdaddr, bdaddr);
+ dev_state->bdaddr_type = bdaddr_type;
+
+ return dev_state;
+}
+
+static struct device_state *get_device_state(struct btd_gatt_server *server,
+ bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+{
+ struct device_state *dev_state;
+
+ /*
+ * Find and return a device state. If a matching state doesn't exist,
+ * then create a new one.
+ */
+ dev_state = find_device_state(server, bdaddr, bdaddr_type);
+ if (dev_state)
+ return dev_state;
+
+ dev_state = device_state_create(bdaddr, bdaddr_type);
+ if (!dev_state)
+ return NULL;
+
+ queue_push_tail(server->device_states, dev_state);
+
+ return dev_state;
+}
+
+static struct ccc_state *get_ccc_state(struct btd_gatt_server *server,
+ bdaddr_t *bdaddr,
+ uint8_t bdaddr_type,
+ uint16_t handle)
+{
+ struct device_state *dev_state;
+ struct ccc_state *ccc;
+
+ dev_state = get_device_state(server, bdaddr, bdaddr_type);
+ if (!dev_state)
+ return NULL;
+
+ ccc = find_ccc_state(dev_state, handle);
+ if (ccc)
+ return ccc;
+
+ ccc = new0(struct ccc_state, 1);
+ if (!ccc)
+ return NULL;
+
+ ccc->handle = handle;
+ queue_push_tail(dev_state->ccc_states, ccc);
+
+ return ccc;
+}
+
+static void device_state_free(void *data)
+{
+ struct device_state *state = data;
+
+ queue_destroy(state->ccc_states, free);
+ free(state);
+}
+
static void gatt_server_free(void *data)
{
struct btd_gatt_server *server = data;
@@ -59,6 +192,8 @@ static void gatt_server_free(void *data)
g_io_channel_unref(server->le_io);
}
+ /* TODO: Persistently store CCC states before freeing them */
+ queue_destroy(server->device_states, device_state_free);
gatt_db_unref(server->db);
btd_adapter_unref(server->adapter);
free(server);
@@ -266,6 +401,10 @@ bool btd_gatt_server_register_adapter(struct btd_adapter *adapter)
if (!server->db)
goto fail;
+ server->device_states = queue_new();
+ if (!server->device_states)
+ goto fail;
+
addr = btd_adapter_get_address(adapter);
server->le_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR, addr,
@@ -299,18 +438,129 @@ void btd_gatt_server_unregister_adapter(struct btd_adapter *adapter)
queue_remove_all(servers, match_adapter, adapter, gatt_server_free);
}
-struct gatt_db *btd_gatt_server_get_db(struct btd_adapter *adapter)
+static struct btd_gatt_server *get_server(struct btd_adapter *adapter)
{
- struct btd_gatt_server *server;
-
if (!servers) {
error("GATT server not initialized");
return false;
}
- server = queue_find(servers, match_adapter, adapter);
- if (!server)
+ return queue_find(servers, match_adapter, adapter);
+}
+
+struct gatt_db *btd_gatt_server_get_db(struct btd_adapter *adapter)
+{
+ struct btd_gatt_server *server;
+
+ server = get_server(adapter);
+ if (!server) {
+ error("Adapter not registered");
return false;
+ }
return server->db;
}
+
+static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type, void *user_data)
+{
+ struct btd_gatt_server *server = user_data;
+ struct ccc_state *ccc;
+ uint16_t handle;
+ uint8_t ecode = 0;
+ const uint8_t *value = NULL;
+ size_t len = 0;
+
+ handle = gatt_db_attribute_get_handle(attrib);
+
+ DBG("CCC read called for handle: 0x%04x", handle);
+
+ if (offset > 2) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ ccc = get_ccc_state(server, bdaddr, bdaddr_type, handle);
+ if (!ccc) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ len -= offset;
+ value = len ? &ccc->value[offset] : NULL;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, ecode, value, len);
+}
+
+static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type, void *user_data)
+{
+ struct btd_gatt_server *server = user_data;
+ struct ccc_state *ccc;
+ uint16_t handle;
+ uint8_t ecode = 0;
+
+ handle = gatt_db_attribute_get_handle(attrib);
+
+ DBG("CCC read called for handle: 0x%04x", handle);
+
+ if (!value || len != 2) {
+ ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto done;
+ }
+
+ if (offset > 2) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ ccc = get_ccc_state(server, bdaddr, bdaddr_type, handle);
+ if (!ccc) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ /*
+ * TODO: Perform this after checking with a callback to the upper
+ * layer.
+ */
+ ccc->value[0] = value[0];
+ ccc->value[1] = value[1];
+
+done:
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+struct gatt_db_attribute *btd_gatt_server_add_ccc(struct btd_adapter *adapter,
+ uint16_t service_handle)
+{
+ struct btd_gatt_server *server;
+ struct gatt_db_attribute *service;
+ bt_uuid_t uuid;
+
+ if (!adapter || !service_handle)
+ return NULL;
+
+ server = get_server(adapter);
+ if (!server) {
+ error("Adapter not registered");
+ return NULL;
+ }
+
+ service = gatt_db_get_attribute(server->db, service_handle);
+ if (!service) {
+ error("No service exists with handle: 0x%04x", service_handle);
+ return NULL;
+ }
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ return gatt_db_service_add_descriptor(service, &uuid,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ gatt_ccc_read_cb, gatt_ccc_write_cb, server);
+}
diff --git a/src/gatt-server.h b/src/gatt-server.h
index f97ad05..9be2471 100644
--- a/src/gatt-server.h
+++ b/src/gatt-server.h
@@ -24,3 +24,6 @@ bool btd_gatt_server_register_adapter(struct btd_adapter *adapter);
void btd_gatt_server_unregister_adapter(struct btd_adapter *adapter);
struct gatt_db *btd_gatt_server_get_db(struct btd_adapter *adapter);
+
+struct gatt_db_attribute *btd_gatt_server_add_ccc(struct btd_adapter *adapter,
+ uint16_t service_handle);
--
2.2.0.rc0.207.ga3a616c
Modified src/adapter so that the UUIDs property includes UUIDs that
were added to the local GATT database.
---
src/adapter.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/src/adapter.c b/src/adapter.c
index db359ad..beb9fda 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -53,6 +53,8 @@
#include "lib/mgmt.h"
#include "src/shared/mgmt.h"
#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
#include "hcid.h"
#include "sdpd.h"
@@ -2189,16 +2191,37 @@ static gboolean property_get_discovering(const GDBusPropertyTable *property,
return TRUE;
}
+static void add_gatt_uuid(struct gatt_db_attribute *attrib, void *user_data)
+{
+ DBusMessageIter *iter = user_data;
+ bt_uuid_t uuid, u128;
+ char uuidstr[MAX_LEN_UUID_STR + 1];
+ const char *ptr = uuidstr;
+
+ if (!gatt_db_service_get_active(attrib))
+ return;
+
+ if (!gatt_db_attribute_get_service_uuid(attrib, &uuid))
+ return;
+
+ bt_uuid_to_uuid128(&uuid, &u128);
+ bt_uuid_to_string(&u128, uuidstr, sizeof(uuidstr));
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
+}
+
static gboolean property_get_uuids(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
struct btd_adapter *adapter = user_data;
DBusMessageIter entry;
sdp_list_t *l;
+ struct gatt_db *db;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING_AS_STRING, &entry);
+ /* SDP records */
for (l = adapter->services; l != NULL; l = l->next) {
sdp_record_t *rec = l->data;
char *uuid;
@@ -2212,6 +2235,11 @@ static gboolean property_get_uuids(const GDBusPropertyTable *property,
free(uuid);
}
+ /* GATT services */
+ db = btd_gatt_server_get_db(adapter);
+ if (db)
+ gatt_db_foreach_service(db, NULL, add_gatt_uuid, &entry);
+
dbus_message_iter_close_container(iter, &entry);
return TRUE;
--
2.2.0.rc0.207.ga3a616c
This patch introduces src/gatt-server.* that handles incoming ATT
connections, manages per-adapter shared/gatt-db instances, and routes
connections to the corresponding device object. This is the layer that
will handle all the CCC management and Service Changed handling.
---
Makefile.am | 1 +
src/adapter.c | 8 ++-
src/gatt-server.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/gatt-server.h | 26 +++++++
src/main.c | 3 +
5 files changed, 239 insertions(+), 2 deletions(-)
create mode 100644 src/gatt-server.c
create mode 100644 src/gatt-server.h
diff --git a/Makefile.am b/Makefile.am
index 60811f1..3c64426 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -167,6 +167,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
src/sdpd-server.c src/sdpd-request.c \
src/sdpd-service.c src/sdpd-database.c \
src/attrib-server.h src/attrib-server.c \
+ src/gatt-server.h src/gatt-server.c \
src/sdp-xml.h src/sdp-xml.c \
src/sdp-client.h src/sdp-client.c \
src/textfile.h src/textfile.c \
diff --git a/src/adapter.c b/src/adapter.c
index 1839286..db359ad 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -68,6 +68,7 @@
#include "attrib/att.h"
#include "attrib/gatt.h"
#include "attrib-server.h"
+#include "gatt-server.h"
#include "eir.h"
#define ADAPTER_INTERFACE "org.bluez.Adapter1"
@@ -302,6 +303,7 @@ static void dev_class_changed_callback(uint16_t index, uint16_t length,
appearance[1] = rp->val[1] & 0x1f; /* removes service class */
appearance[2] = rp->val[2];
+ /* TODO: Do this through btd_gatt_server instead */
attrib_gap_set(adapter, GATT_CHARAC_APPEARANCE, appearance, 2);
}
@@ -4014,6 +4016,7 @@ static void convert_sdp_entry(char *key, char *value, void *user_data)
if (record_has_uuid(rec, att_uuid))
goto failed;
+ /* TODO: Do this through btd_gatt_server */
if (!gatt_parse_record(rec, &uuid, &psm, &start, &end))
goto failed;
@@ -4548,7 +4551,7 @@ static void adapter_remove(struct btd_adapter *adapter)
adapter->devices = NULL;
unload_drivers(adapter);
- btd_adapter_gatt_server_stop(adapter);
+ btd_gatt_server_unregister_adapter(adapter);
g_slist_free(adapter->pin_callbacks);
adapter->pin_callbacks = NULL;
@@ -6590,7 +6593,8 @@ static int adapter_register(struct btd_adapter *adapter)
agent_unref(agent);
}
- btd_adapter_gatt_server_start(adapter);
+ if (!btd_gatt_server_register_adapter(adapter))
+ error("Failed to register adapter with GATT server");
load_config(adapter);
fix_storage(adapter);
diff --git a/src/gatt-server.c b/src/gatt-server.c
new file mode 100644
index 0000000..977a057
--- /dev/null
+++ b/src/gatt-server.c
@@ -0,0 +1,203 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Google Inc.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "lib/uuid.h"
+#include "btio/btio.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "log.h"
+#include "adapter.h"
+#include "device.h"
+#include "gatt-server.h"
+
+#ifndef ATT_CID
+#define ATT_CID 4
+#endif
+
+static struct queue *servers = NULL;
+
+struct btd_gatt_server {
+ struct btd_adapter *adapter;
+ struct gatt_db *db;
+ GIOChannel *le_io;
+};
+
+static void gatt_server_free(void *data)
+{
+ struct btd_gatt_server *server = data;
+
+ if (server->le_io) {
+ g_io_channel_shutdown(server->le_io, FALSE, NULL);
+ g_io_channel_unref(server->le_io);
+ }
+
+ gatt_db_unref(server->db);
+ btd_adapter_unref(server->adapter);
+ free(server);
+}
+
+bool btd_gatt_server_init(void)
+{
+
+ info("Initializing GATT server");
+
+ servers = queue_new();
+ if (!servers) {
+ error("Failed to set up local GATT server");
+ return false;
+ }
+
+ return true;
+}
+
+void btd_gatt_server_cleanup(void)
+{
+ info("Cleaning up GATT server");
+
+ queue_destroy(servers, gatt_server_free);
+ servers = NULL;
+}
+
+static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ uint8_t dst_type;
+ bdaddr_t src, dst;
+
+ DBG("New incoming LE ATT connection");
+
+ if (gerr) {
+ error("%s", gerr->message);
+ return;
+ }
+
+ bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST_TYPE, &dst_type,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ return;
+ }
+
+ adapter = adapter_find(&src);
+ if (!adapter)
+ return;
+
+ device = btd_adapter_get_device(adapter, &dst, dst_type);
+ if (!device)
+ return;
+
+ device_attach_att(device, io);
+}
+
+static bool match_adapter(const void *a, const void *b)
+{
+ const struct btd_gatt_server *server = a;
+ const struct btd_adapter *adapter = b;
+
+ return server->adapter == adapter;
+}
+
+bool btd_gatt_server_register_adapter(struct btd_adapter *adapter)
+{
+ struct btd_gatt_server *server;
+ GError *gerr = NULL;
+ const bdaddr_t *addr;
+
+ if (!adapter)
+ return false;
+
+ if (!servers) {
+ error("GATT server not initialized");
+ return false;
+ }
+
+ if (queue_find(servers, match_adapter, adapter)) {
+ error("Adapter already registered with GATT server");
+ return false;
+ }
+
+ server = new0(struct btd_gatt_server, 1);
+ if (!server)
+ return false;
+
+ server->adapter = btd_adapter_ref(adapter);
+ server->db = gatt_db_new();
+ if (!server->db)
+ goto fail;
+
+ addr = btd_adapter_get_address(adapter);
+ server->le_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, addr,
+ BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+ BT_IO_OPT_CID, ATT_CID,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (!server->le_io) {
+ error("Failed to start listening: %s", gerr->message);
+ g_error_free(gerr);
+ goto fail;
+ }
+
+ queue_push_tail(servers, server);
+
+ /* TODO: Set up GAP/GATT services */
+
+ return true;
+
+fail:
+ gatt_server_free(server);
+
+ return false;
+}
+
+void btd_gatt_server_unregister_adapter(struct btd_adapter *adapter)
+{
+ if (!adapter || !servers)
+ return;
+
+ queue_remove_all(servers, match_adapter, adapter, gatt_server_free);
+}
+
+struct gatt_db *btd_gatt_server_get_db(struct btd_adapter *adapter)
+{
+ struct btd_gatt_server *server;
+
+ if (!servers) {
+ error("GATT server not initialized");
+ return false;
+ }
+
+ server = queue_find(servers, match_adapter, adapter);
+ if (!server)
+ return false;
+
+ return server->db;
+}
diff --git a/src/gatt-server.h b/src/gatt-server.h
new file mode 100644
index 0000000..f97ad05
--- /dev/null
+++ b/src/gatt-server.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Google Inc.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+bool btd_gatt_server_init(void);
+void btd_gatt_server_cleanup(void);
+
+bool btd_gatt_server_register_adapter(struct btd_adapter *adapter);
+void btd_gatt_server_unregister_adapter(struct btd_adapter *adapter);
+
+struct gatt_db *btd_gatt_server_get_db(struct btd_adapter *adapter);
diff --git a/src/main.c b/src/main.c
index 061060d..510388a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -56,6 +56,7 @@
#include "agent.h"
#include "profile.h"
#include "gatt.h"
+#include "gatt-server.h"
#include "systemd.h"
#define BLUEZ_NAME "org.bluez"
@@ -579,6 +580,7 @@ int main(int argc, char *argv[])
g_dbus_set_flags(gdbus_flags);
gatt_init();
+ btd_gatt_server_init();
if (adapter_init() < 0) {
error("Adapter handling initialization failed");
@@ -642,6 +644,7 @@ int main(int argc, char *argv[])
adapter_cleanup();
+ btd_gatt_server_cleanup();
gatt_cleanup();
rfkill_exit();
--
2.2.0.rc0.207.ga3a616c
This patch adds the GATT & GAP services to the local GATT database.
---
src/gatt-server.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 114 insertions(+), 1 deletion(-)
diff --git a/src/gatt-server.c b/src/gatt-server.c
index 977a057..14e335e 100644
--- a/src/gatt-server.c
+++ b/src/gatt-server.c
@@ -29,6 +29,7 @@
#include "src/shared/util.h"
#include "src/shared/queue.h"
#include "src/shared/gatt-db.h"
+#include "src/shared/att-types.h"
#include "log.h"
#include "adapter.h"
#include "device.h"
@@ -38,6 +39,9 @@
#define ATT_CID 4
#endif
+#define UUID_GAP 0x1800
+#define UUID_GATT 0x1801
+
static struct queue *servers = NULL;
struct btd_gatt_server {
@@ -125,6 +129,115 @@ static bool match_adapter(const void *a, const void *b)
return server->adapter == adapter;
}
+static void gap_device_name_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type, void *user_data)
+{
+ struct btd_gatt_server *server = user_data;
+ uint8_t error = 0;
+ size_t len = 0;
+ const uint8_t *value = NULL;
+ const char *device_name;
+
+ DBG("GAP Device Name read request\n");
+
+ device_name = btd_adapter_get_name(server->adapter);
+ len = strlen(device_name);
+
+ if (offset > len) {
+ error = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ len -= offset;
+ value = len ? (const uint8_t *) &device_name[offset] : NULL;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+static void gap_appearance_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type, void *user_data)
+{
+ struct btd_gatt_server *server = user_data;
+ uint8_t error = 0;
+ size_t len = 2;
+ const uint8_t *value = NULL;
+ uint8_t appearance[2];
+ uint32_t dev_class;
+
+ DBG("GAP Appearance read request\n");
+
+ dev_class = btd_adapter_get_class(server->adapter);
+
+ if (offset > 2) {
+ error = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ appearance[0] = dev_class & 0x00ff;
+ appearance[1] = (dev_class >> 8) & 0x001f;
+
+ len -= offset;
+ value = len ? &appearance[offset] : NULL;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+static void populate_gap_service(struct btd_gatt_server *server)
+{
+ bt_uuid_t uuid;
+ struct gatt_db_attribute *service;
+
+ /* Add the GAP service */
+ bt_uuid16_create(&uuid, UUID_GAP);
+ service = gatt_db_add_service(server->db, &uuid, true, 5);
+
+ /*
+ * Device Name characteristic.
+ */
+ bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+ gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ gap_device_name_read_cb,
+ NULL, server);
+
+ /*
+ * Device Appearance characteristic.
+ */
+ bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+ gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ gap_appearance_read_cb,
+ NULL, server);
+
+ gatt_db_service_set_active(service, true);
+}
+
+static void populate_gatt_service(struct btd_gatt_server *server)
+{
+ bt_uuid_t uuid;
+ struct gatt_db_attribute *service;
+
+ /* Add the GATT service */
+ bt_uuid16_create(&uuid, UUID_GATT);
+ service = gatt_db_add_service(server->db, &uuid, true, 1);
+
+ /* TODO: Add "Service Changed" characteristic and handle CCC */
+
+ gatt_db_service_set_active(service, true);
+}
+
+static void register_core_services(struct btd_gatt_server *server)
+{
+ populate_gap_service(server);
+ populate_gatt_service(server);
+}
+
bool btd_gatt_server_register_adapter(struct btd_adapter *adapter)
{
struct btd_gatt_server *server;
@@ -168,7 +281,7 @@ bool btd_gatt_server_register_adapter(struct btd_adapter *adapter)
queue_push_tail(servers, server);
- /* TODO: Set up GAP/GATT services */
+ register_core_services(server);
return true;
--
2.2.0.rc0.207.ga3a616c
With this patch, btd_device now creates a bt_gatt_server and attaches
it to the ATT transport which coexists with a bt_gatt_client.
---
src/device.c | 45 +++++++++++++++++++++++++++++----------------
1 file changed, 29 insertions(+), 16 deletions(-)
diff --git a/src/device.c b/src/device.c
index a28d6fb..b8b89cd 100644
--- a/src/device.c
+++ b/src/device.c
@@ -51,11 +51,13 @@
#include "src/shared/queue.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-client.h"
+#include "src/shared/gatt-server.h"
#include "btio/btio.h"
#include "lib/mgmt.h"
#include "attrib/att.h"
#include "hcid.h"
#include "adapter.h"
+#include "gatt-server.h"
#include "attrib/gattrib.h"
#include "attio.h"
#include "device.h"
@@ -210,7 +212,6 @@ struct btd_device {
GAttrib *attrib;
GSList *attios;
GSList *attios_offline;
- guint attachid; /* Attrib server attach */
struct bt_att *att; /* The new ATT transport */
uint16_t att_mtu; /* The ATT MTU */
@@ -223,6 +224,7 @@ struct btd_device {
*/
struct gatt_db *db; /* GATT db cache */
struct bt_gatt_client *client; /* GATT client instance */
+ struct bt_gatt_server *server; /* GATT server instance */
struct btd_gatt_client *client_dbus;
@@ -515,6 +517,15 @@ static void gatt_client_cleanup(struct btd_device *device)
gatt_db_clear(device->db);
}
+static void gatt_server_cleanup(struct btd_device *device)
+{
+ if (!device->server)
+ return;
+
+ bt_gatt_server_unref(device->server);
+ device->server = NULL;
+}
+
static void attio_cleanup(struct btd_device *device)
{
if (device->att_disconn_id)
@@ -528,6 +539,7 @@ static void attio_cleanup(struct btd_device *device)
}
gatt_client_cleanup(device);
+ gatt_server_cleanup(device);
if (device->att) {
bt_att_unref(device->att);
@@ -538,14 +550,6 @@ static void attio_cleanup(struct btd_device *device)
GAttrib *attrib = device->attrib;
device->attrib = NULL;
-
- if (device->attachid) {
- guint attachid = device->attachid;
-
- device->attachid = 0;
- attrib_channel_detach(attrib, attachid);
- }
-
g_attrib_cancel_all(attrib);
g_attrib_unref(attrib);
}
@@ -3950,6 +3954,20 @@ static void gatt_client_init(struct btd_device *device)
}
}
+static void gatt_server_init(struct btd_device *device, struct gatt_db *db)
+{
+ if (!db) {
+ error("No local GATT database exists for this adapter");
+ return;
+ }
+
+ gatt_server_cleanup(device);
+
+ device->server = bt_gatt_server_new(db, device->att, device->att_mtu);
+ if (!device->server)
+ error("Failed to initialize bt_gatt_server");
+}
+
bool device_attach_att(struct btd_device *dev, GIOChannel *io)
{
GError *gerr = NULL;
@@ -3989,23 +4007,18 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
return false;
}
- dev->attachid = attrib_channel_attach(attrib);
- if (dev->attachid == 0) {
- g_attrib_unref(attrib);
- error("Attribute server attach failure!");
- return false;
- }
-
dev->attrib = attrib;
dev->att = g_attrib_get_att(attrib);
bt_att_ref(dev->att);
+ bt_att_set_bdaddr(dev->att, &dev->bdaddr, dev->bdaddr_type);
dev->att_disconn_id = bt_att_register_disconnect(dev->att,
att_disconnected_cb, dev, NULL);
bt_att_set_close_on_unref(dev->att, true);
gatt_client_init(dev);
+ gatt_server_init(dev, btd_gatt_server_get_db(dev->adapter));
/*
* Remove the device from the connect_list and give the passive
--
2.2.0.rc0.207.ga3a616c
Added a bdaddr_type parameter to gatt_db's read/write functions and
callbacks, in addition to the bdaddr_t* parameter.
---
android/gatt.c | 32 +++++++++++++------------
src/gatt-client.c | 20 +++++++++-------
src/shared/gatt-db.c | 6 +++--
src/shared/gatt-db.h | 4 ++++
src/shared/gatt-server.c | 62 +++++++++++++++++++++++++++++++++++++-----------
tools/btgatt-server.c | 22 ++++++++---------
unit/test-gatt.c | 7 +++---
7 files changed, 99 insertions(+), 54 deletions(-)
diff --git a/android/gatt.c b/android/gatt.c
index a36922c..fd98f84 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -4889,7 +4889,8 @@ static void read_requested_attributes(void *data, void *user_data)
gatt_db_attribute_read(attrib, resp_data->offset, process_data->opcode,
&process_data->device->bdaddr,
- attribute_read_cb, resp_data);
+ 0, attribute_read_cb,
+ resp_data);
}
static void process_dev_pending_requests(struct gatt_device *device,
@@ -4937,7 +4938,7 @@ static struct pending_trans_data *conn_add_transact(struct app_connection *conn,
static void read_cb(struct gatt_db_attribute *attrib, unsigned int id,
uint16_t offset, uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
struct pending_trans_data *transaction;
struct hal_ev_gatt_server_request_read ev;
@@ -4985,7 +4986,8 @@ failed:
static void write_cb(struct gatt_db_attribute *attrib, unsigned int id,
uint16_t offset, const uint8_t *value, size_t len,
- uint8_t opcode, bdaddr_t *bdaddr, void *user_data)
+ uint8_t opcode, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ void *user_data)
{
uint8_t buf[IPC_MTU];
struct hal_ev_gatt_server_request_write *ev = (void *) buf;
@@ -6401,7 +6403,7 @@ static void write_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
if (check_device_permissions(dev, cmd[0], permissions))
return;
- gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0], &dev->bdaddr,
+ gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0], &dev->bdaddr, 0,
write_confirm, NULL);
}
@@ -6474,7 +6476,7 @@ static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
/* Signature OK, proceed with write */
bt_update_sign_counter(&dev->bdaddr, REMOTE_CSRK, r_sign_cnt);
gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
- &dev->bdaddr, write_confirm, NULL);
+ &dev->bdaddr, 0, write_confirm, NULL);
}
}
@@ -6534,7 +6536,7 @@ static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len,
}
if (!gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
- &dev->bdaddr, attribute_write_cb,
+ &dev->bdaddr, 0, attribute_write_cb,
data)) {
queue_remove(dev->pending_requests, data);
free(data);
@@ -6594,7 +6596,7 @@ static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len,
data->length = vlen;
if (!gatt_db_attribute_write(attrib, offset, value, vlen, cmd[0],
- &dev->bdaddr, attribute_write_cb,
+ &dev->bdaddr, 0, attribute_write_cb,
data)) {
queue_remove(dev->pending_requests, data);
g_free(data->value);
@@ -6817,7 +6819,7 @@ static struct gap_srvc_handles gap_srvc_data;
static void device_name_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
const char *name = bt_get_adapter_name();
@@ -6857,7 +6859,7 @@ static void register_gap_service(void)
value = cpu_to_le16(APPEARANCE_GENERIC_PHONE);
gatt_db_attribute_write(gap_srvc_data.appear, 0,
(void *) &value, sizeof(value),
- ATT_OP_WRITE_REQ, NULL,
+ ATT_OP_WRITE_REQ, NULL, 0,
write_confirm, NULL);
}
@@ -6875,7 +6877,7 @@ static void register_gap_service(void)
value = PERIPHERAL_PRIVACY_DISABLE;
gatt_db_attribute_write(gap_srvc_data.priv, 0,
&value, sizeof(value),
- ATT_OP_WRITE_REQ, NULL,
+ ATT_OP_WRITE_REQ, NULL, 0,
write_confirm, NULL);
}
@@ -6893,7 +6895,7 @@ static void register_gap_service(void)
static void device_info_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
char *buf = user_data;
@@ -6903,7 +6905,7 @@ static void device_info_read_cb(struct gatt_db_attribute *attrib,
static void device_info_read_system_id_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
uint8_t pdu[8];
@@ -6915,7 +6917,7 @@ static void device_info_read_system_id_cb(struct gatt_db_attribute *attrib,
static void device_info_read_pnp_id_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
uint8_t pdu[7];
@@ -7029,7 +7031,7 @@ static void gatt_srvc_change_write_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
struct gatt_device *dev;
@@ -7061,7 +7063,7 @@ static void gatt_srvc_change_write_cb(struct gatt_db_attribute *attrib,
static void gatt_srvc_change_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
struct gatt_device *dev;
uint8_t pdu[2];
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 9811bd8..ed6bd86 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -34,6 +34,7 @@
#include "adapter.h"
#include "device.h"
#include "lib/uuid.h"
+#include "lib/bluetooth.h"
#include "src/shared/queue.h"
#include "src/shared/att.h"
#include "src/shared/gatt-db.h"
@@ -160,7 +161,7 @@ static gboolean descriptor_get_value(const GDBusPropertyTable *property,
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
- gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_cb, &array);
+ gatt_db_attribute_read(desc->attr, 0, 0, NULL, 0, read_cb, &array);
dbus_message_iter_close_container(iter, &array);
@@ -187,7 +188,7 @@ static gboolean descriptor_value_exists(const GDBusPropertyTable *property,
struct descriptor *desc = data;
gboolean ret;
- gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_check_cb, &ret);
+ gatt_db_attribute_read(desc->attr, 0, 0, NULL, 0, read_check_cb, &ret);
return ret;
}
@@ -355,7 +356,7 @@ static void desc_read_cb(bool success, uint8_t att_ecode,
gatt_db_attribute_reset(desc->attr);
gatt_db_attribute_write(desc->attr, op->offset, value, length, 0, NULL,
- write_descriptor_cb, desc);
+ 0, write_descriptor_cb, desc);
/*
* If the value length is exactly MTU-1, then we may not have read the
@@ -378,7 +379,7 @@ static void desc_read_cb(bool success, uint8_t att_ecode,
desc->read_id = 0;
/* Read the stored data from db */
- gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_op_cb, op);
+ gatt_db_attribute_read(desc->attr, 0, 0, NULL, 0, read_op_cb, op);
}
static DBusMessage *descriptor_read_value(DBusConnection *conn,
@@ -680,7 +681,7 @@ static gboolean characteristic_get_value(const GDBusPropertyTable *property,
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
- gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_cb, &array);
+ gatt_db_attribute_read(chrc->attr, 0, 0, NULL, 0, read_cb, &array);
dbus_message_iter_close_container(iter, &array);
@@ -693,7 +694,7 @@ static gboolean characteristic_value_exists(const GDBusPropertyTable *property,
struct characteristic *chrc = data;
gboolean ret;
- gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_check_cb, &ret);
+ gatt_db_attribute_read(chrc->attr, 0, 0, NULL, 0, read_check_cb, &ret);
return TRUE;
}
@@ -789,7 +790,8 @@ static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
gatt_db_attribute_reset(chrc->attr);
gatt_db_attribute_write(chrc->attr, op->offset, value, length, 0, NULL,
- write_characteristic_cb, chrc);
+ 0, write_characteristic_cb,
+ chrc);
/*
* If the value length is exactly MTU-1, then we may not have read the
@@ -812,7 +814,7 @@ static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
chrc->read_id = 0;
/* Read the stored data from db */
- gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_op_cb, op);
+ gatt_db_attribute_read(chrc->attr, 0, 0, NULL, 0, read_op_cb, op);
}
static DBusMessage *characteristic_read_value(DBusConnection *conn,
@@ -1065,7 +1067,7 @@ static void notify_cb(uint16_t value_handle, const uint8_t *value,
* applications.
*/
gatt_db_attribute_reset(chrc->attr);
- gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
+ gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL, 0,
write_characteristic_cb, chrc);
}
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index f72d58e..cd28689 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -1428,6 +1428,7 @@ static bool read_timeout(void *user_data)
bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset,
uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type,
gatt_db_attribute_read_t func, void *user_data)
{
uint8_t *value;
@@ -1452,7 +1453,7 @@ bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset,
queue_push_tail(attrib->pending_reads, p);
attrib->read_func(attrib, p->id, offset, opcode, bdaddr,
- attrib->user_data);
+ bdaddr_type, attrib->user_data);
return true;
}
@@ -1523,6 +1524,7 @@ static bool write_timeout(void *user_data)
bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset,
const uint8_t *value, size_t len,
uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type,
gatt_db_attribute_write_t func,
void *user_data)
{
@@ -1546,7 +1548,7 @@ bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset,
queue_push_tail(attrib->pending_writes, p);
attrib->write_func(attrib, p->id, offset, value, len, opcode,
- bdaddr, attrib->user_data);
+ bdaddr, bdaddr_type, attrib->user_data);
return true;
}
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 37df4d5..b5f190c 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -51,12 +51,14 @@ struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db,
typedef void (*gatt_db_read_t) (struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type,
void *user_data);
typedef void (*gatt_db_write_t) (struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type,
void *user_data);
struct gatt_db_attribute *
@@ -197,6 +199,7 @@ typedef void (*gatt_db_attribute_read_t) (struct gatt_db_attribute *attrib,
bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset,
uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type,
gatt_db_attribute_read_t func, void *user_data);
bool gatt_db_attribute_read_result(struct gatt_db_attribute *attrib,
@@ -209,6 +212,7 @@ typedef void (*gatt_db_attribute_write_t) (struct gatt_db_attribute *attrib,
bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset,
const uint8_t *value, size_t len,
uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type,
gatt_db_attribute_write_t func,
void *user_data);
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 8f7b5cd..34f82d6 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -24,8 +24,9 @@
#include <sys/uio.h>
#include <errno.h>
-#include "src/shared/att.h"
#include "lib/uuid.h"
+#include "lib/bluetooth.h"
+#include "src/shared/att.h"
#include "src/shared/queue.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-server.h"
@@ -168,8 +169,10 @@ static void attribute_read_cb(struct gatt_db_attribute *attrib, int err,
}
static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q,
- uint16_t mtu,
- uint8_t *pdu, uint16_t *len)
+ bdaddr_t *bdaddr,
+ uint8_t bdaddr_type,
+ uint16_t mtu, uint8_t *pdu,
+ uint16_t *len)
{
int iter = 0;
uint16_t start_handle, end_handle;
@@ -190,7 +193,8 @@ static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q,
*/
if (!gatt_db_attribute_read(attrib, 0,
BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
- NULL, attribute_read_cb,
+ bdaddr, bdaddr_type,
+ attribute_read_cb,
&value) || !value.iov_len)
return false;
@@ -239,6 +243,8 @@ static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
uint8_t ecode = 0;
uint16_t ehandle = 0;
struct queue *q = NULL;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
if (length != 6 && length != 20) {
ecode = BT_ATT_ERROR_INVALID_PDU;
@@ -291,8 +297,10 @@ static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
goto error;
}
- if (!encode_read_by_grp_type_rsp(server->db, q, mtu, rsp_pdu,
- &rsp_len)) {
+ bt_att_get_bdaddr(server->att, &bdaddr, &bdaddr_type);
+
+ if (!encode_read_by_grp_type_rsp(server->db, q, &bdaddr, bdaddr_type,
+ mtu, rsp_pdu, &rsp_len)) {
ecode = BT_ATT_ERROR_UNLIKELY;
goto error;
}
@@ -381,6 +389,8 @@ static void process_read_by_type(struct async_read_op *op)
uint8_t ecode;
struct gatt_db_attribute *attr;
uint32_t perm;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
attr = queue_pop_head(op->db_data);
@@ -406,8 +416,10 @@ static void process_read_by_type(struct async_read_op *op)
goto error;
}
- if (gatt_db_attribute_read(attr, 0, op->opcode, NULL,
- read_by_type_read_complete_cb, op))
+ bt_att_get_bdaddr(server->att, &bdaddr, &bdaddr_type);
+
+ if (gatt_db_attribute_read(attr, 0, op->opcode, &bdaddr, bdaddr_type,
+ read_by_type_read_complete_cb, op))
return;
ecode = BT_ATT_ERROR_UNLIKELY;
@@ -772,6 +784,8 @@ static void write_cb(uint8_t opcode, const void *pdu,
struct async_write_op *op = NULL;
uint8_t ecode;
uint32_t perm;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
if (length < 2) {
ecode = BT_ATT_ERROR_INVALID_PDU;
@@ -812,8 +826,11 @@ static void write_cb(uint8_t opcode, const void *pdu,
op->opcode = opcode;
server->pending_write_op = op;
+ bt_att_get_bdaddr(server->att, &bdaddr, &bdaddr_type);
+
if (gatt_db_attribute_write(attr, 0, pdu + 2, length - 2, opcode,
- NULL, write_complete_cb, op))
+ &bdaddr, bdaddr_type,
+ write_complete_cb, op))
return;
if (op)
@@ -891,6 +908,8 @@ static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode,
uint8_t ecode;
uint32_t perm;
struct async_read_op *op = NULL;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
attr = gatt_db_get_attribute(server->db, handle);
if (!attr) {
@@ -925,7 +944,9 @@ static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode,
op->server = server;
server->pending_read_op = op;
- if (gatt_db_attribute_read(attr, offset, opcode, NULL,
+ bt_att_get_bdaddr(server->att, &bdaddr, &bdaddr_type);
+
+ if (gatt_db_attribute_read(attr, offset, opcode, &bdaddr, bdaddr_type,
read_complete_cb, op))
return;
@@ -999,8 +1020,9 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
struct read_multiple_resp_data *data = user_data;
struct gatt_db_attribute *next_attr;
uint32_t perm;
-
uint16_t handle = gatt_db_attribute_get_handle(attr);
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
if (err != 0) {
bt_att_send_error_rsp(data->server->att,
@@ -1051,7 +1073,10 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
return;
}
- if (!gatt_db_attribute_read(next_attr, 0, BT_ATT_OP_READ_MULT_REQ, NULL,
+ bt_att_get_bdaddr(data->server->att, &bdaddr, &bdaddr_type);
+
+ if (!gatt_db_attribute_read(next_attr, 0, BT_ATT_OP_READ_MULT_REQ,
+ &bdaddr, bdaddr_type,
read_multiple_complete_cb, data)) {
bt_att_send_error_rsp(data->server->att,
BT_ATT_OP_READ_MULT_REQ,
@@ -1069,6 +1094,8 @@ static void read_multiple_cb(uint8_t opcode, const void *pdu,
struct read_multiple_resp_data data;
uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
size_t i = 0;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
data.handles = NULL;
data.rsp_data = NULL;
@@ -1107,7 +1134,9 @@ static void read_multiple_cb(uint8_t opcode, const void *pdu,
goto error;
}
- if (gatt_db_attribute_read(attr, 0, opcode, NULL,
+ bt_att_get_bdaddr(server->att, &bdaddr, &bdaddr_type);
+
+ if (gatt_db_attribute_read(attr, 0, opcode, &bdaddr, bdaddr_type,
read_multiple_complete_cb, &data))
return;
@@ -1215,6 +1244,8 @@ static void exec_next_prep_write(struct bt_gatt_server *server,
struct prep_write_data *next = NULL;
struct gatt_db_attribute *attr;
bool status;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
if (err)
goto error;
@@ -1232,9 +1263,12 @@ static void exec_next_prep_write(struct bt_gatt_server *server,
goto error;
}
+ bt_att_get_bdaddr(server->att, &bdaddr, &bdaddr_type);
+
status = gatt_db_attribute_write(attr, next->offset,
next->value, next->length,
- BT_ATT_OP_EXEC_WRITE_REQ, NULL,
+ BT_ATT_OP_EXEC_WRITE_REQ,
+ &bdaddr, bdaddr_type,
exec_write_complete_cb, server);
prep_write_data_destroy(next);
diff --git a/tools/btgatt-server.c b/tools/btgatt-server.c
index d27cf10..4a78b44 100644
--- a/tools/btgatt-server.c
+++ b/tools/btgatt-server.c
@@ -126,7 +126,7 @@ static void gatt_debug_cb(const char *str, void *user_data)
static void gap_device_name_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
struct server *server = user_data;
uint8_t error = 0;
@@ -153,7 +153,7 @@ static void gap_device_name_write_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
struct server *server = user_data;
uint8_t error = 0;
@@ -197,7 +197,7 @@ done:
static void gap_device_name_ext_prop_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
uint8_t value[2];
@@ -212,7 +212,7 @@ static void gap_device_name_ext_prop_read_cb(struct gatt_db_attribute *attrib,
static void gatt_service_changed_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
PRLOG("Service Changed Read called\n");
@@ -222,7 +222,7 @@ static void gatt_service_changed_cb(struct gatt_db_attribute *attrib,
static void gatt_svc_chngd_ccc_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
struct server *server = user_data;
uint8_t value[2];
@@ -239,7 +239,7 @@ static void gatt_svc_chngd_ccc_write_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
struct server *server = user_data;
uint8_t ecode = 0;
@@ -273,7 +273,7 @@ done:
static void hr_msrmt_ccc_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
struct server *server = user_data;
uint8_t value[2];
@@ -327,7 +327,7 @@ static void hr_msrmt_ccc_write_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
struct server *server = user_data;
uint8_t ecode = 0;
@@ -367,7 +367,7 @@ static void hr_control_point_write_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
uint8_t opcode, bdaddr_t *bdaddr,
- void *user_data)
+ uint8_t bdaddr_type, void *user_data)
{
struct server *server = user_data;
uint8_t ecode = 0;
@@ -446,7 +446,7 @@ static void populate_gap_service(struct server *server)
gatt_db_attribute_write(tmp, 0, (void *) &appearance,
sizeof(appearance),
BT_ATT_OP_WRITE_REQ,
- NULL, confirm_write,
+ NULL, 0, confirm_write,
NULL);
gatt_db_service_set_active(service, true);
@@ -514,7 +514,7 @@ static void populate_hr_service(struct server *server)
NULL, NULL, server);
gatt_db_attribute_write(body, 0, (void *) &body_loc, sizeof(body_loc),
BT_ATT_OP_WRITE_REQ,
- NULL, confirm_write,
+ NULL, 0, confirm_write,
NULL);
/* HR Control Point Characteristic */
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index cd90d83..4755bcd 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -36,6 +36,7 @@
#include <glib.h>
#include "lib/uuid.h"
+#include "lib/bluetooth.h"
#include "src/shared/util.h"
#include "src/shared/att.h"
#include "src/shared/gatt-helpers.h"
@@ -691,8 +692,8 @@ static struct gatt_db_attribute *add_char_with_value(struct gatt_db *db,
g_assert(attrib != NULL);
- gatt_db_attribute_write(attrib, 0, value, len, 0x00, NULL, att_write_cb,
- NULL);
+ gatt_db_attribute_write(attrib, 0, value, len, 0x00, NULL, 0,
+ att_write_cb, NULL);
return attrib;
}
@@ -707,7 +708,7 @@ add_desc_with_value(struct gatt_db_attribute *att, bt_uuid_t *uuid,
desc_att = gatt_db_service_add_descriptor(att, uuid, att_perms, NULL,
NULL, NULL);
- gatt_db_attribute_write(desc_att, 0, value, len, 0x00, NULL,
+ gatt_db_attribute_write(desc_att, 0, value, len, 0x00, NULL, 0,
att_write_cb, NULL);
return desc_att;
--
2.2.0.rc0.207.ga3a616c
Added functions to get and set bdaddr_t* and bdaddr_type on a bt_att
structure.
---
src/shared/att.c | 28 ++++++++++++++++++++++++++++
src/shared/att.h | 3 +++
src/shared/gatt-client.c | 3 ++-
src/shared/gatt-helpers.c | 3 ++-
4 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/src/shared/att.c b/src/shared/att.c
index a98909e..5b04c1f 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -76,6 +76,9 @@ struct bt_att {
unsigned int next_send_id; /* IDs for "send" ops */
unsigned int next_reg_id; /* IDs for registered callbacks */
+ bdaddr_t bdaddr; /* BDADDR assigned to this transport */
+ uint8_t bdaddr_type;
+
bt_att_timeout_func_t timeout_callback;
bt_att_destroy_func_t timeout_destroy;
void *timeout_data;
@@ -889,6 +892,31 @@ bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close)
return io_set_close_on_destroy(att->io, do_close);
}
+bool bt_att_get_bdaddr(struct bt_att *att, bdaddr_t *bdaddr, uint8_t *type)
+{
+ if (!att)
+ return false;
+
+ if (bdaddr)
+ *bdaddr = att->bdaddr;
+
+ if (type)
+ *type = att->bdaddr_type;
+
+ return true;
+}
+
+bool bt_att_set_bdaddr(struct bt_att *att, bdaddr_t *addr, uint8_t type)
+{
+ if (!att || !addr)
+ return false;
+
+ bacpy(&att->bdaddr, addr);
+ att->bdaddr_type = type;
+
+ return true;
+}
+
bool bt_att_set_debug(struct bt_att *att, bt_att_debug_func_t callback,
void *user_data, bt_att_destroy_func_t destroy)
{
diff --git a/src/shared/att.h b/src/shared/att.h
index cd00a1e..48241fc 100644
--- a/src/shared/att.h
+++ b/src/shared/att.h
@@ -35,6 +35,9 @@ void bt_att_unref(struct bt_att *att);
bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close);
+bool bt_att_get_bdaddr(struct bt_att *att, bdaddr_t *bdaddr, uint8_t *type);
+bool bt_att_set_bdaddr(struct bt_att *att, bdaddr_t *addr, uint8_t type);
+
typedef void (*bt_att_response_func_t)(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data);
typedef void (*bt_att_notify_func_t)(uint8_t opcode, const void *pdu,
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index d0fc054..0c8fa13 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -21,8 +21,9 @@
*
*/
-#include "src/shared/att.h"
#include "lib/uuid.h"
+#include "lib/bluetooth.h"
+#include "src/shared/att.h"
#include "src/shared/gatt-helpers.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index a33f960..b469116 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -26,9 +26,10 @@
#include <config.h>
#endif
+#include "lib/uuid.h"
+#include "lib/bluetooth.h"
#include "src/shared/queue.h"
#include "src/shared/att.h"
-#include "lib/uuid.h"
#include "src/shared/gatt-helpers.h"
#include "src/shared/util.h"
--
2.2.0.rc0.207.ga3a616c