summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-09-02 13:52:20 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-09-02 13:52:20 -0700
commit4e06ee349f044e0482f3d59d7b49893f24844db5 (patch)
tree05138f4537fb2e80c52fafaaa669e05b18f7c1e8
parenta278b7c5a4ed7347a85fbfdb56c15c833b2a9feb (diff)
parente7203bbcb0dc06de3bb9e1cf32639442ffb0d201 (diff)
Merge "icnss: Fix NULL pointer dererference"
-rw-r--r--drivers/soc/qcom/icnss.c74
1 files changed, 60 insertions, 14 deletions
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index aff9683b394f..e69d32dfbd83 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -217,6 +217,8 @@ module_param(quirks, ulong, 0600);
void *icnss_ipc_log_context;
+#define ICNSS_EVENT_PENDING 2989
+
enum icnss_driver_event_type {
ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
ICNSS_DRIVER_EVENT_SERVER_EXIT,
@@ -370,6 +372,7 @@ static struct icnss_priv {
struct notifier_block get_service_nb;
void *modem_notify_handler;
struct notifier_block modem_ssr_nb;
+ struct wakeup_source ws;
} *penv;
static void icnss_hw_write_reg(void *base, u32 offset, u32 val)
@@ -486,6 +489,7 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type,
event->type = type;
event->data = data;
init_completion(&event->complete);
+ event->ret = ICNSS_EVENT_PENDING;
event->sync = sync;
spin_lock_irqsave(&penv->event_lock, flags);
@@ -494,12 +498,26 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type,
penv->stats.events[type].posted++;
queue_work(penv->event_wq, &penv->event_work);
- if (sync) {
- ret = wait_for_completion_interruptible(&event->complete);
- if (ret == 0)
- ret = event->ret;
- kfree(event);
+
+ if (!sync)
+ return ret;
+
+ ret = wait_for_completion_interruptible(&event->complete);
+
+ icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
+ icnss_driver_event_to_str(type), type, penv->state, ret,
+ event->ret);
+
+ spin_lock_irqsave(&penv->event_lock, flags);
+ if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
+ event->sync = false;
+ spin_unlock_irqrestore(&penv->event_lock, flags);
+ return ret;
}
+ spin_unlock_irqrestore(&penv->event_lock, flags);
+
+ ret = event->ret;
+ kfree(event);
return ret;
}
@@ -1039,7 +1057,7 @@ int icnss_hw_reset(struct icnss_priv *priv)
MPM_WCSSAON_CONFIG_FORCE_ACTIVE, 1);
icnss_hw_poll_reg_field(priv->mem_base_va, SR_WCSSAON_SR_LSB_OFFSET,
- SR_WCSSAON_SR_LSB_RETENTION_STATUS, 1, 10,
+ SR_WCSSAON_SR_LSB_RETENTION_STATUS, 1, 100,
ICNSS_HW_REG_RETRY);
for (i = 0; i < ICNSS_HW_REG_RETRY; i++) {
@@ -1546,6 +1564,13 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
goto out;
}
+ /* During recovery do not send mode request for WLAN OFF as
+ * FW not able to process it.
+ */
+ if (test_bit(ICNSS_PD_RESTART, &penv->state) &&
+ mode == QMI_WLFW_OFF_V01)
+ return 0;
+
icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
penv->state, mode);
@@ -1916,6 +1941,8 @@ static int icnss_driver_event_fw_ready_ind(void *data)
if (!penv)
return -ENODEV;
+ __pm_stay_awake(&penv->ws);
+
set_bit(ICNSS_FW_READY, &penv->state);
icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
@@ -1933,7 +1960,10 @@ static int icnss_driver_event_fw_ready_ind(void *data)
else
ret = icnss_call_driver_probe(penv);
+ __pm_relax(&penv->ws);
+
out:
+ __pm_relax(&penv->ws);
return ret;
}
@@ -1941,10 +1971,10 @@ static int icnss_driver_event_register_driver(void *data)
{
int ret = 0;
- if (penv->ops) {
- ret = -EEXIST;
- goto out;
- }
+ if (penv->ops)
+ return -EEXIST;
+
+ __pm_stay_awake(&penv->ws);
penv->ops = data;
@@ -1971,16 +2001,21 @@ static int icnss_driver_event_register_driver(void *data)
set_bit(ICNSS_DRIVER_PROBED, &penv->state);
+ __pm_relax(&penv->ws);
+
return 0;
power_off:
icnss_hw_power_off(penv);
out:
+ __pm_relax(&penv->ws);
return ret;
}
static int icnss_driver_event_unregister_driver(void *data)
{
+ __pm_stay_awake(&penv->ws);
+
if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
penv->ops = NULL;
goto out;
@@ -1996,6 +2031,7 @@ static int icnss_driver_event_unregister_driver(void *data)
icnss_hw_power_off(penv);
out:
+ __pm_relax(&penv->ws);
return 0;
}
@@ -2071,11 +2107,15 @@ static void icnss_driver_event_work(struct work_struct *work)
penv->stats.events[event->type].processed++;
+ spin_lock_irqsave(&penv->event_lock, flags);
if (event->sync) {
event->ret = ret;
complete(&event->complete);
- } else
- kfree(event);
+ continue;
+ }
+ spin_unlock_irqrestore(&penv->event_lock, flags);
+
+ kfree(event);
spin_lock_irqsave(&penv->event_lock, flags);
}
@@ -2372,6 +2412,9 @@ int icnss_register_driver(struct icnss_driver_ops *ops)
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
true, ops);
+ if (ret == -ERESTARTSYS)
+ ret = 0;
+
out:
return ret;
}
@@ -2890,7 +2933,7 @@ static int icnss_get_vreg_info(struct device *dev,
reg = devm_regulator_get_optional(dev, vreg_info->name);
- if (IS_ERR(reg) == -EPROBE_DEFER) {
+ if (PTR_ERR(reg) == -EPROBE_DEFER) {
icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
vreg_info->name);
ret = PTR_ERR(reg);
@@ -2910,7 +2953,6 @@ static int icnss_get_vreg_info(struct device *dev,
vreg_info->name, ret);
goto done;
}
-
}
vreg_info->reg = reg;
@@ -3502,6 +3544,8 @@ static int icnss_probe(struct platform_device *pdev)
spin_lock_init(&priv->event_lock);
spin_lock_init(&priv->on_off_lock);
+ wakeup_source_init(&priv->ws, "icnss_ws");
+
priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
if (!priv->event_wq) {
icnss_pr_err("Workqueue creation failed\n");
@@ -3563,6 +3607,8 @@ static int icnss_remove(struct platform_device *pdev)
icnss_bw_deinit(penv);
+ wakeup_source_trash(&penv->ws);
+
icnss_hw_power_off(penv);
dev_set_drvdata(&pdev->dev, NULL);