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@xxxxxxxxxxxx> + * 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@xxxxxxxxxxxx> + * 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@xxxxxxxxxxxx> - * 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@xxxxxxxxxxxx> - * 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 -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html