aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/bluetooth
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@intel.com>2012-07-16 09:12:16 -0400
committerGustavo Padovan <gustavo.padovan@collabora.co.uk>2012-07-17 13:48:37 -0400
commit95c5c22097433711de93bc377af89918c6140f77 (patch)
treef6bef30e1ba112cb510465ffb358a08da5c62249 /drivers/bluetooth
parentf674a057c1f6b0fedf7d989c6dac0b482fef1b55 (diff)
Bluetooth: Implement proper low-power support for Three-wire UART
This patch adds on-demand wakeup request sending (and re-sendind) when we are in low-power state. When the controller enters this state it will send a sleep message after which the host is not allowed to send any other packets until a wakeup request has been sent and the woken message received as a response to it. The wakeup requests are re-sent periodically until a woken message is received. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r--drivers/bluetooth/hci_h5.c37
1 files changed, 32 insertions, 5 deletions
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index 6fb8d4eca0f..8819ce81ca5 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -81,7 +81,11 @@ struct h5 {
81 H5_ACTIVE, 81 H5_ACTIVE,
82 } state; 82 } state;
83 83
84 bool sleeping; 84 enum {
85 H5_AWAKE,
86 H5_SLEEPING,
87 H5_WAKING_UP,
88 } sleep;
85}; 89};
86 90
87static void h5_reset_rx(struct h5 *h5); 91static void h5_reset_rx(struct h5 *h5);
@@ -111,6 +115,8 @@ static void h5_timed_event(unsigned long arg)
111 struct sk_buff *skb; 115 struct sk_buff *skb;
112 unsigned long flags; 116 unsigned long flags;
113 117
118 BT_DBG("%s", hu->hdev->name);
119
114 if (h5->state == H5_UNINITIALIZED) 120 if (h5->state == H5_UNINITIALIZED)
115 h5_link_control(hu, sync_req, sizeof(sync_req)); 121 h5_link_control(hu, sync_req, sizeof(sync_req));
116 122
@@ -122,6 +128,11 @@ static void h5_timed_event(unsigned long arg)
122 goto wakeup; 128 goto wakeup;
123 } 129 }
124 130
131 if (h5->sleep != H5_AWAKE) {
132 h5->sleep = H5_SLEEPING;
133 goto wakeup;
134 }
135
125 BT_DBG("hu %p retransmitting %u pkts", hu, h5->unack.qlen); 136 BT_DBG("hu %p retransmitting %u pkts", hu, h5->unack.qlen);
126 137
127 spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING); 138 spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING);
@@ -262,12 +273,15 @@ static void h5_handle_internal_rx(struct hci_uart *hu)
262 return; 273 return;
263 } else if (memcmp(data, sleep_req, 2) == 0) { 274 } else if (memcmp(data, sleep_req, 2) == 0) {
264 BT_DBG("Peer went to sleep"); 275 BT_DBG("Peer went to sleep");
265 h5->sleeping = true; 276 h5->sleep = H5_SLEEPING;
266 h5_link_control(hu, wakeup_req, 2); 277 return;
267 } else if (memcmp(data, woken_req, 2) == 0) { 278 } else if (memcmp(data, woken_req, 2) == 0) {
268 BT_DBG("Peer woke up"); 279 BT_DBG("Peer woke up");
269 h5->sleeping = false; 280 h5->sleep = H5_AWAKE;
270 return; 281 } else if (memcmp(data, wakeup_req, 2) == 0) {
282 BT_DBG("Peer requested wakeup");
283 h5_link_control(hu, woken_req, 2);
284 h5->sleep = H5_AWAKE;
271 } else { 285 } else {
272 BT_DBG("Link Control: 0x%02hhx 0x%02hhx", data[0], data[1]); 286 BT_DBG("Link Control: 0x%02hhx 0x%02hhx", data[0], data[1]);
273 return; 287 return;
@@ -625,6 +639,19 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
625 unsigned long flags; 639 unsigned long flags;
626 struct sk_buff *skb, *nskb; 640 struct sk_buff *skb, *nskb;
627 641
642 if (h5->sleep != H5_AWAKE) {
643 const unsigned char wakeup_req[] = { 0x05, 0xfa };
644
645 if (h5->sleep == H5_WAKING_UP)
646 return NULL;
647
648 h5->sleep = H5_WAKING_UP;
649 BT_DBG("Sending wakeup request");
650
651 mod_timer(&h5->timer, jiffies + HZ / 100);
652 return h5_prepare_pkt(hu, HCI_3WIRE_LINK_PKT, wakeup_req, 2);
653 }
654
628 if ((skb = skb_dequeue(&h5->unrel)) != NULL) { 655 if ((skb = skb_dequeue(&h5->unrel)) != NULL) {
629 nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type, 656 nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
630 skb->data, skb->len); 657 skb->data, skb->len);