Bluetooth: L2CAP: Add bt_l2cap_chan_send
This adds bt_l2cap_chan_send which can be used to send data to dynamic channels resulting in the following trace: < ACL Data TX: Handle 3585 flags 0x01 dlen 17 Channel: 64 len 24 [PSM 128 mode 0] {chan 5} a0 02 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ ff ff ff ff ff ff ff ff ........ Change-Id: I753e301b1125ef21a345ab96470f15bc53a4869a Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
parent
e54d74f696
commit
6a4affef19
2 changed files with 139 additions and 3 deletions
|
@ -83,6 +83,11 @@ struct bt_l2cap_chan_ops {
|
|||
};
|
||||
|
||||
#if defined(CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL)
|
||||
/** @def BT_L2CAP_CHAN_SEND_RESERVE
|
||||
* @brief Headroom needed for outgoing buffers
|
||||
*/
|
||||
#define BT_L2CAP_CHAN_SEND_RESERVE (CONFIG_BLUETOOTH_HCI_SEND_RESERVE + 4 + 4 \
|
||||
+ 2)
|
||||
|
||||
/** @brief L2CAP Server structure. */
|
||||
struct bt_l2cap_server {
|
||||
|
@ -129,6 +134,16 @@ int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
|
|||
*/
|
||||
int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan);
|
||||
|
||||
/** @brief Send data to L2CAP channel
|
||||
*
|
||||
* Send data from buffer to the channel. This procedure may block waiting for
|
||||
* credits to send data therefore it shall be used from a fiber to be able to
|
||||
* receive credits when necessary.
|
||||
*
|
||||
* @return 0 in case of success or negative value in case of error.
|
||||
*/
|
||||
int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf);
|
||||
|
||||
#endif /* CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL */
|
||||
#endif /* CONFIG_BLUETOOTH_CENTRAL || CONFIG_BLUETOOTH_PERIPHERAL */
|
||||
#endif /* __BT_L2CAP_H */
|
||||
|
|
|
@ -66,8 +66,15 @@ static struct bt_l2cap_server *servers;
|
|||
|
||||
/* Pool for outgoing LE signaling packets, MTU is 23 */
|
||||
static struct nano_fifo le_sig;
|
||||
static NET_BUF_POOL(le_pool, CONFIG_BLUETOOTH_MAX_CONN, BT_L2CAP_BUF_SIZE(23),
|
||||
&le_sig, NULL, 0);
|
||||
static NET_BUF_POOL(le_sig_pool, CONFIG_BLUETOOTH_MAX_CONN,
|
||||
BT_L2CAP_BUF_SIZE(L2CAP_LE_MIN_MTU), &le_sig, NULL, 0);
|
||||
|
||||
#if defined(CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL)
|
||||
/* Pool for outgoing LE data packets, MTU is 23 */
|
||||
static struct nano_fifo le_data;
|
||||
static NET_BUF_POOL(le_data_pool, CONFIG_BLUETOOTH_MAX_CONN,
|
||||
BT_L2CAP_BUF_SIZE(L2CAP_LE_MIN_MTU), &le_data, NULL, 0);
|
||||
#endif /* CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL */
|
||||
|
||||
/* L2CAP signalling channel specific context */
|
||||
struct bt_l2cap {
|
||||
|
@ -929,7 +936,10 @@ void bt_l2cap_init(void)
|
|||
.accept = l2cap_accept,
|
||||
};
|
||||
|
||||
net_buf_pool_init(le_pool);
|
||||
net_buf_pool_init(le_sig_pool);
|
||||
#if defined(CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL)
|
||||
net_buf_pool_init(le_data_pool);
|
||||
#endif /* CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL */
|
||||
|
||||
bt_l2cap_fixed_chan_register(&chan);
|
||||
}
|
||||
|
@ -1059,4 +1069,115 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct net_buf *l2cap_chan_create_seg(struct bt_l2cap_chan *chan,
|
||||
struct net_buf *buf,
|
||||
uint16_t len)
|
||||
{
|
||||
struct net_buf *seg;
|
||||
size_t headroom;
|
||||
|
||||
/* Segment if data is bigger than MPS */
|
||||
if (buf->len + (len ? sizeof(len) : 0) > chan->tx.mps) {
|
||||
goto segment;
|
||||
}
|
||||
|
||||
/* Check if original buffer has enough headroom */
|
||||
headroom = sizeof(struct bt_hci_acl_hdr) + sizeof(struct bt_l2cap_hdr);
|
||||
|
||||
if (len) {
|
||||
headroom += sizeof(len);
|
||||
}
|
||||
|
||||
if (net_buf_headroom(buf) >= headroom) {
|
||||
if (len) {
|
||||
/* Push SDU length if set */
|
||||
memcpy(net_buf_push(buf, sizeof(len)),
|
||||
&sys_cpu_to_le16(len), sizeof(len));
|
||||
}
|
||||
return net_buf_ref(buf);
|
||||
}
|
||||
|
||||
segment:
|
||||
seg = bt_l2cap_create_pdu(&le_data);
|
||||
if (!seg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
net_buf_add_le16(seg, len);
|
||||
}
|
||||
|
||||
len = min(min(buf->len, L2CAP_LE_MIN_MTU - (len ? sizeof(len) : 0)),
|
||||
chan->tx.mps);
|
||||
memcpy(net_buf_add(seg, len), buf->data, len);
|
||||
net_buf_pull(buf, len);
|
||||
|
||||
BT_DBG("chan %p seg %p len %u\n", chan, seg, seg->len);
|
||||
|
||||
return seg;
|
||||
}
|
||||
|
||||
static int l2cap_chan_le_send(struct bt_l2cap_chan *chan, struct net_buf *buf,
|
||||
uint16_t len)
|
||||
{
|
||||
/* TODO: Wait for credits */
|
||||
if (!chan->tx.credits) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
chan->tx.credits--;
|
||||
|
||||
buf = l2cap_chan_create_seg(chan, buf, len);
|
||||
if (!buf) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
BT_DBG("chan %p cid 0x%04x len %u credits %u\n", chan, chan->tx.cid,
|
||||
buf->len, chan->tx.credits);
|
||||
|
||||
bt_l2cap_send(chan->conn, chan->tx.cid, buf);
|
||||
|
||||
return buf->len;
|
||||
}
|
||||
|
||||
static int l2cap_chan_le_send_sdu(struct bt_l2cap_chan *chan,
|
||||
struct net_buf *buf)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (buf->len > chan->tx.mtu) {
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
/* Add SDU length for the first segment */
|
||||
len = l2cap_chan_le_send(chan, buf, buf->len);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Send remaining segments */
|
||||
while (buf->len > 0 && buf->len != len) {
|
||||
len = l2cap_chan_le_send(chan, buf, 0);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
net_buf_unref(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
||||
{
|
||||
BT_DBG("chan %p buf %p len %u\n", chan, buf, buf->len);
|
||||
|
||||
if (!buf) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* TODO: Check conn/address type when BR/EDR is introduced */
|
||||
return l2cap_chan_le_send_sdu(chan, buf);
|
||||
}
|
||||
#endif /* CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue