/* * 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 "host/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(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 } }); } TEST_CASE(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, } }); } TEST_CASE(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 } }); } TEST_CASE(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 } }); } TEST_CASE(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); } } TEST_CASE(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); } TEST_SUITE(ble_gatt_read_test_suite) { tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL); 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(); } int ble_gatt_read_test_all(void) { ble_gatt_read_test_suite(); return tu_any_failed; }