[PATCH 04/10] added sleep mode

From: Mark P . Mendelsohn
Date: Wed Aug 17 2011 - 18:12:17 EST


---
drivers/bluetooth/hci_h5.c | 101 +++++++++++++++++++++++++++++++++++---------
1 files changed, 81 insertions(+), 20 deletions(-)

diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index 0168269..265a9c5 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -61,6 +61,10 @@ static int h5extn = 1;
#define H5_ACK_PKT 0x00
#define H5_LE_PKT 0x0f

+static u8 h5_wakeup_msg[] = { 0x05, 0xfa };
+static u8 h5_woken_msg[] = { 0x06, 0xf9 };
+static u8 h5_sleep_msg[] = { 0x07, 0x78 };
+
struct h5_struct {
struct sk_buff_head unack; /* Unack'ed packets queue */
struct sk_buff_head rel; /* Reliable packets queue */
@@ -92,8 +96,53 @@ struct h5_struct {

/* Reliable packet sequence number - used to assign seq to each rel pkt. */
u8 msgq_txseq;
+
+ u8 sleep_state;
+ u8 is_there_activity;
+ u16 inactive_period;
+};
+
+enum sleep_states {
+ H5_ASLEEP,
+ H5_ASLEEP_TO_AWAKE,
+ H5_AWAKE,
+ H5_AWAKE_TO_ASLEEP
};

+#define TIMER_PERIOD 100
+#define HOST_CONTROLLER_IDLE_THRSH 4000
+
+static struct timer_list sleep_timer;
+
+extern struct sk_buff *h5_prepare_pkt(struct h5_struct *h5, u8 *data,
+ int len, int pkt_type);
+
+//
+// Timeout Handler
+//
+
+void sleep_timer_function(unsigned long data)
+{
+ struct h5_struct *lh5 = (struct h5_struct *)data;
+
+ if (lh5->is_there_activity) {
+ lh5->is_there_activity = 0;
+ lh5->inactive_period = 0;
+ } else if (lh5->sleep_state != H5_ASLEEP) {
+ lh5->inactive_period += TIMER_PERIOD;
+
+ if (lh5->inactive_period >= HOST_CONTROLLER_IDLE_THRSH) {
+ BT_DBG("moves to ASLEEP");
+ lh5->sleep_state = H5_ASLEEP;
+ lh5->inactive_period = 0;
+ struct sk_buff *nskb = h5_prepare_pkt(lh5, h5_sleep_msg, sizeof(h5_sleep_msg), 15);
+ skb_queue_tail(&lh5->unrel, nskb);
+ }
+ }
+
+ mod_timer(&sleep_timer, jiffies + TIMER_PERIOD * HZ / 1000);
+}
+
/* ---- H5 CRC calculation ---- */

/* Table for calculating CRC for polynomial 0x1021, LSB processed first,
@@ -170,6 +219,14 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
return 0;
}

+ h5->is_there_activity = 1;
+
+ if (h5->sleep_state == H5_ASLEEP) {
+ struct sk_buff *nskb = h5_prepare_pkt(h5, h5_wakeup_msg, sizeof(h5_wakeup_msg), 15);
+ skb_queue_tail(&h5->unrel, skb);
+ h5->sleep_state = H5_AWAKE;
+ }
+
switch (bt_cb(skb)->pkt_type) {
case HCI_ACLDATA_PKT:
case HCI_COMMAND_PKT:
@@ -189,7 +246,7 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
return 0;
}

-static struct sk_buff *h5_prepare_pkt(struct h5_struct *h5, u8 *data,
+struct sk_buff *h5_prepare_pkt(struct h5_struct *h5, u8 *data,
int len, int pkt_type)
{
struct sk_buff *nskb;
@@ -357,6 +414,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
static int h5_flush(struct hci_uart *hu)
{
BT_DBG("hu %p\n", hu);
+ del_timer_sync(&sleep_timer);
return 0;
}

@@ -417,28 +475,22 @@ static void h5_pkt_cull(struct h5_struct *h5)
static void h5_handle_le_pkt(struct hci_uart *hu)
{
struct h5_struct *h5 = hu->priv;
- u8 conf_pkt[4] = { 0xad, 0xef, 0xac, 0xed };
- u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 };
- u8 sync_pkt[4] = { 0x00, 0xd0, 0x01, 0x7e };
-
- /* spot "conf" pkts and reply with a "conf rsp" pkt */
- if (h5->rx_skb->data[1] >> 4 == 4 && h5->rx_skb->data[2] == 0 &&
- !memcmp(&h5->rx_skb->data[4], conf_pkt, 4)) {
- struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
-
- BT_DBG("Found a LE conf pkt\n");
- if (!nskb)
- return;
- memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
- bt_cb(nskb)->pkt_type = H5_LE_PKT;
+ u8 wakeup_pkt[] = { 0x05, 0xfa };
+ u8 woken_pkt[] = { 0x06, 0xf9 };
+
+ struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
+
+ BT_DBG("Found a LE pkt\n");

+ if (!nskb)
+ return;
+
+ if (!memcmp(&h5->rx_skb->data[4], wakeup_pkt, 2)) {
+ memcpy(skb_put(nskb, 2), woken_pkt, 2);
+ bt_cb(nskb)->pkt_type = H5_LE_PKT;
skb_queue_head(&h5->unrel, nskb);
hci_uart_tx_wakeup(hu);
- }
- /* Spot "sync" pkts. If we find one...disaster! */
- else if (h5->rx_skb->data[1] >> 4 == 4 && h5->rx_skb->data[2] == 0 &&
- !memcmp(&h5->rx_skb->data[4], sync_pkt, 4)) {
- BT_ERR("Found a LE sync pkt, card has reset");
+ } else if (!memcmp(&h5->rx_skb->data[4], woken_pkt, 2)) {
}
}

@@ -772,6 +824,15 @@ static int h5_open(struct hci_uart *hu)
if (h5txcrc)
h5->use_crc = 1;

+ init_timer(&sleep_timer);
+
+ sleep_timer.expires = jiffies + TIMER_PERIOD * HZ / 1000;
+ sleep_timer.data = (unsigned long)h5;
+ sleep_timer.function = sleep_timer_function;
+
+ add_timer(&sleep_timer);
+ h5->is_there_activity = 0;
+
return 0;
}

--
1.7.1