static void l2cap_chan_le_recv_sdu(struct bt_l2cap_le_chan *chan, struct net_buf *buf) { struct net_buf *frag; uint16_t len; BT_DBG("chan %p len %u sdu %zu", chan, buf->len, net_buf_frags_len(chan->_sdu)); if (net_buf_frags_len(chan->_sdu) + buf->len > chan->_sdu_len) { BT_ERR("SDU length mismatch"); bt_l2cap_chan_disconnect(&chan->chan); return; } /* Jump to last fragment */ frag = net_buf_frag_last(chan->_sdu); while (buf->len) { /* Check if there is any space left in the current fragment */ if (!net_buf_tailroom(frag)) { frag = l2cap_alloc_frag(chan); if (!frag) { BT_ERR("Unable to store SDU"); bt_l2cap_chan_disconnect(&chan->chan); return; } } len = min(net_buf_tailroom(frag), buf->len); net_buf_add_mem(frag, buf->data, len); net_buf_pull(buf, len); BT_DBG("frag %p len %u", frag, frag->len); } if (net_buf_frags_len(chan->_sdu) == chan->_sdu_len) { /* Receiving complete SDU, notify channel and reset SDU buf */ chan->chan.ops->recv(&chan->chan, chan->_sdu); net_buf_unref(chan->_sdu); chan->_sdu = NULL; chan->_sdu_len = 0; } l2cap_chan_update_credits(chan); }
static void l2cap_chan_le_recv(struct bt_l2cap_le_chan *chan, struct net_buf *buf) { uint16_t sdu_len; if (!nano_fiber_sem_take(&chan->rx.credits, TICKS_NONE)) { BT_ERR("No credits to receive packet"); bt_l2cap_chan_disconnect(&chan->chan); return; } /* Check if segments already exist */ if (chan->_sdu) { l2cap_chan_le_recv_sdu(chan, buf); return; } sdu_len = net_buf_pull_le16(buf); BT_DBG("chan %p len %u sdu_len %u", chan, buf->len, sdu_len); if (sdu_len > chan->rx.mtu) { BT_ERR("Invalid SDU length"); bt_l2cap_chan_disconnect(&chan->chan); return; } /* Always allocate buffer from the channel if supported. */ if (chan->chan.ops && chan->chan.ops->alloc_buf) { chan->_sdu = chan->chan.ops->alloc_buf(&chan->chan); if (!chan->_sdu) { BT_ERR("Unable to allocate buffer for SDU"); bt_l2cap_chan_disconnect(&chan->chan); return; } chan->_sdu_len = sdu_len; l2cap_chan_le_recv_sdu(chan, buf); return; } chan->chan.ops->recv(&chan->chan, buf); l2cap_chan_update_credits(chan); }
static int bt_disconnect(u32_t mgmt_request, struct net_if *iface, void *data, size_t len) { struct bt_context *ctxt = net_if_get_device(iface)->driver_data; if (!ctxt->ipsp_chan.chan.conn) { NET_ERR("Not connected"); return -ENOTCONN; } /* Release connect reference in case of central/router role */ if (default_conn) { bt_conn_unref(default_conn); default_conn = NULL; } return bt_l2cap_chan_disconnect(&ctxt->ipsp_chan.chan); }
static void le_credits(struct bt_l2cap *l2cap, uint8_t ident, struct net_buf *buf) { struct bt_conn *conn = l2cap->chan.chan.conn; struct bt_l2cap_chan *chan; struct bt_l2cap_le_credits *ev = (void *)buf->data; struct bt_l2cap_le_chan *ch; uint16_t credits, cid; if (buf->len < sizeof(*ev)) { BT_ERR("Too small LE Credits packet size"); return; } cid = sys_le16_to_cpu(ev->cid); credits = sys_le16_to_cpu(ev->credits); BT_DBG("cid 0x%04x credits %u", cid, credits); chan = bt_l2cap_le_lookup_tx_cid(conn, cid); if (!chan) { BT_ERR("Unable to find channel of LE Credits packet"); return; } ch = BT_L2CAP_LE_CHAN(chan); if (k_sem_count_get(&ch->tx.credits) + credits > UINT16_MAX) { BT_ERR("Credits overflow"); bt_l2cap_chan_disconnect(chan); return; } l2cap_chan_tx_give_credits(ch, credits); BT_DBG("chan %p total credits %u", ch, k_sem_count_get(&ch->tx.credits)); l2cap_chan_le_send_resume(ch); }
static void l2cap_chan_le_recv_sdu(struct bt_l2cap_le_chan *chan, struct net_buf *buf) { BT_DBG("chan %p len %u sdu len %u", chan, buf->len, chan->_sdu->len); if (chan->_sdu->len + buf->len > chan->_sdu_len) { BT_ERR("SDU length mismatch"); bt_l2cap_chan_disconnect(&chan->chan); return; } memcpy(net_buf_add(chan->_sdu, buf->len), buf->data, buf->len); if (chan->_sdu->len == chan->_sdu_len) { /* Receiving complete SDU, notify channel and reset SDU buf */ chan->chan.ops->recv(&chan->chan, chan->_sdu); net_buf_unref(chan->_sdu); chan->_sdu = NULL; chan->_sdu_len = 0; } l2cap_chan_update_credits(chan); }