|
@@ -970,6 +970,45 @@ static void l2cap_info_timeout(unsigned long arg)
|
|
|
l2cap_conn_start(conn);
|
|
|
}
|
|
|
|
|
|
+static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
|
|
+{
|
|
|
+ struct l2cap_conn *conn = hcon->l2cap_data;
|
|
|
+ struct l2cap_chan *chan, *l;
|
|
|
+ struct sock *sk;
|
|
|
+
|
|
|
+ if (!conn)
|
|
|
+ return;
|
|
|
+
|
|
|
+ BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
|
|
|
+
|
|
|
+ kfree_skb(conn->rx_skb);
|
|
|
+
|
|
|
+ /* Kill channels */
|
|
|
+ list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
|
|
|
+ sk = chan->sk;
|
|
|
+ bh_lock_sock(sk);
|
|
|
+ l2cap_chan_del(chan, err);
|
|
|
+ bh_unlock_sock(sk);
|
|
|
+ chan->ops->close(chan->data);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
|
|
|
+ del_timer_sync(&conn->info_timer);
|
|
|
+
|
|
|
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
|
|
|
+ del_timer(&conn->security_timer);
|
|
|
+
|
|
|
+ hcon->l2cap_data = NULL;
|
|
|
+ kfree(conn);
|
|
|
+}
|
|
|
+
|
|
|
+static void security_timeout(unsigned long arg)
|
|
|
+{
|
|
|
+ struct l2cap_conn *conn = (void *) arg;
|
|
|
+
|
|
|
+ l2cap_conn_del(conn->hcon, ETIMEDOUT);
|
|
|
+}
|
|
|
+
|
|
|
static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
|
|
|
{
|
|
|
struct l2cap_conn *conn = hcon->l2cap_data;
|
|
@@ -1001,7 +1040,10 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
|
|
|
|
|
|
INIT_LIST_HEAD(&conn->chan_l);
|
|
|
|
|
|
- if (hcon->type != LE_LINK)
|
|
|
+ if (hcon->type == LE_LINK)
|
|
|
+ setup_timer(&conn->security_timer, security_timeout,
|
|
|
+ (unsigned long) conn);
|
|
|
+ else
|
|
|
setup_timer(&conn->info_timer, l2cap_info_timeout,
|
|
|
(unsigned long) conn);
|
|
|
|
|
@@ -1010,35 +1052,6 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
|
|
|
return conn;
|
|
|
}
|
|
|
|
|
|
-static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
|
|
-{
|
|
|
- struct l2cap_conn *conn = hcon->l2cap_data;
|
|
|
- struct l2cap_chan *chan, *l;
|
|
|
- struct sock *sk;
|
|
|
-
|
|
|
- if (!conn)
|
|
|
- return;
|
|
|
-
|
|
|
- BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
|
|
|
-
|
|
|
- kfree_skb(conn->rx_skb);
|
|
|
-
|
|
|
- /* Kill channels */
|
|
|
- list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
|
|
|
- sk = chan->sk;
|
|
|
- bh_lock_sock(sk);
|
|
|
- l2cap_chan_del(chan, err);
|
|
|
- bh_unlock_sock(sk);
|
|
|
- chan->ops->close(chan->data);
|
|
|
- }
|
|
|
-
|
|
|
- if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
|
|
|
- del_timer_sync(&conn->info_timer);
|
|
|
-
|
|
|
- hcon->l2cap_data = NULL;
|
|
|
- kfree(conn);
|
|
|
-}
|
|
|
-
|
|
|
static inline void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
|
|
|
{
|
|
|
write_lock_bh(&conn->chan_lock);
|
|
@@ -4182,6 +4195,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
|
|
if (chan->scid == L2CAP_CID_LE_DATA) {
|
|
|
if (!status && encrypt) {
|
|
|
chan->sec_level = hcon->sec_level;
|
|
|
+ del_timer(&conn->security_timer);
|
|
|
l2cap_chan_ready(sk);
|
|
|
}
|
|
|
|