aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath9k/virtual.c
diff options
context:
space:
mode:
authorJouni Malinen <jouni.malinen@atheros.com>2009-03-03 12:23:31 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-03-05 14:39:45 -0500
commitf0ed85c6c7960b26666db013e02e748b56eef98a (patch)
tree005de0605ebb2899a671e5ebc06e5b45393f91e5 /drivers/net/wireless/ath9k/virtual.c
parentb93bce2a5e8fd5c9f5d8c982efd6bca71a9b83f3 (diff)
ath9k: Virtual wiphy pause/unpause functionality
Allow virtual wiphys to be paused/unpaused to allow off-channel operations. Pause will stop all TX queues for the wiphy and move the STA into power save mode if in managed mode. Unpause wakes up the TX queues and notifies the AP that the STA woke up if in managed mode. Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath9k/virtual.c')
-rw-r--r--drivers/net/wireless/ath9k/virtual.c161
1 files changed, 161 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath9k/virtual.c b/drivers/net/wireless/ath9k/virtual.c
index 67bcb9343ca6..a8bac97bd847 100644
--- a/drivers/net/wireless/ath9k/virtual.c
+++ b/drivers/net/wireless/ath9k/virtual.c
@@ -175,3 +175,164 @@ int ath9k_wiphy_del(struct ath_wiphy *aphy)
175 spin_unlock_bh(&sc->wiphy_lock); 175 spin_unlock_bh(&sc->wiphy_lock);
176 return -ENOENT; 176 return -ENOENT;
177} 177}
178
179static int ath9k_send_nullfunc(struct ath_wiphy *aphy,
180 struct ieee80211_vif *vif, const u8 *bssid,
181 int ps)
182{
183 struct ath_softc *sc = aphy->sc;
184 struct ath_tx_control txctl;
185 struct sk_buff *skb;
186 struct ieee80211_hdr *hdr;
187 __le16 fc;
188 struct ieee80211_tx_info *info;
189
190 skb = dev_alloc_skb(24);
191 if (skb == NULL)
192 return -ENOMEM;
193 hdr = (struct ieee80211_hdr *) skb_put(skb, 24);
194 memset(hdr, 0, 24);
195 fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
196 IEEE80211_FCTL_TODS);
197 if (ps)
198 fc |= cpu_to_le16(IEEE80211_FCTL_PM);
199 hdr->frame_control = fc;
200 memcpy(hdr->addr1, bssid, ETH_ALEN);
201 memcpy(hdr->addr2, aphy->hw->wiphy->perm_addr, ETH_ALEN);
202 memcpy(hdr->addr3, bssid, ETH_ALEN);
203
204 info = IEEE80211_SKB_CB(skb);
205 memset(info, 0, sizeof(*info));
206 info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS;
207 info->control.vif = vif;
208 info->control.rates[0].idx = 0;
209 info->control.rates[0].count = 4;
210 info->control.rates[1].idx = -1;
211
212 memset(&txctl, 0, sizeof(struct ath_tx_control));
213 txctl.txq = &sc->tx.txq[sc->tx.hwq_map[ATH9K_WME_AC_VO]];
214 txctl.frame_type = ps ? ATH9K_INT_PAUSE : ATH9K_INT_UNPAUSE;
215
216 if (ath_tx_start(aphy->hw, skb, &txctl) != 0)
217 goto exit;
218
219 return 0;
220exit:
221 dev_kfree_skb_any(skb);
222 return -1;
223}
224
225/*
226 * ath9k version of ieee80211_tx_status() for TX frames that are generated
227 * internally in the driver.
228 */
229void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
230{
231 struct ath_wiphy *aphy = hw->priv;
232 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
233 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
234 struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
235
236 if (tx_info_priv && tx_info_priv->frame_type == ATH9K_INT_PAUSE &&
237 aphy->state == ATH_WIPHY_PAUSING) {
238 if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
239 printk(KERN_DEBUG "ath9k: %s: no ACK for pause "
240 "frame\n", wiphy_name(hw->wiphy));
241 /*
242 * The AP did not reply; ignore this to allow us to
243 * continue.
244 */
245 }
246 aphy->state = ATH_WIPHY_PAUSED;
247 }
248
249 kfree(tx_info_priv);
250 tx_info->rate_driver_data[0] = NULL;
251
252 dev_kfree_skb(skb);
253}
254
255static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
256{
257 struct ath_wiphy *aphy = data;
258 struct ath_vif *avp = (void *) vif->drv_priv;
259
260 switch (vif->type) {
261 case NL80211_IFTYPE_STATION:
262 if (!vif->bss_conf.assoc) {
263 aphy->state = ATH_WIPHY_PAUSED;
264 break;
265 }
266 /* TODO: could avoid this if already in PS mode */
267 ath9k_send_nullfunc(aphy, vif, avp->bssid, 1);
268 break;
269 case NL80211_IFTYPE_AP:
270 /* Beacon transmission is paused by aphy->state change */
271 aphy->state = ATH_WIPHY_PAUSED;
272 break;
273 default:
274 break;
275 }
276}
277
278/* caller must hold wiphy_lock */
279static int __ath9k_wiphy_pause(struct ath_wiphy *aphy)
280{
281 ieee80211_stop_queues(aphy->hw);
282 aphy->state = ATH_WIPHY_PAUSING;
283 /*
284 * TODO: handle PAUSING->PAUSED for the case where there are multiple
285 * active vifs (now we do it on the first vif getting ready; should be
286 * on the last)
287 */
288 ieee80211_iterate_active_interfaces_atomic(aphy->hw, ath9k_pause_iter,
289 aphy);
290 return 0;
291}
292
293int ath9k_wiphy_pause(struct ath_wiphy *aphy)
294{
295 int ret;
296 spin_lock_bh(&aphy->sc->wiphy_lock);
297 ret = __ath9k_wiphy_pause(aphy);
298 spin_unlock_bh(&aphy->sc->wiphy_lock);
299 return ret;
300}
301
302static void ath9k_unpause_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
303{
304 struct ath_wiphy *aphy = data;
305 struct ath_vif *avp = (void *) vif->drv_priv;
306
307 switch (vif->type) {
308 case NL80211_IFTYPE_STATION:
309 if (!vif->bss_conf.assoc)
310 break;
311 ath9k_send_nullfunc(aphy, vif, avp->bssid, 0);
312 break;
313 case NL80211_IFTYPE_AP:
314 /* Beacon transmission is re-enabled by aphy->state change */
315 break;
316 default:
317 break;
318 }
319}
320
321/* caller must hold wiphy_lock */
322static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy)
323{
324 ieee80211_iterate_active_interfaces_atomic(aphy->hw,
325 ath9k_unpause_iter, aphy);
326 aphy->state = ATH_WIPHY_ACTIVE;
327 ieee80211_wake_queues(aphy->hw);
328 return 0;
329}
330
331int ath9k_wiphy_unpause(struct ath_wiphy *aphy)
332{
333 int ret;
334 spin_lock_bh(&aphy->sc->wiphy_lock);
335 ret = __ath9k_wiphy_unpause(aphy);
336 spin_unlock_bh(&aphy->sc->wiphy_lock);
337 return ret;
338}