diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx/ps.c')
-rw-r--r-- | drivers/net/wireless/wl12xx/ps.c | 90 |
1 files changed, 83 insertions, 7 deletions
diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index 60a3738eadb0..971f13e792da 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include "reg.h" | 24 | #include "reg.h" |
25 | #include "ps.h" | 25 | #include "ps.h" |
26 | #include "io.h" | 26 | #include "io.h" |
27 | #include "tx.h" | ||
27 | 28 | ||
28 | #define WL1271_WAKEUP_TIMEOUT 500 | 29 | #define WL1271_WAKEUP_TIMEOUT 500 |
29 | 30 | ||
@@ -68,7 +69,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) | |||
68 | } | 69 | } |
69 | } | 70 | } |
70 | 71 | ||
71 | int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) | 72 | int wl1271_ps_elp_wakeup(struct wl1271 *wl) |
72 | { | 73 | { |
73 | DECLARE_COMPLETION_ONSTACK(compl); | 74 | DECLARE_COMPLETION_ONSTACK(compl); |
74 | unsigned long flags; | 75 | unsigned long flags; |
@@ -86,7 +87,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) | |||
86 | * the completion variable in one entity. | 87 | * the completion variable in one entity. |
87 | */ | 88 | */ |
88 | spin_lock_irqsave(&wl->wl_lock, flags); | 89 | spin_lock_irqsave(&wl->wl_lock, flags); |
89 | if (work_pending(&wl->irq_work) || chip_awake) | 90 | if (test_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags)) |
90 | pending = true; | 91 | pending = true; |
91 | else | 92 | else |
92 | wl->elp_compl = &compl; | 93 | wl->elp_compl = &compl; |
@@ -139,8 +140,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, | |||
139 | return ret; | 140 | return ret; |
140 | } | 141 | } |
141 | 142 | ||
142 | ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE, | 143 | ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE); |
143 | rates, send); | ||
144 | if (ret < 0) | 144 | if (ret < 0) |
145 | return ret; | 145 | return ret; |
146 | 146 | ||
@@ -149,7 +149,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, | |||
149 | case STATION_ACTIVE_MODE: | 149 | case STATION_ACTIVE_MODE: |
150 | default: | 150 | default: |
151 | wl1271_debug(DEBUG_PSM, "leaving psm"); | 151 | wl1271_debug(DEBUG_PSM, "leaving psm"); |
152 | ret = wl1271_ps_elp_wakeup(wl, false); | 152 | ret = wl1271_ps_elp_wakeup(wl); |
153 | if (ret < 0) | 153 | if (ret < 0) |
154 | return ret; | 154 | return ret; |
155 | 155 | ||
@@ -163,8 +163,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, | |||
163 | if (ret < 0) | 163 | if (ret < 0) |
164 | return ret; | 164 | return ret; |
165 | 165 | ||
166 | ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE, | 166 | ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE); |
167 | rates, send); | ||
168 | if (ret < 0) | 167 | if (ret < 0) |
169 | return ret; | 168 | return ret; |
170 | 169 | ||
@@ -175,4 +174,81 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, | |||
175 | return ret; | 174 | return ret; |
176 | } | 175 | } |
177 | 176 | ||
177 | static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) | ||
178 | { | ||
179 | int i, filtered = 0; | ||
180 | struct sk_buff *skb; | ||
181 | struct ieee80211_tx_info *info; | ||
182 | unsigned long flags; | ||
183 | |||
184 | /* filter all frames currently the low level queus for this hlid */ | ||
185 | for (i = 0; i < NUM_TX_QUEUES; i++) { | ||
186 | while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { | ||
187 | info = IEEE80211_SKB_CB(skb); | ||
188 | info->flags |= IEEE80211_TX_STAT_TX_FILTERED; | ||
189 | info->status.rates[0].idx = -1; | ||
190 | ieee80211_tx_status(wl->hw, skb); | ||
191 | filtered++; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | spin_lock_irqsave(&wl->wl_lock, flags); | ||
196 | wl->tx_queue_count -= filtered; | ||
197 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
198 | |||
199 | wl1271_handle_tx_low_watermark(wl); | ||
200 | } | ||
201 | |||
202 | void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues) | ||
203 | { | ||
204 | struct ieee80211_sta *sta; | ||
205 | |||
206 | if (test_bit(hlid, &wl->ap_ps_map)) | ||
207 | return; | ||
208 | |||
209 | wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d blks %d " | ||
210 | "clean_queues %d", hlid, wl->links[hlid].allocated_blks, | ||
211 | clean_queues); | ||
212 | |||
213 | rcu_read_lock(); | ||
214 | sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr); | ||
215 | if (!sta) { | ||
216 | wl1271_error("could not find sta %pM for starting ps", | ||
217 | wl->links[hlid].addr); | ||
218 | rcu_read_unlock(); | ||
219 | return; | ||
220 | } | ||
178 | 221 | ||
222 | ieee80211_sta_ps_transition_ni(sta, true); | ||
223 | rcu_read_unlock(); | ||
224 | |||
225 | /* do we want to filter all frames from this link's queues? */ | ||
226 | if (clean_queues) | ||
227 | wl1271_ps_filter_frames(wl, hlid); | ||
228 | |||
229 | __set_bit(hlid, &wl->ap_ps_map); | ||
230 | } | ||
231 | |||
232 | void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid) | ||
233 | { | ||
234 | struct ieee80211_sta *sta; | ||
235 | |||
236 | if (!test_bit(hlid, &wl->ap_ps_map)) | ||
237 | return; | ||
238 | |||
239 | wl1271_debug(DEBUG_PSM, "end mac80211 PSM on hlid %d", hlid); | ||
240 | |||
241 | __clear_bit(hlid, &wl->ap_ps_map); | ||
242 | |||
243 | rcu_read_lock(); | ||
244 | sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr); | ||
245 | if (!sta) { | ||
246 | wl1271_error("could not find sta %pM for ending ps", | ||
247 | wl->links[hlid].addr); | ||
248 | goto end; | ||
249 | } | ||
250 | |||
251 | ieee80211_sta_ps_transition_ni(sta, false); | ||
252 | end: | ||
253 | rcu_read_unlock(); | ||
254 | } | ||