aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath9k/main.c
diff options
context:
space:
mode:
authorMohammed Shafi Shajakhan <mohammed@qca.qualcomm.com>2012-07-10 05:26:52 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-07-12 15:27:17 -0400
commitb11e640aef3e23ef3834ce95b27640d28680c79b (patch)
tree9d350ee2d51a4ad790f49c6f229819371c687a49 /drivers/net/wireless/ath/ath9k/main.c
parentb3ba6c529b80e714b8941a7fa7397272afc685b6 (diff)
ath9k: Add WoW related mac80211 callbacks
add suspend/resume/set_wakeup callbacks to the driver *suspend - bail out only if all the conditions for configuring WoW. is fine, currently multivif case is not handled - check for associated state. - map wow triggers from user space data. - add deauth/disassoc pattern and user defined pattern, for the later a list is maintained. - store the interrupt mask before suspend, enabled beacon miss interrupt for WoW. - configure WoW in the hardware by calling ath9k_hw_wow_enable. *resume - restore the interrupts based on the interrupt mask stored before suspend. - call ath9k_hw_wow_wakeup to configure/restore the hardware. - after wow wakeup clear away WoW events and query the WoW wakeup reason from the status register *set_wakeup - to call 'device_set_wakeup_enable' from cfg80211/mac80211 when wow is configured and as per Rafael/Johannnes the right way to do so rather in the driver suspend/resume call back Cc: Senthil Balasubramanian <senthilb@qca.qualcomm.com> Cc: Rajkumar Manoharan <rmanohar@qca.qualcomm.com> Cc: vadivel@qca.qualcomm.com Signed-off-by: Mohammed Shafi Shajakhan <mohammed@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/main.c')
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c373
1 files changed, 373 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 248e5b24acfa..01a5f1814bc8 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -493,6 +493,17 @@ irqreturn_t ath_isr(int irq, void *dev)
493 if (status & SCHED_INTR) 493 if (status & SCHED_INTR)
494 sched = true; 494 sched = true;
495 495
496#ifdef CONFIG_PM_SLEEP
497 if (status & ATH9K_INT_BMISS) {
498 if (atomic_read(&sc->wow_sleep_proc_intr) == 0) {
499 ath_dbg(common, ANY, "during WoW we got a BMISS\n");
500 atomic_inc(&sc->wow_got_bmiss_intr);
501 atomic_dec(&sc->wow_sleep_proc_intr);
502 }
503 ath_dbg(common, INTERRUPT, "beacon miss interrupt\n");
504 }
505#endif
506
496 /* 507 /*
497 * If a FATAL or RXORN interrupt is received, we have to reset the 508 * If a FATAL or RXORN interrupt is received, we have to reset the
498 * chip immediately. 509 * chip immediately.
@@ -2075,6 +2086,362 @@ static void ath9k_get_et_stats(struct ieee80211_hw *hw,
2075#endif 2086#endif
2076 2087
2077 2088
2089#ifdef CONFIG_PM_SLEEP
2090
2091static void ath9k_wow_map_triggers(struct ath_softc *sc,
2092 struct cfg80211_wowlan *wowlan,
2093 u32 *wow_triggers)
2094{
2095 if (wowlan->disconnect)
2096 *wow_triggers |= AH_WOW_LINK_CHANGE |
2097 AH_WOW_BEACON_MISS;
2098 if (wowlan->magic_pkt)
2099 *wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
2100
2101 if (wowlan->n_patterns)
2102 *wow_triggers |= AH_WOW_USER_PATTERN_EN;
2103
2104 sc->wow_enabled = *wow_triggers;
2105
2106}
2107
2108static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
2109{
2110 struct ath_hw *ah = sc->sc_ah;
2111 struct ath_common *common = ath9k_hw_common(ah);
2112 struct ath9k_hw_capabilities *pcaps = &ah->caps;
2113 int pattern_count = 0;
2114 int i, byte_cnt;
2115 u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
2116 u8 dis_deauth_mask[MAX_PATTERN_SIZE];
2117
2118 memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE);
2119 memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE);
2120
2121 /*
2122 * Create Dissassociate / Deauthenticate packet filter
2123 *
2124 * 2 bytes 2 byte 6 bytes 6 bytes 6 bytes
2125 * +--------------+----------+---------+--------+--------+----
2126 * + Frame Control+ Duration + DA + SA + BSSID +
2127 * +--------------+----------+---------+--------+--------+----
2128 *
2129 * The above is the management frame format for disassociate/
2130 * deauthenticate pattern, from this we need to match the first byte
2131 * of 'Frame Control' and DA, SA, and BSSID fields
2132 * (skipping 2nd byte of FC and Duration feild.
2133 *
2134 * Disassociate pattern
2135 * --------------------
2136 * Frame control = 00 00 1010
2137 * DA, SA, BSSID = x:x:x:x:x:x
2138 * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x
2139 * | x:x:x:x:x:x -- 22 bytes
2140 *
2141 * Deauthenticate pattern
2142 * ----------------------
2143 * Frame control = 00 00 1100
2144 * DA, SA, BSSID = x:x:x:x:x:x
2145 * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x
2146 * | x:x:x:x:x:x -- 22 bytes
2147 */
2148
2149 /* Create Disassociate Pattern first */
2150
2151 byte_cnt = 0;
2152
2153 /* Fill out the mask with all FF's */
2154
2155 for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++)
2156 dis_deauth_mask[i] = 0xff;
2157
2158 /* copy the first byte of frame control field */
2159 dis_deauth_pattern[byte_cnt] = 0xa0;
2160 byte_cnt++;
2161
2162 /* skip 2nd byte of frame control and Duration field */
2163 byte_cnt += 3;
2164
2165 /*
2166 * need not match the destination mac address, it can be a broadcast
2167 * mac address or an unicast to this station
2168 */
2169 byte_cnt += 6;
2170
2171 /* copy the source mac address */
2172 memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
2173
2174 byte_cnt += 6;
2175
2176 /* copy the bssid, its same as the source mac address */
2177
2178 memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
2179
2180 /* Create Disassociate pattern mask */
2181
2182 if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_EXACT) {
2183
2184 if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_DWORD) {
2185 /*
2186 * for AR9280, because of hardware limitation, the
2187 * first 4 bytes have to be matched for all patterns.
2188 * the mask for disassociation and de-auth pattern
2189 * matching need to enable the first 4 bytes.
2190 * also the duration field needs to be filled.
2191 */
2192 dis_deauth_mask[0] = 0xf0;
2193
2194 /*
2195 * fill in duration field
2196 FIXME: what is the exact value ?
2197 */
2198 dis_deauth_pattern[2] = 0xff;
2199 dis_deauth_pattern[3] = 0xff;
2200 } else {
2201 dis_deauth_mask[0] = 0xfe;
2202 }
2203
2204 dis_deauth_mask[1] = 0x03;
2205 dis_deauth_mask[2] = 0xc0;
2206 } else {
2207 dis_deauth_mask[0] = 0xef;
2208 dis_deauth_mask[1] = 0x3f;
2209 dis_deauth_mask[2] = 0x00;
2210 dis_deauth_mask[3] = 0xfc;
2211 }
2212
2213 ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n");
2214
2215 ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
2216 pattern_count, byte_cnt);
2217
2218 pattern_count++;
2219 /*
2220 * for de-authenticate pattern, only the first byte of the frame
2221 * control field gets changed from 0xA0 to 0xC0
2222 */
2223 dis_deauth_pattern[0] = 0xC0;
2224
2225 ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
2226 pattern_count, byte_cnt);
2227
2228}
2229
2230static void ath9k_wow_add_pattern(struct ath_softc *sc,
2231 struct cfg80211_wowlan *wowlan)
2232{
2233 struct ath_hw *ah = sc->sc_ah;
2234 struct ath9k_wow_pattern *wow_pattern = NULL;
2235 struct cfg80211_wowlan_trig_pkt_pattern *patterns = wowlan->patterns;
2236 int mask_len;
2237 s8 i = 0;
2238
2239 if (!wowlan->n_patterns)
2240 return;
2241
2242 /*
2243 * Add the new user configured patterns
2244 */
2245 for (i = 0; i < wowlan->n_patterns; i++) {
2246
2247 wow_pattern = kzalloc(sizeof(*wow_pattern), GFP_KERNEL);
2248
2249 if (!wow_pattern)
2250 return;
2251
2252 /*
2253 * TODO: convert the generic user space pattern to
2254 * appropriate chip specific/802.11 pattern.
2255 */
2256
2257 mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
2258 memset(wow_pattern->pattern_bytes, 0, MAX_PATTERN_SIZE);
2259 memset(wow_pattern->mask_bytes, 0, MAX_PATTERN_SIZE);
2260 memcpy(wow_pattern->pattern_bytes, patterns[i].pattern,
2261 patterns[i].pattern_len);
2262 memcpy(wow_pattern->mask_bytes, patterns[i].mask, mask_len);
2263 wow_pattern->pattern_len = patterns[i].pattern_len;
2264
2265 /*
2266 * just need to take care of deauth and disssoc pattern,
2267 * make sure we don't overwrite them.
2268 */
2269
2270 ath9k_hw_wow_apply_pattern(ah, wow_pattern->pattern_bytes,
2271 wow_pattern->mask_bytes,
2272 i + 2,
2273 wow_pattern->pattern_len);
2274 kfree(wow_pattern);
2275
2276 }
2277
2278}
2279
2280static int ath9k_suspend(struct ieee80211_hw *hw,
2281 struct cfg80211_wowlan *wowlan)
2282{
2283 struct ath_softc *sc = hw->priv;
2284 struct ath_hw *ah = sc->sc_ah;
2285 struct ath_common *common = ath9k_hw_common(ah);
2286 u32 wow_triggers_enabled = 0;
2287 int ret = 0;
2288
2289 mutex_lock(&sc->mutex);
2290
2291 ath_cancel_work(sc);
2292 del_timer_sync(&common->ani.timer);
2293 del_timer_sync(&sc->rx_poll_timer);
2294
2295 if (test_bit(SC_OP_INVALID, &sc->sc_flags)) {
2296 ath_dbg(common, ANY, "Device not present\n");
2297 ret = -EINVAL;
2298 goto fail_wow;
2299 }
2300
2301 if (WARN_ON(!wowlan)) {
2302 ath_dbg(common, WOW, "None of the WoW triggers enabled\n");
2303 ret = -EINVAL;
2304 goto fail_wow;
2305 }
2306
2307 if (!device_can_wakeup(sc->dev)) {
2308 ath_dbg(common, WOW, "device_can_wakeup failed, WoW is not enabled\n");
2309 ret = 1;
2310 goto fail_wow;
2311 }
2312
2313 /*
2314 * none of the sta vifs are associated
2315 * and we are not currently handling multivif
2316 * cases, for instance we have to seperately
2317 * configure 'keep alive frame' for each
2318 * STA.
2319 */
2320
2321 if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
2322 ath_dbg(common, WOW, "None of the STA vifs are associated\n");
2323 ret = 1;
2324 goto fail_wow;
2325 }
2326
2327 if (sc->nvifs > 1) {
2328 ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
2329 ret = 1;
2330 goto fail_wow;
2331 }
2332
2333 ath9k_wow_map_triggers(sc, wowlan, &wow_triggers_enabled);
2334
2335 ath_dbg(common, WOW, "WoW triggers enabled 0x%x\n",
2336 wow_triggers_enabled);
2337
2338 ath9k_ps_wakeup(sc);
2339
2340 ath9k_stop_btcoex(sc);
2341
2342 /*
2343 * Enable wake up on recieving disassoc/deauth
2344 * frame by default.
2345 */
2346 ath9k_wow_add_disassoc_deauth_pattern(sc);
2347
2348 if (wow_triggers_enabled & AH_WOW_USER_PATTERN_EN)
2349 ath9k_wow_add_pattern(sc, wowlan);
2350
2351 spin_lock_bh(&sc->sc_pcu_lock);
2352 /*
2353 * To avoid false wake, we enable beacon miss interrupt only
2354 * when we go to sleep. We save the current interrupt mask
2355 * so we can restore it after the system wakes up
2356 */
2357 sc->wow_intr_before_sleep = ah->imask;
2358 ah->imask &= ~ATH9K_INT_GLOBAL;
2359 ath9k_hw_disable_interrupts(ah);
2360 ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL;
2361 ath9k_hw_set_interrupts(ah);
2362 ath9k_hw_enable_interrupts(ah);
2363
2364 spin_unlock_bh(&sc->sc_pcu_lock);
2365
2366 /*
2367 * we can now sync irq and kill any running tasklets, since we already
2368 * disabled interrupts and not holding a spin lock
2369 */
2370 synchronize_irq(sc->irq);
2371 tasklet_kill(&sc->intr_tq);
2372
2373 ath9k_hw_wow_enable(ah, wow_triggers_enabled);
2374
2375 ath9k_ps_restore(sc);
2376 ath_dbg(common, ANY, "WoW enabled in ath9k\n");
2377 atomic_inc(&sc->wow_sleep_proc_intr);
2378
2379fail_wow:
2380 mutex_unlock(&sc->mutex);
2381 return ret;
2382}
2383
2384static int ath9k_resume(struct ieee80211_hw *hw)
2385{
2386 struct ath_softc *sc = hw->priv;
2387 struct ath_hw *ah = sc->sc_ah;
2388 struct ath_common *common = ath9k_hw_common(ah);
2389 u32 wow_status;
2390
2391 mutex_lock(&sc->mutex);
2392
2393 ath9k_ps_wakeup(sc);
2394
2395 spin_lock_bh(&sc->sc_pcu_lock);
2396
2397 ath9k_hw_disable_interrupts(ah);
2398 ah->imask = sc->wow_intr_before_sleep;
2399 ath9k_hw_set_interrupts(ah);
2400 ath9k_hw_enable_interrupts(ah);
2401
2402 spin_unlock_bh(&sc->sc_pcu_lock);
2403
2404 wow_status = ath9k_hw_wow_wakeup(ah);
2405
2406 if (atomic_read(&sc->wow_got_bmiss_intr) == 0) {
2407 /*
2408 * some devices may not pick beacon miss
2409 * as the reason they woke up so we add
2410 * that here for that shortcoming.
2411 */
2412 wow_status |= AH_WOW_BEACON_MISS;
2413 atomic_dec(&sc->wow_got_bmiss_intr);
2414 ath_dbg(common, ANY, "Beacon miss interrupt picked up during WoW sleep\n");
2415 }
2416
2417 atomic_dec(&sc->wow_sleep_proc_intr);
2418
2419 if (wow_status) {
2420 ath_dbg(common, ANY, "Waking up due to WoW triggers %s with WoW status = %x\n",
2421 ath9k_hw_wow_event_to_string(wow_status), wow_status);
2422 }
2423
2424 ath_restart_work(sc);
2425 ath9k_start_btcoex(sc);
2426
2427 ath9k_ps_restore(sc);
2428 mutex_unlock(&sc->mutex);
2429
2430 return 0;
2431}
2432
2433static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
2434{
2435 struct ath_softc *sc = hw->priv;
2436
2437 mutex_lock(&sc->mutex);
2438 device_init_wakeup(sc->dev, 1);
2439 device_set_wakeup_enable(sc->dev, enabled);
2440 mutex_unlock(&sc->mutex);
2441}
2442
2443#endif
2444
2078struct ieee80211_ops ath9k_ops = { 2445struct ieee80211_ops ath9k_ops = {
2079 .tx = ath9k_tx, 2446 .tx = ath9k_tx,
2080 .start = ath9k_start, 2447 .start = ath9k_start,
@@ -2104,6 +2471,12 @@ struct ieee80211_ops ath9k_ops = {
2104 .set_antenna = ath9k_set_antenna, 2471 .set_antenna = ath9k_set_antenna,
2105 .get_antenna = ath9k_get_antenna, 2472 .get_antenna = ath9k_get_antenna,
2106 2473
2474#ifdef CONFIG_PM_SLEEP
2475 .suspend = ath9k_suspend,
2476 .resume = ath9k_resume,
2477 .set_wakeup = ath9k_set_wakeup,
2478#endif
2479
2107#ifdef CONFIG_ATH9K_DEBUGFS 2480#ifdef CONFIG_ATH9K_DEBUGFS
2108 .get_et_sset_count = ath9k_get_et_sset_count, 2481 .get_et_sset_count = ath9k_get_et_sset_count,
2109 .get_et_stats = ath9k_get_et_stats, 2482 .get_et_stats = ath9k_get_et_stats,