From 7ea647ba747682d07645a982383ece2c3c14b67a Mon Sep 17 00:00:00 2001 From: Krishna Kumaar Natarajan Date: Fri, 10 Mar 2017 10:38:46 -0800 Subject: qcacld-3.0: Add support to read link layer stats via debugfs Add support to read link layer stats via debugfs. This change set will issue get_req and store the received stats response is debugfs entry. Change-Id: If3af9f0a37283583ff011e35c7275a329a6e175d CRs-Fixed: 2018087 --- Kbuild | 1 + core/hdd/inc/wlan_hdd_debugfs_llstat.h | 116 ++++++++ core/hdd/src/wlan_hdd_debugfs.c | 4 + core/hdd/src/wlan_hdd_debugfs_llstat.c | 526 +++++++++++++++++++++++++++++++++ core/hdd/src/wlan_hdd_stats.c | 251 +++++++++++----- core/hdd/src/wlan_hdd_stats.h | 36 +++ core/wma/inc/wma_api.h | 8 + core/wma/src/wma_utils.c | 17 ++ 8 files changed, 882 insertions(+), 77 deletions(-) create mode 100644 core/hdd/inc/wlan_hdd_debugfs_llstat.h create mode 100644 core/hdd/src/wlan_hdd_debugfs_llstat.c diff --git a/Kbuild b/Kbuild index 640faec8ac0f..3c07b413f00b 100644 --- a/Kbuild +++ b/Kbuild @@ -414,6 +414,7 @@ HDD_OBJS := $(HDD_SRC_DIR)/wlan_hdd_assoc.o \ ifeq ($(CONFIG_WLAN_DEBUGFS), y) HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs.o +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_llstat.o endif ifeq ($(CONFIG_WLAN_FEATURE_DSRC), y) diff --git a/core/hdd/inc/wlan_hdd_debugfs_llstat.h b/core/hdd/inc/wlan_hdd_debugfs_llstat.h new file mode 100644 index 000000000000..e72bb84ed757 --- /dev/null +++ b/core/hdd/inc/wlan_hdd_debugfs_llstat.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved. + * + * Previously licensed under the ISC license by Qualcomm Atheros, Inc. + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file was originally distributed by Qualcomm Atheros, Inc. + * under proprietary terms before Copyright ownership was assigned + * to the Linux Foundation. + */ + +/** + * DOC: wlan_hdd_debugfs_llstat.h + * + * WLAN Host Device Driver implementation to update + * debugfs with Link Layer statistics + */ + +#ifndef _WLAN_HDD_DEBUGFS_LLSTAT_H +#define _WLAN_HDD_DEBUGFS_LLSTAT_H + +#define DEBUGFS_LLSTATS_BUF_SIZE 10240 +#define DEBUGFS_LLSTATS_REQID 4294967295 +#define DEBUGFS_LLSTATS_REQMASK 0x7 + +#include + +#if defined(WLAN_FEATURE_LINK_LAYER_STATS) && defined(WLAN_DEBUGFS) +/** + * hdd_debugfs_process_peer_stats() - Parse Peer stats and add it to buffer + * @adapter: Pointer to device adapter + * @data: Pointer to stats data + * + * Receiving Link Layer peer statistics from FW. This function stores the + * firmware data in a buffer to be written into debugfs. + * + * Return: None + */ +void hdd_debugfs_process_peer_stats(struct hdd_adapter_s *adapter, void *data); + +/** + * hdd_debugfs_process_radio_stats() - Parse Radio stats and add it to buffer + * @adapter: Pointer to device adapter + * @more_data: More data + * @data: Pointer to stats data + * @num_radio: Number of radios + * + * Receiving Link Layer Radio statistics from FW. This function stores the + * firmware data in a buffer to be written into debugfs. + * + * Return: None + */ +void hdd_debugfs_process_radio_stats(struct hdd_adapter_s *adapter, + uint32_t more_data, void *data, uint32_t num_radio); + +/** + * hdd_link_layer_process_iface_stats() - This function is called after + * @adapter: Pointer to device adapter + * @data: Pointer to stats data + * @num_peers: Number of peers + * + * Receiving Link Layer Interface statistics from FW.This function converts + * the firmware data to the NL data and sends the same to the kernel/upper + * layers. + * + * Return: None + */ +void hdd_debugfs_process_iface_stats(struct hdd_adapter_s *adapter, + void *data, uint32_t num_peers); + +/** + * wlan_hdd_create_ll_stats_file() - API to create Link Layer stats file + * @adapter: interface adapter pointer + * + * Return: 0 on success and errno on failure + */ +int wlan_hdd_create_ll_stats_file(struct hdd_adapter_s *adapter); +#else +static inline void hdd_debugfs_process_peer_stats(struct hdd_adapter_s *adapter, + void *data) +{ +} + +static inline void hdd_debugfs_process_radio_stats( + struct hdd_adapter_s *adapter, + uint32_t more_data, void *data, uint32_t num_radio) +{ +} + +static inline void hdd_debugfs_process_iface_stats( + struct hdd_adapter_s *adapter, + void *data, uint32_t num_peers) +{ +} +static inline int wlan_hdd_create_ll_stats_file(struct hdd_adapter_s *adapter) +{ + return 0; +} +#endif +#endif /* #ifndef _WLAN_HDD_DEBUGFS_LLSTAT_H */ diff --git a/core/hdd/src/wlan_hdd_debugfs.c b/core/hdd/src/wlan_hdd_debugfs.c index 53ee4d8a72cf..aabd7bd3c8df 100644 --- a/core/hdd/src/wlan_hdd_debugfs.c +++ b/core/hdd/src/wlan_hdd_debugfs.c @@ -39,6 +39,7 @@ #include #include #include +#include #define MAX_USER_COMMAND_SIZE_WOWL_ENABLE 8 #define MAX_USER_COMMAND_SIZE_WOWL_PATTERN 512 @@ -873,6 +874,9 @@ QDF_STATUS hdd_debugfs_init(hdd_adapter_t *adapter) if (QDF_STATUS_SUCCESS != wlan_hdd_create_power_stats_file(adapter)) return QDF_STATUS_E_FAILURE; + if (0 != wlan_hdd_create_ll_stats_file(adapter)) + return QDF_STATUS_E_FAILURE; + return QDF_STATUS_SUCCESS; } diff --git a/core/hdd/src/wlan_hdd_debugfs_llstat.c b/core/hdd/src/wlan_hdd_debugfs_llstat.c new file mode 100644 index 000000000000..22dd0671183d --- /dev/null +++ b/core/hdd/src/wlan_hdd_debugfs_llstat.c @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved. + * + * Previously licensed under the ISC license by Qualcomm Atheros, Inc. + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_llstat.c + * + * WLAN Host Device Driver implementation to update + * debugfs with Link Layer statistics + */ + +#include +#include +#include +#include + +struct ll_stats_buf { + ssize_t len; + uint8_t *result; +}; + +static struct ll_stats_buf ll_stats; + +void hdd_debugfs_process_iface_stats(hdd_adapter_t *adapter, + void *data, uint32_t num_peers) +{ + tpSirWifiIfaceStat iface_stat; + tpSirWifiInterfaceInfo iface_info; + tpSirWifiWmmAcStat ac_stat; + struct wifi_iface_offload_stat *offload_stat; + uint64_t average_tsf_offset; + int i; + ssize_t len = 0; + uint8_t *buffer; + + ENTER(); + iface_stat = data; + + buffer = ll_stats.result; + buffer += ll_stats.len; + len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\n\n===LL_STATS_IFACE: num_peers: %d===", num_peers); + + if (false == hdd_get_interface_info(adapter, &iface_stat->info)) { + hdd_err("hdd_get_interface_info get fail"); + return; + } + + iface_info = &iface_stat->info; + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nmode: %u, MAC_ADDR: %pM, state: %u, roaming: %u, capabilities: %u, SSID: %s, BSSID_MAC: %pM, ap_country_str: %s, country_str: %s", + iface_info->mode, &iface_info->macAddr.bytes[0], + iface_info->state, iface_info->roaming, + iface_info->capabilities, iface_info->ssid, + &iface_info->bssid.bytes[0], iface_info->apCountryStr, + iface_info->countryStr); + + average_tsf_offset = iface_stat->avg_bcn_spread_offset_high; + average_tsf_offset = (average_tsf_offset << 32) | + iface_stat->avg_bcn_spread_offset_low; + + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nbeacon_rx: %u, mgmt_rx: %u, mgmt_action_rx: %u, mgmt_action_tx: %u, rssi_mgmt: %u, rssi_data: %u, rssi_ack: %u, is_leaky_ap: %u, avg_rx_frms_leaked: %u, rx_leak_window: %u, average_tsf_offset: %llu, Tx RTS success count: %u, Tx RTS fail count: %u, Tx ppdu success count: %u, Tx ppdu fail count: %u, Connected duration: %u, Disconnected duration: %u, RTT ranging duration: %u, RTT responder duration: %u, Num tx probes: %u, Num beacon miss: %u,\n\nNumber of AC: %d", + iface_stat->beaconRx, iface_stat->mgmtRx, + iface_stat->mgmtActionRx, iface_stat->mgmtActionTx, + iface_stat->rssiMgmt, iface_stat->rssiData, + iface_stat->rssiAck, iface_stat->is_leaky_ap, + iface_stat->avg_rx_frms_leaked, + iface_stat->rx_leak_window, average_tsf_offset, + iface_stat->tx_rts_succ_cnt, + iface_stat->tx_rts_fail_cnt, + iface_stat->tx_ppdu_succ_cnt, + iface_stat->tx_ppdu_fail_cnt, + iface_stat->connected_duration, + iface_stat->disconnected_duration, + iface_stat->rtt_ranging_duration, + iface_stat->rtt_responder_duration, + iface_stat->num_probes_tx, iface_stat->num_beacon_miss, + iface_stat->num_ac); + + for (i = 0; i < iface_stat->num_ac; i++) { + ac_stat = &iface_stat->AccessclassStats[i]; + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nAC: %d, tx_mpdu: %u, rx_mpdu: %u, tx_mcast: %u, rx_mcast: %u, rx_ampdu: %u tx_ampdu: %u, mpdu_lost: %u, retries: %u, retries_short: %u, retries_long: %u, contention_time: min-%u max-%u avg-%u, contention num samples: %u", + ac_stat->ac, ac_stat->txMpdu, ac_stat->rxMpdu, + ac_stat->txMcast, ac_stat->rxMcast, + ac_stat->rxAmpdu, ac_stat->txAmpdu, + ac_stat->mpduLost, ac_stat->retries, + ac_stat->retriesShort, ac_stat->retriesLong, + ac_stat->contentionTimeMin, + ac_stat->contentionTimeMax, + ac_stat->contentionTimeAvg, + ac_stat->contentionNumSamples); + } + + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\n\nNumber of offload stats: %d", + iface_stat->num_offload_stats); + + for (i = 0; i < iface_stat->num_offload_stats; i++) { + offload_stat = &iface_stat->offload_stat[i]; + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\ntype: %d, rx_count: %u, drp_count: %u, fwd_count: %u", + offload_stat->type, offload_stat->rx_count, + offload_stat->drp_count, + offload_stat->fwd_count); + } + + ll_stats.len += len; + EXIT(); +} + +void hdd_debugfs_process_peer_stats(hdd_adapter_t *adapter, void *data) +{ + tpSirWifiPeerStat peer_stat; + tpSirWifiPeerInfo peer_info; + tpSirWifiRateStat rate_stat; + int i, j, num_rate; + ssize_t len = 0; + uint8_t *buffer; + + ENTER(); + + if (!ll_stats.result) { + hdd_err("LL statistics buffer is NULL"); + return; + } + + peer_stat = (tpSirWifiPeerStat) data; + + buffer = ll_stats.result; + buffer += ll_stats.len; + len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\n\n===LL_STATS_PEER_ALL : num_peers %u===", + peer_stat->numPeers); + + peer_info = (tpSirWifiPeerInfo) ((uint8_t *) peer_stat->peerInfo); + for (i = 1; i <= peer_stat->numPeers; i++) { + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nType: %d, peer_mac: %pM, capabilities: %u\nnum_rates: %d", + wmi_to_sir_peer_type(peer_info->type), + &peer_info->peerMacAddress.bytes[0], + peer_info->capabilities, peer_info->numRate); + + num_rate = peer_info->numRate; + for (j = 0; j < num_rate; j++) { + rate_stat = (tpSirWifiRateStat) ((uint8_t *) + peer_info->rateStats + (j * + sizeof(tSirWifiRateStat))); + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\npreamble: %0x, nss: %0x, bw: %0x, mcs: %0x, bitrate: %0x, txmpdu: %u, rxmpdu: %u, mpdu_lost: %u, retries: %u, retries_short: %u, retries_long: %u", + rate_stat->rate.preamble, rate_stat->rate.nss, + rate_stat->rate.bw, rate_stat->rate.rateMcsIdx, + rate_stat->rate.bitrate, rate_stat->txMpdu, + rate_stat->rxMpdu, rate_stat->mpduLost, + rate_stat->retries, rate_stat->retriesShort, + rate_stat->retriesLong); + } + peer_info = (tpSirWifiPeerInfo) ((uint8_t *) + peer_stat->peerInfo + (i * + sizeof(tSirWifiPeerInfo)) + + (num_rate * sizeof(tSirWifiRateStat))); + } + ll_stats.len += len; + EXIT(); + +} + +void hdd_debugfs_process_radio_stats(hdd_adapter_t *adapter, + uint32_t more_data, void *data, uint32_t num_radio) +{ + int i, j; + ssize_t len = 0; + uint8_t *buffer; + tSirWifiRadioStat *radio_stat = (tpSirWifiRadioStat) data; + tSirWifiChannelStats *chan_stat; + + ENTER(); + + if (!ll_stats.result) { + hdd_err("LL statistics buffer is NULL"); + return; + } + + buffer = ll_stats.result; + buffer += ll_stats.len; + len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE, + "\n\n===LL_STATS_RADIO: number of radios: %u===", + num_radio); + + for (i = 0; i < num_radio; i++) { + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nRadio: %u on_time: %u, tx_time: %u, rx_time: %u, on_time_scan: %u, on_time_nbd: %u, on_time_gscan: %u, on_time_roam_scan: %u, on_time_pno_scan: %u on_time_hs20: %u, on_time_host_scan: %u, on_time_lpi_scan: %u\ntotal_num_tx_pwr_levels: %u\n", + radio_stat->radio, radio_stat->onTime, + radio_stat->txTime, radio_stat->rxTime, + radio_stat->onTimeScan, radio_stat->onTimeNbd, + radio_stat->onTimeGscan, + radio_stat->onTimeRoamScan, + radio_stat->onTimePnoScan, + radio_stat->onTimeHs20, + radio_stat->on_time_host_scan, + radio_stat->on_time_lpi_scan, + radio_stat->total_num_tx_power_levels); + + for (j = 0; j < radio_stat->total_num_tx_power_levels; j++) { + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "%d ", radio_stat->tx_time_per_power_level[j]); + } + + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nNum channels: %d", radio_stat->numChannels); + + for (j = 0; j < radio_stat->numChannels; j++) { + chan_stat = (tSirWifiChannelStats *) + ((uint8_t *)radio_stat->channels + + (j * sizeof(tSirWifiChannelStats))); + + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nChan width: %d, center_freq: %d, center_freq0: %d, center_freq1: %d, on_time: %d, cca_busy_time: %d", + chan_stat->channel.width, + chan_stat->channel.centerFreq, + chan_stat->channel.centerFreq0, + chan_stat->channel.centerFreq1, + chan_stat->onTime, chan_stat->ccaBusyTime); + } + + radio_stat++; + } + ll_stats.len += len; + EXIT(); +} + +static inline void wlan_hdd_llstats_free_buf(void) +{ + qdf_mem_free(ll_stats.result); + ll_stats.result = NULL; + ll_stats.len = 0; +} + +static int wlan_hdd_llstats_alloc_buf(void) +{ + ll_stats.len = 0; + + ll_stats.result = qdf_mem_malloc(DEBUGFS_LLSTATS_BUF_SIZE); + if (!ll_stats.result) { + hdd_err("LL Stats buffer allocation failed"); + return -EINVAL; + } + + return 0; +} + +/** + * hdd_debugfs_stats_update() - Update userspace with local statistics buffer + * @buf: userspace buffer (to which data is being copied into) + * @count: max data that can be copied into buf + * @pos: offset (where data should be copied into) + * + * This function should copies link layer statistics buffer into debugfs + * entry. + * + * Return: number of characters copied; 0 on no-copy + */ +static ssize_t hdd_debugfs_stats_update(char __user *buf, size_t count, + loff_t *pos) +{ + ssize_t ret_cnt; + + ENTER(); + + if (!ll_stats.result) { + hdd_err("Trying to read from NULL buffer"); + return 0; + } + + hdd_info("LL stats read req: count: %zu, pos: %lld", count, *pos); + ret_cnt = simple_read_from_buffer(buf, count, pos, + ll_stats.result, ll_stats.len); + + EXIT(); + return ret_cnt; +} + +/** + * __wlan_hdd_read_ll_stats_debugfs() - API to collect LL stats from FW + * @file: file pointer + * @buf: buffer + * @count: count + * @pos: position pointer + * + * Return: Number of bytes read on success, error number otherwise + */ +static ssize_t __wlan_hdd_read_ll_stats_debugfs(struct file *file, + char __user *buf, size_t count, loff_t *pos) +{ + hdd_adapter_t *adapter; + hdd_context_t *hdd_ctx; + ssize_t ret = 0; + + ENTER(); + + adapter = (hdd_adapter_t *)file->private_data; + if ((!adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + /* All the events are received and buffer is populated */ + ret = hdd_debugfs_stats_update(buf, count, pos); + hdd_info("%zu characters written into debugfs", ret); + + EXIT(); + return ret; + +} + +/** + * wlan_hdd_read_ll_stats_debugfs() - SSR wrapper function to read LL debugfs + * @file: file pointer + * @buf: buffer + * @count: count + * @pos: position pointer + * + * Return: Number of bytes read on success, error number otherwise + */ +static ssize_t wlan_hdd_read_ll_stats_debugfs(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + int ret; + + cds_ssr_protect(__func__); + ret = __wlan_hdd_read_ll_stats_debugfs(file, buf, count, pos); + cds_ssr_unprotect(__func__); + + return ret; +} + +/** + * __wlan_hdd_open_ll_stats_debugfs() - Function to save private on open + * @inode: Pointer to inode structure + * @file: file pointer + * + * Return: zero + */ +static int __wlan_hdd_open_ll_stats_debugfs(struct inode *inode, + struct file *file) +{ + hdd_adapter_t *adapter; + hdd_context_t *hdd_ctx; + int ret; + + ENTER(); + + if (inode->i_private) + file->private_data = inode->i_private; + + adapter = (hdd_adapter_t *)file->private_data; + if ((NULL == adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = wlan_hdd_llstats_alloc_buf(); + if (0 != ret) + return ret; + + ret = wlan_hdd_ll_stats_get(adapter, DEBUGFS_LLSTATS_REQID, + DEBUGFS_LLSTATS_REQMASK); + if (0 != ret) + return ret; + + EXIT(); + return 0; +} + + +/** + * wlan_hdd_open_ll_stats_debugfs() - SSR wrapper function to save private + * on open + * @inode: Pointer to inode structure + * @file: file pointer + * + * Return: zero + */ +static int wlan_hdd_open_ll_stats_debugfs(struct inode *inode, + struct file *file) +{ + int ret; + + cds_ssr_protect(__func__); + ret = __wlan_hdd_open_ll_stats_debugfs(inode, file); + cds_ssr_unprotect(__func__); + + return ret; +} + +/** + * __wlan_hdd_release_ll_stats_debugfs() - Function to save private on release + * @inode: Pointer to inode structure + * @file: file pointer + * + * Return: zero + */ +static int __wlan_hdd_release_ll_stats_debugfs(struct inode *inode, + struct file *file) +{ + hdd_adapter_t *adapter; + hdd_context_t *hdd_ctx; + int ret; + + ENTER(); + + if (inode->i_private) + file->private_data = inode->i_private; + + adapter = (hdd_adapter_t *)file->private_data; + if ((NULL == adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + wlan_hdd_llstats_free_buf(); + + EXIT(); + return 0; +} + +/** + * wlan_hdd_release_ll_stats_debugfs() - SSR wrapper function to save private + * on release + * @inode: Pointer to inode structure + * @file: file pointer + * + * Return: zero + */ +static int wlan_hdd_release_ll_stats_debugfs(struct inode *inode, + struct file *file) +{ + int ret; + + cds_ssr_protect(__func__); + ret = __wlan_hdd_release_ll_stats_debugfs(inode, file); + cds_ssr_unprotect(__func__); + + return ret; +} + +static const struct file_operations fops_ll_stats_debugfs = { + .read = wlan_hdd_read_ll_stats_debugfs, + .open = wlan_hdd_open_ll_stats_debugfs, + .release = wlan_hdd_release_ll_stats_debugfs, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int wlan_hdd_create_ll_stats_file(hdd_adapter_t *adapter) +{ + if (!debugfs_create_file("ll_stats", 0444, adapter->debugfs_phy, + adapter, &fops_ll_stats_debugfs)) + return -EINVAL; + + return 0; +} diff --git a/core/hdd/src/wlan_hdd_stats.c b/core/hdd/src/wlan_hdd_stats.c index 7197588b620f..68f464886868 100644 --- a/core/hdd/src/wlan_hdd_stats.c +++ b/core/hdd/src/wlan_hdd_stats.c @@ -33,26 +33,8 @@ #include "wlan_hdd_lpass.h" #include "hif.h" #include "wlan_hdd_hostapd.h" - -#ifdef WLAN_FEATURE_LINK_LAYER_STATS - -/** - * struct hdd_ll_stats_context - hdd link layer stats context - * - * @request_id: userspace-assigned link layer stats request id - * @request_bitmap: userspace-assigned link layer stats request bitmap - * @response_event: LL stats request wait event - */ -struct hdd_ll_stats_context { - uint32_t request_id; - uint32_t request_bitmap; - struct completion response_event; - spinlock_t context_lock; -}; - -static struct hdd_ll_stats_context ll_stats_context; - -#endif /* End of WLAN_FEATURE_LINK_LAYER_STATS */ +#include "wlan_hdd_debugfs_llstat.h" +#include "wma_api.h" /* 11B, 11G Rate table include Basic rate and Extended rate * The IDX field is the rate index @@ -192,6 +174,7 @@ static int rssi_mcs_tbl[][10] = { #ifdef WLAN_FEATURE_LINK_LAYER_STATS +static struct hdd_ll_stats_context ll_stats_context; /** * put_wifi_rate_stat() - put wifi rate stats @@ -243,23 +226,6 @@ static bool put_wifi_rate_stat(tpSirWifiRateStat stats, return true; } -static tSirWifiPeerType wmi_to_sir_peer_type(enum wmi_peer_type type) -{ - switch (type) { - case WMI_PEER_TYPE_DEFAULT: - return WIFI_PEER_STA; - case WMI_PEER_TYPE_BSS: - return WIFI_PEER_AP; - case WMI_PEER_TYPE_TDLS: - return WIFI_PEER_TDLS; - case WMI_PEER_TYPE_NAN_DATA: - return WIFI_PEER_NAN; - default: - hdd_err("Cannot map wmi_peer_type %d to HAL peer type", type); - return WIFI_PEER_INVALID; - } -} - /** * put_wifi_peer_info() - put wifi peer info * @stats: Pointer to stats context @@ -546,15 +512,8 @@ static tSirWifiInterfaceMode hdd_map_device_to_ll_iface_mode(int deviceMode) } } -/** - * hdd_get_interface_info() - get interface info - * @pAdapter: Pointer to device adapter - * @pInfo: Pointer to interface info - * - * Return: bool - */ -static bool hdd_get_interface_info(hdd_adapter_t *pAdapter, - tpSirWifiInterfaceInfo pInfo) +bool hdd_get_interface_info(hdd_adapter_t *pAdapter, + tpSirWifiInterfaceInfo pInfo) { uint8_t *staMac = NULL; hdd_station_ctx_t *pHddStaCtx; @@ -1031,6 +990,76 @@ static void hdd_link_layer_process_radio_stats(hdd_adapter_t *pAdapter, return; } +/** + * hdd_ll_process_radio_stats() - Wrapper function for cfg80211/debugfs + * @adapter: Pointer to device adapter + * @more_data: More data + * @data: Pointer to stats data + * @num_radios: Number of radios + * @resp_id: Response ID from FW + * + * Receiving Link Layer Radio statistics from FW. This function is a wrapper + * function which calls cfg80211/debugfs functions based on the response ID. + * + * Return: None + */ +static void hdd_ll_process_radio_stats(hdd_adapter_t *adapter, + uint32_t more_data, void *data, uint32_t num_radio, + uint32_t resp_id) +{ + if (DEBUGFS_LLSTATS_REQID == resp_id) + hdd_debugfs_process_radio_stats(adapter, more_data, + (tpSirWifiRadioStat)data, num_radio); + else + hdd_link_layer_process_radio_stats(adapter, more_data, + (tpSirWifiRadioStat)data, num_radio); +} + +/** + * hdd_ll_process_iface_stats() - Wrapper function for cfg80211/debugfs + * @adapter: Pointer to device adapter + * @data: Pointer to stats data + * @num_peers: Number of peers + * @resp_id: Response ID from FW + * + * Receiving Link Layer Radio statistics from FW. This function is a wrapper + * function which calls cfg80211/debugfs functions based on the response ID. + * + * Return: None + */ +static void hdd_ll_process_iface_stats(hdd_adapter_t *adapter, + void *data, uint32_t num_peers, uint32_t resp_id) +{ + if (DEBUGFS_LLSTATS_REQID == resp_id) + hdd_debugfs_process_iface_stats(adapter, + (tpSirWifiIfaceStat) data, num_peers); + else + hdd_link_layer_process_iface_stats(adapter, + (tpSirWifiIfaceStat) data, num_peers); +} + +/** + * hdd_ll_process_peer_stats() - Wrapper function for cfg80211/debugfs + * @adapter: Pointer to device adapter + * @more_data: More data + * @data: Pointer to stats data + * @resp_id: Response ID from FW + * + * Receiving Link Layer Radio statistics from FW. This function is a wrapper + * function which calls cfg80211/debugfs functions based on the response ID. + * + * Return: None + */ +static void hdd_ll_process_peer_stats(hdd_adapter_t *adapter, + uint32_t more_data, void *data, uint32_t resp_id) +{ + if (DEBUGFS_LLSTATS_REQID == resp_id) + hdd_debugfs_process_peer_stats(adapter, data); + else + hdd_link_layer_process_peer_stats(adapter, more_data, + (tpSirWifiPeerStat) data); +} + /** * wlan_hdd_cfg80211_link_layer_stats_callback() - This function is called * @ctx: Pointer to hdd context @@ -1090,23 +1119,24 @@ void wlan_hdd_cfg80211_link_layer_stats_callback(void *ctx, } spin_unlock(&context->context_lock); - if (linkLayerStatsResults-> - paramId & WMI_LINK_STATS_RADIO) { - hdd_link_layer_process_radio_stats(pAdapter, + if (linkLayerStatsResults->paramId & WMI_LINK_STATS_RADIO) { + hdd_ll_process_radio_stats(pAdapter, linkLayerStatsResults->moreResultToFollow, - (tpSirWifiRadioStat)linkLayerStatsResults->results, - linkLayerStatsResults->num_radio); + linkLayerStatsResults->results, + linkLayerStatsResults->num_radio, + linkLayerStatsResults->rspId); spin_lock(&context->context_lock); if (!linkLayerStatsResults->moreResultToFollow) context->request_bitmap &= ~(WMI_LINK_STATS_RADIO); spin_unlock(&context->context_lock); - } else if (linkLayerStatsResults-> - paramId & WMI_LINK_STATS_IFACE) { - hdd_link_layer_process_iface_stats(pAdapter, - (tpSirWifiIfaceStat)linkLayerStatsResults->results, - linkLayerStatsResults->num_peers); + } else if (linkLayerStatsResults->paramId & + WMI_LINK_STATS_IFACE) { + hdd_ll_process_iface_stats(pAdapter, + linkLayerStatsResults->results, + linkLayerStatsResults->num_peers, + linkLayerStatsResults->rspId); spin_lock(&context->context_lock); /* Firmware doesn't send peerstats event if no peers are @@ -1122,9 +1152,10 @@ void wlan_hdd_cfg80211_link_layer_stats_callback(void *ctx, } else if (linkLayerStatsResults-> paramId & WMI_LINK_STATS_ALL_PEER) { - hdd_link_layer_process_peer_stats(pAdapter, + hdd_ll_process_peer_stats(pAdapter, linkLayerStatsResults->moreResultToFollow, - (tpSirWifiPeerStat)linkLayerStatsResults->results); + linkLayerStatsResults->results, + linkLayerStatsResults->rspId); spin_lock(&context->context_lock); if (!linkLayerStatsResults->moreResultToFollow) @@ -1306,6 +1337,86 @@ nla_policy [QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK] = {.type = NLA_U32} }; +static int wlan_hdd_send_ll_stats_req(hdd_context_t *hdd_ctx, + tSirLLStatsGetReq *req) +{ + unsigned long rc; + struct hdd_ll_stats_context *context; + + context = &ll_stats_context; + spin_lock(&context->context_lock); + context->request_id = req->reqId; + context->request_bitmap = req->paramIdMask; + INIT_COMPLETION(context->response_event); + spin_unlock(&context->context_lock); + + if (QDF_STATUS_SUCCESS != + sme_ll_stats_get_req(hdd_ctx->hHal, req)) { + hdd_err("sme_ll_stats_get_req Failed"); + return -EINVAL; + } + + rc = wait_for_completion_timeout(&context->response_event, + msecs_to_jiffies(WLAN_WAIT_TIME_LL_STATS)); + if (!rc) { + hdd_err("Target response timed out request id %d request bitmap 0x%x", + context->request_id, context->request_bitmap); + return -ETIMEDOUT; + } + + return 0; +} + +int wlan_hdd_ll_stats_get(hdd_adapter_t *adapter, uint32_t req_id, + uint32_t req_mask) +{ + unsigned long rc; + tSirLLStatsGetReq get_req; + hdd_station_ctx_t *hddstactx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int status; + + ENTER(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_warn("Command not allowed in FTM mode"); + return -EPERM; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return -EINVAL; + + if (hddstactx->hdd_ReassocScenario) { + hdd_err("Roaming in progress, so unable to proceed this request"); + return -EBUSY; + } + + if (!adapter->isLinkLayerStatsSet) + hdd_info("isLinkLayerStatsSet: %d; STATs will be all zero", + adapter->isLinkLayerStatsSet); + + get_req.reqId = req_id; + get_req.paramIdMask = req_mask; + get_req.staId = adapter->sessionId; + + rtnl_lock(); + rc = wlan_hdd_send_ll_stats_req(hdd_ctx, &get_req); + if (0 != rc) { + hdd_err("Failed to send LL stats request (id:%u)", req_id); + goto err_rtnl_unlock; + } + + rtnl_unlock(); + + EXIT(); + return rc; + +err_rtnl_unlock: + rtnl_unlock(); + return rc; +} + /** * __wlan_hdd_cfg80211_ll_stats_get() - get link layer stats * @wiphy: Pointer to wiphy @@ -1322,7 +1433,6 @@ __wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy, int data_len) { unsigned long rc; - struct hdd_ll_stats_context *context; hdd_context_t *pHddCtx = wiphy_priv(wiphy); struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX + 1]; tSirLLStatsGetReq LinkLayerStatsGetReq; @@ -1379,26 +1489,13 @@ __wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy, LinkLayerStatsGetReq.staId = pAdapter->sessionId; - context = &ll_stats_context; - spin_lock(&context->context_lock); - context->request_id = LinkLayerStatsGetReq.reqId; - context->request_bitmap = LinkLayerStatsGetReq.paramIdMask; - INIT_COMPLETION(context->response_event); - spin_unlock(&context->context_lock); - - if (QDF_STATUS_SUCCESS != sme_ll_stats_get_req(pHddCtx->hHal, - &LinkLayerStatsGetReq)) { - hdd_err("sme_ll_stats_get_req Failed"); - return -EINVAL; - } - - rc = wait_for_completion_timeout(&context->response_event, - msecs_to_jiffies(WLAN_WAIT_TIME_LL_STATS)); + rc = wlan_hdd_send_ll_stats_req(pHddCtx, &LinkLayerStatsGetReq); if (!rc) { - hdd_err("Target response timed out request id %d request bitmap 0x%x", - context->request_id, context->request_bitmap); - return -ETIMEDOUT; + hdd_err("Failed to send LL stats request (id:%u)", + LinkLayerStatsGetReq.reqId); + return rc; } + EXIT(); return 0; } diff --git a/core/hdd/src/wlan_hdd_stats.h b/core/hdd/src/wlan_hdd_stats.h index 8465041ad8d5..22c47f449bc5 100644 --- a/core/hdd/src/wlan_hdd_stats.h +++ b/core/hdd/src/wlan_hdd_stats.h @@ -81,6 +81,21 @@ struct index_data_rate_type { }; #ifdef WLAN_FEATURE_LINK_LAYER_STATS + +/** + * struct hdd_ll_stats_context - hdd link layer stats context + * + * @request_id: userspace-assigned link layer stats request id + * @request_bitmap: userspace-assigned link layer stats request bitmap + * @response_event: LL stats request wait event + */ +struct hdd_ll_stats_context { + uint32_t request_id; + uint32_t request_bitmap; + struct completion response_event; + spinlock_t context_lock; +}; + /* * Used to allocate the size of 4096 for the link layer stats. * The size of 4096 is considered assuming that all data per @@ -140,6 +155,27 @@ static inline bool hdd_link_layer_stats_supported(void) return true; } +/** + * hdd_get_interface_info() - get interface info + * @adapter: Pointer to device adapter + * @info: Pointer to interface info + * + * Return: bool + */ +bool hdd_get_interface_info(hdd_adapter_t *adapter, + tpSirWifiInterfaceInfo info); + +/** + * wlan_hdd_ll_stats_get() - Get Link Layer statistics from FW + * @adapter: Pointer to device adapter + * @req_id: request id + * @req_mask: bitmask used by FW for the request + * + * Return: 0 on success and error code otherwise + */ +int wlan_hdd_ll_stats_get(hdd_adapter_t *adapter, uint32_t req_id, + uint32_t req_mask); + #else static inline void hdd_init_ll_stats_ctx(void) diff --git a/core/wma/inc/wma_api.h b/core/wma/inc/wma_api.h index db602c91719c..f4b5ca6cafe0 100644 --- a/core/wma/inc/wma_api.h +++ b/core/wma/inc/wma_api.h @@ -416,4 +416,12 @@ static inline void wma_tx_failure_cb(void *ctx, uint32_t num_msdu, { } #endif + +/** + * wmi_to_sir_peer_type() - convert peer type from WMI to SIR enum + * @type: enum wmi_peer_type + * + * Return: tSirWifiPeerType + */ +tSirWifiPeerType wmi_to_sir_peer_type(enum wmi_peer_type type); #endif diff --git a/core/wma/src/wma_utils.c b/core/wma/src/wma_utils.c index 19abd0b8d593..47be176c0e82 100644 --- a/core/wma/src/wma_utils.c +++ b/core/wma/src/wma_utils.c @@ -4466,3 +4466,20 @@ QDF_STATUS wma_send_vdev_stop_to_fw(t_wma_handle *wma, uint8_t vdev_id) return status; } + +tSirWifiPeerType wmi_to_sir_peer_type(enum wmi_peer_type type) +{ + switch (type) { + case WMI_PEER_TYPE_DEFAULT: + return WIFI_PEER_STA; + case WMI_PEER_TYPE_BSS: + return WIFI_PEER_AP; + case WMI_PEER_TYPE_TDLS: + return WIFI_PEER_TDLS; + case WMI_PEER_TYPE_NAN_DATA: + return WIFI_PEER_NAN; + default: + hdd_err("Cannot map wmi_peer_type %d to HAL peer type", type); + return WIFI_PEER_INVALID; + } +} -- cgit v1.2.3