* Re: [PATCH 06/10] mac80211: add HW flag for disabling auto link-PS in AP mode
From: Johannes Berg @ 2011-01-16 8:50 UTC (permalink / raw)
To: Arik Nemtsov; +Cc: linux-wireless, Luciano Coelho
In-Reply-To: <1295156534-4178-7-git-send-email-arik@wizery.com>
On Sun, 2011-01-16 at 07:42 +0200, Arik Nemtsov wrote:
> When operating in AP mode the wl1271 hardware filters our null-data
> packets as well as management packets. This makes it impossible for
> mac80211 to monitor the PS mode by using the PM bit of incoming frames.
>
> Implement a HW flag to indicate that mac80211 should ignore the PM bit.
> In addition, expose ieee80211_start_ps()/ieee80211_end_ps() to make
> low-level drivers capable of controlling PS-mode.
>
> Signed-off-by: Arik Nemtsov <arik@wizery.com>
> ---
> include/net/mac80211.h | 38 ++++++++++++++++++++++++++++++++++++++
> net/mac80211/rx.c | 19 ++++++++++++++++++-
> 2 files changed, 56 insertions(+), 1 deletions(-)
>
> diff --git a/include/net/mac80211.h b/include/net/mac80211.h
> index 62c0ce2..26a8cfb 100644
> --- a/include/net/mac80211.h
> +++ b/include/net/mac80211.h
> @@ -1069,6 +1069,13 @@ enum ieee80211_tkip_key_type {
> * to decrypt group addressed frames, then IBSS RSN support is still
> * possible but software crypto will be used. Advertise the wiphy flag
> * only in that case.
> + *
> + * @IEEE80211_HW_AP_LINK_PS: When operating in AP mode the device
> + * autonomously manages the PS status of connected stations. When
> + * this flag is set mac80211 will not trigger PS mode for connected
> + * stations based on the PM bit of incoming frames.
> + * Use ieee80211_start_ps()/ieee8021_end_ps() to manually configure
> + * the PS mode of connected stations.
> */
> enum ieee80211_hw_flags {
> IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
> @@ -1093,6 +1100,7 @@ enum ieee80211_hw_flags {
> IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
> IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<20,
> IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21,
> + IEEE80211_HW_AP_LINK_PS = 1<<22,
> };
>
> /**
> @@ -2113,6 +2121,36 @@ static inline void ieee80211_rx_ni(struct ieee80211_hw *hw,
> local_bh_enable();
> }
>
> +/**
> + * ieee80211_start_ps - start PS for connected sta
> + *
> + * When operating in AP mode, use this function to inform mac80211
> + * about a station entering PS mode.
> + * Note that by default mac80211 will automatically call the internal
> + * equivalent of this function according to the PM bit of incoming frames.
> + * Use the IEEE80211_HW_AP_LINK_PS flag to change this.
I think that explanation should be clearer and say that you must call
this only when the flag is set and vice versa. Also, there should be a
WARN_ON(hw->flags & HW_AP_LINK_PS)
in the functions.
Also, maybe just a single function with a bool argument would be more
appropriate? Call it "ieee80211_sta_ps_transition()" or something like
that. You should also remove the text about calling the internal
equivalent -- the internal details might change at any time but the API
should be documented in a way that makes sense without the internal
details for the benefit of both sides -- people implementing a driver
don't need to know about the internal details, and people changing the
internal details know what semantics to keep.
> + * This function may not be called in IRQ context or process context.
This can't be true -- the latter part you must mean "or with softirqs
enabled".
Also, I'm worried about races between this and RX. All of the RX path
assumes that it is running at the same time. The ap_sta_ps_{start,end}
functions won't be called from the RX path when the flag is set, and
this is dependent on setting the flag, but is there really nothing in
them that could race?
Finally, I'm worried about this calling back into the driver's
sta_notify callback. If that is to remain that way, it needs to be
*very* clearly documented, I'd *much* prefer it not doing that but
handing off to a work struct or so internally.
johannes
^ permalink raw reply
* Re: [PATCH 04/10] mac80211: do not calc frame duration when using HW rate-control
From: Johannes Berg @ 2011-01-16 8:34 UTC (permalink / raw)
To: Arik Nemtsov; +Cc: linux-wireless, Luciano Coelho
In-Reply-To: <1295156534-4178-5-git-send-email-arik@wizery.com>
On Sun, 2011-01-16 at 07:42 +0200, Arik Nemtsov wrote:
> When rate-control is performed in HW, we cannot calculate frame
> duration as we do not have the skb transmission rate in SW.
>
> ieee80211_tx_h_calculate_duration() should only be called when
> ieee80211_tx_h_rate_ctrl() has been called before to initialize data
> in skb->cb. This doesn't happen for drivers with HW rate-control.
>
> Fixes the following warning when operating in AP-mode
> in a driver with HW rate-control.
>
> <4>[16747.586029] WARNING: at net/mac80211/tx.c:57 ieee80211_duration
> +0x54/0x1d8 [mac80211]()
> <4>[16747.594085] Modules linked in: wl12xx_sdio wl12xx firmware_class crc7
> mac80211 cfg80211 [last unloaded: crc7]
> <4>[16747.604095] [<c004707c>] (unwind_backtrace+0x0/0x124) from [<c006644c>]
> (warn_slowpath_common+0x4c/0x64)
> <4>[16747.613616] [<c006644c>] (warn_slowpath_common+0x4c/0x64) from
> [<c006647c>] (warn_slowpath_null+0x18/0x1c)
> <4>[16747.623382] [<c006647c>] (warn_slowpath_null+0x18/0x1c) from [<bf1aa8e8>]
> (ieee80211_duration+0x54/0x1d8 [mac80211])
> <4>[16747.634124] [<bf1aa8e8>] (ieee80211_duration+0x54/0x1d8 [mac80211]) from
> [<bf1aacec>] (ieee80211_tx_h_calculate_duration+0x3c/0x5c [mac80211])
> <4>[16747.647094] [<bf1aacec>] (ieee80211_tx_h_calculate_duration+0x3c/0x5c
> [mac80211]) from [<bf1ab21c>] (invoke_tx_handlers+0xdc/0x118 [mac80211])
> <4>[16747.660064] [<bf1ab21c>] (invoke_tx_handlers+0xdc/0x118 [mac80211])
> from [<bf1ab2dc>] (ieee80211_tx+0x84/0x248 [mac80211])
> <4>[16747.671295] [<bf1ab2dc>] (ieee80211_tx+0x84/0x248 [mac80211]) from
> [<bf1abe3c>] (ieee80211_tx_pending+0x12c/0x278 [mac80211])
> <4>[16747.682739] [<bf1abe3c>] (ieee80211_tx_pending+0x12c/0x278 [mac80211])
> from [<c006b3bc>] (tasklet_action+0x68/0xc0)
>
> Signed-off-by: Arik Nemtsov <arik@wizery.com>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
though I wish the backtrace in your commit log wasn't all mangled and
word-wrapped.
johannes
> ---
> net/mac80211/tx.c | 3 ++-
> 1 files changed, 2 insertions(+), 1 deletions(-)
>
> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
> index 5950e3a..e0d6d5c 100644
> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -1394,7 +1394,8 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
> /* handlers after fragment must be aware of tx info fragmentation! */
> CALL_TXH(ieee80211_tx_h_stats);
> CALL_TXH(ieee80211_tx_h_encrypt);
> - CALL_TXH(ieee80211_tx_h_calculate_duration);
> + if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL))
> + CALL_TXH(ieee80211_tx_h_calculate_duration);
> #undef CALL_TXH
>
> txh_done:
^ permalink raw reply
* Re: [PATCH] wl12xx: fix some sparse warnings
From: Johannes Berg @ 2011-01-16 8:28 UTC (permalink / raw)
To: Eliad Peller; +Cc: Luciano Coelho, linux-wireless
In-Reply-To: <1295164184-25592-1-git-send-email-eliad@wizery.com>
On Sun, 2011-01-16 at 09:49 +0200, Eliad Peller wrote:
> @@ -813,7 +813,7 @@ int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
> acx->rate_policy.long_retry_limit = c->long_retry_limit;
> acx->rate_policy.aflags = c->aflags;
>
> - acx->rate_policy_idx = idx;
> + acx->rate_policy_idx = __cpu_to_le32(idx);
The underscore version doesn't look appropriate at this point or in fact
anywhere in this patch.
Also, your patch subject and commit log is wrong -- this doesn't just
fix sparse warnings, it fixes actual bugs pointed out by sparse, so
commit log should reflect that.
johannes
^ permalink raw reply
* [PATCH] wl12xx: fix some sparse warnings
From: Eliad Peller @ 2011-01-16 7:49 UTC (permalink / raw)
To: Luciano Coelho; +Cc: linux-wireless
fix the following warnings:
CHECK drivers/net/wireless/wl12xx/cmd.c
drivers/net/wireless/wl12xx/cmd.c:987:20: warning: incorrect type in assignment (different base types)
drivers/net/wireless/wl12xx/cmd.c:987:20: expected restricted __le16 [usertype] aging_period
drivers/net/wireless/wl12xx/cmd.c:987:20: got int
CHECK drivers/net/wireless/wl12xx/tx.c
drivers/net/wireless/wl12xx/tx.c:197:2: warning: cast from restricted __le16
drivers/net/wireless/wl12xx/tx.c:197:2: warning: cast from restricted __le16
drivers/net/wireless/wl12xx/tx.c:197:2: warning: cast from restricted __le16
CHECK drivers/net/wireless/wl12xx/acx.c
drivers/net/wireless/wl12xx/acx.c:816:23: warning: incorrect type in assignment (different base types)
drivers/net/wireless/wl12xx/acx.c:816:23: expected restricted __le32 [usertype] rate_policy_idx
drivers/net/wireless/wl12xx/acx.c:816:23: got unsigned char [unsigned] [usertype] idx
Signed-off-by: Eliad Peller <eliad@wizery.com>
---
drivers/net/wireless/wl12xx/acx.c | 2 +-
drivers/net/wireless/wl12xx/cmd.c | 2 +-
drivers/net/wireless/wl12xx/tx.c | 6 +++---
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c
index 646d278..df73a21 100644
--- a/drivers/net/wireless/wl12xx/acx.c
+++ b/drivers/net/wireless/wl12xx/acx.c
@@ -813,7 +813,7 @@ int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
acx->rate_policy.long_retry_limit = c->long_retry_limit;
acx->rate_policy.aflags = c->aflags;
- acx->rate_policy_idx = idx;
+ acx->rate_policy_idx = __cpu_to_le32(idx);
ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx));
if (ret < 0) {
diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c
index e28d9ca..6ce188d 100644
--- a/drivers/net/wireless/wl12xx/cmd.c
+++ b/drivers/net/wireless/wl12xx/cmd.c
@@ -984,7 +984,7 @@ int wl1271_cmd_start_bss(struct wl1271 *wl)
memcpy(cmd->bssid, bss_conf->bssid, ETH_ALEN);
- cmd->aging_period = WL1271_AP_DEF_INACTIV_SEC;
+ cmd->aging_period = __cpu_to_le16(WL1271_AP_DEF_INACTIV_SEC);
cmd->bss_index = WL1271_AP_BSS_INDEX;
cmd->global_hlid = WL1271_AP_GLOBAL_HLID;
cmd->broadcast_hlid = WL1271_AP_BROADCAST_HLID;
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index 2347f25..209d4db 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -195,9 +195,9 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
desc->tx_attr = cpu_to_le16(tx_attr);
wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d hlid: %d "
- "tx_attr: 0x%x len: %d life: %d mem: %d", pad, (int)desc->hlid,
- (int)desc->tx_attr, (int)desc->length, (int)desc->life_time,
- (int)desc->total_mem_blocks);
+ "tx_attr: 0x%x len: %d life: %d mem: %d", pad, desc->hlid,
+ __le16_to_cpu(desc->tx_attr), __le16_to_cpu(desc->length),
+ __le16_to_cpu(desc->life_time), desc->total_mem_blocks);
}
/* caller must hold wl->mutex */
--
1.7.0.4
^ permalink raw reply related
* [PATCH] ath9k_htc: abort tx/rx on fast channel change
From: Rajkumar Manoharan @ 2011-01-16 6:19 UTC (permalink / raw)
To: linux-wireless; +Cc: Rajkumar Manoharan
Abort Tx/Rx on fast channel change before hw reset.
This helps to avoid timeout on loading noise floor
values after channel change.
Signed-off-by: Rajkumar Manoharan <rmanoharan@atheros.com>
---
drivers/net/wireless/ath/ath9k/htc_drv_main.c | 17 ++++++++++++++---
1 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index 187af5b..d374867 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -193,8 +193,6 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
ath9k_htc_ps_wakeup(priv);
htc_stop(priv->htc);
WMI_CMD(WMI_DISABLE_INTR_CMDID);
- WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
- WMI_CMD(WMI_STOP_RECV_CMDID);
ath_dbg(common, ATH_DBG_CONFIG,
"(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
@@ -202,8 +200,21 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
fastcc);
- if (!fastcc)
+ if (fastcc) {
+ /* abort pending tx frames */
+ WMI_CMD(WMI_ABORT_TX_DMA_CMDID);
+ WMI_CMD(WMI_ABORT_TXQ_CMDID);
+
+ /* Clear receive filter */
+ ath9k_hw_setrxfilter(ah, 0);
+
+ WMI_CMD(WMI_STOP_DMA_RECV_CMDID);
+ WMI_CMD(WMI_RX_LINK_CMDID);
+ } else {
+ WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
+ WMI_CMD(WMI_STOP_RECV_CMDID);
caldata = &priv->caldata;
+ }
ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
if (ret) {
ath_err(common,
--
1.7.3.5
^ permalink raw reply related
* [PATCH 10/10] wl12xx: AP-mode - management of links in PS-mode
From: Arik Nemtsov @ 2011-01-16 5:42 UTC (permalink / raw)
To: linux-wireless; +Cc: Luciano Coelho, Johannes Berg, Arik Nemtsov
In-Reply-To: <1295156534-4178-1-git-send-email-arik@wizery.com>
Update the PS mode of each link according to a bitmap polled from
fw_status. Manually notify mac80211 about PS mode changes in connected
stations.
mac80211 will only be notified about PS start when the station is in PS
and there is a small number of TX blocks from this link ready in HW.
This is required for waking up the remote station since the TIM is
updated entirely by FW.
When a station enters mac80211-PS-mode, we drop all the skbs in the
low-level TX queues belonging to this sta with STAT_TX_FILTERED
to keep our queues clean.
Signed-off-by: Arik Nemtsov <arik@wizery.com>
---
drivers/net/wireless/wl12xx/main.c | 73 ++++++++++++++++++++++++++-----
drivers/net/wireless/wl12xx/ps.c | 80 ++++++++++++++++++++++++++++++++++
drivers/net/wireless/wl12xx/ps.h | 2 +
drivers/net/wireless/wl12xx/tx.c | 24 ++++++++++-
drivers/net/wireless/wl12xx/wl12xx.h | 19 ++++++++
5 files changed, 186 insertions(+), 12 deletions(-)
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index a83d26d..d362a30 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -518,6 +518,57 @@ static int wl1271_plt_init(struct wl1271 *wl)
return ret;
}
+static void wl1271_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_blks)
+{
+ bool fw_ps;
+
+ /* only regulate station links */
+ if (hlid < WL1271_AP_STA_HLID_START)
+ return;
+
+ fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+
+ /*
+ * Wake up from high level PS if the STA is asleep with too little
+ * blocks in FW or if the STA is awake.
+ */
+ if (!fw_ps || tx_blks < WL1271_PS_STA_MAX_BLOCKS)
+ wl1271_ps_link_end(wl, hlid);
+
+ /* Start high-level PS if the STA is asleep with enough blocks in FW */
+ else if (fw_ps && tx_blks >= WL1271_PS_STA_MAX_BLOCKS)
+ wl1271_ps_link_start(wl, hlid, true);
+}
+
+static void wl1271_irq_update_links_status(struct wl1271 *wl,
+ struct wl1271_fw_status *status)
+{
+ u32 cur_fw_ps_map;
+ u8 hlid;
+
+ cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
+ if (wl->ap_fw_ps_map != cur_fw_ps_map) {
+ wl1271_debug(DEBUG_PSM,
+ "link ps prev 0x%x cur 0x%x changed 0x%x",
+ wl->ap_fw_ps_map, cur_fw_ps_map,
+ wl->ap_fw_ps_map ^ cur_fw_ps_map);
+
+ wl->ap_fw_ps_map = cur_fw_ps_map;
+ }
+
+ for (hlid = WL1271_AP_STA_HLID_START; hlid < AP_MAX_LINKS; hlid++) {
+ u8 cnt = status->tx_lnk_free_blks[hlid] -
+ wl->links[hlid].prev_freed_blks;
+
+ wl->links[hlid].prev_freed_blks =
+ status->tx_lnk_free_blks[hlid];
+ wl->links[hlid].allocated_blks -= cnt;
+
+ wl1271_irq_ps_regulate_link(wl, hlid,
+ wl->links[hlid].allocated_blks);
+ }
+}
+
static void wl1271_fw_status(struct wl1271 *wl,
struct wl1271_fw_status *status)
{
@@ -549,16 +600,9 @@ static void wl1271_fw_status(struct wl1271 *wl,
if (total)
clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
- if (wl->bss_type == BSS_TYPE_AP_BSS) {
- for (i = 0; i < AP_MAX_LINKS; i++) {
- u8 cnt = status->tx_lnk_free_blks[i] -
- wl->links[i].prev_freed_blks;
-
- wl->links[i].prev_freed_blks =
- status->tx_lnk_free_blks[i];
- wl->links[i].allocated_blks -= cnt;
- }
- }
+ /* for AP update num of allocated TX blocks per link and ps status */
+ if (wl->bss_type == BSS_TYPE_AP_BSS)
+ wl1271_irq_update_links_status(wl, status);
/* update the host-chipset time offset */
getnstimeofday(&ts);
@@ -1239,6 +1283,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
wl->filters = 0;
wl1271_free_ap_keys(wl);
memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
+ wl->ap_fw_ps_map = 0;
+ wl->ap_ps_map = 0;
for (i = 0; i < NUM_TX_QUEUES; i++)
wl->tx_blocks_freed[i] = 0;
@@ -2636,10 +2682,10 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
}
wl_sta = (struct wl1271_station *)sta->drv_priv;
-
__set_bit(id, wl->ap_hlid_map);
wl_sta->hlid = WL1271_AP_STA_HLID_START + id;
*hlid = wl_sta->hlid;
+ memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
return 0;
}
@@ -2651,7 +2697,10 @@ static void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
return;
__clear_bit(id, wl->ap_hlid_map);
+ memset(wl->links[hlid].addr, 0, ETH_ALEN);
wl1271_tx_reset_link_queues(wl, hlid);
+ __clear_bit(hlid, &wl->ap_ps_map);
+ __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
}
static int wl1271_op_sta_add(struct ieee80211_hw *hw,
@@ -3275,6 +3324,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->set_bss_type = MAX_BSS_TYPE;
wl->fw_bss_type = MAX_BSS_TYPE;
wl->last_tx_hlid = 0;
+ wl->ap_ps_map = 0;
+ wl->ap_fw_ps_map = 0;
memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c
index 92397ad..eb80192 100644
--- a/drivers/net/wireless/wl12xx/ps.c
+++ b/drivers/net/wireless/wl12xx/ps.c
@@ -24,6 +24,7 @@
#include "reg.h"
#include "ps.h"
#include "io.h"
+#include "tx.h"
#define WL1271_WAKEUP_TIMEOUT 500
@@ -174,3 +175,82 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
return ret;
}
+
+static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
+{
+ int i, filtered = 0;
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *info;
+ unsigned long flags;
+
+ /* filter all frames currently the low level queus for this hlid */
+ for (i = 0; i < NUM_TX_QUEUES; i++) {
+ while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
+ info = IEEE80211_SKB_CB(skb);
+ info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+ info->status.rates[0].idx = -1;
+ ieee80211_tx_status(wl->hw, skb);
+ filtered++;
+ }
+ }
+
+ spin_lock_irqsave(&wl->wl_lock, flags);
+ wl->tx_queue_count -= filtered;
+ spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+ wl1271_handle_tx_low_watermark(wl);
+}
+
+void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues)
+{
+ struct ieee80211_sta *sta;
+
+ if (test_bit(hlid, &wl->ap_ps_map))
+ return;
+
+ wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d blks %d "
+ "clean_queues %d", hlid, wl->links[hlid].allocated_blks,
+ clean_queues);
+
+ rcu_read_lock();
+ sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr);
+ if (!sta) {
+ wl1271_error("could not find sta %pM for starting ps",
+ wl->links[hlid].addr);
+ rcu_read_unlock();
+ return;
+ }
+
+ ieee80211_start_ps(sta);
+ rcu_read_unlock();
+
+ /* do we want to filter all frames from this link's queues? */
+ if (clean_queues)
+ wl1271_ps_filter_frames(wl, hlid);
+
+ __set_bit(hlid, &wl->ap_ps_map);
+}
+
+void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid)
+{
+ struct ieee80211_sta *sta;
+
+ if (!test_bit(hlid, &wl->ap_ps_map))
+ return;
+
+ wl1271_debug(DEBUG_PSM, "end mac80211 PSM on hlid %d", hlid);
+
+ __clear_bit(hlid, &wl->ap_ps_map);
+
+ rcu_read_lock();
+ sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr);
+ if (!sta) {
+ wl1271_error("could not find sta %pM for ending ps",
+ wl->links[hlid].addr);
+ goto end;
+ }
+
+ ieee80211_end_ps(sta);
+end:
+ rcu_read_unlock();
+}
diff --git a/drivers/net/wireless/wl12xx/ps.h b/drivers/net/wireless/wl12xx/ps.h
index 8415060..fc1f4c1 100644
--- a/drivers/net/wireless/wl12xx/ps.h
+++ b/drivers/net/wireless/wl12xx/ps.h
@@ -32,5 +32,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
void wl1271_ps_elp_sleep(struct wl1271 *wl);
int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake);
void wl1271_elp_work(struct work_struct *work);
+void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues);
+void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid);
#endif /* __WL1271_PS_H__ */
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index 128c274..e515b72 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -86,6 +86,26 @@ static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
}
+static void wl1271_tx_regulate_link(struct wl1271 *wl, u8 hlid)
+{
+ bool fw_ps;
+ u8 tx_blks;
+
+ /* only regulate station links */
+ if (hlid < WL1271_AP_STA_HLID_START)
+ return;
+
+ fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+ tx_blks = wl->links[hlid].allocated_blks;
+
+ /*
+ * if in FW PS and there is enough data in FW we can put the link
+ * into high-level PS and clean out its TX queues.
+ */
+ if (fw_ps && tx_blks >= WL1271_PS_STA_MAX_BLOCKS)
+ wl1271_ps_link_start(wl, hlid, true);
+}
+
u8 wl1271_tx_get_hlid(struct sk_buff *skb)
{
struct ieee80211_tx_info *control = IEEE80211_SKB_CB(skb);
@@ -277,8 +297,10 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
if (ret < 0)
return ret;
- if (wl->bss_type == BSS_TYPE_AP_BSS)
+ if (wl->bss_type == BSS_TYPE_AP_BSS) {
wl1271_tx_ap_update_inconnection_sta(wl, skb);
+ wl1271_tx_regulate_link(wl, hlid);
+ }
wl1271_tx_fill_hdr(wl, skb, extra, info, hlid);
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index 59d687b..7cb3d5b 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -146,6 +146,17 @@ extern u32 wl12xx_debug_level;
#define WL1271_AP_BROADCAST_HLID 1
#define WL1271_AP_STA_HLID_START 2
+/*
+ * When in AP-mode, we allow (at least) this number of mem-blocks
+ * to be transmitted to FW for a STA in PS-mode. Only when packets are
+ * present in the FW buffers it will wake the sleeping STA. We want to put
+ * enough packets for the driver to transmit all of its buffered data before
+ * the STA goes to sleep again. But we don't want to take too much mem-blocks
+ * as it might hurt the throughput of active STAs.
+ * The number of blocks (18) is enough for 2 large packets.
+ */
+#define WL1271_PS_STA_MAX_BLOCKS (2 * 9)
+
#define WL1271_AP_BSS_INDEX 0
#define WL1271_AP_DEF_INACTIV_SEC 300
#define WL1271_AP_DEF_BEACON_EXP 20
@@ -275,6 +286,8 @@ struct wl1271_link {
/* accounting for allocated / available TX blocks in FW */
u8 allocated_blks;
u8 prev_freed_blks;
+
+ u8 addr[ETH_ALEN];
};
struct wl1271 {
@@ -478,6 +491,12 @@ struct wl1271 {
/* the hlid of the link where the last transmitted skb came from */
int last_tx_hlid;
+
+ /* AP-mode - a bitmap of links currently in PS mode according to FW */
+ u32 ap_fw_ps_map;
+
+ /* AP-mode - a bitmap of links currently in PS mode in mac80211 */
+ unsigned long ap_ps_map;
};
struct wl1271_station {
--
1.7.1
^ permalink raw reply related
* [PATCH 09/10] wl12xx: AP-mode - count free FW TX blocks per link
From: Arik Nemtsov @ 2011-01-16 5:42 UTC (permalink / raw)
To: linux-wireless; +Cc: Luciano Coelho, Johannes Berg, Arik Nemtsov
In-Reply-To: <1295156534-4178-1-git-send-email-arik@wizery.com>
Count the number of FW TX blocks allocated per link. We add blocks to a
link counter when allocated for a TX descriptor. We remove blocks
according to counters in fw_status indicating the number of freed blocks
in FW. These counters are polled after each IRQ.
Signed-off-by: Arik Nemtsov <arik@wizery.com>
---
drivers/net/wireless/wl12xx/main.c | 11 +++++++
drivers/net/wireless/wl12xx/ps.c | 2 -
drivers/net/wireless/wl12xx/tx.c | 53 ++++++++++++++++++---------------
drivers/net/wireless/wl12xx/wl12xx.h | 4 ++
4 files changed, 44 insertions(+), 26 deletions(-)
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index d600cff..a83d26d 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -549,6 +549,17 @@ static void wl1271_fw_status(struct wl1271 *wl,
if (total)
clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
+ if (wl->bss_type == BSS_TYPE_AP_BSS) {
+ for (i = 0; i < AP_MAX_LINKS; i++) {
+ u8 cnt = status->tx_lnk_free_blks[i] -
+ wl->links[i].prev_freed_blks;
+
+ wl->links[i].prev_freed_blks =
+ status->tx_lnk_free_blks[i];
+ wl->links[i].allocated_blks -= cnt;
+ }
+ }
+
/* update the host-chipset time offset */
getnstimeofday(&ts);
wl->time_offset = (timespec_to_ns(&ts) >> 10) -
diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c
index 60a3738..92397ad 100644
--- a/drivers/net/wireless/wl12xx/ps.c
+++ b/drivers/net/wireless/wl12xx/ps.c
@@ -174,5 +174,3 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
return ret;
}
-
-
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index d43193e..128c274 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -108,7 +108,7 @@ u8 wl1271_tx_get_hlid(struct sk_buff *skb)
}
static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
- u32 buf_offset)
+ u32 buf_offset, u8 hlid)
{
struct wl1271_tx_hw_descr *desc;
u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra;
@@ -137,6 +137,9 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
wl->tx_blocks_available -= total_blocks;
+ if (wl->bss_type == BSS_TYPE_AP_BSS)
+ wl->links[hlid].allocated_blks += total_blocks;
+
ret = 0;
wl1271_debug(DEBUG_TX,
@@ -150,7 +153,8 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
}
static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
- u32 extra, struct ieee80211_tx_info *control)
+ u32 extra, struct ieee80211_tx_info *control,
+ u8 hlid)
{
struct timespec ts;
struct wl1271_tx_hw_descr *desc;
@@ -186,7 +190,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
desc->tid = ac;
if (wl->bss_type != BSS_TYPE_AP_BSS) {
- desc->aid = TX_HW_DEFAULT_AID;
+ desc->aid = hlid;
/* if the packets are destined for AP (have a STA entry)
send them with AP rate policies, otherwise use default
@@ -196,25 +200,17 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
else
rate_idx = ACX_TX_BASIC_RATE;
} else {
- if (control->control.sta) {
- struct wl1271_station *wl_sta;
-
- wl_sta = (struct wl1271_station *)
- control->control.sta->drv_priv;
- desc->hlid = wl_sta->hlid;
+ desc->hlid = hlid;
+ switch (hlid) {
+ case WL1271_AP_GLOBAL_HLID:
+ rate_idx = ACX_TX_AP_MODE_MGMT_RATE;
+ break;
+ case WL1271_AP_BROADCAST_HLID:
+ rate_idx = ACX_TX_AP_MODE_BCST_RATE;
+ break;
+ default:
rate_idx = ac;
- } else {
- struct ieee80211_hdr *hdr;
-
- hdr = (struct ieee80211_hdr *)
- (skb->data + sizeof(*desc));
- if (ieee80211_is_mgmt(hdr->frame_control)) {
- desc->hlid = WL1271_AP_GLOBAL_HLID;
- rate_idx = ACX_TX_AP_MODE_MGMT_RATE;
- } else {
- desc->hlid = WL1271_AP_BROADCAST_HLID;
- rate_idx = ACX_TX_AP_MODE_BCST_RATE;
- }
+ break;
}
}
@@ -245,6 +241,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
u32 extra = 0;
int ret = 0;
u32 total_len;
+ u8 hlid;
if (!skb)
return -EINVAL;
@@ -271,14 +268,19 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
}
}
- ret = wl1271_tx_allocate(wl, skb, extra, buf_offset);
+ if (wl->bss_type == BSS_TYPE_AP_BSS)
+ hlid = wl1271_tx_get_hlid(skb);
+ else
+ hlid = TX_HW_DEFAULT_AID;
+
+ ret = wl1271_tx_allocate(wl, skb, extra, buf_offset, hlid);
if (ret < 0)
return ret;
if (wl->bss_type == BSS_TYPE_AP_BSS)
wl1271_tx_ap_update_inconnection_sta(wl, skb);
- wl1271_tx_fill_hdr(wl, skb, extra, info);
+ wl1271_tx_fill_hdr(wl, skb, extra, info, hlid);
/*
* The length of each packet is stored in terms of words. Thus, we must
@@ -656,8 +658,11 @@ void wl1271_tx_reset(struct wl1271 *wl)
/* TX failure */
if (wl->bss_type == BSS_TYPE_AP_BSS) {
- for (i = 0; i < AP_MAX_LINKS; i++)
+ for (i = 0; i < AP_MAX_LINKS; i++) {
wl1271_tx_reset_link_queues(wl, i);
+ wl->links[i].allocated_blks = 0;
+ wl->links[i].prev_freed_blks = 0;
+ }
wl->last_tx_hlid = 0;
} else {
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index 0027e00..59d687b 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -271,6 +271,10 @@ struct wl1271_ap_key {
struct wl1271_link {
/* AP-mode - TX queue per AC in link */
struct sk_buff_head tx_queue[NUM_TX_QUEUES];
+
+ /* accounting for allocated / available TX blocks in FW */
+ u8 allocated_blks;
+ u8 prev_freed_blks;
};
struct wl1271 {
--
1.7.1
^ permalink raw reply related
* [PATCH 08/10] wl12xx: AP mode - fix bug in cleanup of wl1271_op_sta_add()
From: Arik Nemtsov @ 2011-01-16 5:42 UTC (permalink / raw)
To: linux-wireless; +Cc: Luciano Coelho, Johannes Berg, Arik Nemtsov
In-Reply-To: <1295156534-4178-1-git-send-email-arik@wizery.com>
Remove an active hlid when chip wakeup fails. In addition rename the
involved functions.
Signed-off-by: Arik Nemtsov <arik@wizery.com>
---
drivers/net/wireless/wl12xx/main.c | 17 ++++++++++++-----
1 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 77c8974..d600cff 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -2611,7 +2611,7 @@ static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
return 0;
}
-static int wl1271_allocate_hlid(struct wl1271 *wl,
+static int wl1271_allocate_sta(struct wl1271 *wl,
struct ieee80211_sta *sta,
u8 *hlid)
{
@@ -2632,10 +2632,13 @@ static int wl1271_allocate_hlid(struct wl1271 *wl,
return 0;
}
-static void wl1271_free_hlid(struct wl1271 *wl, u8 hlid)
+static void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
{
int id = hlid - WL1271_AP_STA_HLID_START;
+ if (WARN_ON(!test_bit(id, wl->ap_hlid_map)))
+ return;
+
__clear_bit(id, wl->ap_hlid_map);
wl1271_tx_reset_link_queues(wl, hlid);
}
@@ -2658,13 +2661,13 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw,
wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
- ret = wl1271_allocate_hlid(wl, sta, &hlid);
+ ret = wl1271_allocate_sta(wl, sta, &hlid);
if (ret < 0)
goto out;
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
- goto out;
+ goto out_free_sta;
ret = wl1271_cmd_add_sta(wl, sta, hlid);
if (ret < 0)
@@ -2673,6 +2676,10 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw,
out_sleep:
wl1271_ps_elp_sleep(wl);
+out_free_sta:
+ if (ret < 0)
+ wl1271_free_sta(wl, hlid);
+
out:
mutex_unlock(&wl->mutex);
return ret;
@@ -2709,7 +2716,7 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
if (ret < 0)
goto out_sleep;
- wl1271_free_hlid(wl, wl_sta->hlid);
+ wl1271_free_sta(wl, wl_sta->hlid);
out_sleep:
wl1271_ps_elp_sleep(wl);
--
1.7.1
^ permalink raw reply related
* [PATCH 07/10] wl12xx: AP-mode - support HW based link PS monitoring
From: Arik Nemtsov @ 2011-01-16 5:42 UTC (permalink / raw)
To: linux-wireless; +Cc: Luciano Coelho, Johannes Berg, Arik Nemtsov
In-Reply-To: <1295156534-4178-1-git-send-email-arik@wizery.com>
When operating in AP mode the wl1271 hardware filters our null-data
packets as well as management packets. This makes it impossible for
mac80211 to monitor the PS mode by using the PM bit of incoming frames.
Disable mac80211 automatic link PS-mode handling by supporting
IEEE80211_HW_AP_LINK_PS in HW flags.
Signed-off-by: Arik Nemtsov <arik@wizery.com>
---
drivers/net/wireless/wl12xx/main.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 6318224..77c8974 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -3147,7 +3147,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
IEEE80211_HW_SUPPORTS_UAPSD |
IEEE80211_HW_HAS_RATE_CONTROL |
IEEE80211_HW_CONNECTION_MONITOR |
- IEEE80211_HW_SUPPORTS_CQM_RSSI;
+ IEEE80211_HW_SUPPORTS_CQM_RSSI |
+ IEEE80211_HW_AP_LINK_PS;
wl->hw->wiphy->cipher_suites = cipher_suites;
wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
--
1.7.1
^ permalink raw reply related
* [PATCH 06/10] mac80211: add HW flag for disabling auto link-PS in AP mode
From: Arik Nemtsov @ 2011-01-16 5:42 UTC (permalink / raw)
To: linux-wireless; +Cc: Luciano Coelho, Johannes Berg, Arik Nemtsov
In-Reply-To: <1295156534-4178-1-git-send-email-arik@wizery.com>
When operating in AP mode the wl1271 hardware filters our null-data
packets as well as management packets. This makes it impossible for
mac80211 to monitor the PS mode by using the PM bit of incoming frames.
Implement a HW flag to indicate that mac80211 should ignore the PM bit.
In addition, expose ieee80211_start_ps()/ieee80211_end_ps() to make
low-level drivers capable of controlling PS-mode.
Signed-off-by: Arik Nemtsov <arik@wizery.com>
---
include/net/mac80211.h | 38 ++++++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 19 ++++++++++++++++++-
2 files changed, 56 insertions(+), 1 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 62c0ce2..26a8cfb 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1069,6 +1069,13 @@ enum ieee80211_tkip_key_type {
* to decrypt group addressed frames, then IBSS RSN support is still
* possible but software crypto will be used. Advertise the wiphy flag
* only in that case.
+ *
+ * @IEEE80211_HW_AP_LINK_PS: When operating in AP mode the device
+ * autonomously manages the PS status of connected stations. When
+ * this flag is set mac80211 will not trigger PS mode for connected
+ * stations based on the PM bit of incoming frames.
+ * Use ieee80211_start_ps()/ieee8021_end_ps() to manually configure
+ * the PS mode of connected stations.
*/
enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
@@ -1093,6 +1100,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<20,
IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21,
+ IEEE80211_HW_AP_LINK_PS = 1<<22,
};
/**
@@ -2113,6 +2121,36 @@ static inline void ieee80211_rx_ni(struct ieee80211_hw *hw,
local_bh_enable();
}
+/**
+ * ieee80211_start_ps - start PS for connected sta
+ *
+ * When operating in AP mode, use this function to inform mac80211
+ * about a station entering PS mode.
+ * Note that by default mac80211 will automatically call the internal
+ * equivalent of this function according to the PM bit of incoming frames.
+ * Use the IEEE80211_HW_AP_LINK_PS flag to change this.
+ *
+ * This function may not be called in IRQ context or process context.
+ *
+ * @sta: currently connected sta
+ */
+void ieee80211_start_ps(struct ieee80211_sta *sta);
+
+/**
+ * ieee80211_end_ps - end PS for connection sta
+ *
+ * When operating in AP mode, use this function to inform mac80211
+ * about a station exiting PS mode.
+ * Note that by default mac80211 will automatically call the internal
+ * equivalent of this function according to the PM bit of incoming frames.
+ * Use the IEEE80211_HW_AP_LINK_PS flag to change this.
+ *
+ * This function may not be called in IRQ context or process context.
+ *
+ * @sta: currently connected sta
+ */
+void ieee80211_end_ps(struct ieee80211_sta *sta);
+
/*
* The TX headroom reserved by mac80211 for its own tx_status functions.
* This is enough for the radiotap header.
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index a6701ed..c892271 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1095,6 +1095,14 @@ static void ap_sta_ps_start(struct sta_info *sta)
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
}
+void ieee80211_start_ps(struct ieee80211_sta *sta)
+{
+ struct sta_info *sta_inf = container_of(sta, struct sta_info, sta);
+
+ ap_sta_ps_start(sta_inf);
+}
+EXPORT_SYMBOL(ieee80211_start_ps);
+
static void ap_sta_ps_end(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
@@ -1117,6 +1125,14 @@ static void ap_sta_ps_end(struct sta_info *sta)
ieee80211_sta_ps_deliver_wakeup(sta);
}
+void ieee80211_end_ps(struct ieee80211_sta *sta)
+{
+ struct sta_info *sta_inf = container_of(sta, struct sta_info, sta);
+
+ ap_sta_ps_end(sta_inf);
+}
+EXPORT_SYMBOL(ieee80211_end_ps);
+
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
{
@@ -1161,7 +1177,8 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
* Change STA power saving mode only at the end of a frame
* exchange sequence.
*/
- if (!ieee80211_has_morefrags(hdr->frame_control) &&
+ if (!(sta->local->hw.flags & IEEE80211_HW_AP_LINK_PS) &&
+ !ieee80211_has_morefrags(hdr->frame_control) &&
!(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) &&
(rx->sdata->vif.type == NL80211_IFTYPE_AP ||
rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) {
--
1.7.1
^ permalink raw reply related
* [PATCH 05/10] wl12xx: report invalid TX rate when returning non-TX-ed skbs
From: Arik Nemtsov @ 2011-01-16 5:42 UTC (permalink / raw)
To: linux-wireless; +Cc: Luciano Coelho, Johannes Berg, Arik Nemtsov
In-Reply-To: <1295156534-4178-1-git-send-email-arik@wizery.com>
Report a TX rate idx of -1 when returning untransmitted skbs to mac80211
using ieee80211_tx_status(). Otherwise mac80211 tries to use the
returned (essentially garbage) status.
Signed-off-by: Arik Nemtsov <arik@wizery.com>
---
drivers/net/wireless/wl12xx/tx.c | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index b7fd840..d43193e 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -628,10 +628,13 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
struct sk_buff *skb;
int i, total = 0;
unsigned long flags;
+ struct ieee80211_tx_info *info;
for (i = 0; i < NUM_TX_QUEUES; i++) {
while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb);
+ info = IEEE80211_SKB_CB(skb);
+ info->status.rates[0].idx = -1;
ieee80211_tx_status(wl->hw, skb);
total++;
}
@@ -649,6 +652,7 @@ void wl1271_tx_reset(struct wl1271 *wl)
{
int i;
struct sk_buff *skb;
+ struct ieee80211_tx_info *info;
/* TX failure */
if (wl->bss_type == BSS_TYPE_AP_BSS) {
@@ -661,6 +665,8 @@ void wl1271_tx_reset(struct wl1271 *wl)
while ((skb = skb_dequeue(&wl->tx_queue[i]))) {
wl1271_debug(DEBUG_TX, "freeing skb 0x%p",
skb);
+ info = IEEE80211_SKB_CB(skb);
+ info->status.rates[0].idx = -1;
ieee80211_tx_status(wl->hw, skb);
}
}
@@ -679,6 +685,8 @@ void wl1271_tx_reset(struct wl1271 *wl)
skb = wl->tx_frames[i];
wl1271_free_tx_id(wl, i);
wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb);
+ info = IEEE80211_SKB_CB(skb);
+ info->status.rates[0].idx = -1;
ieee80211_tx_status(wl->hw, skb);
}
}
--
1.7.1
^ permalink raw reply related
* [PATCH 04/10] mac80211: do not calc frame duration when using HW rate-control
From: Arik Nemtsov @ 2011-01-16 5:42 UTC (permalink / raw)
To: linux-wireless; +Cc: Luciano Coelho, Johannes Berg, Arik Nemtsov
In-Reply-To: <1295156534-4178-1-git-send-email-arik@wizery.com>
When rate-control is performed in HW, we cannot calculate frame
duration as we do not have the skb transmission rate in SW.
ieee80211_tx_h_calculate_duration() should only be called when
ieee80211_tx_h_rate_ctrl() has been called before to initialize data
in skb->cb. This doesn't happen for drivers with HW rate-control.
Fixes the following warning when operating in AP-mode
in a driver with HW rate-control.
<4>[16747.586029] WARNING: at net/mac80211/tx.c:57 ieee80211_duration
+0x54/0x1d8 [mac80211]()
<4>[16747.594085] Modules linked in: wl12xx_sdio wl12xx firmware_class crc7
mac80211 cfg80211 [last unloaded: crc7]
<4>[16747.604095] [<c004707c>] (unwind_backtrace+0x0/0x124) from [<c006644c>]
(warn_slowpath_common+0x4c/0x64)
<4>[16747.613616] [<c006644c>] (warn_slowpath_common+0x4c/0x64) from
[<c006647c>] (warn_slowpath_null+0x18/0x1c)
<4>[16747.623382] [<c006647c>] (warn_slowpath_null+0x18/0x1c) from [<bf1aa8e8>]
(ieee80211_duration+0x54/0x1d8 [mac80211])
<4>[16747.634124] [<bf1aa8e8>] (ieee80211_duration+0x54/0x1d8 [mac80211]) from
[<bf1aacec>] (ieee80211_tx_h_calculate_duration+0x3c/0x5c [mac80211])
<4>[16747.647094] [<bf1aacec>] (ieee80211_tx_h_calculate_duration+0x3c/0x5c
[mac80211]) from [<bf1ab21c>] (invoke_tx_handlers+0xdc/0x118 [mac80211])
<4>[16747.660064] [<bf1ab21c>] (invoke_tx_handlers+0xdc/0x118 [mac80211])
from [<bf1ab2dc>] (ieee80211_tx+0x84/0x248 [mac80211])
<4>[16747.671295] [<bf1ab2dc>] (ieee80211_tx+0x84/0x248 [mac80211]) from
[<bf1abe3c>] (ieee80211_tx_pending+0x12c/0x278 [mac80211])
<4>[16747.682739] [<bf1abe3c>] (ieee80211_tx_pending+0x12c/0x278 [mac80211])
from [<c006b3bc>] (tasklet_action+0x68/0xc0)
Signed-off-by: Arik Nemtsov <arik@wizery.com>
---
net/mac80211/tx.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 5950e3a..e0d6d5c 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1394,7 +1394,8 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
/* handlers after fragment must be aware of tx info fragmentation! */
CALL_TXH(ieee80211_tx_h_stats);
CALL_TXH(ieee80211_tx_h_encrypt);
- CALL_TXH(ieee80211_tx_h_calculate_duration);
+ if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL))
+ CALL_TXH(ieee80211_tx_h_calculate_duration);
#undef CALL_TXH
txh_done:
--
1.7.1
^ permalink raw reply related
* [PATCH 03/10] wl12xx: AP-mode - TX queue per link in AC
From: Arik Nemtsov @ 2011-01-16 5:42 UTC (permalink / raw)
To: linux-wireless; +Cc: Luciano Coelho, Johannes Berg, Arik Nemtsov
In-Reply-To: <1295156534-4178-1-git-send-email-arik@wizery.com>
When operating in AP-mode we require a per link tx-queue.
This allows us to implement HW assisted PS mode for links,
as well as regulate per-link FW TX blocks consumption.
Split each link into ACs to support future QoS for AP-mode.
AC queues are emptied in priority and per-link queues are
scheduled in a simple round-robin fashion.
Signed-off-by: Arik Nemtsov <arik@wizery.com>
---
drivers/net/wireless/wl12xx/main.c | 17 ++++-
drivers/net/wireless/wl12xx/tx.c | 130 +++++++++++++++++++++++++++++++---
drivers/net/wireless/wl12xx/tx.h | 3 +
drivers/net/wireless/wl12xx/wl12xx.h | 14 ++++
4 files changed, 151 insertions(+), 13 deletions(-)
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 6163167..6318224 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -955,6 +955,7 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
struct ieee80211_sta *sta = txinfo->control.sta;
unsigned long flags;
int q;
+ u8 hlid = 0;
/*
* peek into the rates configured in the STA entry.
@@ -999,7 +1000,13 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
/* queue the packet */
q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
- skb_queue_tail(&wl->tx_queue[q], skb);
+ if (wl->bss_type == BSS_TYPE_AP_BSS) {
+ hlid = wl1271_tx_get_hlid(skb);
+ wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q);
+ skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
+ } else {
+ skb_queue_tail(&wl->tx_queue[q], skb);
+ }
/*
* The chip specific setup must run before the first TX packet -
@@ -2630,6 +2637,7 @@ static void wl1271_free_hlid(struct wl1271 *wl, u8 hlid)
int id = hlid - WL1271_AP_STA_HLID_START;
__clear_bit(id, wl->ap_hlid_map);
+ wl1271_tx_reset_link_queues(wl, hlid);
}
static int wl1271_op_sta_add(struct ieee80211_hw *hw,
@@ -3189,7 +3197,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
struct ieee80211_hw *hw;
struct platform_device *plat_dev = NULL;
struct wl1271 *wl;
- int i, ret;
+ int i, j, ret;
unsigned int order;
hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
@@ -3217,6 +3225,10 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
for (i = 0; i < NUM_TX_QUEUES; i++)
skb_queue_head_init(&wl->tx_queue[i]);
+ for (i = 0; i < NUM_TX_QUEUES; i++)
+ for (j = 0; j < AP_MAX_LINKS; j++)
+ skb_queue_head_init(&wl->links[j].tx_queue[i]);
+
INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work);
INIT_WORK(&wl->irq_work, wl1271_irq_work);
@@ -3243,6 +3255,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->bss_type = MAX_BSS_TYPE;
wl->set_bss_type = MAX_BSS_TYPE;
wl->fw_bss_type = MAX_BSS_TYPE;
+ wl->last_tx_hlid = 0;
memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index d3ba27c..b7fd840 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -86,6 +86,27 @@ static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
}
+u8 wl1271_tx_get_hlid(struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *control = IEEE80211_SKB_CB(skb);
+
+ if (control->control.sta) {
+ struct wl1271_station *wl_sta;
+
+ wl_sta = (struct wl1271_station *)
+ control->control.sta->drv_priv;
+ return wl_sta->hlid;
+ } else {
+ struct ieee80211_hdr *hdr;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ if (ieee80211_is_mgmt(hdr->frame_control))
+ return WL1271_AP_GLOBAL_HLID;
+ else
+ return WL1271_AP_BROADCAST_HLID;
+ }
+}
+
static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
u32 buf_offset)
{
@@ -298,7 +319,7 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
return enabled_rates;
}
-static void handle_tx_low_watermark(struct wl1271 *wl)
+void wl1271_handle_tx_low_watermark(struct wl1271 *wl)
{
unsigned long flags;
@@ -312,7 +333,7 @@ static void handle_tx_low_watermark(struct wl1271 *wl)
}
}
-static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
+static struct sk_buff *wl1271_sta_skb_dequeue(struct wl1271 *wl)
{
struct sk_buff *skb = NULL;
unsigned long flags;
@@ -338,12 +359,69 @@ out:
return skb;
}
+static struct sk_buff *wl1271_ap_skb_dequeue(struct wl1271 *wl)
+{
+ struct sk_buff *skb = NULL;
+ unsigned long flags;
+ int i, h, start_hlid;
+
+ /* start from the link after the last one */
+ start_hlid = (wl->last_tx_hlid + 1) % AP_MAX_LINKS;
+
+ /* dequeue according to AC, round robin on each link */
+ for (i = 0; i < AP_MAX_LINKS; i++) {
+ h = (start_hlid + i) % AP_MAX_LINKS;
+
+ skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_VO]);
+ if (skb)
+ goto out;
+ skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_VI]);
+ if (skb)
+ goto out;
+ skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_BE]);
+ if (skb)
+ goto out;
+ skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_BK]);
+ if (skb)
+ goto out;
+ }
+
+out:
+ if (skb) {
+ wl->last_tx_hlid = h;
+ spin_lock_irqsave(&wl->wl_lock, flags);
+ wl->tx_queue_count--;
+ spin_unlock_irqrestore(&wl->wl_lock, flags);
+ } else {
+ wl->last_tx_hlid = 0;
+ }
+
+ return skb;
+}
+
+static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
+{
+ if (wl->bss_type == BSS_TYPE_AP_BSS)
+ return wl1271_ap_skb_dequeue(wl);
+
+ return wl1271_sta_skb_dequeue(wl);
+}
+
static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb)
{
unsigned long flags;
int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
- skb_queue_head(&wl->tx_queue[q], skb);
+ if (wl->bss_type == BSS_TYPE_AP_BSS) {
+ u8 hlid = wl1271_tx_get_hlid(skb);
+ skb_queue_head(&wl->links[hlid].tx_queue[q], skb);
+
+ /* make sure we dequeue the same packet next time */
+ wl->last_tx_hlid = (hlid + AP_MAX_LINKS - 1) % AP_MAX_LINKS;
+ } else {
+ skb_queue_head(&wl->tx_queue[q], skb);
+ }
+
spin_lock_irqsave(&wl->wl_lock, flags);
wl->tx_queue_count++;
spin_unlock_irqrestore(&wl->wl_lock, flags);
@@ -428,7 +506,7 @@ out_ack:
if (sent_packets) {
/* interrupt the firmware with the new packets */
wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count);
- handle_tx_low_watermark(wl);
+ wl1271_handle_tx_low_watermark(wl);
}
out:
@@ -545,6 +623,27 @@ void wl1271_tx_complete(struct wl1271 *wl)
}
}
+void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
+{
+ struct sk_buff *skb;
+ int i, total = 0;
+ unsigned long flags;
+
+ for (i = 0; i < NUM_TX_QUEUES; i++) {
+ while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
+ wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb);
+ ieee80211_tx_status(wl->hw, skb);
+ total++;
+ }
+ }
+
+ spin_lock_irqsave(&wl->wl_lock, flags);
+ wl->tx_queue_count -= total;
+ spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+ wl1271_handle_tx_low_watermark(wl);
+}
+
/* caller must hold wl->mutex */
void wl1271_tx_reset(struct wl1271 *wl)
{
@@ -552,19 +651,28 @@ void wl1271_tx_reset(struct wl1271 *wl)
struct sk_buff *skb;
/* TX failure */
- for (i = 0; i < NUM_TX_QUEUES; i++) {
- while ((skb = skb_dequeue(&wl->tx_queue[i]))) {
- wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb);
- ieee80211_tx_status(wl->hw, skb);
+ if (wl->bss_type == BSS_TYPE_AP_BSS) {
+ for (i = 0; i < AP_MAX_LINKS; i++)
+ wl1271_tx_reset_link_queues(wl, i);
+
+ wl->last_tx_hlid = 0;
+ } else {
+ for (i = 0; i < NUM_TX_QUEUES; i++) {
+ while ((skb = skb_dequeue(&wl->tx_queue[i]))) {
+ wl1271_debug(DEBUG_TX, "freeing skb 0x%p",
+ skb);
+ ieee80211_tx_status(wl->hw, skb);
+ }
}
}
+
wl->tx_queue_count = 0;
/*
* Make sure the driver is at a consistent state, in case this
* function is called from a context other than interface removal.
*/
- handle_tx_low_watermark(wl);
+ wl1271_handle_tx_low_watermark(wl);
for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
if (wl->tx_frames[i] != NULL) {
@@ -585,8 +693,8 @@ void wl1271_tx_flush(struct wl1271 *wl)
while (!time_after(jiffies, timeout)) {
mutex_lock(&wl->mutex);
- wl1271_debug(DEBUG_TX, "flushing tx buffer: %d",
- wl->tx_frames_cnt);
+ wl1271_debug(DEBUG_TX, "flushing tx buffer: %d %d",
+ wl->tx_frames_cnt, wl->tx_queue_count);
if ((wl->tx_frames_cnt == 0) && (wl->tx_queue_count == 0)) {
mutex_unlock(&wl->mutex);
return;
diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h
index 05722a5..76df5d5 100644
--- a/drivers/net/wireless/wl12xx/tx.h
+++ b/drivers/net/wireless/wl12xx/tx.h
@@ -152,5 +152,8 @@ void wl1271_tx_flush(struct wl1271 *wl);
u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set);
u32 wl1271_tx_min_rate_get(struct wl1271 *wl);
+u8 wl1271_tx_get_hlid(struct sk_buff *skb);
+void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid);
+void wl1271_handle_tx_low_watermark(struct wl1271 *wl);
#endif
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index cf5c1a5..0027e00 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -268,6 +268,11 @@ struct wl1271_ap_key {
u16 tx_seq_16;
};
+struct wl1271_link {
+ /* AP-mode - TX queue per AC in link */
+ struct sk_buff_head tx_queue[NUM_TX_QUEUES];
+};
+
struct wl1271 {
struct platform_device *plat_dev;
struct ieee80211_hw *hw;
@@ -460,6 +465,15 @@ struct wl1271 {
/* bands supported by this instance of wl12xx */
struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
+
+ /*
+ * AP-mode - links indexed by HLID. The global and broadcast links
+ * are always active.
+ */
+ struct wl1271_link links[AP_MAX_LINKS];
+
+ /* the hlid of the link where the last transmitted skb came from */
+ int last_tx_hlid;
};
struct wl1271_station {
--
1.7.1
^ permalink raw reply related
* [PATCH 02/10] wl12xx: AP-mode - fix race condition on sta connection
From: Arik Nemtsov @ 2011-01-16 5:42 UTC (permalink / raw)
To: linux-wireless; +Cc: Luciano Coelho, Johannes Berg, Arik Nemtsov
In-Reply-To: <1295156534-4178-1-git-send-email-arik@wizery.com>
If a sta starts transmitting immediately after authentication, sometimes
the FW deauthenticates it. Fix this by marking the sta "in-connection"
in FW before sending the autentication response.
Signed-off-by: Arik Nemtsov <arik@wizery.com>
---
drivers/net/wireless/wl12xx/acx.c | 25 +++++++++++++++++++++++++
drivers/net/wireless/wl12xx/acx.h | 9 +++++++++
drivers/net/wireless/wl12xx/tx.c | 19 +++++++++++++++++++
3 files changed, 53 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c
index 646d278..1b5a371 100644
--- a/drivers/net/wireless/wl12xx/acx.c
+++ b/drivers/net/wireless/wl12xx/acx.c
@@ -1391,3 +1391,28 @@ out:
kfree(acx);
return ret;
}
+
+int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr)
+{
+ struct wl1271_acx_inconnection_sta *acx = NULL;
+ int ret;
+
+ wl1271_debug(DEBUG_ACX, "acx set inconnaction sta %pM", addr);
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx)
+ return -ENOMEM;
+
+ memcpy(acx->addr, addr, ETH_ALEN);
+
+ ret = wl1271_cmd_configure(wl, ACX_UPDATE_INCONNECTION_STA_LIST,
+ acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("acx set inconnaction sta failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h
index 89d3748..4ea3378 100644
--- a/drivers/net/wireless/wl12xx/acx.h
+++ b/drivers/net/wireless/wl12xx/acx.h
@@ -1083,6 +1083,13 @@ struct wl1271_acx_max_tx_retry {
u8 padding_1[2];
} __packed;
+struct wl1271_acx_inconnection_sta {
+ struct acx_header header;
+
+ u8 addr[ETH_ALEN];
+ u8 padding1[2];
+} __packed;
+
enum {
ACX_WAKE_UP_CONDITIONS = 0x0002,
ACX_MEM_CFG = 0x0003,
@@ -1141,6 +1148,7 @@ enum {
ACX_COEX_ACTIVITY = 0x0059,
ACX_SET_DCO_ITRIM_PARAMS = 0x0061,
ACX_MAX_TX_FAILURE = 0x0072,
+ ACX_UPDATE_INCONNECTION_STA_LIST = 0x0073,
DOT11_RX_MSDU_LIFE_TIME = 0x1004,
DOT11_CUR_TX_PWR = 0x100D,
DOT11_RX_DOT11_MODE = 0x1012,
@@ -1211,5 +1219,6 @@ int wl1271_acx_set_ht_information(struct wl1271 *wl,
u16 ht_operation_mode);
int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
int wl1271_acx_max_tx_retry(struct wl1271 *wl);
+int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
#endif /* __WL1271_ACX_H__ */
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index fdec4a3..d3ba27c 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -70,6 +70,22 @@ static void wl1271_free_tx_id(struct wl1271 *wl, int id)
}
}
+static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr;
+
+ /*
+ * add the station to the known list before broadcasting the
+ * authentication response. this way it won't get de-authed by FW
+ * when transmitting too soon.
+ */
+ hdr = (struct ieee80211_hdr *)(skb->data +
+ sizeof(struct wl1271_tx_hw_descr));
+ if (ieee80211_is_auth(hdr->frame_control))
+ wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
+}
+
static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
u32 buf_offset)
{
@@ -238,6 +254,9 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
if (ret < 0)
return ret;
+ if (wl->bss_type == BSS_TYPE_AP_BSS)
+ wl1271_tx_ap_update_inconnection_sta(wl, skb);
+
wl1271_tx_fill_hdr(wl, skb, extra, info);
/*
--
1.7.1
^ permalink raw reply related
* [PATCH 01/10] wl12xx: fix potential race condition with TX queue watermark
From: Arik Nemtsov @ 2011-01-16 5:42 UTC (permalink / raw)
To: linux-wireless; +Cc: Luciano Coelho, Johannes Berg, Arik Nemtsov
In-Reply-To: <1295156534-4178-1-git-send-email-arik@wizery.com>
Check the conditions for the high/low TX queue watermarks when the
spin-lock is taken. This prevents race conditions as tx_queue_count and
the flag checked are only modified when the spin-lock is taken.
The following race was in mind:
- Queues are almost full and wl1271_op_tx() will stop the queues, but it
doesn't get the spin-lock yet.
- (on another CPU) tx_work_locked() dequeues 15 skbs from this queue and
tx_queue_count is updated to reflect this
- wl1271_op_tx() does not check tx_queue_count after taking the
spin-lock and incorrectly stops the queue.
Signed-off-by: Arik Nemtsov <arik@wizery.com>
---
drivers/net/wireless/wl12xx/main.c | 24 +++++++++++-------------
drivers/net/wireless/wl12xx/tx.c | 2 +-
2 files changed, 12 insertions(+), 14 deletions(-)
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 9076555..6163167 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -984,6 +984,17 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
}
#endif
wl->tx_queue_count++;
+
+ /*
+ * The workqueue is slow to process the tx_queue and we need stop
+ * the queue here, otherwise the queue will get too long.
+ */
+ if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
+ wl1271_debug(DEBUG_TX, "op_tx: stopping queues");
+ ieee80211_stop_queues(wl->hw);
+ __set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
+ }
+
spin_unlock_irqrestore(&wl->wl_lock, flags);
/* queue the packet */
@@ -998,19 +1009,6 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
ieee80211_queue_work(wl->hw, &wl->tx_work);
- /*
- * The workqueue is slow to process the tx_queue and we need stop
- * the queue here, otherwise the queue will get too long.
- */
- if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
- wl1271_debug(DEBUG_TX, "op_tx: stopping queues");
-
- spin_lock_irqsave(&wl->wl_lock, flags);
- ieee80211_stop_queues(wl->hw);
- set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
- spin_unlock_irqrestore(&wl->wl_lock, flags);
- }
-
return NETDEV_TX_OK;
}
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index 2347f25..fdec4a3 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -288,7 +288,7 @@ static void handle_tx_low_watermark(struct wl1271 *wl)
/* firmware buffer has space, restart queues */
spin_lock_irqsave(&wl->wl_lock, flags);
ieee80211_wake_queues(wl->hw);
- clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
+ __clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
spin_unlock_irqrestore(&wl->wl_lock, flags);
}
}
--
1.7.1
^ permalink raw reply related
* [PATCH 00/10] wl12xx: AP-mode per link PSM
From: Arik Nemtsov @ 2011-01-16 5:42 UTC (permalink / raw)
To: linux-wireless; +Cc: Luciano Coelho, Johannes Berg, Arik Nemtsov
This patchset introduces per-link PSM for AP-mode. The PS status of each
link is updated from FW.
The wl12xx concept of PSM is different from the mac80211 one - some data
must remain in FW for a link in PSM since the TIM is updated automatically
by FW.
Exported functions to manually toggle PSM in mac80211 are added
to bridge this gap.
We set up a skb-queue per link which allows for a cleaner link
disconnection and prepares the code for regulating each link separately.
A few other AP-mode related small bug fixes and enchancements are included.
Arik Nemtsov (10):
wl12xx: fix potential race condition with TX queue watermark
wl12xx: AP-mode - fix race condition on sta connection
wl12xx: AP-mode - TX queue per link in AC
mac80211: do not calc frame duration when using HW rate-control
wl12xx: report invalid TX rate when returning non-TX-ed skbs
mac80211: add HW flag for disabling auto link-PS in AP mode
wl12xx: AP-mode - support HW based link PS monitoring
wl12xx: AP mode - fix bug in cleanup of wl1271_op_sta_add()
wl12xx: AP-mode - count free FW TX blocks per link
wl12xx: AP-mode - management of links in PS-mode
drivers/net/wireless/wl12xx/acx.c | 25 ++++
drivers/net/wireless/wl12xx/acx.h | 9 ++
drivers/net/wireless/wl12xx/main.c | 125 +++++++++++++++----
drivers/net/wireless/wl12xx/ps.c | 78 ++++++++++++
drivers/net/wireless/wl12xx/ps.h | 2 +
drivers/net/wireless/wl12xx/tx.c | 232 +++++++++++++++++++++++++++++-----
drivers/net/wireless/wl12xx/tx.h | 3 +
drivers/net/wireless/wl12xx/wl12xx.h | 37 ++++++
include/net/mac80211.h | 38 ++++++
net/mac80211/rx.c | 19 +++-
net/mac80211/tx.c | 3 +-
11 files changed, 512 insertions(+), 59 deletions(-)
^ permalink raw reply
* [PATCH] ath9k_htc: Fix endian issue in tx header
From: Rajkumar Manoharan @ 2011-01-16 5:26 UTC (permalink / raw)
To: linux-wireless; +Cc: Rajkumar Manoharan
Signed-off-by: Rajkumar Manoharan <rmanoharan@atheros.com>
---
drivers/net/wireless/ath/ath9k/htc.h | 2 +-
drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 8 +++++---
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index c976600..6354986 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -78,7 +78,7 @@ struct tx_frame_hdr {
u8 node_idx;
u8 vif_idx;
u8 tidno;
- u32 flags; /* ATH9K_HTC_TX_* */
+ __be32 flags; /* ATH9K_HTC_TX_* */
u8 key_type;
u8 keyix;
u8 reserved[26];
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index 33f3602..7a5ffca 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -113,6 +113,7 @@ int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, struct sk_buff *skb)
if (ieee80211_is_data(fc)) {
struct tx_frame_hdr tx_hdr;
+ u32 flags = 0;
u8 *qc;
memset(&tx_hdr, 0, sizeof(struct tx_frame_hdr));
@@ -136,13 +137,14 @@ int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, struct sk_buff *skb)
/* Check for RTS protection */
if (priv->hw->wiphy->rts_threshold != (u32) -1)
if (skb->len > priv->hw->wiphy->rts_threshold)
- tx_hdr.flags |= ATH9K_HTC_TX_RTSCTS;
+ flags |= ATH9K_HTC_TX_RTSCTS;
/* CTS-to-self */
- if (!(tx_hdr.flags & ATH9K_HTC_TX_RTSCTS) &&
+ if (!(flags & ATH9K_HTC_TX_RTSCTS) &&
(priv->op_flags & OP_PROTECT_ENABLE))
- tx_hdr.flags |= ATH9K_HTC_TX_CTSONLY;
+ flags |= ATH9K_HTC_TX_CTSONLY;
+ tx_hdr.flags = cpu_to_be32(flags);
tx_hdr.key_type = ath9k_cmn_get_hw_crypto_keytype(skb);
if (tx_hdr.key_type == ATH9K_KEY_TYPE_CLEAR)
tx_hdr.keyix = (u8) ATH9K_TXKEYIX_INVALID;
--
1.7.3.5
^ permalink raw reply related
* Re: BUG in rt2x00lib_txdone() with 2.6.37-rc8
From: Ingo Brunberg @ 2011-01-16 2:58 UTC (permalink / raw)
To: Ivo Van Doorn; +Cc: helmut.schaa, linux-kernel, linux-wireless
In-Reply-To: <AANLkTimMVdsGONSD9OmnX2eJk51H5Pzj4K8irHa=E-qx@mail.gmail.com>
Ivo Van Doorn <ivdoorn@gmail.com> writes:
> This could be, would be interesting to know if compat-wireless also shows
> this problem. Because the queue refactoring code which should have solved
> these race conditions was added after 2.6.37.
I really would like to give it a try, but compat-wireless-2011-01-15
crashes right on module loading. Is there a version known to work?
^ permalink raw reply
* Re: BUG in rt2x00lib_txdone() with 2.6.37-rc8
From: Helmut Schaa @ 2011-01-16 1:59 UTC (permalink / raw)
To: Ivo Van Doorn; +Cc: Ingo Brunberg, linux-kernel, linux-wireless
In-Reply-To: <AANLkTimMVdsGONSD9OmnX2eJk51H5Pzj4K8irHa=E-qx@mail.gmail.com>
Am Samstag, 15. Januar 2011 schrieb Ivo Van Doorn:
> Hi,
>
> > Just a shot in the dark but since the stack trace shows the newly added
> > watchdog this might be the result of a race between a regular txdone work
> > (mac80211 workqueue) vs the watchdog work (global workqueue).
> >
> > I guess the following situation could happen:
> > A regular tx done work calls rt2x00lib_txdone which first sets entry->skb to
> > NULL, calls the driver specific clear_entry and afterwards increases
> > Q_INDEX_DONE. If the watchdog work calls rt2x00lib_txdone on a different CPU
> > inbetween the skb might be NULL and cause the above oops.
>
> This could be, would be interesting to know if compat-wireless also shows
> this problem. Because the queue refactoring code which should have solved
> these race conditions was added after 2.6.37.
I also guess that this issue would be fixed in compat-wireless due to the queue
refactoring. But I guess that is way too big for a stable kernel :(
Helmut
^ permalink raw reply
* Re: [Driver] NEC WL300NU-G
From: u2 @ 2011-01-15 23:38 UTC (permalink / raw)
To: sedat.dilek; +Cc: Sedat Dilek, Gertjan van Wingerde, linux-wireless
In-Reply-To: <AANLkTincDG82pmYAixgQnMjPJvc4EWPS2jzU=mKX2PxH@mail.gmail.com>
-------- Message --------:
>> I will wait for the 2.6.34~ appears in dist because of maintain-ability.
>
> Give 2.6.37 kernel from Debian/experimental a try?
No thanks!! I'm almost a stable(not experimental) user..
It's not a "try" but a "big challenge"! And I have a physical LAN cable
now, instead.
^ permalink raw reply
* Re: [Driver] NEC WL300NU-G
From: Sedat Dilek @ 2011-01-15 23:14 UTC (permalink / raw)
To: u2@whoplays.asia; +Cc: Gertjan van Wingerde, linux-wireless
In-Reply-To: <4D3223F1.60901@whoplays.asia>
On Sat, Jan 15, 2011 at 11:47 PM, u2@whoplays.asia <u2@whoplays.asia> wrote:
> Gertjan,
>
> Yes, distribution Debian I use is sometimes old...
>
> I will wait for the 2.6.34~ appears in dist because of maintain-ability.
Give 2.6.37 kernel from Debian/experimental a try?
- Sedat -
^ permalink raw reply
* Re: [Driver] NEC WL300NU-G
From: u2 @ 2011-01-15 22:47 UTC (permalink / raw)
To: Gertjan van Wingerde; +Cc: linux-wireless
In-Reply-To: <4D31DBD2.9060909@gmail.com>
Gertjan,
Yes, distribution Debian I use is sometimes old...
I will wait for the 2.6.34~ appears in dist because of maintain-ability.
Thanks for your helpful reply.
Regards,
Miyagi
(2011-01-16 02:39), -------- Message --------:
> On 01/15/11 16:49, u2@whoplays.asia wrote:
>> hi,
>>
>> According to the list linuxwireless.org/en/users/Devices/USB, Wireless USB adapter NEC WL300NU-G[0x409:0x093f] is supported by ar9170usb.
>>
>> But the one I bought via www.amazon.co.jp/NEC-Aterm-WL300NU-G-PA-WL300NU-G is [0x409:0x0249] and not detected by any drivers...
>>
>> $ lsusb
>> Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
>> Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
>> Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
>> Bus 004 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
>> Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
>> Bus 002 Device 006: ID 0a5c:2101 Broadcom Corp. Bluetooth Controller
>> Bus 002 Device 005: ID 0853:0100 Topre Corporation HHKB Professional
>> Bus 002 Device 004: ID 0409:005a NEC Corp. HighSpeed Hub
>> Bus 002 Device 003: ID 0409:0249 NEC Corp.
>> Bus 002 Device 002: ID 2019:ab50 PLANEX GW-US54Mini2
>> Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
>> Bus 001 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
>> $ uname -a
>> Linux localhost 2.6.32-5-amd64 #1 SMP Wed Jan 12 03:40:32 UTC 2011 x86_64 GNU/Linux
>>
>> Plz anyone describe me why this one has another vendor-productID, and will this one supported?
>
> It looks like this device is supported in the latest ar9170usb driver. I can't really find in which kernel
> release support for this device has been added, but I'm almost certain that this was after 2.6.32 had been
> released (it is more likely that 2.6.34 is the first kernel release that supports your device).
>
> In other words, the kernel version that you are testing with is simply too old to support the device.
> I guess you need to upgrade to a later kernel version (or use compat-wireless).
>
> ---
> Gertjan.
>
^ permalink raw reply
* Compat-wireless release for 2011-01-15 is baked
From: Compat-wireless cronjob account @ 2011-01-15 20:04 UTC (permalink / raw)
To: linux-wireless
>From git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next
+ e70f43d...0eb1b34 akpm-end -> origin/akpm-end (forced update)
e0e736f..6ab8219 akpm-start -> origin/akpm-start
2271958..bed36d7 history -> origin/history
+ 1d506f8...e8883f8 master -> origin/master (forced update)
581548d..6ab8219 stable -> origin/stable
* [new tag] next-20110115 -> next-20110115
compat-wireless code metrics
782520 - Total upstream lines of code being pulled
2103 - backport code changes
1843 - backport code additions
260 - backport code deletions
7279 - backport from compat module
9382 - total backport code
1.1989 - % of code consists of backport work
1531 - Crap changes not yet posted
1488 - Crap additions not yet posted
43 - Crap deletions not yet posted
0.1956 - % of crap code
Base tree: linux-next.git
Base tree version: next-20110115
compat-wireless release: compat-wireless-2011-01-06-3-g8db1608-pc
^ permalink raw reply
* [PATCH v3 2/2] ath9k: Add 'misc' file to debugfs, fix queue indexes.
From: greearb @ 2011-01-15 19:13 UTC (permalink / raw)
To: linux-wireless; +Cc: ath9k-devel, Ben Greear
In-Reply-To: <1295118829-7464-1-git-send-email-greearb@candelatech.com>
From: Ben Greear <greearb@candelatech.com>
Add a misc file to show hardware op-mode, irq setup,
number of various types of VIFs and more.
Also, previous patches were using the wrong xmit queue
indexes. Change to use the internal ath9k indexes instead
of the mac80211 queue indexes.
Signed-off-by: Ben Greear <greearb@candelatech.com>
---
v3: Update to use iterator to gather vif stats. Print
calculated bssid mask as well.
This patch requires previous debugfs patches that I have posted.
:100644 100644 b0cb792... 5005621... M drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.c | 128 +++++++++++++++++++++++++++++---
1 files changed, 116 insertions(+), 12 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index b0cb792..5005621 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -595,10 +595,10 @@ static const struct file_operations fops_wiphy = {
do { \
len += snprintf(buf + len, size - len, \
"%s%13u%11u%10u%10u\n", str, \
- (unsigned int)(sc->tx.txq[WME_AC_BE].elem), \
- (unsigned int)(sc->tx.txq[WME_AC_BK].elem), \
- (unsigned int)(sc->tx.txq[WME_AC_VI].elem), \
- (unsigned int)(sc->tx.txq[WME_AC_VO].elem)); \
+ (unsigned int)(sc->tx.txq[ATH_TXQ_AC_BE].elem), \
+ (unsigned int)(sc->tx.txq[ATH_TXQ_AC_BK].elem), \
+ (unsigned int)(sc->tx.txq[ATH_TXQ_AC_VI].elem), \
+ (unsigned int)(sc->tx.txq[ATH_TXQ_AC_VO].elem)); \
if (len >= size) \
goto done; \
} while(0)
@@ -607,10 +607,10 @@ do { \
do { \
len += snprintf(buf + len, size - len, \
"%s%13i%11i%10i%10i\n", str, \
- list_empty(&sc->tx.txq[WME_AC_BE].elem), \
- list_empty(&sc->tx.txq[WME_AC_BK].elem), \
- list_empty(&sc->tx.txq[WME_AC_VI].elem), \
- list_empty(&sc->tx.txq[WME_AC_VO].elem)); \
+ list_empty(&sc->tx.txq[ATH_TXQ_AC_BE].elem), \
+ list_empty(&sc->tx.txq[ATH_TXQ_AC_BK].elem), \
+ list_empty(&sc->tx.txq[ATH_TXQ_AC_VI].elem), \
+ list_empty(&sc->tx.txq[ATH_TXQ_AC_VO].elem)); \
if (len >= size) \
goto done; \
} while (0)
@@ -657,10 +657,10 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
PR("hw-tx-proc-desc: ", txprocdesc);
len += snprintf(buf + len, size - len,
"%s%11p%11p%10p%10p\n", "txq-memory-address:",
- &(sc->tx.txq[WME_AC_BE]),
- &(sc->tx.txq[WME_AC_BK]),
- &(sc->tx.txq[WME_AC_VI]),
- &(sc->tx.txq[WME_AC_VO]));
+ &(sc->tx.txq[ATH_TXQ_AC_BE]),
+ &(sc->tx.txq[ATH_TXQ_AC_BK]),
+ &(sc->tx.txq[ATH_TXQ_AC_VI]),
+ &(sc->tx.txq[ATH_TXQ_AC_VO]));
if (len >= size)
goto done;
@@ -777,6 +777,99 @@ done:
return retval;
}
+static ssize_t read_file_misc(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath_softc *sc = file->private_data;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_hw *ah = sc->sc_ah;
+ char *buf;
+ unsigned int len = 0, size = 8000;
+ ssize_t retval = 0;
+ const char *tmp;
+ unsigned int reg;
+
+ buf = kzalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ switch (sc->sc_ah->opmode) {
+ case NL80211_IFTYPE_ADHOC:
+ tmp = "ADHOC";
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ tmp = "MESH";
+ break;
+ case NL80211_IFTYPE_AP:
+ tmp = "AP";
+ break;
+ case NL80211_IFTYPE_STATION:
+ tmp = "STATION";
+ break;
+ default:
+ tmp = "???";
+ break;
+ }
+
+ len += snprintf(buf + len, size - len,
+ "curbssid: %pM\n"
+ "OP-Mode: %s(%i)\n"
+ "Beacon-Timer-Register: 0x%x\n",
+ common->curbssid,
+ tmp, (int)(sc->sc_ah->opmode),
+ REG_READ(ah, AR_BEACON_PERIOD));
+
+ reg = REG_READ(ah, AR_TIMER_MODE);
+ len += snprintf(buf + len, size - len, "Timer-Mode-Register: 0x%x (",
+ reg);
+ if (reg & AR_TBTT_TIMER_EN)
+ len += snprintf(buf + len, size - len, "TBTT ");
+ if (reg & AR_DBA_TIMER_EN)
+ len += snprintf(buf + len, size - len, "DBA ");
+ if (reg & AR_SWBA_TIMER_EN)
+ len += snprintf(buf + len, size - len, "SWBA ");
+ if (reg & AR_HCF_TIMER_EN)
+ len += snprintf(buf + len, size - len, "HCF ");
+ if (reg & AR_TIM_TIMER_EN)
+ len += snprintf(buf + len, size - len, "TIM ");
+ if (reg & AR_DTIM_TIMER_EN)
+ len += snprintf(buf + len, size - len, "DTIM ");
+ len += snprintf(buf + len, size - len, ")\n");
+
+ reg = sc->sc_ah->imask;
+ len += snprintf(buf + len, size - len, "imask: 0x%x (", reg);
+ if (reg & ATH9K_INT_SWBA)
+ len += snprintf(buf + len, size - len, "SWBA ");
+ if (reg & ATH9K_INT_BMISS)
+ len += snprintf(buf + len, size - len, "BMISS ");
+ if (reg & ATH9K_INT_CST)
+ len += snprintf(buf + len, size - len, "CST ");
+ if (reg & ATH9K_INT_RX)
+ len += snprintf(buf + len, size - len, "RX ");
+ if (reg & ATH9K_INT_RXHP)
+ len += snprintf(buf + len, size - len, "RXHP ");
+ if (reg & ATH9K_INT_RXLP)
+ len += snprintf(buf + len, size - len, "RXLP ");
+ if (reg & ATH9K_INT_BB_WATCHDOG)
+ len += snprintf(buf + len, size - len, "BB_WATCHDOG ");
+ /* there are other IRQs if one wanted to add them. */
+ len += snprintf(buf + len, size - len, ")\n");
+
+ len += snprintf(buf + len, size - len,
+ "VIF Counts: AP: %hi STA: %hi MESH: %hi WDS: %hi"
+ " ADHOC: %hi nvifs: %hi beacon-vifs: %hi\n",
+ sc->naps, sc->nstations, sc->nmeshes, sc->nwds,
+ sc->nadhocs, sc->nvifs, sc->nbcnvifs);
+
+ if (len > size)
+ len = size;
+
+ retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+
+ return retval;
+}
+
void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_status *ts)
{
@@ -822,6 +915,13 @@ static const struct file_operations fops_stations = {
.llseek = default_llseek,
};
+static const struct file_operations fops_misc = {
+ .read = read_file_misc,
+ .open = ath9k_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
static ssize_t read_file_recv(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -1063,6 +1163,10 @@ int ath9k_init_debug(struct ath_hw *ah)
sc, &fops_stations))
goto err;
+ if (!debugfs_create_file("misc", S_IRUSR, sc->debug.debugfs_phy,
+ sc, &fops_misc))
+ goto err;
+
if (!debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy,
sc, &fops_recv))
goto err;
--
1.7.2.3
^ permalink raw reply related
* [PATCH v3 1/2] ath9k: Fix up hardware mode and beacons with multiple vifs.
From: greearb @ 2011-01-15 19:13 UTC (permalink / raw)
To: linux-wireless; +Cc: ath9k-devel, Ben Greear
From: Ben Greear <greearb@candelatech.com>
When using a mixture of AP and Station interfaces,
the hardware mode was using the type of the
last VIF registered. Instead, we should keep track
of the number of different types of vifs and set the
mode accordingly.
In addtion, use the vif type instead of hardware opmode
when dealing with beacons.
Attempt to move some of the common setup code into smaller
methods so we can re-use it when changing vif mode as
well as adding/deleting vifs.
Signed-off-by: Ben Greear <greearb@candelatech.com>
---
v3: Remove vif counters and use iterator to build current
state for all interfaces as needed. Move bssid mask setting
logic to 'main.c', and combine with other summary state
setting.
:100644 100644 3108699... dab0271... M drivers/net/wireless/ath/ath9k/ath9k.h
:100644 100644 385ba03... 8de591e... M drivers/net/wireless/ath/ath9k/beacon.c
:100644 100644 0452580... e91d927... M drivers/net/wireless/ath/ath9k/main.c
:100644 100644 ea2f67c... 8def8c2... M drivers/net/wireless/ath/ath9k/recv.c
:100644 100644 2dc7095... d205c66... M drivers/net/wireless/ath/ath9k/virtual.c
drivers/net/wireless/ath/ath9k/ath9k.h | 23 ++-
drivers/net/wireless/ath/ath9k/beacon.c | 14 +-
drivers/net/wireless/ath/ath9k/main.c | 308 +++++++++++++++++++++---------
drivers/net/wireless/ath/ath9k/recv.c | 16 ++-
drivers/net/wireless/ath/ath9k/virtual.c | 48 -----
5 files changed, 263 insertions(+), 146 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 3108699..dab0271 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -568,6 +568,20 @@ struct ath_ant_comb {
struct ath_wiphy;
struct ath_rate_table;
+struct ath9k_vif_iter_data {
+ const u8 *hw_macaddr; /* phy's hardware address, set
+ * before starting iteration for
+ * valid bssid mask.
+ */
+ u8 mask[ETH_ALEN]; /* bssid mask */
+ int naps; /* number of AP vifs */
+ int nmeshes; /* number of mesh vifs */
+ int nstations; /* number of station vifs */
+ int nwds; /* number of nwd vifs */
+ int nadhocs; /* number of adhoc vifs */
+ int nothers; /* number of vifs not specified above. */
+};
+
struct ath_softc {
struct ieee80211_hw *hw;
struct device *dev;
@@ -607,10 +621,10 @@ struct ath_softc {
u32 sc_flags; /* SC_OP_* */
u16 ps_flags; /* PS_* */
u16 curtxpow;
- u8 nbcnvifs;
- u16 nvifs;
bool ps_enabled;
bool ps_idle;
+ short nbcnvifs;
+ short nvifs;
unsigned long ps_usecount;
struct ath_config config;
@@ -694,6 +708,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw);
void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw);
bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode);
+bool ath9k_uses_beacons(int type);
#ifdef CONFIG_PCI
int ath_pci_init(void);
@@ -738,5 +753,9 @@ bool ath_mac80211_start_queue(struct ath_softc *sc, u16 skb_queue);
void ath_start_rfkill_poll(struct ath_softc *sc);
extern void ath9k_rfkill_poll_state(struct ieee80211_hw *hw);
+void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ath9k_vif_iter_data *iter_data);
+
#endif /* ATH9K_H */
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index 385ba03..8de591e 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -244,9 +244,7 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
struct ath_buf, list);
list_del(&avp->av_bcbuf->list);
- if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
- sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC ||
- sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) {
+ if (ath9k_uses_beacons(vif->type)) {
int slot;
/*
* Assign the vif to a beacon xmit slot. As
@@ -282,7 +280,7 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
/* NB: the beacon data buffer must be 32-bit aligned. */
skb = ieee80211_beacon_get(sc->hw, vif);
if (skb == NULL) {
- ath_dbg(common, ATH_DBG_BEACON, "cannot get skb\n");
+ ath_err(common, "ieee80211_beacon_get failed\n");
return -ENOMEM;
}
@@ -720,10 +718,10 @@ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
iftype = sc->sc_ah->opmode;
}
- cur_conf->listen_interval = 1;
- cur_conf->dtim_count = 1;
- cur_conf->bmiss_timeout =
- ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
+ cur_conf->listen_interval = 1;
+ cur_conf->dtim_count = 1;
+ cur_conf->bmiss_timeout =
+ ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
/*
* It looks like mac80211 may end up using beacon interval of zero in
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 0452580..e91d927 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1366,112 +1366,251 @@ static void ath9k_stop(struct ieee80211_hw *hw)
ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
}
-static int ath9k_add_interface(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+bool ath9k_uses_beacons(int type)
+{
+ switch (type) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void ath9k_reclaim_beacon(struct ath_softc *sc,
+ struct ieee80211_vif *vif)
{
- struct ath_wiphy *aphy = hw->priv;
- struct ath_softc *sc = aphy->sc;
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
struct ath_vif *avp = (void *)vif->drv_priv;
- enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED;
- int ret = 0;
- mutex_lock(&sc->mutex);
+ /* Disable SWBA interrupt */
+ sc->sc_ah->imask &= ~ATH9K_INT_SWBA;
+ ath9k_ps_wakeup(sc);
+ ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_ah->imask);
+ ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
+ tasklet_kill(&sc->bcon_tasklet);
+ ath9k_ps_restore(sc);
+
+ ath_beacon_return(sc, avp);
+ sc->sc_flags &= ~SC_OP_BEACONS;
+
+ if (sc->nbcnvifs > 0) {
+ /* Re-enable beaconing */
+ sc->sc_ah->imask |= ATH9K_INT_SWBA;
+ ath9k_ps_wakeup(sc);
+ ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_ah->imask);
+ ath9k_ps_restore(sc);
+ }
+}
+
+static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct ath9k_vif_iter_data *iter_data = data;
+ int i;
+
+ if (iter_data->hw_macaddr)
+ for (i = 0; i < ETH_ALEN; i++)
+ iter_data->mask[i] &=
+ ~(iter_data->hw_macaddr[i] ^ mac[i]);
switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- ic_opmode = NL80211_IFTYPE_STATION;
+ case NL80211_IFTYPE_AP:
+ iter_data->naps++;
break;
- case NL80211_IFTYPE_WDS:
- ic_opmode = NL80211_IFTYPE_WDS;
+ case NL80211_IFTYPE_STATION:
+ iter_data->nstations++;
break;
case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_AP:
+ iter_data->nadhocs++;
+ break;
case NL80211_IFTYPE_MESH_POINT:
- if (sc->nbcnvifs >= ATH_BCBUF) {
- ret = -ENOBUFS;
- goto out;
- }
- ic_opmode = vif->type;
+ iter_data->nmeshes++;
+ break;
+ case NL80211_IFTYPE_WDS:
+ iter_data->nwds++;
break;
default:
- ath_err(common, "Interface type %d not yet supported\n",
- vif->type);
- ret = -EOPNOTSUPP;
- goto out;
+ iter_data->nothers++;
+ break;
}
+}
- ath_dbg(common, ATH_DBG_CONFIG,
- "Attach a VIF of type: %d\n", ic_opmode);
+/* Called with sc->mutex held. */
+void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ath9k_vif_iter_data *iter_data)
+{
+ struct ath_wiphy *aphy = hw->priv;
+ struct ath_softc *sc = aphy->sc;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ int i;
- /* Set the VIF opmode */
- avp->av_opmode = ic_opmode;
- avp->av_bslot = -1;
+ /*
+ * Use the hardware MAC address as reference, the hardware uses it
+ * together with the BSSID mask when matching addresses.
+ */
+ memset(iter_data, 0, sizeof(*iter_data));
+ iter_data->hw_macaddr = common->macaddr;
+ memset(&iter_data->mask, 0xff, ETH_ALEN);
- sc->nvifs++;
+ if (vif)
+ ath9k_vif_iter(iter_data, vif->addr, vif);
+
+ /* Get list of all active MAC addresses */
+ spin_lock_bh(&sc->wiphy_lock);
+ ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter,
+ iter_data);
+ for (i = 0; i < sc->num_sec_wiphy; i++) {
+ if (sc->sec_wiphy[i] == NULL)
+ continue;
+ ieee80211_iterate_active_interfaces_atomic(
+ sc->sec_wiphy[i]->hw, ath9k_vif_iter, iter_data);
+ }
+ spin_unlock_bh(&sc->wiphy_lock);
+}
+
+/* Called with sc->mutex held. */
+static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct ath_wiphy *aphy = hw->priv;
+ struct ath_softc *sc = aphy->sc;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_vif_iter_data iter_data;
- ath9k_set_bssid_mask(hw, vif);
+ ath9k_calculate_iter_data(hw, vif, &iter_data);
- if (sc->nvifs > 1)
- goto out; /* skip global settings for secondary vif */
+ /* Set BSSID mask. */
+ memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
+ ath_hw_setbssidmask(common);
- if (ic_opmode == NL80211_IFTYPE_AP) {
+ /* Set op-mode & TSF */
+ if (iter_data.naps > 0) {
ath9k_hw_set_tsfadjust(ah, 1);
sc->sc_flags |= SC_OP_TSF_RESET;
- }
+ ah->opmode = NL80211_IFTYPE_AP;
+ } else {
+ ath9k_hw_set_tsfadjust(ah, 0);
+ sc->sc_flags &= ~SC_OP_TSF_RESET;
- /* Set the device opmode */
- ah->opmode = ic_opmode;
+ if (iter_data.nwds + iter_data.nmeshes)
+ ah->opmode = NL80211_IFTYPE_AP;
+ else if (iter_data.nadhocs)
+ ah->opmode = NL80211_IFTYPE_ADHOC;
+ else
+ ah->opmode = NL80211_IFTYPE_STATION;
+ }
/*
* Enable MIB interrupts when there are hardware phy counters.
- * Note we only do this (at the moment) for station mode.
*/
- if ((vif->type == NL80211_IFTYPE_STATION) ||
- (vif->type == NL80211_IFTYPE_ADHOC) ||
- (vif->type == NL80211_IFTYPE_MESH_POINT)) {
+ if ((iter_data.nstations + iter_data.nadhocs + iter_data.nmeshes) > 0) {
if (ah->config.enable_ani)
ah->imask |= ATH9K_INT_MIB;
ah->imask |= ATH9K_INT_TSFOOR;
+ } else {
+ ah->imask &= ~ATH9K_INT_MIB;
+ ah->imask &= ~ATH9K_INT_TSFOOR;
}
ath9k_hw_set_interrupts(ah, ah->imask);
- if (vif->type == NL80211_IFTYPE_AP ||
- vif->type == NL80211_IFTYPE_ADHOC) {
+ /* Set up ANI */
+ if ((iter_data.naps + iter_data.nadhocs) > 0) {
sc->sc_flags |= SC_OP_ANI_RUN;
ath_start_ani(common);
+ } else {
+ sc->sc_flags &= ~SC_OP_ANI_RUN;
+ del_timer_sync(&common->ani.timer);
}
+}
-out:
- mutex_unlock(&sc->mutex);
- return ret;
+/* Called with sc->mutex held, vif counts set up properly. */
+static void ath9k_do_vif_add_setup(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct ath_wiphy *aphy = hw->priv;
+ struct ath_softc *sc = aphy->sc;
+
+ ath9k_calculate_summary_state(hw, vif);
+
+ if (ath9k_uses_beacons(vif->type)) {
+ int error;
+ ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
+ /* This may fail because upper levels do not have beacons
+ * properly configured yet. That's OK, we assume it
+ * will be properly configured and then we will be notified
+ * in the info_changed method and set up beacons properly
+ * there.
+ */
+ error = ath_beacon_alloc(aphy, vif);
+ if (error)
+ ath9k_reclaim_beacon(sc, vif);
+ else
+ ath_beacon_config(sc, vif);
+ }
}
-static void ath9k_reclaim_beacon(struct ath_softc *sc,
- struct ieee80211_vif *vif)
+
+static int ath9k_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
+ struct ath_wiphy *aphy = hw->priv;
+ struct ath_softc *sc = aphy->sc;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath_vif *avp = (void *)vif->drv_priv;
+ int ret = 0;
- /* Disable SWBA interrupt */
- sc->sc_ah->imask &= ~ATH9K_INT_SWBA;
- ath9k_ps_wakeup(sc);
- ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_ah->imask);
- ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
- tasklet_kill(&sc->bcon_tasklet);
- ath9k_ps_restore(sc);
+ mutex_lock(&sc->mutex);
- ath_beacon_return(sc, avp);
- sc->sc_flags &= ~SC_OP_BEACONS;
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_MESH_POINT:
+ break;
+ default:
+ ath_err(common, "Interface type %d not yet supported\n",
+ vif->type);
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
- if (sc->nbcnvifs > 0) {
- /* Re-enable beaconing */
- sc->sc_ah->imask |= ATH9K_INT_SWBA;
- ath9k_ps_wakeup(sc);
- ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_ah->imask);
- ath9k_ps_restore(sc);
+ if (ath9k_uses_beacons(vif->type)) {
+ if (sc->nbcnvifs >= ATH_BCBUF) {
+ ath_err(common, "Not enough beacon buffers when adding"
+ " new interface of type: %i\n",
+ vif->type);
+ ret = -ENOBUFS;
+ goto out;
+ }
+ }
+
+ if ((vif->type == NL80211_IFTYPE_ADHOC) &&
+ sc->nvifs > 0) {
+ ath_err(common, "Cannot create ADHOC interface when other"
+ " interfaces already exist.\n");
+ ret = -EINVAL;
+ goto out;
}
+
+ ath_dbg(common, ATH_DBG_CONFIG,
+ "Attach a VIF of type: %d\n", vif->type);
+
+ /* Set the VIF opmode */
+ avp->av_opmode = vif->type;
+ avp->av_bslot = -1;
+
+ sc->nvifs++;
+
+ ath9k_do_vif_add_setup(hw, vif);
+out:
+ mutex_unlock(&sc->mutex);
+ return ret;
}
static int ath9k_change_interface(struct ieee80211_hw *hw,
@@ -1487,32 +1626,33 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
ath_dbg(common, ATH_DBG_CONFIG, "Change Interface\n");
mutex_lock(&sc->mutex);
- switch (new_type) {
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_ADHOC:
+ /* See if new interface type is valid. */
+ if ((new_type == NL80211_IFTYPE_ADHOC) &&
+ (sc->nvifs > 1)) {
+ ath_err(common, "When using ADHOC, it must be the only"
+ " interface.\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (ath9k_uses_beacons(new_type) &&
+ !ath9k_uses_beacons(vif->type)) {
if (sc->nbcnvifs >= ATH_BCBUF) {
ath_err(common, "No beacon slot available\n");
ret = -ENOBUFS;
goto out;
}
- break;
- case NL80211_IFTYPE_STATION:
- /* Stop ANI */
- sc->sc_flags &= ~SC_OP_ANI_RUN;
- del_timer_sync(&common->ani.timer);
- if ((vif->type == NL80211_IFTYPE_AP) ||
- (vif->type == NL80211_IFTYPE_ADHOC))
- ath9k_reclaim_beacon(sc, vif);
- break;
- default:
- ath_err(common, "Interface type %d not yet supported\n",
- vif->type);
- ret = -ENOTSUPP;
- goto out;
}
+
+ /* Clean up old vif stuff */
+ if (ath9k_uses_beacons(vif->type))
+ ath9k_reclaim_beacon(sc, vif);
+
+ /* Add new settings */
vif->type = new_type;
vif->p2p = p2p;
+ ath9k_do_vif_add_setup(hw, vif);
out:
mutex_unlock(&sc->mutex);
return ret;
@@ -1529,17 +1669,13 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
mutex_lock(&sc->mutex);
- /* Stop ANI */
- sc->sc_flags &= ~SC_OP_ANI_RUN;
- del_timer_sync(&common->ani.timer);
+ sc->nvifs--;
/* Reclaim beacon resources */
- if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) ||
- (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) ||
- (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT))
+ if (ath9k_uses_beacons(vif->type))
ath9k_reclaim_beacon(sc, vif);
- sc->nvifs--;
+ ath9k_calculate_summary_state(hw, NULL);
mutex_unlock(&sc->mutex);
}
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index ea2f67c..8def8c2 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -589,8 +589,14 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
return;
mgmt = (struct ieee80211_mgmt *)skb->data;
- if (memcmp(common->curbssid, mgmt->bssid, ETH_ALEN) != 0)
+ if (memcmp(common->curbssid, mgmt->bssid, ETH_ALEN) != 0) {
+ /* TODO: This doesn't work well if you have stations
+ * associated to two different APs because curbssid
+ * is just the last AP that any of the stations associated
+ * with.
+ */
return; /* not from our current AP */
+ }
sc->ps_flags &= ~PS_WAIT_FOR_BEACON;
@@ -985,8 +991,14 @@ static void ath9k_process_rssi(struct ath_common *common,
fc = hdr->frame_control;
if (!ieee80211_is_beacon(fc) ||
- compare_ether_addr(hdr->addr3, common->curbssid))
+ compare_ether_addr(hdr->addr3, common->curbssid)) {
+ /* TODO: This doesn't work well if you have stations
+ * associated to two different APs because curbssid
+ * is just the last AP that any of the stations associated
+ * with.
+ */
return;
+ }
if (rx_stats->rs_rssi != ATH9K_RSSI_BAD && !rx_stats->rs_moreaggr)
ATH_RSSI_LPF(aphy->last_rssi, rx_stats->rs_rssi);
diff --git a/drivers/net/wireless/ath/ath9k/virtual.c b/drivers/net/wireless/ath/ath9k/virtual.c
index 2dc7095..d205c66 100644
--- a/drivers/net/wireless/ath/ath9k/virtual.c
+++ b/drivers/net/wireless/ath/ath9k/virtual.c
@@ -18,54 +18,6 @@
#include "ath9k.h"
-struct ath9k_vif_iter_data {
- const u8 *hw_macaddr;
- u8 mask[ETH_ALEN];
-};
-
-static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
-{
- struct ath9k_vif_iter_data *iter_data = data;
- int i;
-
- for (i = 0; i < ETH_ALEN; i++)
- iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
-}
-
-void ath9k_set_bssid_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
-{
- struct ath_wiphy *aphy = hw->priv;
- struct ath_softc *sc = aphy->sc;
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- struct ath9k_vif_iter_data iter_data;
- int i;
-
- /*
- * Use the hardware MAC address as reference, the hardware uses it
- * together with the BSSID mask when matching addresses.
- */
- iter_data.hw_macaddr = common->macaddr;
- memset(&iter_data.mask, 0xff, ETH_ALEN);
-
- if (vif)
- ath9k_vif_iter(&iter_data, vif->addr, vif);
-
- /* Get list of all active MAC addresses */
- spin_lock_bh(&sc->wiphy_lock);
- ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter,
- &iter_data);
- for (i = 0; i < sc->num_sec_wiphy; i++) {
- if (sc->sec_wiphy[i] == NULL)
- continue;
- ieee80211_iterate_active_interfaces_atomic(
- sc->sec_wiphy[i]->hw, ath9k_vif_iter, &iter_data);
- }
- spin_unlock_bh(&sc->wiphy_lock);
-
- memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
- ath_hw_setbssidmask(common);
-}
-
int ath9k_wiphy_add(struct ath_softc *sc)
{
int i, error;
--
1.7.2.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox