]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge tag 'mac80211-next-for-davem-2017-01-13' of git://git.kernel.org/pub/scm/linux...
authorDavid S. Miller <davem@davemloft.net>
Sat, 14 Jan 2017 17:02:15 +0000 (12:02 -0500)
committerDavid S. Miller <davem@davemloft.net>
Sat, 14 Jan 2017 17:02:15 +0000 (12:02 -0500)
Johannes Berg says:

====================
For 4.11, we seem to have more than in the past few releases:
 * socket owner support for connections, so when the wifi
   manager (e.g. wpa_supplicant) is killed, connections are
   torn down - wpa_supplicant is critical to managing certain
   operations, and can opt in to this where applicable
 * minstrel & minstrel_ht updates to be more efficient (time and space)
 * set wifi_acked/wifi_acked_valid for skb->destructor use in the
   kernel, which was already available to userspace
 * don't indicate new mesh peers that might be used if there's no
   room to add them
 * multicast-to-unicast support in mac80211, for better medium usage
   (since unicast frames can use *much* higher rates, by ~3 orders of
   magnitude)
 * add API to read channel (frequency) limitations from DT
 * add infrastructure to allow randomizing public action frames for
   MAC address privacy (still requires driver support)
 * many cleanups and small improvements/fixes across the board
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
49 files changed:
Documentation/devicetree/bindings/net/wireless/ieee80211.txt [new file with mode: 0644]
Documentation/driver-api/80211/cfg80211.rst
Documentation/networking/regulatory.txt
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/marvell/mwifiex/cfg80211.c
include/linux/ieee80211.h
include/net/cfg80211.h
include/net/iw_handler.h
include/net/mac80211.h
include/uapi/linux/nl80211.h
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/debugfs.c
net/mac80211/debugfs_netdev.c
net/mac80211/ieee80211_i.h
net/mac80211/mesh.c
net/mac80211/mesh.h
net/mac80211/mesh_plink.c
net/mac80211/mesh_sync.c
net/mac80211/mlme.c
net/mac80211/rc80211_minstrel.c
net/mac80211/rc80211_minstrel.h
net/mac80211/rc80211_minstrel_debugfs.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rc80211_minstrel_ht.h
net/mac80211/rc80211_minstrel_ht_debugfs.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/vht.c
net/mac80211/wep.c
net/mac80211/wpa.c
net/rfkill/core.c
net/wireless/Makefile
net/wireless/core.c
net/wireless/core.h
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/of.c [new file with mode: 0644]
net/wireless/reg.c
net/wireless/scan.c
net/wireless/sme.c
net/wireless/sysfs.c
net/wireless/util.c
net/wireless/wext-core.c
net/wireless/wext-sme.c

diff --git a/Documentation/devicetree/bindings/net/wireless/ieee80211.txt b/Documentation/devicetree/bindings/net/wireless/ieee80211.txt
new file mode 100644 (file)
index 0000000..f6442b1
--- /dev/null
@@ -0,0 +1,24 @@
+Common IEEE 802.11 properties
+
+This provides documentation of common properties that are valid for all wireless
+devices.
+
+Optional properties:
+ - ieee80211-freq-limit : list of supported frequency ranges in KHz. This can be
+       used for devices that in a given config support less channels than
+       normally. It may happen chipset supports a wide wireless band but it is
+       limited to some part of it due to used antennas or power amplifier.
+       An example case for this can be tri-band wireless router with two
+       identical chipsets used for two different 5 GHz subbands. Using them
+       incorrectly could not work or decrease performance noticeably.
+
+Example:
+
+pcie@0,0 {
+       reg = <0x0000 0 0 0 0>;
+       wifi@0,0 {
+               reg = <0x0000 0 0 0 0>;
+               ieee80211-freq-limit = <2402000 2482000>,
+                                      <5170000 5250000>;
+       };
+};
index b1e149ea6feeb9debed0f51208033eeee35219f4..eca534ab617259a63bdf0d63f6a14319dedc7c0d 100644 (file)
@@ -44,6 +44,9 @@ Device registration
 .. kernel-doc:: include/net/cfg80211.h
    :functions: wiphy_new
 
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_read_of_freq_limits
+
 .. kernel-doc:: include/net/cfg80211.h
    :functions: wiphy_register
 
index 356f791af5747fe4a2e1f25dea588d8b371d3d5b..7818b5fe448b60dae421e391ec4679b1c1f18c79 100644 (file)
@@ -156,12 +156,12 @@ struct ieee80211_regdomain mydriver_jp_regdom = {
        //.alpha2 =  "99", /* If I have no alpha2 to map it to */
        .reg_rules = {
                /* IEEE 802.11b/g, channels 1..14 */
-               REG_RULE(2412-20, 2484+20, 40, 6, 20, 0),
+               REG_RULE(2412-10, 2484+10, 40, 6, 20, 0),
                /* IEEE 802.11a, channels 34..48 */
-               REG_RULE(5170-20, 5240+20, 40, 6, 20,
+               REG_RULE(5170-10, 5240+10, 40, 6, 20,
                        NL80211_RRF_NO_IR),
                /* IEEE 802.11a, channels 52..64 */
-               REG_RULE(5260-20, 5320+20, 40, 6, 20,
+               REG_RULE(5260-10, 5320+10, 40, 6, 20,
                        NL80211_RRF_NO_IR|
                        NL80211_RRF_DFS),
        }
@@ -205,7 +205,7 @@ the data in regdb.c as an alternative to using CRDA.
 The file net/wireless/db.txt should be kept up-to-date with the db.txt
 file available in the git repository here:
 
-    git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git
+    git://git.kernel.org/pub/scm/linux/kernel/git/sforshee/wireless-regdb.git
 
 Again, most users in most situations should be using the CRDA package
 provided with their distribution, and in most other situations users
index 86d082cf4eef06f607dc8e32f790721f63bf3b77..0bc7fe8c4b528008c591b7f8dcf098bdac2d8bec 100644 (file)
@@ -2451,8 +2451,7 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
                u32 phymode = __le32_to_cpu(resp->chan_change.phymode);
                u32 freq = __le32_to_cpu(resp->chan_change.freq);
 
-               ar->tgt_oper_chan =
-                       __ieee80211_get_channel(ar->hw->wiphy, freq);
+               ar->tgt_oper_chan = ieee80211_get_channel(ar->hw->wiphy, freq);
                ath10k_dbg(ar, ATH10K_DBG_HTT,
                           "htt chan change freq %u phymode %s\n",
                           freq, ath10k_wmi_phymode_str(phymode));
index 145cc4b5103b80bc2a0b0f5c94987a2d1349b39d..1e3bd435a694534f4a450b32e8b0bd69702e5269 100644 (file)
@@ -2078,7 +2078,7 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv)
        ie_len = ie_buf[1] + sizeof(struct ieee_types_header);
 
        band = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
-       chan = __ieee80211_get_channel(priv->wdev.wiphy,
+       chan = ieee80211_get_channel(priv->wdev.wiphy,
                        ieee80211_channel_to_frequency(bss_info.bss_chan,
                                                       band));
 
index fe849329511a7b4e46ddb6d61441b8675131eb1b..87d1937e46712db3c74f3697a62bba4a393c7806 100644 (file)
@@ -185,6 +185,8 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2)
 
 /* number of user priorities 802.11 uses */
 #define IEEE80211_NUM_UPS              8
+/* number of ACs */
+#define IEEE80211_NUM_ACS              4
 
 #define IEEE80211_QOS_CTL_LEN          2
 /* 1d tag mask */
index 814be4b4200c253da028555613a67eead223933b..b7aba6e1a586aa6ce02aa6d7ec953976617c4d31 100644 (file)
@@ -311,6 +311,34 @@ struct ieee80211_supported_band {
        struct ieee80211_sta_vht_cap vht_cap;
 };
 
+/**
+ * wiphy_read_of_freq_limits - read frequency limits from device tree
+ *
+ * @wiphy: the wireless device to get extra limits for
+ *
+ * Some devices may have extra limitations specified in DT. This may be useful
+ * for chipsets that normally support more bands but are limited due to board
+ * design (e.g. by antennas or external power amplifier).
+ *
+ * This function reads info from DT and uses it to *modify* channels (disable
+ * unavailable ones). It's usually a *bad* idea to use it in drivers with
+ * shared channel data as DT limitations are device specific. You should make
+ * sure to call it only if channels in wiphy are copied and can be modified
+ * without affecting other devices.
+ *
+ * As this function access device node it has to be called after set_wiphy_dev.
+ * It also modifies channels so they have to be set first.
+ * If using this helper, call it before wiphy_register().
+ */
+#ifdef CONFIG_OF
+void wiphy_read_of_freq_limits(struct wiphy *wiphy);
+#else /* CONFIG_OF */
+static inline void wiphy_read_of_freq_limits(struct wiphy *wiphy)
+{
+}
+#endif /* !CONFIG_OF */
+
+
 /*
  * Wireless hardware/device configuration structures and methods
  */
@@ -1591,6 +1619,17 @@ struct cfg80211_sched_scan_plan {
        u32 iterations;
 };
 
+/**
+ * struct cfg80211_bss_select_adjust - BSS selection with RSSI adjustment.
+ *
+ * @band: band of BSS which should match for RSSI level adjustment.
+ * @delta: value of RSSI level adjustment.
+ */
+struct cfg80211_bss_select_adjust {
+       enum nl80211_band band;
+       s8 delta;
+};
+
 /**
  * struct cfg80211_sched_scan_request - scheduled scan request description
  *
@@ -1626,6 +1665,16 @@ struct cfg80211_sched_scan_plan {
  *     cycle.  The driver may ignore this parameter and start
  *     immediately (or at any other time), if this feature is not
  *     supported.
+ * @relative_rssi_set: Indicates whether @relative_rssi is set or not.
+ * @relative_rssi: Relative RSSI threshold in dB to restrict scan result
+ *     reporting in connected state to cases where a matching BSS is determined
+ *     to have better or slightly worse RSSI than the current connected BSS.
+ *     The relative RSSI threshold values are ignored in disconnected state.
+ * @rssi_adjust: delta dB of RSSI preference to be given to the BSSs that belong
+ *     to the specified band while deciding whether a better BSS is reported
+ *     using @relative_rssi. If delta is a negative number, the BSSs that
+ *     belong to the specified band will be penalized by delta dB in relative
+ *     comparisions.
  */
 struct cfg80211_sched_scan_request {
        struct cfg80211_ssid *ssids;
@@ -1645,6 +1694,10 @@ struct cfg80211_sched_scan_request {
        u8 mac_addr[ETH_ALEN] __aligned(2);
        u8 mac_addr_mask[ETH_ALEN] __aligned(2);
 
+       bool relative_rssi_set;
+       s8 relative_rssi;
+       struct cfg80211_bss_select_adjust rssi_adjust;
+
        /* internal */
        struct wiphy *wiphy;
        struct net_device *dev;
@@ -1952,17 +2005,6 @@ struct cfg80211_ibss_params {
        struct ieee80211_ht_cap ht_capa_mask;
 };
 
-/**
- * struct cfg80211_bss_select_adjust - BSS selection with RSSI adjustment.
- *
- * @band: band of BSS which should match for RSSI level adjustment.
- * @delta: value of RSSI level adjustment.
- */
-struct cfg80211_bss_select_adjust {
-       enum nl80211_band band;
-       s8 delta;
-};
-
 /**
  * struct cfg80211_bss_selection - connection parameters for BSS selection.
  *
@@ -3837,6 +3879,9 @@ struct cfg80211_cached_keys;
  * @conn: (private) cfg80211 software SME connection state machine data
  * @connect_keys: (private) keys to set after connection is established
  * @conn_bss_type: connecting/connected BSS type
+ * @conn_owner_nlportid: (private) connection owner socket port ID
+ * @disconnect_wk: (private) auto-disconnect work
+ * @disconnect_bssid: (private) the BSSID to use for auto-disconnect
  * @ibss_fixed: (private) IBSS is using fixed BSSID
  * @ibss_dfs_possible: (private) IBSS may change to a DFS channel
  * @event_list: (private) list for internal event processing
@@ -3868,6 +3913,10 @@ struct wireless_dev {
        struct cfg80211_conn *conn;
        struct cfg80211_cached_keys *connect_keys;
        enum ieee80211_bss_type conn_bss_type;
+       u32 conn_owner_nlportid;
+
+       struct work_struct disconnect_wk;
+       u8 disconnect_bssid[ETH_ALEN];
 
        struct list_head event_list;
        spinlock_t event_lock;
@@ -3955,26 +4004,15 @@ int ieee80211_channel_to_frequency(int chan, enum nl80211_band band);
  */
 int ieee80211_frequency_to_channel(int freq);
 
-/*
- * Name indirection necessary because the ieee80211 code also has
- * a function named "ieee80211_get_channel", so if you include
- * cfg80211's header file you get cfg80211's version, if you try
- * to include both header files you'll (rightfully!) get a symbol
- * clash.
- */
-struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy,
-                                                 int freq);
 /**
  * ieee80211_get_channel - get channel struct from wiphy for specified frequency
+ *
  * @wiphy: the struct wiphy to get the channel for
  * @freq: the center frequency of the channel
+ *
  * Return: The channel struct from @wiphy at @freq.
  */
-static inline struct ieee80211_channel *
-ieee80211_get_channel(struct wiphy *wiphy, int freq)
-{
-       return __ieee80211_get_channel(wiphy, freq);
-}
+struct ieee80211_channel *ieee80211_get_channel(struct wiphy *wiphy, int freq);
 
 /**
  * ieee80211_get_response_rate - get basic rate for a given rate
@@ -5048,20 +5086,32 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
  * @req_ie_len: association request IEs length
  * @resp_ie: association response IEs (may be %NULL)
  * @resp_ie_len: assoc response IEs length
- * @status: status code, 0 for successful connection, use
- *      %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
- *      the real status code for failures.
+ * @status: status code, %WLAN_STATUS_SUCCESS for successful connection, use
+ *     %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
+ *     the real status code for failures. If this call is used to report a
+ *     failure due to a timeout (e.g., not receiving an Authentication frame
+ *     from the AP) instead of an explicit rejection by the AP, -1 is used to
+ *     indicate that this is a failure, but without a status code.
+ *     @timeout_reason is used to report the reason for the timeout in that
+ *     case.
  * @gfp: allocation flags
- *
- * It should be called by the underlying driver whenever connect() has
- * succeeded. This is similar to cfg80211_connect_result(), but with the
- * option of identifying the exact bss entry for the connection. Only one of
- * these functions should be called.
+ * @timeout_reason: reason for connection timeout. This is used when the
+ *     connection fails due to a timeout instead of an explicit rejection from
+ *     the AP. %NL80211_TIMEOUT_UNSPECIFIED is used when the timeout reason is
+ *     not known. This value is used only if @status < 0 to indicate that the
+ *     failure is due to a timeout and not due to explicit rejection by the AP.
+ *     This value is ignored in other cases (@status >= 0).
+ *
+ * It should be called by the underlying driver once execution of the connection
+ * request from connect() has been completed. This is similar to
+ * cfg80211_connect_result(), but with the option of identifying the exact bss
+ * entry for the connection. Only one of these functions should be called.
  */
 void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
                          struct cfg80211_bss *bss, const u8 *req_ie,
                          size_t req_ie_len, const u8 *resp_ie,
-                         size_t resp_ie_len, int status, gfp_t gfp);
+                         size_t resp_ie_len, int status, gfp_t gfp,
+                         enum nl80211_timeout_reason timeout_reason);
 
 /**
  * cfg80211_connect_result - notify cfg80211 of connection result
@@ -5072,13 +5122,15 @@ void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
  * @req_ie_len: association request IEs length
  * @resp_ie: association response IEs (may be %NULL)
  * @resp_ie_len: assoc response IEs length
- * @status: status code, 0 for successful connection, use
+ * @status: status code, %WLAN_STATUS_SUCCESS for successful connection, use
  *     %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
  *     the real status code for failures.
  * @gfp: allocation flags
  *
- * It should be called by the underlying driver whenever connect() has
- * succeeded.
+ * It should be called by the underlying driver once execution of the connection
+ * request from connect() has been completed. This is similar to
+ * cfg80211_connect_bss() which allows the exact bss entry to be specified. Only
+ * one of these functions should be called.
  */
 static inline void
 cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
@@ -5087,7 +5139,8 @@ cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                        u16 status, gfp_t gfp)
 {
        cfg80211_connect_bss(dev, bssid, NULL, req_ie, req_ie_len, resp_ie,
-                            resp_ie_len, status, gfp);
+                            resp_ie_len, status, gfp,
+                            NL80211_TIMEOUT_UNSPECIFIED);
 }
 
 /**
@@ -5098,6 +5151,7 @@ cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
  * @req_ie: association request IEs (maybe be %NULL)
  * @req_ie_len: association request IEs length
  * @gfp: allocation flags
+ * @timeout_reason: reason for connection timeout.
  *
  * It should be called by the underlying driver whenever connect() has failed
  * in a sequence where no explicit authentication/association rejection was
@@ -5107,10 +5161,11 @@ cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
  */
 static inline void
 cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid,
-                        const u8 *req_ie, size_t req_ie_len, gfp_t gfp)
+                        const u8 *req_ie, size_t req_ie_len, gfp_t gfp,
+                        enum nl80211_timeout_reason timeout_reason)
 {
        cfg80211_connect_bss(dev, bssid, NULL, req_ie, req_ie_len, NULL, 0, -1,
-                            gfp);
+                            gfp, timeout_reason);
 }
 
 /**
index e0f4109e64c6fca9ba87d768c2c7b1220a6557f4..2509728650bd79f8caabe6de6342c04eae0dd490 100644 (file)
@@ -505,25 +505,8 @@ static inline int iwe_stream_event_len_adjust(struct iw_request_info *info,
 /*
  * Wrapper to add an Wireless Event to a stream of events.
  */
-static inline char *
-iwe_stream_add_event(struct iw_request_info *info, char *stream, char *ends,
-                    struct iw_event *iwe, int event_len)
-{
-       int lcp_len = iwe_stream_lcp_len(info);
-
-       event_len = iwe_stream_event_len_adjust(info, event_len);
-
-       /* Check if it's possible */
-       if(likely((stream + event_len) < ends)) {
-               iwe->len = event_len;
-               /* Beware of alignement issues on 64 bits */
-               memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
-               memcpy(stream + lcp_len, &iwe->u,
-                      event_len - lcp_len);
-               stream += event_len;
-       }
-       return stream;
-}
+char *iwe_stream_add_event(struct iw_request_info *info, char *stream,
+                          char *ends, struct iw_event *iwe, int event_len);
 
 static inline char *
 iwe_stream_add_event_check(struct iw_request_info *info, char *stream,
@@ -541,26 +524,8 @@ iwe_stream_add_event_check(struct iw_request_info *info, char *stream,
  * Wrapper to add an short Wireless Event containing a pointer to a
  * stream of events.
  */
-static inline char *
-iwe_stream_add_point(struct iw_request_info *info, char *stream, char *ends,
-                    struct iw_event *iwe, char *extra)
-{
-       int event_len = iwe_stream_point_len(info) + iwe->u.data.length;
-       int point_len = iwe_stream_point_len(info);
-       int lcp_len   = iwe_stream_lcp_len(info);
-
-       /* Check if it's possible */
-       if(likely((stream + event_len) < ends)) {
-               iwe->len = event_len;
-               memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
-               memcpy(stream + lcp_len,
-                      ((char *) &iwe->u) + IW_EV_POINT_OFF,
-                      IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
-               memcpy(stream + point_len, extra, iwe->u.data.length);
-               stream += event_len;
-       }
-       return stream;
-}
+char *iwe_stream_add_point(struct iw_request_info *info, char *stream,
+                          char *ends, struct iw_event *iwe, char *extra);
 
 static inline char *
 iwe_stream_add_point_check(struct iw_request_info *info, char *stream,
@@ -579,25 +544,8 @@ iwe_stream_add_point_check(struct iw_request_info *info, char *stream,
  * Be careful, this one is tricky to use properly :
  * At the first run, you need to have (value = event + IW_EV_LCP_LEN).
  */
-static inline char *
-iwe_stream_add_value(struct iw_request_info *info, char *event, char *value,
-                    char *ends, struct iw_event *iwe, int event_len)
-{
-       int lcp_len = iwe_stream_lcp_len(info);
-
-       /* Don't duplicate LCP */
-       event_len -= IW_EV_LCP_LEN;
-
-       /* Check if it's possible */
-       if(likely((value + event_len) < ends)) {
-               /* Add new value */
-               memcpy(value, &iwe->u, event_len);
-               value += event_len;
-               /* Patch LCP */
-               iwe->len = value - event;
-               memcpy(event, (char *) iwe, lcp_len);
-       }
-       return value;
-}
+char *iwe_stream_add_value(struct iw_request_info *info, char *event,
+                          char *value, char *ends, struct iw_event *iwe,
+                          int event_len);
 
 #endif /* _IW_HANDLER_H */
index 5345d358a510ce63d5c13fd48cfd7f9284405768..86967b85dfd07231e9e31032f67388ee6202dc95 100644 (file)
@@ -147,7 +147,6 @@ enum ieee80211_ac_numbers {
        IEEE80211_AC_BE         = 2,
        IEEE80211_AC_BK         = 3,
 };
-#define IEEE80211_NUM_ACS      4
 
 /**
  * struct ieee80211_tx_queue_params - transmit queue configuration
@@ -1018,7 +1017,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  * @RX_FLAG_DECRYPTED: This frame was decrypted in hardware.
  * @RX_FLAG_MMIC_STRIPPED: the Michael MIC is stripped off this frame,
  *     verification has been done by the hardware.
- * @RX_FLAG_IV_STRIPPED: The IV/ICV are stripped from this frame.
+ * @RX_FLAG_IV_STRIPPED: The IV and ICV are stripped from this frame.
  *     If this flag is set, the stack cannot do any replay detection
  *     hence the driver or hardware will have to do that.
  * @RX_FLAG_PN_VALIDATED: Currently only valid for CCMP/GCMP frames, this
@@ -1089,6 +1088,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  * @RX_FLAG_ALLOW_SAME_PN: Allow the same PN as same packet before.
  *     This is used for AMSDU subframes which can have the same PN as
  *     the first subframe.
+ * @RX_FLAG_ICV_STRIPPED: The ICV is stripped from this frame. CRC checking must
+ *     be done in the hardware.
  */
 enum mac80211_rx_flags {
        RX_FLAG_MMIC_ERROR              = BIT(0),
@@ -1124,6 +1125,7 @@ enum mac80211_rx_flags {
        RX_FLAG_RADIOTAP_VENDOR_DATA    = BIT(31),
        RX_FLAG_MIC_STRIPPED            = BIT_ULL(32),
        RX_FLAG_ALLOW_SAME_PN           = BIT_ULL(33),
+       RX_FLAG_ICV_STRIPPED            = BIT_ULL(34),
 };
 
 #define RX_FLAG_STBC_SHIFT             26
index 6b76e3b0c18eac57491268b5ce395050914cef99..c51b40cc0645d6beae84a8f3ef0ea4e28e483c0c 100644 (file)
@@ -1820,6 +1820,8 @@ enum nl80211_commands {
  *     and remove functions. NAN notifications will be sent in unicast to that
  *     socket. Without this attribute, any socket can add functions and the
  *     notifications will be sent to the %NL80211_MCGRP_NAN multicast group.
+ *     If set during %NL80211_CMD_ASSOCIATE or %NL80211_CMD_CONNECT the
+ *     station will deauthenticate when the socket is closed.
  *
  * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
  *     the TDLS link initiator.
@@ -1980,6 +1982,24 @@ enum nl80211_commands {
  * @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also
  *     used in various commands/events for specifying the BSSID.
  *
+ * @NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI: Relative RSSI threshold by which
+ *     other BSSs has to be better or slightly worse than the current
+ *     connected BSS so that they get reported to user space.
+ *     This will give an opportunity to userspace to consider connecting to
+ *     other matching BSSs which have better or slightly worse RSSI than
+ *     the current connected BSS by using an offloaded operation to avoid
+ *     unnecessary wakeups.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in
+ *     the specified band is to be adjusted before doing
+ *     %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparision to figure out
+ *     better BSSs. The attribute value is a packed structure
+ *     value as specified by &struct nl80211_bss_select_rssi_adjust.
+ *
+ * @NL80211_ATTR_TIMEOUT_REASON: The reason for which an operation timed out.
+ *     u32 attribute with an &enum nl80211_timeout_reason value. This is used,
+ *     e.g., with %NL80211_CMD_CONNECT event.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2386,6 +2406,11 @@ enum nl80211_attrs {
 
        NL80211_ATTR_BSSID,
 
+       NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
+       NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+
+       NL80211_ATTR_TIMEOUT_REASON,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -3078,6 +3103,13 @@ enum nl80211_reg_rule_attr {
  *     how this API was implemented in the past. Also, due to the same problem,
  *     the only way to create a matchset with only an RSSI filter (with this
  *     attribute) is if there's only a single matchset with the RSSI attribute.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI: Flag indicating whether
+ *     %NL80211_SCHED_SCAN_MATCH_ATTR_RSSI to be used as absolute RSSI or
+ *     relative to current bss's RSSI.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST: When present the RSSI level for
+ *     BSS-es in the specified band is to be adjusted before doing
+ *     RSSI-based BSS selection. The attribute value is a packed structure
+ *     value as specified by &struct nl80211_bss_select_rssi_adjust.
  * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
  *     attribute number currently defined
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -3087,6 +3119,8 @@ enum nl80211_sched_scan_match_attr {
 
        NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
        NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+       NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
+       NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
 
        /* keep last */
        __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
@@ -4697,6 +4731,13 @@ enum nl80211_feature_flags {
  *     configuration (AP/mesh) with VHT rates.
  * @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup
  *     with user space SME (NL80211_CMD_AUTHENTICATE) in station mode.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA: This driver supports randomized TA
+ *     in @NL80211_CMD_FRAME while not associated.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED: This driver supports
+ *     randomized TA in @NL80211_CMD_FRAME while associated.
+ * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan
+ *     for reporting BSSs with better RSSI than the current connected BSS
+ *     (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI).
  *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -4712,6 +4753,9 @@ enum nl80211_ext_feature_index {
        NL80211_EXT_FEATURE_BEACON_RATE_HT,
        NL80211_EXT_FEATURE_BEACON_RATE_VHT,
        NL80211_EXT_FEATURE_FILS_STA,
+       NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA,
+       NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED,
+       NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
 
        /* add new features before the definition below */
        NUM_NL80211_EXT_FEATURES,
@@ -4750,6 +4794,21 @@ enum nl80211_connect_failed_reason {
        NL80211_CONN_FAIL_BLOCKED_CLIENT,
 };
 
+/**
+ * enum nl80211_timeout_reason - timeout reasons
+ *
+ * @NL80211_TIMEOUT_UNSPECIFIED: Timeout reason unspecified.
+ * @NL80211_TIMEOUT_SCAN: Scan (AP discovery) timed out.
+ * @NL80211_TIMEOUT_AUTH: Authentication timed out.
+ * @NL80211_TIMEOUT_ASSOC: Association timed out.
+ */
+enum nl80211_timeout_reason {
+       NL80211_TIMEOUT_UNSPECIFIED,
+       NL80211_TIMEOUT_SCAN,
+       NL80211_TIMEOUT_AUTH,
+       NL80211_TIMEOUT_ASSOC,
+};
+
 /**
  * enum nl80211_scan_flags -  scan request control flags
  *
@@ -4964,8 +5023,9 @@ enum nl80211_sched_scan_plan {
 /**
  * struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters.
  *
- * @band: band of BSS that must match for RSSI value adjustment.
- * @delta: value used to adjust the RSSI value of matching BSS.
+ * @band: band of BSS that must match for RSSI value adjustment. The value
+ *     of this field is according to &enum nl80211_band.
+ * @delta: value used to adjust the RSSI value of matching BSS in dB.
  */
 struct nl80211_bss_select_rssi_adjust {
        __u8 band;
index e91e503bf99257d7e8f3945c1500eebaa91f5e81..a0be2f6cd1212ae3a35219d59b8f2bdf90c7d07e 100644 (file)
@@ -3563,6 +3563,17 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif,
 }
 EXPORT_SYMBOL(ieee80211_nan_func_match);
 
+static int ieee80211_set_multicast_to_unicast(struct wiphy *wiphy,
+                                             struct net_device *dev,
+                                             const bool enabled)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       sdata->u.ap.multicast_to_unicast = enabled;
+
+       return 0;
+}
+
 const struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -3653,4 +3664,5 @@ const struct cfg80211_ops mac80211_config_ops = {
        .nan_change_conf = ieee80211_nan_change_conf,
        .add_nan_func = ieee80211_add_nan_func,
        .del_nan_func = ieee80211_del_nan_func,
+       .set_multicast_to_unicast = ieee80211_set_multicast_to_unicast,
 };
index e75cbf6ecc26e4ec7bde777c385e001c0e49e371..7550fd264286e9aecd7aca35ebaea4604d3780cd 100644 (file)
@@ -1270,7 +1270,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
        struct ieee80211_sub_if_data *sdata, *sdata_tmp;
        struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx;
        struct ieee80211_chanctx *new_ctx = NULL;
-       int i, err, n_assigned, n_reserved, n_ready;
+       int err, n_assigned, n_reserved, n_ready;
        int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0;
 
        lockdep_assert_held(&local->mtx);
@@ -1391,8 +1391,6 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
         * Update all structures, values and pointers to point to new channel
         * context(s).
         */
-
-       i = 0;
        list_for_each_entry(ctx, &local->chanctx_list, list) {
                if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
                        continue;
index e02ba42ca827501fdefc155d0ad71f2d4ffc2f36..f62cd0e13c58315337726f6795ecf08822f26e89 100644 (file)
@@ -243,6 +243,31 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
        return rv;
 }
 
+static ssize_t misc_read(struct file *file, char __user *user_buf,
+                        size_t count, loff_t *ppos)
+{
+       struct ieee80211_local *local = file->private_data;
+       /* Max len of each line is 16 characters, plus 9 for 'pending:\n' */
+       size_t bufsz = IEEE80211_MAX_QUEUES * 16 + 9;
+       char *buf = kzalloc(bufsz, GFP_KERNEL);
+       char *pos = buf, *end = buf + bufsz - 1;
+       ssize_t rv;
+       int i;
+       int ln;
+
+       pos += scnprintf(pos, end - pos, "pending:\n");
+
+       for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
+               ln = skb_queue_len(&local->pending[i]);
+               pos += scnprintf(pos, end - pos, "[%i] %d\n",
+                                i, ln);
+       }
+
+       rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+       kfree(buf);
+       return rv;
+}
+
 static ssize_t queues_read(struct file *file, char __user *user_buf,
                           size_t count, loff_t *ppos)
 {
@@ -263,6 +288,7 @@ static ssize_t queues_read(struct file *file, char __user *user_buf,
 
 DEBUGFS_READONLY_FILE_OPS(hwflags);
 DEBUGFS_READONLY_FILE_OPS(queues);
+DEBUGFS_READONLY_FILE_OPS(misc);
 
 /* statistics stuff */
 
@@ -331,6 +357,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
        DEBUGFS_ADD(total_ps_buffered);
        DEBUGFS_ADD(wep_iv);
        DEBUGFS_ADD(queues);
+       DEBUGFS_ADD(misc);
 #ifdef CONFIG_PM
        DEBUGFS_ADD_MODE(reset, 0200);
 #endif
index 1a05f85cb1f0610b41ea212df2624a9078ea16cd..8f5fff8b20409e6ea5ed011fb1770b71f225fa1d 100644 (file)
@@ -519,6 +519,8 @@ static ssize_t ieee80211_if_fmt_aqm(
 }
 IEEE80211_IF_FILE_R(aqm);
 
+IEEE80211_IF_FILE(multicast_to_unicast, u.ap.multicast_to_unicast, HEX);
+
 /* IBSS attributes */
 static ssize_t ieee80211_if_fmt_tsf(
        const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
@@ -683,6 +685,7 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata)
        DEBUGFS_ADD(dtim_count);
        DEBUGFS_ADD(num_buffered_multicast);
        DEBUGFS_ADD_MODE(tkip_mic_test, 0200);
+       DEBUGFS_ADD_MODE(multicast_to_unicast, 0600);
 }
 
 static void add_vlan_files(struct ieee80211_sub_if_data *sdata)
index b2069fbd60f9e13a7888a3daed3fba789cd3593b..159a1a733725069417631f9c6386dc8e49450947 100644 (file)
@@ -297,6 +297,7 @@ struct ieee80211_if_ap {
                         driver_smps_mode; /* smps mode request */
 
        struct work_struct request_smps_work;
+       bool multicast_to_unicast;
 };
 
 struct ieee80211_if_wds {
@@ -624,8 +625,8 @@ struct ieee80211_mesh_sync_ops {
                             struct ieee80211_rx_status *rx_status);
 
        /* should be called with beacon_data under RCU read lock */
-       void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata,
-                           struct beacon_data *beacon);
+       void (*adjust_tsf)(struct ieee80211_sub_if_data *sdata,
+                          struct beacon_data *beacon);
        /* add other framework functions here */
 };
 
@@ -688,7 +689,6 @@ struct ieee80211_if_mesh {
        const struct ieee80211_mesh_sync_ops *sync_ops;
        s64 sync_offset_clockdrift_max;
        spinlock_t sync_offset_lock;
-       bool adjusting_tbtt;
        /* mesh power save */
        enum nl80211_mesh_power_mode nonpeer_pm;
        int ps_peers_light_sleep;
index 42120d965263d2ec1719211da37b7900814e4122..9c23172feba07abd63d7fdf2ab1530cbca28b8b9 100644 (file)
@@ -279,10 +279,6 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
        /* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */
        *pos |= ifmsh->ps_peers_deep_sleep ?
                        IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00;
-       *pos++ |= ifmsh->adjusting_tbtt ?
-                       IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
-       *pos++ = 0x00;
-
        return 0;
 }
 
@@ -850,7 +846,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
        ifmsh->mesh_cc_id = 0;  /* Disabled */
        /* register sync ops from extensible synchronization framework */
        ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id);
-       ifmsh->adjusting_tbtt = false;
        ifmsh->sync_offset_clockdrift_max = 0;
        set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags);
        ieee80211_mesh_root_setup(ifmsh);
@@ -1349,7 +1344,7 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
                ieee80211_mesh_rootpath(sdata);
 
        if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags))
-               mesh_sync_adjust_tbtt(sdata);
+               mesh_sync_adjust_tsf(sdata);
 
        if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags))
                mesh_bss_info_changed(sdata);
index 26b9ccbe1fce7cc5125c84419cf47e60c0201d55..7e5f271e3c30dbf960b78aecb87afeaff9d347c3 100644 (file)
@@ -341,7 +341,7 @@ static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)
 }
 
 void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata);
-void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata);
+void mesh_sync_adjust_tsf(struct ieee80211_sub_if_data *sdata);
 void ieee80211s_stop(void);
 #else
 static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)
index 7fcdcf622655286b01eb965acea7d631faf43c24..fcba70e57073f372793d69d247e78ea11f8c2597 100644 (file)
@@ -505,12 +505,14 @@ mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
 
        /* Userspace handles station allocation */
        if (sdata->u.mesh.user_mpm ||
-           sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
-               cfg80211_notify_new_peer_candidate(sdata->dev, addr,
-                                                  elems->ie_start,
-                                                  elems->total_len,
-                                                  GFP_KERNEL);
-       else
+           sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) {
+               if (mesh_peer_accepts_plinks(elems) &&
+                   mesh_plink_availables(sdata))
+                       cfg80211_notify_new_peer_candidate(sdata->dev, addr,
+                                                          elems->ie_start,
+                                                          elems->total_len,
+                                                          GFP_KERNEL);
+       } else
                sta = __mesh_sta_info_alloc(sdata, addr);
 
        return sta;
index faca22cd02b59cf9c79e20307fe47f36a562ad60..a435f094a82e30ef1494342463556c0b669ddd00 100644 (file)
@@ -12,7 +12,7 @@
 #include "mesh.h"
 #include "driver-ops.h"
 
-/* This is not in the standard.  It represents a tolerable tbtt drift below
+/* This is not in the standard.  It represents a tolerable tsf drift below
  * which we do no TSF adjustment.
  */
 #define TOFFSET_MINIMUM_ADJUSTMENT 10
@@ -46,7 +46,7 @@ static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie)
                        IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING) != 0;
 }
 
-void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
+void mesh_sync_adjust_tsf(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
@@ -57,12 +57,12 @@ void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
 
        spin_lock_bh(&ifmsh->sync_offset_lock);
        if (ifmsh->sync_offset_clockdrift_max < beacon_int_fraction) {
-               msync_dbg(sdata, "TBTT : max clockdrift=%lld; adjusting\n",
+               msync_dbg(sdata, "TSF : max clockdrift=%lld; adjusting\n",
                          (long long) ifmsh->sync_offset_clockdrift_max);
                tsfdelta = -ifmsh->sync_offset_clockdrift_max;
                ifmsh->sync_offset_clockdrift_max = 0;
        } else {
-               msync_dbg(sdata, "TBTT : max clockdrift=%lld; adjusting by %llu\n",
+               msync_dbg(sdata, "TSF : max clockdrift=%lld; adjusting by %llu\n",
                          (long long) ifmsh->sync_offset_clockdrift_max,
                          (unsigned long long) beacon_int_fraction);
                tsfdelta = -beacon_int_fraction;
@@ -123,7 +123,6 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
         */
 
        if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) {
-               clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
                msync_dbg(sdata, "STA %pM : is adjusting TBTT\n",
                          sta->sta.addr);
                goto no_sync;
@@ -168,15 +167,13 @@ no_sync:
        rcu_read_unlock();
 }
 
-static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata,
+static void mesh_sync_offset_adjust_tsf(struct ieee80211_sub_if_data *sdata,
                                         struct beacon_data *beacon)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-       u8 cap;
 
        WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET);
        WARN_ON(!rcu_read_lock_held());
-       cap = beacon->meshconf->meshconf_cap;
 
        spin_lock_bh(&ifmsh->sync_offset_lock);
 
@@ -187,24 +184,16 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata,
                 * the tsf adjustment to the mesh tasklet
                 */
                msync_dbg(sdata,
-                         "TBTT : kicking off TBTT adjustment with clockdrift_max=%lld\n",
+                         "TSF : kicking off TSF adjustment with clockdrift_max=%lld\n",
                          ifmsh->sync_offset_clockdrift_max);
                set_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags);
-
-               ifmsh->adjusting_tbtt = true;
        } else {
                msync_dbg(sdata,
-                         "TBTT : max clockdrift=%lld; too small to adjust\n",
+                         "TSF : max clockdrift=%lld; too small to adjust\n",
                          (long long)ifmsh->sync_offset_clockdrift_max);
                ifmsh->sync_offset_clockdrift_max = 0;
-
-               ifmsh->adjusting_tbtt = false;
        }
        spin_unlock_bh(&ifmsh->sync_offset_lock);
-
-       beacon->meshconf->meshconf_cap = ifmsh->adjusting_tbtt ?
-                       IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING | cap :
-                       ~IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING & cap;
 }
 
 static const struct sync_method sync_methods[] = {
@@ -212,7 +201,7 @@ static const struct sync_method sync_methods[] = {
                .method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
                .ops = {
                        .rx_bcn_presp = &mesh_sync_offset_rx_bcn_presp,
-                       .adjust_tbtt = &mesh_sync_offset_adjust_tbtt,
+                       .adjust_tsf = &mesh_sync_offset_adjust_tsf,
                }
        },
 };
index 098ce9b179ee3753410eda69b9267c5a34b7b9b7..8a634451867464d8b8494000b11189f152143b8e 100644 (file)
@@ -1486,10 +1486,6 @@ void ieee80211_recalc_ps(struct ieee80211_local *local)
 
        if (count == 1 && ieee80211_powersave_allowed(found)) {
                u8 dtimper = found->u.mgd.dtim_period;
-               s32 beaconint_us;
-
-               beaconint_us = ieee80211_tu_to_usec(
-                                       found->vif.bss_conf.beacon_int);
 
                timeout = local->dynamic_ps_forced_timeout;
                if (timeout < 0)
index 14c5ba3a1b1c6c3b139729f8921ec01ef1b03d3d..3ebe4405a2d43d66f76bdef3fd4f10cd3fa82b8f 100644 (file)
@@ -159,21 +159,23 @@ minstrel_update_rates(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
 void
 minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs)
 {
+       unsigned int cur_prob;
+
        if (unlikely(mrs->attempts > 0)) {
                mrs->sample_skipped = 0;
-               mrs->cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
+               cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
                if (unlikely(!mrs->att_hist)) {
-                       mrs->prob_ewma = mrs->cur_prob;
+                       mrs->prob_ewma = cur_prob;
                } else {
                        /* update exponential weighted moving variance */
-                       mrs->prob_ewmsd = minstrel_ewmsd(mrs->prob_ewmsd,
-                                                        mrs->cur_prob,
-                                                        mrs->prob_ewma,
-                                                        EWMA_LEVEL);
+                       mrs->prob_ewmv = minstrel_ewmv(mrs->prob_ewmv,
+                                                       cur_prob,
+                                                       mrs->prob_ewma,
+                                                       EWMA_LEVEL);
 
                        /*update exponential weighted moving avarage */
                        mrs->prob_ewma = minstrel_ewma(mrs->prob_ewma,
-                                                      mrs->cur_prob,
+                                                      cur_prob,
                                                       EWMA_LEVEL);
                }
                mrs->att_hist += mrs->attempts;
@@ -365,6 +367,11 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
                return;
 #endif
 
+       /* Don't use EAPOL frames for sampling on non-mrr hw */
+       if (mp->hw->max_rates == 1 &&
+           (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
+               return;
+
        delta = (mi->total_packets * sampling_ratio / 100) -
                        (mi->sample_packets + mi->sample_deferred / 2);
 
index c230bbe93262b0affc476c01b8d1db9ab7dfe054..be6c3f35f48b30b2cb17a2ceb398fe37f6f593f3 100644 (file)
@@ -14,7 +14,7 @@
 #define SAMPLE_COLUMNS 10      /* number of columns in sample table */
 
 /* scaled fraction values */
-#define MINSTREL_SCALE  16
+#define MINSTREL_SCALE  12
 #define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
 #define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
 
@@ -36,21 +36,16 @@ minstrel_ewma(int old, int new, int weight)
 }
 
 /*
- * Perform EWMSD (Exponentially Weighted Moving Standard Deviation) calculation
+ * Perform EWMV (Exponentially Weighted Moving Variance) calculation
  */
 static inline int
-minstrel_ewmsd(int old_ewmsd, int cur_prob, int prob_ewma, int weight)
+minstrel_ewmv(int old_ewmv, int cur_prob, int prob_ewma, int weight)
 {
-       int diff, incr, tmp_var;
+       int diff, incr;
 
-       /* calculate exponential weighted moving variance */
-       diff = MINSTREL_TRUNC((cur_prob - prob_ewma) * 1000000);
+       diff = cur_prob - prob_ewma;
        incr = (EWMA_DIV - weight) * diff / EWMA_DIV;
-       tmp_var = old_ewmsd * old_ewmsd;
-       tmp_var = weight * (tmp_var + diff * incr / 1000000) / EWMA_DIV;
-
-       /* return standard deviation */
-       return (u16) int_sqrt(tmp_var);
+       return weight * (old_ewmv + MINSTREL_TRUNC(diff * incr)) / EWMA_DIV;
 }
 
 struct minstrel_rate_stats {
@@ -59,15 +54,13 @@ struct minstrel_rate_stats {
        u16 success, last_success;
 
        /* total attempts/success counters */
-       u64 att_hist, succ_hist;
+       u32 att_hist, succ_hist;
 
        /* statistis of packet delivery probability
-        *  cur_prob  - current prob within last update intervall
         *  prob_ewma - exponential weighted moving average of prob
         *  prob_ewmsd - exp. weighted moving standard deviation of prob */
-       unsigned int cur_prob;
-       unsigned int prob_ewma;
-       u16 prob_ewmsd;
+       u16 prob_ewma;
+       u16 prob_ewmv;
 
        /* maximum retry counts */
        u8 retry_count;
@@ -153,6 +146,14 @@ struct minstrel_debugfs_info {
        char buf[];
 };
 
+/* Get EWMSD (Exponentially Weighted Moving Standard Deviation) * 10 */
+static inline int
+minstrel_get_ewmsd10(struct minstrel_rate_stats *mrs)
+{
+       unsigned int ewmv = mrs->prob_ewmv;
+       return int_sqrt(MINSTREL_TRUNC(ewmv * 1000 * 1000));
+}
+
 extern const struct rate_control_ops mac80211_minstrel;
 void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
 void minstrel_remove_sta_debugfs(void *priv, void *priv_sta);
index 820b0abc9c0d6f7662cf7a98cc497b310d1281e3..36fc971deb860e5ee3b3e70e9de2b5fd91f7ea95 100644 (file)
@@ -75,7 +75,7 @@ minstrel_stats_open(struct inode *inode, struct file *file)
 {
        struct minstrel_sta_info *mi = inode->i_private;
        struct minstrel_debugfs_info *ms;
-       unsigned int i, tp_max, tp_avg, prob, eprob;
+       unsigned int i, tp_max, tp_avg, eprob;
        char *p;
 
        ms = kmalloc(2048, GFP_KERNEL);
@@ -86,13 +86,14 @@ minstrel_stats_open(struct inode *inode, struct file *file)
        p = ms->buf;
        p += sprintf(p, "\n");
        p += sprintf(p,
-                    "best   __________rate_________    ________statistics________    ________last_______    ______sum-of________\n");
+                    "best   __________rate_________    ________statistics________    ____last_____    ______sum-of________\n");
        p += sprintf(p,
-                    "rate  [name idx airtime max_tp]  [avg(tp) avg(prob) sd(prob)]  [prob.|retry|suc|att]  [#success | #attempts]\n");
+                    "rate  [name idx airtime max_tp]  [avg(tp) avg(prob) sd(prob)]  [retry|suc|att]  [#success | #attempts]\n");
 
        for (i = 0; i < mi->n_rates; i++) {
                struct minstrel_rate *mr = &mi->r[i];
                struct minstrel_rate_stats *mrs = &mi->r[i].stats;
+               unsigned int prob_ewmsd;
 
                *(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' ';
                *(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' ';
@@ -107,17 +108,16 @@ minstrel_stats_open(struct inode *inode, struct file *file)
 
                tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
                tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma);
-               prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
                eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+               prob_ewmsd = minstrel_get_ewmsd10(mrs);
 
                p += sprintf(p, "%4u.%1u    %4u.%1u     %3u.%1u    %3u.%1u"
-                               "     %3u.%1u %3u   %3u %-3u   "
+                               "     %3u   %3u %-3u   "
                                "%9llu   %-9llu\n",
                                tp_max / 10, tp_max % 10,
                                tp_avg / 10, tp_avg % 10,
                                eprob / 10, eprob % 10,
-                               mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
-                               prob / 10, prob % 10,
+                               prob_ewmsd / 10, prob_ewmsd % 10,
                                mrs->retry_count,
                                mrs->last_success,
                                mrs->last_attempts,
@@ -148,7 +148,7 @@ minstrel_stats_csv_open(struct inode *inode, struct file *file)
 {
        struct minstrel_sta_info *mi = inode->i_private;
        struct minstrel_debugfs_info *ms;
-       unsigned int i, tp_max, tp_avg, prob, eprob;
+       unsigned int i, tp_max, tp_avg, eprob;
        char *p;
 
        ms = kmalloc(2048, GFP_KERNEL);
@@ -161,6 +161,7 @@ minstrel_stats_csv_open(struct inode *inode, struct file *file)
        for (i = 0; i < mi->n_rates; i++) {
                struct minstrel_rate *mr = &mi->r[i];
                struct minstrel_rate_stats *mrs = &mi->r[i].stats;
+               unsigned int prob_ewmsd;
 
                p += sprintf(p, "%s" ,((i == mi->max_tp_rate[0]) ? "A" : ""));
                p += sprintf(p, "%s" ,((i == mi->max_tp_rate[1]) ? "B" : ""));
@@ -175,16 +176,15 @@ minstrel_stats_csv_open(struct inode *inode, struct file *file)
 
                tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
                tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma);
-               prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
                eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+               prob_ewmsd = minstrel_get_ewmsd10(mrs);
 
-               p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u,"
+               p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u,"
                                "%llu,%llu,%d,%d\n",
                                tp_max / 10, tp_max % 10,
                                tp_avg / 10, tp_avg % 10,
                                eprob / 10, eprob % 10,
-                               mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
-                               prob / 10, prob % 10,
+                               prob_ewmsd / 10, prob_ewmsd % 10,
                                mrs->retry_count,
                                mrs->last_success,
                                mrs->last_attempts,
index 30fbabf4bcbc16aeb93e9c673d190cd3b615988b..8e783e197e935cd8bcd0b7b7d5e88b8d2c9210cc 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/ieee80211.h>
 #include <net/mac80211.h>
 #include "rate.h"
+#include "sta_info.h"
 #include "rc80211_minstrel.h"
 #include "rc80211_minstrel_ht.h"
 
@@ -154,67 +155,47 @@ MODULE_PARM_DESC(minstrel_vht_only,
 const struct mcs_group minstrel_mcs_groups[] = {
        MCS_GROUP(1, 0, BW_20),
        MCS_GROUP(2, 0, BW_20),
-#if MINSTREL_MAX_STREAMS >= 3
        MCS_GROUP(3, 0, BW_20),
-#endif
 
        MCS_GROUP(1, 1, BW_20),
        MCS_GROUP(2, 1, BW_20),
-#if MINSTREL_MAX_STREAMS >= 3
        MCS_GROUP(3, 1, BW_20),
-#endif
 
        MCS_GROUP(1, 0, BW_40),
        MCS_GROUP(2, 0, BW_40),
-#if MINSTREL_MAX_STREAMS >= 3
        MCS_GROUP(3, 0, BW_40),
-#endif
 
        MCS_GROUP(1, 1, BW_40),
        MCS_GROUP(2, 1, BW_40),
-#if MINSTREL_MAX_STREAMS >= 3
        MCS_GROUP(3, 1, BW_40),
-#endif
 
        CCK_GROUP,
 
 #ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
        VHT_GROUP(1, 0, BW_20),
        VHT_GROUP(2, 0, BW_20),
-#if MINSTREL_MAX_STREAMS >= 3
        VHT_GROUP(3, 0, BW_20),
-#endif
 
        VHT_GROUP(1, 1, BW_20),
        VHT_GROUP(2, 1, BW_20),
-#if MINSTREL_MAX_STREAMS >= 3
        VHT_GROUP(3, 1, BW_20),
-#endif
 
        VHT_GROUP(1, 0, BW_40),
        VHT_GROUP(2, 0, BW_40),
-#if MINSTREL_MAX_STREAMS >= 3
        VHT_GROUP(3, 0, BW_40),
-#endif
 
        VHT_GROUP(1, 1, BW_40),
        VHT_GROUP(2, 1, BW_40),
-#if MINSTREL_MAX_STREAMS >= 3
        VHT_GROUP(3, 1, BW_40),
-#endif
 
        VHT_GROUP(1, 0, BW_80),
        VHT_GROUP(2, 0, BW_80),
-#if MINSTREL_MAX_STREAMS >= 3
        VHT_GROUP(3, 0, BW_80),
-#endif
 
        VHT_GROUP(1, 1, BW_80),
        VHT_GROUP(2, 1, BW_80),
-#if MINSTREL_MAX_STREAMS >= 3
        VHT_GROUP(3, 1, BW_80),
 #endif
-#endif
 };
 
 static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;
@@ -301,7 +282,7 @@ minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
                                break;
 
                /* short preamble */
-               if (!(mi->groups[group].supported & BIT(idx)))
+               if (!(mi->supported[group] & BIT(idx)))
                        idx += 4;
        }
        return &mi->groups[group].rates[idx];
@@ -486,7 +467,7 @@ minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
                          MCS_GROUP_RATES].streams;
        for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
                mg = &mi->groups[group];
-               if (!mg->supported || group == MINSTREL_CCK_GROUP)
+               if (!mi->supported[group] || group == MINSTREL_CCK_GROUP)
                        continue;
 
                tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES;
@@ -540,7 +521,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
        for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
 
                mg = &mi->groups[group];
-               if (!mg->supported)
+               if (!mi->supported[group])
                        continue;
 
                mi->sample_count++;
@@ -550,7 +531,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
                        tmp_group_tp_rate[j] = group;
 
                for (i = 0; i < MCS_GROUP_RATES; i++) {
-                       if (!(mg->supported & BIT(i)))
+                       if (!(mi->supported[group] & BIT(i)))
                                continue;
 
                        index = MCS_GROUP_RATES * group + i;
@@ -636,7 +617,7 @@ minstrel_set_next_sample_idx(struct minstrel_ht_sta *mi)
                mi->sample_group %= ARRAY_SIZE(minstrel_mcs_groups);
                mg = &mi->groups[mi->sample_group];
 
-               if (!mg->supported)
+               if (!mi->supported[mi->sample_group])
                        continue;
 
                if (++mg->index >= MCS_GROUP_RATES) {
@@ -657,7 +638,7 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
        while (group > 0) {
                group--;
 
-               if (!mi->groups[group].supported)
+               if (!mi->supported[group])
                        continue;
 
                if (minstrel_mcs_groups[group].streams >
@@ -994,7 +975,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
        sample_idx = sample_table[mg->column][mg->index];
        minstrel_set_next_sample_idx(mi);
 
-       if (!(mg->supported & BIT(sample_idx)))
+       if (!(mi->supported[sample_group] & BIT(sample_idx)))
                return -1;
 
        mrs = &mg->rates[sample_idx];
@@ -1048,22 +1029,6 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
        return sample_idx;
 }
 
-static void
-minstrel_ht_check_cck_shortpreamble(struct minstrel_priv *mp,
-                                   struct minstrel_ht_sta *mi, bool val)
-{
-       u8 supported = mi->groups[MINSTREL_CCK_GROUP].supported;
-
-       if (!supported || !mi->cck_supported_short)
-               return;
-
-       if (supported & (mi->cck_supported_short << (val * 4)))
-               return;
-
-       supported ^= mi->cck_supported_short | (mi->cck_supported_short << 4);
-       mi->groups[MINSTREL_CCK_GROUP].supported = supported;
-}
-
 static void
 minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
                      struct ieee80211_tx_rate_control *txrc)
@@ -1087,7 +1052,6 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
                minstrel_aggr_check(sta, txrc->skb);
 
        info->flags |= mi->tx_flags;
-       minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);
 
 #ifdef CONFIG_MAC80211_DEBUGFS
        if (mp->fixed_rate_idx != -1)
@@ -1154,7 +1118,7 @@ minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
                        mi->cck_supported_short |= BIT(i);
        }
 
-       mi->groups[MINSTREL_CCK_GROUP].supported = mi->cck_supported;
+       mi->supported[MINSTREL_CCK_GROUP] = mi->cck_supported;
 }
 
 static void
@@ -1168,6 +1132,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
        struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
        u16 sta_cap = sta->ht_cap.cap;
        struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+       struct sta_info *sinfo = container_of(sta, struct sta_info, sta);
        int use_vht;
        int n_supported = 0;
        int ack_dur;
@@ -1224,7 +1189,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
                u32 gflags = minstrel_mcs_groups[i].flags;
                int bw, nss;
 
-               mi->groups[i].supported = 0;
+               mi->supported[i] = 0;
                if (i == MINSTREL_CCK_GROUP) {
                        minstrel_ht_update_cck(mp, mi, sband, sta);
                        continue;
@@ -1256,8 +1221,8 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
                        if (use_vht && minstrel_vht_only)
                                continue;
 #endif
-                       mi->groups[i].supported = mcs->rx_mask[nss - 1];
-                       if (mi->groups[i].supported)
+                       mi->supported[i] = mcs->rx_mask[nss - 1];
+                       if (mi->supported[i])
                                n_supported++;
                        continue;
                }
@@ -1283,16 +1248,19 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
                else
                        bw = BW_20;
 
-               mi->groups[i].supported = minstrel_get_valid_vht_rates(bw, nss,
+               mi->supported[i] = minstrel_get_valid_vht_rates(bw, nss,
                                vht_cap->vht_mcs.tx_mcs_map);
 
-               if (mi->groups[i].supported)
+               if (mi->supported[i])
                        n_supported++;
        }
 
        if (!n_supported)
                goto use_legacy;
 
+       if (test_sta_flag(sinfo, WLAN_STA_SHORT_PREAMBLE))
+               mi->cck_supported_short |= mi->cck_supported_short << 4;
+
        /* create an initial rate table with the lowest supported rates */
        minstrel_ht_update_stats(mp, mi);
        minstrel_ht_update_rates(mp, mi);
index e8b52a94d24b64fd3e78b4d3a390c3dbdad9e328..de1646c42e82df72f883f47daa8b2a696746e7f0 100644 (file)
@@ -52,9 +52,6 @@ struct minstrel_mcs_group_data {
        u8 index;
        u8 column;
 
-       /* bitfield of supported MCS rates of this group */
-       u16 supported;
-
        /* sorted rate set within a MCS group*/
        u16 max_group_tp_rate[MAX_THR_RATES];
        u16 max_group_prob_rate;
@@ -101,6 +98,9 @@ struct minstrel_ht_sta {
        u8 cck_supported;
        u8 cck_supported_short;
 
+       /* Bitfield of supported MCS rates of all groups */
+       u16 supported[MINSTREL_GROUPS_NB];
+
        /* MCS rate group info and statistics */
        struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB];
 };
index 5320e35ed3d0f2dc899176b9e569b59877de9e38..7d969e300fb3a48742e841eb0140aba03b5de071 100644 (file)
@@ -19,12 +19,12 @@ static char *
 minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
 {
        const struct mcs_group *mg;
-       unsigned int j, tp_max, tp_avg, prob, eprob, tx_time;
+       unsigned int j, tp_max, tp_avg, eprob, tx_time;
        char htmode = '2';
        char gimode = 'L';
        u32 gflags;
 
-       if (!mi->groups[i].supported)
+       if (!mi->supported[i])
                return p;
 
        mg = &minstrel_mcs_groups[i];
@@ -41,8 +41,9 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
                struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
                static const int bitrates[4] = { 10, 20, 55, 110 };
                int idx = i * MCS_GROUP_RATES + j;
+               unsigned int prob_ewmsd;
 
-               if (!(mi->groups[i].supported & BIT(j)))
+               if (!(mi->supported[i] & BIT(j)))
                        continue;
 
                if (gflags & IEEE80211_TX_RC_MCS) {
@@ -83,17 +84,16 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
 
                tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
                tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma);
-               prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
                eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+               prob_ewmsd = minstrel_get_ewmsd10(mrs);
 
                p += sprintf(p, "%4u.%1u    %4u.%1u     %3u.%1u    %3u.%1u"
-                               "     %3u.%1u %3u   %3u %-3u   "
+                               "     %3u   %3u %-3u   "
                                "%9llu   %-9llu\n",
                                tp_max / 10, tp_max % 10,
                                tp_avg / 10, tp_avg % 10,
                                eprob / 10, eprob % 10,
-                               mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
-                               prob / 10, prob % 10,
+                               prob_ewmsd / 10, prob_ewmsd % 10,
                                mrs->retry_count,
                                mrs->last_success,
                                mrs->last_attempts,
@@ -130,9 +130,9 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
 
        p += sprintf(p, "\n");
        p += sprintf(p,
-                    "              best   ____________rate__________    ________statistics________    ________last_______    ______sum-of________\n");
+                    "              best   ____________rate__________    ________statistics________    _____last____    ______sum-of________\n");
        p += sprintf(p,
-                    "mode guard #  rate  [name   idx airtime  max_tp]  [avg(tp) avg(prob) sd(prob)]  [prob.|retry|suc|att]  [#success | #attempts]\n");
+                    "mode guard #  rate  [name   idx airtime  max_tp]  [avg(tp) avg(prob) sd(prob)]  [retry|suc|att]  [#success | #attempts]\n");
 
        p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p);
        for (i = 0; i < MINSTREL_CCK_GROUP; i++)
@@ -165,12 +165,12 @@ static char *
 minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
 {
        const struct mcs_group *mg;
-       unsigned int j, tp_max, tp_avg, prob, eprob, tx_time;
+       unsigned int j, tp_max, tp_avg, eprob, tx_time;
        char htmode = '2';
        char gimode = 'L';
        u32 gflags;
 
-       if (!mi->groups[i].supported)
+       if (!mi->supported[i])
                return p;
 
        mg = &minstrel_mcs_groups[i];
@@ -187,8 +187,9 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
                struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
                static const int bitrates[4] = { 10, 20, 55, 110 };
                int idx = i * MCS_GROUP_RATES + j;
+               unsigned int prob_ewmsd;
 
-               if (!(mi->groups[i].supported & BIT(j)))
+               if (!(mi->supported[i] & BIT(j)))
                        continue;
 
                if (gflags & IEEE80211_TX_RC_MCS) {
@@ -226,16 +227,15 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
 
                tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
                tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma);
-               prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
                eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+               prob_ewmsd = minstrel_get_ewmsd10(mrs);
 
-               p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,"
+               p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,"
                                "%u,%llu,%llu,",
                                tp_max / 10, tp_max % 10,
                                tp_avg / 10, tp_avg % 10,
                                eprob / 10, eprob % 10,
-                               mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
-                               prob / 10, prob % 10,
+                               prob_ewmsd / 10, prob_ewmsd % 10,
                                mrs->retry_count,
                                mrs->last_success,
                                mrs->last_attempts,
index 3e289a64ed4317a8a495cb7cac8197fb9ce10c3e..bfa5f4df9d4bdfd35a00edd0c2f8c8ff1e87478d 100644 (file)
@@ -1908,7 +1908,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
        unsigned int frag, seq;
        struct ieee80211_fragment_entry *entry;
        struct sk_buff *skb;
-       struct ieee80211_rx_status *status;
 
        hdr = (struct ieee80211_hdr *)rx->skb->data;
        fc = hdr->frame_control;
@@ -2034,9 +2033,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
                dev_kfree_skb(skb);
        }
 
-       /* Complete frame has been reassembled - process it now */
-       status = IEEE80211_SKB_RXCB(rx->skb);
-
  out:
        ieee80211_led_rx(rx->local);
  out_no_led:
index 23d8ac8292796714df893e32072e44edb3617ca0..faab3c490d2b755f32ee09638eaf0d45067780cd 100644 (file)
@@ -1120,7 +1120,6 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
        u32 rate_masks[NUM_NL80211_BANDS] = {};
        u8 bands_used = 0;
        u8 *ie;
-       size_t len;
 
        iebufsz = local->scan_ies_len + req->ie_len;
 
@@ -1145,10 +1144,9 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
 
-       len = ieee80211_build_preq_ies(local, ie, num_bands * iebufsz,
-                                      &sched_scan_ies, req->ie,
-                                      req->ie_len, bands_used,
-                                      rate_masks, &chandef);
+       ieee80211_build_preq_ies(local, ie, num_bands * iebufsz,
+                                &sched_scan_ies, req->ie,
+                                req->ie_len, bands_used, rate_masks, &chandef);
 
        ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
        if (ret == 0) {
index b6cfcf038c11fa529e00da2eed70f6ff48426a37..d889841b1d2319d9d3704572ec9ef0d2fceef24d 100644 (file)
@@ -513,23 +513,23 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
 {
        struct ieee80211_local *local = sta->local;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
-       struct station_info *sinfo;
+       struct station_info *sinfo = NULL;
        int err = 0;
 
        lockdep_assert_held(&local->sta_mtx);
 
-       sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
-       if (!sinfo) {
-               err = -ENOMEM;
-               goto out_err;
-       }
-
        /* check if STA exists already */
        if (sta_info_get_bss(sdata, sta->sta.addr)) {
                err = -EEXIST;
                goto out_err;
        }
 
+       sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
+       if (!sinfo) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
        local->num_sta++;
        local->sta_generation++;
        smp_mb();
@@ -2051,16 +2051,12 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
-       struct rate_control_ref *ref = NULL;
        u32 thr = 0;
        int i, ac, cpu;
        struct ieee80211_sta_rx_stats *last_rxstats;
 
        last_rxstats = sta_get_last_rx_stats(sta);
 
-       if (test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
-               ref = local->rate_ctrl;
-
        sinfo->generation = sdata->local->sta_generation;
 
        /* do before driver, so beacon filtering drivers have a
index ddf71c648cab008baaff8ed430b9b222410f8ce1..d6a1bfaa7a817f0511fca16262f2c132ff5432f2 100644 (file)
@@ -541,6 +541,11 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
        } else if (info->ack_frame_id) {
                ieee80211_report_ack_skb(local, info, acked, dropped);
        }
+
+       if (!dropped && skb->destructor) {
+               skb->wifi_acked_valid = 1;
+               skb->wifi_acked = acked;
+       }
 }
 
 /*
@@ -633,10 +638,9 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_supported_band *sband;
        int retry_count;
-       int rates_idx;
        bool acked, noack_success;
 
-       rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+       ieee80211_tx_get_rates(hw, info, &retry_count);
 
        sband = hw->wiphy->bands[info->band];
 
index 0d8b716e509edca48fbc65ceaf06e6b7d6a2c189..3182e0c4e157c1066935786391e52f55abcd4dbd 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/skbuff.h>
+#include <linux/if_vlan.h>
 #include <linux/etherdevice.h>
 #include <linux/bitmap.h>
 #include <linux/rcupdate.h>
@@ -63,6 +64,10 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
        struct ieee80211_chanctx_conf *chanctx_conf;
        u32 rate_flags = 0;
 
+       /* assume HW handles this */
+       if (tx->rate.flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))
+               return 0;
+
        rcu_read_lock();
        chanctx_conf = rcu_dereference(tx->sdata->vif.chanctx_conf);
        if (chanctx_conf) {
@@ -71,10 +76,6 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
        }
        rcu_read_unlock();
 
-       /* assume HW handles this */
-       if (tx->rate.flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))
-               return 0;
-
        /* uh huh? */
        if (WARN_ON_ONCE(tx->rate.idx < 0))
                return 0;
@@ -3574,6 +3575,115 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
        rcu_read_unlock();
 }
 
+static int ieee80211_change_da(struct sk_buff *skb, struct sta_info *sta)
+{
+       struct ethhdr *eth;
+       int err;
+
+       err = skb_ensure_writable(skb, ETH_HLEN);
+       if (unlikely(err))
+               return err;
+
+       eth = (void *)skb->data;
+       ether_addr_copy(eth->h_dest, sta->sta.addr);
+
+       return 0;
+}
+
+static bool ieee80211_multicast_to_unicast(struct sk_buff *skb,
+                                          struct net_device *dev)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       const struct ethhdr *eth = (void *)skb->data;
+       const struct vlan_ethhdr *ethvlan = (void *)skb->data;
+       __be16 ethertype;
+
+       if (likely(!is_multicast_ether_addr(eth->h_dest)))
+               return false;
+
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_AP_VLAN:
+               if (sdata->u.vlan.sta)
+                       return false;
+               if (sdata->wdev.use_4addr)
+                       return false;
+               /* fall through */
+       case NL80211_IFTYPE_AP:
+               /* check runtime toggle for this bss */
+               if (!sdata->bss->multicast_to_unicast)
+                       return false;
+               break;
+       default:
+               return false;
+       }
+
+       /* multicast to unicast conversion only for some payload */
+       ethertype = eth->h_proto;
+       if (ethertype == htons(ETH_P_8021Q) && skb->len >= VLAN_ETH_HLEN)
+               ethertype = ethvlan->h_vlan_encapsulated_proto;
+       switch (ethertype) {
+       case htons(ETH_P_ARP):
+       case htons(ETH_P_IP):
+       case htons(ETH_P_IPV6):
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+static void
+ieee80211_convert_to_unicast(struct sk_buff *skb, struct net_device *dev,
+                            struct sk_buff_head *queue)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       const struct ethhdr *eth = (struct ethhdr *)skb->data;
+       struct sta_info *sta, *first = NULL;
+       struct sk_buff *cloned_skb;
+
+       rcu_read_lock();
+
+       list_for_each_entry_rcu(sta, &local->sta_list, list) {
+               if (sdata != sta->sdata)
+                       /* AP-VLAN mismatch */
+                       continue;
+               if (unlikely(ether_addr_equal(eth->h_source, sta->sta.addr)))
+                       /* do not send back to source */
+                       continue;
+               if (!first) {
+                       first = sta;
+                       continue;
+               }
+               cloned_skb = skb_clone(skb, GFP_ATOMIC);
+               if (!cloned_skb)
+                       goto multicast;
+               if (unlikely(ieee80211_change_da(cloned_skb, sta))) {
+                       dev_kfree_skb(cloned_skb);
+                       goto multicast;
+               }
+               __skb_queue_tail(queue, cloned_skb);
+       }
+
+       if (likely(first)) {
+               if (unlikely(ieee80211_change_da(skb, first)))
+                       goto multicast;
+               __skb_queue_tail(queue, skb);
+       } else {
+               /* no STA connected, drop */
+               kfree_skb(skb);
+               skb = NULL;
+       }
+
+       goto out;
+multicast:
+       __skb_queue_purge(queue);
+       __skb_queue_tail(queue, skb);
+out:
+       rcu_read_unlock();
+}
+
 /**
  * ieee80211_subif_start_xmit - netif start_xmit function for 802.3 vifs
  * @skb: packet to be sent
@@ -3584,7 +3694,17 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
 netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                                       struct net_device *dev)
 {
-       __ieee80211_subif_start_xmit(skb, dev, 0);
+       if (unlikely(ieee80211_multicast_to_unicast(skb, dev))) {
+               struct sk_buff_head queue;
+
+               __skb_queue_head_init(&queue);
+               ieee80211_convert_to_unicast(skb, dev, &queue);
+               while ((skb = __skb_dequeue(&queue)))
+                       __ieee80211_subif_start_xmit(skb, dev, 0);
+       } else {
+               __ieee80211_subif_start_xmit(skb, dev, 0);
+       }
+
        return NETDEV_TX_OK;
 }
 
@@ -4077,7 +4197,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
                }
 
                if (ifmsh->sync_ops)
-                       ifmsh->sync_ops->adjust_tbtt(sdata, beacon);
+                       ifmsh->sync_ops->adjust_tsf(sdata, beacon);
 
                skb = dev_alloc_skb(local->tx_headroom +
                                    beacon->head_len +
index 6832bf6ab69fe012ea4eeb3c02b79523083cdc58..d5a56e039106c2fd7073fcffd74d93bbbbc033e7 100644 (file)
@@ -436,14 +436,10 @@ u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
                                  struct sta_info *sta, u8 opmode,
                                  enum nl80211_band band)
 {
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_supported_band *sband;
        enum ieee80211_sta_rx_bandwidth new_bw;
        u32 changed = 0;
        u8 nss;
 
-       sband = local->hw.wiphy->bands[band];
-
        /* ignore - no support for BF yet */
        if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)
                return 0;
index efa3f48f1ec5d51ea7191c8ae90dda77c0d330e1..73e8f347802ecadf5efb11fffa7b13d32a5705c1 100644 (file)
@@ -293,7 +293,8 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
                        return RX_DROP_UNUSABLE;
                ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
                /* remove ICV */
-               if (pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN))
+               if (!(status->flag & RX_FLAG_ICV_STRIPPED) &&
+                   pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN))
                        return RX_DROP_UNUSABLE;
        }
 
index 8af6dd388d1195540fdaf66f7ac55a452620ad5f..c1ef22df865fe77bf7cf0a42d560f54db98a3edd 100644 (file)
@@ -294,7 +294,8 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
                return RX_DROP_UNUSABLE;
 
        /* Trim ICV */
-       skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
+       if (!(status->flag & RX_FLAG_ICV_STRIPPED))
+               skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
 
        /* Remove IV */
        memmove(skb->data + IEEE80211_TKIP_IV_LEN, skb->data, hdrlen);
index 884027f62783a6a975b42c0bdd5f39010df9834a..2064c3a35ef84d4c54c065e8b54d524cdb407736 100644 (file)
@@ -176,6 +176,50 @@ static void rfkill_led_trigger_unregister(struct rfkill *rfkill)
 {
        led_trigger_unregister(&rfkill->led_trigger);
 }
+
+static struct led_trigger rfkill_any_led_trigger;
+static struct work_struct rfkill_any_work;
+
+static void rfkill_any_led_trigger_worker(struct work_struct *work)
+{
+       enum led_brightness brightness = LED_OFF;
+       struct rfkill *rfkill;
+
+       mutex_lock(&rfkill_global_mutex);
+       list_for_each_entry(rfkill, &rfkill_list, node) {
+               if (!(rfkill->state & RFKILL_BLOCK_ANY)) {
+                       brightness = LED_FULL;
+                       break;
+               }
+       }
+       mutex_unlock(&rfkill_global_mutex);
+
+       led_trigger_event(&rfkill_any_led_trigger, brightness);
+}
+
+static void rfkill_any_led_trigger_event(void)
+{
+       schedule_work(&rfkill_any_work);
+}
+
+static void rfkill_any_led_trigger_activate(struct led_classdev *led_cdev)
+{
+       rfkill_any_led_trigger_event();
+}
+
+static int rfkill_any_led_trigger_register(void)
+{
+       INIT_WORK(&rfkill_any_work, rfkill_any_led_trigger_worker);
+       rfkill_any_led_trigger.name = "rfkill-any";
+       rfkill_any_led_trigger.activate = rfkill_any_led_trigger_activate;
+       return led_trigger_register(&rfkill_any_led_trigger);
+}
+
+static void rfkill_any_led_trigger_unregister(void)
+{
+       led_trigger_unregister(&rfkill_any_led_trigger);
+       cancel_work_sync(&rfkill_any_work);
+}
 #else
 static void rfkill_led_trigger_event(struct rfkill *rfkill)
 {
@@ -189,6 +233,19 @@ static inline int rfkill_led_trigger_register(struct rfkill *rfkill)
 static inline void rfkill_led_trigger_unregister(struct rfkill *rfkill)
 {
 }
+
+static void rfkill_any_led_trigger_event(void)
+{
+}
+
+static int rfkill_any_led_trigger_register(void)
+{
+       return 0;
+}
+
+static void rfkill_any_led_trigger_unregister(void)
+{
+}
 #endif /* CONFIG_RFKILL_LEDS */
 
 static void rfkill_fill_event(struct rfkill_event *ev, struct rfkill *rfkill,
@@ -297,6 +354,7 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
        spin_unlock_irqrestore(&rfkill->lock, flags);
 
        rfkill_led_trigger_event(rfkill);
+       rfkill_any_led_trigger_event();
 
        if (prev != curr)
                rfkill_event(rfkill);
@@ -477,11 +535,9 @@ bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
        spin_unlock_irqrestore(&rfkill->lock, flags);
 
        rfkill_led_trigger_event(rfkill);
+       rfkill_any_led_trigger_event();
 
-       if (!rfkill->registered)
-               return ret;
-
-       if (prev != blocked)
+       if (rfkill->registered && prev != blocked)
                schedule_work(&rfkill->uevent_work);
 
        return ret;
@@ -523,6 +579,7 @@ bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
                schedule_work(&rfkill->uevent_work);
 
        rfkill_led_trigger_event(rfkill);
+       rfkill_any_led_trigger_event();
 
        return blocked;
 }
@@ -572,6 +629,7 @@ void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
                        schedule_work(&rfkill->uevent_work);
 
                rfkill_led_trigger_event(rfkill);
+               rfkill_any_led_trigger_event();
        }
 }
 EXPORT_SYMBOL(rfkill_set_states);
@@ -988,6 +1046,7 @@ int __must_check rfkill_register(struct rfkill *rfkill)
 #endif
        }
 
+       rfkill_any_led_trigger_event();
        rfkill_send_events(rfkill, RFKILL_OP_ADD);
 
        mutex_unlock(&rfkill_global_mutex);
@@ -1020,6 +1079,7 @@ void rfkill_unregister(struct rfkill *rfkill)
        mutex_lock(&rfkill_global_mutex);
        rfkill_send_events(rfkill, RFKILL_OP_DEL);
        list_del_init(&rfkill->node);
+       rfkill_any_led_trigger_event();
        mutex_unlock(&rfkill_global_mutex);
 
        rfkill_led_trigger_unregister(rfkill);
@@ -1266,24 +1326,33 @@ static int __init rfkill_init(void)
 
        error = class_register(&rfkill_class);
        if (error)
-               goto out;
+               goto error_class;
 
        error = misc_register(&rfkill_miscdev);
-       if (error) {
-               class_unregister(&rfkill_class);
-               goto out;
-       }
+       if (error)
+               goto error_misc;
+
+       error = rfkill_any_led_trigger_register();
+       if (error)
+               goto error_led_trigger;
 
 #ifdef CONFIG_RFKILL_INPUT
        error = rfkill_handler_init();
-       if (error) {
-               misc_deregister(&rfkill_miscdev);
-               class_unregister(&rfkill_class);
-               goto out;
-       }
+       if (error)
+               goto error_input;
 #endif
 
- out:
+       return 0;
+
+#ifdef CONFIG_RFKILL_INPUT
+error_input:
+       rfkill_any_led_trigger_unregister();
+#endif
+error_led_trigger:
+       misc_deregister(&rfkill_miscdev);
+error_misc:
+       class_unregister(&rfkill_class);
+error_class:
        return error;
 }
 subsys_initcall(rfkill_init);
@@ -1293,6 +1362,7 @@ static void __exit rfkill_exit(void)
 #ifdef CONFIG_RFKILL_INPUT
        rfkill_handler_exit();
 #endif
+       rfkill_any_led_trigger_unregister();
        misc_deregister(&rfkill_miscdev);
        class_unregister(&rfkill_class);
 }
index 816c9331c8d2dbef32b99e0355bc68c0d15999ea..d06e5015751a4ea94dfa51fe437aa8c38f59ceaf 100644 (file)
@@ -11,6 +11,7 @@ obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
 
 cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
 cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o
+cfg80211-$(CONFIG_OF) += of.o
 cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
 cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
 cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
index 158c59ecf90a35a6af8ca353c7583e0bd0cda807..903fc419217a0df764c68abc346396f3b069f54e 100644 (file)
@@ -1142,6 +1142,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                     wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
                        dev->priv_flags |= IFF_DONT_BRIDGE;
 
+               INIT_WORK(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
+
                nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
                break;
        case NETDEV_GOING_DOWN:
@@ -1230,6 +1232,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
 #ifdef CONFIG_CFG80211_WEXT
                        kzfree(wdev->wext.keys);
 #endif
+                       flush_work(&wdev->disconnect_wk);
                }
                /*
                 * synchronise (so that we won't find this netdev
index af6e023020b14a09733b84560e50269317aa2537..58ca206982feafa7ddfe2c90d4483791e5057cf0 100644 (file)
@@ -228,6 +228,7 @@ struct cfg80211_event {
                        size_t resp_ie_len;
                        struct cfg80211_bss *bss;
                        int status; /* -1 = failed; 0..65535 = status code */
+                       enum nl80211_timeout_reason timeout_reason;
                } cr;
                struct {
                        const u8 *req_ie;
@@ -388,7 +389,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                               const u8 *req_ie, size_t req_ie_len,
                               const u8 *resp_ie, size_t resp_ie_len,
                               int status, bool wextev,
-                              struct cfg80211_bss *bss);
+                              struct cfg80211_bss *bss,
+                              enum nl80211_timeout_reason timeout_reason);
 void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
                             size_t ie_len, u16 reason, bool from_ap);
 int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
@@ -400,6 +402,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
                       const u8 *resp_ie, size_t resp_ie_len);
 int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
                              struct wireless_dev *wdev);
+void cfg80211_autodisconnect_wk(struct work_struct *work);
 
 /* SME implementation */
 void cfg80211_conn_work(struct work_struct *work);
@@ -430,6 +433,9 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
 void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
 void cfg80211_process_wdev_events(struct wireless_dev *wdev);
 
+bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range,
+                               u32 center_freq_khz, u32 bw_khz);
+
 /**
  * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
  * @wiphy: the wiphy to validate against
index 4646cf5695b958fc461a6b884046bb944f9f13cf..22b3d999006559d7572287c2b8e6f4fad286766d 100644 (file)
@@ -48,7 +48,8 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
        /* update current_bss etc., consumes the bss reference */
        __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
                                  status_code,
-                                 status_code == WLAN_STATUS_SUCCESS, bss);
+                                 status_code == WLAN_STATUS_SUCCESS, bss,
+                                 NL80211_TIMEOUT_UNSPECIFIED);
 }
 EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
 
@@ -345,6 +346,11 @@ int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
             !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
                return 0;
 
+       if (ether_addr_equal(wdev->disconnect_bssid, bssid) ||
+           (wdev->current_bss &&
+            ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
+               wdev->conn_owner_nlportid = 0;
+
        return rdev_deauth(rdev, dev, &req);
 }
 
@@ -657,8 +663,25 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
                        return err;
        }
 
-       if (!ether_addr_equal(mgmt->sa, wdev_address(wdev)))
-               return -EINVAL;
+       if (!ether_addr_equal(mgmt->sa, wdev_address(wdev))) {
+               /* Allow random TA to be used with Public Action frames if the
+                * driver has indicated support for this. Otherwise, only allow
+                * the local address to be used.
+                */
+               if (!ieee80211_is_action(mgmt->frame_control) ||
+                   mgmt->u.action.category != WLAN_CATEGORY_PUBLIC)
+                       return -EINVAL;
+               if (!wdev->current_bss &&
+                   !wiphy_ext_feature_isset(
+                           &rdev->wiphy,
+                           NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA))
+                       return -EINVAL;
+               if (wdev->current_bss &&
+                   !wiphy_ext_feature_isset(
+                           &rdev->wiphy,
+                           NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED))
+                       return -EINVAL;
+       }
 
        /* Transmit the Action frame as requested by user space */
        return rdev_mgmt_tx(rdev, wdev, params, cookie);
index ef5eff93a8b817dda5fa41bebf4fe4963663a9d6..bed816cfd7543bde13540d3a96de1ad68683941c 100644 (file)
@@ -405,6 +405,11 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_FILS_NONCES] = { .len = 2 * FILS_NONCE_LEN },
        [NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED] = { .type = NLA_FLAG, },
        [NL80211_ATTR_BSSID] = { .len = ETH_ALEN },
+       [NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] = { .type = NLA_S8 },
+       [NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST] = {
+               .len = sizeof(struct nl80211_bss_select_rssi_adjust)
+       },
+       [NL80211_ATTR_TIMEOUT_REASON] = { .type = NLA_U32 },
 };
 
 /* policy for the key attributes */
@@ -6775,13 +6780,10 @@ nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans,
 
                /*
                 * If scan plans are not specified,
-                * %NL80211_ATTR_SCHED_SCAN_INTERVAL must be specified. In this
+                * %NL80211_ATTR_SCHED_SCAN_INTERVAL will be specified. In this
                 * case one scan plan will be set with the specified scan
                 * interval and infinite number of iterations.
                 */
-               if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
-                       return -EINVAL;
-
                interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
                if (!interval)
                        return -EINVAL;
@@ -6953,6 +6955,12 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
        if (!n_plans || n_plans > wiphy->max_sched_scan_plans)
                return ERR_PTR(-EINVAL);
 
+       if (!wiphy_ext_feature_isset(
+                   wiphy, NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI) &&
+           (attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] ||
+            attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]))
+               return ERR_PTR(-EINVAL);
+
        request = kzalloc(sizeof(*request)
                        + sizeof(*request->ssids) * n_ssids
                        + sizeof(*request->match_sets) * n_match_sets
@@ -7159,6 +7167,26 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
                request->delay =
                        nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_DELAY]);
 
+       if (attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI]) {
+               request->relative_rssi = nla_get_s8(
+                       attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI]);
+               request->relative_rssi_set = true;
+       }
+
+       if (request->relative_rssi_set &&
+           attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]) {
+               struct nl80211_bss_select_rssi_adjust *rssi_adjust;
+
+               rssi_adjust = nla_data(
+                       attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]);
+               request->rssi_adjust.band = rssi_adjust->band;
+               request->rssi_adjust.delta = rssi_adjust->delta;
+               if (!is_band_valid(wiphy, request->rssi_adjust.band)) {
+                       err = -EINVAL;
+                       goto out_free;
+               }
+       }
+
        err = nl80211_parse_sched_scan_plans(wiphy, n_plans, request, attrs);
        if (err)
                goto out_free;
@@ -8053,8 +8081,17 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
        err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
        if (!err) {
                wdev_lock(dev->ieee80211_ptr);
+
                err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
                                          ssid, ssid_len, &req);
+
+               if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
+                       dev->ieee80211_ptr->conn_owner_nlportid =
+                               info->snd_portid;
+                       memcpy(dev->ieee80211_ptr->disconnect_bssid,
+                              bssid, ETH_ALEN);
+               }
+
                wdev_unlock(dev->ieee80211_ptr);
        }
 
@@ -8773,11 +8810,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
        }
 
        wdev_lock(dev->ieee80211_ptr);
+
        err = cfg80211_connect(rdev, dev, &connect, connkeys,
                               connect.prev_bssid);
-       wdev_unlock(dev->ieee80211_ptr);
        if (err)
                kzfree(connkeys);
+
+       if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
+               dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid;
+               if (connect.bssid)
+                       memcpy(dev->ieee80211_ptr->disconnect_bssid,
+                              connect.bssid, ETH_ALEN);
+               else
+                       memset(dev->ieee80211_ptr->disconnect_bssid,
+                              0, ETH_ALEN);
+       }
+
+       wdev_unlock(dev->ieee80211_ptr);
+
        return err;
 }
 
@@ -9673,6 +9723,20 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
        if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY, req->delay))
                return -ENOBUFS;
 
+       if (req->relative_rssi_set) {
+               struct nl80211_bss_select_rssi_adjust rssi_adjust;
+
+               if (nla_put_s8(msg, NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
+                              req->relative_rssi))
+                       return -ENOBUFS;
+
+               rssi_adjust.band = req->rssi_adjust.band;
+               rssi_adjust.delta = req->rssi_adjust.delta;
+               if (nla_put(msg, NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+                           sizeof(rssi_adjust), &rssi_adjust))
+                       return -ENOBUFS;
+       }
+
        freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
        if (!freqs)
                return -ENOBUFS;
@@ -11807,9 +11871,6 @@ static int nl80211_set_multicast_to_unicast(struct sk_buff *skb,
        const struct nlattr *nla;
        bool enabled;
 
-       if (netif_running(dev))
-               return -EBUSY;
-
        if (!rdev->ops->set_multicast_to_unicast)
                return -EOPNOTSUPP;
 
@@ -12810,7 +12871,7 @@ static int nl80211_add_scan_req(struct sk_buff *msg,
        return -ENOBUFS;
 }
 
-static int nl80211_send_scan_msg(struct sk_buff *msg,
+static int nl80211_prep_scan_msg(struct sk_buff *msg,
                                 struct cfg80211_registered_device *rdev,
                                 struct wireless_dev *wdev,
                                 u32 portid, u32 seq, int flags,
@@ -12841,7 +12902,7 @@ static int nl80211_send_scan_msg(struct sk_buff *msg,
 }
 
 static int
-nl80211_send_sched_scan_msg(struct sk_buff *msg,
+nl80211_prep_sched_scan_msg(struct sk_buff *msg,
                            struct cfg80211_registered_device *rdev,
                            struct net_device *netdev,
                            u32 portid, u32 seq, int flags, u32 cmd)
@@ -12873,7 +12934,7 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
        if (!msg)
                return;
 
-       if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
+       if (nl80211_prep_scan_msg(msg, rdev, wdev, 0, 0, 0,
                                  NL80211_CMD_TRIGGER_SCAN) < 0) {
                nlmsg_free(msg);
                return;
@@ -12892,7 +12953,7 @@ struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
        if (!msg)
                return NULL;
 
-       if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
+       if (nl80211_prep_scan_msg(msg, rdev, wdev, 0, 0, 0,
                                  aborted ? NL80211_CMD_SCAN_ABORTED :
                                            NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
                nlmsg_free(msg);
@@ -12902,31 +12963,13 @@ struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
        return msg;
 }
 
-void nl80211_send_scan_result(struct cfg80211_registered_device *rdev,
-                             struct sk_buff *msg)
-{
-       if (!msg)
-               return;
-
-       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
-                               NL80211_MCGRP_SCAN, GFP_KERNEL);
-}
-
-void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
-                                    struct net_device *netdev)
+/* send message created by nl80211_build_scan_msg() */
+void nl80211_send_scan_msg(struct cfg80211_registered_device *rdev,
+                          struct sk_buff *msg)
 {
-       struct sk_buff *msg;
-
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
 
-       if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
-                                       NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
-
        genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
                                NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
@@ -12940,7 +12983,7 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
        if (!msg)
                return;
 
-       if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
+       if (nl80211_prep_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
                nlmsg_free(msg);
                return;
        }
@@ -13042,7 +13085,7 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       msg = nlmsg_new(100 + len, gfp);
        if (!msg)
                return;
 
@@ -13189,12 +13232,14 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
                                 struct net_device *netdev, const u8 *bssid,
                                 const u8 *req_ie, size_t req_ie_len,
                                 const u8 *resp_ie, size_t resp_ie_len,
-                                int status, gfp_t gfp)
+                                int status,
+                                enum nl80211_timeout_reason timeout_reason,
+                                gfp_t gfp)
 {
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       msg = nlmsg_new(100 + req_ie_len + resp_ie_len, gfp);
        if (!msg)
                return;
 
@@ -13210,7 +13255,9 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
            nla_put_u16(msg, NL80211_ATTR_STATUS_CODE,
                        status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE :
                        status) ||
-           (status < 0 && nla_put_flag(msg, NL80211_ATTR_TIMED_OUT)) ||
+           (status < 0 &&
+            (nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) ||
+             nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON, timeout_reason))) ||
            (req_ie &&
             nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
            (resp_ie &&
@@ -13236,7 +13283,7 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       msg = nlmsg_new(100 + req_ie_len + resp_ie_len, gfp);
        if (!msg)
                return;
 
@@ -13273,7 +13320,7 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       msg = nlmsg_new(100 + ie_len, GFP_KERNEL);
        if (!msg)
                return;
 
@@ -13349,7 +13396,7 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
 
        trace_cfg80211_notify_new_peer_candidate(dev, addr);
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       msg = nlmsg_new(100 + ie_len, gfp);
        if (!msg)
                return;
 
@@ -13720,7 +13767,7 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       msg = nlmsg_new(100 + len, gfp);
        if (!msg)
                return -ENOMEM;
 
@@ -13764,7 +13811,7 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
 
        trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       msg = nlmsg_new(100 + len, gfp);
        if (!msg)
                return;
 
@@ -14519,6 +14566,8 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
 
                        if (wdev->owner_nlportid == notify->portid)
                                schedule_destroy_work = true;
+                       else if (wdev->conn_owner_nlportid == notify->portid)
+                               schedule_work(&wdev->disconnect_wk);
                }
 
                spin_lock_bh(&rdev->beacon_registrations_lock);
@@ -14573,7 +14622,7 @@ void cfg80211_ft_event(struct net_device *netdev,
        if (!ft_event->target_ap)
                return;
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       msg = nlmsg_new(100 + ft_event->ric_ies_len, GFP_KERNEL);
        if (!msg)
                return;
 
index 7e3821d7fcc5eb185359d3b9c45bd3d526fd36c2..e488dca87423eb7c85fc80d408c3d45f91e9a97a 100644 (file)
@@ -14,12 +14,10 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
                             struct wireless_dev *wdev);
 struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
                                       struct wireless_dev *wdev, bool aborted);
-void nl80211_send_scan_result(struct cfg80211_registered_device *rdev,
-                             struct sk_buff *msg);
+void nl80211_send_scan_msg(struct cfg80211_registered_device *rdev,
+                          struct sk_buff *msg);
 void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
                             struct net_device *netdev, u32 cmd);
-void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
-                                    struct net_device *netdev);
 void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
                                     struct regulatory_request *request);
 
@@ -58,7 +56,9 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
                                 struct net_device *netdev, const u8 *bssid,
                                 const u8 *req_ie, size_t req_ie_len,
                                 const u8 *resp_ie, size_t resp_ie_len,
-                                int status, gfp_t gfp);
+                                int status,
+                                enum nl80211_timeout_reason timeout_reason,
+                                gfp_t gfp);
 void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
                         struct net_device *netdev, const u8 *bssid,
                         const u8 *req_ie, size_t req_ie_len,
diff --git a/net/wireless/of.c b/net/wireless/of.c
new file mode 100644 (file)
index 0000000..de221f0
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2017 RafaÅ‚ MiÅ‚ecki <rafal@milecki.pl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/of.h>
+#include <net/cfg80211.h>
+#include "core.h"
+
+static bool wiphy_freq_limits_valid_chan(struct wiphy *wiphy,
+                                        struct ieee80211_freq_range *freq_limits,
+                                        unsigned int n_freq_limits,
+                                        struct ieee80211_channel *chan)
+{
+       u32 bw = MHZ_TO_KHZ(20);
+       int i;
+
+       for (i = 0; i < n_freq_limits; i++) {
+               struct ieee80211_freq_range *limit = &freq_limits[i];
+
+               if (cfg80211_does_bw_fit_range(limit,
+                                              MHZ_TO_KHZ(chan->center_freq),
+                                              bw))
+                       return true;
+       }
+
+       return false;
+}
+
+static void wiphy_freq_limits_apply(struct wiphy *wiphy,
+                                   struct ieee80211_freq_range *freq_limits,
+                                   unsigned int n_freq_limits)
+{
+       enum nl80211_band band;
+       int i;
+
+       if (WARN_ON(!n_freq_limits))
+               return;
+
+       for (band = 0; band < NUM_NL80211_BANDS; band++) {
+               struct ieee80211_supported_band *sband = wiphy->bands[band];
+
+               if (!sband)
+                       continue;
+
+               for (i = 0; i < sband->n_channels; i++) {
+                       struct ieee80211_channel *chan = &sband->channels[i];
+
+                       if (chan->flags & IEEE80211_CHAN_DISABLED)
+                               continue;
+
+                       if (!wiphy_freq_limits_valid_chan(wiphy, freq_limits,
+                                                         n_freq_limits,
+                                                         chan)) {
+                               pr_debug("Disabling freq %d MHz as it's out of OF limits\n",
+                                        chan->center_freq);
+                               chan->flags |= IEEE80211_CHAN_DISABLED;
+                       }
+               }
+       }
+}
+
+void wiphy_read_of_freq_limits(struct wiphy *wiphy)
+{
+       struct device *dev = wiphy_dev(wiphy);
+       struct device_node *np;
+       struct property *prop;
+       struct ieee80211_freq_range *freq_limits;
+       unsigned int n_freq_limits;
+       const __be32 *p;
+       int len, i;
+       int err = 0;
+
+       if (!dev)
+               return;
+       np = dev_of_node(dev);
+       if (!np)
+               return;
+
+       prop = of_find_property(np, "ieee80211-freq-limit", &len);
+       if (!prop)
+               return;
+
+       if (!len || len % sizeof(u32) || len / sizeof(u32) % 2) {
+               dev_err(dev, "ieee80211-freq-limit wrong format");
+               return;
+       }
+       n_freq_limits = len / sizeof(u32) / 2;
+
+       freq_limits = kcalloc(n_freq_limits, sizeof(*freq_limits), GFP_KERNEL);
+       if (!freq_limits) {
+               err = -ENOMEM;
+               goto out_kfree;
+       }
+
+       p = NULL;
+       for (i = 0; i < n_freq_limits; i++) {
+               struct ieee80211_freq_range *limit = &freq_limits[i];
+
+               p = of_prop_next_u32(prop, p, &limit->start_freq_khz);
+               if (!p) {
+                       err = -EINVAL;
+                       goto out_kfree;
+               }
+
+               p = of_prop_next_u32(prop, p, &limit->end_freq_khz);
+               if (!p) {
+                       err = -EINVAL;
+                       goto out_kfree;
+               }
+
+               if (!limit->start_freq_khz ||
+                   !limit->end_freq_khz ||
+                   limit->start_freq_khz >= limit->end_freq_khz) {
+                       err = -EINVAL;
+                       goto out_kfree;
+               }
+       }
+
+       wiphy_freq_limits_apply(wiphy, freq_limits, n_freq_limits);
+
+out_kfree:
+       kfree(freq_limits);
+       if (err)
+               dev_err(dev, "Failed to get limits: %d\n", err);
+}
+EXPORT_SYMBOL(wiphy_read_of_freq_limits);
index 5dbac37497386fffb23de9bf5fe384891f874eca..753efcd51fa3495c66e7063d3ce6c1aca7fd9d14 100644 (file)
@@ -748,21 +748,6 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd)
        return true;
 }
 
-static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,
-                           u32 center_freq_khz, u32 bw_khz)
-{
-       u32 start_freq_khz, end_freq_khz;
-
-       start_freq_khz = center_freq_khz - (bw_khz/2);
-       end_freq_khz = center_freq_khz + (bw_khz/2);
-
-       if (start_freq_khz >= freq_range->start_freq_khz &&
-           end_freq_khz <= freq_range->end_freq_khz)
-               return true;
-
-       return false;
-}
-
 /**
  * freq_in_rule_band - tells us if a frequency is in a frequency band
  * @freq_range: frequency rule we want to query
@@ -1070,7 +1055,7 @@ freq_reg_info_regd(u32 center_freq,
                if (!band_rule_found)
                        band_rule_found = freq_in_rule_band(fr, center_freq);
 
-               bw_fits = reg_does_bw_fit(fr, center_freq, bw);
+               bw_fits = cfg80211_does_bw_fit_range(fr, center_freq, bw);
 
                if (band_rule_found && bw_fits)
                        return rr;
@@ -1138,11 +1123,13 @@ static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd
                max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
 
        /* If we get a reg_rule we can assume that at least 5Mhz fit */
-       if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
-                            MHZ_TO_KHZ(10)))
+       if (!cfg80211_does_bw_fit_range(freq_range,
+                                       MHZ_TO_KHZ(chan->center_freq),
+                                       MHZ_TO_KHZ(10)))
                bw_flags |= IEEE80211_CHAN_NO_10MHZ;
-       if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
-                            MHZ_TO_KHZ(20)))
+       if (!cfg80211_does_bw_fit_range(freq_range,
+                                       MHZ_TO_KHZ(chan->center_freq),
+                                       MHZ_TO_KHZ(20)))
                bw_flags |= IEEE80211_CHAN_NO_20MHZ;
 
        if (max_bandwidth_khz < MHZ_TO_KHZ(10))
index 35ad69fd08383a2a8392170d2a580fc4549276d7..21be56b3128ee74c4119ee67d5f0b85fecfe0b2e 100644 (file)
@@ -227,7 +227,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
        ASSERT_RTNL();
 
        if (rdev->scan_msg) {
-               nl80211_send_scan_result(rdev, rdev->scan_msg);
+               nl80211_send_scan_msg(rdev, rdev->scan_msg);
                rdev->scan_msg = NULL;
                return;
        }
@@ -273,7 +273,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
        if (!send_message)
                rdev->scan_msg = msg;
        else
-               nl80211_send_scan_result(rdev, msg);
+               nl80211_send_scan_msg(rdev, msg);
 }
 
 void __cfg80211_scan_done(struct work_struct *wk)
@@ -321,7 +321,8 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
                        spin_unlock_bh(&rdev->bss_lock);
                        request->scan_start = jiffies;
                }
-               nl80211_send_sched_scan_results(rdev, request->dev);
+               nl80211_send_sched_scan(rdev, request->dev,
+                                       NL80211_CMD_SCHED_SCAN_RESULTS);
        }
 
        rtnl_unlock();
@@ -1147,7 +1148,7 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
        else
                rcu_assign_pointer(tmp.pub.beacon_ies, ies);
        rcu_assign_pointer(tmp.pub.ies, ies);
-       
+
        memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
        tmp.pub.channel = channel;
        tmp.pub.scan_width = data->scan_width;
index 5e0d19380302c73cae700cc43b2751a12979ff2a..b347e63d7aaa6814f524d3b080a005a88966d65d 100644 (file)
@@ -34,10 +34,11 @@ struct cfg80211_conn {
                CFG80211_CONN_SCAN_AGAIN,
                CFG80211_CONN_AUTHENTICATE_NEXT,
                CFG80211_CONN_AUTHENTICATING,
-               CFG80211_CONN_AUTH_FAILED,
+               CFG80211_CONN_AUTH_FAILED_TIMEOUT,
                CFG80211_CONN_ASSOCIATE_NEXT,
                CFG80211_CONN_ASSOCIATING,
                CFG80211_CONN_ASSOC_FAILED,
+               CFG80211_CONN_ASSOC_FAILED_TIMEOUT,
                CFG80211_CONN_DEAUTH,
                CFG80211_CONN_ABANDON,
                CFG80211_CONN_CONNECTED,
@@ -140,7 +141,8 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
        return err;
 }
 
-static int cfg80211_conn_do_work(struct wireless_dev *wdev)
+static int cfg80211_conn_do_work(struct wireless_dev *wdev,
+                                enum nl80211_timeout_reason *treason)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_connect_params *params;
@@ -171,7 +173,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
                                          NULL, 0,
                                          params->key, params->key_len,
                                          params->key_idx, NULL, 0);
-       case CFG80211_CONN_AUTH_FAILED:
+       case CFG80211_CONN_AUTH_FAILED_TIMEOUT:
+               *treason = NL80211_TIMEOUT_AUTH;
                return -ENOTCONN;
        case CFG80211_CONN_ASSOCIATE_NEXT:
                if (WARN_ON(!rdev->ops->assoc))
@@ -198,6 +201,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
                                             WLAN_REASON_DEAUTH_LEAVING,
                                             false);
                return err;
+       case CFG80211_CONN_ASSOC_FAILED_TIMEOUT:
+               *treason = NL80211_TIMEOUT_ASSOC;
+               /* fall through */
        case CFG80211_CONN_ASSOC_FAILED:
                cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
                                     NULL, 0,
@@ -223,6 +229,7 @@ void cfg80211_conn_work(struct work_struct *work)
                container_of(work, struct cfg80211_registered_device, conn_work);
        struct wireless_dev *wdev;
        u8 bssid_buf[ETH_ALEN], *bssid = NULL;
+       enum nl80211_timeout_reason treason;
 
        rtnl_lock();
 
@@ -244,10 +251,12 @@ void cfg80211_conn_work(struct work_struct *work)
                        memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
                        bssid = bssid_buf;
                }
-               if (cfg80211_conn_do_work(wdev)) {
+               treason = NL80211_TIMEOUT_UNSPECIFIED;
+               if (cfg80211_conn_do_work(wdev, &treason)) {
                        __cfg80211_connect_result(
                                        wdev->netdev, bssid,
-                                       NULL, 0, NULL, 0, -1, false, NULL);
+                                       NULL, 0, NULL, 0, -1, false, NULL,
+                                       treason);
                }
                wdev_unlock(wdev);
        }
@@ -352,7 +361,8 @@ void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
        } else if (status_code != WLAN_STATUS_SUCCESS) {
                __cfg80211_connect_result(wdev->netdev, mgmt->bssid,
                                          NULL, 0, NULL, 0,
-                                         status_code, false, NULL);
+                                         status_code, false, NULL,
+                                         NL80211_TIMEOUT_UNSPECIFIED);
        } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
                wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
                schedule_work(&rdev->conn_work);
@@ -400,7 +410,7 @@ void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
        if (!wdev->conn)
                return;
 
-       wdev->conn->state = CFG80211_CONN_AUTH_FAILED;
+       wdev->conn->state = CFG80211_CONN_AUTH_FAILED_TIMEOUT;
        schedule_work(&rdev->conn_work);
 }
 
@@ -422,7 +432,7 @@ void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
        if (!wdev->conn)
                return;
 
-       wdev->conn->state = CFG80211_CONN_ASSOC_FAILED;
+       wdev->conn->state = CFG80211_CONN_ASSOC_FAILED_TIMEOUT;
        schedule_work(&rdev->conn_work);
 }
 
@@ -564,7 +574,9 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
 
        /* we're good if we have a matching bss struct */
        if (bss) {
-               err = cfg80211_conn_do_work(wdev);
+               enum nl80211_timeout_reason treason;
+
+               err = cfg80211_conn_do_work(wdev, &treason);
                cfg80211_put_bss(wdev->wiphy, bss);
        } else {
                /* otherwise we'll need to scan for the AP first */
@@ -661,7 +673,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                               const u8 *req_ie, size_t req_ie_len,
                               const u8 *resp_ie, size_t resp_ie_len,
                               int status, bool wextev,
-                              struct cfg80211_bss *bss)
+                              struct cfg80211_bss *bss,
+                              enum nl80211_timeout_reason timeout_reason)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        const u8 *country_ie;
@@ -680,7 +693,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
        nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev,
                                    bssid, req_ie, req_ie_len,
                                    resp_ie, resp_ie_len,
-                                   status, GFP_KERNEL);
+                                   status, timeout_reason, GFP_KERNEL);
 
 #ifdef CONFIG_CFG80211_WEXT
        if (wextev) {
@@ -727,6 +740,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                kzfree(wdev->connect_keys);
                wdev->connect_keys = NULL;
                wdev->ssid_len = 0;
+               wdev->conn_owner_nlportid = 0;
                if (bss) {
                        cfg80211_unhold_bss(bss_from_pub(bss));
                        cfg80211_put_bss(wdev->wiphy, bss);
@@ -770,7 +784,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
 void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
                          struct cfg80211_bss *bss, const u8 *req_ie,
                          size_t req_ie_len, const u8 *resp_ie,
-                         size_t resp_ie_len, int status, gfp_t gfp)
+                         size_t resp_ie_len, int status, gfp_t gfp,
+                         enum nl80211_timeout_reason timeout_reason)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
@@ -810,6 +825,7 @@ void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
                cfg80211_hold_bss(bss_from_pub(bss));
        ev->cr.bss = bss;
        ev->cr.status = status;
+       ev->cr.timeout_reason = timeout_reason;
 
        spin_lock_irqsave(&wdev->event_lock, flags);
        list_add_tail(&ev->list, &wdev->event_list);
@@ -955,6 +971,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
 
        wdev->current_bss = NULL;
        wdev->ssid_len = 0;
+       wdev->conn_owner_nlportid = 0;
 
        nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
 
@@ -1098,6 +1115,8 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
        kzfree(wdev->connect_keys);
        wdev->connect_keys = NULL;
 
+       wdev->conn_owner_nlportid = 0;
+
        if (wdev->conn)
                err = cfg80211_sme_disconnect(wdev, reason);
        else if (!rdev->ops->disconnect)
@@ -1107,3 +1126,32 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
 
        return err;
 }
+
+/*
+ * Used to clean up after the connection / connection attempt owner socket
+ * disconnects
+ */
+void cfg80211_autodisconnect_wk(struct work_struct *work)
+{
+       struct wireless_dev *wdev =
+               container_of(work, struct wireless_dev, disconnect_wk);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+       wdev_lock(wdev);
+
+       if (wdev->conn_owner_nlportid) {
+               /*
+                * Use disconnect_bssid if still connecting and ops->disconnect
+                * not implemented.  Otherwise we can use cfg80211_disconnect.
+                */
+               if (rdev->ops->disconnect || wdev->current_bss)
+                       cfg80211_disconnect(rdev, wdev->netdev,
+                                           WLAN_REASON_DEAUTH_LEAVING, true);
+               else
+                       cfg80211_mlme_deauth(rdev, wdev->netdev,
+                                            wdev->disconnect_bssid, NULL, 0,
+                                            WLAN_REASON_DEAUTH_LEAVING, false);
+       }
+
+       wdev_unlock(wdev);
+}
index 14b3f007826d91da6c5a71aee105b735eb9a2071..16b6b5988be969299c34a9881f258a300b366e2c 100644 (file)
@@ -39,9 +39,11 @@ SHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
 
 static ssize_t name_show(struct device *dev,
                         struct device_attribute *attr,
-                        char *buf) {
+                        char *buf)
+{
        struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy;
-       return sprintf(buf, "%s\n", dev_name(&wiphy->dev));
+
+       return sprintf(buf, "%s\n", wiphy_name(wiphy));
 }
 static DEVICE_ATTR_RO(name);
 
index e9d040d29846f87517c0b6b40f0ff9dbeca63a05..1b9296882dcd6a0b585dfd604a30807e7f26290c 100644 (file)
@@ -114,8 +114,7 @@ int ieee80211_frequency_to_channel(int freq)
 }
 EXPORT_SYMBOL(ieee80211_frequency_to_channel);
 
-struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy,
-                                                 int freq)
+struct ieee80211_channel *ieee80211_get_channel(struct wiphy *wiphy, int freq)
 {
        enum nl80211_band band;
        struct ieee80211_supported_band *sband;
@@ -135,14 +134,13 @@ struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy,
 
        return NULL;
 }
-EXPORT_SYMBOL(__ieee80211_get_channel);
+EXPORT_SYMBOL(ieee80211_get_channel);
 
-static void set_mandatory_flags_band(struct ieee80211_supported_band *sband,
-                                    enum nl80211_band band)
+static void set_mandatory_flags_band(struct ieee80211_supported_band *sband)
 {
        int i, want;
 
-       switch (band) {
+       switch (sband->band) {
        case NL80211_BAND_5GHZ:
                want = 3;
                for (i = 0; i < sband->n_bitrates; i++) {
@@ -192,6 +190,7 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband,
                WARN_ON((sband->ht_cap.mcs.rx_mask[0] & 0x1e) != 0x1e);
                break;
        case NUM_NL80211_BANDS:
+       default:
                WARN_ON(1);
                break;
        }
@@ -203,7 +202,7 @@ void ieee80211_set_bitrate_flags(struct wiphy *wiphy)
 
        for (band = 0; band < NUM_NL80211_BANDS; band++)
                if (wiphy->bands[band])
-                       set_mandatory_flags_band(wiphy->bands[band], band);
+                       set_mandatory_flags_band(wiphy->bands[band]);
 }
 
 bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher)
@@ -952,7 +951,7 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
                                ev->cr.resp_ie, ev->cr.resp_ie_len,
                                ev->cr.status,
                                ev->cr.status == WLAN_STATUS_SUCCESS,
-                               ev->cr.bss);
+                               ev->cr.bss, ev->cr.timeout_reason);
                        break;
                case EVENT_ROAMED:
                        __cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie,
@@ -1848,6 +1847,21 @@ void cfg80211_free_nan_func(struct cfg80211_nan_func *f)
 }
 EXPORT_SYMBOL(cfg80211_free_nan_func);
 
+bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range,
+                               u32 center_freq_khz, u32 bw_khz)
+{
+       u32 start_freq_khz, end_freq_khz;
+
+       start_freq_khz = center_freq_khz - (bw_khz / 2);
+       end_freq_khz = center_freq_khz + (bw_khz / 2);
+
+       if (start_freq_khz >= freq_range->start_freq_khz &&
+           end_freq_khz <= freq_range->end_freq_khz)
+               return true;
+
+       return false;
+}
+
 /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
 /* Ethernet-II snap header (RFC1042 for most EtherTypes) */
 const unsigned char rfc1042_header[] __aligned(2) =
index 6250b1cfcde58758bb480758d1c61217d37a7cd1..1a4db6790e2077d4ea922d1c32cde5352543fa2c 100644 (file)
@@ -1119,3 +1119,70 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
        return ret;
 }
 #endif
+
+char *iwe_stream_add_event(struct iw_request_info *info, char *stream,
+                          char *ends, struct iw_event *iwe, int event_len)
+{
+       int lcp_len = iwe_stream_lcp_len(info);
+
+       event_len = iwe_stream_event_len_adjust(info, event_len);
+
+       /* Check if it's possible */
+       if (likely((stream + event_len) < ends)) {
+               iwe->len = event_len;
+               /* Beware of alignement issues on 64 bits */
+               memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
+               memcpy(stream + lcp_len, &iwe->u,
+                      event_len - lcp_len);
+               stream += event_len;
+       }
+
+       return stream;
+}
+EXPORT_SYMBOL(iwe_stream_add_event);
+
+char *iwe_stream_add_point(struct iw_request_info *info, char *stream,
+                          char *ends, struct iw_event *iwe, char *extra)
+{
+       int event_len = iwe_stream_point_len(info) + iwe->u.data.length;
+       int point_len = iwe_stream_point_len(info);
+       int lcp_len   = iwe_stream_lcp_len(info);
+
+       /* Check if it's possible */
+       if (likely((stream + event_len) < ends)) {
+               iwe->len = event_len;
+               memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
+               memcpy(stream + lcp_len,
+                      ((char *) &iwe->u) + IW_EV_POINT_OFF,
+                      IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
+               if (iwe->u.data.length && extra)
+                       memcpy(stream + point_len, extra, iwe->u.data.length);
+               stream += event_len;
+       }
+
+       return stream;
+}
+EXPORT_SYMBOL(iwe_stream_add_point);
+
+char *iwe_stream_add_value(struct iw_request_info *info, char *event,
+                          char *value, char *ends, struct iw_event *iwe,
+                          int event_len)
+{
+       int lcp_len = iwe_stream_lcp_len(info);
+
+       /* Don't duplicate LCP */
+       event_len -= IW_EV_LCP_LEN;
+
+       /* Check if it's possible */
+       if (likely((value + event_len) < ends)) {
+               /* Add new value */
+               memcpy(value, &iwe->u, event_len);
+               value += event_len;
+               /* Patch LCP */
+               iwe->len = value - event;
+               memcpy(event, (char *) iwe, lcp_len);
+       }
+
+       return value;
+}
+EXPORT_SYMBOL(iwe_stream_add_value);
index 995163830a61f582c11b9234b370bb2c3766f7f0..c434f193f39aa6a1215c2791e25a891faed6b573 100644 (file)
@@ -105,30 +105,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
                        goto out;
        }
 
-
        wdev->wext.connect.channel = chan;
-
-       /*
-        * SSID is not set, we just want to switch monitor channel,
-        * this is really just backward compatibility, if the SSID
-        * is set then we use the channel to select the BSS to use
-        * to connect to instead. If we were connected on another
-        * channel we disconnected above and reconnect below.
-        */
-       if (chan && !wdev->wext.connect.ssid_len) {
-               struct cfg80211_chan_def chandef = {
-                       .width = NL80211_CHAN_WIDTH_20_NOHT,
-                       .center_freq1 = freq,
-               };
-
-               chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
-               if (chandef.chan)
-                       err = cfg80211_set_monitor_channel(rdev, &chandef);
-               else
-                       err = -EINVAL;
-               goto out;
-       }
-
        err = cfg80211_mgd_wext_connect(rdev, wdev);
  out:
        wdev_unlock(wdev);