mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-28 09:39:00 +00:00
b7ce76b4b9
Signed-off-by: Felix Fietkau <nbd@openwrt.org> SVN-Revision: 41017
748 lines
24 KiB
Diff
748 lines
24 KiB
Diff
commit 60ccc107c9b9fb732fdee1f76bb2dad44f0e1798
|
|
Author: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
|
|
Date: Tue May 27 16:58:02 2014 +0530
|
|
|
|
ath9k: Fix deadlock while updating p2p beacon timer
|
|
|
|
pm_lock is taken twice while syncing HW TSF of p2p vif.
|
|
Fix this by taking the lock at caller side.
|
|
|
|
Cc: Felix Fietkau <nbd@openwrt.org>
|
|
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
|
|
Signed-off-by: John W. Linville <linville@tuxdriver.com>
|
|
|
|
commit f3831a4e3903dbc1a57d5df56deb6a143fd001bc
|
|
Author: Stanislaw Gruszka <sgruszka@redhat.com>
|
|
Date: Thu Jun 5 13:52:27 2014 +0200
|
|
|
|
rt2x00: do not initialize BCN_OFFSET registers
|
|
|
|
We setup BCN_OFFSET{0,1} registers dynamically, don't have to
|
|
initialize them.
|
|
|
|
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
|
|
|
|
commit e5c58ca7a48d4c82f282749a978052c47fd95998
|
|
Author: Stanislaw Gruszka <sgruszka@redhat.com>
|
|
Date: Thu Jun 5 13:52:26 2014 +0200
|
|
|
|
rt2x00: change order when stop beaconing
|
|
|
|
When no beaconing is needed, first stop beacon queue (disable beaconing
|
|
globally) to avoid possible sending of not prepared beacon on short
|
|
period after clearing beacon and before stop of BCN queue.
|
|
|
|
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
|
|
|
|
commit 382c1b9e03f52d0cd741ef1d942cad0f649f0744
|
|
Author: Stanislaw Gruszka <sgruszka@redhat.com>
|
|
Date: Thu Jun 5 13:52:25 2014 +0200
|
|
|
|
rt2x00: change default MAC_BSSID_DW1_BSS_BCN_NUM
|
|
|
|
We setup MAC_BSSID_DW1_BSS_BCN_NUM dynamically when numbers of active
|
|
beacons increase. Change default to 0 to tell hardware that we want to
|
|
send only one beacon as default.
|
|
|
|
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
|
|
|
|
commit 3b400571dd033e46fa7e76c5bb92a3ce8198afa9
|
|
Author: Stanislaw Gruszka <sgruszka@redhat.com>
|
|
Date: Thu Jun 5 13:52:24 2014 +0200
|
|
|
|
rt2x00: change beaconing setup on RT2800
|
|
|
|
As reported by Matthias, on 5572 chip, even if we clear up TXWI
|
|
of corresponding beacon, hardware still try to send it or do other
|
|
action that increase power consumption peak up to 1A.
|
|
|
|
To avoid the issue, setup beaconing dynamically by configuring offsets
|
|
of currently active beacons and MAC_BSSID_DW1_BSS_BCN_NUM variable,
|
|
which limit number of beacons that hardware will try to send.
|
|
|
|
Reported-by: Matthias Fend <Matthias.Fend@wolfvision.net>
|
|
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
|
|
|
|
commit 916e591b2cc41f7e572992175ca56d866d7bc958
|
|
Author: Stanislaw Gruszka <sgruszka@redhat.com>
|
|
Date: Thu Jun 5 13:52:23 2014 +0200
|
|
|
|
rt2x00: change beaconing locking
|
|
|
|
This patch is needed for further changes to keep global variables
|
|
consistent when changing beaconing on diffrent vif's.
|
|
|
|
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
|
|
|
|
commit 930b0dffd1731f3f418f9132faea720a23b7af61
|
|
Author: Johannes Berg <johannes.berg@intel.com>
|
|
Date: Tue Jun 3 11:18:47 2014 +0200
|
|
|
|
mac80211: fix station/driver powersave race
|
|
|
|
It is currently possible to have a race due to the station PS
|
|
unblock work like this:
|
|
* station goes to sleep with frames buffered in the driver
|
|
* driver blocks wakeup
|
|
* station wakes up again
|
|
* driver flushes/returns frames, and unblocks, which schedules
|
|
the unblock work
|
|
* unblock work starts to run, and checks that the station is
|
|
awake (i.e. that the WLAN_STA_PS_STA flag isn't set)
|
|
* we process a received frame with PM=1, setting the flag again
|
|
* ieee80211_sta_ps_deliver_wakeup() runs, delivering all frames
|
|
to the driver, and then clearing the WLAN_STA_PS_DRIVER and
|
|
WLAN_STA_PS_STA flags
|
|
|
|
In this scenario, mac80211 will think that the station is awake,
|
|
while it really is asleep, and any TX'ed frames should be filtered
|
|
by the device (it will know that the station is sleeping) but then
|
|
passed to mac80211 again, which will not buffer it either as it
|
|
thinks the station is awake, and eventually the packets will be
|
|
dropped.
|
|
|
|
Fix this by moving the clearing of the flags to exactly where we
|
|
learn about the situation. This creates a problem of reordering,
|
|
so introduce another flag indicating that delivery is being done,
|
|
this new flag also queues frames and is cleared only while the
|
|
spinlock is held (which the queuing code also holds) so that any
|
|
concurrent delivery/TX is handled correctly.
|
|
|
|
Reported-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com>
|
|
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
|
|
|
commit 6df35206bc6c1c6aad1d8077df5786b4a7f77873
|
|
Author: Felix Fietkau <nbd@openwrt.org>
|
|
Date: Fri May 23 19:58:14 2014 +0200
|
|
|
|
mac80211: reduce packet loss notifications under load
|
|
|
|
During strong signal fluctuations under high throughput, few consecutive
|
|
failed A-MPDU transmissions can easily trigger packet loss notification,
|
|
and thus (in AP mode) client disconnection.
|
|
|
|
Reduce the number of false positives by checking the A-MPDU status flag
|
|
and treating a failed A-MPDU as a single packet.
|
|
|
|
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
|
|
|
commit 7b7843a36fbcc568834404c7430ff895d8502131
|
|
Author: Felix Fietkau <nbd@openwrt.org>
|
|
Date: Fri May 23 19:26:32 2014 +0200
|
|
|
|
mac80211: fix a memory leak on sta rate selection table
|
|
|
|
Cc: stable@vger.kernel.org
|
|
Reported-by: Christophe Prévotaux <cprevotaux@nltinc.com>
|
|
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
|
|
|
commit 96892d6aa0a153423070addf3070bc79578b3897
|
|
Author: Felix Fietkau <nbd@openwrt.org>
|
|
Date: Mon May 19 21:20:49 2014 +0200
|
|
|
|
ath9k: avoid passing buffers to the hardware during flush
|
|
|
|
The commit "ath9k: fix possible hang on flush" changed the receive code
|
|
to always link rx descriptors of processed frames, even when flushing.
|
|
In some cases, this leads to flushed rx buffers being passed to the
|
|
hardware while rx is already stopped.
|
|
|
|
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
|
|
|
--- a/drivers/net/wireless/ath/ath9k/recv.c
|
|
+++ b/drivers/net/wireless/ath/ath9k/recv.c
|
|
@@ -34,7 +34,8 @@ static inline bool ath9k_check_auto_slee
|
|
* buffer (or rx fifo). This can incorrectly acknowledge packets
|
|
* to a sender if last desc is self-linked.
|
|
*/
|
|
-static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf)
|
|
+static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf,
|
|
+ bool flush)
|
|
{
|
|
struct ath_hw *ah = sc->sc_ah;
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
|
@@ -59,18 +60,19 @@ static void ath_rx_buf_link(struct ath_s
|
|
common->rx_bufsize,
|
|
0);
|
|
|
|
- if (sc->rx.rxlink == NULL)
|
|
- ath9k_hw_putrxbuf(ah, bf->bf_daddr);
|
|
- else
|
|
+ if (sc->rx.rxlink)
|
|
*sc->rx.rxlink = bf->bf_daddr;
|
|
+ else if (!flush)
|
|
+ ath9k_hw_putrxbuf(ah, bf->bf_daddr);
|
|
|
|
sc->rx.rxlink = &ds->ds_link;
|
|
}
|
|
|
|
-static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf)
|
|
+static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf,
|
|
+ bool flush)
|
|
{
|
|
if (sc->rx.buf_hold)
|
|
- ath_rx_buf_link(sc, sc->rx.buf_hold);
|
|
+ ath_rx_buf_link(sc, sc->rx.buf_hold, flush);
|
|
|
|
sc->rx.buf_hold = bf;
|
|
}
|
|
@@ -442,7 +444,7 @@ int ath_startrecv(struct ath_softc *sc)
|
|
sc->rx.buf_hold = NULL;
|
|
sc->rx.rxlink = NULL;
|
|
list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) {
|
|
- ath_rx_buf_link(sc, bf);
|
|
+ ath_rx_buf_link(sc, bf, false);
|
|
}
|
|
|
|
/* We could have deleted elements so the list may be empty now */
|
|
@@ -1118,12 +1120,12 @@ requeue_drop_frag:
|
|
requeue:
|
|
list_add_tail(&bf->list, &sc->rx.rxbuf);
|
|
|
|
- if (edma) {
|
|
- ath_rx_edma_buf_link(sc, qtype);
|
|
- } else {
|
|
- ath_rx_buf_relink(sc, bf);
|
|
+ if (!edma) {
|
|
+ ath_rx_buf_relink(sc, bf, flush);
|
|
if (!flush)
|
|
ath9k_hw_rxena(ah);
|
|
+ } else if (!flush) {
|
|
+ ath_rx_edma_buf_link(sc, qtype);
|
|
}
|
|
|
|
if (!budget--)
|
|
--- a/net/mac80211/sta_info.c
|
|
+++ b/net/mac80211/sta_info.c
|
|
@@ -100,7 +100,8 @@ static void __cleanup_single_sta(struct
|
|
struct ps_data *ps;
|
|
|
|
if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
|
|
- test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
|
|
+ test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
|
|
+ test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
|
|
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
|
|
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
|
|
ps = &sdata->bss->ps;
|
|
@@ -111,6 +112,7 @@ static void __cleanup_single_sta(struct
|
|
|
|
clear_sta_flag(sta, WLAN_STA_PS_STA);
|
|
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
|
+ clear_sta_flag(sta, WLAN_STA_PS_DELIVER);
|
|
|
|
atomic_dec(&ps->num_sta_ps);
|
|
sta_info_recalc_tim(sta);
|
|
@@ -125,7 +127,7 @@ static void __cleanup_single_sta(struct
|
|
if (ieee80211_vif_is_mesh(&sdata->vif))
|
|
mesh_sta_cleanup(sta);
|
|
|
|
- cancel_work_sync(&sta->drv_unblock_wk);
|
|
+ cancel_work_sync(&sta->drv_deliver_wk);
|
|
|
|
/*
|
|
* Destroy aggregation state here. It would be nice to wait for the
|
|
@@ -227,6 +229,7 @@ struct sta_info *sta_info_get_by_idx(str
|
|
*/
|
|
void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
|
|
{
|
|
+ struct ieee80211_sta_rates *rates;
|
|
int i;
|
|
|
|
if (sta->rate_ctrl)
|
|
@@ -238,6 +241,10 @@ void sta_info_free(struct ieee80211_loca
|
|
kfree(sta->tx_lat);
|
|
}
|
|
|
|
+ rates = rcu_dereference_protected(sta->sta.rates, true);
|
|
+ if (rates)
|
|
+ kfree(rates);
|
|
+
|
|
sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
|
|
|
|
kfree(sta);
|
|
@@ -252,33 +259,23 @@ static void sta_info_hash_add(struct iee
|
|
rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
|
|
}
|
|
|
|
-static void sta_unblock(struct work_struct *wk)
|
|
+static void sta_deliver_ps_frames(struct work_struct *wk)
|
|
{
|
|
struct sta_info *sta;
|
|
|
|
- sta = container_of(wk, struct sta_info, drv_unblock_wk);
|
|
+ sta = container_of(wk, struct sta_info, drv_deliver_wk);
|
|
|
|
if (sta->dead)
|
|
return;
|
|
|
|
- if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
|
|
- local_bh_disable();
|
|
+ local_bh_disable();
|
|
+ if (!test_sta_flag(sta, WLAN_STA_PS_STA))
|
|
ieee80211_sta_ps_deliver_wakeup(sta);
|
|
- local_bh_enable();
|
|
- } else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) {
|
|
- clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
|
-
|
|
- local_bh_disable();
|
|
+ else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL))
|
|
ieee80211_sta_ps_deliver_poll_response(sta);
|
|
- local_bh_enable();
|
|
- } else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) {
|
|
- clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
|
-
|
|
- local_bh_disable();
|
|
+ else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD))
|
|
ieee80211_sta_ps_deliver_uapsd(sta);
|
|
- local_bh_enable();
|
|
- } else
|
|
- clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
|
+ local_bh_enable();
|
|
}
|
|
|
|
static int sta_prepare_rate_control(struct ieee80211_local *local,
|
|
@@ -340,7 +337,7 @@ struct sta_info *sta_info_alloc(struct i
|
|
|
|
spin_lock_init(&sta->lock);
|
|
spin_lock_init(&sta->ps_lock);
|
|
- INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
|
|
+ INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
|
|
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
|
|
mutex_init(&sta->ampdu_mlme.mtx);
|
|
#ifdef CPTCFG_MAC80211_MESH
|
|
@@ -1140,8 +1137,15 @@ void ieee80211_sta_ps_deliver_wakeup(str
|
|
}
|
|
|
|
ieee80211_add_pending_skbs(local, &pending);
|
|
- clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
|
- clear_sta_flag(sta, WLAN_STA_PS_STA);
|
|
+
|
|
+ /* now we're no longer in the deliver code */
|
|
+ clear_sta_flag(sta, WLAN_STA_PS_DELIVER);
|
|
+
|
|
+ /* The station might have polled and then woken up before we responded,
|
|
+ * so clear these flags now to avoid them sticking around.
|
|
+ */
|
|
+ clear_sta_flag(sta, WLAN_STA_PSPOLL);
|
|
+ clear_sta_flag(sta, WLAN_STA_UAPSD);
|
|
spin_unlock(&sta->ps_lock);
|
|
|
|
atomic_dec(&ps->num_sta_ps);
|
|
@@ -1542,10 +1546,26 @@ void ieee80211_sta_block_awake(struct ie
|
|
|
|
trace_api_sta_block_awake(sta->local, pubsta, block);
|
|
|
|
- if (block)
|
|
+ if (block) {
|
|
set_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
|
- else if (test_sta_flag(sta, WLAN_STA_PS_DRIVER))
|
|
- ieee80211_queue_work(hw, &sta->drv_unblock_wk);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!test_sta_flag(sta, WLAN_STA_PS_DRIVER))
|
|
+ return;
|
|
+
|
|
+ if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
|
|
+ set_sta_flag(sta, WLAN_STA_PS_DELIVER);
|
|
+ clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
|
+ ieee80211_queue_work(hw, &sta->drv_deliver_wk);
|
|
+ } else if (test_sta_flag(sta, WLAN_STA_PSPOLL) ||
|
|
+ test_sta_flag(sta, WLAN_STA_UAPSD)) {
|
|
+ /* must be asleep in this case */
|
|
+ clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
|
+ ieee80211_queue_work(hw, &sta->drv_deliver_wk);
|
|
+ } else {
|
|
+ clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
|
+ }
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_sta_block_awake);
|
|
|
|
--- a/net/mac80211/status.c
|
|
+++ b/net/mac80211/status.c
|
|
@@ -541,6 +541,23 @@ static void ieee80211_tx_latency_end_msr
|
|
*/
|
|
#define STA_LOST_PKT_THRESHOLD 50
|
|
|
|
+static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb)
|
|
+{
|
|
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
|
+
|
|
+ /* This packet was aggregated but doesn't carry status info */
|
|
+ if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
|
|
+ !(info->flags & IEEE80211_TX_STAT_AMPDU))
|
|
+ return;
|
|
+
|
|
+ if (++sta->lost_packets < STA_LOST_PKT_THRESHOLD)
|
|
+ return;
|
|
+
|
|
+ cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr,
|
|
+ sta->lost_packets, GFP_ATOMIC);
|
|
+ sta->lost_packets = 0;
|
|
+}
|
|
+
|
|
void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
|
{
|
|
struct sk_buff *skb2;
|
|
@@ -680,12 +697,8 @@ void ieee80211_tx_status(struct ieee8021
|
|
if (info->flags & IEEE80211_TX_STAT_ACK) {
|
|
if (sta->lost_packets)
|
|
sta->lost_packets = 0;
|
|
- } else if (++sta->lost_packets >= STA_LOST_PKT_THRESHOLD) {
|
|
- cfg80211_cqm_pktloss_notify(sta->sdata->dev,
|
|
- sta->sta.addr,
|
|
- sta->lost_packets,
|
|
- GFP_ATOMIC);
|
|
- sta->lost_packets = 0;
|
|
+ } else {
|
|
+ ieee80211_lost_packet(sta, skb);
|
|
}
|
|
}
|
|
|
|
--- a/net/mac80211/rx.c
|
|
+++ b/net/mac80211/rx.c
|
|
@@ -1107,6 +1107,8 @@ static void sta_ps_end(struct sta_info *
|
|
return;
|
|
}
|
|
|
|
+ set_sta_flag(sta, WLAN_STA_PS_DELIVER);
|
|
+ clear_sta_flag(sta, WLAN_STA_PS_STA);
|
|
ieee80211_sta_ps_deliver_wakeup(sta);
|
|
}
|
|
|
|
--- a/net/mac80211/sta_info.h
|
|
+++ b/net/mac80211/sta_info.h
|
|
@@ -82,6 +82,7 @@ enum ieee80211_sta_info_flags {
|
|
WLAN_STA_TOFFSET_KNOWN,
|
|
WLAN_STA_MPSP_OWNER,
|
|
WLAN_STA_MPSP_RECIPIENT,
|
|
+ WLAN_STA_PS_DELIVER,
|
|
};
|
|
|
|
#define ADDBA_RESP_INTERVAL HZ
|
|
@@ -265,7 +266,7 @@ struct ieee80211_tx_latency_stat {
|
|
* @last_rx_rate_vht_nss: rx status nss of last data packet
|
|
* @lock: used for locking all fields that require locking, see comments
|
|
* in the header file.
|
|
- * @drv_unblock_wk: used for driver PS unblocking
|
|
+ * @drv_deliver_wk: used for delivering frames after driver PS unblocking
|
|
* @listen_interval: listen interval of this station, when we're acting as AP
|
|
* @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly
|
|
* @ps_lock: used for powersave (when mac80211 is the AP) related locking
|
|
@@ -345,7 +346,7 @@ struct sta_info {
|
|
void *rate_ctrl_priv;
|
|
spinlock_t lock;
|
|
|
|
- struct work_struct drv_unblock_wk;
|
|
+ struct work_struct drv_deliver_wk;
|
|
|
|
u16 listen_interval;
|
|
|
|
--- a/net/mac80211/tx.c
|
|
+++ b/net/mac80211/tx.c
|
|
@@ -469,7 +469,8 @@ ieee80211_tx_h_unicast_ps_buf(struct iee
|
|
return TX_CONTINUE;
|
|
|
|
if (unlikely((test_sta_flag(sta, WLAN_STA_PS_STA) ||
|
|
- test_sta_flag(sta, WLAN_STA_PS_DRIVER)) &&
|
|
+ test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
|
|
+ test_sta_flag(sta, WLAN_STA_PS_DELIVER)) &&
|
|
!(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) {
|
|
int ac = skb_get_queue_mapping(tx->skb);
|
|
|
|
@@ -486,7 +487,8 @@ ieee80211_tx_h_unicast_ps_buf(struct iee
|
|
* ahead and Tx the packet.
|
|
*/
|
|
if (!test_sta_flag(sta, WLAN_STA_PS_STA) &&
|
|
- !test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
|
|
+ !test_sta_flag(sta, WLAN_STA_PS_DRIVER) &&
|
|
+ !test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
|
|
spin_unlock(&sta->ps_lock);
|
|
return TX_CONTINUE;
|
|
}
|
|
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
|
|
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
|
|
@@ -947,6 +947,40 @@ static inline u8 rt2800_get_beacon_offse
|
|
return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index));
|
|
}
|
|
|
|
+static void rt2800_update_beacons_setup(struct rt2x00_dev *rt2x00dev)
|
|
+{
|
|
+ struct data_queue *queue = rt2x00dev->bcn;
|
|
+ struct queue_entry *entry;
|
|
+ int i, bcn_num = 0;
|
|
+ u64 off, reg = 0;
|
|
+ u32 bssid_dw1;
|
|
+
|
|
+ /*
|
|
+ * Setup offsets of all active beacons in BCN_OFFSET{0,1} registers.
|
|
+ */
|
|
+ for (i = 0; i < queue->limit; i++) {
|
|
+ entry = &queue->entries[i];
|
|
+ if (!test_bit(ENTRY_BCN_ENABLED, &entry->flags))
|
|
+ continue;
|
|
+ off = rt2800_get_beacon_offset(rt2x00dev, entry->entry_idx);
|
|
+ reg |= off << (8 * bcn_num);
|
|
+ bcn_num++;
|
|
+ }
|
|
+
|
|
+ WARN_ON_ONCE(bcn_num != rt2x00dev->intf_beaconing);
|
|
+
|
|
+ rt2800_register_write(rt2x00dev, BCN_OFFSET0, (u32) reg);
|
|
+ rt2800_register_write(rt2x00dev, BCN_OFFSET1, (u32) (reg >> 32));
|
|
+
|
|
+ /*
|
|
+ * H/W sends up to MAC_BSSID_DW1_BSS_BCN_NUM + 1 consecutive beacons.
|
|
+ */
|
|
+ rt2800_register_read(rt2x00dev, MAC_BSSID_DW1, &bssid_dw1);
|
|
+ rt2x00_set_field32(&bssid_dw1, MAC_BSSID_DW1_BSS_BCN_NUM,
|
|
+ bcn_num > 0 ? bcn_num - 1 : 0);
|
|
+ rt2800_register_write(rt2x00dev, MAC_BSSID_DW1, bssid_dw1);
|
|
+}
|
|
+
|
|
void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
|
|
{
|
|
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
|
|
@@ -1003,6 +1037,12 @@ void rt2800_write_beacon(struct queue_en
|
|
|
|
rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data,
|
|
entry->skb->len + padding_len);
|
|
+ __set_bit(ENTRY_BCN_ENABLED, &entry->flags);
|
|
+
|
|
+ /*
|
|
+ * Change global beacons settings.
|
|
+ */
|
|
+ rt2800_update_beacons_setup(rt2x00dev);
|
|
|
|
/*
|
|
* Restore beaconing state.
|
|
@@ -1053,8 +1093,13 @@ void rt2800_clear_beacon(struct queue_en
|
|
* Clear beacon.
|
|
*/
|
|
rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx);
|
|
+ __clear_bit(ENTRY_BCN_ENABLED, &entry->flags);
|
|
|
|
/*
|
|
+ * Change global beacons settings.
|
|
+ */
|
|
+ rt2800_update_beacons_setup(rt2x00dev);
|
|
+ /*
|
|
* Restore beaconing state.
|
|
*/
|
|
rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg);
|
|
@@ -1556,7 +1601,7 @@ void rt2800_config_intf(struct rt2x00_de
|
|
if (!is_zero_ether_addr((const u8 *)conf->bssid)) {
|
|
reg = le32_to_cpu(conf->bssid[1]);
|
|
rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_ID_MASK, 3);
|
|
- rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_BCN_NUM, 7);
|
|
+ rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_BCN_NUM, 0);
|
|
conf->bssid[1] = cpu_to_le32(reg);
|
|
}
|
|
|
|
@@ -4517,28 +4562,6 @@ static int rt2800_init_registers(struct
|
|
if (ret)
|
|
return ret;
|
|
|
|
- rt2800_register_read(rt2x00dev, BCN_OFFSET0, ®);
|
|
- rt2x00_set_field32(®, BCN_OFFSET0_BCN0,
|
|
- rt2800_get_beacon_offset(rt2x00dev, 0));
|
|
- rt2x00_set_field32(®, BCN_OFFSET0_BCN1,
|
|
- rt2800_get_beacon_offset(rt2x00dev, 1));
|
|
- rt2x00_set_field32(®, BCN_OFFSET0_BCN2,
|
|
- rt2800_get_beacon_offset(rt2x00dev, 2));
|
|
- rt2x00_set_field32(®, BCN_OFFSET0_BCN3,
|
|
- rt2800_get_beacon_offset(rt2x00dev, 3));
|
|
- rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg);
|
|
-
|
|
- rt2800_register_read(rt2x00dev, BCN_OFFSET1, ®);
|
|
- rt2x00_set_field32(®, BCN_OFFSET1_BCN4,
|
|
- rt2800_get_beacon_offset(rt2x00dev, 4));
|
|
- rt2x00_set_field32(®, BCN_OFFSET1_BCN5,
|
|
- rt2800_get_beacon_offset(rt2x00dev, 5));
|
|
- rt2x00_set_field32(®, BCN_OFFSET1_BCN6,
|
|
- rt2800_get_beacon_offset(rt2x00dev, 6));
|
|
- rt2x00_set_field32(®, BCN_OFFSET1_BCN7,
|
|
- rt2800_get_beacon_offset(rt2x00dev, 7));
|
|
- rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg);
|
|
-
|
|
rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f);
|
|
rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
|
|
|
|
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
|
|
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
|
|
@@ -141,8 +141,11 @@ static void rt2x00lib_intf_scheduled_ite
|
|
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
|
|
return;
|
|
|
|
- if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags))
|
|
+ if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags)) {
|
|
+ mutex_lock(&intf->beacon_skb_mutex);
|
|
rt2x00queue_update_beacon(rt2x00dev, vif);
|
|
+ mutex_unlock(&intf->beacon_skb_mutex);
|
|
+ }
|
|
}
|
|
|
|
static void rt2x00lib_intf_scheduled(struct work_struct *work)
|
|
@@ -216,7 +219,7 @@ static void rt2x00lib_beaconupdate_iter(
|
|
* never be called for USB devices.
|
|
*/
|
|
WARN_ON(rt2x00_is_usb(rt2x00dev));
|
|
- rt2x00queue_update_beacon_locked(rt2x00dev, vif);
|
|
+ rt2x00queue_update_beacon(rt2x00dev, vif);
|
|
}
|
|
|
|
void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
|
|
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
|
|
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
|
|
@@ -624,25 +624,24 @@ void rt2x00mac_bss_info_changed(struct i
|
|
* Start/stop beaconing.
|
|
*/
|
|
if (changes & BSS_CHANGED_BEACON_ENABLED) {
|
|
+ mutex_lock(&intf->beacon_skb_mutex);
|
|
if (!bss_conf->enable_beacon && intf->enable_beacon) {
|
|
rt2x00dev->intf_beaconing--;
|
|
intf->enable_beacon = false;
|
|
- /*
|
|
- * Clear beacon in the H/W for this vif. This is needed
|
|
- * to disable beaconing on this particular interface
|
|
- * and keep it running on other interfaces.
|
|
- */
|
|
- rt2x00queue_clear_beacon(rt2x00dev, vif);
|
|
|
|
if (rt2x00dev->intf_beaconing == 0) {
|
|
/*
|
|
* Last beaconing interface disabled
|
|
* -> stop beacon queue.
|
|
*/
|
|
- mutex_lock(&intf->beacon_skb_mutex);
|
|
rt2x00queue_stop_queue(rt2x00dev->bcn);
|
|
- mutex_unlock(&intf->beacon_skb_mutex);
|
|
}
|
|
+ /*
|
|
+ * Clear beacon in the H/W for this vif. This is needed
|
|
+ * to disable beaconing on this particular interface
|
|
+ * and keep it running on other interfaces.
|
|
+ */
|
|
+ rt2x00queue_clear_beacon(rt2x00dev, vif);
|
|
} else if (bss_conf->enable_beacon && !intf->enable_beacon) {
|
|
rt2x00dev->intf_beaconing++;
|
|
intf->enable_beacon = true;
|
|
@@ -658,11 +657,10 @@ void rt2x00mac_bss_info_changed(struct i
|
|
* First beaconing interface enabled
|
|
* -> start beacon queue.
|
|
*/
|
|
- mutex_lock(&intf->beacon_skb_mutex);
|
|
rt2x00queue_start_queue(rt2x00dev->bcn);
|
|
- mutex_unlock(&intf->beacon_skb_mutex);
|
|
}
|
|
}
|
|
+ mutex_unlock(&intf->beacon_skb_mutex);
|
|
}
|
|
|
|
/*
|
|
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
|
|
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
|
|
@@ -754,8 +754,6 @@ int rt2x00queue_clear_beacon(struct rt2x
|
|
if (unlikely(!intf->beacon))
|
|
return -ENOBUFS;
|
|
|
|
- mutex_lock(&intf->beacon_skb_mutex);
|
|
-
|
|
/*
|
|
* Clean up the beacon skb.
|
|
*/
|
|
@@ -768,13 +766,11 @@ int rt2x00queue_clear_beacon(struct rt2x
|
|
if (rt2x00dev->ops->lib->clear_beacon)
|
|
rt2x00dev->ops->lib->clear_beacon(intf->beacon);
|
|
|
|
- mutex_unlock(&intf->beacon_skb_mutex);
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
-int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev,
|
|
- struct ieee80211_vif *vif)
|
|
+int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
|
|
+ struct ieee80211_vif *vif)
|
|
{
|
|
struct rt2x00_intf *intf = vif_to_intf(vif);
|
|
struct skb_frame_desc *skbdesc;
|
|
@@ -815,19 +811,6 @@ int rt2x00queue_update_beacon_locked(str
|
|
|
|
}
|
|
|
|
-int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
|
|
- struct ieee80211_vif *vif)
|
|
-{
|
|
- struct rt2x00_intf *intf = vif_to_intf(vif);
|
|
- int ret;
|
|
-
|
|
- mutex_lock(&intf->beacon_skb_mutex);
|
|
- ret = rt2x00queue_update_beacon_locked(rt2x00dev, vif);
|
|
- mutex_unlock(&intf->beacon_skb_mutex);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
bool rt2x00queue_for_each_entry(struct data_queue *queue,
|
|
enum queue_index start,
|
|
enum queue_index end,
|
|
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
|
|
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
|
|
@@ -353,6 +353,7 @@ struct txentry_desc {
|
|
*/
|
|
enum queue_entry_flags {
|
|
ENTRY_BCN_ASSIGNED,
|
|
+ ENTRY_BCN_ENABLED,
|
|
ENTRY_OWNER_DEVICE_DATA,
|
|
ENTRY_DATA_PENDING,
|
|
ENTRY_DATA_IO_FAILED,
|
|
--- a/drivers/net/wireless/ath/ath9k/main.c
|
|
+++ b/drivers/net/wireless/ath/ath9k/main.c
|
|
@@ -1757,7 +1757,6 @@ out:
|
|
void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif)
|
|
{
|
|
struct ath_vif *avp = (void *)vif->drv_priv;
|
|
- unsigned long flags;
|
|
u32 tsf;
|
|
|
|
if (!sc->p2p_ps_timer)
|
|
@@ -1767,14 +1766,9 @@ void ath9k_update_p2p_ps(struct ath_soft
|
|
return;
|
|
|
|
sc->p2p_ps_vif = avp;
|
|
-
|
|
- spin_lock_irqsave(&sc->sc_pm_lock, flags);
|
|
- if (!(sc->ps_flags & PS_BEACON_SYNC)) {
|
|
- tsf = ath9k_hw_gettsf32(sc->sc_ah);
|
|
- ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf);
|
|
- ath9k_update_p2p_ps_timer(sc, avp);
|
|
- }
|
|
- spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
|
|
+ tsf = ath9k_hw_gettsf32(sc->sc_ah);
|
|
+ ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf);
|
|
+ ath9k_update_p2p_ps_timer(sc, avp);
|
|
}
|
|
|
|
static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
|
|
@@ -1791,6 +1785,7 @@ static void ath9k_bss_info_changed(struc
|
|
struct ath_hw *ah = sc->sc_ah;
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
|
struct ath_vif *avp = (void *)vif->drv_priv;
|
|
+ unsigned long flags;
|
|
int slottime;
|
|
|
|
ath9k_ps_wakeup(sc);
|
|
@@ -1853,7 +1848,10 @@ static void ath9k_bss_info_changed(struc
|
|
|
|
if (changed & BSS_CHANGED_P2P_PS) {
|
|
spin_lock_bh(&sc->sc_pcu_lock);
|
|
- ath9k_update_p2p_ps(sc, vif);
|
|
+ spin_lock_irqsave(&sc->sc_pm_lock, flags);
|
|
+ if (!(sc->ps_flags & PS_BEACON_SYNC))
|
|
+ ath9k_update_p2p_ps(sc, vif);
|
|
+ spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
|
|
spin_unlock_bh(&sc->sc_pcu_lock);
|
|
}
|
|
|