misc: bluedroid_pm: check BT TX and RX status before wake_lock release
Nagarjuna Kristam [Thu, 27 Dec 2012 06:28:04 +0000 (11:28 +0530)]
bluedroid stack, updates only TX busy status through proc interface.

When BT HID/PAN-U connection are active, high probability of data is RX only
and no activity on Tx path. So, dis-allow suspend when either of BT TX or
BT RX is active.

Bug 1381466

Change-Id: Ib174b7d95d173c34e41ce393f93fda5fa204f217
Signed-off-by: Nagarjuna Kristam <nkristam@nvidia.com>
Reviewed-on: http://git-master/r/299894
(cherry picked from commit 912b388337c2b6703ddaabd95250afac95fee303)
Reviewed-on: http://git-master/r/336465
GVS: Gerrit_Virtual_Submit
Reviewed-by: Bitan Biswas <bbiswas@nvidia.com>

drivers/misc/bluedroid_pm.c

index c6f33bd..534e719 100644 (file)
 #include <linux/pm_qos.h>
 #include <linux/bluedroid_pm.h>
 #include <linux/delay.h>
+#include <linux/timer.h>
 
 #define PROC_DIR       "bluetooth/sleep"
+
+/* 5 seconds of Min CPU configurations during resume */
+#define DEFAULT_RESUME_CPU_TIMEOUT     5000000
+
+#define TX_TIMER_INTERVAL 5
+
+/* Macro to enable or disable debug logging */
+/* #define BLUEDROID_PM_DBG */
+#ifndef BLUEDROID_PM_DBG
+#define BDP_DBG(fmt, ...)      pr_debug("%s: " fmt, __func__, ##__VA_ARGS__)
+#else
+#define BDP_DBG(fmt, ...)      pr_warn("%s: " fmt, __func__, ##__VA_ARGS__)
+#endif
+
+#define BDP_WARN(fmt, ...)     pr_warn("%s: " fmt, __func__, ##__VA_ARGS__)
+#define BDP_ERR(fmt, ...)      pr_err("%s: " fmt, __func__, ##__VA_ARGS__)
+
+/* status flags for bluedroid_pm_driver */
+#define BT_WAKE        0x01
+
 struct bluedroid_pm_data {
        int gpio_reset;
        int gpio_shutdown;
@@ -47,6 +68,7 @@ struct bluedroid_pm_data {
        int ext_wake;
        int is_blocked;
        int resume_min_frequency;
+       unsigned long flags;
        unsigned host_wake_irq;
        struct regulator *vdd_3v3;
        struct regulator *vdd_1v8;
@@ -61,12 +83,59 @@ static bool bluedroid_pm_blocked = 1;
 static int create_bt_proc_interface(void *drv_data);
 static void remove_bt_proc_interface(void);
 
+static DEFINE_MUTEX(bt_wlan_sync);
+
+void bt_wlan_lock(void)
+{
+       mutex_lock(&bt_wlan_sync);
+}
+EXPORT_SYMBOL(bt_wlan_lock);
+
+void bt_wlan_unlock(void)
+{
+       mutex_unlock(&bt_wlan_sync);
+}
+EXPORT_SYMBOL(bt_wlan_unlock);
+
+/** bluedroid_m busy timer */
+static void bluedroid_pm_timer_expire(unsigned long data);
+static DEFINE_TIMER(bluedroid_pm_timer, bluedroid_pm_timer_expire, 0, 0);
+
 static irqreturn_t bluedroid_pm_hostwake_isr(int irq, void *dev_id)
 {
        /* schedule a tasklet to handle the change in the host wake line */
        return IRQ_HANDLED;
 }
 
+/**
+ * Handles bluedroid_pm busy timer expiration.
+ * @param data: bluedroid_pm strcuture.
+ */
+static void bluedroid_pm_timer_expire(unsigned long data)
+{
+       struct bluedroid_pm_data *bluedroid_pm =
+                               (struct bluedroid_pm_data *)data;
+
+       /*
+        * if bluedroid_pm data is NULL or timer is deleted with TX busy.
+        * return from the function.
+        */
+       if (!bluedroid_pm || test_bit(BT_WAKE, &bluedroid_pm->flags))
+               return;
+
+       if (!gpio_get_value(bluedroid_pm->host_wake)) {
+               /* BT can sleep */
+               BDP_DBG("Tx and Rx are idle, BT sleeping");
+               gpio_set_value(bluedroid_pm->ext_wake, 0);
+               wake_unlock(&bluedroid_pm->wake_lock);
+       } else {
+               /* BT Rx is busy, Reset Timer */
+               BDP_DBG("Rx is busy, restarting the timer");
+               mod_timer(&bluedroid_pm_timer,
+                                       jiffies + (TX_TIMER_INTERVAL * HZ));
+       }
+}
+
 static int bluedroid_pm_rfkill_set_power(void *data, bool blocked)
 {
        struct bluedroid_pm_data *bluedroid_pm = data;
@@ -157,12 +226,12 @@ static int bluedroid_pm_probe(struct platform_device *pdev)
                bluedroid_pm->gpio_reset = res->start;
                ret = gpio_request(bluedroid_pm->gpio_reset, "reset_gpio");
                if (ret) {
-                       pr_err("%s: Failed to get reset gpio\n", __func__);
+                       BDP_ERR("Failed to get reset gpio\n");
                        goto free_res;
                }
                gpio_direction_output(bluedroid_pm->gpio_reset, enable);
        } else {
-               pr_debug("%s: Reset gpio not registered.\n", __func__);
+               BDP_DBG("Reset gpio not registered.\n");
                bluedroid_pm->gpio_reset = 0;
        }
 
@@ -173,12 +242,12 @@ static int bluedroid_pm_probe(struct platform_device *pdev)
                ret = gpio_request(bluedroid_pm->gpio_shutdown,
                                                "shutdown_gpio");
                if (ret) {
-                       pr_err("%s: Failed to get shutdown gpio\n", __func__);
+                       BDP_ERR("Failed to get shutdown gpio\n");
                        goto free_res;
                }
                gpio_direction_output(bluedroid_pm->gpio_shutdown, enable);
        } else {
-               pr_debug("%s: shutdown gpio not registered\n", __func__);
+               BDP_DBG("shutdown gpio not registered\n");
                bluedroid_pm->gpio_shutdown = 0;
        }
 
@@ -214,31 +283,31 @@ static int bluedroid_pm_probe(struct platform_device *pdev)
                bluedroid_pm->host_wake = res->start;
                ret = gpio_request(bluedroid_pm->host_wake, "bt_host_wake");
                if (ret) {
-                       pr_err("%s: Failed to get host_wake gpio\n", __func__);
+                       BDP_ERR("Failed to get host_wake gpio\n");
                        goto free_res;
                }
                /* configure host_wake as input */
                gpio_direction_input(bluedroid_pm->host_wake);
        } else {
-               pr_debug("%s: gpio_host_wake not registered\n", __func__);
+               BDP_DBG("gpio_host_wake not registered\n");
                bluedroid_pm->host_wake = 0;
        }
 
 
        res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "host_wake");
        if (res) {
-               pr_err("%s : found host_wake irq\n", __func__);
+               BDP_DBG("found host_wake irq\n");
                bluedroid_pm->host_wake_irq = res->start;
                ret = request_irq(bluedroid_pm->host_wake_irq,
                                        bluedroid_pm_hostwake_isr,
                                        IRQF_DISABLED | IRQF_TRIGGER_RISING,
                                        "bluetooth hostwake", bluedroid_pm);
                if (ret) {
-                       pr_err("%s: Failed to get host_wake irq\n", __func__);
+                       BDP_ERR("Failed to get host_wake irq\n");
                        goto free_res;
                }
        } else {
-               pr_debug("%s: host_wake not registered\n", __func__);
+               BDP_DBG("host_wake not registered\n");
                bluedroid_pm->host_wake_irq = 0;
        }
 
