Subject: iwlwifi: support beacon filtering To conserve (system) power, this patch enables the beacon filtering feature in iwlwifi firmware. In this mode, the firmware only passes beacon frames to the host when they change, or when approximately five seconds have passed (when testing I found that it was neither a multiple of seconds nor of TUs, but close). The firmware also reports, every 20 beacons, how many were missed, which we use in order to tell mac80211 about lost beacons. Beacon filtering can be disabled by loading iwlcore manually with modprobe iwlcore disable_beacon_filter=1 There was a question about how this affects calibrations, but as it turns out, the calibrations use the statistics notification we get from the microcode after every beacon, not the beacon itself. This notification is sent regardless of the beacon filtering feature, so that unfortunately beacon filtering alone can't significantly help conserving power since the CPU has to process the notification that we get for every beacon. Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-agn.c | 6 ++++ drivers/net/wireless/iwlwifi/iwl-commands.h | 6 +++- drivers/net/wireless/iwlwifi/iwl-power.c | 8 ++++++ drivers/net/wireless/iwlwifi/iwl-power.h | 1 drivers/net/wireless/iwlwifi/iwl-rx.c | 37 +++++++++++++++++++--------- drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 + 6 files changed, 48 insertions(+), 12 deletions(-) --- wireless-testing.orig/drivers/net/wireless/iwlwifi/iwl3945-base.c 2009-11-16 17:12:04.000000000 +0100 +++ wireless-testing/drivers/net/wireless/iwlwifi/iwl3945-base.c 2009-11-17 12:45:21.000000000 +0100 @@ -3900,6 +3900,8 @@ static int iwl3945_setup_mac(struct iwl_ IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS; + if (!disable_beacon_filter) + hw->flags |= IEEE80211_HW_BEACON_FILTER; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | --- wireless-testing.orig/drivers/net/wireless/iwlwifi/iwl-power.c 2009-11-16 17:12:04.000000000 +0100 +++ wireless-testing/drivers/net/wireless/iwlwifi/iwl-power.c 2009-11-17 12:45:21.000000000 +0100 @@ -59,6 +59,11 @@ MODULE_PARM_DESC(no_sleep_autoadjust, "don't automatically adjust sleep level " "according to maximum network latency"); +/* temporary (?) module parameter for beacon filtering */ +bool disable_beacon_filter = true; +module_param(disable_beacon_filter, bool, 0444); +EXPORT_SYMBOL(disable_beacon_filter); /* for iwl3945 */ + /* * This defines the old power levels. They are still used by default * (level 1) and for thermal throttle (levels 3 through 5) @@ -276,6 +281,9 @@ static void iwl_power_fill_sleep_cmd(str if (priv->power_data.pci_pm) cmd->flags |= IWL_POWER_PCI_PM_MSK; + if (!disable_beacon_filter) + cmd->flags |= IWL_POWER_BEACON_FILTER; + cmd->rx_data_timeout = cpu_to_le32(1000 * dynps_ms); cmd->tx_data_timeout = cpu_to_le32(1000 * dynps_ms); --- wireless-testing.orig/drivers/net/wireless/iwlwifi/iwl-commands.h 2009-11-17 12:45:15.000000000 +0100 +++ wireless-testing/drivers/net/wireless/iwlwifi/iwl-commands.h 2009-11-17 12:45:21.000000000 +0100 @@ -2428,6 +2428,9 @@ struct iwl_spectrum_notification { * Fast PD * bit 4 - '1' Put radio to sleep when receiving frame for others * + * Beacon filter + * bit 5 - '1' Pass up beacons only when changed or every 5 seconds + * * Force sleep Modes * bit 31/30- '00' use both mac/xtal sleeps * '01' force Mac sleep @@ -2444,6 +2447,7 @@ struct iwl_spectrum_notification { #define IWL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2)) #define IWL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3)) #define IWL_POWER_FAST_PD cpu_to_le16(BIT(4)) +#define IWL_POWER_BEACON_FILTER cpu_to_le16(BIT(5)) struct iwl3945_powertable_cmd { __le16 flags; @@ -3163,7 +3167,7 @@ struct iwl_notif_statistics { #define CONSECUTIVE_MISSED_BCONS_TH 20 struct iwl_missed_beacon_notif { - __le32 consequtive_missed_beacons; + __le32 consecutive_missed_beacons; __le32 total_missed_becons; __le32 num_expected_beacons; __le32 num_recvd_beacons; --- wireless-testing.orig/drivers/net/wireless/iwlwifi/iwl-rx.c 2009-11-17 12:45:15.000000000 +0100 +++ wireless-testing/drivers/net/wireless/iwlwifi/iwl-rx.c 2009-11-17 12:45:21.000000000 +0100 @@ -499,18 +499,33 @@ void iwl_rx_missed_beacon_notif(struct i { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_missed_beacon_notif *missed_beacon; + struct iwl_missed_beacon_notif *missed_beacon = &pkt->u.missed_beacon; + u32 missed_consecutive; - missed_beacon = &pkt->u.missed_beacon; - if (le32_to_cpu(missed_beacon->consequtive_missed_beacons) > 5) { - IWL_DEBUG_CALIB(priv, "missed bcn cnsq %d totl %d rcd %d expctd %d\n", - le32_to_cpu(missed_beacon->consequtive_missed_beacons), - le32_to_cpu(missed_beacon->total_missed_becons), - le32_to_cpu(missed_beacon->num_recvd_beacons), - le32_to_cpu(missed_beacon->num_expected_beacons)); - if (!test_bit(STATUS_SCANNING, &priv->status)) - iwl_init_sensitivity(priv); - } + missed_consecutive = + le32_to_cpu(missed_beacon->consecutive_missed_beacons); + + if (missed_consecutive) + IWL_DEBUG_CALIB(priv, + "missed bcn cnsq %d totl %d rcd %d expctd %d\n", + le32_to_cpu(missed_beacon->consecutive_missed_beacons), + le32_to_cpu(missed_beacon->total_missed_becons), + le32_to_cpu(missed_beacon->num_recvd_beacons), + le32_to_cpu(missed_beacon->num_expected_beacons)); + + if (missed_consecutive > 5 && !test_bit(STATUS_SCANNING, &priv->status)) + iwl_init_sensitivity(priv); + + /* + * We should only be able to get this notification + * while associated -- and then we have priv->vif. + */ + if (WARN_ON(!priv->vif)) + return; + + if (missed_consecutive && !disable_beacon_filter && + (priv->hw->conf.flags & IEEE80211_CONF_PS)) + ieee80211_beacon_loss(priv->vif); } EXPORT_SYMBOL(iwl_rx_missed_beacon_notif); --- wireless-testing.orig/drivers/net/wireless/iwlwifi/iwl-power.h 2009-11-16 17:12:04.000000000 +0100 +++ wireless-testing/drivers/net/wireless/iwlwifi/iwl-power.h 2009-11-17 12:45:21.000000000 +0100 @@ -142,5 +142,6 @@ void iwl_tt_exit(struct iwl_priv *priv); void iwl_power_initialize(struct iwl_priv *priv); extern bool no_sleep_autoadjust; +extern bool disable_beacon_filter; #endif /* __iwl_power_setting_h__ */ --- wireless-testing.orig/drivers/net/wireless/iwlwifi/iwl-agn.c 2009-11-17 12:45:16.000000000 +0100 +++ wireless-testing/drivers/net/wireless/iwlwifi/iwl-agn.c 2009-11-17 12:45:21.000000000 +0100 @@ -2401,6 +2401,12 @@ static int iwl_setup_mac(struct iwl_priv if (!priv->cfg->broken_powersave) hw->flags |= IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS; + /* + * This is ok -- mac80211 doesn't care about beacon + * filter flag when PS is not enabled. + */ + if (!disable_beacon_filter) + hw->flags |= IEEE80211_HW_BEACON_FILTER; hw->sta_data_size = sizeof(struct iwl_station_priv); hw->wiphy->interface_modes =