diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/main.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/main.c | 338 |
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 | ||
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, |