blob: 83a1b3afe76b41622a8ddf543f2d090a83874450 [file] [log] [blame]
/* 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)
{
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(bt_testlib_gatt_long_read(&attr_value_buf, NULL, 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");
}