]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - net/bluetooth/l2cap_sock.c
Bluetooth: Add LE L2CAP flow control mode
[mirror_ubuntu-artful-kernel.git] / net / bluetooth / l2cap_sock.c
index 7cc24d263caaab45af9d8bbe5be09ebda20e87a4..485ca349fed51bab4bedcf9b04d11783c46d48be 100644 (file)
@@ -27,6 +27,7 @@
 
 /* Bluetooth L2CAP sockets. */
 
+#include <linux/module.h>
 #include <linux/export.h>
 
 #include <net/bluetooth/bluetooth.h>
@@ -35,6 +36,8 @@
 
 #include "smp.h"
 
+bool enable_lecoc;
+
 static struct bt_sock_list l2cap_sk_list = {
        .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock)
 };
@@ -73,11 +76,11 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
                return -EINVAL;
 
        if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
-               /* Connection oriented channels are not supported on LE */
-               if (la.l2_psm)
+               if (!enable_lecoc && la.l2_psm)
                        return -EINVAL;
                /* We only allow ATT user space socket */
-               if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+               if (la.l2_cid &&
+                   la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
                        return -EINVAL;
        }
 
@@ -127,6 +130,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
        bacpy(&chan->src, &la.l2_bdaddr);
        chan->src_type = la.l2_bdaddr_type;
 
+       if (chan->psm && bdaddr_type_is_le(chan->src_type))
+               l2cap_le_flowctl_init(chan);
+
        chan->state = BT_BOUND;
        sk->sk_state = BT_BOUND;
 
@@ -189,14 +195,17 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
                return -EINVAL;
 
        if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
-               /* Connection oriented channels are not supported on LE */
-               if (la.l2_psm)
+               if (!enable_lecoc && la.l2_psm)
                        return -EINVAL;
                /* We only allow ATT user space socket */
-               if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+               if (la.l2_cid &&
+                   la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
                        return -EINVAL;
        }
 
+       if (chan->psm && bdaddr_type_is_le(chan->src_type))
+               l2cap_le_flowctl_init(chan);
+
        err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
                                 &la.l2_bdaddr, la.l2_bdaddr_type);
        if (err)
@@ -234,6 +243,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
 
        switch (chan->mode) {
        case L2CAP_MODE_BASIC:
+       case L2CAP_MODE_LE_FLOWCTL:
                break;
        case L2CAP_MODE_ERTM:
        case L2CAP_MODE_STREAMING:
@@ -585,6 +595,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
 
                chan->mode = opts.mode;
                switch (chan->mode) {
+               case L2CAP_MODE_LE_FLOWCTL:
+                       break;
                case L2CAP_MODE_BASIC:
                        clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
                        break;
@@ -859,10 +871,16 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 
        if (sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP,
                                                    &bt_sk(sk)->flags)) {
-               sk->sk_state = BT_CONFIG;
-               pi->chan->state = BT_CONFIG;
+               if (bdaddr_type_is_le(pi->chan->src_type)) {
+                       sk->sk_state = BT_CONNECTED;
+                       pi->chan->state = BT_CONNECTED;
+                       __l2cap_le_connect_rsp_defer(pi->chan);
+               } else {
+                       sk->sk_state = BT_CONFIG;
+                       pi->chan->state = BT_CONFIG;
+                       __l2cap_connect_rsp_defer(pi->chan);
+               }
 
-               __l2cap_connect_rsp_defer(pi->chan);
                err = 0;
                goto done;
        }
@@ -1469,3 +1487,6 @@ void l2cap_cleanup_sockets(void)
        bt_sock_unregister(BTPROTO_L2CAP);
        proto_unregister(&l2cap_proto);
 }
+
+module_param(enable_lecoc, bool, 0644);
+MODULE_PARM_DESC(enable_lecoc, "Enable support for LE CoC");