From patchwork Thu Jun 17 16:01:11 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viktor Barna X-Patchwork-Id: 462681 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, MSGID_FROM_MTA_HEADER, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D6633C2B9F4 for ; Thu, 17 Jun 2021 16:10:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AF12F61426 for ; Thu, 17 Jun 2021 16:10:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232194AbhFQQMs (ORCPT ); Thu, 17 Jun 2021 12:12:48 -0400 Received: from mail-eopbgr80051.outbound.protection.outlook.com ([40.107.8.51]:34016 "EHLO EUR04-VI1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S231679AbhFQQLl (ORCPT ); Thu, 17 Jun 2021 12:11:41 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=DeZYW+u1B9pSFOOeAqlh+4CNF13pl6kPJXqJVX8Hj+ofu35xmuRQ8J28fqIwODtrjwMOkngdc28vueFL4FU714vsvRHxPOI4vjktKXdStkQuk8xzLY8RZwCNNlsmuonPM67QSI3wHUva8WSKmnx+7msrbdPXhVNJ0ZcmpEvKCyljuWjXIv7weVxzsu6384tdEDwUUkKeAqzB/l/AVUrx+be4i7cBouLyWLFcDsCSG5NSgn4MQlB4ZSBvh8jsMIC2PbQB2zlPeyIJAHdXLCi3ky+5wo+oDhZdcME9+l4L9ahVMcu70IVgXyepM7fjcljISq2MgIfJBwPknTEGuLJJ2w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=CRfZOwK60+MCq8T91Q/iYXkLJVCgt7NaCgdXD0CW62A=; b=Yt7d95mWlIwvJSQgkmRRr4EF4dGHJ8bROHc5Jazt2S0pZ7aeeKSh5u9cUWaRJMoF6a55a6sL6Yhkb3QhYykuunyE0qNoh1vjS5FB4z5HU5eov3mmoTBVZbchCgl7lVXGMvzqcdT0JClfUGcYRf3mmy0958ndT97BNeIZhaKfYpuTFWnBXTh9VRx2GtJan0Tl7IyE8HXwcfa0qPuJZLorZBBZ5WOoisFQDPxH5cw8k1Kst1AANbLFMkPyqagd+e1AeACU0F2zlEgjFeUszlKpXJf8agPtQO3I5960x47nX4jhoGXwBt4424CBBwhfjqctstaTzChWNMXAzNjkdNZdnw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=celeno.com; dmarc=pass action=none header.from=celeno.com; dkim=pass header.d=celeno.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=celeno.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=CRfZOwK60+MCq8T91Q/iYXkLJVCgt7NaCgdXD0CW62A=; b=SRsICqVmYGq+HBfQolUDp8QLXELfNIe7Mh2wA9QeWWC0Fz2kaYnv/bk487IPNNHa6+kKjJqBin7SVlg7mzn51PlOEXqBs5oTdxaippk0bvrHeQWmd0l6PVnueCkUr+zNISads2XWbMK1TsuEDkBhslViIUQW982U4nVWY3pEb8k= Authentication-Results: vger.kernel.org; dkim=none (message not signed) header.d=none; vger.kernel.org; dmarc=none action=none header.from=celeno.com; Received: from AM9P192MB1412.EURP192.PROD.OUTLOOK.COM (2603:10a6:20b:38b::16) by AM0P192MB0260.EURP192.PROD.OUTLOOK.COM (2603:10a6:208:47::23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4219.21; Thu, 17 Jun 2021 16:07:09 +0000 Received: from AM9P192MB1412.EURP192.PROD.OUTLOOK.COM ([fe80::1847:5583:4db7:102f]) by AM9P192MB1412.EURP192.PROD.OUTLOOK.COM ([fe80::1847:5583:4db7:102f%4]) with mapi id 15.20.4242.021; Thu, 17 Jun 2021 16:07:09 +0000 From: viktor.barna@celeno.com To: linux-wireless@vger.kernel.org Cc: Kalle Valo , "David S . Miller" , Jakub Kicinski , Aviad Brikman , Eliav Farber , Oleksandr Savchenko , Shay Bar , Viktor Barna Subject: [RFC v1 184/256] cl8k: add stats.c Date: Thu, 17 Jun 2021 16:01:11 +0000 Message-Id: <20210617160223.160998-185-viktor.barna@celeno.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210617160223.160998-1-viktor.barna@celeno.com> References: <20210617160223.160998-1-viktor.barna@celeno.com> X-Originating-IP: [62.216.42.54] X-ClientProxiedBy: PR3PR09CA0018.eurprd09.prod.outlook.com (2603:10a6:102:b7::23) To AM9P192MB1412.EURP192.PROD.OUTLOOK.COM (2603:10a6:20b:38b::16) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from localhost.localdomain (62.216.42.54) by PR3PR09CA0018.eurprd09.prod.outlook.com (2603:10a6:102:b7::23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4242.18 via Frontend Transport; Thu, 17 Jun 2021 16:05:57 +0000 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: b0d3d35c-6ed1-45c7-fe07-08d931a9cb28 X-MS-TrafficTypeDiagnostic: AM0P192MB0260: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:2; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: FvH3CYKLen7bL6xhpwO99QOns/6stZvRleLapd6n+nGqxHjrwMaKTLfSOjpGDO4KmbYnebB2caBKMD6UnkHL3VM8xdseYWmTsSd39qIdz63EIwSmxVVR2rOBEeah3uYEn303tCVFkUVZ4GfPI46ee6dVKCWqyfdZW1reolW5Hsq6zuy7H18PyHG+pXgXi/g5kAd2Eglg3FKoyCj1RTiGrIbMHnIYgkekRgcO0uZeuag3c2eoXGoNxJiwau38dTro/3ZCiYWsQ9gWG8OnkdxDhqvoLZdm05/OATW/j4qs9I5IDt+JlIKSQD6CrqylfbxiqdyJEea0aCbJDsjLRy0XNQXL6j1pNtoswNOSiKdAEXIxbfpPpFm4h3loDzRhOQMpo11gGfCCpUkIrqTAKIIeSoqca1CpoWC8Cq9/MNMeqZUHm2kgCUm31uhfcaSFbI6aicL1S3AOX5bDzTIz/Gxb/8zPwFsuAFWTBqCwYhuyzMsVD5+qceLofztJnil2fpQgrGEB6FnNCNZLeN57W/BNyJvXxVokcAbO+J2Wbg2BR41e6KCvDc8vOXbfDl6Z0NkXFTeDrR8SgPh0SWOiINGRDNByhHAzSbFU6fgEOsaDJfwJpfUJJP/nC+gvow+js5MruT905VGR76UKSt4IT5XOUXPWYGJwl6YD4SopePz744PzYTZP7Lf4Gxtm+h0N2yy+yrpTaMwEYh7LF/DsgfxsPA== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AM9P192MB1412.EURP192.PROD.OUTLOOK.COM; PTR:; CAT:NONE; SFS:(366004)(8936002)(86362001)(107886003)(4326008)(26005)(8676002)(956004)(45080400002)(186003)(6666004)(2906002)(6512007)(66556008)(2616005)(5660300002)(498600001)(38350700002)(54906003)(38100700002)(16526019)(66946007)(9686003)(83380400001)(55236004)(52116002)(66476007)(6486002)(6916009)(30864003)(6506007)(36756003)(1076003)(69590400013)(32563001)(579004)(559001); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: GU0hAL4BjVMp2Zye58Y7Ind17oyEhVsRfPpPwFQfXO/8BjvxBFv8Aj4beZHDpAvzFbPM/8g9EsaBBxGh8BFMbttgzjNmOEi657ezvFvYJo+IYPtwEpNadv82W+xGkeEW42YC7eAG2Lgu4d+0v8R7blApjAXwmEfYer9xchJF+So2s0p4VTEaHRAEcBuWz1TwxE2ROQrV5bm1G9LCPlsueDhVfXIUtTbsdBhP3V+ePiqRfjA/QSDGu6P04d02fOUnby+yYXpxDhLHM92f/9bCR4Umz0fJZgBPm+FzpK+iRW4pB6ZZI8GkV/8+q01gajvf0w87GSBquuSPFmxB3P3iTrVTRtpRxXyZkKBIj/Wz/jwJ7aLXPSTnAA7pftW0WBN3M8fmuWu6l+pSJkf+D/PVzKYSU9S30Szj+wspG6IF9xJrNuk0osAa4Vfas8Vgx2+Bb88xx8pi4jPICz5+vk+7tPXHwZx4SnnxP7+wdOguibanw/CAia9IrND8IyTjJ0k8zkMo8HSU7rqpIpClvmzvqj46KZxRPXodkLY+R7T8juLoXe/RQOwFX7kDsfUGw7apsdGjHTp31heJZF395CwjVh3qxPJiZnxg5bHH20pfG7JsoUicojUBVq0tcwehOXfkl5njhLQ/9opg2at7DSIGv9XWl87nXnZDsJWC177RN76PBxAtYU2nqRjy9j5cfCHS0rdQjxehauRu3sY5H4ZskSqFY00saD3EKjWGZvzVVBSp7mdRktUztuNb9d/D82c+P2SLs+6oGw60/UNotiwVjfpekm0Xs1TX1W98wWZ5/Cmp/OEF42iYFK1/ZVkFIC8Jq+2X1d4HB2TjQJvo531U6xcmlJWqR3CkuryAuFaYQ+6y9A1E4X7zA8QOzAK3PKTCMFze1i2SVbSdx12GmfW97Mw6p6VCtS3PZFuDi6SRTzOElBIoFeGhB14XPgNwviADAypz31pTjs5O2mDvkZDPcPStw4SdL/Q6IftMipGQTyRFhviaLjrOQLQbBnplJN+nf0vK2i4wuf1M89gq06oi4X58RL4XBZIdFV/OD7qlO75LfgJjIrpsqAHJGBfxpOPZRpGPevpOqK+UjmcncnWafDiPnGmQi1mA6R8HmaTFWOAjM6e8gYfUe15p95QpIcT+8bIcz0Njz/uc160yGkN25qR+IEwifpO29lXwOXdmWhl4+xuzj+T/ec+XmQs1C2MEfC9D9f4pHqq9B6xhqCyT91DzpLCAjx55Jb9FrjV3xTVwL5EWzt46AoX4HGYf/cYYb/XZLkrF/rS9nNa9VcDXe8+Su9JKSP/5H3AqGn2jRHaHmIqgxid1qogEXpYTCTso X-OriginatorOrg: celeno.com X-MS-Exchange-CrossTenant-Network-Message-Id: b0d3d35c-6ed1-45c7-fe07-08d931a9cb28 X-MS-Exchange-CrossTenant-AuthSource: AM9P192MB1412.EURP192.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Jun 2021 16:05:58.0666 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: f313103b-4c9f-4fd3-b5cf-b97f91c4afa8 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: p+dLlNyc7sxFriRht+g8CCHIb59wU7IULwkGtrMhAe7BrSk2Fr81EEtKjCyzxhq60HGA1pT8xljYq08S2LxKbA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0P192MB0260 Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Viktor Barna (Part of the split. Please, take a look at the cover letter for more details). Signed-off-by: Viktor Barna --- drivers/net/wireless/celeno/cl8k/stats.c | 1402 ++++++++++++++++++++++ 1 file changed, 1402 insertions(+) create mode 100644 drivers/net/wireless/celeno/cl8k/stats.c -- 2.30.0 diff --git a/drivers/net/wireless/celeno/cl8k/stats.c b/drivers/net/wireless/celeno/cl8k/stats.c new file mode 100644 index 000000000000..f340f60ebab9 --- /dev/null +++ b/drivers/net/wireless/celeno/cl8k/stats.c @@ -0,0 +1,1402 @@ +// SPDX-License-Identifier: MIT +/* Copyright(c) 2019-2021, Celeno Communications Ltd. */ + +#include +#include "stats.h" +#include "rx/rx.h" +#include "reg/reg_access.h" +#include "sta.h" +#include "band.h" +#include "mib.h" +#include "rate_ctrl.h" +#include "vif.h" +#include "data_rates.h" + +typedef void (*stats_callback)(struct cl_hw *, struct cl_sta *); + +static const char *bw_str[CHNL_BW_MAX_OFDMA] = { + [CHNL_BW_2_5] = "2.5", + [CHNL_BW_5] = "5", + [CHNL_BW_10] = "10", + [CHNL_BW_20] = "20", + [CHNL_BW_40] = "40", + [CHNL_BW_80] = "80", + [CHNL_BW_160] = "160" +}; + +static const char *gi_he_str[WRS_GI_MAX_HE] = { + [WRS_GI_LONG] = "3.2", + [WRS_GI_SHORT] = "1.6", + [WRS_GI_VSHORT] = "0.8" +}; + +static const char *gi_ht_vht_str[WRS_GI_MAX_HT] = { + [WRS_GI_LONG] = "0.8", + [WRS_GI_SHORT] = "0.4", +}; + +static void cl_stats_sta_reset(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + struct cl_ps_stats *ps = &cl_sta->stats->ps; + bool is_ps = ps->is_ps; + unsigned long timestamp_sleep = ps->timestamp_sleep; + + memset(cl_sta->stats, 0, sizeof(struct cl_stats)); + + /* Restore value of power-save state and timestamp */ + ps->is_ps = is_ps; + ps->timestamp_sleep = timestamp_sleep; +} + +static void cl_stats_print_per(struct cl_hw *cl_hw, u32 delay) +{ + u32 system_per = 0, air_per = 0, ampdu_all, + old_phy_error, old_fcs_error, old_overflow, old_complete, + old_ampdu_failed, old_real_fcs, old_ampdu_success, + new_phy_error, new_fcs_error, new_overflow, new_complete, + new_ampdu_failed, new_real_fcs, new_ampdu_success; + char *buf = NULL; + ssize_t buf_size; + int len = 0; + + old_phy_error = cl_mib_cntr_read(cl_hw, MIB_DOT11_RX_PHY_ERROR_COUNT); + old_fcs_error = cl_mib_cntr_read(cl_hw, MIB_DOT11_FCS_ERROR_COUNT); + old_overflow = cl_mib_cntr_read(cl_hw, MIB_DOT11_RX_FIFO_OVERFLOW_COUNT); + old_complete = cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT0) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT1) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT2) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT3) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT4) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT5) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT6) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT7); + old_ampdu_failed = cl_mib_cntr_read(cl_hw, MIB_AMPDU_INCORRECT_RCVED_COUNT); + old_ampdu_success = cl_mib_cntr_read(cl_hw, MIB_RW_U_AMPDU_RECEIVED_COUNT); + old_real_fcs = old_fcs_error - old_overflow; + + mdelay(delay); + + new_phy_error = cl_mib_cntr_read(cl_hw, MIB_DOT11_RX_PHY_ERROR_COUNT); + new_fcs_error = cl_mib_cntr_read(cl_hw, MIB_DOT11_FCS_ERROR_COUNT); + new_overflow = cl_mib_cntr_read(cl_hw, MIB_DOT11_RX_FIFO_OVERFLOW_COUNT); + new_complete = cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT0) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT1) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT2) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT3) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT4) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT5) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT6) + + cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT7); + new_ampdu_failed = cl_mib_cntr_read(cl_hw, MIB_AMPDU_INCORRECT_RCVED_COUNT); + new_ampdu_success = cl_mib_cntr_read(cl_hw, MIB_RW_U_AMPDU_RECEIVED_COUNT); + new_real_fcs = new_fcs_error - new_overflow; + + /* Overflow handling */ + if (old_complete > new_complete || + old_overflow > new_overflow || + old_fcs_error > new_fcs_error || + old_phy_error > new_phy_error || + old_ampdu_failed > new_ampdu_failed) + return; + + ampdu_all = (new_ampdu_failed - old_ampdu_failed) + + (new_ampdu_success - old_ampdu_success); + + if (new_complete - old_complete) { + system_per = ((new_overflow - old_overflow) * 100 / + (new_complete - old_complete)); + } else { + cl_snprintf(&buf, &len, &buf_size, "No successfully received packets\n"); + goto out; + } + + if ((new_fcs_error + new_phy_error + new_complete) - + (old_fcs_error + old_phy_error + old_complete)) + air_per = (new_real_fcs - old_real_fcs) * 100 / + ((new_real_fcs + new_complete) - (old_real_fcs + old_complete)); + + cl_snprintf(&buf, &len, &buf_size, + "Air PER = [%u%%]\n" + "System PER = [%u%%]\n" + "Successfully received packets = [%u]\n" + "Number of phy errors received: [%u]\n" + "Aggregation failure ratio: [%u/%u]\n", + air_per, system_per, new_complete - old_complete, + new_phy_error - old_phy_error, + new_ampdu_failed - old_ampdu_failed, ampdu_all); + +out: + cl_vendor_reply(cl_hw, buf, len); + kfree(buf); +} + +static void cl_stats_print_rssi(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + struct cl_stats *stats = cl_sta->stats; + u32 i = 0, j = 0; + u64 avg_rssi[MAX_ANTENNAS] = {0}; + u64 sum_rssi[MAX_ANTENNAS] = {0}; + u64 total_rssi = 0; + char *buf = NULL; + ssize_t buf_size; + int len = 0; + + cl_snprintf(&buf, &len, &buf_size, + "STA #%u, MAC %pM\n", cl_sta->sta_idx, cl_sta->addr); + + cl_snprintf(&buf, &len, &buf_size, "|----"); + for (j = 0; j < cl_hw->num_antennas; j++) + cl_snprintf(&buf, &len, &buf_size, "-----------"); + cl_snprintf(&buf, &len, &buf_size, "|\n"); + + cl_snprintf(&buf, &len, &buf_size, "|RSSI"); + for (j = 0; j < cl_hw->num_antennas; j++) + cl_snprintf(&buf, &len, &buf_size, "| Ant%u ", j + 1); + cl_snprintf(&buf, &len, &buf_size, "|\n"); + + cl_snprintf(&buf, &len, &buf_size, "|----"); + for (j = 0; j < cl_hw->num_antennas; j++) + cl_snprintf(&buf, &len, &buf_size, "+----------"); + cl_snprintf(&buf, &len, &buf_size, "|\n"); + + for (i = 0; i < RSSI_ARR_SIZE; i++) { + total_rssi = 0; + + for (j = 0; j < cl_hw->num_antennas; j++) { + total_rssi += stats->rssi[i][j]; + sum_rssi[j] += stats->rssi[i][j]; + avg_rssi[j] += (i * stats->rssi[i][j]); + } + + /* Does not print rssi entries with 0 packets */ + if (total_rssi == 0) + continue; + + cl_snprintf(&buf, &len, &buf_size, "|%3d ", -i); + for (j = 0; j < cl_hw->num_antennas; j++) + cl_snprintf(&buf, &len, &buf_size, + "|%10u", stats->rssi[i][j]); + cl_snprintf(&buf, &len, &buf_size, "|\n"); + } + + total_rssi = 0; + + for (j = 0; j < cl_hw->num_antennas; j++) { + if (sum_rssi[j] == 0) + goto out; + + avg_rssi[j] = div64_u64(avg_rssi[j], sum_rssi[j]); + } + + cl_snprintf(&buf, &len, &buf_size, "|----"); + for (j = 0; j < cl_hw->num_antennas; j++) + cl_snprintf(&buf, &len, &buf_size, "+----------"); + cl_snprintf(&buf, &len, &buf_size, "|\n"); + + cl_snprintf(&buf, &len, &buf_size, "|AVG "); + for (j = 0; j < cl_hw->num_antennas; j++) + cl_snprintf(&buf, &len, &buf_size, "|%10lld", -avg_rssi[j]); + cl_snprintf(&buf, &len, &buf_size, "|\n"); + + cl_snprintf(&buf, &len, &buf_size, "|----"); + for (j = 0; j < cl_hw->num_antennas; j++) + cl_snprintf(&buf, &len, &buf_size, "-----------"); + cl_snprintf(&buf, &len, &buf_size, "|\n"); + +out: + cl_vendor_reply(cl_hw, buf, len); + kfree(buf); +} + +static void cl_stats_print_fec_coding(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + struct cl_stats *stats = cl_sta->stats; + char *buf = NULL; + ssize_t buf_size; + int len = 0; + + cl_snprintf(&buf, &len, &buf_size, + "\nSTA #%u, MAC %pM\n", cl_sta->sta_idx, cl_sta->addr); + cl_snprintf(&buf, &len, &buf_size, + "BCC = %u\n", stats->fec_coding[CL_FEC_CODING_BCC]); + cl_snprintf(&buf, &len, &buf_size, + "LDPC = %u\n", stats->fec_coding[CL_FEC_CODING_LDPC]); + + cl_vendor_reply(cl_hw, buf, len); + kfree(buf); +} + +static void cl_stats_print_power_save(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + struct cl_ps_stats *ps_stats = &cl_sta->stats->ps; + char *buf = NULL; + ssize_t buf_size; + int len = 0; + + cl_snprintf(&buf, &len, &buf_size, + "\n"); + cl_snprintf(&buf, &len, &buf_size, + "STA #%u, MAC %pM\n", cl_sta->sta_idx, cl_sta->addr); + cl_snprintf(&buf, &len, &buf_size, + "Current state = %s\n", ps_stats->is_ps ? "SLEEP" : "AWAKE"); + cl_snprintf(&buf, &len, &buf_size, + "|-------------------------|\n"); + cl_snprintf(&buf, &len, &buf_size, + "| Period | Counter |\n"); + cl_snprintf(&buf, &len, &buf_size, + "|-------------------------|\n"); + cl_snprintf(&buf, &len, &buf_size, + "| <= 50ms | %10u |\n", ps_stats->period[PS_PERIOD_50MS]); + cl_snprintf(&buf, &len, &buf_size, + "| <= 100ms | %10u |\n", ps_stats->period[PS_PERIOD_100MS]); + cl_snprintf(&buf, &len, &buf_size, + "| <= 250ms | %10u |\n", ps_stats->period[PS_PERIOD_250MS]); + cl_snprintf(&buf, &len, &buf_size, + "| <= 500ms | %10u |\n", ps_stats->period[PS_PERIOD_500MS]); + cl_snprintf(&buf, &len, &buf_size, + "| <= 750ms | %10u |\n", ps_stats->period[PS_PERIOD_750MS]); + cl_snprintf(&buf, &len, &buf_size, + "| <= 1000ms | %10u |\n", ps_stats->period[PS_PERIOD_1000MS]); + cl_snprintf(&buf, &len, &buf_size, + "| <= 2000ms | %10u |\n", ps_stats->period[PS_PERIOD_2000MS]); + cl_snprintf(&buf, &len, &buf_size, + "| <= 5000ms | %10u |\n", ps_stats->period[PS_PERIOD_5000MS]); + cl_snprintf(&buf, &len, &buf_size, + "| <= 10000ms | %10u |\n", ps_stats->period[PS_PERIOD_10000MS]); + cl_snprintf(&buf, &len, &buf_size, + "| > 10000ms | %10u |\n", ps_stats->period[PS_PERIOD_ABOVE]); + cl_snprintf(&buf, &len, &buf_size, + "|-------------------------|\n"); + + cl_vendor_reply(cl_hw, buf, len); + kfree(buf); +} + +static void _cl_stats_print_tx(char **buf, int *len, ssize_t *buf_size, + struct cl_tx_cntrs *cntrs, + const char *mode_str, const char *bw_str, const char *gi_str, + u8 nss, u8 mcs, u8 bf, + u64 *total_success, u64 *total_fail) +{ + u8 per = 0; + + if (cntrs->fail == 0 && cntrs->success == 0) + return; + + per = (u8)div64_u64(100 * (u64)cntrs->fail, cntrs->fail + cntrs->success); + + cl_snprintf(buf, len, buf_size, + "|%-8s|%-3s|%2u|%3u|%3s|%2u|%10u|%10u|%3u|\n", + mode_str, bw_str, nss, mcs, gi_str, bf, cntrs->success, cntrs->fail, per); + + *total_success += cntrs->success; + *total_fail += cntrs->fail; +} + +static void cl_stats_print_tx_he(char **buf, int *len, ssize_t *buf_size, + struct cl_stats *stats, u64 *total_success, u64 *total_fail) +{ + struct cl_tx_cntrs *cntrs; + u8 bw = 0, nss = 0, mcs = 0, gi = 0, bf = 0; + + for (bw = 0; bw < CHNL_BW_MAX; bw++) + for (nss = 0; nss < WRS_SS_MAX; nss++) + for (mcs = 0; mcs < WRS_MCS_MAX; mcs++) + for (gi = 0; gi < WRS_GI_MAX; gi++) + for (bf = 0; bf < BF_IDX_MAX; bf++) { + cntrs = &stats->tx.he[bw][nss][mcs][gi][bf]; + _cl_stats_print_tx(buf, len, buf_size, + cntrs, + "HE", + bw_str[bw], gi_he_str[gi], + nss, mcs, bf, + total_success, total_fail); + } +} + +static void cl_stats_print_tx_vht(char **buf, int *len, ssize_t *buf_size, + struct cl_stats *stats, u64 *total_success, u64 *total_fail) +{ + struct cl_tx_cntrs *cntrs; + u8 bw = 0, nss = 0, mcs = 0, gi = 0, bf = 0; + + for (bw = 0; bw < CHNL_BW_MAX_VHT; bw++) + for (nss = 0; nss < WRS_SS_MAX; nss++) + for (mcs = 0; mcs < WRS_MCS_MAX_VHT; mcs++) + for (gi = 0; gi < WRS_GI_MAX_VHT; gi++) + for (bf = 0; bf < BF_IDX_MAX; bf++) { + cntrs = &stats->tx.vht[bw][nss][mcs][gi][bf]; + _cl_stats_print_tx(buf, len, buf_size, + cntrs, + "VHT", + bw_str[bw], gi_ht_vht_str[gi], + nss, mcs, bf, + total_success, total_fail); + } +} + +static void cl_stats_print_tx_ht(char **buf, int *len, ssize_t *buf_size, + struct cl_stats *stats, u64 *total_success, u64 *total_fail) +{ + struct cl_tx_cntrs *cntrs; + u8 bw = 0, nss = 0, mcs = 0, gi = 0; + + for (bw = 0; bw < CHNL_BW_MAX_HT; bw++) + for (nss = 0; nss < WRS_SS_MAX; nss++) + for (mcs = 0; mcs < WRS_MCS_MAX_HT; mcs++) + for (gi = 0; gi < WRS_GI_MAX_HT; gi++) { + cntrs = &stats->tx.ht[bw][nss][mcs][gi]; + _cl_stats_print_tx(buf, len, buf_size, + cntrs, + "HT", + bw_str[bw], gi_ht_vht_str[gi], + nss, mcs, 0, + total_success, total_fail); + } +} + +static void cl_stats_print_tx_ofdm(char **buf, int *len, ssize_t *buf_size, + struct cl_stats *stats, u64 *total_success, u64 *total_fail) +{ + struct cl_tx_cntrs *cntrs; + u8 mcs; + + for (mcs = 0; mcs < WRS_MCS_MAX_OFDM; mcs++) { + cntrs = &stats->tx.ofdm[mcs]; + _cl_stats_print_tx(buf, len, buf_size, + cntrs, + "OFDM", bw_str[0], "0", + 0, mcs, 0, + total_success, total_fail); + } +} + +static void cl_stats_print_tx_cck(char **buf, int *len, ssize_t *buf_size, + struct cl_stats *stats, u64 *total_success, u64 *total_fail) +{ + struct cl_tx_cntrs *cntrs; + u8 mcs; + + for (mcs = 0; mcs < WRS_MCS_MAX_CCK; mcs++) { + cntrs = &stats->tx.cck[mcs]; + _cl_stats_print_tx(buf, len, buf_size, + cntrs, + "CCK", bw_str[0], "0", + 0, mcs, 0, + total_success, total_fail); + } +} + +static void cl_stats_print_tx(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + struct cl_stats *stats = cl_sta->stats; + u64 total_success = 0, total_fail = 0; + char *buf = NULL; + ssize_t buf_size; + int len = 0; + + cl_snprintf(&buf, &len, &buf_size, + "\nSTA #%u, MAC %pM\n", cl_sta->sta_idx, cl_sta->addr); + cl_snprintf(&buf, &len, &buf_size, + "------------------------------------------------------\n"); + cl_snprintf(&buf, &len, &buf_size, + "|MODE |BW |SS|MCS|GI |BF| Success | Fail |PER|\n"); + cl_snprintf(&buf, &len, &buf_size, + "|--------+---+--+---+---+--+----------+----------+---|\n"); + + cl_stats_print_tx_he(&buf, &len, &buf_size, stats, &total_success, &total_fail); + cl_stats_print_tx_vht(&buf, &len, &buf_size, stats, &total_success, &total_fail); + cl_stats_print_tx_ht(&buf, &len, &buf_size, stats, &total_success, &total_fail); + cl_stats_print_tx_ofdm(&buf, &len, &buf_size, stats, &total_success, &total_fail); + cl_stats_print_tx_cck(&buf, &len, &buf_size, stats, &total_success, &total_fail); + + cl_snprintf(&buf, &len, &buf_size, + "------------------------------------------------------\n"); + cl_snprintf(&buf, &len, &buf_size, + " |%10llu|%10llu|\n", total_success, total_fail); + cl_snprintf(&buf, &len, &buf_size, + " -----------------------\n"); + cl_snprintf(&buf, &len, &buf_size, + "\n"); + + cl_vendor_reply(cl_hw, buf, len); + kfree(buf); +} + +static void _cl_stats_print_rx(struct cl_hw *cl_hw, struct cl_rx_stats *rx_stats, + struct cl_sta *cl_sta) +{ + u8 mode = 0, mcs = 0, nss = 0, bw = 0, gi = 0, flag = rx_stats->flag; + u16 data_rate = 0, data_rate_div_10 = 0, data_rate_mod_10 = 0; + u64 remainder = 0, packets = 0, total_packets[WRS_MODE_MAX] = {0}, + total_data_rate[WRS_MODE_MAX] = {0}, equivalent_data_rate = 0; + char *buf = NULL; + ssize_t buf_size; + int len = 0; + + if (cl_sta) + cl_snprintf(&buf, &len, &buf_size, + "\nSTA #%u, MAC %pM\n", + cl_sta->sta_idx, cl_sta->addr); + + cl_snprintf(&buf, &len, &buf_size, + "-------------------------------------------------------\n"); + cl_snprintf(&buf, &len, &buf_size, + "| MODE | BW | SS | MCS | GI | Data-Rate | #Packets |\n"); + cl_snprintf(&buf, &len, &buf_size, + "|---------+----+----+-----+----+-----------+----------|\n"); + + if ((flag & RX_STATS_HE_TRIG) == 0) + goto stats_he_ext; + + for (bw = 0; bw < CHNL_BW_MAX_HE; bw++) { + for (nss = 0; nss < WRS_SS_MAX; nss++) { + for (mcs = 0; mcs < WRS_MCS_MAX_HE; mcs++) { + for (gi = 0; gi < WRS_GI_MAX_HE; gi++) { + packets = rx_stats->he_trig[bw][nss][mcs][gi]; + + if (packets == 0) + continue; + + data_rate = cl_data_rates_get_x10(WRS_MODE_HE, bw, + nss, mcs, gi); + data_rate_div_10 = data_rate / 10; + data_rate_mod_10 = data_rate % 10; + + total_packets[WRS_MODE_HE] += packets; + total_data_rate[WRS_MODE_HE] += (packets * data_rate); + + cl_snprintf(&buf, &len, &buf_size, + "| HE_TRIG |%4u|%4u|%5u|%4s| %7u.%u |%10llu|\n", + BW_TO_MHZ(bw), nss, mcs, gi_he_str[gi], + data_rate_div_10, data_rate_mod_10, packets); + } + } + } + } + +stats_he_ext: + if ((flag & RX_STATS_HE_EXT) == 0) + goto stats_he_mu; + + for (bw = 0; bw < CHNL_BW_MAX_HE; bw++) { + for (nss = 0; nss < WRS_SS_MAX; nss++) { + for (mcs = 0; mcs < WRS_MCS_MAX_HE; mcs++) { + for (gi = 0; gi < WRS_GI_MAX_HE; gi++) { + packets = rx_stats->he_ext[bw][nss][mcs][gi]; + + if (packets == 0) + continue; + + data_rate = cl_data_rates_get_x10(WRS_MODE_HE, bw, + nss, mcs, gi); + data_rate_div_10 = data_rate / 10; + data_rate_mod_10 = data_rate % 10; + + total_packets[WRS_MODE_HE] += packets; + total_data_rate[WRS_MODE_HE] += (packets * data_rate); + + cl_snprintf(&buf, &len, &buf_size, + "| HE_EXT |%4u|%4u|%5u|%4s| %7u.%u |%10llu|\n", + BW_TO_MHZ(bw), nss, mcs, gi_he_str[gi], + data_rate_div_10, data_rate_mod_10, packets); + } + } + } + } + +stats_he_mu: + if ((flag & RX_STATS_HE_MU) == 0) + goto stats_he_su; + + for (bw = 0; bw < CHNL_BW_MAX_HE; bw++) { + for (nss = 0; nss < WRS_SS_MAX; nss++) { + for (mcs = 0; mcs < WRS_MCS_MAX_HE; mcs++) { + for (gi = 0; gi < WRS_GI_MAX_HE; gi++) { + packets = rx_stats->he_mu[bw][nss][mcs][gi]; + + if (packets == 0) + continue; + + data_rate = cl_data_rates_get_x10(WRS_MODE_HE, bw, + nss, mcs, gi); + data_rate_div_10 = data_rate / 10; + data_rate_mod_10 = data_rate % 10; + + total_packets[WRS_MODE_HE] += packets; + total_data_rate[WRS_MODE_HE] += (packets * data_rate); + + cl_snprintf(&buf, &len, &buf_size, + "| HE_MU |%4u|%4u|%5u|%4s| %7u.%u |%10llu|\n", + BW_TO_MHZ(bw), nss, mcs, gi_he_str[gi], + data_rate_div_10, data_rate_mod_10, packets); + } + } + } + } + +stats_he_su: + if ((flag & RX_STATS_HE_SU) == 0) + goto stats_vht; + + for (bw = 0; bw < CHNL_BW_MAX_HE; bw++) { + for (nss = 0; nss < WRS_SS_MAX; nss++) { + for (mcs = 0; mcs < WRS_MCS_MAX_HE; mcs++) { + for (gi = 0; gi < WRS_GI_MAX_HE; gi++) { + packets = rx_stats->he_su[bw][nss][mcs][gi]; + + if (packets == 0) + continue; + + data_rate = cl_data_rates_get_x10(WRS_MODE_HE, bw, + nss, mcs, gi); + data_rate_div_10 = data_rate / 10; + data_rate_mod_10 = data_rate % 10; + + total_packets[WRS_MODE_HE] += packets; + total_data_rate[WRS_MODE_HE] += (packets * data_rate); + + cl_snprintf(&buf, &len, &buf_size, + "| HE_SU |%4u|%4u|%5u|%4s| %7u.%u |%10llu|\n", + BW_TO_MHZ(bw), nss, mcs, gi_he_str[gi], + data_rate_div_10, data_rate_mod_10, packets); + } + } + } + } + +stats_vht: + if ((flag & RX_STATS_VHT) == 0) + goto stats_ht; + + for (bw = 0; bw < CHNL_BW_MAX_VHT; bw++) { + for (nss = 0; nss < WRS_SS_MAX; nss++) { + for (mcs = 0; mcs < WRS_MCS_MAX_VHT; mcs++) { + for (gi = 0; gi < WRS_GI_MAX_VHT; gi++) { + packets = rx_stats->vht[bw][nss][mcs][gi]; + + if (packets == 0) + continue; + + data_rate = cl_data_rates_get_x10(WRS_MODE_VHT, + bw, nss, mcs, gi); + data_rate_div_10 = data_rate / 10; + data_rate_mod_10 = data_rate % 10; + + total_packets[WRS_MODE_VHT] += packets; + total_data_rate[WRS_MODE_VHT] += (packets * data_rate); + + cl_snprintf(&buf, &len, &buf_size, + "| VHT |%4u|%4u|%5u|%4s| %7u.%u |%10llu|\n", + BW_TO_MHZ(bw), nss, mcs, gi_ht_vht_str[gi], + data_rate_div_10, data_rate_mod_10, packets); + } + } + } + } + +stats_ht: + if ((flag & RX_STATS_HT) == 0) + goto stats_ofdm; + + for (bw = 0; bw < CHNL_BW_MAX_HT; bw++) { + for (nss = 0; nss < WRS_SS_MAX; nss++) { + for (mcs = 0; mcs < WRS_MCS_MAX_HT; mcs++) { + for (gi = 0; gi < WRS_GI_MAX_HT; gi++) { + packets = rx_stats->ht[bw][nss][mcs][gi]; + + if (packets == 0) + continue; + + data_rate = cl_data_rates_get_x10(WRS_MODE_HT, + bw, nss, mcs, gi); + data_rate_div_10 = data_rate / 10; + data_rate_mod_10 = data_rate % 10; + + total_packets[WRS_MODE_HT] += packets; + total_data_rate[WRS_MODE_HT] += (packets * data_rate); + + cl_snprintf(&buf, &len, &buf_size, + "| HT |%4u|%4u|%5u|%4s| %7u.%u |%10llu|\n", + BW_TO_MHZ(bw), nss, mcs, gi_ht_vht_str[gi], + data_rate_div_10, data_rate_mod_10, packets); + } + } + } + } + +stats_ofdm: + if ((flag & RX_STATS_OFDM) == 0) + goto stats_cck; + + for (mcs = 0; mcs < WRS_MCS_MAX_OFDM; mcs++) { + packets = rx_stats->ofdm[mcs]; + + if (packets == 0) + continue; + + data_rate = cl_data_rates_get_x10(WRS_MODE_OFDM, 0, 0, mcs, 0); + data_rate_div_10 = data_rate / 10; + data_rate_mod_10 = data_rate % 10; + + total_packets[WRS_MODE_OFDM] += packets; + total_data_rate[WRS_MODE_OFDM] += (packets * data_rate); + + cl_snprintf(&buf, &len, &buf_size, + "| OFDM | 20| 0|%5u| 0| %7u.%u |%10llu|\n", + mcs, data_rate_div_10, data_rate_mod_10, packets); + } + +stats_cck: + if ((flag & RX_STATS_CCK) == 0) + goto stats_end; + + for (mcs = 0; mcs < WRS_MCS_MAX_CCK; mcs++) { + packets = rx_stats->cck[mcs]; + + if (packets == 0) + continue; + + data_rate = cl_data_rates_get_x10(WRS_MODE_CCK, 0, 0, mcs, 0); + data_rate_div_10 = data_rate / 10; + data_rate_mod_10 = data_rate % 10; + + total_packets[WRS_MODE_CCK] += packets; + total_data_rate[WRS_MODE_CCK] += (packets * data_rate); + + cl_snprintf(&buf, &len, &buf_size, + "| CCK | 20| 0|%5u| 0| %7u.%u |%10llu|\n", + mcs, data_rate_div_10, data_rate_mod_10, packets); + } + +stats_end: + cl_snprintf(&buf, &len, &buf_size, + "-------------------------------------------------------\n"); + + for (mode = 0; mode < WRS_MODE_MAX; mode++) { + if (total_packets[mode] == 0) + continue; + + equivalent_data_rate = div64_u64(total_data_rate[mode], total_packets[mode]); + data_rate_div_10 = (u16)div64_u64_rem(equivalent_data_rate, 10, &remainder); + + cl_snprintf(&buf, &len, &buf_size, + "%s: Equivalent data rate = %u.%u Mbps\n\n", + WRS_MODE_STR(mode), data_rate_div_10, (u16)remainder); + } + + cl_vendor_reply(cl_hw, buf, len); + kfree(buf); +} + +static void cl_stats_print_rx(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + if (cl_hw->chip->conf->ce_production_mode) + _cl_stats_print_rx(cl_hw, cl_hw->rx_stats, NULL); + else + _cl_stats_print_rx(cl_hw, &cl_sta->stats->rx, cl_sta); +} + +static void cl_stats_print_cpu(struct cl_hw *cl_hw) +{ + u32 cpu = 0; + char *buf = NULL; + ssize_t buf_size; + int len = 0; + struct cl_cpu_cntr *cpu_cntr = &cl_hw->cpu_cntr; + + cl_snprintf(&buf, &len, &buf_size, "|-------------------------|\n"); + cl_snprintf(&buf, &len, &buf_size, "|CPU|Tx Agg |Tx Single |\n"); + cl_snprintf(&buf, &len, &buf_size, "|---+----------+----------|\n"); + + for (cpu = 0; cpu < CPU_MAX_NUM; cpu++) { + if (cpu_cntr->tx_agg[cpu] == 0 && cpu_cntr->tx_single[cpu] == 0) + continue; + + cl_snprintf(&buf, &len, &buf_size, "| %u |%10u|%10u|\n", + cpu, + cpu_cntr->tx_agg[cpu], + cpu_cntr->tx_single[cpu]); + } + + cl_snprintf(&buf, &len, &buf_size, "|-------------------------|\n"); + + cl_vendor_reply(cl_hw, buf, len); + kfree(buf); +} + +static void cl_sta_print_do(struct cl_hw *cl_hw, u8 sta_idx, stats_callback callback) +{ + struct cl_sta *cl_sta; + + cl_sta_lock(cl_hw); + + cl_sta = cl_sta_get(cl_hw, sta_idx); + if (cl_sta) + callback(cl_hw, cl_sta); + + cl_sta_unlock(cl_hw); +} + +static void cl_stats_cli_cpu(struct cl_hw *cl_hw, bool action) +{ + if (action) + cl_stats_print_cpu(cl_hw); + else + memset(&cl_hw->cpu_cntr, 0, sizeof(struct cl_cpu_cntr)); +} + +static void cl_stats_cli_rx_info(struct cl_hw *cl_hw, bool action) +{ + if (action) + cl_rx_info_print(cl_hw); + else + cl_rx_info_reset(cl_hw); +} + +static void cll_stats_config_ps(struct cl_sta *cl_sta) +{ + cl_sta->stats->ps.timestamp_sleep = jiffies; + cl_sta->stats->ps.is_ps = test_sta_flag(cl_sta->stainfo, WLAN_STA_PS_STA) ? true : false; +} + +static void cl_stats_free(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + kfree(cl_sta->stats); + cl_sta->stats = NULL; +} + +static void cl_stats_disable(struct cl_hw *cl_hw) +{ + pr_debug("Statistics disabled\n"); + cl_hw->conf->ci_stats_en = false; + cl_sta_loop(cl_hw, cl_stats_free); + + if (cl_hw->chip->conf->ce_production_mode) { + kfree(cl_hw->rx_stats); + cl_hw->rx_stats = NULL; + } +} + +static void cl_stats_enable(struct cl_hw *cl_hw) +{ + /* + * Allocate for all existing stations. + * If one of the allocations fails disable ci_stats_en (and free the + * stations that were already allocated). + * In production mode also allocate cl_hw->rx_stats + */ + struct cl_sta *cl_sta = NULL; + bool success = true; + + if (cl_hw->chip->conf->ce_production_mode) { + cl_hw->rx_stats = kzalloc(sizeof(*cl_hw->rx_stats), GFP_ATOMIC); + + if (!cl_hw->rx_stats) + goto out; + } + + read_lock(&cl_hw->cl_sta_db.lock); + + list_for_each_entry(cl_sta, &cl_hw->cl_sta_db.head, list) { + cl_sta->stats = kzalloc(sizeof(*cl_sta->stats), GFP_ATOMIC); + + if (cl_sta->stats) { + cll_stats_config_ps(cl_sta); + } else { + success = false; + break; + } + } + + if (success) { + pr_debug("Statistics enabled\n"); + cl_hw->conf->ci_stats_en = true; + } else { + list_for_each_entry(cl_sta, &cl_hw->cl_sta_db.head, list) + cl_stats_free(cl_hw, cl_sta); + } + +out: + read_unlock(&cl_hw->cl_sta_db.lock); +} + +static void cl_stats_cli_enable(struct cl_hw *cl_hw, bool enable) +{ + spin_lock_bh(&cl_hw->lock_stats); + + if (enable == cl_hw->conf->ci_stats_en) { + pr_debug("Statistics are already %s\n", enable ? "Enabled" : "Disabled"); + goto out; + } + + if (enable) + cl_stats_enable(cl_hw); + else + cl_stats_disable(cl_hw); + +out: + spin_unlock_bh(&cl_hw->lock_stats); +} + +static void cl_stats_cli_rx(struct cl_hw *cl_hw, u8 sta_idx) +{ + spin_lock_bh(&cl_hw->lock_stats); + + if (!cl_hw->conf->ci_stats_en) + goto out; + + if (cl_hw->chip->conf->ce_production_mode) { + cl_stats_print_rx(cl_hw, NULL); + } else { + if (sta_idx == STA_IDX_INVALID) + cl_sta_loop(cl_hw, cl_stats_print_rx); + else + cl_sta_print_do(cl_hw, sta_idx, cl_stats_print_rx); + } + +out: + spin_unlock_bh(&cl_hw->lock_stats); +} + +static void cl_stats_cli_rssi(struct cl_hw *cl_hw, u8 sta_idx) +{ + spin_lock_bh(&cl_hw->lock_stats); + + if (!cl_hw->conf->ci_stats_en) + goto out; + + if (sta_idx == STA_IDX_INVALID) + cl_sta_loop(cl_hw, cl_stats_print_rssi); + else + cl_sta_print_do(cl_hw, sta_idx, cl_stats_print_rssi); + +out: + spin_unlock_bh(&cl_hw->lock_stats); +} + +static void cl_stats_cli_fec_coding(struct cl_hw *cl_hw, u8 sta_idx) +{ + spin_lock_bh(&cl_hw->lock_stats); + + if (!cl_hw->conf->ci_stats_en) + goto out; + + if (sta_idx == STA_IDX_INVALID) + cl_sta_loop(cl_hw, cl_stats_print_fec_coding); + else + cl_sta_print_do(cl_hw, sta_idx, cl_stats_print_fec_coding); + +out: + spin_unlock_bh(&cl_hw->lock_stats); +} + +static void cl_stats_cli_ps(struct cl_hw *cl_hw, u8 sta_idx) +{ + spin_lock_bh(&cl_hw->lock_stats); + + if (!cl_hw->conf->ci_stats_en) + goto out; + + if (sta_idx == STA_IDX_INVALID) + cl_sta_loop(cl_hw, cl_stats_print_power_save); + else + cl_sta_print_do(cl_hw, sta_idx, cl_stats_print_power_save); + +out: + spin_unlock_bh(&cl_hw->lock_stats); +} + +static void cl_stats_cli_tx(struct cl_hw *cl_hw, u8 sta_idx) +{ + spin_lock_bh(&cl_hw->lock_stats); + + if (!cl_hw->conf->ci_stats_en) + goto out; + + if (sta_idx == STA_IDX_INVALID) + cl_sta_loop(cl_hw, cl_stats_print_tx); + else + cl_sta_print_do(cl_hw, sta_idx, cl_stats_print_tx); + +out: + spin_unlock_bh(&cl_hw->lock_stats); +} + +static void cl_stats_cli_reset(struct cl_hw *cl_hw, u8 sta_idx) +{ + spin_lock_bh(&cl_hw->lock_stats); + + if (!cl_hw->conf->ci_stats_en) + goto out; + + if (cl_hw->chip->conf->ce_production_mode) { + memset(cl_hw->rx_stats, 0, sizeof(struct cl_rx_stats)); + } else { + if (sta_idx == STA_IDX_INVALID) + cl_sta_loop(cl_hw, cl_stats_sta_reset); + else + cl_sta_print_do(cl_hw, sta_idx, cl_stats_sta_reset); + } + +out: + spin_unlock_bh(&cl_hw->lock_stats); +} + +static int cl_stats_cli_help(struct cl_hw *cl_hw) +{ + char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + int err = 0; + + if (!buf) + return -ENOMEM; + + snprintf(buf, PAGE_SIZE, + "stats usage:\n" + "-a : Print air PER and system PER [delay-ms)]\n" + "-b : CPU stats [0-reset, 1-print]\n" + "-e : Enable/Disable statistics [0-dis, 1-en]\n" + "-f : Print RX FEC coding counters [sta_idx]\n" + "-p : Print power-save statistics [sta_idx]\n" + "-r : Print RX stats table [sta_idx]\n" + "-s : Print RSSI stats tables [sta_idx]\n" + "-t : Print TX stats table [sta_idx]\n" + "-u : Uplink stats [0-reset, 1-print]\n" + "-z : Reset stats tables [sta_idx]\n"); + + err = cl_vendor_reply(cl_hw, buf, strlen(buf)); + kfree(buf); + + return err; +} + +static void _cl_stats_update_tx(struct cl_hw *cl_hw, struct cl_sta *cl_sta, + struct cl_agg_tx_report *agg_report) +{ + struct cl_stats *stats = cl_sta->stats; + struct cl_tx_cntrs *cntrs; + union cl_rate_ctrl_info rate_ctrl_info = {.word = agg_report->rate_cntrl_info}; + u8 bw, nss, mcs, gi, bf; + + switch (rate_ctrl_info.field.format_mod) { + case WRS_MODE_HE: + nss = (rate_ctrl_info.field.mcs_index >> 4); + mcs = (rate_ctrl_info.field.mcs_index & 0xF); + gi = rate_ctrl_info.field.gi; + bw = rate_ctrl_info.field.bw; + bf = agg_report->bf; + cntrs = &stats->tx.he[bw][nss][mcs][gi][bf]; + break; + case WRS_MODE_VHT: + bw = rate_ctrl_info.field.bw; + nss = (rate_ctrl_info.field.mcs_index >> 4); + mcs = (rate_ctrl_info.field.mcs_index & 0xF); + gi = rate_ctrl_info.field.gi; + bf = agg_report->bf; + cntrs = &stats->tx.vht[bw][nss][mcs][gi][bf]; + break; + case WRS_MODE_HT: + bw = rate_ctrl_info.field.bw; + nss = (rate_ctrl_info.field.mcs_index >> 3); + mcs = (rate_ctrl_info.field.mcs_index & 0x7); + gi = rate_ctrl_info.field.gi; + cntrs = &stats->tx.ht[bw][nss][mcs][gi]; + break; + case WRS_MODE_OFDM: + mcs = rate_ctrl_info.field.mcs_index - RATE_CTRL_OFFSET_OFDM; + cntrs = &stats->tx.ofdm[mcs]; + break; + case WRS_MODE_CCK: + mcs = rate_ctrl_info.field.mcs_index; + cntrs = &stats->tx.cck[mcs]; + break; + default: + return; + } + + cntrs->success += agg_report->success; + cntrs->fail += agg_report->fail; +} + +void cl_stats_init(struct cl_hw *cl_hw) +{ + spin_lock_init(&cl_hw->lock_stats); + + if (cl_hw->conf->ci_stats_en && cl_hw->chip->conf->ce_production_mode) { + cl_hw->rx_stats = kzalloc(sizeof(*cl_hw->rx_stats), GFP_ATOMIC); + + if (!cl_hw->rx_stats) + cl_hw->conf->ci_stats_en = false; + } +} + +void cl_stats_deinit(struct cl_hw *cl_hw) +{ + spin_lock_bh(&cl_hw->lock_stats); + + if (cl_hw->conf->ci_stats_en && cl_hw->chip->conf->ce_production_mode) { + cl_hw->conf->ci_stats_en = false; + + kfree(cl_hw->rx_stats); + cl_hw->rx_stats = NULL; + } + + spin_unlock_bh(&cl_hw->lock_stats); +} + +void cl_stats_sta_add(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + /* If allocation failed disable ci_stats_en, and free the memory of all other stations */ + bool disable = false; + + /* Take regular lock and not BH, because cl_sta_add_to_lut() already disables BH */ + spin_lock(&cl_hw->lock_stats); + + if (cl_hw->conf->ci_stats_en) { + cl_sta->stats = kzalloc(sizeof(*cl_sta->stats), GFP_ATOMIC); + + if (cl_sta->stats) + cll_stats_config_ps(cl_sta); + else + disable = true; + } + + spin_unlock(&cl_hw->lock_stats); + + if (disable) + cl_stats_cli_enable(cl_hw, false); +} + +void cl_stats_sta_remove(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + spin_lock_bh(&cl_hw->lock_stats); + + if (cl_hw->conf->ci_stats_en) + cl_stats_free(cl_hw, cl_sta); + + spin_unlock_bh(&cl_hw->lock_stats); +} + +void cl_stats_update_tx_agg(struct cl_hw *cl_hw, struct cl_sta *cl_sta, + struct cl_agg_tx_report *agg_report) +{ + spin_lock(&cl_hw->lock_stats); + + if (cl_hw->conf->ci_stats_en) { + struct cl_stats *stats = cl_sta->stats; + + stats->tx.agg_cntr++; + stats->tx.fail_cntr += agg_report->fail; + _cl_stats_update_tx(cl_hw, cl_sta, agg_report); + } + + spin_unlock(&cl_hw->lock_stats); +} + +void cl_stats_update_tx_single(struct cl_hw *cl_hw, struct cl_sta *cl_sta, + struct cl_agg_tx_report *agg_report) +{ + spin_lock(&cl_hw->lock_stats); + + if (cl_hw->conf->ci_stats_en) { + cl_sta->stats->tx.fail_cntr += agg_report->fail; + _cl_stats_update_tx(cl_hw, cl_sta, agg_report); + } + + spin_unlock(&cl_hw->lock_stats); +} + +void cl_stats_update_rx_rssi(struct cl_hw *cl_hw, struct cl_sta *cl_sta, s8 rssi[MAX_ANTENNAS]) +{ + int i; + s8 rx_rssi; + + spin_lock(&cl_hw->lock_stats); + + if (!cl_hw->conf->ci_stats_en) + goto out; + + for (i = 0; i < cl_hw->num_antennas; i++) { + rx_rssi = rssi[i] * -1; + + if (rx_rssi >= 0 && rx_rssi < RSSI_ARR_SIZE) + cl_sta->stats->rssi[rx_rssi][i]++; + } + +out: + spin_unlock(&cl_hw->lock_stats); +} + +void _cl_stats_update_rx_rate(struct cl_hw *cl_hw, struct cl_rx_stats *rx_stats, + struct hw_rxhdr *rxhdr) +{ + u8 bw, nss, mcs, gi; + + switch (rxhdr->format_mod) { + case FORMATMOD_HE_TRIG: + nss = rxhdr->n_sts & 0x3; + mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_HE); + gi = min_t(u8, rxhdr->gi_type, WRS_GI_MAX_HE); + rx_stats->he_trig[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_HE_TRIG; + break; + case FORMATMOD_HE_EXT: + nss = rxhdr->n_sts & 0x3; + mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_HE); + gi = min_t(u8, rxhdr->gi_type, WRS_GI_MAX_HE); + rx_stats->he_ext[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_HE_EXT; + break; + case FORMATMOD_HE_MU: + nss = rxhdr->n_sts & 0x3; + mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_HE); + gi = min_t(u8, rxhdr->gi_type, WRS_GI_MAX_HE); + rx_stats->he_mu[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_HE_MU; + break; + case FORMATMOD_HE_SU: + nss = rxhdr->n_sts & 0x3; + mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_HE); + gi = min_t(u8, rxhdr->gi_type, WRS_GI_MAX_HE); + rx_stats->he_su[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_HE_SU; + break; + case FORMATMOD_VHT: + nss = rxhdr->n_sts & 0x3; + mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_VHT); + gi = rxhdr->gi_type & 0x1; + rx_stats->vht[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_VHT; + break; + case FORMATMOD_HT_MF: + case FORMATMOD_HT_GF: + bw = rxhdr->ch_bw & 0x1; + nss = (rxhdr->mcs >> 3) & 0x3; + mcs = rxhdr->mcs & 0x7; + gi = rxhdr->gi_type & 0x1; + rx_stats->ht[bw][nss][mcs][gi] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_HT; + break; + case FORMATMOD_NON_HT: + if (rxhdr->mcs >= RATE_CTRL_OFFSET_OFDM) { + mcs = (rxhdr->mcs - RATE_CTRL_OFFSET_OFDM) & 0x7; + rx_stats->ofdm[mcs] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_OFDM; + } else if (cl_band_is_24g(cl_hw)) { + mcs = rxhdr->mcs & 0x3; + rx_stats->cck[mcs] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_CCK; + } + break; + } +} + +void cl_stats_update_rx_rate(struct cl_hw *cl_hw, struct cl_sta *cl_sta, struct hw_rxhdr *rxhdr) +{ + spin_lock(&cl_hw->lock_stats); + + if (cl_hw->conf->ci_stats_en) { + _cl_stats_update_rx_rate(cl_hw, &cl_sta->stats->rx, rxhdr); + cl_sta->stats->fec_coding[rxhdr->fec_coding]++; + } + + spin_unlock(&cl_hw->lock_stats); +} + +void cl_stats_update_rx_rate_production(struct cl_hw *cl_hw, struct hw_rxhdr *rxhdr) +{ + spin_lock(&cl_hw->lock_stats); + + if (cl_hw->conf->ci_stats_en) + _cl_stats_update_rx_rate(cl_hw, cl_hw->rx_stats, rxhdr); + + spin_unlock(&cl_hw->lock_stats); +} + +void cl_stats_update_ps(struct cl_hw *cl_hw, struct cl_sta *cl_sta, bool is_ps) +{ + struct cl_ps_stats *ps; + + spin_lock(&cl_hw->lock_stats); + + if (!cl_hw->conf->ci_stats_en) + goto out; + + ps = &cl_sta->stats->ps; + + if (ps->is_ps == is_ps) + goto out; + + ps->is_ps = is_ps; + + if (is_ps) { + ps->timestamp_sleep = jiffies; + } else { + unsigned long sleep_time = jiffies_to_msecs(jiffies - ps->timestamp_sleep); + + if (sleep_time <= 50) + ps->period[PS_PERIOD_50MS]++; + else if (sleep_time <= 100) + ps->period[PS_PERIOD_100MS]++; + else if (sleep_time <= 250) + ps->period[PS_PERIOD_250MS]++; + else if (sleep_time <= 500) + ps->period[PS_PERIOD_500MS]++; + else if (sleep_time <= 750) + ps->period[PS_PERIOD_750MS]++; + else if (sleep_time <= 1000) + ps->period[PS_PERIOD_1000MS]++; + else if (sleep_time <= 2000) + ps->period[PS_PERIOD_2000MS]++; + else if (sleep_time <= 5000) + ps->period[PS_PERIOD_5000MS]++; + else if (sleep_time <= 10000) + ps->period[PS_PERIOD_10000MS]++; + else + ps->period[PS_PERIOD_ABOVE]++; + } + +out: + spin_unlock(&cl_hw->lock_stats); +} + +int cl_stats_cli(struct cl_hw *cl_hw, struct cl_vif *cl_vif, struct cli_params *cli_params) +{ + bool print_per = false; + bool cpu_stats = false; + bool enable_tx_rx_rssi = false; + bool print_rx = false; + bool print_rssi = false; + bool print_fec_coding = false; + bool print_power_save = false; + bool print_tx = false; + bool uplink_stats = false; + bool reset_stats = false; + u32 expected_params = -1; + + switch (cli_params->option) { + case 'a': + print_per = true; + expected_params = 1; + break; + case 'b': + cpu_stats = true; + expected_params = 1; + break; + case 'e': + enable_tx_rx_rssi = true; + expected_params = 1; + break; + case 'f': + print_fec_coding = true; + expected_params = 1; + break; + case 'p': + print_power_save = true; + expected_params = 1; + break; + case 't': + print_tx = true; + expected_params = 1; + break; + case 'r': + print_rx = true; + expected_params = cl_hw->chip->conf->ce_production_mode ? 0 : 1; + break; + case 'z': + reset_stats = true; + expected_params = cl_hw->chip->conf->ce_production_mode ? 0 : 1; + break; + case 's': + print_rssi = true; + expected_params = 1; + break; + case 'u': + uplink_stats = true; + expected_params = 1; + break; + case '?': + return cl_stats_cli_help(cl_hw); + default: + cl_dbg_err(cl_hw, "Illegal option (%c) - try '?' for help\n", cli_params->option); + goto out_err; + } + + if (expected_params != cli_params->num_params) { + cl_dbg_err(cl_hw, "Wrong number of arguments (expected %u) (actual %u)\n", + expected_params, cli_params->num_params); + goto out_err; + } + + if (print_per) { + cl_stats_print_per(cl_hw, (u32)cli_params->params[0]); + return 0; + } + + if (cpu_stats) { + cl_stats_cli_cpu(cl_hw, (bool)cli_params->params[0]); + return 0; + } + + if (uplink_stats) { + cl_stats_cli_rx_info(cl_hw, (bool)cli_params->params[0]); + return 0; + } + + if (enable_tx_rx_rssi) { + cl_stats_cli_enable(cl_hw, (bool)cli_params->params[0]); + return 0; + } + + if (!cl_hw->conf->ci_stats_en) { + pr_debug("Statistics are disabled!\n" + "To enable them type: " + "'iwcl cecli stats -e.1'\n"); + return 0; + } + + if (print_rx) { + u8 sta_idx = (u8)cli_params->params[0]; + + cl_stats_cli_rx(cl_hw, sta_idx); + return 0; + } + + if (print_rssi) { + u8 sta_idx = (u8)cli_params->params[0]; + + cl_stats_cli_rssi(cl_hw, sta_idx); + return 0; + } + + if (print_fec_coding) { + u8 sta_idx = (u8)cli_params->params[0]; + + cl_stats_cli_fec_coding(cl_hw, sta_idx); + return 0; + } + + if (print_power_save) { + u8 sta_idx = (u8)cli_params->params[0]; + + cl_stats_cli_ps(cl_hw, sta_idx); + return 0; + } + + if (print_tx) { + u8 sta_idx = (u8)cli_params->params[0]; + + cl_stats_cli_tx(cl_hw, sta_idx); + return 0; + } + + if (reset_stats) { + u8 sta_idx = (u8)cli_params->params[0]; + + cl_stats_cli_reset(cl_hw, sta_idx); + return 0; + } + +out_err: + return -EIO; +}