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<[email protected]>
>
> 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<[email protected]>
> Cc: Rajkumar Manoharan<[email protected]>
> Cc: [email protected]
> Signed-off-by: Mohammed Shafi Shajakhan<[email protected]>
> ---
>   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
_______________________________________________
ath9k-devel mailing list
[email protected]
https://lists.ath9k.org/mailman/listinfo/ath9k-devel

Reply via email to