Skip to content

Commit

Permalink
rt2x00: Refactor beacon code to make use of start- and stop_queue
Browse files Browse the repository at this point in the history
This patch allows to dynamically remove beaconing interfaces without
shutting beaconing down on all interfaces.

The only place to start and stop beaconing are now the start- and
stop_queue callbacks. Hence, we can remove some register writes during
interface bring up (config_intf) and only write the correct sync mode
to the register there.

When multiple beaconing interfaces are present we should enable
beaconing as soon as mac80211 enables beaconing on at least one of
them. The beacon queue gets stopped when the last beaconing
interface was stopped by mac80211. Therefore, introduce another
interface counter to keep track ot the number of enabled beaconing
interfaces and start or stop the beacon queue accordingly.

To allow single interfaces to stop beaconing, add a new driver
callback clear_beacon to clear a single interface's beacon without
affecting the other interfaces. Don't overload the clear_entry callback
for clearing beacons as that would introduce additional overhead
(check for each TX queue) into the clear_entry callback which is used
on the drivers TX/RX hotpaths.

Furthermore, the write beacon callback doesn't need to enable beaconing
anymore but since beaconing should be disabled while a new beacon is
written or cleared we still disable beacon generation and enable it
afterwards again in the driver specific callbacks. However, beacon
related interrupts should not be disabled/enabled here, that's solely
done from the start- and stop queue callbacks. It would be nice to stop
the beacon queue just before the beacon update and enable it afterwards
in rt2x00queue itself instead of the current implementation that relies
on the driver doing the right thing. However, since start- and
stop_queue are mutex protected we cannot use them for atomic beacon
updates.

Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com>
Acked-by: Gertjan van Wingerde <gwingerde@gmail.com>
Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Helmut Schaa authored and John W. Linville committed Jan 31, 2011
1 parent d828cd5 commit 69cf36a
Show file tree
Hide file tree
Showing 14 changed files with 185 additions and 85 deletions.
4 changes: 0 additions & 4 deletions drivers/net/wireless/rt2x00/rt2400pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,7 @@ static void rt2400pci_config_intf(struct rt2x00_dev *rt2x00dev,
* Enable synchronisation.
*/
rt2x00pci_register_read(rt2x00dev, CSR14, &reg);
rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1);
rt2x00_set_field32(&reg, CSR14_TSF_SYNC, conf->sync);
rt2x00_set_field32(&reg, CSR14_TBCN, 1);
rt2x00pci_register_write(rt2x00dev, CSR14, reg);
}

Expand Down Expand Up @@ -1183,8 +1181,6 @@ static void rt2400pci_write_beacon(struct queue_entry *entry,
/*
* Enable beaconing again.
*/
rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1);
rt2x00_set_field32(&reg, CSR14_TBCN, 1);
rt2x00_set_field32(&reg, CSR14_BEACON_GEN, 1);
rt2x00pci_register_write(rt2x00dev, CSR14, reg);
}
Expand Down
4 changes: 0 additions & 4 deletions drivers/net/wireless/rt2x00/rt2500pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,7 @@ static void rt2500pci_config_intf(struct rt2x00_dev *rt2x00dev,
* Enable synchronisation.
*/
rt2x00pci_register_read(rt2x00dev, CSR14, &reg);
rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1);
rt2x00_set_field32(&reg, CSR14_TSF_SYNC, conf->sync);
rt2x00_set_field32(&reg, CSR14_TBCN, 1);
rt2x00pci_register_write(rt2x00dev, CSR14, reg);
}

Expand Down Expand Up @@ -1337,8 +1335,6 @@ static void rt2500pci_write_beacon(struct queue_entry *entry,
/*
* Enable beaconing again.
*/
rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1);
rt2x00_set_field32(&reg, CSR14_TBCN, 1);
rt2x00_set_field32(&reg, CSR14_BEACON_GEN, 1);
rt2x00pci_register_write(rt2x00dev, CSR14, reg);
}
Expand Down
2 changes: 0 additions & 2 deletions drivers/net/wireless/rt2x00/rt2500usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -478,9 +478,7 @@ static void rt2500usb_config_intf(struct rt2x00_dev *rt2x00dev,
rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg);

rt2500usb_register_read(rt2x00dev, TXRX_CSR19, &reg);
rt2x00_set_field16(&reg, TXRX_CSR19_TSF_COUNT, 1);
rt2x00_set_field16(&reg, TXRX_CSR19_TSF_SYNC, conf->sync);
rt2x00_set_field16(&reg, TXRX_CSR19_TBCN, 1);
rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
}

Expand Down
67 changes: 37 additions & 30 deletions drivers/net/wireless/rt2x00/rt2800lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -818,8 +818,6 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
/*
* Enable beaconing again.
*/
rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);

Expand All @@ -831,8 +829,8 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
}
EXPORT_SYMBOL_GPL(rt2800_write_beacon);

static inline void rt2800_clear_beacon(struct rt2x00_dev *rt2x00dev,
unsigned int beacon_base)
static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev,
unsigned int beacon_base)
{
int i;

Expand All @@ -845,6 +843,33 @@ static inline void rt2800_clear_beacon(struct rt2x00_dev *rt2x00dev,
rt2800_register_write(rt2x00dev, beacon_base + i, 0);
}

void rt2800_clear_beacon(struct queue_entry *entry)
{
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
u32 reg;

/*
* Disable beaconing while we are reloading the beacon data,
* otherwise we might be sending out invalid data.
*/
rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 0);
rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);

/*
* Clear beacon.
*/
rt2800_clear_beacon_register(rt2x00dev,
HW_BEACON_OFFSET(entry->entry_idx));

/*
* Enabled beaconing again.
*/
rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
}
EXPORT_SYMBOL_GPL(rt2800_clear_beacon);

#ifdef CONFIG_RT2X00_LIB_DEBUGFS
const struct rt2x00debug rt2800_rt2x00debug = {
.owner = THIS_MODULE,
Expand Down Expand Up @@ -1154,30 +1179,12 @@ void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
bool update_bssid = false;

if (flags & CONFIG_UPDATE_TYPE) {
/*
* Clear current synchronisation setup.
*/
rt2800_clear_beacon(rt2x00dev,
HW_BEACON_OFFSET(intf->beacon->entry_idx));
/*
* Enable synchronisation.
*/
rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_SYNC, conf->sync);
rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE,
(conf->sync == TSF_SYNC_ADHOC ||
conf->sync == TSF_SYNC_AP_NONE));
rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);

/*
* Enable pre tbtt interrupt for beaconing modes
*/
rt2800_register_read(rt2x00dev, INT_TIMER_EN, &reg);
rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER,
(conf->sync == TSF_SYNC_AP_NONE));
rt2800_register_write(rt2x00dev, INT_TIMER_EN, reg);

}

if (flags & CONFIG_UPDATE_MAC) {
Expand Down Expand Up @@ -2187,14 +2194,14 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
/*
* Clear all beacons
*/
rt2800_clear_beacon(rt2x00dev, HW_BEACON_BASE0);
rt2800_clear_beacon(rt2x00dev, HW_BEACON_BASE1);
rt2800_clear_beacon(rt2x00dev, HW_BEACON_BASE2);
rt2800_clear_beacon(rt2x00dev, HW_BEACON_BASE3);
rt2800_clear_beacon(rt2x00dev, HW_BEACON_BASE4);
rt2800_clear_beacon(rt2x00dev, HW_BEACON_BASE5);
rt2800_clear_beacon(rt2x00dev, HW_BEACON_BASE6);
rt2800_clear_beacon(rt2x00dev, HW_BEACON_BASE7);
rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE0);
rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE1);
rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE2);
rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE3);
rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE4);
rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE5);
rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE6);
rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE7);

if (rt2x00_is_usb(rt2x00dev)) {
rt2800_register_read(rt2x00dev, US_CYC_CNT, &reg);
Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/rt2x00/rt2800lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ void rt2800_txdone(struct rt2x00_dev *rt2x00dev);
void rt2800_txdone_entry(struct queue_entry *entry, u32 status);

void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc);
void rt2800_clear_beacon(struct queue_entry *entry);

extern const struct rt2x00debug rt2800_rt2x00debug;

Expand Down
9 changes: 9 additions & 0 deletions drivers/net/wireless/rt2x00/rt2800pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ static void rt2800pci_start_queue(struct data_queue *queue)
rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);

rt2800_register_read(rt2x00dev, INT_TIMER_EN, &reg);
rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER, 1);
rt2800_register_write(rt2x00dev, INT_TIMER_EN, reg);
break;
default:
break;
Expand Down Expand Up @@ -250,6 +254,10 @@ static void rt2800pci_stop_queue(struct data_queue *queue)
rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 0);
rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 0);
rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);

rt2800_register_read(rt2x00dev, INT_TIMER_EN, &reg);
rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER, 0);
rt2800_register_write(rt2x00dev, INT_TIMER_EN, reg);
break;
default:
break;
Expand Down Expand Up @@ -974,6 +982,7 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
.write_tx_desc = rt2800pci_write_tx_desc,
.write_tx_data = rt2800_write_tx_data,
.write_beacon = rt2800_write_beacon,
.clear_beacon = rt2800_clear_beacon,
.fill_rxdone = rt2800pci_fill_rxdone,
.config_shared_key = rt2800_config_shared_key,
.config_pairwise_key = rt2800_config_pairwise_key,
Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/rt2x00/rt2800usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
.write_tx_desc = rt2800usb_write_tx_desc,
.write_tx_data = rt2800usb_write_tx_data,
.write_beacon = rt2800_write_beacon,
.clear_beacon = rt2800_clear_beacon,
.get_tx_data_len = rt2800usb_get_tx_data_len,
.fill_rxdone = rt2800usb_fill_rxdone,
.config_shared_key = rt2800_config_shared_key,
Expand Down
4 changes: 4 additions & 0 deletions drivers/net/wireless/rt2x00/rt2x00.h
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ struct rt2x00_intf {
* dedicated beacon entry.
*/
struct queue_entry *beacon;
bool enable_beacon;

/*
* Actions that needed rescheduling.
Expand Down Expand Up @@ -573,6 +574,7 @@ struct rt2x00lib_ops {
struct txentry_desc *txdesc);
void (*write_beacon) (struct queue_entry *entry,
struct txentry_desc *txdesc);
void (*clear_beacon) (struct queue_entry *entry);
int (*get_tx_data_len) (struct queue_entry *entry);

/*
Expand Down Expand Up @@ -788,10 +790,12 @@ struct rt2x00_dev {
* - Open ap interface count.
* - Open sta interface count.
* - Association count.
* - Beaconing enabled count.
*/
unsigned int intf_ap_count;
unsigned int intf_sta_count;
unsigned int intf_associated;
unsigned int intf_beaconing;

/*
* Link quality
Expand Down
4 changes: 2 additions & 2 deletions drivers/net/wireless/rt2x00/rt2x00dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac,
return;

if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags))
rt2x00queue_update_beacon(rt2x00dev, vif, true);
rt2x00queue_update_beacon(rt2x00dev, vif);
}

static void rt2x00lib_intf_scheduled(struct work_struct *work)
Expand Down Expand Up @@ -174,7 +174,7 @@ static void rt2x00lib_beaconupdate_iter(void *data, u8 *mac,
vif->type != NL80211_IFTYPE_WDS)
return;

rt2x00queue_update_beacon(rt2x00dev, vif, true);
rt2x00queue_update_beacon(rt2x00dev, vif);
}

void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
Expand Down
12 changes: 9 additions & 3 deletions drivers/net/wireless/rt2x00/rt2x00lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,17 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
* rt2x00queue_update_beacon - Send new beacon from mac80211 to hardware
* @rt2x00dev: Pointer to &struct rt2x00_dev.
* @vif: Interface for which the beacon should be updated.
* @enable_beacon: Enable beaconing
*/
int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
struct ieee80211_vif *vif,
const bool enable_beacon);
struct ieee80211_vif *vif);

/**
* rt2x00queue_clear_beacon - Clear beacon in hardware
* @rt2x00dev: Pointer to &struct rt2x00_dev.
* @vif: Interface for which the beacon should be updated.
*/
int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev,
struct ieee80211_vif *vif);

/**
* rt2x00queue_index_inc - Index incrementation function
Expand Down
41 changes: 38 additions & 3 deletions drivers/net/wireless/rt2x00/rt2x00mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -619,9 +619,44 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
/*
* Update the beacon.
*/
if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED))
rt2x00queue_update_beacon(rt2x00dev, vif,
bss_conf->enable_beacon);
if (changes & BSS_CHANGED_BEACON)
rt2x00queue_update_beacon(rt2x00dev, vif);

/*
* Start/stop beaconing.
*/
if (changes & BSS_CHANGED_BEACON_ENABLED) {
if (!bss_conf->enable_beacon && intf->enable_beacon) {
rt2x00queue_clear_beacon(rt2x00dev, vif);
rt2x00dev->intf_beaconing--;
intf->enable_beacon = false;

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);
}


} else if (bss_conf->enable_beacon && !intf->enable_beacon) {
rt2x00dev->intf_beaconing++;
intf->enable_beacon = true;

if (rt2x00dev->intf_beaconing == 1) {
/*
* First beaconing interface enabled
* -> start beacon queue.
*/
mutex_lock(&intf->beacon_skb_mutex);
rt2x00queue_start_queue(rt2x00dev->bcn);
mutex_unlock(&intf->beacon_skb_mutex);
}
}
}

/*
* When the association status has changed we must reset the link
Expand Down
38 changes: 29 additions & 9 deletions drivers/net/wireless/rt2x00/rt2x00queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,35 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
return 0;
}

int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev,
struct ieee80211_vif *vif)
{
struct rt2x00_intf *intf = vif_to_intf(vif);

if (unlikely(!intf->beacon))
return -ENOBUFS;

mutex_lock(&intf->beacon_skb_mutex);

/*
* Clean up the beacon skb.
*/
rt2x00queue_free_skb(intf->beacon);

/*
* Clear beacon (single bssid devices don't need to clear the beacon
* since the beacon queue will get stopped anyway).
*/
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(struct rt2x00_dev *rt2x00dev,
struct ieee80211_vif *vif,
const bool enable_beacon)
struct ieee80211_vif *vif)
{
struct rt2x00_intf *intf = vif_to_intf(vif);
struct skb_frame_desc *skbdesc;
Expand All @@ -584,12 +610,6 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
*/
rt2x00queue_free_skb(intf->beacon);

if (!enable_beacon) {
rt2x00queue_stop_queue(intf->beacon->queue);
mutex_unlock(&intf->beacon_skb_mutex);
return 0;
}

intf->beacon->skb = ieee80211_beacon_get(rt2x00dev->hw, vif);
if (!intf->beacon->skb) {
mutex_unlock(&intf->beacon_skb_mutex);
Expand All @@ -611,7 +631,7 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
skbdesc->entry = intf->beacon;

/*
* Send beacon to hardware and enable beacon genaration..
* Send beacon to hardware.
*/
rt2x00dev->ops->lib->write_beacon(intf->beacon, &txdesc);

Expand Down
Loading

0 comments on commit 69cf36a

Please sign in to comment.