qcacld-3.0: Add support to get per chain rssi stats
Propagation from qcacld-2.0 to qcalcd-3.0.
Add support to get per chain rssi from fw via
already existing WMI command WMI_REQUEST_STATS_CMDID
and then store it in the station_info structure defined
by cfg80211.
Change-Id: Id50c96dd322b5ca4db34cf2df901730d8b103251
CRs-Fixed: 1005367
diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h
index 4063495..d2cda7c 100644
--- a/core/hdd/inc/wlan_hdd_main.h
+++ b/core/hdd/inc/wlan_hdd_main.h
@@ -401,6 +401,7 @@
tCsrGlobalClassCStatsInfo ClassC_stat;
tCsrGlobalClassDStatsInfo ClassD_stat;
tCsrPerStaStatsInfo perStaStats;
+ struct csr_per_chain_rssi_stats_info per_chain_rssi_stats;
hdd_tx_rx_stats_t hddTxRxStats;
#ifdef WLAN_FEATURE_11W
hdd_pmf_stats_t hddPmfStats;
diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c
index 991b36c..8f1c09f 100644
--- a/core/hdd/src/wlan_hdd_cfg80211.c
+++ b/core/hdd/src/wlan_hdd_cfg80211.c
@@ -14031,6 +14031,7 @@
.mgmt_tx_cancel_wait = wlan_hdd_cfg80211_mgmt_tx_cancel_wait,
.set_default_mgmt_key = wlan_hdd_set_default_mgmt_key,
.set_txq_params = wlan_hdd_set_txq_params,
+ .dump_station = wlan_hdd_cfg80211_dump_station,
.get_station = wlan_hdd_cfg80211_get_station,
.set_power_mgmt = wlan_hdd_cfg80211_set_power_mgmt,
.del_station = wlan_hdd_cfg80211_del_station,
diff --git a/core/hdd/src/wlan_hdd_stats.c b/core/hdd/src/wlan_hdd_stats.c
index ff7a538..600545f 100644
--- a/core/hdd/src/wlan_hdd_stats.c
+++ b/core/hdd/src/wlan_hdd_stats.c
@@ -1650,6 +1650,9 @@
int status, mode = 0, maxHtIdx;
struct index_vht_data_rate_type *supported_vht_mcs_rate;
struct index_data_rate_type *supported_mcs_rate;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ bool rssi_stats_valid = false;
+#endif
uint32_t vht_mcs_map;
enum eDataRate11ACMaxMcs vhtMaxMcs;
@@ -2128,6 +2131,35 @@
sinfo->txrate.mcs, sinfo->txrate.flags,
sinfo->tx_packets, sinfo->rx_packets);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ sinfo->signal_avg = WLAN_HDD_TGT_NOISE_FLOOR_DBM;
+ for (i = 0; i < NUM_CHAINS_MAX; i++) {
+ sinfo->chain_signal_avg[i] =
+ pAdapter->hdd_stats.per_chain_rssi_stats.rssi[i];
+ sinfo->chains |= 1 << i;
+ if (sinfo->chain_signal_avg[i] > sinfo->signal_avg &&
+ sinfo->chain_signal_avg[i] != 0)
+ sinfo->signal_avg = sinfo->chain_signal_avg[i];
+
+ hdd_info("RSSI for chain %d, vdev_id %d is %d",
+ i, pAdapter->sessionId, sinfo->chain_signal_avg[i]);
+
+ if (!rssi_stats_valid && sinfo->chain_signal_avg[i])
+ rssi_stats_valid = true;
+ }
+
+ if (rssi_stats_valid) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS)
+ sinfo->filled |= STATION_INFO_CHAIN_SIGNAL_AVG;
+ sinfo->filled |= STATION_INFO_SIGNAL_AVG;
+#else
+ sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG);
+#endif
+ }
+#endif
+
+
MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_GET_STA,
pAdapter->sessionId, maxRate));
@@ -2164,6 +2196,54 @@
}
/**
+ * __wlan_hdd_cfg80211_dump_station() - dump station statistics
+ * @wiphy: Pointer to wiphy
+ * @dev: Pointer to network device
+ * @idx: variable to determine whether to get stats or not
+ * @mac: Pointer to mac
+ * @sinfo: Pointer to station info
+ *
+ * Return: 0 for success, non-zero for failure
+ */
+static int __wlan_hdd_cfg80211_dump_station(struct wiphy *wiphy,
+ struct net_device *dev,
+ int idx, u8 *mac,
+ struct station_info *sinfo)
+{
+ hdd_context_t *hdd_ctx = (hdd_context_t *) wiphy_priv(wiphy);
+
+ hdd_debug("%s: idx %d", __func__, idx);
+ if (idx != 0)
+ return -ENOENT;
+ qdf_mem_copy(mac, hdd_ctx->config->intfMacAddr[0].bytes,
+ QDF_MAC_ADDR_SIZE);
+ return __wlan_hdd_cfg80211_get_station(wiphy, dev, mac, sinfo);
+}
+
+/**
+ * wlan_hdd_cfg80211_dump_station() - dump station statistics
+ * @wiphy: Pointer to wiphy
+ * @dev: Pointer to network device
+ * @idx: variable to determine whether to get stats or not
+ * @mac: Pointer to mac
+ * @sinfo: Pointer to station info
+ *
+ * Return: 0 for success, non-zero for failure
+ */
+int wlan_hdd_cfg80211_dump_station(struct wiphy *wiphy,
+ struct net_device *dev,
+ int idx, u8 *mac,
+ struct station_info *sinfo)
+{
+ int ret;
+
+ cds_ssr_protect(__func__);
+ ret = __wlan_hdd_cfg80211_dump_station(wiphy, dev, idx, mac, sinfo);
+ cds_ssr_unprotect(__func__);
+ return ret;
+}
+
+/**
* hdd_get_stats() - Function to retrieve interface statistics
* @dev: pointer to network device
*
diff --git a/core/hdd/src/wlan_hdd_stats.h b/core/hdd/src/wlan_hdd_stats.h
index c33031f..a7f4e12 100644
--- a/core/hdd/src/wlan_hdd_stats.h
+++ b/core/hdd/src/wlan_hdd_stats.h
@@ -40,6 +40,8 @@
/* LL stats get request time out value */
#define WLAN_WAIT_TIME_LL_STATS 800
+#define WLAN_HDD_TGT_NOISE_FLOOR_DBM (-96)
+
/**
* struct index_vht_data_rate_type - vht data rate type
* @beacon_rate_index: Beacon rate index
@@ -190,6 +192,21 @@
struct station_info *sinfo);
#endif
+/**
+ * wlan_hdd_cfg80211_dump_station() - dump station statistics
+ * @wiphy: Pointer to wiphy
+ * @dev: Pointer to network device
+ * @idx: variable to determine whether to get stats or not
+ * @mac: Pointer to mac
+ * @sinfo: Pointer to station info
+ *
+ * Return: 0 for success, non-zero for failure
+ */
+int wlan_hdd_cfg80211_dump_station(struct wiphy *wiphy,
+ struct net_device *dev,
+ int idx, u8 *mac,
+ struct station_info *sinfo);
+
struct net_device_stats *hdd_get_stats(struct net_device *dev);
int wlan_hdd_cfg80211_dump_survey(struct wiphy *wiphy,
diff --git a/core/hdd/src/wlan_hdd_wext.c b/core/hdd/src/wlan_hdd_wext.c
index 8d176a9..6f6051d 100644
--- a/core/hdd/src/wlan_hdd_wext.c
+++ b/core/hdd/src/wlan_hdd_wext.c
@@ -3717,6 +3717,7 @@
struct statsContext *pStatsContext;
tCsrSummaryStatsInfo *pSummaryStats;
tCsrGlobalClassAStatsInfo *pClassAStats;
+ struct csr_per_chain_rssi_stats_info *per_chain_rssi_stats;
hdd_adapter_t *pAdapter;
if (ioctl_debug) {
@@ -3739,6 +3740,8 @@
pSummaryStats = (tCsrSummaryStatsInfo *) pStats;
pClassAStats = (tCsrGlobalClassAStatsInfo *) (pSummaryStats + 1);
+ per_chain_rssi_stats = (struct csr_per_chain_rssi_stats_info *)
+ (pClassAStats + 1);
pStatsContext = pContext;
pAdapter = pStatsContext->pAdapter;
if ((NULL == pAdapter) ||
@@ -3764,6 +3767,7 @@
/* copy over the stats. do so as a struct copy */
pAdapter->hdd_stats.summary_stat = *pSummaryStats;
pAdapter->hdd_stats.ClassA_stat = *pClassAStats;
+ pAdapter->hdd_stats.per_chain_rssi_stats = *per_chain_rssi_stats;
/* notify the caller */
complete(&pStatsContext->completion);
@@ -3799,7 +3803,8 @@
hstatus = sme_get_statistics(WLAN_HDD_GET_HAL_CTX(pAdapter),
eCSR_HDD,
SME_SUMMARY_STATS |
- SME_GLOBAL_CLASSA_STATS,
+ SME_GLOBAL_CLASSA_STATS |
+ SME_PER_CHAIN_RSSI_STATS,
hdd_get_station_statistics_cb,
0, /* not periodic */
false, /* non-cached results */
diff --git a/core/mac/inc/sir_api.h b/core/mac/inc/sir_api.h
index c1fc594..de87474 100644
--- a/core/mac/inc/sir_api.h
+++ b/core/mac/inc/sir_api.h
@@ -109,6 +109,8 @@
#define MAX_POWER_DBG_ARGS_SUPPORTED 8
+#define NUM_CHAINS_MAX 2
+
/**
* enum sir_conn_update_reason: Reason for conc connection update
* @SIR_UPDATE_REASON_SET_OPER_CHAN: Set probable operating channel
diff --git a/core/sme/inc/csr_api.h b/core/sme/inc/csr_api.h
index d032de0..4fc6567 100644
--- a/core/sme/inc/csr_api.h
+++ b/core/sme/inc/csr_api.h
@@ -1522,6 +1522,16 @@
uint32_t tx_mpdu_in_ampdu_cnt;
} tCsrPerStaStatsInfo;
+/**
+ * struct csr_per_chain_rssi_stats_info - stores chain rssi
+ * @rssi: array containing rssi for all chains
+ * @peer_mac_addr: peer mac address
+ */
+struct csr_per_chain_rssi_stats_info {
+ int8_t rssi[NUM_CHAINS_MAX];
+ tSirMacAddr peer_mac_addr;
+};
+
typedef struct tagCsrRoamSetKey {
eCsrEncryptionType encType;
tAniKeyDirection keyDirection; /* Tx, Rx or Tx-and-Rx */
diff --git a/core/sme/inc/csr_internal.h b/core/sme/inc/csr_internal.h
index 57df53f..8fe191b 100644
--- a/core/sme/inc/csr_internal.h
+++ b/core/sme/inc/csr_internal.h
@@ -240,6 +240,7 @@
eCsrGlobalClassCStats,
eCsrGlobalClassDStats,
eCsrPerStaStats,
+ csr_per_chain_rssi_stats,
eCsrMaxStats
} eCsrRoamStatsClassTypes;
@@ -1023,6 +1024,7 @@
tCsrGlobalClassCStatsInfo classCStatsInfo;
tCsrGlobalClassDStatsInfo classDStatsInfo;
tCsrPerStaStatsInfo perStaStatsInfo[CSR_MAX_STA];
+ struct csr_per_chain_rssi_stats_info per_chain_rssi_stats;
tDblLinkList statsClientReqList;
tDblLinkList peStatsReqList;
tCsrTlStatsReqInfo tlStatsReqInfo;
diff --git a/core/sme/inc/sme_api.h b/core/sme/inc/sme_api.h
index db91ca7..2514084 100644
--- a/core/sme/inc/sme_api.h
+++ b/core/sme/inc/sme_api.h
@@ -60,6 +60,7 @@
#define SME_GLOBAL_CLASSC_STATS 8
#define SME_GLOBAL_CLASSD_STATS 16
#define SME_PER_STA_STATS 32
+#define SME_PER_CHAIN_RSSI_STATS 64
#define SME_SESSION_ID_ANY 50
diff --git a/core/sme/src/csr/csr_api_roam.c b/core/sme/src/csr/csr_api_roam.c
index c804e5a..0086e0d 100644
--- a/core/sme/src/csr/csr_api_roam.c
+++ b/core/sme/src/csr/csr_api_roam.c
@@ -15986,6 +15986,14 @@
*stats += sizeof(tCsrPerStaStatsInfo);
*length -= sizeof(tCsrPerStaStatsInfo);
break;
+ case csr_per_chain_rssi_stats:
+ sms_log(mac, LOG2,
+ FL("csrRoamStatsRspProcessor:Per Chain RSSI stats"));
+ qdf_mem_copy((uint8_t *)&mac->roam.per_chain_rssi_stats,
+ *stats, sizeof(struct csr_per_chain_rssi_stats_info));
+ *stats += sizeof(struct csr_per_chain_rssi_stats_info);
+ *length -= sizeof(struct csr_per_chain_rssi_stats_info);
+ break;
default:
sms_log(mac, LOGW, FL("unknown stats type"));
break;
@@ -17754,6 +17762,14 @@
sizeof(tCsrPerStaStatsInfo));
pStats += sizeof(tCsrPerStaStatsInfo);
break;
+ case csr_per_chain_rssi_stats:
+ sms_log(pMac, LOG2, FL("Per Chain RSSI stats"));
+ qdf_mem_copy(pStats,
+ (uint8_t *)&pMac->roam.per_chain_rssi_stats,
+ sizeof(struct csr_per_chain_rssi_stats_info));
+ pStats += sizeof(
+ struct csr_per_chain_rssi_stats_info);
+ break;
default:
sms_log(pMac, LOGE,
FL("Unknown stats type and counter %d"),
diff --git a/core/wma/inc/wma_internal.h b/core/wma/inc/wma_internal.h
index b5fc9f4..dadac75 100644
--- a/core/wma/inc/wma_internal.h
+++ b/core/wma/inc/wma_internal.h
@@ -55,12 +55,14 @@
#define FW_PDEV_STATS_SET 0x1
#define FW_VDEV_STATS_SET 0x2
#define FW_PEER_STATS_SET 0x4
-#define FW_STATS_SET 0x7
+#define FW_RSSI_PER_CHAIN_STATS_SET 0x8
+#define FW_STATS_SET 0xf
/*AR9888/AR6320 noise floor approx value
* similar to the mentioned the WMA
*/
#define WMA_TGT_NOISE_FLOOR_DBM (-96)
+#define WMA_TGT_RSSI_INVALID 96
/*
* Make sure that link monitor and keep alive
diff --git a/core/wma/src/wma_utils.c b/core/wma/src/wma_utils.c
index b8f3570..aa18f51 100644
--- a/core/wma/src/wma_utils.c
+++ b/core/wma/src/wma_utils.c
@@ -1298,11 +1298,6 @@
node->rate_flags, node->nss,
classa_stats->max_pwr);
}
-
- if (node->fw_stats_set & FW_STATS_SET) {
- WMA_LOGD("<--STATS RSP VDEV_ID:%d", vdev_id);
- wma_post_stats(wma, node);
- }
}
}
@@ -1332,6 +1327,107 @@
}
/**
+ * wma_update_per_chain_rssi_stats() - to store per chain rssi stats
+ * @wma: wma handle
+ * @rssi_stats: rssi stats
+ * @rssi_per_chain_stats: buffer where rssi stats to be stored
+ *
+ * This function stores per chain rssi stats received from fw for all vdevs for
+ * which the stats were requested into a csr stats structure.
+ *
+ * Return: void
+ */
+static void wma_update_per_chain_rssi_stats(tp_wma_handle wma,
+ wmi_rssi_stats *rssi_stats,
+ struct csr_per_chain_rssi_stats_info *rssi_per_chain_stats)
+{
+ int i;
+ int8_t bcn_snr, dat_snr;
+
+ for (i = 0; i < NUM_CHAINS_MAX; i++) {
+ bcn_snr = rssi_stats->rssi_avg_beacon[i];
+ dat_snr = rssi_stats->rssi_avg_data[i];
+ WMA_LOGD("chain %d beacon snr %d data snr %d",
+ i, bcn_snr, dat_snr);
+ if (dat_snr != WMA_TGT_INVALID_SNR)
+ rssi_per_chain_stats->rssi[i] = dat_snr;
+ else if (bcn_snr != WMA_TGT_INVALID_SNR)
+ rssi_per_chain_stats->rssi[i] = bcn_snr;
+ else
+ /*
+ * Firmware sends invalid snr till it sees
+ * Beacon/Data after connection since after
+ * vdev up fw resets the snr to invalid.
+ * In this duartion Host will return an invalid rssi
+ * value.
+ */
+ rssi_per_chain_stats->rssi[i] = WMA_TGT_RSSI_INVALID;
+
+ /*
+ * Get the absolute rssi value from the current rssi value the
+ * sinr value is hardcoded into 0 in the qcacld-new/CORE stack
+ */
+ rssi_per_chain_stats->rssi[i] += WMA_TGT_NOISE_FLOOR_DBM;
+ WMI_MAC_ADDR_TO_CHAR_ARRAY(&(rssi_stats->peer_macaddr),
+ rssi_per_chain_stats->peer_mac_addr);
+ }
+}
+
+/**
+ * wma_update_rssi_stats() - to update rssi stats for all vdevs
+ * for which the stats were requested.
+ * @wma: wma handle
+ * @rssi_stats: rssi stats
+ *
+ * This function updates the rssi stats for all vdevs for which
+ * the stats were requested.
+ *
+ * Return: void
+ */
+static void wma_update_rssi_stats(tp_wma_handle wma,
+ wmi_rssi_stats *rssi_stats)
+{
+ tAniGetPEStatsRsp *stats_rsp_params;
+ struct csr_per_chain_rssi_stats_info *rssi_per_chain_stats = NULL;
+ struct wma_txrx_node *node;
+ uint8_t *stats_buf;
+ uint32_t temp_mask;
+ uint8_t vdev_id;
+
+ vdev_id = rssi_stats->vdev_id;
+ node = &wma->interfaces[vdev_id];
+ if (node->stats_rsp) {
+ node->fw_stats_set |= FW_RSSI_PER_CHAIN_STATS_SET;
+ WMA_LOGD("<-- FW RSSI PER CHAIN STATS received for vdevId:%d",
+ vdev_id);
+ stats_rsp_params = (tAniGetPEStatsRsp *) node->stats_rsp;
+ stats_buf = (uint8_t *) (stats_rsp_params + 1);
+ temp_mask = stats_rsp_params->statsMask;
+
+ if (temp_mask & (1 << eCsrSummaryStats))
+ stats_buf += sizeof(tCsrSummaryStatsInfo);
+ if (temp_mask & (1 << eCsrGlobalClassAStats))
+ stats_buf += sizeof(tCsrGlobalClassAStatsInfo);
+ if (temp_mask & (1 << eCsrGlobalClassBStats))
+ stats_buf += sizeof(tCsrGlobalClassBStatsInfo);
+ if (temp_mask & (1 << eCsrGlobalClassCStats))
+ stats_buf += sizeof(tCsrGlobalClassCStatsInfo);
+ if (temp_mask & (1 << eCsrGlobalClassDStats))
+ stats_buf += sizeof(tCsrGlobalClassDStatsInfo);
+ if (temp_mask & (1 << eCsrPerStaStats))
+ stats_buf += sizeof(tCsrPerStaStatsInfo);
+
+ if (temp_mask & (1 << csr_per_chain_rssi_stats)) {
+ rssi_per_chain_stats =
+ (struct csr_per_chain_rssi_stats_info *)stats_buf;
+ wma_update_per_chain_rssi_stats(wma, rssi_stats,
+ rssi_per_chain_stats);
+ }
+ }
+}
+
+
+/**
* wma_link_status_event_handler() - link status event handler
* @handle: wma handle
* @cmd_param_info: data from event
@@ -1412,9 +1508,11 @@
wmi_pdev_stats *pdev_stats;
wmi_vdev_stats *vdev_stats;
wmi_peer_stats *peer_stats;
+ wmi_rssi_stats *rssi_stats;
+ wmi_per_chain_rssi_stats *rssi_event;
+ struct wma_txrx_node *node;
uint8_t i, *temp;
-
param_buf = (WMI_UPDATE_STATS_EVENTID_param_tlvs *) cmd_param_info;
if (!param_buf) {
WMA_LOGA("%s: Invalid stats event", __func__);
@@ -1450,6 +1548,34 @@
}
}
+ rssi_event = (wmi_per_chain_rssi_stats *) param_buf->chain_stats;
+ if (rssi_event) {
+ if ((rssi_event->tlv_header & 0xFFFF0000) >> 16 ==
+ WMITLV_TAG_STRUC_wmi_per_chain_rssi_stats) {
+ WMA_LOGD("%s: num_rssi_stats %u", __func__,
+ rssi_event->num_per_chain_rssi_stats);
+ if (rssi_event->num_per_chain_rssi_stats > 0) {
+ temp = (uint8_t *) rssi_event;
+ temp += sizeof(*rssi_event);
+ for (i = 0;
+ i < rssi_event->num_per_chain_rssi_stats;
+ i++) {
+ rssi_stats = (wmi_rssi_stats *)temp;
+ wma_update_rssi_stats(wma, rssi_stats);
+ temp += sizeof(wmi_rssi_stats);
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < wma->max_bssid; i++) {
+ node = &wma->interfaces[i];
+ if (node->fw_stats_set & FW_PEER_STATS_SET) {
+ WMA_LOGD("<--STATS RSP VDEV_ID:%d", i);
+ wma_post_stats(wma, node);
+ }
+ }
+
WMA_LOGI("%s: Exit", __func__);
return 0;
}
@@ -1880,6 +2006,10 @@
case eCsrPerStaStats:
len += sizeof(tCsrPerStaStatsInfo);
break;
+ case csr_per_chain_rssi_stats:
+ len +=
+ sizeof(struct csr_per_chain_rssi_stats_info);
+ break;
}
}