summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2009-02-16 09:15:34 +0200
committerJohan Hedberg <johan.hedberg@nokia.com>2009-02-16 09:15:34 +0200
commit1dbc2990d638e6079e14e00c4e2e3ff528760801 (patch)
tree5aefdc3621f035570188881a937f2b78a0ac5ab1 /test
parentd3351f1b45477451d28e9e65996946fa6291f319 (diff)
Merge btio test tool
Diffstat (limited to 'test')
-rw-r--r--test/Makefile.am4
-rw-r--r--test/btiotest.c428
2 files changed, 431 insertions, 1 deletions
diff --git a/test/Makefile.am b/test/Makefile.am
index 81b5b379..bfd8f9d6 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -4,7 +4,7 @@ sbin_PROGRAMS = hciemu
bin_PROGRAMS = l2test rctest
-noinst_PROGRAMS = sdptest scotest attest hstest bdaddr lmptest agent
+noinst_PROGRAMS = sdptest scotest attest hstest bdaddr lmptest agent btiotest
hciemu_LDADD = $(top_builddir)/common/libhelper.a \
@GLIB_LIBS@ @BLUEZ_LIBS@
@@ -29,6 +29,8 @@ lmptest_LDADD = @BLUEZ_LIBS@
agent_LDADD = @DBUS_LIBS@
+btiotest_LDADD = @GLIB_LIBS@ @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a
+
noinst_MANS = bdaddr.8
AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@
diff --git a/test/btiotest.c b/test/btiotest.c
new file mode 100644
index 00000000..cb672abb
--- /dev/null
+++ b/test/btiotest.c
@@ -0,0 +1,428 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+
+#include <glib.h>
+
+#include "btio.h"
+
+#define DEFAULT_ACCEPT_TIMEOUT 2
+
+struct io_data {
+ guint ref;
+ GIOChannel *io;
+ BtIOType type;
+ gint reject;
+ gint disconn;
+ gint accept;
+};
+
+static void io_data_unref(struct io_data *data)
+{
+ data->ref--;
+
+ if (data->ref)
+ return;
+
+ if (data->io)
+ g_io_channel_unref(data->io);
+
+ g_free(data);
+}
+
+static struct io_data *io_data_ref(struct io_data *data)
+{
+ data->ref++;
+ return data;
+}
+
+static struct io_data *io_data_new(GIOChannel *io, BtIOType type, gint reject,
+ gint disconn, gint accept)
+{
+ struct io_data *data;
+
+ data = g_new0(struct io_data, 1);
+ data->io = io;
+ data->type = type;
+ data->reject = reject;
+ data->disconn = disconn;
+ data->accept = accept;
+
+ return io_data_ref(data);
+}
+
+static gboolean io_watch(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+ printf("Disconnected\n");
+ return FALSE;
+}
+
+static gboolean disconn_timeout(gpointer user_data)
+{
+ struct io_data *data = user_data;
+
+ printf("Disconnecting\n");
+
+ g_io_channel_close(data->io);
+
+ return FALSE;
+}
+
+static void connect_cb(GIOChannel *io, int err, gpointer user_data)
+{
+ struct io_data *data = user_data;
+ GIOCondition cond;
+ char addr[18];
+
+ if (!bt_io_get(io, data->type, BT_IO_OPT_DEST, addr,
+ BT_IO_OPT_INVALID)) {
+ printf("Unable to get destination address\n");
+ strcpy(addr, "(unknown)");
+ }
+
+ if (err) {
+ printf("Connecting failed: %s\n", strerror(err));
+ return;
+ }
+
+ printf("Successfully connected to %s\n", addr);
+
+ if (data->type == BT_IO_L2CAP) {
+ uint16_t omtu, imtu;
+
+ if (!bt_io_get(io, data->type,
+ BT_IO_OPT_OMTU, &omtu,
+ BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_INVALID))
+ printf("Unable to get L2CAP MTU sizes\n");
+ else
+ printf("L2CAP imtu=%u, omtu=%u\n", imtu, omtu);
+ }
+
+ if (data->disconn == 0) {
+ g_io_channel_close(io);
+ printf("Disconnected\n");
+ return;
+ }
+
+ if (data->io == NULL)
+ data->io = g_io_channel_ref(io);
+
+ if (data->disconn > 0) {
+ io_data_ref(data);
+ g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, data->disconn,
+ disconn_timeout, data,
+ (GDestroyNotify) io_data_unref);
+ }
+
+
+ io_data_ref(data);
+ cond = G_IO_NVAL | G_IO_HUP | G_IO_ERR;
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, io_watch, data,
+ (GDestroyNotify) io_data_unref);
+}
+
+static gboolean confirm_timeout(gpointer user_data)
+{
+ struct io_data *data = user_data;
+
+ if (data->reject >= 0) {
+ printf("Rejecting connection\n");
+ g_io_channel_close(data->io);
+ return FALSE;
+ }
+
+ printf("Accepting connection\n");
+
+ io_data_ref(data);
+
+ if (!bt_io_accept(data->io, connect_cb, data,
+ (GDestroyNotify) io_data_unref)) {
+ printf("bt_io_accept() failed\n");
+ io_data_unref(data);
+ }
+
+ return FALSE;
+}
+
+static void confirm_cb(GIOChannel *io, gpointer user_data)
+{
+ char addr[18];
+ struct io_data *data = user_data;
+
+ if (!bt_io_get(io, data->type, BT_IO_OPT_DEST, addr,
+ BT_IO_OPT_INVALID))
+ printf("Unable to get destination address\n");
+ else
+ printf("Got confirmation request for %s\n", addr);
+
+ if (data->accept < 0 && data->reject < 0)
+ return;
+
+ if (data->reject == 0) {
+ printf("Rejecting connection\n");
+ g_io_channel_close(io);
+ return;
+ }
+
+ data->io = g_io_channel_ref(io);
+ io_data_ref(data);
+
+ if (data->accept == 0) {
+ if (!bt_io_accept(io, connect_cb, data,
+ (GDestroyNotify) io_data_unref)) {
+ printf("bt_io_accept() failed\n");
+ io_data_unref(data);
+ return;
+ }
+ } else {
+ gint seconds = (data->reject > 0) ?
+ data->reject : data->accept;
+ g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, seconds,
+ confirm_timeout, data,
+ (GDestroyNotify) io_data_unref);
+ }
+}
+
+static void l2cap_connect(const char *src, const char *dst, uint16_t psm,
+ gint disconn)
+{
+ struct io_data *data;
+
+ printf("Connecting to %s L2CAP PSM %u\n", dst, psm);
+
+ data = io_data_new(NULL, BT_IO_L2CAP, -1, disconn, -1);
+
+ if (src)
+ data->io = bt_io_connect(BT_IO_L2CAP, connect_cb, data,
+ (GDestroyNotify) io_data_unref,
+ BT_IO_OPT_SOURCE, src,
+ BT_IO_OPT_DEST, dst,
+ BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_INVALID);
+ else
+ data->io = bt_io_connect(BT_IO_L2CAP, connect_cb, data,
+ (GDestroyNotify) io_data_unref,
+ BT_IO_OPT_DEST, dst,
+ BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_INVALID);
+
+ if (!data->io) {
+ printf("Connecting to %s failed\n", dst);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void l2cap_listen(const char *src, uint16_t psm, gint defer,
+ gint reject, gint disconn, gint accept)
+{
+ struct io_data *data;
+ BtIOConnect conn;
+ BtIOConfirm cfm;
+ GIOChannel *l2_srv;
+
+ if (defer) {
+ conn = NULL;
+ cfm = confirm_cb;
+ } else {
+ conn = connect_cb;
+ cfm = NULL;
+ }
+
+ printf("Listening on L2CAP PSM %u\n", psm);
+
+ data = io_data_new(NULL, BT_IO_L2CAP, reject, disconn, accept);
+
+ if (src)
+ l2_srv = bt_io_listen(BT_IO_L2CAP, conn, cfm,
+ data, (GDestroyNotify) io_data_unref,
+ BT_IO_OPT_SOURCE, src,
+ BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_INVALID);
+ else
+ l2_srv = bt_io_listen(BT_IO_L2CAP, conn, cfm,
+ data, (GDestroyNotify) io_data_unref,
+ BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_INVALID);
+
+ if (!l2_srv) {
+ printf("Listeing failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ g_io_channel_unref(l2_srv);
+}
+
+static void rfcomm_connect(const char *src, const char *dst, uint8_t ch,
+ gint disconn)
+{
+ struct io_data *data;
+
+ printf("Connecting to %s RFCOMM channel %u\n", dst, ch);
+
+ data = io_data_new(NULL, BT_IO_RFCOMM, -1, disconn, -1);
+
+ if (src)
+ data->io = bt_io_connect(BT_IO_RFCOMM, connect_cb, data,
+ (GDestroyNotify) io_data_unref,
+ BT_IO_OPT_SOURCE, src,
+ BT_IO_OPT_DEST, dst,
+ BT_IO_OPT_CHANNEL, ch,
+ BT_IO_OPT_INVALID);
+ else
+ data->io = bt_io_connect(BT_IO_RFCOMM, connect_cb, data,
+ (GDestroyNotify) io_data_unref,
+ BT_IO_OPT_DEST, dst,
+ BT_IO_OPT_CHANNEL, ch,
+ BT_IO_OPT_INVALID);
+
+ if (!data->io) {
+ printf("Connecting to %s failed\n", dst);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void rfcomm_listen(const char *src, uint8_t ch, gboolean defer,
+ gint reject, gint disconn, gint accept)
+{
+ struct io_data *data;
+ BtIOConnect conn;
+ BtIOConfirm cfm;
+ GIOChannel *rc_srv;
+
+ if (defer) {
+ conn = NULL;
+ cfm = confirm_cb;
+ } else {
+ conn = connect_cb;
+ cfm = NULL;
+ }
+
+ printf("Listening on RFCOMM channel %u\n", ch);
+
+ data = io_data_new(NULL, BT_IO_RFCOMM, reject, disconn, accept);
+
+ if (src)
+ rc_srv = bt_io_listen(BT_IO_RFCOMM, conn, cfm,
+ data, (GDestroyNotify) io_data_unref,
+ BT_IO_OPT_SOURCE, src,
+ BT_IO_OPT_CHANNEL, ch,
+ BT_IO_OPT_INVALID);
+ else
+ rc_srv = bt_io_listen(BT_IO_RFCOMM, conn, cfm,
+ data, (GDestroyNotify) io_data_unref,
+ BT_IO_OPT_CHANNEL, ch,
+ BT_IO_OPT_INVALID);
+
+ if (!rc_srv) {
+ printf("Listeing failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ g_io_channel_unref(rc_srv);
+}
+
+static gint opt_channel = 0;
+static gint opt_psm = 0;
+static gboolean opt_defer = FALSE;
+static char *opt_dev = NULL;
+static gint opt_reject = -1;
+static gint opt_disconn = -1;
+static gint opt_accept = DEFAULT_ACCEPT_TIMEOUT;
+
+static GMainLoop *main_loop;
+
+static GOptionEntry options[] = {
+ { "channel", 'c', 0, G_OPTION_ARG_INT, &opt_channel,
+ "RFCOMM channel" },
+ { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
+ "L2CAP PSM" },
+ { "defer", 'd', 0, G_OPTION_ARG_NONE, &opt_defer,
+ "Use DEFER_SETUP for incoming connections" },
+ { "dev", 'i', 0, G_OPTION_ARG_STRING, &opt_dev,
+ "Which HCI device to use" },
+ { "reject", 'r', 0, G_OPTION_ARG_INT, &opt_reject,
+ "Reject connection after N seconds" },
+ { "disconnect", 'D', 0, G_OPTION_ARG_INT, &opt_disconn,
+ "Disconnect connection after N seconds" },
+ { "accept", 'a', 0, G_OPTION_ARG_INT, &opt_accept,
+ "Accept connection after N seconds" },
+ { NULL },
+};
+
+static void sig_term(int sig)
+{
+ g_main_loop_quit(main_loop);
+}
+
+int main(int argc, char *argv[])
+{
+ GOptionContext *context;
+
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, options, NULL);
+
+ if (!g_option_context_parse(context, &argc, &argv, NULL))
+ exit(EXIT_FAILURE);
+
+ g_option_context_free(context);
+
+ printf("accept=%d, reject=%d, discon=%d\n", opt_accept, opt_reject,
+ opt_disconn);
+
+ if (opt_psm) {
+ if (argc > 1)
+ l2cap_connect(opt_dev, argv[1], opt_psm, opt_disconn);
+ else
+ l2cap_listen(opt_dev, opt_psm, opt_defer, opt_reject,
+ opt_disconn, opt_accept);
+ }
+
+ if (opt_channel) {
+ if (argc > 1)
+ rfcomm_connect(opt_dev, argv[1], opt_channel,
+ opt_disconn);
+ else
+ rfcomm_listen(opt_dev, opt_channel, opt_defer,
+ opt_reject, opt_disconn, opt_accept);
+ }
+
+ signal(SIGTERM, sig_term);
+ signal(SIGINT, sig_term);
+
+ main_loop = g_main_loop_new(NULL, FALSE);
+
+ g_main_loop_run(main_loop);
+
+ g_main_loop_unref(main_loop);
+
+ printf("Exiting\n");
+
+ exit(EXIT_SUCCESS);
+}