From: Mohammed Shafi Shajakhan <mohammed@qca.qualcomm.com>
To: ath9k-devel@lists.ath9k.org
Subject: [ath9k-devel] [PATCH v2 09/10] ath9k: Add WoW related mac80211 callbacks
Date: Mon, 25 Jun 2012 16:28:00 +0530 [thread overview]
Message-ID: <4FE84438.1040007@qca.qualcomm.com> (raw)
In-Reply-To: <1340467194-16930-10-git-send-email-mohammed@qca.qualcomm.com>
Hi John,
please wait, i will send a v3 for this. i missed something while
rebasing with the latest wireless-testing.
On Saturday 23 June 2012 09:29 PM, Mohammed Shafi Shajakhan wrote:
> From: Mohammed Shafi Shajakhan<mohammed@qca.qualcomm.com>
>
> 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 at qca.qualcomm.com
> Signed-off-by: Mohammed Shafi Shajakhan<mohammed@qca.qualcomm.com>
> ---
> drivers/net/wireless/ath/ath9k/ath9k.h | 9 +
> drivers/net/wireless/ath/ath9k/init.c | 1 +
> drivers/net/wireless/ath/ath9k/main.c | 389 ++++++++++++++++++++++++++++++++
> 3 files changed, 399 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
> index 4c530e5..43de1e1 100644
> --- a/drivers/net/wireless/ath/ath9k/ath9k.h
> +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
> @@ -518,6 +518,14 @@ struct ath9k_wow_info {
> struct list_head wow_patterns;
> };
>
> +#ifdef CONFIG_PM_SLEEP
> +void ath_wow_cleanup(struct ath_softc *sc);
> +#else
> +static inline void ath_wow_cleanup(struct ath_softc *sc)
> +{
> +}
> +#endif
> +
> /********************/
> /* LED Control */
> /********************/
> @@ -719,6 +727,7 @@ struct ath_softc {
> struct ath_ant_comb ant_comb;
> u8 ant_tx, ant_rx;
> struct dfs_pattern_detector *dfs_detector;
> + u32 wow_enabled;
>
> #ifdef CONFIG_PM_SLEEP
> bool wow_got_bmiss_intr;
> diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
> index 576f808..a5a4794 100644
> --- a/drivers/net/wireless/ath/ath9k/init.c
> +++ b/drivers/net/wireless/ath/ath9k/init.c
> @@ -885,6 +885,7 @@ void ath9k_deinit_device(struct ath_softc *sc)
> ath9k_ps_restore(sc);
>
> ieee80211_unregister_hw(hw);
> + ath_wow_cleanup(sc);
> ath_rx_cleanup(sc);
> ath_tx_cleanup(sc);
> ath9k_deinit_softc(sc);
> diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
> index 85f9ab4..3029b1c 100644
> --- a/drivers/net/wireless/ath/ath9k/main.c
> +++ b/drivers/net/wireless/ath/ath9k/main.c
> @@ -486,6 +486,17 @@ irqreturn_t ath_isr(int irq, void *dev)
> if (status& SCHED_INTR)
> sched = true;
>
> +#ifdef CONFIG_PM_SLEEP
> + if (status& ATH9K_INT_BMISS) {
> + if (sc->wow_sleep_proc_intr) {
> + ath_dbg(common, ANY, "during WoW we got a BMISS\n");
> + sc->wow_got_bmiss_intr = true;
> + sc->wow_sleep_proc_intr = false;
> + }
> + ath_dbg(common, INTERRUPT, "beacon miss interrupt\n");
> + }
> +#endif
> +
> /*
> * If a FATAL or RXORN interrupt is received, we have to reset the
> * chip immediately.
> @@ -2069,6 +2080,378 @@ static void ath9k_get_et_stats(struct ieee80211_hw *hw,
> #endif
>
>
> +#ifdef CONFIG_PM_SLEEP
> +
> +void ath_wow_cleanup(struct ath_softc *sc)
> +{
> + struct ath9k_wow_info *wow_info =&sc->wow_info;
> + struct ath9k_wow_pattern *wow_pattern = NULL, *tmp;
> +
> + if (!(sc->wow_enabled& AH_WOW_USER_PATTERN_EN))
> + return;
> +
> + list_for_each_entry_safe(wow_pattern, tmp,
> + &wow_info->wow_patterns, list) {
> +
> + list_del(&wow_pattern->list);
> + kfree(wow_pattern);
> + }
> +
> +}
> +
> +static void ath9k_wow_map_triggers(struct ath_softc *sc,
> + struct cfg80211_wowlan *wowlan,
> + u32 *wow_triggers)
> +{
> + if (wowlan->disconnect)
> + *wow_triggers |= AH_WOW_LINK_CHANGE |
> + AH_WOW_BEACON_MISS;
> + if (wowlan->magic_pkt)
> + *wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
> +
> + if (wowlan->n_patterns)
> + *wow_triggers |= AH_WOW_USER_PATTERN_EN;
> +
> + sc->wow_enabled = *wow_triggers;
> +
> +}
> +
> +static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
> +{
> + struct ath_hw *ah = sc->sc_ah;
> + struct ath_common *common = ath9k_hw_common(ah);
> + struct ath9k_hw_capabilities *pcaps =&ah->caps;
> + int pattern_count = 0;
> + int i, byte_cnt;
> + u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
> + u8 dis_deauth_mask[MAX_PATTERN_SIZE];
> +
> + memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE);
> + memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE);
> +
> + /*
> + * Create Dissassociate / Deauthenticate packet filter
> + *
> + * 2 bytes 2 byte 6 bytes 6 bytes 6 bytes
> + * +--------------+----------+---------+--------+--------+----
> + * + Frame Control+ Duration + DA + SA + BSSID +
> + * +--------------+----------+---------+--------+--------+----
> + *
> + * The above is the management frame format for disassociate/
> + * deauthenticate pattern, from this we need to match the first byte
> + * of 'Frame Control' and DA, SA, and BSSID fields
> + * (skipping 2nd byte of FC and Duration feild.
> + *
> + * Disassociate pattern
> + * --------------------
> + * Frame control = 00 00 1010
> + * DA, SA, BSSID = x:x:x:x:x:x
> + * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x
> + * | x:x:x:x:x:x -- 22 bytes
> + *
> + * Deauthenticate pattern
> + * ----------------------
> + * Frame control = 00 00 1100
> + * DA, SA, BSSID = x:x:x:x:x:x
> + * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x
> + * | x:x:x:x:x:x -- 22 bytes
> + */
> +
> + /* Create Disassociate Pattern first */
> +
> + byte_cnt = 0;
> +
> + /* Fill out the mask with all FF's */
> +
> + for (i = 0; i< MAX_PATTERN_MASK_SIZE; i++)
> + dis_deauth_mask[i] = 0xff;
> +
> + /* copy the first byte of frame control field */
> + dis_deauth_pattern[byte_cnt] = 0xa0;
> + byte_cnt++;
> +
> + /* skip 2nd byte of frame control and Duration field */
> + byte_cnt += 3;
> +
> + /*
> + * need not match the destination mac address, it can be a broadcast
> + * mac address or an unicast to this station
> + */
> + byte_cnt += 6;
> +
> + /* copy the source mac address */
> + memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
> +
> + byte_cnt += 6;
> +
> + /* copy the bssid, its same as the source mac address */
> +
> + memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
> +
> + /* Create Disassociate pattern mask */
> +
> + if (pcaps->hw_caps& ATH9K_HW_WOW_PATTERN_MATCH_EXACT) {
> +
> + if (pcaps->hw_caps& ATH9K_HW_WOW_PATTERN_MATCH_DWORD) {
> + /*
> + * for AR9280, because of hardware limitation, the
> + * first 4 bytes have to be matched for all patterns.
> + * the mask for disassociation and de-auth pattern
> + * matching need to enable the first 4 bytes.
> + * also the duration field needs to be filled.
> + */
> + dis_deauth_mask[0] = 0xf0;
> +
> + /*
> + * fill in duration field
> + FIXME: what is the exact value ?
> + */
> + dis_deauth_pattern[2] = 0xff;
> + dis_deauth_pattern[3] = 0xff;
> + } else {
> + dis_deauth_mask[0] = 0xfe;
> + }
> +
> + dis_deauth_mask[1] = 0x03;
> + dis_deauth_mask[2] = 0xc0;
> + } else {
> + dis_deauth_mask[0] = 0xef;
> + dis_deauth_mask[1] = 0x3f;
> + dis_deauth_mask[2] = 0x00;
> + dis_deauth_mask[3] = 0xfc;
> + }
> +
> + ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n");
> +
> + ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
> + pattern_count, byte_cnt);
> +
> + pattern_count++;
> + /*
> + * for de-authenticate pattern, only the first byte of the frame
> + * control field gets changed from 0xA0 to 0xC0
> + */
> + dis_deauth_pattern[0] = 0xC0;
> +
> + ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
> + pattern_count, byte_cnt);
> +
> +}
> +
> +static void ath9k_wow_add_pattern(struct ath_softc *sc,
> + struct cfg80211_wowlan *wowlan)
> +{
> + struct ath_hw *ah = sc->sc_ah;
> + struct ath9k_wow_info *wow_info =&sc->wow_info;
> + struct ath9k_wow_pattern *wow_pattern = NULL;
> + struct cfg80211_wowlan_trig_pkt_pattern *patterns = wowlan->patterns;
> + int mask_len;
> + s8 i = 0;
> +
> + if (!wowlan->n_patterns)
> + return;
> +
> + /*
> + * Clear existing WoW patterns.
> + */
> + ath_wow_cleanup(sc);
> +
> + /*
> + * Add the new user configured patterns
> + */
> + for (i = 0; i< wowlan->n_patterns; i++) {
> +
> + wow_pattern = kzalloc(sizeof(*wow_pattern), GFP_KERNEL);
> +
> + if (!wow_pattern)
> + return;
> +
> + mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
> + memset(wow_pattern->pattern_bytes, 0, MAX_PATTERN_SIZE);
> + memset(wow_pattern->mask_bytes, 0, MAX_PATTERN_SIZE);
> + memcpy(wow_pattern->pattern_bytes, patterns[i].pattern,
> + patterns[i].pattern_len);
> + memcpy(wow_pattern->mask_bytes, patterns[i].mask, mask_len);
> + wow_pattern->pattern_len = patterns[i].pattern_len;
> +
> + list_add_tail(&wow_pattern->list,&wow_info->wow_patterns);
> + wow_info->num_of_patterns = i + 2;
> + /*
> + * just need to take care of deauth and disssoc pattern,
> + * make sure we don't overwrite them.
> + */
> +
> + ath9k_hw_wow_apply_pattern(ah, wow_pattern->pattern_bytes,
> + wow_pattern->mask_bytes,
> + wow_info->num_of_patterns,
> + wow_pattern->pattern_len);
> +
> + }
> +
> +}
> +
> +static int ath9k_suspend(struct ieee80211_hw *hw,
> + struct cfg80211_wowlan *wowlan)
> +{
> + struct ath_softc *sc = hw->priv;
> + struct ath_hw *ah = sc->sc_ah;
> + struct ath_common *common = ath9k_hw_common(ah);
> + u32 wow_triggers_enabled = 0;
> + int ret = 0;
> +
> + mutex_lock(&sc->mutex);
> +
> + ath_cancel_work(sc);
> + del_timer_sync(&common->ani.timer);
> + del_timer_sync(&sc->rx_poll_timer);
> +
> + if (test_bit(SC_OP_INVALID,&sc->sc_flags)) {
> + ath_dbg(common, ANY, "Device not present\n");
> + ret = -EINVAL;
> + goto fail_wow;
> + }
> +
> + if (WARN_ON(!wowlan)) {
> + ath_dbg(common, WOW, "None of the WoW triggers enabled\n");
> + ret = -EINVAL;
> + goto fail_wow;
> + }
> +
> + if (!device_can_wakeup(sc->dev)) {
> + ath_dbg(common, WOW, "device_can_wakeup failed, WoW is not enabled\n");
> + ret = 1;
> + goto fail_wow;
> + }
> +
> + /*
> + * none of the sta vifs are associated
> + * and we are not currently handling multivif
> + * cases, for instance we have to seperately
> + * configure 'keep alive frame' for each
> + * STA.
> + */
> + if (!(sc->sc_flags& SC_OP_PRIM_STA_VIF)) {
the above should be if (!test_bit(SC_OP_PRIM_STA_VIF,
&sc->sc_flags)).sorry i missed it in re-basing with the latest
wireless-testing, quite tricky nothing complained.just found it was
broken due to this :(
> + ath_dbg(common, WOW, "None of the STA vifs are associated\n");
> + ret = 1;
> + goto fail_wow;
> + }
> +
> + if (sc->nvifs> 1) {
> + ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
> + ret = 1;
> + goto fail_wow;
> + }
> +
> + 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_stop_btcoex(sc);
> +
> + /*
> + * Enable wake up on recieving disassoc/deauth
> + * frame by default.
> + */
> + ath9k_wow_add_disassoc_deauth_pattern(sc);
> +
> + if (wow_triggers_enabled& AH_WOW_USER_PATTERN_EN)
> + ath9k_wow_add_pattern(sc, wowlan);
> +
> + spin_lock_bh(&sc->sc_pcu_lock);
> + /*
> + * To avoid false wake, we enable beacon miss interrupt only
> + * when we go to sleep. We save the current interrupt mask
> + * so we can restore it after the system wakes up
> + */
> + sc->wow_intr_before_sleep = ah->imask;
> + ah->imask&= ~ATH9K_INT_GLOBAL;
> + ath9k_hw_disable_interrupts(ah);
> + ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL;
> + ath9k_hw_set_interrupts(ah);
> + ath9k_hw_enable_interrupts(ah);
> +
> + spin_unlock_bh(&sc->sc_pcu_lock);
> +
> + /*
> + * we can now sync irq and kill any running tasklets, since we already
> + * disabled interrupts and not holding a spin lock
> + */
> + synchronize_irq(sc->irq);
> + tasklet_kill(&sc->intr_tq);
> +
> + ath9k_hw_wow_enable(ah, wow_triggers_enabled);
> +
> + ath9k_ps_restore(sc);
> + ath_dbg(common, ANY, "WoW enabled in ath9k\n");
> + sc->wow_sleep_proc_intr = true;
> +
> +fail_wow:
> + mutex_unlock(&sc->mutex);
> + return ret;
> +}
> +
> +static int ath9k_resume(struct ieee80211_hw *hw)
> +{
> + struct ath_softc *sc = hw->priv;
> + struct ath_hw *ah = sc->sc_ah;
> + struct ath_common *common = ath9k_hw_common(ah);
> + u32 wow_status;
> +
> + mutex_lock(&sc->mutex);
> +
> + ath9k_ps_wakeup(sc);
> +
> + spin_lock_bh(&sc->sc_pcu_lock);
> +
> + ath9k_hw_disable_interrupts(ah);
> + ah->imask = sc->wow_intr_before_sleep;
> + ath9k_hw_set_interrupts(ah);
> + ath9k_hw_enable_interrupts(ah);
> +
> + spin_unlock_bh(&sc->sc_pcu_lock);
> +
> + wow_status = ath9k_hw_wow_wakeup(ah);
> +
> + if (sc->wow_got_bmiss_intr) {
> + /*
> + * some devices may not pick beacon miss
> + * as the reason they woke up so we add
> + * that here for that shortcoming.
> + */
> + wow_status |= AH_WOW_BEACON_MISS;
> + sc->wow_got_bmiss_intr = false;
> + ath_dbg(common, ANY, "Beacon miss interrupt picked up during WoW sleep");
> + }
> +
> + if (wow_status) {
> + ath_dbg(common, ANY, "Waking up due to WoW triggers %s with WoW status = %x\n",
> + ath9k_hw_wow_event_to_string(wow_status), wow_status);
> + }
> +
> + ath_restart_work(sc);
> + ath9k_start_btcoex(sc);
> +
> + ath9k_ps_restore(sc);
> + mutex_unlock(&sc->mutex);
> +
> + return 0;
> +}
> +
> +static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
> +{
> + struct ath_softc *sc = hw->priv;
> +
> + mutex_lock(&sc->mutex);
> + device_init_wakeup(sc->dev, 1);
> + device_set_wakeup_enable(sc->dev, enabled);
> + mutex_unlock(&sc->mutex);
> +}
> +
> +#endif
> +
> struct ieee80211_ops ath9k_ops = {
> .tx = ath9k_tx,
> .start = ath9k_start,
> @@ -2098,6 +2481,12 @@ struct ieee80211_ops ath9k_ops = {
> .set_antenna = ath9k_set_antenna,
> .get_antenna = ath9k_get_antenna,
>
> +#ifdef CONFIG_PM_SLEEP
> + .suspend = ath9k_suspend,
> + .resume = ath9k_resume,
> + .set_wakeup = ath9k_set_wakeup,
> +#endif
> +
> #ifdef CONFIG_ATH9K_DEBUGFS
> .get_et_sset_count = ath9k_get_et_sset_count,
> .get_et_stats = ath9k_get_et_stats,
--
thanks,
shafi
WARNING: multiple messages have this Message-ID (diff)
From: Mohammed Shafi Shajakhan <mohammed@qca.qualcomm.com>
To: "John W. Linville" <linville@tuxdriver.com>
Cc: Mohammed Shafi Shajakhan <mohammed@qca.qualcomm.com>,
<linux-wireless@vger.kernel.org>,
Rodriguez Luis <rodrigue@qca.qualcomm.com>,
<ath9k-devel@lists.ath9k.org>,
Rajkumar Manoharan <rmanohar@qca.qualcomm.com>,
Sujith Manoharan <c_manoha@qca.qualcomm.com>,
Bala Shanmugam <bkamatch@qca.qualcomm.com>,
<vadivel@qca.qualcomm.com>, <rhu@qca.qualcomm.com>,
Senthil Balasubramanian <senthilb@qca.qualcomm.com>
Subject: Re: [PATCH v2 09/10] ath9k: Add WoW related mac80211 callbacks
Date: Mon, 25 Jun 2012 16:28:00 +0530 [thread overview]
Message-ID: <4FE84438.1040007@qca.qualcomm.com> (raw)
In-Reply-To: <1340467194-16930-10-git-send-email-mohammed@qca.qualcomm.com>
Hi John,
please wait, i will send a v3 for this. i missed something while
rebasing with the latest wireless-testing.
On Saturday 23 June 2012 09:29 PM, Mohammed Shafi Shajakhan wrote:
> From: Mohammed Shafi Shajakhan<mohammed@qca.qualcomm.com>
>
> 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>
> ---
> drivers/net/wireless/ath/ath9k/ath9k.h | 9 +
> drivers/net/wireless/ath/ath9k/init.c | 1 +
> drivers/net/wireless/ath/ath9k/main.c | 389 ++++++++++++++++++++++++++++++++
> 3 files changed, 399 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
> index 4c530e5..43de1e1 100644
> --- a/drivers/net/wireless/ath/ath9k/ath9k.h
> +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
> @@ -518,6 +518,14 @@ struct ath9k_wow_info {
> struct list_head wow_patterns;
> };
>
> +#ifdef CONFIG_PM_SLEEP
> +void ath_wow_cleanup(struct ath_softc *sc);
> +#else
> +static inline void ath_wow_cleanup(struct ath_softc *sc)
> +{
> +}
> +#endif
> +
> /********************/
> /* LED Control */
> /********************/
> @@ -719,6 +727,7 @@ struct ath_softc {
> struct ath_ant_comb ant_comb;
> u8 ant_tx, ant_rx;
> struct dfs_pattern_detector *dfs_detector;
> + u32 wow_enabled;
>
> #ifdef CONFIG_PM_SLEEP
> bool wow_got_bmiss_intr;
> diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
> index 576f808..a5a4794 100644
> --- a/drivers/net/wireless/ath/ath9k/init.c
> +++ b/drivers/net/wireless/ath/ath9k/init.c
> @@ -885,6 +885,7 @@ void ath9k_deinit_device(struct ath_softc *sc)
> ath9k_ps_restore(sc);
>
> ieee80211_unregister_hw(hw);
> + ath_wow_cleanup(sc);
> ath_rx_cleanup(sc);
> ath_tx_cleanup(sc);
> ath9k_deinit_softc(sc);
> diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
> index 85f9ab4..3029b1c 100644
> --- a/drivers/net/wireless/ath/ath9k/main.c
> +++ b/drivers/net/wireless/ath/ath9k/main.c
> @@ -486,6 +486,17 @@ irqreturn_t ath_isr(int irq, void *dev)
> if (status& SCHED_INTR)
> sched = true;
>
> +#ifdef CONFIG_PM_SLEEP
> + if (status& ATH9K_INT_BMISS) {
> + if (sc->wow_sleep_proc_intr) {
> + ath_dbg(common, ANY, "during WoW we got a BMISS\n");
> + sc->wow_got_bmiss_intr = true;
> + sc->wow_sleep_proc_intr = false;
> + }
> + ath_dbg(common, INTERRUPT, "beacon miss interrupt\n");
> + }
> +#endif
> +
> /*
> * If a FATAL or RXORN interrupt is received, we have to reset the
> * chip immediately.
> @@ -2069,6 +2080,378 @@ static void ath9k_get_et_stats(struct ieee80211_hw *hw,
> #endif
>
>
> +#ifdef CONFIG_PM_SLEEP
> +
> +void ath_wow_cleanup(struct ath_softc *sc)
> +{
> + struct ath9k_wow_info *wow_info =&sc->wow_info;
> + struct ath9k_wow_pattern *wow_pattern = NULL, *tmp;
> +
> + if (!(sc->wow_enabled& AH_WOW_USER_PATTERN_EN))
> + return;
> +
> + list_for_each_entry_safe(wow_pattern, tmp,
> + &wow_info->wow_patterns, list) {
> +
> + list_del(&wow_pattern->list);
> + kfree(wow_pattern);
> + }
> +
> +}
> +
> +static void ath9k_wow_map_triggers(struct ath_softc *sc,
> + struct cfg80211_wowlan *wowlan,
> + u32 *wow_triggers)
> +{
> + if (wowlan->disconnect)
> + *wow_triggers |= AH_WOW_LINK_CHANGE |
> + AH_WOW_BEACON_MISS;
> + if (wowlan->magic_pkt)
> + *wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
> +
> + if (wowlan->n_patterns)
> + *wow_triggers |= AH_WOW_USER_PATTERN_EN;
> +
> + sc->wow_enabled = *wow_triggers;
> +
> +}
> +
> +static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
> +{
> + struct ath_hw *ah = sc->sc_ah;
> + struct ath_common *common = ath9k_hw_common(ah);
> + struct ath9k_hw_capabilities *pcaps =&ah->caps;
> + int pattern_count = 0;
> + int i, byte_cnt;
> + u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
> + u8 dis_deauth_mask[MAX_PATTERN_SIZE];
> +
> + memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE);
> + memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE);
> +
> + /*
> + * Create Dissassociate / Deauthenticate packet filter
> + *
> + * 2 bytes 2 byte 6 bytes 6 bytes 6 bytes
> + * +--------------+----------+---------+--------+--------+----
> + * + Frame Control+ Duration + DA + SA + BSSID +
> + * +--------------+----------+---------+--------+--------+----
> + *
> + * The above is the management frame format for disassociate/
> + * deauthenticate pattern, from this we need to match the first byte
> + * of 'Frame Control' and DA, SA, and BSSID fields
> + * (skipping 2nd byte of FC and Duration feild.
> + *
> + * Disassociate pattern
> + * --------------------
> + * Frame control = 00 00 1010
> + * DA, SA, BSSID = x:x:x:x:x:x
> + * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x
> + * | x:x:x:x:x:x -- 22 bytes
> + *
> + * Deauthenticate pattern
> + * ----------------------
> + * Frame control = 00 00 1100
> + * DA, SA, BSSID = x:x:x:x:x:x
> + * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x
> + * | x:x:x:x:x:x -- 22 bytes
> + */
> +
> + /* Create Disassociate Pattern first */
> +
> + byte_cnt = 0;
> +
> + /* Fill out the mask with all FF's */
> +
> + for (i = 0; i< MAX_PATTERN_MASK_SIZE; i++)
> + dis_deauth_mask[i] = 0xff;
> +
> + /* copy the first byte of frame control field */
> + dis_deauth_pattern[byte_cnt] = 0xa0;
> + byte_cnt++;
> +
> + /* skip 2nd byte of frame control and Duration field */
> + byte_cnt += 3;
> +
> + /*
> + * need not match the destination mac address, it can be a broadcast
> + * mac address or an unicast to this station
> + */
> + byte_cnt += 6;
> +
> + /* copy the source mac address */
> + memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
> +
> + byte_cnt += 6;
> +
> + /* copy the bssid, its same as the source mac address */
> +
> + memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
> +
> + /* Create Disassociate pattern mask */
> +
> + if (pcaps->hw_caps& ATH9K_HW_WOW_PATTERN_MATCH_EXACT) {
> +
> + if (pcaps->hw_caps& ATH9K_HW_WOW_PATTERN_MATCH_DWORD) {
> + /*
> + * for AR9280, because of hardware limitation, the
> + * first 4 bytes have to be matched for all patterns.
> + * the mask for disassociation and de-auth pattern
> + * matching need to enable the first 4 bytes.
> + * also the duration field needs to be filled.
> + */
> + dis_deauth_mask[0] = 0xf0;
> +
> + /*
> + * fill in duration field
> + FIXME: what is the exact value ?
> + */
> + dis_deauth_pattern[2] = 0xff;
> + dis_deauth_pattern[3] = 0xff;
> + } else {
> + dis_deauth_mask[0] = 0xfe;
> + }
> +
> + dis_deauth_mask[1] = 0x03;
> + dis_deauth_mask[2] = 0xc0;
> + } else {
> + dis_deauth_mask[0] = 0xef;
> + dis_deauth_mask[1] = 0x3f;
> + dis_deauth_mask[2] = 0x00;
> + dis_deauth_mask[3] = 0xfc;
> + }
> +
> + ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n");
> +
> + ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
> + pattern_count, byte_cnt);
> +
> + pattern_count++;
> + /*
> + * for de-authenticate pattern, only the first byte of the frame
> + * control field gets changed from 0xA0 to 0xC0
> + */
> + dis_deauth_pattern[0] = 0xC0;
> +
> + ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
> + pattern_count, byte_cnt);
> +
> +}
> +
> +static void ath9k_wow_add_pattern(struct ath_softc *sc,
> + struct cfg80211_wowlan *wowlan)
> +{
> + struct ath_hw *ah = sc->sc_ah;
> + struct ath9k_wow_info *wow_info =&sc->wow_info;
> + struct ath9k_wow_pattern *wow_pattern = NULL;
> + struct cfg80211_wowlan_trig_pkt_pattern *patterns = wowlan->patterns;
> + int mask_len;
> + s8 i = 0;
> +
> + if (!wowlan->n_patterns)
> + return;
> +
> + /*
> + * Clear existing WoW patterns.
> + */
> + ath_wow_cleanup(sc);
> +
> + /*
> + * Add the new user configured patterns
> + */
> + for (i = 0; i< wowlan->n_patterns; i++) {
> +
> + wow_pattern = kzalloc(sizeof(*wow_pattern), GFP_KERNEL);
> +
> + if (!wow_pattern)
> + return;
> +
> + mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
> + memset(wow_pattern->pattern_bytes, 0, MAX_PATTERN_SIZE);
> + memset(wow_pattern->mask_bytes, 0, MAX_PATTERN_SIZE);
> + memcpy(wow_pattern->pattern_bytes, patterns[i].pattern,
> + patterns[i].pattern_len);
> + memcpy(wow_pattern->mask_bytes, patterns[i].mask, mask_len);
> + wow_pattern->pattern_len = patterns[i].pattern_len;
> +
> + list_add_tail(&wow_pattern->list,&wow_info->wow_patterns);
> + wow_info->num_of_patterns = i + 2;
> + /*
> + * just need to take care of deauth and disssoc pattern,
> + * make sure we don't overwrite them.
> + */
> +
> + ath9k_hw_wow_apply_pattern(ah, wow_pattern->pattern_bytes,
> + wow_pattern->mask_bytes,
> + wow_info->num_of_patterns,
> + wow_pattern->pattern_len);
> +
> + }
> +
> +}
> +
> +static int ath9k_suspend(struct ieee80211_hw *hw,
> + struct cfg80211_wowlan *wowlan)
> +{
> + struct ath_softc *sc = hw->priv;
> + struct ath_hw *ah = sc->sc_ah;
> + struct ath_common *common = ath9k_hw_common(ah);
> + u32 wow_triggers_enabled = 0;
> + int ret = 0;
> +
> + mutex_lock(&sc->mutex);
> +
> + ath_cancel_work(sc);
> + del_timer_sync(&common->ani.timer);
> + del_timer_sync(&sc->rx_poll_timer);
> +
> + if (test_bit(SC_OP_INVALID,&sc->sc_flags)) {
> + ath_dbg(common, ANY, "Device not present\n");
> + ret = -EINVAL;
> + goto fail_wow;
> + }
> +
> + if (WARN_ON(!wowlan)) {
> + ath_dbg(common, WOW, "None of the WoW triggers enabled\n");
> + ret = -EINVAL;
> + goto fail_wow;
> + }
> +
> + if (!device_can_wakeup(sc->dev)) {
> + ath_dbg(common, WOW, "device_can_wakeup failed, WoW is not enabled\n");
> + ret = 1;
> + goto fail_wow;
> + }
> +
> + /*
> + * none of the sta vifs are associated
> + * and we are not currently handling multivif
> + * cases, for instance we have to seperately
> + * configure 'keep alive frame' for each
> + * STA.
> + */
> + if (!(sc->sc_flags& SC_OP_PRIM_STA_VIF)) {
the above should be if (!test_bit(SC_OP_PRIM_STA_VIF,
&sc->sc_flags)).sorry i missed it in re-basing with the latest
wireless-testing, quite tricky nothing complained.just found it was
broken due to this :(
> + ath_dbg(common, WOW, "None of the STA vifs are associated\n");
> + ret = 1;
> + goto fail_wow;
> + }
> +
> + if (sc->nvifs> 1) {
> + ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
> + ret = 1;
> + goto fail_wow;
> + }
> +
> + 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_stop_btcoex(sc);
> +
> + /*
> + * Enable wake up on recieving disassoc/deauth
> + * frame by default.
> + */
> + ath9k_wow_add_disassoc_deauth_pattern(sc);
> +
> + if (wow_triggers_enabled& AH_WOW_USER_PATTERN_EN)
> + ath9k_wow_add_pattern(sc, wowlan);
> +
> + spin_lock_bh(&sc->sc_pcu_lock);
> + /*
> + * To avoid false wake, we enable beacon miss interrupt only
> + * when we go to sleep. We save the current interrupt mask
> + * so we can restore it after the system wakes up
> + */
> + sc->wow_intr_before_sleep = ah->imask;
> + ah->imask&= ~ATH9K_INT_GLOBAL;
> + ath9k_hw_disable_interrupts(ah);
> + ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL;
> + ath9k_hw_set_interrupts(ah);
> + ath9k_hw_enable_interrupts(ah);
> +
> + spin_unlock_bh(&sc->sc_pcu_lock);
> +
> + /*
> + * we can now sync irq and kill any running tasklets, since we already
> + * disabled interrupts and not holding a spin lock
> + */
> + synchronize_irq(sc->irq);
> + tasklet_kill(&sc->intr_tq);
> +
> + ath9k_hw_wow_enable(ah, wow_triggers_enabled);
> +
> + ath9k_ps_restore(sc);
> + ath_dbg(common, ANY, "WoW enabled in ath9k\n");
> + sc->wow_sleep_proc_intr = true;
> +
> +fail_wow:
> + mutex_unlock(&sc->mutex);
> + return ret;
> +}
> +
> +static int ath9k_resume(struct ieee80211_hw *hw)
> +{
> + struct ath_softc *sc = hw->priv;
> + struct ath_hw *ah = sc->sc_ah;
> + struct ath_common *common = ath9k_hw_common(ah);
> + u32 wow_status;
> +
> + mutex_lock(&sc->mutex);
> +
> + ath9k_ps_wakeup(sc);
> +
> + spin_lock_bh(&sc->sc_pcu_lock);
> +
> + ath9k_hw_disable_interrupts(ah);
> + ah->imask = sc->wow_intr_before_sleep;
> + ath9k_hw_set_interrupts(ah);
> + ath9k_hw_enable_interrupts(ah);
> +
> + spin_unlock_bh(&sc->sc_pcu_lock);
> +
> + wow_status = ath9k_hw_wow_wakeup(ah);
> +
> + if (sc->wow_got_bmiss_intr) {
> + /*
> + * some devices may not pick beacon miss
> + * as the reason they woke up so we add
> + * that here for that shortcoming.
> + */
> + wow_status |= AH_WOW_BEACON_MISS;
> + sc->wow_got_bmiss_intr = false;
> + ath_dbg(common, ANY, "Beacon miss interrupt picked up during WoW sleep");
> + }
> +
> + if (wow_status) {
> + ath_dbg(common, ANY, "Waking up due to WoW triggers %s with WoW status = %x\n",
> + ath9k_hw_wow_event_to_string(wow_status), wow_status);
> + }
> +
> + ath_restart_work(sc);
> + ath9k_start_btcoex(sc);
> +
> + ath9k_ps_restore(sc);
> + mutex_unlock(&sc->mutex);
> +
> + return 0;
> +}
> +
> +static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
> +{
> + struct ath_softc *sc = hw->priv;
> +
> + mutex_lock(&sc->mutex);
> + device_init_wakeup(sc->dev, 1);
> + device_set_wakeup_enable(sc->dev, enabled);
> + mutex_unlock(&sc->mutex);
> +}
> +
> +#endif
> +
> struct ieee80211_ops ath9k_ops = {
> .tx = ath9k_tx,
> .start = ath9k_start,
> @@ -2098,6 +2481,12 @@ struct ieee80211_ops ath9k_ops = {
> .set_antenna = ath9k_set_antenna,
> .get_antenna = ath9k_get_antenna,
>
> +#ifdef CONFIG_PM_SLEEP
> + .suspend = ath9k_suspend,
> + .resume = ath9k_resume,
> + .set_wakeup = ath9k_set_wakeup,
> +#endif
> +
> #ifdef CONFIG_ATH9K_DEBUGFS
> .get_et_sset_count = ath9k_get_et_sset_count,
> .get_et_stats = ath9k_get_et_stats,
--
thanks,
shafi
next prev parent reply other threads:[~2012-06-25 10:58 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-06-23 15:59 [ath9k-devel] [PATCH v2 00/10] Add support for WOW in ath9k Mohammed Shafi Shajakhan
2012-06-23 15:59 ` Mohammed Shafi Shajakhan
2012-06-23 15:59 ` [ath9k-devel] [PATCH v2 01/10] ath9k_hw: Add register definitions for WoW support Mohammed Shafi Shajakhan
2012-06-23 15:59 ` Mohammed Shafi Shajakhan
2012-06-23 15:59 ` [ath9k-devel] [PATCH v2 02/10] ath9k: Add definitions and structures to support WoW Mohammed Shafi Shajakhan
2012-06-23 15:59 ` Mohammed Shafi Shajakhan
2012-06-23 15:59 ` [ath9k-devel] [PATCH v2 03/10] ath9k_hw: Add WoW hardware capability flags Mohammed Shafi Shajakhan
2012-06-23 15:59 ` Mohammed Shafi Shajakhan
2012-06-23 15:59 ` [ath9k-devel] [PATCH v2 04/10] ath9k_hw: advertise WoW support for capable chipsets Mohammed Shafi Shajakhan
2012-06-23 15:59 ` Mohammed Shafi Shajakhan
2012-06-23 15:59 ` [ath9k-devel] [PATCH v2 05/10] ath9k: advertise supported WoW flags to upper layer Mohammed Shafi Shajakhan
2012-06-23 15:59 ` Mohammed Shafi Shajakhan
2012-06-23 15:59 ` [ath9k-devel] [PATCH v2 06/10] ath9k_hw: INI changes for WoW for AR9002 chipsets Mohammed Shafi Shajakhan
2012-06-23 15:59 ` Mohammed Shafi Shajakhan
2012-06-23 15:59 ` [ath9k-devel] [PATCH v2 07/10] ath9k_hw: Add hardware code for WoW Mohammed Shafi Shajakhan
2012-06-23 15:59 ` Mohammed Shafi Shajakhan
2012-06-23 15:59 ` [ath9k-devel] [PATCH v2 08/10] ath: Add Wake-on-Wireless debug mask Mohammed Shafi Shajakhan
2012-06-23 15:59 ` Mohammed Shafi Shajakhan
2012-06-23 15:59 ` [ath9k-devel] [PATCH v2 09/10] ath9k: Add WoW related mac80211 callbacks Mohammed Shafi Shajakhan
2012-06-23 15:59 ` Mohammed Shafi Shajakhan
2012-06-25 10:58 ` Mohammed Shafi Shajakhan [this message]
2012-06-25 10:58 ` Mohammed Shafi Shajakhan
2012-06-23 15:59 ` [ath9k-devel] [PATCH v2 10/10] ath9k: do not disable hardware while wow is enabled Mohammed Shafi Shajakhan
2012-06-23 15:59 ` Mohammed Shafi Shajakhan
2012-06-25 11:04 ` [ath9k-devel] [PATCH v2 00/10] Add support for WOW in ath9k Mohammed Shafi Shajakhan
2012-06-25 11:04 ` Mohammed Shafi Shajakhan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4FE84438.1040007@qca.qualcomm.com \
--to=mohammed@qca.qualcomm.com \
--cc=ath9k-devel@lists.ath9k.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.