Linux wireless drivers development
 help / color / mirror / Atom feed
* Re: [RFC] rt2x00: Add autowakeup timer for receiving beacons while in powersave mode
From: Johannes Stezenbach @ 2011-01-31 18:17 UTC (permalink / raw)
  To: Ivo van Doorn; +Cc: users, linux-wireless, Johannes Berg
In-Reply-To: <201101311600.39486.IvDoorn@gmail.com>

Hi,

On Mon, Jan 31, 2011 at 04:00:38PM +0100, Ivo van Doorn wrote:
> --- a/drivers/net/wireless/rt2x00/rt2x00config.c
> +++ b/drivers/net/wireless/rt2x00/rt2x00config.c
> @@ -192,6 +192,11 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
>  		       sizeof(libconf.channel));
>  	}
>  
> +	if (test_bit(DRIVER_REQUIRE_PS_AUTOWAKE, &rt2x00dev->flags) &&
> +	    (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) &&
> +	    !(conf->flags & IEEE80211_CONF_PS))
> +		cancel_delayed_work_sync(&rt2x00dev->autowakeup_work);	
> +
>  	/*
>  	 * Start configuration.
>  	 */
> @@ -204,6 +209,13 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
>  	if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL)
>  		rt2x00link_reset_tuner(rt2x00dev, false);
>  
> +	if (test_bit(DRIVER_REQUIRE_PS_AUTOWAKE, &rt2x00dev->flags) &&
> +	    (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) &&
> +	    (conf->flags & IEEE80211_CONF_PS))
> +		queue_delayed_work(rt2x00dev->workqueue,
> +				   &rt2x00dev->autowakeup_work,
> +				   msecs_to_jiffies(rt2x00dev->beacon_int) - 1);
> +

I have doubts about this part.  Doesn't this just catch
one beacon?  I'm not sure how often mac80211 calls the .config
op, I thought it is called once when going to sleep, and then the
hw/fw/driver is responsible to wakeup every n'th beacon
(according to the configured listen interval).

Also, I think rt2x00dev->beacon_int is in TUs, not msecs.


Johannes

^ permalink raw reply

* [PATCH 1/5] ath9k_htc: cancel ani work in ath9k_htc_stop
From: Rajkumar Manoharan @ 2011-01-31 18:17 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, Rajkumar Manoharan

ani work is cancelled in dissaoctiation. But in some cases
during suspend, deauthention never be called. So we failed
to stop ani work which was identified by the following
warning.

Call Trace:
 [<ffffffffa0454a1d>] ieee80211_can_queue_work.clone.17+0x2d/0x40 [mac80211]
 [<ffffffffa0454a60>] ieee80211_queue_delayed_work+0x30/0x60 [mac80211]
 [<ffffffffa0567f82>] ath9k_ani_work+0x142/0x250 [ath9k_htc]
 [<ffffffff81073c70>] async_run_entry_fn+0x0/0x180
 [<ffffffffa0567e40>] ath9k_ani_work+0x0/0x250 [ath9k_htc]

Signed-off-by: Rajkumar Manoharan <rmanoharan@atheros.com>
---
 drivers/net/wireless/ath/ath9k/htc_drv_main.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index a702089..0526c25 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -1052,6 +1052,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
 	cancel_work_sync(&priv->fatal_work);
 	cancel_work_sync(&priv->ps_work);
 	cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
+	cancel_delayed_work_sync(&priv->ath9k_ani_work);
 	ath9k_led_stop_brightness(priv);
 
 	mutex_lock(&priv->mutex);
-- 
1.7.3.5


^ permalink raw reply related

* [PATCH 2/5] ath9k: use common get current channel function
From: Rajkumar Manoharan @ 2011-01-31 18:17 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, Rajkumar Manoharan
In-Reply-To: <1296497866-3189-1-git-send-email-rmanoharan@atheros.com>

Signed-off-by: Rajkumar Manoharan <rmanoharan@atheros.com>
---
 drivers/net/wireless/ath/ath9k/main.c |   19 +++----------------
 1 files changed, 3 insertions(+), 16 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 20c70ba..9e3faa6 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -64,19 +64,6 @@ static u8 parse_mpdudensity(u8 mpdudensity)
 	}
 }
 
-static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc,
-						struct ieee80211_hw *hw)
-{
-	struct ieee80211_channel *curchan = hw->conf.channel;
-	struct ath9k_channel *channel;
-	u8 chan_idx;
-
-	chan_idx = curchan->hw_value;
-	channel = &sc->sc_ah->channels[chan_idx];
-	ath9k_cmn_update_ichannel(channel, curchan, hw->conf.channel_type);
-	return channel;
-}
-
 bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode)
 {
 	unsigned long flags;
@@ -861,7 +848,7 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
 	ath9k_hw_configpcipowersave(ah, 0, 0);
 
 	if (!ah->curchan)
-		ah->curchan = ath_get_curchannel(sc, sc->hw);
+		ah->curchan = ath9k_cmn_get_curchannel(sc->hw, ah);
 
 	r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
 	if (r) {
@@ -922,7 +909,7 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
 	ath_flushrecv(sc);		/* flush recv queue */
 
 	if (!ah->curchan)
-		ah->curchan = ath_get_curchannel(sc, hw);
+		ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
 
 	r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
 	if (r) {
@@ -1023,7 +1010,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
 	/* setup initial channel */
 	sc->chan_idx = curchan->hw_value;
 
-	init_channel = ath_get_curchannel(sc, hw);
+	init_channel = ath9k_cmn_get_curchannel(hw, ah);
 
 	/* Reset SERDES registers */
 	ath9k_hw_configpcipowersave(ah, 0, 0);
-- 
1.7.3.5


^ permalink raw reply related

* [PATCH 3/5] ath9k: move update tx power to common
From: Rajkumar Manoharan @ 2011-01-31 18:17 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, Rajkumar Manoharan
In-Reply-To: <1296497866-3189-1-git-send-email-rmanoharan@atheros.com>

move ath_update_txpow to common to remove code duplication
in both ath9k & ath9k_htc.

Signed-off-by: Rajkumar Manoharan <rmanoharan@atheros.com>
---
 drivers/net/wireless/ath/ath9k/common.c |   11 +++++++++++
 drivers/net/wireless/ath/ath9k/common.h |    2 ++
 drivers/net/wireless/ath/ath9k/main.c   |   26 ++++++++++----------------
 3 files changed, 23 insertions(+), 16 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
index df1998d..615e682 100644
--- a/drivers/net/wireless/ath/ath9k/common.c
+++ b/drivers/net/wireless/ath/ath9k/common.c
@@ -189,6 +189,17 @@ void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common,
 }
 EXPORT_SYMBOL(ath9k_cmn_btcoex_bt_stomp);
 
+void ath9k_cmn_update_txpow(struct ath_hw *ah, u16 cur_txpow,
+			    u16 new_txpow, u16 *txpower)
+{
+	if (cur_txpow != new_txpow) {
+		ath9k_hw_set_txpowerlimit(ah, new_txpow, false);
+		/* read back in case value is clamped */
+		*txpower = ath9k_hw_regulatory(ah)->power_limit;
+	}
+}
+EXPORT_SYMBOL(ath9k_cmn_update_txpow);
+
 static int __init ath9k_cmn_init(void)
 {
 	return 0;
diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h
index 4c7020b..b2f7b5f 100644
--- a/drivers/net/wireless/ath/ath9k/common.h
+++ b/drivers/net/wireless/ath/ath9k/common.h
@@ -68,3 +68,5 @@ struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
 int ath9k_cmn_count_streams(unsigned int chainmask, int max);
 void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common,
 				  enum ath_stomp_type stomp_type);
+void ath9k_cmn_update_txpow(struct ath_hw *ah, u16 cur_txpow,
+			    u16 new_txpow, u16 *txpower);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 9e3faa6..21e8d29 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -18,17 +18,6 @@
 #include "ath9k.h"
 #include "btcoex.h"
 
-static void ath_update_txpow(struct ath_softc *sc)
-{
-	struct ath_hw *ah = sc->sc_ah;
-
-	if (sc->curtxpow != sc->config.txpowlimit) {
-		ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false);
-		/* read back in case value is clamped */
-		sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
-	}
-}
-
 static u8 parse_mpdudensity(u8 mpdudensity)
 {
 	/*
@@ -271,7 +260,8 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
 		goto ps_restore;
 	}
 
-	ath_update_txpow(sc);
+	ath9k_cmn_update_txpow(ah, sc->curtxpow,
+			       sc->config.txpowlimit, &sc->curtxpow);
 	ath9k_hw_set_interrupts(ah, ah->imask);
 
 	if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) {
@@ -857,7 +847,8 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
 			channel->center_freq, r);
 	}
 
-	ath_update_txpow(sc);
+	ath9k_cmn_update_txpow(ah, sc->curtxpow,
+			       sc->config.txpowlimit, &sc->curtxpow);
 	if (ath_startrecv(sc) != 0) {
 		ath_err(common, "Unable to restart recv logic\n");
 		goto out;
@@ -960,7 +951,8 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
 	 * that changes the channel so update any state that
 	 * might change as a result.
 	 */
-	ath_update_txpow(sc);
+	ath9k_cmn_update_txpow(ah, sc->curtxpow,
+			       sc->config.txpowlimit, &sc->curtxpow);
 
 	if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL)))
 		ath_beacon_config(sc, NULL);	/* restart beacons */
