wlan: Add SNR monitoring support
Add support to retrieve SNR from the received frame
and return it to the user space.
CRs-Fixed: 529156
Change-Id: Ic63c41e6bc2d9402dc42554e96637dd829a59a53
diff --git a/CORE/HDD/src/wlan_hdd_cfg.c b/CORE/HDD/src/wlan_hdd_cfg.c
index 26de48d..5212cd3 100644
--- a/CORE/HDD/src/wlan_hdd_cfg.c
+++ b/CORE/HDD/src/wlan_hdd_cfg.c
@@ -2362,6 +2362,14 @@
CFG_ANTENNA_DIVERSITY_PARAM_DEFAULT,
CFG_ANTENNA_DIVERSITY_PARAM_MIN,
CFG_ANTENNA_DIVERSITY_PARAM_MAX),
+
+ REG_VARIABLE( CFG_ENABLE_SNR_MONITORING_NAME, WLAN_PARAM_Integer,
+ hdd_config_t, fEnableSNRMonitoring,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK ,
+ CFG_ENABLE_SNR_MONITORING_DEFAULT,
+ CFG_ENABLE_SNR_MONITORING_MIN,
+ CFG_ENABLE_SNR_MONITORING_MAX),
+
};
/*
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 9c05427..45a5875 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -8236,6 +8236,107 @@
}
#endif /* CONFIG_NL80211_TESTMODE */
+static int wlan_hdd_cfg80211_dump_survey(struct wiphy *wiphy,
+ struct net_device *dev,
+ int idx, struct survey_info *survey)
+{
+ hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
+ hdd_context_t *pHddCtx;
+ tHalHandle halHandle;
+ v_U32_t channel, freq;
+ v_S7_t snr,rssi;
+ int status, i, j, filled = 0;
+
+ ENTER();
+
+
+ if (NULL == pAdapter)
+ {
+ VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
+ "%s: HDD adapter is Null", __func__);
+ return -ENODEV;
+ }
+
+ if (NULL == wiphy)
+ {
+ VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
+ "%s: wiphy is Null", __func__);
+ return -ENODEV;
+ }
+
+ pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+ status = wlan_hdd_validate_context(pHddCtx);
+
+ if (0 != status)
+ {
+ VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+ "%s: HDD context is not valid", __func__);
+ return status;
+ }
+
+ if (0 == pHddCtx->cfg_ini->fEnableSNRMonitoring ||
+ 0 != pAdapter->survey_idx)
+ {
+ /* The survey dump ops when implemented completely is expected to
+ * return a survey of all channels and the ops is called by the
+ * kernel with incremental values of the argument 'idx' till it
+ * returns -ENONET. But we can only support the survey for the
+ * operating channel for now. survey_idx is used to track
+ * that the ops is called only once and then return -ENONET for
+ * the next iteration
+ */
+ pAdapter->survey_idx = 0;
+ return -ENONET;
+ }
+
+ halHandle = WLAN_HDD_GET_HAL_CTX(pAdapter);
+
+ wlan_hdd_get_snr(pAdapter, &snr);
+ wlan_hdd_get_rssi(pAdapter, &rssi);
+
+ sme_GetOperationChannel(halHandle, &channel, pAdapter->sessionId);
+ hdd_wlan_get_freq(channel, &freq);
+
+
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++)
+ {
+ if (NULL == wiphy->bands[i])
+ {
+ VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
+ "%s: wiphy->bands[i] is NULL, i = %d", __func__, i);
+ continue;
+ }
+
+ for (j = 0; j < wiphy->bands[i]->n_channels; j++)
+ {
+ struct ieee80211_supported_band *band = wiphy->bands[i];
+
+ if (band->channels[j].center_freq == (v_U16_t)freq)
+ {
+ survey->channel = &band->channels[j];
+ /* The Rx BDs contain SNR values in dB for the received frames
+ * while the supplicant expects noise. So we calculate and
+ * return the value of noise (dBm)
+ * SNR (dB) = RSSI (dBm) - NOISE (dBm)
+ */
+ survey->noise = rssi - snr;
+ survey->filled = SURVEY_INFO_NOISE_DBM;
+ filled = 1;
+ }
+ }
+ }
+
+ if (filled)
+ pAdapter->survey_idx = 1;
+ else
+ {
+ pAdapter->survey_idx = 0;
+ return -ENONET;
+ }
+
+ return 0;
+}
+
/* cfg80211_ops */
static struct cfg80211_ops wlan_hdd_cfg80211_ops =
{
@@ -8303,5 +8404,6 @@
#ifdef WLAN_NL80211_TESTMODE
.testmode_cmd = wlan_hdd_cfg80211_testmode,
#endif
+ .dump_survey = wlan_hdd_cfg80211_dump_survey,
};
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index b868e32..fd7e923 100644
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -5577,6 +5577,21 @@
pHddCtx->cfg_ini->nBandCapability = 1;
}
}
+
+ /* If SNR Monitoring is enabled, FW has to parse all beacons
+ * for calcaluting and storing the average SNR, so set Nth beacon
+ * filter to 1 to enable FW to parse all the beaocons
+ */
+ if (1 == pHddCtx->cfg_ini->fEnableSNRMonitoring)
+ {
+ /* The log level is deliberately set to WARN as overriding
+ * nthBeaconFilter to 1 will increase power cosumption and this
+ * might just prove helpful to detect the power issue.
+ */
+ hddLog(VOS_TRACE_LEVEL_WARN,
+ "%s: Setting pHddCtx->cfg_ini->nthBeaconFilter = 1", __func__);
+ pHddCtx->cfg_ini->nthBeaconFilter = 1;
+ }
/*
* cfg80211: Initialization and registration ...
*/
diff --git a/CORE/HDD/src/wlan_hdd_wext.c b/CORE/HDD/src/wlan_hdd_wext.c
index b50c0d3..55b2c35 100644
--- a/CORE/HDD/src/wlan_hdd_wext.c
+++ b/CORE/HDD/src/wlan_hdd_wext.c
@@ -140,7 +140,8 @@
#define STATS_CONTEXT_MAGIC 0x53544154 //STAT
#define RSSI_CONTEXT_MAGIC 0x52535349 //RSSI
-#define POWER_CONTEXT_MAGIC 0x504F5752 // POWR
+#define POWER_CONTEXT_MAGIC 0x504F5752 //POWR
+#define SNR_CONTEXT_MAGIC 0x534E5200 //SNR
/* To Validate Channel against the Frequency and Vice-Versa */
static const hdd_freq_chan_map_t freq_chan_map[] = { {2412, 1}, {2417, 2},
@@ -613,6 +614,57 @@
complete(&pStatsContext->completion);
}
+static void hdd_GetSnrCB(tANI_S8 snr, tANI_U32 staId, void *pContext)
+{
+ struct statsContext *pStatsContext;
+ hdd_adapter_t *pAdapter;
+
+ if (ioctl_debug)
+ {
+ pr_info("%s: snr [%d] STA [%d] pContext [%p]\n",
+ __func__, (int)snr, (int)staId, pContext);
+ }
+
+ if (NULL == pContext)
+ {
+ hddLog(VOS_TRACE_LEVEL_ERROR,
+ "%s: Bad param, pContext [%p]",
+ __func__, pContext);
+ return;
+ }
+
+ /* there is a race condition that exists between this callback function
+ * and the caller since the caller could time out either before or
+ * while this code is executing. we'll assume the timeout hasn't
+ * occurred, but we'll verify that right before we save our work
+ */
+
+ pStatsContext = pContext;
+ pAdapter = pStatsContext->pAdapter;
+ if ((NULL == pAdapter) || (SNR_CONTEXT_MAGIC != pStatsContext->magic))
+ {
+ /* the caller presumably timed out so there is nothing we can do */
+ hddLog(VOS_TRACE_LEVEL_WARN,
+ "%s: Invalid context, pAdapter [%p] magic [%08x]",
+ __func__, pAdapter, pStatsContext->magic);
+ if (ioctl_debug)
+ {
+ pr_info("%s: Invalid context, pAdapter [%p] magic [%08x]\n",
+ __func__, pAdapter, pStatsContext->magic);
+ }
+ return;
+ }
+
+ /* the race is on. caller could have timed out immediately after
+ * we verified the magic, but if so, caller will wait a short time
+ * for us to copy over the snr
+ */
+ pAdapter->snr = snr;
+
+ /* and notify the caller */
+ complete(&pStatsContext->completion);
+}
+
VOS_STATUS wlan_hdd_get_rssi(hdd_adapter_t *pAdapter, v_S7_t *rssi_value)
{
struct statsContext context;
@@ -680,6 +732,81 @@
return VOS_STATUS_SUCCESS;
}
+VOS_STATUS wlan_hdd_get_snr(hdd_adapter_t *pAdapter, v_S7_t *snr)
+{
+ struct statsContext context;
+ hdd_context_t *pHddCtx;
+ hdd_station_ctx_t *pHddStaCtx;
+ eHalStatus hstatus;
+ long lrc;
+ int valid;
+
+ if (NULL == pAdapter)
+ {
+ hddLog(VOS_TRACE_LEVEL_ERROR,
+ "%s: Invalid context, pAdapter", __func__);
+ return VOS_STATUS_E_FAULT;
+ }
+
+ pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+
+ valid = wlan_hdd_validate_context(pHddCtx);
+ if (0 != valid)
+ {
+ hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
+ return VOS_STATUS_E_FAULT;
+ }
+
+ pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
+ if (NULL == pHddStaCtx)
+ {
+ hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD STA context is not valid"));
+ return VOS_STATUS_E_FAULT;
+ }
+
+ init_completion(&context.completion);
+ context.pAdapter = pAdapter;
+ context.magic = SNR_CONTEXT_MAGIC;
+
+ hstatus = sme_GetSnr(pHddCtx->hHal, hdd_GetSnrCB,
+ pHddStaCtx->conn_info.staId[ 0 ],
+ pHddStaCtx->conn_info.bssId,
+ &context);
+ if (eHAL_STATUS_SUCCESS != hstatus)
+ {
+ hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Unable to retrieve RSSI",
+ __func__);
+ /* we'll returned a cached value below */
+ }
+ else
+ {
+ /* request was sent -- wait for the response */
+ lrc = wait_for_completion_interruptible_timeout(&context.completion,
+ msecs_to_jiffies(WLAN_WAIT_TIME_STATS));
+ /* either we have a response or we timed out
+ * either way, first invalidate our magic
+ */
+ context.magic = 0;
+ if (lrc <= 0)
+ {
+ hddLog(VOS_TRACE_LEVEL_ERROR,"%s: SME %s while retrieving SNR ",
+ __func__, (0 == lrc) ? "timeout" : "interrupt");
+ /* there is a race condition such that the callback
+ * function could be executing at the same time we are. Of
+ * primary concern is if the callback function had already
+ * verified the "magic" but hasn't yet set the completion
+ * variable. Since the completion variable is on our
+ * stack, we'll delay just a bit to make sure the data is
+ * still valid if that is the case
+ */
+ msleep(50);
+ /* we'll now returned a cached value below */
+ }
+ }
+ *snr = pAdapter->snr;
+
+ return VOS_STATUS_SUCCESS;
+}
#if defined WLAN_FEATURE_VOWIFI_11R || defined FEATURE_WLAN_CCX || defined(FEATURE_WLAN_LFR)
static void hdd_GetRoamRssiCB( v_S7_t rssi, tANI_U32 staId, void *pContext )