libertas: release bss references and avoid warning from cfg80211_inform_bss
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / libertas / cfg.c
index 63009c7eb2f1a0105795bc853f244d4d8810b9c1..89f34ad8d34acd1f01ba34644339bdffa228fdb1 100644 (file)
@@ -485,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;
@@ -632,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,
+                           !(channel->flags & IEEE80211_CHAN_DISABLED)) {
+                               bss = cfg80211_inform_bss(wiphy, channel,
                                        bssid, le64_to_cpu(*(__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");
 
@@ -695,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);
@@ -712,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));
        }
@@ -728,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)
@@ -775,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)
@@ -1302,24 +1313,26 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
        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),
@@ -1409,31 +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;
-
-       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;
+       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;
@@ -1441,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,
@@ -1658,28 +1678,20 @@ static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev,
        if (dev == priv->mesh_dev)
                return -EOPNOTSUPP;
 
-       lbs_deb_enter(LBS_DEB_CFG80211);
-
        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;
 
@@ -1711,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);
 
@@ -1754,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;