summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorJaikumar Ganesh <jaikumar@google.com>2010-11-09 19:07:45 -0800
committerJaikumar Ganesh <jaikumar@google.com>2010-11-10 15:59:50 -0800
commit901204dc1797ec61c066ce9afecc56d6e874c187 (patch)
treeb14fbd9642c6a64b4a33594f437492122e608ac9 /net
parentdfa196f0e99696d22a4329ea3494175740904a26 (diff)
Bluetooth: Add BT_POWER L2CAP socket option.
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>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/hci_conn.c9
-rw-r--r--net/bluetooth/hci_core.c4
-rw-r--r--net/bluetooth/l2cap.c40
3 files changed, 48 insertions, 5 deletions
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index aabe0c3d4829..481e4b1cd981 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -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)) {
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index a49aa2bf97f3..de9127d6b9d3 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -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) {
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index d527b10f8a05..0f2e1c66ac33 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -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;