summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/bneptest.c710
-rw-r--r--tools/btgatt-client.c218
-rw-r--r--tools/btgatt-server.c23
-rw-r--r--tools/btmgmt.c999
-rw-r--r--tools/btsnoop.c100
-rw-r--r--tools/mgmt-tester.c1123
-rw-r--r--tools/mpris-proxy.c2
-rw-r--r--tools/obexctl.c37
-rw-r--r--tools/oobtest.c451
9 files changed, 3459 insertions, 204 deletions
diff --git a/tools/bneptest.c b/tools/bneptest.c
new file mode 100644
index 00000000..84319b9e
--- /dev/null
+++ b/tools/bneptest.c
@@ -0,0 +1,710 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Intel 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <linux/if_bridge.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "btio/btio.h"
+#include "lib/bnep.h"
+#include "profiles/network/bnep.h"
+
+enum {
+ MODE_LISTEN,
+ MODE_CONNECT,
+};
+
+static GMainLoop *mloop;
+static GIOChannel *bnep_io;
+static struct bnep *session;
+
+static int mode;
+static bool no_close_after_disconn;
+static int send_frame_timeout;
+
+static bdaddr_t src_addr, dst_addr;
+static char iface[16];
+static char bridge[16];
+static bool send_ctrl_msg_type_set = false;
+static uint8_t ctrl_msg_type = 0x00;
+static bool send_bnep_msg_type_set = false;
+static uint8_t bnep_msg_type = 0x00;
+static int ctrl_msg_retransmition_nb = 0;
+static int bnep_msg_retransmission_nb = 0;
+static uint16_t local_role = BNEP_SVC_PANU;
+static uint16_t remote_role = BNEP_SVC_NAP;
+static uint16_t ntw_proto_down_range = 0x0000;
+static uint16_t ntw_proto_up_range = 0xdc05;
+static uint16_t ntw_proto_type = 0x0000;
+static uint8_t mcast_addr_down_range[6];
+static uint8_t mcast_addr_up_range[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+static uint8_t src_hw_addr[6];
+static uint8_t dst_hw_addr[6];
+static uint8_t general_frame_payload[] = "abcdef0123456789_bnep_test_data";
+
+static int set_forward_delay(int sk)
+{
+ unsigned long args[4] = { BRCTL_SET_BRIDGE_FORWARD_DELAY, 0, 0, 0 };
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, bridge, IFNAMSIZ);
+ ifr.ifr_data = (char *) args;
+
+ if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0) {
+ error("setting forward delay failed: %d (%s)",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int nap_create_bridge(void)
+{
+ int sk, err;
+
+ sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (sk < 0)
+ return -EOPNOTSUPP;
+
+ if (ioctl(sk, SIOCBRADDBR, bridge) < 0) {
+ if (errno != EEXIST) {
+ close(sk);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ err = set_forward_delay(sk);
+ if (err < 0) {
+ printf("failed to set forward delay\n");
+ ioctl(sk, SIOCBRDELBR, bridge);
+ }
+
+ close(sk);
+
+ return err;
+}
+
+static int cleanup(void)
+{
+ bnep_cleanup();
+
+ if (mode == MODE_LISTEN)
+ bnep_server_delete(bridge, iface, &dst_addr);
+
+ if (bnep_io) {
+ g_io_channel_shutdown(bnep_io, TRUE, NULL);
+ g_io_channel_unref(bnep_io);
+ bnep_io = NULL;
+ }
+
+ return 0;
+}
+
+static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ printf("%s\n", __func__);
+
+ if (no_close_after_disconn)
+ return FALSE;
+
+ /* Cleanup since it's called when disconnected l2cap */
+ if (cleanup() < 0) {
+ printf("cleanup went wrong...\n");
+ return FALSE;
+ }
+
+ g_main_loop_quit(mloop);
+ return FALSE;
+}
+
+static ssize_t send_compressed_frame(int sk, uint8_t type)
+{
+ uint8_t frame[100];
+
+ printf("%s\n", __func__);
+
+ if (send_frame_timeout > 0) {
+ printf("waiting %d seconds before sending msg\n",
+ send_frame_timeout);
+ sleep(send_frame_timeout);
+ }
+
+ frame[0] = type;
+ memcpy(&frame[1], dst_hw_addr, sizeof(dst_hw_addr));
+ memcpy(&frame[7], src_hw_addr, sizeof(src_hw_addr));
+ frame[13] = ntw_proto_type & 0xff;
+ frame[14] = (ntw_proto_type >> 8);
+ memcpy(&frame[15], general_frame_payload,
+ sizeof(general_frame_payload));
+
+ /* TODO - set frame payload by user */
+ return send(sk, frame, 15 + sizeof(general_frame_payload), 0);
+}
+
+static ssize_t send_general_frame(int sk)
+{
+ uint8_t frame[100];
+
+ printf("%s\n", __func__);
+
+ if (send_frame_timeout > 0) {
+ printf("waiting %d seconds before sending msg\n",
+ send_frame_timeout);
+ sleep(send_frame_timeout);
+ }
+
+ frame[0] = BNEP_GENERAL;
+ memcpy(&frame[1], dst_hw_addr, sizeof(dst_hw_addr));
+ memcpy(&frame[7], src_hw_addr, sizeof(src_hw_addr));
+ frame[13] = ntw_proto_type & 0xff;
+ frame[14] = (ntw_proto_type >> 8);
+ memcpy(&frame[15], general_frame_payload,
+ sizeof(general_frame_payload));
+
+ /* TODO - set frame payload by user */
+ return send(sk, frame, 15 + sizeof(general_frame_payload), 0);
+}
+
+static ssize_t send_ctrl_frame(int sk)
+{
+ /*
+ * Max buff size = type(1byte) + ctrl(1byte) + len(2byte) +
+ * mcast_addr_down(6byte) + mcast_addr_up(6byte)
+ */
+ uint8_t buff[16];
+ struct bnep_set_filter_req *frame = (void *) buff;
+ int err;
+
+ printf("%s\n", __func__);
+
+ if (send_frame_timeout > 0) {
+ printf("waiting %d seconds before sending msg\n",
+ send_frame_timeout);
+ sleep(send_frame_timeout);
+ }
+
+ switch (ctrl_msg_type) {
+ case BNEP_FILTER_NET_TYPE_SET:
+ frame->type = BNEP_CONTROL;
+ frame->ctrl = ctrl_msg_type;
+ frame->len = htons(sizeof(ntw_proto_down_range) +
+ sizeof(ntw_proto_up_range));
+ memcpy(frame->list, &ntw_proto_down_range,
+ sizeof(ntw_proto_down_range));
+ memcpy(frame->list + sizeof(ntw_proto_down_range),
+ &ntw_proto_up_range, sizeof(ntw_proto_up_range));
+
+ err = send(sk, frame, sizeof(*frame) +
+ sizeof(ntw_proto_down_range) +
+ sizeof(ntw_proto_up_range), 0);
+ break;
+ case BNEP_FILTER_MULT_ADDR_SET:
+ frame->type = BNEP_CONTROL;
+ frame->ctrl = ctrl_msg_type;
+ frame->len = htons(sizeof(mcast_addr_down_range) +
+ sizeof(mcast_addr_up_range));
+ memcpy(frame->list, mcast_addr_down_range,
+ sizeof(mcast_addr_down_range));
+ memcpy(frame->list + sizeof(mcast_addr_down_range),
+ mcast_addr_up_range, sizeof(mcast_addr_up_range));
+
+ err = send(sk, frame, sizeof(*frame) +
+ sizeof(mcast_addr_down_range) +
+ sizeof(mcast_addr_up_range), 0);
+ break;
+ default:
+ err = -1;
+ break;
+ }
+
+ return err;
+}
+
+static int send_bnep_frame(int sk)
+{
+ int err;
+
+ switch (bnep_msg_type) {
+ case BNEP_GENERAL:
+ err = send_general_frame(sk);
+ break;
+ case BNEP_COMPRESSED:
+ err = send_compressed_frame(sk, BNEP_COMPRESSED);
+ break;
+ case BNEP_COMPRESSED_SRC_ONLY:
+ err = send_compressed_frame(sk,
+ BNEP_COMPRESSED_SRC_ONLY);
+ break;
+ case BNEP_COMPRESSED_DST_ONLY:
+ err = send_compressed_frame(sk,
+ BNEP_COMPRESSED_DST_ONLY);
+ break;
+ default:
+ printf("wrong bnep_msg_type 0x%02x\n", bnep_msg_type);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static void handle_bnep_msg_send(int sk)
+{
+ if (send_ctrl_msg_type_set) {
+ do {
+ if (send_ctrl_frame(sk) < 0)
+ printf("sending ctrl frame error: %s (%d)\n",
+ strerror(errno), errno);
+ } while (ctrl_msg_retransmition_nb--);
+ }
+
+ if (send_bnep_msg_type_set) {
+ do {
+ if (send_bnep_frame(sk) < 0)
+ printf("sending bnep frame error: %s (%d)\n",
+ strerror(errno), errno);
+ } while (bnep_msg_retransmission_nb--);
+ }
+}
+
+static gboolean setup_bnep_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ uint8_t packet[BNEP_MTU];
+ int sk, n, err;
+
+ printf("%s\n", __func__);
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ error("hangup or error or inval on BNEP socket");
+ return FALSE;
+ }
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ /* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
+#ifdef __TIZEN_PATCH__
+ n = recv(sk, packet, sizeof(packet), MSG_PEEK);
+#else
+ n = read(sk, packet, sizeof(packet));
+#endif
+ if (n < 0) {
+ error("read(): %s(%d)", strerror(errno), errno);
+ return FALSE;
+ }
+
+ err = nap_create_bridge();
+ if (err < 0) {
+ error("failed to create bridge: %s (%d)", strerror(-err), err);
+ return FALSE;
+ }
+
+ if (bnep_server_add(sk, (err < 0) ? NULL : bridge, iface, &dst_addr,
+ packet, n) < 0) {
+ printf("server_connadd failed\n");
+ cleanup();
+ return FALSE;
+ }
+
+ g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL, bnep_watchdog_cb,
+ NULL);
+
+ handle_bnep_msg_send(sk);
+
+ g_io_channel_unref(bnep_io);
+ bnep_io = NULL;
+
+ return FALSE;
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ printf("%s\n", __func__);
+
+ if (err) {
+ error("%s", err->message);
+ return;
+ }
+
+ g_io_add_watch(chan, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ setup_bnep_cb, NULL);
+}
+
+static void connected_client_cb(char *iface, int err, void *data)
+{
+ int sk = PTR_TO_INT(data);
+
+ printf("%s\n", __func__);
+
+ handle_bnep_msg_send(sk);
+}
+
+static void disconnected_client_cb(void *data)
+{
+ printf("%s\n", __func__);
+
+ if (no_close_after_disconn)
+ return;
+
+ /* Cleanup since it's called when disconnected l2cap */
+ if (cleanup() < 0) {
+ printf("cleanup went wrong...\n");
+ return;
+ }
+
+ g_main_loop_quit(mloop);
+}
+
+static void connect_client_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ int perr;
+ int sk;
+
+ sk = g_io_channel_unix_get_fd(bnep_io);
+
+ session = bnep_new(sk, local_role, remote_role, bridge);
+ if (!session) {
+ printf("cannot create bnep session\n");
+ return;
+ }
+
+ perr = bnep_connect(session, connected_client_cb,
+ disconnected_client_cb, INT_TO_PTR(sk), NULL);
+ if (perr < 0)
+ printf("cannot initiate bnep connection\n");
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+ GError *err = NULL;
+ char address[18];
+
+ printf("%s\n", __func__);
+
+ bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst_addr, BT_IO_OPT_DEST,
+ address, BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ return;
+ }
+
+ printf("incoming connection from: %s\n", address);
+
+ bnep_io = g_io_channel_ref(chan);
+ g_io_channel_set_close_on_unref(bnep_io, TRUE);
+
+ if (!bt_io_accept(bnep_io, connect_cb, NULL, NULL, &err)) {
+ error("bt_io_accept: %s", err->message);
+ g_error_free(err);
+ g_io_channel_unref(bnep_io);
+ }
+}
+
+static int bnep_server_listen(void)
+{
+ GError *gerr = NULL;
+
+ printf("%s\n", __func__);
+
+ bnep_io = bt_io_listen(NULL, confirm_cb, NULL, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &src_addr,
+ BT_IO_OPT_PSM, BNEP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_OMTU, BNEP_MTU,
+ BT_IO_OPT_IMTU, BNEP_MTU,
+ BT_IO_OPT_INVALID);
+ if (!bnep_io) {
+ printf("can't start server listening: err %s\n", gerr->message);
+ g_error_free(gerr);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int bnep_client_connect(void)
+{
+ GError *gerr = NULL;
+ char bdastr[18];
+
+ printf("%s\n", __func__);
+
+ ba2str(&dst_addr, bdastr);
+ printf("connecting %s\n", bdastr);
+
+ bnep_io = bt_io_connect(connect_client_cb, NULL, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &src_addr,
+ BT_IO_OPT_DEST_BDADDR, &dst_addr,
+ BT_IO_OPT_PSM, BNEP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_OMTU, BNEP_MTU,
+ BT_IO_OPT_IMTU, BNEP_MTU,
+ BT_IO_OPT_INVALID);
+ if (!bnep_io) {
+ printf("cannot connect: err %s\n", gerr->message);
+ g_error_free(gerr);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void exit_handler(int sig)
+{
+ printf("got sig = %d, cleaning up...\n", sig);
+
+ if (cleanup() < 0)
+ printf("cleanup failure...\n");
+ else
+ printf("cleanup successful - exit\n");
+
+ exit(0);
+}
+
+static void usage(void)
+{
+ printf("bneptest - BNEP testing ver %s\n", VERSION);
+ printf("Usage:\n"
+ "\tbneptest [-i] -b <bridge name> -n <iface name>"
+ " <connection mode> [send_ctrl_cmd] [options]\n"
+ "\t-i hci dev number <hci number>, def. 0\n"
+ "\t-b bridge name <string>\n"
+ "\t-n interface name <string>\n");
+ printf("Connect Mode:\n"
+ "\t-c connect <dst_addr>\n"
+ "\t-r remote role <16 bit svc value>\n"
+ "\t-l local role <16 bit svc valu>\n");
+ printf("Listen Mode:\n"
+ "\t-s start server listening\n");
+ printf("Send control command:\n"
+ "\t-t send message type <control msg type>, def. 0\n"
+ "\t-e start network protocol type range <16 bit val>, def. 0\n"
+ "\t-d end network protocol type range <16 bit val>, def. 1500\n"
+ "\t-g start multicast addr range <xx:xx:xx:xx:xx:xx>, def. 0\n"
+ "\t-j end multicast addr range <xx:xx:xx:xx:xx:xx>, def. f\n"
+ "\t-y number of ctrl frame retransmission <integer>, def. 0\n"
+ "\t-u number of bnep frame retransmission <integer>, def. 0\n");
+ printf("Send bnep generic frame:\n"
+ "\t-w send bnep generic frame <bnep generic type>, def. 0\n"
+ "\t-k set src mac addr <xx:xx:xx:xx:xx:xx>, def. 0\n"
+ "\t-f set dst mac addr <xx:xx:xx:xx:xx:xx>, def. 0\n");
+ printf("Options:\n"
+ "\t-T send message timeout after setup <seconds>\n"
+ "\t-N don't close bneptest after disconnect\n");
+}
+
+static struct option main_options[] = {
+ { "device", 1, 0, 'i' },
+ { "listen", 0, 0, 's' },
+ { "connect", 1, 0, 'c' },
+ { "snd_ctrl_msg_type", 1, 0, 't' },
+ { "snd_bnep_msg_type", 1, 0, 'w' },
+ { "src_hw_addr", 1, 0, 'k' },
+ { "dst_hw_addr", 1, 0, 'f' },
+ { "send_timeout", 1, 0, 'T' },
+ { "ntw_proto_down_range", 1, 0, 'd' },
+ { "ntw_proto_up_range", 1, 0, 'e' },
+ { "mcast_addr_down_range", 1, 0, 'g' },
+ { "mcast_addr_up_range", 1, 0, 'j' },
+ { "local_role", 1, 0, 'l' },
+ { "remote_role", 1, 0, 'r' },
+ { "bridge name", 1, 0, 'b' },
+ { "iface name", 1, 0, 'n' },
+ { "no_close", 0, 0, 'N' },
+ { "retrans_ctrl_nb", 0, 0, 'y' },
+ { "retrans_bnep_nb", 0, 0, 'u' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ int opt, i;
+ int err;
+ bool is_set_b_name = false, is_set_i_name = false;
+
+ DBG("");
+
+ signal(SIGINT, exit_handler);
+
+ hci_devba(0, &src_addr);
+ bacpy(&src_addr, BDADDR_ANY);
+
+ mloop = g_main_loop_new(NULL, FALSE);
+ if (!mloop) {
+ printf("cannot create main loop\n");
+
+ exit(1);
+ }
+
+ while ((opt = getopt_long(argc, argv,
+ "+i:c:b:n:t:T:d:e:g:j:k:f:w:l:r:y:u:Nsh",
+ main_options, NULL)) != EOF) {
+ switch (opt) {
+ case 'i':
+ if (!strncmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &src_addr);
+ else
+ str2ba(optarg, &src_addr);
+ break;
+ case 's':
+ mode = MODE_LISTEN;
+ break;
+ case 'c':
+ str2ba(optarg, &dst_addr);
+ mode = MODE_CONNECT;
+ break;
+ case 't':
+ send_ctrl_msg_type_set = true;
+ ctrl_msg_type = atoi(optarg);
+ break;
+ case 'w':
+ send_bnep_msg_type_set = true;
+ bnep_msg_type = atoi(optarg);
+ break;
+ case 'k':
+ for (i = 0; i <= 5; i++, optarg += 3)
+ src_hw_addr[i] = strtol(optarg, NULL, 16);
+ break;
+ case 'f':
+ for (i = 0; i <= 5; i++, optarg += 3)
+ dst_hw_addr[i] = strtol(optarg, NULL, 16);
+ break;
+ case 'T':
+ send_frame_timeout = atoi(optarg);
+ break;
+ case 'd':
+ ntw_proto_down_range = htons(atoi(optarg));
+ break;
+ case 'e':
+ ntw_proto_up_range = htons(atoi(optarg));
+ break;
+ case 'g':
+ for (i = 5; i >= 0; i--, optarg += 3)
+ mcast_addr_down_range[i] =
+ strtol(optarg, NULL, 16);
+ break;
+ case 'j':
+ for (i = 5; i >= 0; i--, optarg += 3)
+ mcast_addr_up_range[i] =
+ strtol(optarg, NULL, 16);
+ break;
+ case 'l':
+ local_role = atoi(optarg);
+ break;
+ case 'r':
+ remote_role = atoi(optarg);
+ break;
+ case 'b':
+ strncpy(bridge, optarg, 16);
+ bridge[15] = '\0';
+ is_set_b_name = true;
+ break;
+ case 'n':
+ strncpy(iface, optarg, 14);
+ strcat(iface, "\%d");
+ iface[15] = '\0';
+ is_set_i_name = true;
+ break;
+ case 'N':
+ no_close_after_disconn = true;
+ break;
+ case 'y':
+ ctrl_msg_retransmition_nb = atoi(optarg);
+ break;
+ case 'u':
+ bnep_msg_retransmission_nb = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ if (!is_set_b_name || !is_set_i_name) {
+ printf("bridge, interface name must be set!\n");
+ exit(1);
+ }
+
+ switch (mode) {
+ case MODE_CONNECT:
+ err = bnep_init();
+ if (err < 0) {
+ printf("cannot initialize bnep\n");
+ exit(1);
+ }
+ err = bnep_client_connect();
+ if (err < 0)
+ exit(1);
+
+ break;
+ case MODE_LISTEN:
+ err = bnep_init();
+ if (err < 0) {
+ printf("cannot initialize bnep\n");
+ exit(1);
+ }
+ err = bnep_server_listen();
+ if (err < 0)
+ exit(1);
+
+ break;
+ default:
+ printf("connect/listen mode not set, exit...\n");
+ exit(1);
+ }
+
+ g_main_loop_run(mloop);
+
+ printf("Done\n");
+
+ g_main_loop_unref(mloop);
+
+ return 0;
+}
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 02d9b57d..ee5315d4 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -67,6 +67,8 @@ struct client {
struct bt_att *att;
struct gatt_db *db;
struct bt_gatt_client *gatt;
+
+ unsigned int reliable_session_id;
};
static void print_prompt(void)
@@ -112,12 +114,6 @@ static const char *ecode_to_string(uint8_t ecode)
return "Group type Not Supported";
case BT_ATT_ERROR_INSUFFICIENT_RESOURCES:
return "Insufficient Resources";
- case BT_ERROR_CCC_IMPROPERLY_CONFIGURED:
- return "CCC Improperly Configured";
- case BT_ERROR_ALREADY_IN_PROGRESS:
- return "Procedure Already in Progress";
- case BT_ERROR_OUT_OF_RANGE:
- return "Out of Range";
default:
return "Unknown error type";
}
@@ -890,6 +886,208 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)
free(value);
}
+static void write_prepare_usage(void)
+{
+ printf("Usage: write-prepare [options] <value_handle> <offset> "
+ "<value>\n"
+ "Options:\n"
+ "\t-s, --session-id\tSession id\n"
+ "e.g.:\n"
+ "\twrite-prepare -s 1 0x0001 00 01 00\n");
+}
+
+static struct option write_prepare_options[] = {
+ { "session-id", 1, 0, 's' },
+ { }
+};
+
+static void cmd_write_prepare(struct client *cli, char *cmd_str)
+{
+ int opt, i;
+ char *argvbuf[516];
+ char **argv = argvbuf;
+ int argc = 0;
+ unsigned int id = 0;
+ uint16_t handle;
+ uint16_t offset;
+ char *endptr = NULL;
+ unsigned int length;
+ uint8_t *value = NULL;
+
+ if (!bt_gatt_client_is_ready(cli->gatt)) {
+ printf("GATT client not initialized\n");
+ return;
+ }
+
+ if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
+ printf("Too many arguments\n");
+ write_value_usage();
+ return;
+ }
+
+ /* Add command name for getopt_long */
+ argc++;
+ argv[0] = "write-prepare";
+
+ optind = 0;
+ while ((opt = getopt_long(argc, argv , "s:", write_prepare_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ if (!optarg) {
+ write_prepare_usage();
+ return;
+ }
+
+ id = atoi(optarg);
+
+ break;
+ default:
+ write_prepare_usage();
+ return;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3) {
+ write_prepare_usage();
+ return;
+ }
+
+ if (cli->reliable_session_id != id) {
+ printf("Session id != Ongoing session id (%u!=%u)\n", id,
+ cli->reliable_session_id);
+ return;
+ }
+
+ handle = strtol(argv[0], &endptr, 0);
+ if (!endptr || *endptr != '\0' || !handle) {
+ printf("Invalid handle: %s\n", argv[0]);
+ return;
+ }
+
+ endptr = NULL;
+ offset = strtol(argv[1], &endptr, 0);
+ if (!endptr || *endptr != '\0' || errno == ERANGE) {
+ printf("Invalid offset: %s\n", argv[1]);
+ return;
+ }
+
+ /*
+ * First two arguments are handle and offset. What remains is the value
+ * length
+ */
+ length = argc - 2;
+
+ if (length == 0)
+ goto done;
+
+ if (length > UINT16_MAX) {
+ printf("Write value too long\n");
+ return;
+ }
+
+ value = malloc(length);
+ if (!value) {
+ printf("Failed to allocate memory for value\n");
+ return;
+ }
+
+ for (i = 2; i < argc; i++) {
+ if (strlen(argv[i]) != 2) {
+ printf("Invalid value byte: %s\n", argv[i]);
+ free(value);
+ return;
+ }
+
+ value[i-2] = strtol(argv[i], &endptr, 0);
+ if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE) {
+ printf("Invalid value byte: %s\n", argv[i]);
+ free(value);
+ return;
+ }
+ }
+
+done:
+ cli->reliable_session_id =
+ bt_gatt_client_prepare_write(cli->gatt, id,
+ handle, offset,
+ value, length,
+ write_long_cb, NULL,
+ NULL);
+ if (!cli->reliable_session_id)
+ printf("Failed to proceed prepare write\n");
+ else
+ printf("Prepare write success.\n"
+ "Session id: %d to be used on next write\n",
+ cli->reliable_session_id);
+
+ free(value);
+}
+
+static void write_execute_usage(void)
+{
+ printf("Usage: write-execute <session_id> <execute>\n"
+ "e.g.:\n"
+ "\twrite-execute 1 0\n");
+}
+
+static void cmd_write_execute(struct client *cli, char *cmd_str)
+{
+ char *argvbuf[516];
+ char **argv = argvbuf;
+ int argc = 0;
+ char *endptr = NULL;
+ unsigned int session_id;
+ bool execute;
+
+ if (!bt_gatt_client_is_ready(cli->gatt)) {
+ printf("GATT client not initialized\n");
+ return;
+ }
+
+ if (!parse_args(cmd_str, 514, argv, &argc)) {
+ printf("Too many arguments\n");
+ write_value_usage();
+ return;
+ }
+
+ if (argc < 2) {
+ write_execute_usage();
+ return;
+ }
+
+ session_id = strtol(argv[0], &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ printf("Invalid session id: %s\n", argv[0]);
+ return;
+ }
+
+ if (session_id != cli->reliable_session_id) {
+ printf("Invalid session id: %u != %u\n", session_id,
+ cli->reliable_session_id);
+ return;
+ }
+
+ execute = !!strtol(argv[1], &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ printf("Invalid execute: %s\n", argv[1]);
+ return;
+ }
+
+ if (execute) {
+ if (!bt_gatt_client_write_execute(cli->gatt, session_id,
+ write_cb, NULL, NULL))
+ printf("Failed to proceed write execute\n");
+ } else {
+ bt_gatt_client_cancel(cli->gatt, session_id);
+ }
+
+ cli->reliable_session_id = 0;
+}
+
static void register_notify_usage(void)
{
printf("Usage: register-notify <chrc value handle>\n");
@@ -1136,14 +1334,18 @@ static struct {
"\tWrite a characteristic or descriptor value" },
{ "write-long-value", cmd_write_long_value,
"Write long characteristic or descriptor value" },
+ { "write-prepare", cmd_write_prepare,
+ "\tWrite prepare characteristic or descriptor value" },
+ { "write-execute", cmd_write_execute,
+ "\tExecute already prepared write" },
{ "register-notify", cmd_register_notify,
"\tSubscribe to not/ind from a characteristic" },
{ "unregister-notify", cmd_unregister_notify,
"Unregister a not/ind session"},
{ "set-sec-level", cmd_set_sec_level,
- "Set security level on le connection"},
+ "\tSet security level on le connection"},
{ "get-sec-level", cmd_get_sec_level,
- "Get security level on le connection"},
+ "\tGet security level on le connection"},
{ "set-sign-key", cmd_set_sign_key,
"\tSet signing key for signed write command"},
{ }
diff --git a/tools/btgatt-server.c b/tools/btgatt-server.c
index b4fbe607..b30a958f 100644
--- a/tools/btgatt-server.c
+++ b/tools/btgatt-server.c
@@ -630,6 +630,7 @@ static void usage(void)
"\t-m, --mtu <mtu>\t\t\tThe ATT MTU to use\n"
"\t-s, --security-level <sec>\tSet security level (low|"
"medium|high)\n"
+ "\t-t, --type [random|public] \t The source address type\n"
"\t-v, --verbose\t\t\tEnable extra logging\n"
"\t-r, --heart-rate\t\tEnable Heart Rate service\n"
"\t-h, --help\t\t\tDisplay help\n");
@@ -639,13 +640,15 @@ static struct option main_options[] = {
{ "index", 1, 0, 'i' },
{ "mtu", 1, 0, 'm' },
{ "security-level", 1, 0, 's' },
+ { "type", 1, 0, 't' },
{ "verbose", 0, 0, 'v' },
{ "heart-rate", 0, 0, 'r' },
{ "help", 0, 0, 'h' },
{ }
};
-static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec)
+static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec,
+ uint8_t src_type)
{
int sk, nsk;
struct sockaddr_l2 srcaddr, addr;
@@ -663,7 +666,7 @@ static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec)
memset(&srcaddr, 0, sizeof(srcaddr));
srcaddr.l2_family = AF_BLUETOOTH;
srcaddr.l2_cid = htobs(ATT_CID);
- srcaddr.l2_bdaddr_type = 0;
+ srcaddr.l2_bdaddr_type = src_type;
bacpy(&srcaddr.l2_bdaddr, src);
if (bind(sk, (struct sockaddr *) &srcaddr, sizeof(srcaddr)) < 0) {
@@ -1131,12 +1134,13 @@ int main(int argc, char *argv[])
int dev_id = -1;
int fd;
int sec = BT_SECURITY_LOW;
+ uint8_t src_type = BDADDR_LE_PUBLIC;
uint16_t mtu = 0;
sigset_t mask;
bool hr_visible = false;
struct server *server;
- while ((opt = getopt_long(argc, argv, "+hvrs:m:i:",
+ while ((opt = getopt_long(argc, argv, "+hvrs:t:m:i:",
main_options, NULL)) != -1) {
switch (opt) {
case 'h':
@@ -1160,6 +1164,17 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
break;
+ case 't':
+ if (strcmp(optarg, "random") == 0)
+ src_type = BDADDR_LE_RANDOM;
+ else if (strcmp(optarg, "public") == 0)
+ src_type = BDADDR_LE_PUBLIC;
+ else {
+ fprintf(stderr,
+ "Allowed types: random, public\n");
+ return EXIT_FAILURE;
+ }
+ break;
case 'm': {
int arg;
@@ -1207,7 +1222,7 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
- fd = l2cap_le_att_listen_and_accept(&src_addr, sec);
+ fd = l2cap_le_att_listen_and_accept(&src_addr, sec, src_type);
if (fd < 0) {
fprintf(stderr, "Failed to accept L2CAP ATT connection\n");
return EXIT_FAILURE;
diff --git a/tools/btmgmt.c b/tools/btmgmt.c
index 8eff56b8..d7af7f08 100644
--- a/tools/btmgmt.c
+++ b/tools/btmgmt.c
@@ -58,6 +58,10 @@
#include "src/shared/util.h"
#include "src/shared/mgmt.h"
+#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
+#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
+#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)
+
static struct mgmt *mgmt = NULL;
static uint16_t mgmt_index = MGMT_INDEX_NONE;
@@ -153,6 +157,56 @@ static size_t bin2hex(const uint8_t *buf, size_t buflen, char *str,
return i;
}
+static void print_eir(const uint8_t *eir, uint16_t eir_len)
+{
+ uint16_t parsed = 0;
+ char str[33];
+
+ while (parsed < eir_len - 1) {
+ uint8_t field_len = eir[0];
+
+ if (field_len == 0)
+ break;
+
+ parsed += field_len + 1;
+
+ if (parsed > eir_len)
+ break;
+
+ switch (eir[1]) {
+ case 0x01:
+ print("Flags: 0x%02x", eir[2]);
+ break;
+ case 0x0d:
+ print("Class of Device: 0x%02x%02x%02x",
+ eir[4], eir[3], eir[2]);
+ break;
+ case 0x1b:
+ ba2str((bdaddr_t *) (eir + 2), str);
+ print("LE Device Address: %s (%s)", str,
+ eir[8] ? "random" : "public");
+ break;
+ case 0x1c:
+ print("LE Role: 0x%02x", eir[2]);
+ break;
+ case 0x22:
+ bin2hex(eir + 2, 16, str, sizeof(str));
+ print("LE SC Confirmation Value: %s", str);
+ break;
+ case 0x23:
+ bin2hex(eir + 2, 16, str, sizeof(str));
+ print("LE SC Random Value: %s", str);
+ break;
+ default:
+ print("Type %u: %u byte%s", eir[1], field_len - 1,
+ (field_len - 1) == 1 ? "" : "s");
+ break;
+ }
+
+ eir += field_len + 1;
+ }
+}
+
static bool load_identity(uint16_t index, struct mgmt_irk_info *irk)
{
char identity_path[PATH_MAX];
@@ -235,6 +289,22 @@ static void unconf_index_removed(uint16_t index, uint16_t len,
print("hci%u removed (unconfigured)", index);
}
+static void ext_index_added(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_ext_index_added *ev = param;
+
+ print("hci%u added (type %u bus %u)", index, ev->type, ev->bus);
+}
+
+static void ext_index_removed(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_ext_index_removed *ev = param;
+
+ print("hci%u removed (type %u bus %u)", index, ev->type, ev->bus);
+}
+
static const char *options_str[] = {
"external",
"public-address",
@@ -472,6 +542,20 @@ static void auth_failed(uint16_t index, uint16_t len, const void *param,
index, addr, ev->status, mgmt_errstr(ev->status));
}
+static void class_of_dev_changed(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_class_of_dev_changed *ev = param;
+
+ if (len != sizeof(*ev)) {
+ error("Invalid class_of_dev_changed length (%u bytes)", len);
+ return;
+ }
+
+ print("hci%u class of device changed: 0x%02x%02x%02x", index,
+ ev->dev_class[2], ev->dev_class[1], ev->dev_class[0]);
+}
+
static void local_name_changed(uint16_t index, uint16_t len, const void *param,
void *user_data)
{
@@ -962,6 +1046,54 @@ static void passkey_notify(uint16_t index, uint16_t len, const void *param,
ev->entered);
}
+static void local_oob_data_updated(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_local_oob_data_updated *ev = param;
+ uint16_t eir_len;
+
+ if (len < sizeof(*ev)) {
+ error("Too small (%u bytes) local_oob_updated event", len);
+ return;
+ }
+
+ eir_len = le16_to_cpu(ev->eir_len);
+ if (len != sizeof(*ev) + eir_len) {
+ error("local_oob_updated: expected %zu bytes, got %u bytes",
+ sizeof(*ev) + eir_len, len);
+ return;
+ }
+
+ print("hci%u oob data updated: type %u len %u", index,
+ ev->type, eir_len);
+}
+
+static void advertising_added(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_advertising_added *ev = param;
+
+ if (len < sizeof(*ev)) {
+ error("Too small (%u bytes) advertising_added event", len);
+ return;
+ }
+
+ print("hci%u advertising_added: instance %u", index, ev->instance);
+}
+
+static void advertising_removed(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_advertising_removed *ev = param;
+
+ if (len < sizeof(*ev)) {
+ error("Too small (%u bytes) advertising_removed event", len);
+ return;
+ }
+
+ print("hci%u advertising_removed: instance %u", index, ev->instance);
+}
+
static void version_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
@@ -1055,6 +1187,43 @@ static void cmd_commands(struct mgmt *mgmt, uint16_t index, int argc,
}
}
+static void config_info_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_config_info *rp = param;
+ uint16_t index = PTR_TO_UINT(user_data);
+ uint32_t supported_options, missing_options;
+
+ if (status != 0) {
+ error("Reading hci%u config failed with status 0x%02x (%s)",
+ index, status, mgmt_errstr(status));
+ goto done;
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small info reply (%u bytes)", len);
+ goto done;
+ }
+
+ print("hci%u:\tUnconfigured controller", index);
+
+ print("\tmanufacturer %u", le16_to_cpu(rp->manufacturer));
+
+ supported_options = le32_to_cpu(rp->supported_options);
+ print("\tsupported options: %s", options2str(supported_options));
+
+ missing_options = le32_to_cpu(rp->missing_options);
+ print("\tmissing options: %s", options2str(missing_options));
+
+done:
+ pending_index--;
+
+ if (pending_index > 0)
+ return;
+
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
static void unconf_index_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
@@ -1065,43 +1234,67 @@ static void unconf_index_rsp(uint8_t status, uint16_t len, const void *param,
if (status != 0) {
error("Reading index list failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
if (len < sizeof(*rp)) {
error("Too small index list reply (%u bytes)", len);
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
- count = get_le16(&rp->num_controllers);
+ count = le16_to_cpu(rp->num_controllers);
if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
error("Index count (%u) doesn't match reply length (%u)",
count, len);
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
print("Unconfigured index list with %u item%s",
- count, count != 1 ? "s" : "");
+ count, count != 1 ? "s" : "");
for (i = 0; i < count; i++) {
- uint16_t index;
+ uint16_t index = le16_to_cpu(rp->index[i]);
- index = get_le16(&rp->index[i]);
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL,
+ config_info_rsp, UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_config_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ pending_index++;
+ }
+
+ if (!count)
+ noninteractive_quit(EXIT_SUCCESS);
+}
- print("\thci%u", index);
+static void cmd_config(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+ if (index == MGMT_INDEX_NONE) {
+ if (!mgmt_send(mgmt, MGMT_OP_READ_UNCONF_INDEX_LIST,
+ MGMT_INDEX_NONE, 0, NULL,
+ unconf_index_rsp, mgmt, NULL)) {
+ error("Unable to send unconf_index_list cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+ return;
}
-done:
- noninteractive_quit(EXIT_SUCCESS);
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL,
+ config_info_rsp, UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_config_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
}
-static void config_info_rsp(uint8_t status, uint16_t len, const void *param,
+static void config_options_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
const struct mgmt_rp_read_config_info *rp = param;
uint16_t index = PTR_TO_UINT(user_data);
+ uint32_t supported_options, missing_options;
if (status != 0) {
error("Reading hci%u config failed with status 0x%02x (%s)",
@@ -1114,39 +1307,21 @@ static void config_info_rsp(uint8_t status, uint16_t len, const void *param,
goto done;
}
- print("hci%u:\tmanufacturer %u", index, get_le16(&rp->manufacturer));
-
- print("\tsupported options: %s",
- options2str(get_le32(&rp->supported_options)));
- print("\tmissing options: %s",
- options2str(get_le32(&rp->missing_options)));
+ print("hci%u:\tConfiguration options", index);
-done:
- noninteractive_quit(EXIT_SUCCESS);
-}
+ supported_options = le32_to_cpu(rp->supported_options);
+ print("\tsupported options: %s", options2str(supported_options));
-static void cmd_config(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
-{
- void *data;
+ missing_options = le32_to_cpu(rp->missing_options);
+ print("\tmissing options: %s", options2str(missing_options));
- if (index == MGMT_INDEX_NONE) {
- if (mgmt_send(mgmt, MGMT_OP_READ_UNCONF_INDEX_LIST,
- MGMT_INDEX_NONE, 0, NULL,
- unconf_index_rsp, mgmt, NULL) == 0) {
- error("Unable to send unconf_index_list cmd");
- return noninteractive_quit(EXIT_FAILURE);
- }
+done:
+ pending_index--;
+ if (pending_index > 0)
return;
- }
- data = UINT_TO_PTR(index);
-
- if (mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL,
- config_info_rsp, data, NULL) == 0) {
- error("Unable to send read_config_info cmd");
- return noninteractive_quit(EXIT_FAILURE);
- }
+ noninteractive_quit(EXIT_SUCCESS);
}
static void info_rsp(uint8_t status, uint16_t len, const void *param,
@@ -1154,10 +1329,9 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param,
{
const struct mgmt_rp_read_info *rp = param;
uint16_t index = PTR_TO_UINT(user_data);
+ uint32_t supported_settings, current_settings;
char addr[18];
- pending_index--;
-
if (status != 0) {
error("Reading hci%u info failed with status 0x%02x (%s)",
index, status, mgmt_errstr(status));
@@ -1169,25 +1343,38 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param,
goto done;
}
+ print("hci%u:\tPrimary controller", index);
+
ba2str(&rp->bdaddr, addr);
- print("hci%u:\taddr %s version %u manufacturer %u"
- " class 0x%02x%02x%02x", index,
- addr, rp->version, get_le16(&rp->manufacturer),
+ print("\taddr %s version %u manufacturer %u class 0x%02x%02x%02x",
+ addr, rp->version, le16_to_cpu(rp->manufacturer),
rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
- print("\tsupported settings: %s",
- settings2str(get_le32(&rp->supported_settings)));
+ supported_settings = le32_to_cpu(rp->supported_settings);
+ print("\tsupported settings: %s", settings2str(supported_settings));
- print("\tcurrent settings: %s",
- settings2str(get_le32(&rp->current_settings)));
+ current_settings = le32_to_cpu(rp->current_settings);
+ print("\tcurrent settings: %s", settings2str(current_settings));
print("\tname %s", rp->name);
print("\tshort name %s", rp->short_name);
- if (pending_index > 0)
+ if (supported_settings & MGMT_SETTING_CONFIGURATION) {
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO,
+ index, 0, NULL, config_options_rsp,
+ UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_config cmd");
+ goto done;
+ }
return;
+ }
done:
+ pending_index--;
+
+ if (pending_index > 0)
+ return;
+
noninteractive_quit(EXIT_SUCCESS);
}
@@ -1210,7 +1397,7 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param,
return noninteractive_quit(EXIT_FAILURE);
}
- count = get_le16(&rp->num_controllers);
+ count = le16_to_cpu(rp->num_controllers);
if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
error("Index count (%u) doesn't match reply length (%u)",
@@ -1221,15 +1408,10 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param,
print("Index list with %u item%s", count, count != 1 ? "s" : "");
for (i = 0; i < count; i++) {
- uint16_t index;
- void *data;
-
- index = get_le16(&rp->index[i]);
+ uint16_t index = le16_to_cpu(rp->index[i]);
- data = UINT_TO_PTR(index);
-
- if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
- info_rsp, data, NULL) == 0) {
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
+ info_rsp, UINT_TO_PTR(index), NULL)) {
error("Unable to send read_info cmd");
return noninteractive_quit(EXIT_FAILURE);
}
@@ -1243,12 +1425,10 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param,
static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
{
- void *data;
-
if (index == MGMT_INDEX_NONE) {
- if (mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST,
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST,
MGMT_INDEX_NONE, 0, NULL,
- index_rsp, mgmt, NULL) == 0) {
+ index_rsp, mgmt, NULL)) {
error("Unable to send index_list cmd");
return noninteractive_quit(EXIT_FAILURE);
}
@@ -1256,15 +1436,93 @@ static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
return;
}
- data = UINT_TO_PTR(index);
-
- if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp,
- data, NULL) == 0) {
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp,
+ UINT_TO_PTR(index), NULL)) {
error("Unable to send read_info cmd");
return noninteractive_quit(EXIT_FAILURE);
}
}
+static void ext_index_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_ext_index_list *rp = param;
+ uint16_t count, index_filter = PTR_TO_UINT(user_data);
+ unsigned int i;
+
+ if (status != 0) {
+ error("Reading ext index list failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small ext index list reply (%u bytes)", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ count = get_le16(&rp->num_controllers);
+
+ if (len < sizeof(*rp) + count * (sizeof(uint16_t) + sizeof(uint8_t))) {
+ error("Index count (%u) doesn't match reply length (%u)",
+ count, len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ print("Extended index list with %u item%s",
+ count, count != 1 ? "s" : "");
+
+ for (i = 0; i < count; i++) {
+ uint16_t index = le16_to_cpu(rp->entry[i].index);
+
+ if (index_filter != MGMT_INDEX_NONE && index_filter != index)
+ continue;
+
+ switch (rp->entry[i].type) {
+ case 0x00:
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INFO,
+ index, 0, NULL, info_rsp,
+ UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+ pending_index++;
+ break;
+ case 0x01:
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO,
+ index, 0, NULL, config_info_rsp,
+ UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_config cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+ pending_index++;
+ break;
+ case 0x02:
+ print("hci%u:\tAMP controller (%u)", index,
+ rp->entry[i].bus);
+ break;
+ default:
+ print("hci%u:\tType %u controller (%u)", index,
+ rp->entry[i].type, rp->entry[i].bus);
+ break;
+ }
+ }
+
+ if (!count)
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_extinfo(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INDEX_LIST,
+ MGMT_INDEX_NONE, 0, NULL,
+ ext_index_rsp, UINT_TO_PTR(index), NULL)) {
+ error("Unable to send ext_index_list cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
/* Wrapper to get the index and opcode to the response callback */
struct command_data {
uint16_t id;
@@ -1540,7 +1798,7 @@ static void class_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len,
}
print("%s succeeded. Class 0x%02x%02x%02x", mgmt_opstr(op),
- rp->class_of_dev[2], rp->class_of_dev[1], rp->class_of_dev[0]);
+ rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
noninteractive_quit(EXIT_SUCCESS);
}
@@ -1621,9 +1879,11 @@ static void cmd_disconnect(struct mgmt *mgmt, uint16_t index, int argc,
break;
case 'h':
disconnect_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
disconnect_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -1739,7 +1999,7 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc,
uuid_t uuid;
uint128_t uint128;
uuid_t uuid128;
- uint8_t type;
+ uint8_t type = SCAN_TYPE_DUAL;
int8_t rssi;
uint16_t count;
int opt;
@@ -1747,10 +2007,6 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc,
if (index == MGMT_INDEX_NONE)
index = 0;
- type = 0;
- type |= (1 << BDADDR_BREDR);
- type |= (1 << BDADDR_LE_PUBLIC);
- type |= (1 << BDADDR_LE_RANDOM);
rssi = 127;
count = 0;
@@ -1763,23 +2019,23 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc,
find_service_options, NULL)) != -1) {
switch (opt) {
case 'l':
- type &= ~(1 << BDADDR_BREDR);
- type |= (1 << BDADDR_LE_PUBLIC);
- type |= (1 << BDADDR_LE_RANDOM);
+ type &= ~SCAN_TYPE_BREDR;
+ type |= SCAN_TYPE_LE;
break;
case 'b':
- type |= (1 << BDADDR_BREDR);
- type &= ~(1 << BDADDR_LE_PUBLIC);
- type &= ~(1 << BDADDR_LE_RANDOM);
+ type |= SCAN_TYPE_BREDR;
+ type &= ~SCAN_TYPE_LE;
break;
case 'u':
if (count == MAX_UUIDS) {
print("Max %u UUIDs supported", MAX_UUIDS);
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
if (bt_string2uuid(&uuid, optarg) < 0) {
print("Invalid UUID: %s", optarg);
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
cp = (void *) buf;
@@ -1793,9 +2049,11 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc,
break;
case 'h':
find_service_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
find_service_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -1850,35 +2108,30 @@ static struct option find_options[] = {
static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
{
struct mgmt_cp_start_discovery cp;
- uint8_t type;
+ uint8_t type = SCAN_TYPE_DUAL;
int opt;
if (index == MGMT_INDEX_NONE)
index = 0;
- type = 0;
- type |= (1 << BDADDR_BREDR);
- type |= (1 << BDADDR_LE_PUBLIC);
- type |= (1 << BDADDR_LE_RANDOM);
-
while ((opt = getopt_long(argc, argv, "+lbh", find_options,
NULL)) != -1) {
switch (opt) {
case 'l':
- type &= ~(1 << BDADDR_BREDR);
- type |= (1 << BDADDR_LE_PUBLIC);
- type |= (1 << BDADDR_LE_RANDOM);
+ type &= ~SCAN_TYPE_BREDR;
+ type |= SCAN_TYPE_LE;
break;
case 'b':
- type |= (1 << BDADDR_BREDR);
- type &= ~(1 << BDADDR_LE_PUBLIC);
- type &= ~(1 << BDADDR_LE_RANDOM);
+ type |= SCAN_TYPE_BREDR;
+ type &= ~SCAN_TYPE_LE;
break;
case 'h':
find_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
find_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -1897,6 +2150,78 @@ static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
}
}
+static void stop_find_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr,
+ "Stop Discovery failed: status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+
+ printf("Discovery stopped\n");
+ discovery = false;
+
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void stop_find_usage(void)
+{
+ printf("Usage: btmgmt stop-find [-l|-b]>\n");
+}
+
+static struct option stop_find_options[] = {
+ { "help", 0, 0, 'h' },
+ { "le-only", 1, 0, 'l' },
+ { "bredr-only", 1, 0, 'b' },
+ { 0, 0, 0, 0 }
+};
+
+static void cmd_stop_find(struct mgmt *mgmt, uint16_t index, int argc,
+ char **argv)
+{
+ struct mgmt_cp_stop_discovery cp;
+ uint8_t type = SCAN_TYPE_DUAL;
+ int opt;
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ while ((opt = getopt_long(argc, argv, "+lbh", stop_find_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 'l':
+ type &= ~SCAN_TYPE_BREDR;
+ type |= SCAN_TYPE_LE;
+ break;
+ case 'b':
+ type |= SCAN_TYPE_BREDR;
+ type &= ~SCAN_TYPE_LE;
+ break;
+ case 'h':
+ default:
+ stop_find_usage();
+ optind = 0;
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.type = type;
+
+ if (mgmt_send(mgmt, MGMT_OP_STOP_DISCOVERY, index, sizeof(cp), &cp,
+ stop_find_rsp, NULL, NULL) == 0) {
+ fprintf(stderr, "Unable to send stop_discovery cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
static void name_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
@@ -1995,9 +2320,11 @@ static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
break;
case 'h':
pair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
pair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -2084,9 +2411,11 @@ static void cmd_cancel_pair(struct mgmt *mgmt, uint16_t index, int argc,
break;
case 'h':
cancel_pair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
cancel_pair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -2168,9 +2497,11 @@ static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc,
break;
case 'h':
unpair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
unpair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -2299,6 +2630,7 @@ static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
case 'l':
if (count >= MAX_IRKS) {
error("Number of IRKs exceeded");
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
if (strlen(optarg) > 3 &&
@@ -2308,15 +2640,18 @@ static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
local_index = atoi(optarg);
if (!load_identity(local_index, &cp->irks[count])) {
error("Unable to load identity");
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
count++;
break;
case 'h':
irks_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
irks_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -2394,9 +2729,11 @@ static void cmd_block(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
break;
case 'h':
block_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
block_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -2444,9 +2781,11 @@ static void cmd_unblock(struct mgmt *mgmt, uint16_t index, int argc,
break;
case 'h':
unblock_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
unblock_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -2555,7 +2894,6 @@ static void local_oob_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
const struct mgmt_rp_read_local_oob_data *rp = param;
- const struct mgmt_rp_read_local_oob_ext_data *rp_ext = param;
char str[33];
if (status != 0) {
@@ -2569,19 +2907,19 @@ static void local_oob_rsp(uint8_t status, uint16_t len, const void *param,
return noninteractive_quit(EXIT_FAILURE);
}
- bin2hex(rp->hash, 16, str, sizeof(str));
+ bin2hex(rp->hash192, 16, str, sizeof(str));
print("Hash C from P-192: %s", str);
- bin2hex(rp->randomizer, 16, str, sizeof(str));
+ bin2hex(rp->rand192, 16, str, sizeof(str));
print("Randomizer R with P-192: %s", str);
- if (len < sizeof(*rp_ext))
+ if (len < sizeof(*rp))
return noninteractive_quit(EXIT_SUCCESS);
- bin2hex(rp_ext->hash256, 16, str, sizeof(str));
+ bin2hex(rp->hash256, 16, str, sizeof(str));
print("Hash C from P-256: %s", str);
- bin2hex(rp_ext->randomizer256, 16, str, sizeof(str));
+ bin2hex(rp->rand256, 16, str, sizeof(str));
print("Randomizer R with P-256: %s", str);
noninteractive_quit(EXIT_SUCCESS);
@@ -3236,6 +3574,456 @@ static void cmd_clr_devices(struct mgmt *mgmt, uint16_t index,
cmd_del_device(mgmt, index, 2, rm_argv);
}
+static void local_oob_ext_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_local_oob_ext_data *rp = param;
+ uint16_t eir_len;
+
+ if (status != 0) {
+ error("Read Local OOB Ext Data failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small (%u bytes) read_local_oob_ext rsp", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ eir_len = le16_to_cpu(rp->eir_len);
+ if (len != sizeof(*rp) + eir_len) {
+ error("local_oob_ext: expected %zu bytes, got %u bytes",
+ sizeof(*rp) + eir_len, len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ print_eir(rp->eir, eir_len);
+
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_bredr_oob(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ struct mgmt_cp_read_local_oob_ext_data cp;
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ cp.type = SCAN_TYPE_BREDR;
+
+ if (!mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
+ index, sizeof(cp), &cp,
+ local_oob_ext_rsp, NULL, NULL)) {
+ error("Unable to send read_local_oob_ext cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
+static void cmd_le_oob(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ struct mgmt_cp_read_local_oob_ext_data cp;
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ cp.type = SCAN_TYPE_LE;
+
+ if (!mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
+ index, sizeof(cp), &cp,
+ local_oob_ext_rsp, NULL, NULL)) {
+ error("Unable to send read_local_oob_ext cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
+static const char *adv_flags_str[] = {
+ "connectable",
+ "general-discoverable",
+ "limited-discoverable",
+ "managed-flags",
+ "tx-power",
+ "scan-rsp-appearance",
+ "scan-rsp-local-name",
+};
+
+static const char *adv_flags2str(uint32_t flags)
+{
+ static char str[256];
+ unsigned i;
+ int off;
+
+ off = 0;
+ str[0] = '\0';
+
+ for (i = 0; i < NELEM(adv_flags_str); i++) {
+ if ((flags & (1 << i)) != 0)
+ off += snprintf(str + off, sizeof(str) - off, "%s ",
+ adv_flags_str[i]);
+ }
+
+ return str;
+}
+
+static void adv_features_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_adv_features *rp = param;
+ uint32_t supported_flags;
+
+ if (status != 0) {
+ error("Reading adv features failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small adv features reply (%u bytes)", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp) + rp->num_instances * sizeof(uint8_t)) {
+ error("Instances count (%u) doesn't match reply length (%u)",
+ rp->num_instances, len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ supported_flags = le32_to_cpu(rp->supported_flags);
+ print("Supported flags: %s", adv_flags2str(supported_flags));
+ print("Max advertising data len: %u", rp->max_adv_data_len);
+ print("Max scan response data len: %u", rp->max_scan_rsp_len);
+ print("Max instances: %u", rp->max_instances);
+
+ print("Instances list with %u item%s", rp->num_instances,
+ rp->num_instances != 1 ? "s" : "");
+
+ return noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_advinfo(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ if (!mgmt_send(mgmt, MGMT_OP_READ_ADV_FEATURES, index, 0, NULL,
+ adv_features_rsp, NULL, NULL)) {
+ error("Unable to send advertising features command");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
+static void add_adv_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_add_advertising *rp = param;
+
+ if (status != 0) {
+ error("Add Advertising failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len != sizeof(*rp)) {
+ error("Invalid Add Advertising response length (%u)", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ print("Instance added: %u", rp->instance);
+
+ return noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void add_adv_usage(void)
+{
+ print("Usage: add-adv [options] <instance_id>\nOptions:\n"
+ "\t -u, --uuid <uuid> Service UUID\n"
+ "\t -d, --adv-data <data> Advertising Data bytes\n"
+ "\t -s, --scan-rsp <data> Scan Response Data bytes\n"
+ "\t -t, --timeout <timeout> Timeout in seconds\n"
+ "\t -c, --connectable \"connectable\" flag\n"
+ "\t -g, --general-discov \"general-discoverable\" flag\n"
+ "\t -l, --limited-discov \"limited-discoverable\" flag\n"
+ "\t -m, --managed-flags \"managed-flags\" flag\n"
+ "\t -p, --tx-power \"tx-power\" flag\n"
+ "e.g.:\n"
+ "\tadd-adv -u 180d -u 180f -d 080954657374204C45 1");
+}
+
+static struct option add_adv_options[] = {
+ { "help", 0, 0, 'h' },
+ { "uuid", 1, 0, 'u' },
+ { "adv-data", 1, 0, 'd' },
+ { "scan-rsp", 1, 0, 's' },
+ { "timeout", 1, 0, 't' },
+ { "connectable", 0, 0, 'c' },
+ { "general-discov", 0, 0, 'g' },
+ { "limited-discov", 0, 0, 'l' },
+ { "managed-flags", 0, 0, 'm' },
+ { "tx-power", 0, 0, 'p' },
+ { 0, 0, 0, 0}
+};
+
+static bool parse_bytes(char *optarg, uint8_t **bytes, size_t *len)
+{
+ unsigned i;
+
+ if (!optarg) {
+ add_adv_usage();
+ return false;
+ }
+
+ *len = strlen(optarg);
+
+ if (*len % 2) {
+ error("Malformed data");
+ return false;
+ }
+
+ *len /= 2;
+ if (*len > UINT8_MAX) {
+ error("Data too long");
+ return false;
+ }
+
+ *bytes = malloc(*len);
+ if (!*bytes) {
+ error("Failed to allocate memory");
+ return false;
+ }
+
+ for (i = 0; i < *len; i++) {
+ if (sscanf(optarg + (i * 2), "%2hhx", *bytes + i) != 1) {
+ error("Invalid data");
+ free(*bytes);
+ *bytes = NULL;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+#define MAX_AD_UUID_BYTES 32
+
+static void cmd_add_adv(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ struct mgmt_cp_add_advertising *cp = NULL;
+ int opt;
+ uint8_t *adv_data = NULL, *scan_rsp = NULL;
+ size_t adv_len = 0, scan_rsp_len = 0;
+ size_t cp_len;
+ uint8_t uuids[MAX_AD_UUID_BYTES];
+ size_t uuid_bytes = 0;
+ uint8_t uuid_type = 0;
+ uint16_t timeout = 0;
+ uint8_t instance;
+ uuid_t uuid;
+ bool success = false;
+ bool quit = true;
+ uint32_t flags = 0;
+
+ while ((opt = getopt_long(argc, argv, "+u:d:s:t:cglmph",
+ add_adv_options, NULL)) != -1) {
+ switch (opt) {
+ case 'u':
+ if (bt_string2uuid(&uuid, optarg) < 0) {
+ print("Invalid UUID: %s", optarg);
+ goto done;
+ }
+
+ if (uuid_type && uuid_type != uuid.type) {
+ print("UUID types must be consistent");
+ goto done;
+ }
+
+ if (uuid.type == SDP_UUID16) {
+ if (uuid_bytes + 2 >= MAX_AD_UUID_BYTES) {
+ print("Too many UUIDs");
+ goto done;
+ }
+
+ put_le16(uuid.value.uuid16, uuids + uuid_bytes);
+ uuid_bytes += 2;
+ } else if (uuid.type == SDP_UUID128) {
+ if (uuid_bytes + 16 >= MAX_AD_UUID_BYTES) {
+ print("Too many UUIDs");
+ goto done;
+ }
+
+ bswap_128(uuid.value.uuid128.data,
+ uuids + uuid_bytes);
+ uuid_bytes += 16;
+ } else {
+ printf("Unsupported UUID type");
+ goto done;
+ }
+
+ if (!uuid_type)
+ uuid_type = uuid.type;
+
+ break;
+ case 'd':
+ if (adv_len) {
+ print("Only one adv-data option allowed");
+ goto done;
+ }
+
+ if (!parse_bytes(optarg, &adv_data, &adv_len))
+ goto done;
+ break;
+ case 's':
+ if (scan_rsp_len) {
+ print("Only one scan-rsp option allowed");
+ goto done;
+ }
+
+ if (!parse_bytes(optarg, &scan_rsp, &scan_rsp_len))
+ goto done;
+ break;
+ case 't':
+ timeout = strtol(optarg, NULL, 0);
+ break;
+ case 'c':
+ flags |= MGMT_ADV_FLAG_CONNECTABLE;
+ break;
+ case 'g':
+ flags |= MGMT_ADV_FLAG_DISCOV;
+ break;
+ case 'l':
+ flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
+ break;
+ case 'm':
+ flags |= MGMT_ADV_FLAG_MANAGED_FLAGS;
+ break;
+ case 'p':
+ flags |= MGMT_ADV_FLAG_TX_POWER;
+ break;
+ case 'h':
+ success = true;
+ default:
+ add_adv_usage();
+ goto done;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc != 1) {
+ add_adv_usage();
+ goto done;
+ }
+
+ if (uuid_bytes)
+ uuid_bytes += 2;
+
+ instance = strtol(argv[0], NULL, 0);
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ cp_len = sizeof(*cp) + uuid_bytes + adv_len + scan_rsp_len;
+ cp = malloc0(cp_len);
+ if (!cp)
+ goto done;
+
+ cp->instance = instance;
+ put_le32(flags, &cp->flags);
+ put_le16(timeout, &cp->timeout);
+ cp->adv_data_len = adv_len + uuid_bytes;
+ cp->scan_rsp_len = scan_rsp_len;
+
+ if (uuid_bytes) {
+ cp->data[0] = uuid_bytes - 1;
+ cp->data[1] = uuid_type == SDP_UUID16 ? 0x03 : 0x07;
+ memcpy(cp->data + 2, uuids, uuid_bytes - 2);
+ }
+
+ memcpy(cp->data + uuid_bytes, adv_data, adv_len);
+ memcpy(cp->data + uuid_bytes + adv_len, scan_rsp, scan_rsp_len);
+
+ if (!mgmt_send(mgmt, MGMT_OP_ADD_ADVERTISING, index, cp_len, cp,
+ add_adv_rsp, NULL, NULL)) {
+ error("Unable to send \"Add Advertising\" command");
+ goto done;
+ }
+
+ quit = false;
+
+done:
+ free(adv_data);
+ free(scan_rsp);
+ free(cp);
+
+ if (quit)
+ noninteractive_quit(success ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static void rm_adv_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_remove_advertising *rp = param;
+
+ if (status != 0) {
+ error("Remove Advertising failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len != sizeof(*rp)) {
+ error("Invalid Remove Advertising response length (%u)", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ print("Instance removed: %u", rp->instance);
+
+ return noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void rm_adv_usage(void)
+{
+ print("Usage: rm-adv <instance_id>");
+}
+
+static void cmd_rm_adv(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_remove_advertising cp;
+ uint8_t instance;
+
+ if (argc != 2) {
+ rm_adv_usage();
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ instance = strtol(argv[1], NULL, 0);
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.instance = instance;
+
+ if (!mgmt_send(mgmt, MGMT_OP_REMOVE_ADVERTISING, index, sizeof(cp), &cp,
+ rm_adv_rsp, NULL, NULL)) {
+ error("Unable to send \"Remove Advertising\" command");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
+static void cmd_clr_adv(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+ char *all_instances = "0";
+ char *rm_argv[] = { "rm-adv", all_instances, NULL };
+
+ cmd_rm_adv(mgmt, index, 2, rm_argv);
+}
+
struct cmd_info {
char *cmd;
void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv);
@@ -3249,6 +4037,7 @@ static struct cmd_info all_cmd[] = {
{ "commands", cmd_commands, "List supported commands" },
{ "config", cmd_config, "Show configuration info" },
{ "info", cmd_info, "Show controller info" },
+ { "extinfo", cmd_extinfo, "Show extended controller info" },
{ "power", cmd_power, "Toggle powered state" },
{ "discov", cmd_discov, "Toggle discoverable state" },
{ "connectable",cmd_connectable,"Toggle connectable state" },
@@ -3268,6 +4057,7 @@ static struct cmd_info all_cmd[] = {
{ "con", cmd_con, "List connections" },
{ "find", cmd_find, "Discover nearby devices" },
{ "find-service", cmd_find_service, "Discover nearby service" },
+ { "stop-find", cmd_stop_find, "Stop discovery" },
{ "name", cmd_name, "Set local name" },
{ "pair", cmd_pair, "Pair with a remote device" },
{ "cancelpair", cmd_cancel_pair,"Cancel pairing" },
@@ -3294,6 +4084,12 @@ static struct cmd_info all_cmd[] = {
{ "add-device", cmd_add_device, "Add Device" },
{ "del-device", cmd_del_device, "Remove Device" },
{ "clr-devices",cmd_clr_devices,"Clear Devices" },
+ { "bredr-oob", cmd_bredr_oob, "Local OOB data (BR/EDR)" },
+ { "le-oob", cmd_le_oob, "Local OOB data (LE)" },
+ { "advinfo", cmd_advinfo, "Show advertising features" },
+ { "add-adv", cmd_add_adv, "Add advertising instance" },
+ { "rm-adv", cmd_rm_adv, "Remove advertising instance" },
+ { "clr-adv", cmd_clr_adv, "Clear advertising instances" },
};
static void cmd_quit(struct mgmt *mgmt, uint16_t index,
@@ -3324,6 +4120,8 @@ static void register_mgmt_callbacks(struct mgmt *mgmt, uint16_t index)
NULL, NULL);
mgmt_register(mgmt, MGMT_EV_AUTH_FAILED, index, auth_failed,
NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_CLASS_OF_DEV_CHANGED, index,
+ class_of_dev_changed, NULL, NULL);
mgmt_register(mgmt, MGMT_EV_LOCAL_NAME_CHANGED, index,
local_name_changed, NULL, NULL);
mgmt_register(mgmt, MGMT_EV_DEVICE_FOUND, index, device_found,
@@ -3342,7 +4140,16 @@ static void register_mgmt_callbacks(struct mgmt *mgmt, uint16_t index)
unconf_index_removed, NULL, NULL);
mgmt_register(mgmt, MGMT_EV_NEW_CONFIG_OPTIONS, index,
new_config_options, NULL, NULL);
-
+ mgmt_register(mgmt, MGMT_EV_EXT_INDEX_ADDED, index,
+ ext_index_added, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_EXT_INDEX_REMOVED, index,
+ ext_index_removed, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_LOCAL_OOB_DATA_UPDATED, index,
+ local_oob_data_updated, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_ADVERTISING_ADDED, index,
+ advertising_added, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_ADVERTISING_REMOVED, index,
+ advertising_removed, NULL, NULL);
}
static void cmd_select(struct mgmt *mgmt, uint16_t index,
diff --git a/tools/btsnoop.c b/tools/btsnoop.c
index 3eb8082d..278e18ce 100644
--- a/tools/btsnoop.c
+++ b/tools/btsnoop.c
@@ -34,6 +34,8 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
+#include <time.h>
+#include <sys/time.h>
#include <getopt.h>
#include <endian.h>
#include <arpa/inet.h>
@@ -272,6 +274,86 @@ close_input:
close(input_fd[i]);
}
+#define BT_SNOOP_TYPE_HCI_PREFIX "btsnoop_type_hci"
+
+static void command_split(const char *input)
+{
+ unsigned char buf[BTSNOOP_MAX_PACKET_SIZE];
+ uint16_t pktlen,opcode;
+ uint32_t type;
+ struct timeval tv;
+ uint16_t index, max_index = 0;
+ char write_file_name[255];
+ struct btsnoop *btsnoop_read_file = NULL;
+ struct btsnoop *btsnoop_write_file[16];
+ time_t t;
+ struct tm tm;
+ unsigned long num_packets = 0;
+
+ btsnoop_read_file = btsnoop_open(input, BTSNOOP_FLAG_PKLG_SUPPORT);
+ if (!btsnoop_read_file)
+ return;
+
+ type = btsnoop_get_type(btsnoop_read_file);
+ if (type != BTSNOOP_TYPE_MONITOR) {
+ fprintf(stderr, "unsupported link data type %u\n", type);
+ btsnoop_unref(btsnoop_read_file);
+ return;
+ }
+
+next_packet:
+ if (!btsnoop_read_hci(btsnoop_read_file, &tv, &index, &opcode, buf,
+ &pktlen))
+ goto close_files;
+
+ if (opcode == 0xffff)
+ goto next_packet;
+
+ switch (opcode) {
+ case BTSNOOP_OPCODE_NEW_INDEX:
+ t = tv.tv_sec;
+ localtime_r(&t, &tm);
+
+ if (max_index < index)
+ max_index = index;
+
+ sprintf(write_file_name, "%s%d_%02d:%02d:%02d.%06lu.log",
+ BT_SNOOP_TYPE_HCI_PREFIX, index, tm.tm_hour, tm.tm_min,
+ tm.tm_sec, tv.tv_usec);
+
+ printf("New Index %d would be saved in %s\n", index,
+ write_file_name);
+
+ btsnoop_write_file[index] = btsnoop_create(write_file_name,
+ BTSNOOP_TYPE_HCI, -1, -1);
+ if (!btsnoop_write_file[index])
+ goto close_files;
+
+ break;
+ case BTSNOOP_OPCODE_DEL_INDEX:
+ printf("Del Index %d\n", index);
+
+ btsnoop_unref(btsnoop_write_file[index]);
+ btsnoop_write_file[index] = NULL;
+ break;
+ default:
+ btsnoop_write_hci(btsnoop_write_file[index], &tv, index,
+ opcode, buf, pktlen);
+ }
+ num_packets++;
+
+ goto next_packet;
+
+close_files:
+ for (index = 0; index < max_index; index++)
+ btsnoop_unref(btsnoop_write_file[index]);
+
+ btsnoop_unref(btsnoop_read_file);
+
+ printf("BT Snoop data link transfer is completed for %lu packets\n",
+ num_packets);
+}
+
static void command_extract_eir(const char *input)
{
struct btsnoop_pkt pkt;
@@ -518,6 +600,7 @@ static void usage(void)
printf("commands:\n"
"\t-m, --merge <output> Merge multiple btsnoop files\n"
"\t-e, --extract <input> Extract data from btsnoop file\n"
+ "\t-s, --split <input> Split btmon file into legacy btsnoop file(s)\n"
"\t-h, --help Show help options\n");
}
@@ -525,12 +608,13 @@ static const struct option main_options[] = {
{ "merge", required_argument, NULL, 'm' },
{ "extract", required_argument, NULL, 'e' },
{ "type", required_argument, NULL, 't' },
+ { "split", required_argument, NULL, 's' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ }
};
-enum { INVALID, MERGE, EXTRACT };
+enum { INVALID, MERGE, EXTRACT, SPLIT };
int main(int argc, char *argv[])
{
@@ -542,7 +626,7 @@ int main(int argc, char *argv[])
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "m:e:t:vh", main_options, NULL);
+ opt = getopt_long(argc, argv, "m:e:s:t:vh", main_options, NULL);
if (opt < 0)
break;
@@ -555,6 +639,9 @@ int main(int argc, char *argv[])
command = EXTRACT;
input_path = optarg;
break;
+ case 's':
+ command = SPLIT;
+ input_path = optarg;
case 't':
type = optarg;
break;
@@ -600,6 +687,15 @@ int main(int argc, char *argv[])
fprintf(stderr, "extract type not supported\n");
break;
+ case SPLIT:
+ if (argc - optind > 0) {
+ fprintf(stderr, "extra arguments not allowed\n");
+ return EXIT_FAILURE;
+ }
+
+ command_split(input_path);
+ break;
+
default:
usage();
return EXIT_FAILURE;
diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c
index e5fb8894..d9970103 100644
--- a/tools/mgmt-tester.c
+++ b/tools/mgmt-tester.c
@@ -319,6 +319,25 @@ static void test_condition_complete(struct test_data *data)
test_post_teardown, 2, user, free); \
} while (0)
+#define test_bredr20(name, data, setup, func) \
+ do { \
+ struct test_data *user; \
+ user = malloc(sizeof(struct test_data)); \
+ if (!user) \
+ break; \
+ user->hciemu_type = HCIEMU_TYPE_LEGACY; \
+ user->test_setup = setup; \
+ user->test_data = data; \
+ user->expected_version = 0x04; \
+ user->expected_manufacturer = 0x003f; \
+ user->expected_supported_settings = 0x000010bf; \
+ user->initial_settings = 0x00000080; \
+ user->unmet_conditions = 0; \
+ tester_add_full(name, data, \
+ test_pre_setup, test_setup, func, NULL, \
+ test_post_teardown, 2, user, free); \
+ } while (0)
+
#define test_bredr(name, data, setup, func) \
do { \
struct test_data *user; \
@@ -378,6 +397,7 @@ struct generic_data {
uint16_t send_len;
const void * (*send_func)(uint16_t *len);
uint8_t expect_status;
+ bool expect_ignore_param;
const void *expect_param;
uint16_t expect_len;
const void * (*expect_func)(uint16_t *len);
@@ -477,6 +497,45 @@ static const struct generic_data read_info_invalid_index_test = {
.expect_status = MGMT_STATUS_INVALID_INDEX,
};
+static const struct generic_data read_unconf_index_list_invalid_param_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_UNCONF_INDEX_LIST,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_unconf_index_list_invalid_index_test = {
+ .send_opcode = MGMT_OP_READ_UNCONF_INDEX_LIST,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_config_info_invalid_param_test = {
+ .send_opcode = MGMT_OP_READ_CONFIG_INFO,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_config_info_invalid_index_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_CONFIG_INFO,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_ext_index_list_invalid_param_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_EXT_INDEX_LIST,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_ext_index_list_invalid_index_test = {
+ .send_opcode = MGMT_OP_READ_EXT_INDEX_LIST,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
static const char set_powered_on_param[] = { 0x01 };
static const char set_powered_invalid_param[] = { 0x02 };
static const char set_powered_garbage_param[] = { 0x01, 0x00 };
@@ -797,6 +856,12 @@ static const struct generic_data set_connectable_off_le_test_2 = {
.expect_hci_len = sizeof(set_connectable_off_adv_param),
};
+static uint16_t settings_powered_le_discoverable[] = {
+ MGMT_OP_SET_LE,
+ MGMT_OP_SET_CONNECTABLE,
+ MGMT_OP_SET_POWERED,
+ MGMT_OP_SET_DISCOVERABLE, 0 };
+
static uint16_t settings_powered_le_discoverable_advertising[] = {
MGMT_OP_SET_LE,
MGMT_OP_SET_CONNECTABLE,
@@ -835,6 +900,8 @@ static const struct generic_data set_connectable_off_le_test_4 = {
static const char set_fast_conn_on_param[] = { 0x01 };
static const char set_fast_conn_on_settings_1[] = { 0x87, 0x00, 0x00, 0x00 };
+static const char set_fast_conn_on_settings_2[] = { 0x85, 0x00, 0x00, 0x00 };
+static const char set_fast_conn_on_settings_3[] = { 0x84, 0x00, 0x00, 0x00 };
static const struct generic_data set_fast_conn_on_success_test_1 = {
.setup_settings = settings_powered_connectable,
@@ -847,6 +914,27 @@ static const struct generic_data set_fast_conn_on_success_test_1 = {
.expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE,
};
+static const struct generic_data set_fast_conn_on_success_test_2 = {
+ .setup_settings = settings_powered,
+ .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+ .send_param = set_fast_conn_on_param,
+ .send_len = sizeof(set_fast_conn_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_fast_conn_on_settings_2,
+ .expect_len = sizeof(set_fast_conn_on_settings_2),
+ .expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE,
+};
+
+static const struct generic_data set_fast_conn_on_success_test_3 = {
+ .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+ .send_param = set_fast_conn_on_param,
+ .send_len = sizeof(set_fast_conn_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_fast_conn_on_settings_3,
+ .expect_len = sizeof(set_fast_conn_on_settings_3),
+ .expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE,
+};
+
static const struct generic_data set_fast_conn_on_not_supported_test_1 = {
.setup_settings = settings_powered_connectable,
.send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
@@ -855,6 +943,15 @@ static const struct generic_data set_fast_conn_on_not_supported_test_1 = {
.expect_status = MGMT_STATUS_NOT_SUPPORTED,
};
+static const char set_fast_conn_nval_param[] = { 0xff };
+
+static const struct generic_data set_fast_conn_nval_param_test_1 = {
+ .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+ .send_param = set_fast_conn_nval_param,
+ .send_len = sizeof(set_fast_conn_nval_param),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
static const char set_bondable_on_param[] = { 0x01 };
static const char set_bondable_invalid_param[] = { 0x02 };
static const char set_bondable_garbage_param[] = { 0x01, 0x00 };
@@ -1286,6 +1383,10 @@ static const struct generic_data set_ssp_on_invalid_index_test = {
static uint16_t settings_powered_ssp[] = { MGMT_OP_SET_SSP,
MGMT_OP_SET_POWERED, 0 };
+static uint16_t settings_powered_sc[] = { MGMT_OP_SET_SSP,
+ MGMT_OP_SET_SECURE_CONN,
+ MGMT_OP_SET_POWERED, 0 };
+
static const char set_sc_on_param[] = { 0x01 };
static const char set_sc_only_on_param[] = { 0x02 };
static const char set_sc_invalid_param[] = { 0x03 };
@@ -3748,6 +3849,650 @@ static const struct generic_data remove_device_success_5 = {
.expect_hci_len = sizeof(set_le_scan_off),
};
+static const struct generic_data read_adv_features_invalid_param_test = {
+ .send_opcode = MGMT_OP_READ_ADV_FEATURES,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_adv_features_invalid_index_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_ADV_FEATURES,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const uint8_t add_advertising_param_1[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_2[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x0a,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+ 0x03, 0x19, 0x40, 0x03,
+ 0x05, 0x03, 0x0d, 0x18, 0x0f, 0x18,
+};
+
+static const uint8_t add_advertising_param_3[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_4[] = {
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_5[] = {
+ 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_6[] = {
+ 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_7[] = {
+ 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_8[] = {
+ 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t advertising_instance_param[] = {
+ 0x01,
+};
+
+static const uint8_t set_adv_data_1[] = {
+ 0x09, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, 0x01, 0x02, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_2[] = {
+ 0x0c, 0x02, 0x01, 0x04, 0x03, 0x03, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_3[] = {
+ 0x06, 0x05, 0x08, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_4[] = {
+ 0x03, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_5[] = {
+ 0x0c, 0x02, 0x01, 0x02, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_6[] = {
+ 0x0c, 0x02, 0x01, 0x01, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_7[] = {
+ 0x0c, 0x02, 0x01, 0x02, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_8[] = {
+ 0x0c, 0x02, 0x0a, 0x00, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_scan_rsp_1[] = {
+ 0x0a, 0x03, 0x19, 0x40, 0x03, 0x05, 0x03, 0x0d, 0x18, 0x0f,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t add_advertising_invalid_param_1[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+};
+
+static const uint8_t add_advertising_invalid_param_2[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x04, 0x03, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_3[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x02, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_4[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x05, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_5[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18,
+};
+
+static const uint8_t add_advertising_invalid_param_6[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+};
+
+static const uint8_t add_advertising_invalid_param_7[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x04, 0x03, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_8[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x02, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_9[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x05, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_10[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18,
+};
+
+static const struct generic_data add_advertising_fail_1 = {
+ .setup_settings = settings_powered,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_1,
+ .send_len = sizeof(add_advertising_param_1),
+ .expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data add_advertising_fail_2 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_1,
+ .send_len = sizeof(add_advertising_invalid_param_1),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_3 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_2,
+ .send_len = sizeof(add_advertising_invalid_param_2),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_4 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_3,
+ .send_len = sizeof(add_advertising_invalid_param_3),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_5 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_4,
+ .send_len = sizeof(add_advertising_invalid_param_4),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_6 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_5,
+ .send_len = sizeof(add_advertising_invalid_param_5),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_7 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_6,
+ .send_len = sizeof(add_advertising_invalid_param_6),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_8 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_7,
+ .send_len = sizeof(add_advertising_invalid_param_7),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_9 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_8,
+ .send_len = sizeof(add_advertising_invalid_param_8),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_10 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_9,
+ .send_len = sizeof(add_advertising_invalid_param_9),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_11 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_10,
+ .send_len = sizeof(add_advertising_invalid_param_10),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_12 = {
+ .setup_settings = settings_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_3,
+ .send_len = sizeof(add_advertising_param_3),
+ .expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data add_advertising_success_1 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_1,
+ .send_len = sizeof(add_advertising_param_1),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_alt_ev = MGMT_EV_ADVERTISING_ADDED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_1,
+ .expect_hci_len = sizeof(set_adv_data_1),
+};
+
+static const char set_powered_adv_instance_settings_param[] = {
+ 0x81, 0x02, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_success_2 = {
+ .send_opcode = MGMT_OP_SET_POWERED,
+ .send_param = set_powered_on_param,
+ .send_len = sizeof(set_powered_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_powered_adv_instance_settings_param,
+ .expect_len = sizeof(set_powered_adv_instance_settings_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_3,
+ .expect_hci_len = sizeof(set_adv_data_3),
+};
+
+static const struct generic_data add_advertising_success_3 = {
+ .send_opcode = MGMT_OP_SET_POWERED,
+ .send_param = set_powered_on_param,
+ .send_len = sizeof(set_powered_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_powered_adv_instance_settings_param,
+ .expect_len = sizeof(set_powered_adv_instance_settings_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+ .expect_hci_param = set_adv_on_set_adv_enable_param,
+ .expect_hci_len = sizeof(set_adv_on_set_adv_enable_param),
+};
+
+static const struct generic_data add_advertising_success_4 = {
+ .send_opcode = MGMT_OP_SET_ADVERTISING,
+ .send_param = set_adv_on_param,
+ .send_len = sizeof(set_adv_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_adv_settings_param_2,
+ .expect_len = sizeof(set_adv_settings_param_2),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_4,
+ .expect_hci_len = sizeof(set_adv_data_4),
+};
+
+static const char set_adv_off_param[] = { 0x00 };
+
+static const struct generic_data add_advertising_success_5 = {
+ .send_opcode = MGMT_OP_SET_ADVERTISING,
+ .send_param = set_adv_off_param,
+ .send_len = sizeof(set_adv_off_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_powered_adv_instance_settings_param,
+ .expect_len = sizeof(set_powered_adv_instance_settings_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_3,
+ .expect_hci_len = sizeof(set_adv_data_3),
+};
+
+static const struct generic_data add_advertising_success_6 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_2,
+ .send_len = sizeof(add_advertising_param_2),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_alt_ev = MGMT_EV_ADVERTISING_ADDED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_1,
+ .expect_hci_len = sizeof(set_adv_data_1),
+};
+
+static const struct generic_data add_advertising_success_7 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_2,
+ .send_len = sizeof(add_advertising_param_2),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_alt_ev = MGMT_EV_ADVERTISING_ADDED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+ .expect_hci_param = set_scan_rsp_1,
+ .expect_hci_len = sizeof(set_scan_rsp_1),
+};
+
+static const struct generic_data add_advertising_success_8 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_4,
+ .send_len = sizeof(add_advertising_param_4),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_on_adv_param,
+ .expect_hci_len = sizeof(set_connectable_on_adv_param),
+};
+
+static const struct generic_data add_advertising_success_9 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_5,
+ .send_len = sizeof(add_advertising_param_5),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_5,
+ .expect_hci_len = sizeof(set_adv_data_5),
+};
+
+static const struct generic_data add_advertising_success_10 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_6,
+ .send_len = sizeof(add_advertising_param_6),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_6,
+ .expect_hci_len = sizeof(set_adv_data_6),
+};
+
+static const struct generic_data add_advertising_success_11 = {
+ .setup_settings = settings_powered_le_discoverable,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_7,
+ .send_len = sizeof(add_advertising_param_7),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_7,
+ .expect_hci_len = sizeof(set_adv_data_7),
+};
+
+static const struct generic_data add_advertising_success_12 = {
+ .setup_settings = settings_powered_le_discoverable,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_8,
+ .send_len = sizeof(add_advertising_param_8),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_8,
+ .expect_hci_len = sizeof(set_adv_data_8),
+};
+
+static uint16_t settings_powered_le_connectable[] = {
+ MGMT_OP_SET_POWERED,
+ MGMT_OP_SET_LE,
+ MGMT_OP_SET_CONNECTABLE, 0 };
+
+static uint8_t set_connectable_off_scan_adv_param[] = {
+ 0x00, 0x08, /* min_interval */
+ 0x00, 0x08, /* max_interval */
+ 0x02, /* type */
+ 0x01, /* own_addr_type */
+ 0x00, /* direct_addr_type */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* direct_addr */
+ 0x07, /* channel_map */
+ 0x00, /* filter_policy */
+};
+
+static const struct generic_data add_advertising_success_13 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_2,
+ .send_len = sizeof(add_advertising_param_2),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_off_scan_adv_param,
+ .expect_hci_len = sizeof(set_connectable_off_scan_adv_param),
+};
+
+static const struct generic_data add_advertising_success_14 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_1,
+ .send_len = sizeof(add_advertising_param_1),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_off_adv_param,
+ .expect_hci_len = sizeof(set_connectable_off_adv_param),
+};
+
+static const struct generic_data add_advertising_success_15 = {
+ .setup_settings = settings_powered_le_connectable,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_1,
+ .send_len = sizeof(add_advertising_param_1),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_on_adv_param,
+ .expect_hci_len = sizeof(set_connectable_on_adv_param),
+};
+
+static const char set_connectable_settings_param_3[] = {
+ 0x83, 0x02, 0x00, 0x00 };
+
+static const struct generic_data add_advertising_success_16 = {
+ .send_opcode = MGMT_OP_SET_CONNECTABLE,
+ .send_param = set_connectable_on_param,
+ .send_len = sizeof(set_connectable_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_connectable_settings_param_3,
+ .expect_len = sizeof(set_connectable_settings_param_3),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_on_adv_param,
+ .expect_hci_len = sizeof(set_connectable_on_adv_param),
+};
+
+static const struct generic_data add_advertising_success_17 = {
+ .send_opcode = MGMT_OP_SET_CONNECTABLE,
+ .send_param = set_connectable_off_param,
+ .send_len = sizeof(set_connectable_off_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_le_settings_param_2,
+ .expect_len = sizeof(set_le_settings_param_2),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_off_adv_param,
+ .expect_hci_len = sizeof(set_connectable_off_adv_param),
+};
+
+static const char set_powered_off_le_settings_param[] = {
+ 0x80, 0x02, 0x00, 0x00
+};
+
+static const struct generic_data add_advertising_timeout_power_off = {
+ .send_opcode = MGMT_OP_SET_POWERED,
+ .send_param = set_powered_off_param,
+ .send_len = sizeof(set_powered_off_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_powered_off_le_settings_param,
+ .expect_len = sizeof(set_powered_off_le_settings_param),
+ .expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+};
+
+static const uint8_t remove_advertising_param_1[] = {
+ 0x01,
+};
+
+static const uint8_t remove_advertising_param_2[] = {
+ 0x00,
+};
+
+static const struct generic_data remove_advertising_fail_1 = {
+ .send_opcode = MGMT_OP_REMOVE_ADVERTISING,
+ .send_param = remove_advertising_param_1,
+ .send_len = sizeof(remove_advertising_param_1),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data remove_advertising_success_1 = {
+ .send_opcode = MGMT_OP_REMOVE_ADVERTISING,
+ .send_param = remove_advertising_param_1,
+ .send_len = sizeof(remove_advertising_param_1),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = remove_advertising_param_1,
+ .expect_len = sizeof(remove_advertising_param_1),
+ .expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+ .expect_hci_param = set_adv_off_param,
+ .expect_hci_len = sizeof(set_adv_off_param),
+};
+
+static const struct generic_data remove_advertising_success_2 = {
+ .send_opcode = MGMT_OP_REMOVE_ADVERTISING,
+ .send_param = remove_advertising_param_2,
+ .send_len = sizeof(remove_advertising_param_2),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = remove_advertising_param_1,
+ .expect_len = sizeof(remove_advertising_param_1),
+ .expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+ .expect_hci_param = set_adv_off_param,
+ .expect_hci_len = sizeof(set_adv_off_param),
+};
+
+static const struct generic_data read_local_oob_not_powered_test = {
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_NOT_POWERED,
+};
+
+static const struct generic_data read_local_oob_invalid_param_test = {
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_local_oob_invalid_index_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_local_oob_legacy_pairing_test = {
+ .setup_settings = settings_powered,
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
+static const struct generic_data read_local_oob_success_ssp_test = {
+ .setup_settings = settings_powered_ssp,
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_ignore_param = true,
+ .expect_hci_command = BT_HCI_CMD_READ_LOCAL_OOB_DATA,
+};
+
+static const struct generic_data read_local_oob_success_sc_test = {
+ .setup_settings = settings_powered_sc,
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_ignore_param = true,
+ .expect_hci_command = BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA,
+};
+
static void client_cmd_complete(uint16_t opcode, uint8_t status,
const void *param, uint8_t len,
void *user_data)
@@ -4098,6 +4843,199 @@ static void setup_add_device(const void *test_data)
setup_powered_callback, NULL, NULL);
}
+static void setup_add_advertising_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ tester_setup_failed();
+ return;
+ }
+
+ tester_print("Add Advertising setup complete");
+
+ setup_bthost();
+}
+
+static void setup_add_advertising_not_powered(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
+static void setup_add_advertising(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
+static void setup_add_advertising_connectable(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
+static void setup_add_advertising_timeout(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->timeout = 5;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
+static void setup_set_and_add_advertising(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
static void setup_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
@@ -4402,18 +5340,20 @@ static void command_generic_callback(uint8_t status, uint16_t length,
return;
}
- if (test->expect_func)
- expect_param = test->expect_func(&expect_len);
+ if (!test->expect_ignore_param) {
+ if (test->expect_func)
+ expect_param = test->expect_func(&expect_len);
- if (length != expect_len) {
- tester_test_failed();
- return;
- }
+ if (length != expect_len) {
+ tester_test_failed();
+ return;
+ }
- if (expect_param && expect_len > 0 &&
- memcmp(param, expect_param, length)) {
- tester_test_failed();
- return;
+ if (expect_param && expect_len > 0 &&
+ memcmp(param, expect_param, length)) {
+ tester_test_failed();
+ return;
+ }
}
test_condition_complete(data);
@@ -4679,6 +5619,24 @@ int main(int argc, char *argv[])
test_bredrle("Read info - Invalid index",
&read_info_invalid_index_test,
NULL, test_command_generic);
+ test_bredrle("Read unconfigured index list - Invalid parameters",
+ &read_unconf_index_list_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read unconfigured index list - Invalid index",
+ &read_unconf_index_list_invalid_index_test,
+ NULL, test_command_generic);
+ test_bredrle("Read configuration info - Invalid parameters",
+ &read_config_info_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read configuration info - Invalid index",
+ &read_config_info_invalid_index_test,
+ NULL, test_command_generic);
+ test_bredrle("Read extended index list - Invalid parameters",
+ &read_ext_index_list_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read extended index list - Invalid index",
+ &read_ext_index_list_invalid_index_test,
+ NULL, test_command_generic);
test_bredrle("Set powered on - Success",
&set_powered_on_success_test,
@@ -4770,6 +5728,15 @@ int main(int argc, char *argv[])
test_bredrle("Set fast connectable on - Success 1",
&set_fast_conn_on_success_test_1,
NULL, test_command_generic);
+ test_bredrle("Set fast connectable on - Success 2",
+ &set_fast_conn_on_success_test_2,
+ NULL, test_command_generic);
+ test_bredrle("Set fast connectable on - Success 3",
+ &set_fast_conn_on_success_test_3,
+ NULL, test_command_generic);
+ test_bredrle("Set fast connectable on - Invalid Params 1",
+ &set_fast_conn_nval_param_test_1,
+ NULL, test_command_generic);
test_le("Set fast connectable on - Not Supported 1",
&set_fast_conn_on_not_supported_test_1,
NULL, test_command_generic);
@@ -5411,5 +6378,141 @@ int main(int argc, char *argv[])
&remove_device_success_5,
setup_add_device, test_command_generic);
+ test_bredrle("Read Advertising Features - Invalid parameters",
+ &read_adv_features_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Advertising Features - Invalid index",
+ &read_adv_features_invalid_index_test,
+ NULL, test_command_generic);
+
+ test_bredrle("Add Advertising - Failure: LE off",
+ &add_advertising_fail_1,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 1 (AD too long)",
+ &add_advertising_fail_2,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 2 (Malformed len)",
+ &add_advertising_fail_3,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 3 (Malformed len)",
+ &add_advertising_fail_4,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 4 (Malformed len)",
+ &add_advertising_fail_5,
+ NULL, test_command_generic);
+ test_le("Add Advertising - Invalid Params 5 (AD too long)",
+ &add_advertising_fail_6,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 6 (ScRsp too long)",
+ &add_advertising_fail_7,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 7 (Malformed len)",
+ &add_advertising_fail_8,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 8 (Malformed len)",
+ &add_advertising_fail_9,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 9 (Malformed len)",
+ &add_advertising_fail_10,
+ NULL, test_command_generic);
+ test_le("Add Advertising - Invalid Params 10 (ScRsp too long)",
+ &add_advertising_fail_11,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Timeout Not Powered",
+ &add_advertising_fail_12,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Timeout Power off",
+ &add_advertising_timeout_power_off,
+ setup_add_advertising_timeout,
+ test_command_generic);
+ test_bredrle("Add Advertising - Success 1",
+ &add_advertising_success_1,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 2",
+ &add_advertising_success_2,
+ setup_add_advertising_not_powered,
+ test_command_generic);
+ test_bredrle("Add Advertising - Success 3",
+ &add_advertising_success_3,
+ setup_add_advertising_not_powered,
+ test_command_generic);
+ test_bredrle("Add Advertising - Set Advertising on override 1",
+ &add_advertising_success_4,
+ setup_add_advertising,
+ test_command_generic);
+ test_bredrle("Add Advertising - Set Advertising off override 2",
+ &add_advertising_success_5,
+ setup_set_and_add_advertising,
+ test_command_generic);
+ test_bredrle("Add Advertising - Success 4",
+ &add_advertising_success_6,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 5",
+ &add_advertising_success_7,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 6 - Flag 0",
+ &add_advertising_success_8,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 7 - Flag 1",
+ &add_advertising_success_9,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 8 - Flag 2",
+ &add_advertising_success_10,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 8 - Flag 3",
+ &add_advertising_success_11,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 9 - Flag 4",
+ &add_advertising_success_12,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 10 - ADV_SCAN_IND",
+ &add_advertising_success_13,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 11 - ADV_NONCONN_IND",
+ &add_advertising_success_14,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 12 - ADV_IND",
+ &add_advertising_success_15,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 13 - connectable -> on",
+ &add_advertising_success_16,
+ setup_add_advertising,
+ test_command_generic);
+ test_bredrle("Add Advertising - Success 14 - connectable -> off",
+ &add_advertising_success_17,
+ setup_add_advertising_connectable,
+ test_command_generic);
+
+ test_bredrle("Remove Advertising - Invalid Params 1",
+ &remove_advertising_fail_1,
+ NULL, test_command_generic);
+ test_bredrle("Remove Advertising - Success 1",
+ &remove_advertising_success_1,
+ setup_add_advertising,
+ test_command_generic);
+ test_bredrle("Remove Advertising - Success 2",
+ &remove_advertising_success_2,
+ setup_add_advertising,
+ test_command_generic);
+
+ test_bredrle("Read Local OOB Data - Not powered",
+ &read_local_oob_not_powered_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Local OOB Data - Invalid parameters",
+ &read_local_oob_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Local OOB Data - Invalid index",
+ &read_local_oob_invalid_index_test,
+ NULL, test_command_generic);
+ test_bredr20("Read Local OOB Data - Legacy pairing",
+ &read_local_oob_legacy_pairing_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Local OOB Data - Success SSP",
+ &read_local_oob_success_ssp_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Local OOB Data - Success SC",
+ &read_local_oob_success_sc_test,
+ NULL, test_command_generic);
+
return tester_run();
}
diff --git a/tools/mpris-proxy.c b/tools/mpris-proxy.c
index bf8148fe..693055ed 100644
--- a/tools/mpris-proxy.c
+++ b/tools/mpris-proxy.c
@@ -406,7 +406,7 @@ static DBusHandlerResult player_message(DBusConnection *conn,
done:
dbus_message_unref(copy);
- return DBUS_HANDLER_RESULT_HANDLED;
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static struct player *find_player_by_bus_name(const char *name)
diff --git a/tools/obexctl.c b/tools/obexctl.c
index 86c81d57..4faff6b6 100644
--- a/tools/obexctl.c
+++ b/tools/obexctl.c
@@ -758,7 +758,7 @@ static void send_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to send/pull: %s\n", error.name);
+ rl_printf("Failed to send: %s\n", error.name);
dbus_error_free(&error);
return;
}
@@ -792,23 +792,6 @@ static void opp_send(GDBusProxy *proxy, int argc, char *argv[])
g_dbus_proxy_get_path(proxy));
}
-static void opp_pull(GDBusProxy *proxy, int argc, char *argv[])
-{
- if (argc < 2) {
- rl_printf("Missing file argument\n");
- return;
- }
-
- if (g_dbus_proxy_method_call(proxy, "PullBusinessCard", send_setup,
- send_reply, g_strdup(argv[1]), g_free) == FALSE) {
- rl_printf("Failed to pull\n");
- return;
- }
-
- rl_printf("Attempting to pull %s from %s\n", argv[1],
- g_dbus_proxy_get_path(proxy));
-}
-
static void push_reply(DBusMessage *message, void *user_data)
{
DBusMessageIter iter;
@@ -886,22 +869,6 @@ static void cmd_send(int argc, char *argv[])
rl_printf("Command not supported\n");
}
-static void cmd_pull(int argc, char *argv[])
-{
- GDBusProxy *proxy;
-
- if (!check_default_session())
- return;
-
- proxy = find_opp(g_dbus_proxy_get_path(default_session));
- if (proxy) {
- opp_pull(proxy, argc, argv);
- return;
- }
-
- rl_printf("Command not supported\n");
-}
-
static void change_folder_reply(DBusMessage *message, void *user_data)
{
DBusError error;
@@ -2012,8 +1979,6 @@ static const struct {
{ "suspend", "<transfer>", cmd_suspend, "Suspend transfer" },
{ "resume", "<transfer>", cmd_resume, "Resume transfer" },
{ "send", "<file>", cmd_send, "Send file" },
- { "pull", "<file>", cmd_pull,
- "Pull Vobject & stores in file" },
{ "cd", "<path>", cmd_cd, "Change current folder" },
{ "ls", "<options>", cmd_ls, "List current folder" },
{ "cp", "<source file> <destination file>", cmd_cp,
diff --git a/tools/oobtest.c b/tools/oobtest.c
index 9cc6c162..e77320ba 100644
--- a/tools/oobtest.c
+++ b/tools/oobtest.c
@@ -34,6 +34,10 @@
#include "src/shared/mainloop.h"
#include "src/shared/util.h"
#include "src/shared/mgmt.h"
+#include "src/shared/crypto.h"
+
+#define REMOTE_IRK "\x69\x30\xde\xc3\x8f\x84\x74\x14" \
+ "\xe1\x23\x99\xc1\xca\x9a\xc3\x31"
static bool use_bredr = false;
static bool use_le = false;
@@ -41,16 +45,21 @@ static bool use_sc = false;
static bool use_sconly = false;
static bool use_legacy = false;
static bool use_random = false;
+static bool use_privacy = false;
static bool use_debug = false;
static bool use_cross = false;
+static bool provide_tk = false;
static bool provide_p192 = false;
static bool provide_p256 = false;
+static bool provide_initiator = false;
+static bool provide_acceptor = false;
static struct mgmt *mgmt;
static uint16_t index1 = MGMT_INDEX_NONE;
static uint16_t index2 = MGMT_INDEX_NONE;
static bdaddr_t bdaddr1;
static bdaddr_t bdaddr2;
+static uint8_t oob_tk[16];
static void pin_code_request_event(uint16_t index, uint16_t len,
const void *param, void *user_data)
@@ -254,9 +263,12 @@ static void add_remote_oob_data(uint16_t index, const bdaddr_t *bdaddr,
cp.addr.type = BDADDR_LE_RANDOM;
else
cp.addr.type = BDADDR_LE_PUBLIC;
- if (hash192 && rand192) {
+ if (hash192) {
memcpy(cp.hash192, hash192, 16);
- memcpy(cp.rand192, rand192, 16);
+ if (rand192)
+ memcpy(cp.rand192, rand192, 16);
+ else
+ memset(cp.rand192, 0, 16);
} else {
memset(cp.hash192, 0, 16);
memset(cp.rand192, 0, 16);
@@ -277,7 +289,7 @@ static void add_remote_oob_data(uint16_t index, const bdaddr_t *bdaddr,
static void read_oob_data_complete(uint8_t status, uint16_t len,
const void *param, void *user_data)
{
- const struct mgmt_rp_read_local_oob_ext_data *rp = param;
+ const struct mgmt_rp_read_local_oob_data *rp = param;
uint16_t index = PTR_TO_UINT(user_data);
const uint8_t *hash192, *rand192, *hash256, *rand256;
int i;
@@ -291,12 +303,22 @@ static void read_oob_data_complete(uint8_t status, uint16_t len,
printf("[Index %u]\n", index);
+ hash192 = NULL;
+ rand192 = NULL;
+ hash256 = NULL;
+ rand256 = NULL;
+
+ if (index == index1 && !provide_initiator) {
+ printf(" Skipping initiator OOB data\n");
+ goto done;
+ } else if (index == index2 && !provide_acceptor) {
+ printf(" Skipping acceptor OOB data\n");
+ goto done;
+ }
+
if (provide_p192) {
hash192 = rp->hash192;
- rand192 = rp->randomizer192;
- } else {
- hash192 = NULL;
- rand192 = NULL;
+ rand192 = rp->rand192;
}
printf(" Hash C from P-192: ");
@@ -306,21 +328,15 @@ static void read_oob_data_complete(uint8_t status, uint16_t len,
printf(" Randomizer R with P-192: ");
for (i = 0; i < 16; i++)
- printf("%02x", rp->randomizer192[i]);
+ printf("%02x", rp->rand192[i]);
printf("\n");
- if (len < sizeof(*rp)) {
- hash256 = NULL;
- rand256 = NULL;
+ if (len < sizeof(*rp))
goto done;
- }
if (provide_p256) {
hash256 = rp->hash256;
- rand256 = rp->randomizer256;
- } else {
- hash256 = NULL;
- rand256 = NULL;
+ rand256 = rp->rand256;
}
printf(" Hash C from P-256: ");
@@ -330,7 +346,7 @@ static void read_oob_data_complete(uint8_t status, uint16_t len,
printf(" Randomizer R with P-256: ");
for (i = 0; i < 16; i++)
- printf("%02x", rp->randomizer256[i]);
+ printf("%02x", rp->rand256[i]);
printf("\n");
done:
@@ -342,6 +358,105 @@ done:
hash192, rand192, hash256, rand256);
}
+static void read_oob_ext_data_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_read_local_oob_ext_data *rp = param;
+ uint16_t index = PTR_TO_UINT(user_data);
+ uint16_t eir_len, parsed;
+ const uint8_t *eir, *tk, *hash256, *rand256;
+ int i;
+
+ if (status) {
+ fprintf(stderr, "Reading OOB data for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+
+ printf("[Index %u]\n", index);
+
+ eir_len = le16_to_cpu(rp->eir_len);
+ printf(" OOB data len: %u\n", eir_len);
+
+ if (provide_tk)
+ tk = oob_tk;
+ else
+ tk = NULL;
+
+ hash256 = NULL;
+ rand256 = NULL;
+
+ if (index == index1 && !provide_initiator) {
+ printf(" Skipping initiator OOB data\n");
+ goto done;
+ } else if (index == index2 && !provide_acceptor) {
+ printf(" Skipping acceptor OOB data\n");
+ goto done;
+ }
+
+ if (eir_len < 2)
+ goto done;
+
+ eir = rp->eir;
+ parsed = 0;
+
+ while (parsed < eir_len - 1) {
+ uint8_t field_len = eir[0];
+
+ if (field_len == 0)
+ break;
+
+ parsed += field_len + 1;
+
+ if (parsed > eir_len)
+ break;
+
+ /* LE Bluetooth Device Address */
+ if (eir[1] == 0x1b) {
+ char str[18];
+
+ ba2str((bdaddr_t *) (eir + 2), str);
+ printf(" Device address: %s (%s)\n", str,
+ eir[8] ? "random" : "public");
+ }
+
+ /* LE Role */
+ if (eir[1] == 0x1c)
+ printf(" Role: 0x%02x\n", eir[2]);
+
+ /* LE Secure Connections Confirmation Value */
+ if (eir[1] == 0x22) {
+ hash256 = eir + 2;
+
+ printf(" Hash C from P-256: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", hash256[i]);
+ printf("\n");
+ }
+
+ /* LE Secure Connections Random Value */
+ if (eir[1] == 0x23) {
+ rand256 = eir + 2;
+
+ printf(" Randomizer R with P-256: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", rand256[i]);
+ printf("\n");
+ }
+
+ eir += field_len + 1;
+ }
+
+done:
+ if (index == index1)
+ add_remote_oob_data(index2, &bdaddr1,
+ tk, NULL, hash256, rand256);
+ else if (index == index2)
+ add_remote_oob_data(index1, &bdaddr2,
+ tk, NULL, hash256, rand256);
+}
+
static void set_powered_complete(uint8_t status, uint16_t len,
const void *param, void *user_data)
{
@@ -380,6 +495,23 @@ static void set_powered_complete(uint8_t status, uint16_t len,
mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_DATA, index, 0, NULL,
read_oob_data_complete,
UINT_TO_PTR(index), NULL);
+ } else if (use_le && provide_p256) {
+ uint8_t type = (1 << BDADDR_LE_PUBLIC) |
+ (1 << BDADDR_LE_RANDOM);
+
+ mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, index,
+ sizeof(type), &type,
+ read_oob_ext_data_complete,
+ UINT_TO_PTR(index), NULL);
+ } else if (use_le && provide_tk) {
+ const uint8_t *tk = oob_tk;
+
+ if (index == index1)
+ add_remote_oob_data(index2, &bdaddr1,
+ tk, NULL, NULL, NULL);
+ else if (index == index2)
+ add_remote_oob_data(index1, &bdaddr2,
+ tk, NULL, NULL, NULL);
} else {
if (index == index1)
add_remote_oob_data(index2, &bdaddr1,
@@ -413,6 +545,17 @@ static void clear_long_term_keys(uint16_t index)
sizeof(cp), &cp, NULL, NULL, NULL);
}
+static void clear_identity_resolving_keys(uint16_t index)
+{
+ struct mgmt_cp_load_irks cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.irk_count = cpu_to_le16(0);
+
+ mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index,
+ sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
static void clear_remote_oob_data(uint16_t index)
{
struct mgmt_cp_remove_remote_oob_data cp;
@@ -425,6 +568,123 @@ static void clear_remote_oob_data(uint16_t index)
sizeof(cp), &cp, NULL, NULL, NULL);
}
+static void set_powered_down_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Power down for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_bredr_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Setting BR/EDR for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_le_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Setting LE for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_ssp_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Simple Pairing for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_static_address_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Static address for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_secure_conn_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Secure connections for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_privacy_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Setting privacy for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_debug_keys_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Setting debug keys for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_bondable_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Setting bondable for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
static void read_info(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
@@ -483,6 +743,12 @@ static void read_info(uint8_t status, uint16_t len, const void *param,
return;
}
+ if (use_privacy && !(supported_settings & MGMT_SETTING_PRIVACY)) {
+ fprintf(stderr, "Privacy support missing\n");
+ mainloop_quit();
+ return;
+ }
+
if (use_debug && !(supported_settings & MGMT_SETTING_DEBUG_KEYS)) {
fprintf(stderr, "Debug keys support missing\n");
mainloop_quit();
@@ -496,6 +762,16 @@ static void read_info(uint8_t status, uint16_t len, const void *param,
return;
}
+ if (provide_tk) {
+ const uint8_t *tk = oob_tk;
+ int i;
+
+ printf(" TK Value: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", tk[i]);
+ printf("\n");
+ }
+
mgmt_register(mgmt, MGMT_EV_PIN_CODE_REQUEST, index,
pin_code_request_event,
UINT_TO_PTR(index), NULL);
@@ -510,32 +786,46 @@ static void read_info(uint8_t status, uint16_t len, const void *param,
val = 0x00;
mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val,
- NULL, NULL, NULL);
+ set_powered_down_complete,
+ UINT_TO_PTR(index), NULL);
clear_link_keys(index);
clear_long_term_keys(index);
+ clear_identity_resolving_keys(index);
clear_remote_oob_data(index);
if (use_bredr) {
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val,
- NULL, NULL, NULL);
+ set_bredr_complete,
+ UINT_TO_PTR(index), NULL);
val = use_cross ? 0x01 : 0x00;
mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val,
- NULL, NULL, NULL);
+ set_le_complete,
+ UINT_TO_PTR(index), NULL);
val = use_legacy ? 0x00 : 0x01;
mgmt_send(mgmt, MGMT_OP_SET_SSP, index, 1, &val,
- NULL, NULL, NULL);
+ set_ssp_complete,
+ UINT_TO_PTR(index), NULL);
} else if (use_le) {
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val,
- NULL, NULL, NULL);
+ set_le_complete,
+ UINT_TO_PTR(index), NULL);
val = use_cross ? 0x01 : 0x00;
mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val,
- NULL, NULL, NULL);
+ set_bredr_complete,
+ UINT_TO_PTR(index), NULL);
+
+ if (use_cross) {
+ val = use_legacy ? 0x00 : 0x01;
+ mgmt_send(mgmt, MGMT_OP_SET_SSP, index, 1, &val,
+ set_ssp_complete,
+ UINT_TO_PTR(index), NULL);
+ }
} else {
fprintf(stderr, "Invalid transport for pairing\n");
mainloop_quit();
@@ -548,8 +838,9 @@ static void read_info(uint8_t status, uint16_t len, const void *param,
str2ba("c0:00:aa:bb:00:00", &bdaddr);
bdaddr.b[0] = index;
- mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index,
- 6, &bdaddr, NULL, NULL, NULL);
+ mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, 6, &bdaddr,
+ set_static_address_complete,
+ UINT_TO_PTR(index), NULL);
if (index == index1)
bacpy(&bdaddr1, &bdaddr);
@@ -560,31 +851,62 @@ static void read_info(uint8_t status, uint16_t len, const void *param,
bacpy(&bdaddr, BDADDR_ANY);
- mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index,
- 6, &bdaddr, NULL, NULL, NULL);
+ mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, 6, &bdaddr,
+ set_static_address_complete,
+ UINT_TO_PTR(index), NULL);
}
if (use_sc) {
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
- NULL, NULL, NULL);
+ set_secure_conn_complete,
+ UINT_TO_PTR(index), NULL);
} else if (use_sconly) {
val = 0x02;
mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
- NULL, NULL, NULL);
+ set_secure_conn_complete,
+ UINT_TO_PTR(index), NULL);
} else {
val = 0x00;
mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
- NULL, NULL, NULL);
+ set_secure_conn_complete,
+ UINT_TO_PTR(index), NULL);
+ }
+
+ if (use_privacy) {
+ struct mgmt_cp_set_privacy cp;
+
+ if (index == index2) {
+ cp.privacy = 0x01;
+ memcpy(cp.irk, REMOTE_IRK, sizeof(cp.irk));
+ } else {
+ cp.privacy = 0x00;
+ memset(cp.irk, 0, sizeof(cp.irk));
+ }
+
+ mgmt_send(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp,
+ set_privacy_complete,
+ UINT_TO_PTR(index), NULL);
+ } else {
+ struct mgmt_cp_set_privacy cp;
+
+ cp.privacy = 0x00;
+ memset(cp.irk, 0, sizeof(cp.irk));
+
+ mgmt_send(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp,
+ set_privacy_complete,
+ UINT_TO_PTR(index), NULL);
}
val = 0x00;
mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val,
- NULL, NULL, NULL);
+ set_debug_keys_complete,
+ UINT_TO_PTR(index), NULL);
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_BONDABLE, index, 1, &val,
- NULL, NULL, NULL);
+ set_bondable_complete,
+ UINT_TO_PTR(index), NULL);
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val,
@@ -631,6 +953,16 @@ static void read_index_list(uint8_t status, uint16_t len, const void *param,
printf("Selecting index %u for initiator\n", index1);
printf("Selecting index %u for acceptor\n", index2);
+ if (provide_tk) {
+ struct bt_crypto *crypto;
+
+ printf("Generating Security Manager TK Value\n");
+
+ crypto = bt_crypto_new();
+ bt_crypto_random_bytes(crypto, oob_tk, 16);
+ bt_crypto_unref(crypto);
+ }
+
mgmt_send(mgmt, MGMT_OP_READ_INFO, index1, 0, NULL,
read_info, UINT_TO_PTR(index1), NULL);
mgmt_send(mgmt, MGMT_OP_READ_INFO, index2, 0, NULL,
@@ -659,28 +991,36 @@ static void usage(void)
"\t-O, --sconly Use Secure Connections Only\n"
"\t-P, --legacy Use Legacy Pairing\n"
"\t-R, --random Use Static random address\n"
+ "\t-Y, --privacy Use LE privacy feature\n"
"\t-D, --debug Use Pairing debug keys\n"
"\t-C, --cross Use cross-transport pairing\n"
+ "\t-0, --tk Provide LE legacy OOB data\n"
"\t-1, --p192 Provide P-192 OOB data\n"
"\t-2, --p256 Provide P-256 OOB data\n"
+ "\t-I, --initiator Initiator provides OOB data\n"
+ "\t-A, --acceptor Acceptor provides OOB data\n"
"\t-h, --help Show help options\n");
}
static const struct option main_options[] = {
- { "bredr", no_argument, NULL, 'B' },
- { "le", no_argument, NULL, 'L' },
- { "sc", no_argument, NULL, 'S' },
- { "sconly", no_argument, NULL, 'O' },
- { "legacy", no_argument, NULL, 'P' },
- { "random", no_argument, NULL, 'R' },
- { "static", no_argument, NULL, 'R' },
- { "debug", no_argument, NULL, 'D' },
- { "cross", no_argument, NULL, 'C' },
- { "dual", no_argument, NULL, 'C' },
- { "p192", no_argument, NULL, '1' },
- { "p256", no_argument, NULL, '2' },
- { "version", no_argument, NULL, 'v' },
- { "help", no_argument, NULL, 'h' },
+ { "bredr", no_argument, NULL, 'B' },
+ { "le", no_argument, NULL, 'L' },
+ { "sc", no_argument, NULL, 'S' },
+ { "sconly", no_argument, NULL, 'O' },
+ { "legacy", no_argument, NULL, 'P' },
+ { "random", no_argument, NULL, 'R' },
+ { "static", no_argument, NULL, 'R' },
+ { "privacy", no_argument, NULL, 'Y' },
+ { "debug", no_argument, NULL, 'D' },
+ { "cross", no_argument, NULL, 'C' },
+ { "dual", no_argument, NULL, 'C' },
+ { "tk", no_argument, NULL, '0' },
+ { "p192", no_argument, NULL, '1' },
+ { "p256", no_argument, NULL, '2' },
+ { "initiator", no_argument, NULL, 'I' },
+ { "acceptor", no_argument, NULL, 'A' },
+ { "version", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
{ }
};
@@ -692,7 +1032,7 @@ int main(int argc ,char *argv[])
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "BLSOPRDC12vh",
+ opt = getopt_long(argc, argv, "BLSOPRYDC012IAvh",
main_options, NULL);
if (opt < 0)
break;
@@ -716,18 +1056,30 @@ int main(int argc ,char *argv[])
case 'R':
use_random = true;
break;
+ case 'Y':
+ use_privacy = true;
+ break;
case 'D':
use_debug = true;
break;
case 'C':
use_cross = true;
break;
+ case '0':
+ provide_tk = true;
+ break;
case '1':
provide_p192 = true;
break;
case '2':
provide_p256 = true;
break;
+ case 'I':
+ provide_initiator = true;
+ break;
+ case 'A':
+ provide_acceptor = true;
+ break;
case 'v':
printf("%s\n", VERSION);
return EXIT_SUCCESS;
@@ -754,6 +1106,11 @@ int main(int argc ,char *argv[])
return EXIT_FAILURE;
}
+ if (use_privacy && !use_le && !use_cross ) {
+ fprintf(stderr, "Specify --privacy with --le or --cross\n");
+ return EXIT_FAILURE;
+ }
+
if (use_random && !use_le) {
fprintf(stderr, "Specify --random with --le\n");
return EXIT_FAILURE;