2014-12-09 00:40:40

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 00/15] core: Use shared/gatt for GATT client role

This patch set integrates shared/gatt into the core bluetoothd for GATT
client role. This set makes the following changes:

- Made small fixes and added getters to support legacy code paths.

- Removed explicit primary and included service discovery logic within
src/device in favor of initializing a bt_gatt_client. This includes
deferring service/profile registration until the bt_gatt_client is ready,
and proper handling for "Service Changed" events using gatt_db's service
added/removed callbacks. When new services are added during a connection,
the profiles are explicitly probed. The code still creates and maintains
instances of struct gatt_primary to support legacy plugins.

- Introduced gatt-callbacks.h. This defines callbacks that are invoked when
services are removed, the gatt-client becomes ready, and when the connection
is closed. These callbacks are similar and parallel to the existing ones
defined in attio.h and provide an alternate solution for shared/gatt.
gatt-callbacks.h is not meant to be the long-term solution and these may
perhaps be better served by new GATT specific functions added to the
btd_profile/btd_service framework.

- Rewrote the built-in GAP/GATT profile. It no longer handles the GATT
service and does no MTU exchange (which was racy and did not belong in a
profile in the first place) as these are handled by shared/gatt-client.
Renamed the profile to profiles/gap as it only handles the remote GAP
service and added handling for the "Device Name" characteristic. The profile
now uses gatt-callbacks instead of attio and bt_gatt_client/gatt_db instead
of GAttrib.

Arman Uguray (15):
attrib/gattrib: Add g_attrib_get_att.
shared/att: Add bt_att_get_fd.
attrib: Check if attrib is NULL in functions
shared/att: cancel_all before calling disconnect cb
shared/gatt-db: Make accessors work on const ptr
shared/gatt-db: Add UUID arg to foreach_service
core: device: Use bt_att_register_disconnect.
core: device: Use shared/gatt-client for GATT.
core: Rename device_attach_attrib
core: Use gatt_db service callbacks
core: Introduce gatt-callbacks
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.am | 1 +
Makefile.plugins | 4 +-
attrib/gattrib.c | 31 ++-
attrib/gattrib.h | 3 +
profiles/gap/gas.c | 321 +++++++++++++++++++++++++++
profiles/gatt/gas.c | 457 --------------------------------------
src/attrib-server.c | 2 +-
src/device.c | 591 +++++++++++++++++++++++++++++++++++--------------
src/device.h | 2 +-
src/gatt-callbacks.h | 36 +++
src/shared/att-types.h | 3 +-
src/shared/att.c | 12 +-
src/shared/att.h | 1 +
src/shared/gatt-db.c | 41 ++--
src/shared/gatt-db.h | 28 ++-
tools/btgatt-client.c | 9 +-
tools/btgatt-server.c | 2 +-
unit/test-gatt.c | 4 +-
18 files changed, 885 insertions(+), 663 deletions(-)
create mode 100644 profiles/gap/gas.c
delete mode 100644 profiles/gatt/gas.c
create mode 100644 src/gatt-callbacks.h

--
2.2.0.rc0.207.ga3a616c



2014-12-09 15:50:17

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH BlueZ 07/15] core: device: Use bt_att_register_disconnect.

Hi Arman,

On Tue, Dec 09, 2014, Arman Uguray wrote:
> > On Tue, Dec 9, 2014 at 12:07 AM, Johan Hedberg <[email protected]> wrote:
> > Hi Arman,
> >
> > On Mon, Dec 08, 2014, Arman Uguray wrote:
> >> + dev->att = bt_att_ref(g_attrib_get_att(attrib));
> >
> > I'd expect a function called "get" to return a new reference, so the
> > extra ref() shouldn't be needed.
> >
>
> I see it mostly as a raw getter and adding ref semantics to it seems
> confusing to me. Unless we call it g_attrib_get_att_ref or something
> to that end.

Could be that this is also just because of me spending too much time
looking at kernel code. There pretty much everywhere foo_ref() is called
foo_get() and foo_unref() is called foo_put(). I thought this was the
common behavior for user space *get() functions too, but looking now I
could only find agent_get() following this practice. The most annoying
thing about your code was really the nested function calls so whatever
solution gets rid of that is a step towards the better.

Johan

2014-12-09 13:12:02

by Arman Uguray

[permalink] [raw]
Subject: Re: [PATCH BlueZ 07/15] core: device: Use bt_att_register_disconnect.

Hi Johan,

> On Tue, Dec 9, 2014 at 12:07 AM, Johan Hedberg <[email protected]> wrote:
> Hi Arman,
>
> On Mon, Dec 08, 2014, Arman Uguray wrote:
>> + dev->att = bt_att_ref(g_attrib_get_att(attrib));
>
> I'd expect a function called "get" to return a new reference, so the
> extra ref() shouldn't be needed.
>

I see it mostly as a raw getter and adding ref semantics to it seems
confusing to me. Unless we call it g_attrib_get_att_ref or something
to that end.

> Btw, I hope this is just a temporary function that you've introduced to
> be able to do the conversions in smaller bits, and that it'll be removed
> as soon as the conversions are done?
>

Pretty much. Basically we have the old code work through the GAttrib
shim while the new code work directly through the bt_att that the
GAttrib is wrapping. Once all conversions are done, device won't need
to include any of the GAttrib code and this function can be removed.

Thanks,
Arman

2014-12-09 13:04:02

by Arman Uguray

[permalink] [raw]
Subject: Re: [PATCH BlueZ 02/15] shared/att: Add bt_att_get_fd.

Hi Luiz,

> On Tue, Dec 9, 2014 at 4:50 AM, Luiz Augusto von Dentz <[email protected]> wrote:
> Hi Arman,
>
> On Tue, Dec 9, 2014 at 2:40 AM, Arman Uguray <[email protected]> wrote:
>> This patch adds a getter for a bt_att structure's underlying connection
>> file descriptor.
>> ---
>> src/shared/att.c | 8 ++++++++
>> src/shared/att.h | 1 +
>> 2 files changed, 9 insertions(+)
>>
>> diff --git a/src/shared/att.c b/src/shared/att.c
>> index ee425d8..9511bb2 100644
>> --- a/src/shared/att.c
>> +++ b/src/shared/att.c
>> @@ -922,6 +922,14 @@ void bt_att_unref(struct bt_att *att)
>> free(att);
>> }
>>
>> +int bt_att_get_fd(struct bt_att *att)
>> +{
>> + if (!att)
>> + return -EINVAL;
>> +
>> + return att->fd;
>> +}
>> +
>> bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close)
>> {
>> if (!att || !att->io)
>> diff --git a/src/shared/att.h b/src/shared/att.h
>> index 99b5a5b..b946b18 100644
>> --- a/src/shared/att.h
>> +++ b/src/shared/att.h
>> @@ -33,6 +33,7 @@ struct bt_att *bt_att_new(int fd);
>> struct bt_att *bt_att_ref(struct bt_att *att);
>> void bt_att_unref(struct bt_att *att);
>>
>> +int bt_att_get_fd(struct bt_att *att);
>
> It is not clear why this would be necessary?
>

I added this to make one call site work where src/device uses it to
obtain a socket error in the event of a disconnect. I could probably
avoid this by getting the fd out of the GIOChannel which device is
still caching anyway.

-Arman

2014-12-09 12:53:56

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ 00/15] core: Use shared/gatt for GATT client role

Hi Arman,

On Tue, Dec 9, 2014 at 2:40 AM, Arman Uguray <[email protected]> wrote:
> This patch set integrates shared/gatt into the core bluetoothd for GATT
> client role. This set makes the following changes:
>
> - Made small fixes and added getters to support legacy code paths.
>
> - Removed explicit primary and included service discovery logic within
> src/device in favor of initializing a bt_gatt_client. This includes
> deferring service/profile registration until the bt_gatt_client is ready,
> and proper handling for "Service Changed" events using gatt_db's service
> added/removed callbacks. When new services are added during a connection,
> the profiles are explicitly probed. The code still creates and maintains
> instances of struct gatt_primary to support legacy plugins.
>
> - Introduced gatt-callbacks.h. This defines callbacks that are invoked when
> services are removed, the gatt-client becomes ready, and when the connection
> is closed. These callbacks are similar and parallel to the existing ones
> defined in attio.h and provide an alternate solution for shared/gatt.
> gatt-callbacks.h is not meant to be the long-term solution and these may
> perhaps be better served by new GATT specific functions added to the
> btd_profile/btd_service framework.
>
> - Rewrote the built-in GAP/GATT profile. It no longer handles the GATT
> service and does no MTU exchange (which was racy and did not belong in a
> profile in the first place) as these are handled by shared/gatt-client.
> Renamed the profile to profiles/gap as it only handles the remote GAP
> service and added handling for the "Device Name" characteristic. The profile
> now uses gatt-callbacks instead of attio and bt_gatt_client/gatt_db instead
> of GAttrib.
>
> Arman Uguray (15):
> attrib/gattrib: Add g_attrib_get_att.
> shared/att: Add bt_att_get_fd.
> attrib: Check if attrib is NULL in functions
> shared/att: cancel_all before calling disconnect cb
> shared/gatt-db: Make accessors work on const ptr
> shared/gatt-db: Add UUID arg to foreach_service
> core: device: Use bt_att_register_disconnect.
> core: device: Use shared/gatt-client for GATT.
> core: Rename device_attach_attrib
> core: Use gatt_db service callbacks
> core: Introduce gatt-callbacks
> 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.am | 1 +
> Makefile.plugins | 4 +-
> attrib/gattrib.c | 31 ++-
> attrib/gattrib.h | 3 +
> profiles/gap/gas.c | 321 +++++++++++++++++++++++++++
> profiles/gatt/gas.c | 457 --------------------------------------
> src/attrib-server.c | 2 +-
> src/device.c | 591 +++++++++++++++++++++++++++++++++++--------------
> src/device.h | 2 +-
> src/gatt-callbacks.h | 36 +++
> src/shared/att-types.h | 3 +-
> src/shared/att.c | 12 +-
> src/shared/att.h | 1 +
> src/shared/gatt-db.c | 41 ++--
> src/shared/gatt-db.h | 28 ++-
> tools/btgatt-client.c | 9 +-
> tools/btgatt-server.c | 2 +-
> unit/test-gatt.c | 4 +-
> 18 files changed, 885 insertions(+), 663 deletions(-)
> create mode 100644 profiles/gap/gas.c
> delete mode 100644 profiles/gatt/gas.c
> create mode 100644 src/gatt-callbacks.h
>
> --
> 2.2.0.rc0.207.ga3a616c

I went ahead and applied the fixes not related to core changes, please
rebase the rest and work in the comments.


--
Luiz Augusto von Dentz

2014-12-09 12:50:42

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ 02/15] shared/att: Add bt_att_get_fd.

Hi Arman,

On Tue, Dec 9, 2014 at 2:40 AM, Arman Uguray <[email protected]> wrote:
> This patch adds a getter for a bt_att structure's underlying connection
> file descriptor.
> ---
> src/shared/att.c | 8 ++++++++
> src/shared/att.h | 1 +
> 2 files changed, 9 insertions(+)
>
> diff --git a/src/shared/att.c b/src/shared/att.c
> index ee425d8..9511bb2 100644
> --- a/src/shared/att.c
> +++ b/src/shared/att.c
> @@ -922,6 +922,14 @@ void bt_att_unref(struct bt_att *att)
> free(att);
> }
>
> +int bt_att_get_fd(struct bt_att *att)
> +{
> + if (!att)
> + return -EINVAL;
> +
> + return att->fd;
> +}
> +
> bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close)
> {
> if (!att || !att->io)
> diff --git a/src/shared/att.h b/src/shared/att.h
> index 99b5a5b..b946b18 100644
> --- a/src/shared/att.h
> +++ b/src/shared/att.h
> @@ -33,6 +33,7 @@ struct bt_att *bt_att_new(int fd);
> struct bt_att *bt_att_ref(struct bt_att *att);
> void bt_att_unref(struct bt_att *att);
>
> +int bt_att_get_fd(struct bt_att *att);

It is not clear why this would be necessary?


--
Luiz Augusto von Dentz

2014-12-09 08:07:56

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH BlueZ 07/15] core: device: Use bt_att_register_disconnect.

Hi Arman,

On Mon, Dec 08, 2014, Arman Uguray wrote:
> + dev->att = bt_att_ref(g_attrib_get_att(attrib));

I'd expect a function called "get" to return a new reference, so the
extra ref() shouldn't be needed.

Btw, I hope this is just a temporary function that you've introduced to
be able to do the conversions in smaller bits, and that it'll be removed
as soon as the conversions are done?

Johan

2014-12-09 00:40:48

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 08/15] core: device: Use shared/gatt-client for GATT.

This patch changes the GATT service discovery code path with the
following:
- As part for device_attach_attrib, a bt_gatt_client structure is
created that performs MTU exchange and service discovery, caches the
services in an internal list, and handles the remote GATT service.

- The device_browse_primary code path obtains service information
by iterating through bt_gatt_client's services, instead of directly
initiating primary and secondary service discovery using attrib/gatt.
If the bt_gatt_client isn't ready at the time, the code defers
registration of services and profile probing until the ready handler
is called.

- The attio connected callbacks are invoked from bt_gatt_client's
ready handler instead of device_attach_attrib.
---
src/device.c | 310 ++++++++++++++++++++++++++-----------------------
src/shared/att-types.h | 3 +-
2 files changed, 168 insertions(+), 145 deletions(-)

diff --git a/src/device.c b/src/device.c
index b31e98d..dcc03af 100644
--- a/src/device.c
+++ b/src/device.c
@@ -45,11 +45,13 @@

#include "log.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 "btio/btio.h"
-#include "lib/uuid.h"
#include "lib/mgmt.h"
#include "attrib/att.h"
#include "hcid.h"
@@ -74,6 +76,10 @@
#define DISCONNECT_TIMER 2
#define DISCOVERY_TIMER 1

+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
static DBusConnection *dbus_conn = NULL;
unsigned service_state_cb_id;

