aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/ath/ath9k/Makefile3
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h28
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.h4
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c338
-rw-r--r--drivers/net/wireless/ath/ath9k/wow.c341
6 files changed, 381 insertions, 337 deletions
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index 796686a65b4c..337c459eda28 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -15,6 +15,7 @@ ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
15ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o 15ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o
16ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o 16ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o
17ath9k-$(CONFIG_ATH9K_TX99) += tx99.o 17ath9k-$(CONFIG_ATH9K_TX99) += tx99.o
18ath9k-$(CONFIG_ATH9K_WOW) += wow.o
18 19
19obj-$(CONFIG_ATH9K) += ath9k.o 20obj-$(CONFIG_ATH9K) += ath9k.o
20 21
@@ -40,7 +41,7 @@ ath9k_hw-y:= \
40 ar9003_eeprom.o \ 41 ar9003_eeprom.o \
41 ar9003_paprd.o 42 ar9003_paprd.o
42 43
43ath9k_hw-$(CONFIG_PM_SLEEP) += ar9003_wow.o 44ath9k_hw-$(CONFIG_ATH9K_WOW) += ar9003_wow.o
44 45
45ath9k_hw-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \ 46ath9k_hw-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \
46 ar9003_mci.o 47 ar9003_mci.o
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index b39ffac3b93a..9927dcaa5593 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -570,6 +570,30 @@ static inline void ath_fill_led_pin(struct ath_softc *sc)
570} 570}
571#endif 571#endif
572 572
573/************************/
574/* Wake on Wireless LAN */
575/************************/
576
577#ifdef CONFIG_ATH9K_WOW
578int ath9k_suspend(struct ieee80211_hw *hw,
579 struct cfg80211_wowlan *wowlan);
580int ath9k_resume(struct ieee80211_hw *hw);
581void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled);
582#else
583static inline int ath9k_suspend(struct ieee80211_hw *hw,
584 struct cfg80211_wowlan *wowlan)
585{
586 return 0;
587}
588static inline int ath9k_resume(struct ieee80211_hw *hw)
589{
590 return 0;
591}
592static inline void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
593{
594}
595#endif /* CONFIG_ATH9K_WOW */
596
573/*******************************/ 597/*******************************/
574/* Antenna diversity/combining */ 598/* Antenna diversity/combining */
575/*******************************/ 599/*******************************/
@@ -785,7 +809,7 @@ struct ath_softc {
785 bool tx99_state; 809 bool tx99_state;
786 s16 tx99_power; 810 s16 tx99_power;
787 811
788#ifdef CONFIG_PM_SLEEP 812#ifdef CONFIG_ATH9K_WOW
789 atomic_t wow_got_bmiss_intr; 813 atomic_t wow_got_bmiss_intr;
790 atomic_t wow_sleep_proc_intr; /* in the middle of WoW sleep ? */ 814 atomic_t wow_sleep_proc_intr; /* in the middle of WoW sleep ? */
791 u32 wow_intr_before_sleep; 815 u32 wow_intr_before_sleep;
@@ -984,6 +1008,8 @@ extern bool is_ath9k_unloaded;
984u8 ath9k_parse_mpdudensity(u8 mpdudensity); 1008u8 ath9k_parse_mpdudensity(u8 mpdudensity);
985irqreturn_t ath_isr(int irq, void *dev); 1009irqreturn_t ath_isr(int irq, void *dev);
986int ath_reset(struct ath_softc *sc); 1010int ath_reset(struct ath_softc *sc);
1011void ath_cancel_work(struct ath_softc *sc);
1012void ath_restart_work(struct ath_softc *sc);
987int ath9k_init_device(u16 devid, struct ath_softc *sc, 1013int ath9k_init_device(u16 devid, struct ath_softc *sc,
988 const struct ath_bus_ops *bus_ops); 1014 const struct ath_bus_ops *bus_ops);
989void ath9k_deinit_device(struct ath_softc *sc); 1015void ath9k_deinit_device(struct ath_softc *sc);
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index a2c9a5dbac6b..ecc3a4efe18a 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -921,7 +921,7 @@ struct ath_hw {
921 /* Enterprise mode cap */ 921 /* Enterprise mode cap */
922 u32 ent_mode; 922 u32 ent_mode;
923 923
924#ifdef CONFIG_PM_SLEEP 924#ifdef CONFIG_ATH9K_WOW
925 u32 wow_event_mask; 925 u32 wow_event_mask;
926#endif 926#endif
927 bool is_clk_25mhz; 927 bool is_clk_25mhz;
@@ -1127,7 +1127,7 @@ ath9k_hw_get_btcoex_scheme(struct ath_hw *ah)
1127#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ 1127#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */
1128 1128
1129 1129
1130#ifdef CONFIG_PM_SLEEP 1130#ifdef CONFIG_ATH9K_WOW
1131const char *ath9k_hw_wow_event_to_string(u32 wow_event); 1131const char *ath9k_hw_wow_event_to_string(u32 wow_event);
1132void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, 1132void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
1133 u8 *user_mask, int pattern_count, 1133 u8 *user_mask, int pattern_count,
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 6ef4c0635b3d..cc23436e3401 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -876,7 +876,7 @@ static const struct ieee80211_iface_combination if_comb[] = {
876 } 876 }
877}; 877};
878 878
879#ifdef CONFIG_PM 879#ifdef CONFIG_ATH9K_WOW
880static const struct wiphy_wowlan_support ath9k_wowlan_support = { 880static const struct wiphy_wowlan_support ath9k_wowlan_support = {
881 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, 881 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
882 .n_patterns = MAX_NUM_USER_PATTERN, 882 .n_patterns = MAX_NUM_USER_PATTERN,
@@ -934,7 +934,7 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
934 hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ; 934 hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ;
935 hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; 935 hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
936 936
937#ifdef CONFIG_PM_SLEEP 937#ifdef CONFIG_ATH9K_WOW
938 if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) && 938 if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) &&
939 (sc->driver_data & ATH9K_PCI_WOW) && 939 (sc->driver_data & ATH9K_PCI_WOW) &&
940 device_can_wakeup(sc->dev)) 940 device_can_wakeup(sc->dev))
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 3d1739864350..e54ffdd36e21 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -163,13 +163,13 @@ static void __ath_cancel_work(struct ath_softc *sc)
163#endif 163#endif
164} 164}
165 165
166static void ath_cancel_work(struct ath_softc *sc) 166void ath_cancel_work(struct ath_softc *sc)
167{ 167{
168 __ath_cancel_work(sc); 168 __ath_cancel_work(sc);
169 cancel_work_sync(&sc->hw_reset_work); 169 cancel_work_sync(&sc->hw_reset_work);
170} 170}
171 171
172static void ath_restart_work(struct ath_softc *sc) 172void ath_restart_work(struct ath_softc *sc)
173{ 173{
174 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); 174 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
175 175
@@ -579,7 +579,8 @@ irqreturn_t ath_isr(int irq, void *dev)
579 579
580 goto chip_reset; 580 goto chip_reset;
581 } 581 }
582#ifdef CONFIG_PM_SLEEP 582
583#ifdef CONFIG_ATH9K_WOW
583 if (status & ATH9K_INT_BMISS) { 584 if (status & ATH9K_INT_BMISS) {
584 if (atomic_read(&sc->wow_sleep_proc_intr) == 0) { 585 if (atomic_read(&sc->wow_sleep_proc_intr) == 0) {
585 ath_dbg(common, ANY, "during WoW we got a BMISS\n"); 586 ath_dbg(common, ANY, "during WoW we got a BMISS\n");
@@ -588,6 +589,8 @@ irqreturn_t ath_isr(int irq, void *dev)
588 } 589 }
589 } 590 }
590#endif 591#endif
592
593
591 if (status & ATH9K_INT_SWBA) 594 if (status & ATH9K_INT_SWBA)
592 tasklet_schedule(&sc->bcon_tasklet); 595 tasklet_schedule(&sc->bcon_tasklet);
593 596
@@ -2021,333 +2024,6 @@ static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
2021 return 0; 2024 return 0;
2022} 2025}
2023 2026
2024#ifdef CONFIG_PM_SLEEP
2025
2026static void ath9k_wow_map_triggers(struct ath_softc *sc,
2027 struct cfg80211_wowlan *wowlan,
2028 u32 *wow_triggers)
2029{
2030 if (wowlan->disconnect)
2031 *wow_triggers |= AH_WOW_LINK_CHANGE |
2032 AH_WOW_BEACON_MISS;
2033 if (wowlan->magic_pkt)
2034 *wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
2035
2036 if (wowlan->n_patterns)
2037 *wow_triggers |= AH_WOW_USER_PATTERN_EN;
2038
2039 sc->wow_enabled = *wow_triggers;
2040
2041}
2042
2043static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
2044{
2045 struct ath_hw *ah = sc->sc_ah;
2046 struct ath_common *common = ath9k_hw_common(ah);
2047 int pattern_count = 0;
2048 int i, byte_cnt;
2049 u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
2050 u8 dis_deauth_mask[MAX_PATTERN_SIZE];
2051
2052 memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE);
2053 memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE);
2054
2055 /*
2056 * Create Dissassociate / Deauthenticate packet filter
2057 *
2058 * 2 bytes 2 byte 6 bytes 6 bytes 6 bytes
2059 * +--------------+----------+---------+--------+--------+----
2060 * + Frame Control+ Duration + DA + SA + BSSID +
2061 * +--------------+----------+---------+--------+--------+----
2062 *
2063 * The above is the management frame format for disassociate/
2064 * deauthenticate pattern, from this we need to match the first byte
2065 * of 'Frame Control' and DA, SA, and BSSID fields
2066 * (skipping 2nd byte of FC and Duration feild.
2067 *
2068 * Disassociate pattern
2069 * --------------------
2070 * Frame control = 00 00 1010
2071 * DA, SA, BSSID = x:x:x:x:x:x
2072 * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x
2073 * | x:x:x:x:x:x -- 22 bytes
2074 *
2075 * Deauthenticate pattern
2076 * ----------------------
2077 * Frame control = 00 00 1100
2078 * DA, SA, BSSID = x:x:x:x:x:x
2079 * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x
2080 * | x:x:x:x:x:x -- 22 bytes
2081 */
2082
2083 /* Create Disassociate Pattern first */
2084
2085 byte_cnt = 0;
2086
2087 /* Fill out the mask with all FF's */
2088
2089 for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++)
2090 dis_deauth_mask[i] = 0xff;
2091
2092 /* copy the first byte of frame control field */
2093 dis_deauth_pattern[byte_cnt] = 0xa0;
2094 byte_cnt++;
2095
2096 /* skip 2nd byte of frame control and Duration field */
2097 byte_cnt += 3;
2098
2099 /*
2100 * need not match the destination mac address, it can be a broadcast
2101 * mac address or an unicast to this station
2102 */
2103 byte_cnt += 6;
2104
2105 /* copy the source mac address */
2106 memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
2107
2108 byte_cnt += 6;
2109
2110 /* copy the bssid, its same as the source mac address */
2111
2112 memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
2113
2114 /* Create Disassociate pattern mask */
2115
2116 dis_deauth_mask[0] = 0xfe;
2117 dis_deauth_mask[1] = 0x03;
2118 dis_deauth_mask[2] = 0xc0;
2119
2120 ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n");
2121
2122 ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
2123 pattern_count, byte_cnt);
2124
2125 pattern_count++;
2126 /*
2127 * for de-authenticate pattern, only the first byte of the frame
2128 * control field gets changed from 0xA0 to 0xC0
2129 */
2130 dis_deauth_pattern[0] = 0xC0;
2131
2132 ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
2133 pattern_count, byte_cnt);
2134
2135}
2136
2137static void ath9k_wow_add_pattern(struct ath_softc *sc,
2138 struct cfg80211_wowlan *wowlan)
2139{
2140 struct ath_hw *ah = sc->sc_ah;
2141 struct ath9k_wow_pattern *wow_pattern = NULL;
2142 struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
2143 int mask_len;
2144 s8 i = 0;
2145
2146 if (!wowlan->n_patterns)
2147 return;
2148
2149 /*
2150 * Add the new user configured patterns
2151 */
2152 for (i = 0; i < wowlan->n_patterns; i++) {
2153
2154 wow_pattern = kzalloc(sizeof(*wow_pattern), GFP_KERNEL);
2155
2156 if (!wow_pattern)
2157 return;
2158
2159 /*
2160 * TODO: convert the generic user space pattern to
2161 * appropriate chip specific/802.11 pattern.
2162 */
2163
2164 mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
2165 memset(wow_pattern->pattern_bytes, 0, MAX_PATTERN_SIZE);
2166 memset(wow_pattern->mask_bytes, 0, MAX_PATTERN_SIZE);
2167 memcpy(wow_pattern->pattern_bytes, patterns[i].pattern,
2168 patterns[i].pattern_len);
2169 memcpy(wow_pattern->mask_bytes, patterns[i].mask, mask_len);
2170 wow_pattern->pattern_len = patterns[i].pattern_len;
2171
2172 /*
2173 * just need to take care of deauth and disssoc pattern,
2174 * make sure we don't overwrite them.
2175 */
2176
2177 ath9k_hw_wow_apply_pattern(ah, wow_pattern->pattern_bytes,
2178 wow_pattern->mask_bytes,
2179 i + 2,
2180 wow_pattern->pattern_len);
2181 kfree(wow_pattern);
2182
2183 }
2184
2185}
2186
2187static int ath9k_suspend(struct ieee80211_hw *hw,
2188 struct cfg80211_wowlan *wowlan)
2189{
2190 struct ath_softc *sc = hw->priv;
2191 struct ath_hw *ah = sc->sc_ah;
2192 struct ath_common *common = ath9k_hw_common(ah);
2193 u32 wow_triggers_enabled = 0;
2194 int ret = 0;
2195
2196 mutex_lock(&sc->mutex);
2197
2198 ath_cancel_work(sc);
2199 ath_stop_ani(sc);
2200 del_timer_sync(&sc->rx_poll_timer);
2201
2202 if (test_bit(SC_OP_INVALID, &sc->sc_flags)) {
2203 ath_dbg(common, ANY, "Device not present\n");
2204 ret = -EINVAL;
2205 goto fail_wow;
2206 }
2207
2208 if (WARN_ON(!wowlan)) {
2209 ath_dbg(common, WOW, "None of the WoW triggers enabled\n");
2210 ret = -EINVAL;
2211 goto fail_wow;
2212 }
2213
2214 if (!device_can_wakeup(sc->dev)) {
2215 ath_dbg(common, WOW, "device_can_wakeup failed, WoW is not enabled\n");
2216 ret = 1;
2217 goto fail_wow;
2218 }
2219
2220 /*
2221 * none of the sta vifs are associated
2222 * and we are not currently handling multivif
2223 * cases, for instance we have to seperately
2224 * configure 'keep alive frame' for each
2225 * STA.
2226 */
2227
2228 if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
2229 ath_dbg(common, WOW, "None of the STA vifs are associated\n");
2230 ret = 1;
2231 goto fail_wow;
2232 }
2233
2234 if (sc->nvifs > 1) {
2235 ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
2236 ret = 1;
2237 goto fail_wow;
2238 }
2239
2240 ath9k_wow_map_triggers(sc, wowlan, &wow_triggers_enabled);
2241
2242 ath_dbg(common, WOW, "WoW triggers enabled 0x%x\n",
2243 wow_triggers_enabled);
2244
2245 ath9k_ps_wakeup(sc);
2246
2247 ath9k_stop_btcoex(sc);
2248
2249 /*
2250 * Enable wake up on recieving disassoc/deauth
2251 * frame by default.
2252 */
2253 ath9k_wow_add_disassoc_deauth_pattern(sc);
2254
2255 if (wow_triggers_enabled & AH_WOW_USER_PATTERN_EN)
2256 ath9k_wow_add_pattern(sc, wowlan);
2257
2258 spin_lock_bh(&sc->sc_pcu_lock);
2259 /*
2260 * To avoid false wake, we enable beacon miss interrupt only
2261 * when we go to sleep. We save the current interrupt mask
2262 * so we can restore it after the system wakes up
2263 */
2264 sc->wow_intr_before_sleep = ah->imask;
2265 ah->imask &= ~ATH9K_INT_GLOBAL;
2266 ath9k_hw_disable_interrupts(ah);
2267 ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL;
2268 ath9k_hw_set_interrupts(ah);
2269 ath9k_hw_enable_interrupts(ah);
2270
2271 spin_unlock_bh(&sc->sc_pcu_lock);
2272
2273 /*
2274 * we can now sync irq and kill any running tasklets, since we already
2275 * disabled interrupts and not holding a spin lock
2276 */
2277 synchronize_irq(sc->irq);
2278 tasklet_kill(&sc->intr_tq);
2279
2280 ath9k_hw_wow_enable(ah, wow_triggers_enabled);
2281
2282 ath9k_ps_restore(sc);
2283 ath_dbg(common, ANY, "WoW enabled in ath9k\n");
2284 atomic_inc(&sc->wow_sleep_proc_intr);
2285
2286fail_wow:
2287 mutex_unlock(&sc->mutex);
2288 return ret;
2289}
2290
2291static int ath9k_resume(struct ieee80211_hw *hw)
2292{
2293 struct ath_softc *sc = hw->priv;
2294 struct ath_hw *ah = sc->sc_ah;
2295 struct ath_common *common = ath9k_hw_common(ah);
2296 u32 wow_status;
2297
2298 mutex_lock(&sc->mutex);
2299
2300 ath9k_ps_wakeup(sc);
2301
2302 spin_lock_bh(&sc->sc_pcu_lock);
2303
2304 ath9k_hw_disable_interrupts(ah);
2305 ah->imask = sc->wow_intr_before_sleep;
2306 ath9k_hw_set_interrupts(ah);
2307 ath9k_hw_enable_interrupts(ah);
2308
2309 spin_unlock_bh(&sc->sc_pcu_lock);
2310
2311 wow_status = ath9k_hw_wow_wakeup(ah);
2312
2313 if (atomic_read(&sc->wow_got_bmiss_intr) == 0) {
2314 /*
2315 * some devices may not pick beacon miss
2316 * as the reason they woke up so we add
2317 * that here for that shortcoming.
2318 */
2319 wow_status |= AH_WOW_BEACON_MISS;
2320 atomic_dec(&sc->wow_got_bmiss_intr);
2321 ath_dbg(common, ANY, "Beacon miss interrupt picked up during WoW sleep\n");
2322 }
2323
2324 atomic_dec(&sc->wow_sleep_proc_intr);
2325
2326 if (wow_status) {
2327 ath_dbg(common, ANY, "Waking up due to WoW triggers %s with WoW status = %x\n",
2328 ath9k_hw_wow_event_to_string(wow_status), wow_status);
2329 }
2330
2331 ath_restart_work(sc);
2332 ath9k_start_btcoex(sc);
2333
2334 ath9k_ps_restore(sc);
2335 mutex_unlock(&sc->mutex);
2336
2337 return 0;
2338}
2339
2340static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
2341{
2342 struct ath_softc *sc = hw->priv;
2343
2344 mutex_lock(&sc->mutex);
2345 device_init_wakeup(sc->dev, 1);
2346 device_set_wakeup_enable(sc->dev, enabled);
2347 mutex_unlock(&sc->mutex);
2348}
2349
2350#endif
2351static void ath9k_sw_scan_start(struct ieee80211_hw *hw) 2027static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
2352{ 2028{
2353 struct ath_softc *sc = hw->priv; 2029 struct ath_softc *sc = hw->priv;
@@ -2403,7 +2079,7 @@ struct ieee80211_ops ath9k_ops = {
2403 .set_antenna = ath9k_set_antenna, 2079 .set_antenna = ath9k_set_antenna,
2404 .get_antenna = ath9k_get_antenna, 2080 .get_antenna = ath9k_get_antenna,
2405 2081
2406#ifdef CONFIG_PM_SLEEP 2082#ifdef CONFIG_ATH9K_WOW
2407 .suspend = ath9k_suspend, 2083 .suspend = ath9k_suspend,
2408 .resume = ath9k_resume, 2084 .resume = ath9k_resume,
2409 .set_wakeup = ath9k_set_wakeup, 2085 .set_wakeup = ath9k_set_wakeup,
diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c
new file mode 100644
index 000000000000..e161bbc04dbc
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/wow.c
@@ -0,0 +1,341 @@
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
19static void ath9k_wow_map_triggers(struct ath_softc *sc,
20 struct cfg80211_wowlan *wowlan,
21 u32 *wow_triggers)
22{
23 if (wowlan->disconnect)
24 *wow_triggers |= AH_WOW_LINK_CHANGE |
25 AH_WOW_BEACON_MISS;
26 if (wowlan->magic_pkt)
27 *wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
28
29 if (wowlan->n_patterns)
30 *wow_triggers |= AH_WOW_USER_PATTERN_EN;
31
32 sc->wow_enabled = *wow_triggers;
33
34}
35
36static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
37{
38 struct ath_hw *ah = sc->sc_ah;
39 struct ath_common *common = ath9k_hw_common(ah);
40 int pattern_count = 0;
41 int i, byte_cnt;
42 u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
43 u8 dis_deauth_mask[MAX_PATTERN_SIZE];
44
45 memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE);
46 memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE);
47
48 /*
49 * Create Dissassociate / Deauthenticate packet filter
50 *
51 * 2 bytes 2 byte 6 bytes 6 bytes 6 bytes
52 * +--------------+----------+---------+--------+--------+----
53 * + Frame Control+ Duration + DA + SA + BSSID +
54 * +--------------+----------+---------+--------+--------+----
55 *
56 * The above is the management frame format for disassociate/
57 * deauthenticate pattern, from this we need to match the first byte
58 * of 'Frame Control' and DA, SA, and BSSID fields
59 * (skipping 2nd byte of FC and Duration feild.
60 *
61 * Disassociate pattern
62 * --------------------
63 * Frame control = 00 00 1010
64 * DA, SA, BSSID = x:x:x:x:x:x
65 * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x
66 * | x:x:x:x:x:x -- 22 bytes
67 *
68 * Deauthenticate pattern
69 * ----------------------
70 * Frame control = 00 00 1100
71 * DA, SA, BSSID = x:x:x:x:x:x
72 * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x
73 * | x:x:x:x:x:x -- 22 bytes
74 */
75
76 /* Create Disassociate Pattern first */
77
78 byte_cnt = 0;
79
80 /* Fill out the mask with all FF's */
81
82 for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++)
83 dis_deauth_mask[i] = 0xff;
84
85 /* copy the first byte of frame control field */
86 dis_deauth_pattern[byte_cnt] = 0xa0;
87 byte_cnt++;
88
89 /* skip 2nd byte of frame control and Duration field */
90 byte_cnt += 3;
91
92 /*
93 * need not match the destination mac address, it can be a broadcast
94 * mac address or an unicast to this station
95 */
96 byte_cnt += 6;
97
98 /* copy the source mac address */
99 memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
100
101 byte_cnt += 6;
102
103 /* copy the bssid, its same as the source mac address */
104
105 memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
106
107 /* Create Disassociate pattern mask */
108
109 dis_deauth_mask[0] = 0xfe;
110 dis_deauth_mask[1] = 0x03;
111 dis_deauth_mask[2] = 0xc0;
112
113 ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n");
114
115 ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
116 pattern_count, byte_cnt);
117
118 pattern_count++;
119 /*
120 * for de-authenticate pattern, only the first byte of the frame
121 * control field gets changed from 0xA0 to 0xC0
122 */
123 dis_deauth_pattern[0] = 0xC0;
124
125 ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
126 pattern_count, byte_cnt);
127
128}
129
130static void ath9k_wow_add_pattern(struct ath_softc *sc,
131 struct cfg80211_wowlan *wowlan)
132{
133 struct ath_hw *ah = sc->sc_ah;
134 struct ath9k_wow_pattern *wow_pattern = NULL;
135 struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
136 int mask_len;
137 s8 i = 0;
138
139 if (!wowlan->n_patterns)
140 return;
141
142 /*
143 * Add the new user configured patterns
144 */
145 for (i = 0; i < wowlan->n_patterns; i++) {
146
147 wow_pattern = kzalloc(sizeof(*wow_pattern), GFP_KERNEL);
148
149 if (!wow_pattern)
150 return;
151
152 /*
153 * TODO: convert the generic user space pattern to
154 * appropriate chip specific/802.11 pattern.
155 */
156
157 mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
158 memset(wow_pattern->pattern_bytes, 0, MAX_PATTERN_SIZE);
159 memset(wow_pattern->mask_bytes, 0, MAX_PATTERN_SIZE);
160 memcpy(wow_pattern->pattern_bytes, patterns[i].pattern,
161 patterns[i].pattern_len);
162 memcpy(wow_pattern->mask_bytes, patterns[i].mask, mask_len);
163 wow_pattern->pattern_len = patterns[i].pattern_len;
164
165 /*
166 * just need to take care of deauth and disssoc pattern,
167 * make sure we don't overwrite them.
168 */
169
170 ath9k_hw_wow_apply_pattern(ah, wow_pattern->pattern_bytes,
171 wow_pattern->mask_bytes,
172 i + 2,
173 wow_pattern->pattern_len);
174 kfree(wow_pattern);
175
176 }
177
178}
179
180int ath9k_suspend(struct ieee80211_hw *hw,
181 struct cfg80211_wowlan *wowlan)
182{
183 struct ath_softc *sc = hw->priv;
184 struct ath_hw *ah = sc->sc_ah;
185 struct ath_common *common = ath9k_hw_common(ah);
186 u32 wow_triggers_enabled = 0;
187 int ret = 0;
188
189 mutex_lock(&sc->mutex);
190
191 ath_cancel_work(sc);
192 ath_stop_ani(sc);
193 del_timer_sync(&sc->rx_poll_timer);
194
195 if (test_bit(SC_OP_INVALID, &sc->sc_flags)) {
196 ath_dbg(common, ANY, "Device not present\n");
197 ret = -EINVAL;
198 goto fail_wow;
199 }
200
201 if (WARN_ON(!wowlan)) {
202 ath_dbg(common, WOW, "None of the WoW triggers enabled\n");
203 ret = -EINVAL;
204 goto fail_wow;
205 }
206
207 if (!device_can_wakeup(sc->dev)) {
208 ath_dbg(common, WOW, "device_can_wakeup failed, WoW is not enabled\n");
209 ret = 1;
210 goto fail_wow;
211 }
212
213 /*
214 * none of the sta vifs are associated
215 * and we are not currently handling multivif
216 * cases, for instance we have to seperately
217 * configure 'keep alive frame' for each
218 * STA.
219 */
220
221 if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
222 ath_dbg(common, WOW, "None of the STA vifs are associated\n");
223 ret = 1;
224 goto fail_wow;
225 }
226
227 if (sc->nvifs > 1) {
228 ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
229 ret = 1;
230 goto fail_wow;
231 }
232
233 ath9k_wow_map_triggers(sc, wowlan, &wow_triggers_enabled);
234
235 ath_dbg(common, WOW, "WoW triggers enabled 0x%x\n",
236 wow_triggers_enabled);
237
238 ath9k_ps_wakeup(sc);
239
240 ath9k_stop_btcoex(sc);
241
242 /*
243 * Enable wake up on recieving disassoc/deauth
244 * frame by default.
245 */
246 ath9k_wow_add_disassoc_deauth_pattern(sc);
247
248 if (wow_triggers_enabled & AH_WOW_USER_PATTERN_EN)
249 ath9k_wow_add_pattern(sc, wowlan);
250
251 spin_lock_bh(&sc->sc_pcu_lock);
252 /*
253 * To avoid false wake, we enable beacon miss interrupt only
254 * when we go to sleep. We save the current interrupt mask
255 * so we can restore it after the system wakes up
256 */
257 sc->wow_intr_before_sleep = ah->imask;
258 ah->imask &= ~ATH9K_INT_GLOBAL;
259 ath9k_hw_disable_interrupts(ah);
260 ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL;
261 ath9k_hw_set_interrupts(ah);
262 ath9k_hw_enable_interrupts(ah);
263
264 spin_unlock_bh(&sc->sc_pcu_lock);
265
266 /*
267 * we can now sync irq and kill any running tasklets, since we already
268 * disabled interrupts and not holding a spin lock
269 */
270 synchronize_irq(sc->irq);
271 tasklet_kill(&sc->intr_tq);
272
273 ath9k_hw_wow_enable(ah, wow_triggers_enabled);
274
275 ath9k_ps_restore(sc);
276 ath_dbg(common, ANY, "WoW enabled in ath9k\n");
277 atomic_inc(&sc->wow_sleep_proc_intr);
278
279fail_wow:
280 mutex_unlock(&sc->mutex);
281 return ret;
282}
283
284int ath9k_resume(struct ieee80211_hw *hw)
285{
286 struct ath_softc *sc = hw->priv;
287 struct ath_hw *ah = sc->sc_ah;
288 struct ath_common *common = ath9k_hw_common(ah);
289 u32 wow_status;
290
291 mutex_lock(&sc->mutex);
292
293 ath9k_ps_wakeup(sc);
294
295 spin_lock_bh(&sc->sc_pcu_lock);
296
297 ath9k_hw_disable_interrupts(ah);
298 ah->imask = sc->wow_intr_before_sleep;
299 ath9k_hw_set_interrupts(ah);
300 ath9k_hw_enable_interrupts(ah);
301
302 spin_unlock_bh(&sc->sc_pcu_lock);
303
304 wow_status = ath9k_hw_wow_wakeup(ah);
305
306 if (atomic_read(&sc->wow_got_bmiss_intr) == 0) {
307 /*
308 * some devices may not pick beacon miss
309 * as the reason they woke up so we add
310 * that here for that shortcoming.
311 */
312 wow_status |= AH_WOW_BEACON_MISS;
313 atomic_dec(&sc->wow_got_bmiss_intr);
314 ath_dbg(common, ANY, "Beacon miss interrupt picked up during WoW sleep\n");
315 }
316
317 atomic_dec(&sc->wow_sleep_proc_intr);
318
319 if (wow_status) {
320 ath_dbg(common, ANY, "Waking up due to WoW triggers %s with WoW status = %x\n",
321 ath9k_hw_wow_event_to_string(wow_status), wow_status);
322 }
323
324 ath_restart_work(sc);
325 ath9k_start_btcoex(sc);
326
327 ath9k_ps_restore(sc);
328 mutex_unlock(&sc->mutex);
329
330 return 0;
331}
332
333void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
334{
335 struct ath_softc *sc = hw->priv;
336
337 mutex_lock(&sc->mutex);
338 device_init_wakeup(sc->dev, 1);
339 device_set_wakeup_enable(sc->dev, enabled);
340 mutex_unlock(&sc->mutex);
341}