From fd079d0c51ec4831187025d77de67a99f3ded897 Mon Sep 17 00:00:00 2001 From: Vitaly Wool Date: Wed, 29 Feb 2012 15:15:22 +0100 Subject: cw1200: Driver hung detection and reporting If there is a firmware exception or a bh error, report this to the userspace. Userspace will then take care of handling this event, e. g. by reloading the driver completely. Category: bugfix FIX=DMS01117799 ST-Ericsson ID: 401162 ST-Ericsson FOSS-OUT ID: NA Change-Id: I74a8a434f9befbab66ff4cf6c5b5ce82a359c766 Signed-off-by: Vitaly Wool Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44302 Reviewed-by: QATOOLS Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/bh.c | 4 ++++ drivers/staging/cw1200/debug.c | 34 +++++++++++++++++++++++++++++++ drivers/staging/cw1200/wsm.c | 45 +++++++++++++++++++++++++++++++----------- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 4b678f40c40..34bf7d733b8 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -549,6 +549,10 @@ tx: if (!term) { cw1200_dbg(CW1200_DBG_ERROR, "[BH] Fatal error, exitting.\n"); priv->bh_error = 1; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); + cw1200_pm_stay_awake(&priv->pm_state, 3*HZ); +#endif /* TODO: schedule_work(recovery) */ #ifndef HAS_PUT_TASK_STRUCT /* The only reason of having this stupid code here is diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 597f94de87f..2056101a3f4 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -426,6 +426,34 @@ static const struct file_operations fops_11n = { .llseek = default_llseek, }; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) +static ssize_t cw1200_hang_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + char buf[1]; + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, 1)) + return -EFAULT; + + if (priv->vif) { + cw1200_pm_stay_awake(&priv->pm_state, 3*HZ); + ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); + } else + return -ENODEV; + + return count; +} + +static const struct file_operations fops_hang = { + .open = cw1200_generic_open, + .write = cw1200_hang_write, + .llseek = default_llseek, +}; +#endif + int cw1200_debug_init(struct cw1200_common *priv) { struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv), @@ -451,6 +479,12 @@ int cw1200_debug_init(struct cw1200_common *priv) d->debugfs_phy, priv, &fops_11n)) goto err; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + if (!debugfs_create_file("hang", S_IWUSR, d->debugfs_phy, + priv, &fops_hang)) + goto err; +#endif + return 0; err: diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 5c52cdbb1c2..bb94d19da08 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1083,9 +1083,15 @@ void wsm_lock_tx(struct cw1200_common *priv) { wsm_cmd_lock(priv); if (atomic_add_return(1, &priv->tx_lock) == 1) { - WARN_ON(wait_event_timeout(priv->bh_evt_wq, - !priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); - wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n"); + if (priv->bh_error) { + wsm_printk(KERN_ERR "fatal error occured, " + "could not take lock\n"); + } else { + WARN_ON(wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, + WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n"); + } } wsm_cmd_unlock(priv); } @@ -1098,20 +1104,29 @@ void wsm_lock_tx_async(struct cw1200_common *priv) void wsm_flush_tx(struct cw1200_common *priv) { - BUG_ON(!atomic_read(&priv->tx_lock)); - WARN_ON(wait_event_timeout(priv->bh_evt_wq, - !priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + if (priv->bh_error) + wsm_printk(KERN_ERR "fatal error occured, will not flush\n"); + else { + BUG_ON(!atomic_read(&priv->tx_lock)); + WARN_ON(wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, + WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + } } void wsm_unlock_tx(struct cw1200_common *priv) { int tx_lock; - tx_lock = atomic_sub_return(1, &priv->tx_lock); - if (tx_lock < 0) { - BUG_ON(1); - } else if (tx_lock == 0) { - cw1200_bh_wakeup(priv); - wsm_printk(KERN_DEBUG "[WSM] TX is unlocked.\n"); + if (priv->bh_error) + wsm_printk(KERN_ERR "fatal error occured, unlock is unsafe\n"); + else { + tx_lock = atomic_sub_return(1, &priv->tx_lock); + if (tx_lock < 0) { + BUG_ON(1); + } else if (tx_lock == 0) { + cw1200_bh_wakeup(priv); + wsm_printk(KERN_DEBUG "[WSM] TX is unlocked.\n"); + } } } @@ -1133,6 +1148,12 @@ int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len) "unknown error", }; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + /* Send the event upwards on the FW exception */ + cw1200_pm_stay_awake(&priv->pm_state, 3*HZ); + ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); +#endif + buf.begin = buf.data = data; buf.end = &buf.begin[len]; -- cgit v1.2.3