@@ -205,9 +211,18 @@ struct btd_device {
GSList *attios_offline;
guint attachid; /* Attrib server attach */

- struct bt_att *att; /* The new ATT transport */
+ struct bt_att *att; /* The new ATT transport */
+ uint16_t att_mtu; /* The ATT MTU */
unsigned int att_disconn_id;

+ /*
+ * TODO: For now, device creates and owns the client-role gatt_db, but
+ * this needs to be persisted in a more central place so that proper
+ * attribute cache support can be built.
+ */
+ struct gatt_db *client_db; /* GATT client cache */
+ struct bt_gatt_client *gatt_client; /* GATT client implementation */
+
struct bearer_state bredr_state;
struct bearer_state le_state;

@@ -236,7 +251,7 @@ static const uint16_t uuid_list[] = {
0
};

-static int device_browse_primary(struct btd_device *device, DBusMessage *msg);
+static int device_browse_gatt(struct btd_device *device, DBusMessage *msg);
static int device_browse_sdp(struct btd_device *device, DBusMessage *msg);

static struct bearer_state *get_state(struct btd_device *dev,
@@ -463,6 +478,21 @@ static void browse_request_free(struct browse_req *req)
g_free(req);
}

+static void gatt_client_cleanup(struct btd_device *device)
+{
+ if (!device->gatt_client)
+ return;
+
+ bt_gatt_client_set_service_changed(device->gatt_client, NULL, NULL,
+ NULL);
+ bt_gatt_client_set_ready_handler(device->gatt_client, NULL, NULL, NULL);
+ bt_gatt_client_unref(device->gatt_client);
+ device->gatt_client = NULL;
+
+ if (!device->le_state.bonded)
+ gatt_db_clear(device->client_db);
+}
+
static void attio_cleanup(struct btd_device *device)
{
if (device->attachid) {
@@ -480,6 +510,8 @@ static void attio_cleanup(struct btd_device *device)
device->att_io = NULL;
}

+ gatt_client_cleanup(device);
+
if (device->att) {
bt_att_unref(device->att);
device->att = NULL;
@@ -530,6 +562,8 @@ static void device_free(gpointer user_data)

attio_cleanup(device);

+ gatt_db_unref(device->client_db);
+
if (device->tmp_records)
sdp_list_free(device->tmp_records,
(sdp_free_func_t) sdp_record_free);
@@ -1450,7 +1484,7 @@ resolve_services:
if (bdaddr_type == BDADDR_BREDR)
err = device_browse_sdp(dev, msg);
else
- err = device_browse_primary(dev, msg);
+ err = device_browse_gatt(dev, msg);
if (err < 0)
return btd_error_failed(msg, strerror(-err));

@@ -2344,6 +2378,12 @@ static struct btd_device *device_new(struct btd_adapter *adapter,
if (device == NULL)
return NULL;

+ device->client_db = gatt_db_new();
+ if (!device->client_db) {
+ g_free(device);
+ return NULL;
+ }
+
address_up = g_ascii_strup(address, -1);
device->path = g_strdup_printf("%s/dev_%s", adapter_path, address_up);
g_strdelimit(device->path, ":", '_');
@@ -3162,7 +3202,7 @@ static int primary_cmp(gconstpointer a, gconstpointer b)
return memcmp(a, b, sizeof(struct gatt_primary));
}

-static void update_gatt_services(struct browse_req *req, GSList *current,
+static void update_gatt_uuids(struct browse_req *req, GSList *current,
GSList *found)
{
GSList *l, *lmatch;
@@ -3445,41 +3485,6 @@ done:
attio_cleanup(device);
}

-static void register_all_services(struct browse_req *req, GSList *services)
-{
- struct btd_device *device = req->device;
-
- btd_device_set_temporary(device, FALSE);
-
- update_gatt_services(req, device->primaries, services);
- g_slist_free_full(device->primaries, g_free);
- device->primaries = NULL;
-
- device_register_primaries(device, services, -1);
-
- device_probe_profiles(device, req->profiles_added);
-
- if (device->attios == NULL && device->attios_offline == NULL)
- attio_cleanup(device);
-
- g_dbus_emit_property_changed(dbus_conn, device->path,
- DEVICE_INTERFACE, "UUIDs");
-
- device_svc_resolved(device, device->bdaddr_type, 0);
-
- store_services(device);
-
- browse_request_free(req);
-}
-
-static int service_by_range_cmp(gconstpointer a, gconstpointer b)
-{
- const struct gatt_primary *prim = a;
- const struct att_range *range = b;
-
- return memcmp(&prim->range, range, sizeof(*range));
-}
-
static void send_le_browse_response(struct browse_req *req)
{
struct btd_device *dev = req->device;
@@ -3510,122 +3515,132 @@ static void send_le_browse_response(struct browse_req *req)
g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID);
}

-static void find_included_cb(uint8_t status, GSList *includes, void *user_data)
+static void add_primary(struct gatt_db_attribute *attr, void *user_data)
{
- struct included_search *search = user_data;
- struct btd_device *device = search->req->device;
struct gatt_primary *prim;
- GSList *l;
+ GSList **services = user_data;
+ bt_uuid_t uuid;

- DBG("status %u", status);
+ prim = g_new0(struct gatt_primary, 1);
+ if (!prim) {
+ DBG("Failed to allocate gatt_primary structure");
+ return;
+ }

- if (device->attrib == NULL || status) {
- struct browse_req *req = device->browse;
+ gatt_db_attribute_get_service_handles(attr, &prim->range.start,
+ &prim->range.end);
+ gatt_db_attribute_get_service_uuid(attr, &uuid);
+ bt_uuid_to_string(&uuid, prim->uuid, sizeof(prim->uuid));

- if (status)
- error("Find included services failed: %s (%d)",
- att_ecode2str(status), status);
- else
- error("Disconnected while doing included discovery");
+ *services = g_slist_append(*services, prim);
+}

- if (!req)
- goto complete;
+static void register_gatt_services(struct browse_req *req)
+{
+ struct btd_device *device = req->device;
+ GSList *services = NULL;

- send_le_browse_response(req);
- device->browse = NULL;
- browse_request_free(req);
+ if (!bt_gatt_client_is_ready(device->gatt_client))
+ return;

- goto complete;
- }
+ /*
+ * TODO: Remove the primaries list entirely once all profiles use
+ * shared/gatt.
+ */
+ gatt_db_foreach_service(device->client_db, NULL, add_primary,
+ &services);

- if (includes == NULL)
- goto next;
+ btd_device_set_temporary(device, FALSE);

- for (l = includes; l; l = l->next) {
- struct gatt_included *incl = l->data;
+ update_gatt_uuids(req, device->primaries, services);
+ g_slist_free_full(device->primaries, g_free);
+ device->primaries = NULL;

- if (g_slist_find_custom(search->services, &incl->range,
- service_by_range_cmp))
- continue;
+ device_register_primaries(device, services, -1);

- prim = g_new0(struct gatt_primary, 1);
- memcpy(prim->uuid, incl->uuid, sizeof(prim->uuid));
- memcpy(&prim->range, &incl->range, sizeof(prim->range));
+ device_probe_profiles(device, req->profiles_added);

- search->services = g_slist_append(search->services, prim);
- }
+ if (device->attios == NULL && device->attios_offline == NULL)
+ attio_cleanup(device);

-next:
- search->current = search->current->next;
- if (search->current == NULL) {
- register_all_services(search->req, search->services);
- search->services = NULL;
- goto complete;
- }
+ device_svc_resolved(device, device->bdaddr_type, 0);

- prim = search->current->data;
- gatt_find_included(device->attrib, prim->range.start, prim->range.end,
- find_included_cb, search);
- return;
+ store_services(device);

-complete:
- g_slist_free_full(search->services, g_free);
- g_free(search);
+ browse_request_free(req);
}

-static void find_included_services(struct browse_req *req, GSList *services)
+static void gatt_client_ready_cb(bool success, uint8_t att_ecode,
+ void *user_data)
{
- struct btd_device *device = req->device;
- struct included_search *search;
- struct gatt_primary *prim;
- GSList *l;
+ struct btd_device *device = user_data;
+
+ DBG("gatt-client ready -- status: %s, ATT error: %u",
+ success ? "success" : "failed",
+ att_ecode);

- DBG("service count %u", g_slist_length(services));
+ if (!success) {
+ if (device->browse) {
+ struct browse_req *req = device->browse;
+
+ send_le_browse_response(req);
+ device->browse = NULL;
+ browse_request_free(req);
+ }

- if (services == NULL) {
- DBG("No services found");
- register_all_services(req, NULL);
return;
}

- search = g_new0(struct included_search, 1);
- search->req = req;
-
- /* We have to completely duplicate the data in order to have a
- * clearly defined responsibility of freeing regardless of
- * failure or success. Otherwise memory leaks are inevitable.
- */
- for (l = services; l; l = g_slist_next(l)) {
- struct gatt_primary *dup;
+ device->att_mtu = bt_att_get_mtu(device->att);

- dup = g_memdup(l->data, sizeof(struct gatt_primary));
+ DBG("gatt-client exchanged MTU: %u", device->att_mtu);

- search->services = g_slist_append(search->services, dup);
- }
+ if (device->browse)
+ register_gatt_services(device->browse);

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

- prim = search->current->data;
- gatt_find_included(device->attrib, prim->range.start, prim->range.end,
- find_included_cb, search);
+static void gatt_client_service_changed(uint16_t start_handle,
+ uint16_t end_handle,
+ void *user_data)
+{
+ DBG("gatt-client: Service Changed: start 0x%04x, end: 0x%04x",
+ start_handle, end_handle);
}

-static void primary_cb(uint8_t status, GSList *services, void *user_data)
+static void gatt_client_init(struct btd_device *device)
{
- struct browse_req *req = user_data;
+ gatt_client_cleanup(device);

- DBG("status %u", status);
+ device->gatt_client = bt_gatt_client_new(device->client_db, device->att,
+ device->att_mtu);
+ if (!device->gatt_client) {
+ DBG("Failed to initialize gatt-client");
+ return;
+ }

- if (status) {
- struct btd_device *device = req->device;
+ if (!bt_gatt_client_set_ready_handler(device->gatt_client,
+ gatt_client_ready_cb,
+ device, NULL)) {
+ DBG("Failed to set ready handler for gatt-client");
+ gatt_client_cleanup(device);
+ return;
+ }

- send_le_browse_response(req);
- device->browse = NULL;
- browse_request_free(req);
+ if (!bt_gatt_client_set_service_changed(device->gatt_client,
+ gatt_client_service_changed,
+ device, NULL)) {
+ DBG("Failed to set service changed handler for gatt-client");
+ gatt_client_cleanup(device);
return;
}

- find_included_services(req, services);
+ DBG("gatt-client created");
}

bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
@@ -3660,10 +3675,8 @@ bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
}
}

- if (cid == ATT_CID)
- mtu = BT_ATT_DEFAULT_LE_MTU;
-
- attrib = g_attrib_new(io, mtu);
+ dev->att_mtu = MIN(mtu, BT_ATT_MAX_LE_MTU);
+ attrib = g_attrib_new(io, dev->att_mtu);
if (!attrib) {
error("Unable to create new GAttrib instance");
return false;
@@ -3678,11 +3691,12 @@ bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)

dev->attrib = attrib;
dev->att = bt_att_ref(g_attrib_get_att(attrib));
-
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);
+
/*
* Remove the device from the connect_list and give the passive
* scanning another chance to be restarted in case there are
@@ -3690,8 +3704,6 @@ bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
*/
adapter_connect_list_remove(dev->adapter, dev);

- g_slist_foreach(dev->attios, attio_connected, dev->attrib);
-
return true;
}

@@ -3742,7 +3754,7 @@ done:

if (device->connect) {
if (!device->le_state.svc_resolved)
- device_browse_primary(device, NULL);
+ device_browse_gatt(device, NULL);

if (err < 0)
reply = btd_error_failed(device->connect,
@@ -3845,16 +3857,32 @@ static void att_browse_error_cb(const GError *gerr, gpointer user_data)
browse_request_free(req);
}

+static void browse_gatt_client(struct browse_req *req)
+{
+ struct btd_device *device = req->device;
+
+ if (!device->gatt_client) {
+ DBG("No gatt-client currently attached");
+ return;
+ }
+
+ /*
+ * If gatt-client is ready, then register all services. Otherwise, this
+ * will be deferred until client becomes ready.
+ */
+ if (bt_gatt_client_is_ready(device->gatt_client))
+ register_gatt_services(req);
+}
+
static void att_browse_cb(gpointer user_data)
{
struct att_callbacks *attcb = user_data;
struct btd_device *device = attcb->user_data;

- gatt_discover_primary(device->attrib, NULL, primary_cb,
- device->browse);
+ browse_gatt_client(device->browse);
}

-static int device_browse_primary(struct btd_device *device, DBusMessage *msg)
+static int device_browse_gatt(struct btd_device *device, DBusMessage *msg)
{
struct btd_adapter *adapter = device->adapter;
struct att_callbacks *attcb;
@@ -3869,7 +3897,7 @@ static int device_browse_primary(struct btd_device *device, DBusMessage *msg)
device->browse = req;

if (device->attrib) {
- gatt_discover_primary(device->attrib, NULL, primary_cb, req);
+ browse_gatt_client(device->browse);
goto done;
}

@@ -3986,7 +4014,7 @@ int device_discover_services(struct btd_device *device)
if (device->bredr)
err = device_browse_sdp(device, NULL);
else
- err = device_browse_primary(device, NULL);
+ err = device_browse_gatt(device, NULL);

if (err == 0 && device->discov_timer) {
g_source_remove(device->discov_timer);
@@ -4132,7 +4160,7 @@ static gboolean start_discovery(gpointer user_data)
if (device->bredr)
device_browse_sdp(device, NULL);
else
- device_browse_primary(device, NULL);
+ device_browse_gatt(device, NULL);

device->discov_timer = 0;

@@ -4269,7 +4297,7 @@ void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
if (bdaddr_type == BDADDR_BREDR)
device_browse_sdp(device, bonding->msg);
else
- device_browse_primary(device, bonding->msg);
+ device_browse_gatt(device, bonding->msg);

bonding_request_free(bonding);
} else if (!state->svc_resolved) {
@@ -4742,16 +4770,10 @@ GSList *btd_device_get_primaries(struct btd_device *device)
void btd_device_gatt_set_service_changed(struct btd_device *device,
uint16_t start, uint16_t end)
{
- GSList *l;
-
- for (l = device->primaries; l; l = g_slist_next(l)) {
- struct gatt_primary *prim = l->data;
-
- if (start <= prim->range.end && end >= prim->range.start)
- prim->changed = TRUE;
- }
-
- device_browse_primary(device, NULL);
+ /*
+ * TODO: Remove this function and handle service changed via
+ * gatt-client.
+ */
}

void btd_device_add_uuid(struct btd_device *device, const char *uuid)
diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index 3d8829a..8b6d537 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -27,7 +27,8 @@
#define __packed __attribute__((packed))
#endif

-#define BT_ATT_DEFAULT_LE_MTU 23
+#define BT_ATT_DEFAULT_LE_MTU 23
+#define BT_ATT_MAX_LE_MTU 517

/* ATT protocol opcodes */
#define BT_ATT_OP_ERROR_RSP 0x01
--
2.2.0.rc0.207.ga3a616c


2014-12-09 00:40:53

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 13/15] 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-09 00:40:51

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 11/15] core: Introduce gatt-callbacks

This patch introduces src/gatt-callbacks.h. This defines API functions
to get notified of bt_gatt_client related events, such as ready,
service removed, and disconnects. These callbacks are parallel to the
existing attio.h functions and will provide a temporary alternative
during the transition from attrib/ to shared/gatt.
---
Makefile.am | 1 +
src/device.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++----
src/gatt-callbacks.h | 36 ++++++++++++
3 files changed, 188 insertions(+), 11 deletions(-)
create mode 100644 src/gatt-callbacks.h

diff --git a/Makefile.am b/Makefile.am
index c506122..ad2c336 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -182,6 +182,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
src/gatt-dbus.h src/gatt-dbus.c \
src/gatt.h src/gatt.c \
src/device.h src/device.c src/attio.h \
+ src/gatt-callbacks.h \
src/dbus-common.c src/dbus-common.h \
src/eir.h src/eir.c
src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
diff --git a/src/device.c b/src/device.c
index 0bfac1e..4b42580 100644
--- a/src/device.c
+++ b/src/device.c
@@ -59,6 +59,7 @@
#include "attrib/gattrib.h"
#include "attio.h"
#include "device.h"
+#include "gatt-callbacks.h"
#include "profile.h"
#include "service.h"
#include "dbus-common.h"
@@ -145,6 +146,14 @@ struct attio_data {
gpointer user_data;
};

+struct gatt_cb_data {
+ unsigned int id;
+ btd_gatt_client_ready_t ready_func;
+ btd_gatt_service_removed_t svc_removed_func;
+ btd_gatt_disconnect_t disconn_func;
+ void *user_data;
+};
+
struct svc_callback {
unsigned int id;
guint idle_id;
@@ -222,6 +231,8 @@ struct btd_device {
*/
struct gatt_db *client_db; /* GATT client cache */
struct bt_gatt_client *gatt_client; /* GATT client implementation */
+ GSList *gatt_callbacks;
+ unsigned int next_gatt_cb_id;

struct bearer_state bredr_state;
struct bearer_state le_state;
@@ -559,6 +570,7 @@ static void device_free(gpointer user_data)
g_slist_free_full(device->attios, g_free);
g_slist_free_full(device->attios_offline, g_free);
g_slist_free_full(device->svc_callbacks, svc_dev_remove);
+ g_slist_free_full(device->gatt_callbacks, g_free);

attio_cleanup(device);

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

+struct svc_removed_data {
+ struct gatt_db_attribute *attr;
+ struct bt_gatt_client *client;
+};
+
+static void notify_gatt_service_removed(gpointer data, gpointer user_data)
+{
+ struct gatt_cb_data *cb = data;
+ struct svc_removed_data *rm_data = user_data;
+
+ if (cb->svc_removed_func)
+ cb->svc_removed_func(rm_data->client, rm_data->attr,
+ cb->user_data);
+}
+
static void gatt_service_removed(struct gatt_db_attribute *attr,
void *user_data)
{
@@ -2446,6 +2473,7 @@ static void gatt_service_removed(struct gatt_db_attribute *attr,
GSList *l;
struct gatt_primary *prim;
uint16_t start, end;
+ struct svc_removed_data data;

if (!bt_gatt_client_is_ready(device->gatt_client))
return;
@@ -2469,13 +2497,19 @@ static void gatt_service_removed(struct gatt_db_attribute *attr,

store_services(device);

- /*
- * TODO: Notify the profiles somehow. It may be sufficient for each
- * profile to register a service_removed handler.
- */
-
g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "UUIDs");
+
+ data.attr = attr;
+ data.client = device->gatt_client;
+
+ g_slist_foreach(device->gatt_callbacks, notify_gatt_service_removed,
+ &data);
+
+ /*
+ * TODO: Remove profiles matching this UUID if no other GATT services
+ * with the same UUID exist.
+ */
}

static struct btd_device *device_new(struct btd_adapter *adapter,
@@ -3560,6 +3594,16 @@ static void attio_disconnected(gpointer data, gpointer user_data)
attio->dcfunc(attio->user_data);
}

+static void gatt_disconnected(gpointer data, gpointer user_data)
+{
+ struct gatt_cb_data *gatt_data = data;
+
+ DBG("");
+
+ if (gatt_data->disconn_func)
+ gatt_data->disconn_func(gatt_data->user_data);
+}
+
static void att_disconnected_cb(void *user_data)
{
struct btd_device *device = user_data;
@@ -3578,6 +3622,7 @@ static void att_disconnected_cb(void *user_data)
DBG("%s (%d)", strerror(err), err);

g_slist_foreach(device->attios, attio_disconnected, NULL);
+ g_slist_foreach(device->gatt_callbacks, gatt_disconnected, NULL);

if (!device_get_auto_connect(device)) {
DBG("Automatic connection disabled");
@@ -3651,7 +3696,15 @@ static void register_gatt_services(struct browse_req *req)

device_probe_profiles(device, req->profiles_added);

- if (device->attios == NULL && device->attios_offline == NULL)
+ /*
+ * TODO: This check seems unnecessary. We may not always want to cleanup
+ * the connection since there will always be built-in plugins who want
+ * to interact with remote GATT services. Even if we didn't have those,
+ * the GATT D-Bus API will need to interact with these, so we should
+ * later remove this check entirely.
+ */
+ if (device->attios == NULL && device->attios_offline == NULL &&
+ device->gatt_callbacks == NULL)
attio_cleanup(device);

device_svc_resolved(device, device->bdaddr_type, 0);
@@ -3661,6 +3714,18 @@ static void register_gatt_services(struct browse_req *req)
browse_request_free(req);
}

+static void notify_gatt_client_ready(gpointer data, gpointer user_data)
+{
+ struct gatt_cb_data *gatt_data = data;
+ struct btd_device *device = user_data;
+
+ DBG("");
+
+ if (gatt_data->ready_func)
+ gatt_data->ready_func(device->gatt_client, device->client_db,
+ gatt_data->user_data);
+}
+
static void gatt_client_ready_cb(bool success, uint8_t att_ecode,
void *user_data)
{
@@ -3689,11 +3754,9 @@ 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.
- */
g_slist_foreach(device->attios, attio_connected, device->attrib);
+ g_slist_foreach(device->gatt_callbacks, notify_gatt_client_ready,
+ device);
}

static void gatt_client_service_changed(uint16_t start_handle,
@@ -5080,7 +5143,8 @@ gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id)

g_free(attio);

- if (device->attios != NULL || device->attios_offline != NULL)
+ if (device->attios != NULL || device->attios_offline != NULL ||
+ device->gatt_callbacks != NULL)
return TRUE;

attio_cleanup(device);
@@ -5088,6 +5152,82 @@ gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id)
return TRUE;
}

+unsigned int btd_device_add_gatt_callbacks(struct btd_device *device,
+ btd_gatt_client_ready_t ready_func,
+ btd_gatt_service_removed_t service_removed_func,
+ btd_gatt_disconnect_t disconnect_func,
+ void *user_data)
+{
+ struct gatt_cb_data *gatt_data;
+
+ gatt_data = new0(struct gatt_cb_data, 1);
+ if (!gatt_data)
+ return 0;
+
+ if (device->next_gatt_cb_id < 1)
+ device->next_gatt_cb_id = 1;
+
+ device_set_auto_connect(device, TRUE);
+
+ gatt_data->id = device->next_gatt_cb_id++;
+ gatt_data->ready_func = ready_func;
+ gatt_data->svc_removed_func = service_removed_func;
+ gatt_data->disconn_func = disconnect_func;
+ gatt_data->user_data = user_data;
+
+ /*
+ * TODO: The connection might be incoming from attrib-server (see
+ * btd_device_add_attio_callback). I don't think this is a good place to
+ * attach the GAttrib to the device. We should come up with a more
+ * unified flow for attaching the GAttrib, bt_att, and bt_gatt_client
+ * for incoming and outgoing connections.
+ */
+ device->gatt_callbacks = g_slist_append(device->gatt_callbacks,
+ gatt_data);
+
+ if (ready_func && bt_gatt_client_is_ready(device->gatt_client))
+ ready_func(device->gatt_client, device->client_db, user_data);
+
+ return gatt_data->id;
+}
+
+static int gatt_cb_id_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_cb_data *gatt_data = a;
+ guint id = GPOINTER_TO_UINT(b);
+
+ return gatt_data->id - id;
+}
+
+bool btd_device_remove_gatt_callbacks(struct btd_device *device,
+ unsigned int id)
+{
+ struct gatt_cb_data *gatt_data;
+ GSList *l;
+
+ l = g_slist_find_custom(device->gatt_callbacks, GUINT_TO_POINTER(id),
+ gatt_cb_id_cmp);
+ if (!l)
+ return false;
+
+ gatt_data = l->data;
+ device->gatt_callbacks = g_slist_remove(device->gatt_callbacks,
+ gatt_data);
+ free(gatt_data);
+
+ /*
+ * TODO: Consider removing this check and avoiding cleanup as it seems
+ * unnecessary.
+ */
+ if (device->attios != NULL || device->attios_offline != NULL ||
+ device->gatt_callbacks != NULL)
+ return true;
+
+ attio_cleanup(device);
+
+ return true;
+}
+
void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
uint16_t vendor, uint16_t product, uint16_t version)
{
diff --git a/src/gatt-callbacks.h b/src/gatt-callbacks.h
new file mode 100644
index 0000000..2ac1800
--- /dev/null
+++ b/src/gatt-callbacks.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Google Inc.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef void (*btd_gatt_client_ready_t) (struct bt_gatt_client *client,
+ struct gatt_db *db,
+ void *user_data);
+typedef void (*btd_gatt_service_removed_t) (struct bt_gatt_client *client,
+ struct gatt_db_attribute *attrib,
+ void *user_data);
+typedef void (*btd_gatt_disconnect_t) (void *user_data);
+
+unsigned int btd_device_add_gatt_callbacks(struct btd_device *device,
+ btd_gatt_client_ready_t ready_func,
+ btd_gatt_service_removed_t service_removed_func,
+ btd_gatt_disconnect_t disconnect_func,
+ void *user_data);
+bool btd_device_remove_gatt_callbacks(struct btd_device *device,
+ unsigned int id);
--
2.2.0.rc0.207.ga3a616c


2014-12-09 00:40:52

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 12/15] 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-09 00:40:49

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 09/15] core: Rename device_attach_attrib

This patch renames the device_attach_attrib function to
device_attach_att_transport to remove ambiguities arise from the word
"attrib".
---
src/attrib-server.c | 2 +-
src/device.c | 4 ++--
src/device.h | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/attrib-server.c b/src/attrib-server.c
index 6571577..e73850d 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -1300,7 +1300,7 @@ static void connect_event(GIOChannel *io, GError *gerr, void *user_data)
if (!device)
return;

- device_attach_attrib(device, io);
+ device_attach_att_transport(device, io);
}

static gboolean register_core_services(struct gatt_server *server)
diff --git a/src/device.c b/src/device.c
index dcc03af..09df701 100644
--- a/src/device.c
+++ b/src/device.c
@@ -3643,7 +3643,7 @@ static void gatt_client_init(struct btd_device *device)
DBG("gatt-client created");
}

-bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
+bool device_attach_att_transport(struct btd_device *dev, GIOChannel *io)
{
GError *gerr = NULL;
GAttrib *attrib;
@@ -3728,7 +3728,7 @@ static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
goto done;
}

- if (!device_attach_attrib(device, io))
+ if (!device_attach_att_transport(device, io))
goto done;

if (attcb->success)
diff --git a/src/device.h b/src/device.h
index 2e0473e..10c4951 100644
--- a/src/device.h
+++ b/src/device.h
@@ -69,7 +69,7 @@ struct gatt_primary *btd_device_get_primary(struct btd_device *device,
GSList *btd_device_get_primaries(struct btd_device *device);
void btd_device_gatt_set_service_changed(struct btd_device *device,
uint16_t start, uint16_t end);
-bool device_attach_attrib(struct btd_device *dev, GIOChannel *io);
+bool device_attach_att_transport(struct btd_device *dev, GIOChannel *io);
void btd_device_add_uuid(struct btd_device *device, const char *uuid);
void device_add_eir_uuids(struct btd_device *dev, GSList *uuids);
void device_probe_profile(gpointer a, gpointer b);
--
2.2.0.rc0.207.ga3a616c


2014-12-09 00:40:55

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 15/15] 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 7b28437..60d729b 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-09 00:40:54

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 14/15] 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 | 225 +++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 166 insertions(+), 59 deletions(-)

