[Bluetooth] Change retrieval of L2CAP features mask
[pandora-kernel.git] / net / bluetooth / l2cap.c
index a4849f2..2e3abdf 100644 (file)
@@ -55,7 +55,7 @@
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "2.9"
+#define VERSION "2.10"
 
 static u32 l2cap_feat_mask = 0x0000;
 
@@ -253,6 +253,21 @@ static void l2cap_chan_del(struct sock *sk, int err)
                sk->sk_state_change(sk);
 }
 
+/* Service level security */
+static inline int l2cap_check_link_mode(struct sock *sk)
+{
+       struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+
+       if ((l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) ||
+                               (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE))
+               return hci_conn_encrypt(conn->hcon);
+
+       if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH)
+               return hci_conn_auth(conn->hcon);
+
+       return 1;
+}
+
 static inline u8 l2cap_get_ident(struct l2cap_conn *conn)
 {
        u8 id;
@@ -287,6 +302,34 @@ static inline int l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16
        return hci_send_acl(conn->hcon, skb, 0);
 }
 
+static void l2cap_do_start(struct sock *sk)
+{
+       struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+
+       if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+               struct l2cap_conn_req req;
+               req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+               req.psm  = l2cap_pi(sk)->psm;
+
+               l2cap_pi(sk)->ident = l2cap_get_ident(conn);
+
+               l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+                                       L2CAP_CONN_REQ, sizeof(req), &req);
+       } else {
+               struct l2cap_info_req req;
+               req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
+
+               conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
+               conn->info_ident = l2cap_get_ident(conn);
+
+               mod_timer(&conn->info_timer, jiffies +
+                                       msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
+
+               l2cap_send_cmd(conn, conn->info_ident,
+                                       L2CAP_INFO_REQ, sizeof(req), &req);
+       }
+}
+
 /* ---- L2CAP connections ---- */
 static void l2cap_conn_start(struct l2cap_conn *conn)
 {
@@ -301,16 +344,35 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                bh_lock_sock(sk);
 
                if (sk->sk_type != SOCK_SEQPACKET) {
-                       l2cap_sock_clear_timer(sk);
-                       sk->sk_state = BT_CONNECTED;
-                       sk->sk_state_change(sk);
-               } else if (sk->sk_state == BT_CONNECT) {
+                       bh_unlock_sock(sk);
+                       continue;
+               }
+
+               if (sk->sk_state == BT_CONNECT) {
                        struct l2cap_conn_req req;
-                       l2cap_pi(sk)->ident = l2cap_get_ident(conn);
                        req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
                        req.psm  = l2cap_pi(sk)->psm;
+
+                       l2cap_pi(sk)->ident = l2cap_get_ident(conn);
+
                        l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
                                        L2CAP_CONN_REQ, sizeof(req), &req);
+               } else if (sk->sk_state == BT_CONNECT2) {
+                       struct l2cap_conn_rsp rsp;
+                       rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
+                       rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
+
+                       if (l2cap_check_link_mode(sk)) {
+                               sk->sk_state = BT_CONFIG;
+                               rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
+                               rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
+                       } else {
+                               rsp.result = cpu_to_le16(L2CAP_CR_PEND);
+                               rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
+                       }
+
+                       l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+                                       L2CAP_CONN_RSP, sizeof(rsp), &rsp);
                }
 
                bh_unlock_sock(sk);
