wl1271: Add hardware recovery mechanism
authorJuuso Oikarinen <juuso.oikarinen@nokia.com>
Tue, 21 Sep 2010 04:23:31 +0000 (06:23 +0200)
committerLuciano Coelho <luciano.coelho@nokia.com>
Tue, 28 Sep 2010 09:30:04 +0000 (12:30 +0300)
There is some probability of hardware failures, which currently go largely
undetected. Attempt to recover from these failures by shutting down the
hardware, and requesting mac80211 to reconfigure it.

Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: Teemu Paasikivi <ext-teemu.3.paasikivi@nokia.com>
Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
drivers/net/wireless/wl12xx/wl1271.h
drivers/net/wireless/wl12xx/wl1271_cmd.c
drivers/net/wireless/wl12xx/wl1271_main.c
drivers/net/wireless/wl12xx/wl1271_ps.c

index 272cff44ab53dfa7269e81f04e410105db3d32f7..3576c1cb067fd7b0efd01d2d53fcb8b3c622b877 100644 (file)
@@ -408,6 +408,9 @@ struct wl1271 {
        /* The target interrupt mask */
        struct work_struct irq_work;
 
+       /* Hardware recovery work */
+       struct work_struct recovery_work;
+
        /* The mbox event mask */
        u32 event_mask;
 
index 06b14f2abf55c3dd37456e952ea09647aadc92c9..170b5a8bdabc0ed80503e1bc8cb0859a8aed16f8 100644 (file)
@@ -94,6 +94,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
        status = le16_to_cpu(cmd->status);
        if (status != CMD_STATUS_SUCCESS) {
                wl1271_error("command execute failure %d", status);
+               ieee80211_queue_work(wl->hw, &wl->recovery_work);
                ret = -EIO;
        }
 
@@ -182,8 +183,10 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
        timeout = jiffies + msecs_to_jiffies(WL1271_EVENT_TIMEOUT);
 
        do {
-               if (time_after(jiffies, timeout))
+               if (time_after(jiffies, timeout)) {
+                       ieee80211_queue_work(wl->hw, &wl->recovery_work);
                        return -ETIMEDOUT;
+               }
 
                msleep(1);
 
index e7f096fb62126f652559cc03f5cc75bd8ba69253..fecb0c313a1d6e0003095b13a8779f30856ec000 100644 (file)
@@ -235,6 +235,9 @@ static struct conf_drv_settings default_conf = {
        }
 };
 
+static void __wl1271_op_remove_interface(struct wl1271 *wl);
+
+
 static void wl1271_device_release(struct device *dev)
 {
 
@@ -612,6 +615,26 @@ out:
        return ret;
 }
 
+static void wl1271_recovery_work(struct work_struct *work)
+{
+       struct wl1271 *wl =
+               container_of(work, struct wl1271, recovery_work);
+
+       mutex_lock(&wl->mutex);
+
+       if (wl->state != WL1271_STATE_ON)
+               goto out;
+
+       wl1271_info("Hardware recovery in progress.");
+
+       /* reboot the chipset */
+       __wl1271_op_remove_interface(wl);
+       ieee80211_restart_hw(wl->hw);
+
+out:
+       mutex_unlock(&wl->mutex);
+}
+
 static void wl1271_fw_wakeup(struct wl1271 *wl)
 {
        u32 elp_reg;
@@ -635,6 +658,7 @@ static int wl1271_setup(struct wl1271 *wl)
        INIT_WORK(&wl->irq_work, wl1271_irq_work);
        INIT_WORK(&wl->tx_work, wl1271_tx_work);
        INIT_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
+       INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
 
        return 0;
 }
@@ -793,11 +817,11 @@ out:
        mutex_unlock(&wl->mutex);
 
        cancel_work_sync(&wl->irq_work);
+       cancel_work_sync(&wl->recovery_work);
 
        return ret;
 }
 
-
 static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct wl1271 *wl = hw->priv;
@@ -1046,6 +1070,8 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
        WARN_ON(wl->vif != vif);
        __wl1271_op_remove_interface(wl);
        mutex_unlock(&wl->mutex);
+
+       cancel_work_sync(&wl->recovery_work);
 }
 
 static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
index 150dc674d8b4a467cd26fc17d44073243ebb83c2..e3c332e2f97c06646174e6661241f723e546b561 100644 (file)
@@ -64,7 +64,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl)
            test_bit(WL1271_FLAG_IDLE, &wl->flags)) {
                cancel_delayed_work(&wl->elp_work);
                ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
-                                       msecs_to_jiffies(ELP_ENTRY_DELAY));
+                                            msecs_to_jiffies(ELP_ENTRY_DELAY));
        }
 }
 
@@ -99,6 +99,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake)
                        &compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT));
                if (ret == 0) {
                        wl1271_error("ELP wakeup timeout!");
+                       ieee80211_queue_work(wl->hw, &wl->recovery_work);
                        ret = -ETIMEDOUT;
                        goto err;
                } else if (ret < 0) {