aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
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
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')
-rw-r--r--drivers/net/wireless/ath9k/ath9k.h10
-rw-r--r--drivers/net/wireless/ath9k/beacon.c3
-rw-r--r--drivers/net/wireless/ath9k/main.c1
-rw-r--r--drivers/net/wireless/ath9k/rc.h7
-rw-r--r--drivers/net/wireless/ath9k/virtual.c161
-rw-r--r--drivers/net/wireless/ath9k/xmit.c11
6 files changed, 191 insertions, 2 deletions
diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h
index 386b93622e58..1153374f94fb 100644
--- a/drivers/net/wireless/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath9k/ath9k.h
@@ -292,6 +292,7 @@ struct ath_atx_ac {
292struct ath_tx_control { 292struct ath_tx_control {
293 struct ath_txq *txq; 293 struct ath_txq *txq;
294 int if_id; 294 int if_id;
295 enum ath9k_internal_frame_type frame_type;
295}; 296};
296 297
297struct ath_xmit_status { 298struct ath_xmit_status {
@@ -392,6 +393,7 @@ struct ath_vif {
392 enum nl80211_iftype av_opmode; 393 enum nl80211_iftype av_opmode;
393 struct ath_buf *av_bcbuf; 394 struct ath_buf *av_bcbuf;
394 struct ath_tx_control av_btxctl; 395 struct ath_tx_control av_btxctl;
396 u8 bssid[ETH_ALEN]; /* current BSSID from config_interface */
395}; 397};
396 398
397/*******************/ 399/*******************/
@@ -619,6 +621,11 @@ struct ath_softc {
619struct ath_wiphy { 621struct ath_wiphy {
620 struct ath_softc *sc; /* shared for all virtual wiphys */ 622 struct ath_softc *sc; /* shared for all virtual wiphys */
621 struct ieee80211_hw *hw; 623 struct ieee80211_hw *hw;
624 enum ath_wiphy_state {
625 ATH_WIPHY_ACTIVE,
626 ATH_WIPHY_PAUSING,
627 ATH_WIPHY_PAUSED,
628 } state;
622}; 629};
623 630
624int ath_reset(struct ath_softc *sc, bool retry_tx); 631int ath_reset(struct ath_softc *sc, bool retry_tx);
@@ -684,5 +691,8 @@ static inline void ath9k_ps_restore(struct ath_softc *sc)
684void ath9k_set_bssid_mask(struct ieee80211_hw *hw); 691void ath9k_set_bssid_mask(struct ieee80211_hw *hw);
685int ath9k_wiphy_add(struct ath_softc *sc); 692int ath9k_wiphy_add(struct ath_softc *sc);
686int ath9k_wiphy_del(struct ath_wiphy *aphy); 693int ath9k_wiphy_del(struct ath_wiphy *aphy);
694void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb);
695int ath9k_wiphy_pause(struct ath_wiphy *aphy);
696int ath9k_wiphy_unpause(struct ath_wiphy *aphy);
687 697
688#endif /* ATH9K_H */ 698#endif /* ATH9K_H */
diff --git a/drivers/net/wireless/ath9k/beacon.c b/drivers/net/wireless/ath9k/beacon.c
index 760f5b80f79e..039c78136c50 100644
--- a/drivers/net/wireless/ath9k/beacon.c
+++ b/drivers/net/wireless/ath9k/beacon.c
@@ -125,6 +125,9 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw,
125 struct ieee80211_tx_info *info; 125 struct ieee80211_tx_info *info;
126 int cabq_depth; 126 int cabq_depth;
127 127
128 if (aphy->state != ATH_WIPHY_ACTIVE)
129 return NULL;
130
128 avp = (void *)vif->drv_priv; 131 avp = (void *)vif->drv_priv;
129 cabq = sc->beacon.cabq; 132 cabq = sc->beacon.cabq;
130 133
diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c
index 433a11c41838..7e44013ba6e7 100644
--- a/drivers/net/wireless/ath9k/main.c
+++ b/drivers/net/wireless/ath9k/main.c
@@ -2373,6 +2373,7 @@ static int ath9k_config_interface(struct ieee80211_hw *hw,
2373 case NL80211_IFTYPE_ADHOC: 2373 case NL80211_IFTYPE_ADHOC:
2374 /* Set BSSID */ 2374 /* Set BSSID */
2375 memcpy(sc->curbssid, conf->bssid, ETH_ALEN); 2375 memcpy(sc->curbssid, conf->bssid, ETH_ALEN);
2376 memcpy(avp->bssid, conf->bssid, ETH_ALEN);
2376 sc->curaid = 0; 2377 sc->curaid = 0;
2377 ath9k_hw_write_associd(sc); 2378 ath9k_hw_write_associd(sc);
2378 2379
diff --git a/drivers/net/wireless/ath9k/rc.h b/drivers/net/wireless/ath9k/rc.h
index 0584122341ad..db9b0b9a3431 100644
--- a/drivers/net/wireless/ath9k/rc.h
+++ b/drivers/net/wireless/ath9k/rc.h
@@ -194,12 +194,19 @@ struct ath_rate_priv {
194 struct ath_rate_softc *asc; 194 struct ath_rate_softc *asc;
195}; 195};
196 196
197enum ath9k_internal_frame_type {
198 ATH9K_NOT_INTERNAL,
199 ATH9K_INT_PAUSE,
200 ATH9K_INT_UNPAUSE
201};
202
197struct ath_tx_info_priv { 203struct ath_tx_info_priv {
198 struct ath_wiphy *aphy; 204 struct ath_wiphy *aphy;
199 struct ath_tx_status tx; 205 struct ath_tx_status tx;
200 int n_frames; 206 int n_frames;
201 int n_bad_frames; 207 int n_bad_frames;
202 bool update_rc; 208 bool update_rc;
209 enum ath9k_internal_frame_type frame_type;
203}; 210};
204 211
205#define ATH_TX_INFO_PRIV(tx_info) \ 212#define ATH_TX_INFO_PRIV(tx_info) \
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}
diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c
index 3c48fa5646f5..a82d2ab7c3a0 100644
--- a/drivers/net/wireless/ath9k/xmit.c
+++ b/drivers/net/wireless/ath9k/xmit.c
@@ -1514,6 +1514,7 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf,
1514 return -ENOMEM; 1514 return -ENOMEM;
1515 tx_info->rate_driver_data[0] = tx_info_priv; 1515 tx_info->rate_driver_data[0] = tx_info_priv;
1516 tx_info_priv->aphy = aphy; 1516 tx_info_priv->aphy = aphy;
1517 tx_info_priv->frame_type = txctl->frame_type;
1517 hdrlen = ieee80211_get_hdrlen_from_skb(skb); 1518 hdrlen = ieee80211_get_hdrlen_from_skb(skb);
1518 fc = hdr->frame_control; 1519 fc = hdr->frame_control;
1519 1520
@@ -1722,11 +1723,14 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
1722 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); 1723 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
1723 struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); 1724 struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
1724 int hdrlen, padsize; 1725 int hdrlen, padsize;
1726 int frame_type = ATH9K_NOT_INTERNAL;
1725 1727
1726 DPRINTF(sc, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb); 1728 DPRINTF(sc, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb);
1727 1729
1728 if (tx_info_priv) 1730 if (tx_info_priv) {
1729 hw = tx_info_priv->aphy->hw; 1731 hw = tx_info_priv->aphy->hw;
1732 frame_type = tx_info_priv->frame_type;
1733 }
1730 1734
1731 if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK || 1735 if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK ||
1732 tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) { 1736 tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
@@ -1757,7 +1761,10 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
1757 skb_pull(skb, padsize); 1761 skb_pull(skb, padsize);
1758 } 1762 }
1759 1763
1760 ieee80211_tx_status(hw, skb); 1764 if (frame_type == ATH9K_NOT_INTERNAL)
1765 ieee80211_tx_status(hw, skb);
1766 else
1767 ath9k_tx_status(hw, skb);
1761} 1768}
1762 1769
1763static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, 1770static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,