Linux bluetooth development
 help / color / mirror / Atom feed
* obex: isolate btio.[ch] as a library and do some cleanup
From: Zhenhua Zhang @ 2010-09-09  9:41 UTC (permalink / raw)
  To: linux-bluetooth

Hi,

These patches move obexd/src/btio.[ch] out from source directory and make it identical with bluez's btio.[ch]. So we can freely copy them among bluez, obex and ofono.

Regards,
Zhenhua


^ permalink raw reply

* [PATCH 1/4] btio: Remove blank line at EOF
From: Zhenhua Zhang @ 2010-09-09  9:41 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284025283-3773-1-git-send-email-zhenhua.zhang@intel.com>

---
 src/btio.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/src/btio.c b/src/btio.c
index 485eec6..42a3bcd 100644
--- a/src/btio.c
+++ b/src/btio.c
@@ -1297,4 +1297,3 @@ GQuark bt_io_error_quark(void)
 {
 	return g_quark_from_static_string("bt-io-error-quark");
 }
-
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 2/4] btio: Seperate btio.[ch] into btio directory
From: Zhenhua Zhang @ 2010-09-09  9:41 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284025283-3773-1-git-send-email-zhenhua.zhang@intel.com>

Seperate btio.[ch] from src directory to btio sub-folder.
---
 Makefile.am |   19 +-
 btio/btio.c | 1299 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 btio/btio.h |   97 +++++
 src/btio.c  | 1299 -----------------------------------------------------------
 src/btio.h  |   97 -----
 5 files changed, 1406 insertions(+), 1405 deletions(-)
 create mode 100644 btio/btio.c
 create mode 100644 btio/btio.h
 delete mode 100644 src/btio.c
 delete mode 100644 src/btio.h

diff --git a/Makefile.am b/Makefile.am
index 07fc27d..961a375 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,6 +18,8 @@ gwobex_sources = gwobex/gw-obex.h gwobex/gw-obex.c \
 			gwobex/obex-xfer.h gwobex/obex-xfer.c \
 			gwobex/utils.h gwobex/utils.c gwobex/log.h
 
+btio_sources = btio/btio.h btio/btio.c
+
 libexec_PROGRAMS =
 
 if SERVER
@@ -65,11 +67,10 @@ builtin_nodist += plugins/phonebook.c
 
 libexec_PROGRAMS += src/obexd
 
-src_obexd_SOURCES = $(gdbus_sources) $(builtin_sources) \
+src_obexd_SOURCES = $(gdbus_sources) $(builtin_sources) $(btio_sources) \
 			src/main.c src/obexd.h src/plugin.h src/plugin.c \
-			src/log.h src/log.c src/btio.h src/btio.c \
-			src/dbus.h src/manager.c src/obex.h src/obex.c \
-			src/obex-priv.h \
+			src/log.h src/log.c src/dbus.h src/manager.c \
+			src/obex.h src/obex.c src/obex-priv.h \
 			src/mimetype.h src/mimetype.c \
 			src/service.h src/service.c \
 			src/transport.h src/transport.c \
@@ -107,13 +108,12 @@ service_in_files += client/obex-client.service.in
 
 libexec_PROGRAMS += client/obex-client
 
-client_obex_client_SOURCES = $(gdbus_sources) $(gwobex_sources) client/main.c \
-				client/session.h client/session.c \
+client_obex_client_SOURCES = $(gdbus_sources) $(gwobex_sources) $(btio_sources) \
+				client/main.c client/session.h client/session.c \
 				src/log.h src/log.c \
 				client/pbap.h client/pbap.c \
 				client/sync.h client/sync.c \
-				client/transfer.h client/transfer.c \
-				src/btio.c src/btio.h
+				client/transfer.h client/transfer.c
 
 client_obex_client_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ @OPENOBEX_LIBS@ @BLUEZ_LIBS@
 endif
@@ -126,7 +126,8 @@ AM_CFLAGS = @OPENOBEX_CFLAGS@ @BLUEZ_CFLAGS@ @EBOOK_CFLAGS@ \
 			-DOBEX_PLUGIN_BUILTIN -DPLUGINDIR=\""$(plugindir)"\"
 
 INCLUDES = -I$(builddir)/src -I$(srcdir)/src -I$(srcdir)/plugins \
-				-I$(srcdir)/gdbus -I$(srcdir)/gwobex
+				-I$(srcdir)/gdbus -I$(srcdir)/gwobex \
+				-I$(srcdir)/btio
 
 CLEANFILES = $(service_DATA) $(builtin_files)
 
diff --git a/btio/btio.c b/btio/btio.c
new file mode 100644
index 0000000..42a3bcd
--- /dev/null
+++ b/btio/btio.c
@@ -0,0 +1,1299 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  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 "btio.h"
+
+#define ERROR_FAILED(gerr, str, err) \
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
+				str ": %s (%d)", strerror(err), err)
+
+#define DEFAULT_DEFER_TIMEOUT 30
+
+struct set_opts {
+	bdaddr_t src;
+	bdaddr_t dst;
+	int defer;
+	int sec_level;
+	uint8_t channel;
+	uint16_t psm;
+	uint16_t mtu;
+	uint16_t imtu;
+	uint16_t omtu;
+	int master;
+	uint8_t mode;
+};
+
+struct connect {
+	BtIOConnect connect;
+	void *user_data;
+	GDestroyNotify destroy;
+};
+
+struct accept {
+	BtIOConnect connect;
+	void *user_data;
+	GDestroyNotify destroy;
+};
+
+struct server {
+	BtIOConnect connect;
+	BtIOConfirm confirm;
+	void *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 check_nval(GIOChannel *io)
+{
+	struct pollfd fds;
+
+	memset(&fds, 0, sizeof(fds));
+	fds.fd = g_io_channel_unix_get_fd(io);
+	fds.events = POLLNVAL;
+
+	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
+							void *user_data)
+{
+	struct accept *accept = user_data;
+	GError *err = NULL;
+
+	/* If the user aborted this accept attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
+				"HUP or ERR on socket");
+
+	accept->connect(io, err, accept->user_data);
+
+	g_clear_error(&err);
+
+	return FALSE;
+}
+
+static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
+							void *user_data)
+{
+	struct connect *conn = user_data;
+	GError *gerr = NULL;
+
+	/* If the user aborted this connect attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & G_IO_OUT) {
+		int err = 0, sock = g_io_channel_unix_get_fd(io);
+		socklen_t len = sizeof(err);
+
+		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
+			err = errno;
+
+		if (err)
+			g_set_error(&gerr, BT_IO_ERROR,
+					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
+					strerror(err), err);
+	} else if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"HUP or ERR on socket");
+
+	conn->connect(io, gerr, conn->user_data);
+
+	if (gerr)
+		g_error_free(gerr);
+
+	return FALSE;
+}
+
+static gboolean server_cb(GIOChannel *io, GIOCondition cond,
+							void *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) || check_nval(io))
+		return FALSE;
+
+	srv_sock = g_io_channel_unix_get_fd(io);
+
+	cli_sock = accept(srv_sock, NULL, NULL);
+	if (cli_sock < 0)
+		return TRUE;
+
+	cli_io = g_io_channel_unix_new(cli_sock);
+
+	g_io_channel_set_close_on_unref(cli_io, TRUE);
+	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
+
+	if (server->confirm)
+		server->confirm(cli_io, server->user_data);
+	else
+		server->connect(cli_io, NULL, server->user_data);
+
+	g_io_channel_unref(cli_io);
+
+	return TRUE;
+}
+
+static void server_add(GIOChannel *io, BtIOConnect connect,
+				BtIOConfirm confirm, void *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,
+				void *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, void *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, const bdaddr_t *src, uint16_t psm, GError **err)
+{
+	struct sockaddr_l2 addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+	addr.l2_psm = htobs(psm);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "l2cap_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int l2cap_connect(int sock, const 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_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & L2CAP_LM_MASTER)
+			return 0;
+		flags |= L2CAP_LM_MASTER;
+	} else {
+		if (!(flags & L2CAP_LM_MASTER))
+			return 0;
+		flags &= ~L2CAP_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & RFCOMM_LM_MASTER)
+			return 0;
+		flags |= RFCOMM_LM_MASTER;
+	} else {
+		if (!(flags & RFCOMM_LM_MASTER))
+			return 0;
+		flags &= ~RFCOMM_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int l2cap_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		L2CAP_LM_AUTH,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		RFCOMM_LM_AUTH,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
+{
+	struct bt_security sec;
+	int ret;
+
+	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Valid security level range is %d-%d",
+				BT_SECURITY_LOW, BT_SECURITY_HIGH);
+		return FALSE;
+	}
+
+	memset(&sec, 0, sizeof(sec));
+	sec.level = level;
+
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
+							sizeof(sec)) == 0)
+		return TRUE;
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_set_lm(sock, level);
+	else
+		ret = rfcomm_set_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "setsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & L2CAP_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & L2CAP_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & L2CAP_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static int rfcomm_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & RFCOMM_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & RFCOMM_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & RFCOMM_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static gboolean get_sec_level(int sock, BtIOType type, int *level,
+								GError **err)
+{
+	struct bt_security sec;
+	socklen_t len;
+	int ret;
+
+	memset(&sec, 0, sizeof(sec));
+	len = sizeof(sec);
+	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+		*level = sec.level;
+		return TRUE;
+	}
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_get_lm(sock, level);
+	else
+		ret = rfcomm_get_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "getsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
+					uint8_t mode, int master, GError **err)
+{
+	if (imtu || omtu || mode) {
+		struct l2cap_options l2o;
+		socklen_t len;
+
+		memset(&l2o, 0, sizeof(l2o));
+		len = sizeof(l2o);
+		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+								&len) < 0) {
+			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+
+		if (imtu)
+			l2o.imtu = imtu;
+		if (omtu)
+			l2o.omtu = omtu;
+		if (mode)
+			l2o.mode = mode;
+
+		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+							sizeof(l2o)) < 0) {
+			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+	}
+
+	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "l2cap_set_master", errno);
+		return FALSE;
+	}
+
+	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
+		return FALSE;
+
+	return TRUE;
+}
+
+static int rfcomm_bind(int sock,
+		const bdaddr_t *src, uint8_t channel, GError **err)
+{
+	struct sockaddr_rc addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, src);
+	addr.rc_channel = channel;
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "rfcomm_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
+{
+	int err;
+	struct sockaddr_rc addr;
+
+	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 gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
+{
+	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
+		return FALSE;
+
+	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "rfcomm_set_master", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int sco_bind(int sock, const bdaddr_t *src, GError **err)
+{
+	struct sockaddr_sco addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, src);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "sco_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sco_connect(int sock, const 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 gboolean sco_set(int sock, uint16_t mtu, GError **err)
+{
+	struct sco_options sco_opt;
+	socklen_t len;
+
+	if (!mtu)
+		return TRUE;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	sco_opt.mtu = mtu;
+	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
+						sizeof(sco_opt)) < 0) {
+		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean parse_set_opts(struct set_opts *opts, GError **err,
+						BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	const char *str;
+
+	memset(opts, 0, sizeof(*opts));
+
+	/* Set defaults */
+	opts->defer = DEFAULT_DEFER_TIMEOUT;
+	opts->master = -1;
+	opts->sec_level = BT_IO_SEC_MEDIUM;
+	opts->mode = L2CAP_MODE_BASIC;
+
+	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, const 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, const bdaddr_t *));
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			opts->defer = 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;
+		case BT_IO_OPT_MASTER:
+			opts->master = va_arg(args, gboolean);
+			break;
+		case BT_IO_OPT_MODE:
+			opts->mode = va_arg(args, int);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
+				socklen_t len, GError **err)
+{
+	socklen_t olen;
+
+	memset(src, 0, len);
+	olen = len;
+	if (getsockname(sock, src, &olen) < 0) {
+		ERROR_FAILED(err, "getsockname", errno);
+		return FALSE;
+	}
+
+	memset(dst, 0, len);
+	olen = len;
+	if (getpeername(sock, dst, &olen) < 0) {
+		ERROR_FAILED(err, "getpeername", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct l2cap_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_l2 src, dst;
+	struct l2cap_options l2o;
+	int flags;
+	uint8_t dev_class[3];
+	uint16_t handle;
+	socklen_t len;
+
+	len = sizeof(l2o);
+	memset(&l2o, 0, len);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		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) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_L2CAP,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_PSM:
+			*(va_arg(args, uint16_t *)) = src.l2_psm ?
+						src.l2_psm : dst.l2_psm;
+			break;
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = l2o.omtu;
+			break;
+		case BT_IO_OPT_IMTU:
+			*(va_arg(args, uint16_t *)) = l2o.imtu;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct rfcomm_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_rc src, dst;
+	int flags;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		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) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_RFCOMM,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel ?
+					src.rc_channel : dst.rc_channel;
+			break;
+		case BT_IO_OPT_SOURCE_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel;
+			break;
+		case BT_IO_OPT_DEST_CHANNEL:
+			*(va_arg(args, uint8_t *)) = dst.rc_channel;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct sco_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_sco src, dst;
+	struct sco_options sco_opt;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		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_MTU:
+		case BT_IO_OPT_IMTU:
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
+						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, err, opt1, args);
+	case BT_IO_RFCOMM:
+		return rfcomm_get(sock, err, opt1, args);
+	case BT_IO_SCO:
+		return sco_get(sock, err, opt1, args);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
+					GDestroyNotify destroy, GError **err)
+{
+	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_FAILED(err, "poll", errno);
+		return FALSE;
+	}
+
+	if (!(pfd.revents & POLLOUT)) {
+		int ret;
+		ret = read(sock, &c, 1);
+	}
+
+	accept_add(io, connect, user_data, destroy);
+
+	return TRUE;
+}
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+	struct set_opts opts;
+	int sock;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, 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:
+		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
+						opts.mode, opts.master, err);
+	case BT_IO_RFCOMM:
+		return rfcomm_set(sock, opts.sec_level, opts.master, err);
+	case BT_IO_SCO:
+		return sco_set(sock, opts.mtu, err);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = get_valist(io, type, err, opt1, args);
+	va_end(args);
+
+	return ret;
+}
+
+static GIOChannel *create_io(BtIOType type, gboolean server,
+					struct set_opts *opts, GError **err)
+{
+	int sock;
+	GIOChannel *io;
+
+	switch (type) {
+	case BT_IO_L2RAW:
+		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
+			goto failed;
+		break;
+	case BT_IO_L2CAP:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
+						opts->mode, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_RFCOMM:
+		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
+			return NULL;
+		}
+		if (rfcomm_bind(sock, &opts->src,
+					server ? opts->channel : 0, err) < 0)
+			goto failed;
+		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_SCO:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
+			return NULL;
+		}
+		if (sco_bind(sock, &opts->src, err) < 0)
+			goto failed;
+		if (!sco_set(sock, opts->mtu, err))
+			goto failed;
+		break;
+	default:
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	io = g_io_channel_unix_new(sock);
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+	return io;
+
+failed:
+	close(sock);
+
+	return NULL;
+}
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				void *user_data, GDestroyNotify destroy,
+				GError **gerr, BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int err, sock;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, gerr, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, FALSE, &opts, gerr);
+	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;
+	default:
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+						"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	if (err < 0) {
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"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, void *user_data,
+				GDestroyNotify destroy, GError **err,
+				BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int sock;
+	gboolean ret;
+
+	if (type == BT_IO_L2RAW) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Server L2CAP RAW sockets not supported");
+		return NULL;
+	}
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, TRUE, &opts, err);
+	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_FAILED(err, "listen", errno);
+		g_io_channel_unref(io);
+		return NULL;
+	}
+
+	server_add(io, connect, confirm, user_data, destroy);
+
+	return io;
+}
+
+GQuark bt_io_error_quark(void)
+{
+	return g_quark_from_static_string("bt-io-error-quark");
+}
diff --git a/btio/btio.h b/btio/btio.h
new file mode 100644
index 0000000..e9dcc9f
--- /dev/null
+++ b/btio/btio.h
@@ -0,0 +1,97 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  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_ERROR_DISCONNECTED,
+	BT_IO_ERROR_CONNECT_FAILED,
+	BT_IO_ERROR_FAILED,
+	BT_IO_ERROR_INVALID_ARGS,
+} BtIOError;
+
+#define BT_IO_ERROR bt_io_error_quark()
+
+GQuark bt_io_error_quark(void);
+
+typedef enum {
+	BT_IO_L2RAW,
+	BT_IO_L2CAP,
+	BT_IO_RFCOMM,
+	BT_IO_SCO,
+} BtIOType;
+
+typedef enum {
+	BT_IO_OPT_INVALID = 0,
+	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_SEC_LEVEL,
+	BT_IO_OPT_CHANNEL,
+	BT_IO_OPT_SOURCE_CHANNEL,
+	BT_IO_OPT_DEST_CHANNEL,
+	BT_IO_OPT_PSM,
+	BT_IO_OPT_MTU,
+	BT_IO_OPT_OMTU,
+	BT_IO_OPT_IMTU,
+	BT_IO_OPT_MASTER,
+	BT_IO_OPT_HANDLE,
+	BT_IO_OPT_CLASS,
+	BT_IO_OPT_MODE,
+} BtIOOption;
+
+typedef enum {
+	BT_IO_SEC_SDP = 0,
+	BT_IO_SEC_LOW,
+	BT_IO_SEC_MEDIUM,
+	BT_IO_SEC_HIGH,
+} BtIOSecLevel;
+
+typedef void (*BtIOConfirm)(GIOChannel *io, void *user_data);
+
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, void *user_data);
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
+					GDestroyNotify destroy, GError **err);
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				void *user_data, GDestroyNotify destroy,
+				GError **err, BtIOOption opt1, ...);
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+				BtIOConfirm confirm, void *user_data,
+				GDestroyNotify destroy, GError **err,
+				BtIOOption opt1, ...);
+
+#endif
diff --git a/src/btio.c b/src/btio.c
deleted file mode 100644
index 42a3bcd..0000000
--- a/src/btio.c
+++ /dev/null
@@ -1,1299 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
- *  Copyright (C) 2009-2010  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 "btio.h"
-
-#define ERROR_FAILED(gerr, str, err) \
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
-				str ": %s (%d)", strerror(err), err)
-
-#define DEFAULT_DEFER_TIMEOUT 30
-
-struct set_opts {
-	bdaddr_t src;
-	bdaddr_t dst;
-	int defer;
-	int sec_level;
-	uint8_t channel;
-	uint16_t psm;
-	uint16_t mtu;
-	uint16_t imtu;
-	uint16_t omtu;
-	int master;
-	uint8_t mode;
-};
-
-struct connect {
-	BtIOConnect connect;
-	void *user_data;
-	GDestroyNotify destroy;
-};
-
-struct accept {
-	BtIOConnect connect;
-	void *user_data;
-	GDestroyNotify destroy;
-};
-
-struct server {
-	BtIOConnect connect;
-	BtIOConfirm confirm;
-	void *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 check_nval(GIOChannel *io)
-{
-	struct pollfd fds;
-
-	memset(&fds, 0, sizeof(fds));
-	fds.fd = g_io_channel_unix_get_fd(io);
-	fds.events = POLLNVAL;
-
-	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
-		return TRUE;
-
-	return FALSE;
-}
-
-static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
-{
-	struct accept *accept = user_data;
-	GError *err = NULL;
-
-	/* If the user aborted this accept attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
-				"HUP or ERR on socket");
-
-	accept->connect(io, err, accept->user_data);
-
-	g_clear_error(&err);
-
-	return FALSE;
-}
-
-static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
-{
-	struct connect *conn = user_data;
-	GError *gerr = NULL;
-
-	/* If the user aborted this connect attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & G_IO_OUT) {
-		int err = 0, sock = g_io_channel_unix_get_fd(io);
-		socklen_t len = sizeof(err);
-
-		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
-			err = errno;
-
-		if (err)
-			g_set_error(&gerr, BT_IO_ERROR,
-					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
-					strerror(err), err);
-	} else if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"HUP or ERR on socket");
-
-	conn->connect(io, gerr, conn->user_data);
-
-	if (gerr)
-		g_error_free(gerr);
-
-	return FALSE;
-}
-
-static gboolean server_cb(GIOChannel *io, GIOCondition cond,
-							void *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) || check_nval(io))
-		return FALSE;
-
-	srv_sock = g_io_channel_unix_get_fd(io);
-
-	cli_sock = accept(srv_sock, NULL, NULL);
-	if (cli_sock < 0)
-		return TRUE;
-
-	cli_io = g_io_channel_unix_new(cli_sock);
-
-	g_io_channel_set_close_on_unref(cli_io, TRUE);
-	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
-
-	if (server->confirm)
-		server->confirm(cli_io, server->user_data);
-	else
-		server->connect(cli_io, NULL, server->user_data);
-
-	g_io_channel_unref(cli_io);
-
-	return TRUE;
-}
-
-static void server_add(GIOChannel *io, BtIOConnect connect,
-				BtIOConfirm confirm, void *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,
-				void *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, void *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, const bdaddr_t *src, uint16_t psm, GError **err)
-{
-	struct sockaddr_l2 addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.l2_family = AF_BLUETOOTH;
-	bacpy(&addr.l2_bdaddr, src);
-	addr.l2_psm = htobs(psm);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "l2cap_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int l2cap_connect(int sock, const 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_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & L2CAP_LM_MASTER)
-			return 0;
-		flags |= L2CAP_LM_MASTER;
-	} else {
-		if (!(flags & L2CAP_LM_MASTER))
-			return 0;
-		flags &= ~L2CAP_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & RFCOMM_LM_MASTER)
-			return 0;
-		flags |= RFCOMM_LM_MASTER;
-	} else {
-		if (!(flags & RFCOMM_LM_MASTER))
-			return 0;
-		flags &= ~RFCOMM_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int l2cap_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		L2CAP_LM_AUTH,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		RFCOMM_LM_AUTH,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
-{
-	struct bt_security sec;
-	int ret;
-
-	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Valid security level range is %d-%d",
-				BT_SECURITY_LOW, BT_SECURITY_HIGH);
-		return FALSE;
-	}
-
-	memset(&sec, 0, sizeof(sec));
-	sec.level = level;
-
-	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
-							sizeof(sec)) == 0)
-		return TRUE;
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_set_lm(sock, level);
-	else
-		ret = rfcomm_set_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "setsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & L2CAP_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & L2CAP_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & L2CAP_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static int rfcomm_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & RFCOMM_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & RFCOMM_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & RFCOMM_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static gboolean get_sec_level(int sock, BtIOType type, int *level,
-								GError **err)
-{
-	struct bt_security sec;
-	socklen_t len;
-	int ret;
-
-	memset(&sec, 0, sizeof(sec));
-	len = sizeof(sec);
-	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
-		*level = sec.level;
-		return TRUE;
-	}
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_get_lm(sock, level);
-	else
-		ret = rfcomm_get_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "getsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
-					uint8_t mode, int master, GError **err)
-{
-	if (imtu || omtu || mode) {
-		struct l2cap_options l2o;
-		socklen_t len;
-
-		memset(&l2o, 0, sizeof(l2o));
-		len = sizeof(l2o);
-		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-								&len) < 0) {
-			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-
-		if (imtu)
-			l2o.imtu = imtu;
-		if (omtu)
-			l2o.omtu = omtu;
-		if (mode)
-			l2o.mode = mode;
-
-		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-							sizeof(l2o)) < 0) {
-			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-	}
-
-	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "l2cap_set_master", errno);
-		return FALSE;
-	}
-
-	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
-		return FALSE;
-
-	return TRUE;
-}
-
-static int rfcomm_bind(int sock,
-		const bdaddr_t *src, uint8_t channel, GError **err)
-{
-	struct sockaddr_rc addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.rc_family = AF_BLUETOOTH;
-	bacpy(&addr.rc_bdaddr, src);
-	addr.rc_channel = channel;
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "rfcomm_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
-{
-	int err;
-	struct sockaddr_rc addr;
-
-	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 gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
-{
-	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
-		return FALSE;
-
-	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "rfcomm_set_master", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int sco_bind(int sock, const bdaddr_t *src, GError **err)
-{
-	struct sockaddr_sco addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sco_family = AF_BLUETOOTH;
-	bacpy(&addr.sco_bdaddr, src);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "sco_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int sco_connect(int sock, const 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 gboolean sco_set(int sock, uint16_t mtu, GError **err)
-{
-	struct sco_options sco_opt;
-	socklen_t len;
-
-	if (!mtu)
-		return TRUE;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	sco_opt.mtu = mtu;
-	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
-						sizeof(sco_opt)) < 0) {
-		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean parse_set_opts(struct set_opts *opts, GError **err,
-						BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	const char *str;
-
-	memset(opts, 0, sizeof(*opts));
-
-	/* Set defaults */
-	opts->defer = DEFAULT_DEFER_TIMEOUT;
-	opts->master = -1;
-	opts->sec_level = BT_IO_SEC_MEDIUM;
-	opts->mode = L2CAP_MODE_BASIC;
-
-	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, const 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, const bdaddr_t *));
-			break;
-		case BT_IO_OPT_DEFER_TIMEOUT:
-			opts->defer = 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;
-		case BT_IO_OPT_MASTER:
-			opts->master = va_arg(args, gboolean);
-			break;
-		case BT_IO_OPT_MODE:
-			opts->mode = va_arg(args, int);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
-				socklen_t len, GError **err)
-{
-	socklen_t olen;
-
-	memset(src, 0, len);
-	olen = len;
-	if (getsockname(sock, src, &olen) < 0) {
-		ERROR_FAILED(err, "getsockname", errno);
-		return FALSE;
-	}
-
-	memset(dst, 0, len);
-	olen = len;
-	if (getpeername(sock, dst, &olen) < 0) {
-		ERROR_FAILED(err, "getpeername", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct l2cap_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_l2 src, dst;
-	struct l2cap_options l2o;
-	int flags;
-	uint8_t dev_class[3];
-	uint16_t handle;
-	socklen_t len;
-
-	len = sizeof(l2o);
-	memset(&l2o, 0, len);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		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) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_L2CAP,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_PSM:
-			*(va_arg(args, uint16_t *)) = src.l2_psm ?
-						src.l2_psm : dst.l2_psm;
-			break;
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = l2o.omtu;
-			break;
-		case BT_IO_OPT_IMTU:
-			*(va_arg(args, uint16_t *)) = l2o.imtu;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct rfcomm_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_rc src, dst;
-	int flags;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		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) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_RFCOMM,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel ?
-					src.rc_channel : dst.rc_channel;
-			break;
-		case BT_IO_OPT_SOURCE_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel;
-			break;
-		case BT_IO_OPT_DEST_CHANNEL:
-			*(va_arg(args, uint8_t *)) = dst.rc_channel;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct sco_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_sco src, dst;
-	struct sco_options sco_opt;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		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_MTU:
-		case BT_IO_OPT_IMTU:
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
-						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, err, opt1, args);
-	case BT_IO_RFCOMM:
-		return rfcomm_get(sock, err, opt1, args);
-	case BT_IO_SCO:
-		return sco_get(sock, err, opt1, args);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
-					GDestroyNotify destroy, GError **err)
-{
-	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_FAILED(err, "poll", errno);
-		return FALSE;
-	}
-
-	if (!(pfd.revents & POLLOUT)) {
-		int ret;
-		ret = read(sock, &c, 1);
-	}
-
-	accept_add(io, connect, user_data, destroy);
-
-	return TRUE;
-}
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-	struct set_opts opts;
-	int sock;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, 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:
-		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
-						opts.mode, opts.master, err);
-	case BT_IO_RFCOMM:
-		return rfcomm_set(sock, opts.sec_level, opts.master, err);
-	case BT_IO_SCO:
-		return sco_set(sock, opts.mtu, err);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = get_valist(io, type, err, opt1, args);
-	va_end(args);
-
-	return ret;
-}
-
-static GIOChannel *create_io(BtIOType type, gboolean server,
-					struct set_opts *opts, GError **err)
-{
-	int sock;
-	GIOChannel *io;
-
-	switch (type) {
-	case BT_IO_L2RAW:
-		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
-			goto failed;
-		break;
-	case BT_IO_L2CAP:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
-						opts->mode, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_RFCOMM:
-		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
-			return NULL;
-		}
-		if (rfcomm_bind(sock, &opts->src,
-					server ? opts->channel : 0, err) < 0)
-			goto failed;
-		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_SCO:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
-			return NULL;
-		}
-		if (sco_bind(sock, &opts->src, err) < 0)
-			goto failed;
-		if (!sco_set(sock, opts->mtu, err))
-			goto failed;
-		break;
-	default:
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	io = g_io_channel_unix_new(sock);
-
-	g_io_channel_set_close_on_unref(io, TRUE);
-	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
-
-	return io;
-
-failed:
-	close(sock);
-
-	return NULL;
-}
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
-				GError **gerr, BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int err, sock;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, gerr, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, FALSE, &opts, gerr);
-	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;
-	default:
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-						"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	if (err < 0) {
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"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, void *user_data,
-				GDestroyNotify destroy, GError **err,
-				BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int sock;
-	gboolean ret;
-
-	if (type == BT_IO_L2RAW) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Server L2CAP RAW sockets not supported");
-		return NULL;
-	}
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, TRUE, &opts, err);
-	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_FAILED(err, "listen", errno);
-		g_io_channel_unref(io);
-		return NULL;
-	}
-
-	server_add(io, connect, confirm, user_data, destroy);
-
-	return io;
-}
-
-GQuark bt_io_error_quark(void)
-{
-	return g_quark_from_static_string("bt-io-error-quark");
-}
diff --git a/src/btio.h b/src/btio.h
deleted file mode 100644
index e9dcc9f..0000000
--- a/src/btio.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
- *  Copyright (C) 2009-2010  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_ERROR_DISCONNECTED,
-	BT_IO_ERROR_CONNECT_FAILED,
-	BT_IO_ERROR_FAILED,
-	BT_IO_ERROR_INVALID_ARGS,
-} BtIOError;
-
-#define BT_IO_ERROR bt_io_error_quark()
-
-GQuark bt_io_error_quark(void);
-
-typedef enum {
-	BT_IO_L2RAW,
-	BT_IO_L2CAP,
-	BT_IO_RFCOMM,
-	BT_IO_SCO,
-} BtIOType;
-
-typedef enum {
-	BT_IO_OPT_INVALID = 0,
-	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_SEC_LEVEL,
-	BT_IO_OPT_CHANNEL,
-	BT_IO_OPT_SOURCE_CHANNEL,
-	BT_IO_OPT_DEST_CHANNEL,
-	BT_IO_OPT_PSM,
-	BT_IO_OPT_MTU,
-	BT_IO_OPT_OMTU,
-	BT_IO_OPT_IMTU,
-	BT_IO_OPT_MASTER,
-	BT_IO_OPT_HANDLE,
-	BT_IO_OPT_CLASS,
-	BT_IO_OPT_MODE,
-} BtIOOption;
-
-typedef enum {
-	BT_IO_SEC_SDP = 0,
-	BT_IO_SEC_LOW,
-	BT_IO_SEC_MEDIUM,
-	BT_IO_SEC_HIGH,
-} BtIOSecLevel;
-
-typedef void (*BtIOConfirm)(GIOChannel *io, void *user_data);
-
-typedef void (*BtIOConnect)(GIOChannel *io, GError *err, void *user_data);
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
-					GDestroyNotify destroy, GError **err);
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
-				GError **err, BtIOOption opt1, ...);
-
-GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
-				GDestroyNotify destroy, GError **err,
-				BtIOOption opt1, ...);
-
-#endif
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 3/4] btio: Remove unused bt_io_set function
From: Zhenhua Zhang @ 2010-09-09  9:41 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284025283-3773-1-git-send-email-zhenhua.zhang@intel.com>

It is not used by either obex or bluez.
---
 btio/btio.c |   33 ---------------------------------
 btio/btio.h |    3 ---
 2 files changed, 0 insertions(+), 36 deletions(-)

diff --git a/btio/btio.c b/btio/btio.c
index 42a3bcd..d4969e5 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -1074,39 +1074,6 @@ gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
 	return TRUE;
 }
 
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-	struct set_opts opts;
-	int sock;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, 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:
-		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
-						opts.mode, opts.master, err);
-	case BT_IO_RFCOMM:
-		return rfcomm_set(sock, opts.sec_level, opts.master, err);
-	case BT_IO_SCO:
-		return sco_set(sock, opts.mtu, err);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
 gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
 							BtIOOption opt1, ...)
 {
diff --git a/btio/btio.h b/btio/btio.h
index e9dcc9f..3c5db2a 100644
--- a/btio/btio.h
+++ b/btio/btio.h
@@ -79,9 +79,6 @@ typedef void (*BtIOConnect)(GIOChannel *io, GError *err, void *user_data);
 gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
 					GDestroyNotify destroy, GError **err);
 
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
 gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
 						BtIOOption opt1, ...);
 
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 4/4] btio: Replace void * with gpointer
From: Zhenhua Zhang @ 2010-09-09  9:41 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284025283-3773-1-git-send-email-zhenhua.zhang@intel.com>

TO make it consistent with bluez btio.c.
---
 btio/btio.c |   24 ++++++++++++------------
 btio/btio.h |   10 +++++-----
 2 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/btio/btio.c b/btio/btio.c
index d4969e5..41b698f 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -62,20 +62,20 @@ struct set_opts {
 
 struct connect {
 	BtIOConnect connect;
-	void *user_data;
+	gpointer user_data;
 	GDestroyNotify destroy;
 };
 
 struct accept {
 	BtIOConnect connect;
-	void *user_data;
+	gpointer user_data;
 	GDestroyNotify destroy;
 };
 
 struct server {
 	BtIOConnect connect;
 	BtIOConfirm confirm;
-	void *user_data;
+	gpointer user_data;
 	GDestroyNotify destroy;
 };
 
@@ -115,7 +115,7 @@ static gboolean check_nval(GIOChannel *io)
 }
 
 static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
+							gpointer user_data)
 {
 	struct accept *accept = user_data;
 	GError *err = NULL;
@@ -136,7 +136,7 @@ static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
 }
 
 static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
+							gpointer user_data)
 {
 	struct connect *conn = user_data;
 	GError *gerr = NULL;
@@ -169,7 +169,7 @@ static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
 }
 
 static gboolean server_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
+							gpointer user_data)
 {
 	struct server *server = user_data;
 	int srv_sock, cli_sock;
@@ -201,7 +201,7 @@ static gboolean server_cb(GIOChannel *io, GIOCondition cond,
 }
 
 static void server_add(GIOChannel *io, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
+				BtIOConfirm confirm, gpointer user_data,
 				GDestroyNotify destroy)
 {
 	struct server *server;
@@ -219,7 +219,7 @@ static void server_add(GIOChannel *io, BtIOConnect connect,
 }
 
 static void connect_add(GIOChannel *io, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy)
+				gpointer user_data, GDestroyNotify destroy)
 {
 	struct connect *conn;
 	GIOCondition cond;
@@ -234,7 +234,7 @@ static void connect_add(GIOChannel *io, BtIOConnect connect,
 					(GDestroyNotify) connect_remove);
 }
 
-static void accept_add(GIOChannel *io, BtIOConnect connect, void *user_data,
+static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data,
 							GDestroyNotify destroy)
 {
 	struct accept *accept;
@@ -1046,7 +1046,7 @@ static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
 	return FALSE;
 }
 
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
 					GDestroyNotify destroy, GError **err)
 {
 	int sock;
@@ -1162,7 +1162,7 @@ failed:
 }
 
 GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
+				gpointer user_data, GDestroyNotify destroy,
 				GError **gerr, BtIOOption opt1, ...)
 {
 	GIOChannel *io;
@@ -1216,7 +1216,7 @@ GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
 }
 
 GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
+				BtIOConfirm confirm, gpointer user_data,
 				GDestroyNotify destroy, GError **err,
 				BtIOOption opt1, ...)
 {
diff --git a/btio/btio.h b/btio/btio.h
index 3c5db2a..5f2911a 100644
--- a/btio/btio.h
+++ b/btio/btio.h
@@ -72,22 +72,22 @@ typedef enum {
 	BT_IO_SEC_HIGH,
 } BtIOSecLevel;
 
-typedef void (*BtIOConfirm)(GIOChannel *io, void *user_data);
+typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
 
-typedef void (*BtIOConnect)(GIOChannel *io, GError *err, void *user_data);
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
 
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
 					GDestroyNotify destroy, GError **err);
 
 gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
 						BtIOOption opt1, ...);
 
 GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
+				gpointer user_data, GDestroyNotify destroy,
 				GError **err, BtIOOption opt1, ...);
 
 GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
+				BtIOConfirm confirm, gpointer user_data,
 				GDestroyNotify destroy, GError **err,
 				BtIOOption opt1, ...);
 
-- 
1.7.0.4


^ permalink raw reply related

* bluez: isolate btio.[ch] as a library and do cleanup
From: Zhenhua Zhang @ 2010-09-09  9:43 UTC (permalink / raw)
  To: linux-bluetooth

Hi,

These patches move bluez/src/btio.[ch] out from source directory and make it identical with obex's btio.[ch]. So we can freely copy them among bluez, obex and ofono.

Regards,
Zhenhua


^ permalink raw reply

* [PATCH 1/4] btio: Remove blank line at EOF
From: Zhenhua Zhang @ 2010-09-09  9:43 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284025388-3831-1-git-send-email-zhenhua.zhang@intel.com>

---
 src/btio.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/src/btio.c b/src/btio.c
index 93079ae..8b273ca 100644
--- a/src/btio.c
+++ b/src/btio.c
@@ -1297,4 +1297,3 @@ GQuark bt_io_error_quark(void)
 {
 	return g_quark_from_static_string("bt-io-error-quark");
 }
-
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 2/4] btio: Seperate btio.[ch] into btio directory
From: Zhenhua Zhang @ 2010-09-09  9:43 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284025388-3831-1-git-send-email-zhenhua.zhang@intel.com>

Seperate btio.[ch] from src directory to btio sub-folder.
---
 Makefile.am    |    8 +-
 Makefile.tools |    2 +-
 btio/btio.c    | 1299 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 btio/btio.h    |   94 ++++
 src/btio.c     | 1299 --------------------------------------------------------
 src/btio.h     |   94 ----
 6 files changed, 1399 insertions(+), 1397 deletions(-)
 create mode 100644 btio/btio.c
 create mode 100644 btio/btio.h
 delete mode 100644 src/btio.c
 delete mode 100644 src/btio.h

diff --git a/Makefile.am b/Makefile.am
index f74872d..4dcb56a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -101,6 +101,8 @@ endif
 
 gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/object.c gdbus/watch.c
 
+btio_sources = btio/btio.h btio/btio.c
+
 builtin_modules =
 builtin_sources =
 builtin_nodist =
@@ -215,14 +217,14 @@ endif
 sbin_PROGRAMS += src/bluetoothd
 
 src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
-			$(attrib_sources) \
+			$(attrib_sources) $(btio_sources) \
 			$(mcap_sources) \
 			src/main.c src/log.h src/log.c \
 			src/security.c src/rfkill.c src/hcid.h src/sdpd.h \
 			src/sdpd-server.c src/sdpd-request.c \
 			src/sdpd-service.c src/sdpd-database.c \
 			src/attrib-server.h src/attrib-server.c \
-			src/sdp-xml.h src/sdp-xml.c src/btio.h src/btio.c \
+			src/sdp-xml.h src/sdp-xml.c \
 			src/textfile.h src/textfile.c \
 			src/glib-helper.h src/glib-helper.c \
 			src/oui.h src/oui.c src/uinput.h src/ppoll.h \
@@ -356,7 +358,7 @@ AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ \
 
 INCLUDES = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
 			-I$(srcdir)/audio -I$(srcdir)/sbc -I$(srcdir)/gdbus \
-			-I$(srcdir)/attrib
+			-I$(srcdir)/attrib -I$(srcdir)/btio
 
 if MCAP
 	INCLUDES += -I$(builddir)/health
diff --git a/Makefile.tools b/Makefile.tools
index 1c46542..14ecf31 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -170,7 +170,7 @@ test_bdaddr_LDADD = lib/libbluetooth.la
 
 test_agent_LDADD = @DBUS_LIBS@
 
-test_btiotest_SOURCES = test/btiotest.c src/btio.h src/btio.c
+test_btiotest_SOURCES = test/btiotest.c btio/btio.h btio/btio.c
 test_btiotest_LDADD = @GLIB_LIBS@ lib/libbluetooth.la
 
 test_test_textfile_SOURCES = test/test-textfile.c src/textfile.h src/textfile.c
diff --git a/btio/btio.c b/btio/btio.c
new file mode 100644
index 0000000..8b273ca
--- /dev/null
+++ b/btio/btio.c
@@ -0,0 +1,1299 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  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 "btio.h"
+
+#define ERROR_FAILED(gerr, str, err) \
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
+				str ": %s (%d)", strerror(err), err)
+
+#define DEFAULT_DEFER_TIMEOUT 30
+
+struct set_opts {
+	bdaddr_t src;
+	bdaddr_t dst;
+	int defer;
+	int sec_level;
+	uint8_t channel;
+	uint16_t psm;
+	uint16_t mtu;
+	uint16_t imtu;
+	uint16_t omtu;
+	int master;
+	uint8_t mode;
+};
+
+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 check_nval(GIOChannel *io)
+{
+	struct pollfd fds;
+
+	memset(&fds, 0, sizeof(fds));
+	fds.fd = g_io_channel_unix_get_fd(io);
+	fds.events = POLLNVAL;
+
+	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct accept *accept = user_data;
+	GError *err = NULL;
+
+	/* If the user aborted this accept attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
+				"HUP or ERR on socket");
+
+	accept->connect(io, err, accept->user_data);
+
+	g_clear_error(&err);
+
+	return FALSE;
+}
+
+static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct connect *conn = user_data;
+	GError *gerr = NULL;
+
+	/* If the user aborted this connect attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & G_IO_OUT) {
+		int err = 0, sock = g_io_channel_unix_get_fd(io);
+		socklen_t len = sizeof(err);
+
+		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
+			err = errno;
+
+		if (err)
+			g_set_error(&gerr, BT_IO_ERROR,
+					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
+					strerror(err), err);
+	} else if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"HUP or ERR on socket");
+
+	conn->connect(io, gerr, conn->user_data);
+
+	if (gerr)
+		g_error_free(gerr);
+
+	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) || check_nval(io))
+		return FALSE;
+
+	srv_sock = g_io_channel_unix_get_fd(io);
+
+	cli_sock = accept(srv_sock, NULL, NULL);
+	if (cli_sock < 0)
+		return TRUE;
+
+	cli_io = g_io_channel_unix_new(cli_sock);
+
+	g_io_channel_set_close_on_unref(cli_io, TRUE);
+	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
+
+	if (server->confirm)
+		server->confirm(cli_io, server->user_data);
+	else
+		server->connect(cli_io, NULL, 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, const bdaddr_t *src, uint16_t psm, GError **err)
+{
+	struct sockaddr_l2 addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+	addr.l2_psm = htobs(psm);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "l2cap_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int l2cap_connect(int sock, const 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_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & L2CAP_LM_MASTER)
+			return 0;
+		flags |= L2CAP_LM_MASTER;
+	} else {
+		if (!(flags & L2CAP_LM_MASTER))
+			return 0;
+		flags &= ~L2CAP_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & RFCOMM_LM_MASTER)
+			return 0;
+		flags |= RFCOMM_LM_MASTER;
+	} else {
+		if (!(flags & RFCOMM_LM_MASTER))
+			return 0;
+		flags &= ~RFCOMM_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int l2cap_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		L2CAP_LM_AUTH,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		RFCOMM_LM_AUTH,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
+{
+	struct bt_security sec;
+	int ret;
+
+	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Valid security level range is %d-%d",
+				BT_SECURITY_LOW, BT_SECURITY_HIGH);
+		return FALSE;
+	}
+
+	memset(&sec, 0, sizeof(sec));
+	sec.level = level;
+
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
+							sizeof(sec)) == 0)
+		return TRUE;
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_set_lm(sock, level);
+	else
+		ret = rfcomm_set_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "setsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & L2CAP_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & L2CAP_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & L2CAP_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static int rfcomm_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & RFCOMM_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & RFCOMM_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & RFCOMM_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static gboolean get_sec_level(int sock, BtIOType type, int *level,
+								GError **err)
+{
+	struct bt_security sec;
+	socklen_t len;
+	int ret;
+
+	memset(&sec, 0, sizeof(sec));
+	len = sizeof(sec);
+	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+		*level = sec.level;
+		return TRUE;
+	}
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_get_lm(sock, level);
+	else
+		ret = rfcomm_get_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "getsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
+					uint8_t mode, int master, GError **err)
+{
+	if (imtu || omtu || mode) {
+		struct l2cap_options l2o;
+		socklen_t len;
+
+		memset(&l2o, 0, sizeof(l2o));
+		len = sizeof(l2o);
+		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+								&len) < 0) {
+			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+
+		if (imtu)
+			l2o.imtu = imtu;
+		if (omtu)
+			l2o.omtu = omtu;
+		if (mode)
+			l2o.mode = mode;
+
+		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+							sizeof(l2o)) < 0) {
+			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+	}
+
+	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "l2cap_set_master", errno);
+		return FALSE;
+	}
+
+	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
+		return FALSE;
+
+	return TRUE;
+}
+
+static int rfcomm_bind(int sock,
+		const bdaddr_t *src, uint8_t channel, GError **err)
+{
+	struct sockaddr_rc addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, src);
+	addr.rc_channel = channel;
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "rfcomm_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
+{
+	int err;
+	struct sockaddr_rc addr;
+
+	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 gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
+{
+	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
+		return FALSE;
+
+	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "rfcomm_set_master", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int sco_bind(int sock, const bdaddr_t *src, GError **err)
+{
+	struct sockaddr_sco addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, src);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "sco_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sco_connect(int sock, const 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 gboolean sco_set(int sock, uint16_t mtu, GError **err)
+{
+	struct sco_options sco_opt;
+	socklen_t len;
+
+	if (!mtu)
+		return TRUE;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	sco_opt.mtu = mtu;
+	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
+						sizeof(sco_opt)) < 0) {
+		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean parse_set_opts(struct set_opts *opts, GError **err,
+						BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	const char *str;
+
+	memset(opts, 0, sizeof(*opts));
+
+	/* Set defaults */
+	opts->defer = DEFAULT_DEFER_TIMEOUT;
+	opts->master = -1;
+	opts->sec_level = BT_IO_SEC_MEDIUM;
+	opts->mode = L2CAP_MODE_BASIC;
+
+	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, const 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, const bdaddr_t *));
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			opts->defer = 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;
+		case BT_IO_OPT_MASTER:
+			opts->master = va_arg(args, gboolean);
+			break;
+		case BT_IO_OPT_MODE:
+			opts->mode = va_arg(args, int);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
+				socklen_t len, GError **err)
+{
+	socklen_t olen;
+
+	memset(src, 0, len);
+	olen = len;
+	if (getsockname(sock, src, &olen) < 0) {
+		ERROR_FAILED(err, "getsockname", errno);
+		return FALSE;
+	}
+
+	memset(dst, 0, len);
+	olen = len;
+	if (getpeername(sock, dst, &olen) < 0) {
+		ERROR_FAILED(err, "getpeername", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct l2cap_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_l2 src, dst;
+	struct l2cap_options l2o;
+	int flags;
+	uint8_t dev_class[3];
+	uint16_t handle;
+	socklen_t len;
+
+	len = sizeof(l2o);
+	memset(&l2o, 0, len);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		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) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_L2CAP,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_PSM:
+			*(va_arg(args, uint16_t *)) = src.l2_psm ?
+						src.l2_psm : dst.l2_psm;
+			break;
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = l2o.omtu;
+			break;
+		case BT_IO_OPT_IMTU:
+			*(va_arg(args, uint16_t *)) = l2o.imtu;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct rfcomm_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_rc src, dst;
+	int flags;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		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) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_RFCOMM,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel ?
+					src.rc_channel : dst.rc_channel;
+			break;
+		case BT_IO_OPT_SOURCE_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel;
+			break;
+		case BT_IO_OPT_DEST_CHANNEL:
+			*(va_arg(args, uint8_t *)) = dst.rc_channel;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct sco_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_sco src, dst;
+	struct sco_options sco_opt;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		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_MTU:
+		case BT_IO_OPT_IMTU:
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
+						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, err, opt1, args);
+	case BT_IO_RFCOMM:
+		return rfcomm_get(sock, err, opt1, args);
+	case BT_IO_SCO:
+		return sco_get(sock, err, opt1, args);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+					GDestroyNotify destroy, GError **err)
+{
+	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_FAILED(err, "poll", errno);
+		return FALSE;
+	}
+
+	if (!(pfd.revents & POLLOUT)) {
+		int ret;
+		ret = read(sock, &c, 1);
+	}
+
+	accept_add(io, connect, user_data, destroy);
+
+	return TRUE;
+}
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+	struct set_opts opts;
+	int sock;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, 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:
+		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
+						opts.mode, opts.master, err);
+	case BT_IO_RFCOMM:
+		return rfcomm_set(sock, opts.sec_level, opts.master, err);
+	case BT_IO_SCO:
+		return sco_set(sock, opts.mtu, err);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = get_valist(io, type, err, opt1, args);
+	va_end(args);
+
+	return ret;
+}
+
+static GIOChannel *create_io(BtIOType type, gboolean server,
+					struct set_opts *opts, GError **err)
+{
+	int sock;
+	GIOChannel *io;
+
+	switch (type) {
+	case BT_IO_L2RAW:
+		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
+			goto failed;
+		break;
+	case BT_IO_L2CAP:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
+						opts->mode, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_RFCOMM:
+		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
+			return NULL;
+		}
+		if (rfcomm_bind(sock, &opts->src,
+					server ? opts->channel : 0, err) < 0)
+			goto failed;
+		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_SCO:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
+			return NULL;
+		}
+		if (sco_bind(sock, &opts->src, err) < 0)
+			goto failed;
+		if (!sco_set(sock, opts->mtu, err))
+			goto failed;
+		break;
+	default:
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	io = g_io_channel_unix_new(sock);
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+	return io;
+
+failed:
+	close(sock);
+
+	return NULL;
+}
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				gpointer user_data, GDestroyNotify destroy,
+				GError **gerr, BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int err, sock;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, gerr, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, FALSE, &opts, gerr);
+	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;
+	default:
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+						"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	if (err < 0) {
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"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, GError **err,
+				BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int sock;
+	gboolean ret;
+
+	if (type == BT_IO_L2RAW) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Server L2CAP RAW sockets not supported");
+		return NULL;
+	}
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, TRUE, &opts, err);
+	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_FAILED(err, "listen", errno);
+		g_io_channel_unref(io);
+		return NULL;
+	}
+
+	server_add(io, connect, confirm, user_data, destroy);
+
+	return io;
+}
+
+GQuark bt_io_error_quark(void)
+{
+	return g_quark_from_static_string("bt-io-error-quark");
+}
diff --git a/btio/btio.h b/btio/btio.h
new file mode 100644
index 0000000..81fda8e
--- /dev/null
+++ b/btio/btio.h
@@ -0,0 +1,94 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  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 <glib.h>
+
+typedef enum {
+	BT_IO_ERROR_DISCONNECTED,
+	BT_IO_ERROR_CONNECT_FAILED,
+	BT_IO_ERROR_FAILED,
+	BT_IO_ERROR_INVALID_ARGS,
+} BtIOError;
+
+#define BT_IO_ERROR bt_io_error_quark()
+
+GQuark bt_io_error_quark(void);
+
+typedef enum {
+	BT_IO_L2RAW,
+	BT_IO_L2CAP,
+	BT_IO_RFCOMM,
+	BT_IO_SCO,
+} BtIOType;
+
+typedef enum {
+	BT_IO_OPT_INVALID = 0,
+	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_SEC_LEVEL,
+	BT_IO_OPT_CHANNEL,
+	BT_IO_OPT_SOURCE_CHANNEL,
+	BT_IO_OPT_DEST_CHANNEL,
+	BT_IO_OPT_PSM,
+	BT_IO_OPT_MTU,
+	BT_IO_OPT_OMTU,
+	BT_IO_OPT_IMTU,
+	BT_IO_OPT_MASTER,
+	BT_IO_OPT_HANDLE,
+	BT_IO_OPT_CLASS,
+	BT_IO_OPT_MODE,
+} BtIOOption;
+
+typedef enum {
+	BT_IO_SEC_SDP = 0,
+	BT_IO_SEC_LOW,
+	BT_IO_SEC_MEDIUM,
+	BT_IO_SEC_HIGH,
+} BtIOSecLevel;
+
+typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
+
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+					GDestroyNotify destroy, GError **err);
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				gpointer user_data, GDestroyNotify destroy,
+				GError **err, BtIOOption opt1, ...);
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+				BtIOConfirm confirm, gpointer user_data,
+				GDestroyNotify destroy, GError **err,
+				BtIOOption opt1, ...);
+
diff --git a/src/btio.c b/src/btio.c
deleted file mode 100644
index 8b273ca..0000000
--- a/src/btio.c
+++ /dev/null
@@ -1,1299 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
- *  Copyright (C) 2009-2010  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 "btio.h"
-
-#define ERROR_FAILED(gerr, str, err) \
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
-				str ": %s (%d)", strerror(err), err)
-
-#define DEFAULT_DEFER_TIMEOUT 30
-
-struct set_opts {
-	bdaddr_t src;
-	bdaddr_t dst;
-	int defer;
-	int sec_level;
-	uint8_t channel;
-	uint16_t psm;
-	uint16_t mtu;
-	uint16_t imtu;
-	uint16_t omtu;
-	int master;
-	uint8_t mode;
-};
-
-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 check_nval(GIOChannel *io)
-{
-	struct pollfd fds;
-
-	memset(&fds, 0, sizeof(fds));
-	fds.fd = g_io_channel_unix_get_fd(io);
-	fds.events = POLLNVAL;
-
-	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
-		return TRUE;
-
-	return FALSE;
-}
-
-static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
-							gpointer user_data)
-{
-	struct accept *accept = user_data;
-	GError *err = NULL;
-
-	/* If the user aborted this accept attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
-				"HUP or ERR on socket");
-
-	accept->connect(io, err, accept->user_data);
-
-	g_clear_error(&err);
-
-	return FALSE;
-}
-
-static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
-							gpointer user_data)
-{
-	struct connect *conn = user_data;
-	GError *gerr = NULL;
-
-	/* If the user aborted this connect attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & G_IO_OUT) {
-		int err = 0, sock = g_io_channel_unix_get_fd(io);
-		socklen_t len = sizeof(err);
-
-		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
-			err = errno;
-
-		if (err)
-			g_set_error(&gerr, BT_IO_ERROR,
-					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
-					strerror(err), err);
-	} else if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"HUP or ERR on socket");
-
-	conn->connect(io, gerr, conn->user_data);
-
-	if (gerr)
-		g_error_free(gerr);
-
-	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) || check_nval(io))
-		return FALSE;
-
-	srv_sock = g_io_channel_unix_get_fd(io);
-
-	cli_sock = accept(srv_sock, NULL, NULL);
-	if (cli_sock < 0)
-		return TRUE;
-
-	cli_io = g_io_channel_unix_new(cli_sock);
-
-	g_io_channel_set_close_on_unref(cli_io, TRUE);
-	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
-
-	if (server->confirm)
-		server->confirm(cli_io, server->user_data);
-	else
-		server->connect(cli_io, NULL, 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, const bdaddr_t *src, uint16_t psm, GError **err)
-{
-	struct sockaddr_l2 addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.l2_family = AF_BLUETOOTH;
-	bacpy(&addr.l2_bdaddr, src);
-	addr.l2_psm = htobs(psm);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "l2cap_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int l2cap_connect(int sock, const 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_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & L2CAP_LM_MASTER)
-			return 0;
-		flags |= L2CAP_LM_MASTER;
-	} else {
-		if (!(flags & L2CAP_LM_MASTER))
-			return 0;
-		flags &= ~L2CAP_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & RFCOMM_LM_MASTER)
-			return 0;
-		flags |= RFCOMM_LM_MASTER;
-	} else {
-		if (!(flags & RFCOMM_LM_MASTER))
-			return 0;
-		flags &= ~RFCOMM_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int l2cap_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		L2CAP_LM_AUTH,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		RFCOMM_LM_AUTH,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
-{
-	struct bt_security sec;
-	int ret;
-
-	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Valid security level range is %d-%d",
-				BT_SECURITY_LOW, BT_SECURITY_HIGH);
-		return FALSE;
-	}
-
-	memset(&sec, 0, sizeof(sec));
-	sec.level = level;
-
-	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
-							sizeof(sec)) == 0)
-		return TRUE;
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_set_lm(sock, level);
-	else
-		ret = rfcomm_set_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "setsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & L2CAP_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & L2CAP_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & L2CAP_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static int rfcomm_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & RFCOMM_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & RFCOMM_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & RFCOMM_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static gboolean get_sec_level(int sock, BtIOType type, int *level,
-								GError **err)
-{
-	struct bt_security sec;
-	socklen_t len;
-	int ret;
-
-	memset(&sec, 0, sizeof(sec));
-	len = sizeof(sec);
-	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
-		*level = sec.level;
-		return TRUE;
-	}
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_get_lm(sock, level);
-	else
-		ret = rfcomm_get_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "getsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
-					uint8_t mode, int master, GError **err)
-{
-	if (imtu || omtu || mode) {
-		struct l2cap_options l2o;
-		socklen_t len;
-
-		memset(&l2o, 0, sizeof(l2o));
-		len = sizeof(l2o);
-		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-								&len) < 0) {
-			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-
-		if (imtu)
-			l2o.imtu = imtu;
-		if (omtu)
-			l2o.omtu = omtu;
-		if (mode)
-			l2o.mode = mode;
-
-		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-							sizeof(l2o)) < 0) {
-			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-	}
-
-	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "l2cap_set_master", errno);
-		return FALSE;
-	}
-
-	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
-		return FALSE;
-
-	return TRUE;
-}
-
-static int rfcomm_bind(int sock,
-		const bdaddr_t *src, uint8_t channel, GError **err)
-{
-	struct sockaddr_rc addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.rc_family = AF_BLUETOOTH;
-	bacpy(&addr.rc_bdaddr, src);
-	addr.rc_channel = channel;
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "rfcomm_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
-{
-	int err;
-	struct sockaddr_rc addr;
-
-	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 gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
-{
-	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
-		return FALSE;
-
-	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "rfcomm_set_master", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int sco_bind(int sock, const bdaddr_t *src, GError **err)
-{
-	struct sockaddr_sco addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sco_family = AF_BLUETOOTH;
-	bacpy(&addr.sco_bdaddr, src);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "sco_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int sco_connect(int sock, const 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 gboolean sco_set(int sock, uint16_t mtu, GError **err)
-{
-	struct sco_options sco_opt;
-	socklen_t len;
-
-	if (!mtu)
-		return TRUE;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	sco_opt.mtu = mtu;
-	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
-						sizeof(sco_opt)) < 0) {
-		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean parse_set_opts(struct set_opts *opts, GError **err,
-						BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	const char *str;
-
-	memset(opts, 0, sizeof(*opts));
-
-	/* Set defaults */
-	opts->defer = DEFAULT_DEFER_TIMEOUT;
-	opts->master = -1;
-	opts->sec_level = BT_IO_SEC_MEDIUM;
-	opts->mode = L2CAP_MODE_BASIC;
-
-	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, const 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, const bdaddr_t *));
-			break;
-		case BT_IO_OPT_DEFER_TIMEOUT:
-			opts->defer = 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;
-		case BT_IO_OPT_MASTER:
-			opts->master = va_arg(args, gboolean);
-			break;
-		case BT_IO_OPT_MODE:
-			opts->mode = va_arg(args, int);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
-				socklen_t len, GError **err)
-{
-	socklen_t olen;
-
-	memset(src, 0, len);
-	olen = len;
-	if (getsockname(sock, src, &olen) < 0) {
-		ERROR_FAILED(err, "getsockname", errno);
-		return FALSE;
-	}
-
-	memset(dst, 0, len);
-	olen = len;
-	if (getpeername(sock, dst, &olen) < 0) {
-		ERROR_FAILED(err, "getpeername", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct l2cap_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_l2 src, dst;
-	struct l2cap_options l2o;
-	int flags;
-	uint8_t dev_class[3];
-	uint16_t handle;
-	socklen_t len;
-
-	len = sizeof(l2o);
-	memset(&l2o, 0, len);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		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) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_L2CAP,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_PSM:
-			*(va_arg(args, uint16_t *)) = src.l2_psm ?
-						src.l2_psm : dst.l2_psm;
-			break;
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = l2o.omtu;
-			break;
-		case BT_IO_OPT_IMTU:
-			*(va_arg(args, uint16_t *)) = l2o.imtu;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct rfcomm_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_rc src, dst;
-	int flags;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		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) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_RFCOMM,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel ?
-					src.rc_channel : dst.rc_channel;
-			break;
-		case BT_IO_OPT_SOURCE_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel;
-			break;
-		case BT_IO_OPT_DEST_CHANNEL:
-			*(va_arg(args, uint8_t *)) = dst.rc_channel;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct sco_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_sco src, dst;
-	struct sco_options sco_opt;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		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_MTU:
-		case BT_IO_OPT_IMTU:
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
-						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, err, opt1, args);
-	case BT_IO_RFCOMM:
-		return rfcomm_get(sock, err, opt1, args);
-	case BT_IO_SCO:
-		return sco_get(sock, err, opt1, args);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
-					GDestroyNotify destroy, GError **err)
-{
-	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_FAILED(err, "poll", errno);
-		return FALSE;
-	}
-
-	if (!(pfd.revents & POLLOUT)) {
-		int ret;
-		ret = read(sock, &c, 1);
-	}
-
-	accept_add(io, connect, user_data, destroy);
-
-	return TRUE;
-}
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-	struct set_opts opts;
-	int sock;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, 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:
-		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
-						opts.mode, opts.master, err);
-	case BT_IO_RFCOMM:
-		return rfcomm_set(sock, opts.sec_level, opts.master, err);
-	case BT_IO_SCO:
-		return sco_set(sock, opts.mtu, err);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = get_valist(io, type, err, opt1, args);
-	va_end(args);
-
-	return ret;
-}
-
-static GIOChannel *create_io(BtIOType type, gboolean server,
-					struct set_opts *opts, GError **err)
-{
-	int sock;
-	GIOChannel *io;
-
-	switch (type) {
-	case BT_IO_L2RAW:
-		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
-			goto failed;
-		break;
-	case BT_IO_L2CAP:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
-						opts->mode, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_RFCOMM:
-		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
-			return NULL;
-		}
-		if (rfcomm_bind(sock, &opts->src,
-					server ? opts->channel : 0, err) < 0)
-			goto failed;
-		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_SCO:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
-			return NULL;
-		}
-		if (sco_bind(sock, &opts->src, err) < 0)
-			goto failed;
-		if (!sco_set(sock, opts->mtu, err))
-			goto failed;
-		break;
-	default:
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	io = g_io_channel_unix_new(sock);
-
-	g_io_channel_set_close_on_unref(io, TRUE);
-	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
-
-	return io;
-
-failed:
-	close(sock);
-
-	return NULL;
-}
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				gpointer user_data, GDestroyNotify destroy,
-				GError **gerr, BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int err, sock;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, gerr, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, FALSE, &opts, gerr);
-	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;
-	default:
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-						"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	if (err < 0) {
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"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, GError **err,
-				BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int sock;
-	gboolean ret;
-
-	if (type == BT_IO_L2RAW) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Server L2CAP RAW sockets not supported");
-		return NULL;
-	}
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, TRUE, &opts, err);
-	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_FAILED(err, "listen", errno);
-		g_io_channel_unref(io);
-		return NULL;
-	}
-
-	server_add(io, connect, confirm, user_data, destroy);
-
-	return io;
-}
-
-GQuark bt_io_error_quark(void)
-{
-	return g_quark_from_static_string("bt-io-error-quark");
-}
diff --git a/src/btio.h b/src/btio.h
deleted file mode 100644
index 81fda8e..0000000
--- a/src/btio.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
- *  Copyright (C) 2009-2010  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 <glib.h>
-
-typedef enum {
-	BT_IO_ERROR_DISCONNECTED,
-	BT_IO_ERROR_CONNECT_FAILED,
-	BT_IO_ERROR_FAILED,
-	BT_IO_ERROR_INVALID_ARGS,
-} BtIOError;
-
-#define BT_IO_ERROR bt_io_error_quark()
-
-GQuark bt_io_error_quark(void);
-
-typedef enum {
-	BT_IO_L2RAW,
-	BT_IO_L2CAP,
-	BT_IO_RFCOMM,
-	BT_IO_SCO,
-} BtIOType;
-
-typedef enum {
-	BT_IO_OPT_INVALID = 0,
-	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_SEC_LEVEL,
-	BT_IO_OPT_CHANNEL,
-	BT_IO_OPT_SOURCE_CHANNEL,
-	BT_IO_OPT_DEST_CHANNEL,
-	BT_IO_OPT_PSM,
-	BT_IO_OPT_MTU,
-	BT_IO_OPT_OMTU,
-	BT_IO_OPT_IMTU,
-	BT_IO_OPT_MASTER,
-	BT_IO_OPT_HANDLE,
-	BT_IO_OPT_CLASS,
-	BT_IO_OPT_MODE,
-} BtIOOption;
-
-typedef enum {
-	BT_IO_SEC_SDP = 0,
-	BT_IO_SEC_LOW,
-	BT_IO_SEC_MEDIUM,
-	BT_IO_SEC_HIGH,
-} BtIOSecLevel;
-
-typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
-
-typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
-					GDestroyNotify destroy, GError **err);
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				gpointer user_data, GDestroyNotify destroy,
-				GError **err, BtIOOption opt1, ...);
-
-GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, gpointer user_data,
-				GDestroyNotify destroy, GError **err,
-				BtIOOption opt1, ...);
-
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 3/4] btio: Remove unused bt_io_set function
From: Zhenhua Zhang @ 2010-09-09  9:43 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284025388-3831-1-git-send-email-zhenhua.zhang@intel.com>

It is not used by either obex or bluez.
---
 btio/btio.c |   33 ---------------------------------
 btio/btio.h |    3 ---
 2 files changed, 0 insertions(+), 36 deletions(-)

diff --git a/btio/btio.c b/btio/btio.c
index 8b273ca..41b698f 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -1074,39 +1074,6 @@ gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
 	return TRUE;
 }
 
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-	struct set_opts opts;
-	int sock;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, 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:
-		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
-						opts.mode, opts.master, err);
-	case BT_IO_RFCOMM:
-		return rfcomm_set(sock, opts.sec_level, opts.master, err);
-	case BT_IO_SCO:
-		return sco_set(sock, opts.mtu, err);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
 gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
 							BtIOOption opt1, ...)
 {
diff --git a/btio/btio.h b/btio/btio.h
index 81fda8e..89d3d08 100644
--- a/btio/btio.h
+++ b/btio/btio.h
@@ -77,9 +77,6 @@ typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
 gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
 					GDestroyNotify destroy, GError **err);
 
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
 gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
 						BtIOOption opt1, ...);
 
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 4/4] btio: Add ifndef/endif guard for btio.h
From: Zhenhua Zhang @ 2010-09-09  9:43 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284025388-3831-1-git-send-email-zhenhua.zhang@intel.com>

To avoid circular inclusion of include file.
---
 btio/btio.h |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/btio/btio.h b/btio/btio.h
index 89d3d08..5f2911a 100644
--- a/btio/btio.h
+++ b/btio/btio.h
@@ -21,6 +21,8 @@
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
+#ifndef BT_IO_H
+#define BT_IO_H
 
 #include <glib.h>
 
@@ -89,3 +91,4 @@ GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
 				GDestroyNotify destroy, GError **err,
 				BtIOOption opt1, ...);
 
+#endif
-- 
1.7.0.4


^ permalink raw reply related

* Re: [PATCH v3] Bluetooth: Add socket option definitions for AMP
From: David Vrabel @ 2010-09-09 10:25 UTC (permalink / raw)
  To: Mat Martineau
  Cc: linux-bluetooth, marcel, gustavo, rshaffer, haijun.liu,
	linux-arm-msm
In-Reply-To: <1283966193-17831-1-git-send-email-mathewm@codeaurora.org>

Mat Martineau wrote:
> This adds a new BT_AMP socket option to control the use of AMP channels.
> It is for use with the SOL_BLUETOOTH option level on L2CAP sockets.

Looks good.

David
-- 
David Vrabel, Senior Software Engineer, Drivers
CSR, Churchill House, Cambridge Business Park,  Tel: +44 (0)1223 692562
Cowley Road, Cambridge, CB4 0WZ                 http://www.csr.com/


Member of the CSR plc group of companies. CSR plc registered in England and Wales, registered number 4187346, registered office Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4 0WZ, United Kingdom

^ permalink raw reply

* Re: [PATCH 3/4] btio: Remove unused bt_io_set function
From: Johan Hedberg @ 2010-09-09 10:38 UTC (permalink / raw)
  To: Zhenhua Zhang; +Cc: linux-bluetooth
In-Reply-To: <1284025283-3773-4-git-send-email-zhenhua.zhang@intel.com>

Hi,

On Thu, Sep 09, 2010, Zhenhua Zhang wrote:
> It is not used by either obex or bluez.
> ---
>  btio/btio.c |   33 ---------------------------------
>  btio/btio.h |    3 ---
>  2 files changed, 0 insertions(+), 36 deletions(-)

I don't think removing this feature is a good idea. Even though there
aren't any current users of bt_io_set doesn't mean that there wont be in
the future. I remember e.g. discussions regarding AMP about applications
needing the ability to request a switch from BR/EDR to WLAN for an
active connection. This could e.g. be accomplished through a setsockopt
call. Other uses for setsockopt while a connection is open, or even
changing settings for an existing server socket might exist too.

Johan

^ permalink raw reply

* MAP implementation status
From: Radoslaw Jablonski @ 2010-09-09 12:46 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org


  Hi all,
We plan to start implementation of Message Access Profile Server as 
obexd plugin. Before we start with coding part I need to check if 
someone else started implementing MAP Server, MNS Server of Map client.
If some code is already available, then we could combine our forces to 
add MAP support for OBEXD.

Thanks for any response,
Radek


^ permalink raw reply

* Re: [PATCH 1/2] Bluetooth: Implement LE Set Advertise Enable cmd
From: Anderson Briglia @ 2010-09-09 19:48 UTC (permalink / raw)
  To: Gustavo F. Padovan; +Cc: linux-bluetooth
In-Reply-To: <20100908214359.GM16973@vigoh>

On 09/08/2010 05:43 PM, Gustavo F. Padovan wrote:
> Hi Anderson,
>
> * Anderson Briglia<anderson.briglia@openbossa.org>  [2010-08-23 15:30:14 -0400]:
>
>    
>> This patch implements LE Set Advertise Enable command for dual mode and
>> Low Energy hci controllers. It also adds new HCI flags in order to
>> indicate the Advertising state for userland applications and kernel
>> itself.
>>
>> Signed-off-by: Anderson Briglia<anderson.briglia@openbossa.org>
>> ---
>>   include/net/bluetooth/hci.h |    6 ++++++
>>   net/bluetooth/hci_event.c   |   27 +++++++++++++++++++++++++++
>>   2 files changed, 33 insertions(+), 0 deletions(-)
>>
>> diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
>> index bcbdd6d..cae1816 100644
>> --- a/include/net/bluetooth/hci.h
>> +++ b/include/net/bluetooth/hci.h
>> @@ -76,6 +76,7 @@ enum {
>>   	HCI_INQUIRY,
>>
>>   	HCI_RAW,
>>      
> Skip a line here and then add the new LE commands
>
>    
>> +	HCI_LE_ADV,
>>   };
>>
>>   /* HCI ioctl defines */
>> @@ -593,6 +594,11 @@ struct hci_rp_read_bd_addr {
>>   	bdaddr_t bdaddr;
>>   } __packed;
>>
>> +/* --- HCI LE Commands --- */
>> +#define HCI_OP_LE_SET_ADVERTISE_ENABLE	0x200a
>> +	#define ADVERTISE_ENABLED	0x01
>> +	#define ADVERTISE_DISABLED	0x00
>> +
>>   /* ---- HCI Events ---- */
>>   #define HCI_EV_INQUIRY_COMPLETE		0x01
>>
>> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
>> index bfef5ba..c86c655 100644
>> --- a/net/bluetooth/hci_event.c
>> +++ b/net/bluetooth/hci_event.c
>> @@ -822,6 +822,29 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
>>   	hci_dev_unlock(hdev);
>>   }
>>
>> +static void hci_cc_le_set_advertise(struct hci_dev *hdev, struct sk_buff *skb)
>> +{
>> +	__u8 status = *((__u8 *) skb->data);
>> +	void *sent;
>> +
>> +	BT_DBG("%s status 0x%x", hdev->name, status);
>> +
>> +	sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADVERTISE_ENABLE);
>> +	if (!sent)
>> +		return;
>> +
>> +	if (!status) {
>> +		__u8 param = *((__u8 *) sent);
>> +
>> +		clear_bit(HCI_LE_ADV,&hdev->flags);
>> +
>> +		if (param&  ADVERTISE_ENABLED)
>> +			set_bit(HCI_LE_ADV,&hdev->flags);
>>      
> We could do that this way:
>
> 		if (param == ADVERTISE_ENABLED)
> 			set_bit(HCI_LE_ADV,&hdev->flags);
> 		else
> 			clear_bit(HCI_LE_ADV,&hdev->flags);
>    

Actually I just followed the design implemented on previous functions.. 
If you check this file you could note this.

>    
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>      
>    


^ permalink raw reply

* set LE advertise not working...
From: Pavan Savoy @ 2010-09-09 19:58 UTC (permalink / raw)
  To: linux-bluetooth

I have an LE enabled BT controller, and I happened to send across a
simple "leadv" from the recently updated hciconfig
I got back the following data
"4 e 5 1 a 20 c 3a" and hence
"Can't set advertise mode on hci0: Success (0)"
and 0xC I guess by the BT spec means the command is disallowed (for
reasons unknown?....)

So I'm just wondering, am I supposed to run some HCI-VS commands to
enable LE? I mean I should ask my vendor I agree, but anyone who has
got it working.. do they run some VS commands before enabling LE
advertising?

^ permalink raw reply

* Re: [PATCH 1/2] Bluetooth: Implement LE Set Advertise Enable cmd
From: Gustavo F. Padovan @ 2010-09-09 20:31 UTC (permalink / raw)
  To: Anderson Briglia; +Cc: linux-bluetooth
In-Reply-To: <4C893A05.3010103@openbossa.org>

* Anderson Briglia <anderson.briglia@openbossa.org> [2010-09-09 15:48:21 -0400]:

> On 09/08/2010 05:43 PM, Gustavo F. Padovan wrote:
> > Hi Anderson,
> >
> > * Anderson Briglia<anderson.briglia@openbossa.org>  [2010-08-23 15:30:14 -0400]:
> >
> >    
> >> This patch implements LE Set Advertise Enable command for dual mode and
> >> Low Energy hci controllers. It also adds new HCI flags in order to
> >> indicate the Advertising state for userland applications and kernel
> >> itself.
> >>
> >> Signed-off-by: Anderson Briglia<anderson.briglia@openbossa.org>
> >> ---
> >>   include/net/bluetooth/hci.h |    6 ++++++
> >>   net/bluetooth/hci_event.c   |   27 +++++++++++++++++++++++++++
> >>   2 files changed, 33 insertions(+), 0 deletions(-)
> >>
> >> diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
> >> index bcbdd6d..cae1816 100644
> >> --- a/include/net/bluetooth/hci.h
> >> +++ b/include/net/bluetooth/hci.h
> >> @@ -76,6 +76,7 @@ enum {
> >>   	HCI_INQUIRY,
> >>
> >>   	HCI_RAW,
> >>      
> > Skip a line here and then add the new LE commands
> >
> >    
> >> +	HCI_LE_ADV,
> >>   };
> >>
> >>   /* HCI ioctl defines */
> >> @@ -593,6 +594,11 @@ struct hci_rp_read_bd_addr {
> >>   	bdaddr_t bdaddr;
> >>   } __packed;
> >>
> >> +/* --- HCI LE Commands --- */
> >> +#define HCI_OP_LE_SET_ADVERTISE_ENABLE	0x200a
> >> +	#define ADVERTISE_ENABLED	0x01
> >> +	#define ADVERTISE_DISABLED	0x00
> >> +
> >>   /* ---- HCI Events ---- */
> >>   #define HCI_EV_INQUIRY_COMPLETE		0x01
> >>
> >> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> >> index bfef5ba..c86c655 100644
> >> --- a/net/bluetooth/hci_event.c
> >> +++ b/net/bluetooth/hci_event.c
> >> @@ -822,6 +822,29 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
> >>   	hci_dev_unlock(hdev);
> >>   }
> >>
> >> +static void hci_cc_le_set_advertise(struct hci_dev *hdev, struct sk_buff *skb)
> >> +{
> >> +	__u8 status = *((__u8 *) skb->data);
> >> +	void *sent;
> >> +
> >> +	BT_DBG("%s status 0x%x", hdev->name, status);
> >> +
> >> +	sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADVERTISE_ENABLE);
> >> +	if (!sent)
> >> +		return;
> >> +
> >> +	if (!status) {
> >> +		__u8 param = *((__u8 *) sent);
> >> +
> >> +		clear_bit(HCI_LE_ADV,&hdev->flags);
> >> +
> >> +		if (param&  ADVERTISE_ENABLED)
> >> +			set_bit(HCI_LE_ADV,&hdev->flags);
> >>      
> > We could do that this way:
> >
> > 		if (param == ADVERTISE_ENABLED)
> > 			set_bit(HCI_LE_ADV,&hdev->flags);
> > 		else
> > 			clear_bit(HCI_LE_ADV,&hdev->flags);
> >    
> 
> Actually I just followed the design implemented on previous functions.. 
> If you check this file you could note this.

Actually not, the standard there is using if..else. Only
hci_cc_write_scan_enable() does clear_bit() if() set_bit().

-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi

^ permalink raw reply

* RE: [PATCH 3/4] btio: Remove unused bt_io_set function
From: Zhang, Zhenhua @ 2010-09-10  0:11 UTC (permalink / raw)
  To: Johan Hedberg; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <20100909101901.GA16324@jh-x301>

Hi Johan,

Johan Hedberg wrote:
> Hi,
> 
> On Thu, Sep 09, 2010, Zhenhua Zhang wrote:
>> It is not used by either obex or bluez.
>> ---
>>  btio/btio.c |   33 ---------------------------------  btio/btio.h |
>>  3 --- 2 files changed, 0 insertions(+), 36 deletions(-)
> 
> I don't think removing this feature is a good idea. Even though there
> aren't any current users of bt_io_set doesn't mean that there
> wont be in
> the future. I remember e.g. discussions regarding AMP about
> applications needing the ability to request a switch from BR/EDR to
> WLAN for an active connection. This could e.g. be accomplished
> through a setsockopt call. Other uses for setsockopt while a
> connection is open, or even changing settings for an existing server
> socket might exist too. 
> 
> Johan

That's fine for me. I don't see any current function call so I have this patch. I will resend them and remove patch 3/4. Let me know if you have other suggestiosn.

Regards,
Zhenhua


^ permalink raw reply

* [PATCH 0/3] obex: isolate btio.[ch] and do some cleanup
From: Zhenhua Zhang @ 2010-09-10  2:47 UTC (permalink / raw)
  To: linux-bluetooth

Hi,

These patches move obexd/src/btio.[ch] out from source directory and make it identical with bluez's btio.[ch]. So we can freely copy them among bluez, obex and ofono.

Best Regards,
Zhenhua


^ permalink raw reply

* [PATCH 1/3] btio: Remove blank line at EOF
From: Zhenhua Zhang @ 2010-09-10  2:47 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284086847-16429-1-git-send-email-zhenhua.zhang@intel.com>

---
 src/btio.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/src/btio.c b/src/btio.c
index 485eec6..42a3bcd 100644
--- a/src/btio.c
+++ b/src/btio.c
@@ -1297,4 +1297,3 @@ GQuark bt_io_error_quark(void)
 {
 	return g_quark_from_static_string("bt-io-error-quark");
 }
-
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 2/3] btio: Seperate btio.[ch] into btio directory
From: Zhenhua Zhang @ 2010-09-10  2:47 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284086847-16429-1-git-send-email-zhenhua.zhang@intel.com>

Seperate btio.[ch] from src directory to btio sub-folder.
---
 Makefile.am |   19 +-
 btio/btio.c | 1299 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 btio/btio.h |   97 +++++
 src/btio.c  | 1299 -----------------------------------------------------------
 src/btio.h  |   97 -----
 5 files changed, 1406 insertions(+), 1405 deletions(-)
 create mode 100644 btio/btio.c
 create mode 100644 btio/btio.h
 delete mode 100644 src/btio.c
 delete mode 100644 src/btio.h

diff --git a/Makefile.am b/Makefile.am
index e6940bc..175747c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -19,6 +19,8 @@ gwobex_sources = gwobex/gw-obex.h gwobex/gw-obex.c \
 			gwobex/obex-xfer.h gwobex/obex-xfer.c \
 			gwobex/utils.h gwobex/utils.c gwobex/log.h
 
+btio_sources = btio/btio.h btio/btio.c
+
 libexec_PROGRAMS =
 
 if SERVER
@@ -66,11 +68,10 @@ builtin_nodist += plugins/phonebook.c
 
 libexec_PROGRAMS += src/obexd
 
-src_obexd_SOURCES = $(gdbus_sources) $(builtin_sources) \
+src_obexd_SOURCES = $(gdbus_sources) $(builtin_sources) $(btio_sources) \
 			src/main.c src/obexd.h src/plugin.h src/plugin.c \
-			src/log.h src/log.c src/btio.h src/btio.c \
-			src/dbus.h src/manager.c src/obex.h src/obex.c \
-			src/obex-priv.h \
+			src/log.h src/log.c src/dbus.h src/manager.c \
+			src/obex.h src/obex.c src/obex-priv.h \
 			src/mimetype.h src/mimetype.c \
 			src/service.h src/service.c \
 			src/transport.h src/transport.c \
@@ -108,13 +109,12 @@ service_in_files += client/obex-client.service.in
 
 libexec_PROGRAMS += client/obex-client
 
-client_obex_client_SOURCES = $(gdbus_sources) $(gwobex_sources) client/main.c \
-				client/session.h client/session.c \
+client_obex_client_SOURCES = $(gdbus_sources) $(gwobex_sources) $(btio_sources) \
+				client/main.c client/session.h client/session.c \
 				src/log.h src/log.c \
 				client/pbap.h client/pbap.c \
 				client/sync.h client/sync.c \
-				client/transfer.h client/transfer.c \
-				src/btio.c src/btio.h
+				client/transfer.h client/transfer.c
 
 client_obex_client_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ @OPENOBEX_LIBS@ @BLUEZ_LIBS@
 endif
@@ -127,7 +127,8 @@ AM_CFLAGS = @OPENOBEX_CFLAGS@ @BLUEZ_CFLAGS@ @EBOOK_CFLAGS@ \
 			-DOBEX_PLUGIN_BUILTIN -DPLUGINDIR=\""$(plugindir)"\"
 
 INCLUDES = -I$(builddir)/src -I$(srcdir)/src -I$(srcdir)/plugins \
-				-I$(srcdir)/gdbus -I$(srcdir)/gwobex
+				-I$(srcdir)/gdbus -I$(srcdir)/gwobex \
+				-I$(srcdir)/btio
 
 CLEANFILES = $(service_DATA) $(builtin_files)
 
