--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -1238,6 +1238,8 @@ void rt2800_watchdog(struct rt2x00_dev * if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) return; + rt2800_update_survey(rt2x00dev); + queue_for_each(rt2x00dev, queue) { switch (queue->qid) { case QID_AC_VO: @@ -1274,6 +1276,18 @@ void rt2800_watchdog(struct rt2x00_dev * } EXPORT_SYMBOL_GPL(rt2800_watchdog); +void rt2800_update_survey(struct rt2x00_dev *rt2x00dev) +{ + struct ieee80211_channel *chan = rt2x00dev->hw->conf.chandef.chan; + struct rt2x00_chan_survey *chan_survey = + &rt2x00dev->chan_survey[chan->hw_value]; + + chan_survey->time_idle += rt2800_register_read(rt2x00dev, CH_IDLE_STA); + chan_survey->time_busy += rt2800_register_read(rt2x00dev, CH_BUSY_STA); + chan_survey->time_ext_busy += rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC); +} +EXPORT_SYMBOL_GPL(rt2800_update_survey); + static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev, unsigned int index) { @@ -12199,26 +12213,30 @@ int rt2800_get_survey(struct ieee80211_h { struct rt2x00_dev *rt2x00dev = hw->priv; struct ieee80211_conf *conf = &hw->conf; - u32 idle, busy, busy_ext; + struct rt2x00_chan_survey *chan_survey = + &rt2x00dev->chan_survey[idx]; + enum nl80211_band band = NL80211_BAND_2GHZ; - if (idx != 0) + if (idx >= rt2x00dev->bands[band].n_channels) { + idx -= rt2x00dev->bands[band].n_channels; + band = NL80211_BAND_5GHZ; + } + + if (idx >= rt2x00dev->bands[band].n_channels) return -ENOENT; - survey->channel = conf->chandef.chan; + if (idx == 0) + rt2800_update_survey(rt2x00dev); - idle = rt2800_register_read(rt2x00dev, CH_IDLE_STA); - busy = rt2800_register_read(rt2x00dev, CH_BUSY_STA); - busy_ext = rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC); - - if (idle || busy) { - survey->filled = SURVEY_INFO_TIME | - SURVEY_INFO_TIME_BUSY | - SURVEY_INFO_TIME_EXT_BUSY; - - survey->time = (idle + busy) / 1000; - survey->time_busy = busy / 1000; - survey->time_ext_busy = busy_ext / 1000; - } + survey->channel = &rt2x00dev->bands[band].channels[idx]; + + survey->filled = SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_EXT_BUSY; + + survey->time = div_u64(chan_survey->time_idle + chan_survey->time_busy, 1000); + survey->time_busy = div_u64(chan_survey->time_busy, 1000); + survey->time_ext_busy = div_u64(chan_survey->time_ext_busy, 1000); if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) survey->filled |= SURVEY_INFO_IN_USE; --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h @@ -243,6 +243,7 @@ bool rt2800_txstatus_timeout(struct rt2x bool rt2800_txstatus_pending(struct rt2x00_dev *rt2x00dev); void rt2800_watchdog(struct rt2x00_dev *rt2x00dev); +void rt2800_update_survey(struct rt2x00_dev *rt2x00dev); void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc); void rt2800_clear_beacon(struct queue_entry *entry); --- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c @@ -360,6 +360,7 @@ static const struct rt2x00lib_ops rt2800 .gain_calibration = rt2800_gain_calibration, .vco_calibration = rt2800_vco_calibration, .watchdog = rt2800_watchdog, + .update_survey = rt2800_update_survey, .start_queue = rt2800mmio_start_queue, .kick_queue = rt2800mmio_kick_queue, .stop_queue = rt2800mmio_stop_queue, --- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c @@ -214,6 +214,7 @@ static const struct rt2x00lib_ops rt2800 .gain_calibration = rt2800_gain_calibration, .vco_calibration = rt2800_vco_calibration, .watchdog = rt2800_watchdog, + .update_survey = rt2800_update_survey, .start_queue = rt2800mmio_start_queue, .kick_queue = rt2800mmio_kick_queue, .stop_queue = rt2800mmio_stop_queue, --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -183,6 +183,15 @@ struct rf_channel { }; /* + * Information structure for channel survey. + */ +struct rt2x00_chan_survey { + u64 time_idle; + u64 time_busy; + u64 time_ext_busy; +}; + +/* * Channel information structure */ struct channel_info { @@ -567,6 +576,7 @@ struct rt2x00lib_ops { * Data queue handlers. */ void (*watchdog) (struct rt2x00_dev *rt2x00dev); + void (*update_survey) (struct rt2x00_dev *rt2x00dev); void (*start_queue) (struct data_queue *queue); void (*kick_queue) (struct data_queue *queue); void (*stop_queue) (struct data_queue *queue); @@ -755,6 +765,7 @@ struct rt2x00_dev { */ struct ieee80211_hw *hw; struct ieee80211_supported_band bands[NUM_NL80211_BANDS]; + struct rt2x00_chan_survey *chan_survey; enum nl80211_band curr_band; int curr_freq; --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -1057,6 +1057,12 @@ static int rt2x00lib_probe_hw_modes(stru if (!rates) goto exit_free_channels; + rt2x00dev->chan_survey = + kcalloc(spec->num_channels, sizeof(struct rt2x00_chan_survey), + GFP_KERNEL); + if (!rt2x00dev->chan_survey) + goto exit_free_rates; + /* * Initialize Rate list. */ @@ -1108,6 +1114,8 @@ static int rt2x00lib_probe_hw_modes(stru return 0; + exit_free_rates: + kfree(rates); exit_free_channels: kfree(channels); rt2x00_err(rt2x00dev, "Allocation ieee80211 modes failed\n"); --- a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c @@ -317,6 +317,15 @@ int rt2x00mac_config(struct ieee80211_hw return 0; /* + * To provide correct survey data for survey-based ACS algorithm + * we have to save survey data for current channel before switching. + */ + if (rt2x00dev->ops->lib->update_survey && + (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { + rt2x00dev->ops->lib->update_survey(rt2x00dev); + } + + /* * Some configuration parameters (e.g. channel and antenna values) can * only be set when the radio is enabled, but do require the RX to * be off. During this period we should keep link tuning enabled,