@@ -1036,7 +1028,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
 	 * This is needed only to setup initial state
 	 * but it's best done after a reset.
 	 */
-	ath_update_txpow(sc);
+	ath9k_cmn_update_txpow(ah, sc->curtxpow,
+			sc->config.txpowlimit, &sc->curtxpow);
 
 	/*
 	 * Setup the hardware after reset:
@@ -1701,7 +1694,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
 	if (changed & IEEE80211_CONF_CHANGE_POWER) {
 		sc->config.txpowlimit = 2 * conf->power_level;
 		ath9k_ps_wakeup(sc);
-		ath_update_txpow(sc);
+		ath9k_cmn_update_txpow(ah, sc->curtxpow,
+				       sc->config.txpowlimit, &sc->curtxpow);
 		ath9k_ps_restore(sc);
 	}
 
-- 
1.7.3.5


^ permalink raw reply related

* [PATCH 4/5] ath9k_htc: make use common of function to update txpower
From: Rajkumar Manoharan @ 2011-01-31 18:17 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, Rajkumar Manoharan
In-Reply-To: <1296497866-3189-1-git-send-email-rmanoharan@atheros.com>

Signed-off-by: Rajkumar Manoharan <rmanoharan@atheros.com>
---
 drivers/net/wireless/ath/ath9k/htc.h          |    1 -
 drivers/net/wireless/ath/ath9k/htc_drv_gpio.c |    3 ++-
 drivers/net/wireless/ath/ath9k/htc_drv_main.c |   23 ++++++++---------------
 3 files changed, 10 insertions(+), 17 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 6354986..0cb504d 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -460,7 +460,6 @@ void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv);
 void ath9k_ps_work(struct work_struct *work);
 bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
 			enum ath9k_power_mode mode);
-void ath_update_txpow(struct ath9k_htc_priv *priv);
 
 void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv);
 void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
index fe70f67..7e630a8 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
@@ -389,7 +389,8 @@ void ath9k_htc_radio_enable(struct ieee80211_hw *hw)
 			ret, ah->curchan->channel);
 	}
 
-	ath_update_txpow(priv);
+	ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
+			       &priv->curtxpow);
 
 	/* Start RX */
 	WMI_CMD(WMI_START_RECV_CMDID);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index 0526c25..953036a 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -24,17 +24,6 @@ static struct dentry *ath9k_debugfs_root;
 /* Utilities */
 /*************/
 
-void ath_update_txpow(struct ath9k_htc_priv *priv)
-{
-	struct ath_hw *ah = priv->ah;
-
-	if (priv->curtxpow != priv->txpowlimit) {
-		ath9k_hw_set_txpowerlimit(ah, priv->txpowlimit, false);
-		/* read back in case value is clamped */
-		priv->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
-	}
-}
-
 /* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */
 static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
 					      struct ath9k_channel *ichan)
@@ -147,7 +136,8 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv)
 			channel->center_freq, ret);
 	}
 
-	ath_update_txpow(priv);
+	ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
+			       &priv->curtxpow);
 
 	WMI_CMD(WMI_START_RECV_CMDID);
 	ath9k_host_rx_init(priv);
@@ -212,7 +202,8 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
 		goto err;
 	}
 
-	ath_update_txpow(priv);
+	ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
+			       &priv->curtxpow);
 
 	WMI_CMD(WMI_START_RECV_CMDID);
 	if (ret)
@@ -988,7 +979,8 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
 		return ret;
 	}
 
-	ath_update_txpow(priv);
+	ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
+			       &priv->curtxpow);
 
 	mode = ath9k_htc_get_curmode(priv, init_channel);
 	htc_mode = cpu_to_be16(mode);
@@ -1254,7 +1246,8 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
 
 	if (changed & IEEE80211_CONF_CHANGE_POWER) {
 		priv->txpowlimit = 2 * conf->power_level;
-		ath_update_txpow(priv);
+		ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
+				       priv->txpowlimit, &priv->curtxpow);
 	}
 
 	if (changed & IEEE80211_CONF_CHANGE_IDLE) {
-- 
1.7.3.5


^ permalink raw reply related

* [PATCH 5/5] ath9k: do not access hw registers in FULL SLEEP
From: Rajkumar Manoharan @ 2011-01-31 18:17 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, Rajkumar Manoharan
In-Reply-To: <1296497866-3189-1-git-send-email-rmanoharan@atheros.com>

The opmode recalculation is accessing hw registers.
When it is called from remove interface callback and
if there are no vifs present then hw is moved to FULL SLEEP
by radio disable. So use power save wrappers before
accessing hw registers in calculating opmode state.

Signed-off-by: Rajkumar Manoharan <rmanoharan@atheros.com>
---
 drivers/net/wireless/ath/ath9k/main.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 21e8d29..d838933 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1348,6 +1348,7 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
 
 	ath9k_calculate_iter_data(hw, vif, &iter_data);
 
+	ath9k_ps_wakeup(sc);
 	/* Set BSSID mask. */
 	memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
 	ath_hw_setbssidmask(common);
@@ -1382,6 +1383,7 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
 	}
 
 	ath9k_hw_set_interrupts(ah, ah->imask);
+	ath9k_ps_restore(sc);
 
 	/* Set up ANI */
 	if ((iter_data.naps + iter_data.nadhocs) > 0) {
-- 
1.7.3.5


^ permalink raw reply related

* Missing config_hw() in ieee80211_ibss_join?
From: Ben Greear @ 2011-01-31 18:29 UTC (permalink / raw)
  To: linux-wireless

The method ieee80211_ibss_join can change the operating channel,
but I don't see anywhere that it calls config_hw to flush the
changes into the kernel.

Should we add such a call?

This was found by code inspection...I didn't actually try using
this code...

Thanks,
Ben

-- 
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc  http://www.candelatech.com


^ permalink raw reply

* [PATCH v2] ath9k:  Show channel type and frequency in debugfs.
From: greearb @ 2011-01-31 18:37 UTC (permalink / raw)
  To: linux-wireless; +Cc: Ben Greear

From: Ben Greear <greearb@candelatech.com>

Signed-off-by: Ben Greear <greearb@candelatech.com>
---
v2:  Merge with upstream code (virtual wiphy removal)

:100644 100644 2a813d7... e803b5c... M	drivers/net/wireless/ath/ath9k/debug.c
 drivers/net/wireless/ath/ath9k/debug.c |   23 +++++++++++++++++++++--
 1 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 2a813d7..e803b5c 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -381,21 +381,40 @@ static const struct file_operations fops_interrupt = {
 	.llseek = default_llseek,
 };
 
+static const char *channel_type_str(enum nl80211_channel_type t)
+{
+	switch (t) {
+	case NL80211_CHAN_NO_HT:
+		return "no ht";
+	case NL80211_CHAN_HT20:
+		return "ht20";
+	case NL80211_CHAN_HT40MINUS:
+		return "ht40-";
+	case NL80211_CHAN_HT40PLUS:
+		return "ht40+";
+	default:
+		return "???";
+	}
+}
+
 static ssize_t read_file_wiphy(struct file *file, char __user *user_buf,
 			       size_t count, loff_t *ppos)
 {
 	struct ath_softc *sc = file->private_data;
 	struct ieee80211_channel *chan = sc->hw->conf.channel;
+	struct ieee80211_conf *conf = &(sc->hw->conf);
 	char buf[512];
 	unsigned int len = 0;
 	u8 addr[ETH_ALEN];
 	u32 tmp;
 
 	len += snprintf(buf + len, sizeof(buf) - len,
-			"%s (chan=%d ht=%d)\n",
+			"%s (chan=%d  center-freq: %d MHz  channel-type: %d (%s))\n",
 			wiphy_name(sc->hw->wiphy),
 			ieee80211_frequency_to_channel(chan->center_freq),
-			conf_is_ht(&sc->hw->conf));
+			chan->center_freq,
+			conf->channel_type,
+			channel_type_str(conf->channel_type));
 
 	put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_STA_ID0), addr);
 	put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4);
-- 
1.7.2.3


^ permalink raw reply related

* [PATCH] mac80211:  Recalculate channel-type on iface removal.
From: greearb @ 2011-01-31 18:37 UTC (permalink / raw)
  To: linux-wireless; +Cc: Ben Greear

From: Ben Greear <greearb@candelatech.com>

When a vif goes away, it could cause the super-chan
to be recalculated differently, so do that calculation
on iface removal.

Also, use WARN_ON_ONCE instead of WARN_ON if user has
ht40- stations in conjunction with ht40+.  It's not
really a kernel bug, just a mis-configuration of the
user's wifi environment.

Signed-off-by: Ben Greear <greearb@candelatech.com>
---
:100644 100644 5b24740... ad9055d... M	net/mac80211/chan.c
:100644 100644 605c222... abf5f8e... M	net/mac80211/iface.c
 net/mac80211/chan.c  |    4 ++--
 net/mac80211/iface.c |    9 ++++++++-
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 5b24740..ad9055d 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -80,11 +80,11 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
 			superchan = tmp->vif.bss_conf.channel_type;
 			break;
 		case NL80211_CHAN_HT40PLUS:
-			WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
+			WARN_ON_ONCE(superchan == NL80211_CHAN_HT40MINUS);
 			superchan = NL80211_CHAN_HT40PLUS;
 			break;
 		case NL80211_CHAN_HT40MINUS:
-			WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
+			WARN_ON_ONCE(superchan == NL80211_CHAN_HT40PLUS);
 			superchan = NL80211_CHAN_HT40MINUS;
 			break;
 		}
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 605c222..abf5f8e 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -382,6 +382,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 	struct sk_buff *skb, *tmp;
 	u32 hw_reconf_flags = 0;
 	int i;
+	enum nl80211_channel_type orig_ct;
 
 	if (local->scan_sdata == sdata)
 		ieee80211_scan_cancel(local);
@@ -542,8 +543,14 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 		hw_reconf_flags = 0;
 	}
 
+	/* Re-calculate channel-type, in case there are multiple vifs
+	 * on different channel types.
+	 */
+	orig_ct = local->_oper_channel_type;
+	ieee80211_set_channel_type(local, NULL, NL80211_CHAN_NO_HT);
+
 	/* do after stop to avoid reconfiguring when we stop anyway */
-	if (hw_reconf_flags)
+	if (hw_reconf_flags || (orig_ct != local->_oper_channel_type))
 		ieee80211_hw_config(local, hw_reconf_flags);
 
 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-- 
1.7.2.3


^ permalink raw reply related

* Re: Missing config_hw() in ieee80211_ibss_join?
From: Johannes Berg @ 2011-01-31 18:43 UTC (permalink / raw)
  To: Ben Greear; +Cc: linux-wireless
In-Reply-To: <4D46FF7E.9080409@candelatech.com>

On Mon, 2011-01-31 at 10:29 -0800, Ben Greear wrote:
> The method ieee80211_ibss_join can change the operating channel,
> but I don't see anywhere that it calls config_hw to flush the
> changes into the kernel.
> 
> Should we add such a call?

I don't think so -- it doesn't actually really join until it has scanned
first etc.?

johannes


^ permalink raw reply

* Re: [PATCH] ath9k: Fix memory leak due to failed PAPRD frames
From: Luis R. Rodriguez @ 2011-01-31 18:43 UTC (permalink / raw)
  To: Mohammed Shafi Shajakhan; +Cc: linville, linux-wireless
In-Reply-To: <1296460529-13669-1-git-send-email-mshajakhan@atheros.com>

On Sun, Jan 30, 2011 at 11:55 PM, Mohammed Shafi Shajakhan
<mshajakhan@atheros.com> wrote:
> From: Mohammed Shafi Shajakhan <mshajakhan@atheros.com>
>
> free the skb's when the Tx of PAPRD frames fails and also add a debug
> message indicating that.
>
> Signed-off-by: Mohammed Shafi Shajakhan <mshajakhan@atheros.com>

Is this a stable fix?

 Luis

^ permalink raw reply

* Re: [PATCH] cfg80211: fix maximum tx power handling
From: Luis R. Rodriguez @ 2011-01-31 18:43 UTC (permalink / raw)
  To: Mark Mentovai, John W. Linville; +Cc: Felix Fietkau, linux-wireless
In-Reply-To: <AANLkTi=4z31H+v2=Dve2FY3aw0iEBm5UQPz55Qx9UkwR@mail.gmail.com>

John, I'll review this today.

  Luis

^ permalink raw reply

* Re: [PATCH] mac80211:  Recalculate channel-type on iface removal.
From: Johannes Berg @ 2011-01-31 18:44 UTC (permalink / raw)
  To: greearb; +Cc: linux-wireless
In-Reply-To: <1296499072-6721-1-git-send-email-greearb@candelatech.com>

On Mon, 2011-01-31 at 10:37 -0800, greearb@candelatech.com wrote:

> Also, use WARN_ON_ONCE instead of WARN_ON if user has
> ht40- stations in conjunction with ht40+.  It's not
> really a kernel bug, just a mis-configuration of the
> user's wifi environment.

But how did we end up here to start with -- shouldn't that have been
rejected?

johannes


^ permalink raw reply

* Re: Missing config_hw() in ieee80211_ibss_join?
From: Ben Greear @ 2011-01-31 18:45 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless
In-Reply-To: <1296499385.3812.40.camel@jlt3.sipsolutions.net>

On 01/31/2011 10:43 AM, Johannes Berg wrote:
> On Mon, 2011-01-31 at 10:29 -0800, Ben Greear wrote:
>> The method ieee80211_ibss_join can change the operating channel,
>> but I don't see anywhere that it calls config_hw to flush the
>> changes into the kernel.
>>
>> Should we add such a call?
>
> I don't think so -- it doesn't actually really join until it has scanned
> first etc.?

Ok, good enough for me.  I don't think this will cause any problems
with my scan-on-channel logic, but it looked a bit strange while
I was auditing the oper-channel related code.

Thanks,
Ben

-- 
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc  http://www.candelatech.com


^ permalink raw reply

* [PATCH] mac80211: disable power save if an infra AP vif exists
From: Rajkumar Manoharan @ 2011-01-31 18:45 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, Rajkumar Manoharan

PS should not be enabled if an infra AP vif exists in
the interface list. So while recalculating PS,
AP vif type should be taken into account.

Reviewed-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Rajkumar Manoharan <rmanoharan@atheros.com>
---
 net/mac80211/mlme.c |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 3221069..82e6b89 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -601,6 +601,15 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
 	list_for_each_entry(sdata, &local->interfaces, list) {
 		if (!ieee80211_sdata_running(sdata))
 			continue;
+		if ((sdata->vif.type == NL80211_IFTYPE_AP) ||
+		    (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) {
+			/* If an AP vif is found, then disable PS
+			 * by setting the count to zero thereby setting
+			 * ps_sdata to NULL.
+			 */
+			count = 0;
+			break;
+		}
 		if (sdata->vif.type != NL80211_IFTYPE_STATION)
 			continue;
 		found = sdata;
-- 
1.7.3.5


^ permalink raw reply related

* [PATCH 00/22] zd1211rw: add support for AP-mode
From: Jussi Kivilinna @ 2011-01-31 18:46 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz

This patchset adds AP-mode support to zd1211rw. Tested with zd1211b devices only.

Patches are mix of fixes and AP support adding code.
   [1-2]  fix oopses noticed while testing
     [3]  add missing locking
     [4]  fix beacon setup to match vendor driver (needed for AP mode)
   [5-6]  'may sleep' cleanups, move code from workers to mac80211-functions
  [7-11]  AP-mode supporting code
 [12-16]  beacon setup fixes/workarounds                                                          
 [17-20]  device reset and device/TX/RX stall workarounds
    [21]  enable AP mode
    [22]  add more debuging output

---

Christian Lamparter (1):
      mac80211: fix race between next beacon dtim and ieee80211_get_buffered_bc

Jussi Kivilinna (21):
      zd1211rw: use urb anchors for tx and fix tx-queue disabling
      zd1211rw: cancel process_intr work on zd_chip_disable_int()
      zd1211rw: add locking for mac->process_intr
      zd1211rw: fix beacon interval setup
      zd1211rw: move set_multicast_hash and set_rx_filter from workers to configure_filter
      zd1211rw: move set_rts_cts_work to bss_info_changed
      zd1211rw: support setting BSSID for AP mode
      zd1211rw: fix ack_pending in filter_ack causing tx-packet ordering problem on monitor
      zd1211rw: let zd_set_beacon_interval() set dtim_period and add AP-beacon flag
      zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc()
      zd1211rw: add beacon watchdog and setting HW beacon more failsafe
      zd1211rw: batch beacon config commands together
      [v2] zd1211rw: use stack and preallocated memory for small cmd-buffers
      zd1211rw: change interrupt URB buffer to DMA buffer
      zd1211rw: lower hw command timeouts
      zd1211rw: collect driver settings and add function to restore theim
      zd1211rw: add TX watchdog and device resetting
      zd1211rw: reset device when CR_BCN_FIFO_SEMAPHORE freezes in beacon setup
      zd1211rw: reset rx urbs after idle period of 30 seconds
      zd1211rw: enable NL80211_IFTYPE_AP
      zd1211rw: add useful debug output


 drivers/net/wireless/zd1211rw/zd_chip.c |  134 ++++++---
 drivers/net/wireless/zd1211rw/zd_chip.h |    5 
 drivers/net/wireless/zd1211rw/zd_mac.c  |  448 ++++++++++++++++++++++++-------
 drivers/net/wireless/zd1211rw/zd_mac.h  |   24 +-
 drivers/net/wireless/zd1211rw/zd_usb.c  |  445 ++++++++++++++++++++++++-------
 drivers/net/wireless/zd1211rw/zd_usb.h  |   30 ++
 net/mac80211/ieee80211_i.h              |    1 
 net/mac80211/tx.c                       |    4 
 8 files changed, 822 insertions(+), 269 deletions(-)

^ permalink raw reply

* [PATCH 01/22] zd1211rw: use urb anchors for tx and fix tx-queue disabling
From: Jussi Kivilinna @ 2011-01-31 18:47 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz
In-Reply-To: <20110131184657.10044.98610.stgit@fate.lan>

When stress testing AP-mode I hit OOPS when unpluging or rmmodding
driver.

It appears that when tx-queue is disabled, tx-urbs might be left pending.
These can cause ehci to call non-existing tx_urb_complete() (after rmmod)
or uninitialized/reseted private structure (after disconnect()). Add skb
queue for submitted packets and unlink pending urbs on zd_usb_disable_tx().

Part of the problem seems to be usb->free_urb_list that isn't always
working as it should, causing machine freeze when trying to free the list
in zd_usb_disable_tx(). Caching free urbs isn't what other drivers seem
to be doing (usbnet for example) so strip free_usb_list.

Patch makes tx-urb handling saner with use of urb anchors.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_usb.c |  108 +++++++++++---------------------
 drivers/net/wireless/zd1211rw/zd_usb.h |    8 +-
 2 files changed, 41 insertions(+), 75 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index 06041cb..c32a247 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -779,19 +779,20 @@ void zd_usb_disable_tx(struct zd_usb *usb)
 {
 	struct zd_usb_tx *tx = &usb->tx;
 	unsigned long flags;
-	struct list_head *pos, *n;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
 
 	spin_lock_irqsave(&tx->lock, flags);
-	list_for_each_safe(pos, n, &tx->free_urb_list) {
-		list_del(pos);
-		usb_free_urb(list_entry(pos, struct urb, urb_list));
-	}
-	tx->enabled = 0;
+	WARN_ON(tx->submitted_urbs != 0);
 	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
 	/* The stopped state is ignored, relying on ieee80211_wake_queues()
 	 * in a potentionally following zd_usb_enable_tx().
 	 */
-	spin_unlock_irqrestore(&tx->lock, flags);
 }
 
 /**
@@ -807,63 +808,13 @@ void zd_usb_enable_tx(struct zd_usb *usb)
 	struct zd_usb_tx *tx = &usb->tx;
 
 	spin_lock_irqsave(&tx->lock, flags);
-	tx->enabled = 1;
+	atomic_set(&tx->enabled, 1);
 	tx->submitted_urbs = 0;
 	ieee80211_wake_queues(zd_usb_to_hw(usb));
 	tx->stopped = 0;
 	spin_unlock_irqrestore(&tx->lock, flags);
 }
 
-/**
- * alloc_tx_urb - provides an tx URB
- * @usb: a &struct zd_usb pointer
- *
- * Allocates a new URB. If possible takes the urb from the free list in
- * usb->tx.
- */
-static struct urb *alloc_tx_urb(struct zd_usb *usb)
-{
-	struct zd_usb_tx *tx = &usb->tx;
-	unsigned long flags;
-	struct list_head *entry;
-	struct urb *urb;
-
-	spin_lock_irqsave(&tx->lock, flags);
-	if (list_empty(&tx->free_urb_list)) {
-		urb = usb_alloc_urb(0, GFP_ATOMIC);
-		goto out;
-	}
-	entry = tx->free_urb_list.next;
-	list_del(entry);
-	urb = list_entry(entry, struct urb, urb_list);
-out:
-	spin_unlock_irqrestore(&tx->lock, flags);
-	return urb;
-}
-
-/**
- * free_tx_urb - frees a used tx URB
- * @usb: a &struct zd_usb pointer
- * @urb: URB to be freed
- *
- * Frees the transmission URB, which means to put it on the free URB
- * list.
- */
-static void free_tx_urb(struct zd_usb *usb, struct urb *urb)
-{
-	struct zd_usb_tx *tx = &usb->tx;
-	unsigned long flags;
-
-	spin_lock_irqsave(&tx->lock, flags);
-	if (!tx->enabled) {
-		usb_free_urb(urb);
-		goto out;
-	}
-	list_add(&urb->urb_list, &tx->free_urb_list);
-out:
-	spin_unlock_irqrestore(&tx->lock, flags);
-}
-
 static void tx_dec_submitted_urbs(struct zd_usb *usb)
 {
 	struct zd_usb_tx *tx = &usb->tx;
@@ -905,6 +856,16 @@ static void tx_urb_complete(struct urb *urb)
 	struct sk_buff *skb;
 	struct ieee80211_tx_info *info;
 	struct zd_usb *usb;
+	struct zd_usb_tx *tx;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/*
+	 * grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by zd_mac_tx_to_dev or mac80211)
+	 */
+	usb = &zd_hw_mac(info->rate_driver_data[0])->chip.usb;
+	tx = &usb->tx;
 
 	switch (urb->status) {
 	case 0:
@@ -922,20 +883,15 @@ static void tx_urb_complete(struct urb *urb)
 		goto resubmit;
 	}
 free_urb:
-	skb = (struct sk_buff *)urb->context;
-	/*
-	 * grab 'usb' pointer before handing off the skb (since
-	 * it might be freed by zd_mac_tx_to_dev or mac80211)
-	 */
-	info = IEEE80211_SKB_CB(skb);
-	usb = &zd_hw_mac(info->rate_driver_data[0])->chip.usb;
 	zd_mac_tx_to_dev(skb, urb->status);
-	free_tx_urb(usb, urb);
+	usb_free_urb(urb);
 	tx_dec_submitted_urbs(usb);
 	return;
 resubmit:
+	usb_anchor_urb(urb, &tx->submitted);
 	r = usb_submit_urb(urb, GFP_ATOMIC);
 	if (r) {
+		usb_unanchor_urb(urb);
 		dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r);
 		goto free_urb;
 	}
@@ -958,8 +914,14 @@ int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb)
 	int r;
 	struct usb_device *udev = zd_usb_to_usbdev(usb);
 	struct urb *urb;
+	struct zd_usb_tx *tx = &usb->tx;
 
-	urb = alloc_tx_urb(usb);
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
 	if (!urb) {
 		r = -ENOMEM;
 		goto out;
@@ -968,13 +930,16 @@ int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb)
 	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
 		          skb->data, skb->len, tx_urb_complete, skb);
 
+	usb_anchor_urb(urb, &tx->submitted);
 	r = usb_submit_urb(urb, GFP_ATOMIC);
-	if (r)
+	if (r) {
+		usb_unanchor_urb(urb);
 		goto error;
+	}
 	tx_inc_submitted_urbs(usb);
 	return 0;
 error:
-	free_tx_urb(usb, urb);
+	usb_free_urb(urb);
 out:
 	return r;
 }
@@ -1005,9 +970,9 @@ static inline void init_usb_tx(struct zd_usb *usb)
 {
 	struct zd_usb_tx *tx = &usb->tx;
 	spin_lock_init(&tx->lock);
-	tx->enabled = 0;
+	atomic_set(&tx->enabled, 0);
 	tx->stopped = 0;
-	INIT_LIST_HEAD(&tx->free_urb_list);
+	init_usb_anchor(&tx->submitted);
 	tx->submitted_urbs = 0;
 }
 
@@ -1240,6 +1205,7 @@ static void disconnect(struct usb_interface *intf)
 	ieee80211_unregister_hw(hw);
 
 	/* Just in case something has gone wrong! */
+	zd_usb_disable_tx(usb);
 	zd_usb_disable_rx(usb);
 	zd_usb_disable_int(usb);
 
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h
index 1b1655c..233ce82 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.h
+++ b/drivers/net/wireless/zd1211rw/zd_usb.h
@@ -184,18 +184,18 @@ struct zd_usb_rx {
 
 /**
  * struct zd_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
  * @lock: lock for transmission
- * @free_urb_list: list of free URBs, contains all the URBs, which can be used
+ * @submitted: anchor for URBs sent to device
  * @submitted_urbs: atomic integer that counts the URBs having sent to the
  *	device, which haven't been completed
- * @enabled: enabled flag, indicates whether tx is enabled
  * @stopped: indicates whether higher level tx queues are stopped
  */
 struct zd_usb_tx {
+	atomic_t enabled;
 	spinlock_t lock;
-	struct list_head free_urb_list;
+	struct usb_anchor submitted;
 	int submitted_urbs;
-	int enabled;
 	int stopped;
 };
 


^ permalink raw reply related

* [PATCH 02/22] zd1211rw: cancel process_intr work on zd_chip_disable_int()
From: Jussi Kivilinna @ 2011-01-31 18:47 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz
In-Reply-To: <20110131184657.10044.98610.stgit@fate.lan>

OOPS if worker is running and disconnect() is called (triggered
by unpluging device). Much harder to trigger at this stage but
later when we have AP beacon work in process_intr it happens very
easy.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_chip.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 6a9b660..b644ced 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -1407,6 +1407,9 @@ void zd_chip_disable_int(struct zd_chip *chip)
 	mutex_lock(&chip->mutex);
 	zd_usb_disable_int(&chip->usb);
 	mutex_unlock(&chip->mutex);
+
+	/* cancel pending interrupt work */
+	cancel_work_sync(&zd_chip_to_mac(chip)->process_intr);
 }
 
 int zd_chip_enable_rxtx(struct zd_chip *chip)


^ permalink raw reply related

