aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorSujith Manoharan <Sujith.Manoharan@atheros.com>2011-04-13 01:53:52 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-04-13 15:22:33 -0400
commitf4c88991f51e097b6541f998fd23d477999e5886 (patch)
tree5cc4be2cb3296f50402793e209012875dfa13f95 /drivers/net
parentb0a6ba983e3663bf256ca2e79d17bb846878cd9e (diff)
ath9k_htc: Queue WMI events
Use a queue to handle WMI events and schedule a tasklet to process the events. This fixes the race between the WMI event ISR and the SWBA tasklet when the arrival of WMI events in quick succession could overwrite the SWBA data before the tasklet from a previous iteration could have been scheduled. Also, drain the WMI queue properly. Signed-off-by: Sujith Manoharan <Sujith.Manoharan@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/ath/ath9k/htc.h3
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_beacon.c27
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_gpio.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_init.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_main.c7
-rw-r--r--drivers/net/wireless/ath/ath9k/wmi.c97
-rw-r--r--drivers/net/wireless/ath/ath9k/wmi.h7
7 files changed, 89 insertions, 57 deletions
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 133fc6dc392..20511af33f5 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -497,7 +497,8 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv);
497void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, 497void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
498 struct ieee80211_vif *vif); 498 struct ieee80211_vif *vif);
499void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv); 499void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv);
500void ath9k_htc_swba(struct ath9k_htc_priv *priv); 500void ath9k_htc_swba(struct ath9k_htc_priv *priv,
501 struct wmi_event_swba *swba);
501 502
502void ath9k_htc_rxep(void *priv, struct sk_buff *skb, 503void ath9k_htc_rxep(void *priv, struct sk_buff *skb,
503 enum htc_endpoint_id ep_id); 504 enum htc_endpoint_id ep_id);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
index 48bc28823f6..2180a9da380 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
@@ -401,10 +401,10 @@ static void ath9k_htc_send_beacon(struct ath9k_htc_priv *priv,
401 spin_unlock_bh(&priv->beacon_lock); 401 spin_unlock_bh(&priv->beacon_lock);
402} 402}
403 403
404static int ath9k_htc_choose_bslot(struct ath9k_htc_priv *priv) 404static int ath9k_htc_choose_bslot(struct ath9k_htc_priv *priv,
405 struct wmi_event_swba *swba)
405{ 406{
406 struct ath_common *common = ath9k_hw_common(priv->ah); 407 struct ath_common *common = ath9k_hw_common(priv->ah);
407 unsigned long flags;
408 u64 tsf; 408 u64 tsf;
409 u32 tsftu; 409 u32 tsftu;
410 u16 intval; 410 u16 intval;
@@ -412,10 +412,7 @@ static int ath9k_htc_choose_bslot(struct ath9k_htc_priv *priv)
412 412
413 intval = priv->cur_beacon_conf.beacon_interval & ATH9K_BEACON_PERIOD; 413 intval = priv->cur_beacon_conf.beacon_interval & ATH9K_BEACON_PERIOD;
414 414
415 spin_lock_irqsave(&priv->wmi->wmi_lock, flags); 415 tsf = be64_to_cpu(swba->tsf);
416 tsf = priv->wmi->tsf;
417 spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags);
418
419 tsftu = TSF_TO_TU(tsf >> 32, tsf); 416 tsftu = TSF_TO_TU(tsf >> 32, tsf);
420 slot = ((tsftu % intval) * ATH9K_HTC_MAX_BCN_VIF) / intval; 417 slot = ((tsftu % intval) * ATH9K_HTC_MAX_BCN_VIF) / intval;
421 slot = ATH9K_HTC_MAX_BCN_VIF - slot - 1; 418 slot = ATH9K_HTC_MAX_BCN_VIF - slot - 1;
@@ -427,33 +424,31 @@ static int ath9k_htc_choose_bslot(struct ath9k_htc_priv *priv)
427 return slot; 424 return slot;
428} 425}
429 426
430void ath9k_htc_swba(struct ath9k_htc_priv *priv) 427void ath9k_htc_swba(struct ath9k_htc_priv *priv,
428 struct wmi_event_swba *swba)
431{ 429{
432 struct ath_common *common = ath9k_hw_common(priv->ah); 430 struct ath_common *common = ath9k_hw_common(priv->ah);
433 unsigned long flags;
434 int slot; 431 int slot;
435 432
436 spin_lock_irqsave(&priv->wmi->wmi_lock, flags); 433 if (swba->beacon_pending != 0) {
437 if (priv->wmi->beacon_pending != 0) {
438 spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags);
439 priv->cur_beacon_conf.bmiss_cnt++; 434 priv->cur_beacon_conf.bmiss_cnt++;
440 if (priv->cur_beacon_conf.bmiss_cnt > BSTUCK_THRESHOLD) { 435 if (priv->cur_beacon_conf.bmiss_cnt > BSTUCK_THRESHOLD) {
441 ath_dbg(common, ATH_DBG_BEACON, 436 ath_dbg(common, ATH_DBG_BSTUCK,
442 "Beacon stuck, HW reset\n"); 437 "Beacon stuck, HW reset\n");
443 ath9k_htc_reset(priv); 438 ieee80211_queue_work(priv->hw,
439 &priv->fatal_work);
444 } 440 }
445 return; 441 return;
446 } 442 }
447 spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags);
448 443
449 if (priv->cur_beacon_conf.bmiss_cnt) { 444 if (priv->cur_beacon_conf.bmiss_cnt) {
450 ath_dbg(common, ATH_DBG_BEACON, 445 ath_dbg(common, ATH_DBG_BSTUCK,
451 "Resuming beacon xmit after %u misses\n", 446 "Resuming beacon xmit after %u misses\n",
452 priv->cur_beacon_conf.bmiss_cnt); 447 priv->cur_beacon_conf.bmiss_cnt);
453 priv->cur_beacon_conf.bmiss_cnt = 0; 448 priv->cur_beacon_conf.bmiss_cnt = 0;
454 } 449 }
455 450
456 slot = ath9k_htc_choose_bslot(priv); 451 slot = ath9k_htc_choose_bslot(priv, swba);
457 spin_lock_bh(&priv->beacon_lock); 452 spin_lock_bh(&priv->beacon_lock);
458 if (priv->cur_beacon_conf.bslot[slot] == NULL) { 453 if (priv->cur_beacon_conf.bslot[slot] == NULL) {
459 spin_unlock_bh(&priv->beacon_lock); 454 spin_unlock_bh(&priv->beacon_lock);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
index 7e630a81b45..459ba0d36f4 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
@@ -436,6 +436,9 @@ void ath9k_htc_radio_disable(struct ieee80211_hw *hw)
436 /* Stop RX */ 436 /* Stop RX */
437 WMI_CMD(WMI_STOP_RECV_CMDID); 437 WMI_CMD(WMI_STOP_RECV_CMDID);
438 438
439 /* Clear the WMI event queue */
440 ath9k_wmi_event_drain(priv);
441
439 /* 442 /*
440 * The MIB counters have to be disabled here, 443 * The MIB counters have to be disabled here,
441 * since the target doesn't do it. 444 * since the target doesn't do it.
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index b1c68bff72a..921d76f3201 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -676,8 +676,6 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
676 spin_lock_init(&priv->tx_lock); 676 spin_lock_init(&priv->tx_lock);
677 mutex_init(&priv->mutex); 677 mutex_init(&priv->mutex);
678 mutex_init(&priv->htc_pm_lock); 678 mutex_init(&priv->htc_pm_lock);
679 tasklet_init(&priv->swba_tasklet, ath9k_swba_tasklet,
680 (unsigned long)priv);
681 tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet, 679 tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet,
682 (unsigned long)priv); 680 (unsigned long)priv);
683 tasklet_init(&priv->tx_tasklet, ath9k_tx_tasklet, 681 tasklet_init(&priv->tx_tasklet, ath9k_tx_tasklet,
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index 8f38075d124..81dfe0782f7 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -202,6 +202,8 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv)
202 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); 202 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
203 WMI_CMD(WMI_STOP_RECV_CMDID); 203 WMI_CMD(WMI_STOP_RECV_CMDID);
204 204
205 ath9k_wmi_event_drain(priv);
206
205 caldata = &priv->caldata; 207 caldata = &priv->caldata;
206 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false); 208 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
207 if (ret) { 209 if (ret) {
@@ -255,6 +257,8 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
255 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); 257 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
256 WMI_CMD(WMI_STOP_RECV_CMDID); 258 WMI_CMD(WMI_STOP_RECV_CMDID);
257 259
260 ath9k_wmi_event_drain(priv);
261
258 ath_dbg(common, ATH_DBG_CONFIG, 262 ath_dbg(common, ATH_DBG_CONFIG,
259 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n", 263 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
260 priv->ah->curchan->channel, 264 priv->ah->curchan->channel,
@@ -1172,12 +1176,13 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
1172 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); 1176 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1173 WMI_CMD(WMI_STOP_RECV_CMDID); 1177 WMI_CMD(WMI_STOP_RECV_CMDID);
1174 1178
1175 tasklet_kill(&priv->swba_tasklet);
1176 tasklet_kill(&priv->rx_tasklet); 1179 tasklet_kill(&priv->rx_tasklet);
1177 tasklet_kill(&priv->tx_tasklet); 1180 tasklet_kill(&priv->tx_tasklet);
1178 1181
1179 skb_queue_purge(&priv->tx_queue); 1182 skb_queue_purge(&priv->tx_queue);
1180 1183
1184 ath9k_wmi_event_drain(priv);
1185
1181 mutex_unlock(&priv->mutex); 1186 mutex_unlock(&priv->mutex);
1182 1187
1183 /* Cancel all the running timers/work .. */ 1188 /* Cancel all the running timers/work .. */
diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c
index a39552b3077..45784754dbc 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.c
+++ b/drivers/net/wireless/ath/ath9k/wmi.c
@@ -104,9 +104,12 @@ struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv)
104 104
105 wmi->drv_priv = priv; 105 wmi->drv_priv = priv;
106 wmi->stopped = false; 106 wmi->stopped = false;
107 skb_queue_head_init(&wmi->wmi_event_queue);
107 mutex_init(&wmi->op_mutex); 108 mutex_init(&wmi->op_mutex);
108 mutex_init(&wmi->multi_write_mutex); 109 mutex_init(&wmi->multi_write_mutex);
109 init_completion(&wmi->cmd_wait); 110 init_completion(&wmi->cmd_wait);
111 tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet,
112 (unsigned long)wmi);
110 113
111 return wmi; 114 return wmi;
112} 115}
@@ -122,11 +125,64 @@ void ath9k_deinit_wmi(struct ath9k_htc_priv *priv)
122 kfree(priv->wmi); 125 kfree(priv->wmi);
123} 126}
124 127
125void ath9k_swba_tasklet(unsigned long data) 128void ath9k_wmi_event_drain(struct ath9k_htc_priv *priv)
126{ 129{
127 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; 130 unsigned long flags;
128 131
129 ath9k_htc_swba(priv); 132 tasklet_kill(&priv->wmi->wmi_event_tasklet);
133 spin_lock_irqsave(&priv->wmi->wmi_lock, flags);
134 __skb_queue_purge(&priv->wmi->wmi_event_queue);
135 spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags);
136}
137
138void ath9k_wmi_event_tasklet(unsigned long data)
139{
140 struct wmi *wmi = (struct wmi *)data;
141 struct ath9k_htc_priv *priv = wmi->drv_priv;
142 struct wmi_cmd_hdr *hdr;
143 void *wmi_event;
144 struct wmi_event_swba *swba;
145 struct sk_buff *skb = NULL;
146 unsigned long flags;
147 u16 cmd_id;
148#ifdef CONFIG_ATH9K_HTC_DEBUGFS
149 __be32 txrate;
150#endif
151
152 do {
153 spin_lock_irqsave(&wmi->wmi_lock, flags);
154 skb = __skb_dequeue(&wmi->wmi_event_queue);
155 if (!skb) {
156 spin_unlock_irqrestore(&wmi->wmi_lock, flags);
157 return;
158 }
159 spin_unlock_irqrestore(&wmi->wmi_lock, flags);
160
161 hdr = (struct wmi_cmd_hdr *) skb->data;
162 cmd_id = be16_to_cpu(hdr->command_id);
163 wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));
164
165 switch (cmd_id) {
166 case WMI_SWBA_EVENTID:
167 swba = (struct wmi_event_swba *) wmi_event;
168 ath9k_htc_swba(priv, swba);
169 break;
170 case WMI_FATAL_EVENTID:
171 ieee80211_queue_work(wmi->drv_priv->hw,
172 &wmi->drv_priv->fatal_work);
173 break;
174 case WMI_TXRATE_EVENTID:
175#ifdef CONFIG_ATH9K_HTC_DEBUGFS
176 txrate = ((struct wmi_event_txrate *)wmi_event)->txrate;
177 wmi->drv_priv->debug.txrate = be32_to_cpu(txrate);
178#endif
179 break;
180 default:
181 break;
182 }
183
184 kfree_skb(skb);
185 } while (1);
130} 186}
131 187
132void ath9k_fatal_work(struct work_struct *work) 188void ath9k_fatal_work(struct work_struct *work)
@@ -155,11 +211,6 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb,
155 struct wmi *wmi = (struct wmi *) priv; 211 struct wmi *wmi = (struct wmi *) priv;
156 struct wmi_cmd_hdr *hdr; 212 struct wmi_cmd_hdr *hdr;
157 u16 cmd_id; 213 u16 cmd_id;
158 void *wmi_event;
159 struct wmi_event_swba *swba;
160#ifdef CONFIG_ATH9K_HTC_DEBUGFS
161 __be32 txrate;
162#endif
163 214
164 if (unlikely(wmi->stopped)) 215 if (unlikely(wmi->stopped))
165 goto free_skb; 216 goto free_skb;
@@ -168,32 +219,10 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb,
168 cmd_id = be16_to_cpu(hdr->command_id); 219 cmd_id = be16_to_cpu(hdr->command_id);
169 220
170 if (cmd_id & 0x1000) { 221 if (cmd_id & 0x1000) {
171 wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr)); 222 spin_lock(&wmi->wmi_lock);
172 switch (cmd_id) { 223 __skb_queue_tail(&wmi->wmi_event_queue, skb);
173 case WMI_SWBA_EVENTID: 224 spin_unlock(&wmi->wmi_lock);
174 swba = (struct wmi_event_swba *) wmi_event; 225 tasklet_schedule(&wmi->wmi_event_tasklet);
175
176 spin_lock(&wmi->wmi_lock);
177 wmi->tsf = be64_to_cpu(swba->tsf);
178 wmi->beacon_pending = swba->beacon_pending;
179 spin_unlock(&wmi->wmi_lock);
180
181 tasklet_schedule(&wmi->drv_priv->swba_tasklet);
182 break;
183 case WMI_FATAL_EVENTID:
184 ieee80211_queue_work(wmi->drv_priv->hw,
185 &wmi->drv_priv->fatal_work);
186 break;
187 case WMI_TXRATE_EVENTID:
188#ifdef CONFIG_ATH9K_HTC_DEBUGFS
189 txrate = ((struct wmi_event_txrate *)wmi_event)->txrate;
190 wmi->drv_priv->debug.txrate = be32_to_cpu(txrate);
191#endif
192 break;
193 default:
194 break;
195 }
196 kfree_skb(skb);
197 return; 226 return;
198 } 227 }
199 228
diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h
index 2fa91a941a7..ff5ba2b30ec 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.h
+++ b/drivers/net/wireless/ath/ath9k/wmi.h
@@ -106,13 +106,13 @@ struct wmi {
106 struct mutex op_mutex; 106 struct mutex op_mutex;
107 struct completion cmd_wait; 107 struct completion cmd_wait;
108 enum wmi_cmd_id last_cmd_id; 108 enum wmi_cmd_id last_cmd_id;
109 struct sk_buff_head wmi_event_queue;
110 struct tasklet_struct wmi_event_tasklet;
109 u16 tx_seq_id; 111 u16 tx_seq_id;
110 u8 *cmd_rsp_buf; 112 u8 *cmd_rsp_buf;
111 u32 cmd_rsp_len; 113 u32 cmd_rsp_len;
112 bool stopped; 114 bool stopped;
113 115
114 u64 tsf;
115 u8 beacon_pending;
116 spinlock_t wmi_lock; 116 spinlock_t wmi_lock;
117 117
118 atomic_t mwrite_cnt; 118 atomic_t mwrite_cnt;
@@ -129,8 +129,9 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
129 u8 *cmd_buf, u32 cmd_len, 129 u8 *cmd_buf, u32 cmd_len,
130 u8 *rsp_buf, u32 rsp_len, 130 u8 *rsp_buf, u32 rsp_len,
131 u32 timeout); 131 u32 timeout);
132void ath9k_swba_tasklet(unsigned long data); 132void ath9k_wmi_event_tasklet(unsigned long data);
133void ath9k_fatal_work(struct work_struct *work); 133void ath9k_fatal_work(struct work_struct *work);
134void ath9k_wmi_event_drain(struct ath9k_htc_priv *priv);
134 135
135#define WMI_CMD(_wmi_cmd) \ 136#define WMI_CMD(_wmi_cmd) \
136 do { \ 137 do { \