-
Notifications
You must be signed in to change notification settings - Fork 6.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bluetooth: Host: Fix GATT Long Read for EATT
When EATT is established, the value returned from `bt_att_get_mtu` is not useful to determine the ATT_MTU that applies to a ATT response. This is because `bt_att_get_mtu` may return the value for a different bearer than the request is serviced on. To fix this, the params struct for the GATT read operation is given a new field that will record the ATT_MTU that applies to this ATT operation. This value is then used to determine if the GATT long read operation is concluded. Fixes: #61741 Signed-off-by: Aleksander Wasaznik <[email protected]>
- Loading branch information
1 parent
89db200
commit 3581a15
Showing
30 changed files
with
1,593 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
cmake_minimum_required(VERSION 3.20.0) | ||
|
||
find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) | ||
project(app) | ||
|
||
target_sources(app PRIVATE | ||
testlib/bs_main.c | ||
testlib/adv.c | ||
testlib/connect.c | ||
testlib/scan.c | ||
testlib/security.c | ||
testlib/att_read.c | ||
testlib/att_write.c | ||
testlib/bs_sync.c | ||
testlib/conn_wait.c | ||
main.c | ||
) | ||
|
||
zephyr_include_directories( | ||
${BSIM_COMPONENTS_PATH}/libPhyComv1/src/ | ||
${BSIM_COMPONENTS_PATH}/libUtilv1/src/ | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -eu | ||
dotslash="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" | ||
bin_dir="${BSIM_OUT_PATH}/bin" | ||
BOARD="${BOARD:-nrf52_bsim}" | ||
|
||
cd "${dotslash}" | ||
|
||
compile_path="${bin_dir}/bs_${BOARD}_" | ||
compile_path+="$(realpath --relative-to "$(west topdir)"/zephyr prj.conf | tr /. _)" | ||
|
||
west build -b nrf52_bsim | ||
cp -v build/zephyr/zephyr.exe "${compile_path}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
/* 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 "testlib/bs_macro.h" | ||
#include "testlib/bs_sync.h" | ||
#include "testlib/conn_ref.h" | ||
#include "testlib/conn_wait.h" | ||
#include "testlib/connect.h" | ||
#include "testlib/log_utils.h" | ||
#include "testlib/scan.h" | ||
#include "testlib/security.h" | ||
|
||
#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) | ||
{ | ||
ssize_t read_len; | ||
|
||
LOG_INF("Server side buf_len %u", buf_len); | ||
|
||
/* Note: We assume `buf_len` is equal to the usable payload | ||
* capacity of the response PDU. I.e. `(ATT_MTU - 1)` for | ||
* BT_ATT_OP_READ_RSP and BT_ATT_OP_READ_BLOB_RSP. | ||
*/ | ||
|
||
/* Send back a full PDU on the first read (on offset 0). Then an | ||
* not full one for the second read to conlude the long read.. | ||
*/ | ||
read_len = buf_len; | ||
if (offset > 0) { | ||
__ASSERT_NO_MSG(read_len > 0); | ||
/* The second PDU is one-less-than-full to test for off | ||
* by one errors. | ||
*/ | ||
read_len -= 1; | ||
} | ||
|
||
/* If the ATT_MTU is too large, sending a one-less-than-full | ||
* response would exeed the max attribute length limit. | ||
*/ | ||
__ASSERT(buf_len < (BT_ATT_MAX_ATTRIBUTE_LEN / 2), | ||
"The EATT buffer is too large for this test."); | ||
|
||
/* Ensure the padding bytes (that are not overwritten later in | ||
* this function) are initialized. | ||
*/ | ||
memset(buf, 0, read_len); | ||
|
||
/* Echo back the requested read size in the first two bytes of | ||
* each read. | ||
*/ | ||
__ASSERT_NO_MSG(read_len >= 2); | ||
sys_put_le16(read_len, buf); | ||
|
||
return read_len; | ||
} | ||
|
||
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_INF("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_INF("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); | ||
} | ||
|
||
static void test_long_read(enum bt_att_chan_opt bearer, uint16_t chrc_value_handle, | ||
struct bt_conn *conn) | ||
{ | ||
bool central = (get_device_nbr() == CENTRAL_DEVICE_NBR); | ||
|
||
if (central) { | ||
size_t read_count; | ||
|
||
NET_BUF_SIMPLE_DEFINE(attr_value_buf, BT_ATT_MAX_ATTRIBUTE_LEN); | ||
|
||
/* Perform the whole long read operation. */ | ||
EXPECT_ZERO(btt_gatt_long_read(&attr_value_buf, NULL, conn, bearer, | ||
chrc_value_handle, 0)); | ||
|
||
/* Parse the read attribute value to verify the | ||
* integrity of the transfer. | ||
* | ||
* Each response starts with the length of the whole | ||
* response and the rest is zero-padded. | ||
*/ | ||
for (read_count = 0; attr_value_buf.len; read_count++) { | ||
uint16_t encoded_len; | ||
uint16_t padding_size; | ||
|
||
LOG_INF("Verifying read %u", read_count); | ||
|
||
__ASSERT(attr_value_buf.len >= sizeof(encoded_len), | ||
"Incomplete encoded length"); | ||
encoded_len = net_buf_simple_pull_le16(&attr_value_buf); | ||
|
||
padding_size = (encoded_len - sizeof(uint16_t)); | ||
LOG_INF("Padding size %u", padding_size); | ||
|
||
/* Check and discard padding. */ | ||
for (uint16_t i = 0; i < padding_size; i++) { | ||
__ASSERT(attr_value_buf.len, "Unexpected end of buffer"); | ||
__ASSERT(net_buf_simple_pull_u8(&attr_value_buf) == 0, | ||
"Expected a padding byte at %u", i); | ||
} | ||
} | ||
LOG_INF("Verified %u reads", read_count); | ||
__ASSERT(read_count > 1, "Expected at least two reads"); | ||
} | ||
} | ||
|
||
void the_test(void) | ||
{ | ||
bool central = (get_device_nbr() == CENTRAL_DEVICE_NBR); | ||
bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR); | ||
bt_addr_le_t adva; | ||
struct bt_conn *conn = NULL; | ||
uint16_t chrc_value_handle = 0; | ||
|
||
if (peripheral) { | ||
EXPECT_ZERO(bt_gatt_service_register(&svc)); | ||
} | ||
|
||
bt_enable_quiet(); | ||
|
||
if (peripheral) { | ||
EXPECT_ZERO(bt_set_name("peripheral")); | ||
EXPECT_ZERO(bt_testlib_adv_conn( | ||
&conn, BT_ID_DEFAULT, | ||
(BT_LE_ADV_OPT_USE_NAME | BT_LE_ADV_OPT_FORCE_NAME_IN_AD))); | ||
} | ||
|
||
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); | ||
} | ||
|
||
bs_sync_all_log("Testing UATT"); | ||
test_long_read(BT_ATT_CHAN_OPT_UNENHANCED_ONLY, chrc_value_handle, conn); | ||
|
||
bs_sync_all_log("Testing EATT"); | ||
test_long_read(BT_ATT_CHAN_OPT_ENHANCED_ONLY, chrc_value_handle, conn); | ||
|
||
bs_sync_all_log("Test Complete"); | ||
|
||
PASS("Test complete\n"); | ||
} |
Oops, something went wrong.