2 * Copyright (c) 2013 Qualcomm Atheros, Inc.
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.
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.
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
,
23 .pattern_max_len
= MAX_PATTERN_SIZE
,
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,
30 .pattern_max_len
= MAX_PATTERN_SIZE
,
33 static u8
ath9k_wow_map_triggers(struct ath_softc
*sc
,
34 struct cfg80211_wowlan
*wowlan
)
38 if (wowlan
->disconnect
)
39 wow_triggers
|= AH_WOW_LINK_CHANGE
|
41 if (wowlan
->magic_pkt
)
42 wow_triggers
|= AH_WOW_MAGIC_PATTERN_EN
;
44 if (wowlan
->n_patterns
)
45 wow_triggers
|= AH_WOW_USER_PATTERN_EN
;
50 static int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc
*sc
)
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
];
59 memset(dis_deauth_pattern
, 0, MAX_PATTERN_SIZE
);
60 memset(dis_deauth_mask
, 0, MAX_PATTERN_SIZE
);
63 * Create Dissassociate / Deauthenticate packet filter
65 * 2 bytes 2 byte 6 bytes 6 bytes 6 bytes
66 * +--------------+----------+---------+--------+--------+----
67 * + Frame Control+ Duration + DA + SA + BSSID +
68 * +--------------+----------+---------+--------+--------+----
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.
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
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
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;
94 /* copy the first byte of frame control field */
95 dis_deauth_pattern
[byte_cnt
] = 0xa0;
98 /* skip 2nd byte of frame control and Duration field */
102 * need not match the destination mac address, it can be a broadcast
103 * mac address or an unicast to this station
107 /* copy the source mac address */
108 memcpy((dis_deauth_pattern
+ byte_cnt
), common
->curbssid
, ETH_ALEN
);
112 /* copy the bssid, its same as the source mac address */
113 memcpy((dis_deauth_pattern
+ byte_cnt
), common
->curbssid
, ETH_ALEN
);
115 /* Create Disassociate pattern mask */
116 dis_deauth_mask
[0] = 0xfe;
117 dis_deauth_mask
[1] = 0x03;
118 dis_deauth_mask
[2] = 0xc0;
120 ret
= ath9k_hw_wow_apply_pattern(ah
, dis_deauth_pattern
, dis_deauth_mask
,
121 pattern_count
, byte_cnt
);
127 * for de-authenticate pattern, only the first byte of the frame
128 * control field gets changed from 0xA0 to 0xC0
130 dis_deauth_pattern
[0] = 0xC0;
132 ret
= ath9k_hw_wow_apply_pattern(ah
, dis_deauth_pattern
, dis_deauth_mask
,
133 pattern_count
, byte_cnt
);
138 static int ath9k_wow_add_pattern(struct ath_softc
*sc
,
139 struct cfg80211_wowlan
*wowlan
)
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;
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
);
155 ret
= ath9k_hw_wow_apply_pattern(ah
,
159 patterns
[i
].pattern_len
);
167 int ath9k_suspend(struct ieee80211_hw
*hw
,
168 struct cfg80211_wowlan
*wowlan
)
170 struct ath_softc
*sc
= hw
->priv
;
171 struct ath_hw
*ah
= sc
->sc_ah
;
172 struct ath_common
*common
= ath9k_hw_common(ah
);
176 ath9k_deinit_channel_context(sc
);
178 mutex_lock(&sc
->mutex
);
180 if (test_bit(ATH_OP_INVALID
, &common
->op_flags
)) {
181 ath_err(common
, "Device not present\n");
186 if (WARN_ON(!wowlan
)) {
187 ath_err(common
, "None of the WoW triggers enabled\n");
192 if (sc
->cur_chan
->nvifs
> 1) {
193 ath_dbg(common
, WOW
, "WoW for multivif is not yet supported\n");
198 if (ath9k_is_chanctx_enabled()) {
199 if (test_bit(ATH_OP_MULTI_CHANNEL
, &common
->op_flags
)) {
201 "Multi-channel WOW is not supported\n");
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");
213 triggers
= ath9k_wow_map_triggers(sc
, wowlan
);
215 ath_dbg(common
, WOW
, "No valid WoW triggers\n");
225 ath9k_stop_btcoex(sc
);
228 * Enable wake up on recieving disassoc/deauth
231 ret
= ath9k_wow_add_disassoc_deauth_pattern(sc
);
234 "Unable to add disassoc/deauth pattern: %d\n", ret
);
238 if (triggers
& AH_WOW_USER_PATTERN_EN
) {
239 ret
= ath9k_wow_add_pattern(sc
, wowlan
);
242 "Unable to add user pattern: %d\n", ret
);
247 spin_lock_bh(&sc
->sc_pcu_lock
);
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
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
);
260 spin_unlock_bh(&sc
->sc_pcu_lock
);
263 * we can now sync irq and kill any running tasklets, since we already
264 * disabled interrupts and not holding a spin lock
266 synchronize_irq(sc
->irq
);
267 tasklet_kill(&sc
->intr_tq
);
269 ath9k_hw_wow_enable(ah
, triggers
);
271 ath9k_ps_restore(sc
);
272 ath_dbg(common
, WOW
, "Suspend with WoW triggers: 0x%x\n", triggers
);
274 set_bit(ATH_OP_WOW_ENABLED
, &common
->op_flags
);
276 mutex_unlock(&sc
->mutex
);
280 int ath9k_resume(struct ieee80211_hw
*hw
)
282 struct ath_softc
*sc
= hw
->priv
;
283 struct ath_hw
*ah
= sc
->sc_ah
;
284 struct ath_common
*common
= ath9k_hw_common(ah
);
287 mutex_lock(&sc
->mutex
);
291 spin_lock_bh(&sc
->sc_pcu_lock
);
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
);
298 spin_unlock_bh(&sc
->sc_pcu_lock
);
300 status
= ath9k_hw_wow_wakeup(ah
);
301 ath_dbg(common
, WOW
, "Resume with WoW status: 0x%x\n", status
);
303 ath_restart_work(sc
);
304 ath9k_start_btcoex(sc
);
306 clear_bit(ATH_OP_WOW_ENABLED
, &common
->op_flags
);
308 ath9k_ps_restore(sc
);
309 mutex_unlock(&sc
->mutex
);
314 void ath9k_set_wakeup(struct ieee80211_hw
*hw
, bool enabled
)
316 struct ath_softc
*sc
= hw
->priv
;
317 struct ath_common
*common
= ath9k_hw_common(sc
->sc_ah
);
319 mutex_lock(&sc
->mutex
);
320 device_set_wakeup_enable(sc
->dev
, enabled
);
321 mutex_unlock(&sc
->mutex
);
323 ath_dbg(common
, WOW
, "WoW wakeup source is %s\n",
324 (enabled
) ? "enabled" : "disabled");
327 void ath9k_init_wow(struct ieee80211_hw
*hw
)
329 struct ath_softc
*sc
= hw
->priv
;
330 struct ath_hw
*ah
= sc
->sc_ah
;
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
;
336 hw
->wiphy
->wowlan
= &ath9k_wowlan_support_legacy
;
338 device_init_wakeup(sc
->dev
, 1);
342 void ath9k_deinit_wow(struct ieee80211_hw
*hw
)
344 struct ath_softc
*sc
= hw
->priv
;
346 if ((sc
->driver_data
& ATH9K_PCI_WOW
) || sc
->force_wow
)
347 device_init_wakeup(sc
->dev
, 0);