diff --git a/profiles/gap/gas.c b/profiles/gap/gas.c
index a4028dd..7b28437 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>
@@ -36,30 +37,34 @@
#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"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
+#include "src/gatt-callbacks.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;
+ unsigned int gatt_cb_id;
};

static GSList *devices;

static void gas_free(struct gas *gas)
{
- if (gas->attioid)
- btd_device_remove_attio_callback(gas->device, gas->attioid);
+ if (gas->gatt_cb_id)
+ btd_device_remove_gatt_callbacks(gas->device,
+ gas->gatt_cb_id);

- 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,83 +76,193 @@ 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);

- if (status != 0) {
- error("Read characteristics by UUID failed: %s",
- att_ecode2str(status));
+ DBG("GAP Device Name: %s", name);
+
+ btd_device_device_set_name(gas->device, name);
+}
+
+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)
+{
+ bt_uuid_t lhs;
+
+ bt_uuid16_create(&lhs, u16);
+
+ return bt_uuid_cmp(&lhs, uuid) == 0;
+}
+
+static void handle_characteristic(struct gatt_db_attribute *attr,
+ void *user_data)
{
struct gas *gas = user_data;
- uint16_t app;
+ uint16_t value_handle;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
+ &uuid)) {
+ error("Failed to obtain characteristic data");
+ return;
+ }

- gas->attrib = g_attrib_ref(attrib);
+ 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];

- if (device_get_appearance(gas->device, &app) < 0) {
- bt_uuid_t uuid;
+ /* TODO: Support peripheral privacy feature */

- bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+ DBG("Unsupported characteristic: %s", uuid_str);
+ }
+}
+
+static void handle_service(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gas *gas = user_data;

- gatt_read_char_by_uuid(gas->attrib, gas->gap.start,
- gas->gap.end, &uuid,
- gap_appearance_cb, gas);
+ if (gas->start_handle) {
+ error("More than one GAP service exists on device");
+ return;
}

- /* TODO: Read other GAP characteristics - See Core spec page 1739 */
+ gatt_db_attribute_get_service_handles(attr, &gas->start_handle,
+ &gas->end_handle);
+ gatt_db_service_foreach_char(attr, handle_characteristic, gas);
}

-static void attio_disconnected_cb(gpointer user_data)
+static void handle_gap_service(struct gas *gas)
+{
+ bt_uuid_t uuid;
+
+ bt_string_to_uuid(&uuid, GAP_UUID);
+
+ gatt_db_foreach_service(gas->db, &uuid, handle_service, gas);
+}
+
+static void gatt_client_ready_cb(struct bt_gatt_client *client,
+ struct gatt_db *db, void *user_data)
{
struct gas *gas = user_data;

- g_attrib_unref(gas->attrib);
- gas->attrib = NULL;
+ gas->client = bt_gatt_client_ref(client);
+ gas->db = gatt_db_ref(db);
+
+ handle_gap_service(gas);
}

-static int gas_register(struct btd_device *device, struct att_range *gap)
+static void gatt_client_disconn_cb(void *user_data)
+{
+ struct gas *gas = user_data;
+
+ gatt_db_unref(gas->db);
+ bt_gatt_client_unref(gas->client);
+
+ gas->db = NULL;
+ gas->client = NULL;
+}
+
+static int gas_register(struct btd_device *device)
{
struct gas *gas;
+ GSList *l;
+
+ /*
+ * 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)
+ return 0;

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);
+ /*
+ * Simply register the callbacks. We will perform initialization during
+ * the ready callback once the gatt-client has been initialized for this
+ * device.
+ *
+ * Here we don't register the service removed handler since the
+ * GAP service is mandatory and should not get removed while the device
+ * is connected.
+ */
+ gas->gatt_cb_id = btd_device_add_gatt_callbacks(device,
+ gatt_client_ready_cb,
+ NULL,
+ gatt_client_disconn_cb,
+ gas);

return 0;
}
@@ -169,16 +284,8 @@ static void gas_unregister(struct btd_device *device)
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);
+ return gas_register(device);
}

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


