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 */ /** Channel operations reference */
struct bt_l2cap_chan_ops *ops; struct bt_l2cap_chan_ops *ops;
struct bt_l2cap_chan *_next; struct bt_l2cap_chan *_next;
void (*destroy)(struct bt_l2cap_chan *chan);
}; };
/** @brief LE L2CAP Endpoint structure. */ /** @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) void bt_l2cap_disconnected(struct bt_conn *conn)
{ {
struct bt_l2cap_chan *chan; struct bt_l2cap_chan *chan;
@ -200,11 +215,8 @@ void bt_l2cap_disconnected(struct bt_conn *conn)
/* prefetch since disconnected callback may cleanup */ /* prefetch since disconnected callback may cleanup */
next = chan->_next; next = chan->_next;
if (chan->ops->disconnected) { l2cap_chan_del(chan);
chan->ops->disconnected(chan);
}
chan->conn = NULL;
chan = next; 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, static void le_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
struct net_buf *buf) 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)) { if (l2cap_chan_add(conn, chan)) {
struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan); struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan);
chan->destroy = l2cap_chan_destroy;
/* Init TX parameters */ /* Init TX parameters */
l2cap_chan_tx_init(ch); l2cap_chan_tx_init(ch);
ch->tx.cid = scid; ch->tx.cid = scid;
@ -542,33 +577,6 @@ static struct bt_l2cap_le_chan *l2cap_remove_tx_cid(struct bt_conn *conn,
return NULL; 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, static void le_disconn_req(struct bt_l2cap *l2cap, uint8_t ident,
struct net_buf *buf) 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; return -ENOMEM;
} }
ch->chan.destroy = l2cap_chan_destroy;
buf = bt_l2cap_create_pdu(&le_sig); buf = bt_l2cap_create_pdu(&le_sig);
if (!buf) { if (!buf) {
BT_ERR("Unable to send L2CAP connection request"); BT_ERR("Unable to send L2CAP connection request");