diff options
Diffstat (limited to 'drivers/net/wireless/ath')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ath9k.h | 3 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/channel.c | 97 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/main.c | 6 |
3 files changed, 106 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index ddd2e81ccd58..d5a586bbab7c 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h | |||
@@ -329,6 +329,7 @@ struct ath_chanctx { | |||
329 | u16 txpower; | 329 | u16 txpower; |
330 | bool offchannel; | 330 | bool offchannel; |
331 | bool stopped; | 331 | bool stopped; |
332 | bool active; | ||
332 | }; | 333 | }; |
333 | 334 | ||
334 | void ath_chanctx_init(struct ath_softc *sc); | 335 | void ath_chanctx_init(struct ath_softc *sc); |
@@ -336,6 +337,8 @@ void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, | |||
336 | struct cfg80211_chan_def *chandef); | 337 | struct cfg80211_chan_def *chandef); |
337 | void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, | 338 | void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, |
338 | struct cfg80211_chan_def *chandef); | 339 | struct cfg80211_chan_def *chandef); |
340 | void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx); | ||
341 | |||
339 | int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan); | 342 | int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan); |
340 | int ath_startrecv(struct ath_softc *sc); | 343 | int ath_startrecv(struct ath_softc *sc); |
341 | bool ath_stoprecv(struct ath_softc *sc); | 344 | bool ath_stoprecv(struct ath_softc *sc); |
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 86404d38901e..26fc98b3495b 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c | |||
@@ -101,10 +101,99 @@ static int ath_set_channel(struct ath_softc *sc) | |||
101 | return 0; | 101 | return 0; |
102 | } | 102 | } |
103 | 103 | ||
104 | static bool | ||
105 | ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp, | ||
106 | bool powersave) | ||
107 | { | ||
108 | struct ieee80211_vif *vif = avp->vif; | ||
109 | struct ieee80211_sta *sta = NULL; | ||
110 | struct ieee80211_hdr_3addr *nullfunc; | ||
111 | struct ath_tx_control txctl; | ||
112 | struct sk_buff *skb; | ||
113 | int band = sc->cur_chan->chandef.chan->band; | ||
114 | |||
115 | switch (vif->type) { | ||
116 | case NL80211_IFTYPE_STATION: | ||
117 | if (!vif->bss_conf.assoc) | ||
118 | return false; | ||
119 | |||
120 | skb = ieee80211_nullfunc_get(sc->hw, vif); | ||
121 | if (!skb) | ||
122 | return false; | ||
123 | |||
124 | nullfunc = (struct ieee80211_hdr_3addr *) skb->data; | ||
125 | if (powersave) | ||
126 | nullfunc->frame_control |= | ||
127 | cpu_to_le16(IEEE80211_FCTL_PM); | ||
128 | |||
129 | skb_set_queue_mapping(skb, IEEE80211_AC_VO); | ||
130 | if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) { | ||
131 | dev_kfree_skb_any(skb); | ||
132 | return false; | ||
133 | } | ||
134 | break; | ||
135 | default: | ||
136 | return false; | ||
137 | } | ||
138 | |||
139 | memset(&txctl, 0, sizeof(txctl)); | ||
140 | txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; | ||
141 | txctl.sta = sta; | ||
142 | txctl.force_channel = true; | ||
143 | if (ath_tx_start(sc->hw, skb, &txctl)) { | ||
144 | ieee80211_free_txskb(sc->hw, skb); | ||
145 | return false; | ||
146 | } | ||
147 | |||
148 | return true; | ||
149 | } | ||
150 | |||
151 | void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx) | ||
152 | { | ||
153 | struct ath_vif *avp; | ||
154 | bool active = false; | ||
155 | |||
156 | if (!ctx) | ||
157 | return; | ||
158 | |||
159 | list_for_each_entry(avp, &ctx->vifs, list) { | ||
160 | struct ieee80211_vif *vif = avp->vif; | ||
161 | |||
162 | switch (vif->type) { | ||
163 | case NL80211_IFTYPE_P2P_CLIENT: | ||
164 | case NL80211_IFTYPE_STATION: | ||
165 | if (vif->bss_conf.assoc) | ||
166 | active = true; | ||
167 | break; | ||
168 | default: | ||
169 | active = true; | ||
170 | break; | ||
171 | } | ||
172 | } | ||
173 | ctx->active = active; | ||
174 | } | ||
175 | |||
176 | static bool | ||
177 | ath_chanctx_send_ps_frame(struct ath_softc *sc, bool powersave) | ||
178 | { | ||
179 | struct ath_vif *avp; | ||
180 | bool sent = false; | ||
181 | |||
182 | rcu_read_lock(); | ||
183 | list_for_each_entry(avp, &sc->cur_chan->vifs, list) { | ||
184 | if (ath_chanctx_send_vif_ps_frame(sc, avp, powersave)) | ||
185 | sent = true; | ||
186 | } | ||
187 | rcu_read_unlock(); | ||
188 | |||
189 | return sent; | ||
190 | } | ||
191 | |||
104 | void ath_chanctx_work(struct work_struct *work) | 192 | void ath_chanctx_work(struct work_struct *work) |
105 | { | 193 | { |
106 | struct ath_softc *sc = container_of(work, struct ath_softc, | 194 | struct ath_softc *sc = container_of(work, struct ath_softc, |
107 | chanctx_work); | 195 | chanctx_work); |
196 | bool send_ps = false; | ||
108 | 197 | ||
109 | mutex_lock(&sc->mutex); | 198 | mutex_lock(&sc->mutex); |
110 | spin_lock_bh(&sc->chan_lock); | 199 | spin_lock_bh(&sc->chan_lock); |
@@ -120,6 +209,10 @@ void ath_chanctx_work(struct work_struct *work) | |||
120 | 209 | ||
121 | __ath9k_flush(sc->hw, ~0, true); | 210 | __ath9k_flush(sc->hw, ~0, true); |
122 | 211 | ||
212 | if (ath_chanctx_send_ps_frame(sc, true)) | ||
213 | __ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO), false); | ||
214 | |||
215 | send_ps = true; | ||
123 | spin_lock_bh(&sc->chan_lock); | 216 | spin_lock_bh(&sc->chan_lock); |
124 | } | 217 | } |
125 | sc->cur_chan = sc->next_chan; | 218 | sc->cur_chan = sc->next_chan; |
@@ -131,6 +224,10 @@ void ath_chanctx_work(struct work_struct *work) | |||
131 | memcmp(&sc->cur_chandef, &sc->cur_chan->chandef, | 224 | memcmp(&sc->cur_chandef, &sc->cur_chan->chandef, |
132 | sizeof(sc->cur_chandef))) | 225 | sizeof(sc->cur_chandef))) |
133 | ath_set_channel(sc); | 226 | ath_set_channel(sc); |
227 | |||
228 | if (send_ps) | ||
229 | ath_chanctx_send_ps_frame(sc, false); | ||
230 | |||
134 | mutex_unlock(&sc->mutex); | 231 | mutex_unlock(&sc->mutex); |
135 | } | 232 | } |
136 | 233 | ||
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index bb73a23f19f8..d8a4510f963a 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c | |||
@@ -1043,6 +1043,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, | |||
1043 | /* XXX - will be removed once chanctx ops are added */ | 1043 | /* XXX - will be removed once chanctx ops are added */ |
1044 | avp->chanctx = sc->cur_chan; | 1044 | avp->chanctx = sc->cur_chan; |
1045 | list_add_tail(&avp->list, &sc->cur_chan->vifs); | 1045 | list_add_tail(&avp->list, &sc->cur_chan->vifs); |
1046 | ath_chanctx_check_active(sc, avp->chanctx); | ||
1046 | 1047 | ||
1047 | an->sc = sc; | 1048 | an->sc = sc; |
1048 | an->sta = NULL; | 1049 | an->sta = NULL; |
@@ -1061,6 +1062,7 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, | |||
1061 | { | 1062 | { |
1062 | struct ath_softc *sc = hw->priv; | 1063 | struct ath_softc *sc = hw->priv; |
1063 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); | 1064 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); |
1065 | struct ath_vif *avp = (void *)vif->drv_priv; | ||
1064 | 1066 | ||
1065 | mutex_lock(&sc->mutex); | 1067 | mutex_lock(&sc->mutex); |
1066 | 1068 | ||
@@ -1083,6 +1085,7 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, | |||
1083 | 1085 | ||
1084 | if (ath9k_uses_beacons(vif->type)) | 1086 | if (ath9k_uses_beacons(vif->type)) |
1085 | ath9k_beacon_assign_slot(sc, vif); | 1087 | ath9k_beacon_assign_slot(sc, vif); |
1088 | ath_chanctx_check_active(sc, avp->chanctx); | ||
1086 | 1089 | ||
1087 | mutex_unlock(&sc->mutex); | 1090 | mutex_unlock(&sc->mutex); |
1088 | return 0; | 1091 | return 0; |
@@ -1141,6 +1144,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, | |||
1141 | ath9k_ps_restore(sc); | 1144 | ath9k_ps_restore(sc); |
1142 | 1145 | ||
1143 | ath_tx_node_cleanup(sc, &avp->mcast_node); | 1146 | ath_tx_node_cleanup(sc, &avp->mcast_node); |
1147 | ath_chanctx_check_active(sc, avp->chanctx); | ||
1144 | 1148 | ||
1145 | mutex_unlock(&sc->mutex); | 1149 | mutex_unlock(&sc->mutex); |
1146 | } | 1150 | } |
@@ -1733,6 +1737,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, | |||
1733 | if (ath9k_hw_mci_is_enabled(sc->sc_ah)) | 1737 | if (ath9k_hw_mci_is_enabled(sc->sc_ah)) |
1734 | ath9k_mci_update_wlan_channels(sc, true); | 1738 | ath9k_mci_update_wlan_channels(sc, true); |
1735 | } | 1739 | } |
1740 | |||
1741 | ath_chanctx_check_active(sc, avp->chanctx); | ||
1736 | } | 1742 | } |
1737 | 1743 | ||
1738 | if (changed & BSS_CHANGED_IBSS) { | 1744 | if (changed & BSS_CHANGED_IBSS) { |