diff options
author | Johan Hedberg <johan.hedberg@intel.com> | 2012-07-16 09:12:16 -0400 |
---|---|---|
committer | Gustavo Padovan <gustavo.padovan@collabora.co.uk> | 2012-07-17 13:48:37 -0400 |
commit | 95c5c22097433711de93bc377af89918c6140f77 (patch) | |
tree | f6bef30e1ba112cb510465ffb358a08da5c62249 /drivers/bluetooth/hci_h5.c | |
parent | f674a057c1f6b0fedf7d989c6dac0b482fef1b55 (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/hci_h5.c')
-rw-r--r-- | drivers/bluetooth/hci_h5.c | 37 |
1 files changed, 32 insertions, 5 deletions
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 6fb8d4eca0fb..8819ce81ca56 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 | ||
87 | static void h5_reset_rx(struct h5 *h5); | 91 | static 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); |