Search Linux Wireless

[PATCH 22/25] wl1271: Add hardware recovery mechanism

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Juuso Oikarinen <juuso.oikarinen@xxxxxxxxx>

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@xxxxxxxxx>
Reviewed-by: Teemu Paasikivi <ext-teemu.3.paasikivi@xxxxxxxxx>
Signed-off-by: Luciano Coelho <luciano.coelho@xxxxxxxxx>
---
 drivers/net/wireless/wl12xx/wl1271.h      |    3 +++
 drivers/net/wireless/wl12xx/wl1271_cmd.c  |    5 ++++-
 drivers/net/wireless/wl12xx/wl1271_main.c |   28 +++++++++++++++++++++++++++-
 drivers/net/wireless/wl12xx/wl1271_ps.c   |    3 ++-
 4 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 55ec656..b5369a6 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -410,6 +410,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;
 
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c
index 06b14f2..170b5a8 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.c
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c
@@ -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);
 
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 00da982..6e98c75 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -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)
diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.c b/drivers/net/wireless/wl12xx/wl1271_ps.c
index 150dc67..e3c332e 100644
--- a/drivers/net/wireless/wl12xx/wl1271_ps.c
+++ b/drivers/net/wireless/wl12xx/wl1271_ps.c
@@ -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) {
-- 
1.6.3.3

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux