diff --git a/include/bluetooth/l2cap.h b/include/bluetooth/l2cap.h index 9a645a80a9919..c67db8d7f5962 100644 --- a/include/bluetooth/l2cap.h +++ b/include/bluetooth/l2cap.h @@ -74,6 +74,13 @@ typedef enum bt_l2cap_chan_status { /** Channel output status */ BT_L2CAP_STATUS_OUT, + /** Channel shutdown status + * + * Once this status is notified it means the channel will no longer be + * able to transmit or receive data. + */ + BT_L2CAP_STATUS_SHUTDOWN, + /* Total number of status - must be at the end of the enum */ BT_L2CAP_NUM_STATUS, } __packed bt_l2cap_chan_status_t; @@ -126,6 +133,8 @@ struct bt_l2cap_le_chan { struct k_fifo tx_queue; /** Channel Pending Transmission buffer */ struct net_buf *tx_buf; + /** Channel Transmission work */ + struct k_work tx_work; /** Segment SDU packet from upper layer */ struct net_buf *_sdu; u16_t _sdu_len; diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig index 8e5ab26189530..c7210357970a1 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -22,6 +22,7 @@ config BT_HCI_CMD_COUNT config BT_RX_BUF_COUNT int "Number of HCI RX buffers" + default NET_BUF_RX_COUNT if NET_L2_BT default 3 if BT_RECV_IS_RX_THREAD default 20 if (BT_MESH && !(BT_DISCARDABLE_BUF_COUNT > 0)) default 10 @@ -233,6 +234,7 @@ if BT_HCI_ACL_FLOW_CONTROL config BT_ACL_RX_COUNT int "Number of incoming ACL data buffers" default BT_CTLR_RX_BUFFERS if BT_CTLR + default NET_BUF_RX_COUNT if NET_L2_BT default 6 range 1 64 help diff --git a/subsys/bluetooth/host/Kconfig.l2cap b/subsys/bluetooth/host/Kconfig.l2cap index 64992c47d07ff..91ed5a758e766 100644 --- a/subsys/bluetooth/host/Kconfig.l2cap +++ b/subsys/bluetooth/host/Kconfig.l2cap @@ -19,6 +19,7 @@ endif # BT_HCI_ACL_FLOW_CONTROL config BT_L2CAP_TX_BUF_COUNT int "Number of L2CAP TX buffers" + default NET_BUF_TX_COUNT if NET_L2_BT default BT_CTLR_TX_BUFFERS if BT_CTLR default 3 range 2 255 @@ -27,6 +28,7 @@ config BT_L2CAP_TX_BUF_COUNT config BT_L2CAP_TX_FRAG_COUNT int "Number of L2CAP TX fragment buffers" + default NET_BUF_TX_COUNT if NET_L2_BT default 2 range 0 255 help diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index 693be1cf098eb..2f9ea93c8ea36 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -1385,11 +1385,7 @@ static struct net_buf *create_frag(struct bt_conn *conn, struct net_buf *buf) struct net_buf *frag; u16_t frag_len; -#if CONFIG_BT_L2CAP_TX_FRAG_COUNT > 0 - frag = bt_conn_create_pdu(&frag_pool, 0); -#else - frag = bt_conn_create_pdu(NULL, 0); -#endif + frag = bt_conn_create_frag(0); if (conn->state != BT_CONN_CONNECTED) { net_buf_unref(frag); @@ -2305,8 +2301,35 @@ int bt_conn_le_conn_update(struct bt_conn *conn, return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CONN_UPDATE, buf, NULL); } +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_conn_create_frag_timeout_debug(size_t reserve, s32_t timeout, + const char *func, int line) +#else +struct net_buf *bt_conn_create_frag_timeout(size_t reserve, s32_t timeout) +#endif +{ + struct net_buf_pool *pool = NULL; + +#if CONFIG_BT_L2CAP_TX_FRAG_COUNT > 0 + pool = &frag_pool; +#endif + +#if defined(CONFIG_NET_BUF_LOG) + return bt_conn_create_pdu_timeout_debug(pool, reserve, timeout, + func, line); +#else + return bt_conn_create_pdu_timeout(pool, reserve, timeout); +#endif /* CONFIG_NET_BUF_LOG */ +} + +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_conn_create_pdu_timeout_debug(struct net_buf_pool *pool, + size_t reserve, s32_t timeout, + const char *func, int line) +#else struct net_buf *bt_conn_create_pdu_timeout(struct net_buf_pool *pool, size_t reserve, s32_t timeout) +#endif { struct net_buf *buf; @@ -2321,13 +2344,27 @@ struct net_buf *bt_conn_create_pdu_timeout(struct net_buf_pool *pool, } if (IS_ENABLED(CONFIG_BT_DEBUG_CONN)) { +#if defined(CONFIG_NET_BUF_LOG) + buf = net_buf_alloc_fixed_debug(pool, K_NO_WAIT, func, line); +#else buf = net_buf_alloc(pool, K_NO_WAIT); +#endif if (!buf) { BT_WARN("Unable to allocate buffer with K_NO_WAIT"); +#if defined(CONFIG_NET_BUF_LOG) + buf = net_buf_alloc_fixed_debug(pool, timeout, func, + line); +#else buf = net_buf_alloc(pool, timeout); +#endif } } else { +#if defined(CONFIG_NET_BUF_LOG) + buf = net_buf_alloc_fixed_debug(pool, timeout, func, + line); +#else buf = net_buf_alloc(pool, timeout); +#endif } if (!buf) { diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index 110bee6199cfa..995a5aea1c44e 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -228,11 +228,43 @@ void bt_conn_security_changed(struct bt_conn *conn, enum bt_security_err err); #endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */ /* Prepare a PDU to be sent over a connection */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_conn_create_pdu_timeout_debug(struct net_buf_pool *pool, + size_t reserve, s32_t timeout, + const char *func, int line); +#define bt_conn_create_pdu_timeout(_pool, _reserve, _timeout) \ + bt_conn_create_pdu_timeout_debug(_pool, _reserve, _timeout, \ + __func__, __LINE__) + +#define bt_conn_create_pdu(_pool, _reserve) \ + bt_conn_create_pdu_timeout_debug(_pool, _reserve, K_FOREVER, \ + __func__, __line__) +#else struct net_buf *bt_conn_create_pdu_timeout(struct net_buf_pool *pool, size_t reserve, s32_t timeout); #define bt_conn_create_pdu(_pool, _reserve) \ bt_conn_create_pdu_timeout(_pool, _reserve, K_FOREVER) +#endif + +/* Prepare a PDU to be sent over a connection */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_conn_create_frag_timeout_debug(size_t reserve, s32_t timeout, + const char *func, int line); + +#define bt_conn_create_frag_timeout(_reserve, _timeout) \ + bt_conn_create_frag_timeout_debug(_reserve, _timeout, \ + __func__, __LINE__) + +#define bt_conn_create_frag(_reserve) \ + bt_conn_create_frag_timeout_debug(_reserve, K_FOREVER, \ + __func__, __LINE__) +#else +struct net_buf *bt_conn_create_frag_timeout(size_t reserve, s32_t timeout); + +#define bt_conn_create_frag(_reserve) \ + bt_conn_create_frag_timeout(_reserve, K_FOREVER) +#endif /* Initialize connection management */ int bt_conn_init(void); diff --git a/subsys/bluetooth/host/l2cap.c b/subsys/bluetooth/host/l2cap.c index 4fbcc8a98a71f..ee24dd9f744d2 100644 --- a/subsys/bluetooth/host/l2cap.c +++ b/subsys/bluetooth/host/l2cap.c @@ -49,6 +49,13 @@ #define L2CAP_CONN_TIMEOUT K_SECONDS(40) #define L2CAP_DISC_TIMEOUT K_SECONDS(2) +#define L2CAP_RTX_TIMEOUT K_SECONDS(2) + +/* Dedicated pool for disconnect buffers so they are guaranteed to be send + * even in case of data congestion due to flooding. + */ +NET_BUF_POOL_FIXED_DEFINE(disc_pool, 1, + BT_L2CAP_BUF_SIZE(CONFIG_BT_L2CAP_TX_MTU), NULL); #if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) /* Size of MTU is based on the maximum amount of data the buffer can hold @@ -61,6 +68,12 @@ #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 { + u16_t len; +}; + +#define data_sent(buf) ((struct data_sent *)net_buf_user_data(buf)) + static sys_slist_t servers; #endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ @@ -243,6 +256,8 @@ void bt_l2cap_chan_del(struct bt_l2cap_chan *chan) if (chan->destroy) { chan->destroy(chan); } + + atomic_clear(chan->status); } static void l2cap_rtx_timeout(struct k_work *work) @@ -370,9 +385,14 @@ static struct net_buf *l2cap_create_le_sig_pdu(struct net_buf *buf, u16_t len) { struct bt_l2cap_sig_hdr *hdr; + struct net_buf_pool *pool = NULL; + + if (code == BT_L2CAP_DISCONN_REQ) { + pool = &disc_pool; + } /* Don't wait more than the minimum RTX timeout of 2 seconds */ - buf = bt_l2cap_create_pdu_timeout(NULL, 0, K_SECONDS(2)); + buf = bt_l2cap_create_pdu_timeout(pool, 0, L2CAP_RTX_TIMEOUT); if (!buf) { /* If it was not possible to allocate a buffer within the * timeout return NULL. @@ -707,6 +727,46 @@ static void l2cap_chan_rx_init(struct bt_l2cap_le_chan *chan) } } +static struct net_buf *l2cap_chan_le_get_tx_buf(struct bt_l2cap_le_chan *ch) +{ + struct net_buf *buf; + + /* Return current buffer */ + if (ch->tx_buf) { + buf = ch->tx_buf; + ch->tx_buf = NULL; + return buf; + } + + return net_buf_get(&ch->tx_queue, K_NO_WAIT); +} + +static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch, + struct net_buf **buf, u16_t sent); + +static void l2cap_chan_tx_process(struct k_work *work) +{ + struct bt_l2cap_le_chan *ch; + struct net_buf *buf; + + ch = CONTAINER_OF(work, struct bt_l2cap_le_chan, tx_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; + + BT_DBG("buf %p sent %u", buf, sent); + + sent = l2cap_chan_le_send_sdu(ch, &buf, sent); + if (sent < 0) { + if (sent == -EAGAIN) { + ch->tx_buf = buf; + } + break; + } + } +} + static void l2cap_chan_tx_init(struct bt_l2cap_le_chan *chan) { BT_DBG("chan %p", chan); @@ -714,6 +774,7 @@ static void l2cap_chan_tx_init(struct bt_l2cap_le_chan *chan) (void)memset(&chan->tx, 0, sizeof(chan->tx)); k_sem_init(&chan->tx.credits, 0, UINT_MAX); k_fifo_init(&chan->tx_queue); + k_work_init(&chan->tx_work, l2cap_chan_tx_process); } static void l2cap_chan_tx_give_credits(struct bt_l2cap_le_chan *chan, @@ -1112,7 +1173,7 @@ static inline struct net_buf *l2cap_alloc_seg(struct net_buf *buf) } /* Fallback to using global connection tx pool */ - return bt_l2cap_create_pdu(NULL, 0); + return bt_l2cap_create_pdu_timeout(NULL, 0, K_NO_WAIT); } static struct net_buf *l2cap_chan_create_seg(struct bt_l2cap_le_chan *ch, @@ -1163,7 +1224,17 @@ static struct net_buf *l2cap_chan_create_seg(struct bt_l2cap_le_chan *ch, return seg; } -void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data) +static void l2cap_chan_tx_resume(struct bt_l2cap_le_chan *ch) +{ + if (!k_sem_count_get(&ch->tx.credits) || + (k_fifo_is_empty(&ch->tx_queue) && !ch->tx_buf)) { + return; + } + + k_work_submit(&ch->tx_work); +} + +static void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data) { struct bt_l2cap_chan *chan = user_data; @@ -1172,29 +1243,49 @@ void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data) if (chan->ops->sent) { chan->ops->sent(chan); } + + l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan)); +} + +static void l2cap_chan_seg_sent(struct bt_conn *conn, void *user_data) +{ + struct bt_l2cap_chan *chan = user_data; + + BT_DBG("conn %p chan %p", conn, chan); + + l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan)); } -static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch, struct net_buf *buf, - u16_t sdu_hdr_len) +/* This returns -EAGAIN whenever a segment cannot be send immediately which can + * happen under the following circuntances: + * + * 1. There are no credits + * 2. There are no buffers + * 3. There are no TX contexts + * + * In all cases the original buffer is unaffected so it can be pushed back to + * be sent later. + */ +static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch, + struct net_buf *buf, u16_t sdu_hdr_len) { struct net_buf *seg; - int len; + struct net_buf_simple_state state; + int len, err; /* Wait for credits */ if (k_sem_take(&ch->tx.credits, K_NO_WAIT)) { - BT_DBG("No credits to transmit packet"); + BT_WARN("No credits to transmit packet"); return -EAGAIN; } + /* Save state so it can be restored if we failed to send */ + net_buf_simple_save(&buf->b, &state); + seg = l2cap_chan_create_seg(ch, buf, sdu_hdr_len); if (!seg) { - return -ENOMEM; - } - - /* Channel may have been disconnected while waiting for a buffer */ - if (!ch->chan.conn) { - net_buf_unref(buf); - return -ECONNRESET; + k_sem_give(&ch->tx.credits); + return -EAGAIN; } BT_DBG("ch %p cid 0x%04x len %u credits %u", ch, ch->tx.cid, @@ -1206,10 +1297,24 @@ static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch, struct net_buf *buf, * callback has been set. */ if ((buf == seg || !buf->len) && ch->chan.ops->sent) { - bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg, - l2cap_chan_sdu_sent, &ch->chan); + err = bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg, + l2cap_chan_sdu_sent, &ch->chan); } else { - bt_l2cap_send(ch->chan.conn, ch->tx.cid, seg); + err = bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg, + l2cap_chan_seg_sent, &ch->chan); + } + + if (err) { + BT_WARN("Unable to send seg %d", err); + k_sem_give(&ch->tx.credits); + + if (err == -ENOBUFS) { + /* Restore state since segment could not be sent */ + net_buf_simple_restore(&buf->b, &state); + return -EAGAIN; + } + + return err; } /* Check if there is no credits left clear output status and notify its @@ -1248,8 +1353,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 */ - memcpy(net_buf_user_data(frag), &sent, - sizeof(sent)); + data_sent(frag)->len = sent; } *buf = frag; return ret; @@ -1268,8 +1372,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 */ - memcpy(net_buf_user_data(frag), &sent, - sizeof(sent)); + data_sent(frag)->len = sent; } *buf = frag; return ret; @@ -1284,40 +1387,6 @@ static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch, return ret; } -static struct net_buf *l2cap_chan_le_get_tx_buf(struct bt_l2cap_le_chan *ch) -{ - struct net_buf *buf; - - /* Return current buffer */ - if (ch->tx_buf) { - buf = ch->tx_buf; - ch->tx_buf = NULL; - return buf; - } - - return net_buf_get(&ch->tx_queue, K_NO_WAIT); -} - -static void l2cap_chan_le_send_resume(struct bt_l2cap_le_chan *ch) -{ - struct net_buf *buf; - - /* Resume tx in case there are buffers in the queue */ - while ((buf = l2cap_chan_le_get_tx_buf(ch))) { - u16_t sent = *((u16_t *)net_buf_user_data(buf)); - - BT_DBG("buf %p sent %u", buf, sent); - - sent = l2cap_chan_le_send_sdu(ch, &buf, sent); - if (sent < 0) { - if (sent == -EAGAIN) { - ch->tx_buf = buf; - } - break; - } - } -} - static void le_credits(struct bt_l2cap *l2cap, u8_t ident, struct net_buf *buf) { @@ -1356,7 +1425,7 @@ static void le_credits(struct bt_l2cap *l2cap, u8_t ident, BT_DBG("chan %p total credits %u", ch, k_sem_count_get(&ch->tx.credits)); - l2cap_chan_le_send_resume(ch); + l2cap_chan_tx_resume(ch); } static void reject_cmd(struct bt_l2cap *l2cap, u8_t ident, @@ -1447,6 +1516,44 @@ static int l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) } #if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) +static void l2cap_chan_shutdown(struct bt_l2cap_chan *chan) +{ + struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan); + struct net_buf *buf; + + BT_DBG("chan %p", chan); + + atomic_set_bit(chan->status, BT_L2CAP_STATUS_SHUTDOWN); + + /* Destroy segmented SDU if it exists */ + if (ch->_sdu) { + net_buf_unref(ch->_sdu); + ch->_sdu = NULL; + ch->_sdu_len = 0U; + } + + /* Cleanup outstanding request */ + if (ch->tx_buf) { + net_buf_unref(ch->tx_buf); + ch->tx_buf = NULL; + } + + /* Remove buffers on the TX queue */ + while ((buf = net_buf_get(&ch->tx_queue, K_NO_WAIT))) { + net_buf_unref(buf); + } + + /* Remove buffers on the RX queue */ + while ((buf = net_buf_get(&ch->rx_queue, K_NO_WAIT))) { + net_buf_unref(buf); + } + + /* Update status */ + if (chan->ops->status) { + chan->ops->status(chan, chan->status); + } +} + static void l2cap_chan_send_credits(struct bt_l2cap_le_chan *chan, struct net_buf *buf, u16_t credits) { @@ -1457,14 +1564,19 @@ static void l2cap_chan_send_credits(struct bt_l2cap_le_chan *chan, credits = chan->rx.init_credits; } - l2cap_chan_rx_give_credits(chan, credits); - buf = l2cap_create_le_sig_pdu(buf, BT_L2CAP_LE_CREDITS, get_ident(), sizeof(*ev)); if (!buf) { + BT_ERR("Unable to send credits update"); + /* Disconnect would probably not work either so the only + * option left is to shutdown the channel. + */ + l2cap_chan_shutdown(&chan->chan); return; } + l2cap_chan_rx_give_credits(chan, credits); + ev = net_buf_add(buf, sizeof(*ev)); ev->cid = sys_cpu_to_le16(chan->rx.cid); ev->credits = sys_cpu_to_le16(credits); @@ -1659,6 +1771,25 @@ static void l2cap_chan_le_recv(struct bt_l2cap_le_chan *chan, l2cap_chan_send_credits(chan, buf, 1); } + +static void l2cap_chan_recv_queue(struct bt_l2cap_le_chan *chan, + struct net_buf *buf) +{ + if (chan->chan.state == BT_L2CAP_DISCONNECT) { + BT_WARN("Ignoring data received while disconnecting"); + net_buf_unref(buf); + return; + } + + if (atomic_test_bit(chan->chan.status, BT_L2CAP_STATUS_SHUTDOWN)) { + BT_WARN("Ignoring data received while channel has shutdown"); + net_buf_unref(buf); + return; + } + + net_buf_put(&chan->rx_queue, buf); + k_work_submit(&chan->rx_work); +} #endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ static void l2cap_chan_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) @@ -1667,8 +1798,7 @@ static void l2cap_chan_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan); if (L2CAP_LE_CID_IS_DYN(ch->rx.cid)) { - net_buf_put(&ch->rx_queue, net_buf_ref(buf)); - k_work_submit(&ch->rx_work); + l2cap_chan_recv_queue(ch, buf); return; } #endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ @@ -1873,6 +2003,7 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan) int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf) { + struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan); int err; if (!buf) { @@ -1885,17 +2016,29 @@ int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf) return -ENOTCONN; } + if (atomic_test_bit(chan->status, BT_L2CAP_STATUS_SHUTDOWN)) { + return -ESHUTDOWN; + } + if (IS_ENABLED(CONFIG_BT_BREDR) && chan->conn->type == BT_CONN_TYPE_BR) { return bt_l2cap_br_chan_send(chan, buf); } - err = l2cap_chan_le_send_sdu(BT_L2CAP_LE_CHAN(chan), &buf, 0); + /* Queue if there pending segments left from previous packets */ + if (ch->tx_buf || !k_fifo_is_empty(&ch->tx_queue)) { + data_sent(buf)->len = 0; + net_buf_put(&ch->tx_queue, buf); + k_work_submit(&ch->tx_work); + return 0; + } + + err = l2cap_chan_le_send_sdu(ch, &buf, 0); if (err < 0) { - if (err == -EAGAIN) { - /* Queue buffer to be sent later */ - net_buf_put(&(BT_L2CAP_LE_CHAN(chan))->tx_queue, buf); - return *((u16_t *)net_buf_user_data(buf)); + if (err == -EAGAIN && data_sent(buf)->len) { + /* Queue buffer if at least one segment could be sent */ + net_buf_put(&ch->tx_queue, buf); + return data_sent(buf)->len; } BT_ERR("failed to send message %d", err); } diff --git a/subsys/bluetooth/host/l2cap_internal.h b/subsys/bluetooth/host/l2cap_internal.h index 5c57ae0ecadd5..8f06795533c4b 100644 --- a/subsys/bluetooth/host/l2cap_internal.h +++ b/subsys/bluetooth/host/l2cap_internal.h @@ -222,8 +222,6 @@ struct bt_l2cap_br_fixed_chan { .accept = _accept, \ } -void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data); - /* Notify L2CAP channels of a new connection */ void bt_l2cap_connected(struct bt_conn *conn); diff --git a/subsys/net/l2/bluetooth/bluetooth.c b/subsys/net/l2/bluetooth/bluetooth.c index 1192ce4c64cb9..60f1b67c3910f 100644 --- a/subsys/net/l2/bluetooth/bluetooth.c +++ b/subsys/net/l2/bluetooth/bluetooth.c @@ -114,6 +114,8 @@ static int net_bt_send(struct net_if *iface, struct net_pkt *pkt) ret = bt_l2cap_chan_send(&conn->ipsp_chan.chan, buffer); if (ret < 0) { + NET_ERR("Unable to send packet: %d", ret); + bt_l2cap_chan_disconnect(&conn->ipsp_chan.chan); return ret; } @@ -250,7 +252,7 @@ static struct net_buf *ipsp_alloc_buf(struct bt_l2cap_chan *chan) { NET_DBG("Channel %p requires buffer", chan); - return net_pkt_get_reserve_rx_data(K_FOREVER); + return net_pkt_get_reserve_rx_data(BUF_TIMEOUT); } static struct bt_l2cap_chan_ops ipsp_ops = {