1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <string.h>
21 #include <errno.h>
22 #include "testutil/testutil.h"
23 #include "nimble/ble.h"
24 #include "host/ble_hs_test.h"
25 #include "host/ble_uuid.h"
26 #include "ble_hs_test_util.h"
27
28 static struct ble_gatt_svc ble_gatt_find_s_test_svcs[256];
29 static int ble_gatt_find_s_test_num_svcs;
30 static int ble_gatt_find_s_test_proc_complete;
31
32 struct ble_gatt_find_s_test_entry {
33 uint16_t inc_handle; /* 0 indicates no more entries. */
34 uint16_t start_handle;
35 uint16_t end_handle;
36 const ble_uuid_t *uuid;
37 };
38
39 static void
ble_gatt_find_s_test_misc_init(void)40 ble_gatt_find_s_test_misc_init(void)
41 {
42 ble_hs_test_util_init();
43 ble_gatt_find_s_test_num_svcs = 0;
44 ble_gatt_find_s_test_proc_complete = 0;
45 }
46
47 static int
ble_gatt_find_s_test_misc_cb(uint16_t conn_handle,const struct ble_gatt_error * error,const struct ble_gatt_svc * service,void * arg)48 ble_gatt_find_s_test_misc_cb(uint16_t conn_handle,
49 const struct ble_gatt_error *error,
50 const struct ble_gatt_svc *service,
51 void *arg)
52 {
53 TEST_ASSERT(!ble_gatt_find_s_test_proc_complete);
54 TEST_ASSERT(error != NULL);
55
56 switch (error->status) {
57 case 0:
58 ble_gatt_find_s_test_svcs[ble_gatt_find_s_test_num_svcs++] = *service;
59 break;
60
61 case BLE_HS_EDONE:
62 ble_gatt_find_s_test_proc_complete = 1;
63 break;
64
65 default:
66 TEST_ASSERT(0);
67 break;
68 }
69
70 return 0;
71 }
72
73 static void
ble_gatt_find_s_test_misc_verify_incs(struct ble_gatt_find_s_test_entry * entries)74 ble_gatt_find_s_test_misc_verify_incs(
75 struct ble_gatt_find_s_test_entry *entries)
76 {
77 int i;
78
79 for (i = 0; entries[i].inc_handle != 0; i++) {
80 TEST_ASSERT(ble_gatt_find_s_test_svcs[i].start_handle ==
81 entries[i].start_handle);
82 TEST_ASSERT(ble_gatt_find_s_test_svcs[i].end_handle ==
83 entries[i].end_handle);
84 TEST_ASSERT(ble_uuid_cmp(&ble_gatt_find_s_test_svcs[i].uuid.u,
85 entries[i].uuid) == 0);
86 }
87
88 TEST_ASSERT(i == ble_gatt_find_s_test_num_svcs);
89 TEST_ASSERT(ble_gatt_find_s_test_proc_complete);
90 }
91
92 static int
ble_gatt_find_s_test_misc_rx_read_type(uint16_t conn_handle,struct ble_gatt_find_s_test_entry * entries)93 ble_gatt_find_s_test_misc_rx_read_type(
94 uint16_t conn_handle, struct ble_gatt_find_s_test_entry *entries)
95 {
96 struct ble_att_read_type_rsp rsp;
97 uint8_t buf[1024];
98 int off;
99 int rc;
100 int i;
101
102 memset(&rsp, 0, sizeof rsp);
103
104 off = BLE_ATT_READ_TYPE_RSP_BASE_SZ;
105 for (i = 0; entries[i].inc_handle != 0; i++) {
106 if (rsp.batp_length == BLE_GATTS_INC_SVC_LEN_NO_UUID + 2) {
107 break;
108 }
109
110 if (entries[i].uuid->type != BLE_UUID_TYPE_16) {
111 if (rsp.batp_length != 0) {
112 break;
113 }
114 rsp.batp_length = BLE_GATTS_INC_SVC_LEN_NO_UUID + 2;
115 } else {
116 rsp.batp_length = BLE_GATTS_INC_SVC_LEN_UUID + 2;
117 }
118
119 TEST_ASSERT_FATAL(off + rsp.batp_length <= sizeof buf);
120
121 put_le16(buf + off, entries[i].inc_handle);
122 off += 2;
123
124 put_le16(buf + off, entries[i].start_handle);
125 off += 2;
126
127 put_le16(buf + off, entries[i].end_handle);
128 off += 2;
129
130 if (entries[i].uuid->type == BLE_UUID_TYPE_16) {
131 put_le16(buf + off, ble_uuid_u16(entries[i].uuid));
132 off += 2;
133 }
134 }
135
136 if (i == 0) {
137 ble_hs_test_util_rx_att_err_rsp(conn_handle, BLE_ATT_OP_READ_TYPE_REQ,
138 BLE_ATT_ERR_ATTR_NOT_FOUND, 0);
139 return 0;
140 }
141
142 ble_att_read_type_rsp_write(buf + 0, BLE_ATT_READ_TYPE_RSP_BASE_SZ, &rsp);
143
144 rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
145 buf, off);
146 TEST_ASSERT(rc == 0);
147
148 return i;
149 }
150
151 static void
ble_gatt_find_s_test_misc_rx_read(uint16_t conn_handle,const ble_uuid_t * uuid)152 ble_gatt_find_s_test_misc_rx_read(uint16_t conn_handle, const ble_uuid_t *uuid)
153 {
154 uint8_t buf[17];
155 int rc;
156
157 TEST_ASSERT(uuid->type == BLE_UUID_TYPE_128);
158
159 buf[0] = BLE_ATT_OP_READ_RSP;
160 ble_uuid_flat(uuid, buf + 1);
161
162 rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
163 buf, 17);
164 TEST_ASSERT(rc == 0);
165 }
166
167 static void
ble_gatt_find_s_test_misc_verify_tx_read_type(uint16_t start_handle,uint16_t end_handle)168 ble_gatt_find_s_test_misc_verify_tx_read_type(uint16_t start_handle,
169 uint16_t end_handle)
170 {
171 struct ble_att_read_type_req req;
172 struct os_mbuf *om;
173 uint16_t uuid16;
174
175 om = ble_hs_test_util_prev_tx_dequeue_pullup();
176 TEST_ASSERT_FATAL(om != NULL);
177
178 ble_att_read_type_req_parse(om->om_data, om->om_len, &req);
179
180 TEST_ASSERT(req.batq_start_handle == start_handle);
181 TEST_ASSERT(req.batq_end_handle == end_handle);
182 TEST_ASSERT(om->om_len == BLE_ATT_READ_TYPE_REQ_BASE_SZ + 2);
183 uuid16 = get_le16(om->om_data + BLE_ATT_READ_TYPE_REQ_BASE_SZ);
184 TEST_ASSERT(uuid16 == BLE_ATT_UUID_INCLUDE);
185 }
186
187 static void
ble_gatt_find_s_test_misc_verify_tx_read(uint16_t handle)188 ble_gatt_find_s_test_misc_verify_tx_read(uint16_t handle)
189 {
190 struct ble_att_read_req req;
191 struct os_mbuf *om;
192
193 om = ble_hs_test_util_prev_tx_dequeue_pullup();
194 TEST_ASSERT_FATAL(om != NULL);
195
196 ble_att_read_req_parse(om->om_data, om->om_len, &req);
197
198 TEST_ASSERT(req.barq_handle == handle);
199 TEST_ASSERT(om->om_len == BLE_ATT_READ_REQ_SZ);
200 }
201
202 static void
ble_gatt_find_s_test_misc_find_inc(uint16_t conn_handle,uint16_t start_handle,uint16_t end_handle,struct ble_gatt_find_s_test_entry * entries)203 ble_gatt_find_s_test_misc_find_inc(uint16_t conn_handle,
204 uint16_t start_handle, uint16_t end_handle,
205 struct ble_gatt_find_s_test_entry *entries)
206 {
207 struct ble_gatt_svc service;
208 int cur_start;
209 int num_found;
210 int idx;
211 int rc;
212 int i;
213
214 rc = ble_gattc_find_inc_svcs(conn_handle, start_handle, end_handle,
215 ble_gatt_find_s_test_misc_cb, &service);
216 TEST_ASSERT(rc == 0);
217
218 cur_start = start_handle;
219 idx = 0;
220 while (1) {
221 ble_gatt_find_s_test_misc_verify_tx_read_type(cur_start, end_handle);
222 num_found = ble_gatt_find_s_test_misc_rx_read_type(conn_handle,
223 entries + idx);
224 if (num_found == 0) {
225 break;
226 }
227
228 if (entries[idx].uuid->type == BLE_UUID_TYPE_128) {
229 TEST_ASSERT(num_found == 1);
230 ble_gatt_find_s_test_misc_verify_tx_read(
231 entries[idx].start_handle);
232 ble_gatt_find_s_test_misc_rx_read(conn_handle,
233 entries[idx].uuid);
234 }
235
236 idx += num_found;
237 cur_start = entries[idx - 1].inc_handle + 1;
238 }
239 TEST_ASSERT(idx == ble_gatt_find_s_test_num_svcs);
240 TEST_ASSERT(ble_gatt_find_s_test_proc_complete);
241
242 for (i = 0; i < ble_gatt_find_s_test_num_svcs; i++) {
243 TEST_ASSERT(ble_gatt_find_s_test_svcs[i].start_handle ==
244 entries[i].start_handle);
245 TEST_ASSERT(ble_gatt_find_s_test_svcs[i].end_handle ==
246 entries[i].end_handle);
247 TEST_ASSERT(ble_uuid_cmp(&ble_gatt_find_s_test_svcs[i].uuid.u,
248 entries[i].uuid) == 0);
249 }
250 }
251
TEST_CASE(ble_gatt_find_s_test_1)252 TEST_CASE(ble_gatt_find_s_test_1)
253 {
254 /* Two 16-bit UUID services; one response. */
255 ble_gatt_find_s_test_misc_init();
256 ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
257 NULL, NULL);
258 ble_gatt_find_s_test_misc_find_inc(2, 5, 10,
259 ((struct ble_gatt_find_s_test_entry[]) { {
260 .inc_handle = 6,
261 .start_handle = 35,
262 .end_handle = 49,
263 .uuid = BLE_UUID16_DECLARE(0x5155),
264 }, {
265 .inc_handle = 9,
266 .start_handle = 543,
267 .end_handle = 870,
268 .uuid = BLE_UUID16_DECLARE(0x1122),
269 }, {
270 0,
271 } })
272 );
273
274 /* One 128-bit UUID service; two responses. */
275 ble_gatt_find_s_test_misc_init();
276 ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
277 NULL, NULL);
278 ble_gatt_find_s_test_misc_find_inc(2, 34, 100,
279 ((struct ble_gatt_find_s_test_entry[]) { {
280 .inc_handle = 36,
281 .start_handle = 403,
282 .end_handle = 859,
283 .uuid = BLE_UUID128_DECLARE(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16),
284 }, {
285 0,
286 } })
287 );
288
289 /* Two 128-bit UUID service; four responses. */
290 ble_gatt_find_s_test_misc_init();
291 ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
292 NULL, NULL);
293 ble_gatt_find_s_test_misc_find_inc(2, 34, 100,
294 ((struct ble_gatt_find_s_test_entry[]) { {
295 .inc_handle = 36,
296 .start_handle = 403,
297 .end_handle = 859,
298 .uuid = BLE_UUID128_DECLARE(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16),
299 }, {
300 .inc_handle = 39,
301 .start_handle = 900,
302 .end_handle = 932,
303 .uuid = BLE_UUID128_DECLARE(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17),
304 }, {
305 0,
306 } })
307 );
308
309 /* Two 16-bit UUID; three 128-bit UUID; seven responses. */
310 ble_gatt_find_s_test_misc_init();
311 ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
312 NULL, NULL);
313 ble_gatt_find_s_test_misc_find_inc(2, 1, 100,
314 ((struct ble_gatt_find_s_test_entry[]) { {
315 .inc_handle = 36,
316 .start_handle = 403,
317 .end_handle = 859,
318 .uuid = BLE_UUID128_DECLARE(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16),
319 }, {
320 .inc_handle = 37,
321 .start_handle = 35,
322 .end_handle = 49,
323 .uuid = BLE_UUID16_DECLARE(0x5155),
324 }, {
325 .inc_handle = 38,
326 .start_handle = 543,
327 .end_handle = 870,
328 .uuid = BLE_UUID16_DECLARE(0x1122),
329 }, {
330 .inc_handle = 39,
331 .start_handle = 900,
332 .end_handle = 932,
333 .uuid = BLE_UUID128_DECLARE(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17),
334 }, {
335 .inc_handle = 40,
336 .start_handle = 940,
337 .end_handle = 950,
338 .uuid = BLE_UUID128_DECLARE(3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18),
339 }, {
340 0,
341 } })
342 );
343 }
344
TEST_CASE(ble_gatt_find_s_test_oom)345 TEST_CASE(ble_gatt_find_s_test_oom)
346 {
347
348 struct ble_gatt_find_s_test_entry incs[] = {
349 {
350 .inc_handle = 21,
351 .start_handle = 800,
352 .end_handle = 899,
353 .uuid = BLE_UUID128_DECLARE(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15),
354 },
355 {
356 .inc_handle = 22,
357 .start_handle = 900,
358 .end_handle = 999,
359 .uuid = BLE_UUID128_DECLARE(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16),
360 },
361 { 0 }
362 };
363
364 struct os_mbuf *oms;
365 int32_t ticks_until;
366 int rc;
367
368 ble_gatt_find_s_test_misc_init();
369
370 ble_hs_test_util_create_conn(1, ((uint8_t[]){2,3,4,5,6,7,8,9}),
371 NULL, NULL);
372
373 /* Initiate a discover all characteristics procedure. */
374 rc = ble_gattc_find_inc_svcs(1, 20, 30,
375 ble_gatt_find_s_test_misc_cb, NULL);
376 TEST_ASSERT_FATAL(rc == 0);
377
378 /* Exhaust the msys pool. Leave one mbuf for the forthcoming response. */
379 oms = ble_hs_test_util_mbuf_alloc_all_but(1);
380 ble_gatt_find_s_test_misc_rx_read_type(1, incs);
381
382 /* Ensure no follow-up request got sent. It should not have gotten sent
383 * due to mbuf exhaustion.
384 */
385 ble_hs_test_util_prev_tx_queue_clear();
386 TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue_pullup() == NULL);
387
388 /* Verify that we will resume the stalled GATT procedure in one second. */
389 ticks_until = ble_gattc_timer();
390 TEST_ASSERT(ticks_until == os_time_ms_to_ticks32(MYNEWT_VAL(BLE_GATT_RESUME_RATE)));
391
392 /* Verify the procedure succeeds after mbufs become available. */
393 rc = os_mbuf_free_chain(oms);
394 TEST_ASSERT_FATAL(rc == 0);
395 os_time_advance(ticks_until);
396 ble_gattc_timer();
397
398 /* We can't cause a memory exhaustion error on the follow up request. The
399 * GATT client frees the read response immediately before sending the
400 * follow-up request, so there is always an mbuf available.
401 */
402 /* XXX: Find a way to test this. */
403 ble_gatt_find_s_test_misc_rx_read(1, incs[0].uuid);
404
405 /* Exhaust the msys pool. Leave one mbuf for the forthcoming response. */
406 oms = ble_hs_test_util_mbuf_alloc_all_but(1);
407 ble_gatt_find_s_test_misc_rx_read_type(1, incs + 1);
408
409 /* Verify the procedure succeeds after mbufs become available. */
410 rc = os_mbuf_free_chain(oms);
411 TEST_ASSERT_FATAL(rc == 0);
412 os_time_advance(ticks_until);
413 ble_gattc_timer();
414
415 ble_gatt_find_s_test_misc_rx_read(1, incs[1].uuid);
416
417 ble_hs_test_util_rx_att_err_rsp(1,
418 BLE_ATT_OP_READ_TYPE_REQ,
419 BLE_ATT_ERR_ATTR_NOT_FOUND,
420 1);
421
422 ble_gatt_find_s_test_misc_verify_incs(incs);
423 }
424
TEST_SUITE(ble_gatt_find_s_test_suite)425 TEST_SUITE(ble_gatt_find_s_test_suite)
426 {
427 tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL);
428
429 ble_gatt_find_s_test_1();
430 ble_gatt_find_s_test_oom();
431 }
432
433 int
ble_gatt_find_s_test_all(void)434 ble_gatt_find_s_test_all(void)
435 {
436 ble_gatt_find_s_test_suite();
437
438 return tu_any_failed;
439 }
440