Bluetooth: L2CAP: Fix not cleanup properly if ACL is disconnected

If ACL is disconnected with channels still active the code should call
l2cap_chan_del to clean it up properly.

Change-Id: Iffa9345a9697ac80c1f2295578c7161ffeb44420
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Luiz Augusto von Dentz 2016-06-28 16:40:57 +03:00 committed by Johan Hedberg
commit b008f74b0b
2 changed files with 42 additions and 31 deletions

View file

@ -55,6 +55,7 @@ struct bt_l2cap_chan {
/** Channel operations reference */
struct bt_l2cap_chan_ops *ops;
struct bt_l2cap_chan *_next;
void (*destroy)(struct bt_l2cap_chan *chan);
};
/** @brief LE L2CAP Endpoint structure. */

View file

@ -190,6 +190,21 @@ void bt_l2cap_connected(struct bt_conn *conn)
}
}
static void l2cap_chan_del(struct bt_l2cap_chan *chan)
{
BT_DBG("conn %p chan %p", chan->conn, chan);
chan->conn = NULL;
if (chan->ops && chan->ops->disconnected) {
chan->ops->disconnected(chan);
}
if (chan->destroy) {
chan->destroy(chan);
}
}
void bt_l2cap_disconnected(struct bt_conn *conn)
{
struct bt_l2cap_chan *chan;
@ -200,11 +215,8 @@ void bt_l2cap_disconnected(struct bt_conn *conn)
/* prefetch since disconnected callback may cleanup */
next = chan->_next;
if (chan->ops->disconnected) {
chan->ops->disconnected(chan);
}
l2cap_chan_del(chan);
chan->conn = NULL;
chan = next;
}
@ -412,6 +424,27 @@ static void l2cap_chan_rx_give_credits(struct bt_l2cap_le_chan *chan,
}
}
static void l2cap_chan_destroy(struct bt_l2cap_chan *chan)
{
struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan);
BT_DBG("chan %p cid 0x%04x", chan->conn, ch, ch->rx.cid);
/* There could be a writer waiting for credits so return a dummy credit
* to wake it up.
*/
if (!ch->tx.credits.nsig) {
l2cap_chan_tx_give_credits(ch, 1);
}
/* Destroy segmented SDU if it exists */
if (ch->_sdu) {
net_buf_unref(ch->_sdu);
ch->_sdu = NULL;
ch->_sdu_len = 0;
}
}
static void le_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
struct net_buf *buf)
{
@ -488,6 +521,8 @@ static void le_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
if (l2cap_chan_add(conn, chan)) {
struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan);
chan->destroy = l2cap_chan_destroy;
/* Init TX parameters */
l2cap_chan_tx_init(ch);
ch->tx.cid = scid;
@ -542,33 +577,6 @@ static struct bt_l2cap_le_chan *l2cap_remove_tx_cid(struct bt_conn *conn,
return NULL;
}
static void l2cap_chan_del(struct bt_l2cap_chan *chan)
{
struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan);
BT_DBG("conn %p chan %p cid 0x%04x", chan->conn, ch, ch->rx.cid);
ch->chan.conn = NULL;
if (chan->ops && chan->ops->disconnected) {
chan->ops->disconnected(chan);
}
/* There could be a writer waiting for credits so return a dummy credit
* to wake it up.
*/
if (!ch->tx.credits.nsig) {
l2cap_chan_tx_give_credits(ch, 1);
}
/* Destroy segmented SDU if it exists */
if (ch->_sdu) {
net_buf_unref(ch->_sdu);
ch->_sdu = NULL;
ch->_sdu_len = 0;
}
}
static void le_disconn_req(struct bt_l2cap *l2cap, uint8_t ident,
struct net_buf *buf)
{
@ -1167,6 +1175,8 @@ static int l2cap_le_connect(struct bt_conn *conn, struct bt_l2cap_le_chan *ch,
return -ENOMEM;
}
ch->chan.destroy = l2cap_chan_destroy;
buf = bt_l2cap_create_pdu(&le_sig);
if (!buf) {
BT_ERR("Unable to send L2CAP connection request");