Bluetooth: Add BT_POWER L2CAP socket option.
authorJaikumar Ganesh <jaikumar@google.com>
Wed, 10 Nov 2010 03:07:45 +0000 (19:07 -0800)
committerJaikumar Ganesh <jaikumar@google.com>
Wed, 10 Nov 2010 23:59:50 +0000 (15:59 -0800)
Add BT_POWER socket option used to control the power
characteristics of the underlying ACL link. When the remote end
has put the link in sniff mode and the host stack wants to send
data we need need to explicitly exit sniff mode to work well with
certain devices (For example, A2DP on Plantronics Voyager 855).
However, this causes problems with HID devices.

Hence, moving into active mode when sending data, irrespective
of who set the sniff mode has been made as a socket option. By
default, we will move into active mode. HID devices can set the
L2CAP socket option to prevent this from happening.

Currently, this has been implemented for L2CAP sockets. This has been
tested with incoming and outgoing L2CAP sockets for HID and A2DP.

Based on discussions on linux-bluetooth and patches submitted by
Andrei Emeltchenko.

Signed-off-by: Jaikumar Ganesh <jaikumar@google.com>
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/l2cap.c

index 30fce0128dd72fa0281795bc0ae03809b85bc9d0..642cda89b1e76e1ddd1b28e4ab037907b3f4618d 100644 (file)
@@ -64,6 +64,11 @@ struct bt_security {
 
 #define BT_DEFER_SETUP 7
 
+#define BT_POWER       8
+struct bt_power {
+       __u8 force_active;
+};
+
 #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)
@@ -142,6 +147,7 @@ struct bt_skb_cb {
        __u8 tx_seq;
        __u8 retries;
        __u8 sar;
+       __u8 force_active;
 };
 #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
 
index 1ce134720cfeb197e8a2837563dca8bd362a35da..95b5f024fb3537791208c0ab8e07b8545a1f900b 100644 (file)
@@ -353,7 +353,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type);
 int hci_conn_change_link_key(struct hci_conn *conn);
 int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
 
-void hci_conn_enter_active_mode(struct hci_conn *conn);
+void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active);
 void hci_conn_enter_sniff_mode(struct hci_conn *conn);
 
 void hci_conn_hold_device(struct hci_conn *conn);
index 8f8e648032e8a0a487da5c71dcaa9c33d174e924..22885e05a826b0e929d45016d14418976a1db47c 100644 (file)
@@ -329,6 +329,7 @@ struct l2cap_pinfo {
        __u8            role_switch;
        __u8            force_reliable;
        __u8            flushable;
+       __u8            force_active;
 
        __u8            conf_req[64];
        __u8            conf_len;
index aabe0c3d48296dad605b48c15664bb8408aa320c..481e4b1cd981546c62b776892cabf0db2a71451b 100644 (file)
@@ -415,7 +415,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type,
        if (acl->state == BT_CONNECTED &&
                        (sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
                acl->power_save = 1;
-               hci_conn_enter_active_mode(acl);
+               hci_conn_enter_active_mode(acl, 1);
 
                if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->pend)) {
                        /* defer SCO setup until mode change completed */
@@ -531,7 +531,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
 EXPORT_SYMBOL(hci_conn_switch_role);
 
 /* Enter active mode */
-void hci_conn_enter_active_mode(struct hci_conn *conn)
+void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
 {
        struct hci_dev *hdev = conn->hdev;
 
@@ -540,7 +540,10 @@ void hci_conn_enter_active_mode(struct hci_conn *conn)
        if (test_bit(HCI_RAW, &hdev->flags))
                return;
 
-       if (conn->mode != HCI_CM_SNIFF || !conn->power_save)
+       if (conn->mode != HCI_CM_SNIFF)
+               goto timer;
+
+       if (!conn->power_save && !force_active)
                goto timer;
 
        if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
index a49aa2bf97f3e184c6acbc7d799c4dcdc1a049f9..de9127d6b9d3b99c28f490dcb05c1510eb18eabd 100644 (file)
@@ -1507,7 +1507,7 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
                while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
                        BT_DBG("skb %p len %d", skb, skb->len);
 
-                       hci_conn_enter_active_mode(conn);
+                       hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active);
 
                        hci_send_frame(skb);
                        hdev->acl_last_tx = jiffies;
@@ -1609,7 +1609,7 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
        if (conn) {
                register struct hci_proto *hp;
 
-               hci_conn_enter_active_mode(conn);
+               hci_conn_enter_active_mode(conn, 1);
 
                /* Send to upper protocol */
                if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) {
index d527b10f8a059cc00ea5bf47ade01030df7c8496..0f2e1c66ac33a73dbe88460e57c229404e991062 100644 (file)
@@ -366,6 +366,8 @@ static inline void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u1
        else
                flags = ACL_START;
 
+       bt_cb(skb)->force_active = 1;
+
        hci_send_acl(conn->hcon, skb, flags);
 }
 
@@ -412,6 +414,8 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control)
                put_unaligned_le16(fcs, skb_put(skb, 2));
        }
 
+       bt_cb(skb)->force_active = l2cap_pi(sk)->force_active;
+
        hci_send_acl(pi->conn->hcon, skb, 0);
 }
 
@@ -908,6 +912,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
                pi->role_switch = l2cap_pi(parent)->role_switch;
                pi->force_reliable = l2cap_pi(parent)->force_reliable;
                pi->flushable = l2cap_pi(parent)->flushable;
+               pi->force_active = l2cap_pi(parent)->force_active;
        } else {
                pi->imtu = L2CAP_DEFAULT_MTU;
                pi->omtu = 0;
@@ -924,6 +929,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
                pi->role_switch = 0;
                pi->force_reliable = 0;
                pi->flushable = 0;
+               pi->force_active = 1;
        }
 
        /* Default config options */
@@ -1452,6 +1458,7 @@ static inline void l2cap_do_send(struct sock *sk, struct sk_buff *skb)
        else
                flags = ACL_START;
 
+       bt_cb(skb)->force_active = pi->force_active;
        hci_send_acl(hcon, skb, flags);
 }
 
@@ -2043,6 +2050,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
 {
        struct sock *sk = sock->sk;
        struct bt_security sec;
+       struct bt_power pwr;
        int len, err = 0;
        u32 opt;
 
@@ -2095,6 +2103,23 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
                bt_sk(sk)->defer_setup = opt;
                break;
 
+       case BT_POWER:
+               if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM
+                               && sk->sk_type != SOCK_RAW) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               pwr.force_active = 1;
+
+               len = min_t(unsigned int, sizeof(pwr), optlen);
+               if (copy_from_user((char *) &pwr, optval, len)) {
+                       err = -EFAULT;
+                       break;
+               }
+               l2cap_pi(sk)->force_active = pwr.force_active;
+               break;
+
        default:
                err = -ENOPROTOOPT;
                break;
@@ -2195,6 +2220,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
 {
        struct sock *sk = sock->sk;
        struct bt_security sec;
+       struct bt_power pwr;
        int len, err = 0;
 
        BT_DBG("sk %p", sk);
@@ -2237,6 +2263,20 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
 
                break;
 
+       case BT_POWER:
+               if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM
+                               && sk->sk_type != SOCK_RAW) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               pwr.force_active = l2cap_pi(sk)->force_active;
+
+               len = min_t(unsigned int, len, sizeof(pwr));
+               if (copy_to_user(optval, (char *) &pwr, len))
+                       err = -EFAULT;
+
+               break;
        default:
                err = -ENOPROTOOPT;
                break;