]> rtime.felk.cvut.cz Git - linux-imx.git/blobdiff - net/bluetooth/l2cap_core.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
[linux-imx.git] / net / bluetooth / l2cap_core.c
index 68843a28a7af6a3e52f92f0cde033e03b031560a..8c3499bec89319289073b2f1e7f3f202cd179ca6 100644 (file)
@@ -504,8 +504,10 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
                if (conn->hcon->type == LE_LINK) {
                        /* LE connection */
                        chan->omtu = L2CAP_DEFAULT_MTU;
-                       chan->scid = L2CAP_CID_LE_DATA;
-                       chan->dcid = L2CAP_CID_LE_DATA;
+                       if (chan->dcid == L2CAP_CID_ATT)
+                               chan->scid = L2CAP_CID_ATT;
+                       else
+                               chan->scid = l2cap_alloc_cid(conn);
                } else {
                        /* Alloc CID for connection-oriented socket */
                        chan->scid = l2cap_alloc_cid(conn);
@@ -543,6 +545,8 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
 
        l2cap_chan_hold(chan);
 
+       hci_conn_hold(conn->hcon);
+
        list_add(&chan->list, &conn->chan_l);
 }
 
@@ -1338,17 +1342,21 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
 
 static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 {
-       struct sock *parent, *sk;
+       struct sock *parent;
        struct l2cap_chan *chan, *pchan;
 
        BT_DBG("");
 
        /* Check if we have socket listening on cid */
-       pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA,
+       pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
                                          conn->src, conn->dst);
        if (!pchan)
                return;
 
+       /* Client ATT sockets should override the server one */
+       if (__l2cap_get_chan_by_dcid(conn, L2CAP_CID_ATT))
+               return;
+
        parent = pchan->sk;
 
        lock_sock(parent);
@@ -1357,17 +1365,12 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
        if (!chan)
                goto clean;
 
-       sk = chan->sk;
-
-       hci_conn_hold(conn->hcon);
-       conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
-
-       bacpy(&bt_sk(sk)->src, conn->src);
-       bacpy(&bt_sk(sk)->dst, conn->dst);
+       chan->dcid = L2CAP_CID_ATT;
 
-       l2cap_chan_add(conn, chan);
+       bacpy(&bt_sk(chan->sk)->src, conn->src);
+       bacpy(&bt_sk(chan->sk)->dst, conn->dst);
 
-       l2cap_chan_ready(chan);
+       __l2cap_chan_add(conn, chan);
 
 clean:
        release_sock(parent);
@@ -1380,14 +1383,17 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
 
        BT_DBG("conn %p", conn);
 
-       if (!hcon->out && hcon->type == LE_LINK)
-               l2cap_le_conn_ready(conn);
-
+       /* For outgoing pairing which doesn't necessarily have an
+        * associated socket (e.g. mgmt_pair_device).
+        */
        if (hcon->out && hcon->type == LE_LINK)
                smp_conn_security(hcon, hcon->pending_sec_level);
 
        mutex_lock(&conn->chan_lock);
 
+       if (hcon->type == LE_LINK)
+               l2cap_le_conn_ready(conn);
+
        list_for_each_entry(chan, &conn->chan_l, list) {
 
                l2cap_chan_lock(chan);
@@ -1792,7 +1798,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
 
        auth_type = l2cap_get_auth_type(chan);
 
-       if (chan->dcid == L2CAP_CID_LE_DATA)
+       if (bdaddr_type_is_le(dst_type))
                hcon = hci_connect(hdev, LE_LINK, dst, dst_type,
                                   chan->sec_level, auth_type);
        else
@@ -1811,16 +1817,10 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
                goto done;
        }
 
-       if (hcon->type == LE_LINK) {
-               err = 0;
-
-               if (!list_empty(&conn->chan_l)) {
-                       err = -EBUSY;
-                       hci_conn_drop(hcon);
-               }
-
-               if (err)
-                       goto done;
+       if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
+               hci_conn_drop(hcon);
+               err = -EBUSY;
+               goto done;
        }
 
        /* Update source addr of the socket */
@@ -1830,6 +1830,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        l2cap_chan_add(conn, chan);
        l2cap_chan_lock(chan);
 
+       /* l2cap_chan_add takes its own ref so we can drop this one */
+       hci_conn_drop(hcon);
+
        l2cap_state_change(chan, BT_CONNECT);
        __set_chan_timer(chan, sk->sk_sndtimeo);
 
@@ -3751,8 +3754,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
 
        sk = chan->sk;
 
-       hci_conn_hold(conn->hcon);
-
        bacpy(&bt_sk(sk)->src, conn->src);
        bacpy(&bt_sk(sk)->dst, conn->dst);
        chan->psm  = psm;
@@ -5292,6 +5293,51 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
        }
 }
 
+static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
+                                       struct sk_buff *skb)
+{
+       u8 *data = skb->data;
+       int len = skb->len;
+       struct l2cap_cmd_hdr cmd;
+       int err;
+
+       l2cap_raw_recv(conn, skb);
+
+       while (len >= L2CAP_CMD_HDR_SIZE) {
+               u16 cmd_len;
+               memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
+               data += L2CAP_CMD_HDR_SIZE;
+               len  -= L2CAP_CMD_HDR_SIZE;
+
+               cmd_len = le16_to_cpu(cmd.len);
+
+               BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len,
+                      cmd.ident);
+
+               if (cmd_len > len || !cmd.ident) {
+                       BT_DBG("corrupted command");
+                       break;
+               }
+
+               err = l2cap_le_sig_cmd(conn, &cmd, data);
+               if (err) {
+                       struct l2cap_cmd_rej_unk rej;
+
+                       BT_ERR("Wrong link type (%d)", err);
+
+                       /* FIXME: Map err to a valid reason */
+                       rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
+                       l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
+                                      sizeof(rej), &rej);
+               }
+
+               data += cmd_len;
+               len  -= cmd_len;
+       }
+
+       kfree_skb(skb);
+}
+
 static inline void l2cap_sig_channel(struct l2cap_conn *conn,
                                     struct sk_buff *skb)
 {
@@ -5318,11 +5364,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
                        break;
                }
 
-               if (conn->hcon->type == LE_LINK)
-                       err = l2cap_le_sig_cmd(conn, &cmd, data);
-               else
-                       err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data);
-
+               err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data);
                if (err) {
                        struct l2cap_cmd_rej_unk rej;
 
@@ -6356,16 +6398,13 @@ static void l2cap_att_channel(struct l2cap_conn *conn,
 {
        struct l2cap_chan *chan;
 
-       chan = l2cap_global_chan_by_scid(0, L2CAP_CID_LE_DATA,
+       chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT,
                                         conn->src, conn->dst);
        if (!chan)
                goto drop;
 
        BT_DBG("chan %p, len %d", chan, skb->len);
 
-       if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
-               goto drop;
-
        if (chan->imtu < skb->len)
                goto drop;
 
@@ -6395,6 +6434,8 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
 
        switch (cid) {
        case L2CAP_CID_LE_SIGNALING:
+               l2cap_le_sig_channel(conn, skb);
+               break;
        case L2CAP_CID_SIGNALING:
                l2cap_sig_channel(conn, skb);
                break;
@@ -6405,7 +6446,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
                l2cap_conless_channel(conn, psm, skb);
                break;
 
-       case L2CAP_CID_LE_DATA:
+       case L2CAP_CID_ATT:
                l2cap_att_channel(conn, skb);
                break;
 
@@ -6531,7 +6572,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
                        continue;
                }
 
-               if (chan->scid == L2CAP_CID_LE_DATA) {
+               if (chan->scid == L2CAP_CID_ATT) {
                        if (!status && encrypt) {
                                chan->sec_level = hcon->sec_level;
                                l2cap_chan_ready(chan);