blob: 172bdd3321ab4d171ddb1e22b4c2475c0aa14b91 [file] [log] [blame]
/*
* 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();
}