Bluetooth: Host: Add support for tx complete callback for ECRED channels

Previously callbacks were only supported on unenhanced channels.

Signed-off-by: Herman Berget <herman.berget@nordicsemi.no>
This commit is contained in:
Herman Berget 2022-03-25 14:26:40 +01:00 committed by Carles Cufí
commit cf3832f0bf
3 changed files with 89 additions and 21 deletions

View file

@ -65,11 +65,32 @@ NET_BUF_POOL_FIXED_DEFINE(disc_pool, 1,
#define l2cap_lookup_ident(conn, ident) __l2cap_lookup_ident(conn, ident, false)
#define l2cap_remove_ident(conn, ident) __l2cap_lookup_ident(conn, ident, true)
struct data_sent {
uint16_t len;
struct l2cap_tx_meta_data {
int sent;
uint16_t cid;
bt_conn_tx_cb_t cb;
void *user_data;
};
#define data_sent(buf) ((struct data_sent *)net_buf_user_data(buf))
struct l2cap_tx_meta {
struct l2cap_tx_meta_data *data;
};
static struct l2cap_tx_meta_data l2cap_tx_meta_data[CONFIG_BT_CONN_TX_MAX];
K_FIFO_DEFINE(free_l2cap_tx_meta_data);
static struct l2cap_tx_meta_data *alloc_tx_meta_data(void)
{
return k_fifo_get(&free_l2cap_tx_meta_data, K_NO_WAIT);
}
static void free_tx_meta_data(struct l2cap_tx_meta_data *data)
{
(void)memset(data, 0, sizeof(*data));
k_fifo_put(&free_l2cap_tx_meta_data, data);
}
#define l2cap_tx_meta_data(buf) (((struct l2cap_tx_meta *)net_buf_user_data(buf))->data)
static sys_slist_t servers;
@ -878,7 +899,7 @@ static void l2cap_chan_tx_process(struct k_work *work)
/* Resume tx in case there are buffers in the queue */
while ((buf = l2cap_chan_le_get_tx_buf(ch))) {
int sent = data_sent(buf)->len;
int sent = l2cap_tx_meta_data(buf)->sent;
BT_DBG("buf %p sent %u", buf, sent);
@ -1810,32 +1831,43 @@ static void l2cap_chan_tx_resume(struct bt_l2cap_le_chan *ch)
static void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data)
{
uint16_t cid = POINTER_TO_UINT(user_data);
struct l2cap_tx_meta_data *data = user_data;
struct bt_l2cap_chan *chan;
bt_conn_tx_cb_t cb;
void *cb_user_data;
BT_DBG("conn %p CID 0x%04x", conn, cid);
BT_DBG("conn %p CID 0x%04x", conn, data->cid);
chan = bt_l2cap_le_lookup_tx_cid(conn, cid);
chan = bt_l2cap_le_lookup_tx_cid(conn, data->cid);
if (!chan) {
/* Received SDU sent callback for disconnected channel */
return;
}
cb = data->cb;
cb_user_data = data->user_data;
free_tx_meta_data(data);
if (chan->ops->sent) {
chan->ops->sent(chan);
}
if (cb) {
cb(conn, cb_user_data);
}
l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan));
}
static void l2cap_chan_seg_sent(struct bt_conn *conn, void *user_data)
{
uint16_t cid = POINTER_TO_UINT(user_data);
struct l2cap_tx_meta_data *data = user_data;
struct bt_l2cap_chan *chan;
BT_DBG("conn %p CID 0x%04x", conn, cid);
BT_DBG("conn %p CID 0x%04x", conn, data->cid);
chan = bt_l2cap_le_lookup_tx_cid(conn, cid);
chan = bt_l2cap_le_lookup_tx_cid(conn, data->cid);
if (!chan) {
/* Received segment sent callback for disconnected channel */
return;
@ -1902,11 +1934,11 @@ static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch,
if ((buf == seg || !buf->len) && ch->chan.ops->sent) {
err = bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg,
l2cap_chan_sdu_sent,
UINT_TO_POINTER(ch->tx.cid));
l2cap_tx_meta_data(buf));
} else {
err = bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg,
l2cap_chan_seg_sent,
UINT_TO_POINTER(ch->tx.cid));
l2cap_tx_meta_data(buf));
}
if (err) {
@ -1965,7 +1997,7 @@ static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch,
if (ret < 0) {
if (ret == -EAGAIN) {
/* Store sent data into user_data */
data_sent(frag)->len = sent;
l2cap_tx_meta_data(frag)->sent = sent;
}
*buf = frag;
return ret;
@ -1984,7 +2016,7 @@ static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch,
if (ret < 0) {
if (ret == -EAGAIN) {
/* Store sent data into user_data */
data_sent(frag)->len = sent;
l2cap_tx_meta_data(frag)->sent = sent;
}
*buf = frag;
return ret;
@ -2604,6 +2636,12 @@ void bt_l2cap_init(void)
if (IS_ENABLED(CONFIG_BT_BREDR)) {
bt_l2cap_br_init();
}
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
for (size_t i = 0; i < ARRAY_SIZE(l2cap_tx_meta_data); i++) {
k_fifo_put(&free_l2cap_tx_meta_data, &l2cap_tx_meta_data[i]);
}
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
}
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
@ -2880,9 +2918,11 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
return 0;
}
int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
int bt_l2cap_chan_send_cb(struct bt_l2cap_chan *chan, struct net_buf *buf, bt_conn_tx_cb_t cb,
void *user_data)
{
struct bt_l2cap_le_chan *le_chan = BT_L2CAP_LE_CHAN(chan);
struct l2cap_tx_meta_data *data;
int err;
if (!buf) {
@ -2901,15 +2941,27 @@ int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
if (IS_ENABLED(CONFIG_BT_BREDR) &&
chan->conn->type == BT_CONN_TYPE_BR) {
return bt_l2cap_br_chan_send(chan, buf);
return bt_l2cap_br_chan_send_cb(chan, buf, cb, user_data);
}
data = alloc_tx_meta_data();
if (!data) {
BT_WARN("Unable to allocate TX context");
return -ENOBUFS;
}
data->sent = 0;
data->cid = le_chan->tx.cid;
data->cb = cb;
data->user_data = user_data;
l2cap_tx_meta_data(buf) = data;
/* Queue if there are pending segments left from previous packet or
* there are no credits available.
*/
if (le_chan->tx_buf || !k_fifo_is_empty(&le_chan->tx_queue) ||
!atomic_get(&le_chan->tx.credits)) {
data_sent(buf)->len = 0;
l2cap_tx_meta_data(buf)->sent = 0;
net_buf_put(&le_chan->tx_queue, buf);
k_work_submit(&le_chan->tx_work);
return 0;
@ -2917,14 +2969,19 @@ int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
err = l2cap_chan_le_send_sdu(le_chan, &buf, 0);
if (err < 0) {
if (err == -EAGAIN && data_sent(buf)->len) {
if (err == -EAGAIN && l2cap_tx_meta_data(buf)->sent) {
/* Queue buffer if at least one segment could be sent */
net_buf_put(&le_chan->tx_queue, buf);
return data_sent(buf)->len;
return l2cap_tx_meta_data(buf)->sent;
}
BT_ERR("failed to send message %d", err);
}
return err;
}
int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
return bt_l2cap_chan_send_cb(chan, buf, NULL, NULL);
}
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */

View file

@ -1353,7 +1353,8 @@ static void l2cap_br_conn_rsp(struct bt_l2cap_br *l2cap, uint8_t ident,
}
}
int bt_l2cap_br_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
int bt_l2cap_br_chan_send_cb(struct bt_l2cap_chan *chan, struct net_buf *buf, bt_conn_tx_cb_t cb,
void *user_data)
{
struct bt_l2cap_br_chan *br_chan = BR_CHAN(chan);
@ -1361,7 +1362,12 @@ int bt_l2cap_br_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
return -EMSGSIZE;
}
return bt_l2cap_send_cb(br_chan->chan.conn, br_chan->tx.cid, buf, NULL, NULL);
return bt_l2cap_send_cb(br_chan->chan.conn, br_chan->tx.cid, buf, cb, user_data);
}
int bt_l2cap_br_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
return bt_l2cap_br_chan_send_cb(chan, buf, NULL, NULL);
}
static int l2cap_br_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)

View file

@ -315,6 +315,9 @@ static inline int bt_l2cap_send(struct bt_conn *conn, uint16_t cid,
return bt_l2cap_send_cb(conn, cid, buf, NULL, NULL);
}
int bt_l2cap_chan_send_cb(struct bt_l2cap_chan *chan, struct net_buf *buf, bt_conn_tx_cb_t cb,
void *user_data);
/* Receive a new L2CAP PDU from a connection */
void bt_l2cap_recv(struct bt_conn *conn, struct net_buf *buf, bool complete);
@ -355,6 +358,8 @@ int bt_l2cap_br_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
/* Send packet data to connected peer */
int bt_l2cap_br_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf);
int bt_l2cap_br_chan_send_cb(struct bt_l2cap_chan *chan, struct net_buf *buf, bt_conn_tx_cb_t cb,
void *user_data);
/*
* Handle security level changed on link passing HCI status of performed