/* firmware access */
struct mutex fw_mutex;
struct task_struct *fw_mutex_owner;
+ struct task_struct *hw_restart_owner;
int fw_mutex_depth;
struct completion *hostcmd_wait;
*/
struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_WMM_QUEUES];
+ /* To perform the task of reloading the firmware */
+ struct work_struct fw_reload;
+ bool hw_restart_in_progress;
+
/* async firmware loading state */
unsigned fw_state;
char *fw_pref;
might_sleep();
+ /* Since fw restart is in progress, allow only the firmware
+ * commands from the restart code and block the other
+ * commands since they are going to fail in any case since
+ * the firmware has crashed
+ */
+ if (priv->hw_restart_in_progress) {
+ if (priv->hw_restart_owner == current)
+ return 0;
+ else
+ return -EBUSY;
+ }
+
/*
* The TX queues are stopped at this point, so this test
* doesn't need to take ->tx_lock.
wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n",
MWL8K_TX_WAIT_TIMEOUT_MS);
mwl8k_dump_tx_rings(hw);
+ priv->hw_restart_in_progress = true;
+ ieee80211_queue_work(hw, &priv->fw_reload);
rc = -ETIMEDOUT;
}
rc = mwl8k_tx_wait_empty(hw);
if (rc) {
- ieee80211_wake_queues(hw);
+ if (!priv->hw_restart_in_progress)
+ ieee80211_wake_queues(hw);
+
mutex_unlock(&priv->fw_mutex);
return rc;
struct mwl8k_priv *priv = hw->priv;
if (!--priv->fw_mutex_depth) {
- ieee80211_wake_queues(hw);
+ if (!priv->hw_restart_in_progress)
+ ieee80211_wake_queues(hw);
+
priv->fw_mutex_owner = NULL;
mutex_unlock(&priv->fw_mutex);
}
struct mwl8k_priv *priv = hw->priv;
int i;
- mwl8k_cmd_radio_disable(hw);
+ if (!priv->hw_restart_in_progress)
+ mwl8k_cmd_radio_disable(hw);
ieee80211_stop_queues(hw);
return 0;
}
+static void mwl8k_remove_vif(struct mwl8k_priv *priv, struct mwl8k_vif *vif)
+{
+ /* Has ieee80211_restart_hw re-added the removed interfaces? */
+ if (!priv->macids_used)
+ return;
+
+ priv->macids_used &= ~(1 << vif->macid);
+ list_del(&vif->list);
+}
+
static void mwl8k_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
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);
+ mwl8k_remove_vif(priv, mwl8k_vif);
+}
+
+static void mwl8k_hw_restart_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;
+ int rc;
+
+ /* If some command is waiting for a response, clear it */
+ if (priv->hostcmd_wait != NULL) {
+ complete(priv->hostcmd_wait);
+ priv->hostcmd_wait = NULL;
+ }
+
+ priv->hw_restart_owner = current;
+ di = priv->device_info;
+ mwl8k_fw_lock(hw);
+
+ if (priv->ap_fw)
+ rc = mwl8k_reload_firmware(hw, di->fw_image_ap);
+ else
+ rc = mwl8k_reload_firmware(hw, di->fw_image_sta);
+
+ if (rc)
+ goto fail;
+
+ priv->hw_restart_owner = NULL;
+ priv->hw_restart_in_progress = false;
+
+ /*
+ * This unlock will wake up the queues and
+ * also opens the command path for other
+ * commands
+ */
+ mwl8k_fw_unlock(hw);
+
+ ieee80211_restart_hw(hw);
+
+ wiphy_err(hw->wiphy, "Firmware restarted successfully\n");
+
+ return;
+fail:
+ mwl8k_fw_unlock(hw);
+
+ wiphy_err(hw->wiphy, "Firmware restart failed\n");
}
static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) {
rc = mwl8k_check_ba(hw, stream);
- if (!rc)
+ /* If HW restart is in progress mwl8k_post_cmd will
+ * return -EBUSY. Avoid retrying mwl8k_check_ba in
+ * such cases
+ */
+ if (!rc || rc == -EBUSY)
break;
/*
* HW queues take time to be flushed, give them
mwl8k_release_firmware(priv);
}
+#define MAX_RESTART_ATTEMPTS 1
static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,
bool nowait)
{
struct mwl8k_priv *priv = hw->priv;
int rc;
+ int count = MAX_RESTART_ATTEMPTS;
+retry:
/* Reset firmware and hardware */
mwl8k_hw_reset(priv);
/* Reclaim memory once firmware is successfully loaded */
mwl8k_release_firmware(priv);
+ if (rc && count) {
+ /* FW did not start successfully;
+ * lets try one more time
+ */
+ count--;
+ wiphy_err(hw->wiphy, "Trying to reload the firmware again\n");
+ msleep(20);
+ goto retry;
+ }
+
return rc;
}
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 (!priv->hw_restart_in_progress)
+ memset(priv->ampdu, 0, sizeof(priv->ampdu));
/*
* Temporarily enable interrupts. Initial firmware host
{
int i, rc = 0;
struct mwl8k_priv *priv = hw->priv;
+ struct mwl8k_vif *vif, *tmp_vif;
mwl8k_stop(hw);
mwl8k_rxq_deinit(hw, 0);
+ /*
+ * All the existing interfaces are re-added by the ieee80211_reconfig;
+ * which means driver should remove existing interfaces before calling
+ * ieee80211_restart_hw
+ */
+ if (priv->hw_restart_in_progress)
+ list_for_each_entry_safe(vif, tmp_vif, &priv->vif_list, list)
+ mwl8k_remove_vif(priv, vif);
+
for (i = 0; i < mwl8k_tx_queues(priv); i++)
mwl8k_txq_deinit(hw, i);
if (rc)
goto fail;
+ if (priv->hw_restart_in_progress)
+ return rc;
+
rc = mwl8k_start(hw);
if (rc)
goto fail;
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_hw_restart_work);
/* TX reclaim and RX tasklets. */
tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw);
rc = mwl8k_init_firmware(hw, priv->fw_pref, true);
if (rc)
goto err_stop_firmware;
+
+ priv->hw_restart_in_progress = false;
+
return rc;
err_stop_firmware: