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 <limits.h>
23 #include "testutil/testutil.h"
24 #include "nimble/ble.h"
25 #include "host/ble_hs_test.h"
26 #include "host/ble_gatt.h"
27 #include "host/ble_uuid.h"
28 #include "ble_hs_test_util.h"
29
30 struct ble_gatt_disc_d_test_dsc {
31 uint16_t chr_val_handle; /* 0 if last entry. */
32 uint16_t dsc_handle;
33 ble_uuid_any_t dsc_uuid;
34 };
35
36 #define BLE_GATT_DISC_D_TEST_MAX_DSCS 256
37 static struct ble_gatt_disc_d_test_dsc
38 ble_gatt_disc_d_test_dscs[BLE_GATT_DISC_D_TEST_MAX_DSCS];
39 static int ble_gatt_disc_d_test_num_dscs;
40 static int ble_gatt_disc_d_test_rx_complete;
41
42 static void
ble_gatt_disc_d_test_init(void)43 ble_gatt_disc_d_test_init(void)
44 {
45 ble_hs_test_util_init();
46
47 ble_gatt_disc_d_test_num_dscs = 0;
48 ble_gatt_disc_d_test_rx_complete = 0;
49 }
50
51 static int
ble_gatt_disc_d_test_misc_rx_rsp_once(uint16_t conn_handle,struct ble_gatt_disc_d_test_dsc * dscs)52 ble_gatt_disc_d_test_misc_rx_rsp_once(
53 uint16_t conn_handle, struct ble_gatt_disc_d_test_dsc *dscs)
54 {
55 struct ble_att_find_info_rsp rsp;
56 uint8_t buf[1024];
57 int off;
58 int rc;
59 int i;
60
61 /* Send the pending ATT Read By Type Request. */
62
63 if (dscs[0].dsc_uuid.u.type == BLE_UUID_TYPE_16) {
64 rsp.bafp_format = BLE_ATT_FIND_INFO_RSP_FORMAT_16BIT;
65 } else {
66 rsp.bafp_format = BLE_ATT_FIND_INFO_RSP_FORMAT_128BIT;
67 }
68
69 ble_att_find_info_rsp_write(buf, BLE_ATT_FIND_INFO_RSP_BASE_SZ, &rsp);
70
71 off = BLE_ATT_FIND_INFO_RSP_BASE_SZ;
72 for (i = 0; ; i++) {
73 if (dscs[i].chr_val_handle == 0) {
74 /* No more descriptors. */
75 break;
76 }
77
78 if (dscs[i].dsc_uuid.u.type == BLE_UUID_TYPE_16) {
79 if (off + BLE_ATT_FIND_INFO_IDATA_16_SZ >
80 ble_att_mtu(conn_handle)) {
81
82 /* Can't fit any more entries. */
83 break;
84 }
85 } else {
86 if (off + BLE_ATT_FIND_INFO_IDATA_128_SZ >
87 ble_att_mtu(conn_handle)) {
88
89 /* Can't fit any more entries. */
90 break;
91 }
92 }
93
94 /* If the value length is changing, we need a separate response. */
95 if (((dscs[0].dsc_uuid.u.type == BLE_UUID_TYPE_16) ^
96 (dscs[i].dsc_uuid.u.type == BLE_UUID_TYPE_16)) != 0) {
97 break;
98 }
99
100 put_le16(buf + off, dscs[i].dsc_handle);
101 off += 2;
102
103 ble_uuid_flat(&dscs[i].dsc_uuid.u, buf + off);
104 off += ble_uuid_length(&dscs[i].dsc_uuid.u);
105 }
106
107 rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
108 buf, off);
109 TEST_ASSERT(rc == 0);
110
111 return i;
112 }
113
114 static void
ble_gatt_disc_d_test_misc_rx_rsp(uint16_t conn_handle,uint16_t end_handle,struct ble_gatt_disc_d_test_dsc * dscs)115 ble_gatt_disc_d_test_misc_rx_rsp(uint16_t conn_handle,
116 uint16_t end_handle,
117 struct ble_gatt_disc_d_test_dsc *dscs)
118 {
119 int count;
120 int idx;
121
122 idx = 0;
123 while (dscs[idx].chr_val_handle != 0) {
124 count = ble_gatt_disc_d_test_misc_rx_rsp_once(conn_handle, dscs + idx);
125 if (count == 0) {
126 break;
127 }
128 idx += count;
129 }
130
131 if (dscs[idx - 1].dsc_handle != end_handle) {
132 /* Send the pending ATT Request. */
133 ble_hs_test_util_rx_att_err_rsp(conn_handle, BLE_ATT_OP_FIND_INFO_REQ,
134 BLE_ATT_ERR_ATTR_NOT_FOUND,
135 end_handle);
136 }
137 }
138
139 static void
ble_gatt_disc_d_test_misc_verify_dscs(struct ble_gatt_disc_d_test_dsc * dscs,int stop_after)140 ble_gatt_disc_d_test_misc_verify_dscs(struct ble_gatt_disc_d_test_dsc *dscs,
141 int stop_after)
142 {
143 int i;
144
145 if (stop_after == 0) {
146 stop_after = BLE_GATT_DISC_D_TEST_MAX_DSCS;
147 }
148
149 for (i = 0; i < stop_after && dscs[i].chr_val_handle != 0; i++) {
150 TEST_ASSERT(dscs[i].chr_val_handle ==
151 ble_gatt_disc_d_test_dscs[i].chr_val_handle);
152 TEST_ASSERT(dscs[i].dsc_handle ==
153 ble_gatt_disc_d_test_dscs[i].dsc_handle);
154 TEST_ASSERT(ble_uuid_cmp(&dscs[i].dsc_uuid.u,
155 &ble_gatt_disc_d_test_dscs[i].dsc_uuid.u) == 0);
156 }
157
158 TEST_ASSERT(i == ble_gatt_disc_d_test_num_dscs);
159 TEST_ASSERT(ble_gatt_disc_d_test_rx_complete);
160 }
161
162 static int
ble_gatt_disc_d_test_misc_cb(uint16_t conn_handle,const struct ble_gatt_error * error,uint16_t chr_val_handle,const struct ble_gatt_dsc * dsc,void * arg)163 ble_gatt_disc_d_test_misc_cb(uint16_t conn_handle,
164 const struct ble_gatt_error *error,
165 uint16_t chr_val_handle,
166 const struct ble_gatt_dsc *dsc,
167 void *arg)
168 {
169 struct ble_gatt_disc_d_test_dsc *dst;
170 int *stop_after;
171
172 TEST_ASSERT(error != NULL);
173 TEST_ASSERT(!ble_gatt_disc_d_test_rx_complete);
174
175 stop_after = arg;
176
177 switch (error->status) {
178 case 0:
179 TEST_ASSERT_FATAL(ble_gatt_disc_d_test_num_dscs <
180 BLE_GATT_DISC_D_TEST_MAX_DSCS);
181
182 dst = ble_gatt_disc_d_test_dscs + ble_gatt_disc_d_test_num_dscs++;
183 dst->chr_val_handle = chr_val_handle;
184 dst->dsc_handle = dsc->handle;
185 dst->dsc_uuid = dsc->uuid;
186 break;
187
188 case BLE_HS_EDONE:
189 ble_gatt_disc_d_test_rx_complete = 1;
190 break;
191
192 default:
193 TEST_ASSERT(0);
194 break;
195 }
196
197 if (*stop_after > 0) {
198 (*stop_after)--;
199 if (*stop_after == 0) {
200 ble_gatt_disc_d_test_rx_complete = 1;
201 return 1;
202 }
203 }
204
205 return 0;
206 }
207
208 static void
ble_gatt_disc_d_test_misc_all(uint16_t chr_val_handle,uint16_t end_handle,int stop_after,struct ble_gatt_disc_d_test_dsc * dscs)209 ble_gatt_disc_d_test_misc_all(uint16_t chr_val_handle, uint16_t end_handle,
210 int stop_after,
211 struct ble_gatt_disc_d_test_dsc *dscs)
212 {
213 int num_left;
214 int rc;
215
216 ble_gatt_disc_d_test_init();
217
218 ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
219 NULL, NULL);
220
221 num_left = stop_after;
222 rc = ble_gattc_disc_all_dscs(2, chr_val_handle, end_handle,
223 ble_gatt_disc_d_test_misc_cb, &num_left);
224 TEST_ASSERT(rc == 0);
225
226 ble_gatt_disc_d_test_misc_rx_rsp(2, end_handle, dscs);
227 ble_gatt_disc_d_test_misc_verify_dscs(dscs, stop_after);
228 }
229
TEST_CASE(ble_gatt_disc_d_test_1)230 TEST_CASE(ble_gatt_disc_d_test_1)
231 {
232 /*** One 16-bit descriptor. */
233 ble_gatt_disc_d_test_misc_all(5, 10, 0,
234 ((struct ble_gatt_disc_d_test_dsc[]) { {
235 .chr_val_handle = 5,
236 .dsc_handle = 6,
237 .dsc_uuid.u16 = BLE_UUID16_INIT(0x1234),
238 }, {
239 0
240 } })
241 );
242
243 /*** Two 16-bit descriptors. */
244 ble_gatt_disc_d_test_misc_all(50, 100, 0,
245 ((struct ble_gatt_disc_d_test_dsc[]) { {
246 .chr_val_handle = 50,
247 .dsc_handle = 51,
248 .dsc_uuid.u16 = BLE_UUID16_INIT(0x1111),
249 }, {
250 .chr_val_handle = 50,
251 .dsc_handle = 52,
252 .dsc_uuid.u16 = BLE_UUID16_INIT(0x2222),
253 }, {
254 0
255 } })
256 );
257
258 /*** Five 16-bit descriptors. */
259 ble_gatt_disc_d_test_misc_all(50, 100, 0,
260 ((struct ble_gatt_disc_d_test_dsc[]) { {
261 .chr_val_handle = 50,
262 .dsc_handle = 51,
263 .dsc_uuid.u16 = BLE_UUID16_INIT(0x1111),
264 }, {
265 .chr_val_handle = 50,
266 .dsc_handle = 52,
267 .dsc_uuid.u16 = BLE_UUID16_INIT(0x2222),
268 }, {
269 .chr_val_handle = 50,
270 .dsc_handle = 53,
271 .dsc_uuid.u16 = BLE_UUID16_INIT(0x3333),
272 }, {
273 .chr_val_handle = 50,
274 .dsc_handle = 54,
275 .dsc_uuid.u16 = BLE_UUID16_INIT(0x4444),
276 }, {
277 .chr_val_handle = 50,
278 .dsc_handle = 55,
279 .dsc_uuid.u16 = BLE_UUID16_INIT(0x5555),
280 }, {
281 0
282 } })
283 );
284
285 /*** Interleaved 16-bit and 128-bit descriptors. */
286 ble_gatt_disc_d_test_misc_all(50, 100, 0,
287 ((struct ble_gatt_disc_d_test_dsc[]) { {
288 .chr_val_handle = 50,
289 .dsc_handle = 51,
290 .dsc_uuid.u16 = BLE_UUID16_INIT(0x1111),
291 }, {
292 .chr_val_handle = 50,
293 .dsc_handle = 52,
294 .dsc_uuid.u128 = BLE_UUID128_INIT( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15),
295 }, {
296 .chr_val_handle = 50,
297 .dsc_handle = 53,
298 .dsc_uuid.u16 = BLE_UUID16_INIT(0x3333),
299 }, {
300 .chr_val_handle = 50,
301 .dsc_handle = 54,
302 .dsc_uuid.u128 = BLE_UUID128_INIT(1,0,4,0,6,9,17,7,8,43,7,4,12,43,19,35),
303 }, {
304 .chr_val_handle = 50,
305 .dsc_handle = 55,
306 .dsc_uuid.u16 = BLE_UUID16_INIT(0x5555),
307 }, {
308 0
309 } })
310 );
311
312 /*** Ends with final handle ID. */
313 ble_gatt_disc_d_test_misc_all(50, 52, 0,
314 ((struct ble_gatt_disc_d_test_dsc[]) { {
315 .chr_val_handle = 50,
316 .dsc_handle = 51,
317 .dsc_uuid.u16 = BLE_UUID16_INIT(0x1111),
318 }, {
319 .chr_val_handle = 50,
320 .dsc_handle = 52,
321 .dsc_uuid.u16 = BLE_UUID16_INIT(0x2222),
322 }, {
323 0
324 } })
325 );
326
327 /*** Stop after two descriptors. */
328 ble_gatt_disc_d_test_misc_all(50, 100, 2,
329 ((struct ble_gatt_disc_d_test_dsc[]) { {
330 .chr_val_handle = 50,
331 .dsc_handle = 51,
332 .dsc_uuid.u16 = BLE_UUID16_INIT(0x1111),
333 }, {
334 .chr_val_handle = 50,
335 .dsc_handle = 52,
336 .dsc_uuid.u16 = BLE_UUID16_INIT(0x2222),
337 }, {
338 .chr_val_handle = 50,
339 .dsc_handle = 53,
340 .dsc_uuid.u16 = BLE_UUID16_INIT(0x3333),
341 }, {
342 .chr_val_handle = 50,
343 .dsc_handle = 54,
344 .dsc_uuid.u16 = BLE_UUID16_INIT(0x4444),
345 }, {
346 .chr_val_handle = 50,
347 .dsc_handle = 55,
348 .dsc_uuid.u16 = BLE_UUID16_INIT(0x5555),
349 }, {
350 0
351 } })
352 );
353 }
354
TEST_CASE(ble_gatt_disc_d_test_oom_all)355 TEST_CASE(ble_gatt_disc_d_test_oom_all)
356 {
357 struct ble_gatt_disc_d_test_dsc dscs[] = {
358 {
359 .chr_val_handle = 543,
360 .dsc_handle = 548,
361 .dsc_uuid.u128 = BLE_UUID128_INIT(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15),
362 },
363 {
364 .chr_val_handle = 543,
365 .dsc_handle = 549,
366 .dsc_uuid.u128 = BLE_UUID128_INIT(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16),
367 },
368 { 0 }
369 };
370
371 struct os_mbuf *oms;
372 int32_t ticks_until;
373 int stop_after;
374 int num_dscs;
375 int rc;
376
377 ble_gatt_disc_d_test_init();
378
379 ble_hs_test_util_create_conn(1, ((uint8_t[]){2,3,4,5,6,7,8,9}),
380 NULL, NULL);
381
382 /* Initiate a discover all characteristics procedure. */
383 stop_after = 0;
384 rc = ble_gattc_disc_all_dscs(1, 543, 560,
385 ble_gatt_disc_d_test_misc_cb, &stop_after);
386 TEST_ASSERT_FATAL(rc == 0);
387
388 /* Exhaust the msys pool. Leave one mbuf for the forthcoming response. */
389 oms = ble_hs_test_util_mbuf_alloc_all_but(1);
390 num_dscs = ble_gatt_disc_d_test_misc_rx_rsp_once(1, dscs);
391
392 /* Make sure there are still undiscovered services. */
393 TEST_ASSERT_FATAL(num_dscs < sizeof dscs / sizeof dscs[0] - 1);
394
395 /* Ensure no follow-up request got sent. It should not have gotten sent
396 * due to mbuf exhaustion.
397 */
398 ble_hs_test_util_prev_tx_queue_clear();
399 TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue_pullup() == NULL);
400
401 /* Verify that we will resume the stalled GATT procedure in one second. */
402 ticks_until = ble_gattc_timer();
403 TEST_ASSERT(ticks_until == os_time_ms_to_ticks32(MYNEWT_VAL(BLE_GATT_RESUME_RATE)));
404
405 /* Verify the procedure proceeds after mbufs become available. */
406 rc = os_mbuf_free_chain(oms);
407 TEST_ASSERT_FATAL(rc == 0);
408 os_time_advance(ticks_until);
409 ble_gattc_timer();
410
411 /* Exhaust the msys pool. Leave one mbuf for the forthcoming response. */
412 oms = ble_hs_test_util_mbuf_alloc_all_but(1);
413 ble_gatt_disc_d_test_misc_rx_rsp_once(1, dscs + num_dscs);
414
415 /* Ensure no follow-up request got sent. It should not have gotten sent
416 * due to mbuf exhaustion.
417 */
418 ble_hs_test_util_prev_tx_queue_clear();
419 TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue_pullup() == NULL);
420
421 /* Verify that we will resume the stalled GATT procedure in one second. */
422 ticks_until = ble_gattc_timer();
423 TEST_ASSERT(ticks_until == os_time_ms_to_ticks32(MYNEWT_VAL(BLE_GATT_RESUME_RATE)));
424
425 /* Verify the procedure succeeds after mbufs become available. */
426 rc = os_mbuf_free_chain(oms);
427 TEST_ASSERT_FATAL(rc == 0);
428 os_time_advance(ticks_until);
429 ble_gattc_timer();
430
431 ble_hs_test_util_rx_att_err_rsp(1,
432 BLE_ATT_OP_READ_TYPE_REQ,
433 BLE_ATT_ERR_ATTR_NOT_FOUND,
434 1);
435 ble_gatt_disc_d_test_misc_verify_dscs(dscs, 0);
436 }
437
TEST_SUITE(ble_gatt_disc_d_test_suite)438 TEST_SUITE(ble_gatt_disc_d_test_suite)
439 {
440 tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL);
441
442 ble_gatt_disc_d_test_1();
443 ble_gatt_disc_d_test_oom_all();
444 }
445
446 int
ble_gatt_disc_d_test_all(void)447 ble_gatt_disc_d_test_all(void)
448 {
449 ble_gatt_disc_d_test_suite();
450
451 return tu_any_failed;
452 }
453