xref: /nrf52832-nimble/packages/NimBLE-latest/nimble/host/src/ble_l2cap_coc.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 #include <string.h>
21*042d53a7SEvalZero #include <errno.h>
22*042d53a7SEvalZero #include "nimble/ble.h"
23*042d53a7SEvalZero #include "ble_hs_priv.h"
24*042d53a7SEvalZero #include "ble_l2cap_priv.h"
25*042d53a7SEvalZero #include "ble_l2cap_coc_priv.h"
26*042d53a7SEvalZero #include "ble_l2cap_sig_priv.h"
27*042d53a7SEvalZero 
28*042d53a7SEvalZero #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
29*042d53a7SEvalZero 
30*042d53a7SEvalZero #define BLE_L2CAP_SDU_SIZE              2
31*042d53a7SEvalZero 
32*042d53a7SEvalZero STAILQ_HEAD(ble_l2cap_coc_srv_list, ble_l2cap_coc_srv);
33*042d53a7SEvalZero 
34*042d53a7SEvalZero static struct ble_l2cap_coc_srv_list ble_l2cap_coc_srvs;
35*042d53a7SEvalZero 
36*042d53a7SEvalZero static os_membuf_t ble_l2cap_coc_srv_mem[
37*042d53a7SEvalZero     OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM),
38*042d53a7SEvalZero                     sizeof (struct ble_l2cap_coc_srv))
39*042d53a7SEvalZero ];
40*042d53a7SEvalZero 
41*042d53a7SEvalZero static struct os_mempool ble_l2cap_coc_srv_pool;
42*042d53a7SEvalZero 
43*042d53a7SEvalZero static void
ble_l2cap_coc_dbg_assert_srv_not_inserted(struct ble_l2cap_coc_srv * srv)44*042d53a7SEvalZero ble_l2cap_coc_dbg_assert_srv_not_inserted(struct ble_l2cap_coc_srv *srv)
45*042d53a7SEvalZero {
46*042d53a7SEvalZero #if MYNEWT_VAL(BLE_HS_DEBUG)
47*042d53a7SEvalZero     struct ble_l2cap_coc_srv *cur;
48*042d53a7SEvalZero 
49*042d53a7SEvalZero     STAILQ_FOREACH(cur, &ble_l2cap_coc_srvs, next) {
50*042d53a7SEvalZero         BLE_HS_DBG_ASSERT(cur != srv);
51*042d53a7SEvalZero     }
52*042d53a7SEvalZero #endif
53*042d53a7SEvalZero }
54*042d53a7SEvalZero 
55*042d53a7SEvalZero static struct ble_l2cap_coc_srv *
ble_l2cap_coc_srv_alloc(void)56*042d53a7SEvalZero ble_l2cap_coc_srv_alloc(void)
57*042d53a7SEvalZero {
58*042d53a7SEvalZero     struct ble_l2cap_coc_srv *srv;
59*042d53a7SEvalZero 
60*042d53a7SEvalZero     srv = os_memblock_get(&ble_l2cap_coc_srv_pool);
61*042d53a7SEvalZero     if (srv != NULL) {
62*042d53a7SEvalZero         memset(srv, 0, sizeof(*srv));
63*042d53a7SEvalZero     }
64*042d53a7SEvalZero 
65*042d53a7SEvalZero     return srv;
66*042d53a7SEvalZero }
67*042d53a7SEvalZero 
68*042d53a7SEvalZero int
ble_l2cap_coc_create_server(uint16_t psm,uint16_t mtu,ble_l2cap_event_fn * cb,void * cb_arg)69*042d53a7SEvalZero ble_l2cap_coc_create_server(uint16_t psm, uint16_t mtu,
70*042d53a7SEvalZero                                         ble_l2cap_event_fn *cb, void *cb_arg)
71*042d53a7SEvalZero {
72*042d53a7SEvalZero     struct ble_l2cap_coc_srv * srv;
73*042d53a7SEvalZero 
74*042d53a7SEvalZero     srv = ble_l2cap_coc_srv_alloc();
75*042d53a7SEvalZero     if (!srv) {
76*042d53a7SEvalZero             return BLE_HS_ENOMEM;
77*042d53a7SEvalZero     }
78*042d53a7SEvalZero 
79*042d53a7SEvalZero     srv->psm = psm;
80*042d53a7SEvalZero     srv->mtu = mtu;
81*042d53a7SEvalZero     srv->cb = cb;
82*042d53a7SEvalZero     srv->cb_arg = cb_arg;
83*042d53a7SEvalZero 
84*042d53a7SEvalZero     ble_l2cap_coc_dbg_assert_srv_not_inserted(srv);
85*042d53a7SEvalZero 
86*042d53a7SEvalZero     STAILQ_INSERT_HEAD(&ble_l2cap_coc_srvs, srv, next);
87*042d53a7SEvalZero 
88*042d53a7SEvalZero     return 0;
89*042d53a7SEvalZero }
90*042d53a7SEvalZero 
91*042d53a7SEvalZero static uint16_t
ble_l2cap_coc_get_cid(void)92*042d53a7SEvalZero ble_l2cap_coc_get_cid(void)
93*042d53a7SEvalZero {
94*042d53a7SEvalZero     static uint16_t next_cid = BLE_L2CAP_COC_CID_START;
95*042d53a7SEvalZero 
96*042d53a7SEvalZero     if (next_cid > BLE_L2CAP_COC_CID_END) {
97*042d53a7SEvalZero             next_cid = BLE_L2CAP_COC_CID_START;
98*042d53a7SEvalZero     }
99*042d53a7SEvalZero 
100*042d53a7SEvalZero     /*TODO: Make it smarter*/
101*042d53a7SEvalZero     return next_cid++;
102*042d53a7SEvalZero }
103*042d53a7SEvalZero 
104*042d53a7SEvalZero static struct ble_l2cap_coc_srv *
ble_l2cap_coc_srv_find(uint16_t psm)105*042d53a7SEvalZero ble_l2cap_coc_srv_find(uint16_t psm)
106*042d53a7SEvalZero {
107*042d53a7SEvalZero     struct ble_l2cap_coc_srv *cur, *srv;
108*042d53a7SEvalZero 
109*042d53a7SEvalZero     srv = NULL;
110*042d53a7SEvalZero     STAILQ_FOREACH(cur, &ble_l2cap_coc_srvs, next) {
111*042d53a7SEvalZero         if (cur->psm == psm) {
112*042d53a7SEvalZero                 srv = cur;
113*042d53a7SEvalZero                 break;
114*042d53a7SEvalZero         }
115*042d53a7SEvalZero     }
116*042d53a7SEvalZero 
117*042d53a7SEvalZero     return srv;
118*042d53a7SEvalZero }
119*042d53a7SEvalZero 
120*042d53a7SEvalZero static void
ble_l2cap_event_coc_received_data(struct ble_l2cap_chan * chan,struct os_mbuf * om)121*042d53a7SEvalZero ble_l2cap_event_coc_received_data(struct ble_l2cap_chan *chan,
122*042d53a7SEvalZero                                   struct os_mbuf *om)
123*042d53a7SEvalZero {
124*042d53a7SEvalZero     struct ble_l2cap_event event;
125*042d53a7SEvalZero 
126*042d53a7SEvalZero     event.type = BLE_L2CAP_EVENT_COC_DATA_RECEIVED;
127*042d53a7SEvalZero     event.receive.conn_handle = chan->conn_handle;
128*042d53a7SEvalZero     event.receive.chan = chan;
129*042d53a7SEvalZero     event.receive.sdu_rx = om;
130*042d53a7SEvalZero 
131*042d53a7SEvalZero     chan->cb(&event, chan->cb_arg);
132*042d53a7SEvalZero }
133*042d53a7SEvalZero 
134*042d53a7SEvalZero static int
ble_l2cap_coc_rx_fn(struct ble_l2cap_chan * chan)135*042d53a7SEvalZero ble_l2cap_coc_rx_fn(struct ble_l2cap_chan *chan)
136*042d53a7SEvalZero {
137*042d53a7SEvalZero     int rc;
138*042d53a7SEvalZero     struct os_mbuf **om;
139*042d53a7SEvalZero     struct ble_l2cap_coc_endpoint *rx;
140*042d53a7SEvalZero     uint16_t om_total;
141*042d53a7SEvalZero 
142*042d53a7SEvalZero     /* Create a shortcut to rx_buf */
143*042d53a7SEvalZero     om = &chan->rx_buf;
144*042d53a7SEvalZero     BLE_HS_DBG_ASSERT(*om != NULL);
145*042d53a7SEvalZero 
146*042d53a7SEvalZero     /* Create a shortcut to rx endpoint */
147*042d53a7SEvalZero     rx = &chan->coc_rx;
148*042d53a7SEvalZero 
149*042d53a7SEvalZero     om_total = OS_MBUF_PKTLEN(*om);
150*042d53a7SEvalZero     rc = ble_hs_mbuf_pullup_base(om, om_total);
151*042d53a7SEvalZero     if (rc != 0) {
152*042d53a7SEvalZero         return rc;
153*042d53a7SEvalZero     }
154*042d53a7SEvalZero 
155*042d53a7SEvalZero     /* Fist LE frame */
156*042d53a7SEvalZero     if (OS_MBUF_PKTLEN(rx->sdu) == 0) {
157*042d53a7SEvalZero         uint16_t sdu_len;
158*042d53a7SEvalZero 
159*042d53a7SEvalZero         sdu_len = get_le16((*om)->om_data);
160*042d53a7SEvalZero         if (sdu_len > rx->mtu) {
161*042d53a7SEvalZero             /* TODO Disconnect?*/
162*042d53a7SEvalZero             BLE_HS_LOG(INFO, "error: sdu_len > rx->mtu (%d>%d)\n",
163*042d53a7SEvalZero                        sdu_len, rx->mtu);
164*042d53a7SEvalZero             return BLE_HS_EBADDATA;
165*042d53a7SEvalZero         }
166*042d53a7SEvalZero 
167*042d53a7SEvalZero         BLE_HS_LOG(DEBUG, "sdu_len=%d, received LE frame=%d, credits=%d\n",
168*042d53a7SEvalZero                    sdu_len, om_total, rx->credits);
169*042d53a7SEvalZero 
170*042d53a7SEvalZero         os_mbuf_adj(*om , BLE_L2CAP_SDU_SIZE);
171*042d53a7SEvalZero 
172*042d53a7SEvalZero         rc = os_mbuf_appendfrom(rx->sdu, *om, 0, om_total - BLE_L2CAP_SDU_SIZE);
173*042d53a7SEvalZero         if (rc != 0) {
174*042d53a7SEvalZero             /* FIXME: User shall give us big enough buffer.
175*042d53a7SEvalZero              * need to handle it better
176*042d53a7SEvalZero              */
177*042d53a7SEvalZero             BLE_HS_LOG(INFO, "Could not append data rc=%d\n", rc);
178*042d53a7SEvalZero             assert(0);
179*042d53a7SEvalZero         }
180*042d53a7SEvalZero 
181*042d53a7SEvalZero         /* In RX case data_offset keeps incoming SDU len */
182*042d53a7SEvalZero         rx->data_offset = sdu_len;
183*042d53a7SEvalZero 
184*042d53a7SEvalZero     } else {
185*042d53a7SEvalZero         BLE_HS_LOG(DEBUG, "Continuation...received %d\n", (*om)->om_len);
186*042d53a7SEvalZero 
187*042d53a7SEvalZero         rc  = os_mbuf_appendfrom(rx->sdu, *om, 0, om_total);
188*042d53a7SEvalZero         if (rc != 0) {
189*042d53a7SEvalZero             /* FIXME: need to handle it better */
190*042d53a7SEvalZero             BLE_HS_LOG(DEBUG, "Could not append data rc=%d\n", rc);
191*042d53a7SEvalZero             assert(0);
192*042d53a7SEvalZero         }
193*042d53a7SEvalZero     }
194*042d53a7SEvalZero 
195*042d53a7SEvalZero     rx->credits--;
196*042d53a7SEvalZero 
197*042d53a7SEvalZero     if (OS_MBUF_PKTLEN(rx->sdu) == rx->data_offset) {
198*042d53a7SEvalZero         struct os_mbuf *sdu_rx = rx->sdu;
199*042d53a7SEvalZero 
200*042d53a7SEvalZero         BLE_HS_LOG(DEBUG, "Received sdu_len=%d, credits left=%d\n",
201*042d53a7SEvalZero                    OS_MBUF_PKTLEN(rx->sdu), rx->credits);
202*042d53a7SEvalZero 
203*042d53a7SEvalZero         /* Lets get back control to os_mbuf to application.
204*042d53a7SEvalZero          * Since it this callback application might want to set new sdu
205*042d53a7SEvalZero          * we need to prepare space for this. Therefore we need sdu_rx
206*042d53a7SEvalZero          */
207*042d53a7SEvalZero         rx->sdu = NULL;
208*042d53a7SEvalZero         rx->data_offset = 0;
209*042d53a7SEvalZero 
210*042d53a7SEvalZero         ble_l2cap_event_coc_received_data(chan, sdu_rx);
211*042d53a7SEvalZero 
212*042d53a7SEvalZero         return 0;
213*042d53a7SEvalZero     }
214*042d53a7SEvalZero 
215*042d53a7SEvalZero     /* If we did not received full SDU and credits are 0 it means
216*042d53a7SEvalZero      * that remote was sending us not fully filled up LE frames.
217*042d53a7SEvalZero      * However, we still have buffer to for next LE Frame so lets give one more
218*042d53a7SEvalZero      * credit to peer so it can send us full SDU
219*042d53a7SEvalZero      */
220*042d53a7SEvalZero     if (rx->credits == 0) {
221*042d53a7SEvalZero         /* Remote did not send full SDU. Lets give him one more credits to do
222*042d53a7SEvalZero          * so since we have still buffer to handle it
223*042d53a7SEvalZero          */
224*042d53a7SEvalZero         rx->credits = 1;
225*042d53a7SEvalZero         ble_l2cap_sig_le_credits(chan->conn_handle, chan->scid, rx->credits);
226*042d53a7SEvalZero     }
227*042d53a7SEvalZero 
228*042d53a7SEvalZero     BLE_HS_LOG(DEBUG, "Received partial sdu_len=%d, credits left=%d\n",
229*042d53a7SEvalZero                OS_MBUF_PKTLEN(rx->sdu), rx->credits);
230*042d53a7SEvalZero 
231*042d53a7SEvalZero     return 0;
232*042d53a7SEvalZero }
233*042d53a7SEvalZero 
234*042d53a7SEvalZero struct ble_l2cap_chan *
ble_l2cap_coc_chan_alloc(uint16_t conn_handle,uint16_t psm,uint16_t mtu,struct os_mbuf * sdu_rx,ble_l2cap_event_fn * cb,void * cb_arg)235*042d53a7SEvalZero ble_l2cap_coc_chan_alloc(uint16_t conn_handle, uint16_t psm, uint16_t mtu,
236*042d53a7SEvalZero                          struct os_mbuf *sdu_rx, ble_l2cap_event_fn *cb,
237*042d53a7SEvalZero                          void *cb_arg)
238*042d53a7SEvalZero {
239*042d53a7SEvalZero     struct ble_l2cap_chan *chan;
240*042d53a7SEvalZero 
241*042d53a7SEvalZero     chan = ble_l2cap_chan_alloc(conn_handle);
242*042d53a7SEvalZero     if (!chan) {
243*042d53a7SEvalZero         return NULL;
244*042d53a7SEvalZero     }
245*042d53a7SEvalZero 
246*042d53a7SEvalZero     chan->psm = psm;
247*042d53a7SEvalZero     chan->cb = cb;
248*042d53a7SEvalZero     chan->cb_arg = cb_arg;
249*042d53a7SEvalZero     chan->scid = ble_l2cap_coc_get_cid();
250*042d53a7SEvalZero     chan->my_mtu = BLE_L2CAP_COC_MTU;
251*042d53a7SEvalZero     chan->rx_fn = ble_l2cap_coc_rx_fn;
252*042d53a7SEvalZero     chan->coc_rx.mtu = mtu;
253*042d53a7SEvalZero     chan->coc_rx.sdu = sdu_rx;
254*042d53a7SEvalZero 
255*042d53a7SEvalZero     /* Number of credits should allow to send full SDU with on given
256*042d53a7SEvalZero      * L2CAP MTU
257*042d53a7SEvalZero      */
258*042d53a7SEvalZero     chan->coc_rx.credits = (mtu + (chan->my_mtu - 1) / 2) / chan->my_mtu;
259*042d53a7SEvalZero 
260*042d53a7SEvalZero     chan->initial_credits = chan->coc_rx.credits;
261*042d53a7SEvalZero     return chan;
262*042d53a7SEvalZero }
263*042d53a7SEvalZero 
264*042d53a7SEvalZero int
ble_l2cap_coc_create_srv_chan(uint16_t conn_handle,uint16_t psm,struct ble_l2cap_chan ** chan)265*042d53a7SEvalZero ble_l2cap_coc_create_srv_chan(uint16_t conn_handle, uint16_t psm,
266*042d53a7SEvalZero                               struct ble_l2cap_chan **chan)
267*042d53a7SEvalZero {
268*042d53a7SEvalZero     struct ble_l2cap_coc_srv *srv;
269*042d53a7SEvalZero 
270*042d53a7SEvalZero     /* Check if there is server registered on this PSM */
271*042d53a7SEvalZero     srv = ble_l2cap_coc_srv_find(psm);
272*042d53a7SEvalZero     if (!srv) {
273*042d53a7SEvalZero         return BLE_HS_ENOTSUP;
274*042d53a7SEvalZero     }
275*042d53a7SEvalZero 
276*042d53a7SEvalZero     *chan = ble_l2cap_coc_chan_alloc(conn_handle, psm, srv->mtu, NULL, srv->cb,
277*042d53a7SEvalZero                                      srv->cb_arg);
278*042d53a7SEvalZero     if (!*chan) {
279*042d53a7SEvalZero         return BLE_HS_ENOMEM;
280*042d53a7SEvalZero     }
281*042d53a7SEvalZero 
282*042d53a7SEvalZero     return 0;
283*042d53a7SEvalZero }
284*042d53a7SEvalZero 
285*042d53a7SEvalZero static void
ble_l2cap_event_coc_disconnected(struct ble_l2cap_chan * chan)286*042d53a7SEvalZero ble_l2cap_event_coc_disconnected(struct ble_l2cap_chan *chan)
287*042d53a7SEvalZero {
288*042d53a7SEvalZero     struct ble_l2cap_event event = { };
289*042d53a7SEvalZero 
290*042d53a7SEvalZero     /* FIXME */
291*042d53a7SEvalZero     if (!chan->cb) {
292*042d53a7SEvalZero         return;
293*042d53a7SEvalZero     }
294*042d53a7SEvalZero 
295*042d53a7SEvalZero     event.type = BLE_L2CAP_EVENT_COC_DISCONNECTED;
296*042d53a7SEvalZero     event.disconnect.conn_handle = chan->conn_handle;
297*042d53a7SEvalZero     event.disconnect.chan = chan;
298*042d53a7SEvalZero 
299*042d53a7SEvalZero     chan->cb(&event, chan->cb_arg);
300*042d53a7SEvalZero }
301*042d53a7SEvalZero 
302*042d53a7SEvalZero void
ble_l2cap_coc_cleanup_chan(struct ble_l2cap_chan * chan)303*042d53a7SEvalZero ble_l2cap_coc_cleanup_chan(struct ble_l2cap_chan *chan)
304*042d53a7SEvalZero {
305*042d53a7SEvalZero     /* PSM 0 is used for fixed channels. */
306*042d53a7SEvalZero     if (chan->psm == 0) {
307*042d53a7SEvalZero             return;
308*042d53a7SEvalZero     }
309*042d53a7SEvalZero 
310*042d53a7SEvalZero     ble_l2cap_event_coc_disconnected(chan);
311*042d53a7SEvalZero 
312*042d53a7SEvalZero     os_mbuf_free_chain(chan->coc_rx.sdu);
313*042d53a7SEvalZero     os_mbuf_free_chain(chan->coc_tx.sdu);
314*042d53a7SEvalZero }
315*042d53a7SEvalZero 
316*042d53a7SEvalZero static int
ble_l2cap_coc_continue_tx(struct ble_l2cap_chan * chan)317*042d53a7SEvalZero ble_l2cap_coc_continue_tx(struct ble_l2cap_chan *chan)
318*042d53a7SEvalZero {
319*042d53a7SEvalZero     struct ble_l2cap_coc_endpoint *tx;
320*042d53a7SEvalZero     uint16_t len;
321*042d53a7SEvalZero     uint16_t left_to_send;
322*042d53a7SEvalZero     struct os_mbuf *txom;
323*042d53a7SEvalZero     struct ble_hs_conn *conn;
324*042d53a7SEvalZero     uint16_t sdu_size_offset;
325*042d53a7SEvalZero     int rc;
326*042d53a7SEvalZero 
327*042d53a7SEvalZero     /* If there is no data to send, just return success */
328*042d53a7SEvalZero     tx = &chan->coc_tx;
329*042d53a7SEvalZero     if (!tx->sdu) {
330*042d53a7SEvalZero         return 0;
331*042d53a7SEvalZero     }
332*042d53a7SEvalZero 
333*042d53a7SEvalZero     while (tx->credits) {
334*042d53a7SEvalZero         sdu_size_offset = 0;
335*042d53a7SEvalZero 
336*042d53a7SEvalZero         BLE_HS_LOG(DEBUG, "Available credits %d\n", tx->credits);
337*042d53a7SEvalZero 
338*042d53a7SEvalZero         /* lets calculate data we are going to send */
339*042d53a7SEvalZero         left_to_send = OS_MBUF_PKTLEN(tx->sdu) - tx->data_offset;
340*042d53a7SEvalZero 
341*042d53a7SEvalZero         if (tx->data_offset == 0) {
342*042d53a7SEvalZero             sdu_size_offset = BLE_L2CAP_SDU_SIZE;
343*042d53a7SEvalZero             left_to_send += sdu_size_offset;
344*042d53a7SEvalZero         }
345*042d53a7SEvalZero 
346*042d53a7SEvalZero         /* Take into account peer MTU */
347*042d53a7SEvalZero         len = min(left_to_send, chan->peer_mtu);
348*042d53a7SEvalZero 
349*042d53a7SEvalZero         /* Prepare packet */
350*042d53a7SEvalZero         txom = ble_hs_mbuf_l2cap_pkt();
351*042d53a7SEvalZero         if (!txom) {
352*042d53a7SEvalZero             BLE_HS_LOG(DEBUG, "Could not prepare l2cap packet len %d", len);
353*042d53a7SEvalZero             rc = BLE_HS_ENOMEM;
354*042d53a7SEvalZero             goto failed;
355*042d53a7SEvalZero         }
356*042d53a7SEvalZero 
357*042d53a7SEvalZero         if (tx->data_offset == 0) {
358*042d53a7SEvalZero             /* First packet needs SDU len first. Left to send */
359*042d53a7SEvalZero             uint16_t l = htole16(OS_MBUF_PKTLEN(tx->sdu));
360*042d53a7SEvalZero 
361*042d53a7SEvalZero             BLE_HS_LOG(DEBUG, "Sending SDU len=%d\n", OS_MBUF_PKTLEN(tx->sdu));
362*042d53a7SEvalZero             rc = os_mbuf_append(txom, &l, sizeof(uint16_t));
363*042d53a7SEvalZero             if (rc) {
364*042d53a7SEvalZero                 BLE_HS_LOG(DEBUG, "Could not append data rc=%d", rc);
365*042d53a7SEvalZero                 goto failed;
366*042d53a7SEvalZero             }
367*042d53a7SEvalZero         }
368*042d53a7SEvalZero 
369*042d53a7SEvalZero         /* In data_offset we keep track on what we already sent. Need to remember
370*042d53a7SEvalZero          * that for first packet we need to decrease data size by 2 bytes for sdu
371*042d53a7SEvalZero          * size
372*042d53a7SEvalZero          */
373*042d53a7SEvalZero         rc = os_mbuf_appendfrom(txom, tx->sdu, tx->data_offset,
374*042d53a7SEvalZero                                 len - sdu_size_offset);
375*042d53a7SEvalZero         if (rc) {
376*042d53a7SEvalZero             BLE_HS_LOG(DEBUG, "Could not append data rc=%d", rc);
377*042d53a7SEvalZero            goto failed;
378*042d53a7SEvalZero         }
379*042d53a7SEvalZero 
380*042d53a7SEvalZero         ble_hs_lock();
381*042d53a7SEvalZero         conn = ble_hs_conn_find_assert(chan->conn_handle);
382*042d53a7SEvalZero         rc = ble_l2cap_tx(conn, chan, txom);
383*042d53a7SEvalZero         ble_hs_unlock();
384*042d53a7SEvalZero 
385*042d53a7SEvalZero         if (rc) {
386*042d53a7SEvalZero           /* txom is consumed by l2cap */
387*042d53a7SEvalZero           txom = NULL;
388*042d53a7SEvalZero           goto failed;
389*042d53a7SEvalZero         } else {
390*042d53a7SEvalZero             tx->credits --;
391*042d53a7SEvalZero             tx->data_offset += len - sdu_size_offset;
392*042d53a7SEvalZero         }
393*042d53a7SEvalZero 
394*042d53a7SEvalZero         BLE_HS_LOG(DEBUG, "Sent %d bytes, credits=%d, to send %d bytes \n",
395*042d53a7SEvalZero                   len, tx->credits, OS_MBUF_PKTLEN(tx->sdu)- tx->data_offset );
396*042d53a7SEvalZero 
397*042d53a7SEvalZero         if (tx->data_offset == OS_MBUF_PKTLEN(tx->sdu)) {
398*042d53a7SEvalZero                 BLE_HS_LOG(DEBUG, "Complete package sent");
399*042d53a7SEvalZero                 os_mbuf_free_chain(tx->sdu);
400*042d53a7SEvalZero                 tx->sdu = 0;
401*042d53a7SEvalZero                 tx->data_offset = 0;
402*042d53a7SEvalZero                 break;
403*042d53a7SEvalZero         }
404*042d53a7SEvalZero     }
405*042d53a7SEvalZero 
406*042d53a7SEvalZero     return 0;
407*042d53a7SEvalZero 
408*042d53a7SEvalZero failed:
409*042d53a7SEvalZero     os_mbuf_free_chain(tx->sdu);
410*042d53a7SEvalZero     tx->sdu = NULL;
411*042d53a7SEvalZero     os_mbuf_free_chain(txom);
412*042d53a7SEvalZero 
413*042d53a7SEvalZero     return rc;
414*042d53a7SEvalZero }
415*042d53a7SEvalZero 
416*042d53a7SEvalZero void
ble_l2cap_coc_le_credits_update(uint16_t conn_handle,uint16_t dcid,uint16_t credits)417*042d53a7SEvalZero ble_l2cap_coc_le_credits_update(uint16_t conn_handle, uint16_t dcid,
418*042d53a7SEvalZero                                 uint16_t credits)
419*042d53a7SEvalZero {
420*042d53a7SEvalZero     struct ble_hs_conn *conn;
421*042d53a7SEvalZero     struct ble_l2cap_chan *chan;
422*042d53a7SEvalZero 
423*042d53a7SEvalZero     /* remote updated its credits */
424*042d53a7SEvalZero     ble_hs_lock();
425*042d53a7SEvalZero     conn = ble_hs_conn_find(conn_handle);
426*042d53a7SEvalZero     if (!conn) {
427*042d53a7SEvalZero         ble_hs_unlock();
428*042d53a7SEvalZero         return;
429*042d53a7SEvalZero     }
430*042d53a7SEvalZero 
431*042d53a7SEvalZero     chan = ble_hs_conn_chan_find_by_dcid(conn, dcid);
432*042d53a7SEvalZero     if (!chan) {
433*042d53a7SEvalZero         ble_hs_unlock();
434*042d53a7SEvalZero         return;
435*042d53a7SEvalZero     }
436*042d53a7SEvalZero 
437*042d53a7SEvalZero     if (chan->coc_tx.credits + credits > 0xFFFF) {
438*042d53a7SEvalZero         BLE_HS_LOG(INFO, "LE CoC credits overflow...disconnecting\n");
439*042d53a7SEvalZero         ble_hs_unlock();
440*042d53a7SEvalZero         ble_l2cap_sig_disconnect(chan);
441*042d53a7SEvalZero         return;
442*042d53a7SEvalZero     }
443*042d53a7SEvalZero 
444*042d53a7SEvalZero     chan->coc_tx.credits += credits;
445*042d53a7SEvalZero     ble_hs_unlock();
446*042d53a7SEvalZero     ble_l2cap_coc_continue_tx(chan);
447*042d53a7SEvalZero }
448*042d53a7SEvalZero 
449*042d53a7SEvalZero void
ble_l2cap_coc_recv_ready(struct ble_l2cap_chan * chan,struct os_mbuf * sdu_rx)450*042d53a7SEvalZero ble_l2cap_coc_recv_ready(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_rx)
451*042d53a7SEvalZero {
452*042d53a7SEvalZero     struct ble_hs_conn *conn;
453*042d53a7SEvalZero     struct ble_l2cap_chan *c;
454*042d53a7SEvalZero 
455*042d53a7SEvalZero     chan->coc_rx.sdu = sdu_rx;
456*042d53a7SEvalZero 
457*042d53a7SEvalZero     ble_hs_lock();
458*042d53a7SEvalZero     conn = ble_hs_conn_find_assert(chan->conn_handle);
459*042d53a7SEvalZero     c = ble_hs_conn_chan_find_by_scid(conn, chan->scid);
460*042d53a7SEvalZero     if (!c) {
461*042d53a7SEvalZero         ble_hs_unlock();
462*042d53a7SEvalZero         return;
463*042d53a7SEvalZero     }
464*042d53a7SEvalZero 
465*042d53a7SEvalZero     /* We want to back only that much credits which remote side is missing
466*042d53a7SEvalZero      * to be able to send complete SDU.
467*042d53a7SEvalZero      */
468*042d53a7SEvalZero     if (chan->coc_rx.credits < c->initial_credits) {
469*042d53a7SEvalZero         ble_hs_unlock();
470*042d53a7SEvalZero         ble_l2cap_sig_le_credits(chan->conn_handle, chan->scid,
471*042d53a7SEvalZero                                  c->initial_credits - chan->coc_rx.credits);
472*042d53a7SEvalZero         ble_hs_lock();
473*042d53a7SEvalZero         chan->coc_rx.credits = c->initial_credits;
474*042d53a7SEvalZero     }
475*042d53a7SEvalZero 
476*042d53a7SEvalZero     ble_hs_unlock();
477*042d53a7SEvalZero }
478*042d53a7SEvalZero 
479*042d53a7SEvalZero /**
480*042d53a7SEvalZero  * Transmits a packet over a connection-oriented channel.  This function only
481*042d53a7SEvalZero  * consumes the supplied mbuf on success.
482*042d53a7SEvalZero  */
483*042d53a7SEvalZero int
ble_l2cap_coc_send(struct ble_l2cap_chan * chan,struct os_mbuf * sdu_tx)484*042d53a7SEvalZero ble_l2cap_coc_send(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_tx)
485*042d53a7SEvalZero {
486*042d53a7SEvalZero     struct ble_l2cap_coc_endpoint *tx;
487*042d53a7SEvalZero 
488*042d53a7SEvalZero     tx = &chan->coc_tx;
489*042d53a7SEvalZero 
490*042d53a7SEvalZero     if (tx->sdu) {
491*042d53a7SEvalZero         return BLE_HS_EBUSY;
492*042d53a7SEvalZero     }
493*042d53a7SEvalZero 
494*042d53a7SEvalZero     if (OS_MBUF_PKTLEN(sdu_tx) > tx->mtu) {
495*042d53a7SEvalZero         return BLE_HS_EBADDATA;
496*042d53a7SEvalZero     }
497*042d53a7SEvalZero 
498*042d53a7SEvalZero     tx->sdu = sdu_tx;
499*042d53a7SEvalZero 
500*042d53a7SEvalZero     return ble_l2cap_coc_continue_tx(chan);
501*042d53a7SEvalZero }
502*042d53a7SEvalZero 
503*042d53a7SEvalZero int
ble_l2cap_get_scid(struct ble_l2cap_chan * chan)504*042d53a7SEvalZero ble_l2cap_get_scid(struct ble_l2cap_chan *chan)
505*042d53a7SEvalZero {
506*042d53a7SEvalZero     if (!chan) {
507*042d53a7SEvalZero         return 0;
508*042d53a7SEvalZero     }
509*042d53a7SEvalZero 
510*042d53a7SEvalZero     return chan->scid;
511*042d53a7SEvalZero }
512*042d53a7SEvalZero 
513*042d53a7SEvalZero int
ble_l2cap_get_dcid(struct ble_l2cap_chan * chan)514*042d53a7SEvalZero ble_l2cap_get_dcid(struct ble_l2cap_chan *chan)
515*042d53a7SEvalZero {
516*042d53a7SEvalZero     if (!chan) {
517*042d53a7SEvalZero         return 0;
518*042d53a7SEvalZero     }
519*042d53a7SEvalZero 
520*042d53a7SEvalZero     return chan->dcid;
521*042d53a7SEvalZero }
522*042d53a7SEvalZero 
523*042d53a7SEvalZero int
ble_l2cap_get_our_mtu(struct ble_l2cap_chan * chan)524*042d53a7SEvalZero ble_l2cap_get_our_mtu(struct ble_l2cap_chan *chan)
525*042d53a7SEvalZero {
526*042d53a7SEvalZero     if (!chan) {
527*042d53a7SEvalZero         return 0;
528*042d53a7SEvalZero     }
529*042d53a7SEvalZero 
530*042d53a7SEvalZero     return chan->my_mtu;
531*042d53a7SEvalZero }
532*042d53a7SEvalZero 
533*042d53a7SEvalZero int
ble_l2cap_get_peer_mtu(struct ble_l2cap_chan * chan)534*042d53a7SEvalZero ble_l2cap_get_peer_mtu(struct ble_l2cap_chan *chan)
535*042d53a7SEvalZero {
536*042d53a7SEvalZero     if (!chan) {
537*042d53a7SEvalZero         return 0;
538*042d53a7SEvalZero     }
539*042d53a7SEvalZero 
540*042d53a7SEvalZero     return chan->peer_mtu;
541*042d53a7SEvalZero }
542*042d53a7SEvalZero 
543*042d53a7SEvalZero int
ble_l2cap_coc_init(void)544*042d53a7SEvalZero ble_l2cap_coc_init(void)
545*042d53a7SEvalZero {
546*042d53a7SEvalZero     STAILQ_INIT(&ble_l2cap_coc_srvs);
547*042d53a7SEvalZero 
548*042d53a7SEvalZero     return os_mempool_init(&ble_l2cap_coc_srv_pool,
549*042d53a7SEvalZero                          MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM),
550*042d53a7SEvalZero                          sizeof (struct ble_l2cap_coc_srv),
551*042d53a7SEvalZero                          ble_l2cap_coc_srv_mem,
552*042d53a7SEvalZero                          "ble_l2cap_coc_srv_pool");
553*042d53a7SEvalZero }
554*042d53a7SEvalZero 
555*042d53a7SEvalZero #endif
556