diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.tools | 8 | ||||
-rw-r--r-- | configure.ac | 11 | ||||
-rw-r--r-- | tools/btpclient.c | 338 |
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; +} |