Search Linux Wireless

[PATCH 1/2] mwl8k: Recover from firmware crash

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

 



In case of firmware crash, reload the firmware and reconfigure it
by triggering ieee80211_hw_restart; mac80211 utility function.

Signed-off-by: Nishant Sarmukadam <nishants@xxxxxxxxxxx>
Signed-off-by: Yogesh Ashok Powar <yogeshp@xxxxxxxxxxx>
---
 drivers/net/wireless/mwl8k.c |  122 ++++++++++++++++++++++++++++++++++++++---
 1 files changed, 113 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index 901cd79..241216c 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -262,6 +262,11 @@ struct mwl8k_priv {
 	 */
 	struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_WMM_QUEUES];
 
+	/* To do the task of reloading the firmware */
+	struct work_struct fw_reload;
+
+	atomic_t  hw_reload_state;
+
 	/* async firmware loading state */
 	unsigned fw_state;
 	char *fw_pref;
@@ -485,6 +490,14 @@ enum {
 	FW_STATE_ERROR,
 };
 
+/* FW reload states */
+enum {
+	FW_RELOAD_INIT = 0,
+	FW_RELOAD_TEST,
+	FW_RELOAD_ALL_BLOCK,
+	FW_RELOAD_FINAL,
+};
+
 /* Request fw image */
 static int mwl8k_request_fw(struct mwl8k_priv *priv,
 			    const char *fname, const struct firmware **fw,
@@ -1516,6 +1529,9 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
 
 		oldcount = priv->pending_tx_pkts;
 
+		if (!atomic_read(&priv->hw_reload_state))
+			atomic_set(&priv->hw_reload_state, FW_RELOAD_TEST);
+
 		spin_unlock_bh(&priv->tx_lock);
 		timeout = wait_for_completion_timeout(&tx_wait,
 			    msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS));
@@ -1540,7 +1556,12 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
 
 		wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n",
 			  MWL8K_TX_WAIT_TIMEOUT_MS);
-		mwl8k_dump_tx_rings(hw);
+
+		if (atomic_read(&priv->hw_reload_state) == FW_RELOAD_TEST) {
+			mwl8k_dump_tx_rings(hw);
+			atomic_set(&priv->hw_reload_state, FW_RELOAD_ALL_BLOCK);
+			ieee80211_queue_work(hw, &priv->fw_reload);
+		}
 
 		rc = -ETIMEDOUT;
 	}
@@ -1626,6 +1647,8 @@ mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)
 		BUG_ON(txq->len == 0);
 		txq->len--;
 		priv->pending_tx_pkts--;
+		if (atomic_read(&priv->hw_reload_state) == FW_RELOAD_TEST)
+			atomic_set(&priv->hw_reload_state, FW_RELOAD_INIT);
 
 		addr = le32_to_cpu(tx_desc->pkt_phys_addr);
 		size = le16_to_cpu(tx_desc->pkt_len);
@@ -1827,6 +1850,16 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
 	bool mgmtframe = false;
 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
 
+	if (atomic_read(&priv->hw_reload_state) > FW_RELOAD_TEST) {
+		/*
+		 * If fw reload is going on there is no point
+		 * in sending the packets down to the firmware.
+		 * Free the packets
+		 */
+		dev_kfree_skb(skb);
+		return;
+	}
+
 	wh = (struct ieee80211_hdr *)skb->data;
 	if (ieee80211_is_data_qos(wh->frame_control))
 		qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh)));
@@ -2102,6 +2135,13 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
 	unsigned long timeout = 0;
 	u8 buf[32];
 
+	if (atomic_read(&priv->hw_reload_state) == FW_RELOAD_ALL_BLOCK) {
+		wiphy_err(hw->wiphy, "Not executing command %s since "
+				"firmware reload is in progress\n",
+				mwl8k_cmd_name(cmd->code, buf, sizeof(buf)));
+		return -EBUSY;
+	}
+
 	cmd->result = (__force __le16) 0xffff;
 	dma_size = le16_to_cpu(cmd->length);
 	dma_addr = pci_map_single(priv->pdev, cmd, dma_size,
@@ -4382,6 +4422,8 @@ static int mwl8k_start(struct ieee80211_hw *hw)
 		mwl8k_fw_unlock(hw);
 	}
 
+	ieee80211_wake_queues(hw);
+
 	if (rc) {
 		iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
 		free_irq(priv->pdev->irq, hw);
@@ -4510,8 +4552,10 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw,
 
 	mwl8k_cmd_set_mac_addr(hw, vif, "\x00\x00\x00\x00\x00\x00");
 
-	priv->macids_used &= ~(1 << mwl8k_vif->macid);
-	list_del(&mwl8k_vif->list);
+	if (priv->macids_used) {
+		priv->macids_used &= ~(1 << mwl8k_vif->macid);
+		list_del(&mwl8k_vif->list);
+	}
 }
 
 static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
@@ -5261,6 +5305,29 @@ fail:
 	complete(&priv->firmware_loading_complete);
 	device_release_driver(&priv->pdev->dev);
 	mwl8k_release_firmware(priv);
