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:
parent
f61b8b8c16
commit
cf3832f0bf
3 changed files with 89 additions and 21 deletions
|
@ -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_lookup_ident(conn, ident) __l2cap_lookup_ident(conn, ident, false)
|
||||||
#define l2cap_remove_ident(conn, ident) __l2cap_lookup_ident(conn, ident, true)
|
#define l2cap_remove_ident(conn, ident) __l2cap_lookup_ident(conn, ident, true)
|
||||||
|
|
||||||
struct data_sent {
|
struct l2cap_tx_meta_data {
|
||||||
uint16_t len;
|
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;
|
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 */
|
/* Resume tx in case there are buffers in the queue */
|
||||||
while ((buf = l2cap_chan_le_get_tx_buf(ch))) {
|
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);
|
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)
|
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;
|
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) {
|
if (!chan) {
|
||||||
/* Received SDU sent callback for disconnected channel */
|
/* Received SDU sent callback for disconnected channel */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cb = data->cb;
|
||||||
|
cb_user_data = data->user_data;
|
||||||
|
|
||||||
|
free_tx_meta_data(data);
|
||||||
|
|
||||||
if (chan->ops->sent) {
|
if (chan->ops->sent) {
|
||||||
chan->ops->sent(chan);
|
chan->ops->sent(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cb) {
|
||||||
|
cb(conn, cb_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan));
|
l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void l2cap_chan_seg_sent(struct bt_conn *conn, void *user_data)
|
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;
|
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) {
|
if (!chan) {
|
||||||
/* Received segment sent callback for disconnected channel */
|
/* Received segment sent callback for disconnected channel */
|
||||||
return;
|
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) {
|
if ((buf == seg || !buf->len) && ch->chan.ops->sent) {
|
||||||
err = bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg,
|
err = bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg,
|
||||||
l2cap_chan_sdu_sent,
|
l2cap_chan_sdu_sent,
|
||||||
UINT_TO_POINTER(ch->tx.cid));
|
l2cap_tx_meta_data(buf));
|
||||||
} else {
|
} else {
|
||||||
err = bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg,
|
err = bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg,
|
||||||
l2cap_chan_seg_sent,
|
l2cap_chan_seg_sent,
|
||||||
UINT_TO_POINTER(ch->tx.cid));
|
l2cap_tx_meta_data(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -1965,7 +1997,7 @@ static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch,
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret == -EAGAIN) {
|
if (ret == -EAGAIN) {
|
||||||
/* Store sent data into user_data */
|
/* Store sent data into user_data */
|
||||||
data_sent(frag)->len = sent;
|
l2cap_tx_meta_data(frag)->sent = sent;
|
||||||
}
|
}
|
||||||
*buf = frag;
|
*buf = frag;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1984,7 +2016,7 @@ static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch,
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret == -EAGAIN) {
|
if (ret == -EAGAIN) {
|
||||||
/* Store sent data into user_data */
|
/* Store sent data into user_data */
|
||||||
data_sent(frag)->len = sent;
|
l2cap_tx_meta_data(frag)->sent = sent;
|
||||||
}
|
}
|
||||||
*buf = frag;
|
*buf = frag;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2604,6 +2636,12 @@ void bt_l2cap_init(void)
|
||||||
if (IS_ENABLED(CONFIG_BT_BREDR)) {
|
if (IS_ENABLED(CONFIG_BT_BREDR)) {
|
||||||
bt_l2cap_br_init();
|
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)
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
||||||
|
@ -2880,9 +2918,11 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
|
||||||
return 0;
|
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 bt_l2cap_le_chan *le_chan = BT_L2CAP_LE_CHAN(chan);
|
||||||
|
struct l2cap_tx_meta_data *data;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!buf) {
|
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) &&
|
if (IS_ENABLED(CONFIG_BT_BREDR) &&
|
||||||
chan->conn->type == BT_CONN_TYPE_BR) {
|
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
|
/* Queue if there are pending segments left from previous packet or
|
||||||
* there are no credits available.
|
* there are no credits available.
|
||||||
*/
|
*/
|
||||||
if (le_chan->tx_buf || !k_fifo_is_empty(&le_chan->tx_queue) ||
|
if (le_chan->tx_buf || !k_fifo_is_empty(&le_chan->tx_queue) ||
|
||||||
!atomic_get(&le_chan->tx.credits)) {
|
!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);
|
net_buf_put(&le_chan->tx_queue, buf);
|
||||||
k_work_submit(&le_chan->tx_work);
|
k_work_submit(&le_chan->tx_work);
|
||||||
return 0;
|
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);
|
err = l2cap_chan_le_send_sdu(le_chan, &buf, 0);
|
||||||
if (err < 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 */
|
/* Queue buffer if at least one segment could be sent */
|
||||||
net_buf_put(&le_chan->tx_queue, buf);
|
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);
|
BT_ERR("failed to send message %d", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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 */
|
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
||||||
|
|
|
@ -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);
|
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 -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)
|
static int l2cap_br_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
||||||
|
|
|
@ -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);
|
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 */
|
/* Receive a new L2CAP PDU from a connection */
|
||||||
void bt_l2cap_recv(struct bt_conn *conn, struct net_buf *buf, bool complete);
|
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 */
|
/* 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(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
|
* Handle security level changed on link passing HCI status of performed
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue