Bluetooth: Add LE L2CAP flow control mode
authorJohan Hedberg <johan.hedberg@intel.com>
Fri, 17 May 2013 09:49:23 +0000 (12:49 +0300)
committerMarcel Holtmann <marcel@holtmann.org>
Thu, 5 Dec 2013 15:05:34 +0000 (07:05 -0800)
The LE connection oriented channels have their own mode with its own
data transfer rules. In order to implement this properly we need to
distinguish L2CAP channels operating in this mode from other modes.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/l2cap.h
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c

index 84b25202596e9270664cd75c2995a064951eae40..063d786b099143cdf67e97359a2702017442263c 100644 (file)
@@ -328,6 +328,12 @@ struct l2cap_conf_rfc {
 #define L2CAP_MODE_ERTM                0x03
 #define L2CAP_MODE_STREAMING   0x04
 
+/* Unlike the above this one doesn't actually map to anything that would
+ * ever be sent over the air. Therefore, use a value that's unlikely to
+ * ever be used in the BR/EDR configuration phase.
+ */
+#define L2CAP_MODE_LE_FLOWCTL  0x80
+
 struct l2cap_conf_efs {
        __u8    id;
        __u8    stype;
@@ -861,6 +867,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
 void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
 int l2cap_chan_check_security(struct l2cap_chan *chan);
 void l2cap_chan_set_defaults(struct l2cap_chan *chan);
+void l2cap_le_flowctl_init(struct l2cap_chan *chan);
 int l2cap_ertm_init(struct l2cap_chan *chan);
 void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
 void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
index 907fc17453216f3fab3bdb212f3e04975ca8b81d..e260753184b65c09ffa697ea3cb347137287c2b0 100644 (file)
@@ -490,6 +490,13 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
        set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
 }
 
+void l2cap_le_flowctl_init(struct l2cap_chan *chan)
+{
+       chan->imtu = L2CAP_DEFAULT_MTU;
+       chan->omtu = L2CAP_LE_MIN_MTU;
+       chan->mode = L2CAP_MODE_LE_FLOWCTL;
+}
+
 void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
 {
        BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
@@ -597,6 +604,9 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
        case L2CAP_MODE_BASIC:
                break;
 
+       case L2CAP_MODE_LE_FLOWCTL:
+               break;
+
        case L2CAP_MODE_ERTM:
                __clear_retrans_timer(chan);
                __clear_monitor_timer(chan);
@@ -1849,6 +1859,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
 
        switch (chan->mode) {
        case L2CAP_MODE_BASIC:
+       case L2CAP_MODE_LE_FLOWCTL:
                break;
        case L2CAP_MODE_ERTM:
        case L2CAP_MODE_STREAMING:
@@ -2530,6 +2541,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
 
        switch (chan->mode) {
        case L2CAP_MODE_BASIC:
+       case L2CAP_MODE_LE_FLOWCTL:
                /* Check outgoing MTU */
                if (len > chan->omtu)
                        return -EMSGSIZE;
@@ -6621,6 +6633,7 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
                goto drop;
 
        switch (chan->mode) {
+       case L2CAP_MODE_LE_FLOWCTL:
        case L2CAP_MODE_BASIC:
                /* If socket recv buffers overflows we drop data here
                 * which is *bad* because L2CAP has to be reliable.
index 5a1d0cb0b8d50f294fed06b8d8d60363e7c7f90c..485ca349fed51bab4bedcf9b04d11783c46d48be 100644 (file)
@@ -130,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;
 
@@ -200,6 +203,9 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
                        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)
@@ -237,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:
@@ -588,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;
@@ -862,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;
        }