8960_BT: Added sleep support on Host side.
Signed-off-by: Bhasker Neti <bneti@codeaurora.org>
Conflicts:
drivers/bluetooth/hci_smd.c
diff --git a/drivers/bluetooth/hci_smd.c b/drivers/bluetooth/hci_smd.c
index 7132c7c..6555e21 100644
--- a/drivers/bluetooth/hci_smd.c
+++ b/drivers/bluetooth/hci_smd.c
@@ -28,18 +28,74 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/hci.h>
#include <mach/msm_smd.h>
+#include <linux/wakelock.h>
-#define EVENT_CHANNEL "APPS_RIVA_BT_CMD"
-#define DATA_CHANNEL "APPS_RIVA_BT_ACL"
+#define EVENT_CHANNEL "APPS_RIVA_BT_CMD"
+#define DATA_CHANNEL "APPS_RIVA_BT_ACL"
+#define RX_Q_MONITOR (1) /* 1 milli second */
struct hci_smd_data {
struct hci_dev *hdev;
struct smd_channel *event_channel;
struct smd_channel *data_channel;
+ struct wake_lock wake_lock_tx;
+ struct wake_lock wake_lock_rx;
+ struct timer_list rx_q_timer;
};
struct hci_smd_data hs;
+/* Rx queue monitor timer function */
+static int is_rx_q_empty(unsigned long arg)
+{
+ struct hci_dev *hdev = (struct hci_dev *) arg;
+ struct sk_buff_head *list_ = &hdev->rx_q;
+ struct sk_buff *list = ((struct sk_buff *)list_)->next;
+ BT_DBG("%s Rx timer triggered", hdev->name);
+
+ if (list == (struct sk_buff *)list_) {
+ BT_DBG("%s RX queue empty", hdev->name);
+ return 1;
+ } else{
+ BT_DBG("%s RX queue not empty", hdev->name);
+ return 0;
+ }
+}
+
+static void release_lock(void)
+{
+ struct hci_smd_data *hsmd = &hs;
+ BT_DBG("Releasing Rx Lock");
+ if (is_rx_q_empty((unsigned long)hsmd->hdev) &&
+ wake_lock_active(&hs.wake_lock_rx))
+ wake_unlock(&hs.wake_lock_rx);
+}
+
+/* Rx timer callback function */
+static void schedule_timer(unsigned long arg)
+{
+ struct hci_dev *hdev = (struct hci_dev *) arg;
+ struct hci_smd_data *hsmd = &hs;
+ BT_DBG("%s Schedule Rx timer", hdev->name);
+
+ if (is_rx_q_empty(arg) && wake_lock_active(&hs.wake_lock_rx)) {
+ BT_DBG("%s RX queue empty", hdev->name);
+ /*
+ * Since the queue is empty, its ideal
+ * to release the wake lock on Rx
+ */
+ wake_unlock(&hs.wake_lock_rx);
+ } else{
+ BT_DBG("%s RX queue not empty", hdev->name);
+ /*
+ * Restart the timer to monitor whether the Rx queue is
+ * empty for releasing the Rx wake lock
+ */
+ mod_timer(&hsmd->rx_q_timer,
+ jiffies + msecs_to_jiffies(RX_Q_MONITOR));
+ }
+}
+
static int hci_smd_open(struct hci_dev *hdev)
{
set_bit(HCI_RUNNING, &hdev->flags);
@@ -63,32 +119,36 @@
static void hci_smd_recv_data(unsigned long arg)
{
- int len;
- int rc;
- struct sk_buff *skb;
- unsigned char *buf;
+ int len = 0;
+ int rc = 0;
+ struct sk_buff *skb = NULL;
+ unsigned char *buf = NULL;
struct hci_smd_data *hsmd = &hs;
+ wake_lock(&hs.wake_lock_rx);
len = smd_read_avail(hsmd->data_channel);
-
+ if (len <= 0) {
+ BT_ERR("Nothing to read from SMD channel\n");
+ goto out_data;
+ }
while (len > 0) {
skb = bt_skb_alloc(len, GFP_KERNEL);
if (!skb) {
BT_ERR("Error in allocating socket buffer\n");
- return;
+ goto out_data;
}
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
BT_ERR("Error in allocating buffer\n");
- kfree_skb(skb);
- return;
+ rc = -ENOMEM;
+ goto out_data;
}
rc = smd_read_from_cb(hsmd->data_channel, (void *)buf, len);
if (rc < len) {
BT_ERR("Error in reading from the channel");
- return;
+ goto out_data;
}
memcpy(skb_put(skb, len), buf, len);
@@ -100,40 +160,64 @@
rc = hci_recv_frame(skb);
if (rc < 0) {
BT_ERR("Error in passing the packet to HCI Layer");
- return;
+ goto out_data;
}
kfree(buf);
+ buf = NULL;
len = smd_read_avail(hsmd->data_channel);
+ /*
+ * Start the timer to monitor whether the Rx queue is
+ * empty for releasing the Rx wake lock
+ */
+ BT_DBG("Rx Timer is starting\n");
+ mod_timer(&hsmd->rx_q_timer,
+ jiffies + msecs_to_jiffies(RX_Q_MONITOR));
+ }
+out_data:
+ release_lock();
+ if (rc) {
+ if (skb)
+ kfree_skb(skb);
+ kfree(buf);
}
}
static void hci_smd_recv_event(unsigned long arg)
{
- int len;
- int rc;
- struct sk_buff *skb;
- unsigned char *buf;
+ int len = 0;
+ int rc = 0;
+ struct sk_buff *skb = NULL;
+ unsigned char *buf = NULL;
struct hci_smd_data *hsmd = &hs;
+ wake_lock(&hs.wake_lock_rx);
len = smd_read_avail(hsmd->event_channel);
if (len > HCI_MAX_FRAME_SIZE) {
BT_ERR("Frame larger than the allowed size");
- return;
+ goto out_event;
+ } else if (len <= 0) {
+ BT_ERR("Nothing to read from SMD channel\n");
+ goto out_event;
}
while (len > 0) {
skb = bt_skb_alloc(len, GFP_KERNEL);
- if (!skb)
- return;
-
+ if (!skb) {
+ BT_ERR("Error in allocating socket buffer\n");
+ goto out_event;
+ }
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
- kfree_skb(skb);
- return;
+ BT_ERR("Error in allocating buffer\n");
+ rc = -ENOMEM;
+ goto out_event;
}
-
rc = smd_read_from_cb(hsmd->event_channel, (void *)buf, len);
+ if (rc < len) {
+ BT_ERR("Error in reading from the event channel");
+ goto out_event;
+ }
memcpy(skb_put(skb, len), buf, len);
skb->dev = (void *)hsmd->hdev;
@@ -144,40 +228,70 @@
rc = hci_recv_frame(skb);
if (rc < 0) {
BT_ERR("Error in passing the packet to HCI Layer");
- return;
+ goto out_event;
}
kfree(buf);
+ buf = NULL;
len = smd_read_avail(hsmd->event_channel);
+ /*
+ * Start the timer to monitor whether the Rx queue is
+ * empty for releasing the Rx wake lock
+ */
+ BT_DBG("Rx Timer is starting\n");
+ mod_timer(&hsmd->rx_q_timer,
+ jiffies + msecs_to_jiffies(RX_Q_MONITOR));
+ }
+out_event:
+ release_lock();
+ if (rc) {
+ if (skb)
+ kfree_skb(skb);
+ kfree(buf);
}
}
static int hci_smd_send_frame(struct sk_buff *skb)
{
int len;
+ int avail;
+ int ret = 0;
+ wake_lock(&hs.wake_lock_tx);
switch (bt_cb(skb)->pkt_type) {
case HCI_COMMAND_PKT:
+ avail = smd_write_avail(hs.event_channel);
+ if (!avail) {
+ BT_ERR("No space available for smd frame");
+ ret = -ENOSPC;
+ }
len = smd_write(hs.event_channel, skb->data, skb->len);
if (len < skb->len) {
BT_ERR("Failed to write Command %d", len);
- return -ENODEV;
+ ret = -ENODEV;
}
break;
case HCI_ACLDATA_PKT:
case HCI_SCODATA_PKT:
+ avail = smd_write_avail(hs.event_channel);
+ if (!avail) {
+ BT_ERR("No space available for smd frame");
+ ret = -ENOSPC;
+ }
len = smd_write(hs.data_channel, skb->data, skb->len);
if (len < skb->len) {
BT_ERR("Failed to write Data %d", len);
- return -ENODEV;
+ ret = -ENODEV;
}
break;
default:
BT_ERR("Uknown packet type\n");
- return -ENODEV;
+ ret = -ENODEV;
break;
}
- return 0;
+
+ wake_unlock(&hs.wake_lock_tx);
+ return ret;
}
@@ -250,6 +364,15 @@
hdev->destruct = hci_smd_destruct;
hdev->owner = THIS_MODULE;
+ wake_lock_init(&hs.wake_lock_rx, WAKE_LOCK_SUSPEND, "msm_smd_Rx");
+ wake_lock_init(&hs.wake_lock_tx, WAKE_LOCK_SUSPEND, "msm_smd_Tx");
+ /*
+ * Setup the timer to monitor whether the Rx queue is empty,
+ * to control the wake lock release
+ */
+ setup_timer(&hsmd->rx_q_timer, schedule_timer,
+ (unsigned long) hsmd->hdev);
+
/* Open the SMD Channel and device and register the callback function */
rc = smd_named_open_on_edge(EVENT_CHANNEL, SMD_APPS_WCNSS,
&hsmd->event_channel, hdev, hci_smd_notify_event);
@@ -286,6 +409,11 @@
hs.event_channel = 0;
smd_close(hs.data_channel);
hs.data_channel = 0;
+ wake_lock_destroy(&hs.wake_lock_rx);
+ wake_lock_destroy(&hs.wake_lock_tx);
+
+ /*Destroy the timer used to monitor the Rx queue for emptiness */
+ del_timer_sync(&hs.rx_q_timer);
}
static int hci_smd_init(void)