From patchwork Thu Jun 17 16:02:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viktor Barna X-Patchwork-Id: 462676 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 77C0DC48BE5 for ; Thu, 17 Jun 2021 16:11:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4FBAF61428 for ; Thu, 17 Jun 2021 16:11:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229580AbhFQQNG (ORCPT ); Thu, 17 Jun 2021 12:13:06 -0400 Received: from mail-eopbgr10062.outbound.protection.outlook.com ([40.107.1.62]:13120 "EHLO EUR02-HE1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S233713AbhFQQLx (ORCPT ); Thu, 17 Jun 2021 12:11:53 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=SSMpkF0YvcL0Dk8ncLNHpS/LTUIVRcK5uhdpMr7UoppuyVFipfHtS2B181rynawnJ74lpsD/Jm9gPNZnxnmpbhwgdrcG1j3/59xMHEKvMzYjtVJ1masTe+VEp5dYjalv0me03ofgClJk0CU7R4d/TUVPIaoI0LiUL1dvGVLJzuozuhQp8+OGtR9MLu/J3kNmiN/AT1Qbi67WOC3rEeVDSUF+1tfWYgbHPMoeBuPjSe8zI61kZIF/PbZON7cuK4wKII6ossEwlq95sYR7E98GjmiA70p4l7l1h6udu7DgRliofdmGH9riT0sGZJtaI60vr98WO6nMswv14IV8NrZidw== 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=/zj9/ivXdi6XLVKAukd4eP6IpgdaQCFPAgiT9M9l0SA=; b=c+sYEdIKLvJ6tepdKTFsq6+tYEMwe+4hhW9IxPZSelcHmbYTxd+TT5qBPe9e93rH8WEp66g08rDnMzweVoOmMnnc1/8TXak4f4bOzqlo+k2Qm1u2xI9p/fwI7vgQDt7hrCEHismIvKzsRB3aoU0fz+34vRub6u7kkw0NS5vjeUDHu1G1+iwAWNCnIqFCc/slpbceGUpllcM5xpah7B8AVZo0W0vKSo8wsYEkBjGcdyap09FqU4GaH6JvdFYcASySxbZFyyT8AOxjAfPQ3RRlf6qLX9bL4psbT+7UWb+tQKa1cxyvgtn0QTe+mxdPtWoIBFTI7wI5v0iDP8F0wUsT2g== 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=/zj9/ivXdi6XLVKAukd4eP6IpgdaQCFPAgiT9M9l0SA=; b=dBJxpzcxFCFgPGdOB6/orGiYj5u9xaBlYMijZwGgoN07ia0UmNul4Kb9G4kkQxHFi7QKhKpaCcfQ+uL8Szfx1a7u05B/3YoE3OuyisH8EAQSgjCeQoK9NpbPqGu4ihewNgCQl5hXOIrMjWWVTuyWIYc/bZgZiQ0EpRzmOzRVooQ= 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 AM0P192MB0515.EURP192.PROD.OUTLOOK.COM (2603:10a6:208:45::19) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4242.19; Thu, 17 Jun 2021 16:08:01 +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:08:01 +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 239/256] cl8k: add wrs/wrs.c Date: Thu, 17 Jun 2021 16:02:06 +0000 Message-Id: <20210617160223.160998-240-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:07:00 +0000 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 2f027522-1b74-4187-4072-08d931a9f0a3 X-MS-TrafficTypeDiagnostic: AM0P192MB0515: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:4502; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: HgRVvUOIpvQWptmD/Zfj7ivhec98rU3Juad2wsPF+1k6UX3pc7e2roNOywTON1g2EN/cBeOpO2JfbsLjxwxYPji047ORoGX1WBY8r18GHp/mE3pkAL6Pgd0gWGTveNOlpzSP1IdoOetvjcXjIqP4hxJkTEppmCX9TvV2Q0ga1pdKbf0+giVTAXyOrL5737JXLKSigY7wmR1FEk5BiNijeN3DVevrhfXxnuOexLPolR59IPWxi01paTaw7JPdPWUKz29PjS8WGY47+T1kF+rJ3fAuaZMorp4GJr45eJmKUKfR4qTdBqWh5PtA4JdjwIImoZW/s5BF3xKXtwDmTN91k62K1wR0OHOcUTl9VMXbPrRVoHF5rWvAKTIKZKyxbWnjn7CtxogdqwN+qrQ7NeRV3mUwW4f6i95cjETvdBt+YsYIrCYkSuWPq9Gaa1lpJyXVTkzHZnKbyMFpj6xlz5cF+k6GJLmYX42ockPSz+KzyD0+CR5Nhu8dYtzDxJzl3sO2XMEquEcx1gKW5S7Wv/7qFJOSGM8roXVTIKYylmDafnel1Q3Y1mG5nGEgfh5W5RgquEUjQ2StfjD5Fdfd53+/6gr4B18Fbau8cTtn/g22r+Kn7GwhUIM83B2QTyx9VHO9upDZ8q+PZ960ISqeeSGNGMC1YOmxFnEsA1eNBqrQjKpacYW7tr640XcKe2g3H3huCH0LqRKlkU2EO+acIQu3r4wtse3PBY2l1eIGg7YCKzU= 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:(396003)(376002)(39850400004)(136003)(366004)(346002)(4326008)(16526019)(83380400001)(1076003)(8676002)(9686003)(55236004)(36756003)(478600001)(8936002)(956004)(186003)(6486002)(38350700002)(6512007)(6916009)(52116002)(86362001)(5660300002)(2906002)(38100700002)(66946007)(2616005)(316002)(6506007)(30864003)(26005)(54906003)(66476007)(107886003)(66556008)(69590400013)(32563001)(559001)(579004)(309714004); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: Gk0DPqhrNVm7dBy6T5HCIOC0HXen1X9xeeZCSmz3iA9Pial+mPW+9+9dPyQaKGJNKok6u6AnXApcnd6GCAn2An6iXdI3evlWa584JoZEr1H96Y+N5cXtg/4RTJkgLci43Yx6irqCpD7D4CXarTbDm/ZAMLWtbrOwqfbyPPTfYtB2S3HNyk7BhfTTN1UkshRfmDWeAmi9lZAvHIlyi2uAgzIPOXK8wKa/j6FdS5/sa8TrDinrIiPgTIQ6XTQ9TqtgG2eMNOyOl8okSPdY5YYm7vImNm5JF81WiM1trNGfQX0upa93EnnWlsjpzwFRbYFxKG5HO5RqxioeNl1iCCJ0T/kxiBOBhJEKpZQSGh+N2MallxAoAPs9HM9SqWL4qrC4YbnYgAgwHMRDtxolXYmLc2K/MvdHWbigBwrLshYXfKmk/NaH3Os5cm2z1IBRLFe3pP+h9/oov+lerihUP5XUYLFP7aoflPBIGSxGOkdkAwlJ5LVWEru3jCL/vj0UEB8zZB/jZjGIlI1kIG7ZZVOmx8OnNvPhoiVu8tRDpXzbmLj2dxYC1WN0XnyZfc+ySLX4A3yCEqlimOqh00OPszEiWANi6yXPgjO8mkaIF+tubMkHyudD0g1/yMe1unF9MgDgZ6LG5U2bz29SscNdAHgh/m3uSKrCDfWPCXf5SxPNkKrvc1rpVpDQUdA0HdJm5wAeO9TzQ1Enad3HJCkLrRMdAPC7igSjsqlfSbfaozpVXSvM5bqtW6jGdkEg+p45P2VeFz00MnmmYkfafhj8ZiJaZ805+naC3umcBKP3tX3AKOXxVYItMffzjWA+HTvxHwI1zYzj76cWkFfUns6+jEucKZKEEYt80kLafi6sSJuFKF1a1P4pfn1Ktqy5F/VW0BsPEnWgO6aQNi+pjFd3eV7LLsu5a4bCzXAauMmkD3nPws+TsVHRHSaUVCFjSWL+DHsv4EcyQ3bx/+CIm5WESxBjYhmH+z1eO3tosLY16iPRcERfhnJU+iMp9vn5E8GKHAB3VpudK1Blbid79Hlpm/B0ZWGcZuL3rWmhrs8WRqW68Hf8tE+bml0En2gyPB/Fgc03y5chra2bAne5WTq3bzzyLurN2HlDn/GAbQA4xgzTg//6Wfdj+GmLbUldsoztoYQlkB8hMAOyomMmlbJkfE3B71NNhzN0dyyVeC1KiY9cR7BKpsuTP/SKEQVlogZgll+dExTjtdmnyH4ykreVKbo3/dfWUX7s2PBq0Es3O+Tgr1wmz4644Wn5gyyoXnv1lqeMLQOY7QSZS+q3wtTfzKtSZ/uNQgAMyRwvooPxFr6PpH3foG3Reoz1RGV8htkfonRt X-OriginatorOrg: celeno.com X-MS-Exchange-CrossTenant-Network-Message-Id: 2f027522-1b74-4187-4072-08d931a9f0a3 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:07:00.8858 (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: iz+jtC/HKXwGddPWivrF5UfaUwaB433pzYTMjw4tfF1eeoG9FCYyu76wNdEXZtRLSEog8zzJXEjBQxZC3wI8hA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0P192MB0515 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/wrs/wrs.c | 1159 ++++++++++++++++++++ 1 file changed, 1159 insertions(+) create mode 100644 drivers/net/wireless/celeno/cl8k/wrs/wrs.c -- 2.30.0 diff --git a/drivers/net/wireless/celeno/cl8k/wrs/wrs.c b/drivers/net/wireless/celeno/cl8k/wrs/wrs.c new file mode 100644 index 000000000000..5e2af5d34c8e --- /dev/null +++ b/drivers/net/wireless/celeno/cl8k/wrs/wrs.c @@ -0,0 +1,1159 @@ +// SPDX-License-Identifier: MIT +/* Copyright(c) 2019-2021, Celeno Communications Ltd. */ + +#include +#include "wrs/wrs.h" +#include "wrs/wrs_stats.h" +#include "wrs/wrs_tables.h" +#include "wrs/wrs_rssi.h" +#include "env_det.h" +#include "utils/math.h" +#include "rssi.h" +#include "band.h" +#include "rate_ctrl.h" +#include "chip.h" +#include "ext/dyn_bcast_rate.h" +#include "reg/reg_mac_hw.h" +#include "data_rates.h" +#include "rsrc_mgmt.h" + +static void cl_wrs_reset_params_cntrs(struct cl_wrs_params *wrs_params) +{ + wrs_params->frames_total = 0; + wrs_params->fail_total = 0; + wrs_params->ba_not_rcv_total = 0; + wrs_params->epr_acc = 0; + wrs_params->up_same_time_cnt = 0; + wrs_params->down_time_cnt = 0; +} + +static bool cl_wrs_down_epr_check(struct cl_wrs_db *wrs_db, struct cl_wrs_sta *wrs_sta, + struct cl_wrs_params *wrs_params, u8 drop_factor, + enum cl_wrs_decision decision) +{ + u16 curr_rate_idx = wrs_params->rate_idx; + struct cl_wrs_table *curr_rate = &wrs_params->table[curr_rate_idx]; + u64 curr_epr_acc = curr_rate->epr_acc; + u32 curr_total = curr_rate->frames_total; + u16 down_rate_idx = curr_rate->rate_down.rate_idx; + struct cl_wrs_table *down_rate = &wrs_params->table[down_rate_idx]; + u64 down_epr_acc = down_rate->epr_acc; + u32 down_total = down_rate->frames_total; + u16 down_data_rate = 0; + u64 condition1 = 0, condition2 = 0; + bool down_decision = false, allow_penalty = true; + + if (wrs_params->calc_ba_not_rcv) { + curr_total += curr_rate->ba_not_rcv_total; + down_total += down_rate->ba_not_rcv_total; + } + + /* + * In the EPR of down candidate is better than or equal to current EPR => return true + * + * (1) curr_epr <= down_epr * factor(%) + * + * curr_epr_acc down_epr_acc factor + * (2) -------------- <= -------------- * -------- + * curr_total down_total 100 + * + * (3) curr_epr_acc * down_total * 100 <= down_epr_acc * curr_total * factor + * + * (4) conditation1 <= conditation2 + * down_epr_acc + * If (down_total == 0) we use down_data_rate instead of: -------------- + * down_total + */ + if (down_total) { + condition1 = curr_epr_acc * down_total * 100; + condition2 = down_epr_acc * curr_total * drop_factor; + } else { + down_data_rate = cl_data_rates_get_x10(wrs_params->tx_params.mode, + down_rate->rate.bw, + down_rate->rate.nss, + down_rate->rate.mcs, + down_rate->rate.gi); + + condition1 = curr_epr_acc * 100; + condition2 = (u64)down_data_rate * curr_total * drop_factor; + allow_penalty = false; + } + + wrs_params->penalty_decision_dn = wrs_db->step_down; + + if (condition2 && condition1 <= condition2) { + down_decision = true; + + if (allow_penalty) { + /* + * The penalty is calculated as follow: + * + * penalty = MAX_STEP * penalty_factor + * epr_curr + * penalty = MAX_STEP * (100% - 100% * ----------) + * epr_down + * + * conditation1 + * penalty = MAX_STEP * (100% - 100% --------------) + * conditation2 + */ + + u64 penalty_factor = 100 - div64_u64(condition1 * 100, condition2); + u16 max_step = wrs_db->time_th_max_up - wrs_db->step_down; + + wrs_params->penalty_decision_dn += + div64_u64(max_step * penalty_factor, 100); + } + + if (decision != WRS_DECISION_SAME) + wrs_pr_info(wrs_db, + "[WRS] EPR check: sta = %u, pkt_curr = %u, pkt_down = %u, " + "epr_curr = %llu, epr_down * %u%% = %llu, penalty = %u\n", + wrs_sta->sta_idx, + curr_total, + down_total, + div64_u64(curr_epr_acc, curr_total * 10), + drop_factor, + down_total ? + div64_u64(down_epr_acc * drop_factor, down_total * 1000) : + (down_data_rate / 10), + wrs_params->penalty_decision_dn); + } + + return down_decision; +} + +static void cl_wrs_time_thr_max_handler(struct cl_wrs_db *wrs_db, + struct cl_wrs_table *table, u8 up_idx) +{ + /* + * Check if there are at least two UP rates, + * and all UP rates reached max time threshold + */ + u8 i = 0; + u8 time_th_max = 0; + + for (i = 0; i < WRS_TABLE_NODE_UP_MAX; i++) { + if (table->rate_up[i].rate_idx == WRS_INVALID_RATE) + continue; + + if (table->rate_up[i].time_th != wrs_db->time_th_max_up) + return; + + time_th_max++; + } + + if (time_th_max < 2) + return; + + /* Find the next max rate, and decrease its time threshold by 1 */ + i = 0; + while (i < WRS_TABLE_NODE_UP_MAX) { + up_idx++; + if (up_idx == WRS_TABLE_NODE_UP_MAX) + up_idx = WRS_TABLE_NODE_UP_MCS; + + if (table->rate_up[up_idx].rate_idx != WRS_INVALID_RATE) { + /* + * If all up rates reached max time threshold,the first up + * rate will always be selected. + * To overcome it, we decrease the time threshold of the next + * up rate by 1 (so it will be samller and selected next time) + */ + table->rate_up[up_idx].time_th--; + break; + } + + i++; + } +} + +static bool cl_wrs_find_up_candidate(struct cl_wrs_db *wrs_db, struct cl_wrs_params *wrs_params, + u16 *up_rate_idx, u32 *up_time_th) +{ + bool up_rate_valid = false; + u8 up_idx = 0; + u8 up_candidate = 0; + u16 rate_idx = 0; + struct cl_wrs_table *table = &wrs_params->table[wrs_params->rate_idx]; + + *up_rate_idx = WRS_INVALID_RATE; + *up_time_th = U32_MAX; + + for (up_idx = 0; up_idx < WRS_TABLE_NODE_UP_MAX; up_idx++) { + rate_idx = table->rate_up[up_idx].rate_idx; + + if (rate_idx == WRS_INVALID_RATE) + continue; + + if (wrs_db->quick_up_en && table->rate_up[up_idx].quick_up_check) { + *up_rate_idx = rate_idx; + *up_time_th = wrs_db->quick_up_interval; + up_rate_valid = true; + up_candidate = up_idx; + break; + } else if (table->rate_up[up_idx].time_th < *up_time_th) { + *up_rate_idx = rate_idx; + *up_time_th = table->rate_up[up_idx].time_th; + up_rate_valid = true; + up_candidate = up_idx; + } + } + + if (wrs_db->time_th_max_up == *up_time_th) + cl_wrs_time_thr_max_handler(wrs_db, table, up_candidate); + + return up_rate_valid; +} + +static bool cl_wrs_epr_immeidate_down(struct cl_hw *cl_hw, struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, struct cl_wrs_params *wrs_params, + u16 down_rate_idx) +{ + if (cl_wrs_down_epr_check(wrs_db, wrs_sta, wrs_params, + wrs_db->immediate_drop_epr_factor, + WRS_DECISION_DOWN_IMMEDIATE)) { + /* + * If there are several immediate drops in a row ignore them, + * because it is probably not realted to bad TX rate + */ + wrs_params->immediate_drop_cntr++; + + if (wrs_params->immediate_drop_cntr > wrs_db->immediate_drop_max_in_row) { + wrs_params->immediate_drop_ignore++; + + cl_wrs_tables_reset(wrs_db, wrs_sta, wrs_params); + cl_wrs_reset_params_cntrs(wrs_params); + + wrs_pr_info(wrs_db, + "[WRS] sta %u - ignore immediate down decision (cntr=%u)\n", + wrs_sta->sta_idx, wrs_params->immediate_drop_cntr); + return true; + } + + cl_wrs_decision_make(cl_hw, wrs_db, wrs_sta, wrs_params, + WRS_DECISION_DOWN_IMMEDIATE, down_rate_idx); + return true; + } + + return false; +} + +static void cl_wrs_decision_up(struct cl_hw *cl_hw, struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, struct cl_wrs_params *wrs_params, + u16 up_rate_idx, u32 up_th) +{ + enum cl_wrs_decision up_decision = (up_th == wrs_db->quick_up_interval) ? + WRS_DECISION_UP_QUICK : WRS_DECISION_UP; + + cl_wrs_decision_make(cl_hw, wrs_db, wrs_sta, wrs_params, up_decision, up_rate_idx); +} + +static void cl_wrs_decision_same(struct cl_hw *cl_hw, struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, struct cl_wrs_params *wrs_params, + u16 rate_idx) +{ + cl_wrs_decision_make(cl_hw, wrs_db, wrs_sta, wrs_params, WRS_DECISION_SAME, rate_idx); +} + +static void cl_wrs_epr_decision(struct cl_hw *cl_hw, struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, struct cl_wrs_params *wrs_params) +{ + u16 curr_rate_idx = wrs_params->rate_idx; + struct cl_wrs_table *table = &wrs_params->table[curr_rate_idx]; + u16 down_rate_idx = table->rate_down.rate_idx; + u16 up_rate_idx = 0; + u16 down_th = table->rate_down.time_th; + u32 up_th = 0; + bool up_rate_valid = false; + + /* Check if we transmitted enough frames for taking decision */ + if ((wrs_params->frames_total + wrs_params->ba_not_rcv_total) < + wrs_db->min_frames_for_decision) + return; + + up_rate_valid = cl_wrs_find_up_candidate(wrs_db, wrs_params, &up_rate_idx, &up_th); + + /* RSSI protect */ + if (wrs_db->rssi_protect_en) + if (cl_wrs_rssi_prot_decision(cl_hw, wrs_db, wrs_sta, up_rate_valid, + up_rate_idx, down_rate_idx)) + return; + + if (down_rate_idx != curr_rate_idx) { + /* Down immediate */ + if (wrs_db->immediate_drop_en) + if (cl_wrs_epr_immeidate_down(cl_hw, wrs_db, wrs_sta, + wrs_params, down_rate_idx)) + return; + + /* Down */ + if (wrs_params->down_time_cnt >= down_th) { + if (cl_wrs_down_epr_check(wrs_db, wrs_sta, wrs_params, + wrs_db->epr_factor, WRS_DECISION_DOWN)) { + cl_wrs_decision_make(cl_hw, wrs_db, wrs_sta, wrs_params, + WRS_DECISION_DOWN, down_rate_idx); + return; + } + + wrs_params->down_time_cnt = 0; + } + } + + /* Up-same */ + if (wrs_params->up_same_time_cnt >= up_th) { + if (up_rate_valid) + cl_wrs_decision_up(cl_hw, wrs_db, wrs_sta, wrs_params, up_rate_idx, up_th); + else + cl_wrs_decision_same(cl_hw, wrs_db, wrs_sta, wrs_params, curr_rate_idx); + + return; + } + + /* + * If there is no valid UP rate and the EPR is more + * than EPR down threshold => make a same decision + */ + if (!up_rate_valid && + !cl_wrs_down_epr_check(wrs_db, wrs_sta, wrs_params, + wrs_db->epr_factor, WRS_DECISION_SAME)) + cl_wrs_decision_same(cl_hw, wrs_db, wrs_sta, wrs_params, curr_rate_idx); +} + +static void cl_wrs_divide_weights_by_two(struct cl_wrs_table *table_node) +{ + u8 up_idx = 0; + struct cl_wrs_table_node *rate_up; + + /* + * Converge weights - divide all weights by 2 + * (make sure they do not go below their init value) + */ + if (table_node->rate_down.rate_idx != WRS_INVALID_RATE) + table_node->rate_down.time_th = max(table_node->rate_down.time_th >> 1, + WRS_INIT_MSEC_WEIGHT_DOWN); + + for (up_idx = 0; up_idx < WRS_TABLE_NODE_UP_MAX; up_idx++) { + rate_up = &table_node->rate_up[up_idx]; + + if (rate_up->rate_idx != WRS_INVALID_RATE) + rate_up->time_th = max(rate_up->time_th >> 1, WRS_INIT_MSEC_WEIGHT_UP); + + if (rate_up->time_th == WRS_INIT_MSEC_WEIGHT_UP) + rate_up->quick_up_check = false; + } +} + +static void cl_wrs_converge_weights(struct cl_wrs_params *wrs_params) +{ + /* + * Converge weights - divide the weights by 2 (except for the current rate), + * and reset PER counters (except for current rate, down rate, and down-down rate). + */ + u16 i; + u16 curr_idx = wrs_params->rate_idx; + u16 down_idx = wrs_params->table[curr_idx].rate_down.rate_idx; + u16 down2_idx = wrs_params->table[down_idx].rate_down.rate_idx; + + for (i = 0; i < wrs_params->table_size; i++) { + if (i == curr_idx) + continue; + + cl_wrs_divide_weights_by_two(&wrs_params->table[i]); + + if (i != down_idx && i != down2_idx) { + wrs_params->table[i].frames_total = 0; + wrs_params->table[i].ba_not_rcv_total = 0; + wrs_params->table[i].epr_acc = 0; + } + } +} + +static void cl_wrs_converge_weights_idle_decision(struct cl_hw *cl_hw, + struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, + struct cl_wrs_params *wrs_params) +{ + /* + * Continue normal converge (just like during traffic). + * After 6 seconds reset table, and select rate based on RSSI. + */ + if (!wrs_db->converge_idle_en) + return; + + wrs_params->converge_time_idle += wrs_db->interval; + + if (wrs_params->converge_mode == WRS_CONVERGE_MODE_RESET) { + if (wrs_params->converge_time_idle < wrs_db->converge_idle_interval_reset) { + cl_wrs_converge_weights(wrs_params); + } else { + wrs_params->converge_mode = WRS_CONVERGE_MODE_RSSI; + wrs_params->converge_time_idle = 0; + + wrs_pr_info(wrs_db, "[WRS] Converge weights: sta %u - RSSI\n", + wrs_sta->sta_idx); + + /* Reset table and choose new rate based on RSSI */ + cl_wrs_tables_reset(wrs_db, wrs_sta, wrs_params); + + cl_wrs_rssi_set_rate(cl_hw, wrs_db, wrs_sta); + } + } else { + if (wrs_params->converge_time_idle < wrs_db->converge_idle_interval_rssi) + return; + + /* Choose new rate based on RSSI */ + wrs_params->converge_time_idle = 0; + cl_wrs_rssi_set_rate(cl_hw, wrs_db, wrs_sta); + } +} + +static void cl_wrs_converge_weights_idle_reset(struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, + struct cl_wrs_params *wrs_params) +{ + /* There was traffic in last maintenance interval - reset converge parameteres */ + wrs_params->converge_time_idle = 0; + + if (wrs_params->converge_mode != WRS_CONVERGE_MODE_RESET) { + wrs_params->converge_mode = WRS_CONVERGE_MODE_RESET; + wrs_pr_info(wrs_db, "[WRS] Converge weights: sta %u - RESET\n", + wrs_sta->sta_idx); + } +} + +static void cl_wrs_converge_weights_trfc_decision(struct cl_hw *cl_hw, struct cl_sta *cl_sta, + struct cl_wrs_db *wrs_db, + struct cl_wrs_params *wrs_params) +{ + u32 converge_interval = 0; + + if (!wrs_db->converge_trfc_en) + return; + + if (cl_motion_sense_is_static(cl_hw, cl_sta) && cl_env_det_is_clean(cl_hw)) + converge_interval = wrs_db->converge_trfc_interval_static; + else + converge_interval = wrs_db->converge_trfc_interval_motion; + + wrs_params->converge_time_trfc += wrs_db->interval; + + if (wrs_params->converge_time_trfc >= converge_interval) { + wrs_params->converge_time_trfc = 0; + cl_wrs_converge_weights(wrs_params); + } +} + +static u32 cl_wrs_get_sync_attempts(struct cl_wrs_sta *wrs_sta, struct cl_wrs_params *wrs_params) +{ + struct cl_sta *cl_sta = container_of(wrs_sta, struct cl_sta, wrs_sta); + + return cl_sta->wrs_info.sync_attempts; +} + +static void cl_wrs_sta_no_sync_handler(struct cl_hw *cl_hw, + struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, + struct cl_wrs_params *wrs_params) +{ + unsigned long time_delta = jiffies_to_msecs(jiffies - wrs_params->no_sync_timestamp); + + if (time_delta < wrs_db->sync_timeout) + return; + + if (cl_wrs_get_sync_attempts(wrs_sta, wrs_params) < wrs_db->sync_min_attempts) { + /* + * Rate not synced but there is also hardly no traffic - + * change mode to synced! + */ + wrs_params->sync = true; + wrs_params->sync_timestamp = jiffies; + } else { + struct cl_wrs_table *wrs_table = &wrs_params->table[wrs_params->rate_idx]; + struct cl_wrs_rate *curr_rate = &wrs_table->rate; + + if (!cl_hw->ate_db.active) + pr_warn("[WRS] NO SYNC - sta = %u, bw = %u, nss = %u, mcs = %u, gi = %u\n", + wrs_sta->sta_idx, curr_rate->bw, curr_rate->nss, + curr_rate->mcs, curr_rate->gi); + + if (WRS_IS_DECISION_UP(wrs_params->last_decision)) { + cl_wrs_decision_make(cl_hw, wrs_db, wrs_sta, wrs_params, + WRS_DECISION_DOWN_NO_SYNC, + wrs_table->rate_down.rate_idx); + } else { + /* If the last decision was DOWN - change state to SYNCED. */ + wrs_params->sync = true; + wrs_params->sync_timestamp = jiffies; + } + } +} + +static void cl_wrs_update_ba_not_rcv(struct cl_wrs_db *wrs_db, struct cl_wrs_params *wrs_params) +{ + unsigned long time_since_sync = jiffies_to_msecs(jiffies - wrs_params->sync_timestamp); + + wrs_params->calc_ba_not_rcv = (wrs_db->ba_not_rcv_force || + (time_since_sync < wrs_db->ba_not_rcv_time_since_sync)); +} + +static void _cl_wrs_tx_cntrs_reset(struct cl_wrs_info *wrs_info) +{ + wrs_info->epr_acc = 0; + wrs_info->tx_success = 0; + wrs_info->tx_fail = 0; + wrs_info->ba_not_rcv = 0; + wrs_info->ba_not_rcv_consecutive_max = 0; +} + +static void cl_wrs_tx_cntrs_read(struct cl_wrs_sta *wrs_sta, + struct cl_wrs_tx_cntrs *tx_cntrs) +{ + struct cl_sta *cl_sta = container_of(wrs_sta, struct cl_sta, wrs_sta); + struct cl_wrs_info *wrs_info = &cl_sta->wrs_info; + + tx_cntrs->epr_acc = wrs_info->epr_acc; + tx_cntrs->total = wrs_info->tx_success + wrs_info->tx_fail; + tx_cntrs->fail = wrs_info->tx_fail; + tx_cntrs->ba_not_rcv = wrs_info->ba_not_rcv; + tx_cntrs->ba_not_rcv_consecutive = wrs_info->ba_not_rcv_consecutive_max; + + _cl_wrs_tx_cntrs_reset(wrs_info); +} + +static void _cl_wrs_sta_maintenance(struct cl_hw *cl_hw, struct cl_sta *cl_sta, + struct cl_wrs_params *wrs_params) +{ + struct cl_wrs_db *wrs_db = &cl_hw->wrs_db; + struct cl_wrs_sta *wrs_sta = &cl_sta->wrs_sta; + struct cl_wrs_tx_cntrs tx_cntrs = {0}; + + if (!wrs_params->sync) { + cl_wrs_sta_no_sync_handler(cl_hw, wrs_db, wrs_sta, wrs_params); + return; + } + + cl_wrs_update_ba_not_rcv(wrs_db, wrs_params); + cl_wrs_tx_cntrs_read(wrs_sta, &tx_cntrs); + + if (wrs_params->is_fixed_rate) { + cl_wrs_stats_per_update(wrs_db, wrs_sta, wrs_params, &tx_cntrs); + return; + } + + wrs_params->down_time_cnt += wrs_db->interval; + wrs_params->up_same_time_cnt += wrs_db->interval; + + if ((tx_cntrs.total + tx_cntrs.ba_not_rcv) < wrs_db->converge_idle_packet_th) { + /* + * Very few frames were sent in last maintenance interval + * Check if weights should be converged + */ + cl_wrs_converge_weights_idle_decision(cl_hw, wrs_db, wrs_sta, wrs_params); + + cl_wrs_stats_per_update(wrs_db, wrs_sta, wrs_params, &tx_cntrs); + + return; + } + + /* There was traffic in last maintenance interval - reset converge parameteres */ + cl_wrs_converge_weights_idle_reset(wrs_db, wrs_sta, wrs_params); + + cl_wrs_stats_per_update(wrs_db, wrs_sta, wrs_params, &tx_cntrs); + + wrs_params->quick_up_check = + (tx_cntrs.ba_not_rcv_consecutive >= wrs_db->quick_up_ba_thr) ? 1 : 0; + cl_wrs_epr_decision(cl_hw, wrs_db, wrs_sta, wrs_params); + + cl_wrs_converge_weights_trfc_decision(cl_hw, cl_sta, wrs_db, wrs_params); +} + +static void cl_wrs_sta_maintenance(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + _cl_wrs_sta_maintenance(cl_hw, cl_sta, &cl_sta->wrs_sta.su_params); +} + +static void cl_wrs_cca_calc(struct cl_hw *cl_hw, struct cl_wrs_db *wrs_db, u8 max_bw) +{ + u32 cca_primary_new = mac_hw_edca_cca_busy_get(cl_hw); + u32 cca_sec80_new = (max_bw > CHNL_BW_80) ? mac_hw_add_cca_busy_sec_80_get(cl_hw) : 0; + u32 cca_sec40_new = (max_bw > CHNL_BW_40) ? mac_hw_add_cca_busy_sec_40_get(cl_hw) : 0; + u32 cca_sec20_new = mac_hw_add_cca_busy_sec_20_get(cl_hw); + + u32 cca_primary_diff = cca_primary_new - wrs_db->cca_primary; + u32 cca_sec80_diff = cca_sec80_new - wrs_db->cca_sec80; + u32 cca_sec40_diff = cca_sec40_new - wrs_db->cca_sec40; + u32 cca_sec20_diff = cca_sec20_new - wrs_db->cca_sec20; + + wrs_db->cca_primary = cca_primary_new; + wrs_db->cca_sec80 = cca_sec80_new; + wrs_db->cca_sec40 = cca_sec40_new; + wrs_db->cca_sec20 = cca_sec20_new; + wrs_db->cca_timestamp = jiffies; + + /* Increase by 25% */ + cca_primary_diff = cca_primary_diff * WRS_CCA_PRIMARY_FACTOR >> WRS_CCA_PRIMARY_SHIFT; + + /* Adjacent interference - if secondary is higher than primary by 25%. */ + wrs_db->adjacent_interference80 = (cca_sec80_diff > cca_primary_diff); + wrs_db->adjacent_interference40 = (cca_sec40_diff > cca_primary_diff); + wrs_db->adjacent_interference20 = (cca_sec20_diff > cca_primary_diff); +} + +static void cl_wrs_cca_maintenance(struct cl_hw *cl_hw, struct cl_wrs_db *wrs_db) +{ + u8 max_bw = wrs_db->max_cap.bw; + + if (max_bw == CHNL_BW_20) + return; + + if (jiffies_to_msecs(jiffies - wrs_db->cca_timestamp) > WRS_CCA_PERIOD_MS) + cl_wrs_cca_calc(cl_hw, wrs_db, max_bw); +} + +static void cl_wrs_maintenance(unsigned long data) +{ + struct cl_hw *cl_hw = (struct cl_hw *)data; + struct cl_wrs_db *wrs_db = &cl_hw->wrs_db; + + cl_wrs_cca_maintenance(cl_hw, wrs_db); + + cl_wrs_lock(wrs_db); + cl_sta_loop(cl_hw, cl_wrs_sta_maintenance); + cl_wrs_unlock(wrs_db); +} + +static void cl_wrs_down_decision_weights_update(struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, + u16 new_rate_idx, + struct cl_wrs_params *wrs_params) +{ + u16 old_rate_idx = wrs_params->rate_idx; + u8 up_idx = 0; + u16 down_th_min = wrs_db->time_th_min; + u16 step = wrs_db->step_down; + u16 *th_down = &wrs_params->table[old_rate_idx].rate_down.time_th; + u16 *th_up = NULL; + struct cl_wrs_table *table_node = &wrs_params->table[new_rate_idx]; + + /* Decrease the weight from old rate to new rate */ + if (*th_down > (down_th_min + step)) + *th_down -= step; + else + *th_down = down_th_min; + + /* Increase the weight from new rate to old rate */ + for (up_idx = 0; up_idx < WRS_TABLE_NODE_UP_MAX; up_idx++) { + if (old_rate_idx == table_node->rate_up[up_idx].rate_idx) { + th_up = &table_node->rate_up[up_idx].time_th; + table_node->rate_up[up_idx].quick_up_check = !!wrs_params->quick_up_check; + step = wrs_params->penalty_decision_dn; + *th_up = min_t(u16, *th_up + step, wrs_db->time_th_max_up); + break; + } + } + + wrs_pr_info(wrs_db, + "[WRS] Down update - sta = %u, " + "down weight [%u-->%u] = %u, up weight [%u-->%u] = %u\n", + wrs_sta->sta_idx, old_rate_idx, new_rate_idx, + *th_down, new_rate_idx, old_rate_idx, th_up ? *th_up : 0); +} + +static void cl_wrs_up_same_decision_weights_update(struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, + struct cl_wrs_params *wrs_params) +{ + u16 curr_rate_idx = wrs_params->rate_idx; + u16 down_rate_idx = wrs_params->table[curr_rate_idx].rate_down.rate_idx; + u8 up_idx = 0; + u16 up_th_min = wrs_db->time_th_min; + u16 step = wrs_db->step_up_same; + u16 *th_down = &wrs_params->table[curr_rate_idx].rate_down.time_th; + u16 *th_up = NULL; + u16 th_down_orig = *th_down; + u16 th_up_orig = 0; + struct cl_wrs_table *table_node = &wrs_params->table[down_rate_idx]; + + /* Increase the weight from current rate to down rate */ + *th_down = min_t(u16, *th_down + step, wrs_db->time_th_max_down); + + /* Decrease the weight from down rate to current rate */ + for (up_idx = 0; up_idx < WRS_TABLE_NODE_UP_MAX; up_idx++) { + if (curr_rate_idx == table_node->rate_up[up_idx].rate_idx) { + th_up = &table_node->rate_up[up_idx].time_th; + table_node->rate_up[up_idx].quick_up_check = false; + + th_up_orig = *th_up; + + if (*th_up > (up_th_min + step)) + *th_up -= step; + else + *th_up = up_th_min; + break; + } + } + + if (th_up && (th_up_orig != *th_up || th_down_orig != *th_down)) + wrs_pr_info(wrs_db, + "[WRS] Up/same update - sta = %u, " + "down weight [%u-->%u] = %u, up weight [%u-->%u] = %u\n", + wrs_sta->sta_idx, curr_rate_idx, + down_rate_idx, *th_down, down_rate_idx, curr_rate_idx, *th_up); +} + +void cl_wrs_init(struct cl_hw *cl_hw) +{ + struct cl_wrs_db *wrs_db = &cl_hw->wrs_db; + + /* Default configuration */ + wrs_db->debug_level = DBG_LVL_ERROR; + wrs_db->rssi_protect_en = true; + wrs_db->rssi_protect_mode = WRS_RSSI_PROT_MODE_RSSI; + wrs_db->rssi_protect_up_thr = WRS_RSSI_PROTECT_UP_THR; + wrs_db->rssi_protect_dn_thr = WRS_RSSI_PROTECT_DN_THR; + wrs_db->min_frames_for_decision = WRS_MIN_FRAMES_FOR_DECISION; + wrs_db->epr_factor = WRS_EPR_FACTOR; + wrs_db->converge_idle_en = true; + wrs_db->converge_idle_interval_reset = WRS_CONVERGE_IDLE_INTERVAL_RESET; + wrs_db->converge_idle_interval_rssi = WRS_CONVERGE_IDLE_INTERVAL_RSSI; + wrs_db->converge_idle_packet_th = WRS_CONVERGE_IDLE_PACKET_TH; + wrs_db->converge_trfc_en = true; + wrs_db->converge_trfc_interval_static = WRS_CONVERGE_TRFC_INTERVAL_STATIC; + wrs_db->converge_trfc_interval_motion = WRS_CONVERGE_TRFC_INTERVAL_MOTION; + wrs_db->immediate_drop_en = true; + wrs_db->immediate_drop_epr_factor = WRS_IMMEDIATE_DROP_EPR_FACTOR; + wrs_db->immediate_drop_max_in_row = WRS_IMMEDIATE_DROP_MAX_IN_ROW; + wrs_db->time_th_min = WRS_MSEC_WEIGHT_MIN; + wrs_db->time_th_max_up = WRS_MSEC_WEIGHT_MAX_UP; + wrs_db->time_th_max_down = WRS_MSEC_WEIGHT_MAX_DOWN; + wrs_db->step_down = WRS_MSEC_STEP_DOWN; + wrs_db->step_up_same = WRS_MSEC_STEP_UP_SAME; + wrs_db->interval = msecs_round(WRS_MAINTENANCE_PERIOD_MS); + wrs_db->conservative_mcs_noisy_env = false; + wrs_db->conservative_nss_noisy_env = false; + wrs_db->quick_up_en = true; + wrs_db->quick_up_ba_thr = WRS_QUICK_UP_BA_THR; + wrs_db->quick_up_interval = msecs_round(WRS_QUICK_UP_INTERVAL_MS); + wrs_db->quick_down_en = true; + wrs_db->quick_down_epr_factor = WRS_QUICK_DOWN_EPR_FACTOR; + wrs_db->quick_down_agg_thr = WRS_QUICK_DOWN_AGG_THR; + wrs_db->quick_down_pkt_thr = WRS_QUICK_DOWN_PKT_THR; + wrs_db->ba_not_rcv_collision_filter = true; + /* Environment of 2.4 is much more noisy, so 'BA not received' are ignored. */ + wrs_db->ba_not_rcv_force = cl_band_is_24g(cl_hw) ? false : true; + wrs_db->ba_not_rcv_time_since_sync = WRS_BA_NOT_RCV_TIME_SINCE_SYNC; + wrs_db->sync_timeout = WRS_SYNC_TIMEOUT; + wrs_db->sync_min_attempts = WRS_SYNC_MIN_ATTEMPTS; + + /* Init WRS periodic timer */ + cl_timer_init(&wrs_db->timer_maintenance, + cl_wrs_maintenance, + (unsigned long)cl_hw, + wrs_db->interval, true); + + if (!cl_hw->chip->conf->ce_production_mode) { + wrs_db->cca_timestamp = jiffies; + cl_timer_enable(&wrs_db->timer_maintenance); + } + + spin_lock_init(&wrs_db->lock); + + if ((cl_hw->conf->ci_wrs_fixed_rate[WRS_FIXED_PARAM_MODE] != -1) && + (cl_hw->conf->ci_wrs_fixed_rate[WRS_FIXED_PARAM_BW] != -1) && + (cl_hw->conf->ci_wrs_fixed_rate[WRS_FIXED_PARAM_NSS] != -1) && + (cl_hw->conf->ci_wrs_fixed_rate[WRS_FIXED_PARAM_MCS] != -1) && + (cl_hw->conf->ci_wrs_fixed_rate[WRS_FIXED_PARAM_GI] != -1)) + wrs_db->is_fixed_rate = WRS_FIXED_FALLBACK_DIS; +} + +inline void cl_wrs_lock_bh(struct cl_wrs_db *wrs_db) +{ + spin_lock_bh(&wrs_db->lock); +} + +inline void cl_wrs_unlock_bh(struct cl_wrs_db *wrs_db) +{ + spin_unlock_bh(&wrs_db->lock); +} + +inline void cl_wrs_lock(struct cl_wrs_db *wrs_db) +{ + spin_lock(&wrs_db->lock); +} + +inline void cl_wrs_unlock(struct cl_wrs_db *wrs_db) +{ + spin_unlock(&wrs_db->lock); +} + +void cl_wrs_tx_param_sync(struct cl_wrs_db *wrs_db, struct cl_wrs_sta *wrs_sta, + struct cl_wrs_params *wrs_params) +{ + if (wrs_params->sync) + return; + + /* Reset the tx Counters */ + cl_wrs_tx_cntrs_reset(wrs_sta, wrs_params); + + /* Reset counters */ + cl_wrs_reset_params_cntrs(wrs_params); + + /* Change state to SYNCED */ + wrs_params->sync = true; + wrs_params->sync_timestamp = jiffies; + + wrs_pr_trace(wrs_db, "[WRS] Sync - timestamp = %u, sta = %u, rate_idx = %u\n", + jiffies_to_msecs(jiffies), + wrs_sta->sta_idx, + wrs_params->rate_idx); +} + +void cl_wrs_tx_params_update(struct cl_hw *cl_hw, struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, struct cl_wrs_params *wrs_params, + u16 new_rate_idx, bool is_sync_required) +{ + struct cl_sta *cl_sta = container_of(wrs_sta, struct cl_sta, wrs_sta); + struct cl_wrs_tx_params *tx_params = &wrs_params->tx_params; + struct cl_wrs_rate *rate = &wrs_params->table[new_rate_idx].rate; + u16 fallback_rate_idx = wrs_params->table[new_rate_idx].rate_down.rate_idx; + struct cl_wrs_rate *rate_fallback = &wrs_params->table[fallback_rate_idx].rate; + + cl_dyn_bcast_rate_change(cl_hw, cl_sta, tx_params->mcs, rate->mcs); + + tx_params->bw = rate->bw; + tx_params->nss = rate->nss; + tx_params->mcs = rate->mcs; + tx_params->gi = rate->gi; + tx_params->mode = wrs_sta->mode; + tx_params->fallback_en = (wrs_params->is_fixed_rate != WRS_FIXED_FALLBACK_DIS); + + wrs_pr_trace(wrs_db, + "[WRS] Tx params update - " + "sta = %u, rate_idx = %u, bw = %u, nss = %u, mcs = %u, gi = %u\n", + wrs_sta->sta_idx, new_rate_idx, tx_params->bw, + tx_params->nss, tx_params->mcs, tx_params->gi); + + wrs_params->rate_idx = new_rate_idx; + + /* Converge - restart the time for converging weights of all old rates */ + wrs_params->converge_time_trfc = 0; + + cl_wrs_tx_param_set(cl_hw, wrs_sta, wrs_params, tx_params, rate_fallback); + + if (is_sync_required) { + wrs_params->sync = false; + wrs_params->no_sync_timestamp = jiffies; + } else { + wrs_params->sync = true; + } + + /* Reset Counters */ + cl_wrs_reset_params_cntrs(wrs_params); +} + +void cl_wrs_decision_make(struct cl_hw *cl_hw, struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, struct cl_wrs_params *wrs_params, + enum cl_wrs_decision decision, u16 new_rate_idx) +{ + if (WRS_IS_DECISION_DOWN(decision)) { + cl_wrs_down_decision_weights_update(wrs_db, wrs_sta, new_rate_idx, wrs_params); + } else if (WRS_IS_DECISION_UP(decision)) { + cl_wrs_up_same_decision_weights_update(wrs_db, wrs_sta, wrs_params); + + if (wrs_params->rate_idx != wrs_params->table[new_rate_idx].rate_down.rate_idx) { + /* + * In case the down rate is different from the previous rate, + * update down rate index and reset the thresholds + */ + struct cl_wrs_table_node *rate_down = + &wrs_params->table[new_rate_idx].rate_down; + + rate_down->rate_idx = wrs_params->rate_idx; + rate_down->time_th = WRS_INIT_MSEC_WEIGHT_DOWN; + } + } else if (decision == WRS_DECISION_SAME) { + cl_wrs_up_same_decision_weights_update(wrs_db, wrs_sta, wrs_params); + + /* Reset counters besides down_time_cnt */ + wrs_params->frames_total = 0; + wrs_params->fail_total = 0; + wrs_params->ba_not_rcv_total = 0; + wrs_params->epr_acc = 0; + wrs_params->up_same_time_cnt = 0; + } + + cl_wrs_decision_update(wrs_db, wrs_sta, wrs_params, decision, new_rate_idx); + + if (WRS_IS_DECISION_DOWN(decision) || WRS_IS_DECISION_UP(decision)) + cl_wrs_tx_params_update(cl_hw, wrs_db, wrs_sta, wrs_params, + new_rate_idx, true); +} + +void cl_wrs_decision_update(struct cl_wrs_db *wrs_db, struct cl_wrs_sta *wrs_sta, + struct cl_wrs_params *wrs_params, enum cl_wrs_decision decision, + u16 new_rate_idx) +{ + wrs_params->last_decision = decision; + wrs_params->decision_cnt[decision]++; + + if (decision != WRS_DECISION_DOWN_IMMEDIATE) + wrs_params->immediate_drop_cntr = 0; + + if (decision == WRS_DECISION_SAME) + return; + + wrs_pr_trace(wrs_db, + "[WRS] Decision update - timestamp [%u] sta [%u] decision [%s]\n", + jiffies_to_msecs(jiffies), + wrs_sta->sta_idx, + WRS_DECISION_STR(decision)); +} + +void cl_wrs_fixed_rate_set(struct cl_hw *cl_hw, struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, struct cl_wrs_params *wrs_params, + u8 is_fixed_rate, u8 mode, u8 bw, u8 nss, u8 mcs, u8 gi) +{ + u16 rate_idx = 0; + + if (!is_fixed_rate) { + wrs_params->is_fixed_rate = WRS_AUTO_RATE; + wrs_pr_verbose(wrs_db, "[WRS] Station %u was set to auto rate!\n", + wrs_sta->sta_idx); + cl_wrs_rssi_set_rate(cl_hw, wrs_db, wrs_sta); + return; + } + + if (mode != wrs_sta->mode) { + /* Set fixed rate with a different format-mode */ + struct cl_wrs_tx_params *tx_params = &wrs_params->tx_params; + struct cl_sta *cl_sta = container_of(wrs_sta, struct cl_sta, wrs_sta); + + if (cl_band_is_6g(cl_hw) && mode != WRS_MODE_HE) { + wrs_pr_verbose(wrs_db, "[WRS] Invalid format mode [%u] for 6GHz band\n", + mode); + return; + } + + cl_dyn_bcast_rate_change(cl_hw, cl_sta, tx_params->mcs, mcs); + + tx_params->bw = bw; + tx_params->nss = nss; + tx_params->mcs = mcs; + tx_params->gi = gi; + tx_params->mode = mode; + tx_params->fallback_en = (wrs_params->is_fixed_rate != WRS_FIXED_FALLBACK_DIS); + + wrs_params->is_fixed_rate = is_fixed_rate; + + cl_wrs_tx_param_set(cl_hw, wrs_sta, wrs_params, tx_params, NULL); + wrs_pr_verbose(wrs_db, + "[WRS] Station %u set to %s - " + "mode=%u, bw=%u, nss=%u, mcs=%u, gi=%u\n", + wrs_sta->sta_idx, FIXED_RATE_STR(is_fixed_rate), + mode, bw, nss, mcs, gi); + return; + } + + rate_idx = cl_wrs_tables_find_rate_idx(wrs_params, bw, nss, mcs, gi); + + if (rate_idx == WRS_INVALID_RATE) { + wrs_pr_err(wrs_db, + "[WRS] Invalid fixed rate - mode=%u, bw=%u, nss=%u, mcs=%u, gi=%u\n", + mode, bw, nss, mcs, gi); + return; + } + + wrs_params->is_fixed_rate = is_fixed_rate; + cl_wrs_tx_params_update(cl_hw, wrs_db, wrs_sta, wrs_params, rate_idx, false); + wrs_pr_verbose(wrs_db, + "[WRS] Station %u set to %s - mode=%u, bw=%u, nss=%u, mcs=%u, gi=%u\n", + wrs_sta->sta_idx, FIXED_RATE_STR(is_fixed_rate), + mode, bw, nss, mcs, gi); +} + +void cl_wrs_quick_down_check(struct cl_hw *cl_hw, struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, struct cl_wrs_params *wrs_params) +{ + struct cl_wrs_tx_cntrs tx_cntrs = {0}; + struct cl_wrs_table *table = NULL; + u16 curr_rate_idx = 0; + u16 down_rate_idx = 0; + + if (!wrs_params->sync || + wrs_params->is_fixed_rate || + !WRS_IS_DECISION_UP(wrs_params->last_decision)) + return; + + cl_wrs_update_ba_not_rcv(wrs_db, wrs_params); + cl_wrs_tx_cntrs_read(wrs_sta, &tx_cntrs); + cl_wrs_stats_per_update(wrs_db, wrs_sta, wrs_params, &tx_cntrs); + + curr_rate_idx = wrs_params->rate_idx; + table = &wrs_params->table[curr_rate_idx]; + down_rate_idx = table->rate_down.rate_idx; + + /* Check if we transmitted enough frames for taking decision */ + if (wrs_params->frames_total < wrs_db->min_frames_for_decision) + return; + + /* Down decision check */ + if (down_rate_idx != curr_rate_idx && + cl_wrs_down_epr_check(wrs_db, wrs_sta, wrs_params, + wrs_db->quick_down_epr_factor, WRS_DECISION_DOWN_QUICK)) + cl_wrs_decision_make(cl_hw, wrs_db, wrs_sta, wrs_params, + WRS_DECISION_DOWN_QUICK, down_rate_idx); +} + +bool cl_wrs_up_mcs1(struct cl_hw *cl_hw, struct cl_wrs_db *wrs_db, + struct cl_wrs_sta *wrs_sta, struct cl_wrs_params *wrs_params) +{ + /* + * In case of big packets (4300 in VHT and 5400 in HE) and low + * rate (BW 20, NSS 1, MCS 0), firmware will increase rate to MCS 1, + * and give an indication to driver (set rate_fix_mcs1 in cl_agg_tx_report). + * WRS should also move to MCS 1, and give the maximum time + * penalty time from MCS 0 toMCS 1. + */ + u16 curr_rate_idx = wrs_params->rate_idx; + u16 up_rate_idx = 0; + struct cl_wrs_table *table = &wrs_params->table[curr_rate_idx]; + + if (!table || wrs_params->is_fixed_rate) + return false; + + if (table->rate.bw != CHNL_BW_20 || + table->rate.nss != WRS_SS_1 || + table->rate.mcs != WRS_MCS_0) + return false; + + up_rate_idx = cl_wrs_tables_find_rate_idx(wrs_params, + CHNL_BW_20, WRS_SS_1, WRS_MCS_1, table->rate.gi); + + if (up_rate_idx == WRS_INVALID_RATE) + return false; + + wrs_params->table[up_rate_idx].rate_down.time_th = wrs_db->time_th_max_up; + + cl_wrs_tx_cntrs_reset(wrs_sta, wrs_params); + cl_wrs_decision_update(wrs_db, wrs_sta, wrs_params, WRS_DECISION_UP_MCS1, up_rate_idx); + cl_wrs_tx_params_update(cl_hw, wrs_db, wrs_sta, wrs_params, + up_rate_idx, true); + + return true; +} + +void cl_wrs_tx_param_set(struct cl_hw *cl_hw, struct cl_wrs_sta *wrs_sta, + struct cl_wrs_params *wrs_params, + struct cl_wrs_tx_params *tx_params, + struct cl_wrs_rate *rate_fallback) +{ + struct cl_sta *cl_sta = container_of(wrs_sta, struct cl_sta, wrs_sta); + struct cl_bf_sta_db *bf_db = &cl_sta->bf_db; + struct cl_wrs_info *wrs_info = NULL; + u8 ltf = 0; + u8 ltf_fallback = 0; + u8 sta_idx = cl_sta->sta_idx; + union cl_rate_ctrl_info rate_ctrl; + union cl_rate_ctrl_info rate_ctrl_fallback; + union cl_rate_ctrl_info_he rate_ctrl_he; + + if (cl_hw->ate_db.active) + return; + + rate_ctrl_he.word = 0; + + wrs_info = &cl_sta->wrs_info; + + wrs_params->data_rate = cl_data_rates_get(tx_params->mode, + tx_params->bw, + tx_params->nss, + tx_params->mcs, + tx_params->gi); + + rate_ctrl.word = cl_rate_ctrl_generate(cl_hw, cl_sta, tx_params->mode, + tx_params->bw, tx_params->nss, + tx_params->mcs, tx_params->gi, + tx_params->fallback_en); + + /* For fallback rate use same mode (if it is NULL use same rate). */ + if (rate_fallback) { + rate_ctrl_fallback.word = cl_rate_ctrl_generate(cl_hw, + cl_sta, + tx_params->mode, + rate_fallback->bw, + rate_fallback->nss, + rate_fallback->mcs, + rate_fallback->gi, + tx_params->fallback_en); + ltf_fallback = cl_map_gi_to_ltf(tx_params->mode, rate_fallback->gi); + } else { + rate_ctrl_fallback.word = rate_ctrl.word; + } + + /* Save current BF state and SS for the fallback rate */ + bf_db->is_on = rate_ctrl.field.tx_bf; + bf_db->is_on_fallback = rate_ctrl_fallback.field.tx_bf; + bf_db->num_ss = tx_params->nss; + bf_db->num_ss_fallback = rate_fallback ? rate_fallback->nss : tx_params->nss; + + /* Reset counters */ + wrs_info->tx_success = 0; + wrs_info->tx_fail = 0; + + /* Mark rate as unsynced */ + wrs_info->synced = false; + wrs_info->quick_rate_check = false; + wrs_info->sync_attempts = 0; + + ltf = cl_map_gi_to_ltf(tx_params->mode, tx_params->gi); + + if (tx_params->mode == WRS_MODE_HE) + rate_ctrl_he.field.spatial_conf = RATE_CNTRL_HE_SPATIAL_CONF_DEF; + + /* Send new rate to firmware */ + cl_msg_tx_update_rate_dl(cl_hw, sta_idx, rate_ctrl.word, + rate_ctrl_fallback.word, tx_params->bw, + RATE_OP_MODE_STA_SU, + ltf, ltf_fallback, rate_ctrl_he.word); + + /* + * TODO: Limit by SU/TX if active function will take control + * over MU-SU/TX-RX. + */ + cl_rsrc_mgmt_rates_update(cl_hw, cl_sta); +} + +s8 cl_wrs_rssi_eq_calc(struct cl_hw *cl_hw, struct cl_wrs_sta *wrs_sta, + bool read_clear, s8 *sorted_rssi) +{ + struct cl_sta *cl_sta = container_of(wrs_sta, struct cl_sta, wrs_sta); + struct cl_wrs_rssi *wrs_rssi = &cl_sta->wrs_rssi; + int i; + + if (wrs_rssi->cnt == 0) { + memcpy(sorted_rssi, cl_sta->last_rssi, cl_hw->num_antennas); + goto sort; + } + + for (i = 0; i < cl_hw->num_antennas; i++) + sorted_rssi[i] = (s8)(wrs_rssi->sum[i] / wrs_rssi->cnt); + + if (read_clear) + memset(wrs_rssi, 0, sizeof(struct cl_wrs_rssi)); + +sort: + /* Sort RSSI values in descending order */ + cl_rssi_sort_descending(sorted_rssi, cl_hw->num_antennas); + + /* Calc equivalent RSSI */ + return cl_rssi_calc_equivalent(cl_hw, sorted_rssi); +} + +void cl_wrs_tx_cntrs_reset(struct cl_wrs_sta *wrs_sta, struct cl_wrs_params *wrs_params) +{ + struct cl_sta *cl_sta = container_of(wrs_sta, struct cl_sta, wrs_sta); + struct cl_wrs_info *wrs_info = &cl_sta->wrs_info; + + _cl_wrs_tx_cntrs_reset(wrs_info); +}