/*
|
* 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 "testutil/testutil.h"
|
#include "nimble/ble.h"
|
#include "ble_hs_test.h"
|
#include "host/ble_uuid.h"
|
#include "ble_hs_test_util.h"
|
|
static struct ble_gatt_svc ble_gatt_find_s_test_svcs[256];
|
static int ble_gatt_find_s_test_num_svcs;
|
static int ble_gatt_find_s_test_proc_complete;
|
|
struct ble_gatt_find_s_test_entry {
|
uint16_t inc_handle; /* 0 indicates no more entries. */
|
uint16_t start_handle;
|
uint16_t end_handle;
|
const ble_uuid_t *uuid;
|
};
|
|
static void
|
ble_gatt_find_s_test_misc_init(void)
|
{
|
ble_hs_test_util_init();
|
ble_gatt_find_s_test_num_svcs = 0;
|
ble_gatt_find_s_test_proc_complete = 0;
|
}
|
|
static int
|
ble_gatt_find_s_test_misc_cb(uint16_t conn_handle,
|
const struct ble_gatt_error *error,
|
const struct ble_gatt_svc *service,
|
void *arg)
|
{
|
TEST_ASSERT(!ble_gatt_find_s_test_proc_complete);
|
TEST_ASSERT(error != NULL);
|
|
switch (error->status) {
|
case 0:
|
ble_gatt_find_s_test_svcs[ble_gatt_find_s_test_num_svcs++] = *service;
|
break;
|
|
case BLE_HS_EDONE:
|
ble_gatt_find_s_test_proc_complete = 1;
|
break;
|
|
default:
|
TEST_ASSERT(0);
|
break;
|
}
|
|
return 0;
|
}
|
|
static void
|
ble_gatt_find_s_test_misc_verify_incs(
|
struct ble_gatt_find_s_test_entry *entries)
|
{
|
int i;
|
|
for (i = 0; entries[i].inc_handle != 0; i++) {
|
TEST_ASSERT(ble_gatt_find_s_test_svcs[i].start_handle ==
|
entries[i].start_handle);
|
TEST_ASSERT(ble_gatt_find_s_test_svcs[i].end_handle ==
|
entries[i].end_handle);
|
TEST_ASSERT(ble_uuid_cmp(&ble_gatt_find_s_test_svcs[i].uuid.u,
|
entries[i].uuid) == 0);
|
}
|
|
TEST_ASSERT(i == ble_gatt_find_s_test_num_svcs);
|
TEST_ASSERT(ble_gatt_find_s_test_proc_complete);
|
}
|
|
static int
|
ble_gatt_find_s_test_misc_rx_read_type(
|
uint16_t conn_handle, struct ble_gatt_find_s_test_entry *entries)
|
{
|
struct ble_att_read_type_rsp rsp;
|
uint8_t buf[1024];
|
int off;
|
int rc;
|
int i;
|
|
memset(&rsp, 0, sizeof rsp);
|
|
off = BLE_ATT_READ_TYPE_RSP_BASE_SZ;
|
for (i = 0; entries[i].inc_handle != 0; i++) {
|
if (rsp.batp_length == BLE_GATTS_INC_SVC_LEN_NO_UUID + 2) {
|
break;
|
}
|
|
if (entries[i].uuid->type != BLE_UUID_TYPE_16) {
|
if (rsp.batp_length != 0) {
|
break;
|
}
|
rsp.batp_length = BLE_GATTS_INC_SVC_LEN_NO_UUID + 2;
|
} else {
|
rsp.batp_length = BLE_GATTS_INC_SVC_LEN_UUID + 2;
|
}
|
|
TEST_ASSERT_FATAL(off + rsp.batp_length <= sizeof buf);
|
|
put_le16(buf + off, entries[i].inc_handle);
|
off += 2;
|
|
put_le16(buf + off, entries[i].start_handle);
|
off += 2;
|
|
put_le16(buf + off, entries[i].end_handle);
|
off += 2;
|
|
if (entries[i].uuid->type == BLE_UUID_TYPE_16) {
|
put_le16(buf + off, ble_uuid_u16(entries[i].uuid));
|
off += 2;
|
}
|
}
|
|
if (i == 0) {
|
ble_hs_test_util_rx_att_err_rsp(conn_handle, BLE_ATT_OP_READ_TYPE_REQ,
|
BLE_ATT_ERR_ATTR_NOT_FOUND, 0);
|
return 0;
|
}
|
|
ble_att_read_type_rsp_write(buf + 0, BLE_ATT_READ_TYPE_RSP_BASE_SZ, &rsp);
|
|
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_find_s_test_misc_rx_read(uint16_t conn_handle, const ble_uuid_t *uuid)
|
{
|
uint8_t buf[17];
|
int rc;
|
|
TEST_ASSERT(uuid->type == BLE_UUID_TYPE_128);
|
|
buf[0] = BLE_ATT_OP_READ_RSP;
|
ble_uuid_flat(uuid, buf + 1);
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
|
buf, 17);
|
TEST_ASSERT(rc == 0);
|
}
|
|
static void
|
ble_gatt_find_s_test_misc_verify_tx_read_type(uint16_t start_handle,
|
uint16_t end_handle)
|
{
|
struct ble_att_read_type_req req;
|
struct os_mbuf *om;
|
uint16_t uuid16;
|
|
om = ble_hs_test_util_prev_tx_dequeue_pullup();
|
TEST_ASSERT_FATAL(om != NULL);
|
|
ble_att_read_type_req_parse(om->om_data, om->om_len, &req);
|
|
TEST_ASSERT(req.batq_start_handle == start_handle);
|
TEST_ASSERT(req.batq_end_handle == end_handle);
|
TEST_ASSERT(om->om_len == BLE_ATT_READ_TYPE_REQ_BASE_SZ + 2);
|
uuid16 = get_le16(om->om_data + BLE_ATT_READ_TYPE_REQ_BASE_SZ);
|
TEST_ASSERT(uuid16 == BLE_ATT_UUID_INCLUDE);
|
}
|
|
static void
|
ble_gatt_find_s_test_misc_verify_tx_read(uint16_t handle)
|
{
|
struct ble_att_read_req req;
|
struct os_mbuf *om;
|
|
om = ble_hs_test_util_prev_tx_dequeue_pullup();
|
TEST_ASSERT_FATAL(om != NULL);
|
|
ble_att_read_req_parse(om->om_data, om->om_len, &req);
|
|
TEST_ASSERT(req.barq_handle == handle);
|
TEST_ASSERT(om->om_len == BLE_ATT_READ_REQ_SZ);
|
}
|
|
static void
|
ble_gatt_find_s_test_misc_find_inc(uint16_t conn_handle,
|
uint16_t start_handle, uint16_t end_handle,
|
struct ble_gatt_find_s_test_entry *entries)
|
{
|
struct ble_gatt_svc service;
|
int cur_start;
|
int num_found;
|
int idx;
|
int rc;
|
int i;
|
|
rc = ble_gattc_find_inc_svcs(conn_handle, start_handle, end_handle,
|
ble_gatt_find_s_test_misc_cb, &service);
|
TEST_ASSERT(rc == 0);
|
|
cur_start = start_handle;
|
idx = 0;
|
while (1) {
|
ble_gatt_find_s_test_misc_verify_tx_read_type(cur_start, end_handle);
|
num_found = ble_gatt_find_s_test_misc_rx_read_type(conn_handle,
|
entries + idx);
|
if (num_found == 0) {
|
break;
|
}
|
|
if (entries[idx].uuid->type == BLE_UUID_TYPE_128) {
|
TEST_ASSERT(num_found == 1);
|
ble_gatt_find_s_test_misc_verify_tx_read(
|
entries[idx].start_handle);
|
ble_gatt_find_s_test_misc_rx_read(conn_handle,
|
entries[idx].uuid);
|
}
|
|
idx += num_found;
|
cur_start = entries[idx - 1].inc_handle + 1;
|
}
|
TEST_ASSERT(idx == ble_gatt_find_s_test_num_svcs);
|
TEST_ASSERT(ble_gatt_find_s_test_proc_complete);
|
|
for (i = 0; i < ble_gatt_find_s_test_num_svcs; i++) {
|
TEST_ASSERT(ble_gatt_find_s_test_svcs[i].start_handle ==
|
entries[i].start_handle);
|
TEST_ASSERT(ble_gatt_find_s_test_svcs[i].end_handle ==
|
entries[i].end_handle);
|
TEST_ASSERT(ble_uuid_cmp(&ble_gatt_find_s_test_svcs[i].uuid.u,
|
entries[i].uuid) == 0);
|
}
|
}
|
|
TEST_CASE_SELF(ble_gatt_find_s_test_1)
|
{
|
/* Two 16-bit UUID services; one response. */
|
ble_gatt_find_s_test_misc_init();
|
ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
ble_gatt_find_s_test_misc_find_inc(2, 5, 10,
|
((struct ble_gatt_find_s_test_entry[]) { {
|
.inc_handle = 6,
|
.start_handle = 35,
|
.end_handle = 49,
|
.uuid = BLE_UUID16_DECLARE(0x5155),
|
}, {
|
.inc_handle = 9,
|
.start_handle = 543,
|
.end_handle = 870,
|
.uuid = BLE_UUID16_DECLARE(0x1122),
|
}, {
|
0,
|
} })
|
);
|
|
/* One 128-bit UUID service; two responses. */
|
ble_gatt_find_s_test_misc_init();
|
ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
ble_gatt_find_s_test_misc_find_inc(2, 34, 100,
|
((struct ble_gatt_find_s_test_entry[]) { {
|
.inc_handle = 36,
|
.start_handle = 403,
|
.end_handle = 859,
|
.uuid = BLE_UUID128_DECLARE(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16),
|
}, {
|
0,
|
} })
|
);
|
|
/* Two 128-bit UUID service; four responses. */
|
ble_gatt_find_s_test_misc_init();
|
ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
ble_gatt_find_s_test_misc_find_inc(2, 34, 100,
|
((struct ble_gatt_find_s_test_entry[]) { {
|
.inc_handle = 36,
|
.start_handle = 403,
|
.end_handle = 859,
|
.uuid = BLE_UUID128_DECLARE(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16),
|
}, {
|
.inc_handle = 39,
|
.start_handle = 900,
|
.end_handle = 932,
|
.uuid = BLE_UUID128_DECLARE(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17),
|
}, {
|
0,
|
} })
|
);
|
|
/* Two 16-bit UUID; three 128-bit UUID; seven responses. */
|
ble_gatt_find_s_test_misc_init();
|
ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
ble_gatt_find_s_test_misc_find_inc(2, 1, 100,
|
((struct ble_gatt_find_s_test_entry[]) { {
|
.inc_handle = 36,
|
.start_handle = 403,
|
.end_handle = 859,
|
.uuid = BLE_UUID128_DECLARE(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16),
|
}, {
|
.inc_handle = 37,
|
.start_handle = 35,
|
.end_handle = 49,
|
.uuid = BLE_UUID16_DECLARE(0x5155),
|
}, {
|
.inc_handle = 38,
|
.start_handle = 543,
|
.end_handle = 870,
|
.uuid = BLE_UUID16_DECLARE(0x1122),
|
}, {
|
.inc_handle = 39,
|
.start_handle = 900,
|
.end_handle = 932,
|
.uuid = BLE_UUID128_DECLARE(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17),
|
}, {
|
.inc_handle = 40,
|
.start_handle = 940,
|
.end_handle = 950,
|
.uuid = BLE_UUID128_DECLARE(3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18),
|
}, {
|
0,
|
} })
|
);
|
|
ble_hs_test_util_assert_mbufs_freed(NULL);
|
}
|
|
TEST_CASE_SELF(ble_gatt_find_s_test_oom)
|
{
|
|
struct ble_gatt_find_s_test_entry incs[] = {
|
{
|
.inc_handle = 21,
|
.start_handle = 800,
|
.end_handle = 899,
|
.uuid = BLE_UUID128_DECLARE(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15),
|
},
|
{
|
.inc_handle = 22,
|
.start_handle = 900,
|
.end_handle = 999,
|
.uuid = BLE_UUID128_DECLARE(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16),
|
},
|
{ 0 }
|
};
|
|
struct os_mbuf *oms;
|
int32_t ticks_until;
|
int rc;
|
|
ble_gatt_find_s_test_misc_init();
|
|
ble_hs_test_util_create_conn(1, ((uint8_t[]){2,3,4,5,6,7,8,9}),
|
NULL, NULL);
|
|
/* Initiate a discover all characteristics procedure. */
|
rc = ble_gattc_find_inc_svcs(1, 20, 30,
|
ble_gatt_find_s_test_misc_cb, NULL);
|
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);
|
ble_gatt_find_s_test_misc_rx_read_type(1, incs);
|
|
/* 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 succeeds after mbufs become available. */
|
rc = os_mbuf_free_chain(oms);
|
TEST_ASSERT_FATAL(rc == 0);
|
os_time_advance(ticks_until);
|
ble_gattc_timer();
|
|
/* We can't cause a memory exhaustion error on the follow up request. The
|
* GATT client frees the read response immediately before sending the
|
* follow-up request, so there is always an mbuf available.
|
*/
|
/* XXX: Find a way to test this. */
|
ble_gatt_find_s_test_misc_rx_read(1, incs[0].uuid);
|
|
/* Exhaust the msys pool. Leave one mbuf for the forthcoming response. */
|
oms = ble_hs_test_util_mbuf_alloc_all_but(1);
|
ble_gatt_find_s_test_misc_rx_read_type(1, incs + 1);
|
|
/* Verify the procedure succeeds after mbufs become available. */
|
rc = os_mbuf_free_chain(oms);
|
TEST_ASSERT_FATAL(rc == 0);
|
os_time_advance(ticks_until);
|
ble_gattc_timer();
|
|
ble_gatt_find_s_test_misc_rx_read(1, incs[1].uuid);
|
|
ble_hs_test_util_rx_att_err_rsp(1,
|
BLE_ATT_OP_READ_TYPE_REQ,
|
BLE_ATT_ERR_ATTR_NOT_FOUND,
|
1);
|
|
ble_gatt_find_s_test_misc_verify_incs(incs);
|
|
ble_hs_test_util_assert_mbufs_freed(NULL);
|
}
|
|
TEST_SUITE(ble_gatt_find_s_test_suite)
|
{
|
ble_gatt_find_s_test_1();
|
ble_gatt_find_s_test_oom();
|
}
|