2014-06-05 11:53:48

by Stanislaw Gruszka

[permalink] [raw]
Subject: [PATCH 1/5] 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 <[email protected]>
---
drivers/net/wireless/rt2x00/rt2x00dev.c | 7 +++++--
drivers/net/wireless/rt2x00/rt2x00mac.c | 6 ++----
drivers/net/wireless/rt2x00/rt2x00queue.c | 21 ++-------------------
3 files changed, 9 insertions(+), 25 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 2bde672..72e3e81 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -141,8 +141,11 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac,
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(void *data, u8 *mac,
* 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)
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 212ac48..0aad22d 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -624,6 +624,7 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
* 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;
@@ -639,9 +640,7 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
* Last beaconing interface disabled
* -> stop beacon queue.
*/
- mutex_lock(&intf->beacon_skb_mutex);
rt2x00queue_stop_queue(rt2x00dev->bcn);
- mutex_unlock(&intf->beacon_skb_mutex);
}
} else if (bss_conf->enable_beacon && !intf->enable_beacon) {
rt2x00dev->intf_beaconing++;
@@ -658,11 +657,10 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
* 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);
}

/*
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 5642ccc..8e68f87 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -754,8 +754,6 @@ int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev,
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 rt2x00_dev *rt2x00dev,
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(struct rt2x00_dev *rt2x00dev,

}

-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,
--
1.8.3.1



2014-06-05 11:53:51

by Stanislaw Gruszka

[permalink] [raw]
Subject: [PATCH 3/5] 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 <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800lib.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index c45b2d3..a9cdddd 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -1601,7 +1601,7 @@ void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
if (!is_zero_ether_addr((const u8 *)conf->bssid)) {
reg = le32_to_cpu(conf->bssid[1]);
rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_ID_MASK, 3);
- rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 7);
+ rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 0);
conf->bssid[1] = cpu_to_le32(reg);
}

--
1.8.3.1


2014-06-05 11:53:55

by Stanislaw Gruszka

[permalink] [raw]
Subject: [PATCH 5/5] 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 <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800lib.c | 22 ----------------------
1 file changed, 22 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index a9cdddd..893c9d5 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -4562,28 +4562,6 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
if (ret)
return ret;

- rt2800_register_read(rt2x00dev, BCN_OFFSET0, &reg);
- rt2x00_set_field32(&reg, BCN_OFFSET0_BCN0,
- rt2800_get_beacon_offset(rt2x00dev, 0));
- rt2x00_set_field32(&reg, BCN_OFFSET0_BCN1,
- rt2800_get_beacon_offset(rt2x00dev, 1));
- rt2x00_set_field32(&reg, BCN_OFFSET0_BCN2,
- rt2800_get_beacon_offset(rt2x00dev, 2));
- rt2x00_set_field32(&reg, BCN_OFFSET0_BCN3,
- rt2800_get_beacon_offset(rt2x00dev, 3));
- rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg);
-
- rt2800_register_read(rt2x00dev, BCN_OFFSET1, &reg);
- rt2x00_set_field32(&reg, BCN_OFFSET1_BCN4,
- rt2800_get_beacon_offset(rt2x00dev, 4));
- rt2x00_set_field32(&reg, BCN_OFFSET1_BCN5,
- rt2800_get_beacon_offset(rt2x00dev, 5));
- rt2x00_set_field32(&reg, BCN_OFFSET1_BCN6,
- rt2800_get_beacon_offset(rt2x00dev, 6));
- rt2x00_set_field32(&reg, 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);

--
1.8.3.1


2014-06-05 11:53:49

by Stanislaw Gruszka

[permalink] [raw]
Subject: [PATCH 2/5] 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 <[email protected]>
Signed-off-by: Stanislaw Gruszka <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800lib.c | 45 +++++++++++++++++++++++++++++++
drivers/net/wireless/rt2x00/rt2x00queue.h | 1 +
2 files changed, 46 insertions(+)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index c17fcf2..c45b2d3 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -947,6 +947,40 @@ static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev,
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_entry *entry, struct txentry_desc *txdesc)

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_entry *entry)
* 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);
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index c48125b..2233b91 100644
--- 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,
--
1.8.3.1


2014-06-11 11:40:54

by Helmut Schaa

[permalink] [raw]
Subject: Re: [PATCH 2/5] rt2x00: change beaconing setup on RT2800

On Thu, Jun 5, 2014 at 1:52 PM, Stanislaw Gruszka <[email protected]> wrote:
> 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.

I was thinking of doing that for a long time, glad you've picked it up!
Thanks a lot Stanislaw.

> Reported-by: Matthias Fend <[email protected]>
> Signed-off-by: Stanislaw Gruszka <[email protected]>


No extensive review but still looks good to me:

Acked-by: Helmut Schaa <[email protected]>

> ---
> drivers/net/wireless/rt2x00/rt2800lib.c | 45 +++++++++++++++++++++++++++++++
> drivers/net/wireless/rt2x00/rt2x00queue.h | 1 +
> 2 files changed, 46 insertions(+)
>
> diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
> index c17fcf2..c45b2d3 100644
> --- a/drivers/net/wireless/rt2x00/rt2800lib.c
> +++ b/drivers/net/wireless/rt2x00/rt2800lib.c
> @@ -947,6 +947,40 @@ static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev,
> 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_entry *entry, struct txentry_desc *txdesc)
>
> 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_entry *entry)
> * 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);
> diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
> index c48125b..2233b91 100644
> --- 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,
> --
> 1.8.3.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2014-06-05 11:53:53

by Stanislaw Gruszka

[permalink] [raw]
Subject: [PATCH 4/5] 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 <[email protected]>
---
drivers/net/wireless/rt2x00/rt2x00mac.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 0aad22d..b144802 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -628,12 +628,6 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
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) {
/*
@@ -642,6 +636,12 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
*/
rt2x00queue_stop_queue(rt2x00dev->bcn);
}
+ /*
+ * 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;
--
1.8.3.1