From 393b442196ed680a43b7ae2b8ac5f828690eb454 Mon Sep 17 00:00:00 2001 From: Nagarjuna Kristam Date: Thu, 27 Dec 2012 11:58:04 +0530 Subject: misc: bluedroid_pm: check BT TX and RX status before wake_lock release 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 Reviewed-on: http://git-master/r/299894 GVS: Gerrit_Virtual_Submit Tested-by: Todd Poynter Reviewed-by: Anshul Jain (SW) --- drivers/misc/bluedroid_pm.c | 109 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 20 deletions(-) diff --git a/drivers/misc/bluedroid_pm.c b/drivers/misc/bluedroid_pm.c index 3b178d429cc2..9994fface05e 100644 --- a/drivers/misc/bluedroid_pm.c +++ b/drivers/misc/bluedroid_pm.c @@ -36,12 +36,29 @@ #include #include #include +#include #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; @@ -49,6 +66,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; @@ -77,12 +95,45 @@ void bt_wlan_unlock(void) } 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 +208,12 @@ static int bluedroid_pm_probe(struct platform_device *pdev) bluedroid_pm->vdd_3v3 = regulator_get(&pdev->dev, "vdd_bt_3v3"); if (IS_ERR_OR_NULL(bluedroid_pm->vdd_3v3)) { - pr_warn("%s: regulator vdd_bt_3v3 not available\n", __func__); + BDP_WARN("regulator vdd_bt_3v3 not available\n"); bluedroid_pm->vdd_3v3 = NULL; } bluedroid_pm->vdd_1v8 = regulator_get(&pdev->dev, "vddio_bt_1v8"); if (IS_ERR_OR_NULL(bluedroid_pm->vdd_1v8)) { - pr_warn("%s: regulator vddio_bt_1v8 not available\n", __func__); + BDP_WARN("regulator vddio_bt_1v8 not available\n"); bluedroid_pm->vdd_1v8 = NULL; } @@ -171,12 +222,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; } @@ -187,12 +238,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; } @@ -228,31 +279,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; } @@ -262,20 +313,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; } @@ -285,7 +340,7 @@ static int bluedroid_pm_probe(struct platform_device *pdev) bluedroid_pm->resume_min_frequency = res->start; platform_set_drvdata(pdev, bluedroid_pm); - pr_debug("RFKILL BT driver successfully registered"); + BDP_WARN("driver successfully registered"); return 0; free_res: @@ -322,6 +377,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) { @@ -406,11 +462,24 @@ static int lpm_write_proc(struct file *file, const char *buffer, if (!bluedroid_pm->is_blocked) { if (buf[0] == '0') { - 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') { + 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; @@ -435,20 +504,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 = create_proc_entry("lpm", 0622, bluetooth_sleep_dir); 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; } -- cgit v1.2.3