Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac802...
authorJohn W. Linville <linville@tuxdriver.com>
Thu, 1 Aug 2013 19:26:52 +0000 (15:26 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 1 Aug 2013 19:26:52 +0000 (15:26 -0400)
41 files changed:
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/rc.c
drivers/net/wireless/iwlegacy/3945-rs.c
drivers/net/wireless/iwlegacy/4965-rs.c
drivers/net/wireless/iwlwifi/dvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/rtlwifi/rc.c
drivers/net/wireless/ti/wlcore/main.c
include/net/cfg80211.h
include/net/ieee80211_radiotap.h
include/net/mac80211.h
include/uapi/linux/nl80211.h
net/mac80211/cfg.c
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/rate.c
net/mac80211/rate.h
net/mac80211/rc80211_minstrel.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rc80211_pid_algo.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/util.c
net/wireless/core.c
net/wireless/core.h
net/wireless/mesh.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/scan.c
net/wireless/trace.h
net/wireless/util.c

index 1737a3e336859013e2b9aeb6d49d61da1b35a688..1adb803a9d86e4cf587aa31f5fafc800d4f0057f 100644 (file)
@@ -2094,7 +2094,7 @@ static void ath9k_wow_add_pattern(struct ath_softc *sc,
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath9k_wow_pattern *wow_pattern = NULL;
-       struct cfg80211_wowlan_trig_pkt_pattern *patterns = wowlan->patterns;
+       struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
        int mask_len;
        s8 i = 0;
 
index 7eb1f4b458e4fb1f3c096fd297bb60b273d9c686..a3c4ca0c94bfa43877f84b2e5baeb61353ece266 100644 (file)
@@ -1275,6 +1275,7 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
 }
 
 static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
+                         struct cfg80211_chan_def *chandef,
                           struct ieee80211_sta *sta, void *priv_sta)
 {
        struct ath_softc *sc = priv;
@@ -1313,6 +1314,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
 }
 
 static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
+                           struct cfg80211_chan_def *chandef,
                            struct ieee80211_sta *sta, void *priv_sta,
                            u32 changed)
 {
index fe31590a51b2d81b41b40b2f862c96c5b2e10980..aea667b430c3ac7e70295562f33de2227813a198 100644 (file)
@@ -887,6 +887,7 @@ il3945_remove_debugfs(void *il, void *il_sta)
  */
 static void
 il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
+                        struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *il_sta)
 {
 }
index ed3c42a63a4369678f2764049f0b7e257f535fb4..3ccbaf791b48bfea34d3053d4d7d3cce6e165b22 100644 (file)
@@ -2803,6 +2803,7 @@ il4965_rs_remove_debugfs(void *il, void *il_sta)
  */
 static void
 il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
+                        struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *il_sta)
 {
 }
index 1b693944123b5b0eda877f8d93a4c64775cc04ec..91eb09b9b56f38d357039cf28c681cfbdfa5e925 100644 (file)
@@ -3319,7 +3319,8 @@ static void rs_remove_debugfs(void *priv, void *priv_sta)
  * station is added we ignore it.
  */
 static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband,
-                        struct ieee80211_sta *sta, void *priv_sta)
+                             struct cfg80211_chan_def *chandef,
+                             struct ieee80211_sta *sta, void *priv_sta)
 {
 }
 static struct rate_control_ops rs_ops = {
index b328a988c130ec6136f5198d78225870877aa1a4..376ea2112de2949ec36b606dc56a4d5e2254e5f9 100644 (file)
@@ -3159,8 +3159,9 @@ static void rs_remove_debugfs(void *mvm, void *mvm_sta)
  * station is added we ignore it.
  */
 static void rs_rate_init_stub(void *mvm_r,
-                                struct ieee80211_supported_band *sband,
-                                struct ieee80211_sta *sta, void *mvm_sta)
+                             struct ieee80211_supported_band *sband,
+                             struct cfg80211_chan_def *chandef,
+                             struct ieee80211_sta *sta, void *mvm_sta)
 {
 }
 static struct rate_control_ops rs_mvm_ops = {
index cb34c7895f2a299b8c6b8f6c094f79cd9ab88c85..7b2a6229eedb2bfcb30e0f742e2c0108951bfade 100644 (file)
@@ -867,7 +867,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
 
        if (WARN_ON(skb->len < 10)) {
                /* Should not happen; just a sanity check for addr1 use */
-               dev_kfree_skb(skb);
+               ieee80211_free_txskb(hw, skb);
                return;
        }
 
@@ -884,13 +884,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
        }
 
        if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) {
-               dev_kfree_skb(skb);
+               ieee80211_free_txskb(hw, skb);
                return;
        }
 
        if (data->idle && !data->tmp_chan) {
                wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
-               dev_kfree_skb(skb);
+               ieee80211_free_txskb(hw, skb);
                return;
        }
 
@@ -2309,7 +2309,9 @@ static int __init init_mac80211_hwsim(void)
                        hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE;
 
                hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
-                                   WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+                                   WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+                                   WIPHY_FLAG_AP_UAPSD;
+               hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
 
                /* ask mac80211 to reserve space for magic */
                hw->vif_data_size = sizeof(struct hwsim_vif_priv);
index cc334d529dbe9eae4d5cda6bd4dfc029715c1b73..8a752637f0ce8cb9b4b3d3dad91e0ecb77b77b52 100644 (file)
@@ -2309,8 +2309,7 @@ EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
 
 #ifdef CONFIG_PM
 static bool
-mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat,
-                            s8 *byte_seq)
+mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq)
 {
        int j, k, valid_byte_cnt = 0;
        bool dont_care_byte = false;
index f9f059dadb734af694c992bb8634fe866d317a61..a98acefb8c06a3802290c9130de368b8effcab83 100644 (file)
@@ -218,6 +218,7 @@ static void rtl_tx_status(void *ppriv,
 
 static void rtl_rate_init(void *ppriv,
                          struct ieee80211_supported_band *sband,
+                         struct cfg80211_chan_def *chandef,
                          struct ieee80211_sta *sta, void *priv_sta)
 {
 }
index b8db55c868c7b0027e96bf28f7174a00a308eb0f..d1b19c38a907ddba1d5dfe4106e8caa2ce49315c 100644 (file)
@@ -1315,7 +1315,7 @@ static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
 
 #ifdef CONFIG_PM
 static int
-wl1271_validate_wowlan_pattern(struct cfg80211_wowlan_trig_pkt_pattern *p)
+wl1271_validate_wowlan_pattern(struct cfg80211_pkt_pattern *p)
 {
        int num_fields = 0, in_field = 0, fields_size = 0;
        int i, pattern_len = 0;
@@ -1458,9 +1458,9 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
  * Allocates an RX filter returned through f
  * which needs to be freed using rx_filter_free()
  */
-static int wl1271_convert_wowlan_pattern_to_rx_filter(
-       struct cfg80211_wowlan_trig_pkt_pattern *p,
-       struct wl12xx_rx_filter **f)
+static int
+wl1271_convert_wowlan_pattern_to_rx_filter(struct cfg80211_pkt_pattern *p,
+                                          struct wl12xx_rx_filter **f)
 {
        int i, j, ret = 0;
        struct wl12xx_rx_filter *filter;
@@ -1562,7 +1562,7 @@ static int wl1271_configure_wowlan(struct wl1271 *wl,
 
        /* Translate WoWLAN patterns into filters */
        for (i = 0; i < wow->n_patterns; i++) {
-               struct cfg80211_wowlan_trig_pkt_pattern *p;
+               struct cfg80211_pkt_pattern *p;
                struct wl12xx_rx_filter *filter = NULL;
 
                p = &wow->patterns[i];
index 7b0730aeb892621abc6419ec9c345b141e184b71..aeaf6dff6e05bd05627148eec841ce8a43bd4bf1 100644 (file)
@@ -460,6 +460,33 @@ ieee80211_chandef_rate_flags(struct cfg80211_chan_def *chandef)
        return 0;
 }
 
+/**
+ * ieee80211_chandef_max_power - maximum transmission power for the chandef
+ *
+ * In some regulations, the transmit power may depend on the configured channel
+ * bandwidth which may be defined as dBm/MHz. This function returns the actual
+ * max_power for non-standard (20 MHz) channels.
+ *
+ * @chandef: channel definition for the channel
+ *
+ * Returns: maximum allowed transmission power in dBm for the chandef
+ */
+static inline int
+ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_5:
+               return min(chandef->chan->max_reg_power - 6,
+                          chandef->chan->max_power);
+       case NL80211_CHAN_WIDTH_10:
+               return min(chandef->chan->max_reg_power - 3,
+                          chandef->chan->max_power);
+       default:
+               break;
+       }
+       return chandef->chan->max_power;
+}
+
 /**
  * enum survey_info_flags - survey information flags
  *
@@ -490,7 +517,7 @@ enum survey_info_flags {
  * @channel: the channel this survey record reports, mandatory
  * @filled: bitflag of flags from &enum survey_info_flags
  * @noise: channel noise in dBm. This and all following fields are
- *     optional
+ *     optional
  * @channel_time: amount of time in ms the radio spent on the channel
  * @channel_time_busy: amount of time the primary channel was sensed busy
  * @channel_time_ext_busy: amount of time the extension channel was sensed busy
@@ -546,9 +573,9 @@ struct cfg80211_crypto_settings {
 /**
  * struct cfg80211_beacon_data - beacon data
  * @head: head portion of beacon (before TIM IE)
- *     or %NULL if not changed
+ *     or %NULL if not changed
  * @tail: tail portion of beacon (after TIM IE)
- *     or %NULL if not changed
+ *     or %NULL if not changed
  * @head_len: length of @head
  * @tail_len: length of @tail
  * @beacon_ies: extra information element(s) to add into Beacon frames or %NULL
@@ -764,7 +791,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
  * @STATION_INFO_PLINK_STATE: @plink_state filled
  * @STATION_INFO_SIGNAL: @signal filled
  * @STATION_INFO_TX_BITRATE: @txrate fields are filled
- *  (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
+ *     (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
  * @STATION_INFO_RX_PACKETS: @rx_packets filled with 32-bit value
  * @STATION_INFO_TX_PACKETS: @tx_packets filled with 32-bit value
  * @STATION_INFO_TX_RETRIES: @tx_retries filled
@@ -1285,6 +1312,7 @@ struct cfg80211_ssid {
  * @n_ssids: number of SSIDs
  * @channels: channels to scan on.
  * @n_channels: total number of channels to scan
+ * @scan_width: channel width for scanning
  * @ie: optional information element(s) to add into Probe Request or %NULL
  * @ie_len: length of ie in octets
  * @flags: bit field of flags controlling operation
@@ -1300,6 +1328,7 @@ struct cfg80211_scan_request {
        struct cfg80211_ssid *ssids;
        int n_ssids;
        u32 n_channels;
+       enum nl80211_bss_scan_width scan_width;
        const u8 *ie;
        size_t ie_len;
        u32 flags;
@@ -1333,6 +1362,7 @@ struct cfg80211_match_set {
  * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans)
  * @n_ssids: number of SSIDs
  * @n_channels: total number of channels to scan
+ * @scan_width: channel width for scanning
  * @interval: interval between each scheduled scan cycle
  * @ie: optional information element(s) to add into Probe Request or %NULL
  * @ie_len: length of ie in octets
@@ -1352,6 +1382,7 @@ struct cfg80211_sched_scan_request {
        struct cfg80211_ssid *ssids;
        int n_ssids;
        u32 n_channels;
+       enum nl80211_bss_scan_width scan_width;
        u32 interval;
        const u8 *ie;
        size_t ie_len;
@@ -1403,6 +1434,7 @@ struct cfg80211_bss_ies {
  * for use in scan results and similar.
  *
  * @channel: channel this BSS is on
+ * @scan_width: width of the control channel
  * @bssid: BSSID of the BSS
  * @beacon_interval: the beacon interval as from the frame
  * @capability: the capability field in host byte order
@@ -1424,6 +1456,7 @@ struct cfg80211_bss_ies {
  */
 struct cfg80211_bss {
        struct ieee80211_channel *channel;
+       enum nl80211_bss_scan_width scan_width;
 
        const struct cfg80211_bss_ies __rcu *ies;
        const struct cfg80211_bss_ies __rcu *beacon_ies;
@@ -1509,7 +1542,7 @@ enum cfg80211_assoc_req_flags {
  * @prev_bssid: previous BSSID, if not %NULL use reassociate frame
  * @flags:  See &enum cfg80211_assoc_req_flags
  * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
- *   will be used in ht_capa.  Un-supported values will be ignored.
+ *     will be used in ht_capa.  Un-supported values will be ignored.
  * @ht_capa_mask:  The bits of ht_capa which are to be used.
  * @vht_capa: VHT capability override
  * @vht_capa_mask: VHT capability mask indicating which fields to use
@@ -1592,6 +1625,9 @@ struct cfg80211_disassoc_request {
  *     user space. Otherwise, port is marked authorized by default.
  * @basic_rates: bitmap of basic rates to use when creating the IBSS
  * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
+ * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
+ *     will be used in ht_capa.  Un-supported values will be ignored.
+ * @ht_capa_mask:  The bits of ht_capa which are to be used.
  */
 struct cfg80211_ibss_params {
        u8 *ssid;
@@ -1605,6 +1641,8 @@ struct cfg80211_ibss_params {
        bool privacy;
        bool control_port;
        int mcast_rate[IEEE80211_NUM_BANDS];
+       struct ieee80211_ht_cap ht_capa;
+       struct ieee80211_ht_cap ht_capa_mask;
 };
 
 /**
@@ -1630,9 +1668,9 @@ struct cfg80211_ibss_params {
  * @key: WEP key for shared key authentication
  * @flags:  See &enum cfg80211_assoc_req_flags
  * @bg_scan_period:  Background scan period in seconds
- *   or -1 to indicate that default value is to be used.
+ *     or -1 to indicate that default value is to be used.
  * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
- *   will be used in ht_capa.  Un-supported values will be ignored.
+ *     will be used in ht_capa.  Un-supported values will be ignored.
  * @ht_capa_mask:  The bits of ht_capa which are to be used.
  * @vht_capa:  VHT Capability overrides
  * @vht_capa_mask: The bits of vht_capa which are to be used.
@@ -1698,7 +1736,7 @@ struct cfg80211_pmksa {
 };
 
 /**
- * struct cfg80211_wowlan_trig_pkt_pattern - packet pattern
+ * struct cfg80211_pkt_pattern - packet pattern
  * @mask: bitmask where to match pattern and where to ignore bytes,
  *     one bit per byte, in same format as nl80211
  * @pattern: bytes to match where bitmask is 1
@@ -1708,7 +1746,7 @@ struct cfg80211_pmksa {
  * Internal note: @mask and @pattern are allocated in one chunk of
  * memory, free @mask only!
  */
-struct cfg80211_wowlan_trig_pkt_pattern {
+struct cfg80211_pkt_pattern {
        u8 *mask, *pattern;
        int pattern_len;
        int pkt_offset;
@@ -1770,11 +1808,40 @@ struct cfg80211_wowlan {
        bool any, disconnect, magic_pkt, gtk_rekey_failure,
             eap_identity_req, four_way_handshake,
             rfkill_release;
-       struct cfg80211_wowlan_trig_pkt_pattern *patterns;
+       struct cfg80211_pkt_pattern *patterns;
        struct cfg80211_wowlan_tcp *tcp;
        int n_patterns;
 };
 
+/**
+ * struct cfg80211_coalesce_rules - Coalesce rule parameters
+ *
+ * This structure defines coalesce rule for the device.
+ * @delay: maximum coalescing delay in msecs.
+ * @condition: condition for packet coalescence.
+ *     see &enum nl80211_coalesce_condition.
+ * @patterns: array of packet patterns
+ * @n_patterns: number of patterns
+ */
+struct cfg80211_coalesce_rules {
+       int delay;
+       enum nl80211_coalesce_condition condition;
+       struct cfg80211_pkt_pattern *patterns;
+       int n_patterns;
+};
+
+/**
+ * struct cfg80211_coalesce - Packet coalescing settings
+ *
+ * This structure defines coalescing settings.
+ * @rules: array of coalesce rules
+ * @n_rules: number of rules
+ */
+struct cfg80211_coalesce {
+       struct cfg80211_coalesce_rules *rules;
+       int n_rules;
+};
+
 /**
  * struct cfg80211_wowlan_wakeup - wakeup report
  * @disconnect: woke up by getting disconnected
@@ -2071,6 +2138,7 @@ struct cfg80211_update_ft_ies_params {
  *     driver can take the most appropriate actions.
  * @crit_proto_stop: Indicates critical protocol no longer needs increased link
  *     reliability. This operation can not fail.
+ * @set_coalesce: Set coalesce parameters.
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -2306,6 +2374,8 @@ struct cfg80211_ops {
                                    u16 duration);
        void    (*crit_proto_stop)(struct wiphy *wiphy,
                                   struct wireless_dev *wdev);
+       int     (*set_coalesce)(struct wiphy *wiphy,
+                               struct cfg80211_coalesce *coalesce);
 };
 
 /*
@@ -2531,6 +2601,25 @@ struct wiphy_wowlan_support {
        const struct wiphy_wowlan_tcp_support *tcp;
 };
 
+/**
+ * struct wiphy_coalesce_support - coalesce support data
+ * @n_rules: maximum number of coalesce rules
+ * @max_delay: maximum supported coalescing delay in msecs
+ * @n_patterns: number of supported patterns in a rule
+ *     (see nl80211.h for the pattern definition)
+ * @pattern_max_len: maximum length of each pattern
+ * @pattern_min_len: minimum length of each pattern
+ * @max_pkt_offset: maximum Rx packet offset
+ */
+struct wiphy_coalesce_support {
+       int n_rules;
+       int max_delay;
+       int n_patterns;
+       int pattern_max_len;
+       int pattern_min_len;
+       int max_pkt_offset;
+};
+
 /**
  * struct wiphy - wireless hardware description
  * @reg_notifier: the driver's regulatory notification callback,
@@ -2641,6 +2730,7 @@ struct wiphy_wowlan_support {
  *     802.11-2012 8.4.2.29 for the defined fields.
  * @extended_capabilities_mask: mask of the valid values
  * @extended_capabilities_len: length of the extended capabilities
+ * @coalesce: packet coalescing support information
  */
 struct wiphy {
        /* assign these fields before you register the wiphy */
@@ -2750,6 +2840,8 @@ struct wiphy {
        const struct iw_handler_def *wext;
 #endif
 
+       const struct wiphy_coalesce_support *coalesce;
+
        char priv[0] __aligned(NETDEV_ALIGN);
 };
 
@@ -3063,11 +3155,13 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
 /**
  * ieee80211_mandatory_rates - get mandatory rates for a given band
  * @sband: the band to look for rates in
+ * @scan_width: width of the control channel
  *
  * This function returns a bitmap of the mandatory rates for the given
  * band, bits are set according to the rate position in the bitrates array.
  */
-u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband);
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
+                             enum nl80211_bss_scan_width scan_width);
 
 /*
  * Radiotap parsing functions -- for controlled injection support
@@ -3379,10 +3473,11 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy);
 void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
 
 /**
- * cfg80211_inform_bss_frame - inform cfg80211 of a received BSS frame
+ * cfg80211_inform_bss_width_frame - inform cfg80211 of a received BSS frame
  *
  * @wiphy: the wiphy reporting the BSS
  * @channel: The channel the frame was received on
+ * @scan_width: width of the control channel
  * @mgmt: the management frame (probe response or beacon)
  * @len: length of the management frame
  * @signal: the signal strength, type depends on the wiphy's signal_type
@@ -3395,16 +3490,29 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
  * Or %NULL on error.
  */
 struct cfg80211_bss * __must_check
+cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
+                               struct ieee80211_channel *channel,
+                               enum nl80211_bss_scan_width scan_width,
+                               struct ieee80211_mgmt *mgmt, size_t len,
+                               s32 signal, gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
 cfg80211_inform_bss_frame(struct wiphy *wiphy,
                          struct ieee80211_channel *channel,
                          struct ieee80211_mgmt *mgmt, size_t len,
-                         s32 signal, gfp_t gfp);
+                         s32 signal, gfp_t gfp)
+{
+       return cfg80211_inform_bss_width_frame(wiphy, channel,
+                                              NL80211_BSS_CHAN_WIDTH_20,
+                                              mgmt, len, signal, gfp);
+}
 
 /**
  * cfg80211_inform_bss - inform cfg80211 of a new BSS
  *
  * @wiphy: the wiphy reporting the BSS
  * @channel: The channel the frame was received on
+ * @scan_width: width of the control channel
  * @bssid: the BSSID of the BSS
  * @tsf: the TSF sent by the peer in the beacon/probe response (or 0)
  * @capability: the capability field sent by the peer
@@ -3421,11 +3529,26 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
  * Or %NULL on error.
  */
 struct cfg80211_bss * __must_check
+cfg80211_inform_bss_width(struct wiphy *wiphy,
+                         struct ieee80211_channel *channel,
+                         enum nl80211_bss_scan_width scan_width,
+                         const u8 *bssid, u64 tsf, u16 capability,
+                         u16 beacon_interval, const u8 *ie, size_t ielen,
+                         s32 signal, gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
 cfg80211_inform_bss(struct wiphy *wiphy,
                    struct ieee80211_channel *channel,
                    const u8 *bssid, u64 tsf, u16 capability,
                    u16 beacon_interval, const u8 *ie, size_t ielen,
-                   s32 signal, gfp_t gfp);
+                   s32 signal, gfp_t gfp)
+{
+       return cfg80211_inform_bss_width(wiphy, channel,
+                                        NL80211_BSS_CHAN_WIDTH_20,
+                                        bssid, tsf, capability,
+                                        beacon_interval, ie, ielen, signal,
+                                        gfp);
+}
 
 struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
                                      struct ieee80211_channel *channel,
@@ -3471,6 +3594,19 @@ void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
  */
 void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
 
+static inline enum nl80211_bss_scan_width
+cfg80211_chandef_to_scan_width(const struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_5:
+               return NL80211_BSS_CHAN_WIDTH_5;
+       case NL80211_CHAN_WIDTH_10:
+               return NL80211_BSS_CHAN_WIDTH_10;
+       default:
+               return NL80211_BSS_CHAN_WIDTH_20;
+       }
+}
+
 /**
  * cfg80211_rx_mlme_mgmt - notification of processed MLME management frame
  * @dev: network device
index c6d07cb074bc3ed04ec8ba76e417248814196e8c..8b5b714332971c547faa032574f56010f7ae3e15 100644 (file)
@@ -230,6 +230,10 @@ enum ieee80211_radiotap_type {
 #define        IEEE80211_CHAN_PASSIVE  0x0200  /* Only passive scan allowed */
 #define        IEEE80211_CHAN_DYN      0x0400  /* Dynamic CCK-OFDM channel */
 #define        IEEE80211_CHAN_GFSK     0x0800  /* GFSK channel (FHSS PHY) */
+#define        IEEE80211_CHAN_GSM      0x1000  /* GSM (900 MHz) */
+#define        IEEE80211_CHAN_STURBO   0x2000  /* Static Turbo */
+#define        IEEE80211_CHAN_HALF     0x4000  /* Half channel (10 MHz wide) */
+#define        IEEE80211_CHAN_QUARTER  0x8000  /* Quarter channel (5 MHz wide) */
 
 /* For IEEE80211_RADIOTAP_FLAGS */
 #define        IEEE80211_RADIOTAP_F_CFP        0x01    /* sent/received
index 5b7a3dadaddec1b7cabb919a1cf9919b90ff638d..3124036285ebe6b048c2731dc8d51e4e58e7b7ad 100644 (file)
@@ -811,6 +811,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
  *     is stored in the @ampdu_delimiter_crc field)
  * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
+ * @RX_FLAG_10MHZ: 10 MHz (half channel) was used
+ * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
  */
 enum mac80211_rx_flags {
        RX_FLAG_MMIC_ERROR              = BIT(0),
@@ -839,6 +841,8 @@ enum mac80211_rx_flags {
        RX_FLAG_80P80MHZ                = BIT(24),
        RX_FLAG_160MHZ                  = BIT(25),
        RX_FLAG_STBC_MASK               = BIT(26) | BIT(27),
+       RX_FLAG_10MHZ                   = BIT(28),
+       RX_FLAG_5MHZ                    = BIT(29),
 };
 
 #define RX_FLAG_STBC_SHIFT             26
@@ -1004,11 +1008,11 @@ enum ieee80211_smps_mode {
  * @radar_enabled: whether radar detection is enabled
  *
  * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame
- *    (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
- *    but actually means the number of transmissions not the number of retries
+ *     (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
+ *     but actually means the number of transmissions not the number of retries
  * @short_frame_max_tx_count: Maximum number of transmissions for a "short"
- *    frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
- *    number of transmissions not the number of retries
+ *     frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
+ *     number of transmissions not the number of retries
  *
  * @smps_mode: spatial multiplexing powersave mode; note that
  *     %IEEE80211_SMPS_STATIC is used when the device is not
@@ -1092,7 +1096,7 @@ enum ieee80211_vif_flags {
  *     be off when it is %NULL there can still be races and packets could be
  *     processed after it switches back to %NULL.
  * @debugfs_dir: debugfs dentry, can be used by drivers to create own per
- *      interface debug files. Note that it will be NULL for the virtual
+ *     interface debug files. Note that it will be NULL for the virtual
  *     monitor interface (if that is requested.)
  * @drv_priv: data area for driver use, will always be aligned to
  *     sizeof(void *).
@@ -1425,10 +1429,10 @@ struct ieee80211_tx_control {
  *     the stack.
  *
  * @IEEE80211_HW_CONNECTION_MONITOR:
- *      The hardware performs its own connection monitoring, including
- *      periodic keep-alives to the AP and probing the AP on beacon loss.
- *      When this flag is set, signaling beacon-loss will cause an immediate
- *      change to disassociated state.
+ *     The hardware performs its own connection monitoring, including
+ *     periodic keep-alives to the AP and probing the AP on beacon loss.
+ *     When this flag is set, signaling beacon-loss will cause an immediate
+ *     change to disassociated state.
  *
  * @IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC:
  *     This device needs to get data from beacon before association (i.e.
@@ -1526,10 +1530,10 @@ enum ieee80211_hw_flags {
  * @channel_change_time: time (in microseconds) it takes to change channels.
  *
  * @max_signal: Maximum value for signal (rssi) in RX information, used
- *     only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB
+ *     only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB
  *
  * @max_listen_interval: max listen interval in units of beacon interval
- *     that HW supports
+ *     that HW supports
  *
  * @queues: number of available hardware transmit queues for
  *     data packets. WMM/QoS requires at least four, these
@@ -2443,7 +2447,7 @@ enum ieee80211_roc_type {
  *     The callback can sleep.
  *
  * @set_tsf: Set the TSF timer to the specified value in the firmware/hardware.
- *      Currently, this is only used for IBSS mode debugging. Is not a
+ *     Currently, this is only used for IBSS mode debugging. Is not a
  *     required function.
  *     The callback can sleep.
  *
@@ -4204,8 +4208,10 @@ struct rate_control_ops {
 
        void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp);
        void (*rate_init)(void *priv, struct ieee80211_supported_band *sband,
+                         struct cfg80211_chan_def *chandef,
                          struct ieee80211_sta *sta, void *priv_sta);
        void (*rate_update)(void *priv, struct ieee80211_supported_band *sband,
+                           struct cfg80211_chan_def *chandef,
                            struct ieee80211_sta *sta, void *priv_sta,
                            u32 changed);
        void (*free_sta)(void *priv, struct ieee80211_sta *sta,
index 861e5eba3953b613956a6ca24f632c13d2a1e937..eb68735b33180fac625f3ac7e38137dd0c0f588e 100644 (file)
  * interfaces that a given device supports.
  */
 
+/**
+ * DOC: packet coalesce support
+ *
+ * In most cases, host that receives IPv4 and IPv6 multicast/broadcast
+ * packets does not do anything with these packets. Therefore the
+ * reception of these unwanted packets causes unnecessary processing
+ * and power consumption.
+ *
+ * Packet coalesce feature helps to reduce number of received interrupts
+ * to host by buffering these packets in firmware/hardware for some
+ * predefined time. Received interrupt will be generated when one of the
+ * following events occur.
+ * a) Expiration of hardware timer whose expiration time is set to maximum
+ * coalescing delay of matching coalesce rule.
+ * b) Coalescing buffer in hardware reaches it's limit.
+ * c) Packet doesn't match any of the configured coalesce rules.
+ *
+ * User needs to configure following parameters for creating a coalesce
+ * rule.
+ * a) Maximum coalescing delay
+ * b) List of packet patterns which needs to be matched
+ * c) Condition for coalescence. pattern 'match' or 'no match'
+ * Multiple such rules can be created.
+ */
+
 /**
  * enum nl80211_commands - supported nl80211 commands
  *
  * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can
  *     return back to normal.
  *
+ * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
+ * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -810,6 +838,9 @@ enum nl80211_commands {
        NL80211_CMD_CRIT_PROTOCOL_START,
        NL80211_CMD_CRIT_PROTOCOL_STOP,
 
+       NL80211_CMD_GET_COALESCE,
+       NL80211_CMD_SET_COALESCE,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -1436,6 +1467,8 @@ enum nl80211_commands {
  *     allowed to be used with the first @NL80211_CMD_SET_STATION command to
  *     update a TDLS peer STA entry.
  *
+ * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1736,6 +1769,8 @@ enum nl80211_attrs {
 
        NL80211_ATTR_PEER_AID,
 
+       NL80211_ATTR_COALESCE_RULE,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2772,6 +2807,21 @@ enum nl80211_chan_width {
        NL80211_CHAN_WIDTH_10,
 };
 
+/**
+ * enum nl80211_bss_scan_width - control channel width for a BSS
+ *
+ * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute.
+ *
+ * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible
+ * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide
+ */
+enum nl80211_bss_scan_width {
+       NL80211_BSS_CHAN_WIDTH_20,
+       NL80211_BSS_CHAN_WIDTH_10,
+       NL80211_BSS_CHAN_WIDTH_5,
+};
+
 /**
  * enum nl80211_bss - netlink attributes for a BSS
  *
@@ -2796,6 +2846,8 @@ enum nl80211_chan_width {
  * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information
  *     elements from a Beacon frame (bin); not present if no Beacon frame has
  *     yet been received
+ * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel
+ *     (u32, enum nl80211_bss_scan_width)
  * @__NL80211_BSS_AFTER_LAST: internal
  * @NL80211_BSS_MAX: highest BSS attribute
  */
@@ -2812,6 +2864,7 @@ enum nl80211_bss {
        NL80211_BSS_STATUS,
        NL80211_BSS_SEEN_MS_AGO,
        NL80211_BSS_BEACON_IES,
+       NL80211_BSS_CHAN_WIDTH,
 
        /* keep last */
        __NL80211_BSS_AFTER_LAST,
@@ -3060,11 +3113,11 @@ enum nl80211_tx_power_setting {
 };
 
 /**
- * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute
- * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute
- * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has
+ * enum nl80211_packet_pattern_attr - packet pattern attribute
+ * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute
+ * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has
  *     a zero bit are ignored
- * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have
+ * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have
  *     a bit for each byte in the pattern. The lowest-order bit corresponds
  *     to the first byte of the pattern, but the bytes of the pattern are
  *     in a little-endian-like format, i.e. the 9th byte of the pattern
@@ -3075,39 +3128,50 @@ enum nl80211_tx_power_setting {
  *     Note that the pattern matching is done as though frames were not
  *     802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
  *     first (including SNAP header unpacking) and then matched.
- * @NL80211_WOWLAN_PKTPAT_OFFSET: packet offset, pattern is matched after
+ * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after
  *     these fixed number of bytes of received packet
- * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes
- * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number
+ * @NUM_NL80211_PKTPAT: number of attributes
+ * @MAX_NL80211_PKTPAT: max attribute number
  */
-enum nl80211_wowlan_packet_pattern_attr {
-       __NL80211_WOWLAN_PKTPAT_INVALID,
-       NL80211_WOWLAN_PKTPAT_MASK,
-       NL80211_WOWLAN_PKTPAT_PATTERN,
-       NL80211_WOWLAN_PKTPAT_OFFSET,
+enum nl80211_packet_pattern_attr {
+       __NL80211_PKTPAT_INVALID,
+       NL80211_PKTPAT_MASK,
+       NL80211_PKTPAT_PATTERN,
+       NL80211_PKTPAT_OFFSET,
 
-       NUM_NL80211_WOWLAN_PKTPAT,
-       MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1,
+       NUM_NL80211_PKTPAT,
+       MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1,
 };
 
 /**
- * struct nl80211_wowlan_pattern_support - pattern support information
+ * struct nl80211_pattern_support - packet pattern support information
  * @max_patterns: maximum number of patterns supported
  * @min_pattern_len: minimum length of each pattern
  * @max_pattern_len: maximum length of each pattern
  * @max_pkt_offset: maximum Rx packet offset
  *
  * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
- * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the
- * capability information given by the kernel to userspace.
+ * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in
+ * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of
+ * %NL80211_ATTR_COALESCE_RULE in the capability information given
+ * by the kernel to userspace.
  */
-struct nl80211_wowlan_pattern_support {
+struct nl80211_pattern_support {
        __u32 max_patterns;
        __u32 min_pattern_len;
        __u32 max_pattern_len;
        __u32 max_pkt_offset;
 } __attribute__((packed));
 
+/* only for backward compatibility */
+#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID
+#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK
+#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN
+#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET
+#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT
+#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT
+#define nl80211_wowlan_pattern_support nl80211_pattern_support
+
 /**
  * enum nl80211_wowlan_triggers - WoWLAN trigger definitions
  * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes
@@ -3127,7 +3191,7 @@ struct nl80211_wowlan_pattern_support {
  *     pattern matching is done after the packet is converted to the MSDU.
  *
  *     In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
- *     carrying a &struct nl80211_wowlan_pattern_support.
+ *     carrying a &struct nl80211_pattern_support.
  *
  *     When reporting wakeup. it is a u32 attribute containing the 0-based
  *     index of the pattern that caused the wakeup, in the patterns passed
@@ -3284,7 +3348,7 @@ struct nl80211_wowlan_tcp_data_token_feature {
  * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a
  *     u32 attribute holding the maximum length
  * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for
- *     feature advertising. The mask works like @NL80211_WOWLAN_PKTPAT_MASK
+ *     feature advertising. The mask works like @NL80211_PKTPAT_MASK
  *     but on the TCP payload only.
  * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes
  * @MAX_NL80211_WOWLAN_TCP: highest attribute number
@@ -3308,6 +3372,55 @@ enum nl80211_wowlan_tcp_attrs {
        MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1
 };
 
+/**
+ * struct nl80211_coalesce_rule_support - coalesce rule support information
+ * @max_rules: maximum number of rules supported
+ * @pat: packet pattern support information
+ * @max_delay: maximum supported coalescing delay in msecs
+ *
+ * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the
+ * capability information given by the kernel to userspace.
+ */
+struct nl80211_coalesce_rule_support {
+       __u32 max_rules;
+       struct nl80211_pattern_support pat;
+       __u32 max_delay;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_attr_coalesce_rule - coalesce rule attribute
+ * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute
+ * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing
+ * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence,
+ *     see &enum nl80211_coalesce_condition.
+ * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched
+ *     after these fixed number of bytes of received packet
+ * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes
+ * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number
+ */
+enum nl80211_attr_coalesce_rule {
+       __NL80211_COALESCE_RULE_INVALID,
+       NL80211_ATTR_COALESCE_RULE_DELAY,
+       NL80211_ATTR_COALESCE_RULE_CONDITION,
+       NL80211_ATTR_COALESCE_RULE_PKT_PATTERN,
+
+       /* keep last */
+       NUM_NL80211_ATTR_COALESCE_RULE,
+       NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1
+};
+
+/**
+ * enum nl80211_coalesce_condition - coalesce rule conditions
+ * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
+ *     in a rule are matched.
+ * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
+ *     in a rule are not matched.
+ */
+enum nl80211_coalesce_condition {
+       NL80211_COALESCE_CONDITION_MATCH,
+       NL80211_COALESCE_CONDITION_NO_MATCH
+};
+
 /**
  * enum nl80211_iface_limit_attrs - limit attributes
  * @NL80211_IFACE_LIMIT_UNSPEC: (reserved)
index 43dd7525bfcba54c02dbd9dde2bd89cfc2d29c97..973594b229f400bbae825034d32f69b07c51c9f7 100644 (file)
@@ -395,9 +395,13 @@ void sta_set_rate_info_tx(struct sta_info *sta,
                rinfo->nss = ieee80211_rate_get_vht_nss(rate);
        } else {
                struct ieee80211_supported_band *sband;
+               int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+               u16 brate;
+
                sband = sta->local->hw.wiphy->bands[
                                ieee80211_get_sdata_band(sta->sdata)];
-               rinfo->legacy = sband->bitrates[rate->idx].bitrate;
+               brate = sband->bitrates[rate->idx].bitrate;
+               rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
        }
        if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
                rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
@@ -422,11 +426,13 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
                rinfo->mcs = sta->last_rx_rate_idx;
        } else {
                struct ieee80211_supported_band *sband;
+               int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+               u16 brate;
 
                sband = sta->local->hw.wiphy->bands[
                                ieee80211_get_sdata_band(sta->sdata)];
-               rinfo->legacy =
-                       sband->bitrates[sta->last_rx_rate_idx].bitrate;
+               brate = sband->bitrates[sta->last_rx_rate_idx].bitrate;
+               rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
        }
 
        if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
@@ -1192,8 +1198,6 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                                struct station_parameters *params)
 {
        int ret = 0;
-       u32 rates;
-       int i, j;
        struct ieee80211_supported_band *sband;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
@@ -1286,16 +1290,10 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                sta->listen_interval = params->listen_interval;
 
        if (params->supported_rates) {
-               rates = 0;
-
-               for (i = 0; i < params->supported_rates_len; i++) {
-                       int rate = (params->supported_rates[i] & 0x7f) * 5;
-                       for (j = 0; j < sband->n_bitrates; j++) {
-                               if (sband->bitrates[j].bitrate == rate)
-                                       rates |= BIT(j);
-                       }
-               }
-               sta->sta.supp_rates[band] = rates;
+               ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
+                                        sband, params->supported_rates,
+                                        params->supported_rates_len,
+                                        &sta->sta.supp_rates[band]);
        }
 
        if (params->ht_capa)
@@ -1958,18 +1956,11 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
        }
 
        if (params->basic_rates) {
-               int i, j;
-               u32 rates = 0;
-               struct ieee80211_supported_band *sband = wiphy->bands[band];
-
-               for (i = 0; i < params->basic_rates_len; i++) {
-                       int rate = (params->basic_rates[i] & 0x7f) * 5;
-                       for (j = 0; j < sband->n_bitrates; j++) {
-                               if (sband->bitrates[j].bitrate == rate)
-                                       rates |= BIT(j);
-                       }
-               }
-               sdata->vif.bss_conf.basic_rates = rates;
+               ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
+                                        wiphy->bands[band],
+                                        params->basic_rates,
+                                        params->basic_rates_len,
+                                        &sdata->vif.bss_conf.basic_rates);
                changed |= BSS_CHANGED_BASIC_RATES;
        }
 
index f83534f6a2eec14abcb237196e4d6a233e2f8136..529bf58bc14511beae95c4ff250ec54eff5c9710 100644 (file)
 #include "ieee80211_i.h"
 #include "rate.h"
 
-static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata,
+static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa,
+                                 struct ieee80211_ht_cap *ht_capa_mask,
                                  struct ieee80211_sta_ht_cap *ht_cap,
                                  u16 flag)
 {
        __le16 le_flag = cpu_to_le16(flag);
-       if (sdata->u.mgd.ht_capa_mask.cap_info & le_flag) {
-               if (!(sdata->u.mgd.ht_capa.cap_info & le_flag))
+       if (ht_capa_mask->cap_info & le_flag) {
+               if (!(ht_capa->cap_info & le_flag))
                        ht_cap->cap &= ~flag;
        }
 }
@@ -33,13 +34,30 @@ static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata,
 void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
                                     struct ieee80211_sta_ht_cap *ht_cap)
 {
-       u8 *scaps = (u8 *)(&sdata->u.mgd.ht_capa.mcs.rx_mask);
-       u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask);
+       struct ieee80211_ht_cap *ht_capa, *ht_capa_mask;
+       u8 *scaps, *smask;
        int i;
 
        if (!ht_cap->ht_supported)
                return;
 
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_STATION:
+               ht_capa = &sdata->u.mgd.ht_capa;
+               ht_capa_mask = &sdata->u.mgd.ht_capa_mask;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               ht_capa = &sdata->u.ibss.ht_capa;
+               ht_capa_mask = &sdata->u.ibss.ht_capa_mask;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return;
+       }
+
+       scaps = (u8 *)(&ht_capa->mcs.rx_mask);
+       smask = (u8 *)(&ht_capa_mask->mcs.rx_mask);
+
        /* NOTE:  If you add more over-rides here, update register_hw
         * ht_capa_mod_msk logic in main.c as well.
         * And, if this method can ever change ht_cap.ht_supported, fix
@@ -55,28 +73,32 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
        }
 
        /* Force removal of HT-40 capabilities? */
-       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40);
-       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40);
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_SGI_40);
 
        /* Allow user to disable SGI-20 (SGI-40 is handled above) */
-       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_20);
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_SGI_20);
 
        /* Allow user to disable the max-AMSDU bit. */
-       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU);
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_MAX_AMSDU);
 
        /* Allow user to decrease AMPDU factor */
-       if (sdata->u.mgd.ht_capa_mask.ampdu_params_info &
+       if (ht_capa_mask->ampdu_params_info &
            IEEE80211_HT_AMPDU_PARM_FACTOR) {
-               u8 n = sdata->u.mgd.ht_capa.ampdu_params_info
-                       & IEEE80211_HT_AMPDU_PARM_FACTOR;
+               u8 n = ht_capa->ampdu_params_info &
+                      IEEE80211_HT_AMPDU_PARM_FACTOR;
                if (n < ht_cap->ampdu_factor)
                        ht_cap->ampdu_factor = n;
        }
 
        /* Allow the user to increase AMPDU density. */
-       if (sdata->u.mgd.ht_capa_mask.ampdu_params_info &
+       if (ht_capa_mask->ampdu_params_info &
            IEEE80211_HT_AMPDU_PARM_DENSITY) {
-               u8 n = (sdata->u.mgd.ht_capa.ampdu_params_info &
+               u8 n = (ht_capa->ampdu_params_info &
                        IEEE80211_HT_AMPDU_PARM_DENSITY)
                        >> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT;
                if (n > ht_cap->ampdu_density)
@@ -112,7 +134,8 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
         * we advertised a restricted capability set to. Override
         * our own capabilities and then use those below.
         */
-       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+       if ((sdata->vif.type == NL80211_IFTYPE_STATION ||
+            sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
            !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
                ieee80211_apply_htcap_overrides(sdata, &own_cap);
 
index ea7b9c2c7e66db19a811244d82e907bdb79620f1..5e6836c3aa4c108a6e1c6fe2430a95d0ec5faba0 100644 (file)
@@ -43,16 +43,18 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct ieee80211_local *local = sdata->local;
-       int ratesi;
+       int rates_n = 0, i, ri;
        struct ieee80211_mgmt *mgmt;
        u8 *pos;
        struct ieee80211_supported_band *sband;
        struct cfg80211_bss *bss;
-       u32 bss_change;
-       u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
+       u32 bss_change, rate_flags, rates = 0, rates_added = 0;
        struct cfg80211_chan_def chandef;
+       enum nl80211_bss_scan_width scan_width;
+       bool have_higher_than_11mbit = false;
        struct beacon_data *presp;
        int frame_len;
+       int shift;
 
        sdata_assert_lock(sdata);
 
@@ -83,6 +85,14 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        chandef = ifibss->chandef;
        if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+               if (chandef.width == NL80211_CHAN_WIDTH_5 ||
+                   chandef.width == NL80211_CHAN_WIDTH_10 ||
+                   chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
+                   chandef.width == NL80211_CHAN_WIDTH_20) {
+                       sdata_info(sdata,
+                                  "Failed to join IBSS, beacons forbidden\n");
+                       return;
+               }
                chandef.width = NL80211_CHAN_WIDTH_20;
                chandef.center_freq1 = chan->center_freq;
        }
@@ -99,6 +109,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        memcpy(ifibss->bssid, bssid, ETH_ALEN);
 
        sband = local->hw.wiphy->bands[chan->band];
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        /* Build IBSS probe response */
        frame_len = sizeof(struct ieee80211_hdr_3addr) +
@@ -134,15 +145,33 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        memcpy(pos, ifibss->ssid, ifibss->ssid_len);
        pos += ifibss->ssid_len;
 
-       rates = min_t(int, 8, sband->n_bitrates);
+       rate_flags = ieee80211_chandef_rate_flags(&chandef);
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+               if (sband->bitrates[i].bitrate > 110)
+                       have_higher_than_11mbit = true;
+
+               rates |= BIT(i);
+               rates_n++;
+       }
+
        *pos++ = WLAN_EID_SUPP_RATES;
-       *pos++ = rates;
-       for (i = 0; i < rates; i++) {
-               int rate = sband->bitrates[i].bitrate;
+       *pos++ = min_t(int, 8, rates_n);
+       for (ri = 0; ri < sband->n_bitrates; ri++) {
+               int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
+                                       5 * (1 << shift));
                u8 basic = 0;
-               if (basic_rates & BIT(i))
+               if (!(rates & BIT(ri)))
+                       continue;
+
+               if (basic_rates & BIT(ri))
                        basic = 0x80;
-               *pos++ = basic | (u8) (rate / 5);
+               *pos++ = basic | (u8) rate;
+               if (++rates_added == 8) {
+                       ri++; /* continue at next rate for EXT_SUPP_RATES */
+                       break;
+               }
        }
 
        if (sband->band == IEEE80211_BAND_2GHZ) {
@@ -157,15 +186,20 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        *pos++ = 0;
        *pos++ = 0;
 
-       if (sband->n_bitrates > 8) {
+       /* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */
+       if (rates_n > 8) {
                *pos++ = WLAN_EID_EXT_SUPP_RATES;
-               *pos++ = sband->n_bitrates - 8;
-               for (i = 8; i < sband->n_bitrates; i++) {
-                       int rate = sband->bitrates[i].bitrate;
+               *pos++ = rates_n - 8;
+               for (; ri < sband->n_bitrates; ri++) {
+                       int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
+                                               5 * (1 << shift));
                        u8 basic = 0;
-                       if (basic_rates & BIT(i))
+                       if (!(rates & BIT(ri)))
+                               continue;
+
+                       if (basic_rates & BIT(ri))
                                basic = 0x80;
-                       *pos++ = basic | (u8) (rate / 5);
+                       *pos++ = basic | (u8) rate;
                }
        }
 
@@ -179,8 +213,12 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
            chandef.width != NL80211_CHAN_WIDTH_5 &&
            chandef.width != NL80211_CHAN_WIDTH_10 &&
            sband->ht_cap.ht_supported) {
-               pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
-                                               sband->ht_cap.cap);
+               struct ieee80211_sta_ht_cap ht_cap;
+
+               memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+               ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+
+               pos = ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
                /*
                 * Note: According to 802.11n-2009 9.13.3.1, HT Protection
                 * field and RIFS Mode are reserved in IBSS mode, therefore
@@ -236,18 +274,26 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        sdata->vif.bss_conf.use_short_slot = chan->band == IEEE80211_BAND_5GHZ;
        bss_change |= BSS_CHANGED_ERP_SLOT;
 
+       /* cf. IEEE 802.11 9.2.12 */
+       if (chan->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit)
+               sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
+       else
+               sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+
        sdata->vif.bss_conf.ibss_joined = true;
        sdata->vif.bss_conf.ibss_creator = creator;
        ieee80211_bss_info_change_notify(sdata, bss_change);
 
-       ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates);
+       ieee80211_set_wmm_default(sdata, true);
 
        ifibss->state = IEEE80211_IBSS_MLME_JOINED;
        mod_timer(&ifibss->timer,
                  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
 
-       bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan,
-                                       mgmt, presp->head_len, 0, GFP_KERNEL);
+       scan_width = cfg80211_chandef_to_scan_width(&chandef);
+       bss = cfg80211_inform_bss_width_frame(local->hw.wiphy, chan,
+                                             scan_width, mgmt,
+                                             presp->head_len, 0, GFP_KERNEL);
        cfg80211_put_bss(local->hw.wiphy, bss);
        netif_carrier_on(sdata->dev);
        cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
@@ -264,6 +310,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        u16 beacon_int = cbss->beacon_interval;
        const struct cfg80211_bss_ies *ies;
        u64 tsf;
+       u32 rate_flags;
+       int shift;
 
        sdata_assert_lock(sdata);
 
@@ -271,15 +319,24 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                beacon_int = 10;
 
        sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
+       rate_flags = ieee80211_chandef_rate_flags(&sdata->u.ibss.chandef);
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        basic_rates = 0;
 
        for (i = 0; i < bss->supp_rates_len; i++) {
-               int rate = (bss->supp_rates[i] & 0x7f) * 5;
+               int rate = bss->supp_rates[i] & 0x7f;
                bool is_basic = !!(bss->supp_rates[i] & 0x80);
 
                for (j = 0; j < sband->n_bitrates; j++) {
-                       if (sband->bitrates[j].bitrate == rate) {
+                       int brate;
+                       if ((rate_flags & sband->bitrates[j].flags)
+                           != rate_flags)
+                               continue;
+
+                       brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
+                                            5 * (1 << shift));
+                       if (brate == rate) {
                                if (is_basic)
                                        basic_rates |= BIT(j);
                                break;
@@ -335,6 +392,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_supported_band *sband;
+       enum nl80211_bss_scan_width scan_width;
        int band;
 
        /*
@@ -363,6 +421,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
        if (WARN_ON_ONCE(!chanctx_conf))
                return NULL;
        band = chanctx_conf->def.chan->band;
+       scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
        rcu_read_unlock();
 
        sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
@@ -376,7 +435,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
        /* make sure mandatory rates are always added */
        sband = local->hw.wiphy->bands[band];
        sta->sta.supp_rates[band] = supp_rates |
-                       ieee80211_mandatory_rates(sband);
+                       ieee80211_mandatory_rates(sband, scan_width);
 
        return ieee80211_ibss_finish_sta(sta);
 }
@@ -440,6 +499,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        u64 beacon_timestamp, rx_timestamp;
        u32 supp_rates = 0;
        enum ieee80211_band band = rx_status->band;
+       enum nl80211_bss_scan_width scan_width;
        struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
        bool rates_updated = false;
 
@@ -461,16 +521,22 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                sta = sta_info_get(sdata, mgmt->sa);
 
                if (elems->supp_rates) {
-                       supp_rates = ieee80211_sta_get_rates(local, elems,
+                       supp_rates = ieee80211_sta_get_rates(sdata, elems,
                                                             band, NULL);
                        if (sta) {
                                u32 prev_rates;
 
                                prev_rates = sta->sta.supp_rates[band];
                                /* make sure mandatory rates are always added */
-                               sta->sta.supp_rates[band] = supp_rates |
-                                       ieee80211_mandatory_rates(sband);
+                               scan_width = NL80211_BSS_CHAN_WIDTH_20;
+                               if (rx_status->flag & RX_FLAG_5MHZ)
+                                       scan_width = NL80211_BSS_CHAN_WIDTH_5;
+                               if (rx_status->flag & RX_FLAG_10MHZ)
+                                       scan_width = NL80211_BSS_CHAN_WIDTH_10;
 
+                               sta->sta.supp_rates[band] = supp_rates |
+                                       ieee80211_mandatory_rates(sband,
+                                                                 scan_width);
                                if (sta->sta.supp_rates[band] != prev_rates) {
                                        ibss_dbg(sdata,
                                                 "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
@@ -585,7 +651,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                         "beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n",
                         mgmt->bssid);
                ieee80211_sta_join_ibss(sdata, bss);
-               supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL);
+               supp_rates = ieee80211_sta_get_rates(sdata, elems, band, NULL);
                ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
                                       supp_rates);
                rcu_read_unlock();
@@ -604,6 +670,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_supported_band *sband;
+       enum nl80211_bss_scan_width scan_width;
        int band;
 
        /*
@@ -629,6 +696,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
                return;
        }
        band = chanctx_conf->def.chan->band;
+       scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
        rcu_read_unlock();
 
        sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
@@ -640,7 +708,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
        /* make sure mandatory rates are always added */
        sband = local->hw.wiphy->bands[band];
        sta->sta.supp_rates[band] = supp_rates |
-                       ieee80211_mandatory_rates(sband);
+                       ieee80211_mandatory_rates(sband, scan_width);
 
        spin_lock(&ifibss->incomplete_lock);
        list_add(&sta->list, &ifibss->incomplete_stations);
@@ -679,6 +747,7 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
 static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+       enum nl80211_bss_scan_width scan_width;
 
        sdata_assert_lock(sdata);
 
@@ -700,8 +769,9 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
        sdata_info(sdata,
                   "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n");
 
+       scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
        ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
-                                   NULL);
+                                   NULL, scan_width);
 }
 
 static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@@ -751,6 +821,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
        struct cfg80211_bss *cbss;
        struct ieee80211_channel *chan = NULL;
        const u8 *bssid = NULL;
+       enum nl80211_bss_scan_width scan_width;
        int active_ibss;
        u16 capability;
 
@@ -799,8 +870,10 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
                                        IEEE80211_SCAN_INTERVAL)) {
                sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
 
+               scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
                ieee80211_request_ibss_scan(sdata, ifibss->ssid,
-                                           ifibss->ssid_len, chan);
+                                           ifibss->ssid_len, chan,
+                                           scan_width);
        } else {
                int interval = IEEE80211_SCAN_INTERVAL;
 
@@ -1020,6 +1093,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
                        struct cfg80211_ibss_params *params)
 {
        u32 changed = 0;
+       u32 rate_flags;
+       struct ieee80211_supported_band *sband;
+       int i;
 
        if (params->bssid) {
                memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
@@ -1030,6 +1106,14 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        sdata->u.ibss.privacy = params->privacy;
        sdata->u.ibss.control_port = params->control_port;
        sdata->u.ibss.basic_rates = params->basic_rates;
+
+       /* fix basic_rates if channel does not support these rates */
+       rate_flags = ieee80211_chandef_rate_flags(&params->chandef);
+       sband = sdata->local->hw.wiphy->bands[params->chandef.chan->band];
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       sdata->u.ibss.basic_rates &= ~BIT(i);
+       }
        memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate,
               sizeof(params->mcast_rate));
 
@@ -1051,6 +1135,11 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len);
        sdata->u.ibss.ssid_len = params->ssid_len;
 
+       memcpy(&sdata->u.ibss.ht_capa, &params->ht_capa,
+              sizeof(sdata->u.ibss.ht_capa));
+       memcpy(&sdata->u.ibss.ht_capa_mask, &params->ht_capa_mask,
+              sizeof(sdata->u.ibss.ht_capa_mask));
+
        /*
         * 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is
         * reserved, but an HT STA shall protect HT transmissions as though
@@ -1131,6 +1220,11 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
        presp = rcu_dereference_protected(ifibss->presp,
                                          lockdep_is_held(&sdata->wdev.mtx));
        RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
+
+       /* on the next join, re-program HT parameters */
+       memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
+       memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask));
+
        sdata->vif.bss_conf.ibss_joined = false;
        sdata->vif.bss_conf.ibss_creator = false;
        sdata->vif.bss_conf.enable_beacon = false;
index 8412a303993a647eed61e6b0794e91e49b27dd29..3d32df1fbc6de92a5eb1e02ba532ba39b5533573 100644 (file)
@@ -509,6 +509,9 @@ struct ieee80211_if_ibss {
        /* probe response/beacon for IBSS */
        struct beacon_data __rcu *presp;
 
+       struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */
+       struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
+
        spinlock_t incomplete_lock;
        struct list_head incomplete_stations;
 
@@ -809,6 +812,34 @@ ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
        return band;
 }
 
+static inline int
+ieee80211_chandef_get_shift(struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_5:
+               return 2;
+       case NL80211_CHAN_WIDTH_10:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static inline int
+ieee80211_vif_get_shift(struct ieee80211_vif *vif)
+{
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       int shift = 0;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       if (chanctx_conf)
+               shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
+       rcu_read_unlock();
+
+       return shift;
+}
+
 enum sdata_queue_type {
        IEEE80211_SDATA_QUEUE_TYPE_FRAME        = 0,
        IEEE80211_SDATA_QUEUE_AGG_START         = 1,
@@ -1026,7 +1057,7 @@ struct ieee80211_local {
        struct cfg80211_ssid scan_ssid;
        struct cfg80211_scan_request *int_scan_req;
        struct cfg80211_scan_request *scan_req, *hw_scan_req;
-       struct ieee80211_channel *scan_channel;
+       struct cfg80211_chan_def scan_chandef;
        enum ieee80211_band hw_scan_band;
        int scan_channel_idx;
        int scan_ies_len;
@@ -1306,7 +1337,8 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 void ieee80211_scan_work(struct work_struct *work);
 int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
                                const u8 *ssid, u8 ssid_len,
-                               struct ieee80211_channel *chan);
+                               struct ieee80211_channel *chan,
+                               enum nl80211_bss_scan_width scan_width);
 int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
                           struct cfg80211_scan_request *req);
 void ieee80211_scan_cancel(struct ieee80211_local *local);
@@ -1465,7 +1497,8 @@ extern void *mac80211_wiphy_privid; /* for wiphy privid */
 u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
                        enum nl80211_iftype type);
 int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
-                            int rate, int erp, int short_preamble);
+                            int rate, int erp, int short_preamble,
+                            int shift);
 void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
                                     struct ieee80211_hdr *hdr, const u8 *tsc,
                                     gfp_t gfp);
@@ -1569,7 +1602,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                             size_t buffer_len, const u8 *ie, size_t ie_len,
                             enum ieee80211_band band, u32 rate_mask,
-                            u8 channel);
+                            struct cfg80211_chan_def *chandef);
 struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
                                          u8 *dst, u32 ratemask,
                                          struct ieee80211_channel *chan,
@@ -1582,10 +1615,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
                              u32 ratemask, bool directed, u32 tx_flags,
                              struct ieee80211_channel *channel, bool scan);
 
-void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
-                                 const size_t supp_rates_len,
-                                 const u8 *supp_rates);
-u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
+u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
                            struct ieee802_11_elems *elems,
                            enum ieee80211_band band, u32 *basic_rates);
 int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
@@ -1602,6 +1632,9 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
                               u16 prot_mode);
 u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
                               u32 cap);
+int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
+                            const struct ieee80211_supported_band *sband,
+                            const u8 *srates, int srates_len, u32 *rates);
 int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
                            struct sk_buff *skb, bool need_basic,
                            enum ieee80211_band band);
index cc117591f678463c2cd9a3eb4060af5e01bf51ca..4c41c11958c8a9de28fcae05465f44bc6712ca45 100644 (file)
@@ -54,7 +54,7 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
                return false;
        }
 
-       power = chanctx_conf->def.chan->max_power;
+       power = ieee80211_chandef_max_power(&chanctx_conf->def);
        rcu_read_unlock();
 
        if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
index 091088ac7890a9c7f873f623f7789cffb9e12def..25eb35b01938e9e91d5dfd75adcee8acb030036c 100644 (file)
@@ -102,17 +102,8 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
 
        offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
 
-       if (local->scan_channel) {
-               chandef.chan = local->scan_channel;
-               /* If scanning on oper channel, use whatever channel-type
-                * is currently in use.
-                */
-               if (chandef.chan == local->_oper_chandef.chan) {
-                       chandef = local->_oper_chandef;
-               } else {
-                       chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
-                       chandef.center_freq1 = chandef.chan->center_freq;
-               }
+       if (local->scan_chandef.chan) {
+               chandef = local->scan_chandef;
        } else if (local->tmp_channel) {
                chandef.chan = local->tmp_channel;
                chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
@@ -151,7 +142,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
                changed |= IEEE80211_CONF_CHANGE_SMPS;
        }
 
-       power = chandef.chan->max_power;
+       power = ieee80211_chandef_max_power(&chandef);
 
        rcu_read_lock();
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
index 447f41bbe744d79b570eb4c85dd4cdb8ffb72e70..885a5f6e2c219b5c4e17b6574bd32e4231ed39b1 100644 (file)
@@ -62,7 +62,6 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
                        struct ieee802_11_elems *ie)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-       struct ieee80211_local *local = sdata->local;
        u32 basic_rates = 0;
        struct cfg80211_chan_def sta_chan_def;
 
@@ -85,7 +84,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
             (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)))
                return false;
 
-       ieee80211_sta_get_rates(local, ie, ieee80211_get_sdata_band(sdata),
+       ieee80211_sta_get_rates(sdata, ie, ieee80211_get_sdata_band(sdata),
                                &basic_rates);
 
        if (sdata->vif.bss_conf.basic_rates != basic_rates)
@@ -274,7 +273,9 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
        neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS);
        *pos++ = neighbors << 1;
        /* Mesh capability */
-       *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING;
+       *pos = 0x00;
+       *pos |= ifmsh->mshcfg.dot11MeshForwarding ?
+                       IEEE80211_MESHCONF_CAPAB_FORWARDING : 0x00;
        *pos |= ifmsh->accepting_plinks ?
                        IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
        /* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */
index 02c05fa15c203b9a51dc576579f7455979fe0376..6b65d5055f5bf7572d29712c6345c88dd11b33a5 100644 (file)
@@ -379,7 +379,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
        u32 rates, basic_rates = 0, changed = 0;
 
        sband = local->hw.wiphy->bands[band];
-       rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates);
+       rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
 
        spin_lock_bh(&sta->lock);
        sta->last_rx = jiffies;
index ae31968d42d3b855eebe05dcf0e660b4d367ff8f..211246b4681927d74967ee9234076e436c9e7f8b 100644 (file)
@@ -478,27 +478,6 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
 
 /* frame sending functions */
 
-static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
-                                     struct ieee80211_supported_band *sband,
-                                     u32 *rates)
-{
-       int i, j, count;
-       *rates = 0;
-       count = 0;
-       for (i = 0; i < supp_rates_len; i++) {
-               int rate = (supp_rates[i] & 0x7F) * 5;
-
-               for (j = 0; j < sband->n_bitrates; j++)
-                       if (sband->bitrates[j].bitrate == rate) {
-                               *rates |= BIT(j);
-                               count++;
-                               break;
-                       }
-       }
-
-       return count;
-}
-
 static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
                                struct sk_buff *skb, u8 ap_ht_param,
                                struct ieee80211_supported_band *sband,
@@ -617,12 +596,12 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_mgmt *mgmt;
        u8 *pos, qos_info;
        size_t offset = 0, noffset;
-       int i, count, rates_len, supp_rates_len;
+       int i, count, rates_len, supp_rates_len, shift;
        u16 capab;
        struct ieee80211_supported_band *sband;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_channel *chan;
-       u32 rates = 0;
+       u32 rate_flags, rates = 0;
 
        sdata_assert_lock(sdata);
 
@@ -633,8 +612,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                return;
        }
        chan = chanctx_conf->def.chan;
+       rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
        rcu_read_unlock();
        sband = local->hw.wiphy->bands[chan->band];
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        if (assoc_data->supp_rates_len) {
                /*
@@ -643,17 +624,24 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                 * in the association request (e.g. D-Link DAP 1353 in
                 * b-only mode)...
                 */
-               rates_len = ieee80211_compatible_rates(assoc_data->supp_rates,
-                                                      assoc_data->supp_rates_len,
-                                                      sband, &rates);
+               rates_len = ieee80211_parse_bitrates(&chanctx_conf->def, sband,
+                                                    assoc_data->supp_rates,
+                                                    assoc_data->supp_rates_len,
+                                                    &rates);
        } else {
                /*
                 * In case AP not provide any supported rates information
                 * before association, we send information element(s) with
                 * all rates that we support.
                 */
-               rates = ~0;
-               rates_len = sband->n_bitrates;
+               rates_len = 0;
+               for (i = 0; i < sband->n_bitrates; i++) {
+                       if ((rate_flags & sband->bitrates[i].flags)
+                           != rate_flags)
+                               continue;
+                       rates |= BIT(i);
+                       rates_len++;
+               }
        }
 
        skb = alloc_skb(local->hw.extra_tx_headroom +
@@ -730,8 +718,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        count = 0;
        for (i = 0; i < sband->n_bitrates; i++) {
                if (BIT(i) & rates) {
-                       int rate = sband->bitrates[i].bitrate;
-                       *pos++ = (u8) (rate / 5);
+                       int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                               5 * (1 << shift));
+                       *pos++ = (u8) rate;
                        if (++count == 8)
                                break;
                }
@@ -744,8 +733,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
 
                for (i++; i < sband->n_bitrates; i++) {
                        if (BIT(i) & rates) {
-                               int rate = sband->bitrates[i].bitrate;
-                               *pos++ = (u8) (rate / 5);
+                               int rate;
+                               rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                                   5 * (1 << shift));
+                               *pos++ = (u8) rate;
                        }
                }
        }
@@ -756,7 +747,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                *pos++ = WLAN_EID_PWR_CAPABILITY;
                *pos++ = 2;
                *pos++ = 0; /* min tx power */
-               *pos++ = chan->max_power; /* max tx power */
+                /* max tx power */
+               *pos++ = ieee80211_chandef_max_power(&chanctx_conf->def);
 
                /* 2. supported channels */
                /* TODO: get this in reg domain format */
@@ -2432,15 +2424,16 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
                                u8 *supp_rates, unsigned int supp_rates_len,
                                u32 *rates, u32 *basic_rates,
                                bool *have_higher_than_11mbit,
-                               int *min_rate, int *min_rate_index)
+                               int *min_rate, int *min_rate_index,
+                               int shift, u32 rate_flags)
 {
        int i, j;
 
        for (i = 0; i < supp_rates_len; i++) {
-               int rate = (supp_rates[i] & 0x7f) * 5;
+               int rate = supp_rates[i] & 0x7f;
                bool is_basic = !!(supp_rates[i] & 0x80);
 
-               if (rate > 110)
+               if ((rate * 5 * (1 << shift)) > 110)
                        *have_higher_than_11mbit = true;
 
                /*
@@ -2456,12 +2449,20 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
                        continue;
 
                for (j = 0; j < sband->n_bitrates; j++) {
-                       if (sband->bitrates[j].bitrate == rate) {
+                       struct ieee80211_rate *br;
+                       int brate;
+
+                       br = &sband->bitrates[j];
+                       if ((rate_flags & br->flags) != rate_flags)
+                               continue;
+
+                       brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
+                       if (brate == rate) {
                                *rates |= BIT(j);
                                if (is_basic)
                                        *basic_rates |= BIT(j);
-                               if (rate < *min_rate) {
-                                       *min_rate = rate;
+                               if ((rate * 5) < *min_rate) {
+                                       *min_rate = rate * 5;
                                        *min_rate_index = j;
                                }
                                break;
@@ -3884,27 +3885,40 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                if (!new_sta)
                        return -ENOMEM;
        }
-
        if (new_sta) {
                u32 rates = 0, basic_rates = 0;
                bool have_higher_than_11mbit;
                int min_rate = INT_MAX, min_rate_index = -1;
+               struct ieee80211_chanctx_conf *chanctx_conf;
                struct ieee80211_supported_band *sband;
                const struct cfg80211_bss_ies *ies;
+               int shift;
+               u32 rate_flags;
 
                sband = local->hw.wiphy->bands[cbss->channel->band];
 
                err = ieee80211_prep_channel(sdata, cbss);
                if (err) {
                        sta_info_free(local, new_sta);
-                       return err;
+                       return -EINVAL;
                }
+               shift = ieee80211_vif_get_shift(&sdata->vif);
+
+               rcu_read_lock();
+               chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+               if (WARN_ON(!chanctx_conf)) {
+                       rcu_read_unlock();
+                       return -EINVAL;
+               }
+               rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
+               rcu_read_unlock();
 
                ieee80211_get_rates(sband, bss->supp_rates,
                                    bss->supp_rates_len,
                                    &rates, &basic_rates,
                                    &have_higher_than_11mbit,
-                                   &min_rate, &min_rate_index);
+                                   &min_rate, &min_rate_index,
+                                   shift, rate_flags);
 
                /*
                 * This used to be a workaround for basic rates missing
index 30d58d2d13e26a667024a3a2fca655b6fce1bd34..ba63ac851c2ba6769d2017c6622387331d9da5a1 100644 (file)
@@ -232,37 +232,28 @@ static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,
        /* could not find a basic rate; use original selection */
 }
 
-static inline s8
-rate_lowest_non_cck_index(struct ieee80211_supported_band *sband,
-                         struct ieee80211_sta *sta)
+static void __rate_control_send_low(struct ieee80211_hw *hw,
+                                   struct ieee80211_supported_band *sband,
+                                   struct ieee80211_sta *sta,
+                                   struct ieee80211_tx_info *info)
 {
        int i;
+       u32 rate_flags =
+               ieee80211_chandef_rate_flags(&hw->conf.chandef);
 
+       if ((sband->band == IEEE80211_BAND_2GHZ) &&
+           (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
+               rate_flags |= IEEE80211_RATE_ERP_G;
+
+       info->control.rates[0].idx = 0;
        for (i = 0; i < sband->n_bitrates; i++) {
-               struct ieee80211_rate *srate = &sband->bitrates[i];
-               if ((srate->bitrate == 10) || (srate->bitrate == 20) ||
-                   (srate->bitrate == 55) || (srate->bitrate == 110))
+               if (!rate_supported(sta, sband->band, i))
                        continue;
 
-               if (rate_supported(sta, sband->band, i))
-                       return i;
+               info->control.rates[0].idx = i;
+               break;
        }
-
-       /* No matching rate found */
-       return 0;
-}
-
-static void __rate_control_send_low(struct ieee80211_hw *hw,
-                                   struct ieee80211_supported_band *sband,
-                                   struct ieee80211_sta *sta,
-                                   struct ieee80211_tx_info *info)
-{
-       if ((sband->band != IEEE80211_BAND_2GHZ) ||
-           !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
-               info->control.rates[0].idx = rate_lowest_index(sband, sta);
-       else
-               info->control.rates[0].idx =
-                       rate_lowest_non_cck_index(sband, sta);
+       WARN_ON_ONCE(i == sband->n_bitrates);
 
        info->control.rates[0].count =
                (info->flags & IEEE80211_TX_CTL_NO_ACK) ?
@@ -585,6 +576,7 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
        u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
        bool has_mcs_mask;
        u32 mask;
+       u32 rate_flags;
        int i;
 
        /*
@@ -594,6 +586,12 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
         */
        mask = sdata->rc_rateidx_mask[info->band];
        has_mcs_mask = sdata->rc_has_mcs_mask[info->band];
+       rate_flags =
+               ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+       for (i = 0; i < sband->n_bitrates; i++)
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       mask &= ~BIT(i);
+
        if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask)
                return;
 
index d35a5dd3fb13d3f6d742cdc2ccbbeb823365ed63..5dedc56c94dbe91a1b9bd6c959094ab3b494be86 100644 (file)
@@ -66,11 +66,12 @@ static inline void rate_control_rate_init(struct sta_info *sta)
        }
 
        sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
-       rcu_read_unlock();
 
        ieee80211_sta_set_rx_nss(sta);
 
-       ref->ops->rate_init(ref->priv, sband, ista, priv_sta);
+       ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
+                           priv_sta);
+       rcu_read_unlock();
        set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
 }
 
@@ -81,10 +82,21 @@ static inline void rate_control_rate_update(struct ieee80211_local *local,
        struct rate_control_ref *ref = local->rate_ctrl;
        struct ieee80211_sta *ista = &sta->sta;
        void *priv_sta = sta->rate_ctrl_priv;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+
+       if (ref && ref->ops->rate_update) {
+               rcu_read_lock();
 
-       if (ref && ref->ops->rate_update)
-               ref->ops->rate_update(ref->priv, sband, ista,
-                                     priv_sta, changed);
+               chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
+               if (WARN_ON(!chanctx_conf)) {
+                       rcu_read_unlock();
+                       return;
+               }
+
+               ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
+                                     ista, priv_sta, changed);
+               rcu_read_unlock();
+       }
        drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
 }
 
index e6512e2ffd200223cd5cb75090802a98f544bb7d..8b5f7ef7c0c9f14db5dba3baf8ab82247500c1c3 100644 (file)
@@ -383,14 +383,18 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
 static void
 calc_rate_durations(enum ieee80211_band band,
                    struct minstrel_rate *d,
-                   struct ieee80211_rate *rate)
+                   struct ieee80211_rate *rate,
+                   struct cfg80211_chan_def *chandef)
 {
        int erp = !!(rate->flags & IEEE80211_RATE_ERP_G);
+       int shift = ieee80211_chandef_get_shift(chandef);
 
        d->perfect_tx_time = ieee80211_frame_duration(band, 1200,
-                       rate->bitrate, erp, 1);
+                       DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
+                       shift);
        d->ack_time = ieee80211_frame_duration(band, 10,
-                       rate->bitrate, erp, 1);
+                       DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
+                       shift);
 }
 
 static void
@@ -418,21 +422,25 @@ init_sample_table(struct minstrel_sta_info *mi)
 
 static void
 minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
-               struct ieee80211_sta *sta, void *priv_sta)
+                  struct cfg80211_chan_def *chandef,
+                  struct ieee80211_sta *sta, void *priv_sta)
 {
        struct minstrel_sta_info *mi = priv_sta;
        struct minstrel_priv *mp = priv;
        struct ieee80211_rate *ctl_rate;
        unsigned int i, n = 0;
        unsigned int t_slot = 9; /* FIXME: get real slot time */
+       u32 rate_flags;
 
        mi->sta = sta;
        mi->lowest_rix = rate_lowest_index(sband, sta);
        ctl_rate = &sband->bitrates[mi->lowest_rix];
        mi->sp_ack_dur = ieee80211_frame_duration(sband->band, 10,
                                ctl_rate->bitrate,
-                               !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1);
+                               !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1,
+                               ieee80211_chandef_get_shift(chandef));
 
+       rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
        memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate));
        mi->max_prob_rate = 0;
 
@@ -441,15 +449,22 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
                unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0;
                unsigned int tx_time_single;
                unsigned int cw = mp->cw_min;
+               int shift;
 
                if (!rate_supported(sta, sband->band, i))
                        continue;
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+
                n++;
                memset(mr, 0, sizeof(*mr));
 
                mr->rix = i;
-               mr->bitrate = sband->bitrates[i].bitrate / 5;
-               calc_rate_durations(sband->band, mr, &sband->bitrates[i]);
+               shift = ieee80211_chandef_get_shift(chandef);
+               mr->bitrate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                          (1 << shift) * 5);
+               calc_rate_durations(sband->band, mr, &sband->bitrates[i],
+                                   chandef);
 
                /* calculate maximum number of retransmissions before
                 * fallback (based on maximum segment size) */
@@ -547,6 +562,7 @@ minstrel_init_cck_rates(struct minstrel_priv *mp)
 {
        static const int bitrates[4] = { 10, 20, 55, 110 };
        struct ieee80211_supported_band *sband;
+       u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
        int i, j;
 
        sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ];
@@ -559,6 +575,9 @@ minstrel_init_cck_rates(struct minstrel_priv *mp)
                if (rate->flags & IEEE80211_RATE_ERP_G)
                        continue;
 
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+
                for (j = 0; j < ARRAY_SIZE(bitrates); j++) {
                        if (rate->bitrate != bitrates[j])
                                continue;
index f5aed963b22e62cd997872d1068bfb6c45eeda7a..61569425b7236e2b45d61bb888cb6823afe7cbdf 100644 (file)
@@ -844,6 +844,7 @@ minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
 
 static void
 minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
+                       struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *priv_sta)
 {
        struct minstrel_priv *mp = priv;
@@ -869,8 +870,9 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
        mi->sta = sta;
        mi->stats_update = jiffies;
 
-       ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1);
-       mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1) + ack_dur;
+       ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0);
+       mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0);
+       mi->overhead += ack_dur;
        mi->overhead_rtscts = mi->overhead + 2 * ack_dur;
 
        mi->avg_ampdu_len = MINSTREL_FRAC(1, 1);
@@ -939,22 +941,25 @@ use_legacy:
        memset(&msp->legacy, 0, sizeof(msp->legacy));
        msp->legacy.r = msp->ratelist;
        msp->legacy.sample_table = msp->sample_table;
-       return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy);
+       return mac80211_minstrel.rate_init(priv, sband, chandef, sta,
+                                          &msp->legacy);
 }
 
 static void
 minstrel_ht_rate_init(void *priv, struct ieee80211_supported_band *sband,
+                     struct cfg80211_chan_def *chandef,
                       struct ieee80211_sta *sta, void *priv_sta)
 {
-       minstrel_ht_update_caps(priv, sband, sta, priv_sta);
+       minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);
 }
 
 static void
 minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband,
+                       struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *priv_sta,
                         u32 changed)
 {
-       minstrel_ht_update_caps(priv, sband, sta, priv_sta);
+       minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);
 }
 
 static void *
index 502d3ecc4a797b4004128604e5324d0cfc198bfe..958fad07b54cf64856e3600bd6299f4ca9abd72a 100644 (file)
@@ -293,6 +293,7 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
 
 static void
 rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
+                          struct cfg80211_chan_def *chandef,
                           struct ieee80211_sta *sta, void *priv_sta)
 {
        struct rc_pid_sta_info *spinfo = priv_sta;
index 2c5a79bd3777f5c313d3205a1154ef6b714a8d4d..6b85f95b9ba16ed010d6f8a87b2829dae3dd7ca9 100644 (file)
@@ -87,11 +87,13 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
        int len;
 
        /* always present fields */
-       len = sizeof(struct ieee80211_radiotap_header) + 9;
+       len = sizeof(struct ieee80211_radiotap_header) + 8;
 
-       /* allocate extra bitmap */
+       /* allocate extra bitmaps */
        if (status->vendor_radiotap_len)
                len += 4;
+       if (status->chains)
+               len += 4 * hweight8(status->chains);
 
        if (ieee80211_have_rx_timestamp(status)) {
                len = ALIGN(len, 8);
@@ -100,6 +102,10 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
        if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
                len += 1;
 
+       /* antenna field, if we don't have per-chain info */
+       if (!status->chains)
+               len += 1;
+
        /* padding for RX_FLAGS if necessary */
        len = ALIGN(len, 2);
 
@@ -116,6 +122,11 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
                len += 12;
        }
 
+       if (status->chains) {
+               /* antenna and antenna signal fields */
+               len += 2 * hweight8(status->chains);
+       }
+
        if (status->vendor_radiotap_len) {
                if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
                        status->vendor_radiotap_align = 1;
@@ -145,8 +156,12 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_radiotap_header *rthdr;
        unsigned char *pos;
+       __le32 *it_present;
+       u32 it_present_val;
        u16 rx_flags = 0;
-       int mpdulen;
+       u16 channel_flags = 0;
+       int mpdulen, chain;
+       unsigned long chains = status->chains;
 
        mpdulen = skb->len;
        if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
@@ -154,25 +169,39 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
 
        rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
        memset(rthdr, 0, rtap_len);
+       it_present = &rthdr->it_present;
 
        /* radiotap header, set always present flags */
-       rthdr->it_present =
-               cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
-                           (1 << IEEE80211_RADIOTAP_CHANNEL) |
-                           (1 << IEEE80211_RADIOTAP_ANTENNA) |
-                           (1 << IEEE80211_RADIOTAP_RX_FLAGS));
        rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len);
+       it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) |
+                        BIT(IEEE80211_RADIOTAP_CHANNEL) |
+                        BIT(IEEE80211_RADIOTAP_RX_FLAGS);
 
-       pos = (unsigned char *)(rthdr + 1);
+       if (!status->chains)
+               it_present_val |= BIT(IEEE80211_RADIOTAP_ANTENNA);
+
+       for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+               it_present_val |=
+                       BIT(IEEE80211_RADIOTAP_EXT) |
+                       BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE);
+               put_unaligned_le32(it_present_val, it_present);
+               it_present++;
+               it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) |
+                                BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+       }
 
        if (status->vendor_radiotap_len) {
-               rthdr->it_present |=
-                       cpu_to_le32(BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE)) |
-                       cpu_to_le32(BIT(IEEE80211_RADIOTAP_EXT));
-               put_unaligned_le32(status->vendor_radiotap_bitmap, pos);
-               pos += 4;
+               it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
+                                 BIT(IEEE80211_RADIOTAP_EXT);
+               put_unaligned_le32(it_present_val, it_present);
+               it_present++;
+               it_present_val = status->vendor_radiotap_bitmap;
        }
 
+       put_unaligned_le32(it_present_val, it_present);
+
+       pos = (void *)(it_present + 1);
+
        /* the order of the following fields is important */
 
        /* IEEE80211_RADIOTAP_TSFT */
@@ -207,28 +236,35 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                 */
                *pos = 0;
        } else {
+               int shift = 0;
                rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
-               *pos = rate->bitrate / 5;
+               if (status->flag & RX_FLAG_10MHZ)
+                       shift = 1;
+               else if (status->flag & RX_FLAG_5MHZ)
+                       shift = 2;
+               *pos = DIV_ROUND_UP(rate->bitrate, 5 * (1 << shift));
        }
        pos++;
 
        /* IEEE80211_RADIOTAP_CHANNEL */
        put_unaligned_le16(status->freq, pos);
        pos += 2;
+       if (status->flag & RX_FLAG_10MHZ)
+               channel_flags |= IEEE80211_CHAN_HALF;
+       else if (status->flag & RX_FLAG_5MHZ)
+               channel_flags |= IEEE80211_CHAN_QUARTER;
+
        if (status->band == IEEE80211_BAND_5GHZ)
-               put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ;
        else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
-               put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
        else if (rate && rate->flags & IEEE80211_RATE_ERP_G)
-               put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
        else if (rate)
-               put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
        else
-               put_unaligned_le16(IEEE80211_CHAN_2GHZ, pos);
+               channel_flags |= IEEE80211_CHAN_2GHZ;
+       put_unaligned_le16(channel_flags, pos);
        pos += 2;
 
        /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
@@ -242,9 +278,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
 
        /* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */
 
-       /* IEEE80211_RADIOTAP_ANTENNA */
-       *pos = status->antenna;
-       pos++;
+       if (!status->chains) {
+               /* IEEE80211_RADIOTAP_ANTENNA */
+               *pos = status->antenna;
+               pos++;
+       }
 
        /* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */
 
@@ -341,6 +379,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                pos += 2;
        }
 
+       for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+               *pos++ = status->chain_signal[chain];
+               *pos++ = chain;
+       }
+
        if (status->vendor_radiotap_len) {
                /* ensure 2 byte alignment for the vendor field as required */
                if ((pos - (u8 *)rthdr) & 1)
index 1b122a79b0d8369a4118a6f238dc6d69db9e072c..08afe74b98f4b6cbdda21ed3d8ff68b9a95fa64b 100644 (file)
@@ -66,6 +66,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
        struct cfg80211_bss *cbss;
        struct ieee80211_bss *bss;
        int clen, srlen;
+       enum nl80211_bss_scan_width scan_width;
        s32 signal = 0;
 
        if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
@@ -73,8 +74,15 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
        else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
                signal = (rx_status->signal * 100) / local->hw.max_signal;
 
-       cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel,
-                                        mgmt, len, signal, GFP_ATOMIC);
+       scan_width = NL80211_BSS_CHAN_WIDTH_20;
+       if (rx_status->flag & RX_FLAG_5MHZ)
+               scan_width = NL80211_BSS_CHAN_WIDTH_5;
+       if (rx_status->flag & RX_FLAG_10MHZ)
+               scan_width = NL80211_BSS_CHAN_WIDTH_10;
+
+       cbss = cfg80211_inform_bss_width_frame(local->hw.wiphy, channel,
+                                              scan_width, mgmt, len, signal,
+                                              GFP_ATOMIC);
        if (!cbss)
                return NULL;
 
@@ -204,10 +212,29 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
                ieee80211_rx_bss_put(local, bss);
 }
 
+static void
+ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef,
+                              enum nl80211_bss_scan_width scan_width)
+{
+       memset(chandef, 0, sizeof(*chandef));
+       switch (scan_width) {
+       case NL80211_BSS_CHAN_WIDTH_5:
+               chandef->width = NL80211_CHAN_WIDTH_5;
+               break;
+       case NL80211_BSS_CHAN_WIDTH_10:
+               chandef->width = NL80211_CHAN_WIDTH_10;
+               break;
+       default:
+               chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+               break;
+       }
+}
+
 /* return false if no more work */
 static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
 {
        struct cfg80211_scan_request *req = local->scan_req;
+       struct cfg80211_chan_def chandef;
        enum ieee80211_band band;
        int i, ielen, n_chans;
 
@@ -229,11 +256,12 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
        } while (!n_chans);
 
        local->hw_scan_req->n_channels = n_chans;
+       ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
 
        ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
                                         local->hw_scan_ies_bufsize,
                                         req->ie, req->ie_len, band,
-                                        req->rates[band], 0);
+                                        req->rates[band], &chandef);
        local->hw_scan_req->ie_len = ielen;
        local->hw_scan_req->no_cck = req->no_cck;
 
@@ -280,7 +308,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
        rcu_assign_pointer(local->scan_sdata, NULL);
 
        local->scanning = 0;
-       local->scan_channel = NULL;
+       local->scan_chandef.chan = NULL;
 
        /* Set power back to normal operating levels. */
        ieee80211_hw_config(local, 0);
@@ -615,11 +643,34 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
 {
        int skip;
        struct ieee80211_channel *chan;
+       enum nl80211_bss_scan_width oper_scan_width;
 
        skip = 0;
        chan = local->scan_req->channels[local->scan_channel_idx];
 
-       local->scan_channel = chan;
+       local->scan_chandef.chan = chan;
+       local->scan_chandef.center_freq1 = chan->center_freq;
+       local->scan_chandef.center_freq2 = 0;
+       switch (local->scan_req->scan_width) {
+       case NL80211_BSS_CHAN_WIDTH_5:
+               local->scan_chandef.width = NL80211_CHAN_WIDTH_5;
+               break;
+       case NL80211_BSS_CHAN_WIDTH_10:
+               local->scan_chandef.width = NL80211_CHAN_WIDTH_10;
+               break;
+       case NL80211_BSS_CHAN_WIDTH_20:
+               /* If scanning on oper channel, use whatever channel-type
+                * is currently in use.
+                */
+               oper_scan_width = cfg80211_chandef_to_scan_width(
+                                       &local->_oper_chandef);
+               if (chan == local->_oper_chandef.chan &&
+                   oper_scan_width == local->scan_req->scan_width)
+                       local->scan_chandef = local->_oper_chandef;
+               else
+                       local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+               break;
+       }
 
        if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
                skip = 1;
@@ -659,7 +710,7 @@ static void ieee80211_scan_state_suspend(struct ieee80211_local *local,
                                         unsigned long *next_delay)
 {
        /* switch back to the operating channel */
-       local->scan_channel = NULL;
+       local->scan_chandef.chan = NULL;
        ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 
        /* disable PS */
@@ -801,7 +852,8 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
 
 int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
                                const u8 *ssid, u8 ssid_len,
-                               struct ieee80211_channel *chan)
+                               struct ieee80211_channel *chan,
+                               enum nl80211_bss_scan_width scan_width)
 {
        struct ieee80211_local *local = sdata->local;
        int ret = -EBUSY;
@@ -851,6 +903,7 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
 
        local->int_scan_req->ssids = &local->scan_ssid;
        local->int_scan_req->n_ssids = 1;
+       local->int_scan_req->scan_width = scan_width;
        memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
        local->int_scan_req->ssids[0].ssid_len = ssid_len;
 
@@ -912,6 +965,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_sched_scan_ies sched_scan_ies = {};
+       struct cfg80211_chan_def chandef;
        int ret, i, iebufsz;
 
        iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
@@ -939,10 +993,12 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
                        goto out_free;
                }
 
+               ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
+
                sched_scan_ies.len[i] =
                        ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
                                                 iebufsz, req->ie, req->ie_len,
-                                                i, (u32) -1, 0);
+                                                i, (u32) -1, &chandef);
        }
 
        ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
index 43439203f4e4cf2262092a9e391e7d048f10ef9a..6ad4c14385ef420bf039660ccce5b1a8672dd500 100644 (file)
@@ -252,9 +252,10 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info)
        return len;
 }
 
-static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
-                                            *sband, struct sk_buff *skb,
-                                            int retry_count, int rtap_len)
+static void
+ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band *sband,
+                                struct sk_buff *skb, int retry_count,
+                                int rtap_len, int shift)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -280,8 +281,11 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
        /* IEEE80211_RADIOTAP_RATE */
        if (info->status.rates[0].idx >= 0 &&
            !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) {
+               u16 rate;
+
                rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
-               *pos = sband->bitrates[info->status.rates[0].idx].bitrate / 5;
+               rate = sband->bitrates[info->status.rates[0].idx].bitrate;
+               *pos = DIV_ROUND_UP(rate, 5 * (1 << shift));
                /* padding for tx flags */
                pos += 2;
        }
@@ -424,6 +428,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        bool acked;
        struct ieee80211_bar *bar;
        int rtap_len;
+       int shift = 0;
 
        for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
                if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
@@ -458,6 +463,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
                        continue;
 
+               shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+
                if (info->flags & IEEE80211_TX_STATUS_EOSP)
                        clear_sta_flag(sta, WLAN_STA_SP);
 
@@ -624,7 +631,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                dev_kfree_skb(skb);
                return;
        }
-       ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len);
+       ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len,
+                                        shift);
 
        /* XXX: is this sufficient for BPF? */
        skb_set_mac_header(skb, 0);
index 4105d0ca963e25dfdd558e30c48274277e5ad2df..be4d3caf4879b5eb09d6a49d524cb4faa81c2752 100644 (file)
@@ -40,12 +40,22 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
                                 struct sk_buff *skb, int group_addr,
                                 int next_frag_len)
 {
-       int rate, mrate, erp, dur, i;
+       int rate, mrate, erp, dur, i, shift = 0;
        struct ieee80211_rate *txrate;
        struct ieee80211_local *local = tx->local;
        struct ieee80211_supported_band *sband;
        struct ieee80211_hdr *hdr;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       u32 rate_flags = 0;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(tx->sdata->vif.chanctx_conf);
+       if (chanctx_conf) {
+               shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
+               rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
+       }
+       rcu_read_unlock();
 
        /* assume HW handles this */
        if (tx->rate.flags & IEEE80211_TX_RC_MCS)
@@ -122,8 +132,11 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
                if (r->bitrate > txrate->bitrate)
                        break;
 
+               if ((rate_flags & r->flags) != rate_flags)
+                       continue;
+
                if (tx->sdata->vif.bss_conf.basic_rates & BIT(i))
-                       rate = r->bitrate;
+                       rate = DIV_ROUND_UP(r->bitrate, 1 << shift);
 
                switch (sband->band) {
                case IEEE80211_BAND_2GHZ: {
@@ -150,7 +163,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
        if (rate == -1) {
                /* No matching basic rate found; use highest suitable mandatory
                 * PHY rate */
-               rate = mrate;
+               rate = DIV_ROUND_UP(mrate, 1 << shift);
        }
 
        /* Don't calculate ACKs for QoS Frames with NoAck Policy set */
@@ -162,7 +175,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
                 * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up
                 * to closest integer */
                dur = ieee80211_frame_duration(sband->band, 10, rate, erp,
-                               tx->sdata->vif.bss_conf.use_short_preamble);
+                               tx->sdata->vif.bss_conf.use_short_preamble,
+                               shift);
 
        if (next_frag_len) {
                /* Frame is fragmented: duration increases with time needed to
@@ -171,7 +185,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
                /* next fragment */
                dur += ieee80211_frame_duration(sband->band, next_frag_len,
                                txrate->bitrate, erp,
-                               tx->sdata->vif.bss_conf.use_short_preamble);
+                               tx->sdata->vif.bss_conf.use_short_preamble,
+                               shift);
        }
 
        return cpu_to_le16(dur);
@@ -1257,6 +1272,10 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_MONITOR:
+               if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) {
+                       vif = &sdata->vif;
+                       break;
+               }
                sdata = rcu_dereference(local->monitor_sdata);
                if (sdata) {
                        vif = &sdata->vif;
index 22654452a56157b44835f97efacdf2e8960ee5bc..d23c5a705a6846db93596958c64139780a9874b4 100644 (file)
@@ -107,7 +107,8 @@ void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
 }
 
 int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
-                            int rate, int erp, int short_preamble)
+                            int rate, int erp, int short_preamble,
+                            int shift)
 {
        int dur;
 
@@ -118,6 +119,9 @@ int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
         *
         * rate is in 100 kbps, so divident is multiplied by 10 in the
         * DIV_ROUND_UP() operations.
+        *
+        * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and
+        * is assumed to be 0 otherwise.
         */
 
        if (band == IEEE80211_BAND_5GHZ || erp) {
@@ -130,13 +134,23 @@ int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
                 * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
                 *
                 * T_SYM = 4 usec
-                * 802.11a - 17.5.2: aSIFSTime = 16 usec
+                * 802.11a - 18.5.2: aSIFSTime = 16 usec
                 * 802.11g - 19.8.4: aSIFSTime = 10 usec +
                 *      signal ext = 6 usec
                 */
                dur = 16; /* SIFS + signal ext */
-               dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */
-               dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */
+               dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */
+               dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */
+
+               /* IEEE 802.11-2012 18.3.2.4: all values above are:
+                *  * times 4 for 5 MHz
+                *  * times 2 for 10 MHz
+                */
+               dur *= 1 << shift;
+
+               /* rates should already consider the channel bandwidth,
+                * don't apply divisor again.
+                */
                dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
                                        4 * rate); /* T_SYM x N_SYM */
        } else {
@@ -168,7 +182,7 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
 {
        struct ieee80211_sub_if_data *sdata;
        u16 dur;
-       int erp;
+       int erp, shift = 0;
        bool short_preamble = false;
 
        erp = 0;
@@ -177,10 +191,11 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
                short_preamble = sdata->vif.bss_conf.use_short_preamble;
                if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
                        erp = rate->flags & IEEE80211_RATE_ERP_G;
+               shift = ieee80211_vif_get_shift(vif);
        }
 
        dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp,
-                                      short_preamble);
+                                      short_preamble, shift);
 
        return cpu_to_le16(dur);
 }
@@ -194,7 +209,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
        struct ieee80211_rate *rate;
        struct ieee80211_sub_if_data *sdata;
        bool short_preamble;
-       int erp;
+       int erp, shift = 0, bitrate;
        u16 dur;
        struct ieee80211_supported_band *sband;
 
@@ -210,17 +225,20 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
                short_preamble = sdata->vif.bss_conf.use_short_preamble;
                if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
                        erp = rate->flags & IEEE80211_RATE_ERP_G;
+               shift = ieee80211_vif_get_shift(vif);
        }
 
+       bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
+
        /* CTS duration */
-       dur = ieee80211_frame_duration(sband->band, 10, rate->bitrate,
-                                      erp, short_preamble);
+       dur = ieee80211_frame_duration(sband->band, 10, bitrate,
+                                      erp, short_preamble, shift);
        /* Data frame duration */
-       dur += ieee80211_frame_duration(sband->band, frame_len, rate->bitrate,
-                                       erp, short_preamble);
+       dur += ieee80211_frame_duration(sband->band, frame_len, bitrate,
+                                       erp, short_preamble, shift);
        /* ACK duration */
-       dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate,
-                                       erp, short_preamble);
+       dur += ieee80211_frame_duration(sband->band, 10, bitrate,
+                                       erp, short_preamble, shift);
 
        return cpu_to_le16(dur);
 }
@@ -235,7 +253,7 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
        struct ieee80211_rate *rate;
        struct ieee80211_sub_if_data *sdata;
        bool short_preamble;
-       int erp;
+       int erp, shift = 0, bitrate;
        u16 dur;
        struct ieee80211_supported_band *sband;
 
@@ -250,15 +268,18 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
                short_preamble = sdata->vif.bss_conf.use_short_preamble;
                if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
                        erp = rate->flags & IEEE80211_RATE_ERP_G;
+               shift = ieee80211_vif_get_shift(vif);
        }
 
+       bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
+
        /* Data frame duration */
-       dur = ieee80211_frame_duration(sband->band, frame_len, rate->bitrate,
-                                      erp, short_preamble);
+       dur = ieee80211_frame_duration(sband->band, frame_len, bitrate,
+                                      erp, short_preamble, shift);
        if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {
                /* ACK duration */
-               dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate,
-                                               erp, short_preamble);
+               dur += ieee80211_frame_duration(sband->band, 10, bitrate,
+                                               erp, short_preamble, shift);
        }
 
        return cpu_to_le16(dur);
@@ -1052,32 +1073,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
        }
 }
 
-void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
-                                 const size_t supp_rates_len,
-                                 const u8 *supp_rates)
-{
-       struct ieee80211_chanctx_conf *chanctx_conf;
-       int i, have_higher_than_11mbit = 0;
-
-       /* cf. IEEE 802.11 9.2.12 */
-       for (i = 0; i < supp_rates_len; i++)
-               if ((supp_rates[i] & 0x7f) * 5 > 110)
-                       have_higher_than_11mbit = 1;
-
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-
-       if (chanctx_conf &&
-           chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ &&
-           have_higher_than_11mbit)
-               sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
-       else
-               sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
-       rcu_read_unlock();
-
-       ieee80211_set_wmm_default(sdata, true);
-}
-
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg, u16 status,
                         const u8 *extra, size_t extra_len, const u8 *da,
@@ -1162,7 +1157,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                             size_t buffer_len, const u8 *ie, size_t ie_len,
                             enum ieee80211_band band, u32 rate_mask,
-                            u8 channel)
+                            struct cfg80211_chan_def *chandef)
 {
        struct ieee80211_supported_band *sband;
        u8 *pos = buffer, *end = buffer + buffer_len;
@@ -1171,16 +1166,26 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
        u8 rates[32];
        int num_rates;
        int ext_rates_len;
+       int shift;
+       u32 rate_flags;
 
        sband = local->hw.wiphy->bands[band];
        if (WARN_ON_ONCE(!sband))
                return 0;
 
+       rate_flags = ieee80211_chandef_rate_flags(chandef);
+       shift = ieee80211_chandef_get_shift(chandef);
+
        num_rates = 0;
        for (i = 0; i < sband->n_bitrates; i++) {
                if ((BIT(i) & rate_mask) == 0)
                        continue; /* skip rate */
-               rates[num_rates++] = (u8) (sband->bitrates[i].bitrate / 5);
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+
+               rates[num_rates++] =
+                       (u8) DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                         (1 << shift) * 5);
        }
 
        supp_rates_len = min_t(int, num_rates, 8);
@@ -1220,12 +1225,13 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                pos += ext_rates_len;
        }
 
-       if (channel && sband->band == IEEE80211_BAND_2GHZ) {
+       if (chandef->chan && sband->band == IEEE80211_BAND_2GHZ) {
                if (end - pos < 3)
                        goto out_err;
                *pos++ = WLAN_EID_DS_PARAMS;
                *pos++ = 1;
-               *pos++ = channel;
+               *pos++ = ieee80211_frequency_to_channel(
+                               chandef->chan->center_freq);
        }
 
        /* insert custom IEs that go before HT */
@@ -1290,9 +1296,9 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
                                          bool directed)
 {
        struct ieee80211_local *local = sdata->local;
+       struct cfg80211_chan_def chandef;
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
-       u8 chan_no;
        int ies_len;
 
        /*
@@ -1300,10 +1306,11 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
         * in order to maximize the chance that we get a response.  Some
         * badly-behaved APs don't respond when this parameter is included.
         */
+       chandef.width = sdata->vif.bss_conf.chandef.width;
        if (directed)
-               chan_no = 0;
+               chandef.chan = NULL;
        else
-               chan_no = ieee80211_frequency_to_channel(chan->center_freq);
+               chandef.chan = chan;
 
        skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
                                     ssid, ssid_len, 100 + ie_len);
@@ -1313,7 +1320,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
        ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
                                           skb_tailroom(skb),
                                           ie, ie_len, chan->band,
-                                          ratemask, chan_no);
+                                          ratemask, &chandef);
        skb_put(skb, ies_len);
 
        if (dst) {
@@ -1347,16 +1354,19 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
        }
 }
 
-u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
+u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
                            struct ieee802_11_elems *elems,
                            enum ieee80211_band band, u32 *basic_rates)
 {
        struct ieee80211_supported_band *sband;
        struct ieee80211_rate *bitrates;
        size_t num_rates;
-       u32 supp_rates;
-       int i, j;
-       sband = local->hw.wiphy->bands[band];
+       u32 supp_rates, rate_flags;
+       int i, j, shift;
+       sband = sdata->local->hw.wiphy->bands[band];
+
+       rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        if (WARN_ON(!sband))
                return 1;
@@ -1381,7 +1391,15 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
                        continue;
 
                for (j = 0; j < num_rates; j++) {
-                       if (bitrates[j].bitrate == own_rate) {
+                       int brate;
+                       if ((rate_flags & sband->bitrates[j].flags)
+                           != rate_flags)
+                               continue;
+
+                       brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
+                                            1 << shift);
+
+                       if (brate == own_rate) {
                                supp_rates |= BIT(j);
                                if (basic_rates && is_basic)
                                        *basic_rates |= BIT(j);
@@ -2004,18 +2022,56 @@ void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
        cfg80211_chandef_create(chandef, control_chan, channel_type);
 }
 
+int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
+                            const struct ieee80211_supported_band *sband,
+                            const u8 *srates, int srates_len, u32 *rates)
+{
+       u32 rate_flags = ieee80211_chandef_rate_flags(chandef);
+       int shift = ieee80211_chandef_get_shift(chandef);
+       struct ieee80211_rate *br;
+       int brate, rate, i, j, count = 0;
+
+       *rates = 0;
+
+       for (i = 0; i < srates_len; i++) {
+               rate = srates[i] & 0x7f;
+
+               for (j = 0; j < sband->n_bitrates; j++) {
+                       br = &sband->bitrates[j];
+                       if ((rate_flags & br->flags) != rate_flags)
+                               continue;
+
+                       brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
+                       if (brate == rate) {
+                               *rates |= BIT(j);
+                               count++;
+                               break;
+                       }
+               }
+       }
+       return count;
+}
+
 int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
                            struct sk_buff *skb, bool need_basic,
                            enum ieee80211_band band)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
-       int rate;
+       int rate, shift;
        u8 i, rates, *pos;
        u32 basic_rates = sdata->vif.bss_conf.basic_rates;
+       u32 rate_flags;
 
+       shift = ieee80211_vif_get_shift(&sdata->vif);
+       rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
        sband = local->hw.wiphy->bands[band];
-       rates = sband->n_bitrates;
+       rates = 0;
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+               rates++;
+       }
        if (rates > 8)
                rates = 8;
 
@@ -2027,10 +2083,15 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
        *pos++ = rates;
        for (i = 0; i < rates; i++) {
                u8 basic = 0;
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+
                if (need_basic && basic_rates & BIT(i))
                        basic = 0x80;
                rate = sband->bitrates[i].bitrate;
-               *pos++ = basic | (u8) (rate / 5);
+               rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                   5 * (1 << shift));
+               *pos++ = basic | (u8) rate;
        }
 
        return 0;
@@ -2042,12 +2103,22 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
-       int rate;
+       int rate, skip, shift;
        u8 i, exrates, *pos;
        u32 basic_rates = sdata->vif.bss_conf.basic_rates;
+       u32 rate_flags;
+
+       rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        sband = local->hw.wiphy->bands[band];
-       exrates = sband->n_bitrates;
+       exrates = 0;
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+               exrates++;
+       }
+
        if (exrates > 8)
                exrates -= 8;
        else
@@ -2060,12 +2131,19 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
                pos = skb_put(skb, exrates + 2);
                *pos++ = WLAN_EID_EXT_SUPP_RATES;
                *pos++ = exrates;
+               skip = 0;
                for (i = 8; i < sband->n_bitrates; i++) {
                        u8 basic = 0;
+                       if ((rate_flags & sband->bitrates[i].flags)
+                           != rate_flags)
+                               continue;
+                       if (skip++ < 8)
+                               continue;
                        if (need_basic && basic_rates & BIT(i))
                                basic = 0x80;
-                       rate = sband->bitrates[i].bitrate;
-                       *pos++ = basic | (u8) (rate / 5);
+                       rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                           5 * (1 << shift));
+                       *pos++ = basic | (u8) rate;
                }
        }
        return 0;
@@ -2149,9 +2227,17 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
                        ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
        } else {
                struct ieee80211_supported_band *sband;
+               int shift = 0;
+               int bitrate;
+
+               if (status->flag & RX_FLAG_10MHZ)
+                       shift = 1;
+               if (status->flag & RX_FLAG_5MHZ)
+                       shift = 2;
 
                sband = local->hw.wiphy->bands[status->band];
-               ri.legacy = sband->bitrates[status->rate_idx].bitrate;
+               bitrate = sband->bitrates[status->rate_idx].bitrate;
+               ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift));
        }
 
        rate = cfg80211_calculate_bitrate(&ri);
index 4f9f216665e9d4d6753172f3c14d7bbdd3f53154..389a3f2ee4640105f6e76c6e6075154c56183df8 100644 (file)
@@ -462,6 +462,14 @@ int wiphy_register(struct wiphy *wiphy)
                return -EINVAL;
 #endif
 
+       if (WARN_ON(wiphy->coalesce &&
+                   (!wiphy->coalesce->n_rules ||
+                    !wiphy->coalesce->n_patterns) &&
+                   (!wiphy->coalesce->pattern_min_len ||
+                    wiphy->coalesce->pattern_min_len >
+                       wiphy->coalesce->pattern_max_len)))
+               return -EINVAL;
+
        if (WARN_ON(wiphy->ap_sme_capa &&
                    !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME)))
                return -EINVAL;
@@ -668,6 +676,7 @@ void wiphy_unregister(struct wiphy *wiphy)
                rdev_set_wakeup(rdev, false);
 #endif
        cfg80211_rdev_free_wowlan(rdev);
+       cfg80211_rdev_free_coalesce(rdev);
 }
 EXPORT_SYMBOL(wiphy_unregister);
 
index a6b45bf00f3357292a1ad6e3cc5a08fd6faf9a7a..9ad43c619c54830f915193daa60eadef92b5bda5 100644 (file)
@@ -79,6 +79,8 @@ struct cfg80211_registered_device {
        /* netlink port which started critical protocol (0 means not started) */
        u32 crit_proto_nlportid;
 
+       struct cfg80211_coalesce *coalesce;
+
        /* must be last because of the way we do wiphy_priv(),
         * and it should at least be aligned to NETDEV_ALIGN */
        struct wiphy wiphy __aligned(NETDEV_ALIGN);
index 30c49202ee4d12804b78bf4cc26e2766c4cc029e..0553fd4d85aeb4b9338d2661ba020448105a68e9 100644 (file)
@@ -167,9 +167,12 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
         * basic rates
         */
        if (!setup->basic_rates) {
+               enum nl80211_bss_scan_width scan_width;
                struct ieee80211_supported_band *sband =
                                rdev->wiphy.bands[setup->chandef.chan->band];
-               setup->basic_rates = ieee80211_mandatory_rates(sband);
+               scan_width = cfg80211_chandef_to_scan_width(&setup->chandef);
+               setup->basic_rates = ieee80211_mandatory_rates(sband,
+                                                              scan_width);
        }
 
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
index 25d217d90807f05f6a56945cb89dcd7b3314a53d..160c1f7c6091260ffcb3a495179060e1521de92b 100644 (file)
@@ -403,6 +403,14 @@ nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = {
        [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },
 };
 
+/* policy for coalesce rule attributes */
+static const struct nla_policy
+nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = {
+       [NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 },
+       [NL80211_ATTR_COALESCE_RULE_CONDITION] = { .type = NLA_U32 },
+       [NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED },
+};
+
 /* policy for GTK rekey offload attributes */
 static const struct nla_policy
 nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
@@ -974,7 +982,7 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
                return -ENOBUFS;
 
        if (dev->wiphy.wowlan->n_patterns) {
-               struct nl80211_wowlan_pattern_support pat = {
+               struct nl80211_pattern_support pat = {
                        .max_patterns = dev->wiphy.wowlan->n_patterns,
                        .min_pattern_len = dev->wiphy.wowlan->pattern_min_len,
                        .max_pattern_len = dev->wiphy.wowlan->pattern_max_len,
@@ -995,6 +1003,27 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
 }
 #endif
 
+static int nl80211_send_coalesce(struct sk_buff *msg,
+                                struct cfg80211_registered_device *dev)
+{
+       struct nl80211_coalesce_rule_support rule;
+
+       if (!dev->wiphy.coalesce)
+               return 0;
+
+       rule.max_rules = dev->wiphy.coalesce->n_rules;
+       rule.max_delay = dev->wiphy.coalesce->max_delay;
+       rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns;
+       rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len;
+       rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len;
+       rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset;
+
+       if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))
+               return -ENOBUFS;
+
+       return 0;
+}
+
 static int nl80211_send_band_rateinfo(struct sk_buff *msg,
                                      struct ieee80211_supported_band *sband)
 {
@@ -1513,6 +1542,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                            dev->wiphy.vht_capa_mod_mask))
                        goto nla_put_failure;
 
+               state->split_start++;
+               break;
+       case 10:
+               if (nl80211_send_coalesce(msg, dev))
+                       goto nla_put_failure;
+
                /* done */
                state->split_start = 0;
                break;
@@ -5639,6 +5674,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
                goto nla_put_failure;
        if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) ||
            nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
+           nla_put_u32(msg, NL80211_BSS_CHAN_WIDTH, res->scan_width) ||
            nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
                        jiffies_to_msecs(jiffies - intbss->ts)))
                goto nla_put_failure;
@@ -6319,6 +6355,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
 
        switch (ibss.chandef.width) {
+       case NL80211_CHAN_WIDTH_5:
+       case NL80211_CHAN_WIDTH_10:
        case NL80211_CHAN_WIDTH_20_NOHT:
                break;
        case NL80211_CHAN_WIDTH_20:
@@ -6346,6 +6384,19 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                        return err;
        }
 
+       if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+               memcpy(&ibss.ht_capa_mask,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+                      sizeof(ibss.ht_capa_mask));
+
+       if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
+               if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+                       return -EINVAL;
+               memcpy(&ibss.ht_capa,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+                      sizeof(ibss.ht_capa));
+       }
+
        if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
            !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
                        nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
@@ -7593,12 +7644,11 @@ static int nl80211_send_wowlan_patterns(struct sk_buff *msg,
                if (!nl_pat)
                        return -ENOBUFS;
                pat_len = wowlan->patterns[i].pattern_len;
-               if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
-                           DIV_ROUND_UP(pat_len, 8),
+               if (nla_put(msg, NL80211_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8),
                            wowlan->patterns[i].mask) ||
-                   nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
-                           pat_len, wowlan->patterns[i].pattern) ||
-                   nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET,
+                   nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
+                           wowlan->patterns[i].pattern) ||
+                   nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
                                wowlan->patterns[i].pkt_offset))
                        return -ENOBUFS;
                nla_nest_end(msg, nl_pat);
@@ -7939,7 +7989,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                struct nlattr *pat;
                int n_patterns = 0;
                int rem, pat_len, mask_len, pkt_offset;
-               struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
+               struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
 
                nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
                                    rem)
@@ -7958,26 +8008,25 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
 
                nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
                                    rem) {
-                       nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
-                                 nla_data(pat), nla_len(pat), NULL);
+                       nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
+                                 nla_len(pat), NULL);
                        err = -EINVAL;
-                       if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
-                           !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
+                       if (!pat_tb[NL80211_PKTPAT_MASK] ||
+                           !pat_tb[NL80211_PKTPAT_PATTERN])
                                goto error;
-                       pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
+                       pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
                        mask_len = DIV_ROUND_UP(pat_len, 8);
-                       if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
-                           mask_len)
+                       if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
                                goto error;
                        if (pat_len > wowlan->pattern_max_len ||
                            pat_len < wowlan->pattern_min_len)
                                goto error;
 
-                       if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET])
+                       if (!pat_tb[NL80211_PKTPAT_OFFSET])
                                pkt_offset = 0;
                        else
                                pkt_offset = nla_get_u32(
-                                       pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]);
+                                       pat_tb[NL80211_PKTPAT_OFFSET]);
                        if (pkt_offset > wowlan->max_pkt_offset)
                                goto error;
                        new_triggers.patterns[i].pkt_offset = pkt_offset;
@@ -7991,11 +8040,11 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                        new_triggers.patterns[i].pattern =
                                new_triggers.patterns[i].mask + mask_len;
                        memcpy(new_triggers.patterns[i].mask,
-                              nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
+                              nla_data(pat_tb[NL80211_PKTPAT_MASK]),
                               mask_len);
                        new_triggers.patterns[i].pattern_len = pat_len;
                        memcpy(new_triggers.patterns[i].pattern,
-                              nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
+                              nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
                               pat_len);
                        i++;
                }
@@ -8034,6 +8083,264 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
 }
 #endif
 
+static int nl80211_send_coalesce_rules(struct sk_buff *msg,
+                                      struct cfg80211_registered_device *rdev)
+{
+       struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules;
+       int i, j, pat_len;
+       struct cfg80211_coalesce_rules *rule;
+
+       if (!rdev->coalesce->n_rules)
+               return 0;
+
+       nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE);
+       if (!nl_rules)
+               return -ENOBUFS;
+
+       for (i = 0; i < rdev->coalesce->n_rules; i++) {
+               nl_rule = nla_nest_start(msg, i + 1);
+               if (!nl_rule)
+                       return -ENOBUFS;
+
+               rule = &rdev->coalesce->rules[i];
+               if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY,
+                               rule->delay))
+                       return -ENOBUFS;
+
+               if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION,
+                               rule->condition))
+                       return -ENOBUFS;
+
+               nl_pats = nla_nest_start(msg,
+                               NL80211_ATTR_COALESCE_RULE_PKT_PATTERN);
+               if (!nl_pats)
+                       return -ENOBUFS;
+
+               for (j = 0; j < rule->n_patterns; j++) {
+                       nl_pat = nla_nest_start(msg, j + 1);
+                       if (!nl_pat)
+                               return -ENOBUFS;
+                       pat_len = rule->patterns[j].pattern_len;
+                       if (nla_put(msg, NL80211_PKTPAT_MASK,
+                                   DIV_ROUND_UP(pat_len, 8),
+                                   rule->patterns[j].mask) ||
+                           nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
+                                   rule->patterns[j].pattern) ||
+                           nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
+                                       rule->patterns[j].pkt_offset))
+                               return -ENOBUFS;
+                       nla_nest_end(msg, nl_pat);
+               }
+               nla_nest_end(msg, nl_pats);
+               nla_nest_end(msg, nl_rule);
+       }
+       nla_nest_end(msg, nl_rules);
+
+       return 0;
+}
+
+static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct sk_buff *msg;
+       void *hdr;
+
+       if (!rdev->wiphy.coalesce)
+               return -EOPNOTSUPP;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+                            NL80211_CMD_GET_COALESCE);
+       if (!hdr)
+               goto nla_put_failure;
+
+       if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return genlmsg_reply(msg, info);
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
+
+void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev)
+{
+       struct cfg80211_coalesce *coalesce = rdev->coalesce;
+       int i, j;
+       struct cfg80211_coalesce_rules *rule;
+
+       if (!coalesce)
+               return;
+
+       for (i = 0; i < coalesce->n_rules; i++) {
+               rule = &coalesce->rules[i];
+               for (j = 0; j < rule->n_patterns; j++)
+                       kfree(rule->patterns[j].mask);
+               kfree(rule->patterns);
+       }
+       kfree(coalesce->rules);
+       kfree(coalesce);
+       rdev->coalesce = NULL;
+}
+
+static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
+                                      struct nlattr *rule,
+                                      struct cfg80211_coalesce_rules *new_rule)
+{
+       int err, i;
+       const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
+       struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat;
+       int rem, pat_len, mask_len, pkt_offset, n_patterns = 0;
+       struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
+
+       err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule),
+                       nla_len(rule), nl80211_coalesce_policy);
+       if (err)
+               return err;
+
+       if (tb[NL80211_ATTR_COALESCE_RULE_DELAY])
+               new_rule->delay =
+                       nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]);
+       if (new_rule->delay > coalesce->max_delay)
+               return -EINVAL;
+
+       if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION])
+               new_rule->condition =
+                       nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]);
+       if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH &&
+           new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH)
+               return -EINVAL;
+
+       if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN])
+               return -EINVAL;
+
+       nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
+                           rem)
+               n_patterns++;
+       if (n_patterns > coalesce->n_patterns)
+               return -EINVAL;
+
+       new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]),
+                                    GFP_KERNEL);
+       if (!new_rule->patterns)
+               return -ENOMEM;
+
+       new_rule->n_patterns = n_patterns;
+       i = 0;
+
+       nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
+                           rem) {
+               nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
+                         nla_len(pat), NULL);
+               if (!pat_tb[NL80211_PKTPAT_MASK] ||
+                   !pat_tb[NL80211_PKTPAT_PATTERN])
+                       return -EINVAL;
+               pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
+               mask_len = DIV_ROUND_UP(pat_len, 8);
+               if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
+                       return -EINVAL;
+               if (pat_len > coalesce->pattern_max_len ||
+                   pat_len < coalesce->pattern_min_len)
+                       return -EINVAL;
+
+               if (!pat_tb[NL80211_PKTPAT_OFFSET])
+                       pkt_offset = 0;
+               else
+                       pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]);
+               if (pkt_offset > coalesce->max_pkt_offset)
+                       return -EINVAL;
+               new_rule->patterns[i].pkt_offset = pkt_offset;
+
+               new_rule->patterns[i].mask =
+                       kmalloc(mask_len + pat_len, GFP_KERNEL);
+               if (!new_rule->patterns[i].mask)
+                       return -ENOMEM;
+               new_rule->patterns[i].pattern =
+                       new_rule->patterns[i].mask + mask_len;
+               memcpy(new_rule->patterns[i].mask,
+                      nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len);
+               new_rule->patterns[i].pattern_len = pat_len;
+               memcpy(new_rule->patterns[i].pattern,
+                      nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len);
+               i++;
+       }
+
+       return 0;
+}
+
+static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
+       struct cfg80211_coalesce new_coalesce = {};
+       struct cfg80211_coalesce *n_coalesce;
+       int err, rem_rule, n_rules = 0, i, j;
+       struct nlattr *rule;
+       struct cfg80211_coalesce_rules *tmp_rule;
+
+       if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) {
+               cfg80211_rdev_free_coalesce(rdev);
+               rdev->ops->set_coalesce(&rdev->wiphy, NULL);
+               return 0;
+       }
+
+       nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
+                           rem_rule)
+               n_rules++;
+       if (n_rules > coalesce->n_rules)
+               return -EINVAL;
+
+       new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]),
+                                    GFP_KERNEL);
+       if (!new_coalesce.rules)
+               return -ENOMEM;
+
+       new_coalesce.n_rules = n_rules;
+       i = 0;
+
+       nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
+                           rem_rule) {
+               err = nl80211_parse_coalesce_rule(rdev, rule,
+                                                 &new_coalesce.rules[i]);
+               if (err)
+                       goto error;
+
+               i++;
+       }
+
+       err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce);
+       if (err)
+               goto error;
+
+       n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL);
+       if (!n_coalesce) {
+               err = -ENOMEM;
+               goto error;
+       }
+       cfg80211_rdev_free_coalesce(rdev);
+       rdev->coalesce = n_coalesce;
+
+       return 0;
+error:
+       for (i = 0; i < new_coalesce.n_rules; i++) {
+               tmp_rule = &new_coalesce.rules[i];
+               for (j = 0; j < tmp_rule->n_patterns; j++)
+                       kfree(tmp_rule->patterns[j].mask);
+               kfree(tmp_rule->patterns);
+       }
+       kfree(new_coalesce.rules);
+
+       return err;
+}
+
 static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -9041,6 +9348,21 @@ static struct genl_ops nl80211_ops[] = {
                .flags = GENL_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_GET_COALESCE,
+               .doit = nl80211_get_coalesce,
+               .policy = nl80211_policy,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_SET_COALESCE,
+               .doit = nl80211_set_coalesce,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY |
+                                 NL80211_FLAG_NEED_RTNL,
        }
 };
 
index a4073e808c13c24bca525617d4de3cba8b18874e..44341bf53cfc98767f41899dd73479557fdc26aa 100644 (file)
@@ -74,4 +74,6 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
                     enum nl80211_radar_event event,
                     struct net_device *netdev, gfp_t gfp);
 
+void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
+
 #endif /* __NET_WIRELESS_NL80211_H */
index ae8c186b50d68510f88e758b959ad663e911a5de..ad1e4068ce06644218e6e005acdc129a1610ae56 100644 (file)
@@ -651,6 +651,8 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
                        continue;
                if (bss->pub.channel != new->pub.channel)
                        continue;
+               if (bss->pub.scan_width != new->pub.scan_width)
+                       continue;
                if (rcu_access_pointer(bss->pub.beacon_ies))
                        continue;
                ies = rcu_access_pointer(bss->pub.ies);
@@ -870,11 +872,12 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
 
 /* Returned bss is reference counted and must be cleaned up appropriately. */
 struct cfg80211_bss*
-cfg80211_inform_bss(struct wiphy *wiphy,
-                   struct ieee80211_channel *channel,
-                   const u8 *bssid, u64 tsf, u16 capability,
-                   u16 beacon_interval, const u8 *ie, size_t ielen,
-                   s32 signal, gfp_t gfp)
+cfg80211_inform_bss_width(struct wiphy *wiphy,
+                         struct ieee80211_channel *channel,
+                         enum nl80211_bss_scan_width scan_width,
+                         const u8 *bssid, u64 tsf, u16 capability,
+                         u16 beacon_interval, const u8 *ie, size_t ielen,
+                         s32 signal, gfp_t gfp)
 {
        struct cfg80211_bss_ies *ies;
        struct cfg80211_internal_bss tmp = {}, *res;
@@ -892,6 +895,7 @@ cfg80211_inform_bss(struct wiphy *wiphy,
 
        memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
        tmp.pub.channel = channel;
+       tmp.pub.scan_width = scan_width;
        tmp.pub.signal = signal;
        tmp.pub.beacon_interval = beacon_interval;
        tmp.pub.capability = capability;
@@ -924,14 +928,15 @@ cfg80211_inform_bss(struct wiphy *wiphy,
        /* cfg80211_bss_update gives us a referenced result */
        return &res->pub;
 }
-EXPORT_SYMBOL(cfg80211_inform_bss);
+EXPORT_SYMBOL(cfg80211_inform_bss_width);
 
 /* Returned bss is reference counted and must be cleaned up appropriately. */
 struct cfg80211_bss *
-cfg80211_inform_bss_frame(struct wiphy *wiphy,
-                         struct ieee80211_channel *channel,
-                         struct ieee80211_mgmt *mgmt, size_t len,
-                         s32 signal, gfp_t gfp)
+cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
+                               struct ieee80211_channel *channel,
+                               enum nl80211_bss_scan_width scan_width,
+                               struct ieee80211_mgmt *mgmt, size_t len,
+                               s32 signal, gfp_t gfp)
 {
        struct cfg80211_internal_bss tmp = {}, *res;
        struct cfg80211_bss_ies *ies;
@@ -941,7 +946,8 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
        BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
                        offsetof(struct ieee80211_mgmt, u.beacon.variable));
 
-       trace_cfg80211_inform_bss_frame(wiphy, channel, mgmt, len, signal);
+       trace_cfg80211_inform_bss_width_frame(wiphy, channel, scan_width, mgmt,
+                                             len, signal);
 
        if (WARN_ON(!mgmt))
                return NULL;
@@ -976,6 +982,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
        
        memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
        tmp.pub.channel = channel;
+       tmp.pub.scan_width = scan_width;
        tmp.pub.signal = signal;
        tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
        tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
@@ -991,7 +998,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
        /* cfg80211_bss_update gives us a referenced result */
        return &res->pub;
 }
-EXPORT_SYMBOL(cfg80211_inform_bss_frame);
+EXPORT_SYMBOL(cfg80211_inform_bss_width_frame);
 
 void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 {
index e1534baf2ebbe5d0fdc41d4e2d6ceb42fd9630f3..09af6eb426a80fcbda096eaaeef4af9522d63099 100644 (file)
@@ -2391,26 +2391,30 @@ TRACE_EVENT(cfg80211_get_bss,
                  __entry->capa_mask, __entry->capa_val)
 );
 
-TRACE_EVENT(cfg80211_inform_bss_frame,
+TRACE_EVENT(cfg80211_inform_bss_width_frame,
        TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
+                enum nl80211_bss_scan_width scan_width,
                 struct ieee80211_mgmt *mgmt, size_t len,
                 s32 signal),
-       TP_ARGS(wiphy, channel, mgmt, len, signal),
+       TP_ARGS(wiphy, channel, scan_width, mgmt, len, signal),
        TP_STRUCT__entry(
                WIPHY_ENTRY
                CHAN_ENTRY
+               __field(enum nl80211_bss_scan_width, scan_width)
                __dynamic_array(u8, mgmt, len)
                __field(s32, signal)
        ),
        TP_fast_assign(
                WIPHY_ASSIGN;
                CHAN_ASSIGN(channel);
+               __entry->scan_width = scan_width;
                if (mgmt)
                        memcpy(__get_dynamic_array(mgmt), mgmt, len);
                __entry->signal = signal;
        ),
-       TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "signal: %d",
-                 WIPHY_PR_ARG, CHAN_PR_ARG, __entry->signal)
+       TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d",
+                 WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width,
+                 __entry->signal)
 );
 
 DECLARE_EVENT_CLASS(cfg80211_bss_evt,
index 74458b7f61eb8bcd00eadd0cd4c24a380c589fb1..ce090c1c5e4fdb36459c4f6fde6b5af241a75f0e 100644 (file)
@@ -33,7 +33,8 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
 }
 EXPORT_SYMBOL(ieee80211_get_response_rate);
 
-u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband)
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
+                             enum nl80211_bss_scan_width scan_width)
 {
        struct ieee80211_rate *bitrates;
        u32 mandatory_rates = 0;
@@ -43,10 +44,15 @@ u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband)
        if (WARN_ON(!sband))
                return 1;
 
-       if (sband->band == IEEE80211_BAND_2GHZ)
-               mandatory_flag = IEEE80211_RATE_MANDATORY_B;
-       else
+       if (sband->band == IEEE80211_BAND_2GHZ) {
+               if (scan_width == NL80211_BSS_CHAN_WIDTH_5 ||
+                   scan_width == NL80211_BSS_CHAN_WIDTH_10)
+                       mandatory_flag = IEEE80211_RATE_MANDATORY_G;
+               else
+                       mandatory_flag = IEEE80211_RATE_MANDATORY_B;
+       } else {
                mandatory_flag = IEEE80211_RATE_MANDATORY_A;
+       }
 
        bitrates = sband->bitrates;
        for (i = 0; i < sband->n_bitrates; i++)