@@ -248,20 +317,24 @@ static int bluedroid_pm_probe(struct platform_device *pdev)
                bluedroid_pm->ext_wake = res->start;
                ret = gpio_request(bluedroid_pm->ext_wake, "bt_ext_wake");
                if (ret) {
-                       pr_err("%s: Failed to get ext_wake gpio\n", __func__);
+                       BDP_ERR("Failed to get ext_wake gpio\n");
                        goto free_res;
                }
                /* configure ext_wake as output mode*/
                gpio_direction_output(bluedroid_pm->ext_wake, 1);
                if (create_bt_proc_interface(bluedroid_pm)) {
-                       pr_err("%s: Failed to create proc interface", __func__);
+                       BDP_ERR("Failed to create proc interface");
                        goto free_res;
                }
                /* initialize wake lock */
                wake_lock_init(&bluedroid_pm->wake_lock, WAKE_LOCK_SUSPEND,
                                                                "bluedroid_pm");
+               /* Initialize timer */
+               init_timer(&bluedroid_pm_timer);
+               bluedroid_pm_timer.function = bluedroid_pm_timer_expire;
+               bluedroid_pm_timer.data = bluedroid_pm;
        } else {
-               pr_debug("%s: gpio_ext_wake not registered\n", __func__);
+               BDP_DBG("gpio_ext_wake not registered\n");
                bluedroid_pm->ext_wake = 0;
        }
 
@@ -271,7 +344,7 @@ static int bluedroid_pm_probe(struct platform_device *pdev)
                                                pdata->resume_min_frequency;
 
        platform_set_drvdata(pdev, bluedroid_pm);
-       pr_debug("RFKILL BT driver successfully registered");
+       BDP_DBG("driver successfully registered");
        return 0;
 
 free_res:
@@ -308,6 +381,7 @@ static int bluedroid_pm_remove(struct platform_device *pdev)
                wake_lock_destroy(&bluedroid_pm->wake_lock);
                gpio_free(bluedroid_pm->ext_wake);
                remove_bt_proc_interface();
+               del_timer(&bluedroid_pm_timer);
        }
        if (bluedroid_pm->gpio_reset || bluedroid_pm->gpio_shutdown ||
                bluedroid_pm->vdd_1v8 || bluedroid_pm->vdd_3v3) {
@@ -387,19 +461,24 @@ static ssize_t lpm_write_proc(struct file *file, const char __user *buffer,
 
        if (!bluedroid_pm->is_blocked) {
                if (buf[0] == '0') {
-                       if (bluedroid_pm->resume_min_frequency)
-                               pm_qos_update_request(&bluedroid_pm->
-                                                       resume_cpu_freq_req,
-                                                       PM_QOS_DEFAULT_VALUE);
-                       gpio_set_value(bluedroid_pm->ext_wake, 0);
-                       wake_unlock(&bluedroid_pm->wake_lock);
+                       if (!gpio_get_value(bluedroid_pm->host_wake)) {
+                               /* BT can sleep */
+                               BDP_DBG("Tx and Rx are idle, BT sleeping");
+                                       gpio_set_value(bluedroid_pm->ext_wake, 0);
+                                       wake_unlock(&bluedroid_pm->wake_lock);
+                               } else {
+                                       /* Reset Timer */
+                                       BDP_DBG("Rx is busy, restarting the timer");
+                                       mod_timer(&bluedroid_pm_timer,
+                                               jiffies + (TX_TIMER_INTERVAL * HZ));
+                               }
+                       clear_bit(BT_WAKE, &bluedroid_pm->flags);
                } else if (buf[0] == '1') {
-                       if (bluedroid_pm->resume_min_frequency)
-                               pm_qos_update_request(&bluedroid_pm->
-                                                       resume_cpu_freq_req,
-                                                       bluedroid_pm->resume_min_frequency);
+                       BDP_DBG("Tx is busy, wake_lock taken, delete timer");
                        gpio_set_value(bluedroid_pm->ext_wake, 1);
                        wake_lock(&bluedroid_pm->wake_lock);
+                       del_timer(&bluedroid_pm_timer);
+                       set_bit(BT_WAKE, &bluedroid_pm->flags);
                } else {
                        kfree(buf);
                        return -EINVAL;
@@ -430,20 +509,20 @@ static int create_bt_proc_interface(void *drv_data)
 
        proc_bt_dir = proc_mkdir("bluetooth", NULL);
        if (proc_bt_dir == NULL) {
-               pr_err("Unable to create /proc/bluetooth directory");
+               BDP_ERR("Unable to create /proc/bluetooth directory");
                return -ENOMEM;
        }
 
        bluetooth_sleep_dir = proc_mkdir("sleep", proc_bt_dir);
        if (proc_bt_dir == NULL) {
-               pr_err("Unable to create /proc/bluetooth directory");
+               BDP_ERR("Unable to create /proc/bluetooth directory");
                return -ENOMEM;
        }
 
        /* Creating read/write "btwake" entry */
        ent = proc_create_data("lpm", 0622, bluetooth_sleep_dir, &lpm_fops, drv_data);
        if (ent == NULL) {
-               pr_err("Unable to create /proc/%s/btwake entry", PROC_DIR);
+               BDP_ERR("Unable to create /proc/%s/btwake entry", PROC_DIR);
                retval = -ENOMEM;
                goto fail;
        }