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;
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;
}
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);
/* 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);
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);
__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);
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)
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
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.
dcid = chan->scid;
- l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
+ l2cap_chan_set_timer(chan, sk->sk_sndtimeo);
chan->ident = cmd->ident;
/* 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;
}
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;
}
/* 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;
}
/* 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;
}
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);
}
}
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;
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;
}
static const struct proto_ops l2cap_sock_ops;
-/* ---- L2CAP timers ---- */
-static void l2cap_sock_timeout(unsigned long arg)
-{
- struct sock *sk = (struct sock *) arg;
- int reason;
-
- BT_DBG("sock %p state %d", sk, sk->sk_state);
-
- bh_lock_sock(sk);
-
- if (sock_owned_by_user(sk)) {
- /* sk is owned by user. Try again later */
- l2cap_sock_set_timer(sk, 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 &&
- l2cap_pi(sk)->chan->sec_level != BT_SECURITY_SDP)
- reason = ECONNREFUSED;
- else
- reason = ETIMEDOUT;
-
- __l2cap_chan_close(l2cap_pi(sk)->chan, reason);
-
- bh_unlock_sock(sk);
-
- l2cap_sock_kill(sk);
- sock_put(sk);
-}
-
-void l2cap_sock_set_timer(struct sock *sk, long timeout)
-{
- BT_DBG("sk %p state %d timeout %ld", sk, sk->sk_state, timeout);
- sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout);
-}
-
-void l2cap_sock_clear_timer(struct sock *sk)
-{
- BT_DBG("sock %p state %d", sk, sk->sk_state);
- sk_stop_timer(sk, &sk->sk_timer);
-}
-
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
{
struct sock *sk = sock->sk;
err = __l2cap_wait_ack(sk);
sk->sk_shutdown = SHUTDOWN_MASK;
- l2cap_sock_clear_timer(sk);
+ l2cap_chan_clear_timer(chan);
__l2cap_chan_close(chan, 0);
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
sk->sk_protocol = proto;
sk->sk_state = BT_OPEN;
- setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk);
-
return sk;
}