• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "securec.h"
48 #include "nimble/ble.h"
49 #include "host/ble_monitor.h"
50 #include "ble_hs_priv.h"
51 
52 /*****************************************************************************
53  * $definitions / declarations                                               *
54  *****************************************************************************/
55 
56 #define BLE_L2CAP_SIG_UNRESPONSIVE_TIMEOUT      30000   /* Milliseconds. */
57 
58 #define BLE_L2CAP_SIG_PROC_OP_UPDATE            0
59 #define BLE_L2CAP_SIG_PROC_OP_CONNECT           1
60 #define BLE_L2CAP_SIG_PROC_OP_RECONFIG          2
61 #define BLE_L2CAP_SIG_PROC_OP_DISCONNECT        3
62 #define BLE_L2CAP_SIG_PROC_OP_MAX               4
63 
64 #if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC)
65 #define BLE_L2CAP_ECOC_MIN_MTU  (64)
66 
67 #define BLE_L2CAP_MAX_COC_CONN_REQ  (5)
68 #else
69 #define BLE_L2CAP_MAX_COC_CONN_REQ  (1)
70 #endif
71 
72 struct ble_l2cap_sig_proc {
73     STAILQ_ENTRY(ble_l2cap_sig_proc) next;
74 
75     ble_npl_time_t exp_os_ticks;
76     uint16_t conn_handle;
77     uint8_t op;
78     uint8_t id;
79 
80     union {
81         struct {
82             ble_l2cap_sig_update_fn *cb;
83             void *cb_arg;
84         } update;
85 #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
86         struct {
87             uint8_t chan_cnt;
88             struct ble_l2cap_chan *chan[BLE_L2CAP_MAX_COC_CONN_REQ];
89         } connect;
90         struct {
91             struct ble_l2cap_chan *chan;
92         } disconnect;
93 #endif
94 #if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC)
95         struct {
96             uint8_t cid_cnt;
97             uint16_t cids[BLE_L2CAP_MAX_COC_CONN_REQ];
98             uint16_t new_mps;
99             uint16_t new_mtu;
100         } reconfig;
101 #endif
102     };
103 };
104 
105 STAILQ_HEAD(ble_l2cap_sig_proc_list, ble_l2cap_sig_proc);
106 
107 static struct ble_l2cap_sig_proc_list ble_l2cap_sig_procs;
108 
109 typedef int ble_l2cap_sig_rx_fn(uint16_t conn_handle,
110                                 struct ble_l2cap_sig_hdr *hdr,
111                                 struct os_mbuf **om);
112 
113 static ble_l2cap_sig_rx_fn ble_l2cap_sig_rx_noop;
114 static ble_l2cap_sig_rx_fn ble_l2cap_sig_update_req_rx;
115 static ble_l2cap_sig_rx_fn ble_l2cap_sig_update_rsp_rx;
116 static ble_l2cap_sig_rx_fn ble_l2cap_sig_rx_reject;
117 
118 #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
119 static ble_l2cap_sig_rx_fn ble_l2cap_sig_coc_req_rx;
120 static ble_l2cap_sig_rx_fn ble_l2cap_sig_coc_rsp_rx;
121 static ble_l2cap_sig_rx_fn ble_l2cap_sig_disc_rsp_rx;
122 static ble_l2cap_sig_rx_fn ble_l2cap_sig_disc_req_rx;
123 static ble_l2cap_sig_rx_fn ble_l2cap_sig_le_credits_rx;
124 #else
125 #define ble_l2cap_sig_coc_req_rx    ble_l2cap_sig_rx_noop
126 #define ble_l2cap_sig_coc_rsp_rx    ble_l2cap_sig_rx_noop
127 #define ble_l2cap_sig_disc_rsp_rx   ble_l2cap_sig_rx_noop
128 #define ble_l2cap_sig_disc_req_rx   ble_l2cap_sig_rx_noop
129 #define ble_l2cap_sig_le_credits_rx                  ble_l2cap_sig_rx_noop
130 #endif
131 
132 #if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC)
133 static ble_l2cap_sig_rx_fn ble_l2cap_sig_credit_base_con_req_rx;
134 static ble_l2cap_sig_rx_fn ble_l2cap_sig_credit_base_con_rsp_rx;
135 static ble_l2cap_sig_rx_fn ble_l2cap_sig_credit_base_reconfig_req_rx;
136 static ble_l2cap_sig_rx_fn ble_l2cap_sig_credit_base_reconfig_rsp_rx;
137 #else
138 #define ble_l2cap_sig_credit_base_con_req_rx      ble_l2cap_sig_rx_noop
139 #define ble_l2cap_sig_credit_base_con_rsp_rx      ble_l2cap_sig_rx_noop
140 #define ble_l2cap_sig_credit_base_reconfig_req_rx ble_l2cap_sig_rx_noop
141 #define ble_l2cap_sig_credit_base_reconfig_rsp_rx ble_l2cap_sig_rx_noop
142 #endif
143 
144 static ble_l2cap_sig_rx_fn *const ble_l2cap_sig_dispatch[] = {
145     [BLE_L2CAP_SIG_OP_REJECT]               = ble_l2cap_sig_rx_reject,
146     [BLE_L2CAP_SIG_OP_CONNECT_RSP]          = ble_l2cap_sig_rx_noop,
147     [BLE_L2CAP_SIG_OP_CONFIG_RSP]           = ble_l2cap_sig_rx_noop,
148     [BLE_L2CAP_SIG_OP_DISCONN_REQ]          = ble_l2cap_sig_disc_req_rx,
149     [BLE_L2CAP_SIG_OP_DISCONN_RSP]          = ble_l2cap_sig_disc_rsp_rx,
150     [BLE_L2CAP_SIG_OP_ECHO_RSP]             = ble_l2cap_sig_rx_noop,
151     [BLE_L2CAP_SIG_OP_INFO_RSP]             = ble_l2cap_sig_rx_noop,
152     [BLE_L2CAP_SIG_OP_CREATE_CHAN_RSP]      = ble_l2cap_sig_rx_noop,
153     [BLE_L2CAP_SIG_OP_MOVE_CHAN_RSP]        = ble_l2cap_sig_rx_noop,
154     [BLE_L2CAP_SIG_OP_MOVE_CHAN_CONF_RSP]   = ble_l2cap_sig_rx_noop,
155     [BLE_L2CAP_SIG_OP_UPDATE_REQ]           = ble_l2cap_sig_update_req_rx,
156     [BLE_L2CAP_SIG_OP_UPDATE_RSP]           = ble_l2cap_sig_update_rsp_rx,
157     [BLE_L2CAP_SIG_OP_LE_CREDIT_CONNECT_REQ]   = ble_l2cap_sig_coc_req_rx,
158     [BLE_L2CAP_SIG_OP_LE_CREDIT_CONNECT_RSP]   = ble_l2cap_sig_coc_rsp_rx,
159     [BLE_L2CAP_SIG_OP_FLOW_CTRL_CREDIT]     = ble_l2cap_sig_le_credits_rx,
160     [BLE_L2CAP_SIG_OP_CREDIT_CONNECT_REQ]   = ble_l2cap_sig_credit_base_con_req_rx,
161     [BLE_L2CAP_SIG_OP_CREDIT_CONNECT_RSP]   = ble_l2cap_sig_credit_base_con_rsp_rx,
162     [BLE_L2CAP_SIG_OP_CREDIT_RECONFIG_REQ]  = ble_l2cap_sig_credit_base_reconfig_req_rx,
163     [BLE_L2CAP_SIG_OP_CREDIT_RECONFIG_RSP]  = ble_l2cap_sig_credit_base_reconfig_rsp_rx,
164 };
165 
166 static uint8_t ble_l2cap_sig_cur_id;
167 
168 static os_membuf_t ble_l2cap_sig_proc_mem[
169                  OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_L2CAP_SIG_MAX_PROCS),
170                                  sizeof(struct ble_l2cap_sig_proc))
171 ];
172 
173 static struct os_mempool ble_l2cap_sig_proc_pool;
174 
175 /*****************************************************************************
176  * $debug                                                                    *
177  *****************************************************************************/
178 
ble_l2cap_sig_dbg_assert_proc_not_inserted(struct ble_l2cap_sig_proc * proc)179 static void ble_l2cap_sig_dbg_assert_proc_not_inserted(struct ble_l2cap_sig_proc *proc)
180 {
181 #if MYNEWT_VAL(BLE_HS_DEBUG)
182     struct ble_l2cap_sig_proc *cur;
183     STAILQ_FOREACH(cur, &ble_l2cap_sig_procs, next) {
184         BLE_HS_DBG_ASSERT(cur != proc);
185     }
186 #endif
187 }
188 
189 /*****************************************************************************
190  * $misc                                                                     *
191  *****************************************************************************/
192 
ble_l2cap_sig_next_id(void)193 static uint8_t ble_l2cap_sig_next_id(void)
194 {
195     ble_l2cap_sig_cur_id++;
196     if (ble_l2cap_sig_cur_id == 0) {
197         /* An ID of 0 is illegal. */
198         ble_l2cap_sig_cur_id = 1;
199     }
200 
201     return ble_l2cap_sig_cur_id;
202 }
203 
ble_l2cap_sig_dispatch_get(uint8_t op)204 static ble_l2cap_sig_rx_fn *ble_l2cap_sig_dispatch_get(uint8_t op)
205 {
206     if (op >= BLE_L2CAP_SIG_OP_MAX) {
207         return NULL;
208     }
209 
210     return ble_l2cap_sig_dispatch[op];
211 }
212 
213 /**
214  * Allocates a proc entry.
215  *
216  * @return                      An entry on success; null on failure.
217  */
ble_l2cap_sig_proc_alloc(void)218 static struct ble_l2cap_sig_proc *ble_l2cap_sig_proc_alloc(void)
219 {
220     struct ble_l2cap_sig_proc *proc;
221     proc = os_memblock_get(&ble_l2cap_sig_proc_pool);
222     if (proc != NULL) {
223         memset_s(proc, sizeof * proc, 0, sizeof * proc);
224     }
225 
226     return proc;
227 }
228 
229 /**
230  * Frees the specified proc entry.  No-op if passed a null pointer.
231  */
ble_l2cap_sig_proc_free(struct ble_l2cap_sig_proc * proc)232 static void ble_l2cap_sig_proc_free(struct ble_l2cap_sig_proc *proc)
233 {
234     if (proc != NULL) {
235         ble_l2cap_sig_dbg_assert_proc_not_inserted(proc);
236 #if MYNEWT_VAL(BLE_HS_DEBUG)
237         memset_s(proc, sizeof * proc, 0xff, sizeof * proc);
238 #endif
239         int rc = os_memblock_put(&ble_l2cap_sig_proc_pool, proc);
240         BLE_HS_DBG_ASSERT_EVAL(rc == 0);
241     }
242 }
243 
ble_l2cap_sig_proc_insert(struct ble_l2cap_sig_proc * proc)244 static void ble_l2cap_sig_proc_insert(struct ble_l2cap_sig_proc *proc)
245 {
246     ble_l2cap_sig_dbg_assert_proc_not_inserted(proc);
247     ble_hs_lock();
248     STAILQ_INSERT_HEAD(&ble_l2cap_sig_procs, proc, next);
249     ble_hs_unlock();
250 }
251 
252 /**
253  * Tests if a proc entry fits the specified criteria.
254  *
255  * @param proc                  The procedure to test.
256  * @param conn_handle           The connection handle to match against.
257  * @param op                    The op code to match against/
258  * @param id                    The identifier to match against.
259  *                                  0=Ignore this criterion.
260  *
261  * @return                      1 if the proc matches; 0 otherwise.
262  */
ble_l2cap_sig_proc_matches(struct ble_l2cap_sig_proc * proc,uint16_t conn_handle,uint8_t op,uint8_t id)263 static int ble_l2cap_sig_proc_matches(struct ble_l2cap_sig_proc *proc,
264                                       uint16_t conn_handle, uint8_t op, uint8_t id)
265 {
266     if (conn_handle != proc->conn_handle) {
267         return 0;
268     }
269 
270     if (op != proc->op) {
271         return 0;
272     }
273 
274     if (id != 0 && id != proc->id) {
275         return 0;
276     }
277 
278     return 1;
279 }
280 
281 /**
282  * Searches the main proc list for an "expecting" entry whose connection handle
283  * and op code match those specified.  If a matching entry is found, it is
284  * removed from the list and returned.
285  *
286  * @param conn_handle           The connection handle to match against.
287  * @param op                    The op code to match against.
288  * @param identifier            The identifier to match against;
289  *                                  0=ignore this criterion.
290  *
291  * @return                      The matching proc entry on success;
292  *                                  null on failure.
293  */
ble_l2cap_sig_proc_extract(uint16_t conn_handle,uint8_t op,uint8_t identifier)294 static struct ble_l2cap_sig_proc *ble_l2cap_sig_proc_extract(uint16_t conn_handle, uint8_t op, uint8_t identifier)
295 {
296     struct ble_l2cap_sig_proc *proc;
297     struct ble_l2cap_sig_proc *prev;
298     ble_hs_lock();
299     prev = NULL;
300     STAILQ_FOREACH(proc, &ble_l2cap_sig_procs, next) {
301         if (ble_l2cap_sig_proc_matches(proc, conn_handle, op, identifier)) {
302             if (prev == NULL) {
303                 STAILQ_REMOVE_HEAD(&ble_l2cap_sig_procs, next);
304             } else {
305                 STAILQ_REMOVE_AFTER(&ble_l2cap_sig_procs, prev, next);
306             }
307 
308             break;
309         }
310 
311         prev = proc;
312     }
313     ble_hs_unlock();
314     return proc;
315 }
316 
ble_l2cap_sig_rx_noop(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)317 static int ble_l2cap_sig_rx_noop(uint16_t conn_handle,
318                                  struct ble_l2cap_sig_hdr *hdr,
319                                  struct os_mbuf **om)
320 {
321     return BLE_HS_ENOTSUP;
322 }
323 
ble_l2cap_sig_proc_set_timer(struct ble_l2cap_sig_proc * proc)324 static void ble_l2cap_sig_proc_set_timer(struct ble_l2cap_sig_proc *proc)
325 {
326     proc->exp_os_ticks = ble_npl_time_get() +
327                          ble_npl_time_ms_to_ticks32(BLE_L2CAP_SIG_UNRESPONSIVE_TIMEOUT);
328     ble_hs_timer_resched();
329 }
330 
ble_l2cap_sig_process_status(struct ble_l2cap_sig_proc * proc,int status)331 static void ble_l2cap_sig_process_status(struct ble_l2cap_sig_proc *proc, int status)
332 {
333     if (status == 0) {
334         ble_l2cap_sig_proc_set_timer(proc);
335         ble_l2cap_sig_proc_insert(proc);
336     } else {
337         ble_l2cap_sig_proc_free(proc);
338     }
339 }
340 
341 /*****************************************************************************
342  * $update                                                                   *
343  *****************************************************************************/
344 
ble_l2cap_sig_update_call_cb(struct ble_l2cap_sig_proc * proc,int status)345 static void ble_l2cap_sig_update_call_cb(struct ble_l2cap_sig_proc *proc, int status)
346 {
347     BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
348 
349     if (status != 0) {
350         STATS_INC(ble_l2cap_stats, update_fail);
351     }
352 
353     if (proc->update.cb != NULL) {
354         proc->update.cb(proc->conn_handle, status, proc->update.cb_arg);
355     }
356 }
357 
ble_l2cap_sig_update_req_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)358 int ble_l2cap_sig_update_req_rx(uint16_t conn_handle,
359                                 struct ble_l2cap_sig_hdr *hdr,
360                                 struct os_mbuf **om)
361 {
362     struct ble_l2cap_sig_update_req *req;
363     struct os_mbuf *txom;
364     struct ble_l2cap_sig_update_rsp *rsp;
365     struct ble_gap_upd_params params;
366     ble_hs_conn_flags_t conn_flags;
367     uint16_t l2cap_result;
368     int sig_err;
369     int rc;
370     l2cap_result = 0; /* Silence spurious gcc warning. */
371     rc = ble_hs_mbuf_pullup_base(om, BLE_L2CAP_SIG_UPDATE_REQ_SZ);
372     if (rc != 0) {
373         return rc;
374     }
375 
376     rc = ble_hs_atomic_conn_flags(conn_handle, &conn_flags);
377     if (rc != 0) {
378         return rc;
379     }
380 
381     /* Only a master can process an update request. */
382     sig_err = !(conn_flags & BLE_HS_CONN_F_MASTER);
383     if (sig_err) {
384         return BLE_HS_EREJECT;
385     }
386 
387     req = (struct ble_l2cap_sig_update_req *)(*om)->om_data;
388     params.itvl_min = le16toh(req->itvl_min);
389     params.itvl_max = le16toh(req->itvl_max);
390     params.latency = le16toh(req->slave_latency);
391     params.supervision_timeout = le16toh(req->timeout_multiplier);
392     params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN;
393     params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN;
394     /* Ask application if slave's connection parameters are acceptable. */
395     rc = ble_gap_rx_l2cap_update_req(conn_handle, &params);
396     if (rc == 0) {
397         /* Application agrees to accept parameters; schedule update. */
398         rc = ble_gap_update_params(conn_handle, &params);
399     }
400 
401     if (rc == 0) {
402         l2cap_result = BLE_L2CAP_SIG_UPDATE_RSP_RESULT_ACCEPT;
403     } else {
404         l2cap_result = BLE_L2CAP_SIG_UPDATE_RSP_RESULT_REJECT;
405     }
406 
407     rsp = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_UPDATE_RSP, hdr->identifier,
408                                 sizeof(*rsp), &txom);
409     if (!rsp) {
410         /* No memory for response, lest allow to timeout on remote side */
411         return 0;
412     }
413 
414     rsp->result = htole16(l2cap_result);
415     /* Send L2CAP response. */
416     ble_l2cap_sig_tx(conn_handle, txom);
417     return 0;
418 }
419 
ble_l2cap_sig_update_rsp_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)420 static int ble_l2cap_sig_update_rsp_rx(uint16_t conn_handle,
421                                        struct ble_l2cap_sig_hdr *hdr,
422                                        struct os_mbuf **om)
423 {
424     struct ble_l2cap_sig_update_rsp *rsp;
425     struct ble_l2cap_sig_proc *proc;
426     int cb_status;
427     int rc;
428     proc = ble_l2cap_sig_proc_extract(conn_handle,
429                                       BLE_L2CAP_SIG_PROC_OP_UPDATE,
430                                       hdr->identifier);
431     if (proc == NULL) {
432         return 0;
433     }
434 
435     rc = ble_hs_mbuf_pullup_base(om, BLE_L2CAP_SIG_UPDATE_RSP_SZ);
436     if (rc != 0) {
437         cb_status = rc;
438         goto done;
439     }
440 
441     rsp = (struct ble_l2cap_sig_update_rsp *)(*om)->om_data;
442 
443     switch (le16toh(rsp->result)) {
444         case BLE_L2CAP_SIG_UPDATE_RSP_RESULT_ACCEPT:
445             cb_status = 0;
446             rc = 0;
447             break;
448 
449         case BLE_L2CAP_SIG_UPDATE_RSP_RESULT_REJECT:
450             cb_status = BLE_HS_EREJECT;
451             rc = 0;
452             break;
453 
454         default:
455             cb_status = BLE_HS_EBADDATA;
456             rc = 0;
457             break;
458     }
459 
460 done:
461     ble_l2cap_sig_update_call_cb(proc, cb_status);
462     ble_l2cap_sig_proc_free(proc);
463     return rc;
464 }
465 
ble_l2cap_sig_update(uint16_t conn_handle,struct ble_l2cap_sig_update_params * params,ble_l2cap_sig_update_fn * cb,void * cb_arg)466 int ble_l2cap_sig_update(uint16_t conn_handle,
467                          struct ble_l2cap_sig_update_params *params,
468                          ble_l2cap_sig_update_fn *cb, void *cb_arg)
469 {
470     struct os_mbuf *txom;
471     struct ble_l2cap_sig_update_req *req;
472     struct ble_l2cap_sig_proc *proc;
473     struct ble_l2cap_chan *chan;
474     struct ble_hs_conn *conn;
475     int master;
476     int rc;
477     proc = NULL;
478     STATS_INC(ble_l2cap_stats, update_init);
479     ble_hs_lock();
480     ble_hs_misc_conn_chan_find_reqd(conn_handle, BLE_L2CAP_CID_SIG,
481                                     &conn, &chan);
482     master = conn->bhc_flags & BLE_HS_CONN_F_MASTER;
483     ble_hs_unlock();
484 
485     if (master) {
486         /* Only the slave can initiate the L2CAP connection update
487          * procedure.
488          */
489         rc = BLE_HS_EINVAL;
490         goto done;
491     }
492 
493     proc = ble_l2cap_sig_proc_alloc();
494     if (proc == NULL) {
495         STATS_INC(ble_l2cap_stats, update_fail);
496         rc = BLE_HS_ENOMEM;
497         goto done;
498     }
499 
500     proc->op = BLE_L2CAP_SIG_PROC_OP_UPDATE;
501     proc->id = ble_l2cap_sig_next_id();
502     proc->conn_handle = conn_handle;
503     proc->update.cb = cb;
504     proc->update.cb_arg = cb_arg;
505     req = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_UPDATE_REQ, proc->id,
506                                 sizeof(*req), &txom);
507     if (!req) {
508         STATS_INC(ble_l2cap_stats, update_fail);
509         rc = BLE_HS_ENOMEM;
510         goto done;
511     }
512 
513     req->itvl_min = htole16(params->itvl_min);
514     req->itvl_max = htole16(params->itvl_max);
515     req->slave_latency = htole16(params->slave_latency);
516     req->timeout_multiplier = htole16(params->timeout_multiplier);
517     rc = ble_l2cap_sig_tx(conn_handle, txom);
518 done:
519     ble_l2cap_sig_process_status(proc, rc);
520     return rc;
521 }
522 
523 /*****************************************************************************
524  * $connect                                                                  *
525  *****************************************************************************/
526 
527 #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
528 
ble_l2cap_sig_coc_err2ble_hs_err(uint16_t l2cap_coc_err)529 static int 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 
535         case BLE_L2CAP_COC_ERR_UNKNOWN_LE_PSM:
536             return BLE_HS_ENOTSUP;
537 
538         case BLE_L2CAP_COC_ERR_NO_RESOURCES:
539             return BLE_HS_ENOMEM;
540 
541         case BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHEN:
542             return BLE_HS_EAUTHEN;
543 
544         case BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHOR:
545             return BLE_HS_EAUTHOR;
546 
547         case BLE_L2CAP_COC_ERR_INSUFFICIENT_KEY_SZ:
548             return BLE_HS_EENCRYPT_KEY_SZ;
549 
550         case BLE_L2CAP_COC_ERR_INSUFFICIENT_ENC:
551             return BLE_HS_EENCRYPT;
552 
553         case BLE_L2CAP_COC_ERR_INVALID_SOURCE_CID:
554             return BLE_HS_EREJECT;
555 
556         case BLE_L2CAP_COC_ERR_SOURCE_CID_ALREADY_USED:
557             return BLE_HS_EALREADY;
558 
559         case BLE_L2CAP_COC_ERR_UNACCEPTABLE_PARAMETERS:
560             return BLE_HS_EINVAL;
561 
562         default:
563             return BLE_HS_EUNKNOWN;
564     }
565 }
566 
ble_l2cap_sig_ble_hs_err2coc_err(uint16_t ble_hs_err)567 static int ble_l2cap_sig_ble_hs_err2coc_err(uint16_t ble_hs_err)
568 {
569     switch (ble_hs_err) {
570         case BLE_HS_ENOTSUP:
571             return BLE_L2CAP_COC_ERR_UNKNOWN_LE_PSM;
572 
573         case BLE_HS_ENOMEM:
574             return BLE_L2CAP_COC_ERR_NO_RESOURCES;
575 
576         case BLE_HS_EAUTHEN:
577             return BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHEN;
578 
579         case BLE_HS_EAUTHOR:
580             return BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHOR;
581 
582         case BLE_HS_EENCRYPT:
583             return BLE_L2CAP_COC_ERR_INSUFFICIENT_ENC;
584 
585         case BLE_HS_EENCRYPT_KEY_SZ:
586             return BLE_L2CAP_COC_ERR_INSUFFICIENT_KEY_SZ;
587 
588         case BLE_HS_EINVAL:
589             return BLE_L2CAP_COC_ERR_UNACCEPTABLE_PARAMETERS;
590 
591         default:
592             return BLE_L2CAP_COC_ERR_NO_RESOURCES;
593     }
594 }
595 
ble_l2cap_event_coc_connected(struct ble_l2cap_chan * chan,uint16_t status)596 static void ble_l2cap_event_coc_connected(struct ble_l2cap_chan *chan, uint16_t status)
597 {
598     struct ble_l2cap_event event = { };
599     event.type = BLE_L2CAP_EVENT_COC_CONNECTED;
600     event.connect.conn_handle = chan->conn_handle;
601     event.connect.chan = chan;
602     event.connect.status = status;
603     chan->cb(&event, chan->cb_arg);
604 }
605 
ble_l2cap_event_coc_accept(struct ble_l2cap_chan * chan,uint16_t peer_sdu_size)606 static int ble_l2cap_event_coc_accept(struct ble_l2cap_chan *chan, uint16_t peer_sdu_size)
607 {
608     struct ble_l2cap_event event = { };
609     event.type = BLE_L2CAP_EVENT_COC_ACCEPT;
610     event.accept.chan = chan;
611     event.accept.conn_handle = chan->conn_handle;
612     event.accept.peer_sdu_size = peer_sdu_size;
613     return chan->cb(&event, chan->cb_arg);
614 }
615 
ble_l2cap_sig_coc_connect_cb(struct ble_l2cap_sig_proc * proc,int status)616 static void ble_l2cap_sig_coc_connect_cb(struct ble_l2cap_sig_proc *proc, int status)
617 {
618     struct ble_hs_conn *conn;
619     struct ble_l2cap_chan *chan;
620     int i;
621     bool some_not_connected = false;
622 
623     if (!proc) {
624         return;
625     }
626 
627     for (i = 0; i < proc->connect.chan_cnt; i++) {
628         chan = proc->connect.chan[i];
629         if (!chan || !chan->cb) {
630             continue;
631         }
632 
633         if ((status == 0) && (chan->dcid != 0)) {
634             ble_l2cap_event_coc_connected(chan, status);
635             /* Let's forget about connected channel now.
636              * Not connected will be freed later on.
637              */
638             proc->connect.chan[i] = NULL;
639             continue;
640         }
641 
642         some_not_connected = true;
643         ble_l2cap_event_coc_connected(chan, status ? status : BLE_HS_EREJECT);
644     }
645 
646     if (!some_not_connected) {
647         return;
648     }
649 
650     /* Free not connected channels */
651     ble_hs_lock();
652     conn = ble_hs_conn_find(chan->conn_handle);
653 
654     for (i = 0; i < proc->connect.chan_cnt; i++) {
655         chan = proc->connect.chan[i];
656         if (chan) {
657             /* Normally in channel free we send disconnected event to application.
658              * However in case on error during creation connection we send connected
659              * event with error status. To avoid additional disconnected event lets
660              * clear callbacks since we don't needed it anymore.
661              */
662             chan->cb = NULL;
663             ble_l2cap_chan_free(conn, chan);
664         }
665     }
666 
667     ble_hs_unlock();
668 }
669 
670 #if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC)
ble_l2cap_event_coc_reconfigured(uint16_t conn_handle,uint16_t status,struct ble_l2cap_chan * chan,bool peer)671 static void ble_l2cap_event_coc_reconfigured(uint16_t conn_handle, uint16_t status,
672                                              struct ble_l2cap_chan *chan, bool peer)
673 {
674     struct ble_l2cap_event event = { };
675 
676     if (peer) {
677         event.type = BLE_L2CAP_EVENT_COC_PEER_RECONFIGURED;
678     } else {
679         event.type = BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED;
680     }
681 
682     event.reconfigured.conn_handle = conn_handle;
683     event.reconfigured.chan = chan;
684     event.reconfigured.status = status;
685     chan->cb(&event, chan->cb_arg);
686 }
687 
ble_l2cap_sig_credit_base_reconfig_req_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)688 static int ble_l2cap_sig_credit_base_reconfig_req_rx(uint16_t conn_handle,
689                                                      struct ble_l2cap_sig_hdr *hdr,
690                                                      struct os_mbuf **om)
691 {
692     struct ble_l2cap_chan *chan[BLE_L2CAP_MAX_COC_CONN_REQ] = {0};
693     struct ble_l2cap_sig_credit_base_reconfig_req *req;
694     struct ble_l2cap_sig_credit_base_reconfig_rsp *rsp;
695     struct ble_hs_conn *conn;
696     struct os_mbuf *txom;
697     int i;
698     int rc;
699     uint8_t cid_cnt;
700     uint8_t reduction_mps = 0;
701     rc = ble_hs_mbuf_pullup_base(om, hdr->length);
702     if (rc != 0) {
703         return rc;
704     }
705 
706     ble_hs_lock();
707     conn = ble_hs_conn_find(conn_handle);
708     if (!conn) {
709         ble_hs_unlock();
710         return 0;
711     }
712 
713     rsp = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_CREDIT_RECONFIG_RSP,
714                                 hdr->identifier, sizeof(*rsp), &txom);
715     if (!rsp) {
716         /* Reuse request buffer for the response. For now in such a case
717          * remote will timeout.
718          */
719         BLE_HS_LOG(ERROR, "No memory for the response\n");
720         ble_hs_unlock();
721         return 0;
722     }
723 
724     if (hdr->length <= sizeof(*req)) {
725         rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_UNACCAPTED_PARAM);
726         goto failed;
727     }
728 
729     req = (struct ble_l2cap_sig_credit_base_reconfig_req *)(*om)->om_data;
730     if ((req->mps < BLE_L2CAP_ECOC_MIN_MTU) || (req->mtu < BLE_L2CAP_ECOC_MIN_MTU)) {
731         rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_UNACCAPTED_PARAM);
732         goto failed;
733     }
734 
735     /* Assume request will succeed. If not, result will be updated */
736     rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_SUCCEED);
737     cid_cnt = (hdr->length - sizeof(*req)) / sizeof(uint16_t);
738     if (cid_cnt > BLE_L2CAP_MAX_COC_CONN_REQ) {
739         rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_UNACCAPTED_PARAM);
740         goto failed;
741     }
742 
743     for (i = 0; i < cid_cnt; i++) {
744         chan[i] = ble_hs_conn_chan_find_by_dcid(conn, req->dcids[i]);
745         if (!chan[i]) {
746             rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_INVALID_DCID);
747             goto failed;
748         }
749 
750         if (chan[i]->peer_coc_mps > req->mps) {
751             reduction_mps++;
752 
753             if (reduction_mps > 1) {
754                 rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_REDUCTION_MPS_NOT_ALLOWED);
755                 goto failed;
756             }
757         }
758 
759         if (chan[i]->coc_tx.mtu > req->mtu) {
760             rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_REDUCTION_MTU_NOT_ALLOWED);
761             goto failed;
762         }
763     }
764 
765     ble_hs_unlock();
766 
767     for (i = 0; i < cid_cnt; i++) {
768         chan[i]->coc_tx.mtu = req->mtu;
769         chan[i]->peer_coc_mps = req->mps;
770         ble_l2cap_event_coc_reconfigured(conn_handle, 0, chan[i], true);
771     }
772 
773     ble_l2cap_sig_tx(conn_handle, txom);
774     return 0;
775 failed:
776     ble_hs_unlock();
777     ble_l2cap_sig_tx(conn_handle, txom);
778     return 0;
779 }
780 
ble_l2cap_sig_coc_reconfig_cb(struct ble_l2cap_sig_proc * proc,int status)781 static void ble_l2cap_sig_coc_reconfig_cb(struct ble_l2cap_sig_proc *proc, int status)
782 {
783     int i;
784     struct ble_l2cap_chan *chan[BLE_L2CAP_MAX_COC_CONN_REQ] = {0};
785     struct ble_hs_conn *conn;
786     ble_hs_lock();
787     conn = ble_hs_conn_find(proc->conn_handle);
788     if (!conn) {
789         ble_hs_unlock();
790         return;
791     }
792 
793     for (i = 0; i < proc->reconfig.cid_cnt; i++) {
794         chan[i] = ble_hs_conn_chan_find_by_scid(conn, proc->reconfig.cids[i]);
795 
796         if (status == 0) {
797             ble_l2cap_coc_set_new_mtu_mps(chan[i], proc->reconfig.new_mtu, proc->reconfig.new_mps);
798         }
799     }
800 
801     ble_hs_unlock();
802 
803     for (i = 0; i < proc->reconfig.cid_cnt; i++) {
804         ble_l2cap_event_coc_reconfigured(proc->conn_handle, status, chan[i], false);
805     }
806 }
807 
ble_l2cap_sig_credit_base_reconfig_rsp_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)808 static int ble_l2cap_sig_credit_base_reconfig_rsp_rx(uint16_t conn_handle,
809                                                      struct ble_l2cap_sig_hdr *hdr,
810                                                      struct os_mbuf **om)
811 {
812     struct ble_l2cap_sig_proc *proc;
813     struct ble_l2cap_sig_credit_base_reconfig_rsp *rsp;
814     int rc;
815     proc = ble_l2cap_sig_proc_extract(conn_handle,
816                                       BLE_L2CAP_SIG_PROC_OP_RECONFIG,
817                                       hdr->identifier);
818     if (!proc) {
819         return 0;
820     }
821 
822     rc = ble_hs_mbuf_pullup_base(om, hdr->length);
823     if (rc != 0) {
824         return rc;
825     }
826 
827     rsp = (struct ble_l2cap_sig_credit_base_reconfig_rsp *)(*om)->om_data;
828     ble_l2cap_sig_coc_reconfig_cb(proc, (rsp->result > 0) ? BLE_HS_EREJECT : 0);
829     return 0;
830 }
831 
ble_l2cap_sig_credit_base_con_req_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)832 static int ble_l2cap_sig_credit_base_con_req_rx(uint16_t conn_handle,
833                                                 struct ble_l2cap_sig_hdr *hdr,
834                                                 struct os_mbuf **om)
835 {
836     int rc;
837     struct ble_l2cap_sig_credit_base_connect_req *req;
838     struct os_mbuf *txom;
839     struct ble_l2cap_sig_credit_base_connect_rsp *rsp;
840     struct ble_l2cap_chan *chans[5] = { 0 };
841     struct ble_hs_conn *conn;
842     uint16_t scid;
843     uint8_t num_of_scids;
844     uint8_t chan_created = 0;
845     int i;
846     uint8_t len;
847     rc = ble_hs_mbuf_pullup_base(om, hdr->length);
848     if (rc != 0) {
849         return rc;
850     }
851 
852     len = (hdr->length > sizeof(*req)) ? hdr->length : sizeof(*req);
853     rsp = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_CREDIT_CONNECT_RSP,
854                                 hdr->identifier, len, &txom);
855     if (!rsp) {
856         /* Well, nothing smart we can do if there is no memory for response.
857          * Remote will timeout.
858          */
859         return 0;
860     }
861 
862     ble_hs_lock();
863     memset_s(rsp, sizeof(*rsp), 0, len);
864     /* Initial dummy values in case of error, just to satisfy PTS */
865     rsp->credits = htole16(1);
866     rsp->mps = htole16(BLE_L2CAP_ECOC_MIN_MTU);
867     rsp->mtu = htole16(BLE_L2CAP_ECOC_MIN_MTU);
868 
869     if (hdr->length <= sizeof(*req)) {
870         rsp->result = htole16(BLE_L2CAP_COC_ERR_INVALID_PARAMETERS);
871         goto failed;
872     }
873 
874     req = (struct ble_l2cap_sig_credit_base_connect_req *)(*om)->om_data;
875     num_of_scids = (hdr->length - sizeof(*req)) / sizeof(uint16_t);
876     if (num_of_scids > 5) { // 5:Analyzing conditions
877         rsp->result = htole16(BLE_L2CAP_COC_ERR_INVALID_PARAMETERS);
878         goto failed;
879     }
880 
881     if ((req->mtu < BLE_L2CAP_ECOC_MIN_MTU) || (req->mps < BLE_L2CAP_ECOC_MIN_MTU)) {
882         rsp->result = htole16(BLE_L2CAP_COC_ERR_INVALID_PARAMETERS);
883         goto failed;
884     }
885 
886     conn = ble_hs_conn_find_assert(conn_handle);
887 
888     /* First verify that provided SCIDs are good */
889     for (i = 0; i < num_of_scids; i++) {
890         scid = le16toh(req->scids[i]);
891         if (scid < BLE_L2CAP_COC_CID_START || scid > BLE_L2CAP_COC_CID_END) {
892             rsp->result = htole16(BLE_L2CAP_COC_ERR_INVALID_SOURCE_CID);
893             goto failed;
894         }
895     }
896 
897     /* Let us try to connect channels */
898     for (i = 0; i < num_of_scids; i++) {
899         /* Verify CID. Note, scid in the request is dcid for out local channel */
900         scid = le16toh(req->scids[i]);
901         chans[i] = ble_hs_conn_chan_find_by_dcid(conn, scid);
902         if (chans[i]) {
903             rsp->result = htole16(BLE_L2CAP_COC_ERR_SOURCE_CID_ALREADY_USED);
904             rsp->dcids[i] = htole16(chans[i]->scid);
905             continue;
906         }
907 
908         rc = ble_l2cap_coc_create_srv_chan(conn, le16toh(req->psm), &chans[i]);
909         if (rc != 0) {
910             if (i == 0) {
911                 /* In case it is very first channel we cannot create it means PSM is incorrect
912                  * or we are out of resources. Just send a response now.
913                  */
914                 rsp->result = htole16(ble_l2cap_sig_ble_hs_err2coc_err(rc));
915                 goto failed;
916             } else {
917                 /* We cannot create number of channels req by peer due to limited resources. */
918                 rsp->result = htole16(BLE_L2CAP_COC_ERR_NO_RESOURCES);
919                 goto done;
920             }
921         }
922 
923         /* Fill up remote configuration. Note MPS is the L2CAP MTU */
924         chans[i]->dcid = scid;
925         chans[i]->peer_coc_mps = le16toh(req->mps);
926         chans[i]->coc_tx.credits = le16toh(req->credits);
927         chans[i]->coc_tx.mtu = le16toh(req->mtu);
928         ble_hs_conn_chan_insert(conn, chans[i]);
929         /* Sending event to the app. Unlock hs */
930         ble_hs_unlock();
931         rc = ble_l2cap_event_coc_accept(chans[i], le16toh(req->mtu));
932         if (rc == 0) {
933             rsp->dcids[i] = htole16(chans[i]->scid);
934             chan_created++;
935             if (chan_created == 1) {
936                 /* We need to set it once as there are same initial parameters
937                  * for all the channels
938                  */
939                 rsp->credits = htole16(chans[i]->coc_rx.credits);
940                 rsp->mps = htole16(chans[i]->my_mtu);
941                 rsp->mtu = htole16(chans[i]->coc_rx.mtu);
942             }
943         } else {
944             /* Make sure we do not send disconnect event when removing channel */
945             chans[i]->cb = NULL;
946             ble_hs_lock();
947             conn = ble_hs_conn_find_assert(conn_handle);
948             ble_hs_conn_delete_chan(conn, chans[i]);
949             chans[i] = NULL;
950             rsp->result = htole16(ble_l2cap_sig_ble_hs_err2coc_err(rc));
951             rc = 0;
952             ble_hs_unlock();
953         }
954 
955         ble_hs_lock();
956         conn = ble_hs_conn_find_assert(conn_handle);
957     }
958 
959 done:
960     ble_hs_unlock();
961     rc = ble_l2cap_sig_tx(conn_handle, txom);
962     if (rc != 0) {
963         ble_hs_lock();
964         conn = ble_hs_conn_find_assert(conn_handle);
965 
966         for (i = 0; i < num_of_scids; i++) {
967             if (chans[i]) {
968                 ble_hs_conn_delete_chan(conn, chans[i]);
969             }
970         }
971 
972         ble_hs_unlock();
973         return 0;
974     }
975 
976     /* Notify user about connection status */
977     for (i = 0; i < num_of_scids; i++) {
978         if (chans[i]) {
979             ble_l2cap_event_coc_connected(chans[i], rc);
980         }
981     }
982 
983     return 0;
984 failed:
985     ble_hs_unlock();
986     ble_l2cap_sig_tx(conn_handle, txom);
987     return 0;
988 }
989 
ble_l2cap_sig_credit_base_con_rsp_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)990 static int ble_l2cap_sig_credit_base_con_rsp_rx(uint16_t conn_handle,
991                                                 struct ble_l2cap_sig_hdr *hdr,
992                                                 struct os_mbuf **om)
993 {
994     struct ble_l2cap_sig_proc *proc;
995     struct ble_l2cap_sig_credit_base_connect_rsp *rsp;
996     struct ble_l2cap_chan *chan;
997     struct ble_hs_conn *conn;
998     int rc;
999     int i;
1000 #if !BLE_MONITOR
1001     BLE_HS_LOG(DEBUG, "L2CAP LE COC connection response received\n");
1002 #endif
1003     proc = ble_l2cap_sig_proc_extract(conn_handle,
1004                                       BLE_L2CAP_SIG_PROC_OP_CONNECT,
1005                                       hdr->identifier);
1006     if (!proc) {
1007         return 0;
1008     }
1009 
1010     rc = ble_hs_mbuf_pullup_base(om, hdr->length);
1011     if (rc != 0) {
1012         goto done;
1013     }
1014 
1015     rsp = (struct ble_l2cap_sig_credit_base_connect_rsp *)(*om)->om_data;
1016     if (rsp->result) {
1017         rc = ble_l2cap_sig_coc_err2ble_hs_err(le16toh(rsp->result));
1018         goto done;
1019     }
1020 
1021     ble_hs_lock();
1022     conn = ble_hs_conn_find(conn_handle);
1023     assert(conn != NULL);
1024 
1025     for (i = 0; i < proc->connect.chan_cnt; i++) {
1026         chan = proc->connect.chan[i];
1027 
1028         if (rsp->dcids[i] == 0) {
1029             /* Channel rejected, dont put it on the list.
1030              * User will get notified later in that function
1031              */
1032             chan->dcid = 0;
1033             continue;
1034         }
1035 
1036         chan->peer_coc_mps = le16toh(rsp->mps);
1037         chan->dcid = le16toh(rsp->dcids[i]);
1038         chan->coc_tx.mtu = le16toh(rsp->mtu);
1039         chan->coc_tx.credits = le16toh(rsp->credits);
1040         ble_hs_conn_chan_insert(conn, chan);
1041     }
1042 
1043     ble_hs_unlock();
1044 done:
1045     ble_l2cap_sig_coc_connect_cb(proc, rc);
1046     ble_l2cap_sig_proc_free(proc);
1047     /* Silently ignore errors as this is response signal */
1048     return 0;
1049 }
1050 #endif
1051 
ble_l2cap_sig_coc_req_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)1052 static int ble_l2cap_sig_coc_req_rx(uint16_t conn_handle, struct ble_l2cap_sig_hdr *hdr,
1053                                     struct os_mbuf **om)
1054 {
1055     int rc;
1056     struct ble_l2cap_sig_le_con_req *req;
1057     struct os_mbuf *txom;
1058     struct ble_l2cap_sig_le_con_rsp *rsp;
1059     struct ble_l2cap_chan *chan = NULL;
1060     struct ble_hs_conn *conn;
1061     uint16_t scid;
1062     rc = ble_hs_mbuf_pullup_base(om, sizeof(req));
1063     if (rc != 0) {
1064         return rc;
1065     }
1066 
1067     rsp = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_LE_CREDIT_CONNECT_RSP,
1068                                 hdr->identifier, sizeof(*rsp), &txom);
1069     if (!rsp) {
1070         /* Well, nothing smart we can do if there is no memory for response.
1071          * Remote will timeout.
1072          */
1073         return 0;
1074     }
1075 
1076     memset_s(rsp, sizeof(*rsp), 0, sizeof(*rsp));
1077     req = (struct ble_l2cap_sig_le_con_req *)(*om)->om_data;
1078     ble_hs_lock();
1079     conn = ble_hs_conn_find_assert(conn_handle);
1080     /* Verify CID. Note, scid in the request is dcid for out local channel */
1081     scid = le16toh(req->scid);
1082     if (scid < BLE_L2CAP_COC_CID_START || scid > BLE_L2CAP_COC_CID_END) {
1083         rsp->result = htole16(BLE_L2CAP_COC_ERR_INVALID_SOURCE_CID);
1084         ble_hs_unlock();
1085         goto failed;
1086     }
1087 
1088     chan = ble_hs_conn_chan_find_by_dcid(conn, scid);
1089     if (chan) {
1090         rsp->result = htole16(BLE_L2CAP_COC_ERR_SOURCE_CID_ALREADY_USED);
1091         ble_hs_unlock();
1092         goto failed;
1093     }
1094 
1095     rc = ble_l2cap_coc_create_srv_chan(conn, le16toh(req->psm), &chan);
1096     if (rc != 0) {
1097         uint16_t coc_err = ble_l2cap_sig_ble_hs_err2coc_err(rc);
1098         rsp->result = htole16(coc_err);
1099         ble_hs_unlock();
1100         goto failed;
1101     }
1102 
1103     /* Fill up remote configuration. Note MPS is the L2CAP MTU */
1104     chan->dcid = scid;
1105     chan->peer_coc_mps = le16toh(req->mps);
1106     chan->coc_tx.credits = le16toh(req->credits);
1107     chan->coc_tx.mtu = le16toh(req->mtu);
1108     ble_hs_conn_chan_insert(conn, chan);
1109     ble_hs_unlock();
1110     rc = ble_l2cap_event_coc_accept(chan, le16toh(req->mtu));
1111     if (rc != 0) {
1112         uint16_t coc_err = ble_l2cap_sig_ble_hs_err2coc_err(rc);
1113         /* Make sure we do not send disconnect event when removing channel */
1114         chan->cb = NULL;
1115         ble_hs_lock();
1116         conn = ble_hs_conn_find_assert(conn_handle);
1117         ble_hs_conn_delete_chan(conn, chan);
1118         ble_hs_unlock();
1119         rsp->result = htole16(coc_err);
1120         goto failed;
1121     }
1122 
1123     rsp->dcid = htole16(chan->scid);
1124     rsp->credits = htole16(chan->coc_rx.credits);
1125     rsp->mps = htole16(chan->my_coc_mps);
1126     rsp->mtu = htole16(chan->coc_rx.mtu);
1127     rsp->result = htole16(BLE_L2CAP_COC_ERR_CONNECTION_SUCCESS);
1128     rc = ble_l2cap_sig_tx(conn_handle, txom);
1129     if (rc != 0) {
1130         ble_hs_lock();
1131         conn = ble_hs_conn_find_assert(conn_handle);
1132         ble_hs_conn_delete_chan(conn, chan);
1133         ble_hs_unlock();
1134         return 0;
1135     }
1136 
1137     /* Notify user about connection status */
1138     ble_l2cap_event_coc_connected(chan, rc);
1139     return 0;
1140 failed:
1141     ble_l2cap_sig_tx(conn_handle, txom);
1142     return 0;
1143 }
1144 
ble_l2cap_sig_coc_rsp_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)1145 static int ble_l2cap_sig_coc_rsp_rx(uint16_t conn_handle, struct ble_l2cap_sig_hdr *hdr,
1146                                     struct os_mbuf **om)
1147 {
1148     struct ble_l2cap_sig_proc *proc;
1149     struct ble_l2cap_sig_le_con_rsp *rsp;
1150     struct ble_l2cap_chan *chan;
1151     struct ble_hs_conn *conn;
1152     int rc;
1153 #if !BLE_MONITOR
1154     BLE_HS_LOG(DEBUG, "L2CAP LE COC connection response received\n");
1155 #endif
1156     proc = ble_l2cap_sig_proc_extract(conn_handle,
1157                                       BLE_L2CAP_SIG_PROC_OP_CONNECT,
1158                                       hdr->identifier);
1159     if (!proc) {
1160         return 0;
1161     }
1162 
1163     rc = ble_hs_mbuf_pullup_base(om, sizeof(*rsp));
1164     if (rc != 0) {
1165         goto done;
1166     }
1167 
1168     rsp = (struct ble_l2cap_sig_le_con_rsp *)(*om)->om_data;
1169     chan = proc->connect.chan[0];
1170 
1171     if (rsp->result) {
1172         rc = ble_l2cap_sig_coc_err2ble_hs_err(le16toh(rsp->result));
1173         goto done;
1174     }
1175 
1176     /* Fill up remote configuration
1177      * Note MPS is the L2CAP MTU
1178      */
1179     chan->peer_coc_mps = le16toh(rsp->mps);
1180     chan->dcid = le16toh(rsp->dcid);
1181     chan->coc_tx.mtu = le16toh(rsp->mtu);
1182     chan->coc_tx.credits = le16toh(rsp->credits);
1183     ble_hs_lock();
1184     conn = ble_hs_conn_find(conn_handle);
1185     assert(conn != NULL);
1186     ble_hs_conn_chan_insert(conn, chan);
1187     ble_hs_unlock();
1188     rc = 0;
1189 done:
1190     ble_l2cap_sig_coc_connect_cb(proc, rc);
1191     ble_l2cap_sig_proc_free(proc);
1192     /* Silently ignore errors as this is response signal */
1193     return 0;
1194 }
1195 
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)1196 int ble_l2cap_sig_coc_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu,
1197                               struct os_mbuf *sdu_rx,
1198                               ble_l2cap_event_fn *cb, void *cb_arg)
1199 {
1200     struct ble_hs_conn *conn;
1201     struct ble_l2cap_sig_proc *proc;
1202     struct os_mbuf *txom;
1203     struct ble_l2cap_sig_le_con_req *req;
1204     struct ble_l2cap_chan *chan = NULL;
1205     int rc;
1206 
1207     if (!sdu_rx || !cb) {
1208         return BLE_HS_EINVAL;
1209     }
1210 
1211     ble_hs_lock();
1212     conn = ble_hs_conn_find(conn_handle);
1213     if (!conn) {
1214         ble_hs_unlock();
1215         return BLE_HS_ENOTCONN;
1216     }
1217 
1218     chan = ble_l2cap_coc_chan_alloc(conn, psm, mtu, sdu_rx, cb, cb_arg);
1219     if (!chan) {
1220         ble_hs_unlock();
1221         return BLE_HS_ENOMEM;
1222     }
1223 
1224     proc = ble_l2cap_sig_proc_alloc();
1225     if (!proc) {
1226         ble_l2cap_chan_free(conn, chan);
1227         ble_hs_unlock();
1228         return BLE_HS_ENOMEM;
1229     }
1230 
1231     proc->op = BLE_L2CAP_SIG_PROC_OP_CONNECT;
1232     proc->id = ble_l2cap_sig_next_id();
1233     proc->conn_handle = conn_handle;
1234     proc->connect.chan[0] = chan;
1235     proc->connect.chan_cnt = 1;
1236     req = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_LE_CREDIT_CONNECT_REQ, proc->id,
1237                                 sizeof(*req), &txom);
1238     if (!req) {
1239         ble_l2cap_chan_free(conn, chan);
1240         ble_hs_unlock();
1241         rc = BLE_HS_ENOMEM;
1242         /* Goto done to clear proc */
1243         goto done;
1244     }
1245 
1246     req->psm = htole16(psm);
1247     req->scid = htole16(chan->scid);
1248     req->mtu = htole16(chan->coc_rx.mtu);
1249     req->mps = htole16(chan->my_coc_mps);
1250     req->credits = htole16(chan->coc_rx.credits);
1251     ble_hs_unlock();
1252     rc = ble_l2cap_sig_tx(proc->conn_handle, txom);
1253     if (rc != 0) {
1254         ble_hs_lock();
1255         conn = ble_hs_conn_find_assert(conn_handle);
1256         ble_l2cap_chan_free(conn, chan);
1257         ble_hs_unlock();
1258     }
1259 
1260 done:
1261     ble_l2cap_sig_process_status(proc, rc);
1262     return rc;
1263 }
1264 
1265 #if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC)
ble_l2cap_sig_ecoc_connect(uint16_t conn_handle,uint16_t psm,uint16_t mtu,uint8_t num,struct os_mbuf * sdu_rx[],ble_l2cap_event_fn * cb,void * cb_arg)1266 int ble_l2cap_sig_ecoc_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu,
1267                                uint8_t num, struct os_mbuf *sdu_rx[],
1268                                ble_l2cap_event_fn *cb, void *cb_arg)
1269 {
1270     struct ble_hs_conn *conn;
1271     struct ble_l2cap_sig_proc *proc;
1272     struct ble_l2cap_chan *chan = NULL;
1273     struct os_mbuf *txom;
1274     struct ble_l2cap_sig_credit_base_connect_req *req;
1275     int rc;
1276     int i;
1277     int j;
1278 
1279     if (!sdu_rx || !cb) {
1280         return BLE_HS_EINVAL;
1281     }
1282 
1283     ble_hs_lock();
1284     conn = ble_hs_conn_find(conn_handle);
1285     if (!conn) {
1286         ble_hs_unlock();
1287         return BLE_HS_ENOTCONN;
1288     }
1289 
1290     proc = ble_l2cap_sig_proc_alloc();
1291     if (!proc) {
1292         ble_hs_unlock();
1293         return BLE_HS_ENOMEM;
1294     }
1295 
1296     proc->op = BLE_L2CAP_SIG_PROC_OP_CONNECT;
1297     proc->id = ble_l2cap_sig_next_id();
1298     proc->conn_handle = conn_handle;
1299     req = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_CREDIT_CONNECT_REQ, proc->id,
1300                                 sizeof(*req) + num * sizeof(uint16_t), &txom);
1301     if (!req) {
1302         ble_hs_unlock();
1303         rc = BLE_HS_ENOMEM;
1304         /* Goto done to clear proc */
1305         goto done;
1306     }
1307 
1308     for (i = 0; i < num; i++) {
1309         chan = ble_l2cap_coc_chan_alloc(conn, psm, mtu, sdu_rx[i], cb, cb_arg);
1310         if (!chan) {
1311             /* Clear request buffer */
1312             os_mbuf_free_chain(txom);
1313 
1314             for (j = 0; j < i; j++) {
1315                 /* Clear callback to make sure "Disconnected event" to the user */
1316                 chan[j].cb = NULL;
1317                 ble_l2cap_chan_free(conn, proc->connect.chan[j]);
1318             }
1319 
1320             ble_hs_unlock();
1321             rc = BLE_HS_ENOMEM;
1322             goto done;
1323         }
1324 
1325         proc->connect.chan[i] = chan;
1326     }
1327 
1328     proc->connect.chan_cnt = num;
1329     req->psm = htole16(psm);
1330     req->mtu = htole16(chan->coc_rx.mtu);
1331     req->mps = htole16(chan->my_mtu);
1332     req->credits = htole16(chan->coc_rx.credits);
1333 
1334     for (i = 0; i < num; i++) {
1335         req->scids[i] = htole16(proc->connect.chan[i]->scid);
1336     }
1337 
1338     ble_hs_unlock();
1339     rc = ble_l2cap_sig_tx(proc->conn_handle, txom);
1340 done:
1341     ble_l2cap_sig_process_status(proc, rc);
1342     return rc;
1343 }
1344 
ble_l2cap_sig_coc_reconfig(uint16_t conn_handle,struct ble_l2cap_chan * chans[],uint8_t num,uint16_t new_mtu)1345 int ble_l2cap_sig_coc_reconfig(uint16_t conn_handle, struct ble_l2cap_chan *chans[],
1346                                uint8_t num, uint16_t new_mtu)
1347 {
1348     struct ble_hs_conn *conn;
1349     struct ble_l2cap_sig_proc *proc;
1350     struct os_mbuf *txom;
1351     struct ble_l2cap_sig_credit_base_reconfig_req *req;
1352     int rc;
1353     int i;
1354     ble_hs_lock();
1355     conn = ble_hs_conn_find(conn_handle);
1356     if (!conn) {
1357         ble_hs_unlock();
1358         return BLE_HS_ENOTCONN;
1359     }
1360 
1361     proc = ble_l2cap_sig_proc_alloc();
1362     if (!proc) {
1363         ble_hs_unlock();
1364         return BLE_HS_ENOMEM;
1365     }
1366 
1367     for (i = 0; i < num; i++) {
1368         if (ble_hs_conn_chan_exist(conn, chans[i])) {
1369             proc->reconfig.cids[i] = chans[i]->scid;
1370         } else {
1371             ble_hs_unlock();
1372             rc = BLE_HS_ENOMEM;
1373             goto done;
1374         }
1375     }
1376 
1377     proc->op = BLE_L2CAP_SIG_PROC_OP_RECONFIG;
1378     proc->reconfig.cid_cnt = num;
1379     proc->reconfig.new_mtu = new_mtu;
1380     proc->reconfig.new_mps = MYNEWT_VAL(BLE_L2CAP_COC_MPS);
1381     proc->id = ble_l2cap_sig_next_id();
1382     proc->conn_handle = conn_handle;
1383     req = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_CREDIT_RECONFIG_REQ, proc->id,
1384                                 sizeof(*req) + num * sizeof(uint16_t), &txom);
1385     if (!req) {
1386         ble_hs_unlock();
1387         rc = BLE_HS_ENOMEM;
1388         goto done;
1389     }
1390 
1391     /* For now we allow to change CoC MTU only. */
1392     req->mtu = htole16(proc->reconfig.new_mtu);
1393     req->mps = htole16(proc->reconfig.new_mps);
1394 
1395     for (i = 0; i < num; i++) {
1396         req->dcids[i] = htole16(proc->reconfig.cids[i]);
1397     }
1398 
1399     ble_hs_unlock();
1400     rc = ble_l2cap_sig_tx(proc->conn_handle, txom);
1401 done:
1402     ble_l2cap_sig_process_status(proc, rc);
1403     return rc;
1404 }
1405 #endif
1406 
1407 /*****************************************************************************
1408  * $disconnect                                                               *
1409  *****************************************************************************/
1410 
ble_l2cap_sig_disc_req_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)1411 static int ble_l2cap_sig_disc_req_rx(uint16_t conn_handle, struct ble_l2cap_sig_hdr *hdr,
1412                                      struct os_mbuf **om)
1413 {
1414     struct ble_l2cap_sig_disc_req *req;
1415     struct os_mbuf *txom;
1416     struct ble_l2cap_sig_disc_rsp *rsp;
1417     struct ble_l2cap_chan *chan;
1418     struct ble_hs_conn *conn;
1419     int rc;
1420     rc = ble_hs_mbuf_pullup_base(om, sizeof(*req));
1421     if (rc != 0) {
1422         return rc;
1423     }
1424 
1425     rsp = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_DISCONN_RSP, hdr->identifier,
1426                                 sizeof(*rsp), &txom);
1427     if (!rsp) {
1428         /* Well, nothing smart we can do if there is no memory for response.
1429          * Remote will timeout.
1430          */
1431         return 0;
1432     }
1433 
1434     ble_hs_lock();
1435     conn = ble_hs_conn_find_assert(conn_handle);
1436     req = (struct ble_l2cap_sig_disc_req *)(*om)->om_data;
1437     /* Let's find matching channel. Note that destination CID in the request
1438      * is from peer perspective. It is source CID from nimble perspective
1439      */
1440     chan = ble_hs_conn_chan_find_by_scid(conn, le16toh(req->dcid));
1441     if (!chan || (le16toh(req->scid) != chan->dcid)) {
1442         os_mbuf_free_chain(txom);
1443         ble_hs_unlock();
1444         return 0;
1445     }
1446 
1447     /* Note that in the response destination CID is form peer perspective and
1448      * it is source CID from nimble perspective.
1449      */
1450     rsp->dcid = htole16(chan->scid);
1451     rsp->scid = htole16(chan->dcid);
1452     ble_hs_conn_delete_chan(conn, chan);
1453     ble_hs_unlock();
1454     ble_l2cap_sig_tx(conn_handle, txom);
1455     return 0;
1456 }
1457 
ble_l2cap_sig_coc_disconnect_cb(struct ble_l2cap_sig_proc * proc,int status)1458 static void ble_l2cap_sig_coc_disconnect_cb(struct ble_l2cap_sig_proc *proc, int status)
1459 {
1460     struct ble_l2cap_chan *chan;
1461     struct ble_l2cap_event event;
1462     struct ble_hs_conn *conn;
1463 
1464     if (!proc) {
1465         return;
1466     }
1467 
1468     memset_s(&event, sizeof(event), 0, sizeof(event));
1469     chan = proc->disconnect.chan;
1470     if (!chan) {
1471         return;
1472     }
1473 
1474     if (!chan->cb) {
1475         goto done;
1476     }
1477 
1478 done:
1479     ble_hs_lock();
1480     conn = ble_hs_conn_find_assert(chan->conn_handle);
1481     if (conn) {
1482         ble_hs_conn_delete_chan(conn, chan);
1483     } else {
1484         ble_l2cap_chan_free(NULL, chan);
1485     }
1486 
1487     ble_hs_unlock();
1488 }
1489 
ble_l2cap_sig_disc_rsp_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)1490 static int ble_l2cap_sig_disc_rsp_rx(uint16_t conn_handle, struct ble_l2cap_sig_hdr *hdr,
1491                                      struct os_mbuf **om)
1492 {
1493     struct ble_l2cap_sig_disc_rsp *rsp;
1494     struct ble_l2cap_sig_proc *proc;
1495     struct ble_l2cap_chan *chan;
1496     int rc;
1497     proc = ble_l2cap_sig_proc_extract(conn_handle,
1498                                       BLE_L2CAP_SIG_PROC_OP_DISCONNECT,
1499                                       hdr->identifier);
1500     if (!proc) {
1501         return 0;
1502     }
1503 
1504     rc = ble_hs_mbuf_pullup_base(om, sizeof(*rsp));
1505     if (rc != 0) {
1506         goto done;
1507     }
1508 
1509     chan = proc->disconnect.chan;
1510     if (!chan) {
1511         goto done;
1512     }
1513 
1514     rsp = (struct ble_l2cap_sig_disc_rsp *)(*om)->om_data;
1515 
1516     if (chan->dcid != le16toh(rsp->dcid) || chan->scid != le16toh(rsp->scid)) {
1517         /* This response is incorrect, lets wait for timeout */
1518         ble_l2cap_sig_process_status(proc, 0);
1519         return 0;
1520     }
1521 
1522     ble_l2cap_sig_coc_disconnect_cb(proc, rc);
1523 done:
1524     ble_l2cap_sig_proc_free(proc);
1525     return 0;
1526 }
1527 
ble_l2cap_sig_disconnect(struct ble_l2cap_chan * chan)1528 int ble_l2cap_sig_disconnect(struct ble_l2cap_chan *chan)
1529 {
1530     struct os_mbuf *txom;
1531     struct ble_l2cap_sig_disc_req *req;
1532     struct ble_l2cap_sig_proc *proc;
1533     int rc;
1534     proc = ble_l2cap_sig_proc_alloc();
1535     if (proc == NULL) {
1536         return BLE_HS_ENOMEM;
1537     }
1538 
1539     proc->op = BLE_L2CAP_SIG_PROC_OP_DISCONNECT;
1540     proc->id = ble_l2cap_sig_next_id();
1541     proc->conn_handle = chan->conn_handle;
1542     proc->disconnect.chan = chan;
1543     req = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_DISCONN_REQ, proc->id,
1544                                 sizeof(*req), &txom);
1545     if (!req) {
1546         rc = BLE_HS_ENOMEM;
1547         goto done;
1548     }
1549 
1550     req->dcid = htole16(chan->dcid);
1551     req->scid = htole16(chan->scid);
1552     rc = ble_l2cap_sig_tx(proc->conn_handle, txom);
1553 done:
1554     ble_l2cap_sig_process_status(proc, rc);
1555     return rc;
1556 }
1557 
ble_l2cap_sig_le_credits_rx(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)1558 static int ble_l2cap_sig_le_credits_rx(uint16_t conn_handle, struct ble_l2cap_sig_hdr *hdr,
1559                                        struct os_mbuf **om)
1560 {
1561     struct ble_l2cap_sig_le_credits *req;
1562     int rc;
1563     rc = ble_hs_mbuf_pullup_base(om, sizeof(*req));
1564     if (rc != 0) {
1565         return 0;
1566     }
1567 
1568     req = (struct ble_l2cap_sig_le_credits *)(*om)->om_data;
1569     /* Ignore when peer sends zero credits */
1570     if (req->credits == 0) {
1571         return 0;
1572     }
1573 
1574     ble_l2cap_coc_le_credits_update(conn_handle, le16toh(req->scid),
1575                                     le16toh(req->credits));
1576     return 0;
1577 }
1578 
ble_l2cap_sig_le_credits(uint16_t conn_handle,uint16_t scid,uint16_t credits)1579 int ble_l2cap_sig_le_credits(uint16_t conn_handle, uint16_t scid, uint16_t credits)
1580 {
1581     struct ble_l2cap_sig_le_credits *cmd;
1582     struct os_mbuf *txom;
1583     cmd = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_FLOW_CTRL_CREDIT,
1584                                 ble_l2cap_sig_next_id(), sizeof(*cmd), &txom);
1585     if (!cmd) {
1586         return BLE_HS_ENOMEM;
1587     }
1588 
1589     cmd->scid = htole16(scid);
1590     cmd->credits = htole16(credits);
1591     return ble_l2cap_sig_tx(conn_handle, txom);
1592 }
1593 #endif
1594 
ble_l2cap_sig_rx_reject(uint16_t conn_handle,struct ble_l2cap_sig_hdr * hdr,struct os_mbuf ** om)1595 static int ble_l2cap_sig_rx_reject(uint16_t conn_handle,
1596                                    struct ble_l2cap_sig_hdr *hdr,
1597                                    struct os_mbuf **om)
1598 {
1599     struct ble_l2cap_sig_proc *proc;
1600     proc = ble_l2cap_sig_proc_extract(conn_handle,
1601                                       BLE_L2CAP_SIG_PROC_OP_CONNECT,
1602                                       hdr->identifier);
1603     if (!proc) {
1604         return 0;
1605     }
1606 
1607     switch (proc->id) {
1608 #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
1609 
1610         case BLE_L2CAP_SIG_PROC_OP_CONNECT:
1611             ble_l2cap_sig_coc_connect_cb(proc, BLE_HS_EREJECT);
1612             break;
1613 #endif
1614 
1615         default:
1616             break;
1617     }
1618 
1619     ble_l2cap_sig_proc_free(proc);
1620     return 0;
1621 }
1622 /*****************************************************************************
1623  * $misc                                                                     *
1624  *****************************************************************************/
1625 
ble_l2cap_sig_rx(struct ble_l2cap_chan * chan)1626 static int ble_l2cap_sig_rx(struct ble_l2cap_chan *chan)
1627 {
1628     struct ble_l2cap_sig_hdr hdr;
1629     ble_l2cap_sig_rx_fn *rx_cb;
1630     uint16_t conn_handle;
1631     struct os_mbuf **om;
1632     int rc;
1633     conn_handle = chan->conn_handle;
1634     om = &chan->rx_buf;
1635     STATS_INC(ble_l2cap_stats, sig_rx);
1636 #if !BLE_MONITOR
1637     BLE_HS_LOG(DEBUG, "L2CAP - rxed signalling msg: ");
1638     ble_hs_log_mbuf(*om);
1639     BLE_HS_LOG(DEBUG, "\n");
1640 #endif
1641     rc = ble_hs_mbuf_pullup_base(om, BLE_L2CAP_SIG_HDR_SZ);
1642     if (rc != 0) {
1643         return rc;
1644     }
1645 
1646     ble_l2cap_sig_hdr_parse((*om)->om_data, (*om)->om_len, &hdr);
1647     /* Strip L2CAP sig header from the front of the mbuf. */
1648     os_mbuf_adj(*om, BLE_L2CAP_SIG_HDR_SZ);
1649 
1650     if (OS_MBUF_PKTLEN(*om) != hdr.length) {
1651         return BLE_HS_EBADDATA;
1652     }
1653 
1654     rx_cb = ble_l2cap_sig_dispatch_get(hdr.op);
1655     if (rx_cb == NULL) {
1656         rc = BLE_HS_EREJECT;
1657     } else {
1658         rc = rx_cb(conn_handle, &hdr, om);
1659     }
1660 
1661     if (rc) {
1662         ble_l2cap_sig_reject_tx(conn_handle, hdr.identifier,
1663                                 BLE_L2CAP_SIG_ERR_CMD_NOT_UNDERSTOOD,
1664                                 NULL, 0);
1665     }
1666 
1667     return rc;
1668 }
1669 
ble_l2cap_sig_create_chan(uint16_t conn_handle)1670 struct ble_l2cap_chan *ble_l2cap_sig_create_chan(uint16_t conn_handle)
1671 {
1672     struct ble_l2cap_chan *chan;
1673     chan = ble_l2cap_chan_alloc(conn_handle);
1674     if (chan == NULL) {
1675         return NULL;
1676     }
1677 
1678     chan->scid = BLE_L2CAP_CID_SIG;
1679     chan->dcid = BLE_L2CAP_CID_SIG;
1680     chan->my_mtu = BLE_L2CAP_SIG_MTU;
1681     chan->rx_fn = ble_l2cap_sig_rx;
1682     return chan;
1683 }
1684 
1685 /**
1686  * @return                      The number of ticks until the next expiration
1687  *                                  occurs.
1688  */
ble_l2cap_sig_extract_expired(struct ble_l2cap_sig_proc_list * dst_list)1689 static int32_t ble_l2cap_sig_extract_expired(struct ble_l2cap_sig_proc_list *dst_list)
1690 {
1691     struct ble_l2cap_sig_proc *proc;
1692     struct ble_l2cap_sig_proc *prev;
1693     struct ble_l2cap_sig_proc *next;
1694     ble_npl_time_t now;
1695     ble_npl_stime_t next_exp_in;
1696     ble_npl_stime_t time_diff;
1697     now = ble_npl_time_get();
1698     STAILQ_INIT(dst_list);
1699     /* Assume each event is either expired or has infinite duration. */
1700     next_exp_in = BLE_HS_FOREVER;
1701     ble_hs_lock();
1702     prev = NULL;
1703     proc = STAILQ_FIRST(&ble_l2cap_sig_procs);
1704     while (proc != NULL) {
1705         next = STAILQ_NEXT(proc, next);
1706         time_diff = proc->exp_os_ticks - now;
1707         if (time_diff <= 0) {
1708             /* Procedure has expired; move it to the destination list. */
1709             if (prev == NULL) {
1710                 STAILQ_REMOVE_HEAD(&ble_l2cap_sig_procs, next);
1711             } else {
1712                 STAILQ_REMOVE_AFTER(&ble_l2cap_sig_procs, prev, next);
1713             }
1714 
1715             STAILQ_INSERT_TAIL(dst_list, proc, next);
1716         } else {
1717             if (time_diff < next_exp_in) {
1718                 next_exp_in = time_diff;
1719             }
1720         }
1721 
1722         proc = next;
1723     }
1724 
1725     ble_hs_unlock();
1726     return next_exp_in;
1727 }
1728 
ble_l2cap_sig_conn_broken(uint16_t conn_handle,int reason)1729 void ble_l2cap_sig_conn_broken(uint16_t conn_handle, int reason)
1730 {
1731     struct ble_l2cap_sig_proc *proc;
1732 
1733     /* Report a failure for each timed out procedure. */
1734     while ((proc = STAILQ_FIRST(&ble_l2cap_sig_procs)) != NULL) {
1735         switch (proc->op) {
1736             case BLE_L2CAP_SIG_PROC_OP_UPDATE:
1737                 ble_l2cap_sig_update_call_cb(proc, reason);
1738                 break;
1739 #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
1740 
1741             case BLE_L2CAP_SIG_PROC_OP_CONNECT:
1742                 ble_l2cap_sig_coc_connect_cb(proc, reason);
1743                 break;
1744 
1745             case BLE_L2CAP_SIG_PROC_OP_DISCONNECT:
1746                 ble_l2cap_sig_coc_disconnect_cb(proc, reason);
1747                 break;
1748 #if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC)
1749 
1750             case BLE_L2CAP_SIG_PROC_OP_RECONFIG:
1751                 ble_l2cap_sig_coc_reconfig_cb(proc, reason);
1752                 break;
1753 #endif
1754 #endif
1755             default:
1756                 break;
1757         }
1758 
1759         STAILQ_REMOVE_HEAD(&ble_l2cap_sig_procs, next);
1760         ble_l2cap_sig_proc_free(proc);
1761     }
1762 }
1763 
1764 /**
1765  * Terminates expired procedures.
1766  *
1767  * @return                      The number of ticks until this function should
1768  *                                  be called again.
1769  */
ble_l2cap_sig_timer(void)1770 int32_t ble_l2cap_sig_timer(void)
1771 {
1772     struct ble_l2cap_sig_proc_list temp_list;
1773     struct ble_l2cap_sig_proc *proc;
1774     int32_t ticks_until_exp;
1775     /* Remove timed-out procedures from the main list and insert them into a
1776      * temporary list.  This function also calculates the number of ticks until
1777      * the next expiration will occur.
1778      */
1779     ticks_until_exp = ble_l2cap_sig_extract_expired(&temp_list);
1780 
1781     /* Report a failure for each timed out procedure. */
1782     while ((proc = STAILQ_FIRST(&temp_list)) != NULL) {
1783         STATS_INC(ble_l2cap_stats, proc_timeout);
1784 
1785         switch (proc->op) {
1786             case BLE_L2CAP_SIG_PROC_OP_UPDATE:
1787                 ble_l2cap_sig_update_call_cb(proc, BLE_HS_ETIMEOUT);
1788                 break;
1789 #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
1790 
1791             case BLE_L2CAP_SIG_PROC_OP_CONNECT:
1792                 ble_l2cap_sig_coc_connect_cb(proc, BLE_HS_ETIMEOUT);
1793                 break;
1794 
1795             case BLE_L2CAP_SIG_PROC_OP_DISCONNECT:
1796                 ble_l2cap_sig_coc_disconnect_cb(proc, BLE_HS_ETIMEOUT);
1797                 break;
1798 #endif
1799             default:
1800                 break;
1801         }
1802 
1803         STAILQ_REMOVE_HEAD(&temp_list, next);
1804         ble_l2cap_sig_proc_free(proc);
1805     }
1806 
1807     return ticks_until_exp;
1808 }
1809 
ble_l2cap_sig_init(void)1810 int ble_l2cap_sig_init(void)
1811 {
1812     int rc;
1813     STAILQ_INIT(&ble_l2cap_sig_procs);
1814     rc = os_mempool_init(&ble_l2cap_sig_proc_pool,
1815                          MYNEWT_VAL(BLE_L2CAP_SIG_MAX_PROCS),
1816                          sizeof(struct ble_l2cap_sig_proc),
1817                          ble_l2cap_sig_proc_mem,
1818                          "ble_l2cap_sig_proc_pool");
1819     if (rc != 0) {
1820         return rc;
1821     }
1822 
1823     return 0;
1824 }