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 * L2CAP Signaling (channel ID = 5).
22 *
23 * Design overview:
24 *
25 * L2CAP sig 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_l2cap_sig_procs). Thread-safety is achieved by locking the mutex
41 * during removal and insertion operations. Procedure objects are only
42 * modified while they are not in the list.
43 */
44
45 #include <string.h>
46 #include <errno.h>
47 #include "nimble/ble.h"
48 #include "host/ble_monitor.h"
49 #include "ble_hs_priv.h"
50
51 /*****************************************************************************
52 * $definitions / declarations *
53 *****************************************************************************/
54
55 #define BLE_L2CAP_SIG_UNRESPONSIVE_TIMEOUT 30000 /* Milliseconds. */
56
57 #define BLE_L2CAP_SIG_PROC_OP_UPDATE 0
58 #define BLE_L2CAP_SIG_PROC_OP_CONNECT 1
59 #define BLE_L2CAP_SIG_PROC_OP_DISCONNECT 2
60 #define BLE_L2CAP_SIG_PROC_OP_MAX 3
61
62 struct ble_l2cap_sig_proc {
63 STAILQ_ENTRY(ble_l2cap_sig_proc) next;
64
65 ble_npl_time_t exp_os_ticks;
66 uint16_t conn_handle;
67 uint8_t op;
68 uint8_t id;
69
70 union {
71 struct {
72 ble_l2cap_sig_update_fn *cb;
73 void *cb_arg;
74 } update;
75 struct {
76 struct ble_l2cap_chan *chan;
77 } connect;
78 struct {
79 struct ble_l2cap_chan *chan;
80 } disconnect;
81 };
82 };
83
84 STAILQ_HEAD(ble_l2cap_sig_proc_list, ble_l2cap_sig_proc);
85
86 static struct ble_l2cap_sig_proc_list ble_l2cap_sig_procs;
87
88 typedef int ble_l2cap_sig_rx_fn(uint16_t conn_handle,
89 struct ble_l2cap_sig_hdr *hdr,
90 struct os_mbuf **om);
91
92 static ble_l2cap_sig_rx_fn ble_l2cap_sig_rx_noop;
93 static ble_l2cap_sig_rx_fn ble_l2cap_sig_update_req_rx;
94 static ble_l2cap_sig_rx_fn ble_l2cap_sig_update_rsp_rx;
95 static ble_l2cap_sig_rx_fn ble_l2cap_sig_rx_reject;
96
97 #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
98 static ble_l2cap_sig_rx_fn ble_l2cap_sig_coc_req_rx;
99 static ble_l2cap_sig_rx_fn ble_l2cap_sig_coc_rsp_rx;
100 static ble_l2cap_sig_rx_fn ble_l2cap_sig_disc_rsp_rx;
101 static ble_l2cap_sig_rx_fn ble_l2cap_sig_disc_req_rx;
102 static ble_l2cap_sig_rx_fn ble_l2cap_sig_le_credits_rx;
103 #else
104 #define ble_l2cap_sig_coc_req_rx ble_l2cap_sig_rx_noop
105 #define ble_l2cap_sig_coc_rsp_rx ble_l2cap_sig_rx_noop
106 #define ble_l2cap_sig_disc_rsp_rx ble_l2cap_sig_rx_noop
107 #define ble_l2cap_sig_disc_req_rx ble_l2cap_sig_rx_noop
108 #define ble_l2cap_sig_le_credits_rx ble_l2cap_sig_rx_noop
109 #endif
110
111 static ble_l2cap_sig_rx_fn * const ble_l2cap_sig_dispatch[] = {
112 [BLE_L2CAP_SIG_OP_REJECT] = ble_l2cap_sig_rx_reject,
113 [BLE_L2CAP_SIG_OP_CONNECT_RSP] = ble_l2cap_sig_rx_noop,
114 [BLE_L2CAP_SIG_OP_CONFIG_RSP] = ble_l2cap_sig_rx_noop,
115 [BLE_L2CAP_SIG_OP_DISCONN_REQ] = ble_l2cap_sig_disc_req_rx,
116 [BLE_L2CAP_SIG_OP_DISCONN_RSP] = ble_l2cap_sig_disc_rsp_rx,
117 [BLE_L2CAP_SIG_OP_ECHO_RSP] = ble_l2cap_sig_rx_noop,
118 [BLE_L2CAP_SIG_OP_INFO_RSP] = ble_l2cap_sig_rx_noop,
119 [BLE_L2CAP_SIG_OP_CREATE_CHAN_RSP] = ble_l2cap_sig_rx_noop,
120 [BLE_L2CAP_SIG_OP_MOVE_CHAN_RSP] = ble_l2cap_sig_rx_noop,
121 [BLE_L2CAP_SIG_OP_MOVE_CHAN_CONF_RSP] = ble_l2cap_sig_rx_noop,
122 [BLE_L2CAP_SIG_OP_UPDATE_REQ] = ble_l2cap_sig_update_req_rx,
123 [BLE_L2CAP_SIG_OP_UPDATE_RSP] = ble_l2cap_sig_update_rsp_rx,
124 [BLE_L2CAP_SIG_OP_CREDIT_CONNECT_REQ] = ble_l2cap_sig_coc_req_rx,
125 [BLE_L2CAP_SIG_OP_CREDIT_CONNECT_RSP] = ble_l2cap_sig_coc_rsp_rx,
126 [BLE_L2CAP_SIG_OP_FLOW_CTRL_CREDIT] = ble_l2cap_sig_le_credits_rx,
127 };
128
129 static uint8_t ble_l2cap_sig_cur_id;
130
131 static os_membuf_t ble_l2cap_sig_proc_mem[
132 OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_L2CAP_SIG_MAX_PROCS),
133 sizeof (struct ble_l2cap_sig_proc))
134 ];
135
136 static struct os_mempool ble_l2cap_sig_proc_pool;
137
138 /*****************************************************************************
139 * $debug *
140 *****************************************************************************/
141
142 static void
ble_l2cap_sig_dbg_assert_proc_not_inserted(struct ble_l2cap_sig_proc * proc)143 ble_l2cap_sig_dbg_assert_proc_not_inserted(struct ble_l2cap_sig_proc *proc)
144 {
145 #if MYNEWT_VAL(BLE_HS_DEBUG)
146 struct ble_l2cap_sig_proc *cur;
147
148 STAILQ_FOREACH(cur, &ble_l2cap_sig_procs, next) {
149 BLE_HS_DBG_ASSERT(cur != proc);
150 }
151 #endif
152 }
153
154 /*****************************************************************************
155 * $misc *
156 *****************************************************************************/
157
158 static uint8_t
ble_l2cap_sig_next_id(void)159 ble_l2cap_sig_next_id(void)
160 {
161 ble_l2cap_sig_cur_id++;
162 if (ble_l2cap_sig_cur_id == 0) {
163 /* An ID of 0 is illegal. */
164 ble_l2cap_sig_cur_id = 1;
165 }
166
167 return ble_l2cap_sig_cur_id;
168 }
169
170 static ble_l2cap_sig_rx_fn *
ble_l2cap_sig_dispatch_get(uint8_t op)171 ble_l2cap_sig_dispatch_get(uint8_t op)
172 {
173 if (op >= BLE_L2CAP_SIG_OP_MAX) {
174 return NULL;
175 }
176
177 return ble_l2cap_sig_dispatch[op];
178 }
179
180 /**
181 * Allocates a proc entry.
182 *
183 * @return An entry on success; null on failure.
184 */
185 static struct ble_l2cap_sig_proc *
ble_l2cap_sig_proc_alloc(void)186 ble_l2cap_sig_proc_alloc(void)
187 {
188 struct ble_l2cap_sig_proc *proc;
189
190 proc = os_memblock_get(&ble_l2cap_sig_proc_pool);
191 if (proc != NULL) {
192 memset(proc, 0, sizeof *proc);
193 }
194
195 return proc;
196 }
197
198 /**
199 * Frees the specified proc entry. No-op if passed a null pointer.
200 */
201 static void
ble_l2cap_sig_proc_free(struct ble_l2cap_sig_proc * proc)202 ble_l2cap_sig_proc_free(struct ble_l2cap_sig_proc *proc)
203 {
204 int rc;
205
206 if (proc != NULL) {
207 ble_l2cap_sig_dbg_assert_proc_not_inserted(proc);
208
209 #if MYNEWT_VAL(BLE_HS_DEBUG)
210 memset(proc, 0xff, sizeof *proc);
211 #endif
212 rc = os_memblock_put(&ble_l2cap_sig_proc_pool, proc);
213 BLE_HS_DBG_ASSERT_EVAL(rc == 0);
214 }
215 }
216
217 static void
ble_l2cap_sig_proc_insert(struct ble_l2cap_sig_proc * proc)218 ble_l2cap_sig_proc_insert(struct ble_l2cap_sig_proc *proc)
219 {
220 ble_l2cap_sig_dbg_assert_proc_not_inserted(proc);
221
222 ble_hs_lock();
223 STAILQ_INSERT_HEAD(&ble_l2cap_sig_procs, proc, next);
224 ble_hs_unlock();
225 }
226
227 /**
228 * Tests if a proc entry fits the specified criteria.
229 *
230 * @param proc The procedure to test.
231 * @param conn_handle The connection handle to match against.
232 * @param op The op code to match against/
233 * @param id The identifier to match against.
234 * 0=Ignore this criterion.
235 *
236 * @return 1 if the proc matches; 0 otherwise.
237 */
238 static int
ble_l2cap_sig_proc_matches(struct ble_l2cap_sig_proc * proc,uint16_t conn_handle,uint8_t op,uint8_t id)239 ble_l2cap_sig_proc_matches(struct ble_l2cap_sig_proc *proc,
240 uint16_t conn_handle, uint8_t op, uint8_t id)
241 {
242 if (conn_handle != proc->conn_handle) {
243 return 0;
244 }
245
246 if (op != proc->op) {
247 return 0;
248 }
249
250 if (id != 0 && id != proc->id) {
251 return 0;
252 }
253
254 return 1;
255 }
256
257 /**
258 * Searches the main proc list for an "expecting" entry whose connection handle
259 * and op code match those specified. If a matching entry is found, it is
260 * removed from the list and returned.
261 *
262 * @param conn_handle The connection handle to match against.
263 * @param op The op code to match against.
264 * @param identifier The identifier to match against;
265 * 0=ignore this criterion.
266 *
267 * @return The matching proc entry on success;
268 * null on failure.
269 */
270 static struct ble_l2cap_sig_proc *
ble_l2cap_sig_proc_extract(uint16_t conn_handle,uint8_t op,uint8_t identifier)271 ble_l2cap_sig_proc_extract(uint16_t conn_handle, uint8_t op,
272 uint8_t identifier)
273 {
274 struct ble_l2cap_sig_proc *proc;
275 struct ble_l2cap_sig_proc *prev;
276
277 ble_hs_lock();
278
279 prev = NULL;
280 STAILQ_FOREACH(proc, &ble_l2cap_sig_procs, next) {
281 if (ble_l2cap_sig_proc_matches(proc, conn_handle, op, identifier)) {
282 if (prev == NULL) {
283 STAILQ_REMOVE_HEAD(&ble_l2cap_sig_procs, next);
284 } else {
285 STAILQ_REMOVE_AFTER(&ble_l2cap_sig_procs, prev, next);
286 }
287 break;
288 }
289 }
290
291 ble_hs_unlock();
292
293 return proc;
294 }
295
296 static int
ble_l2cap_sig_rx_noop(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)297 ble_l2cap_sig_rx_noop(uint16_t conn_handle,
298 struct ble_l2cap_sig_hdr *hdr,
299 struct os_mbuf **om)
300 {
301 return BLE_HS_ENOTSUP;
302 }
303
304 static void
ble_l2cap_sig_proc_set_timer(struct ble_l2cap_sig_proc * proc)305 ble_l2cap_sig_proc_set_timer(struct ble_l2cap_sig_proc *proc)
306 {
307 proc->exp_os_ticks = ble_npl_time_get() +
308 ble_npl_time_ms_to_ticks32(BLE_L2CAP_SIG_UNRESPONSIVE_TIMEOUT);
309 ble_hs_timer_resched();
310 }
311
312 static void
ble_l2cap_sig_process_status(struct ble_l2cap_sig_proc * proc,int status)313 ble_l2cap_sig_process_status(struct ble_l2cap_sig_proc *proc, int status)
314 {
315 if (status == 0) {
316 ble_l2cap_sig_proc_set_timer(proc);
317 ble_l2cap_sig_proc_insert(proc);
318 } else {
319 ble_l2cap_sig_proc_free(proc);
320 }
321 }
322
323 /*****************************************************************************
324 * $update *
325 *****************************************************************************/
326
327 static void
ble_l2cap_sig_update_call_cb(struct ble_l2cap_sig_proc * proc,int status)328 ble_l2cap_sig_update_call_cb(struct ble_l2cap_sig_proc *proc, int status)
329 {
330 BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
331
332 if (status != 0) {
333 STATS_INC(ble_l2cap_stats, update_fail);
334 }
335
336 if (proc->update.cb != NULL) {
337 proc->update.cb(proc->conn_handle, status, proc->update.cb_arg);
338 }
339 }
340
341 int
ble_l2cap_sig_update_req_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)342 ble_l2cap_sig_update_req_rx(uint16_t conn_handle,
343 struct ble_l2cap_sig_hdr *hdr,
344 struct os_mbuf **om)
345 {
346 struct ble_l2cap_sig_update_req *req;
347 struct os_mbuf *txom;
348 struct ble_l2cap_sig_update_rsp *rsp;
349 struct ble_gap_upd_params params;
350 ble_hs_conn_flags_t conn_flags;
351 uint16_t l2cap_result;
352 int sig_err;
353 int rc;
354
355 l2cap_result = 0; /* Silence spurious gcc warning. */
356
357 rc = ble_hs_mbuf_pullup_base(om, BLE_L2CAP_SIG_UPDATE_REQ_SZ);
358 if (rc != 0) {
359 return rc;
360 }
361
362 rc = ble_hs_atomic_conn_flags(conn_handle, &conn_flags);
363 if (rc != 0) {
364 return rc;
365 }
366
367 /* Only a master can process an update request. */
368 sig_err = !(conn_flags & BLE_HS_CONN_F_MASTER);
369 if (sig_err) {
370 return BLE_HS_EREJECT;
371 }
372
373 req = (struct ble_l2cap_sig_update_req *)(*om)->om_data;
374
375 params.itvl_min = le16toh(req->itvl_min);
376 params.itvl_max = le16toh(req->itvl_max);
377 params.latency = le16toh(req->slave_latency);
378 params.supervision_timeout = le16toh(req->timeout_multiplier);
379 params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN;
380 params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN;
381
382 /* Ask application if slave's connection parameters are acceptable. */
383 rc = ble_gap_rx_l2cap_update_req(conn_handle, ¶ms);
384 if (rc == 0) {
385 /* Application agrees to accept parameters; schedule update. */
386 rc = ble_gap_update_params(conn_handle, ¶ms);
387 }
388
389 if (rc == 0) {
390 l2cap_result = BLE_L2CAP_SIG_UPDATE_RSP_RESULT_ACCEPT;
391 } else {
392 l2cap_result = BLE_L2CAP_SIG_UPDATE_RSP_RESULT_REJECT;
393 }
394
395 rsp = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_UPDATE_RSP, hdr->identifier,
396 sizeof(*rsp), &txom);
397 if (!rsp) {
398 /* No memory for response, lest allow to timeout on remote side */
399 return 0;
400 }
401
402 rsp->result = htole16(l2cap_result);
403
404 /* Send L2CAP response. */
405 ble_l2cap_sig_tx(conn_handle, txom);
406
407 return 0;
408 }
409
410 static int
ble_l2cap_sig_update_rsp_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)411 ble_l2cap_sig_update_rsp_rx(uint16_t conn_handle,
412 struct ble_l2cap_sig_hdr *hdr,
413 struct os_mbuf **om)
414 {
415 struct ble_l2cap_sig_update_rsp *rsp;
416 struct ble_l2cap_sig_proc *proc;
417 int cb_status;
418 int rc;
419
420 proc = ble_l2cap_sig_proc_extract(conn_handle,
421 BLE_L2CAP_SIG_PROC_OP_UPDATE,
422 hdr->identifier);
423 if (proc == NULL) {
424 return 0;
425 }
426
427 rc = ble_hs_mbuf_pullup_base(om, BLE_L2CAP_SIG_UPDATE_RSP_SZ);
428 if (rc != 0) {
429 cb_status = rc;
430 goto done;
431 }
432
433 rsp = (struct ble_l2cap_sig_update_rsp *)(*om)->om_data;
434
435 switch (le16toh(rsp->result)) {
436 case BLE_L2CAP_SIG_UPDATE_RSP_RESULT_ACCEPT:
437 cb_status = 0;
438 rc = 0;
439 break;
440
441 case BLE_L2CAP_SIG_UPDATE_RSP_RESULT_REJECT:
442 cb_status = BLE_HS_EREJECT;
443 rc = 0;
444 break;
445
446 default:
447 cb_status = BLE_HS_EBADDATA;
448 rc = 0;
449 break;
450 }
451
452 done:
453 ble_l2cap_sig_update_call_cb(proc, cb_status);
454 ble_l2cap_sig_proc_free(proc);
455 return rc;
456 }
457
458 int
ble_l2cap_sig_update(uint16_t conn_handle,struct ble_l2cap_sig_update_params * params,ble_l2cap_sig_update_fn * cb,void * cb_arg)459 ble_l2cap_sig_update(uint16_t conn_handle,
460 struct ble_l2cap_sig_update_params *params,
461 ble_l2cap_sig_update_fn *cb, void *cb_arg)
462 {
463 struct os_mbuf *txom;
464 struct ble_l2cap_sig_update_req *req;
465 struct ble_l2cap_sig_proc *proc;
466 struct ble_l2cap_chan *chan;
467 struct ble_hs_conn *conn;
468 int master;
469 int rc;
470
471 proc = NULL;
472
473 STATS_INC(ble_l2cap_stats, update_init);
474
475 ble_hs_lock();
476 ble_hs_misc_conn_chan_find_reqd(conn_handle, BLE_L2CAP_CID_SIG,
477 &conn, &chan);
478 master = conn->bhc_flags & BLE_HS_CONN_F_MASTER;
479 ble_hs_unlock();
480
481 if (master) {
482 /* Only the slave can initiate the L2CAP connection update
483 * procedure.
484 */
485 rc = BLE_HS_EINVAL;
486 goto done;
487 }
488
489 proc = ble_l2cap_sig_proc_alloc();
490 if (proc == NULL) {
491 STATS_INC(ble_l2cap_stats, update_fail);
492 rc = BLE_HS_ENOMEM;
493 goto done;
494 }
495
496 proc->op = BLE_L2CAP_SIG_PROC_OP_UPDATE;
497 proc->id = ble_l2cap_sig_next_id();
498 proc->conn_handle = conn_handle;
499 proc->update.cb = cb;
500 proc->update.cb_arg = cb_arg;
501
502 req = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_UPDATE_REQ, proc->id,
503 sizeof(*req), &txom);
504 if (!req) {
505 STATS_INC(ble_l2cap_stats, update_fail);
506 rc = BLE_HS_ENOMEM;
507 goto done;
508 }
509
510 req->itvl_min = htole16(params->itvl_min);
511 req->itvl_max = htole16(params->itvl_max);
512 req->slave_latency = htole16(params->slave_latency);
513 req->timeout_multiplier = htole16(params->timeout_multiplier);
514
515 rc = ble_l2cap_sig_tx(conn_handle, txom);
516
517 done:
518 ble_l2cap_sig_process_status(proc, rc);
519 return rc;
520 }
521
522 /*****************************************************************************
523 * $connect *
524 *****************************************************************************/
525
526 #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
527
528 static int
ble_l2cap_sig_coc_err2ble_hs_err(uint16_t l2cap_coc_err)529 ble_l2cap_sig_coc_err2ble_hs_err(uint16_t l2cap_coc_err)
530 {
531 switch (l2cap_coc_err) {
532 case BLE_L2CAP_COC_ERR_CONNECTION_SUCCESS:
533 return 0;
534 case BLE_L2CAP_COC_ERR_UNKNOWN_LE_PSM:
535 return BLE_HS_ENOTSUP;
536 case BLE_L2CAP_COC_ERR_NO_RESOURCES:
537 return BLE_HS_ENOMEM;
538 case BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHEN:
539 return BLE_HS_EAUTHEN;
540 case BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHOR:
541 return BLE_HS_EAUTHOR;
542 case BLE_L2CAP_COC_ERR_INSUFFICIENT_KEY_SZ:
543 return BLE_HS_EENCRYPT_KEY_SZ;
544 case BLE_L2CAP_COC_ERR_INSUFFICIENT_ENC:
545 return BLE_HS_EENCRYPT;
546 case BLE_L2CAP_COC_ERR_INVALID_SOURCE_CID:
547 return BLE_HS_EREJECT;
548 case BLE_L2CAP_COC_ERR_SOURCE_CID_ALREADY_USED:
549 return BLE_HS_EALREADY;
550 case BLE_L2CAP_COC_ERR_UNACCEPTABLE_PARAMETERS:
551 return BLE_HS_EINVAL;
552 default:
553 return BLE_HS_EUNKNOWN;
554 }
555 }
556
557 static int
ble_l2cap_sig_ble_hs_err2coc_err(uint16_t ble_hs_err)558 ble_l2cap_sig_ble_hs_err2coc_err(uint16_t ble_hs_err)
559 {
560 switch (ble_hs_err) {
561 case BLE_HS_ENOTSUP:
562 return BLE_L2CAP_COC_ERR_UNKNOWN_LE_PSM;
563 case BLE_HS_ENOMEM:
564 return BLE_L2CAP_COC_ERR_NO_RESOURCES;
565 case BLE_HS_EAUTHEN:
566 return BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHEN;
567 case BLE_HS_EAUTHOR:
568 return BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHOR;
569 case BLE_HS_EENCRYPT:
570 return BLE_L2CAP_COC_ERR_INSUFFICIENT_ENC;
571 case BLE_HS_EENCRYPT_KEY_SZ:
572 return BLE_L2CAP_COC_ERR_INSUFFICIENT_KEY_SZ;
573 case BLE_HS_EINVAL:
574 return BLE_L2CAP_COC_ERR_UNACCEPTABLE_PARAMETERS;
575 default:
576 return BLE_L2CAP_COC_ERR_NO_RESOURCES;
577 }
578 }
579
580 static void
ble_l2cap_event_coc_connected(struct ble_l2cap_chan * chan,uint16_t status)581 ble_l2cap_event_coc_connected(struct ble_l2cap_chan *chan, uint16_t status)
582 {
583 struct ble_l2cap_event event = { };
584
585 event.type = BLE_L2CAP_EVENT_COC_CONNECTED;
586 event.connect.conn_handle = chan->conn_handle;
587 event.connect.chan = chan;
588 event.connect.status = status;
589
590 chan->cb(&event, chan->cb_arg);
591 }
592
593 static int
ble_l2cap_event_coc_accept(struct ble_l2cap_chan * chan,uint16_t peer_sdu_size)594 ble_l2cap_event_coc_accept(struct ble_l2cap_chan *chan, uint16_t peer_sdu_size)
595 {
596 struct ble_l2cap_event event = { };
597
598 event.type = BLE_L2CAP_EVENT_COC_ACCEPT;
599 event.accept.chan = chan;
600 event.accept.conn_handle = chan->conn_handle;
601 event.accept.peer_sdu_size = peer_sdu_size;
602
603 return chan->cb(&event, chan->cb_arg);
604 }
605
606 static void
ble_l2cap_sig_coc_connect_cb(struct ble_l2cap_sig_proc * proc,int status)607 ble_l2cap_sig_coc_connect_cb(struct ble_l2cap_sig_proc *proc, int status)
608 {
609 struct ble_l2cap_chan *chan;
610
611 if (!proc) {
612 return;
613 }
614
615 chan = proc->connect.chan;
616 if (!chan || !chan->cb) {
617 return;
618 }
619
620 ble_l2cap_event_coc_connected(chan, status);
621
622 if (status) {
623 /* Normally in channel free we send disconnected event to application.
624 * However in case on error during creation connection we send connected
625 * event with error status. To avoid additional disconnected event lets
626 * clear callbacks since we don't needed it anymore.*/
627 chan->cb = NULL;
628 ble_l2cap_chan_free(chan);
629 }
630 }
631
632 static int
ble_l2cap_sig_coc_req_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)633 ble_l2cap_sig_coc_req_rx(uint16_t conn_handle, struct ble_l2cap_sig_hdr *hdr,
634 struct os_mbuf **om)
635 {
636 int rc;
637 struct ble_l2cap_sig_le_con_req *req;
638 struct os_mbuf *txom;
639 struct ble_l2cap_sig_le_con_rsp *rsp;
640 struct ble_l2cap_chan *chan = NULL;
641 struct ble_hs_conn *conn;
642 uint16_t scid;
643
644 rc = ble_hs_mbuf_pullup_base(om, sizeof(req));
645 if (rc != 0) {
646 return rc;
647 }
648
649 rsp = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_CREDIT_CONNECT_RSP,
650 hdr->identifier, sizeof(*rsp), &txom);
651 if (!rsp) {
652 /* Well, nothing smart we can do if there is no memory for response.
653 * Remote will timeout.
654 */
655 return 0;
656 }
657
658 memset(rsp, 0, sizeof(*rsp));
659
660 req = (struct ble_l2cap_sig_le_con_req *)(*om)->om_data;
661
662 ble_hs_lock();
663 conn = ble_hs_conn_find_assert(conn_handle);
664
665 /* Verify CID. Note, scid in the request is dcid for out local channel */
666 scid = le16toh(req->scid);
667 if (scid < BLE_L2CAP_COC_CID_START || scid > BLE_L2CAP_COC_CID_END) {
668 rsp->result = htole16(BLE_L2CAP_COC_ERR_INVALID_SOURCE_CID);
669 ble_hs_unlock();
670 goto failed;
671 }
672
673 chan = ble_hs_conn_chan_find_by_dcid(conn, scid);
674 if (chan) {
675 rsp->result = htole16(BLE_L2CAP_COC_ERR_SOURCE_CID_ALREADY_USED);
676 ble_hs_unlock();
677 goto failed;
678 }
679
680 rc = ble_l2cap_coc_create_srv_chan(conn_handle, le16toh(req->psm), &chan);
681 if (rc != 0) {
682 uint16_t coc_err = ble_l2cap_sig_ble_hs_err2coc_err(rc);
683 rsp->result = htole16(coc_err);
684 ble_hs_unlock();
685 goto failed;
686 }
687
688 /* Fill up remote configuration. Note MPS is the L2CAP MTU*/
689 chan->dcid = scid;
690 chan->peer_mtu = le16toh(req->mps);
691 chan->coc_tx.credits = le16toh(req->credits);
692 chan->coc_tx.mtu = le16toh(req->mtu);
693
694 ble_hs_unlock();
695
696 rc = ble_l2cap_event_coc_accept(chan, le16toh(req->mtu));
697 if (rc != 0) {
698 uint16_t coc_err = ble_l2cap_sig_ble_hs_err2coc_err(rc);
699
700 /* Make sure we do not send disconnect event when removing channel */
701 chan->cb = NULL;
702
703 ble_l2cap_chan_free(chan);
704 rsp->result = htole16(coc_err);
705 goto failed;
706 }
707
708 rsp->dcid = htole16(chan->scid);
709 rsp->credits = htole16(chan->coc_rx.credits);
710 rsp->mps = htole16(chan->my_mtu);
711 rsp->mtu = htole16(chan->coc_rx.mtu);
712 rsp->result = htole16(BLE_L2CAP_COC_ERR_CONNECTION_SUCCESS);
713
714 rc = ble_l2cap_sig_tx(conn_handle, txom);
715 if (rc == 0) {
716 /* Response sent out with a success. We are connected now*/
717 ble_hs_lock();
718 ble_hs_conn_chan_insert(conn, chan);
719 ble_hs_unlock();
720 } else {
721 ble_l2cap_chan_free(chan);
722 }
723
724 /* Notify user about connection status */
725 ble_l2cap_event_coc_connected(chan, rc);
726
727 return 0;
728
729 failed:
730 ble_l2cap_sig_tx(conn_handle, txom);
731 return 0;
732 }
733
734 static int
ble_l2cap_sig_coc_rsp_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)735 ble_l2cap_sig_coc_rsp_rx(uint16_t conn_handle, struct ble_l2cap_sig_hdr *hdr,
736 struct os_mbuf **om)
737 {
738 struct ble_l2cap_sig_proc *proc;
739 struct ble_l2cap_sig_le_con_rsp *rsp;
740 struct ble_l2cap_chan *chan;
741 struct ble_hs_conn *conn;
742 int rc;
743
744 #if !BLE_MONITOR
745 BLE_HS_LOG(DEBUG, "L2CAP LE COC connection response received\n");
746 #endif
747
748 proc = ble_l2cap_sig_proc_extract(conn_handle,
749 BLE_L2CAP_SIG_PROC_OP_CONNECT,
750 hdr->identifier);
751 if (!proc) {
752 return 0;
753 }
754
755 rc = ble_hs_mbuf_pullup_base(om, sizeof(*rsp));
756 if (rc != 0) {
757 goto done;
758 }
759
760 rsp = (struct ble_l2cap_sig_le_con_rsp *)(*om)->om_data;
761
762 chan = proc->connect.chan;
763
764 if (rsp->result) {
765 rc = ble_l2cap_sig_coc_err2ble_hs_err(le16toh(rsp->result));
766 goto done;
767 }
768
769 /* Fill up remote configuration
770 * Note MPS is the L2CAP MTU
771 */
772 chan->peer_mtu = le16toh(rsp->mps);
773 chan->dcid = le16toh(rsp->dcid);
774 chan->coc_tx.mtu = le16toh(rsp->mtu);
775 chan->coc_tx.credits = le16toh(rsp->credits);
776
777 ble_hs_lock();
778 conn = ble_hs_conn_find(conn_handle);
779 assert(conn != NULL);
780 ble_hs_conn_chan_insert(conn, chan);
781 ble_hs_unlock();
782
783 rc = 0;
784
785 done:
786 ble_l2cap_sig_coc_connect_cb(proc, rc);
787 ble_l2cap_sig_proc_free(proc);
788
789 /* Silently ignore errors as this is response signal */
790 return 0;
791 }
792
793 int
ble_l2cap_sig_coc_connect(uint16_t conn_handle,uint16_t psm,uint16_t mtu,struct os_mbuf * sdu_rx,ble_l2cap_event_fn * cb,void * cb_arg)794 ble_l2cap_sig_coc_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu,
795 struct os_mbuf *sdu_rx,
796 ble_l2cap_event_fn *cb, void *cb_arg)
797 {
798 struct ble_hs_conn *conn;
799 struct ble_l2cap_sig_proc *proc;
800 struct os_mbuf *txom;
801 struct ble_l2cap_sig_le_con_req *req;
802 struct ble_l2cap_chan *chan = NULL;
803 int rc;
804
805 if (!sdu_rx || !cb) {
806 return BLE_HS_EINVAL;
807 }
808
809 ble_hs_lock();
810 conn = ble_hs_conn_find(conn_handle);
811
812 if (!conn) {
813 ble_hs_unlock();
814 return BLE_HS_ENOTCONN;
815 }
816
817 chan = ble_l2cap_coc_chan_alloc(conn_handle, psm, mtu, sdu_rx, cb, cb_arg);
818 if (!chan) {
819 ble_hs_unlock();
820 return BLE_HS_ENOMEM;
821 }
822
823 proc = ble_l2cap_sig_proc_alloc();
824 if (!proc) {
825 ble_l2cap_chan_free(chan);
826 ble_hs_unlock();
827 return BLE_HS_ENOMEM;
828 }
829
830 proc->op = BLE_L2CAP_SIG_PROC_OP_CONNECT;
831 proc->id = ble_l2cap_sig_next_id();
832 proc->conn_handle = conn_handle;
833 proc->connect.chan = chan;
834
835 req = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_CREDIT_CONNECT_REQ, proc->id,
836 sizeof(*req), &txom);
837 if (!req) {
838 ble_l2cap_chan_free(chan);
839 ble_hs_unlock();
840 return BLE_HS_ENOMEM;
841 }
842
843 req->psm = htole16(psm);
844 req->scid = htole16(chan->scid);
845 req->mtu = htole16(chan->coc_rx.mtu);
846 req->mps = htole16(chan->my_mtu);
847 req->credits = htole16(chan->coc_rx.credits);
848
849 ble_hs_unlock();
850
851 rc = ble_l2cap_sig_tx(proc->conn_handle, txom);
852 if (rc != 0) {
853 ble_l2cap_chan_free(chan);
854 }
855
856 ble_l2cap_sig_process_status(proc, rc);
857
858 return rc;
859 }
860
861 /*****************************************************************************
862 * $disconnect *
863 *****************************************************************************/
864
865 static int
ble_l2cap_sig_disc_req_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)866 ble_l2cap_sig_disc_req_rx(uint16_t conn_handle, struct ble_l2cap_sig_hdr *hdr,
867 struct os_mbuf **om)
868 {
869 struct ble_l2cap_sig_disc_req *req;
870 struct os_mbuf *txom;
871 struct ble_l2cap_sig_disc_rsp *rsp;
872 struct ble_l2cap_chan *chan;
873 struct ble_hs_conn *conn;
874 int rc;
875
876 rc = ble_hs_mbuf_pullup_base(om, sizeof(*req));
877 if (rc != 0) {
878 return rc;
879 }
880
881 rsp = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_DISCONN_RSP, hdr->identifier,
882 sizeof(*rsp), &txom);
883 if (!rsp) {
884 /* Well, nothing smart we can do if there is no memory for response.
885 * Remote will timeout.
886 */
887 return 0;
888 }
889
890 ble_hs_lock();
891 conn = ble_hs_conn_find_assert(conn_handle);
892
893 req = (struct ble_l2cap_sig_disc_req *) (*om)->om_data;
894
895 /* Let's find matching channel. Note that destination CID in the request
896 * is from peer perspective. It is source CID from nimble perspective
897 */
898 chan = ble_hs_conn_chan_find_by_scid(conn, le16toh(req->dcid));
899 if (!chan || (le16toh(req->scid) != chan->dcid)) {
900 os_mbuf_free_chain(txom);
901 ble_hs_unlock();
902 return 0;
903 }
904
905 /* Note that in the response destination CID is form peer perspective and
906 * it is source CID from nimble perspective.
907 */
908 rsp->dcid = htole16(chan->scid);
909 rsp->scid = htole16(chan->dcid);
910
911 ble_hs_conn_delete_chan(conn, chan);
912 ble_hs_unlock();
913
914 ble_l2cap_sig_tx(conn_handle, txom);
915 return 0;
916 }
917
918 static void
ble_l2cap_sig_coc_disconnect_cb(struct ble_l2cap_sig_proc * proc,int status)919 ble_l2cap_sig_coc_disconnect_cb(struct ble_l2cap_sig_proc *proc, int status)
920 {
921 struct ble_l2cap_chan *chan;
922 struct ble_l2cap_event event;
923 struct ble_hs_conn *conn;
924
925 if (!proc) {
926 return;
927 }
928
929 memset(&event, 0, sizeof(event));
930 chan = proc->disconnect.chan;
931
932 if (!chan) {
933 return;
934 }
935
936 if (!chan->cb) {
937 goto done;
938 }
939
940 done:
941 ble_hs_lock();
942 conn = ble_hs_conn_find(chan->conn_handle);
943 if (conn) {
944 ble_hs_conn_delete_chan(conn, chan);
945 } else {
946 ble_l2cap_chan_free(chan);
947 }
948 ble_hs_unlock();
949 }
950
951 static int
ble_l2cap_sig_disc_rsp_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)952 ble_l2cap_sig_disc_rsp_rx(uint16_t conn_handle, struct ble_l2cap_sig_hdr *hdr,
953 struct os_mbuf **om)
954 {
955 struct ble_l2cap_sig_disc_rsp *rsp;
956 struct ble_l2cap_sig_proc *proc;
957 struct ble_l2cap_chan *chan;
958 int rc;
959
960 proc = ble_l2cap_sig_proc_extract(conn_handle,
961 BLE_L2CAP_SIG_PROC_OP_DISCONNECT,
962 hdr->identifier);
963 if (!proc) {
964 return 0;
965 }
966
967 rc = ble_hs_mbuf_pullup_base(om, sizeof(*rsp));
968 if (rc != 0) {
969 goto done;
970 }
971
972 chan = proc->disconnect.chan;
973 if (!chan) {
974 goto done;
975 }
976
977 rsp = (struct ble_l2cap_sig_disc_rsp *)(*om)->om_data;
978 if (chan->dcid != le16toh(rsp->dcid) || chan->scid != le16toh(rsp->scid)) {
979 /* This response is incorrect, lets wait for timeout */
980 ble_l2cap_sig_process_status(proc, 0);
981 return 0;
982 }
983
984 ble_l2cap_sig_coc_disconnect_cb(proc, rc);
985
986 done:
987 ble_l2cap_sig_proc_free(proc);
988 return 0;
989 }
990
991 int
ble_l2cap_sig_disconnect(struct ble_l2cap_chan * chan)992 ble_l2cap_sig_disconnect(struct ble_l2cap_chan *chan)
993 {
994 struct os_mbuf *txom;
995 struct ble_l2cap_sig_disc_req *req;
996 struct ble_l2cap_sig_proc *proc;
997 int rc;
998
999 proc = ble_l2cap_sig_proc_alloc();
1000 if (proc == NULL) {
1001 return BLE_HS_ENOMEM;
1002 }
1003
1004 proc->op = BLE_L2CAP_SIG_PROC_OP_DISCONNECT;
1005 proc->id = ble_l2cap_sig_next_id();
1006 proc->conn_handle = chan->conn_handle;
1007 proc->disconnect.chan = chan;
1008
1009 req = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_DISCONN_REQ, proc->id,
1010 sizeof(*req), &txom);
1011 if (!req) {
1012 rc = BLE_HS_ENOMEM;
1013 goto done;
1014 }
1015
1016 req->dcid = htole16(chan->dcid);
1017 req->scid = htole16(chan->scid);
1018
1019 rc = ble_l2cap_sig_tx(proc->conn_handle, txom);
1020
1021 done:
1022 ble_l2cap_sig_process_status(proc, rc);
1023
1024 return rc;
1025 }
1026
1027 static int
ble_l2cap_sig_le_credits_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)1028 ble_l2cap_sig_le_credits_rx(uint16_t conn_handle, struct ble_l2cap_sig_hdr *hdr,
1029 struct os_mbuf **om)
1030 {
1031 struct ble_l2cap_sig_le_credits *req;
1032 int rc;
1033
1034 rc = ble_hs_mbuf_pullup_base(om, sizeof(*req));
1035 if (rc != 0) {
1036 return 0;
1037 }
1038
1039 req = (struct ble_l2cap_sig_le_credits *) (*om)->om_data;
1040
1041 /* Ignore when peer sends zero credits */
1042 if (req->credits == 0) {
1043 return 0;
1044 }
1045
1046 ble_l2cap_coc_le_credits_update(conn_handle, le16toh(req->scid),
1047 le16toh(req->credits));
1048
1049 return 0;
1050 }
1051
1052 int
ble_l2cap_sig_le_credits(uint16_t conn_handle,uint16_t scid,uint16_t credits)1053 ble_l2cap_sig_le_credits(uint16_t conn_handle, uint16_t scid, uint16_t credits)
1054 {
1055 struct ble_l2cap_sig_le_credits *cmd;
1056 struct os_mbuf *txom;
1057
1058 cmd = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_FLOW_CTRL_CREDIT,
1059 ble_l2cap_sig_next_id(), sizeof(*cmd), &txom);
1060
1061 if (!cmd) {
1062 return BLE_HS_ENOMEM;
1063 }
1064
1065 cmd->scid = htole16(scid);
1066 cmd->credits = htole16(credits);
1067
1068 return ble_l2cap_sig_tx(conn_handle, txom);
1069 }
1070 #endif
1071
1072 static int
ble_l2cap_sig_rx_reject(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)1073 ble_l2cap_sig_rx_reject(uint16_t conn_handle,
1074 struct ble_l2cap_sig_hdr *hdr,
1075 struct os_mbuf **om)
1076 {
1077 struct ble_l2cap_sig_proc *proc;
1078 proc = ble_l2cap_sig_proc_extract(conn_handle,
1079 BLE_L2CAP_SIG_PROC_OP_CONNECT,
1080 hdr->identifier);
1081 if (!proc) {
1082 return 0;
1083 }
1084
1085 switch (proc->id) {
1086 #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
1087 case BLE_L2CAP_SIG_PROC_OP_CONNECT:
1088 ble_l2cap_sig_coc_connect_cb(proc, BLE_HS_EREJECT);
1089 break;
1090 #endif
1091 default:
1092 break;
1093 }
1094
1095 ble_l2cap_sig_proc_free(proc);
1096 return 0;
1097 }
1098 /*****************************************************************************
1099 * $misc *
1100 *****************************************************************************/
1101
1102 static int
ble_l2cap_sig_rx(struct ble_l2cap_chan * chan)1103 ble_l2cap_sig_rx(struct ble_l2cap_chan *chan)
1104 {
1105 struct ble_l2cap_sig_hdr hdr;
1106 ble_l2cap_sig_rx_fn *rx_cb;
1107 uint16_t conn_handle;
1108 struct os_mbuf **om;
1109 int rc;
1110
1111 conn_handle = chan->conn_handle;
1112 om = &chan->rx_buf;
1113
1114 STATS_INC(ble_l2cap_stats, sig_rx);
1115
1116 #if !BLE_MONITOR
1117 BLE_HS_LOG(DEBUG, "L2CAP - rxed signalling msg: ");
1118 ble_hs_log_mbuf(*om);
1119 BLE_HS_LOG(DEBUG, "\n");
1120 #endif
1121
1122 rc = ble_hs_mbuf_pullup_base(om, BLE_L2CAP_SIG_HDR_SZ);
1123 if (rc != 0) {
1124 return rc;
1125 }
1126
1127 ble_l2cap_sig_hdr_parse((*om)->om_data, (*om)->om_len, &hdr);
1128
1129 /* Strip L2CAP sig header from the front of the mbuf. */
1130 os_mbuf_adj(*om, BLE_L2CAP_SIG_HDR_SZ);
1131
1132 if (OS_MBUF_PKTLEN(*om) != hdr.length) {
1133 return BLE_HS_EBADDATA;
1134 }
1135
1136 rx_cb = ble_l2cap_sig_dispatch_get(hdr.op);
1137 if (rx_cb == NULL) {
1138 rc = BLE_HS_EREJECT;
1139 } else {
1140 rc = rx_cb(conn_handle, &hdr, om);
1141 }
1142
1143 if (rc) {
1144 ble_l2cap_sig_reject_tx(conn_handle, hdr.identifier,
1145 BLE_L2CAP_SIG_ERR_CMD_NOT_UNDERSTOOD,
1146 NULL, 0);
1147 }
1148
1149 return rc;
1150 }
1151
1152 struct ble_l2cap_chan *
ble_l2cap_sig_create_chan(uint16_t conn_handle)1153 ble_l2cap_sig_create_chan(uint16_t conn_handle)
1154 {
1155 struct ble_l2cap_chan *chan;
1156
1157 chan = ble_l2cap_chan_alloc(conn_handle);
1158 if (chan == NULL) {
1159 return NULL;
1160 }
1161
1162 chan->scid = BLE_L2CAP_CID_SIG;
1163 chan->dcid = BLE_L2CAP_CID_SIG;
1164 chan->my_mtu = BLE_L2CAP_SIG_MTU;
1165 chan->rx_fn = ble_l2cap_sig_rx;
1166
1167 return chan;
1168 }
1169
1170 /**
1171 * @return The number of ticks until the next expiration
1172 * occurs.
1173 */
1174 static int32_t
ble_l2cap_sig_extract_expired(struct ble_l2cap_sig_proc_list * dst_list)1175 ble_l2cap_sig_extract_expired(struct ble_l2cap_sig_proc_list *dst_list)
1176 {
1177 struct ble_l2cap_sig_proc *proc;
1178 struct ble_l2cap_sig_proc *prev;
1179 struct ble_l2cap_sig_proc *next;
1180 ble_npl_time_t now;
1181 ble_npl_stime_t next_exp_in;
1182 ble_npl_stime_t time_diff;
1183
1184 now = ble_npl_time_get();
1185 STAILQ_INIT(dst_list);
1186
1187 /* Assume each event is either expired or has infinite duration. */
1188 next_exp_in = BLE_HS_FOREVER;
1189
1190 ble_hs_lock();
1191
1192 prev = NULL;
1193 proc = STAILQ_FIRST(&ble_l2cap_sig_procs);
1194 while (proc != NULL) {
1195 next = STAILQ_NEXT(proc, next);
1196
1197 time_diff = proc->exp_os_ticks - now;
1198 if (time_diff <= 0) {
1199 /* Procedure has expired; move it to the destination list. */
1200 if (prev == NULL) {
1201 STAILQ_REMOVE_HEAD(&ble_l2cap_sig_procs, next);
1202 } else {
1203 STAILQ_REMOVE_AFTER(&ble_l2cap_sig_procs, prev, next);
1204 }
1205 STAILQ_INSERT_TAIL(dst_list, proc, next);
1206 } else {
1207 if (time_diff < next_exp_in) {
1208 next_exp_in = time_diff;
1209 }
1210 }
1211
1212 proc = next;
1213 }
1214
1215 ble_hs_unlock();
1216
1217 return next_exp_in;
1218 }
1219
1220 void
ble_l2cap_sig_conn_broken(uint16_t conn_handle,int reason)1221 ble_l2cap_sig_conn_broken(uint16_t conn_handle, int reason)
1222 {
1223 struct ble_l2cap_sig_proc *proc;
1224
1225 /* Report a failure for each timed out procedure. */
1226 while ((proc = STAILQ_FIRST(&ble_l2cap_sig_procs)) != NULL) {
1227 switch(proc->op) {
1228 case BLE_L2CAP_SIG_PROC_OP_UPDATE:
1229 ble_l2cap_sig_update_call_cb(proc, reason);
1230 break;
1231 #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
1232 case BLE_L2CAP_SIG_PROC_OP_CONNECT:
1233 ble_l2cap_sig_coc_connect_cb(proc, reason);
1234 break;
1235 case BLE_L2CAP_SIG_PROC_OP_DISCONNECT:
1236 ble_l2cap_sig_coc_disconnect_cb(proc, reason);
1237 break;
1238 #endif
1239 }
1240
1241 STAILQ_REMOVE_HEAD(&ble_l2cap_sig_procs, next);
1242 ble_l2cap_sig_proc_free(proc);
1243 }
1244
1245 }
1246
1247 /**
1248 * Terminates expired procedures.
1249 *
1250 * @return The number of ticks until this function should
1251 * be called again.
1252 */
1253 int32_t
ble_l2cap_sig_timer(void)1254 ble_l2cap_sig_timer(void)
1255 {
1256 struct ble_l2cap_sig_proc_list temp_list;
1257 struct ble_l2cap_sig_proc *proc;
1258 int32_t ticks_until_exp;
1259
1260 /* Remove timed-out procedures from the main list and insert them into a
1261 * temporary list. This function also calculates the number of ticks until
1262 * the next expiration will occur.
1263 */
1264 ticks_until_exp = ble_l2cap_sig_extract_expired(&temp_list);
1265
1266 /* Report a failure for each timed out procedure. */
1267 while ((proc = STAILQ_FIRST(&temp_list)) != NULL) {
1268 STATS_INC(ble_l2cap_stats, proc_timeout);
1269 switch(proc->op) {
1270 case BLE_L2CAP_SIG_PROC_OP_UPDATE:
1271 ble_l2cap_sig_update_call_cb(proc, BLE_HS_ETIMEOUT);
1272 break;
1273 #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
1274 case BLE_L2CAP_SIG_PROC_OP_CONNECT:
1275 ble_l2cap_sig_coc_connect_cb(proc, BLE_HS_ETIMEOUT);
1276 break;
1277 case BLE_L2CAP_SIG_PROC_OP_DISCONNECT:
1278 ble_l2cap_sig_coc_disconnect_cb(proc, BLE_HS_ETIMEOUT);
1279 break;
1280 #endif
1281 }
1282
1283 STAILQ_REMOVE_HEAD(&temp_list, next);
1284 ble_l2cap_sig_proc_free(proc);
1285 }
1286
1287 return ticks_until_exp;
1288 }
1289
1290 int
ble_l2cap_sig_init(void)1291 ble_l2cap_sig_init(void)
1292 {
1293 int rc;
1294
1295 STAILQ_INIT(&ble_l2cap_sig_procs);
1296
1297 rc = os_mempool_init(&ble_l2cap_sig_proc_pool,
1298 MYNEWT_VAL(BLE_L2CAP_SIG_MAX_PROCS),
1299 sizeof (struct ble_l2cap_sig_proc),
1300 ble_l2cap_sig_proc_mem,
1301 "ble_l2cap_sig_proc_pool");
1302 if (rc != 0) {
1303 return rc;
1304 }
1305
1306 return 0;
1307 }
1308