From bdc1eff35b5bb8f6fff498797c47e33bfdf14ff3 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 19 Nov 2019 15:31:37 +0200 Subject: [PATCH] Bluetooth: L2CAP: Introduce BT_L2CAP_STATUS_SHUTDOWN flag This introduces BT_L2CAP_STATUS_SHUTDOWN which is used to indicate when a channel has been shutdown. Signed-off-by: Luiz Augusto von Dentz --- include/bluetooth/l2cap.h | 7 +++++ subsys/bluetooth/host/l2cap.c | 55 ++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/include/bluetooth/l2cap.h b/include/bluetooth/l2cap.h index b33652ace5e..c67db8d7f59 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; diff --git a/subsys/bluetooth/host/l2cap.c b/subsys/bluetooth/host/l2cap.c index 32d84fbc547..de334609997 100644 --- a/subsys/bluetooth/host/l2cap.c +++ b/subsys/bluetooth/host/l2cap.c @@ -255,6 +255,8 @@ destroy: if (chan->destroy) { chan->destroy(chan); } + + atomic_clear(chan->status); } static void l2cap_rtx_timeout(struct k_work *work) @@ -1503,6 +1505,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) { @@ -1517,7 +1557,10 @@ static void l2cap_chan_send_credits(struct bt_l2cap_le_chan *chan, sizeof(*ev)); if (!buf) { BT_ERR("Unable to send credits update"); - bt_l2cap_chan_disconnect(&chan->chan); + /* Disconnect would probably not work either so the only + * option left is to shutdown the channel. + */ + l2cap_chan_shutdown(&chan->chan); return; } @@ -1727,6 +1770,12 @@ static void l2cap_chan_recv_queue(struct bt_l2cap_le_chan *chan, 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); } @@ -1956,6 +2005,10 @@ 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);