ipw2200: queue direct scans
authorDan Williams <dcbw@redhat.com>
Mon, 2 Jun 2008 21:51:23 +0000 (17:51 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 9 Jun 2008 19:50:16 +0000 (15:50 -0400)
When another scan is in progress, a direct scan gets dropped on the
floor.  However, that direct scan is usually the scan that's really
needed by userspace, and gets stomped on by all the broadcast scans the
ipw2200 driver issues internally.  Make sure the direct scan happens
eventually, and as a bonus ensure that the passive scan worker is
cleaned up when appropriate.

The change of request_passive_scan form a struct work to struct
delayed_work is only to make the set_wx_scan() code a bit simpler, it's
still only used with a delay of 0 to match previous behavior.

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

index 7293367..6e70460 100644 (file)
@@ -1753,6 +1753,8 @@ static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
 
                if (priv->workqueue) {
                        cancel_delayed_work(&priv->request_scan);
+                       cancel_delayed_work(&priv->request_direct_scan);
+                       cancel_delayed_work(&priv->request_passive_scan);
                        cancel_delayed_work(&priv->scan_event);
                }
                queue_work(priv->workqueue, &priv->down);
@@ -2005,6 +2007,8 @@ static void ipw_irq_tasklet(struct ipw_priv *priv)
                wake_up_interruptible(&priv->wait_command_queue);
                priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
                cancel_delayed_work(&priv->request_scan);
+               cancel_delayed_work(&priv->request_direct_scan);
+               cancel_delayed_work(&priv->request_passive_scan);
                cancel_delayed_work(&priv->scan_event);
                schedule_work(&priv->link_down);
                queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
@@ -4712,6 +4716,12 @@ static void ipw_rx_notification(struct ipw_priv *priv,
                        priv->status &= ~STATUS_SCAN_FORCED;
 #endif                         /* CONFIG_IPW2200_MONITOR */
 
+                       /* Do queued direct scans first */
+                       if (priv->status & STATUS_DIRECT_SCAN_PENDING) {
+                               queue_delayed_work(priv->workqueue,
+                                                  &priv->request_direct_scan, 0);
+                       }
+
                        if (!(priv->status & (STATUS_ASSOCIATED |
                                              STATUS_ASSOCIATING |
                                              STATUS_ROAMING |
@@ -6267,7 +6277,7 @@ static void ipw_add_scan_channels(struct ipw_priv *priv,
        }
 }
 
-static int ipw_request_scan_helper(struct ipw_priv *priv, int type)
+static int ipw_request_scan_helper(struct ipw_priv *priv, int type, int direct)
 {
        struct ipw_scan_request_ext scan;
        int err = 0, scan_type;
@@ -6278,22 +6288,31 @@ static int ipw_request_scan_helper(struct ipw_priv *priv, int type)
 
        mutex_lock(&priv->mutex);
 
+       if (direct && (priv->direct_scan_ssid_len == 0)) {
+               IPW_DEBUG_HC("Direct scan requested but no SSID to scan for\n");
+               priv->status &= ~STATUS_DIRECT_SCAN_PENDING;
+               goto done;
+       }
+
        if (priv->status & STATUS_SCANNING) {
-               IPW_DEBUG_HC("Concurrent scan requested.  Ignoring.\n");
-               priv->status |= STATUS_SCAN_PENDING;
+               IPW_DEBUG_HC("Concurrent scan requested.  Queuing.\n");
+               priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
+                                       STATUS_SCAN_PENDING;
                goto done;
        }
 
        if (!(priv->status & STATUS_SCAN_FORCED) &&
            priv->status & STATUS_SCAN_ABORTING) {
                IPW_DEBUG_HC("Scan request while abort pending.  Queuing.\n");
-               priv->status |= STATUS_SCAN_PENDING;
+               priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
+                                       STATUS_SCAN_PENDING;
                goto done;
        }
 
        if (priv->status & STATUS_RF_KILL_MASK) {
-               IPW_DEBUG_HC("Aborting scan due to RF Kill activation\n");
-               priv->status |= STATUS_SCAN_PENDING;
+               IPW_DEBUG_HC("Queuing scan due to RF Kill activation\n");
+               priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
+                                       STATUS_SCAN_PENDING;
                goto done;
        }
 
@@ -6321,6 +6340,7 @@ static int ipw_request_scan_helper(struct ipw_priv *priv, int type)
                cpu_to_le16(20);
 
        scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
+       scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20);
 
 #ifdef CONFIG_IPW2200_MONITOR
        if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
@@ -6360,13 +6380,23 @@ static int ipw_request_scan_helper(struct ipw_priv *priv, int type)
                        cpu_to_le16(2000);
        } else {
 #endif                         /* CONFIG_IPW2200_MONITOR */
-               /* If we are roaming, then make this a directed scan for the
-                * current network.  Otherwise, ensure that every other scan
-                * is a fast channel hop scan */
-               if ((priv->status & STATUS_ROAMING)
-                   || (!(priv->status & STATUS_ASSOCIATED)
-                       && (priv->config & CFG_STATIC_ESSID)
-                       && (le32_to_cpu(scan.full_scan_index) % 2))) {
+               /* Honor direct scans first, otherwise if we are roaming make
+                * this a direct scan for the current network.  Finally,
+                * ensure that every other scan is a fast channel hop scan */
+               if (direct) {
+                       err = ipw_send_ssid(priv, priv->direct_scan_ssid,
+                                           priv->direct_scan_ssid_len);
+                       if (err) {
+                               IPW_DEBUG_HC("Attempt to send SSID command  "
+                                            "failed\n");
+                               goto done;
+                       }
+
+                       scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
+               } else if ((priv->status & STATUS_ROAMING)
+                          || (!(priv->status & STATUS_ASSOCIATED)
+                              && (priv->config & CFG_STATIC_ESSID)
+                              && (le32_to_cpu(scan.full_scan_index) % 2))) {
                        err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
                        if (err) {
                                IPW_DEBUG_HC("Attempt to send SSID command "
@@ -6391,7 +6421,12 @@ send_request:
        }
 
        priv->status |= STATUS_SCANNING;
-       priv->status &= ~STATUS_SCAN_PENDING;
+       if (direct) {
+               priv->status &= ~STATUS_DIRECT_SCAN_PENDING;
+               priv->direct_scan_ssid_len = 0;
+       } else
+               priv->status &= ~STATUS_SCAN_PENDING;
+
        queue_delayed_work(priv->workqueue, &priv->scan_check,
                           IPW_SCAN_CHECK_WATCHDOG);
 done:
@@ -6402,15 +6437,22 @@ done:
 static void ipw_request_passive_scan(struct work_struct *work)
 {
        struct ipw_priv *priv =
-               container_of(work, struct ipw_priv, request_passive_scan);
-       ipw_request_scan_helper(priv, IW_SCAN_TYPE_PASSIVE);
+               container_of(work, struct ipw_priv, request_passive_scan.work);
+       ipw_request_scan_helper(priv, IW_SCAN_TYPE_PASSIVE, 0);
 }
 
 static void ipw_request_scan(struct work_struct *work)
 {
        struct ipw_priv *priv =
                container_of(work, struct ipw_priv, request_scan.work);
-       ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE);
+       ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 0);
+}
+
+static void ipw_request_direct_scan(struct work_struct *work)
+{
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, request_direct_scan.work);
+       ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 1);
 }
 
 static void ipw_bg_abort_scan(struct work_struct *work)
@@ -9477,99 +9519,38 @@ static int ipw_wx_get_retry(struct net_device *dev,
        return 0;
 }
 
-static int ipw_request_direct_scan(struct ipw_priv *priv, char *essid,
-                                  int essid_len)
-{
-       struct ipw_scan_request_ext scan;
-       int err = 0, scan_type;
-
-       if (!(priv->status & STATUS_INIT) ||
-           (priv->status & STATUS_EXIT_PENDING))
-               return 0;
-
-       mutex_lock(&priv->mutex);
-
-       if (priv->status & STATUS_RF_KILL_MASK) {
-               IPW_DEBUG_HC("Aborting scan due to RF kill activation\n");
-               priv->status |= STATUS_SCAN_PENDING;
-               goto done;
-       }
-
-       IPW_DEBUG_HC("starting request direct scan!\n");
-
-       if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
-               /* We should not sleep here; otherwise we will block most
-                * of the system (for instance, we hold rtnl_lock when we
-                * get here).
-                */
-               err = -EAGAIN;
-               goto done;
-       }
-       memset(&scan, 0, sizeof(scan));
-
-       if (priv->config & CFG_SPEED_SCAN)
-               scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
-                   cpu_to_le16(30);
-       else
-               scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
-                   cpu_to_le16(20);
-
-       scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
-           cpu_to_le16(20);
-       scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
-       scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20);
-
-       scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
-
-       err = ipw_send_ssid(priv, essid, essid_len);
-       if (err) {
-               IPW_DEBUG_HC("Attempt to send SSID command failed\n");
-               goto done;
-       }
-       scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
-
-       ipw_add_scan_channels(priv, &scan, scan_type);
-
-       err = ipw_send_scan_request_ext(priv, &scan);
-       if (err) {
-               IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
-               goto done;
-       }
-
-       priv->status |= STATUS_SCANNING;
-
-      done:
-       mutex_unlock(&priv->mutex);
-       return err;
-}
-
 static int ipw_wx_set_scan(struct net_device *dev,
                           struct iw_request_info *info,
                           union iwreq_data *wrqu, char *extra)
 {
        struct ipw_priv *priv = ieee80211_priv(dev);
        struct iw_scan_req *req = (struct iw_scan_req *)extra;
+       struct delayed_work *work = NULL;
 
        mutex_lock(&priv->mutex);
+
        priv->user_requested_scan = 1;
-       mutex_unlock(&priv->mutex);
 
        if (wrqu->data.length == sizeof(struct iw_scan_req)) {
                if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
-                       ipw_request_direct_scan(priv, req->essid,
-                                               req->essid_len);
-                       return 0;
-               }
-               if (req->scan_type == IW_SCAN_TYPE_PASSIVE) {
-                       queue_work(priv->workqueue,
-                                  &priv->request_passive_scan);
-                       return 0;
+                       int len = min((int)req->essid_len,
+                                     (int)sizeof(priv->direct_scan_ssid));
+                       memcpy(priv->direct_scan_ssid, req->essid, len);
+                       priv->direct_scan_ssid_len = len;
+                       work = &priv->request_direct_scan;
+               } else if (req->scan_type == IW_SCAN_TYPE_PASSIVE) {
+                       work = &priv->request_passive_scan;
                }
+       } else {
+               /* Normal active broadcast scan */
+               work = &priv->request_scan;
        }
 
+       mutex_unlock(&priv->mutex);
+
        IPW_DEBUG_WX("Start scan\n");
 
-       queue_delayed_work(priv->workqueue, &priv->request_scan, 0);
+       queue_delayed_work(priv->workqueue, work, 0);
 
        return 0;
 }
@@ -10731,6 +10712,8 @@ static void ipw_link_up(struct ipw_priv *priv)
        }
 
        cancel_delayed_work(&priv->request_scan);
+       cancel_delayed_work(&priv->request_direct_scan);
+       cancel_delayed_work(&priv->request_passive_scan);
        cancel_delayed_work(&priv->scan_event);
        ipw_reset_stats(priv);
        /* Ensure the rate is updated immediately */
@@ -10761,6 +10744,8 @@ static void ipw_link_down(struct ipw_priv *priv)
 
        /* Cancel any queued work ... */
        cancel_delayed_work(&priv->request_scan);
+       cancel_delayed_work(&priv->request_direct_scan);
+       cancel_delayed_work(&priv->request_passive_scan);
        cancel_delayed_work(&priv->adhoc_check);
        cancel_delayed_work(&priv->gather_stats);
 
@@ -10800,8 +10785,9 @@ static int __devinit ipw_setup_deferred_work(struct ipw_priv *priv)
        INIT_WORK(&priv->up, ipw_bg_up);
        INIT_WORK(&priv->down, ipw_bg_down);
        INIT_DELAYED_WORK(&priv->request_scan, ipw_request_scan);
+       INIT_DELAYED_WORK(&priv->request_direct_scan, ipw_request_direct_scan);
+       INIT_DELAYED_WORK(&priv->request_passive_scan, ipw_request_passive_scan);
        INIT_DELAYED_WORK(&priv->scan_event, ipw_scan_event);
-       INIT_WORK(&priv->request_passive_scan, ipw_request_passive_scan);
        INIT_DELAYED_WORK(&priv->gather_stats, ipw_bg_gather_stats);
        INIT_WORK(&priv->abort_scan, ipw_bg_abort_scan);
        INIT_WORK(&priv->roam, ipw_bg_roam);
@@ -11835,6 +11821,8 @@ static void __devexit ipw_pci_remove(struct pci_dev *pdev)
        cancel_delayed_work(&priv->adhoc_check);
        cancel_delayed_work(&priv->gather_stats);
        cancel_delayed_work(&priv->request_scan);
+       cancel_delayed_work(&priv->request_direct_scan);
+       cancel_delayed_work(&priv->request_passive_scan);
        cancel_delayed_work(&priv->scan_event);
        cancel_delayed_work(&priv->rf_kill);
        cancel_delayed_work(&priv->scan_check);
index cd3295b..d4ab28b 100644 (file)
@@ -1037,6 +1037,7 @@ struct ipw_cmd {   /* XXX */
 #define STATUS_DISASSOC_PENDING (1<<12)
 #define STATUS_STATE_PENDING    (1<<13)
 
+#define STATUS_DIRECT_SCAN_PENDING (1<<19)
 #define STATUS_SCAN_PENDING     (1<<20)
 #define STATUS_SCANNING         (1<<21)
 #define STATUS_SCAN_ABORTING    (1<<22)
@@ -1292,6 +1293,8 @@ struct ipw_priv {
        struct iw_public_data wireless_data;
 
        int user_requested_scan;
+       u8 direct_scan_ssid[IW_ESSID_MAX_SIZE];
+       u8 direct_scan_ssid_len;
 
        struct workqueue_struct *workqueue;
 
@@ -1301,8 +1304,9 @@ struct ipw_priv {
        struct work_struct system_config;
        struct work_struct rx_replenish;
        struct delayed_work request_scan;
+       struct delayed_work request_direct_scan;
+       struct delayed_work request_passive_scan;
        struct delayed_work scan_event;
-       struct work_struct request_passive_scan;
        struct work_struct adapter_restart;
        struct delayed_work rf_kill;
        struct work_struct up;