]> git.openfabrics.org - ~emulex/infiniband.git/commitdiff
Bluetooth: Use non-flushable by default L2CAP data packets
authorAndrei Emeltchenko <andrei.emeltchenko@nokia.com>
Mon, 3 Jan 2011 09:14:36 +0000 (11:14 +0200)
committerGustavo F. Padovan <padovan@profusion.mobi>
Tue, 8 Feb 2011 03:40:04 +0000 (01:40 -0200)
Modification of Nick Pelly <npelly@google.com> patch.

With Bluetooth 2.1 ACL packets can be flushable or non-flushable. This commit
makes ACL data packets non-flushable by default on compatible chipsets, and
adds the BT_FLUSHABLE socket option to explicitly request flushable ACL
data packets for a given L2CAP socket. This is useful for A2DP data which can
be safely discarded if it can not be delivered within a short time (while
other ACL data should not be discarded).

Note that making ACL data flushable has no effect unless the automatic flush
timeout for that ACL link is changed from its default of 0 (infinite).

Default packet types (for compatible chipsets):
Frame 34: 13 bytes on wire (104 bits), 13 bytes captured (104 bits)
Bluetooth HCI H4
Bluetooth HCI ACL Packet
    .... 0000 0000 0010 = Connection Handle: 0x0002
    ..00 .... .... .... = PB Flag: First Non-automatically Flushable Packet (0)
    00.. .... .... .... = BC Flag: Point-To-Point (0)
    Data Total Length: 8
Bluetooth L2CAP Packet

After setting BT_FLUSHABLE
(sock.setsockopt(274 /*SOL_BLUETOOTH*/, 8 /* BT_FLUSHABLE */, 1 /* flush */))
Frame 34: 13 bytes on wire (104 bits), 13 bytes captured (104 bits)
Bluetooth HCI H4
Bluetooth HCI ACL Packet
    .... 0000 0000 0010 = Connection Handle: 0x0002
    ..10 .... .... .... = PB Flag: First Automatically Flushable Packet (2)
    00.. .... .... .... = BC Flag: Point-To-Point (0)
    Data Total Length: 8
Bluetooth L2CAP Packet

Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@nokia.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
net/bluetooth/hci_core.c
net/bluetooth/l2cap.c

index 0c5e72503b775282bda667771c54ca8c3a448e6c..ed7d775337e02a811eb91e208942a5d748c47bfe 100644 (file)
@@ -64,6 +64,11 @@ struct bt_security {
 
 #define BT_DEFER_SETUP 7
 
+#define BT_FLUSHABLE   8
+
+#define BT_FLUSHABLE_OFF       0
+#define BT_FLUSHABLE_ON                1
+
 #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg)
 #define BT_ERR(fmt, arg...)  printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg)
 #define BT_DBG(fmt, arg...)  pr_debug("%s: " fmt "\n" , __func__ , ## arg)
index 29a7a8ca0438996f51d153033bde8f60ea2ca3d6..5d033dc9d43bede0e9b36f033372f3af5a6881f7 100644 (file)
@@ -150,6 +150,7 @@ enum {
 #define EDR_ESCO_MASK  (ESCO_2EV3 | ESCO_3EV3 | ESCO_2EV5 | ESCO_3EV5)
 
 /* ACL flags */
+#define ACL_START_NO_FLUSH     0x00
 #define ACL_CONT               0x01
 #define ACL_START              0x02
 #define ACL_ACTIVE_BCAST       0x04
@@ -194,6 +195,7 @@ enum {
 #define LMP_EDR_3S_ESCO        0x80
 
 #define LMP_SIMPLE_PAIR        0x08
+#define LMP_NO_FLUSH   0x40
 
 /* Connection modes */
 #define HCI_CM_ACTIVE  0x0000
index d2cf88407690e432f80938985d7cbcf4fb16d0ed..4e14610baecec26d6c963d88390652c8ff55a38d 100644 (file)
@@ -458,6 +458,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
 #define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR)
 #define lmp_esco_capable(dev)      ((dev)->features[3] & LMP_ESCO)
 #define lmp_ssp_capable(dev)       ((dev)->features[6] & LMP_SIMPLE_PAIR)
+#define lmp_no_flush_capable(dev)  ((dev)->features[6] & LMP_NO_FLUSH)
 
 /* ----- HCI protocols ----- */
 struct hci_proto {
index 7ad25ca60ec042aeac8b2336c003fbc9e6fe07e5..7f88a87d7a46bf14d36cf8d86e7cd82af95c06e9 100644 (file)
@@ -327,6 +327,7 @@ struct l2cap_pinfo {
        __u8            sec_level;
        __u8            role_switch;
        __u8            force_reliable;
+       __u8            flushable;
 
        __u8            conf_req[64];
        __u8            conf_len;
index 9c4541bc488ab9b2a74dda2809929f842b9b97e6..9ba92adaa9adb736d5901881d782f5cf9d8c72df 100644 (file)
@@ -1395,7 +1395,7 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
 
        skb->dev = (void *) hdev;
        bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
-       hci_add_acl_hdr(skb, conn->handle, flags | ACL_START);
+       hci_add_acl_hdr(skb, conn->handle, flags);
 
        list = skb_shinfo(skb)->frag_list;
        if (!list) {
@@ -1413,12 +1413,15 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
                spin_lock_bh(&conn->data_q.lock);
 
                __skb_queue_tail(&conn->data_q, skb);
+
+               flags &= ~ACL_START;
+               flags |= ACL_CONT;
                do {
                        skb = list; list = list->next;
 
                        skb->dev = (void *) hdev;
                        bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
-                       hci_add_acl_hdr(skb, conn->handle, flags | ACL_CONT);
+                       hci_add_acl_hdr(skb, conn->handle, flags);
 
                        BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
 
index 675614e38e149c7d28d13903763b966b3c37e5f8..4bf98dfd24bcceca15f2730527710ef30a68e17d 100644 (file)
@@ -373,13 +373,19 @@ static inline u8 l2cap_get_ident(struct l2cap_conn *conn)
 static inline void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data)
 {
        struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
+       u8 flags;
 
        BT_DBG("code 0x%2.2x", code);
 
        if (!skb)
                return;
 
-       hci_send_acl(conn->hcon, skb, 0);
+       if (lmp_no_flush_capable(conn->hcon->hdev))
+               flags = ACL_START_NO_FLUSH;
+       else
+               flags = ACL_START;
+
+       hci_send_acl(conn->hcon, skb, flags);
 }
 
 static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control)
@@ -389,6 +395,7 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control)
        struct l2cap_conn *conn = pi->conn;
        struct sock *sk = (struct sock *)pi;
        int count, hlen = L2CAP_HDR_SIZE + 2;
+       u8 flags;
 
        if (sk->sk_state != BT_CONNECTED)
                return;
@@ -425,7 +432,12 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control)
                put_unaligned_le16(fcs, skb_put(skb, 2));
        }
 
-       hci_send_acl(pi->conn->hcon, skb, 0);
+       if (lmp_no_flush_capable(conn->hcon->hdev))
+               flags = ACL_START_NO_FLUSH;
+       else
+               flags = ACL_START;
+
+       hci_send_acl(pi->conn->hcon, skb, flags);
 }
 
 static inline void l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control)
@@ -912,6 +924,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
                pi->sec_level = l2cap_pi(parent)->sec_level;
                pi->role_switch = l2cap_pi(parent)->role_switch;
                pi->force_reliable = l2cap_pi(parent)->force_reliable;
+               pi->flushable = l2cap_pi(parent)->flushable;
        } else {
                pi->imtu = L2CAP_DEFAULT_MTU;
                pi->omtu = 0;
@@ -927,6 +940,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
                pi->sec_level = BT_SECURITY_LOW;
                pi->role_switch = 0;
                pi->force_reliable = 0;
+               pi->flushable = BT_FLUSHABLE_OFF;
        }
 
        /* Default config options */
@@ -1431,10 +1445,17 @@ static void l2cap_drop_acked_frames(struct sock *sk)
 static inline void l2cap_do_send(struct sock *sk, struct sk_buff *skb)
 {
        struct l2cap_pinfo *pi = l2cap_pi(sk);
+       struct hci_conn *hcon = pi->conn->hcon;
+       u16 flags;
 
        BT_DBG("sk %p, skb %p len %d", sk, skb, skb->len);
 
-       hci_send_acl(pi->conn->hcon, skb, 0);
+       if (!pi->flushable && lmp_no_flush_capable(hcon->hdev))
+               flags = ACL_START_NO_FLUSH;
+       else
+               flags = ACL_START;
+
+       hci_send_acl(hcon, skb, flags);
 }
 
 static void l2cap_streaming_send(struct sock *sk)
@@ -2079,6 +2100,30 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
                bt_sk(sk)->defer_setup = opt;
                break;
 
+       case BT_FLUSHABLE:
+               if (get_user(opt, (u32 __user *) optval)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               if (opt > BT_FLUSHABLE_ON) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (opt == BT_FLUSHABLE_OFF) {
+                       struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+                       /* proceed futher only when we have l2cap_conn and
+                          No Flush support in the LM */
+                       if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) {
+                               err = -EINVAL;
+                               break;
+                       }
+               }
+
+               l2cap_pi(sk)->flushable = opt;
+               break;
+
        default:
                err = -ENOPROTOOPT;
                break;
@@ -2218,6 +2263,12 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
 
                break;
 
+       case BT_FLUSHABLE:
+               if (put_user(l2cap_pi(sk)->flushable, (u32 __user *) optval))
+                       err = -EFAULT;
+
+               break;
+
        default:
                err = -ENOPROTOOPT;
                break;
@@ -4678,7 +4729,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
 
        BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
 
-       if (flags & ACL_START) {
+       if (!(flags & ACL_CONT)) {
                struct l2cap_hdr *hdr;
                struct sock *sk;
                u16 cid;