ath10k: implement get_survey()
authorMichal Kazior <michal.kazior@tieto.com>
Wed, 31 Jul 2013 08:32:40 +0000 (10:32 +0200)
committerKalle Valo <kvalo@qca.qualcomm.com>
Fri, 2 Aug 2013 06:35:21 +0000 (09:35 +0300)
This implements a limited subset of what can be
reported in the survey dump.

This can be used for assessing approximate channel
load, e.g. for automatic channel selection.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.h

index ff42bb744d9ea666db9e47ecda8c45f44bf56601..e4bba563ed4273613e58332e45c4146c04818516 100644 (file)
@@ -38,6 +38,7 @@
 #define ATH10K_SCAN_ID 0
 #define WMI_READY_TIMEOUT (5 * HZ)
 #define ATH10K_FLUSH_TIMEOUT_HZ (5*HZ)
+#define ATH10K_NUM_CHANS 38
 
 /* Antenna noise floor */
 #define ATH10K_DEFAULT_NOISE_FLOOR -95
@@ -375,6 +376,12 @@ struct ath10k {
 
        struct work_struct restart_work;
 
+       /* cycle count is reported twice for each visited channel during scan.
+        * access protected by data_lock */
+       u32 survey_last_rx_clear_count;
+       u32 survey_last_cycle_count;
+       struct survey_info survey[ATH10K_NUM_CHANS];
+
 #ifdef CONFIG_ATH10K_DEBUGFS
        struct ath10k_debug debug;
 #endif
index 57843377fa0b74ae7d6f633a4bf85ab1c162e1be..9ea31f89b7487b76ae8c61760ac83515153ae064 100644 (file)
@@ -2934,6 +2934,41 @@ static void ath10k_restart_complete(struct ieee80211_hw *hw)
        mutex_unlock(&ar->conf_mutex);
 }
 
+static int ath10k_get_survey(struct ieee80211_hw *hw, int idx,
+                            struct survey_info *survey)
+{
+       struct ath10k *ar = hw->priv;
+       struct ieee80211_supported_band *sband;
+       struct survey_info *ar_survey = &ar->survey[idx];
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ];
+       if (sband && idx >= sband->n_channels) {
+               idx -= sband->n_channels;
+               sband = NULL;
+       }
+
+       if (!sband)
+               sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ];
+
+       if (!sband || idx >= sband->n_channels) {
+               ret = -ENOENT;
+               goto exit;
+       }
+
+       spin_lock_bh(&ar->data_lock);
+       memcpy(survey, ar_survey, sizeof(*survey));
+       spin_unlock_bh(&ar->data_lock);
+
+       survey->channel = &sband->channels[idx];
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
 static const struct ieee80211_ops ath10k_ops = {
        .tx                             = ath10k_tx,
        .start                          = ath10k_start,
@@ -2955,6 +2990,7 @@ static const struct ieee80211_ops ath10k_ops = {
        .flush                          = ath10k_flush,
        .tx_last_beacon                 = ath10k_tx_last_beacon,
        .restart_complete               = ath10k_restart_complete,
+       .get_survey                     = ath10k_get_survey,
 #ifdef CONFIG_PM
        .suspend                        = ath10k_suspend,
        .resume                         = ath10k_resume,
index 1cbcb2ea12f7375cc3313bdd18c35673e89f8e38..55f90c761868ddd9fa228510efb27465d360c00b 100644 (file)
@@ -390,9 +390,82 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
        return 0;
 }
 
+static int freq_to_idx(struct ath10k *ar, int freq)
+{
+       struct ieee80211_supported_band *sband;
+       int band, ch, idx = 0;
+
+       for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
+               sband = ar->hw->wiphy->bands[band];
+               if (!sband)
+                       continue;
+
+               for (ch = 0; ch < sband->n_channels; ch++, idx++)
+                       if (sband->channels[ch].center_freq == freq)
+                               goto exit;
+       }
+
+exit:
+       return idx;
+}
+
 static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
 {
-       ath10k_dbg(ATH10K_DBG_WMI, "WMI_CHAN_INFO_EVENTID\n");
+       struct wmi_chan_info_event *ev;
+       struct survey_info *survey;
+       u32 err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count;
+       int idx;
+
+       ev = (struct wmi_chan_info_event *)skb->data;
+
+       err_code = __le32_to_cpu(ev->err_code);
+       freq = __le32_to_cpu(ev->freq);
+       cmd_flags = __le32_to_cpu(ev->cmd_flags);
+       noise_floor = __le32_to_cpu(ev->noise_floor);
+       rx_clear_count = __le32_to_cpu(ev->rx_clear_count);
+       cycle_count = __le32_to_cpu(ev->cycle_count);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n",
+                  err_code, freq, cmd_flags, noise_floor, rx_clear_count,
+                  cycle_count);
+
+       spin_lock_bh(&ar->data_lock);
+
+       if (!ar->scan.in_progress) {
+               ath10k_warn("chan info event without a scan request?\n");
+               goto exit;
+       }
+
+       idx = freq_to_idx(ar, freq);
+       if (idx >= ARRAY_SIZE(ar->survey)) {
+               ath10k_warn("chan info: invalid frequency %d (idx %d out of bounds)\n",
+                           freq, idx);
+               goto exit;
+       }
+
+       if (cmd_flags & WMI_CHAN_INFO_FLAG_COMPLETE) {
+               /* During scanning chan info is reported twice for each
+                * visited channel. The reported cycle count is global
+                * and per-channel cycle count must be calculated */
+
+               cycle_count -= ar->survey_last_cycle_count;
+               rx_clear_count -= ar->survey_last_rx_clear_count;
+
+               survey = &ar->survey[idx];
+               survey->channel_time = WMI_CHAN_INFO_MSEC(cycle_count);
+               survey->channel_time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count);
+               survey->noise = noise_floor;
+               survey->filled = SURVEY_INFO_CHANNEL_TIME |
+                                SURVEY_INFO_CHANNEL_TIME_RX |
+                                SURVEY_INFO_NOISE_DBM;
+       }
+
+       ar->survey_last_rx_clear_count = rx_clear_count;
+       ar->survey_last_cycle_count = cycle_count;
+
+exit:
+       spin_unlock_bh(&ar->data_lock);
 }
 
 static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb)
index da3b2bc4c88a48eb3d937de53dd145592caba476..2c5a4f8daf2ee4b80e9a9e84391586e638389271 100644 (file)
@@ -2931,6 +2931,11 @@ struct wmi_chan_info_event {
        __le32 cycle_count;
 } __packed;
 
+#define WMI_CHAN_INFO_FLAG_COMPLETE BIT(0)
+
+/* FIXME: empirically extrapolated */
+#define WMI_CHAN_INFO_MSEC(x) ((x) / 76595)
+
 /* Beacon filter wmi command info */
 #define BCN_FLT_MAX_SUPPORTED_IES      256
 #define BCN_FLT_MAX_ELEMS_IE_LIST      (BCN_FLT_MAX_SUPPORTED_IES / 32)