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, ¶ms);
384*042d53a7SEvalZero if (rc == 0) {
385*042d53a7SEvalZero /* Application agrees to accept parameters; schedule update. */
386*042d53a7SEvalZero rc = ble_gap_update_params(conn_handle, ¶ms);
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