summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2009-02-16 09:01:29 +0200
committerJohan Hedberg <johan.hedberg@nokia.com>2009-02-16 09:01:29 +0200
commitd3351f1b45477451d28e9e65996946fa6291f319 (patch)
treea53e990e1bb3fe6252a74fb2c662c02dd06bfe8c /common
parent87e1eceda35d2e17ddfff0d099aaac57392e8bf1 (diff)
Merge external btio rework
Diffstat (limited to 'common')
-rw-r--r--common/Makefile.am2
-rw-r--r--common/btio.c968
-rw-r--r--common/btio.h71
3 files changed, 1040 insertions, 1 deletions
diff --git a/common/Makefile.am b/common/Makefile.am
index 8cf6aecc..8fe15477 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -2,7 +2,7 @@
noinst_LIBRARIES = libhelper.a
libhelper_a_SOURCES = oui.h oui.c textfile.h textfile.c logging.h logging.c \
- glib-helper.h glib-helper.c sdp-xml.h sdp-xml.c
+ glib-helper.h glib-helper.c sdp-xml.h sdp-xml.c btio.h btio.c
noinst_PROGRAMS = test_textfile
diff --git a/common/btio.c b/common/btio.c
new file mode 100644
index 00000000..601a5fd0
--- /dev/null
+++ b/common/btio.c
@@ -0,0 +1,968 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ *
+ * 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
+ *
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include <glib.h>
+
+#include "logging.h"
+#include "btio.h"
+
+#define DEFAULT_DEFER_TIMEOUT 30
+
+struct set_opts {
+ bdaddr_t src;
+ bdaddr_t dst;
+ int defer;
+ int lm_flags;
+ int sec_level;
+ uint8_t channel;
+ uint16_t psm;
+ uint16_t mtu;
+ uint16_t imtu;
+ uint16_t omtu;
+};
+
+struct connect {
+ BtIOConnect connect;
+ gpointer user_data;
+ GDestroyNotify destroy;
+};
+
+struct accept {
+ BtIOConnect connect;
+ gpointer user_data;
+ GDestroyNotify destroy;
+};
+
+struct server {
+ BtIOConnect connect;
+ BtIOConfirm confirm;
+ gpointer user_data;
+ GDestroyNotify destroy;
+};
+
+static void server_remove(struct server *server)
+{
+ if (server->destroy)
+ server->destroy(server->user_data);
+ g_free(server);
+}
+
+static void connect_remove(struct connect *conn)
+{
+ if (conn->destroy)
+ conn->destroy(conn->user_data);
+ g_free(conn);
+}
+
+static void accept_remove(struct accept *accept)
+{
+ if (accept->destroy)
+ accept->destroy(accept->user_data);
+ g_free(accept);
+}
+
+static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct accept *accept = user_data;
+
+ /* If the user aborted this accept attempt */
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_HUP | G_IO_ERR))
+ accept->connect(io, EIO, accept->user_data);
+ else
+ accept->connect(io, 0, accept->user_data);
+
+ return FALSE;
+}
+
+static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct connect *conn = user_data;
+ int err;
+
+ /* If the user aborted this connect attempt */
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ err = 0;
+
+ if (cond & G_IO_OUT) {
+ socklen_t len = sizeof(err);
+ int sock = g_io_channel_unix_get_fd(io);
+ if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
+ err = errno;
+
+ } else if (cond & (G_IO_HUP | G_IO_ERR))
+ err = EIO;
+
+ conn->connect(io, err, conn->user_data);
+
+ return FALSE;
+}
+
+static gboolean server_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct server *server = user_data;
+ int srv_sock, cli_sock;
+ GIOChannel *cli_io;
+
+ /* If the user closed the server */
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ srv_sock = g_io_channel_unix_get_fd(io);
+
+ cli_sock = accept(srv_sock, NULL, NULL);
+ if (cli_sock < 0) {
+ error("accept: %s (%d)", strerror(errno), errno);
+ return TRUE;
+ }
+
+ cli_io = g_io_channel_unix_new(cli_sock);
+
+ if (server->confirm)
+ server->confirm(cli_io, server->user_data);
+ else
+ server->connect(cli_io, 0, server->user_data);
+
+ g_io_channel_unref(cli_io);
+
+ return TRUE;
+}
+
+static void server_add(GIOChannel *io, BtIOConnect connect,
+ BtIOConfirm confirm, gpointer user_data,
+ GDestroyNotify destroy)
+{
+ struct server *server;
+ GIOCondition cond;
+
+ server = g_new0(struct server, 1);
+ server->connect = connect;
+ server->confirm = confirm;
+ server->user_data = user_data;
+ server->destroy = destroy;
+
+ cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, server_cb, server,
+ (GDestroyNotify) server_remove);
+}
+
+static void connect_add(GIOChannel *io, BtIOConnect connect,
+ gpointer user_data, GDestroyNotify destroy)
+{
+ struct connect *conn;
+ GIOCondition cond;
+
+ conn = g_new0(struct connect, 1);
+ conn->connect = connect;
+ conn->user_data = user_data;
+ conn->destroy = destroy;
+
+ cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb, conn,
+ (GDestroyNotify) connect_remove);
+}
+
+static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+ GDestroyNotify destroy)
+{
+ struct accept *accept;
+ GIOCondition cond;
+
+ accept = g_new0(struct accept, 1);
+ accept->connect = connect;
+ accept->user_data = user_data;
+ accept->destroy = destroy;
+
+ cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, accept_cb, accept,
+ (GDestroyNotify) accept_remove);
+}
+
+static int l2cap_bind(int sock, bdaddr_t *src, uint16_t psm)
+{
+ struct sockaddr_l2 addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, src);
+ addr.l2_psm = htobs(psm);
+
+ return bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+}
+
+static int l2cap_connect(int sock, bdaddr_t *dst, uint16_t psm)
+{
+ int err;
+ struct sockaddr_l2 addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, dst);
+ addr.l2_psm = htobs(psm);
+
+ err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+ if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+ return err;
+
+ return 0;
+}
+
+static int l2cap_set(int sock, int lm_flags, int sec_level, uint16_t imtu,
+ uint16_t omtu)
+{
+ int err;
+
+ if (lm_flags) {
+ err = setsockopt(sock, SOL_L2CAP, L2CAP_LM, &lm_flags,
+ sizeof(lm_flags));
+ if (err < 0)
+ return err;
+ }
+
+ if (imtu || omtu) {
+ struct l2cap_options l2o;
+ socklen_t len;
+
+ memset(&l2o, 0, sizeof(l2o));
+ len = sizeof(l2o);
+ err = getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len);
+ if (err < 0)
+ return err;
+
+ if (imtu)
+ l2o.imtu = imtu;
+ if (omtu)
+ l2o.omtu = omtu;
+
+ err = setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+ sizeof(l2o));
+ if (err < 0)
+ return err;
+ }
+
+ if (sec_level) {
+ struct bt_security sec;
+
+ memset(&sec, 0, sizeof(sec));
+ sec.level = sec_level;
+
+ err = setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
+ sizeof(sec));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rfcomm_bind(int sock, bdaddr_t *src, uint8_t channel)
+{
+ struct sockaddr_rc addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, src);
+ addr.rc_channel = channel;
+
+ return bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+}
+
+static int rfcomm_connect(int sock, bdaddr_t *dst, uint8_t channel)
+{
+ int err;
+ struct sockaddr_rc addr;
+ char str[18];
+
+ ba2str(dst, str);
+ debug("Connecting to %s", str);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, dst);
+ addr.rc_channel = channel;
+
+ err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+ if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+ return err;
+
+ return 0;
+}
+
+static int rfcomm_set(int sock, int lm_flags, int sec_level)
+{
+ int err;
+
+ if (lm_flags) {
+ err = setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm_flags,
+ sizeof(lm_flags));
+ if (err < 0)
+ return err;
+ }
+
+ if (sec_level) {
+ struct bt_security sec;
+
+ memset(&sec, 0, sizeof(sec));
+ sec.level = sec_level;
+
+ err = setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
+ sizeof(sec));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int sco_bind(int sock, bdaddr_t *src)
+{
+ struct sockaddr_sco addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, src);
+
+ return bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+}
+
+static int sco_connect(int sock, bdaddr_t *dst)
+{
+ struct sockaddr_sco addr;
+ int err;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, dst);
+
+ err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+ if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+ return err;
+
+ return 0;
+}
+
+static int sco_set(int sock, uint16_t mtu)
+{
+ int err;
+ struct sco_options sco_opt;
+
+ if (mtu) {
+ socklen_t len;
+
+ memset(&sco_opt, 0, len);
+ len = sizeof(sco_opt);
+ err = getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len);
+ if (err < 0)
+ return err;
+
+ sco_opt.mtu = mtu;
+ err = setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
+ sizeof(sco_opt));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static gboolean set_valist(GIOChannel *io, struct set_opts *opts,
+ BtIOOption opt1, va_list args)
+{
+ BtIOOption opt = opt1;
+ const char *str;
+
+ while (opt != BT_IO_OPT_INVALID) {
+ switch (opt) {
+ case BT_IO_OPT_SOURCE:
+ str = va_arg(args, const char *);
+ if (strncasecmp(str, "hci", 3) == 0)
+ hci_devba(atoi(str + 3), &opts->src);
+ else
+ str2ba(str, &opts->src);
+ break;
+ case BT_IO_OPT_SOURCE_BDADDR:
+ bacpy(&opts->src, va_arg(args, bdaddr_t *));
+ break;
+ case BT_IO_OPT_DEST:
+ str2ba(va_arg(args, const char *), &opts->dst);
+ break;
+ case BT_IO_OPT_DEST_BDADDR:
+ bacpy(&opts->dst, va_arg(args, bdaddr_t *));
+ break;
+ case BT_IO_OPT_DEFER_TIMEOUT:
+ opts->defer = va_arg(args, int);
+ break;
+ case BT_IO_OPT_LM_FLAGS:
+ opts->lm_flags = va_arg(args, int);
+ break;
+ case BT_IO_OPT_SEC_LEVEL:
+ opts->sec_level = va_arg(args, int);
+ break;
+ case BT_IO_OPT_CHANNEL:
+ opts->channel = va_arg(args, int);
+ break;
+ case BT_IO_OPT_PSM:
+ opts->psm = va_arg(args, int);
+ break;
+ case BT_IO_OPT_MTU:
+ opts->mtu = va_arg(args, int);
+ opts->imtu = opts->mtu;
+ opts->omtu = opts->mtu;
+ break;
+ case BT_IO_OPT_OMTU:
+ opts->omtu = va_arg(args, int);
+ if (!opts->mtu)
+ opts->mtu = opts->omtu;
+ break;
+ case BT_IO_OPT_IMTU:
+ opts->imtu = va_arg(args, int);
+ if (!opts->mtu)
+ opts->mtu = opts->imtu;
+ break;
+ default:
+ error("set_valist: unknown option %d", opt);
+ return FALSE;
+ }
+
+ opt = va_arg(args, int);
+ }
+
+ return TRUE;
+}
+
+static gboolean get_sec_level(int sock, int *level)
+{
+ struct bt_security sec;
+ socklen_t len;
+
+ memset(&sec, 0, sizeof(sec));
+ len = sizeof(sec);
+ if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) < 0) {
+ error("getsockopt(BT_SECURITY): %s (%d)", strerror(errno),
+ errno);
+ return FALSE;
+ }
+
+ *level = sec.level;
+
+ return TRUE;
+}
+
+static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
+ socklen_t len)
+{
+ socklen_t olen;
+
+ memset(src, 0, len);
+ olen = len;
+ if (getsockname(sock, src, &olen) < 0) {
+ error("getsockname: %s (%d)", strerror(errno), errno);
+ return FALSE;
+ }
+
+ memset(dst, 0, len);
+ olen = len;
+ if (getpeername(sock, dst, &olen) < 0) {
+ error("getpeername: %s (%d)", strerror(errno), errno);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean l2cap_get(int sock, BtIOOption opt1, va_list args)
+{
+ BtIOOption opt = opt1;
+ struct sockaddr_l2 src, dst;
+ struct l2cap_options l2o;
+ socklen_t len;
+
+ len = sizeof(l2o);
+ memset(&l2o, 0, len);
+ if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+ error("getsockopt: %s (%d)", strerror(errno), errno);
+ return FALSE;
+ }
+
+ if (!get_peers(sock, (struct sockaddr *) &src,
+ (struct sockaddr *) &dst, sizeof(src)))
+ return FALSE;
+
+ while (opt != BT_IO_OPT_INVALID) {
+ switch (opt) {
+ case BT_IO_OPT_SOURCE:
+ ba2str(&src.l2_bdaddr, va_arg(args, char *));
+ break;
+ case BT_IO_OPT_SOURCE_BDADDR:
+ bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr);
+ break;
+ case BT_IO_OPT_DEST:
+ ba2str(&dst.l2_bdaddr, va_arg(args, char *));
+ break;
+ case BT_IO_OPT_DEST_BDADDR:
+ bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
+ break;
+ case BT_IO_OPT_DEFER_TIMEOUT:
+ len = sizeof(int);
+ if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+ va_arg(args, int *), &len) < 0)
+ return FALSE;
+ break;
+ case BT_IO_OPT_LM_FLAGS:
+ len = sizeof(int);
+ if (getsockopt(sock, SOL_L2CAP, L2CAP_LM,
+ va_arg(args, int *), &len) < 0)
+ return FALSE;
+ break;
+ case BT_IO_OPT_SEC_LEVEL:
+ if (!get_sec_level(sock, va_arg(args, int *)))
+ return FALSE;
+ break;
+ case BT_IO_OPT_PSM:
+ *(va_arg(args, int *)) = src.l2_psm ?
+ src.l2_psm : dst.l2_psm;
+ break;
+ case BT_IO_OPT_OMTU:
+ *(va_arg(args, int *)) = l2o.omtu;
+ break;
+ case BT_IO_OPT_IMTU:
+ *(va_arg(args, int *)) = l2o.imtu;
+ break;
+ default:
+ error("l2cap_get: unknown option %d", opt);
+ return FALSE;
+ }
+
+ opt = va_arg(args, int);
+ }
+
+ return TRUE;
+}
+
+static gboolean rfcomm_get(int sock, BtIOOption opt1, va_list args)
+{
+ BtIOOption opt = opt1;
+ struct sockaddr_rc src, dst;
+ socklen_t len;
+
+ if (!get_peers(sock, (struct sockaddr *) &src,
+ (struct sockaddr *) &dst, sizeof(src)))
+ return FALSE;
+
+ while (opt != BT_IO_OPT_INVALID) {
+ switch (opt) {
+ case BT_IO_OPT_SOURCE:
+ ba2str(&src.rc_bdaddr, va_arg(args, char *));
+ break;
+ case BT_IO_OPT_SOURCE_BDADDR:
+ bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr);
+ break;
+ case BT_IO_OPT_DEST:
+ ba2str(&dst.rc_bdaddr, va_arg(args, char *));
+ break;
+ case BT_IO_OPT_DEST_BDADDR:
+ bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr);
+ break;
+ case BT_IO_OPT_DEFER_TIMEOUT:
+ len = sizeof(int);
+ if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+ va_arg(args, int *), &len) < 0)
+ return FALSE;
+ break;
+ case BT_IO_OPT_LM_FLAGS:
+ len = sizeof(int);
+ if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM,
+ va_arg(args, int *), &len) < 0)
+ return FALSE;
+ break;
+ case BT_IO_OPT_SEC_LEVEL:
+ if (!get_sec_level(sock, va_arg(args, int *)))
+ return FALSE;
+ break;
+ case BT_IO_OPT_CHANNEL:
+ *(va_arg(args, int *)) = src.rc_channel ?
+ src.rc_channel : dst.rc_channel;
+ break;
+ default:
+ error("rfcomm_get: unknown option %d", opt);
+ return FALSE;
+ }
+
+ opt = va_arg(args, int);
+ }
+
+ return TRUE;
+}
+
+static gboolean sco_get(int sock, BtIOOption opt1, va_list args)
+{
+ BtIOOption opt = opt1;
+ struct sockaddr_sco src, dst;
+ struct sco_options sco_opt;
+ socklen_t len;
+
+ len = sizeof(sco_opt);
+ memset(&sco_opt, 0, len);
+ if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+ error("getsockopt: %s (%d)", strerror(errno), errno);
+ return FALSE;
+ }
+
+ if (!get_peers(sock, (struct sockaddr *) &src,
+ (struct sockaddr *) &dst, sizeof(src)))
+ return FALSE;
+
+ while (opt != BT_IO_OPT_INVALID) {
+ switch (opt) {
+ case BT_IO_OPT_SOURCE:
+ ba2str(&src.sco_bdaddr, va_arg(args, char *));
+ break;
+ case BT_IO_OPT_SOURCE_BDADDR:
+ bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr);
+ break;
+ case BT_IO_OPT_DEST:
+ ba2str(&dst.sco_bdaddr, va_arg(args, char *));
+ break;
+ case BT_IO_OPT_DEST_BDADDR:
+ bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr);
+ break;
+ case BT_IO_OPT_DEFER_TIMEOUT:
+ len = sizeof(int);
+ if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+ va_arg(args, int *), &len) < 0)
+ return FALSE;
+ break;
+ case BT_IO_OPT_SEC_LEVEL:
+ if (!get_sec_level(sock, va_arg(args, int *)))
+ return FALSE;
+ break;
+ case BT_IO_OPT_MTU:
+ case BT_IO_OPT_IMTU:
+ case BT_IO_OPT_OMTU:
+ *(va_arg(args, int *)) = sco_opt.mtu;
+ break;
+ default:
+ error("sco_get: unknown option %d", opt);
+ return FALSE;
+ }
+
+ opt = va_arg(args, int);
+ }
+
+ return TRUE;
+}
+
+static gboolean get_valist(GIOChannel *io, BtIOType type, BtIOOption opt1,
+ va_list args)
+{
+ int sock;
+
+ sock = g_io_channel_unix_get_fd(io);
+
+ switch (type) {
+ case BT_IO_L2RAW:
+ case BT_IO_L2CAP:
+ return l2cap_get(sock, opt1, args);
+ case BT_IO_RFCOMM:
+ return rfcomm_get(sock, opt1, args);
+ case BT_IO_SCO:
+ return sco_get(sock, opt1, args);
+ }
+
+ return FALSE;
+}
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+ GDestroyNotify destroy)
+{
+ int sock;
+ char c;
+ struct pollfd pfd;
+
+ sock = g_io_channel_unix_get_fd(io);
+
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = sock;
+ pfd.events = POLLOUT;
+
+ if (poll(&pfd, 1, 0) < 0)
+ error("poll: %s (%d)", strerror(errno), errno);
+ else if (!(pfd.revents & POLLOUT))
+ read(sock, &c, 1);
+
+ accept_add(io, connect, user_data, destroy);
+
+ return TRUE;
+}
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, BtIOOption opt1, ...)
+{
+ va_list args;
+ gboolean ret;
+ struct set_opts opts;
+ int sock;
+
+ memset(&opts, 0, sizeof(opts));
+
+ va_start(args, opt1);
+ ret = set_valist(io, &opts, opt1, args);
+ va_end(args);
+
+ if (!ret)
+ return ret;
+
+ sock = g_io_channel_unix_get_fd(io);
+
+ switch (type) {
+ case BT_IO_L2RAW:
+ case BT_IO_L2CAP:
+ if (l2cap_set(sock, opts.lm_flags, opts.sec_level,
+ opts.imtu, opts.omtu) < 0)
+ return FALSE;
+ break;
+ case BT_IO_RFCOMM:
+ if (rfcomm_set(sock, opts.lm_flags, opts.sec_level) < 0)
+ return FALSE;
+ break;
+ case BT_IO_SCO:
+ if (sco_set(sock, opts.mtu) < 0)
+ return FALSE;
+ break;
+ }
+
+ return ret;
+}
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, BtIOOption opt1, ...)
+{
+ va_list args;
+ gboolean ret;
+
+ va_start(args, opt1);
+ ret = get_valist(io, type, opt1, args);
+ va_end(args);
+
+ return ret;
+}
+
+static GIOChannel *create_io(BtIOType type, gboolean server, struct set_opts *opts)
+{
+ int sock, err;
+ GIOChannel *io;
+
+ switch (type) {
+ case BT_IO_L2RAW:
+ sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+ if (sock < 0) {
+ error("socket: %s (%d)", strerror(errno), errno);
+ return NULL;
+ }
+ err = l2cap_bind(sock, &opts->src, server ? opts->psm : 0);
+ if (err < 0) {
+ error("l2cap_bind: %s (%d)", strerror(-err), -err);
+ return NULL;
+ }
+ err = l2cap_set(sock, 0, opts->sec_level, 0, 0);
+ if (err < 0) {
+ error("l2cap_set: %s (%d)", strerror(-err), -err);
+ return NULL;
+ }
+ break;
+ case BT_IO_L2CAP:
+ sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (sock < 0) {
+ error("socket: %s (%d)", strerror(errno), errno);
+ return NULL;
+ }
+ err = l2cap_bind(sock, &opts->src, server ? opts->psm : 0);
+ if (err < 0) {
+ error("l2cap_bind: %s (%d)", strerror(-err), -err);
+ return NULL;
+ }
+ err = l2cap_set(sock, opts->lm_flags, opts->sec_level,
+ opts->imtu, opts->omtu);
+ if (err < 0) {
+ error("l2cap_set: %s (%d)", strerror(-err), -err);
+ return NULL;
+ }
+ break;
+ case BT_IO_RFCOMM:
+ sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sock < 0) {
+ error("socket: %s (%d)", strerror(errno), errno);
+ return NULL;
+ }
+ err = rfcomm_bind(sock, &opts->src, server ? opts->channel : 0);
+ if (err < 0) {
+ error("rfcomm_bind: %s (%d)", strerror(-err), -err);
+ return NULL;
+ }
+ err = rfcomm_set(sock, opts->lm_flags, opts->sec_level);
+ if (err < 0) {
+ error("rfcomm_set: %s (%d)", strerror(-err), -err);
+ return NULL;
+ }
+ break;
+ case BT_IO_SCO:
+ sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+ if (sock < 0) {
+ error("socket: %s (%d)", strerror(errno), errno);
+ return NULL;
+ }
+ err = sco_bind(sock, &opts->src);
+ if (err < 0) {
+ error("sco_bind: %s (%d)", strerror(-err), -err);
+ return NULL;
+ }
+ err = sco_set(sock, opts->mtu);
+ if (err < 0) {
+ error("sco_set: %s (%d)", strerror(-err), -err);
+ return NULL;
+ }
+ break;
+ }
+
+ io = g_io_channel_unix_new(sock);
+
+ g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+ return io;
+}
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+ gpointer user_data, GDestroyNotify destroy,
+ BtIOOption opt1, ...)
+{
+ GIOChannel *io;
+ va_list args;
+ struct set_opts opts;
+ int err, sock;
+ gboolean ret;
+
+ memset(&opts, 0, sizeof(opts));
+
+ va_start(args, opt1);
+ ret = set_valist(io, &opts, opt1, args);
+ va_end(args);
+
+ if (ret == FALSE)
+ return NULL;
+
+ io = create_io(type, FALSE, &opts);
+ if (io == NULL)
+ return NULL;
+
+ sock = g_io_channel_unix_get_fd(io);
+
+ switch (type) {
+ case BT_IO_L2RAW:
+ err = l2cap_connect(sock, &opts.dst, 0);
+ break;
+ case BT_IO_L2CAP:
+ err = l2cap_connect(sock, &opts.dst, opts.psm);
+ break;
+ case BT_IO_RFCOMM:
+ err = rfcomm_connect(sock, &opts.dst, opts.channel);
+ break;
+ case BT_IO_SCO:
+ err = sco_connect(sock, &opts.dst);
+ break;
+ }
+
+ if (ret < 0) {
+ error("connect: %s (%d)", strerror(-err), -err);
+ g_io_channel_unref(io);
+ return NULL;
+ }
+
+ connect_add(io, connect, user_data, destroy);
+
+ return io;
+}
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+ BtIOConfirm confirm, gpointer user_data,
+ GDestroyNotify destroy, BtIOOption opt1, ...)
+{
+ GIOChannel *io;
+ va_list args;
+ struct set_opts opts;
+ int sock;
+ gboolean ret;
+
+ if (type == BT_IO_L2RAW)
+ return NULL;
+
+ memset(&opts, 0, sizeof(opts));
+
+ opts.defer = DEFAULT_DEFER_TIMEOUT;
+
+ va_start(args, opt1);
+ ret = set_valist(io, &opts, opt1, args);
+ va_end(args);
+
+ if (ret == FALSE)
+ return NULL;
+
+ io = create_io(type, TRUE, &opts);
+ if (io == NULL)
+ return NULL;
+
+ sock = g_io_channel_unix_get_fd(io);
+
+ if (confirm)
+ setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer,
+ sizeof(opts.defer));
+
+ if (listen(sock, 5) < 0) {
+ error("listen: %s (%d)", strerror(errno), errno);
+ g_io_channel_unref(io);
+ return NULL;
+ }
+
+ server_add(io, connect, confirm, user_data, destroy);
+
+ return io;
+}
diff --git a/common/btio.h b/common/btio.h
new file mode 100644
index 00000000..bfdd2966
--- /dev/null
+++ b/common/btio.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ *
+ * 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
+ *
+ */
+#ifndef BT_IO_H
+#define BT_IO_H
+
+#include <glib.h>
+
+typedef enum {
+ BT_IO_L2RAW,
+ BT_IO_L2CAP,
+ BT_IO_RFCOMM,
+ BT_IO_SCO,
+} BtIOType;
+
+typedef enum {
+ BT_IO_OPT_SOURCE,
+ BT_IO_OPT_SOURCE_BDADDR,
+ BT_IO_OPT_DEST,
+ BT_IO_OPT_DEST_BDADDR,
+ BT_IO_OPT_DEFER_TIMEOUT,
+ BT_IO_OPT_LM_FLAGS,
+ BT_IO_OPT_SEC_LEVEL,
+ BT_IO_OPT_CHANNEL,
+ BT_IO_OPT_PSM,
+ BT_IO_OPT_MTU,
+ BT_IO_OPT_OMTU,
+ BT_IO_OPT_IMTU,
+ BT_IO_OPT_INVALID,
+} BtIOOption;
+
+typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
+
+typedef void (*BtIOConnect)(GIOChannel *io, int err, gpointer user_data);
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+ GDestroyNotify destroy);
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, BtIOOption opt1, ...);
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, BtIOOption opt1, ...);
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+ gpointer user_data, GDestroyNotify destroy,
+ BtIOOption opt1, ...);
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+ BtIOConfirm confirm, gpointer user_data,
+ GDestroyNotify destroy, BtIOOption opt1, ...);
+
+#endif