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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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