From patchwork Thu Jun 17 15:59: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: 462762 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 DAFB4C2B9F4 for ; Thu, 17 Jun 2021 16:04:35 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AD0F2613E1 for ; Thu, 17 Jun 2021 16:04:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231856AbhFQQGj (ORCPT ); Thu, 17 Jun 2021 12:06:39 -0400 Received: from mail-vi1eur05on2081.outbound.protection.outlook.com ([40.107.21.81]:58626 "EHLO EUR05-VI1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S233644AbhFQQGJ (ORCPT ); Thu, 17 Jun 2021 12:06:09 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=XVPC+T80+HyuxhzzdRCrcAx0yr9nlNjmDhqrS6yopWiS772gboFJmuwy20vLWqLz0Luxac1sWWiYvYO+3bwZVg+rTtqjknNoHKeZEZk2WZaJ6BGyb7kU9JImbIm+23XYTdKdz4g0Iey+Yc5IJo7pyiTVFqtaqK6V562nR6UPqkVNLcAcw1fJfxizpxDmDeh8YAfa8qDlO4Y6SS3xj2hSMxm/ikzWYYfBT/V7+NuWe0nO22DMnWKrPdoL5bTeu/W2qe3bzV0ETUkvpX05QRfEfdJo/W3UUow6HCfSdhrzIOrDc9hjwoqTZDEH2lXRWfUg9zJgbTk3Zmud24T/C5Topg== 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=6GUoDSoczNNQUgumtLTp+ew/hJxs5ovMI/hnArTTsOA=; b=D9Bqi6DZoZkm9amPCC4biHWq9d/4pnsQnLrgAZXQ0FiMlTd9HGB91xfc0vsmUeAgi70QAP4zFZiZwOm5maPhS5uM3bF/p2snP0nhQh3u985K82KZX9169cBK6PkCrSgLrLitnSJzJGr4D7OwojOJAQu+TFUZZccR3fkrg3jrgdrPgcY3UYusnlATGp/Dx1mMQIkmkUdZYQW8eYC7KE0X4UfKWZzxsTOK1Nl/Ur91lVC5jlhtGAB9kEInqkisdsadhZK6XgyC0pIj2HLYHQdHngI6kODt8MUF8WB0WngPzOyDGhMP/xk0arhHVZlTKJnzqWretFjT8/96GAROmeC57g== 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=6GUoDSoczNNQUgumtLTp+ew/hJxs5ovMI/hnArTTsOA=; b=aaJj7NKWP4ygLq74Kg1HelgTZ7UyXhjY3To4Of/uVrdBcEEwFKx6fSzVA481E8NDTYFckcbZQgIt52d/l9hic0qnoKyc9GUygrbqAH+/GuL+YAlzde4cwwtUqD+EAylF2NWvfQ5ldDlDtbk5FMCc+t02FSJHfSyhpNaRHIdHpBY= 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 AM0P192MB0499.EURP192.PROD.OUTLOOK.COM (2603:10a6:208:4e::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4242.15; Thu, 17 Jun 2021 16:03:48 +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:03:48 +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 059/256] cl8k: add dfs/dfs.c Date: Thu, 17 Jun 2021 15:59:06 +0000 Message-Id: <20210617160223.160998-60-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:03:37 +0000 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: ebe3e279-10d1-4606-f0fd-08d931a97818 X-MS-TrafficTypeDiagnostic: AM0P192MB0499: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:9508; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: zeitfKhqiu0Bm3qc9CTNjGBWK0u7TODOYAzpahDbtKf4MD1gg0m2wGkqHCuvaQvBLRku0u/i1Cq6/bPijJtfP/NdE0JwQ5jNUI8xlYzBPjpobVAclmBb0fkJrqOWUbFlZEweX05twGQRp0qnYwuZTFBPZXx68sIm4YMk3JntZxzJRTM/XIkpmldw/J3EMneZ8nJ5qbUcxEOrDVxYXFMbCCxsU3ipekv7K1m4ziB0FhRx0czl4IW0hrXS91UdORrJBj2V35tvh8jkUStSJrWGQ/eWe3nP+z5MEhUdOQvxp272NSUn4vF6SeUyNXtuKzTL8k4qrp7KAIQirEsE6XWXa9JVLF3f8CFMUdgJwMOhddfMzK5rBIwQ0GHl3BwRVuHChYFg/af1OwMDgSC2GdMOBBRBZRHbDF7oJuUL9ODgLESQzn+2WM4lK8CRhG8iNoPxDgDc6qjJ0kO4FMCtI9ORpr3quUA9rEqDi4z+Qjvx8qaHISS01Eqh6/OhAiEk4RCc/bVVHh6egkRE/YPRuuyK+ruXIpl/20lzZe8YhHNnjgrTJyZGoILAIXDQpo5Qj9/OvK/2FwVVLmlQ3ZSHOvPougHQbEiJXS1sr1iJI/bd4ypwAloJM3V+B8bCpaK1oRJFaIY+Tcw15mgHOTbXO53wsMA+kTGz5c1DLQ4EZPh9NRV0E82FZZigO2410lfP/TwFULOcCERVHqRUv47uFZPoYQ== 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:(136003)(39840400004)(396003)(376002)(366004)(346002)(86362001)(107886003)(66556008)(52116002)(83380400001)(55236004)(36756003)(4326008)(186003)(16526019)(956004)(316002)(6486002)(8676002)(54906003)(38100700002)(38350700002)(66476007)(6506007)(2906002)(26005)(9686003)(6512007)(66946007)(2616005)(5660300002)(478600001)(30864003)(1076003)(6666004)(8936002)(6916009)(69590400013)(32563001); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: bjdOby3w002dD9a044kxySNvy2qC5auFwufg4zu2Vs5TJToq4xCbZynRIGu0zqm956IpVmLlsIbZde8rhHu2ooQKLdYtkVXNIekISJ5F9C4+eN+Iss7tmTkcnXKIjop/UYSms4mcU6JSoXUT7Da+pSZ6L3Ys10I3+zCoaQ581U+8DsifsCfRjqjBAmh5UWVId+MVsuIQri5wOkdhXtFvkG8CL86PgFyapZEHYOXqXdNXDmdJdfe3UXE0zdwsItrd+TsOFjTEoy3h/1GBSdIER5OHosps784UWwKA8xl84LBHqOjfsEO9OlvTPMYlgiYjxgzn7pxG4bztfPEv4eSGeuzezQRf//7uBN2dhvmCx57JTTOV46KbEtp/PYzU6iELPSADhg+C4JJas5JhwEQisbjZxS8aHWHkSS8RweMRqLPDAEPVk+nEVngdkasZ4s+DanM4DQoe93St9YmfJpEULaEwM0BFQ9f0gnpSCa8ynDNwKNGDcI10iuNjgU/3D3fb3s9a/6naoiMm2qFq62Pl19AkbySD3chWNy8NvFb+5BR71xUCDwAEs34eUhsrVMY251WT6LBppUwaOl69mft/4CAgt8F+kvLX7MrD31fmsh7+ZQBqrnG57DSX+afEIXkrr7eiv9rIvUA7GPU3Tl3hoIUyEXosuz3naYh1Y+A1nPSZtyygqjUDfbwwySpb9JQm6AK270tmfpM33j4RDGcyD5+UJxpVUMNWAIcUdUWqB6746aPg/+b1oAh67PneWIcBPBQjI2MMitVY3A7DKCck3Oy3Ap6ZlP4OE9kbnQDRxs1JX3TIZtPEGZy37FKRas9BkQ5RTMB/DV1OqyAJwF1GV+mWVPb/JkM946o/UqnJ2IZp3W59FPLO9wb9HK7C/mDCUa460FwAj7AXo0EHpYiq43KdAD+3/r9HRLUkbBq9glNKnGkxPupfK9aMIXERPGz3XMVU2OE1mqwq8SHZnVdKStFtrU7NBBxDIlNZ5x2VgOT53JNHGulPEUO5jzphuypQ4andm3+2FcpieqGhyMEcd/gN+pFpBRC17RtyMddejOl/8eLZcUS9s8XBNcg45qArCtWgyq7RidfqoUA1cUfkq3gFaOlVMuoOi29CzgS5Z0UHW8v3FoSJ50lyQuAJ3ESxwI/MtNgvMDTkCBjxN2BPoMDW6cMrT6pk9BrV7bneOi58hylzBbrO8ZWX25WFEBD7/ulpeqvyMJlINZk723bv8T3ARAnuhD1xg9SFrzjIqIfOoR9G0URhZcgBpDIVY8B22Se3dGItEEuk25A0cLhFs4yg+Z6a6grijH+9iv9fls4E/E2husyqMq2h/ARC5rIb X-OriginatorOrg: celeno.com X-MS-Exchange-CrossTenant-Network-Message-Id: ebe3e279-10d1-4606-f0fd-08d931a97818 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:03:38.8001 (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: TicE9WvpBg9s1GH312fhlIlu3aNcpNX6cpG15e0hCezBZ6CiDb01g54jw6hb5Bw7wAuFF4O6G3xLnR0ak2J4lQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0P192MB0499 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/dfs/dfs.c | 977 +++++++++++++++++++++ 1 file changed, 977 insertions(+) create mode 100644 drivers/net/wireless/celeno/cl8k/dfs/dfs.c -- 2.30.0 diff --git a/drivers/net/wireless/celeno/cl8k/dfs/dfs.c b/drivers/net/wireless/celeno/cl8k/dfs/dfs.c new file mode 100644 index 000000000000..b2717a8be26a --- /dev/null +++ b/drivers/net/wireless/celeno/cl8k/dfs/dfs.c @@ -0,0 +1,977 @@ +// SPDX-License-Identifier: MIT +/* Copyright(c) 2019-2021, Celeno Communications Ltd. */ + +#include "dfs/dfs.h" +#include "dfs/dfs_db.h" +#include "dfs/radar.h" +#include "chip.h" +#include "channel.h" +#include "chan_info.h" +#include "env_det.h" +#include "debug.h" +#include "utils/timer.h" +#include "tx/tx.h" +#include "temperature.h" +#include "utils/math.h" +#include "calib.h" +#include "traffic.h" +#include "utils/utils.h" +#include "reg/reg_riu.h" +#include "reg/reg_mac_hw.h" +#include "band.h" +#include "chandef.h" +#include "utils/string.h" +#include "utils/file.h" +#include "config.h" + +#define dfs_pr(cl_hw, level, ...) \ + do { \ + if ((level) <= (cl_hw)->dfs_db.dbg_lvl) \ + pr_debug(__VA_ARGS__); \ + } while (0) + +#define dfs_pr_verbose(cl_hw, ...) dfs_pr((cl_hw), DBG_LVL_VERBOSE, ##__VA_ARGS__) +#define dfs_pr_err(cl_hw, ...) dfs_pr((cl_hw), DBG_LVL_ERROR, ##__VA_ARGS__) +#define dfs_pr_warn(cl_hw, ...) dfs_pr((cl_hw), DBG_LVL_WARNING, ##__VA_ARGS__) +#define dfs_pr_trace(cl_hw, ...) dfs_pr((cl_hw), DBG_LVL_TRACE, ##__VA_ARGS__) +#define dfs_pr_info(cl_hw, ...) dfs_pr((cl_hw), DBG_LVL_INFO, ##__VA_ARGS__) + +#define COUNTRY_CODE_LEN 2 + +/* + * ID Min Max Tol Min Max Tol Tol MIN PPB Trig Type + * Width Width Width PRI PRI PRI FREQ Burst Count + */ + +/* ETSI Radar Types v1.8.2 */ +static struct cl_radar_type radar_type_etsi[] = { + + {0, 1, 1, 1, 1428, 1428, 1, 1, 1, 18, 10, RADAR_WAVEFORM_SHORT}, + {1, 1, 5, 1, 1000, 5000, 1, 1, 1, 10, 5, RADAR_WAVEFORM_SHORT}, + {2, 1, 15, 1, 625, 5000, 1, 1, 1, 15, 8, RADAR_WAVEFORM_SHORT}, + {3, 1, 15, 1, 250, 435, 1, 1, 1, 25, 10, RADAR_WAVEFORM_SHORT}, + {4, 10, 30, 1, 250, 500, 1, 1, 1, 20, 10, RADAR_WAVEFORM_SHORT}, + {5, 1, 5, 1, 2500, 3334, 1, 1, 2, 10, 5, RADAR_WAVEFORM_STAGGERED}, + {6, 1, 5, 1, 833, 2500, 1, 1, 2, 15, 8, RADAR_WAVEFORM_STAGGERED}, +}; + +/* FCC Radar Types 8/14 */ +static struct cl_radar_type radar_type_fcc[] = { + {0, 1, 1, 0, 1428, 1428, 1, 1, 1, 18, 10, RADAR_WAVEFORM_SHORT}, + {1, 1, 5, 3, 518, 3066, 3, 1, 1, 18, 10, RADAR_WAVEFORM_SHORT}, + {2, 1, 5, 3, 150, 230, 3, 1, 1, 23, 10, RADAR_WAVEFORM_SHORT}, + {3, 3, 10, 3, 200, 500, 3, 1, 1, 16, 6, RADAR_WAVEFORM_SHORT}, + {4, 6, 20, 3, 200, 500, 3, 1, 1, 12, 6, RADAR_WAVEFORM_SHORT}, + {5, 50, 100, 50, 1000, 2000, 1, 1, 2, 10, 5, RADAR_WAVEFORM_LONG}, + {6, 1, 1, 0, 333, 333, 1, 1, 2, 30, 10, RADAR_WAVEFORM_LONG}, +}; + +static void cl_dfs_fw_en(struct cl_hw *cl_hw, u8 dfs_en) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + struct cl_tcv_conf *conf = cl_hw->conf; + + cl_msg_tx_set_dfs(cl_hw, dfs_en, dfs_db->dfs_standard, + conf->ci_dfs_initial_gain, conf->ci_dfs_agc_cd_th); +} + +static int cl_dfs_print_tbl(struct cl_hw *cl_hw) +{ + int i; + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + char *buf = NULL; + ssize_t buf_size; + int err = 0; + int len = 0; + + cl_snprintf(&buf, &len, &buf_size, + "-------------------------------------------------------------------------" + "---------------\n" + "| | Min | Max | Tol | Min | Max | Tol | Tol | Min | |" + " Trig | |\n" + "| ID | Width | Width | Width | PRI | PRI | PRI | FREQ | Burst | PPB |" + " Count | Type |\n" + "-------------------------------------------------------------------------" + "---------------\n"); + + for (i = 0; i < dfs_db->radar_type_cnt; i++) { + cl_snprintf(&buf, &len, &buf_size, + "| %2u | %5d | %5d | %5d | %5d | %5d | %3d | %4d | %5u |" + " %3u | %5u | %4d |\n", + dfs_db->radar_type[i].id, + dfs_db->radar_type[i].min_width, + dfs_db->radar_type[i].max_width, + dfs_db->radar_type[i].tol_width, + dfs_db->radar_type[i].min_pri, + dfs_db->radar_type[i].max_pri, + dfs_db->radar_type[i].tol_pri, + dfs_db->radar_type[i].tol_freq, + dfs_db->radar_type[i].min_burst, + dfs_db->radar_type[i].ppb, + dfs_db->radar_type[i].trig_count, + dfs_db->radar_type[i].waveform); + cl_snprintf(&buf, &len, &buf_size, + "-----------------------------------------------------------------" + "-----------------------\n"); + } + + err = cl_vendor_reply(cl_hw, buf, len); + kfree(buf); + + return err; +} + +static bool cl_dfs_create_detection_buffer(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db, + struct cl_dfs_pulse *pulse_buffer, u8 *samples_cnt, + unsigned long time) +{ + u8 i; + u8 pulse_idx; + /* Init First index to last */ + u8 first_pulse_idx = (dfs_db->buf_idx - 1 + CL_DFS_PULSE_BUF_SIZE) & CL_DFS_PULSE_BUF_MASK; + + /* Find Start Pulse indexes */ + for (i = 0; i < CL_DFS_PULSE_BUF_SIZE; i++) { + pulse_idx = (i + dfs_db->buf_idx) & CL_DFS_PULSE_BUF_MASK; + + if ((time - dfs_db->dfs_pulse[pulse_idx].time) < dfs_db->search_window) { + first_pulse_idx = pulse_idx; + break; + } + } + + dfs_pr_info(cl_hw, "DFS: First pulse idx = %u, Last pulse idx = %u\n", + first_pulse_idx, dfs_db->buf_idx - 1); + + if (dfs_db->buf_idx - 1 >= first_pulse_idx) + if ((dfs_db->buf_idx - first_pulse_idx - 1) < (dfs_db->min_pulse_eeq - 1)) { + /* Return if buffer don't hold enough valid samples */ + dfs_pr_warn(cl_hw, "DFS: Not enough pulses in buffer\n"); + + return false; + } + + /* Copy the processed samples to local Buf to avoid index castings */ + for (i = 0; pulse_idx != ((dfs_db->buf_idx - 1 + CL_DFS_PULSE_BUF_SIZE) + & CL_DFS_PULSE_BUF_MASK); i++) { + pulse_idx = (i + first_pulse_idx) & CL_DFS_PULSE_BUF_MASK; + memcpy(&pulse_buffer[i], &dfs_db->dfs_pulse[pulse_idx], sizeof(pulse_buffer[i])); + } + *samples_cnt = i + 1; + + return true; +} + +static void cl_dfs_add_pulses_to_global_buffer(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db, + struct cl_radar_pulse *pulse, u8 pulse_cnt, + unsigned long time) +{ + int i; + + for (i = 0; i < pulse_cnt; i++) + dfs_pr_info(cl_hw, "Pulse=%d, Width=%u, PRI=%u, FREQ=%d, Time=%lu, FOM=%x\n", + i, pulse[i].len, pulse[i].rep, pulse[i].freq, time, pulse[i].fom); + + /* Maintain cyclic pulse buffer */ + for (i = 0; i < pulse_cnt; i++) { + dfs_db->dfs_pulse[dfs_db->buf_idx].freq = pulse[i].freq; + dfs_db->dfs_pulse[dfs_db->buf_idx].width = pulse[i].len; + dfs_db->dfs_pulse[dfs_db->buf_idx].pri = pulse[i].rep; + dfs_db->dfs_pulse[dfs_db->buf_idx].occ = 0; /* occ temp disabled. */ + dfs_db->dfs_pulse[dfs_db->buf_idx].time = time; + + dfs_db->buf_idx++; + dfs_db->buf_idx &= CL_DFS_PULSE_BUF_MASK; + } +} + +static bool cl_dfs_buf_maintain(struct cl_hw *cl_hw, struct cl_radar_pulse *pulse, + struct cl_dfs_pulse *pulse_buffer, u8 pulse_cnt, + unsigned long time, u8 *samples_cnt, struct cl_dfs_db *dfs_db) +{ + int i; + + cl_dfs_add_pulses_to_global_buffer(cl_hw, dfs_db, pulse, pulse_cnt, time); + if (!cl_dfs_create_detection_buffer(cl_hw, dfs_db, pulse_buffer, samples_cnt, time)) + return false; + + for (i = 0; i < *samples_cnt; i++) + dfs_pr_info(cl_hw, "DFS: pulse[%d]: width=%u, pri=%u, freq=%d\n", + i, pulse_buffer[i].width, pulse_buffer[i].pri, pulse_buffer[i].freq); + + return true; +} + +static inline bool cl_dfs_pulse_match(s32 pulse_val, s32 spec_min_val, + s32 spec_max_val, s32 spec_tol) +{ + return ((pulse_val >= (spec_min_val - spec_tol)) && + (pulse_val <= (spec_max_val + spec_tol))); +} + +static u8 cl_dfs_is_stag_pulse(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db, + struct cl_dfs_pulse *pulse) +{ + int i; + struct cl_radar_type *radar_type; + + for (i = 0; i < dfs_db->radar_type_cnt; i++) { + radar_type = &dfs_db->radar_type[i]; + + if (radar_type->waveform != RADAR_WAVEFORM_STAGGERED) + continue; + + if (cl_dfs_pulse_match((s32)pulse->width, radar_type->min_width, + radar_type->max_width, radar_type->tol_width) && + cl_dfs_pulse_match((s32)pulse->pri, radar_type->min_pri, + radar_type->max_pri, radar_type->tol_pri)) { + /* Search for the second burst */ + if (abs(pulse[0].pri - pulse[2].pri) <= dfs_db->radar_type[i].tol_pri && + abs(pulse[1].pri - pulse[3].pri) <= radar_type->tol_pri && + abs(pulse[0].pri - pulse[1].pri) > radar_type->tol_pri && + abs(pulse[2].pri - pulse[3].pri) > radar_type->tol_pri) { + dfs_pr_info(cl_hw, "DFS: Found match type %d\n", i); + return (i + 1); + } else if (abs(pulse[0].pri - pulse[3].pri) <= radar_type->tol_pri && + abs(pulse[1].pri - pulse[4].pri) <= radar_type->tol_pri && + abs(pulse[0].pri - pulse[1].pri) > radar_type->tol_pri && + abs(pulse[3].pri - pulse[4].pri) > radar_type->tol_pri) { + dfs_pr_info(cl_hw, "DFS: Found match radar %d\n", i); + return (i + 1); + } + } + } + + return 0; +} + +static u8 cl_dfs_is_non_stag_pulse(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db, + struct cl_dfs_pulse *pulse) +{ + int i; + struct cl_radar_type *radar_type; + + for (i = 0; i < dfs_db->radar_type_cnt; i++) { + radar_type = &dfs_db->radar_type[i]; + + if (radar_type->waveform == RADAR_WAVEFORM_STAGGERED) + continue; + + if (cl_dfs_pulse_match((s32)pulse->width, radar_type->min_width, + radar_type->max_width, radar_type->tol_width) && + cl_dfs_pulse_match((s32)pulse->pri, radar_type->min_pri, + radar_type->max_pri, radar_type->tol_pri)) { + dfs_pr_info(cl_hw, "DFS: Found match type %d\n", i); + return (i + 1); + } + } + + dfs_pr_warn(cl_hw, "DFS: Match not found\n"); + + return 0; +} + +static u8 cl_dfs_get_pulse_type(struct cl_hw *cl_hw, struct cl_dfs_pulse *pulse, + bool stag_candidate) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + + if (stag_candidate) { + u8 pulse_type = cl_dfs_is_stag_pulse(cl_hw, dfs_db, pulse); + + if (pulse_type) + return pulse_type; + } + + return cl_dfs_is_non_stag_pulse(cl_hw, dfs_db, pulse); +} + +static bool cl_dfs_compare_cand(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db, u8 pulse_type, + struct cl_dfs_pulse radar_cand, u8 *match, int idx, + u8 *occ_ch_cand) +{ + int i; + + if (!(abs(dfs_db->pulse_buffer[idx].width - radar_cand.width) <= + dfs_db->radar_type[pulse_type].tol_width)) + goto end; + + if (!(abs(dfs_db->pulse_buffer[idx].freq - radar_cand.freq) <= + dfs_db->radar_type[pulse_type].tol_freq)) + goto end; + + for (i = 1; i < CL_DFS_CONCEAL_CNT; i++) + if (abs(dfs_db->pulse_buffer[idx].pri - i * radar_cand.pri) <= + dfs_db->radar_type[pulse_type].tol_pri) + break; + + if (i == CL_DFS_CONCEAL_CNT) + goto end; + + (*match)++; + (*occ_ch_cand) += dfs_db->pulse_buffer[i].occ; + +end: + dfs_pr_info(cl_hw, "DFS: compared pulse - width=%u, pri=%u, freq=%u match: %u " + "trig cnt: %u\n", + dfs_db->pulse_buffer[idx].width, dfs_db->pulse_buffer[idx].pri, + dfs_db->pulse_buffer[idx].freq, *match, + dfs_db->radar_type[pulse_type].trig_count); + + if (*match < dfs_db->radar_type[pulse_type].trig_count) + return false; + + return true; +} + +static bool cl_dfs_check_cand(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db, u8 pulse_type, + struct cl_dfs_pulse radar_cand, u8 samples_cnt) +{ + u8 occ_ch_cand = 0; + u8 match = 0; + int i; + + dfs_pr_info(cl_hw, "DFS: candidate pulse - width=%u, pri=%u, freq=%u\n", + radar_cand.width, radar_cand.pri, radar_cand.freq); + + for (i = 0; i < samples_cnt; i++) { + if (!cl_dfs_compare_cand(cl_hw, dfs_db, pulse_type, radar_cand, &match, i, + &occ_ch_cand)) + continue; + + dfs_pr_verbose(cl_hw, "DFS: Radar detected - type %u\n", pulse_type); + return true; + } + + return false; +} + +static bool cl_dfs_short_pulse_search(struct cl_hw *cl_hw, struct cl_radar_pulse *pulse, + u8 pulse_cnt, unsigned long time, struct cl_dfs_db *dfs_db) +{ + int i; + bool stag_candidate; + u8 samples_cnt = 0; + u8 pulse_type; + + /* Return if not enough pulses in the buffer */ + if (!cl_dfs_buf_maintain(cl_hw, pulse, dfs_db->pulse_buffer, pulse_cnt, time, + &samples_cnt, dfs_db)) + return false; + + for (i = 0; i < samples_cnt; i++) { + struct cl_dfs_pulse radar_cand; + + stag_candidate = false; + + /* Make sure there is enough samples to staggered check */ + if (dfs_db->dfs_standard == CL_STANDARD_ETSI && + (samples_cnt - i) > CL_DFS_STAGGERED_CHEC_LEN) + stag_candidate = true; + + pulse_type = cl_dfs_get_pulse_type(cl_hw, &dfs_db->pulse_buffer[i], stag_candidate); + + if (!pulse_type) + continue; + + radar_cand.width = dfs_db->pulse_buffer[i].width; + radar_cand.pri = dfs_db->pulse_buffer[i].pri; + radar_cand.freq = dfs_db->pulse_buffer[i].freq; + + if (cl_dfs_check_cand(cl_hw, dfs_db, pulse_type - 1, radar_cand, samples_cnt)) + return true; + } + + return false; +} + +static bool cl_dfs_long_pulse_search(struct cl_hw *cl_hw, struct cl_radar_pulse *pulse, + u8 pulse_cnt, unsigned long time) +{ + u32 prev_pulse_time_diff; + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + struct cl_tcv_conf *conf = cl_hw->conf; + int i; + + for (i = 0; i < pulse_cnt; i++) { + if (pulse[i].len > CL_DFS_LONG_MIN_WIDTH) { + prev_pulse_time_diff = time - dfs_db->last_long_pulse_ts; + + if (pulse[i].rep >= dfs_db->radar_type[5].min_pri && + pulse[i].rep <= dfs_db->radar_type[5].max_pri) + dfs_db->long_pri_match_count += 1; + + dfs_pr_info(cl_hw, "DFS: Long pulse search: width = %u, delta_time = %u\n", + pulse[i].len, prev_pulse_time_diff); + + if (dfs_db->long_pulse_count == 0 || + (prev_pulse_time_diff >= conf->ci_dfs_long_pulse_min && + prev_pulse_time_diff <= conf->ci_dfs_long_pulse_max)) { + dfs_db->long_pulse_count += 1; + } else if (prev_pulse_time_diff > min(dfs_db->max_interrupt_diff, + conf->ci_dfs_long_pulse_min)) { + dfs_db->long_pulse_count = 0; + dfs_db->short_pulse_count = 0; + dfs_db->long_pri_match_count = 0; + } + dfs_db->last_long_pulse_ts = time; + } else if (pulse[i].len < CL_DFS_LONG_FALSE_WIDTH) { + dfs_db->short_pulse_count++; + + if (dfs_db->short_pulse_count > CL_DFS_LONG_FALSE_IND) { + dfs_db->long_pulse_count = 0; + dfs_db->short_pulse_count = 0; + dfs_db->long_pri_match_count = 0; + + dfs_pr_warn(cl_hw, "DFS: Restart long sequence search\n"); + } + } + } + + if (dfs_db->long_pulse_count >= dfs_db->radar_type[5].trig_count && + dfs_db->long_pri_match_count >= (dfs_db->radar_type[5].trig_count - 1)) { + dfs_db->short_pulse_count = 0; + dfs_db->long_pulse_count = 0; + dfs_db->long_pri_match_count = 0; + return true; + } else { + return false; + } +} + +static bool cl_dfs_post_detection(struct cl_hw *cl_hw) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + struct cl_tcv_conf *conf = cl_hw->conf; + + /* Make sure firmware sets the DFS registers */ + cl_radar_flush(cl_hw); + cl_msg_tx_set_dfs(cl_hw, false, dfs_db->dfs_standard, + conf->ci_dfs_initial_gain, conf->ci_dfs_agc_cd_th); + + ieee80211_radar_detected(cl_hw->hw); + + return true; +} + +bool cl_dfs_pulse_process(struct cl_hw *cl_hw, struct cl_radar_pulse *pulse, u8 pulse_cnt, + unsigned long time) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + + dfs_db->pulse_cnt += pulse_cnt; + + if (dfs_db->dfs_standard == CL_STANDARD_FCC && + cl_dfs_long_pulse_search(cl_hw, pulse, pulse_cnt, time)) { + dfs_pr_verbose(cl_hw, "DFS: Radar detected - long\n"); + return cl_dfs_post_detection(cl_hw); + } else if (cl_dfs_short_pulse_search(cl_hw, pulse, pulse_cnt, time, dfs_db)) { + dfs_pr_verbose(cl_hw, "DFS: Radar detected - short\n"); + return cl_dfs_post_detection(cl_hw); + } + + return false; +} + +static void cl_dfs_set_min_pulse(struct cl_hw *cl_hw) +{ + int i; + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + + dfs_db->min_pulse_eeq = U8_MAX; + + for (i = 0; i < dfs_db->radar_type_cnt; i++) { + if (dfs_db->radar_type[i].trig_count < dfs_db->min_pulse_eeq) + dfs_db->min_pulse_eeq = dfs_db->radar_type[i].trig_count; + } + dfs_db->min_pulse_eeq = max(dfs_db->min_pulse_eeq, (u8)CL_DFS_MIN_PULSE_TRIG); +} + +static void cl_dfs_set_region(struct cl_hw *cl_hw, enum cl_reg_standard std) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + + dfs_db->dfs_standard = std; + + if (dfs_db->dfs_standard == CL_STANDARD_FCC) { + dfs_db->csa_cnt = CL_DFS_FCC_CSA_CNT; + dfs_db->radar_type = radar_type_fcc; + dfs_db->radar_type_cnt = sizeof(radar_type_fcc) / sizeof(struct cl_radar_type); + } else { + dfs_db->csa_cnt = CL_DFS_CE_CSA_CNT; + dfs_db->radar_type = radar_type_etsi; + dfs_db->radar_type_cnt = sizeof(radar_type_etsi) / sizeof(struct cl_radar_type); + } +} + +static void cl_dfs_start_cac(struct cl_dfs_db *db) +{ + db->cac.started = true; +} + +static void cl_dfs_end_cac(struct cl_dfs_db *db) +{ + db->cac.started = false; +} + +void cl_dfs_radar_listen_start(struct cl_hw *cl_hw) +{ + set_bit(CL_DEV_RADAR_LISTEN, &cl_hw->drv_flags); + + cl_dfs_fw_en(cl_hw, true); + + dfs_pr_verbose(cl_hw, "DFS: Started radar listening\n"); +} + +void cl_dfs_radar_listen_end(struct cl_hw *cl_hw) +{ + clear_bit(CL_DEV_RADAR_LISTEN, &cl_hw->drv_flags); + + cl_dfs_fw_en(cl_hw, false); + + dfs_pr_verbose(cl_hw, "DFS: Ended radar listening\n"); +} + +void cl_dfs_force_cac_start(struct cl_hw *cl_hw) +{ + bool is_listening = test_bit(CL_DEV_RADAR_LISTEN, &cl_hw->drv_flags); + + cl_dfs_start_cac(&cl_hw->dfs_db); + + /* Reset request state upon completion */ + cl_dfs_request_cac(cl_hw, false); + + /* Disable all the TX flow - be silent */ + cl_tx_en(cl_hw, CL_TX_EN_DFS, false); + + /* If for some reason we are still not listening radar, do it */ + if (unlikely(!is_listening && cl_hw->hw->conf.radar_enabled)) + cl_dfs_radar_listen_start(cl_hw); + + dfs_pr_verbose(cl_hw, "DFS: CAC started\n"); +} + +void cl_dfs_force_cac_end(struct cl_hw *cl_hw) +{ + bool is_listening = test_bit(CL_DEV_RADAR_LISTEN, &cl_hw->drv_flags); + + /* Enable all the TX flow */ + cl_tx_en(cl_hw, CL_TX_EN_DFS, true); + + /* + * If for some reason we are still listening and mac80211 does not + * require to listen radar - disable it + */ + if (unlikely(is_listening && !cl_hw->hw->conf.radar_enabled)) + cl_dfs_radar_listen_end(cl_hw); + + cl_dfs_end_cac(&cl_hw->dfs_db); + + dfs_pr_verbose(cl_hw, "DFS: CAC ended\n"); +} + +static u16 cl_dfs_get_remain_cac_time(struct cl_hw *cl_hw) +{ + struct cl_vif *cl_vif = cl_vif_get_first_ap(cl_hw); + struct wireless_dev *wdev = cl_vif ? ieee80211_vif_to_wdev(cl_vif->vif) : NULL; + + if (wdev && wdev->cac_started) + return (jiffies_to_msecs(jiffies - wdev->cac_start_time) / 1000U); + + return 0; +} + +bool __must_check cl_dfs_is_en(struct cl_hw *cl_hw) +{ + return cl_hw->dfs_db.en; +} + +bool __must_check cl_dfs_is_in_cac(struct cl_hw *cl_hw) +{ + return cl_hw->dfs_db.cac.started; +} + +bool __must_check cl_dfs_radar_listening(struct cl_hw *cl_hw) +{ + return test_bit(CL_DEV_RADAR_LISTEN, &cl_hw->drv_flags); +} + +bool __must_check cl_dfs_requested_cac(struct cl_hw *cl_hw) +{ + return cl_hw->dfs_db.cac.requested; +} + +void cl_dfs_request_cac(struct cl_hw *cl_hw, bool should_do) +{ + cl_hw->dfs_db.cac.requested = should_do; +} + +static void cl_dfs_edit_tbl(struct cl_hw *cl_hw, u8 row, u8 line, s16 val) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + + if (row > dfs_db->radar_type_cnt) { + dfs_pr_err(cl_hw, "Invalid row number (%u) [0 - %u]\n", line, + dfs_db->radar_type_cnt - 1); + return; + } + + if (line == 0 || line > CL_DFS_MAX_TBL_LINE) { + dfs_pr_err(cl_hw, "Invalid line number (%u) [1 - %u]\n", line, + CL_DFS_MAX_TBL_LINE - 1); + return; + } + + if (line == 1) + dfs_db->radar_type[row].min_width = (s32)val; + else if (line == 2) + dfs_db->radar_type[row].max_width = (s32)val; + else if (line == 3) + dfs_db->radar_type[row].tol_width = (s32)val; + else if (line == 4) + dfs_db->radar_type[row].min_pri = (s32)val; + else if (line == 5) + dfs_db->radar_type[row].max_pri = (s32)val; + else if (line == 6) + dfs_db->radar_type[row].tol_pri = (s32)val; + else if (line == 7) + dfs_db->radar_type[row].tol_freq = (s32)val; + else if (line == 8) + dfs_db->radar_type[row].min_burst = (u8)val; + else if (line == 9) + dfs_db->radar_type[row].ppb = (u8)val; + else if (line == 10) + dfs_db->radar_type[row].trig_count = (u8)val; + else if (line == 11) + dfs_db->radar_type[row].waveform = (enum cl_radar_waveform)val; + + /* Verify if min_pulse_eeq was changed */ + cl_dfs_set_min_pulse(cl_hw); +} + +static void cl_dfs_tbl_overwrite_set(struct cl_hw *cl_hw) +{ + s8 *tok = NULL, *saveptr = NULL; + u8 param1 = 0; + u8 param2 = 0; + s16 param3 = 0; + char str[64]; + + if (strlen(cl_hw->conf->ce_dfs_tbl_overwrite) == 0) + return; + + snprintf(str, sizeof(str), cl_hw->conf->ce_dfs_tbl_overwrite); + + tok = cl_strtok_r(str, ",", &saveptr); + while (tok) { + if (sscanf(tok, "%hhd,%hhd,%hd", ¶m1, ¶m2, ¶m3) == 3) + cl_dfs_edit_tbl(cl_hw, param1, param2, param3); + tok = cl_strtok_r(NULL, ",", &saveptr); + } +} + +void cl_dfs_close(struct cl_hw *cl_hw) +{ + if (!cl_band_is_5g(cl_hw)) + return; + + cl_hw->dfs_db.en = false; +} + +void cl_dfs_init(struct cl_hw *cl_hw) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + struct cl_tcv_conf *conf = cl_hw->conf; + + if (!cl_band_is_5g(cl_hw)) + return; + + dfs_db->en = conf->ci_ieee80211h; + + /* + * Setting min window size to avoid the case where the second interrupt + * within the burst is setting the counter to 0. the max is between jiffies + * unit and max PRI in ms. + */ + dfs_db->max_interrupt_diff = max(1000 / HZ, 2); + dfs_db->search_window = CL_DFS_PULSE_WINDOW; + + cl_dfs_set_region(cl_hw, cl_hw->channel_info.standard); + cl_dfs_set_min_pulse(cl_hw); + cl_dfs_tbl_overwrite_set(cl_hw); +} + +void cl_dfs_recovery(struct cl_hw *cl_hw) +{ + /* Re-enable DFS after recovery */ + if (cl_dfs_is_in_cac(cl_hw)) { + cl_dfs_fw_en(cl_hw, true); + + /* If recovery happened during CAC make sure to disable beacon backup */ + cl_tx_en(cl_hw, CL_TX_EN_DFS, false); + } +} + +static int cl_dfs_print_pulse_buffer(struct cl_hw *cl_hw, bool clear_buf) +{ + int i; + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + char *buf = NULL; + ssize_t buf_size; + int err = 0; + int len = 0; + + cl_snprintf(&buf, &len, &buf_size, + "DFS Pulse Count = %u\n", dfs_db->pulse_cnt); + + for (i = 0; i < ARRAY_SIZE(dfs_db->dfs_pulse); i++) { + cl_snprintf(&buf, &len, &buf_size, + "Pulse Buffer: i=%d, Width=%u, PRI=%u, FREQ=%d, OCC=%u, Time=%lu\n", + i, dfs_db->dfs_pulse[i].width, + dfs_db->dfs_pulse[i].pri, + dfs_db->dfs_pulse[i].freq, + dfs_db->dfs_pulse[i].occ, + dfs_db->dfs_pulse[i].time); + } + + if (clear_buf) { + dfs_db->pulse_cnt = 0; + memset(dfs_db->dfs_pulse, 0, sizeof(dfs_db->dfs_pulse)); + } + + err = cl_vendor_reply(cl_hw, buf, len); + kfree(buf); + + return err; +} + +static void cl_dfs_cli_force_detection(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db) +{ + dfs_pr_verbose(cl_hw, "DFS: Force Radar Detection\n"); + cl_dfs_post_detection(cl_hw); +} + +static void cl_dfs_cli_set_cac(struct cl_hw *cl_hw, bool start) +{ + if (start) + cl_dfs_force_cac_start(cl_hw); + else + cl_dfs_force_cac_end(cl_hw); +} + +static int cl_dfs_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, + "dfs usage:\n" + "-b : Print pulse buffer [(0)Read / (1)Read and clear]\n" + "-c : Stop/Start CAC[(0)Stop / (1)Start]\n" + "-d : DFS debug mode[0: Verbose 1: Error, 2: Warning, 3: Trace, 4: Info]\n" + "-e : Enable/Disable DFS [(0)Disable DFS / (1)Enable DFS]\n" + "-f : Force radar detection\n" + "-i : Set initial gain\n" + "-j : Set agc cd threshold\n" + "-k : return remaining cac time (in seconds)\n" + "-m : Set Min/Max Windows for Long radar types\n" + "-p : Print radar tables [0:Detection Table, 1:Channel info]\n" + "-q : Print jumpable channel list\n" + "-s : Simulate radar detection table " + "[width][pri][freq][time]\n" + "-t : Edit radar detection table [row][line][value]\n" + "-w : Set search window size\n"); + + err = cl_vendor_reply(cl_hw, buf, strlen(buf)); + kfree(buf); + + return err; +} + +int cl_dfs_cli(struct cl_hw *cl_hw, struct cli_params *cli_params, u8 *ret_buf, u16 *ret_buf_len) +{ + s32 *params = cli_params->params; + u32 expected_params = 0; + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + struct cl_tcv_conf *conf = cl_hw->conf; + bool print_buf = false; + bool dbg_lvl = false; + bool en = false; + bool force = false; + bool print_tbl = false; + bool return_remain_cac_time = false; + bool radar_sim = false; + bool edit_tbl = false; + bool win_set = false; + bool cac = false; + bool long_prms = false; + bool init_gain = false; + bool agc_cd_hh = false; + + switch (cli_params->option) { + case 'b': + print_buf = true; + expected_params = 1; + break; + case 'c': + cac = true; + expected_params = 1; + break; + case 'd': + dbg_lvl = true; + expected_params = 1; + break; + case 'e': + en = true; + expected_params = 1; + break; + case 'f': + force = true; + expected_params = 0; + break; + case 'i': + init_gain = true; + expected_params = 1; + break; + case 'j': + agc_cd_hh = true; + expected_params = 1; + break; + case 'k': + return_remain_cac_time = true; + expected_params = 0; + break; + case 'm': + long_prms = true; + expected_params = 2; + break; + case 'p': + print_tbl = true; + expected_params = 1; + break; + case 's': + radar_sim = true; + expected_params = 4; + break; + case 't': + edit_tbl = true; + expected_params = 3; + break; + case 'w': + win_set = true; + expected_params = 1; + break; + case '?': + return cl_dfs_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 (cac) { + bool start = (bool)params[0]; + + cl_dfs_cli_set_cac(cl_hw, start); + return 0; + } + + if (print_buf) + return cl_dfs_print_pulse_buffer(cl_hw, (bool)params[0]); + + if (dbg_lvl) { + s32 dbg_lvl = params[0]; + + if (dbg_lvl > 0 && dbg_lvl < DBG_LVL_MAX) { + dfs_db->dbg_lvl = dbg_lvl; + dfs_pr_verbose(cl_hw, "Debug level = %d\n", dbg_lvl); + } else { + dfs_pr_err(cl_hw, "Invalid debug level (%d)\n", dbg_lvl); + } + + return 0; + } + + if (en) { + dfs_db->en = (bool)(params[0]); + dfs_pr_verbose(cl_hw, "DFS = %s\n", dfs_db->en ? "Enabled" : "Disabled"); + cl_dfs_fw_en(cl_hw, dfs_db->en); + return 0; + } + + if (force) { + cl_dfs_cli_force_detection(cl_hw, dfs_db); + return 0; + } + + if (print_tbl) { + u8 table = (u8)params[0]; + + if (table == 0) + return cl_dfs_print_tbl(cl_hw); + return 0; + } + + if (return_remain_cac_time) { + u16 cac_time = cl_dfs_get_remain_cac_time(cl_hw); + + snprintf(ret_buf, PAGE_SIZE, "%u", cac_time); + *ret_buf_len = strlen(ret_buf); + return 1; + } + + if (radar_sim) { + struct cl_radar_pulse pulse[CL_DFS_MAX_PULSE]; + unsigned long time; + + pulse[0].len = (u32)params[0]; + pulse[0].rep = (u32)params[1]; + pulse[0].freq = (s32)params[2]; + time = (unsigned long)params[3]; + cl_dfs_pulse_process(cl_hw, pulse, 1, time); + return 0; + } + + if (edit_tbl) { + cl_dfs_edit_tbl(cl_hw, (u8)params[0], (u8)params[1], (s16)params[2]); + return 0; + } + + if (win_set) { + dfs_db->search_window = (u16)params[0]; + dfs_pr_verbose(cl_hw, "Search window size = %u\n", dfs_db->search_window); + return 0; + } + + if (long_prms) { + conf->ci_dfs_long_pulse_min = (u16)params[0]; + conf->ci_dfs_long_pulse_max = (u16)params[1]; + dfs_pr_verbose(cl_hw, "Long pulse min = %u\n", conf->ci_dfs_long_pulse_min); + dfs_pr_verbose(cl_hw, "Long pulse max = %u\n", conf->ci_dfs_long_pulse_max); + return 0; + } + + if (init_gain) { + conf->ci_dfs_initial_gain = (u8)params[0]; + dfs_pr_verbose(cl_hw, "Initial gain = %u\n", conf->ci_dfs_initial_gain); + return 0; + } + + if (agc_cd_hh) { + conf->ci_dfs_agc_cd_th = (u8)params[0]; + dfs_pr_verbose(cl_hw, "AGC CD threshold = %u\n", conf->ci_dfs_agc_cd_th); + return 0; + } + +out_err: + return -EIO; +} +