| --- 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) |
| { |
| @@ -12225,26 +12239,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, |