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 }