Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / net / wireless / ath / ath9k / wow.c
1 /*
2  * Copyright (c) 2013 Qualcomm Atheros, Inc.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include "ath9k.h"
18
19 static const struct wiphy_wowlan_support ath9k_wowlan_support_legacy = {
20         .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
21         .n_patterns = MAX_NUM_USER_PATTERN,
22         .pattern_min_len = 1,
23         .pattern_max_len = MAX_PATTERN_SIZE,
24 };
25
26 static const struct wiphy_wowlan_support ath9k_wowlan_support = {
27         .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
28         .n_patterns = MAX_NUM_PATTERN - 2,
29         .pattern_min_len = 1,
30         .pattern_max_len = MAX_PATTERN_SIZE,
31 };
32
33 static u8 ath9k_wow_map_triggers(struct ath_softc *sc,
34                                  struct cfg80211_wowlan *wowlan)
35 {
36         u8 wow_triggers = 0;
37
38         if (wowlan->disconnect)
39                 wow_triggers |= AH_WOW_LINK_CHANGE |
40                                 AH_WOW_BEACON_MISS;
41         if (wowlan->magic_pkt)
42                 wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
43
44         if (wowlan->n_patterns)
45                 wow_triggers |= AH_WOW_USER_PATTERN_EN;
46
47         return wow_triggers;
48 }
49
50 static int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
51 {
52         struct ath_hw *ah = sc->sc_ah;
53         struct ath_common *common = ath9k_hw_common(ah);
54         int pattern_count = 0;
55         int ret, i, byte_cnt = 0;
56         u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
57         u8 dis_deauth_mask[MAX_PATTERN_SIZE];
58
59         memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE);
60         memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE);
61
62         /*
63          * Create Dissassociate / Deauthenticate packet filter
64          *
65          *     2 bytes        2 byte    6 bytes   6 bytes  6 bytes
66          *  +--------------+----------+---------+--------+--------+----
67          *  + Frame Control+ Duration +   DA    +  SA    +  BSSID +
68          *  +--------------+----------+---------+--------+--------+----
69          *
70          * The above is the management frame format for disassociate/
71          * deauthenticate pattern, from this we need to match the first byte
72          * of 'Frame Control' and DA, SA, and BSSID fields
73          * (skipping 2nd byte of FC and Duration feild.
74          *
75          * Disassociate pattern
76          * --------------------
77          * Frame control = 00 00 1010
78          * DA, SA, BSSID = x:x:x:x:x:x
79          * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x
80          *                          | x:x:x:x:x:x  -- 22 bytes
81          *
82          * Deauthenticate pattern
83          * ----------------------
84          * Frame control = 00 00 1100
85          * DA, SA, BSSID = x:x:x:x:x:x
86          * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x
87          *                          | x:x:x:x:x:x  -- 22 bytes
88          */
89
90         /* Fill out the mask with all FF's */
91         for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++)
92                 dis_deauth_mask[i] = 0xff;
93
94         /* copy the first byte of frame control field */
95         dis_deauth_pattern[byte_cnt] = 0xa0;
96         byte_cnt++;
97
98         /* skip 2nd byte of frame control and Duration field */
99         byte_cnt += 3;
100
101         /*
102          * need not match the destination mac address, it can be a broadcast
103          * mac address or an unicast to this station
104          */
105         byte_cnt += 6;
106
107         /* copy the source mac address */
108         memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
109
110         byte_cnt += 6;
111
112         /* copy the bssid, its same as the source mac address */
113         memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
114
115         /* Create Disassociate pattern mask */
116         dis_deauth_mask[0] = 0xfe;
117         dis_deauth_mask[1] = 0x03;
118         dis_deauth_mask[2] = 0xc0;
119
120         ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
121                                          pattern_count, byte_cnt);
122         if (ret)
123                 goto exit;
124
125         pattern_count++;
126         /*
127          * for de-authenticate pattern, only the first byte of the frame
128          * control field gets changed from 0xA0 to 0xC0
129          */
130         dis_deauth_pattern[0] = 0xC0;
131
132         ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
133                                          pattern_count, byte_cnt);
134 exit:
135         return ret;
136 }
137
138 static int ath9k_wow_add_pattern(struct ath_softc *sc,
139                                  struct cfg80211_wowlan *wowlan)
140 {
141         struct ath_hw *ah = sc->sc_ah;
142         struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
143         u8 wow_pattern[MAX_PATTERN_SIZE];
144         u8 wow_mask[MAX_PATTERN_SIZE];
145         int mask_len, ret = 0;
146         s8 i = 0;
147
148         for (i = 0; i < wowlan->n_patterns; i++) {
149                 mask_len = DIV_ROUND_UP(patterns[i].pattern_len, 8);
150                 memset(wow_pattern, 0, MAX_PATTERN_SIZE);
151                 memset(wow_mask, 0, MAX_PATTERN_SIZE);
152                 memcpy(wow_pattern, patterns[i].pattern, patterns[i].pattern_len);
153                 memcpy(wow_mask, patterns[i].mask, mask_len);
154
155                 ret = ath9k_hw_wow_apply_pattern(ah,
156                                                  wow_pattern,
157                                                  wow_mask,
158                                                  i + 2,
159                                                  patterns[i].pattern_len);
160                 if (ret)
161                         break;
162         }
163
164         return ret;
165 }
166
167 int ath9k_suspend(struct ieee80211_hw *hw,
168                   struct cfg80211_wowlan *wowlan)
169 {
170         struct ath_softc *sc = hw->priv;
171         struct ath_hw *ah = sc->sc_ah;
172         struct ath_common *common = ath9k_hw_common(ah);
173         u8 triggers;
174         int ret = 0;
175
176         ath9k_deinit_channel_context(sc);
177
178         mutex_lock(&sc->mutex);
179
180         if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
181                 ath_err(common, "Device not present\n");
182                 ret = -ENODEV;
183                 goto fail_wow;
184         }
185
186         if (WARN_ON(!wowlan)) {
187                 ath_err(common, "None of the WoW triggers enabled\n");
188                 ret = -EINVAL;
189                 goto fail_wow;
190         }
191
192         if (sc->cur_chan->nvifs > 1) {
193                 ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
194                 ret = 1;
195                 goto fail_wow;
196         }
197
198         if (ath9k_is_chanctx_enabled()) {
199                 if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) {
200                         ath_dbg(common, WOW,
201                                 "Multi-channel WOW is not supported\n");
202                         ret = 1;
203                         goto fail_wow;
204                 }
205         }
206
207         if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
208                 ath_dbg(common, WOW, "None of the STA vifs are associated\n");
209                 ret = 1;
210                 goto fail_wow;
211         }
212
213         triggers = ath9k_wow_map_triggers(sc, wowlan);
214         if (!triggers) {
215                 ath_dbg(common, WOW, "No valid WoW triggers\n");
216                 ret = 1;
217                 goto fail_wow;
218         }
219
220         ath_cancel_work(sc);
221         ath_stop_ani(sc);
222
223         ath9k_ps_wakeup(sc);
224
225         ath9k_stop_btcoex(sc);
226
227         /*
228          * Enable wake up on recieving disassoc/deauth
229          * frame by default.
230          */
231         ret = ath9k_wow_add_disassoc_deauth_pattern(sc);
232         if (ret) {
233                 ath_err(common,
234                         "Unable to add disassoc/deauth pattern: %d\n", ret);
235                 goto fail_wow;
236         }
237
238         if (triggers & AH_WOW_USER_PATTERN_EN) {
239                 ret = ath9k_wow_add_pattern(sc, wowlan);
240                 if (ret) {
241                         ath_err(common,
242                                 "Unable to add user pattern: %d\n", ret);
243                         goto fail_wow;
244                 }
245         }
246
247         spin_lock_bh(&sc->sc_pcu_lock);
248         /*
249          * To avoid false wake, we enable beacon miss interrupt only
250          * when we go to sleep. We save the current interrupt mask
251          * so we can restore it after the system wakes up
252          */
253         sc->wow_intr_before_sleep = ah->imask;
254         ah->imask &= ~ATH9K_INT_GLOBAL;
255         ath9k_hw_disable_interrupts(ah);
256         ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL;
257         ath9k_hw_set_interrupts(ah);
258         ath9k_hw_enable_interrupts(ah);
259
260         spin_unlock_bh(&sc->sc_pcu_lock);
261
262         /*
263          * we can now sync irq and kill any running tasklets, since we already
264          * disabled interrupts and not holding a spin lock
265          */
266         synchronize_irq(sc->irq);
267         tasklet_kill(&sc->intr_tq);
268
269         ath9k_hw_wow_enable(ah, triggers);
270
271         ath9k_ps_restore(sc);
272         ath_dbg(common, WOW, "Suspend with WoW triggers: 0x%x\n", triggers);
273
274         set_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
275 fail_wow:
276         mutex_unlock(&sc->mutex);
277         return ret;
278 }
279
280 int ath9k_resume(struct ieee80211_hw *hw)
281 {
282         struct ath_softc *sc = hw->priv;
283         struct ath_hw *ah = sc->sc_ah;
284         struct ath_common *common = ath9k_hw_common(ah);
285         u8 status;
286
287         mutex_lock(&sc->mutex);
288
289         ath9k_ps_wakeup(sc);
290
291         spin_lock_bh(&sc->sc_pcu_lock);
292
293         ath9k_hw_disable_interrupts(ah);
294         ah->imask = sc->wow_intr_before_sleep;
295         ath9k_hw_set_interrupts(ah);
296         ath9k_hw_enable_interrupts(ah);
297
298         spin_unlock_bh(&sc->sc_pcu_lock);
299
300         status = ath9k_hw_wow_wakeup(ah);
301         ath_dbg(common, WOW, "Resume with WoW status: 0x%x\n", status);
302
303         ath_restart_work(sc);
304         ath9k_start_btcoex(sc);
305
306         clear_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
307
308         ath9k_ps_restore(sc);
309         mutex_unlock(&sc->mutex);
310
311         return 0;
312 }
313
314 void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
315 {
316         struct ath_softc *sc = hw->priv;
317         struct ath_common *common = ath9k_hw_common(sc->sc_ah);
318
319         mutex_lock(&sc->mutex);
320         device_set_wakeup_enable(sc->dev, enabled);
321         mutex_unlock(&sc->mutex);
322
323         ath_dbg(common, WOW, "WoW wakeup source is %s\n",
324                 (enabled) ? "enabled" : "disabled");
325 }
326
327 void ath9k_init_wow(struct ieee80211_hw *hw)
328 {
329         struct ath_softc *sc = hw->priv;
330         struct ath_hw *ah = sc->sc_ah;
331
332         if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow) {
333                 if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565_11_OR_LATER(ah))
334                         hw->wiphy->wowlan = &ath9k_wowlan_support;
335                 else
336                         hw->wiphy->wowlan = &ath9k_wowlan_support_legacy;
337
338                 device_init_wakeup(sc->dev, 1);
339         }
340 }
341
342 void ath9k_deinit_wow(struct ieee80211_hw *hw)
343 {
344         struct ath_softc *sc = hw->priv;
345
346         if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow)
347                 device_init_wakeup(sc->dev, 0);
348 }