Commit 14b12d0b98f87162b7e9e93dde66d1af97886567
Committed by
Gustavo F. Padovan
1 parent
96d97a673d
Exists in
master
and in
6 other branches
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>
Showing 7 changed files with 59 additions and 6 deletions Side-by-side Diff
include/net/bluetooth/bluetooth.h
... | ... | @@ -69,6 +69,13 @@ |
69 | 69 | #define BT_FLUSHABLE_OFF 0 |
70 | 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 | 79 | #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg) |
73 | 80 | #define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg) |
74 | 81 | #define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg) |
... | ... | @@ -150,6 +157,7 @@ |
150 | 157 | __u8 retries; |
151 | 158 | __u8 sar; |
152 | 159 | unsigned short channel; |
160 | + __u8 force_active; | |
153 | 161 | }; |
154 | 162 | #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb)) |
155 | 163 |
include/net/bluetooth/hci_core.h
... | ... | @@ -438,7 +438,7 @@ |
438 | 438 | int hci_conn_change_link_key(struct hci_conn *conn); |
439 | 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 | 442 | void hci_conn_enter_sniff_mode(struct hci_conn *conn); |
443 | 443 | |
444 | 444 | void hci_conn_hold_device(struct hci_conn *conn); |
include/net/bluetooth/l2cap.h
net/bluetooth/hci_conn.c
... | ... | @@ -507,7 +507,7 @@ |
507 | 507 | if (acl->state == BT_CONNECTED && |
508 | 508 | (sco->state == BT_OPEN || sco->state == BT_CLOSED)) { |
509 | 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 | 512 | if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->pend)) { |
513 | 513 | /* defer SCO setup until mode change completed */ |
... | ... | @@ -688,7 +688,7 @@ |
688 | 688 | EXPORT_SYMBOL(hci_conn_switch_role); |
689 | 689 | |
690 | 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 | 693 | struct hci_dev *hdev = conn->hdev; |
694 | 694 | |
... | ... | @@ -697,7 +697,10 @@ |
697 | 697 | if (test_bit(HCI_RAW, &hdev->flags)) |
698 | 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 | 704 | goto timer; |
702 | 705 | |
703 | 706 | if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { |
net/bluetooth/hci_core.c
... | ... | @@ -1969,7 +1969,7 @@ |
1969 | 1969 | while (quote-- && (skb = skb_dequeue(&conn->data_q))) { |
1970 | 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 | 1974 | hci_send_frame(skb); |
1975 | 1975 | hdev->acl_last_tx = jiffies; |
... | ... | @@ -2108,7 +2108,7 @@ |
2108 | 2108 | if (conn) { |
2109 | 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 | 2113 | /* Send to upper protocol */ |
2114 | 2114 | hp = hci_proto[HCI_PROTO_L2CAP]; |
net/bluetooth/l2cap_core.c
... | ... | @@ -537,6 +537,8 @@ |
537 | 537 | else |
538 | 538 | flags = ACL_START; |
539 | 539 | |
540 | + bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON; | |
541 | + | |
540 | 542 | hci_send_acl(conn->hcon, skb, flags); |
541 | 543 | } |
542 | 544 | |
... | ... | @@ -590,6 +592,8 @@ |
590 | 592 | else |
591 | 593 | flags = ACL_START; |
592 | 594 | |
595 | + bt_cb(skb)->force_active = chan->force_active; | |
596 | + | |
593 | 597 | hci_send_acl(chan->conn->hcon, skb, flags); |
594 | 598 | } |
595 | 599 | |
... | ... | @@ -1215,6 +1219,7 @@ |
1215 | 1219 | else |
1216 | 1220 | flags = ACL_START; |
1217 | 1221 | |
1222 | + bt_cb(skb)->force_active = chan->force_active; | |
1218 | 1223 | hci_send_acl(hcon, skb, flags); |
1219 | 1224 | } |
1220 | 1225 |
net/bluetooth/l2cap_sock.c
... | ... | @@ -390,6 +390,7 @@ |
390 | 390 | struct sock *sk = sock->sk; |
391 | 391 | struct l2cap_chan *chan = l2cap_pi(sk)->chan; |
392 | 392 | struct bt_security sec; |
393 | + struct bt_power pwr; | |
393 | 394 | int len, err = 0; |
394 | 395 | |
395 | 396 | BT_DBG("sk %p", sk); |
... | ... | @@ -438,6 +439,21 @@ |
438 | 439 | |
439 | 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 | 457 | default: |
442 | 458 | err = -ENOPROTOOPT; |
443 | 459 | break; |
... | ... | @@ -538,6 +554,7 @@ |
538 | 554 | struct sock *sk = sock->sk; |
539 | 555 | struct l2cap_chan *chan = l2cap_pi(sk)->chan; |
540 | 556 | struct bt_security sec; |
557 | + struct bt_power pwr; | |
541 | 558 | int len, err = 0; |
542 | 559 | u32 opt; |
543 | 560 | |
... | ... | @@ -614,6 +631,23 @@ |
614 | 631 | chan->flushable = opt; |
615 | 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 | 651 | default: |
618 | 652 | err = -ENOPROTOOPT; |
619 | 653 | break; |
... | ... | @@ -771,6 +805,7 @@ |
771 | 805 | chan->role_switch = pchan->role_switch; |
772 | 806 | chan->force_reliable = pchan->force_reliable; |
773 | 807 | chan->flushable = pchan->flushable; |
808 | + chan->force_active = pchan->force_active; | |
774 | 809 | } else { |
775 | 810 | |
776 | 811 | switch (sk->sk_type) { |
... | ... | @@ -801,6 +836,7 @@ |
801 | 836 | chan->role_switch = 0; |
802 | 837 | chan->force_reliable = 0; |
803 | 838 | chan->flushable = BT_FLUSHABLE_OFF; |
839 | + chan->force_active = BT_POWER_FORCE_ACTIVE_ON; | |
804 | 840 | } |
805 | 841 | |
806 | 842 | /* Default config options */ |