2014-12-09 00:40:50

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 10/15] core: Use gatt_db service callbacks

This patch integrates the gatt_db service callbacks into btd_device.
Based on the event, the device objects UUIDs list is updated and
profiles are probed for new services.

There is currently no mechanism to tell a profile that a service has
been removed, however profiles can use the gatt_db callbacks to make
necessary updates in the future.
---
src/device.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 117 insertions(+), 26 deletions(-)

diff --git a/src/device.c b/src/device.c
index 09df701..0bfac1e 100644
--- a/src/device.c
+++ b/src/device.c
@@ -2365,6 +2365,119 @@ static void load_att_info(struct btd_device *device, const char *local,
free(prim_uuid);
}

+static void device_register_primaries(struct btd_device *device,
+ GSList *prim_list, int psm)
+{
+ device->primaries = g_slist_concat(device->primaries, prim_list);
+}
+
+static void add_primary(struct gatt_db_attribute *attr, void *user_data)
+{
+ GSList **new_services = user_data;
+ struct gatt_primary *prim;
+ bt_uuid_t uuid;
+
+ prim = g_new0(struct gatt_primary, 1);
+ if (!prim) {
+ DBG("Failed to allocate gatt_primary structure");
+ return;
+ }
+
+ gatt_db_attribute_get_service_handles(attr, &prim->range.start,
+ &prim->range.end);
+ gatt_db_attribute_get_service_uuid(attr, &uuid);
+ bt_uuid_to_string(&uuid, prim->uuid, sizeof(prim->uuid));
+
+ *new_services = g_slist_append(*new_services, prim);
+}
+
+static void store_services(struct btd_device *device);
+
+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;
+
+ if (!bt_gatt_client_is_ready(device->gatt_client))
+ return;
+
+ gatt_db_attribute_get_service_handles(attr, &start, &end);
+
+ DBG("GATT service added - start: 0x%04x, end: 0x%04x", start, end);
+
+ /*
+ * TODO: Remove the primaries list entirely once all profiles use
+ * shared/gatt.
+ */
+ add_primary(attr, &new_service);
+ if (!new_service)
+ return;
+
+ device_register_primaries(device, new_service, -1);
+
+ prim = new_service->data;
+ profiles_added = g_slist_append(profiles_added, g_strdup(prim->uuid));
+
+ device_probe_profiles(device, profiles_added);
+
+ store_services(device);
+
+ g_slist_free_full(profiles_added, g_free);
+}
+
+static gint prim_attr_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_primary *prim = a;
+ const struct gatt_db_attribute *attr = b;
+ uint16_t start, end;
+
+ gatt_db_attribute_get_service_handles(attr, &start, &end);
+
+ return !(prim->range.start == start && prim->range.end == end);
+}
+
+static void gatt_service_removed(struct gatt_db_attribute *attr,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+ GSList *l;
+ struct gatt_primary *prim;
+ uint16_t start, end;
+
+ if (!bt_gatt_client_is_ready(device->gatt_client))
+ return;
+
+ gatt_db_attribute_get_service_handles(attr, &start, &end);
+
+ DBG("GATT service removed - start: 0x%04x, end: 0x%04x", start, end);
+
+ /* Remove the corresponding gatt_primary */
+ l = g_slist_find_custom(device->primaries, attr, prim_attr_cmp);
+ if (!l)
+ return;
+
+ 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);
+
+ /*
+ * TODO: Notify the profiles somehow. It may be sufficient for each
+ * profile to register a service_removed handler.
+ */
+
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "UUIDs");
+}
+
static struct btd_device *device_new(struct btd_adapter *adapter,
const char *address)
{
@@ -2405,6 +2518,10 @@ static struct btd_device *device_new(struct btd_adapter *adapter,
device->adapter = adapter;
device->temporary = TRUE;

+ gatt_db_register(device->client_db, gatt_service_added,
+ gatt_service_removed,
+ device, NULL);
+
return btd_device_ref(device);
}

@@ -3264,12 +3381,6 @@ static GSList *device_services_from_record(struct btd_device *device,
return prim_list;
}

-static void device_register_primaries(struct btd_device *device,
- GSList *prim_list, int psm)
-{
- device->primaries = g_slist_concat(device->primaries, prim_list);
-}
-
static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
{
struct browse_req *req = user_data;
@@ -3515,26 +3626,6 @@ static void send_le_browse_response(struct browse_req *req)
g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID);
}

-static void add_primary(struct gatt_db_attribute *attr, void *user_data)
-{
- struct gatt_primary *prim;
- GSList **services = user_data;
- bt_uuid_t uuid;
-
- prim = g_new0(struct gatt_primary, 1);
- if (!prim) {
- DBG("Failed to allocate gatt_primary structure");
- return;
- }
-
- gatt_db_attribute_get_service_handles(attr, &prim->range.start,
- &prim->range.end);
- gatt_db_attribute_get_service_uuid(attr, &uuid);
- bt_uuid_to_string(&uuid, prim->uuid, sizeof(prim->uuid));
-
- *services = g_slist_append(*services, prim);
-}
-
static void register_gatt_services(struct browse_req *req)
{
struct btd_device *device = req->device;
--
2.2.0.rc0.207.ga3a616c


2014-12-09 00:40:47

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 07/15] core: device: Use bt_att_register_disconnect.

btd_device is now notified of ATT channel disconnections by registering
a disconnect handler with bt_att instead of setting up an io watch with
the GIOChannel.
---
src/device.c | 34 +++++++++++++++++++++-------------
1 file changed, 21 insertions(+), 13 deletions(-)

diff --git a/src/device.c b/src/device.c
index e9630ed..b31e98d 100644
--- a/src/device.c
+++ b/src/device.c
@@ -46,6 +46,8 @@
#include "log.h"

#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-client.h"
#include "btio/btio.h"
#include "lib/uuid.h"
#include "lib/mgmt.h"
@@ -203,6 +205,9 @@ struct btd_device {
GSList *attios_offline;
guint attachid; /* Attrib server attach */

+ struct bt_att *att; /* The new ATT transport */
+ unsigned int att_disconn_id;
+
struct bearer_state bredr_state;
struct bearer_state le_state;

@@ -221,7 +226,6 @@ struct btd_device {
int8_t rssi;

GIOChannel *att_io;
- guint cleanup_id;
guint store_id;
};

@@ -466,10 +470,9 @@ static void attio_cleanup(struct btd_device *device)
device->attachid = 0;
}

- if (device->cleanup_id) {
- g_source_remove(device->cleanup_id);
- device->cleanup_id = 0;
- }
+ if (device->att_disconn_id)
+ bt_att_unregister_disconnect(device->att,
+ device->att_disconn_id);

if (device->att_io) {
g_io_channel_shutdown(device->att_io, FALSE, NULL);
@@ -477,6 +480,11 @@ static void attio_cleanup(struct btd_device *device)
device->att_io = NULL;
}

+ if (device->att) {
+ bt_att_unref(device->att);
+ device->att = NULL;
+ }
+
if (device->attrib) {
GAttrib *attrib = device->attrib;
device->attrib = NULL;
@@ -3401,8 +3409,7 @@ static void attio_disconnected(gpointer data, gpointer user_data)
attio->dcfunc(attio->user_data);
}

-static gboolean attrib_disconnected_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
+static void att_disconnected_cb(void *user_data)
{
struct btd_device *device = user_data;
int sock, err = 0;
@@ -3413,7 +3420,7 @@ static gboolean attrib_disconnected_cb(GIOChannel *io, GIOCondition cond,
if (device->browse)
goto done;

- sock = g_io_channel_unix_get_fd(io);
+ sock = bt_att_get_fd(device->att);
len = sizeof(err);
getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len);

@@ -3436,8 +3443,6 @@ static gboolean attrib_disconnected_cb(GIOChannel *io, GIOCondition cond,

done:
attio_cleanup(device);
-
- return FALSE;
}

static void register_all_services(struct browse_req *req, GSList *services)
@@ -3656,7 +3661,7 @@ bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
}

if (cid == ATT_CID)
- mtu = ATT_DEFAULT_LE_MTU;
+ mtu = BT_ATT_DEFAULT_LE_MTU;

attrib = g_attrib_new(io, mtu);
if (!attrib) {
@@ -3672,8 +3677,11 @@ bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
}

dev->attrib = attrib;
- dev->cleanup_id = g_io_add_watch(io, G_IO_HUP,
- attrib_disconnected_cb, dev);
+ dev->att = bt_att_ref(g_attrib_get_att(attrib));
+
+ dev->att_disconn_id = bt_att_register_disconnect(dev->att,
+ att_disconnected_cb, dev, NULL);
+ bt_att_set_close_on_unref(dev->att, true);

/*
* Remove the device from the connect_list and give the passive
--
2.2.0.rc0.207.ga3a616c


2014-12-09 00:40:46

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 06/15] shared/gatt-db: Add UUID arg to foreach_service

This patch adds the "uuid" argument to gatt_db_foreach_service, which
invokes the callback for a service only if "uuid" is NULL or if it
matches the GATT service UUID.
---
src/shared/gatt-db.c | 19 ++++++++++++++++---
src/shared/gatt-db.h | 6 ++++--
tools/btgatt-client.c | 9 ++++-----
tools/btgatt-server.c | 2 +-
unit/test-gatt.c | 4 ++--
5 files changed, 27 insertions(+), 13 deletions(-)

diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 379b4ad..98fb8a0 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -996,14 +996,17 @@ void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
queue_foreach(db->services, find_information, &data);
}

-void gatt_db_foreach_service(struct gatt_db *db, gatt_db_attribute_cb_t func,
- void *user_data)
+void gatt_db_foreach_service(struct gatt_db *db, const bt_uuid_t *uuid,
+ gatt_db_attribute_cb_t func,
+ void *user_data)
{
- gatt_db_foreach_service_in_range(db, func, user_data, 0x0001, 0xffff);
+ gatt_db_foreach_service_in_range(db, uuid, func, user_data, 0x0001,
+ 0xffff);
}

struct foreach_data {
gatt_db_attribute_cb_t func;
+ const bt_uuid_t *uuid;
void *user_data;
uint16_t start, end;
};
@@ -1013,16 +1016,25 @@ static void foreach_service_in_range(void *data, void *user_data)
struct gatt_db_service *service = data;
struct foreach_data *foreach_data = user_data;
uint16_t svc_start;
+ bt_uuid_t uuid;

svc_start = get_handle_at_index(service, 0);

if (svc_start > foreach_data->end || svc_start < foreach_data->start)
return;

+ if (foreach_data->uuid) {
+ gatt_db_attribute_get_service_uuid(service->attributes[0],
+ &uuid);
+ if (bt_uuid_cmp(&uuid, foreach_data->uuid))
+ return;
+ }
+
foreach_data->func(service->attributes[0], foreach_data->user_data);
}

void gatt_db_foreach_service_in_range(struct gatt_db *db,
+ const bt_uuid_t *uuid,
gatt_db_attribute_cb_t func,
void *user_data,
uint16_t start_handle,
@@ -1034,6 +1046,7 @@ void gatt_db_foreach_service_in_range(struct gatt_db *db,
return;

data.func = func;
+ data.uuid = uuid;
data.user_data = user_data;
data.start = start_handle;
data.end = end_handle;
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index b9f12e3..e5fe6bb 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -105,9 +105,11 @@ void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
typedef void (*gatt_db_attribute_cb_t)(struct gatt_db_attribute *attrib,
void *user_data);

-void gatt_db_foreach_service(struct gatt_db *db, gatt_db_attribute_cb_t func,
- void *user_data);
+void gatt_db_foreach_service(struct gatt_db *db, const bt_uuid_t *uuid,
+ gatt_db_attribute_cb_t func,
+ void *user_data);
void gatt_db_foreach_service_in_range(struct gatt_db *db,
+ const bt_uuid_t *uuid,
gatt_db_attribute_cb_t func,
void *user_data,
uint16_t start_handle,
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index fe94ae8..015142d 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -288,15 +288,14 @@ static void print_services(struct client *cli)
{
printf("\n");

- gatt_db_foreach_service(cli->db, print_service, cli);
+ gatt_db_foreach_service(cli->db, NULL, print_service, cli);
}

static void print_services_by_uuid(struct client *cli, const bt_uuid_t *uuid)
{
printf("\n");

- /* TODO: Filter by UUID */
- gatt_db_foreach_service(cli->db, print_service, cli);
+ gatt_db_foreach_service(cli->db, uuid, print_service, cli);
}

static void print_services_by_handle(struct client *cli, uint16_t handle)
@@ -304,7 +303,7 @@ static void print_services_by_handle(struct client *cli, uint16_t handle)
printf("\n");

/* TODO: Filter by handle */
- gatt_db_foreach_service(cli->db, print_service, cli);
+ gatt_db_foreach_service(cli->db, NULL, print_service, cli);
}

static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
@@ -331,7 +330,7 @@ static void service_changed_cb(uint16_t start_handle, uint16_t end_handle,
printf("\nService Changed handled - start: 0x%04x end: 0x%04x\n",
start_handle, end_handle);

- gatt_db_foreach_service_in_range(cli->db, print_service, cli,
+ gatt_db_foreach_service_in_range(cli->db, NULL, print_service, cli,
start_handle, end_handle);
print_prompt();
}
diff --git a/tools/btgatt-server.c b/tools/btgatt-server.c
index a2a0ec9..1a9b9fb 100644
--- a/tools/btgatt-server.c
+++ b/tools/btgatt-server.c
@@ -969,7 +969,7 @@ static void print_service(struct gatt_db_attribute *attr, void *user_data)

static void cmd_services(struct server *server, char *cmd_str)
{
- gatt_db_foreach_service(server->db, print_service, server);
+ gatt_db_foreach_service(server->db, NULL, print_service, server);
}

static void cmd_help(struct server *server, char *cmd_str);
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 3ce3d80..2f3f26a 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -414,7 +414,7 @@ static void match_services(struct gatt_db_attribute *client_serv_attr,
serv_test_data.match = client_serv_attr;
serv_test_data.found = false;

- gatt_db_foreach_service(source_db,
+ gatt_db_foreach_service(source_db, NULL,
find_matching_service, &serv_test_data);

g_assert(serv_test_data.found);
@@ -434,7 +434,7 @@ static void client_ready_cb(bool success, uint8_t att_ecode, void *user_data)
g_assert(context->client);
g_assert(context->client_db);

- gatt_db_foreach_service(context->client_db, match_services,
+ gatt_db_foreach_service(context->client_db, NULL, match_services,
context->data->source_db);

if (context->data->step) {
--
2.2.0.rc0.207.ga3a616c


2014-12-09 00:40:44

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 04/15] shared/att: cancel_all before calling disconnect cb

Moved the call to bt_att_cancel_all to before the call to the registered
disconnect callbacks in bt_att's internal disconnect handler to make
sure that all affected user_data is destroyed. This is to prevent cases
of invalid access, where a user_data destroy function refers to data
that the upper layer might free in the disconnect callback.
---
src/shared/att.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/shared/att.c b/src/shared/att.c
index 9511bb2..fc8c598 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -554,6 +554,8 @@ static bool disconnect_cb(struct io *io, void *user_data)
util_debug(att->debug_callback, att->debug_data,
"Physical link disconnected");

+ bt_att_cancel_all(att);
+
bt_att_ref(att);
att->in_disconn = true;
queue_foreach(att->disconn_list, disconn_handler, NULL);
@@ -565,9 +567,7 @@ static bool disconnect_cb(struct io *io, void *user_data)
att->need_disconn_cleanup = false;
}

- bt_att_cancel_all(att);
bt_att_unregister_all(att);
-
bt_att_unref(att);

return false;
--
2.2.0.rc0.207.ga3a616c


2014-12-09 00:40:45

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 05/15] shared/gatt-db: Make accessors work on const ptr

Made gatt_db_attribute accessor functions accept a const pointer.
---
src/shared/gatt-db.c | 22 ++++++++++++----------
src/shared/gatt-db.h | 22 ++++++++++++----------
2 files changed, 24 insertions(+), 20 deletions(-)

diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 37ec946..379b4ad 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -1151,7 +1151,8 @@ struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db,
return service->attributes[handle - service_handle];
}

-const bt_uuid_t *gatt_db_attribute_get_type(struct gatt_db_attribute *attrib)
+const bt_uuid_t *gatt_db_attribute_get_type(
+ const struct gatt_db_attribute *attrib)
{
if (!attrib)
return NULL;
@@ -1159,7 +1160,7 @@ const bt_uuid_t *gatt_db_attribute_get_type(struct gatt_db_attribute *attrib)
return &attrib->uuid;
}

-uint16_t gatt_db_attribute_get_handle(struct gatt_db_attribute *attrib)
+uint16_t gatt_db_attribute_get_handle(const struct gatt_db_attribute *attrib)
{
if (!attrib)
return 0;
@@ -1167,7 +1168,7 @@ uint16_t gatt_db_attribute_get_handle(struct gatt_db_attribute *attrib)
return attrib->handle;
}

-bool gatt_db_attribute_get_service_uuid(struct gatt_db_attribute *attrib,
+bool gatt_db_attribute_get_service_uuid(const struct gatt_db_attribute *attrib,
bt_uuid_t *uuid)
{
struct gatt_db_service *service;
@@ -1198,9 +1199,10 @@ bool gatt_db_attribute_get_service_uuid(struct gatt_db_attribute *attrib,
return false;
}

-bool gatt_db_attribute_get_service_handles(struct gatt_db_attribute *attrib,
- uint16_t *start_handle,
- uint16_t *end_handle)
+bool gatt_db_attribute_get_service_handles(
+ const struct gatt_db_attribute *attrib,
+ uint16_t *start_handle,
+ uint16_t *end_handle)
{
struct gatt_db_service *service;

@@ -1214,7 +1216,7 @@ bool gatt_db_attribute_get_service_handles(struct gatt_db_attribute *attrib,
return true;
}

-bool gatt_db_attribute_get_service_data(struct gatt_db_attribute *attrib,
+bool gatt_db_attribute_get_service_data(const struct gatt_db_attribute *attrib,
uint16_t *start_handle,
uint16_t *end_handle,
bool *primary,
@@ -1244,7 +1246,7 @@ bool gatt_db_attribute_get_service_data(struct gatt_db_attribute *attrib,
return le_to_uuid(decl->value, decl->value_len, uuid);
}

-bool gatt_db_attribute_get_char_data(struct gatt_db_attribute *attrib,
+bool gatt_db_attribute_get_char_data(const struct gatt_db_attribute *attrib,
uint16_t *handle,
uint16_t *value_handle,
uint8_t *properties,
@@ -1281,7 +1283,7 @@ bool gatt_db_attribute_get_char_data(struct gatt_db_attribute *attrib,
return le_to_uuid(attrib->value + 3, attrib->value_len - 3, uuid);
}

-bool gatt_db_attribute_get_incl_data(struct gatt_db_attribute *attrib,
+bool gatt_db_attribute_get_incl_data(const struct gatt_db_attribute *attrib,
uint16_t *handle,
uint16_t *start_handle,
uint16_t *end_handle)
@@ -1317,7 +1319,7 @@ bool gatt_db_attribute_get_incl_data(struct gatt_db_attribute *attrib,
return true;
}

-bool gatt_db_attribute_get_permissions(struct gatt_db_attribute *attrib,
+bool gatt_db_attribute_get_permissions(const struct gatt_db_attribute *attrib,
uint32_t *permissions)
{
if (!attrib || !permissions)
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 987ccf4..b9f12e3 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -139,35 +139,37 @@ bool gatt_db_unregister(struct gatt_db *db, unsigned int id);
struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db,
uint16_t handle);

-const bt_uuid_t *gatt_db_attribute_get_type(struct gatt_db_attribute *attrib);
+const bt_uuid_t *gatt_db_attribute_get_type(
+ const struct gatt_db_attribute *attrib);

-uint16_t gatt_db_attribute_get_handle(struct gatt_db_attribute *attrib);
+uint16_t gatt_db_attribute_get_handle(const struct gatt_db_attribute *attrib);

-bool gatt_db_attribute_get_service_uuid(struct gatt_db_attribute *attrib,
+bool gatt_db_attribute_get_service_uuid(const struct gatt_db_attribute *attrib,
bt_uuid_t *uuid);

-bool gatt_db_attribute_get_service_handles(struct gatt_db_attribute *attrib,
- uint16_t *start_handle,
- uint16_t *end_handle);
+bool gatt_db_attribute_get_service_handles(
+ const struct gatt_db_attribute *attrib,
+ uint16_t *start_handle,
+ uint16_t *end_handle);

-bool gatt_db_attribute_get_service_data(struct gatt_db_attribute *attrib,
+bool gatt_db_attribute_get_service_data(const struct gatt_db_attribute *attrib,
uint16_t *start_handle,
uint16_t *end_handle,
bool *primary,
bt_uuid_t *uuid);

-bool gatt_db_attribute_get_char_data(struct gatt_db_attribute *attrib,
+bool gatt_db_attribute_get_char_data(const struct gatt_db_attribute *attrib,
uint16_t *handle,
uint16_t *value_handle,
uint8_t *properties,
bt_uuid_t *uuid);

-bool gatt_db_attribute_get_incl_data(struct gatt_db_attribute *attrib,
+bool gatt_db_attribute_get_incl_data(const struct gatt_db_attribute *attrib,
uint16_t *handle,
uint16_t *start_handle,
uint16_t *end_handle);

-bool gatt_db_attribute_get_permissions(struct gatt_db_attribute *attrib,
+bool gatt_db_attribute_get_permissions(const struct gatt_db_attribute *attrib,
uint32_t *permissions);

typedef void (*gatt_db_attribute_read_t) (struct gatt_db_attribute *attrib,
--
2.2.0.rc0.207.ga3a616c


2014-12-09 00:40:43

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 03/15] attrib: Check if attrib is NULL in functions

This patch adds an early return to attrib/gattrib functions if the
attrib argument is NULL.
---
attrib/gattrib.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index f3b1366..b76d0a4 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -266,6 +266,9 @@ guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len,
bt_att_response_func_t response_cb = NULL;
bt_att_destroy_func_t destroy_cb = NULL;

+ if (!attrib)
+ return 0;
+
if (!pdu || !len)
return 0;

@@ -288,11 +291,17 @@ guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len,

gboolean g_attrib_cancel(GAttrib *attrib, guint id)
{
+ if (!attrib)
+ return FALSE;
+
return bt_att_cancel(attrib->att, id);
}

gboolean g_attrib_cancel_all(GAttrib *attrib)
{
+ if (!attrib)
+ return FALSE;
+
return bt_att_cancel_all(attrib->att);
}

@@ -302,6 +311,9 @@ guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
{
struct attrib_callbacks *cb = NULL;

+ if (!attrib)
+ return 0;
+
if (func || notify) {
cb = new0(struct attrib_callbacks, 1);
if (!cb)
@@ -323,7 +335,7 @@ guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,

uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len)
{
- if (!len)
+ if (!attrib || !len)
return NULL;

*len = attrib->buflen;
@@ -332,6 +344,9 @@ uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len)

gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu)
{
+ if (!attrib)
+ return FALSE;
+
/*
* Clients of this expect a buffer to use.
*
@@ -349,10 +364,16 @@ gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu)

gboolean g_attrib_unregister(GAttrib *attrib, guint id)
{
+ if (!attrib)
+ return FALSE;
+
return bt_att_unregister(attrib->att, id);
}

gboolean g_attrib_unregister_all(GAttrib *attrib)
{
+ if (!attrib)
+ return false;
+
return bt_att_unregister_all(attrib->att);
}
--
2.2.0.rc0.207.ga3a616c


2014-12-09 00:40:42

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 02/15] shared/att: Add bt_att_get_fd.

This patch adds a getter for a bt_att structure's underlying connection
file descriptor.
---
src/shared/att.c | 8 ++++++++
src/shared/att.h | 1 +
2 files changed, 9 insertions(+)

diff --git a/src/shared/att.c b/src/shared/att.c
index ee425d8..9511bb2 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -922,6 +922,14 @@ void bt_att_unref(struct bt_att *att)
free(att);
}

+int bt_att_get_fd(struct bt_att *att)
+{
+ if (!att)
+ return -EINVAL;
+
+ return att->fd;
+}
+
bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close)
{
if (!att || !att->io)
diff --git a/src/shared/att.h b/src/shared/att.h
index 99b5a5b..b946b18 100644
--- a/src/shared/att.h
+++ b/src/shared/att.h
@@ -33,6 +33,7 @@ struct bt_att *bt_att_new(int fd);
struct bt_att *bt_att_ref(struct bt_att *att);
void bt_att_unref(struct bt_att *att);

+int bt_att_get_fd(struct bt_att *att);
bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close);

typedef void (*bt_att_response_func_t)(uint8_t opcode, const void *pdu,
--
2.2.0.rc0.207.ga3a616c


2014-12-09 00:40:41

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ 01/15] attrib/gattrib: Add g_attrib_get_att.

Added the g_attrib_get_att function which returns the underlying bt_att
structure associated with a GAttrib.
---
attrib/gattrib.c | 8 ++++++++
attrib/gattrib.h | 3 +++
2 files changed, 11 insertions(+)

diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index cfa3b78..f3b1366 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -169,6 +169,14 @@ GIOChannel *g_attrib_get_channel(GAttrib *attrib)
return attrib->io;
}

+struct bt_att *g_attrib_get_att(GAttrib *attrib)
+{
+ if (!attrib)
+ return NULL;
+
+ return attrib->att;
+}
+
gboolean g_attrib_set_destroy_function(GAttrib *attrib, GDestroyNotify destroy,
gpointer user_data)
{
diff --git a/attrib/gattrib.h b/attrib/gattrib.h
index 2ed57c1..374bac2 100644
--- a/attrib/gattrib.h
+++ b/attrib/gattrib.h
@@ -31,6 +31,7 @@ extern "C" {
#define GATTRIB_ALL_REQS 0xFE
#define GATTRIB_ALL_HANDLES 0x0000

+struct bt_att; /* Forward declaration for compatibility */
struct _GAttrib;
typedef struct _GAttrib GAttrib;

@@ -47,6 +48,8 @@ void g_attrib_unref(GAttrib *attrib);

GIOChannel *g_attrib_get_channel(GAttrib *attrib);

+struct bt_att *g_attrib_get_att(GAttrib *attrib);
+
gboolean g_attrib_set_destroy_function(GAttrib *attrib,
GDestroyNotify destroy, gpointer user_data);

--
2.2.0.rc0.207.ga3a616c