ath9k_htc: Handle FATAL events
The device has to be reset when a FATAL event is received. Not doing so would leave the card in a non-working state. Signed-off-by: Sujith Manoharan <Sujith.Manoharan@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
committed by
John W. Linville
parent
66e3547431
commit
73908674c6
@@ -377,7 +377,7 @@ struct ath9k_htc_priv {
|
|||||||
struct ieee80211_vif *vif;
|
struct ieee80211_vif *vif;
|
||||||
struct htc_beacon_config cur_beacon_conf;
|
struct htc_beacon_config cur_beacon_conf;
|
||||||
unsigned int rxfilter;
|
unsigned int rxfilter;
|
||||||
struct tasklet_struct wmi_tasklet;
|
struct tasklet_struct swba_tasklet;
|
||||||
struct tasklet_struct rx_tasklet;
|
struct tasklet_struct rx_tasklet;
|
||||||
struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
|
struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
|
||||||
struct ath9k_htc_rx rx;
|
struct ath9k_htc_rx rx;
|
||||||
@@ -385,6 +385,7 @@ struct ath9k_htc_priv {
|
|||||||
struct sk_buff_head tx_queue;
|
struct sk_buff_head tx_queue;
|
||||||
struct delayed_work ath9k_ani_work;
|
struct delayed_work ath9k_ani_work;
|
||||||
struct work_struct ps_work;
|
struct work_struct ps_work;
|
||||||
|
struct work_struct fatal_work;
|
||||||
|
|
||||||
struct mutex htc_pm_lock;
|
struct mutex htc_pm_lock;
|
||||||
unsigned long ps_usecount;
|
unsigned long ps_usecount;
|
||||||
@@ -419,6 +420,8 @@ static inline void ath_read_cachesize(struct ath_common *common, int *csz)
|
|||||||
common->bus_ops->read_cachesize(common, csz);
|
common->bus_ops->read_cachesize(common, csz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ath9k_htc_reset(struct ath9k_htc_priv *priv);
|
||||||
|
|
||||||
void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv);
|
void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv);
|
||||||
void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
|
void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
|
||||||
struct ieee80211_vif *vif);
|
struct ieee80211_vif *vif);
|
||||||
@@ -434,6 +437,7 @@ void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb,
|
|||||||
void ath9k_htc_station_work(struct work_struct *work);
|
void ath9k_htc_station_work(struct work_struct *work);
|
||||||
void ath9k_htc_aggr_work(struct work_struct *work);
|
void ath9k_htc_aggr_work(struct work_struct *work);
|
||||||
void ath9k_ani_work(struct work_struct *work);;
|
void ath9k_ani_work(struct work_struct *work);;
|
||||||
|
void ath_start_ani(struct ath9k_htc_priv *priv);
|
||||||
|
|
||||||
int ath9k_tx_init(struct ath9k_htc_priv *priv);
|
int ath9k_tx_init(struct ath9k_htc_priv *priv);
|
||||||
void ath9k_tx_tasklet(unsigned long data);
|
void ath9k_tx_tasklet(unsigned long data);
|
||||||
|
@@ -142,7 +142,7 @@ static void ath9k_deinit_priv(struct ath9k_htc_priv *priv)
|
|||||||
{
|
{
|
||||||
ath9k_htc_exit_debug(priv->ah);
|
ath9k_htc_exit_debug(priv->ah);
|
||||||
ath9k_hw_deinit(priv->ah);
|
ath9k_hw_deinit(priv->ah);
|
||||||
tasklet_kill(&priv->wmi_tasklet);
|
tasklet_kill(&priv->swba_tasklet);
|
||||||
tasklet_kill(&priv->rx_tasklet);
|
tasklet_kill(&priv->rx_tasklet);
|
||||||
tasklet_kill(&priv->tx_tasklet);
|
tasklet_kill(&priv->tx_tasklet);
|
||||||
kfree(priv->ah);
|
kfree(priv->ah);
|
||||||
@@ -647,13 +647,15 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
|
|||||||
spin_lock_init(&priv->tx_lock);
|
spin_lock_init(&priv->tx_lock);
|
||||||
mutex_init(&priv->mutex);
|
mutex_init(&priv->mutex);
|
||||||
mutex_init(&priv->htc_pm_lock);
|
mutex_init(&priv->htc_pm_lock);
|
||||||
tasklet_init(&priv->wmi_tasklet, ath9k_wmi_tasklet,
|
tasklet_init(&priv->swba_tasklet, ath9k_swba_tasklet,
|
||||||
(unsigned long)priv);
|
(unsigned long)priv);
|
||||||
tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet,
|
tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet,
|
||||||
(unsigned long)priv);
|
(unsigned long)priv);
|
||||||
tasklet_init(&priv->tx_tasklet, ath9k_tx_tasklet, (unsigned long)priv);
|
tasklet_init(&priv->tx_tasklet, ath9k_tx_tasklet,
|
||||||
|
(unsigned long)priv);
|
||||||
INIT_DELAYED_WORK(&priv->ath9k_ani_work, ath9k_ani_work);
|
INIT_DELAYED_WORK(&priv->ath9k_ani_work, ath9k_ani_work);
|
||||||
INIT_WORK(&priv->ps_work, ath9k_ps_work);
|
INIT_WORK(&priv->ps_work, ath9k_ps_work);
|
||||||
|
INIT_WORK(&priv->fatal_work, ath9k_fatal_work);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cache line size is used to size and align various
|
* Cache line size is used to size and align various
|
||||||
|
@@ -116,6 +116,60 @@ void ath9k_ps_work(struct work_struct *work)
|
|||||||
ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
|
ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ath9k_htc_reset(struct ath9k_htc_priv *priv)
|
||||||
|
{
|
||||||
|
struct ath_hw *ah = priv->ah;
|
||||||
|
struct ath_common *common = ath9k_hw_common(ah);
|
||||||
|
struct ieee80211_channel *channel = priv->hw->conf.channel;
|
||||||
|
struct ath9k_hw_cal_data *caldata;
|
||||||
|
enum htc_phymode mode;
|
||||||
|
__be16 htc_mode;
|
||||||
|
u8 cmd_rsp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&priv->mutex);
|
||||||
|
ath9k_htc_ps_wakeup(priv);
|
||||||
|
|
||||||
|
if (priv->op_flags & OP_ASSOCIATED)
|
||||||
|
cancel_delayed_work_sync(&priv->ath9k_ani_work);
|
||||||
|
|
||||||
|
ieee80211_stop_queues(priv->hw);
|
||||||
|
htc_stop(priv->htc);
|
||||||
|
WMI_CMD(WMI_DISABLE_INTR_CMDID);
|
||||||
|
WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
|
||||||
|
WMI_CMD(WMI_STOP_RECV_CMDID);
|
||||||
|
|
||||||
|
caldata = &priv->caldata[channel->hw_value];
|
||||||
|
ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
|
||||||
|
if (ret) {
|
||||||
|
ath_err(common,
|
||||||
|
"Unable to reset device (%u Mhz) reset status %d\n",
|
||||||
|
channel->center_freq, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
ath_update_txpow(priv);
|
||||||
|
|
||||||
|
WMI_CMD(WMI_START_RECV_CMDID);
|
||||||
|
ath9k_host_rx_init(priv);
|
||||||
|
|
||||||
|
mode = ath9k_htc_get_curmode(priv, ah->curchan);
|
||||||
|
htc_mode = cpu_to_be16(mode);
|
||||||
|
WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
|
||||||
|
|
||||||
|
WMI_CMD(WMI_ENABLE_INTR_CMDID);
|
||||||
|
htc_start(priv->htc);
|
||||||
|
|
||||||
|
if (priv->op_flags & OP_ASSOCIATED) {
|
||||||
|
ath9k_htc_beacon_config(priv, priv->vif);
|
||||||
|
ath_start_ani(priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
ieee80211_wake_queues(priv->hw);
|
||||||
|
|
||||||
|
ath9k_htc_ps_restore(priv);
|
||||||
|
mutex_unlock(&priv->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
|
static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
|
||||||
struct ieee80211_hw *hw,
|
struct ieee80211_hw *hw,
|
||||||
struct ath9k_channel *hchan)
|
struct ath9k_channel *hchan)
|
||||||
@@ -690,7 +744,7 @@ void ath9k_htc_debug_remove_root(void)
|
|||||||
/* ANI */
|
/* ANI */
|
||||||
/*******/
|
/*******/
|
||||||
|
|
||||||
static void ath_start_ani(struct ath9k_htc_priv *priv)
|
void ath_start_ani(struct ath9k_htc_priv *priv)
|
||||||
{
|
{
|
||||||
struct ath_common *common = ath9k_hw_common(priv->ah);
|
struct ath_common *common = ath9k_hw_common(priv->ah);
|
||||||
unsigned long timestamp = jiffies_to_msecs(jiffies);
|
unsigned long timestamp = jiffies_to_msecs(jiffies);
|
||||||
@@ -1219,6 +1273,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
|
|||||||
u8 cmd_rsp;
|
u8 cmd_rsp;
|
||||||
|
|
||||||
/* Cancel all the running timers/work .. */
|
/* Cancel all the running timers/work .. */
|
||||||
|
cancel_work_sync(&priv->fatal_work);
|
||||||
cancel_work_sync(&priv->ps_work);
|
cancel_work_sync(&priv->ps_work);
|
||||||
cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
|
cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
|
||||||
ath9k_led_stop_brightness(priv);
|
ath9k_led_stop_brightness(priv);
|
||||||
|
@@ -120,7 +120,7 @@ void ath9k_deinit_wmi(struct ath9k_htc_priv *priv)
|
|||||||
kfree(priv->wmi);
|
kfree(priv->wmi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ath9k_wmi_tasklet(unsigned long data)
|
void ath9k_swba_tasklet(unsigned long data)
|
||||||
{
|
{
|
||||||
struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data;
|
struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data;
|
||||||
struct ath_common *common = ath9k_hw_common(priv->ah);
|
struct ath_common *common = ath9k_hw_common(priv->ah);
|
||||||
@@ -131,6 +131,16 @@ void ath9k_wmi_tasklet(unsigned long data)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ath9k_fatal_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv,
|
||||||
|
fatal_work);
|
||||||
|
struct ath_common *common = ath9k_hw_common(priv->ah);
|
||||||
|
|
||||||
|
ath_dbg(common, ATH_DBG_FATAL, "FATAL Event received, resetting device\n");
|
||||||
|
ath9k_htc_reset(priv);
|
||||||
|
}
|
||||||
|
|
||||||
static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb)
|
static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
skb_pull(skb, sizeof(struct wmi_cmd_hdr));
|
skb_pull(skb, sizeof(struct wmi_cmd_hdr));
|
||||||
@@ -163,7 +173,11 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb,
|
|||||||
switch (cmd_id) {
|
switch (cmd_id) {
|
||||||
case WMI_SWBA_EVENTID:
|
case WMI_SWBA_EVENTID:
|
||||||
wmi->beacon_pending = *(u8 *)wmi_event;
|
wmi->beacon_pending = *(u8 *)wmi_event;
|
||||||
tasklet_schedule(&wmi->drv_priv->wmi_tasklet);
|
tasklet_schedule(&wmi->drv_priv->swba_tasklet);
|
||||||
|
break;
|
||||||
|
case WMI_FATAL_EVENTID:
|
||||||
|
ieee80211_queue_work(wmi->drv_priv->hw,
|
||||||
|
&wmi->drv_priv->fatal_work);
|
||||||
break;
|
break;
|
||||||
case WMI_TXRATE_EVENTID:
|
case WMI_TXRATE_EVENTID:
|
||||||
#ifdef CONFIG_ATH9K_HTC_DEBUGFS
|
#ifdef CONFIG_ATH9K_HTC_DEBUGFS
|
||||||
|
@@ -117,7 +117,8 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
|
|||||||
u8 *cmd_buf, u32 cmd_len,
|
u8 *cmd_buf, u32 cmd_len,
|
||||||
u8 *rsp_buf, u32 rsp_len,
|
u8 *rsp_buf, u32 rsp_len,
|
||||||
u32 timeout);
|
u32 timeout);
|
||||||
void ath9k_wmi_tasklet(unsigned long data);
|
void ath9k_swba_tasklet(unsigned long data);
|
||||||
|
void ath9k_fatal_work(struct work_struct *work);
|
||||||
|
|
||||||
#define WMI_CMD(_wmi_cmd) \
|
#define WMI_CMD(_wmi_cmd) \
|
||||||
do { \
|
do { \
|
||||||
|
Reference in New Issue
Block a user