/* * 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 #include #include #include "testutil/testutil.h" #include "nimble/ble.h" #include "ble_hs_test.h" #include "host/ble_gatt.h" #include "host/ble_uuid.h" #include "ble_hs_test_util.h" struct ble_gatt_disc_c_test_char { uint16_t def_handle; uint16_t val_handle; uint8_t properties; const ble_uuid_t *uuid; }; #define BLE_GATT_DISC_C_TEST_MAX_CHARS 256 static struct ble_gatt_chr ble_gatt_disc_c_test_chars[BLE_GATT_DISC_C_TEST_MAX_CHARS]; static int ble_gatt_disc_c_test_num_chars; static int ble_gatt_disc_c_test_rx_complete; static void ble_gatt_disc_c_test_init(void) { ble_hs_test_util_init(); ble_gatt_disc_c_test_num_chars = 0; ble_gatt_disc_c_test_rx_complete = 0; } static int ble_gatt_disc_c_test_misc_rx_rsp_once( uint16_t conn_handle, struct ble_gatt_disc_c_test_char *chars) { struct ble_att_read_type_rsp rsp; uint8_t buf[1024]; int off; int rc; int i; /* Send the pending ATT Read By Type Request. */ if (chars[0].uuid->type == BLE_UUID_TYPE_16) { rsp.batp_length = BLE_ATT_READ_TYPE_ADATA_BASE_SZ + BLE_GATT_CHR_DECL_SZ_16; } else { rsp.batp_length = BLE_ATT_READ_TYPE_ADATA_BASE_SZ + BLE_GATT_CHR_DECL_SZ_128; } ble_att_read_type_rsp_write(buf, BLE_ATT_READ_TYPE_RSP_BASE_SZ, &rsp); off = BLE_ATT_READ_TYPE_RSP_BASE_SZ; for (i = 0; ; i++) { if (chars[i].def_handle == 0) { /* No more services. */ break; } /* If the value length is changing, we need a separate response. */ if (((chars[i].uuid->type == BLE_UUID_TYPE_16) ^ (chars[0].uuid->type == BLE_UUID_TYPE_16)) != 0) { break; } if (chars[i].uuid->type == BLE_UUID_TYPE_16) { if (off + BLE_ATT_READ_TYPE_ADATA_SZ_16 > ble_att_mtu(conn_handle)) { /* Can't fit any more entries. */ break; } } else { if (off + BLE_ATT_READ_TYPE_ADATA_SZ_128 > ble_att_mtu(conn_handle)) { /* Can't fit any more entries. */ break; } } put_le16(buf + off, chars[i].def_handle); off += 2; buf[off] = chars[i].properties; off++; put_le16(buf + off, chars[i].val_handle); off += 2; ble_uuid_flat(chars[i].uuid, buf + off); off += ble_uuid_length(chars[i].uuid); } 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_disc_c_test_misc_rx_rsp(uint16_t conn_handle, uint16_t end_handle, struct ble_gatt_disc_c_test_char *chars) { int count; int idx; idx = 0; while (chars[idx].def_handle != 0) { count = ble_gatt_disc_c_test_misc_rx_rsp_once(conn_handle, chars + idx); if (count == 0) { break; } idx += count; } if (chars[idx - 1].def_handle != end_handle) { /* Send the pending ATT Request. */ ble_hs_test_util_rx_att_err_rsp(conn_handle, BLE_ATT_OP_READ_TYPE_REQ, BLE_ATT_ERR_ATTR_NOT_FOUND, chars[idx - 1].def_handle); } } static void ble_gatt_disc_c_test_misc_verify_chars(struct ble_gatt_disc_c_test_char *chars, int stop_after) { int i; if (stop_after == 0) { stop_after = BLE_GATT_DISC_C_TEST_MAX_CHARS; } for (i = 0; i < stop_after && chars[i].def_handle != 0; i++) { TEST_ASSERT(chars[i].def_handle == ble_gatt_disc_c_test_chars[i].def_handle); TEST_ASSERT(chars[i].val_handle == ble_gatt_disc_c_test_chars[i].val_handle); TEST_ASSERT(ble_uuid_cmp(chars[i].uuid, &ble_gatt_disc_c_test_chars[i].uuid.u) == 0); } TEST_ASSERT(i == ble_gatt_disc_c_test_num_chars); TEST_ASSERT(ble_gatt_disc_c_test_rx_complete); } static int ble_gatt_disc_c_test_misc_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *chr, void *arg) { struct ble_gatt_chr *dst; int *stop_after; TEST_ASSERT(error != NULL); TEST_ASSERT(!ble_gatt_disc_c_test_rx_complete); stop_after = arg; switch (error->status) { case 0: TEST_ASSERT_FATAL(ble_gatt_disc_c_test_num_chars < BLE_GATT_DISC_C_TEST_MAX_CHARS); dst = ble_gatt_disc_c_test_chars + ble_gatt_disc_c_test_num_chars++; *dst = *chr; break; case BLE_HS_EDONE: ble_gatt_disc_c_test_rx_complete = 1; break; default: TEST_ASSERT(0); break; } if (*stop_after > 0) { (*stop_after)--; if (*stop_after == 0) { ble_gatt_disc_c_test_rx_complete = 1; return 1; } } return 0; } static void ble_gatt_disc_c_test_misc_all(uint16_t start_handle, uint16_t end_handle, int stop_after, struct ble_gatt_disc_c_test_char *chars) { int num_left; int rc; ble_gatt_disc_c_test_init(); ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), NULL, NULL); num_left = stop_after; rc = ble_gattc_disc_all_chrs(2, start_handle, end_handle, ble_gatt_disc_c_test_misc_cb, &num_left); TEST_ASSERT(rc == 0); ble_gatt_disc_c_test_misc_rx_rsp(2, end_handle, chars); ble_gatt_disc_c_test_misc_verify_chars(chars, stop_after); } static void ble_gatt_disc_c_test_misc_uuid(uint16_t start_handle, uint16_t end_handle, int stop_after, const ble_uuid_t *uuid, struct ble_gatt_disc_c_test_char *rsp_chars, struct ble_gatt_disc_c_test_char *ret_chars) { int rc; ble_gatt_disc_c_test_init(); ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), NULL, NULL); rc = ble_gattc_disc_chrs_by_uuid(2, start_handle, end_handle, uuid, ble_gatt_disc_c_test_misc_cb, &stop_after); TEST_ASSERT(rc == 0); ble_gatt_disc_c_test_misc_rx_rsp(2, end_handle, rsp_chars); ble_gatt_disc_c_test_misc_verify_chars(ret_chars, 0); } TEST_CASE_SELF(ble_gatt_disc_c_test_disc_all) { /*** One 16-bit characteristic. */ ble_gatt_disc_c_test_misc_all(50, 100, 0, (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 55, .val_handle = 56, .uuid = BLE_UUID16_DECLARE(0x2010), }, { 0 } }); /*** Two 16-bit characteristics. */ ble_gatt_disc_c_test_misc_all(50, 100, 0, (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 55, .val_handle = 56, .uuid = BLE_UUID16_DECLARE(0x2010), }, { .def_handle = 57, .val_handle = 58, .uuid = BLE_UUID16_DECLARE(0x64ba), }, { 0 } }); /*** Five 16-bit characteristics. */ ble_gatt_disc_c_test_misc_all(50, 100, 0, (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 55, .val_handle = 56, .uuid = BLE_UUID16_DECLARE(0x2010), }, { .def_handle = 57, .val_handle = 58, .uuid = BLE_UUID16_DECLARE(0x64ba), }, { .def_handle = 59, .val_handle = 60, .uuid = BLE_UUID16_DECLARE(0x5372), }, { .def_handle = 61, .val_handle = 62, .uuid = BLE_UUID16_DECLARE(0xab93), }, { .def_handle = 63, .val_handle = 64, .uuid = BLE_UUID16_DECLARE(0x0023), }, { 0 } }); /*** Interleaved 16-bit and 128-bit characteristics. */ ble_gatt_disc_c_test_misc_all(50, 100, 0, (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 83, .val_handle = 84, .uuid = BLE_UUID16_DECLARE(0x2010), }, { .def_handle = 87, .val_handle = 88, .uuid = BLE_UUID128_DECLARE(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), }, { .def_handle = 91, .val_handle = 92, .uuid = BLE_UUID16_DECLARE(0x0003), }, { .def_handle = 93, .val_handle = 94, .uuid = BLE_UUID128_DECLARE(1, 0, 4, 0, 6, 9, 17, 7, 8, 43, 7, 4, 12, 43, 19, 35), }, { .def_handle = 98, .val_handle = 99, .uuid = BLE_UUID16_DECLARE(0xabfa), }, { 0 } }); /*** Ends with final handle ID. */ ble_gatt_disc_c_test_misc_all(50, 100, 0, (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 55, .val_handle = 56, .uuid = BLE_UUID16_DECLARE(0x2010), }, { .def_handle = 99, .val_handle = 100, .uuid = BLE_UUID16_DECLARE(0x64ba), }, { 0 } }); /*** Stop after two characteristics. */ ble_gatt_disc_c_test_misc_all(50, 100, 2, (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 55, .val_handle = 56, .uuid = BLE_UUID16_DECLARE(0x2010), }, { .def_handle = 57, .val_handle = 58, .uuid = BLE_UUID16_DECLARE(0x64ba), }, { .def_handle = 59, .val_handle = 60, .uuid = BLE_UUID16_DECLARE(0x5372), }, { .def_handle = 61, .val_handle = 62, .uuid = BLE_UUID16_DECLARE(0xab93), }, { .def_handle = 63, .val_handle = 64, .uuid = BLE_UUID16_DECLARE(0x0023), }, { 0 } }); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gatt_disc_c_test_disc_uuid) { /*** One 16-bit characteristic. */ ble_gatt_disc_c_test_misc_uuid(50, 100, 0, BLE_UUID16_DECLARE(0x2010), (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 55, .val_handle = 56, .uuid = BLE_UUID16_DECLARE(0x2010), }, { 0 } }, (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 55, .val_handle = 56, .uuid = BLE_UUID16_DECLARE(0x2010), }, { 0 } } ); /*** No matching characteristics. */ ble_gatt_disc_c_test_misc_uuid(50, 100, 0, BLE_UUID16_DECLARE(0x2010), (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 55, .val_handle = 56, .uuid = BLE_UUID16_DECLARE(0x1234), }, { 0 } }, (struct ble_gatt_disc_c_test_char[]) { { 0 } } ); /*** 2/5 16-bit characteristics. */ ble_gatt_disc_c_test_misc_uuid(50, 100, 0, BLE_UUID16_DECLARE(0x2010), (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 55, .val_handle = 56, .uuid = BLE_UUID16_DECLARE(0x2010), }, { .def_handle = 57, .val_handle = 58, .uuid = BLE_UUID16_DECLARE(0x64ba), }, { .def_handle = 59, .val_handle = 60, .uuid = BLE_UUID16_DECLARE(0x5372), }, { .def_handle = 61, .val_handle = 62, .uuid = BLE_UUID16_DECLARE(0x2010), }, { .def_handle = 63, .val_handle = 64, .uuid = BLE_UUID16_DECLARE(0x0023), }, { 0 } }, (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 55, .val_handle = 56, .uuid = BLE_UUID16_DECLARE(0x2010), }, { .def_handle = 61, .val_handle = 62, .uuid = BLE_UUID16_DECLARE(0x2010), }, { 0 } } ); /*** Interleaved 16-bit and 128-bit characteristics. */ ble_gatt_disc_c_test_misc_uuid( 50, 100, 0, BLE_UUID128_DECLARE(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15), (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 83, .val_handle = 84, .uuid = BLE_UUID16_DECLARE(0x2010), }, { .def_handle = 87, .val_handle = 88, .uuid = BLE_UUID128_DECLARE(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15), }, { .def_handle = 91, .val_handle = 92, .uuid = BLE_UUID16_DECLARE(0x0003), }, { .def_handle = 93, .val_handle = 94, .uuid = BLE_UUID128_DECLARE(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15), }, { .def_handle = 98, .val_handle = 99, .uuid = BLE_UUID16_DECLARE(0xabfa), }, { 0 } }, (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 87, .val_handle = 88, .uuid = BLE_UUID128_DECLARE(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15), }, { .def_handle = 93, .val_handle = 94, .uuid = BLE_UUID128_DECLARE(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15), }, { 0 } } ); /*** Ends with final handle ID. */ ble_gatt_disc_c_test_misc_uuid(50, 100, 0, BLE_UUID16_DECLARE(0x64ba), (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 55, .val_handle = 56, .uuid = BLE_UUID16_DECLARE(0x2010), }, { .def_handle = 99, .val_handle = 100, .uuid = BLE_UUID16_DECLARE(0x64ba), }, { 0 } }, (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 99, .val_handle = 100, .uuid = BLE_UUID16_DECLARE(0x64ba), }, { 0 } } ); /*** Stop after first characteristic. */ ble_gatt_disc_c_test_misc_uuid(50, 100, 1, BLE_UUID16_DECLARE(0x2010), (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 55, .val_handle = 56, .uuid = BLE_UUID16_DECLARE(0x2010), }, { .def_handle = 57, .val_handle = 58, .uuid = BLE_UUID16_DECLARE(0x64ba), }, { .def_handle = 59, .val_handle = 60, .uuid = BLE_UUID16_DECLARE(0x5372), }, { .def_handle = 61, .val_handle = 62, .uuid = BLE_UUID16_DECLARE(0x2010), }, { .def_handle = 63, .val_handle = 64, .uuid = BLE_UUID16_DECLARE(0x0023), }, { 0 } }, (struct ble_gatt_disc_c_test_char[]) { { .def_handle = 55, .val_handle = 56, .uuid = BLE_UUID16_DECLARE(0x2010), }, { 0 } } ); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gatt_disc_c_test_oom_all) { /* Retrieve enough characteristics to require two transactions. */ struct ble_gatt_disc_c_test_char chrs[] = { { .def_handle = 93, .val_handle = 94, .uuid = BLE_UUID128_DECLARE(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15), }, { .def_handle = 95, .val_handle = 96, .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 stop_after; int num_chrs; int rc; ble_gatt_disc_c_test_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. */ stop_after = 0; rc = ble_gattc_disc_all_chrs(1, 1, 0xffff, ble_gatt_disc_c_test_misc_cb, &stop_after); 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); num_chrs = ble_gatt_disc_c_test_misc_rx_rsp_once(1, chrs); /* Make sure there are still undiscovered characteristics. */ TEST_ASSERT_FATAL(num_chrs < sizeof chrs / sizeof chrs[0] - 1); /* 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); ble_gatt_disc_c_test_misc_rx_rsp_once(1, chrs + num_chrs); /* 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(); ble_hs_test_util_rx_att_err_rsp(1, BLE_ATT_OP_READ_TYPE_REQ, BLE_ATT_ERR_ATTR_NOT_FOUND, 1); ble_gatt_disc_c_test_misc_verify_chars(chrs, 0); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gatt_disc_c_test_oom_uuid) { /* Retrieve enough characteristics to require two transactions. */ struct ble_gatt_disc_c_test_char chrs[] = { { .def_handle = 93, .val_handle = 94, .uuid = BLE_UUID128_DECLARE(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15), }, { .def_handle = 95, .val_handle = 96, .uuid = BLE_UUID128_DECLARE(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15), }, { 0 } }; struct os_mbuf *oms; int32_t ticks_until; int stop_after; int num_chrs; int rc; ble_gatt_disc_c_test_init(); ble_hs_test_util_create_conn(1, ((uint8_t[]){2,3,4,5,6,7,8,9}), NULL, NULL); /* Initiate a discover characteristics by UUID procedure. */ stop_after = 0; rc = ble_gattc_disc_chrs_by_uuid(1, 1, 0xffff, chrs[0].uuid, ble_gatt_disc_c_test_misc_cb, &stop_after); 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); num_chrs = ble_gatt_disc_c_test_misc_rx_rsp_once(1, chrs); /* Make sure there are still undiscovered characteristics. */ TEST_ASSERT_FATAL(num_chrs < sizeof chrs / sizeof chrs[0] - 1); /* 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); ble_gatt_disc_c_test_misc_rx_rsp_once(1, chrs + num_chrs); /* 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(); ble_hs_test_util_rx_att_err_rsp(1, BLE_ATT_OP_READ_TYPE_REQ, BLE_ATT_ERR_ATTR_NOT_FOUND, 1); ble_gatt_disc_c_test_misc_verify_chars(chrs, 0); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_SUITE(ble_gatt_disc_c_test_suite) { ble_gatt_disc_c_test_disc_all(); ble_gatt_disc_c_test_disc_uuid(); ble_gatt_disc_c_test_oom_all(); ble_gatt_disc_c_test_oom_uuid(); }