diff options
-rw-r--r-- | common/glib-helper.c | 206 | ||||
-rw-r--r-- | common/glib-helper.h | 9 |
2 files changed, 196 insertions, 19 deletions
diff --git a/common/glib-helper.c b/common/glib-helper.c index 771b9d76..b92a1ff4 100644 --- a/common/glib-helper.c +++ b/common/glib-helper.c @@ -26,10 +26,13 @@ #endif #include <errno.h> +#include <unistd.h> #include <arpa/inet.h> +#include <sys/ioctl.h> #include <sys/socket.h> #include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> #include <bluetooth/sdp.h> #include <bluetooth/sdp_lib.h> @@ -37,6 +40,12 @@ #include "glib-helper.h" +struct io_context { + GIOChannel *io; + bt_io_callback_t cb; + gpointer user_data; +}; + struct search_context { bdaddr_t src; bdaddr_t dst; @@ -44,6 +53,7 @@ struct search_context { bt_callback_t cb; bt_destroy_t destroy; gpointer user_data; + uuid_t uuid; }; static void search_context_cleanup(struct search_context *ctxt) @@ -94,7 +104,8 @@ static void search_completed_cb(uint8_t type, uint16_t status, } while (scanned < size); done: - ctxt->cb(ctxt->user_data, recs, err); + if (ctxt->cb) + ctxt->cb(ctxt->user_data, recs, err); search_context_cleanup(ctxt); } @@ -117,7 +128,8 @@ static gboolean search_process_cb(GIOChannel *chan, failed: if (err) { - ctxt->cb(ctxt->user_data, NULL, -err); + if (ctxt->cb) + ctxt->cb(ctxt->user_data, NULL, -err); search_context_cleanup(ctxt); } @@ -129,7 +141,6 @@ static gboolean connect_watch(GIOChannel *chan, GIOCondition cond, gpointer user struct search_context *ctxt = user_data; sdp_list_t *search, *attrids; uint32_t range = 0x0000ffff; - uuid_t uuid; socklen_t len; int sk, err = 0; @@ -149,8 +160,7 @@ static gboolean connect_watch(GIOChannel *chan, GIOCondition cond, gpointer user goto failed; } - sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); - search = sdp_list_append(NULL, &uuid); + search = sdp_list_append(NULL, &ctxt->uuid); attrids = sdp_list_append(NULL, &range); if (sdp_service_search_attr_async(ctxt->session, search, SDP_ATTR_REQ_RANGE, attrids) < 0) { @@ -169,47 +179,77 @@ static gboolean connect_watch(GIOChannel *chan, GIOCondition cond, gpointer user return FALSE; failed: - ctxt->cb(ctxt->user_data, NULL, -err); + if (ctxt->cb) + ctxt->cb(ctxt->user_data, NULL, -err); search_context_cleanup(ctxt); return FALSE; } -int bt_discover_services(const bdaddr_t *src, const bdaddr_t *dst, - bt_callback_t cb, void *user_data, bt_destroy_t destroy) +static int create_search_context(struct search_context **ctxt, + const bdaddr_t *src, const bdaddr_t *dst, + uuid_t uuid) { - struct search_context *ctxt; sdp_session_t *s; GIOChannel *chan; - if (!cb) + if (!ctxt) return -EINVAL; s = sdp_connect(src, dst, SDP_NON_BLOCKING); if (!s) return -errno; - ctxt = g_try_malloc0(sizeof(struct search_context)); - if (!ctxt) { + *ctxt = g_try_malloc0(sizeof(struct search_context)); + if (!*ctxt) { sdp_close(s); return -ENOMEM; } - bacpy(&ctxt->src, src); - bacpy(&ctxt->dst, dst); - ctxt->session = s; - ctxt->cb = cb; - ctxt->destroy = destroy; - ctxt->user_data = user_data; + bacpy(&(*ctxt)->src, src); + bacpy(&(*ctxt)->dst, dst); + (*ctxt)->session = s; + (*ctxt)->uuid = uuid; chan = g_io_channel_unix_new(sdp_get_socket(s)); g_io_add_watch(chan, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - connect_watch, ctxt); + connect_watch, *ctxt); g_io_channel_unref(chan); return 0; } +int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst, + uuid_t uuid, bt_callback_t cb, void *user_data, + bt_destroy_t destroy) +{ + struct search_context *ctxt; + int err; + + if (!cb) + return -EINVAL; + + err = create_search_context(&ctxt, src, dst, uuid); + if (err < 0) + return err; + + ctxt->cb = cb; + ctxt->destroy = destroy; + ctxt->user_data = user_data; + + return 0; +} + +int bt_discover_services(const bdaddr_t *src, const bdaddr_t *dst, + bt_callback_t cb, void *user_data, bt_destroy_t destroy) +{ + uuid_t uuid; + + sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); + + return bt_search_service(src, dst, uuid, cb, user_data, destroy); +} + char *bt_uuid2string(uuid_t *uuid) { gchar *str; @@ -301,3 +341,131 @@ GSList *bt_string2list(const gchar *str) return l; } + +static gboolean rfcomm_connect_cb(GIOChannel *io, GIOCondition cond, + struct io_context *io_ctxt) +{ + int sk, err, ret; + socklen_t len; + + if (cond & G_IO_NVAL) + return FALSE; + + sk = g_io_channel_unix_get_fd(io); + + len = sizeof(ret); + if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { + err = -errno; + goto done; + } + + if (ret != 0) + err = -ret; + + io_ctxt->io = NULL; + +done: + if (io_ctxt->cb) + io_ctxt->cb(io, err, io_ctxt->user_data); + if (io_ctxt->io) { + g_io_channel_close(io_ctxt->io); + g_io_channel_unref(io_ctxt->io); + } + g_free(io_ctxt); + + return FALSE; +} + +static int rfcomm_connect(struct io_context *io_ctxt, const bdaddr_t *src, + const bdaddr_t *dst, int channel) +{ + struct sockaddr_rc addr; + int sk, err; + + sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sk < 0) + return -errno; + + io_ctxt->io = g_io_channel_unix_new(sk); + if (!io_ctxt->io) + return -ENOMEM; + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, src); + addr.rc_channel = 0; + + err = bind(sk, (struct sockaddr *) &addr, sizeof(addr)); + if (err < 0) + return err; + + if (g_io_channel_set_flags(io_ctxt->io, G_IO_FLAG_NONBLOCK, NULL) != + G_IO_STATUS_NORMAL) + return -EPERM; + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, dst); + addr.rc_channel = channel; + + err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); + + if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) + return err; + + g_io_add_watch(io_ctxt->io, G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) rfcomm_connect_cb, io_ctxt); + + return 0; +} + +static int create_io_context(struct io_context **io_ctxt, bt_io_callback_t cb, + void *user_data) +{ + *io_ctxt = g_try_malloc0(sizeof(struct search_context)); + if (!*io_ctxt) + return -ENOMEM; + + (*io_ctxt)->cb = cb; + (*io_ctxt)->user_data = user_data; + + return 0; +} + +int bt_rfcomm_connect(const bdaddr_t *src, const bdaddr_t *dst, + sdp_record_t *record, bt_io_callback_t cb, + void *user_data) +{ + struct io_context *io_ctxt; + sdp_list_t *protos; + int err, channel = -1; + + if (!record) + return -EINVAL; + + if (!sdp_get_access_protos(record, &protos)) { + channel = sdp_get_proto_port(protos, RFCOMM_UUID); + sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, + NULL); + sdp_list_free(protos, NULL); + } + + if (channel < 0) + return -EINVAL; + + err = create_io_context(&io_ctxt, cb, user_data); + if (err < 0) + return err; + + err = rfcomm_connect(io_ctxt, src, dst, channel); + if (err < 0) { + if (io_ctxt->io) { + g_io_channel_close(io_ctxt->io); + g_io_channel_unref(io_ctxt->io); + } + g_free(io_ctxt); + return err; + } + + return 0; +} diff --git a/common/glib-helper.h b/common/glib-helper.h index ed724af6..a6939fd1 100644 --- a/common/glib-helper.h +++ b/common/glib-helper.h @@ -21,12 +21,21 @@ * */ +typedef void (*bt_io_callback_t) (GIOChannel *io, int err, gpointer user_data); typedef void (*bt_callback_t) (gpointer user_data, sdp_list_t *recs, int err); typedef void (*bt_destroy_t) (gpointer user_data); int bt_discover_services(const bdaddr_t *src, const bdaddr_t *dst, bt_callback_t cb, void *user_data, bt_destroy_t destroy); +int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst, + uuid_t uuid, bt_callback_t cb, void *user_data, + bt_destroy_t destroy); + gchar *bt_uuid2string(uuid_t *uuid); gchar *bt_list2string(GSList *list); GSList *bt_string2list(const gchar *str); + +int bt_rfcomm_connect(const bdaddr_t *src, const bdaddr_t *dst, + sdp_record_t *record, bt_io_callback_t cb, + void *user_data); |