• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *  http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 #include <string.h>
21 #include <errno.h>
22 #include "securec.h"
23 #include "syscfg/syscfg.h"
24 #include "os/os.h"
25 #include "nimble/ble.h"
26 #include "nimble/hci_common.h"
27 #include "ble_hs_priv.h"
28 #include "ble_l2cap_coc_priv.h"
29 
30 _Static_assert(sizeof(struct ble_l2cap_hdr) == BLE_L2CAP_HDR_SZ,
31                "struct ble_l2cap_hdr must be 4 bytes");
32 
33 struct os_mempool ble_l2cap_chan_pool;
34 
35 static os_membuf_t ble_l2cap_chan_mem[
36                  OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_L2CAP_MAX_CHANS) +
37                                  MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM),
38                                  sizeof(struct ble_l2cap_chan))
39 ];
40 
41 STATS_SECT_DECL(ble_l2cap_stats) ble_l2cap_stats;
42 STATS_NAME_START(ble_l2cap_stats)
STATS_NAME(ble_l2cap_stats,chan_create)43 STATS_NAME(ble_l2cap_stats, chan_create)
44 STATS_NAME(ble_l2cap_stats, chan_delete)
45 STATS_NAME(ble_l2cap_stats, update_init)
46 STATS_NAME(ble_l2cap_stats, update_rx)
47 STATS_NAME(ble_l2cap_stats, update_fail)
48 STATS_NAME(ble_l2cap_stats, proc_timeout)
49 STATS_NAME(ble_l2cap_stats, sig_tx)
50 STATS_NAME(ble_l2cap_stats, sig_rx)
51 STATS_NAME(ble_l2cap_stats, sm_tx)
52 STATS_NAME(ble_l2cap_stats, sm_rx)
53 STATS_NAME_END(ble_l2cap_stats)
54 
55 struct ble_l2cap_chan *ble_l2cap_chan_alloc(uint16_t conn_handle)
56 {
57     struct ble_l2cap_chan *chan;
58     chan = os_memblock_get(&ble_l2cap_chan_pool);
59     if (chan == NULL) {
60         return NULL;
61     }
62 
63     memset_s(chan, sizeof * chan, 0, sizeof * chan);
64     chan->conn_handle = conn_handle;
65     STATS_INC(ble_l2cap_stats, chan_create);
66     return chan;
67 }
68 
ble_l2cap_chan_free(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan)69 void ble_l2cap_chan_free(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
70 {
71     int rc;
72 
73     if (chan == NULL) {
74         return;
75     }
76 
77     os_mbuf_free_chain(chan->rx_buf);
78     ble_l2cap_coc_cleanup_chan(conn, chan);
79 #if MYNEWT_VAL(BLE_HS_DEBUG)
80     memset_s(chan, sizeof * chan, 0xff, sizeof * chan);
81 #endif
82     rc = os_memblock_put(&ble_l2cap_chan_pool, chan);
83     BLE_HS_DBG_ASSERT_EVAL(rc == 0);
84     STATS_INC(ble_l2cap_stats, chan_delete);
85 }
86 
ble_l2cap_is_mtu_req_sent(const struct ble_l2cap_chan * chan)87 bool ble_l2cap_is_mtu_req_sent(const struct ble_l2cap_chan *chan)
88 {
89     return (chan->flags & BLE_L2CAP_CHAN_F_TXED_MTU);
90 }
91 
ble_l2cap_parse_hdr(struct os_mbuf * om,int off,struct ble_l2cap_hdr * l2cap_hdr)92 int ble_l2cap_parse_hdr(struct os_mbuf *om, int off,
93                         struct ble_l2cap_hdr *l2cap_hdr)
94 {
95     int rc;
96     rc = os_mbuf_copydata(om, off, sizeof * l2cap_hdr, l2cap_hdr);
97     if (rc != 0) {
98         return BLE_HS_EMSGSIZE;
99     }
100 
101     l2cap_hdr->len = get_le16(&l2cap_hdr->len);
102     l2cap_hdr->cid = get_le16(&l2cap_hdr->cid);
103     return 0;
104 }
105 
ble_l2cap_prepend_hdr(struct os_mbuf * om,uint16_t cid,uint16_t len)106 struct os_mbuf *ble_l2cap_prepend_hdr(struct os_mbuf *om, uint16_t cid, uint16_t len)
107 {
108     struct ble_l2cap_hdr hdr;
109     put_le16(&hdr.len, len);
110     put_le16(&hdr.cid, cid);
111     om = os_mbuf_prepend_pullup(om, sizeof hdr);
112     if (om == NULL) {
113         return NULL;
114     }
115 
116     memcpy_s(om->om_data, sizeof(om->om_data), &hdr, sizeof hdr);
117     return om;
118 }
119 
ble_l2cap_get_conn_handle(struct ble_l2cap_chan * chan)120 uint16_t ble_l2cap_get_conn_handle(struct ble_l2cap_chan *chan)
121 {
122     if (!chan) {
123         return BLE_HS_CONN_HANDLE_NONE;
124     }
125 
126     return chan->conn_handle;
127 }
128 
ble_l2cap_create_server(uint16_t psm,uint16_t mtu,ble_l2cap_event_fn * cb,void * cb_arg)129 int ble_l2cap_create_server(uint16_t psm, uint16_t mtu,
130                             ble_l2cap_event_fn *cb, void *cb_arg)
131 {
132     return ble_l2cap_coc_create_server(psm, mtu, cb, cb_arg);
133 }
134 
ble_l2cap_connect(uint16_t conn_handle,uint16_t psm,uint16_t mtu,struct os_mbuf * sdu_rx,ble_l2cap_event_fn * cb,void * cb_arg)135 int ble_l2cap_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu,
136                       struct os_mbuf *sdu_rx, ble_l2cap_event_fn *cb, void *cb_arg)
137 {
138     return ble_l2cap_sig_coc_connect(conn_handle, psm, mtu, sdu_rx, cb, cb_arg);
139 }
140 
ble_l2cap_get_chan_info(struct ble_l2cap_chan * chan,struct ble_l2cap_chan_info * chan_info)141 int ble_l2cap_get_chan_info(struct ble_l2cap_chan *chan, struct ble_l2cap_chan_info *chan_info)
142 {
143     if (!chan || !chan_info) {
144         return BLE_HS_EINVAL;
145     }
146 
147     memset_s(chan_info, sizeof(*chan_info), 0, sizeof(*chan_info));
148     chan_info->dcid = chan->dcid;
149     chan_info->scid = chan->scid;
150     chan_info->our_l2cap_mtu = chan->my_mtu;
151     chan_info->peer_l2cap_mtu = chan->peer_mtu;
152 #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
153     chan_info->psm = chan->psm;
154     chan_info->our_coc_mtu = chan->coc_rx.mtu;
155     chan_info->peer_coc_mtu = chan->coc_tx.mtu;
156 #endif
157     return 0;
158 }
159 
ble_l2cap_enhanced_connect(uint16_t conn_handle,uint16_t psm,uint16_t mtu,uint8_t num,struct os_mbuf * sdu_rx[],ble_l2cap_event_fn * cb,void * cb_arg)160 int ble_l2cap_enhanced_connect(uint16_t conn_handle,
161                                uint16_t psm, uint16_t mtu,
162                                uint8_t num, struct os_mbuf *sdu_rx[],
163                                ble_l2cap_event_fn *cb, void *cb_arg)
164 {
165     return ble_l2cap_sig_ecoc_connect(conn_handle, psm, mtu,
166                                       num, sdu_rx, cb, cb_arg);
167 }
168 
ble_l2cap_reconfig(struct ble_l2cap_chan * chans[],uint8_t num,uint16_t new_mtu)169 int ble_l2cap_reconfig(struct ble_l2cap_chan *chans[], uint8_t num, uint16_t new_mtu)
170 {
171     int i;
172     uint16_t conn_handle;
173 
174     if (num == 0 || !chans) {
175         return BLE_HS_EINVAL;
176     }
177 
178     conn_handle = chans[0]->conn_handle;
179 
180     for (i = 1; i < num; i++) {
181         if (conn_handle != chans[i]->conn_handle) {
182             BLE_HS_LOG(ERROR, "All channels should have same conn handle\n");
183             return BLE_HS_EINVAL;
184         }
185     }
186 
187     return ble_l2cap_sig_coc_reconfig(conn_handle, chans, num, new_mtu);
188 }
189 
ble_l2cap_disconnect(struct ble_l2cap_chan * chan)190 int ble_l2cap_disconnect(struct ble_l2cap_chan *chan)
191 {
192     return ble_l2cap_sig_disconnect(chan);
193 }
194 
195 /**
196  * Transmits a packet over an L2CAP channel.  This function only consumes the
197  * supplied mbuf on success.
198  */
ble_l2cap_send(struct ble_l2cap_chan * chan,struct os_mbuf * sdu)199 int ble_l2cap_send(struct ble_l2cap_chan *chan, struct os_mbuf *sdu)
200 {
201     return ble_l2cap_coc_send(chan, sdu);
202 }
203 
ble_l2cap_recv_ready(struct ble_l2cap_chan * chan,struct os_mbuf * sdu_rx)204 int ble_l2cap_recv_ready(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_rx)
205 {
206     return ble_l2cap_coc_recv_ready(chan, sdu_rx);
207 }
208 
ble_l2cap_remove_rx(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan)209 void ble_l2cap_remove_rx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
210 {
211     conn->bhc_rx_chan = NULL;
212     os_mbuf_free_chain(chan->rx_buf);
213     chan->rx_buf = NULL;
214     chan->rx_len = 0;
215 }
216 
ble_l2cap_append_rx(struct ble_l2cap_chan * chan,struct os_mbuf * frag)217 static void ble_l2cap_append_rx(struct ble_l2cap_chan *chan, struct os_mbuf *frag)
218 {
219 #if MYNEWT_VAL(BLE_L2CAP_JOIN_RX_FRAGS)
220     struct os_mbuf *m;
221     /* Copy the data from the incoming fragment into the packet in progress. */
222     m = os_mbuf_pack_chains(chan->rx_buf, frag);
223     assert(m);
224 #else
225     /* Join disabled or append failed due to mbuf shortage.  Just attach the
226      * mbuf to the end of the packet.
227      */
228     os_mbuf_concat(chan->rx_buf, frag);
229 #endif
230 }
231 
ble_l2cap_rx_payload(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan,struct os_mbuf * om,ble_l2cap_rx_fn ** out_rx_cb)232 static int ble_l2cap_rx_payload(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
233                                 struct os_mbuf *om,
234                                 ble_l2cap_rx_fn **out_rx_cb)
235 {
236     int len_diff;
237     int rc;
238 
239     if (chan->rx_buf == NULL) {
240         /* First fragment in packet. */
241         chan->rx_buf = om;
242     } else {
243         /* Continuation of packet in progress. */
244         ble_l2cap_append_rx(chan, om);
245     }
246 
247     /* Determine if packet is fully reassembled. */
248     len_diff = OS_MBUF_PKTLEN(chan->rx_buf) - chan->rx_len;
249     if (len_diff > 0) {
250         /* More data than expected; data corruption. */
251         ble_l2cap_remove_rx(conn, chan);
252         rc = BLE_HS_EBADDATA;
253     } else if (len_diff == 0) {
254         /* All fragments received. */
255         *out_rx_cb = chan->rx_fn;
256         rc = 0;
257     } else {
258         /* More fragments remain. */
259 #if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0
260         conn->bhc_rx_timeout =
261                         ble_npl_time_get() + MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT);
262         ble_hs_timer_resched();
263 #endif
264         rc = BLE_HS_EAGAIN;
265     }
266 
267     return rc;
268 }
269 
ble_l2cap_get_mtu(struct ble_l2cap_chan * chan)270 static uint16_t ble_l2cap_get_mtu(struct ble_l2cap_chan *chan)
271 {
272     if (chan->scid == BLE_L2CAP_CID_ATT) {
273         /* In case of ATT chan->my_mtu keeps preferred MTU which is later
274          * used during exchange MTU procedure. Helper below will gives us actual
275          * MTU on the channel, which is 23 or higher if exchange MTU has been
276          * done
277          */
278         return ble_att_chan_mtu(chan);
279     }
280 
281     return chan->my_mtu;
282 }
283 
284 /**
285  * Processes an incoming L2CAP fragment.
286  *
287  * @param conn                  The connection the L2CAP fragment was sent
288  *                                  over.
289  * @param hci_hdr               The ACL data header that was at the start of
290  *                                  the L2CAP fragment.  This header has been
291  *                                  stripped from the mbuf parameter.
292  * @param om                    An mbuf containing the L2CAP data.  If this is
293  *                                  the first fragment, the L2CAP header is at
294  *                                  the start of the mbuf.  For subsequent
295  *                                  fragments, the mbuf starts with L2CAP
296  *                                  payload data.
297  * @param out_rx_cb             If a full L2CAP packet has been received, a
298  *                                  pointer to the appropriate handler gets
299  *                                  written here.  The caller should pass the
300  *                                  receive buffer to this callback.
301  * @param out_rx_buf            If a full L2CAP packet has been received, this
302  *                                  will point to the entire L2CAP packet.  To
303  *                                  process the packet, pass this buffer to the
304  *                                  receive handler (out_rx_cb).
305  * @param out_reject_cid        Indicates whether an L2CAP Command Reject
306  *                                  command should be sent.  If this equals -1,
307  *                                  no reject should get sent.  Otherwise, the
308  *                                  value indicates the CID that the outgoing
309  *                                  reject should specify.
310  *
311  * @return                      0 if a complete L2CAP packet has been received.
312  *                              BLE_HS_EAGAIN if a partial L2CAP packet has
313  *                                  been received; more fragments are expected.
314  *                              Other value on error.
315  */
ble_l2cap_rx(struct ble_hs_conn * conn,struct hci_data_hdr * hci_hdr,struct os_mbuf * om,ble_l2cap_rx_fn ** out_rx_cb,int * out_reject_cid)316 int ble_l2cap_rx(struct ble_hs_conn *conn,
317                  struct hci_data_hdr *hci_hdr,
318                  struct os_mbuf *om,
319                  ble_l2cap_rx_fn **out_rx_cb,
320                  int *out_reject_cid)
321 {
322     struct ble_l2cap_chan *chan;
323     struct ble_l2cap_hdr l2cap_hdr;
324     uint8_t pb;
325     int rc;
326     *out_reject_cid = -1;
327     pb = BLE_HCI_DATA_PB(hci_hdr->hdh_handle_pb_bc);
328     switch (pb) {
329         case BLE_HCI_PB_FIRST_FLUSH:
330             /* First fragment. */
331             rc = ble_l2cap_parse_hdr(om, 0, &l2cap_hdr);
332             if (rc != 0) {
333                 goto err;
334             }
335 
336             /* Strip L2CAP header from the front of the mbuf. */
337             os_mbuf_adj(om, BLE_L2CAP_HDR_SZ);
338             chan = ble_hs_conn_chan_find_by_scid(conn, l2cap_hdr.cid);
339             if (chan == NULL) {
340                 rc = BLE_HS_ENOENT;
341 
342                 /* Unsupported channel. If the target CID is the black hole
343                  * channel, quietly drop the packet.  Otherwise, send an invalid
344                  * CID response.
345                  */
346                 if (l2cap_hdr.cid != BLE_L2CAP_CID_BLACK_HOLE) {
347                     BLE_HS_LOG(DEBUG, "rx on unknown L2CAP channel: %d\n",
348                                l2cap_hdr.cid);
349                     *out_reject_cid = l2cap_hdr.cid;
350                 }
351 
352                 goto err;
353             }
354 
355             if (chan->rx_buf != NULL) {
356                 /* Previous data packet never completed.  Discard old packet. */
357                 ble_l2cap_remove_rx(conn, chan);
358             }
359 
360             if (l2cap_hdr.len > ble_l2cap_get_mtu(chan)) {
361                 /* More data then we expected on the channel */
362                 rc = BLE_HS_EBADDATA;
363                 goto err;
364             }
365 
366             /* Remember channel and length of L2CAP data for reassembly. */
367             conn->bhc_rx_chan = chan;
368             chan->rx_len = l2cap_hdr.len;
369             break;
370 
371         case BLE_HCI_PB_MIDDLE:
372             chan = conn->bhc_rx_chan;
373             if (chan == NULL || chan->rx_buf == NULL) {
374                 /* Middle fragment without the start.  Discard new packet. */
375                 rc = BLE_HS_EBADDATA;
376                 goto err;
377             }
378 
379             break;
380 
381         default:
382             rc = BLE_HS_EBADDATA;
383             goto err;
384     }
385 
386     rc = ble_l2cap_rx_payload(conn, chan, om, out_rx_cb);
387     om = NULL;
388 
389     if (rc != 0) {
390         goto err;
391     }
392 
393     return 0;
394 err:
395     os_mbuf_free_chain(om);
396     return rc;
397 }
398 
399 /**
400  * Transmits the L2CAP payload contained in the specified mbuf.  The supplied
401  * mbuf is consumed, regardless of the outcome of the function call.
402  *
403  * @param chan                  The L2CAP channel to transmit over.
404  * @param txom                  The data to transmit.
405  *
406  * @return                      0 on success; nonzero on error.
407  */
ble_l2cap_tx(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan,struct os_mbuf * txom)408 int ble_l2cap_tx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
409                  struct os_mbuf *txom)
410 {
411     int rc;
412     txom = ble_l2cap_prepend_hdr(txom, chan->dcid, OS_MBUF_PKTLEN(txom));
413     if (txom == NULL) {
414         return BLE_HS_ENOMEM;
415     }
416 
417     rc = ble_hs_hci_acl_tx(conn, &txom);
418     switch (rc) {
419         case 0:
420             /* Success. */
421             return 0;
422 
423         case BLE_HS_EAGAIN:
424             /* Controller could not accommodate full packet.  Enqueue remainder. */
425             STAILQ_INSERT_TAIL(&conn->bhc_tx_q, OS_MBUF_PKTHDR(txom), omp_next);
426             return 0;
427 
428         default:
429             /* Error. */
430             return rc;
431     }
432 }
433 
ble_l2cap_init(void)434 int ble_l2cap_init(void)
435 {
436     int rc;
437     rc = os_mempool_init(&ble_l2cap_chan_pool,
438                          MYNEWT_VAL(BLE_L2CAP_MAX_CHANS) +
439                          MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM),
440                          sizeof(struct ble_l2cap_chan),
441                          ble_l2cap_chan_mem, "ble_l2cap_chan_pool");
442     if (rc != 0) {
443         return BLE_HS_EOS;
444     }
445 
446     rc = ble_l2cap_sig_init();
447     if (rc != 0) {
448         return rc;
449     }
450 
451     ble_l2cap_coc_init();
452 
453     ble_sm_init();
454 
455     rc = stats_init_and_reg(STATS_HDR(ble_l2cap_stats), STATS_SIZE_INIT_PARMS(ble_l2cap_stats, STATS_SIZE_32),
456         STATS_NAME_INIT_PARMS(ble_l2cap_stats), "ble_l2cap");
457     if (rc != 0) {
458         return BLE_HS_EOS;
459     }
460 
461     return 0;
462 }