aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath9k/wmi.c
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/wireless/ath/ath9k/wmi.c
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/wireless/ath/ath9k/wmi.c')
-rw-r--r--drivers/net/wireless/ath/ath9k/wmi.c97
1 files changed, 63 insertions, 34 deletions
diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c
index a39552b3077b..45784754dbc2 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