ath9k: Check WOW triggers properly
This patch makes sure that valid WOW triggers are present before trying to suspend the device. Also, introduce and use ATH_OP_WOW_ENABLED to bypass PCI suspend and clear it in resume(). Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
Родитель
661d25815e
Коммит
249943a221
|
@ -64,6 +64,7 @@ enum ath_op_flags {
|
||||||
ATH_OP_HW_RESET,
|
ATH_OP_HW_RESET,
|
||||||
ATH_OP_SCANNING,
|
ATH_OP_SCANNING,
|
||||||
ATH_OP_MULTI_CHANNEL,
|
ATH_OP_MULTI_CHANNEL,
|
||||||
|
ATH_OP_WOW_ENABLED,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ath_bus_type {
|
enum ath_bus_type {
|
||||||
|
|
|
@ -998,9 +998,12 @@ static int ath_pci_suspend(struct device *device)
|
||||||
struct pci_dev *pdev = to_pci_dev(device);
|
struct pci_dev *pdev = to_pci_dev(device);
|
||||||
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
|
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
|
||||||
struct ath_softc *sc = hw->priv;
|
struct ath_softc *sc = hw->priv;
|
||||||
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
||||||
|
|
||||||
if (sc->wow_enabled)
|
if (test_bit(ATH_OP_WOW_ENABLED, &common->op_flags)) {
|
||||||
|
dev_info(&pdev->dev, "WOW is enabled, bypassing PCI suspend\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* The device has to be moved to FULLSLEEP forcibly.
|
/* The device has to be moved to FULLSLEEP forcibly.
|
||||||
* Otherwise the chip never moved to full sleep,
|
* Otherwise the chip never moved to full sleep,
|
||||||
|
|
|
@ -23,21 +23,21 @@ static const struct wiphy_wowlan_support ath9k_wowlan_support = {
|
||||||
.pattern_max_len = MAX_PATTERN_SIZE,
|
.pattern_max_len = MAX_PATTERN_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ath9k_wow_map_triggers(struct ath_softc *sc,
|
static u8 ath9k_wow_map_triggers(struct ath_softc *sc,
|
||||||
struct cfg80211_wowlan *wowlan,
|
struct cfg80211_wowlan *wowlan)
|
||||||
u32 *wow_triggers)
|
|
||||||
{
|
{
|
||||||
|
u8 wow_triggers = 0;
|
||||||
|
|
||||||
if (wowlan->disconnect)
|
if (wowlan->disconnect)
|
||||||
*wow_triggers |= AH_WOW_LINK_CHANGE |
|
wow_triggers |= AH_WOW_LINK_CHANGE |
|
||||||
AH_WOW_BEACON_MISS;
|
AH_WOW_BEACON_MISS;
|
||||||
if (wowlan->magic_pkt)
|
if (wowlan->magic_pkt)
|
||||||
*wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
|
wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
|
||||||
|
|
||||||
if (wowlan->n_patterns)
|
if (wowlan->n_patterns)
|
||||||
*wow_triggers |= AH_WOW_USER_PATTERN_EN;
|
wow_triggers |= AH_WOW_USER_PATTERN_EN;
|
||||||
|
|
||||||
sc->wow_enabled = *wow_triggers;
|
|
||||||
|
|
||||||
|
return wow_triggers;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
|
static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
|
||||||
|
@ -45,7 +45,7 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
|
||||||
struct ath_hw *ah = sc->sc_ah;
|
struct ath_hw *ah = sc->sc_ah;
|
||||||
struct ath_common *common = ath9k_hw_common(ah);
|
struct ath_common *common = ath9k_hw_common(ah);
|
||||||
int pattern_count = 0;
|
int pattern_count = 0;
|
||||||
int i, byte_cnt;
|
int i, byte_cnt = 0;
|
||||||
u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
|
u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
|
||||||
u8 dis_deauth_mask[MAX_PATTERN_SIZE];
|
u8 dis_deauth_mask[MAX_PATTERN_SIZE];
|
||||||
|
|
||||||
|
@ -80,12 +80,7 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
|
||||||
* | x:x:x:x:x:x -- 22 bytes
|
* | x:x:x:x:x:x -- 22 bytes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Create Disassociate Pattern first */
|
|
||||||
|
|
||||||
byte_cnt = 0;
|
|
||||||
|
|
||||||
/* Fill out the mask with all FF's */
|
/* Fill out the mask with all FF's */
|
||||||
|
|
||||||
for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++)
|
for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++)
|
||||||
dis_deauth_mask[i] = 0xff;
|
dis_deauth_mask[i] = 0xff;
|
||||||
|
|
||||||
|
@ -108,17 +103,13 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
|
||||||
byte_cnt += 6;
|
byte_cnt += 6;
|
||||||
|
|
||||||
/* copy the bssid, its same as the source mac address */
|
/* copy the bssid, its same as the source mac address */
|
||||||
|
|
||||||
memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
|
memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
|
||||||
|
|
||||||
/* Create Disassociate pattern mask */
|
/* Create Disassociate pattern mask */
|
||||||
|
|
||||||
dis_deauth_mask[0] = 0xfe;
|
dis_deauth_mask[0] = 0xfe;
|
||||||
dis_deauth_mask[1] = 0x03;
|
dis_deauth_mask[1] = 0x03;
|
||||||
dis_deauth_mask[2] = 0xc0;
|
dis_deauth_mask[2] = 0xc0;
|
||||||
|
|
||||||
ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n");
|
|
||||||
|
|
||||||
ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
|
ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
|
||||||
pattern_count, byte_cnt);
|
pattern_count, byte_cnt);
|
||||||
|
|
||||||
|
@ -131,7 +122,6 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
|
||||||
|
|
||||||
ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
|
ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
|
||||||
pattern_count, byte_cnt);
|
pattern_count, byte_cnt);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ath9k_wow_add_pattern(struct ath_softc *sc,
|
static void ath9k_wow_add_pattern(struct ath_softc *sc,
|
||||||
|
@ -190,7 +180,7 @@ int ath9k_suspend(struct ieee80211_hw *hw,
|
||||||
struct ath_softc *sc = hw->priv;
|
struct ath_softc *sc = hw->priv;
|
||||||
struct ath_hw *ah = sc->sc_ah;
|
struct ath_hw *ah = sc->sc_ah;
|
||||||
struct ath_common *common = ath9k_hw_common(ah);
|
struct ath_common *common = ath9k_hw_common(ah);
|
||||||
u32 wow_triggers_enabled = 0;
|
u8 triggers;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
ath9k_deinit_channel_context(sc);
|
ath9k_deinit_channel_context(sc);
|
||||||
|
@ -230,14 +220,16 @@ int ath9k_suspend(struct ieee80211_hw *hw,
|
||||||
goto fail_wow;
|
goto fail_wow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
triggers = ath9k_wow_map_triggers(sc, wowlan);
|
||||||
|
if (!triggers) {
|
||||||
|
ath_dbg(common, WOW, "No valid WoW triggers\n");
|
||||||
|
ret = 1;
|
||||||
|
goto fail_wow;
|
||||||
|
}
|
||||||
|
|
||||||
ath_cancel_work(sc);
|
ath_cancel_work(sc);
|
||||||
ath_stop_ani(sc);
|
ath_stop_ani(sc);
|
||||||
|
|
||||||
ath9k_wow_map_triggers(sc, wowlan, &wow_triggers_enabled);
|
|
||||||
|
|
||||||
ath_dbg(common, WOW, "WoW triggers enabled 0x%x\n",
|
|
||||||
wow_triggers_enabled);
|
|
||||||
|
|
||||||
ath9k_ps_wakeup(sc);
|
ath9k_ps_wakeup(sc);
|
||||||
|
|
||||||
ath9k_stop_btcoex(sc);
|
ath9k_stop_btcoex(sc);
|
||||||
|
@ -248,7 +240,7 @@ int ath9k_suspend(struct ieee80211_hw *hw,
|
||||||
*/
|
*/
|
||||||
ath9k_wow_add_disassoc_deauth_pattern(sc);
|
ath9k_wow_add_disassoc_deauth_pattern(sc);
|
||||||
|
|
||||||
if (wow_triggers_enabled & AH_WOW_USER_PATTERN_EN)
|
if (triggers & AH_WOW_USER_PATTERN_EN)
|
||||||
ath9k_wow_add_pattern(sc, wowlan);
|
ath9k_wow_add_pattern(sc, wowlan);
|
||||||
|
|
||||||
spin_lock_bh(&sc->sc_pcu_lock);
|
spin_lock_bh(&sc->sc_pcu_lock);
|
||||||
|
@ -273,12 +265,13 @@ int ath9k_suspend(struct ieee80211_hw *hw,
|
||||||
synchronize_irq(sc->irq);
|
synchronize_irq(sc->irq);
|
||||||
tasklet_kill(&sc->intr_tq);
|
tasklet_kill(&sc->intr_tq);
|
||||||
|
|
||||||
ath9k_hw_wow_enable(ah, wow_triggers_enabled);
|
ath9k_hw_wow_enable(ah, triggers);
|
||||||
|
|
||||||
ath9k_ps_restore(sc);
|
ath9k_ps_restore(sc);
|
||||||
ath_dbg(common, ANY, "WoW enabled in ath9k\n");
|
ath_dbg(common, WOW, "Suspend with WoW triggers: 0x%x\n", triggers);
|
||||||
atomic_inc(&sc->wow_sleep_proc_intr);
|
atomic_inc(&sc->wow_sleep_proc_intr);
|
||||||
|
|
||||||
|
set_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
|
||||||
fail_wow:
|
fail_wow:
|
||||||
mutex_unlock(&sc->mutex);
|
mutex_unlock(&sc->mutex);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -327,6 +320,8 @@ int ath9k_resume(struct ieee80211_hw *hw)
|
||||||
ath_restart_work(sc);
|
ath_restart_work(sc);
|
||||||
ath9k_start_btcoex(sc);
|
ath9k_start_btcoex(sc);
|
||||||
|
|
||||||
|
clear_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
|
||||||
|
|
||||||
ath9k_ps_restore(sc);
|
ath9k_ps_restore(sc);
|
||||||
mutex_unlock(&sc->mutex);
|
mutex_unlock(&sc->mutex);
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче