Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
[deliverable/linux.git] / net / bluetooth / l2cap_core.c
index 6f30d1da89a0b4baee22e81a49192af4f81819a9..4ca88247b7c25595b8a1c05141e74be4e714a641 100644 (file)
@@ -37,6 +37,7 @@
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/smp.h>
+#include <net/bluetooth/a2mp.h>
 
 bool disable_ertm;
 
@@ -448,7 +449,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
        case L2CAP_CHAN_CONN_ORIENTED:
                if (conn->hcon->type == LE_LINK) {
                        /* LE connection */
-                       chan->omtu = L2CAP_LE_DEFAULT_MTU;
+                       chan->omtu = L2CAP_DEFAULT_MTU;
                        chan->scid = L2CAP_CID_LE_DATA;
                        chan->dcid = L2CAP_CID_LE_DATA;
                } else {
@@ -465,6 +466,13 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
                chan->omtu = L2CAP_DEFAULT_MTU;
                break;
 
+       case L2CAP_CHAN_CONN_FIX_A2MP:
+               chan->scid = L2CAP_CID_A2MP;
+               chan->dcid = L2CAP_CID_A2MP;
+               chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
+               chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+               break;
+
        default:
                /* Raw socket can send/recv signalling messages only */
                chan->scid = L2CAP_CID_SIGNALING;
@@ -484,14 +492,14 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
        list_add(&chan->list, &conn->chan_l);
 }
 
-static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
+void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
 {
        mutex_lock(&conn->chan_lock);
        __l2cap_chan_add(conn, chan);
        mutex_unlock(&conn->chan_lock);
 }
 
-static void l2cap_chan_del(struct l2cap_chan *chan, int err)
+void l2cap_chan_del(struct l2cap_chan *chan, int err)
 {
        struct l2cap_conn *conn = chan->conn;
 
@@ -506,7 +514,9 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
                l2cap_chan_put(chan);
 
                chan->conn = NULL;
-               hci_conn_put(conn->hcon);
+
+               if (chan->chan_type != L2CAP_CHAN_CONN_FIX_A2MP)
+                       hci_conn_put(conn->hcon);
        }
 
        if (chan->ops->teardown)
@@ -814,17 +824,20 @@ static inline void __pack_control(struct l2cap_chan *chan,
        }
 }
 
+static inline unsigned int __ertm_hdr_size(struct l2cap_chan *chan)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return L2CAP_EXT_HDR_SIZE;
+       else
+               return L2CAP_ENH_HDR_SIZE;
+}
+
 static struct sk_buff *l2cap_create_sframe_pdu(struct l2cap_chan *chan,
                                               u32 control)
 {
        struct sk_buff *skb;
        struct l2cap_hdr *lh;
-       int hlen;
-
-       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-               hlen = L2CAP_EXT_HDR_SIZE;
-       else
-               hlen = L2CAP_ENH_HDR_SIZE;
+       int hlen = __ertm_hdr_size(chan);
 
        if (chan->fcs == L2CAP_FCS_CRC16)
                hlen += L2CAP_FCS_SIZE;
@@ -937,8 +950,7 @@ static void l2cap_chan_ready(struct l2cap_chan *chan)
 
        chan->state = BT_CONNECTED;
 
-       if (chan->ops->ready)
-               chan->ops->ready(chan);
+       chan->ops->ready(chan);
 }
 
 static void l2cap_do_start(struct l2cap_chan *chan)
@@ -1001,6 +1013,11 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
                __clear_ack_timer(chan);
        }
 
+       if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+               __l2cap_state_change(chan, BT_DISCONN);
+               return;
+       }
+
        req.dcid = cpu_to_le16(chan->dcid);
        req.scid = cpu_to_le16(chan->scid);
        l2cap_send_cmd(conn, l2cap_get_ident(conn),
@@ -1195,6 +1212,11 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
 
                l2cap_chan_lock(chan);
 
+               if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+                       l2cap_chan_unlock(chan);
+                       continue;
+               }
+
                if (conn->hcon->type == LE_LINK) {
                        if (smp_conn_security(conn, chan->sec_level))
                                l2cap_chan_ready(chan);
@@ -1292,7 +1314,12 @@ static void security_timeout(struct work_struct *work)
        struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
                                                security_timer.work);
 
-       l2cap_conn_del(conn->hcon, ETIMEDOUT);
+       BT_DBG("conn %p", conn);
+
+       if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) {
+               smp_chan_destroy(conn);
+               l2cap_conn_del(conn->hcon, ETIMEDOUT);
+       }
 }
 
 static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
@@ -1999,10 +2026,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
        if (!conn)
                return ERR_PTR(-ENOTCONN);
 
-       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-               hlen = L2CAP_EXT_HDR_SIZE;
-       else
-               hlen = L2CAP_ENH_HDR_SIZE;
+       hlen = __ertm_hdr_size(chan);
 
        if (sdulen)
                hlen += L2CAP_SDULEN_SIZE;
@@ -2049,7 +2073,6 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
        struct sk_buff *skb;
        u16 sdu_len;
        size_t pdu_len;
-       int err = 0;
        u8 sar;
 
        BT_DBG("chan %p, msg %p, len %d", chan, msg, (int)len);
@@ -2068,10 +2091,7 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
        if (chan->fcs)
                pdu_len -= L2CAP_FCS_SIZE;
 
-       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-               pdu_len -= L2CAP_EXT_HDR_SIZE;
-       else
-               pdu_len -= L2CAP_ENH_HDR_SIZE;
+       pdu_len -= __ertm_hdr_size(chan);
 
        /* Remote device may have requested smaller PDUs */
        pdu_len = min_t(size_t, pdu_len, chan->remote_mps);
@@ -2111,7 +2131,7 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
                }
        }
 
-       return err;
+       return 0;
 }
 
 int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
@@ -2691,7 +2711,7 @@ static void l2cap_ack_timeout(struct work_struct *work)
        l2cap_chan_put(chan);
 }
 
-static inline int l2cap_ertm_init(struct l2cap_chan *chan)
+int l2cap_ertm_init(struct l2cap_chan *chan)
 {
        int err;
 
@@ -5109,16 +5129,27 @@ drop:
        return 0;
 }
 
-static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb)
+static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
+                              struct sk_buff *skb)
 {
        struct l2cap_chan *chan;
 
        chan = l2cap_get_chan_by_scid(conn, cid);
        if (!chan) {
-               BT_DBG("unknown cid 0x%4.4x", cid);
-               /* Drop packet and return */
-               kfree_skb(skb);
-               return 0;
+               if (cid == L2CAP_CID_A2MP) {
+                       chan = a2mp_channel_create(conn, skb);
+                       if (!chan) {
+                               kfree_skb(skb);
+                               return;
+                       }
+
+                       l2cap_chan_lock(chan);
+               } else {
+                       BT_DBG("unknown cid 0x%4.4x", cid);
+                       /* Drop packet and return */
+                       kfree_skb(skb);
+                       return;
+               }
        }
 
        BT_DBG("chan %p, len %d", chan, skb->len);
@@ -5155,11 +5186,10 @@ drop:
 
 done:
        l2cap_chan_unlock(chan);
-
-       return 0;
 }
 
-static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb)
+static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
+                                 struct sk_buff *skb)
 {
        struct l2cap_chan *chan;
 
@@ -5176,16 +5206,14 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str
                goto drop;
 
        if (!chan->ops->recv(chan, skb))
-               return 0;
+               return;
 
 drop:
        kfree_skb(skb);
-
-       return 0;
 }
 
-static inline int l2cap_att_channel(struct l2cap_conn *conn, u16 cid,
-                                   struct sk_buff *skb)
+static void l2cap_att_channel(struct l2cap_conn *conn, u16 cid,
+                             struct sk_buff *skb)
 {
        struct l2cap_chan *chan;
 
@@ -5202,12 +5230,10 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, u16 cid,
                goto drop;
 
        if (!chan->ops->recv(chan, skb))
-               return 0;
+               return;
 
 drop:
        kfree_skb(skb);
-
-       return 0;
 }
 
 static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
@@ -5235,7 +5261,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
 
        case L2CAP_CID_CONN_LESS:
                psm = get_unaligned((__le16 *) skb->data);
-               skb_pull(skb, 2);
+               skb_pull(skb, L2CAP_PSMLEN_SIZE);
                l2cap_conless_channel(conn, psm, skb);
                break;
 
This page took 0.032472 seconds and 5 git commands to generate.