| /* Copyright (c) 2023 Nordic Semiconductor ASA |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <argparse.h> |
| #include <zephyr/bluetooth/gatt.h> |
| #include <zephyr/logging/log.h> |
| #include <zephyr/sys/__assert.h> |
| #include <zephyr/settings/settings.h> |
| #include <zephyr/sys/byteorder.h> |
| #include <zephyr/bluetooth/bluetooth.h> |
| |
| #include "testlib/adv.h" |
| #include "testlib/att_read.h" |
| #include "testlib/att_write.h" |
| #include "bs_macro.h" |
| #include "bs_sync.h" |
| #include <testlib/conn.h> |
| #include "testlib/log_utils.h" |
| #include "testlib/scan.h" |
| #include "testlib/security.h" |
| |
| /* This test uses system asserts to fail tests. */ |
| BUILD_ASSERT(__ASSERT_ON); |
| |
| #define CENTRAL_DEVICE_NBR 0 |
| #define PERIPHERAL_DEVICE_NBR 1 |
| |
| LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG); |
| |
| #define UUID_1 \ |
| BT_UUID_DECLARE_128(0xdb, 0x1f, 0xe2, 0x52, 0xf3, 0xc6, 0x43, 0x66, 0xb3, 0x92, 0x5d, \ |
| 0xc6, 0xe7, 0xc9, 0x59, 0x9d) |
| |
| #define UUID_2 \ |
| BT_UUID_DECLARE_128(0x3f, 0xa4, 0x7f, 0x44, 0x2e, 0x2a, 0x43, 0x05, 0xab, 0x38, 0x07, \ |
| 0x8d, 0x16, 0xbf, 0x99, 0xf1) |
| |
| static ssize_t read_mtu_validation_chrc(struct bt_conn *conn, const struct bt_gatt_attr *attr, |
| void *buf, uint16_t buf_len, uint16_t offset) |
| { |
| |
| LOG_DBG("Server side buf_len %u", buf_len); |
| |
| k_msleep(100); |
| |
| LOG_DBG("============================> trigger disconnect"); |
| bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_POWER_OFF); |
| |
| /* We ain't read nothin' */ |
| return 0; |
| } |
| |
| static struct bt_gatt_attr attrs[] = { |
| BT_GATT_PRIMARY_SERVICE(UUID_1), |
| BT_GATT_CHARACTERISTIC(UUID_2, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, |
| read_mtu_validation_chrc, NULL, NULL), |
| }; |
| |
| static struct bt_gatt_service svc = { |
| .attrs = attrs, |
| .attr_count = ARRAY_SIZE(attrs), |
| }; |
| |
| static void find_the_chrc(struct bt_conn *conn, uint16_t *chrc_value_handle) |
| { |
| uint16_t svc_handle; |
| uint16_t svc_end_handle; |
| uint16_t chrc_end_handle; |
| |
| EXPECT_ZERO(bt_testlib_gatt_discover_primary(&svc_handle, &svc_end_handle, conn, UUID_1, 1, |
| 0xffff)); |
| |
| LOG_DBG("svc_handle: %u, svc_end_handle: %u", svc_handle, svc_end_handle); |
| |
| EXPECT_ZERO(bt_testlib_gatt_discover_characteristic(chrc_value_handle, &chrc_end_handle, |
| NULL, conn, UUID_2, (svc_handle + 1), |
| svc_end_handle)); |
| |
| LOG_DBG("chrc_value_handle: %u, chrc_end_handle: %u", *chrc_value_handle, chrc_end_handle); |
| } |
| |
| static void bs_sync_all_log(char *log_msg) |
| { |
| /* Everyone meets here. */ |
| bt_testlib_bs_sync_all(); |
| |
| if (get_device_nbr() == 0) { |
| LOG_WRN("Sync point: %s", log_msg); |
| } |
| |
| /* Everyone waits for d0 to finish logging. */ |
| bt_testlib_bs_sync_all(); |
| } |
| |
| static inline void bt_enable_quiet(void) |
| { |
| bt_testlib_log_level_set("bt_hci_core", LOG_LEVEL_ERR); |
| bt_testlib_log_level_set("bt_id", LOG_LEVEL_ERR); |
| |
| EXPECT_ZERO(bt_enable(NULL)); |
| |
| bt_testlib_log_level_set("bt_hci_core", LOG_LEVEL_INF); |
| bt_testlib_log_level_set("bt_id", LOG_LEVEL_INF); |
| } |
| |
| #define ITERATIONS 20 |
| #define READ_PARAMS_COUNT 20 |
| static struct bt_gatt_read_params closet[READ_PARAMS_COUNT]; |
| |
| static volatile int outstanding; |
| |
| static uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err, |
| struct bt_gatt_read_params *params, const void *data, |
| uint16_t length) |
| { |
| LOG_DBG("<------------------------- read done: err %d", err); |
| |
| outstanding--; |
| |
| return 0; |
| } |
| |
| void a_test_iteration(int i) |
| { |
| bool central = (get_device_nbr() == CENTRAL_DEVICE_NBR); |
| bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR); |
| bt_addr_le_t adva; |
| uint16_t chrc_value_handle = 0; |
| struct bt_conn *conn = NULL; |
| int err; |
| |
| LOG_DBG("############################## start iteration %d", i); |
| |
| bs_sync_all_log("Start iteration"); |
| |
| if (peripheral) { |
| EXPECT_ZERO(bt_set_name("peripheral")); |
| EXPECT_ZERO(bt_testlib_adv_conn(&conn, BT_ID_DEFAULT, bt_get_name())); |
| } |
| |
| if (central) { |
| EXPECT_ZERO(bt_testlib_scan_find_name(&adva, "peripheral")); |
| EXPECT_ZERO(bt_testlib_connect(&adva, &conn)); |
| |
| /* Establish EATT bearers. */ |
| EXPECT_ZERO(bt_testlib_secure(conn, BT_SECURITY_L2)); |
| |
| while (bt_eatt_count(conn) == 0) { |
| k_msleep(100); |
| }; |
| } |
| |
| bs_sync_all_log("Connected"); |
| |
| /* Perform discovery. */ |
| if (central) { |
| find_the_chrc(conn, &chrc_value_handle); |
| } else { |
| /* Peripheral will use handle 0. |
| * |
| * This will return a permission denied from the central's |
| * server. It doesn't matter as the only thing we want as the |
| * peripheral is to also be trying to fill the TX queue with ATT |
| * PDUs. |
| */ |
| } |
| |
| /* Test purpose: verify no allocated resource leaks when disconnecting |
| * abruptly with non-empty queues. |
| * |
| * Test procedure (in a nutshell): |
| * - open channels |
| * - queue up lots of ATT bufs from both sides |
| * - disconnect ACL |
| * - see if anything stalls or leaks |
| * |
| * Run this procedure more times than there are said resources. |
| */ |
| for (int p = 0; p < ARRAY_SIZE(closet); p++) { |
| memset(&closet[p], 0, sizeof(struct bt_gatt_read_params)); |
| |
| closet[p].handle_count = 1; |
| closet[p].single.handle = chrc_value_handle; |
| closet[p].func = gatt_read_cb; |
| |
| /* A disconnected channel (or ACL conn) can end up with |
| * gatt_read returning -ENOMEM instead of -ENOTCONN. sometimes. |
| */ |
| LOG_DBG("-------------------------> gatt_read %d", p); |
| err = bt_gatt_read(conn, &closet[p]); |
| switch (err) { |
| case -ENOMEM: |
| case -ENOTCONN: |
| LOG_DBG("not connected"); |
| break; |
| case 0: |
| outstanding++; |
| break; |
| default: |
| FAIL("unexpected error: %d\n", err); |
| break; |
| } |
| } |
| |
| bt_testlib_wait_disconnected(conn); |
| bt_testlib_conn_unref(&conn); |
| |
| k_msleep(1000); /* beauty rest */ |
| EXPECT_ZERO(outstanding); |
| |
| LOG_DBG("ended iteration %d", i); |
| } |
| |
| void the_test(void) |
| { |
| bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR); |
| |
| if (peripheral) { |
| EXPECT_ZERO(bt_gatt_service_register(&svc)); |
| } |
| |
| bt_enable_quiet(); |
| |
| for (int i = 0; i < ITERATIONS; i++) { |
| a_test_iteration(i); |
| } |
| |
| bs_sync_all_log("Test Complete"); |
| |
| PASS("Test complete\n"); |
| } |