diff options
author | Sujith Manoharan <Sujith.Manoharan@atheros.com> | 2010-12-28 03:58:27 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-01-04 14:43:00 -0500 |
commit | 73908674c6957082e8ab57daed57d2bb97a1ebba (patch) | |
tree | ab265ea89903ad81486622ae9a71534f63dcd300 /drivers/net/wireless | |
parent | 66e3547431a8738416b508badfb9f326d11dabcc (diff) |
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>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/htc.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/htc_drv_init.c | 8 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/htc_drv_main.c | 57 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/wmi.c | 18 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/wmi.h | 3 |
5 files changed, 84 insertions, 8 deletions
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index e6c2f0aad28f..10622740dc76 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h | |||
@@ -377,7 +377,7 @@ struct ath9k_htc_priv { | |||
377 | struct ieee80211_vif *vif; | 377 | struct ieee80211_vif *vif; |
378 | struct htc_beacon_config cur_beacon_conf; | 378 | struct htc_beacon_config cur_beacon_conf; |
379 | unsigned int rxfilter; | 379 | unsigned int rxfilter; |
380 | struct tasklet_struct wmi_tasklet; | 380 | struct tasklet_struct swba_tasklet; |
381 | struct tasklet_struct rx_tasklet; | 381 | struct tasklet_struct rx_tasklet; |
382 | struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; | 382 | struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; |
383 | struct ath9k_htc_rx rx; | 383 | struct ath9k_htc_rx rx; |
@@ -385,6 +385,7 @@ struct ath9k_htc_priv { | |||
385 | struct sk_buff_head tx_queue; | 385 | struct sk_buff_head tx_queue; |
386 | struct delayed_work ath9k_ani_work; | 386 | struct delayed_work ath9k_ani_work; |
387 | struct work_struct ps_work; | 387 | struct work_struct ps_work; |
388 | struct work_struct fatal_work; | ||
388 | 389 | ||
389 | struct mutex htc_pm_lock; | 390 | struct mutex htc_pm_lock; |
390 | unsigned long ps_usecount; | 391 | unsigned long ps_usecount; |
@@ -419,6 +420,8 @@ static inline void ath_read_cachesize(struct ath_common *common, int *csz) | |||
419 | common->bus_ops->read_cachesize(common, csz); | 420 | common->bus_ops->read_cachesize(common, csz); |
420 | } | 421 | } |
421 | 422 | ||
423 | void ath9k_htc_reset(struct ath9k_htc_priv *priv); | ||
424 | |||
422 | void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv); | 425 | void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv); |
423 | void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, | 426 | void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, |
424 | struct ieee80211_vif *vif); | 427 | struct ieee80211_vif *vif); |
@@ -434,6 +437,7 @@ void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb, | |||
434 | void ath9k_htc_station_work(struct work_struct *work); | 437 | void ath9k_htc_station_work(struct work_struct *work); |
435 | void ath9k_htc_aggr_work(struct work_struct *work); | 438 | void ath9k_htc_aggr_work(struct work_struct *work); |
436 | void ath9k_ani_work(struct work_struct *work);; | 439 | void ath9k_ani_work(struct work_struct *work);; |
440 | void ath_start_ani(struct ath9k_htc_priv *priv); | ||
437 | 441 | ||
438 | int ath9k_tx_init(struct ath9k_htc_priv *priv); | 442 | int ath9k_tx_init(struct ath9k_htc_priv *priv); |
439 | void ath9k_tx_tasklet(unsigned long data); | 443 | void ath9k_tx_tasklet(unsigned long data); |
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 724f5451a415..9150ca665367 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c | |||
@@ -142,7 +142,7 @@ static void ath9k_deinit_priv(struct ath9k_htc_priv *priv) | |||
142 | { | 142 | { |
143 | ath9k_htc_exit_debug(priv->ah); | 143 | ath9k_htc_exit_debug(priv->ah); |
144 | ath9k_hw_deinit(priv->ah); | 144 | ath9k_hw_deinit(priv->ah); |
145 | tasklet_kill(&priv->wmi_tasklet); | 145 | tasklet_kill(&priv->swba_tasklet); |
146 | tasklet_kill(&priv->rx_tasklet); | 146 | tasklet_kill(&priv->rx_tasklet); |
147 | tasklet_kill(&priv->tx_tasklet); | 147 | tasklet_kill(&priv->tx_tasklet); |
148 | kfree(priv->ah); | 148 | kfree(priv->ah); |
@@ -647,13 +647,15 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, | |||
647 | spin_lock_init(&priv->tx_lock); | 647 | spin_lock_init(&priv->tx_lock); |
648 | mutex_init(&priv->mutex); | 648 | mutex_init(&priv->mutex); |
649 | mutex_init(&priv->htc_pm_lock); | 649 | mutex_init(&priv->htc_pm_lock); |
650 | tasklet_init(&priv->wmi_tasklet, ath9k_wmi_tasklet, | 650 | tasklet_init(&priv->swba_tasklet, ath9k_swba_tasklet, |
651 | (unsigned long)priv); | 651 | (unsigned long)priv); |
652 | tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet, | 652 | tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet, |
653 | (unsigned long)priv); | 653 | (unsigned long)priv); |
654 | tasklet_init(&priv->tx_tasklet, ath9k_tx_tasklet, (unsigned long)priv); | 654 | tasklet_init(&priv->tx_tasklet, ath9k_tx_tasklet, |
655 | (unsigned long)priv); | ||
655 | INIT_DELAYED_WORK(&priv->ath9k_ani_work, ath9k_ani_work); | 656 | INIT_DELAYED_WORK(&priv->ath9k_ani_work, ath9k_ani_work); |
656 | INIT_WORK(&priv->ps_work, ath9k_ps_work); | 657 | INIT_WORK(&priv->ps_work, ath9k_ps_work); |
658 | INIT_WORK(&priv->fatal_work, ath9k_fatal_work); | ||
657 | 659 | ||
658 | /* | 660 | /* |
659 | * Cache line size is used to size and align various | 661 | * Cache line size is used to size and align various |
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 5f75f70db5a7..07f10ddee6a5 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c | |||
@@ -116,6 +116,60 @@ void ath9k_ps_work(struct work_struct *work) | |||
116 | ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP); | 116 | ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP); |
117 | } | 117 | } |
118 | 118 | ||
119 | void ath9k_htc_reset(struct ath9k_htc_priv *priv) | ||
120 | { | ||
121 | struct ath_hw *ah = priv->ah; | ||
122 | struct ath_common *common = ath9k_hw_common(ah); | ||
123 | struct ieee80211_channel *channel = priv->hw->conf.channel; | ||
124 | struct ath9k_hw_cal_data *caldata; | ||
125 | enum htc_phymode mode; | ||
126 | __be16 htc_mode; | ||
127 | u8 cmd_rsp; | ||
128 | int ret; | ||
129 | |||
130 | mutex_lock(&priv->mutex); | ||
131 | ath9k_htc_ps_wakeup(priv); | ||
132 | |||
133 | if (priv->op_flags & OP_ASSOCIATED) | ||
134 | cancel_delayed_work_sync(&priv->ath9k_ani_work); | ||
135 | |||
136 | ieee80211_stop_queues(priv->hw); | ||
137 | htc_stop(priv->htc); | ||
138 | WMI_CMD(WMI_DISABLE_INTR_CMDID); | ||
139 | WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); | ||
140 | WMI_CMD(WMI_STOP_RECV_CMDID); | ||
141 | |||
142 | caldata = &priv->caldata[channel->hw_value]; | ||
143 | ret = ath9k_hw_reset(ah, ah->curchan, caldata, false); | ||
144 | if (ret) { | ||
145 | ath_err(common, | ||
146 | "Unable to reset device (%u Mhz) reset status %d\n", | ||
147 | channel->center_freq, ret); | ||
148 | } | ||
149 | |||
150 | ath_update_txpow(priv); | ||
151 | |||
152 | WMI_CMD(WMI_START_RECV_CMDID); | ||
153 | ath9k_host_rx_init(priv); | ||
154 | |||
155 | mode = ath9k_htc_get_curmode(priv, ah->curchan); | ||
156 | htc_mode = cpu_to_be16(mode); | ||
157 | WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); | ||
158 | |||
159 | WMI_CMD(WMI_ENABLE_INTR_CMDID); | ||
160 | htc_start(priv->htc); | ||
161 | |||
162 | if (priv->op_flags & OP_ASSOCIATED) { | ||
163 | ath9k_htc_beacon_config(priv, priv->vif); | ||
164 | ath_start_ani(priv); | ||
165 | } | ||
166 | |||
167 | ieee80211_wake_queues(priv->hw); | ||
168 | |||
169 | ath9k_htc_ps_restore(priv); | ||
170 | mutex_unlock(&priv->mutex); | ||
171 | } | ||
172 | |||
119 | static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, | 173 | static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, |
120 | struct ieee80211_hw *hw, | 174 | struct ieee80211_hw *hw, |
121 | struct ath9k_channel *hchan) | 175 | struct ath9k_channel *hchan) |
@@ -690,7 +744,7 @@ void ath9k_htc_debug_remove_root(void) | |||
690 | /* ANI */ | 744 | /* ANI */ |
691 | /*******/ | 745 | /*******/ |
692 | 746 | ||
693 | static void ath_start_ani(struct ath9k_htc_priv *priv) | 747 | void ath_start_ani(struct ath9k_htc_priv *priv) |
694 | { | 748 | { |
695 | struct ath_common *common = ath9k_hw_common(priv->ah); | 749 | struct ath_common *common = ath9k_hw_common(priv->ah); |
696 | unsigned long timestamp = jiffies_to_msecs(jiffies); | 750 | unsigned long timestamp = jiffies_to_msecs(jiffies); |
@@ -1219,6 +1273,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) | |||
1219 | u8 cmd_rsp; | 1273 | u8 cmd_rsp; |
1220 | 1274 | ||
1221 | /* Cancel all the running timers/work .. */ | 1275 | /* Cancel all the running timers/work .. */ |
1276 | cancel_work_sync(&priv->fatal_work); | ||
1222 | cancel_work_sync(&priv->ps_work); | 1277 | cancel_work_sync(&priv->ps_work); |
1223 | cancel_delayed_work_sync(&priv->ath9k_led_blink_work); | 1278 | cancel_delayed_work_sync(&priv->ath9k_led_blink_work); |
1224 | ath9k_led_stop_brightness(priv); | 1279 | ath9k_led_stop_brightness(priv); |
diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c index 573daca135fd..dc862f5e1162 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.c +++ b/drivers/net/wireless/ath/ath9k/wmi.c | |||
@@ -120,7 +120,7 @@ void ath9k_deinit_wmi(struct ath9k_htc_priv *priv) | |||
120 | kfree(priv->wmi); | 120 | kfree(priv->wmi); |
121 | } | 121 | } |
122 | 122 | ||
123 | void ath9k_wmi_tasklet(unsigned long data) | 123 | void ath9k_swba_tasklet(unsigned long data) |
124 | { | 124 | { |
125 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; | 125 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; |
126 | struct ath_common *common = ath9k_hw_common(priv->ah); | 126 | struct ath_common *common = ath9k_hw_common(priv->ah); |
@@ -131,6 +131,16 @@ void ath9k_wmi_tasklet(unsigned long data) | |||
131 | 131 | ||
132 | } | 132 | } |
133 | 133 | ||
134 | void ath9k_fatal_work(struct work_struct *work) | ||
135 | { | ||
136 | struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, | ||
137 | fatal_work); | ||
138 | struct ath_common *common = ath9k_hw_common(priv->ah); | ||
139 | |||
140 | ath_dbg(common, ATH_DBG_FATAL, "FATAL Event received, resetting device\n"); | ||
141 | ath9k_htc_reset(priv); | ||
142 | } | ||
143 | |||
134 | static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb) | 144 | static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb) |
135 | { | 145 | { |
136 | skb_pull(skb, sizeof(struct wmi_cmd_hdr)); | 146 | skb_pull(skb, sizeof(struct wmi_cmd_hdr)); |
@@ -163,7 +173,11 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb, | |||
163 | switch (cmd_id) { | 173 | switch (cmd_id) { |
164 | case WMI_SWBA_EVENTID: | 174 | case WMI_SWBA_EVENTID: |
165 | wmi->beacon_pending = *(u8 *)wmi_event; | 175 | wmi->beacon_pending = *(u8 *)wmi_event; |
166 | tasklet_schedule(&wmi->drv_priv->wmi_tasklet); | 176 | tasklet_schedule(&wmi->drv_priv->swba_tasklet); |
177 | break; | ||
178 | case WMI_FATAL_EVENTID: | ||
179 | ieee80211_queue_work(wmi->drv_priv->hw, | ||
180 | &wmi->drv_priv->fatal_work); | ||
167 | break; | 181 | break; |
168 | case WMI_TXRATE_EVENTID: | 182 | case WMI_TXRATE_EVENTID: |
169 | #ifdef CONFIG_ATH9K_HTC_DEBUGFS | 183 | #ifdef CONFIG_ATH9K_HTC_DEBUGFS |
diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h index ac61074af8ac..42084277522d 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.h +++ b/drivers/net/wireless/ath/ath9k/wmi.h | |||
@@ -117,7 +117,8 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, | |||
117 | u8 *cmd_buf, u32 cmd_len, | 117 | u8 *cmd_buf, u32 cmd_len, |
118 | u8 *rsp_buf, u32 rsp_len, | 118 | u8 *rsp_buf, u32 rsp_len, |
119 | u32 timeout); | 119 | u32 timeout); |
120 | void ath9k_wmi_tasklet(unsigned long data); | 120 | void ath9k_swba_tasklet(unsigned long data); |
121 | void ath9k_fatal_work(struct work_struct *work); | ||
121 | 122 | ||
122 | #define WMI_CMD(_wmi_cmd) \ | 123 | #define WMI_CMD(_wmi_cmd) \ |
123 | do { \ | 124 | do { \ |