Implement primary service search when creating a device
Discover primary services implemented inside the device entity to allow
proper integration of attribute plugin. Implements a single entry point
to the attribute plugin no matter the transport(BR/EDR or LE), the device
probe callback is called for both types.
Add a new function to discover all primary services without additional
calls to fetch the remaining primary services, sub-procedure iterations
is handled inside this function.
The next action are: clean the attribute client removing implicity service
and characteristics discovery, issue the Discover Primary Service based on
the remote properties and fetch the characteristic on demand.
diff --git a/attrib/manager.c b/attrib/manager.c
index 9bd1774..9b06c8c 100644
--- a/attrib/manager.c
+++ b/attrib/manager.c
@@ -45,26 +45,17 @@
{
const sdp_record_t *rec;
sdp_list_t *list;
- int psm;
-
- /*
- * Entry point for BR/EDR GATT probe. LE scanning and primary service
- * search will be handled temporaly inside the gatt plugin. For the
- * final solution all LE operations should be moved to the "core",
- * otherwise it will not be possible serialize/schedule BR/EDR device
- * discovery and LE scanning.
- */
+ int psm = -1;
rec = btd_device_get_record(device, GATT_UUID);
- if (!rec)
- return -1;
+ if (rec) {
+ if (sdp_get_access_protos(rec, &list) < 0)
+ return -1;
- if (sdp_get_access_protos(rec, &list) < 0)
- return -1;
-
- psm = sdp_get_proto_port(list, L2CAP_UUID);
- if (psm < 0)
- return -1;
+ psm = sdp_get_proto_port(list, L2CAP_UUID);
+ if (psm < 0)
+ return -1;
+ }
return attrib_client_register(device, psm);
}
diff --git a/src/device.c b/src/device.c
index 65acc08..3fd5d5f 100644
--- a/src/device.c
+++ b/src/device.c
@@ -107,6 +107,7 @@
struct btd_device {
bdaddr_t bdaddr;
+ gboolean le;
gchar *path;
char name[MAX_NAME_LENGTH + 1];
char *alias;
@@ -211,7 +212,8 @@
adapter_get_address(adapter, &src);
- bt_cancel_discovery(&src, &device->bdaddr);
+ if (device->le == FALSE)
+ bt_cancel_discovery(&src, &device->bdaddr);
device->browse = NULL;
browse_request_free(req);
@@ -1562,29 +1564,67 @@
l->data);
}
-int device_browse(struct btd_device *device, DBusConnection *conn,
- DBusMessage *msg, uuid_t *search, gboolean reverse)
+static void primary_cb(GSList *services, int err, gpointer user_data)
+{
+ struct browse_req *req = user_data;
+ struct btd_device *device = req->device;
+
+ if (err) {
+ error_failed_errno(req->conn, req->msg, -err);
+ goto done;
+ }
+
+ services_changed(req->device);
+ device_set_temporary(req->device, FALSE);
+ device_probe_drivers(req->device, services);
+
+ create_device_reply(req->device, req);
+
+done:
+ device->browse = NULL;
+ browse_request_free(req);
+}
+
+static struct browse_req *browse_primary(struct btd_device *device, int *err)
{
struct btd_adapter *adapter = device->adapter;
struct browse_req *req;
bdaddr_t src;
- uuid_t uuid;
- bt_callback_t cb;
- int err;
+ int ret;
- if (device->browse)
- return -EBUSY;
+ req = g_new0(struct browse_req, 1);
+ req->device = btd_device_ref(device);
+
+ adapter_get_address(adapter, &src);
+
+ ret = bt_discover_primary(&src, &device->bdaddr, -1, primary_cb, req,
+ NULL);
+
+ if (ret < 0) {
+ browse_request_free(req);
+ if (err)
+ *err = ret;
+
+ return NULL;
+ }
+
+ return req;
+}
+
+static struct browse_req *browse_sdp(struct btd_device *device, uuid_t *search,
+ gboolean reverse, int *err)
+{
+ struct btd_adapter *adapter = device->adapter;
+ struct browse_req *req;
+ bt_callback_t cb;
+ bdaddr_t src;
+ uuid_t uuid;
+ int ret;
adapter_get_address(adapter, &src);
req = g_new0(struct browse_req, 1);
-
- if (conn == NULL)
- conn = get_dbus_connection();
-
- req->conn = dbus_connection_ref(conn);
req->device = btd_device_ref(device);
-
if (search) {
memcpy(&uuid, search, sizeof(uuid_t));
cb = search_cb;
@@ -1594,6 +1634,39 @@
cb = browse_cb;
}
+ ret = bt_search_service(&src, &device->bdaddr, &uuid, cb, req, NULL);
+ if (ret < 0) {
+ browse_request_free(req);
+ if (err)
+ *err = ret;
+
+ return NULL;
+ }
+
+ return req;
+}
+
+int device_browse(struct btd_device *device, DBusConnection *conn,
+ DBusMessage *msg, uuid_t *search, gboolean reverse)
+{
+ struct browse_req *req;
+ int err = 0;
+
+ if (device->browse)
+ return -EBUSY;
+
+ if (device->le)
+ req = browse_primary(device, &err);
+ else
+ req = browse_sdp(device, search, reverse, &err);
+
+ if (req == NULL)
+ return err;
+
+ if (conn == NULL)
+ conn = get_dbus_connection();
+
+ req->conn = dbus_connection_ref(conn);
device->browse = req;
if (msg) {
@@ -1608,13 +1681,6 @@
req, NULL);
}
- err = bt_search_service(&src, &device->bdaddr,
- &uuid, cb, req, NULL);
- if (err < 0) {
- device->browse = NULL;
- browse_request_free(req);
- }
-
return err;
}
diff --git a/src/glib-helper.c b/src/glib-helper.c
index 26ac0df..927fb7c 100644
--- a/src/glib-helper.c
+++ b/src/glib-helper.c
@@ -37,12 +37,26 @@
#include <glib.h>
-#include "glib-helper.h"
+#include "btio.h"
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
#include "sdpd.h"
+#include "glib-helper.h"
/* Number of seconds to keep a sdp_session_t in the cache */
#define CACHE_TIMEOUT 2
+struct gattrib_context {
+ bdaddr_t src;
+ bdaddr_t dst;
+ GAttrib *attrib;
+ bt_primary_t cb;
+ bt_destroy_t destroy;
+ gpointer user_data;
+ GSList *uuids;
+};
+
struct cached_sdp_session {
bdaddr_t src;
bdaddr_t dst;
@@ -52,6 +66,17 @@
static GSList *cached_sdp_sessions = NULL;
+static void gattrib_context_free(struct gattrib_context *ctxt)
+{
+ if (ctxt->destroy)
+ ctxt->destroy(ctxt->user_data);
+
+ g_slist_foreach(ctxt->uuids, (GFunc) g_free, NULL);
+ g_slist_free(ctxt->uuids);
+ g_attrib_unref(ctxt->attrib);
+ g_free(ctxt);
+}
+
static gboolean cached_session_expired(gpointer user_data)
{
struct cached_sdp_session *cached = user_data;
@@ -113,6 +138,7 @@
bdaddr_t dst;
sdp_session_t *session;
bt_callback_t cb;
+ bt_primary_t prim_cb;
bt_destroy_t destroy;
gpointer user_data;
uuid_t uuid;
@@ -373,6 +399,125 @@
return 0;
}
+static void primary_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct gattrib_context *ctxt = user_data;
+ struct att_data_list *list;
+ unsigned int i, err;
+ uint16_t end;
+
+ if (status == ATT_ECODE_ATTR_NOT_FOUND) {
+ err = 0;
+ goto done;
+ }
+
+ if (status != 0) {
+ err = -EIO;
+ goto done;
+ }
+
+ list = dec_read_by_grp_resp(pdu, plen);
+ if (list == NULL) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ for (i = 0, end = 0; i < list->num; i++) {
+ const uint8_t *data = list->data[i];
+ char *prim;
+ uuid_t u128, u16;
+
+ end = att_get_u16(&data[2]);
+
+ if (list->len == 6) {
+ sdp_uuid16_create(&u16,
+ att_get_u16(&data[4]));
+ sdp_uuid16_to_uuid128(&u128, &u16);
+
+ } else if (list->len == 20)
+ sdp_uuid128_create(&u128, &data[4]);
+ else
+ /* Skipping invalid data */
+ continue;
+
+ prim = bt_uuid2string(&u128);
+ ctxt->uuids = g_slist_append(ctxt->uuids, prim);
+ }
+
+ att_data_list_free(list);
+ err = 0;
+
+ if (end != 0xffff) {
+ gatt_discover_primary(ctxt->attrib, end + 1, 0xffff, NULL,
+ primary_cb, ctxt);
+ return;
+ }
+
+done:
+ ctxt->cb(ctxt->uuids, err, ctxt->user_data);
+ gattrib_context_free(ctxt);
+}
+
+static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+ struct gattrib_context *ctxt = user_data;
+
+ if (gerr) {
+ ctxt->cb(NULL, -EIO, ctxt->user_data);
+ gattrib_context_free(ctxt);
+ return;
+ }
+
+ gatt_discover_primary(ctxt->attrib, 0x0001, 0xffff, NULL, primary_cb,
+ ctxt);
+}
+
+int bt_discover_primary(const bdaddr_t *src, const bdaddr_t *dst, int psm,
+ bt_primary_t cb, void *user_data,
+ bt_destroy_t destroy)
+{
+ struct gattrib_context *ctxt;
+ GIOChannel *io;
+ GError *gerr = NULL;
+
+ ctxt = g_try_new0(struct gattrib_context, 1);
+ if (ctxt == NULL)
+ return -ENOMEM;
+
+ bacpy(&ctxt->src, src);
+ bacpy(&ctxt->dst, dst);
+ ctxt->user_data = user_data;
+ ctxt->cb = cb;
+ ctxt->destroy = destroy;
+
+ if (psm < 0)
+ io = bt_io_connect(BT_IO_L2CAP, connect_cb, ctxt, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_DEST_BDADDR, dst,
+ BT_IO_OPT_CID, GATT_CID,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ else
+ io = bt_io_connect(BT_IO_L2CAP, connect_cb, ctxt, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_DEST_BDADDR, dst,
+ BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+
+ if (io == NULL) {
+ gattrib_context_free(ctxt);
+ return -EIO;
+ }
+
+ ctxt->attrib = g_attrib_new(io);
+
+ g_io_channel_unref(io);
+
+ return 0;
+}
+
char *bt_uuid2string(uuid_t *uuid)
{
gchar *str;
diff --git a/src/glib-helper.h b/src/glib-helper.h
index dfe4123..018ff92 100644
--- a/src/glib-helper.h
+++ b/src/glib-helper.h
@@ -22,6 +22,7 @@
*/
typedef void (*bt_callback_t) (sdp_list_t *recs, int err, gpointer user_data);
+typedef void (*bt_primary_t) (GSList *l, int err, gpointer user_data);
typedef void (*bt_destroy_t) (gpointer user_data);
int bt_discover_services(const bdaddr_t *src, const bdaddr_t *dst,
@@ -32,6 +33,10 @@
bt_destroy_t destroy);
int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst);
+int bt_discover_primary(const bdaddr_t *src, const bdaddr_t *dst, int psm,
+ bt_primary_t cb, void *user_data,
+ bt_destroy_t destroy);
+
gchar *bt_uuid2string(uuid_t *uuid);
uint16_t bt_name2class(const char *string);
char *bt_name2string(const char *string);