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 /**
21 * GATT client - Generic Attribute Profile; client operations.
22 *
23 * Design overview:
24 *
25 * GATT client procedures are initiated by the application via function calls.
26 * Such functions return when either of the following happens:
27 *
28 * (1) The procedure completes (success or failure).
29 * (2) The procedure cannot proceed until a BLE peer responds.
30 *
31 * For (1), the result of the procedure if fully indicated by the function
32 * return code.
33 * For (2), the procedure result is indicated by an application-configured
34 * callback. The callback is executed when the procedure completes.
35 *
36 * Notes on thread-safety:
37 * 1. The ble_hs mutex must never be locked when an application callback is
38 * executed. A callback is free to initiate additional host procedures.
39 * 2. The only resource protected by the mutex is the list of active procedures
40 * (ble_gattc_procs). Thread-safety is achieved by locking the mutex during
41 * removal and insertion operations. Procedure objects are only modified
42 * while they are not in the list. This is sufficient, as the host parent
43 * task is the only task which inspects or modifies individual procedure
44 * entries. Tasks have the following permissions regarding procedure
45 * entries:
46 *
47 * | insert | remove | inspect | modify
48 * ------------+---------+-----------|-----------|---------
49 * parent task | X | X | X | X
50 * other tasks | X | | |
51 */
52
53 #include <stddef.h>
54 #include <stdlib.h>
55 #include <errno.h>
56 #include <string.h>
57 #include "os/os_mempool.h"
58 #include "nimble/ble.h"
59 #include "host/ble_uuid.h"
60 #include "host/ble_gap.h"
61 #include "ble_hs_priv.h"
62
63 /*****************************************************************************
64 * $definitions / declarations *
65 *****************************************************************************/
66
67 /**
68 * The maximum time to wait for a single ATT response. The spec defines this
69 * as the ATT transaction time (Vol. 3, Part F, 3.3.3)
70 */
71 #define BLE_GATTC_UNRESPONSIVE_TIMEOUT_MS 30000 /* ms */
72
73 #define BLE_GATT_OP_NONE UINT8_MAX
74 #define BLE_GATT_OP_MTU 0
75 #define BLE_GATT_OP_DISC_ALL_SVCS 1
76 #define BLE_GATT_OP_DISC_SVC_UUID 2
77 #define BLE_GATT_OP_FIND_INC_SVCS 3
78 #define BLE_GATT_OP_DISC_ALL_CHRS 4
79 #define BLE_GATT_OP_DISC_CHR_UUID 5
80 #define BLE_GATT_OP_DISC_ALL_DSCS 6
81 #define BLE_GATT_OP_READ 7
82 #define BLE_GATT_OP_READ_UUID 8
83 #define BLE_GATT_OP_READ_LONG 9
84 #define BLE_GATT_OP_READ_MULT 10
85 #define BLE_GATT_OP_WRITE 11
86 #define BLE_GATT_OP_WRITE_LONG 12
87 #define BLE_GATT_OP_WRITE_RELIABLE 13
88 #define BLE_GATT_OP_INDICATE 14
89 #define BLE_GATT_OP_CNT 15
90
91 /** Procedure stalled due to resource exhaustion. */
92 #define BLE_GATTC_PROC_F_STALLED 0x01
93
94 /** Represents an in-progress GATT procedure. */
95 struct ble_gattc_proc {
96 STAILQ_ENTRY(ble_gattc_proc) next;
97
98 uint32_t exp_os_ticks;
99 uint16_t conn_handle;
100 uint8_t op;
101 uint8_t flags;
102
103 union {
104 struct {
105 ble_gatt_mtu_fn *cb;
106 void *cb_arg;
107 } mtu;
108
109 struct {
110 uint16_t prev_handle;
111 ble_gatt_disc_svc_fn *cb;
112 void *cb_arg;
113 } disc_all_svcs;
114
115 struct {
116 ble_uuid_any_t service_uuid;
117 uint16_t prev_handle;
118 ble_gatt_disc_svc_fn *cb;
119 void *cb_arg;
120 } disc_svc_uuid;
121
122 struct {
123 uint16_t prev_handle;
124 uint16_t end_handle;
125
126 uint16_t cur_start;
127 uint16_t cur_end;
128
129 ble_gatt_disc_svc_fn *cb;
130 void *cb_arg;
131 } find_inc_svcs;
132
133 struct {
134 uint16_t prev_handle;
135 uint16_t end_handle;
136 ble_gatt_chr_fn *cb;
137 void *cb_arg;
138 } disc_all_chrs;
139
140 struct {
141 ble_uuid_any_t chr_uuid;
142 uint16_t prev_handle;
143 uint16_t end_handle;
144 ble_gatt_chr_fn *cb;
145 void *cb_arg;
146 } disc_chr_uuid;
147
148 struct {
149 uint16_t chr_val_handle;
150 uint16_t prev_handle;
151 uint16_t end_handle;
152 ble_gatt_dsc_fn *cb;
153 void *cb_arg;
154 } disc_all_dscs;
155
156 struct {
157 uint16_t handle;
158 ble_gatt_attr_fn *cb;
159 void *cb_arg;
160 } read;
161
162 struct {
163 ble_uuid_any_t chr_uuid;
164 uint16_t start_handle;
165 uint16_t end_handle;
166 ble_gatt_attr_fn *cb;
167 void *cb_arg;
168 } read_uuid;
169
170 struct {
171 uint16_t handle;
172 uint16_t offset;
173 ble_gatt_attr_fn *cb;
174 void *cb_arg;
175 } read_long;
176
177 struct {
178 uint16_t handles[MYNEWT_VAL(BLE_GATT_READ_MAX_ATTRS)];
179 uint8_t num_handles;
180 ble_gatt_attr_fn *cb;
181 void *cb_arg;
182 } read_mult;
183
184 struct {
185 uint16_t att_handle;
186 ble_gatt_attr_fn *cb;
187 void *cb_arg;
188 } write;
189
190 struct {
191 struct ble_gatt_attr attr;
192 uint16_t length;
193 ble_gatt_attr_fn *cb;
194 void *cb_arg;
195 } write_long;
196
197 struct {
198 struct ble_gatt_attr attrs[MYNEWT_VAL(BLE_GATT_WRITE_MAX_ATTRS)];
199 uint8_t num_attrs;
200 uint8_t cur_attr;
201 uint16_t length;
202 ble_gatt_reliable_attr_fn *cb;
203 void *cb_arg;
204 } write_reliable;
205
206 struct {
207 uint16_t chr_val_handle;
208 } indicate;
209 };
210 };
211
212 STAILQ_HEAD(ble_gattc_proc_list, ble_gattc_proc);
213
214 /**
215 * Error functions - these handle an incoming ATT error response and apply it
216 * to the appropriate active GATT procedure.
217 */
218 typedef void ble_gattc_err_fn(struct ble_gattc_proc *proc, int status,
219 uint16_t att_handle);
220 static ble_gattc_err_fn ble_gattc_mtu_err;
221 static ble_gattc_err_fn ble_gattc_disc_all_svcs_err;
222 static ble_gattc_err_fn ble_gattc_disc_svc_uuid_err;
223 static ble_gattc_err_fn ble_gattc_find_inc_svcs_err;
224 static ble_gattc_err_fn ble_gattc_disc_all_chrs_err;
225 static ble_gattc_err_fn ble_gattc_disc_chr_uuid_err;
226 static ble_gattc_err_fn ble_gattc_disc_all_dscs_err;
227 static ble_gattc_err_fn ble_gattc_read_err;
228 static ble_gattc_err_fn ble_gattc_read_uuid_err;
229 static ble_gattc_err_fn ble_gattc_read_long_err;
230 static ble_gattc_err_fn ble_gattc_read_mult_err;
231 static ble_gattc_err_fn ble_gattc_write_err;
232 static ble_gattc_err_fn ble_gattc_write_long_err;
233 static ble_gattc_err_fn ble_gattc_write_reliable_err;
234 static ble_gattc_err_fn ble_gattc_indicate_err;
235
236 static ble_gattc_err_fn * const ble_gattc_err_dispatch[BLE_GATT_OP_CNT] = {
237 [BLE_GATT_OP_MTU] = ble_gattc_mtu_err,
238 [BLE_GATT_OP_DISC_ALL_SVCS] = ble_gattc_disc_all_svcs_err,
239 [BLE_GATT_OP_DISC_SVC_UUID] = ble_gattc_disc_svc_uuid_err,
240 [BLE_GATT_OP_FIND_INC_SVCS] = ble_gattc_find_inc_svcs_err,
241 [BLE_GATT_OP_DISC_ALL_CHRS] = ble_gattc_disc_all_chrs_err,
242 [BLE_GATT_OP_DISC_CHR_UUID] = ble_gattc_disc_chr_uuid_err,
243 [BLE_GATT_OP_DISC_ALL_DSCS] = ble_gattc_disc_all_dscs_err,
244 [BLE_GATT_OP_READ] = ble_gattc_read_err,
245 [BLE_GATT_OP_READ_UUID] = ble_gattc_read_uuid_err,
246 [BLE_GATT_OP_READ_LONG] = ble_gattc_read_long_err,
247 [BLE_GATT_OP_READ_MULT] = ble_gattc_read_mult_err,
248 [BLE_GATT_OP_WRITE] = ble_gattc_write_err,
249 [BLE_GATT_OP_WRITE_LONG] = ble_gattc_write_long_err,
250 [BLE_GATT_OP_WRITE_RELIABLE] = ble_gattc_write_reliable_err,
251 [BLE_GATT_OP_INDICATE] = ble_gattc_indicate_err,
252 };
253
254 /**
255 * Resume functions - these handle periodic retries of procedures that have
256 * stalled due to memory exhaustion.
257 */
258 typedef int ble_gattc_resume_fn(struct ble_gattc_proc *proc);
259
260 static ble_gattc_resume_fn ble_gattc_disc_all_svcs_resume;
261 static ble_gattc_resume_fn ble_gattc_disc_svc_uuid_resume;
262 static ble_gattc_resume_fn ble_gattc_find_inc_svcs_resume;
263 static ble_gattc_resume_fn ble_gattc_disc_all_chrs_resume;
264 static ble_gattc_resume_fn ble_gattc_disc_chr_uuid_resume;
265 static ble_gattc_resume_fn ble_gattc_disc_all_dscs_resume;
266 static ble_gattc_resume_fn ble_gattc_read_long_resume;
267 static ble_gattc_resume_fn ble_gattc_write_long_resume;
268 static ble_gattc_resume_fn ble_gattc_write_reliable_resume;
269
270 static ble_gattc_resume_fn * const
271 ble_gattc_resume_dispatch[BLE_GATT_OP_CNT] = {
272 [BLE_GATT_OP_MTU] = NULL,
273 [BLE_GATT_OP_DISC_ALL_SVCS] = ble_gattc_disc_all_svcs_resume,
274 [BLE_GATT_OP_DISC_SVC_UUID] = ble_gattc_disc_svc_uuid_resume,
275 [BLE_GATT_OP_FIND_INC_SVCS] = ble_gattc_find_inc_svcs_resume,
276 [BLE_GATT_OP_DISC_ALL_CHRS] = ble_gattc_disc_all_chrs_resume,
277 [BLE_GATT_OP_DISC_CHR_UUID] = ble_gattc_disc_chr_uuid_resume,
278 [BLE_GATT_OP_DISC_ALL_DSCS] = ble_gattc_disc_all_dscs_resume,
279 [BLE_GATT_OP_READ] = NULL,
280 [BLE_GATT_OP_READ_UUID] = NULL,
281 [BLE_GATT_OP_READ_LONG] = ble_gattc_read_long_resume,
282 [BLE_GATT_OP_READ_MULT] = NULL,
283 [BLE_GATT_OP_WRITE] = NULL,
284 [BLE_GATT_OP_WRITE_LONG] = ble_gattc_write_long_resume,
285 [BLE_GATT_OP_WRITE_RELIABLE] = ble_gattc_write_reliable_resume,
286 [BLE_GATT_OP_INDICATE] = NULL,
287 };
288
289 /**
290 * Timeout functions - these notify the application that a GATT procedure has
291 * timed out while waiting for a response.
292 */
293 typedef void ble_gattc_tmo_fn(struct ble_gattc_proc *proc);
294
295 static ble_gattc_tmo_fn ble_gattc_mtu_tmo;
296 static ble_gattc_tmo_fn ble_gattc_disc_all_svcs_tmo;
297 static ble_gattc_tmo_fn ble_gattc_disc_svc_uuid_tmo;
298 static ble_gattc_tmo_fn ble_gattc_find_inc_svcs_tmo;
299 static ble_gattc_tmo_fn ble_gattc_disc_all_chrs_tmo;
300 static ble_gattc_tmo_fn ble_gattc_disc_chr_uuid_tmo;
301 static ble_gattc_tmo_fn ble_gattc_disc_all_dscs_tmo;
302 static ble_gattc_tmo_fn ble_gattc_read_tmo;
303 static ble_gattc_tmo_fn ble_gattc_read_uuid_tmo;
304 static ble_gattc_tmo_fn ble_gattc_read_long_tmo;
305 static ble_gattc_tmo_fn ble_gattc_read_mult_tmo;
306 static ble_gattc_tmo_fn ble_gattc_write_tmo;
307 static ble_gattc_tmo_fn ble_gattc_write_long_tmo;
308 static ble_gattc_tmo_fn ble_gattc_write_reliable_tmo;
309 static ble_gattc_tmo_fn ble_gattc_indicate_tmo;
310
311 static ble_gattc_tmo_fn * const
312 ble_gattc_tmo_dispatch[BLE_GATT_OP_CNT] = {
313 [BLE_GATT_OP_MTU] = ble_gattc_mtu_tmo,
314 [BLE_GATT_OP_DISC_ALL_SVCS] = ble_gattc_disc_all_svcs_tmo,
315 [BLE_GATT_OP_DISC_SVC_UUID] = ble_gattc_disc_svc_uuid_tmo,
316 [BLE_GATT_OP_FIND_INC_SVCS] = ble_gattc_find_inc_svcs_tmo,
317 [BLE_GATT_OP_DISC_ALL_CHRS] = ble_gattc_disc_all_chrs_tmo,
318 [BLE_GATT_OP_DISC_CHR_UUID] = ble_gattc_disc_chr_uuid_tmo,
319 [BLE_GATT_OP_DISC_ALL_DSCS] = ble_gattc_disc_all_dscs_tmo,
320 [BLE_GATT_OP_READ] = ble_gattc_read_tmo,
321 [BLE_GATT_OP_READ_UUID] = ble_gattc_read_uuid_tmo,
322 [BLE_GATT_OP_READ_LONG] = ble_gattc_read_long_tmo,
323 [BLE_GATT_OP_READ_MULT] = ble_gattc_read_mult_tmo,
324 [BLE_GATT_OP_WRITE] = ble_gattc_write_tmo,
325 [BLE_GATT_OP_WRITE_LONG] = ble_gattc_write_long_tmo,
326 [BLE_GATT_OP_WRITE_RELIABLE] = ble_gattc_write_reliable_tmo,
327 [BLE_GATT_OP_INDICATE] = ble_gattc_indicate_tmo,
328 };
329
330 /**
331 * Receive functions - these handle specific incoming responses and apply them
332 * to the appropriate active GATT procedure.
333 */
334 typedef int ble_gattc_rx_adata_fn(struct ble_gattc_proc *proc,
335 struct ble_att_read_type_adata *adata);
336
337 typedef int ble_gattc_rx_prep_fn(struct ble_gattc_proc *proc, int status,
338 uint16_t handle, uint16_t offset,
339 struct os_mbuf **om);
340
341 typedef int ble_gattc_rx_attr_fn(struct ble_gattc_proc *proc, int status,
342 struct os_mbuf **om);
343
344 typedef int ble_gattc_rx_complete_fn(struct ble_gattc_proc *proc, int status);
345 typedef int ble_gattc_rx_exec_fn(struct ble_gattc_proc *proc, int status);
346
347 static ble_gattc_rx_adata_fn ble_gattc_find_inc_svcs_rx_adata;
348 static ble_gattc_rx_complete_fn ble_gattc_find_inc_svcs_rx_complete;
349 static ble_gattc_rx_attr_fn ble_gattc_find_inc_svcs_rx_read_rsp;
350 static ble_gattc_rx_adata_fn ble_gattc_disc_all_chrs_rx_adata;
351 static ble_gattc_rx_complete_fn ble_gattc_disc_all_chrs_rx_complete;
352 static ble_gattc_rx_adata_fn ble_gattc_disc_chr_uuid_rx_adata;
353 static ble_gattc_rx_complete_fn ble_gattc_disc_chr_uuid_rx_complete;
354 static ble_gattc_rx_attr_fn ble_gattc_read_rx_read_rsp;
355 static ble_gattc_rx_attr_fn ble_gattc_read_long_rx_read_rsp;
356 static ble_gattc_rx_adata_fn ble_gattc_read_uuid_rx_adata;
357 static ble_gattc_rx_complete_fn ble_gattc_read_uuid_rx_complete;
358 static ble_gattc_rx_prep_fn ble_gattc_write_long_rx_prep;
359 static ble_gattc_rx_exec_fn ble_gattc_write_long_rx_exec;
360 static ble_gattc_rx_prep_fn ble_gattc_write_reliable_rx_prep;
361 static ble_gattc_rx_exec_fn ble_gattc_write_reliable_rx_exec;
362
363 static const struct ble_gattc_rx_adata_entry {
364 uint8_t op;
365 ble_gattc_rx_adata_fn *cb;
366 } ble_gattc_rx_read_type_elem_entries[] = {
367 { BLE_GATT_OP_FIND_INC_SVCS, ble_gattc_find_inc_svcs_rx_adata },
368 { BLE_GATT_OP_DISC_ALL_CHRS, ble_gattc_disc_all_chrs_rx_adata },
369 { BLE_GATT_OP_DISC_CHR_UUID, ble_gattc_disc_chr_uuid_rx_adata },
370 { BLE_GATT_OP_READ_UUID, ble_gattc_read_uuid_rx_adata },
371 };
372
373 static const struct ble_gattc_rx_complete_entry {
374 uint8_t op;
375 ble_gattc_rx_complete_fn *cb;
376 } ble_gattc_rx_read_type_complete_entries[] = {
377 { BLE_GATT_OP_FIND_INC_SVCS, ble_gattc_find_inc_svcs_rx_complete },
378 { BLE_GATT_OP_DISC_ALL_CHRS, ble_gattc_disc_all_chrs_rx_complete },
379 { BLE_GATT_OP_DISC_CHR_UUID, ble_gattc_disc_chr_uuid_rx_complete },
380 { BLE_GATT_OP_READ_UUID, ble_gattc_read_uuid_rx_complete },
381 };
382
383 static const struct ble_gattc_rx_attr_entry {
384 uint8_t op;
385 ble_gattc_rx_attr_fn *cb;
386 } ble_gattc_rx_read_rsp_entries[] = {
387 { BLE_GATT_OP_READ, ble_gattc_read_rx_read_rsp },
388 { BLE_GATT_OP_READ_LONG, ble_gattc_read_long_rx_read_rsp },
389 { BLE_GATT_OP_FIND_INC_SVCS, ble_gattc_find_inc_svcs_rx_read_rsp },
390 };
391
392 static const struct ble_gattc_rx_prep_entry {
393 uint8_t op;
394 ble_gattc_rx_prep_fn *cb;
395 } ble_gattc_rx_prep_entries[] = {
396 { BLE_GATT_OP_WRITE_LONG, ble_gattc_write_long_rx_prep },
397 { BLE_GATT_OP_WRITE_RELIABLE, ble_gattc_write_reliable_rx_prep },
398 };
399
400 static const struct ble_gattc_rx_exec_entry {
401 uint8_t op;
402 ble_gattc_rx_exec_fn *cb;
403 } ble_gattc_rx_exec_entries[] = {
404 { BLE_GATT_OP_WRITE_LONG, ble_gattc_write_long_rx_exec },
405 { BLE_GATT_OP_WRITE_RELIABLE, ble_gattc_write_reliable_rx_exec },
406 };
407
408 static os_membuf_t ble_gattc_proc_mem[
409 OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_GATT_MAX_PROCS),
410 sizeof (struct ble_gattc_proc))
411 ];
412
413 static struct os_mempool ble_gattc_proc_pool;
414
415 /* The list of active GATT client procedures. */
416 static struct ble_gattc_proc_list ble_gattc_procs;
417
418 /* The time when we should attempt to resume stalled procedures, in OS ticks.
419 * A value of 0 indicates no stalled procedures.
420 */
421 static ble_npl_time_t ble_gattc_resume_at;
422
423 /* Statistics. */
424 STATS_SECT_DECL(ble_gattc_stats) ble_gattc_stats;
425 STATS_NAME_START(ble_gattc_stats)
STATS_NAME(ble_gattc_stats,mtu)426 STATS_NAME(ble_gattc_stats, mtu)
427 STATS_NAME(ble_gattc_stats, mtu_fail)
428 STATS_NAME(ble_gattc_stats, disc_all_svcs)
429 STATS_NAME(ble_gattc_stats, disc_all_svcs_fail)
430 STATS_NAME(ble_gattc_stats, disc_svc_uuid)
431 STATS_NAME(ble_gattc_stats, disc_svc_uuid_fail)
432 STATS_NAME(ble_gattc_stats, find_inc_svcs)
433 STATS_NAME(ble_gattc_stats, find_inc_svcs_fail)
434 STATS_NAME(ble_gattc_stats, disc_all_chrs)
435 STATS_NAME(ble_gattc_stats, disc_all_chrs_fail)
436 STATS_NAME(ble_gattc_stats, disc_chrs_uuid)
437 STATS_NAME(ble_gattc_stats, disc_chrs_uuid_fail)
438 STATS_NAME(ble_gattc_stats, disc_all_dscs)
439 STATS_NAME(ble_gattc_stats, disc_all_dscs_fail)
440 STATS_NAME(ble_gattc_stats, read)
441 STATS_NAME(ble_gattc_stats, read_fail)
442 STATS_NAME(ble_gattc_stats, read_uuid)
443 STATS_NAME(ble_gattc_stats, read_uuid_fail)
444 STATS_NAME(ble_gattc_stats, read_long)
445 STATS_NAME(ble_gattc_stats, read_long_fail)
446 STATS_NAME(ble_gattc_stats, read_mult)
447 STATS_NAME(ble_gattc_stats, read_mult_fail)
448 STATS_NAME(ble_gattc_stats, write_no_rsp)
449 STATS_NAME(ble_gattc_stats, write_no_rsp_fail)
450 STATS_NAME(ble_gattc_stats, write)
451 STATS_NAME(ble_gattc_stats, write_fail)
452 STATS_NAME(ble_gattc_stats, write_long)
453 STATS_NAME(ble_gattc_stats, write_long_fail)
454 STATS_NAME(ble_gattc_stats, write_reliable)
455 STATS_NAME(ble_gattc_stats, write_reliable_fail)
456 STATS_NAME(ble_gattc_stats, notify)
457 STATS_NAME(ble_gattc_stats, notify_fail)
458 STATS_NAME(ble_gattc_stats, indicate)
459 STATS_NAME(ble_gattc_stats, indicate_fail)
460 STATS_NAME(ble_gattc_stats, proc_timeout)
461 STATS_NAME_END(ble_gattc_stats)
462
463 /*****************************************************************************
464 * $debug *
465 *****************************************************************************/
466
467 static void
468 ble_gattc_dbg_assert_proc_not_inserted(struct ble_gattc_proc *proc)
469 {
470 #if MYNEWT_VAL(BLE_HS_DEBUG)
471 struct ble_gattc_proc *cur;
472
473 ble_hs_lock();
474
475 STAILQ_FOREACH(cur, &ble_gattc_procs, next) {
476 BLE_HS_DBG_ASSERT(cur != proc);
477 }
478
479 ble_hs_unlock();
480 #endif
481 }
482
483 /*****************************************************************************
484 * $log *
485 *****************************************************************************/
486
487 static void
ble_gattc_log_proc_init(char * name)488 ble_gattc_log_proc_init(char *name)
489 {
490 BLE_HS_LOG(INFO, "GATT procedure initiated: %s", name);
491 }
492
493 static void
ble_gattc_log_uuid(const ble_uuid_t * uuid)494 ble_gattc_log_uuid(const ble_uuid_t *uuid)
495 {
496 char buf[BLE_UUID_STR_LEN];
497
498 ble_uuid_to_str(uuid, buf);
499
500 BLE_HS_LOG(INFO, "%s", buf);
501 }
502
503 static void
ble_gattc_log_disc_svc_uuid(struct ble_gattc_proc * proc)504 ble_gattc_log_disc_svc_uuid(struct ble_gattc_proc *proc)
505 {
506 ble_gattc_log_proc_init("discover service by uuid; uuid=");
507 ble_gattc_log_uuid(&proc->disc_svc_uuid.service_uuid.u);
508 BLE_HS_LOG(INFO, "\n");
509 }
510
511 static void
ble_gattc_log_find_inc_svcs(struct ble_gattc_proc * proc)512 ble_gattc_log_find_inc_svcs(struct ble_gattc_proc *proc)
513 {
514 ble_gattc_log_proc_init("find included services; ");
515 BLE_HS_LOG(INFO, "start_handle=%d end_handle=%d\n",
516 proc->find_inc_svcs.prev_handle + 1,
517 proc->find_inc_svcs.end_handle);
518 }
519
520 static void
ble_gattc_log_disc_all_chrs(struct ble_gattc_proc * proc)521 ble_gattc_log_disc_all_chrs(struct ble_gattc_proc *proc)
522 {
523 ble_gattc_log_proc_init("discover all characteristics; ");
524 BLE_HS_LOG(INFO, "start_handle=%d end_handle=%d\n",
525 proc->disc_all_chrs.prev_handle + 1,
526 proc->disc_all_chrs.end_handle);
527 }
528
529 static void
ble_gattc_log_disc_chr_uuid(struct ble_gattc_proc * proc)530 ble_gattc_log_disc_chr_uuid(struct ble_gattc_proc *proc)
531 {
532 ble_gattc_log_proc_init("discover characteristics by uuid; ");
533 BLE_HS_LOG(INFO, "start_handle=%d end_handle=%d uuid=",
534 proc->disc_chr_uuid.prev_handle + 1,
535 proc->disc_chr_uuid.end_handle);
536 ble_gattc_log_uuid(&proc->disc_chr_uuid.chr_uuid.u);
537 BLE_HS_LOG(INFO, "\n");
538 }
539
540 static void
ble_gattc_log_disc_all_dscs(struct ble_gattc_proc * proc)541 ble_gattc_log_disc_all_dscs(struct ble_gattc_proc *proc)
542 {
543 ble_gattc_log_proc_init("discover all descriptors; ");
544 BLE_HS_LOG(INFO, "chr_val_handle=%d end_handle=%d\n",
545 proc->disc_all_dscs.chr_val_handle,
546 proc->disc_all_dscs.end_handle);
547 }
548
549 static void
ble_gattc_log_read(uint16_t att_handle)550 ble_gattc_log_read(uint16_t att_handle)
551 {
552 ble_gattc_log_proc_init("read; ");
553 BLE_HS_LOG(INFO, "att_handle=%d\n", att_handle);
554 }
555
556 static void
ble_gattc_log_read_uuid(uint16_t start_handle,uint16_t end_handle,const ble_uuid_t * uuid)557 ble_gattc_log_read_uuid(uint16_t start_handle, uint16_t end_handle,
558 const ble_uuid_t *uuid)
559 {
560 ble_gattc_log_proc_init("read by uuid; ");
561 BLE_HS_LOG(INFO, "start_handle=%d end_handle=%d uuid=",
562 start_handle, end_handle);
563 ble_gattc_log_uuid(uuid);
564 BLE_HS_LOG(INFO, "\n");
565 }
566
567 static void
ble_gattc_log_read_long(struct ble_gattc_proc * proc)568 ble_gattc_log_read_long(struct ble_gattc_proc *proc)
569 {
570 ble_gattc_log_proc_init("read long; ");
571 BLE_HS_LOG(INFO, "att_handle=%d\n", proc->read_long.handle);
572 }
573
574 static void
ble_gattc_log_read_mult(const uint16_t * handles,uint8_t num_handles)575 ble_gattc_log_read_mult(const uint16_t *handles, uint8_t num_handles)
576 {
577 int i;
578
579 ble_gattc_log_proc_init("read multiple; ");
580 BLE_HS_LOG(INFO, "att_handles=");
581 for (i = 0; i < num_handles; i++) {
582 BLE_HS_LOG(INFO, "%s%d", i != 0 ? "," : "", handles[i]);
583 }
584 BLE_HS_LOG(INFO, "\n");
585 }
586
587 static void
ble_gattc_log_write(uint16_t att_handle,uint16_t len,int expecting_rsp)588 ble_gattc_log_write(uint16_t att_handle, uint16_t len, int expecting_rsp)
589 {
590 char *name;
591
592 if (expecting_rsp) {
593 name = "write; ";
594 } else {
595 name = "write no rsp; ";
596 }
597
598 ble_gattc_log_proc_init(name);
599 BLE_HS_LOG(INFO, "att_handle=%d len=%d\n", att_handle, len);
600 }
601
602 static void
ble_gattc_log_write_long(struct ble_gattc_proc * proc)603 ble_gattc_log_write_long(struct ble_gattc_proc *proc)
604 {
605 ble_gattc_log_proc_init("write long; ");
606 BLE_HS_LOG(INFO, "att_handle=%d len=%d\n",
607 proc->write_long.attr.handle,
608 OS_MBUF_PKTLEN(proc->write_long.attr.om));
609 }
610
611 static void
ble_gattc_log_write_reliable(struct ble_gattc_proc * proc)612 ble_gattc_log_write_reliable(struct ble_gattc_proc *proc)
613 {
614 int i;
615
616 ble_gattc_log_proc_init("write reliable; ");
617 BLE_HS_LOG(INFO, "att_handles=");
618 for (i = 0; i < proc->write_reliable.num_attrs; i++) {
619 BLE_HS_LOG(INFO, "%s%d", i != 0 ? "," : "",
620 proc->write_reliable.attrs[i].handle);
621 }
622 BLE_HS_LOG(INFO, "\n");
623 }
624
625 static void
ble_gattc_log_notify(uint16_t att_handle)626 ble_gattc_log_notify(uint16_t att_handle)
627 {
628 ble_gattc_log_proc_init("notify; ");
629 BLE_HS_LOG(INFO, "att_handle=%d\n", att_handle);
630 }
631
632 static void
ble_gattc_log_indicate(uint16_t att_handle)633 ble_gattc_log_indicate(uint16_t att_handle)
634 {
635 ble_gattc_log_proc_init("indicate; ");
636 BLE_HS_LOG(INFO, "att_handle=%d\n", att_handle);
637 }
638
639 /*****************************************************************************
640 * $rx entry *
641 *****************************************************************************/
642
643 static const void *
ble_gattc_rx_entry_find(uint8_t op,const void * rx_entries,int num_entries)644 ble_gattc_rx_entry_find(uint8_t op, const void *rx_entries, int num_entries)
645 {
646 struct gen_entry {
647 uint8_t op;
648 void (*cb)(void);
649 };
650
651 const struct gen_entry *entries;
652 int i;
653
654 entries = rx_entries;
655 for (i = 0; i < num_entries; i++) {
656 if (entries[i].op == op) {
657 return entries + i;
658 }
659 }
660
661 return NULL;
662 }
663
664 /*****************************************************************************
665 * $proc *
666 *****************************************************************************/
667
668 /**
669 * Allocates a proc entry.
670 *
671 * @return An entry on success; null on failure.
672 */
673 static struct ble_gattc_proc *
ble_gattc_proc_alloc(void)674 ble_gattc_proc_alloc(void)
675 {
676 struct ble_gattc_proc *proc;
677
678 proc = os_memblock_get(&ble_gattc_proc_pool);
679 if (proc != NULL) {
680 memset(proc, 0, sizeof *proc);
681 }
682
683 return proc;
684 }
685
686 /**
687 * Frees the specified proc entry. No-op if passed a null pointer.
688 */
689 static void
ble_gattc_proc_free(struct ble_gattc_proc * proc)690 ble_gattc_proc_free(struct ble_gattc_proc *proc)
691 {
692 int rc;
693 int i;
694
695 if (proc != NULL) {
696 ble_gattc_dbg_assert_proc_not_inserted(proc);
697
698 switch (proc->op) {
699 case BLE_GATT_OP_WRITE_LONG:
700 os_mbuf_free_chain(proc->write_long.attr.om);
701 break;
702
703 case BLE_GATT_OP_WRITE_RELIABLE:
704 for (i = 0; i < proc->write_reliable.num_attrs; i++) {
705 os_mbuf_free_chain(proc->write_reliable.attrs[i].om);
706 }
707 break;
708
709 default:
710 break;
711 }
712
713 #if MYNEWT_VAL(BLE_HS_DEBUG)
714 memset(proc, 0xff, sizeof *proc);
715 #endif
716 rc = os_memblock_put(&ble_gattc_proc_pool, proc);
717 BLE_HS_DBG_ASSERT_EVAL(rc == 0);
718 }
719 }
720
721 static void
ble_gattc_proc_insert(struct ble_gattc_proc * proc)722 ble_gattc_proc_insert(struct ble_gattc_proc *proc)
723 {
724 ble_gattc_dbg_assert_proc_not_inserted(proc);
725
726 ble_hs_lock();
727 STAILQ_INSERT_TAIL(&ble_gattc_procs, proc, next);
728 ble_hs_unlock();
729 }
730
731 static void
ble_gattc_proc_set_exp_timer(struct ble_gattc_proc * proc)732 ble_gattc_proc_set_exp_timer(struct ble_gattc_proc *proc)
733 {
734 proc->exp_os_ticks = ble_npl_time_get() +
735 ble_npl_time_ms_to_ticks32(BLE_GATTC_UNRESPONSIVE_TIMEOUT_MS);
736 }
737
738 static void
ble_gattc_proc_set_resume_timer(struct ble_gattc_proc * proc)739 ble_gattc_proc_set_resume_timer(struct ble_gattc_proc *proc)
740 {
741 proc->flags |= BLE_GATTC_PROC_F_STALLED;
742
743 /* Don't overwrite resume time if it is already set; piggyback on it
744 * instead.
745 */
746 if (ble_gattc_resume_at == 0) {
747 ble_gattc_resume_at = ble_npl_time_get() +
748 ble_npl_time_ms_to_ticks32(MYNEWT_VAL(BLE_GATT_RESUME_RATE));
749
750 /* A value of 0 indicates the timer is unset. Disambiguate this. */
751 if (ble_gattc_resume_at == 0) {
752 ble_gattc_resume_at++;
753 }
754 }
755 }
756
757 static void
ble_gattc_process_status(struct ble_gattc_proc * proc,int status)758 ble_gattc_process_status(struct ble_gattc_proc *proc, int status)
759 {
760 switch (status) {
761 case 0:
762 if (!(proc->flags & BLE_GATTC_PROC_F_STALLED)) {
763 ble_gattc_proc_set_exp_timer(proc);
764 }
765
766 ble_gattc_proc_insert(proc);
767 ble_hs_timer_resched();
768 break;
769
770 default:
771 ble_gattc_proc_free(proc);
772 break;
773 }
774 }
775
776 /**
777 * Processes the return code that results from an attempt to resume a
778 * procedure. If the resume attempt failed due to memory exhaustion at a lower
779 * layer, the procedure is marked as stalled but still in progress. Otherwise,
780 * the resume error code is unmodified.
781 */
782 static int
ble_gattc_process_resume_status(struct ble_gattc_proc * proc,int status)783 ble_gattc_process_resume_status(struct ble_gattc_proc *proc, int status)
784 {
785 switch (status) {
786 case 0:
787 return 0;
788
789 case BLE_HS_ENOMEM:
790 ble_gattc_proc_set_resume_timer(proc);
791 return 0;
792
793 default:
794 return status;
795 }
796 }
797
798 /*****************************************************************************
799 * $util *
800 *****************************************************************************/
801
802 /**
803 * Retrieves the error dispatch entry with the specified op code.
804 */
805 static ble_gattc_err_fn *
ble_gattc_err_dispatch_get(uint8_t op)806 ble_gattc_err_dispatch_get(uint8_t op)
807 {
808 BLE_HS_DBG_ASSERT(op < BLE_GATT_OP_CNT);
809 return ble_gattc_err_dispatch[op];
810 }
811
812 /**
813 * Retrieves the error dispatch entry with the specified op code.
814 */
815 static ble_gattc_resume_fn *
ble_gattc_resume_dispatch_get(uint8_t op)816 ble_gattc_resume_dispatch_get(uint8_t op)
817 {
818 BLE_HS_DBG_ASSERT(op < BLE_GATT_OP_CNT);
819 return ble_gattc_resume_dispatch[op];
820 }
821
822 static ble_gattc_tmo_fn *
ble_gattc_tmo_dispatch_get(uint8_t op)823 ble_gattc_tmo_dispatch_get(uint8_t op)
824 {
825 BLE_HS_DBG_ASSERT(op < BLE_GATT_OP_CNT);
826 return ble_gattc_tmo_dispatch[op];
827 }
828
829 typedef int ble_gattc_match_fn(struct ble_gattc_proc *proc, void *arg);
830
831 struct ble_gattc_criteria_conn_op {
832 uint16_t conn_handle;
833 uint8_t op;
834 };
835
836 /**
837 * Tests if a proc entry fits the specified criteria.
838 *
839 * @param proc The procedure to test.
840 * @param conn_handle The connection handle to match against.
841 * @param op The op code to match against, or
842 * BLE_GATT_OP_NONE to ignore this criterion.
843 *
844 * @return 1 if the proc matches; 0 otherwise.
845 */
846 static int
ble_gattc_proc_matches_conn_op(struct ble_gattc_proc * proc,void * arg)847 ble_gattc_proc_matches_conn_op(struct ble_gattc_proc *proc, void *arg)
848 {
849 const struct ble_gattc_criteria_conn_op *criteria;
850
851 criteria = arg;
852
853 if (criteria->conn_handle != proc->conn_handle) {
854 return 0;
855 }
856
857 if (criteria->op != proc->op && criteria->op != BLE_GATT_OP_NONE) {
858 return 0;
859 }
860
861 return 1;
862 }
863
864 struct ble_gattc_criteria_exp {
865 ble_npl_time_t now;
866 int32_t next_exp_in;
867 };
868
869 static int
ble_gattc_proc_matches_expired(struct ble_gattc_proc * proc,void * arg)870 ble_gattc_proc_matches_expired(struct ble_gattc_proc *proc, void *arg)
871 {
872 struct ble_gattc_criteria_exp *criteria;
873 int32_t time_diff;
874
875 criteria = arg;
876
877 time_diff = proc->exp_os_ticks - criteria->now;
878
879 if (time_diff <= 0) {
880 /* Procedure is expired. */
881 return 1;
882 }
883
884 /* Procedure isn't expired; determine if it is the next to expire. */
885 if (time_diff < criteria->next_exp_in) {
886 criteria->next_exp_in = time_diff;
887 }
888 return 0;
889 }
890
891 struct ble_gattc_criteria_conn_rx_entry {
892 uint16_t conn_handle;
893 const void *rx_entries;
894 int num_rx_entries;
895 const void *matching_rx_entry;
896 };
897
898 static int
ble_gattc_proc_matches_conn_rx_entry(struct ble_gattc_proc * proc,void * arg)899 ble_gattc_proc_matches_conn_rx_entry(struct ble_gattc_proc *proc, void *arg)
900 {
901 struct ble_gattc_criteria_conn_rx_entry *criteria;
902
903 criteria = arg;
904
905 if (criteria->conn_handle != BLE_HS_CONN_HANDLE_NONE &&
906 criteria->conn_handle != proc->conn_handle) {
907
908 return 0;
909 }
910
911 /* Entry matches; indicate corresponding rx entry. */
912 criteria->matching_rx_entry = ble_gattc_rx_entry_find(
913 proc->op, criteria->rx_entries, criteria->num_rx_entries);
914
915 return 1;
916 }
917
918 static void
ble_gattc_extract(ble_gattc_match_fn * cb,void * arg,int max_procs,struct ble_gattc_proc_list * dst_list)919 ble_gattc_extract(ble_gattc_match_fn *cb, void *arg, int max_procs,
920 struct ble_gattc_proc_list *dst_list)
921 {
922 struct ble_gattc_proc *proc;
923 struct ble_gattc_proc *prev;
924 struct ble_gattc_proc *next;
925 int num_extracted;
926
927 /* Only the parent task is allowed to remove entries from the list. */
928 BLE_HS_DBG_ASSERT(ble_hs_is_parent_task());
929
930 STAILQ_INIT(dst_list);
931 num_extracted = 0;
932
933 ble_hs_lock();
934
935 prev = NULL;
936 proc = STAILQ_FIRST(&ble_gattc_procs);
937 while (proc != NULL) {
938 next = STAILQ_NEXT(proc, next);
939
940 if (cb(proc, arg)) {
941 if (prev == NULL) {
942 STAILQ_REMOVE_HEAD(&ble_gattc_procs, next);
943 } else {
944 STAILQ_REMOVE_AFTER(&ble_gattc_procs, prev, next);
945 }
946 STAILQ_INSERT_TAIL(dst_list, proc, next);
947
948 if (max_procs > 0) {
949 num_extracted++;
950 if (num_extracted >= max_procs) {
951 break;
952 }
953 }
954 } else {
955 prev = proc;
956 }
957
958 proc = next;
959 }
960
961 ble_hs_unlock();
962 }
963
964 static struct ble_gattc_proc *
ble_gattc_extract_one(ble_gattc_match_fn * cb,void * arg)965 ble_gattc_extract_one(ble_gattc_match_fn *cb, void *arg)
966 {
967 struct ble_gattc_proc_list dst_list;
968
969 ble_gattc_extract(cb, arg, 1, &dst_list);
970 return STAILQ_FIRST(&dst_list);
971 }
972
973 static void
ble_gattc_extract_by_conn_op(uint16_t conn_handle,uint8_t op,int max_procs,struct ble_gattc_proc_list * dst_list)974 ble_gattc_extract_by_conn_op(uint16_t conn_handle, uint8_t op, int max_procs,
975 struct ble_gattc_proc_list *dst_list)
976 {
977 struct ble_gattc_criteria_conn_op criteria;
978
979 criteria.conn_handle = conn_handle;
980 criteria.op = op;
981
982 ble_gattc_extract(ble_gattc_proc_matches_conn_op, &criteria, max_procs, dst_list);
983 }
984
985 static struct ble_gattc_proc *
ble_gattc_extract_first_by_conn_op(uint16_t conn_handle,uint8_t op)986 ble_gattc_extract_first_by_conn_op(uint16_t conn_handle, uint8_t op)
987 {
988 struct ble_gattc_proc_list dst_list;
989
990 ble_gattc_extract_by_conn_op(conn_handle, op, 1, &dst_list);
991 return STAILQ_FIRST(&dst_list);
992 }
993
994 static int
ble_gattc_proc_matches_stalled(struct ble_gattc_proc * proc,void * unused)995 ble_gattc_proc_matches_stalled(struct ble_gattc_proc *proc, void *unused)
996 {
997 return proc->flags & BLE_GATTC_PROC_F_STALLED;
998 }
999
1000 static void
ble_gattc_extract_stalled(struct ble_gattc_proc_list * dst_list)1001 ble_gattc_extract_stalled(struct ble_gattc_proc_list *dst_list)
1002 {
1003 ble_gattc_extract(ble_gattc_proc_matches_stalled, NULL, 0, dst_list);
1004 }
1005
1006 /**
1007 * @return The number of ticks until the next expiration
1008 * occurs.
1009 */
1010 static int32_t
ble_gattc_extract_expired(struct ble_gattc_proc_list * dst_list)1011 ble_gattc_extract_expired(struct ble_gattc_proc_list *dst_list)
1012 {
1013 struct ble_gattc_criteria_exp criteria;
1014
1015 criteria.now = ble_npl_time_get();
1016 criteria.next_exp_in = BLE_HS_FOREVER;
1017
1018 STAILQ_INIT(dst_list);
1019 ble_gattc_extract(ble_gattc_proc_matches_expired, &criteria, 0, dst_list);
1020
1021 return criteria.next_exp_in;
1022 }
1023
1024 static struct ble_gattc_proc *
ble_gattc_extract_with_rx_entry(uint16_t conn_handle,const void * rx_entries,int num_rx_entries,const void ** out_rx_entry)1025 ble_gattc_extract_with_rx_entry(uint16_t conn_handle,
1026 const void *rx_entries, int num_rx_entries,
1027 const void **out_rx_entry)
1028 {
1029 struct ble_gattc_criteria_conn_rx_entry criteria;
1030 struct ble_gattc_proc *proc;
1031
1032 criteria.conn_handle = conn_handle;
1033 criteria.rx_entries = rx_entries;
1034 criteria.num_rx_entries = num_rx_entries;
1035 criteria.matching_rx_entry = NULL;
1036
1037 proc = ble_gattc_extract_one(ble_gattc_proc_matches_conn_rx_entry,
1038 &criteria);
1039 *out_rx_entry = criteria.matching_rx_entry;
1040
1041 return proc;
1042 }
1043
1044 /**
1045 * Searches the main proc list for an entry whose connection handle and op code
1046 * match those specified. If a matching entry is found, it is removed from the
1047 * list and returned.
1048 *
1049 * @param conn_handle The connection handle to match against.
1050 * @param rx_entries The array of rx entries corresponding to the
1051 * op code of the incoming response.
1052 * @param out_rx_entry On success, the address of the matching rx
1053 * entry is written to this pointer.
1054 *
1055 * @return The matching proc entry on success;
1056 * null on failure.
1057 */
1058 #define BLE_GATTC_RX_EXTRACT_RX_ENTRY(conn_handle, rx_entries, out_rx_entry) \
1059 ble_gattc_extract_with_rx_entry( \
1060 (conn_handle), (rx_entries), \
1061 sizeof (rx_entries) / sizeof (rx_entries)[0], \
1062 (const void **)(out_rx_entry))
1063
1064
1065 /**
1066 * Causes all GATT procedures matching the specified criteria to fail with the
1067 * specified status code.
1068 */
1069 static void
ble_gattc_fail_procs(uint16_t conn_handle,uint8_t op,int status)1070 ble_gattc_fail_procs(uint16_t conn_handle, uint8_t op, int status)
1071 {
1072 struct ble_gattc_proc_list temp_list;
1073 struct ble_gattc_proc *proc;
1074 ble_gattc_err_fn *err_cb;
1075
1076 /* Remove all procs with the specified conn handle-op-pair and insert them
1077 * into the temporary list.
1078 */
1079 ble_gattc_extract_by_conn_op(conn_handle, op, 0, &temp_list);
1080
1081 /* Notify application of failed procedures and free the corresponding proc
1082 * entries.
1083 */
1084 while ((proc = STAILQ_FIRST(&temp_list)) != NULL) {
1085 err_cb = ble_gattc_err_dispatch_get(proc->op);
1086 err_cb(proc, status, 0);
1087
1088 STAILQ_REMOVE_HEAD(&temp_list, next);
1089 ble_gattc_proc_free(proc);
1090 }
1091 }
1092
1093 static void
ble_gattc_resume_procs(void)1094 ble_gattc_resume_procs(void)
1095 {
1096 struct ble_gattc_proc_list stall_list;
1097 struct ble_gattc_proc *proc;
1098 ble_gattc_resume_fn *resume_cb;
1099 int rc;
1100
1101 /* Cancel resume timer since it is being serviced. */
1102 ble_gattc_resume_at = 0;
1103
1104 ble_gattc_extract_stalled(&stall_list);
1105
1106 STAILQ_FOREACH(proc, &stall_list, next) {
1107 resume_cb = ble_gattc_resume_dispatch_get(proc->op);
1108 BLE_HS_DBG_ASSERT(resume_cb != NULL);
1109
1110 proc->flags &= ~BLE_GATTC_PROC_F_STALLED;
1111 rc = resume_cb(proc);
1112 ble_gattc_process_status(proc, rc);
1113 }
1114 }
1115
1116 static int32_t
ble_gattc_ticks_until_resume(void)1117 ble_gattc_ticks_until_resume(void)
1118 {
1119 ble_npl_time_t now;
1120 int32_t diff;
1121
1122 /* Resume timer not set. */
1123 if (ble_gattc_resume_at == 0) {
1124 return BLE_HS_FOREVER;
1125 }
1126
1127 now = ble_npl_time_get();
1128 diff = ble_gattc_resume_at - now;
1129 if (diff <= 0) {
1130 /* Timer already expired; resume immediately. */
1131 return 0;
1132 }
1133
1134 return diff;
1135 }
1136
1137 static void
ble_gattc_proc_timeout(struct ble_gattc_proc * proc)1138 ble_gattc_proc_timeout(struct ble_gattc_proc *proc)
1139 {
1140 ble_gattc_tmo_fn *cb;
1141
1142 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
1143 ble_gattc_dbg_assert_proc_not_inserted(proc);
1144
1145 cb = ble_gattc_tmo_dispatch_get(proc->op);
1146 if (cb != NULL) {
1147 cb(proc);
1148 }
1149 }
1150
1151 /**
1152 * Times out expired GATT client procedures.
1153 *
1154 * @return The number of ticks until this function should
1155 * be called again.
1156 */
1157 int32_t
ble_gattc_timer(void)1158 ble_gattc_timer(void)
1159 {
1160 struct ble_gattc_proc_list exp_list;
1161 struct ble_gattc_proc *proc;
1162 int32_t ticks_until_resume;
1163 int32_t ticks_until_exp;
1164
1165 /* Remove timed-out procedures from the main list and insert them into a
1166 * temporary list. This function also calculates the number of ticks until
1167 * the next expiration will occur.
1168 */
1169 ticks_until_exp = ble_gattc_extract_expired(&exp_list);
1170
1171 /* Terminate the connection associated with each timed-out procedure. */
1172 while ((proc = STAILQ_FIRST(&exp_list)) != NULL) {
1173 STATS_INC(ble_gattc_stats, proc_timeout);
1174
1175 ble_gattc_proc_timeout(proc);
1176
1177 ble_gap_terminate(proc->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
1178
1179 STAILQ_REMOVE_HEAD(&exp_list, next);
1180 ble_gattc_proc_free(proc);
1181 }
1182
1183 /* If there are stalled procedures, the GATT client will need to wake up to
1184 * resume them.
1185 */
1186 ticks_until_resume = ble_gattc_ticks_until_resume();
1187 if (ticks_until_resume == 0) {
1188 ble_gattc_resume_procs();
1189 ticks_until_resume = ble_gattc_ticks_until_resume();
1190 }
1191
1192 return min(ticks_until_exp, ticks_until_resume);
1193 }
1194
1195 /**
1196 * Returns a pointer to a GATT error object with the specified fields. The
1197 * returned object is statically allocated, so this function is not reentrant.
1198 * This function should only ever be called by the ble_hs task.
1199 */
1200 static struct ble_gatt_error *
ble_gattc_error(int status,uint16_t att_handle)1201 ble_gattc_error(int status, uint16_t att_handle)
1202 {
1203 static struct ble_gatt_error error;
1204
1205 /* For consistency, always indicate a handle of 0 on success. */
1206 if (status == 0 || status == BLE_HS_EDONE) {
1207 att_handle = 0;
1208 }
1209
1210 error.status = status;
1211 error.att_handle = att_handle;
1212 return &error;
1213 }
1214
1215 /*****************************************************************************
1216 * $mtu *
1217 *****************************************************************************/
1218
1219 /**
1220 * Calls an mtu-exchange proc's callback with the specified parameters. If the
1221 * proc has no callback, this function is a no-op.
1222 *
1223 * @return The return code of the callback (or 0 if there
1224 * is no callback).
1225 */
1226 static int
ble_gattc_mtu_cb(struct ble_gattc_proc * proc,int status,uint16_t att_handle,uint16_t mtu)1227 ble_gattc_mtu_cb(struct ble_gattc_proc *proc, int status, uint16_t att_handle,
1228 uint16_t mtu)
1229 {
1230 int rc;
1231
1232 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
1233 ble_gattc_dbg_assert_proc_not_inserted(proc);
1234
1235 if (status != 0 && status != BLE_HS_EDONE) {
1236 STATS_INC(ble_gattc_stats, mtu_fail);
1237 }
1238
1239 if (proc->mtu.cb == NULL) {
1240 rc = 0;
1241 } else {
1242 rc = proc->mtu.cb(proc->conn_handle,
1243 ble_gattc_error(status, att_handle),
1244 mtu, proc->mtu.cb_arg);
1245 }
1246
1247 return rc;
1248 }
1249
1250 static void
ble_gattc_mtu_tmo(struct ble_gattc_proc * proc)1251 ble_gattc_mtu_tmo(struct ble_gattc_proc *proc)
1252 {
1253 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
1254 ble_gattc_dbg_assert_proc_not_inserted(proc);
1255
1256 ble_gattc_mtu_cb(proc, BLE_HS_ETIMEOUT, 0, 0);
1257 }
1258
1259 /**
1260 * Handles an incoming ATT error response for the specified mtu-exchange proc.
1261 */
1262 static void
ble_gattc_mtu_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)1263 ble_gattc_mtu_err(struct ble_gattc_proc *proc, int status, uint16_t att_handle)
1264 {
1265 ble_gattc_dbg_assert_proc_not_inserted(proc);
1266 ble_gattc_mtu_cb(proc, status, att_handle, 0);
1267 }
1268
1269 static int
ble_gattc_mtu_tx(struct ble_gattc_proc * proc)1270 ble_gattc_mtu_tx(struct ble_gattc_proc *proc)
1271 {
1272 struct ble_l2cap_chan *chan;
1273 struct ble_hs_conn *conn;
1274 uint16_t mtu;
1275 int rc;
1276
1277 ble_hs_lock();
1278 rc = ble_att_conn_chan_find(proc->conn_handle, &conn, &chan);
1279 if (rc == 0) {
1280 mtu = chan->my_mtu;
1281 }
1282 ble_hs_unlock();
1283
1284 if (rc == 0) {
1285 rc = ble_att_clt_tx_mtu(proc->conn_handle, mtu);
1286 }
1287
1288 return rc;
1289 }
1290
1291 int
ble_gattc_exchange_mtu(uint16_t conn_handle,ble_gatt_mtu_fn * cb,void * cb_arg)1292 ble_gattc_exchange_mtu(uint16_t conn_handle, ble_gatt_mtu_fn *cb, void *cb_arg)
1293 {
1294 struct ble_gattc_proc *proc;
1295 int rc;
1296
1297 STATS_INC(ble_gattc_stats, mtu);
1298
1299 proc = ble_gattc_proc_alloc();
1300 if (proc == NULL) {
1301 rc = BLE_HS_ENOMEM;
1302 goto done;
1303 }
1304
1305 proc->op = BLE_GATT_OP_MTU;
1306 proc->conn_handle = conn_handle;
1307 proc->mtu.cb = cb;
1308 proc->mtu.cb_arg = cb_arg;
1309
1310 ble_gattc_log_proc_init("exchange mtu\n");
1311
1312 rc = ble_gattc_mtu_tx(proc);
1313 if (rc != 0) {
1314 goto done;
1315 }
1316
1317 done:
1318 if (rc != 0) {
1319 STATS_INC(ble_gattc_stats, mtu_fail);
1320 }
1321
1322 ble_gattc_process_status(proc, rc);
1323 return rc;
1324 }
1325
1326 /*****************************************************************************
1327 * $discover all services *
1328 *****************************************************************************/
1329
1330 /**
1331 * Calls a discover-all-services proc's callback with the specified parameters.
1332 * If the proc has no callback, this function is a no-op.
1333 *
1334 * @return The return code of the callback (or 0 if there
1335 * is no callback).
1336 */
1337 static int
ble_gattc_disc_all_svcs_cb(struct ble_gattc_proc * proc,uint16_t status,uint16_t att_handle,struct ble_gatt_svc * service)1338 ble_gattc_disc_all_svcs_cb(struct ble_gattc_proc *proc,
1339 uint16_t status, uint16_t att_handle,
1340 struct ble_gatt_svc *service)
1341 {
1342 int rc;
1343
1344 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
1345 BLE_HS_DBG_ASSERT(service != NULL || status != 0);
1346 ble_gattc_dbg_assert_proc_not_inserted(proc);
1347
1348 if (status != 0 && status != BLE_HS_EDONE) {
1349 STATS_INC(ble_gattc_stats, disc_all_svcs_fail);
1350 }
1351
1352 if (proc->disc_all_svcs.cb == NULL) {
1353 rc = 0;
1354 } else {
1355 rc = proc->disc_all_svcs.cb(proc->conn_handle,
1356 ble_gattc_error(status, att_handle),
1357 service, proc->disc_all_svcs.cb_arg);
1358 }
1359
1360 return rc;
1361 }
1362
1363 static void
ble_gattc_disc_all_svcs_tmo(struct ble_gattc_proc * proc)1364 ble_gattc_disc_all_svcs_tmo(struct ble_gattc_proc *proc)
1365 {
1366 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
1367 ble_gattc_dbg_assert_proc_not_inserted(proc);
1368
1369 ble_gattc_disc_all_svcs_cb(proc, BLE_HS_ETIMEOUT, 0, 0);
1370 }
1371
1372 /**
1373 * Triggers a pending transmit for the specified discover-all-services proc.
1374 */
1375 static int
ble_gattc_disc_all_svcs_tx(struct ble_gattc_proc * proc)1376 ble_gattc_disc_all_svcs_tx(struct ble_gattc_proc *proc)
1377 {
1378 ble_uuid16_t uuid = BLE_UUID16_INIT(BLE_ATT_UUID_PRIMARY_SERVICE);
1379 int rc;
1380
1381 ble_gattc_dbg_assert_proc_not_inserted(proc);
1382
1383 rc = ble_att_clt_tx_read_group_type(proc->conn_handle,
1384 proc->disc_all_svcs.prev_handle + 1,
1385 0xffff, &uuid.u);
1386 if (rc != 0) {
1387 return rc;
1388 }
1389
1390 return 0;
1391 }
1392
1393 static int
ble_gattc_disc_all_svcs_resume(struct ble_gattc_proc * proc)1394 ble_gattc_disc_all_svcs_resume(struct ble_gattc_proc *proc)
1395 {
1396 int status;
1397 int rc;
1398
1399 status = ble_gattc_disc_all_svcs_tx(proc);
1400 rc = ble_gattc_process_resume_status(proc, status);
1401 if (rc != 0) {
1402 ble_gattc_disc_all_svcs_cb(proc, rc, 0, NULL);
1403 return rc;
1404 }
1405
1406 return 0;
1407 }
1408
1409 /**
1410 * Handles an incoming ATT error response for the specified
1411 * discover-all-services proc.
1412 */
1413 static void
ble_gattc_disc_all_svcs_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)1414 ble_gattc_disc_all_svcs_err(struct ble_gattc_proc *proc, int status,
1415 uint16_t att_handle)
1416 {
1417 ble_gattc_dbg_assert_proc_not_inserted(proc);
1418
1419 if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_FOUND)) {
1420 /* Discovery is complete. */
1421 status = BLE_HS_EDONE;
1422 }
1423
1424 ble_gattc_disc_all_svcs_cb(proc, status, att_handle, NULL);
1425 }
1426
1427 /**
1428 * Handles an incoming attribute data entry from a read-group-type response for
1429 * the specified discover-all-services proc.
1430 */
1431 static int
ble_gattc_disc_all_svcs_rx_adata(struct ble_gattc_proc * proc,struct ble_att_read_group_type_adata * adata)1432 ble_gattc_disc_all_svcs_rx_adata(struct ble_gattc_proc *proc,
1433 struct ble_att_read_group_type_adata *adata)
1434 {
1435 struct ble_gatt_svc service;
1436 int cbrc;
1437 int rc;
1438
1439 ble_gattc_dbg_assert_proc_not_inserted(proc);
1440
1441 switch (adata->value_len) {
1442 case 2:
1443 case 16:
1444 rc = ble_uuid_init_from_att_buf(&service.uuid, adata->value,
1445 adata->value_len);
1446 if (rc != 0) {
1447 rc = BLE_HS_EBADDATA;
1448 goto done;
1449 }
1450 break;
1451
1452 default:
1453 rc = BLE_HS_EBADDATA;
1454 goto done;
1455 }
1456
1457 if (adata->end_group_handle <= proc->disc_all_svcs.prev_handle) {
1458 /* Peer sent services out of order; terminate procedure. */
1459 rc = BLE_HS_EBADDATA;
1460 goto done;
1461 }
1462
1463 proc->disc_all_svcs.prev_handle = adata->end_group_handle;
1464
1465 service.start_handle = adata->att_handle;
1466 service.end_handle = adata->end_group_handle;
1467
1468 rc = 0;
1469
1470 done:
1471 cbrc = ble_gattc_disc_all_svcs_cb(proc, rc, 0, &service);
1472 if (rc != 0 || cbrc != 0) {
1473 return BLE_HS_EDONE;
1474 } else {
1475 return 0;
1476 }
1477 }
1478
1479 /**
1480 * Handles a notification that an incoming read-group-type response has been
1481 * fully processed.
1482 */
1483 static int
ble_gattc_disc_all_svcs_rx_complete(struct ble_gattc_proc * proc,int status)1484 ble_gattc_disc_all_svcs_rx_complete(struct ble_gattc_proc *proc, int status)
1485 {
1486 int rc;
1487
1488 ble_gattc_dbg_assert_proc_not_inserted(proc);
1489
1490 if (status != 0) {
1491 ble_gattc_disc_all_svcs_cb(proc, status, 0, NULL);
1492 return BLE_HS_EDONE;
1493 }
1494
1495 if (proc->disc_all_svcs.prev_handle == 0xffff) {
1496 /* Service discovery complete. */
1497 ble_gattc_disc_all_svcs_cb(proc, BLE_HS_EDONE, 0, NULL);
1498 return BLE_HS_EDONE;
1499 }
1500
1501 /* Send follow-up request. */
1502 rc = ble_gattc_disc_all_svcs_resume(proc);
1503 if (rc != 0) {
1504 return BLE_HS_EDONE;
1505 }
1506
1507 return 0;
1508 }
1509
1510 int
ble_gattc_disc_all_svcs(uint16_t conn_handle,ble_gatt_disc_svc_fn * cb,void * cb_arg)1511 ble_gattc_disc_all_svcs(uint16_t conn_handle, ble_gatt_disc_svc_fn *cb,
1512 void *cb_arg)
1513 {
1514 #if !MYNEWT_VAL(BLE_GATT_DISC_ALL_SVCS)
1515 return BLE_HS_ENOTSUP;
1516 #endif
1517
1518 struct ble_gattc_proc *proc;
1519 int rc;
1520
1521 STATS_INC(ble_gattc_stats, disc_all_svcs);
1522
1523 proc = ble_gattc_proc_alloc();
1524 if (proc == NULL) {
1525 rc = BLE_HS_ENOMEM;
1526 goto done;
1527 }
1528
1529 proc->op = BLE_GATT_OP_DISC_ALL_SVCS;
1530 proc->conn_handle = conn_handle;
1531 proc->disc_all_svcs.prev_handle = 0x0000;
1532 proc->disc_all_svcs.cb = cb;
1533 proc->disc_all_svcs.cb_arg = cb_arg;
1534
1535 ble_gattc_log_proc_init("discover all services\n");
1536
1537 rc = ble_gattc_disc_all_svcs_tx(proc);
1538 if (rc != 0) {
1539 goto done;
1540 }
1541
1542 done:
1543 if (rc != 0) {
1544 STATS_INC(ble_gattc_stats, disc_all_svcs_fail);
1545 }
1546
1547 ble_gattc_process_status(proc, rc);
1548 return rc;
1549 }
1550
1551 /*****************************************************************************
1552 * $discover service by uuid *
1553 *****************************************************************************/
1554
1555 /**
1556 * Calls a discover-service-by-uuid proc's callback with the specified
1557 * parameters. If the proc has no callback, this function is a no-op.
1558 *
1559 * @return The return code of the callback (or 0 if there
1560 * is no callback).
1561 */
1562 static int
ble_gattc_disc_svc_uuid_cb(struct ble_gattc_proc * proc,int status,uint16_t att_handle,struct ble_gatt_svc * service)1563 ble_gattc_disc_svc_uuid_cb(struct ble_gattc_proc *proc, int status,
1564 uint16_t att_handle,
1565 struct ble_gatt_svc *service)
1566 {
1567 int rc;
1568
1569 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
1570 BLE_HS_DBG_ASSERT(service != NULL || status != 0);
1571 ble_gattc_dbg_assert_proc_not_inserted(proc);
1572
1573 if (status != 0 && status != BLE_HS_EDONE) {
1574 STATS_INC(ble_gattc_stats, disc_svc_uuid_fail);
1575 }
1576
1577 if (proc->disc_svc_uuid.cb == NULL) {
1578 rc = 0;
1579 } else {
1580 rc = proc->disc_svc_uuid.cb(proc->conn_handle,
1581 ble_gattc_error(status, att_handle),
1582 service, proc->disc_svc_uuid.cb_arg);
1583 }
1584
1585 return rc;
1586 }
1587
1588 static void
ble_gattc_disc_svc_uuid_tmo(struct ble_gattc_proc * proc)1589 ble_gattc_disc_svc_uuid_tmo(struct ble_gattc_proc *proc)
1590 {
1591 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
1592 ble_gattc_dbg_assert_proc_not_inserted(proc);
1593
1594 ble_gattc_disc_svc_uuid_cb(proc, BLE_HS_ETIMEOUT, 0, 0);
1595 }
1596
1597 /**
1598 * Triggers a pending transmit for the specified discover-service-by-uuid proc.
1599 */
1600 static int
ble_gattc_disc_svc_uuid_tx(struct ble_gattc_proc * proc)1601 ble_gattc_disc_svc_uuid_tx(struct ble_gattc_proc *proc)
1602 {
1603 uint8_t val[16];
1604 int rc;
1605
1606 ble_gattc_dbg_assert_proc_not_inserted(proc);
1607
1608 ble_uuid_flat(&proc->disc_svc_uuid.service_uuid.u, val);
1609 rc = ble_att_clt_tx_find_type_value(proc->conn_handle,
1610 proc->disc_svc_uuid.prev_handle + 1,
1611 0xffff, BLE_ATT_UUID_PRIMARY_SERVICE,
1612 val,
1613 ble_uuid_length(&proc->disc_svc_uuid.service_uuid.u));
1614 if (rc != 0) {
1615 return rc;
1616 }
1617
1618 return 0;
1619 }
1620
1621 static int
ble_gattc_disc_svc_uuid_resume(struct ble_gattc_proc * proc)1622 ble_gattc_disc_svc_uuid_resume(struct ble_gattc_proc *proc)
1623 {
1624 int status;
1625 int rc;
1626
1627 status = ble_gattc_disc_svc_uuid_tx(proc);
1628 rc = ble_gattc_process_resume_status(proc, status);
1629 if (rc != 0) {
1630 ble_gattc_disc_svc_uuid_cb(proc, rc, 0, NULL);
1631 return rc;
1632 }
1633
1634 return 0;
1635 }
1636
1637 /**
1638 * Handles an incoming ATT error response for the specified
1639 * discover-service-by-uuid proc.
1640 */
1641 static void
ble_gattc_disc_svc_uuid_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)1642 ble_gattc_disc_svc_uuid_err(struct ble_gattc_proc *proc, int status,
1643 uint16_t att_handle)
1644 {
1645 ble_gattc_dbg_assert_proc_not_inserted(proc);
1646
1647 if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_FOUND)) {
1648 /* Discovery is complete. */
1649 status = BLE_HS_EDONE;
1650 }
1651
1652 ble_gattc_disc_svc_uuid_cb(proc, status, att_handle, NULL);
1653 }
1654
1655 /**
1656 * Handles an incoming "handles info" entry from a find-type-value response for
1657 * the specified discover-service-by-uuid proc.
1658 */
1659 static int
ble_gattc_disc_svc_uuid_rx_hinfo(struct ble_gattc_proc * proc,struct ble_att_find_type_value_hinfo * hinfo)1660 ble_gattc_disc_svc_uuid_rx_hinfo(struct ble_gattc_proc *proc,
1661 struct ble_att_find_type_value_hinfo *hinfo)
1662 {
1663 struct ble_gatt_svc service;
1664 int cbrc;
1665 int rc;
1666
1667 ble_gattc_dbg_assert_proc_not_inserted(proc);
1668
1669 if (hinfo->group_end_handle <= proc->disc_svc_uuid.prev_handle) {
1670 /* Peer sent services out of order; terminate procedure. */
1671 rc = BLE_HS_EBADDATA;
1672 goto done;
1673 }
1674
1675 proc->disc_svc_uuid.prev_handle = hinfo->group_end_handle;
1676
1677 service.start_handle = hinfo->attr_handle;
1678 service.end_handle = hinfo->group_end_handle;
1679 service.uuid = proc->disc_svc_uuid.service_uuid;
1680
1681 rc = 0;
1682
1683 done:
1684 cbrc = ble_gattc_disc_svc_uuid_cb(proc, rc, 0, &service);
1685 if (rc != 0 || cbrc != 0) {
1686 return BLE_HS_EDONE;
1687 } else {
1688 return 0;
1689 }
1690 }
1691
1692 /**
1693 * Handles a notification that a find-type-value response has been fully
1694 * processed for the specified discover-service-by-uuid proc.
1695 */
1696 static int
ble_gattc_disc_svc_uuid_rx_complete(struct ble_gattc_proc * proc,int status)1697 ble_gattc_disc_svc_uuid_rx_complete(struct ble_gattc_proc *proc, int status)
1698 {
1699 int rc;
1700
1701 ble_gattc_dbg_assert_proc_not_inserted(proc);
1702
1703 if (status != 0) {
1704 ble_gattc_disc_svc_uuid_cb(proc, status, 0, NULL);
1705 return BLE_HS_EDONE;
1706 }
1707
1708 if (proc->disc_svc_uuid.prev_handle == 0xffff) {
1709 /* Service discovery complete. */
1710 ble_gattc_disc_svc_uuid_cb(proc, BLE_HS_EDONE, 0, NULL);
1711 return BLE_HS_EDONE;
1712 }
1713
1714 /* Send follow-up request. */
1715 rc = ble_gattc_disc_svc_uuid_resume(proc);
1716 if (rc != 0) {
1717 return BLE_HS_EDONE;
1718 }
1719
1720 return 0;
1721 }
1722
1723 int
ble_gattc_disc_svc_by_uuid(uint16_t conn_handle,const ble_uuid_t * uuid,ble_gatt_disc_svc_fn * cb,void * cb_arg)1724 ble_gattc_disc_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid,
1725 ble_gatt_disc_svc_fn *cb, void *cb_arg)
1726 {
1727 #if !MYNEWT_VAL(BLE_GATT_DISC_SVC_UUID)
1728 return BLE_HS_ENOTSUP;
1729 #endif
1730
1731 struct ble_gattc_proc *proc;
1732 int rc;
1733
1734 STATS_INC(ble_gattc_stats, disc_svc_uuid);
1735
1736 proc = ble_gattc_proc_alloc();
1737 if (proc == NULL) {
1738 rc = BLE_HS_ENOMEM;
1739 goto done;
1740 }
1741
1742 proc->op = BLE_GATT_OP_DISC_SVC_UUID;
1743 proc->conn_handle = conn_handle;
1744 ble_uuid_to_any(uuid, &proc->disc_svc_uuid.service_uuid);
1745 proc->disc_svc_uuid.prev_handle = 0x0000;
1746 proc->disc_svc_uuid.cb = cb;
1747 proc->disc_svc_uuid.cb_arg = cb_arg;
1748
1749 ble_gattc_log_disc_svc_uuid(proc);
1750
1751 rc = ble_gattc_disc_svc_uuid_tx(proc);
1752 if (rc != 0) {
1753 goto done;
1754 }
1755
1756 done:
1757 if (rc != 0) {
1758 STATS_INC(ble_gattc_stats, disc_svc_uuid_fail);
1759 }
1760
1761 ble_gattc_process_status(proc, rc);
1762 return rc;
1763 }
1764
1765 /*****************************************************************************
1766 * $find included svcs *
1767 *****************************************************************************/
1768
1769 /**
1770 * Calls a find-included-services proc's callback with the specified
1771 * parameters. If the proc has no callback, this function is a no-op.
1772 *
1773 * @return The return code of the callback (or 0 if there
1774 * is no callback).
1775 */
1776 static int
ble_gattc_find_inc_svcs_cb(struct ble_gattc_proc * proc,int status,uint16_t att_handle,struct ble_gatt_svc * service)1777 ble_gattc_find_inc_svcs_cb(struct ble_gattc_proc *proc, int status,
1778 uint16_t att_handle,
1779 struct ble_gatt_svc *service)
1780 {
1781 int rc;
1782
1783 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
1784 BLE_HS_DBG_ASSERT(service != NULL || status != 0);
1785 ble_gattc_dbg_assert_proc_not_inserted(proc);
1786
1787 if (status != 0 && status != BLE_HS_EDONE) {
1788 STATS_INC(ble_gattc_stats, find_inc_svcs_fail);
1789 }
1790
1791 if (proc->find_inc_svcs.cb == NULL) {
1792 rc = 0;
1793 } else {
1794 rc = proc->find_inc_svcs.cb(proc->conn_handle,
1795 ble_gattc_error(status, att_handle),
1796 service, proc->find_inc_svcs.cb_arg);
1797 }
1798
1799 return rc;
1800 }
1801
1802 static void
ble_gattc_find_inc_svcs_tmo(struct ble_gattc_proc * proc)1803 ble_gattc_find_inc_svcs_tmo(struct ble_gattc_proc *proc)
1804 {
1805 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
1806 ble_gattc_dbg_assert_proc_not_inserted(proc);
1807
1808 ble_gattc_find_inc_svcs_cb(proc, BLE_HS_ETIMEOUT, 0, 0);
1809 }
1810
1811 /**
1812 * Triggers a pending transmit for the specified find-included-services proc.
1813 */
1814 static int
ble_gattc_find_inc_svcs_tx(struct ble_gattc_proc * proc)1815 ble_gattc_find_inc_svcs_tx(struct ble_gattc_proc *proc)
1816 {
1817 ble_uuid16_t uuid = BLE_UUID16_INIT(BLE_ATT_UUID_INCLUDE);
1818 int rc;
1819
1820 ble_gattc_dbg_assert_proc_not_inserted(proc);
1821
1822 if (proc->find_inc_svcs.cur_start == 0) {
1823 /* Find the next included service. */
1824 rc = ble_att_clt_tx_read_type(proc->conn_handle,
1825 proc->find_inc_svcs.prev_handle + 1,
1826 proc->find_inc_svcs.end_handle, &uuid.u);
1827 if (rc != 0) {
1828 return rc;
1829 }
1830 } else {
1831 /* Read the UUID of the previously found service. */
1832 rc = ble_att_clt_tx_read(proc->conn_handle,
1833 proc->find_inc_svcs.cur_start);
1834 if (rc != 0) {
1835 return rc;
1836 }
1837 }
1838
1839 return 0;
1840 }
1841
1842 static int
ble_gattc_find_inc_svcs_resume(struct ble_gattc_proc * proc)1843 ble_gattc_find_inc_svcs_resume(struct ble_gattc_proc *proc)
1844 {
1845 int status;
1846 int rc;
1847
1848 status = ble_gattc_find_inc_svcs_tx(proc);
1849 rc = ble_gattc_process_resume_status(proc, status);
1850 if (rc != 0) {
1851 ble_gattc_find_inc_svcs_cb(proc, rc, 0, NULL);
1852 return rc;
1853 }
1854
1855 return 0;
1856 }
1857
1858 /**
1859 * Handles an incoming ATT error response for the specified
1860 * find-included-services proc.
1861 */
1862 static void
ble_gattc_find_inc_svcs_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)1863 ble_gattc_find_inc_svcs_err(struct ble_gattc_proc *proc, int status,
1864 uint16_t att_handle)
1865 {
1866 ble_gattc_dbg_assert_proc_not_inserted(proc);
1867
1868 if (proc->find_inc_svcs.cur_start == 0 &&
1869 status == BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_FOUND)) {
1870
1871 /* Discovery is complete. */
1872 status = BLE_HS_EDONE;
1873 }
1874
1875 ble_gattc_find_inc_svcs_cb(proc, status, att_handle, NULL);
1876 }
1877
1878 /**
1879 * Handles an incoming read-response for the specified find-included-services
1880 * proc.
1881 */
1882 static int
ble_gattc_find_inc_svcs_rx_read_rsp(struct ble_gattc_proc * proc,int status,struct os_mbuf ** om)1883 ble_gattc_find_inc_svcs_rx_read_rsp(struct ble_gattc_proc *proc, int status,
1884 struct os_mbuf **om)
1885 {
1886 struct ble_gatt_svc service;
1887 int rc;
1888
1889 ble_gattc_dbg_assert_proc_not_inserted(proc);
1890
1891 rc = ble_uuid_init_from_att_mbuf(&service.uuid, *om, 0, 16);
1892 os_mbuf_free_chain(*om);
1893 *om = NULL;
1894
1895 if (rc != 0) {
1896 /* Invalid UUID. */
1897 rc = BLE_HS_EBADDATA;
1898 goto err;
1899 }
1900
1901 if (proc->find_inc_svcs.cur_start == 0) {
1902 /* Unexpected read response; terminate procedure. */
1903 rc = BLE_HS_EBADDATA;
1904 goto err;
1905 }
1906
1907 if (status != 0) {
1908 rc = status;
1909 goto err;
1910 }
1911
1912 /* Report discovered service to application. */
1913 service.start_handle = proc->find_inc_svcs.cur_start;
1914 service.end_handle = proc->find_inc_svcs.cur_end;
1915 rc = ble_gattc_find_inc_svcs_cb(proc, 0, 0, &service);
1916 if (rc != 0) {
1917 /* Application has indicated that the procedure should be aborted. */
1918 return BLE_HS_EDONE;
1919 }
1920
1921 /* Proceed to the next service. */
1922 proc->find_inc_svcs.cur_start = 0;
1923 proc->find_inc_svcs.cur_end = 0;
1924 rc = ble_gattc_find_inc_svcs_resume(proc);
1925 if (rc != 0) {
1926 goto err;
1927 }
1928
1929 return 0;
1930
1931 err:
1932 ble_gattc_find_inc_svcs_cb(proc, rc, 0, NULL);
1933 return BLE_HS_EDONE;
1934 }
1935
1936 /**
1937 * Handles an incoming "attribute data" entry from a read-by-type response for
1938 * the specified find-included-services proc.
1939 */
1940 static int
ble_gattc_find_inc_svcs_rx_adata(struct ble_gattc_proc * proc,struct ble_att_read_type_adata * adata)1941 ble_gattc_find_inc_svcs_rx_adata(struct ble_gattc_proc *proc,
1942 struct ble_att_read_type_adata *adata)
1943 {
1944 struct ble_gatt_svc service;
1945 int call_cb;
1946 int cbrc;
1947 int rc;
1948
1949 ble_gattc_dbg_assert_proc_not_inserted(proc);
1950
1951 if (proc->find_inc_svcs.cur_start != 0) {
1952 /* We only read one 128-bit UUID service at a time. Ignore the
1953 * additional services in the response.
1954 */
1955 return 0;
1956 }
1957
1958 call_cb = 1;
1959
1960 if (adata->att_handle <= proc->find_inc_svcs.prev_handle) {
1961 /* Peer sent services out of order; terminate procedure. */
1962 rc = BLE_HS_EBADDATA;
1963 goto done;
1964 }
1965
1966 proc->find_inc_svcs.prev_handle = adata->att_handle;
1967
1968 rc = 0;
1969
1970 switch (adata->value_len) {
1971 case BLE_GATTS_INC_SVC_LEN_NO_UUID:
1972 proc->find_inc_svcs.cur_start = get_le16(adata->value + 0);
1973 proc->find_inc_svcs.cur_end = get_le16(adata->value + 2);
1974 call_cb = 0;
1975 break;
1976
1977 case BLE_GATTS_INC_SVC_LEN_UUID:
1978 service.start_handle = get_le16(adata->value + 0);
1979 service.end_handle = get_le16(adata->value + 2);
1980 rc = ble_uuid_init_from_att_buf(&service.uuid, adata->value + 4, 2);
1981 if (rc != 0) {
1982 rc = BLE_HS_EBADDATA;
1983 }
1984 break;
1985
1986 default:
1987 rc = BLE_HS_EBADDATA;
1988 break;
1989 }
1990
1991 done:
1992 if (call_cb) {
1993 cbrc = ble_gattc_find_inc_svcs_cb(proc, 0, 0, &service);
1994 if (rc != 0) {
1995 rc = cbrc;
1996 }
1997 } else {
1998 cbrc = 0;
1999 }
2000
2001 if (rc != 0 || cbrc != 0) {
2002 return BLE_HS_EDONE;
2003 } else {
2004 return 0;
2005 }
2006 }
2007
2008 /**
2009 * Handles a notification that a read-by-type response has been fully
2010 * processed for the specified find-included-services proc.
2011 */
2012 static int
ble_gattc_find_inc_svcs_rx_complete(struct ble_gattc_proc * proc,int status)2013 ble_gattc_find_inc_svcs_rx_complete(struct ble_gattc_proc *proc, int status)
2014 {
2015 int rc;
2016
2017 ble_gattc_dbg_assert_proc_not_inserted(proc);
2018
2019 if (status != 0) {
2020 ble_gattc_find_inc_svcs_cb(proc, status, 0, NULL);
2021 return BLE_HS_EDONE;
2022 }
2023
2024 if (proc->find_inc_svcs.prev_handle == 0xffff) {
2025 /* Procedure complete. */
2026 ble_gattc_find_inc_svcs_cb(proc, BLE_HS_EDONE, 0, NULL);
2027 return BLE_HS_EDONE;
2028 }
2029
2030 /* Send follow-up request. */
2031 rc = ble_gattc_find_inc_svcs_resume(proc);
2032 if (rc != 0) {
2033 return BLE_HS_EDONE;
2034 }
2035 return 0;
2036 }
2037
2038 int
ble_gattc_find_inc_svcs(uint16_t conn_handle,uint16_t start_handle,uint16_t end_handle,ble_gatt_disc_svc_fn * cb,void * cb_arg)2039 ble_gattc_find_inc_svcs(uint16_t conn_handle, uint16_t start_handle,
2040 uint16_t end_handle,
2041 ble_gatt_disc_svc_fn *cb, void *cb_arg)
2042 {
2043 #if !MYNEWT_VAL(BLE_GATT_FIND_INC_SVCS)
2044 return BLE_HS_ENOTSUP;
2045 #endif
2046
2047 struct ble_gattc_proc *proc;
2048 int rc;
2049
2050 STATS_INC(ble_gattc_stats, find_inc_svcs);
2051
2052 proc = ble_gattc_proc_alloc();
2053 if (proc == NULL) {
2054 rc = BLE_HS_ENOMEM;
2055 goto done;
2056 }
2057
2058 proc->op = BLE_GATT_OP_FIND_INC_SVCS;
2059 proc->conn_handle = conn_handle;
2060 proc->find_inc_svcs.prev_handle = start_handle - 1;
2061 proc->find_inc_svcs.end_handle = end_handle;
2062 proc->find_inc_svcs.cb = cb;
2063 proc->find_inc_svcs.cb_arg = cb_arg;
2064
2065 ble_gattc_log_find_inc_svcs(proc);
2066
2067 rc = ble_gattc_find_inc_svcs_tx(proc);
2068 if (rc != 0) {
2069 goto done;
2070 }
2071
2072 done:
2073 if (rc != 0) {
2074 STATS_INC(ble_gattc_stats, find_inc_svcs_fail);
2075 }
2076
2077 ble_gattc_process_status(proc, rc);
2078 return rc;
2079 }
2080
2081 /*****************************************************************************
2082 * $discover all characteristics *
2083 *****************************************************************************/
2084
2085 /**
2086 * Calls a discover-all-characteristics proc's callback with the specified
2087 * parameters. If the proc has no callback, this function is a no-op.
2088 *
2089 * @return The return code of the callback (or 0 if there
2090 * is no callback).
2091 */
2092 static int
ble_gattc_disc_all_chrs_cb(struct ble_gattc_proc * proc,int status,uint16_t att_handle,struct ble_gatt_chr * chr)2093 ble_gattc_disc_all_chrs_cb(struct ble_gattc_proc *proc, int status,
2094 uint16_t att_handle, struct ble_gatt_chr *chr)
2095 {
2096 int rc;
2097
2098 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
2099 BLE_HS_DBG_ASSERT(chr != NULL || status != 0);
2100 ble_gattc_dbg_assert_proc_not_inserted(proc);
2101
2102 if (status != 0 && status != BLE_HS_EDONE) {
2103 STATS_INC(ble_gattc_stats, disc_all_chrs_fail);
2104 }
2105
2106 if (proc->disc_all_chrs.cb == NULL) {
2107 rc = 0;
2108 } else {
2109 rc = proc->disc_all_chrs.cb(proc->conn_handle,
2110 ble_gattc_error(status, att_handle), chr,
2111 proc->disc_all_chrs.cb_arg);
2112 }
2113
2114 return rc;
2115 }
2116
2117 static void
ble_gattc_disc_all_chrs_tmo(struct ble_gattc_proc * proc)2118 ble_gattc_disc_all_chrs_tmo(struct ble_gattc_proc *proc)
2119 {
2120 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
2121 ble_gattc_dbg_assert_proc_not_inserted(proc);
2122
2123 ble_gattc_disc_all_chrs_cb(proc, BLE_HS_ETIMEOUT, 0, NULL);
2124 }
2125
2126 /**
2127 * Triggers a pending transmit for the specified discover-all-characteristics
2128 * proc.
2129 */
2130 static int
ble_gattc_disc_all_chrs_tx(struct ble_gattc_proc * proc)2131 ble_gattc_disc_all_chrs_tx(struct ble_gattc_proc *proc)
2132 {
2133 ble_uuid16_t uuid = BLE_UUID16_INIT(BLE_ATT_UUID_CHARACTERISTIC);
2134 int rc;
2135
2136 ble_gattc_dbg_assert_proc_not_inserted(proc);
2137
2138 rc = ble_att_clt_tx_read_type(proc->conn_handle,
2139 proc->disc_all_chrs.prev_handle + 1,
2140 proc->disc_all_chrs.end_handle, &uuid.u);
2141 if (rc != 0) {
2142 return rc;
2143 }
2144
2145 return 0;
2146 }
2147
2148 static int
ble_gattc_disc_all_chrs_resume(struct ble_gattc_proc * proc)2149 ble_gattc_disc_all_chrs_resume(struct ble_gattc_proc *proc)
2150 {
2151 int status;
2152 int rc;
2153
2154 status = ble_gattc_disc_all_chrs_tx(proc);
2155 rc = ble_gattc_process_resume_status(proc, status);
2156 if (rc != 0) {
2157 ble_gattc_disc_all_chrs_cb(proc, rc, 0, NULL);
2158 return rc;
2159 }
2160
2161 return 0;
2162 }
2163
2164 /**
2165 * Handles an incoming ATT error response for the specified
2166 * discover-all-characteristics proc.
2167 */
2168 static void
ble_gattc_disc_all_chrs_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)2169 ble_gattc_disc_all_chrs_err(struct ble_gattc_proc *proc, int status,
2170 uint16_t att_handle)
2171 {
2172 ble_gattc_dbg_assert_proc_not_inserted(proc);
2173
2174 if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_FOUND)) {
2175 /* Discovery is complete. */
2176 status = BLE_HS_EDONE;
2177 }
2178
2179 ble_gattc_disc_all_chrs_cb(proc, status, att_handle, NULL);
2180 }
2181
2182 /**
2183 * Handles an incoming "attribute data" entry from a read-by-type response for
2184 * the specified discover-all-characteristics proc.
2185 */
2186 static int
ble_gattc_disc_all_chrs_rx_adata(struct ble_gattc_proc * proc,struct ble_att_read_type_adata * adata)2187 ble_gattc_disc_all_chrs_rx_adata(struct ble_gattc_proc *proc,
2188 struct ble_att_read_type_adata *adata)
2189 {
2190 struct ble_gatt_chr chr;
2191 int cbrc;
2192 int rc;
2193
2194 ble_gattc_dbg_assert_proc_not_inserted(proc);
2195
2196 memset(&chr, 0, sizeof chr);
2197 chr.def_handle = adata->att_handle;
2198
2199 switch (adata->value_len) {
2200 case BLE_GATT_CHR_DECL_SZ_16:
2201 case BLE_GATT_CHR_DECL_SZ_128:
2202 rc = ble_uuid_init_from_att_buf(&chr.uuid, adata->value + 3,
2203 adata->value_len - 3);
2204 if (rc != 0) {
2205 rc = BLE_HS_EBADDATA;
2206 goto done;
2207 }
2208 break;
2209
2210 default:
2211 rc = BLE_HS_EBADDATA;
2212 goto done;
2213 }
2214
2215 chr.properties = adata->value[0];
2216 chr.val_handle = get_le16(adata->value + 1);
2217
2218 if (adata->att_handle <= proc->disc_all_chrs.prev_handle) {
2219 /* Peer sent characteristics out of order; terminate procedure. */
2220 rc = BLE_HS_EBADDATA;
2221 goto done;
2222 }
2223 proc->disc_all_chrs.prev_handle = adata->att_handle;
2224
2225 rc = 0;
2226
2227 done:
2228 cbrc = ble_gattc_disc_all_chrs_cb(proc, rc, 0, &chr);
2229 if (rc != 0 || cbrc != 0) {
2230 return BLE_HS_EDONE;
2231 } else {
2232 return 0;
2233 }
2234 }
2235
2236 /**
2237 * Handles a notification that a read-by-type response has been fully
2238 * processed for the specified discover-all-characteristics proc.
2239 */
2240 static int
ble_gattc_disc_all_chrs_rx_complete(struct ble_gattc_proc * proc,int status)2241 ble_gattc_disc_all_chrs_rx_complete(struct ble_gattc_proc *proc, int status)
2242 {
2243 int rc;
2244
2245 ble_gattc_dbg_assert_proc_not_inserted(proc);
2246
2247 if (status != 0) {
2248 ble_gattc_disc_all_chrs_cb(proc, status, 0, NULL);
2249 return BLE_HS_EDONE;
2250 }
2251
2252 if (proc->disc_all_chrs.prev_handle == proc->disc_all_chrs.end_handle) {
2253 /* Characteristic discovery complete. */
2254 ble_gattc_disc_all_chrs_cb(proc, BLE_HS_EDONE, 0, NULL);
2255 return BLE_HS_EDONE;
2256 }
2257
2258 /* Send follow-up request. */
2259 rc = ble_gattc_disc_all_chrs_resume(proc);
2260 if (rc != 0) {
2261 return BLE_HS_EDONE;
2262 }
2263 return 0;
2264 }
2265
2266 int
ble_gattc_disc_all_chrs(uint16_t conn_handle,uint16_t start_handle,uint16_t end_handle,ble_gatt_chr_fn * cb,void * cb_arg)2267 ble_gattc_disc_all_chrs(uint16_t conn_handle, uint16_t start_handle,
2268 uint16_t end_handle, ble_gatt_chr_fn *cb,
2269 void *cb_arg)
2270 {
2271 #if !MYNEWT_VAL(BLE_GATT_DISC_ALL_CHRS)
2272 return BLE_HS_ENOTSUP;
2273 #endif
2274
2275 struct ble_gattc_proc *proc;
2276 int rc;
2277
2278 STATS_INC(ble_gattc_stats, disc_all_chrs);
2279
2280 proc = ble_gattc_proc_alloc();
2281 if (proc == NULL) {
2282 rc = BLE_HS_ENOMEM;
2283 goto done;
2284 }
2285
2286 proc->op = BLE_GATT_OP_DISC_ALL_CHRS;
2287 proc->conn_handle = conn_handle;
2288 proc->disc_all_chrs.prev_handle = start_handle - 1;
2289 proc->disc_all_chrs.end_handle = end_handle;
2290 proc->disc_all_chrs.cb = cb;
2291 proc->disc_all_chrs.cb_arg = cb_arg;
2292
2293 ble_gattc_log_disc_all_chrs(proc);
2294
2295 rc = ble_gattc_disc_all_chrs_tx(proc);
2296 if (rc != 0) {
2297 goto done;
2298 }
2299
2300 done:
2301 if (rc != 0) {
2302 STATS_INC(ble_gattc_stats, disc_all_chrs_fail);
2303 }
2304
2305 ble_gattc_process_status(proc, rc);
2306 return rc;
2307 }
2308
2309 /*****************************************************************************
2310 * $discover characteristic by uuid *
2311 *****************************************************************************/
2312
2313 /**
2314 * Calls a discover-characteristic-by-uuid proc's callback with the specified
2315 * parameters. If the proc has no callback, this function is a no-op.
2316 *
2317 * @return The return code of the callback (or 0 if there
2318 * is no callback).
2319 */
2320 static int
ble_gattc_disc_chr_uuid_cb(struct ble_gattc_proc * proc,int status,uint16_t att_handle,struct ble_gatt_chr * chr)2321 ble_gattc_disc_chr_uuid_cb(struct ble_gattc_proc *proc, int status,
2322 uint16_t att_handle, struct ble_gatt_chr *chr)
2323 {
2324 int rc;
2325
2326 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
2327 BLE_HS_DBG_ASSERT(chr != NULL || status != 0);
2328 ble_gattc_dbg_assert_proc_not_inserted(proc);
2329
2330 if (status != 0 && status != BLE_HS_EDONE) {
2331 STATS_INC(ble_gattc_stats, disc_chrs_uuid_fail);
2332 }
2333
2334 if (proc->disc_chr_uuid.cb == NULL) {
2335 rc = 0;
2336 } else {
2337 rc = proc->disc_chr_uuid.cb(proc->conn_handle,
2338 ble_gattc_error(status, att_handle), chr,
2339 proc->disc_chr_uuid.cb_arg);
2340 }
2341
2342 return rc;
2343 }
2344
2345 static void
ble_gattc_disc_chr_uuid_tmo(struct ble_gattc_proc * proc)2346 ble_gattc_disc_chr_uuid_tmo(struct ble_gattc_proc *proc)
2347 {
2348 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
2349 ble_gattc_dbg_assert_proc_not_inserted(proc);
2350
2351 ble_gattc_disc_chr_uuid_cb(proc, BLE_HS_ETIMEOUT, 0, NULL);
2352 }
2353
2354 /**
2355 * Triggers a pending transmit for the specified
2356 * discover-characteristic-by-uuid proc.
2357 */
2358 static int
ble_gattc_disc_chr_uuid_tx(struct ble_gattc_proc * proc)2359 ble_gattc_disc_chr_uuid_tx(struct ble_gattc_proc *proc)
2360 {
2361 ble_uuid16_t uuid = BLE_UUID16_INIT(BLE_ATT_UUID_CHARACTERISTIC);
2362 int rc;
2363
2364 ble_gattc_dbg_assert_proc_not_inserted(proc);
2365
2366 rc = ble_att_clt_tx_read_type(proc->conn_handle,
2367 proc->disc_chr_uuid.prev_handle + 1,
2368 proc->disc_chr_uuid.end_handle, &uuid.u);
2369 if (rc != 0) {
2370 return rc;
2371 }
2372
2373 return 0;
2374 }
2375
2376 static int
ble_gattc_disc_chr_uuid_resume(struct ble_gattc_proc * proc)2377 ble_gattc_disc_chr_uuid_resume(struct ble_gattc_proc *proc)
2378 {
2379 int status;
2380 int rc;
2381
2382 status = ble_gattc_disc_chr_uuid_tx(proc);
2383 rc = ble_gattc_process_resume_status(proc, status);
2384 if (rc != 0) {
2385 ble_gattc_disc_chr_uuid_cb(proc, rc, 0, NULL);
2386 return rc;
2387 }
2388
2389 return 0;
2390 }
2391
2392 /**
2393 * Handles an incoming ATT error response for the specified
2394 * discover-characteristic-by-uuid proc.
2395 */
2396 static void
ble_gattc_disc_chr_uuid_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)2397 ble_gattc_disc_chr_uuid_err(struct ble_gattc_proc *proc, int status,
2398 uint16_t att_handle)
2399 {
2400 ble_gattc_dbg_assert_proc_not_inserted(proc);
2401
2402 if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_FOUND)) {
2403 /* Discovery is complete. */
2404 status = BLE_HS_EDONE;
2405 }
2406
2407 ble_gattc_disc_chr_uuid_cb(proc, status, att_handle, NULL);
2408 }
2409
2410 /**
2411 * Handles an incoming "attribute data" entry from a read-by-type response for
2412 * the specified discover-characteristics-by-uuid proc.
2413 */
2414 static int
ble_gattc_disc_chr_uuid_rx_adata(struct ble_gattc_proc * proc,struct ble_att_read_type_adata * adata)2415 ble_gattc_disc_chr_uuid_rx_adata(struct ble_gattc_proc *proc,
2416 struct ble_att_read_type_adata *adata)
2417 {
2418 struct ble_gatt_chr chr;
2419 int cbrc;
2420 int rc;
2421
2422 ble_gattc_dbg_assert_proc_not_inserted(proc);
2423
2424 memset(&chr, 0, sizeof chr);
2425 chr.def_handle = adata->att_handle;
2426
2427 switch (adata->value_len) {
2428 case BLE_GATT_CHR_DECL_SZ_16:
2429 case BLE_GATT_CHR_DECL_SZ_128:
2430 rc = ble_uuid_init_from_att_buf(&chr.uuid, adata->value + 3,
2431 adata->value_len - 3);
2432 if (rc != 0) {
2433 rc = BLE_HS_EBADDATA;
2434 goto done;
2435 }
2436 break;
2437
2438 default:
2439 rc = BLE_HS_EBADDATA;
2440 goto done;
2441 }
2442
2443 chr.properties = adata->value[0];
2444 chr.val_handle = get_le16(adata->value + 1);
2445
2446 if (adata->att_handle <= proc->disc_chr_uuid.prev_handle) {
2447 /* Peer sent characteristics out of order; terminate procedure. */
2448 rc = BLE_HS_EBADDATA;
2449 goto done;
2450 }
2451
2452 proc->disc_chr_uuid.prev_handle = adata->att_handle;
2453
2454 rc = 0;
2455
2456 done:
2457 if (rc != 0) {
2458 /* Failure. */
2459 cbrc = ble_gattc_disc_chr_uuid_cb(proc, rc, 0, NULL);
2460 } else if (ble_uuid_cmp(&chr.uuid.u, &proc->disc_chr_uuid.chr_uuid.u) == 0) {
2461 /* Requested characteristic discovered. */
2462 cbrc = ble_gattc_disc_chr_uuid_cb(proc, 0, 0, &chr);
2463 } else {
2464 /* Uninteresting characteristic; ignore. */
2465 cbrc = 0;
2466 }
2467
2468 if (rc != 0 || cbrc != 0) {
2469 return BLE_HS_EDONE;
2470 } else {
2471 return 0;
2472 }
2473 }
2474
2475 /**
2476 * Handles a notification that a read-by-type response has been fully
2477 * processed for the specified discover-characteristics-by-uuid proc.
2478 */
2479 static int
ble_gattc_disc_chr_uuid_rx_complete(struct ble_gattc_proc * proc,int status)2480 ble_gattc_disc_chr_uuid_rx_complete(struct ble_gattc_proc *proc, int status)
2481 {
2482 int rc;
2483
2484 ble_gattc_dbg_assert_proc_not_inserted(proc);
2485
2486 if (status != 0) {
2487 ble_gattc_disc_chr_uuid_cb(proc, status, 0, NULL);
2488 return BLE_HS_EDONE;
2489 }
2490
2491 if (proc->disc_chr_uuid.prev_handle == proc->disc_chr_uuid.end_handle) {
2492 /* Characteristic discovery complete. */
2493 ble_gattc_disc_chr_uuid_cb(proc, BLE_HS_EDONE, 0, NULL);
2494 return BLE_HS_EDONE;
2495 }
2496
2497 /* Send follow-up request. */
2498 rc = ble_gattc_disc_chr_uuid_resume(proc);
2499 if (rc != 0) {
2500 return BLE_HS_EDONE;
2501 }
2502 return 0;
2503 }
2504
2505 int
ble_gattc_disc_chrs_by_uuid(uint16_t conn_handle,uint16_t start_handle,uint16_t end_handle,const ble_uuid_t * uuid,ble_gatt_chr_fn * cb,void * cb_arg)2506 ble_gattc_disc_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle,
2507 uint16_t end_handle, const ble_uuid_t *uuid,
2508 ble_gatt_chr_fn *cb, void *cb_arg)
2509 {
2510 #if !MYNEWT_VAL(BLE_GATT_DISC_CHR_UUID)
2511 return BLE_HS_ENOTSUP;
2512 #endif
2513
2514 struct ble_gattc_proc *proc;
2515 int rc;
2516
2517 STATS_INC(ble_gattc_stats, disc_chrs_uuid);
2518
2519 proc = ble_gattc_proc_alloc();
2520 if (proc == NULL) {
2521 rc = BLE_HS_ENOMEM;
2522 goto done;
2523 }
2524
2525 proc->op = BLE_GATT_OP_DISC_CHR_UUID;
2526 proc->conn_handle = conn_handle;
2527 ble_uuid_to_any(uuid, &proc->disc_chr_uuid.chr_uuid);
2528 proc->disc_chr_uuid.prev_handle = start_handle - 1;
2529 proc->disc_chr_uuid.end_handle = end_handle;
2530 proc->disc_chr_uuid.cb = cb;
2531 proc->disc_chr_uuid.cb_arg = cb_arg;
2532
2533 ble_gattc_log_disc_chr_uuid(proc);
2534
2535 rc = ble_gattc_disc_chr_uuid_tx(proc);
2536 if (rc != 0) {
2537 goto done;
2538 }
2539
2540 done:
2541 if (rc != 0) {
2542 STATS_INC(ble_gattc_stats, disc_chrs_uuid_fail);
2543 }
2544
2545 ble_gattc_process_status(proc, rc);
2546 return rc;
2547 }
2548
2549 /*****************************************************************************
2550 * $discover all characteristic descriptors *
2551 *****************************************************************************/
2552
2553 /**
2554 * Calls a discover-all-descriptors proc's callback with the specified
2555 * parameters. If the proc has no callback, this function is a no-op.
2556 *
2557 * @return The return code of the callback (or 0 if there
2558 * is no callback).
2559 */
2560 static int
ble_gattc_disc_all_dscs_cb(struct ble_gattc_proc * proc,int status,uint16_t att_handle,struct ble_gatt_dsc * dsc)2561 ble_gattc_disc_all_dscs_cb(struct ble_gattc_proc *proc, int status,
2562 uint16_t att_handle, struct ble_gatt_dsc *dsc)
2563 {
2564 int rc;
2565
2566 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
2567 BLE_HS_DBG_ASSERT(dsc != NULL || status != 0);
2568 ble_gattc_dbg_assert_proc_not_inserted(proc);
2569
2570 if (status != 0 && status != BLE_HS_EDONE) {
2571 STATS_INC(ble_gattc_stats, disc_all_dscs_fail);
2572 }
2573
2574 if (proc->disc_all_dscs.cb == NULL) {
2575 rc = 0;
2576 } else {
2577 rc = proc->disc_all_dscs.cb(proc->conn_handle,
2578 ble_gattc_error(status, att_handle),
2579 proc->disc_all_dscs.chr_val_handle,
2580 dsc, proc->disc_all_dscs.cb_arg);
2581 }
2582
2583 return rc;
2584 }
2585
2586 static void
ble_gattc_disc_all_dscs_tmo(struct ble_gattc_proc * proc)2587 ble_gattc_disc_all_dscs_tmo(struct ble_gattc_proc *proc)
2588 {
2589 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
2590 ble_gattc_dbg_assert_proc_not_inserted(proc);
2591
2592 ble_gattc_disc_all_dscs_cb(proc, BLE_HS_ETIMEOUT, 0, NULL);
2593 }
2594
2595 /**
2596 * Triggers a pending transmit for the specified discover-all-descriptors proc.
2597 */
2598 static int
ble_gattc_disc_all_dscs_tx(struct ble_gattc_proc * proc)2599 ble_gattc_disc_all_dscs_tx(struct ble_gattc_proc *proc)
2600 {
2601 int rc;
2602
2603 ble_gattc_dbg_assert_proc_not_inserted(proc);
2604
2605 rc = ble_att_clt_tx_find_info(proc->conn_handle,
2606 proc->disc_all_dscs.prev_handle + 1,
2607 proc->disc_all_dscs.end_handle);
2608 if (rc != 0) {
2609 return rc;
2610 }
2611
2612 return 0;
2613 }
2614
2615 static int
ble_gattc_disc_all_dscs_resume(struct ble_gattc_proc * proc)2616 ble_gattc_disc_all_dscs_resume(struct ble_gattc_proc *proc)
2617 {
2618 int status;
2619 int rc;
2620
2621 status = ble_gattc_disc_all_dscs_tx(proc);
2622 rc = ble_gattc_process_resume_status(proc, status);
2623 if (rc != 0) {
2624 ble_gattc_disc_all_dscs_cb(proc, rc, 0, NULL);
2625 return rc;
2626 }
2627
2628 return 0;
2629 }
2630
2631 /**
2632 * Handles an incoming ATT error response for the specified
2633 * discover-all-descriptors proc.
2634 */
2635 static void
ble_gattc_disc_all_dscs_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)2636 ble_gattc_disc_all_dscs_err(struct ble_gattc_proc *proc, int status,
2637 uint16_t att_handle)
2638 {
2639 ble_gattc_dbg_assert_proc_not_inserted(proc);
2640
2641 if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_FOUND)) {
2642 /* Discovery is complete. */
2643 status = BLE_HS_EDONE;
2644 }
2645
2646 ble_gattc_disc_all_dscs_cb(proc, status, att_handle, NULL);
2647 }
2648
2649 /**
2650 * Handles an incoming "information data" entry from a find-information
2651 * response for the specified discover-all-descriptors proc.
2652 */
2653 static int
ble_gattc_disc_all_dscs_rx_idata(struct ble_gattc_proc * proc,struct ble_att_find_info_idata * idata)2654 ble_gattc_disc_all_dscs_rx_idata(struct ble_gattc_proc *proc,
2655 struct ble_att_find_info_idata *idata)
2656 {
2657 struct ble_gatt_dsc dsc;
2658 int cbrc;
2659 int rc;
2660
2661 ble_gattc_dbg_assert_proc_not_inserted(proc);
2662
2663 if (idata->attr_handle <= proc->disc_all_dscs.prev_handle) {
2664 /* Peer sent descriptors out of order; terminate procedure. */
2665 rc = BLE_HS_EBADDATA;
2666 goto done;
2667 }
2668 proc->disc_all_dscs.prev_handle = idata->attr_handle;
2669
2670 rc = 0;
2671
2672 done:
2673 dsc.handle = idata->attr_handle;
2674 dsc.uuid = idata->uuid;
2675
2676 cbrc = ble_gattc_disc_all_dscs_cb(proc, rc, 0, &dsc);
2677 if (rc != 0 || cbrc != 0) {
2678 return BLE_HS_EDONE;
2679 } else {
2680 return 0;
2681 }
2682 }
2683
2684 /**
2685 * Handles a notification that a find-information response has been fully
2686 * processed for the specified discover-all-descriptors proc.
2687 */
2688 static int
ble_gattc_disc_all_dscs_rx_complete(struct ble_gattc_proc * proc,int status)2689 ble_gattc_disc_all_dscs_rx_complete(struct ble_gattc_proc *proc, int status)
2690 {
2691 int rc;
2692
2693 ble_gattc_dbg_assert_proc_not_inserted(proc);
2694
2695 if (status != 0) {
2696 ble_gattc_disc_all_dscs_cb(proc, status, 0, NULL);
2697 return BLE_HS_EDONE;
2698 }
2699
2700 if (proc->disc_all_dscs.prev_handle == proc->disc_all_dscs.end_handle) {
2701 /* All descriptors discovered. */
2702 ble_gattc_disc_all_dscs_cb(proc, BLE_HS_EDONE, 0, NULL);
2703 return BLE_HS_EDONE;
2704 }
2705
2706 /* Send follow-up request. */
2707 rc = ble_gattc_disc_all_dscs_resume(proc);
2708 if (rc != 0) {
2709 return BLE_HS_EDONE;
2710 }
2711
2712 return 0;
2713 }
2714
2715 int
ble_gattc_disc_all_dscs(uint16_t conn_handle,uint16_t start_handle,uint16_t end_handle,ble_gatt_dsc_fn * cb,void * cb_arg)2716 ble_gattc_disc_all_dscs(uint16_t conn_handle, uint16_t start_handle,
2717 uint16_t end_handle,
2718 ble_gatt_dsc_fn *cb, void *cb_arg)
2719 {
2720 #if !MYNEWT_VAL(BLE_GATT_DISC_ALL_DSCS)
2721 return BLE_HS_ENOTSUP;
2722 #endif
2723
2724 struct ble_gattc_proc *proc;
2725 int rc;
2726
2727 STATS_INC(ble_gattc_stats, disc_all_dscs);
2728
2729 proc = ble_gattc_proc_alloc();
2730 if (proc == NULL) {
2731 rc = BLE_HS_ENOMEM;
2732 goto done;
2733 }
2734
2735 proc->op = BLE_GATT_OP_DISC_ALL_DSCS;
2736 proc->conn_handle = conn_handle;
2737 proc->disc_all_dscs.chr_val_handle = start_handle;
2738 proc->disc_all_dscs.prev_handle = start_handle;
2739 proc->disc_all_dscs.end_handle = end_handle;
2740 proc->disc_all_dscs.cb = cb;
2741 proc->disc_all_dscs.cb_arg = cb_arg;
2742
2743 ble_gattc_log_disc_all_dscs(proc);
2744
2745 rc = ble_gattc_disc_all_dscs_tx(proc);
2746 if (rc != 0) {
2747 goto done;
2748 }
2749
2750 done:
2751 if (rc != 0) {
2752 STATS_INC(ble_gattc_stats, disc_all_dscs_fail);
2753 }
2754
2755 ble_gattc_process_status(proc, rc);
2756 return rc;
2757 }
2758
2759 /*****************************************************************************
2760 * $read *
2761 *****************************************************************************/
2762
2763 /**
2764 * Calls a read-characteristic proc's callback with the specified parameters.
2765 * If the proc has no callback, this function is a no-op.
2766 *
2767 * @return The return code of the callback (or 0 if there
2768 * is no callback).
2769 */
2770 static int
ble_gattc_read_cb(struct ble_gattc_proc * proc,int status,uint16_t att_handle,struct ble_gatt_attr * attr)2771 ble_gattc_read_cb(struct ble_gattc_proc *proc, int status,
2772 uint16_t att_handle, struct ble_gatt_attr *attr)
2773 {
2774 int rc;
2775
2776 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
2777 BLE_HS_DBG_ASSERT(attr != NULL || status != 0);
2778 ble_gattc_dbg_assert_proc_not_inserted(proc);
2779
2780 if (status != 0 && status != BLE_HS_EDONE) {
2781 STATS_INC(ble_gattc_stats, read_fail);
2782 }
2783
2784 if (proc->read.cb == NULL) {
2785 rc = 0;
2786 } else {
2787 rc = proc->read.cb(proc->conn_handle,
2788 ble_gattc_error(status, att_handle), attr,
2789 proc->read.cb_arg);
2790 }
2791
2792 return rc;
2793 }
2794
2795 static void
ble_gattc_read_tmo(struct ble_gattc_proc * proc)2796 ble_gattc_read_tmo(struct ble_gattc_proc *proc)
2797 {
2798 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
2799 ble_gattc_dbg_assert_proc_not_inserted(proc);
2800
2801 ble_gattc_read_cb(proc, BLE_HS_ETIMEOUT, 0, NULL);
2802 }
2803
2804 /**
2805 * Handles an incoming ATT error response for the specified
2806 * read-characteristic-value proc.
2807 */
2808 static void
ble_gattc_read_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)2809 ble_gattc_read_err(struct ble_gattc_proc *proc, int status,
2810 uint16_t att_handle)
2811 {
2812 ble_gattc_dbg_assert_proc_not_inserted(proc);
2813 ble_gattc_read_cb(proc, status, att_handle, NULL);
2814 }
2815
2816 /**
2817 * Handles an incoming read-response for the specified
2818 * read-characteristic-value proc.
2819 */
2820 static int
ble_gattc_read_rx_read_rsp(struct ble_gattc_proc * proc,int status,struct os_mbuf ** om)2821 ble_gattc_read_rx_read_rsp(struct ble_gattc_proc *proc, int status,
2822 struct os_mbuf **om)
2823 {
2824 struct ble_gatt_attr attr;
2825
2826 ble_gattc_dbg_assert_proc_not_inserted(proc);
2827
2828 attr.handle = proc->read.handle;
2829 attr.offset = 0;
2830 attr.om = *om;
2831
2832 ble_gattc_read_cb(proc, status, 0, &attr);
2833
2834 /* Indicate to the caller whether the application consumed the mbuf. */
2835 *om = attr.om;
2836
2837 /* The read operation only has a single request / response exchange. */
2838 return BLE_HS_EDONE;
2839 }
2840
2841 static int
ble_gattc_read_tx(struct ble_gattc_proc * proc)2842 ble_gattc_read_tx(struct ble_gattc_proc *proc)
2843 {
2844 int rc;
2845
2846 rc = ble_att_clt_tx_read(proc->conn_handle, proc->read.handle);
2847 if (rc != 0) {
2848 return rc;
2849 }
2850
2851 return 0;
2852 }
2853
2854 int
ble_gattc_read(uint16_t conn_handle,uint16_t attr_handle,ble_gatt_attr_fn * cb,void * cb_arg)2855 ble_gattc_read(uint16_t conn_handle, uint16_t attr_handle,
2856 ble_gatt_attr_fn *cb, void *cb_arg)
2857 {
2858 #if !MYNEWT_VAL(BLE_GATT_READ)
2859 return BLE_HS_ENOTSUP;
2860 #endif
2861
2862 struct ble_gattc_proc *proc;
2863 int rc;
2864
2865 STATS_INC(ble_gattc_stats, read);
2866
2867 proc = ble_gattc_proc_alloc();
2868 if (proc == NULL) {
2869 rc = BLE_HS_ENOMEM;
2870 goto done;
2871 }
2872
2873 proc->op = BLE_GATT_OP_READ;
2874 proc->conn_handle = conn_handle;
2875 proc->read.handle = attr_handle;
2876 proc->read.cb = cb;
2877 proc->read.cb_arg = cb_arg;
2878
2879 ble_gattc_log_read(attr_handle);
2880 rc = ble_gattc_read_tx(proc);
2881 if (rc != 0) {
2882 goto done;
2883 }
2884
2885 done:
2886 if (rc != 0) {
2887 STATS_INC(ble_gattc_stats, read_fail);
2888 }
2889
2890 ble_gattc_process_status(proc, rc);
2891 return rc;
2892 }
2893
2894 /*****************************************************************************
2895 * $read by uuid *
2896 *****************************************************************************/
2897
2898 /**
2899 * Calls a read-using-characteristic-uuid proc's callback with the specified
2900 * parameters. If the proc has no callback, this function is a no-op.
2901 *
2902 * @return The return code of the callback (or 0 if there
2903 * is no callback).
2904 */
2905 static int
ble_gattc_read_uuid_cb(struct ble_gattc_proc * proc,int status,uint16_t att_handle,struct ble_gatt_attr * attr)2906 ble_gattc_read_uuid_cb(struct ble_gattc_proc *proc, int status,
2907 uint16_t att_handle, struct ble_gatt_attr *attr)
2908 {
2909 int rc;
2910
2911 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
2912 BLE_HS_DBG_ASSERT(attr != NULL || status != 0);
2913 ble_gattc_dbg_assert_proc_not_inserted(proc);
2914
2915 if (status != 0 && status != BLE_HS_EDONE) {
2916 STATS_INC(ble_gattc_stats, read_uuid_fail);
2917 }
2918
2919 if (proc->read_uuid.cb == NULL) {
2920 rc = 0;
2921 } else {
2922 rc = proc->read_uuid.cb(proc->conn_handle,
2923 ble_gattc_error(status, att_handle), attr,
2924 proc->read_uuid.cb_arg);
2925 }
2926
2927 return rc;
2928 }
2929
2930 static void
ble_gattc_read_uuid_tmo(struct ble_gattc_proc * proc)2931 ble_gattc_read_uuid_tmo(struct ble_gattc_proc *proc)
2932 {
2933 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
2934 ble_gattc_dbg_assert_proc_not_inserted(proc);
2935
2936 ble_gattc_read_uuid_cb(proc, BLE_HS_ETIMEOUT, 0, NULL);
2937 }
2938
2939 /**
2940 * Handles an incoming ATT error response for the specified
2941 * read-using-characteristic-uuid proc.
2942 */
2943 static void
ble_gattc_read_uuid_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)2944 ble_gattc_read_uuid_err(struct ble_gattc_proc *proc, int status,
2945 uint16_t att_handle)
2946 {
2947 ble_gattc_dbg_assert_proc_not_inserted(proc);
2948
2949 ble_gattc_read_uuid_cb(proc, status, att_handle, NULL);
2950 }
2951
2952 /**
2953 * Handles an incoming "attribute data" entry from a read-by-type response for
2954 * the specified read-using-characteristic-uuid proc.
2955 */
2956 static int
ble_gattc_read_uuid_rx_adata(struct ble_gattc_proc * proc,struct ble_att_read_type_adata * adata)2957 ble_gattc_read_uuid_rx_adata(struct ble_gattc_proc *proc,
2958 struct ble_att_read_type_adata *adata)
2959 {
2960 struct ble_gatt_attr attr;
2961 int rc;
2962
2963 ble_gattc_dbg_assert_proc_not_inserted(proc);
2964
2965 attr.handle = adata->att_handle;
2966 attr.offset = 0;
2967 attr.om = ble_hs_mbuf_from_flat(adata->value, adata->value_len);
2968 if (attr.om == NULL) {
2969 rc = BLE_HS_ENOMEM;
2970 } else {
2971 rc = 0;
2972 }
2973 rc = ble_gattc_read_uuid_cb(proc, rc, 0, &attr);
2974
2975 /* Free the attribute mbuf if the application has not consumed it. */
2976 os_mbuf_free_chain(attr.om);
2977
2978 if (rc != 0) {
2979 return BLE_HS_EDONE;
2980 }
2981
2982 return 0;
2983 }
2984
2985 /**
2986 * Handles a notification that a read-by-type response has been fully
2987 * processed for the specified read-using-characteristic-uuid proc.
2988 */
2989 static int
ble_gattc_read_uuid_rx_complete(struct ble_gattc_proc * proc,int status)2990 ble_gattc_read_uuid_rx_complete(struct ble_gattc_proc *proc, int status)
2991 {
2992 ble_gattc_dbg_assert_proc_not_inserted(proc);
2993
2994 if (status != 0) {
2995 ble_gattc_read_uuid_cb(proc, status, 0, NULL);
2996 return BLE_HS_EDONE;
2997 }
2998
2999 /* XXX: We may need to send a follow-up request to address the possibility
3000 * of multiple characteristics with identical UUIDs.
3001 */
3002 ble_gattc_read_uuid_cb(proc, BLE_HS_EDONE, 0, NULL);
3003 return BLE_HS_EDONE;
3004 }
3005
3006 static int
ble_gattc_read_uuid_tx(struct ble_gattc_proc * proc)3007 ble_gattc_read_uuid_tx(struct ble_gattc_proc *proc)
3008 {
3009 return ble_att_clt_tx_read_type(proc->conn_handle,
3010 proc->read_uuid.start_handle,
3011 proc->read_uuid.end_handle,
3012 &proc->read_uuid.chr_uuid.u);
3013 }
3014
3015 int
ble_gattc_read_by_uuid(uint16_t conn_handle,uint16_t start_handle,uint16_t end_handle,const ble_uuid_t * uuid,ble_gatt_attr_fn * cb,void * cb_arg)3016 ble_gattc_read_by_uuid(uint16_t conn_handle, uint16_t start_handle,
3017 uint16_t end_handle, const ble_uuid_t *uuid,
3018 ble_gatt_attr_fn *cb, void *cb_arg)
3019 {
3020 #if !MYNEWT_VAL(BLE_GATT_READ_UUID)
3021 return BLE_HS_ENOTSUP;
3022 #endif
3023
3024 struct ble_gattc_proc *proc;
3025 int rc;
3026
3027 STATS_INC(ble_gattc_stats, read_uuid);
3028
3029 proc = ble_gattc_proc_alloc();
3030 if (proc == NULL) {
3031 rc = BLE_HS_ENOMEM;
3032 goto done;
3033 }
3034
3035 proc->op = BLE_GATT_OP_READ_UUID;
3036 proc->conn_handle = conn_handle;
3037 ble_uuid_to_any(uuid, &proc->read_uuid.chr_uuid);
3038 proc->read_uuid.start_handle = start_handle;
3039 proc->read_uuid.end_handle = end_handle;
3040 proc->read_uuid.cb = cb;
3041 proc->read_uuid.cb_arg = cb_arg;
3042
3043 ble_gattc_log_read_uuid(start_handle, end_handle, uuid);
3044 rc = ble_gattc_read_uuid_tx(proc);
3045 if (rc != 0) {
3046 goto done;
3047 }
3048
3049 done:
3050 if (rc != 0) {
3051 STATS_INC(ble_gattc_stats, read_uuid_fail);
3052 }
3053
3054 ble_gattc_process_status(proc, rc);
3055 return rc;
3056 }
3057
3058 /*****************************************************************************
3059 * $read long *
3060 *****************************************************************************/
3061
3062 /**
3063 * Calls a read-long-characteristic proc's callback with the specified
3064 * parameters. If the proc has no callback, this function is a no-op.
3065 *
3066 * @return The return code of the callback (or 0 if there
3067 * is no callback).
3068 */
3069 static int
ble_gattc_read_long_cb(struct ble_gattc_proc * proc,int status,uint16_t att_handle,struct ble_gatt_attr * attr)3070 ble_gattc_read_long_cb(struct ble_gattc_proc *proc, int status,
3071 uint16_t att_handle, struct ble_gatt_attr *attr)
3072 {
3073 int rc;
3074
3075 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
3076 BLE_HS_DBG_ASSERT(attr != NULL || status != 0);
3077 ble_gattc_dbg_assert_proc_not_inserted(proc);
3078
3079 if (status != 0 && status != BLE_HS_EDONE) {
3080 STATS_INC(ble_gattc_stats, read_long_fail);
3081 }
3082
3083 if (proc->read_long.cb == NULL) {
3084 rc = 0;
3085 } else {
3086 rc = proc->read_long.cb(proc->conn_handle,
3087 ble_gattc_error(status, att_handle), attr,
3088 proc->read_long.cb_arg);
3089 }
3090
3091 return rc;
3092 }
3093
3094 static void
ble_gattc_read_long_tmo(struct ble_gattc_proc * proc)3095 ble_gattc_read_long_tmo(struct ble_gattc_proc *proc)
3096 {
3097 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
3098 ble_gattc_dbg_assert_proc_not_inserted(proc);
3099
3100 ble_gattc_read_long_cb(proc, BLE_HS_ETIMEOUT, 0, NULL);
3101 }
3102
3103 /**
3104 * Triggers a pending transmit for the specified read-long-characteristic proc.
3105 */
3106 static int
ble_gattc_read_long_tx(struct ble_gattc_proc * proc)3107 ble_gattc_read_long_tx(struct ble_gattc_proc *proc)
3108 {
3109 int rc;
3110
3111 ble_gattc_dbg_assert_proc_not_inserted(proc);
3112
3113 if (proc->read_long.offset == 0) {
3114 rc = ble_att_clt_tx_read(proc->conn_handle, proc->read_long.handle);
3115 if (rc != 0) {
3116 return rc;
3117 }
3118 } else {
3119 rc = ble_att_clt_tx_read_blob(proc->conn_handle,
3120 proc->read_long.handle,
3121 proc->read_long.offset);
3122 if (rc != 0) {
3123 return rc;
3124 }
3125 }
3126
3127 return 0;
3128 }
3129
3130 static int
ble_gattc_read_long_resume(struct ble_gattc_proc * proc)3131 ble_gattc_read_long_resume(struct ble_gattc_proc *proc)
3132 {
3133 int status;
3134 int rc;
3135
3136 status = ble_gattc_read_long_tx(proc);
3137 rc = ble_gattc_process_resume_status(proc, status);
3138 if (rc != 0) {
3139 ble_gattc_read_long_cb(proc, rc, 0, NULL);
3140 return rc;
3141 }
3142
3143 return 0;
3144 }
3145
3146 /**
3147 * Handles an incoming ATT error response for the specified
3148 * read-long-characteristic proc.
3149 */
3150 static void
ble_gattc_read_long_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)3151 ble_gattc_read_long_err(struct ble_gattc_proc *proc, int status,
3152 uint16_t att_handle)
3153 {
3154 ble_gattc_dbg_assert_proc_not_inserted(proc);
3155 ble_gattc_read_long_cb(proc, status, att_handle, NULL);
3156 }
3157
3158 /**
3159 * Handles an incoming read-response for the specified
3160 * read-long-characteristic-values proc.
3161 */
3162 static int
ble_gattc_read_long_rx_read_rsp(struct ble_gattc_proc * proc,int status,struct os_mbuf ** om)3163 ble_gattc_read_long_rx_read_rsp(struct ble_gattc_proc *proc, int status,
3164 struct os_mbuf **om)
3165 {
3166 struct ble_gatt_attr attr;
3167 uint16_t data_len;
3168 uint16_t mtu;
3169 int rc;
3170
3171 ble_gattc_dbg_assert_proc_not_inserted(proc);
3172
3173 data_len = OS_MBUF_PKTLEN(*om);
3174
3175 attr.handle = proc->read_long.handle;
3176 attr.offset = proc->read_long.offset;
3177 attr.om = *om;
3178
3179 /* Report partial payload to application. */
3180 rc = ble_gattc_read_long_cb(proc, status, 0, &attr);
3181
3182 /* Indicate to the caller whether the application consumed the mbuf. */
3183 *om = attr.om;
3184
3185 if (rc != 0 || status != 0) {
3186 return BLE_HS_EDONE;
3187 }
3188
3189 /* Determine if this is the end of the attribute value. */
3190 mtu = ble_att_mtu(proc->conn_handle);
3191 if (mtu == 0) {
3192 /* No longer connected. */
3193 return BLE_HS_EDONE;
3194 }
3195
3196 if (data_len < mtu - 1) {
3197 /* Response shorter than maximum allowed; read complete. */
3198 ble_gattc_read_long_cb(proc, BLE_HS_EDONE, 0, NULL);
3199 return BLE_HS_EDONE;
3200 }
3201
3202 /* Send follow-up request. */
3203 proc->read_long.offset += data_len;
3204 rc = ble_gattc_read_long_resume(proc);
3205 if (rc != 0) {
3206 return BLE_HS_EDONE;
3207 }
3208
3209 return 0;
3210 }
3211
3212 int
ble_gattc_read_long(uint16_t conn_handle,uint16_t handle,uint16_t offset,ble_gatt_attr_fn * cb,void * cb_arg)3213 ble_gattc_read_long(uint16_t conn_handle, uint16_t handle, uint16_t offset,
3214 ble_gatt_attr_fn *cb, void *cb_arg)
3215 {
3216 #if !MYNEWT_VAL(BLE_GATT_READ_LONG)
3217 return BLE_HS_ENOTSUP;
3218 #endif
3219
3220 struct ble_gattc_proc *proc;
3221 int rc;
3222
3223 STATS_INC(ble_gattc_stats, read_long);
3224
3225 proc = ble_gattc_proc_alloc();
3226 if (proc == NULL) {
3227 rc = BLE_HS_ENOMEM;
3228 goto done;
3229 }
3230
3231 proc->op = BLE_GATT_OP_READ_LONG;
3232 proc->conn_handle = conn_handle;
3233 proc->read_long.handle = handle;
3234 proc->read_long.offset = offset;
3235 proc->read_long.cb = cb;
3236 proc->read_long.cb_arg = cb_arg;
3237
3238 ble_gattc_log_read_long(proc);
3239
3240 rc = ble_gattc_read_long_tx(proc);
3241 if (rc != 0) {
3242 goto done;
3243 }
3244
3245 done:
3246 if (rc != 0) {
3247 STATS_INC(ble_gattc_stats, read_long_fail);
3248 }
3249
3250 ble_gattc_process_status(proc, rc);
3251 return rc;
3252 }
3253
3254 /*****************************************************************************
3255 * $read multiple *
3256 *****************************************************************************/
3257
3258 /**
3259 * Calls a read-multiple-characteristics proc's callback with the specified
3260 * parameters. If the proc has no callback, this function is a no-op.
3261 *
3262 * @return The return code of the callback (or 0 if there
3263 * is no callback).
3264 */
3265 static int
ble_gattc_read_mult_cb(struct ble_gattc_proc * proc,int status,uint16_t att_handle,struct os_mbuf ** om)3266 ble_gattc_read_mult_cb(struct ble_gattc_proc *proc, int status,
3267 uint16_t att_handle, struct os_mbuf **om)
3268 {
3269 struct ble_gatt_attr attr;
3270 int rc;
3271
3272 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
3273 BLE_HS_DBG_ASSERT(om != NULL || status != 0);
3274 ble_gattc_dbg_assert_proc_not_inserted(proc);
3275
3276 if (status != 0 && status != BLE_HS_EDONE) {
3277 STATS_INC(ble_gattc_stats, read_mult_fail);
3278 }
3279
3280 attr.handle = 0;
3281 attr.offset = 0;
3282 if (om == NULL) {
3283 attr.om = NULL;
3284 } else {
3285 attr.om = *om;
3286 }
3287
3288 if (proc->read_mult.cb == NULL) {
3289 rc = 0;
3290 } else {
3291 rc = proc->read_mult.cb(proc->conn_handle,
3292 ble_gattc_error(status, att_handle), &attr,
3293 proc->read_mult.cb_arg);
3294 }
3295
3296 /* Indicate to the caller whether the application consumed the mbuf. */
3297 if (om != NULL) {
3298 *om = attr.om;
3299 }
3300
3301 return rc;
3302 }
3303
3304 static void
ble_gattc_read_mult_tmo(struct ble_gattc_proc * proc)3305 ble_gattc_read_mult_tmo(struct ble_gattc_proc *proc)
3306 {
3307 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
3308 ble_gattc_dbg_assert_proc_not_inserted(proc);
3309
3310 ble_gattc_read_mult_cb(proc, BLE_HS_ETIMEOUT, 0, 0);
3311 }
3312
3313 /**
3314 * Handles an incoming ATT error response for the specified
3315 * read-multiple-characteristics proc.
3316 */
3317 static void
ble_gattc_read_mult_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)3318 ble_gattc_read_mult_err(struct ble_gattc_proc *proc, int status,
3319 uint16_t att_handle)
3320 {
3321 ble_gattc_dbg_assert_proc_not_inserted(proc);
3322 ble_gattc_read_mult_cb(proc, status, att_handle, NULL);
3323 }
3324
3325 static int
ble_gattc_read_mult_tx(struct ble_gattc_proc * proc)3326 ble_gattc_read_mult_tx(struct ble_gattc_proc *proc)
3327 {
3328 int rc;
3329
3330 rc = ble_att_clt_tx_read_mult(proc->conn_handle, proc->read_mult.handles,
3331 proc->read_mult.num_handles);
3332 if (rc != 0) {
3333 return rc;
3334 }
3335
3336 return 0;
3337 }
3338
3339
3340 int
ble_gattc_read_mult(uint16_t conn_handle,const uint16_t * handles,uint8_t num_handles,ble_gatt_attr_fn * cb,void * cb_arg)3341 ble_gattc_read_mult(uint16_t conn_handle, const uint16_t *handles,
3342 uint8_t num_handles, ble_gatt_attr_fn *cb,
3343 void *cb_arg)
3344 {
3345 #if !MYNEWT_VAL(BLE_GATT_READ_MULT)
3346 return BLE_HS_ENOTSUP;
3347 #endif
3348
3349 struct ble_gattc_proc *proc;
3350 int rc;
3351
3352 proc = NULL;
3353
3354 STATS_INC(ble_gattc_stats, read_mult);
3355
3356 if (num_handles > MYNEWT_VAL(BLE_GATT_READ_MAX_ATTRS)) {
3357 rc = BLE_HS_EINVAL;
3358 goto done;
3359 }
3360
3361 proc = ble_gattc_proc_alloc();
3362 if (proc == NULL) {
3363 rc = BLE_HS_ENOMEM;
3364 goto done;
3365 }
3366
3367 proc->op = BLE_GATT_OP_READ_MULT;
3368 proc->conn_handle = conn_handle;
3369 memcpy(proc->read_mult.handles, handles, num_handles * sizeof *handles);
3370 proc->read_mult.num_handles = num_handles;
3371 proc->read_mult.cb = cb;
3372 proc->read_mult.cb_arg = cb_arg;
3373
3374 ble_gattc_log_read_mult(handles, num_handles);
3375 rc = ble_gattc_read_mult_tx(proc);
3376 if (rc != 0) {
3377 goto done;
3378 }
3379
3380 done:
3381 if (rc != 0) {
3382 STATS_INC(ble_gattc_stats, read_mult_fail);
3383 }
3384
3385 ble_gattc_process_status(proc, rc);
3386 return rc;
3387 }
3388
3389 /*****************************************************************************
3390 * $write no response *
3391 *****************************************************************************/
3392
3393 int
ble_gattc_write_no_rsp(uint16_t conn_handle,uint16_t attr_handle,struct os_mbuf * txom)3394 ble_gattc_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle,
3395 struct os_mbuf *txom)
3396 {
3397 #if !MYNEWT_VAL(BLE_GATT_WRITE_NO_RSP)
3398 return BLE_HS_ENOTSUP;
3399 #endif
3400
3401 int rc;
3402
3403 STATS_INC(ble_gattc_stats, write_no_rsp);
3404
3405 ble_gattc_log_write(attr_handle, OS_MBUF_PKTLEN(txom), 0);
3406
3407 rc = ble_att_clt_tx_write_cmd(conn_handle, attr_handle, txom);
3408 if (rc != 0) {
3409 STATS_INC(ble_gattc_stats, write);
3410 }
3411
3412 return rc;
3413 }
3414
3415 int
ble_gattc_write_no_rsp_flat(uint16_t conn_handle,uint16_t attr_handle,const void * data,uint16_t data_len)3416 ble_gattc_write_no_rsp_flat(uint16_t conn_handle, uint16_t attr_handle,
3417 const void *data, uint16_t data_len)
3418 {
3419 struct os_mbuf *om;
3420 int rc;
3421
3422 om = ble_hs_mbuf_from_flat(data, data_len);
3423 if (om == NULL) {
3424 return BLE_HS_ENOMEM;
3425 }
3426
3427 rc = ble_gattc_write_no_rsp(conn_handle, attr_handle, om);
3428 if (rc != 0) {
3429 return rc;
3430 }
3431
3432 return 0;
3433 }
3434
3435 /*****************************************************************************
3436 * $write *
3437 *****************************************************************************/
3438
3439 /**
3440 * Calls a write-characteristic-value proc's callback with the specified
3441 * parameters. If the proc has no callback, this function is a no-op.
3442 *
3443 * @return The return code of the callback (or 0 if there
3444 * is no callback).
3445 */
3446 static int
ble_gattc_write_cb(struct ble_gattc_proc * proc,int status,uint16_t att_handle)3447 ble_gattc_write_cb(struct ble_gattc_proc *proc, int status,
3448 uint16_t att_handle)
3449 {
3450 struct ble_gatt_attr attr;
3451 int rc;
3452
3453 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
3454 ble_gattc_dbg_assert_proc_not_inserted(proc);
3455
3456 if (status != 0 && status != BLE_HS_EDONE) {
3457 STATS_INC(ble_gattc_stats, write_fail);
3458 }
3459
3460 if (proc->write.cb == NULL) {
3461 rc = 0;
3462 } else {
3463 memset(&attr, 0, sizeof attr);
3464 attr.handle = proc->write.att_handle;
3465 rc = proc->write.cb(proc->conn_handle,
3466 ble_gattc_error(status, att_handle),
3467 &attr, proc->write.cb_arg);
3468 }
3469
3470 return rc;
3471 }
3472
3473 static void
ble_gattc_write_tmo(struct ble_gattc_proc * proc)3474 ble_gattc_write_tmo(struct ble_gattc_proc *proc)
3475 {
3476 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
3477 ble_gattc_dbg_assert_proc_not_inserted(proc);
3478
3479 ble_gattc_write_cb(proc, BLE_HS_ETIMEOUT, 0);
3480 }
3481
3482 /**
3483 * Handles an incoming ATT error response for the specified
3484 * write-characteristic-value proc.
3485 */
3486 static void
ble_gattc_write_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)3487 ble_gattc_write_err(struct ble_gattc_proc *proc, int status,
3488 uint16_t att_handle)
3489 {
3490 ble_gattc_dbg_assert_proc_not_inserted(proc);
3491 ble_gattc_write_cb(proc, status, att_handle);
3492 }
3493
3494 int
ble_gattc_write(uint16_t conn_handle,uint16_t attr_handle,struct os_mbuf * txom,ble_gatt_attr_fn * cb,void * cb_arg)3495 ble_gattc_write(uint16_t conn_handle, uint16_t attr_handle,
3496 struct os_mbuf *txom, ble_gatt_attr_fn *cb, void *cb_arg)
3497 {
3498 #if !MYNEWT_VAL(BLE_GATT_WRITE)
3499 return BLE_HS_ENOTSUP;
3500 #endif
3501
3502 struct ble_gattc_proc *proc;
3503 int rc;
3504
3505 STATS_INC(ble_gattc_stats, write);
3506
3507 proc = ble_gattc_proc_alloc();
3508 if (proc == NULL) {
3509 rc = BLE_HS_ENOMEM;
3510 goto done;
3511 }
3512
3513 proc->op = BLE_GATT_OP_WRITE;
3514 proc->conn_handle = conn_handle;
3515 proc->write.att_handle = attr_handle;
3516 proc->write.cb = cb;
3517 proc->write.cb_arg = cb_arg;
3518
3519 ble_gattc_log_write(attr_handle, OS_MBUF_PKTLEN(txom), 1);
3520
3521 rc = ble_att_clt_tx_write_req(conn_handle, attr_handle, txom);
3522 txom = NULL;
3523 if (rc != 0) {
3524 goto done;
3525 }
3526
3527 done:
3528 if (rc != 0) {
3529 STATS_INC(ble_gattc_stats, write_fail);
3530 }
3531
3532 /* Free the mbuf in case the send failed. */
3533 os_mbuf_free_chain(txom);
3534
3535 ble_gattc_process_status(proc, rc);
3536 return rc;
3537 }
3538
3539 int
ble_gattc_write_flat(uint16_t conn_handle,uint16_t attr_handle,const void * data,uint16_t data_len,ble_gatt_attr_fn * cb,void * cb_arg)3540 ble_gattc_write_flat(uint16_t conn_handle, uint16_t attr_handle,
3541 const void *data, uint16_t data_len,
3542 ble_gatt_attr_fn *cb, void *cb_arg)
3543 {
3544 struct os_mbuf *om;
3545 int rc;
3546
3547 om = ble_hs_mbuf_from_flat(data, data_len);
3548 if (om == NULL) {
3549 return BLE_HS_ENOMEM;
3550 }
3551
3552 rc = ble_gattc_write(conn_handle, attr_handle, om, cb, cb_arg);
3553 if (rc != 0) {
3554 return rc;
3555 }
3556
3557 return 0;
3558 }
3559
3560 /*****************************************************************************
3561 * $write long *
3562 *****************************************************************************/
3563
3564 /**
3565 * Calls a write-long-characteristic-value proc's callback with the specified
3566 * parameters. If the proc has no callback, this function is a no-op.
3567 *
3568 * @return The return code of the callback (or 0 if there
3569 * is no callback).
3570 */
3571 static int
ble_gattc_write_long_cb(struct ble_gattc_proc * proc,int status,uint16_t att_handle)3572 ble_gattc_write_long_cb(struct ble_gattc_proc *proc, int status,
3573 uint16_t att_handle)
3574 {
3575 int rc;
3576
3577 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
3578 ble_gattc_dbg_assert_proc_not_inserted(proc);
3579
3580 if (status != 0 && status != BLE_HS_EDONE) {
3581 STATS_INC(ble_gattc_stats, write_long_fail);
3582 }
3583
3584 if (proc->write_long.cb == NULL) {
3585 rc = 0;
3586 } else {
3587 rc = proc->write_long.cb(proc->conn_handle,
3588 ble_gattc_error(status, att_handle),
3589 &proc->write_long.attr,
3590 proc->write_long.cb_arg);
3591 }
3592
3593 return rc;
3594 }
3595
3596 static void
ble_gattc_write_long_tmo(struct ble_gattc_proc * proc)3597 ble_gattc_write_long_tmo(struct ble_gattc_proc *proc)
3598 {
3599 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
3600 ble_gattc_dbg_assert_proc_not_inserted(proc);
3601
3602 ble_gattc_write_long_cb(proc, BLE_HS_ETIMEOUT, 0);
3603 }
3604
3605 /**
3606 * Triggers a pending transmit for the specified
3607 * write-long-characteristic-value proc.
3608 */
3609 static int
ble_gattc_write_long_tx(struct ble_gattc_proc * proc)3610 ble_gattc_write_long_tx(struct ble_gattc_proc *proc)
3611 {
3612 struct os_mbuf *om;
3613 int write_len;
3614 int max_sz;
3615 int rc;
3616
3617 ble_gattc_dbg_assert_proc_not_inserted(proc);
3618
3619 om = NULL;
3620
3621 max_sz = ble_att_mtu(proc->conn_handle) - BLE_ATT_PREP_WRITE_CMD_BASE_SZ;
3622 if (max_sz <= 0) {
3623 /* Not connected. */
3624 rc = BLE_HS_ENOTCONN;
3625 goto done;
3626 }
3627
3628 write_len = min(max_sz,
3629 OS_MBUF_PKTLEN(proc->write_long.attr.om) -
3630 proc->write_long.attr.offset);
3631
3632 if (write_len <= 0) {
3633 rc = ble_att_clt_tx_exec_write(proc->conn_handle,
3634 BLE_ATT_EXEC_WRITE_F_EXECUTE);
3635 goto done;
3636 }
3637
3638 proc->write_long.length = write_len;
3639 om = ble_hs_mbuf_att_pkt();
3640 if (om == NULL) {
3641 rc = BLE_HS_ENOMEM;
3642 goto done;
3643 }
3644
3645 rc = os_mbuf_appendfrom(om, proc->write_long.attr.om,
3646 proc->write_long.attr.offset,
3647 proc->write_long.length);
3648 if (rc != 0) {
3649 rc = BLE_HS_ENOMEM;
3650 goto done;
3651 }
3652
3653 rc = ble_att_clt_tx_prep_write(proc->conn_handle,
3654 proc->write_long.attr.handle,
3655 proc->write_long.attr.offset, om);
3656 om = NULL;
3657 if (rc != 0) {
3658 goto done;
3659 }
3660
3661 done:
3662 os_mbuf_free_chain(om);
3663 return rc;
3664 }
3665
3666 static int
ble_gattc_write_long_resume(struct ble_gattc_proc * proc)3667 ble_gattc_write_long_resume(struct ble_gattc_proc *proc)
3668 {
3669 int status;
3670 int rc;
3671
3672 status = ble_gattc_write_long_tx(proc);
3673 rc = ble_gattc_process_resume_status(proc, status);
3674 if (rc != 0) {
3675 ble_gattc_write_long_cb(proc, rc, 0);
3676 return rc;
3677 }
3678
3679 return 0;
3680 }
3681
3682 /**
3683 * Handles an incoming ATT error response for the specified
3684 * write-long-characteristic-value proc.
3685 */
3686 static void
ble_gattc_write_long_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)3687 ble_gattc_write_long_err(struct ble_gattc_proc *proc, int status,
3688 uint16_t att_handle)
3689 {
3690 ble_gattc_dbg_assert_proc_not_inserted(proc);
3691
3692 /* If we have successfully queued any data, and the failure occurred before
3693 * we could send the execute write command, then erase all queued data.
3694 */
3695 if (proc->write_long.attr.offset > 0 &&
3696 proc->write_long.attr.offset <
3697 OS_MBUF_PKTLEN(proc->write_long.attr.om)) {
3698
3699 ble_att_clt_tx_exec_write(proc->conn_handle,
3700 BLE_ATT_EXEC_WRITE_F_CANCEL);
3701 }
3702
3703 /* Report failure. */
3704 ble_gattc_write_long_cb(proc, status, att_handle);
3705 }
3706
3707 /**
3708 * Handles an incoming prepare-write-response for the specified
3709 * write-long-cahracteristic-values proc.
3710 */
3711 static int
ble_gattc_write_long_rx_prep(struct ble_gattc_proc * proc,int status,uint16_t handle,uint16_t offset,struct os_mbuf ** rxom)3712 ble_gattc_write_long_rx_prep(struct ble_gattc_proc *proc,
3713 int status,
3714 uint16_t handle, uint16_t offset,
3715 struct os_mbuf **rxom)
3716 {
3717 struct os_mbuf *om;
3718 int rc;
3719
3720 ble_gattc_dbg_assert_proc_not_inserted(proc);
3721
3722 /* Let the caller free the mbuf. */
3723 om = *rxom;
3724
3725 if (status != 0) {
3726 rc = status;
3727 goto err;
3728 }
3729
3730 /* Verify the response. */
3731 if (proc->write_long.attr.offset >=
3732 OS_MBUF_PKTLEN(proc->write_long.attr.om)) {
3733
3734 /* Expecting a prepare write response, not an execute write
3735 * response.
3736 */
3737 rc = BLE_HS_EBADDATA;
3738 goto err;
3739 }
3740 if (handle != proc->write_long.attr.handle) {
3741 rc = BLE_HS_EBADDATA;
3742 goto err;
3743 }
3744 if (offset != proc->write_long.attr.offset) {
3745 rc = BLE_HS_EBADDATA;
3746 goto err;
3747 }
3748 if (offset + OS_MBUF_PKTLEN(om) >
3749 OS_MBUF_PKTLEN(proc->write_long.attr.om)) {
3750
3751 rc = BLE_HS_EBADDATA;
3752 goto err;
3753 }
3754 if (OS_MBUF_PKTLEN(om) != proc->write_long.length) {
3755 rc = BLE_HS_EBADDATA;
3756 goto err;
3757 }
3758 if (os_mbuf_cmpm(om, 0,
3759 proc->write_long.attr.om, offset,
3760 proc->write_long.length) != 0) {
3761
3762 rc = BLE_HS_EBADDATA;
3763 goto err;
3764 }
3765
3766 /* Send follow-up request. */
3767 proc->write_long.attr.offset += OS_MBUF_PKTLEN(om);
3768 rc = ble_gattc_write_long_resume(proc);
3769 if (rc != 0) {
3770 goto err;
3771 }
3772
3773 return 0;
3774
3775 err:
3776 /* XXX: Might need to cancel pending writes. */
3777 ble_gattc_write_long_cb(proc, rc, 0);
3778 return BLE_HS_EDONE;
3779 }
3780
3781 /**
3782 * Handles an incoming execute-write-response for the specified
3783 * write-long-characteristic-values proc.
3784 */
3785 static int
ble_gattc_write_long_rx_exec(struct ble_gattc_proc * proc,int status)3786 ble_gattc_write_long_rx_exec(struct ble_gattc_proc *proc, int status)
3787 {
3788 ble_gattc_dbg_assert_proc_not_inserted(proc);
3789
3790 if (proc->write_long.attr.offset <
3791 OS_MBUF_PKTLEN(proc->write_long.attr.om)) {
3792
3793 /* Expecting an execute write response, not a prepare write
3794 * response.
3795 */
3796 return BLE_HS_EBADDATA;
3797 }
3798
3799 ble_gattc_write_long_cb(proc, status, 0);
3800 return BLE_HS_EDONE;
3801 }
3802
3803 int
ble_gattc_write_long(uint16_t conn_handle,uint16_t attr_handle,uint16_t offset,struct os_mbuf * txom,ble_gatt_attr_fn * cb,void * cb_arg)3804 ble_gattc_write_long(uint16_t conn_handle, uint16_t attr_handle,
3805 uint16_t offset, struct os_mbuf *txom,
3806 ble_gatt_attr_fn *cb, void *cb_arg)
3807 {
3808 #if !MYNEWT_VAL(BLE_GATT_WRITE_LONG)
3809 return BLE_HS_ENOTSUP;
3810 #endif
3811
3812 struct ble_gattc_proc *proc;
3813 int rc;
3814
3815 STATS_INC(ble_gattc_stats, write_long);
3816
3817 proc = ble_gattc_proc_alloc();
3818 if (proc == NULL) {
3819 rc = BLE_HS_ENOMEM;
3820 goto done;
3821 }
3822
3823 proc->op = BLE_GATT_OP_WRITE_LONG;
3824 proc->conn_handle = conn_handle;
3825 proc->write_long.attr.handle = attr_handle;
3826 proc->write_long.attr.offset = offset;
3827 proc->write_long.attr.om = txom;
3828 proc->write_long.cb = cb;
3829 proc->write_long.cb_arg = cb_arg;
3830
3831 /* The mbuf is consumed by the procedure. */
3832 txom = NULL;
3833
3834 ble_gattc_log_write_long(proc);
3835
3836 rc = ble_gattc_write_long_tx(proc);
3837 if (rc != 0) {
3838 goto done;
3839 }
3840
3841 done:
3842 if (rc != 0) {
3843 STATS_INC(ble_gattc_stats, write_long_fail);
3844 }
3845
3846 /* Free the mbuf in case of failure. */
3847 os_mbuf_free_chain(txom);
3848
3849 ble_gattc_process_status(proc, rc);
3850 return rc;
3851 }
3852
3853 /*****************************************************************************
3854 * $write reliable *
3855 *****************************************************************************/
3856
3857 /**
3858 * Calls a write-long-characteristic-value proc's callback with the specified
3859 * parameters. If the proc has no callback, this function is a no-op.
3860 *
3861 * @return The return code of the callback (or 0 if there
3862 * is no callback).
3863 */
3864 static int
ble_gattc_write_reliable_cb(struct ble_gattc_proc * proc,int status,uint16_t att_handle)3865 ble_gattc_write_reliable_cb(struct ble_gattc_proc *proc, int status,
3866 uint16_t att_handle)
3867 {
3868 int rc;
3869
3870 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
3871 ble_gattc_dbg_assert_proc_not_inserted(proc);
3872
3873 if (status != 0 && status != BLE_HS_EDONE) {
3874 STATS_INC(ble_gattc_stats, write_reliable_fail);
3875 }
3876
3877 if (proc->write_reliable.cb == NULL) {
3878 rc = 0;
3879 } else {
3880 rc = proc->write_reliable.cb(proc->conn_handle,
3881 ble_gattc_error(status, att_handle),
3882 proc->write_reliable.attrs,
3883 proc->write_reliable.num_attrs,
3884 proc->write_reliable.cb_arg);
3885 }
3886
3887 return rc;
3888 }
3889
3890 static void
ble_gattc_write_reliable_tmo(struct ble_gattc_proc * proc)3891 ble_gattc_write_reliable_tmo(struct ble_gattc_proc *proc)
3892 {
3893 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
3894 ble_gattc_dbg_assert_proc_not_inserted(proc);
3895
3896 ble_gattc_write_reliable_cb(proc, BLE_HS_ETIMEOUT, 0);
3897 }
3898
3899 /**
3900 * Triggers a pending transmit for the specified
3901 * write-reliable-characteristic-value proc.
3902 */
3903 static int
ble_gattc_write_reliable_tx(struct ble_gattc_proc * proc)3904 ble_gattc_write_reliable_tx(struct ble_gattc_proc *proc)
3905 {
3906 struct ble_gatt_attr *attr;
3907 struct os_mbuf *om;
3908 uint16_t max_sz;
3909 int attr_idx;
3910 int rc;
3911
3912 ble_gattc_dbg_assert_proc_not_inserted(proc);
3913
3914 om = NULL;
3915
3916 attr_idx = proc->write_reliable.cur_attr;
3917
3918 if (attr_idx >= proc->write_reliable.num_attrs) {
3919 rc = ble_att_clt_tx_exec_write(proc->conn_handle,
3920 BLE_ATT_EXEC_WRITE_F_EXECUTE);
3921 goto done;
3922 }
3923
3924 attr = proc->write_reliable.attrs + attr_idx;
3925
3926 max_sz = ble_att_mtu(proc->conn_handle) - BLE_ATT_PREP_WRITE_CMD_BASE_SZ;
3927 if (max_sz <= 0) {
3928 /* Not connected. */
3929 rc = BLE_HS_ENOTCONN;
3930 goto done;
3931 }
3932
3933 proc->write_reliable.length =
3934 min(max_sz, OS_MBUF_PKTLEN(attr->om) - attr->offset);
3935
3936 om = ble_hs_mbuf_att_pkt();
3937 if (om == NULL) {
3938 rc = BLE_HS_ENOMEM;
3939 goto done;
3940 }
3941
3942 rc = os_mbuf_appendfrom(om, attr->om, attr->offset,
3943 proc->write_reliable.length);
3944 if (rc != 0) {
3945 rc = BLE_HS_ENOMEM;
3946 goto done;
3947 }
3948
3949 rc = ble_att_clt_tx_prep_write(proc->conn_handle, attr->handle,
3950 attr->offset, om);
3951 om = NULL;
3952 if (rc != 0) {
3953 goto done;
3954 }
3955
3956 done:
3957 os_mbuf_free_chain(om);
3958 return rc;
3959 }
3960
3961 static int
ble_gattc_write_reliable_resume(struct ble_gattc_proc * proc)3962 ble_gattc_write_reliable_resume(struct ble_gattc_proc *proc)
3963 {
3964 int status;
3965 int rc;
3966
3967 status = ble_gattc_write_reliable_tx(proc);
3968 rc = ble_gattc_process_resume_status(proc, status);
3969 if (rc != 0) {
3970 ble_gattc_write_reliable_cb(proc, rc, 0);
3971 return rc;
3972 }
3973
3974 return 0;
3975 }
3976
3977 /**
3978 * Handles an incoming ATT error response for the specified
3979 * write-reliable-characteristic-value proc.
3980 */
3981 static void
ble_gattc_write_reliable_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)3982 ble_gattc_write_reliable_err(struct ble_gattc_proc *proc, int status,
3983 uint16_t att_handle)
3984 {
3985 ble_gattc_dbg_assert_proc_not_inserted(proc);
3986 ble_gattc_write_reliable_cb(proc, status, att_handle);
3987
3988 /* If we have successfully queued any data, and the failure occurred before
3989 * we could send the execute write command, then erase all queued data.
3990 */
3991 if (proc->write_reliable.cur_attr < proc->write_reliable.num_attrs) {
3992
3993 ble_att_clt_tx_exec_write(proc->conn_handle,
3994 BLE_ATT_EXEC_WRITE_F_CANCEL);
3995 }
3996 }
3997
3998 /**
3999 * Handles an incoming prepare-write-response for the specified
4000 * write-reliable-cahracteristic-values proc.
4001 */
4002 static int
ble_gattc_write_reliable_rx_prep(struct ble_gattc_proc * proc,int status,uint16_t handle,uint16_t offset,struct os_mbuf ** rxom)4003 ble_gattc_write_reliable_rx_prep(struct ble_gattc_proc *proc,
4004 int status,
4005 uint16_t handle, uint16_t offset,
4006 struct os_mbuf **rxom)
4007 {
4008 struct ble_gatt_attr *attr;
4009 struct os_mbuf *om;
4010 int rc;
4011
4012 ble_gattc_dbg_assert_proc_not_inserted(proc);
4013
4014 /* Let the caller free the mbuf. */
4015 om = *rxom;
4016
4017 if (status != 0) {
4018 rc = status;
4019 goto err;
4020 }
4021
4022 if (proc->write_reliable.cur_attr >= proc->write_reliable.num_attrs) {
4023 /* Expecting an execute write response, not a prepare write
4024 * response.
4025 */
4026 rc = BLE_HS_EBADDATA;
4027 goto err;
4028 }
4029 attr = proc->write_reliable.attrs + proc->write_reliable.cur_attr;
4030
4031 /* Verify the response. */
4032 if (handle != attr->handle) {
4033 rc = BLE_HS_EBADDATA;
4034 goto err;
4035 }
4036 if (offset != attr->offset) {
4037 rc = BLE_HS_EBADDATA;
4038 goto err;
4039 }
4040 if (os_mbuf_cmpm(attr->om, offset, om, 0,
4041 proc->write_reliable.length) != 0) {
4042
4043 rc = BLE_HS_EBADDATA;
4044 goto err;
4045 }
4046
4047 /* Send follow-up request. */
4048 attr->offset += proc->write_reliable.length;
4049 if (attr->offset >= OS_MBUF_PKTLEN(attr->om)) {
4050 attr->offset = 0;
4051 proc->write_reliable.cur_attr++;
4052 }
4053 rc = ble_gattc_write_reliable_resume(proc);
4054 if (rc != 0) {
4055 goto err;
4056 }
4057
4058 return 0;
4059
4060 err:
4061 ble_gattc_write_reliable_err(proc, rc, 0);
4062 return BLE_HS_EDONE;
4063 }
4064
4065 /**
4066 * Handles an incoming execute-write-response for the specified
4067 * write-reliable-characteristic-values proc.
4068 */
4069 static int
ble_gattc_write_reliable_rx_exec(struct ble_gattc_proc * proc,int status)4070 ble_gattc_write_reliable_rx_exec(struct ble_gattc_proc *proc, int status)
4071 {
4072 ble_gattc_dbg_assert_proc_not_inserted(proc);
4073 ble_gattc_write_reliable_cb(proc, status, 0);
4074 return BLE_HS_EDONE;
4075 }
4076
4077 int
ble_gattc_write_reliable(uint16_t conn_handle,struct ble_gatt_attr * attrs,int num_attrs,ble_gatt_reliable_attr_fn * cb,void * cb_arg)4078 ble_gattc_write_reliable(uint16_t conn_handle,
4079 struct ble_gatt_attr *attrs,
4080 int num_attrs,
4081 ble_gatt_reliable_attr_fn *cb, void *cb_arg)
4082 {
4083 #if !MYNEWT_VAL(BLE_GATT_WRITE_RELIABLE)
4084 return BLE_HS_ENOTSUP;
4085 #endif
4086
4087 struct ble_gattc_proc *proc;
4088 int rc;
4089 int i;
4090
4091 proc = NULL;
4092
4093 STATS_INC(ble_gattc_stats, write_reliable);
4094
4095 if (num_attrs > MYNEWT_VAL(BLE_GATT_WRITE_MAX_ATTRS)) {
4096 rc = BLE_HS_EINVAL;
4097 goto done;
4098 }
4099
4100 proc = ble_gattc_proc_alloc();
4101 if (proc == NULL) {
4102 rc = BLE_HS_ENOMEM;
4103 goto done;
4104 }
4105
4106 proc->op = BLE_GATT_OP_WRITE_RELIABLE;
4107 proc->conn_handle = conn_handle;
4108 proc->write_reliable.num_attrs = num_attrs;
4109 proc->write_reliable.cur_attr = 0;
4110 proc->write_reliable.cb = cb;
4111 proc->write_reliable.cb_arg = cb_arg;
4112
4113 for (i = 0; i < num_attrs; i++) {
4114 proc->write_reliable.attrs[i] = attrs[i];
4115 proc->write_reliable.attrs[i].offset = 0;
4116
4117 /* Consume mbuf from caller. */
4118 attrs[i].om = NULL;
4119 }
4120
4121 ble_gattc_log_write_reliable(proc);
4122 rc = ble_gattc_write_reliable_tx(proc);
4123 if (rc != 0) {
4124 goto done;
4125 }
4126
4127 done:
4128 if (rc != 0) {
4129 STATS_INC(ble_gattc_stats, write_reliable_fail);
4130 }
4131
4132 /* Free supplied mbufs in case something failed. */
4133 for (i = 0; i < num_attrs; i++) {
4134 os_mbuf_free_chain(attrs[i].om);
4135 attrs[i].om = NULL;
4136 }
4137
4138 ble_gattc_process_status(proc, rc);
4139 return rc;
4140 }
4141
4142 /*****************************************************************************
4143 * $notify *
4144 *****************************************************************************/
4145
4146 int
ble_gattc_notify_custom(uint16_t conn_handle,uint16_t chr_val_handle,struct os_mbuf * txom)4147 ble_gattc_notify_custom(uint16_t conn_handle, uint16_t chr_val_handle,
4148 struct os_mbuf *txom)
4149 {
4150 #if !MYNEWT_VAL(BLE_GATT_NOTIFY)
4151 return BLE_HS_ENOTSUP;
4152 #endif
4153
4154 int rc;
4155
4156 STATS_INC(ble_gattc_stats, notify);
4157
4158 ble_gattc_log_notify(chr_val_handle);
4159
4160 if (txom == NULL) {
4161 /* No custom attribute data; read the value from the specified
4162 * attribute.
4163 */
4164 txom = ble_hs_mbuf_att_pkt();
4165 if (txom == NULL) {
4166 rc = BLE_HS_ENOMEM;
4167 goto done;
4168 }
4169 rc = ble_att_svr_read_handle(BLE_HS_CONN_HANDLE_NONE,
4170 chr_val_handle, 0, txom, NULL);
4171 if (rc != 0) {
4172 /* Fatal error; application disallowed attribute read. */
4173 rc = BLE_HS_EAPP;
4174 goto done;
4175 }
4176 }
4177
4178 rc = ble_att_clt_tx_notify(conn_handle, chr_val_handle, txom);
4179 txom = NULL;
4180 if (rc != 0) {
4181 goto done;
4182 }
4183
4184 done:
4185 if (rc != 0) {
4186 STATS_INC(ble_gattc_stats, notify_fail);
4187 }
4188
4189 /* Tell the application that a notification transmission was attempted. */
4190 ble_gap_notify_tx_event(rc, conn_handle, chr_val_handle, 0);
4191
4192 os_mbuf_free_chain(txom);
4193
4194 return rc;
4195 }
4196
4197 int
ble_gattc_notify(uint16_t conn_handle,uint16_t chr_val_handle)4198 ble_gattc_notify(uint16_t conn_handle, uint16_t chr_val_handle)
4199 {
4200 #if !MYNEWT_VAL(BLE_GATT_NOTIFY)
4201 return BLE_HS_ENOTSUP;
4202 #endif
4203
4204 int rc;
4205
4206 rc = ble_gattc_notify_custom(conn_handle, chr_val_handle, NULL);
4207
4208 return rc;
4209 }
4210
4211 /*****************************************************************************
4212 * $indicate *
4213 *****************************************************************************/
4214
4215 /**
4216 * Handles an incoming ATT error response for the specified indication proc.
4217 * A device should never send an error in response to an indication. If this
4218 * happens, we treat it like a confirmation (indication ack), but report the
4219 * error status to the application.
4220 */
4221 static void
ble_gattc_indicate_err(struct ble_gattc_proc * proc,int status,uint16_t att_handle)4222 ble_gattc_indicate_err(struct ble_gattc_proc *proc, int status,
4223 uint16_t att_handle)
4224 {
4225 int rc;
4226
4227 ble_gattc_dbg_assert_proc_not_inserted(proc);
4228
4229 if (status != BLE_HS_ENOTCONN) {
4230 rc = ble_gatts_rx_indicate_ack(proc->conn_handle,
4231 proc->indicate.chr_val_handle);
4232 if (rc != 0) {
4233 return;
4234 }
4235 }
4236
4237 /* Tell the application about the received acknowledgment. */
4238 ble_gap_notify_tx_event(status, proc->conn_handle,
4239 proc->indicate.chr_val_handle, 1);
4240
4241 /* Send the next indication if one is pending. */
4242 ble_gatts_send_next_indicate(proc->conn_handle);
4243 }
4244
4245 static void
ble_gattc_indicate_tmo(struct ble_gattc_proc * proc)4246 ble_gattc_indicate_tmo(struct ble_gattc_proc *proc)
4247 {
4248 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
4249 ble_gattc_dbg_assert_proc_not_inserted(proc);
4250
4251 ble_gap_notify_tx_event(BLE_HS_ETIMEOUT, proc->conn_handle,
4252 proc->indicate.chr_val_handle, 1);
4253 }
4254
4255 /**
4256 * Handles an incoming handle-value-confirmation for the specified indication
4257 * proc.
4258 */
4259 static void
ble_gattc_indicate_rx_rsp(struct ble_gattc_proc * proc)4260 ble_gattc_indicate_rx_rsp(struct ble_gattc_proc *proc)
4261 {
4262 int rc;
4263
4264 ble_gattc_dbg_assert_proc_not_inserted(proc);
4265
4266 rc = ble_gatts_rx_indicate_ack(proc->conn_handle,
4267 proc->indicate.chr_val_handle);
4268 if (rc != 0) {
4269 return;
4270 }
4271
4272 /* Tell the application about the received acknowledgment. */
4273 ble_gap_notify_tx_event(BLE_HS_EDONE, proc->conn_handle,
4274 proc->indicate.chr_val_handle, 1);
4275
4276 /* Send the next indication if one is pending. */
4277 ble_gatts_send_next_indicate(proc->conn_handle);
4278 }
4279
4280 /**
4281 * Causes the indication in progress for the specified connection (if any) to
4282 * fail with a status code of BLE_HS_ENOTCONN;
4283 */
4284 void
ble_gatts_indicate_fail_notconn(uint16_t conn_handle)4285 ble_gatts_indicate_fail_notconn(uint16_t conn_handle)
4286 {
4287 ble_gattc_fail_procs(conn_handle, BLE_GATT_OP_INDICATE, BLE_HS_ENOTCONN);
4288 }
4289
4290 int
ble_gattc_indicate_custom(uint16_t conn_handle,uint16_t chr_val_handle,struct os_mbuf * txom)4291 ble_gattc_indicate_custom(uint16_t conn_handle, uint16_t chr_val_handle,
4292 struct os_mbuf *txom)
4293 {
4294 #if !MYNEWT_VAL(BLE_GATT_INDICATE)
4295 return BLE_HS_ENOTSUP;
4296 #endif
4297
4298 struct ble_gattc_proc *proc;
4299 struct ble_hs_conn *conn;
4300 int rc;
4301
4302 STATS_INC(ble_gattc_stats, indicate);
4303
4304 proc = ble_gattc_proc_alloc();
4305 if (proc == NULL) {
4306 rc = BLE_HS_ENOMEM;
4307 goto done;
4308 }
4309
4310 proc->op = BLE_GATT_OP_INDICATE;
4311 proc->conn_handle = conn_handle;
4312 proc->indicate.chr_val_handle = chr_val_handle;
4313
4314 ble_gattc_log_indicate(chr_val_handle);
4315
4316 if (txom == NULL) {
4317 /* No custom attribute data; read the value from the specified
4318 * attribute.
4319 */
4320 txom = ble_hs_mbuf_att_pkt();
4321 if (txom == NULL) {
4322 rc = BLE_HS_ENOMEM;
4323 goto done;
4324 }
4325
4326 rc = ble_att_svr_read_handle(BLE_HS_CONN_HANDLE_NONE, chr_val_handle,
4327 0, txom, NULL);
4328 if (rc != 0) {
4329 /* Fatal error; application disallowed attribute read. */
4330 BLE_HS_DBG_ASSERT(0);
4331 rc = BLE_HS_EAPP;
4332 goto done;
4333 }
4334 }
4335
4336 rc = ble_att_clt_tx_indicate(conn_handle, chr_val_handle, txom);
4337 txom = NULL;
4338 if (rc != 0) {
4339 goto done;
4340 }
4341
4342 ble_hs_lock();
4343 conn = ble_hs_conn_find(conn_handle);
4344 if (conn != NULL) {
4345 BLE_HS_DBG_ASSERT(conn->bhc_gatt_svr.indicate_val_handle == 0);
4346 conn->bhc_gatt_svr.indicate_val_handle = chr_val_handle;
4347 }
4348 ble_hs_unlock();
4349
4350 done:
4351 if (rc != 0) {
4352 STATS_INC(ble_gattc_stats, indicate_fail);
4353 }
4354
4355 /* Tell the application that an indication transmission was attempted. */
4356 ble_gap_notify_tx_event(rc, conn_handle, chr_val_handle, 1);
4357
4358 ble_gattc_process_status(proc, rc);
4359 os_mbuf_free_chain(txom);
4360 return rc;
4361 }
4362
4363 int
ble_gattc_indicate(uint16_t conn_handle,uint16_t chr_val_handle)4364 ble_gattc_indicate(uint16_t conn_handle, uint16_t chr_val_handle)
4365 {
4366 return ble_gattc_indicate_custom(conn_handle, chr_val_handle, NULL);
4367 }
4368
4369 /*****************************************************************************
4370 * $rx *
4371 *****************************************************************************/
4372
4373 /**
4374 * Dispatches an incoming ATT error-response to the appropriate active GATT
4375 * procedure.
4376 */
4377 void
ble_gattc_rx_err(uint16_t conn_handle,uint16_t handle,uint16_t status)4378 ble_gattc_rx_err(uint16_t conn_handle, uint16_t handle, uint16_t status)
4379 {
4380 struct ble_gattc_proc *proc;
4381 ble_gattc_err_fn *err_cb;
4382
4383 proc = ble_gattc_extract_first_by_conn_op(conn_handle, BLE_GATT_OP_NONE);
4384 if (proc != NULL) {
4385 err_cb = ble_gattc_err_dispatch_get(proc->op);
4386 if (err_cb != NULL) {
4387 err_cb(proc, BLE_HS_ERR_ATT_BASE + status, handle);
4388 }
4389 ble_gattc_proc_free(proc);
4390 }
4391 }
4392
4393 /**
4394 * Dispatches an incoming ATT exchange-mtu-response to the appropriate active
4395 * GATT procedure.
4396 */
4397 void
ble_gattc_rx_mtu(uint16_t conn_handle,int status,uint16_t chan_mtu)4398 ble_gattc_rx_mtu(uint16_t conn_handle, int status, uint16_t chan_mtu)
4399 {
4400 struct ble_gattc_proc *proc;
4401
4402 proc = ble_gattc_extract_first_by_conn_op(conn_handle, BLE_GATT_OP_MTU);
4403 if (proc != NULL) {
4404 ble_gattc_mtu_cb(proc, status, 0, chan_mtu);
4405 ble_gattc_process_status(proc, BLE_HS_EDONE);
4406 }
4407 }
4408
4409 /**
4410 * Dispatches an incoming "information data" entry from a
4411 * find-information-response to the appropriate active GATT procedure.
4412 */
4413 void
ble_gattc_rx_find_info_idata(uint16_t conn_handle,struct ble_att_find_info_idata * idata)4414 ble_gattc_rx_find_info_idata(uint16_t conn_handle,
4415 struct ble_att_find_info_idata *idata)
4416 {
4417 #if !NIMBLE_BLE_ATT_CLT_FIND_INFO
4418 return;
4419 #endif
4420
4421 struct ble_gattc_proc *proc;
4422 int rc;
4423
4424 proc = ble_gattc_extract_first_by_conn_op(conn_handle,
4425 BLE_GATT_OP_DISC_ALL_DSCS);
4426 if (proc != NULL) {
4427 rc = ble_gattc_disc_all_dscs_rx_idata(proc, idata);
4428 ble_gattc_process_status(proc, rc);
4429 }
4430 }
4431
4432 /**
4433 * Dispatches an incoming notification of the end of a
4434 * find-information-response to the appropriate active GATT procedure.
4435 */
4436 void
ble_gattc_rx_find_info_complete(uint16_t conn_handle,int status)4437 ble_gattc_rx_find_info_complete(uint16_t conn_handle, int status)
4438 {
4439 #if !NIMBLE_BLE_ATT_CLT_FIND_INFO
4440 return;
4441 #endif
4442
4443 struct ble_gattc_proc *proc;
4444 int rc;
4445
4446 proc = ble_gattc_extract_first_by_conn_op(conn_handle,
4447 BLE_GATT_OP_DISC_ALL_DSCS);
4448 if (proc != NULL) {
4449 rc = ble_gattc_disc_all_dscs_rx_complete(proc, status);
4450 ble_gattc_process_status(proc, rc);
4451 }
4452 }
4453
4454 /**
4455 * Dispatches an incoming "handles info" entry from a
4456 * find-by-type-value-response to the appropriate active GATT procedure.
4457 */
4458 void
ble_gattc_rx_find_type_value_hinfo(uint16_t conn_handle,struct ble_att_find_type_value_hinfo * hinfo)4459 ble_gattc_rx_find_type_value_hinfo(uint16_t conn_handle,
4460 struct ble_att_find_type_value_hinfo *hinfo)
4461 {
4462 #if !NIMBLE_BLE_ATT_CLT_FIND_TYPE
4463 return;
4464 #endif
4465
4466 struct ble_gattc_proc *proc;
4467 int rc;
4468
4469 proc = ble_gattc_extract_first_by_conn_op(conn_handle,
4470 BLE_GATT_OP_DISC_SVC_UUID);
4471 if (proc != NULL) {
4472 rc = ble_gattc_disc_svc_uuid_rx_hinfo(proc, hinfo);
4473 ble_gattc_process_status(proc, rc);
4474 }
4475 }
4476
4477 /**
4478 * Dispatches an incoming notification of the end of a
4479 * find-by-type-value-response to the appropriate active GATT procedure.
4480 */
4481 void
ble_gattc_rx_find_type_value_complete(uint16_t conn_handle,int status)4482 ble_gattc_rx_find_type_value_complete(uint16_t conn_handle, int status)
4483 {
4484 #if !NIMBLE_BLE_ATT_CLT_FIND_TYPE
4485 return;
4486 #endif
4487
4488 struct ble_gattc_proc *proc;
4489 int rc;
4490
4491 proc = ble_gattc_extract_first_by_conn_op(conn_handle,
4492 BLE_GATT_OP_DISC_SVC_UUID);
4493 if (proc != NULL) {
4494 rc = ble_gattc_disc_svc_uuid_rx_complete(proc, status);
4495 ble_gattc_process_status(proc, rc);
4496 }
4497 }
4498
4499 /**
4500 * Dispatches an incoming "attribute data" entry from a read-by-type-response
4501 * to the appropriate active GATT procedure.
4502 */
4503 void
ble_gattc_rx_read_type_adata(uint16_t conn_handle,struct ble_att_read_type_adata * adata)4504 ble_gattc_rx_read_type_adata(uint16_t conn_handle,
4505 struct ble_att_read_type_adata *adata)
4506 {
4507 #if !NIMBLE_BLE_ATT_CLT_READ_TYPE
4508 return;
4509 #endif
4510
4511 const struct ble_gattc_rx_adata_entry *rx_entry;
4512 struct ble_gattc_proc *proc;
4513 int rc;
4514
4515 proc = BLE_GATTC_RX_EXTRACT_RX_ENTRY(conn_handle,
4516 ble_gattc_rx_read_type_elem_entries,
4517 &rx_entry);
4518 if (proc != NULL) {
4519 rc = rx_entry->cb(proc, adata);
4520 ble_gattc_process_status(proc, rc);
4521 }
4522 }
4523
4524 /**
4525 * Dispatches an incoming notification of the end of a read-by-type-response to
4526 * the appropriate active GATT procedure.
4527 */
4528 void
ble_gattc_rx_read_type_complete(uint16_t conn_handle,int status)4529 ble_gattc_rx_read_type_complete(uint16_t conn_handle, int status)
4530 {
4531 #if !NIMBLE_BLE_ATT_CLT_READ_TYPE
4532 return;
4533 #endif
4534
4535 const struct ble_gattc_rx_complete_entry *rx_entry;
4536 struct ble_gattc_proc *proc;
4537 int rc;
4538
4539 proc = BLE_GATTC_RX_EXTRACT_RX_ENTRY(
4540 conn_handle, ble_gattc_rx_read_type_complete_entries,
4541 &rx_entry);
4542 if (proc != NULL) {
4543 rc = rx_entry->cb(proc, status);
4544 ble_gattc_process_status(proc, rc);
4545 }
4546 }
4547
4548 /**
4549 * Dispatches an incoming "attribute data" entry from a
4550 * read-by-group-type-response to the appropriate active GATT procedure.
4551 */
4552 void
ble_gattc_rx_read_group_type_adata(uint16_t conn_handle,struct ble_att_read_group_type_adata * adata)4553 ble_gattc_rx_read_group_type_adata(uint16_t conn_handle,
4554 struct ble_att_read_group_type_adata *adata)
4555 {
4556 #if !NIMBLE_BLE_ATT_CLT_READ_GROUP_TYPE
4557 return;
4558 #endif
4559
4560 struct ble_gattc_proc *proc;
4561 int rc;
4562
4563 proc = ble_gattc_extract_first_by_conn_op(conn_handle,
4564 BLE_GATT_OP_DISC_ALL_SVCS);
4565 if (proc != NULL) {
4566 rc = ble_gattc_disc_all_svcs_rx_adata(proc, adata);
4567 ble_gattc_process_status(proc, rc);
4568 }
4569 }
4570
4571 /**
4572 * Dispatches an incoming notification of the end of a
4573 * read-by-group-type-response to the appropriate active GATT procedure.
4574 */
4575 void
ble_gattc_rx_read_group_type_complete(uint16_t conn_handle,int status)4576 ble_gattc_rx_read_group_type_complete(uint16_t conn_handle, int status)
4577 {
4578 #if !NIMBLE_BLE_ATT_CLT_READ_GROUP_TYPE
4579 return;
4580 #endif
4581
4582 struct ble_gattc_proc *proc;
4583 int rc;
4584
4585 proc = ble_gattc_extract_first_by_conn_op(conn_handle,
4586 BLE_GATT_OP_DISC_ALL_SVCS);
4587 if (proc != NULL) {
4588 rc = ble_gattc_disc_all_svcs_rx_complete(proc, status);
4589 ble_gattc_process_status(proc, rc);
4590 }
4591 }
4592
4593 /**
4594 * Dispatches an incoming ATT read-response to the appropriate active GATT
4595 * procedure.
4596 */
4597 void
ble_gattc_rx_read_rsp(uint16_t conn_handle,int status,struct os_mbuf ** om)4598 ble_gattc_rx_read_rsp(uint16_t conn_handle, int status, struct os_mbuf **om)
4599 {
4600 #if !NIMBLE_BLE_ATT_CLT_READ
4601 return;
4602 #endif
4603
4604 const struct ble_gattc_rx_attr_entry *rx_entry;
4605 struct ble_gattc_proc *proc;
4606 int rc;
4607
4608 proc = BLE_GATTC_RX_EXTRACT_RX_ENTRY(conn_handle,
4609 ble_gattc_rx_read_rsp_entries,
4610 &rx_entry);
4611 if (proc != NULL) {
4612 rc = rx_entry->cb(proc, status, om);
4613 ble_gattc_process_status(proc, rc);
4614 }
4615 }
4616
4617 /**
4618 * Dispatches an incoming ATT read-blob-response to the appropriate active GATT
4619 * procedure.
4620 */
4621 void
ble_gattc_rx_read_blob_rsp(uint16_t conn_handle,int status,struct os_mbuf ** om)4622 ble_gattc_rx_read_blob_rsp(uint16_t conn_handle, int status,
4623 struct os_mbuf **om)
4624 {
4625 #if !NIMBLE_BLE_ATT_CLT_READ_BLOB
4626 return;
4627 #endif
4628
4629 struct ble_gattc_proc *proc;
4630 int rc;
4631
4632 proc = ble_gattc_extract_first_by_conn_op(conn_handle,
4633 BLE_GATT_OP_READ_LONG);
4634 if (proc != NULL) {
4635 rc = ble_gattc_read_long_rx_read_rsp(proc, status, om);
4636 ble_gattc_process_status(proc, rc);
4637 }
4638 }
4639
4640 /**
4641 * Dispatches an incoming ATT read-multiple-response to the appropriate active
4642 * GATT procedure.
4643 */
4644 void
ble_gattc_rx_read_mult_rsp(uint16_t conn_handle,int status,struct os_mbuf ** om)4645 ble_gattc_rx_read_mult_rsp(uint16_t conn_handle, int status,
4646 struct os_mbuf **om)
4647 {
4648 #if !NIMBLE_BLE_ATT_CLT_READ_MULT
4649 return;
4650 #endif
4651
4652 struct ble_gattc_proc *proc;
4653
4654 proc = ble_gattc_extract_first_by_conn_op(conn_handle,
4655 BLE_GATT_OP_READ_MULT);
4656 if (proc != NULL) {
4657 ble_gattc_read_mult_cb(proc, status, 0, om);
4658 ble_gattc_process_status(proc, BLE_HS_EDONE);
4659 }
4660 }
4661
4662 /**
4663 * Dispatches an incoming ATT write-response to the appropriate active GATT
4664 * procedure.
4665 */
4666 void
ble_gattc_rx_write_rsp(uint16_t conn_handle)4667 ble_gattc_rx_write_rsp(uint16_t conn_handle)
4668 {
4669 #if !NIMBLE_BLE_ATT_CLT_WRITE
4670 return;
4671 #endif
4672
4673 struct ble_gattc_proc *proc;
4674
4675 proc = ble_gattc_extract_first_by_conn_op(conn_handle,
4676 BLE_GATT_OP_WRITE);
4677 if (proc != NULL) {
4678 ble_gattc_write_cb(proc, 0, 0);
4679 ble_gattc_process_status(proc, BLE_HS_EDONE);
4680 }
4681 }
4682
4683 /**
4684 * Dispatches an incoming ATT prepare-write-response to the appropriate active
4685 * GATT procedure.
4686 */
4687 void
ble_gattc_rx_prep_write_rsp(uint16_t conn_handle,int status,uint16_t handle,uint16_t offset,struct os_mbuf ** om)4688 ble_gattc_rx_prep_write_rsp(uint16_t conn_handle, int status,
4689 uint16_t handle, uint16_t offset,
4690 struct os_mbuf **om)
4691 {
4692 #if !NIMBLE_BLE_ATT_CLT_PREP_WRITE
4693 return;
4694 #endif
4695
4696 const struct ble_gattc_rx_prep_entry *rx_entry;
4697 struct ble_gattc_proc *proc;
4698 int rc;
4699
4700 proc = BLE_GATTC_RX_EXTRACT_RX_ENTRY(conn_handle,
4701 ble_gattc_rx_prep_entries,
4702 &rx_entry);
4703 if (proc != NULL) {
4704 rc = rx_entry->cb(proc, status, handle, offset, om);
4705 ble_gattc_process_status(proc, rc);
4706 }
4707 }
4708
4709 /**
4710 * Dispatches an incoming ATT execute-write-response to the appropriate active
4711 * GATT procedure.
4712 */
4713 void
ble_gattc_rx_exec_write_rsp(uint16_t conn_handle,int status)4714 ble_gattc_rx_exec_write_rsp(uint16_t conn_handle, int status)
4715 {
4716 #if !NIMBLE_BLE_ATT_CLT_EXEC_WRITE
4717 return;
4718 #endif
4719
4720 const struct ble_gattc_rx_exec_entry *rx_entry;
4721 struct ble_gattc_proc *proc;
4722 int rc;
4723
4724 proc = BLE_GATTC_RX_EXTRACT_RX_ENTRY(conn_handle,
4725 ble_gattc_rx_exec_entries, &rx_entry);
4726 if (proc != NULL) {
4727 rc = rx_entry->cb(proc, status);
4728 ble_gattc_process_status(proc, rc);
4729 }
4730 }
4731
4732 /**
4733 * Dispatches an incoming ATT handle-value-confirmation to the appropriate
4734 * active GATT procedure.
4735 */
4736 void
ble_gattc_rx_indicate_rsp(uint16_t conn_handle)4737 ble_gattc_rx_indicate_rsp(uint16_t conn_handle)
4738 {
4739 #if !NIMBLE_BLE_ATT_CLT_INDICATE
4740 return;
4741 #endif
4742
4743 struct ble_gattc_proc *proc;
4744
4745 proc = ble_gattc_extract_first_by_conn_op(conn_handle,
4746 BLE_GATT_OP_INDICATE);
4747 if (proc != NULL) {
4748 ble_gattc_indicate_rx_rsp(proc);
4749 ble_gattc_process_status(proc, BLE_HS_EDONE);
4750 }
4751 }
4752
4753 /*****************************************************************************
4754 * $misc *
4755 *****************************************************************************/
4756
4757 /**
4758 * Called when a BLE connection ends. Frees all GATT resources associated with
4759 * the connection and cancels all relevant pending and in-progress GATT
4760 * procedures.
4761 *
4762 * @param conn_handle The handle of the connection that was
4763 * terminated.
4764 */
4765 void
ble_gattc_connection_broken(uint16_t conn_handle)4766 ble_gattc_connection_broken(uint16_t conn_handle)
4767 {
4768 ble_gattc_fail_procs(conn_handle, BLE_GATT_OP_NONE, BLE_HS_ENOTCONN);
4769 }
4770
4771 /**
4772 * Indicates whether there are currently any active GATT client procedures.
4773 */
4774 int
ble_gattc_any_jobs(void)4775 ble_gattc_any_jobs(void)
4776 {
4777 return !STAILQ_EMPTY(&ble_gattc_procs);
4778 }
4779
4780 int
ble_gattc_init(void)4781 ble_gattc_init(void)
4782 {
4783 int rc;
4784
4785 STAILQ_INIT(&ble_gattc_procs);
4786
4787 if (MYNEWT_VAL(BLE_GATT_MAX_PROCS) > 0) {
4788 rc = os_mempool_init(&ble_gattc_proc_pool,
4789 MYNEWT_VAL(BLE_GATT_MAX_PROCS),
4790 sizeof (struct ble_gattc_proc),
4791 ble_gattc_proc_mem,
4792 "ble_gattc_proc_pool");
4793 if (rc != 0) {
4794 return rc;
4795 }
4796 }
4797
4798 rc = stats_init_and_reg(
4799 STATS_HDR(ble_gattc_stats), STATS_SIZE_INIT_PARMS(ble_gattc_stats,
4800 STATS_SIZE_32), STATS_NAME_INIT_PARMS(ble_gattc_stats), "ble_gattc");
4801 if (rc != 0) {
4802 return BLE_HS_EOS;
4803 }
4804
4805 return 0;
4806 }
4807