diff options
author | Sujith Manoharan <c_manoha@qca.qualcomm.com> | 2013-10-28 02:52:04 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-12-02 14:24:54 -0500 |
commit | e60001e7167536d99bb2026367751704c623f9f8 (patch) | |
tree | 1f8baa634abac0bae7819856e0599880a68e963b | |
parent | bb00b1f786efc4c38d8e1e3e0e1996a59277a90e (diff) |
ath9k: Use CONFIG_ATH9K_WOW
Move the WoW code to wow.c and compile it conditionally
based on CONFIG_ATH9K_WOW.
Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/ath/ath9k/Makefile | 3 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ath9k.h | 28 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hw.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/init.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/main.c | 338 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/wow.c | 341 |
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 | |||
15 | ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o | 15 | ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o |
16 | ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o | 16 | ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o |
17 | ath9k-$(CONFIG_ATH9K_TX99) += tx99.o | 17 | ath9k-$(CONFIG_ATH9K_TX99) += tx99.o |
18 | ath9k-$(CONFIG_ATH9K_WOW) += wow.o | ||
18 | 19 | ||
19 | obj-$(CONFIG_ATH9K) += ath9k.o | 20 | obj-$(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 | ||
43 | ath9k_hw-$(CONFIG_PM_SLEEP) += ar9003_wow.o | 44 | ath9k_hw-$(CONFIG_ATH9K_WOW) += ar9003_wow.o |
44 | 45 | ||
45 | ath9k_hw-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \ | 46 | ath9k_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 | ||
578 | int ath9k_suspend(struct ieee80211_hw *hw, | ||
579 | struct cfg80211_wowlan *wowlan); | ||
580 | int ath9k_resume(struct ieee80211_hw *hw); | ||
581 | void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled); | ||
582 | #else | ||
583 | static inline int ath9k_suspend(struct ieee80211_hw *hw, | ||
584 | struct cfg80211_wowlan *wowlan) | ||
585 | { | ||
586 | return 0; | ||
587 | } | ||
588 | static inline int ath9k_resume(struct ieee80211_hw *hw) | ||
589 | { | ||
590 | return 0; | ||
591 | } | ||
592 | static 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; | |||
984 | u8 ath9k_parse_mpdudensity(u8 mpdudensity); | 1008 | u8 ath9k_parse_mpdudensity(u8 mpdudensity); |
985 | irqreturn_t ath_isr(int irq, void *dev); | 1009 | irqreturn_t ath_isr(int irq, void *dev); |
986 | int ath_reset(struct ath_softc *sc); | 1010 | int ath_reset(struct ath_softc *sc); |
1011 | void ath_cancel_work(struct ath_softc *sc); | ||
1012 | void ath_restart_work(struct ath_softc *sc); | ||
987 | int ath9k_init_device(u16 devid, struct ath_softc *sc, | 1013 | int 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); |
989 | void ath9k_deinit_device(struct ath_softc *sc); | 1015 | void 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 |
1131 | const char *ath9k_hw_wow_event_to_string(u32 wow_event); | 1131 | const char *ath9k_hw_wow_event_to_string(u32 wow_event); |
1132 | void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, | 1132 | void 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 |
880 | static const struct wiphy_wowlan_support ath9k_wowlan_support = { | 880 | static 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 | ||
166 | static void ath_cancel_work(struct ath_softc *sc) | 166 | void 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 | ||
172 | static void ath_restart_work(struct ath_softc *sc) | 172 | void 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 | |||
2026 | static 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 | |||
2043 | static 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 | |||
2137 | static 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 | |||
2187 | static 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 | |||
2286 | fail_wow: | ||
2287 | mutex_unlock(&sc->mutex); | ||
2288 | return ret; | ||
2289 | } | ||
2290 | |||
2291 | static 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 | |||
2340 | static 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 | ||
2351 | static void ath9k_sw_scan_start(struct ieee80211_hw *hw) | 2027 | static 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 | |||
19 | static 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 | |||
36 | static 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 | |||
130 | static 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 | |||
180 | int 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 | |||
279 | fail_wow: | ||
280 | mutex_unlock(&sc->mutex); | ||
281 | return ret; | ||
282 | } | ||
283 | |||
284 | int 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 | |||
333 | void 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 | } | ||