+	atomic_set(&priv->hw_reload_state, FW_RELOAD_INIT);
+}
+
+static void  mwl8k_reload_fw_work(struct work_struct *work)
+{
+	struct mwl8k_priv *priv =
+			container_of(work, struct mwl8k_priv, fw_reload);
+
+	struct ieee80211_hw *hw = priv->hw;
+	struct mwl8k_device_info *di;
+
+	di = priv->device_info;
+
+	/* If some command is waiting for a response, clear it */
+	if (priv->hostcmd_wait != NULL)
+		complete(priv->hostcmd_wait);
+
+	if (mwl8k_reload_firmware(hw, di->fw_image_ap))
+		wiphy_err(hw->wiphy, "Firmware reloading failed\n");
+	else
+		wiphy_err(hw->wiphy, "Firmware restarted successfully\n");
+
+	return;
 }
 
 static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,
@@ -5268,6 +5335,8 @@ static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,
 {
 	struct mwl8k_priv *priv = hw->priv;
 	int rc;
+#define MAX_RESATRT_ATTEMEPT_COUNT	5
+	static int restart_count;
 
 	/* Reset firmware and hardware */
 	mwl8k_hw_reset(priv);
@@ -5290,6 +5359,23 @@ static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,
 	/* Reclaim memory once firmware is successfully loaded */
 	mwl8k_release_firmware(priv);
 
+	if (atomic_read(&priv->hw_reload_state) == FW_RELOAD_ALL_BLOCK) {
+		if (rc) {
+			/* FW did not start successfully;
+			 * lets try it some more time
+			 */
+			if (++restart_count < MAX_RESATRT_ATTEMEPT_COUNT) {
+				wiphy_err(hw->wiphy, "Re-trying %d time\n",
+								restart_count);
+				msleep(20);
+				rc = mwl8k_init_firmware(hw, fw_image, nowait);
+			}
+		} else {
+			restart_count = 0;
+			atomic_set(&priv->hw_reload_state, FW_RELOAD_FINAL);
+		}
+	}
+
 	return rc;
 }
 
@@ -5365,7 +5451,14 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw)
 		goto err_free_queues;
 	}
 
-	memset(priv->ampdu, 0, sizeof(priv->ampdu));
+	/*
+	 * When hw restart is requested,
+	 * mac80211 will take care of clearing
+	 * the ampdu streams, so do not clear
+	 * the ampdu state here
+	 */
+	if (atomic_read(&priv->hw_reload_state) != FW_RELOAD_FINAL)
+		memset(priv->ampdu, 0, sizeof(priv->ampdu));
 
 	/*
 	 * Temporarily enable interrupts.  Initial firmware host
@@ -5431,18 +5524,20 @@ err_stop_firmware:
 	return rc;
 }
 
-/*
- * invoke mwl8k_reload_firmware to change the firmware image after the device
- * has already been registered
- */
 static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
 {
 	int i, rc = 0;
 	struct mwl8k_priv *priv = hw->priv;
+	struct mwl8k_vif *mwl8k_vif, *tmp_vif;
 
 	mwl8k_stop(hw);
 	mwl8k_rxq_deinit(hw, 0);
 
+	list_for_each_entry_safe(mwl8k_vif, tmp_vif, &priv->vif_list, list) {
+		priv->macids_used &= ~(1 << mwl8k_vif->macid);
+		list_del(&mwl8k_vif->list);
+	}
+
 	for (i = 0; i < mwl8k_tx_queues(priv); i++)
 		mwl8k_txq_deinit(hw, i);
 
@@ -5454,6 +5549,12 @@ static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
 	if (rc)
 		goto fail;
 
+	if (atomic_read(&priv->hw_reload_state) == FW_RELOAD_FINAL) {
+		ieee80211_restart_hw(hw);
+		atomic_set(&priv->hw_reload_state, FW_RELOAD_INIT);
+		return 0;
+	}
+
 	rc = mwl8k_start(hw);
 	if (rc)
 		goto fail;
@@ -5472,6 +5573,8 @@ static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
 
 fail:
 	printk(KERN_WARNING "mwl8k: Failed to reload firmware image.\n");
+	atomic_set(&priv->hw_reload_state, FW_RELOAD_ALL_BLOCK);
+
 	return rc;
 }
 
@@ -5524,6 +5627,8 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
 	INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
 	/* Handle watchdog ba events */
 	INIT_WORK(&priv->watchdog_ba_handle, mwl8k_watchdog_ba_events);
+	/* To reload the firmware if it crashes */
+	INIT_WORK(&priv->fw_reload, mwl8k_reload_fw_work);
 
 	/* TX reclaim and RX tasklets.  */
 	tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw);
@@ -5624,7 +5729,6 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
 	priv->pdev = pdev;
 	priv->device_info = &mwl8k_info_tbl[id->driver_data];
 
-
 	priv->sram = pci_iomap(pdev, 0, 0x10000);
 	if (priv->sram == NULL) {
 		wiphy_err(hw->wiphy, "Cannot map device SRAM\n");
-- 
1.7.5.4

--
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