* [PATCH 03/22] zd1211rw: add locking for mac->process_intr
From: Jussi Kivilinna @ 2011-01-31 18:47 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz
In-Reply-To: <20110131184657.10044.98610.stgit@fate.lan>

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_mac.c |    6 +++++-
 drivers/net/wireless/zd1211rw/zd_usb.c |    2 ++
 2 files changed, 7 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 6107304..8b3d779 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -911,9 +911,13 @@ static int zd_op_config(struct ieee80211_hw *hw, u32 changed)
 static void zd_process_intr(struct work_struct *work)
 {
 	u16 int_status;
+	unsigned long flags;
 	struct zd_mac *mac = container_of(work, struct zd_mac, process_intr);
 
-	int_status = le16_to_cpu(*(__le16 *)(mac->intr_buffer+4));
+	spin_lock_irqsave(&mac->lock, flags);
+	int_status = le16_to_cpu(*(__le16 *)(mac->intr_buffer + 4));
+	spin_unlock_irqrestore(&mac->lock, flags);
+
 	if (int_status & INT_CFG_NEXT_BCN)
 		dev_dbg_f_limit(zd_mac_dev(mac), "INT_CFG_NEXT_BCN\n");
 	else
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index c32a247..9493ab8 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -377,8 +377,10 @@ static inline void handle_regs_int(struct urb *urb)
 	int_num = le16_to_cpu(*(__le16 *)(urb->transfer_buffer+2));
 	if (int_num == CR_INTERRUPT) {
 		struct zd_mac *mac = zd_hw_mac(zd_usb_to_hw(urb->context));
+		spin_lock(&mac->lock);
 		memcpy(&mac->intr_buffer, urb->transfer_buffer,
 				USB_MAX_EP_INT_BUFFER);
+		spin_unlock(&mac->lock);
 		schedule_work(&mac->process_intr);
 	} else if (intr->read_regs_enabled) {
 		intr->read_regs.length = len = urb->actual_length;


^ permalink raw reply related

* [PATCH 04/22] zd1211rw: fix beacon interval setup
From: Jussi Kivilinna @ 2011-01-31 18:47 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz
In-Reply-To: <20110131184657.10044.98610.stgit@fate.lan>

Vendor driver uses CR_BNC_INTERVAL at various places, one is HW_EnableBeacon()
that combinies beacon interval with BSS-type flag and DTIM value in upper 16bit
of u32. The other one is HW_UpdateBcnInterval() that set_aw_pt_bi()
appears to be based on. HW_UpdateBcnInterval() takes interval argument as u16
and uses that for calculations, set_aw_pt_bi() uses u32 value that has flags
and dtim in upper part. This clearly seems wrong. Also HW_UpdateBcnInterval()
updates only lower 16bit part of CR_BNC_INTERVAL. So make set_aw_pt_bi() do
calculations on only lower u16 part of s->beacon_interval.

Also set 32bit beacon interval register before reading values from device,
as HW_EnableBeacon() on vendor driver does. This is required to make beacon
work on AP-mode, simply reading and then writing updated values is not enough
at least with zd1211b.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_chip.c |   16 ++++++++++------
 1 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index b644ced..447f236 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -849,11 +849,12 @@ static int get_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
 static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
 {
 	struct zd_ioreq32 reqs[3];
+	u16 b_interval = s->beacon_interval & 0xffff;
 
-	if (s->beacon_interval <= 5)
-		s->beacon_interval = 5;
-	if (s->pre_tbtt < 4 || s->pre_tbtt >= s->beacon_interval)
-		s->pre_tbtt = s->beacon_interval - 1;
+	if (b_interval <= 5)
+		b_interval = 5;
+	if (s->pre_tbtt < 4 || s->pre_tbtt >= b_interval)
+		s->pre_tbtt = b_interval - 1;
 	if (s->atim_wnd_period >= s->pre_tbtt)
 		s->atim_wnd_period = s->pre_tbtt - 1;
 
@@ -862,7 +863,7 @@ static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
 	reqs[1].addr = CR_PRE_TBTT;
 	reqs[1].value = s->pre_tbtt;
 	reqs[2].addr = CR_BCN_INTERVAL;
-	reqs[2].value = s->beacon_interval;
+	reqs[2].value = (s->beacon_interval & ~0xffff) | b_interval;
 
 	return zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs));
 }
@@ -874,10 +875,13 @@ static int set_beacon_interval(struct zd_chip *chip, u32 interval)
 	struct aw_pt_bi s;
 
 	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+
+	r = zd_iowrite32_locked(chip, interval, CR_BCN_INTERVAL);
+	if (r)
+		return r;
 	r = get_aw_pt_bi(chip, &s);
 	if (r)
 		return r;
-	s.beacon_interval = interval;
 	return set_aw_pt_bi(chip, &s);
 }
 


^ permalink raw reply related

* [PATCH 05/22] zd1211rw: move set_multicast_hash and set_rx_filter from workers to configure_filter
From: Jussi Kivilinna @ 2011-01-31 18:47 UTC (permalink / raw)
  To: linux-wireless
  Cc: Daniel Drake, Jussi Kivilinna, John W. Linville, Ulrich Kunitz
In-Reply-To: <20110131184657.10044.98610.stgit@fate.lan>

Workers not needed anymore since configure_filter may sleep. Keep
mac->multicast_hash for later use (hw reset).

Signed-off-by: Jussi Kivilinna <jussi.kivilina@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_mac.c |   38 ++++++--------------------------
 drivers/net/wireless/zd1211rw/zd_mac.h |    2 --
 2 files changed, 7 insertions(+), 33 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 8b3d779..75d9a13 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -927,31 +927,6 @@ static void zd_process_intr(struct work_struct *work)
 }
 
 
-static void set_multicast_hash_handler(struct work_struct *work)
-{
-	struct zd_mac *mac =
-		container_of(work, struct zd_mac, set_multicast_hash_work);
-	struct zd_mc_hash hash;
-
-	spin_lock_irq(&mac->lock);
-	hash = mac->multicast_hash;
-	spin_unlock_irq(&mac->lock);
-
-	zd_chip_set_multicast_hash(&mac->chip, &hash);
-}
-
-static void set_rx_filter_handler(struct work_struct *work)
-{
-	struct zd_mac *mac =
-		container_of(work, struct zd_mac, set_rx_filter_work);
-	int r;
-
-	dev_dbg_f(zd_mac_dev(mac), "\n");
-	r = set_rx_filter(mac);
-	if (r)
-		dev_err(zd_mac_dev(mac), "set_rx_filter_handler error %d\n", r);
-}
-
 static u64 zd_op_prepare_multicast(struct ieee80211_hw *hw,
 				   struct netdev_hw_addr_list *mc_list)
 {
@@ -983,6 +958,7 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw,
 	};
 	struct zd_mac *mac = zd_hw_mac(hw);
 	unsigned long flags;
+	int r;
 
 	/* Only deal with supported flags */
 	changed_flags &= SUPPORTED_FIF_FLAGS;
@@ -1004,11 +980,13 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw,
 	mac->multicast_hash = hash;
 	spin_unlock_irqrestore(&mac->lock, flags);
 
-	/* XXX: these can be called here now, can sleep now! */
-	queue_work(zd_workqueue, &mac->set_multicast_hash_work);
+	zd_chip_set_multicast_hash(&mac->chip, &hash);
 
-	if (changed_flags & FIF_CONTROL)
-		queue_work(zd_workqueue, &mac->set_rx_filter_work);
+	if (changed_flags & FIF_CONTROL) {
+		r = set_rx_filter(mac);
+		if (r)
+			dev_err(zd_mac_dev(mac), "set_rx_filter error %d\n", r);
+	}
 
 	/* no handling required for FIF_OTHER_BSS as we don't currently
 	 * do BSSID filtering */
@@ -1164,9 +1142,7 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
 
 	zd_chip_init(&mac->chip, hw, intf);
 	housekeeping_init(mac);
-	INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler);
 	INIT_WORK(&mac->set_rts_cts_work, set_rts_cts_work);
-	INIT_WORK(&mac->set_rx_filter_work, set_rx_filter_handler);
 	INIT_WORK(&mac->process_intr, zd_process_intr);
 
 	SET_IEEE80211_DEV(hw, &intf->dev);
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index a6d86b9..f28ecb9 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -173,9 +173,7 @@ struct zd_mac {
 	spinlock_t intr_lock;
 	struct ieee80211_hw *hw;
 	struct housekeeping housekeeping;
-	struct work_struct set_multicast_hash_work;
 	struct work_struct set_rts_cts_work;
-	struct work_struct set_rx_filter_work;
 	struct work_struct process_intr;
 	struct zd_mc_hash multicast_hash;
 	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];


^ permalink raw reply related

* [PATCH 06/22] zd1211rw: move set_rts_cts_work to bss_info_changed
From: Jussi Kivilinna @ 2011-01-31 18:47 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz
In-Reply-To: <20110131184657.10044.98610.stgit@fate.lan>

As bss_info_changed may sleep, we can as well set RTS_CTS register right away.
Keep mac->short_preamble for later use (hw reset).

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_mac.c |   27 +++++----------------------
 drivers/net/wireless/zd1211rw/zd_mac.h |    3 ---
 2 files changed, 5 insertions(+), 25 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 75d9a13..487ed33 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -998,20 +998,9 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw,
 	 * time. */
 }
 
-static void set_rts_cts_work(struct work_struct *work)
+static void set_rts_cts(struct zd_mac *mac, unsigned int short_preamble)
 {
-	struct zd_mac *mac =
-		container_of(work, struct zd_mac, set_rts_cts_work);
-	unsigned long flags;
-	unsigned int short_preamble;
-
 	mutex_lock(&mac->chip.mutex);
-
-	spin_lock_irqsave(&mac->lock, flags);
-	mac->updating_rts_rate = 0;
-	short_preamble = mac->short_preamble;
-	spin_unlock_irqrestore(&mac->lock, flags);
-
 	zd_chip_set_rts_cts_rate_locked(&mac->chip, short_preamble);
 	mutex_unlock(&mac->chip.mutex);
 }
@@ -1022,7 +1011,6 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw,
 				   u32 changes)
 {
 	struct zd_mac *mac = zd_hw_mac(hw);
-	unsigned long flags;
 	int associated;
 
 	dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes);
@@ -1060,15 +1048,11 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw,
 	/* TODO: do hardware bssid filtering */
 
 	if (changes & BSS_CHANGED_ERP_PREAMBLE) {
-		spin_lock_irqsave(&mac->lock, flags);
+		spin_lock_irq(&mac->lock);
 		mac->short_preamble = bss_conf->use_short_preamble;
-		if (!mac->updating_rts_rate) {
-			mac->updating_rts_rate = 1;
-			/* FIXME: should disable TX here, until work has
-			 * completed and RTS_CTS reg is updated */
-			queue_work(zd_workqueue, &mac->set_rts_cts_work);
-		}
-		spin_unlock_irqrestore(&mac->lock, flags);
+		spin_unlock_irq(&mac->lock);
+
+		set_rts_cts(mac, bss_conf->use_short_preamble);
 	}
 }
 
@@ -1142,7 +1126,6 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
 
 	zd_chip_init(&mac->chip, hw, intf);
 	housekeeping_init(mac);
-	INIT_WORK(&mac->set_rts_cts_work, set_rts_cts_work);
 	INIT_WORK(&mac->process_intr, zd_process_intr);
 
 	SET_IEEE80211_DEV(hw, &intf->dev);
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index f28ecb9..ff7ef30 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -189,9 +189,6 @@ struct zd_mac {
 	/* Short preamble (used for RTS/CTS) */
 	unsigned int short_preamble:1;
 
-	/* flags to indicate update in progress */
-	unsigned int updating_rts_rate:1;
-
 	/* whether to pass frames with CRC errors to stack */
 	unsigned int pass_failed_fcs:1;
 


^ permalink raw reply related

* [PATCH 07/22] zd1211rw: support setting BSSID for AP mode
From: Jussi Kivilinna @ 2011-01-31 18:48 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz
In-Reply-To: <20110131184657.10044.98610.stgit@fate.lan>

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_chip.c |   39 +++++++++++++++++++++++--------
 drivers/net/wireless/zd1211rw/zd_chip.h |    1 +
 drivers/net/wireless/zd1211rw/zd_mac.c  |   25 +++++++++++++++++++-
 drivers/net/wireless/zd1211rw/zd_mac.h  |    1 +
 4 files changed, 55 insertions(+), 11 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 447f236..71d3cde 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -370,16 +370,12 @@ error:
 	return r;
 }
 
-/* MAC address: if custom mac addresses are to be used CR_MAC_ADDR_P1 and
- *              CR_MAC_ADDR_P2 must be overwritten
- */
-int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
+static int zd_write_mac_addr_common(struct zd_chip *chip, const u8 *mac_addr,
+				    const struct zd_ioreq32 *in_reqs,
+				    const char *type)
 {
 	int r;
-	struct zd_ioreq32 reqs[2] = {
-		[0] = { .addr = CR_MAC_ADDR_P1 },
-		[1] = { .addr = CR_MAC_ADDR_P2 },
-	};
+	struct zd_ioreq32 reqs[2] = {in_reqs[0], in_reqs[1]};
 
 	if (mac_addr) {
 		reqs[0].value = (mac_addr[3] << 24)
@@ -388,9 +384,9 @@ int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
 			      |  mac_addr[0];
 		reqs[1].value = (mac_addr[5] <<  8)
 			      |  mac_addr[4];
-		dev_dbg_f(zd_chip_dev(chip), "mac addr %pM\n", mac_addr);
+		dev_dbg_f(zd_chip_dev(chip), "%s addr %pM\n", type, mac_addr);
 	} else {
-		dev_dbg_f(zd_chip_dev(chip), "set NULL mac\n");
+		dev_dbg_f(zd_chip_dev(chip), "set NULL %s\n", type);
 	}
 
 	mutex_lock(&chip->mutex);
@@ -399,6 +395,29 @@ int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
 	return r;
 }
 
+/* MAC address: if custom mac addresses are to be used CR_MAC_ADDR_P1 and
+ *              CR_MAC_ADDR_P2 must be overwritten
+ */
+int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
+{
+	static const struct zd_ioreq32 reqs[2] = {
+		[0] = { .addr = CR_MAC_ADDR_P1 },
+		[1] = { .addr = CR_MAC_ADDR_P2 },
+	};
+
+	return zd_write_mac_addr_common(chip, mac_addr, reqs, "mac");
+}
+
+int zd_write_bssid(struct zd_chip *chip, const u8 *bssid)
+{
+	static const struct zd_ioreq32 reqs[2] = {
+		[0] = { .addr = CR_BSSID_P1 },
+		[1] = { .addr = CR_BSSID_P2 },
+	};
+
+	return zd_write_mac_addr_common(chip, bssid, reqs, "bssid");
+}
+
 int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain)
 {
 	int r;
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h
index f8bbf7d..7b0c58c 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.h
+++ b/drivers/net/wireless/zd1211rw/zd_chip.h
@@ -881,6 +881,7 @@ static inline u8 _zd_chip_get_channel(struct zd_chip *chip)
 u8  zd_chip_get_channel(struct zd_chip *chip);
 int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain);
 int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr);
+int zd_write_bssid(struct zd_chip *chip, const u8 *bssid);
 int zd_chip_switch_radio_on(struct zd_chip *chip);
 int zd_chip_switch_radio_off(struct zd_chip *chip);
 int zd_chip_enable_int(struct zd_chip *chip);
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 487ed33..ab0d1b9 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -231,6 +231,26 @@ static int set_rx_filter(struct zd_mac *mac)
 	return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter);
 }
 
+static int set_mac_and_bssid(struct zd_mac *mac)
+{
+	int r;
+
+	if (!mac->vif)
+		return -1;
+
+	r = zd_write_mac_addr(&mac->chip, mac->vif->addr);
+	if (r)
+		return r;
+
+	/* Vendor driver after setting MAC either sets BSSID for AP or
+	 * filter for other modes.
+	 */
+	if (mac->type != NL80211_IFTYPE_AP)
+		return set_rx_filter(mac);
+	else
+		return zd_write_bssid(&mac->chip, mac->vif->addr);
+}
+
 static int set_mc_hash(struct zd_mac *mac)
 {
 	struct zd_mc_hash hash;
@@ -888,7 +908,9 @@ static int zd_op_add_interface(struct ieee80211_hw *hw,
 		return -EOPNOTSUPP;
 	}
 
-	return zd_write_mac_addr(&mac->chip, vif->addr);
+	mac->vif = vif;
+
+	return set_mac_and_bssid(mac);
 }
 
 static void zd_op_remove_interface(struct ieee80211_hw *hw,
@@ -896,6 +918,7 @@ static void zd_op_remove_interface(struct ieee80211_hw *hw,
 {
 	struct zd_mac *mac = zd_hw_mac(hw);
 	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
 	zd_set_beacon_interval(&mac->chip, 0);
 	zd_write_mac_addr(&mac->chip, NULL);
 }
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index ff7ef30..0ec6bde 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -172,6 +172,7 @@ struct zd_mac {
 	spinlock_t lock;
 	spinlock_t intr_lock;
 	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
 	struct housekeeping housekeeping;
 	struct work_struct set_rts_cts_work;
 	struct work_struct process_intr;


^ permalink raw reply related

* [PATCH 08/22] zd1211rw: fix ack_pending in filter_ack causing tx-packet ordering problem on monitor
From: Jussi Kivilinna @ 2011-01-31 18:48 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz
In-Reply-To: <20110131184657.10044.98610.stgit@fate.lan>

For reasons not very clear yet to me, filter_ack leaves matching tx-packet
pending with 'ack_pending'. This causes tx-packet to be passed back to upper
layer after next packet has been transfered and tx-packets might end up
coming come out of monitor interface in wrong order vs. rx.

Because of this when enable AP-mode, hostapd monitor interface would get
packets in wrong order causing problems in WPA association.

So don't use mac->ack_pending when in AP-mode.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_mac.c |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index ab0d1b9..84ac95e 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -799,6 +799,13 @@ static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
 
 		mac->ack_pending = 1;
 		mac->ack_signal = stats->signal;
+
+		/* Prevent pending tx-packet on AP-mode */
+		if (mac->type == NL80211_IFTYPE_AP) {
+			skb = __skb_dequeue(q);
+			zd_mac_tx_status(hw, skb, mac->ack_signal, NULL);
+			mac->ack_pending = 0;
+		}
 	}
 
 	spin_unlock_irqrestore(&q->lock, flags);


^ permalink raw reply related

* [PATCH 09/22] zd1211rw: let zd_set_beacon_interval() set dtim_period and add AP-beacon flag
From: Jussi Kivilinna @ 2011-01-31 18:48 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz
In-Reply-To: <20110131184657.10044.98610.stgit@fate.lan>

Add support for AP-mode beacon. Also disable beacon when interface is set
down as otherwise hw will keep flooding NEXT_BCN interrupts.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_chip.c |   33 ++++++++++++++++++++++++++-----
 drivers/net/wireless/zd1211rw/zd_chip.h |    4 +++-
 drivers/net/wireless/zd1211rw/zd_mac.c  |   17 ++++++++--------
 3 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 71d3cde..d8dc927 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -888,14 +888,36 @@ static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
 }
 
 
-static int set_beacon_interval(struct zd_chip *chip, u32 interval)
+static int set_beacon_interval(struct zd_chip *chip, u16 interval,
+			       u8 dtim_period, int type)
 {
 	int r;
 	struct aw_pt_bi s;
+	u32 b_interval, mode_flag;
 
 	ZD_ASSERT(mutex_is_locked(&chip->mutex));
 
-	r = zd_iowrite32_locked(chip, interval, CR_BCN_INTERVAL);
+	if (interval > 0) {
+		switch (type) {
+		case NL80211_IFTYPE_ADHOC:
+		case NL80211_IFTYPE_MESH_POINT:
+			mode_flag = BCN_MODE_IBSS;
+			break;
+		case NL80211_IFTYPE_AP:
+			mode_flag = BCN_MODE_AP;
+			break;
+		default:
+			mode_flag = 0;
+			break;
+		}
+	} else {
+		dtim_period = 0;
+		mode_flag = 0;
+	}
+
+	b_interval = mode_flag | (dtim_period << 16) | interval;
+
+	r = zd_iowrite32_locked(chip, b_interval, CR_BCN_INTERVAL);
 	if (r)
 		return r;
 	r = get_aw_pt_bi(chip, &s);
@@ -904,12 +926,13 @@ static int set_beacon_interval(struct zd_chip *chip, u32 interval)
 	return set_aw_pt_bi(chip, &s);
 }
 
-int zd_set_beacon_interval(struct zd_chip *chip, u32 interval)
+int zd_set_beacon_interval(struct zd_chip *chip, u16 interval, u8 dtim_period,
+			   int type)
 {
 	int r;
 
 	mutex_lock(&chip->mutex);
-	r = set_beacon_interval(chip, interval);
+	r = set_beacon_interval(chip, interval, dtim_period, type);
 	mutex_unlock(&chip->mutex);
 	return r;
 }
@@ -928,7 +951,7 @@ static int hw_init(struct zd_chip *chip)
 	if (r)
 		return r;
 
-	return set_beacon_interval(chip, 100);
+	return set_beacon_interval(chip, 100, 0, NL80211_IFTYPE_UNSPECIFIED);
 }
 
 static zd_addr_t fw_reg_addr(struct zd_chip *chip, u16 offset)
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h
index 7b0c58c..14e4402 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.h
+++ b/drivers/net/wireless/zd1211rw/zd_chip.h
@@ -546,6 +546,7 @@ enum {
 #define RX_FILTER_CTRL (RX_FILTER_RTS | RX_FILTER_CTS | \
 	RX_FILTER_CFEND | RX_FILTER_CFACK)
 
+#define BCN_MODE_AP			0x1000000
 #define BCN_MODE_IBSS			0x2000000
 
 /* Monitor mode sets filter to 0xfffff */
@@ -921,7 +922,8 @@ enum led_status {
 
 int zd_chip_control_leds(struct zd_chip *chip, enum led_status status);
 
-int zd_set_beacon_interval(struct zd_chip *chip, u32 interval);
+int zd_set_beacon_interval(struct zd_chip *chip, u16 interval, u8 dtim_period,
+			   int type);
 
 static inline int zd_get_beacon_interval(struct zd_chip *chip, u32 *interval)
 {
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 84ac95e..1bd275b 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -926,7 +926,7 @@ static void zd_op_remove_interface(struct ieee80211_hw *hw,
 	struct zd_mac *mac = zd_hw_mac(hw);
 	mac->type = NL80211_IFTYPE_UNSPECIFIED;
 	mac->vif = NULL;
-	zd_set_beacon_interval(&mac->chip, 0);
+	zd_set_beacon_interval(&mac->chip, 0, 0, NL80211_IFTYPE_UNSPECIFIED);
 	zd_write_mac_addr(&mac->chip, NULL);
 }
 
@@ -1058,15 +1058,16 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw,
 		}
 
 		if (changes & BSS_CHANGED_BEACON_ENABLED) {
-			u32 interval;
+			u16 interval = 0;
+			u8 period = 0;
 
-			if (bss_conf->enable_beacon)
-				interval = BCN_MODE_IBSS |
-						bss_conf->beacon_int;
-			else
-				interval = 0;
+			if (bss_conf->enable_beacon) {
+				period = bss_conf->dtim_period;
+				interval = bss_conf->beacon_int;
+			}
 
-			zd_set_beacon_interval(&mac->chip, interval);
+			zd_set_beacon_interval(&mac->chip, interval, period,
+					       mac->type);
 		}
 	} else
 		associated = is_valid_ether_addr(bss_conf->bssid);


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox