libertas: scan before assocation if no BSSID was given
authorDan Williams <dcbw@redhat.com>
Wed, 4 Aug 2010 05:43:47 +0000 (00:43 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 5 Aug 2010 20:05:27 +0000 (16:05 -0400)
Fix this leftover TODO from the cfg80211 conversion by doing a scan
if cfg80211 didn't pass in the BSSID for us.  Since the scan code
uses so much of the cfg80211_scan_request structure to build up the
firmware command, we just fake one when the scan request is triggered
internally.  But we need to make sure that internal 'fake' cfg82011
scan request does not get back to cfg82011 via cfg80211_scan_done().

Signed-off-by: Dan Williams <dcbw@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/libertas/cfg.c
drivers/net/wireless/libertas/dev.h
drivers/net/wireless/libertas/main.c

index 6b35d057426dd451f4035b1f4dd64fd40dd20c97..8e9fbfd804b6b4a5a4856ffa224573e0172d1715 100644 (file)
@@ -699,8 +699,13 @@ static void lbs_scan_worker(struct work_struct *work)
 
        if (priv->scan_channel >= priv->scan_req->n_channels) {
                /* Mark scan done */
-               cfg80211_scan_done(priv->scan_req, false);
+               if (priv->internal_scan)
+                       kfree(priv->scan_req);
+               else
+                       cfg80211_scan_done(priv->scan_req, false);
+
                priv->scan_req = NULL;
+               priv->last_scan = jiffies;
        }
 
        /* Restart network */
@@ -711,10 +716,33 @@ static void lbs_scan_worker(struct work_struct *work)
 
        kfree(scan_cmd);
 
+       /* Wake up anything waiting on scan completion */
+       if (priv->scan_req == NULL) {
+               lbs_deb_scan("scan: waking up waiters\n");
+               wake_up_all(&priv->scan_q);
+       }
+
  out_no_scan_cmd:
        lbs_deb_leave(LBS_DEB_SCAN);
 }
 
+static void _internal_start_scan(struct lbs_private *priv, bool internal,
+       struct cfg80211_scan_request *request)
+{
+       lbs_deb_enter(LBS_DEB_CFG80211);
+
+       lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n",
+               request->n_ssids, request->n_channels, request->ie_len);
+
+       priv->scan_channel = 0;
+       queue_delayed_work(priv->work_thread, &priv->scan_work,
+               msecs_to_jiffies(50));
+
+       priv->scan_req = request;
+       priv->internal_scan = internal;
+
+       lbs_deb_leave(LBS_DEB_CFG80211);
+}
 
 static int lbs_cfg_scan(struct wiphy *wiphy,
        struct net_device *dev,
@@ -731,18 +759,11 @@ static int lbs_cfg_scan(struct wiphy *wiphy,
                goto out;
        }
 
-       lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n",
-               request->n_ssids, request->n_channels, request->ie_len);
-
-       priv->scan_channel = 0;
-       queue_delayed_work(priv->work_thread, &priv->scan_work,
-               msecs_to_jiffies(50));
+       _internal_start_scan(priv, false, request);
 
        if (priv->surpriseremoved)
                ret = -EIO;
 
-       priv->scan_req = request;
-
  out:
        lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
        return ret;
@@ -1156,7 +1177,62 @@ done:
        return ret;
 }
 
+static struct cfg80211_scan_request *
+_new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme)
+{
+       struct cfg80211_scan_request *creq = NULL;
+       int i, n_channels = 0;
+       enum ieee80211_band band;
+
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               if (wiphy->bands[band])
+                       n_channels += wiphy->bands[band]->n_channels;
+       }
+
+       creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
+                      n_channels * sizeof(void *),
+                      GFP_ATOMIC);
+       if (!creq)
+               return NULL;
+
+       /* SSIDs come after channels */
+       creq->ssids = (void *)&creq->channels[n_channels];
+       creq->n_channels = n_channels;
+       creq->n_ssids = 1;
+
+       /* Scan all available channels */
+       i = 0;
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               int j;
+
+               if (!wiphy->bands[band])
+                       continue;
+
+               for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
+                       /* ignore disabled channels */
+                       if (wiphy->bands[band]->channels[j].flags &
+                                               IEEE80211_CHAN_DISABLED)
+                               continue;
+
+                       creq->channels[i] = &wiphy->bands[band]->channels[j];
+                       i++;
+               }
+       }
+       if (i) {
+               /* Set real number of channels specified in creq->channels[] */
+               creq->n_channels = i;
+
+               /* Scan for the SSID we're going to connect to */
+               memcpy(creq->ssids[0].ssid, sme->ssid, sme->ssid_len);
+               creq->ssids[0].ssid_len = sme->ssid_len;
+       } else {
+               /* No channels found... */
+               kfree(creq);
+               creq = NULL;
+       }
 
+       return creq;
+}
 
 static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
                           struct cfg80211_connect_params *sme)
@@ -1168,37 +1244,43 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
 
        lbs_deb_enter(LBS_DEB_CFG80211);
 
-       if (sme->bssid) {
-               bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
-                       sme->ssid, sme->ssid_len,
-                       WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
-       } else {
-               /*
-                * Here we have an impedance mismatch. The firmware command
-                * CMD_802_11_ASSOCIATE always needs a BSSID, it cannot
-                * connect otherwise. However, for the connect-API of
-                * cfg80211 the bssid is purely optional. We don't get one,
-                * except the user specifies one on the "iw" command line.
-                *
-                * If we don't got one, we could initiate a scan and look
-                * for the best matching cfg80211_bss entry.
-                *
-                * Or, better yet, net/wireless/sme.c get's rewritten into
-                * something more generally useful.
+       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.
                 */
-               lbs_pr_err("TODO: no BSS specified\n");
-               ret = -ENOTSUPP;
-               goto done;
-       }
+               if (priv->scan_req == NULL &&
+                   time_after(jiffies, priv->last_scan + (2 * HZ))) {
+                       struct cfg80211_scan_request *creq;
 
+                       creq = _new_connect_scan_req(wiphy, sme);
+                       if (!creq) {
+                               ret = -EINVAL;
+                               goto done;
+                       }
+
+                       lbs_deb_assoc("assoc: scanning for compatible AP\n");
+                       _internal_start_scan(priv, true, creq);
+               }
+
+               /* Wait for any in-progress scan to complete */
+               lbs_deb_assoc("assoc: waiting for scan to complete\n");
+               wait_event_interruptible_timeout(priv->scan_q,
+                                                (priv->scan_req == NULL),
+                                                (15 * HZ));
+               lbs_deb_assoc("assoc: scanning competed\n");
+       }
 
+       /* Find the BSS we want using available scan results */
+       bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
+               sme->ssid, sme->ssid_len,
+               WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
        if (!bss) {
-               lbs_pr_err("assicate: bss %pM not in scan results\n",
+               lbs_pr_err("assoc: bss %pM not in scan results\n",
                           sme->bssid);
                ret = -ENOENT;
                goto done;
        }
-       lbs_deb_assoc("trying %pM", sme->bssid);
+       lbs_deb_assoc("trying %pM\n", bss->bssid);
        lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n",
                      sme->crypto.cipher_group,
                      sme->key_idx, sme->key_len);
@@ -1261,7 +1343,7 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
        lbs_set_radio(priv, preamble, 1);
 
        /* Do the actual association */
-       lbs_associate(priv, bss, sme);
+       ret = lbs_associate(priv, bss, sme);
 
  done:
        if (bss)
index 3c7e255e18c7617e34af21c23af288f68047ab34..f062ed5839016c4752d3f30922e27c7aa6dca2f8 100644 (file)
@@ -161,6 +161,11 @@ struct lbs_private {
        /** Scanning */
        struct delayed_work scan_work;
        int scan_channel;
+       /* Queue of things waiting for scan completion */
+       wait_queue_head_t scan_q;
+       /* Whether the scan was initiated internally and not by cfg80211 */
+       bool internal_scan;
+       unsigned long last_scan;
 };
 
 extern struct cmd_confirm_sleep confirm_sleep;
index 258967144b962684a50c5a50c48591b2de14bbe9..24958a86747b3261d76c13d235b7f63211740ed8 100644 (file)
@@ -719,6 +719,7 @@ static int lbs_init_adapter(struct lbs_private *priv)
        priv->deep_sleep_required = 0;
        priv->wakeup_dev_required = 0;
        init_waitqueue_head(&priv->ds_awake_q);
+       init_waitqueue_head(&priv->scan_q);
        priv->authtype_auto = 1;
        priv->is_host_sleep_configured = 0;
        priv->is_host_sleep_activated = 0;