|
@@ -208,6 +208,56 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void l2cap_chan_set_timer(struct l2cap_chan *chan, long timeout)
|
|
|
+{
|
|
|
+ BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->sk->sk_state,
|
|
|
+ timeout);
|
|
|
+ if (!mod_timer(&chan->chan_timer, jiffies + timeout))
|
|
|
+ sock_hold(chan->sk);
|
|
|
+}
|
|
|
+
|
|
|
+void l2cap_chan_clear_timer(struct l2cap_chan *chan)
|
|
|
+{
|
|
|
+ BT_DBG("chan %p state %d", chan, chan->sk->sk_state);
|
|
|
+
|
|
|
+ if (timer_pending(&chan->chan_timer) && del_timer(&chan->chan_timer))
|
|
|
+ __sock_put(chan->sk);
|
|
|
+}
|
|
|
+
|
|
|
+static void l2cap_chan_timeout(unsigned long arg)
|
|
|
+{
|
|
|
+ struct l2cap_chan *chan = (struct l2cap_chan *) arg;
|
|
|
+ struct sock *sk = chan->sk;
|
|
|
+ int reason;
|
|
|
+
|
|
|
+ BT_DBG("chan %p state %d", chan, sk->sk_state);
|
|
|
+
|
|
|
+ bh_lock_sock(sk);
|
|
|
+
|
|
|
+ if (sock_owned_by_user(sk)) {
|
|
|
+ /* sk is owned by user. Try again later */
|
|
|
+ l2cap_chan_set_timer(chan, HZ / 5);
|
|
|
+ bh_unlock_sock(sk);
|
|
|
+ sock_put(sk);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)
|
|
|
+ reason = ECONNREFUSED;
|
|
|
+ else if (sk->sk_state == BT_CONNECT &&
|
|
|
+ chan->sec_level != BT_SECURITY_SDP)
|
|
|
+ reason = ECONNREFUSED;
|
|
|
+ else
|
|
|
+ reason = ETIMEDOUT;
|
|
|
+
|
|
|
+ __l2cap_chan_close(chan, reason);
|
|
|
+
|
|
|
+ bh_unlock_sock(sk);
|
|
|
+
|
|
|
+ l2cap_sock_kill(sk);
|
|
|
+ sock_put(sk);
|
|
|
+}
|
|
|
+
|
|
|
struct l2cap_chan *l2cap_chan_create(struct sock *sk)
|
|
|
{
|
|
|
struct l2cap_chan *chan;
|
|
@@ -222,6 +272,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk)
|
|
|
list_add(&chan->global_l, &chan_list);
|
|
|
write_unlock_bh(&chan_list_lock);
|
|
|
|
|
|
+ setup_timer(&chan->chan_timer, l2cap_chan_timeout, (unsigned long) chan);
|
|
|
+
|
|
|
return chan;
|
|
|
}
|
|
|
|
|
@@ -281,7 +333,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
|
|
|
struct l2cap_conn *conn = chan->conn;
|
|
|
struct sock *parent = bt_sk(sk)->parent;
|
|
|
|
|
|
- l2cap_sock_clear_timer(sk);
|
|
|
+ l2cap_chan_clear_timer(chan);
|
|
|
|
|
|
BT_DBG("chan %p, conn %p, err %d", chan, conn, err);
|
|
|
|
|
@@ -334,7 +386,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
|
|
|
/* Must be called on unlocked socket. */
|
|
|
static void l2cap_chan_close(struct sock *sk)
|
|
|
{
|
|
|
- l2cap_sock_clear_timer(sk);
|
|
|
+ l2cap_chan_clear_timer(l2cap_pi(sk)->chan);
|
|
|
lock_sock(sk);
|
|
|
__l2cap_chan_close(l2cap_pi(sk)->chan, ECONNRESET);
|
|
|
release_sock(sk);
|
|
@@ -371,7 +423,7 @@ void __l2cap_chan_close(struct l2cap_chan *chan, int reason)
|
|
|
case BT_CONFIG:
|
|
|
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
|
|
|
conn->hcon->type == ACL_LINK) {
|
|
|
- l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
|
|
|
+ l2cap_chan_set_timer(chan, sk->sk_sndtimeo);
|
|
|
l2cap_send_disconn_req(conn, chan, reason);
|
|
|
} else
|
|
|
l2cap_chan_del(chan, reason);
|
|
@@ -814,7 +866,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
|
|
|
|
|
|
__l2cap_chan_add(conn, chan);
|
|
|
|
|
|
- l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
|
|
|
+ l2cap_chan_set_timer(chan, sk->sk_sndtimeo);
|
|
|
|
|
|
sk->sk_state = BT_CONNECTED;
|
|
|
parent->sk_data_ready(parent, 0);
|
|
@@ -842,13 +894,13 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
|
|
|
bh_lock_sock(sk);
|
|
|
|
|
|
if (conn->hcon->type == LE_LINK) {
|
|
|
- l2cap_sock_clear_timer(sk);
|
|
|
+ l2cap_chan_clear_timer(chan);
|
|
|
sk->sk_state = BT_CONNECTED;
|
|
|
sk->sk_state_change(sk);
|
|
|
}
|
|
|
|
|
|
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
|
|
|
- l2cap_sock_clear_timer(sk);
|
|
|
+ l2cap_chan_clear_timer(chan);
|
|
|
sk->sk_state = BT_CONNECTED;
|
|
|
sk->sk_state_change(sk);
|
|
|
} else if (sk->sk_state == BT_CONNECT)
|
|
@@ -1047,11 +1099,11 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
|
|
|
l2cap_chan_add(conn, chan);
|
|
|
|
|
|
sk->sk_state = BT_CONNECT;
|
|
|
- l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
|
|
|
+ l2cap_chan_set_timer(chan, sk->sk_sndtimeo);
|
|
|
|
|
|
if (hcon->state == BT_CONNECTED) {
|
|
|
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
|
|
|
- l2cap_sock_clear_timer(sk);
|
|
|
+ l2cap_chan_clear_timer(chan);
|
|
|
if (l2cap_check_security(chan))
|
|
|
sk->sk_state = BT_CONNECTED;
|
|
|
} else
|
|
@@ -1615,7 +1667,7 @@ static void l2cap_chan_ready(struct sock *sk)
|
|
|
BT_DBG("sk %p, parent %p", sk, parent);
|
|
|
|
|
|
chan->conf_state = 0;
|
|
|
- l2cap_sock_clear_timer(sk);
|
|
|
+ l2cap_chan_clear_timer(chan);
|
|
|
|
|
|
if (!parent) {
|
|
|
/* Outgoing channel.
|
|
@@ -2317,7 +2369,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
|
|
|
|
|
dcid = chan->scid;
|
|
|
|
|
|
- l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
|
|
|
+ l2cap_chan_set_timer(chan, sk->sk_sndtimeo);
|
|
|
|
|
|
chan->ident = cmd->ident;
|
|
|
|
|
@@ -2434,8 +2486,8 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
|
|
/* don't delete l2cap channel if sk is owned by user */
|
|
|
if (sock_owned_by_user(sk)) {
|
|
|
sk->sk_state = BT_DISCONN;
|
|
|
- l2cap_sock_clear_timer(sk);
|
|
|
- l2cap_sock_set_timer(sk, HZ / 5);
|
|
|
+ l2cap_chan_clear_timer(chan);
|
|
|
+ l2cap_chan_set_timer(chan, HZ / 5);
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -2608,7 +2660,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
|
|
|
|
|
|
default:
|
|
|
sk->sk_err = ECONNRESET;
|
|
|
- l2cap_sock_set_timer(sk, HZ * 5);
|
|
|
+ l2cap_chan_set_timer(chan, HZ * 5);
|
|
|
l2cap_send_disconn_req(conn, chan, ECONNRESET);
|
|
|
goto done;
|
|
|
}
|
|
@@ -2664,8 +2716,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
|
|
|
/* don't delete l2cap channel if sk is owned by user */
|
|
|
if (sock_owned_by_user(sk)) {
|
|
|
sk->sk_state = BT_DISCONN;
|
|
|
- l2cap_sock_clear_timer(sk);
|
|
|
- l2cap_sock_set_timer(sk, HZ / 5);
|
|
|
+ l2cap_chan_clear_timer(chan);
|
|
|
+ l2cap_chan_set_timer(chan, HZ / 5);
|
|
|
bh_unlock_sock(sk);
|
|
|
return 0;
|
|
|
}
|
|
@@ -2698,8 +2750,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
|
|
|
/* don't delete l2cap channel if sk is owned by user */
|
|
|
if (sock_owned_by_user(sk)) {
|
|
|
sk->sk_state = BT_DISCONN;
|
|
|
- l2cap_sock_clear_timer(sk);
|
|
|
- l2cap_sock_set_timer(sk, HZ / 5);
|
|
|
+ l2cap_chan_clear_timer(chan);
|
|
|
+ l2cap_chan_set_timer(chan, HZ / 5);
|
|
|
bh_unlock_sock(sk);
|
|
|
return 0;
|
|
|
}
|
|
@@ -4090,20 +4142,18 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
|
|
|
|
|
|
static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
|
|
|
{
|
|
|
- struct sock *sk = chan->sk;
|
|
|
-
|
|
|
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED)
|
|
|
return;
|
|
|
|
|
|
if (encrypt == 0x00) {
|
|
|
if (chan->sec_level == BT_SECURITY_MEDIUM) {
|
|
|
- l2cap_sock_clear_timer(sk);
|
|
|
- l2cap_sock_set_timer(sk, HZ * 5);
|
|
|
+ l2cap_chan_clear_timer(chan);
|
|
|
+ l2cap_chan_set_timer(chan, HZ * 5);
|
|
|
} else if (chan->sec_level == BT_SECURITY_HIGH)
|
|
|
__l2cap_chan_close(chan, ECONNREFUSED);
|
|
|
} else {
|
|
|
if (chan->sec_level == BT_SECURITY_MEDIUM)
|
|
|
- l2cap_sock_clear_timer(sk);
|
|
|
+ l2cap_chan_clear_timer(chan);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -4148,8 +4198,8 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
|
|
l2cap_send_cmd(conn, chan->ident,
|
|
|
L2CAP_CONN_REQ, sizeof(req), &req);
|
|
|
} else {
|
|
|
- l2cap_sock_clear_timer(sk);
|
|
|
- l2cap_sock_set_timer(sk, HZ / 10);
|
|
|
+ l2cap_chan_clear_timer(chan);
|
|
|
+ l2cap_chan_set_timer(chan, HZ / 10);
|
|
|
}
|
|
|
} else if (sk->sk_state == BT_CONNECT2) {
|
|
|
struct l2cap_conn_rsp rsp;
|
|
@@ -4160,7 +4210,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
|
|
result = L2CAP_CR_SUCCESS;
|
|
|
} else {
|
|
|
sk->sk_state = BT_DISCONN;
|
|
|
- l2cap_sock_set_timer(sk, HZ / 10);
|
|
|
+ l2cap_chan_set_timer(chan, HZ / 10);
|
|
|
result = L2CAP_CR_SEC_BLOCK;
|
|
|
}
|
|
|
|