@@ -87,7 +87,7 @@ noinst_PROGRAMS += emulator/btvirt emulator/b1ee emulator/hfp \
tools/smp-tester tools/hci-tester \
tools/rfcomm-tester tools/bnep-tester \
tools/userchan-tester tools/iso-tester \
- tools/mesh-tester
+ tools/mesh-tester tools/ioctl-tester
emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
emulator/serial.h emulator/serial.c \
@@ -213,6 +213,15 @@ tools_iso_tester_SOURCES = tools/iso-tester.c monitor/bt.h \
emulator/smp.c
tools_iso_tester_LDADD = lib/libbluetooth-internal.la \
src/libshared-glib.la $(GLIB_LIBS)
+
+tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \
+ emulator/hciemu.h emulator/hciemu.c \
+ emulator/vhci.h emulator/vhci.c \
+ emulator/btdev.h emulator/btdev.c \
+ emulator/bthost.h emulator/bthost.c \
+ emulator/smp.c
+tools_ioctl_tester_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la $(GLIB_LIBS)
endif
if TOOLS
new file mode 100644
@@ -0,0 +1,955 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2022 Intel Corporation.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdbool.h>
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "monitor/bt.h"
+#include "emulator/bthost.h"
+#include "emulator/hciemu.h"
+
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/util.h"
+
+struct test_data {
+ const void *test_data;
+ int sock_fd;
+ struct hciemu *hciemu;
+ enum hciemu_type hciemu_type;
+ uint8_t client_num;
+ uint16_t hci_dev_id;
+
+ struct mgmt *mgmt;
+ uint16_t mgmt_index;
+ struct mgmt *mgmt_alt;
+ unsigned int mgmt_alt_ev_id;
+
+ uint16_t handle;
+ uint16_t acl_handle;
+ GIOChannel *io;
+ unsigned int io_id[2];
+ int step;
+ bool reconnect;
+
+ int unmet_conditions;
+};
+
+struct ioctl_data {
+ uint32_t cmd;
+ const uint32_t opt;
+ const void *param;
+ int (*cmd_param_func)(void *param, uint32_t *length);
+ int expected_ioctl_err;
+ const void *block_bdaddr;
+ const void *expected_data;
+ int (*expect_data_check_func)(const void *param, uint32_t length);
+};
+
+static void print_debug(const char *str, void *user_data)
+{
+ const char *prefix = user_data;
+
+ tester_print("%s%s", prefix, str);
+}
+
+static void test_add_condition(struct test_data *data)
+{
+ data->unmet_conditions++;
+
+ tester_print("Test condition added, total %d", data->unmet_conditions);
+}
+
+static void test_condition_complete(struct test_data *data)
+{
+ data->unmet_conditions--;
+
+ tester_print("Test condition complete, %d left",
+ data->unmet_conditions);
+
+ if (data->unmet_conditions > 0)
+ return;
+
+ tester_test_passed();
+}
+
+static int update_hci_dev_id(struct test_data *data)
+{
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+ int ret = 0;
+
+ dl = malloc(HCI_MAX_DEV * sizeof(*dr) + sizeof(uint16_t));
+ if (!dl)
+ return -ENOMEM;
+
+ dl->dev_num = HCI_MAX_DEV;
+ dr = dl->dev_req;
+
+ if (ioctl(data->sock_fd, HCIGETDEVLIST, (void *) dl) < 0) {
+ ret = -EIO;
+ goto exit;
+ }
+
+ if (dl->dev_num != 1) {
+ tester_warn("dev num mismatch returned %d:expected 1",
+ dl->dev_num);
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ data->hci_dev_id = dr->dev_id;
+ tester_print("HCI device id: %d", data->hci_dev_id);
+
+exit:
+ free(dl);
+ return ret;
+}
+
+static void read_info_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = tester_get_data();
+ const struct mgmt_rp_read_info *rp = param;
+ char addr[18];
+ uint16_t manufacturer;
+ uint32_t supported_settings, current_settings;
+
+ tester_print("Read Info callback");
+ tester_print(" Status: 0x%02x", status);
+
+ if (status || !param) {
+ tester_pre_setup_failed();
+ return;
+ }
+
+ ba2str(&rp->bdaddr, addr);
+ manufacturer = btohs(rp->manufacturer);
+ supported_settings = btohl(rp->supported_settings);
+ current_settings = btohl(rp->current_settings);
+
+ tester_print(" Address: %s", addr);
+ tester_print(" Version: 0x%02x", rp->version);
+ tester_print(" Manufacturer: 0x%04x", manufacturer);
+ tester_print(" Supported settings: 0x%08x", supported_settings);
+ tester_print(" Current settings: 0x%08x", current_settings);
+ tester_print(" Class: 0x%02x%02x%02x",
+ rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+ tester_print(" Name: %s", rp->name);
+ tester_print(" Short name: %s", rp->short_name);
+
+ if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+ tester_pre_setup_failed();
+ return;
+ }
+
+ tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = tester_get_data();
+
+ tester_print("Index Added callback");
+ tester_print(" Index: 0x%04x", index);
+
+ data->mgmt_index = index;
+
+ mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+ read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = tester_get_data();
+
+ tester_print("Index Removed callback");
+ tester_print(" Index: 0x%04x", index);
+
+ if (index != data->mgmt_index)
+ return;
+
+ mgmt_unregister_index(data->mgmt, data->mgmt_index);
+ mgmt_unregister_index(data->mgmt_alt, data->mgmt_index);
+
+ mgmt_unref(data->mgmt);
+ data->mgmt = NULL;
+
+ mgmt_unref(data->mgmt_alt);
+ data->mgmt_alt = NULL;
+
+ tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = tester_get_data();
+
+ tester_print("Read Index List callback");
+ tester_print(" Status: 0x%02x", status);
+
+ if (status || !param) {
+ tester_pre_setup_failed();
+ return;
+ }
+
+ mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+ index_added_callback, NULL, NULL);
+
+ mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+ index_removed_callback, NULL, NULL);
+
+ data->hciemu = hciemu_new(data->hciemu_type);
+ if (!data->hciemu) {
+ tester_warn("Failed to setup HCI emulation");
+ tester_pre_setup_failed();
+ }
+
+ if (tester_use_debug())
+ hciemu_set_debug(data->hciemu, print_debug, "hciemu: ", NULL);
+
+ tester_print("New hciemu instance created");
+
+ data->sock_fd = hci_open_dev(0);
+ if (data->sock_fd < 0) {
+ tester_warn("Failed to open socket for ioctl");
+ tester_pre_setup_failed();
+ return;
+ }
+
+ update_hci_dev_id(data);
+}
+
+static void test_pre_setup(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+
+ data->mgmt = mgmt_new_default();
+ if (!data->mgmt) {
+ tester_warn("Failed to setup mgmt interface");
+ tester_pre_setup_failed();
+ return;
+ }
+
+ data->mgmt_alt = mgmt_new_default();
+ if (!data->mgmt_alt) {
+ tester_warn("Failed to setup alternate management interface");
+ tester_pre_setup_failed();
+
+ mgmt_unref(data->mgmt);
+ data->mgmt = NULL;
+ return;
+ }
+
+
+ if (tester_use_debug()) {
+ mgmt_set_debug(data->mgmt, print_debug, "mgmt: ", NULL);
+ mgmt_set_debug(data->mgmt_alt, print_debug, "mgmt-alt: ", NULL);
+ }
+
+ mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
+ read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+
+ if (data->sock_fd >= 0) {
+ tester_print("Socket closed");
+ hci_close_dev(data->sock_fd);
+ }
+
+ hciemu_unref(data->hciemu);
+ data->hciemu = NULL;
+}
+
+static void test_data_free(void *test_data)
+{
+ struct test_data *data = test_data;
+
+ // TODO: free any data allocated during pre-setup
+
+ free(data);
+}
+
+#define test_ioctl_full(name, data, setup, func, num) \
+ do { \
+ struct test_data *user; \
+ user = new0(struct test_data, 1); \
+ if (!user) \
+ break; \
+ user->hciemu_type = HCIEMU_TYPE_BREDRLE; \
+ user->test_data = data; \
+ user->client_num = num; \
+ tester_add_full(name, data, \
+ test_pre_setup, setup, func, NULL, \
+ test_post_teardown, 2, user, test_data_free); \
+ } while (0)
+
+#define test_ioctl(name, data, setup, func) \
+ test_ioctl_full(name, data, setup, func, 1)
+
+static void setup_powered_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ tester_setup_failed();
+ return;
+ }
+
+ tester_print("Controller powered on");
+
+ tester_setup_complete();
+}
+
+static void setup_powered(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ unsigned char param[] = { 0x01 };
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_BONDABLE, 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_SET_SSP, data->mgmt_index,
+ sizeof(param), param, NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), param,
+ setup_powered_callback, NULL, NULL);
+}
+
+static void setup_add_block_bdaddr(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ const struct ioctl_data *ioctl_data = data->test_data;
+
+ if (!ioctl_data->block_bdaddr) {
+ tester_warn("Invalid test data: block bdaddr");
+ tester_setup_failed();
+ return;
+ }
+
+ if (ioctl(data->sock_fd, HCIBLOCKADDR, ioctl_data->block_bdaddr) < 0) {
+ tester_warn("Failed to add block bdaddr");
+ tester_setup_failed();
+ return;
+ }
+
+ tester_print("Added block BDADDR");
+
+ tester_setup_complete();
+}
+
+static int conn_list_empty_check_func(const void *param, uint32_t length)
+{
+ struct test_data *data = tester_get_data();
+ const struct ioctl_data *ioctl_data = data->test_data;
+ const struct hci_conn_list_req *cl_input = ioctl_data->expected_data;
+ const struct hci_conn_list_req *cl = param;
+
+ if (cl->conn_num != cl_input->conn_num)
+ return -1;
+
+ return 0;
+}
+
+static int conn_info_cmd_param_func(void *param, uint32_t *length)
+{
+ struct test_data *data = tester_get_data();
+ const struct ioctl_data *ioctl_data = data->test_data;
+ const struct hci_conn_info_req *cr_input = ioctl_data->param;
+ struct hci_conn_info_req *cr = param;
+
+ memcpy(&cr->bdaddr, &cr_input->bdaddr, sizeof(bdaddr_t));
+ cr->type = cr_input->type;
+
+ return 0;
+}
+
+static int auth_info_cmd_param_func(void *param, uint32_t *length)
+{
+ struct test_data *data = tester_get_data();
+ const struct ioctl_data *ioctl_data = data->test_data;
+ const struct hci_auth_info_req *ar_input = ioctl_data->param;
+ struct hci_auth_info_req *ar = param;
+
+ memcpy(&ar->bdaddr, &ar_input->bdaddr, sizeof(bdaddr_t));
+ if (ar_input->type)
+ ar->type = ar_input->type;
+
+ return 0;
+}
+
+static const struct ioctl_data dev_down = {
+ .cmd = HCIDEVDOWN,
+};
+
+static const struct hci_dev_list_req dev_list_1 = {
+ .dev_num = 0x01,
+ .dev_req = {{
+ .dev_id = 0x00,
+ .dev_opt = 0x04,
+ }},
+};
+
+static const struct ioctl_data dev_list = {
+ .cmd = HCIGETDEVLIST,
+ .expected_data = (void *)&dev_list_1,
+};
+
+static const struct hci_dev_list_req dev_list_invalid_1_param = {
+ .dev_num = 0x00,
+};
+
+static const struct ioctl_data dev_list_invalid_1 = {
+ .cmd = HCIGETDEVLIST,
+ .param = (void *)&dev_list_invalid_1_param,
+ .expected_ioctl_err = EINVAL,
+};
+
+static const struct ioctl_data dev_info = {
+ .cmd = HCIGETDEVINFO,
+};
+
+static const struct ioctl_data reset_stat = {
+ .cmd = HCIDEVRESTAT,
+};
+
+static const struct ioctl_data set_link_mode_master = {
+ .cmd = HCISETLINKMODE,
+ .opt = HCI_LM_MASTER,
+};
+
+static const struct ioctl_data set_link_mode_accept = {
+ .cmd = HCISETLINKMODE,
+ .opt = HCI_LM_ACCEPT,
+};
+
+static const struct ioctl_data set_pkt_type_dm = {
+ .cmd = HCISETPTYPE,
+ .opt = HCI_DM1 | HCI_DM3 | HCI_DM5,
+};
+
+static const struct ioctl_data set_pkt_type_dh = {
+ .cmd = HCISETPTYPE,
+ .opt = HCI_DH1 | HCI_DH3 | HCI_DH5,
+};
+
+static const struct ioctl_data set_pkt_type_hv = {
+ .cmd = HCISETPTYPE,
+ .opt = HCI_HV1 | HCI_HV2 | HCI_HV3,
+};
+
+static const struct ioctl_data set_pkt_type_2dh = {
+ .cmd = HCISETPTYPE,
+ .opt = HCI_2DH1 | HCI_2DH3 | HCI_2DH5,
+};
+
+static const struct ioctl_data set_pkt_type_3dh = {
+ .cmd = HCISETPTYPE,
+ .opt = HCI_3DH1 | HCI_3DH3 | HCI_3DH5,
+};
+
+static const struct ioctl_data set_pkt_type_all = {
+ .cmd = HCISETPTYPE,
+ .opt = HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5 |
+ HCI_HV1 | HCI_HV2 | HCI_HV3 | HCI_2DH1 | HCI_2DH3 | HCI_2DH5 |
+ HCI_3DH1 | HCI_3DH3 | HCI_3DH5,
+};
+
+static const struct ioctl_data set_acl_mtu_1 = {
+ .cmd = HCISETACLMTU,
+ .opt = 0x1 | (0x3FE << 16),
+};
+
+static const struct ioctl_data set_acl_mtu_2 = {
+ .cmd = HCISETACLMTU,
+ .opt = 0x4 | (0x63 << 16),
+};
+
+static const struct ioctl_data set_sco_mtu_1 = {
+ .cmd = HCISETSCOMTU,
+ .opt = 0x1 | (0x3FE << 16),
+};
+
+static const struct ioctl_data set_sco_mtu_2 = {
+ .cmd = HCISETSCOMTU,
+ .opt = 0x4 | (0x63 << 16),
+};
+
+static const uint8_t bdaddr1[] = {
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66
+};
+
+static const struct ioctl_data block_bdaddr_success = {
+ .cmd = HCIBLOCKADDR,
+ .param = bdaddr1,
+};
+
+static const struct ioctl_data block_bdaddr_fail = {
+ .cmd = HCIBLOCKADDR,
+ .param = bdaddr1,
+ .expected_ioctl_err = EEXIST,
+ .block_bdaddr = bdaddr1,
+};
+
+static const struct ioctl_data unblock_bdaddr_success = {
+ .cmd = HCIUNBLOCKADDR,
+ .param = bdaddr1,
+ .block_bdaddr = bdaddr1,
+};
+
+static const struct ioctl_data unblock_bdaddr_fail = {
+ .cmd = HCIUNBLOCKADDR,
+ .param = bdaddr1,
+ .expected_ioctl_err = ENOENT,
+};
+
+static const struct hci_conn_list_req conn_list_empty = {
+ .dev_id = 0x00,
+ .conn_num = 0x00,
+};
+
+static const struct ioctl_data conn_list_no_conn = {
+ .cmd = HCIGETCONNLIST,
+ .expected_data = (void *)&conn_list_empty,
+ .expect_data_check_func = conn_list_empty_check_func,
+};
+
+static const struct hci_conn_info conn_info_1 = {
+ .handle = 0x002a,
+ .bdaddr = {{ 0x00, 0x00, 0x01, 0x01, 0xaa, 0x00 }},
+ .type = 0x01,
+ .out = 0x00,
+ .state = 0x0001,
+ .link_mode = 0x00000000,
+};
+
+static const struct hci_conn_list_req conn_list_req_1 = {
+ .dev_id = 0x00,
+ .conn_num = 0x01,
+ .conn_info = { conn_info_1 },
+};
+
+static const struct ioctl_data conn_list = {
+ .cmd = HCIGETCONNLIST,
+ .expected_data = (void *)&conn_list_req_1,
+};
+
+static const struct hci_conn_info_req conn_info_req = {
+ .bdaddr = {{ 0x00, 0x00, 0x01, 0x01, 0xaa, 0x00 }},
+ .type = ACL_LINK,
+ .conn_info = { conn_info_1 },
+};
+
+static const struct hci_conn_info_req conn_info_req_acl = {
+ .bdaddr = {{ 0x00, 0x00, 0x01, 0x01, 0xaa, 0x00 }},
+ .type = ACL_LINK,
+};
+
+static const struct hci_conn_info_req conn_info_req_sco = {
+ .bdaddr = {{ 0x00, 0x00, 0x01, 0x01, 0xaa, 0x00 }},
+ .type = SCO_LINK,
+};
+
+static const struct ioctl_data conn_info = {
+ .cmd = HCIGETCONNINFO,
+ .param = (void *)&conn_info_req_acl,
+ .cmd_param_func = conn_info_cmd_param_func,
+ .expected_data = (void *)&conn_info_req,
+};
+
+static const struct ioctl_data conn_info_no_conn = {
+ .cmd = HCIGETCONNINFO,
+ .param = (void *)&conn_info_req_acl,
+ .expected_ioctl_err = ENOENT,
+ .cmd_param_func = conn_info_cmd_param_func,
+};
+
+static const struct ioctl_data conn_info_wrong_type = {
+ .cmd = HCIGETCONNINFO,
+ .param = (void *)&conn_info_req_sco,
+ .expected_ioctl_err = ENOENT,
+ .cmd_param_func = conn_info_cmd_param_func,
+};
+
+static const struct hci_auth_info_req auth_info_req = {
+ .bdaddr = {{ 0x00, 0x00, 0x01, 0x01, 0xaa, 0x00 }},
+};
+
+static const struct hci_auth_info_req auth_info_connected = {
+ .bdaddr = {{ 0x00, 0x00, 0x01, 0x01, 0xaa, 0x00 }},
+ .type = 0x04,
+};
+
+static const struct ioctl_data auth_info_no_conn = {
+ .cmd = HCIGETAUTHINFO,
+ .param = (void *)&auth_info_req,
+ .expected_ioctl_err = ENOENT,
+ .cmd_param_func = auth_info_cmd_param_func,
+};
+
+static const struct ioctl_data auth_info = {
+ .cmd = HCIGETAUTHINFO,
+ .param = (void *)&auth_info_req,
+ .cmd_param_func = auth_info_cmd_param_func,
+ .expected_data = (void *)&auth_info_connected,
+};
+
+/* Allocate the command request parameters based on the command.
+ * returns the allocated request buffer and its length
+ */
+static int test_alloc_cmd_param(void **req, uint32_t *req_len)
+{
+ struct test_data *data = tester_get_data();
+ const struct ioctl_data *ioctl_data = data->test_data;
+ struct hci_dev_req *dr = NULL;
+ struct hci_dev_info *di = NULL;
+ struct hci_dev_list_req *dl = NULL;
+ struct hci_conn_list_req *cl = NULL;
+ struct hci_conn_info *ci = NULL;
+ struct hci_conn_info_req *cr = NULL;
+ struct hci_auth_info_req *ar = NULL;
+ bdaddr_t *bdaddr = NULL;
+ uint32_t len;
+
+ switch (ioctl_data->cmd) {
+ case HCISETAUTH:
+ case HCISETENCRYPT:
+ case HCISETLINKMODE:
+ case HCISETPTYPE:
+ case HCISETACLMTU:
+ case HCISETSCOMTU:
+ len = sizeof(*dr);
+ dr = malloc(len);
+ if (!dr)
+ return -ENOMEM;
+ memset(dr, 0, len);
+ dr->dev_id = data->hci_dev_id;
+ dr->dev_opt = ioctl_data->opt;
+ *req = dr;
+ *req_len = len;
+ break;
+ case HCIGETDEVINFO:
+ len = sizeof(*di);
+ di = malloc(len);
+ if (!di)
+ return -ENOMEM;
+ memset(di, 0, len);
+ di->dev_id = data->hci_dev_id;
+ *req = di;
+ *req_len = len;
+ break;
+ case HCIGETDEVLIST:
+ len = sizeof(*dr) + sizeof(uint16_t);
+ dl = malloc(len);
+ if (!dl)
+ return -ENOMEM;
+ memset(dl, 0, len);
+ dl->dev_num = 1;
+ *req = dl;
+ *req_len = len;
+ break;
+ case HCIGETCONNLIST:
+ len = sizeof(*cl) + sizeof(*ci);
+ cl = malloc(len);
+ if (!cl)
+ return -ENOMEM;
+ memset(cl, 0, len);
+ cl->dev_id = data->hci_dev_id;
+ cl->conn_num = 1;
+ *req = cl;
+ *req_len = len;
+ break;
+ case HCIGETCONNINFO:
+ len = sizeof(*cr) + sizeof(*ci);
+ cr = malloc(len);
+ if (!cr)
+ return -ENOMEM;
+ memset(cr, 0, len);
+ *req = cr;
+ *req_len = len;
+ break;
+ case HCIGETAUTHINFO:
+ len = sizeof(*ar);
+ ar = malloc(len);
+ if (!ar)
+ return -ENOMEM;
+ memset(ar, 0, len);
+ *req = ar;
+ *req_len = len;
+ break;
+ case HCIBLOCKADDR:
+ case HCIUNBLOCKADDR:
+ len = sizeof(bdaddr_t);
+ bdaddr = malloc(len);
+ if (!bdaddr)
+ return -ENOMEM;
+ memset(bdaddr, 0, len);
+ *req = bdaddr;
+ *req_len = len;
+ break;
+ case HCIDEVUP:
+ case HCIDEVDOWN:
+ case HCIDEVRESET:
+ case HCIDEVRESTAT:
+ /* These command uses the HCI dev id for param */
+ return -ENODATA;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void test_ioctl_common(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ const struct ioctl_data *ioctl_data = data->test_data;
+ bool use_dev_id = false;
+ void *req = NULL;
+ uint32_t req_len = 0;
+ int ret;
+
+ ret = test_alloc_cmd_param(&req, &req_len);
+ if (ret < 0) {
+ if (ret == -ENODATA)
+ use_dev_id = true;
+ else {
+ tester_warn("Failed to allocate CMD parameter");
+ tester_test_failed();
+ return;
+ }
+ }
+
+ if (ioctl_data->expected_ioctl_err)
+ test_add_condition(data);
+
+ if (ioctl_data->expected_data)
+ test_add_condition(data);
+
+ if (ioctl_data->param) {
+ test_add_condition(data);
+ if (ioctl_data->cmd_param_func) {
+ ret = ioctl_data->cmd_param_func(req, &req_len);
+ if (ret) {
+ tester_warn("Failed to update cmd param");
+ tester_test_failed();
+ return;
+ }
+ } else
+ memcpy(req, ioctl_data->param, req_len);
+
+ tester_print("Command Parameter is updated");
+ test_condition_complete(data);
+ }
+
+ if (use_dev_id)
+ ret = ioctl(data->sock_fd, ioctl_data->cmd, data->hci_dev_id);
+ else
+ ret = ioctl(data->sock_fd, ioctl_data->cmd, req);
+
+ if (ret < 0) {
+ if (ioctl_data->expected_ioctl_err) {
+ if (errno != ioctl_data->expected_ioctl_err) {
+ tester_warn("Unexpected error: %d expected: %d",
+ errno, ioctl_data->expected_ioctl_err);
+ tester_test_failed();
+ return;
+ }
+
+ test_condition_complete(data);
+ tester_print("Received expected error: %d", errno);
+ tester_test_passed();
+ return;
+ }
+
+ tester_warn("IOCTL failed with error: %d", errno);
+ tester_test_failed();
+ return;
+ }
+
+ if (ioctl_data->expected_data && req) {
+ if (ioctl_data->expect_data_check_func)
+ ret = ioctl_data->expect_data_check_func(req, req_len);
+ else
+ ret = memcmp(req, ioctl_data->expected_data, req_len);
+
+ if (ret != 0) {
+ tester_warn("Mismatch expected data");
+ util_hexdump('>', req, req_len, print_debug, "");
+ util_hexdump('!', ioctl_data->expected_data, req_len,
+ print_debug, "");
+ tester_test_failed();
+ return;
+ }
+
+ test_condition_complete(data);
+ }
+
+ if (req)
+ free(req);
+
+ tester_print("Test Completed--");
+ tester_test_passed();
+}
+
+static void test_ioctl_connected_event(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = tester_get_data();
+
+ tester_print("Device Connected");
+
+ test_ioctl_common(data);
+}
+
+static void test_ioctl_connection(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ unsigned int id;
+ const uint8_t *central_bdaddr;
+ struct bthost *bthost;
+ uint8_t addr_type;
+
+ tester_print("Registering %s notification",
+ mgmt_evstr(MGMT_EV_DEVICE_CONNECTED));
+ id = mgmt_register(data->mgmt_alt, MGMT_EV_DEVICE_CONNECTED,
+ data->mgmt_index,
+ test_ioctl_connected_event,
+ NULL, NULL);
+ data->mgmt_alt_ev_id = id;
+
+ central_bdaddr = hciemu_get_central_bdaddr(data->hciemu);
+ if (!central_bdaddr) {
+ tester_warn("No central bdaddr");
+ tester_setup_failed();
+ return;
+ }
+
+ addr_type = data->hciemu_type == HCIEMU_TYPE_BREDRLE ? BDADDR_BREDR :
+ BDADDR_LE_PUBLIC;
+ tester_print("ADDR TYPE: %d", addr_type);
+ bthost = hciemu_client_get_host(data->hciemu);
+ bthost_hci_connect(bthost, central_bdaddr, addr_type);
+}
+
+int main(int argc, char *argv[])
+{
+ tester_init(&argc, &argv);
+
+ test_ioctl("HCI Down", &dev_down, NULL, test_ioctl_common);
+
+ test_ioctl("Device List", &dev_list,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Device List - Invalid Param 1", &dev_list_invalid_1,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Device Info", &dev_info,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Reset Stat", &reset_stat,
+ setup_powered, test_ioctl_common);
+
+ test_ioctl("Set Link Mode - ACCEPT", &set_link_mode_accept,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Set Link Mode - MASTER", &set_link_mode_master,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Set Pkt Type - DM", &set_pkt_type_dm,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Set Pkt Type - DH", &set_pkt_type_dh,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Set Pkt Type - HV", &set_pkt_type_hv,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Set Pkt Type - 2-DH", &set_pkt_type_2dh,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Set Pkt Type - 2-DH", &set_pkt_type_3dh,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Set Pkt Type - ALL", &set_pkt_type_all,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Set ACL MTU - 1", &set_acl_mtu_1,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Set ACL MTU - 2", &set_acl_mtu_2,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Set SCO MTU - 1", &set_sco_mtu_1,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Set SCO MTU - 2", &set_sco_mtu_2,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Block BDADDR - Success", &block_bdaddr_success,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Block BDADDR - Fail", &block_bdaddr_fail,
+ setup_add_block_bdaddr, test_ioctl_common);
+
+ test_ioctl("Unblock BDADDR - Success", &unblock_bdaddr_success,
+ setup_add_block_bdaddr, test_ioctl_common);
+
+ test_ioctl("Unblock BDADDR - Fail", &unblock_bdaddr_fail,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Connection List - No Conn", &conn_list_no_conn,
+ NULL, test_ioctl_common);
+
+ test_ioctl("Connection List", &conn_list,
+ setup_powered, test_ioctl_connection);
+
+ test_ioctl("Connection Info", &conn_info,
+ setup_powered, test_ioctl_connection);
+
+ test_ioctl("Connection Info - No Connection", &conn_info_no_conn,
+ setup_powered, test_ioctl_common);
+
+ test_ioctl("Connection Info - Wrong Type", &conn_info_wrong_type,
+ setup_powered, test_ioctl_common);
+
+ test_ioctl("Authentication Info - No Connection", &auth_info_no_conn,
+ setup_powered, test_ioctl_common);
+
+ test_ioctl("Authentication Info", &auth_info,
+ setup_powered, test_ioctl_connection);
+
+ return tester_run();
+}
@@ -615,6 +615,7 @@ static const char *test_table[] = {
"sco-tester",
"iso-tester",
"mesh-tester",
+ "ioctl-tester",
"bnep-tester",
"check-selftest",
"tools/mgmt-tester",
@@ -624,6 +625,7 @@ static const char *test_table[] = {
"tools/sco-tester",
"tools/iso-tester",
"tools/mesh-tester",
+ "tools/ioctl-tester",
"tools/bnep-tester",
"tools/check-selftest",
NULL
From: Tedd Ho-Jeong An <tedd.an@intel.com> This patch adds ioctl-tester which tests the IOCTL commands. HCI Down Device List Device List - Invalid Param 1 Device Info Reset Stat Set Link Mode - ACCEPT Set Link Mode - MASTER Set Pkt Type - DM Set Pkt Type - DH Set Pkt Type - HV Set Pkt Type - 2-DH Set Pkt Type - 2-DH Set Pkt Type - ALL Set ACL MTU - 1 Set ACL MTU - 2 Set SCO MTU - 1 Set SCO MTU - 2 Block BDADDR - Success Block BDADDR - Fail Unblock BDADDR - Success Unblock BDADDR - Fail Connection List - No Conn Connection List Connection Info Connection Info - No Connection Connection Info - Wrong Type Authentication Info - No Connection Authentication Info --- Makefile.tools | 11 +- tools/ioctl-tester.c | 955 +++++++++++++++++++++++++++++++++++++++++++ tools/test-runner.c | 2 + 3 files changed, 967 insertions(+), 1 deletion(-) create mode 100644 tools/ioctl-tester.c