diff options
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/cfg.c | 182 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 37 | ||||
-rw-r--r-- | net/mac80211/driver-trace.h | 15 | ||||
-rw-r--r-- | net/mac80211/ibss.c | 2 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 3 | ||||
-rw-r--r-- | net/mac80211/mesh.c | 18 | ||||
-rw-r--r-- | net/mac80211/mesh_plink.c | 96 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 2 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 1 | ||||
-rw-r--r-- | net/mac80211/util.c | 9 |
10 files changed, 343 insertions, 22 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7e6781f8c57..495831ee48f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -450,6 +450,180 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); } +static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { + "rx_packets", "rx_bytes", "wep_weak_iv_count", + "rx_duplicates", "rx_fragments", "rx_dropped", + "tx_packets", "tx_bytes", "tx_fragments", + "tx_filtered", "tx_retry_failed", "tx_retries", + "beacon_loss", "sta_state", "txrate", "rxrate", "signal", + "channel", "noise", "ch_time", "ch_time_busy", + "ch_time_ext_busy", "ch_time_rx", "ch_time_tx" +}; +#define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats) + +static int ieee80211_get_et_sset_count(struct wiphy *wiphy, + struct net_device *dev, + int sset) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int rv = 0; + + if (sset == ETH_SS_STATS) + rv += STA_STATS_LEN; + + rv += drv_get_et_sset_count(sdata, sset); + + if (rv == 0) + return -EOPNOTSUPP; + return rv; +} + +static void ieee80211_get_et_stats(struct wiphy *wiphy, + struct net_device *dev, + struct ethtool_stats *stats, + u64 *data) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct sta_info *sta; + struct ieee80211_local *local = sdata->local; + struct station_info sinfo; + struct survey_info survey; + int i, q; +#define STA_STATS_SURVEY_LEN 7 + + memset(data, 0, sizeof(u64) * STA_STATS_LEN); + +#define ADD_STA_STATS(sta) \ + do { \ + data[i++] += sta->rx_packets; \ + data[i++] += sta->rx_bytes; \ + data[i++] += sta->wep_weak_iv_count; \ + data[i++] += sta->num_duplicates; \ + data[i++] += sta->rx_fragments; \ + data[i++] += sta->rx_dropped; \ + \ + data[i++] += sta->tx_packets; \ + data[i++] += sta->tx_bytes; \ + data[i++] += sta->tx_fragments; \ + data[i++] += sta->tx_filtered_count; \ + data[i++] += sta->tx_retry_failed; \ + data[i++] += sta->tx_retry_count; \ + data[i++] += sta->beacon_loss_count; \ + } while (0) + + /* For Managed stations, find the single station based on BSSID + * and use that. For interface types, iterate through all available + * stations and add stats for any station that is assigned to this + * network device. + */ + + rcu_read_lock(); + + if (sdata->vif.type == NL80211_IFTYPE_STATION) { + sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid); + + if (!(sta && !WARN_ON(sta->sdata->dev != dev))) + goto do_survey; + + i = 0; + ADD_STA_STATS(sta); + + data[i++] = sta->sta_state; + + sinfo.filled = 0; + sta_set_sinfo(sta, &sinfo); + + if (sinfo.filled | STATION_INFO_TX_BITRATE) + data[i] = 100000 * + cfg80211_calculate_bitrate(&sinfo.txrate); + i++; + if (sinfo.filled | STATION_INFO_RX_BITRATE) + data[i] = 100000 * + cfg80211_calculate_bitrate(&sinfo.rxrate); + i++; + + if (sinfo.filled | STATION_INFO_SIGNAL_AVG) + data[i] = (u8)sinfo.signal_avg; + i++; + } else { + list_for_each_entry_rcu(sta, &local->sta_list, list) { + /* Make sure this station belongs to the proper dev */ + if (sta->sdata->dev != dev) + continue; + + i = 0; + ADD_STA_STATS(sta); + } + } + +do_survey: + i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; + /* Get survey stats for current channel */ + q = 0; + while (true) { + survey.filled = 0; + if (drv_get_survey(local, q, &survey) != 0) { + survey.filled = 0; + break; + } + + if (survey.channel && + (local->oper_channel->center_freq == + survey.channel->center_freq)) + break; + q++; + } + + if (survey.filled) + data[i++] = survey.channel->center_freq; + else + data[i++] = 0; + if (survey.filled & SURVEY_INFO_NOISE_DBM) + data[i++] = (u8)survey.noise; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME) + data[i++] = survey.channel_time; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY) + data[i++] = survey.channel_time_busy; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) + data[i++] = survey.channel_time_ext_busy; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX) + data[i++] = survey.channel_time_rx; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX) + data[i++] = survey.channel_time_tx; + else + data[i++] = -1LL; + + rcu_read_unlock(); + + if (WARN_ON(i != STA_STATS_LEN)) + return; + + drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN])); +} + +static void ieee80211_get_et_strings(struct wiphy *wiphy, + struct net_device *dev, + u32 sset, u8 *data) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int sz_sta_stats = 0; + + if (sset == ETH_SS_STATS) { + sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); + memcpy(data, *ieee80211_gstrings_sta_stats, sz_sta_stats); + } + drv_get_et_strings(sdata, sset, &(data[sz_sta_stats])); +} static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo) @@ -1364,6 +1538,11 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, return -ENOTSUPP; conf->rssi_threshold = nconf->rssi_threshold; } + if (_chg_mesh_attr(NL80211_MESHCONF_HT_OPMODE, mask)) { + conf->ht_opmode = nconf->ht_opmode; + sdata->vif.bss_conf.ht_operation_mode = nconf->ht_opmode; + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT); + } return 0; } @@ -2794,4 +2973,7 @@ struct cfg80211_ops mac80211_config_ops = { #ifdef CONFIG_PM .set_wakeup = ieee80211_set_wakeup, #endif + .get_et_sset_count = ieee80211_get_et_sset_count, + .get_et_stats = ieee80211_get_et_stats, + .get_et_strings = ieee80211_get_et_strings, }; diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 4a0e559cb26..6d33a0c743a 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -35,6 +35,43 @@ static inline void drv_tx_frags(struct ieee80211_local *local, local->ops->tx_frags(&local->hw, vif, sta, skbs); } +static inline void drv_get_et_strings(struct ieee80211_sub_if_data *sdata, + u32 sset, u8 *data) +{ + struct ieee80211_local *local = sdata->local; + if (local->ops->get_et_strings) { + trace_drv_get_et_strings(local, sset); + local->ops->get_et_strings(&local->hw, &sdata->vif, sset, data); + trace_drv_return_void(local); + } +} + +static inline void drv_get_et_stats(struct ieee80211_sub_if_data *sdata, + struct ethtool_stats *stats, + u64 *data) +{ + struct ieee80211_local *local = sdata->local; + if (local->ops->get_et_stats) { + trace_drv_get_et_stats(local); + local->ops->get_et_stats(&local->hw, &sdata->vif, stats, data); + trace_drv_return_void(local); + } +} + +static inline int drv_get_et_sset_count(struct ieee80211_sub_if_data *sdata, + int sset) +{ + struct ieee80211_local *local = sdata->local; + int rv = 0; + if (local->ops->get_et_sset_count) { + trace_drv_get_et_sset_count(local, sset); + rv = local->ops->get_et_sset_count(&local->hw, &sdata->vif, + sset); + trace_drv_return_int(local, rv); + } + return rv; +} + static inline int drv_start(struct ieee80211_local *local) { int ret; diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 7c0754bed61..6de00b2c268 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -161,6 +161,21 @@ DEFINE_EVENT(local_only_evt, drv_start, TP_ARGS(local) ); +DEFINE_EVENT(local_u32_evt, drv_get_et_strings, + TP_PROTO(struct ieee80211_local *local, u32 sset), + TP_ARGS(local, sset) +); + +DEFINE_EVENT(local_u32_evt, drv_get_et_sset_count, + TP_PROTO(struct ieee80211_local *local, u32 sset), + TP_ARGS(local, sset) +); + +DEFINE_EVENT(local_only_evt, drv_get_et_stats, + TP_PROTO(struct ieee80211_local *local), + TP_ARGS(local) +); + DEFINE_EVENT(local_only_evt, drv_suspend, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index d307d3b3e32..ebafba61c2f 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -164,7 +164,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap); pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap, - chan, channel_type); + chan, channel_type, 0); } if (local->hw.queues >= IEEE80211_NUM_ACS) { diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fabee974bf6..3f3cd50fff1 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1497,7 +1497,8 @@ u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u16 cap); u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type); + enum nl80211_channel_type channel_type, + u16 prot_mode); /* internal work items */ void ieee80211_work_init(struct ieee80211_local *local); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 775627166e4..0675a2fec6a 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -76,6 +76,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; u32 basic_rates = 0; + enum nl80211_channel_type sta_channel_type = NL80211_CHAN_NO_HT; /* * As support for each feature is added, check for matching @@ -102,10 +103,15 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, if (sdata->vif.bss_conf.basic_rates != basic_rates) goto mismatch; - /* disallow peering with mismatched channel types for now */ + if (ie->ht_operation) + sta_channel_type = + ieee80211_ht_oper_to_channel_type(ie->ht_operation); + + /* Disallow HT40+/- mismatch */ if (ie->ht_operation && - (local->_oper_channel_type != - ieee80211_ht_oper_to_channel_type(ie->ht_operation))) + local->_oper_channel_type > NL80211_CHAN_HT20 && + sta_channel_type > NL80211_CHAN_HT20 && + local->_oper_channel_type != sta_channel_type) goto mismatch; return true; @@ -396,7 +402,8 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb, return -ENOMEM; pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation)); - ieee80211_ie_build_ht_oper(pos, ht_cap, channel, channel_type); + ieee80211_ie_build_ht_oper(pos, ht_cap, channel, channel_type, + sdata->vif.bss_conf.ht_operation_mode); return 0; } @@ -588,12 +595,15 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags); ieee80211_mesh_root_setup(ifmsh); ieee80211_queue_work(&local->hw, &sdata->work); + sdata->vif.bss_conf.ht_operation_mode = + ifmsh->mshcfg.ht_opmode; sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(sdata->local, sdata->local->hw.conf.channel->band); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED | + BSS_CHANGED_HT | BSS_CHANGED_BASIC_RATES | BSS_CHANGED_BEACON_INT); } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 1ff2a5c63e4..8cc8461b48a 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -102,12 +102,70 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, set_sta_flag(sta, WLAN_STA_WME); - if (sta_info_insert(sta)) - return NULL; - return sta; } +/** mesh_set_ht_prot_mode - set correct HT protection mode + * + * Section 9.23.3.5 of IEEE 80211s standard describes the protection rules for + * HT mesh STA in a MBSS. Three HT protection modes are supported for now, + * non-HT mixed mode, 20MHz-protection and no-protection mode. non-HT mixed + * mode is selected if any non-HT peers are present in our MBSS. + * 20MHz-protection mode is selected if all peers in our 20/40MHz MBSS support + * HT and atleast one HT20 peer is present. Otherwise no-protection mode is + * selected. + */ +static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + u32 changed = 0; + u16 ht_opmode; + bool non_ht_sta = false, ht20_sta = false; + + if (local->_oper_channel_type == NL80211_CHAN_NO_HT) + return 0; + + rcu_read_lock(); + list_for_each_entry_rcu(sta, &local->sta_list, list) { + if (sdata == sta->sdata && + sta->plink_state == NL80211_PLINK_ESTAB) { + switch (sta->ch_type) { + case NL80211_CHAN_NO_HT: + mpl_dbg("mesh_plink %pM: nonHT sta (%pM) is present", + sdata->vif.addr, sta->sta.addr); + non_ht_sta = true; + goto out; + case NL80211_CHAN_HT20: + mpl_dbg("mesh_plink %pM: HT20 sta (%pM) is present", + sdata->vif.addr, sta->sta.addr); + ht20_sta = true; + default: + break; + } + } + } +out: + rcu_read_unlock(); + + if (non_ht_sta) + ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED; + else if (ht20_sta && local->_oper_channel_type > NL80211_CHAN_HT20) + ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ; + else + ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE; + + if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { + sdata->vif.bss_conf.ht_operation_mode = ht_opmode; + sdata->u.mesh.mshcfg.ht_opmode = ht_opmode; + changed = BSS_CHANGED_HT; + mpl_dbg("mesh_plink %pM: protection mode changed to %d", + sdata->vif.addr, ht_opmode); + } + + return changed; +} + /** * __mesh_plink_deactivate - deactivate mesh peer link * @@ -281,6 +339,7 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband; u32 rates, basic_rates = 0; struct sta_info *sta; + bool insert = false; sband = local->hw.wiphy->bands[band]; rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates); @@ -290,6 +349,7 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, sta = mesh_plink_alloc(sdata, addr); if (!sta) return NULL; + insert = true; } spin_lock_bh(&sta->lock); @@ -303,9 +363,21 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, else memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap)); + if (elems->ht_operation) { + if (!(elems->ht_operation->ht_param & + IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) + sta->sta.ht_cap.cap &= + ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + sta->ch_type = + ieee80211_ht_oper_to_channel_type(elems->ht_operation); + } + rate_control_rate_init(sta); spin_unlock_bh(&sta->lock); + if (insert && sta_info_insert(sta)) + return NULL; + return sta; } @@ -487,9 +559,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m enum plink_event event; enum ieee80211_self_protected_actioncode ftype; size_t baselen; - bool deactivated, matches_local = true; + bool matches_local = true; u8 ie_len; u8 *baseaddr; + u32 changed = 0; __le16 plid, llid, reason; #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG static const char *mplstates[] = { @@ -775,7 +848,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m sta->plink_state = NL80211_PLINK_ESTAB; spin_unlock_bh(&sta->lock); mesh_plink_inc_estab_count(sdata); - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); + changed |= mesh_set_ht_prot_mode(sdata); + changed |= BSS_CHANGED_BEACON; mpl_dbg("Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); break; @@ -810,7 +884,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m sta->plink_state = NL80211_PLINK_ESTAB; spin_unlock_bh(&sta->lock); mesh_plink_inc_estab_count(sdata); - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); + changed |= mesh_set_ht_prot_mode(sdata); + changed |= BSS_CHANGED_BEACON; mpl_dbg("Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); mesh_plink_frame_tx(sdata, @@ -828,13 +903,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m case CLS_ACPT: reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; - deactivated = __mesh_plink_deactivate(sta); + __mesh_plink_deactivate(sta); sta->plink_state = NL80211_PLINK_HOLDING; llid = sta->llid; mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->lock); - if (deactivated) - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); + changed |= mesh_set_ht_prot_mode(sdata); + changed |= BSS_CHANGED_BEACON; mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; @@ -881,4 +956,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } rcu_read_unlock(); + + if (changed) + ieee80211_bss_info_change_notify(sdata, changed); } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b1c617fdabd..52cd301245a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -210,7 +210,7 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, disable_40 = true; if (sta && (!reconfig || - (disable_40 != !!(sta->sta.ht_cap.cap & + (disable_40 != !(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)))) { if (disable_40) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index e920b229fb8..3bb24a121c9 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -362,6 +362,7 @@ struct sta_info { struct timer_list plink_timer; s64 t_offset; s64 t_offset_setpoint; + enum nl80211_channel_type ch_type; #endif #ifdef CONFIG_MAC80211_DEBUGFS diff --git a/net/mac80211/util.c b/net/mac80211/util.c index d9a747d387f..22f2216b397 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1663,7 +1663,8 @@ u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type) + enum nl80211_channel_type channel_type, + u16 prot_mode) { struct ieee80211_ht_operation *ht_oper; /* Build HT Information */ @@ -1689,11 +1690,7 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, channel_type != NL80211_CHAN_HT20) ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; - /* - * Note: According to 802.11n-2009 9.13.3.1, HT Protection field and - * RIFS Mode are reserved in IBSS mode, therefore keep them at 0 - */ - ht_oper->operation_mode = 0x0000; + ht_oper->operation_mode = cpu_to_le16(prot_mode); ht_oper->stbc_param = 0x0000; /* It seems that Basic MCS set and Supported MCS set |