Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH 3/3] mac80211: move flag IEEE80211_TX_CTL_ASSIGN_SEQ from flag to flags2
From: Thomas Huehn @ 2013-06-07 18:13 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, nbd, johannes, bvahl
In-Reply-To: <1370628803-28785-1-git-send-email-thomas@net.t-labs.tu-berlin.de>

Flag IEEE80211_TX_CTL_ASSIGN_SEQ at bit(1) in mac80211_tx_control_flags
is moved to ieee80211_tx_info->control.flags2 as it is only used in the
tx-path. This frees one bit in ieee80211_tx_info->flags.

Signed-off-by: Thomas Huehn <thomas@net.t-labs.tu-berlin.de>
---
 drivers/net/wireless/ath/ath9k/beacon.c         |    2 +-
 drivers/net/wireless/ath/ath9k/htc_drv_beacon.c |    2 +-
 drivers/net/wireless/ath/ath9k/xmit.c           |    2 +-
 drivers/net/wireless/ath/carl9170/tx.c          |    2 +-
 drivers/net/wireless/b43/xmit.c                 |    2 +-
 drivers/net/wireless/b43legacy/xmit.c           |    2 +-
 drivers/net/wireless/brcm80211/brcmsmac/main.c  |    2 +-
 drivers/net/wireless/iwlwifi/dvm/tx.c           |    2 +-
 drivers/net/wireless/iwlwifi/mvm/tx.c           |    2 +-
 drivers/net/wireless/mwl8k.c                    |    2 +-
 drivers/net/wireless/p54/txrx.c                 |    2 +-
 drivers/net/wireless/rt2x00/rt2x00queue.c       |    2 +-
 drivers/net/wireless/rtl818x/rtl8180/dev.c      |    2 +-
 drivers/net/wireless/rtl818x/rtl8187/dev.c      |    2 +-
 include/net/mac80211.h                          |   25 ++++++++++++-----------
 net/mac80211/tx.c                               |    4 ++--
 16 files changed, 29 insertions(+), 28 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index fd1eeba..b768e3b 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -161,7 +161,7 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
 	mgmt_hdr->u.beacon.timestamp = avp->tsf_adjust;
 
 	info = IEEE80211_SKB_CB(skb);
-	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+	if (info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ) {
 		/*
 		 * TODO: make sure the seq# gets assigned properly (vs. other
 		 * TX frames)
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
index e0c03bd..7dc56e2 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
@@ -386,7 +386,7 @@ static void ath9k_htc_send_beacon(struct ath9k_htc_priv *priv,
 	mgmt->u.beacon.timestamp = avp->tsfadjust;
 
 	info = IEEE80211_SKB_CB(beacon);
-	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+	if (info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ) {
 		struct ieee80211_hdr *hdr =
 			(struct ieee80211_hdr *) beacon->data;
 		avp->seq_no += 0x10;
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index eab0fcb..15fdfa1 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1929,7 +1929,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 	 * to be cleaned up to work better with Beacon transmission and virtual
 	 * BSSes.
 	 */
-	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+	if (info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ) {
 		if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
 			sc->tx.seq_no += 0x10;
 		hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c
index 6ee8c26..23b02aa 100644
--- a/drivers/net/wireless/ath/carl9170/tx.c
+++ b/drivers/net/wireless/ath/carl9170/tx.c
@@ -921,7 +921,7 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
 	if (unlikely(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM))
 		txc->s.misc |= CARL9170_TX_SUPER_MISC_CAB;
 
-	if (unlikely(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
+	if (unlikely(info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ))
 		txc->s.misc |= CARL9170_TX_SUPER_MISC_ASSIGN_SEQ;
 
 	if (unlikely(ieee80211_is_probe_resp(hdr->frame_control)))
diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c
index 8cb206a..0771593 100644
--- a/drivers/net/wireless/b43/xmit.c
+++ b/drivers/net/wireless/b43/xmit.c
@@ -404,7 +404,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
 	if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
 		mac_ctl |= B43_TXH_MAC_ACK;
 	/* use hardware sequence counter as the non-TID counter */
-	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
+	if (info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ)
 		mac_ctl |= B43_TXH_MAC_HWSEQ;
 	if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
 		mac_ctl |= B43_TXH_MAC_STMSDU;
diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c
index 849a28c..6920d17 100644
--- a/drivers/net/wireless/b43legacy/xmit.c
+++ b/drivers/net/wireless/b43legacy/xmit.c
@@ -283,7 +283,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev,
 	rates = info->control.rates;
 	if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
 		mac_ctl |= B43legacy_TX4_MAC_ACK;
-	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
+	if (info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ)
 		mac_ctl |= B43legacy_TX4_MAC_HWSEQ;
 	if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
 		mac_ctl |= B43legacy_TX4_MAC_STMSDU;
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c
index 28e7aee..ed97e69 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/main.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c
@@ -6327,7 +6327,7 @@ brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw,
 	memset(txh, 0, D11_TXH_LEN);
 
 	/* setup frameid */
-	if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+	if (tx_info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ) {
 		/* non-AP STA should never use BCMC queue */
 		if (queue == TX_BCMC_FIFO) {
 			brcms_err(wlc->hw->d11core,
diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c
index 353a053..b659254 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/dvm/tx.c
@@ -101,7 +101,7 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
 		tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
 	} else {
 		tx_cmd->tid_tspec = IWL_TID_NON_QOS;
-		if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
+		if (info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ)
 			tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
 		else
 			tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
index f212f16..e5f4cbc 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tx.c
@@ -107,7 +107,7 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
 		tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
 	} else {
 		tx_cmd->tid_tspec = IWL_TID_NON_QOS;
-		if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
+		if (info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ)
 			tx_flags |= TX_CMD_FLG_SEQ_CTL;
 		else
 			tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index a3707fd..46c20fd 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -1921,7 +1921,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw,
 	tx_info = IEEE80211_SKB_CB(skb);
 	mwl8k_vif = MWL8K_VIF(tx_info->control.vif);
 
-	if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+	if (tx_info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ) {
 		wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
 		wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno);
 		mwl8k_vif->seqno += 0x10;
diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c
index f95de0d..6053a48 100644
--- a/drivers/net/wireless/p54/txrx.c
+++ b/drivers/net/wireless/p54/txrx.c
@@ -692,7 +692,7 @@ static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb,
 	else
 		*burst_possible = false;
 
-	if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
+	if (!(info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ))
 		*flags |= P54_HDR_FLAG_DATA_OUT_SEQNR;
 
 	if (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 2c12311..87e6dba 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -221,7 +221,7 @@ static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev,
 	struct rt2x00_intf *intf = vif_to_intf(tx_info->control.vif);
 	u16 seqno;
 
-	if (!(tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
+	if (!(tx_info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ))
 		return;
 
 	__set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c
index 91a04e2..fc47402 100644
--- a/drivers/net/wireless/rtl818x/rtl8180/dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c
@@ -302,7 +302,7 @@ static void rtl8180_tx(struct ieee80211_hw *dev,
 
 	spin_lock_irqsave(&priv->lock, flags);
 
-	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+	if (info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ) {
 		if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
 			priv->seqno += 0x10;
 		hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c
index f49220e..dcaec73 100644
--- a/drivers/net/wireless/rtl818x/rtl8187/dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c
@@ -264,7 +264,7 @@ static void rtl8187_tx(struct ieee80211_hw *dev,
 		flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
 	}
 
-	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+	if (info->control.flags2 & IEEE80211_TX_CTL_ASSIGN_SEQ) {
 		if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
 			priv->seqno += 0x10;
 		tx_hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 6c31a92..c4816ab 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -374,17 +374,6 @@ struct ieee80211_bss_conf {
  *
  * These flags are used with the @flags member of &ieee80211_tx_info.
  *
- * @IEEE80211_TX_CTL_ASSIGN_SEQ: The driver has to assign a sequence
- *	number to this frame, taking care of not overwriting the fragment
- *	number and increasing the sequence number only when the
- *	IEEE80211_TX_CTL_FIRST_FRAGMENT flag is set. mac80211 will properly
- *	assign sequence numbers to QoS-data frames but cannot do so correctly
- *	for non-QoS-data and management frames because beacons need them from
- *	that counter as well and mac80211 cannot guarantee proper sequencing.
- *	If this flag is set, the driver should instruct the hardware to
- *	assign a sequence number to the frame or assign one itself. Cf. IEEE
- *	802.11-2007 7.1.3.4.1 paragraph 3. This flag will always be set for
- *	beacons and always be clear for frames without a sequence number field.
  * @IEEE80211_TX_CTL_NO_ACK: tell the low level not to wait for an ack
  * @IEEE80211_TX_CTL_CLEAR_PS_FILT: clear powersave filter for destination
  *	station
@@ -469,8 +458,8 @@ enum mac80211_tx_control_flags {
 	/*
 	 * moved to flags2:
 	 * IEEE80211_TX_CTL_REQ_TX_STATUS		= BIT(0),
+	 * IEEE80211_TX_CTL_ASSIGN_SEQ		= BIT(1),
 	 */
-	IEEE80211_TX_CTL_ASSIGN_SEQ		= BIT(1),
 	IEEE80211_TX_CTL_NO_ACK			= BIT(2),
 	IEEE80211_TX_CTL_CLEAR_PS_FILT		= BIT(3),
 	IEEE80211_TX_CTL_FIRST_FRAGMENT		= BIT(4),
@@ -509,9 +498,21 @@ enum mac80211_tx_control_flags {
  * and are only available within the tx path where tx_info->control is valid.
  *
  * @IEEE80211_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame.
+ * @IEEE80211_TX_CTL_ASSIGN_SEQ: The driver has to assign a sequence
+ *	number to this frame, taking care of not overwriting the fragment
+ *	number and increasing the sequence number only when the
+ *	IEEE80211_TX_CTL_FIRST_FRAGMENT flag is set. mac80211 will properly
+ *	assign sequence numbers to QoS-data frames but cannot do so correctly
+ *	for non-QoS-data and management frames because beacons need them from
+ *	that counter as well and mac80211 cannot guarantee proper sequencing.
+ *	If this flag is set, the driver should instruct the hardware to
+ *	assign a sequence number to the frame or assign one itself. Cf. IEEE
+ *	802.11-2007 7.1.3.4.1 paragraph 3. This flag will always be set for
+ *	beacons and always be clear for frames without a sequence number field.
  */
 enum mac80211_tx_control_flags2 {
 	IEEE80211_TX_CTL_REQ_TX_STATUS		= BIT(0),
+	IEEE80211_TX_CTL_ASSIGN_SEQ		= BIT(1),
 };
 
 #define IEEE80211_TX_CTL_STBC_SHIFT		23
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index b1fe9b5..c3c58f0 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -768,7 +768,7 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
 	 */
 	if (!ieee80211_is_data_qos(hdr->frame_control)) {
 		/* driver should assign sequence number */
-		info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
+		info->control.flags2 |= IEEE80211_TX_CTL_ASSIGN_SEQ;
 		/* for pure STA mode without beacons, we can do it */
 		hdr->seq_ctrl = cpu_to_le16(tx->sdata->sequence_number);
 		tx->sdata->sequence_number += 0x10;
@@ -2447,8 +2447,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
 	info->control.vif = vif;
 
 	info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT |
-			IEEE80211_TX_CTL_ASSIGN_SEQ |
 			IEEE80211_TX_CTL_FIRST_FRAGMENT;
+	info->control.flags2 |= IEEE80211_TX_CTL_ASSIGN_SEQ;
  out:
 	rcu_read_unlock();
 	return skb;
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 1/3] mac80211: restructure tx_control_flag handling in ieee80211_tx_info
From: Thomas Huehn @ 2013-06-07 18:13 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, nbd, johannes, bvahl
In-Reply-To: <1370628803-28785-1-git-send-email-thomas@net.t-labs.tu-berlin.de>

All usable  bits in enum mac80211_tx_control_flags are depleted. In
order to allow aditional control flag assigments, we introduce a
new u16 variable ieee8011_tx_info->control.flags2 with its
corresponding enum mac80211_tx_control_flags2. Such tx control flags
within ieee80211_tx_info->flags that are only used in the tx path
while ieee80211_tx_info->control is valid can be moved to
ieee80211_tx_control->control.flags2.

Signed-off-by: Thomas Huehn <thomas@net.t-labs.tu-berlin.de>
---
 include/net/mac80211.h |   12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index a405a7a..2d8e461 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -500,6 +500,15 @@ enum mac80211_tx_control_flags {
 	IEEE80211_TX_CTL_PS_RESPONSE		= BIT(31),
 };
 
+/**
+ * enum mac80211_tx_control_flags2 - control related flags to describe transmission information/status
+ *
+ * These flags are used with the @flags2 member of &ieee80211_tx_info inside control
+ * and are only available within the tx path where tx_info->control is valid.
+ */
+enum mac80211_tx_control_flags2 {
+};
+
 #define IEEE80211_TX_CTL_STBC_SHIFT		23
 
 /*
@@ -675,7 +684,8 @@ struct ieee80211_tx_info {
 			/* NB: vif can be NULL for injected frames */
 			struct ieee80211_vif *vif;
 			struct ieee80211_key_conf *hw_key;
-			/* 8 bytes free */
+			u16 flags2;
+			/* 6 bytes free */
 		} control;
 		struct {
 			struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 0/3] mac80211: restructure tx control flag handling
From: Thomas Huehn @ 2013-06-07 18:13 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, nbd, johannes, bvahl

Currently all possible bits to assign tx control flags via
ieee80211_tx_info->flags are depleted. This patch series frees 2 bits
in ieee80211_tx_info->flags by shifting possible tx control flags
into a new variable flags2.

Greetings Thomas

[PATCH 1/3] mac80211: restructure tx_control_flag handling in
[PATCH 2/3] mac80211: move flag IEEE80211_TX_CTL_REQ_TX_STATUS from
[PATCH 3/3] mac80211: move flag IEEE80211_TX_CTL_ASSIGN_SEQ from

^ permalink raw reply

* Re: [PATCH 0/5] add master channel switch announcement support
From: Felix Fietkau @ 2013-06-07 17:33 UTC (permalink / raw)
  To: Simon Wunderlich
  Cc: linux-wireless, Johannes Berg, Mathias Kretschmer,
	Simon Wunderlich
In-Reply-To: <1370624745-2267-1-git-send-email-siwu@hrz.tu-chemnitz.de>

On 2013-06-07 7:05 PM, Simon Wunderlich wrote:
> This is the follow up from the RFC posted a month ago. This patchset adds generic
> channel switch support for AP. This is required for DFS operation
> (e.g. Wi-Fi Alliance requires this for 802.11h certification). This will also
> be required for IBSS-DFS later.
> 
> The rough design is:
>  * userspace asks kernel to switch a channel using the new NL80211_CMD_CHANNEL_SWITCH
>    command. It supplies IE information for the time while staying on the old channel and
>    announcing the switch, and IE information for after the switch to the new channel. 
>  * IE information contains the beacon and optionally probe responses, which should
>    include (E)CSA IEs for the CSA case. Furthermore an offset is provided (for beacon
>    and probe response) to point to the counter field within the channel switch IEs.
>  * The driver gets the new beacons passed and must set them, and decrement the
>    counter field. When it reaches 0, the channel is changed and userspace notified.
> 
> Discussion points:
>  * Assembling all these IE information is a little bit tedious but doable (I've
>    patched hostapd).
>  * Other future users like IBSS/MESH will not get the beacon/probe response IEs, as they
>    generate these beacons themselves. Therefore they need the COUNT attribute, which
>    is kind of duplicate right now.
>  * Userspace must generate/handle all IEs, which lifts the previous limitations of
>    the RFC (e.g. no change of band allowed, no operation mode change allowed).
>  * it currently works for me [TM] on my ath9k based machine
I think this is a bit racy. Just because the driver gets a beacon from
mac80211 doesn't mean that the beacon will be sent *immediately*. I'd
recommend calling back from the driver into mac80211 on beacon tx
completion. Also, it might make sense to skip CAB queue transmissions in
this case.

- Felix

^ permalink raw reply

* [PATCH 3/5] mac80211: add functions to duplicate a cfg80211_beacon
From: Simon Wunderlich @ 2013-06-07 17:05 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg, Mathias Kretschmer, Simon Wunderlich
In-Reply-To: <1370624745-2267-1-git-send-email-siwu@hrz.tu-chemnitz.de>

For the channel switch announcement, it is required to set a new beacon
after the driver arrived on the new channel. The new beacon will be set
by nl80211, but is required to duplicate the beacon as the original
memory within the netlink message will be gone. Add functions to
duplicate and free these cfg80211 beacons.

Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
---
 net/mac80211/cfg.c |   76 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 3062210..1f8e5cf 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2775,6 +2775,82 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
 	return 0;
 }
 
+static void cfg80211_beacon_free(struct cfg80211_beacon_data *beacon)
+{
+	kfree(beacon->head);
+	kfree(beacon->tail);
+	kfree(beacon->beacon_ies);
+	kfree(beacon->proberesp_ies);
+	kfree(beacon->assocresp_ies);
+	kfree(beacon->probe_resp);
+	kfree(beacon);
+}
+
+static struct cfg80211_beacon_data *
+cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
+{
+	struct cfg80211_beacon_data *new_beacon;
+	new_beacon = kzalloc(sizeof(*new_beacon), GFP_KERNEL);
+	if (!beacon)
+		return NULL;
+
+	if (beacon->head_len) {
+		new_beacon->head = kmalloc(beacon->head_len, GFP_KERNEL);
+		new_beacon->head_len = beacon->head_len;
+		if (!new_beacon->head)
+			goto error;
+		memcpy((u8 *)new_beacon->head, beacon->head, beacon->head_len);
+	}
+	if (beacon->tail_len) {
+		new_beacon->tail = kmalloc(beacon->tail_len, GFP_KERNEL);
+		new_beacon->tail_len = beacon->tail_len;
+		if (!new_beacon->tail)
+			goto error;
+		memcpy((u8 *)new_beacon->tail, beacon->tail, beacon->tail_len);
+	}
+	if (beacon->beacon_ies_len) {
+		new_beacon->beacon_ies = kmalloc(beacon->beacon_ies_len,
+						 GFP_KERNEL);
+		new_beacon->beacon_ies_len = beacon->beacon_ies_len;
+		if (!new_beacon->beacon_ies)
+			goto error;
+		memcpy((u8 *)new_beacon->beacon_ies, beacon->beacon_ies,
+		       beacon->beacon_ies_len);
+	}
+	if (beacon->proberesp_ies_len) {
+		new_beacon->proberesp_ies = kmalloc(beacon->proberesp_ies_len,
+						    GFP_KERNEL);
+		new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
+		if (!new_beacon->proberesp_ies)
+			goto error;
+		memcpy((u8 *)new_beacon->proberesp_ies, beacon->proberesp_ies,
+		       beacon->proberesp_ies_len);
+	}
+	if (beacon->assocresp_ies_len) {
+		new_beacon->assocresp_ies = kmalloc(beacon->assocresp_ies_len,
+						    GFP_KERNEL);
+		new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
+		if (!new_beacon->assocresp_ies)
+			goto error;
+		memcpy((u8 *)new_beacon->assocresp_ies, beacon->assocresp_ies,
+		       beacon->assocresp_ies_len);
+	}
+	if (beacon->probe_resp_len) {
+		new_beacon->probe_resp = kmalloc(beacon->probe_resp_len,
+						 GFP_KERNEL);
+		new_beacon->probe_resp_len = beacon->probe_resp_len;
+		if (!new_beacon->probe_resp)
+			goto error;
+		memcpy((u8 *)new_beacon->probe_resp, beacon->probe_resp,
+		       beacon->probe_resp_len);
+	}
+
+	return new_beacon;
+error:
+	cfg80211_beacon_free(new_beacon);
+	return NULL;
+}
+
 static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 			     struct ieee80211_channel *chan, bool offchan,
 			     unsigned int wait, const u8 *buf, size_t len,
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 5/5] ath9k: enable CSA functionality in ath9k
From: Simon Wunderlich @ 2013-06-07 17:05 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg, Mathias Kretschmer, Simon Wunderlich
In-Reply-To: <1370624745-2267-1-git-send-email-siwu@hrz.tu-chemnitz.de>

Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
---
 drivers/net/wireless/ath/ath9k/main.c |    6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index cc5a98b..0418cc6 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2319,6 +2319,11 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
 	clear_bit(SC_OP_SCANNING, &sc->sc_flags);
 }
 
+static void ath9k_channel_switch_beacon(struct ieee80211_vif *vif)
+{
+	vif->csa_active = 1;
+}
+
 struct ieee80211_ops ath9k_ops = {
 	.tx 		    = ath9k_tx,
 	.start 		    = ath9k_start,
@@ -2366,4 +2371,5 @@ struct ieee80211_ops ath9k_ops = {
 #endif
 	.sw_scan_start	    = ath9k_sw_scan_start,
 	.sw_scan_complete   = ath9k_sw_scan_complete,
+	.channel_switch_beacon     = ath9k_channel_switch_beacon,
 };
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 4/5] mac80211: add channel switch command and beacon callbacks
From: Simon Wunderlich @ 2013-06-07 17:05 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg, Mathias Kretschmer, Simon Wunderlich
In-Reply-To: <1370624745-2267-1-git-send-email-siwu@hrz.tu-chemnitz.de>

The count field in CSA must be decremented with each beacon
transmitted. This patch implements the functionality for drivers
using ieee80211_beacon_get(). Other drivers must call back manually
after reaching count == 0.

This patch also contains the handling and finish worker for the channel
switch command.

Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
---
Changes to RFCv1:
 * use beacons as supplied from nl80211 without generating/parsing them
 * update beacons using offsets
 * report back by calling the channel switch event in cfg80211
---
 include/net/mac80211.h     |   21 +++++++++
 net/mac80211/cfg.c         |  106 +++++++++++++++++++++++++++++++++++++++++++-
 net/mac80211/driver-ops.h  |   11 +++++
 net/mac80211/ieee80211_i.h |   11 +++++
 net/mac80211/iface.c       |    2 +
 net/mac80211/trace.h       |   20 +++++++++
 net/mac80211/tx.c          |   45 +++++++++++++++++++
 7 files changed, 214 insertions(+), 2 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index a405a7a..efaacd1 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1078,6 +1078,7 @@ enum ieee80211_vif_flags {
  * @addr: address of this interface
  * @p2p: indicates whether this AP or STA interface is a p2p
  *	interface, i.e. a GO or p2p-sta respectively
+ * @csa_active: marks whether a channel switch is going on
  * @driver_flags: flags/capabilities the driver has for this interface,
  *	these need to be set (or cleared) when the interface is added
  *	or, if supported by the driver, the interface type is changed
@@ -1100,6 +1101,7 @@ struct ieee80211_vif {
 	struct ieee80211_bss_conf bss_conf;
 	u8 addr[ETH_ALEN];
 	bool p2p;
+	bool csa_active;
 
 	u8 cab_queue;
 	u8 hw_queue[IEEE80211_NUM_ACS];
@@ -2631,6 +2633,14 @@ enum ieee80211_roc_type {
  * @ipv6_addr_change: IPv6 address assignment on the given interface changed.
  *	Currently, this is only called for managed or P2P client interfaces.
  *	This callback is optional; it must not sleep.
+ *
+ * @channel_switch_beacon: Starts a channel switch for the to a new channel.
+ *	Beacons are modified to include CSA or ECSA IEs before calling this
+ *	function. The corresponding count fields in these IEs must be
+ *	decremented, and when they reach zero the driver must call
+ *	ieee80211_finish_csa(). Drivers which use ieee80211_get_beacon()
+ *	already get this done by mac80211.
+ *
  */
 struct ieee80211_ops {
 	void (*tx)(struct ieee80211_hw *hw,
@@ -2818,6 +2828,7 @@ struct ieee80211_ops {
 				 struct ieee80211_vif *vif,
 				 struct inet6_dev *idev);
 #endif
+	void (*channel_switch_beacon)(struct ieee80211_vif *vif);
 };
 
 /**
@@ -3313,6 +3324,16 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
 }
 
 /**
+ * ieee80211_finish_csa - notify mac80211 about channel switch
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * After a channel switch announcement was scheduled and the counter in this
+ * announcement hit zero, this function must be called by the driver to
+ * notify mac80211 that the channel can be changed.
+ */
+void ieee80211_finish_csa(struct ieee80211_vif *vif);
+
+/**
  * ieee80211_proberesp_get - retrieve a Probe Response template
  * @hw: pointer obtained from ieee80211_alloc_hw().
  * @vif: &struct ieee80211_vif pointer from the add_interface callback.
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 1f8e5cf..24b39c7 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -854,8 +854,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
 	return 0;
 }
 
-static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
-				   struct cfg80211_beacon_data *params)
+int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+			    struct cfg80211_beacon_data *params)
 {
 	struct beacon_data *new, *old;
 	int new_head_len, new_tail_len;
@@ -2851,6 +2851,107 @@ error:
 	return NULL;
 }
 
+void ieee80211_csa_finalize_work(struct work_struct *work)
+{
+	struct ieee80211_sub_if_data *sdata =
+		container_of(work, struct ieee80211_sub_if_data,
+			     csa_finalize_work);
+	struct ieee80211_local *local = sdata->local;
+	int err;
+
+	if (!ieee80211_sdata_running(sdata))
+		return;
+
+	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
+		return;
+
+	netif_carrier_off(sdata->dev);
+	err = ieee80211_vif_use_channel(sdata, &local->csa_chandef,
+					IEEE80211_CHANCTX_SHARED);
+	netif_carrier_on(sdata->dev);
+	if (WARN_ON(err < 0))
+		return;
+	netif_carrier_on(sdata->dev);
+
+	err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
+	cfg80211_beacon_free(sdata->u.ap.next_beacon);
+	sdata->u.ap.next_beacon = NULL;
+
+	ieee80211_wake_queues_by_reason(&sdata->local->hw,
+					IEEE80211_MAX_QUEUE_MAP,
+					IEEE80211_QUEUE_STOP_REASON_CSA);
+
+	ieee80211_bss_info_change_notify(sdata, err);
+
+	cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
+}
+
+static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+				    struct cfg80211_csa_settings *params)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_chanctx_conf *chanctx_conf;
+	struct ieee80211_chanctx *chanctx;
+	int err;
+
+	if (!list_empty(&local->roc_list) || local->scanning)
+		return -EBUSY;
+
+	/* don't handle if chanctx is used */
+	if (local->use_chanctx)
+		return -EBUSY;
+
+	rcu_read_lock();
+	mutex_lock(&local->chanctx_mtx);
+	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+	if (!chanctx_conf) {
+		mutex_unlock(&local->chanctx_mtx);
+		rcu_read_unlock();
+		return -EBUSY;
+	}
+
+	/* don't handle for multi-VIF cases */
+	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+	if (chanctx->refcount > 1) {
+		mutex_unlock(&local->chanctx_mtx);
+		rcu_read_unlock();
+		return -EBUSY;
+	}
+	mutex_unlock(&local->chanctx_mtx);
+	rcu_read_unlock();
+
+	/* only handle AP for now. */
+	switch (sdata->vif.type) {
+	case NL80211_IFTYPE_AP:
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	sdata->csa_counter_offset_beacon = params->counter_offset_beacon;
+	sdata->csa_counter_offset_presp = params->counter_offset_presp;
+	sdata->u.ap.next_beacon = cfg80211_beacon_dup(&params->beacon_after);
+	if (!sdata->u.ap.next_beacon)
+		return -ENOMEM;
+
+	if (params->block_tx)
+		ieee80211_stop_queues_by_reason(&local->hw,
+				IEEE80211_MAX_QUEUE_MAP,
+				IEEE80211_QUEUE_STOP_REASON_CSA);
+
+	err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
+	if (err < 0)
+		return err;
+
+	local->csa_chandef = params->chandef;
+
+	ieee80211_bss_info_change_notify(sdata, err);
+	drv_channel_switch_beacon(sdata);
+
+	return 0;
+}
+
 static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 			     struct ieee80211_channel *chan, bool offchan,
 			     unsigned int wait, const u8 *buf, size_t len,
@@ -3567,4 +3668,5 @@ struct cfg80211_ops mac80211_config_ops = {
 	.get_et_strings = ieee80211_get_et_strings,
 	.get_channel = ieee80211_cfg_get_channel,
 	.start_radar_detection = ieee80211_start_radar_detection,
+	.channel_switch = ieee80211_channel_switch,
 };
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index b931c96..47a97ef 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1072,4 +1072,15 @@ static inline void drv_ipv6_addr_change(struct ieee80211_local *local,
 }
 #endif
 
+static inline void
+drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_local *local = sdata->local;
+	if (local->ops->channel_switch_beacon) {
+		trace_drv_channel_switch_beacon(sdata);
+		local->ops->channel_switch_beacon(&sdata->vif);
+	}
+}
+
+
 #endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 7a6f1a0..d34d816 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -258,6 +258,8 @@ struct ieee80211_if_ap {
 	struct beacon_data __rcu *beacon;
 	struct probe_resp __rcu *probe_resp;
 
+	/* to be used after channel switch. */
+	struct cfg80211_beacon_data *next_beacon;
 	struct list_head vlans;
 
 	struct ps_data ps;
@@ -713,6 +715,10 @@ struct ieee80211_sub_if_data {
 
 	struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
 
+	struct work_struct csa_finalize_work;
+	int csa_counter_offset_beacon;
+	int csa_counter_offset_presp;
+
 	/* used to reconfigure hardware SM PS */
 	struct work_struct recalc_smps;
 
@@ -1340,6 +1346,8 @@ void ieee80211_roc_purge(struct ieee80211_local *local,
 void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free);
 void ieee80211_sw_roc_work(struct work_struct *work);
 void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
+/* channel switch handling */
+void ieee80211_csa_finalize_work(struct work_struct *work);
 
 /* interface handling */
 int ieee80211_iface_init(void);
@@ -1362,6 +1370,9 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
 
 bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
 void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+			    struct cfg80211_beacon_data *params);
+
 
 static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
 {
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 7cabaf2..d17692d 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -800,6 +800,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 	cancel_work_sync(&local->dynamic_ps_enable_work);
 
 	cancel_work_sync(&sdata->recalc_smps);
+	cancel_work_sync(&sdata->csa_finalize_work);
 
 	cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
 
@@ -1263,6 +1264,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
 	skb_queue_head_init(&sdata->skb_queue);
 	INIT_WORK(&sdata->work, ieee80211_iface_work);
 	INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
+	INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
 
 	switch (type) {
 	case NL80211_IFTYPE_P2P_GO:
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index c215fafd7..6b2af54 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -1906,6 +1906,26 @@ TRACE_EVENT(api_radar_detected,
 	)
 );
 
+TRACE_EVENT(drv_channel_switch_beacon,
+	TP_PROTO(struct ieee80211_sub_if_data *sdata),
+
+	TP_ARGS(sdata),
+
+	TP_STRUCT__entry(
+		VIF_ENTRY
+	),
+
+	TP_fast_assign(
+		VIF_ASSIGN;
+	),
+
+	TP_printk(
+		VIF_PR_FMT " channel switch",
+		VIF_PR_ARG
+	)
+);
+
+
 #ifdef CONFIG_MAC80211_MESSAGE_TRACING
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM mac80211_msg
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 4105d0c..0f1916f 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2320,6 +2320,47 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
 	return 0;
 }
 
+void ieee80211_finish_csa(struct ieee80211_vif *vif)
+{
+	struct ieee80211_sub_if_data *sdata = NULL;
+	sdata = vif_to_sdata(vif);
+
+	vif->csa_active = 0;
+	ieee80211_queue_work(&sdata->local->hw,
+			     &sdata->csa_finalize_work);
+}
+EXPORT_SYMBOL(ieee80211_finish_csa);
+
+static int ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
+				struct beacon_data *beacon)
+{
+	struct probe_resp *resp;
+	int counter_beacon = sdata->csa_counter_offset_beacon;
+	int counter_presp = sdata->csa_counter_offset_presp;
+
+	if (WARN_ON(counter_beacon > beacon->tail_len))
+		return 1;
+
+	((u8 *)beacon->tail)[counter_beacon]--;
+
+	if (counter_presp && sdata->vif.type == NL80211_IFTYPE_AP) {
+		resp = rcu_dereference(sdata->u.ap.probe_resp);
+		if (WARN_ON(!resp))
+			return 1;
+		if (WARN_ON(counter_presp > resp->len))
+			return 1;
+
+		((u8 *)resp->data)[counter_presp]--;
+	}
+
+	if (((u8 *)beacon->tail)[counter_beacon] == 0) {
+		sdata->vif.csa_active = 0;
+		ieee80211_finish_csa(&sdata->vif);
+		return 1;
+	}
+	return 0;
+}
+
 struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
 					 struct ieee80211_vif *vif,
 					 u16 *tim_offset, u16 *tim_length)
@@ -2350,6 +2391,10 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
 		struct beacon_data *beacon = rcu_dereference(ap->beacon);
 
 		if (beacon) {
+			if (sdata->vif.csa_active)
+				if (ieee80211_update_csa(sdata, beacon))
+					goto out;
+
 			/*
 			 * headroom, head length,
 			 * tail length and maximum TIM length
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 1/5] nl80211: use attributes to parse beacons
From: Simon Wunderlich @ 2013-06-07 17:05 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg, Mathias Kretschmer, Simon Wunderlich
In-Reply-To: <1370624745-2267-1-git-send-email-siwu@hrz.tu-chemnitz.de>

only the attributes are required and not the whole netlink info, as the
function accesses the attributes only anyway. This makes it easier to
parse nested beacon IEs later.

Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
---
 net/wireless/nl80211.c |   53 +++++++++++++++++++++++-------------------------
 1 file changed, 25 insertions(+), 28 deletions(-)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 88e820b..1623f22 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -2879,61 +2879,58 @@ static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info)
 	return err;
 }
 
-static int nl80211_parse_beacon(struct genl_info *info,
+static int nl80211_parse_beacon(struct nlattr *attrs[],
 				struct cfg80211_beacon_data *bcn)
 {
 	bool haveinfo = false;
 
-	if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
-	    !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
-	    !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
-	    !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
+	if (!is_valid_ie_attr(attrs[NL80211_ATTR_BEACON_TAIL]) ||
+	    !is_valid_ie_attr(attrs[NL80211_ATTR_IE]) ||
+	    !is_valid_ie_attr(attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
+	    !is_valid_ie_attr(attrs[NL80211_ATTR_IE_ASSOC_RESP]))
 		return -EINVAL;
 
 	memset(bcn, 0, sizeof(*bcn));
 
-	if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
-		bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
-		bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
+	if (attrs[NL80211_ATTR_BEACON_HEAD]) {
+		bcn->head = nla_data(attrs[NL80211_ATTR_BEACON_HEAD]);
+		bcn->head_len = nla_len(attrs[NL80211_ATTR_BEACON_HEAD]);
 		if (!bcn->head_len)
 			return -EINVAL;
 		haveinfo = true;
 	}
 
-	if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
-		bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
-		bcn->tail_len =
-		    nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
+	if (attrs[NL80211_ATTR_BEACON_TAIL]) {
+		bcn->tail = nla_data(attrs[NL80211_ATTR_BEACON_TAIL]);
+		bcn->tail_len = nla_len(attrs[NL80211_ATTR_BEACON_TAIL]);
 		haveinfo = true;
 	}
 
 	if (!haveinfo)
 		return -EINVAL;
 
-	if (info->attrs[NL80211_ATTR_IE]) {
-		bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
-		bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+	if (attrs[NL80211_ATTR_IE]) {
+		bcn->beacon_ies = nla_data(attrs[NL80211_ATTR_IE]);
+		bcn->beacon_ies_len = nla_len(attrs[NL80211_ATTR_IE]);
 	}
 
-	if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
+	if (attrs[NL80211_ATTR_IE_PROBE_RESP]) {
 		bcn->proberesp_ies =
-			nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
+			nla_data(attrs[NL80211_ATTR_IE_PROBE_RESP]);
 		bcn->proberesp_ies_len =
-			nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
+			nla_len(attrs[NL80211_ATTR_IE_PROBE_RESP]);
 	}
 
-	if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
+	if (attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
 		bcn->assocresp_ies =
-			nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
+			nla_data(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
 		bcn->assocresp_ies_len =
-			nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
+			nla_len(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
 	}
 
-	if (info->attrs[NL80211_ATTR_PROBE_RESP]) {
-		bcn->probe_resp =
-			nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]);
-		bcn->probe_resp_len =
-			nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
+	if (attrs[NL80211_ATTR_PROBE_RESP]) {
+		bcn->probe_resp = nla_data(attrs[NL80211_ATTR_PROBE_RESP]);
+		bcn->probe_resp_len = nla_len(attrs[NL80211_ATTR_PROBE_RESP]);
 	}
 
 	return 0;
@@ -3012,7 +3009,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
 	    !info->attrs[NL80211_ATTR_BEACON_HEAD])
 		return -EINVAL;
 
-	err = nl80211_parse_beacon(info, &params.beacon);
+	err = nl80211_parse_beacon(info->attrs, &params.beacon);
 	if (err)
 		return err;
 
@@ -3164,7 +3161,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
 	if (!wdev->beacon_interval)
 		return -EINVAL;
 
-	err = nl80211_parse_beacon(info, &params);
+	err = nl80211_parse_beacon(info->attrs, &params);
 	if (err)
 		return err;
 
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 2/5] nl80211/cfg80211: add channel switch command
From: Simon Wunderlich @ 2013-06-07 17:05 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg, Mathias Kretschmer, Simon Wunderlich
In-Reply-To: <1370624745-2267-1-git-send-email-siwu@hrz.tu-chemnitz.de>

To allow channel switch announcements within beacons, add
the channel switch command to nl80211/cfg80211. This is
implementation is intended for AP and (later) IBSS mode.

Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
---
Changes to RFCv1:
 * accept and pass CSA IEs and after-change IEs
 * use parameter structure instead of individual parameters
---
 include/net/cfg80211.h       |   26 ++++++++++
 include/uapi/linux/nl80211.h |   29 +++++++++++
 net/wireless/nl80211.c       |  118 +++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 172 insertions(+), 1 deletion(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 4cdab6d..efff759 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -610,6 +610,29 @@ struct cfg80211_ap_settings {
 	bool radar_required;
 };
 
+/* struct cfg80211_csa_settings - channel switch settings
+ *
+ * Used for channel switch
+ *
+ * @chandef: defines the channel to use after the switch
+ * @beacon_csa: beacon data while performing the switch
+ * @counter_offset_beacon: offset for the counter within the beacon (tail)
+ * @counter_offset_presp: offset for the counter within the probe response
+ * @beacon_after: beacon data to be used on the new channel
+ * @radar_required: whether radar detection is required on the new channel
+ * @block_tx: whether transmissions should be blocked while changing
+ * @count: number of beacons until switch
+ */
+struct cfg80211_csa_settings {
+	struct cfg80211_chan_def chandef;
+	struct cfg80211_beacon_data beacon_csa;
+	u16 counter_offset_beacon, counter_offset_presp;
+	struct cfg80211_beacon_data beacon_after;
+	bool radar_required;
+	bool block_tx;
+	u8 count;
+};
+
 /**
  * enum station_parameters_apply_mask - station parameter values to apply
  * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp)
@@ -2272,6 +2295,9 @@ struct cfg80211_ops {
 				    u16 duration);
 	void	(*crit_proto_stop)(struct wiphy *wiphy,
 				   struct wireless_dev *wdev);
+	int	(*channel_switch)(struct wiphy *wiphy,
+				  struct net_device *dev,
+				  struct cfg80211_csa_settings *params);
 };
 
 /*
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 5920715..71c85bd 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -648,6 +648,16 @@
  * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can
  *	return back to normal.
  *
+ * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the
+ *	the new channel information (Channel Switch Announcement - CSA)
+ *	in the beacon for some time (as defined in the
+ *	%NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the
+ *	new channel. Userspace provides the new channel information (using
+ *	%NL80211_ATTR_WIPHY_FREQ and the attributes determining channel
+ *	width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform
+ *	other station that transmission must be blocked until the channel
+ *	switch is complete.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -810,6 +820,7 @@ enum nl80211_commands {
 	NL80211_CMD_CRIT_PROTOCOL_START,
 	NL80211_CMD_CRIT_PROTOCOL_STOP,
 
+	NL80211_CMD_CHANNEL_SWITCH,
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -1436,6 +1447,18 @@ enum nl80211_commands {
  *	allowed to be used with the first @NL80211_CMD_SET_STATION command to
  *	update a TDLS peer STA entry.
  *
+ * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
+ *	until the channel switch event.
+ * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
+ *	must be blocked on the current channel (before the channel switch
+ *	operation).
+ * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
+ *	for the time while performing a channel switch.
+ * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
+ *	field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
+ *	field in the probe response (%NL80211_ATTR_PROBE_RESP).
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1736,6 +1759,12 @@ enum nl80211_attrs {
 
 	NL80211_ATTR_PEER_AID,
 
+	NL80211_ATTR_CH_SWITCH_COUNT,
+	NL80211_ATTR_CH_SWITCH_BLOCK_TX,
+	NL80211_ATTR_CSA_IES,
+	NL80211_ATTR_CSA_C_OFF_BEACON,
+	NL80211_ATTR_CSA_C_OFF_PRESP,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 1623f22..71b60da 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -349,6 +349,11 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
 	[NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
 				  .len = IEEE80211_MAX_DATA_LEN },
 	[NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
+	[NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },
+	[NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },
+	[NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
+	[NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
+	[NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
 };
 
 /* policy for the key attributes */
@@ -5529,6 +5534,109 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
 	return err;
 }
 
+static int nl80211_channel_switch(struct sk_buff *skb,
+				  struct genl_info *info)
+{
+	struct cfg80211_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_csa_settings params;
+	struct nlattr *csa_ies[NL80211_ATTR_MAX+1];
+	u8 radar_detect_width = 0;
+	int err;
+
+	if (!rdev->ops->channel_switch)
+		return -EOPNOTSUPP;
+
+	/* may add IBSS support later */
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+		return -EOPNOTSUPP;
+
+	memset(&params, 0, sizeof(params));
+
+	if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
+	    !info->attrs[NL80211_ATTR_CH_SWITCH_COUNT])
+		return -EINVAL;
+
+	/* only important for AP, IBSS and mesh create IEs internally */
+	if (!info->attrs[NL80211_ATTR_CSA_IES] ||
+	    !info->attrs[NL80211_ATTR_CSA_C_OFF_BEACON])
+		return -EINVAL;
+
+	/* useless if AP is not running */
+	if (!wdev->beacon_interval)
+		return -EINVAL;
+
+	params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
+
+	err = nl80211_parse_beacon(info->attrs, &params.beacon_after);
+	if (err)
+		return err;
+
+	err = nla_parse_nested(csa_ies, NL80211_ATTR_MAX,
+				   info->attrs[NL80211_ATTR_CSA_IES],
+				   nl80211_policy);
+	if (err)
+		return err;
+
+	err = nl80211_parse_beacon(csa_ies, &params.beacon_csa);
+	if (err)
+		return err;
+
+	params.counter_offset_beacon =
+		nla_get_u16(info->attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+	if (params.counter_offset_beacon > params.beacon_csa.tail_len)
+		return -EINVAL;
+
+	/* sanity check - counter should be the same this should be the same */
+	if (params.beacon_csa.tail[params.counter_offset_beacon] !=
+	    params.count)
+		return -EINVAL;
+
+	if (info->attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
+		params.counter_offset_presp =
+			nla_get_u16(info->attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+		if (params.counter_offset_presp >
+		    params.beacon_csa.probe_resp_len)
+			return -EINVAL;
+
+		if (params.beacon_csa.probe_resp[params.counter_offset_presp] !=
+		    params.count)
+			return -EINVAL;
+	}
+
+	err = nl80211_parse_chandef(rdev, info, &params.chandef);
+	if (err)
+		return err;
+
+	if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
+		return -EINVAL;
+
+	err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef);
+	if (err < 0)
+		return err;
+
+	if (err)
+		radar_detect_width = BIT(params.chandef.width);
+	else
+		radar_detect_width = 0;
+
+	err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+					   params.chandef.chan,
+					   CHAN_MODE_SHARED,
+					   radar_detect_width);
+	if (err)
+		return err;
+
+	if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
+		params.block_tx = true;
+
+	err = rdev->ops->channel_switch(&rdev->wiphy, dev, &params);
+
+	return err;
+}
+
 static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
 			    u32 seq, int flags,
 			    struct cfg80211_registered_device *rdev,
@@ -8956,7 +9064,15 @@ static struct genl_ops nl80211_ops[] = {
 		.flags = GENL_ADMIN_PERM,
 		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
 				  NL80211_FLAG_NEED_RTNL,
-	}
+	},
+	{
+		.cmd = NL80211_CMD_CHANNEL_SWITCH,
+		.doit = nl80211_channel_switch,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+		NL80211_FLAG_NEED_RTNL,
+	},
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 0/5] add master channel switch announcement support
From: Simon Wunderlich @ 2013-06-07 17:05 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg, Mathias Kretschmer, Simon Wunderlich

This is the follow up from the RFC posted a month ago. This patchset adds generic
channel switch support for AP. This is required for DFS operation
(e.g. Wi-Fi Alliance requires this for 802.11h certification). This will also
be required for IBSS-DFS later.

The rough design is:
 * userspace asks kernel to switch a channel using the new NL80211_CMD_CHANNEL_SWITCH
   command. It supplies IE information for the time while staying on the old channel and
   announcing the switch, and IE information for after the switch to the new channel. 
 * IE information contains the beacon and optionally probe responses, which should
   include (E)CSA IEs for the CSA case. Furthermore an offset is provided (for beacon
   and probe response) to point to the counter field within the channel switch IEs.
 * The driver gets the new beacons passed and must set them, and decrement the
   counter field. When it reaches 0, the channel is changed and userspace notified.

Discussion points:
 * Assembling all these IE information is a little bit tedious but doable (I've
   patched hostapd).
 * Other future users like IBSS/MESH will not get the beacon/probe response IEs, as they
   generate these beacons themselves. Therefore they need the COUNT attribute, which
   is kind of duplicate right now.
 * Userspace must generate/handle all IEs, which lifts the previous limitations of
   the RFC (e.g. no change of band allowed, no operation mode change allowed).
 * it currently works for me [TM] on my ath9k based machine

As always, any comments are appreciated.

Cheers,
	Simon 

Simon Wunderlich (5):
  nl80211: use attributes to parse beacons
  nl80211/cfg80211: add channel switch command
  mac80211: add functions to duplicate a cfg80211_beacon
  mac80211: add channel switch command and beacon callbacks
  ath9k: enable CSA functionality in ath9k

 drivers/net/wireless/ath/ath9k/main.c |    6 ++
 include/net/cfg80211.h                |   26 +++++
 include/net/mac80211.h                |   21 ++++
 include/uapi/linux/nl80211.h          |   29 ++++++
 net/mac80211/cfg.c                    |  182 ++++++++++++++++++++++++++++++++-
 net/mac80211/driver-ops.h             |   11 ++
 net/mac80211/ieee80211_i.h            |   11 ++
 net/mac80211/iface.c                  |    2 +
 net/mac80211/trace.h                  |   20 ++++
 net/mac80211/tx.c                     |   45 ++++++++
 net/wireless/nl80211.c                |  171 +++++++++++++++++++++++++------
 11 files changed, 493 insertions(+), 31 deletions(-)

-- 
1.7.10.4


^ permalink raw reply

* Re: [RFC 4/5] mac80211: enforce address verification on monitors
From: Jakub Kiciński @ 2013-06-07 16:42 UTC (permalink / raw)
  To: Felix Fietkau
  Cc: linux-wireless, Helmut Schaa, Johannes Berg,
	nietrywialneprzejscie
In-Reply-To: <51B1EDA8.10406@openwrt.org>

On Fri, 07 Jun 2013 16:26:48 +0200, Felix Fietkau wrote:
>On 2013-06-07 4:04 PM, Jakub Kiciński wrote:
>> On Fri, 07 Jun 2013 15:49:39 +0200, Felix Fietkau wrote:
>>> On 2013-06-07 3:36 PM, Jakub Kicinski wrote:
>>>> From: Jakub Kicinski <kubakici@wp.pl>
>>>> 
>>>> Mac address of passive monitor have to treated
>>>> the same as address any other interface because
>>>> type of interface can be changed or monitor can
>>>> be later put into active mode.
>>> It can't. There's checks preventing that. I'd prefer to leave that part
>>> of the code as it is, the MAC address for normal monitor interfaces
>>> should be ignored completely.
>> 
>> I must have misread the code regarding active monitors then.
>> Main motivation behind this patch was the first part though
>> - user can change type of interface and once ignored address
>> of monitor now becomes address of AP, sta etc. 
> I'm pretty sure that switching from monitor to something else is also
> not supported.

Ok, let me explain what I mean to prevent by showing a command
trace - running unpatched wireless-testing and patched iw [1].

# lsmod | grep mac8
# modprobe rt2800usb
# iw dev wlan0 interface add wlan0-1 type managed
# ip link
75: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 00:4f:6a:06:57:90 brd ff:ff:ff:ff:ff:ff
76: wlan0-1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 00:4f:6a:06:57:91 brd ff:ff:ff:ff:ff:ff

Now I can start two hostapd on those interfaces and
everything works just fine. 

# iw dev wlan0-1 set type monitor
# ip link set dev wlan0-1 address 00:00:fa:22:7c:00
# iw dev wlan0-1 set type managed
# ip link
75: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    link/ether 00:4f:6a:06:57:90 brd ff:ff:ff:ff:ff:ff
76: wlan0-1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    link/ether 00:00:fa:22:7c:00 brd ff:ff:ff:ff:ff:ff

If I start hostapd on both interfaces now the one on wlan0-1
will not work correctly (hw won't ack frames).

Also I think it's possible to change active flag on a monitor
while it's down (check in net/mac80211/cfg.c:75 only applies
to interfaces that are up):

# iw dev wlan0 interface add wlan0-1 type monitor flags active
# iw dev wlan0-1 set monitor none
# iw dev wlan0-1 set monitor active
# iw dev wlan0-1 set monitor none

  -- kuba

[1] http://moorray.hopto.org/iw_active_monitors.patch

^ permalink raw reply

* [PATCH v3 2/3] ath9k: implement support for .release_buffered_frames()
From: Felix Fietkau @ 2013-06-07 16:12 UTC (permalink / raw)
  To: linux-wireless; +Cc: linville
In-Reply-To: <1370621522-59812-1-git-send-email-nbd@openwrt.org>

This adds support for PS-Poll and U-APSD driver-buffered frames (part of
an aggregation session).

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
 drivers/net/wireless/ath/ath9k/ath9k.h |   5 +
 drivers/net/wireless/ath/ath9k/main.c  |   1 +
 drivers/net/wireless/ath/ath9k/xmit.c  | 175 ++++++++++++++++++++++++++-------
 3 files changed, 145 insertions(+), 36 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 4f79827..bd35a54 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -354,6 +354,11 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid
 void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an);
 void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
 		       struct ath_node *an);
+void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
+				   struct ieee80211_sta *sta,
+				   u16 tids, int nframes,
+				   enum ieee80211_frame_release_type reason,
+				   bool more_data);
 
 /********/
 /* VIFs */
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index e5b186b..bf61a1c 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2347,6 +2347,7 @@ struct ieee80211_ops ath9k_ops = {
 	.flush		    = ath9k_flush,
 	.tx_frames_pending  = ath9k_tx_frames_pending,
 	.tx_last_beacon     = ath9k_tx_last_beacon,
+	.release_buffered_frames = ath9k_release_buffered_frames,
 	.get_stats	    = ath9k_get_stats,
 	.set_antenna	    = ath9k_set_antenna,
 	.get_antenna	    = ath9k_get_antenna,
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 3931bd8..6c9ff9c 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -518,6 +518,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
 			ath_tx_complete_buf(sc, bf, txq, &bf_head, ts,
 				!txfail);
 		} else {
+			if (tx_info->flags & IEEE80211_TX_STATUS_EOSP) {
+				tx_info->flags &= ~IEEE80211_TX_STATUS_EOSP;
+				ieee80211_sta_eosp(sta);
+			}
 			/* retry the un-acked ones */
 			if (bf->bf_next == NULL && bf_last->bf_stale) {
 				struct ath_buf *tbf;
@@ -786,25 +790,20 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
 	return ndelim;
 }
 
-static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
-					     struct ath_txq *txq,
-					     struct ath_atx_tid *tid,
-					     struct list_head *bf_q,
-					     int *aggr_len)
+static struct ath_buf *
+ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq,
+			struct ath_atx_tid *tid)
 {
-#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
-	struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL;
-	int rl = 0, nframes = 0, ndelim, prev_al = 0;
-	u16 aggr_limit = 0, al = 0, bpad = 0,
-		al_delta, h_baw = tid->baw_size / 2;
-	enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
-	struct ieee80211_tx_info *tx_info;
 	struct ath_frame_info *fi;
 	struct sk_buff *skb;
+	struct ath_buf *bf;
 	u16 seqno;
 
-	do {
+	while (1) {
 		skb = skb_peek(&tid->buf_q);
+		if (!skb)
+			break;
+
 		fi = get_frame_info(skb);
 		bf = fi->bf;
 		if (!fi->bf)
@@ -820,10 +819,8 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
 		seqno = bf->bf_state.seqno;
 
 		/* do not step over block-ack window */
-		if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) {
-			status = ATH_AGGR_BAW_CLOSED;
+		if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno))
 			break;
-		}
 
 		if (tid->bar_index > ATH_BA_INDEX(tid->seq_start, seqno)) {
 			struct ath_tx_status ts = {};
@@ -837,6 +834,40 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
 			continue;
 		}
 
+		bf->bf_next = NULL;
+		bf->bf_lastbf = bf;
+		return bf;
+	}
+
+	return NULL;
+}
+
+static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
+					     struct ath_txq *txq,
+					     struct ath_atx_tid *tid,
+					     struct list_head *bf_q,
+					     int *aggr_len)
+{
+#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
+	struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL;
+	int rl = 0, nframes = 0, ndelim, prev_al = 0;
+	u16 aggr_limit = 0, al = 0, bpad = 0,
+		al_delta, h_baw = tid->baw_size / 2;
+	enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
+	struct ieee80211_tx_info *tx_info;
+	struct ath_frame_info *fi;
+	struct sk_buff *skb;
+
+	do {
+		bf = ath_tx_get_tid_subframe(sc, txq, tid);
+		if (!bf) {
+			status = ATH_AGGR_BAW_CLOSED;
+			break;
+		}
+
+		skb = bf->bf_mpdu;
+		fi = get_frame_info(skb);
+
 		if (!bf_first)
 			bf_first = bf;
 
@@ -882,7 +913,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
 
 		/* link buffers of this frame to the aggregate */
 		if (!fi->retries)
-			ath_tx_addto_baw(sc, tid, seqno);
+			ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
 		bf->bf_state.ndelim = ndelim;
 
 		__skb_unlink(skb, &tid->buf_q);
@@ -1090,10 +1121,8 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
 			     struct ath_txq *txq, int len)
 {
 	struct ath_hw *ah = sc->sc_ah;
-	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
-	struct ath_buf *bf_first = bf;
+	struct ath_buf *bf_first = NULL;
 	struct ath_tx_info info;
-	bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR);
 
 	memset(&info, 0, sizeof(info));
 	info.is_first = true;
@@ -1101,24 +1130,11 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
 	info.txpower = MAX_RATE_POWER;
 	info.qcu = txq->axq_qnum;
 
-	info.flags = ATH9K_TXDESC_INTREQ;
-	if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
-		info.flags |= ATH9K_TXDESC_NOACK;
-	if (tx_info->flags & IEEE80211_TX_CTL_LDPC)
-		info.flags |= ATH9K_TXDESC_LDPC;
-
-	ath_buf_set_rate(sc, bf, &info, len);
-
-	if (tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
-		info.flags |= ATH9K_TXDESC_CLRDMASK;
-
-	if (bf->bf_state.bfs_paprd)
-		info.flags |= (u32) bf->bf_state.bfs_paprd << ATH9K_TXDESC_PAPRD_S;
-
-
 	while (bf) {
 		struct sk_buff *skb = bf->bf_mpdu;
+		struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
 		struct ath_frame_info *fi = get_frame_info(skb);
+		bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR);
 
 		info.type = get_hw_packet_type(skb);
 		if (bf->bf_next)
@@ -1126,6 +1142,26 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
 		else
 			info.link = 0;
 
+		if (!bf_first) {
+			bf_first = bf;
+
+			info.flags = ATH9K_TXDESC_INTREQ;
+			if ((tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) ||
+			    txq == sc->tx.uapsdq)
+				info.flags |= ATH9K_TXDESC_CLRDMASK;
+
+			if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
+				info.flags |= ATH9K_TXDESC_NOACK;
+			if (tx_info->flags & IEEE80211_TX_CTL_LDPC)
+				info.flags |= ATH9K_TXDESC_LDPC;
+
+			if (bf->bf_state.bfs_paprd)
+				info.flags |= (u32) bf->bf_state.bfs_paprd <<
+					      ATH9K_TXDESC_PAPRD_S;
+
+			ath_buf_set_rate(sc, bf, &info, len);
+		}
+
 		info.buf_addr[0] = bf->bf_buf_addr;
 		info.buf_len[0] = skb->len;
 		info.pkt_len = fi->framelen;
@@ -1135,7 +1171,7 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
 		if (aggr) {
 			if (bf == bf_first)
 				info.aggr = AGGR_BUF_FIRST;
-			else if (!bf->bf_next)
+			else if (bf == bf_first->bf_lastbf)
 				info.aggr = AGGR_BUF_LAST;
 			else
 				info.aggr = AGGR_BUF_MIDDLE;
@@ -1144,6 +1180,9 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
 			info.aggr_len = len;
 		}
 
+		if (bf == bf_first->bf_lastbf)
+			bf_first = NULL;
+
 		ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
 		bf = bf->bf_next;
 	}
@@ -1328,6 +1367,70 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
 	ath_txq_unlock_complete(sc, txq);
 }
 
+void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
+				   struct ieee80211_sta *sta,
+				   u16 tids, int nframes,
+				   enum ieee80211_frame_release_type reason,
+				   bool more_data)
+{
+	struct ath_softc *sc = hw->priv;
+	struct ath_node *an = (struct ath_node *)sta->drv_priv;
+	struct ath_txq *txq = sc->tx.uapsdq;
+	struct ieee80211_tx_info *info;
+	struct list_head bf_q;
+	struct ath_buf *bf_tail = NULL, *bf;
+	int sent = 0;
+	int i;
+
+	INIT_LIST_HEAD(&bf_q);
+	for (i = 0; tids && nframes; i++, tids >>= 1) {
+		struct ath_atx_tid *tid;
+
+		if (!(tids & 1))
+			continue;
+
+		tid = ATH_AN_2_TID(an, i);
+		if (tid->paused)
+			continue;
+
+		ath_txq_lock(sc, tid->ac->txq);
+		while (!skb_queue_empty(&tid->buf_q) && nframes > 0) {
+			bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid);
+			if (!bf)
+				break;
+
+			__skb_unlink(bf->bf_mpdu, &tid->buf_q);
+			list_add_tail(&bf->list, &bf_q);
+			ath_set_rates(tid->an->vif, tid->an->sta, bf);
+			ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
+			bf->bf_state.bf_type &= ~BUF_AGGR;
+			if (bf_tail)
+				bf_tail->bf_next = bf;
+
+			bf_tail = bf;
+			nframes--;
+			sent++;
+			TX_STAT_INC(txq->axq_qnum, a_queued_hw);
+
+			if (skb_queue_empty(&tid->buf_q))
+				ieee80211_sta_set_buffered(an->sta, i, false);
+		}
+		ath_txq_unlock_complete(sc, tid->ac->txq);
+	}
+
+	if (list_empty(&bf_q))
+		return;
+
+	info = IEEE80211_SKB_CB(bf_tail->bf_mpdu);
+	info->flags |= IEEE80211_TX_STATUS_EOSP;
+
+	bf = list_first_entry(&bf_q, struct ath_buf, list);
+	ath_txq_lock(sc, txq);
+	ath_tx_fill_desc(sc, bf, txq, 0);
+	ath_tx_txqaddbuf(sc, txq, &bf_q, false);
+	ath_txq_unlock(sc, txq);
+}
+
 /********************/
 /* Queue Management */
 /********************/
-- 
1.8.0.2


^ permalink raw reply related

* [PATCH v3 3/3] ath9k: limit multicast buffer hardware queue depth
From: Felix Fietkau @ 2013-06-07 16:12 UTC (permalink / raw)
  To: linux-wireless; +Cc: linville
In-Reply-To: <1370621522-59812-1-git-send-email-nbd@openwrt.org>

The CAB (Content after Beacon) queue is used for beacon-triggered
transmission of buffered multicast frames. If lots of multicast frames
were buffered and this queue fills up, it drowns out all regular
traffic. To limit the damage that buffered traffic can do, try to limit
the queued data to becaon_interval / 8.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
 drivers/net/wireless/ath/ath9k/ath9k.h  |   2 +
 drivers/net/wireless/ath/ath9k/beacon.c |  23 +------
 drivers/net/wireless/ath/ath9k/xmit.c   | 115 +++++++++++++++++++++++++++-----
 3 files changed, 104 insertions(+), 36 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index bd35a54..a6e666b 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -344,6 +344,8 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
 void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop);
 int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 		 struct ath_tx_control *txctl);
+void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		 struct sk_buff *skb);
 void ath_tx_tasklet(struct ath_softc *sc);
 void ath_tx_edma_tasklet(struct ath_softc *sc);
 int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index fd1eeba..1a17732 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -108,23 +108,6 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
 	ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
 }
 
-static void ath9k_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
-{
-	struct ath_softc *sc = hw->priv;
-	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-	struct ath_tx_control txctl;
-
-	memset(&txctl, 0, sizeof(struct ath_tx_control));
-	txctl.txq = sc->beacon.cabq;
-
-	ath_dbg(common, XMIT, "transmitting CABQ packet, skb: %p\n", skb);
-
-	if (ath_tx_start(hw, skb, &txctl) != 0) {
-		ath_dbg(common, XMIT, "CABQ TX failed\n");
-		ieee80211_free_txskb(hw, skb);
-	}
-}
-
 static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
 					     struct ieee80211_vif *vif)
 {
@@ -206,10 +189,8 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
 
 	ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx);
 
-	while (skb) {
-		ath9k_tx_cabq(hw, skb);
-		skb = ieee80211_get_buffered_bc(hw, vif);
-	}
+	if (skb)
+		ath_tx_cabq(hw, vif, skb);
 
 	return bf;
 }
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 6c9ff9c..7e19d9b 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1970,22 +1970,16 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
 	return bf;
 }
 
-/* Upon failure caller should free skb */
-int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
-		 struct ath_tx_control *txctl)
+static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb,
+			  struct ath_tx_control *txctl)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_sta *sta = txctl->sta;
 	struct ieee80211_vif *vif = info->control.vif;
 	struct ath_softc *sc = hw->priv;
-	struct ath_txq *txq = txctl->txq;
-	struct ath_atx_tid *tid = NULL;
-	struct ath_buf *bf;
-	int padpos, padsize;
 	int frmlen = skb->len + FCS_LEN;
-	u8 tidno;
-	int q;
+	int padpos, padsize;
 
 	/* NOTE:  sta can be NULL according to net/mac80211.h */
 	if (sta)
@@ -2006,6 +2000,11 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 		hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
 	}
 
+	if ((vif && vif->type != NL80211_IFTYPE_AP &&
+	            vif->type != NL80211_IFTYPE_AP_VLAN) ||
+	    !ieee80211_is_data(hdr->frame_control))
+		info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
+
 	/* Add the padding after the header if this is not already done */
 	padpos = ieee80211_hdrlen(hdr->frame_control);
 	padsize = padpos & 3;
@@ -2015,16 +2014,34 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 
 		skb_push(skb, padsize);
 		memmove(skb->data, skb->data + padsize, padpos);
-		hdr = (struct ieee80211_hdr *) skb->data;
 	}
 
-	if ((vif && vif->type != NL80211_IFTYPE_AP &&
-	            vif->type != NL80211_IFTYPE_AP_VLAN) ||
-	    !ieee80211_is_data(hdr->frame_control))
-		info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
-
 	setup_frame_info(hw, sta, skb, frmlen);
+	return 0;
+}
+
 
+/* Upon failure caller should free skb */
+int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
+		 struct ath_tx_control *txctl)
+{
+	struct ieee80211_hdr *hdr;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_sta *sta = txctl->sta;
+	struct ieee80211_vif *vif = info->control.vif;
+	struct ath_softc *sc = hw->priv;
+	struct ath_txq *txq = txctl->txq;
+	struct ath_atx_tid *tid = NULL;
+	struct ath_buf *bf;
+	u8 tidno;
+	int q;
+	int ret;
+
+	ret = ath_tx_prepare(hw, skb, txctl);
+	if (ret)
+	    return ret;
+
+	hdr = (struct ieee80211_hdr *) skb->data;
 	/*
 	 * At this point, the vif, hw_key and sta pointers in the tx control
 	 * info are no longer valid (overwritten by the ath_frame_info data.
@@ -2086,6 +2103,74 @@ out:
 	return 0;
 }
 
+void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		 struct sk_buff *skb)
+{
+	struct ath_softc *sc = hw->priv;
+	struct ath_tx_control txctl = {
+		.txq = sc->beacon.cabq
+	};
+	struct ath_tx_info info = {};
+	struct ieee80211_hdr *hdr;
+	struct ath_buf *bf_tail = NULL;
+	struct ath_buf *bf;
+	LIST_HEAD(bf_q);
+	int duration = 0;
+	int max_duration;
+
+	max_duration =
+		sc->cur_beacon_conf.beacon_interval * 1000 *
+		sc->cur_beacon_conf.dtim_period / ATH_BCBUF;
+
+	do {
+		struct ath_frame_info *fi = get_frame_info(skb);
+
+		if (ath_tx_prepare(hw, skb, &txctl))
+			break;
+
+		bf = ath_tx_setup_buffer(sc, txctl.txq, NULL, skb);
+		if (!bf)
+			break;
+
+		bf->bf_lastbf = bf;
+		ath_set_rates(vif, NULL, bf);
+		ath_buf_set_rate(sc, bf, &info, fi->framelen);
+		duration += info.rates[0].PktDuration;
+		if (bf_tail)
+			bf_tail->bf_next = bf;
+
+		list_add_tail(&bf->list, &bf_q);
+		bf_tail = bf;
+		skb = NULL;
+
+		if (duration > max_duration)
+			break;
+
+		skb = ieee80211_get_buffered_bc(hw, vif);
+	} while(skb);
+
+	if (skb)
+		ieee80211_free_txskb(hw, skb);
+
+	if (list_empty(&bf_q))
+		return;
+
+	bf = list_first_entry(&bf_q, struct ath_buf, list);
+	hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data;
+
+	if (hdr->frame_control & IEEE80211_FCTL_MOREDATA) {
+		hdr->frame_control &= ~IEEE80211_FCTL_MOREDATA;
+		dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
+			sizeof(*hdr), DMA_TO_DEVICE);
+	}
+
+	ath_txq_lock(sc, txctl.txq);
+	ath_tx_fill_desc(sc, bf, txctl.txq, 0);
+	ath_tx_txqaddbuf(sc, txctl.txq, &bf_q, false);
+	TX_STAT_INC(txctl.txq->axq_qnum, queued);
+	ath_txq_unlock(sc, txctl.txq);
+}
+
 /*****************/
 /* TX Completion */
 /*****************/
-- 
1.8.0.2


^ permalink raw reply related

* [PATCH v3 1/3] ath9k: add support for IEEE80211_TX_CTL_PS_RESPONSE
From: Felix Fietkau @ 2013-06-07 16:12 UTC (permalink / raw)
  To: linux-wireless; +Cc: linville

Use the UAPSD hardware queue to get PS-Poll responses out as fast as
possible and without backoff.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
 drivers/net/wireless/ath/ath9k/ath9k.h |  1 +
 drivers/net/wireless/ath/ath9k/init.c  |  2 ++
 drivers/net/wireless/ath/ath9k/xmit.c  | 43 +++++++++++++++++++++-------------
 3 files changed, 30 insertions(+), 16 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 18fcee4..4f79827 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -296,6 +296,7 @@ struct ath_tx {
 	struct ath_txq txq[ATH9K_NUM_TX_QUEUES];
 	struct ath_descdma txdma;
 	struct ath_txq *txq_map[IEEE80211_NUM_ACS];
+	struct ath_txq *uapsdq;
 	u32 txq_max_pending[IEEE80211_NUM_ACS];
 	u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32];
 };
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 389ee1b..d65ee6e 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -432,6 +432,8 @@ static int ath9k_init_queues(struct ath_softc *sc)
 	sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
 	ath_cabq_update(sc);
 
+	sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0);
+
 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
 		sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i);
 		sc->tx.txq_map[i]->mac80211_qnum = i;
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 1c9b1ba..3931bd8 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1681,8 +1681,9 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
 	}
 }
 
-static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
-			      struct sk_buff *skb, struct ath_tx_control *txctl)
+static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_txq *txq,
+			      struct ath_atx_tid *tid, struct sk_buff *skb,
+			      struct ath_tx_control *txctl)
 {
 	struct ath_frame_info *fi = get_frame_info(skb);
 	struct list_head bf_head;
@@ -1695,21 +1696,22 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
 	 * - seqno is not within block-ack window
 	 * - h/w queue depth exceeds low water mark
 	 */
-	if (!skb_queue_empty(&tid->buf_q) || tid->paused ||
-	    !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) ||
-	    txctl->txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) {
+	if ((!skb_queue_empty(&tid->buf_q) || tid->paused ||
+	     !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) ||
+	     txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) &&
+	    txq != sc->tx.uapsdq) {
 		/*
 		 * Add this frame to software queue for scheduling later
 		 * for aggregation.
 		 */
-		TX_STAT_INC(txctl->txq->axq_qnum, a_queued_sw);
+		TX_STAT_INC(txq->axq_qnum, a_queued_sw);
 		__skb_queue_tail(&tid->buf_q, skb);
 		if (!txctl->an || !txctl->an->sleeping)
-			ath_tx_queue_tid(txctl->txq, tid);
+			ath_tx_queue_tid(txq, tid);
 		return;
 	}
 
-	bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+	bf = ath_tx_setup_buffer(sc, txq, tid, skb);
 	if (!bf) {
 		ieee80211_free_txskb(sc->hw, skb);
 		return;
@@ -1724,10 +1726,10 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
 	ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
 
 	/* Queue to h/w without aggregation */
-	TX_STAT_INC(txctl->txq->axq_qnum, a_queued_hw);
+	TX_STAT_INC(txq->axq_qnum, a_queued_hw);
 	bf->bf_lastbf = bf;
-	ath_tx_fill_desc(sc, bf, txctl->txq, fi->framelen);
-	ath_tx_txqaddbuf(sc, txctl->txq, &bf_head, false);
+	ath_tx_fill_desc(sc, bf, txq, fi->framelen);
+	ath_tx_txqaddbuf(sc, txq, &bf_head, false);
 }
 
 static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
@@ -1935,6 +1937,12 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 		txq->stopped = true;
 	}
 
+	if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) {
+		ath_txq_unlock(sc, txq);
+		txq = sc->tx.uapsdq;
+		ath_txq_lock(sc, txq);
+	}
+
 	if (txctl->an && ieee80211_is_data_qos(hdr->frame_control)) {
 		tidno = ieee80211_get_qos_ctl(hdr)[0] &
 			IEEE80211_QOS_CTL_TID_MASK;
@@ -1948,11 +1956,11 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 		 * Try aggregation if it's a unicast data frame
 		 * and the destination is HT capable.
 		 */
-		ath_tx_send_ampdu(sc, tid, skb, txctl);
+		ath_tx_send_ampdu(sc, txq, tid, skb, txctl);
 		goto out;
 	}
 
-	bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+	bf = ath_tx_setup_buffer(sc, txq, tid, skb);
 	if (!bf) {
 		if (txctl->paprd)
 			dev_kfree_skb_any(skb);
@@ -1967,7 +1975,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 		bf->bf_state.bfs_paprd_timestamp = jiffies;
 
 	ath_set_rates(vif, sta, bf);
-	ath_tx_send_normal(sc, txctl->txq, tid, skb);
+	ath_tx_send_normal(sc, txq, tid, skb);
 
 out:
 	ath_txq_unlock(sc, txq);
@@ -2020,7 +2028,12 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
 	}
 	spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 
+	__skb_queue_tail(&txq->complete_q, skb);
+
 	q = skb_get_queue_mapping(skb);
+	if (txq == sc->tx.uapsdq)
+		txq = sc->tx.txq_map[q];
+
 	if (txq == sc->tx.txq_map[q]) {
 		if (WARN_ON(--txq->pending_frames < 0))
 			txq->pending_frames = 0;
@@ -2031,8 +2044,6 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
 			txq->stopped = false;
 		}
 	}
-
-	__skb_queue_tail(&txq->complete_q, skb);
 }
 
 static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
-- 
1.8.0.2


^ permalink raw reply related

* Re: trying to build backports, "input in flex scanner failed"
From: Hauke Mehrtens @ 2013-06-07 15:43 UTC (permalink / raw)
  To: George Nychis; +Cc: linux-wireless@vger.kernel.org
In-Reply-To: <CA+7oygfDcFYaE+=n_5Vygk5SuwwMCtF2SCeeeW-c3dx16E1Uyw@mail.gmail.com>

On 06/07/2013 05:14 PM, George Nychis wrote:
> Hi all,
> 
> I am trying to build backports-3.10-rc1-2 on an Ubuntu 12.04 LTS.
> Whenever I try to start a clean build, I get the error:
> 
> gnychis@parallels:/media/psf/Documents/thesis/backports-3.10-rc1-2$
> make defconfig-rt2x00
> cc -Wall -Wmissing-prototypes -Wstrict-prototypes -O2
> -fomit-frame-pointer   -c -o conf.o conf.c
> cc -Wall -Wmissing-prototypes -Wstrict-prototypes -O2
> -fomit-frame-pointer   -c -o zconf.tab.o zconf.tab.c
> cc   conf.o zconf.tab.o   -o conf
> input in flex scanner failed
> make[1]: *** [defconfig-rt2x00] Error 2
> make: *** [defconfig-rt2x00] Error 2
> 
> I have flex installed.  It's not getting very far.  Does anyone know
> what the issue could be here?

The package does not contain a rt2x00 default config, have you generated
one by yourself? Does this problem also occur when your run make
menuconfig or make defconfig-rtlwifi?

Hauke

^ permalink raw reply

* trying to build backports, "input in flex scanner failed"
From: George Nychis @ 2013-06-07 15:14 UTC (permalink / raw)
  To: linux-wireless@vger.kernel.org

Hi all,

I am trying to build backports-3.10-rc1-2 on an Ubuntu 12.04 LTS.
Whenever I try to start a clean build, I get the error:

gnychis@parallels:/media/psf/Documents/thesis/backports-3.10-rc1-2$
make defconfig-rt2x00
cc -Wall -Wmissing-prototypes -Wstrict-prototypes -O2
-fomit-frame-pointer   -c -o conf.o conf.c
cc -Wall -Wmissing-prototypes -Wstrict-prototypes -O2
-fomit-frame-pointer   -c -o zconf.tab.o zconf.tab.c
cc   conf.o zconf.tab.o   -o conf
input in flex scanner failed
make[1]: *** [defconfig-rt2x00] Error 2
make: *** [defconfig-rt2x00] Error 2

I have flex installed.  It's not getting very far.  Does anyone know
what the issue could be here?

Thanks!

^ permalink raw reply

* Re: [RFC 4/5] mac80211: enforce address verification on monitors
From: Felix Fietkau @ 2013-06-07 14:26 UTC (permalink / raw)
  To: Jakub Kiciński
  Cc: linux-wireless, Helmut Schaa, Johannes Berg,
	nietrywialneprzejscie
In-Reply-To: <20130607160402.49ea16c3@north>

On 2013-06-07 4:04 PM, Jakub Kiciński wrote:
> On Fri, 07 Jun 2013 15:49:39 +0200, Felix Fietkau wrote:
>> On 2013-06-07 3:36 PM, Jakub Kicinski wrote:
>>> From: Jakub Kicinski <kubakici@wp.pl>
>>> 
>>> Mac address of passive monitor have to treated
>>> the same as address any other interface because
>>> type of interface can be changed or monitor can
>>> be later put into active mode.
>> It can't. There's checks preventing that. I'd prefer to leave that part
>> of the code as it is, the MAC address for normal monitor interfaces
>> should be ignored completely.
> 
> I must have misread the code regarding active monitors then.
> Main motivation behind this patch was the first part though
> - user can change type of interface and once ignored address
> of monitor now becomes address of AP, sta etc. 
I'm pretty sure that switching from monitor to something else is also
not supported.

- Felix


^ permalink raw reply

* Re: [RFC 4/5] mac80211: enforce address verification on monitors
From: Jakub Kiciński @ 2013-06-07 14:04 UTC (permalink / raw)
  To: Felix Fietkau
  Cc: linux-wireless, Helmut Schaa, Johannes Berg,
	nietrywialneprzejscie
In-Reply-To: <51B1E4F3.9020509@openwrt.org>

On Fri, 07 Jun 2013 15:49:39 +0200, Felix Fietkau wrote:
> On 2013-06-07 3:36 PM, Jakub Kicinski wrote:
>> From: Jakub Kicinski <kubakici@wp.pl>
>> 
>> Mac address of passive monitor have to treated
>> the same as address any other interface because
>> type of interface can be changed or monitor can
>> be later put into active mode.
> It can't. There's checks preventing that. I'd prefer to leave that part
> of the code as it is, the MAC address for normal monitor interfaces
> should be ignored completely.

I must have misread the code regarding active monitors then.
Main motivation behind this patch was the first part though
- user can change type of interface and once ignored address
of monitor now becomes address of AP, sta etc. 

  -- kuba

^ permalink raw reply

* Re: [RFC 4/5] mac80211: enforce address verification on monitors
From: Felix Fietkau @ 2013-06-07 13:49 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: linux-wireless, Helmut Schaa, Johannes Berg,
	nietrywialneprzejscie
In-Reply-To: <1370612179-24385-5-git-send-email-moorray3@wp.pl>

On 2013-06-07 3:36 PM, Jakub Kicinski wrote:
> From: Jakub Kicinski <kubakici@wp.pl>
> 
> Mac address of passive monitor have to treated
> the same as address any other interface because
> type of interface can be changed or monitor can
> be later put into active mode.
It can't. There's checks preventing that. I'd prefer to leave that part
of the code as it is, the MAC address for normal monitor interfaces
should be ignored completely.

- Felix

^ permalink raw reply

* [RFC 5/5] mac80211: assign right mac addr to active monitors
From: Jakub Kicinski @ 2013-06-07 13:36 UTC (permalink / raw)
  To: linux-wireless
  Cc: Helmut Schaa, Felix Fietkau, Johannes Berg, nietrywialneprzejscie
In-Reply-To: <1370612179-24385-1-git-send-email-moorray3@wp.pl>

From: Jakub Kicinski <kubakici@wp.pl>

Active monitors should have a valid mac
address as they are supposed to be able
to ack incoming frames.

Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
---
 net/mac80211/cfg.c         |  2 +-
 net/mac80211/ieee80211_i.h |  2 +-
 net/mac80211/iface.c       | 12 +++++++-----
 net/mac80211/main.c        |  2 +-
 4 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 3062210..123b35f 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -31,7 +31,7 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
 	struct ieee80211_sub_if_data *sdata;
 	int err;
 
-	err = ieee80211_if_add(local, name, &wdev, type, params);
+	err = ieee80211_if_add(local, name, &wdev, type, flags, params);
 	if (err)
 		return ERR_PTR(err);
 
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 923e177..a2fa214 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1345,7 +1345,7 @@ int ieee80211_iface_init(void);
 void ieee80211_iface_exit(void);
 int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 		     struct wireless_dev **new_wdev, enum nl80211_iftype type,
-		     struct vif_params *params);
+		     u32 *flags, struct vif_params *params);
 int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
 			     enum nl80211_iftype type);
 void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 96bfafa..16fc8283 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1493,7 +1493,8 @@ static void __ieee80211_assign_perm_addr(struct ieee80211_local *local,
 }
 
 static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
-				       u8 *perm_addr, enum nl80211_iftype type)
+				       u8 *perm_addr, enum nl80211_iftype type,
+				       u32 *flags)
 {
 	struct ieee80211_sub_if_data *sdata;
 
@@ -1517,7 +1518,8 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
 
 	switch (type) {
 	case NL80211_IFTYPE_MONITOR:
-		/* doesn't matter */
+		if (flags && (*flags & MONITOR_FLAG_ACTIVE))
+			__ieee80211_assign_perm_addr(local, perm_addr);
 		break;
 	case NL80211_IFTYPE_WDS:
 	case NL80211_IFTYPE_AP_VLAN:
@@ -1563,7 +1565,7 @@ static void ieee80211_cleanup_sdata_stas_wk(struct work_struct *wk)
 
 int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 		     struct wireless_dev **new_wdev, enum nl80211_iftype type,
-		     struct vif_params *params)
+		     u32 *flags, struct vif_params *params)
 {
 	struct net_device *ndev = NULL;
 	struct ieee80211_sub_if_data *sdata = NULL;
@@ -1583,7 +1585,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 
 		sdata->dev = NULL;
 		strlcpy(sdata->name, name, IFNAMSIZ);
-		ieee80211_assign_perm_addr(local, wdev->address, type);
+		ieee80211_assign_perm_addr(local, wdev->address, type, flags);
 		memcpy(sdata->vif.addr, wdev->address, ETH_ALEN);
 	} else {
 		if (local->hw.queues >= IEEE80211_NUM_ACS)
@@ -1611,7 +1613,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 			return ret;
 		}
 
-		ieee80211_assign_perm_addr(local, ndev->perm_addr, type);
+		ieee80211_assign_perm_addr(local, ndev->perm_addr, type, flags);
 		memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
 		SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
 
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 1998f14..edaca0f 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -968,7 +968,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 	/* add one default STA interface if supported */
 	if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
 		result = ieee80211_if_add(local, "wlan%d", NULL,
-					  NL80211_IFTYPE_STATION, NULL);
+					  NL80211_IFTYPE_STATION, NULL, NULL);
 		if (result)
 			wiphy_warn(local->hw.wiphy,
 				   "Failed to add default virtual iface\n");
-- 
1.8.1.4


^ permalink raw reply related

* [RFC 4/5] mac80211: enforce address verification on monitors
From: Jakub Kicinski @ 2013-06-07 13:36 UTC (permalink / raw)
  To: linux-wireless
  Cc: Helmut Schaa, Felix Fietkau, Johannes Berg, nietrywialneprzejscie
In-Reply-To: <1370612179-24385-1-git-send-email-moorray3@wp.pl>

From: Jakub Kicinski <kubakici@wp.pl>

Mac address of passive monitor have to treated
the same as address any other interface because
type of interface can be changed or monitor can
be later put into active mode.

Refusing to change interface type because of
mismatch in mac addresses would likely be
confusing to users.

This requirement only applies to hardware that
sets addr_mask and need to have all addresses
kept within it.

Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
---
 net/mac80211/iface.c | 17 ++---------------
 1 file changed, 2 insertions(+), 15 deletions(-)

diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 1f26980..96bfafa 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -159,8 +159,7 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
 	return 0;
 }
 
-static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr,
-				bool check_dup)
+static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr)
 {
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_sub_if_data *iter;
@@ -181,18 +180,11 @@ static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr,
 		((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
 		((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
 
-	if (!check_dup)
-		return ret;
-
 	mutex_lock(&local->iflist_mtx);
 	list_for_each_entry(iter, &local->interfaces, list) {
 		if (iter == sdata)
 			continue;
 
-		if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
-		    !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
-			continue;
-
 		m = iter->vif.addr;
 		tmp =	((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
 			((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
@@ -212,17 +204,12 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct sockaddr *sa = addr;
-	bool check_dup = true;
 	int ret;
 
 	if (ieee80211_sdata_running(sdata))
 		return -EBUSY;
 
-	if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
-	    !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
-		check_dup = false;
-
-	ret = ieee80211_verify_mac(sdata, sa->sa_data, check_dup);
+	ret = ieee80211_verify_mac(sdata, sa->sa_data);
 	if (ret)
 		return ret;
 
-- 
1.8.1.4


^ permalink raw reply related

* [RFC 1/5] mac80211: introduce __ieee80211_assign_perm_addr
From: Jakub Kicinski @ 2013-06-07 13:36 UTC (permalink / raw)
  To: linux-wireless
  Cc: Helmut Schaa, Felix Fietkau, Johannes Berg, nietrywialneprzejscie
In-Reply-To: <1370612179-24385-1-git-send-email-moorray3@wp.pl>

From: Jakub Kicinski <kubakici@wp.pl>

Move code from the default case of the big switch in
ieee80211_assign_perm_addr into a separate function.
It will be handy later.

No functional changes.

Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
---
 net/mac80211/iface.c | 169 ++++++++++++++++++++++++++-------------------------
 1 file changed, 87 insertions(+), 82 deletions(-)

diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index a7eba16..dbee397 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1425,8 +1425,8 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
 	return 0;
 }
 
-static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
-				       u8 *perm_addr, enum nl80211_iftype type)
+static void __ieee80211_assign_perm_addr(struct ieee80211_local *local,
+					 u8 *perm_addr)
 {
 	struct ieee80211_sub_if_data *sdata;
 	u64 mask, start, addr, val, inc;
@@ -1434,6 +1434,90 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
 	u8 tmp_addr[ETH_ALEN];
 	int i;
 
+	/* assign a new address if possible -- try n_addresses first */
+	for (i = 0; i < local->hw.wiphy->n_addresses; i++) {
+		bool used = false;
+
+		list_for_each_entry(sdata, &local->interfaces, list) {
+			if (memcmp(local->hw.wiphy->addresses[i].addr,
+				   sdata->vif.addr, ETH_ALEN) == 0) {
+				used = true;
+				break;
+			}
+		}
+
+		if (!used) {
+			memcpy(perm_addr, local->hw.wiphy->addresses[i].addr,
+			       ETH_ALEN);
+			break;
+		}
+	}
+
+	/* try mask if available */
+	if (is_zero_ether_addr(local->hw.wiphy->addr_mask))
+		return;
+
+	m = local->hw.wiphy->addr_mask;
+	mask =	((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
+		((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
+		((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
+
+	if (__ffs64(mask) + hweight64(mask) != fls64(mask)) {
+		/* not a contiguous mask ... not handled now! */
+		pr_info("not contiguous\n");
+		return;
+	}
+
+	/*
+	 * Pick address of existing interface in case user changed
+	 * MAC address manually, default to perm_addr.
+	 */
+	m = local->hw.wiphy->perm_addr;
+	list_for_each_entry(sdata, &local->interfaces, list) {
+		if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+			continue;
+		m = sdata->vif.addr;
+		break;
+	}
+	start = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
+		((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
+		((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
+
+	inc = 1ULL<<__ffs64(mask);
+	val = (start & mask);
+	addr = (start & ~mask) | (val & mask);
+	do {
+		bool used = false;
+
+		tmp_addr[5] = addr >> 0*8;
+		tmp_addr[4] = addr >> 1*8;
+		tmp_addr[3] = addr >> 2*8;
+		tmp_addr[2] = addr >> 3*8;
+		tmp_addr[1] = addr >> 4*8;
+		tmp_addr[0] = addr >> 5*8;
+
+		val += inc;
+
+		list_for_each_entry(sdata, &local->interfaces, list) {
+			if (memcmp(tmp_addr, sdata->vif.addr, ETH_ALEN) == 0) {
+				used = true;
+				break;
+			}
+		}
+
+		if (!used) {
+			memcpy(perm_addr, tmp_addr, ETH_ALEN);
+			break;
+		}
+		addr = (start & ~mask) | (val & mask);
+	} while (addr != start);
+}
+
+static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
+				       u8 *perm_addr, enum nl80211_iftype type)
+{
+	struct ieee80211_sub_if_data *sdata;
+
 	/* default ... something at least */
 	memcpy(perm_addr, local->hw.wiphy->perm_addr, ETH_ALEN);
 
@@ -1472,86 +1556,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
 		}
 		/* otherwise fall through */
 	default:
-		/* assign a new address if possible -- try n_addresses first */
-		for (i = 0; i < local->hw.wiphy->n_addresses; i++) {
-			bool used = false;
-
-			list_for_each_entry(sdata, &local->interfaces, list) {
-				if (memcmp(local->hw.wiphy->addresses[i].addr,
-					   sdata->vif.addr, ETH_ALEN) == 0) {
-					used = true;
-					break;
-				}
-			}
-
-			if (!used) {
-				memcpy(perm_addr,
-				       local->hw.wiphy->addresses[i].addr,
-				       ETH_ALEN);
-				break;
-			}
-		}
-
-		/* try mask if available */
-		if (is_zero_ether_addr(local->hw.wiphy->addr_mask))
-			break;
-
-		m = local->hw.wiphy->addr_mask;
-		mask =	((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
-			((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
-			((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
-
-		if (__ffs64(mask) + hweight64(mask) != fls64(mask)) {
-			/* not a contiguous mask ... not handled now! */
-			pr_info("not contiguous\n");
-			break;
-		}
-
-		/*
-		 * Pick address of existing interface in case user changed
-		 * MAC address manually, default to perm_addr.
-		 */
-		m = local->hw.wiphy->perm_addr;
-		list_for_each_entry(sdata, &local->interfaces, list) {
-			if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
-				continue;
-			m = sdata->vif.addr;
-			break;
-		}
-		start = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
-			((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
-			((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
-
-		inc = 1ULL<<__ffs64(mask);
-		val = (start & mask);
-		addr = (start & ~mask) | (val & mask);
-		do {
-			bool used = false;
-
-			tmp_addr[5] = addr >> 0*8;
-			tmp_addr[4] = addr >> 1*8;
-			tmp_addr[3] = addr >> 2*8;
-			tmp_addr[2] = addr >> 3*8;
-			tmp_addr[1] = addr >> 4*8;
-			tmp_addr[0] = addr >> 5*8;
-
-			val += inc;
-
-			list_for_each_entry(sdata, &local->interfaces, list) {
-				if (memcmp(tmp_addr, sdata->vif.addr,
-							ETH_ALEN) == 0) {
-					used = true;
-					break;
-				}
-			}
-
-			if (!used) {
-				memcpy(perm_addr, tmp_addr, ETH_ALEN);
-				break;
-			}
-			addr = (start & ~mask) | (val & mask);
-		} while (addr != start);
-
+		__ieee80211_assign_perm_addr(local, perm_addr);
 		break;
 	}
 
-- 
1.8.1.4


^ permalink raw reply related

* [RFC 3/5] mac80211: always default to address compatible with mask
From: Jakub Kicinski @ 2013-06-07 13:36 UTC (permalink / raw)
  To: linux-wireless
  Cc: Helmut Schaa, Felix Fietkau, Johannes Berg, nietrywialneprzejscie
In-Reply-To: <1370612179-24385-1-git-send-email-moorray3@wp.pl>

From: Jakub Kicinski <kubakici@wp.pl>

Currently default mac address for new interfaces
is set to perm_addr. It can be addr_mask-wise
incompatible with other addresses if user have
changed address of existing interfaces manualy.

This is especially important after introduction
of active monitors. We have to make sure that
monitor interfaces have mac addresses that fall
into addr_mask, otherwise hardware may not
acknowledge frames.

Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
---
 net/mac80211/iface.c | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 38898b3..1f26980 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1468,17 +1468,7 @@ static void __ieee80211_assign_perm_addr(struct ieee80211_local *local,
 		return;
 	}
 
-	/*
-	 * Pick address of existing interface in case user changed
-	 * MAC address manually, default to perm_addr.
-	 */
-	m = local->hw.wiphy->perm_addr;
-	list_for_each_entry(sdata, &local->interfaces, list) {
-		if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
-			continue;
-		m = sdata->vif.addr;
-		break;
-	}
+	m = perm_addr;
 	start = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
 		((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
 		((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
@@ -1529,6 +1519,15 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
 
 	mutex_lock(&local->iflist_mtx);
 
+	/* After manual mac change default addr may no longer fit into mask. */
+	if (!is_zero_ether_addr(local->hw.wiphy->addr_mask) &&
+	    !list_empty(&local->interfaces)) {
+		sdata = list_first_entry(&local->interfaces,
+					 struct ieee80211_sub_if_data,
+					 list);
+		memcpy(perm_addr, sdata->vif.addr, ETH_ALEN);
+	}
+
 	switch (type) {
 	case NL80211_IFTYPE_MONITOR:
 		/* doesn't matter */
-- 
1.8.1.4


^ permalink raw reply related

* [RFC 2/5] mac80211: return new mac address as soon as possible
From: Jakub Kicinski @ 2013-06-07 13:36 UTC (permalink / raw)
  To: linux-wireless
  Cc: Helmut Schaa, Felix Fietkau, Johannes Berg, nietrywialneprzejscie
In-Reply-To: <1370612179-24385-1-git-send-email-moorray3@wp.pl>

From: Jakub Kicinski <kubakici@wp.pl>

Once one of wiphy->addresses is found to be unused
we can return from __ieee80211_assign_perm_addr and
use it. The same for masked addresses.

Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
---
 net/mac80211/iface.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index dbee397..38898b3 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1449,7 +1449,7 @@ static void __ieee80211_assign_perm_addr(struct ieee80211_local *local,
 		if (!used) {
 			memcpy(perm_addr, local->hw.wiphy->addresses[i].addr,
 			       ETH_ALEN);
-			break;
+			return;
 		}
 	}
 
@@ -1507,10 +1507,12 @@ static void __ieee80211_assign_perm_addr(struct ieee80211_local *local,
 
 		if (!used) {
 			memcpy(perm_addr, tmp_addr, ETH_ALEN);
-			break;
+			return;
 		}
 		addr = (start & ~mask) | (val & mask);
 	} while (addr != start);
+
+	pr_debug("no free address found - using default\n");
 }
 
 static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
-- 
1.8.1.4


^ permalink raw reply related

* [RFC 0/5] Fix mac addr enforcement on monitor interfaces
From: Jakub Kicinski @ 2013-06-07 13:36 UTC (permalink / raw)
  To: linux-wireless
  Cc: Helmut Schaa, Felix Fietkau, Johannes Berg, nietrywialneprzejscie

From: Jakub Kicinski <kubakici@wp.pl>

It's still possible for users to hurt themselves 
by setting incompatible mac addresses on hardware
that needs conformance to addr_mask. This series
aims to fix that and improve address assignment to
active monitors.

Unfortunately there is a merge conflict in mac80211.git
and mac80211-next.git between:

ac20976dca mac80211: Allow single vif mac address change with addr_mask
and
31eba5bc56 mac80211: support active monitor interfaces

so I got confused and based the series on
wireless-testing.git. Is this acceptable?

Jakub Kicinski (5):
  mac80211: introduce __ieee80211_assign_perm_addr
  mac80211: return new mac address as soon as possible
  mac80211: always default to address compatible with mask
  mac80211: enforce address verification on monitors
  mac80211: assign right mac addr to active monitors

 net/mac80211/cfg.c         |   2 +-
 net/mac80211/ieee80211_i.h |   2 +-
 net/mac80211/iface.c       | 197 ++++++++++++++++++++++-----------------------
 net/mac80211/main.c        |   2 +-
 4 files changed, 99 insertions(+), 104 deletions(-)

-- 
1.8.1.4


^ permalink raw reply


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