/* main.c - Host long advertising receive */ /* * Copyright (c) 2021-2024 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define DT_DRV_COMPAT zephyr_bt_hci_test struct driver_data { bt_hci_recv_t recv; }; #define LOG_LEVEL CONFIG_BT_LOG_LEVEL #include LOG_MODULE_REGISTER(host_test_app); DEFINE_FFF_GLOBALS; struct test_adv_report { uint8_t data[CONFIG_BT_EXT_SCAN_BUF_SIZE]; uint8_t length; uint16_t evt_prop; bt_addr_le_t addr; }; #define COMPLETE BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_COMPLETE << 5 #define MORE_TO_COME BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_PARTIAL << 5 #define TRUNCATED BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_INCOMPLETE << 5 /* Command handler structure for cmd_handle(). */ struct cmd_handler { uint16_t opcode; /* HCI command opcode */ uint8_t len; /* HCI command response length */ void (*handler)(struct net_buf *buf, struct net_buf **evt, uint8_t len, uint16_t opcode); }; /* Add event to net_buf. */ static void evt_create(struct net_buf *buf, uint8_t evt, uint8_t len) { struct bt_hci_evt_hdr *hdr; hdr = net_buf_add(buf, sizeof(*hdr)); hdr->evt = evt; hdr->len = len; } static void le_meta_evt_create(struct bt_hci_evt_le_meta_event *evt, uint8_t subevent) { evt->subevent = subevent; } static void adv_info_create(struct bt_hci_evt_le_ext_advertising_info *evt, uint16_t evt_type, const bt_addr_le_t *const addr, uint8_t length) { evt->evt_type = evt_type; bt_addr_le_copy(&evt->addr, addr); evt->prim_phy = 0; evt->sec_phy = 0; evt->sid = 0; evt->tx_power = 0; evt->rssi = 0; evt->interval = 0; bt_addr_le_copy(&evt->direct_addr, BT_ADDR_LE_NONE); evt->length = length; } /* Create a command complete event. */ static void *cmd_complete(struct net_buf **buf, uint8_t plen, uint16_t opcode) { struct bt_hci_evt_cmd_complete *cc; *buf = bt_buf_get_evt(BT_HCI_EVT_CMD_COMPLETE, false, K_FOREVER); evt_create(*buf, BT_HCI_EVT_CMD_COMPLETE, sizeof(*cc) + plen); cc = net_buf_add(*buf, sizeof(*cc)); cc->ncmd = 1U; cc->opcode = sys_cpu_to_le16(opcode); return net_buf_add(*buf, plen); } /* Loop over handlers to try to handle the command given by opcode. */ static int cmd_handle_helper(uint16_t opcode, struct net_buf *cmd, struct net_buf **evt, const struct cmd_handler *handlers, size_t num_handlers) { for (size_t i = 0; i < num_handlers; i++) { const struct cmd_handler *handler = &handlers[i]; if (handler->opcode != opcode) { continue; } if (handler->handler) { handler->handler(cmd, evt, handler->len, opcode); return 0; } } zassert_unreachable("opcode %X failed", opcode); return -EINVAL; } /* Lookup the command opcode and invoke handler. */ static int cmd_handle(const struct device *dev, struct net_buf *cmd, const struct cmd_handler *handlers, size_t num_handlers) { struct driver_data *drv = dev->data; struct net_buf *evt = NULL; struct bt_hci_evt_cc_status *ccst; struct bt_hci_cmd_hdr *chdr; uint16_t opcode; int err; chdr = net_buf_pull_mem(cmd, sizeof(*chdr)); opcode = sys_le16_to_cpu(chdr->opcode); err = cmd_handle_helper(opcode, cmd, &evt, handlers, num_handlers); if (err == -EINVAL) { ccst = cmd_complete(&evt, sizeof(*ccst), opcode); ccst->status = BT_HCI_ERR_UNKNOWN_CMD; } if (evt) { drv->recv(dev, evt); } return err; } /* Generic command complete with success status. */ static void generic_success(struct net_buf *buf, struct net_buf **evt, uint8_t len, uint16_t opcode) { struct bt_hci_evt_cc_status *ccst; ccst = cmd_complete(evt, len, opcode); /* Fill any event parameters with zero */ (void)memset(ccst, 0, len); ccst->status = BT_HCI_ERR_SUCCESS; } /* Bogus handler for BT_HCI_OP_READ_LOCAL_FEATURES. */ static void read_local_features(struct net_buf *buf, struct net_buf **evt, uint8_t len, uint16_t opcode) { struct bt_hci_rp_read_local_features *rp; rp = cmd_complete(evt, sizeof(*rp), opcode); rp->status = 0x00; (void)memset(rp->features, 0xFF, sizeof(rp->features)); } /* Bogus handler for BT_HCI_OP_READ_SUPPORTED_COMMANDS. */ static void read_supported_commands(struct net_buf *buf, struct net_buf **evt, uint8_t len, uint16_t opcode) { struct bt_hci_rp_read_supported_commands *rp; rp = cmd_complete(evt, sizeof(*rp), opcode); (void)memset(rp->commands, 0xFF, sizeof(rp->commands)); rp->status = 0x00; } /* Bogus handler for BT_HCI_OP_LE_READ_LOCAL_FEATURES. */ static void le_read_local_features(struct net_buf *buf, struct net_buf **evt, uint8_t len, uint16_t opcode) { struct bt_hci_rp_le_read_local_features *rp; rp = cmd_complete(evt, sizeof(*rp), opcode); rp->status = 0x00; (void)memset(rp->features, 0xFF, sizeof(rp->features)); } /* Bogus handler for BT_HCI_OP_LE_READ_SUPP_STATES. */ static void le_read_supp_states(struct net_buf *buf, struct net_buf **evt, uint8_t len, uint16_t opcode) { struct bt_hci_rp_le_read_supp_states *rp; rp = cmd_complete(evt, sizeof(*rp), opcode); rp->status = 0x00; (void)memset(&rp->le_states, 0xFF, sizeof(rp->le_states)); } /* Setup handlers needed for bt_enable to function. */ static const struct cmd_handler cmds[] = { { BT_HCI_OP_READ_LOCAL_VERSION_INFO, sizeof(struct bt_hci_rp_read_local_version_info), generic_success }, { BT_HCI_OP_READ_SUPPORTED_COMMANDS, sizeof(struct bt_hci_rp_read_supported_commands), read_supported_commands }, { BT_HCI_OP_READ_LOCAL_FEATURES, sizeof(struct bt_hci_rp_read_local_features), read_local_features }, { BT_HCI_OP_READ_BD_ADDR, sizeof(struct bt_hci_rp_read_bd_addr), generic_success }, { BT_HCI_OP_SET_EVENT_MASK, sizeof(struct bt_hci_evt_cc_status), generic_success }, { BT_HCI_OP_LE_SET_EVENT_MASK, sizeof(struct bt_hci_evt_cc_status), generic_success }, { BT_HCI_OP_LE_READ_LOCAL_FEATURES, sizeof(struct bt_hci_rp_le_read_local_features), le_read_local_features }, { BT_HCI_OP_LE_READ_SUPP_STATES, sizeof(struct bt_hci_rp_le_read_supp_states), le_read_supp_states }, { BT_HCI_OP_LE_RAND, sizeof(struct bt_hci_rp_le_rand), generic_success }, { BT_HCI_OP_LE_SET_RANDOM_ADDRESS, sizeof(struct bt_hci_cp_le_set_random_address), generic_success }, { BT_HCI_OP_LE_SET_EXT_SCAN_PARAM, 0, generic_success }, { BT_HCI_OP_LE_SET_EXT_SCAN_ENABLE, 0, generic_success }, { BT_HCI_OP_RESET, 0, generic_success }, }; /* HCI driver open. */ static int driver_open(const struct device *dev, bt_hci_recv_t recv) { struct driver_data *drv = dev->data; drv->recv = recv; return 0; } /* HCI driver send. */ static int driver_send(const struct device *dev, struct net_buf *buf) { zassert_true(cmd_handle(dev, buf, cmds, ARRAY_SIZE(cmds)) == 0, "Unknown HCI command"); net_buf_unref(buf); return 0; } static DEVICE_API(bt_hci, driver_api) = { .open = driver_open, .send = driver_send, }; #define TEST_DEVICE_INIT(inst) \ static struct driver_data driver_data_##inst = { \ }; \ DEVICE_DT_INST_DEFINE(inst, NULL, NULL, &driver_data_##inst, NULL, \ POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &driver_api) DT_INST_FOREACH_STATUS_OKAY(TEST_DEVICE_INIT) struct bt_recv_job_data { struct k_work work; /* Work item */ struct k_sem *sync; /* Semaphore to synchronize with */ struct net_buf *buf; /* Net buffer to be passed to bt_recv() */ } job_data[CONFIG_BT_BUF_EVT_RX_COUNT]; #define job(buf) (&job_data[net_buf_id(buf)]) /* Work item handler for bt_recv() jobs. */ static void bt_recv_job_cb(struct k_work *item) { const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0)); struct driver_data *drv = dev->data; struct bt_recv_job_data *data = CONTAINER_OF(item, struct bt_recv_job_data, work); /* Send net buffer to host */ drv->recv(dev, data->buf); /* Wake up bt_recv_job_submit */ k_sem_give(job(data->buf)->sync); } /* Prepare a job to call bt_recv() to be submitted to the system workqueue. */ static void bt_recv_job_submit(struct net_buf *buf) { struct k_sem sync_sem; /* Store the net buffer to be passed to bt_recv */ job(buf)->buf = buf; /* Initialize job work item/semaphore */ k_work_init(&job(buf)->work, bt_recv_job_cb); k_sem_init(&sync_sem, 0, 1); job(buf)->sync = &sync_sem; /* Make sure the buffer stays around until the command completes */ buf = net_buf_ref(buf); /* Submit the work item */ k_work_submit(&job(buf)->work); /* Wait for bt_recv_job_cb to be done */ k_sem_take(&sync_sem, K_FOREVER); net_buf_unref(buf); } /* Semaphore to test if the prop callback was called. */ static K_SEM_DEFINE(prop_cb_sem, 0, 1); static void *adv_report_evt(struct net_buf *buf, uint8_t data_len, uint16_t evt_type, const bt_addr_le_t *const addr) { struct bt_hci_evt_le_meta_event *meta_evt; struct bt_hci_evt_le_ext_advertising_info *evt; evt_create(buf, BT_HCI_EVT_LE_META_EVENT, sizeof(*meta_evt) + sizeof(*evt) + data_len + 1); meta_evt = net_buf_add(buf, sizeof(*meta_evt)); le_meta_evt_create(meta_evt, BT_HCI_EVT_LE_EXT_ADVERTISING_REPORT); net_buf_add_u8(buf, 1); /* Number of reports */ evt = net_buf_add(buf, sizeof(*evt)); adv_info_create(evt, evt_type, addr, data_len); return net_buf_add(buf, data_len); } /* Send a prop event report wit the given data. */ static void send_adv_report(const struct test_adv_report *report) { LOG_DBG("Sending adv report"); struct net_buf *buf; uint8_t *adv_data; buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER); adv_data = adv_report_evt(buf, report->length, report->evt_prop, &report->addr); memcpy(adv_data, &report->data, report->length); /* Submit job */ bt_recv_job_submit(buf); } FAKE_VALUE_FUNC(struct test_adv_report, get_expected_report); static void scan_recv_cb(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf) { ARG_UNUSED(info); LOG_DBG("Received event with length %u", buf->len); const struct test_adv_report expected = get_expected_report(); zassert_equal(buf->len, expected.length, "Lengths should be equal"); zassert_mem_equal(buf->data, expected.data, buf->len, "Data should be equal"); } static void scan_timeout_cb(void) { zassert_unreachable("Timeout should not happen"); } static void generate_sequence(uint8_t *dest, uint16_t len, uint8_t range_start, uint8_t range_end) { uint16_t written = 0; uint8_t value = range_start; while (written < len) { *dest++ = value++; written++; if (value > range_end) { value = range_start; } } } ZTEST_SUITE(long_adv_rx_tests, NULL, NULL, NULL, NULL, NULL); ZTEST(long_adv_rx_tests, test_host_long_adv_recv) { struct test_adv_report expected_reports[2]; /* Go! Wait until Bluetooth initialization is done */ zassert_true((bt_enable(NULL) == 0), "bt_enable failed"); static struct bt_le_scan_cb scan_callbacks = { .recv = scan_recv_cb, .timeout = scan_timeout_cb }; bt_le_scan_cb_register(&scan_callbacks); zassert_true((bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL) == 0), "bt_le_scan_start failed"); bt_addr_le_t addr_a; bt_addr_le_t addr_b; bt_addr_le_t addr_c; bt_addr_le_t addr_d; bt_addr_le_create_static(&addr_a); bt_addr_le_create_static(&addr_b); bt_addr_le_create_static(&addr_c); bt_addr_le_create_static(&addr_d); struct test_adv_report report_a_1 = { .length = 30, .evt_prop = MORE_TO_COME }; struct test_adv_report report_a_2 = { .length = 30, .evt_prop = COMPLETE }; bt_addr_le_copy(&report_a_1.addr, &addr_a); bt_addr_le_copy(&report_a_2.addr, &addr_a); struct test_adv_report report_b_1 = { .length = 30, .evt_prop = MORE_TO_COME }; struct test_adv_report report_b_2 = { .length = 30, .evt_prop = COMPLETE }; bt_addr_le_copy(&report_b_1.addr, &addr_b); bt_addr_le_copy(&report_b_2.addr, &addr_b); struct test_adv_report report_c = { .length = 30, .evt_prop = COMPLETE | BT_HCI_LE_ADV_EVT_TYPE_LEGACY }; bt_addr_le_copy(&report_c.addr, &addr_c); struct test_adv_report report_d = { .length = 30, .evt_prop = TRUNCATED }; bt_addr_le_copy(&report_c.addr, &addr_c); struct test_adv_report report_a_combined = { .length = report_a_1.length + report_a_2.length }; struct test_adv_report report_a_1_repeated = { .length = CONFIG_BT_EXT_SCAN_BUF_SIZE }; struct test_adv_report report_b_combined = { .length = report_b_1.length + report_b_2.length }; generate_sequence(report_a_combined.data, report_a_combined.length, 'A', 'Z'); generate_sequence(report_b_combined.data, report_b_combined.length, 'a', 'z'); generate_sequence(report_c.data, report_c.length, '0', '9'); (void)memcpy(report_a_1.data, report_a_combined.data, report_a_1.length); (void)memcpy(report_a_2.data, &report_a_combined.data[report_a_1.length], report_a_2.length); for (int i = 0; i < report_a_1_repeated.length; i += report_a_1.length) { memcpy(&report_a_1_repeated.data[i], report_a_1.data, MIN(report_a_1.length, (report_a_1_repeated.length - i))); } (void)memcpy(report_b_1.data, report_b_combined.data, report_b_1.length); (void)memcpy(report_b_2.data, &report_b_combined.data[report_b_1.length], report_b_2.length); /* Check that non-interleaved fragmented adv reports work */ expected_reports[0] = report_a_combined; expected_reports[1] = report_b_combined; SET_RETURN_SEQ(get_expected_report, expected_reports, 2); send_adv_report(&report_a_1); send_adv_report(&report_a_2); send_adv_report(&report_b_1); send_adv_report(&report_b_2); zassert_equal(2, get_expected_report_fake.call_count); RESET_FAKE(get_expected_report); FFF_RESET_HISTORY(); /* Check that legacy adv reports interleaved with fragmented adv reports work */ expected_reports[0] = report_c; expected_reports[1] = report_a_combined; SET_RETURN_SEQ(get_expected_report, expected_reports, 2); send_adv_report(&report_a_1); send_adv_report(&report_c); /* Interleaved legacy adv report */ send_adv_report(&report_a_2); zassert_equal(2, get_expected_report_fake.call_count); RESET_FAKE(get_expected_report); FFF_RESET_HISTORY(); /* Check that complete adv reports interleaved with fragmented adv reports work */ expected_reports[0] = report_b_2; expected_reports[1] = report_a_combined; SET_RETURN_SEQ(get_expected_report, expected_reports, 2); send_adv_report(&report_a_1); send_adv_report(&report_b_2); /* Interleaved short extended adv report */ send_adv_report(&report_a_2); zassert_equal(2, get_expected_report_fake.call_count); RESET_FAKE(get_expected_report); FFF_RESET_HISTORY(); /* Check that fragmented adv reports from one peer are received, * and that interleaved fragmented adv reports from other peers are discarded */ expected_reports[0] = report_a_combined; expected_reports[1] = report_b_2; SET_RETURN_SEQ(get_expected_report, expected_reports, 2); send_adv_report(&report_a_1); send_adv_report(&report_b_1); /* Interleaved fragmented adv report, NOT SUPPORTED */ send_adv_report(&report_a_2); send_adv_report(&report_b_2); zassert_equal(2, get_expected_report_fake.call_count); RESET_FAKE(get_expected_report); FFF_RESET_HISTORY(); /* Check that host discards the data if the controller keeps sending * incomplete packets. */ expected_reports[0] = report_b_combined; SET_RETURN_SEQ(get_expected_report, expected_reports, 1); for (int i = 0; i < (2 + (CONFIG_BT_EXT_SCAN_BUF_SIZE / report_a_1.length)); i++) { send_adv_report(&report_a_1); } send_adv_report(&report_a_2); /* Check that controller truncated reports do not generate events */ send_adv_report(&report_d); /* Check that reports from a different advertiser works after truncation */ send_adv_report(&report_b_1); send_adv_report(&report_b_2); zassert_equal(1, get_expected_report_fake.call_count); }