diff --git a/btio/btio.c b/btio/btio.c
new file mode 100644
index 0000000..42a3bcd
--- /dev/null
+++ b/btio/btio.c
@@ -0,0 +1,1299 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  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 "btio.h"
+
+#define ERROR_FAILED(gerr, str, err) \
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
+				str ": %s (%d)", strerror(err), err)
+
+#define DEFAULT_DEFER_TIMEOUT 30
+
+struct set_opts {
+	bdaddr_t src;
+	bdaddr_t dst;
+	int defer;
+	int sec_level;
+	uint8_t channel;
+	uint16_t psm;
+	uint16_t mtu;
+	uint16_t imtu;
+	uint16_t omtu;
+	int master;
+	uint8_t mode;
+};
+
+struct connect {
+	BtIOConnect connect;
+	void *user_data;
+	GDestroyNotify destroy;
+};
+
+struct accept {
+	BtIOConnect connect;
+	void *user_data;
+	GDestroyNotify destroy;
+};
+
+struct server {
+	BtIOConnect connect;
+	BtIOConfirm confirm;
+	void *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 check_nval(GIOChannel *io)
+{
+	struct pollfd fds;
+
+	memset(&fds, 0, sizeof(fds));
+	fds.fd = g_io_channel_unix_get_fd(io);
+	fds.events = POLLNVAL;
+
+	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
+							void *user_data)
+{
+	struct accept *accept = user_data;
+	GError *err = NULL;
+
+	/* If the user aborted this accept attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
+				"HUP or ERR on socket");
+
+	accept->connect(io, err, accept->user_data);
+
+	g_clear_error(&err);
+
+	return FALSE;
+}
+
+static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
+							void *user_data)
+{
+	struct connect *conn = user_data;
+	GError *gerr = NULL;
+
+	/* If the user aborted this connect attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & G_IO_OUT) {
+		int err = 0, sock = g_io_channel_unix_get_fd(io);
+		socklen_t len = sizeof(err);
+
+		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
+			err = errno;
+
+		if (err)
+			g_set_error(&gerr, BT_IO_ERROR,
+					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
+					strerror(err), err);
+	} else if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"HUP or ERR on socket");
+
+	conn->connect(io, gerr, conn->user_data);
+
+	if (gerr)
+		g_error_free(gerr);
+
+	return FALSE;
+}
+
+static gboolean server_cb(GIOChannel *io, GIOCondition cond,
+							void *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) || check_nval(io))
+		return FALSE;
+
+	srv_sock = g_io_channel_unix_get_fd(io);
+
+	cli_sock = accept(srv_sock, NULL, NULL);
+	if (cli_sock < 0)
+		return TRUE;
+
+	cli_io = g_io_channel_unix_new(cli_sock);
+
+	g_io_channel_set_close_on_unref(cli_io, TRUE);
+	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
+
+	if (server->confirm)
+		server->confirm(cli_io, server->user_data);
+	else
+		server->connect(cli_io, NULL, server->user_data);
+
+	g_io_channel_unref(cli_io);
+
+	return TRUE;
+}
+
+static void server_add(GIOChannel *io, BtIOConnect connect,
+				BtIOConfirm confirm, void *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,
+				void *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, void *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, const bdaddr_t *src, uint16_t psm, GError **err)
+{
+	struct sockaddr_l2 addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+	addr.l2_psm = htobs(psm);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "l2cap_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int l2cap_connect(int sock, const 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_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & L2CAP_LM_MASTER)
+			return 0;
+		flags |= L2CAP_LM_MASTER;
+	} else {
+		if (!(flags & L2CAP_LM_MASTER))
+			return 0;
+		flags &= ~L2CAP_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & RFCOMM_LM_MASTER)
+			return 0;
+		flags |= RFCOMM_LM_MASTER;
+	} else {
+		if (!(flags & RFCOMM_LM_MASTER))
+			return 0;
+		flags &= ~RFCOMM_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int l2cap_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		L2CAP_LM_AUTH,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		RFCOMM_LM_AUTH,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
+{
+	struct bt_security sec;
+	int ret;
+
+	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Valid security level range is %d-%d",
+				BT_SECURITY_LOW, BT_SECURITY_HIGH);
+		return FALSE;
+	}
+
+	memset(&sec, 0, sizeof(sec));
+	sec.level = level;
+
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
+							sizeof(sec)) == 0)
+		return TRUE;
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_set_lm(sock, level);
+	else
+		ret = rfcomm_set_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "setsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & L2CAP_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & L2CAP_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & L2CAP_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static int rfcomm_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & RFCOMM_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & RFCOMM_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & RFCOMM_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static gboolean get_sec_level(int sock, BtIOType type, int *level,
+								GError **err)
+{
+	struct bt_security sec;
+	socklen_t len;
+	int ret;
+
+	memset(&sec, 0, sizeof(sec));
+	len = sizeof(sec);
+	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+		*level = sec.level;
+		return TRUE;
+	}
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_get_lm(sock, level);
+	else
+		ret = rfcomm_get_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "getsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
+					uint8_t mode, int master, GError **err)
+{
+	if (imtu || omtu || mode) {
+		struct l2cap_options l2o;
+		socklen_t len;
+
+		memset(&l2o, 0, sizeof(l2o));
+		len = sizeof(l2o);
+		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+								&len) < 0) {
+			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+
+		if (imtu)
+			l2o.imtu = imtu;
+		if (omtu)
+			l2o.omtu = omtu;
+		if (mode)
+			l2o.mode = mode;
+
+		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+							sizeof(l2o)) < 0) {
+			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+	}
+
+	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "l2cap_set_master", errno);
+		return FALSE;
+	}
+
+	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
+		return FALSE;
+
+	return TRUE;
+}
+
+static int rfcomm_bind(int sock,
+		const bdaddr_t *src, uint8_t channel, GError **err)
+{
+	struct sockaddr_rc addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, src);
+	addr.rc_channel = channel;
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "rfcomm_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
+{
+	int err;
+	struct sockaddr_rc addr;
+
+	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 gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
+{
+	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
+		return FALSE;
+
+	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "rfcomm_set_master", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int sco_bind(int sock, const bdaddr_t *src, GError **err)
+{
+	struct sockaddr_sco addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, src);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "sco_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sco_connect(int sock, const 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 gboolean sco_set(int sock, uint16_t mtu, GError **err)
+{
+	struct sco_options sco_opt;
+	socklen_t len;
+
+	if (!mtu)
+		return TRUE;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	sco_opt.mtu = mtu;
+	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
+						sizeof(sco_opt)) < 0) {
+		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean parse_set_opts(struct set_opts *opts, GError **err,
+						BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	const char *str;
+
+	memset(opts, 0, sizeof(*opts));
+
+	/* Set defaults */
+	opts->defer = DEFAULT_DEFER_TIMEOUT;
+	opts->master = -1;
+	opts->sec_level = BT_IO_SEC_MEDIUM;
+	opts->mode = L2CAP_MODE_BASIC;
+
+	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, const 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, const bdaddr_t *));
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			opts->defer = 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;
+		case BT_IO_OPT_MASTER:
+			opts->master = va_arg(args, gboolean);
+			break;
+		case BT_IO_OPT_MODE:
+			opts->mode = va_arg(args, int);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
+				socklen_t len, GError **err)
+{
+	socklen_t olen;
+
+	memset(src, 0, len);
+	olen = len;
+	if (getsockname(sock, src, &olen) < 0) {
+		ERROR_FAILED(err, "getsockname", errno);
+		return FALSE;
+	}
+
+	memset(dst, 0, len);
+	olen = len;
+	if (getpeername(sock, dst, &olen) < 0) {
+		ERROR_FAILED(err, "getpeername", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct l2cap_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_l2 src, dst;
+	struct l2cap_options l2o;
+	int flags;
+	uint8_t dev_class[3];
+	uint16_t handle;
+	socklen_t len;
+
+	len = sizeof(l2o);
+	memset(&l2o, 0, len);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		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) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_L2CAP,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_PSM:
+			*(va_arg(args, uint16_t *)) = src.l2_psm ?
+						src.l2_psm : dst.l2_psm;
+			break;
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = l2o.omtu;
+			break;
+		case BT_IO_OPT_IMTU:
+			*(va_arg(args, uint16_t *)) = l2o.imtu;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct rfcomm_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_rc src, dst;
+	int flags;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		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) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_RFCOMM,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel ?
+					src.rc_channel : dst.rc_channel;
+			break;
+		case BT_IO_OPT_SOURCE_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel;
+			break;
+		case BT_IO_OPT_DEST_CHANNEL:
+			*(va_arg(args, uint8_t *)) = dst.rc_channel;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct sco_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_sco src, dst;
+	struct sco_options sco_opt;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		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_MTU:
+		case BT_IO_OPT_IMTU:
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
+						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, err, opt1, args);
+	case BT_IO_RFCOMM:
+		return rfcomm_get(sock, err, opt1, args);
+	case BT_IO_SCO:
+		return sco_get(sock, err, opt1, args);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
+					GDestroyNotify destroy, GError **err)
+{
+	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_FAILED(err, "poll", errno);
+		return FALSE;
+	}
+
+	if (!(pfd.revents & POLLOUT)) {
+		int ret;
+		ret = read(sock, &c, 1);
+	}
+
+	accept_add(io, connect, user_data, destroy);
+
+	return TRUE;
+}
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+	struct set_opts opts;
+	int sock;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, 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:
+		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
+						opts.mode, opts.master, err);
+	case BT_IO_RFCOMM:
+		return rfcomm_set(sock, opts.sec_level, opts.master, err);
+	case BT_IO_SCO:
+		return sco_set(sock, opts.mtu, err);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = get_valist(io, type, err, opt1, args);
+	va_end(args);
+
+	return ret;
+}
+
+static GIOChannel *create_io(BtIOType type, gboolean server,
+					struct set_opts *opts, GError **err)
+{
+	int sock;
+	GIOChannel *io;
+
+	switch (type) {
+	case BT_IO_L2RAW:
+		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
+			goto failed;
+		break;
+	case BT_IO_L2CAP:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
+						opts->mode, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_RFCOMM:
+		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
+			return NULL;
+		}
+		if (rfcomm_bind(sock, &opts->src,
+					server ? opts->channel : 0, err) < 0)
+			goto failed;
+		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_SCO:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
+			return NULL;
+		}
+		if (sco_bind(sock, &opts->src, err) < 0)
+			goto failed;
+		if (!sco_set(sock, opts->mtu, err))
+			goto failed;
+		break;
+	default:
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	io = g_io_channel_unix_new(sock);
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+	return io;
+
+failed:
+	close(sock);
+
+	return NULL;
+}
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				void *user_data, GDestroyNotify destroy,
+				GError **gerr, BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int err, sock;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, gerr, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, FALSE, &opts, gerr);
+	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;
+	default:
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+						"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	if (err < 0) {
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"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, void *user_data,
+				GDestroyNotify destroy, GError **err,
+				BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int sock;
+	gboolean ret;
+
+	if (type == BT_IO_L2RAW) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Server L2CAP RAW sockets not supported");
+		return NULL;
+	}
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, TRUE, &opts, err);
+	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_FAILED(err, "listen", errno);
+		g_io_channel_unref(io);
+		return NULL;
+	}
+
+	server_add(io, connect, confirm, user_data, destroy);
+
+	return io;
+}
+
+GQuark bt_io_error_quark(void)
+{
+	return g_quark_from_static_string("bt-io-error-quark");
+}
diff --git a/btio/btio.h b/btio/btio.h
new file mode 100644
index 0000000..e9dcc9f
--- /dev/null
+++ b/btio/btio.h
@@ -0,0 +1,97 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  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_ERROR_DISCONNECTED,
+	BT_IO_ERROR_CONNECT_FAILED,
+	BT_IO_ERROR_FAILED,
+	BT_IO_ERROR_INVALID_ARGS,
+} BtIOError;
+
+#define BT_IO_ERROR bt_io_error_quark()
+
+GQuark bt_io_error_quark(void);
+
+typedef enum {
+	BT_IO_L2RAW,
+	BT_IO_L2CAP,
+	BT_IO_RFCOMM,
+	BT_IO_SCO,
+} BtIOType;
+
+typedef enum {
+	BT_IO_OPT_INVALID = 0,
+	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_SEC_LEVEL,
+	BT_IO_OPT_CHANNEL,
+	BT_IO_OPT_SOURCE_CHANNEL,
+	BT_IO_OPT_DEST_CHANNEL,
+	BT_IO_OPT_PSM,
+	BT_IO_OPT_MTU,
+	BT_IO_OPT_OMTU,
+	BT_IO_OPT_IMTU,
+	BT_IO_OPT_MASTER,
+	BT_IO_OPT_HANDLE,
+	BT_IO_OPT_CLASS,
+	BT_IO_OPT_MODE,
+} BtIOOption;
+
+typedef enum {
+	BT_IO_SEC_SDP = 0,
+	BT_IO_SEC_LOW,
+	BT_IO_SEC_MEDIUM,
+	BT_IO_SEC_HIGH,
+} BtIOSecLevel;
+
+typedef void (*BtIOConfirm)(GIOChannel *io, void *user_data);
+
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, void *user_data);
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
+					GDestroyNotify destroy, GError **err);
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				void *user_data, GDestroyNotify destroy,
+				GError **err, BtIOOption opt1, ...);
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+				BtIOConfirm confirm, void *user_data,
+				GDestroyNotify destroy, GError **err,
+				BtIOOption opt1, ...);
+
+#endif
diff --git a/src/btio.c b/src/btio.c
deleted file mode 100644
index 42a3bcd..0000000
--- a/src/btio.c
+++ /dev/null
@@ -1,1299 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
- *  Copyright (C) 2009-2010  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 "btio.h"
-
-#define ERROR_FAILED(gerr, str, err) \
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
-				str ": %s (%d)", strerror(err), err)
-
-#define DEFAULT_DEFER_TIMEOUT 30
-
-struct set_opts {
-	bdaddr_t src;
-	bdaddr_t dst;
-	int defer;
-	int sec_level;
-	uint8_t channel;
-	uint16_t psm;
-	uint16_t mtu;
-	uint16_t imtu;
-	uint16_t omtu;
-	int master;
-	uint8_t mode;
-};
-
-struct connect {
-	BtIOConnect connect;
-	void *user_data;
-	GDestroyNotify destroy;
-};
-
-struct accept {
-	BtIOConnect connect;
-	void *user_data;
-	GDestroyNotify destroy;
-};
-
-struct server {
-	BtIOConnect connect;
-	BtIOConfirm confirm;
-	void *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 check_nval(GIOChannel *io)
-{
-	struct pollfd fds;
-
-	memset(&fds, 0, sizeof(fds));
-	fds.fd = g_io_channel_unix_get_fd(io);
-	fds.events = POLLNVAL;
-
-	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
-		return TRUE;
-
-	return FALSE;
-}
-
-static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
-{
-	struct accept *accept = user_data;
-	GError *err = NULL;
-
-	/* If the user aborted this accept attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
-				"HUP or ERR on socket");
-
-	accept->connect(io, err, accept->user_data);
-
-	g_clear_error(&err);
-
-	return FALSE;
-}
-
-static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
-{
-	struct connect *conn = user_data;
-	GError *gerr = NULL;
-
-	/* If the user aborted this connect attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & G_IO_OUT) {
-		int err = 0, sock = g_io_channel_unix_get_fd(io);
-		socklen_t len = sizeof(err);
-
-		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
-			err = errno;
-
-		if (err)
-			g_set_error(&gerr, BT_IO_ERROR,
-					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
-					strerror(err), err);
-	} else if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"HUP or ERR on socket");
-
-	conn->connect(io, gerr, conn->user_data);
-
-	if (gerr)
-		g_error_free(gerr);
-
-	return FALSE;
-}
-
-static gboolean server_cb(GIOChannel *io, GIOCondition cond,
-							void *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) || check_nval(io))
-		return FALSE;
-
-	srv_sock = g_io_channel_unix_get_fd(io);
-
-	cli_sock = accept(srv_sock, NULL, NULL);
-	if (cli_sock < 0)
-		return TRUE;
-
-	cli_io = g_io_channel_unix_new(cli_sock);
-
-	g_io_channel_set_close_on_unref(cli_io, TRUE);
-	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
-
-	if (server->confirm)
-		server->confirm(cli_io, server->user_data);
-	else
-		server->connect(cli_io, NULL, server->user_data);
-
-	g_io_channel_unref(cli_io);
-
-	return TRUE;
-}
-
-static void server_add(GIOChannel *io, BtIOConnect connect,
-				BtIOConfirm confirm, void *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,
-				void *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, void *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, const bdaddr_t *src, uint16_t psm, GError **err)
-{
-	struct sockaddr_l2 addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.l2_family = AF_BLUETOOTH;
-	bacpy(&addr.l2_bdaddr, src);
-	addr.l2_psm = htobs(psm);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "l2cap_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int l2cap_connect(int sock, const 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_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & L2CAP_LM_MASTER)
-			return 0;
-		flags |= L2CAP_LM_MASTER;
-	} else {
-		if (!(flags & L2CAP_LM_MASTER))
-			return 0;
-		flags &= ~L2CAP_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & RFCOMM_LM_MASTER)
-			return 0;
-		flags |= RFCOMM_LM_MASTER;
-	} else {
-		if (!(flags & RFCOMM_LM_MASTER))
-			return 0;
-		flags &= ~RFCOMM_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int l2cap_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		L2CAP_LM_AUTH,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		RFCOMM_LM_AUTH,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
-{
-	struct bt_security sec;
-	int ret;
-
-	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Valid security level range is %d-%d",
-				BT_SECURITY_LOW, BT_SECURITY_HIGH);
-		return FALSE;
-	}
-
-	memset(&sec, 0, sizeof(sec));
-	sec.level = level;
-
-	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
-							sizeof(sec)) == 0)
-		return TRUE;
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_set_lm(sock, level);
-	else
-		ret = rfcomm_set_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "setsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & L2CAP_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & L2CAP_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & L2CAP_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static int rfcomm_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & RFCOMM_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & RFCOMM_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & RFCOMM_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static gboolean get_sec_level(int sock, BtIOType type, int *level,
-								GError **err)
-{
-	struct bt_security sec;
-	socklen_t len;
-	int ret;
-
-	memset(&sec, 0, sizeof(sec));
-	len = sizeof(sec);
-	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
-		*level = sec.level;
-		return TRUE;
-	}
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_get_lm(sock, level);
-	else
-		ret = rfcomm_get_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "getsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
-					uint8_t mode, int master, GError **err)
-{
-	if (imtu || omtu || mode) {
-		struct l2cap_options l2o;
-		socklen_t len;
-
-		memset(&l2o, 0, sizeof(l2o));
-		len = sizeof(l2o);
-		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-								&len) < 0) {
-			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-
-		if (imtu)
-			l2o.imtu = imtu;
-		if (omtu)
-			l2o.omtu = omtu;
-		if (mode)
-			l2o.mode = mode;
-
-		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-							sizeof(l2o)) < 0) {
-			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-	}
-
-	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "l2cap_set_master", errno);
-		return FALSE;
-	}
-
-	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
-		return FALSE;
-
-	return TRUE;
-}
-
-static int rfcomm_bind(int sock,
-		const bdaddr_t *src, uint8_t channel, GError **err)
-{
-	struct sockaddr_rc addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.rc_family = AF_BLUETOOTH;
-	bacpy(&addr.rc_bdaddr, src);
-	addr.rc_channel = channel;
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "rfcomm_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
-{
-	int err;
-	struct sockaddr_rc addr;
-
-	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 gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
-{
-	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
-		return FALSE;
-
-	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "rfcomm_set_master", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int sco_bind(int sock, const bdaddr_t *src, GError **err)
-{
-	struct sockaddr_sco addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sco_family = AF_BLUETOOTH;
-	bacpy(&addr.sco_bdaddr, src);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "sco_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int sco_connect(int sock, const 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 gboolean sco_set(int sock, uint16_t mtu, GError **err)
-{
-	struct sco_options sco_opt;
-	socklen_t len;
-
-	if (!mtu)
-		return TRUE;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	sco_opt.mtu = mtu;
-	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
-						sizeof(sco_opt)) < 0) {
-		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean parse_set_opts(struct set_opts *opts, GError **err,
-						BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	const char *str;
-
-	memset(opts, 0, sizeof(*opts));
-
-	/* Set defaults */
-	opts->defer = DEFAULT_DEFER_TIMEOUT;
-	opts->master = -1;
-	opts->sec_level = BT_IO_SEC_MEDIUM;
-	opts->mode = L2CAP_MODE_BASIC;
-
-	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, const 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, const bdaddr_t *));
-			break;
-		case BT_IO_OPT_DEFER_TIMEOUT:
-			opts->defer = 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;
-		case BT_IO_OPT_MASTER:
-			opts->master = va_arg(args, gboolean);
-			break;
-		case BT_IO_OPT_MODE:
-			opts->mode = va_arg(args, int);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
-				socklen_t len, GError **err)
-{
-	socklen_t olen;
-
-	memset(src, 0, len);
-	olen = len;
-	if (getsockname(sock, src, &olen) < 0) {
-		ERROR_FAILED(err, "getsockname", errno);
-		return FALSE;
-	}
-
-	memset(dst, 0, len);
-	olen = len;
-	if (getpeername(sock, dst, &olen) < 0) {
-		ERROR_FAILED(err, "getpeername", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct l2cap_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_l2 src, dst;
-	struct l2cap_options l2o;
-	int flags;
-	uint8_t dev_class[3];
-	uint16_t handle;
-	socklen_t len;
-
-	len = sizeof(l2o);
-	memset(&l2o, 0, len);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		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) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_L2CAP,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_PSM:
-			*(va_arg(args, uint16_t *)) = src.l2_psm ?
-						src.l2_psm : dst.l2_psm;
-			break;
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = l2o.omtu;
-			break;
-		case BT_IO_OPT_IMTU:
-			*(va_arg(args, uint16_t *)) = l2o.imtu;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct rfcomm_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_rc src, dst;
-	int flags;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		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) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_RFCOMM,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel ?
-					src.rc_channel : dst.rc_channel;
-			break;
-		case BT_IO_OPT_SOURCE_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel;
-			break;
-		case BT_IO_OPT_DEST_CHANNEL:
-			*(va_arg(args, uint8_t *)) = dst.rc_channel;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct sco_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_sco src, dst;
-	struct sco_options sco_opt;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		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_MTU:
-		case BT_IO_OPT_IMTU:
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
-						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, err, opt1, args);
-	case BT_IO_RFCOMM:
-		return rfcomm_get(sock, err, opt1, args);
-	case BT_IO_SCO:
-		return sco_get(sock, err, opt1, args);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
-					GDestroyNotify destroy, GError **err)
-{
-	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_FAILED(err, "poll", errno);
-		return FALSE;
-	}
-
-	if (!(pfd.revents & POLLOUT)) {
-		int ret;
-		ret = read(sock, &c, 1);
-	}
-
-	accept_add(io, connect, user_data, destroy);
-
-	return TRUE;
-}
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-	struct set_opts opts;
-	int sock;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, 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:
-		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
-						opts.mode, opts.master, err);
-	case BT_IO_RFCOMM:
-		return rfcomm_set(sock, opts.sec_level, opts.master, err);
-	case BT_IO_SCO:
-		return sco_set(sock, opts.mtu, err);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = get_valist(io, type, err, opt1, args);
-	va_end(args);
-
-	return ret;
-}
-
-static GIOChannel *create_io(BtIOType type, gboolean server,
-					struct set_opts *opts, GError **err)
-{
-	int sock;
-	GIOChannel *io;
-
-	switch (type) {
-	case BT_IO_L2RAW:
-		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
-			goto failed;
-		break;
-	case BT_IO_L2CAP:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
-						opts->mode, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_RFCOMM:
-		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
-			return NULL;
-		}
-		if (rfcomm_bind(sock, &opts->src,
-					server ? opts->channel : 0, err) < 0)
-			goto failed;
-		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_SCO:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
-			return NULL;
-		}
-		if (sco_bind(sock, &opts->src, err) < 0)
-			goto failed;
-		if (!sco_set(sock, opts->mtu, err))
-			goto failed;
-		break;
-	default:
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	io = g_io_channel_unix_new(sock);
-
-	g_io_channel_set_close_on_unref(io, TRUE);
-	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
-
-	return io;
-
-failed:
-	close(sock);
-
-	return NULL;
-}
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
-				GError **gerr, BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int err, sock;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, gerr, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, FALSE, &opts, gerr);
-	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;
-	default:
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-						"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	if (err < 0) {
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"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, void *user_data,
-				GDestroyNotify destroy, GError **err,
-				BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int sock;
-	gboolean ret;
-
-	if (type == BT_IO_L2RAW) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Server L2CAP RAW sockets not supported");
-		return NULL;
-	}
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, TRUE, &opts, err);
-	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_FAILED(err, "listen", errno);
-		g_io_channel_unref(io);
-		return NULL;
-	}
-
-	server_add(io, connect, confirm, user_data, destroy);
-
-	return io;
-}
-
-GQuark bt_io_error_quark(void)
-{
-	return g_quark_from_static_string("bt-io-error-quark");
-}
diff --git a/src/btio.h b/src/btio.h
deleted file mode 100644
index e9dcc9f..0000000
--- a/src/btio.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
- *  Copyright (C) 2009-2010  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_ERROR_DISCONNECTED,
-	BT_IO_ERROR_CONNECT_FAILED,
-	BT_IO_ERROR_FAILED,
-	BT_IO_ERROR_INVALID_ARGS,
-} BtIOError;
-
-#define BT_IO_ERROR bt_io_error_quark()
-
-GQuark bt_io_error_quark(void);
-
-typedef enum {
-	BT_IO_L2RAW,
-	BT_IO_L2CAP,
-	BT_IO_RFCOMM,
-	BT_IO_SCO,
-} BtIOType;
-
-typedef enum {
-	BT_IO_OPT_INVALID = 0,
-	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_SEC_LEVEL,
-	BT_IO_OPT_CHANNEL,
-	BT_IO_OPT_SOURCE_CHANNEL,
-	BT_IO_OPT_DEST_CHANNEL,
-	BT_IO_OPT_PSM,
-	BT_IO_OPT_MTU,
-	BT_IO_OPT_OMTU,
-	BT_IO_OPT_IMTU,
-	BT_IO_OPT_MASTER,
-	BT_IO_OPT_HANDLE,
-	BT_IO_OPT_CLASS,
-	BT_IO_OPT_MODE,
-} BtIOOption;
-
-typedef enum {
-	BT_IO_SEC_SDP = 0,
-	BT_IO_SEC_LOW,
-	BT_IO_SEC_MEDIUM,
-	BT_IO_SEC_HIGH,
-} BtIOSecLevel;
-
-typedef void (*BtIOConfirm)(GIOChannel *io, void *user_data);
-
-typedef void (*BtIOConnect)(GIOChannel *io, GError *err, void *user_data);
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
-					GDestroyNotify destroy, GError **err);
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
-				GError **err, BtIOOption opt1, ...);
-
-GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
-				GDestroyNotify destroy, GError **err,
-				BtIOOption opt1, ...);
-
-#endif
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 3/3] btio: Replace void * with gpointer
From: Zhenhua Zhang @ 2010-09-10  2:47 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284086847-16429-1-git-send-email-zhenhua.zhang@intel.com>

TO make it consistent with bluez btio.c.
---
 btio/btio.c |   24 ++++++++++++------------
 btio/btio.h |   10 +++++-----
 2 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/btio/btio.c b/btio/btio.c
index 42a3bcd..8b273ca 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -62,20 +62,20 @@ struct set_opts {
 
 struct connect {
 	BtIOConnect connect;
-	void *user_data;
+	gpointer user_data;
 	GDestroyNotify destroy;
 };
 
 struct accept {
 	BtIOConnect connect;
-	void *user_data;
+	gpointer user_data;
 	GDestroyNotify destroy;
 };
 
 struct server {
 	BtIOConnect connect;
 	BtIOConfirm confirm;
-	void *user_data;
+	gpointer user_data;
 	GDestroyNotify destroy;
 };
 
@@ -115,7 +115,7 @@ static gboolean check_nval(GIOChannel *io)
 }
 
 static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
+							gpointer user_data)
 {
 	struct accept *accept = user_data;
 	GError *err = NULL;
@@ -136,7 +136,7 @@ static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
 }
 
 static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
+							gpointer user_data)
 {
 	struct connect *conn = user_data;
 	GError *gerr = NULL;
@@ -169,7 +169,7 @@ static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
 }
 
 static gboolean server_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
+							gpointer user_data)
 {
 	struct server *server = user_data;
 	int srv_sock, cli_sock;
@@ -201,7 +201,7 @@ static gboolean server_cb(GIOChannel *io, GIOCondition cond,
 }
 
 static void server_add(GIOChannel *io, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
+				BtIOConfirm confirm, gpointer user_data,
 				GDestroyNotify destroy)
 {
 	struct server *server;
@@ -219,7 +219,7 @@ static void server_add(GIOChannel *io, BtIOConnect connect,
 }
 
 static void connect_add(GIOChannel *io, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy)
+				gpointer user_data, GDestroyNotify destroy)
 {
 	struct connect *conn;
 	GIOCondition cond;
@@ -234,7 +234,7 @@ static void connect_add(GIOChannel *io, BtIOConnect connect,
 					(GDestroyNotify) connect_remove);
 }
 
-static void accept_add(GIOChannel *io, BtIOConnect connect, void *user_data,
+static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data,
 							GDestroyNotify destroy)
 {
 	struct accept *accept;
@@ -1046,7 +1046,7 @@ static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
 	return FALSE;
 }
 
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
 					GDestroyNotify destroy, GError **err)
 {
 	int sock;
@@ -1195,7 +1195,7 @@ failed:
 }
 
 GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
+				gpointer user_data, GDestroyNotify destroy,
 				GError **gerr, BtIOOption opt1, ...)
 {
 	GIOChannel *io;
@@ -1249,7 +1249,7 @@ GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
 }
 
 GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
+				BtIOConfirm confirm, gpointer user_data,
 				GDestroyNotify destroy, GError **err,
 				BtIOOption opt1, ...)
 {
diff --git a/btio/btio.h b/btio/btio.h
index e9dcc9f..d373ed1 100644
--- a/btio/btio.h
+++ b/btio/btio.h
@@ -72,11 +72,11 @@ typedef enum {
 	BT_IO_SEC_HIGH,
 } BtIOSecLevel;
 
-typedef void (*BtIOConfirm)(GIOChannel *io, void *user_data);
+typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
 
-typedef void (*BtIOConnect)(GIOChannel *io, GError *err, void *user_data);
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
 
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
 					GDestroyNotify destroy, GError **err);
 
 gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
@@ -86,11 +86,11 @@ gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
 						BtIOOption opt1, ...);
 
 GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
+				gpointer user_data, GDestroyNotify destroy,
 				GError **err, BtIOOption opt1, ...);
 
 GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
+				BtIOConfirm confirm, gpointer user_data,
 				GDestroyNotify destroy, GError **err,
 				BtIOOption opt1, ...);
 
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 0/3] bluez: isolate btio.[ch] and do some cleanup
From: Zhenhua Zhang @ 2010-09-10  2:49 UTC (permalink / raw)
  To: linux-bluetooth

Hi,

Similar with previous patch threads. These patches move bluez/src/btio.[ch] out from source directory and make it identical with obex's btio.[ch]. So we can freely copy them among bluez, obex and ofono.

Best Regards,
Zhenhua


^ permalink raw reply

* [PATCH 1/3] btio: Remove blank line at EOF
From: Zhenhua Zhang @ 2010-09-10  2:49 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284086984-17072-1-git-send-email-zhenhua.zhang@intel.com>

---
 src/btio.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/src/btio.c b/src/btio.c
index 93079ae..8b273ca 100644
--- a/src/btio.c
+++ b/src/btio.c
@@ -1297,4 +1297,3 @@ GQuark bt_io_error_quark(void)
 {
 	return g_quark_from_static_string("bt-io-error-quark");
 }
-
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 2/3] btio: Seperate btio.[ch] into btio directory
From: Zhenhua Zhang @ 2010-09-10  2:49 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284086984-17072-1-git-send-email-zhenhua.zhang@intel.com>

Seperate btio.[ch] from src directory to btio sub-folder.
---
 Makefile.am    |    8 +-
 Makefile.tools |    2 +-
 btio/btio.c    | 1299 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 btio/btio.h    |   94 ++++
 src/btio.c     | 1299 --------------------------------------------------------
 src/btio.h     |   94 ----
 6 files changed, 1399 insertions(+), 1397 deletions(-)
 create mode 100644 btio/btio.c
 create mode 100644 btio/btio.h
 delete mode 100644 src/btio.c
 delete mode 100644 src/btio.h

diff --git a/Makefile.am b/Makefile.am
index 7895be2..a8829d9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -102,6 +102,8 @@ endif
 gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
 					gdbus/object.c gdbus/polkit.c
 
+btio_sources = btio/btio.h btio/btio.c
+
 builtin_modules =
 builtin_sources =
 builtin_nodist =
@@ -216,14 +218,14 @@ endif
 sbin_PROGRAMS += src/bluetoothd
 
 src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
-			$(attrib_sources) \
+			$(attrib_sources) $(btio_sources) \
 			$(mcap_sources) \
 			src/main.c src/log.h src/log.c \
 			src/security.c src/rfkill.c src/hcid.h src/sdpd.h \
 			src/sdpd-server.c src/sdpd-request.c \
 			src/sdpd-service.c src/sdpd-database.c \
 			src/attrib-server.h src/attrib-server.c \
-			src/sdp-xml.h src/sdp-xml.c src/btio.h src/btio.c \
+			src/sdp-xml.h src/sdp-xml.c \
 			src/textfile.h src/textfile.c \
 			src/glib-helper.h src/glib-helper.c \
 			src/oui.h src/oui.c src/uinput.h src/ppoll.h \
@@ -357,7 +359,7 @@ AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ \
 
 INCLUDES = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
 			-I$(srcdir)/audio -I$(srcdir)/sbc -I$(srcdir)/gdbus \
-			-I$(srcdir)/attrib
+			-I$(srcdir)/attrib -I$(srcdir)/btio
 
 if MCAP
 	INCLUDES += -I$(builddir)/health
diff --git a/Makefile.tools b/Makefile.tools
index 1c46542..14ecf31 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -170,7 +170,7 @@ test_bdaddr_LDADD = lib/libbluetooth.la
 
 test_agent_LDADD = @DBUS_LIBS@
 
-test_btiotest_SOURCES = test/btiotest.c src/btio.h src/btio.c
+test_btiotest_SOURCES = test/btiotest.c btio/btio.h btio/btio.c
 test_btiotest_LDADD = @GLIB_LIBS@ lib/libbluetooth.la
 
 test_test_textfile_SOURCES = test/test-textfile.c src/textfile.h src/textfile.c
diff --git a/btio/btio.c b/btio/btio.c
new file mode 100644
index 0000000..8b273ca
--- /dev/null
+++ b/btio/btio.c
@@ -0,0 +1,1299 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  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 "btio.h"
+
+#define ERROR_FAILED(gerr, str, err) \
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
+				str ": %s (%d)", strerror(err), err)
+
+#define DEFAULT_DEFER_TIMEOUT 30
+
+struct set_opts {
+	bdaddr_t src;
+	bdaddr_t dst;
+	int defer;
+	int sec_level;
+	uint8_t channel;
+	uint16_t psm;
+	uint16_t mtu;
+	uint16_t imtu;
+	uint16_t omtu;
+	int master;
+	uint8_t mode;
+};
+
+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 check_nval(GIOChannel *io)
+{
+	struct pollfd fds;
+
+	memset(&fds, 0, sizeof(fds));
+	fds.fd = g_io_channel_unix_get_fd(io);
+	fds.events = POLLNVAL;
+
+	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct accept *accept = user_data;
+	GError *err = NULL;
+
+	/* If the user aborted this accept attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
+				"HUP or ERR on socket");
+
+	accept->connect(io, err, accept->user_data);
+
+	g_clear_error(&err);
+
+	return FALSE;
+}
+
+static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct connect *conn = user_data;
+	GError *gerr = NULL;
+
+	/* If the user aborted this connect attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & G_IO_OUT) {
+		int err = 0, sock = g_io_channel_unix_get_fd(io);
+		socklen_t len = sizeof(err);
+
+		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
+			err = errno;
+
+		if (err)
+			g_set_error(&gerr, BT_IO_ERROR,
+					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
+					strerror(err), err);
+	} else if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"HUP or ERR on socket");
+
+	conn->connect(io, gerr, conn->user_data);
+
+	if (gerr)
+		g_error_free(gerr);
+
+	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) || check_nval(io))
+		return FALSE;
+
+	srv_sock = g_io_channel_unix_get_fd(io);
+
+	cli_sock = accept(srv_sock, NULL, NULL);
+	if (cli_sock < 0)
+		return TRUE;
+
+	cli_io = g_io_channel_unix_new(cli_sock);
+
+	g_io_channel_set_close_on_unref(cli_io, TRUE);
+	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
+
+	if (server->confirm)
+		server->confirm(cli_io, server->user_data);
+	else
+		server->connect(cli_io, NULL, 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, const bdaddr_t *src, uint16_t psm, GError **err)
+{
+	struct sockaddr_l2 addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+	addr.l2_psm = htobs(psm);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "l2cap_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int l2cap_connect(int sock, const 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_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & L2CAP_LM_MASTER)
+			return 0;
+		flags |= L2CAP_LM_MASTER;
+	} else {
+		if (!(flags & L2CAP_LM_MASTER))
+			return 0;
+		flags &= ~L2CAP_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & RFCOMM_LM_MASTER)
+			return 0;
+		flags |= RFCOMM_LM_MASTER;
+	} else {
+		if (!(flags & RFCOMM_LM_MASTER))
+			return 0;
+		flags &= ~RFCOMM_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int l2cap_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		L2CAP_LM_AUTH,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		RFCOMM_LM_AUTH,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
+{
+	struct bt_security sec;
+	int ret;
+
+	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Valid security level range is %d-%d",
+				BT_SECURITY_LOW, BT_SECURITY_HIGH);
+		return FALSE;
+	}
+
+	memset(&sec, 0, sizeof(sec));
+	sec.level = level;
+
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
+							sizeof(sec)) == 0)
+		return TRUE;
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_set_lm(sock, level);
+	else
+		ret = rfcomm_set_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "setsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & L2CAP_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & L2CAP_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & L2CAP_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static int rfcomm_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & RFCOMM_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & RFCOMM_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & RFCOMM_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static gboolean get_sec_level(int sock, BtIOType type, int *level,
+								GError **err)
+{
+	struct bt_security sec;
+	socklen_t len;
+	int ret;
+
+	memset(&sec, 0, sizeof(sec));
+	len = sizeof(sec);
+	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+		*level = sec.level;
+		return TRUE;
+	}
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_get_lm(sock, level);
+	else
+		ret = rfcomm_get_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "getsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
+					uint8_t mode, int master, GError **err)
+{
+	if (imtu || omtu || mode) {
+		struct l2cap_options l2o;
+		socklen_t len;
+
+		memset(&l2o, 0, sizeof(l2o));
+		len = sizeof(l2o);
+		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+								&len) < 0) {
+			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+
+		if (imtu)
+			l2o.imtu = imtu;
+		if (omtu)
+			l2o.omtu = omtu;
+		if (mode)
+			l2o.mode = mode;
+
+		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+							sizeof(l2o)) < 0) {
+			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+	}
+
+	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "l2cap_set_master", errno);
+		return FALSE;
+	}
+
+	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
+		return FALSE;
+
+	return TRUE;
+}
+
+static int rfcomm_bind(int sock,
+		const bdaddr_t *src, uint8_t channel, GError **err)
+{
+	struct sockaddr_rc addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, src);
+	addr.rc_channel = channel;
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "rfcomm_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
+{
+	int err;
+	struct sockaddr_rc addr;
+
+	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 gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
+{
+	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
+		return FALSE;
+
+	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "rfcomm_set_master", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int sco_bind(int sock, const bdaddr_t *src, GError **err)
+{
+	struct sockaddr_sco addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, src);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "sco_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sco_connect(int sock, const 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 gboolean sco_set(int sock, uint16_t mtu, GError **err)
+{
+	struct sco_options sco_opt;
+	socklen_t len;
+
+	if (!mtu)
+		return TRUE;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	sco_opt.mtu = mtu;
+	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
+						sizeof(sco_opt)) < 0) {
+		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean parse_set_opts(struct set_opts *opts, GError **err,
+						BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	const char *str;
+
+	memset(opts, 0, sizeof(*opts));
+
+	/* Set defaults */
+	opts->defer = DEFAULT_DEFER_TIMEOUT;
+	opts->master = -1;
+	opts->sec_level = BT_IO_SEC_MEDIUM;
+	opts->mode = L2CAP_MODE_BASIC;
+
+	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, const 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, const bdaddr_t *));
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			opts->defer = 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;
+		case BT_IO_OPT_MASTER:
+			opts->master = va_arg(args, gboolean);
+			break;
+		case BT_IO_OPT_MODE:
+			opts->mode = va_arg(args, int);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
+				socklen_t len, GError **err)
+{
+	socklen_t olen;
+
+	memset(src, 0, len);
+	olen = len;
+	if (getsockname(sock, src, &olen) < 0) {
+		ERROR_FAILED(err, "getsockname", errno);
+		return FALSE;
+	}
+
+	memset(dst, 0, len);
+	olen = len;
+	if (getpeername(sock, dst, &olen) < 0) {
+		ERROR_FAILED(err, "getpeername", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct l2cap_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_l2 src, dst;
+	struct l2cap_options l2o;
+	int flags;
+	uint8_t dev_class[3];
+	uint16_t handle;
+	socklen_t len;
+
+	len = sizeof(l2o);
+	memset(&l2o, 0, len);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		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) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_L2CAP,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_PSM:
+			*(va_arg(args, uint16_t *)) = src.l2_psm ?
+						src.l2_psm : dst.l2_psm;
+			break;
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = l2o.omtu;
+			break;
+		case BT_IO_OPT_IMTU:
+			*(va_arg(args, uint16_t *)) = l2o.imtu;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct rfcomm_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_rc src, dst;
+	int flags;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		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) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_RFCOMM,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel ?
+					src.rc_channel : dst.rc_channel;
+			break;
+		case BT_IO_OPT_SOURCE_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel;
+			break;
+		case BT_IO_OPT_DEST_CHANNEL:
+			*(va_arg(args, uint8_t *)) = dst.rc_channel;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct sco_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_sco src, dst;
+	struct sco_options sco_opt;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		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_MTU:
+		case BT_IO_OPT_IMTU:
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
+						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, err, opt1, args);
+	case BT_IO_RFCOMM:
+		return rfcomm_get(sock, err, opt1, args);
+	case BT_IO_SCO:
+		return sco_get(sock, err, opt1, args);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+					GDestroyNotify destroy, GError **err)
+{
+	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_FAILED(err, "poll", errno);
+		return FALSE;
+	}
+
+	if (!(pfd.revents & POLLOUT)) {
+		int ret;
+		ret = read(sock, &c, 1);
+	}
+
+	accept_add(io, connect, user_data, destroy);
+
+	return TRUE;
+}
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+	struct set_opts opts;
+	int sock;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, 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:
+		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
+						opts.mode, opts.master, err);
+	case BT_IO_RFCOMM:
+		return rfcomm_set(sock, opts.sec_level, opts.master, err);
+	case BT_IO_SCO:
+		return sco_set(sock, opts.mtu, err);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = get_valist(io, type, err, opt1, args);
+	va_end(args);
+
+	return ret;
+}
+
+static GIOChannel *create_io(BtIOType type, gboolean server,
+					struct set_opts *opts, GError **err)
+{
+	int sock;
+	GIOChannel *io;
+
+	switch (type) {
+	case BT_IO_L2RAW:
+		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
+			goto failed;
+		break;
+	case BT_IO_L2CAP:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
+						opts->mode, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_RFCOMM:
+		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
+			return NULL;
+		}
+		if (rfcomm_bind(sock, &opts->src,
+					server ? opts->channel : 0, err) < 0)
+			goto failed;
+		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_SCO:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
+			return NULL;
+		}
+		if (sco_bind(sock, &opts->src, err) < 0)
+			goto failed;
+		if (!sco_set(sock, opts->mtu, err))
+			goto failed;
+		break;
+	default:
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	io = g_io_channel_unix_new(sock);
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+	return io;
+
+failed:
+	close(sock);
+
+	return NULL;
+}
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				gpointer user_data, GDestroyNotify destroy,
+				GError **gerr, BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int err, sock;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, gerr, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, FALSE, &opts, gerr);
+	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;
+	default:
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+						"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	if (err < 0) {
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"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, GError **err,
+				BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int sock;
+	gboolean ret;
+
+	if (type == BT_IO_L2RAW) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Server L2CAP RAW sockets not supported");
+		return NULL;
+	}
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, TRUE, &opts, err);
+	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_FAILED(err, "listen", errno);
+		g_io_channel_unref(io);
+		return NULL;
+	}
+
+	server_add(io, connect, confirm, user_data, destroy);
+
+	return io;
+}
+
+GQuark bt_io_error_quark(void)
+{
+	return g_quark_from_static_string("bt-io-error-quark");
+}
diff --git a/btio/btio.h b/btio/btio.h
new file mode 100644
index 0000000..81fda8e
--- /dev/null
+++ b/btio/btio.h
@@ -0,0 +1,94 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  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 <glib.h>
+
+typedef enum {
+	BT_IO_ERROR_DISCONNECTED,
+	BT_IO_ERROR_CONNECT_FAILED,
+	BT_IO_ERROR_FAILED,
+	BT_IO_ERROR_INVALID_ARGS,
+} BtIOError;
+
+#define BT_IO_ERROR bt_io_error_quark()
+
+GQuark bt_io_error_quark(void);
+
+typedef enum {
+	BT_IO_L2RAW,
+	BT_IO_L2CAP,
+	BT_IO_RFCOMM,
+	BT_IO_SCO,
+} BtIOType;
+
+typedef enum {
+	BT_IO_OPT_INVALID = 0,
+	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_SEC_LEVEL,
+	BT_IO_OPT_CHANNEL,
+	BT_IO_OPT_SOURCE_CHANNEL,
+	BT_IO_OPT_DEST_CHANNEL,
+	BT_IO_OPT_PSM,
+	BT_IO_OPT_MTU,
+	BT_IO_OPT_OMTU,
+	BT_IO_OPT_IMTU,
+	BT_IO_OPT_MASTER,
+	BT_IO_OPT_HANDLE,
+	BT_IO_OPT_CLASS,
+	BT_IO_OPT_MODE,
+} BtIOOption;
+
+typedef enum {
+	BT_IO_SEC_SDP = 0,
+	BT_IO_SEC_LOW,
+	BT_IO_SEC_MEDIUM,
+	BT_IO_SEC_HIGH,
+} BtIOSecLevel;
+
+typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
+
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+					GDestroyNotify destroy, GError **err);
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				gpointer user_data, GDestroyNotify destroy,
+				GError **err, BtIOOption opt1, ...);
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+				BtIOConfirm confirm, gpointer user_data,
+				GDestroyNotify destroy, GError **err,
+				BtIOOption opt1, ...);
+
diff --git a/src/btio.c b/src/btio.c
deleted file mode 100644
index 8b273ca..0000000
--- a/src/btio.c
+++ /dev/null
@@ -1,1299 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
- *  Copyright (C) 2009-2010  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 "btio.h"
-
-#define ERROR_FAILED(gerr, str, err) \
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
-				str ": %s (%d)", strerror(err), err)
-
-#define DEFAULT_DEFER_TIMEOUT 30
-
-struct set_opts {
-	bdaddr_t src;
-	bdaddr_t dst;
-	int defer;
-	int sec_level;
-	uint8_t channel;
-	uint16_t psm;
-	uint16_t mtu;
-	uint16_t imtu;
-	uint16_t omtu;
-	int master;
-	uint8_t mode;
-};
-
-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 check_nval(GIOChannel *io)
-{
-	struct pollfd fds;
-
-	memset(&fds, 0, sizeof(fds));
-	fds.fd = g_io_channel_unix_get_fd(io);
-	fds.events = POLLNVAL;
-
-	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
-		return TRUE;
-
-	return FALSE;
-}
-
-static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
-							gpointer user_data)
-{
-	struct accept *accept = user_data;
-	GError *err = NULL;
-
-	/* If the user aborted this accept attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
-				"HUP or ERR on socket");
-
-	accept->connect(io, err, accept->user_data);
-
-	g_clear_error(&err);
-
-	return FALSE;
-}
-
-static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
-							gpointer user_data)
-{
-	struct connect *conn = user_data;
-	GError *gerr = NULL;
-
-	/* If the user aborted this connect attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & G_IO_OUT) {
-		int err = 0, sock = g_io_channel_unix_get_fd(io);
-		socklen_t len = sizeof(err);
-
-		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
-			err = errno;
-
-		if (err)
-			g_set_error(&gerr, BT_IO_ERROR,
-					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
-					strerror(err), err);
-	} else if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"HUP or ERR on socket");
-
-	conn->connect(io, gerr, conn->user_data);
-
-	if (gerr)
-		g_error_free(gerr);
-
-	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) || check_nval(io))
-		return FALSE;
-
-	srv_sock = g_io_channel_unix_get_fd(io);
-
-	cli_sock = accept(srv_sock, NULL, NULL);
-	if (cli_sock < 0)
-		return TRUE;
-
-	cli_io = g_io_channel_unix_new(cli_sock);
-
-	g_io_channel_set_close_on_unref(cli_io, TRUE);
-	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
-
-	if (server->confirm)
-		server->confirm(cli_io, server->user_data);
-	else
-		server->connect(cli_io, NULL, 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, const bdaddr_t *src, uint16_t psm, GError **err)
-{
-	struct sockaddr_l2 addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.l2_family = AF_BLUETOOTH;
-	bacpy(&addr.l2_bdaddr, src);
-	addr.l2_psm = htobs(psm);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "l2cap_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int l2cap_connect(int sock, const 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_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & L2CAP_LM_MASTER)
-			return 0;
-		flags |= L2CAP_LM_MASTER;
-	} else {
-		if (!(flags & L2CAP_LM_MASTER))
-			return 0;
-		flags &= ~L2CAP_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & RFCOMM_LM_MASTER)
-			return 0;
-		flags |= RFCOMM_LM_MASTER;
-	} else {
-		if (!(flags & RFCOMM_LM_MASTER))
-			return 0;
-		flags &= ~RFCOMM_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int l2cap_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		L2CAP_LM_AUTH,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		RFCOMM_LM_AUTH,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
-{
-	struct bt_security sec;
-	int ret;
-
-	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Valid security level range is %d-%d",
-				BT_SECURITY_LOW, BT_SECURITY_HIGH);
-		return FALSE;
-	}
-
-	memset(&sec, 0, sizeof(sec));
-	sec.level = level;
-
-	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
-							sizeof(sec)) == 0)
-		return TRUE;
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_set_lm(sock, level);
-	else
-		ret = rfcomm_set_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "setsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & L2CAP_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & L2CAP_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & L2CAP_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static int rfcomm_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & RFCOMM_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & RFCOMM_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & RFCOMM_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static gboolean get_sec_level(int sock, BtIOType type, int *level,
-								GError **err)
-{
-	struct bt_security sec;
-	socklen_t len;
-	int ret;
-
-	memset(&sec, 0, sizeof(sec));
-	len = sizeof(sec);
-	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
-		*level = sec.level;
-		return TRUE;
-	}
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_get_lm(sock, level);
-	else
-		ret = rfcomm_get_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "getsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
-					uint8_t mode, int master, GError **err)
-{
-	if (imtu || omtu || mode) {
-		struct l2cap_options l2o;
-		socklen_t len;
-
-		memset(&l2o, 0, sizeof(l2o));
-		len = sizeof(l2o);
-		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-								&len) < 0) {
-			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-
-		if (imtu)
-			l2o.imtu = imtu;
-		if (omtu)
-			l2o.omtu = omtu;
-		if (mode)
-			l2o.mode = mode;
-
-		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-							sizeof(l2o)) < 0) {
-			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-	}
-
-	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "l2cap_set_master", errno);
-		return FALSE;
-	}
-
-	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
-		return FALSE;
-
-	return TRUE;
-}
-
-static int rfcomm_bind(int sock,
-		const bdaddr_t *src, uint8_t channel, GError **err)
-{
-	struct sockaddr_rc addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.rc_family = AF_BLUETOOTH;
-	bacpy(&addr.rc_bdaddr, src);
-	addr.rc_channel = channel;
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "rfcomm_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
-{
-	int err;
-	struct sockaddr_rc addr;
-
-	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 gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
-{
-	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
-		return FALSE;
-
-	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "rfcomm_set_master", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int sco_bind(int sock, const bdaddr_t *src, GError **err)
-{
-	struct sockaddr_sco addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sco_family = AF_BLUETOOTH;
-	bacpy(&addr.sco_bdaddr, src);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "sco_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int sco_connect(int sock, const 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 gboolean sco_set(int sock, uint16_t mtu, GError **err)
-{
-	struct sco_options sco_opt;
-	socklen_t len;
-
-	if (!mtu)
-		return TRUE;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	sco_opt.mtu = mtu;
-	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
-						sizeof(sco_opt)) < 0) {
-		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean parse_set_opts(struct set_opts *opts, GError **err,
-						BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	const char *str;
-
-	memset(opts, 0, sizeof(*opts));
-
-	/* Set defaults */
-	opts->defer = DEFAULT_DEFER_TIMEOUT;
-	opts->master = -1;
-	opts->sec_level = BT_IO_SEC_MEDIUM;
-	opts->mode = L2CAP_MODE_BASIC;
-
-	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, const 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, const bdaddr_t *));
-			break;
-		case BT_IO_OPT_DEFER_TIMEOUT:
-			opts->defer = 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;
-		case BT_IO_OPT_MASTER:
-			opts->master = va_arg(args, gboolean);
-			break;
-		case BT_IO_OPT_MODE:
-			opts->mode = va_arg(args, int);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
-				socklen_t len, GError **err)
-{
-	socklen_t olen;
-
-	memset(src, 0, len);
-	olen = len;
-	if (getsockname(sock, src, &olen) < 0) {
-		ERROR_FAILED(err, "getsockname", errno);
-		return FALSE;
-	}
-
-	memset(dst, 0, len);
-	olen = len;
-	if (getpeername(sock, dst, &olen) < 0) {
-		ERROR_FAILED(err, "getpeername", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct l2cap_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_l2 src, dst;
-	struct l2cap_options l2o;
-	int flags;
-	uint8_t dev_class[3];
-	uint16_t handle;
-	socklen_t len;
-
-	len = sizeof(l2o);
-	memset(&l2o, 0, len);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		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) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_L2CAP,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_PSM:
-			*(va_arg(args, uint16_t *)) = src.l2_psm ?
-						src.l2_psm : dst.l2_psm;
-			break;
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = l2o.omtu;
-			break;
-		case BT_IO_OPT_IMTU:
-			*(va_arg(args, uint16_t *)) = l2o.imtu;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct rfcomm_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_rc src, dst;
-	int flags;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		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) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_RFCOMM,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel ?
-					src.rc_channel : dst.rc_channel;
-			break;
-		case BT_IO_OPT_SOURCE_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel;
-			break;
-		case BT_IO_OPT_DEST_CHANNEL:
-			*(va_arg(args, uint8_t *)) = dst.rc_channel;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct sco_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_sco src, dst;
-	struct sco_options sco_opt;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		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_MTU:
-		case BT_IO_OPT_IMTU:
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
-						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, err, opt1, args);
-	case BT_IO_RFCOMM:
-		return rfcomm_get(sock, err, opt1, args);
-	case BT_IO_SCO:
-		return sco_get(sock, err, opt1, args);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
-					GDestroyNotify destroy, GError **err)
-{
-	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_FAILED(err, "poll", errno);
-		return FALSE;
-	}
-
-	if (!(pfd.revents & POLLOUT)) {
-		int ret;
-		ret = read(sock, &c, 1);
-	}
-
-	accept_add(io, connect, user_data, destroy);
-
-	return TRUE;
-}
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-	struct set_opts opts;
-	int sock;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, 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:
-		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
-						opts.mode, opts.master, err);
-	case BT_IO_RFCOMM:
-		return rfcomm_set(sock, opts.sec_level, opts.master, err);
-	case BT_IO_SCO:
-		return sco_set(sock, opts.mtu, err);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = get_valist(io, type, err, opt1, args);
-	va_end(args);
-
-	return ret;
-}
-
-static GIOChannel *create_io(BtIOType type, gboolean server,
-					struct set_opts *opts, GError **err)
-{
-	int sock;
-	GIOChannel *io;
-
-	switch (type) {
-	case BT_IO_L2RAW:
-		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
-			goto failed;
-		break;
-	case BT_IO_L2CAP:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
-						opts->mode, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_RFCOMM:
-		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
-			return NULL;
-		}
-		if (rfcomm_bind(sock, &opts->src,
-					server ? opts->channel : 0, err) < 0)
-			goto failed;
-		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_SCO:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
-			return NULL;
-		}
-		if (sco_bind(sock, &opts->src, err) < 0)
-			goto failed;
-		if (!sco_set(sock, opts->mtu, err))
-			goto failed;
-		break;
-	default:
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	io = g_io_channel_unix_new(sock);
-
-	g_io_channel_set_close_on_unref(io, TRUE);
-	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
-
-	return io;
-
-failed:
-	close(sock);
-
-	return NULL;
-}
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				gpointer user_data, GDestroyNotify destroy,
-				GError **gerr, BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int err, sock;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, gerr, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, FALSE, &opts, gerr);
-	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;
-	default:
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-						"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	if (err < 0) {
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"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, GError **err,
-				BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int sock;
-	gboolean ret;
-
-	if (type == BT_IO_L2RAW) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Server L2CAP RAW sockets not supported");
-		return NULL;
-	}
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, TRUE, &opts, err);
-	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_FAILED(err, "listen", errno);
-		g_io_channel_unref(io);
-		return NULL;
-	}
-
-	server_add(io, connect, confirm, user_data, destroy);
-
-	return io;
-}
-
-GQuark bt_io_error_quark(void)
-{
-	return g_quark_from_static_string("bt-io-error-quark");
-}
diff --git a/src/btio.h b/src/btio.h
deleted file mode 100644
index 81fda8e..0000000
--- a/src/btio.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
- *  Copyright (C) 2009-2010  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 <glib.h>
-
-typedef enum {
-	BT_IO_ERROR_DISCONNECTED,
-	BT_IO_ERROR_CONNECT_FAILED,
-	BT_IO_ERROR_FAILED,
-	BT_IO_ERROR_INVALID_ARGS,
-} BtIOError;
-
-#define BT_IO_ERROR bt_io_error_quark()
-
-GQuark bt_io_error_quark(void);
-
-typedef enum {
-	BT_IO_L2RAW,
-	BT_IO_L2CAP,
-	BT_IO_RFCOMM,
-	BT_IO_SCO,
-} BtIOType;
-
-typedef enum {
-	BT_IO_OPT_INVALID = 0,
-	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_SEC_LEVEL,
-	BT_IO_OPT_CHANNEL,
-	BT_IO_OPT_SOURCE_CHANNEL,
-	BT_IO_OPT_DEST_CHANNEL,
-	BT_IO_OPT_PSM,
-	BT_IO_OPT_MTU,
-	BT_IO_OPT_OMTU,
-	BT_IO_OPT_IMTU,
-	BT_IO_OPT_MASTER,
-	BT_IO_OPT_HANDLE,
-	BT_IO_OPT_CLASS,
-	BT_IO_OPT_MODE,
-} BtIOOption;
-
-typedef enum {
-	BT_IO_SEC_SDP = 0,
-	BT_IO_SEC_LOW,
-	BT_IO_SEC_MEDIUM,
-	BT_IO_SEC_HIGH,
-} BtIOSecLevel;
-
-typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
-
-typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
-					GDestroyNotify destroy, GError **err);
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				gpointer user_data, GDestroyNotify destroy,
-				GError **err, BtIOOption opt1, ...);
-
-GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, gpointer user_data,
-				GDestroyNotify destroy, GError **err,
-				BtIOOption opt1, ...);
-
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 3/3] btio: Add ifndef/endif guard for btio.h
From: Zhenhua Zhang @ 2010-09-10  2:49 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284086984-17072-1-git-send-email-zhenhua.zhang@intel.com>

To avoid circular inclusion of include file.
---
 btio/btio.h |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/btio/btio.h b/btio/btio.h
index 81fda8e..d373ed1 100644
--- a/btio/btio.h
+++ b/btio/btio.h
@@ -21,6 +21,8 @@
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
+#ifndef BT_IO_H
+#define BT_IO_H
 
 #include <glib.h>
 
@@ -92,3 +94,4 @@ GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
 				GDestroyNotify destroy, GError **err,
 				BtIOOption opt1, ...);
 
+#endif
-- 
1.7.0.4


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox