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