aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath9k/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/main.c')
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c338
1 files changed, 7 insertions, 331 deletions
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,