Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (c) 2013 Qualcomm Atheros, Inc.
0003  *
0004  * Permission to use, copy, modify, and/or distribute this software for any
0005  * purpose with or without fee is hereby granted, provided that the above
0006  * copyright notice and this permission notice appear in all copies.
0007  *
0008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
0009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
0010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
0011  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
0012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
0013  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
0014  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
0015  */
0016 
0017 #include "ath9k.h"
0018 
0019 static const struct wiphy_wowlan_support ath9k_wowlan_support_legacy = {
0020     .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
0021     .n_patterns = MAX_NUM_USER_PATTERN,
0022     .pattern_min_len = 1,
0023     .pattern_max_len = MAX_PATTERN_SIZE,
0024 };
0025 
0026 static const struct wiphy_wowlan_support ath9k_wowlan_support = {
0027     .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
0028     .n_patterns = MAX_NUM_PATTERN - 2,
0029     .pattern_min_len = 1,
0030     .pattern_max_len = MAX_PATTERN_SIZE,
0031 };
0032 
0033 static u8 ath9k_wow_map_triggers(struct ath_softc *sc,
0034                  struct cfg80211_wowlan *wowlan)
0035 {
0036     u8 wow_triggers = 0;
0037 
0038     if (wowlan->disconnect)
0039         wow_triggers |= AH_WOW_LINK_CHANGE |
0040                 AH_WOW_BEACON_MISS;
0041     if (wowlan->magic_pkt)
0042         wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
0043 
0044     if (wowlan->n_patterns)
0045         wow_triggers |= AH_WOW_USER_PATTERN_EN;
0046 
0047     return wow_triggers;
0048 }
0049 
0050 static int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
0051 {
0052     struct ath_hw *ah = sc->sc_ah;
0053     struct ath_common *common = ath9k_hw_common(ah);
0054     int pattern_count = 0;
0055     int ret, i, byte_cnt = 0;
0056     u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
0057     u8 dis_deauth_mask[MAX_PATTERN_SIZE];
0058 
0059     memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE);
0060     memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE);
0061 
0062     /*
0063      * Create Dissassociate / Deauthenticate packet filter
0064      *
0065      *     2 bytes        2 byte    6 bytes   6 bytes  6 bytes
0066      *  +--------------+----------+---------+--------+--------+----
0067      *  + Frame Control+ Duration +   DA    +  SA    +  BSSID +
0068      *  +--------------+----------+---------+--------+--------+----
0069      *
0070      * The above is the management frame format for disassociate/
0071      * deauthenticate pattern, from this we need to match the first byte
0072      * of 'Frame Control' and DA, SA, and BSSID fields
0073      * (skipping 2nd byte of FC and Duration feild.
0074      *
0075      * Disassociate pattern
0076      * --------------------
0077      * Frame control = 00 00 1010
0078      * DA, SA, BSSID = x:x:x:x:x:x
0079      * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x
0080      *              | x:x:x:x:x:x  -- 22 bytes
0081      *
0082      * Deauthenticate pattern
0083      * ----------------------
0084      * Frame control = 00 00 1100
0085      * DA, SA, BSSID = x:x:x:x:x:x
0086      * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x
0087      *              | x:x:x:x:x:x  -- 22 bytes
0088      */
0089 
0090     /* Fill out the mask with all FF's */
0091     for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++)
0092         dis_deauth_mask[i] = 0xff;
0093 
0094     /* copy the first byte of frame control field */
0095     dis_deauth_pattern[byte_cnt] = 0xa0;
0096     byte_cnt++;
0097 
0098     /* skip 2nd byte of frame control and Duration field */
0099     byte_cnt += 3;
0100 
0101     /*
0102      * need not match the destination mac address, it can be a broadcast
0103      * mac address or an unicast to this station
0104      */
0105     byte_cnt += 6;
0106 
0107     /* copy the source mac address */
0108     memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
0109 
0110     byte_cnt += 6;
0111 
0112     /* copy the bssid, its same as the source mac address */
0113     memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
0114 
0115     /* Create Disassociate pattern mask */
0116     dis_deauth_mask[0] = 0xfe;
0117     dis_deauth_mask[1] = 0x03;
0118     dis_deauth_mask[2] = 0xc0;
0119 
0120     ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
0121                      pattern_count, byte_cnt);
0122     if (ret)
0123         goto exit;
0124 
0125     pattern_count++;
0126     /*
0127      * for de-authenticate pattern, only the first byte of the frame
0128      * control field gets changed from 0xA0 to 0xC0
0129      */
0130     dis_deauth_pattern[0] = 0xC0;
0131 
0132     ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
0133                      pattern_count, byte_cnt);
0134 exit:
0135     return ret;
0136 }
0137 
0138 static int ath9k_wow_add_pattern(struct ath_softc *sc,
0139                  struct cfg80211_wowlan *wowlan)
0140 {
0141     struct ath_hw *ah = sc->sc_ah;
0142     struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
0143     u8 wow_pattern[MAX_PATTERN_SIZE];
0144     u8 wow_mask[MAX_PATTERN_SIZE];
0145     int mask_len, ret = 0;
0146     s8 i = 0;
0147 
0148     for (i = 0; i < wowlan->n_patterns; i++) {
0149         mask_len = DIV_ROUND_UP(patterns[i].pattern_len, 8);
0150         memset(wow_pattern, 0, MAX_PATTERN_SIZE);
0151         memset(wow_mask, 0, MAX_PATTERN_SIZE);
0152         memcpy(wow_pattern, patterns[i].pattern, patterns[i].pattern_len);
0153         memcpy(wow_mask, patterns[i].mask, mask_len);
0154 
0155         ret = ath9k_hw_wow_apply_pattern(ah,
0156                          wow_pattern,
0157                          wow_mask,
0158                          i + 2,
0159                          patterns[i].pattern_len);
0160         if (ret)
0161             break;
0162     }
0163 
0164     return ret;
0165 }
0166 
0167 int ath9k_suspend(struct ieee80211_hw *hw,
0168           struct cfg80211_wowlan *wowlan)
0169 {
0170     struct ath_softc *sc = hw->priv;
0171     struct ath_hw *ah = sc->sc_ah;
0172     struct ath_common *common = ath9k_hw_common(ah);
0173     u8 triggers;
0174     int ret = 0;
0175 
0176     ath9k_deinit_channel_context(sc);
0177 
0178     mutex_lock(&sc->mutex);
0179 
0180     if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
0181         ath_err(common, "Device not present\n");
0182         ret = -ENODEV;
0183         goto fail_wow;
0184     }
0185 
0186     if (WARN_ON(!wowlan)) {
0187         ath_err(common, "None of the WoW triggers enabled\n");
0188         ret = -EINVAL;
0189         goto fail_wow;
0190     }
0191 
0192     if (sc->cur_chan->nvifs > 1) {
0193         ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
0194         ret = 1;
0195         goto fail_wow;
0196     }
0197 
0198     if (ath9k_is_chanctx_enabled()) {
0199         if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) {
0200             ath_dbg(common, WOW,
0201                 "Multi-channel WOW is not supported\n");
0202             ret = 1;
0203             goto fail_wow;
0204         }
0205     }
0206 
0207     if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
0208         ath_dbg(common, WOW, "None of the STA vifs are associated\n");
0209         ret = 1;
0210         goto fail_wow;
0211     }
0212 
0213     triggers = ath9k_wow_map_triggers(sc, wowlan);
0214     if (!triggers) {
0215         ath_dbg(common, WOW, "No valid WoW triggers\n");
0216         ret = 1;
0217         goto fail_wow;
0218     }
0219 
0220     ath_cancel_work(sc);
0221     ath_stop_ani(sc);
0222 
0223     ath9k_ps_wakeup(sc);
0224 
0225     ath9k_stop_btcoex(sc);
0226 
0227     /*
0228      * Enable wake up on recieving disassoc/deauth
0229      * frame by default.
0230      */
0231     ret = ath9k_wow_add_disassoc_deauth_pattern(sc);
0232     if (ret) {
0233         ath_err(common,
0234             "Unable to add disassoc/deauth pattern: %d\n", ret);
0235         goto fail_wow;
0236     }
0237 
0238     if (triggers & AH_WOW_USER_PATTERN_EN) {
0239         ret = ath9k_wow_add_pattern(sc, wowlan);
0240         if (ret) {
0241             ath_err(common,
0242                 "Unable to add user pattern: %d\n", ret);
0243             goto fail_wow;
0244         }
0245     }
0246 
0247     spin_lock_bh(&sc->sc_pcu_lock);
0248     /*
0249      * To avoid false wake, we enable beacon miss interrupt only
0250      * when we go to sleep. We save the current interrupt mask
0251      * so we can restore it after the system wakes up
0252      */
0253     sc->wow_intr_before_sleep = ah->imask;
0254     ah->imask &= ~ATH9K_INT_GLOBAL;
0255     ath9k_hw_disable_interrupts(ah);
0256     ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL;
0257     ath9k_hw_set_interrupts(ah);
0258     ath9k_hw_enable_interrupts(ah);
0259 
0260     spin_unlock_bh(&sc->sc_pcu_lock);
0261 
0262     /*
0263      * we can now sync irq and kill any running tasklets, since we already
0264      * disabled interrupts and not holding a spin lock
0265      */
0266     synchronize_irq(sc->irq);
0267     tasklet_kill(&sc->intr_tq);
0268 
0269     ath9k_hw_wow_enable(ah, triggers);
0270 
0271     ath9k_ps_restore(sc);
0272     ath_dbg(common, WOW, "Suspend with WoW triggers: 0x%x\n", triggers);
0273 
0274     set_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
0275 fail_wow:
0276     mutex_unlock(&sc->mutex);
0277     return ret;
0278 }
0279 
0280 int ath9k_resume(struct ieee80211_hw *hw)
0281 {
0282     struct ath_softc *sc = hw->priv;
0283     struct ath_hw *ah = sc->sc_ah;
0284     struct ath_common *common = ath9k_hw_common(ah);
0285     u8 status;
0286 
0287     mutex_lock(&sc->mutex);
0288 
0289     ath9k_ps_wakeup(sc);
0290 
0291     spin_lock_bh(&sc->sc_pcu_lock);
0292 
0293     ath9k_hw_disable_interrupts(ah);
0294     ah->imask = sc->wow_intr_before_sleep;
0295     ath9k_hw_set_interrupts(ah);
0296     ath9k_hw_enable_interrupts(ah);
0297 
0298     spin_unlock_bh(&sc->sc_pcu_lock);
0299 
0300     status = ath9k_hw_wow_wakeup(ah);
0301     ath_dbg(common, WOW, "Resume with WoW status: 0x%x\n", status);
0302 
0303     ath_restart_work(sc);
0304     ath9k_start_btcoex(sc);
0305 
0306     clear_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
0307 
0308     ath9k_ps_restore(sc);
0309     mutex_unlock(&sc->mutex);
0310 
0311     return 0;
0312 }
0313 
0314 void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
0315 {
0316     struct ath_softc *sc = hw->priv;
0317     struct ath_common *common = ath9k_hw_common(sc->sc_ah);
0318 
0319     mutex_lock(&sc->mutex);
0320     device_set_wakeup_enable(sc->dev, enabled);
0321     mutex_unlock(&sc->mutex);
0322 
0323     ath_dbg(common, WOW, "WoW wakeup source is %s\n",
0324         (enabled) ? "enabled" : "disabled");
0325 }
0326 
0327 void ath9k_init_wow(struct ieee80211_hw *hw)
0328 {
0329     struct ath_softc *sc = hw->priv;
0330     struct ath_hw *ah = sc->sc_ah;
0331 
0332     if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow) {
0333         if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565_11_OR_LATER(ah))
0334             hw->wiphy->wowlan = &ath9k_wowlan_support;
0335         else
0336             hw->wiphy->wowlan = &ath9k_wowlan_support_legacy;
0337 
0338         device_init_wakeup(sc->dev, 1);
0339     }
0340 }
0341 
0342 void ath9k_deinit_wow(struct ieee80211_hw *hw)
0343 {
0344     struct ath_softc *sc = hw->priv;
0345 
0346     if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow)
0347         device_init_wakeup(sc->dev, 0);
0348 }