summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorNick Pelly <npelly@google.com>2009-12-08 19:42:21 -0800
committerNick Pelly <npelly@google.com>2010-03-09 12:09:56 -0800
commita47633bdbcc9b87f2a72e9bdf317f05e2e6c8051 (patch)
tree92267d8ff16c999977ffb60b2db511599bae4c79 /net
parent71682ea19f8d5d96034ce113acbbf83ac58173e6 (diff)
Bluetooth: Use non-flushable pb flag by default for ACL data on capable chipsets.
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 L2CAP_LM_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). Change-Id: Ie3d4befdeaefb8c979de7ae603ff5ec462b3483c Signed-off-by: Nick Pelly <npelly@google.com>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/hci_core.c6
-rw-r--r--net/bluetooth/l2cap.c25
2 files changed, 26 insertions, 5 deletions
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index e1da8f68759c..84a9d75b0c61 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1239,7 +1239,7 @@ int 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);
if (!(list = skb_shinfo(skb)->frag_list)) {
/* Non fragmented */
@@ -1256,12 +1256,14 @@ int 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_PB_MASK;
+ 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);
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 6e3b59690d0e..4529e99b2940 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -325,13 +325,19 @@ static inline u8 l2cap_get_ident(struct l2cap_conn *conn)
static inline int 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 -ENOMEM;
- 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;
+
+ return hci_send_acl(conn->hcon, skb, flags);
}
static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control)
@@ -770,6 +776,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;
@@ -778,6 +785,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 = 0;
}
/* Default config options */
@@ -1258,11 +1266,18 @@ static void l2cap_drop_acked_frames(struct sock *sk)
static inline int l2cap_do_send(struct sock *sk, struct sk_buff *skb)
{
struct l2cap_pinfo *pi = l2cap_pi(sk);
+ struct hci_conn *hcon = pi->conn->hcon;
int err;
+ u16 flags;
BT_DBG("sk %p, skb %p len %d", sk, skb, skb->len);
- err = hci_send_acl(pi->conn->hcon, skb, 0);
+ if (lmp_no_flush_capable(hcon->hdev) && !l2cap_pi(sk)->flushable)
+ flags = ACL_START_NO_FLUSH;
+ else
+ flags = ACL_START;
+
+ err = hci_send_acl(hcon, skb, flags);
if (err < 0)
kfree_skb(skb);
@@ -1747,6 +1762,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER);
l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE);
+ l2cap_pi(sk)->flushable = (opt & L2CAP_LM_FLUSHABLE);
break;
default:
@@ -1874,6 +1890,9 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
if (l2cap_pi(sk)->force_reliable)
opt |= L2CAP_LM_RELIABLE;
+ if (l2cap_pi(sk)->flushable)
+ opt |= L2CAP_LM_FLUSHABLE;
+
if (put_user(opt, (u32 __user *) optval))
err = -EFAULT;
break;
@@ -3801,7 +3820,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;
int len;