* [PATCH 2/6] bluetooth: Add Btio library for DUN
2010-09-13 5:21 [PATCH 0/6]Add DUN emulator in oFono Zhenhua Zhang
2010-09-13 5:21 ` [PATCH 1/6] bluetooth: Add reference count for bluetooth utils Zhenhua Zhang
@ 2010-09-13 5:21 ` Zhenhua Zhang
2010-09-13 5:21 ` [PATCH 3/6] bluetooth: Add bluetooth server support " Zhenhua Zhang
` (3 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Zhenhua Zhang @ 2010-09-13 5:21 UTC (permalink / raw)
To: ofono
[-- Attachment #1: Type: text/plain, Size: 37052 bytes --]
Btio library is the low level socket API for BT RFCOMM connection. We
share the same library among BlueZ, Obex and oFono. So make sure you
synchronize to other two projects when you make changes to btio.[ch].
---
Makefile.am | 8 +-
btio/btio.c | 1299 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
btio/btio.h | 97 +++++
configure.ac | 5 +
4 files changed, 1407 insertions(+), 2 deletions(-)
create mode 100644 btio/btio.c
create mode 100644 btio/btio.h
diff --git a/Makefile.am b/Makefile.am
index 802e94b..d0be7b8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -72,6 +72,8 @@ gatchat_sources = gatchat/gatchat.h gatchat/gatchat.c \
gatchat/ppp_auth.c gatchat/ppp_net.c \
gatchat/ppp_ipcp.c
+btio_sources = btio/btio.h btio/btio.c
+
udev_files = plugins/ofono.rules
if UDEV
@@ -286,7 +288,8 @@ src_ofonod_SOURCES = $(gdbus_sources) $(builtin_sources) \
src/nettime.c src/stkagent.c src/stkagent.h \
src/simfs.c src/simfs.h
-src_ofonod_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@ -ldl
+src_ofonod_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@ \
+ @BLUEZ_LIBS@ -ldl
src_ofonod_LDFLAGS = -Wl,--export-dynamic -Wl,--version-script=src/ofono.ver
@@ -308,7 +311,8 @@ AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ $(builtin_cflags) \
-DPLUGINDIR=\""$(build_plugindir)"\"
INCLUDES = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/src \
- -I$(srcdir)/gdbus -I$(srcdir)/gisi -I$(srcdir)/gatchat
+ -I$(srcdir)/gdbus -I$(srcdir)/gisi -I$(srcdir)/gatchat \
+ -I$(srcdir)/btio
doc_files = doc/overview.txt doc/ofono-paper.txt \
doc/manager-api.txt doc/modem-api.txt doc/network-api.txt \
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..d373ed1
--- /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, 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, ...);
+
+#endif
diff --git a/configure.ac b/configure.ac
index bd1dacb..4acd9c8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -105,6 +105,11 @@ else
fi
AC_SUBST(DBUS_DATADIR)
+PKG_CHECK_MODULES(BLUEZ, bluez, dummy=yes,
+ AC_MSG_ERROR(libbluetooth is required))
+AC_SUBST(BLUEZ_CFLAGS)
+AC_SUBST(BLUEZ_LIBS)
+
AC_ARG_ENABLE(udev, AC_HELP_STRING([--disable-udev],
[don't use udev support even if available]),
[enable_udev=${enableval}])
--
1.7.0.4
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 3/6] bluetooth: Add bluetooth server support for DUN
2010-09-13 5:21 [PATCH 0/6]Add DUN emulator in oFono Zhenhua Zhang
2010-09-13 5:21 ` [PATCH 1/6] bluetooth: Add reference count for bluetooth utils Zhenhua Zhang
2010-09-13 5:21 ` [PATCH 2/6] bluetooth: Add Btio library for DUN Zhenhua Zhang
@ 2010-09-13 5:21 ` Zhenhua Zhang
2010-09-13 5:21 ` [PATCH 4/6] modem: Add method to get modem by path Zhenhua Zhang
` (2 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Zhenhua Zhang @ 2010-09-13 5:21 UTC (permalink / raw)
To: ofono
[-- Attachment #1: Type: text/plain, Size: 12937 bytes --]
It watches Bluetooth adapter property changes and addes DUN record to
listen DUN client connection request.
---
plugins/bluetooth.c | 379 +++++++++++++++++++++++++++++++++++++++++++++++++++
plugins/bluetooth.h | 14 ++
2 files changed, 393 insertions(+), 0 deletions(-)
diff --git a/plugins/bluetooth.c b/plugins/bluetooth.c
index 10cc49d..0577747 100644
--- a/plugins/bluetooth.c
+++ b/plugins/bluetooth.c
@@ -34,13 +34,72 @@
#include <ofono/dbus.h>
+#include <btio.h>
#include "bluetooth.h"
static DBusConnection *connection;
static GHashTable *uuid_hash = NULL;
static GHashTable *adapter_address_hash = NULL;
+static GSList *server_list = NULL;
static gint ref_count;
+static const gchar *dun_record = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
+<record> \
+ <attribute id=\"0x0001\"> \
+ <sequence> \
+ <uuid value=\"0x1103\"/> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0004\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\"/> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0003\"/> \
+ <uint8 value=\"%u\" name=\"channel\"/> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0008\"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0009\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x1103\"/> \
+ <uint16 value=\"0x0100\" name=\"version\"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0100\"> \
+ <text value=\"%s\" name=\"name\"/> \
+ </attribute> \
+</record>";
+
+#define TIMEOUT (60*1000) /* Timeout for user response (miliseconds) */
+
+struct client {
+ GIOChannel *io;
+ guint watch;
+};
+
+struct server {
+ guint16 service;
+ gchar *name;
+ guint8 channel;
+ GIOChannel *io;
+ gchar *adapter;
+ guint handle;
+ ConnectFunc connect_cb;
+ gpointer user_data;
+
+ struct client client;
+};
+
void bluetooth_create_path(const char *dev_addr, const char *adapter_addr,
char *buf, int size)
{
@@ -370,6 +429,253 @@ static gboolean property_changed(DBusConnection *connection, DBusMessage *msg,
return TRUE;
}
+static void disconnect(struct server *server)
+{
+ struct client *client = &server->client;
+
+ if (!client->io)
+ return;
+
+ g_io_channel_unref(client->io);
+ client->io = NULL;
+
+ if (client->watch > 0) {
+ g_source_remove(client->watch);
+ client->watch = 0;
+ }
+
+ return;
+}
+
+static void server_stop(gpointer data, gpointer user_data)
+{
+ struct server *server = data;
+
+ disconnect(server);
+
+ if (server->handle) {
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(BLUEZ_SERVICE,
+ server->adapter,
+ BLUEZ_SERVICE_INTERFACE,
+ "RemoveRecord");
+ dbus_message_append_args(msg, DBUS_TYPE_UINT32, &server->handle,
+ DBUS_TYPE_INVALID);
+ g_dbus_send_message(connection, msg);
+
+ server->handle = 0;
+ }
+
+ if (server->io) {
+ g_io_channel_shutdown(server->io, TRUE, NULL);
+ g_io_channel_unref(server->io);
+ server->io = NULL;
+ }
+
+ g_free(server->adapter);
+ server->adapter = NULL;
+}
+
+static gboolean client_event(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ struct server *server = data;
+
+ disconnect(server);
+
+ return FALSE;
+}
+
+static void cancel_authorization(struct server *server)
+{
+ DBusMessage *msg;
+
+ if (!server->adapter)
+ return;
+
+ msg = dbus_message_new_method_call(BLUEZ_SERVICE, server->adapter,
+ BLUEZ_SERVICE_INTERFACE,
+ "CancelAuthorization");
+
+ g_dbus_send_message(connection, msg);
+}
+
+static void auth_cb(DBusPendingCall *call, gpointer user_data)
+{
+ struct server *server = user_data;
+ struct client *client = &server->client;
+ GIOChannel *io = client->io;
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError derr;
+ GError *err = NULL;
+
+ dbus_error_init(&derr);
+
+ if (dbus_set_error_from_message(&derr, reply)) {
+ ofono_error("RequestAuthorization error: %s, %s",
+ derr.name, derr.message);
+
+ if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY))
+ cancel_authorization(server);
+
+ dbus_error_free(&derr);
+ goto failed;
+ }
+
+ ofono_info("RequestAuthorization succeeded");
+
+ if (!bt_io_accept(io, server->connect_cb, server->user_data,
+ NULL, &err)) {
+ ofono_error("%s", err->message);
+ g_error_free(err);
+ goto failed;
+ }
+
+ g_source_remove(client->watch);
+
+ client->watch = g_io_add_watch(client->io,
+ G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ client_event, server);
+
+ dbus_message_unref(reply);
+
+ return;
+
+failed:
+ dbus_message_unref(reply);
+ disconnect(server);
+}
+
+static gboolean auth_watch(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct server *server = user_data;
+
+ cancel_authorization(server);
+ disconnect(server);
+
+ return FALSE;
+}
+
+static void confirm_event(GIOChannel *io, gpointer user_data)
+{
+ struct server *server = user_data;
+ struct client *client = &server->client;
+ GError *err = NULL;
+ char address[18];
+ const char *addr;
+ guint8 channel;
+ int ret;
+
+ if (client->io) {
+ ofono_error("Rejecting connection since one client already \
+ connected");
+ return;
+ }
+
+ bt_io_get(io, BT_IO_RFCOMM, &err, BT_IO_OPT_DEST, address,
+ BT_IO_OPT_CHANNEL, &channel,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ ofono_error("%s", err->message);
+ g_error_free(err);
+ return;
+ }
+
+ ofono_info("New connection from: %s, channel %u", address, channel);
+
+ addr = address;
+ ret = bluetooth_send_with_reply(server->adapter,
+ BLUEZ_SERVICE_INTERFACE,
+ "RequestAuthorization",
+ auth_cb, server, NULL, TIMEOUT,
+ DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_UINT32, &server->handle,
+ DBUS_TYPE_INVALID);
+ if (ret < 0) {
+ ofono_error("Request Bluetooth authorization failed");
+ return;
+ }
+
+ ofono_info("RequestAuthorization(%s, 0x%x)", address, server->handle);
+
+ client->io = g_io_channel_ref(io);
+ client->watch = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ auth_watch, server);
+
+ return;
+}
+
+static void add_record_cb(DBusPendingCall *call, gpointer user_data)
+{
+ struct server *server = user_data;
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError derr;
+ guint32 handle;
+
+ dbus_error_init(&derr);
+
+ if (dbus_set_error_from_message(&derr, reply)) {
+ ofono_error("Replied with an error: %s, %s",
+ derr.name, derr.message);
+ dbus_error_free(&derr);
+ server_stop(server, NULL);
+ goto done;
+ }
+
+ dbus_message_get_args(reply, NULL, DBUS_TYPE_UINT32, &handle,
+ DBUS_TYPE_INVALID);
+ server->handle = handle;
+
+ ofono_info("Registered: %s, handle: 0x%x", server->name, handle);
+
+done:
+ dbus_message_unref(reply);
+}
+
+static void server_start(gpointer data, gpointer user_data)
+{
+ struct server *server = data;
+ gchar *path = user_data;
+ GError *err = NULL;
+ gchar *xml;
+
+ if (server->handle != 0)
+ return;
+
+ if (server->service != DUN_GW)
+ goto failed;
+
+ server->io = bt_io_listen(BT_IO_RFCOMM, NULL, confirm_event,
+ server, NULL, &err,
+ BT_IO_OPT_CHANNEL, server->channel,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID);
+ if (!server->io) {
+ ofono_error("Bluetooth %s register failed: %s",
+ server->name, err->message);
+ g_error_free(err);
+ goto failed;
+ }
+
+ server->adapter = g_strdup(path);
+
+ xml = g_markup_printf_escaped(dun_record, server->channel,
+ server->name);
+
+ bluetooth_send_with_reply(path, BLUEZ_SERVICE_INTERFACE, "AddRecord",
+ add_record_cb, server, NULL, -1,
+ DBUS_TYPE_STRING, &xml,
+ DBUS_TYPE_INVALID);
+
+ g_free(xml);
+
+ return;
+
+failed:
+ server_stop(server, NULL);
+}
+
static void adapter_properties_cb(DBusPendingCall *call, gpointer user_data)
{
const char *path = user_data;
@@ -394,6 +700,9 @@ static void adapter_properties_cb(DBusPendingCall *call, gpointer user_data)
g_hash_table_insert(adapter_address_hash,
g_strdup(path), g_strdup(addr));
+ if (server_list)
+ g_slist_foreach(server_list, server_start, (gpointer)path);
+
for (l = device_list; l; l = l->next) {
const char *device = l->data;
@@ -428,11 +737,26 @@ static gboolean adapter_removed(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
const char *path;
+ GSList *l;
if (dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID) == TRUE)
g_hash_table_remove(adapter_address_hash, path);
+ for (l = server_list; l; l = l->next) {
+ struct server *server = l->data;
+
+ if (!server->adapter)
+ continue;
+
+ if (!g_str_equal(path, server->adapter))
+ continue;
+
+ /* Don't remove handle if the adapter has been removed */
+ server->handle = 0;
+ server_stop(server, NULL);
+ }
+
return TRUE;
}
@@ -564,6 +888,7 @@ static int bluetooth_ref()
static void bluetooth_unref()
{
gboolean is_zero;
+ GSList *l;
is_zero = g_atomic_int_dec_and_test(&ref_count);
@@ -580,6 +905,20 @@ static void bluetooth_unref()
if (adapter_address_hash)
g_hash_table_destroy(adapter_address_hash);
+
+ if (server_list == NULL)
+ return;
+
+ for (l = server_list; l; l = l->next) {
+ struct server *server = l->data;
+
+ server_stop(server, NULL);
+ g_free(server->name);
+ g_free(server);
+ }
+
+ g_slist_free(server_list);
+ server_list = NULL;
}
int bluetooth_register_uuid(const char *uuid, struct bluetooth_profile *profile)
@@ -604,5 +943,45 @@ void bluetooth_unregister_uuid(const char *uuid)
bluetooth_unref();
}
+struct server *bluetooth_register_server(guint16 service, char *name,
+ guint8 channel, ConnectFunc cb,
+ gpointer user_data)
+{
+ struct server *server;
+ int err = bluetooth_ref();
+
+ if (err != 0)
+ return NULL;
+
+ server = g_try_new0(struct server, 1);
+ if (!server)
+ return NULL;
+
+ server->service = service;
+ server->name = g_strdup(name);
+ server->channel = channel;
+ server->connect_cb = cb;
+ server->user_data = user_data;
+
+ server_list = g_slist_prepend(server_list, server);
+
+ bluetooth_send_with_reply("/", BLUEZ_MANAGER_INTERFACE, "GetProperties",
+ manager_properties_cb, NULL, NULL, -1,
+ DBUS_TYPE_INVALID);
+
+ return server;
+}
+
+void bluetooth_unregister_server(struct server *server)
+{
+ server_list = g_slist_remove(server_list, server);
+
+ server_stop(server, NULL);
+ g_free(server->name);
+ g_free(server);
+
+ bluetooth_unref();
+}
+
OFONO_PLUGIN_DEFINE(bluetooth, "Bluetooth Utils Plugins", VERSION,
OFONO_PLUGIN_PRIORITY_DEFAULT, NULL, NULL)
diff --git a/plugins/bluetooth.h b/plugins/bluetooth.h
index b70bb0c..1780e85 100644
--- a/plugins/bluetooth.h
+++ b/plugins/bluetooth.h
@@ -2,6 +2,7 @@
* oFono - Open Source Telephony
*
* Copyright (C) 2010 Gustavo F. Padovan <gustavo@padovan.org>
+ * Copyright (C) 2010 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -22,6 +23,7 @@
#define BLUEZ_MANAGER_INTERFACE BLUEZ_SERVICE ".Manager"
#define BLUEZ_ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter"
#define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device"
+#define BLUEZ_SERVICE_INTERFACE BLUEZ_SERVICE ".Service"
#define DBUS_TIMEOUT 15
@@ -30,6 +32,9 @@
/* Profiles bitfield */
#define HFP_AG 0x01
+/* Service bitfield */
+#define DUN_GW 0x01
+
struct bluetooth_profile {
const char *name;
int (*create)(const char *device, const char *dev_addr,
@@ -38,10 +43,19 @@ struct bluetooth_profile {
void (*set_alias)(const char *device, const char *);
};
+typedef void (*ConnectFunc)(GIOChannel *io, GError *err, gpointer user_data);
+
+struct server;
+
int bluetooth_register_uuid(const char *uuid,
struct bluetooth_profile *profile);
void bluetooth_unregister_uuid(const char *uuid);
+struct server *bluetooth_register_server(guint16 service, char *name,
+ guint8 channel, ConnectFunc cb,
+ gpointer user_data);
+void bluetooth_unregister_server(struct server *server);
+
void bluetooth_create_path(const char *dev_addr, const char *adapter_addr,
char *buf, int size);
--
1.7.0.4
^ permalink raw reply related [flat|nested] 10+ messages in thread