/*
|
* Licensed to the Apache Software Foundation (ASF) under one
|
* or more contributor license agreements. See the NOTICE file
|
* distributed with this work for additional information
|
* regarding copyright ownership. The ASF licenses this file
|
* to you under the Apache License, Version 2.0 (the
|
* "License"); you may not use this file except in compliance
|
* with the License. You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing,
|
* software distributed under the License is distributed on an
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* KIND, either express or implied. See the License for the
|
* specific language governing permissions and limitations
|
* under the License.
|
*/
|
|
#include <string.h>
|
#include <errno.h>
|
#include <limits.h>
|
#include "testutil/testutil.h"
|
#include "nimble/ble.h"
|
#include "ble_hs_test.h"
|
#include "host/ble_uuid.h"
|
#include "ble_hs_test_util.h"
|
|
struct ble_gatt_read_test_attr {
|
uint16_t conn_handle;
|
uint16_t handle;
|
uint8_t value_len;
|
uint8_t value[BLE_ATT_ATTR_MAX_LEN];
|
};
|
|
#define BLE_GATT_READ_TEST_MAX_ATTRS 256
|
|
struct ble_gatt_read_test_attr
|
ble_gatt_read_test_attrs[BLE_GATT_READ_TEST_MAX_ATTRS];
|
int ble_gatt_read_test_num_attrs;
|
int ble_gatt_read_test_complete;
|
|
uint16_t ble_gatt_read_test_bad_conn_handle;
|
int ble_gatt_read_test_bad_status;
|
|
static void
|
ble_gatt_read_test_misc_init(void)
|
{
|
ble_hs_test_util_init();
|
ble_gatt_read_test_num_attrs = 0;
|
ble_gatt_read_test_complete = 0;
|
ble_gatt_read_test_bad_conn_handle = 0;
|
ble_gatt_read_test_bad_status = 0;
|
|
memset(&ble_gatt_read_test_attrs[0], 0,
|
sizeof ble_gatt_read_test_attrs[0]);
|
}
|
|
static int
|
ble_gatt_read_test_cb(uint16_t conn_handle, const struct ble_gatt_error *error,
|
struct ble_gatt_attr *attr, void *arg)
|
{
|
struct ble_gatt_read_test_attr *dst;
|
int *stop_after;
|
int rc;
|
|
stop_after = arg;
|
|
TEST_ASSERT_FATAL(error != NULL);
|
|
if (error->status != 0) {
|
ble_gatt_read_test_bad_conn_handle = conn_handle;
|
ble_gatt_read_test_bad_status = error->status;
|
ble_gatt_read_test_complete = 1;
|
return 0;
|
}
|
|
if (attr == NULL) {
|
ble_gatt_read_test_complete = 1;
|
return 0;
|
}
|
|
TEST_ASSERT_FATAL(ble_gatt_read_test_num_attrs <
|
BLE_GATT_READ_TEST_MAX_ATTRS);
|
dst = ble_gatt_read_test_attrs + ble_gatt_read_test_num_attrs++;
|
|
TEST_ASSERT_FATAL(OS_MBUF_PKTLEN(attr->om) <= sizeof dst->value);
|
|
dst->conn_handle = conn_handle;
|
dst->handle = attr->handle;
|
dst->value_len = OS_MBUF_PKTLEN(attr->om);
|
rc = os_mbuf_copydata(attr->om, 0, OS_MBUF_PKTLEN(attr->om), dst->value);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
if (stop_after != NULL && *stop_after > 0) {
|
(*stop_after)--;
|
if (*stop_after == 0) {
|
ble_gatt_read_test_complete = 1;
|
return 1;
|
}
|
} else {
|
ble_gatt_read_test_complete = 1;
|
}
|
|
return 0;
|
}
|
|
static int
|
ble_gatt_read_test_long_cb(uint16_t conn_handle,
|
const struct ble_gatt_error *error,
|
struct ble_gatt_attr *attr, void *arg)
|
{
|
struct ble_gatt_read_test_attr *dst;
|
int *reads_left;
|
int rc;
|
|
reads_left = arg;
|
|
TEST_ASSERT_FATAL(error != NULL);
|
|
if (error->status != 0) {
|
ble_gatt_read_test_bad_conn_handle = conn_handle;
|
ble_gatt_read_test_bad_status = error->status;
|
ble_gatt_read_test_complete = 1;
|
return 0;
|
}
|
|
if (attr == NULL) {
|
ble_gatt_read_test_complete = 1;
|
return 0;
|
}
|
|
dst = ble_gatt_read_test_attrs + 0;
|
|
TEST_ASSERT_FATAL(OS_MBUF_PKTLEN(attr->om) <=
|
dst->value_len + sizeof dst->value);
|
TEST_ASSERT(attr->offset == dst->value_len);
|
|
if (attr->offset == 0) {
|
dst->conn_handle = conn_handle;
|
dst->handle = attr->handle;
|
} else {
|
TEST_ASSERT(conn_handle == dst->conn_handle);
|
TEST_ASSERT(attr->handle == dst->handle);
|
}
|
rc = os_mbuf_copydata(attr->om, 0, OS_MBUF_PKTLEN(attr->om),
|
dst->value + dst->value_len);
|
TEST_ASSERT_FATAL(rc == 0);
|
dst->value_len += OS_MBUF_PKTLEN(attr->om);
|
|
if (reads_left != NULL && *reads_left > 0) {
|
(*reads_left)--;
|
if (*reads_left == 0) {
|
ble_gatt_read_test_complete = 1;
|
return 1;
|
}
|
}
|
|
return 0;
|
}
|
|
static void
|
ble_gatt_read_test_misc_rx_rsp_good_raw(uint16_t conn_handle,
|
uint8_t att_op,
|
const void *data, int data_len)
|
{
|
uint8_t buf[1024];
|
int rc;
|
|
TEST_ASSERT_FATAL(data_len <= sizeof buf);
|
|
/* Send the pending ATT Read Request. */
|
|
buf[0] = att_op;
|
memcpy(buf + 1, data, data_len);
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
|
buf, 1 + data_len);
|
TEST_ASSERT(rc == 0);
|
}
|
|
static void
|
ble_gatt_read_test_misc_rx_rsp_good(uint16_t conn_handle,
|
struct ble_hs_test_util_flat_attr *attr)
|
{
|
ble_gatt_read_test_misc_rx_rsp_good_raw(conn_handle, BLE_ATT_OP_READ_RSP,
|
attr->value,
|
attr->value_len);
|
}
|
|
static void
|
ble_gatt_read_test_misc_rx_rsp_bad(uint16_t conn_handle,
|
uint8_t att_error, uint16_t err_handle)
|
{
|
/* Send the pending ATT Read Request. */
|
|
ble_hs_test_util_rx_att_err_rsp(conn_handle, BLE_ATT_OP_READ_REQ,
|
att_error, err_handle);
|
}
|
|
static int
|
ble_gatt_read_test_misc_uuid_rx_rsp_good(
|
uint16_t conn_handle, struct ble_hs_test_util_flat_attr *attrs)
|
{
|
struct ble_att_read_type_rsp rsp;
|
uint8_t buf[1024];
|
int prev_len;
|
int off;
|
int rc;
|
int i;
|
|
if (ble_gatt_read_test_complete || attrs[0].handle == 0) {
|
return 0;
|
}
|
|
/* Send the pending ATT Read By Type Request. */
|
|
rsp.batp_length = 2 + attrs[0].value_len;
|
ble_att_read_type_rsp_write(buf, sizeof buf, &rsp);
|
|
prev_len = 0;
|
off = BLE_ATT_READ_TYPE_RSP_BASE_SZ;
|
for (i = 0; attrs[i].handle != 0; i++) {
|
if (prev_len != 0 && prev_len != attrs[i].value_len) {
|
break;
|
}
|
prev_len = attrs[i].value_len;
|
|
put_le16(buf + off, attrs[i].handle);
|
off += 2;
|
|
memcpy(buf + off, attrs[i].value, attrs[i].value_len);
|
off += attrs[i].value_len;
|
}
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
|
buf, off);
|
TEST_ASSERT(rc == 0);
|
|
return i;
|
}
|
|
static void
|
ble_gatt_read_test_misc_verify_good(struct ble_hs_test_util_flat_attr *attr)
|
{
|
int rc;
|
|
ble_gatt_read_test_misc_init();
|
ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
|
/* Exchange MTU: We need plus 1 for the read response opcode */
|
ble_hs_test_util_set_att_mtu(2, attr->value_len + 1);
|
|
rc = ble_gattc_read(2, attr->handle, ble_gatt_read_test_cb, NULL);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
ble_gatt_read_test_misc_rx_rsp_good(2, attr);
|
|
TEST_ASSERT(ble_gatt_read_test_num_attrs == 1);
|
TEST_ASSERT(ble_gatt_read_test_attrs[0].conn_handle == 2);
|
TEST_ASSERT(ble_gatt_read_test_attrs[0].handle == attr->handle);
|
TEST_ASSERT(ble_gatt_read_test_attrs[0].value_len == attr->value_len);
|
TEST_ASSERT(memcmp(ble_gatt_read_test_attrs[0].value, attr->value,
|
attr->value_len) == 0);
|
}
|
|
static void
|
ble_gatt_read_test_misc_verify_bad(uint8_t att_status,
|
struct ble_hs_test_util_flat_attr *attr)
|
{
|
int rc;
|
|
ble_gatt_read_test_misc_init();
|
ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
|
rc = ble_gattc_read(2, attr->handle, ble_gatt_read_test_cb, NULL);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
ble_gatt_read_test_misc_rx_rsp_bad(2, att_status, attr->handle);
|
|
TEST_ASSERT(ble_gatt_read_test_num_attrs == 0);
|
TEST_ASSERT(ble_gatt_read_test_bad_conn_handle == 2);
|
TEST_ASSERT(ble_gatt_read_test_bad_status ==
|
BLE_HS_ERR_ATT_BASE + att_status);
|
TEST_ASSERT(!ble_gattc_any_jobs());
|
}
|
|
static void
|
ble_gatt_read_test_misc_uuid_verify_good(
|
uint16_t start_handle, uint16_t end_handle, const ble_uuid_t *uuid,
|
int stop_after, struct ble_hs_test_util_flat_attr *attrs)
|
{
|
int num_read;
|
int idx;
|
int rc;
|
int i;
|
|
ble_gatt_read_test_misc_init();
|
ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
|
rc = ble_gattc_read_by_uuid(2, start_handle, end_handle, uuid,
|
ble_gatt_read_test_cb, &stop_after);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
idx = 0;
|
while (1) {
|
num_read = ble_gatt_read_test_misc_uuid_rx_rsp_good(2, attrs + idx);
|
if (num_read == 0) {
|
ble_hs_test_util_rx_att_err_rsp(2, BLE_ATT_OP_READ_TYPE_REQ,
|
BLE_ATT_ERR_ATTR_NOT_FOUND,
|
start_handle);
|
break;
|
}
|
|
idx += num_read;
|
}
|
|
TEST_ASSERT(ble_gatt_read_test_complete);
|
TEST_ASSERT(idx == ble_gatt_read_test_num_attrs);
|
|
for (i = 0; i < idx; i++) {
|
TEST_ASSERT(ble_gatt_read_test_attrs[i].conn_handle == 2);
|
TEST_ASSERT(ble_gatt_read_test_attrs[i].handle == attrs[i].handle);
|
TEST_ASSERT(ble_gatt_read_test_attrs[i].value_len ==
|
attrs[i].value_len);
|
TEST_ASSERT(memcmp(ble_gatt_read_test_attrs[i].value, attrs[i].value,
|
attrs[i].value_len) == 0);
|
}
|
TEST_ASSERT(!ble_gattc_any_jobs());
|
}
|
|
static void
|
ble_gatt_read_test_misc_long_verify_good(
|
int max_reads, struct ble_hs_test_util_flat_attr *attr)
|
{
|
int reads_left;
|
int chunk_sz;
|
int rem_len;
|
int att_op;
|
uint16_t offset = 0;
|
int off;
|
int rc;
|
|
ble_gatt_read_test_misc_init();
|
ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
|
if (max_reads == 0) {
|
max_reads = INT_MAX;
|
}
|
reads_left = max_reads;
|
rc = ble_gattc_read_long(2, attr->handle, offset,
|
ble_gatt_read_test_long_cb, &reads_left);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
off = 0;
|
rem_len = attr->value_len;
|
do {
|
if (rem_len > BLE_ATT_MTU_DFLT - 1) {
|
chunk_sz = BLE_ATT_MTU_DFLT - 1;
|
} else {
|
chunk_sz = rem_len;
|
}
|
if (off == 0) {
|
att_op = BLE_ATT_OP_READ_RSP;
|
} else {
|
att_op = BLE_ATT_OP_READ_BLOB_RSP;
|
}
|
ble_gatt_read_test_misc_rx_rsp_good_raw(2, att_op,
|
attr->value + off, chunk_sz);
|
rem_len -= chunk_sz;
|
off += chunk_sz;
|
} while (rem_len > 0 && reads_left > 0);
|
|
TEST_ASSERT(ble_gatt_read_test_complete);
|
TEST_ASSERT(!ble_gattc_any_jobs());
|
TEST_ASSERT(ble_gatt_read_test_attrs[0].conn_handle == 2);
|
TEST_ASSERT(ble_gatt_read_test_attrs[0].handle == attr->handle);
|
if (reads_left > 0) {
|
TEST_ASSERT(ble_gatt_read_test_attrs[0].value_len == attr->value_len);
|
}
|
TEST_ASSERT(memcmp(ble_gatt_read_test_attrs[0].value, attr->value,
|
ble_gatt_read_test_attrs[0].value_len) == 0);
|
}
|
|
static void
|
ble_gatt_read_test_misc_long_verify_bad(
|
uint8_t att_status, struct ble_hs_test_util_flat_attr *attr)
|
{
|
uint16_t offset = 0;
|
int rc;
|
|
ble_gatt_read_test_misc_init();
|
ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
|
rc = ble_gattc_read_long(2, attr->handle, offset,
|
ble_gatt_read_test_long_cb, NULL);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
ble_gatt_read_test_misc_rx_rsp_bad(2, att_status, attr->handle);
|
|
TEST_ASSERT(ble_gatt_read_test_num_attrs == 0);
|
TEST_ASSERT(ble_gatt_read_test_bad_conn_handle == 2);
|
TEST_ASSERT(ble_gatt_read_test_bad_status ==
|
BLE_HS_ERR_ATT_BASE + att_status);
|
TEST_ASSERT(!ble_gattc_any_jobs());
|
}
|
|
static int
|
ble_gatt_read_test_misc_extract_handles(
|
struct ble_hs_test_util_flat_attr *attrs, uint16_t *handles)
|
{
|
int i;
|
|
for (i = 0; attrs[i].handle != 0; i++) {
|
handles[i] = attrs[i].handle;
|
}
|
return i;
|
}
|
|
static void
|
ble_gatt_read_test_misc_mult_verify_good(
|
struct ble_hs_test_util_flat_attr *attrs)
|
{
|
uint8_t expected_value[512];
|
uint16_t handles[256];
|
int num_attrs;
|
int chunk_sz;
|
int off;
|
int rc;
|
int i;
|
|
ble_gatt_read_test_misc_init();
|
ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
|
num_attrs = ble_gatt_read_test_misc_extract_handles(attrs, handles);
|
|
off = 0;
|
for (i = 0; i < num_attrs; i++) {
|
if (attrs[i].value_len > BLE_ATT_MTU_DFLT - 1 - off) {
|
chunk_sz = BLE_ATT_MTU_DFLT - 1 - off;
|
} else {
|
chunk_sz = attrs[i].value_len;
|
}
|
|
if (chunk_sz > 0) {
|
memcpy(expected_value + off, attrs[i].value, chunk_sz);
|
off += chunk_sz;
|
}
|
}
|
|
rc = ble_gattc_read_mult(2, handles, num_attrs,
|
ble_gatt_read_test_cb, NULL);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
ble_gatt_read_test_misc_rx_rsp_good_raw(2, BLE_ATT_OP_READ_MULT_RSP,
|
expected_value, off);
|
|
TEST_ASSERT(ble_gatt_read_test_complete);
|
TEST_ASSERT(!ble_gattc_any_jobs());
|
TEST_ASSERT(ble_gatt_read_test_attrs[0].conn_handle == 2);
|
TEST_ASSERT(ble_gatt_read_test_attrs[0].value_len == off);
|
TEST_ASSERT(memcmp(ble_gatt_read_test_attrs[0].value, expected_value,
|
off) == 0);
|
}
|
|
static void
|
ble_gatt_read_test_misc_mult_verify_bad(
|
uint8_t att_status, uint16_t err_handle,
|
struct ble_hs_test_util_flat_attr *attrs)
|
{
|
uint16_t handles[256];
|
int num_attrs;
|
int rc;
|
|
ble_gatt_read_test_misc_init();
|
ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
|
num_attrs = ble_gatt_read_test_misc_extract_handles(attrs, handles);
|
|
rc = ble_gattc_read_mult(2, handles, num_attrs,
|
ble_gatt_read_test_cb, NULL);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
ble_gatt_read_test_misc_rx_rsp_bad(2, att_status, err_handle);
|
|
TEST_ASSERT(ble_gatt_read_test_num_attrs == 0);
|
TEST_ASSERT(ble_gatt_read_test_bad_conn_handle == 2);
|
TEST_ASSERT(ble_gatt_read_test_bad_status ==
|
BLE_HS_ERR_ATT_BASE + att_status);
|
TEST_ASSERT(!ble_gattc_any_jobs());
|
}
|
|
TEST_CASE_SELF(ble_gatt_read_test_by_handle)
|
{
|
/* Read a seven-byte attribute. */
|
ble_gatt_read_test_misc_verify_good(
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 43,
|
.value = { 1,2,3,4,5,6,7 },
|
.value_len = 7
|
} });
|
|
/* Read a one-byte attribute. */
|
ble_gatt_read_test_misc_verify_good(
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 0x5432,
|
.value = { 0xff },
|
.value_len = 1
|
} });
|
|
/* Read a 200-byte attribute. */
|
ble_gatt_read_test_misc_verify_good(
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 815,
|
.value = { 0 },
|
.value_len = 200,
|
} });
|
|
/* Fail due to attribute not found. */
|
ble_gatt_read_test_misc_verify_bad(BLE_ATT_ERR_ATTR_NOT_FOUND,
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 719,
|
.value = { 1,2,3,4,5,6,7 },
|
.value_len = 7
|
} });
|
|
/* Fail due to invalid PDU. */
|
ble_gatt_read_test_misc_verify_bad(BLE_ATT_ERR_INVALID_PDU,
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 65,
|
.value = { 0xfa, 0x4c },
|
.value_len = 2
|
} });
|
|
ble_hs_test_util_assert_mbufs_freed(NULL);
|
}
|
|
TEST_CASE_SELF(ble_gatt_read_test_by_uuid)
|
{
|
/* Read a single seven-byte attribute. */
|
ble_gatt_read_test_misc_uuid_verify_good(1, 100, BLE_UUID16_DECLARE(0x1234), 0,
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 43,
|
.value = { 1,2,3,4,5,6,7 },
|
.value_len = 7
|
}, {
|
0,
|
} });
|
|
/* Read two seven-byte attributes; one response. */
|
ble_gatt_read_test_misc_uuid_verify_good(1, 100, BLE_UUID16_DECLARE(0x1234), 0,
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 43,
|
.value = { 1,2,3,4,5,6,7 },
|
.value_len = 7
|
}, {
|
.handle = 44,
|
.value = { 2,3,4,5,6,7,8 },
|
.value_len = 7
|
}, {
|
0,
|
} });
|
|
/* Read two attributes; two responses. */
|
ble_gatt_read_test_misc_uuid_verify_good(1, 100, BLE_UUID16_DECLARE(0x1234), 0,
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 43,
|
.value = { 1,2,3,4,5,6,7 },
|
.value_len = 7
|
}, {
|
.handle = 44,
|
.value = { 2,3,4 },
|
.value_len = 3
|
}, {
|
0,
|
} });
|
|
/* Stop after three reads. */
|
ble_gatt_read_test_misc_uuid_verify_good(1, 100, BLE_UUID16_DECLARE(0x1234), 3,
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 43,
|
.value = { 1,2,3,4,5,6,7 },
|
.value_len = 7
|
}, {
|
.handle = 44,
|
.value = { 2,3,4 },
|
.value_len = 3
|
}, {
|
.handle = 45,
|
.value = { 2,3,4 },
|
.value_len = 3
|
}, {
|
.handle = 46,
|
.value = { 3,4,5,6 },
|
.value_len = 4
|
}, {
|
.handle = 47,
|
.value = { 2,3,4 },
|
.value_len = 3
|
}, {
|
0,
|
} });
|
|
ble_hs_test_util_assert_mbufs_freed(NULL);
|
}
|
|
TEST_CASE_SELF(ble_gatt_read_test_long)
|
{
|
uint8_t data512[512];
|
int i;
|
|
for (i = 0; i < sizeof data512; i++) {
|
data512[i] = i;
|
}
|
|
/* Read a seven-byte attribute. */
|
ble_gatt_read_test_misc_long_verify_good(0,
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 43,
|
.value = { 1,2,3,4,5,6,7 },
|
.value_len = 7
|
} });
|
|
/* Read a zero-byte attribute. */
|
ble_gatt_read_test_misc_long_verify_good(0,
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 43,
|
.value = { 0 },
|
.value_len = 0
|
} });
|
|
/* Read a 60-byte attribute; three requests. */
|
ble_gatt_read_test_misc_long_verify_good(0,
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 34,
|
.value = {
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60
|
},
|
.value_len = 60
|
} });
|
|
/* Stop after two reads. */
|
ble_gatt_read_test_misc_long_verify_good(2,
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 34,
|
.value = {
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60
|
},
|
.value_len = 60
|
} });
|
|
/* Fail due to attribute not found. */
|
ble_gatt_read_test_misc_long_verify_bad(BLE_ATT_ERR_ATTR_NOT_FOUND,
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 719,
|
.value = { 1, 2, 3, 4, 5, 6, 7 },
|
.value_len = 7
|
} });
|
|
ble_hs_test_util_assert_mbufs_freed(NULL);
|
}
|
|
TEST_CASE_SELF(ble_gatt_read_test_mult)
|
{
|
uint8_t data512[512];
|
int i;
|
|
for (i = 0; i < sizeof data512; i++) {
|
data512[i] = i;
|
}
|
|
/* Read one attribute. */
|
ble_gatt_read_test_misc_mult_verify_good(
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 43,
|
.value = { 0, 1, 2, 3, 4, 5, 6, 7 },
|
.value_len = 7
|
}, {
|
0
|
} });
|
|
/* Read two attributes. */
|
ble_gatt_read_test_misc_mult_verify_good(
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 43,
|
.value = { 0, 1, 2, 3, 4, 5, 6, 7 },
|
.value_len = 7,
|
}, {
|
.handle = 44,
|
.value = { 8, 9, 10, 11 },
|
.value_len = 4,
|
}, {
|
0
|
} });
|
|
/* Read two attributes (swap order). */
|
ble_gatt_read_test_misc_mult_verify_good(
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 44,
|
.value = { 8, 9, 10, 11 },
|
.value_len = 4,
|
}, {
|
.handle = 43,
|
.value = { 0, 1, 2, 3, 4, 5, 6, 7 },
|
.value_len = 7,
|
}, {
|
0
|
} });
|
|
/* Read five attributes. */
|
ble_gatt_read_test_misc_mult_verify_good(
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 43,
|
.value = { 0, 1, 2, 3, 4, 5, 6, 7 },
|
.value_len = 7,
|
}, {
|
.handle = 44,
|
.value = { 8, 9, 10, 11 },
|
.value_len = 4,
|
}, {
|
.handle = 145,
|
.value = { 12, 13 },
|
.value_len = 2,
|
}, {
|
.handle = 191,
|
.value = { 14, 15, 16 },
|
.value_len = 3,
|
}, {
|
.handle = 352,
|
.value = { 17, 18, 19, 20 },
|
.value_len = 4,
|
}, {
|
0
|
} });
|
|
/* Fail due to attribute not found. */
|
ble_gatt_read_test_misc_mult_verify_bad(BLE_ATT_ERR_ATTR_NOT_FOUND, 719,
|
(struct ble_hs_test_util_flat_attr[]) { {
|
.handle = 719,
|
.value = { 1,2,3,4,5,6,7 },
|
.value_len = 7
|
}, {
|
0
|
} });
|
|
ble_hs_test_util_assert_mbufs_freed(NULL);
|
}
|
|
TEST_CASE_SELF(ble_gatt_read_test_concurrent)
|
{
|
int rc;
|
int i;
|
|
ble_gatt_read_test_misc_init();
|
ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
|
/***
|
* Perform three concurrent reads. Assert that each response is correctly
|
* matched up with its corresponding GATT procedure.
|
*/
|
|
struct ble_hs_test_util_flat_attr attrs[3] = {
|
{
|
.handle = 1,
|
.offset = 0,
|
.value_len = 3,
|
.value = { 1, 2, 3 },
|
},
|
{
|
.handle = 2,
|
.offset = 0,
|
.value_len = 4,
|
.value = { 2, 3, 4, 5 },
|
},
|
{
|
.handle = 3,
|
.offset = 0,
|
.value_len = 5,
|
.value = { 3, 4, 5, 6, 7 },
|
},
|
};
|
|
rc = ble_gattc_read(2, attrs[0].handle, ble_gatt_read_test_cb, NULL);
|
TEST_ASSERT_FATAL(rc == 0);
|
rc = ble_gattc_read(2, attrs[1].handle, ble_gatt_read_test_cb, NULL);
|
TEST_ASSERT_FATAL(rc == 0);
|
rc = ble_gattc_read(2, attrs[2].handle, ble_gatt_read_test_cb, NULL);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
ble_gatt_read_test_misc_rx_rsp_good(2, attrs + 0);
|
ble_gatt_read_test_misc_rx_rsp_good(2, attrs + 1);
|
ble_gatt_read_test_misc_rx_rsp_good(2, attrs + 2);
|
|
TEST_ASSERT(ble_gatt_read_test_num_attrs == 3);
|
|
for (i = 0; i < 3; i++) {
|
TEST_ASSERT(ble_gatt_read_test_attrs[i].handle == attrs[i].handle);
|
TEST_ASSERT(ble_gatt_read_test_attrs[i].value_len ==
|
attrs[i].value_len);
|
TEST_ASSERT(memcmp(ble_gatt_read_test_attrs[i].value, attrs[i].value,
|
attrs[i].value_len) == 0);
|
}
|
|
ble_hs_test_util_assert_mbufs_freed(NULL);
|
}
|
|
TEST_CASE_SELF(ble_gatt_read_test_long_oom)
|
{
|
static const struct ble_hs_test_util_flat_attr attr = {
|
.handle = 34,
|
.value = {
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60
|
},
|
.value_len = 60,
|
};
|
|
struct os_mbuf *oms;
|
int32_t ticks_until;
|
int reads_left;
|
int chunk_sz;
|
uint16_t offset = 0;
|
int off;
|
int rc;
|
|
ble_gatt_read_test_misc_init();
|
ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
|
/* Initiate a read long procedure. */
|
off = 0;
|
reads_left = 0;
|
rc = ble_gattc_read_long(2, attr.handle, offset, ble_gatt_read_test_long_cb,
|
&reads_left);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
/* Exhaust the msys pool. Leave one mbuf for the forthcoming response. */
|
oms = ble_hs_test_util_mbuf_alloc_all_but(1);
|
chunk_sz = ble_att_mtu(2) - BLE_ATT_READ_RSP_BASE_SZ;
|
ble_gatt_read_test_misc_rx_rsp_good_raw(2, BLE_ATT_OP_READ_RSP,
|
attr.value + off, chunk_sz);
|
off += chunk_sz;
|
|
/* Ensure no follow-up request got sent. It should not have gotten sent
|
* due to mbuf exhaustion.
|
*/
|
ble_hs_test_util_prev_tx_queue_clear();
|
TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue_pullup() == NULL);
|
|
/* Verify that we will resume the stalled GATT procedure in one second. */
|
ticks_until = ble_gattc_timer();
|
TEST_ASSERT(ticks_until == os_time_ms_to_ticks32(MYNEWT_VAL(BLE_GATT_RESUME_RATE)));
|
|
/* Verify the procedure proceeds after mbufs become available. */
|
rc = os_mbuf_free_chain(oms);
|
TEST_ASSERT_FATAL(rc == 0);
|
os_time_advance(ticks_until);
|
ble_gattc_timer();
|
|
/* Exhaust the msys pool. Leave one mbuf for the forthcoming response. */
|
oms = ble_hs_test_util_mbuf_alloc_all_but(1);
|
chunk_sz = ble_att_mtu(2) - BLE_ATT_READ_RSP_BASE_SZ;
|
ble_gatt_read_test_misc_rx_rsp_good_raw(2, BLE_ATT_OP_READ_RSP,
|
attr.value + off, chunk_sz);
|
off += chunk_sz;
|
|
/* Ensure no follow-up request got sent. It should not have gotten sent
|
* due to mbuf exhaustion.
|
*/
|
ble_hs_test_util_prev_tx_queue_clear();
|
TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue_pullup() == NULL);
|
|
/* Verify that we will resume the stalled GATT procedure in one second. */
|
ticks_until = ble_gattc_timer();
|
TEST_ASSERT(ticks_until == os_time_ms_to_ticks32(MYNEWT_VAL(BLE_GATT_RESUME_RATE)));
|
|
/* Verify that procedure completes when mbufs are available. */
|
rc = os_mbuf_free_chain(oms);
|
TEST_ASSERT_FATAL(rc == 0);
|
os_time_advance(ticks_until);
|
ble_gattc_timer();
|
|
chunk_sz = attr.value_len - off;
|
ble_gatt_read_test_misc_rx_rsp_good_raw(2, BLE_ATT_OP_READ_RSP,
|
attr.value + off, chunk_sz);
|
off += chunk_sz;
|
|
TEST_ASSERT(ble_gatt_read_test_complete);
|
TEST_ASSERT(!ble_gattc_any_jobs());
|
TEST_ASSERT(ble_gatt_read_test_attrs[0].conn_handle == 2);
|
TEST_ASSERT(ble_gatt_read_test_attrs[0].handle == attr.handle);
|
TEST_ASSERT(ble_gatt_read_test_attrs[0].value_len == attr.value_len);
|
TEST_ASSERT(memcmp(ble_gatt_read_test_attrs[0].value, attr.value,
|
ble_gatt_read_test_attrs[0].value_len) == 0);
|
|
ble_hs_test_util_assert_mbufs_freed(NULL);
|
}
|
|
TEST_SUITE(ble_gatt_read_test_suite)
|
{
|
ble_gatt_read_test_by_handle();
|
ble_gatt_read_test_by_uuid();
|
ble_gatt_read_test_long();
|
ble_gatt_read_test_mult();
|
ble_gatt_read_test_concurrent();
|
ble_gatt_read_test_long_oom();
|
}
|