Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac802...
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / mwifiex / cfg80211.c
index e42b266a023a2c0df18e7cd85fd7dbbb3cf07d45..8a752637f0ce8cb9b4b3d3dad91e0ecb77b77b52 100644 (file)
 #include "cfg80211.h"
 #include "main.h"
 
+static char *reg_alpha2;
+module_param(reg_alpha2, charp, 0);
+
 static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = {
        {
-               .max = 2, .types = BIT(NL80211_IFTYPE_STATION),
+               .max = 2, .types = BIT(NL80211_IFTYPE_STATION) |
+                                  BIT(NL80211_IFTYPE_P2P_GO) |
+                                  BIT(NL80211_IFTYPE_P2P_CLIENT),
        },
        {
                .max = 1, .types = BIT(NL80211_IFTYPE_AP),
@@ -186,6 +191,7 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        struct sk_buff *skb;
        u16 pkt_len;
        const struct ieee80211_mgmt *mgmt;
+       struct mwifiex_txinfo *tx_info;
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
 
        if (!buf || !len) {
@@ -213,6 +219,10 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                return -ENOMEM;
        }
 
+       tx_info = MWIFIEX_SKB_TXCB(skb);
+       tx_info->bss_num = priv->bss_num;
+       tx_info->bss_type = priv->bss_type;
+
        mwifiex_form_mgmt_frame(skb, buf, len);
        mwifiex_queue_tx_pkt(priv, skb);
 
@@ -232,16 +242,20 @@ mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
                                     u16 frame_type, bool reg)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
+       u32 mask;
 
        if (reg)
-               priv->mgmt_frame_mask |= BIT(frame_type >> 4);
+               mask = priv->mgmt_frame_mask | BIT(frame_type >> 4);
        else
-               priv->mgmt_frame_mask &= ~BIT(frame_type >> 4);
+               mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4);
 
-       mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG,
-                              HostCmd_ACT_GEN_SET, 0, &priv->mgmt_frame_mask);
-
-       wiphy_dbg(wiphy, "info: mgmt frame registered\n");
+       if (mask != priv->mgmt_frame_mask) {
+               priv->mgmt_frame_mask = mask;
+               mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG,
+                                      HostCmd_ACT_GEN_SET, 0,
+                                      &priv->mgmt_frame_mask);
+               wiphy_dbg(wiphy, "info: mgmt frame registered\n");
+       }
 }
 
 /*
@@ -1231,6 +1245,51 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy,
        return 0;
 }
 
+/* cfg80211 operation handler for del_station.
+ * Function deauthenticates station which value is provided in mac parameter.
+ * If mac is NULL/broadcast, all stations in associated station list are
+ * deauthenticated. If bss is not started or there are no stations in
+ * associated stations list, no action is taken.
+ */
+static int
+mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
+                            u8 *mac)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       struct mwifiex_sta_node *sta_node;
+       unsigned long flags;
+
+       if (list_empty(&priv->sta_list) || !priv->bss_started)
+               return 0;
+
+       if (!mac || is_broadcast_ether_addr(mac)) {
+               wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__);
+               list_for_each_entry(sta_node, &priv->sta_list, list) {
+                       if (mwifiex_send_cmd_sync(priv,
+                                                 HostCmd_CMD_UAP_STA_DEAUTH,
+                                                 HostCmd_ACT_GEN_SET, 0,
+                                                 sta_node->mac_addr))
+                               return -1;
+                       mwifiex_uap_del_sta_data(priv, sta_node);
+               }
+       } else {
+               wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, mac);
+               spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+               sta_node = mwifiex_get_sta_entry(priv, mac);
+               spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+               if (sta_node) {
+                       if (mwifiex_send_cmd_sync(priv,
+                                                 HostCmd_CMD_UAP_STA_DEAUTH,
+                                                 HostCmd_ACT_GEN_SET, 0,
+                                                 sta_node->mac_addr))
+                               return -1;
+                       mwifiex_uap_del_sta_data(priv, sta_node);
+               }
+       }
+
+       return 0;
+}
+
 static int
 mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
 {
@@ -1859,6 +1918,7 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
        int i, offset, ret;
        struct ieee80211_channel *chan;
        struct ieee_types_header *ie;
+       struct mwifiex_user_scan_cfg *user_scan_cfg;
 
        wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name);
 
@@ -1869,20 +1929,22 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
                return -EBUSY;
        }
 
-       if (priv->user_scan_cfg) {
+       /* Block scan request if scan operation or scan cleanup when interface
+        * is disabled is in process
+        */
+       if (priv->scan_request || priv->scan_aborting) {
                dev_err(priv->adapter->dev, "cmd: Scan already in process..\n");
                return -EBUSY;
        }
 
-       priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg),
-                                     GFP_KERNEL);
-       if (!priv->user_scan_cfg)
+       user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL);
+       if (!user_scan_cfg)
                return -ENOMEM;
 
        priv->scan_request = request;
 
-       priv->user_scan_cfg->num_ssids = request->n_ssids;
-       priv->user_scan_cfg->ssid_list = request->ssids;
+       user_scan_cfg->num_ssids = request->n_ssids;
+       user_scan_cfg->ssid_list = request->ssids;
 
        if (request->ie && request->ie_len) {
                offset = 0;
@@ -1902,25 +1964,25 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
        for (i = 0; i < min_t(u32, request->n_channels,
                              MWIFIEX_USER_SCAN_CHAN_MAX); i++) {
                chan = request->channels[i];
-               priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
-               priv->user_scan_cfg->chan_list[i].radio_type = chan->band;
+               user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
+               user_scan_cfg->chan_list[i].radio_type = chan->band;
 
                if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
-                       priv->user_scan_cfg->chan_list[i].scan_type =
+                       user_scan_cfg->chan_list[i].scan_type =
                                                MWIFIEX_SCAN_TYPE_PASSIVE;
                else
-                       priv->user_scan_cfg->chan_list[i].scan_type =
+                       user_scan_cfg->chan_list[i].scan_type =
                                                MWIFIEX_SCAN_TYPE_ACTIVE;
 
-               priv->user_scan_cfg->chan_list[i].scan_time = 0;
+               user_scan_cfg->chan_list[i].scan_time = 0;
        }
 
-       ret = mwifiex_scan_networks(priv, priv->user_scan_cfg);
+       ret = mwifiex_scan_networks(priv, user_scan_cfg);
+       kfree(user_scan_cfg);
        if (ret) {
                dev_err(priv->adapter->dev, "scan failed: %d\n", ret);
+               priv->scan_aborting = false;
                priv->scan_request = NULL;
-               kfree(priv->user_scan_cfg);
-               priv->user_scan_cfg = NULL;
                return ret;
        }
 
@@ -2247,8 +2309,7 @@ EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
 
 #ifdef CONFIG_PM
 static bool
-mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat,
-                            s8 *byte_seq)
+mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq)
 {
        int j, k, valid_byte_cnt = 0;
        bool dont_care_byte = false;
@@ -2419,6 +2480,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
        .change_beacon = mwifiex_cfg80211_change_beacon,
        .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config,
        .set_antenna = mwifiex_cfg80211_set_antenna,
+       .del_station = mwifiex_cfg80211_del_station,
 #ifdef CONFIG_PM
        .suspend = mwifiex_cfg80211_suspend,
        .resume = mwifiex_cfg80211_resume,
@@ -2426,6 +2488,27 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
 #endif
 };
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support mwifiex_wowlan_support = {
+       .flags = WIPHY_WOWLAN_MAGIC_PKT,
+       .n_patterns = MWIFIEX_MAX_FILTERS,
+       .pattern_min_len = 1,
+       .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
+       .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN,
+};
+#endif
+
+static bool mwifiex_is_valid_alpha2(const char *alpha2)
+{
+       if (!alpha2 || strlen(alpha2) != 2)
+               return false;
+
+       if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
+               return true;
+
+       return false;
+}
+
 /*
  * This function registers the device with CFG802.11 subsystem.
  *
@@ -2478,16 +2561,13 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
                        WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
                        WIPHY_FLAG_AP_UAPSD |
                        WIPHY_FLAG_CUSTOM_REGULATORY |
+                       WIPHY_FLAG_STRICT_REGULATORY |
                        WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
 
        wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom);
 
 #ifdef CONFIG_PM
-       wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT;
-       wiphy->wowlan.n_patterns = MWIFIEX_MAX_FILTERS;
-       wiphy->wowlan.pattern_min_len = 1;
-       wiphy->wowlan.pattern_max_len = MWIFIEX_MAX_PATTERN_LEN;
-       wiphy->wowlan.max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN;
+       wiphy->wowlan = &mwifiex_wowlan_support;
 #endif
 
        wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
@@ -2519,10 +2599,16 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
                wiphy_free(wiphy);
                return ret;
        }
-       country_code = mwifiex_11d_code_2_region(priv->adapter->region_code);
-       if (country_code)
-               dev_info(adapter->dev,
-                        "ignoring F/W country code %2.2s\n", country_code);
+
+       if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) {
+               wiphy_info(wiphy, "driver hint alpha2: %2.2s\n", reg_alpha2);
+               regulatory_hint(wiphy, reg_alpha2);
+       } else {
+               country_code = mwifiex_11d_code_2_region(adapter->region_code);
+               if (country_code)
+                       wiphy_info(wiphy, "ignoring F/W country code %2.2s\n",
+                                  country_code);
+       }
 
        adapter->wiphy = wiphy;
        return ret;