2014-12-19 01:24:32

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ v5 0/7] core: Use shared/gatt-client

*v5:
- Made probing a bit smarter, so that profiles are only probed once when
necessary and they are removed if they no longer valid after non-bonded
discovery.

*v4:
- Removed gatt-callbacks in favor of using btd_profile probe and remove
functions. Added the new btd_profile function "accept" to notify profiles
that gatt-client is ready.

- Using device_remove_profile to unregister btd_service's when services are
removed from the client database. This happens on Service Changed events and
on disconnection if the device is not bonded.

- Removed immediated disconnection logic that triggers when no attio callbacks
are registered after probe.

- Other minor fixes to shared/gatt and shared/att.

*v3:
- Renamed device_attach_att_transport to device_attach_att.

*v2:
- Rebased.

*v1:
- Addressed comments by Johan and Luiz.

Arman Uguray (7):
core: device: Don't disconnect if attios not set
core: device: Add getters for GATT db and client
core: device: Make profile calls in GATT events
profiles/gatt: Don't handle GATT service.
profiles/gatt: Rename profile to gap.
profiles/gap: Rewrite using bt_gatt_client.
profiles/gap: Add Google copyright.

Makefile.plugins | 4 +-
profiles/gap/gas.c | 339 ++++++++++++++++++++++++++++++++++++++
profiles/gatt/gas.c | 457 ----------------------------------------------------
src/device.c | 390 +++++++++++++++++++++++++++++++++++++-------
src/device.h | 2 +
5 files changed, 676 insertions(+), 516 deletions(-)
create mode 100644 profiles/gap/gas.c
delete mode 100644 profiles/gatt/gas.c

--
2.2.0.rc0.207.ga3a616c



2014-12-19 21:00:25

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ v5 0/7] core: Use shared/gatt-client

Hi Arman,

On Thu, Dec 18, 2014 at 11:24 PM, Arman Uguray <[email protected]> wrote:
> *v5:
> - Made probing a bit smarter, so that profiles are only probed once when
> necessary and they are removed if they no longer valid after non-bonded
> discovery.
>
> *v4:
> - Removed gatt-callbacks in favor of using btd_profile probe and remove
> functions. Added the new btd_profile function "accept" to notify profiles
> that gatt-client is ready.
>
> - Using device_remove_profile to unregister btd_service's when services are
> removed from the client database. This happens on Service Changed events and
> on disconnection if the device is not bonded.
>
> - Removed immediated disconnection logic that triggers when no attio callbacks
> are registered after probe.
>
> - Other minor fixes to shared/gatt and shared/att.
>
> *v3:
> - Renamed device_attach_att_transport to device_attach_att.
>
> *v2:
> - Rebased.
>
> *v1:
> - Addressed comments by Johan and Luiz.
>
> Arman Uguray (7):
> core: device: Don't disconnect if attios not set
> core: device: Add getters for GATT db and client
> core: device: Make profile calls in GATT events
> profiles/gatt: Don't handle GATT service.
> profiles/gatt: Rename profile to gap.
> profiles/gap: Rewrite using bt_gatt_client.
> profiles/gap: Add Google copyright.
>
> Makefile.plugins | 4 +-
> profiles/gap/gas.c | 339 ++++++++++++++++++++++++++++++++++++++
> profiles/gatt/gas.c | 457 ----------------------------------------------------
> src/device.c | 390 +++++++++++++++++++++++++++++++++++++-------
> src/device.h | 2 +
> 5 files changed, 676 insertions(+), 516 deletions(-)
> create mode 100644 profiles/gap/gas.c
> delete mode 100644 profiles/gatt/gas.c
>
> --
> 2.2.0.rc0.207.ga3a616c

Applied, thanks.


--
Luiz Augusto von Dentz

2014-12-19 01:24:39

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ v5 7/7] profiles/gap: Add Google copyright.

Added Google Inc. to the copyright comment since the profile has been
mostly rewritten.
---
profiles/gap/gas.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/profiles/gap/gas.c b/profiles/gap/gas.c
index 5702e47..87892d7 100644
--- a/profiles/gap/gas.c
+++ b/profiles/gap/gas.c
@@ -3,6 +3,7 @@
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT
+ * Copyright (C) 2014 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
--
2.2.0.rc0.207.ga3a616c


2014-12-19 01:24:38

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ v5 6/7] profiles/gap: Rewrite using bt_gatt_client.

This patch rewrites the GAP profile to use the shared GATT stack instead
of GAttrib. The profile now also handles the Device Name characteristic.
---
profiles/gap/gas.c | 269 +++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 197 insertions(+), 72 deletions(-)

diff --git a/profiles/gap/gas.c b/profiles/gap/gas.c
index a4028dd..5702e47 100644
--- a/profiles/gap/gas.c
+++ b/profiles/gap/gas.c
@@ -19,6 +19,7 @@
#include <config.h>
#endif

+#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/types.h>
@@ -28,38 +29,34 @@

#include <glib.h>

-#include "btio/btio.h"
#include "lib/uuid.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
#include "src/plugin.h"
#include "src/adapter.h"
#include "src/device.h"
#include "src/profile.h"
#include "src/service.h"
-#include "src/shared/util.h"
-#include "attrib/att.h"
-#include "attrib/gattrib.h"
-#include "src/attio.h"
-#include "attrib/gatt.h"
#include "src/log.h"
-#include "src/textfile.h"

/* Generic Attribute/Access Service */
struct gas {
struct btd_device *device;
- struct att_range gap; /* GAP Primary service range */
- GAttrib *attrib;
- guint attioid;
+ struct gatt_db *db;
+ struct bt_gatt_client *client;
+ uint16_t start_handle, end_handle;
};

static GSList *devices;

static void gas_free(struct gas *gas)
{
- if (gas->attioid)
- btd_device_remove_attio_callback(gas->device, gas->attioid);
-
- g_attrib_unref(gas->attrib);
btd_device_unref(gas->device);
+ gatt_db_unref(gas->db);
+ bt_gatt_client_unref(gas->client);
g_free(gas);
}

@@ -71,128 +68,256 @@ static int cmp_device(gconstpointer a, gconstpointer b)
return gas->device == device ? 0 : -1;
}

-static void gap_appearance_cb(guint8 status, const guint8 *pdu, guint16 plen,
- gpointer user_data)
+static char *name2utf8(const uint8_t *name, uint8_t len)
+{
+ char utf8_name[HCI_MAX_NAME_LENGTH + 2];
+ int i;
+
+ if (g_utf8_validate((const char *) name, len, NULL))
+ return g_strndup((char *) name, len);
+
+ memset(utf8_name, 0, sizeof(utf8_name));
+ strncpy(utf8_name, (char *) name, len);
+
+ /* Assume ASCII, and replace all non-ASCII with spaces */
+ for (i = 0; utf8_name[i] != '\0'; i++) {
+ if (!isascii(utf8_name[i]))
+ utf8_name[i] = ' ';
+ }
+
+ /* Remove leading and trailing whitespace characters */
+ g_strstrip(utf8_name);
+
+ return g_strdup(utf8_name);
+}
+
+static void read_device_name_cb(bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
{
struct gas *gas = user_data;
- struct att_data_list *list = NULL;
- uint16_t app;
- uint8_t *atval;
+ char *name = name2utf8(value, length);
+
+ DBG("GAP Device Name: %s", name);
+
+ btd_device_device_set_name(gas->device, name);
+}

- if (status != 0) {
- error("Read characteristics by UUID failed: %s",
- att_ecode2str(status));
+static void handle_device_name(struct gas *gas, uint16_t value_handle)
+{
+ if (!bt_gatt_client_read_long_value(gas->client, value_handle, 0,
+ read_device_name_cb, gas, NULL))
+ DBG("Failed to send request to read device name");
+}
+
+static void read_appearance_cb(bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ struct gas *gas = user_data;
+ uint16_t appearance;
+
+ if (!success) {
+ DBG("Reading appearance failed with ATT error: %u", att_ecode);
return;
}

- list = dec_read_by_type_resp(pdu, plen);
- if (list == NULL)
+ /* The appearance value is a 16-bit unsigned integer */
+ if (length != 2) {
+ DBG("Malformed appearance value");
return;
-
- if (list->len != 4) {
- error("GAP Appearance value: invalid data");
- goto done;
}

- atval = list->data[0] + 2; /* skip handle value */
- app = get_le16(atval);
+ appearance = get_le16(value);

- DBG("GAP Appearance: 0x%04x", app);
+ DBG("GAP Appearance: 0x%04x", appearance);

- device_set_appearance(gas->device, app);
+ device_set_appearance(gas->device, appearance);
+}

-done:
- att_data_list_free(list);
+static void handle_appearance(struct gas *gas, uint16_t value_handle)
+{
+ if (!bt_gatt_client_read_value(gas->client, value_handle,
+ read_appearance_cb, gas, NULL))
+ DBG("Failed to send request to read appearance");
}

-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+static bool uuid_cmp(uint16_t u16, const bt_uuid_t *uuid)
{
- struct gas *gas = user_data;
- uint16_t app;
+ bt_uuid_t lhs;

- gas->attrib = g_attrib_ref(attrib);
+ bt_uuid16_create(&lhs, u16);

- if (device_get_appearance(gas->device, &app) < 0) {
- bt_uuid_t uuid;
+ return bt_uuid_cmp(&lhs, uuid) == 0;
+}

- bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+static void handle_characteristic(struct gatt_db_attribute *attr,
+ void *user_data)
+{
+ struct gas *gas = user_data;
+ uint16_t value_handle;
+ bt_uuid_t uuid;

- gatt_read_char_by_uuid(gas->attrib, gas->gap.start,
- gas->gap.end, &uuid,
- gap_appearance_cb, gas);
+ if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
+ &uuid)) {
+ error("Failed to obtain characteristic data");
+ return;
}

- /* TODO: Read other GAP characteristics - See Core spec page 1739 */
+ if (uuid_cmp(GATT_CHARAC_DEVICE_NAME, &uuid))
+ handle_device_name(gas, value_handle);
+ else if (uuid_cmp(GATT_CHARAC_APPEARANCE, &uuid))
+ handle_appearance(gas, value_handle);
+ else {
+ char uuid_str[MAX_LEN_UUID_STR];
+
+ /* TODO: Support peripheral privacy feature */
+
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+ DBG("Unsupported characteristic: %s", uuid_str);
+ }
}

-static void attio_disconnected_cb(gpointer user_data)
+static void handle_gap_service(struct gas *gas)
{
- struct gas *gas = user_data;
+ struct gatt_db_attribute *attr;

- g_attrib_unref(gas->attrib);
- gas->attrib = NULL;
+ attr = gatt_db_get_attribute(gas->db, gas->start_handle);
+ if (!attr) {
+ error("Service with handle 0x%04x not found in db",
+ gas->start_handle);
+ return;
+ }
+
+ gatt_db_service_foreach_char(attr, handle_characteristic, gas);
}

-static int gas_register(struct btd_device *device, struct att_range *gap)
+static int gap_driver_probe(struct btd_service *service)
{
+ struct btd_device *device = btd_service_get_device(service);
struct gas *gas;
+ uint16_t start_handle, end_handle;
+ GSList *l;
+ char addr[18];
+
+ if (!btd_service_get_gatt_handles(service, &start_handle, &end_handle))
+ return -1;
+
+ ba2str(device_get_address(device), addr);
+ DBG("GAP profile probe (%s): start: 0x%04x, end 0x%04x", addr,
+ start_handle, end_handle);
+
+ /*
+ * There can't be more than one instance of the GAP service on the same
+ * device.
+ */
+ l = g_slist_find_custom(devices, device, cmp_device);
+ if (l) {
+ error("More than one GAP service exists on device");
+ return -1;
+ }

gas = g_new0(struct gas, 1);
- gas->gap.start = gap->start;
- gas->gap.end = gap->end;

gas->device = btd_device_ref(device);
-
+ gas->start_handle = start_handle;
+ gas->end_handle = end_handle;
devices = g_slist_append(devices, gas);

- gas->attioid = btd_device_add_attio_callback(device,
- attio_connected_cb,
- attio_disconnected_cb, gas);
-
return 0;
}

-static void gas_unregister(struct btd_device *device)
+static void gap_driver_remove(struct btd_service *service)
{
+ struct btd_device *device = btd_service_get_device(service);
struct gas *gas;
+ uint16_t start_handle, end_handle;
GSList *l;
+ char addr[18];
+
+ if (!btd_service_get_gatt_handles(service, &start_handle,
+ &end_handle)) {
+ error("Removed service is not a GATT service");
+ return;
+ }
+
+ ba2str(device_get_address(device), addr);
+ DBG("GAP profile remove (%s): start: 0x%04x, end 0x%04x", addr,
+ start_handle, end_handle);

l = g_slist_find_custom(devices, device, cmp_device);
- if (l == NULL)
+ if (!l) {
+ error("GAP service not handled by profile");
return;
+ }

gas = l->data;
+
+ if (gas->start_handle != start_handle ||
+ gas->end_handle != end_handle) {
+ error("Removed unknown GAP service");
+ return;
+ }
+
devices = g_slist_remove(devices, gas);
gas_free(gas);
}

-static int gap_driver_probe(struct btd_service *service)
+static int gap_driver_accept(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
- struct gatt_primary *gap;
+ struct gatt_db *db = btd_device_get_gatt_db(device);
+ struct bt_gatt_client *client = btd_device_get_gatt_client(device);
+ struct gas *gas;
+ uint16_t start_handle, end_handle;
+ GSList *l;
+ char addr[18];

- gap = btd_device_get_primary(device, GAP_UUID);
+ if (!btd_service_get_gatt_handles(service, &start_handle,
+ &end_handle)) {
+ error("Service is not a GATT service");
+ return -1;
+ }
+
+ ba2str(device_get_address(device), addr);
+ DBG("GAP profile accept (%s): start: 0x%04x, end 0x%04x", addr,
+ start_handle, end_handle);

- if (gap == NULL) {
- error("GAP service is mandatory");
- return -EINVAL;
+ l = g_slist_find_custom(devices, device, cmp_device);
+ if (!l) {
+ error("GAP service not handled by profile");
+ return -1;
}

- return gas_register(device, &gap->range);
-}
+ gas = l->data;

-static void gap_driver_remove(struct btd_service *service)
-{
- struct btd_device *device = btd_service_get_device(service);
+ if (gas->start_handle != start_handle ||
+ gas->end_handle != end_handle) {
+ error("Accepting unknown GAP service");
+ return -1;
+ }

- gas_unregister(device);
+ /* Clean-up any old client/db and acquire the new ones */
+ gatt_db_unref(gas->db);
+ bt_gatt_client_unref(gas->client);
+ gas->db = NULL;
+ gas->client = NULL;
+
+ gas->db = gatt_db_ref(db);
+ gas->client = bt_gatt_client_ref(client);
+
+ /* Handle the service */
+ handle_gap_service(gas);
+
+ return 0;
}

static struct btd_profile gap_profile = {
.name = "gap-profile",
.remote_uuid = GAP_UUID,
.device_probe = gap_driver_probe,
- .device_remove = gap_driver_remove
+ .device_remove = gap_driver_remove,
+ .accept = gap_driver_accept
};

static int gap_init(void)
--
2.2.0.rc0.207.ga3a616c


2014-12-19 01:24:37

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ v5 5/7] profiles/gatt: Rename profile to gap.

Since this built in profile only handles the GAP service, this patch renames it
to gap.
---
Makefile.plugins | 4 +-
profiles/gap/gas.c | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++
profiles/gatt/gas.c | 216 ----------------------------------------------------
3 files changed, 215 insertions(+), 218 deletions(-)
create mode 100644 profiles/gap/gas.c
delete mode 100644 profiles/gatt/gas.c

diff --git a/Makefile.plugins b/Makefile.plugins
index 0448b91..52b51c5 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -72,8 +72,8 @@ builtin_sources += profiles/health/mcap.h profiles/health/mcap.c \
profiles/health/hdp_util.h profiles/health/hdp_util.c
endif

-builtin_modules += gatt
-builtin_sources += profiles/gatt/gas.c
+builtin_modules += gap
+builtin_sources += profiles/gap/gas.c

builtin_modules += scanparam
builtin_sources += profiles/scanparam/scan.c
diff --git a/profiles/gap/gas.c b/profiles/gap/gas.c
new file mode 100644
index 0000000..a4028dd
--- /dev/null
+++ b/profiles/gap/gas.c
@@ -0,0 +1,213 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT
+ *
+ * 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 <stdbool.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/uuid.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "src/attio.h"
+#include "attrib/gatt.h"
+#include "src/log.h"
+#include "src/textfile.h"
+
+/* Generic Attribute/Access Service */
+struct gas {
+ struct btd_device *device;
+ struct att_range gap; /* GAP Primary service range */
+ GAttrib *attrib;
+ guint attioid;
+};
+
+static GSList *devices;
+
+static void gas_free(struct gas *gas)
+{
+ if (gas->attioid)
+ btd_device_remove_attio_callback(gas->device, gas->attioid);
+
+ g_attrib_unref(gas->attrib);
+ btd_device_unref(gas->device);
+ g_free(gas);
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+ const struct gas *gas = a;
+ const struct btd_device *device = b;
+
+ return gas->device == device ? 0 : -1;
+}
+
+static void gap_appearance_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct gas *gas = user_data;
+ struct att_data_list *list = NULL;
+ uint16_t app;
+ uint8_t *atval;
+
+ if (status != 0) {
+ error("Read characteristics by UUID failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ list = dec_read_by_type_resp(pdu, plen);
+ if (list == NULL)
+ return;
+
+ if (list->len != 4) {
+ error("GAP Appearance value: invalid data");
+ goto done;
+ }
+
+ atval = list->data[0] + 2; /* skip handle value */
+ app = get_le16(atval);
+
+ DBG("GAP Appearance: 0x%04x", app);
+
+ device_set_appearance(gas->device, app);
+
+done:
+ att_data_list_free(list);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct gas *gas = user_data;
+ uint16_t app;
+
+ gas->attrib = g_attrib_ref(attrib);
+
+ if (device_get_appearance(gas->device, &app) < 0) {
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+
+ gatt_read_char_by_uuid(gas->attrib, gas->gap.start,
+ gas->gap.end, &uuid,
+ gap_appearance_cb, gas);
+ }
+
+ /* TODO: Read other GAP characteristics - See Core spec page 1739 */
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct gas *gas = user_data;
+
+ g_attrib_unref(gas->attrib);
+ gas->attrib = NULL;
+}
+
+static int gas_register(struct btd_device *device, struct att_range *gap)
+{
+ struct gas *gas;
+
+ gas = g_new0(struct gas, 1);
+ gas->gap.start = gap->start;
+ gas->gap.end = gap->end;
+
+ gas->device = btd_device_ref(device);
+
+ devices = g_slist_append(devices, gas);
+
+ gas->attioid = btd_device_add_attio_callback(device,
+ attio_connected_cb,
+ attio_disconnected_cb, gas);
+
+ return 0;
+}
+
+static void gas_unregister(struct btd_device *device)
+{
+ struct gas *gas;
+ GSList *l;
+
+ l = g_slist_find_custom(devices, device, cmp_device);
+ if (l == NULL)
+ return;
+
+ gas = l->data;
+ devices = g_slist_remove(devices, gas);
+ gas_free(gas);
+}
+
+static int gap_driver_probe(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct gatt_primary *gap;
+
+ gap = btd_device_get_primary(device, GAP_UUID);
+
+ if (gap == NULL) {
+ error("GAP service is mandatory");
+ return -EINVAL;
+ }
+
+ return gas_register(device, &gap->range);
+}
+
+static void gap_driver_remove(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+
+ gas_unregister(device);
+}
+
+static struct btd_profile gap_profile = {
+ .name = "gap-profile",
+ .remote_uuid = GAP_UUID,
+ .device_probe = gap_driver_probe,
+ .device_remove = gap_driver_remove
+};
+
+static int gap_init(void)
+{
+ devices = NULL;
+
+ btd_profile_register(&gap_profile);
+
+ return 0;
+}
+
+static void gap_exit(void)
+{
+ btd_profile_unregister(&gap_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(gap, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ gap_init, gap_exit)
diff --git a/profiles/gatt/gas.c b/profiles/gatt/gas.c
deleted file mode 100644
index 9d5d31e..0000000
--- a/profiles/gatt/gas.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdbool.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include <glib.h>
-
-#include "btio/btio.h"
-#include "lib/uuid.h"
-#include "src/plugin.h"
-#include "src/adapter.h"
-#include "src/device.h"
-#include "src/profile.h"
-#include "src/service.h"
-#include "src/shared/util.h"
-#include "attrib/att.h"
-#include "attrib/gattrib.h"
-#include "src/attio.h"
-#include "attrib/gatt.h"
-#include "src/log.h"
-#include "src/textfile.h"
-
-/* Generic Attribute/Access Service */
-struct gas {
- struct btd_device *device;
- struct att_range gap; /* GAP Primary service range */
- GAttrib *attrib;
- guint attioid;
-};
-
-static GSList *devices = NULL;
-
-static void gas_free(struct gas *gas)
-{
- if (gas->attioid)
- btd_device_remove_attio_callback(gas->device, gas->attioid);
-
- g_attrib_unref(gas->attrib);
- btd_device_unref(gas->device);
- g_free(gas);
-}
-
-static int cmp_device(gconstpointer a, gconstpointer b)
-{
- const struct gas *gas = a;
- const struct btd_device *device = b;
-
- return (gas->device == device ? 0 : -1);
-}
-
-static void gap_appearance_cb(guint8 status, const guint8 *pdu, guint16 plen,
- gpointer user_data)
-{
- struct gas *gas = user_data;
- struct att_data_list *list = NULL;
- uint16_t app;
- uint8_t *atval;
-
- if (status != 0) {
- error("Read characteristics by UUID failed: %s",
- att_ecode2str(status));
- return;
- }
-
- list = dec_read_by_type_resp(pdu, plen);
- if (list == NULL)
- return;
-
- if (list->len != 4) {
- error("GAP Appearance value: invalid data");
- goto done;
- }
-
- atval = list->data[0] + 2; /* skip handle value */
- app = get_le16(atval);
-
- DBG("GAP Appearance: 0x%04x", app);
-
- device_set_appearance(gas->device, app);
-
-done:
- att_data_list_free(list);
-}
-
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
-{
- struct gas *gas = user_data;
- uint16_t app;
-
- gas->attrib = g_attrib_ref(attrib);
-
- if (device_get_appearance(gas->device, &app) < 0) {
- bt_uuid_t uuid;
-
- bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
-
- gatt_read_char_by_uuid(gas->attrib, gas->gap.start,
- gas->gap.end, &uuid,
- gap_appearance_cb, gas);
- }
-
- /* TODO: Read other GAP characteristics - See Core spec page 1739 */
-}
-
-static void attio_disconnected_cb(gpointer user_data)
-{
- struct gas *gas = user_data;
-
- g_attrib_unref(gas->attrib);
- gas->attrib = NULL;
-}
-
-static int gas_register(struct btd_device *device, struct att_range *gap)
-{
- struct gas *gas;
-
- gas = g_new0(struct gas, 1);
- gas->gap.start = gap->start;
- gas->gap.end = gap->end;
-
- gas->device = btd_device_ref(device);
-
- devices = g_slist_append(devices, gas);
-
- gas->attioid = btd_device_add_attio_callback(device,
- attio_connected_cb,
- attio_disconnected_cb, gas);
-
- return 0;
-}
-
-static void gas_unregister(struct btd_device *device)
-{
- struct gas *gas;
- GSList *l;
-
- l = g_slist_find_custom(devices, device, cmp_device);
- if (l == NULL)
- return;
-
- gas = l->data;
- devices = g_slist_remove(devices, gas);
- gas_free(gas);
-}
-
-static int gatt_driver_probe(struct btd_service *service)
-{
- struct btd_device *device = btd_service_get_device(service);
- struct gatt_primary *gap;
-
- gap = btd_device_get_primary(device, GAP_UUID);
-
- if (gap == NULL) {
- error("GAP service is mandatory");
- return -EINVAL;
- }
-
- return gas_register(device, &gap->range);
-}
-
-static void gatt_driver_remove(struct btd_service *service)
-{
- struct btd_device *device = btd_service_get_device(service);
-
- gas_unregister(device);
-}
-
-static struct btd_profile gatt_profile = {
- .name = "gap-gatt-profile",
- .remote_uuid = GATT_UUID,
- .device_probe = gatt_driver_probe,
- .device_remove = gatt_driver_remove
-};
-
-static int gatt_init(void)
-{
- btd_profile_register(&gatt_profile);
-
- return 0;
-}
-
-static void gatt_exit(void)
-{
- btd_profile_unregister(&gatt_profile);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(gatt, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
- gatt_init, gatt_exit)
--
2.2.0.rc0.207.ga3a616c


2014-12-19 01:24:36

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ v5 4/7] profiles/gatt: Don't handle GATT service.

ATT MTU exchange and handling of indications from the "Service Changed"
characteristic are now handled by shared/gatt-client, so this profile
should only deal with the GAP service.
---
profiles/gatt/gas.c | 251 ++--------------------------------------------------
1 file changed, 5 insertions(+), 246 deletions(-)

diff --git a/profiles/gatt/gas.c b/profiles/gatt/gas.c
index b51b4a8..9d5d31e 100644
--- a/profiles/gatt/gas.c
+++ b/profiles/gatt/gas.c
@@ -52,12 +52,8 @@
struct gas {
struct btd_device *device;
struct att_range gap; /* GAP Primary service range */
- struct att_range gatt; /* GATT Primary service range */
GAttrib *attrib;
guint attioid;
- guint changed_ind;
- uint16_t changed_handle;
- uint16_t mtu;
};

static GSList *devices = NULL;
@@ -80,68 +76,6 @@ static int cmp_device(gconstpointer a, gconstpointer b)
return (gas->device == device ? 0 : -1);
}

-static void write_ctp_handle(struct btd_device *device, uint16_t uuid,
- uint16_t handle)
-{
- char *filename, group[6], value[7];
- GKeyFile *key_file;
- char *data;
- gsize length = 0;
-
- filename = btd_device_get_storage_path(device, "gatt");
- if (!filename) {
- warn("Unable to get gatt storage path for device");
- return;
- }
-
- key_file = g_key_file_new();
- g_key_file_load_from_file(key_file, filename, 0, NULL);
-
- snprintf(group, sizeof(group), "%hu", uuid);
- snprintf(value, sizeof(value), "0x%4.4X", handle);
- g_key_file_set_string(key_file, group, "Value", value);
-
- data = g_key_file_to_data(key_file, &length, NULL);
- if (length > 0) {
- create_file(filename, S_IRUSR | S_IWUSR);
- g_file_set_contents(filename, data, length, NULL);
- }
-
- g_free(data);
- g_free(filename);
- g_key_file_free(key_file);
-}
-
-static int read_ctp_handle(struct btd_device *device, uint16_t uuid,
- uint16_t *value)
-{
- char *filename, group[6];
- GKeyFile *key_file;
- char *str;
- int err = 0;
-
- filename = btd_device_get_storage_path(device, "gatt");
- if (!filename) {
- warn("Unable to get gatt storage path for device");
- return -ENOENT;
- }
-
- snprintf(group, sizeof(group), "%hu", uuid);
-
- key_file = g_key_file_new();
- g_key_file_load_from_file(key_file, filename, 0, NULL);
-
- str = g_key_file_get_string(key_file, group, "Value", NULL);
- if (str == NULL || sscanf(str, "%hx", value) != 1)
- err = -ENOENT;
-
- g_free(str);
- g_free(filename);
- g_key_file_free(key_file);
-
- return err;
-}
-
static void gap_appearance_cb(guint8 status, const guint8 *pdu, guint16 plen,
gpointer user_data)
{
@@ -176,163 +110,12 @@ done:
att_data_list_free(list);
}

-static void indication_cb(const uint8_t *pdu, uint16_t len, gpointer user_data)
-{
- uint8_t bdaddr_type;
- struct gas *gas = user_data;
- uint16_t start, end, olen;
- size_t plen;
- uint8_t *opdu;
-
- if (len < 7) { /* 1-byte opcode + 2-byte handle + 4 range */
- error("Malformed ATT notification");
- return;
- }
-
- start = get_le16(&pdu[3]);
- end = get_le16(&pdu[5]);
-
- DBG("Service Changed start: 0x%04X end: 0x%04X", start, end);
-
- /* Confirming indication received */
- opdu = g_attrib_get_buffer(gas->attrib, &plen);
- olen = enc_confirmation(opdu, plen);
- g_attrib_send(gas->attrib, 0, opdu, olen, NULL, NULL, NULL);
-
- bdaddr_type = btd_device_get_bdaddr_type(gas->device);
- if (!device_is_bonded(gas->device, bdaddr_type)) {
- DBG("Ignoring Service Changed: device is not bonded");
- return;
- }
-
- btd_device_gatt_set_service_changed(gas->device, start, end);
-}
-
-static void ccc_written_cb(guint8 status, const guint8 *pdu, guint16 plen,
- gpointer user_data)
-{
- struct gas *gas = user_data;
-
- if (status) {
- error("Write Service Changed CCC failed: %s",
- att_ecode2str(status));
- return;
- }
-
- DBG("Service Changed indications enabled");
-
- gas->changed_ind = g_attrib_register(gas->attrib, ATT_OP_HANDLE_IND,
- gas->changed_handle,
- indication_cb, gas, NULL);
-
- write_ctp_handle(gas->device, GATT_CHARAC_SERVICE_CHANGED,
- gas->changed_handle);
-}
-
-static void write_ccc(GAttrib *attrib, uint16_t handle, gpointer user_data)
-{
- uint8_t value[2];
-
- put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value);
- gatt_write_char(attrib, handle, value, sizeof(value), ccc_written_cb,
- user_data);
-}
-
-static void discover_ccc_cb(uint8_t status, GSList *descs, void *user_data)
-{
- struct gas *gas = user_data;
- struct gatt_desc *desc;
-
- if (status != 0) {
- error("Discover Service Changed CCC failed: %s",
- att_ecode2str(status));
- return;
- }
-
- /* There will be only one descriptor on list and it will be CCC */
- desc = descs->data;
-
- DBG("CCC: 0x%04x", desc->handle);
- write_ccc(gas->attrib, desc->handle, user_data);
-}
-
-static void gatt_characteristic_cb(uint8_t status, GSList *characteristics,
- void *user_data)
-{
- struct gas *gas = user_data;
- struct gatt_char *chr;
- uint16_t start, end;
- bt_uuid_t uuid;
-
- if (status) {
- error("Discover Service Changed handle: %s", att_ecode2str(status));
- return;
- }
-
- chr = characteristics->data;
-
- start = chr->value_handle + 1;
- end = gas->gatt.end;
-
- if (start > end) {
- error("Inconsistent database: Service Changed CCC missing");
- return;
- }
-
- gas->changed_handle = chr->value_handle;
-
- bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
-
- gatt_discover_desc(gas->attrib, start, end, &uuid, discover_ccc_cb,
- gas);
-}
-
-static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
- gpointer user_data)
-{
- struct gas *gas = user_data;
- uint16_t rmtu;
-
- if (status) {
- error("MTU exchange: %s", att_ecode2str(status));
- return;
- }
-
- if (!dec_mtu_resp(pdu, plen, &rmtu)) {
- error("MTU exchange: protocol error");
- return;
- }
-
- gas->mtu = MIN(rmtu, gas->mtu);
- if (g_attrib_set_mtu(gas->attrib, gas->mtu))
- DBG("MTU exchange succeeded: %d", gas->mtu);
- else
- DBG("MTU exchange failed");
-}
-
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
{
struct gas *gas = user_data;
- GIOChannel *io;
- GError *gerr = NULL;
- uint16_t cid, imtu;
uint16_t app;

gas->attrib = g_attrib_ref(attrib);
- io = g_attrib_get_channel(attrib);
-
- if (bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu,
- BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID) &&
- cid == ATT_CID) {
- gatt_exchange_mtu(gas->attrib, imtu, exchange_mtu_cb, gas);
- gas->mtu = imtu;
- DBG("MTU Exchange: Requesting %d", imtu);
- }
-
- if (gerr) {
- error("Could not acquire att imtu and cid: %s", gerr->message);
- g_error_free(gerr);
- }

if (device_get_appearance(gas->device, &app) < 0) {
bt_uuid_t uuid;
@@ -345,43 +128,23 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
}

/* TODO: Read other GAP characteristics - See Core spec page 1739 */
-
- /*
- * When re-connecting <<Service Changed>> handle and characteristic
- * value doesn't need to read again: known information from the
- * previous interaction.
- */
- if (gas->changed_handle == 0) {
- bt_uuid_t uuid;
-
- bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
-
- gatt_discover_char(gas->attrib, gas->gatt.start, gas->gatt.end,
- &uuid, gatt_characteristic_cb, gas);
- }
}

static void attio_disconnected_cb(gpointer user_data)
{
struct gas *gas = user_data;

- g_attrib_unregister(gas->attrib, gas->changed_ind);
- gas->changed_ind = 0;
-
g_attrib_unref(gas->attrib);
gas->attrib = NULL;
}

-static int gas_register(struct btd_device *device, struct att_range *gap,
- struct att_range *gatt)
+static int gas_register(struct btd_device *device, struct att_range *gap)
{
struct gas *gas;

gas = g_new0(struct gas, 1);
gas->gap.start = gap->start;
gas->gap.end = gap->end;
- gas->gatt.start = gatt->start;
- gas->gatt.end = gatt->end;

gas->device = btd_device_ref(device);

@@ -391,9 +154,6 @@ static int gas_register(struct btd_device *device, struct att_range *gap,
attio_connected_cb,
attio_disconnected_cb, gas);

- read_ctp_handle(gas->device, GATT_CHARAC_SERVICE_CHANGED,
- &gas->changed_handle);
-
return 0;
}

@@ -414,17 +174,16 @@ static void gas_unregister(struct btd_device *device)
static int gatt_driver_probe(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
- struct gatt_primary *gap, *gatt;
+ struct gatt_primary *gap;

gap = btd_device_get_primary(device, GAP_UUID);
- gatt = btd_device_get_primary(device, GATT_UUID);

- if (gap == NULL || gatt == NULL) {
- error("GAP and GATT are mandatory");
+ if (gap == NULL) {
+ error("GAP service is mandatory");
return -EINVAL;
}

- return gas_register(device, &gap->range, &gatt->range);
+ return gas_register(device, &gap->range);
}

static void gatt_driver_remove(struct btd_service *service)
--
2.2.0.rc0.207.ga3a616c


2014-12-19 01:24:35

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ v5 3/7] core: device: Make profile calls in GATT events

This patch correctly integrates all profile calls into the various GATT
client events (ready, service-added, service-removed) so that profiles
can perform the necessary updates. The major changes introduced in this
this patch are:

1. Added new profile probe/remove functions for GATT services, which
operate on a btd_device's client db and initialize btd_service
instances with start & end handles:
- device_probe_gatt_profiles
- device_probe_gatt_profile
- device_remove_gatt_profile
- device_accept_gatt_profiles

2. device_probe_gatt_profiles is called after service discovery on all
profiles for new services and any stale services are removed from
the profiles.

3. device_accept_gatt_profiles is called to notify profiles of a new
connection when the gatt-client becomes ready. This is call
immediately after a probe, if the probe was done for a newly found
service after a "Service Changed" event.

4. device_probe_gatt_profile is called when a new GATT service is
added to the db.

5. device_remove_gatt_profile is called when a GATT service is removed
from the db.
---
src/device.c | 366 +++++++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 317 insertions(+), 49 deletions(-)

diff --git a/src/device.c b/src/device.c
index 854d9f3..cfc5894 100644
--- a/src/device.c
+++ b/src/device.c
@@ -292,6 +292,27 @@ static GSList *find_service_with_state(GSList *list,
return NULL;
}

+static GSList *find_service_with_gatt_handles(GSList *list,
+ uint16_t start_handle,
+ uint16_t end_handle)
+{
+ GSList *l;
+ uint16_t svc_start, svc_end;
+
+ for (l = list; l != NULL; l = g_slist_next(l)) {
+ struct btd_service *service = l->data;
+
+ if (!btd_service_get_gatt_handles(service, &svc_start,
+ &svc_end))
+ continue;
+
+ if (svc_start == start_handle && svc_end == end_handle)
+ return l;
+ }
+
+ return NULL;
+}
+
static void update_technologies(GKeyFile *file, struct btd_device *dev)
{
const char *list[2];
@@ -2390,15 +2411,265 @@ static void add_primary(struct gatt_db_attribute *attr, void *user_data)
*new_services = g_slist_append(*new_services, prim);
}

+static void device_add_uuids(struct btd_device *device, GSList *uuids)
+{
+ GSList *l;
+
+ for (l = uuids; l != NULL; l = g_slist_next(l)) {
+ GSList *match = g_slist_find_custom(device->uuids, l->data,
+ bt_uuid_strcmp);
+ if (match)
+ continue;
+
+ device->uuids = g_slist_insert_sorted(device->uuids,
+ g_strdup(l->data),
+ bt_uuid_strcmp);
+ }
+
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "UUIDs");
+}
+
static void store_services(struct btd_device *device);

+struct gatt_probe_data {
+ struct btd_device *dev;
+ bool all_services;
+ GSList *uuids;
+ struct gatt_db_attribute *cur_attr;
+ char cur_uuid[MAX_LEN_UUID_STR];
+ uint16_t start_handle, end_handle;
+ GSList *valid_services;
+};
+
+static bool device_match_profile(struct btd_device *device,
+ struct btd_profile *profile,
+ GSList *uuids)
+{
+ if (profile->remote_uuid == NULL)
+ return false;
+
+ if (g_slist_find_custom(uuids, profile->remote_uuid,
+ bt_uuid_strcmp) == NULL)
+ return false;
+
+ return true;
+}
+
+static void dev_probe_gatt(struct btd_profile *p, void *user_data)
+{
+ struct gatt_probe_data *data = user_data;
+ struct btd_service *service;
+
+ if (p->device_probe == NULL)
+ return;
+
+ if (!p->remote_uuid || bt_uuid_strcmp(p->remote_uuid, data->cur_uuid))
+ return;
+
+ service = service_create_gatt(data->dev, p, data->start_handle,
+ data->end_handle);
+ if (!service)
+ return;
+
+ if (service_probe(service) < 0) {
+ btd_service_unref(service);
+ return;
+ }
+
+ data->dev->services = g_slist_append(data->dev->services, service);
+
+ if (data->all_services)
+ data->valid_services = g_slist_append(data->valid_services,
+ service);
+}
+
+static bool match_existing_service(struct gatt_probe_data *data)
+{
+ struct btd_device *dev = data->dev;
+ struct btd_service *service;
+ struct btd_profile *profile;
+ uint16_t start, end;
+ GSList *l, *tmp;
+
+ /*
+ * Check if the profiles should be probed for the service in the
+ * database.
+ */
+ for (l = dev->services; l != NULL;) {
+ service = l->data;
+ profile = btd_service_get_profile(service);
+
+ /* If this is local or non-GATT based service, then skip. */
+ if (!profile->remote_uuid ||
+ !btd_service_get_gatt_handles(service, &start, &end)) {
+ l = g_slist_next(l);
+ continue;
+ }
+
+ /* Skip this service if the handle ranges don't overlap. */
+ if (start > data->end_handle || end < data->start_handle) {
+ l = g_slist_next(l);
+ continue;
+ }
+
+ /*
+ * If there is an exact match, then there's no need to probe the
+ * profiles. An exact match is when the service handles AND the
+ * service UUID match.
+ */
+ if (start == data->start_handle && end == data->end_handle &&
+ !bt_uuid_strcmp(profile->remote_uuid, data->cur_uuid)) {
+ if (data->all_services)
+ data->valid_services = g_slist_append(
+ data->valid_services, service);
+ return true;
+ }
+
+ /*
+ * The handles overlap but there is no exact match. This means
+ * that the service is no longer valid. Remove it.
+ *
+ * TODO: The following code is fairly inefficient, especially
+ * when we consider all the extra searches that we're already
+ * doing. Consider changing the services list to a GList.
+ */
+ tmp = l->next;
+ dev->services = g_slist_delete_link(dev->services, l);
+ dev->pending = g_slist_remove(dev->pending, service);
+ service_remove(service);
+
+ l = tmp;
+ }
+
+ /* No match found */
+ return false;
+}
+
+static void dev_probe_gatt_profile(struct gatt_db_attribute *attr,
+ void *user_data)
+{
+ struct gatt_probe_data *data = user_data;
+ bt_uuid_t uuid;
+ GSList *l;
+
+ gatt_db_attribute_get_service_data(attr, &data->start_handle,
+ &data->end_handle, NULL,
+ &uuid);
+ bt_uuid_to_string(&uuid, data->cur_uuid, sizeof(data->cur_uuid));
+
+ data->cur_attr = attr;
+
+ /* Don't probe the profiles if a matching service already exists. */
+ if (!match_existing_service(data))
+ btd_profile_foreach(dev_probe_gatt, data);
+
+ if (data->all_services) {
+ data->uuids = g_slist_append(data->uuids,
+ g_strdup(data->cur_uuid));
+ return;
+ }
+
+ l = g_slist_append(l, g_strdup(data->cur_uuid));
+ device_add_uuids(data->dev, l);
+}
+
+static void device_probe_gatt_profile(struct btd_device *device,
+ struct gatt_db_attribute *attr)
+{
+ struct gatt_probe_data data;
+
+ memset(&data, 0, sizeof(data));
+
+ data.dev = device;
+
+ dev_probe_gatt_profile(attr, &data);
+ g_slist_free_full(data.uuids, g_free);
+}
+
+static void remove_invalid_services(struct gatt_probe_data *data)
+{
+ struct btd_device *dev = data->dev;
+ struct btd_service *service;
+ GSList *l, *tmp;
+
+ for (l = dev->services; l != NULL;) {
+ service = l->data;
+
+ if (g_slist_find(data->valid_services, service)) {
+ l = g_slist_next(l);
+ continue;
+ }
+
+ /* Service no longer valid, so remove it */
+ tmp = l->next;
+ dev->services = g_slist_delete_link(dev->services, l);
+ dev->pending = g_slist_remove(dev->pending, service);
+ service_remove(service);
+
+ l = tmp;
+ }
+}
+
+static void device_probe_gatt_profiles(struct btd_device *device)
+{
+ struct gatt_probe_data data;
+ char addr[18];
+
+ ba2str(&device->bdaddr, addr);
+
+ if (device->blocked) {
+ DBG("Skipping profiles for blocked device %s", addr);
+ return;
+ }
+
+ memset(&data, 0, sizeof(data));
+
+ data.dev = device;
+ data.all_services = true;
+
+ gatt_db_foreach_service(device->db, NULL, dev_probe_gatt_profile,
+ &data);
+ device_add_uuids(device, data.uuids);
+ g_slist_free_full(data.uuids, g_free);
+
+ remove_invalid_services(&data);
+ g_slist_free(data.valid_services);
+}
+
+static void device_accept_gatt_profiles(struct btd_device *device)
+{
+ GSList *l;
+
+ for (l = device->services; l != NULL; l = g_slist_next(l))
+ service_accept(l->data);
+}
+
+static void device_remove_gatt_profile(struct btd_device *device,
+ struct gatt_db_attribute *attr)
+{
+ uint16_t start, end;
+ struct btd_service *service;
+ GSList *l;
+
+ gatt_db_attribute_get_service_handles(attr, &start, &end);
+
+ l = find_service_with_gatt_handles(device->services, start, end);
+ if (!l)
+ return;
+
+ service = l->data;
+ device->services = g_slist_delete_link(device->services, l);
+ device->pending = g_slist_remove(device->pending, service);
+ service_remove(service);
+}
+
static void gatt_service_added(struct gatt_db_attribute *attr, void *user_data)
{
struct btd_device *device = user_data;
- struct gatt_primary *prim;
GSList *new_service = NULL;
- GSList *profiles_added = NULL;
uint16_t start, end;
+ GSList *l;

if (!bt_gatt_client_is_ready(device->client))
return;
@@ -2417,14 +2688,13 @@ static void gatt_service_added(struct gatt_db_attribute *attr, void *user_data)

device_register_primaries(device, new_service, -1);

- prim = new_service->data;
- profiles_added = g_slist_append(profiles_added, g_strdup(prim->uuid));
+ device_probe_gatt_profile(device, attr);

- device_probe_profiles(device, profiles_added);
+ l = find_service_with_gatt_handles(device->services, start, end);
+ if (l)
+ service_accept(l->data);

store_services(device);
-
- g_slist_free_full(profiles_added, g_free);
}

static gint prim_attr_cmp(gconstpointer a, gconstpointer b)
@@ -2438,6 +2708,14 @@ static gint prim_attr_cmp(gconstpointer a, gconstpointer b)
return !(prim->range.start == start && prim->range.end == end);
}

+static gint prim_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_primary *prim = a;
+ const char *uuid = b;
+
+ return bt_uuid_strcmp(prim->uuid, uuid);
+}
+
static void gatt_service_removed(struct gatt_db_attribute *attr,
void *user_data)
{
@@ -2461,20 +2739,32 @@ static void gatt_service_removed(struct gatt_db_attribute *attr,
prim = l->data;
device->primaries = g_slist_delete_link(device->primaries, l);

- /* Remove the corresponding UUIDs entry */
- l = g_slist_find_custom(device->uuids, prim->uuid, bt_uuid_strcmp);
- device->uuids = g_slist_delete_link(device->uuids, l);
- g_free(prim);
-
- store_services(device);
+ /*
+ * If this happend since the db was cleared for a non-bonded device,
+ * then don't remove the btd_service just yet. We do this so that we can
+ * avoid re-probing the profile if the same GATT service is found on the
+ * device on re-connection. However, if the device is marked as
+ * temporary, then we remove it anyway.
+ */
+ if (device->client || device->temporary == TRUE)
+ device_remove_gatt_profile(device, attr);

/*
- * TODO: Notify the profiles somehow. It may be sufficient for each
- * profile to register a service_removed handler.
+ * Remove the corresponding UUIDs entry, only if this is the last
+ * service with this UUID.
*/
+ l = g_slist_find_custom(device->uuids, prim->uuid, bt_uuid_strcmp);

- g_dbus_emit_property_changed(dbus_conn, device->path,
+ if (!g_slist_find_custom(device->primaries, prim->uuid,
+ prim_uuid_cmp)) {
+ device->uuids = g_slist_delete_link(device->uuids, l);
+ g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "UUIDs");
+ }
+
+ g_free(prim);
+
+ store_services(device);
}

static struct btd_device *device_new(struct btd_adapter *adapter,
@@ -2975,20 +3265,6 @@ GSList *btd_device_get_uuids(struct btd_device *device)
return device->uuids;
}

-static bool device_match_profile(struct btd_device *device,
- struct btd_profile *profile,
- GSList *uuids)
-{
- if (profile->remote_uuid == NULL)
- return false;
-
- if (g_slist_find_custom(uuids, profile->remote_uuid,
- bt_uuid_strcmp) == NULL)
- return false;
-
- return true;
-}
-
struct probe_data {
struct btd_device *dev;
GSList *uuids;
@@ -3065,7 +3341,6 @@ void device_remove_profile(gpointer a, gpointer b)
void device_probe_profiles(struct btd_device *device, GSList *uuids)
{
struct probe_data d = { device, uuids };
- GSList *l;
char addr[18];

ba2str(&device->bdaddr, addr);
@@ -3080,19 +3355,7 @@ void device_probe_profiles(struct btd_device *device, GSList *uuids)
btd_profile_foreach(dev_probe, &d);

add_uuids:
- for (l = uuids; l != NULL; l = g_slist_next(l)) {
- GSList *match = g_slist_find_custom(device->uuids, l->data,
- bt_uuid_strcmp);
- if (match)
- continue;
-
- device->uuids = g_slist_insert_sorted(device->uuids,
- g_strdup(l->data),
- bt_uuid_strcmp);
- }
-
- g_dbus_emit_property_changed(dbus_conn, device->path,
- DEVICE_INTERFACE, "UUIDs");
+ device_add_uuids(device, uuids);
}

static void store_sdp_record(GKeyFile *key_file, sdp_record_t *rec)
@@ -3412,6 +3675,13 @@ static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
if (primaries)
device_register_primaries(device, primaries, ATT_PSM);

+ /*
+ * TODO: The btd_service instances for GATT services need to be
+ * initialized with the service handles. Eventually this code should
+ * perform ATT protocol service discovery over the ATT PSM to obtain
+ * the full list of services and populate a client-role gatt_db over
+ * BR/EDR.
+ */
device_probe_profiles(device, req->profiles_added);

/* Propagate services changes */
@@ -3640,7 +3910,7 @@ static void register_gatt_services(struct browse_req *req)

device_register_primaries(device, services, -1);

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

device_svc_resolved(device, device->bdaddr_type, 0);

@@ -3675,10 +3945,8 @@ static void gatt_client_ready_cb(bool success, uint8_t att_ecode,
if (device->browse)
register_gatt_services(device->browse);

- /*
- * TODO: Change attio callbacks to accept a gatt-client instead of a
- * GAttrib.
- */
+ device_accept_gatt_profiles(device);
+
g_slist_foreach(device->attios, attio_connected, device->attrib);
}

--
2.2.0.rc0.207.ga3a616c


2014-12-19 01:24:34

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ v5 2/7] core: device: Add getters for GATT db and client

This patch adds btd_device_get_gatt_db and btd_device_get_gatt_client
functions.
---
src/device.c | 16 ++++++++++++++++
src/device.h | 2 ++
2 files changed, 18 insertions(+)

diff --git a/src/device.c b/src/device.c
index 64591d0..854d9f3 100644
--- a/src/device.c
+++ b/src/device.c
@@ -4844,6 +4844,22 @@ GSList *btd_device_get_primaries(struct btd_device *device)
return device->primaries;
}

+struct gatt_db *btd_device_get_gatt_db(struct btd_device *device)
+{
+ if (!device)
+ return NULL;
+
+ return device->db;
+}
+
+struct bt_gatt_client *btd_device_get_gatt_client(struct btd_device *device)
+{
+ if (!device)
+ return NULL;
+
+ return device->client;
+}
+
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 487bd27..a7fefee 100644
--- a/src/device.h
+++ b/src/device.h
@@ -67,6 +67,8 @@ const sdp_record_t *btd_device_get_record(struct btd_device *device,
struct gatt_primary *btd_device_get_primary(struct btd_device *device,
const char *uuid);
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);
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


2014-12-19 01:24:33

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ v5 1/7] core: device: Don't disconnect if attios not set

Currently there is no way for external applications to claim ownership
on a GATT connection and as profiles move away from attio.h it doesn't
make much sense to immediately disconnect if no attio callbacks have
been set after probing the profiles (as this will always immediately
disconnect the device). This patch removes this logic from btd_device.
---
src/device.c | 8 --------
1 file changed, 8 deletions(-)

diff --git a/src/device.c b/src/device.c
index e8bdc18..64591d0 100644
--- a/src/device.c
+++ b/src/device.c
@@ -3642,9 +3642,6 @@ static void register_gatt_services(struct browse_req *req)

device_probe_profiles(device, req->profiles_added);

- if (device->attios == NULL && device->attios_offline == NULL)
- attio_cleanup(device);
-
device_svc_resolved(device, device->bdaddr_type, 0);

store_services(device);
@@ -5069,11 +5066,6 @@ gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id)

g_free(attio);

- if (device->attios != NULL || device->attios_offline != NULL)
- return TRUE;
-
- attio_cleanup(device);
-
return TRUE;
}

--
2.2.0.rc0.207.ga3a616c