summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSzymon Janc <szymon.janc@codecoup.pl>2017-12-07 12:43:44 +0100
committerSzymon Janc <szymon.janc@codecoup.pl>2017-12-14 10:08:32 +0100
commitf68235b2c03235695837abfb6d7ceebacbf14dd9 (patch)
tree19c44684a87b4a47b47347407b789e04bf4b73b7
parentb8cd86c3e2be9ae8f3294e4090179611ff69ba9c (diff)
downloadbluez-f68235b2c03235695837abfb6d7ceebacbf14dd9.tar.gz
tools/btpclient: Add initial code
This adds initial code for BTP client tool that allows for automated (binary protocol ) control of BlueZ stack. Currently this tool depends only on Embedded Linux Library and requires master branch of ELL. When 0.3 is released dependencies will be bumped. Initial code allows to connect D-Bus client, discover objects and keep proxies for it. It also implements basics for BTP core service.
-rw-r--r--.gitignore1
-rw-r--r--Makefile.tools8
-rw-r--r--configure.ac11
-rw-r--r--tools/btpclient.c338
4 files changed, 358 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index af205ec6a..393735e0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -116,6 +116,7 @@ tools/btattach
tools/btconfig
tools/btmgmt
tools/btsnoop
+tools/btpclient
peripheral/btsensor
monitor/btmon
emulator/btvirt
diff --git a/Makefile.tools b/Makefile.tools
index 8074a536d..9e56603d8 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -444,3 +444,11 @@ test_scripts += test/sap_client.py test/bluezutils.py \
test/pbap-client test/map-client test/example-advertisement \
test/example-gatt-server test/example-gatt-client \
test/test-gatt-profile
+
+if BTPCLIENT
+noinst_PROGRAMS += tools/btpclient
+
+tools_btpclient_SOURCES = tools/btpclient.c src/shared/btp.c src/shared/btp.h
+tools_btpclient_CFLAGS = $(AM_CFLAGS) @ELL_CFLAGS@
+tools_btpclient_LDADD = @ELL_LIBS@
+endif
diff --git a/configure.ac b/configure.ac
index 964101412..fcba28543 100644
--- a/configure.ac
+++ b/configure.ac
@@ -244,6 +244,17 @@ if (test "${enable_obex}" != "no"); then
fi
AM_CONDITIONAL(OBEX, test "${enable_obex}" != "no")
+AC_ARG_ENABLE(btpclient, AC_HELP_STRING([--enable-btpclient],
+ [enable BTP client]), [enable_btpclient=${enableval}])
+AM_CONDITIONAL(BTPCLIENT, test "${enable_btpclient}" = "yes")
+
+if (test "${enable_btpclient}" = "yes"); then
+ PKG_CHECK_MODULES(ELL, ell >= 0.2, dummy=yes,
+ AC_MSG_ERROR(ell library >= 0.2 is required))
+ AC_SUBST(ELL_CFLAGS)
+ AC_SUBST(ELL_LIBS)
+fi
+
AC_ARG_ENABLE(client, AC_HELP_STRING([--disable-client],
[disable command line client]), [enable_client=${enableval}])
AM_CONDITIONAL(CLIENT, test "${enable_client}" != "no")
diff --git a/tools/btpclient.c b/tools/btpclient.c
new file mode 100644
index 000000000..bde59406c
--- /dev/null
+++ b/tools/btpclient.c
@@ -0,0 +1,338 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2017 Intel Corporation. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <getopt.h>
+
+#include <ell/ell.h>
+
+#include "src/shared/btp.h"
+
+struct btp_adapter {
+ struct l_dbus_proxy *proxy;
+ uint8_t index;
+};
+
+struct btp_device {
+ struct l_dbus_proxy *proxy;
+};
+
+static struct l_queue *adapters;
+static struct l_queue *devices;
+static char *socket_path;
+static struct btp *btp;
+
+static void btp_core_read_commands(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ uint8_t commands = 0;
+
+ if (index != BTP_INDEX_NON_CONTROLLER) {
+ btp_send_error(btp, BTP_CORE_SERVICE, index,
+ BTP_ERROR_INVALID_INDEX);
+ return;
+ }
+
+ commands |= (1 << BTP_OP_CORE_READ_SUPPORTED_COMMANDS);
+ commands |= (1 << BTP_OP_CORE_READ_SUPPORTED_SERVICES);
+ commands |= (1 << BTP_OP_CORE_REGISTER);
+ commands |= (1 << BTP_OP_CORE_UNREGISTER);
+
+ btp_send(btp, BTP_CORE_SERVICE, BTP_OP_CORE_READ_SUPPORTED_COMMANDS,
+ BTP_INDEX_NON_CONTROLLER, sizeof(commands), &commands);
+}
+
+static void btp_core_read_services(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ uint8_t services = 0;
+
+ if (index != BTP_INDEX_NON_CONTROLLER) {
+ btp_send_error(btp, BTP_CORE_SERVICE, index,
+ BTP_ERROR_INVALID_INDEX);
+ return;
+ }
+
+ services |= (1 << BTP_CORE_SERVICE);
+
+ btp_send(btp, BTP_CORE_SERVICE, BTP_OP_CORE_READ_SUPPORTED_SERVICES,
+ BTP_INDEX_NON_CONTROLLER, sizeof(services), &services);
+}
+
+static void btp_core_register(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ if (index != BTP_INDEX_NON_CONTROLLER) {
+ btp_send_error(btp, BTP_CORE_SERVICE, index,
+ BTP_ERROR_INVALID_INDEX);
+ return;
+ }
+
+ btp_send_error(btp, BTP_CORE_SERVICE, index, BTP_ERROR_FAIL);
+}
+
+static void btp_core_unregister(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ if (index != BTP_INDEX_NON_CONTROLLER) {
+ btp_send_error(btp, BTP_CORE_SERVICE, index,
+ BTP_ERROR_INVALID_INDEX);
+ return;
+ }
+
+ btp_send_error(btp, BTP_CORE_SERVICE, index, BTP_ERROR_FAIL);
+}
+
+static void register_core_service(void)
+{
+ btp_register(btp, BTP_CORE_SERVICE,
+ BTP_OP_CORE_READ_SUPPORTED_COMMANDS,
+ btp_core_read_commands, NULL, NULL);
+
+ btp_register(btp, BTP_CORE_SERVICE,
+ BTP_OP_CORE_READ_SUPPORTED_SERVICES,
+ btp_core_read_services, NULL, NULL);
+
+ btp_register(btp, BTP_CORE_SERVICE, BTP_OP_CORE_REGISTER,
+ btp_core_register, NULL, NULL);
+
+ btp_register(btp, BTP_CORE_SERVICE, BTP_OP_CORE_UNREGISTER,
+ btp_core_unregister, NULL, NULL);
+}
+
+static void signal_handler(struct l_signal *signal, uint32_t signo,
+ void *user_data)
+{
+ switch (signo) {
+ case SIGINT:
+ case SIGTERM:
+ l_info("Terminating");
+ l_main_quit();
+ break;
+ }
+}
+
+static void btp_adapter_free(struct btp_adapter *adapter)
+{
+ l_free(adapter);
+}
+
+static void btp_device_free(struct btp_device *device)
+{
+ l_free(device);
+}
+
+static void proxy_added(struct l_dbus_proxy *proxy, void *user_data)
+{
+ const char *interface = l_dbus_proxy_get_interface(proxy);
+ const char *path = l_dbus_proxy_get_path(proxy);
+
+ l_info("Proxy added: %s (%s)", interface, path);
+
+ if (!strcmp(interface, "org.bluez.Adapter1")) {
+ struct btp_adapter *adapter;
+
+ adapter = l_new(struct btp_adapter, 1);
+ adapter->proxy = proxy;
+ adapter->index = l_queue_length(adapters);
+
+ l_queue_push_tail(adapters, adapter);
+ return;
+ }
+
+ if (!strcmp(interface, "org.bluez.Device1")) {
+ struct btp_device *device;
+
+ device = l_new(struct btp_device, 1);
+ device->proxy = proxy;
+
+ l_queue_push_tail(devices, device);
+ return;
+ }
+}
+
+static bool device_match_by_proxy(const void *a, const void *b)
+{
+ const struct btp_device *device = a;
+ const struct l_dbus_proxy *proxy = b;
+
+ return device->proxy == proxy;
+}
+
+static void proxy_removed(struct l_dbus_proxy *proxy, void *user_data)
+{
+ const char *interface = l_dbus_proxy_get_interface(proxy);
+ const char *path = l_dbus_proxy_get_path(proxy);
+
+ l_info("Proxy removed: %s (%s)", interface, path);
+
+ if (!strcmp(interface, "org.bluez.Adapter1")) {
+ l_info("Adapter removed, terminating.");
+ l_main_quit();
+ return;
+ }
+
+ if (!strcmp(interface, "org.bluez.Device1")) {
+ l_queue_remove_if(devices, device_match_by_proxy, proxy);
+
+ return;
+ }
+}
+
+static void property_changed(struct l_dbus_proxy *proxy, const char *name,
+ struct l_dbus_message *msg, void *user_data)
+{
+ l_info("property_changed %s %s %s", name, l_dbus_proxy_get_path(proxy),
+ l_dbus_proxy_get_interface(proxy));
+}
+
+static void client_connected(struct l_dbus *dbus, void *user_data)
+{
+ l_info("D-Bus client connected");
+}
+
+static void client_disconnected(struct l_dbus *dbus, void *user_data)
+{
+ l_info("D-Bus client disconnected, terminated");
+ l_main_quit();
+}
+
+static void client_ready(struct l_dbus_client *client, void *user_data)
+{
+ l_info("D-Bus client ready, connecting BTP");
+
+ btp = btp_new(socket_path);
+ if (!btp) {
+ l_error("Failed to connect BTP, terminating");
+ l_main_quit();
+ return;
+ }
+
+ register_core_service();
+
+ btp_send(btp, BTP_CORE_SERVICE, BTP_EV_CORE_READY,
+ BTP_INDEX_NON_CONTROLLER, 0, NULL);
+}
+
+static void usage(void)
+{
+ l_info("btpclient - Bluetooth tester");
+ l_info("Usage:");
+ l_info("\tbtpclient [options]");
+ l_info("options:\n"
+ "\t-s, --socket <socket> Socket to use for BTP\n"
+ "\t-q, --quiet Don't emit any logs\n"
+ "\t-v, --version Show version\n"
+ "\t-h, --help Show help options");
+}
+
+static const struct option options[] = {
+ { "socket", 1, 0, 's' },
+ { "quiet", 0, 0, 'q' },
+ { "version", 0, 0, 'v' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct l_dbus_client *client;
+ struct l_signal *signal;
+ struct l_dbus *dbus;
+ sigset_t mask;
+ int opt;
+
+ l_log_set_stderr();
+
+ while ((opt = getopt_long(argc, argv, "+hs:vq", options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ socket_path = l_strdup(optarg);
+ break;
+ case 'q':
+ l_log_set_null();
+ break;
+ case 'd':
+ break;
+ case 'v':
+ l_info("%s", VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ default:
+ usage();
+ return EXIT_SUCCESS;
+ }
+ }
+
+ if (!socket_path) {
+ l_info("Socket option is required");
+ l_info("Type --help for usage");
+ return EXIT_FAILURE;
+ }
+
+ if (!l_main_init())
+ return EXIT_FAILURE;
+
+
+ adapters = l_queue_new();
+ devices = l_queue_new();
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+ signal = l_signal_create(&mask, signal_handler, NULL, NULL);
+
+ dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS);
+ client = l_dbus_client_new(dbus, "org.bluez", "/org/bluez");
+
+ l_dbus_client_set_connect_handler(client, client_connected, NULL, NULL);
+ l_dbus_client_set_disconnect_handler(client, client_disconnected, NULL,
+ NULL);
+
+ l_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
+ property_changed, NULL, NULL);
+
+ l_dbus_client_set_ready_handler(client, client_ready, NULL, NULL);
+
+ l_main_run();
+
+ l_dbus_client_destroy(client);
+ l_dbus_destroy(dbus);
+ l_signal_remove(signal);
+ btp_cleanup(btp);
+
+ l_queue_destroy(devices, (l_queue_destroy_func_t)btp_device_free);
+ l_queue_destroy(adapters, (l_queue_destroy_func_t)btp_adapter_free);
+
+ l_free(socket_path);
+
+ l_main_exit();
+
+ return EXIT_SUCCESS;
+}