Merge branch 'mac80211' into mac80211-next
authorJohannes Berg <johannes.berg@intel.com>
Wed, 7 Jan 2015 13:39:02 +0000 (14:39 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 7 Jan 2015 13:39:16 +0000 (14:39 +0100)
Merge mac80211.git to get some changes that would otherwise
cause conflicts with new changes coming here.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
34 files changed:
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/cw1200/main.c
drivers/net/wireless/cw1200/sta.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/ti/wl1251/main.c
drivers/net/wireless/ti/wlcore/main.c
include/net/cfg80211.h
include/net/mac80211.h
include/net/regulatory.h
include/uapi/linux/nl80211.h
net/mac80211/cfg.c
net/mac80211/debugfs.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/main.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/rc80211_minstrel.c
net/mac80211/rc80211_minstrel.h
net/mac80211/rx.c
net/mac80211/spectmgmt.c
net/mac80211/sta_info.c
net/mac80211/status.c
net/mac80211/util.c
net/mac80211/vht.c
net/wireless/core.c
net/wireless/core.h
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/reg.c
net/wireless/reg.h
net/wireless/scan.c

index c400567..2619db1 100644 (file)
@@ -2871,6 +2871,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        int bit;
        u32 vdev_param;
 
+       vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
+
        mutex_lock(&ar->conf_mutex);
 
        memset(arvif, 0, sizeof(*arvif));
@@ -5024,7 +5026,6 @@ int ath10k_mac_register(struct ath10k *ar)
        ar->hw->flags = IEEE80211_HW_SIGNAL_DBM |
                        IEEE80211_HW_SUPPORTS_PS |
                        IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
-                       IEEE80211_HW_SUPPORTS_UAPSD |
                        IEEE80211_HW_MFP_CAPABLE |
                        IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                        IEEE80211_HW_HAS_RATE_CONTROL |
index e9bd02c..4caee66 100644 (file)
@@ -2259,7 +2259,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
        struct ath_txq *txq = txctl->txq;
        struct ath_atx_tid *tid = NULL;
        struct ath_buf *bf;
-       bool queue, skip_uapsd = false;
+       bool queue, skip_uapsd = false, ps_resp;
        int q, ret;
 
        if (vif)
@@ -2268,6 +2268,8 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
        if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
                txctl->force_channel = true;
 
+       ps_resp = !!(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE);
+
        ret = ath_tx_prepare(hw, skb, txctl);
        if (ret)
            return ret;
@@ -2310,7 +2312,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
        if (txctl->an && queue)
                tid = ath_get_skb_tid(sc, txctl->an, skb);
 
-       if (!skip_uapsd && (info->flags & IEEE80211_TX_CTL_PS_RESPONSE)) {
+       if (!skip_uapsd && ps_resp) {
                ath_txq_unlock(sc, txq);
                txq = sc->tx.uapsdq;
                ath_txq_lock(sc, txq);
index 3e78cc3..0da6e42 100644 (file)
@@ -282,7 +282,6 @@ static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
                    IEEE80211_HW_SUPPORTS_PS |
                    IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
                    IEEE80211_HW_REPORTS_TX_ACK_STATUS |
-                   IEEE80211_HW_SUPPORTS_UAPSD |
                    IEEE80211_HW_CONNECTION_MONITOR |
                    IEEE80211_HW_AMPDU_AGGREGATION |
                    IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
index 5b84664..a1e3237 100644 (file)
@@ -213,6 +213,7 @@ int cw1200_add_interface(struct ieee80211_hw *dev,
        /* __le32 auto_calibration_mode = __cpu_to_le32(1); */
 
        vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+                            IEEE80211_VIF_SUPPORTS_UAPSD |
                             IEEE80211_VIF_SUPPORTS_CQM_RSSI;
 
        mutex_lock(&priv->conf_mutex);
index 31a5b3f..346331d 100644 (file)
@@ -326,6 +326,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
                IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED;
        hw->rate_control_algorithm = "iwl-mvm-rs";
+       hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
+       hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
 
        /*
         * Enable 11w if advertised by firmware and software crypto
@@ -336,13 +338,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
            !iwlwifi_mod_params.sw_crypto)
                hw->flags |= IEEE80211_HW_MFP_CAPABLE;
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
-           !iwlwifi_mod_params.uapsd_disable) {
-               hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
-               hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
-               hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
-       }
-
        if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
            mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
                hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
@@ -1147,6 +1142,10 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
                mvm->bf_allowed_vif = mvmvif;
                vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
                                     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+               if (mvm->fw->ucode_capa.flags &
+                                       IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
+                   !iwlwifi_mod_params.uapsd_disable)
+                       vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
        }
 
        /*
index a71b9d5..057a99e 100644 (file)
@@ -2150,14 +2150,14 @@ static int append_radio_msg(struct sk_buff *skb, int id,
        if (param->regd) {
                int i;
 
-               for (i = 0; hwsim_world_regdom_custom[i] != param->regd &&
-                    i < ARRAY_SIZE(hwsim_world_regdom_custom); i++)
-                       ;
+               for (i = 0; i < ARRAY_SIZE(hwsim_world_regdom_custom); i++) {
+                       if (hwsim_world_regdom_custom[i] != param->regd)
+                               continue;
 
-               if (i < ARRAY_SIZE(hwsim_world_regdom_custom)) {
                        ret = nla_put_u32(skb, HWSIM_ATTR_REG_CUSTOM_REG, i);
                        if (ret < 0)
                                return ret;
+                       break;
                }
        }
 
index 0b30a7b..d4ba009 100644 (file)
@@ -500,6 +500,7 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw,
        int ret = 0;
 
        vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+                            IEEE80211_VIF_SUPPORTS_UAPSD |
                             IEEE80211_VIF_SUPPORTS_CQM_RSSI;
 
        wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
@@ -1480,9 +1481,7 @@ int wl1251_init_ieee80211(struct wl1251 *wl)
        /* unit us */
        /* FIXME: find a proper value */
 
-       wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
-               IEEE80211_HW_SUPPORTS_PS |
-               IEEE80211_HW_SUPPORTS_UAPSD;
+       wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS;
 
        wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
                                         BIT(NL80211_IFTYPE_ADHOC);
index 6ad3fce..2a99456 100644 (file)
@@ -2508,6 +2508,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
        }
 
        vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+                            IEEE80211_VIF_SUPPORTS_UAPSD |
                             IEEE80211_VIF_SUPPORTS_CQM_RSSI;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
@@ -5776,7 +5777,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
                IEEE80211_HW_SUPPORTS_PS |
                IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
-               IEEE80211_HW_SUPPORTS_UAPSD |
                IEEE80211_HW_HAS_RATE_CONTROL |
                IEEE80211_HW_CONNECTION_MONITOR |
                IEEE80211_HW_REPORTS_TX_ACK_STATUS |
index 4ebb816..f38645f 100644 (file)
@@ -1516,6 +1516,9 @@ struct cfg80211_match_set {
  * @mac_addr_mask: MAC address mask used with randomisation, bits that
  *     are 0 in the mask should be randomised, bits that are 1 should
  *     be taken from the @mac_addr
+ * @rcu_head: RCU callback used to free the struct
+ * @owner_nlportid: netlink portid of owner (if this should is a request
+ *     owned by a particular socket)
  */
 struct cfg80211_sched_scan_request {
        struct cfg80211_ssid *ssids;
@@ -1537,6 +1540,8 @@ struct cfg80211_sched_scan_request {
        struct wiphy *wiphy;
        struct net_device *dev;
        unsigned long scan_start;
+       struct rcu_head rcu_head;
+       u32 owner_nlportid;
 
        /* keep last */
        struct ieee80211_channel *channels[0];
@@ -3011,6 +3016,8 @@ struct wiphy_vendor_command {
  * @regulatory_flags: wiphy regulatory flags, see
  *     &enum ieee80211_regulatory_flags
  * @features: features advertised to nl80211, see &enum nl80211_feature_flags.
+ * @ext_features: extended features advertised to nl80211, see
+ *     &enum nl80211_ext_feature_index.
  * @bss_priv_size: each BSS struct has private data allocated with it,
  *     this variable determines its size
  * @max_scan_ssids: maximum number of SSIDs the device can scan for in
@@ -3120,6 +3127,7 @@ struct wiphy {
        u16 max_acl_mac_addrs;
 
        u32 flags, regulatory_flags, features;
+       u8 ext_features[DIV_ROUND_UP(NUM_NL80211_EXT_FEATURES, 8)];
 
        u32 ap_sme_capa;
 
@@ -3807,6 +3815,20 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,
  */
 int regulatory_hint(struct wiphy *wiphy, const char *alpha2);
 
+/**
+ * regulatory_set_wiphy_regd - set regdom info for self managed drivers
+ * @wiphy: the wireless device we want to process the regulatory domain on
+ * @rd: the regulatory domain informatoin to use for this wiphy
+ *
+ * Set the regulatory domain information for self-managed wiphys, only they
+ * may use this function. See %REGULATORY_WIPHY_SELF_MANAGED for more
+ * information.
+ *
+ * Return: 0 on success. -EINVAL, -EPERM
+ */
+int regulatory_set_wiphy_regd(struct wiphy *wiphy,
+                             struct ieee80211_regdomain *rd);
+
 /**
  * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain
  * @wiphy: the wireless device we want to process the regulatory domain on
@@ -5033,6 +5055,42 @@ void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
  */
 void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy);
 
+/**
+ * wiphy_ext_feature_set - set the extended feature flag
+ *
+ * @wiphy: the wiphy to modify.
+ * @ftidx: extended feature bit index.
+ *
+ * The extended features are flagged in multiple bytes (see
+ * &struct wiphy.@ext_features)
+ */
+static inline void wiphy_ext_feature_set(struct wiphy *wiphy,
+                                        enum nl80211_ext_feature_index ftidx)
+{
+       u8 *ft_byte;
+
+       ft_byte = &wiphy->ext_features[ftidx / 8];
+       *ft_byte |= BIT(ftidx % 8);
+}
+
+/**
+ * wiphy_ext_feature_isset - check the extended feature flag
+ *
+ * @wiphy: the wiphy to modify.
+ * @ftidx: extended feature bit index.
+ *
+ * The extended features are flagged in multiple bytes (see
+ * &struct wiphy.@ext_features)
+ */
+static inline bool
+wiphy_ext_feature_isset(struct wiphy *wiphy,
+                       enum nl80211_ext_feature_index ftidx)
+{
+       u8 ft_byte;
+
+       ft_byte = wiphy->ext_features[ftidx / 8];
+       return (ft_byte & BIT(ftidx % 8)) != 0;
+}
 
 /* ethtool helper */
 void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
index 29c7be8..ece1a54 100644 (file)
@@ -505,8 +505,11 @@ struct ieee80211_bss_conf {
  * @IEEE80211_TX_CTL_DONTFRAG: Don't fragment this packet even if it
  *     would be fragmented by size (this is optional, only used for
  *     monitor injection).
- * @IEEE80211_TX_CTL_PS_RESPONSE: This frame is a response to a poll
- *     frame (PS-Poll or uAPSD).
+ * @IEEE80211_TX_STAT_NOACK_TRANSMITTED: A frame that was marked with
+ *     IEEE80211_TX_CTL_NO_ACK has been successfully transmitted without
+ *     any errors (like issues specific to the driver/HW).
+ *     This flag must not be set for frames that don't request no-ack
+ *     behaviour with IEEE80211_TX_CTL_NO_ACK.
  *
  * Note: If you have to add new flags to the enumeration, then don't
  *      forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary.
@@ -542,7 +545,7 @@ enum mac80211_tx_info_flags {
        IEEE80211_TX_STATUS_EOSP                = BIT(28),
        IEEE80211_TX_CTL_USE_MINRATE            = BIT(29),
        IEEE80211_TX_CTL_DONTFRAG               = BIT(30),
-       IEEE80211_TX_CTL_PS_RESPONSE            = BIT(31),
+       IEEE80211_TX_STAT_NOACK_TRANSMITTED     = BIT(31),
 };
 
 #define IEEE80211_TX_CTL_STBC_SHIFT            23
@@ -552,11 +555,14 @@ enum mac80211_tx_info_flags {
  *
  * @IEEE80211_TX_CTRL_PORT_CTRL_PROTO: this frame is a port control
  *     protocol frame (e.g. EAP)
+ * @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
+ *     frame (PS-Poll or uAPSD).
  *
  * These flags are used in tx_info->control.flags.
  */
 enum mac80211_tx_control_flags {
        IEEE80211_TX_CTRL_PORT_CTRL_PROTO       = BIT(0),
+       IEEE80211_TX_CTRL_PS_RESPONSE           = BIT(1),
 };
 
 /*
@@ -1181,10 +1187,15 @@ struct ieee80211_channel_switch {
  *     monitoring on this virtual interface -- i.e. it can monitor
  *     connection quality related parameters, such as the RSSI level and
  *     provide notifications if configured trigger levels are reached.
+ * @IEEE80211_VIF_SUPPORTS_UAPSD: The device can do U-APSD for this
+ *     interface. This flag should be set during interface addition,
+ *     but may be set/cleared as late as authentication to an AP. It is
+ *     only valid for managed/station mode interfaces.
  */
 enum ieee80211_vif_flags {
        IEEE80211_VIF_BEACON_FILTER             = BIT(0),
        IEEE80211_VIF_SUPPORTS_CQM_RSSI         = BIT(1),
+       IEEE80211_VIF_SUPPORTS_UAPSD            = BIT(2),
 };
 
 /**
@@ -1580,11 +1591,6 @@ struct ieee80211_tx_control {
  * @IEEE80211_HW_MFP_CAPABLE:
  *     Hardware supports management frame protection (MFP, IEEE 802.11w).
  *
- * @IEEE80211_HW_SUPPORTS_UAPSD:
- *     Hardware supports Unscheduled Automatic Power Save Delivery
- *     (U-APSD) in managed mode. The mode is configured with
- *     conf_tx() operation.
- *
  * @IEEE80211_HW_REPORTS_TX_ACK_STATUS:
  *     Hardware can provide ack status reports of Tx frames to
  *     the stack.
@@ -1670,8 +1676,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_MFP_CAPABLE                        = 1<<13,
        IEEE80211_HW_WANT_MONITOR_VIF                   = 1<<14,
        IEEE80211_HW_NO_AUTO_VIF                        = 1<<15,
-       /* free slot */
-       IEEE80211_HW_SUPPORTS_UAPSD                     = 1<<17,
+       /* free slots */
        IEEE80211_HW_REPORTS_TX_ACK_STATUS              = 1<<18,
        IEEE80211_HW_CONNECTION_MONITOR                 = 1<<19,
        IEEE80211_HW_QUEUE_CONTROL                      = 1<<20,
@@ -2023,7 +2028,7 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
  * enabled whenever user has enabled powersave.
  *
  * Driver informs U-APSD client support by enabling
- * %IEEE80211_HW_SUPPORTS_UAPSD flag. The mode is configured through the
+ * %IEEE80211_VIF_SUPPORTS_UAPSD flag. The mode is configured through the
  * uapsd parameter in conf_tx() operation. Hardware needs to send the QoS
  * Nullfunc frames and stay awake until the service period has ended. To
  * utilize U-APSD, dynamic powersave is disabled for voip AC and all frames
index b776d72..ebc5a2e 100644 (file)
@@ -147,6 +147,24 @@ struct regulatory_request {
  *     NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO,
  *     NL80211_IFTYPE_P2P_DEVICE. The flag will be set by default if a device
  *     includes any modes unsupported for enforcement checking.
+ * @REGULATORY_WIPHY_SELF_MANAGED: for devices that employ wiphy-specific
+ *     regdom management. These devices will ignore all regdom changes not
+ *     originating from their own wiphy.
+ *     A self-managed wiphys only employs regulatory information obtained from
+ *     the FW and driver and does not use other cfg80211 sources like
+ *     beacon-hints, country-code IEs and hints from other devices on the same
+ *     system. Conversely, a self-managed wiphy does not share its regulatory
+ *     hints with other devices in the system. If a system contains several
+ *     devices, one or more of which are self-managed, there might be
+ *     contradictory regulatory settings between them. Usage of flag is
+ *     generally discouraged. Only use it if the FW/driver is incompatible
+ *     with non-locally originated hints.
+ *     This flag is incompatible with the flags: %REGULATORY_CUSTOM_REG,
+ *     %REGULATORY_STRICT_REG, %REGULATORY_COUNTRY_IE_FOLLOW_POWER,
+ *     %REGULATORY_COUNTRY_IE_IGNORE and %REGULATORY_DISABLE_BEACON_HINTS.
+ *     Mixing any of the above flags with this flag will result in a failure
+ *     to register the wiphy. This flag implies
+ *     %REGULATORY_DISABLE_BEACON_HINTS and %REGULATORY_COUNTRY_IE_IGNORE.
  */
 enum ieee80211_regulatory_flags {
        REGULATORY_CUSTOM_REG                   = BIT(0),
@@ -156,6 +174,7 @@ enum ieee80211_regulatory_flags {
        REGULATORY_COUNTRY_IE_IGNORE            = BIT(4),
        REGULATORY_ENABLE_RELAX_NO_IR           = BIT(5),
        REGULATORY_IGNORE_STALE_KICKOFF         = BIT(6),
+       REGULATORY_WIPHY_SELF_MANAGED           = BIT(7),
 };
 
 struct ieee80211_freq_range {
index b37bd5a..7ba9404 100644 (file)
 
 #define NL80211_GENL_NAME "nl80211"
 
+#define NL80211_MULTICAST_GROUP_CONFIG         "config"
+#define NL80211_MULTICAST_GROUP_SCAN           "scan"
+#define NL80211_MULTICAST_GROUP_REG            "regulatory"
+#define NL80211_MULTICAST_GROUP_MLME           "mlme"
+#define NL80211_MULTICAST_GROUP_VENDOR         "vendor"
+#define NL80211_MULTICAST_GROUP_TESTMODE       "testmode"
+
 /**
  * DOC: Station handling
  *
  *     %NL80211_ATTR_IFINDEX.
  *
  * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
- *     regulatory domain.
+ *     regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device
+ *     has a private regulatory domain, it will be returned. Otherwise, the
+ *     global regdomain will be returned.
+ *     A device will have a private regulatory domain if it uses the
+ *     regulatory_hint() API. Even when a private regdomain is used the channel
+ *     information will still be mended according to further hints from
+ *     the regulatory core to help with compliance. A dump version of this API
+ *     is now available which will returns the global regdomain as well as
+ *     all private regdomains of present wiphys (for those that have it).
+ *     If a wiphy is self-managed (%NL80211_ATTR_WIPHY_SELF_MANAGED_REG), then
+ *     its private regdomain is the only valid one for it. The regulatory
+ *     core is not used to help with compliance in this case.
  * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command
  *     after being queried by the kernel. CRDA replies by sending a regulatory
  *     domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
  *     peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
  *     when this command completes.
  *
+ * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used
+ *     as an event to indicate changes for devices with wiphy-specific regdom
+ *     management.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -958,6 +980,8 @@ enum nl80211_commands {
        NL80211_CMD_TDLS_CHANNEL_SWITCH,
        NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
 
+       NL80211_CMD_WIPHY_REG_CHANGE,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -1655,6 +1679,9 @@ enum nl80211_commands {
  * @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface
  *     creation then the new interface will be owned by the netlink socket
  *     that created it and will be destroyed when the socket is closed.
+ *     If set during scheduled scan start then the new scan req will be
+ *     owned by the netlink socket that created it and the scheduled scan will
+ *     be stopped when the socket is closed.
  *
  * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
  *     the TDLS link initiator.
@@ -1688,6 +1715,18 @@ enum nl80211_commands {
  *
  * @NL80211_ATTR_MAC_MASK: MAC address mask
  *
+ * @NL80211_ATTR_WIPHY_SELF_MANAGED_REG: flag attribute indicating this device
+ *     is self-managing its regulatory information and any regulatory domain
+ *     obtained from it is coming from the device's wiphy and not the global
+ *     cfg80211 regdomain.
+ *
+ * @NL80211_ATTR_EXT_FEATURES: extended feature flags contained in a byte
+ *     array. The feature flags are identified by their bit index (see &enum
+ *     nl80211_ext_feature_index). The bit index is ordered starting at the
+ *     least-significant bit of the first byte in the array, ie. bit index 0
+ *     is located at bit 0 of byte 0. bit index 25 would be located at bit 1
+ *     of byte 3 (u8 array).
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2045,6 +2084,10 @@ enum nl80211_attrs {
 
        NL80211_ATTR_MAC_MASK,
 
+       NL80211_ATTR_WIPHY_SELF_MANAGED_REG,
+
+       NL80211_ATTR_EXT_FEATURES,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2085,7 +2128,7 @@ enum nl80211_attrs {
 
 #define NL80211_MAX_SUPP_RATES                 32
 #define NL80211_MAX_SUPP_HT_RATES              77
-#define NL80211_MAX_SUPP_REG_RULES             32
+#define NL80211_MAX_SUPP_REG_RULES             64
 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY      0
 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY    16
 #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY    24
@@ -3238,6 +3281,9 @@ enum nl80211_bss {
 /**
  * enum nl80211_bss_status - BSS "status"
  * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS.
+ *     Note that this is no longer used since cfg80211 no longer
+ *     keeps track of whether or not authentication was done with
+ *     a given BSS.
  * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS.
  * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS.
  *
@@ -4193,6 +4239,19 @@ enum nl80211_feature_flags {
        NL80211_FEATURE_ND_RANDOM_MAC_ADDR              = 1 << 31,
 };
 
+/**
+ * enum nl80211_ext_feature_index - bit index of extended features.
+ *
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+enum nl80211_ext_feature_index {
+
+       /* add new features before the definition below */
+       NUM_NL80211_EXT_FEATURES,
+       MAX_NL80211_EXT_FEATURES = NUM_NL80211_EXT_FEATURES - 1
+};
+
 /**
  * enum nl80211_probe_resp_offload_support_attr - optional supported
  *     protocols for probe-response offloading by the driver/FW.
index e75d5c5..1696658 100644 (file)
@@ -678,7 +678,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
                      BSS_CHANGED_BEACON_ENABLED |
                      BSS_CHANGED_BEACON |
                      BSS_CHANGED_SSID |
-                     BSS_CHANGED_P2P_PS;
+                     BSS_CHANGED_P2P_PS |
+                     BSS_CHANGED_TXPOWER;
        int err;
 
        old = sdata_dereference(sdata->u.ap.beacon, sdata);
index 54a189f..eeb0bbd 100644 (file)
@@ -303,8 +303,6 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
                sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
        if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE)
                sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
-       if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
-               sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");
        if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
                sf += scnprintf(buf + sf, mxln - sf,
                                "REPORTS_TX_ACK_STATUS\n");
index 509bc15..b606b53 100644 (file)
@@ -1069,9 +1069,16 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                }
 
                if (sta && rates_updated) {
-                       drv_sta_rc_update(local, sdata, &sta->sta,
-                                         IEEE80211_RC_SUPP_RATES_CHANGED);
+                       u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
+                       u8 rx_nss = sta->sta.rx_nss;
+
+                       /* Force rx_nss recalculation */
+                       sta->sta.rx_nss = 0;
                        rate_control_rate_init(sta);
+                       if (sta->sta.rx_nss != rx_nss)
+                               changed |= IEEE80211_RC_NSS_CHANGED;
+
+                       drv_sta_rc_update(local, sdata, &sta->sta, changed);
                }
 
                rcu_read_unlock();
index cc6e964..4f45cab 100644 (file)
@@ -1704,6 +1704,7 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
                                    struct ieee80211_supported_band *sband,
                                    const struct ieee80211_vht_cap *vht_cap_ie,
                                    struct sta_info *sta);
+enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta);
 enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta);
 void ieee80211_sta_set_rx_nss(struct sta_info *sta);
 u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
index 6ab99da..d9ce336 100644 (file)
@@ -916,10 +916,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                }
        }
 
-       WARN((local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
-            && (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK),
-            "U-APSD not supported with HW_PS_NULLFUNC_STACK\n");
-
        /*
         * Calculate scan IE length -- we need this to alloc
         * memory and to subtract from the driver limit. It
index b488e18..fa94ca1 100644 (file)
@@ -523,6 +523,13 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
            sdata->u.mesh.mshcfg.auto_open_plinks &&
            rssi_threshold_check(sdata, sta))
                changed = mesh_plink_open(sta);
+       else if (sta->plink_state == NL80211_PLINK_LISTEN &&
+                (sdata->u.mesh.user_mpm ||
+                 sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED))
+               cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr,
+                                                  elems->ie_start,
+                                                  elems->total_len,
+                                                  GFP_ATOMIC);
 
        ieee80211_mps_frame_release(sta, elems);
 out:
index 2c36c47..8354bed 100644 (file)
@@ -157,14 +157,18 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct cfg80211_chan_def vht_chandef;
+       struct ieee80211_sta_ht_cap sta_ht_cap;
        u32 ht_cfreq, ret;
 
+       memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
+       ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
+
        chandef->chan = channel;
        chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
        chandef->center_freq1 = channel->center_freq;
        chandef->center_freq2 = 0;
 
-       if (!ht_cap || !ht_oper || !sband->ht_cap.ht_supported) {
+       if (!ht_cap || !ht_oper || !sta_ht_cap.ht_supported) {
                ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
                goto out;
        }
@@ -198,7 +202,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
        }
 
        /* check 40 MHz support, if we have it */
-       if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+       if (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
                switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
                case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
                        chandef->width = NL80211_CHAN_WIDTH_40;
@@ -1054,8 +1058,6 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
                sdata->csa_block_tx = false;
        }
 
-       cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef);
-
        sdata->vif.csa_active = false;
        ifmgd->csa_waiting_bcn = false;
 
@@ -1067,6 +1069,8 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
                                     &ifmgd->csa_connection_drop_work);
                return;
        }
+
+       cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef);
 }
 
 void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
@@ -1284,8 +1288,11 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
                country_ie_len -= 3;
        }
 
-       if (have_chan_pwr)
+       if (have_chan_pwr && pwr_constr_elem)
                *pwr_reduction = *pwr_constr_elem;
+       else
+               *pwr_reduction = 0;
+
        return have_chan_pwr;
 }
 
@@ -1314,10 +1321,11 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
        int chan_pwr = 0, pwr_reduction_80211h = 0;
        int pwr_level_cisco, pwr_level_80211h;
        int new_ap_level;
+       __le16 capab = mgmt->u.probe_resp.capab_info;
 
-       if (country_ie && pwr_constr_ie &&
-           mgmt->u.probe_resp.capab_info &
-               cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) {
+       if (country_ie &&
+           (capab & cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT) ||
+            capab & cpu_to_le16(WLAN_CAPABILITY_RADIO_MEASURE))) {
                has_80211h_pwr = ieee80211_find_80211h_pwr_constr(
                        sdata, channel, country_ie, country_ie_len,
                        pwr_constr_ie, &chan_pwr, &pwr_reduction_80211h);
@@ -4197,9 +4205,13 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_bss *bss = (void *)cbss->priv;
        struct sta_info *new_sta = NULL;
-       bool have_sta = false;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_sta_ht_cap sta_ht_cap;
+       bool have_sta = false, is_override = false;
        int err;
 
+       sband = local->hw.wiphy->bands[cbss->channel->band];
+
        if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
                return -EINVAL;
 
@@ -4214,25 +4226,32 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                if (!new_sta)
                        return -ENOMEM;
        }
+
+       memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
+       ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
+
+       is_override = (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) !=
+                     (sband->ht_cap.cap &
+                      IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+
+       if (new_sta || is_override) {
+               err = ieee80211_prep_channel(sdata, cbss);
+               if (err) {
+                       if (new_sta)
+                               sta_info_free(local, new_sta);
+                       return -EINVAL;
+               }
+       }
+
        if (new_sta) {
                u32 rates = 0, basic_rates = 0;
                bool have_higher_than_11mbit;
                int min_rate = INT_MAX, min_rate_index = -1;
                struct ieee80211_chanctx_conf *chanctx_conf;
-               struct ieee80211_supported_band *sband;
                const struct cfg80211_bss_ies *ies;
-               int shift;
+               int shift = ieee80211_vif_get_shift(&sdata->vif);
                u32 rate_flags;
 
-               sband = local->hw.wiphy->bands[cbss->channel->band];
-
-               err = ieee80211_prep_channel(sdata, cbss);
-               if (err) {
-                       sta_info_free(local, new_sta);
-                       return -EINVAL;
-               }
-               shift = ieee80211_vif_get_shift(&sdata->vif);
-
                rcu_read_lock();
                chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
                if (WARN_ON(!chanctx_conf)) {
@@ -4668,8 +4687,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
        rcu_read_unlock();
 
+       if (WARN((sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD) &&
+                (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK),
+            "U-APSD not supported with HW_PS_NULLFUNC_STACK\n"))
+               sdata->vif.driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
+
        if (bss->wmm_used && bss->uapsd_supported &&
-           (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
+           (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD)) {
                assoc_data->uapsd = true;
                ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
        } else {
index d51f6b1..7c86a00 100644 (file)
@@ -263,12 +263,12 @@ static inline unsigned int
 minstrel_get_retry_count(struct minstrel_rate *mr,
                         struct ieee80211_tx_info *info)
 {
-       unsigned int retry = mr->adjusted_retry_count;
+       u8 retry = mr->adjusted_retry_count;
 
        if (info->control.use_rts)
-               retry = max(2U, min(mr->stats.retry_count_rtscts, retry));
+               retry = max_t(u8, 2, min(mr->stats.retry_count_rtscts, retry));
        else if (info->control.use_cts_prot)
-               retry = max(2U, min(mr->retry_count_cts, retry));
+               retry = max_t(u8, 2, min(mr->retry_count_cts, retry));
        return retry;
 }
 
index 97eca86..410efe6 100644 (file)
@@ -33,8 +33,8 @@ minstrel_ewma(int old, int new, int weight)
 
 struct minstrel_rate_stats {
        /* current / last sampling period attempts/success counters */
-       unsigned int attempts, last_attempts;
-       unsigned int success, last_success;
+       u16 attempts, last_attempts;
+       u16 success, last_success;
 
        /* total attempts/success counters */
        u64 att_hist, succ_hist;
@@ -46,8 +46,8 @@ struct minstrel_rate_stats {
        unsigned int cur_prob, probability;
 
        /* maximum retry counts */
-       unsigned int retry_count;
-       unsigned int retry_count_rtscts;
+       u8 retry_count;
+       u8 retry_count_rtscts;
 
        u8 sample_skipped;
        bool retry_updated;
@@ -55,14 +55,15 @@ struct minstrel_rate_stats {
 
 struct minstrel_rate {
        int bitrate;
-       int rix;
+
+       s8 rix;
+       u8 retry_count_cts;
+       u8 adjusted_retry_count;
 
        unsigned int perfect_tx_time;
        unsigned int ack_time;
 
        int sample_limit;
-       unsigned int retry_count_cts;
-       unsigned int adjusted_retry_count;
 
        struct minstrel_rate_stats stats;
 };
index 683b10f..fa5d870 100644 (file)
@@ -2598,7 +2598,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: {
                        struct ieee80211_supported_band *sband;
                        u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth;
-                       enum ieee80211_sta_rx_bandwidth new_bw;
+                       enum ieee80211_sta_rx_bandwidth max_bw, new_bw;
 
                        /* If it doesn't support 40 MHz it can't change ... */
                        if (!(rx->sta->sta.ht_cap.cap &
@@ -2606,13 +2606,18 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                                goto handled;
 
                        if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ)
-                               new_bw = IEEE80211_STA_RX_BW_20;
+                               max_bw = IEEE80211_STA_RX_BW_20;
                        else
-                               new_bw = ieee80211_sta_cur_vht_bw(rx->sta);
+                               max_bw = ieee80211_sta_cap_rx_bw(rx->sta);
+
+                       /* set cur_max_bandwidth and recalc sta bw */
+                       rx->sta->cur_max_bandwidth = max_bw;
+                       new_bw = ieee80211_sta_cur_vht_bw(rx->sta);
 
                        if (rx->sta->sta.bandwidth == new_bw)
                                goto handled;
 
+                       rx->sta->sta.bandwidth = new_bw;
                        sband = rx->local->hw.wiphy->bands[status->band];
 
                        rate_control_rate_update(local, sband, rx->sta,
index efeba56..06e6ac8 100644 (file)
@@ -34,19 +34,15 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
        struct cfg80211_chan_def new_vht_chandef = {};
        const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
        const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
-       const struct ieee80211_ht_operation *ht_oper;
        int secondary_channel_offset = -1;
 
        sec_chan_offs = elems->sec_chan_offs;
        wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
-       ht_oper = elems->ht_operation;
 
        if (sta_flags & (IEEE80211_STA_DISABLE_HT |
                         IEEE80211_STA_DISABLE_40MHZ)) {
                sec_chan_offs = NULL;
                wide_bw_chansw_ie = NULL;
-               /* only used for bandwidth here */
-               ht_oper = NULL;
        }
 
        if (sta_flags & IEEE80211_STA_DISABLE_VHT)
index a42f5b2..db8b07a 100644 (file)
@@ -1243,10 +1243,11 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
         * ends the poll/service period.
         */
        info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
-                      IEEE80211_TX_CTL_PS_RESPONSE |
                       IEEE80211_TX_STATUS_EOSP |
                       IEEE80211_TX_CTL_REQ_TX_STATUS;
 
+       info->control.flags |= IEEE80211_TX_CTRL_PS_RESPONSE;
+
        if (call_driver)
                drv_allow_buffered_frames(local, sta, BIT(tid), 1,
                                          reason, false);
@@ -1395,8 +1396,8 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                         * STA may still remain is PS mode after this frame
                         * exchange.
                         */
-                       info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
-                                      IEEE80211_TX_CTL_PS_RESPONSE;
+                       info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
+                       info->control.flags |= IEEE80211_TX_CTRL_PS_RESPONSE;
 
                        /*
                         * Use MoreData flag to indicate whether there are
index bb146f3..7d4e930 100644 (file)
@@ -664,13 +664,15 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
        struct ieee80211_supported_band *sband;
        int retry_count;
        int rates_idx;
-       bool acked;
+       bool acked, noack_success;
 
        rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
 
        sband = hw->wiphy->bands[info->band];
 
        acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
+       noack_success = !!(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED);
+
        if (pubsta) {
                struct sta_info *sta;
 
@@ -696,7 +698,7 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
                rate_control_tx_status_noskb(local, sband, sta, info);
        }
 
-       if (acked) {
+       if (acked || noack_success) {
                    local->dot11TransmittedFrameCount++;
                    if (!pubsta)
                            local->dot11MulticastTransmittedFrameCount++;
@@ -856,10 +858,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
         * Fragments are passed to low-level drivers as separate skbs, so these
         * are actually fragments, not frames. Update frame counters only for
         * the first fragment of the frame. */
-       if (info->flags & IEEE80211_TX_STAT_ACK) {
+       if ((info->flags & IEEE80211_TX_STAT_ACK) ||
+           (info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED)) {
                if (ieee80211_is_first_frag(hdr->seq_ctrl)) {
                        local->dot11TransmittedFrameCount++;
-                       if (is_multicast_ether_addr(hdr->addr1))
+                       if (is_multicast_ether_addr(ieee80211_get_DA(hdr)))
                                local->dot11MulticastTransmittedFrameCount++;
                        if (retry_count > 0)
                                local->dot11RetryCount++;
index 974ebe7..ad8cb4f 100644 (file)
@@ -1470,10 +1470,12 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
 
        /* Check if any channel in this sband supports at least 80 MHz */
        for (i = 0; i < sband->n_channels; i++) {
-               if (!(sband->channels[i].flags & IEEE80211_CHAN_NO_80MHZ)) {
-                       have_80mhz = true;
-                       break;
-               }
+               if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
+                                               IEEE80211_CHAN_NO_80MHZ))
+                       continue;
+
+               have_80mhz = true;
+               break;
        }
 
        if (sband->vht_cap.vht_supported && have_80mhz) {
@@ -1735,6 +1737,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        struct cfg80211_sched_scan_request *sched_scan_req;
        bool sched_scan_stopped = false;
 
+       /* nothing to do if HW shouldn't run */
+       if (!local->open_count)
+               goto wake_up;
+
 #ifdef CONFIG_PM
        if (local->suspended)
                local->resuming = true;
@@ -1756,9 +1762,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                reconfig_due_to_wowlan = true;
        }
 #endif
-       /* everything else happens only if HW was up & running */
-       if (!local->open_count)
-               goto wake_up;
 
        /*
         * Upon resume hardware can sometimes be goofy due to
@@ -2042,7 +2045,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
         * If this is for hw restart things are still running.
         * We may want to change that later, however.
         */
-       if (!local->suspended || reconfig_due_to_wowlan)
+       if (local->open_count && (!local->suspended || reconfig_due_to_wowlan))
                drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
 
        if (!local->suspended)
@@ -2054,7 +2057,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        mb();
        local->resuming = false;
 
-       if (!reconfig_due_to_wowlan)
+       if (local->open_count && !reconfig_due_to_wowlan)
                drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);
 
        list_for_each_entry(sdata, &local->interfaces, list) {
index bc9e8fc..85f9596 100644 (file)
@@ -269,51 +269,54 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
        sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta);
 }
 
-enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
+enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta)
 {
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
-       u32 cap = sta->sta.vht_cap.cap;
-       enum ieee80211_sta_rx_bandwidth bw;
+       struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap;
+       u32 cap_width;
 
-       if (!sta->sta.vht_cap.vht_supported) {
-               bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
-                               IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
-               goto check_max;
-       }
+       if (!vht_cap->vht_supported)
+               return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
+                               IEEE80211_STA_RX_BW_40 :
+                               IEEE80211_STA_RX_BW_20;
 
-       switch (sdata->vif.bss_conf.chandef.width) {
-       default:
-               WARN_ON_ONCE(1);
-               /* fall through */
+       cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+
+       if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ||
+           cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
+               return IEEE80211_STA_RX_BW_160;
+
+       return IEEE80211_STA_RX_BW_80;
+}
+
+static enum ieee80211_sta_rx_bandwidth
+ieee80211_chan_width_to_rx_bw(enum nl80211_chan_width width)
+{
+       switch (width) {
        case NL80211_CHAN_WIDTH_20_NOHT:
        case NL80211_CHAN_WIDTH_20:
-               bw = IEEE80211_STA_RX_BW_20;
-               break;
+               return IEEE80211_STA_RX_BW_20;
        case NL80211_CHAN_WIDTH_40:
-               bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
-                               IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
-               break;
+               return IEEE80211_STA_RX_BW_40;
+       case NL80211_CHAN_WIDTH_80:
+               return IEEE80211_STA_RX_BW_80;
        case NL80211_CHAN_WIDTH_160:
-               if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
-                               IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) {
-                       bw = IEEE80211_STA_RX_BW_160;
-                       break;
-               }
-               /* fall through */
        case NL80211_CHAN_WIDTH_80P80:
-               if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
-                               IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
-                       bw = IEEE80211_STA_RX_BW_160;
-                       break;
-               }
-               /* fall through */
-       case NL80211_CHAN_WIDTH_80:
-               bw = IEEE80211_STA_RX_BW_80;
+               return IEEE80211_STA_RX_BW_160;
+       default:
+               WARN_ON_ONCE(1);
+               return IEEE80211_STA_RX_BW_20;
        }
+}
+
+enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
+{
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       enum ieee80211_sta_rx_bandwidth bw;
+
+       bw = ieee80211_chan_width_to_rx_bw(sdata->vif.bss_conf.chandef.width);
+       bw = min(bw, ieee80211_sta_cap_rx_bw(sta));
+       bw = min(bw, sta->cur_max_bandwidth);
 
- check_max:
-       if (bw > sta->cur_max_bandwidth)
-               bw = sta->cur_max_bandwidth;
        return bw;
 }
 
index 53dda77..456e4c3 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/sched.h>
 #include <net/genetlink.h>
 #include <net/cfg80211.h>
+#include <net/rtnetlink.h>
 #include "nl80211.h"
 #include "core.h"
 #include "sysfs.h"
@@ -320,6 +321,20 @@ static void cfg80211_destroy_iface_wk(struct work_struct *work)
        rtnl_unlock();
 }
 
+static void cfg80211_sched_scan_stop_wk(struct work_struct *work)
+{
+       struct cfg80211_registered_device *rdev;
+
+       rdev = container_of(work, struct cfg80211_registered_device,
+                          sched_scan_stop_wk);
+
+       rtnl_lock();
+
+       __cfg80211_stop_sched_scan(rdev, false);
+
+       rtnl_unlock();
+}
+
 /* exported functions */
 
 struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
@@ -406,6 +421,7 @@ use_default_name:
        INIT_LIST_HEAD(&rdev->destroy_list);
        spin_lock_init(&rdev->destroy_list_lock);
        INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
+       INIT_WORK(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk);
 
 #ifdef CONFIG_CFG80211_DEFAULT_PS
        rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -560,6 +576,14 @@ int wiphy_register(struct wiphy *wiphy)
                                       BIT(NL80211_IFTYPE_MONITOR)))
                wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
 
+       if (WARN_ON((wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) &&
+                   (wiphy->regulatory_flags &
+                                       (REGULATORY_CUSTOM_REG |
+                                        REGULATORY_STRICT_REG |
+                                        REGULATORY_COUNTRY_IE_FOLLOW_POWER |
+                                        REGULATORY_COUNTRY_IE_IGNORE))))
+               return -EINVAL;
+
        if (WARN_ON(wiphy->coalesce &&
                    (!wiphy->coalesce->n_rules ||
                     !wiphy->coalesce->n_patterns) &&
@@ -778,6 +802,7 @@ void wiphy_unregister(struct wiphy *wiphy)
        flush_work(&rdev->event_work);
        cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
        flush_work(&rdev->destroy_work);
+       flush_work(&rdev->sched_scan_stop_wk);
 
 #ifdef CONFIG_PM
        if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
@@ -858,6 +883,7 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
                      struct wireless_dev *wdev)
 {
        struct net_device *dev = wdev->netdev;
+       struct cfg80211_sched_scan_request *sched_scan_req;
 
        ASSERT_RTNL();
        ASSERT_WDEV_LOCK(wdev);
@@ -868,7 +894,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
-               if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev)
+               sched_scan_req = rtnl_dereference(rdev->sched_scan_req);
+               if (sched_scan_req && dev == sched_scan_req->dev)
                        __cfg80211_stop_sched_scan(rdev, false);
 
 #ifdef CONFIG_CFG80211_WEXT
@@ -937,12 +964,17 @@ void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
 }
 EXPORT_SYMBOL(cfg80211_stop_iface);
 
+static const struct rtnl_link_ops wireless_link_ops = {
+       .kind = "wlan",
+};
+
 static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                                         unsigned long state, void *ptr)
 {
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_registered_device *rdev;
+       struct cfg80211_sched_scan_request *sched_scan_req;
 
        if (!wdev)
                return NOTIFY_DONE;
@@ -954,6 +986,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
        switch (state) {
        case NETDEV_POST_INIT:
                SET_NETDEV_DEVTYPE(dev, &wiphy_type);
+               dev->rtnl_link_ops = &wireless_link_ops;
                break;
        case NETDEV_REGISTER:
                /*
@@ -1007,8 +1040,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                        ___cfg80211_scan_done(rdev, false);
                }
 
-               if (WARN_ON(rdev->sched_scan_req &&
-                           rdev->sched_scan_req->dev == wdev->netdev)) {
+               sched_scan_req = rtnl_dereference(rdev->sched_scan_req);
+               if (WARN_ON(sched_scan_req &&
+                           sched_scan_req->dev == wdev->netdev)) {
                        __cfg80211_stop_sched_scan(rdev, false);
                }
 
index faa5b16..801cd49 100644 (file)
@@ -36,6 +36,13 @@ struct cfg80211_registered_device {
         * the country on the country IE changed. */
        char country_ie_alpha2[2];
 
+       /*
+        * the driver requests the regulatory core to set this regulatory
+        * domain as the wiphy's. Only used for %REGULATORY_WIPHY_SELF_MANAGED
+        * devices using the regulatory_set_wiphy_regd() API
+        */
+       const struct ieee80211_regdomain *requested_regd;
+
        /* If a Country IE has been received this tells us the environment
         * which its telling us its in. This defaults to ENVIRON_ANY */
        enum environment_cap env;
@@ -63,7 +70,7 @@ struct cfg80211_registered_device {
        u32 bss_generation;
        struct cfg80211_scan_request *scan_req; /* protected by RTNL */
        struct sk_buff *scan_msg;
-       struct cfg80211_sched_scan_request *sched_scan_req;
+       struct cfg80211_sched_scan_request __rcu *sched_scan_req;
        unsigned long suspend_at;
        struct work_struct scan_done_wk;
        struct work_struct sched_scan_results_wk;
@@ -84,6 +91,8 @@ struct cfg80211_registered_device {
        struct list_head destroy_list;
        struct work_struct destroy_work;
 
+       struct work_struct sched_scan_stop_wk;
+
        /* must be last because of the way we do wiphy_priv(),
         * and it should at least be aligned to NETDEV_ALIGN */
        struct wiphy wiphy __aligned(NETDEV_ALIGN);
index 7ca4b51..ad3e294 100644 (file)
@@ -59,13 +59,13 @@ enum nl80211_multicast_groups {
 };
 
 static const struct genl_multicast_group nl80211_mcgrps[] = {
-       [NL80211_MCGRP_CONFIG] = { .name = "config", },
-       [NL80211_MCGRP_SCAN] = { .name = "scan", },
-       [NL80211_MCGRP_REGULATORY] = { .name = "regulatory", },
-       [NL80211_MCGRP_MLME] = { .name = "mlme", },
-       [NL80211_MCGRP_VENDOR] = { .name = "vendor", },
+       [NL80211_MCGRP_CONFIG] = { .name = NL80211_MULTICAST_GROUP_CONFIG },
+       [NL80211_MCGRP_SCAN] = { .name = NL80211_MULTICAST_GROUP_SCAN },
+       [NL80211_MCGRP_REGULATORY] = { .name = NL80211_MULTICAST_GROUP_REG },
+       [NL80211_MCGRP_MLME] = { .name = NL80211_MULTICAST_GROUP_MLME },
+       [NL80211_MCGRP_VENDOR] = { .name = NL80211_MULTICAST_GROUP_VENDOR },
 #ifdef CONFIG_NL80211_TESTMODE
-       [NL80211_MCGRP_TESTMODE] = { .name = "testmode", }
+       [NL80211_MCGRP_TESTMODE] = { .name = NL80211_MULTICAST_GROUP_TESTMODE }
 #endif
 };
 
@@ -396,6 +396,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
        [NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
        [NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
+       [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -1701,6 +1702,15 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                               rdev->wiphy.max_num_csa_counters))
                        goto nla_put_failure;
 
+               if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+                   nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+                       goto nla_put_failure;
+
+               if (nla_put(msg, NL80211_ATTR_EXT_FEATURES,
+                           sizeof(rdev->wiphy.ext_features),
+                           rdev->wiphy.ext_features))
+                       goto nla_put_failure;
+
                /* done */
                state->split_start = 0;
                break;
@@ -5327,42 +5337,20 @@ static int nl80211_update_mesh_config(struct sk_buff *skb,
        return err;
 }
 
-static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom,
+                             struct sk_buff *msg)
 {
-       const struct ieee80211_regdomain *regdom;
-       struct sk_buff *msg;
-       void *hdr = NULL;
        struct nlattr *nl_reg_rules;
        unsigned int i;
 
-       if (!cfg80211_regdomain)
-               return -EINVAL;
-
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!msg)
-               return -ENOBUFS;
-
-       hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
-                            NL80211_CMD_GET_REG);
-       if (!hdr)
-               goto put_failure;
-
-       if (reg_last_request_cell_base() &&
-           nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
-                       NL80211_USER_REG_HINT_CELL_BASE))
-               goto nla_put_failure;
-
-       rcu_read_lock();
-       regdom = rcu_dereference(cfg80211_regdomain);
-
        if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) ||
            (regdom->dfs_region &&
             nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region)))
-               goto nla_put_failure_rcu;
+               goto nla_put_failure;
 
        nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
        if (!nl_reg_rules)
-               goto nla_put_failure_rcu;
+               goto nla_put_failure;
 
        for (i = 0; i < regdom->n_reg_rules; i++) {
                struct nlattr *nl_reg_rule;
@@ -5377,7 +5365,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
 
                nl_reg_rule = nla_nest_start(msg, i);
                if (!nl_reg_rule)
-                       goto nla_put_failure_rcu;
+                       goto nla_put_failure;
 
                max_bandwidth_khz = freq_range->max_bandwidth_khz;
                if (!max_bandwidth_khz)
@@ -5398,13 +5386,74 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
                                power_rule->max_eirp) ||
                    nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME,
                                reg_rule->dfs_cac_ms))
-                       goto nla_put_failure_rcu;
+                       goto nla_put_failure;
 
                nla_nest_end(msg, nl_reg_rule);
        }
-       rcu_read_unlock();
 
        nla_nest_end(msg, nl_reg_rules);
+       return 0;
+
+nla_put_failure:
+       return -EMSGSIZE;
+}
+
+static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
+{
+       const struct ieee80211_regdomain *regdom = NULL;
+       struct cfg80211_registered_device *rdev;
+       struct wiphy *wiphy = NULL;
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOBUFS;
+
+       hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+                            NL80211_CMD_GET_REG);
+       if (!hdr)
+               goto put_failure;
+
+       if (info->attrs[NL80211_ATTR_WIPHY]) {
+               bool self_managed;
+
+               rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
+               if (IS_ERR(rdev)) {
+                       nlmsg_free(msg);
+                       return PTR_ERR(rdev);
+               }
+
+               wiphy = &rdev->wiphy;
+               self_managed = wiphy->regulatory_flags &
+                              REGULATORY_WIPHY_SELF_MANAGED;
+               regdom = get_wiphy_regdom(wiphy);
+
+               /* a self-managed-reg device must have a private regdom */
+               if (WARN_ON(!regdom && self_managed)) {
+                       nlmsg_free(msg);
+                       return -EINVAL;
+               }
+
+               if (regdom &&
+                   nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
+                       goto nla_put_failure;
+       }
+
+       if (!wiphy && reg_last_request_cell_base() &&
+           nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
+                       NL80211_USER_REG_HINT_CELL_BASE))
+               goto nla_put_failure;
+
+       rcu_read_lock();
+
+       if (!regdom)
+               regdom = rcu_dereference(cfg80211_regdomain);
+
+       if (nl80211_put_regdom(regdom, msg))
+               goto nla_put_failure_rcu;
+
+       rcu_read_unlock();
 
        genlmsg_end(msg, hdr);
        return genlmsg_reply(msg, info);
@@ -5418,6 +5467,83 @@ put_failure:
        return -EMSGSIZE;
 }
 
+static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb,
+                              u32 seq, int flags, struct wiphy *wiphy,
+                              const struct ieee80211_regdomain *regdom)
+{
+       void *hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags,
+                                  NL80211_CMD_GET_REG);
+
+       if (!hdr)
+               return -1;
+
+       genl_dump_check_consistent(cb, hdr, &nl80211_fam);
+
+       if (nl80211_put_regdom(regdom, msg))
+               goto nla_put_failure;
+
+       if (!wiphy && reg_last_request_cell_base() &&
+           nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
+                       NL80211_USER_REG_HINT_CELL_BASE))
+               goto nla_put_failure;
+
+       if (wiphy &&
+           nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
+               goto nla_put_failure;
+
+       if (wiphy && wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+           nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+               goto nla_put_failure;
+
+       return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int nl80211_get_reg_dump(struct sk_buff *skb,
+                               struct netlink_callback *cb)
+{
+       const struct ieee80211_regdomain *regdom = NULL;
+       struct cfg80211_registered_device *rdev;
+       int err, reg_idx, start = cb->args[2];
+
+       rtnl_lock();
+
+       if (cfg80211_regdomain && start == 0) {
+               err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
+                                         NLM_F_MULTI, NULL,
+                                         rtnl_dereference(cfg80211_regdomain));
+               if (err < 0)
+                       goto out_err;
+       }
+
+       /* the global regdom is idx 0 */
+       reg_idx = 1;
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               regdom = get_wiphy_regdom(&rdev->wiphy);
+               if (!regdom)
+                       continue;
+
+               if (++reg_idx <= start)
+                       continue;
+
+               err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
+                                         NLM_F_MULTI, &rdev->wiphy, regdom);
+               if (err < 0) {
+                       reg_idx--;
+                       break;
+               }
+       }
+
+       cb->args[2] = reg_idx;
+       err = skb->len;
+out_err:
+       rtnl_unlock();
+       return err;
+}
+
 static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
@@ -6069,6 +6195,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_sched_scan_request *sched_scan_req;
        int err;
 
        if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
@@ -6078,27 +6205,32 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        if (rdev->sched_scan_req)
                return -EINPROGRESS;
 
-       rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
-                                                       info->attrs);
-       err = PTR_ERR_OR_ZERO(rdev->sched_scan_req);
+       sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
+                                                 info->attrs);
+
+       err = PTR_ERR_OR_ZERO(sched_scan_req);
        if (err)
                goto out_err;
 
-       err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req);
+       err = rdev_sched_scan_start(rdev, dev, sched_scan_req);
        if (err)
                goto out_free;
 
-       rdev->sched_scan_req->dev = dev;
-       rdev->sched_scan_req->wiphy = &rdev->wiphy;
+       sched_scan_req->dev = dev;
+       sched_scan_req->wiphy = &rdev->wiphy;
+
+       if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
+               sched_scan_req->owner_nlportid = info->snd_portid;
+
+       rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req);
 
        nl80211_send_sched_scan(rdev, dev,
                                NL80211_CMD_START_SCHED_SCAN);
        return 0;
 
 out_free:
-       kfree(rdev->sched_scan_req);
+       kfree(sched_scan_req);
 out_err:
-       rdev->sched_scan_req = NULL;
        return err;
 }
 
@@ -10225,7 +10357,8 @@ static const struct genl_ops nl80211_ops[] = {
        },
        {
                .cmd = NL80211_CMD_GET_REG,
-               .doit = nl80211_get_reg,
+               .doit = nl80211_get_reg_do,
+               .dumpit = nl80211_get_reg_dump,
                .policy = nl80211_policy,
                .internal_flags = NL80211_FLAG_NEED_RTNL,
                /* can be retrieved by unprivileged users */
@@ -10939,25 +11072,9 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
                                NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
-/*
- * This can happen on global regulatory changes or device specific settings
- * based on custom world regulatory domains.
- */
-void nl80211_send_reg_change_event(struct regulatory_request *request)
+static bool nl80211_reg_change_event_fill(struct sk_buff *msg,
+                                         struct regulatory_request *request)
 {
-       struct sk_buff *msg;
-       void *hdr;
-
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!msg)
-               return;
-
-       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
-       if (!hdr) {
-               nlmsg_free(msg);
-               return;
-       }
-
        /* Userspace can always count this one always being set */
        if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
                goto nla_put_failure;
@@ -10983,8 +11100,46 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
                        goto nla_put_failure;
        }
 
-       if (request->wiphy_idx != WIPHY_IDX_INVALID &&
-           nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
+       if (request->wiphy_idx != WIPHY_IDX_INVALID) {
+               struct wiphy *wiphy = wiphy_idx_to_wiphy(request->wiphy_idx);
+
+               if (wiphy &&
+                   nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
+                       goto nla_put_failure;
+
+               if (wiphy &&
+                   wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+                   nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+                       goto nla_put_failure;
+       }
+
+       return true;
+
+nla_put_failure:
+       return false;
+}
+
+/*
+ * This can happen on global regulatory changes or device specific settings
+ * based on custom regulatory domains.
+ */
+void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
+                                    struct regulatory_request *request)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, cmd_id);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       if (nl80211_reg_change_event_fill(msg, request) == false)
                goto nla_put_failure;
 
        genlmsg_end(msg, hdr);
@@ -12471,6 +12626,13 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
 
        list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
                bool schedule_destroy_work = false;
+               bool schedule_scan_stop = false;
+               struct cfg80211_sched_scan_request *sched_scan_req =
+                       rcu_dereference(rdev->sched_scan_req);
+
+               if (sched_scan_req && notify->portid &&
+                   sched_scan_req->owner_nlportid == notify->portid)
+                       schedule_scan_stop = true;
 
                list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) {
                        cfg80211_mlme_unregister_socket(wdev, notify->portid);
@@ -12501,6 +12663,12 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
                                spin_unlock(&rdev->destroy_list_lock);
                                schedule_work(&rdev->destroy_work);
                        }
+               } else if (schedule_scan_stop) {
+                       sched_scan_req->owner_nlportid = 0;
+
+                       if (rdev->ops->sched_scan_stop &&
+                           rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
+                               schedule_work(&rdev->sched_scan_stop_wk);
                }
        }
 
index 7ad70d6..84d4edf 100644 (file)
@@ -17,7 +17,21 @@ 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_send_reg_change_event(struct regulatory_request *request);
+void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
+                                    struct regulatory_request *request);
+
+static inline void
+nl80211_send_reg_change_event(struct regulatory_request *request)
+{
+       nl80211_common_reg_change_event(NL80211_CMD_REG_CHANGE, request);
+}
+
+static inline void
+nl80211_send_wiphy_reg_change_event(struct regulatory_request *request)
+{
+       nl80211_common_reg_change_event(NL80211_CMD_WIPHY_REG_CHANGE, request);
+}
+
 void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
                          struct net_device *netdev,
                          const u8 *buf, size_t len, gfp_t gfp);
index 7b83098..8d232b9 100644 (file)
@@ -109,7 +109,7 @@ static struct regulatory_request core_request_world = {
  * protected by RTNL (and can be accessed with RCU protection)
  */
 static struct regulatory_request __rcu *last_request =
-       (void __rcu *)&core_request_world;
+       (void __force __rcu *)&core_request_world;
 
 /* To trigger userspace events */
 static struct platform_device *reg_pdev;
@@ -142,7 +142,7 @@ static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
        return rtnl_dereference(cfg80211_regdomain);
 }
 
-static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
+const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
 {
        return rtnl_dereference(wiphy->regd);
 }
@@ -1307,6 +1307,9 @@ static bool ignore_reg_update(struct wiphy *wiphy,
 {
        struct regulatory_request *lr = get_last_request();
 
+       if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+               return true;
+
        if (!lr) {
                REG_DBG_PRINT("Ignoring regulatory request set by %s "
                              "since last_request is not set\n",
@@ -1683,8 +1686,12 @@ static void handle_channel_custom(struct wiphy *wiphy,
        if (IS_ERR(reg_rule)) {
                REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
                              chan->center_freq);
-               chan->orig_flags |= IEEE80211_CHAN_DISABLED;
-               chan->flags = chan->orig_flags;
+               if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {
+                       chan->flags |= IEEE80211_CHAN_DISABLED;
+               } else {
+                       chan->orig_flags |= IEEE80211_CHAN_DISABLED;
+                       chan->flags = chan->orig_flags;
+               }
                return;
        }
 
@@ -1709,7 +1716,13 @@ static void handle_channel_custom(struct wiphy *wiphy,
        chan->dfs_state = NL80211_DFS_USABLE;
 
        chan->beacon_found = false;
-       chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
+
+       if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+               chan->flags = chan->orig_flags | bw_flags |
+                             map_regdom_flags(reg_rule->flags);
+       else
+               chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
+
        chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
        chan->max_reg_power = chan->max_power =
                (int) MBM_TO_DBM(power_rule->max_eirp);
@@ -2153,11 +2166,52 @@ static void reg_process_pending_beacon_hints(void)
        spin_unlock_bh(&reg_pending_beacons_lock);
 }
 
+static void reg_process_self_managed_hints(void)
+{
+       struct cfg80211_registered_device *rdev;
+       struct wiphy *wiphy;
+       const struct ieee80211_regdomain *tmp;
+       const struct ieee80211_regdomain *regd;
+       enum ieee80211_band band;
+       struct regulatory_request request = {};
+
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               wiphy = &rdev->wiphy;
+
+               spin_lock(&reg_requests_lock);
+               regd = rdev->requested_regd;
+               rdev->requested_regd = NULL;
+               spin_unlock(&reg_requests_lock);
+
+               if (regd == NULL)
+                       continue;
+
+               tmp = get_wiphy_regdom(wiphy);
+               rcu_assign_pointer(wiphy->regd, regd);
+               rcu_free_regdom(tmp);
+
+               for (band = 0; band < IEEE80211_NUM_BANDS; band++)
+                       handle_band_custom(wiphy, wiphy->bands[band], regd);
+
+               reg_process_ht_flags(wiphy);
+
+               request.wiphy_idx = get_wiphy_idx(wiphy);
+               request.alpha2[0] = regd->alpha2[0];
+               request.alpha2[1] = regd->alpha2[1];
+               request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
+
+               nl80211_send_wiphy_reg_change_event(&request);
+       }
+
+       reg_check_channels();
+}
+
 static void reg_todo(struct work_struct *work)
 {
        rtnl_lock();
        reg_process_pending_hints();
        reg_process_pending_beacon_hints();
+       reg_process_self_managed_hints();
        rtnl_unlock();
 }
 
@@ -2438,6 +2492,8 @@ static void restore_regulatory_settings(bool reset_user)
        world_alpha2[1] = cfg80211_world_regdom->alpha2[1];
 
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+                       continue;
                if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)
                        restore_custom_reg_settings(&rdev->wiphy);
        }
@@ -2841,10 +2897,52 @@ int set_regdom(const struct ieee80211_regdomain *rd)
        return 0;
 }
 
+int regulatory_set_wiphy_regd(struct wiphy *wiphy,
+                             struct ieee80211_regdomain *rd)
+{
+       const struct ieee80211_regdomain *regd;
+       const struct ieee80211_regdomain *prev_regd;
+       struct cfg80211_registered_device *rdev;
+
+       if (WARN_ON(!wiphy || !rd))
+               return -EINVAL;
+
+       if (WARN(!(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED),
+                "wiphy should have REGULATORY_WIPHY_SELF_MANAGED\n"))
+               return -EPERM;
+
+       if (WARN(!is_valid_rd(rd), "Invalid regulatory domain detected\n")) {
+               print_regdomain_info(rd);
+               return -EINVAL;
+       }
+
+       regd = reg_copy_regd(rd);
+       if (IS_ERR(regd))
+               return PTR_ERR(regd);
+
+       rdev = wiphy_to_rdev(wiphy);
+
+       spin_lock(&reg_requests_lock);
+       prev_regd = rdev->requested_regd;
+       rdev->requested_regd = regd;
+       spin_unlock(&reg_requests_lock);
+
+       kfree(prev_regd);
+
+       schedule_work(&reg_work);
+       return 0;
+}
+EXPORT_SYMBOL(regulatory_set_wiphy_regd);
+
 void wiphy_regulatory_register(struct wiphy *wiphy)
 {
        struct regulatory_request *lr;
 
+       /* self-managed devices ignore external hints */
+       if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+               wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
+                                          REGULATORY_COUNTRY_IE_IGNORE;
+
        if (!reg_dev_ignore_cell_hint(wiphy))
                reg_num_devs_support_basehint++;
 
index 5e48031..4b45d6e 100644 (file)
@@ -38,6 +38,7 @@ unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
                                   const struct ieee80211_reg_rule *rule);
 
 bool reg_last_request_cell_base(void);
+const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy);
 
 /**
  * regulatory_hint_found_beacon - hints a beacon was found on a channel
index bda39f1..c705c3e 100644 (file)
@@ -257,7 +257,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
 
        rtnl_lock();
 
-       request = rdev->sched_scan_req;
+       request = rtnl_dereference(rdev->sched_scan_req);
 
        /* we don't have sched_scan_req anymore if the scan is stopping */
        if (request) {
@@ -279,7 +279,8 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy)
 {
        trace_cfg80211_sched_scan_results(wiphy);
        /* ignore if we're not scanning */
-       if (wiphy_to_rdev(wiphy)->sched_scan_req)
+
+       if (rcu_access_pointer(wiphy_to_rdev(wiphy)->sched_scan_req))
                queue_work(cfg80211_wq,
                           &wiphy_to_rdev(wiphy)->sched_scan_results_wk);
 }
@@ -308,6 +309,7 @@ EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
 int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
                               bool driver_initiated)
 {
+       struct cfg80211_sched_scan_request *sched_scan_req;
        struct net_device *dev;
 
        ASSERT_RTNL();
@@ -315,7 +317,8 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
        if (!rdev->sched_scan_req)
                return -ENOENT;
 
-       dev = rdev->sched_scan_req->dev;
+       sched_scan_req = rtnl_dereference(rdev->sched_scan_req);
+       dev = sched_scan_req->dev;
 
        if (!driver_initiated) {
                int err = rdev_sched_scan_stop(rdev, dev);
@@ -325,8 +328,8 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
 
        nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED);
 
-       kfree(rdev->sched_scan_req);
-       rdev->sched_scan_req = NULL;
+       RCU_INIT_POINTER(rdev->sched_scan_req, NULL);
+       kfree_rcu(sched_scan_req, rcu_head);
 
        return 0;
 }