Move interactive code of Discover Primary to gatt.c
Initial patch to move the shared code related to Discover All Primary
Services and Discover Primary Services by UUID to gatt.c.
diff --git a/attrib/gatt.c b/attrib/gatt.c
index ae33211..7d09689 100644
--- a/attrib/gatt.c
+++ b/attrib/gatt.c
@@ -31,10 +31,24 @@
#include "gattrib.h"
#include "gatt.h"
-guint gatt_discover_primary(GAttrib *attrib, uint16_t start, uint16_t end,
- uuid_t *uuid, GAttribResultFunc func, gpointer user_data)
+struct discover_primary {
+ GAttrib *attrib;
+ uuid_t uuid;
+ GSList *primaries;
+ gatt_primary_t cb;
+ void *user_data;
+};
+
+static void discover_primary_free(struct discover_primary *dp)
{
- uint8_t pdu[ATT_DEFAULT_MTU];
+ g_slist_free(dp->primaries);
+ g_attrib_unref(dp->attrib);
+ g_free(dp);
+}
+
+static guint16 encode_discover_primary(uint16_t start, uint16_t end,
+ uuid_t *uuid, uint8_t *pdu, size_t len)
+{
uuid_t prim;
guint16 plen;
uint8_t op;
@@ -42,10 +56,9 @@
sdp_uuid16_create(&prim, GATT_PRIM_SVC_UUID);
if (uuid == NULL) {
-
/* Discover all primary services */
op = ATT_OP_READ_BY_GROUP_REQ;
- plen = enc_read_by_grp_req(start, end, &prim, pdu, sizeof(pdu));
+ plen = enc_read_by_grp_req(start, end, &prim, pdu, len);
} else {
const void *value;
int vlen;
@@ -62,13 +75,151 @@
}
plen = enc_find_by_type_req(start, end, &prim, value, vlen,
- pdu, sizeof(pdu));
+ pdu, len);
}
+ return plen;
+}
+
+static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu,
+ guint16 iplen, gpointer user_data)
+
+{
+ struct discover_primary *dp = user_data;
+ GSList *ranges, *last;
+ struct att_range *range;
+ uint8_t opdu[ATT_DEFAULT_MTU];
+ guint16 oplen;
+ int err = 0;
+
+ if (status) {
+ err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
+ goto done;
+ }
+
+ ranges = dec_find_by_type_resp(ipdu, iplen);
+ if (ranges == NULL)
+ goto done;
+
+ dp->primaries = g_slist_concat(dp->primaries, ranges);
+
+ last = g_slist_last(ranges);
+ g_slist_free(ranges);
+ range = last->data;
+
+ if (range->end == 0xffff)
+ goto done;
+
+ oplen = encode_discover_primary(range->end + 1, 0xffff, &dp->uuid,
+ opdu, sizeof(opdu));
+
+ if (oplen == 0)
+ goto done;
+
+ g_attrib_send(dp->attrib, 0, opdu[0], opdu, oplen, primary_by_uuid_cb,
+ dp, NULL);
+ return;
+
+done:
+ dp->cb(dp->primaries, err, dp->user_data);
+ discover_primary_free(dp);
+}
+
+static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
+ gpointer user_data)
+{
+ struct discover_primary *dp = user_data;
+ struct att_data_list *list;
+ unsigned int i, err;
+ uint16_t start, end;
+
+ if (status) {
+ err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
+ goto done;
+ }
+
+ list = dec_read_by_grp_resp(ipdu, iplen);
+ if (list == NULL) {
+ err = ATT_ECODE_IO;
+ goto done;
+ }
+
+ for (i = 0, end = 0; i < list->num; i++) {
+ const uint8_t *data = list->data[i];
+ struct att_primary *primary;
+ uuid_t u128, u16;
+
+ start = att_get_u16(&data[0]);
+ 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;
+
+ primary = g_try_new0(struct att_primary, 1);
+ if (!primary) {
+ err = ATT_ECODE_INSUFF_RESOURCES;
+ goto done;
+ }
+ primary->start = start;
+ primary->end = end;
+ sdp_uuid2strn(&u128, primary->uuid, sizeof(primary->uuid));
+ dp->primaries = g_slist_append(dp->primaries, primary);
+ }
+
+ att_data_list_free(list);
+ err = 0;
+
+ if (end != 0xffff) {
+ uint8_t opdu[ATT_DEFAULT_MTU];
+ guint16 oplen = encode_discover_primary(end + 1, 0xffff, NULL,
+ opdu, sizeof(opdu));
+
+ g_attrib_send(dp->attrib, 0, opdu[0], opdu, oplen,
+ primary_all_cb, dp, NULL);
+
+ return;
+ }
+
+done:
+ dp->cb(dp->primaries, err, dp->user_data);
+ discover_primary_free(dp);
+}
+
+guint gatt_discover_primary(GAttrib *attrib, uuid_t *uuid, gatt_primary_t func,
+ gpointer user_data)
+{
+ struct discover_primary *dp;
+ uint8_t pdu[ATT_DEFAULT_MTU];
+ GAttribResultFunc cb;
+ guint16 plen;
+
+ plen = encode_discover_primary(0x0001, 0xffff, uuid, pdu, sizeof(pdu));
if (plen == 0)
return 0;
- return g_attrib_send(attrib, 0, op, pdu, plen, func, user_data, NULL);
+ dp = g_try_new0(struct discover_primary, 1);
+ if (dp == NULL)
+ return 0;
+
+ dp->attrib = g_attrib_ref(attrib);
+ dp->cb = func;
+ dp->user_data = user_data;
+
+ if (uuid) {
+ memcpy(&dp->uuid, uuid, sizeof(uuid_t));
+ cb = primary_by_uuid_cb;
+ } else
+ cb = primary_all_cb;
+
+ return g_attrib_send(attrib, 0, pdu[0], pdu, plen, cb, dp, NULL);
}
guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
diff --git a/attrib/gatt.h b/attrib/gatt.h
index 1e1e628..936c592 100644
--- a/attrib/gatt.h
+++ b/attrib/gatt.h
@@ -24,8 +24,10 @@
#define GATT_CID 4
-guint gatt_discover_primary(GAttrib *attrib, uint16_t start, uint16_t end,
- uuid_t *uuid, GAttribResultFunc func, gpointer user_data);
+typedef void (*gatt_primary_t) (GSList *l, guint8 status, gpointer user_data);
+
+guint gatt_discover_primary(GAttrib *attrib, uuid_t *uuid, gatt_primary_t func,
+ gpointer user_data);
guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
GAttribResultFunc func, gpointer user_data);
diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index a6f92db..ad0216b 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -145,76 +145,30 @@
return chan;
}
-static void primary_all_cb(guint8 status, const guint8 *pdu, guint16 plen,
- gpointer user_data)
+static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
{
- GAttrib *attrib = user_data;
- struct att_data_list *list;
- unsigned int i;
- uint16_t end;
+ GSList *l;
- if (status == ATT_ECODE_ATTR_NOT_FOUND)
- goto done;
-
- if (status != 0) {
+ if (status) {
g_printerr("Discover all primary services failed: %s\n",
att_ecode2str(status));
goto done;
}
- list = dec_read_by_grp_resp(pdu, plen);
- if (list == NULL)
- goto done;
-
- for (i = 0, end = 0; i < list->num; i++) {
- char uuidstr[MAX_LEN_UUID_STR];
- uint8_t *value = list->data[i];
- uint8_t length;
- uint16_t start;
- uuid_t uuid;
-
- /* Each element contains: attribute handle, end group handle
- * and attribute value */
- length = list->len - 2 * sizeof(uint16_t);
- start = att_get_u16(value);
- end = att_get_u16(&value[2]);
-
- g_print("attr handle = 0x%04x, end grp handle = 0x%04x, ",
- start, end);
- if (length == 2)
- sdp_uuid16_create(&uuid, att_get_u16(&value[4]));
- else
- sdp_uuid128_create(&uuid, value + 4);
-
- sdp_uuid2strn(&uuid, uuidstr, MAX_LEN_UUID_STR);
- g_print("attr value (UUID) = %s\n", uuidstr);
+ for (l = services; l; l = l->next) {
+ struct att_primary *prim = l->data;
+ g_print("attr handle = 0x%04x, end grp handle = 0x%04x "
+ "uuid: %s\n", prim->start, prim->end, prim->uuid);
}
- att_data_list_free(list);
-
- /* Don't go beyond the maximum handle value */
- if (end == 0xffff)
- goto done;
-
- /*
- * Discover all primary services sub-procedure shall send another
- * Read by Group Type Request until Error Response is received and
- * the Error Code is set to Attribute Not Found.
- */
-
- gatt_discover_primary(attrib, end + 1, opt_end, NULL, primary_all_cb,
- attrib);
- return;
-
done:
- if (opt_listen == FALSE)
- g_main_loop_quit(event_loop);
+ g_main_loop_quit(event_loop);
}
-static void primary_by_uuid_cb(guint8 status, const guint8 *pdu, guint16 plen,
+static void primary_by_uuid_cb(GSList *ranges, guint8 status,
gpointer user_data)
{
- GSList *ranges, *l;
+ GSList *l;
if (status != 0) {
g_printerr("Discover primary services by UUID failed: %s\n",
@@ -222,21 +176,12 @@
goto done;
}
- ranges = dec_find_by_type_resp(pdu, plen);
- if (ranges == NULL) {
- g_printerr("Protocol error!\n");
- goto done;
- }
-
for (l = ranges; l; l = l->next) {
struct att_range *range = l->data;
g_print("Starting handle: %04x Ending handle: %04x\n",
range->start, range->end);
}
- g_slist_foreach(ranges, (GFunc) g_free, NULL);
- g_slist_free(ranges);
-
done:
g_main_loop_quit(event_loop);
}
@@ -292,11 +237,10 @@
GAttrib *attrib = user_data;
if (opt_uuid)
- gatt_discover_primary(attrib, opt_start, opt_end, opt_uuid,
- primary_by_uuid_cb, attrib);
+ gatt_discover_primary(attrib, opt_uuid, primary_by_uuid_cb,
+ NULL);
else
- gatt_discover_primary(attrib, opt_start, opt_end, NULL,
- primary_all_cb, attrib);
+ gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);
return FALSE;
}
diff --git a/src/device.c b/src/device.c
index 4d53228..b7b9a98 100644
--- a/src/device.c
+++ b/src/device.c
@@ -53,6 +53,8 @@
#include "event.h"
#include "error.h"
#include "glib-helper.h"
+#include "gattrib.h"
+#include "gatt.h"
#include "agent.h"
#include "sdp-xml.h"
#include "storage.h"
@@ -95,6 +97,8 @@
struct browse_req {
DBusConnection *conn;
DBusMessage *msg;
+ GAttrib *attrib;
+ GIOChannel *io;
struct btd_device *device;
GSList *match_uuids;
GSList *profiles_added;
@@ -170,6 +174,13 @@
g_slist_free(req->profiles_removed);
if (req->records)
sdp_list_free(req->records, (sdp_free_func_t) sdp_record_free);
+
+ if (req->io) {
+ g_attrib_unref(req->attrib);
+ g_io_channel_unref(req->io);
+ g_io_channel_shutdown(req->io, FALSE, NULL);
+ }
+
g_free(req);
}
@@ -1559,7 +1570,7 @@
return g_string_free(services, FALSE);
}
-static void primary_cb(GSList *services, int err, gpointer user_data)
+static void primary_cb(GSList *services, guint8 status, gpointer user_data)
{
struct browse_req *req = user_data;
struct btd_device *device = req->device;
@@ -1568,9 +1579,9 @@
bdaddr_t dba, sba;
char *str;
- if (err) {
+ if (status) {
DBusMessage *reply;
- reply = btd_error_failed(req->msg, strerror(-err));
+ reply = btd_error_failed(req->msg, att_ecode2str(status));
g_dbus_send_message(req->conn, reply);
goto done;
}
@@ -1603,13 +1614,36 @@
browse_request_free(req);
}
+static void gatt_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+ struct browse_req *req = user_data;
+ struct btd_device *device = req->device;
+
+ if (gerr) {
+ DBusMessage *reply;
+
+ DBG("%s", gerr->message);
+
+ reply = btd_error_failed(req->msg, gerr->message);
+ g_dbus_send_message(req->conn, reply);
+
+ device->browse = NULL;
+ browse_request_free(req);
+
+ return;
+ }
+
+ req->attrib = g_attrib_new(io);
+ gatt_discover_primary(req->attrib, NULL, primary_cb, req);
+}
+
int device_browse_primary(struct btd_device *device, DBusConnection *conn,
DBusMessage *msg, gboolean secure)
{
struct btd_adapter *adapter = device->adapter;
struct browse_req *req;
+ BtIOSecLevel sec_level;
bdaddr_t src;
- int err;
if (device->browse)
return -EBUSY;
@@ -1619,11 +1653,18 @@
adapter_get_address(adapter, &src);
- err = bt_discover_primary(&src, &device->bdaddr, -1, primary_cb, req,
- secure, NULL);
- if (err < 0) {
+ sec_level = secure ? BT_IO_SEC_HIGH : BT_IO_SEC_LOW;
+
+ req->io = bt_io_connect(BT_IO_L2CAP, gatt_connect_cb, req, NULL, NULL,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
+ BT_IO_OPT_CID, GATT_CID,
+ BT_IO_OPT_SEC_LEVEL, sec_level,
+ BT_IO_OPT_INVALID);
+
+ if (req->io == NULL ) {
browse_request_free(req);
- return err;
+ return -EIO;
}
if (conn == NULL)
@@ -1644,7 +1685,7 @@
req, NULL);
}
- return err;
+ return 0;
}
int device_browse_sdp(struct btd_device *device, DBusConnection *conn,
diff --git a/src/glib-helper.c b/src/glib-helper.c
index b5f038d..22c14e7 100644
--- a/src/glib-helper.c
+++ b/src/glib-helper.c
@@ -38,28 +38,12 @@
#include <glib.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;
- GIOChannel *io;
- bt_primary_t cb;
- bt_destroy_t destroy;
- gpointer user_data;
- GSList *primaries;
-};
-
-static GSList *gattrib_list = NULL;
-
struct cached_sdp_session {
bdaddr_t src;
bdaddr_t dst;
@@ -69,22 +53,6 @@
static GSList *cached_sdp_sessions = NULL;
-static void gattrib_context_free(struct gattrib_context *ctxt)
-{
- gattrib_list = g_slist_remove(gattrib_list, ctxt);
- if (ctxt->destroy)
- ctxt->destroy(ctxt->user_data);
-
- g_slist_free(ctxt->primaries);
- g_attrib_unref(ctxt->attrib);
- if (ctxt->io) {
- g_io_channel_unref(ctxt->io);
- g_io_channel_shutdown(ctxt->io, FALSE, NULL);
- }
-
- g_free(ctxt);
-}
-
static gboolean cached_session_expired(gpointer user_data)
{
struct cached_sdp_session *cached = user_data;
@@ -370,16 +338,22 @@
bacmp(&ctxt->src, &search->src));
}
-static gint gattrib_find_by_bdaddr(gconstpointer data, gconstpointer user_data)
+int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst)
{
- const struct gattrib_context *ctxt = data, *search = user_data;
+ struct search_context match, *ctxt;
+ GSList *l;
- return (bacmp(&ctxt->dst, &search->dst) &&
- bacmp(&ctxt->src, &search->src));
-}
+ memset(&match, 0, sizeof(match));
+ bacpy(&match.src, src);
+ bacpy(&match.dst, dst);
-static int cancel_sdp(struct search_context *ctxt)
-{
+ /* Ongoing SDP Discovery */
+ l = g_slist_find_custom(context_list, &match, find_by_bdaddr);
+ if (l == NULL)
+ return -ENOENT;
+
+ ctxt = l->data;
+
if (!ctxt->session)
return -ENOTCONN;
@@ -394,178 +368,6 @@
return 0;
}
-static int cancel_gattrib(struct gattrib_context *ctxt)
-{
- if (ctxt->attrib)
- g_attrib_cancel_all(ctxt->attrib);
-
- gattrib_context_free(ctxt);
-
- return 0;
-}
-
-int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst)
-{
- struct search_context sdp_ctxt;
- struct gattrib_context gatt_ctxt;
- GSList *match;
-
- memset(&sdp_ctxt, 0, sizeof(sdp_ctxt));
- bacpy(&sdp_ctxt.src, src);
- bacpy(&sdp_ctxt.dst, dst);
-
- /* Ongoing SDP Discovery */
- match = g_slist_find_custom(context_list, &sdp_ctxt, find_by_bdaddr);
- if (match)
- return cancel_sdp(match->data);
-
- memset(&gatt_ctxt, 0, sizeof(gatt_ctxt));
- bacpy(&gatt_ctxt.src, src);
- bacpy(&gatt_ctxt.dst, dst);
-
- /* Ongoing Discover All Primary Services */
- match = g_slist_find_custom(gattrib_list, &gatt_ctxt,
- gattrib_find_by_bdaddr);
- if (match == NULL)
- return -ENOTCONN;
-
- return cancel_gattrib(match->data);
-}
-
-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 start, 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];
- struct att_primary *primary;
- uuid_t u128, u16;
-
- start = att_get_u16(&data[0]);
- 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;
-
- primary = g_try_new0(struct att_primary, 1);
- if (!primary) {
- err = -ENOMEM;
- goto done;
- }
- primary->start = start;
- primary->end = end;
- sdp_uuid2strn(&u128, primary->uuid, sizeof(primary->uuid));
- ctxt->primaries = g_slist_append(ctxt->primaries, primary);
- }
-
- 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->primaries, 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;
- }
-
- ctxt->attrib = g_attrib_new(io);
- 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,
- gboolean secure,
- bt_destroy_t destroy)
-{
- struct gattrib_context *ctxt;
- BtIOSecLevel sec_level;
- GIOChannel *io;
-
- 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 (secure == TRUE)
- sec_level = BT_IO_SEC_HIGH;
- else
- sec_level = BT_IO_SEC_LOW;
-
- if (psm < 0)
- io = bt_io_connect(BT_IO_L2CAP, connect_cb, ctxt, NULL, NULL,
- BT_IO_OPT_SOURCE_BDADDR, src,
- BT_IO_OPT_DEST_BDADDR, dst,
- BT_IO_OPT_CID, GATT_CID,
- BT_IO_OPT_SEC_LEVEL, sec_level,
- BT_IO_OPT_INVALID);
- else
- io = bt_io_connect(BT_IO_L2CAP, connect_cb, ctxt, NULL, NULL,
- BT_IO_OPT_SOURCE_BDADDR, src,
- BT_IO_OPT_DEST_BDADDR, dst,
- BT_IO_OPT_PSM, psm,
- BT_IO_OPT_SEC_LEVEL, sec_level,
- BT_IO_OPT_INVALID);
-
- if (io == NULL) {
- gattrib_context_free(ctxt);
- return -EIO;
- }
-
- ctxt->io = io;
-
- gattrib_list = g_slist_append(gattrib_list, ctxt);
-
- return 0;
-}
-
char *bt_uuid2string(uuid_t *uuid)
{
gchar *str;
diff --git a/src/glib-helper.h b/src/glib-helper.h
index 25fe276..c83f5e2 100644
--- a/src/glib-helper.h
+++ b/src/glib-helper.h
@@ -22,7 +22,6 @@
*/
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_search_service(const bdaddr_t *src, const bdaddr_t *dst,
@@ -30,11 +29,6 @@
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,
- gboolean secure,
- bt_destroy_t destroy);
-
gchar *bt_uuid2string(uuid_t *uuid);
char *bt_name2string(const char *string);
int bt_string2uuid(uuid_t *uuid, const char *string);