Skip to content

Commit aec071e

Browse files
VudentzAnas Nashif
authored andcommitted
Bluetooth: L2CAP: Add TX queueing for LE CoC
This allows to queue buffer to sent later in case it runs out of credits so it no longer blocks the caller thread. Jira: ZEP-1776 Change-Id: Ifa9b412f98889b50c0b889655d910520d11a4718 Signed-off-by: Luiz Augusto von Dentz <[email protected]>
1 parent d111394 commit aec071e

File tree

2 files changed

+73
-13
lines changed

2 files changed

+73
-13
lines changed

include/bluetooth/l2cap.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ struct bt_l2cap_le_chan {
110110
struct bt_l2cap_le_endpoint rx;
111111
/** Channel Transmission Endpoint */
112112
struct bt_l2cap_le_endpoint tx;
113+
/** Channel Transmission queue */
114+
struct k_fifo tx_queue;
115+
/** Channel Pending Transmission buffer */
116+
struct net_buf *tx_buf;
113117
/** Segment SDU packet from upper layer */
114118
struct net_buf *_sdu;
115119
uint16_t _sdu_len;

subsys/bluetooth/host/l2cap.c

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,7 @@ static void l2cap_chan_tx_init(struct bt_l2cap_le_chan *chan)
648648

649649
memset(&chan->tx, 0, sizeof(chan->tx));
650650
k_sem_init(&chan->tx.credits, 0, UINT_MAX);
651+
k_fifo_init(&chan->tx_queue);
651652
}
652653

653654
static void l2cap_chan_tx_give_credits(struct bt_l2cap_le_chan *chan,
@@ -673,16 +674,17 @@ static void l2cap_chan_rx_give_credits(struct bt_l2cap_le_chan *chan,
673674
static void l2cap_chan_destroy(struct bt_l2cap_chan *chan)
674675
{
675676
struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan);
677+
struct net_buf *buf;
676678

677679
BT_DBG("chan %p cid 0x%04x", ch, ch->rx.cid);
678680

679681
/* Cancel ongoing work */
680682
k_delayed_work_cancel(&chan->rtx_work);
681683

682-
/* There could be a writer waiting for credits so return a dummy credit
683-
* to wake it up.
684-
*/
685-
l2cap_chan_tx_give_credits(ch, 1);
684+
/* Remove buffers on the TX queue */
685+
while ((buf = net_buf_get(&ch->tx_queue, K_NO_WAIT))) {
686+
net_buf_unref(buf);
687+
}
686688

687689
/* Destroy segmented SDU if it exists */
688690
if (ch->_sdu) {
@@ -1084,12 +1086,12 @@ static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch, struct net_buf *buf,
10841086
}
10851087

10861088
static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch,
1087-
struct net_buf *buf)
1089+
struct net_buf *buf, int sent)
10881090
{
1089-
int ret, sent, total_len;
1091+
int ret, total_len;
10901092
struct net_buf *frag;
10911093

1092-
total_len = net_buf_frags_len(buf);
1094+
total_len = net_buf_frags_len(buf) + sent;
10931095

10941096
if (total_len > ch->tx.mtu) {
10951097
return -EMSGSIZE;
@@ -1100,21 +1102,34 @@ static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch,
11001102
frag = frag->frags;
11011103
}
11021104

1103-
/* Add SDU length for the first segment */
1104-
ret = l2cap_chan_le_send(ch, frag, BT_L2CAP_SDU_HDR_LEN);
1105-
if (ret < 0) {
1106-
return ret;
1105+
if (!sent) {
1106+
/* Add SDU length for the first segment */
1107+
sent = l2cap_chan_le_send(ch, frag, BT_L2CAP_SDU_HDR_LEN);
1108+
if (sent < 0) {
1109+
if (sent == -EAGAIN) {
1110+
sent = 0;
1111+
/* Store sent data into user_data */
1112+
memcpy(net_buf_user_data(buf), &sent,
1113+
sizeof(sent));
1114+
}
1115+
return sent;
1116+
}
11071117
}
11081118

11091119
/* Send remaining segments */
1110-
for (sent = ret; sent < total_len; sent += ret) {
1120+
for (ret = 0; sent < total_len; sent += ret) {
11111121
/* Proceed to next fragment */
11121122
if (!frag->len) {
11131123
frag = net_buf_frag_del(buf, frag);
11141124
}
11151125

11161126
ret = l2cap_chan_le_send(ch, frag, 0);
11171127
if (ret < 0) {
1128+
if (ret == -EAGAIN) {
1129+
/* Store sent data into user_data */
1130+
memcpy(net_buf_user_data(buf), &sent,
1131+
sizeof(sent));
1132+
}
11181133
return ret;
11191134
}
11201135
}
@@ -1126,6 +1141,40 @@ static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch,
11261141
return ret;
11271142
}
11281143

1144+
static struct net_buf *l2cap_chan_le_get_tx_buf(struct bt_l2cap_le_chan *ch)
1145+
{
1146+
struct net_buf *buf;
1147+
1148+
/* Return current buffer */
1149+
if (ch->tx_buf) {
1150+
buf = ch->tx_buf;
1151+
ch->tx_buf = NULL;
1152+
return buf;
1153+
}
1154+
1155+
return net_buf_get(&ch->tx_queue, K_NO_WAIT);
1156+
}
1157+
1158+
static void l2cap_chan_le_send_resume(struct bt_l2cap_le_chan *ch)
1159+
{
1160+
struct net_buf *buf;
1161+
1162+
/* Resume tx in case there are buffers in the queue */
1163+
while ((buf = l2cap_chan_le_get_tx_buf(ch))) {
1164+
int sent = *((int *)net_buf_user_data(buf));
1165+
1166+
BT_DBG("buf %p sent %u", buf, sent);
1167+
1168+
sent = l2cap_chan_le_send_sdu(ch, buf, sent);
1169+
if (sent < 0) {
1170+
if (sent == -EAGAIN) {
1171+
ch->tx_buf = buf;
1172+
}
1173+
break;
1174+
}
1175+
}
1176+
}
1177+
11291178
static void le_credits(struct bt_l2cap *l2cap, uint8_t ident,
11301179
struct net_buf *buf)
11311180
{
@@ -1163,6 +1212,8 @@ static void le_credits(struct bt_l2cap *l2cap, uint8_t ident,
11631212

11641213
BT_DBG("chan %p total credits %u", ch,
11651214
k_sem_count_get(&ch->tx.credits));
1215+
1216+
l2cap_chan_le_send_resume(ch);
11661217
}
11671218

11681219
static void reject_cmd(struct bt_l2cap *l2cap, uint8_t ident,
@@ -1646,8 +1697,13 @@ int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
16461697
return bt_l2cap_br_chan_send(chan, buf);
16471698
}
16481699

1649-
err = l2cap_chan_le_send_sdu(BT_L2CAP_LE_CHAN(chan), buf);
1700+
err = l2cap_chan_le_send_sdu(BT_L2CAP_LE_CHAN(chan), buf, 0);
16501701
if (err < 0) {
1702+
if (err == -EAGAIN) {
1703+
/* Queue buffer to be sent later */
1704+
net_buf_put(&(BT_L2CAP_LE_CHAN(chan))->tx_queue, buf);
1705+
return *((int *)net_buf_user_data(buf));
1706+
}
16511707
BT_ERR("failed to send message %d", err);
16521708
}
16531709

0 commit comments

Comments
 (0)