carl9170: improve site survey
authorChristian Lamparter <chunkeey@googlemail.com>
Mon, 15 Aug 2011 17:50:48 +0000 (19:50 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 24 Aug 2011 18:41:42 +0000 (14:41 -0400)
The firmware keeps track of channel usage. This data can
be used by the automatic channel selection to find the
*best* channel.

Survey data from wlan22
frequency:                      2412 MHz [in use]
noise:                          -86 dBm
channel active time:            3339608 ms
channel busy time:              270982 ms
channel transmit time:          121515 ms
Survey data from wlan22
frequency:                      2417 MHz
noise:                          -86 dBm
channel active time:            70 ms
channel busy time:              2 ms
channel transmit time:          1 ms

Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/carl9170/carl9170.h
drivers/net/wireless/ath/carl9170/cmd.c
drivers/net/wireless/ath/carl9170/cmd.h
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/ath/carl9170/phy.c

index f7dbdaa74c63187d6290a352efbe8d24caf965f4..74350d63f6865eef0991c5d36b190f288fa0eca2 100644 (file)
@@ -151,6 +151,7 @@ struct carl9170_sta_tid {
 #define CARL9170_TX_TIMEOUT            2500
 #define CARL9170_JANITOR_DELAY         128
 #define CARL9170_QUEUE_STUCK_TIMEOUT   5500
+#define CARL9170_STAT_WORK             30000
 
 #define CARL9170_NUM_TX_AGG_MAX                30
 
@@ -332,11 +333,21 @@ struct ar9170 {
 
        /* PHY */
        struct ieee80211_channel *channel;
+       unsigned int num_channels;
        int noise[4];
        unsigned int chan_fail;
        unsigned int total_chan_fail;
        u8 heavy_clip;
        u8 ht_settings;
+       struct {
+               u64 active;     /* usec */
+               u64 cca;        /* usec */
+               u64 tx_time;    /* usec */
+               u64 rx_total;
+               u64 rx_overrun;
+       } tally;
+       struct delayed_work stat_work;
+       struct survey_info *survey;
 
        /* power calibration data */
        u8 power_5G_leg[4];
index cdfc94c371b41930e0fd50aca0119e877a6d0f10..9970bf8edc40923c8f614ab799ced1eb22494d22 100644 (file)
@@ -165,6 +165,37 @@ int carl9170_bcn_ctrl(struct ar9170 *ar, const unsigned int vif_id,
        return __carl9170_exec_cmd(ar, cmd, true);
 }
 
+int carl9170_collect_tally(struct ar9170 *ar)
+{
+       struct carl9170_tally_rsp tally;
+       struct survey_info *info;
+       unsigned int tick;
+       int err;
+
+       err = carl9170_exec_cmd(ar, CARL9170_CMD_TALLY, 0, NULL,
+                               sizeof(tally), (u8 *)&tally);
+       if (err)
+               return err;
+
+       tick = le32_to_cpu(tally.tick);
+       if (tick) {
+               ar->tally.active += le32_to_cpu(tally.active) / tick;
+               ar->tally.cca += le32_to_cpu(tally.cca) / tick;
+               ar->tally.tx_time += le32_to_cpu(tally.tx_time) / tick;
+               ar->tally.rx_total += le32_to_cpu(tally.rx_total);
+               ar->tally.rx_overrun += le32_to_cpu(tally.rx_overrun);
+
+               if (ar->channel) {
+                       info = &ar->survey[ar->channel->hw_value];
+
+                       info->channel_time = ar->tally.active / 1000;
+                       info->channel_time_busy = ar->tally.cca / 1000;
+                       info->channel_time_tx = ar->tally.tx_time / 1000;
+               }
+       }
+       return 0;
+}
+
 int carl9170_powersave(struct ar9170 *ar, const bool ps)
 {
        struct carl9170_cmd *cmd;
index d5f95bdc75c1cf1f1e8abafa09f3235b6faef7d4..885c42778b8b5817941c9c184904446cc91834fe 100644 (file)
@@ -50,6 +50,7 @@ int carl9170_echo_test(struct ar9170 *ar, u32 v);
 int carl9170_reboot(struct ar9170 *ar);
 int carl9170_mac_reset(struct ar9170 *ar);
 int carl9170_powersave(struct ar9170 *ar, const bool power_on);
+int carl9170_collect_tally(struct ar9170 *ar);
 int carl9170_bcn_ctrl(struct ar9170 *ar, const unsigned int vif_id,
                       const u32 mode, const u32 addr, const u32 len);
 
index 0122930b14c71f85ab0f9d779a87030373011ebb..85cb1bdebaaaa11c3d372ddcfe95440f916a6cd3 100644 (file)
@@ -413,6 +413,9 @@ static int carl9170_op_start(struct ieee80211_hw *hw)
 
        carl9170_set_state_when(ar, CARL9170_IDLE, CARL9170_STARTED);
 
+       ieee80211_queue_delayed_work(ar->hw, &ar->stat_work,
+               round_jiffies(msecs_to_jiffies(CARL9170_STAT_WORK)));
+
        ieee80211_wake_queues(ar->hw);
        err = 0;
 
@@ -423,6 +426,7 @@ out:
 
 static void carl9170_cancel_worker(struct ar9170 *ar)
 {
+       cancel_delayed_work_sync(&ar->stat_work);
        cancel_delayed_work_sync(&ar->tx_janitor);
 #ifdef CONFIG_CARL9170_LEDS
        cancel_delayed_work_sync(&ar->led_work);
@@ -794,6 +798,43 @@ static void carl9170_ps_work(struct work_struct *work)
        mutex_unlock(&ar->mutex);
 }
 
+static int carl9170_update_survey(struct ar9170 *ar, bool flush, bool noise)
+{
+       int err;
+
+       if (noise) {
+               err = carl9170_get_noisefloor(ar);
+               if (err)
+                       return err;
+       }
+
+       if (ar->fw.hw_counters) {
+               err = carl9170_collect_tally(ar);
+               if (err)
+                       return err;
+       }
+
+       if (flush)
+               memset(&ar->tally, 0, sizeof(ar->tally));
+
+       return 0;
+}
+
+static void carl9170_stat_work(struct work_struct *work)
+{
+       struct ar9170 *ar = container_of(work, struct ar9170, stat_work.work);
+       int err;
+
+       mutex_lock(&ar->mutex);
+       err = carl9170_update_survey(ar, false, true);
+       mutex_unlock(&ar->mutex);
+
+       if (err)
+               return;
+
+       ieee80211_queue_delayed_work(ar->hw, &ar->stat_work,
+               round_jiffies(msecs_to_jiffies(CARL9170_STAT_WORK)));
+}
 
 static int carl9170_op_config(struct ieee80211_hw *hw, u32 changed)
 {
@@ -828,11 +869,19 @@ static int carl9170_op_config(struct ieee80211_hw *hw, u32 changed)
                if (err)
                        goto out;
 
+               err = carl9170_update_survey(ar, true, false);
+               if (err)
+                       goto out;
+
                err = carl9170_set_channel(ar, hw->conf.channel,
                        hw->conf.channel_type, CARL9170_RFI_NONE);
                if (err)
                        goto out;
 
+               err = carl9170_update_survey(ar, false, true);
+               if (err)
+                       goto out;
+
                err = carl9170_set_dyn_sifs_ack(ar);
                if (err)
                        goto out;
@@ -1423,20 +1472,52 @@ static int carl9170_op_get_survey(struct ieee80211_hw *hw, int idx,
                                struct survey_info *survey)
 {
        struct ar9170 *ar = hw->priv;
-       int err;
+       struct ieee80211_channel *chan;
+       struct ieee80211_supported_band *band;
+       int err, b, i;
 
-       if (idx != 0)
-               return -ENOENT;
+       chan = ar->channel;
+       if (!chan)
+               return -ENODEV;
 
-       mutex_lock(&ar->mutex);
-       err = carl9170_get_noisefloor(ar);
-       mutex_unlock(&ar->mutex);
-       if (err)
-               return err;
+       if (idx == chan->hw_value) {
+               mutex_lock(&ar->mutex);
+               err = carl9170_update_survey(ar, false, true);
+               mutex_unlock(&ar->mutex);
+               if (err)
+                       return err;
+       }
 
-       survey->channel = ar->channel;
+       for (b = 0; b < IEEE80211_NUM_BANDS; b++) {
+               band = ar->hw->wiphy->bands[b];
+
+               if (!band)
+                       continue;
+
+               for (i = 0; i < band->n_channels; i++) {
+                       if (band->channels[i].hw_value == idx) {
+                               chan = &band->channels[i];
+                               goto found;
+                       }
+               }
+       }
+       return -ENOENT;
+
+found:
+       memcpy(survey, &ar->survey[idx], sizeof(*survey));
+
+       survey->channel = chan;
        survey->filled = SURVEY_INFO_NOISE_DBM;
-       survey->noise = ar->noise[0];
+
+       if (ar->channel == chan)
+               survey->filled |= SURVEY_INFO_IN_USE;
+
+       if (ar->fw.hw_counters) {
+               survey->filled |= SURVEY_INFO_CHANNEL_TIME |
+                                 SURVEY_INFO_CHANNEL_TIME_BUSY |
+                                 SURVEY_INFO_CHANNEL_TIME_TX;
+       }
+
        return 0;
 }
 
@@ -1569,6 +1650,7 @@ void *carl9170_alloc(size_t priv_size)
        INIT_WORK(&ar->ping_work, carl9170_ping_work);
        INIT_WORK(&ar->restart_work, carl9170_restart_work);
        INIT_WORK(&ar->ampdu_work, carl9170_ampdu_work);
+       INIT_DELAYED_WORK(&ar->stat_work, carl9170_stat_work);
        INIT_DELAYED_WORK(&ar->tx_janitor, carl9170_tx_janitor);
        INIT_LIST_HEAD(&ar->tx_ampdu_list);
        rcu_assign_pointer(ar->tx_ampdu_iter,
@@ -1652,6 +1734,7 @@ static int carl9170_parse_eeprom(struct ar9170 *ar)
        struct ath_regulatory *regulatory = &ar->common.regulatory;
        unsigned int rx_streams, tx_streams, tx_params = 0;
        int bands = 0;
+       int chans = 0;
 
        if (ar->eeprom.length == cpu_to_le16(0xffff))
                return -ENODATA;
@@ -1675,14 +1758,24 @@ static int carl9170_parse_eeprom(struct ar9170 *ar)
        if (ar->eeprom.operating_flags & AR9170_OPFLAG_2GHZ) {
                ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
                        &carl9170_band_2GHz;
+               chans += carl9170_band_2GHz.n_channels;
                bands++;
        }
        if (ar->eeprom.operating_flags & AR9170_OPFLAG_5GHZ) {
                ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
                        &carl9170_band_5GHz;
+               chans += carl9170_band_5GHz.n_channels;
                bands++;
        }
 
+       if (!bands)
+               return -EINVAL;
+
+       ar->survey = kzalloc(sizeof(struct survey_info) * chans, GFP_KERNEL);
+       if (!ar->survey)
+               return -ENOMEM;
+       ar->num_channels = chans;
+
        /*
         * I measured this, a bandswitch takes roughly
         * 135 ms and a frequency switch about 80.
@@ -1701,7 +1794,7 @@ static int carl9170_parse_eeprom(struct ar9170 *ar)
        /* second part of wiphy init */
        SET_IEEE80211_PERM_ADDR(ar->hw, ar->eeprom.mac_address);
 
-       return bands ? 0 : -EINVAL;
+       return 0;
 }
 
 static int carl9170_reg_notifier(struct wiphy *wiphy,
@@ -1834,6 +1927,9 @@ void carl9170_free(struct ar9170 *ar)
        kfree(ar->mem_bitmap);
        ar->mem_bitmap = NULL;
 
+       kfree(ar->survey);
+       ar->survey = NULL;
+
        mutex_destroy(&ar->mutex);
 
        ieee80211_free_hw(ar->hw);
index 8635c5c8463cf4dc8b4fd61a53fdd20e11137a2d..472efc7e34022ffbc9162afb9752069803833edd 100644 (file)
@@ -1573,6 +1573,9 @@ int carl9170_get_noisefloor(struct ar9170 *ar)
                        AR9170_PHY_EXT_CCA_MIN_PWR, phy_res[i + 2]), 8);
        }
 
+       if (ar->channel)
+               ar->survey[ar->channel->hw_value].noise = ar->noise[0];
+
        return 0;
 }
 
@@ -1765,10 +1768,6 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
                ar->chan_fail = 0;
        }
 
-       err = carl9170_get_noisefloor(ar);
-       if (err)
-               return err;
-
        if (ar->heavy_clip) {
                err = carl9170_write_reg(ar, AR9170_PHY_REG_HEAVY_CLIP_ENABLE,
                                         0x200 | ar->heavy_clip);