diff options
author | Jaikumar Ganesh <jaikumar@google.com> | 2011-05-23 21:06:04 -0400 |
---|---|---|
committer | Gustavo F. Padovan <padovan@profusion.mobi> | 2011-06-08 15:58:19 -0400 |
commit | 14b12d0b98f87162b7e9e93dde66d1af97886567 (patch) | |
tree | 3ee2430863ad890b9b36ee4172fadc5147a01ad0 | |
parent | 96d97a673d42408c0f960cc54d44be7629343bce (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>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
-rw-r--r-- | include/net/bluetooth/bluetooth.h | 8 | ||||
-rw-r--r-- | include/net/bluetooth/hci_core.h | 2 | ||||
-rw-r--r-- | include/net/bluetooth/l2cap.h | 1 | ||||
-rw-r--r-- | net/bluetooth/hci_conn.c | 9 | ||||
-rw-r--r-- | net/bluetooth/hci_core.c | 4 | ||||
-rw-r--r-- | net/bluetooth/l2cap_core.c | 5 | ||||
-rw-r--r-- | net/bluetooth/l2cap_sock.c | 36 |
7 files changed, 59 insertions, 6 deletions
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 43750439c521..af930a3a66be 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h | |||
@@ -69,6 +69,13 @@ struct bt_security { | |||
69 | #define BT_FLUSHABLE_OFF 0 | 69 | #define BT_FLUSHABLE_OFF 0 |
70 | #define BT_FLUSHABLE_ON 1 | 70 | #define BT_FLUSHABLE_ON 1 |
71 | 71 | ||
72 | #define BT_POWER 9 | ||
73 | struct bt_power { | ||
74 | __u8 force_active; | ||
75 | }; | ||
76 | #define BT_POWER_FORCE_ACTIVE_OFF 0 | ||
77 | #define BT_POWER_FORCE_ACTIVE_ON 1 | ||
78 | |||
72 | #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg) | 79 | #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg) |
73 | #define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg) | 80 | #define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg) |
74 | #define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg) | 81 | #define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg) |
@@ -150,6 +157,7 @@ struct bt_skb_cb { | |||
150 | __u8 retries; | 157 | __u8 retries; |
151 | __u8 sar; | 158 | __u8 sar; |
152 | unsigned short channel; | 159 | unsigned short channel; |
160 | __u8 force_active; | ||
153 | }; | 161 | }; |
154 | #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb)) | 162 | #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb)) |
155 | 163 | ||
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5be150229574..818eadbc3f7f 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h | |||
@@ -438,7 +438,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type); | |||
438 | int hci_conn_change_link_key(struct hci_conn *conn); | 438 | int hci_conn_change_link_key(struct hci_conn *conn); |
439 | int hci_conn_switch_role(struct hci_conn *conn, __u8 role); | 439 | int hci_conn_switch_role(struct hci_conn *conn, __u8 role); |
440 | 440 | ||
441 | void hci_conn_enter_active_mode(struct hci_conn *conn); | 441 | void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active); |
442 | void hci_conn_enter_sniff_mode(struct hci_conn *conn); | 442 | void hci_conn_enter_sniff_mode(struct hci_conn *conn); |
443 | 443 | ||
444 | void hci_conn_hold_device(struct hci_conn *conn); | 444 | void hci_conn_hold_device(struct hci_conn *conn); |
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index a2dcbfff1c51..0529d278e068 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h | |||
@@ -303,6 +303,7 @@ struct l2cap_chan { | |||
303 | __u8 role_switch; | 303 | __u8 role_switch; |
304 | __u8 force_reliable; | 304 | __u8 force_reliable; |
305 | __u8 flushable; | 305 | __u8 flushable; |
306 | __u8 force_active; | ||
306 | 307 | ||
307 | __u8 ident; | 308 | __u8 ident; |
308 | 309 | ||
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ce67d0ff486f..0408a93570d6 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c | |||
@@ -507,7 +507,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 | |||
507 | if (acl->state == BT_CONNECTED && | 507 | if (acl->state == BT_CONNECTED && |
508 | (sco->state == BT_OPEN || sco->state == BT_CLOSED)) { | 508 | (sco->state == BT_OPEN || sco->state == BT_CLOSED)) { |
509 | acl->power_save = 1; | 509 | acl->power_save = 1; |
510 | hci_conn_enter_active_mode(acl); | 510 | hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON); |
511 | 511 | ||
512 | if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->pend)) { | 512 | if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->pend)) { |
513 | /* defer SCO setup until mode change completed */ | 513 | /* defer SCO setup until mode change completed */ |
@@ -688,7 +688,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role) | |||
688 | EXPORT_SYMBOL(hci_conn_switch_role); | 688 | EXPORT_SYMBOL(hci_conn_switch_role); |
689 | 689 | ||
690 | /* Enter active mode */ | 690 | /* Enter active mode */ |
691 | void hci_conn_enter_active_mode(struct hci_conn *conn) | 691 | void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) |
692 | { | 692 | { |
693 | struct hci_dev *hdev = conn->hdev; | 693 | struct hci_dev *hdev = conn->hdev; |
694 | 694 | ||
@@ -697,7 +697,10 @@ void hci_conn_enter_active_mode(struct hci_conn *conn) | |||
697 | if (test_bit(HCI_RAW, &hdev->flags)) | 697 | if (test_bit(HCI_RAW, &hdev->flags)) |
698 | return; | 698 | return; |
699 | 699 | ||
700 | if (conn->mode != HCI_CM_SNIFF || !conn->power_save) | 700 | if (conn->mode != HCI_CM_SNIFF) |
701 | goto timer; | ||
702 | |||
703 | if (!conn->power_save && !force_active) | ||
701 | goto timer; | 704 | goto timer; |
702 | 705 | ||
703 | if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { | 706 | 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 ff6b784c58c5..e14e8a1cb04e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c | |||
@@ -1969,7 +1969,7 @@ static inline void hci_sched_acl(struct hci_dev *hdev) | |||
1969 | while (quote-- && (skb = skb_dequeue(&conn->data_q))) { | 1969 | while (quote-- && (skb = skb_dequeue(&conn->data_q))) { |
1970 | BT_DBG("skb %p len %d", skb, skb->len); | 1970 | BT_DBG("skb %p len %d", skb, skb->len); |
1971 | 1971 | ||
1972 | hci_conn_enter_active_mode(conn); | 1972 | hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active); |
1973 | 1973 | ||
1974 | hci_send_frame(skb); | 1974 | hci_send_frame(skb); |
1975 | hdev->acl_last_tx = jiffies; | 1975 | hdev->acl_last_tx = jiffies; |
@@ -2108,7 +2108,7 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) | |||
2108 | if (conn) { | 2108 | if (conn) { |
2109 | register struct hci_proto *hp; | 2109 | register struct hci_proto *hp; |
2110 | 2110 | ||
2111 | hci_conn_enter_active_mode(conn); | 2111 | hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active); |
2112 | 2112 | ||
2113 | /* Send to upper protocol */ | 2113 | /* Send to upper protocol */ |
2114 | hp = hci_proto[HCI_PROTO_L2CAP]; | 2114 | hp = hci_proto[HCI_PROTO_L2CAP]; |
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bb6be6377891..6908a835a7a3 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c | |||
@@ -537,6 +537,8 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, | |||
537 | else | 537 | else |
538 | flags = ACL_START; | 538 | flags = ACL_START; |
539 | 539 | ||
540 | bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON; | ||
541 | |||
540 | hci_send_acl(conn->hcon, skb, flags); | 542 | hci_send_acl(conn->hcon, skb, flags); |
541 | } | 543 | } |
542 | 544 | ||
@@ -590,6 +592,8 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) | |||
590 | else | 592 | else |
591 | flags = ACL_START; | 593 | flags = ACL_START; |
592 | 594 | ||
595 | bt_cb(skb)->force_active = chan->force_active; | ||
596 | |||
593 | hci_send_acl(chan->conn->hcon, skb, flags); | 597 | hci_send_acl(chan->conn->hcon, skb, flags); |
594 | } | 598 | } |
595 | 599 | ||
@@ -1215,6 +1219,7 @@ void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) | |||
1215 | else | 1219 | else |
1216 | flags = ACL_START; | 1220 | flags = ACL_START; |
1217 | 1221 | ||
1222 | bt_cb(skb)->force_active = chan->force_active; | ||
1218 | hci_send_acl(hcon, skb, flags); | 1223 | hci_send_acl(hcon, skb, flags); |
1219 | } | 1224 | } |
1220 | 1225 | ||
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index b79fb7561836..bec3e043b254 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c | |||
@@ -390,6 +390,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch | |||
390 | struct sock *sk = sock->sk; | 390 | struct sock *sk = sock->sk; |
391 | struct l2cap_chan *chan = l2cap_pi(sk)->chan; | 391 | struct l2cap_chan *chan = l2cap_pi(sk)->chan; |
392 | struct bt_security sec; | 392 | struct bt_security sec; |
393 | struct bt_power pwr; | ||
393 | int len, err = 0; | 394 | int len, err = 0; |
394 | 395 | ||
395 | BT_DBG("sk %p", sk); | 396 | BT_DBG("sk %p", sk); |
@@ -438,6 +439,21 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch | |||
438 | 439 | ||
439 | break; | 440 | break; |
440 | 441 | ||
442 | case BT_POWER: | ||
443 | if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM | ||
444 | && sk->sk_type != SOCK_RAW) { | ||
445 | err = -EINVAL; | ||
446 | break; | ||
447 | } | ||
448 | |||
449 | pwr.force_active = chan->force_active; | ||
450 | |||
451 | len = min_t(unsigned int, len, sizeof(pwr)); | ||
452 | if (copy_to_user(optval, (char *) &pwr, len)) | ||
453 | err = -EFAULT; | ||
454 | |||
455 | break; | ||
456 | |||
441 | default: | 457 | default: |
442 | err = -ENOPROTOOPT; | 458 | err = -ENOPROTOOPT; |
443 | break; | 459 | break; |
@@ -538,6 +554,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch | |||
538 | struct sock *sk = sock->sk; | 554 | struct sock *sk = sock->sk; |
539 | struct l2cap_chan *chan = l2cap_pi(sk)->chan; | 555 | struct l2cap_chan *chan = l2cap_pi(sk)->chan; |
540 | struct bt_security sec; | 556 | struct bt_security sec; |
557 | struct bt_power pwr; | ||
541 | int len, err = 0; | 558 | int len, err = 0; |
542 | u32 opt; | 559 | u32 opt; |
543 | 560 | ||
@@ -614,6 +631,23 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch | |||
614 | chan->flushable = opt; | 631 | chan->flushable = opt; |
615 | break; | 632 | break; |
616 | 633 | ||
634 | case BT_POWER: | ||
635 | if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && | ||
636 | chan->chan_type != L2CAP_CHAN_RAW) { | ||
637 | err = -EINVAL; | ||
638 | break; | ||
639 | } | ||
640 | |||
641 | pwr.force_active = BT_POWER_FORCE_ACTIVE_ON; | ||
642 | |||
643 | len = min_t(unsigned int, sizeof(pwr), optlen); | ||
644 | if (copy_from_user((char *) &pwr, optval, len)) { | ||
645 | err = -EFAULT; | ||
646 | break; | ||
647 | } | ||
648 | chan->force_active = pwr.force_active; | ||
649 | break; | ||
650 | |||
617 | default: | 651 | default: |
618 | err = -ENOPROTOOPT; | 652 | err = -ENOPROTOOPT; |
619 | break; | 653 | break; |
@@ -771,6 +805,7 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent) | |||
771 | chan->role_switch = pchan->role_switch; | 805 | chan->role_switch = pchan->role_switch; |
772 | chan->force_reliable = pchan->force_reliable; | 806 | chan->force_reliable = pchan->force_reliable; |
773 | chan->flushable = pchan->flushable; | 807 | chan->flushable = pchan->flushable; |
808 | chan->force_active = pchan->force_active; | ||
774 | } else { | 809 | } else { |
775 | 810 | ||
776 | switch (sk->sk_type) { | 811 | switch (sk->sk_type) { |
@@ -801,6 +836,7 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent) | |||
801 | chan->role_switch = 0; | 836 | chan->role_switch = 0; |
802 | chan->force_reliable = 0; | 837 | chan->force_reliable = 0; |
803 | chan->flushable = BT_FLUSHABLE_OFF; | 838 | chan->flushable = BT_FLUSHABLE_OFF; |
839 | chan->force_active = BT_POWER_FORCE_ACTIVE_ON; | ||
804 | } | 840 | } |
805 | 841 | ||
806 | /* Default config options */ | 842 | /* Default config options */ |