linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] mac80211: hardware scan rework (V3)
@ 2007-11-22  2:53 Zhu Yi
  2007-11-22  2:53 ` [PATCH 2/2] iwlwifi: cache mac80211 conf setting during a hardware scan Zhu Yi
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Zhu Yi @ 2007-11-22  2:53 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, Zhu Yi, Mohamed Abbas, Ben Cahill

Hi,

This is the third version of the hardware scan rework patch. Comparing
with the second version, it allows mac80211 to set channel during the
hardware scan case. If the low level driver doesn't support setting
channels in the hardware scan process, the driver should cache this
setting and replay it after the scan is finished.

See Patch 2/2 for such modification for the iwlwifi drivers.


The scan code in mac80211 makes the software scan assumption in various
places. For example, we stop the Tx queue during a software scan so that
all the Tx packets will be queued by the stack. We also drop frames not
related to scan in the software scan process. But these are not true for
hardware scan.

Some wireless hardwares (for example iwl3945/4965) has the ability to
perform the whole scan process by hardware and/or firmware. The hardware
scan is relative powerful in that it tries to maintain normal network
traffic while doing a scan in the background. Some drivers (i.e iwlwifi)
do provide a way to tune the hardware scan parameters (for example if the
STA is associated, what's the max time could the STA leave from the
associated channel, how long the scans get suspended after returning to
the service channel, etc). But basically this is transparent to the
stack. mac80211 should not stop Tx queues or drop Rx packets during a
hardware scan.

This patch resolves the above problem by spliting the current scan
indicator local->sta_scanning into local->sta_sw_scanning and
local->sta_hw_scanning. It then changes the scan related code to be aware
of hardware scan or software scan in various places. With this patch,
iwlwifi performs much better in the scan-while-associated condition and
disable_hw_scan=1 should never be required.

Cc: Mohamed Abbas <mohamed.abbas@intel.com>
Cc: Ben Cahill <ben.m.cahill@intel.com>
Signed-off-by: Zhu Yi <yi.zhu@intel.com>


diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
index 59350b8..acbe717 100644
--- a/net/mac80211/ieee80211.c
+++ b/net/mac80211/ieee80211.c
@@ -351,11 +351,14 @@ static int ieee80211_stop(struct net_device *dev)
 		synchronize_rcu();
 		skb_queue_purge(&sdata->u.sta.skb_queue);
 
-		if (!local->ops->hw_scan &&
-		    local->scan_dev == sdata->dev) {
-			local->sta_scanning = 0;
-			cancel_delayed_work(&local->scan_work);
+		if (local->scan_dev == sdata->dev) {
+			if (!local->ops->hw_scan) {
+				local->sta_sw_scanning = 0;
+				cancel_delayed_work(&local->scan_work);
+			} else
+				local->sta_hw_scanning = 0;
 		}
+
 		flush_workqueue(local->hw.workqueue);
 		/* fall through */
 	default:
@@ -523,7 +526,7 @@ int ieee80211_hw_config(struct ieee80211_local *local)
 	struct ieee80211_channel *chan;
 	int ret = 0;
 
-	if (local->sta_scanning) {
+	if (local->sta_sw_scanning) {
 		chan = local->scan_channel;
 		mode = local->scan_hw_mode;
 	} else {
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 72e1c93..35829b1 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -470,7 +470,8 @@ struct ieee80211_local {
 
 	struct list_head interfaces;
 
-	int sta_scanning;
+	bool sta_sw_scanning;
+	bool sta_hw_scanning;
 	int scan_channel_idx;
 	enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
 	unsigned long last_scan_completed;
@@ -749,7 +750,8 @@ int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len);
 void ieee80211_sta_req_auth(struct net_device *dev,
 			    struct ieee80211_if_sta *ifsta);
 int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len);
-void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
+ieee80211_txrx_result ieee80211_sta_rx_scan(struct net_device *dev,
+					    struct sk_buff *skb,
 			   struct ieee80211_rx_status *rx_status);
 void ieee80211_rx_bss_list_init(struct net_device *dev);
 void ieee80211_rx_bss_list_deinit(struct net_device *dev);
diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c
index 942b9cc..f2d84f3 100644
--- a/net/mac80211/ieee80211_ioctl.c
+++ b/net/mac80211/ieee80211_ioctl.c
@@ -315,7 +315,7 @@ int ieee80211_set_channel(struct ieee80211_local *local, int channel, int freq)
 	}
 
 	if (set) {
-		if (local->sta_scanning)
+		if (local->sta_sw_scanning)
 			ret = 0;
 		else
 			ret = ieee80211_hw_config(local);
@@ -558,8 +558,10 @@ static int ieee80211_ioctl_giwscan(struct net_device *dev,
 {
 	int res;
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	if (local->sta_scanning)
+
+	if (local->sta_sw_scanning || local->sta_hw_scanning)
 		return -EAGAIN;
+
 	res = ieee80211_sta_scan_results(dev, extra, data->length);
 	if (res >= 0) {
 		data->length = res;
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c
index 015b3f8..26f404a 100644
--- a/net/mac80211/ieee80211_sta.c
+++ b/net/mac80211/ieee80211_sta.c
@@ -1487,8 +1487,18 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
 		u32 supp_rates, prev_rates;
 		int i, j;
 
-		mode = local->sta_scanning ?
+		mode = local->sta_sw_scanning ?
 		       local->scan_hw_mode : local->oper_hw_mode;
+
+		if (local->sta_hw_scanning) {
+			/* search for the correct mode matches the beacon */
+			list_for_each_entry(mode, &local->modes_list, list)
+				if (mode->mode == rx_status->phymode)
+					break;
+
+			if (mode == NULL)
+				mode = local->oper_hw_mode;
+		}
 		rates = mode->rates;
 		num_rates = mode->num_rates;
 
@@ -1871,31 +1881,39 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
 }
 
 
-void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
-			   struct ieee80211_rx_status *rx_status)
+ieee80211_txrx_result
+ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
+		      struct ieee80211_rx_status *rx_status)
 {
 	struct ieee80211_mgmt *mgmt;
 	u16 fc;
 
-	if (skb->len < 24) {
-		dev_kfree_skb(skb);
-		return;
-	}
+	if (skb->len < 2)
+		return TXRX_DROP;
 
 	mgmt = (struct ieee80211_mgmt *) skb->data;
 	fc = le16_to_cpu(mgmt->frame_control);
 
+	if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL)
+		return TXRX_CONTINUE;
+
+	if (skb->len < 24)
+		return TXRX_DROP;
+
 	if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
 		if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP) {
 			ieee80211_rx_mgmt_probe_resp(dev, mgmt,
 						     skb->len, rx_status);
+			dev_kfree_skb(skb);
+			return TXRX_QUEUED;
 		} else if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) {
 			ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len,
 						 rx_status);
+			dev_kfree_skb(skb);
+			return TXRX_QUEUED;
 		}
 	}
-
-	dev_kfree_skb(skb);
+	return TXRX_CONTINUE;
 }
 
 
@@ -1985,7 +2003,7 @@ void ieee80211_sta_work(struct work_struct *work)
 	if (!netif_running(dev))
 		return;
 
-	if (local->sta_scanning)
+	if (local->sta_sw_scanning || local->sta_hw_scanning)
 		return;
 
 	if (sdata->type != IEEE80211_IF_TYPE_STA &&
@@ -2643,9 +2661,15 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
 	union iwreq_data wrqu;
 
 	local->last_scan_completed = jiffies;
-	wmb();
-	local->sta_scanning = 0;
+	memset(&wrqu, 0, sizeof(wrqu));
+	wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
 
+	if (local->sta_hw_scanning) {
+		local->sta_hw_scanning = 0;
+		goto done;
+	}
+
+	local->sta_sw_scanning = 0;
 	if (ieee80211_hw_config(local))
 		printk(KERN_DEBUG "%s: failed to restore operational"
 		       "channel after scan\n", dev->name);
@@ -2661,9 +2685,6 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
 
 	netif_tx_unlock_bh(local->mdev);
 
-	memset(&wrqu, 0, sizeof(wrqu));
-	wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
-
 	rcu_read_lock();
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 
@@ -2681,6 +2702,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
 	}
 	rcu_read_unlock();
 
+done:
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
 		struct ieee80211_if_sta *ifsta = &sdata->u.sta;
@@ -2703,7 +2725,7 @@ void ieee80211_sta_scan_work(struct work_struct *work)
 	int skip;
 	unsigned long next_delay = 0;
 
-	if (!local->sta_scanning)
+	if (!local->sta_sw_scanning)
 		return;
 
 	switch (local->scan_state) {
@@ -2766,7 +2788,7 @@ void ieee80211_sta_scan_work(struct work_struct *work)
 		break;
 	}
 
-	if (local->sta_scanning)
+	if (local->sta_sw_scanning)
 		queue_delayed_work(local->hw.workqueue, &local->scan_work,
 				   next_delay);
 }
@@ -2798,7 +2820,7 @@ static int ieee80211_sta_start_scan(struct net_device *dev,
 	  * ResultCode: SUCCESS, INVALID_PARAMETERS
 	 */
 
-	if (local->sta_scanning) {
+	if (local->sta_sw_scanning || local->sta_hw_scanning) {
 		if (local->scan_dev == dev)
 			return 0;
 		return -EBUSY;
@@ -2806,15 +2828,15 @@ static int ieee80211_sta_start_scan(struct net_device *dev,
 
 	if (local->ops->hw_scan) {
 		int rc = local->ops->hw_scan(local_to_hw(local),
-					    ssid, ssid_len);
+					     ssid, ssid_len);
 		if (!rc) {
-			local->sta_scanning = 1;
+			local->sta_hw_scanning = 1;
 			local->scan_dev = dev;
 		}
 		return rc;
 	}
 
-	local->sta_scanning = 1;
+	local->sta_sw_scanning = 1;
 
 	rcu_read_lock();
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
@@ -2869,7 +2891,7 @@ int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len)
 	if (sdata->type != IEEE80211_IF_TYPE_STA)
 		return ieee80211_sta_start_scan(dev, ssid, ssid_len);
 
-	if (local->sta_scanning) {
+	if (local->sta_sw_scanning || local->sta_hw_scanning) {
 		if (local->scan_dev == dev)
 			return 0;
 		return -EBUSY;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 428a9fc..596ff71 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -338,8 +338,14 @@ ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
 	struct ieee80211_local *local = rx->local;
 	struct sk_buff *skb = rx->skb;
 
-	if (unlikely(local->sta_scanning != 0)) {
-		ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
+	if (unlikely(local->sta_hw_scanning))
+		return ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
+
+	if (unlikely(local->sta_sw_scanning)) {
+		/* drop all the other packets during a software scan anyway */
+		if (ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status)
+		    != TXRX_QUEUED)
+			dev_kfree_skb(skb);
 		return TXRX_QUEUED;
 	}
 
@@ -1486,7 +1492,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
 		goto end;
 	}
 
-	if (unlikely(local->sta_scanning))
+	if (unlikely(local->sta_sw_scanning || local->sta_hw_scanning))
 		rx.flags |= IEEE80211_TXRXD_RXIN_SCAN;
 
 	if (__ieee80211_invoke_rx_handlers(local, local->rx_pre_handlers, &rx,
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 1a53154..a6a657f 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -225,7 +225,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
 	if (unlikely(tx->flags & IEEE80211_TXRXD_TX_INJECTED))
 		return TXRX_CONTINUE;
 
-	if (unlikely(tx->local->sta_scanning != 0) &&
+	if (unlikely(tx->local->sta_sw_scanning) &&
 	    ((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
 	     (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
 		return TXRX_DROP;

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/2] iwlwifi: cache mac80211 conf setting during a hardware scan
  2007-11-22  2:53 [PATCH 1/2] mac80211: hardware scan rework (V3) Zhu Yi
@ 2007-11-22  2:53 ` Zhu Yi
  2007-11-22  5:18 ` [PATCH 1/2] mac80211: hardware scan rework (V3) Abbas, Mohamed
  2007-11-22 13:09 ` Johannes Berg
  2 siblings, 0 replies; 4+ messages in thread
From: Zhu Yi @ 2007-11-22  2:53 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, Zhu Yi

This patch caches mac80211 configuration setting during a hardware
scan for the iwlwifi drivers.

Signed-off-by: Zhu Yi <yi.zhu@intel.com>
---

diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h
index e5b345d..101e0b5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.h
@@ -732,6 +732,7 @@ struct iwl3945_priv {
 	struct ieee80211_hw *hw;
 	struct ieee80211_channel *ieee_channels;
 	struct ieee80211_rate *ieee_rates;
+	struct ieee80211_conf *cache_conf;
 
 	/* temporary frame storage list */
 	struct list_head free_frames;
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.h b/drivers/net/wireless/iwlwifi/iwl-4965.h
index 5944b4b..8713077 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.h
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.h
@@ -1053,6 +1053,7 @@ struct iwl4965_priv {
 	struct ieee80211_hw *hw;
 	struct ieee80211_channel *ieee_channels;
 	struct ieee80211_rate *ieee_rates;
+	struct ieee80211_conf *cache_conf;
 
 	/* temporary frame storage list */
 	struct list_head free_frames;
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 96420d5..8b1bb74 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -6822,6 +6822,8 @@ static void iwl3945_bg_abort_scan(struct work_struct *work)
 	mutex_unlock(&priv->mutex);
 }
 
+static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf);
+
 static void iwl3945_bg_scan_completed(struct work_struct *work)
 {
 	struct iwl3945_priv *priv =
@@ -6832,6 +6834,9 @@ static void iwl3945_bg_scan_completed(struct work_struct *work)
 	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
 		return;
 
+	if (priv->cache_conf)
+		iwl3945_mac_config(priv->hw, priv->cache_conf);
+
 	ieee80211_scan_completed(priv->hw);
 
 	/* Since setting the TXPOWER may have been deferred while
@@ -6952,23 +6957,38 @@ static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
 	struct iwl3945_priv *priv = hw->priv;
 	const struct iwl3945_channel_info *ch_info;
 	unsigned long flags;
+	int ret = 0;
 
 	mutex_lock(&priv->mutex);
 	IWL_DEBUG_MAC80211("enter to channel %d\n", conf->channel);
 
 	if (!iwl3945_is_ready(priv)) {
 		IWL_DEBUG_MAC80211("leave - not ready\n");
-		mutex_unlock(&priv->mutex);
-		return -EIO;
+		ret = -EIO;
+		goto out;
 	}
 
 	/* TODO: Figure out how to get ieee80211_local->sta_scanning w/ only
 	 * what is exposed through include/ declarations */
 	if (unlikely(!iwl3945_param_disable_hw_scan &&
 		     test_bit(STATUS_SCANNING, &priv->status))) {
-		IWL_DEBUG_MAC80211("leave - scanning\n");
+
+		if (priv->cache_conf)
+			IWL_DEBUG_MAC80211("leave - still scanning\n");
+		else {
+			/* Cache the configuration now so that we can
+			 * replay it after the hardware scan is finished. */
+			priv->cache_conf = kmalloc(sizeof(*conf), GFP_KERNEL);
+			if (priv->cache_conf) {
+				memcpy(priv->cache_conf, conf, sizeof(*conf));
+				IWL_DEBUG_MAC80211("leave - scanning\n");
+			} else {
+				IWL_DEBUG_MAC80211("leave - no memory\n");
+				ret = -ENOMEM;
+			}
+		}
 		mutex_unlock(&priv->mutex);
-		return 0;
+		return ret;
 	}
 
 	spin_lock_irqsave(&priv->lock, flags);
@@ -6979,8 +6999,8 @@ static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
 			       conf->channel, conf->phymode);
 		IWL_DEBUG_MAC80211("leave - invalid channel\n");
 		spin_unlock_irqrestore(&priv->lock, flags);
-		mutex_unlock(&priv->mutex);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto out;
 	}
 
 	iwl3945_set_rxon_channel(priv, conf->phymode, conf->channel);
@@ -6997,8 +7017,7 @@ static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
 #ifdef IEEE80211_CONF_CHANNEL_SWITCH
 	if (conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) {
 		iwl3945_hw_channel_switch(priv, conf->channel);
-		mutex_unlock(&priv->mutex);
-		return 0;
+		goto out;
 	}
 #endif
 
@@ -7006,14 +7025,13 @@ static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
 
 	if (!conf->radio_enabled) {
 		IWL_DEBUG_MAC80211("leave - radio disabled\n");
-		mutex_unlock(&priv->mutex);
-		return 0;
+		goto out;
 	}
 
 	if (iwl3945_is_rfkill(priv)) {
 		IWL_DEBUG_MAC80211("leave - RF kill\n");
-		mutex_unlock(&priv->mutex);
-		return -EIO;
+		ret = -EIO;
+		goto out;
 	}
 
 	iwl3945_set_rate(priv);
@@ -7026,9 +7044,13 @@ static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
 
 	IWL_DEBUG_MAC80211("leave\n");
 
+out:
+	if (priv->cache_conf) {
+		kfree(priv->cache_conf);
+		priv->cache_conf = NULL;
+	}
 	mutex_unlock(&priv->mutex);
-
-	return 0;
+	return ret;
 }
 
 static void iwl3945_config_ap(struct iwl3945_priv *priv)
diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c
index df011ea..c0e5900 100644
--- a/drivers/net/wireless/iwlwifi/iwl4965-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c
@@ -7222,6 +7222,8 @@ static void iwl4965_bg_abort_scan(struct work_struct *work)
 	mutex_unlock(&priv->mutex);
 }
 
+static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf);
+
 static void iwl4965_bg_scan_completed(struct work_struct *work)
 {
 	struct iwl4965_priv *priv =
@@ -7232,6 +7234,9 @@ static void iwl4965_bg_scan_completed(struct work_struct *work)
 	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
 		return;
 
+	if (priv->cache_conf)
+		iwl4965_mac_config(priv->hw, priv->cache_conf);
+
 	ieee80211_scan_completed(priv->hw);
 
 	/* Since setting the TXPOWER may have been deferred while
@@ -7352,23 +7357,38 @@ static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
 	struct iwl4965_priv *priv = hw->priv;
 	const struct iwl4965_channel_info *ch_info;
 	unsigned long flags;
+	int ret = 0;
 
 	mutex_lock(&priv->mutex);
 	IWL_DEBUG_MAC80211("enter to channel %d\n", conf->channel);
 
 	if (!iwl4965_is_ready(priv)) {
 		IWL_DEBUG_MAC80211("leave - not ready\n");
-		mutex_unlock(&priv->mutex);
-		return -EIO;
+		ret = -EIO;
+		goto out;
 	}
 
 	/* TODO: Figure out how to get ieee80211_local->sta_scanning w/ only
 	 * what is exposed through include/ declarations */
 	if (unlikely(!iwl4965_param_disable_hw_scan &&
 		     test_bit(STATUS_SCANNING, &priv->status))) {
-		IWL_DEBUG_MAC80211("leave - scanning\n");
+
+		if (unlikely(priv->cache_conf))
+			IWL_DEBUG_MAC80211("leave - still scanning\n");
+		else {
+			/* Cache the configuration now so that we can
+			 * replay it after the hardware scan is finished. */
+			priv->cache_conf = kmalloc(sizeof(*conf), GFP_KERNEL);
+			if (priv->cache_conf) {
+				memcpy(priv->cache_conf, conf, sizeof(*conf));
+				IWL_DEBUG_MAC80211("leave - scanning\n");
+			} else {
+				IWL_DEBUG_MAC80211("leave - no memory\n");
+				ret = -ENOMEM;
+			}
+		}
 		mutex_unlock(&priv->mutex);
-		return 0;
+		return ret;
 	}
 
 	spin_lock_irqsave(&priv->lock, flags);
@@ -7379,8 +7399,8 @@ static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
 			       conf->channel, conf->phymode);
 		IWL_DEBUG_MAC80211("leave - invalid channel\n");
 		spin_unlock_irqrestore(&priv->lock, flags);
-		mutex_unlock(&priv->mutex);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto out;
 	}
 
 #ifdef CONFIG_IWL4965_HT
@@ -7409,8 +7429,7 @@ static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
 #ifdef IEEE80211_CONF_CHANNEL_SWITCH
 	if (conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) {
 		iwl4965_hw_channel_switch(priv, conf->channel);
-		mutex_unlock(&priv->mutex);
-		return 0;
+		goto out;
 	}
 #endif
 
@@ -7418,14 +7437,13 @@ static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
 
 	if (!conf->radio_enabled) {
 		IWL_DEBUG_MAC80211("leave - radio disabled\n");
-		mutex_unlock(&priv->mutex);
-		return 0;
+		goto out;
 	}
 
 	if (iwl4965_is_rfkill(priv)) {
 		IWL_DEBUG_MAC80211("leave - RF kill\n");
-		mutex_unlock(&priv->mutex);
-		return -EIO;
+		ret = -EIO;
+		goto out;
 	}
 
 	iwl4965_set_rate(priv);
@@ -7438,9 +7456,13 @@ static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
 
 	IWL_DEBUG_MAC80211("leave\n");
 
+out:
+	if (priv->cache_conf) {
+		kfree(priv->cache_conf);
+		priv->cache_conf = NULL;
+	}
 	mutex_unlock(&priv->mutex);
-
-	return 0;
+	return ret;
 }
 
 static void iwl4965_config_ap(struct iwl4965_priv *priv)

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* RE: [PATCH 1/2] mac80211: hardware scan rework (V3)
  2007-11-22  2:53 [PATCH 1/2] mac80211: hardware scan rework (V3) Zhu Yi
  2007-11-22  2:53 ` [PATCH 2/2] iwlwifi: cache mac80211 conf setting during a hardware scan Zhu Yi
@ 2007-11-22  5:18 ` Abbas, Mohamed
  2007-11-22 13:09 ` Johannes Berg
  2 siblings, 0 replies; 4+ messages in thread
From: Abbas, Mohamed @ 2007-11-22  5:18 UTC (permalink / raw)
  To: Zhu, Yi, linville; +Cc: linux-wireless, Cahill, Ben M

Actually ignore the previous email your patch still make sure we don't
handle management frame while scanning. The patch looks good and I will
run more test to make sure it covers all cases. Excellent job Yi.
Mohamed

-----Original Message-----
From: Zhu, Yi 
Sent: Wednesday, November 21, 2007 6:53 PM
To: linville@tuxdriver.com
Cc: linux-wireless@vger.kernel.org; Zhu, Yi; Abbas, Mohamed; Cahill, Ben
M
Subject: [PATCH 1/2] mac80211: hardware scan rework (V3)

Hi,

This is the third version of the hardware scan rework patch. Comparing
with the second version, it allows mac80211 to set channel during the
hardware scan case. If the low level driver doesn't support setting
channels in the hardware scan process, the driver should cache this
setting and replay it after the scan is finished.

See Patch 2/2 for such modification for the iwlwifi drivers.


The scan code in mac80211 makes the software scan assumption in various
places. For example, we stop the Tx queue during a software scan so that
all the Tx packets will be queued by the stack. We also drop frames not
related to scan in the software scan process. But these are not true for
hardware scan.

Some wireless hardwares (for example iwl3945/4965) has the ability to
perform the whole scan process by hardware and/or firmware. The hardware
scan is relative powerful in that it tries to maintain normal network
traffic while doing a scan in the background. Some drivers (i.e iwlwifi)
do provide a way to tune the hardware scan parameters (for example if
the
STA is associated, what's the max time could the STA leave from the
associated channel, how long the scans get suspended after returning to
the service channel, etc). But basically this is transparent to the
stack. mac80211 should not stop Tx queues or drop Rx packets during a
hardware scan.

This patch resolves the above problem by spliting the current scan
indicator local->sta_scanning into local->sta_sw_scanning and
local->sta_hw_scanning. It then changes the scan related code to be
aware
of hardware scan or software scan in various places. With this patch,
iwlwifi performs much better in the scan-while-associated condition and
disable_hw_scan=1 should never be required.

Cc: Mohamed Abbas <mohamed.abbas@intel.com>
Cc: Ben Cahill <ben.m.cahill@intel.com>
Signed-off-by: Zhu Yi <yi.zhu@intel.com>


diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
index 59350b8..acbe717 100644
--- a/net/mac80211/ieee80211.c
+++ b/net/mac80211/ieee80211.c
@@ -351,11 +351,14 @@ static int ieee80211_stop(struct net_device *dev)
 		synchronize_rcu();
 		skb_queue_purge(&sdata->u.sta.skb_queue);
 
-		if (!local->ops->hw_scan &&
-		    local->scan_dev == sdata->dev) {
-			local->sta_scanning = 0;
-			cancel_delayed_work(&local->scan_work);
+		if (local->scan_dev == sdata->dev) {
+			if (!local->ops->hw_scan) {
+				local->sta_sw_scanning = 0;
+				cancel_delayed_work(&local->scan_work);
+			} else
+				local->sta_hw_scanning = 0;
 		}
+
 		flush_workqueue(local->hw.workqueue);
 		/* fall through */
 	default:
@@ -523,7 +526,7 @@ int ieee80211_hw_config(struct ieee80211_local
*local)
 	struct ieee80211_channel *chan;
 	int ret = 0;
 
-	if (local->sta_scanning) {
+	if (local->sta_sw_scanning) {
 		chan = local->scan_channel;
 		mode = local->scan_hw_mode;
 	} else {
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 72e1c93..35829b1 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -470,7 +470,8 @@ struct ieee80211_local {
 
 	struct list_head interfaces;
 
-	int sta_scanning;
+	bool sta_sw_scanning;
+	bool sta_hw_scanning;
 	int scan_channel_idx;
 	enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
 	unsigned long last_scan_completed;
@@ -749,7 +750,8 @@ int ieee80211_sta_req_scan(struct net_device *dev,
u8 *ssid, size_t ssid_len);
 void ieee80211_sta_req_auth(struct net_device *dev,
 			    struct ieee80211_if_sta *ifsta);
 int ieee80211_sta_scan_results(struct net_device *dev, char *buf,
size_t len);
-void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
+ieee80211_txrx_result ieee80211_sta_rx_scan(struct net_device *dev,
+					    struct sk_buff *skb,
 			   struct ieee80211_rx_status *rx_status);
 void ieee80211_rx_bss_list_init(struct net_device *dev);
 void ieee80211_rx_bss_list_deinit(struct net_device *dev);
diff --git a/net/mac80211/ieee80211_ioctl.c
b/net/mac80211/ieee80211_ioctl.c
index 942b9cc..f2d84f3 100644
--- a/net/mac80211/ieee80211_ioctl.c
+++ b/net/mac80211/ieee80211_ioctl.c
@@ -315,7 +315,7 @@ int ieee80211_set_channel(struct ieee80211_local
*local, int channel, int freq)
 	}
 
 	if (set) {
-		if (local->sta_scanning)
+		if (local->sta_sw_scanning)
 			ret = 0;
 		else
 			ret = ieee80211_hw_config(local);
@@ -558,8 +558,10 @@ static int ieee80211_ioctl_giwscan(struct
net_device *dev,
 {
 	int res;
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	if (local->sta_scanning)
+
+	if (local->sta_sw_scanning || local->sta_hw_scanning)
 		return -EAGAIN;
+
 	res = ieee80211_sta_scan_results(dev, extra, data->length);
 	if (res >= 0) {
 		data->length = res;
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c
index 015b3f8..26f404a 100644
--- a/net/mac80211/ieee80211_sta.c
+++ b/net/mac80211/ieee80211_sta.c
@@ -1487,8 +1487,18 @@ static void ieee80211_rx_bss_info(struct
net_device *dev,
 		u32 supp_rates, prev_rates;
 		int i, j;
 
-		mode = local->sta_scanning ?
+		mode = local->sta_sw_scanning ?
 		       local->scan_hw_mode : local->oper_hw_mode;
+
+		if (local->sta_hw_scanning) {
+			/* search for the correct mode matches the
beacon */
+			list_for_each_entry(mode, &local->modes_list,
list)
+				if (mode->mode == rx_status->phymode)
+					break;
+
+			if (mode == NULL)
+				mode = local->oper_hw_mode;
+		}
 		rates = mode->rates;
 		num_rates = mode->num_rates;
 
@@ -1871,31 +1881,39 @@ static void ieee80211_sta_rx_queued_mgmt(struct
net_device *dev,
 }
 
 
-void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
-			   struct ieee80211_rx_status *rx_status)
+ieee80211_txrx_result
+ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
+		      struct ieee80211_rx_status *rx_status)
 {
 	struct ieee80211_mgmt *mgmt;
 	u16 fc;
 
-	if (skb->len < 24) {
-		dev_kfree_skb(skb);
-		return;
-	}
+	if (skb->len < 2)
+		return TXRX_DROP;
 
 	mgmt = (struct ieee80211_mgmt *) skb->data;
 	fc = le16_to_cpu(mgmt->frame_control);
 
+	if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL)
+		return TXRX_CONTINUE;
+
+	if (skb->len < 24)
+		return TXRX_DROP;
+
 	if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
 		if ((fc & IEEE80211_FCTL_STYPE) ==
IEEE80211_STYPE_PROBE_RESP) {
 			ieee80211_rx_mgmt_probe_resp(dev, mgmt,
 						     skb->len,
rx_status);
+			dev_kfree_skb(skb);
+			return TXRX_QUEUED;
 		} else if ((fc & IEEE80211_FCTL_STYPE) ==
IEEE80211_STYPE_BEACON) {
 			ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len,
 						 rx_status);
+			dev_kfree_skb(skb);
+			return TXRX_QUEUED;
 		}
 	}
-
-	dev_kfree_skb(skb);
+	return TXRX_CONTINUE;
 }
 
 
@@ -1985,7 +2003,7 @@ void ieee80211_sta_work(struct work_struct *work)
 	if (!netif_running(dev))
 		return;
 
-	if (local->sta_scanning)
+	if (local->sta_sw_scanning || local->sta_hw_scanning)
 		return;
 
 	if (sdata->type != IEEE80211_IF_TYPE_STA &&
@@ -2643,9 +2661,15 @@ void ieee80211_scan_completed(struct ieee80211_hw
*hw)
 	union iwreq_data wrqu;
 
 	local->last_scan_completed = jiffies;
-	wmb();
-	local->sta_scanning = 0;
+	memset(&wrqu, 0, sizeof(wrqu));
+	wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
 
+	if (local->sta_hw_scanning) {
+		local->sta_hw_scanning = 0;
+		goto done;
+	}
+
+	local->sta_sw_scanning = 0;
 	if (ieee80211_hw_config(local))
 		printk(KERN_DEBUG "%s: failed to restore operational"
 		       "channel after scan\n", dev->name);
@@ -2661,9 +2685,6 @@ void ieee80211_scan_completed(struct ieee80211_hw
*hw)
 
 	netif_tx_unlock_bh(local->mdev);
 
-	memset(&wrqu, 0, sizeof(wrqu));
-	wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
-
 	rcu_read_lock();
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 
@@ -2681,6 +2702,7 @@ void ieee80211_scan_completed(struct ieee80211_hw
*hw)
 	}
 	rcu_read_unlock();
 
+done:
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
 		struct ieee80211_if_sta *ifsta = &sdata->u.sta;
@@ -2703,7 +2725,7 @@ void ieee80211_sta_scan_work(struct work_struct
*work)
 	int skip;
 	unsigned long next_delay = 0;
 
-	if (!local->sta_scanning)
+	if (!local->sta_sw_scanning)
 		return;
 
 	switch (local->scan_state) {
@@ -2766,7 +2788,7 @@ void ieee80211_sta_scan_work(struct work_struct
*work)
 		break;
 	}
 
-	if (local->sta_scanning)
+	if (local->sta_sw_scanning)
 		queue_delayed_work(local->hw.workqueue,
&local->scan_work,
 				   next_delay);
 }
@@ -2798,7 +2820,7 @@ static int ieee80211_sta_start_scan(struct
net_device *dev,
 	  * ResultCode: SUCCESS, INVALID_PARAMETERS
 	 */
 
-	if (local->sta_scanning) {
+	if (local->sta_sw_scanning || local->sta_hw_scanning) {
 		if (local->scan_dev == dev)
 			return 0;
 		return -EBUSY;
@@ -2806,15 +2828,15 @@ static int ieee80211_sta_start_scan(struct
net_device *dev,
 
 	if (local->ops->hw_scan) {
 		int rc = local->ops->hw_scan(local_to_hw(local),
-					    ssid, ssid_len);
+					     ssid, ssid_len);
 		if (!rc) {
-			local->sta_scanning = 1;
+			local->sta_hw_scanning = 1;
 			local->scan_dev = dev;
 		}
 		return rc;
 	}
 
-	local->sta_scanning = 1;
+	local->sta_sw_scanning = 1;
 
 	rcu_read_lock();
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
@@ -2869,7 +2891,7 @@ int ieee80211_sta_req_scan(struct net_device *dev,
u8 *ssid, size_t ssid_len)
 	if (sdata->type != IEEE80211_IF_TYPE_STA)
 		return ieee80211_sta_start_scan(dev, ssid, ssid_len);
 
-	if (local->sta_scanning) {
+	if (local->sta_sw_scanning || local->sta_hw_scanning) {
 		if (local->scan_dev == dev)
 			return 0;
 		return -EBUSY;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 428a9fc..596ff71 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -338,8 +338,14 @@ ieee80211_rx_h_passive_scan(struct
ieee80211_txrx_data *rx)
 	struct ieee80211_local *local = rx->local;
 	struct sk_buff *skb = rx->skb;
 
-	if (unlikely(local->sta_scanning != 0)) {
-		ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
+	if (unlikely(local->sta_hw_scanning))
+		return ieee80211_sta_rx_scan(rx->dev, skb,
rx->u.rx.status);
+
+	if (unlikely(local->sta_sw_scanning)) {
+		/* drop all the other packets during a software scan
anyway */
+		if (ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status)
+		    != TXRX_QUEUED)
+			dev_kfree_skb(skb);
 		return TXRX_QUEUED;
 	}
 
@@ -1486,7 +1492,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw,
struct sk_buff *skb,
 		goto end;
 	}
 
-	if (unlikely(local->sta_scanning))
+	if (unlikely(local->sta_sw_scanning || local->sta_hw_scanning))
 		rx.flags |= IEEE80211_TXRXD_RXIN_SCAN;
 
 	if (__ieee80211_invoke_rx_handlers(local,
local->rx_pre_handlers, &rx,
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 1a53154..a6a657f 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -225,7 +225,7 @@ ieee80211_tx_h_check_assoc(struct
ieee80211_txrx_data *tx)
 	if (unlikely(tx->flags & IEEE80211_TXRXD_TX_INJECTED))
 		return TXRX_CONTINUE;
 
-	if (unlikely(tx->local->sta_scanning != 0) &&
+	if (unlikely(tx->local->sta_sw_scanning) &&
 	    ((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
 	     (tx->fc & IEEE80211_FCTL_STYPE) !=
IEEE80211_STYPE_PROBE_REQ))
 		return TXRX_DROP;

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/2] mac80211: hardware scan rework (V3)
  2007-11-22  2:53 [PATCH 1/2] mac80211: hardware scan rework (V3) Zhu Yi
  2007-11-22  2:53 ` [PATCH 2/2] iwlwifi: cache mac80211 conf setting during a hardware scan Zhu Yi
  2007-11-22  5:18 ` [PATCH 1/2] mac80211: hardware scan rework (V3) Abbas, Mohamed
@ 2007-11-22 13:09 ` Johannes Berg
  2 siblings, 0 replies; 4+ messages in thread
From: Johannes Berg @ 2007-11-22 13:09 UTC (permalink / raw)
  To: Zhu Yi; +Cc: linville, linux-wireless, Mohamed Abbas, Ben Cahill

[-- Attachment #1: Type: text/plain, Size: 12028 bytes --]


> The scan code in mac80211 makes the software scan assumption in various
> places. For example, we stop the Tx queue during a software scan so that
> all the Tx packets will be queued by the stack. We also drop frames not
> related to scan in the software scan process. But these are not true for
> hardware scan.
> 
> Some wireless hardwares (for example iwl3945/4965) has the ability to
> perform the whole scan process by hardware and/or firmware. The hardware
> scan is relative powerful in that it tries to maintain normal network
> traffic while doing a scan in the background. Some drivers (i.e iwlwifi)
> do provide a way to tune the hardware scan parameters (for example if the
> STA is associated, what's the max time could the STA leave from the
> associated channel, how long the scans get suspended after returning to
> the service channel, etc). But basically this is transparent to the
> stack. mac80211 should not stop Tx queues or drop Rx packets during a
> hardware scan.
> 
> This patch resolves the above problem by spliting the current scan
> indicator local->sta_scanning into local->sta_sw_scanning and
> local->sta_hw_scanning. It then changes the scan related code to be aware
> of hardware scan or software scan in various places. With this patch,
> iwlwifi performs much better in the scan-while-associated condition and
> disable_hw_scan=1 should never be required.
> 
> Cc: Mohamed Abbas <mohamed.abbas@intel.com>
> Cc: Ben Cahill <ben.m.cahill@intel.com>
> Signed-off-by: Zhu Yi <yi.zhu@intel.com>

Looks good to me.
Acked-by: Johannes Berg <johannes@sipsolutions.net>

> 
> diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
> index 59350b8..acbe717 100644
> --- a/net/mac80211/ieee80211.c
> +++ b/net/mac80211/ieee80211.c
> @@ -351,11 +351,14 @@ static int ieee80211_stop(struct net_device *dev)
>  		synchronize_rcu();
>  		skb_queue_purge(&sdata->u.sta.skb_queue);
>  
> -		if (!local->ops->hw_scan &&
> -		    local->scan_dev == sdata->dev) {
> -			local->sta_scanning = 0;
> -			cancel_delayed_work(&local->scan_work);
> +		if (local->scan_dev == sdata->dev) {
> +			if (!local->ops->hw_scan) {
> +				local->sta_sw_scanning = 0;
> +				cancel_delayed_work(&local->scan_work);
> +			} else
> +				local->sta_hw_scanning = 0;
>  		}
> +
>  		flush_workqueue(local->hw.workqueue);
>  		/* fall through */
>  	default:
> @@ -523,7 +526,7 @@ int ieee80211_hw_config(struct ieee80211_local *local)
>  	struct ieee80211_channel *chan;
>  	int ret = 0;
>  
> -	if (local->sta_scanning) {
> +	if (local->sta_sw_scanning) {
>  		chan = local->scan_channel;
>  		mode = local->scan_hw_mode;
>  	} else {
> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
> index 72e1c93..35829b1 100644
> --- a/net/mac80211/ieee80211_i.h
> +++ b/net/mac80211/ieee80211_i.h
> @@ -470,7 +470,8 @@ struct ieee80211_local {
>  
>  	struct list_head interfaces;
>  
> -	int sta_scanning;
> +	bool sta_sw_scanning;
> +	bool sta_hw_scanning;
>  	int scan_channel_idx;
>  	enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
>  	unsigned long last_scan_completed;
> @@ -749,7 +750,8 @@ int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len);
>  void ieee80211_sta_req_auth(struct net_device *dev,
>  			    struct ieee80211_if_sta *ifsta);
>  int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len);
> -void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
> +ieee80211_txrx_result ieee80211_sta_rx_scan(struct net_device *dev,
> +					    struct sk_buff *skb,
>  			   struct ieee80211_rx_status *rx_status);
>  void ieee80211_rx_bss_list_init(struct net_device *dev);
>  void ieee80211_rx_bss_list_deinit(struct net_device *dev);
> diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c
> index 942b9cc..f2d84f3 100644
> --- a/net/mac80211/ieee80211_ioctl.c
> +++ b/net/mac80211/ieee80211_ioctl.c
> @@ -315,7 +315,7 @@ int ieee80211_set_channel(struct ieee80211_local *local, int channel, int freq)
>  	}
>  
>  	if (set) {
> -		if (local->sta_scanning)
> +		if (local->sta_sw_scanning)
>  			ret = 0;
>  		else
>  			ret = ieee80211_hw_config(local);
> @@ -558,8 +558,10 @@ static int ieee80211_ioctl_giwscan(struct net_device *dev,
>  {
>  	int res;
>  	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
> -	if (local->sta_scanning)
> +
> +	if (local->sta_sw_scanning || local->sta_hw_scanning)
>  		return -EAGAIN;
> +
>  	res = ieee80211_sta_scan_results(dev, extra, data->length);
>  	if (res >= 0) {
>  		data->length = res;
> diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c
> index 015b3f8..26f404a 100644
> --- a/net/mac80211/ieee80211_sta.c
> +++ b/net/mac80211/ieee80211_sta.c
> @@ -1487,8 +1487,18 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
>  		u32 supp_rates, prev_rates;
>  		int i, j;
>  
> -		mode = local->sta_scanning ?
> +		mode = local->sta_sw_scanning ?
>  		       local->scan_hw_mode : local->oper_hw_mode;
> +
> +		if (local->sta_hw_scanning) {
> +			/* search for the correct mode matches the beacon */
> +			list_for_each_entry(mode, &local->modes_list, list)
> +				if (mode->mode == rx_status->phymode)
> +					break;
> +
> +			if (mode == NULL)
> +				mode = local->oper_hw_mode;
> +		}
>  		rates = mode->rates;
>  		num_rates = mode->num_rates;
>  
> @@ -1871,31 +1881,39 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
>  }
>  
> 
> -void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
> -			   struct ieee80211_rx_status *rx_status)
> +ieee80211_txrx_result
> +ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
> +		      struct ieee80211_rx_status *rx_status)
>  {
>  	struct ieee80211_mgmt *mgmt;
>  	u16 fc;
>  
> -	if (skb->len < 24) {
> -		dev_kfree_skb(skb);
> -		return;
> -	}
> +	if (skb->len < 2)
> +		return TXRX_DROP;
>  
>  	mgmt = (struct ieee80211_mgmt *) skb->data;
>  	fc = le16_to_cpu(mgmt->frame_control);
>  
> +	if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL)
> +		return TXRX_CONTINUE;
> +
> +	if (skb->len < 24)
> +		return TXRX_DROP;
> +
>  	if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
>  		if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP) {
>  			ieee80211_rx_mgmt_probe_resp(dev, mgmt,
>  						     skb->len, rx_status);
> +			dev_kfree_skb(skb);
> +			return TXRX_QUEUED;
>  		} else if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) {
>  			ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len,
>  						 rx_status);
> +			dev_kfree_skb(skb);
> +			return TXRX_QUEUED;
>  		}
>  	}
> -
> -	dev_kfree_skb(skb);
> +	return TXRX_CONTINUE;
>  }
>  
> 
> @@ -1985,7 +2003,7 @@ void ieee80211_sta_work(struct work_struct *work)
>  	if (!netif_running(dev))
>  		return;
>  
> -	if (local->sta_scanning)
> +	if (local->sta_sw_scanning || local->sta_hw_scanning)
>  		return;
>  
>  	if (sdata->type != IEEE80211_IF_TYPE_STA &&
> @@ -2643,9 +2661,15 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
>  	union iwreq_data wrqu;
>  
>  	local->last_scan_completed = jiffies;
> -	wmb();
> -	local->sta_scanning = 0;
> +	memset(&wrqu, 0, sizeof(wrqu));
> +	wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
>  
> +	if (local->sta_hw_scanning) {
> +		local->sta_hw_scanning = 0;
> +		goto done;
> +	}
> +
> +	local->sta_sw_scanning = 0;
>  	if (ieee80211_hw_config(local))
>  		printk(KERN_DEBUG "%s: failed to restore operational"
>  		       "channel after scan\n", dev->name);
> @@ -2661,9 +2685,6 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
>  
>  	netif_tx_unlock_bh(local->mdev);
>  
> -	memset(&wrqu, 0, sizeof(wrqu));
> -	wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
> -
>  	rcu_read_lock();
>  	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
>  
> @@ -2681,6 +2702,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
>  	}
>  	rcu_read_unlock();
>  
> +done:
>  	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
>  	if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
>  		struct ieee80211_if_sta *ifsta = &sdata->u.sta;
> @@ -2703,7 +2725,7 @@ void ieee80211_sta_scan_work(struct work_struct *work)
>  	int skip;
>  	unsigned long next_delay = 0;
>  
> -	if (!local->sta_scanning)
> +	if (!local->sta_sw_scanning)
>  		return;
>  
>  	switch (local->scan_state) {
> @@ -2766,7 +2788,7 @@ void ieee80211_sta_scan_work(struct work_struct *work)
>  		break;
>  	}
>  
> -	if (local->sta_scanning)
> +	if (local->sta_sw_scanning)
>  		queue_delayed_work(local->hw.workqueue, &local->scan_work,
>  				   next_delay);
>  }
> @@ -2798,7 +2820,7 @@ static int ieee80211_sta_start_scan(struct net_device *dev,
>  	  * ResultCode: SUCCESS, INVALID_PARAMETERS
>  	 */
>  
> -	if (local->sta_scanning) {
> +	if (local->sta_sw_scanning || local->sta_hw_scanning) {
>  		if (local->scan_dev == dev)
>  			return 0;
>  		return -EBUSY;
> @@ -2806,15 +2828,15 @@ static int ieee80211_sta_start_scan(struct net_device *dev,
>  
>  	if (local->ops->hw_scan) {
>  		int rc = local->ops->hw_scan(local_to_hw(local),
> -					    ssid, ssid_len);
> +					     ssid, ssid_len);
>  		if (!rc) {
> -			local->sta_scanning = 1;
> +			local->sta_hw_scanning = 1;
>  			local->scan_dev = dev;
>  		}
>  		return rc;
>  	}
>  
> -	local->sta_scanning = 1;
> +	local->sta_sw_scanning = 1;
>  
>  	rcu_read_lock();
>  	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
> @@ -2869,7 +2891,7 @@ int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len)
>  	if (sdata->type != IEEE80211_IF_TYPE_STA)
>  		return ieee80211_sta_start_scan(dev, ssid, ssid_len);
>  
> -	if (local->sta_scanning) {
> +	if (local->sta_sw_scanning || local->sta_hw_scanning) {
>  		if (local->scan_dev == dev)
>  			return 0;
>  		return -EBUSY;
> diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
> index 428a9fc..596ff71 100644
> --- a/net/mac80211/rx.c
> +++ b/net/mac80211/rx.c
> @@ -338,8 +338,14 @@ ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
>  	struct ieee80211_local *local = rx->local;
>  	struct sk_buff *skb = rx->skb;
>  
> -	if (unlikely(local->sta_scanning != 0)) {
> -		ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
> +	if (unlikely(local->sta_hw_scanning))
> +		return ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
> +
> +	if (unlikely(local->sta_sw_scanning)) {
> +		/* drop all the other packets during a software scan anyway */
> +		if (ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status)
> +		    != TXRX_QUEUED)
> +			dev_kfree_skb(skb);
>  		return TXRX_QUEUED;
>  	}
>  
> @@ -1486,7 +1492,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
>  		goto end;
>  	}
>  
> -	if (unlikely(local->sta_scanning))
> +	if (unlikely(local->sta_sw_scanning || local->sta_hw_scanning))
>  		rx.flags |= IEEE80211_TXRXD_RXIN_SCAN;
>  
>  	if (__ieee80211_invoke_rx_handlers(local, local->rx_pre_handlers, &rx,
> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
> index 1a53154..a6a657f 100644
> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -225,7 +225,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
>  	if (unlikely(tx->flags & IEEE80211_TXRXD_TX_INJECTED))
>  		return TXRX_CONTINUE;
>  
> -	if (unlikely(tx->local->sta_scanning != 0) &&
> +	if (unlikely(tx->local->sta_sw_scanning) &&
>  	    ((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
>  	     (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
>  		return TXRX_DROP;
> -
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2007-11-22 13:09 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-11-22  2:53 [PATCH 1/2] mac80211: hardware scan rework (V3) Zhu Yi
2007-11-22  2:53 ` [PATCH 2/2] iwlwifi: cache mac80211 conf setting during a hardware scan Zhu Yi
2007-11-22  5:18 ` [PATCH 1/2] mac80211: hardware scan rework (V3) Abbas, Mohamed
2007-11-22 13:09 ` Johannes Berg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).