@@ -321,22 +383,27 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
 
 static void l2cap_conn_ready(struct l2cap_conn *conn)
 {
-       BT_DBG("conn %p", conn);
+       struct l2cap_chan_list *l = &conn->chan_list;
+       struct sock *sk;
 
-       if (conn->chan_list.head || !hlist_empty(&l2cap_sk_list.head)) {
-               struct l2cap_info_req req;
+       BT_DBG("conn %p", conn);
 
-               req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
+       read_lock(&l->lock);
 
-               conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
-               conn->info_ident = l2cap_get_ident(conn);
+       for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+               bh_lock_sock(sk);
 
-               mod_timer(&conn->info_timer,
-                       jiffies + msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
+               if (sk->sk_type != SOCK_SEQPACKET) {
+                       l2cap_sock_clear_timer(sk);
+                       sk->sk_state = BT_CONNECTED;
+                       sk->sk_state_change(sk);
+               } else if (sk->sk_state == BT_CONNECT)
+                       l2cap_do_start(sk);
 
-               l2cap_send_cmd(conn, conn->info_ident,
-                                       L2CAP_INFO_REQ, sizeof(req), &req);
+               bh_unlock_sock(sk);
        }
+
+       read_unlock(&l->lock);
 }
 
 /* Notify sockets that we cannot guaranty reliability anymore */
@@ -729,22 +796,11 @@ static int l2cap_do_connect(struct sock *sk)
        l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
 
        if (hcon->state == BT_CONNECTED) {
-               if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)) {
-                       l2cap_conn_ready(conn);
-                       goto done;
-               }
-
-               if (sk->sk_type == SOCK_SEQPACKET) {
-                       struct l2cap_conn_req req;
-                       l2cap_pi(sk)->ident = l2cap_get_ident(conn);
-                       req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
-                       req.psm  = l2cap_pi(sk)->psm;
-                       l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
-                                       L2CAP_CONN_REQ, sizeof(req), &req);
-               } else {
+               if (sk->sk_type != SOCK_SEQPACKET) {
                        l2cap_sock_clear_timer(sk);
                        sk->sk_state = BT_CONNECTED;
-               }
+               } else
+                       l2cap_do_start(sk);
        }
 
 done:
@@ -1477,7 +1533,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
        struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
        struct l2cap_conn_rsp rsp;
        struct sock *sk, *parent;
-       int result = 0, status = 0;
+       int result, status = 0;
 
        u16 dcid = 0, scid = __le16_to_cpu(req->scid);
        __le16 psm  = req->psm;
@@ -1526,25 +1582,24 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
 
        l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
 
-       /* Service level security */
-       result = L2CAP_CR_PEND;
-       status = L2CAP_CS_AUTHEN_PEND;
-       sk->sk_state = BT_CONNECT2;
        l2cap_pi(sk)->ident = cmd->ident;
 
-       if ((l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) ||
-                       (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)) {
-               if (!hci_conn_encrypt(conn->hcon))
-                       goto done;
-       } else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) {
-               if (!hci_conn_auth(conn->hcon))
-                       goto done;
+       if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+               if (l2cap_check_link_mode(sk)) {
+                       sk->sk_state = BT_CONFIG;
+                       result = L2CAP_CR_SUCCESS;
+                       status = L2CAP_CS_NO_INFO;
+               } else {
+                       sk->sk_state = BT_CONNECT2;
+                       result = L2CAP_CR_PEND;
+                       status = L2CAP_CS_AUTHEN_PEND;
+               }
+       } else {
+               sk->sk_state = BT_CONNECT2;
+               result = L2CAP_CR_PEND;
+               status = L2CAP_CS_NO_INFO;
        }
 
-       sk->sk_state = BT_CONFIG;
-       result = status = 0;
-
-done:
        write_unlock_bh(&list->lock);
 
 response:
@@ -1556,6 +1611,21 @@ sendresp:
        rsp.result = cpu_to_le16(result);
        rsp.status = cpu_to_le16(status);
        l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+
+       if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) {
+               struct l2cap_info_req info;
+               info.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
+
+               conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
+               conn->info_ident = l2cap_get_ident(conn);
+
+               mod_timer(&conn->info_timer, jiffies +
+                                       msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
+
+               l2cap_send_cmd(conn, conn->info_ident,
+                                       L2CAP_INFO_REQ, sizeof(info), &info);
+       }
+
        return 0;
 }
 
@@ -1664,9 +1734,9 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
        }
 
        if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {
-               u8 req[64];
+               u8 buf[64];
                l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
-                                       l2cap_build_conf_req(sk, req), req);
+                                       l2cap_build_conf_req(sk, buf), buf);
        }
 
 unlock:
@@ -1827,7 +1897,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
        del_timer(&conn->info_timer);
 
        if (type == L2CAP_IT_FEAT_MASK)
-               conn->feat_mask = __le32_to_cpu(get_unaligned((__le32 *) rsp->data));
+               conn->feat_mask = get_unaligned_le32(rsp->data);
 
        l2cap_conn_start(conn);