Bluetooth: notify userspace of security level change
[linux-flexiantxendom0-3.2.10.git] / net / bluetooth / l2cap_sock.c
index b48d6c1..04e7c17 100644 (file)
@@ -82,7 +82,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
        }
 
        if (la.l2_cid)
-               err = l2cap_add_scid(chan, la.l2_cid);
+               err = l2cap_add_scid(chan, __le16_to_cpu(la.l2_cid));
        else
                err = l2cap_add_psm(chan, &la.l2_bdaddr, la.l2_psm);
 
@@ -123,15 +123,18 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
        if (la.l2_cid && la.l2_psm)
                return -EINVAL;
 
-       err = l2cap_chan_connect(chan, la.l2_psm, la.l2_cid, &la.l2_bdaddr);
+       err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
+                               &la.l2_bdaddr);
        if (err)
-               goto done;
+               return err;
+
+       lock_sock(sk);
 
        err = bt_sock_wait_state(sk, BT_CONNECTED,
                        sock_sndtimeo(sk, flags & O_NONBLOCK));
-done:
-       if (sock_owned_by_user(sk))
-               release_sock(sk);
+
+       release_sock(sk);
+
        return err;
 }
 
@@ -589,10 +592,14 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
                        sk->sk_state = BT_CONFIG;
                        chan->state = BT_CONFIG;
 
-               /* or for ACL link, under defer_setup time */
-               } else if (sk->sk_state == BT_CONNECT2 &&
-                                       bt_sk(sk)->defer_setup) {
-                       err = l2cap_chan_check_security(chan);
+               /* or for ACL link */
+               } else if ((sk->sk_state == BT_CONNECT2 &&
+                          bt_sk(sk)->defer_setup) ||
+                          sk->sk_state == BT_CONNECTED) {
+                       if (!l2cap_chan_check_security(chan))
+                               bt_sk(sk)->suspended = true;
+                       else
+                               sk->sk_state_change(sk);
                } else {
                        err = -EINVAL;
                }
@@ -796,6 +803,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
 {
        struct sock *sk = sock->sk;
        struct l2cap_chan *chan;
+       struct l2cap_conn *conn;
        int err = 0;
 
        BT_DBG("sock %p, sk %p", sock, sk);
@@ -804,14 +812,23 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
                return 0;
 
        chan = l2cap_pi(sk)->chan;
+       conn = chan->conn;
 
+       if (conn)
+               mutex_lock(&conn->chan_lock);
+
+       l2cap_chan_lock(chan);
        lock_sock(sk);
+
        if (!sk->sk_shutdown) {
                if (chan->mode == L2CAP_MODE_ERTM)
                        err = __l2cap_wait_ack(sk);
 
                sk->sk_shutdown = SHUTDOWN_MASK;
+
+               release_sock(sk);
                l2cap_chan_close(chan, 0);
+               lock_sock(sk);
 
                if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
                        err = bt_sock_wait_state(sk, BT_CLOSED,
@@ -822,6 +839,11 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
                err = -sk->sk_err;
 
        release_sock(sk);
+       l2cap_chan_unlock(chan);
+
+       if (conn)
+               mutex_unlock(&conn->chan_lock);
+
        return err;
 }
 
@@ -864,8 +886,12 @@ static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
        struct sock *sk = data;
        struct l2cap_pinfo *pi = l2cap_pi(sk);
 
-       if (pi->rx_busy_skb)
-               return -ENOMEM;
+       lock_sock(sk);
+
+       if (pi->rx_busy_skb) {
+               err = -ENOMEM;
+               goto done;
+       }
 
        err = sock_queue_rcv_skb(sk, skb);
 
@@ -884,6 +910,9 @@ static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
                err = 0;
        }
 
+done:
+       release_sock(sk);
+
        return err;
 }
 
@@ -902,7 +931,8 @@ static void l2cap_sock_state_change_cb(void *data, int state)
 }
 
 static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
-                                       unsigned long len, int nb, int *err)
+                                              unsigned long len, int nb,
+                                              int *err)
 {
        struct sock *sk = chan->sk;
 
@@ -1015,7 +1045,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p
        INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
 
        sk->sk_destruct = l2cap_sock_destruct;
-       sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT);
+       sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT;
 
        sock_reset_flag(sk, SOCK_ZAPPED);