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, ¶ms);
396 if (rc == 0) {
397 /* Application agrees to accept parameters; schedule update. */
398 rc = ble_gap_update_params(conn_handle, ¶ms);
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 }