Files
android_kernel_samsung_sm86…/tools/linux/peerstats.c
Amir Patel c87166665a qca-wifi: Fix zero rate index issue in peer rate stats
Rate index zero (representing 11 Mbps in ratetable) was ignored in current
stats implementation. Added code changes to include zero rate index

Some formatting is done in peerstats app to avoid exceeding 80 chars

CRs-Fixed: 2424176
Change-Id: I9d6938cc840ee3db3639b3608b429a97832c3168
2019-04-15 05:07:45 -07:00

365 sor
10 KiB
C

/*
* Copyright (c) 2019 The Linux Foundation. All rights reserved.
*
* 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.
*/
/**
* file: peer rate statitistics application
* This file provides framework to display peer rate statistics
*/
#include <netinet/in.h>
#include <arpa/inet.h>
#include <qcatools_lib.h>
#include <dp_rate_stats_pub.h>
#ifndef min
#define min(x, y) ((x) < (y) ? (x) : (y))
#else
#error confilicting defs of min
#endif
#define PRINT(fmt, ...) \
do { \
printf(fmt, ##__VA_ARGS__); \
printf("\n"); \
} while (0)
char interface[IFNAMSIZ];
static void dp_peer_rx_rate_stats_print(uint8_t *peer_mac,
uint64_t peer_cookie,
void *buffer,
uint32_t buffer_len)
{
int i = 0;
struct wlan_rx_rate_stats *rx_stats;
rx_stats = (struct wlan_rx_rate_stats *)buffer;
PRINT("\n......................................");
PRINT("......................................");
PRINT("PEER %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
peer_mac[0],
peer_mac[1],
peer_mac[2],
peer_mac[3],
peer_mac[4],
peer_mac[5]);
PRINT("\tpeer cookie: %016llx\n", peer_cookie);
PRINT("\n..............................................");
PRINT("................................");
PRINT("................................................");
PRINT(".................................\n");
PRINT("\tRx statistics:");
PRINT(" %10s | %10s | %10s | %10s | %10s | %10s",
"rate",
"rix",
"bytes",
"msdus",
"mpdus",
"ppdus");
PRINT("\t\t%10s | %10s | %10s | %10s | %10s | %10s |",
"retries",
"rssi",
"rssi 1 p20",
"rssi 1 e20",
"rssi 1 e40",
"rssi 1 e80");
PRINT(" | %10s | | %10s | %10s | %10s | %10s | %10s",
"rssi 2 p20",
"rssi 2 e20",
"rssi 2 e40",
"rssi 2 e80",
"rssi 3 p20",
"rssi 3 e20");
PRINT(" | %10s | %10s | %10s | %10s | %10s | %10s\n\n\n",
"rssi 3 e40",
"rssi 3 e80",
"rssi 4 p20",
"rssi 4 e20",
"rssi 4 e40",
"rssi 4 e80");
for (i = 0; i < WLANSTATS_CACHE_SIZE; i++) {
if (rx_stats->rix != INVALID_CACHE_IDX) {
PRINT(" %10u | %10u | %10u | %10u | %10u |",
rx_stats->rate,
rx_stats->rix,
rx_stats->num_bytes,
rx_stats->num_msdus,
rx_stats->num_mpdus);
PRINT(" %10u | %10u | %10u | %10lu | %10lu |",
rx_stats->num_ppdus,
rx_stats->num_retries,
rx_stats->num_sgi,
rx_stats->avg_rssi,
rx_stats->avg_rssi_ant[0][0]);
PRINT(" %10lu | %10lu | %10lu | %10lu | %10lu |",
rx_stats->avg_rssi_ant[0][1],
rx_stats->avg_rssi_ant[0][2],
rx_stats->avg_rssi_ant[0][3],
rx_stats->avg_rssi_ant[1][0],
rx_stats->avg_rssi_ant[1][1]);
PRINT(" %10lu | %10lu | %10lu | %10lu | %10lu |",
rx_stats->avg_rssi_ant[1][2],
rx_stats->avg_rssi_ant[1][3],
rx_stats->avg_rssi_ant[2][0],
rx_stats->avg_rssi_ant[2][1],
rx_stats->avg_rssi_ant[2][2]);
PRINT(" %10lu | %10lu | %10lu | %10lu | %10lu\n\n\n",
rx_stats->avg_rssi_ant[2][3],
rx_stats->avg_rssi_ant[3][0],
rx_stats->avg_rssi_ant[3][1],
rx_stats->avg_rssi_ant[3][2],
rx_stats->avg_rssi_ant[3][3]);
}
rx_stats = rx_stats + 1;
}
}
static void
dp_peer_tx_sojourn_stats_print(uint8_t *peer_mac,
uint64_t peer_cookie,
struct wlan_tx_sojourn_stats *sojourn_stats)
{
uint8_t tid;
PRINT("\n..........................................");
PRINT("....................................");
PRINT("....................................");
PRINT(".........................................\n");
PRINT("PEER %02hhx:%02hhx:%02hhx:%02hhx%02hhx:%02hhx\n",
peer_mac[0],
peer_mac[1],
peer_mac[2],
peer_mac[3],
peer_mac[4],
peer_mac[5]);
PRINT("\tPEER Cookie: %016llx\n", peer_cookie);
PRINT("\n...........................................");
PRINT("...................................");
PRINT("..................................");
PRINT("............................................");
PRINT("\n\tSojourn statistics:\n");
PRINT("\t\t%10s %10s %20s %20s\n", "tid", "ave", "sum", "num");
for (tid = 0; tid < WLAN_DATA_TID_MAX; tid++) {
/* change sum_sojourn_msdu data type to u64 */
PRINT("\t\t%10d %10lu %20u %20u\n",
tid,
sojourn_stats->avg_sojourn_msdu[tid],
sojourn_stats->sum_sojourn_msdu[tid],
sojourn_stats->num_msdus[tid]);
}
PRINT("sizeof(avg): %d", sizeof(sojourn_stats->avg_sojourn_msdu[tid]));
PRINT("\n...........................................");
PRINT("...................................");
PRINT("...................................");
PRINT("...........................................\n");
}
static void dp_peer_tx_rate_stats_print(uint8_t *peer_mac,
uint64_t peer_cookie,
void *buffer,
uint32_t buffer_len)
{
int i = 0;
struct wlan_tx_rate_stats *tx_stats;
struct wlan_tx_sojourn_stats *sojourn_stats;
if (buffer_len < (WLANSTATS_CACHE_SIZE *
sizeof(struct wlan_tx_rate_stats))
+ sizeof(struct wlan_tx_sojourn_stats)) {
PRINT("invalid buffer len, return");
return;
}
tx_stats = (struct wlan_tx_rate_stats *)buffer;
PRINT("\n...........................................");
PRINT("...................................");
PRINT("...................................");
PRINT("...........................................\n");
PRINT("PEER %02hhx:%02hhx:%02hhx:%02hhx%02hhx:%02hhx\n\n",
peer_mac[0],
peer_mac[1],
peer_mac[2],
peer_mac[3],
peer_mac[4],
peer_mac[5]);
PRINT("\tPEER Cookie: %016llx", peer_cookie);
PRINT("\n...........................................");
PRINT("...................................");
PRINT("...................................");
PRINT("...........................................\n");
PRINT("\tTx statistics:\n");
PRINT("\t\t%10s | %10s | %10s | %10s | %10s",
"rate",
"rix",
"attempts",
"success",
"ppdus");
for (i = 0; i < WLANSTATS_CACHE_SIZE; i++) {
if (tx_stats->rix != INVALID_CACHE_IDX) {
PRINT("\t\t%10u | %10u | %10u | %10u | %10u\n",
tx_stats->rate,
tx_stats->rix,
tx_stats->mpdu_attempts,
tx_stats->mpdu_success,
tx_stats->num_ppdus);
}
tx_stats = tx_stats + 1;
}
sojourn_stats = (struct wlan_tx_sojourn_stats *)((uint8_t *)buffer
+ (WLANSTATS_CACHE_SIZE *
sizeof(struct wlan_tx_rate_stats)));
dp_peer_tx_sojourn_stats_print(peer_mac, peer_cookie, sojourn_stats);
return;
}
static void dp_peer_stats_handler(uint32_t cache_type,
uint8_t *peer_mac,
uint64_t peer_cookie,
void *buffer,
uint32_t buffer_len)
{
switch (cache_type) {
case DP_PEER_RX_RATE_STATS:
dp_peer_rx_rate_stats_print(peer_mac, peer_cookie,
buffer, buffer_len);
break;
case DP_PEER_TX_RATE_STATS:
dp_peer_tx_rate_stats_print(peer_mac, peer_cookie,
buffer, buffer_len);
break;
}
}
static void
dp_peer_stats_event_callback(char *ifname,
uint32_t cmdid,
uint8_t *data,
size_t len)
{
struct nlattr *tb_array[QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_MAX + 1];
struct nlattr *tb;
void *buffer = NULL;
uint32_t buffer_len = 0;
uint8_t *peer_mac;
uint32_t cache_type;
uint64_t peer_cookie;
if (cmdid != QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH) {
/* ignore anyother events*/
return;
}
if (strncmp(interface, ifname, sizeof(interface)) != 0) {
/* ignore events for other interfaces*/
return;
}
if (nla_parse(tb_array, QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_MAX,
(struct nlattr *)data, len, NULL)) {
PRINT("Invalid event\n");
return;
}
tb = tb_array[QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE];
if (!tb) {
PRINT("Cache type in NULL, return");
return;
}
cache_type = nla_get_u32(tb);
tb = tb_array[QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC];
if (!tb) {
PRINT("Peer mac addr is null, return");
return;
}
peer_mac = (uint8_t *)nla_data(tb);
tb = tb_array[QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA];
if (tb) {
buffer = (void *)nla_data(tb);
buffer_len = nla_len(tb);
}
tb = tb_array[QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE];
if (!tb) {
PRINT("peer cookie attribute is null, return");
return;
}
peer_cookie = nla_get_u64(tb);
if (!buffer) {
PRINT(" stats buffer is null, return");
return;
}
dp_peer_stats_handler(cache_type, peer_mac, peer_cookie,
buffer, buffer_len);
}
int main(int argc, char *argv[])
{
int err = 0;
wifi_cfg80211_context cfg80211_ctxt;
char *ifname;
int num_msecs = 0;
int status = 0;
if (argc < 2) {
fprintf(stderr, "Invalid commands args\n");
return -EIO;
}
/* Reset the cfg80211 context to 0 if the application does not pass
* custom private event and command sockets. In this case, the default
* port is used for netlink communication.
*/
memset(&cfg80211_ctxt, 0, sizeof(wifi_cfg80211_context));
ifname = argv[1];
memcpy(interface, ifname, sizeof(interface));
cfg80211_ctxt.event_callback = dp_peer_stats_event_callback;
err = wifi_init_nl80211(&cfg80211_ctxt);
if (err) {
fprintf(stderr, "unable to create NL socket\n");
return -EIO;
}
/* Starting event thread to listen for responses*/
if (wifi_nl80211_start_event_thread(&cfg80211_ctxt)) {
fprintf(stderr, "Unable to setup nl80211 event thread\n");
status = -EIO;
goto cleanup;
}
while (true) {
/*sleep for 1 ms*/
usleep(1000);
num_msecs++;
}
wifi_destroy_nl80211(&cfg80211_ctxt);
return 0;
cleanup:
wifi_destroy_nl80211(&cfg80211_ctxt);
return status;
}