[PATCH -v2 1/6] bt tool: initial tool handling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Gustavo Padovan <gustavo.padovan@xxxxxxxxxxxxxxx>

All core code to using this tool is here, the nexts steps will add
commands like 'discover' 'pair' 'agent', etc.
---
 Makefile.am   |   8 ++
 acinclude.m4  |   6 +
 client/main.c | 421 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 435 insertions(+)
 create mode 100644 client/main.c

diff --git a/Makefile.am b/Makefile.am
index 3b08f9a..2b0bdc5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -431,6 +431,14 @@ else
 unit_tests =
 endif
 
+if CLIENT
+client_bt_SOURCES = $(gdbus_sources) client/main.c
+
+client_bt_LDADD = lib/libbluetooth-private.la @DBUS_LIBS@ @GLIB_LIBS@
+
+bin_PROGRAMS += client/bt
+endif
+
 TESTS = $(unit_tests)
 
 pkgconfigdir = $(libdir)/pkgconfig
diff --git a/acinclude.m4 b/acinclude.m4
index 4bac3f0..d9fd6ce 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -190,6 +190,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	wiimote_enable=no
 	gatt_enable=no
 	neard_enable=no
+	client_enable=yes
 
 	AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], [disable code optimization]), [
 		optimization_enable=${enableval}
@@ -303,6 +304,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	AC_ARG_ENABLE(neard, AC_HELP_STRING([--enable-neard], [compile with neard plugin]), [
 		neard_enable=${enableval}
 	])
+	AC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-client], [install BlueZ client]), [
+		client_enable=${enableval}
+	])
+
 
 	misc_cflags=""
 	misc_ldflags=""
@@ -356,4 +361,5 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	AM_CONDITIONAL(GATTMODULES, test "${gatt_enable}" = "yes")
 	AM_CONDITIONAL(HOGPLUGIN, test "${gatt_enable}" = "yes" && test "${input_enable}" = "yes")
 	AM_CONDITIONAL(NEARDPLUGIN, test "${neard_enable}" = "yes")
+	AM_CONDITIONAL(CLIENT, test "${client_enable}" = "yes")
 ])
diff --git a/client/main.c b/client/main.c
new file mode 100644
index 0000000..4edc255
--- /dev/null
+++ b/client/main.c
@@ -0,0 +1,421 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@xxxxxxxxxxxx>
+ *  Copyright (C) 2011 Bruno Dilly <bdilly@xxxxxxxxxxxxxx>
+ *  Copyright (C) 2011 Gustavo Padovan <gustavo@xxxxxxxxxxx>
+ *  Copyright (C) 2012 Collabora Limited.
+ *
+ *  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
+ *
+ */
+
+/*
+ * This code is meant, in part, to be a working example of a BlueZ client
+ * written in C.
+ *
+ * If you are new to BlueZ, we recommend you read the code breadth-first,
+ * starting with main(). The function and variable names should be fairly
+ * self-explanatory, with comments filling in details along the way.
+ *
+ * You will also want to reference the BlueZ D-Bus API, which is documented in
+ * /doc of this soruce tree, and the libdbus API, documented here:
+ *
+ *   http://dbus.freedesktop.org/doc/api/html/index.html
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#define BLUEZ_SERVICE	"org.bluez"
+#define BLUEZ_MANAGER	"org.bluez.Manager"
+#define BLUEZ_ADAPTER	"org.bluez.Adapter"
+#define BLUEZ_AGENT	"org.bluez.Agent"
+#define BLUEZ_ERROR	"org.bluez.Error"
+
+#define ARRAY_SIZE(x) (int)(sizeof(x)/sizeof(x[0]))
+
+#define ERR(fmt, ...)	fprintf(stderr, fmt "\n", ##__VA_ARGS__)
+
+struct cmd_param {
+	char	*path;
+	int	argc;
+	char	**argv;
+};
+
+struct cmd_struct {
+	const char	*cmd;
+	GSourceFunc	fn;
+};
+
+struct find_adapter_cb_data {
+	GSourceFunc		fn;
+	struct cmd_param	*param;
+};
+
+static GMainLoop *mainloop = NULL;
+static gchar *program_name = NULL;
+
+static bdaddr_t bdaddr;
+static DBusConnection *conn;
+
+static void show_help()
+{
+	printf("usage: %s [-i interface] <command> [<args>]\n"
+	"\n"
+	"options:\n"
+	"	-i <hci dev>, --interface <hci dev>	HCI device\n"
+	"	--help					This help\n"
+	"	--version				Version\n"
+	"\n"
+	"commands:\n"
+	"	discover				Scan for devices\n"
+	"	pair <device address>			Start pairing\n"
+	"	agent					Run BlueZ agent\n"
+							"\n", program_name);
+
+	if(mainloop != NULL)
+		g_main_loop_quit(mainloop);
+}
+
+static struct find_adapter_cb_data *find_adapter_cb_data_new(GSourceFunc fn,
+							struct cmd_param *param)
+{
+	struct find_adapter_cb_data *cb_data;
+
+	cb_data = g_new0(struct find_adapter_cb_data, 1);
+	cb_data->fn = fn;
+	cb_data->param = param;
+
+	return cb_data;
+}
+
+static DBusMessage* create_method_call(const char *adapter_path,
+							const char *interface,
+							const char *method_name)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(BLUEZ_SERVICE, adapter_path,
+						interface, method_name);
+	if (msg == NULL)
+		ERR("Can't allocate new method call");
+
+	return msg;
+}
+
+/* Utility function to call a D-Bus method and register a notify function to
+ * handle the reply */
+static gboolean send_with_reply_and_set_notify(DBusMessage *msg,
+				DBusPendingCallNotifyFunction notify_func,
+				gpointer user_data,
+				DBusFreeFunction user_data_free)
+{
+	DBusPendingCall *pending;
+	dbus_bool_t success;
+
+	success = dbus_connection_send_with_reply(conn, msg, &pending, -1);
+	if (pending) {
+		dbus_pending_call_set_notify(pending, notify_func, user_data,
+								user_data_free);
+	} else {
+		ERR("D-Bus connection lost before message could be sent");
+		return FALSE;
+	}
+
+	if (!success) {
+		ERR("Not enough memory to send D-Bus message");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void run_func(const char *adapter_path, GSourceFunc fn,
+							struct cmd_param *param)
+{
+	param->path = strdup(adapter_path);
+
+	/* track timeout source */
+	g_timeout_add(50, fn, param);
+}
+
+/* Handle the D-Bus method reply for FindAdapter or DefaultAdapter */
+static void get_adapter_reply(DBusPendingCall *pending, void *user_data)
+{
+	struct find_adapter_cb_data *cb_data = user_data;
+	DBusMessage *reply;
+	const char *adapter_path;
+
+	if (!pending)
+		return;
+
+	/* We must ensure that this object remains alive while we need it and
+	 * objects it references (such as the reply) */
+	dbus_pending_call_ref(pending);
+
+	/* By "stealing" the reply, we are accepting responsibility to
+	 * unreference it below. Otherwise, its memory will be leaked */
+	reply = dbus_pending_call_steal_reply(pending);
+	if (!reply) {
+		ERR("FindAdapter() failed.");
+		exit(1);
+	}
+
+	/* Ensure that the reply does not indicate an error */
+	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+		ERR("FindAdapter() failed");
+		exit(1);
+	} else {
+		struct DBusError err;
+		dbus_error_init(&err);
+
+		if (!dbus_message_get_args(reply, &err,
+					DBUS_TYPE_OBJECT_PATH, &adapter_path,
+					DBUS_TYPE_INVALID)) {
+			/* Ensure that there was not an error retrieving the
+			 * reply arguments */
+			if (dbus_error_is_set(&err)) {
+				ERR("FindAdapter() failed: %s", err.message);
+				dbus_error_free(&err);
+			} else {
+				ERR("FindAdapter() failed.");
+			}
+			exit(1);
+		}
+	}
+
+	/* In case we successfully retrieved the adapter object, execute our
+	 * core command using its path */
+	if (adapter_path != NULL && strlen(adapter_path) > 0) {
+		run_func(adapter_path, cb_data->fn, cb_data->param);
+	} else {
+		ERR("No such adapter");
+		exit(1);
+	}
+
+	/* Drop our references to these objects to avoid memory leaks */
+	dbus_message_unref(reply);
+	dbus_pending_call_unref(pending);
+}
+
+/* Looks up the adapter D-Bus object which corresponds to the Bluetooth device
+ * we will use (whether the user specified one or we fall back to the default.
+ *
+ * Once we have that object, we execute the core command. */
+static void get_adapter_and_run_func(GSourceFunc fn, struct cmd_param *param)
+{
+	DBusMessage *msg;
+	char *bt_str = NULL;
+	const char *method_name = NULL;
+	gpointer user_data;
+
+	/* The bacmp(), ba2str(), and other Bluetooth address utilities are
+	 * defined in bluetooth.h, in this source tree */
+	if (!bacmp(&bdaddr, BDADDR_ANY)) {
+		method_name = "DefaultAdapter";
+	} else {
+		method_name = "FindAdapter";
+
+		/* Allocate a string large enough for the biggest BT address */
+		bt_str = g_new(char, 18);
+		ba2str(&bdaddr, bt_str);
+	}
+
+	/* Create a new D-Bus method call */
+	if (!(msg = create_method_call("/", BLUEZ_MANAGER, method_name)))
+		exit(1);
+
+	/* In case we're making the FindAdapter D-Bus method call, we need to
+	 * append a method argument */
+	if (bt_str != NULL)
+		dbus_message_append_args(msg, DBUS_TYPE_STRING, &bt_str,
+							DBUS_TYPE_INVALID);
+
+	/* Finally, we need to asynchronously send and receive a response for
+	 * the D-Bus method call. This function does not block at all. Instead,
+	 * the GMainLoop will continue iterating until we get a response, which
+	 * will be passed to the notify function we specify here.
+	 *
+	 * We use only async D-Bus messaging because some of these D-Bus methods
+	 * could potentially take several seconds to complete, In the meantime,
+	 * if we were blocking, our application would be unresponsive and look
+	 * as if it (or * even the entire system upon which it is running) had
+	 * frozen. */
+	user_data = find_adapter_cb_data_new(fn, param);
+	if (!send_with_reply_and_set_notify(msg,  get_adapter_reply, user_data,
+									g_free))
+		exit(1);
+
+	dbus_message_unref(msg);
+}
+
+static void bluetoothd_disconnect(DBusConnection *conn, void *user_data)
+{
+	g_main_loop_quit(mainloop);
+
+	printf("Bluetooth daemon exited.\n");
+
+}
+
+static struct cmd_struct commands[] = {
+	{ NULL, NULL},
+};
+
+/* Returns FALSE in case the command could not be parsed. Any failures during
+ * execution will be handled later */
+static gboolean run_argv(int argc, char **argv)
+{
+	const char *cmd = argv[0];
+	struct cmd_param *param;
+	int i;
+
+	if (argc > 1 && g_str_equal(argv[1], "--help"))
+		cmd = "help";
+
+	for (i = 0; i < ARRAY_SIZE(commands); i++) {
+		struct cmd_struct *p = commands+i;
+		if (cmd == NULL || !g_str_equal(p->cmd, cmd))
+			continue;
+
+		param = malloc(sizeof(*param));
+		if (!param)
+			exit(1);
+
+		/* skip over the name of this program itself */
+		argc--;
+		argv++;
+
+		param->argc = argc;
+		param->argv = argv;
+
+		get_adapter_and_run_func(p->fn, param);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean handle_options(int *argc, char ***argv)
+{
+	gboolean terminal_opt = FALSE;
+
+	bacpy(&bdaddr, BDADDR_ANY);
+
+	while (*argc > 0) {
+		const char *cmd = (*argv)[0];
+		if (cmd != NULL && cmd[0] != '-')
+			break;
+
+		if (g_str_equal(cmd, "--help") ||
+						g_str_equal(cmd, "--version")) {
+			terminal_opt = TRUE;
+			break;
+		}
+
+		/*
+		 * Check remaining flags.
+		 */
+		if(g_str_equal(cmd, "-i") || g_str_equal(cmd, "--interface")) {
+			const char *iface;
+			(*argv)++;
+			(*argc)--;
+			iface = (*argv)[0];
+			if (!strncasecmp(iface, "hci", 3))
+				hci_devba(atoi(iface + 3), &bdaddr);
+			else
+				str2ba(iface, &bdaddr);
+		}
+
+		(*argv)++;
+		(*argc)--;
+	}
+
+	return terminal_opt;
+}
+
+int main(int argc, char **argv)
+{
+	DBusError err;
+	gboolean terminal_opt;
+
+	program_name = g_strdup(argv[0]);
+
+	argv++;
+	argc--;
+	terminal_opt = handle_options(&argc, &argv);
+
+	if (terminal_opt) {
+		if (g_str_equal(argv[0], "--help"))
+			show_help();
+		else if (g_str_equal(argv[0], "--version"))
+			printf("%s\n", VERSION);
+
+		exit(0);
+	}
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+
+	/* This structure must be initialized before it can be used */
+	dbus_error_init(&err);
+
+	/* Set up our connection to the D-Bus system bus, which we will use
+	 * throughout this code.
+	 *
+	 * NOTE: the "g_dbus" namespace used in this file is due to BlueZ's
+	 * internal "gdbus" D-Bus client library (included by gdbus.h), which is
+	 * not to be confused with libgio's D-Bus client functionality with the
+	 * same namespace (which would be included by gio.h) */
+	conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err);
+	if (!conn) {
+		if (dbus_error_is_set(&err)) {
+			ERR("Can't connect to system bus: %s", err.message);
+			dbus_error_free(&err);
+		} else {
+			ERR("Can't connect to system bus");
+		}
+
+		exit(1);
+	}
+
+	/* Run the command given in the command line */
+	if (!run_argv(argc, argv)) {
+		ERR("%s: command not understood.", program_name);
+		show_help();
+		exit(1);
+	}
+
+	/* Set up handler in case the bluetooth daemon exits and it disappears
+	 * from the bus */
+	g_dbus_add_service_watch(conn, BLUEZ_SERVICE, NULL,
+					bluetoothd_disconnect, NULL, NULL);
+
+	g_main_loop_run(mainloop);
+
+	return 0;
+}
-- 
1.7.11.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


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux