From 07be57e8f827984ff1dfb2f9a0790bf8a1a6deb6 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Mon, 2 Dec 2024 22:03:54 +0530 Subject: [PATCH 1/3] samples: wifi: radio_test: Add a single domain combo radio test With the introduction of 54L15 SoC where the complete BLE stack runs on the CPUAPP itself, reorg the radio test directories based on domains. Single domain radio test is primarily for 54L15 (and other similar SoCs) which basically combines Wi-Fi and Peripheral radio test samples in to one. Signed-off-by: Chaitanya Tata --- CODEOWNERS | 1 + doc/nrf/samples/wifi.rst | 1 + doc/nrf/samples/wifi_provisioning.rst | 4 +- doc/nrf/samples/wifi_radiotest.rst | 13 + .../{ => multi_domain}/CMakeLists.txt | 0 .../{ => multi_domain}/Kconfig.sysbuild | 0 .../radio_test/{ => multi_domain}/README.rst | 5 +- .../boards/thingy91x_nrf9151_ns.overlay | 0 .../radio_test/{ => multi_domain}/ficr.rst | 0 .../inc/nrf_wifi_radio_test_shell.h | 0 .../radio_test/{ => multi_domain}/prj.conf | 0 .../radio_test_subcommands.rst | 0 .../radio_test/{ => multi_domain}/sample.yaml | 4 +- .../{ => multi_domain}/sample_description.rst | 0 .../{ => multi_domain}/src/ficr_prog.c | 0 .../{ => multi_domain}/src/ficr_prog.h | 0 .../src/nrf_wifi_radio_ficr_shell.c | 0 .../src/nrf_wifi_radio_test_main.c | 0 .../src/nrf_wifi_radio_test_shell.c | 0 .../{ => multi_domain}/sysbuild.conf | 0 .../radio_test/single_domain/CMakeLists.txt | 23 + samples/wifi/radio_test/single_domain/Kconfig | 27 + .../wifi/radio_test/single_domain/README.rst | 16 + .../boards/nrf54l15dk_nrf54l15_cpuapp.conf | 12 + .../inc/nrf_wifi_radio_test_shell.h | 24 + .../wifi/radio_test/single_domain/prj.conf | 52 + .../wifi/radio_test/single_domain/sample.yaml | 15 + .../radio_test/single_domain/src/ficr_prog.c | 380 +++ .../radio_test/single_domain/src/ficr_prog.h | 23 + .../wifi/radio_test/single_domain/src/main.c | 105 + .../src/nrf_wifi_radio_ficr_shell.c | 528 ++++ .../src/nrf_wifi_radio_test_shell.c | 2535 +++++++++++++++++ .../radio_test/single_domain/src/radio_cmd.c | 1497 ++++++++++ .../radio_test/single_domain/src/radio_test.c | 1180 ++++++++ .../radio_test/single_domain/src/radio_test.h | 235 ++ .../radio_test/single_domain/sysbuild.conf | 8 + 36 files changed, 6682 insertions(+), 6 deletions(-) create mode 100644 doc/nrf/samples/wifi_radiotest.rst rename samples/wifi/radio_test/{ => multi_domain}/CMakeLists.txt (100%) rename samples/wifi/radio_test/{ => multi_domain}/Kconfig.sysbuild (100%) rename samples/wifi/radio_test/{ => multi_domain}/README.rst (60%) rename samples/wifi/radio_test/{ => multi_domain}/boards/thingy91x_nrf9151_ns.overlay (100%) rename samples/wifi/radio_test/{ => multi_domain}/ficr.rst (100%) rename samples/wifi/radio_test/{ => multi_domain}/inc/nrf_wifi_radio_test_shell.h (100%) rename samples/wifi/radio_test/{ => multi_domain}/prj.conf (100%) rename samples/wifi/radio_test/{ => multi_domain}/radio_test_subcommands.rst (100%) rename samples/wifi/radio_test/{ => multi_domain}/sample.yaml (96%) rename samples/wifi/radio_test/{ => multi_domain}/sample_description.rst (100%) rename samples/wifi/radio_test/{ => multi_domain}/src/ficr_prog.c (100%) rename samples/wifi/radio_test/{ => multi_domain}/src/ficr_prog.h (100%) rename samples/wifi/radio_test/{ => multi_domain}/src/nrf_wifi_radio_ficr_shell.c (100%) rename samples/wifi/radio_test/{ => multi_domain}/src/nrf_wifi_radio_test_main.c (100%) rename samples/wifi/radio_test/{ => multi_domain}/src/nrf_wifi_radio_test_shell.c (100%) rename samples/wifi/radio_test/{ => multi_domain}/sysbuild.conf (100%) create mode 100644 samples/wifi/radio_test/single_domain/CMakeLists.txt create mode 100644 samples/wifi/radio_test/single_domain/Kconfig create mode 100644 samples/wifi/radio_test/single_domain/README.rst create mode 100644 samples/wifi/radio_test/single_domain/boards/nrf54l15dk_nrf54l15_cpuapp.conf create mode 100644 samples/wifi/radio_test/single_domain/inc/nrf_wifi_radio_test_shell.h create mode 100644 samples/wifi/radio_test/single_domain/prj.conf create mode 100644 samples/wifi/radio_test/single_domain/sample.yaml create mode 100644 samples/wifi/radio_test/single_domain/src/ficr_prog.c create mode 100644 samples/wifi/radio_test/single_domain/src/ficr_prog.h create mode 100644 samples/wifi/radio_test/single_domain/src/main.c create mode 100644 samples/wifi/radio_test/single_domain/src/nrf_wifi_radio_ficr_shell.c create mode 100644 samples/wifi/radio_test/single_domain/src/nrf_wifi_radio_test_shell.c create mode 100644 samples/wifi/radio_test/single_domain/src/radio_cmd.c create mode 100644 samples/wifi/radio_test/single_domain/src/radio_test.c create mode 100644 samples/wifi/radio_test/single_domain/src/radio_test.h create mode 100644 samples/wifi/radio_test/single_domain/sysbuild.conf diff --git a/CODEOWNERS b/CODEOWNERS index 41949f018bcf..536e8e730f0e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -273,6 +273,7 @@ /doc/nrf/samples/tfm.rst @nrfconnect/ncs-aegir-doc /doc/nrf/samples/thread.rst @nrfconnect/ncs-terahertz-doc /doc/nrf/samples/wifi.rst @nrfconnect/ncs-wifi-doc +/doc/nrf/samples/wifi_radiotest.rst @nrfconnect/ncs-wifi-doc /doc/nrf/samples/wifi_provisioning.rst @nrfconnect/ncs-wifi-doc /doc/nrf/samples/wifi_zephyr.rst @nrfconnect/ncs-wifi-doc /doc/nrf/samples/zigbee.rst @nrfconnect/ncs-terahertz-doc diff --git a/doc/nrf/samples/wifi.rst b/doc/nrf/samples/wifi.rst index 3beee0f7af1b..a42f436f733f 100644 --- a/doc/nrf/samples/wifi.rst +++ b/doc/nrf/samples/wifi.rst @@ -17,5 +17,6 @@ This section lists the available |NCS| samples for the :ref:`Wi-Fi® ` :glob: ../../../samples/wifi/*/README + wifi_radiotest wifi_provisioning wifi_zephyr diff --git a/doc/nrf/samples/wifi_provisioning.rst b/doc/nrf/samples/wifi_provisioning.rst index 868d8a306995..cc2f8509a95f 100644 --- a/doc/nrf/samples/wifi_provisioning.rst +++ b/doc/nrf/samples/wifi_provisioning.rst @@ -1,6 +1,6 @@ .. _wifi_provisioning_samples: -Wi-Fi: provisioning samples +Wi-Fi: Provisioning samples ########################### The following samples demonstrate how to provision Wi-Fi® credentials to a Wi-Fi-capable Nordic Semiconductor's device. @@ -10,4 +10,4 @@ The following samples demonstrate how to provision Wi-Fi® credentials to a Wi-F :caption: Subpages :glob: - ../../../samples/wifi/*/*/README + ../../../samples/wifi/provisioning/*/README diff --git a/doc/nrf/samples/wifi_radiotest.rst b/doc/nrf/samples/wifi_radiotest.rst new file mode 100644 index 000000000000..148354b71b89 --- /dev/null +++ b/doc/nrf/samples/wifi_radiotest.rst @@ -0,0 +1,13 @@ +.. _wifi_radiotest_samples: + +Wi-Fi: Radio test samples +########################### + +The following samples demonstrate how to use the Wi-Fi® radio test commands. + +.. toctree:: + :maxdepth: 1 + :caption: Subpages + :glob: + + ../../../samples/wifi/radio_test/*/README diff --git a/samples/wifi/radio_test/CMakeLists.txt b/samples/wifi/radio_test/multi_domain/CMakeLists.txt similarity index 100% rename from samples/wifi/radio_test/CMakeLists.txt rename to samples/wifi/radio_test/multi_domain/CMakeLists.txt diff --git a/samples/wifi/radio_test/Kconfig.sysbuild b/samples/wifi/radio_test/multi_domain/Kconfig.sysbuild similarity index 100% rename from samples/wifi/radio_test/Kconfig.sysbuild rename to samples/wifi/radio_test/multi_domain/Kconfig.sysbuild diff --git a/samples/wifi/radio_test/README.rst b/samples/wifi/radio_test/multi_domain/README.rst similarity index 60% rename from samples/wifi/radio_test/README.rst rename to samples/wifi/radio_test/multi_domain/README.rst index 1b5458a07559..cdb10533393e 100644 --- a/samples/wifi/radio_test/README.rst +++ b/samples/wifi/radio_test/multi_domain/README.rst @@ -1,9 +1,10 @@ .. _wifi_radio_test: -Wi-Fi: Radio test -################# +Wi-Fi: Radio test (Multi domain) +################################ The Radio test sample supports the Radio test subcommands and subcommands to program the Factory Information Configuration Registers (FICR) fields defined in the nRF7002 one-time programmable (OTP) memory. +The sample can be run on multiple domains, for example, Wi-Fi® only on the application core or Wi-Fi and Bluetooth® combination mode, where Wi-Fi runs on the application core and Bluetooth runs on the network core. See the subpages for detailed documentation on the sample and its features. diff --git a/samples/wifi/radio_test/boards/thingy91x_nrf9151_ns.overlay b/samples/wifi/radio_test/multi_domain/boards/thingy91x_nrf9151_ns.overlay similarity index 100% rename from samples/wifi/radio_test/boards/thingy91x_nrf9151_ns.overlay rename to samples/wifi/radio_test/multi_domain/boards/thingy91x_nrf9151_ns.overlay diff --git a/samples/wifi/radio_test/ficr.rst b/samples/wifi/radio_test/multi_domain/ficr.rst similarity index 100% rename from samples/wifi/radio_test/ficr.rst rename to samples/wifi/radio_test/multi_domain/ficr.rst diff --git a/samples/wifi/radio_test/inc/nrf_wifi_radio_test_shell.h b/samples/wifi/radio_test/multi_domain/inc/nrf_wifi_radio_test_shell.h similarity index 100% rename from samples/wifi/radio_test/inc/nrf_wifi_radio_test_shell.h rename to samples/wifi/radio_test/multi_domain/inc/nrf_wifi_radio_test_shell.h diff --git a/samples/wifi/radio_test/prj.conf b/samples/wifi/radio_test/multi_domain/prj.conf similarity index 100% rename from samples/wifi/radio_test/prj.conf rename to samples/wifi/radio_test/multi_domain/prj.conf diff --git a/samples/wifi/radio_test/radio_test_subcommands.rst b/samples/wifi/radio_test/multi_domain/radio_test_subcommands.rst similarity index 100% rename from samples/wifi/radio_test/radio_test_subcommands.rst rename to samples/wifi/radio_test/multi_domain/radio_test_subcommands.rst diff --git a/samples/wifi/radio_test/sample.yaml b/samples/wifi/radio_test/multi_domain/sample.yaml similarity index 96% rename from samples/wifi/radio_test/sample.yaml rename to samples/wifi/radio_test/multi_domain/sample.yaml index d8285a70f996..76c1843c9c2a 100644 --- a/samples/wifi/radio_test/sample.yaml +++ b/samples/wifi/radio_test/multi_domain/sample.yaml @@ -63,7 +63,7 @@ tests: sample.nrf7002_eb.thingy53.radio_test: sysbuild: true build_only: true - extra_args: radio_test_SHIELD=nrf7002eb + extra_args: multi_domain_SHIELD=nrf7002eb integration_platforms: - thingy53/nrf5340/cpuapp platform_allow: thingy53/nrf5340/cpuapp @@ -78,7 +78,7 @@ tests: build_only: true extra_args: - FILE_SUFFIX=wifi_combo - - radio_test_SHIELD="nrf7002eb_interposer_p1;nrf7002eb" + - multi_domain_SHIELD="nrf7002eb_interposer_p1;nrf7002eb" - SNIPPET=nrf70-wifi integration_platforms: - nrf54h20dk/nrf54h20/cpuapp diff --git a/samples/wifi/radio_test/sample_description.rst b/samples/wifi/radio_test/multi_domain/sample_description.rst similarity index 100% rename from samples/wifi/radio_test/sample_description.rst rename to samples/wifi/radio_test/multi_domain/sample_description.rst diff --git a/samples/wifi/radio_test/src/ficr_prog.c b/samples/wifi/radio_test/multi_domain/src/ficr_prog.c similarity index 100% rename from samples/wifi/radio_test/src/ficr_prog.c rename to samples/wifi/radio_test/multi_domain/src/ficr_prog.c diff --git a/samples/wifi/radio_test/src/ficr_prog.h b/samples/wifi/radio_test/multi_domain/src/ficr_prog.h similarity index 100% rename from samples/wifi/radio_test/src/ficr_prog.h rename to samples/wifi/radio_test/multi_domain/src/ficr_prog.h diff --git a/samples/wifi/radio_test/src/nrf_wifi_radio_ficr_shell.c b/samples/wifi/radio_test/multi_domain/src/nrf_wifi_radio_ficr_shell.c similarity index 100% rename from samples/wifi/radio_test/src/nrf_wifi_radio_ficr_shell.c rename to samples/wifi/radio_test/multi_domain/src/nrf_wifi_radio_ficr_shell.c diff --git a/samples/wifi/radio_test/src/nrf_wifi_radio_test_main.c b/samples/wifi/radio_test/multi_domain/src/nrf_wifi_radio_test_main.c similarity index 100% rename from samples/wifi/radio_test/src/nrf_wifi_radio_test_main.c rename to samples/wifi/radio_test/multi_domain/src/nrf_wifi_radio_test_main.c diff --git a/samples/wifi/radio_test/src/nrf_wifi_radio_test_shell.c b/samples/wifi/radio_test/multi_domain/src/nrf_wifi_radio_test_shell.c similarity index 100% rename from samples/wifi/radio_test/src/nrf_wifi_radio_test_shell.c rename to samples/wifi/radio_test/multi_domain/src/nrf_wifi_radio_test_shell.c diff --git a/samples/wifi/radio_test/sysbuild.conf b/samples/wifi/radio_test/multi_domain/sysbuild.conf similarity index 100% rename from samples/wifi/radio_test/sysbuild.conf rename to samples/wifi/radio_test/multi_domain/sysbuild.conf diff --git a/samples/wifi/radio_test/single_domain/CMakeLists.txt b/samples/wifi/radio_test/single_domain/CMakeLists.txt new file mode 100644 index 000000000000..dd48607de173 --- /dev/null +++ b/samples/wifi/radio_test/single_domain/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(wifi_ble_radio_test) + +target_include_directories(app PRIVATE + inc +) + +# Application sources +FILE(GLOB app_sources src/*.c) + +# NORDIC SDK APP START +target_sources(app PRIVATE + ${app_sources} +) +# NORDIC SDK APP END diff --git a/samples/wifi/radio_test/single_domain/Kconfig b/samples/wifi/radio_test/single_domain/Kconfig new file mode 100644 index 000000000000..fd58c97206a7 --- /dev/null +++ b/samples/wifi/radio_test/single_domain/Kconfig @@ -0,0 +1,27 @@ +# +# Copyright (c) 2024 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +mainmenu "Nordic Wi-Fi Bluetooth LE Radio test sample" + +config RADIO_TEST_SD_POWER_CONTROL_AUTOMATIC + bool "Automatic power control" + depends on FEM + default y + help + Sets SoC output power and Front-End Module (FEM) gain to achieve TX output power requested + by the user. If the exact value cannot be achieved, power is set to the closest value that does + not exceed the limits. If this option is disabled, the user has to set SoC output power and + FEM gain with separate commands. + +config RADIO_TEST_SD_RX_TIMEOUT + int "RX packet reception timeout" + default 10 + help + Specifies the time in seconds that the application waits for the first packet to be + received in RX mode when a specified number of packets are set to be received. + If the timeout is reached before the first packet is received, the radio will be disabled. + +source "Kconfig.zephyr" diff --git a/samples/wifi/radio_test/single_domain/README.rst b/samples/wifi/radio_test/single_domain/README.rst new file mode 100644 index 000000000000..b4cc3bfed39d --- /dev/null +++ b/samples/wifi/radio_test/single_domain/README.rst @@ -0,0 +1,16 @@ +.. _wifi_radio_test_sd: + +Wi-Fi: Wi-Fi Bluetooth LE Radio test (Single domain) +#################################################### + +The Wi-Fi Bluetooth LE Radio test sample demonstrates how to use the radio test for both Wi-Fi® and Bluetooth® LE protocols using a single domain image, that is, both running on the same core (application core). +The sample shows how to use the radio test subcommands to configure the parameters and run the radio test. + +The Wi-Fi Radio test also supports programming the Factory Information Configuration Registers (FICR) fields defined in the nRF7002 one-time programmable (OTP) memory. + +See the below links for detailed documentation on the sample and its features. + +* Sample description: :ref:`wifi_radio_sample_desc` +* Peripheral radio test: :ref:`radio_test` +* Radio test subcommands: :ref:`wifi_radio_subcommands` +* FICR programming subcommands: :ref:`wifi_ficr_prog` diff --git a/samples/wifi/radio_test/single_domain/boards/nrf54l15dk_nrf54l15_cpuapp.conf b/samples/wifi/radio_test/single_domain/boards/nrf54l15dk_nrf54l15_cpuapp.conf new file mode 100644 index 000000000000..e029d65ce7bf --- /dev/null +++ b/samples/wifi/radio_test/single_domain/boards/nrf54l15dk_nrf54l15_cpuapp.conf @@ -0,0 +1,12 @@ +# +# Copyright (c) 2024 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Disable the unsupported driver +CONFIG_NRFX_TIMER0=n + +# Enable the necessary drivers +CONFIG_NRFX_TIMER10=y +CONFIG_NRFX_GPPI=y diff --git a/samples/wifi/radio_test/single_domain/inc/nrf_wifi_radio_test_shell.h b/samples/wifi/radio_test/single_domain/inc/nrf_wifi_radio_test_shell.h new file mode 100644 index 000000000000..6105c91cf456 --- /dev/null +++ b/samples/wifi/radio_test/single_domain/inc/nrf_wifi_radio_test_shell.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/* @file + * @brief nRF Wi-Fi radio-test mode shell module + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct nrf_wifi_ctx_zep_rt { + struct nrf_wifi_fmac_priv *fmac_priv; + struct rpu_conf_params conf_params; +}; diff --git a/samples/wifi/radio_test/single_domain/prj.conf b/samples/wifi/radio_test/single_domain/prj.conf new file mode 100644 index 000000000000..0a74f343e738 --- /dev/null +++ b/samples/wifi/radio_test/single_domain/prj.conf @@ -0,0 +1,52 @@ +# +# Copyright (c) 2022 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# +CONFIG_WIFI=y +CONFIG_WIFI_NRF70=y +CONFIG_NRF70_RADIO_TEST=y + +#CONFIG_INIT_STACKS=y + +# Memories +CONFIG_MAIN_STACK_SIZE=5200 +CONFIG_SHELL_STACK_SIZE=5200 +#64K memory needed for IQ sample captures. +CONFIG_HEAP_MEM_POOL_SIZE=98304 +CONFIG_HEAP_MEM_POOL_IGNORE_MIN=y + + +# Debugging +CONFIG_STACK_SENTINEL=y +CONFIG_DEBUG_COREDUMP=y +CONFIG_DEBUG_COREDUMP_BACKEND_LOGGING=y +CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_MIN=y +CONFIG_SHELL_CMDS_RESIZE=n +#CONFIG_DEBUG=y + +# Logging +CONFIG_LOG=y +CONFIG_PRINTK=y +CONFIG_SHELL=y +CONFIG_SHELL_GETOPT=y +CONFIG_DEVICE_SHELL=y +CONFIG_POSIX_CLOCK=y +CONFIG_DATE_SHELL=y +CONFIG_LOG_BUFFER_SIZE=4096 +CONFIG_SHELL_BACKEND_SERIAL_RX_RING_BUFFER_SIZE=1024 + +# Peripheral radio test configuration +CONFIG_CONSOLE_HANDLER=y +CONFIG_SHELL=y + +CONFIG_DYNAMIC_INTERRUPTS=y + +CONFIG_NRFX_TIMER0=y +CONFIG_CLOCK_CONTROL=y + +CONFIG_ENTROPY_GENERATOR=y +CONFIG_NRF_SECURITY=y + +CONFIG_FEM_AL_LIB=y +CONFIG_PICOLIBC_IO_FLOAT=y diff --git a/samples/wifi/radio_test/single_domain/sample.yaml b/samples/wifi/radio_test/single_domain/sample.yaml new file mode 100644 index 000000000000..647872f8480f --- /dev/null +++ b/samples/wifi/radio_test/single_domain/sample.yaml @@ -0,0 +1,15 @@ +sample: + description: Wi-Fi BLE radio test sample single domain + name: Wi-Fi BLE radio test Single Domain +tests: + sample.nrf70.wifi_ble_radio_test_sd: + sysbuild: true + build_only: true + extra_args: + - single_domain_SHIELD="nrf7002eb_interposer_p1;nrf7002eb" + - single_domain_SNIPPET=nrf70-wifi + integration_platforms: + - nrf54l15dk/nrf54l15/cpuapp + platform_allow: + - nrf54l15dk/nrf54l15/cpuapp + tags: ci_build sysbuild ci_samples_wifi diff --git a/samples/wifi/radio_test/single_domain/src/ficr_prog.c b/samples/wifi/radio_test/single_domain/src/ficr_prog.c new file mode 100644 index 000000000000..58fd4dcf0cb4 --- /dev/null +++ b/samples/wifi/radio_test/single_domain/src/ficr_prog.c @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/* @file + * @brief NRF Wi-Fi radio FICR programming functions + */ + +#include +#include +#include + +#include "rpu_if.h" +#include "ficr_prog.h" + + +LOG_MODULE_DECLARE(otp_prog, CONFIG_WIFI_LOG_LEVEL); + +static void write_word(unsigned int addr, unsigned int data) +{ + rpu_write(addr, &data, 4); +} + +static void read_word(unsigned int addr, unsigned int *data) +{ + rpu_read(addr, data, 4); +} + +unsigned int check_protection(unsigned int *buff, unsigned int off1, unsigned int off2, + unsigned int off3, unsigned int off4) +{ + if ((buff[off1] == OTP_PROGRAMMED) && + (buff[off2] == OTP_PROGRAMMED) && + (buff[off3] == OTP_PROGRAMMED) && + (buff[off4] == OTP_PROGRAMMED)) + return OTP_PROGRAMMED; + else if ((buff[off1] == OTP_FRESH_FROM_FAB) && + (buff[off2] == OTP_FRESH_FROM_FAB) && + (buff[off3] == OTP_FRESH_FROM_FAB) && + (buff[off4] == OTP_FRESH_FROM_FAB)) + return OTP_FRESH_FROM_FAB; + else if ((buff[off1] == OTP_ENABLE_PATTERN) && + (buff[off2] == OTP_ENABLE_PATTERN) && + (buff[off3] == OTP_ENABLE_PATTERN) && + (buff[off4] == OTP_ENABLE_PATTERN)) + return OTP_ENABLE_PATTERN; + else + return OTP_INVALID; +} + + +static void set_otp_timing_reg_40mhz(void) +{ + write_word(OTP_TIMING_REG1_ADDR, OTP_TIMING_REG1_VAL); + write_word(OTP_TIMING_REG2_ADDR, OTP_TIMING_REG2_VAL); +} + +static int poll_otp_ready(void) +{ + int otp_mem_status = 0; + int poll = 0; + + while (poll != 100) { + read_word(OTP_POLL_ADDR, &otp_mem_status); + + if ((otp_mem_status & OTP_READY) == OTP_READY) { + return 0; + } + poll++; + } + LOG_ERR("OTP is not ready"); + return -ENOEXEC; +} + + +static int req_otp_standby_mode(void) +{ + write_word(OTP_RWSBMODE_ADDR, 0x0); + return poll_otp_ready(); +} + + +static int otp_wr_voltage_2V5(void) +{ + int err; + + err = req_otp_standby_mode(); + + if (err) { + LOG_ERR("Failed Setting OTP voltage IOVDD to 2.5V"); + return -ENOEXEC; + } + write_word(OTP_VOLTCTRL_ADDR, OTP_VOLTCTRL_2V5); + return 0; +} + +static int poll_otp_read_valid(void) +{ + int otp_mem_status = 0; + int poll = 0; + + while (poll < 100) { + read_word(OTP_POLL_ADDR, &otp_mem_status); + + if ((otp_mem_status & OTP_READ_VALID) == OTP_READ_VALID) { + return 0; + } + poll++; + } + LOG_ERR("%s: OTP read failed", __func__); + return -ENOEXEC; +} + +static int poll_otp_wrdone(void) +{ + int otp_mem_status = 0; + int poll = 0; + + while (poll < 100) { + read_word(OTP_POLL_ADDR, &otp_mem_status); + + if ((otp_mem_status & OTP_WR_DONE) == OTP_WR_DONE) { + return 0; + } + poll++; + } + LOG_ERR("%s: OTP write done failed", __func__); + return -ENOEXEC; +} + +static int req_otp_read_mode(void) +{ + write_word(OTP_RWSBMODE_ADDR, OTP_READ_MODE); + return poll_otp_ready(); +} + + +static int req_otp_byte_write_mode(void) +{ + write_word(OTP_RWSBMODE_ADDR, OTP_BYTE_WRITE_MODE); + return poll_otp_ready(); +} + +static unsigned int read_otp_location(unsigned int offset, unsigned int *read_val) +{ + int err; + + write_word(OTP_RDENABLE_ADDR, offset); + err = poll_otp_read_valid(); + if (err) { + LOG_ERR("OTP read failed"); + return err; + } + read_word(OTP_READREG_ADDR, read_val); + + return 0; +} + +static int write_otp_location(unsigned int otp_location_offset, unsigned int otp_data) +{ + write_word(OTP_WRENABLE_ADDR, otp_location_offset); + write_word(OTP_WRITEREG_ADDR, otp_data); + + return poll_otp_wrdone(); +} + + +static int otp_rd_voltage_1V8(void) +{ + int err; + + err = req_otp_standby_mode(); + if (err) { + LOG_ERR("error in %s", __func__); + return err; + } + write_word(OTP_VOLTCTRL_ADDR, OTP_VOLTCTRL_1V8); + + return 0; +} + +static int update_mac_addr(unsigned int index, unsigned int *write_val) +{ + int ret = 0; + + for (int i = 0; i < 2; i++) { + ret = write_otp_location(MAC0_ADDR + 2 * index + i, write_val[i]); + if (ret == -ENOEXEC) { + LOG_ERR("FICR: Failed to update MAC ADDR%d", index); + break; + } + LOG_INF("mac addr %d : Reg%d (0x%x) = 0x%04x", + index, (i+1), (MAC0_ADDR + i) << 2, write_val[i]); + } + return ret; +} + +int write_otp_memory(unsigned int otp_addr, unsigned int *write_val) +{ + int err = 0; + int mask_val; + int ret = 0; + int retrim_loc = 0; + + err = poll_otp_ready(); + if (err) { + LOG_ERR("err in otp ready poll"); + return err; + } + + set_otp_timing_reg_40mhz(); + + err = otp_wr_voltage_2V5(); + if (err) { + LOG_ERR("error in write_voltage 2V5"); + goto _exit_otp_write; + } + + err = req_otp_byte_write_mode(); + if (err) { + LOG_ERR("error in OTP byte write mode"); + goto _exit_otp_write; + } + + switch (otp_addr) { + case REGION_PROTECT: + write_otp_location(REGION_PROTECT, write_val[0]); + write_otp_location(REGION_PROTECT+1, write_val[0]); + write_otp_location(REGION_PROTECT+2, write_val[0]); + write_otp_location(REGION_PROTECT+3, write_val[0]); + + LOG_INF("Written REGION_PROTECT0 (0x%x) : 0x%04x", + (REGION_PROTECT << 2), write_val[0]); + LOG_INF("Written REGION_PROTECT1 (0x%x) : 0x%04x", + (REGION_PROTECT+1) << 2, write_val[0]); + LOG_INF("Written REGION_PROTECT2 (0x%x) : 0x%04x", + (REGION_PROTECT+2) << 2, write_val[0]); + LOG_INF("Written REGION_PROTECT3 (0x%x) : 0x%04x", + (REGION_PROTECT+3) << 2, write_val[0]); + break; + case QSPI_KEY: + mask_val = QSPI_KEY_FLAG_MASK; + for (int i = 0; i < QSPI_KEY_LENGTH_BYTES / 4; i++) { + ret = write_otp_location(QSPI_KEY + i, write_val[i]); + if (ret == -ENOEXEC) { + LOG_ERR("FICR: Failed to write QSPI key offset-%d", QSPI_KEY + i); + goto _exit_otp_write; + } + LOG_INF("Written QSPI_KEY0 (0x%x) : 0x%04x", + (QSPI_KEY + i) << 2, write_val[i]); + } + write_otp_location(REGION_DEFAULTS, mask_val); + LOG_INF("Written REGION_DEFAULTS (0x%x) : 0x%04x", + (REGION_DEFAULTS) << 2, mask_val); + break; + case MAC0_ADDR: + mask_val = MAC0_ADDR_FLAG_MASK; + ret = update_mac_addr(0, write_val); + if (ret == -ENOEXEC) { + goto _exit_otp_write; + } + + write_otp_location(REGION_DEFAULTS, mask_val); + LOG_INF("Written MAC address 0"); + LOG_INF("Written REGION_DEFAULTS (0x%x) : 0x%04x", + (REGION_DEFAULTS) << 2, mask_val); + break; + case MAC1_ADDR: + mask_val = MAC1_ADDR_FLAG_MASK; + ret = update_mac_addr(1, write_val); + if (ret == -ENOEXEC) { + goto _exit_otp_write; + } + write_otp_location(REGION_DEFAULTS, mask_val); + LOG_INF("Written MAC address 1"); + LOG_INF("Written REGION_DEFAULTS (0x%x) : 0x%04x", + (REGION_DEFAULTS) << 2, mask_val); + break; + case CALIB_XO: + mask_val = CALIB_XO_FLAG_MASK; + ret = write_otp_location(CALIB_XO, write_val[0]); + + if (ret == -ENOEXEC) { + LOG_ERR("XO_Update Exception"); + goto _exit_otp_write; + } else { + write_otp_location(REGION_DEFAULTS, mask_val); + + LOG_INF("Written CALIB_XO (0x%x) to 0x%04x", + CALIB_XO << 2, write_val[0]); + LOG_INF("Written REGION_DEFAULTS (0x%x) : 0x%04x", + (REGION_DEFAULTS) << 2, mask_val); + } + break; + case PRODRETEST_PROGVERSION: + ret = write_otp_location(PRODRETEST_PROGVERSION, *write_val); + + if (ret == -ENOEXEC) { + LOG_ERR("PRODRETEST.PROGVERSION_Update Exception"); + goto _exit_otp_write; + } else { + LOG_INF("Written PRODRETEST.PROGVERSION 0x%04x", *write_val); + } + break; + case PRODRETEST_TRIM0: + case PRODRETEST_TRIM1: + case PRODRETEST_TRIM2: + case PRODRETEST_TRIM3: + case PRODRETEST_TRIM4: + case PRODRETEST_TRIM5: + case PRODRETEST_TRIM6: + case PRODRETEST_TRIM7: + case PRODRETEST_TRIM8: + case PRODRETEST_TRIM9: + case PRODRETEST_TRIM10: + case PRODRETEST_TRIM11: + case PRODRETEST_TRIM12: + case PRODRETEST_TRIM13: + case PRODRETEST_TRIM14: + retrim_loc = otp_addr - PRODRETEST_TRIM0; + ret = write_otp_location(otp_addr, *write_val); + + if (ret == -ENOEXEC) { + LOG_ERR("PRODRETEST.TRIM_Update Exception"); + goto _exit_otp_write; + } else { + LOG_INF("Written PRODRETEST.TRIM%d 0x%04x", + retrim_loc, *write_val); + } + break; + case REGION_DEFAULTS: + write_otp_location(REGION_DEFAULTS, write_val[0]); + + LOG_INF("Written REGION_DEFAULTS (0x%x) to 0x%04x", + REGION_DEFAULTS << 2, write_val[0]); + break; + default: + LOG_ERR("unknown field received: %d", otp_addr); + + } + return ret; + +_exit_otp_write: + err = req_otp_standby_mode(); + err |= otp_rd_voltage_1V8(); + return err; +} + +int read_otp_memory(unsigned int otp_addr, unsigned int *read_val, int len) +{ + int err; + + err = poll_otp_ready(); + if (err) { + LOG_ERR("err in otp ready poll"); + return -ENOEXEC; + } + + set_otp_timing_reg_40mhz(); + + err = otp_rd_voltage_1V8(); + if (err) { + LOG_ERR("error in read_voltage 1V8"); + return -ENOEXEC; + } + + err = req_otp_read_mode(); + if (err) { + LOG_ERR("error in req_otp_read_mode()"); + return -ENOEXEC; + } + + for (int i = 0; i < len; i++) { + read_otp_location(otp_addr + i, &read_val[i]); + } + + return req_otp_standby_mode(); +} diff --git a/samples/wifi/radio_test/single_domain/src/ficr_prog.h b/samples/wifi/radio_test/single_domain/src/ficr_prog.h new file mode 100644 index 000000000000..a9c3eb424841 --- /dev/null +++ b/samples/wifi/radio_test/single_domain/src/ficr_prog.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/** + * @brief Header containing address/offets and functions for writing + * the FICR fields of the OTP memory on nRF7002 device + */ + +#ifndef __OTP_PROG_H_ +#define __OTP_PROG_H_ + +#include +#include + +int write_otp_memory(unsigned int otp_addr, unsigned int *write_val); +int read_otp_memory(unsigned int otp_addr, unsigned int *read_val, int len); +unsigned int check_protection(unsigned int *buff, unsigned int off1, unsigned int off2, + unsigned int off3, unsigned int off4); + +#endif /* __OTP_PROG_H_ */ diff --git a/samples/wifi/radio_test/single_domain/src/main.c b/samples/wifi/radio_test/single_domain/src/main.c new file mode 100644 index 000000000000..c5a498383692 --- /dev/null +++ b/samples/wifi/radio_test/single_domain/src/main.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#if defined(NRF54L15_XXAA) +#include +#endif /* defined(NRF54L15_XXAA) */ +#if defined(CONFIG_CLOCK_CONTROL_NRF2) +#include +#endif + +#if defined(CONFIG_CLOCK_CONTROL_NRF) +static void clock_init(void) +{ + int err; + int res; + struct onoff_manager *clk_mgr; + struct onoff_client clk_cli; + + clk_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF); + if (!clk_mgr) { + printk("Unable to get the Clock manager\n"); + return; + } + + sys_notify_init_spinwait(&clk_cli.notify); + + err = onoff_request(clk_mgr, &clk_cli); + if (err < 0) { + printk("Clock request failed: %d\n", err); + return; + } + + do { + err = sys_notify_fetch_result(&clk_cli.notify, &res); + if (!err && res) { + printk("Clock could not be started: %d\n", res); + return; + } + } while (err); + +#if defined(NRF54L15_XXAA) + /* MLTPAN-20 */ + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_PLLSTART); +#endif /* defined(NRF54L15_XXAA) */ + + printk("Clock has started\n"); +} + +#elif defined(CONFIG_CLOCK_CONTROL_NRF2) + +static void clock_init(void) +{ + int err; + int res; + const struct device *radio_clk_dev = + DEVICE_DT_GET_OR_NULL(DT_CLOCKS_CTLR(DT_NODELABEL(radio))); + struct onoff_client radio_cli; + + /** Keep radio domain powered all the time to reduce latency. */ + nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_1, true); + + sys_notify_init_spinwait(&radio_cli.notify); + + err = nrf_clock_control_request(radio_clk_dev, NULL, &radio_cli); + + do { + err = sys_notify_fetch_result(&radio_cli.notify, &res); + if (!err && res) { + printk("Clock could not be started: %d\n", res); + return; + } + } while (err == -EAGAIN); + +#if defined(NRF54L15_XXAA) + /* MLTPAN-20 */ + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_PLLSTART); +#endif /* defined(NRF54L15_XXAA) */ + + printk("Clock has started\n"); +} + +#else +BUILD_ASSERT(false, "No Clock Control driver"); +#endif /* defined(CONFIG_CLOCK_CONTROL_NRF) */ + +int main(void) +{ + printk("Starting Radio Test example\n"); + + clock_init(); + +#if defined(CONFIG_SOC_SERIES_NRF54HX) + /* Apply HMPAN-102 workaround for nRF54H series */ + *(volatile uint32_t *)0x5302C7E4 = + (((*((volatile uint32_t *)0x5302C7E4)) & 0xFF000FFF) | 0x0012C000); +#endif + + return 0; +} diff --git a/samples/wifi/radio_test/single_domain/src/nrf_wifi_radio_ficr_shell.c b/samples/wifi/radio_test/single_domain/src/nrf_wifi_radio_ficr_shell.c new file mode 100644 index 000000000000..6581718ecff7 --- /dev/null +++ b/samples/wifi/radio_test/single_domain/src/nrf_wifi_radio_ficr_shell.c @@ -0,0 +1,528 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/* @file + * @brief NRF Wi-Fi radio test shell module + */ + +#include +#include +#include +#include +#include "ficr_prog.h" + +#include "fmac_api_common.h" + +extern struct nrf_wifi_drv_priv_zep rpu_drv_priv_zep; +static struct nrf_wifi_ctx_zep *ctx = &rpu_drv_priv_zep.rpu_ctx_zep; + +LOG_MODULE_REGISTER(otp_prog, CONFIG_WIFI_LOG_LEVEL); + +static void disp_location_status(const struct shell *shell, char *region, unsigned int ret) +{ + switch (ret) { + + case OTP_PROGRAMMED: + shell_fprintf(shell, SHELL_INFO, "%s Region is locked!!!\n", region); + break; + case OTP_ENABLE_PATTERN: + shell_fprintf(shell, SHELL_INFO, "%s Region is open for R/W\n", region); + break; + case OTP_FRESH_FROM_FAB: + shell_fprintf(shell, SHELL_INFO, + "%s Region is unprogrammed - program to enable R/W\n", region); + break; + default: + shell_fprintf(shell, SHELL_ERROR, "%s Region is in invalid state\n", region); + break; + } + shell_fprintf(shell, SHELL_INFO, "\n"); +} + +static void disp_fields_status(const struct shell *shell, unsigned int flags) +{ + if (flags & (~QSPI_KEY_FLAG_MASK)) { + shell_fprintf(shell, SHELL_INFO, "QSPI Keys are not programmed in OTP\n"); + } else { + shell_fprintf(shell, SHELL_INFO, "QSPI Keys are programmed in OTP\n"); + } + + if (flags & (~MAC0_ADDR_FLAG_MASK)) { + shell_fprintf(shell, SHELL_INFO, "MAC0 Address are not programmed in OTP\n"); + } else { + shell_fprintf(shell, SHELL_INFO, "MAC0 Address is programmed in OTP\n"); + } + + if (flags & (~MAC1_ADDR_FLAG_MASK)) { + shell_fprintf(shell, SHELL_INFO, "MAC1 Address are not programmed in OTP\n"); + } else { + shell_fprintf(shell, SHELL_INFO, "MAC1 Address is programmed in OTP\n"); + } + + if (flags & (~CALIB_XO_FLAG_MASK)) { + shell_fprintf(shell, SHELL_INFO, "CALIB_XO is not programmed in OTP\n"); + } else { + shell_fprintf(shell, SHELL_INFO, "CALIB_XO is programmed in OTP\n"); + } +} + + +static int nrf_wifi_radio_test_otp_get_status(const struct shell *shell, + size_t argc, + char *argv[]) +{ + unsigned int ret, err; + unsigned int val[OTP_MAX_WORD_LEN]; + + /* read all the OTP memory */ + err = read_otp_memory(0, &val[0], OTP_MAX_WORD_LEN); + if (err) { + shell_fprintf(shell, SHELL_ERROR, "FAILED reading otp memory......\n"); + return -ENOEXEC; + } + + shell_fprintf(shell, SHELL_INFO, "Checking OTP PROTECT Region......\n"); + ret = check_protection(&val[0], REGION_PROTECT, REGION_PROTECT + 1, + REGION_PROTECT + 2, REGION_PROTECT + 3); + + disp_location_status(shell, "OTP", ret); + disp_fields_status(shell, val[REGION_DEFAULTS]); + + return 0; +} + +static int nrf_wifi_radio_test_otp_read_params(const struct shell *shell, + size_t argc, + char *argv[]) +{ + unsigned int val[OTP_MAX_WORD_LEN]; + unsigned int ret, err; + + err = read_otp_memory(0, &val[0], OTP_MAX_WORD_LEN); + if (err) { + shell_fprintf(shell, SHELL_ERROR, "FAILED reading otp memory......\n"); + return -ENOEXEC; + } + + ret = check_protection(&val[0], REGION_PROTECT + 0, REGION_PROTECT + 1, + REGION_PROTECT + 2, REGION_PROTECT + 3); + disp_location_status(shell, "OTP", ret); + + shell_fprintf(shell, SHELL_INFO, "PRODTEST.FT.PROGVERSION = 0x%08x\n", + val[PRODTEST_FT_PROGVERSION]); + shell_fprintf(shell, SHELL_INFO, "\n"); + + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM0 = 0x%08x\n", val[PRODTEST_TRIM0]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM1 = 0x%08x\n", val[PRODTEST_TRIM1]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM2 = 0x%08x\n", val[PRODTEST_TRIM2]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM3 = 0x%08x\n", val[PRODTEST_TRIM3]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM4 = 0x%08x\n", val[PRODTEST_TRIM4]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM5 = 0x%08x\n", val[PRODTEST_TRIM5]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM6 = 0x%08x\n", val[PRODTEST_TRIM6]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM7 = 0x%08x\n", val[PRODTEST_TRIM7]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM8 = 0x%08x\n", val[PRODTEST_TRIM8]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM9 = 0x%08x\n", val[PRODTEST_TRIM9]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM10 = 0x%08x\n", + val[PRODTEST_TRIM10]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM11 = 0x%08x\n", + val[PRODTEST_TRIM11]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM12 = 0x%08x\n", + val[PRODTEST_TRIM12]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM13 = 0x%08x\n", + val[PRODTEST_TRIM13]); + shell_fprintf(shell, SHELL_INFO, "PRODTEST.TRIM14 = 0x%08x\n", + val[PRODTEST_TRIM14]); + shell_fprintf(shell, SHELL_INFO, "\n"); + + shell_fprintf(shell, SHELL_INFO, "INFO.PART = 0x%08x\n", val[INFO_PART]); + shell_fprintf(shell, SHELL_INFO, "INFO.VARIANT = 0x%08x\n", val[INFO_VARIANT]); + shell_fprintf(shell, SHELL_INFO, "\n"); + + shell_fprintf(shell, SHELL_INFO, "INFO.UUID0 = 0x%08x\n", val[INFO_UUID + 0]); + shell_fprintf(shell, SHELL_INFO, "INFO.UUID1 = 0x%08x\n", val[INFO_UUID + 1]); + shell_fprintf(shell, SHELL_INFO, "INFO.UUID2 = 0x%08x\n", val[INFO_UUID + 2]); + shell_fprintf(shell, SHELL_INFO, "INFO.UUID3 = 0x%08x\n", val[INFO_UUID + 3]); + shell_fprintf(shell, SHELL_INFO, "\n"); + + shell_fprintf(shell, SHELL_INFO, "REGION.PROTECT0 = 0x%08x\n", val[REGION_PROTECT + 0]); + shell_fprintf(shell, SHELL_INFO, "REGION.PROTECT1 = 0x%08x\n", val[REGION_PROTECT + 1]); + shell_fprintf(shell, SHELL_INFO, "REGION.PROTECT2 = 0x%08x\n", val[REGION_PROTECT + 2]); + shell_fprintf(shell, SHELL_INFO, "REGION.PROTECT3 = 0x%08x\n", val[REGION_PROTECT + 3]); + shell_fprintf(shell, SHELL_INFO, "\n"); + + if (val[REGION_PROTECT + 0] != OTP_FRESH_FROM_FAB) { + shell_fprintf(shell, SHELL_INFO, "MAC0.ADDRESS0 = 0x%08x\n", val[MAC0_ADDR]); + shell_fprintf(shell, SHELL_INFO, "MAC0.ADDRESS1 = 0x%08x\n", val[MAC0_ADDR + 1]); + shell_fprintf(shell, SHELL_INFO, "MAC0.ADDRESS = %02x:%02x:%02x:%02x:%02x:%02x\n", + (uint8_t)(val[MAC0_ADDR]), (uint8_t)(val[MAC0_ADDR] >> 8), + (uint8_t)(val[MAC0_ADDR] >> 16), (uint8_t)(val[MAC0_ADDR] >> 24), + (uint8_t)(val[MAC0_ADDR + 1]), (uint8_t)(val[MAC0_ADDR + 1] >> 8)); + shell_fprintf(shell, SHELL_INFO, "\n"); + + shell_fprintf(shell, SHELL_INFO, "MAC1.ADDRESS0 = 0x%08x\n", val[MAC1_ADDR]); + shell_fprintf(shell, SHELL_INFO, "MAC1.ADDRESS1 = 0x%08x\n", val[MAC1_ADDR + 1]); + shell_fprintf(shell, SHELL_INFO, "MAC1.ADDRESS = %02x:%02x:%02x:%02x:%02x:%02x\n", + (uint8_t)(val[MAC1_ADDR]), (uint8_t)(val[MAC1_ADDR] >> 8), + (uint8_t)(val[MAC1_ADDR] >> 16), (uint8_t)(val[MAC1_ADDR] >> 24), + (uint8_t)(val[MAC1_ADDR + 1]), (uint8_t)(val[MAC1_ADDR + 1] >> 8)); + shell_fprintf(shell, SHELL_INFO, "\n"); + + shell_fprintf(shell, SHELL_INFO, "CALIB.XO = 0x%02x\n", val[CALIB_XO] & 0xFF); + + shell_fprintf(shell, SHELL_INFO, "REGION_DEFAULTS = 0x%08x\n", + val[REGION_DEFAULTS]); + shell_fprintf(shell, SHELL_INFO, "\n"); + } + return 0; +} + +static int nrf_wifi_radio_test_otp_read_retrim_version(const struct shell *shell, + size_t argc, + char *argv[]) +{ + unsigned int val[1]; + unsigned int err; + + err = read_otp_memory(REGION_PROTECT, &val[0], 1); + if (err) { + shell_fprintf(shell, SHELL_ERROR, "FAILED reading otp memory......\n"); + return -ENOEXEC; + } + + if (val[0] == OTP_FRESH_FROM_FAB) { + shell_fprintf(shell, SHELL_ERROR, + "Region is unprogrammed - program to enable R/W\n"); + } else { + err = read_otp_memory(PRODRETEST_PROGVERSION, &val[0], 1); + shell_fprintf(shell, SHELL_INFO, "\nPRODRETEST.PROGVERSION = 0x%08x\n", + val[0]); + shell_fprintf(shell, SHELL_INFO, "\n"); + } + return 0; +} + +static int nrf_wifi_radio_test_otp_read_retrim_params(const struct shell *shell, + size_t argc, + char *argv[]) +{ + unsigned int val[RETRIM_LEN]; + unsigned int err; + + err = read_otp_memory(REGION_PROTECT, val, 1); + if (err) { + shell_fprintf(shell, SHELL_ERROR, "FAILED reading otp memory......\n"); + return -ENOEXEC; + } + + if (val[0] == OTP_FRESH_FROM_FAB) { + shell_fprintf(shell, SHELL_ERROR, + "Region is unprogrammed - program to enable R/W\n"); + } else { + shell_fprintf(shell, SHELL_INFO, "\n"); + err = read_otp_memory(PRODRETEST_TRIM0, val, RETRIM_LEN); + for (int i = 0; i < RETRIM_LEN; i++) { + shell_fprintf(shell, SHELL_INFO, "PRODRETEST.TRIM%d = 0x%08x\n", i, val[i]); + } + shell_fprintf(shell, SHELL_INFO, "\n"); + } + + return 0; +} + +static int nrf_wifi_radio_test_otp_write_params(const struct shell *shell, + size_t argc, + char *argv[]) +{ + unsigned int field; + unsigned int write_val[20]; + unsigned int val[OTP_MAX_WORD_LEN]; + unsigned int ret, err; + int status = 0; + + if (argc < 2) { + shell_fprintf(shell, SHELL_ERROR, "invalid # of args : %d\n", argc); + return -ENOEXEC; + } + + field = strtoul(argv[1], NULL, 0); + /* Align to 32-bit word address */ + field >>= 2; + + if (field < REGION_PROTECT) { + shell_fprintf(shell, SHELL_ERROR, "INVALID Address 0x%x......\n", field << 2); + return -ENOEXEC; + } + + if ((field >= PRODRETEST_PROGVERSION) && (field <= PRODRETEST_TRIM14)) { + shell_fprintf(shell, SHELL_ERROR, "INVALID Address 0x%x......\n", field << 2); + return -ENOEXEC; + } + + err = read_otp_memory(REGION_PROTECT, &val[REGION_PROTECT], 4); + if (err) { + shell_fprintf(shell, SHELL_ERROR, "FAILED reading otp memory......\n"); + return -ENOEXEC; + } + + ret = check_protection(&val[0], REGION_PROTECT, REGION_PROTECT + 1, + REGION_PROTECT + 2, REGION_PROTECT + 3); + disp_location_status(shell, "OTP", ret); + if (ret != OTP_ENABLE_PATTERN && ret != OTP_FRESH_FROM_FAB) { + shell_fprintf(shell, SHELL_ERROR, "USER Region is not Writeable\n"); + return -ENOEXEC; + } + + switch (field) { + case REGION_PROTECT: + if (argc != 3) { + shell_fprintf(shell, SHELL_ERROR, + "invalid # of args for REGION_PROTECT (expected 3) : %d\n", + argc); + return -ENOEXEC; + } + write_val[0] = strtoul(argv[2], NULL, 0); + /* All consecutive 4 words of the REGION_PROTECT are written */ + write_otp_memory(REGION_PROTECT, &write_val[0]); + break; + case MAC0_ADDR: + case MAC1_ADDR: + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = ctx->rpu_ctx; + + if (!fmac_dev_ctx) { + shell_fprintf(shell, SHELL_ERROR, "Driver not initialized\n"); + return -ENOEXEC; + } + + if (argc != 4) { + shell_fprintf(shell, SHELL_ERROR, + "invalid # of args for MAC ADDR write (expected 4) : %d\n", + argc); + return -ENOEXEC; + } + write_val[0] = strtoul(argv[2], NULL, 0); + write_val[1] = strtoul(argv[3], NULL, 0) & 0xFFFF; + + if (!nrf_wifi_utils_is_mac_addr_valid((const char *)&write_val[0])) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid MAC address. MAC address cannot be all 0's, broadcast or multicast address\n"); + return -ENOEXEC; + } + + status = write_otp_memory(field, &write_val[0]); + break; + case CALIB_XO: + case REGION_DEFAULTS: + if (argc != 3) { + shell_fprintf(shell, SHELL_ERROR, + "invalid # of args for field %d (expected 3) : %d\n", field, argc); + return -ENOEXEC; + } + write_val[0] = strtoul(argv[2], NULL, 0); + status = write_otp_memory(field, &write_val[0]); + break; + case QSPI_KEY: + if (argc != 6) { + shell_fprintf(shell, SHELL_ERROR, + "invalid # of args for QSPI_KEY (expected 6) : %d\n", argc); + return -ENOEXEC; + } + write_val[0] = strtoul(argv[2], NULL, 0); + write_val[1] = strtoul(argv[3], NULL, 0); + write_val[2] = strtoul(argv[4], NULL, 0); + write_val[3] = strtoul(argv[5], NULL, 0); + /* All consecutive 4 words of the qspi keys are written now */ + status = write_otp_memory(QSPI_KEY, &write_val[0]); + break; + default: + shell_fprintf(shell, SHELL_ERROR, "unsupported field %d\n", field); + return -ENOEXEC; + } + + if (!status) { + shell_fprintf(shell, SHELL_INFO, "Finished Writing OTP params\n"); + } + + return 0; +} + +static int nrf_wifi_radio_test_otp_write_retrim_version(const struct shell *shell, + size_t argc, + char *argv[]) +{ + unsigned int field; + unsigned int write_val; + unsigned int val[OTP_MAX_WORD_LEN]; + unsigned int ret, err; + int status = 0; + + field = PRODRETEST_PROGVERSION; + + if (argc != 2) { + shell_fprintf(shell, SHELL_ERROR, + "invalid # of args for field %d (expected 2) : %d\n", field, argc); + return -ENOEXEC; + } + + err = read_otp_memory(REGION_PROTECT, &val[REGION_PROTECT], 4); + if (err) { + shell_fprintf(shell, SHELL_ERROR, "FAILED reading otp memory......\n"); + return -ENOEXEC; + } + + ret = check_protection(&val[0], REGION_PROTECT, REGION_PROTECT + 1, + REGION_PROTECT + 2, REGION_PROTECT + 3); + disp_location_status(shell, "OTP", ret); + if (ret != OTP_ENABLE_PATTERN && ret != OTP_FRESH_FROM_FAB) { + shell_fprintf(shell, SHELL_ERROR, "USER Region is not Writeable\n"); + return -ENOEXEC; + } + + write_val = strtoul(argv[1], NULL, 0); + status = write_otp_memory(field, &write_val); + + if (!status) { + shell_fprintf(shell, SHELL_INFO, "Finished Writing Retrim version\n"); + } + + return 0; +} + +static int nrf_wifi_radio_test_otp_write_retrim_params(const struct shell *shell, + size_t argc, + char *argv[]) +{ + unsigned int index; + unsigned int field; + unsigned int write_val; + unsigned int val[OTP_MAX_WORD_LEN]; + unsigned int ret, err; + int status = 0; + + index = strtoul(argv[1], NULL, 0); + field = index+PRODRETEST_TRIM0; + + if ((field < PRODRETEST_TRIM0) || (field > PRODRETEST_TRIM14)) { + shell_fprintf(shell, SHELL_ERROR, "INVALID Index %d......\n", index); + return -ENOEXEC; + } + + err = read_otp_memory(REGION_PROTECT, &val[REGION_PROTECT], 4); + if (err) { + shell_fprintf(shell, SHELL_ERROR, "FAILED reading otp memory......\n"); + return -ENOEXEC; + } + + ret = check_protection(&val[0], REGION_PROTECT, REGION_PROTECT + 1, + REGION_PROTECT + 2, REGION_PROTECT + 3); + disp_location_status(shell, "OTP", ret); + if (ret != OTP_ENABLE_PATTERN && ret != OTP_FRESH_FROM_FAB) { + shell_fprintf(shell, SHELL_ERROR, "USER Region is not Writeable\n"); + return -ENOEXEC; + } + + switch (field) { + case PRODRETEST_TRIM0: + case PRODRETEST_TRIM1: + case PRODRETEST_TRIM2: + case PRODRETEST_TRIM3: + case PRODRETEST_TRIM4: + case PRODRETEST_TRIM5: + case PRODRETEST_TRIM6: + case PRODRETEST_TRIM7: + case PRODRETEST_TRIM8: + case PRODRETEST_TRIM9: + case PRODRETEST_TRIM10: + case PRODRETEST_TRIM11: + case PRODRETEST_TRIM12: + case PRODRETEST_TRIM13: + case PRODRETEST_TRIM14: + if (argc != 3) { + shell_fprintf(shell, SHELL_ERROR, + "invalid # of args for field %d (expected 3) : %d\n", field, argc); + return -ENOEXEC; + } + write_val = strtoul(argv[2], NULL, 0); + status = write_otp_memory(field, &write_val); + break; + default: + shell_fprintf(shell, SHELL_ERROR, "unsupported field %d\n", field); + return -ENOEXEC; + } + + if (!status) { + shell_fprintf(shell, SHELL_INFO, "Finished Writing Retrim param\n"); + } + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE( + nrf_wifi_radio_otp_subcmds, + SHELL_CMD_ARG(otp_get_status, + NULL, + "Read OTP status", + nrf_wifi_radio_test_otp_get_status, + 1, + 0), + SHELL_CMD_ARG(otp_read_params, + NULL, + "Read User region status and information on programmed fields", + nrf_wifi_radio_test_otp_read_params, + 1, + 0), + SHELL_CMD_ARG(otp_read_retrim_version, + NULL, + "Read Retrim Version", + nrf_wifi_radio_test_otp_read_retrim_version, + 1, + 0), + SHELL_CMD_ARG(otp_read_retrim_params, + NULL, + "Read Retrim Params", + nrf_wifi_radio_test_otp_read_retrim_params, + 1, + 0), + SHELL_CMD_ARG(otp_write_params, + NULL, + "Write OTP Params\n" + "otp_write_params [arg1] [arg2]...[argN]", + nrf_wifi_radio_test_otp_write_params, + 2, + 16), + SHELL_CMD_ARG(otp_write_retrim_version, + NULL, + "Write OTP Retrim Version\n" + "otp_write_retrim_version ", + nrf_wifi_radio_test_otp_write_retrim_version, + 2, + 0), + SHELL_CMD_ARG(otp_write_retrim_params, + NULL, + "Write OTP Retrim Params\n" + "otp_write_params [retrim data]", + nrf_wifi_radio_test_otp_write_retrim_params, + 3, + 0), + SHELL_SUBCMD_SET_END); + + +SHELL_CMD_REGISTER(wifi_radio_ficr_prog, + &nrf_wifi_radio_otp_subcmds, + "nRF Wi-Fi radio FICR commands", + NULL); + + +static int nrf_wifi_radio_otp_shell_init(void) +{ + + return 0; +} + + +SYS_INIT(nrf_wifi_radio_otp_shell_init, + APPLICATION, + CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/samples/wifi/radio_test/single_domain/src/nrf_wifi_radio_test_shell.c b/samples/wifi/radio_test/single_domain/src/nrf_wifi_radio_test_shell.c new file mode 100644 index 000000000000..70a8e51520fc --- /dev/null +++ b/samples/wifi/radio_test/single_domain/src/nrf_wifi_radio_test_shell.c @@ -0,0 +1,2535 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/* @file + * @brief NRF Wi-Fi radio test shell module + */ + +#include +#include +#include +#include "fmac_api_common.h" + +extern struct nrf_wifi_drv_priv_zep rpu_drv_priv_zep; +struct nrf_wifi_ctx_zep *ctx = &rpu_drv_priv_zep.rpu_ctx_zep; + +#define NRF_WIFI_RADIO_TEST_INIT_TIMEOUT_MS 5000 + +static bool check_test_in_prog(const struct shell *shell) +{ + if (ctx->conf_params.rx) { + shell_fprintf(shell, + SHELL_ERROR, + "Disable RX\n"); + return false; + } + + if (ctx->conf_params.tx) { + shell_fprintf(shell, + SHELL_ERROR, + "Disable TX\n"); + return false; + } + + if (ctx->rf_test_run) { + shell_fprintf(shell, + SHELL_ERROR, + "Disable RF Test (%d)\n", + ctx->rf_test); + return false; + } + return true; +} + + +static bool check_valid_data_rate(const struct shell *shell, + unsigned char tput_mode, + unsigned int nss, + int dr) +{ + bool is_mcs = dr & 0x80; + bool ret = false; + + if (dr == -1) + return true; + + if (is_mcs) { + dr = dr & 0x7F; + + if (tput_mode & RPU_TPUT_MODE_HT) { + if (nss == 2) { + if ((dr >= 8) && (dr <= 15)) { + ret = true; + } else { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid MIMO HT MCS: %d\n", + dr); + } + } + + if (nss == 1) { + if ((dr >= 0) && (dr <= 7)) { + ret = true; + } else { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid SISO HT MCS: %d\n", + dr); + } + } + + } else if (tput_mode == RPU_TPUT_MODE_VHT) { + if ((dr >= 0 && dr <= 9)) { + ret = true; + } else { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid VHT MCS value: %d\n", + dr); + } + } else if (tput_mode == RPU_TPUT_MODE_HE_SU) { + if ((dr >= 0 && dr <= 7)) { + ret = true; + } else { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid HE_SU MCS value: %d\n", + dr); + } + } else if (tput_mode == RPU_TPUT_MODE_HE_ER_SU) { + if ((dr >= 0 && dr <= 2)) { + ret = true; + } else { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid HE_ER_SU MCS value: %d\n", + dr); + } + } else { + shell_fprintf(shell, + SHELL_ERROR, + "%s: Invalid throughput mode: %d\n", __func__, + dr); + } + } else { + if (tput_mode != RPU_TPUT_MODE_LEGACY) { + ret = false; + + shell_fprintf(shell, + SHELL_ERROR, + "Invalid rate_flags for legacy: %d\n", + dr); + } + + if ((dr == 1) || + (dr == 2) || + (dr == 55) || + (dr == 11) || + (dr == 6) || + (dr == 9) || + (dr == 12) || + (dr == 18) || + (dr == 24) || + (dr == 36) || + (dr == 48) || + (dr == 54) || + (dr == -1)) { + ret = true; + } else { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid Legacy Rate value: %d\n", + dr); + } + } + + return ret; +} + + +static bool check_valid_chan_2g(unsigned char chan_num) +{ + if ((chan_num >= 1) && (chan_num <= 14)) { + return true; + } + + return false; +} + + +#ifndef CONFIG_NRF70_2_4G_ONLY +static bool check_valid_chan_5g(unsigned char chan_num) +{ + if ((chan_num == 32) || + (chan_num == 36) || + (chan_num == 40) || + (chan_num == 44) || + (chan_num == 48) || + (chan_num == 52) || + (chan_num == 56) || + (chan_num == 60) || + (chan_num == 64) || + (chan_num == 68) || + (chan_num == 96) || + (chan_num == 100) || + (chan_num == 104) || + (chan_num == 108) || + (chan_num == 112) || + (chan_num == 116) || + (chan_num == 120) || + (chan_num == 124) || + (chan_num == 128) || + (chan_num == 132) || + (chan_num == 136) || + (chan_num == 140) || + (chan_num == 144) || + (chan_num == 149) || + (chan_num == 153) || + (chan_num == 157) || + (chan_num == 159) || + (chan_num == 161) || + (chan_num == 163) || + (chan_num == 165) || + (chan_num == 167) || + (chan_num == 169) || + (chan_num == 171) || + (chan_num == 173) || + (chan_num == 175) || + (chan_num == 177)) { + return true; + } + + return false; +} +#endif /* CONFIG_NRF70_2_4G_ONLY */ + + +static bool check_valid_channel(unsigned char chan_num) +{ + bool ret = false; + + ret = check_valid_chan_2g(chan_num); + + if (ret) { + goto out; + } + +#ifndef CONFIG_NRF70_2_4G_ONLY + ret = check_valid_chan_5g(chan_num); +#endif /* CONFIG_NRF70_2_4G_ONLY */ + +out: + return ret; +} + + +static int check_channel_settings(const struct shell *shell, + unsigned char tput_mode, + struct chan_params *chan) +{ + if (tput_mode == RPU_TPUT_MODE_LEGACY) { + if (chan->bw != RPU_CH_BW_20) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid bandwidth setting for legacy channel = %d\n", + chan->primary_num); + return -1; + } + } else if (tput_mode == RPU_TPUT_MODE_HT) { + if (chan->bw != RPU_CH_BW_20) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid bandwidth setting for HT channel = %d\n", + chan->primary_num); + return -1; + } + } else if (tput_mode == RPU_TPUT_MODE_VHT) { + if ((chan->primary_num >= 1) && + (chan->primary_num <= 14)) { + shell_fprintf(shell, + SHELL_ERROR, + "VHT setting not allowed in 2.4GHz band\n"); + return -1; + } + + if (chan->bw != RPU_CH_BW_20) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid bandwidth setting for VHT channel = %d\n", + chan->primary_num); + return -1; + } + } else if ((tput_mode == RPU_TPUT_MODE_HE_SU) || + (tput_mode == RPU_TPUT_MODE_HE_TB) || + (tput_mode == RPU_TPUT_MODE_HE_ER_SU)) { + if (chan->bw != RPU_CH_BW_20) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid bandwidth setting for HE channel = %d\n", + chan->primary_num); + return -1; + } + } else { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid throughput mode = %d\n", + tput_mode); + return -1; + } + + return 0; +} + + +enum nrf_wifi_status nrf_wifi_radio_test_conf_init(struct rpu_conf_params *conf_params) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + unsigned char country_code[NRF_WIFI_COUNTRY_CODE_LEN] = {0}; + + /* Check and save regulatory country code currently set */ + if (strlen(conf_params->country_code)) { + memcpy(country_code, + conf_params->country_code, + NRF_WIFI_COUNTRY_CODE_LEN); + } + + memset(conf_params, + 0, + sizeof(*conf_params)); + + /* Initialize values which are other than 0 */ + conf_params->op_mode = RPU_OP_MODE_RADIO_TEST; + + status = nrf_wifi_fmac_rf_params_get( + ctx->rpu_ctx, + (struct nrf_wifi_phy_rf_params *)conf_params->rf_params); + if (status != NRF_WIFI_STATUS_SUCCESS) { + goto out; + } + + conf_params->tx_pkt_nss = 1; + conf_params->tx_pkt_gap_us = 0; + + conf_params->tx_power = MAX_TX_PWR_SYS_TEST; + + conf_params->chan.primary_num = 1; + conf_params->tx_mode = 1; + conf_params->tx_pkt_num = -1; + conf_params->tx_pkt_len = 1400; + conf_params->tx_pkt_preamble = 0; + conf_params->tx_pkt_rate = 6; + conf_params->he_ltf = 2; + conf_params->he_gi = 2; + conf_params->aux_adc_input_chain_id = 1; + conf_params->ru_tone = 26; + conf_params->ru_index = 1; + conf_params->tx_pkt_cw = 15; + conf_params->phy_calib = NRF_WIFI_DEF_PHY_CALIB; + + /* Store back the currently set country code */ + if (strlen(country_code)) { + memcpy(conf_params->country_code, + country_code, + NRF_WIFI_COUNTRY_CODE_LEN); + } else { + memcpy(conf_params->country_code, + "00", + NRF_WIFI_COUNTRY_CODE_LEN); + } +out: + return status; +} + + +static int nrf_wifi_radio_test_set_defaults(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + status = nrf_wifi_radio_test_conf_init(&ctx->conf_params); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "Configuration init failed\n"); + return -ENOEXEC; + } + + return 0; +} + + +static int nrf_wifi_radio_test_set_phy_calib_rxdc(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long phy_calib_rxdc = 0; + + phy_calib_rxdc = strtoul(argv[1], &ptr, 10); + + if (phy_calib_rxdc > 1) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid PHY RX DC calibration value(%lu).\n", + phy_calib_rxdc); + shell_help(shell); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + if (phy_calib_rxdc) + ctx->conf_params.phy_calib = (ctx->conf_params.phy_calib | + NRF_WIFI_PHY_CALIB_FLAG_RXDC); + else + ctx->conf_params.phy_calib = (ctx->conf_params.phy_calib & + ~(NRF_WIFI_PHY_CALIB_FLAG_RXDC)); + + return 0; +} + + +static int nrf_wifi_radio_test_set_phy_calib_txdc(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long phy_calib_txdc = 0; + + phy_calib_txdc = strtoul(argv[1], &ptr, 10); + + if (phy_calib_txdc > 1) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid PHY TX DC calibration value(%lu).\n", + phy_calib_txdc); + shell_help(shell); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + if (phy_calib_txdc) + ctx->conf_params.phy_calib = (ctx->conf_params.phy_calib | + NRF_WIFI_PHY_CALIB_FLAG_TXDC); + else + ctx->conf_params.phy_calib = (ctx->conf_params.phy_calib & + ~(NRF_WIFI_PHY_CALIB_FLAG_TXDC)); + + return 0; +} + + +static int nrf_wifi_radio_test_set_phy_calib_txpow(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long phy_calib_txpow = 0; + + phy_calib_txpow = strtoul(argv[1], &ptr, 10); + + if (phy_calib_txpow > 1) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid PHY TX power calibration value(%lu).\n", + phy_calib_txpow); + shell_help(shell); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + if (phy_calib_txpow) + ctx->conf_params.phy_calib = (ctx->conf_params.phy_calib | + NRF_WIFI_PHY_CALIB_FLAG_TXPOW); + else + ctx->conf_params.phy_calib = (ctx->conf_params.phy_calib & + ~(NRF_WIFI_PHY_CALIB_FLAG_TXPOW)); + + return 0; +} + + +static int nrf_wifi_radio_test_set_phy_calib_rxiq(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long phy_calib_rxiq = 0; + + phy_calib_rxiq = strtoul(argv[1], &ptr, 10); + + if (phy_calib_rxiq > 1) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid PHY RX IQ calibration value(%lu).\n", + phy_calib_rxiq); + shell_help(shell); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + if (phy_calib_rxiq) + ctx->conf_params.phy_calib = (ctx->conf_params.phy_calib | + NRF_WIFI_PHY_CALIB_FLAG_RXIQ); + else + ctx->conf_params.phy_calib = (ctx->conf_params.phy_calib & + ~(NRF_WIFI_PHY_CALIB_FLAG_RXIQ)); + + return 0; +} + + +static int nrf_wifi_radio_test_set_phy_calib_txiq(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long phy_calib_txiq = 0; + + phy_calib_txiq = strtoul(argv[1], &ptr, 10); + + if (phy_calib_txiq > 1) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid PHY TX IQ calibration value(%lu).\n", + phy_calib_txiq); + shell_help(shell); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + if (phy_calib_txiq) + ctx->conf_params.phy_calib = (ctx->conf_params.phy_calib | + NRF_WIFI_PHY_CALIB_FLAG_TXIQ); + else + ctx->conf_params.phy_calib = (ctx->conf_params.phy_calib & + ~(NRF_WIFI_PHY_CALIB_FLAG_TXIQ)); + return 0; +} + + +static int nrf_wifi_radio_test_set_he_ltf(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long he_ltf = 0; + + he_ltf = strtoul(argv[1], &ptr, 10); + + if (he_ltf > 2) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid HE LTF value(%lu).\n", + he_ltf); + shell_help(shell); + return -ENOEXEC; + } + + ctx->conf_params.he_ltf = he_ltf; + + return 0; +} + + +static int nrf_wifi_radio_test_set_he_gi(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long he_gi = 0; + + he_gi = strtoul(argv[1], &ptr, 10); + + if (he_gi > 2) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid HE GI value(%lu).\n", + he_gi); + shell_help(shell); + return -ENOEXEC; + } + + ctx->conf_params.he_gi = he_gi; + + return 0; +} + + +static int nrf_wifi_radio_test_set_tx_pkt_tput_mode(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if (val >= RPU_TPUT_MODE_MAX) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.tx_pkt_tput_mode = val; + + return 0; +} + + +static int nrf_wifi_radio_test_set_tx_pkt_sgi(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if (val > 1) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.tx_pkt_sgi = val; + + return 0; +} + + +static int nrf_wifi_radio_test_set_tx_pkt_preamble(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if (val >= RPU_PKT_PREAMBLE_MAX) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + if (ctx->conf_params.tx_pkt_tput_mode == 0) { + if ((val != RPU_PKT_PREAMBLE_SHORT) && + (val != RPU_PKT_PREAMBLE_LONG)) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu for legacy mode\n", + val); + return -ENOEXEC; + } + } else { + if (val != RPU_PKT_PREAMBLE_MIXED) { + shell_fprintf(shell, + SHELL_ERROR, + "Only mixed preamble mode supported in HT, VHT and HE modes\n"); + return -ENOEXEC; + } + } + + ctx->conf_params.tx_pkt_preamble = val; + + return 0; +} + + +static int nrf_wifi_radio_test_set_tx_pkt_mcs(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + long val = -1; + + val = strtol(argv[1], &ptr, 10); + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + if (!(check_valid_data_rate(shell, + ctx->conf_params.tx_pkt_tput_mode, + ctx->conf_params.tx_pkt_nss, + (val | 0x80)))) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + return -ENOEXEC; + } + + ctx->conf_params.tx_pkt_mcs = val; + + return 0; +} + + +static int nrf_wifi_radio_test_set_tx_pkt_rate(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + long val = -1; + + val = strtol(argv[1], &ptr, 10); + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + if (!(check_valid_data_rate(shell, + ctx->conf_params.tx_pkt_tput_mode, + ctx->conf_params.tx_pkt_nss, + val))) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + return -ENOEXEC; + } + + ctx->conf_params.tx_pkt_rate = val; + + return 0; +} + + +static int nrf_wifi_radio_test_set_tx_pkt_gap(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if (val > 200000) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.tx_pkt_gap_us = val; + + return 0; +} + + +static int nrf_wifi_radio_test_set_tx_pkt_num(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + long val = 0; + + val = strtol(argv[1], &ptr, 10); + + if ((val < -1) || (val == 0)) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.tx_pkt_num = val; + + return 0; +} + +static int nrf_wifi_radio_test_set_tx_pkt_len(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if (val == 0) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + + if (ctx->conf_params.tx_pkt_tput_mode == RPU_TPUT_MODE_MAX) { + shell_fprintf(shell, + SHELL_ERROR, + "Throughput mode not set\n"); + return -ENOEXEC; + } + + if (ctx->conf_params.tx_pkt_tput_mode == RPU_TPUT_MODE_LEGACY) { + if (val > 4000) { + shell_fprintf(shell, + SHELL_ERROR, + "max 'tx_pkt_len' size for legacy is 4000 bytes\n"); + return -ENOEXEC; + } + } else if (ctx->conf_params.tx_pkt_tput_mode == RPU_TPUT_MODE_HE_TB) { + if (ctx->conf_params.ru_tone == 26) { + if (val >= 350) { + shell_fprintf(shell, + SHELL_ERROR, + "'tx_pkt_len' has to be less than 350 bytes\n"); + return -ENOEXEC; + } + } else if (ctx->conf_params.ru_tone == 52) { + if (val >= 800) { + shell_fprintf(shell, + SHELL_ERROR, + "'tx_pkt_len' has to be less than 800 bytes\n"); + return -ENOEXEC; + } + } else if (ctx->conf_params.ru_tone == 106) { + if (val >= 1800) { + shell_fprintf(shell, + SHELL_ERROR, + "'tx_pkt_len' has to be less than 1800 bytes\n"); + return -ENOEXEC; + } + } else if (ctx->conf_params.ru_tone == 242) { + if (val >= 4000) { + shell_fprintf(shell, + SHELL_ERROR, + "'tx_pkt_len' has to be less than 4000 bytes\n"); + return -ENOEXEC; + } + } else { + shell_fprintf(shell, + SHELL_ERROR, + "'ru_tone' not set\n"); + return -ENOEXEC; + } + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.tx_pkt_len = val; + + return 0; +} + + +static int nrf_wifi_radio_test_set_tx_power(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if (((val > MAX_TX_PWR_RADIO_TEST) && (val != MAX_TX_PWR_SYS_TEST))) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid TX power setting\n"); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.tx_power = val; + + return 0; +} + + +static int nrf_wifi_radio_test_set_tx_tone_freq(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + signed char val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if ((val > 10) || (val < -10)) { + shell_fprintf(shell, + SHELL_ERROR, + "'tx_tone_freq' has to be in between -10 to +10\n"); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.tx_tone_freq = val; + + return 0; +} + + +static int nrf_wifi_radio_test_set_rx_lna_gain(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + signed char val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if ((val > 4) || (val < 0)) { + shell_fprintf(shell, + SHELL_ERROR, + "'lna_gain' has to be in between 0 to 4\n"); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.lna_gain = val; + + return 0; +} + + +static int nrf_wifi_radio_test_set_rx_bb_gain(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + signed char val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if ((val > 31) || (val < 0)) { + shell_fprintf(shell, + SHELL_ERROR, + "'bb_gain' has to be in between 0 to 31\n"); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.bb_gain = val; + + return 0; +} + + +static int nrf_wifi_radio_test_set_rx_capture_length(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned short int val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if ((val > MAX_CAPTURE_LEN) || (val < MIN_CAPTURE_LEN)) { + shell_fprintf(shell, + SHELL_ERROR, + "'capture_length' has to be non-zero and less than 16384\n"); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.capture_length = val; + + return 0; +} + +static int nrf_wifi_radio_test_set_rx_capture_timeout(const struct shell *shell, size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned short int val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if (val > CAPTURE_DURATION_IN_SEC) { + shell_fprintf(shell, + SHELL_ERROR, + "'capture_timeout' has to be less than 600 seconds\n"); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.capture_timeout = val; + + return 0; +} + +static int nrf_wifi_radio_test_set_ru_tone(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if ((val != 26) && + (val != 52) && + (val != 106) && + (val != 242)) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.ru_tone = val; + + return 0; +} + + +static int nrf_wifi_radio_test_set_ru_index(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if (ctx->conf_params.ru_tone == 26) { + if ((val < 1) || (val > 9)) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + } else if (ctx->conf_params.ru_tone == 52) { + if ((val < 1) || (val > 4)) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + } else if (ctx->conf_params.ru_tone == 106) { + if ((val < 1) || (val > 2)) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + } else if (ctx->conf_params.ru_tone == 242) { + if ((val != 1)) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + } else { + shell_fprintf(shell, + SHELL_ERROR, + "RU tone not set\n"); + shell_help(shell); + return -ENOEXEC; + } + + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.ru_index = val; + + return 0; +} + + +static int nrf_wifi_radio_test_init(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + char *ptr = NULL; + unsigned long val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if (!(check_valid_channel(val))) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + return -ENOEXEC; + } + + if (ctx->conf_params.rx) { + shell_fprintf(shell, + SHELL_INFO, + "Disabling ongoing RX test\n"); + + ctx->conf_params.rx = 0; + + status = nrf_wifi_fmac_radio_test_prog_rx(ctx->rpu_ctx, + &ctx->conf_params); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "Disabling RX failed\n"); + return -ENOEXEC; + } + } + + if (ctx->conf_params.tx) { + shell_fprintf(shell, + SHELL_INFO, + "Disabling ongoing TX test\n"); + + ctx->conf_params.tx = 0; + + status = nrf_wifi_fmac_radio_test_prog_tx(ctx->rpu_ctx, + &ctx->conf_params); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "Disabling TX failed\n"); + return -ENOEXEC; + } + } + + if (ctx->rf_test_run) { + if (ctx->rf_test != NRF_WIFI_RF_TEST_TX_TONE) { + shell_fprintf(shell, + SHELL_ERROR, + "Unexpected: RF Test (%d) running\n", + ctx->rf_test); + + return -ENOEXEC; + } + + shell_fprintf(shell, + SHELL_INFO, + "Disabling ongoing TX tone test\n"); + + status = nrf_wifi_fmac_rf_test_tx_tone(ctx->rpu_ctx, + 0, + ctx->conf_params.tx_tone_freq, + ctx->conf_params.tx_power); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "Disabling TX tone test failed\n"); + return -ENOEXEC; + } + + ctx->rf_test_run = false; + ctx->rf_test = NRF_WIFI_RF_TEST_MAX; + + } + + status = nrf_wifi_radio_test_conf_init(&ctx->conf_params); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "Configuration init failed\n"); + return -ENOEXEC; + } + + ctx->conf_params.chan.primary_num = val; + + status = nrf_wifi_fmac_radio_test_init(ctx->rpu_ctx, + &ctx->conf_params); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "Programming init failed\n"); + return -ENOEXEC; + } + + return 0; +} + +static int nrf_wifi_radio_test_set_tx_pkt_cw(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + long val = 0; + + val = strtol(argv[1], &ptr, 10); + + if (!((val == 0) || + (val == 3) || + (val == 7) || + (val == 15) || + (val == 31) || + (val == 63) || + (val == 127) || + (val == 255) || + (val == 511) || + (val == 1023))) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.tx_pkt_cw = val; + + return 0; +} + +static int nrf_wifi_radio_test_set_tx(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + char *ptr = NULL; + unsigned long val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if (val > 1) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + + if (val == 1) { + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + if (check_channel_settings(shell, + ctx->conf_params.tx_pkt_tput_mode, + &ctx->conf_params.chan) != 0) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid channel settings\n"); + return -ENOEXEC; + } + + /** Max TX power values differ based on the test being performed. + * For TX EVM Vs Power, Max TX power required is + * "MAX_TX_PWR_RADIO_TEST" (24dB) whereas for testing the + * Max TX power for which both EVM and spectrum mask are passing + * for specific band and MCS/rate, TX power values will be read from + * RF params string. + */ + if (ctx->conf_params.tx_power != MAX_TX_PWR_SYS_TEST) { + /** Max TX power is represented in 0.25dB resolution + * So,multiply 4 to MAX_TX_PWR_RADIO_TEST and + * configure the RF params corresponding to Max TX power. + */ + memset(&ctx->conf_params.rf_params[NRF_WIFI_TX_PWR_CEIL_BYTE_OFFSET], + (MAX_TX_PWR_RADIO_TEST << 2), + sizeof(struct nrf_wifi_tx_pwr_ceil)); + } + } + + ctx->conf_params.tx = val; + + status = nrf_wifi_fmac_radio_test_prog_tx(ctx->rpu_ctx, + &ctx->conf_params); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "Programming TX failed\n"); + return -ENOEXEC; + } + + return 0; +} + + +static int nrf_wifi_radio_test_set_rx(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + char *ptr = NULL; + unsigned long val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if (val > 1) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + + if (val == 1) { + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + } + + ctx->conf_params.rx = val; + + status = nrf_wifi_fmac_radio_test_prog_rx(ctx->rpu_ctx, + &ctx->conf_params); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "Programming RX failed\n"); + return -ENOEXEC; + } + + return 0; +} + +#ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH +static int nrf_wifi_radio_test_sr_ant_switch_ctrl(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + unsigned int val; + + if (argc < 2) { + shell_fprintf(shell, SHELL_ERROR, "invalid # of args : %d\n", argc); + return -ENOEXEC; + } + + val = strtoul(argv[1], NULL, 0); + + ctx->conf_params.sr_ant_switch_ctrl = val; + + return sr_ant_switch(val); +} +#endif /* CONFIG_NRF70_SR_COEX_RF_SWITCH */ + +static int nrf_wifi_radio_test_rx_cap(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + unsigned long rx_cap_type = 0; + unsigned char *rx_cap_buf = NULL; + unsigned char capture_status = 0; + char *ptr = NULL; + unsigned int i = 0; + int ret = -ENOEXEC; + + rx_cap_type = strtoul(argv[1], &ptr, 10); + + if ((rx_cap_type != NRF_WIFI_RF_TEST_RX_ADC_CAP) && + (rx_cap_type != NRF_WIFI_RF_TEST_RX_STAT_PKT_CAP) && + (rx_cap_type != NRF_WIFI_RF_TEST_RX_DYN_PKT_CAP)) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + rx_cap_type); + shell_help(shell); + return -ENOEXEC; + } + + if (!ctx->conf_params.capture_length) { + shell_fprintf(shell, + SHELL_ERROR, + "%s: Invalid rx_capture_length %d\n", + __func__, + ctx->conf_params.capture_length); + goto out; + } + + if (rx_cap_type == NRF_WIFI_RF_TEST_RX_DYN_PKT_CAP) { + if (!ctx->conf_params.capture_timeout) { + shell_fprintf(shell, + SHELL_ERROR, + "%s: Invalid rx_capture_timeout %d\n", + __func__, + ctx->conf_params.capture_timeout); + goto out; + } + } + + if (!check_test_in_prog(shell)) { + goto out; + } + + rx_cap_buf = k_calloc((ctx->conf_params.capture_length * 3), + sizeof(char)); + + if (!rx_cap_buf) { + shell_fprintf(shell, + SHELL_ERROR, + "%s: Unable to allocate (%d) bytes for RX capture\n", + __func__, + (ctx->conf_params.capture_length * 3)); + goto out; + } + + ctx->rf_test_run = true; + ctx->rf_test = NRF_WIFI_RF_TEST_RX_ADC_CAP; + + status = nrf_wifi_fmac_rf_test_rx_cap(ctx->rpu_ctx, + rx_cap_type, + rx_cap_buf, + ctx->conf_params.capture_length, + ctx->conf_params.capture_timeout, + ctx->conf_params.lna_gain, + ctx->conf_params.bb_gain, + &capture_status); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "RX ADC capture programming failed\n"); + goto out; + } + + if (capture_status == 0) { + shell_fprintf(shell, + SHELL_INFO, + "\n************* RX capture data ***********\n"); + + for (i = 0; i < (ctx->conf_params.capture_length); i++) { + shell_fprintf(shell, + SHELL_INFO, + "%02X%02X%02X\n", + rx_cap_buf[i*3 + 2], + rx_cap_buf[i*3 + 1], + rx_cap_buf[i*3 + 0]); + } + } else if (capture_status == 1) { + shell_fprintf(shell, + SHELL_INFO, + "\n************* Capture failed ***********\n"); + } else { + shell_fprintf(shell, + SHELL_INFO, + "\n************* Packet detection failed ***********\n"); + } + + ret = 0; +out: + if (rx_cap_buf) + k_free(rx_cap_buf); + + ctx->rf_test_run = false; + ctx->rf_test = NRF_WIFI_RF_TEST_MAX; + + return ret; +} + + +static int nrf_wifi_radio_test_tx_tone(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + char *ptr = NULL; + unsigned long val = 0; + int ret = -ENOEXEC; + + val = strtoul(argv[1], &ptr, 10); + + if (val > 1) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + goto out; + + } + + if (val == 1) { + if (!check_test_in_prog(shell)) { + goto out; + } + + } + + status = nrf_wifi_fmac_rf_test_tx_tone(ctx->rpu_ctx, + (unsigned char)val, + ctx->conf_params.tx_tone_freq, + ctx->conf_params.tx_power); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "TX tone programming failed\n"); + goto out; + } + + if (val == 1) { + ctx->rf_test_run = true; + ctx->rf_test = NRF_WIFI_RF_TEST_TX_TONE; + } else { + ctx->rf_test_run = false; + ctx->rf_test = NRF_WIFI_RF_TEST_MAX; + } + + ret = 0; +out: + return ret; +} + + +static int nrf_wifi_radio_set_dpd(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + char *ptr = NULL; + unsigned long val = 0; + int ret = -ENOEXEC; + + val = strtoul(argv[1], &ptr, 10); + + if (val > 1) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + goto out; + } + + if (val == 1) { + if (!check_test_in_prog(shell)) { + goto out; + } + } + + ctx->rf_test_run = true; + ctx->rf_test = NRF_WIFI_RF_TEST_DPD; + + status = nrf_wifi_fmac_rf_test_dpd(ctx->rpu_ctx, + val); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "DPD programming failed\n"); + goto out; + } + + ret = 0; +out: + ctx->rf_test_run = false; + ctx->rf_test = NRF_WIFI_RF_TEST_MAX; + + return ret; +} + +static int nrf_wifi_radio_get_temperature(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int ret = -ENOEXEC; + + if (!check_test_in_prog(shell)) { + goto out; + } + + ctx->rf_test_run = true; + ctx->rf_test = NRF_WIFI_RF_TEST_GET_TEMPERATURE; + + status = nrf_wifi_fmac_rf_get_temp(ctx->rpu_ctx); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "DPD programming failed\n"); + goto out; + } + + ret = 0; +out: + ctx->rf_test_run = false; + ctx->rf_test = NRF_WIFI_RF_TEST_MAX; + + return ret; +} + + +static int nrf_wifi_radio_get_rf_rssi(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int ret = -ENOEXEC; + + if (!check_test_in_prog(shell)) { + goto out; + } + + ctx->rf_test_run = true; + ctx->rf_test = NRF_WIFI_RF_TEST_RF_RSSI; + + status = nrf_wifi_fmac_rf_get_rf_rssi(ctx->rpu_ctx); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "RF RSSI get failed\n"); + goto out; + } + + ret = 0; +out: + ctx->rf_test_run = false; + ctx->rf_test = NRF_WIFI_RF_TEST_MAX; + + return ret; +} + + +static int nrf_wifi_radio_set_xo_val(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + char *ptr = NULL; + unsigned long val = 0; + int ret = -ENOEXEC; + + val = strtoul(argv[1], &ptr, 10); + + if (val > 0x7f) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value XO value should be <= 0x7f\n"); + shell_help(shell); + goto out; + } + + if (val < 0) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value XO value should be >= 0\n"); + shell_help(shell); + goto out; + } + + if (val == 1) { + if (!check_test_in_prog(shell)) { + goto out; + } + } + + ctx->rf_test_run = true; + ctx->rf_test = NRF_WIFI_RF_TEST_XO_CALIB; + + status = nrf_wifi_fmac_set_xo_val(ctx->rpu_ctx, + (unsigned char)val); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "XO value programming failed\n"); + goto out; + } + + ctx->conf_params.rf_params[NRF_WIFI_XO_FREQ_BYTE_OFFSET] = val; + + ret = 0; +out: + ctx->rf_test_run = false; + ctx->rf_test = NRF_WIFI_RF_TEST_MAX; + + return ret; +} + +static int nrf_wifi_radio_comp_opt_xo_val(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + + int ret = -ENOEXEC; + + if (!check_test_in_prog(shell)) { + goto out; + } + + ctx->rf_test_run = true; + ctx->rf_test = NRF_WIFI_RF_TEST_XO_TUNE; + + status = nrf_wifi_fmac_rf_test_compute_xo(ctx->rpu_ctx); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "XO value computation failed\n"); + goto out; + } + + ret = 0; +out: + ctx->rf_test_run = false; + ctx->rf_test = NRF_WIFI_RF_TEST_MAX; + + return ret; +} + +static int nrf_wifi_radio_test_set_ant_gain(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long ant_gain = 0; + + ant_gain = strtoul(argv[1], &ptr, 10); + + if ((ant_gain < 0) || (ant_gain > 6)) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid antenna gain setting\n"); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + memset(&ctx->conf_params.rf_params[ANT_GAIN_2G_OFST], + ant_gain, + NUM_ANT_GAIN); + + return 0; +} + +static int nrf_wifi_radio_test_set_edge_bo(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long edge_bo = 0; + + edge_bo = strtoul(argv[1], &ptr, 10); + + if ((edge_bo < 0) || (edge_bo > 10)) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid edge backoff setting\n"); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + memset(&ctx->conf_params.rf_params[BAND_2G_LW_ED_BKF_DSSS_OFST], + edge_bo, + NUM_EDGE_BACKOFF); + + return 0; +} + +static int nrf_wifi_radio_test_show_cfg(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + struct rpu_conf_params *conf_params = NULL; + + conf_params = &ctx->conf_params; + + shell_fprintf(shell, + SHELL_INFO, + "************* Configured Parameters ***********\n"); + shell_fprintf(shell, + SHELL_INFO, + "\n"); + + shell_fprintf(shell, + SHELL_INFO, + "tx_pkt_tput_mode = %d\n", + conf_params->tx_pkt_tput_mode); + + shell_fprintf(shell, + SHELL_INFO, + "tx_pkt_sgi = %d\n", + conf_params->tx_pkt_sgi); + + shell_fprintf(shell, + SHELL_INFO, + "tx_pkt_preamble = %d\n", + conf_params->tx_pkt_preamble); + + shell_fprintf(shell, + SHELL_INFO, + "tx_pkt_mcs = %d\n", + conf_params->tx_pkt_mcs); + + if (conf_params->tx_pkt_rate == 5) + shell_fprintf(shell, + SHELL_INFO, + "tx_pkt_rate = 5.5\n"); + else + shell_fprintf(shell, + SHELL_INFO, + "tx_pkt_rate = %d\n", + conf_params->tx_pkt_rate); + + shell_fprintf(shell, + SHELL_INFO, + "tx_pkt_gap = %d\n", + conf_params->tx_pkt_gap_us); + + shell_fprintf(shell, + SHELL_INFO, + "phy_calib_rxdc = %d\n", + (conf_params->phy_calib & + NRF_WIFI_PHY_CALIB_FLAG_RXDC) ? 1:0); + + shell_fprintf(shell, + SHELL_INFO, + "phy_calib_txdc = %d\n", + (conf_params->phy_calib & + NRF_WIFI_PHY_CALIB_FLAG_TXDC) ? 1:0); + + shell_fprintf(shell, + SHELL_INFO, + "phy_calib_txpow = %d\n", + (conf_params->phy_calib & + NRF_WIFI_PHY_CALIB_FLAG_TXPOW) ? 1:0); + + shell_fprintf(shell, + SHELL_INFO, + "phy_calib_rxiq = %d\n", + (conf_params->phy_calib & + NRF_WIFI_PHY_CALIB_FLAG_RXIQ) ? 1:0); + + shell_fprintf(shell, + SHELL_INFO, + "phy_calib_txiq = %d\n", + (conf_params->phy_calib & + NRF_WIFI_PHY_CALIB_FLAG_TXIQ) ? 1:0); + + shell_fprintf(shell, + SHELL_INFO, + "tx_pkt_num = %d\n", + conf_params->tx_pkt_num); + + shell_fprintf(shell, + SHELL_INFO, + "tx_pkt_len = %d\n", + conf_params->tx_pkt_len); + + shell_fprintf(shell, + SHELL_INFO, + "tx_power = %d\n", + conf_params->tx_power); + + shell_fprintf(shell, + SHELL_INFO, + "he_ltf = %d\n", + conf_params->he_ltf); + + shell_fprintf(shell, + SHELL_INFO, + "he_gi = %d\n", + conf_params->he_gi); + + shell_fprintf(shell, + SHELL_INFO, + "xo_val = %d\n", + conf_params->rf_params[NRF_WIFI_XO_FREQ_BYTE_OFFSET]); + + shell_fprintf(shell, + SHELL_INFO, + "init = %d\n", + conf_params->chan.primary_num); + + shell_fprintf(shell, + SHELL_INFO, + "tx = %d\n", + conf_params->tx); + + shell_fprintf(shell, + SHELL_INFO, + "rx = %d\n", + conf_params->rx); + + shell_fprintf(shell, + SHELL_INFO, + "tx_tone_freq = %d\n", + conf_params->tx_tone_freq); + + shell_fprintf(shell, + SHELL_INFO, + "rx_lna_gain = %d\n", + conf_params->lna_gain); + + shell_fprintf(shell, + SHELL_INFO, + "rx_bb_gain = %d\n", + conf_params->bb_gain); + + shell_fprintf(shell, + SHELL_INFO, + "rx_capture_length = %d\n", + conf_params->capture_length); + + shell_fprintf(shell, + SHELL_INFO, + "rx_capture_timeout = %d\n", + conf_params->capture_timeout); + +#if defined(CONFIG_BOARD_NRF7002DK_NRF7001_NRF5340_CPUAPP) || \ + defined(CONFIG_BOARD_NRF7002DK_NRF5340_CPUAPP) + shell_fprintf(shell, + SHELL_INFO, + "sr_ant_switch_ctrl = %d\n", + conf_params->sr_ant_switch_ctrl); +#endif /* CONFIG_BOARD_NRF700XDK_NRF5340 */ + + shell_fprintf(shell, + SHELL_INFO, + "wlan_ant_switch_ctrl = %d\n", + conf_params->wlan_ant_switch_ctrl); + shell_fprintf(shell, + SHELL_INFO, + "tx_pkt_cw = %d\n", + conf_params->tx_pkt_cw); + + shell_fprintf(shell, + SHELL_INFO, + "reg_domain = %c%c\n", + conf_params->country_code[0], + conf_params->country_code[1]); + + shell_fprintf(shell, + SHELL_INFO, + "bypass_reg_domain = %d\n", + conf_params->bypass_regulatory); + + shell_fprintf(shell, + SHELL_INFO, + "ru_tone = %d\n", + conf_params->ru_tone); + + shell_fprintf(shell, + SHELL_INFO, + "ru_index = %d\n", + conf_params->ru_index); + + return 0; +} + + +static int nrf_wifi_radio_test_get_stats(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct rpu_op_stats stats; + + memset(&stats, + 0, + sizeof(stats)); + + status = nrf_wifi_fmac_stats_get(ctx->rpu_ctx, + ctx->conf_params.op_mode, + &stats); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "nrf_wifi_fmac_stats_get failed\n"); + return -ENOEXEC; + } + + shell_fprintf(shell, + SHELL_INFO, + "************* PHY STATS ***********\n"); + + shell_fprintf(shell, + SHELL_INFO, + "rssi_avg = %d dBm\n", + stats.fw.phy.rssi_avg); + + shell_fprintf(shell, + SHELL_INFO, + "ofdm_crc32_pass_cnt=%d\n", + stats.fw.phy.ofdm_crc32_pass_cnt); + + shell_fprintf(shell, + SHELL_INFO, + "ofdm_crc32_fail_cnt=%d\n", + stats.fw.phy.ofdm_crc32_fail_cnt); + + shell_fprintf(shell, + SHELL_INFO, + "dsss_crc32_pass_cnt=%d\n", + stats.fw.phy.dsss_crc32_pass_cnt); + + shell_fprintf(shell, + SHELL_INFO, + "dsss_crc32_fail_cnt=%d\n", + stats.fw.phy.dsss_crc32_fail_cnt); + + return 0; +} + +/* See enum CD2CM_MSG_ID_T in RPU Coexistence Manager API */ +#define CD2CM_UPDATE_SWITCH_CONFIG 0x7 +static int nrf_wifi_radio_test_wlan_switch_ctrl(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + char *ptr = NULL; + struct coex_wlan_switch_ctrl params = { 0 }; + + if (argc < 2) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid number of parameters\n"); + shell_help(shell); + return -ENOEXEC; + } + + params.rpu_msg_id = CD2CM_UPDATE_SWITCH_CONFIG; + params.switch_A = strtoul(argv[1], &ptr, 10); + + if (params.switch_A > 1) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid WLAN switch config (%d)\n", + params.switch_A); + shell_help(shell); + return -ENOEXEC; + } + + ctx->conf_params.wlan_ant_switch_ctrl = params.switch_A; + + status = nrf_wifi_fmac_conf_srcoex(ctx->rpu_ctx, + ¶ms, sizeof(params)); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "WLAN switch configuration failed\n"); + return -ENOEXEC; + } + + return 0; +} + + +static int nrf_wifi_radio_test_set_reg_domain(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int ret = -ENOEXEC; + struct nrf_wifi_fmac_reg_info reg_domain_info = {0}; + + if (strlen(argv[1]) != 2) { + shell_fprintf(shell, SHELL_WARNING, + "Invalid reg domain: Length should be two letters/digits\n"); + goto out; + } + + /* Two letter country code with special case of 00 for WORLD */ + if (((argv[1][0] < 'A' || argv[1][0] > 'Z') || + (argv[1][1] < 'A' || argv[1][1] > 'Z')) && + (argv[1][0] != '0' || argv[1][1] != '0')) { + shell_fprintf(shell, SHELL_WARNING, "Invalid reg domain %c%c\n", argv[1][0], + argv[2][1]); + goto out; + } + + ctx->conf_params.country_code[0] = argv[1][0]; + ctx->conf_params.country_code[1] = argv[1][1]; + + if (!check_test_in_prog(shell)) { + goto out; + } + + memcpy(reg_domain_info.alpha2, ctx->conf_params.country_code, + NRF_WIFI_COUNTRY_CODE_LEN); + + status = nrf_wifi_fmac_set_reg(ctx->rpu_ctx, ®_domain_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "Regulatory programming failed\n"); + goto out; + } + + ret = 0; +out: + return ret; +} + + +static int nrf_wifi_radio_test_set_bypass_reg(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long val = 0; + + val = strtoul(argv[1], &ptr, 10); + + if (val > 1) { + shell_fprintf(shell, + SHELL_ERROR, + "Invalid value %lu\n", + val); + shell_help(shell); + return -ENOEXEC; + } + + if (!check_test_in_prog(shell)) { + return -ENOEXEC; + } + + ctx->conf_params.bypass_regulatory = val; + + return 0; +} + + +SHELL_STATIC_SUBCMD_SET_CREATE( + nrf_wifi_radio_test_subcmds, + SHELL_CMD_ARG(set_defaults, + NULL, + "Reset configuration parameter to their default values", + nrf_wifi_radio_test_set_defaults, + 1, + 0), + SHELL_CMD_ARG(phy_calib_rxdc, + NULL, + "0 - Disable RX DC calibration\n" + "1 - Enable RX DC calibration", + nrf_wifi_radio_test_set_phy_calib_rxdc, + 2, + 0), + SHELL_CMD_ARG(phy_calib_txdc, + NULL, + "0 - Disable TX DC calibration\n" + "1 - Enable TX DC calibration", + nrf_wifi_radio_test_set_phy_calib_txdc, + 2, + 0), + SHELL_CMD_ARG(phy_calib_txpow, + NULL, + "0 - Disable TX power calibration\n" + "1 - Enable TX power calibration", + nrf_wifi_radio_test_set_phy_calib_txpow, + 2, + 0), + SHELL_CMD_ARG(phy_calib_rxiq, + NULL, + "0 - Disable RX IQ calibration\n" + "1 - Enable RX IQ calibration", + nrf_wifi_radio_test_set_phy_calib_rxiq, + 2, + 0), + SHELL_CMD_ARG(phy_calib_txiq, + NULL, + "0 - Disable TX IQ calibration\n" + "1 - Enable TX IQ calibration", + nrf_wifi_radio_test_set_phy_calib_txiq, + 2, + 0), + SHELL_CMD_ARG(he_ltf, + NULL, + "0 - 1x HE LTF\n" + "1 - 2x HE LTF\n" + "2 - 4x HE LTF ", + nrf_wifi_radio_test_set_he_ltf, + 2, + 0), + SHELL_CMD_ARG(he_gi, + NULL, + "0 - 0.8 us\n" + "1 - 1.6 us\n" + "2 - 3.2 us ", + nrf_wifi_radio_test_set_he_gi, + 2, + 0), + SHELL_CMD_ARG(tx_pkt_tput_mode, + NULL, + "0 - Legacy mode\n" + "1 - HT mode\n" +#ifndef CONFIG_NRF70_2_4G_ONLY + "2 - VHT mode\n" +#endif /* CONFIG_NRF70_2_4G_ONLY */ + "3 - HE(SU) mode\n" + "4 - HE(ER SU) mode\n" + "5 - HE (TB) mode ", + nrf_wifi_radio_test_set_tx_pkt_tput_mode, + 2, + 0), + SHELL_CMD_ARG(tx_pkt_sgi, + NULL, + "0 - Disable\n" + "1 - Enable", + nrf_wifi_radio_test_set_tx_pkt_sgi, + 2, + 0), + SHELL_CMD_ARG(tx_pkt_preamble, + NULL, + "0 - Long preamble\n" + "1 - Short preamble\n" + "2 - Mixed preamble ", + nrf_wifi_radio_test_set_tx_pkt_preamble, + 2, + 0), + SHELL_CMD_ARG(tx_pkt_mcs, + NULL, + "-1 - Not being used\n" + " - MCS index to be used", + nrf_wifi_radio_test_set_tx_pkt_mcs, + 2, + 0), + SHELL_CMD_ARG(tx_pkt_rate, + NULL, + "-1 - Not being used\n" + " - Legacy rate to be used in Mbps (1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54)", + nrf_wifi_radio_test_set_tx_pkt_rate, + 2, + 0), + SHELL_CMD_ARG(tx_pkt_gap, + NULL, + " - Interval between TX packets in us (Min: 200, Max: 200000, Default: 200)", + nrf_wifi_radio_test_set_tx_pkt_gap, + 2, + 0), + SHELL_CMD_ARG(tx_pkt_num, + NULL, + "-1 - Transmit infinite packets\n" + " - Number of packets to transmit", + nrf_wifi_radio_test_set_tx_pkt_num, + 2, + 0), + SHELL_CMD_ARG(tx_pkt_len, + NULL, + " - Length of the packet (in bytes) to be transmitted (Default: 1400)", + nrf_wifi_radio_test_set_tx_pkt_len, + 2, + 0), + SHELL_CMD_ARG(tx_power, + NULL, + " - Value in dBm", + nrf_wifi_radio_test_set_tx_power, + 2, + 0), + SHELL_CMD_ARG(ru_tone, + NULL, + " - Resource unit (RU) size (26,52,106 or 242)", + nrf_wifi_radio_test_set_ru_tone, + 2, + 0), + SHELL_CMD_ARG(ru_index, + NULL, + " - Location of resource unit (RU) in 20 MHz spectrum\n" + " Valid Values:\n" + " For 26 ru_tone: 1 to 9\n" + " For 52 ru_tone: 1 to 4\n" + " For 106 ru_tone: 1 to 2\n" + " For 242 ru_tone: 1", + nrf_wifi_radio_test_set_ru_index, + 2, + 0), + SHELL_CMD_ARG(init, + NULL, + " - Primary channel number", + nrf_wifi_radio_test_init, + 2, + 0), + SHELL_CMD_ARG(tx, + NULL, + "0 - Disable TX\n" + "1 - Enable TX", + nrf_wifi_radio_test_set_tx, + 2, + 0), + SHELL_CMD_ARG(rx, + NULL, + "0 - Disable RX\n" + "1 - Enable RX", + nrf_wifi_radio_test_set_rx, + 2, + 0), +#ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH + SHELL_CMD_ARG(sr_ant_switch_ctrl, + NULL, + "0 - Switch set to use the BLE antenna\n" + "1 - Switch set to use the shared Wi-Fi antenna", + nrf_wifi_radio_test_sr_ant_switch_ctrl, + 2, + 0), +#endif /* CONFIG_NRF70_SR_COEX_RF_SWITCH */ + SHELL_CMD_ARG(rx_lna_gain, + NULL, + " - LNA gain to be configured.\n" + "0 = 24 dB\n" + "1 = 18 dB\n" + "2 = 12 dB\n" + "3 = 0 dB\n" + "4 = -12 dB ", + nrf_wifi_radio_test_set_rx_lna_gain, + 2, + 0), + SHELL_CMD_ARG(rx_bb_gain, + NULL, + " - Baseband gain to be configured\n" + "It is a 5 bit value. Supports 64dB range in steps of 2dB", + nrf_wifi_radio_test_set_rx_bb_gain, + 2, + 0), + SHELL_CMD_ARG(rx_capture_length, + NULL, + " - Number of RX samples to be captured\n" + "Max allowed length is 16384 complex samples", + nrf_wifi_radio_test_set_rx_capture_length, + 2, + 0), + SHELL_CMD_ARG(rx_capture_timeout, + NULL, + " - Wait time allowed in seconds\n" + "Max timeout allowed is 600 seconds", + nrf_wifi_radio_test_set_rx_capture_timeout, + 2, + 0), + SHELL_CMD_ARG(rx_cap, + NULL, + "0 = ADC capture\n" + "1 = Filtered ADC capture\n" + "2 = Dynamic packet capture ", + nrf_wifi_radio_test_rx_cap, + 2, + 0), + SHELL_CMD_ARG(tx_tone_freq, + NULL, + " - Frequency in the range of -10MHz to 10MHz", + nrf_wifi_radio_test_set_tx_tone_freq, + 2, + 0), + SHELL_CMD_ARG(tx_tone, + NULL, + "\n" + "0: Disable tone\n" + "1: Enable tone ", + nrf_wifi_radio_test_tx_tone, + 2, + 0), + SHELL_CMD_ARG(dpd, + NULL, + "0 - Bypass DPD\n" + "1 - Enable DPD", + nrf_wifi_radio_set_dpd, + 2, + 0), + SHELL_CMD_ARG(get_temperature, + NULL, + "No arguments required", + nrf_wifi_radio_get_temperature, + 1, + 0), + SHELL_CMD_ARG(get_rf_rssi, + NULL, + "No arguments required", + nrf_wifi_radio_get_rf_rssi, + 1, + 0), + SHELL_CMD_ARG(set_xo_val, + NULL, + " - XO value in the range 0 to 127", + nrf_wifi_radio_set_xo_val, + 2, + 0), + SHELL_CMD_ARG(compute_optimal_xo_val, + NULL, + "Compute optimal XO trim value", + nrf_wifi_radio_comp_opt_xo_val, + 1, + 0), + SHELL_CMD_ARG(show_config, + NULL, + "Display the current configuration values", + nrf_wifi_radio_test_show_cfg, + 1, + 0), + SHELL_CMD_ARG(get_stats, + NULL, + "Display statistics", + nrf_wifi_radio_test_get_stats, + 1, + 0), + SHELL_CMD_ARG(wlan_ant_switch_ctrl, + NULL, + "Configure WLAN antenna switch (0-separate/1-shared)", + nrf_wifi_radio_test_wlan_switch_ctrl, + 2, + 0), + SHELL_CMD_ARG(tx_pkt_cw, + NULL, + " - Contention window value to be configured (0, 3, 7, 15, 31, 63, 127, 255, 511, 1023)", + nrf_wifi_radio_test_set_tx_pkt_cw, + 2, + 0), + SHELL_CMD_ARG(reg_domain, + NULL, + "Configure WLAN regulatory domain country code", + nrf_wifi_radio_test_set_reg_domain, + 2, + 0), + SHELL_CMD_ARG(bypass_reg_domain, + NULL, + "Configure WLAN to bypass regulatory\n" + "0 - TX power of the channel will be set to " + "minimum between user configured TX power & " + "maximum TX power of channel in the configured regulatory domain.\n" + "1 - Configured TX power value will be used for the channel. ", + nrf_wifi_radio_test_set_bypass_reg, + 2, + 0), + SHELL_CMD_ARG(set_ant_gain, + NULL, + " - Antenna gain in dB (Min: 0, Max: 6)", + nrf_wifi_radio_test_set_ant_gain, + 2, + 0), + SHELL_CMD_ARG(set_edge_bo, + NULL, + " - Edge backoff in dB (Min: 0, Max: 10)", + nrf_wifi_radio_test_set_edge_bo, + 2, + 0), + SHELL_SUBCMD_SET_END); + + +SHELL_CMD_REGISTER(wifi_radio_test, + &nrf_wifi_radio_test_subcmds, + "nRF Wi-Fi radio test commands", + NULL); + + +static int nrf_wifi_radio_test_shell_init(void) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + unsigned int timeout = 0; + + while (!ctx->rpu_ctx && timeout < NRF_WIFI_RADIO_TEST_INIT_TIMEOUT_MS) { + k_sleep(K_MSEC(100)); + timeout += 100; + } + + if (!ctx->rpu_ctx) { + printf("nRF Wi-Fi radio test shell init timedout waiting for driver: %d\n", + NRF_WIFI_RADIO_TEST_INIT_TIMEOUT_MS); + return -ENOEXEC; + } + + status = nrf_wifi_radio_test_conf_init(&ctx->conf_params); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + return -ENOEXEC; + } + + return 0; +} + + +SYS_INIT(nrf_wifi_radio_test_shell_init, + APPLICATION, + CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/samples/wifi/radio_test/single_domain/src/radio_cmd.c b/samples/wifi/radio_test/single_domain/src/radio_cmd.c new file mode 100644 index 000000000000..07eeb32ddf14 --- /dev/null +++ b/samples/wifi/radio_test/single_domain/src/radio_cmd.c @@ -0,0 +1,1497 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include + +#include +#include +#include +#include +#if !defined(CONFIG_SOC_SERIES_NRF54HX) +#include +#endif /* !defined(CONFIG_SOC_SERIES_NRF54HX) */ + +#if CONFIG_FEM +#include "fem_al/fem_al.h" +#endif /* CONFIG_FEM */ + +#include "radio_test.h" + +#if NRF_POWER_HAS_DCDCEN_VDDH + #define TOGGLE_DCDC_HELP \ + "Toggle DCDC state , " \ + "if state = 1 then toggle DC/DC state, or " \ + "if state = 0 then toggle DC/DC VDDH state" +#elif NRF_POWER_HAS_DCDCEN + #define TOGGLE_DCDC_HELP \ + "Toggle DCDC state , " \ + "Toggle DC/DC state regardless of state value" +#endif + + +/* Radio parameter configuration. */ +static struct radio_param_config { + /** Radio transmission pattern. */ + enum transmit_pattern tx_pattern; + + /** Radio mode. Data rate and modulation. */ + nrf_radio_mode_t mode; + + /** Radio output power. */ + int8_t txpower; + + /** Radio start channel (frequency). */ + uint8_t channel_start; + + /** Radio end channel (frequency). */ + uint8_t channel_end; + + /** Delay time in milliseconds. */ + uint32_t delay_ms; + + /** Duty cycle. */ + uint32_t duty_cycle; + + /** + * Number of packets to be received. + * Set to zero for continuous RX. + */ + uint32_t rx_packets_num; + +#if CONFIG_FEM + /* Front-end module (FEM) configuration. */ + struct radio_test_fem fem; +#endif /* CONFIG_FEM */ +} config = { + .tx_pattern = TRANSMIT_PATTERN_RANDOM, + .mode = NRF_RADIO_MODE_BLE_1MBIT, + .txpower = 0, + .channel_start = 0, + .channel_end = 80, + .delay_ms = 10, + .duty_cycle = 50, +#if CONFIG_FEM + .fem.tx_power_control = FEM_USE_DEFAULT_TX_POWER_CONTROL +#endif /* CONFIG_FEM */ +}; + +/* Radio test configuration. */ +static struct radio_test_config test_config; + +/* If true, RX sweep, TX sweep or duty cycle test is performed. */ +static bool test_in_progress; + +#if CONFIG_HAS_HW_NRF_RADIO_IEEE802154 +static void ieee_channel_check(const struct shell *shell, uint8_t channel) +{ + if (config.mode == NRF_RADIO_MODE_IEEE802154_250KBIT) { + if ((channel < IEEE_MIN_CHANNEL) || + (channel > IEEE_MAX_CHANNEL)) { + shell_print(shell, + "For %s config.mode channel must be between %d and %d", + STRINGIFY(NRF_RADIO_MODE_IEEE802154_250KBIT), + IEEE_MIN_CHANNEL, + IEEE_MAX_CHANNEL); + + shell_print(shell, "Channel set to %d", + IEEE_MIN_CHANNEL); + } + + } +} +#endif /* CONFIG_HAS_HW_NRF_RADIO_IEEE802154 */ + +static int cmd_start_channel_set(const struct shell *shell, size_t argc, + char **argv) +{ + uint32_t channel; + + if (argc == 1) { + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + } + + if (argc > 2) { + shell_error(shell, "%s: bad parameters count", argv[0]); + return -EINVAL; + } + + channel = atoi(argv[1]); + + if (channel > 80) { + shell_error(shell, "Channel must be between 0 and 80"); + return -EINVAL; + } + + config.channel_start = (uint8_t) channel; + + shell_print(shell, "Start channel set to: %d", channel); + return 0; +} + +static int cmd_end_channel_set(const struct shell *shell, size_t argc, + char **argv) +{ + uint32_t channel; + + if (argc == 1) { + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + } + + if (argc > 2) { + shell_error(shell, "%s: bad parameters count", argv[0]); + return -EINVAL; + } + + channel = atoi(argv[1]); + + if (channel > 80) { + shell_error(shell, "Channel must be between 0 and 80"); + return -EINVAL; + } + + config.channel_end = (uint8_t) channel; + + shell_print(shell, "End channel set to: %d", channel); + return 0; +} + +static int cmd_time_set(const struct shell *shell, size_t argc, char **argv) +{ + uint32_t time; + + if (argc == 1) { + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + } + + if (argc > 2) { + shell_error(shell, "%s: bad parameters count", argv[0]); + return -EINVAL; + } + + time = atoi(argv[1]); + + if (time > 99) { + shell_error(shell, "Delay time must be between 0 and 99 ms"); + return -EINVAL; + } + + config.delay_ms = time; + + shell_print(shell, "Delay time set to: %d", time); + return 0; +} + +static int cmd_cancel(const struct shell *shell, size_t argc, char **argv) +{ + radio_test_cancel(); + return 0; +} + +static int cmd_data_rate_set(const struct shell *shell, size_t argc, + char **argv) +{ + if (argc == 1) { + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + } + + if (argc > 2) { + shell_error(shell, "%s: bad parameters count", argv[0]); + return -EINVAL; + } + + if (argc == 2) { + shell_error(shell, "Uknown argument: %s", argv[1]); + return -EINVAL; + } + + return 0; +} + +static int cmd_tx_carrier_start(const struct shell *shell, size_t argc, + char **argv) +{ + if (test_in_progress) { + radio_test_cancel(); + test_in_progress = false; + } + +#if CONFIG_HAS_HW_NRF_RADIO_IEEE802154 + ieee_channel_check(shell, config.channel_start); +#endif /* CONFIG_HAS_HW_NRF_RADIO_IEEE802154 */ + + memset(&test_config, 0, sizeof(test_config)); + test_config.type = UNMODULATED_TX; + test_config.mode = config.mode; + test_config.params.unmodulated_tx.txpower = config.txpower; + test_config.params.unmodulated_tx.channel = config.channel_start; +#if CONFIG_FEM + test_config.fem = config.fem; +#endif /* CONFIG_FEM */ + radio_test_start(&test_config); + + shell_print(shell, "Start the TX carrier"); + return 0; +} + +static void tx_modulated_carrier_end(void) +{ + printk("The modulated TX has finished\n"); +} + +static void rx_end(void) +{ + uint32_t recv_pkt, req_pkt; + float error_rate; + + struct radio_rx_stats rx_stats; + + memset(&rx_stats, 0, sizeof(rx_stats)); + + radio_rx_stats_get(&rx_stats); + + recv_pkt = rx_stats.packet_cnt; + req_pkt = config.rx_packets_num; + + if (req_pkt == 0 || req_pkt < recv_pkt) { + printk("Error receiving packets\n"); + return; + } + + error_rate = ((float)(req_pkt - recv_pkt) / req_pkt) * 100.0f; + + printk("\n"); + printk("Received number of packets: %d\n", recv_pkt); + printk("Required number of packages: %d\n", req_pkt); + printk("Error rate: %.2f%%\n", (double)error_rate); + + if (error_rate >= 10) { + printk("\033[91mWarning: High error rate! \033[0m\n"); + } +} + +static int cmd_tx_modulated_carrier_start(const struct shell *shell, + size_t argc, + char **argv) +{ + if (test_in_progress) { + radio_test_cancel(); + test_in_progress = false; + } + +#if CONFIG_HAS_HW_NRF_RADIO_IEEE802154 + ieee_channel_check(shell, config.channel_start); +#endif /* CONFIG_HAS_HW_NRF_RADIO_IEEE802154 */ + + if (argc > 2) { + shell_error(shell, "%s: bad parameters count.", argv[0]); + return -EINVAL; + } + + memset(&test_config, 0, sizeof(test_config)); + test_config.type = MODULATED_TX; + test_config.mode = config.mode; + test_config.params.modulated_tx.txpower = config.txpower; + test_config.params.modulated_tx.channel = config.channel_start; + test_config.params.modulated_tx.pattern = config.tx_pattern; +#if CONFIG_FEM + test_config.fem = config.fem; +#endif /* CONFIG_FEM */ + + if (argc == 2) { + test_config.params.modulated_tx.packets_num = atoi(argv[1]); + test_config.params.modulated_tx.cb = tx_modulated_carrier_end; + } + + radio_test_start(&test_config); + + shell_print(shell, "Start the modulated TX carrier"); + return 0; +} + +static int cmd_duty_cycle_set(const struct shell *shell, size_t argc, + char **argv) +{ + uint32_t duty_cycle; + + if (argc == 1) { + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + } + + if (argc > 2) { + shell_error(shell, "%s: bad parameters count.", argv[0]); + return -EINVAL; + } + + duty_cycle = atoi(argv[1]); + + if (duty_cycle > 90) { + shell_error(shell, "Duty cycle must be between 1 and 90."); + return -EINVAL; + } + + config.duty_cycle = duty_cycle; + +#if CONFIG_HAS_HW_NRF_RADIO_IEEE802154 + ieee_channel_check(shell, config.channel_start); +#endif /* CONFIG_HAS_HW_NRF_RADIO_IEEE802154 */ + + memset(&test_config, 0, sizeof(test_config)); + test_config.type = MODULATED_TX_DUTY_CYCLE; + test_config.mode = config.mode; + test_config.params.modulated_tx_duty_cycle.txpower = config.txpower; + test_config.params.modulated_tx_duty_cycle.pattern = config.tx_pattern; + test_config.params.modulated_tx_duty_cycle.channel = + config.channel_start; + test_config.params.modulated_tx_duty_cycle.duty_cycle = + config.duty_cycle; +#if CONFIG_FEM + test_config.fem = config.fem; +#endif /* CONFIG_FEM */ + + radio_test_start(&test_config); + test_in_progress = true; + + return 0; +} + +#if defined(TOGGLE_DCDC_HELP) +static int cmd_toggle_dc(const struct shell *shell, size_t argc, char **argv) +{ + uint32_t state; + + if (argc == 1) { + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + } + + if (argc > 2) { + shell_error(shell, "%s: bad parameters count", argv[0]); + return -EINVAL; + } + + state = atoi(argv[1]); + if (state > 1) { + shell_error(shell, "Invalid DCDC value provided"); + return -EINVAL; + } + + toggle_dcdc_state((uint8_t) state); + +#if NRF_POWER_HAS_DCDCEN_VDDH + shell_print(shell, + "DCDC VDDH state %d\n" + "Write '0' to toggle state of DCDC REG0\n" + "Write '1' to toggle state of DCDC REG1", + nrf_power_dcdcen_vddh_get(NRF_POWER)); +#endif /* NRF_POWER_HAS_DCDCEN_VDDH */ + +#if NRF_POWER_HAS_DCDCEN + shell_print(shell, + "DCDC state %d\n" + "Write '1' or '0' to toggle", + nrf_power_dcdcen_get(NRF_POWER)); +#endif /* NRF_POWER_HAS_DCDCEN */ + + return 0; +} +#endif + +static int cmd_output_power_set(const struct shell *shell, size_t argc, + char **argv) +{ + if (argc == 1) { + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + } + + if (argc > 2) { + shell_error(shell, "%s: bad parameters count.", argv[0]); + return -EINVAL; + } + + if (argc == 2) { + shell_error(shell, "Uknown argument: %s", argv[1]); + return -EINVAL; + } + + return 0; +} + +static int cmd_transmit_pattern_set(const struct shell *shell, size_t argc, + char **argv) +{ + if (argc == 1) { + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + } + + if (argc > 2) { + shell_error(shell, "%s: bad parameters count.", argv[0]); + return -EINVAL; + } + + if (argc == 2) { + shell_error(shell, "Uknown argument: %s.", argv[1]); + return -EINVAL; + } + + return 0; +} + +static int cmd_print(const struct shell *shell, size_t argc, char **argv) +{ + shell_print(shell, "Parameters:"); + + switch (config.mode) { +#if defined(RADIO_MODE_MODE_Nrf_250Kbit) + case NRF_RADIO_MODE_NRF_250KBIT: + shell_print(shell, + "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_250KBIT)); + break; + +#endif /* defined(RADIO_MODE_MODE_Nrf_250Kbit) */ + +#if defined(RADIO_MODE_MODE_Nrf_4Mbit0_5) + case NRF_RADIO_MODE_NRF_4MBIT_H_0_5: + shell_print(shell, + "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_4MBIT_H_0_5)); + break; +#endif /* defined(RADIO_MODE_MODE_Nrf_4Mbit0_5) */ + +#if defined(RADIO_MODE_MODE_Nrf_4Mbit0_25) + case NRF_RADIO_MODE_NRF_4MBIT_H_0_25: + shell_print(shell, + "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_4MBIT_H_0_25)); + break; +#endif /* defined(RADIO_MODE_MODE_Nrf_4Mbit0_25) */ + +#if defined(RADIO_MODE_MODE_Nrf_4Mbit_0BT6) + case NRF_RADIO_MODE_NRF_4MBIT_BT_0_6: + shell_print(shell, + "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_4MBIT_BT_0_6)); + break; +#endif /* defined(RADIO_MODE_MODE_Nrf_4Mbit_0BT6) */ + +#if defined(RADIO_MODE_MODE_Nrf_4Mbit_0BT4) + case NRF_RADIO_MODE_NRF_4MBIT_BT_0_4: + shell_print(shell, + "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_4MBIT_BT_0_4)); + break; +#endif /* defined(RADIO_MODE_MODE_Nrf_4Mbit_0BT4) */ + + case NRF_RADIO_MODE_NRF_1MBIT: + shell_print(shell, + "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_1MBIT)); + break; + + case NRF_RADIO_MODE_NRF_2MBIT: + shell_print(shell, + "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_2MBIT)); + break; + + case NRF_RADIO_MODE_BLE_1MBIT: + shell_print(shell, + "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_BLE_1MBIT)); + break; + + case NRF_RADIO_MODE_BLE_2MBIT: + shell_print(shell, + "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_BLE_2MBIT)); + break; + +#if CONFIG_HAS_HW_NRF_RADIO_BLE_CODED + case NRF_RADIO_MODE_BLE_LR125KBIT: + shell_print(shell, + "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_BLE_LR125KBIT)); + break; + + case NRF_RADIO_MODE_BLE_LR500KBIT: + shell_print(shell, + "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_BLE_LR500KBIT)); + break; +#endif /* CONFIG_HAS_HW_NRF_RADIO_BLE_CODED */ + +#if CONFIG_HAS_HW_NRF_RADIO_IEEE802154 + case NRF_RADIO_MODE_IEEE802154_250KBIT: + shell_print(shell, + "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_IEEE802154_250KBIT)); + break; +#endif /* CONFIG_HAS_HW_NRF_RADIO_IEEE802154 */ + + default: + shell_print(shell, + "Data rate unknown or deprecated: %d\n\r", + config.mode); + break; + } + + shell_print(shell, "TX power : %d dBm", config.txpower); + + switch (config.tx_pattern) { + case TRANSMIT_PATTERN_RANDOM: + shell_print(shell, + "Transmission pattern: %s", + STRINGIFY(TRANSMIT_PATTERN_RANDOM)); + break; + + case TRANSMIT_PATTERN_11110000: + shell_print(shell, + "Transmission pattern: %s", + STRINGIFY(TRANSMIT_PATTERN_11110000)); + break; + + case TRANSMIT_PATTERN_11001100: + shell_print(shell, + "Transmission pattern: %s", + STRINGIFY(TRANSMIT_PATTERN_11001100)); + break; + + default: + shell_print(shell, + "Transmission pattern unknown: %d", + config.tx_pattern); + break; + } + + shell_print(shell, + "Start Channel: %hhu\n" + "End Channel: %hhu\n" + "Time on each channel: %u ms\n" + "Duty cycle: %u percent\n", + config.channel_start, + config.channel_end, + config.delay_ms, + config.duty_cycle); + + return 0; +} + +static int cmd_rx_sweep_start(const struct shell *shell, size_t argc, + char **argv) +{ + memset(&test_config, 0, sizeof(test_config)); + test_config.type = RX_SWEEP; + test_config.mode = config.mode; + test_config.params.rx_sweep.channel_start = config.channel_start; + test_config.params.rx_sweep.channel_end = config.channel_end; + test_config.params.rx_sweep.delay_ms = config.delay_ms; +#if CONFIG_FEM + test_config.fem = config.fem; +#endif /* CONFIG_FEM */ + + radio_test_start(&test_config); + + test_in_progress = true; + + shell_print(shell, "RX sweep"); + return 0; +} + +static int cmd_tx_sweep_start(const struct shell *shell, size_t argc, + char **argv) +{ + memset(&test_config, 0, sizeof(test_config)); + test_config.type = TX_SWEEP; + test_config.mode = config.mode; + test_config.params.tx_sweep.channel_start = config.channel_start; + test_config.params.tx_sweep.channel_end = config.channel_end; + test_config.params.tx_sweep.delay_ms = config.delay_ms; + test_config.params.tx_sweep.txpower = config.txpower; +#if CONFIG_FEM + test_config.fem = config.fem; +#endif /* CONFIG_FEM */ + + radio_test_start(&test_config); + + test_in_progress = true; + + shell_print(shell, "TX sweep"); + return 0; +} + +static int cmd_rx_start(const struct shell *shell, size_t argc, char **argv) +{ + if (test_in_progress) { + radio_test_cancel(); + test_in_progress = false; + } + + if (argc > 2) { + shell_error(shell, "%s: too many arguments", argv[0]); + return -EINVAL; + } + +#if CONFIG_HAS_HW_NRF_RADIO_IEEE802154 + ieee_channel_check(shell, config.channel_start); +#endif /* CONFIG_HAS_HW_NRF_RADIO_IEEE802154 */ + + memset(&test_config, 0, sizeof(test_config)); + test_config.type = RX; + test_config.mode = config.mode; + test_config.params.rx.channel = config.channel_start; + test_config.params.rx.pattern = config.tx_pattern; +#if CONFIG_FEM + test_config.fem = config.fem; +#endif /* CONFIG_FEM */ + + if (argc == 2) { + config.rx_packets_num = atoi(argv[1]); + test_config.params.rx.packets_num = config.rx_packets_num; + test_config.params.rx.cb = rx_end; + + if (config.rx_packets_num == 0) { + shell_error(shell, + "The number of packets to receive must be greater than zero."); + return -EINVAL; + } + } + + radio_test_start(&test_config); + + return 0; +} + +#if defined(RADIO_TXPOWER_TXPOWER_Pos10dBm) +static void cmd_pos10dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = 10; + shell_print(shell, "TX power: %d", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos10dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Pos9dBm) +static void cmd_pos9dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = 9; + shell_print(shell, "TX power: %d", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos9dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Pos8dBm) +static void cmd_pos8dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = 8; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos8dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Pos7dBm) +static void cmd_pos7dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = 7; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos7dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Pos6dBm) +static void cmd_pos6dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = 6; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos6dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Pos5dBm) +static void cmd_pos5dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = 5; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos5dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Pos4dBm) +static void cmd_pos4dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = 4; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos4dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Pos3dBm) +static void cmd_pos3dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = 3; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos3dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Pos2dBm) +static void cmd_pos2dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = 2; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos2dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Pos1dBm) +static void cmd_pos1dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = 1; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos1dBm) */ + +static void cmd_pos0dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = 0; + shell_print(shell, "TX power : %d dBm", config.txpower); +} + +#if defined(RADIO_TXPOWER_TXPOWER_Neg1dBm) +static void cmd_neg1dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -1; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg1dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Neg2dBm) +static void cmd_neg2dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -2; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg2dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Neg3dBm) +static void cmd_neg3dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -3; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg3dBm) */ + +static void cmd_neg4dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -4; + shell_print(shell, "TX power : %d dBm", config.txpower); +} + +#if defined(RADIO_TXPOWER_TXPOWER_Neg5dBm) +static void cmd_neg5dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -5; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg5dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Neg6dBm) +static void cmd_neg6dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -6; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg6dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Neg7dBm) +static void cmd_neg7dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -7; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg7dBm) */ + +static void cmd_neg8dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -8; + shell_print(shell, "TX power : %d dBm", config.txpower); +} + +#if defined(RADIO_TXPOWER_TXPOWER_Neg9dBm) +static void cmd_neg9dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -9; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg9dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Neg10dBm) +static void cmd_neg10dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -10; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg10dBm) */ + +static void cmd_neg12dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -12; + shell_print(shell, "TX power : %d dBm", config.txpower); +} + +#if defined(RADIO_TXPOWER_TXPOWER_Neg14dBm) +static void cmd_neg14dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -14; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg14dBm) */ + +static void cmd_neg16dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -16; + shell_print(shell, "TX power : %d dBm", config.txpower); +} + +#if defined(RADIO_TXPOWER_TXPOWER_Neg18dBm) +static void cmd_neg18dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -18; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg18dBm) */ + +static void cmd_neg20dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -20; + shell_print(shell, "TX power : %d dBm", config.txpower); +} + +#if defined(RADIO_TXPOWER_TXPOWER_Neg22dBm) +static void cmd_neg22dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -22; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg22dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Neg28dBm) +static void cmd_neg28dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -28; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg28dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Neg30dBm) +static void cmd_neg30dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -30; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg30dBm) */ + +static void cmd_neg40dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -40; + shell_print(shell, "TX power : %d dBm", config.txpower); +} + +#if defined(RADIO_TXPOWER_TXPOWER_Neg46dBm) +static void cmd_neg46dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -46; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg46dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Neg70dBm) +static void cmd_neg70dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -70; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg70dBm) */ + +#if defined(RADIO_TXPOWER_TXPOWER_Neg100dBm) +static void cmd_neg100dbm(const struct shell *shell, size_t argc, char **argv) +{ + config.txpower = -100; + shell_print(shell, "TX power : %d dBm", config.txpower); +} +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg100dBm) */ + +static int cmd_nrf_1mbit(const struct shell *shell, size_t argc, char **argv) +{ + config.mode = NRF_RADIO_MODE_NRF_1MBIT; + shell_print(shell, "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_1MBIT)); + + return 0; +} + +static int cmd_nrf_2mbit(const struct shell *shell, size_t argc, char **argv) +{ + config.mode = NRF_RADIO_MODE_NRF_2MBIT; + shell_print(shell, "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_2MBIT)); + + return 0; +} + +#if defined(RADIO_MODE_MODE_Nrf_250Kbit) +static int cmd_nrf_250kbit(const struct shell *shell, size_t argc, + char **argv) +{ + config.mode = NRF_RADIO_MODE_NRF_250KBIT; + shell_print(shell, "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_250KBIT)); + + return 0; +} +#endif /* defined(RADIO_MODE_MODE_Nrf_250Kbit) */ + +#if defined(RADIO_MODE_MODE_Nrf_4Mbit0_5) +static int cmd_nrf_4mbit_h_0_5(const struct shell *shell, size_t argc, + char **argv) +{ + config.mode = NRF_RADIO_MODE_NRF_4MBIT_H_0_5; + shell_print(shell, "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_4MBIT_H_0_5)); + + return 0; +} +#endif /* defined(RADIO_MODE_MODE_Nrf_4Mbit0_5) */ + +#if defined(RADIO_MODE_MODE_Nrf_4Mbit0_25) +static int cmd_nrf_4mbit_h_0_25(const struct shell *shell, size_t argc, + char **argv) +{ + config.mode = NRF_RADIO_MODE_NRF_4MBIT_H_0_25; + shell_print(shell, "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_4MBIT_H_0_25)); + + return 0; +} +#endif /* defined(RADIO_MODE_MODE_Nrf_4Mbit0_25) */ + +#if defined(RADIO_MODE_MODE_Nrf_4Mbit_0BT6) +static int cmd_nrf_4mbit_bt_0_6(const struct shell *shell, size_t argc, + char **argv) +{ + config.mode = NRF_RADIO_MODE_NRF_4MBIT_BT_0_6; + shell_print(shell, "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_4MBIT_BT_0_6)); + + return 0; +} +#endif /* defined(RADIO_MODE_MODE_Nrf_4Mbit_0BT6) */ + +#if defined(RADIO_MODE_MODE_Nrf_4Mbit_0BT4) +static int cmd_nrf_4mbit_bt_0_4(const struct shell *shell, size_t argc, + char **argv) +{ + config.mode = NRF_RADIO_MODE_NRF_4MBIT_BT_0_4; + shell_print(shell, "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_NRF_4MBIT_BT_0_4)); + + return 0; +} +#endif /* defined(RADIO_MODE_MODE_Nrf_4Mbit_0BT4) */ + +static int cmd_ble_1mbit(const struct shell *shell, size_t argc, char **argv) +{ + config.mode = NRF_RADIO_MODE_BLE_1MBIT; + shell_print(shell, "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_BLE_1MBIT)); + + return 0; +} + +static int cmd_ble_2mbit(const struct shell *shell, size_t argc, char **argv) +{ + config.mode = NRF_RADIO_MODE_BLE_2MBIT; + shell_print(shell, "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_BLE_2MBIT)); + + return 0; +} + +#if CONFIG_HAS_HW_NRF_RADIO_BLE_CODED +static int cmd_ble_lr125kbit(const struct shell *shell, size_t argc, + char **argv) +{ + config.mode = NRF_RADIO_MODE_BLE_LR125KBIT; + shell_print(shell, "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_BLE_LR125KBIT)); + + return 0; +} + +static int cmd_ble_lr500kbit(const struct shell *shell, size_t argc, + char **argv) +{ + config.mode = NRF_RADIO_MODE_BLE_LR500KBIT; + shell_print(shell, "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_BLE_LR500KBIT)); + + return 0; +} +#endif /* CONFIG_HAS_HW_NRF_RADIO_BLE_CODED */ + +#if CONFIG_HAS_HW_NRF_RADIO_IEEE802154 +static int cmd_ble_ieee(const struct shell *shell, size_t argc, char **argv) +{ + config.mode = NRF_RADIO_MODE_IEEE802154_250KBIT; + shell_print(shell, "Data rate: %s", + STRINGIFY(NRF_RADIO_MODE_IEEE802154_250KBIT)); + + return 0; +} +#endif /* CONFIG_HAS_HW_NRF_RADIO_IEEE802154 */ + +static int cmd_pattern_random(const struct shell *shell, size_t argc, + char **argv) +{ + config.tx_pattern = TRANSMIT_PATTERN_RANDOM; + shell_print(shell, + "Transmission pattern: %s", + STRINGIFY(TRANSMIT_PATTERN_RANDOM)); + + return 0; +} + +static int cmd_pattern_11110000(const struct shell *shell, size_t argc, + char **argv) +{ + config.tx_pattern = TRANSMIT_PATTERN_11110000; + shell_print(shell, + "Transmission pattern: %s", + STRINGIFY(TRANSMIT_PATTERN_11110000)); + + return 0; +} + +static int cmd_pattern_11001100(const struct shell *shell, size_t argc, + char **argv) +{ + config.tx_pattern = TRANSMIT_PATTERN_11001100; + shell_print(shell, + "Transmission pattern: %s", + STRINGIFY(TRANSMIT_PATTERN_11001100)); + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE(sub_data_rate, + SHELL_CMD(nrf_1Mbit, NULL, "1 Mbit/s Nordic proprietary radio mode", + cmd_nrf_1mbit), + SHELL_CMD(nrf_2Mbit, NULL, "2 Mbit/s Nordic proprietary radio mode", + cmd_nrf_2mbit), + +#if defined(RADIO_MODE_MODE_Nrf_250Kbit) + SHELL_CMD(nrf_250Kbit, NULL, + "250 kbit/s Nordic proprietary radio mode", + cmd_nrf_250kbit), +#endif /* defined(RADIO_MODE_MODE_Nrf_250Kbit) */ + +#if defined(RADIO_MODE_MODE_Nrf_4Mbit0_5) + SHELL_CMD(nrf_4Mbit0_5, NULL, + "4 Mbit/s Nordic proprietary radio mode (BT=0.5/h=0.5)", + cmd_nrf_4mbit_h_0_5), +#endif /* defined(RADIO_MODE_MODE_Nrf_4Mbit0_5) */ + +#if defined(RADIO_MODE_MODE_Nrf_4Mbit0_25) + SHELL_CMD(nrf_4Mbit0_25, NULL, + "4 Mbit/s Nordic proprietary radio mode (BT=0.5/h=0.25)", + cmd_nrf_4mbit_h_0_25), +#endif /* defined(RADIO_MODE_MODE_Nrf_4Mbit0_25) */ + +#if defined(RADIO_MODE_MODE_Nrf_4Mbit_0BT6) + SHELL_CMD(nrf_4Mbit_BT06, NULL, + "4 Mbps Nordic proprietary radio mode (BT=0.6/h=0.5)", + cmd_nrf_4mbit_bt_0_6), +#endif /* defined(RADIO_MODE_MODE_Nrf_4Mbit_0BT6) */ + +#if defined(RADIO_MODE_MODE_Nrf_4Mbit_0BT4) + SHELL_CMD(nrf_4Mbit_BT04, NULL, + "4 Mbps Nordic proprietary radio mode (BT=0.4/h=0.5)", + cmd_nrf_4mbit_bt_0_4), +#endif /* defined(RADIO_MODE_MODE_Nrf_4Mbit_0BT4) */ + + SHELL_CMD(ble_1Mbit, NULL, "1 Mbit/s Bluetooth Low Energy", + cmd_ble_1mbit), + SHELL_CMD(ble_2Mbit, NULL, "2 Mbit/s Bluetooth Low Energy", + cmd_ble_2mbit), + +#if CONFIG_HAS_HW_NRF_RADIO_BLE_CODED + SHELL_CMD(ble_lr125Kbit, NULL, + "Long range 125 kbit/s TX, 125 kbit/s and 500 kbit/s RX", + cmd_ble_lr125kbit), + + SHELL_CMD(ble_lr500Kbit, NULL, + "Long range 500 kbit/s TX, 125 kbit/s and 500 kbit/s RX", + cmd_ble_lr500kbit), +#endif /* CONFIG_HAS_HW_NRF_RADIO_BLE_CODED */ + +#if CONFIG_HAS_HW_NRF_RADIO_IEEE802154 + SHELL_CMD(ieee802154_250Kbit, NULL, "IEEE 802.15.4-2006 250 kbit/s", + cmd_ble_ieee), +#endif /* CONFIG_HAS_HW_NRF_RADIO_IEEE802154 */ + + SHELL_SUBCMD_SET_END +); + +static int cmd_print_payload(const struct shell *shell, size_t argc, + char **argv) +{ + struct radio_rx_stats rx_stats; + + memset(&rx_stats, 0, sizeof(rx_stats)); + + radio_rx_stats_get(&rx_stats); + + shell_print(shell, "Received payload:"); + shell_hexdump(shell, rx_stats.last_packet.buf, + rx_stats.last_packet.len); + shell_print(shell, "Number of packets: %d", rx_stats.packet_cnt); + + return 0; +} + +#if CONFIG_FEM +static int cmd_fem(const struct shell *shell, size_t argc, char **argv) +{ + if (argc == 1) { + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + } + + if (argc > 2) { + shell_error(shell, "%s: bad parameters count.", argv[0]); + return -EINVAL; + } + + if (argc == 2) { + shell_error(shell, "Uknown argument: %s.", argv[1]); + return -EINVAL; + } + + return 0; +} + +#if !CONFIG_RADIO_TEST_SD_POWER_CONTROL_AUTOMATIC +static int cmd_fem_tx_power_control_set(const struct shell *shell, size_t argc, + char **argv) +{ + uint32_t tx_power_control; + + if (argc == 1) { + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + } + + if (argc > 2) { + shell_error(shell, "%s: bad parameters count", argv[0]); + return -EINVAL; + } + + tx_power_control = atoi(argv[1]); + + config.fem.tx_power_control = tx_power_control; + + shell_print(shell, "Front-end module (FEM) Tx power control set to %u", tx_power_control); + + return 0; +} +#endif /* !CONFIG_RADIO_TEST_SD_POWER_CONTROL_AUTOMATIC */ + +static int cmd_fem_antenna_select(const struct shell *shell, size_t argc, + char **argv) +{ + if (argc == 1) { + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + } + + if (argc > 2) { + shell_error(shell, "%s: bad parameters count.", argv[0]); + return -EINVAL; + } + + if (argc == 2) { + shell_error(shell, "Uknown argument: %s.", argv[1]); + return -EINVAL; + } + + return 0; +} + +static int cmd_fem_antenna_1(const struct shell *shell, size_t argc, + char **argv) +{ + shell_print(shell, "ANT1 enabled, ANT2 disabled"); + + return fem_antenna_select(FEM_ANTENNA_1); +} + +static int cmd_fem_antenna_2(const struct shell *shell, size_t argc, + char **argv) +{ + shell_print(shell, "ANT1 disabled, ANT2 enabled"); + + return fem_antenna_select(FEM_ANTENNA_2); +} + +static int cmd_fem_ramp_up_set(const struct shell *shell, size_t argc, char **argv) +{ + uint32_t ramp_up_time; + + if (argc == 1) { + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + } + + if (argc > 2) { + shell_error(shell, "%s: bad parameters count", argv[0]); + return -EINVAL; + } + + ramp_up_time = atoi(argv[1]); + + config.fem.ramp_up_time = ramp_up_time; + + shell_print(shell, "Front-end module (FEM) radio rump-up time set to %d us", ramp_up_time); + + return 0; +} +#endif /* CONFIG_FEM */ + +SHELL_STATIC_SUBCMD_SET_CREATE(sub_output_power, +#if defined(RADIO_TXPOWER_TXPOWER_Pos10dBm) + SHELL_CMD(pos10dBm, NULL, "TX power: +10 dBm", cmd_pos10dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos10dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Pos9dBm) + SHELL_CMD(pos9dBm, NULL, "TX power: +9 dBm", cmd_pos9dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos9dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Pos8dBm) + SHELL_CMD(pos8dBm, NULL, "TX power: +8 dBm", cmd_pos8dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos8dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Pos7dBm) + SHELL_CMD(pos7dBm, NULL, "TX power: +7 dBm", cmd_pos7dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos7dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Pos6dBm) + SHELL_CMD(pos6dBm, NULL, "TX power: +6 dBm", cmd_pos6dbm), +#endif/* defined(RADIO_TXPOWER_TXPOWER_Pos6dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Pos5dBm) + SHELL_CMD(pos5dBm, NULL, "TX power: +5 dBm", cmd_pos5dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos5dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Pos4dBm) + SHELL_CMD(pos4dBm, NULL, "TX power: +4 dBm", cmd_pos4dbm), +#endif /* RADIO_TXPOWER_TXPOWER_Pos4dBm */ +#if defined(RADIO_TXPOWER_TXPOWER_Pos3dBm) + SHELL_CMD(pos3dBm, NULL, "TX power: +3 dBm", cmd_pos3dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos3dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Pos2dBm) + SHELL_CMD(pos2dBm, NULL, "TX power: +2 dBm", cmd_pos2dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos2dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Pos1dBm) + SHELL_CMD(pos1dBm, NULL, "TX power: +1 dBm", cmd_pos1dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Pos1dBm) */ + SHELL_CMD(pos0dBm, NULL, "TX power: 0 dBm", cmd_pos0dbm), +#if defined(RADIO_TXPOWER_TXPOWER_Neg1dBm) + SHELL_CMD(neg1dBm, NULL, "TX power: -1 dBm", cmd_neg1dbm), +#endif /* RADIO_TXPOWER_TXPOWER_Neg1dBm */ +#if defined(RADIO_TXPOWER_TXPOWER_Neg2dBm) + SHELL_CMD(neg2dBm, NULL, "TX power: -2 dBm", cmd_neg2dbm), +#endif /* RADIO_TXPOWER_TXPOWER_Neg2dBm */ +#if defined(RADIO_TXPOWER_TXPOWER_Neg3dBm) + SHELL_CMD(neg3dBm, NULL, "TX power: -3 dBm", cmd_neg3dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg3dBm) */ + SHELL_CMD(neg4dBm, NULL, "TX power: -4 dBm", cmd_neg4dbm), +#if defined(RADIO_TXPOWER_TXPOWER_Neg5dBm) + SHELL_CMD(neg5dBm, NULL, "TX power: -5 dBm", cmd_neg5dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg5dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Neg6dBm) + SHELL_CMD(neg6dBm, NULL, "TX power: -6 dBm", cmd_neg6dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg6dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Neg7dBm) + SHELL_CMD(neg7dBm, NULL, "TX power: -7 dBm", cmd_neg7dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg7dBm) */ + SHELL_CMD(neg8dBm, NULL, "TX power: -8 dBm", cmd_neg8dbm), +#if defined(RADIO_TXPOWER_TXPOWER_Neg9dBm) + SHELL_CMD(neg9dBm, NULL, "TX power: -9 dBm", cmd_neg9dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg9dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Neg10dBm) + SHELL_CMD(neg10dBm, NULL, "TX power: -10 dBm", cmd_neg10dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg10dBm) */ + SHELL_CMD(neg12dBm, NULL, "TX power: -12 dBm", cmd_neg12dbm), +#if defined(RADIO_TXPOWER_TXPOWER_Neg14dBm) + SHELL_CMD(neg14dBm, NULL, "TX power: -14 dBm", cmd_neg14dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg14dBm) */ + SHELL_CMD(neg16dBm, NULL, "TX power: -16 dBm", cmd_neg16dbm), +#if defined(RADIO_TXPOWER_TXPOWER_Neg18dBm) + SHELL_CMD(neg18dBm, NULL, "TX power: -18 dBm", cmd_neg18dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg18dBm) */ + SHELL_CMD(neg20dBm, NULL, "TX power: -20 dBm", cmd_neg20dbm), +#if defined(RADIO_TXPOWER_TXPOWER_Neg22dBm) + SHELL_CMD(neg22dBm, NULL, "TX power: -22 dBm", cmd_neg22dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg22dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Neg28dBm) + SHELL_CMD(neg28dBm, NULL, "TX power: -28 dBm", cmd_neg28dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg28dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Neg30dBm) + SHELL_CMD(neg30dBm, NULL, "TX power: -30 dBm", cmd_neg30dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg30dBm) */ + SHELL_CMD(neg40dBm, NULL, "TX power: -40 dBm", cmd_neg40dbm), +#if defined(RADIO_TXPOWER_TXPOWER_Neg46dBm) + SHELL_CMD(neg46dBm, NULL, "TX power: -46 dBm", cmd_neg46dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg46dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Neg70dBm) + SHELL_CMD(neg70dBm, NULL, "TX power: -70 dBm", cmd_neg70dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg70dBm) */ +#if defined(RADIO_TXPOWER_TXPOWER_Neg100dBm) + SHELL_CMD(neg100dBm, NULL, "TX power: -100 dBm", cmd_neg100dbm), +#endif /* defined(RADIO_TXPOWER_TXPOWER_Neg100dBm) */ + SHELL_SUBCMD_SET_END +); + +SHELL_STATIC_SUBCMD_SET_CREATE(sub_transmit_pattern, + SHELL_CMD(pattern_random, NULL, + "Set the transmission pattern to random.", + cmd_pattern_random), + SHELL_CMD(pattern_11110000, NULL, + "Set the transmission pattern to 11110000.", + cmd_pattern_11110000), + SHELL_CMD(pattern_11001100, NULL, + "Set the transmission pattern to 10101010.", + cmd_pattern_11001100), + SHELL_SUBCMD_SET_END +); + +#if CONFIG_FEM +SHELL_STATIC_SUBCMD_SET_CREATE(sub_fem_antenna, + SHELL_CMD(ant_1, NULL, + "ANT1 enabled, ANT2 disabled.", + cmd_fem_antenna_1), + SHELL_CMD(ant_2, NULL, + "ANT1 disabled, ANT2 enabled", + cmd_fem_antenna_2), + SHELL_SUBCMD_SET_END +); + +SHELL_STATIC_SUBCMD_SET_CREATE(sub_fem, +#if !CONFIG_RADIO_TEST_SD_POWER_CONTROL_AUTOMATIC + SHELL_CMD(tx_power_control, NULL, + "Set the front-end module (FEM) Tx power control specific to the FEM in use .", + cmd_fem_tx_power_control_set), +#endif /* !CONFIG_RADIO_TEST_SD_POWER_CONTROL_AUTOMATIC */ + SHELL_CMD(antenna, &sub_fem_antenna, + "Select the front-end module (FEM) antenna ", + cmd_fem_antenna_select), + SHELL_CMD(ramp_up_time, NULL, + "Set the front-end module (FEM) radio ramp-up time