| /* |
| * 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 <stddef.h> |
| #include <errno.h> |
| #include <string.h> |
| #include "testutil/testutil.h" |
| #include "nimble/hci_common.h" |
| #include "host/ble_hs_test.h" |
| #include "host/ble_uuid.h" |
| #include "ble_hs_test_util.h" |
| |
| static uint8_t *ble_att_svr_test_attr_r_1; |
| static uint16_t ble_att_svr_test_attr_r_1_len; |
| static uint8_t *ble_att_svr_test_attr_r_2; |
| static uint16_t ble_att_svr_test_attr_r_2_len; |
| |
| static uint8_t ble_att_svr_test_attr_w_1[1024]; |
| static uint16_t ble_att_svr_test_attr_w_1_len; |
| static uint8_t ble_att_svr_test_attr_w_2[1024]; |
| static uint16_t ble_att_svr_test_attr_w_2_len; |
| |
| static uint16_t ble_att_svr_test_n_conn_handle; |
| static uint16_t ble_att_svr_test_n_attr_handle; |
| static uint8_t ble_att_svr_test_attr_n[1024]; |
| static uint16_t ble_att_svr_test_attr_n_len; |
| |
| static int |
| ble_att_svr_test_misc_gap_cb(struct ble_gap_event *event, void *arg) |
| { |
| int rc; |
| |
| switch (event->type) { |
| case BLE_GAP_EVENT_NOTIFY_RX: |
| ble_att_svr_test_n_conn_handle = event->notify_rx.conn_handle; |
| ble_att_svr_test_n_attr_handle = event->notify_rx.attr_handle; |
| TEST_ASSERT_FATAL(OS_MBUF_PKTLEN(event->notify_rx.om) <= |
| sizeof ble_att_svr_test_attr_n); |
| ble_att_svr_test_attr_n_len = OS_MBUF_PKTLEN(event->notify_rx.om); |
| rc = os_mbuf_copydata(event->notify_rx.om, 0, |
| ble_att_svr_test_attr_n_len, |
| ble_att_svr_test_attr_n); |
| TEST_ASSERT_FATAL(rc == 0); |
| break; |
| |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * @return The handle of the new test connection. |
| */ |
| static uint16_t |
| ble_att_svr_test_misc_init(uint16_t mtu) |
| { |
| struct ble_l2cap_chan *chan; |
| struct ble_hs_conn *conn; |
| int rc; |
| |
| ble_hs_test_util_init(); |
| |
| ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), |
| ble_att_svr_test_misc_gap_cb, NULL); |
| |
| ble_hs_lock(); |
| |
| rc = ble_hs_misc_conn_chan_find(2, BLE_L2CAP_CID_ATT, &conn, &chan); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| if (mtu != 0) { |
| chan->my_mtu = mtu; |
| chan->peer_mtu = mtu; |
| chan->flags |= BLE_L2CAP_CHAN_F_TXED_MTU; |
| } |
| |
| ble_hs_unlock(); |
| |
| ble_att_svr_test_attr_r_1_len = 0; |
| ble_att_svr_test_attr_r_2_len = 0; |
| ble_att_svr_test_attr_w_1_len = 0; |
| |
| return 2; |
| } |
| |
| static int |
| ble_att_svr_test_misc_attr_fn_r_1(uint16_t conn_handle, uint16_t attr_handle, |
| uint8_t op, uint16_t offset, |
| struct os_mbuf **om, void *arg) |
| { |
| int rc; |
| |
| switch (op) { |
| case BLE_ATT_ACCESS_OP_READ: |
| if (offset > ble_att_svr_test_attr_r_1_len) { |
| return BLE_ATT_ERR_INVALID_OFFSET; |
| } |
| |
| rc = os_mbuf_append(*om, ble_att_svr_test_attr_r_1 + offset, |
| ble_att_svr_test_attr_r_1_len - offset); |
| TEST_ASSERT_FATAL(rc == 0); |
| return 0; |
| |
| default: |
| return BLE_ATT_ERR_UNLIKELY; |
| } |
| } |
| |
| static int |
| ble_att_svr_test_misc_attr_fn_r_2(uint16_t conn_handle, uint16_t attr_handle, |
| uint8_t op, uint16_t offset, |
| struct os_mbuf **om, void *arg) |
| { |
| int rc; |
| |
| switch (op) { |
| case BLE_ATT_ACCESS_OP_READ: |
| if (offset > ble_att_svr_test_attr_r_2_len) { |
| return BLE_ATT_ERR_INVALID_OFFSET; |
| } |
| |
| rc = os_mbuf_append(*om, ble_att_svr_test_attr_r_2 + offset, |
| ble_att_svr_test_attr_r_2_len - offset); |
| TEST_ASSERT_FATAL(rc == 0); |
| return 0; |
| |
| default: |
| return BLE_ATT_ERR_UNLIKELY; |
| } |
| } |
| |
| static int |
| ble_att_svr_test_misc_attr_fn_r_err(uint16_t conn_handle, uint16_t attr_handle, |
| uint8_t op, uint16_t offset, |
| struct os_mbuf **om, void *arg) |
| { |
| int rc; |
| |
| rc = os_mbuf_append(*om, (uint8_t[4]){1,2,3,4}, 4); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| return BLE_ATT_ERR_UNLIKELY; |
| } |
| |
| #define BLE_ATT_SVR_TEST_LAST_SVC 11 |
| #define BLE_ATT_SVR_TEST_LAST_ATTR 24 |
| |
| static int |
| ble_att_svr_test_misc_attr_fn_r_group(uint16_t conn_handle, |
| uint16_t attr_handle, |
| uint8_t op, |
| uint16_t offset, |
| struct os_mbuf **om, |
| void *arg) |
| { |
| uint8_t *src; |
| int rc; |
| |
| /* Service 0x1122 from 1 to 5 */ |
| /* Service 0x2233 from 6 to 10 */ |
| /* Service 010203...0f from 11 to 24 */ |
| |
| static uint8_t vals[25][16] = { |
| [1] = { 0x22, 0x11 }, |
| [2] = { 0x01, 0x11 }, |
| [3] = { 0x02, 0x11 }, |
| [4] = { 0x03, 0x11 }, |
| [5] = { 0x04, 0x11 }, |
| [6] = { 0x33, 0x22 }, |
| [7] = { 0x01, 0x22 }, |
| [8] = { 0x02, 0x22 }, |
| [9] = { 0x03, 0x22 }, |
| [10] = { 0x04, 0x22 }, |
| [11] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 }, |
| [12] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }, |
| [13] = { 0xdd, 0xdd }, |
| [14] = { 0x55, 0x55 }, |
| [15] = { 0xdd, 0xdd }, |
| [16] = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 }, |
| [17] = { 0xdd, 0xdd }, |
| [18] = { 0x66, 0x66 }, |
| [19] = { 0xdd, 0xdd }, |
| [20] = { 0x77, 0x77 }, |
| [21] = { 0xdd, 0xdd }, |
| [22] = { 0x88, 0x88 }, |
| [23] = { 0xdd, 0xdd }, |
| [24] = { 0x99, 0x99 }, |
| }; |
| |
| static uint8_t zeros[14]; |
| |
| if (op != BLE_ATT_ACCESS_OP_READ) { |
| return -1; |
| } |
| |
| TEST_ASSERT_FATAL(attr_handle >= 1 && |
| attr_handle <= BLE_ATT_SVR_TEST_LAST_ATTR); |
| |
| src = &vals[attr_handle][0]; |
| if (memcmp(src + 2, zeros, 14) == 0) { |
| rc = os_mbuf_append(*om, src, 2); |
| } else { |
| rc = os_mbuf_append(*om, src, 16); |
| } |
| if (rc != 0) { |
| return BLE_ATT_ERR_INSUFFICIENT_RES; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| ble_att_svr_test_misc_register_uuid(const ble_uuid_t *uuid, uint8_t flags, |
| uint16_t expected_handle, |
| ble_att_svr_access_fn *fn) |
| { |
| uint16_t handle; |
| int rc; |
| |
| rc = ble_att_svr_register(uuid, flags, 0, &handle, fn, NULL); |
| TEST_ASSERT_FATAL(rc == 0); |
| TEST_ASSERT_FATAL(handle == expected_handle); |
| } |
| |
| static void |
| ble_att_svr_test_misc_register_group_attrs(void) |
| { |
| /* Service 0x1122 from 1 to 5 */ |
| /* Service 0x2233 from 6 to 10 */ |
| /* Service 010203...0f from 11 to 24 */ |
| |
| static const ble_uuid16_t uuid_svc = |
| BLE_UUID16_INIT(BLE_ATT_UUID_PRIMARY_SERVICE); |
| static const ble_uuid16_t uuid_inc = |
| BLE_UUID16_INIT(BLE_ATT_UUID_INCLUDE); |
| static const ble_uuid16_t uuid_chr = |
| BLE_UUID16_INIT(BLE_ATT_UUID_CHARACTERISTIC); |
| static ble_uuid16_t uuids[24]; |
| |
| int i; |
| |
| /* Service 0x1122 from 1 to 5 */ |
| ble_att_svr_test_misc_register_uuid(&uuid_svc.u, HA_FLAG_PERM_RW, 1, |
| ble_att_svr_test_misc_attr_fn_r_group); |
| for (i = 2; i <= 5; i++) { |
| if ((i - 2) % 2 == 0) { |
| ble_att_svr_test_misc_register_uuid(&uuid_chr.u, HA_FLAG_PERM_RW, i, |
| ble_att_svr_test_misc_attr_fn_r_group); |
| } else { |
| uuids[i] = *BLE_UUID16(BLE_UUID16_DECLARE(i)); |
| ble_att_svr_test_misc_register_uuid(&uuids[i].u, HA_FLAG_PERM_RW, i, |
| ble_att_svr_test_misc_attr_fn_r_group); |
| } |
| } |
| |
| /* Service 0x2233 from 6 to 10 */ |
| ble_att_svr_test_misc_register_uuid(&uuid_svc.u, HA_FLAG_PERM_RW, 6, |
| ble_att_svr_test_misc_attr_fn_r_group); |
| for (i = 7; i <= 10; i++) { |
| ble_att_svr_test_misc_register_uuid(&uuid_inc.u, HA_FLAG_PERM_RW, i, |
| ble_att_svr_test_misc_attr_fn_r_group); |
| } |
| |
| /* Service 010203...0f from 11 to 24 */ |
| ble_att_svr_test_misc_register_uuid(&uuid_svc.u, HA_FLAG_PERM_RW, 11, |
| ble_att_svr_test_misc_attr_fn_r_group); |
| for (i = 12; i <= 24; i++) { |
| if ((i - 12) % 2 == 0) { |
| ble_att_svr_test_misc_register_uuid(&uuid_chr.u, HA_FLAG_PERM_RW, i, |
| ble_att_svr_test_misc_attr_fn_r_group); |
| } else { |
| uuids[i] = *BLE_UUID16(BLE_UUID16_DECLARE(i)); |
| ble_att_svr_test_misc_register_uuid(&uuids[i].u, HA_FLAG_PERM_RW, i, |
| ble_att_svr_test_misc_attr_fn_r_group); |
| } |
| } |
| } |
| |
| static int |
| ble_att_svr_test_misc_attr_fn_rw_1(uint16_t conn_handle, uint16_t attr_handle, |
| uint8_t op, uint16_t offset, |
| struct os_mbuf **om, void *arg) |
| { |
| int rc; |
| |
| switch (op) { |
| case BLE_ATT_ACCESS_OP_READ: |
| if (offset > ble_att_svr_test_attr_w_1_len) { |
| return BLE_ATT_ERR_INVALID_OFFSET; |
| } |
| |
| rc = os_mbuf_append(*om, ble_att_svr_test_attr_w_1 + offset, |
| ble_att_svr_test_attr_w_1_len - offset); |
| TEST_ASSERT_FATAL(rc == 0); |
| return 0; |
| |
| case BLE_ATT_ACCESS_OP_WRITE: |
| rc = os_mbuf_copydata(*om, 0, OS_MBUF_PKTLEN(*om), |
| ble_att_svr_test_attr_w_1); |
| TEST_ASSERT_FATAL(rc == 0); |
| ble_att_svr_test_attr_w_1_len = OS_MBUF_PKTLEN(*om); |
| return 0; |
| |
| default: |
| return BLE_ATT_ERR_UNLIKELY; |
| } |
| } |
| |
| static int |
| ble_att_svr_test_misc_attr_fn_w_1(uint16_t conn_handle, uint16_t attr_handle, |
| uint8_t op, uint16_t offset, |
| struct os_mbuf **om, void *arg) |
| { |
| int rc; |
| |
| switch (op) { |
| case BLE_ATT_ACCESS_OP_WRITE: |
| rc = os_mbuf_copydata(*om, 0, OS_MBUF_PKTLEN(*om), |
| ble_att_svr_test_attr_w_1); |
| TEST_ASSERT_FATAL(rc == 0); |
| ble_att_svr_test_attr_w_1_len = OS_MBUF_PKTLEN(*om); |
| return 0; |
| |
| default: |
| return BLE_ATT_ERR_UNLIKELY; |
| } |
| } |
| |
| static int |
| ble_att_svr_test_misc_attr_fn_w_2(uint16_t conn_handle, uint16_t attr_handle, |
| uint8_t op, uint16_t offset, |
| struct os_mbuf **om, void *arg) |
| { |
| int rc; |
| |
| switch (op) { |
| case BLE_ATT_ACCESS_OP_WRITE: |
| rc = os_mbuf_copydata(*om, 0, OS_MBUF_PKTLEN(*om), |
| ble_att_svr_test_attr_w_2); |
| TEST_ASSERT_FATAL(rc == 0); |
| ble_att_svr_test_attr_w_2_len = OS_MBUF_PKTLEN(*om); |
| return 0; |
| |
| default: |
| return BLE_ATT_ERR_UNLIKELY; |
| } |
| } |
| |
| static int |
| ble_att_svr_test_misc_attr_fn_w_fail(uint16_t conn_handle, |
| uint16_t attr_handle, |
| uint8_t op, uint16_t offset, |
| struct os_mbuf **om, void *arg) |
| { |
| return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; |
| } |
| |
| static void |
| ble_att_svr_test_misc_verify_w_1(void *data, int data_len) |
| { |
| TEST_ASSERT(ble_att_svr_test_attr_w_1_len == data_len); |
| TEST_ASSERT(memcmp(ble_att_svr_test_attr_w_1, data, data_len) == 0); |
| } |
| |
| static void |
| ble_att_svr_test_misc_verify_w_2(void *data, int data_len) |
| { |
| TEST_ASSERT(ble_att_svr_test_attr_w_2_len == data_len); |
| TEST_ASSERT(memcmp(ble_att_svr_test_attr_w_2, data, data_len) == 0); |
| } |
| |
| static void |
| ble_att_svr_test_misc_rx_read_mult_req(uint16_t conn_handle, |
| uint16_t *handles, int num_handles, |
| int success) |
| { |
| int rc; |
| |
| rc = ble_hs_test_util_rx_att_read_mult_req(conn_handle, handles, |
| num_handles); |
| if (success) { |
| TEST_ASSERT(rc == 0); |
| } else { |
| TEST_ASSERT(rc != 0); |
| } |
| } |
| |
| static void |
| ble_att_svr_test_misc_verify_tx_read_mult_rsp( |
| uint16_t conn_handle, struct ble_hs_test_util_flat_attr *attrs, |
| int num_attrs) |
| { |
| struct ble_l2cap_chan *chan; |
| struct os_mbuf *om; |
| uint16_t attr_len; |
| uint16_t mtu; |
| uint8_t u8; |
| int rc; |
| int off; |
| int i; |
| |
| om = ble_hs_test_util_prev_tx_dequeue(); |
| |
| rc = os_mbuf_copydata(om, 0, 1, &u8); |
| TEST_ASSERT(rc == 0); |
| TEST_ASSERT(u8 == BLE_ATT_OP_READ_MULT_RSP); |
| |
| ble_hs_lock(); |
| |
| rc = ble_hs_misc_conn_chan_find(conn_handle, BLE_L2CAP_CID_ATT, |
| NULL, &chan); |
| TEST_ASSERT_FATAL(rc == 0); |
| mtu = ble_att_chan_mtu(chan); |
| |
| ble_hs_unlock(); |
| |
| off = 1; |
| for (i = 0; i < num_attrs; i++) { |
| attr_len = min(attrs[i].value_len, mtu - off); |
| |
| rc = os_mbuf_cmpf(om, off, attrs[i].value, attr_len); |
| TEST_ASSERT(rc == 0); |
| |
| off += attr_len; |
| } |
| |
| TEST_ASSERT(OS_MBUF_PKTLEN(om) == off); |
| } |
| |
| static void |
| ble_att_svr_test_misc_verify_all_read_mult( |
| uint16_t conn_handle, struct ble_hs_test_util_flat_attr *attrs, |
| int num_attrs) |
| { |
| uint16_t handles[256]; |
| int i; |
| |
| TEST_ASSERT_FATAL(num_attrs <= sizeof handles / sizeof handles[0]); |
| |
| for (i = 0; i < num_attrs; i++) { |
| handles[i] = attrs[i].handle; |
| } |
| |
| ble_att_svr_test_misc_rx_read_mult_req(conn_handle, handles, num_attrs, 1); |
| ble_att_svr_test_misc_verify_tx_read_mult_rsp(conn_handle, |
| attrs, num_attrs); |
| } |
| |
| static void |
| ble_att_svr_test_misc_verify_tx_mtu_rsp(uint16_t conn_handle) |
| { |
| struct ble_l2cap_chan *chan; |
| struct ble_hs_conn *conn; |
| uint16_t my_mtu; |
| int rc; |
| |
| ble_hs_lock(); |
| |
| rc = ble_att_conn_chan_find(conn_handle, &conn, &chan); |
| assert(rc == 0); |
| my_mtu = chan->my_mtu; |
| |
| ble_hs_unlock(); |
| |
| ble_hs_test_util_verify_tx_mtu_cmd(0, my_mtu); |
| } |
| |
| struct ble_att_svr_test_type_value_entry { |
| uint16_t first; /* 0 on last entry */ |
| uint16_t last; |
| }; |
| |
| static void |
| ble_att_svr_test_misc_verify_tx_find_type_value_rsp( |
| struct ble_att_svr_test_type_value_entry *entries) |
| { |
| struct ble_att_svr_test_type_value_entry *entry; |
| struct os_mbuf *om; |
| uint16_t u16; |
| uint8_t op; |
| int off; |
| int rc; |
| |
| off = 0; |
| |
| om = ble_hs_test_util_prev_tx_dequeue_pullup(); |
| |
| rc = os_mbuf_copydata(om, off, 1, &op); |
| TEST_ASSERT(rc == 0); |
| off += 1; |
| |
| TEST_ASSERT(op == BLE_ATT_OP_FIND_TYPE_VALUE_RSP); |
| |
| for (entry = entries; entry->first != 0; entry++) { |
| rc = os_mbuf_copydata(om, off, 2, &u16); |
| TEST_ASSERT(rc == 0); |
| put_le16(&u16, u16); |
| TEST_ASSERT(u16 == entry->first); |
| off += 2; |
| |
| rc = os_mbuf_copydata(om, off, 2, &u16); |
| TEST_ASSERT(rc == 0); |
| put_le16(&u16, u16); |
| TEST_ASSERT(u16 == entry->last); |
| off += 2; |
| } |
| |
| /* Ensure there is no extra data in the response. */ |
| TEST_ASSERT(off == OS_MBUF_PKTHDR(om)->omp_len); |
| } |
| |
| /** Returns the number of entries successfully verified. */ |
| |
| struct ble_att_svr_test_type_entry { |
| uint16_t handle; /* 0 on last entry */ |
| void *value; |
| int value_len; |
| }; |
| |
| /** Returns the number of entries successfully verified. */ |
| static void |
| ble_att_svr_test_misc_verify_tx_read_type_rsp( |
| struct ble_att_svr_test_type_entry *entries) |
| { |
| struct ble_att_svr_test_type_entry *entry; |
| struct ble_att_read_type_rsp rsp; |
| struct os_mbuf *om; |
| uint16_t handle; |
| uint8_t buf[512]; |
| int off; |
| int rc; |
| |
| om = ble_hs_test_util_prev_tx_dequeue_pullup(); |
| TEST_ASSERT(om); |
| |
| ble_att_read_type_rsp_parse(om->om_data, om->om_len, &rsp); |
| |
| off = BLE_ATT_READ_TYPE_RSP_BASE_SZ; |
| for (entry = entries; entry->handle != 0; entry++) { |
| TEST_ASSERT_FATAL(rsp.batp_length == |
| BLE_ATT_READ_TYPE_ADATA_BASE_SZ + entry->value_len); |
| |
| rc = os_mbuf_copydata(om, off, 2, &handle); |
| TEST_ASSERT(rc == 0); |
| handle = get_le16(&handle); |
| TEST_ASSERT(handle == entry->handle); |
| off += 2; |
| |
| rc = os_mbuf_copydata(om, off, entry->value_len, buf); |
| TEST_ASSERT(rc == 0); |
| TEST_ASSERT(memcmp(entry->value, buf, entry->value_len) == 0); |
| off += entry->value_len; |
| } |
| |
| /* Ensure there is no extra data in the response. */ |
| TEST_ASSERT(off == OS_MBUF_PKTLEN(om)); |
| } |
| |
| static void |
| ble_att_svr_test_misc_verify_tx_prep_write_rsp(uint16_t attr_handle, |
| uint16_t offset, |
| void *data, int data_len) |
| { |
| struct ble_att_prep_write_cmd rsp; |
| struct os_mbuf *om; |
| uint8_t buf[1024]; |
| int rc; |
| |
| om = ble_hs_test_util_prev_tx_dequeue(); |
| |
| rc = os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), buf); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| ble_att_prep_write_rsp_parse(buf, sizeof buf, &rsp); |
| |
| TEST_ASSERT(rsp.bapc_handle == attr_handle); |
| TEST_ASSERT(rsp.bapc_offset == offset); |
| TEST_ASSERT(memcmp(buf + BLE_ATT_PREP_WRITE_CMD_BASE_SZ, data, |
| data_len) == 0); |
| |
| TEST_ASSERT(OS_MBUF_PKTLEN(om) == |
| BLE_ATT_PREP_WRITE_CMD_BASE_SZ + data_len); |
| } |
| |
| static void |
| ble_att_svr_test_misc_verify_tx_exec_write_rsp(void) |
| { |
| struct os_mbuf *om; |
| |
| om = ble_hs_test_util_prev_tx_dequeue_pullup(); |
| TEST_ASSERT(om); |
| |
| ble_att_exec_write_rsp_parse(om->om_data, om->om_len); |
| } |
| |
| static void |
| ble_att_svr_test_misc_mtu_exchange(uint16_t my_mtu, uint16_t peer_sent, |
| uint16_t peer_actual, uint16_t chan_mtu) |
| { |
| struct ble_att_mtu_cmd req; |
| struct ble_l2cap_chan *chan; |
| struct ble_hs_conn *conn; |
| uint16_t conn_handle; |
| uint8_t buf[BLE_ATT_MTU_CMD_SZ]; |
| int rc; |
| |
| conn_handle = ble_att_svr_test_misc_init(my_mtu); |
| |
| req.bamc_mtu = peer_sent; |
| ble_att_mtu_req_write(buf, sizeof buf, &req); |
| |
| rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, |
| buf, sizeof buf); |
| TEST_ASSERT(rc == 0); |
| |
| ble_att_svr_test_misc_verify_tx_mtu_rsp(conn_handle); |
| |
| ble_hs_lock(); |
| rc = ble_hs_misc_conn_chan_find(conn_handle, BLE_L2CAP_CID_ATT, |
| &conn, &chan); |
| TEST_ASSERT_FATAL(rc == 0); |
| TEST_ASSERT(chan->peer_mtu == peer_actual); |
| TEST_ASSERT(ble_att_chan_mtu(chan) == chan_mtu); |
| ble_hs_unlock(); |
| |
| } |
| |
| static void |
| ble_att_svr_test_misc_prep_write(uint16_t conn_handle, uint16_t attr_handle, |
| uint16_t offset, void *data, |
| int data_len, uint8_t error_code) |
| { |
| int rc; |
| |
| rc = ble_hs_test_util_rx_att_prep_write_req(conn_handle, attr_handle, |
| offset, data, data_len); |
| if (error_code == 0) { |
| TEST_ASSERT(rc == 0); |
| ble_att_svr_test_misc_verify_tx_prep_write_rsp(attr_handle, offset, |
| data, data_len); |
| } else { |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_PREP_WRITE_REQ, |
| attr_handle, error_code); |
| } |
| } |
| |
| static void |
| ble_att_svr_test_misc_exec_write(uint16_t conn_handle, uint8_t flags, |
| uint8_t error_code, uint16_t error_handle) |
| { |
| int rc; |
| |
| rc = ble_hs_test_util_rx_att_exec_write_req(conn_handle, flags); |
| if (error_code == 0) { |
| TEST_ASSERT(rc == 0); |
| ble_att_svr_test_misc_verify_tx_exec_write_rsp(); |
| } else { |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_EXEC_WRITE_REQ, |
| error_handle, error_code); |
| } |
| } |
| |
| static void |
| ble_att_svr_test_misc_rx_notify(uint16_t conn_handle, uint16_t attr_handle, |
| void *attr_val, int attr_len, int good) |
| { |
| struct ble_att_notify_req req; |
| uint8_t buf[1024]; |
| int off; |
| int rc; |
| |
| req.banq_handle = attr_handle; |
| ble_att_notify_req_write(buf, sizeof buf, &req); |
| off = BLE_ATT_NOTIFY_REQ_BASE_SZ; |
| |
| memcpy(buf + off, attr_val, attr_len); |
| off += attr_len; |
| |
| rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, |
| buf, off); |
| if (good) { |
| TEST_ASSERT(rc == 0); |
| } else { |
| TEST_ASSERT(rc == BLE_HS_EBADDATA); |
| } |
| } |
| |
| static void |
| ble_att_svr_test_misc_verify_notify(uint16_t conn_handle, uint16_t attr_handle, |
| void *attr_val, int attr_len, int good) |
| { |
| ble_att_svr_test_n_conn_handle = 0xffff; |
| ble_att_svr_test_n_attr_handle = 0; |
| ble_att_svr_test_attr_n_len = 0; |
| |
| ble_att_svr_test_misc_rx_notify(conn_handle, attr_handle, attr_val, |
| attr_len, good); |
| |
| if (good) { |
| TEST_ASSERT(ble_att_svr_test_n_conn_handle == conn_handle); |
| TEST_ASSERT(ble_att_svr_test_n_attr_handle == attr_handle); |
| TEST_ASSERT(ble_att_svr_test_attr_n_len == attr_len); |
| TEST_ASSERT(memcmp(ble_att_svr_test_attr_n, attr_val, attr_len) == 0); |
| } else { |
| TEST_ASSERT(ble_att_svr_test_n_conn_handle == 0xffff); |
| TEST_ASSERT(ble_att_svr_test_n_attr_handle == 0); |
| TEST_ASSERT(ble_att_svr_test_attr_n_len == 0); |
| } |
| } |
| |
| static void |
| ble_att_svr_test_misc_verify_tx_indicate_rsp(void) |
| { |
| struct os_mbuf *om; |
| |
| om = ble_hs_test_util_prev_tx_dequeue_pullup(); |
| TEST_ASSERT(om); |
| |
| ble_att_indicate_rsp_parse(om->om_data, om->om_len); |
| } |
| |
| static void |
| ble_att_svr_test_misc_rx_indicate(uint16_t conn_handle, uint16_t attr_handle, |
| void *attr_val, int attr_len, int good) |
| { |
| int rc; |
| |
| rc = ble_hs_test_util_rx_att_indicate_req(conn_handle, attr_handle, |
| attr_val, attr_len); |
| if (good) { |
| TEST_ASSERT(rc == 0); |
| } else { |
| TEST_ASSERT(rc == BLE_HS_EBADDATA); |
| } |
| } |
| |
| static void |
| ble_att_svr_test_misc_verify_indicate(uint16_t conn_handle, |
| uint16_t attr_handle, |
| void *attr_val, int attr_len, int good) |
| { |
| ble_att_svr_test_n_conn_handle = 0xffff; |
| ble_att_svr_test_n_attr_handle = 0; |
| ble_att_svr_test_attr_n_len = 0; |
| |
| ble_att_svr_test_misc_rx_indicate(conn_handle, attr_handle, attr_val, |
| attr_len, good); |
| |
| if (good) { |
| TEST_ASSERT(ble_att_svr_test_n_conn_handle == conn_handle); |
| TEST_ASSERT(ble_att_svr_test_n_attr_handle == attr_handle); |
| TEST_ASSERT(ble_att_svr_test_attr_n_len == attr_len); |
| TEST_ASSERT(memcmp(ble_att_svr_test_attr_n, attr_val, attr_len) == 0); |
| ble_att_svr_test_misc_verify_tx_indicate_rsp(); |
| } else { |
| TEST_ASSERT(ble_att_svr_test_n_conn_handle == 0xffff); |
| TEST_ASSERT(ble_att_svr_test_n_attr_handle == 0); |
| TEST_ASSERT(ble_att_svr_test_attr_n_len == 0); |
| TEST_ASSERT(ble_hs_test_util_prev_tx_queue_sz() == 0); |
| } |
| } |
| |
| TEST_CASE(ble_att_svr_test_mtu) |
| { |
| /*** MTU too low; should pretend peer sent default value instead. */ |
| ble_att_svr_test_misc_mtu_exchange(BLE_ATT_MTU_DFLT, 5, |
| BLE_ATT_MTU_DFLT, BLE_ATT_MTU_DFLT); |
| |
| /*** MTUs equal. */ |
| ble_att_svr_test_misc_mtu_exchange(50, 50, 50, 50); |
| |
| /*** Peer's higher than mine. */ |
| ble_att_svr_test_misc_mtu_exchange(50, 100, 100, 50); |
| |
| /*** Mine higher than peer's. */ |
| ble_att_svr_test_misc_mtu_exchange(100, 50, 50, 50); |
| } |
| |
| TEST_CASE(ble_att_svr_test_read) |
| { |
| struct ble_hs_conn *conn; |
| struct os_mbuf *om; |
| uint16_t attr_handle; |
| uint16_t conn_handle; |
| const ble_uuid_t *uuid_sec = BLE_UUID128_DECLARE( \ |
| 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); |
| const ble_uuid_t *uuid_bad = BLE_UUID128_DECLARE( \ |
| 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); |
| const ble_uuid_t *uuid = BLE_UUID128_DECLARE( \ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ); |
| int rc; |
| |
| conn_handle = ble_att_svr_test_misc_init(0); |
| |
| /*** Nonexistent attribute. */ |
| attr_handle = 0; |
| rc = ble_hs_test_util_rx_att_read_req(conn_handle, attr_handle); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_READ_REQ, 0, |
| BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** Application error. */ |
| rc = ble_att_svr_register(uuid_bad, HA_FLAG_PERM_RW, 0, &attr_handle, |
| ble_att_svr_test_misc_attr_fn_r_err, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_hs_test_util_rx_att_read_req(conn_handle, attr_handle); |
| TEST_ASSERT(rc == BLE_HS_EAPP); |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_READ_REQ, attr_handle, |
| BLE_ATT_ERR_UNLIKELY); |
| |
| /*** Successful read. */ |
| ble_att_svr_test_attr_r_1 = (uint8_t[]){0,1,2,3,4,5,6,7}; |
| ble_att_svr_test_attr_r_1_len = 8; |
| rc = ble_att_svr_register(uuid, HA_FLAG_PERM_RW, 0, &attr_handle, |
| ble_att_svr_test_misc_attr_fn_r_1, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_hs_test_util_rx_att_read_req(conn_handle, attr_handle); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_read_rsp( |
| ble_att_svr_test_attr_r_1, ble_att_svr_test_attr_r_1_len); |
| |
| /*** Partial read. */ |
| ble_att_svr_test_attr_r_1 = |
| (uint8_t[]){0,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}; |
| ble_att_svr_test_attr_r_1_len = 40; |
| |
| rc = ble_hs_test_util_rx_att_read_req(conn_handle, attr_handle); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_read_rsp(ble_att_svr_test_attr_r_1, |
| BLE_ATT_MTU_DFLT - 1); |
| |
| /*** Read requires encryption. */ |
| /* Insufficient authentication. */ |
| rc = ble_att_svr_register(uuid_sec, BLE_ATT_F_READ | BLE_ATT_F_READ_ENC, 0, |
| &attr_handle, |
| ble_att_svr_test_misc_attr_fn_r_1, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_hs_test_util_rx_att_read_req(conn_handle, attr_handle); |
| TEST_ASSERT(rc == BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN)); |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_READ_REQ, attr_handle, |
| BLE_ATT_ERR_INSUFFICIENT_AUTHEN); |
| |
| /* Security check bypassed for local reads. */ |
| rc = ble_att_svr_read_local(attr_handle, &om); |
| TEST_ASSERT_FATAL(rc == 0); |
| TEST_ASSERT(OS_MBUF_PKTLEN(om) == ble_att_svr_test_attr_r_1_len); |
| TEST_ASSERT(os_mbuf_cmpf(om, 0, ble_att_svr_test_attr_r_1, |
| ble_att_svr_test_attr_r_1_len) == 0); |
| rc = os_mbuf_free_chain(om); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| /* Ensure no response got sent. */ |
| TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL); |
| |
| /* Encrypt link; success. */ |
| ble_hs_lock(); |
| conn = ble_hs_conn_find(conn_handle); |
| conn->bhc_sec_state.encrypted = 1; |
| ble_hs_unlock(); |
| |
| rc = ble_hs_test_util_rx_att_read_req(conn_handle, attr_handle); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_read_rsp(ble_att_svr_test_attr_r_1, |
| BLE_ATT_MTU_DFLT - 1); |
| } |
| |
| TEST_CASE(ble_att_svr_test_read_blob) |
| { |
| uint16_t attr_handle; |
| uint16_t conn_handle; |
| const ble_uuid_t *uuid = BLE_UUID128_DECLARE( \ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); |
| int rc; |
| |
| conn_handle = ble_att_svr_test_misc_init(0); |
| |
| /*** Nonexistent attribute. */ |
| rc = ble_hs_test_util_rx_att_read_blob_req(conn_handle, 0, 0); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_READ_BLOB_REQ, 0, |
| BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** Successful partial read. */ |
| ble_att_svr_test_attr_r_1 = |
| (uint8_t[]){0,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}; |
| ble_att_svr_test_attr_r_1_len = 40; |
| rc = ble_att_svr_register(uuid, HA_FLAG_PERM_RW, 0, &attr_handle, |
| ble_att_svr_test_misc_attr_fn_r_1, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_hs_test_util_rx_att_read_blob_req(conn_handle, attr_handle, 0); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_read_blob_rsp(ble_att_svr_test_attr_r_1, |
| BLE_ATT_MTU_DFLT - 1); |
| |
| /*** Read remainder of attribute. */ |
| rc = ble_hs_test_util_rx_att_read_blob_req(conn_handle, attr_handle, |
| BLE_ATT_MTU_DFLT - 1); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_read_blob_rsp( |
| ble_att_svr_test_attr_r_1 + BLE_ATT_MTU_DFLT - 1, |
| 40 - (BLE_ATT_MTU_DFLT - 1)); |
| |
| /*** Zero-length read. */ |
| rc = ble_hs_test_util_rx_att_read_blob_req(conn_handle, attr_handle, |
| ble_att_svr_test_attr_r_1_len); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_read_blob_rsp(ble_att_svr_test_attr_r_1, |
| 0); |
| } |
| |
| TEST_CASE(ble_att_svr_test_read_mult) |
| { |
| uint16_t conn_handle; |
| int rc; |
| |
| conn_handle = ble_att_svr_test_misc_init(0); |
| |
| struct ble_hs_test_util_flat_attr attrs[2] = { |
| { |
| .handle = 0, |
| .offset = 0, |
| .value = { 1, 2, 3, 4 }, |
| .value_len = 4, |
| }, |
| { |
| .handle = 0, |
| .offset = 0, |
| .value = { 2, 3, 4, 5, 6 }, |
| .value_len = 5, |
| }, |
| }; |
| |
| ble_att_svr_test_attr_r_1 = attrs[0].value; |
| ble_att_svr_test_attr_r_1_len = attrs[0].value_len; |
| ble_att_svr_test_attr_r_2 = attrs[1].value; |
| ble_att_svr_test_attr_r_2_len = attrs[1].value_len; |
| |
| rc = ble_att_svr_register(BLE_UUID16_DECLARE(0x1111), HA_FLAG_PERM_RW, 0, |
| &attrs[0].handle, |
| ble_att_svr_test_misc_attr_fn_r_1, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_att_svr_register(BLE_UUID16_DECLARE(0x2222), HA_FLAG_PERM_RW, 0, |
| &attrs[1].handle, |
| ble_att_svr_test_misc_attr_fn_r_2, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| /*** Single nonexistent attribute. */ |
| ble_att_svr_test_misc_rx_read_mult_req( |
| conn_handle, ((uint16_t[]){ 100 }), 1, 0); |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_READ_MULT_REQ, |
| 100, BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** Single attribute. */ |
| ble_att_svr_test_misc_verify_all_read_mult(conn_handle, &attrs[0], 1); |
| |
| /*** Two attributes. */ |
| ble_att_svr_test_misc_verify_all_read_mult(conn_handle, attrs, 2); |
| |
| /*** Reverse order. */ |
| ble_att_svr_test_misc_verify_all_read_mult(conn_handle, attrs, 2); |
| |
| /*** Second attribute nonexistent; verify only error txed. */ |
| ble_att_svr_test_misc_rx_read_mult_req( |
| conn_handle, ((uint16_t[]){ attrs[0].handle, 100 }), 2, 0); |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_READ_MULT_REQ, |
| 100, BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** Response too long; verify only MTU bytes sent. */ |
| attrs[0].value_len = 20; |
| memcpy(attrs[0].value, |
| ((uint8_t[]){0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}), |
| attrs[0].value_len); |
| ble_att_svr_test_attr_r_1_len = attrs[0].value_len; |
| |
| attrs[1].value_len = 20; |
| memcpy(attrs[1].value, |
| ((uint8_t[]){ |
| 22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39 |
| }), |
| attrs[1].value_len); |
| ble_att_svr_test_attr_r_2_len = attrs[1].value_len; |
| |
| ble_att_svr_test_misc_verify_all_read_mult(conn_handle, attrs, 2); |
| |
| } |
| |
| TEST_CASE(ble_att_svr_test_write) |
| { |
| struct ble_hs_conn *conn; |
| uint16_t conn_handle; |
| uint16_t attr_handle; |
| const ble_uuid_t *uuid_sec = BLE_UUID128_DECLARE( \ |
| 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); |
| const ble_uuid_t *uuid_rw = BLE_UUID128_DECLARE( \ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); |
| const ble_uuid_t *uuid_r = BLE_UUID128_DECLARE( \ |
| 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); |
| int rc; |
| |
| static const uint8_t attr_val[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; |
| |
| conn_handle = ble_att_svr_test_misc_init(0); |
| |
| /*** Nonexistent attribute. */ |
| rc = ble_hs_test_util_rx_att_write_req(conn_handle, 0, |
| attr_val, sizeof attr_val); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_WRITE_REQ, 0, BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** Write not permitted if non-local. */ |
| /* Non-local write (fail). */ |
| rc = ble_att_svr_register(uuid_r, BLE_ATT_F_READ, 0, &attr_handle, |
| ble_att_svr_test_misc_attr_fn_w_1, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_hs_test_util_rx_att_write_req(conn_handle, attr_handle, |
| attr_val, sizeof attr_val); |
| TEST_ASSERT(rc == BLE_HS_EREJECT); |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_WRITE_REQ, |
| attr_handle, |
| BLE_ATT_ERR_WRITE_NOT_PERMITTED); |
| |
| /* Local write (success). */ |
| rc = ble_hs_test_util_write_local_flat(attr_handle, |
| attr_val, sizeof attr_val); |
| TEST_ASSERT(rc == 0); |
| |
| /* Ensure no response got sent. */ |
| TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL); |
| |
| /*** Successful write. */ |
| rc = ble_att_svr_register(uuid_rw, HA_FLAG_PERM_RW, 0, &attr_handle, |
| ble_att_svr_test_misc_attr_fn_w_1, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_hs_test_util_rx_att_write_req(conn_handle, attr_handle, |
| attr_val, sizeof attr_val); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_write_rsp(); |
| |
| /*** Write requires encryption. */ |
| /* Insufficient authentication. */ |
| rc = ble_att_svr_register(uuid_sec, BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_ENC, |
| 0, &attr_handle, |
| ble_att_svr_test_misc_attr_fn_w_1, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_hs_test_util_rx_att_write_req(conn_handle, attr_handle, |
| attr_val, sizeof attr_val); |
| TEST_ASSERT(rc == BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN)); |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_WRITE_REQ, |
| attr_handle, |
| BLE_ATT_ERR_INSUFFICIENT_AUTHEN); |
| |
| /* Security check bypassed for local writes. */ |
| rc = ble_hs_test_util_write_local_flat(attr_handle, |
| attr_val, sizeof attr_val); |
| TEST_ASSERT(rc == 0); |
| |
| /* Ensure no response got sent. */ |
| TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL); |
| |
| /* Encrypt link; success. */ |
| ble_hs_lock(); |
| conn = ble_hs_conn_find(conn_handle); |
| conn->bhc_sec_state.encrypted = 1; |
| ble_hs_unlock(); |
| |
| rc = ble_hs_test_util_rx_att_write_req(conn_handle, attr_handle, |
| attr_val, sizeof attr_val); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_write_rsp(); |
| } |
| |
| TEST_CASE(ble_att_svr_test_find_info) |
| { |
| uint16_t conn_handle; |
| uint16_t handle1; |
| uint16_t handle2; |
| uint16_t handle3; |
| const ble_uuid_t *uuid1 = |
| BLE_UUID128_DECLARE(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,11, 12, 13, 14, 15); |
| const ble_uuid_t *uuid2 = |
| BLE_UUID128_DECLARE(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); |
| const ble_uuid_t *uuid3 = BLE_UUID16_DECLARE(0x000f); |
| int rc; |
| |
| conn_handle = ble_att_svr_test_misc_init(128); |
| |
| /*** Start handle of 0. */ |
| rc = ble_hs_test_util_rx_att_find_info_req(conn_handle, 0, 0); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_FIND_INFO_REQ, 0, BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** Start handle > end handle. */ |
| rc = ble_hs_test_util_rx_att_find_info_req(conn_handle, 101, 100); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_FIND_INFO_REQ, 101, BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** No attributes. */ |
| rc = ble_hs_test_util_rx_att_find_info_req(conn_handle, 200, 300); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_FIND_INFO_REQ, 200, BLE_ATT_ERR_ATTR_NOT_FOUND); |
| |
| /*** Range too late. */ |
| rc = ble_att_svr_register(uuid1, HA_FLAG_PERM_RW, 0, &handle1, |
| ble_att_svr_test_misc_attr_fn_r_1, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_hs_test_util_rx_att_find_info_req(conn_handle, 200, 300); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_FIND_INFO_REQ, 200, BLE_ATT_ERR_ATTR_NOT_FOUND); |
| |
| /*** One 128-bit entry. */ |
| rc = ble_hs_test_util_rx_att_find_info_req(conn_handle, handle1, handle1); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_find_info_rsp( |
| ((struct ble_hs_test_util_att_info_entry[]) { { |
| .handle = handle1, |
| .uuid = BLE_UUID128_DECLARE(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15), |
| }, { |
| .handle = 0, |
| } })); |
| |
| /*** Two 128-bit entries. */ |
| rc = ble_att_svr_register(uuid2, HA_FLAG_PERM_RW, 0, &handle2, |
| ble_att_svr_test_misc_attr_fn_r_1, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_hs_test_util_rx_att_find_info_req(conn_handle, handle1, handle2); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_find_info_rsp( |
| ((struct ble_hs_test_util_att_info_entry[]) { { |
| .handle = handle1, |
| .uuid = BLE_UUID128_DECLARE(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15), |
| }, { |
| .handle = handle2, |
| .uuid = BLE_UUID128_DECLARE(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16), |
| }, { |
| .handle = 0, |
| } })); |
| |
| /*** Two 128-bit entries; 16-bit entry doesn't get sent. */ |
| rc = ble_att_svr_register(uuid3, HA_FLAG_PERM_RW, 0, &handle3, |
| ble_att_svr_test_misc_attr_fn_r_1, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_hs_test_util_rx_att_find_info_req(conn_handle, handle1, handle3); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_find_info_rsp( |
| ((struct ble_hs_test_util_att_info_entry[]) { { |
| .handle = handle1, |
| .uuid = BLE_UUID128_DECLARE(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15), |
| }, { |
| .handle = handle2, |
| .uuid = BLE_UUID128_DECLARE(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16), |
| }, { |
| .handle = 0, |
| } })); |
| |
| /*** Remaining 16-bit entry requested. */ |
| rc = ble_hs_test_util_rx_att_find_info_req(conn_handle, handle3, handle3); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_find_info_rsp( |
| ((struct ble_hs_test_util_att_info_entry[]) { { |
| .handle = handle3, |
| .uuid = BLE_UUID16_DECLARE(0x000f), |
| }, { |
| .handle = 0, |
| } })); |
| |
| } |
| |
| TEST_CASE(ble_att_svr_test_find_type_value) |
| { |
| uint16_t conn_handle; |
| uint16_t handle1; |
| uint16_t handle2; |
| uint16_t handle3; |
| uint16_t handle4; |
| uint16_t handle5; |
| uint16_t handle_desc; |
| const ble_uuid_t *uuid1 = BLE_UUID16_DECLARE(0x2800); |
| const ble_uuid_t *uuid2 = BLE_UUID16_DECLARE(0x2803); |
| const ble_uuid_t *uuid3 = |
| BLE_UUID128_DECLARE(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); |
| int rc; |
| |
| conn_handle = ble_att_svr_test_misc_init(128); |
| |
| ble_att_svr_test_attr_r_1 = (uint8_t[]){0x99, 0x99}; |
| ble_att_svr_test_attr_r_1_len = 2; |
| |
| /*** Start handle of 0. */ |
| rc = ble_hs_test_util_rx_att_find_type_value_req( |
| conn_handle, 0, 0, 0x2800, ble_att_svr_test_attr_r_1, |
| ble_att_svr_test_attr_r_1_len); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_FIND_TYPE_VALUE_REQ, 0, |
| BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** Start handle > end handle. */ |
| rc = ble_hs_test_util_rx_att_find_type_value_req( |
| conn_handle, 101, 100, 0x2800, ble_att_svr_test_attr_r_1, |
| ble_att_svr_test_attr_r_1_len); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_FIND_TYPE_VALUE_REQ, 101, |
| BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** No attributes. */ |
| rc = ble_hs_test_util_rx_att_find_type_value_req( |
| conn_handle, 200, 300, 0x2800, ble_att_svr_test_attr_r_1, |
| ble_att_svr_test_attr_r_1_len); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_FIND_TYPE_VALUE_REQ, 200, |
| BLE_ATT_ERR_ATTR_NOT_FOUND); |
| |
| /*** Range too late. */ |
| rc = ble_att_svr_register(uuid1, HA_FLAG_PERM_RW, 0, &handle1, |
| ble_att_svr_test_misc_attr_fn_r_1, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_hs_test_util_rx_att_find_type_value_req( |
| conn_handle, 200, 300, 0x2800, ble_att_svr_test_attr_r_1, |
| ble_att_svr_test_attr_r_1_len); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_FIND_TYPE_VALUE_REQ, 200, |
| BLE_ATT_ERR_ATTR_NOT_FOUND); |
| |
| /*** One entry, one attribute. */ |
| rc = ble_hs_test_util_rx_att_find_type_value_req( |
| conn_handle, handle1, handle1, 0x2800, ble_att_svr_test_attr_r_1, |
| ble_att_svr_test_attr_r_1_len); |
| TEST_ASSERT(rc == 0); |
| ble_att_svr_test_misc_verify_tx_find_type_value_rsp( |
| ((struct ble_att_svr_test_type_value_entry[]) { { |
| .first = handle1, |
| .last = handle1, |
| }, { |
| .first = 0, |
| } })); |
| |
| /*** One entry, two attributes. */ |
| rc = ble_att_svr_register(uuid2, HA_FLAG_PERM_RW, 0, &handle2, |
| ble_att_svr_test_misc_attr_fn_r_1, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_hs_test_util_rx_att_find_type_value_req( |
| conn_handle, handle1, handle2, 0x2800, ble_att_svr_test_attr_r_1, |
| ble_att_svr_test_attr_r_1_len); |
| TEST_ASSERT(rc == 0); |
| ble_att_svr_test_misc_verify_tx_find_type_value_rsp( |
| ((struct ble_att_svr_test_type_value_entry[]) { { |
| .first = handle1, |
| .last = handle2, |
| }, { |
| .first = 0, |
| } })); |
| |
| /*** Entry 1: four attributes; entry 2 (invalid value): one attribute; |
| * entry 3: one attribute; Check that invalid value is not returned. */ |
| ble_att_svr_test_attr_r_2 = (uint8_t[]){0x00, 0x00}; |
| ble_att_svr_test_attr_r_2_len = 2; |
| |
| rc = ble_att_svr_register(uuid3, HA_FLAG_PERM_RW, 0, &handle_desc, |
| ble_att_svr_test_misc_attr_fn_r_2, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_att_svr_register(uuid2, HA_FLAG_PERM_RW, 0, &handle3, |
| ble_att_svr_test_misc_attr_fn_r_2, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_att_svr_register(uuid1, HA_FLAG_PERM_RW, 0, &handle4, |
| ble_att_svr_test_misc_attr_fn_r_2, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_att_svr_register(uuid1, HA_FLAG_PERM_RW, 0, &handle5, |
| ble_att_svr_test_misc_attr_fn_r_1, NULL); |
| TEST_ASSERT(rc == 0); |
| |
| rc = ble_hs_test_util_rx_att_find_type_value_req( |
| conn_handle, 0x0001, 0xffff, 0x2800, ble_att_svr_test_attr_r_1, |
| ble_att_svr_test_attr_r_1_len); |
| TEST_ASSERT(rc == 0); |
| ble_att_svr_test_misc_verify_tx_find_type_value_rsp( |
| ((struct ble_att_svr_test_type_value_entry[]) { { |
| .first = handle1, |
| .last = handle3, |
| }, { |
| .first = handle5, |
| .last = handle5, |
| }, { |
| .first = 0, |
| } })); |
| |
| /*** As above, check proper range is returned with smaller search range */ |
| rc = ble_hs_test_util_rx_att_find_type_value_req( |
| conn_handle, 0x0001, 0x0001, 0x2800, ble_att_svr_test_attr_r_1, |
| ble_att_svr_test_attr_r_1_len); |
| TEST_ASSERT(rc == 0); |
| ble_att_svr_test_misc_verify_tx_find_type_value_rsp( |
| ((struct ble_att_svr_test_type_value_entry[]) { { |
| .first = handle1, |
| .last = handle3, |
| }, { |
| .first = 0, |
| } })); |
| |
| /*** As above, check grouping by Characteristic UUID */ |
| rc = ble_hs_test_util_rx_att_find_type_value_req( |
| conn_handle, handle1, handle3, 0x2803, ble_att_svr_test_attr_r_1, |
| ble_att_svr_test_attr_r_1_len); |
| TEST_ASSERT(rc == 0); |
| ble_att_svr_test_misc_verify_tx_find_type_value_rsp( |
| ((struct ble_att_svr_test_type_value_entry[]) { { |
| .first = handle2, |
| .last = handle_desc, |
| }, { |
| .first = 0, |
| } })); |
| } |
| |
| static void |
| ble_att_svr_test_misc_read_type(uint16_t mtu) |
| { |
| uint16_t conn_handle; |
| int rc; |
| |
| conn_handle = ble_att_svr_test_misc_init(mtu); |
| |
| /*** Start handle of 0. */ |
| rc = ble_hs_test_util_rx_att_read_type_req16(conn_handle, 0, 0, |
| BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_READ_TYPE_REQ, 0, |
| BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** Start handle > end handle. */ |
| rc = ble_hs_test_util_rx_att_read_type_req16(conn_handle, 101, 100, |
| BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_READ_TYPE_REQ, 101, |
| BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** No attributes. */ |
| rc = ble_hs_test_util_rx_att_read_type_req16(conn_handle, 1, 0xffff, |
| BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_READ_TYPE_REQ, 1, |
| BLE_ATT_ERR_ATTR_NOT_FOUND); |
| |
| /*** Range too late. */ |
| ble_att_svr_test_misc_register_group_attrs(); |
| rc = ble_hs_test_util_rx_att_read_type_req16(conn_handle, 200, 300, |
| BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_READ_TYPE_REQ, 200, |
| BLE_ATT_ERR_ATTR_NOT_FOUND); |
| |
| /*** One characteristic from one service. */ |
| rc = ble_hs_test_util_rx_att_read_type_req16(conn_handle, 1, 2, |
| BLE_ATT_UUID_CHARACTERISTIC); |
| TEST_ASSERT(rc == 0); |
| ble_att_svr_test_misc_verify_tx_read_type_rsp( |
| ((struct ble_att_svr_test_type_entry[]) { { |
| .handle = 2, |
| .value = (uint8_t[]){ 0x01, 0x11 }, |
| .value_len = 2, |
| }, { |
| .handle = 0, |
| } })); |
| |
| /*** Both characteristics from one service. */ |
| rc = ble_hs_test_util_rx_att_read_type_req16(conn_handle, 1, 10, |
| BLE_ATT_UUID_CHARACTERISTIC); |
| TEST_ASSERT(rc == 0); |
| ble_att_svr_test_misc_verify_tx_read_type_rsp( |
| ((struct ble_att_svr_test_type_entry[]) { { |
| .handle = 2, |
| .value = (uint8_t[]){ 0x01, 0x11 }, |
| .value_len = 2, |
| }, { |
| .handle = 4, |
| .value = (uint8_t[]){ 0x03, 0x11 }, |
| .value_len = 2, |
| }, { |
| .handle = 0, |
| } })); |
| |
| /*** Ensure 16-bit and 128-bit values are retrieved separately. */ |
| rc = ble_hs_test_util_rx_att_read_type_req16(conn_handle, 11, 0xffff, |
| BLE_ATT_UUID_CHARACTERISTIC); |
| TEST_ASSERT(rc == 0); |
| ble_att_svr_test_misc_verify_tx_read_type_rsp( |
| ((struct ble_att_svr_test_type_entry[]) { { |
| .handle = 12, |
| .value = (uint8_t[]){ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }, |
| .value_len = 16, |
| }, { |
| .handle = 0, |
| } })); |
| |
| rc = ble_hs_test_util_rx_att_read_type_req16(conn_handle, 13, 0xffff, |
| BLE_ATT_UUID_CHARACTERISTIC); |
| TEST_ASSERT(rc == 0); |
| ble_att_svr_test_misc_verify_tx_read_type_rsp( |
| ((struct ble_att_svr_test_type_entry[]) { { |
| .handle = 14, |
| .value = (uint8_t[]){ 0x55, 0x55 }, |
| .value_len = 2, |
| }, { |
| .handle = 0, |
| } })); |
| |
| rc = ble_hs_test_util_rx_att_read_type_req16(conn_handle, 15, 0xffff, |
| BLE_ATT_UUID_CHARACTERISTIC); |
| TEST_ASSERT(rc == 0); |
| ble_att_svr_test_misc_verify_tx_read_type_rsp( |
| ((struct ble_att_svr_test_type_entry[]) { { |
| .handle = 16, |
| .value = (uint8_t[]){ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 }, |
| .value_len = 16, |
| }, { |
| .handle = 0, |
| } })); |
| |
| /*** Read until the end of the attribute list. */ |
| rc = ble_hs_test_util_rx_att_read_type_req16(conn_handle, 17, 0xffff, |
| BLE_ATT_UUID_CHARACTERISTIC); |
| TEST_ASSERT(rc == 0); |
| ble_att_svr_test_misc_verify_tx_read_type_rsp( |
| ((struct ble_att_svr_test_type_entry[]) { { |
| .handle = 18, |
| .value = (uint8_t[]){ 0x66, 0x66 }, |
| .value_len = 2, |
| }, { |
| .handle = 20, |
| .value = (uint8_t[]){ 0x77, 0x77 }, |
| .value_len = 2, |
| }, { |
| .handle = 22, |
| .value = (uint8_t[]){ 0x88, 0x88 }, |
| .value_len = 2, |
| }, { |
| .handle = 24, |
| .value = (uint8_t[]){ 0x99, 0x99 }, |
| .value_len = 2, |
| }, { |
| .handle = 0, |
| } })); |
| |
| } |
| |
| TEST_CASE(ble_att_svr_test_read_type) |
| { |
| ble_att_svr_test_misc_read_type(0); |
| ble_att_svr_test_misc_read_type(128); |
| } |
| |
| TEST_CASE(ble_att_svr_test_read_group_type) |
| { |
| uint16_t conn_handle; |
| int rc; |
| |
| conn_handle = ble_att_svr_test_misc_init(128); |
| |
| /*** Start handle of 0. */ |
| rc = ble_hs_test_util_rx_att_read_group_type_req16( |
| conn_handle, 0, 0, BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_READ_GROUP_TYPE_REQ, 0, |
| BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** Start handle > end handle. */ |
| rc = ble_hs_test_util_rx_att_read_group_type_req16( |
| conn_handle, 101, 100, BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_READ_GROUP_TYPE_REQ, 101, |
| BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** Invalid group UUID (0x1234). */ |
| rc = ble_hs_test_util_rx_att_read_group_type_req16( |
| conn_handle, 110, 150, 0x1234); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_READ_GROUP_TYPE_REQ, 110, |
| BLE_ATT_ERR_UNSUPPORTED_GROUP); |
| |
| /*** No attributes. */ |
| rc = ble_hs_test_util_rx_att_read_group_type_req16( |
| conn_handle, 1, 0xffff, BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_READ_GROUP_TYPE_REQ, 1, |
| BLE_ATT_ERR_ATTR_NOT_FOUND); |
| |
| /*** Range too late. */ |
| ble_att_svr_test_misc_register_group_attrs(); |
| |
| rc = ble_hs_test_util_rx_att_read_group_type_req16( |
| conn_handle, 200, 300, BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp( |
| BLE_ATT_OP_READ_GROUP_TYPE_REQ, 200, |
| BLE_ATT_ERR_ATTR_NOT_FOUND); |
| |
| /*** One 16-bit UUID service. */ |
| rc = ble_hs_test_util_rx_att_read_group_type_req16( |
| conn_handle, 1, 5, BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_read_group_type_rsp( |
| ((struct ble_hs_test_util_att_group_type_entry[]) { { |
| .start_handle = 1, |
| .end_handle = 5, |
| .uuid = BLE_UUID16_DECLARE(0x1122), |
| }, { |
| .start_handle = 0, |
| } })); |
| |
| /*** Two 16-bit UUID services. */ |
| rc = ble_hs_test_util_rx_att_read_group_type_req16( |
| conn_handle, 1, 10, BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_read_group_type_rsp( |
| ((struct ble_hs_test_util_att_group_type_entry[]) { { |
| .start_handle = 1, |
| .end_handle = 5, |
| .uuid = BLE_UUID16_DECLARE(0x1122), |
| }, { |
| .start_handle = 6, |
| .end_handle = 10, |
| .uuid = BLE_UUID16_DECLARE(0x2233), |
| }, { |
| .start_handle = 0, |
| } })); |
| |
| /*** Two 16-bit UUID services; ensure 128-bit service not returned. */ |
| rc = ble_hs_test_util_rx_att_read_group_type_req16( |
| conn_handle, 1, 100, BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_read_group_type_rsp( |
| ((struct ble_hs_test_util_att_group_type_entry[]) { { |
| .start_handle = 1, |
| .end_handle = 5, |
| .uuid = BLE_UUID16_DECLARE(0x1122), |
| }, { |
| .start_handle = 6, |
| .end_handle = 10, |
| .uuid = BLE_UUID16_DECLARE(0x2233), |
| }, { |
| .start_handle = 0, |
| } })); |
| |
| /*** One 128-bit service. */ |
| rc = ble_hs_test_util_rx_att_read_group_type_req16( |
| conn_handle, 11, 100, BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT(rc == 0); |
| ble_hs_test_util_verify_tx_read_group_type_rsp( |
| ((struct ble_hs_test_util_att_group_type_entry[]) { { |
| .start_handle = 11, |
| .end_handle = 0xffff, |
| .uuid = BLE_UUID128_DECLARE(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16), |
| }, { |
| .start_handle = 0, |
| } })); |
| |
| } |
| |
| TEST_CASE(ble_att_svr_test_prep_write) |
| { |
| struct ble_hs_conn *conn; |
| uint16_t conn_handle; |
| int i; |
| |
| static uint8_t data[1024]; |
| |
| conn_handle = ble_att_svr_test_misc_init(205); |
| |
| /* Initialize some attribute data. */ |
| for (i = 0; i < sizeof data; i++) { |
| data[i] = i; |
| } |
| |
| /* Register two writable attributes. */ |
| ble_att_svr_test_misc_register_uuid(BLE_UUID16_DECLARE(0x1234), |
| HA_FLAG_PERM_RW, 1, |
| ble_att_svr_test_misc_attr_fn_w_1); |
| ble_att_svr_test_misc_register_uuid(BLE_UUID16_DECLARE(0x8989), |
| HA_FLAG_PERM_RW, 2, |
| ble_att_svr_test_misc_attr_fn_w_2); |
| |
| /* 3: not writable. */ |
| ble_att_svr_test_misc_register_uuid(BLE_UUID16_DECLARE(0xabab), |
| BLE_ATT_F_READ, 3, |
| ble_att_svr_test_misc_attr_fn_r_1); |
| /* 4: Encryption required. */ |
| ble_att_svr_test_misc_register_uuid( |
| BLE_UUID16_DECLARE(0xabac), BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_ENC, 4, |
| ble_att_svr_test_misc_attr_fn_w_1); |
| |
| /* 5: Encryption+authentication required. */ |
| ble_att_svr_test_misc_register_uuid( |
| BLE_UUID16_DECLARE(0xabad), |
| BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_ENC | BLE_ATT_F_WRITE_AUTHEN, |
| 5, ble_att_svr_test_misc_attr_fn_w_1); |
| |
| /* 6: Write callback always fails. */ |
| ble_att_svr_test_misc_register_uuid( |
| BLE_UUID16_DECLARE(0xabae), BLE_ATT_F_WRITE, 6, |
| ble_att_svr_test_misc_attr_fn_w_fail); |
| |
| /*** Empty write succeeds. */ |
| ble_att_svr_test_misc_exec_write(conn_handle, BLE_ATT_EXEC_WRITE_F_EXECUTE, |
| 0, 0); |
| |
| /*** Empty cancel succeeds. */ |
| ble_att_svr_test_misc_exec_write(conn_handle, 0, 0, 0); |
| |
| /*** Failure for prep write to nonexistent attribute. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 53525, 0, data, 10, |
| BLE_ATT_ERR_INVALID_HANDLE); |
| |
| /*** Failure due to write-not-permitted. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 3, 0, data, 35, |
| BLE_ATT_ERR_WRITE_NOT_PERMITTED); |
| |
| /*** Failure due to insufficient authentication (encryption required). */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 4, 0, data, 1, |
| BLE_ATT_ERR_INSUFFICIENT_AUTHEN); |
| |
| /*** Encrypt connection; ensure previous prep write now succeeds. */ |
| ble_hs_lock(); |
| conn = ble_hs_conn_find(2); |
| TEST_ASSERT_FATAL(conn != NULL); |
| conn->bhc_sec_state.encrypted = 1; |
| ble_hs_unlock(); |
| |
| ble_att_svr_test_misc_prep_write(conn_handle, 4, 0, data, 1, 0); |
| ble_att_svr_test_misc_exec_write(conn_handle, 0, 0, 0); |
| |
| /*** Failure due to insufficient authentication (not authenticated). */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 5, 0, data, 35, |
| BLE_ATT_ERR_INSUFFICIENT_AUTHEN); |
| |
| /*** Failure for write starting at nonzero offset. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 1, data, 10, 0); |
| ble_att_svr_test_misc_exec_write(conn_handle, BLE_ATT_EXEC_WRITE_F_EXECUTE, |
| BLE_ATT_ERR_INVALID_OFFSET, 1); |
| ble_att_svr_test_misc_verify_w_1(NULL, 0); |
| |
| /*** Success for clear starting at nonzero offset. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 1, data, 10, 0); |
| ble_att_svr_test_misc_exec_write(conn_handle, 0, 0, 0); |
| ble_att_svr_test_misc_verify_w_1(NULL, 0); |
| |
| /*** Failure for write with gap. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 0, data, 10, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 11, data, 10, 0); |
| ble_att_svr_test_misc_exec_write(conn_handle, BLE_ATT_EXEC_WRITE_F_EXECUTE, |
| BLE_ATT_ERR_INVALID_OFFSET, 1); |
| ble_att_svr_test_misc_verify_w_1(NULL, 0); |
| |
| /*** Success for clear with gap. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 0, data, 10, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 11, data, 10, 0); |
| ble_att_svr_test_misc_exec_write(conn_handle, 0, 0, 0); |
| ble_att_svr_test_misc_verify_w_1(NULL, 0); |
| |
| /*** Failure for overlong write. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 0, data, 200, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 200, data + 200, 200, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 400, data + 400, 200, 0); |
| ble_att_svr_test_misc_exec_write(conn_handle, BLE_ATT_EXEC_WRITE_F_EXECUTE, |
| BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN, 1); |
| ble_att_svr_test_misc_verify_w_1(NULL, 0); |
| |
| /*** Successful two part write. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 0, data, 20, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 20, data + 20, 20, 0); |
| ble_att_svr_test_misc_exec_write(conn_handle, BLE_ATT_EXEC_WRITE_F_EXECUTE, |
| 0, 0); |
| ble_att_svr_test_misc_verify_w_1(data, 40); |
| |
| /*** Successful three part write. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 0, data, 35, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 35, data + 35, 43, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 78, data + 78, 1, 0); |
| ble_att_svr_test_misc_exec_write(conn_handle, BLE_ATT_EXEC_WRITE_F_EXECUTE, |
| 0, 0); |
| ble_att_svr_test_misc_verify_w_1(data, 79); |
| |
| /*** Successful two part write to two attributes. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 0, data, 7, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 7, data + 7, 10, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 2, 0, data, 20, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 2, 20, data + 20, 10, 0); |
| ble_att_svr_test_misc_exec_write(conn_handle, BLE_ATT_EXEC_WRITE_F_EXECUTE, |
| 0, 0); |
| ble_att_svr_test_misc_verify_w_1(data, 17); |
| ble_att_svr_test_misc_verify_w_2(data, 30); |
| |
| /*** Fail write to second attribute; ensure first write doesn't occur. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 0, data, 5, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 5, data + 5, 2, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 2, 0, data, 11, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 2, 12, data + 11, 19, 0); |
| ble_att_svr_test_misc_exec_write(conn_handle, BLE_ATT_EXEC_WRITE_F_EXECUTE, |
| BLE_ATT_ERR_INVALID_OFFSET, 2); |
| ble_att_svr_test_misc_verify_w_1(data, 17); |
| ble_att_svr_test_misc_verify_w_2(data, 30); |
| |
| /*** Successful out of order write to two attributes. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 0, data, 9, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 2, 0, data, 18, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 9, data + 9, 3, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 2, 18, data + 18, 43, 0); |
| ble_att_svr_test_misc_exec_write(conn_handle, BLE_ATT_EXEC_WRITE_F_EXECUTE, |
| 0, 0); |
| ble_att_svr_test_misc_verify_w_1(data, 12); |
| ble_att_svr_test_misc_verify_w_2(data, 61); |
| |
| /*** Fail due to attribute callback error. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 6, 0, data, 35, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 6, 35, data + 35, 43, 0); |
| ble_att_svr_test_misc_prep_write(conn_handle, 6, 78, data + 78, 1, 0); |
| ble_att_svr_test_misc_exec_write(conn_handle, BLE_ATT_EXEC_WRITE_F_EXECUTE, |
| BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN, 6); |
| } |
| |
| TEST_CASE(ble_att_svr_test_notify) |
| { |
| uint16_t conn_handle; |
| |
| conn_handle = ble_att_svr_test_misc_init(0); |
| |
| /*** Successful notifies; verify callback is executed. */ |
| /* 3-length attribute. */ |
| ble_att_svr_test_misc_verify_notify(conn_handle, 10, |
| (uint8_t[]) { 1, 2, 3 }, 3, 1); |
| /* 1-length attribute. */ |
| ble_att_svr_test_misc_verify_notify(conn_handle, 1, |
| (uint8_t[]) { 0xff }, 1, 1); |
| /* 0-length attribute. */ |
| ble_att_svr_test_misc_verify_notify(conn_handle, 43, NULL, 0, 1); |
| |
| /*** Bad notifies; verify callback is not executed. */ |
| /* Attribute handle of 0. */ |
| ble_att_svr_test_misc_verify_notify(conn_handle, 0, |
| (uint8_t[]) { 1, 2, 3 }, 3, 0); |
| |
| } |
| |
| TEST_CASE(ble_att_svr_test_prep_write_tmo) |
| { |
| int32_t ticks_from_now; |
| uint16_t conn_handle; |
| int rc; |
| int i; |
| |
| static uint8_t data[1024]; |
| |
| conn_handle = ble_att_svr_test_misc_init(205); |
| |
| /* Initialize some attribute data. */ |
| for (i = 0; i < sizeof data; i++) { |
| data[i] = i; |
| } |
| |
| /* Register a writable attribute. */ |
| ble_att_svr_test_misc_register_uuid(BLE_UUID16_DECLARE(0x1234), |
| HA_FLAG_PERM_RW, 1, |
| ble_att_svr_test_misc_attr_fn_w_1); |
| |
| /* Ensure timer is not set. */ |
| ticks_from_now = ble_hs_conn_timer(); |
| TEST_ASSERT_FATAL(ticks_from_now == BLE_HS_FOREVER); |
| |
| /* Receive a prepare write request. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 0, data, 7, 0); |
| |
| /* Ensure timer will expire in 30 seconds. */ |
| ticks_from_now = ble_hs_conn_timer(); |
| TEST_ASSERT(ticks_from_now == BLE_HS_ATT_SVR_QUEUED_WRITE_TMO); |
| |
| /* Almost let the timer expire. */ |
| os_time_advance(BLE_HS_ATT_SVR_QUEUED_WRITE_TMO - 1); |
| ticks_from_now = ble_hs_conn_timer(); |
| TEST_ASSERT(ticks_from_now == 1); |
| |
| /* Receive a second prepare write request. */ |
| ble_att_svr_test_misc_prep_write(conn_handle, 1, 7, data + 7, 10, 0); |
| |
| /* Ensure timer got reset. */ |
| ticks_from_now = ble_hs_conn_timer(); |
| TEST_ASSERT(ticks_from_now == BLE_HS_ATT_SVR_QUEUED_WRITE_TMO); |
| |
| /* Allow the timer to expire. */ |
| ble_hs_test_util_hci_ack_set_disconnect(0); |
| os_time_advance(BLE_HS_ATT_SVR_QUEUED_WRITE_TMO); |
| ticks_from_now = ble_hs_conn_timer(); |
| TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER); |
| |
| /* Ensure connection was terminated. */ |
| ble_hs_test_util_hci_verify_tx_disconnect(2, BLE_ERR_REM_USER_CONN_TERM); |
| |
| /* Free connection. This is needed so that the prep write mbufs get |
| * freed and no mbuf leak gets reported. |
| */ |
| rc = ble_hs_atomic_conn_delete(conn_handle); |
| TEST_ASSERT_FATAL(rc == 0); |
| } |
| |
| TEST_CASE(ble_att_svr_test_indicate) |
| { |
| uint16_t conn_handle; |
| |
| conn_handle = ble_att_svr_test_misc_init(0); |
| |
| /*** Successful indicates; verify callback is executed. */ |
| /* 3-length attribute. */ |
| ble_att_svr_test_misc_verify_indicate(conn_handle, 10, |
| (uint8_t[]) { 1, 2, 3 }, 3, 1); |
| /* 1-length attribute. */ |
| ble_att_svr_test_misc_verify_indicate(conn_handle, 1, |
| (uint8_t[]) { 0xff }, 1, 1); |
| /* 0-length attribute. */ |
| ble_att_svr_test_misc_verify_indicate(conn_handle, 43, NULL, 0, 1); |
| |
| /*** Bad indicates; verify callback is not executed. */ |
| /* Attribute handle of 0. */ |
| ble_att_svr_test_misc_verify_indicate(conn_handle, 0, |
| (uint8_t[]) { 1, 2, 3 }, 3, 0); |
| } |
| |
| TEST_CASE(ble_att_svr_test_oom) |
| { |
| struct os_mbuf *oms; |
| uint16_t conn_handle; |
| int rc; |
| |
| conn_handle = ble_att_svr_test_misc_init(0); |
| |
| /* Register an attribute (primary service) for incoming read commands. */ |
| ble_att_svr_test_misc_register_uuid( |
| BLE_UUID16_DECLARE(BLE_ATT_UUID_PRIMARY_SERVICE), |
| HA_FLAG_PERM_RW, 1, ble_att_svr_test_misc_attr_fn_rw_1); |
| ble_att_svr_test_attr_w_1_len = 2; |
| ble_att_svr_test_attr_w_1[0] = 0x12; |
| ble_att_svr_test_attr_w_1[1] = 0x34; |
| |
| /* Exhaust the msys pool. Leave one mbuf for the forthcoming request. */ |
| oms = ble_hs_test_util_mbuf_alloc_all_but(1); |
| |
| /*** MTU; always respond affirmatively, even when no mbufs. */ |
| |
| /* Receive a request. */ |
| rc = ble_hs_test_util_rx_att_mtu_cmd(conn_handle, 1, 100); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| /* Ensure we were able to send a real response. */ |
| ble_att_svr_test_misc_verify_tx_mtu_rsp(conn_handle); |
| |
| /*** Find information; always respond affirmatively, even when no mbufs. */ |
| ble_hs_test_util_prev_tx_dequeue(); |
| |
| /* Receive a request. */ |
| rc = ble_hs_test_util_rx_att_find_info_req(conn_handle, 1, 100); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| /* Ensure we were able to send a real response. */ |
| ble_hs_test_util_verify_tx_find_info_rsp( |
| (struct ble_hs_test_util_att_info_entry[]) { |
| { .handle = 1, .uuid = BLE_UUID16_DECLARE(BLE_ATT_UUID_PRIMARY_SERVICE) }, |
| { 0 }, |
| }); |
| |
| /*** Find by type value. */ |
| ble_hs_test_util_prev_tx_dequeue(); |
| |
| /* Receive a request. */ |
| rc = ble_hs_test_util_rx_att_find_type_value_req( |
| conn_handle, 1, 100, 0x0001, ((uint8_t[2]){0x99, 0x99}), 2); |
| TEST_ASSERT_FATAL(rc == BLE_HS_ENOMEM); |
| |
| /* Ensure we were able to send an error response. */ |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_FIND_TYPE_VALUE_REQ, 1, |
| BLE_ATT_ERR_INSUFFICIENT_RES); |
| |
| /*** Read by type; always respond affirmatively, even when no mbufs. */ |
| ble_hs_test_util_prev_tx_dequeue(); |
| |
| /* Receive a request. */ |
| rc = ble_hs_test_util_rx_att_read_type_req16(conn_handle, 100, 0xffff, |
| BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT_FATAL(rc == BLE_HS_ENOENT); |
| |
| /* Ensure we were able to send a non-OOM error response. */ |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_READ_TYPE_REQ, 100, |
| BLE_ATT_ERR_ATTR_NOT_FOUND); |
| |
| /*** Read; always respond affirmatively, even when no mbufs. */ |
| ble_hs_test_util_prev_tx_dequeue(); |
| |
| /* Receive a request. */ |
| rc = ble_hs_test_util_rx_att_read_req(conn_handle, 1); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| /* Ensure we were able to send a real response. */ |
| ble_hs_test_util_verify_tx_read_rsp(ble_att_svr_test_attr_w_1, |
| ble_att_svr_test_attr_w_1_len); |
| |
| /*** Read blob; always respond affirmatively, even when no mbufs. */ |
| ble_hs_test_util_prev_tx_dequeue(); |
| |
| /* Receive a request. */ |
| rc = ble_hs_test_util_rx_att_read_blob_req(conn_handle, 1, 0); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| /* Ensure we were able to send a real response. */ |
| ble_hs_test_util_verify_tx_read_blob_rsp(ble_att_svr_test_attr_w_1, |
| ble_att_svr_test_attr_w_1_len); |
| |
| /*** Read multiple. */ |
| ble_hs_test_util_prev_tx_dequeue(); |
| |
| /* Receive a request. */ |
| rc = ble_hs_test_util_rx_att_read_mult_req(conn_handle, |
| ((uint16_t[2]){0x0001, 0x0002}), |
| 2); |
| TEST_ASSERT_FATAL(rc == BLE_HS_ENOMEM); |
| |
| /* Ensure we were able to send an error response. */ |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_READ_MULT_REQ, 0, |
| BLE_ATT_ERR_INSUFFICIENT_RES); |
| |
| /*** |
| * Read by group type; always respond affirmatively, even when no |
| * mbufs. |
| */ |
| ble_hs_test_util_prev_tx_dequeue(); |
| |
| /* Receive a request. */ |
| rc = ble_hs_test_util_rx_att_read_group_type_req16( |
| conn_handle, 11, 100, BLE_ATT_UUID_PRIMARY_SERVICE); |
| TEST_ASSERT_FATAL(rc == BLE_HS_ENOENT); |
| |
| /* Ensure we were able to send a non-OOM error response. */ |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_READ_GROUP_TYPE_REQ, 11, |
| BLE_ATT_ERR_ATTR_NOT_FOUND); |
| |
| /*** Write. */ |
| ble_hs_test_util_prev_tx_dequeue(); |
| |
| /* Receive a request. */ |
| rc = ble_hs_test_util_rx_att_write_req(conn_handle, 1, |
| ((uint8_t[1]){1}), 1); |
| TEST_ASSERT_FATAL(rc == BLE_HS_ENOMEM); |
| |
| /* Ensure we were able to send an error response. */ |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_WRITE_REQ, 1, |
| BLE_ATT_ERR_INSUFFICIENT_RES); |
| |
| /*** Write command; no response. */ |
| ble_hs_test_util_prev_tx_dequeue(); |
| |
| /* Receive a request. */ |
| rc = ble_hs_test_util_rx_att_write_cmd(conn_handle, 1, |
| ((uint8_t[1]){1}), 1); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| /* Ensure no response sent. */ |
| TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL); |
| |
| /*** Prepare write. */ |
| ble_hs_test_util_prev_tx_dequeue(); |
| |
| /* Receive a request. */ |
| rc = ble_hs_test_util_rx_att_prep_write_req(conn_handle, 1, 0, |
| ((uint8_t[1]){1}), 1); |
| TEST_ASSERT_FATAL(rc == BLE_HS_ENOMEM); |
| |
| /* Ensure we were able to send an error response. */ |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_PREP_WRITE_REQ, 1, |
| BLE_ATT_ERR_INSUFFICIENT_RES); |
| |
| /*** Notify; no response. */ |
| ble_hs_test_util_prev_tx_dequeue(); |
| |
| /* Receive a request. */ |
| rc = ble_hs_test_util_rx_att_notify_req(conn_handle, 1, |
| ((uint8_t[1]){1}), 1); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| /* Ensure no response sent. */ |
| TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL); |
| |
| /*** Indicate. */ |
| ble_hs_test_util_prev_tx_dequeue(); |
| |
| /* Receive a request. */ |
| rc = ble_hs_test_util_rx_att_indicate_req(conn_handle, 1, |
| ((uint8_t[1]){1}), 1); |
| TEST_ASSERT_FATAL(rc == BLE_HS_ENOMEM); |
| |
| /* Ensure we were able to send a real response. */ |
| ble_hs_test_util_verify_tx_err_rsp(BLE_ATT_OP_INDICATE_REQ, 1, |
| BLE_ATT_ERR_INSUFFICIENT_RES); |
| |
| rc = os_mbuf_free_chain(oms); |
| TEST_ASSERT_FATAL(rc == 0); |
| } |
| |
| TEST_CASE(ble_att_svr_test_unsupported_req) |
| { |
| uint16_t conn_handle; |
| int rc; |
| uint8_t buf[] = {0x3f, 0x00, 0x00, 0x01, 0x02, 0x03}; |
| |
| conn_handle = ble_att_svr_test_misc_init(0); |
| |
| /* Put handle into buf */ |
| (*(uint16_t *)&buf[1]) = htole16(conn_handle); |
| rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, |
| buf, sizeof buf); |
| TEST_ASSERT(rc != 0); |
| ble_hs_test_util_verify_tx_err_rsp(0x3f, 0, |
| BLE_ATT_ERR_REQ_NOT_SUPPORTED); |
| |
| /* Check for no response when unknown command is sent */ |
| buf[0] = 0x4f; |
| rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, |
| buf, sizeof buf); |
| TEST_ASSERT(rc != 0); |
| |
| /* Ensure no response sent. */ |
| TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL); |
| } |
| |
| TEST_SUITE(ble_att_svr_suite) |
| { |
| /* When checking for mbuf leaks, ensure no stale prep entries. */ |
| static struct ble_hs_test_util_mbuf_params mbuf_params = { |
| .prev_tx = 1, |
| .rx_queue = 1, |
| .prep_list = 0, |
| }; |
| |
| tu_suite_set_post_test_cb(ble_hs_test_util_post_test, &mbuf_params); |
| |
| ble_att_svr_test_mtu(); |
| ble_att_svr_test_read(); |
| ble_att_svr_test_read_blob(); |
| ble_att_svr_test_read_mult(); |
| ble_att_svr_test_write(); |
| ble_att_svr_test_find_info(); |
| ble_att_svr_test_find_type_value(); |
| ble_att_svr_test_read_type(); |
| ble_att_svr_test_read_group_type(); |
| ble_att_svr_test_prep_write(); |
| ble_att_svr_test_prep_write_tmo(); |
| ble_att_svr_test_notify(); |
| ble_att_svr_test_indicate(); |
| ble_att_svr_test_oom(); |
| ble_att_svr_test_unsupported_req(); |
| } |
| |
| int |
| ble_att_svr_test_all(void) |
| { |
| ble_att_svr_suite(); |
| |
| return tu_any_failed; |
| } |