xref: /nrf52832-nimble/packages/NimBLE-latest/nimble/host/src/ble_l2cap_sig.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
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, &params);
384     if (rc == 0) {
385         /* Application agrees to accept parameters; schedule update. */
386         rc = ble_gap_update_params(conn_handle, &params);
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