Skip to content

Commit

Permalink
Bluetooth: Use non-flushable by default L2CAP data packets
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Andrei Emeltchenko authored and Gustavo F. Padovan committed Feb 8, 2011
1 parent b2c60d4 commit e702112
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 6 deletions.
5 changes: 5 additions & 0 deletions include/net/bluetooth/bluetooth.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions include/net/bluetooth/hci.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/net/bluetooth/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions include/net/bluetooth/l2cap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 5 additions & 2 deletions net/bluetooth/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);

Expand Down
59 changes: 55 additions & 4 deletions net/bluetooth/l2cap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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 */
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit e702112

Please sign in to comment.