From 420e7fabd9c6d907280ed6b3e40eef425c5d8d8d Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Thu, 11 Dec 2008 22:04:19 +0100 Subject: nl80211: Add signal strength and bandwith to nl80211station info This patch adds signal strength and transmission bitrate to the station_info of nl80211. Signed-off-by: Henning Rogge Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a0c0bf19496c..65e03ac93109 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -169,6 +169,9 @@ struct station_parameters { * @STATION_INFO_LLID: @llid filled * @STATION_INFO_PLID: @plid filled * @STATION_INFO_PLINK_STATE: @plink_state filled + * @STATION_INFO_SIGNAL: @signal filled + * @STATION_INFO_TX_BITRATE: @tx_bitrate fields are filled + * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -177,6 +180,39 @@ enum station_info_flags { STATION_INFO_LLID = 1<<3, STATION_INFO_PLID = 1<<4, STATION_INFO_PLINK_STATE = 1<<5, + STATION_INFO_SIGNAL = 1<<6, + STATION_INFO_TX_BITRATE = 1<<7, +}; + +/** + * enum station_info_rate_flags - bitrate info flags + * + * Used by the driver to indicate the specific rate transmission + * type for 802.11n transmissions. + * + * @RATE_INFO_FLAGS_MCS: @tx_bitrate_mcs filled + * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 Mhz width transmission + * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval + */ +enum rate_info_flags { + RATE_INFO_FLAGS_MCS = 1<<0, + RATE_INFO_FLAGS_40_MHZ_WIDTH = 1<<1, + RATE_INFO_FLAGS_SHORT_GI = 1<<2, +}; + +/** + * struct rate_info - bitrate information + * + * Information about a receiving or transmitting bitrate + * + * @flags: bitflag of flags from &enum rate_info_flags + * @mcs: mcs index if struct describes a 802.11n bitrate + * @legacy: bitrate in 100kbit/s for 802.11abg + */ +struct rate_info { + u8 flags; + u8 mcs; + u16 legacy; }; /** @@ -191,6 +227,8 @@ enum station_info_flags { * @llid: mesh local link id * @plid: mesh peer link id * @plink_state: mesh peer link state + * @signal: signal strength of last received packet in dBm + * @txrate: current unicast bitrate to this station */ struct station_info { u32 filled; @@ -200,6 +238,8 @@ struct station_info { u16 llid; u16 plid; u8 plink_state; + s8 signal; + struct rate_info txrate; }; /** -- cgit v1.2.3 From 094d05dc32fc2930e381189a942016e5561775d9 Mon Sep 17 00:00:00 2001 From: Sujith Date: Fri, 12 Dec 2008 11:57:43 +0530 Subject: mac80211: Fix HT channel selection HT management is done differently for AP and STA modes, unify to just the ->config() callback since HT is fundamentally a PHY property and cannot be per-BSS. Rename enum nl80211_sec_chan_offset as nl80211_channel_type to denote the channel type ( NO_HT, HT20, HT40+, HT40- ). Signed-off-by: Johannes Berg Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/main.c | 123 ++++++++------------------------- drivers/net/wireless/iwlwifi/iwl-agn.c | 18 +++-- drivers/net/wireless/mac80211_hwsim.c | 6 +- include/linux/nl80211.h | 22 +++--- include/net/cfg80211.h | 2 +- include/net/mac80211.h | 9 +-- net/mac80211/cfg.c | 4 +- net/mac80211/ht.c | 35 +++++++--- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/main.c | 27 +++----- net/mac80211/mlme.c | 1 + net/mac80211/util.c | 2 +- net/wireless/nl80211.c | 27 ++++---- 13 files changed, 109 insertions(+), 169 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 02e1771bb274..e22fea18bad6 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -623,37 +623,40 @@ static int ath_get_channel(struct ath_softc *sc, return -1; } -/* ext_chan_offset: (-1, 0, 1) (below, none, above) */ - static u32 ath_get_extchanmode(struct ath_softc *sc, struct ieee80211_channel *chan, - int ext_chan_offset, - enum ath9k_ht_macmode tx_chan_width) + enum nl80211_channel_type channel_type) { u32 chanmode = 0; switch (chan->band) { case IEEE80211_BAND_2GHZ: - if ((ext_chan_offset == 0) && - (tx_chan_width == ATH9K_HT_MACMODE_20)) + switch(channel_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: chanmode = CHANNEL_G_HT20; - if ((ext_chan_offset == 1) && - (tx_chan_width == ATH9K_HT_MACMODE_2040)) + break; + case NL80211_CHAN_HT40PLUS: chanmode = CHANNEL_G_HT40PLUS; - if ((ext_chan_offset == -1) && - (tx_chan_width == ATH9K_HT_MACMODE_2040)) + break; + case NL80211_CHAN_HT40MINUS: chanmode = CHANNEL_G_HT40MINUS; + break; + } break; case IEEE80211_BAND_5GHZ: - if ((ext_chan_offset == 0) && - (tx_chan_width == ATH9K_HT_MACMODE_20)) + switch(channel_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: chanmode = CHANNEL_A_HT20; - if ((ext_chan_offset == 1) && - (tx_chan_width == ATH9K_HT_MACMODE_2040)) + break; + case NL80211_CHAN_HT40PLUS: chanmode = CHANNEL_A_HT40PLUS; - if ((ext_chan_offset == -1) && - (tx_chan_width == ATH9K_HT_MACMODE_2040)) + break; + case NL80211_CHAN_HT40MINUS: chanmode = CHANNEL_A_HT40MINUS; + break; + } break; default: break; @@ -829,45 +832,15 @@ static void setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info) ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; } -static void ath9k_ht_conf(struct ath_softc *sc, - struct ieee80211_bss_conf *bss_conf) -{ - if (sc->hw->conf.ht.enabled) { - if (bss_conf->ht.width_40_ok) - sc->tx_chan_width = ATH9K_HT_MACMODE_2040; - else - sc->tx_chan_width = ATH9K_HT_MACMODE_20; - - ath9k_hw_set11nmac2040(sc->sc_ah, sc->tx_chan_width); - - DPRINTF(sc, ATH_DBG_CONFIG, - "BSS Changed HT, chanwidth: %d\n", sc->tx_chan_width); - } -} - -static inline int ath_sec_offset(u8 ext_offset) -{ - if (ext_offset == IEEE80211_HT_PARAM_CHA_SEC_NONE) - return 0; - else if (ext_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) - return 1; - else if (ext_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW) - return -1; - - return 0; -} - static void ath9k_bss_assoc_info(struct ath_softc *sc, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf) { - struct ieee80211_hw *hw = sc->hw; - struct ieee80211_channel *curchan = hw->conf.channel; struct ath_vap *avp = (void *)vif->drv_priv; - int pos; if (bss_conf->assoc) { - DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info ASSOC %d\n", bss_conf->aid); + DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info ASSOC %d, bssid: %pM\n", + bss_conf->aid, sc->sc_curbssid); /* New association, store aid */ if (avp->av_opmode == NL80211_IFTYPE_STATION) { @@ -886,40 +859,6 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc, sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; sc->sc_halstats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER; - /* Update chainmask */ - ath_update_chainmask(sc, hw->conf.ht.enabled); - - DPRINTF(sc, ATH_DBG_CONFIG, - "bssid %pM aid 0x%x\n", - sc->sc_curbssid, sc->sc_curaid); - - pos = ath_get_channel(sc, curchan); - if (pos == -1) { - DPRINTF(sc, ATH_DBG_FATAL, - "Invalid channel: %d\n", curchan->center_freq); - return; - } - - if (hw->conf.ht.enabled) { - int offset = - ath_sec_offset(bss_conf->ht.secondary_channel_offset); - sc->tx_chan_width = (bss_conf->ht.width_40_ok) ? - ATH9K_HT_MACMODE_2040 : ATH9K_HT_MACMODE_20; - - sc->sc_ah->ah_channels[pos].chanmode = - ath_get_extchanmode(sc, curchan, - offset, sc->tx_chan_width); - } else { - sc->sc_ah->ah_channels[pos].chanmode = - (curchan->band == IEEE80211_BAND_2GHZ) ? - CHANNEL_G : CHANNEL_A; - } - - /* set h/w channel */ - if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0) - DPRINTF(sc, ATH_DBG_FATAL, "Unable to set channel: %d\n", - curchan->center_freq); - /* Start ANI */ mod_timer(&sc->sc_ani.timer, jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL)); @@ -2146,7 +2085,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) struct ath_softc *sc = hw->priv; struct ieee80211_conf *conf = &hw->conf; - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + if (changed & (IEEE80211_CONF_CHANGE_CHANNEL | + IEEE80211_CONF_CHANGE_HT)) { struct ieee80211_channel *curchan = hw->conf.channel; int pos; @@ -2165,25 +2105,23 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) (curchan->band == IEEE80211_BAND_2GHZ) ? CHANNEL_G : CHANNEL_A; - if ((sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP) && - (conf->ht.enabled)) { - sc->tx_chan_width = (!!conf->ht.sec_chan_offset) ? - ATH9K_HT_MACMODE_2040 : ATH9K_HT_MACMODE_20; + if (conf->ht.enabled) { + if (conf->ht.channel_type == NL80211_CHAN_HT40PLUS || + conf->ht.channel_type == NL80211_CHAN_HT40MINUS) + sc->tx_chan_width = ATH9K_HT_MACMODE_2040; sc->sc_ah->ah_channels[pos].chanmode = ath_get_extchanmode(sc, curchan, - conf->ht.sec_chan_offset, - sc->tx_chan_width); + conf->ht.channel_type); } if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0) { DPRINTF(sc, ATH_DBG_FATAL, "Unable to set channel\n"); return -EINVAL; } - } - if (changed & IEEE80211_CONF_CHANGE_HT) ath_update_chainmask(sc, conf->ht.enabled); + } if (changed & IEEE80211_CONF_CHANGE_POWER) sc->sc_config.txpowlimit = 2 * conf->power_level; @@ -2417,9 +2355,6 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, sc->sc_flags &= ~SC_OP_PROTECT_ENABLE; } - if (changed & BSS_CHANGED_HT) - ath9k_ht_conf(sc, bss_conf); - if (changed & BSS_CHANGED_ASSOC) { DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", bss_conf->assoc); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 2f5e86e12916..bbc1c8052ffa 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -515,19 +515,27 @@ static void iwl_ht_conf(struct iwl_priv *priv, iwl_conf->supported_chan_width = !!(ht_conf->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40); - iwl_conf->extension_chan_offset = bss_conf->ht.secondary_channel_offset; + /* + * XXX: The HT configuration needs to be moved into iwl_mac_config() + * to be done there correctly. + */ + + iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + if (priv->hw->conf.ht.channel_type == NL80211_CHAN_HT40MINUS) + iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + else if(priv->hw->conf.ht.channel_type == NL80211_CHAN_HT40PLUS) + iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + /* If no above or below channel supplied disable FAT channel */ if (iwl_conf->extension_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_ABOVE && - iwl_conf->extension_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_BELOW) { - iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + iwl_conf->extension_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_BELOW) iwl_conf->supported_chan_width = 0; - } iwl_conf->sm_ps = (u8)((ht_conf->cap & IEEE80211_HT_CAP_SM_PS) >> 2); memcpy(&iwl_conf->mcs, &ht_conf->mcs, 16); - iwl_conf->tx_chan_width = bss_conf->ht.width_40_ok; + iwl_conf->tx_chan_width = iwl_conf->supported_chan_width != 0; iwl_conf->ht_protection = bss_conf->ht.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; iwl_conf->non_GF_STA_present = diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index fd5a537ac51d..f83d69e813d3 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -495,11 +495,9 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, } if (changed & BSS_CHANGED_HT) { - printk(KERN_DEBUG " %s: HT: sec_ch_offs=%d width_40_ok=%d " - "op_mode=%d\n", + printk(KERN_DEBUG " %s: HT: op_mode=0x%x\n", wiphy_name(hw->wiphy), - info->ht.secondary_channel_offset, - info->ht.width_40_ok, info->ht.operation_mode); + info->ht.operation_mode); } if (changed & BSS_CHANGED_BASIC_RATES) { diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 7501acfcfdc4..e86ed59f9ad5 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -201,13 +201,13 @@ enum nl80211_commands { * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz - * @NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET: included with NL80211_ATTR_WIPHY_FREQ + * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): - * NL80211_SEC_CHAN_NO_HT = HT not allowed (i.e., same as not including + * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including * this attribute) - * NL80211_SEC_CHAN_DISABLED = HT20 only - * NL80211_SEC_CHAN_BELOW = secondary channel is below the primary channel - * NL80211_SEC_CHAN_ABOVE = secondary channel is above the primary channel + * NL80211_CHAN_HT20 = HT20 only + * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel + * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel * * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on * @NL80211_ATTR_IFNAME: network interface name @@ -344,7 +344,7 @@ enum nl80211_attrs { NL80211_ATTR_WIPHY_TXQ_PARAMS, NL80211_ATTR_WIPHY_FREQ, - NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET, + NL80211_ATTR_WIPHY_CHANNEL_TYPE, /* add attributes here, update the policy in nl80211.c */ @@ -805,10 +805,10 @@ enum nl80211_txq_q { NL80211_TXQ_Q_BK }; -enum nl80211_sec_chan_offset { - NL80211_SEC_CHAN_NO_HT /* No HT */, - NL80211_SEC_CHAN_DISABLED /* HT20 only */, - NL80211_SEC_CHAN_BELOW /* HT40- */, - NL80211_SEC_CHAN_ABOVE /* HT40+ */ +enum nl80211_channel_type { + NL80211_CHAN_NO_HT, + NL80211_CHAN_HT20, + NL80211_CHAN_HT40MINUS, + NL80211_CHAN_HT40PLUS }; #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 65e03ac93109..23c0ab74ded6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -563,7 +563,7 @@ struct cfg80211_ops { int (*set_channel)(struct wiphy *wiphy, struct ieee80211_channel *chan, - enum nl80211_sec_chan_offset); + enum nl80211_channel_type channel_type); }; /* temporary wext handlers */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 046ce692a906..22ae72c58d76 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -165,14 +165,9 @@ enum ieee80211_bss_change { /** * struct ieee80211_bss_ht_conf - BSS's changing HT configuration - * @secondary_channel_offset: secondary channel offset, uses - * %IEEE80211_HT_PARAM_CHA_SEC_ values - * @width_40_ok: indicates that 40 MHz bandwidth may be used for TX * @operation_mode: HT operation mode (like in &struct ieee80211_ht_info) */ struct ieee80211_bss_ht_conf { - u8 secondary_channel_offset; - bool width_40_ok; u16 operation_mode; }; @@ -508,9 +503,7 @@ static inline int __deprecated __IEEE80211_CONF_SHORT_SLOT_TIME(void) struct ieee80211_ht_conf { bool enabled; - int sec_chan_offset; /* 0 = HT40 disabled; -1 = HT40 enabled, secondary - * channel below primary; 1 = HT40 enabled, - * secondary channel above primary */ + enum nl80211_channel_type channel_type; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 23b5eeaf7bc5..3ea0884c9432 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1122,12 +1122,12 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy, static int ieee80211_set_channel(struct wiphy *wiphy, struct ieee80211_channel *chan, - enum nl80211_sec_chan_offset sec_chan_offset) + enum nl80211_channel_type channel_type) { struct ieee80211_local *local = wiphy_priv(wiphy); local->oper_channel = chan; - local->oper_sec_chan_offset = sec_chan_offset; + local->oper_channel_type = channel_type; return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); } diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index a1eed7032c9b..5f510a13b9f0 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -98,6 +98,7 @@ u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss_ht_conf ht; u32 changed = 0; bool enable_ht = true, ht_changed; + enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; @@ -112,24 +113,36 @@ u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, ieee80211_channel_to_frequency(hti->control_chan)) enable_ht = false; - /* - * XXX: This is totally incorrect when there are multiple virtual - * interfaces, needs to be fixed later. - */ - ht_changed = local->hw.conf.ht.enabled != enable_ht; + if (enable_ht) { + channel_type = NL80211_CHAN_HT20; + + if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) && + (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) && + (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) { + switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + channel_type = NL80211_CHAN_HT40PLUS; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + channel_type = NL80211_CHAN_HT40MINUS; + break; + } + } + } + + ht_changed = local->hw.conf.ht.enabled != enable_ht || + channel_type != local->hw.conf.ht.channel_type; + + local->oper_channel_type = channel_type; local->hw.conf.ht.enabled = enable_ht; + if (ht_changed) ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_HT); /* disable HT */ if (!enable_ht) return 0; - ht.secondary_channel_offset = - hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET; - ht.width_40_ok = - !(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) && - (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) && - (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY); + ht.operation_mode = le16_to_cpu(hti->operation_mode); /* if bss configuration changed store the new one */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6f59e11d7b33..a7dabaecfc72 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -625,7 +625,7 @@ struct ieee80211_local { struct delayed_work scan_work; struct ieee80211_sub_if_data *scan_sdata; struct ieee80211_channel *oper_channel, *scan_channel; - enum nl80211_sec_chan_offset oper_sec_chan_offset; + enum nl80211_channel_type oper_channel_type; u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; size_t scan_ssid_len; struct list_head bss_list; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 6d8710327d14..a0371caf01ce 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -195,37 +195,30 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) struct ieee80211_channel *chan; int ret = 0; int power; - enum nl80211_sec_chan_offset sec_chan_offset; + enum nl80211_channel_type channel_type; might_sleep(); if (local->sw_scanning) { chan = local->scan_channel; - sec_chan_offset = NL80211_SEC_CHAN_NO_HT; + channel_type = NL80211_CHAN_NO_HT; } else { chan = local->oper_channel; - sec_chan_offset = local->oper_sec_chan_offset; + channel_type = local->oper_channel_type; } if (chan != local->hw.conf.channel || - sec_chan_offset != local->hw.conf.ht.sec_chan_offset) { + channel_type != local->hw.conf.ht.channel_type) { local->hw.conf.channel = chan; - switch (sec_chan_offset) { - case NL80211_SEC_CHAN_NO_HT: + local->hw.conf.ht.channel_type = channel_type; + switch (channel_type) { + case NL80211_CHAN_NO_HT: local->hw.conf.ht.enabled = false; - local->hw.conf.ht.sec_chan_offset = 0; break; - case NL80211_SEC_CHAN_DISABLED: + case NL80211_CHAN_HT20: + case NL80211_CHAN_HT40MINUS: + case NL80211_CHAN_HT40PLUS: local->hw.conf.ht.enabled = true; - local->hw.conf.ht.sec_chan_offset = 0; - break; - case NL80211_SEC_CHAN_BELOW: - local->hw.conf.ht.enabled = true; - local->hw.conf.ht.sec_chan_offset = -1; - break; - case NL80211_SEC_CHAN_ABOVE: - local->hw.conf.ht.enabled = true; - local->hw.conf.ht.sec_chan_offset = 1; break; } changed |= IEEE80211_CONF_CHANGE_CHANNEL; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 290b0017ef2e..e4d1fca5c72d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -858,6 +858,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); local->hw.conf.ht.enabled = false; + local->oper_channel_type = NL80211_CHAN_NO_HT; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_HT); ieee80211_bss_info_change_notify(sdata, changed); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 505d68f344ce..71a8391c54f6 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -641,7 +641,7 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freqMHz) chan->flags & IEEE80211_CHAN_NO_IBSS) return ret; local->oper_channel = chan; - local->oper_sec_chan_offset = NL80211_SEC_CHAN_NO_HT; + local->oper_channel_type = NL80211_CHAN_NO_HT; if (local->sw_scanning || local->hw_scanning) ret = 0; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 93c9b983ce08..1e728fff474e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -60,7 +60,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { .len = BUS_ID_SIZE-1 }, [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, @@ -362,8 +362,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - enum nl80211_sec_chan_offset sec_chan_offset = - NL80211_SEC_CHAN_NO_HT; + enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; struct ieee80211_channel *chan; struct ieee80211_sta_ht_cap *ht_cap; u32 freq, sec_freq; @@ -375,13 +374,13 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) result = -EINVAL; - if (info->attrs[NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET]) { - sec_chan_offset = nla_get_u32(info->attrs[ - NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET]); - if (sec_chan_offset != NL80211_SEC_CHAN_NO_HT && - sec_chan_offset != NL80211_SEC_CHAN_DISABLED && - sec_chan_offset != NL80211_SEC_CHAN_BELOW && - sec_chan_offset != NL80211_SEC_CHAN_ABOVE) + if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + channel_type = nla_get_u32(info->attrs[ + NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + if (channel_type != NL80211_CHAN_NO_HT && + channel_type != NL80211_CHAN_HT20 && + channel_type != NL80211_CHAN_HT40PLUS && + channel_type != NL80211_CHAN_HT40MINUS) goto bad_res; } @@ -392,9 +391,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) goto bad_res; - if (sec_chan_offset == NL80211_SEC_CHAN_BELOW) + if (channel_type == NL80211_CHAN_HT40MINUS) sec_freq = freq - 20; - else if (sec_chan_offset == NL80211_SEC_CHAN_ABOVE) + else if (channel_type == NL80211_CHAN_HT40PLUS) sec_freq = freq + 20; else sec_freq = 0; @@ -402,7 +401,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; /* no HT capabilities */ - if (sec_chan_offset != NL80211_SEC_CHAN_NO_HT && + if (channel_type != NL80211_CHAN_NO_HT && !ht_cap->ht_supported) goto bad_res; @@ -422,7 +421,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) } result = rdev->ops->set_channel(&rdev->wiphy, chan, - sec_chan_offset); + channel_type); if (result) goto bad_res; } -- cgit v1.2.3 From 0fb8ca45eb164c405eef8978f26829f9348b4d4d Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 12 Dec 2008 14:38:33 +0200 Subject: mac80211: Add HT rates into RX status reporting This patch adds option for HT-enabled drivers to report HT rates (HT20/HT40, short GI, MCS index) to mac80211. These rates are currently not in the rate table, so the rate_idx is used to indicate MCS index. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- include/net/mac80211.h | 11 ++++++++-- net/mac80211/mlme.c | 7 ++++++- net/mac80211/rx.c | 56 +++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 61 insertions(+), 13 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 22ae72c58d76..9428d3e2f113 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -436,6 +436,9 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * is valid. This is useful in monitor mode and necessary for beacon frames * to enable IBSS merging. * @RX_FLAG_SHORTPRE: Short preamble was used for this frame + * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index + * @RX_FLAG_40MHZ: HT40 (40 MHz) was used + * @RX_FLAG_SHORT_GI: Short guard interval was used */ enum mac80211_rx_flags { RX_FLAG_MMIC_ERROR = 1<<0, @@ -446,7 +449,10 @@ enum mac80211_rx_flags { RX_FLAG_FAILED_FCS_CRC = 1<<5, RX_FLAG_FAILED_PLCP_CRC = 1<<6, RX_FLAG_TSFT = 1<<7, - RX_FLAG_SHORTPRE = 1<<8 + RX_FLAG_SHORTPRE = 1<<8, + RX_FLAG_HT = 1<<9, + RX_FLAG_40MHZ = 1<<10, + RX_FLAG_SHORT_GI = 1<<11, }; /** @@ -466,7 +472,8 @@ enum mac80211_rx_flags { * @noise: noise when receiving this frame, in dBm. * @qual: overall signal quality indication, in percent (0-100). * @antenna: antenna used - * @rate_idx: index of data rate into band's supported rates + * @rate_idx: index of data rate into band's supported rates or MCS index if + * HT rates are use (RX_FLAG_HT) * @flag: %RX_FLAG_* */ struct ieee80211_rx_status { diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e4d1fca5c72d..a444b38f4901 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1613,8 +1613,13 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, * e.g: at 1 MBit that means mactime is 192 usec earlier * (=24 bytes * 8 usecs/byte) than the beacon timestamp. */ - int rate = local->hw.wiphy->bands[band]-> + int rate; + if (rx_status->flag & RX_FLAG_HT) { + rate = 65; /* TODO: HT rates */ + } else { + rate = local->hw.wiphy->bands[band]-> bitrates[rx_status->rate_idx].bitrate; + } rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate); } else if (local && local->ops && local->ops->get_tsf) /* second best option: get current TSF */ diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 73cf126cef49..b729c005a2b3 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -149,7 +149,17 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, pos++; /* IEEE80211_RADIOTAP_RATE */ - *pos = rate->bitrate / 5; + if (status->flag & RX_FLAG_HT) { + /* + * TODO: add following information into radiotap header once + * suitable fields are defined for it: + * - MCS index (status->rate_idx) + * - HT40 (status->flag & RX_FLAG_40MHZ) + * - short-GI (status->flag & RX_FLAG_SHORT_GI) + */ + *pos = 0; + } else + *pos = rate->bitrate / 5; pos++; /* IEEE80211_RADIOTAP_CHANNEL */ @@ -1849,9 +1859,15 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata, if (!(sdata->dev->flags & IFF_PROMISC)) return 0; rx->flags &= ~IEEE80211_RX_RA_MATCH; - } else if (!rx->sta) + } else if (!rx->sta) { + int rate_idx; + if (rx->status->flag & RX_FLAG_HT) + rate_idx = 0; /* TODO: HT rates */ + else + rate_idx = rx->status->rate_idx; rx->sta = ieee80211_ibss_add_sta(sdata, bssid, hdr->addr2, - BIT(rx->status->rate_idx)); + BIT(rate_idx)); + } break; case NL80211_IFTYPE_MESH_POINT: if (!multicast && @@ -2057,7 +2073,13 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, tid_agg_rx->reorder_buf[index]->cb, sizeof(status)); sband = local->hw.wiphy->bands[status.band]; - rate = &sband->bitrates[status.rate_idx]; + if (status.flag & RX_FLAG_HT) { + /* TODO: HT rates */ + rate = sband->bitrates; + } else { + rate = &sband->bitrates + [status.rate_idx]; + } __ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index], &status, rate); @@ -2101,7 +2123,10 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, memcpy(&status, tid_agg_rx->reorder_buf[index]->cb, sizeof(status)); sband = local->hw.wiphy->bands[status.band]; - rate = &sband->bitrates[status.rate_idx]; + if (status.flag & RX_FLAG_HT) + rate = sband->bitrates; /* TODO: HT rates */ + else + rate = &sband->bitrates[status.rate_idx]; __ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index], &status, rate); tid_agg_rx->stored_mpdu_num--; @@ -2189,15 +2214,26 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, } sband = local->hw.wiphy->bands[status->band]; - - if (!sband || - status->rate_idx < 0 || - status->rate_idx >= sband->n_bitrates) { + if (!sband) { WARN_ON(1); return; } - rate = &sband->bitrates[status->rate_idx]; + if (status->flag & RX_FLAG_HT) { + /* rate_idx is MCS index */ + if (WARN_ON(status->rate_idx < 0 || + status->rate_idx >= 76)) + return; + /* HT rates are not in the table - use the highest legacy rate + * for now since other parts of mac80211 may not yet be fully + * MCS aware. */ + rate = &sband->bitrates[sband->n_bitrates - 1]; + } else { + if (WARN_ON(status->rate_idx < 0 || + status->rate_idx >= sband->n_bitrates)) + return; + rate = &sband->bitrates[status->rate_idx]; + } /* * key references and virtual interfaces are protected using RCU -- cgit v1.2.3 From 520eb82076993b7f55ef9b80771d264272e5127b Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 18 Dec 2008 23:35:27 +0200 Subject: mac80211: implement dynamic power save This patch implements dynamic power save for mac80211. Basically it means enabling power save mode after an idle period. Implementing it dynamically gives a good compromise of low power consumption and low latency. Some hardware have support for this in firmware, but some require the host to do it. The dynamic power save is implemented by adding an timeout to ieee80211_subif_start_xmit(). The timeout can be enabled from userspace with Wireless Extensions. For example, the command below enables the dynamic power save and sets the time timeout to 500 ms: iwconfig wlan0 power timeout 500m Power save now only works with devices which handle power save in firmware. It's also disabled by default and the heuristics when and how to enable is considered as a policy decision and will be left for the userspace to handle. In case the firmware has support for this, drivers can disable this feature with IEEE80211_HW_NO_STACK_DYNAMIC_PS. Big thanks to Johannes Berg for the help with the design and code. Signed-off-by: Kalle Valo Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 6 ++++++ net/mac80211/ieee80211_i.h | 10 ++++++++++ net/mac80211/main.c | 7 +++++++ net/mac80211/mlme.c | 49 ++++++++++++++++++++++++++++++++++++++++++++-- net/mac80211/tx.c | 13 ++++++++++++ net/mac80211/wext.c | 30 ++++++++++++++++++---------- 6 files changed, 103 insertions(+), 12 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9428d3e2f113..b3bd00a9d992 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -854,6 +854,11 @@ enum ieee80211_tkip_key_type { * * @IEEE80211_HW_AMPDU_AGGREGATION: * Hardware supports 11n A-MPDU aggregation. + * + * @IEEE80211_HW_NO_STACK_DYNAMIC_PS: + * Hardware which has dynamic power save support, meaning + * that power save is enabled in idle periods, and don't need support + * from stack. */ enum ieee80211_hw_flags { IEEE80211_HW_RX_INCLUDES_FCS = 1<<1, @@ -866,6 +871,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_NOISE_DBM = 1<<8, IEEE80211_HW_SPECTRUM_MGMT = 1<<9, IEEE80211_HW_AMPDU_AGGREGATION = 1<<10, + IEEE80211_HW_NO_STACK_DYNAMIC_PS = 1<<11, }; /** diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a74d6738b30a..f3eec989662b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -540,6 +540,7 @@ enum { enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_DRIVER, + IEEE80211_QUEUE_STOP_REASON_PS, }; /* maximum number of hardware queues we support. */ @@ -693,7 +694,12 @@ struct ieee80211_local { */ int wifi_wme_noack_test; unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */ + bool powersave; + int dynamic_ps_timeout; + struct work_struct dynamic_ps_enable_work; + struct work_struct dynamic_ps_disable_work; + struct timer_list dynamic_ps_timer; #ifdef CONFIG_MAC80211_DEBUGFS struct local_debugfsdentries { @@ -977,6 +983,10 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freq); u64 ieee80211_mandatory_rates(struct ieee80211_local *local, enum ieee80211_band band); +void ieee80211_dynamic_ps_enable_work(struct work_struct *work); +void ieee80211_dynamic_ps_disable_work(struct work_struct *work); +void ieee80211_dynamic_ps_timer(unsigned long data); + void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason); void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 21335382f530..24b14363d6e7 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -729,6 +729,13 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work); + INIT_WORK(&local->dynamic_ps_enable_work, + ieee80211_dynamic_ps_enable_work); + INIT_WORK(&local->dynamic_ps_disable_work, + ieee80211_dynamic_ps_disable_work); + setup_timer(&local->dynamic_ps_timer, + ieee80211_dynamic_ps_timer, (unsigned long) local); + sta_info_init(local); tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index dac8bd37dcf5..5ba721b6a399 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -745,8 +745,14 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ieee80211_bss_info_change_notify(sdata, bss_info_changed); if (local->powersave) { - local->hw.conf.flags |= IEEE80211_CONF_PS; - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + if (local->dynamic_ps_timeout > 0) + mod_timer(&local->dynamic_ps_timer, jiffies + + msecs_to_jiffies(local->dynamic_ps_timeout)); + else { + conf->flags |= IEEE80211_CONF_PS; + ieee80211_hw_config(local, + IEEE80211_CONF_CHANGE_PS); + } } netif_tx_start_all_queues(sdata->dev); @@ -866,6 +872,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, local->oper_channel_type = NL80211_CHAN_NO_HT; config_changed |= IEEE80211_CONF_CHANGE_HT; + del_timer_sync(&local->dynamic_ps_timer); + cancel_work_sync(&local->dynamic_ps_enable_work); + if (local->hw.conf.flags & IEEE80211_CONF_PS) { local->hw.conf.flags &= ~IEEE80211_CONF_PS; config_changed |= IEEE80211_CONF_CHANGE_PS; @@ -2593,3 +2602,39 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local) ieee80211_restart_sta_timer(sdata); rcu_read_unlock(); } + +void ieee80211_dynamic_ps_disable_work(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, + dynamic_ps_disable_work); + + if (local->hw.conf.flags & IEEE80211_CONF_PS) { + local->hw.conf.flags &= ~IEEE80211_CONF_PS; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + } + + ieee80211_wake_queues_by_reason(&local->hw, + IEEE80211_QUEUE_STOP_REASON_PS); +} + +void ieee80211_dynamic_ps_enable_work(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, + dynamic_ps_enable_work); + + if (local->hw.conf.flags & IEEE80211_CONF_PS) + return; + + local->hw.conf.flags |= IEEE80211_CONF_PS; + + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); +} + +void ieee80211_dynamic_ps_timer(unsigned long data) +{ + struct ieee80211_local *local = (void *) data; + + queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work); +} diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index b098c58d216f..a4af3a124cce 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1473,6 +1473,19 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, goto fail; } + if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) && + local->dynamic_ps_timeout > 0) { + if (local->hw.conf.flags & IEEE80211_CONF_PS) { + ieee80211_stop_queues_by_reason(&local->hw, + IEEE80211_QUEUE_STOP_REASON_PS); + queue_work(local->hw.workqueue, + &local->dynamic_ps_disable_work); + } + + mod_timer(&local->dynamic_ps_timer, jiffies + + msecs_to_jiffies(local->dynamic_ps_timeout)); + } + nh_pos = skb_network_header(skb) - skb->data; h_pos = skb_transport_header(skb) - skb->data; diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index f6640d047157..7162d5816f39 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -833,7 +833,7 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev, struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_conf *conf = &local->hw.conf; - int ret = 0; + int ret = 0, timeout = 0; bool ps; if (sdata->vif.type != NL80211_IFTYPE_STATION) @@ -841,6 +841,7 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev, if (wrq->disabled) { ps = false; + timeout = 0; goto set; } @@ -850,22 +851,31 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev, case IW_POWER_ALL_R: /* If explicitely state all */ ps = true; break; - default: /* Otherwise we don't support it */ - return -EINVAL; + default: /* Otherwise we ignore */ + break; } - if (ps == local->powersave) - return ret; + if (wrq->flags & IW_POWER_TIMEOUT) + timeout = wrq->value / 1000; set: + if (ps == local->powersave && timeout == local->dynamic_ps_timeout) + return ret; + local->powersave = ps; + local->dynamic_ps_timeout = timeout; if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) { - if (local->powersave) - conf->flags |= IEEE80211_CONF_PS; - else - conf->flags &= ~IEEE80211_CONF_PS; - + if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) && + local->dynamic_ps_timeout > 0) + mod_timer(&local->dynamic_ps_timer, jiffies + + msecs_to_jiffies(local->dynamic_ps_timeout)); + else { + if (local->powersave) + conf->flags |= IEEE80211_CONF_PS; + else + conf->flags &= ~IEEE80211_CONF_PS; + } ret = ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } -- cgit v1.2.3