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/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index 1fe46a72431b..b3b8b1d56a48 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -386,7 +386,13 @@ Zigbee samples Wi-Fi samples ------------- -|no_changes_yet_note| +* Added: + + * The :ref:`wifi_radio_test_sd` sample to demonstrate the Wi-Fi and Bluetooth LE radio test running on the application core. + +* Updated: + + * The :ref:`wifi_radio_test` sample is now moved to :zephyr_file:`samples/wifi/radio_test/multi_domain`. Other samples ------------- 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/bluetooth/throughput/src/cmds.c b/samples/bluetooth/throughput/src/cmds.c index c95d285154fe..d4f4e52359c6 100644 --- a/samples/bluetooth/throughput/src/cmds.c +++ b/samples/bluetooth/throughput/src/cmds.c @@ -76,7 +76,7 @@ static int default_cmd(const struct shell *shell, size_t argc, } if (argc == 2) { - shell_error(shell, "Uknown argument: %s", argv[1]); + shell_error(shell, "Unknown argument: %s", argv[1]); return -EINVAL; } diff --git a/samples/cellular/modem_shell/src/link/link_settings.c b/samples/cellular/modem_shell/src/link/link_settings.c index faddf1b13426..4498ac756b92 100644 --- a/samples/cellular/modem_shell/src/link/link_settings.c +++ b/samples/cellular/modem_shell/src/link/link_settings.c @@ -421,7 +421,7 @@ int link_sett_save_defcontauth_prot(int auth_prot) err = link_shell_pdn_auth_prot_to_pdn_lib_method_map(auth_prot, &method); if (err) { - mosh_error("Uknown auth protocol %d", auth_prot); + mosh_error("Unknown auth protocol %d", auth_prot); return -EINVAL; } diff --git a/samples/peripheral/radio_test/src/radio_cmd.c b/samples/peripheral/radio_test/src/radio_cmd.c index cd2659a4a23a..8b73a26e1aaa 100644 --- a/samples/peripheral/radio_test/src/radio_cmd.c +++ b/samples/peripheral/radio_test/src/radio_cmd.c @@ -207,7 +207,7 @@ static int cmd_data_rate_set(const struct shell *shell, size_t argc, } if (argc == 2) { - shell_error(shell, "Uknown argument: %s", argv[1]); + shell_error(shell, "Unknown argument: %s", argv[1]); return -EINVAL; } @@ -418,7 +418,7 @@ static int cmd_output_power_set(const struct shell *shell, size_t argc, } if (argc == 2) { - shell_error(shell, "Uknown argument: %s", argv[1]); + shell_error(shell, "Unknown argument: %s", argv[1]); return -EINVAL; } @@ -439,7 +439,7 @@ static int cmd_transmit_pattern_set(const struct shell *shell, size_t argc, } if (argc == 2) { - shell_error(shell, "Uknown argument: %s.", argv[1]); + shell_error(shell, "Unknown argument: %s.", argv[1]); return -EINVAL; } @@ -1173,7 +1173,7 @@ static int cmd_fem(const struct shell *shell, size_t argc, char **argv) } if (argc == 2) { - shell_error(shell, "Uknown argument: %s.", argv[1]); + shell_error(shell, "Unknown argument: %s.", argv[1]); return -EINVAL; } @@ -1220,7 +1220,7 @@ static int cmd_fem_antenna_select(const struct shell *shell, size_t argc, } if (argc == 2) { - shell_error(shell, "Uknown argument: %s.", argv[1]); + shell_error(shell, "Unknown argument: %s.", argv[1]); return -EINVAL; } 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..104fecff0850 --- /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, "Unknown 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, "Unknown 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, "Unknown 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, "Unknown 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, "Unknown 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