Bluetooth: L2CAP_BR: Sending multiple SDU at same time
Improve the retransmission and flow control to support sending multiple SDU at the same time if the TX windows is not full. Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
This commit is contained in:
parent
88bd18b6cd
commit
5fd98e10bd
2 changed files with 189 additions and 156 deletions
|
@ -374,7 +374,11 @@ struct bt_l2cap_br_window {
|
|||
/** srej flag */
|
||||
bool srej;
|
||||
/* Save PDU state */
|
||||
struct net_buf_simple_state pdu;
|
||||
struct net_buf_simple_state sdu_state;
|
||||
/** @internal Holds the sending buffer. */
|
||||
struct net_buf *sdu;
|
||||
/** @internal Total length of TX SDU */
|
||||
uint16_t sdu_total_len;
|
||||
};
|
||||
|
||||
/** @brief BREDR L2CAP Channel structure. */
|
||||
|
@ -403,8 +407,8 @@ struct bt_l2cap_br_chan {
|
|||
sys_snode_t _pdu_ready;
|
||||
/** @internal To be used with @ref bt_conn.upper_data_ready */
|
||||
atomic_t _pdu_ready_lock;
|
||||
/** @internal Queue of net bufs not yet sent to lower layer */
|
||||
struct k_fifo _pdu_tx_queue;
|
||||
/** @internal List of net bufs not yet sent to lower layer */
|
||||
sys_slist_t _pdu_tx_queue;
|
||||
|
||||
#if defined(CONFIG_BT_L2CAP_RET_FC) || defined(__DOXYGEN__)
|
||||
/** @internal Total length of TX SDU */
|
||||
|
|
|
@ -149,7 +149,6 @@ enum {
|
|||
L2CAP_FLAG_FIXED_CONNECTED, /* fixed connected */
|
||||
|
||||
/* Retransmition and flow control flags*/
|
||||
L2CAP_FLAG_SDU_SENDING, /* SDU is sending */
|
||||
L2CAP_FLAG_RET_TIMER, /* Retransmission timer is working */
|
||||
L2CAP_FLAG_PDU_RETRANS, /* PDU retransmission */
|
||||
|
||||
|
@ -379,7 +378,7 @@ static uint8_t l2cap_br_get_ident(void)
|
|||
/* L2CAP channel wants to send a PDU */
|
||||
static bool chan_has_data(struct bt_l2cap_br_chan *br_chan)
|
||||
{
|
||||
return !k_fifo_is_empty(&br_chan->_pdu_tx_queue);
|
||||
return !sys_slist_is_empty(&br_chan->_pdu_tx_queue);
|
||||
}
|
||||
|
||||
static void raise_data_ready(struct bt_l2cap_br_chan *br_chan)
|
||||
|
@ -487,6 +486,15 @@ static struct bt_l2cap_br_window *l2cap_br_find_window(struct bt_l2cap_br_chan *
|
|||
return win;
|
||||
}
|
||||
|
||||
static void l2cap_br_free_window(struct bt_l2cap_br_chan *br_chan,
|
||||
struct bt_l2cap_br_window *tx_win)
|
||||
{
|
||||
if (tx_win) {
|
||||
memset(tx_win, 0, sizeof(*tx_win));
|
||||
k_fifo_put(&br_chan->_free_tx_win, tx_win);
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t bt_l2cap_br_update_seq(struct bt_l2cap_br_chan *br_chan, uint16_t seq)
|
||||
{
|
||||
if ((br_chan->tx.mode == BT_L2CAP_BR_LINK_MODE_ERET) ||
|
||||
|
@ -553,33 +561,82 @@ static bool bt_l2cap_br_check_req_seq_valid(struct bt_l2cap_br_chan *br_chan, ui
|
|||
return false;
|
||||
}
|
||||
|
||||
static void l2cap_br_sdu_is_done(struct bt_l2cap_br_chan *br_chan, struct net_buf *sdu, int err)
|
||||
{
|
||||
bt_conn_tx_cb_t cb;
|
||||
|
||||
__ASSERT(sdu, "Invalid sdu buffer on chan %p", br_chan);
|
||||
|
||||
/* The SDU is done */
|
||||
LOG_DBG("SDU is done, removing %p", sdu);
|
||||
|
||||
if (!sys_slist_find_and_remove(&br_chan->_pdu_tx_queue, &sdu->node)) {
|
||||
LOG_WRN("SDU %p is not found", sdu);
|
||||
}
|
||||
|
||||
cb = closure_cb(sdu->user_data);
|
||||
if (cb) {
|
||||
cb(br_chan->chan.conn, closure_data(sdu->user_data), err);
|
||||
}
|
||||
|
||||
/* Remove the pdu */
|
||||
net_buf_unref(sdu);
|
||||
|
||||
LOG_DBG("chan %p done", br_chan);
|
||||
|
||||
/* Append channel to list if it still has data */
|
||||
if (chan_has_data(br_chan)) {
|
||||
LOG_DBG("chan %p ready", br_chan);
|
||||
raise_data_ready(br_chan);
|
||||
}
|
||||
}
|
||||
|
||||
static int bt_l2cap_br_update_req_seq_direct(struct bt_l2cap_br_chan *br_chan, uint16_t req_seq,
|
||||
bool rej)
|
||||
{
|
||||
struct bt_l2cap_br_window *tx_win;
|
||||
struct net_buf *pdu = k_fifo_peek_head(&br_chan->_pdu_tx_queue);
|
||||
struct net_buf *sdu;
|
||||
|
||||
tx_win = (void *)sys_slist_peek_head(&br_chan->_pdu_outstanding);
|
||||
while (tx_win && (tx_win->tx_seq != req_seq)) {
|
||||
sdu = tx_win->sdu;
|
||||
if ((tx_win->sar == BT_L2CAP_CONTROL_SAR_UNSEG) ||
|
||||
(tx_win->sar == BT_L2CAP_CONTROL_SAR_END)) {
|
||||
l2cap_br_sdu_is_done(br_chan, sdu, 0);
|
||||
}
|
||||
tx_win = (void *)sys_slist_get(&br_chan->_pdu_outstanding);
|
||||
memset(tx_win, 0, sizeof(*tx_win));
|
||||
k_fifo_put(&br_chan->_free_tx_win, tx_win);
|
||||
l2cap_br_free_window(br_chan, tx_win);
|
||||
tx_win = (void *)sys_slist_peek_head(&br_chan->_pdu_outstanding);
|
||||
}
|
||||
br_chan->expected_ack_seq = req_seq;
|
||||
|
||||
if (rej && pdu) {
|
||||
if (rej) {
|
||||
tx_win = (void *)sys_slist_peek_head(&br_chan->_pdu_outstanding);
|
||||
if (tx_win) {
|
||||
net_buf_simple_restore(&pdu->b, &tx_win->pdu);
|
||||
sdu = tx_win->sdu;
|
||||
__ASSERT(sdu, "Invalid sdu buffer on chan %p", br_chan);
|
||||
net_buf_simple_restore(&sdu->b, &tx_win->sdu_state);
|
||||
if ((tx_win->sar == BT_L2CAP_CONTROL_SAR_UNSEG) ||
|
||||
(tx_win->sar == BT_L2CAP_CONTROL_SAR_START)) {
|
||||
br_chan->_sdu_total_len = 0;
|
||||
} else {
|
||||
br_chan->_sdu_total_len = tx_win->sdu_total_len;
|
||||
}
|
||||
br_chan->next_tx_seq = tx_win->tx_seq;
|
||||
}
|
||||
|
||||
while (tx_win) {
|
||||
tx_win = (void *)sys_slist_get(&br_chan->_pdu_outstanding);
|
||||
memset(tx_win, 0, sizeof(*tx_win));
|
||||
k_fifo_put(&br_chan->_free_tx_win, tx_win);
|
||||
l2cap_br_free_window(br_chan, tx_win);
|
||||
tx_win = (void *)sys_slist_peek_head(&br_chan->_pdu_outstanding);
|
||||
if (tx_win) {
|
||||
sdu = tx_win->sdu;
|
||||
__ASSERT(sdu, "Invalid sdu buffer on chan %p", br_chan);
|
||||
if ((tx_win->sar == BT_L2CAP_CONTROL_SAR_UNSEG) ||
|
||||
(tx_win->sar == BT_L2CAP_CONTROL_SAR_START)) {
|
||||
net_buf_simple_restore(&sdu->b, &tx_win->sdu_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -791,7 +848,7 @@ static bool l2cap_br_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan,
|
|||
return false;
|
||||
}
|
||||
|
||||
k_fifo_init(&ch->_pdu_tx_queue);
|
||||
sys_slist_init(&ch->_pdu_tx_queue);
|
||||
|
||||
/* All dynamic channels have the destroy handler which makes sure that
|
||||
* the RTX work structure is properly released with a cancel sync.
|
||||
|
@ -848,7 +905,7 @@ int bt_l2cap_br_send_cb(struct bt_conn *conn, uint16_t cid, struct net_buf *buf,
|
|||
LOG_DBG("push PDU: cb %p userdata %p", cb, user_data);
|
||||
|
||||
make_closure(buf->user_data, cb, user_data);
|
||||
k_fifo_put(&br_chan->_pdu_tx_queue, buf);
|
||||
sys_slist_append(&br_chan->_pdu_tx_queue, &buf->node);
|
||||
raise_data_ready(br_chan);
|
||||
|
||||
return 0;
|
||||
|
@ -1061,6 +1118,27 @@ static void bt_l2cap_br_pack_i_frame_header(struct bt_l2cap_br_chan *br_chan, st
|
|||
}
|
||||
}
|
||||
|
||||
static struct net_buf *l2cap_br_get_next_sdu(struct bt_l2cap_br_chan *br_chan)
|
||||
{
|
||||
struct net_buf *sdu, *next;
|
||||
|
||||
if (br_chan->_pdu_buf) {
|
||||
return br_chan->_pdu_buf;
|
||||
}
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&br_chan->_pdu_tx_queue, sdu, next, node) {
|
||||
if (sdu->len) {
|
||||
return sdu;
|
||||
}
|
||||
|
||||
if (L2CAP_BR_IS_S_FRAME(closure_data(sdu->user_data))) {
|
||||
return sdu;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool l2cap_br_send_i_frame(struct bt_l2cap_br_chan *br_chan, struct net_buf *sdu)
|
||||
{
|
||||
if (atomic_test_bit(br_chan->flags, L2CAP_FLAG_RECV_FRAME_R)) {
|
||||
|
@ -1119,132 +1197,84 @@ static struct net_buf *l2cap_br_ret_fc_data_pull(struct bt_conn *conn, size_t am
|
|||
struct bt_l2cap_br_chan *br_chan =
|
||||
CONTAINER_OF(pdu_ready, struct bt_l2cap_br_chan, _pdu_ready);
|
||||
|
||||
/* Leave the PDU buffer in the queue until we have sent all its
|
||||
* fragments.
|
||||
*/
|
||||
struct net_buf *pdu = k_fifo_peek_head(&br_chan->_pdu_tx_queue);
|
||||
struct net_buf *send_buf = NULL;
|
||||
|
||||
__ASSERT(pdu, "signaled ready but no PDUs in the TX queue");
|
||||
|
||||
if (br_chan->_pdu_buf && bt_buf_has_view(br_chan->_pdu_buf)) {
|
||||
LOG_ERR("already have view on %p of %p", br_chan->_pdu_buf, pdu);
|
||||
LOG_ERR("already have view on %p", br_chan->_pdu_buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!br_chan->_pdu_buf && !bt_l2cap_br_get_outstanding_count(br_chan)) {
|
||||
if (atomic_test_bit(br_chan->flags, L2CAP_FLAG_SDU_SENDING) && (!pdu->len)) {
|
||||
/* There is a sdu has been done */
|
||||
LOG_DBG("last frag, removing %p", pdu);
|
||||
|
||||
__maybe_unused struct net_buf *b;
|
||||
bt_conn_tx_cb_t cb;
|
||||
|
||||
b = k_fifo_get(&br_chan->_pdu_tx_queue, K_NO_WAIT);
|
||||
|
||||
__ASSERT_NO_MSG(b == pdu);
|
||||
|
||||
atomic_clear_bit(br_chan->flags, L2CAP_FLAG_SDU_SENDING);
|
||||
|
||||
br_chan->_pdu_buf = NULL;
|
||||
br_chan->_pdu_remaining = 0;
|
||||
|
||||
cb = closure_cb(pdu->user_data);
|
||||
|
||||
if (cb) {
|
||||
cb(conn, closure_data(pdu->user_data), 0);
|
||||
}
|
||||
|
||||
/* Remove the pdu */
|
||||
net_buf_unref(pdu);
|
||||
|
||||
LOG_DBG("chan %p done", br_chan);
|
||||
lower_data_ready(br_chan);
|
||||
|
||||
/* Append channel to list if it still has data */
|
||||
if (chan_has_data(br_chan)) {
|
||||
LOG_DBG("chan %p ready", br_chan);
|
||||
raise_data_ready(br_chan);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/* Leave the PDU buffer in the queue until we have sent all its
|
||||
* fragments.
|
||||
*/
|
||||
struct net_buf *pdu = l2cap_br_get_next_sdu(br_chan);
|
||||
struct net_buf *send_buf = NULL;
|
||||
|
||||
if (br_chan->_pdu_remaining == 0) {
|
||||
struct bt_l2cap_br_window *tx_win;
|
||||
bool first = true;
|
||||
uint8_t s_bit = BT_L2CAP_CONTROL_S_RR;
|
||||
bool alloc = false;
|
||||
|
||||
send_buf = NULL;
|
||||
if (pdu && !pdu->len && L2CAP_BR_IS_S_FRAME(closure_data(pdu->user_data))) {
|
||||
/* It is a S-frame */
|
||||
s_bit = L2CAP_BR_GET_S_BIT(closure_data(pdu->user_data));
|
||||
alloc = true;
|
||||
}
|
||||
|
||||
/* There is not any PDU in sending. Check whether needs to send S-frame. */
|
||||
if (!br_chan->_pdu_buf) {
|
||||
uint8_t s_bit = BT_L2CAP_CONTROL_S_RR;
|
||||
bool alloc = false;
|
||||
if ((atomic_test_bit(br_chan->flags, L2CAP_FLAG_RECV_FRAME_R) &&
|
||||
atomic_test_bit(br_chan->flags, L2CAP_FLAG_RECV_FRAME_R_CHANGED)) ||
|
||||
(atomic_test_bit(br_chan->flags, L2CAP_FLAG_SEND_FRAME_REJ) &&
|
||||
atomic_test_bit(br_chan->flags, L2CAP_FLAG_SEND_FRAME_REJ_CHANGED)) ||
|
||||
(atomic_test_bit(br_chan->flags, L2CAP_FLAG_SEND_FRAME_P) &&
|
||||
atomic_test_bit(br_chan->flags, L2CAP_FLAG_SEND_FRAME_P_CHANGED)) ||
|
||||
(atomic_test_bit(br_chan->flags, L2CAP_FLAG_LOCAL_BUSY) &&
|
||||
atomic_test_bit(br_chan->flags, L2CAP_FLAG_LOCAL_BUSY_CHANGED)) ||
|
||||
atomic_test_bit(br_chan->flags, L2CAP_FLAG_SEND_S_FRAME)) {
|
||||
alloc = true;
|
||||
}
|
||||
|
||||
if (!pdu->len && !atomic_test_bit(br_chan->flags, L2CAP_FLAG_SDU_SENDING)) {
|
||||
if (L2CAP_BR_IS_S_FRAME(closure_data(pdu->user_data))) {
|
||||
/* It is a S-frame */
|
||||
s_bit = L2CAP_BR_GET_S_BIT(closure_data(pdu->user_data));
|
||||
atomic_set_bit(br_chan->flags, L2CAP_FLAG_SDU_SENDING);
|
||||
alloc = true;
|
||||
}
|
||||
if (atomic_test_bit(br_chan->flags, L2CAP_FLAG_RECV_FRAME_P) &&
|
||||
!l2cap_br_send_i_frame(br_chan, pdu)) {
|
||||
alloc = true;
|
||||
}
|
||||
|
||||
if (alloc) {
|
||||
int err;
|
||||
|
||||
/* Send S-Frame with R bit set. */
|
||||
send_buf = bt_l2cap_create_pdu_timeout(&br_tx_pool, 0, K_NO_WAIT);
|
||||
if (send_buf == NULL) {
|
||||
/* No buffer can be used for transmission.
|
||||
* Waiting for buffer available.
|
||||
*/
|
||||
LOG_WRN("no buf for sending S-frame on %p", br_chan);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((atomic_test_bit(br_chan->flags, L2CAP_FLAG_RECV_FRAME_R) &&
|
||||
atomic_test_bit(br_chan->flags, L2CAP_FLAG_RECV_FRAME_R_CHANGED)) ||
|
||||
(atomic_test_bit(br_chan->flags, L2CAP_FLAG_SEND_FRAME_REJ) &&
|
||||
atomic_test_bit(br_chan->flags, L2CAP_FLAG_SEND_FRAME_REJ_CHANGED)) ||
|
||||
(atomic_test_bit(br_chan->flags, L2CAP_FLAG_SEND_FRAME_P) &&
|
||||
atomic_test_bit(br_chan->flags, L2CAP_FLAG_SEND_FRAME_P_CHANGED)) ||
|
||||
(atomic_test_bit(br_chan->flags, L2CAP_FLAG_LOCAL_BUSY) &&
|
||||
atomic_test_bit(br_chan->flags, L2CAP_FLAG_LOCAL_BUSY_CHANGED)) ||
|
||||
atomic_test_bit(br_chan->flags, L2CAP_FLAG_SEND_S_FRAME)) {
|
||||
alloc = true;
|
||||
err = bt_l2cap_br_pack_s_frame_header(br_chan, send_buf, s_bit);
|
||||
if (err < 0) {
|
||||
net_buf_unref(send_buf);
|
||||
LOG_WRN("Cannot sending S-frame on %p", br_chan);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (atomic_test_bit(br_chan->flags, L2CAP_FLAG_RECV_FRAME_P) &&
|
||||
!l2cap_br_send_i_frame(br_chan, pdu)) {
|
||||
alloc = true;
|
||||
atomic_clear_bit(br_chan->flags, L2CAP_FLAG_SEND_S_FRAME);
|
||||
|
||||
if (BT_L2CAP_RT_FC_SDU_TAIL_SIZE(br_chan)) {
|
||||
uint16_t fcs =
|
||||
crc16_reflect(0xa001, 0, send_buf->data, send_buf->len);
|
||||
|
||||
net_buf_add_le16(send_buf, fcs);
|
||||
}
|
||||
br_chan->_pdu_remaining = send_buf->len;
|
||||
br_chan->_pdu_buf = send_buf;
|
||||
|
||||
make_closure(send_buf->user_data, NULL, NULL);
|
||||
|
||||
if (pdu && !pdu->len && L2CAP_BR_IS_S_FRAME(closure_data(pdu->user_data))) {
|
||||
l2cap_br_sdu_is_done(br_chan, pdu, 0);
|
||||
}
|
||||
|
||||
if (alloc) {
|
||||
int err;
|
||||
|
||||
/* Send S-Frame with R bit set. */
|
||||
send_buf = bt_l2cap_create_pdu_timeout(&br_tx_pool, 0, K_NO_WAIT);
|
||||
if (send_buf == NULL) {
|
||||
/* No buffer can be used for transmission.
|
||||
* Waiting for buffer available.
|
||||
*/
|
||||
LOG_WRN("no buf for sending S-frame on %p", br_chan);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (send_buf != NULL) {
|
||||
err = bt_l2cap_br_pack_s_frame_header(br_chan, send_buf, s_bit);
|
||||
if (err < 0) {
|
||||
net_buf_unref(send_buf);
|
||||
LOG_WRN("Cannot sending S-frame on %p", br_chan);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
atomic_clear_bit(br_chan->flags, L2CAP_FLAG_SEND_S_FRAME);
|
||||
|
||||
if (BT_L2CAP_RT_FC_SDU_TAIL_SIZE(br_chan)) {
|
||||
uint16_t fcs = crc16_reflect(0xa001, 0, send_buf->data,
|
||||
send_buf->len);
|
||||
|
||||
net_buf_add_le16(send_buf, fcs);
|
||||
}
|
||||
br_chan->_pdu_remaining = send_buf->len;
|
||||
br_chan->_pdu_buf = send_buf;
|
||||
|
||||
make_closure(send_buf->user_data, NULL, NULL);
|
||||
|
||||
goto done;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (atomic_test_bit(br_chan->flags, L2CAP_FLAG_RECV_FRAME_R)) {
|
||||
|
@ -1304,6 +1334,15 @@ static struct net_buf *l2cap_br_ret_fc_data_pull(struct bt_conn *conn, size_t am
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Check whether all PDUs have been sent. But not of all are confirmed. */
|
||||
if (!pdu) {
|
||||
/* No pending I-frame needs to be sent
|
||||
*/
|
||||
LOG_DBG("No pending I-frame needs to be sent on %p", br_chan);
|
||||
lower_data_ready(br_chan);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tx_win = l2cap_br_find_window(br_chan);
|
||||
if (!tx_win) {
|
||||
/* No more window for sending I-frame PDU.
|
||||
|
@ -1315,26 +1354,6 @@ static struct net_buf *l2cap_br_ret_fc_data_pull(struct bt_conn *conn, size_t am
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Check whether all PDUs have been sent. But not of all are confirmed. */
|
||||
if (!pdu->len) {
|
||||
/* All SDU are sent, but not all I-frames have been acked.
|
||||
* Waiting for all acks.
|
||||
* Remove the channel from the ready-list, it will be added
|
||||
* back later when the S-frame PDU has been received.
|
||||
*/
|
||||
if (!atomic_test_bit(br_chan->flags, L2CAP_FLAG_SDU_SENDING)) {
|
||||
/* It is new SDU */
|
||||
if (L2CAP_BR_IS_S_FRAME(closure_data(pdu->user_data))) {
|
||||
LOG_DBG("Shouldn't be able to reach here");
|
||||
__ASSERT(false, "Shouldn't be able to reach here");
|
||||
}
|
||||
} else {
|
||||
LOG_DBG("Waiting for S-frame on %p", br_chan);
|
||||
lower_data_ready(br_chan);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
send_i_frame:
|
||||
send_buf = bt_l2cap_create_pdu_timeout(&br_tx_pool, 0, K_NO_WAIT);
|
||||
if (send_buf == NULL) {
|
||||
|
@ -1343,6 +1362,9 @@ send_i_frame:
|
|||
*/
|
||||
LOG_WRN("no buf for sending I-frame on %p", br_chan);
|
||||
/* Maybe we can disconnect channel here if retransmission I-frame. */
|
||||
|
||||
/* Free allocated tx_win. */
|
||||
l2cap_br_free_window(br_chan, tx_win);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1357,11 +1379,7 @@ send_i_frame:
|
|||
bool last_seg;
|
||||
bool start_seg;
|
||||
|
||||
if (atomic_test_and_set_bit(br_chan->flags, L2CAP_FLAG_SDU_SENDING)) {
|
||||
start_seg = false;
|
||||
} else {
|
||||
start_seg = true;
|
||||
}
|
||||
start_seg = br_chan->_sdu_total_len ? false : true;
|
||||
|
||||
if (start_seg) {
|
||||
br_chan->_sdu_total_len = pdu->len;
|
||||
|
@ -1371,7 +1389,7 @@ send_i_frame:
|
|||
}
|
||||
}
|
||||
|
||||
net_buf_simple_save(&pdu->b, &tx_win->pdu);
|
||||
net_buf_simple_save(&pdu->b, &tx_win->sdu_state);
|
||||
|
||||
pdu_len = get_pdu_len(br_chan, pdu, start_seg);
|
||||
|
||||
|
@ -1398,6 +1416,7 @@ send_i_frame:
|
|||
__ASSERT(false, "Tailroom of the buffer cannot be filled %zu / %zu",
|
||||
net_buf_tailroom(send_buf), actual_pdu_len);
|
||||
net_buf_unref(send_buf);
|
||||
l2cap_br_free_window(br_chan, tx_win);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1414,6 +1433,8 @@ send_i_frame:
|
|||
tx_win->srej = false;
|
||||
tx_win->sar = sar;
|
||||
tx_win->transmit_counter = 1;
|
||||
tx_win->sdu_total_len = br_chan->_sdu_total_len;
|
||||
tx_win->sdu = pdu;
|
||||
|
||||
LOG_DBG("Sending I-frame %u: buf %p chan %p len %zu", tx_win->tx_seq, pdu,
|
||||
br_chan, pdu_len);
|
||||
|
@ -1480,8 +1501,7 @@ send_i_frame:
|
|||
if (br_chan->rx.mode != BT_L2CAP_BR_LINK_MODE_STREAM) {
|
||||
sys_slist_append(&br_chan->_pdu_outstanding, &tx_win->node);
|
||||
} else {
|
||||
memset(tx_win, 0, sizeof(*tx_win));
|
||||
k_fifo_put(&br_chan->_free_tx_win, tx_win);
|
||||
l2cap_br_free_window(br_chan, tx_win);
|
||||
|
||||
if (pdu->len == 0) {
|
||||
l2cap_br_sdu_is_done(br_chan, pdu, 0);
|
||||
|
@ -1512,6 +1532,9 @@ done:
|
|||
} else {
|
||||
br_chan->_pdu_buf = NULL;
|
||||
br_chan->_pdu_remaining = 0;
|
||||
if (pdu && !pdu->len) {
|
||||
br_chan->_sdu_total_len = 0;
|
||||
}
|
||||
|
||||
LOG_DBG("done sending PDU");
|
||||
|
||||
|
@ -1559,9 +1582,11 @@ struct net_buf *l2cap_br_data_pull(struct bt_conn *conn, size_t amount, size_t *
|
|||
/* Leave the PDU buffer in the queue until we have sent all its
|
||||
* fragments.
|
||||
*/
|
||||
struct net_buf *pdu = k_fifo_peek_head(&br_chan->_pdu_tx_queue);
|
||||
const sys_snode_t *tx_pdu = sys_slist_peek_head(&br_chan->_pdu_tx_queue);
|
||||
|
||||
__ASSERT(pdu, "signaled ready but no PDUs in the TX queue");
|
||||
__ASSERT(tx_pdu, "signaled ready but no PDUs in the TX queue");
|
||||
|
||||
struct net_buf *pdu = CONTAINER_OF(tx_pdu, struct net_buf, node);
|
||||
|
||||
if (bt_buf_has_view(pdu)) {
|
||||
LOG_ERR("already have view on %p", pdu);
|
||||
|
@ -1576,9 +1601,11 @@ struct net_buf *l2cap_br_data_pull(struct bt_conn *conn, size_t amount, size_t *
|
|||
|
||||
if (last_frag) {
|
||||
LOG_DBG("last frag, removing %p", pdu);
|
||||
__maybe_unused struct net_buf *b = k_fifo_get(&br_chan->_pdu_tx_queue, K_NO_WAIT);
|
||||
__maybe_unused bool found;
|
||||
|
||||
__ASSERT_NO_MSG(b == pdu);
|
||||
found = sys_slist_find_and_remove(&br_chan->_pdu_tx_queue, &pdu->node);
|
||||
|
||||
__ASSERT_NO_MSG(found);
|
||||
|
||||
LOG_DBG("chan %p done", br_chan);
|
||||
lower_data_ready(br_chan);
|
||||
|
@ -2461,7 +2488,9 @@ void bt_l2cap_br_chan_del(struct bt_l2cap_chan *chan)
|
|||
|
||||
/* Remove buffers on the PDU TX queue. */
|
||||
while (chan_has_data(br_chan)) {
|
||||
struct net_buf *buf = k_fifo_get(&br_chan->_pdu_tx_queue, K_NO_WAIT);
|
||||
const sys_snode_t *tx_buf = sys_slist_get(&br_chan->_pdu_tx_queue);
|
||||
|
||||
struct net_buf *buf = CONTAINER_OF(tx_buf, struct net_buf, node);
|
||||
|
||||
net_buf_unref(buf);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue