Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / libertas / cfg.c
index b456a53b64b19b22aa3442b13ca1a2859ea622c6..d1d84e0e30fcfd2e6a19aa11dbdc6e6db2c77112 100644 (file)
@@ -19,6 +19,7 @@
 #include "decl.h"
 #include "cfg.h"
 #include "cmd.h"
+#include "mesh.h"
 
 
 #define CHAN2G(_channel, _freq, _flags) {        \
@@ -442,13 +443,16 @@ static int lbs_cfg_set_channel(struct wiphy *wiphy,
        struct lbs_private *priv = wiphy_priv(wiphy);
        int ret = -ENOTSUPP;
 
-       lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d",
-                          channel->center_freq, channel_type);
+       lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d, type %d",
+                          netdev_name(netdev), channel->center_freq, channel_type);
 
        if (channel_type != NL80211_CHAN_NO_HT)
                goto out;
 
-       ret = lbs_set_channel(priv, channel->hw_value);
+       if (netdev == priv->mesh_dev)
+               ret = lbs_mesh_set_channel(priv, channel->hw_value);
+       else
+               ret = lbs_set_channel(priv, channel->hw_value);
 
  out:
        lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
@@ -481,6 +485,7 @@ static int lbs_cfg_set_channel(struct wiphy *wiphy,
 static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
        struct cmd_header *resp)
 {
+       struct cfg80211_bss *bss;
        struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp;
        int bsssize;
        const u8 *pos;
@@ -628,12 +633,14 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
                                     LBS_SCAN_RSSI_TO_MBM(rssi)/100);
 
                        if (channel &&
-                           !(channel->flags & IEEE80211_CHAN_DISABLED))
-                               cfg80211_inform_bss(wiphy, channel,
-                                       bssid, le64_to_cpu(*(__le64 *)tsfdesc),
+                           !(channel->flags & IEEE80211_CHAN_DISABLED)) {
+                               bss = cfg80211_inform_bss(wiphy, channel,
+                                       bssid, get_unaligned_le64(tsfdesc),
                                        capa, intvl, ie, ielen,
                                        LBS_SCAN_RSSI_TO_MBM(rssi),
                                        GFP_KERNEL);
+                               cfg80211_put_bss(bss);
+                       }
                } else
                        lbs_deb_scan("scan response: missing BSS channel IE\n");
 
@@ -691,7 +698,7 @@ static void lbs_scan_worker(struct work_struct *work)
        tlv = scan_cmd->tlvbuffer;
 
        /* add SSID TLV */
-       if (priv->scan_req->n_ssids)
+       if (priv->scan_req->n_ssids && priv->scan_req->ssids[0].ssid_len > 0)
                tlv += lbs_add_ssid_tlv(tlv,
                                        priv->scan_req->ssids[0].ssid,
                                        priv->scan_req->ssids[0].ssid_len);
@@ -708,7 +715,7 @@ static void lbs_scan_worker(struct work_struct *work)
 
        if (priv->scan_channel < priv->scan_req->n_channels) {
                cancel_delayed_work(&priv->scan_work);
-               if (!priv->stopping)
+               if (netif_running(priv->dev))
                        queue_delayed_work(priv->work_thread, &priv->scan_work,
                                msecs_to_jiffies(300));
        }
@@ -724,16 +731,9 @@ static void lbs_scan_worker(struct work_struct *work)
                le16_to_cpu(scan_cmd->hdr.size),
                lbs_ret_scan, 0);
 
-       if (priv->scan_channel >= priv->scan_req->n_channels) {
+       if (priv->scan_channel >= priv->scan_req->n_channels)
                /* Mark scan done */
-               if (priv->internal_scan)
-                       kfree(priv->scan_req);
-               else
-                       cfg80211_scan_done(priv->scan_req, false);
-
-               priv->scan_req = NULL;
-               priv->last_scan = jiffies;
-       }
+               lbs_scan_done(priv);
 
        /* Restart network */
        if (carrier)
@@ -771,6 +771,21 @@ static void _internal_start_scan(struct lbs_private *priv, bool internal,
        lbs_deb_leave(LBS_DEB_CFG80211);
 }
 
+/*
+ * Clean up priv->scan_req.  Should be used to handle the allocation details.
+ */
+void lbs_scan_done(struct lbs_private *priv)
+{
+       WARN_ON(!priv->scan_req);
+
+       if (priv->internal_scan)
+               kfree(priv->scan_req);
+       else
+               cfg80211_scan_done(priv->scan_req, false);
+
+       priv->scan_req = NULL;
+}
+
 static int lbs_cfg_scan(struct wiphy *wiphy,
        struct net_device *dev,
        struct cfg80211_scan_request *request)
@@ -1292,27 +1307,32 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
        int ret = 0;
        u8 preamble = RADIO_PREAMBLE_SHORT;
 
+       if (dev == priv->mesh_dev)
+               return -EOPNOTSUPP;
+
        lbs_deb_enter(LBS_DEB_CFG80211);
 
        if (!sme->bssid) {
-               /* Run a scan if one isn't in-progress already and if the last
-                * scan was done more than 2 seconds ago.
-                */
-               if (priv->scan_req == NULL &&
-                   time_after(jiffies, priv->last_scan + (2 * HZ))) {
-                       struct cfg80211_scan_request *creq;
+               struct cfg80211_scan_request *creq;
 
-                       creq = _new_connect_scan_req(wiphy, sme);
-                       if (!creq) {
-                               ret = -EINVAL;
-                               goto done;
-                       }
+               /*
+                * Scan for the requested network after waiting for existing
+                * scans to finish.
+                */
+               lbs_deb_assoc("assoc: waiting for existing scans\n");
+               wait_event_interruptible_timeout(priv->scan_q,
+                                                (priv->scan_req == NULL),
+                                                (15 * HZ));
 
-                       lbs_deb_assoc("assoc: scanning for compatible AP\n");
-                       _internal_start_scan(priv, true, creq);
+               creq = _new_connect_scan_req(wiphy, sme);
+               if (!creq) {
+                       ret = -EINVAL;
+                       goto done;
                }
 
-               /* Wait for any in-progress scan to complete */
+               lbs_deb_assoc("assoc: scanning for compatible AP\n");
+               _internal_start_scan(priv, true, creq);
+
                lbs_deb_assoc("assoc: waiting for scan to complete\n");
                wait_event_interruptible_timeout(priv->scan_q,
                                                 (priv->scan_req == NULL),
@@ -1402,28 +1422,23 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
        return ret;
 }
 
-static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
-       u16 reason_code)
+int lbs_disconnect(struct lbs_private *priv, u16 reason)
 {
-       struct lbs_private *priv = wiphy_priv(wiphy);
        struct cmd_ds_802_11_deauthenticate cmd;
-
-       lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code);
-
-       /* store for lbs_cfg_ret_disconnect() */
-       priv->disassoc_reason = reason_code;
+       int ret;
 
        memset(&cmd, 0, sizeof(cmd));
        cmd.hdr.size = cpu_to_le16(sizeof(cmd));
        /* Mildly ugly to use a locally store my own BSSID ... */
        memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN);
-       cmd.reasoncode = cpu_to_le16(reason_code);
+       cmd.reasoncode = cpu_to_le16(reason);
 
-       if (lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd))
-               return -EFAULT;
+       ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd);
+       if (ret)
+               return ret;
 
        cfg80211_disconnected(priv->dev,
-                       priv->disassoc_reason,
+                       reason,
                        NULL, 0,
                        GFP_KERNEL);
        priv->connect_status = LBS_DISCONNECTED;
@@ -1431,6 +1446,21 @@ static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
+static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
+       u16 reason_code)
+{
+       struct lbs_private *priv = wiphy_priv(wiphy);
+
+       if (dev == priv->mesh_dev)
+               return -EOPNOTSUPP;
+
+       lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code);
+
+       /* store for lbs_cfg_ret_disconnect() */
+       priv->disassoc_reason = reason_code;
+
+       return lbs_disconnect(priv, reason_code);
+}
 
 static int lbs_cfg_set_default_key(struct wiphy *wiphy,
                                   struct net_device *netdev,
@@ -1439,6 +1469,9 @@ static int lbs_cfg_set_default_key(struct wiphy *wiphy,
 {
        struct lbs_private *priv = wiphy_priv(wiphy);
 
+       if (netdev == priv->mesh_dev)
+               return -EOPNOTSUPP;
+
        lbs_deb_enter(LBS_DEB_CFG80211);
 
        if (key_index != priv->wep_tx_key) {
@@ -1460,6 +1493,9 @@ static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev,
        u16 key_type;
        int ret = 0;
 
+       if (netdev == priv->mesh_dev)
+               return -EOPNOTSUPP;
+
        lbs_deb_enter(LBS_DEB_CFG80211);
 
        lbs_deb_assoc("add_key: cipher 0x%x, mac_addr %pM\n",
@@ -1603,6 +1639,9 @@ static int lbs_get_survey(struct wiphy *wiphy, struct net_device *dev,
        s8 signal, noise;
        int ret;
 
+       if (dev == priv->mesh_dev)
+               return -EOPNOTSUPP;
+
        if (idx != 0)
                ret = -ENOENT;
 
@@ -1636,28 +1675,23 @@ static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev,
        struct lbs_private *priv = wiphy_priv(wiphy);
        int ret = 0;
 
-       lbs_deb_enter(LBS_DEB_CFG80211);
+       if (dev == priv->mesh_dev)
+               return -EOPNOTSUPP;
 
        switch (type) {
        case NL80211_IFTYPE_MONITOR:
-               ret = lbs_set_monitor_mode(priv, 1);
-               break;
        case NL80211_IFTYPE_STATION:
-               if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
-                       ret = lbs_set_monitor_mode(priv, 0);
-               if (!ret)
-                       ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1);
-               break;
        case NL80211_IFTYPE_ADHOC:
-               if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
-                       ret = lbs_set_monitor_mode(priv, 0);
-               if (!ret)
-                       ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2);
                break;
        default:
-               ret = -ENOTSUPP;
+               return -EOPNOTSUPP;
        }
 
+       lbs_deb_enter(LBS_DEB_CFG80211);
+
+       if (priv->iface_running)
+               ret = lbs_set_iface_type(priv, type);
+
        if (!ret)
                priv->wdev->iftype = type;
 
@@ -1689,6 +1723,7 @@ static void lbs_join_post(struct lbs_private *priv,
                   2 + 2 +                      /* atim */
                   2 + 8];                      /* extended rates */
        u8 *fake = fake_ie;
+       struct cfg80211_bss *bss;
 
        lbs_deb_enter(LBS_DEB_CFG80211);
 
@@ -1732,14 +1767,15 @@ static void lbs_join_post(struct lbs_private *priv,
        *fake++ = 0x6c;
        lbs_deb_hex(LBS_DEB_CFG80211, "IE", fake_ie, fake - fake_ie);
 
-       cfg80211_inform_bss(priv->wdev->wiphy,
-                           params->channel,
-                           bssid,
-                           0,
-                           capability,
-                           params->beacon_interval,
-                           fake_ie, fake - fake_ie,
-                           0, GFP_KERNEL);
+       bss = cfg80211_inform_bss(priv->wdev->wiphy,
+                                 params->channel,
+                                 bssid,
+                                 0,
+                                 capability,
+                                 params->beacon_interval,
+                                 fake_ie, fake - fake_ie,
+                                 0, GFP_KERNEL);
+       cfg80211_put_bss(bss);
 
        memcpy(priv->wdev->ssid, params->ssid, params->ssid_len);
        priv->wdev->ssid_len = params->ssid_len;
@@ -1959,6 +1995,9 @@ static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev,
        struct cfg80211_bss *bss;
        DECLARE_SSID_BUF(ssid_buf);
 
+       if (dev == priv->mesh_dev)
+               return -EOPNOTSUPP;
+
        lbs_deb_enter(LBS_DEB_CFG80211);
 
        if (!params->channel) {
@@ -1995,6 +2034,9 @@ static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
        struct cmd_ds_802_11_ad_hoc_stop cmd;
        int ret = 0;
 
+       if (dev == priv->mesh_dev)
+               return -EOPNOTSUPP;
+
        lbs_deb_enter(LBS_DEB_CFG80211);
 
        memset(&cmd, 0, sizeof(cmd));
@@ -2117,6 +2159,8 @@ int lbs_cfg_register(struct lbs_private *priv)
                        BIT(NL80211_IFTYPE_ADHOC);
        if (lbs_rtap_supported(priv))
                wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
+       if (lbs_mesh_activated(priv))
+               wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT);
 
        wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz;