From a61c4ef5d76830d86c356894a4b25c3abf3c9da2 Mon Sep 17 00:00:00 2001 From: Nikodem Kastelik Date: Fri, 26 Jul 2024 15:08:58 +0200 Subject: [PATCH 01/51] [nrf fromtree] manifest: update hal_nordic revision to integrate nrfx 3.6.0 New nrfx version contains support for nRF54L20 Eng A and nRF9230 Eng B devices. Signed-off-by: Nikodem Kastelik (cherry picked from commit 8c9ac0038e17bfb9d63e724eb59af3c7338609b9) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index b77d741b71d7..643cb862e52c 100644 --- a/west.yml +++ b/west.yml @@ -183,7 +183,7 @@ manifest: groups: - hal - name: hal_nordic - revision: ab5cb2e2faeb1edfad7a25286dcb513929ae55da + revision: d4030afcc0befef645eda11e82c0d7c0e1d604f1 path: modules/hal/nordic groups: - hal From 7817e9ab48c8e454dd9aed3727e37bf97be72e74 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Thu, 13 Jun 2024 00:31:24 +0530 Subject: [PATCH 02/51] [nrf fromtree] dts: bindings: wifi: Add nRF70 Wi-Fi support Add necessary bindings for the nRF70 Wi-Fi chips from Nordic semiconductors ASA. Signed-off-by: Chaitanya Tata (cherry picked from commit 1d18144e64aaf1729efcf2eaa784a9a14d33b7ed) --- dts/bindings/wifi/nordic,nrf70-coex.yaml | 35 ++++++++++++++++++ dts/bindings/wifi/nordic,nrf70-qspi.yaml | 43 ++++++++++++++++++++++ dts/bindings/wifi/nordic,nrf70-spi.yaml | 6 +++ dts/bindings/wifi/nordic,nrf70.yaml | 21 +++++++++++ dts/bindings/wifi/nordic,nrf7000-qspi.yaml | 11 ++++++ dts/bindings/wifi/nordic,nrf7000-spi.yaml | 11 ++++++ dts/bindings/wifi/nordic,nrf7001-qspi.yaml | 10 +++++ dts/bindings/wifi/nordic,nrf7001-spi.yaml | 10 +++++ dts/bindings/wifi/nordic,nrf7002-qspi.yaml | 11 ++++++ dts/bindings/wifi/nordic,nrf7002-spi.yaml | 11 ++++++ dts/bindings/wifi/wifi-tx-power-2g.yaml | 23 ++++++++++++ dts/bindings/wifi/wifi-tx-power-5g.yaml | 39 ++++++++++++++++++++ 12 files changed, 231 insertions(+) create mode 100644 dts/bindings/wifi/nordic,nrf70-coex.yaml create mode 100644 dts/bindings/wifi/nordic,nrf70-qspi.yaml create mode 100644 dts/bindings/wifi/nordic,nrf70-spi.yaml create mode 100644 dts/bindings/wifi/nordic,nrf70.yaml create mode 100644 dts/bindings/wifi/nordic,nrf7000-qspi.yaml create mode 100644 dts/bindings/wifi/nordic,nrf7000-spi.yaml create mode 100644 dts/bindings/wifi/nordic,nrf7001-qspi.yaml create mode 100644 dts/bindings/wifi/nordic,nrf7001-spi.yaml create mode 100644 dts/bindings/wifi/nordic,nrf7002-qspi.yaml create mode 100644 dts/bindings/wifi/nordic,nrf7002-spi.yaml create mode 100644 dts/bindings/wifi/wifi-tx-power-2g.yaml create mode 100644 dts/bindings/wifi/wifi-tx-power-5g.yaml diff --git a/dts/bindings/wifi/nordic,nrf70-coex.yaml b/dts/bindings/wifi/nordic,nrf70-coex.yaml new file mode 100644 index 000000000000..208657f3fb53 --- /dev/null +++ b/dts/bindings/wifi/nordic,nrf70-coex.yaml @@ -0,0 +1,35 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: | + This is a representation of an external radio coexistence setup for coexistence + with nRF70 WiFi chips. + +properties: + req-gpios: + type: phandle-array + description: | + GPIO of the SOC connected to the PTA's REQUEST pin. + + status0-gpios: + type: phandle-array + description: | + GPIO of the SOC connected to the PTA's PRIORITY pin. + This GPIO is also used to indicate direction (TX/RX). + + grant-gpios: + type: phandle-array + description: | + GPIO of the SOC connected to the PTA's GRANT pin. + + swctrl1-gpios: + type: phandle-array + description: | + GPIO of the SOC controlling the Priority (STATUS1) pin (in 4-wire + coex case) of the nRF7002 + + srrf-switch-gpios: + type: phandle-array + description: | + GPIO of the RF Switch to control SR RF output to either SR Antenna + or shared Antenna with Wi-Fi diff --git a/dts/bindings/wifi/nordic,nrf70-qspi.yaml b/dts/bindings/wifi/nordic,nrf70-qspi.yaml new file mode 100644 index 000000000000..87a98d5b3583 --- /dev/null +++ b/dts/bindings/wifi/nordic,nrf70-qspi.yaml @@ -0,0 +1,43 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: This is a representation of the nRF70 Wi-Fi chip. + +include: ["nordic,nrf70.yaml", "base.yaml"] + +on-bus: qspi + +properties: + qspi-frequency: + type: int + required: true + description: | + Maximum clock speed (in Hz) supported by the device. + + reg: + required: true + + qspi-quad-mode: + type: boolean + description: | + If specified, Use QSPI in quad mode (4 IO lines) otherwise in + SPI mode (2 IO lines - MOSI & MISO). + + qspi-rx-delay: + type: int + default: 0 + description: | + Number of clock cycles from the rising edge of the SPI clock + until the input serial data is sampled. + + qspi-cpha: + type: boolean + description: | + Set to indicate phase starts with asserted half-phase (CPHA=1). + The driver using this property must also use `cpol`. + + qspi-cpol: + type: boolean + description: | + Set to indicate that the clock leading edge is falling (CPOL=1). + The driver using this property requires also use `cpha`. diff --git a/dts/bindings/wifi/nordic,nrf70-spi.yaml b/dts/bindings/wifi/nordic,nrf70-spi.yaml new file mode 100644 index 000000000000..a0363f1b1a48 --- /dev/null +++ b/dts/bindings/wifi/nordic,nrf70-spi.yaml @@ -0,0 +1,6 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: This is a representation of the nRF70 Wi-Fi chip. + +include: ["nordic,nrf70.yaml", "spi-device.yaml"] diff --git a/dts/bindings/wifi/nordic,nrf70.yaml b/dts/bindings/wifi/nordic,nrf70.yaml new file mode 100644 index 000000000000..ff9021739ace --- /dev/null +++ b/dts/bindings/wifi/nordic,nrf70.yaml @@ -0,0 +1,21 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +# GPIO lines for controlling the nRF70 Series Wi-Fi chip. +include: nordic,nrf70-coex.yaml + +properties: + iovdd-ctrl-gpios: + type: phandle-array + required: true + description: GPIO of the SoC controlling IO_VDD Control pin of the nRF70 + + bucken-gpios: + type: phandle-array + required: true + description: GPIO of the SoC controlling BUCK_EN pin of the nRF70 + + host-irq-gpios: + type: phandle-array + required: true + description: GPIO of the SoC controlling the HOST_IRQ pin of the nRF70 diff --git a/dts/bindings/wifi/nordic,nrf7000-qspi.yaml b/dts/bindings/wifi/nordic,nrf7000-qspi.yaml new file mode 100644 index 000000000000..f25663749e8a --- /dev/null +++ b/dts/bindings/wifi/nordic,nrf7000-qspi.yaml @@ -0,0 +1,11 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: This is a representation of the nRF7002 Wi-Fi chip with QSPI interface. + +compatible: nordic,nrf7000-qspi + +include: + - "nordic,nrf70-qspi.yaml" + - "wifi-tx-power-2g.yaml" + - "wifi-tx-power-5g.yaml" diff --git a/dts/bindings/wifi/nordic,nrf7000-spi.yaml b/dts/bindings/wifi/nordic,nrf7000-spi.yaml new file mode 100644 index 000000000000..3c5710a38f24 --- /dev/null +++ b/dts/bindings/wifi/nordic,nrf7000-spi.yaml @@ -0,0 +1,11 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: This is a representation of the nRF7002 Wi-Fi chip with SPI interface. + +compatible: nordic,nrf7000-spi + +include: + - "nordic,nrf70-spi.yaml" + - "wifi-tx-power-2g.yaml" + - "wifi-tx-power-5g.yaml" diff --git a/dts/bindings/wifi/nordic,nrf7001-qspi.yaml b/dts/bindings/wifi/nordic,nrf7001-qspi.yaml new file mode 100644 index 000000000000..1961677fad2d --- /dev/null +++ b/dts/bindings/wifi/nordic,nrf7001-qspi.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: This is a representation of the nRF7002 Wi-Fi chip with QSPI interface. + +compatible: nordic,nrf7001-qspi + +include: + - "nordic,nrf70-qspi.yaml" + - "wifi-tx-power-2g.yaml" diff --git a/dts/bindings/wifi/nordic,nrf7001-spi.yaml b/dts/bindings/wifi/nordic,nrf7001-spi.yaml new file mode 100644 index 000000000000..5c516945e442 --- /dev/null +++ b/dts/bindings/wifi/nordic,nrf7001-spi.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: This is a representation of the nRF7002 Wi-Fi chip with SPI interface. + +compatible: nordic,nrf7001-spi + +include: + - "nordic,nrf70-spi.yaml" + - "wifi-tx-power-2g.yaml" diff --git a/dts/bindings/wifi/nordic,nrf7002-qspi.yaml b/dts/bindings/wifi/nordic,nrf7002-qspi.yaml new file mode 100644 index 000000000000..101bcfce11f0 --- /dev/null +++ b/dts/bindings/wifi/nordic,nrf7002-qspi.yaml @@ -0,0 +1,11 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: This is a representation of the nRF7002 Wi-Fi chip with QSPI interface. + +compatible: nordic,nrf7002-qspi + +include: + - "nordic,nrf70-qspi.yaml" + - "wifi-tx-power-2g.yaml" + - "wifi-tx-power-5g.yaml" diff --git a/dts/bindings/wifi/nordic,nrf7002-spi.yaml b/dts/bindings/wifi/nordic,nrf7002-spi.yaml new file mode 100644 index 000000000000..af12744161a1 --- /dev/null +++ b/dts/bindings/wifi/nordic,nrf7002-spi.yaml @@ -0,0 +1,11 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: This is a representation of the nRF7002 Wi-Fi chip with SPI interface. + +compatible: nordic,nrf7002-spi + +include: + - "nordic,nrf70-spi.yaml" + - "wifi-tx-power-2g.yaml" + - "wifi-tx-power-5g.yaml" diff --git a/dts/bindings/wifi/wifi-tx-power-2g.yaml b/dts/bindings/wifi/wifi-tx-power-2g.yaml new file mode 100644 index 000000000000..967cee439812 --- /dev/null +++ b/dts/bindings/wifi/wifi-tx-power-2g.yaml @@ -0,0 +1,23 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: | + This is a representation of the Wi-Fi transmit power ceilings + i.e., maximum transmit power allowed for 2.4GHz band. Based on the + RF performance of the device, the transmit power can be limited + for different data rates and sub-bands to get the best performance. + Resolution is 1 dBm + +properties: + wifi-max-tx-pwr-2g-dsss: + type: int + required: true + description: Maximum transmit power allowed in 2.4GHz band for DSSS/CCK rates + wifi-max-tx-pwr-2g-mcs0: + type: int + required: true + description: Maximum transmit power allowed in 2.4GHz band for MCS0 + wifi-max-tx-pwr-2g-mcs7: + type: int + required: true + description: Maximum transmit power allowed in 2.4GHz band for MCS7 diff --git a/dts/bindings/wifi/wifi-tx-power-5g.yaml b/dts/bindings/wifi/wifi-tx-power-5g.yaml new file mode 100644 index 000000000000..b9af40e0b9f6 --- /dev/null +++ b/dts/bindings/wifi/wifi-tx-power-5g.yaml @@ -0,0 +1,39 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: | + This is a representation of the Wi-Fi transmit power ceilings + i.e., maximum transmit power allowed for 5GHz band. Based on the + RF performance of the device, the transmit power can be limited + for different data rates and sub-bands to get the best performance. + + low sub-band: Channels 36-64 + mid sub-band: Channels 96-132 + high sub-band: Channels 136-177 + + Resolution is 1 dBm + +properties: + wifi-max-tx-pwr-5g-low-mcs0: + required: true + type: int + description: Maximum transmit power allowed in 5GHz band MCS0 low sub-band + wifi-max-tx-pwr-5g-low-mcs7: + required: true + type: int + description: Maximum transmit power allowed in 5GHz band MCS7 low sub-band + wifi-max-tx-pwr-5g-mid-mcs0: + required: true + type: int + description: Maximum transmit power allowed in 5GHz band MCS0 mid sub-band + wifi-max-tx-pwr-5g-mid-mcs7: + required: true + type: int + description: Maximum transmit power allowed in 5GHz band MCS7 mid sub-band + wifi-max-tx-pwr-5g-high-mcs0: + required: true + type: int + description: Maximum transmit power allowed in 5GHz band MCS0 high sub-band + wifi-max-tx-pwr-5g-high-mcs7: + type: int + description: Maximum transmit power allowed in 5GHz band MCS7 high sub-band From 41572817d205f7daaf47f1b404bc9ae1fbb97a73 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 12 Jun 2024 23:59:17 +0530 Subject: [PATCH 03/51] [nrf fromtree] manifest: hal_nordic: Pull nRF70 OSAL driver This pulls in OS agnostic parts of the nRF70 Wi-Fi driver. Signed-off-by: Chaitanya Tata (cherry picked from commit d1f2e7d6e7ef68c6f52edfd384ddd1f7381806c3) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 643cb862e52c..1272e32b1bcd 100644 --- a/west.yml +++ b/west.yml @@ -183,7 +183,7 @@ manifest: groups: - hal - name: hal_nordic - revision: d4030afcc0befef645eda11e82c0d7c0e1d604f1 + revision: bda16da6f92380579b8cf681a5faf61e1fe55220 path: modules/hal/nordic groups: - hal From a6e446ac05866a3630b6b5c5f54e8a54f126438a Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Thu, 13 Jun 2024 00:02:02 +0530 Subject: [PATCH 04/51] [nrf fromtree] drivers: wifi: Add nRF70 Wi-Fi driver Driver for Nordic nRF70 Wi-Fi6 companion chipset, depends on hal_nordic/nrf_wifi for OS agnostic part of the driver. This supports (Q)SPI interface to communicate from host to chip. Signed-off-by: Chaitanya Tata (cherry picked from commit 638ce2fbfd0fa897ed7174794b59c62eb65d550f) --- MAINTAINERS.yml | 21 + drivers/wifi/CMakeLists.txt | 1 + drivers/wifi/Kconfig | 1 + drivers/wifi/nrfwifi/CMakeLists.txt | 288 ++ drivers/wifi/nrfwifi/Kconfig.nrfwifi | 670 ++++ drivers/wifi/nrfwifi/inc/coex.h | 84 + drivers/wifi/nrfwifi/inc/coex_struct.h | 182 ++ drivers/wifi/nrfwifi/inc/fmac_main.h | 133 + drivers/wifi/nrfwifi/inc/net_if.h | 64 + drivers/wifi/nrfwifi/inc/wifi_mgmt.h | 75 + drivers/wifi/nrfwifi/inc/wifi_mgmt_scan.h | 34 + drivers/wifi/nrfwifi/inc/wpa_supp_if.h | 143 + drivers/wifi/nrfwifi/rpu_fw_patches.ld | 51 + drivers/wifi/nrfwifi/src/coex.c | 333 ++ drivers/wifi/nrfwifi/src/fmac_main.c | 933 ++++++ drivers/wifi/nrfwifi/src/fw_load.c | 85 + drivers/wifi/nrfwifi/src/net_if.c | 1145 +++++++ drivers/wifi/nrfwifi/src/qspi/inc/ficr_prog.h | 23 + drivers/wifi/nrfwifi/src/qspi/inc/qspi_if.h | 116 + drivers/wifi/nrfwifi/src/qspi/inc/rpu_hw_if.h | 61 + drivers/wifi/nrfwifi/src/qspi/inc/spi_if.h | 36 + drivers/wifi/nrfwifi/src/qspi/inc/spi_nor.h | 49 + drivers/wifi/nrfwifi/src/qspi/src/device.c | 86 + drivers/wifi/nrfwifi/src/qspi/src/ficr_prog.c | 379 +++ drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c | 1327 ++++++++ drivers/wifi/nrfwifi/src/qspi/src/rpu_hw_if.c | 489 +++ drivers/wifi/nrfwifi/src/qspi/src/spi_if.c | 343 ++ drivers/wifi/nrfwifi/src/shim.c | 969 ++++++ drivers/wifi/nrfwifi/src/shim.h | 68 + drivers/wifi/nrfwifi/src/timer.c | 43 + drivers/wifi/nrfwifi/src/timer.h | 26 + drivers/wifi/nrfwifi/src/wifi_mgmt.c | 1021 ++++++ drivers/wifi/nrfwifi/src/wifi_mgmt_scan.c | 447 +++ drivers/wifi/nrfwifi/src/wifi_util.c | 1005 ++++++ drivers/wifi/nrfwifi/src/wifi_util.h | 24 + drivers/wifi/nrfwifi/src/work.c | 158 + drivers/wifi/nrfwifi/src/work.h | 44 + drivers/wifi/nrfwifi/src/wpa_supp_if.c | 2761 +++++++++++++++++ scripts/ci/check_compliance.py | 1 + 39 files changed, 13719 insertions(+) create mode 100644 drivers/wifi/nrfwifi/CMakeLists.txt create mode 100644 drivers/wifi/nrfwifi/Kconfig.nrfwifi create mode 100644 drivers/wifi/nrfwifi/inc/coex.h create mode 100644 drivers/wifi/nrfwifi/inc/coex_struct.h create mode 100644 drivers/wifi/nrfwifi/inc/fmac_main.h create mode 100644 drivers/wifi/nrfwifi/inc/net_if.h create mode 100644 drivers/wifi/nrfwifi/inc/wifi_mgmt.h create mode 100644 drivers/wifi/nrfwifi/inc/wifi_mgmt_scan.h create mode 100644 drivers/wifi/nrfwifi/inc/wpa_supp_if.h create mode 100644 drivers/wifi/nrfwifi/rpu_fw_patches.ld create mode 100644 drivers/wifi/nrfwifi/src/coex.c create mode 100644 drivers/wifi/nrfwifi/src/fmac_main.c create mode 100644 drivers/wifi/nrfwifi/src/fw_load.c create mode 100644 drivers/wifi/nrfwifi/src/net_if.c create mode 100644 drivers/wifi/nrfwifi/src/qspi/inc/ficr_prog.h create mode 100644 drivers/wifi/nrfwifi/src/qspi/inc/qspi_if.h create mode 100644 drivers/wifi/nrfwifi/src/qspi/inc/rpu_hw_if.h create mode 100644 drivers/wifi/nrfwifi/src/qspi/inc/spi_if.h create mode 100644 drivers/wifi/nrfwifi/src/qspi/inc/spi_nor.h create mode 100644 drivers/wifi/nrfwifi/src/qspi/src/device.c create mode 100644 drivers/wifi/nrfwifi/src/qspi/src/ficr_prog.c create mode 100644 drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c create mode 100644 drivers/wifi/nrfwifi/src/qspi/src/rpu_hw_if.c create mode 100644 drivers/wifi/nrfwifi/src/qspi/src/spi_if.c create mode 100644 drivers/wifi/nrfwifi/src/shim.c create mode 100644 drivers/wifi/nrfwifi/src/shim.h create mode 100644 drivers/wifi/nrfwifi/src/timer.c create mode 100644 drivers/wifi/nrfwifi/src/timer.h create mode 100644 drivers/wifi/nrfwifi/src/wifi_mgmt.c create mode 100644 drivers/wifi/nrfwifi/src/wifi_mgmt_scan.c create mode 100644 drivers/wifi/nrfwifi/src/wifi_util.c create mode 100644 drivers/wifi/nrfwifi/src/wifi_util.h create mode 100644 drivers/wifi/nrfwifi/src/work.c create mode 100644 drivers/wifi/nrfwifi/src/work.h create mode 100644 drivers/wifi/nrfwifi/src/wpa_supp_if.c diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index c24eb2e290cc..46ce64ffa507 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -1951,6 +1951,27 @@ Release Notes: labels: - "area: Wi-Fi" +"Drivers: Wi-Fi as nRF Wi-Fi": + status: maintained + maintainers: + - krish2718 + - jukkar + files: + - drivers/wifi/nrfwifi/ + - dts/bindings/wifi/nordic,nrf70.yaml + - dts/bindings/wifi/nordic,nrf70-qspi.yaml + - dts/bindings/wifi/nordic,nrf70-spi.yaml + - dts/bindings/wifi/nordic,nrf70-coex.yaml + - dts/bindings/wifi/nordic,nrf7002-qspi.yaml + - dts/bindings/wifi/nordic,nrf7002-spi.yaml + - dts/bindings/wifi/nordic,nrf7000-qspi.yaml + - dts/bindings/wifi/nordic,nrf7000-spi.yaml + - dts/bindings/wifi/nordic,nrf7001-qspi.yaml + - dts/bindings/wifi/nordic,nrf7001-spi.yaml + - boards/shields/nrf7002ek/ + labels: + - "area: Wi-Fi" + "Drivers: Memory Management": status: maintained maintainers: diff --git a/drivers/wifi/CMakeLists.txt b/drivers/wifi/CMakeLists.txt index df1e7c394653..78a91d480cfd 100644 --- a/drivers/wifi/CMakeLists.txt +++ b/drivers/wifi/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory_ifdef(CONFIG_WIFI_ESWIFI eswifi) add_subdirectory_ifdef(CONFIG_WIFI_SIMPLELINK simplelink) add_subdirectory_ifdef(CONFIG_WIFI_WINC1500 winc1500) add_subdirectory_ifdef(CONFIG_WIFI_AIROC infineon) +add_subdirectory_ifdef(CONFIG_WIFI_NRF70 nrfwifi) diff --git a/drivers/wifi/Kconfig b/drivers/wifi/Kconfig index d7133f5be040..769a23b4eb64 100644 --- a/drivers/wifi/Kconfig +++ b/drivers/wifi/Kconfig @@ -41,5 +41,6 @@ source "drivers/wifi/eswifi/Kconfig.eswifi" source "drivers/wifi/esp_at/Kconfig.esp_at" source "drivers/wifi/esp32/Kconfig.esp32" source "drivers/wifi/infineon/Kconfig.airoc" +source "drivers/wifi/nrfwifi/Kconfig.nrfwifi" endif # WIFI diff --git a/drivers/wifi/nrfwifi/CMakeLists.txt b/drivers/wifi/nrfwifi/CMakeLists.txt new file mode 100644 index 000000000000..4ba57b417ac4 --- /dev/null +++ b/drivers/wifi/nrfwifi/CMakeLists.txt @@ -0,0 +1,288 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +zephyr_library() + +set(OS_AGNOSTIC_BASE ${ZEPHYR_HAL_NORDIC_MODULE_DIR}/drivers/nrf_wifi) +set(FW_BINS_BASE ${ZEPHYR_HAL_NORDIC_MODULE_DIR}/zephyr/blobs/wifi_fw_bins) + +zephyr_include_directories( + inc + ${OS_AGNOSTIC_BASE}/utils/inc + ${OS_AGNOSTIC_BASE}/os_if/inc + ${OS_AGNOSTIC_BASE}/bus_if/bus/qspi/inc + ${OS_AGNOSTIC_BASE}/bus_if/bal/inc + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/inc + ${OS_AGNOSTIC_BASE}/fw_load/mips/fw/inc + ${OS_AGNOSTIC_BASE}/hw_if/hal/inc + src/qspi/inc + # for net_sprint_ll_addr + ${ZEPHYR_BASE}/subsys/net/ip + ${OS_AGNOSTIC_BASE}/hw_if/hal/inc/fw + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/inc/fw +) + +zephyr_include_directories_ifdef(CONFIG_NRF70_RADIO_TEST + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/inc/radio_test +) + +zephyr_include_directories_ifndef(CONFIG_NRF70_RADIO_TEST + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/inc/default +) + +zephyr_library_sources_ifdef(CONFIG_NRF70_SR_COEX + src/coex.c +) + +zephyr_library_sources( + ${OS_AGNOSTIC_BASE}/os_if/src/osal.c + ${OS_AGNOSTIC_BASE}/utils/src/list.c + ${OS_AGNOSTIC_BASE}/utils/src/queue.c + ${OS_AGNOSTIC_BASE}/utils/src/util.c + ${OS_AGNOSTIC_BASE}/hw_if/hal/src/hal_api.c + ${OS_AGNOSTIC_BASE}/hw_if/hal/src/hal_fw_patch_loader.c + ${OS_AGNOSTIC_BASE}/hw_if/hal/src/hal_interrupt.c + ${OS_AGNOSTIC_BASE}/hw_if/hal/src/hal_mem.c + ${OS_AGNOSTIC_BASE}/hw_if/hal/src/hal_reg.c + ${OS_AGNOSTIC_BASE}/hw_if/hal/src/hpqm.c + ${OS_AGNOSTIC_BASE}/hw_if/hal/src/pal.c + ${OS_AGNOSTIC_BASE}/bus_if/bal/src/bal.c + ${OS_AGNOSTIC_BASE}/bus_if/bus/qspi/src/qspi.c + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/cmd.c + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/event.c + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/fmac_api_common.c + src/shim.c + src/work.c + src/timer.c + src/fmac_main.c + src/fw_load.c + src/qspi/src/device.c + src/qspi/src/rpu_hw_if.c + src/qspi/src/ficr_prog.c +) + +zephyr_library_sources_ifndef(CONFIG_NRF70_RADIO_TEST + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/rx.c + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/fmac_vif.c + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/fmac_util.c + src/net_if.c + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/default/fmac_api.c +) + +zephyr_library_sources_ifdef(CONFIG_NET_L2_WIFI_MGMT + src/wifi_mgmt_scan.c +) + +zephyr_library_sources_ifdef(CONFIG_NRF70_SYSTEM_MODE + src/wifi_mgmt.c +) + +zephyr_library_sources_ifdef(CONFIG_NRF70_SCAN_ONLY + src/wifi_mgmt_scan.c +) + +zephyr_library_sources_ifdef(CONFIG_NRF70_RADIO_TEST + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/radio_test/fmac_api.c + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/fmac_util.c +) + +zephyr_library_sources_ifdef(CONFIG_NRF70_DATA_TX + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/tx.c + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/fmac_peer.c +) + +zephyr_library_sources_ifdef(CONFIG_NRF70_STA_MODE + src/wpa_supp_if.c + src/wifi_mgmt.c + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/fmac_peer.c + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/fmac_util.c +) + +zephyr_library_sources_ifdef(CONFIG_NRF70_AP_MODE + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/fmac_ap.c +) + + +# Without WPA supplicant we only support scan +zephyr_library_sources_ifdef(CONFIG_NRF70_STA_MODE + src/wpa_supp_if.c + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/fmac_peer.c + ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/fmac_util.c +) + +zephyr_library_sources_ifdef(CONFIG_NRF70_ON_QSPI + src/qspi/src/qspi_if.c +) + +zephyr_library_sources_ifdef(CONFIG_NRF70_ON_SPI + src/qspi/src/spi_if.c +) + +zephyr_library_sources_ifdef(CONFIG_NRF70_UTIL + src/wifi_util.c +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_ON_QSPI +# These are XIP related anomalies and aren't applicable for nRF7002 and cause +# throughput issues. + -DNRF53_ERRATA_43_ENABLE_WORKAROUND=0 + -DNRF52_ERRATA_215_ENABLE_WORKAROUND=0 +# nRF70 QSPI doesn't use 192MHz clock and most samples use 128MHz, this can cause anomaly 159 +# but as its rare and not seen in most cases, we can disable it. +# Alternative is 128MHz CPU should be disabled that impacts Wi-Fi performance. + -DNRF53_ERRATA_159_ENABLE_WORKAROUND=0 +) + +# RPU FW patch binaries based on the selected configuration +if(CONFIG_NRF70_SYSTEM_MODE) + set(NRF70_PATCH ${FW_BINS_BASE}/default/nrf70.bin) +elseif(CONFIG_NRF70_RADIO_TEST) + set(NRF70_PATCH ${FW_BINS_BASE}/radio_test/nrf70.bin) +elseif(CONFIG_NRF70_SCAN_ONLY) + set(NRF70_PATCH ${FW_BINS_BASE}/scan_only/nrf70.bin) +elseif (CONFIG_NRF70_SYSTEM_WITH_RAW_MODES) + set(NRF70_PATCH ${FW_BINS_BASE}/system_with_raw/nrf70.bin) +else() + # Error + message(FATAL_ERROR "Unsupported nRF70 patch configuration") +endif() + +if(NOT EXISTS ${NRF70_PATCH}) + message(FATAL_ERROR " + ------------------------------------------------------------------------ + Missing blobs for nRF70 device driver, please install by running: + $ west update + $ west blobs fetch hal_nordic + ------------------------------------------------------------------------") +endif() + +zephyr_compile_definitions( + -DCONFIG_NRF_WIFI_FW_BIN=${NRF70_PATCH} +) + +# Translate the configuration to the OS agnostic code +zephyr_compile_definitions_ifdef(CONFIG_NRF_WIFI_LOW_POWER + -DNRF_WIFI_LOW_POWER +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF_WIFI_RPU_RECOVERY + -DNRF_WIFI_RPU_RECOVERY +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF_WIFI_AP_DEAD_DETECT_TIMEOUT + -DNRF_WIFI_AP_DEAD_DETECT_TIMEOUT=${CONFIG_NRF_WIFI_AP_DEAD_DETECT_TIMEOUT} +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF_WIFI_IFACE_MTU + -DNRF_WIFI_IFACE_MTU=${CONFIG_NRF_WIFI_IFACE_MTU} +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_STA_MODE + -DNRF70_STA_MODE +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_DATA_TX + -DNRF70_DATA_TX +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_RAW_DATA_TX + -DNRF70_RAW_DATA_TX +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_RAW_DATA_RX + -DNRF70_RAW_DATA_RX +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_PROMISC_DATA_RX + -DNRF70_PROMISC_DATA_RX +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_TX_DONE_WQ_ENABLED + -DNRF70_TX_DONE_WQ_ENABLED +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_RX_WQ_ENABLED + -DNRF70_RX_WQ_ENABLED +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_UTIL + -DNRF70_UTIL +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_RADIO_TEST + -DNRF70_RADIO_TEST +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_TCP_IP_CHECKSUM_OFFLOAD + -DNRF70_TCP_IP_CHECKSUM_OFFLOAD +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_RPU_EXTEND_TWT_SP + -DNRF70_RPU_EXTEND_TWT_SP +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_SYSTEM_WITH_RAW_MODES + -DNRF70_SYSTEM_WITH_RAW_MODES +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_SCAN_ONLY + -DNRF70_SCAN_ONLY +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_SYSTEM_MODE + -DNRF70_SYSTEM_MODE +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_2_4G_ONLY + -DNRF70_2_4G_ONLY +) + +zephyr_compile_definitions_ifdef(CONFIG_NRF70_LOG_VERBOSE + -DNRF70_LOG_VERBOSE +) + +zephyr_compile_definitions( + -DNRF70_RX_NUM_BUFS=${CONFIG_NRF70_RX_NUM_BUFS} + -DNRF70_MAX_TX_TOKENS=${CONFIG_NRF70_MAX_TX_TOKENS} + -DNRF70_RX_MAX_DATA_SIZE=${CONFIG_NRF70_RX_MAX_DATA_SIZE} + -DNRF70_MAX_TX_PENDING_QLEN=${CONFIG_NRF70_MAX_TX_PENDING_QLEN} + -DNRF70_RPU_PS_IDLE_TIMEOUT_MS=${CONFIG_NRF70_RPU_PS_IDLE_TIMEOUT_MS} + -DNRF70_REG_DOMAIN=${CONFIG_NRF70_REG_DOMAIN} + -DNRF70_BAND_2G_LOWER_EDGE_BACKOFF_DSSS=${CONFIG_NRF70_BAND_2G_LOWER_EDGE_BACKOFF_DSSS} + -DNRF70_BAND_2G_LOWER_EDGE_BACKOFF_HT=${CONFIG_NRF70_BAND_2G_LOWER_EDGE_BACKOFF_HT} + -DNRF70_BAND_2G_LOWER_EDGE_BACKOFF_HE=${CONFIG_NRF70_BAND_2G_LOWER_EDGE_BACKOFF_HE} + -DNRF70_BAND_2G_UPPER_EDGE_BACKOFF_DSSS=${CONFIG_NRF70_BAND_2G_UPPER_EDGE_BACKOFF_DSSS} + -DNRF70_BAND_2G_UPPER_EDGE_BACKOFF_HT=${CONFIG_NRF70_BAND_2G_UPPER_EDGE_BACKOFF_HT} + -DNRF70_BAND_2G_UPPER_EDGE_BACKOFF_HE=${CONFIG_NRF70_BAND_2G_UPPER_EDGE_BACKOFF_HE} + -DNRF70_BAND_UNII_1_LOWER_EDGE_BACKOFF_HT=${CONFIG_NRF70_BAND_UNII_1_LOWER_EDGE_BACKOFF_HT} + -DNRF70_BAND_UNII_1_LOWER_EDGE_BACKOFF_HE=${CONFIG_NRF70_BAND_UNII_1_LOWER_EDGE_BACKOFF_HE} + -DNRF70_BAND_UNII_1_UPPER_EDGE_BACKOFF_HT=${CONFIG_NRF70_BAND_UNII_1_UPPER_EDGE_BACKOFF_HT} + -DNRF70_BAND_UNII_1_UPPER_EDGE_BACKOFF_HE=${CONFIG_NRF70_BAND_UNII_1_UPPER_EDGE_BACKOFF_HE} + -DNRF70_BAND_UNII_2A_LOWER_EDGE_BACKOFF_HT=${CONFIG_NRF70_BAND_UNII_2A_LOWER_EDGE_BACKOFF_HT} + -DNRF70_BAND_UNII_2A_LOWER_EDGE_BACKOFF_HE=${CONFIG_NRF70_BAND_UNII_2A_LOWER_EDGE_BACKOFF_HE} + -DNRF70_BAND_UNII_2A_UPPER_EDGE_BACKOFF_HT=${CONFIG_NRF70_BAND_UNII_2A_UPPER_EDGE_BACKOFF_HT} + -DNRF70_BAND_UNII_2A_UPPER_EDGE_BACKOFF_HE=${CONFIG_NRF70_BAND_UNII_2A_UPPER_EDGE_BACKOFF_HE} + -DNRF70_BAND_UNII_2C_LOWER_EDGE_BACKOFF_HT=${CONFIG_NRF70_BAND_UNII_2C_LOWER_EDGE_BACKOFF_HT} + -DNRF70_BAND_UNII_2C_LOWER_EDGE_BACKOFF_HE=${CONFIG_NRF70_BAND_UNII_2C_LOWER_EDGE_BACKOFF_HE} + -DNRF70_BAND_UNII_2C_UPPER_EDGE_BACKOFF_HT=${CONFIG_NRF70_BAND_UNII_2C_UPPER_EDGE_BACKOFF_HT} + -DNRF70_BAND_UNII_2C_UPPER_EDGE_BACKOFF_HE=${CONFIG_NRF70_BAND_UNII_2C_UPPER_EDGE_BACKOFF_HE} + -DNRF70_BAND_UNII_3_LOWER_EDGE_BACKOFF_HT=${CONFIG_NRF70_BAND_UNII_3_LOWER_EDGE_BACKOFF_HT} + -DNRF70_BAND_UNII_3_LOWER_EDGE_BACKOFF_HE=${CONFIG_NRF70_BAND_UNII_3_LOWER_EDGE_BACKOFF_HE} + -DNRF70_BAND_UNII_3_UPPER_EDGE_BACKOFF_HT=${CONFIG_NRF70_BAND_UNII_3_UPPER_EDGE_BACKOFF_HT} + -DNRF70_BAND_UNII_3_UPPER_EDGE_BACKOFF_HE=${CONFIG_NRF70_BAND_UNII_3_UPPER_EDGE_BACKOFF_HE} + -DNRF70_BAND_UNII_4_LOWER_EDGE_BACKOFF_HT=${CONFIG_NRF70_BAND_UNII_4_LOWER_EDGE_BACKOFF_HT} + -DNRF70_BAND_UNII_4_LOWER_EDGE_BACKOFF_HE=${CONFIG_NRF70_BAND_UNII_4_LOWER_EDGE_BACKOFF_HE} + -DNRF70_BAND_UNII_4_UPPER_EDGE_BACKOFF_HT=${CONFIG_NRF70_BAND_UNII_4_UPPER_EDGE_BACKOFF_HT} + -DNRF70_BAND_UNII_4_UPPER_EDGE_BACKOFF_HE=${CONFIG_NRF70_BAND_UNII_4_UPPER_EDGE_BACKOFF_HE} + -DNRF70_PCB_LOSS_2G=${CONFIG_NRF70_PCB_LOSS_2G} + -DNRF70_PCB_LOSS_5G_BAND1=${CONFIG_NRF70_PCB_LOSS_5G_BAND1} + -DNRF70_PCB_LOSS_5G_BAND2=${CONFIG_NRF70_PCB_LOSS_5G_BAND2} + -DNRF70_PCB_LOSS_5G_BAND3=${CONFIG_NRF70_PCB_LOSS_5G_BAND3} + -DNRF70_ANT_GAIN_2G=${CONFIG_NRF70_ANT_GAIN_2G} + -DNRF70_ANT_GAIN_5G_BAND1=${CONFIG_NRF70_ANT_GAIN_5G_BAND1} + -DNRF70_ANT_GAIN_5G_BAND2=${CONFIG_NRF70_ANT_GAIN_5G_BAND2} + -DNRF70_ANT_GAIN_5G_BAND3=${CONFIG_NRF70_ANT_GAIN_5G_BAND3} +) diff --git a/drivers/wifi/nrfwifi/Kconfig.nrfwifi b/drivers/wifi/nrfwifi/Kconfig.nrfwifi new file mode 100644 index 000000000000..bad9cfb7b8d2 --- /dev/null +++ b/drivers/wifi/nrfwifi/Kconfig.nrfwifi @@ -0,0 +1,670 @@ +# Nordic Wi-Fi driver for nRF70 series SoCs +# +# Copyright (c) 2024 Nordic Semiconductor +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig WIFI_NRF70 + bool "nRF70 driver" + select NET_L2_WIFI_MGMT if NETWORKING + select NET_L2_ETHERNET_MGMT if NETWORKING && NET_L2_ETHERNET + select WIFI_USE_NATIVE_NETWORKING if NETWORKING + select EXPERIMENTAL if !SOC_SERIES_NRF53X && !SOC_SERIES_NRF91X + default y + depends on \ + DT_HAS_NORDIC_NRF7002_SPI_ENABLED || DT_HAS_NORDIC_NRF7002_QSPI_ENABLED || \ + DT_HAS_NORDIC_NRF7001_SPI_ENABLED || DT_HAS_NORDIC_NRF7001_QSPI_ENABLED || \ + DT_HAS_NORDIC_NRF7000_SPI_ENABLED || DT_HAS_NORDIC_NRF7000_QSPI_ENABLED + help + Nordic Wi-Fi Driver + +if WIFI_NRF70 +# Hidden symbols for internal use +config WIFI_NRF7002 + bool + default y if DT_HAS_NORDIC_NRF7002_SPI_ENABLED || DT_HAS_NORDIC_NRF7002_QSPI_ENABLED + +config WIFI_NRF7001 + bool + default y if DT_HAS_NORDIC_NRF7001_SPI_ENABLED || DT_HAS_NORDIC_NRF7001_QSPI_ENABLED + +config WIFI_NRF7000 + bool + default y if DT_HAS_NORDIC_NRF7000_SPI_ENABLED || DT_HAS_NORDIC_NRF7000_QSPI_ENABLED + + +module = WIFI_NRF70_BUS +module-dep = LOG +module-str = Log level for Wi-Fi nRF70 bus layers +module-help = Sets log level for Wi-Fi nRF70 bus layers +source "subsys/net/Kconfig.template.log_config.net" + +config WIFI_NRF70_BUS_LOG_LEVEL + # Enable error by default + default 1 + +choice NRF70_OPER_MODES + bool "nRF70 operating modes" + default NRF70_SYSTEM_WITH_RAW_MODES if !WIFI_NRF7000 && \ + (NRF70_RAW_DATA_TX || NRF70_RAW_DATA_RX || NRF70_PROMISC_DATA_RX) + default NRF70_SYSTEM_MODE if !WIFI_NRF7000 + default NRF70_SCAN_ONLY if WIFI_NRF7000 + help + Select the operating mode of the nRF70 driver + +config NRF70_SYSTEM_MODE + bool "nRF70 system mode" + depends on WIFI_NRF7002 || WIFI_NRF7001 + select WIFI_NM_WPA_SUPPLICANT + help + Select this option to enable system mode of the nRF70 driver + +config NRF70_SCAN_ONLY + bool "nRF70 scan only mode" + depends on WIFI_NRF7000 + help + Select this option to enable scan only mode of the nRF70 driver + +config NRF70_RADIO_TEST + bool "Radio test mode of the nRF70 driver" + +config NRF70_SYSTEM_WITH_RAW_MODES + bool "nRF70 system mode with raw modes" + depends on WIFI_NRF7002 || WIFI_NRF7001 + select WIFI_NM_WPA_SUPPLICANT + help + Select this option to enable system mode of the nRF70 driver with raw modes + +endchoice + +config NET_L2_ETHERNET + default y if !NRF70_RADIO_TEST + +config HEAP_MEM_POOL_ADD_SIZE_NRF70 + # Use a maximum that works for typical usecases and boards, each sample/app can override + # this value if needed by using CONFIG_HEAP_MEM_POOL_IGNORE_MIN + def_int 25000 if NRF70_SCAN_ONLY + def_int 150000 + +if NRF70_SYSTEM_MODE || NRF70_SYSTEM_WITH_RAW_MODES +config NRF70_STA_MODE + bool "nRF70 STA mode" + default y + help + Select this option to enable STA mode of the nRF70 driver + +config NRF70_AP_MODE + bool "Access point mode" + depends on WIFI_NM_WPA_SUPPLICANT_AP + +config NRF70_P2P_MODE + bool "P2P support in driver" +endif # NRF70_SYSTEM_MODE || NRF70_SYSTEM_WITH_RAW_MODES + +config NRF70_RAW_DATA_TX + bool "RAW TX data path in the driver" + select EXPERIMENTAL + +config NRF70_RAW_DATA_RX + bool "RAW RX sniffer operation in the driver" + select EXPERIMENTAL + +config NRF70_PROMISC_DATA_RX + bool "promiscuous RX sniffer operation in the driver" + select WIFI_NM_WPA_SUPPLICANT + select EXPERIMENTAL + select NET_PROMISCUOUS_MODE + +config NRF70_DATA_TX + bool "TX data path in the driver" + default y if NRF70_SYSTEM_MODE || NRF70_SYSTEM_WITH_RAW_MODES + +config NRF_WIFI_IF_AUTO_START + bool "Wi-Fi interface auto start on boot" + default y + +config NRF_WIFI_PATCHES_BUILTIN + bool "Store nRF70 FW patches as part of the driver" + default y + help + Select this option to store nRF70 FW patches as part of the driver. + This option impacts the code memory footprint of the driver. + +config CUSTOM_LINKER_SCRIPT + string "Custom linker script for nRF70 FW patches" + default "${ZEPHYR_BASE}/../nrf/drivers/wifi/nrf70/rpu_fw_patches.ld" + +config NRF_WIFI_LOW_POWER + bool "low power mode in nRF Wi-Fi chipsets" + default y + +config NRF70_TCP_IP_CHECKSUM_OFFLOAD + bool "TCP/IP checksum offload" + default y + +config NRF70_REG_DOMAIN + string "The ISO/IEC alpha2 country code for the country in which this device is currently operating. Default 00 (World regulatory)" + # 00 is used for World regulatory + default "00" + +# Making calls to RPU from net_mgmt callbacks. +# +# If WPA supplicant is enabled, then don't override as it has higher +# stack requirements. +config NET_MGMT_EVENT_STACK_SIZE + default 2048 if !WIFI_NM_WPA_SUPPLICANT + +config NRF70_LOG_VERBOSE + bool "Maintains the verbosity of information in logs" + default y + +module = WIFI_NRF70 +module-dep = LOG +module-str = Log level for Wi-Fi nRF70 driver +module-help = Sets log level for Wi-Fi nRF70 driver +source "subsys/logging/Kconfig.template.log_config" + +config WIFI_NRF70_LOG_LEVEL + # Enable error by default + default 1 + +config NRF70_ON_QSPI + def_bool DT_HAS_NORDIC_NRF7002_QSPI_ENABLED || \ + DT_HAS_NORDIC_NRF7001_QSPI_ENABLED || \ + DT_HAS_NORDIC_NRF7000_QSPI_ENABLED + select NRFX_QSPI + +config NRF70_ON_SPI + def_bool DT_HAS_NORDIC_NRF7002_SPI_ENABLED || \ + DT_HAS_NORDIC_NRF7001_SPI_ENABLED || \ + DT_HAS_NORDIC_NRF7000_SPI_ENABLED + select SPI + +config NRF70_2_4G_ONLY + def_bool y if WIFI_NRF7001 + +# Wi-Fi and SR Coexistence Hardware configuration. +config NRF70_SR_COEX + bool "Wi-Fi and SR coexistence support" + +config NRF70_SR_COEX_RF_SWITCH + bool "GPIO configuration to control SR side RF switch position" + +config NRF70_WORKQ_STACK_SIZE + int "Stack size for workqueue" + default 4096 + +config NRF70_WORKQ_MAX_ITEMS + int "Maximum work items for all workqueues" + default 100 + +config NRF70_MAX_TX_PENDING_QLEN + int "Maximum number of pending TX packets" + default 18 + +config NRF70_UTIL + depends on SHELL + bool "Utility shell in nRF70 driver" + +config NRF70_QSPI_LOW_POWER + bool "low power mode in QSPI" + default y if NRF_WIFI_LOW_POWER + +config NRF70_PCB_LOSS_2G + int "PCB loss for 2.4 GHz band" + default 0 + range 0 4 + help + Specifies PCB loss from the antenna connector to the RF pin. + The values are in dB scale in steps of 1dB and range of 0-4dB. + The loss is considered in the RX path only. + +config NRF70_PCB_LOSS_5G_BAND1 + int "PCB loss for 5 GHz band (5150 MHz - 5350 MHz, Channel-32 - Channel-68)" + default 0 + range 0 4 + help + Specifies PCB loss from the antenna connector to the RF pin. + The values are in dB scale in steps of 1dB and range of 0-4dB. + The loss is considered in the RX path only. + +config NRF70_PCB_LOSS_5G_BAND2 + int "PCB loss for 5 GHz band (5470 MHz - 5730 MHz, Channel-96 - Channel-144)" + default 0 + range 0 4 + help + Specifies PCB loss from the antenna connector to the RF pin. + The values are in dB scale in steps of 1dB and range of 0-4dB. + The loss is considered in the RX path only. + +config NRF70_PCB_LOSS_5G_BAND3 + int "PCB loss for 5 GHz band (5730 MHz - 5895 MHz, Channel-149 - Channel-177)" + default 0 + range 0 4 + help + Specifies PCB loss from the antenna connector to the RF pin. + The values are in dB scale in steps of 1dB and range of 0-4dB. + The loss is considered in the RX path only. + +config NRF70_ANT_GAIN_2G + int "Antenna gain for 2.4 GHz band" + default 0 + range 0 6 + +config NRF70_ANT_GAIN_5G_BAND1 + int "Antenna gain for 5 GHz band (5150 MHz - 5350 MHz)" + default 0 + range 0 6 + +config NRF70_ANT_GAIN_5G_BAND2 + int "Antenna gain for 5 GHz band (5470 MHz - 5730 MHz)" + default 0 + range 0 6 + +config NRF70_ANT_GAIN_5G_BAND3 + int "Antenna gain for 5 GHz band (5730 MHz - 5895 MHz)" + default 0 + range 0 6 + +config NRF70_BAND_2G_LOWER_EDGE_BACKOFF_DSSS + int "DSSS Transmit power backoff (in dB) for lower edge of 2.4 GHz frequency band" + default 0 + range 0 10 + +config NRF70_BAND_2G_LOWER_EDGE_BACKOFF_HT + int "HT/VHT Transmit power backoff (in dB) for lower edge of 2.4 GHz frequency band" + default 0 + range 0 10 + +config NRF70_BAND_2G_LOWER_EDGE_BACKOFF_HE + int "HE Transmit power backoff (in dB) for lower edge of 2.4 GHz frequency band" + default 0 + range 0 10 + +config NRF70_BAND_2G_UPPER_EDGE_BACKOFF_DSSS + int "DSSS Transmit power backoff (in dB) for upper edge of 2.4 GHz frequency band" + default 0 + range 0 10 + +config NRF70_BAND_2G_UPPER_EDGE_BACKOFF_HT + int "HT/VHT Transmit power backoff (in dB) for upper edge of 2.4 GHz frequency band" + default 0 + range 0 10 + +config NRF70_BAND_2G_UPPER_EDGE_BACKOFF_HE + int "HE Transmit power backoff (in dB) for upper edge of 2.4 GHz frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_1_LOWER_EDGE_BACKOFF_HT + int "HT/VHT Transmit power backoff (in dB) for lower edge of UNII-1 frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_1_LOWER_EDGE_BACKOFF_HE + int "HE Transmit power backoff (in dB) for lower edge of UNII-1 frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_1_UPPER_EDGE_BACKOFF_HT + int "HT/VHT Transmit power backoff (in dB) for upper edge of UNII-1 frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_1_UPPER_EDGE_BACKOFF_HE + int "HE Transmit power backoff (in dB) for upper edge of UNII-1 frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_2A_LOWER_EDGE_BACKOFF_HT + int "HT/VHT Transmit power backoff (in dB) for lower edge of UNII-2A frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_2A_LOWER_EDGE_BACKOFF_HE + int "HE Transmit power backoff (in dB) for lower edge of UNII-2A frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_2A_UPPER_EDGE_BACKOFF_HT + int "HT/VHT Transmit power backoff (in dB) for upper edge of UNII-2A frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_2A_UPPER_EDGE_BACKOFF_HE + int "HE Transmit power backoff (in dB) for upper edge of UNII-2A frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_2C_LOWER_EDGE_BACKOFF_HT + int "HT/VHT Transmit power backoff (in dB) for lower edge of UNII-2C frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_2C_LOWER_EDGE_BACKOFF_HE + int "HE Transmit power backoff (in dB) for lower edge of UNII-2C frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_2C_UPPER_EDGE_BACKOFF_HT + int "HT/VHT Transmit power backoff (in dB) for upper edge of UNII-2C frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_2C_UPPER_EDGE_BACKOFF_HE + int "HE Transmit power backoff (in dB) for upper edge of UNII-2C frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_3_LOWER_EDGE_BACKOFF_HT + int "HT/VHT Transmit power backoff (in dB) for lower edge of UNII-3 frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_3_LOWER_EDGE_BACKOFF_HE + int "HE Transmit power backoff (in dB) for lower edge of UNII-3 frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_3_UPPER_EDGE_BACKOFF_HT + int "HT/VHT Transmit power backoff (in dB) for upper edge of UNII-3 frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_3_UPPER_EDGE_BACKOFF_HE + int "HE Transmit power backoff (in dB) for upper edge of UNII-3 frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_4_LOWER_EDGE_BACKOFF_HT + int "HT/VHT Transmit power backoff (in dB) for lower edge of UNII-4 frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_4_LOWER_EDGE_BACKOFF_HE + int "HE Transmit power backoff (in dB) for lower edge of UNII-4 frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_4_UPPER_EDGE_BACKOFF_HT + int "HT/VHT Transmit power backoff (in dB) for upper edge of UNII-4 frequency band" + default 0 + range 0 10 + +config NRF70_BAND_UNII_4_UPPER_EDGE_BACKOFF_HE + int "HE Transmit power backoff (in dB) for upper edge of UNII-4 frequency band" + default 0 + range 0 10 + +# Performance fine tuning options + +config NRF70_RX_NUM_BUFS + int "Number of RX buffers" + default 48 + +config NRF70_MAX_TX_AGGREGATION + int "Maximum number of TX packets to aggregate" + default 12 + +config NRF70_MAX_TX_TOKENS + int "Maximum number of TX tokens" + range 5 12 if !NRF70_RADIO_TEST + default 10 + +config NRF70_TX_MAX_DATA_SIZE + int "Maximum size of TX data" + default 1600 + +config NRF70_RX_MAX_DATA_SIZE + int "Maximum size of RX data" + default 1600 + +config NRF70_TX_DONE_WQ_ENABLED + bool "TX done workqueue (impacts performance negatively)" + +config NRF70_RX_WQ_ENABLED + bool "RX workqueue" + +# Use for IRQ processing (TODO: using for BH processing causes issues) +config NUM_METAIRQ_PRIORITIES + default 1 + +config NRF70_IRQ_WQ_PRIORITY + int "Priority of the workqueue for handling IRQs" + default -15 + +config NRF70_BH_WQ_PRIORITY + int "Priority of the workqueue for handling bottom half" + default 0 + +config NRF70_IRQ_WQ_STACK_SIZE + int "Stack size of the workqueue for handling IRQs" + default 2048 + +config NRF70_BH_WQ_STACK_SIZE + int "Stack size of the workqueue for handling bottom half" + default 2048 + +if NRF70_TX_DONE_WQ_ENABLED +config NRF70_TX_DONE_WQ_PRIORITY + int "Priority of the workqueue for handling TX done" + default 0 + +config NRF70_TX_DONE_WQ_STACK_SIZE + int "Stack size of the workqueue for handling TX done" + default 2048 +endif # NRF70_TX_DONE_WQ_ENABLED +if NRF70_RX_WQ_ENABLED +config NRF70_RX_WQ_PRIORITY + int "Priority of the workqueue for handling RX" + default 0 + +config NRF70_RX_WQ_STACK_SIZE + int "Stack size of the workqueue for handling RX" + default 2048 +endif # NRF70_RX_WQ_ENABLED + +if NRF_WIFI_LOW_POWER +config NRF70_RPU_PS_IDLE_TIMEOUT_MS + int "RPU power save idle timeout in milliseconds" + default 10 + +config NRF70_RPU_EXTEND_TWT_SP + bool "extending TWT service period" + help + In case frames accepted before beginning of SP are not + transmitted before the SP completes then typically they are + dropped to conform to SP window as per specification i.e., no + transmission outside SP window. + + This feature mitigates the frame loss by transmitting even after SP + completion by using standard contention mechanism which is allowed + in specification but not recommended. As the device is actively transmitting + beyond SP, the power consumption increases depending on the amount + of traffic available at the start of SP. + + Please note that if a frame is sent after SP starts it will be queued and this + mechanism is not used. +endif # NRF_WIFI_LOW_POWER + +config WIFI_FIXED_MAC_ADDRESS + string "WiFi Fixed MAC address in format XX:XX:XX:XX:XX:XX" + help + This overrides the MAC address read from OTP. Strictly for testing purposes only. + +choice + prompt "Wi-Fi MAC address type" + default WIFI_OTP_MAC_ADDRESS if WIFI_FIXED_MAC_ADDRESS = "" + default WIFI_FIXED_MAC_ADDRESS_ENABLED if WIFI_FIXED_MAC_ADDRESS != "" + help + Select the type of MAC address to be used by the Wi-Fi driver + +config WIFI_OTP_MAC_ADDRESS + bool "Use MAC address from OTP" + help + This option uses the MAC address stored in the OTP memory of the nRF70. + +config WIFI_FIXED_MAC_ADDRESS_ENABLED + bool "fixed MAC address" + help + Enable fixed MAC address + +config WIFI_RANDOM_MAC_ADDRESS + bool "random MAC address generation at runtime" + depends on ENTROPY_GENERATOR + help + This option enables random MAC address generation at runtime. + The random MAC address is generated using the entropy device random generator. + +endchoice + +config NRF70_RSSI_STALE_TIMEOUT_MS + int "RSSI stale timeout in milliseconds" + default 1000 + help + RSSI stale timeout is the period after which driver queries + RPU to get the RSSI the value. + If data is active (e.g. ping), driver stores the RSSI value from + the received frames and provides this stored information + to wpa_supplicant. In this case a higher value will be suitable + as stored RSSI value at driver will be updated regularly. + If data is not active or after the stale timeout duration, + driver queries the RPU to get the RSSI value + and provides it to wpa_supplicant. The value should be set to lower + value as driver does not store it and requires RPU to provide the + info. + +if NETWORKING +# Finetune defaults for certain system components used by the driver +config SYSTEM_WORKQUEUE_STACK_SIZE + default 4096 + +config NET_TX_STACK_SIZE + default 4096 + +config NET_RX_STACK_SIZE + default 4096 + +config NET_TC_TX_COUNT + default 1 + +endif # NETWORKING + +config MAIN_STACK_SIZE + default 4096 + +config SHELL_STACK_SIZE + default 4096 + +# Override the Wi-Fi subsytems WIFI_MGMT_SCAN_SSID_FILT_MAX parameter, +# since we support a maximum of 2 SSIDs for scan result filtering. +config WIFI_MGMT_SCAN_SSID_FILT_MAX + default 2 + +config NRF_WIFI_SCAN_MAX_BSS_CNT + int "Maximum number of scan results to return." + default 0 + range 0 65535 + help + Maximum number of scan results to return. 0 represents unlimited number of BSSes. + +config NRF_WIFI_BEAMFORMING + bool "Wi-Fi beamforming. Enabling beamforming can provide slight improvement in performance where as disabling it can provide better power saving in low network activity applications" + default y + +config WIFI_NRF70_SCAN_TIMEOUT_S + int "Scan timeout in seconds" + default 30 + +menu "nRF Wi-Fi operation band(s)" + visible if !NRF70_2_4G_ONLY + +config NRF_WIFI_2G_BAND + bool "Set operation band to 2.4GHz" + default y if NRF70_2_4G_ONLY + +config NRF_WIFI_5G_BAND + bool "Set operation band to 5GHz" + depends on !NRF70_2_4G_ONLY + +config NRF_WIFI_OP_BAND + int "Options to set operation band" + default 1 if NRF_WIFI_2G_BAND + default 2 if NRF_WIFI_5G_BAND + default 3 + help + Set this option to select frequency band + 1 - 2.4GHz + 2 - 5GHz + 3 - All ( 2.4GHz and 5GHz ) +endmenu + +config NRF_WIFI_IFACE_MTU + int "MTU for Wi-Fi interface" + range 576 2304 if NET_IPV4 + range 1280 2304 if NET_IPV6 + default 1500 + +config WIFI_NRF70_SKIP_LOCAL_ADMIN_MAC + bool "Suppress networks with non-individual MAC address as BSSID in the scan results" + help + Wi-Fi access points use locally administered MAC address to manage + multiple virtual interfaces, for geo-location usecase these networks + from the virtual interfaces do not help in anyway as they are co-located with the primary interface + that has globally unique MAC address. + + So, to save resources, this option drops such networks from the scan results. + +config WIFI_NRF70_SCAN_DISABLE_DFS_CHANNELS + bool "Disables DFS channels in scan operation" + help + This option disables inclusion of DFS channels in scan operation. + This is useful to reduce the scan time, as DFS channels are seldom used. + +config NET_INTERFACE_NAME_LEN + # nordic_wlanN + default 15 + +config NRF_WIFI_AP_DEAD_DETECT_TIMEOUT + int "Access point dead detection timeout in seconds" + range 1 30 + default 20 + help + The number of seconds after which AP is declared dead if no beacons + are received from the AP. Used to detect AP silently going down e.g., power off. + +config NRF_WIFI_RPU_RECOVERY + bool "RPU recovery mechanism" + select EXPERIMENTAL + help + Enable RPU recovery mechanism to recover from RPU (nRF70) hang. + This feature performs an interface reset (down and up) which triggers + a RPU coldboot. Application's network connection will be lost during + the recovery process and it is application's responsibility to + re-establish the network connection. + +if NRF_WIFI_RPU_RECOVERY + +config NRF_WIFI_RPU_RECOVERY_PROPAGATION_DELAY_MS + int "RPU recovery propagation delay in milliseconds" + default 10 + help + Propagation delay in milliseconds to wait after RPU is powered down + before powering it up. This delay is required to ensure that the recovery + is propagted to all the applications and stack and have enough time to + clean up the resources. + +config NET_MGMT_EVENT_QUEUE_SIZE + # Doing interface down and up even with delay puts a lot of events in the queue + default 16 +endif # NRF_WIFI_RPU_RECOVERY + +config NRF_WIFI_COMBINED_BUCKEN_IOVDD_GPIO + bool + help + Enable this option to use a single GPIO to control both buck enable and IOVDD enable, + there will be a internal hardware switch to add delay between the two operations. This + is typically 4ms delay for nRF70. + +endif # WIFI_NRF70 diff --git a/drivers/wifi/nrfwifi/inc/coex.h b/drivers/wifi/nrfwifi/inc/coex.h new file mode 100644 index 000000000000..969a4c9628dc --- /dev/null +++ b/drivers/wifi/nrfwifi/inc/coex.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing Coexistence APIs. + */ + +#ifndef __COEX_H__ +#define __COEX_H__ + +#include + +/* Indicates WLAN frequency band of operation */ +enum nrf_wifi_pta_wlan_op_band { + NRF_WIFI_PTA_WLAN_OP_BAND_2_4_GHZ = 0, + NRF_WIFI_PTA_WLAN_OP_BAND_5_GHZ, + NRF_WIFI_PTA_WLAN_OP_BAND_NONE = 0xFF +}; + +/** + * @function nrf_wifi_coex_config_pta(enum nrf_wifi_pta_wlan_op_band wlan_band, + * bool separate_antennas, bool is_sr_protocol_ble) + * + * @brief Function used to configure PTA tables of coexistence hardware. + * + * @param[in] enum nrf_wifi_pta_wlan_op_band wlan_band + * @param[in] separate_antennas + * Indicates whether separate antenans are used or not. + * @param[in] is_sr_protocol_ble + * Indicates if SR protocol is Bluetooth LE or not. + * @return Returns status of configuration. + * Returns zero upon successful configuration. + * Returns non-zero upon unsuccessful configuration. + */ +int nrf_wifi_coex_config_pta(enum nrf_wifi_pta_wlan_op_band wlan_band, bool separate_antennas, + bool is_sr_protocol_ble); + +#if defined(CONFIG_NRF70_SR_COEX_RF_SWITCH) || defined(__DOXYGEN__) +/** + * @function nrf_wifi_config_sr_switch(bool separate_antennas) + * + * @brief Function used to configure SR side switch (nRF5340 side switch in nRF7002 DK). + * + * @param[in] separate_antennas + * Indicates whether separate antenans are used or not. + * + * @return Returns status of configuration. + * Returns zero upon successful configuration. + * Returns non-zero upon unsuccessful configuration. + */ +int nrf_wifi_config_sr_switch(bool separate_antennas); +#endif /* CONFIG_NRF70_SR_COEX_RF_SWITCH */ + +/** + * @function nrf_wifi_coex_config_non_pta(bool separate_antennas) + * + * @brief Function used to configure non-PTA registers of coexistence hardware. + * + * @param[in] separate_antennas + * Indicates whether separate antenans are used or not. + * @param[in] is_sr_protocol_ble + * Indicates if SR protocol is Bluetooth LE or not. + * + * @return Returns status of configuration. + * Returns zero upon successful configuration. + * Returns non-zero upon unsuccessful configuration. + */ +int nrf_wifi_coex_config_non_pta(bool separate_antennas, bool is_sr_protocol_ble); + +/** + * @function nrf_wifi_coex_hw_reset(void) + * + * @brief Function used to reset coexistence hardware. + * + * @return Returns status of configuration. + * Returns zero upon successful configuration. + * Returns non-zero upon unsuccessful configuration. + */ +int nrf_wifi_coex_hw_reset(void); + +#endif /* __COEX_H__ */ diff --git a/drivers/wifi/nrfwifi/inc/coex_struct.h b/drivers/wifi/nrfwifi/inc/coex_struct.h new file mode 100644 index 000000000000..a6695b47fbc7 --- /dev/null +++ b/drivers/wifi/nrfwifi/inc/coex_struct.h @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Structures and related enumerations used in Coexistence. + */ + +#ifndef __COEX_STRUCT_H__ +#define __COEX_STRUCT_H__ + +#include +#include + +/* Max size of message buffer (exchanged between host and MAC). This is in "bytes" */ +#define MAX_MESSAGE_BUF_SIZE 320 +/* Number of elements in coex_ch_configuration other than configbuf[] */ +#define NUM_ELEMENTS_EXCL_CONFIGBUF 4 +/* Each configuration value is of type uint32_t */ +#define MAX_NUM_CONFIG_VALUES ((MAX_MESSAGE_BUF_SIZE-\ + (NUM_ELEMENTS_EXCL_CONFIGBUF*sizeof(uint32_t)))>>2) +/* Number of elements in coex_sr_traffic_info other than sr_traffic_info[] */ +#define NUM_ELEMENTS_EXCL_SRINFOBUF 1 +/* Each SR Traffic Info is of type uint32_t */ +#define MAX_SR_TRAFFIC_BUF_SIZE 32 + +enum { + /** Used two different values for AGGREGATION module because offset from base is + * beyond supported message buffer size for WAIT_STATE_1_TIME register + */ + COEX_HARDWARE = 1, + MAC_CTRL, + MAC_CTRL_AGG_WAIT_STATE_1_TIME, + MAC_CTRL_AGG, + MAC_CTRL_DEAGG, + WLAN_CTRL, +}; + +/* IDs of different messages posted from Coexistence Driver to Coexistence Manager */ +enum { + /* To insturct Coexistence Manager to collect and post SR traffic information */ + COLLECT_SR_TRAFFIC_INFO = 1, + /* To insturct Coexistence Manager to allocate a priority window to SR */ + ALLOCATE_PTI_WINDOW, + /* To do configuration of hardware related to coexistence */ + HW_CONFIGURATION, + /* To start allocating periodic priority windows to Wi-Fi and SR */ + ALLOCATE_PPW, + /* To start allocating virtual priority windows to Wi-Fi */ + ALLOCATE_VPW, + /* To configure CM SW parameters */ + SW_CONFIGURATION, + /* To control sheliak side switch */ + UPDATE_SWITCH_CONFIG +}; + +/* ID(s) of different messages posted from Coexistence Manager to Coexistence Driver */ +enum { + /* To post SR traffic information */ + SR_TRAFFIC_INFO = 1 +}; + +/** + * struct coex_collect_sr_traffic_info - Message from CD to CM to request SR traffic info. + * @message_id: Indicates message ID. This is to be set to COLLECT_SR_TRAFFIC_INFO. + * @num_sets_requested: Indicates the number of sets of duration and periodicity to be collected. + * + * Message from CD to CM to request SR traffic information. + */ +struct coex_collect_sr_traffic_info { + uint32_t message_id; + uint32_t num_sets_requested; +}; + +/** + * struct coex_ch_configuration -Message from CD to CM to configure CH. + * @message_id: Indicates message ID. This is to be set to HW_CONFIGURATION. + * @num_reg_to_config: Indicates the number of registers to be configured. + * @hw_to_config: Indicates the hardware block that is to be configured. + * @hw_block_base_addr: Base address of the hardware block to be configured. + * @configbuf: Configuration buffer that holds packed offset and configuration value. + * + * Message from CD to CM to configure CH + */ +struct coex_ch_configuration { + uint32_t message_id; + uint32_t num_reg_to_config; + uint32_t hw_to_config; + uint32_t hw_block_base_addr; + uint32_t configbuf[MAX_NUM_CONFIG_VALUES]; +}; + +/** + * struct coex_allocate_pti_window - Message to CM to request a priority window. + * @message_id: Indicates message ID. This is to be set to ALLOCATE_PTI_WINDOW. + * @device_req_window: Indicates device requesting a priority window. + * @window_start_or_end: Indicates if request is posted to START or END a priority window. + * @imp_of_request: Indicates importance of activity for which the window is requested. + * @can_be_deferred: activity of Wi-Fi/SR, for which window is requested can be deferred or not. + * + * Message to CM to request a priority window + */ +struct coex_allocate_pti_window { + uint32_t message_id; + uint32_t device_req_window; + uint32_t window_start_or_end; + uint32_t imp_of_request; + uint32_t can_be_deferred; +}; + +/** + * struct coex_allocate_ppw - Message from CD to CM to allocate Periodic Priority Windows. + * @message_id: Indicates message ID. This is to be set to ALLOCATE_PPW. + * @start_or_stop: Indiates start or stop allocation of PPWs. + * @first_pti_window: Indicates first priority window in the series of PPWs. + * @ps_mechanism: Indicates recommended powersave mechanism for Wi-Fi's downlink. + * @wifi_window_duration: Indicates duration of Wi-Fi priority window. + * @sr_window_duration: Indicates duration of SR priority window. + * + * Message from CD to CM to allocate Periodic Priority Windows. + */ +struct coex_allocate_ppw { + uint32_t message_id; + uint32_t start_or_stop; + uint32_t first_pti_window; + uint32_t ps_mechanism; + uint32_t wifi_window_duration; + uint32_t sr_window_duration; +}; + +/** + * struct coex_allocate_vpw - Message from CD to CM to allocate Virtual Priority Windows. + * @message_id: Indicates message ID. This is to be set to ALLOCATE_VPW. + * @start_or_stop: Indicates start or stop allocation of VPWs. + * @wifi_window_duration: Indicates duration of Wi-Fi virtual priority window. + * @ps_mechanism: Indicates recommended powersave mechanism for Wi-Fi's downlink. + * + * Message from CD to CM to allocate Virtual Priority Windows. + */ +struct coex_allocate_vpw { + uint32_t message_id; + uint32_t start_or_stop; + uint32_t wifi_window_duration; + uint32_t ps_mechanism; +}; + +/** + * struct coex_config_cm_params - Message from CD to CM to configure CM parameters + * @message_id: Indicates message ID. This is to be set to SW_CONFIGURATION. + * @first_isr_trigger_period: microseconds . used to trigger the ISR mechanism. + * @sr_window_poll_periodicity_vpw: microseconds. This is used to poll through SR window. + * that comes after Wi-Fi window ends and next SR activity starts, in the case of VPWs. + * @lead_time_from_end_of_wlan_win: microseconds. Lead time from the end of Wi-Fi window. + * (to inform AP that Wi-Fi is entering powersave) in the case of PPW and VPW generation. + * @sr_window_poll_count_threshold: This is equal to "Wi-Fi contention timeout. + * threshold"/sr_window_poll_periodicity_vpw. + * + * Message from CD to CM to configure CM parameters. + */ +struct coex_config_cm_params { + uint32_t message_id; + uint32_t first_isr_trigger_period; + uint32_t sr_window_poll_periodicity_vpw; + uint32_t lead_time_from_end_of_wlan_win; + uint32_t sr_window_poll_count_threshold; +}; + +/** + * struct coex_sr_traffic_info - Message from CM to CD to post SR traffic information. + * @message_id: Indicates message ID. This is to be set to SR_TRAFFIC_INFO. + * @sr_traffic_info: Traffic information buffer. + * + * Message from CM to CD to post SR traffic inforamtion + */ +struct coex_sr_traffic_info { + uint32_t message_id; + uint32_t sr_traffic_info[MAX_SR_TRAFFIC_BUF_SIZE]; +}; + +#endif /* __COEX_STRUCT_H__ */ diff --git a/drivers/wifi/nrfwifi/inc/fmac_main.h b/drivers/wifi/nrfwifi/inc/fmac_main.h new file mode 100644 index 000000000000..dbe6c34baa0f --- /dev/null +++ b/drivers/wifi/nrfwifi/inc/fmac_main.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing FMAC interface specific declarations for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#ifndef __ZEPHYR_FMAC_MAIN_H__ +#define __ZEPHYR_FMAC_MAIN_H__ + +#include + +#include +#include +#include +#ifndef CONFIG_NRF70_RADIO_TEST +#include +#include +#ifdef CONFIG_NETWORKING +#include +#endif /* CONFIG_NETWORKING */ +#ifdef CONFIG_NRF70_STA_MODE +#include +#endif /* CONFIG_NRF70_STA_MODE */ +#endif /* !CONFIG_NRF70_RADIO_TEST */ + +#include +#include + +#define NRF70_DRIVER_VERSION "1."KERNEL_VERSION_STRING + +#ifndef CONFIG_NRF70_RADIO_TEST +struct nrf_wifi_vif_ctx_zep { + const struct device *zep_dev_ctx; + struct net_if *zep_net_if_ctx; + void *supp_drv_if_ctx; + void *rpu_ctx_zep; + unsigned char vif_idx; + struct k_mutex vif_lock; + + scan_result_cb_t disp_scan_cb; + bool scan_in_progress; + int scan_type; + uint16_t max_bss_cnt; + unsigned int scan_res_cnt; + struct k_work_delayable scan_timeout_work; + + struct net_eth_addr mac_addr; + int if_type; + char ifname[16]; + enum nrf_wifi_fmac_if_op_state if_op_state; + bool set_if_event_received; + int set_if_status; +#ifdef CONFIG_NET_STATISTICS_ETHERNET + struct net_stats_eth eth_stats; +#endif /* CONFIG_NET_STATISTICS_ETHERNET */ +#ifdef CONFIG_NRF70_STA_MODE + unsigned int assoc_freq; + enum nrf_wifi_fmac_if_carr_state if_carr_state; + struct wpa_signal_info *signal_info; + struct wpa_conn_info *conn_info; + struct zep_wpa_supp_dev_callbk_fns supp_callbk_fns; + unsigned char twt_flows_map; + unsigned char twt_flow_in_progress_map; + struct wifi_ps_config *ps_info; + bool ps_config_info_evnt; + bool authorized; + bool cookie_resp_received; +#ifdef CONFIG_NRF70_DATA_TX + struct k_work nrf_wifi_net_iface_work; +#endif /* CONFIG_NRF70_DATA_TX */ + unsigned long rssi_record_timestamp_us; + signed short rssi; +#endif /* CONFIG_NRF70_STA_MODE */ +#ifdef CONFIG_NRF70_AP_MODE + int inactive_time_sec; +#endif /* CONFIG_NRF70_AP_MODE */ +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY + struct k_work nrf_wifi_rpu_recovery_work; +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ +}; + +struct nrf_wifi_vif_ctx_map { + const char *ifname; + struct nrf_wifi_vif_ctx_zep *vif_ctx; +}; +#endif /* !CONFIG_NRF70_RADIO_TEST */ + +struct nrf_wifi_ctx_zep { + void *drv_priv_zep; + void *rpu_ctx; +#ifdef CONFIG_NRF70_RADIO_TEST + struct rpu_conf_params conf_params; + bool rf_test_run; + unsigned char rf_test; +#else /* CONFIG_NRF70_RADIO_TEST */ + struct nrf_wifi_vif_ctx_zep vif_ctx_zep[MAX_NUM_VIFS]; +#ifdef CONFIG_NRF70_UTIL + struct rpu_conf_params conf_params; +#endif /* CONFIG_NRF70_UTIL */ +#endif /* CONFIG_NRF70_RADIO_TEST */ + unsigned char *extended_capa, *extended_capa_mask; + unsigned int extended_capa_len; + struct k_mutex rpu_lock; +}; + +struct nrf_wifi_drv_priv_zep { + struct nrf_wifi_fmac_priv *fmac_priv; + /* TODO: Replace with a linked list to handle unlimited RPUs */ + struct nrf_wifi_ctx_zep rpu_ctx_zep; +}; + +extern struct nrf_wifi_drv_priv_zep rpu_drv_priv_zep; + +void nrf_wifi_scan_timeout_work(struct k_work *work); +void configure_tx_pwr_settings(struct nrf_wifi_tx_pwr_ctrl_params *tx_pwr_ctrl_params, + struct nrf_wifi_tx_pwr_ceil_params *tx_pwr_ceil_params); +void configure_board_dep_params(struct nrf_wifi_board_params *board_params); +void set_tx_pwr_ceil_default(struct nrf_wifi_tx_pwr_ceil_params *pwr_ceil_params); +const char *nrf_wifi_get_drv_version(void); +enum nrf_wifi_status nrf_wifi_fmac_dev_add_zep(struct nrf_wifi_drv_priv_zep *drv_priv_zep); +enum nrf_wifi_status nrf_wifi_fmac_dev_rem_zep(struct nrf_wifi_drv_priv_zep *drv_priv_zep); +enum nrf_wifi_status nrf_wifi_fw_load(void *rpu_ctx); +struct nrf_wifi_vif_ctx_zep *nrf_wifi_get_vif_ctx(struct net_if *iface); +void nrf_wifi_rpu_recovery_cb(void *vif_ctx, + void *event_data, + unsigned int event_len); + +#endif /* __ZEPHYR_FMAC_MAIN_H__ */ diff --git a/drivers/wifi/nrfwifi/inc/net_if.h b/drivers/wifi/nrfwifi/inc/net_if.h new file mode 100644 index 000000000000..f4abf50c13ff --- /dev/null +++ b/drivers/wifi/nrfwifi/inc/net_if.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing network stack interface specific declarations for + * the Zephyr OS layer of the Wi-Fi driver. + */ + +#ifndef __ZEPHYR_NET_IF_H__ +#define __ZEPHYR_NET_IF_H__ +#include +#include +#include +#include +#include +#include + +#define UNICAST_MASK GENMASK(7, 1) +#define LOCAL_BIT BIT(1) + +void nrf_wifi_if_init_zep(struct net_if *iface); + +int nrf_wifi_if_start_zep(const struct device *dev); + +int nrf_wifi_if_stop_zep(const struct device *dev); + +int nrf_wifi_if_set_config_zep(const struct device *dev, + enum ethernet_config_type type, + const struct ethernet_config *config); + +int nrf_wifi_if_get_config_zep(const struct device *dev, + enum ethernet_config_type type, + struct ethernet_config *config); + +enum ethernet_hw_caps nrf_wifi_if_caps_get(const struct device *dev); + +int nrf_wifi_if_send(const struct device *dev, + struct net_pkt *pkt); + +void nrf_wifi_if_rx_frm(void *os_vif_ctx, + void *frm); + +#if defined(CONFIG_NRF70_RAW_DATA_RX) || defined(CONFIG_NRF70_PROMISC_DATA_RX) +void nrf_wifi_if_sniffer_rx_frm(void *os_vif_ctx, + void *frm, + struct raw_rx_pkt_header *raw_rx_hdr, + bool pkt_free); +#endif /* CONFIG_NRF70_RAW_DATA_RX || CONFIG_NRF70_PROMISC_DATA_RX */ + +enum nrf_wifi_status nrf_wifi_if_carr_state_chg(void *os_vif_ctx, + enum nrf_wifi_fmac_if_carr_state carr_state); + +int nrf_wifi_stats_get(const struct device *dev, + struct net_stats_wifi *stats); + +struct net_stats_eth *nrf_wifi_eth_stats_get(const struct device *dev); + +void nrf_wifi_set_iface_event_handler(void *os_vif_ctx, + struct nrf_wifi_umac_event_set_interface *event, + unsigned int event_len); +#endif /* __ZEPHYR_NET_IF_H__ */ diff --git a/drivers/wifi/nrfwifi/inc/wifi_mgmt.h b/drivers/wifi/nrfwifi/inc/wifi_mgmt.h new file mode 100644 index 000000000000..2e169bc0dcad --- /dev/null +++ b/drivers/wifi/nrfwifi/inc/wifi_mgmt.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing WiFi management operation implementations + * for the Zephyr OS. + */ + +#ifndef __ZEPHYR_WIFI_MGMT_H__ +#define __ZEPHYR_WIFI_MGMT_H__ +#include + +#include +#include + +#include "osal_api.h" + +/** Filter setting defines for sniffer mode. */ +#define WIFI_MGMT_DATA_CTRL_FILTER_SETTING 0xE +#define WIFI_ALL_FILTER_SETTING 0xF + +struct twt_interval_float { + unsigned short mantissa; + unsigned char exponent; +}; + +int nrf_wifi_set_power_save(const struct device *dev, + struct wifi_ps_params *params); + +int nrf_wifi_set_twt(const struct device *dev, + struct wifi_twt_params *twt_params); + +void nrf_wifi_event_proc_twt_setup_zep(void *vif_ctx, + struct nrf_wifi_umac_cmd_config_twt *twt_setup_info, + unsigned int event_len); + +void nrf_wifi_event_proc_twt_teardown_zep(void *vif_ctx, + struct nrf_wifi_umac_cmd_teardown_twt *twt_teardown_info, + unsigned int event_len); + +void nrf_wifi_event_proc_twt_sleep_zep(void *vif_ctx, + struct nrf_wifi_umac_event_twt_sleep *twt_sleep_info, + unsigned int event_len); + +int nrf_wifi_twt_teardown_flows(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep, + unsigned char start_flow_id, unsigned char end_flow_id); + +int nrf_wifi_get_power_save_config(const struct device *dev, + struct wifi_ps_config *ps_config); + +void nrf_wifi_event_proc_get_power_save_info(void *vif_ctx, + struct nrf_wifi_umac_event_power_save_info *ps_info, + unsigned int event_len); + +#ifdef CONFIG_NRF70_SYSTEM_WITH_RAW_MODES +int nrf_wifi_mode(const struct device *dev, + struct wifi_mode_info *mode); +#endif + +#if defined(CONFIG_NRF70_RAW_DATA_TX) || defined(CONFIG_NRF70_RAW_DATA_RX) +int nrf_wifi_channel(const struct device *dev, + struct wifi_channel_info *channel); +#endif /* CONFIG_NRF70_RAW_DATA_TX || CONFIG_NRF70_RAW_DATA_RX */ + +#if defined(CONFIG_NRF70_RAW_DATA_RX) || defined(CONFIG_NRF70_PROMISC_DATA_RX) +int nrf_wifi_filter(const struct device *dev, + struct wifi_filter_info *filter); +#endif /* CONFIG_NRF70_RAW_DATA_RX || CONFIG_NRF70_PROMISC_DATA_RX */ + +int nrf_wifi_set_rts_threshold(const struct device *dev, + unsigned int rts_threshold); +#endif /* __ZEPHYR_WIFI_MGMT_H__ */ diff --git a/drivers/wifi/nrfwifi/inc/wifi_mgmt_scan.h b/drivers/wifi/nrfwifi/inc/wifi_mgmt_scan.h new file mode 100644 index 000000000000..e97f9293a34b --- /dev/null +++ b/drivers/wifi/nrfwifi/inc/wifi_mgmt_scan.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing display scan specific declarations for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#ifndef __ZEPHYR_DISP_SCAN_H__ +#define __ZEPHYR_DISP_SCAN_H__ +#include +#include + +#include "osal_api.h" +int nrf_wifi_disp_scan_zep(const struct device *dev, struct wifi_scan_params *params, + scan_result_cb_t cb); + +enum nrf_wifi_status nrf_wifi_disp_scan_res_get_zep(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep); + +void nrf_wifi_event_proc_disp_scan_res_zep(void *vif_ctx, + struct nrf_wifi_umac_event_new_scan_display_results *scan_res, + unsigned int event_len, + bool is_last); + +#ifdef CONFIG_WIFI_MGMT_RAW_SCAN_RESULTS +void nrf_wifi_rx_bcn_prb_resp_frm(void *vif_ctx, + void *frm, + unsigned short frequency, + signed short signal); +#endif /* CONFIG_WIFI_MGMT_RAW_SCAN_RESULTS */ +#endif /* __ZEPHYR_DISP_SCAN_H__ */ diff --git a/drivers/wifi/nrfwifi/inc/wpa_supp_if.h b/drivers/wifi/nrfwifi/inc/wpa_supp_if.h new file mode 100644 index 000000000000..06a13261a706 --- /dev/null +++ b/drivers/wifi/nrfwifi/inc/wpa_supp_if.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing WPA supplicant interface specific declarations for + * the Zephyr OS layer of the Wi-Fi driver. + */ + +#ifndef __ZEPHYR_WPA_SUPP_IF_H__ +#define __ZEPHYR_WPA_SUPP_IF_H__ + + +#define RPU_RESP_EVENT_TIMEOUT 5000 +#ifdef CONFIG_NRF70_STA_MODE +#include + +void *nrf_wifi_wpa_supp_dev_init(void *supp_drv_if_ctx, const char *iface_name, + struct zep_wpa_supp_dev_callbk_fns *supp_callbk_fns); + +void nrf_wifi_wpa_supp_dev_deinit(void *if_priv); + +int nrf_wifi_wpa_supp_scan2(void *if_priv, struct wpa_driver_scan_params *params); + +int nrf_wifi_wpa_supp_scan_abort(void *if_priv); + +int nrf_wifi_wpa_supp_scan_results_get(void *if_priv); + +int nrf_wifi_wpa_supp_deauthenticate(void *if_priv, const char *addr, unsigned short reason_code); + +int nrf_wifi_wpa_supp_authenticate(void *if_priv, struct wpa_driver_auth_params *params, + struct wpa_bss *curr_bss); + +int nrf_wifi_wpa_supp_associate(void *if_priv, struct wpa_driver_associate_params *params); + +int nrf_wifi_wpa_set_supp_port(void *if_priv, int authorized, char *bssid); + +int nrf_wifi_wpa_supp_signal_poll(void *if_priv, struct wpa_signal_info *si, + unsigned char *bssid); + +int nrf_wifi_nl80211_send_mlme(void *if_priv, const u8 *data, size_t data_len, int noack, + unsigned int freq, int no_cck, int offchanok, unsigned int wait_time, + int cookie); + +int nrf_wifi_supp_get_wiphy(void *if_priv); + +int nrf_wifi_supp_register_frame(void *if_priv, + u16 type, const u8 *match, size_t match_len, + bool multicast); + +int nrf_wifi_wpa_supp_set_key(void *if_priv, + const unsigned char *ifname, + enum wpa_alg alg, + const unsigned char *addr, + int key_idx, + int set_tx, + const unsigned char *seq, + size_t seq_len, + const unsigned char *key, + size_t key_len, + enum key_flag key_flag); + +void nrf_wifi_wpa_supp_event_proc_scan_start(void *if_priv); + +void nrf_wifi_wpa_supp_event_proc_scan_done(void *if_priv, + struct nrf_wifi_umac_event_trigger_scan *scan_done_event, + unsigned int event_len, + int aborted); + +void nrf_wifi_wpa_supp_event_proc_scan_res(void *if_priv, + struct nrf_wifi_umac_event_new_scan_results *scan_res, + unsigned int event_len, + bool more_res); + +void nrf_wifi_wpa_supp_event_proc_auth_resp(void *if_priv, + struct nrf_wifi_umac_event_mlme *auth_resp, + unsigned int event_len); + +void nrf_wifi_wpa_supp_event_proc_assoc_resp(void *if_priv, + struct nrf_wifi_umac_event_mlme *assoc_resp, + unsigned int event_len); + +void nrf_wifi_wpa_supp_event_proc_deauth(void *if_priv, + struct nrf_wifi_umac_event_mlme *deauth, + unsigned int event_len); + +void nrf_wifi_wpa_supp_event_proc_disassoc(void *if_priv, + struct nrf_wifi_umac_event_mlme *disassoc, + unsigned int event_len); + +void nrf_wifi_wpa_supp_event_proc_get_sta(void *if_priv, + struct nrf_wifi_umac_event_new_station *info, + unsigned int event_len); + +void nrf_wifi_wpa_supp_event_proc_get_if(void *if_priv, + struct nrf_wifi_interface_info *info, + unsigned int event_len); + +void nrf_wifi_wpa_supp_event_mgmt_tx_status(void *if_priv, + struct nrf_wifi_umac_event_mlme *mlme_event, + unsigned int event_len); + + +void nrf_wifi_wpa_supp_event_proc_unprot_mgmt(void *if_priv, + struct nrf_wifi_umac_event_mlme *unprot_mgmt, + unsigned int event_len); + +void nrf_wifi_wpa_supp_event_get_wiphy(void *if_priv, + struct nrf_wifi_event_get_wiphy *get_wiphy, + unsigned int event_len); + +void nrf_wifi_wpa_supp_event_mgmt_rx_callbk_fn(void *if_priv, + struct nrf_wifi_umac_event_mlme *mgmt_rx_event, + unsigned int event_len); + +int nrf_wifi_supp_get_capa(void *if_priv, struct wpa_driver_capa *capa); + +void nrf_wifi_wpa_supp_event_mac_chgd(void *if_priv); +int nrf_wifi_supp_get_conn_info(void *if_priv, struct wpa_conn_info *info); + +void nrf_wifi_supp_event_proc_get_conn_info(void *os_vif_ctx, + struct nrf_wifi_umac_event_conn_info *info, + unsigned int event_len); + +#endif /* CONFIG_NRF70_STA_MODE */ +#ifdef CONFIG_NRF70_AP_MODE +int nrf_wifi_wpa_supp_init_ap(void *if_priv, struct wpa_driver_associate_params *params); +int nrf_wifi_wpa_supp_start_ap(void *if_priv, struct wpa_driver_ap_params *params); +int nrf_wifi_wpa_supp_change_beacon(void *if_priv, struct wpa_driver_ap_params *params); +int nrf_wifi_wpa_supp_stop_ap(void *if_priv); +int nrf_wifi_wpa_supp_deinit_ap(void *if_priv); +int nrf_wifi_wpa_supp_sta_add(void *if_priv, struct hostapd_sta_add_params *params); +int nrf_wifi_wpa_supp_sta_remove(void *if_priv, const u8 *addr); +int nrf_wifi_supp_register_mgmt_frame(void *if_priv, + u16 frame_type, size_t match_len, const u8 *match); +int nrf_wifi_wpa_supp_sta_set_flags(void *if_priv, const u8 *addr, + unsigned int total_flags, unsigned int flags_or, + unsigned int flags_and); +int nrf_wifi_wpa_supp_sta_get_inact_sec(void *if_priv, const u8 *addr); +#endif /* CONFIG_NRF70_AP_MODE */ +#endif /* __ZEPHYR_WPA_SUPP_IF_H__ */ diff --git a/drivers/wifi/nrfwifi/rpu_fw_patches.ld b/drivers/wifi/nrfwifi/rpu_fw_patches.ld new file mode 100644 index 000000000000..4ecbaedb0960 --- /dev/null +++ b/drivers/wifi/nrfwifi/rpu_fw_patches.ld @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Custom Linker command/script file + * + * Custom Linker script for the Cortex-M platforms. + */ + +#include +#include + +#include +#include + +#if CONFIG_BOARD_NRF5340DK_NRF5340_CPUAPP || CONFIG_BOARD_NRF52840DK_NRF52840 +/* + * nRF53/52 series ship an external flash that can be used for XIP using QSPI/SPI. + * + * Note: In nRF7002 external flash using is accessible only using SPI but there is no + * support for XIP, so, relocation cannot be used. + */ +#if CONFIG_BOARD_NRF5340DK_NRF5340_CPUAPP +#define EXTFLASH_BASE_ADDR 0x10000000 +#define EXTFLASH_SIZE 0x800000 +#elif CONFIG_BOARD_NRF52840DK_NRF52840 +#define EXTFLASH_BASE_ADDR 0x12000000 +#define EXTFLASH_SIZE 0x800000 +#endif /* CONFIG_BOARD_NRF5340DK_NRF5340_CPUAPP */ + +#if USE_PARTITION_MANAGER && PM_EXTERNAL_FLASH_ADDRESS +#include +#define EXTFLASH_ADDRESS (EXTFLASH_BASE_ADDR + PM_EXTERNAL_FLASH_ADDRESS) +#undef EXTFLASH_SIZE +#define EXTFLASH_SIZE (PM_EXTERNAL_FLASH_SIZE) +#else +#define EXTFLASH_ADDRESS (EXTFLASH_BASE_ADDR) +#endif /* USE_PARTITION_MANAGER && PM_EXTERNAL_FLASH_ADDRESS */ + +MEMORY +{ + EXTFLASH (wx) : ORIGIN = EXTFLASH_ADDRESS, LENGTH = EXTFLASH_SIZE +} + +#endif /* CONFIG_BOARD_NRF5340DK_NRF5340_CPUAPP || CONFIG_BOARD_NRF52840DK_NRF52840 */ + +#include diff --git a/drivers/wifi/nrfwifi/src/coex.c b/drivers/wifi/nrfwifi/src/coex.c new file mode 100644 index 000000000000..7bb89a07e9ed --- /dev/null +++ b/drivers/wifi/nrfwifi/src/coex.c @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @file + * @brief Coexistence functions + */ + +#include +#include +#include +#include +#include + +#include "coex.h" +#include "coex_struct.h" +#include "fmac_main.h" +#include "fmac_api.h" + +LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL); + +extern struct nrf_wifi_drv_priv_zep rpu_drv_priv_zep; +static struct nrf_wifi_ctx_zep *rpu_ctx = &rpu_drv_priv_zep.rpu_ctx_zep; + +#define CH_BASE_ADDRESS ABS_EXT_SYS_WLANSYSCOEX_CH_CONTROL +#define COEX_CONFIG_FIXED_PARAMS 4 +#define COEX_REG_CFG_OFFSET_SHIFT 24 + +/* copied from uccp530_77_registers_ext_sys_bus.h of UCCP toolkit */ +#define EXT_SYS_WLANSYSCOEX_CH_CONTROL 0x0000 +#define ABS_EXT_SYS_WLANSYSCOEX_CH_CONTROL 0xA401BC00UL +#define EXT_SYS_WLANSYSCOEX_CH_TIME_REFERENCE 0x0004 +#define EXT_SYS_WLANSYSCOEX_CH_SR_INFO_STATUS 0x0040 +#define EXT_SYS_WLANSYSCOEX_CH_NO_WINDOW_LOOKUP_0 0x008C +#define EXT_SYS_WLANSYSCOEX_CH_NO_WINDOW_LOOKUP_44 0x013C +#define EXT_SYS_WLANSYSCOEX_RESET_SHIFT 0 + +/* copied from uccp530_77_registers.h of UCCP toolkit */ +#define ABS_PMB_WLAN_MAC_CTRL_PULSED_SOFTWARE_RESET 0xA5009A00UL + +#ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH + #define NRF_RADIO_COEX_NODE DT_NODELABEL(nrf70) + static const struct gpio_dt_spec sr_rf_switch_spec = + GPIO_DT_SPEC_GET(NRF_RADIO_COEX_NODE, srrf_switch_gpios); +#endif /* CONFIG_NRF70_SR_COEX_RF_SWITCH */ + +/* PTA registers configuration of Coexistence Hardware */ +/* Separate antenna configuration, WLAN in 2.4GHz. For BLE protocol. */ +const uint16_t config_buffer_SEA_ble[] = { + 0x0019, 0x00F6, 0x0008, 0x0062, 0x00F5, + 0x00F5, 0x0019, 0x0019, 0x0074, 0x0074, + 0x0008, 0x01E2, 0x00D5, 0x00D5, 0x01F6, + 0x01F6, 0x0061, 0x0061, 0x01E2, 0x0008, + 0x0004, 0x0004, 0x0019, 0x0019, 0x0008, + 0x0008, 0x00F5, 0x00F5, 0x00D5, 0x00D5, + 0x0008, 0x01E2, 0x0051, 0x0051, 0x0074, + 0x0074, 0x00F6, 0x0019, 0x0062, 0x0019, + 0x00F6, 0x0008, 0x0062, 0x0008, 0x001A +}; + +/* Separate antenna configuration, WLAN in 2.4GHz. For non BLE protocol */ +const uint16_t config_buffer_SEA_non_ble[] = { + 0x0019, 0x00F6, 0x0008, 0x0062, 0x00F5, + 0x00F5, 0x0061, 0x0061, 0x0074, 0x0074, + 0x01E2, 0x01E2, 0x00D5, 0x00D5, 0x01F6, + 0x01F6, 0x0061, 0x0061, 0x01E2, 0x01E2, + 0x00C4, 0x00C4, 0x0061, 0x0061, 0x0008, + 0x0008, 0x00F5, 0x00F5, 0x00D5, 0x00D5, + 0x0162, 0x0162, 0x0019, 0x0019, 0x01F6, + 0x01F6, 0x00F6, 0x0019, 0x0062, 0x0019, + 0x00F6, 0x0008, 0x0062, 0x0008, 0x001A +}; + +/* Shared antenna configuration, WLAN in 2.4GHz. */ +const uint16_t config_buffer_SHA[] = { + 0x0019, 0x00F6, 0x0008, 0x00E2, 0x0015, + 0x00F5, 0x0019, 0x0019, 0x0004, 0x01F6, + 0x0008, 0x01E2, 0x00F5, 0x00F5, 0x01F6, + 0x01F6, 0x00E1, 0x00E1, 0x01E2, 0x0008, + 0x0004, 0x0004, 0x0019, 0x0019, 0x0008, + 0x0008, 0x0015, 0x00F5, 0x00F5, 0x00F5, + 0x0008, 0x01E2, 0x00E1, 0x00E1, 0x0004, + 0x01F6, 0x00F6, 0x0019, 0x00E2, 0x0019, + 0x00F6, 0x0008, 0x00E2, 0x0008, 0x001A +}; + +/* Shared/separate antennas, WLAN in 5GHz */ +const uint16_t config_buffer_5G[] = { + 0x0039, 0x0076, 0x0028, 0x0062, 0x0075, + 0x0075, 0x0061, 0x0061, 0x0074, 0x0074, + 0x0060, 0x0060, 0x0075, 0x0075, 0x0064, + 0x0064, 0x0071, 0x0071, 0x0060, 0x0060, + 0x0064, 0x0064, 0x0061, 0x0061, 0x0060, + 0x0060, 0x0075, 0x0075, 0x0075, 0x0075, + 0x0060, 0x0060, 0x0071, 0x0071, 0x0074, + 0x0074, 0x0076, 0x0039, 0x0062, 0x0039, + 0x0076, 0x0028, 0x0062, 0x0028, 0x003A +}; + +/* non-PTA register configuration of coexistence hardware */ +/* Shared antenna */ +const uint32_t ch_config_sha[] = { + 0x00000028, 0x00000000, 0x001e1023, 0x00000000, 0x00000000, + 0x00000000, 0x00000021, 0x000002ca, 0x00000050, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000 +}; + +/* Separate antennas. For BLE protocol. */ +const uint32_t ch_config_sep_ble[] = { + 0x00000028, 0x00000000, 0x001e1023, 0x00000000, 0x00000000, + 0x00000000, 0x00000021, 0x000002ca, 0x00000055, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000 +}; + +/* Separate antennas. For non BLE protocol. */ +const uint32_t ch_config_sep_non_ble[] = { + 0x00000028, 0x00000000, 0x001e1023, 0x00000000, 0x00000000, + 0x00000000, 0x00000021, 0x000002ca, 0x00000055, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000 +}; + +int nrf_wifi_coex_config_non_pta(bool separate_antennas, bool is_sr_protocol_ble) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct coex_ch_configuration params = { 0 }; + const uint32_t *config_buffer_ptr = NULL; + uint32_t start_offset = 0; + uint32_t num_reg_to_config = 1; + uint32_t cmd_len, index; + + /* Offset from the base address of CH */ + start_offset = ((EXT_SYS_WLANSYSCOEX_CH_TIME_REFERENCE - + EXT_SYS_WLANSYSCOEX_CH_CONTROL) >> 2); + /* Number of registers to be configured */ + num_reg_to_config = ((EXT_SYS_WLANSYSCOEX_CH_SR_INFO_STATUS - + EXT_SYS_WLANSYSCOEX_CH_TIME_REFERENCE) >> 2) + 1; + + if (separate_antennas) { + if (is_sr_protocol_ble) { + config_buffer_ptr = ch_config_sep_ble; + } else { + config_buffer_ptr = ch_config_sep_non_ble; + } + } else { + config_buffer_ptr = ch_config_sha; + } + + params.message_id = HW_CONFIGURATION; + params.num_reg_to_config = num_reg_to_config; + params.hw_to_config = COEX_HARDWARE; + params.hw_block_base_addr = CH_BASE_ADDRESS; + + for (index = 0; index < num_reg_to_config; index++) { + params.configbuf[index] = (start_offset << COEX_REG_CFG_OFFSET_SHIFT) | + (*(config_buffer_ptr + index)); + start_offset++; + } + + cmd_len = (COEX_CONFIG_FIXED_PARAMS + num_reg_to_config) * sizeof(uint32_t); + status = nrf_wifi_fmac_conf_srcoex(rpu_ctx->rpu_ctx, + (void *)(¶ms), cmd_len); + if (status != NRF_WIFI_STATUS_SUCCESS) { + return -1; + } + + return 0; +} + +int nrf_wifi_coex_config_pta(enum nrf_wifi_pta_wlan_op_band wlan_band, bool separate_antennas, + bool is_sr_protocol_ble) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct coex_ch_configuration params = { 0 }; + const uint16_t *config_buffer_ptr = NULL; + uint32_t start_offset = 0; + uint32_t num_reg_to_config = 1; + uint32_t cmd_len, index; + + /* common for both SHA and SEA */ + /* Indicates offset from the base address of CH */ + start_offset = ((EXT_SYS_WLANSYSCOEX_CH_NO_WINDOW_LOOKUP_0 - + EXT_SYS_WLANSYSCOEX_CH_CONTROL) >> 2); + /* Number of contiguous registers to be configured starting from base+offset */ + num_reg_to_config = ((EXT_SYS_WLANSYSCOEX_CH_NO_WINDOW_LOOKUP_44 - + EXT_SYS_WLANSYSCOEX_CH_NO_WINDOW_LOOKUP_0) >> 2) + 1; + + if (wlan_band == NRF_WIFI_PTA_WLAN_OP_BAND_2_4_GHZ) { + /* WLAN operating in 2.4GHz */ + if (separate_antennas) { + /* separate antennas configuration */ + if (is_sr_protocol_ble) { + config_buffer_ptr = config_buffer_SEA_ble; + } else { + config_buffer_ptr = config_buffer_SEA_non_ble; + } + } else { + /* Shared antenna configuration */ + config_buffer_ptr = config_buffer_SHA; + } + } else if (wlan_band == NRF_WIFI_PTA_WLAN_OP_BAND_5_GHZ) { + /* WLAN operating in 5GHz */ + config_buffer_ptr = config_buffer_5G; + } else { + return -EINVAL; + } + + params.message_id = HW_CONFIGURATION; + params.num_reg_to_config = num_reg_to_config; + params.hw_to_config = COEX_HARDWARE; + params.hw_block_base_addr = CH_BASE_ADDRESS; + + for (index = 0; index < num_reg_to_config; index++) { + params.configbuf[index] = (start_offset << COEX_REG_CFG_OFFSET_SHIFT) | + (*(config_buffer_ptr+index)); + start_offset++; + } + + cmd_len = (COEX_CONFIG_FIXED_PARAMS + num_reg_to_config) * sizeof(uint32_t); + status = nrf_wifi_fmac_conf_srcoex(rpu_ctx->rpu_ctx, + (void *)(¶ms), cmd_len); + if (status != NRF_WIFI_STATUS_SUCCESS) { + return -1; + } + + return 0; +} +#ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH +int nrf_wifi_config_sr_switch(bool separate_antennas) +{ + int ret; + + if (!device_is_ready(sr_rf_switch_spec.port)) { + LOG_ERR("Unable to open GPIO device"); + return -ENODEV; + } + + ret = gpio_pin_configure_dt(&sr_rf_switch_spec, GPIO_OUTPUT); + if (ret < 0) { + LOG_ERR("Unable to configure GPIO device"); + return -1; + } + + if (separate_antennas) { + gpio_pin_set_dt(&sr_rf_switch_spec, 0x0); + LOG_INF("GPIO P1.10 set to 0"); + } else { + gpio_pin_set_dt(&sr_rf_switch_spec, 0x1); + LOG_INF("GPIO P1.10 set to 1"); + } + + return 0; +} +#endif /* CONFIG_NRF70_SR_COEX_RF_SWITCH */ + +int nrf_wifi_coex_hw_reset(void) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct coex_ch_configuration params = { 0 }; + uint32_t num_reg_to_config = 1; + uint32_t start_offset = 0; + uint32_t index = 0; + uint32_t coex_hw_reset = 1; + uint32_t cmd_len; + + /* reset CH */ + params.message_id = HW_CONFIGURATION; + params.num_reg_to_config = num_reg_to_config; + params.hw_to_config = COEX_HARDWARE; + params.hw_block_base_addr = CH_BASE_ADDRESS; + + start_offset = ((EXT_SYS_WLANSYSCOEX_CH_CONTROL - EXT_SYS_WLANSYSCOEX_CH_CONTROL) >> 2); + params.configbuf[index] = (start_offset << COEX_REG_CFG_OFFSET_SHIFT) | + (coex_hw_reset << EXT_SYS_WLANSYSCOEX_RESET_SHIFT); + + cmd_len = (COEX_CONFIG_FIXED_PARAMS + num_reg_to_config) * sizeof(uint32_t); + + status = nrf_wifi_fmac_conf_srcoex(rpu_ctx->rpu_ctx, + (void *)(¶ms), cmd_len); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("CH reset configuration failed"); + return -1; + } + + return 0; +} + +#ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH +int sr_gpio_config(void) +{ + int ret; + + if (!device_is_ready(sr_rf_switch_spec.port)) { + return -ENODEV; + } + + ret = gpio_pin_configure_dt(&sr_rf_switch_spec, GPIO_OUTPUT); + if (ret) { + LOG_ERR("SR GPIO configuration failed %d", ret); + } + + return ret; +} + +int sr_gpio_remove(void) +{ + int ret; + + ret = gpio_pin_configure_dt(&sr_rf_switch_spec, GPIO_DISCONNECTED); + if (ret) { + LOG_ERR("SR GPIO remove failed %d", ret); + } + + return ret; +} + +int sr_ant_switch(unsigned int ant_switch) +{ + int ret; + + ret = gpio_pin_set_dt(&sr_rf_switch_spec, ant_switch & 0x1); + if (ret) { + LOG_ERR("SR GPIO set failed %d", ret); + } + + return ret; +} +#endif /* CONFIG_NRF70_SR_COEX_RF_SWITCH */ diff --git a/drivers/wifi/nrfwifi/src/fmac_main.c b/drivers/wifi/nrfwifi/src/fmac_main.c new file mode 100644 index 000000000000..a6ae71e4b3cd --- /dev/null +++ b/drivers/wifi/nrfwifi/src/fmac_main.c @@ -0,0 +1,933 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief File containing FMAC interface specific definitions for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#include + +#include +#ifdef CONFIG_NET_L2_ETHERNET +#include +#endif +#include +#include +#include +#include + + +#include +#include +#include "fmac_util.h" +#include + +#ifndef CONFIG_NRF70_RADIO_TEST +#ifdef CONFIG_NRF70_STA_MODE +#include +#include +#include +#include +#else +#include +#include +#endif /* CONFIG_NRF70_STA_MODE */ +#include + +#endif /* !CONFIG_NRF70_RADIO_TEST */ + +#define DT_DRV_COMPAT nordic_wlan +LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL); + +struct nrf_wifi_drv_priv_zep rpu_drv_priv_zep; + +/* 3 bytes for addreess, 3 bytes for length */ +#define MAX_PKT_RAM_TX_ALIGN_OVERHEAD 6 +#ifndef CONFIG_NRF70_RADIO_TEST +#ifdef CONFIG_NRF70_DATA_TX + +#define MAX_RX_QUEUES 3 + +#define MAX_TX_FRAME_SIZE \ + (CONFIG_NRF_WIFI_IFACE_MTU + NRF_WIFI_FMAC_ETH_HDR_LEN + TX_BUF_HEADROOM) +#define TOTAL_TX_SIZE \ + (CONFIG_NRF70_MAX_TX_TOKENS * CONFIG_NRF70_TX_MAX_DATA_SIZE) +#define TOTAL_RX_SIZE \ + (CONFIG_NRF70_RX_NUM_BUFS * CONFIG_NRF70_RX_MAX_DATA_SIZE) + +BUILD_ASSERT(CONFIG_NRF70_MAX_TX_TOKENS >= 1, + "At least one TX token is required"); +BUILD_ASSERT(CONFIG_NRF70_MAX_TX_AGGREGATION <= 15, + "Max TX aggregation is 15"); +BUILD_ASSERT(CONFIG_NRF70_RX_NUM_BUFS >= 1, + "At least one RX buffer is required"); +BUILD_ASSERT(RPU_PKTRAM_SIZE - TOTAL_RX_SIZE >= TOTAL_TX_SIZE, + "Packet RAM overflow: not enough memory for TX"); + +BUILD_ASSERT(CONFIG_NRF70_TX_MAX_DATA_SIZE >= MAX_TX_FRAME_SIZE, + "TX buffer size must be at least as big as the MTU and headroom"); + +static const unsigned char aggregation = 1; +static const unsigned char wmm = 1; +static const unsigned char max_num_tx_agg_sessions = 4; +static const unsigned char max_num_rx_agg_sessions = 8; +static const unsigned char reorder_buf_size = 16; +static const unsigned char max_rxampdu_size = MAX_RX_AMPDU_SIZE_64KB; + +static const unsigned char max_tx_aggregation = CONFIG_NRF70_MAX_TX_AGGREGATION; + +static const unsigned int rx1_num_bufs = CONFIG_NRF70_RX_NUM_BUFS / MAX_RX_QUEUES; +static const unsigned int rx2_num_bufs = CONFIG_NRF70_RX_NUM_BUFS / MAX_RX_QUEUES; +static const unsigned int rx3_num_bufs = CONFIG_NRF70_RX_NUM_BUFS / MAX_RX_QUEUES; + +static const unsigned int rx1_buf_sz = CONFIG_NRF70_RX_MAX_DATA_SIZE; +static const unsigned int rx2_buf_sz = CONFIG_NRF70_RX_MAX_DATA_SIZE; +static const unsigned int rx3_buf_sz = CONFIG_NRF70_RX_MAX_DATA_SIZE; + +static const unsigned char rate_protection_type; +#else +/* Reduce buffers to Scan only operation */ +static const unsigned int rx1_num_bufs = 2; +static const unsigned int rx2_num_bufs = 2; +static const unsigned int rx3_num_bufs = 2; + +static const unsigned int rx1_buf_sz = 1000; +static const unsigned int rx2_buf_sz = 1000; +static const unsigned int rx3_buf_sz = 1000; +#endif + +struct nrf_wifi_drv_priv_zep rpu_drv_priv_zep; +static K_MUTEX_DEFINE(reg_lock); + +const char *nrf_wifi_get_drv_version(void) +{ + return NRF70_DRIVER_VERSION; +} + +/* If the interface is not Wi-Fi then errors are expected, so, fail silently */ +struct nrf_wifi_vif_ctx_zep *nrf_wifi_get_vif_ctx(struct net_if *iface) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx = &rpu_drv_priv_zep.rpu_ctx_zep; + + if (!iface || !rpu_ctx || !rpu_ctx->rpu_ctx) { + return NULL; + } + + for (int i = 0; i < ARRAY_SIZE(rpu_ctx->vif_ctx_zep); i++) { + if (rpu_ctx->vif_ctx_zep[i].zep_net_if_ctx == iface) { + vif_ctx_zep = &rpu_ctx->vif_ctx_zep[i]; + break; + } + } + + return vif_ctx_zep; +} + +void nrf_wifi_event_proc_scan_start_zep(void *if_priv, + struct nrf_wifi_umac_event_trigger_scan *scan_start_event, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + vif_ctx_zep = if_priv; + + if (vif_ctx_zep->scan_type == SCAN_DISPLAY) { + return; + } + +#ifdef CONFIG_NRF70_STA_MODE + nrf_wifi_wpa_supp_event_proc_scan_start(if_priv); +#endif /* CONFIG_NRF70_STA_MODE */ +} + + +void nrf_wifi_event_proc_scan_done_zep(void *vif_ctx, + struct nrf_wifi_umac_event_trigger_scan *scan_done_event, + unsigned int event_len) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + vif_ctx_zep = vif_ctx; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return; + } + + switch (vif_ctx_zep->scan_type) { +#ifdef CONFIG_NET_L2_WIFI_MGMT + case SCAN_DISPLAY: + status = nrf_wifi_disp_scan_res_get_zep(vif_ctx_zep); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_disp_scan_res_get_zep failed", __func__); + return; + } + vif_ctx_zep->scan_in_progress = false; + break; +#endif /* CONFIG_NET_L2_WIFI_MGMT */ +#ifdef CONFIG_NRF70_STA_MODE + case SCAN_CONNECT: + nrf_wifi_wpa_supp_event_proc_scan_done(vif_ctx_zep, + scan_done_event, + event_len, + 0); + break; +#endif /* CONFIG_NRF70_STA_MODE */ + default: + LOG_ERR("%s: Scan type = %d not supported yet", __func__, vif_ctx_zep->scan_type); + return; + } + + status = NRF_WIFI_STATUS_SUCCESS; +} + +void nrf_wifi_scan_timeout_work(struct k_work *work) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + + vif_ctx_zep = CONTAINER_OF(work, struct nrf_wifi_vif_ctx_zep, scan_timeout_work.work); + + if (!vif_ctx_zep->scan_in_progress) { + LOG_INF("%s: Scan not in progress", __func__); + return; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + +#ifdef CONFIG_NET_L2_WIFI_MGMT + if (vif_ctx_zep->disp_scan_cb) { + vif_ctx_zep->disp_scan_cb(vif_ctx_zep->zep_net_if_ctx, -ETIMEDOUT, NULL); + vif_ctx_zep->disp_scan_cb = NULL; + } else +#endif /* CONFIG_NET_L2_WIFI_MGMT */ + { +#ifdef CONFIG_NRF70_STA_MODE + /* WPA supplicant scan */ + union wpa_event_data event; + struct scan_info *info = NULL; + + memset(&event, 0, sizeof(event)); + + info = &event.scan_info; + + info->aborted = 0; + info->external_scan = 0; + info->nl_scan_event = 1; + + if (vif_ctx_zep->supp_drv_if_ctx && + vif_ctx_zep->supp_callbk_fns.scan_done) { + vif_ctx_zep->supp_callbk_fns.scan_done(vif_ctx_zep->supp_drv_if_ctx, + &event); + } +#endif /* CONFIG_NRF70_STA_MODE */ + } + + vif_ctx_zep->scan_in_progress = false; +} + +#ifdef CONFIG_NRF70_STA_MODE +static void nrf_wifi_process_rssi_from_rx(void *vif_ctx, + signed short signal) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = vif_ctx; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + + vif_ctx_zep = vif_ctx; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return; + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + + vif_ctx_zep->rssi = MBM_TO_DBM(signal); + vif_ctx_zep->rssi_record_timestamp_us = + nrf_wifi_osal_time_get_curr_us(fmac_dev_ctx->fpriv->opriv); +} +#endif /* CONFIG_NRF70_STA_MODE */ + + +void nrf_wifi_event_get_reg_zep(void *vif_ctx, + struct nrf_wifi_reg *get_reg_event, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + + LOG_DBG("%s: alpha2 = %c%c", __func__, + get_reg_event->nrf_wifi_alpha2[0], + get_reg_event->nrf_wifi_alpha2[1]); + vif_ctx_zep = vif_ctx; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + + if (fmac_dev_ctx->alpha2_valid) { + LOG_ERR("%s: Unsolicited regulatory get!", __func__); + return; + } + + memcpy(&fmac_dev_ctx->alpha2, + &get_reg_event->nrf_wifi_alpha2, + sizeof(get_reg_event->nrf_wifi_alpha2)); + + fmac_dev_ctx->reg_chan_count = get_reg_event->num_channels; + memcpy(fmac_dev_ctx->reg_chan_info, + &get_reg_event->chn_info, + fmac_dev_ctx->reg_chan_count * + sizeof(struct nrf_wifi_get_reg_chn_info)); + + fmac_dev_ctx->alpha2_valid = true; +} + +int nrf_wifi_reg_domain(const struct device *dev, struct wifi_reg_domain *reg_domain) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_fmac_reg_info reg_domain_info = {0}; + struct wifi_reg_chan_info *chan_info = NULL; + struct nrf_wifi_get_reg_chn_info *reg_domain_chan_info = NULL; + int ret = -1; + int chan_idx = 0; + + k_mutex_lock(®_lock, K_FOREVER); + + if (!dev || !reg_domain) { + goto err; + } + + vif_ctx_zep = dev->data; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + goto err; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + goto err; + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + if (!fmac_dev_ctx) { + LOG_ERR("%s: fmac_dev_ctx is NULL", __func__); + goto err; + } + + if (reg_domain->oper == WIFI_MGMT_SET) { +#ifndef CONFIG_NRF70_RADIO_TEST +#ifdef CONFIG_NRF70_STA_MODE + /* Need to check if WPA supplicant is initialized or not. + * Must be checked when CONFIG_WIFI_NM_WPA_SUPPLICANT is enabled. + * Not applicable for RADIO_TEST or when + * CONFIG_WIFI_NM_WPA_SUPPLICANT is not enabled. + */ + /* It is possbile that during supplicant initialization driver may + * get the command. lock will try to ensure that supplicant + * initialization is complete. + */ + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if ((!vif_ctx_zep->supp_drv_if_ctx) || + (!wifi_nm_get_instance_iface(vif_ctx_zep->zep_net_if_ctx))) { + LOG_ERR("%s: WPA supplicant initialization not complete yet", __func__); + k_mutex_unlock(&vif_ctx_zep->vif_lock); + goto err; + } + k_mutex_unlock(&vif_ctx_zep->vif_lock); +#endif /* CONFIG_NRF70_STA_MODE */ +#endif /* !CONFIG_NRF70_RADIO_TEST */ + memcpy(reg_domain_info.alpha2, reg_domain->country_code, WIFI_COUNTRY_CODE_LEN); + + reg_domain_info.force = reg_domain->force; + + status = nrf_wifi_fmac_set_reg(fmac_dev_ctx, ®_domain_info); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Failed to set regulatory domain", __func__); + goto err; + } + } else if (reg_domain->oper == WIFI_MGMT_GET) { + + if (!reg_domain->chan_info) { + LOG_ERR("%s: Invalid regulatory info (NULL)\n", __func__); + goto err; + } + + status = nrf_wifi_fmac_get_reg(fmac_dev_ctx, ®_domain_info); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Failed to get regulatory domain", __func__); + goto err; + } + + memcpy(reg_domain->country_code, reg_domain_info.alpha2, WIFI_COUNTRY_CODE_LEN); + reg_domain->num_channels = reg_domain_info.reg_chan_count; + + for (chan_idx = 0; chan_idx < reg_domain_info.reg_chan_count; chan_idx++) { + chan_info = &(reg_domain->chan_info[chan_idx]); + reg_domain_chan_info = &(reg_domain_info.reg_chan_info[chan_idx]); + chan_info->center_frequency = reg_domain_chan_info->center_frequency; + chan_info->dfs = !!reg_domain_chan_info->dfs; + chan_info->max_power = reg_domain_chan_info->max_power; + chan_info->passive_only = !!reg_domain_chan_info->passive_channel; + chan_info->supported = !!reg_domain_chan_info->supported; + } + } else { + LOG_ERR("%s: Invalid operation: %d", __func__, reg_domain->oper); + goto err; + } + + ret = 0; +err: + k_mutex_unlock(®_lock); + return ret; +} +#ifdef CONFIG_NRF70_STA_MODE +void nrf_wifi_event_proc_cookie_rsp(void *vif_ctx, + struct nrf_wifi_umac_event_cookie_rsp *cookie_rsp_event, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + + vif_ctx_zep = vif_ctx; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + + LOG_DBG("%s: cookie_rsp_event->cookie = %llx", __func__, cookie_rsp_event->cookie); + LOG_DBG("%s: host_cookie = %llx", __func__, cookie_rsp_event->host_cookie); + LOG_DBG("%s: mac_addr = %x:%x:%x:%x:%x:%x", __func__, + cookie_rsp_event->mac_addr[0], + cookie_rsp_event->mac_addr[1], + cookie_rsp_event->mac_addr[2], + cookie_rsp_event->mac_addr[3], + cookie_rsp_event->mac_addr[4], + cookie_rsp_event->mac_addr[5]); + + vif_ctx_zep->cookie_resp_received = true; + /* TODO: When supp_callbk_fns.mgmt_tx_status is implemented, add logic + * here to use the cookie and host_cookie to map requests to responses. + */ +} +#endif /* CONFIG_NRF70_STA_MODE */ + +void reg_change_callbk_fn(void *vif_ctx, + struct nrf_wifi_event_regulatory_change *reg_change_event, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + + vif_ctx_zep = vif_ctx; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return; + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + if (!fmac_dev_ctx) { + LOG_ERR("%s: fmac_dev_ctx is NULL", __func__); + return; + } + + fmac_dev_ctx->reg_change = k_malloc(sizeof(struct nrf_wifi_event_regulatory_change)); + if (!fmac_dev_ctx->reg_change) { + LOG_ERR("%s: Failed to allocate memory for reg_change", __func__); + return; + } + + memcpy(fmac_dev_ctx->reg_change, + reg_change_event, + sizeof(struct nrf_wifi_event_regulatory_change)); + fmac_dev_ctx->reg_set_status = true; +} +#endif /* !CONFIG_NRF70_RADIO_TEST */ + +/* DTS uses 1dBm as the unit for TX power, while the RPU uses 0.25dBm */ +#define MAX_TX_PWR(label) DT_PROP(DT_NODELABEL(nrf70), label) * 4 + +void configure_tx_pwr_settings(struct nrf_wifi_tx_pwr_ctrl_params *tx_pwr_ctrl_params, + struct nrf_wifi_tx_pwr_ceil_params *tx_pwr_ceil_params) +{ + tx_pwr_ctrl_params->ant_gain_2g = CONFIG_NRF70_ANT_GAIN_2G; + tx_pwr_ctrl_params->ant_gain_5g_band1 = CONFIG_NRF70_ANT_GAIN_5G_BAND1; + tx_pwr_ctrl_params->ant_gain_5g_band2 = CONFIG_NRF70_ANT_GAIN_5G_BAND2; + tx_pwr_ctrl_params->ant_gain_5g_band3 = CONFIG_NRF70_ANT_GAIN_5G_BAND3; + tx_pwr_ctrl_params->band_edge_2g_lo_dss = CONFIG_NRF70_BAND_2G_LOWER_EDGE_BACKOFF_DSSS; + tx_pwr_ctrl_params->band_edge_2g_lo_ht = CONFIG_NRF70_BAND_2G_LOWER_EDGE_BACKOFF_HT; + tx_pwr_ctrl_params->band_edge_2g_lo_he = CONFIG_NRF70_BAND_2G_LOWER_EDGE_BACKOFF_HE; + tx_pwr_ctrl_params->band_edge_2g_hi_dsss = CONFIG_NRF70_BAND_2G_UPPER_EDGE_BACKOFF_DSSS; + tx_pwr_ctrl_params->band_edge_2g_hi_ht = CONFIG_NRF70_BAND_2G_UPPER_EDGE_BACKOFF_HT; + tx_pwr_ctrl_params->band_edge_2g_hi_he = CONFIG_NRF70_BAND_2G_UPPER_EDGE_BACKOFF_HE; + tx_pwr_ctrl_params->band_edge_5g_unii_1_lo_ht = + CONFIG_NRF70_BAND_UNII_1_LOWER_EDGE_BACKOFF_HT; + tx_pwr_ctrl_params->band_edge_5g_unii_1_lo_he = + CONFIG_NRF70_BAND_UNII_1_LOWER_EDGE_BACKOFF_HE; + tx_pwr_ctrl_params->band_edge_5g_unii_1_hi_ht = + CONFIG_NRF70_BAND_UNII_1_UPPER_EDGE_BACKOFF_HT; + tx_pwr_ctrl_params->band_edge_5g_unii_1_hi_he = + CONFIG_NRF70_BAND_UNII_1_UPPER_EDGE_BACKOFF_HE; + tx_pwr_ctrl_params->band_edge_5g_unii_2a_lo_ht = + CONFIG_NRF70_BAND_UNII_2A_LOWER_EDGE_BACKOFF_HT; + tx_pwr_ctrl_params->band_edge_5g_unii_2a_lo_he = + CONFIG_NRF70_BAND_UNII_2A_LOWER_EDGE_BACKOFF_HE; + tx_pwr_ctrl_params->band_edge_5g_unii_2a_hi_ht = + CONFIG_NRF70_BAND_UNII_2A_UPPER_EDGE_BACKOFF_HT; + tx_pwr_ctrl_params->band_edge_5g_unii_2a_hi_he = + CONFIG_NRF70_BAND_UNII_2A_UPPER_EDGE_BACKOFF_HE; + tx_pwr_ctrl_params->band_edge_5g_unii_2c_lo_ht = + CONFIG_NRF70_BAND_UNII_2C_LOWER_EDGE_BACKOFF_HT; + tx_pwr_ctrl_params->band_edge_5g_unii_2c_lo_he = + CONFIG_NRF70_BAND_UNII_2C_LOWER_EDGE_BACKOFF_HE; + tx_pwr_ctrl_params->band_edge_5g_unii_2c_hi_ht = + CONFIG_NRF70_BAND_UNII_2C_UPPER_EDGE_BACKOFF_HT; + tx_pwr_ctrl_params->band_edge_5g_unii_2c_hi_he = + CONFIG_NRF70_BAND_UNII_2C_UPPER_EDGE_BACKOFF_HE; + tx_pwr_ctrl_params->band_edge_5g_unii_3_lo_ht = + CONFIG_NRF70_BAND_UNII_3_LOWER_EDGE_BACKOFF_HT; + tx_pwr_ctrl_params->band_edge_5g_unii_3_lo_he = + CONFIG_NRF70_BAND_UNII_3_LOWER_EDGE_BACKOFF_HE; + tx_pwr_ctrl_params->band_edge_5g_unii_3_hi_ht = + CONFIG_NRF70_BAND_UNII_3_UPPER_EDGE_BACKOFF_HT; + tx_pwr_ctrl_params->band_edge_5g_unii_3_hi_he = + CONFIG_NRF70_BAND_UNII_3_UPPER_EDGE_BACKOFF_HE; + tx_pwr_ctrl_params->band_edge_5g_unii_4_lo_ht = + CONFIG_NRF70_BAND_UNII_4_LOWER_EDGE_BACKOFF_HT; + tx_pwr_ctrl_params->band_edge_5g_unii_4_lo_he = + CONFIG_NRF70_BAND_UNII_4_LOWER_EDGE_BACKOFF_HE; + tx_pwr_ctrl_params->band_edge_5g_unii_4_hi_ht = + CONFIG_NRF70_BAND_UNII_4_UPPER_EDGE_BACKOFF_HT; + tx_pwr_ctrl_params->band_edge_5g_unii_4_hi_he = + CONFIG_NRF70_BAND_UNII_4_UPPER_EDGE_BACKOFF_HE; + + + tx_pwr_ceil_params->max_pwr_2g_dsss = MAX_TX_PWR(wifi_max_tx_pwr_2g_dsss); + tx_pwr_ceil_params->max_pwr_2g_mcs7 = MAX_TX_PWR(wifi_max_tx_pwr_2g_mcs7); + tx_pwr_ceil_params->max_pwr_2g_mcs0 = MAX_TX_PWR(wifi_max_tx_pwr_2g_mcs0); +#ifndef CONFIG_NRF70_2_4G_ONLY + tx_pwr_ceil_params->max_pwr_5g_low_mcs7 = MAX_TX_PWR(wifi_max_tx_pwr_5g_low_mcs7); + tx_pwr_ceil_params->max_pwr_5g_mid_mcs7 = MAX_TX_PWR(wifi_max_tx_pwr_5g_mid_mcs7); + tx_pwr_ceil_params->max_pwr_5g_high_mcs7 = MAX_TX_PWR(wifi_max_tx_pwr_5g_high_mcs7); + tx_pwr_ceil_params->max_pwr_5g_low_mcs0 = MAX_TX_PWR(wifi_max_tx_pwr_5g_low_mcs0); + tx_pwr_ceil_params->max_pwr_5g_mid_mcs0 = MAX_TX_PWR(wifi_max_tx_pwr_5g_mid_mcs0); + tx_pwr_ceil_params->max_pwr_5g_high_mcs0 = MAX_TX_PWR(wifi_max_tx_pwr_5g_high_mcs0); +#endif /* CONFIG_NRF70_2_4G_ONLY */ +} + +void configure_board_dep_params(struct nrf_wifi_board_params *board_params) +{ + board_params->pcb_loss_2g = CONFIG_NRF70_PCB_LOSS_2G; +#ifndef CONFIG_NRF70_2_4G_ONLY + board_params->pcb_loss_5g_band1 = CONFIG_NRF70_PCB_LOSS_5G_BAND1; + board_params->pcb_loss_5g_band2 = CONFIG_NRF70_PCB_LOSS_5G_BAND2; + board_params->pcb_loss_5g_band3 = CONFIG_NRF70_PCB_LOSS_5G_BAND3; +#endif /* CONFIG_NRF70_2_4G_ONLY */ +} + +enum nrf_wifi_status nrf_wifi_fmac_dev_add_zep(struct nrf_wifi_drv_priv_zep *drv_priv_zep) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + void *rpu_ctx = NULL; + enum op_band op_band = CONFIG_NRF_WIFI_OP_BAND; +#ifdef CONFIG_NRF_WIFI_LOW_POWER + int sleep_type = -1; + +#ifndef CONFIG_NRF70_RADIO_TEST + sleep_type = HW_SLEEP_ENABLE; +#else + sleep_type = SLEEP_DISABLE; +#endif /* CONFIG_NRF70_RADIO_TEST */ +#endif /* CONFIG_NRF_WIFI_LOW_POWER */ + struct nrf_wifi_tx_pwr_ctrl_params tx_pwr_ctrl_params; + struct nrf_wifi_tx_pwr_ceil_params tx_pwr_ceil_params; + struct nrf_wifi_board_params board_params; + + unsigned int fw_ver = 0; + + rpu_ctx_zep = &drv_priv_zep->rpu_ctx_zep; + + rpu_ctx_zep->drv_priv_zep = drv_priv_zep; + + rpu_ctx = nrf_wifi_fmac_dev_add(drv_priv_zep->fmac_priv, rpu_ctx_zep); + + if (!rpu_ctx) { + LOG_ERR("%s: nrf_wifi_fmac_dev_add failed", __func__); + rpu_ctx_zep = NULL; + goto err; + } + + rpu_ctx_zep->rpu_ctx = rpu_ctx; + + status = nrf_wifi_fw_load(rpu_ctx); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fw_load failed", __func__); + goto err; + } + + status = nrf_wifi_fmac_ver_get(rpu_ctx, + &fw_ver); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: FW version read failed", __func__); + goto err; + } + + LOG_DBG("Firmware (v%d.%d.%d.%d) booted successfully", + NRF_WIFI_UMAC_VER(fw_ver), + NRF_WIFI_UMAC_VER_MAJ(fw_ver), + NRF_WIFI_UMAC_VER_MIN(fw_ver), + NRF_WIFI_UMAC_VER_EXTRA(fw_ver)); + + configure_tx_pwr_settings(&tx_pwr_ctrl_params, + &tx_pwr_ceil_params); + + configure_board_dep_params(&board_params); + +#ifdef CONFIG_NRF70_RADIO_TEST + status = nrf_wifi_fmac_dev_init_rt(rpu_ctx_zep->rpu_ctx, +#ifdef CONFIG_NRF_WIFI_LOW_POWER + sleep_type, +#endif /* CONFIG_NRF_WIFI_LOW_POWER */ + NRF_WIFI_DEF_PHY_CALIB, + op_band, + IS_ENABLED(CONFIG_NRF_WIFI_BEAMFORMING), + &tx_pwr_ctrl_params, + &tx_pwr_ceil_params, + &board_params); +#else + status = nrf_wifi_fmac_dev_init(rpu_ctx_zep->rpu_ctx, +#ifdef CONFIG_NRF_WIFI_LOW_POWER + sleep_type, +#endif /* CONFIG_NRF_WIFI_LOW_POWER */ + NRF_WIFI_DEF_PHY_CALIB, + op_band, + IS_ENABLED(CONFIG_NRF_WIFI_BEAMFORMING), + &tx_pwr_ctrl_params, + &tx_pwr_ceil_params, + &board_params); +#endif /* CONFIG_NRF70_RADIO_TEST */ + + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_dev_init failed", __func__); + goto err; + } + + k_mutex_init(&rpu_ctx_zep->rpu_lock); + + return status; +err: + if (rpu_ctx) { +#ifdef CONFIG_NRF70_RADIO_TEST + nrf_wifi_fmac_dev_rem_rt(rpu_ctx); +#else + nrf_wifi_fmac_dev_rem(rpu_ctx); +#endif /* CONFIG_NRF70_RADIO_TEST */ + rpu_ctx_zep->rpu_ctx = NULL; + } + return status; +} + +enum nrf_wifi_status nrf_wifi_fmac_dev_rem_zep(struct nrf_wifi_drv_priv_zep *drv_priv_zep) +{ + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + + rpu_ctx_zep = &drv_priv_zep->rpu_ctx_zep; +#ifdef CONFIG_NRF70_RADIO_TEST + nrf_wifi_fmac_dev_deinit_rt(rpu_ctx_zep->rpu_ctx); + nrf_wifi_fmac_dev_rem_rt(rpu_ctx_zep->rpu_ctx); +#else + nrf_wifi_fmac_dev_deinit(rpu_ctx_zep->rpu_ctx); + nrf_wifi_fmac_dev_rem(rpu_ctx_zep->rpu_ctx); +#endif /* CONFIG_NRF70_RADIO_TEST */ + + k_free(rpu_ctx_zep->extended_capa); + rpu_ctx_zep->extended_capa = NULL; + k_free(rpu_ctx_zep->extended_capa_mask); + rpu_ctx_zep->extended_capa_mask = NULL; + + rpu_ctx_zep->rpu_ctx = NULL; + LOG_DBG("%s: FMAC device removed", __func__); + + return NRF_WIFI_STATUS_SUCCESS; +} + +static int nrf_wifi_drv_main_zep(const struct device *dev) +{ +#ifndef CONFIG_NRF70_RADIO_TEST + struct nrf_wifi_fmac_callbk_fns callbk_fns = { 0 }; + struct nrf_wifi_data_config_params data_config = { 0 }; + struct rx_buf_pool_params rx_buf_pools[MAX_NUM_OF_RX_QUEUES]; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = dev->data; + + vif_ctx_zep->rpu_ctx_zep = &rpu_drv_priv_zep.rpu_ctx_zep; + +#ifdef CONFIG_NRF70_DATA_TX + data_config.aggregation = aggregation; + data_config.wmm = wmm; + data_config.max_num_tx_agg_sessions = max_num_tx_agg_sessions; + data_config.max_num_rx_agg_sessions = max_num_rx_agg_sessions; + data_config.max_tx_aggregation = max_tx_aggregation; + data_config.reorder_buf_size = reorder_buf_size; + data_config.max_rxampdu_size = max_rxampdu_size; + data_config.rate_protection_type = rate_protection_type; + callbk_fns.if_carr_state_chg_callbk_fn = nrf_wifi_if_carr_state_chg; + callbk_fns.rx_frm_callbk_fn = nrf_wifi_if_rx_frm; +#if defined(CONFIG_NRF70_RAW_DATA_RX) || defined(CONFIG_NRF70_PROMISC_DATA_RX) + callbk_fns.rx_sniffer_frm_callbk_fn = nrf_wifi_if_sniffer_rx_frm; +#endif /* CONFIG_NRF70_RAW_DATA_RX || CONFIG_NRF70_PROMISC_DATA_RX */ +#endif + rx_buf_pools[0].num_bufs = rx1_num_bufs; + rx_buf_pools[1].num_bufs = rx2_num_bufs; + rx_buf_pools[2].num_bufs = rx3_num_bufs; + rx_buf_pools[0].buf_sz = rx1_buf_sz; + rx_buf_pools[1].buf_sz = rx2_buf_sz; + rx_buf_pools[2].buf_sz = rx3_buf_sz; + +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY + callbk_fns.rpu_recovery_callbk_fn = nrf_wifi_rpu_recovery_cb; +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ + callbk_fns.scan_start_callbk_fn = nrf_wifi_event_proc_scan_start_zep; + callbk_fns.scan_done_callbk_fn = nrf_wifi_event_proc_scan_done_zep; + callbk_fns.reg_change_callbk_fn = reg_change_callbk_fn; +#ifdef CONFIG_NET_L2_WIFI_MGMT + callbk_fns.disp_scan_res_callbk_fn = nrf_wifi_event_proc_disp_scan_res_zep; +#endif /* CONFIG_NET_L2_WIFI_MGMT */ +#ifdef CONFIG_WIFI_MGMT_RAW_SCAN_RESULTS + callbk_fns.rx_bcn_prb_resp_callbk_fn = nrf_wifi_rx_bcn_prb_resp_frm; +#endif /* CONFIG_WIFI_MGMT_RAW_SCAN_RESULTS */ +#if defined(CONFIG_NRF70_SYSTEM_MODE) || defined(CONFIG_NRF70_SYSTEM_WITH_RAW_MODES) + callbk_fns.set_if_callbk_fn = nrf_wifi_set_iface_event_handler; +#endif /* CONFIG_NRF70_SYSTEM_MODE */ +#ifdef CONFIG_NRF70_STA_MODE + callbk_fns.twt_config_callbk_fn = nrf_wifi_event_proc_twt_setup_zep; + callbk_fns.twt_teardown_callbk_fn = nrf_wifi_event_proc_twt_teardown_zep; + callbk_fns.twt_sleep_callbk_fn = nrf_wifi_event_proc_twt_sleep_zep; + callbk_fns.event_get_reg = nrf_wifi_event_get_reg_zep; + callbk_fns.event_get_ps_info = nrf_wifi_event_proc_get_power_save_info; + callbk_fns.cookie_rsp_callbk_fn = nrf_wifi_event_proc_cookie_rsp; + callbk_fns.process_rssi_from_rx = nrf_wifi_process_rssi_from_rx; + callbk_fns.scan_res_callbk_fn = nrf_wifi_wpa_supp_event_proc_scan_res; + callbk_fns.auth_resp_callbk_fn = nrf_wifi_wpa_supp_event_proc_auth_resp; + callbk_fns.assoc_resp_callbk_fn = nrf_wifi_wpa_supp_event_proc_assoc_resp; + callbk_fns.deauth_callbk_fn = nrf_wifi_wpa_supp_event_proc_deauth; + callbk_fns.disassoc_callbk_fn = nrf_wifi_wpa_supp_event_proc_disassoc; + callbk_fns.get_station_callbk_fn = nrf_wifi_wpa_supp_event_proc_get_sta; + callbk_fns.get_interface_callbk_fn = nrf_wifi_wpa_supp_event_proc_get_if; + callbk_fns.mgmt_tx_status = nrf_wifi_wpa_supp_event_mgmt_tx_status; + callbk_fns.unprot_mlme_mgmt_rx_callbk_fn = nrf_wifi_wpa_supp_event_proc_unprot_mgmt; + callbk_fns.event_get_wiphy = nrf_wifi_wpa_supp_event_get_wiphy; + callbk_fns.mgmt_rx_callbk_fn = nrf_wifi_wpa_supp_event_mgmt_rx_callbk_fn; + callbk_fns.get_conn_info_callbk_fn = nrf_wifi_supp_event_proc_get_conn_info; +#endif /* CONFIG_NRF70_STA_MODE */ + + rpu_drv_priv_zep.fmac_priv = nrf_wifi_fmac_init(&data_config, + rx_buf_pools, + &callbk_fns); +#else /* !CONFIG_NRF70_RADIO_TEST */ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + + rpu_drv_priv_zep.fmac_priv = nrf_wifi_fmac_init_rt(); +#endif /* CONFIG_NRF70_RADIO_TEST */ + + if (rpu_drv_priv_zep.fmac_priv == NULL) { + LOG_ERR("%s: nrf_wifi_fmac_init failed", + __func__); + goto err; + } + +#ifdef CONFIG_NRF70_DATA_TX + struct nrf_wifi_fmac_priv_def *def_priv = NULL; + + def_priv = wifi_fmac_priv(rpu_drv_priv_zep.fmac_priv); + def_priv->max_ampdu_len_per_token = + (RPU_PKTRAM_SIZE - (CONFIG_NRF70_RX_NUM_BUFS * CONFIG_NRF70_RX_MAX_DATA_SIZE)) / + CONFIG_NRF70_MAX_TX_TOKENS; + /* Align to 4-byte */ + def_priv->max_ampdu_len_per_token &= ~0x3; + + /* Alignment overhead for size based coalesce */ + def_priv->avail_ampdu_len_per_token = + def_priv->max_ampdu_len_per_token - + (MAX_PKT_RAM_TX_ALIGN_OVERHEAD * max_tx_aggregation); +#endif /* CONFIG_NRF70_DATA_TX */ + +#ifdef CONFIG_NRF70_RADIO_TEST + status = nrf_wifi_fmac_dev_add_zep(&rpu_drv_priv_zep); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_dev_add_zep failed", __func__); + goto fmac_deinit; + } +#else + k_work_init_delayable(&vif_ctx_zep->scan_timeout_work, + nrf_wifi_scan_timeout_work); +#endif /* CONFIG_NRF70_RADIO_TEST */ + + return 0; +#ifdef CONFIG_NRF70_RADIO_TEST +fmac_deinit: + nrf_wifi_fmac_deinit_rt(rpu_drv_priv_zep.fmac_priv); +#endif /* CONFIG_NRF70_RADIO_TEST */ +err: + return -1; +} + +#ifndef CONFIG_NRF70_RADIO_TEST +#ifdef CONFIG_NET_L2_WIFI_MGMT +static struct wifi_mgmt_ops nrf_wifi_mgmt_ops = { + .scan = nrf_wifi_disp_scan_zep, +#ifdef CONFIG_NET_STATISTICS_WIFI + .get_stats = nrf_wifi_stats_get, +#endif /* CONFIG_NET_STATISTICS_WIFI */ +#ifdef CONFIG_NRF70_STA_MODE + .set_power_save = nrf_wifi_set_power_save, + .set_twt = nrf_wifi_set_twt, + .reg_domain = nrf_wifi_reg_domain, + .get_power_save_config = nrf_wifi_get_power_save_config, + .set_rts_threshold = nrf_wifi_set_rts_threshold, +#endif /* CONFIG_NRF70_STA_MODE */ +#ifdef CONFIG_NRF70_SYSTEM_WITH_RAW_MODES + .mode = nrf_wifi_mode, +#endif +#if defined(CONFIG_NRF70_RAW_DATA_TX) || defined(CONFIG_NRF70_RAW_DATA_RX) + .channel = nrf_wifi_channel, +#endif /* CONFIG_NRF70_RAW_DATA_TX || CONFIG_NRF70_RAW_DATA_RX */ +#if defined(CONFIG_NRF70_RAW_DATA_RX) || defined(CONFIG_NRF70_PROMISC_DATA_RX) + .filter = nrf_wifi_filter, +#endif /* CONFIG_NRF70_RAW_DATA_RX || CONFIG_NRF70_PROMISC_DATA_RX */ +}; +#endif /* CONFIG_NET_L2_WIFI_MGMT */ + + + +#ifdef CONFIG_NRF70_STA_MODE +static struct zep_wpa_supp_dev_ops wpa_supp_ops = { + .init = nrf_wifi_wpa_supp_dev_init, + .deinit = nrf_wifi_wpa_supp_dev_deinit, + .scan2 = nrf_wifi_wpa_supp_scan2, + .scan_abort = nrf_wifi_wpa_supp_scan_abort, + .get_scan_results2 = nrf_wifi_wpa_supp_scan_results_get, + .deauthenticate = nrf_wifi_wpa_supp_deauthenticate, + .authenticate = nrf_wifi_wpa_supp_authenticate, + .associate = nrf_wifi_wpa_supp_associate, + .set_supp_port = nrf_wifi_wpa_set_supp_port, + .set_key = nrf_wifi_wpa_supp_set_key, + .signal_poll = nrf_wifi_wpa_supp_signal_poll, + .send_mlme = nrf_wifi_nl80211_send_mlme, + .get_wiphy = nrf_wifi_supp_get_wiphy, + .register_frame = nrf_wifi_supp_register_frame, + .get_capa = nrf_wifi_supp_get_capa, + .get_conn_info = nrf_wifi_supp_get_conn_info, +#ifdef CONFIG_NRF70_AP_MODE + .init_ap = nrf_wifi_wpa_supp_init_ap, + .start_ap = nrf_wifi_wpa_supp_start_ap, + .change_beacon = nrf_wifi_wpa_supp_change_beacon, + .stop_ap = nrf_wifi_wpa_supp_stop_ap, + .deinit_ap = nrf_wifi_wpa_supp_deinit_ap, + .sta_add = nrf_wifi_wpa_supp_sta_add, + .sta_remove = nrf_wifi_wpa_supp_sta_remove, + .register_mgmt_frame = nrf_wifi_supp_register_mgmt_frame, + .sta_set_flags = nrf_wifi_wpa_supp_sta_set_flags, + .get_inact_sec = nrf_wifi_wpa_supp_sta_get_inact_sec, +#endif /* CONFIG_NRF70_AP_MODE */ +}; +#endif /* CONFIG_NRF70_STA_MODE */ +#endif /* !CONFIG_NRF70_RADIO_TEST */ + + +#ifdef CONFIG_NET_L2_ETHERNET +static const struct net_wifi_mgmt_offload wifi_offload_ops = { + .wifi_iface.iface_api.init = nrf_wifi_if_init_zep, + .wifi_iface.start = nrf_wifi_if_start_zep, + .wifi_iface.stop = nrf_wifi_if_stop_zep, + .wifi_iface.set_config = nrf_wifi_if_set_config_zep, + .wifi_iface.get_config = nrf_wifi_if_get_config_zep, + .wifi_iface.get_capabilities = nrf_wifi_if_caps_get, + .wifi_iface.send = nrf_wifi_if_send, +#ifdef CONFIG_NET_STATISTICS_ETHERNET + .wifi_iface.get_stats = nrf_wifi_eth_stats_get, +#endif /* CONFIG_NET_STATISTICS_ETHERNET */ +#ifdef CONFIG_NET_L2_WIFI_MGMT + .wifi_mgmt_api = &nrf_wifi_mgmt_ops, +#endif /* CONFIG_NET_L2_WIFI_MGMT */ +#ifdef CONFIG_NRF70_STA_MODE + .wifi_drv_ops = &wpa_supp_ops, +#endif /* CONFIG_NRF70_STA_MODE */ +}; +#endif /* CONFIG_NET_L2_ETHERNET */ + + + +#ifdef CONFIG_NET_L2_ETHERNET +ETH_NET_DEVICE_DT_INST_DEFINE(0, + nrf_wifi_drv_main_zep, /* init_fn */ + NULL, /* pm_action_cb */ + &rpu_drv_priv_zep.rpu_ctx_zep.vif_ctx_zep[0], /* data */ +#ifdef CONFIG_NRF70_STA_MODE + &wpa_supp_ops, /* cfg */ +#else /* CONFIG_NRF70_STA_MODE */ + NULL, /* cfg */ +#endif /* !CONFIG_NRF70_STA_MODE */ + CONFIG_WIFI_INIT_PRIORITY, /* prio */ + &wifi_offload_ops, /* api */ + CONFIG_NRF_WIFI_IFACE_MTU); /*mtu */ +#else +DEVICE_DT_INST_DEFINE(0, + nrf_wifi_drv_main_zep, /* init_fn */ + NULL, /* pm_action_cb */ +#ifndef CONFIG_NRF70_RADIO_TEST + &rpu_drv_priv_zep, /* data */ +#else /* !CONFIG_NRF70_RADIO_TEST */ + NULL, +#endif /* CONFIG_NRF70_RADIO_TEST */ + NULL, /* cfg */ + POST_KERNEL, + CONFIG_WIFI_INIT_PRIORITY, /* prio */ + NULL); /* api */ +#endif /* CONFIG_NRF70_STA_MODE */ + +#ifdef CONFIG_NET_CONNECTION_MANAGER_CONNECTIVITY_WIFI_MGMT +CONNECTIVITY_WIFI_MGMT_BIND(Z_DEVICE_DT_DEV_ID(DT_DRV_INST(0)), L2_CONN_WLAN0); +#endif /* CONFIG_NET_CONNECTION_MANAGER_CONNECTIVITY_WIFI_MGMT */ diff --git a/drivers/wifi/nrfwifi/src/fw_load.c b/drivers/wifi/nrfwifi/src/fw_load.c new file mode 100644 index 000000000000..14087a7d35ca --- /dev/null +++ b/drivers/wifi/nrfwifi/src/fw_load.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief File containing FW load functions for Zephyr. + */ +#include +#include + +#include +#include + +#include +LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL); + +#include + +#ifdef CONFIG_NRF_WIFI_PATCHES_BUILTIN +/* INCBIN macro Taken from https://gist.github.com/mmozeiko/ed9655cf50341553d282 */ +#define STR2(x) #x +#define STR(x) STR2(x) + +#ifdef __APPLE__ +#define USTR(x) "_" STR(x) +#else +#define USTR(x) STR(x) +#endif + +#ifdef _WIN32 +#define INCBIN_SECTION ".rdata, \"dr\"" +#elif defined __APPLE__ +#define INCBIN_SECTION "__TEXT,__const" +#else +#define INCBIN_SECTION ".rodata.*" +#endif + +/* this aligns start address to 16 and terminates byte array with explicit 0 + * which is not really needed, feel free to change it to whatever you want/need + */ +#define INCBIN(prefix, name, file) \ + __asm__(".section " INCBIN_SECTION "\n" \ + ".global " USTR(prefix) "_" STR(name) "_start\n" \ + ".balign 16\n" \ + USTR(prefix) "_" STR(name) "_start:\n" \ + ".incbin \"" file "\"\n" \ + \ + ".global " STR(prefix) "_" STR(name) "_end\n" \ + ".balign 1\n" \ + USTR(prefix) "_" STR(name) "_end:\n" \ + ".byte 0\n" \ + ); \ + extern __aligned(16) const char prefix ## _ ## name ## _start[]; \ + extern const char prefix ## _ ## name ## _end[]; + +INCBIN(_bin, nrf70_fw, STR(CONFIG_NRF_WIFI_FW_BIN)); +#endif /* CONFIG_NRF_WIFI_PATCHES_BUILTIN */ + +enum nrf_wifi_status nrf_wifi_fw_load(void *rpu_ctx) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_fmac_fw_info fw_info = { 0 }; + uint8_t *fw_start; + uint8_t *fw_end; + + fw_start = (uint8_t *)_bin_nrf70_fw_start; + fw_end = (uint8_t *)_bin_nrf70_fw_end; + + status = nrf_wifi_fmac_fw_parse(rpu_ctx, fw_start, fw_end - fw_start, + &fw_info); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_fw_parse failed", __func__); + return status; + } + /* Load the FW patches to the RPU */ + status = nrf_wifi_fmac_fw_load(rpu_ctx, &fw_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_fw_load failed", __func__); + } + + return status; +} diff --git a/drivers/wifi/nrfwifi/src/net_if.c b/drivers/wifi/nrfwifi/src/net_if.c new file mode 100644 index 000000000000..55d490852411 --- /dev/null +++ b/drivers/wifi/nrfwifi/src/net_if.c @@ -0,0 +1,1145 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief File containing netowrk stack interface specific definitions for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#include + +#ifdef CONFIG_WIFI_RANDOM_MAC_ADDRESS +#include +#endif + +#include +LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL); + +#include "net_private.h" + +#include "util.h" +#include "fmac_api.h" +#include "fmac_util.h" +#include "shim.h" +#include "fmac_main.h" +#include "wpa_supp_if.h" +#include "net_if.h" + +extern char *net_sprint_ll_addr_buf(const uint8_t *ll, uint8_t ll_len, + char *buf, int buflen); + +#ifdef CONFIG_NRF70_STA_MODE +static struct net_if_mcast_monitor mcast_monitor; +#endif /* CONFIG_NRF70_STA_MODE */ + +void nrf_wifi_set_iface_event_handler(void *os_vif_ctx, + struct nrf_wifi_umac_event_set_interface *event, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + if (!os_vif_ctx) { + LOG_ERR("%s: Invalid parameters", + __func__); + goto out; + } + + if (!event) { + LOG_ERR("%s: event is NULL", + __func__); + goto out; + } + + (void)event_len; + + vif_ctx_zep = os_vif_ctx; + + vif_ctx_zep->set_if_event_received = true; + vif_ctx_zep->set_if_status = event->return_value; + +out: + return; +} + +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY +static void nrf_wifi_rpu_recovery_work_handler(struct k_work *work) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = CONTAINER_OF(work, + struct nrf_wifi_vif_ctx_zep, + nrf_wifi_rpu_recovery_work); + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return; + } + + if (!vif_ctx_zep->zep_net_if_ctx) { + LOG_ERR("%s: zep_net_if_ctx is NULL", __func__); + return; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return; + } + + k_mutex_lock(&rpu_ctx_zep->rpu_lock, K_FOREVER); + LOG_DBG("%s: Bringing the interface down", __func__); + /* This indirectly does a cold-boot of RPU */ + net_if_down(vif_ctx_zep->zep_net_if_ctx); + k_msleep(CONFIG_NRF_WIFI_RPU_RECOVERY_PROPAGATION_DELAY_MS); + LOG_DBG("%s: Bringing the interface up", __func__); + net_if_up(vif_ctx_zep->zep_net_if_ctx); + k_mutex_unlock(&rpu_ctx_zep->rpu_lock); +} + +void nrf_wifi_rpu_recovery_cb(void *vif_ctx_handle, + void *event_data, + unsigned int event_len) +{ + struct nrf_wifi_fmac_vif_ctx *vif_ctx = vif_ctx_handle; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + if (!vif_ctx) { + LOG_ERR("%s: vif_ctx is NULL", + __func__); + goto out; + } + + fmac_dev_ctx = vif_ctx->fmac_dev_ctx; + def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); + if (!def_dev_ctx) { + LOG_ERR("%s: def_dev_ctx is NULL", + __func__); + goto out; + } + + vif_ctx_zep = vif_ctx->os_vif_ctx; + (void)event_data; + + k_work_submit(&vif_ctx_zep->nrf_wifi_rpu_recovery_work); +out: + return; +} +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ + +#ifdef CONFIG_NRF70_DATA_TX +static void nrf_wifi_net_iface_work_handler(struct k_work *work) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = CONTAINER_OF(work, + struct nrf_wifi_vif_ctx_zep, + nrf_wifi_net_iface_work); + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return; + } + + if (!vif_ctx_zep->zep_net_if_ctx) { + LOG_ERR("%s: zep_net_if_ctx is NULL", __func__); + return; + } + + if (vif_ctx_zep->if_carr_state == NRF_WIFI_FMAC_IF_CARR_STATE_ON) { + net_if_dormant_off(vif_ctx_zep->zep_net_if_ctx); + } else if (vif_ctx_zep->if_carr_state == NRF_WIFI_FMAC_IF_CARR_STATE_OFF) { + net_if_dormant_on(vif_ctx_zep->zep_net_if_ctx); + } +} + +#if defined(CONFIG_NRF70_RAW_DATA_RX) || defined(CONFIG_NRF70_PROMISC_DATA_RX) +void nrf_wifi_if_sniffer_rx_frm(void *os_vif_ctx, void *frm, + struct raw_rx_pkt_header *raw_rx_hdr, + bool pkt_free) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = os_vif_ctx; + struct net_if *iface = vif_ctx_zep->zep_net_if_ctx; + struct net_pkt *pkt; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + int ret; + + def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); + + pkt = net_raw_pkt_from_nbuf(iface, frm, sizeof(struct raw_rx_pkt_header), + raw_rx_hdr, + pkt_free); + if (!pkt) { + LOG_DBG("Failed to allocate net_pkt"); + return; + } + + net_capture_pkt(iface, pkt); + + ret = net_recv_data(iface, pkt); + if (ret < 0) { + LOG_DBG("RCV Packet dropped by NET stack: %d", ret); + net_pkt_unref(pkt); + } +} +#endif /* CONFIG_NRF70_RAW_DATA_RX || CONFIG_NRF70_PROMISC_DATA_RX */ + +void nrf_wifi_if_rx_frm(void *os_vif_ctx, void *frm) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = os_vif_ctx; + struct net_if *iface = vif_ctx_zep->zep_net_if_ctx; + struct net_pkt *pkt; + int status; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + struct rpu_host_stats *host_stats = NULL; + + def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); + host_stats = &def_dev_ctx->host_stats; + host_stats->total_rx_pkts++; + + pkt = net_pkt_from_nbuf(iface, frm); + if (!pkt) { + LOG_DBG("Failed to allocate net_pkt"); + host_stats->total_rx_drop_pkts++; + return; + } + + status = net_recv_data(iface, pkt); + + if (status < 0) { + LOG_DBG("RCV Packet dropped by NET stack: %d", status); + host_stats->total_rx_drop_pkts++; + net_pkt_unref(pkt); + } +} + +enum nrf_wifi_status nrf_wifi_if_carr_state_chg(void *os_vif_ctx, + enum nrf_wifi_fmac_if_carr_state carr_state) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + if (!os_vif_ctx) { + LOG_ERR("%s: Invalid parameters", + __func__); + goto out; + } + + vif_ctx_zep = os_vif_ctx; + + vif_ctx_zep->if_carr_state = carr_state; + + LOG_DBG("%s: Carrier state: %d", __func__, carr_state); + + k_work_submit(&vif_ctx_zep->nrf_wifi_net_iface_work); + + status = NRF_WIFI_STATUS_SUCCESS; + +out: + return status; +} + +static bool is_eapol(struct net_pkt *pkt) +{ + struct net_eth_hdr *hdr; + uint16_t ethertype; + + hdr = NET_ETH_HDR(pkt); + ethertype = ntohs(hdr->type); + + return ethertype == NET_ETH_PTYPE_EAPOL; +} +#endif /* CONFIG_NRF70_DATA_TX */ + +enum ethernet_hw_caps nrf_wifi_if_caps_get(const struct device *dev) +{ + enum ethernet_hw_caps caps = (ETHERNET_LINK_10BASE_T | + ETHERNET_LINK_100BASE_T | ETHERNET_LINK_1000BASE_T); + +#ifdef CONFIG_NRF70_TCP_IP_CHECKSUM_OFFLOAD + caps |= ETHERNET_HW_TX_CHKSUM_OFFLOAD | + ETHERNET_HW_RX_CHKSUM_OFFLOAD; +#endif /* CONFIG_NRF70_TCP_IP_CHECKSUM_OFFLOAD */ + +#ifdef CONFIG_NRF70_RAW_DATA_TX + caps |= ETHERNET_TXINJECTION_MODE; +#endif +#ifdef CONFIG_NRF70_PROMISC_DATA_RX + caps |= ETHERNET_PROMISC_MODE; +#endif + return caps; +} + +int nrf_wifi_if_send(const struct device *dev, + struct net_pkt *pkt) +{ + int ret = -1; +#ifdef CONFIG_NRF70_DATA_TX + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + + if (!dev || !pkt) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + goto out; + } + + vif_ctx_zep = dev->data; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + goto out; + } + + ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (ret != 0) { + LOG_ERR("%s: Failed to lock vif_lock", __func__); + goto out; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) { + goto unlock; + } + +#ifdef CONFIG_NRF70_RAW_DATA_TX + if ((*(unsigned int *)pkt->frags->data) == NRF_WIFI_MAGIC_NUM_RAWTX) { + if (vif_ctx_zep->if_carr_state != NRF_WIFI_FMAC_IF_CARR_STATE_ON) { + goto unlock; + } + + ret = nrf_wifi_fmac_start_rawpkt_xmit(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + net_pkt_to_nbuf(pkt)); + } else { +#endif /* CONFIG_NRF70_RAW_DATA_TX */ + if ((vif_ctx_zep->if_carr_state != NRF_WIFI_FMAC_IF_CARR_STATE_ON) || + (!vif_ctx_zep->authorized && !is_eapol(pkt))) { + goto unlock; + } + + ret = nrf_wifi_fmac_start_xmit(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + net_pkt_to_nbuf(pkt)); +#ifdef CONFIG_NRF70_RAW_DATA_TX + } +#endif /* CONFIG_NRF70_RAW_DATA_TX */ +unlock: + k_mutex_unlock(&vif_ctx_zep->vif_lock); +#else + ARG_UNUSED(dev); + ARG_UNUSED(pkt); + goto out; +#endif /* CONFIG_NRF70_DATA_TX */ + +out: + return ret; +} + +#ifdef CONFIG_NRF70_STA_MODE +static void ip_maddr_event_handler(struct net_if *iface, + const struct net_addr *addr, bool is_joined) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct net_eth_addr mac_addr; + struct nrf_wifi_umac_mcast_cfg *mcast_info = NULL; + enum nrf_wifi_status status; + uint8_t mac_string_buf[sizeof("xx:xx:xx:xx:xx:xx")]; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + int ret; + + vif_ctx_zep = nrf_wifi_get_vif_ctx(iface); + if (!vif_ctx_zep) { + return; + } + + ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (ret != 0) { + LOG_ERR("%s: Failed to lock vif_lock", __func__); + goto out; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: rpu_ctx_zep or rpu_ctx is NULL", + __func__); + goto unlock; + } + + mcast_info = k_calloc(sizeof(*mcast_info), sizeof(char)); + + if (!mcast_info) { + LOG_ERR("%s: Unable to allocate memory of size %d " + "for mcast_info", __func__, sizeof(*mcast_info)); + return; + } + + switch (addr->family) { + case AF_INET: + net_eth_ipv4_mcast_to_mac_addr(&addr->in_addr, &mac_addr); + break; + case AF_INET6: + net_eth_ipv6_mcast_to_mac_addr(&addr->in6_addr, &mac_addr); + break; + default: + LOG_ERR("%s: Invalid address family %d", __func__, addr->family); + break; + } + + if (is_joined) { + mcast_info->type = MCAST_ADDR_ADD; + } else { + mcast_info->type = MCAST_ADDR_DEL; + } + + memcpy(((char *)(mcast_info->mac_addr)), + &mac_addr, + NRF_WIFI_ETH_ADDR_LEN); + + status = nrf_wifi_fmac_set_mcast_addr(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + mcast_info); + if (status == NRF_WIFI_STATUS_FAIL) { + LOG_ERR("%s: nrf_wifi_fmac_set_multicast failed for" + " mac addr=%s", + __func__, + net_sprint_ll_addr_buf(mac_addr.addr, + WIFI_MAC_ADDR_LEN, mac_string_buf, + sizeof(mac_string_buf))); + } +unlock: + k_mutex_unlock(&vif_ctx_zep->vif_lock); +out: + k_free(mcast_info); +} +#endif /* CONFIG_NRF70_STA_MODE */ + +#ifdef CONFIG_WIFI_FIXED_MAC_ADDRESS_ENABLED +BUILD_ASSERT(sizeof(CONFIG_WIFI_FIXED_MAC_ADDRESS) - 1 == ((WIFI_MAC_ADDR_LEN * 2) + 5), + "Invalid fixed MAC address length"); +#endif + +enum nrf_wifi_status nrf_wifi_get_mac_addr(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + int ret; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", + __func__); + goto out; + } + + ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (ret != 0) { + LOG_ERR("%s: Failed to lock vif_lock", __func__); + goto out; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: rpu_ctx_zep or rpu_ctx is NULL", + __func__); + goto unlock; + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + +#ifdef CONFIG_WIFI_FIXED_MAC_ADDRESS_ENABLED + char fixed_mac_addr[WIFI_MAC_ADDR_LEN]; + + ret = net_bytes_from_str(fixed_mac_addr, + WIFI_MAC_ADDR_LEN, + CONFIG_WIFI_FIXED_MAC_ADDRESS); + if (ret < 0) { + LOG_ERR("%s: Failed to parse MAC address: %s", + __func__, + CONFIG_WIFI_FIXED_MAC_ADDRESS); + goto unlock; + } + + memcpy(vif_ctx_zep->mac_addr.addr, + fixed_mac_addr, + WIFI_MAC_ADDR_LEN); +#elif CONFIG_WIFI_RANDOM_MAC_ADDRESS + char random_mac_addr[WIFI_MAC_ADDR_LEN]; + + sys_rand_get(random_mac_addr, WIFI_MAC_ADDR_LEN); + random_mac_addr[0] = (random_mac_addr[0] & UNICAST_MASK) | LOCAL_BIT; + + memcpy(vif_ctx_zep->mac_addr.addr, + random_mac_addr, + WIFI_MAC_ADDR_LEN); +#elif CONFIG_WIFI_OTP_MAC_ADDRESS + status = nrf_wifi_fmac_otp_mac_addr_get(fmac_dev_ctx, + vif_ctx_zep->vif_idx, + vif_ctx_zep->mac_addr.addr); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Fetching of MAC address from OTP failed", + __func__); + goto unlock; + } +#endif + + if (!nrf_wifi_utils_is_mac_addr_valid(fmac_dev_ctx->fpriv->opriv, + vif_ctx_zep->mac_addr.addr)) { + LOG_ERR("%s: Invalid MAC address: %s", + __func__, + net_sprint_ll_addr(vif_ctx_zep->mac_addr.addr, + WIFI_MAC_ADDR_LEN)); + status = NRF_WIFI_STATUS_FAIL; + memset(vif_ctx_zep->mac_addr.addr, + 0, + WIFI_MAC_ADDR_LEN); + goto unlock; + } + status = NRF_WIFI_STATUS_SUCCESS; +unlock: + k_mutex_unlock(&vif_ctx_zep->vif_lock); +out: + return status; +} + +void nrf_wifi_if_init_zep(struct net_if *iface) +{ + const struct device *dev = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct ethernet_context *eth_ctx = net_if_l2_data(iface); + + if (!iface) { + LOG_ERR("%s: Invalid parameters", + __func__); + return; + } + + dev = net_if_get_device(iface); + + if (!dev) { + LOG_ERR("%s: Invalid dev", + __func__); + return; + } + + vif_ctx_zep = dev->data; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", + __func__); + return; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", + __func__); + return; + } + + vif_ctx_zep->zep_net_if_ctx = iface; + vif_ctx_zep->zep_dev_ctx = dev; + + eth_ctx->eth_if_type = L2_ETH_IF_TYPE_WIFI; + ethernet_init(iface); + net_eth_carrier_on(iface); + net_if_dormant_on(iface); + +#ifdef CONFIG_NRF70_STA_MODE + net_if_mcast_mon_register(&mcast_monitor, iface, ip_maddr_event_handler); +#endif /* CONFIG_NRF70_STA_MODE */ +#ifdef CONFIG_NRF70_DATA_TX + k_work_init(&vif_ctx_zep->nrf_wifi_net_iface_work, + nrf_wifi_net_iface_work_handler); +#endif /* CONFIG_NRF70_DATA_TX */ + +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY + k_work_init(&vif_ctx_zep->nrf_wifi_rpu_recovery_work, + nrf_wifi_rpu_recovery_work_handler); +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ + +#if !defined(CONFIG_NRF_WIFI_IF_AUTO_START) + net_if_flag_set(iface, NET_IF_NO_AUTO_START); +#endif /* CONFIG_NRF_WIFI_IF_AUTO_START */ + net_if_flag_set(iface, NET_IF_NO_TX_LOCK); +} + +/* Board-specific Wi-Fi startup code to run before the Wi-Fi device is started */ +__weak int nrf_wifi_if_zep_start_board(const struct device *dev) +{ + ARG_UNUSED(dev); + return 0; +} + +/* Board-specific Wi-Fi shutdown code to run after the Wi-Fi device is stopped */ +__weak int nrf_wifi_if_zep_stop_board(const struct device *dev) +{ + ARG_UNUSED(dev); + return 0; +} + +int nrf_wifi_if_start_zep(const struct device *dev) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + struct nrf_wifi_umac_chg_vif_state_info vif_info; + struct nrf_wifi_umac_add_vif_info add_vif_info; + char *mac_addr = NULL; + unsigned int mac_addr_len = 0; + int ret = -1; + bool fmac_dev_added = false; + + if (!dev) { + LOG_ERR("%s: Invalid parameters", + __func__); + goto out; + } + + if (!device_is_ready(dev)) { + LOG_ERR("%s: Device %s is not ready", + __func__, dev->name); + goto out; + } + + ret = nrf_wifi_if_zep_start_board(dev); + if (ret) { + LOG_ERR("nrf_wifi_if_zep_start_board failed with error: %d", + ret); + goto out; + } + + vif_ctx_zep = dev->data; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", + __func__); + goto out; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", + __func__); + goto out; + } + + ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (ret != 0) { + LOG_ERR("%s: Failed to lock vif_lock", __func__); + goto out; + } + + if (!rpu_ctx_zep->rpu_ctx) { + status = nrf_wifi_fmac_dev_add_zep(&rpu_drv_priv_zep); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_dev_add_zep failed", + __func__); + goto out; + } + fmac_dev_added = true; + LOG_DBG("%s: FMAC device added", __func__); + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + + memset(&add_vif_info, + 0, + sizeof(add_vif_info)); + + /* TODO: This should be configurable */ + add_vif_info.iftype = NRF_WIFI_IFTYPE_STATION; + + memcpy(add_vif_info.ifacename, + dev->name, + strlen(dev->name)); + + vif_ctx_zep->vif_idx = nrf_wifi_fmac_add_vif(fmac_dev_ctx, + vif_ctx_zep, + &add_vif_info); + if (vif_ctx_zep->vif_idx >= MAX_NUM_VIFS) { + LOG_ERR("%s: FMAC returned invalid interface index", + __func__); + goto dev_rem; + } + + k_mutex_init(&vif_ctx_zep->vif_lock); + rpu_ctx_zep->vif_ctx_zep[vif_ctx_zep->vif_idx].if_type = + add_vif_info.iftype; + + /* Check if user has provided a valid MAC address, if not + * fetch it from OTP. + */ + mac_addr = net_if_get_link_addr(vif_ctx_zep->zep_net_if_ctx)->addr; + mac_addr_len = net_if_get_link_addr(vif_ctx_zep->zep_net_if_ctx)->len; + + if (!nrf_wifi_utils_is_mac_addr_valid(fmac_dev_ctx->fpriv->opriv, + mac_addr)) { + status = nrf_wifi_get_mac_addr(vif_ctx_zep); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Failed to get MAC address", + __func__); + goto del_vif; + } + net_if_set_link_addr(vif_ctx_zep->zep_net_if_ctx, + vif_ctx_zep->mac_addr.addr, + WIFI_MAC_ADDR_LEN, + NET_LINK_ETHERNET); + mac_addr = vif_ctx_zep->mac_addr.addr; + mac_addr_len = WIFI_MAC_ADDR_LEN; + } + + status = nrf_wifi_fmac_set_vif_macaddr(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + mac_addr); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: MAC address change failed", + __func__); + goto del_vif; + } + + memset(&vif_info, + 0, + sizeof(vif_info)); + + vif_info.state = NRF_WIFI_FMAC_IF_OP_STATE_UP; + vif_info.if_index = vif_ctx_zep->vif_idx; + + memcpy(vif_ctx_zep->ifname, + dev->name, + strlen(dev->name)); + + status = nrf_wifi_fmac_chg_vif_state(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + &vif_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_chg_vif_state failed", + __func__); + goto del_vif; + } + +#ifdef CONFIG_NRF70_STA_MODE + nrf_wifi_wpa_supp_event_mac_chgd(vif_ctx_zep); + +#ifdef CONFIG_NRF_WIFI_LOW_POWER + status = nrf_wifi_fmac_set_power_save(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + NRF_WIFI_PS_ENABLED); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_set_power_save failed", + __func__); + goto dev_rem; + } +#endif /* CONFIG_NRF_WIFI_LOW_POWER */ +#endif /* CONFIG_NRF70_STA_MODE */ + + vif_ctx_zep->if_op_state = NRF_WIFI_FMAC_IF_OP_STATE_UP; + + ret = 0; + + goto out; +del_vif: + status = nrf_wifi_fmac_del_vif(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_del_vif failed", + __func__); + } +dev_rem: + /* Free only if we added above i.e., for 1st VIF */ + if (fmac_dev_added) { + nrf_wifi_fmac_dev_rem_zep(&rpu_drv_priv_zep); + } +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + + +int nrf_wifi_if_stop_zep(const struct device *dev) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_umac_chg_vif_state_info vif_info; + int ret = -1; + + if (!dev) { + LOG_ERR("%s: Invalid parameters", + __func__); + goto out; + } + + vif_ctx_zep = dev->data; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", + __func__); + goto out; + } + + ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (ret != 0) { + LOG_ERR("%s: Failed to lock vif_lock", __func__); + goto out; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", + __func__); + goto out; + } + +#ifdef CONFIG_NRF70_STA_MODE +#ifdef CONFIG_NRF_WIFI_LOW_POWER + status = nrf_wifi_fmac_set_power_save(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + NRF_WIFI_PS_DISABLED); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_set_power_save failed", + __func__); + goto unlock; + } +#endif /* CONFIG_NRF_WIFI_LOW_POWER */ +#endif /* CONFIG_NRF70_STA_MODE */ + + memset(&vif_info, + 0, + sizeof(vif_info)); + + vif_info.state = NRF_WIFI_FMAC_IF_OP_STATE_DOWN; + vif_info.if_index = vif_ctx_zep->vif_idx; + + status = nrf_wifi_fmac_chg_vif_state(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + &vif_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_chg_vif_state failed", + __func__); + goto unlock; + } + + status = nrf_wifi_fmac_del_vif(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_del_vif failed", + __func__); + goto unlock; + } + + vif_ctx_zep->if_op_state = NRF_WIFI_FMAC_IF_OP_STATE_DOWN; + + if (nrf_wifi_fmac_get_num_vifs(rpu_ctx_zep->rpu_ctx) == 0) { + nrf_wifi_fmac_dev_rem_zep(&rpu_drv_priv_zep); + } + ret = 0; +unlock: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + + ret = nrf_wifi_if_zep_stop_board(dev); + if (ret) { + LOG_ERR("nrf_wifi_if_zep_stop_board failed with error: %d", + ret); + } +out: + return ret; +} + +int nrf_wifi_if_get_config_zep(const struct device *dev, + enum ethernet_config_type type, + struct ethernet_config *config) +{ + int ret = -1; +#ifdef CONFIG_NRF70_RAW_DATA_TX + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + + if (!dev || !config) { + LOG_ERR("%s: Invalid parameters", + __func__); + goto out; + } + + vif_ctx_zep = dev->data; + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", + __func__); + goto out; + } + + ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (ret != 0) { + LOG_ERR("%s: Failed to lock vif_lock", __func__); + goto out; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: rpu_ctx_zep or rpu_ctx is NULL", + __func__); + goto unlock; + } + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); + if (!def_dev_ctx) { + LOG_ERR("%s: def_dev_ctx is NULL", + __func__); + goto unlock; + } + + if (type == ETHERNET_CONFIG_TYPE_TXINJECTION_MODE) { + config->txinjection_mode = + def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->txinjection_mode; + } + ret = 0; +unlock: + k_mutex_unlock(&vif_ctx_zep->vif_lock); +out: +#endif + return ret; +} + +int nrf_wifi_if_set_config_zep(const struct device *dev, + enum ethernet_config_type type, + const struct ethernet_config *config) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + int ret = -1; + + if (!dev) { + LOG_ERR("%s: Invalid parameters", + __func__); + goto out; + } + + vif_ctx_zep = dev->data; + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", + __func__); + goto out; + } + + ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (ret != 0) { + LOG_ERR("%s: Failed to lock vif_lock", __func__); + goto out; + } + + /* Commands without FMAC interaction */ + if (type == ETHERNET_CONFIG_TYPE_MAC_ADDRESS) { + if (!net_eth_is_addr_valid((struct net_eth_addr *)&config->mac_address)) { + LOG_ERR("%s: Invalid MAC address", + __func__); + goto unlock; + } + memcpy(vif_ctx_zep->mac_addr.addr, + config->mac_address.addr, + sizeof(vif_ctx_zep->mac_addr.addr)); + + net_if_set_link_addr(vif_ctx_zep->zep_net_if_ctx, + vif_ctx_zep->mac_addr.addr, + sizeof(vif_ctx_zep->mac_addr.addr), + NET_LINK_ETHERNET); + ret = 0; + goto unlock; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: rpu_ctx_zep or rpu_ctx is NULL", + __func__); + goto unlock; + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); + if (!def_dev_ctx) { + LOG_ERR("%s: def_dev_ctx is NULL", + __func__); + goto unlock; + } + +#ifdef CONFIG_NRF70_RAW_DATA_TX + if (type == ETHERNET_CONFIG_TYPE_TXINJECTION_MODE) { + unsigned char mode; + + if (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->txinjection_mode == + config->txinjection_mode) { + LOG_INF("%s: Driver TX injection setting is same as configured setting", + __func__); + goto unlock; + } + /** + * Since UMAC wishes to use the same mode command as previously + * used for mode, `OR` the primary mode with TX-Injection mode and + * send it to the UMAC. That way UMAC can still maintain code + * as is + */ + if (config->txinjection_mode) { + mode = (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->mode) | + (NRF_WIFI_TX_INJECTION_MODE); + } else { + mode = (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->mode) ^ + (NRF_WIFI_TX_INJECTION_MODE); + } + + ret = nrf_wifi_fmac_set_mode(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, mode); + + if (ret != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Mode set operation failed", __func__); + goto unlock; + } + } +#endif +#ifdef CONFIG_NRF70_PROMISC_DATA_RX + else if (type == ETHERNET_CONFIG_TYPE_PROMISC_MODE) { + unsigned char mode; + + if (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->promisc_mode == + config->promisc_mode) { + LOG_ERR("%s: Driver promisc mode setting is same as configured setting", + __func__); + goto out; + } + + if (config->promisc_mode) { + mode = (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->mode) | + (NRF_WIFI_PROMISCUOUS_MODE); + } else { + mode = (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->mode) ^ + (NRF_WIFI_PROMISCUOUS_MODE); + } + + ret = nrf_wifi_fmac_set_mode(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, mode); + + if (ret != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: mode set operation failed", __func__); + goto out; + } + } +#endif + ret = 0; +unlock: + k_mutex_unlock(&vif_ctx_zep->vif_lock); +out: + return ret; +} + +#ifdef CONFIG_NET_STATISTICS_ETHERNET +struct net_stats_eth *nrf_wifi_eth_stats_get(const struct device *dev) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + if (!dev) { + LOG_ERR("%s Device not found", __func__); + goto out; + } + + vif_ctx_zep = dev->data; + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + goto out; + } + + return &vif_ctx_zep->eth_stats; +out: + return NULL; +} +#endif /* CONFIG_NET_STATISTICS_ETHERNET */ + +#ifdef CONFIG_NET_STATISTICS_WIFI +int nrf_wifi_stats_get(const struct device *dev, struct net_stats_wifi *zstats) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; +#ifdef CONFIG_NRF70_RAW_DATA_TX + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; +#endif /* CONFIG_NRF70_RAW_DATA_TX */ + struct rpu_op_stats stats; + int ret = -1; + + if (!dev) { + LOG_ERR("%s Device not found", __func__); + goto out; + } + + if (!zstats) { + LOG_ERR("%s Stats buffer not allocated", __func__); + goto out; + } + + vif_ctx_zep = dev->data; + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + goto out; + } + + ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (ret != 0) { + LOG_ERR("%s: Failed to lock vif_lock", __func__); + goto out; + } + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: rpu_ctx_zep or rpu_ctx is NULL", + __func__); + goto unlock; + } + + memset(&stats, 0, sizeof(struct rpu_op_stats)); + status = nrf_wifi_fmac_stats_get(rpu_ctx_zep->rpu_ctx, 0, &stats); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_stats_get failed", __func__); + goto unlock; + } + + zstats->pkts.tx = stats.host.total_tx_pkts; + zstats->pkts.rx = stats.host.total_rx_pkts; + zstats->errors.tx = stats.host.total_tx_drop_pkts; + zstats->errors.rx = stats.host.total_rx_drop_pkts + + stats.fw.umac.interface_data_stats.rx_checksum_error_count; + zstats->bytes.received = stats.fw.umac.interface_data_stats.rx_bytes; + zstats->bytes.sent = stats.fw.umac.interface_data_stats.tx_bytes; + zstats->sta_mgmt.beacons_rx = stats.fw.umac.interface_data_stats.rx_beacon_success_count; + zstats->sta_mgmt.beacons_miss = stats.fw.umac.interface_data_stats.rx_beacon_miss_count; + zstats->broadcast.rx = stats.fw.umac.interface_data_stats.rx_broadcast_pkt_count; + zstats->broadcast.tx = stats.fw.umac.interface_data_stats.tx_broadcast_pkt_count; + zstats->multicast.rx = stats.fw.umac.interface_data_stats.rx_multicast_pkt_count; + zstats->multicast.tx = stats.fw.umac.interface_data_stats.tx_multicast_pkt_count; + zstats->unicast.rx = stats.fw.umac.interface_data_stats.rx_unicast_pkt_count; + zstats->unicast.tx = stats.fw.umac.interface_data_stats.tx_unicast_pkt_count; + +#ifdef CONFIG_NRF70_RAW_DATA_TX + def_dev_ctx = wifi_dev_priv(rpu_ctx_zep->rpu_ctx); + zstats->errors.tx += def_dev_ctx->raw_pkt_stats.raw_pkt_send_failure; +#endif /* CONFIG_NRF70_RAW_DATA_TX */ + ret = 0; +unlock: + k_mutex_unlock(&vif_ctx_zep->vif_lock); +out: + return ret; +} +#endif /* CONFIG_NET_STATISTICS_WIFI */ diff --git a/drivers/wifi/nrfwifi/src/qspi/inc/ficr_prog.h b/drivers/wifi/nrfwifi/src/qspi/inc/ficr_prog.h new file mode 100644 index 000000000000..35c4bda1fa32 --- /dev/null +++ b/drivers/wifi/nrfwifi/src/qspi/inc/ficr_prog.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @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/drivers/wifi/nrfwifi/src/qspi/inc/qspi_if.h b/drivers/wifi/nrfwifi/src/qspi/inc/qspi_if.h new file mode 100644 index 000000000000..e66db6c48369 --- /dev/null +++ b/drivers/wifi/nrfwifi/src/qspi/inc/qspi_if.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing QSPI device interface specific declarations for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#ifndef __QSPI_IF_H__ +#define __QSPI_IF_H__ + +#include +#include +#ifdef CONFIG_NRF70_ON_QSPI +#include +#endif + +#define RPU_WAKEUP_NOW BIT(0) /* WAKEUP RPU - RW */ +#define RPU_AWAKE_BIT BIT(1) /* RPU AWAKE FROM SLEEP - RO */ +#define RPU_READY_BIT BIT(2) /* RPU IS READY - RO*/ + +struct qspi_config { +#ifdef CONFIG_NRF70_ON_QSPI + nrf_qspi_addrmode_t addrmode; + nrf_qspi_readoc_t readoc; + nrf_qspi_writeoc_t writeoc; + nrf_qspi_frequency_t sckfreq; +#endif + unsigned char RDC4IO; + bool easydma; + bool single_op; + bool quad_spi; + bool encryption; + bool CMD_CNONCE; + bool enc_enabled; + struct k_sem lock; + unsigned int addrmask; + unsigned char qspi_slave_latency; +#if defined(CONFIG_NRF70_ON_QSPI) && (NRF_QSPI_HAS_XIP_ENC || NRF_QSPI_HAS_DMA_ENC) + nrf_qspi_encryption_t p_cfg; +#endif /*CONFIG_NRF70_ON_QSPI && (NRF_QSPI_HAS_XIP_ENC || NRF_QSPI_HAS_DMA_ENC)*/ + int test_hlread; + char *test_name; + int test_start; + int test_end; + int test_iterations; + int test_timediff_read; + int test_timediff_write; + int test_status; + int test_iteration; +}; +struct qspi_dev { + int (*deinit)(void); + void *config; + int (*init)(struct qspi_config *config); + int (*write)(unsigned int addr, const void *data, int len); + int (*read)(unsigned int addr, void *data, int len); + int (*hl_read)(unsigned int addr, void *data, int len); + void (*hard_reset)(void); +}; + +int qspi_cmd_wakeup_rpu(const struct device *dev, uint8_t data); + +int qspi_init(struct qspi_config *config); + +int qspi_write(unsigned int addr, const void *data, int len); + +int qspi_read(unsigned int addr, void *data, int len); + +int qspi_hl_read(unsigned int addr, void *data, int len); + +int qspi_deinit(void); + +void gpio_free_irq(int pin, struct gpio_callback *button_cb_data); + +int gpio_request_irq(int pin, struct gpio_callback *button_cb_data, void (*irq_handler)()); + +struct qspi_config *qspi_defconfig(void); + +struct qspi_dev *qspi_dev(void); +struct qspi_config *qspi_get_config(void); + +int qspi_cmd_sleep_rpu(const struct device *dev); + +void hard_reset(void); +void get_sleep_stats(uint32_t addr, uint32_t *buff, uint32_t wrd_len); + +extern struct device qspi_perip; + +int qspi_validate_rpu_wake_writecmd(const struct device *dev); +int qspi_cmd_wakeup_rpu(const struct device *dev, uint8_t data); +int qspi_wait_while_rpu_awake(const struct device *dev); + +int qspi_RDSR1(const struct device *dev, uint8_t *rdsr1); +int qspi_RDSR2(const struct device *dev, uint8_t *rdsr2); +int qspi_WRSR2(const struct device *dev, const uint8_t wrsr2); + +#ifdef CONFIG_NRF_WIFI_LOW_POWER +int func_rpu_sleep(void); +int func_rpu_wake(void); +int func_rpu_sleep_status(void); +#endif /* CONFIG_NRF_WIFI_LOW_POWER */ + +#define QSPI_KEY_LEN_BYTES 16 + +/*! \brief Enable encryption + * + * \param key Pointer to the 128-bit key + * \return 0 on success, negative errno code on failure. + */ +int qspi_enable_encryption(uint8_t *key); + +#endif /* __QSPI_IF_H__ */ diff --git a/drivers/wifi/nrfwifi/src/qspi/inc/rpu_hw_if.h b/drivers/wifi/nrfwifi/src/qspi/inc/rpu_hw_if.h new file mode 100644 index 000000000000..2524b64ab505 --- /dev/null +++ b/drivers/wifi/nrfwifi/src/qspi/inc/rpu_hw_if.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing common functions for RPU hardware interaction + * using QSPI and SPI that can be invoked by shell or the driver. + */ + +#ifndef __RPU_HW_IF_H_ +#define __RPU_HW_IF_H_ + +#include +#include +#include + +enum { + SYSBUS = 0, + EXT_SYS_BUS, + PBUS, + PKTRAM, + GRAM, + LMAC_ROM, + LMAC_RET_RAM, + LMAC_SRC_RAM, + UMAC_ROM, + UMAC_RET_RAM, + UMAC_SRC_RAM, + NUM_MEM_BLOCKS +}; + +extern char blk_name[][15]; +extern uint32_t rpu_7002_memmap[][3]; + +int rpu_read(unsigned int addr, void *data, int len); +int rpu_write(unsigned int addr, const void *data, int len); + +int rpu_sleep(void); +int rpu_wakeup(void); +int rpu_sleep_status(void); +void rpu_get_sleep_stats(uint32_t addr, uint32_t *buff, uint32_t wrd_len); +int rpu_irq_config(struct gpio_callback *irq_callback_data, void (*irq_handler)()); +int rpu_irq_remove(struct gpio_callback *irq_callback_data); + +int rpu_wrsr2(uint8_t data); +int rpu_rdsr2(void); +int rpu_rdsr1(void); +int rpu_clks_on(void); + +int rpu_init(void); +int rpu_enable(void); +int rpu_disable(void); + +#ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH +int sr_ant_switch(unsigned int ant_switch); +int sr_gpio_remove(void); +int sr_gpio_config(void); +#endif /* CONFIG_NRF70_SR_COEX_RF_SWITCH */ +#endif /* __RPU_HW_IF_H_ */ diff --git a/drivers/wifi/nrfwifi/src/qspi/inc/spi_if.h b/drivers/wifi/nrfwifi/src/qspi/inc/spi_if.h new file mode 100644 index 000000000000..2f64bd2c262a --- /dev/null +++ b/drivers/wifi/nrfwifi/src/qspi/inc/spi_if.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing SPI device interface specific declarations for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +/* SPIM driver config */ + +int spim_init(struct qspi_config *config); + +int spim_deinit(void); + +int spim_write(unsigned int addr, const void *data, int len); + +int spim_read(unsigned int addr, void *data, int len); + +int spim_hl_read(unsigned int addr, void *data, int len); + +int spim_cmd_rpu_wakeup_fn(uint32_t data); + +int spim_wait_while_rpu_awake(void); + +int spi_validate_rpu_wake_writecmd(void); + +int spim_cmd_sleep_rpu_fn(void); + +int spim_RDSR1(const struct device *dev, uint8_t *rdsr1); + +int spim_RDSR2(const struct device *dev, uint8_t *rdsr2); + +int spim_WRSR2(const struct device *dev, const uint8_t wrsr2); diff --git a/drivers/wifi/nrfwifi/src/qspi/inc/spi_nor.h b/drivers/wifi/nrfwifi/src/qspi/inc/spi_nor.h new file mode 100644 index 000000000000..3d56f348d24c --- /dev/null +++ b/drivers/wifi/nrfwifi/src/qspi/inc/spi_nor.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing SPI device specific declarations for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#ifndef __SPI_NOR_H__ +#define __SPI_NOR_H__ + +#include + +#define SPI_NOR_MAX_ID_LEN 3 + +/* Status register bits */ +#define SPI_NOR_WIP_BIT BIT(0) /* Write in progress */ +#define SPI_NOR_WEL_BIT BIT(1) /* Write enable latch */ + +/* Flash opcodes */ +#define SPI_NOR_CMD_WRSR 0x01 /* Write status register */ +#define SPI_NOR_CMD_RDSR 0x05 /* Read status register */ +#define SPI_NOR_CMD_READ 0x03 /* Read data */ +#define SPI_NOR_CMD_WREN 0x06 /* Write enable */ +#define SPI_NOR_CMD_WRDI 0x04 /* Write disable */ +#define SPI_NOR_CMD_PP 0x02 /* Page program */ +#define SPI_NOR_CMD_SE 0x20 /* Sector erase */ +#define SPI_NOR_CMD_BE_32K 0x52 /* Block erase 32KB */ +#define SPI_NOR_CMD_BE 0xD8 /* Block erase */ +#define SPI_NOR_CMD_CE 0xC7 /* Chip erase */ +#define SPI_NOR_CMD_RDID 0x9F /* Read JEDEC ID */ +#define SPI_NOR_CMD_ULBPR 0x98 /* Global Block Protection Unlock */ +#define SPI_NOR_CMD_4BA 0xB7 /* Enter 4-Byte Address Mode */ +#define SPI_NOR_CMD_DPD 0xB9 /* Deep Power Down */ +#define SPI_NOR_CMD_RDPD 0xAB /* Release from Deep Power Down */ + +/* Page, sector, and block size are standard, not configurable. */ +#define SPI_NOR_PAGE_SIZE 0x0100U +#define SPI_NOR_SECTOR_SIZE 0x1000U +#define SPI_NOR_BLOCK_SIZE 0x10000U + +/* Test whether offset is aligned to a given number of bits. */ +#define SPI_NOR_IS_ALIGNED(_ofs, _bits) (((_ofs)&BIT_MASK(_bits)) == 0) +#define SPI_NOR_IS_SECTOR_ALIGNED(_ofs) SPI_NOR_IS_ALIGNED(_ofs, 12) + +#endif /*__SPI_NOR_H__*/ diff --git a/drivers/wifi/nrfwifi/src/qspi/src/device.c b/drivers/wifi/nrfwifi/src/qspi/src/device.c new file mode 100644 index 000000000000..4acbaf3861fc --- /dev/null +++ b/drivers/wifi/nrfwifi/src/qspi/src/device.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief File containing QSPI device specific definitions for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#include +#include +#include +#include +#include + +#include "qspi_if.h" +#include "spi_if.h" + +static struct qspi_config config; + +#if defined(CONFIG_NRF70_ON_QSPI) +static struct qspi_dev qspi = { + .init = qspi_init, + .deinit = qspi_deinit, + .read = qspi_read, + .write = qspi_write, + .hl_read = qspi_hl_read +}; +#else +static struct qspi_dev spim = { + .init = spim_init, + .deinit = spim_deinit, + .read = spim_read, + .write = spim_write, + .hl_read = spim_hl_read +}; +#endif + +struct qspi_config *qspi_defconfig(void) +{ + memset(&config, 0, sizeof(struct qspi_config)); +#if defined(CONFIG_NRF70_ON_QSPI) + config.addrmode = NRF_QSPI_ADDRMODE_24BIT; + config.RDC4IO = 0xA0; + config.easydma = true; + config.quad_spi = true; +#endif + config.addrmask = 0x800000; /* set bit23 (incr. addr mode) */ + + config.test_name = "QSPI TEST"; + config.test_hlread = false; + config.test_iteration = 0; + + config.qspi_slave_latency = 0; + + config.encryption = config.CMD_CNONCE = false; + +#if defined(CONFIG_NRF70_ON_QSPI) && (NRF_QSPI_HAS_XIP_ENC || NRF_QSPI_HAS_DMA_ENC) + + /*For #Bit 6 Enable below: i.e ALL Ones for QSPI Key*/ + memset(&config.p_cfg.key, 0xff, sizeof(config.p_cfg.key)); + + config.p_cfg.nonce[0] = 0x16181648; + config.p_cfg.nonce[1] = 0x0; + config.p_cfg.nonce[2] = 0x1; + +#endif /*CONFIG_NRF70_ON_QSPI && (NRF_QSPI_HAS_XIP_ENC || NRF_QSPI_HAS_DMA_ENC)*/ + + return &config; +} + +struct qspi_config *qspi_get_config(void) +{ + return &config; +} + +struct qspi_dev *qspi_dev(void) +{ +#if CONFIG_NRF70_ON_QSPI + return &qspi; +#else + return &spim; +#endif +} diff --git a/drivers/wifi/nrfwifi/src/qspi/src/ficr_prog.c b/drivers/wifi/nrfwifi/src/qspi/src/ficr_prog.c new file mode 100644 index 000000000000..5558b35e8374 --- /dev/null +++ b/drivers/wifi/nrfwifi/src/qspi/src/ficr_prog.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* @file + * @brief NRF Wi-Fi radio FICR programming functions + */ + +#include +#include +#include "rpu_if.h" +#include "rpu_hw_if.h" +#include "ficr_prog.h" + + +LOG_MODULE_DECLARE(otp_prog, CONFIG_WIFI_NRF70_BUS_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/drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c b/drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c new file mode 100644 index 000000000000..968ca72ad273 --- /dev/null +++ b/drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c @@ -0,0 +1,1327 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief File containing QSPI device interface specific definitions for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "spi_nor.h" +#include "qspi_if.h" + +/* The QSPI bus node which the NRF70 is on */ +#define QSPI_IF_BUS_NODE DT_NODELABEL(qspi) + +/* QSPI bus properties from the devicetree */ +#define QSPI_IF_BUS_IRQN DT_IRQN(QSPI_IF_BUS_NODE) +#define QSPI_IF_BUS_IRQ_PRIO DT_IRQ(QSPI_IF_BUS_NODE, priority) +#define QSPI_IF_BUS_SCK_PIN DT_PROP(QSPI_IF_BUS_NODE, sck_pin) +#define QSPI_IF_BUS_CSN_PIN DT_PROP(QSPI_IF_BUS_NODE, csn_pins) +#define QSPI_IF_BUS_IO0_PIN DT_PROP_BY_IDX(QSPI_IF_BUS_NODE, io_pins, 0) +#define QSPI_IF_BUS_IO1_PIN DT_PROP_BY_IDX(QSPI_IF_BUS_NODE, io_pins, 1) +#define QSPI_IF_BUS_IO2_PIN DT_PROP_BY_IDX(QSPI_IF_BUS_NODE, io_pins, 2) +#define QSPI_IF_BUS_IO3_PIN DT_PROP_BY_IDX(QSPI_IF_BUS_NODE, io_pins, 3) + +#define QSPI_IF_BUS_HAS_4_IO_PINS \ + (DT_PROP_LEN(QSPI_IF_BUS_NODE, io_pins) == 4) + +#define QSPI_IF_BUS_PINCTRL_DT_DEV_CONFIG_GET \ + PINCTRL_DT_DEV_CONFIG_GET(QSPI_IF_BUS_NODE) + +/* The NRF70 device node which is on the QSPI bus */ +#define QSPI_IF_DEVICE_NODE DT_NODELABEL(nrf70) + +/* NRF70 device QSPI properties */ +#define QSPI_IF_DEVICE_FREQUENCY DT_PROP(QSPI_IF_DEVICE_NODE, qspi_frequency) +#define QSPI_IF_DEVICE_CPHA DT_PROP(QSPI_IF_DEVICE_NODE, qspi_cpha) +#define QSPI_IF_DEVICE_CPOL DT_PROP(QSPI_IF_DEVICE_NODE, qspi_cpol) +#define QSPI_IF_DEVICE_QUAD_MODE DT_PROP(QSPI_IF_DEVICE_NODE, qspi_quad_mode) +#define QSPI_IF_DEVICE_RX_DELAY DT_PROP(QSPI_IF_DEVICE_NODE, qspi_rx_delay) + +static struct qspi_config *qspi_cfg; +#if NRF_QSPI_HAS_XIP_ENC || NRF_QSPI_HAS_DMA_ENC +static unsigned int nonce_last_addr; +static unsigned int nonce_cnt; +#endif /*NRF_QSPI_HAS_XIP_ENC || NRF_QSPI_HAS_DMA_ENC*/ + +/* Main config structure */ +static nrfx_qspi_config_t QSPIconfig; + +/* + * According to the respective specifications, the nRF52 QSPI supports clock + * frequencies 2 - 32 MHz and the nRF53 one supports 6 - 96 MHz. + */ +BUILD_ASSERT(QSPI_IF_DEVICE_FREQUENCY >= (NRF_QSPI_BASE_CLOCK_FREQ / 16), + "Unsupported SCK frequency."); + +/* + * Determine a configuration value (INST_0_SCK_CFG) and, if needed, a divider + * (BASE_CLOCK_DIV) for the clock from which the SCK frequency is derived that + * need to be used to achieve the SCK frequency as close as possible (but not + * higher) to the one specified in DT. + */ +#if defined(CONFIG_SOC_SERIES_NRF53X) +/* + * On nRF53 Series SoCs, the default /4 divider for the HFCLK192M clock can + * only be used when the QSPI peripheral is idle. When a QSPI operation is + * performed, the divider needs to be changed to /1 or /2 (particularly, + * the specification says that the peripheral "supports 192 MHz and 96 MHz + * PCLK192M frequency"), but after that operation is complete, the default + * divider needs to be restored to avoid increased current consumption. + */ +/* To prevent anomaly 159, use only divider /1 for HFCLK192M. */ +#define BASE_CLOCK_DIV NRF_CLOCK_HFCLK_DIV_1 +#if (QSPI_IF_DEVICE_FREQUENCY >= (NRF_QSPI_BASE_CLOCK_FREQ / 4)) +/* For requested SCK >= 24 MHz, use HFCLK192M / 1 / (2*4) = 24 MHz */ +#define INST_0_SCK_CFG NRF_QSPI_FREQ_DIV4 +#else +/* For requested SCK < 24 MHz, calculate the configuration value. */ +#define INST_0_SCK_CFG (DIV_ROUND_UP(NRF_QSPI_BASE_CLOCK_FREQ / 2, \ + QSPI_IF_DEVICE_FREQUENCY) - 1) +#endif +/* For 8 MHz, use HFCLK192M / 2 / (2*6) */ +#define INST_0_SCK_CFG_WAKE NRF_QSPI_FREQ_DIV6 + +#else +/* + * On nRF52 Series SoCs, the base clock divider is not configurable, + * so BASE_CLOCK_DIV is not defined. + */ +#if (QSPI_IF_DEVICE_FREQUENCY >= NRF_QSPI_BASE_CLOCK_FREQ) +#define INST_0_SCK_CFG NRF_QSPI_FREQ_DIV1 +#else +#define INST_0_SCK_CFG (DIV_ROUND_UP(NRF_QSPI_BASE_CLOCK_FREQ, \ + QSPI_IF_DEVICE_FREQUENCY) - 1) +#endif + +/* For 8 MHz, use PCLK32M / 4 */ +#define INST_0_SCK_CFG_WAKE NRF_QSPI_FREQ_DIV4 + +#endif /* defined(CONFIG_SOC_SERIES_NRF53X) */ + +static int qspi_device_init(const struct device *dev); +static void qspi_device_uninit(const struct device *dev); + +#define WORD_SIZE 4 + +LOG_MODULE_DECLARE(wifi_nrf_bus, CONFIG_WIFI_NRF70_BUS_LOG_LEVEL); + +/** + * @brief QSPI buffer structure + * Structure used both for TX and RX purposes. + * + * @param buf is a valid pointer to a data buffer. + * Can not be NULL. + * @param len is the length of the data to be handled. + * If no data to transmit/receive - pass 0. + */ +struct qspi_buf { + uint8_t *buf; + size_t len; +}; + +/** + * @brief QSPI command structure + * Structure used for custom command usage. + * + * @param op_code is a command value (i.e 0x9F - get Jedec ID) + * @param tx_buf structure used for TX purposes. Can be NULL if not used. + * @param rx_buf structure used for RX purposes. Can be NULL if not used. + */ +struct qspi_cmd { + uint8_t op_code; + const struct qspi_buf *tx_buf; + const struct qspi_buf *rx_buf; +}; + +/** + * @brief Structure for defining the QSPI NOR access + */ +struct qspi_nor_data { +#ifdef CONFIG_MULTITHREADING + /* The semaphore to control exclusive access on write/erase. */ + struct k_sem trans; + /* The semaphore to control exclusive access to the device. */ + struct k_sem sem; + /* The semaphore to indicate that transfer has completed. */ + struct k_sem sync; + /* The semaphore to control driver init/uninit. */ + struct k_sem count; +#else /* CONFIG_MULTITHREADING */ + /* A flag that signals completed transfer when threads are + * not enabled. + */ + volatile bool ready; +#endif /* CONFIG_MULTITHREADING */ +}; + +static inline int qspi_get_mode(bool cpol, bool cpha) +{ + register int ret = -EINVAL; + + if ((!cpol) && (!cpha)) + ret = 0; + else if (cpol && cpha) + ret = 1; + + __ASSERT(ret != -EINVAL, "Invalid QSPI mode"); + + return ret; +} + +static inline bool qspi_write_is_quad(nrf_qspi_writeoc_t lines) +{ + switch (lines) { + case NRF_QSPI_WRITEOC_PP4IO: + case NRF_QSPI_WRITEOC_PP4O: + return true; + default: + return false; + } +} + +static inline bool qspi_read_is_quad(nrf_qspi_readoc_t lines) +{ + switch (lines) { + case NRF_QSPI_READOC_READ4IO: + case NRF_QSPI_READOC_READ4O: + return true; + default: + return false; + } +} + +static inline int qspi_get_lines_write(uint8_t lines) +{ + register int ret = -EINVAL; + + switch (lines) { + case 3: + ret = NRF_QSPI_WRITEOC_PP4IO; + break; + case 2: + ret = NRF_QSPI_WRITEOC_PP4O; + break; + case 1: + ret = NRF_QSPI_WRITEOC_PP2O; + break; + case 0: + ret = NRF_QSPI_WRITEOC_PP; + break; + default: + break; + } + + __ASSERT(ret != -EINVAL, "Invalid QSPI write line"); + + return ret; +} + +static inline int qspi_get_lines_read(uint8_t lines) +{ + register int ret = -EINVAL; + + switch (lines) { + case 4: + ret = NRF_QSPI_READOC_READ4IO; + break; + case 3: + ret = NRF_QSPI_READOC_READ4O; + break; + case 2: + ret = NRF_QSPI_READOC_READ2IO; + break; + case 1: + ret = NRF_QSPI_READOC_READ2O; + break; + case 0: + ret = NRF_QSPI_READOC_FASTREAD; + break; + default: + break; + } + + __ASSERT(ret != -EINVAL, "Invalid QSPI read line"); + + return ret; +} + +nrfx_err_t _nrfx_qspi_read(void *p_rx_buffer, size_t rx_buffer_length, uint32_t src_address) +{ + return nrfx_qspi_read(p_rx_buffer, rx_buffer_length, src_address); +} + +nrfx_err_t _nrfx_qspi_write(void const *p_tx_buffer, size_t tx_buffer_length, uint32_t dst_address) +{ + return nrfx_qspi_write(p_tx_buffer, tx_buffer_length, dst_address); +} + +nrfx_err_t _nrfx_qspi_init(nrfx_qspi_config_t const *p_config, nrfx_qspi_handler_t handler, + void *p_context) +{ + NRF_QSPI_Type *p_reg = NRF_QSPI; + + nrfx_qspi_init(p_config, handler, p_context); + + /* RDC4IO = 4'hA (register IFTIMING), which means 10 Dummy Cycles for READ4. */ + p_reg->IFTIMING |= qspi_cfg->RDC4IO; + + /* LOG_DBG("%04x : IFTIMING", p_reg->IFTIMING & qspi_cfg->RDC4IO); */ + + /* ACTIVATE task fails for slave bitfile so ignore it */ + return NRFX_SUCCESS; +} + + +/** + * @brief Main configuration structure + */ +static struct qspi_nor_data qspi_nor_memory_data = { +#ifdef CONFIG_MULTITHREADING + .trans = Z_SEM_INITIALIZER(qspi_nor_memory_data.trans, 1, 1), + .sem = Z_SEM_INITIALIZER(qspi_nor_memory_data.sem, 1, 1), + .sync = Z_SEM_INITIALIZER(qspi_nor_memory_data.sync, 0, 1), + .count = Z_SEM_INITIALIZER(qspi_nor_memory_data.count, 0, K_SEM_MAX_LIMIT), +#endif /* CONFIG_MULTITHREADING */ +}; + +NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(QSPI_IF_BUS_NODE); + +IF_ENABLED(CONFIG_PINCTRL, (PINCTRL_DT_DEFINE(QSPI_IF_BUS_NODE))); + +/** + * @brief Converts NRFX return codes to the zephyr ones + */ +static inline int qspi_get_zephyr_ret_code(nrfx_err_t res) +{ + switch (res) { + case NRFX_SUCCESS: + return 0; + case NRFX_ERROR_INVALID_PARAM: + case NRFX_ERROR_INVALID_ADDR: + return -EINVAL; + case NRFX_ERROR_INVALID_STATE: + return -ECANCELED; +#if NRF53_ERRATA_159_ENABLE_WORKAROUND + case NRFX_ERROR_FORBIDDEN: + LOG_ERR("nRF5340 anomaly 159 conditions detected"); + LOG_ERR("Set the CPU clock to 64 MHz before starting QSPI operation"); + return -ECANCELED; +#endif + case NRFX_ERROR_BUSY: + case NRFX_ERROR_TIMEOUT: + default: + return -EBUSY; + } +} + +static inline struct qspi_nor_data *get_dev_data(const struct device *dev) +{ + return dev->data; +} + +static inline void qspi_lock(const struct device *dev) +{ +#ifdef CONFIG_MULTITHREADING + struct qspi_nor_data *dev_data = get_dev_data(dev); + + k_sem_take(&dev_data->sem, K_FOREVER); +#else /* CONFIG_MULTITHREADING */ + ARG_UNUSED(dev); +#endif /* CONFIG_MULTITHREADING */ + + /* + * Change the base clock divider only for the time the driver is locked + * to perform a QSPI operation, otherwise the power consumption would be + * increased also when the QSPI peripheral is idle. + */ +#if defined(CONFIG_SOC_SERIES_NRF53X) + nrf_clock_hfclk192m_div_set(NRF_CLOCK, BASE_CLOCK_DIV); +#endif +} + +static inline void qspi_unlock(const struct device *dev) +{ +#if defined(CONFIG_SOC_SERIES_NRF53X) + /* Restore the default base clock divider to reduce power consumption. + */ + nrf_clock_hfclk192m_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_4); +#endif + +#ifdef CONFIG_MULTITHREADING + struct qspi_nor_data *dev_data = get_dev_data(dev); + + k_sem_give(&dev_data->sem); +#else /* CONFIG_MULTITHREADING */ + ARG_UNUSED(dev); +#endif /* CONFIG_MULTITHREADING */ +} + +static inline void qspi_trans_lock(const struct device *dev) +{ +#ifdef CONFIG_MULTITHREADING + struct qspi_nor_data *dev_data = get_dev_data(dev); + + k_sem_take(&dev_data->trans, K_FOREVER); +#else /* CONFIG_MULTITHREADING */ + ARG_UNUSED(dev); +#endif /* CONFIG_MULTITHREADING */ +} + +static inline void qspi_trans_unlock(const struct device *dev) +{ +#ifdef CONFIG_MULTITHREADING + struct qspi_nor_data *dev_data = get_dev_data(dev); + + k_sem_give(&dev_data->trans); +#else /* CONFIG_MULTITHREADING */ + ARG_UNUSED(dev); +#endif /* CONFIG_MULTITHREADING */ +} + +static inline void qspi_wait_for_completion(const struct device *dev, nrfx_err_t res) +{ + struct qspi_nor_data *dev_data = get_dev_data(dev); + + if (res == NRFX_SUCCESS) { +#ifdef CONFIG_MULTITHREADING + k_sem_take(&dev_data->sync, K_FOREVER); +#else /* CONFIG_MULTITHREADING */ + unsigned int key = irq_lock(); + + while (!dev_data->ready) { + k_cpu_atomic_idle(key); + key = irq_lock(); + } + + dev_data->ready = false; + irq_unlock(key); +#endif /* CONFIG_MULTITHREADING */ + } +} + +static inline void qspi_complete(struct qspi_nor_data *dev_data) +{ +#ifdef CONFIG_MULTITHREADING + k_sem_give(&dev_data->sync); +#else /* CONFIG_MULTITHREADING */ + dev_data->ready = true; +#endif /* CONFIG_MULTITHREADING */ +} + +static inline void _qspi_complete(struct qspi_nor_data *dev_data) +{ + if (!qspi_cfg->easydma) + return; + + qspi_complete(dev_data); +} +static inline void _qspi_wait_for_completion(const struct device *dev, nrfx_err_t res) +{ + if (!qspi_cfg->easydma) + return; + + qspi_wait_for_completion(dev, res); +} + +/** + * @brief QSPI handler + * + * @param event Driver event type + * @param p_context Pointer to context. Use in interrupt handler. + * @retval None + */ +static void qspi_handler(nrfx_qspi_evt_t event, void *p_context) +{ + struct qspi_nor_data *dev_data = p_context; + + if (event == NRFX_QSPI_EVENT_DONE) + _qspi_complete(dev_data); +} + +static bool qspi_initialized; + +static int qspi_device_init(const struct device *dev) +{ + struct qspi_nor_data *dev_data = get_dev_data(dev); + nrfx_err_t res; + int ret = 0; + + if (!IS_ENABLED(CONFIG_NRF70_QSPI_LOW_POWER)) { + return 0; + } + + qspi_lock(dev); + + /* In multithreading, driver can call qspi_device_init more than once + * before calling qspi_device_uninit. Keepping count, so QSPI is + * uninitialized only at the last call (count == 0). + */ +#ifdef CONFIG_MULTITHREADING + k_sem_give(&dev_data->count); +#endif + + if (!qspi_initialized) { + res = nrfx_qspi_init(&QSPIconfig, qspi_handler, dev_data); + ret = qspi_get_zephyr_ret_code(res); + NRF_QSPI->IFTIMING |= qspi_cfg->RDC4IO; + qspi_initialized = (ret == 0); + } + + qspi_unlock(dev); + + return ret; +} + +static void qspi_device_uninit(const struct device *dev) +{ + bool last = true; + + if (!IS_ENABLED(CONFIG_NRF70_QSPI_LOW_POWER)) { + return; + } + + qspi_lock(dev); + +#ifdef CONFIG_MULTITHREADING + struct qspi_nor_data *dev_data = get_dev_data(dev); + + /* The last thread to finish using the driver uninit the QSPI */ + (void)k_sem_take(&dev_data->count, K_NO_WAIT); + last = (k_sem_count_get(&dev_data->count) == 0); +#endif + + if (last) { + while (nrfx_qspi_mem_busy_check() != NRFX_SUCCESS) { + if (IS_ENABLED(CONFIG_MULTITHREADING)) + k_msleep(50); + else + k_busy_wait(50000); + } + + nrfx_qspi_uninit(); + +#ifndef CONFIG_PINCTRL + nrf_gpio_cfg_output(QSPI_PROP_AT(csn_pins, 0)); + nrf_gpio_pin_set(QSPI_PROP_AT(csn_pins, 0)); +#endif + + qspi_initialized = false; + } + + qspi_unlock(dev); +} + +/* QSPI send custom command. + * + * If this is used for both send and receive the buffer sizes must be + * equal and cover the whole transaction. + */ +static int qspi_send_cmd(const struct device *dev, const struct qspi_cmd *cmd, bool wren) +{ + /* Check input parameters */ + if (!cmd) + return -EINVAL; + + const void *tx_buf = NULL; + size_t tx_len = 0; + void *rx_buf = NULL; + size_t rx_len = 0; + size_t xfer_len = sizeof(cmd->op_code); + + if (cmd->tx_buf) { + tx_buf = cmd->tx_buf->buf; + tx_len = cmd->tx_buf->len; + } + + if (cmd->rx_buf) { + rx_buf = cmd->rx_buf->buf; + rx_len = cmd->rx_buf->len; + } + + if ((rx_len != 0) && (tx_len != 0)) { + if (rx_len != tx_len) + return -EINVAL; + + xfer_len += tx_len; + } else + /* At least one of these is zero. */ + xfer_len += tx_len + rx_len; + + if (xfer_len > NRF_QSPI_CINSTR_LEN_9B) { + LOG_WRN("cinstr %02x transfer too long: %zu", cmd->op_code, xfer_len); + + return -EINVAL; + } + + nrf_qspi_cinstr_conf_t cinstr_cfg = { + .opcode = cmd->op_code, + .length = xfer_len, + .io2_level = true, + .io3_level = true, + .wipwait = false, + .wren = wren, + }; + + qspi_lock(dev); + + int res = nrfx_qspi_cinstr_xfer(&cinstr_cfg, tx_buf, rx_buf); + + qspi_unlock(dev); + return qspi_get_zephyr_ret_code(res); +} + +/* RDSR wrapper. Negative value is error. */ +static int qspi_rdsr(const struct device *dev) +{ + uint8_t sr = -1; + const struct qspi_buf sr_buf = { + .buf = &sr, + .len = sizeof(sr), + }; + struct qspi_cmd cmd = { + .op_code = SPI_NOR_CMD_RDSR, + .rx_buf = &sr_buf, + }; + int ret = qspi_send_cmd(dev, &cmd, false); + + return (ret < 0) ? ret : sr; +} + +/* Wait until RDSR confirms write is not in progress. */ +static int qspi_wait_while_writing(const struct device *dev) +{ + int ret; + + do { + ret = qspi_rdsr(dev); + } while ((ret >= 0) && ((ret & SPI_NOR_WIP_BIT) != 0U)); + + return (ret < 0) ? ret : 0; +} + +/** + * @brief Fills init struct + * + * @param config Pointer to the config struct provided by user + * @param initstruct Pointer to the configuration struct + * @retval None + */ +static inline void qspi_fill_init_struct(nrfx_qspi_config_t *initstruct) +{ + /* Configure XIP offset */ + initstruct->xip_offset = 0; + +#ifdef CONFIG_PINCTRL + initstruct->skip_gpio_cfg = true, + initstruct->skip_psel_cfg = true, +#else + /* Configure pins */ + initstruct->pins.sck_pin = QSPI_IF_BUS_SCK_PIN; + initstruct->pins.csn_pin = QSPI_IF_BUS_CSN_PIN; + initstruct->pins.io0_pin = QSPI_IF_BUS_IO0_PIN; + initstruct->pins.io1_pin = QSPI_IF_BUS_IO1_PIN; +#if QSPI_IF_BUS_HAS_4_IO_PINS + initstruct->pins.io2_pin = QSPI_IF_BUS_IO2_PIN; + initstruct->pins.io3_pin = QSPI_IF_BUS_IO3_PIN; +#else + initstruct->pins.io2_pin = NRF_QSPI_PIN_NOT_CONNECTED; + initstruct->pins.io3_pin = NRF_QSPI_PIN_NOT_CONNECTED; +#endif +#endif /* CONFIG_PINCTRL */ + /* Configure Protocol interface */ + initstruct->prot_if.addrmode = NRF_QSPI_ADDRMODE_24BIT; + + initstruct->prot_if.dpmconfig = false; + + /* Configure physical interface */ + initstruct->phy_if.sck_freq = INST_0_SCK_CFG; + /* Using MHZ fails checkpatch constant check */ + if (QSPI_IF_DEVICE_FREQUENCY >= 16000000) { + qspi_cfg->qspi_slave_latency = 1; + } + initstruct->phy_if.sck_delay = QSPI_IF_DEVICE_RX_DELAY; + initstruct->phy_if.spi_mode = qspi_get_mode(QSPI_IF_DEVICE_CPOL, QSPI_IF_DEVICE_CPHA); + + if (QSPI_IF_DEVICE_QUAD_MODE) { + initstruct->prot_if.readoc = NRF_QSPI_READOC_READ4IO; + initstruct->prot_if.writeoc = NRF_QSPI_WRITEOC_PP4IO; + } else { + initstruct->prot_if.readoc = NRF_QSPI_READOC_FASTREAD; + initstruct->prot_if.writeoc = NRF_QSPI_WRITEOC_PP; + } + + initstruct->phy_if.dpmen = false; +} + +/* Configures QSPI memory for the transfer */ +static int qspi_nrfx_configure(const struct device *dev) +{ + if (!dev) + return -ENXIO; + + struct qspi_nor_data *dev_data = dev->data; + + qspi_fill_init_struct(&QSPIconfig); + +#if defined(CONFIG_SOC_SERIES_NRF53X) + /* When the QSPI peripheral is activated, during the nrfx_qspi driver + * initialization, it reads the status of the connected flash chip. + * Make sure this transaction is performed with a valid base clock + * divider. + */ + nrf_clock_hfclk192m_div_set(NRF_CLOCK, BASE_CLOCK_DIV); +#endif + + nrfx_err_t res = _nrfx_qspi_init(&QSPIconfig, qspi_handler, dev_data); + +#if defined(CONFIG_SOC_SERIES_NRF53X) + /* Restore the default /4 divider after the QSPI initialization. */ + nrf_clock_hfclk192m_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_4); +#endif + + int ret = qspi_get_zephyr_ret_code(res); + + if (ret == 0) { + /* Set QE to match transfer mode. If not using quad + * it's OK to leave QE set, but doing so prevents use + * of WP#/RESET#/HOLD# which might be useful. + * + * Note build assert above ensures QER is S1B6. Other + * options require more logic. + */ + ret = qspi_rdsr(dev); + + if (ret < 0) { + LOG_ERR("RDSR failed: %d", ret); + return ret; + } + + uint8_t sr = (uint8_t)ret; + bool qe_value = (qspi_write_is_quad(QSPIconfig.prot_if.writeoc)) || + (qspi_read_is_quad(QSPIconfig.prot_if.readoc)); + const uint8_t qe_mask = BIT(6); /* only S1B6 */ + bool qe_state = ((sr & qe_mask) != 0U); + + LOG_DBG("RDSR %02x QE %d need %d: %s", sr, qe_state, qe_value, + (qe_state != qe_value) ? "updating" : "no-change"); + + ret = 0; + + if (qe_state != qe_value) { + const struct qspi_buf sr_buf = { + .buf = &sr, + .len = sizeof(sr), + }; + struct qspi_cmd cmd = { + .op_code = SPI_NOR_CMD_WRSR, + .tx_buf = &sr_buf, + }; + + sr ^= qe_mask; + ret = qspi_send_cmd(dev, &cmd, true); + + /* Writing SR can take some time, and further + * commands sent while it's happening can be + * corrupted. Wait. + */ + if (ret == 0) + ret = qspi_wait_while_writing(dev); + } + + if (ret < 0) + LOG_ERR("QE %s failed: %d", qe_value ? "set" : "clear", ret); + } + + return ret; +} + +static inline nrfx_err_t read_non_aligned(const struct device *dev, int addr, void *dest, + size_t size) +{ + uint8_t __aligned(WORD_SIZE) buf[WORD_SIZE * 2]; + uint8_t *dptr = dest; + + int flash_prefix = (WORD_SIZE - (addr % WORD_SIZE)) % WORD_SIZE; + + if (flash_prefix > size) + flash_prefix = size; + + int dest_prefix = (WORD_SIZE - (int)dptr % WORD_SIZE) % WORD_SIZE; + + if (dest_prefix > size) + dest_prefix = size; + + int flash_suffix = (size - flash_prefix) % WORD_SIZE; + int flash_middle = size - flash_prefix - flash_suffix; + int dest_middle = size - dest_prefix - (size - dest_prefix) % WORD_SIZE; + + if (flash_middle > dest_middle) { + flash_middle = dest_middle; + flash_suffix = size - flash_prefix - flash_middle; + } + + nrfx_err_t res = NRFX_SUCCESS; + + /* read from aligned flash to aligned memory */ + if (flash_middle != 0) { + res = _nrfx_qspi_read(dptr + dest_prefix, flash_middle, addr + flash_prefix); + + _qspi_wait_for_completion(dev, res); + + if (res != NRFX_SUCCESS) + return res; + + /* perform shift in RAM */ + if (flash_prefix != dest_prefix) + memmove(dptr + flash_prefix, dptr + dest_prefix, flash_middle); + } + + /* read prefix */ + if (flash_prefix != 0) { + res = _nrfx_qspi_read(buf, WORD_SIZE, addr - (WORD_SIZE - flash_prefix)); + + _qspi_wait_for_completion(dev, res); + + if (res != NRFX_SUCCESS) + return res; + + memcpy(dptr, buf + WORD_SIZE - flash_prefix, flash_prefix); + } + + /* read suffix */ + if (flash_suffix != 0) { + res = _nrfx_qspi_read(buf, WORD_SIZE * 2, addr + flash_prefix + flash_middle); + + _qspi_wait_for_completion(dev, res); + + if (res != NRFX_SUCCESS) + return res; + + memcpy(dptr + flash_prefix + flash_middle, buf, flash_suffix); + } + + return res; +} + +static int qspi_nor_read(const struct device *dev, int addr, void *dest, size_t size) +{ + if (!dest) + return -EINVAL; + + /* read size must be non-zero */ + if (!size) + return 0; + + int rc = qspi_device_init(dev); + + if (rc != 0) + goto out; + + qspi_lock(dev); + + nrfx_err_t res = read_non_aligned(dev, addr, dest, size); + + qspi_unlock(dev); + + rc = qspi_get_zephyr_ret_code(res); + +out: + qspi_device_uninit(dev); + return rc; +} + +/* addr aligned, sptr not null, slen less than 4 */ +static inline nrfx_err_t write_sub_word(const struct device *dev, int addr, const void *sptr, + size_t slen) +{ + uint8_t __aligned(4) buf[4]; + nrfx_err_t res; + + /* read out the whole word so that unchanged data can be + * written back + */ + res = _nrfx_qspi_read(buf, sizeof(buf), addr); + _qspi_wait_for_completion(dev, res); + + if (res == NRFX_SUCCESS) { + memcpy(buf, sptr, slen); + res = _nrfx_qspi_write(buf, sizeof(buf), addr); + _qspi_wait_for_completion(dev, res); + } + + return res; +} + +static int qspi_nor_write(const struct device *dev, int addr, const void *src, size_t size) +{ + if (!src) + return -EINVAL; + + /* write size must be non-zero, less than 4, or a multiple of 4 */ + if ((size == 0) || ((size > 4) && ((size % 4U) != 0))) + return -EINVAL; + + /* address must be 4-byte aligned */ + if ((addr % 4U) != 0) + return -EINVAL; + + nrfx_err_t res = NRFX_SUCCESS; + + int rc = qspi_device_init(dev); + + if (rc != 0) + goto out; + + qspi_trans_lock(dev); + + qspi_lock(dev); + + if (size < 4U) + res = write_sub_word(dev, addr, src, size); + else { + res = _nrfx_qspi_write(src, size, addr); + _qspi_wait_for_completion(dev, res); + } + + qspi_unlock(dev); + + qspi_trans_unlock(dev); + + rc = qspi_get_zephyr_ret_code(res); +out: + qspi_device_uninit(dev); + return rc; +} + +/** + * @brief Configure the flash + * + * @param dev The flash device structure + * @param info The flash info structure + * @return 0 on success, negative errno code otherwise + */ +static int qspi_nor_configure(const struct device *dev) +{ + int ret = qspi_nrfx_configure(dev); + + if (ret != 0) + return ret; + + qspi_device_uninit(dev); + + return 0; +} + +/** + * @brief Initialize and configure the flash + * + * @param name The flash name + * @return 0 on success, negative errno code otherwise + */ +static int qspi_nor_init(const struct device *dev) +{ +#ifdef CONFIG_PINCTRL + int ret = pinctrl_apply_state(QSPI_IF_BUS_PINCTRL_DT_DEV_CONFIG_GET, + PINCTRL_STATE_DEFAULT); + + if (ret < 0) { + return ret; + } +#endif + + IRQ_CONNECT(QSPI_IF_BUS_IRQN, + QSPI_IF_BUS_IRQ_PRIO, + nrfx_isr, + nrfx_qspi_irq_handler, + 0); + + return qspi_nor_configure(dev); +} + +#if defined(CONFIG_SOC_SERIES_NRF53X) +static int qspi_cmd_encryption(const struct device *dev, nrf_qspi_encryption_t *p_cfg) +{ + const struct qspi_buf tx_buf = { .buf = (uint8_t *)&p_cfg->nonce[1], + .len = sizeof(p_cfg->nonce[1]) }; + const struct qspi_cmd cmd = { + .op_code = 0x4f, + .tx_buf = &tx_buf, + }; + + int ret = qspi_device_init(dev); + + if (ret == 0) + ret = qspi_send_cmd(dev, &cmd, false); + + qspi_device_uninit(dev); + + if (ret < 0) + LOG_DBG("cmd_encryption failed %d", ret); + + return ret; +} +#endif + +int qspi_RDSR2(const struct device *dev, uint8_t *rdsr2) +{ + int ret = 0; + uint8_t sr = 0; + + const struct qspi_buf sr_buf = { + .buf = &sr, + .len = sizeof(sr), + }; + struct qspi_cmd cmd = { + .op_code = 0x2f, + .rx_buf = &sr_buf, + }; + + ret = qspi_device_init(dev); + + ret = qspi_send_cmd(dev, &cmd, false); + + qspi_device_uninit(dev); + + LOG_DBG("RDSR2 = 0x%x", sr); + + if (ret == 0) + *rdsr2 = sr; + + return ret; +} + +/* Wait until RDSR2 confirms RPU_WAKE write is successful */ +int qspi_validate_rpu_wake_writecmd(const struct device *dev) +{ + int ret = 0; + uint8_t rdsr2 = 0; + + for (int ii = 0; ii < 1; ii++) { + ret = qspi_RDSR2(dev, &rdsr2); + if (!ret && (rdsr2 & RPU_WAKEUP_NOW)) { + return 0; + } + } + + return -1; +} + + +int qspi_RDSR1(const struct device *dev, uint8_t *rdsr1) +{ + int ret = 0; + uint8_t sr = 0; + + const struct qspi_buf sr_buf = { + .buf = &sr, + .len = sizeof(sr), + }; + struct qspi_cmd cmd = { + .op_code = 0x1f, + .rx_buf = &sr_buf, + }; + + ret = qspi_device_init(dev); + + ret = qspi_send_cmd(dev, &cmd, false); + + qspi_device_uninit(dev); + + LOG_DBG("RDSR1 = 0x%x", sr); + + if (ret == 0) + *rdsr1 = sr; + + return ret; +} + +/* Wait until RDSR1 confirms RPU_AWAKE/RPU_READY */ +int qspi_wait_while_rpu_awake(const struct device *dev) +{ + int ret; + uint8_t val = 0; + + for (int ii = 0; ii < 10; ii++) { + ret = qspi_RDSR1(dev, &val); + + LOG_DBG("RDSR1 = 0x%x", val); + + if (!ret && (val & RPU_AWAKE_BIT)) { + break; + } + + k_msleep(1); + } + + if (ret || !(val & RPU_AWAKE_BIT)) { + LOG_ERR("RPU is not awake even after 10ms"); + return -1; + } + + /* Restore QSPI clock frequency from DTS */ + QSPIconfig.phy_if.sck_freq = INST_0_SCK_CFG; + + return val; +} + +int qspi_WRSR2(const struct device *dev, uint8_t data) +{ + const struct qspi_buf tx_buf = { + .buf = &data, + .len = sizeof(data), + }; + const struct qspi_cmd cmd = { + .op_code = 0x3f, + .tx_buf = &tx_buf, + }; + int ret = qspi_device_init(dev); + + if (ret == 0) + ret = qspi_send_cmd(dev, &cmd, false); + + qspi_device_uninit(dev); + + if (ret < 0) + LOG_ERR("cmd_wakeup RPU failed %d", ret); + + return ret; +} + +int qspi_cmd_wakeup_rpu(const struct device *dev, uint8_t data) +{ + int ret; + + /* Waking RPU works reliably only with lowest frequency (8MHz) */ + QSPIconfig.phy_if.sck_freq = INST_0_SCK_CFG_WAKE; + + ret = qspi_WRSR2(dev, data); + + return ret; +} + +struct device qspi_perip = { + .data = &qspi_nor_memory_data, +}; + +int qspi_deinit(void) +{ + LOG_DBG("TODO : %s", __func__); + + return 0; +} + +int qspi_init(struct qspi_config *config) +{ + unsigned int rc; + + qspi_cfg = config; + + config->readoc = config->quad_spi ? NRF_QSPI_READOC_READ4IO : NRF_QSPI_READOC_FASTREAD; + config->writeoc = config->quad_spi ? NRF_QSPI_WRITEOC_PP4IO : NRF_QSPI_WRITEOC_PP; + + rc = qspi_nor_init(&qspi_perip); + + k_sem_init(&qspi_cfg->lock, 1, 1); + + return rc; +} + +void qspi_update_nonce(unsigned int addr, int len, int hlread) +{ +#if NRF_QSPI_HAS_XIP_ENC || NRF_QSPI_HAS_DMA_ENC + + NRF_QSPI_Type *p_reg = NRF_QSPI; + + if (!qspi_cfg->encryption) + return; + + if (nonce_last_addr == 0 || hlread) + p_reg->DMA_ENC.NONCE2 = ++nonce_cnt; + else if ((nonce_last_addr + 4) != addr) + p_reg->DMA_ENC.NONCE2 = ++nonce_cnt; + + nonce_last_addr = addr + len - 4; + +#endif /*NRF_QSPI_HAS_XIP_ENC || NRF_QSPI_HAS_DMA_ENC*/ +} + +void qspi_addr_check(unsigned int addr, const void *data, unsigned int len) +{ + if ((addr % 4 != 0) || (((unsigned int)data) % 4 != 0) || (len % 4 != 0)) { + LOG_ERR("%s : Unaligned address %x %x %d %x %x", __func__, addr, + (unsigned int)data, (addr % 4 != 0), (((unsigned int)data) % 4 != 0), + (len % 4 != 0)); + } +} + +int qspi_write(unsigned int addr, const void *data, int len) +{ + int status; + + qspi_addr_check(addr, data, len); + + addr |= qspi_cfg->addrmask; + + k_sem_take(&qspi_cfg->lock, K_FOREVER); + + qspi_update_nonce(addr, len, 0); + + status = qspi_nor_write(&qspi_perip, addr, data, len); + + k_sem_give(&qspi_cfg->lock); + + return status; +} + +int qspi_read(unsigned int addr, void *data, int len) +{ + int status; + + qspi_addr_check(addr, data, len); + + addr |= qspi_cfg->addrmask; + + k_sem_take(&qspi_cfg->lock, K_FOREVER); + + qspi_update_nonce(addr, len, 0); + + status = qspi_nor_read(&qspi_perip, addr, data, len); + + k_sem_give(&qspi_cfg->lock); + + return status; +} + +int qspi_hl_readw(unsigned int addr, void *data) +{ + int status; + uint8_t *rxb = NULL; + uint32_t len = 4; + + len = len + (4 * qspi_cfg->qspi_slave_latency); + + rxb = k_malloc(len); + + if (rxb == NULL) { + LOG_ERR("%s: ERROR ENOMEM line %d", __func__, __LINE__); + return -ENOMEM; + } + + memset(rxb, 0, len); + + k_sem_take(&qspi_cfg->lock, K_FOREVER); + + qspi_update_nonce(addr, 4, 1); + + status = qspi_nor_read(&qspi_perip, addr, rxb, len); + + k_sem_give(&qspi_cfg->lock); + + *(uint32_t *)data = *(uint32_t *)(rxb + (len - 4)); + + k_free(rxb); + + return status; +} + +int qspi_hl_read(unsigned int addr, void *data, int len) +{ + int count = 0; + + qspi_addr_check(addr, data, len); + + while (count < (len / 4)) { + qspi_hl_readw(addr + (4 * count), ((char *)data + (4 * count))); + count++; + } + + return 0; +} + +int qspi_cmd_sleep_rpu(const struct device *dev) +{ + uint8_t data = 0x0; + + /* printf("TODO : %s:", __func__); */ + const struct qspi_buf tx_buf = { + .buf = &data, + .len = sizeof(data), + }; + + const struct qspi_cmd cmd = { + .op_code = 0x3f, /* 0x3f, //WRSR2(0x3F) WakeUP RPU. */ + .tx_buf = &tx_buf, + }; + + int ret = qspi_device_init(dev); + + if (ret == 0) { + ret = qspi_send_cmd(dev, &cmd, false); + } + + qspi_device_uninit(dev); + + if (ret < 0) { + LOG_ERR("cmd_wakeup RPU failed: %d", ret); + } + + return ret; +} + +/* Encryption public API */ + +int qspi_enable_encryption(uint8_t *key) +{ +#if defined(CONFIG_SOC_SERIES_NRF53X) + int err = 0; + + if (qspi_cfg->encryption) + return -EALREADY; + + int ret = qspi_device_init(&qspi_perip); + + if (ret != 0) { + LOG_ERR("qspi_device_init failed: %d", ret); + return -EIO; + } + + memcpy(qspi_cfg->p_cfg.key, key, 16); + + err = nrfx_qspi_dma_encrypt(&qspi_cfg->p_cfg); + if (err != NRFX_SUCCESS) { + LOG_ERR("nrfx_qspi_dma_encrypt failed: %d", err); + return -EIO; + } + + err = qspi_cmd_encryption(&qspi_perip, &qspi_cfg->p_cfg); + if (err != 0) { + LOG_ERR("qspi_cmd_encryption failed: %d", err); + return -EIO; + } + + qspi_cfg->encryption = true; + + qspi_device_uninit(&qspi_perip); + + return 0; +#else + return -ENOTSUP; +#endif +} diff --git a/drivers/wifi/nrfwifi/src/qspi/src/rpu_hw_if.c b/drivers/wifi/nrfwifi/src/qspi/src/rpu_hw_if.c new file mode 100644 index 000000000000..4ab65805e8ec --- /dev/null +++ b/drivers/wifi/nrfwifi/src/qspi/src/rpu_hw_if.c @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief File containing common functions for RPU hardware interaction + * using QSPI and SPI that can be invoked by shell or the driver. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "rpu_hw_if.h" +#include "qspi_if.h" +#include "spi_if.h" + +LOG_MODULE_REGISTER(wifi_nrf_bus, CONFIG_WIFI_NRF70_BUS_LOG_LEVEL); + +#define NRF7002_NODE DT_NODELABEL(nrf70) + +static const struct gpio_dt_spec host_irq_spec = +GPIO_DT_SPEC_GET(NRF7002_NODE, host_irq_gpios); + +static const struct gpio_dt_spec iovdd_ctrl_spec = +GPIO_DT_SPEC_GET(NRF7002_NODE, iovdd_ctrl_gpios); + +static const struct gpio_dt_spec bucken_spec = +GPIO_DT_SPEC_GET(NRF7002_NODE, bucken_gpios); + +char blk_name[][15] = { "SysBus", "ExtSysBus", "PBus", "PKTRAM", + "GRAM", "LMAC_ROM", "LMAC_RET_RAM", "LMAC_SRC_RAM", + "UMAC_ROM", "UMAC_RET_RAM", "UMAC_SRC_RAM" }; + +uint32_t rpu_7002_memmap[][3] = { + { 0x000000, 0x008FFF, 1 }, + { 0x009000, 0x03FFFF, 2 }, + { 0x040000, 0x07FFFF, 1 }, + { 0x0C0000, 0x0F0FFF, 0 }, + { 0x080000, 0x092000, 1 }, + { 0x100000, 0x134000, 1 }, + { 0x140000, 0x14C000, 1 }, + { 0x180000, 0x190000, 1 }, + { 0x200000, 0x261800, 1 }, + { 0x280000, 0x2A4000, 1 }, + { 0x300000, 0x338000, 1 } +}; + +static const struct qspi_dev *qdev; +static struct qspi_config *cfg; + +static int validate_addr_blk(uint32_t start_addr, + uint32_t end_addr, + uint32_t block_no, + bool *hl_flag, + int *selected_blk) +{ + uint32_t *block_map = rpu_7002_memmap[block_no]; + + if (((start_addr >= block_map[0]) && (start_addr <= block_map[1])) && + ((end_addr >= block_map[0]) && (end_addr <= block_map[1]))) { + if (block_no == PKTRAM) { + *hl_flag = 0; + } + *selected_blk = block_no; + return 0; + } + + return -1; +} + +static int rpu_validate_addr(uint32_t start_addr, uint32_t len, bool *hl_flag) +{ + int ret = 0, i; + uint32_t end_addr; + int selected_blk; + + end_addr = start_addr + len - 1; + + *hl_flag = 1; + + for (i = 0; i < NUM_MEM_BLOCKS; i++) { + ret = validate_addr_blk(start_addr, end_addr, i, hl_flag, &selected_blk); + if (!ret) { + break; + } + } + + if (ret) { + LOG_ERR("Address validation failed - pls check memmory map and re-try"); + return -1; + } + + if ((selected_blk == LMAC_ROM) || (selected_blk == UMAC_ROM)) { + LOG_ERR("Error: Cannot write to ROM blocks"); + return -1; + } + + cfg->qspi_slave_latency = (*hl_flag) ? rpu_7002_memmap[selected_blk][2] : 0; + + return 0; +} + +int rpu_irq_config(struct gpio_callback *irq_callback_data, void (*irq_handler)()) +{ + int ret; + + if (!device_is_ready(host_irq_spec.port)) { + LOG_ERR("Host IRQ GPIO %s is not ready", host_irq_spec.port->name); + return -ENODEV; + } + + ret = gpio_pin_configure_dt(&host_irq_spec, GPIO_INPUT); + if (ret) { + LOG_ERR("Failed to configure host_irq pin %d", host_irq_spec.pin); + goto out; + } + + ret = gpio_pin_interrupt_configure_dt(&host_irq_spec, + GPIO_INT_EDGE_TO_ACTIVE); + if (ret) { + LOG_ERR("Failed to configure interrupt on host_irq pin %d", + host_irq_spec.pin); + goto out; + } + + gpio_init_callback(irq_callback_data, + irq_handler, + BIT(host_irq_spec.pin)); + + ret = gpio_add_callback(host_irq_spec.port, irq_callback_data); + if (ret) { + LOG_ERR("Failed to add callback on host_irq pin %d", + host_irq_spec.pin); + goto out; + } + + LOG_DBG("Finished Interrupt config\n"); + +out: + return ret; +} + +int rpu_irq_remove(struct gpio_callback *irq_callback_data) +{ + int ret; + + ret = gpio_pin_configure_dt(&host_irq_spec, GPIO_DISCONNECTED); + if (ret) { + LOG_ERR("Failed to remove host_irq pin %d", host_irq_spec.pin); + goto out; + } + + ret = gpio_remove_callback(host_irq_spec.port, irq_callback_data); + if (ret) { + LOG_ERR("Failed to remove callback on host_irq pin %d", + host_irq_spec.pin); + goto out; + } + +out: + return ret; +} + +static int rpu_gpio_config(void) +{ + int ret; + + if (!device_is_ready(iovdd_ctrl_spec.port)) { + LOG_ERR("IOVDD GPIO %s is not ready", iovdd_ctrl_spec.port->name); + return -ENODEV; + } + + if (!device_is_ready(bucken_spec.port)) { + LOG_ERR("BUCKEN GPIO %s is not ready", bucken_spec.port->name); + return -ENODEV; + } + + ret = gpio_pin_configure_dt(&bucken_spec, (GPIO_OUTPUT | NRF_GPIO_DRIVE_H0H1)); + if (ret) { + LOG_ERR("BUCKEN GPIO configuration failed..."); + return ret; + } + + ret = gpio_pin_configure_dt(&iovdd_ctrl_spec, GPIO_OUTPUT); + if (ret) { + LOG_ERR("IOVDD GPIO configuration failed..."); + gpio_pin_configure_dt(&bucken_spec, GPIO_DISCONNECTED); + return ret; + } + + LOG_DBG("GPIO configuration done...\n"); + + return 0; +} + +static int rpu_gpio_remove(void) +{ + int ret; + + ret = gpio_pin_configure_dt(&bucken_spec, GPIO_DISCONNECTED); + if (ret) { + LOG_ERR("BUCKEN GPIO remove failed..."); + return ret; + } + + ret = gpio_pin_configure_dt(&iovdd_ctrl_spec, GPIO_DISCONNECTED); + if (ret) { + LOG_ERR("IOVDD GPIO remove failed..."); + return ret; + } + + LOG_DBG("GPIO remove done...\n"); + return ret; +} + +static int rpu_pwron(void) +{ + int ret; + + ret = gpio_pin_set_dt(&bucken_spec, 1); + if (ret) { + LOG_ERR("BUCKEN GPIO set failed..."); + return ret; + } + /* Settling time is 50us (H0) or 100us (L0) */ + k_msleep(1); + + ret = gpio_pin_set_dt(&iovdd_ctrl_spec, 1); + if (ret) { + LOG_ERR("IOVDD GPIO set failed..."); + gpio_pin_set_dt(&bucken_spec, 0); + return ret; + } + /* Settling time for iovdd nRF7002 DK/EK - switch (TCK106AG): ~600us */ + k_msleep(1); + + if (IS_ENABLED(CONFIG_NRF_WIFI_COMBINED_BUCKEN_IOVDD_GPIO)) { + /* When a single GPIO is used, we need a total wait time after bucken assertion + * to be 6ms (1ms + 1ms + 4ms). + */ + k_msleep(4); + } + + LOG_DBG("Bucken = %d, IOVDD = %d", gpio_pin_get_dt(&bucken_spec), + gpio_pin_get_dt(&iovdd_ctrl_spec)); + + return ret; +} + +static int rpu_pwroff(void) +{ + int ret; + + ret = gpio_pin_set_dt(&bucken_spec, 0); /* BUCKEN = 0 */ + if (ret) { + LOG_ERR("BUCKEN GPIO set failed..."); + return ret; + } + + ret = gpio_pin_set_dt(&iovdd_ctrl_spec, 0); /* IOVDD CNTRL = 0 */ + if (ret) { + LOG_ERR("IOVDD GPIO set failed..."); + return ret; + } + + return ret; +} + +int rpu_read(unsigned int addr, void *data, int len) +{ + bool hl_flag; + + if (rpu_validate_addr(addr, len, &hl_flag)) { + return -1; + } + + if (hl_flag) + return qdev->hl_read(addr, data, len); + else + return qdev->read(addr, data, len); +} + +int rpu_write(unsigned int addr, const void *data, int len) +{ + bool hl_flag; + + if (rpu_validate_addr(addr, len, &hl_flag)) { + return -1; + } + + return qdev->write(addr, data, len); +} + +int rpu_sleep(void) +{ +#if CONFIG_NRF70_ON_QSPI + return qspi_cmd_sleep_rpu(&qspi_perip); +#else + return spim_cmd_sleep_rpu_fn(); +#endif +} + +int rpu_wakeup(void) +{ + int ret; + + ret = rpu_wrsr2(1); + if (ret) { + LOG_ERR("Error: WRSR2 failed"); + return ret; + } + + ret = rpu_rdsr2(); + if (ret < 0) { + LOG_ERR("Error: RDSR2 failed"); + return ret; + } + + ret = rpu_rdsr1(); + if (ret < 0) { + LOG_ERR("Error: RDSR1 failed"); + return ret; + } + + return 0; +} + +int rpu_sleep_status(void) +{ + return rpu_rdsr1(); +} + +void rpu_get_sleep_stats(uint32_t addr, uint32_t *buff, uint32_t wrd_len) +{ + int ret; + + ret = rpu_wakeup(); + if (ret) { + LOG_ERR("Error: RPU wakeup failed"); + return; + } + + ret = rpu_read(addr, buff, wrd_len * 4); + if (ret) { + LOG_ERR("Error: RPU read failed"); + return; + } + + ret = rpu_sleep(); + if (ret) { + LOG_ERR("Error: RPU sleep failed"); + return; + } +} + +int rpu_wrsr2(uint8_t data) +{ + int ret; + +#if CONFIG_NRF70_ON_QSPI + ret = qspi_cmd_wakeup_rpu(&qspi_perip, data); +#else + ret = spim_cmd_rpu_wakeup_fn(data); +#endif + + LOG_DBG("Written 0x%x to WRSR2", data); + return ret; +} + +int rpu_rdsr2(void) +{ +#if CONFIG_NRF70_ON_QSPI + return qspi_validate_rpu_wake_writecmd(&qspi_perip); +#else + return spi_validate_rpu_wake_writecmd(); +#endif +} + +int rpu_rdsr1(void) +{ +#if CONFIG_NRF70_ON_QSPI + return qspi_wait_while_rpu_awake(&qspi_perip); +#else + return spim_wait_while_rpu_awake(); +#endif +} + + +int rpu_clks_on(void) +{ + uint32_t rpu_clks = 0x100; + /* Enable RPU Clocks */ + qdev->write(0x048C20, &rpu_clks, 4); + LOG_DBG("RPU Clocks ON..."); + return 0; +} + +int rpu_init(void) +{ + int ret; + + qdev = qspi_dev(); + cfg = qspi_get_config(); + + ret = rpu_gpio_config(); + if (ret) { + goto out; + } + +#ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH + ret = sr_gpio_config(); + if (ret) { + goto remove_rpu_gpio; + } +#endif + ret = rpu_pwron(); + if (ret) { +#ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH + goto remove_sr_gpio; +#else + goto remove_rpu_gpio; +#endif + } + + return 0; + +#ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH +remove_sr_gpio: + sr_gpio_remove(); +#endif +remove_rpu_gpio: + rpu_gpio_remove(); +out: + return ret; +} + +int rpu_enable(void) +{ + int ret; + + ret = rpu_wakeup(); + if (ret) { + goto rpu_pwroff; + } + + ret = rpu_clks_on(); + if (ret) { + goto rpu_pwroff; + } + + return 0; +rpu_pwroff: + rpu_pwroff(); + return ret; +} + +int rpu_disable(void) +{ + int ret; + + ret = rpu_pwroff(); + if (ret) { + goto out; + } + ret = rpu_gpio_remove(); + if (ret) { + goto out; + } + +#ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH + ret = sr_gpio_remove(); + if (ret) { + goto out; + } +#endif + qdev = NULL; + cfg = NULL; + +out: + return ret; +} diff --git a/drivers/wifi/nrfwifi/src/qspi/src/spi_if.c b/drivers/wifi/nrfwifi/src/qspi/src/spi_if.c new file mode 100644 index 000000000000..03ffa4657b97 --- /dev/null +++ b/drivers/wifi/nrfwifi/src/qspi/src/spi_if.c @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief File containing SPI device interface specific definitions for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#include + +#include +#include + +#include "qspi_if.h" +#include "spi_if.h" + +LOG_MODULE_DECLARE(wifi_nrf_bus, CONFIG_WIFI_NRF70_BUS_LOG_LEVEL); + +#define NRF7002_NODE DT_NODELABEL(nrf70) + +static struct qspi_config *spim_config; + +static const struct spi_dt_spec spi_spec = +SPI_DT_SPEC_GET(NRF7002_NODE, SPI_WORD_SET(8) | SPI_TRANSFER_MSB, 0); + +static int spim_xfer_tx(unsigned int addr, void *data, unsigned int len) +{ + int err; + uint8_t hdr[4] = { + 0x02, /* PP opcode */ + (((addr >> 16) & 0xFF) | 0x80), + (addr >> 8) & 0xFF, + (addr & 0xFF) + }; + + const struct spi_buf tx_buf[] = { + {.buf = hdr, .len = sizeof(hdr) }, + {.buf = data, .len = len }, + }; + const struct spi_buf_set tx = { .buffers = tx_buf, .count = 2 }; + + + err = spi_transceive_dt(&spi_spec, &tx, NULL); + + return err; +} + + +static int spim_xfer_rx(unsigned int addr, void *data, unsigned int len, unsigned int discard_bytes) +{ + uint8_t hdr[] = { + 0x0b, /* FASTREAD opcode */ + (addr >> 16) & 0xFF, + (addr >> 8) & 0xFF, + addr & 0xFF, + 0 /* dummy byte */ + }; + + const struct spi_buf tx_buf[] = { + {.buf = hdr, .len = sizeof(hdr) }, + {.buf = NULL, .len = len }, + }; + + const struct spi_buf_set tx = { .buffers = tx_buf, .count = 2 }; + + const struct spi_buf rx_buf[] = { + {.buf = NULL, .len = sizeof(hdr) + discard_bytes}, + {.buf = data, .len = len }, + }; + + const struct spi_buf_set rx = { .buffers = rx_buf, .count = 2 }; + + return spi_transceive_dt(&spi_spec, &tx, &rx); +} + +int spim_read_reg(uint32_t reg_addr, uint8_t *reg_value) +{ + int err; + uint8_t tx_buffer[6] = { reg_addr }; + + const struct spi_buf tx_buf = { + .buf = tx_buffer, + .len = sizeof(tx_buffer) + }; + + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + uint8_t sr[6]; + + struct spi_buf rx_buf = { + .buf = &sr, + .len = sizeof(sr), + }; + const struct spi_buf_set rx = { .buffers = &rx_buf, .count = 1 }; + + err = spi_transceive_dt(&spi_spec, &tx, &rx); + + LOG_DBG("err: %d -> %x %x %x %x %x %x", err, sr[0], sr[1], sr[2], sr[3], sr[4], sr[5]); + + if (err == 0) + *reg_value = sr[1]; + + return err; +} + +int spim_write_reg(uint32_t reg_addr, const uint8_t reg_value) +{ + int err; + uint8_t tx_buffer[] = { reg_addr, reg_value }; + + const struct spi_buf tx_buf = { .buf = tx_buffer, .len = sizeof(tx_buffer) }; + const struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1 }; + + err = spi_transceive_dt(&spi_spec, &tx, NULL); + + if (err) { + LOG_ERR("SPI error: %d", err); + } + + return err; +} + + +int spim_RDSR1(const struct device *dev, uint8_t *rdsr1) +{ + uint8_t val = 0; + + return spim_read_reg(0x1F, &val); +} + +int spim_RDSR2(const struct device *dev, uint8_t *rdsr1) +{ + uint8_t val = 0; + + return spim_read_reg(0x2F, &val); +} + +int spim_WRSR2(const struct device *dev, const uint8_t wrsr2) +{ + return spim_write_reg(0x3F, wrsr2); +} + +int _spim_wait_while_rpu_awake(void) +{ + int ret; + uint8_t val = 0; + + for (int ii = 0; ii < 10; ii++) { + + ret = spim_read_reg(0x1F, &val); + + LOG_DBG("RDSR1 = 0x%x", val); + + if (!ret && (val & RPU_AWAKE_BIT)) { + break; + } + + k_msleep(1); + } + + if (ret || !(val & RPU_AWAKE_BIT)) { + LOG_ERR("RPU is not awake even after 10ms"); + return -1; + } + + return val; +} + +/* Wait until RDSR2 confirms RPU_WAKEUP_NOW write is successful */ +int spim_wait_while_rpu_wake_write(void) +{ + int ret; + uint8_t val = 0; + + for (int ii = 0; ii < 10; ii++) { + + ret = spim_read_reg(0x2F, &val); + + LOG_DBG("RDSR2 = 0x%x", val); + + if (!ret && (val & RPU_WAKEUP_NOW)) { + break; + } + + k_msleep(1); + } + + if (ret || !(val & RPU_WAKEUP_NOW)) { + LOG_ERR("RPU wakeup write ACK failed even after 10ms"); + return -1; + } + + return ret; +} + +int spim_cmd_rpu_wakeup(uint32_t data) +{ + return spim_write_reg(0x3F, data); +} + +unsigned int spim_cmd_sleep_rpu(void) +{ + int err; + uint8_t tx_buffer[] = { 0x3f, 0x0 }; + + const struct spi_buf tx_buf = { .buf = tx_buffer, .len = sizeof(tx_buffer) }; + const struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1 }; + + err = spi_transceive_dt(&spi_spec, &tx, NULL); + + if (err) { + LOG_ERR("SPI error: %d", err); + } + + return 0; +} + +int spim_init(struct qspi_config *config) +{ + if (!spi_is_ready_dt(&spi_spec)) { + LOG_ERR("Device %s is not ready", spi_spec.bus->name); + return -ENODEV; + } + + spim_config = config; + + k_sem_init(&spim_config->lock, 1, 1); + + if (spi_spec.config.frequency >= MHZ(16)) { + spim_config->qspi_slave_latency = 1; + } + + LOG_INF("SPIM %s: freq = %d MHz", spi_spec.bus->name, + spi_spec.config.frequency / MHZ(1)); + LOG_INF("SPIM %s: latency = %d", spi_spec.bus->name, spim_config->qspi_slave_latency); + + return 0; +} + +int spim_deinit(void) +{ + LOG_DBG("TODO : %s", __func__); + + return 0; +} + +static void spim_addr_check(unsigned int addr, const void *data, unsigned int len) +{ + if ((addr % 4 != 0) || (((unsigned int)data) % 4 != 0) || (len % 4 != 0)) { + LOG_ERR("%s : Unaligned address %x %x %d %x %x", __func__, addr, + (unsigned int)data, (addr % 4 != 0), (((unsigned int)data) % 4 != 0), + (len % 4 != 0)); + } +} + +int spim_write(unsigned int addr, const void *data, int len) +{ + int status; + + spim_addr_check(addr, data, len); + + addr |= spim_config->addrmask; + + k_sem_take(&spim_config->lock, K_FOREVER); + + status = spim_xfer_tx(addr, (void *)data, len); + + k_sem_give(&spim_config->lock); + + return status; +} + +int spim_read(unsigned int addr, void *data, int len) +{ + int status; + + spim_addr_check(addr, data, len); + + addr |= spim_config->addrmask; + + k_sem_take(&spim_config->lock, K_FOREVER); + + status = spim_xfer_rx(addr, data, len, 0); + + k_sem_give(&spim_config->lock); + + return status; +} + +static int spim_hl_readw(unsigned int addr, void *data) +{ + int status = -1; + + k_sem_take(&spim_config->lock, K_FOREVER); + + status = spim_xfer_rx(addr, data, 4, 4 * spim_config->qspi_slave_latency); + + k_sem_give(&spim_config->lock); + + return status; +} + +int spim_hl_read(unsigned int addr, void *data, int len) +{ + int count = 0; + + spim_addr_check(addr, data, len); + + while (count < (len / 4)) { + spim_hl_readw(addr + (4 * count), (char *)data + (4 * count)); + count++; + } + + return 0; +} + +/* ------------------------------added for wifi utils -------------------------------- */ + +int spim_cmd_rpu_wakeup_fn(uint32_t data) +{ + return spim_cmd_rpu_wakeup(data); +} + +int spim_cmd_sleep_rpu_fn(void) +{ + return spim_cmd_sleep_rpu(); +} + +int spim_wait_while_rpu_awake(void) +{ + return _spim_wait_while_rpu_awake(); +} + +int spi_validate_rpu_wake_writecmd(void) +{ + return spim_wait_while_rpu_wake_write(); +} diff --git a/drivers/wifi/nrfwifi/src/shim.c b/drivers/wifi/nrfwifi/src/shim.c new file mode 100644 index 000000000000..209bc1256526 --- /dev/null +++ b/drivers/wifi/nrfwifi/src/shim.c @@ -0,0 +1,969 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing OS specific definitions for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "rpu_hw_if.h" +#include "shim.h" +#include "work.h" +#include "timer.h" +#include "osal_ops.h" +#include "qspi_if.h" + +LOG_MODULE_REGISTER(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL); + +struct zep_shim_intr_priv *intr_priv; + +static void *zep_shim_mem_alloc(size_t size) +{ + size_t size_aligned = ROUND_UP(size, 4); + + return k_malloc(size_aligned); +} + +static void *zep_shim_mem_zalloc(size_t size) +{ + size_t size_aligned = ROUND_UP(size, 4); + + return k_calloc(size_aligned, sizeof(char)); +} + +static void *zep_shim_mem_cpy(void *dest, const void *src, size_t count) +{ + return memcpy(dest, src, count); +} + +static void *zep_shim_mem_set(void *start, int val, size_t size) +{ + return memset(start, val, size); +} + +static int zep_shim_mem_cmp(const void *addr1, + const void *addr2, + size_t size) +{ + return memcmp(addr1, addr2, size); +} + +static unsigned int zep_shim_qspi_read_reg32(void *priv, unsigned long addr) +{ + unsigned int val; + struct zep_shim_bus_qspi_priv *qspi_priv = priv; + struct qspi_dev *dev; + + dev = qspi_priv->qspi_dev; + + if (addr < 0x0C0000) { + dev->hl_read(addr, &val, 4); + } else { + dev->read(addr, &val, 4); + } + + return val; +} + +static void zep_shim_qspi_write_reg32(void *priv, unsigned long addr, unsigned int val) +{ + struct zep_shim_bus_qspi_priv *qspi_priv = priv; + struct qspi_dev *dev; + + dev = qspi_priv->qspi_dev; + + dev->write(addr, &val, 4); +} + +static void zep_shim_qspi_cpy_from(void *priv, void *dest, unsigned long addr, size_t count) +{ + struct zep_shim_bus_qspi_priv *qspi_priv = priv; + struct qspi_dev *dev; + size_t count_aligned = ROUND_UP(count, 4); + + dev = qspi_priv->qspi_dev; + + if (addr < 0x0C0000) { + dev->hl_read(addr, dest, count_aligned); + } else { + dev->read(addr, dest, count_aligned); + } +} + +static void zep_shim_qspi_cpy_to(void *priv, unsigned long addr, const void *src, size_t count) +{ + struct zep_shim_bus_qspi_priv *qspi_priv = priv; + struct qspi_dev *dev; + size_t count_aligned = ROUND_UP(count, 4); + + dev = qspi_priv->qspi_dev; + + dev->write(addr, src, count_aligned); +} + +static void *zep_shim_spinlock_alloc(void) +{ + struct k_sem *lock = NULL; + + lock = k_malloc(sizeof(*lock)); + + if (!lock) { + LOG_ERR("%s: Unable to allocate memory for spinlock", __func__); + } + + return lock; +} + +static void zep_shim_spinlock_free(void *lock) +{ + k_free(lock); +} + +static void zep_shim_spinlock_init(void *lock) +{ + k_sem_init(lock, 1, 1); +} + +static void zep_shim_spinlock_take(void *lock) +{ + k_sem_take(lock, K_FOREVER); +} + +static void zep_shim_spinlock_rel(void *lock) +{ + k_sem_give(lock); +} + +static void zep_shim_spinlock_irq_take(void *lock, unsigned long *flags) +{ + k_sem_take(lock, K_FOREVER); +} + +static void zep_shim_spinlock_irq_rel(void *lock, unsigned long *flags) +{ + k_sem_give(lock); +} + +static int zep_shim_pr_dbg(const char *fmt, va_list args) +{ + static char buf[80]; + + vsnprintf(buf, sizeof(buf), fmt, args); + + LOG_DBG("%s", buf); + + return 0; +} + +static int zep_shim_pr_info(const char *fmt, va_list args) +{ + static char buf[80]; + + vsnprintf(buf, sizeof(buf), fmt, args); + + LOG_INF("%s", buf); + + return 0; +} + +static int zep_shim_pr_err(const char *fmt, va_list args) +{ + static char buf[256]; + + vsnprintf(buf, sizeof(buf), fmt, args); + + LOG_ERR("%s", buf); + + return 0; +} + +struct nwb { + unsigned char *data; + unsigned char *tail; + int len; + int headroom; + void *next; + void *priv; + int iftype; + void *ifaddr; + void *dev; + int hostbuffer; + void *cleanup_ctx; + void (*cleanup_cb)(); + unsigned char priority; + bool chksum_done; +}; + +static void *zep_shim_nbuf_alloc(unsigned int size) +{ + struct nwb *nbuff; + + nbuff = (struct nwb *)k_calloc(sizeof(struct nwb), sizeof(char)); + + if (!nbuff) + return NULL; + + nbuff->priv = k_calloc(size, sizeof(char)); + + if (!nbuff->priv) { + k_free(nbuff); + return NULL; + } + + nbuff->data = (unsigned char *)nbuff->priv; + nbuff->tail = nbuff->data; + nbuff->len = 0; + nbuff->headroom = 0; + nbuff->next = NULL; + + return nbuff; +} + +static void zep_shim_nbuf_free(void *nbuf) +{ + k_free(((struct nwb *)nbuf)->priv); + k_free(nbuf); +} + +static void zep_shim_nbuf_headroom_res(void *nbuf, unsigned int size) +{ + struct nwb *nwb = (struct nwb *)nbuf; + + nwb->data += size; + nwb->tail += size; + nwb->headroom += size; +} + +static unsigned int zep_shim_nbuf_headroom_get(void *nbuf) +{ + return ((struct nwb *)nbuf)->headroom; +} + +static unsigned int zep_shim_nbuf_data_size(void *nbuf) +{ + return ((struct nwb *)nbuf)->len; +} + +static void *zep_shim_nbuf_data_get(void *nbuf) +{ + return ((struct nwb *)nbuf)->data; +} + +static void *zep_shim_nbuf_data_put(void *nbuf, unsigned int size) +{ + struct nwb *nwb = (struct nwb *)nbuf; + unsigned char *data = nwb->tail; + + nwb->tail += size; + nwb->len += size; + + return data; +} + +static void *zep_shim_nbuf_data_push(void *nbuf, unsigned int size) +{ + struct nwb *nwb = (struct nwb *)nbuf; + + nwb->data -= size; + nwb->headroom -= size; + nwb->len += size; + + return nwb->data; +} + +static void *zep_shim_nbuf_data_pull(void *nbuf, unsigned int size) +{ + struct nwb *nwb = (struct nwb *)nbuf; + + nwb->data += size; + nwb->headroom += size; + nwb->len -= size; + + return nwb->data; +} + +static unsigned char zep_shim_nbuf_get_priority(void *nbuf) +{ + struct nwb *nwb = (struct nwb *)nbuf; + + return nwb->priority; +} + +static unsigned char zep_shim_nbuf_get_chksum_done(void *nbuf) +{ + struct nwb *nwb = (struct nwb *)nbuf; + + return nwb->chksum_done; +} + +static void zep_shim_nbuf_set_chksum_done(void *nbuf, unsigned char chksum_done) +{ + struct nwb *nwb = (struct nwb *)nbuf; + + nwb->chksum_done = (bool)chksum_done; +} + +#include +#include + +void *net_pkt_to_nbuf(struct net_pkt *pkt) +{ + struct nwb *nbuff; + unsigned char *data; + unsigned int len; + + len = net_pkt_get_len(pkt); + + nbuff = zep_shim_nbuf_alloc(len + 100); + + if (!nbuff) { + return NULL; + } + + zep_shim_nbuf_headroom_res(nbuff, 100); + + data = zep_shim_nbuf_data_put(nbuff, len); + + net_pkt_read(pkt, data, len); + + nbuff->priority = net_pkt_priority(pkt); + nbuff->chksum_done = (bool)net_pkt_is_chksum_done(pkt); + + return nbuff; +} + +void *net_pkt_from_nbuf(void *iface, void *frm) +{ + struct net_pkt *pkt = NULL; + unsigned char *data; + unsigned int len; + struct nwb *nwb = frm; + + if (!nwb) { + return NULL; + } + + len = zep_shim_nbuf_data_size(nwb); + + data = zep_shim_nbuf_data_get(nwb); + + pkt = net_pkt_rx_alloc_with_buffer(iface, len, AF_UNSPEC, 0, K_MSEC(100)); + + if (!pkt) { + goto out; + } + + if (net_pkt_write(pkt, data, len)) { + net_pkt_unref(pkt); + pkt = NULL; + goto out; + } + +out: + zep_shim_nbuf_free(nwb); + return pkt; +} + +#if defined(CONFIG_NRF70_RAW_DATA_RX) || defined(CONFIG_NRF70_PROMISC_DATA_RX) +void *net_raw_pkt_from_nbuf(void *iface, void *frm, + unsigned short raw_hdr_len, + void *raw_rx_hdr, + bool pkt_free) +{ + struct net_pkt *pkt = NULL; + unsigned char *nwb_data; + unsigned char *data = NULL; + unsigned int nwb_len; + unsigned int total_len; + struct nwb *nwb = frm; + + if (!nwb) { + LOG_ERR("%s: Received network buffer is NULL", __func__); + return NULL; + } + + nwb_len = zep_shim_nbuf_data_size(nwb); + nwb_data = zep_shim_nbuf_data_get(nwb); + total_len = raw_hdr_len + nwb_len; + + data = (unsigned char *)k_malloc(total_len); + if (!data) { + LOG_ERR("%s: Unable to allocate memory for sniffer data packet", __func__); + goto out; + } + + pkt = net_pkt_rx_alloc_with_buffer(iface, total_len, AF_PACKET, ETH_P_ALL, K_MSEC(100)); + if (!pkt) { + LOG_ERR("%s: Unable to allocate net packet buffer", __func__); + goto out; + } + + memcpy(data, raw_rx_hdr, raw_hdr_len); + memcpy((data+raw_hdr_len), nwb_data, nwb_len); + + if (net_pkt_write(pkt, data, total_len)) { + net_pkt_unref(pkt); + pkt = NULL; + goto out; + } +out: + if (data != NULL) { + k_free(data); + } + + if (pkt_free) { + zep_shim_nbuf_free(nwb); + } + + return pkt; +} +#endif /* CONFIG_NRF70_RAW_DATA_RX || CONFIG_NRF70_PROMISC_DATA_RX */ + +static void *zep_shim_llist_node_alloc(void) +{ + struct zep_shim_llist_node *llist_node = NULL; + + llist_node = k_calloc(sizeof(*llist_node), sizeof(char)); + + if (!llist_node) { + LOG_ERR("%s: Unable to allocate memory for linked list node", __func__); + return NULL; + } + + sys_dnode_init(&llist_node->head); + + return llist_node; +} + +static void zep_shim_llist_node_free(void *llist_node) +{ + k_free(llist_node); +} + +static void *zep_shim_llist_node_data_get(void *llist_node) +{ + struct zep_shim_llist_node *zep_llist_node = NULL; + + zep_llist_node = (struct zep_shim_llist_node *)llist_node; + + return zep_llist_node->data; +} + +static void zep_shim_llist_node_data_set(void *llist_node, void *data) +{ + struct zep_shim_llist_node *zep_llist_node = NULL; + + zep_llist_node = (struct zep_shim_llist_node *)llist_node; + + zep_llist_node->data = data; +} + +static void *zep_shim_llist_alloc(void) +{ + struct zep_shim_llist *llist = NULL; + + llist = k_calloc(sizeof(*llist), sizeof(char)); + + if (!llist) { + LOG_ERR("%s: Unable to allocate memory for linked list", __func__); + } + + return llist; +} + +static void zep_shim_llist_free(void *llist) +{ + k_free(llist); +} + +static void zep_shim_llist_init(void *llist) +{ + struct zep_shim_llist *zep_llist = NULL; + + zep_llist = (struct zep_shim_llist *)llist; + + sys_dlist_init(&zep_llist->head); +} + +static void zep_shim_llist_add_node_tail(void *llist, void *llist_node) +{ + struct zep_shim_llist *zep_llist = NULL; + struct zep_shim_llist_node *zep_node = NULL; + + zep_llist = (struct zep_shim_llist *)llist; + zep_node = (struct zep_shim_llist_node *)llist_node; + + sys_dlist_append(&zep_llist->head, &zep_node->head); + + zep_llist->len += 1; +} + +static void zep_shim_llist_add_node_head(void *llist, void *llist_node) +{ + struct zep_shim_llist *zep_llist = NULL; + struct zep_shim_llist_node *zep_node = NULL; + + zep_llist = (struct zep_shim_llist *)llist; + zep_node = (struct zep_shim_llist_node *)llist_node; + + sys_dlist_prepend(&zep_llist->head, &zep_node->head); + + zep_llist->len += 1; +} + +static void *zep_shim_llist_get_node_head(void *llist) +{ + struct zep_shim_llist_node *zep_head_node = NULL; + struct zep_shim_llist *zep_llist = NULL; + + zep_llist = (struct zep_shim_llist *)llist; + + if (!zep_llist->len) { + return NULL; + } + + zep_head_node = (struct zep_shim_llist_node *)sys_dlist_peek_head(&zep_llist->head); + + return zep_head_node; +} + +static void *zep_shim_llist_get_node_nxt(void *llist, void *llist_node) +{ + struct zep_shim_llist_node *zep_node = NULL; + struct zep_shim_llist_node *zep_nxt_node = NULL; + struct zep_shim_llist *zep_llist = NULL; + + zep_llist = (struct zep_shim_llist *)llist; + zep_node = (struct zep_shim_llist_node *)llist_node; + + zep_nxt_node = (struct zep_shim_llist_node *)sys_dlist_peek_next(&zep_llist->head, + &zep_node->head); + + return zep_nxt_node; +} + +static void zep_shim_llist_del_node(void *llist, void *llist_node) +{ + struct zep_shim_llist_node *zep_node = NULL; + struct zep_shim_llist *zep_llist = NULL; + + zep_llist = (struct zep_shim_llist *)llist; + zep_node = (struct zep_shim_llist_node *)llist_node; + + sys_dlist_remove(&zep_node->head); + + zep_llist->len -= 1; +} + +static unsigned int zep_shim_llist_len(void *llist) +{ + struct zep_shim_llist *zep_llist = NULL; + + zep_llist = (struct zep_shim_llist *)llist; + + return zep_llist->len; +} + +static void *zep_shim_work_alloc(int type) +{ + return work_alloc(type); +} + +static void zep_shim_work_free(void *item) +{ + return work_free(item); +} + +static void zep_shim_work_init(void *item, void (*callback)(unsigned long data), + unsigned long data) +{ + work_init(item, callback, data); +} + +static void zep_shim_work_schedule(void *item) +{ + work_schedule(item); +} + +static void zep_shim_work_kill(void *item) +{ + work_kill(item); +} + +static unsigned long zep_shim_time_get_curr_us(void) +{ + return k_uptime_get() * USEC_PER_MSEC; +} + +static unsigned int zep_shim_time_elapsed_us(unsigned long start_time_us) +{ + unsigned long curr_time_us = 0; + + curr_time_us = zep_shim_time_get_curr_us(); + + return curr_time_us - start_time_us; +} + +static enum nrf_wifi_status zep_shim_bus_qspi_dev_init(void *os_qspi_dev_ctx) +{ + ARG_UNUSED(os_qspi_dev_ctx); + + return NRF_WIFI_STATUS_SUCCESS; +} + +static void zep_shim_bus_qspi_dev_deinit(void *priv) +{ + struct zep_shim_bus_qspi_priv *qspi_priv = priv; + volatile struct qspi_dev *dev = qspi_priv->qspi_dev; + + dev->deinit(); +} + +static void *zep_shim_bus_qspi_dev_add(void *os_qspi_priv, void *osal_qspi_dev_ctx) +{ + struct zep_shim_bus_qspi_priv *zep_qspi_priv = os_qspi_priv; + struct qspi_dev *dev = qspi_dev(); + int ret; + enum nrf_wifi_status status; + + ret = rpu_init(); + if (ret) { + LOG_ERR("%s: RPU init failed with error %d", __func__, ret); + return NULL; + } + + status = dev->init(qspi_defconfig()); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: QSPI device init failed", __func__); + return NULL; + } + + ret = rpu_enable(); + if (ret) { + LOG_ERR("%s: RPU enable failed with error %d", __func__, ret); + return NULL; + } + zep_qspi_priv->qspi_dev = dev; + zep_qspi_priv->dev_added = true; + + return zep_qspi_priv; +} + +static void zep_shim_bus_qspi_dev_rem(void *priv) +{ + struct zep_shim_bus_qspi_priv *qspi_priv = priv; + struct qspi_dev *dev = qspi_priv->qspi_dev; + + ARG_UNUSED(dev); + + /* TODO: Make qspi_dev a dynamic instance and remove it here */ + rpu_disable(); +} + +static void *zep_shim_bus_qspi_init(void) +{ + struct zep_shim_bus_qspi_priv *qspi_priv = NULL; + + qspi_priv = k_calloc(sizeof(*qspi_priv), sizeof(char)); + + if (!qspi_priv) { + LOG_ERR("%s: Unable to allocate memory for qspi_priv", __func__); + goto out; + } +out: + return qspi_priv; +} + +static void zep_shim_bus_qspi_deinit(void *os_qspi_priv) +{ + struct zep_shim_bus_qspi_priv *qspi_priv = NULL; + + qspi_priv = os_qspi_priv; + + k_free(qspi_priv); +} + +#ifdef CONFIG_NRF_WIFI_LOW_POWER +static int zep_shim_bus_qspi_ps_sleep(void *os_qspi_priv) +{ + rpu_sleep(); + + return 0; +} + +static int zep_shim_bus_qspi_ps_wake(void *os_qspi_priv) +{ + rpu_wakeup(); + + return 0; +} + +static int zep_shim_bus_qspi_ps_status(void *os_qspi_priv) +{ + return rpu_sleep_status(); +} +#endif /* CONFIG_NRF_WIFI_LOW_POWER */ + +static void zep_shim_bus_qspi_dev_host_map_get(void *os_qspi_dev_ctx, + struct nrf_wifi_osal_host_map *host_map) +{ + if (!os_qspi_dev_ctx || !host_map) { + LOG_ERR("%s: Invalid parameters", __func__); + return; + } + + host_map->addr = 0; +} + +static void irq_work_handler(struct k_work *work) +{ + int ret = 0; + + ret = intr_priv->callbk_fn(intr_priv->callbk_data); + + if (ret) { + LOG_ERR("%s: Interrupt callback failed", __func__); + } +} + + +extern struct k_work_q zep_wifi_intr_q; + +static void zep_shim_irq_handler(const struct device *dev, struct gpio_callback *cb, uint32_t pins) +{ + ARG_UNUSED(cb); + ARG_UNUSED(pins); + + k_work_schedule_for_queue(&zep_wifi_intr_q, &intr_priv->work, K_NO_WAIT); +} + +static enum nrf_wifi_status zep_shim_bus_qspi_intr_reg(void *os_dev_ctx, void *callbk_data, + int (*callbk_fn)(void *callbk_data)) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int ret = -1; + + ARG_UNUSED(os_dev_ctx); + + intr_priv = k_calloc(sizeof(*intr_priv), sizeof(char)); + + if (!intr_priv) { + LOG_ERR("%s: Unable to allocate memory for intr_priv", __func__); + goto out; + } + + intr_priv->callbk_data = callbk_data; + intr_priv->callbk_fn = callbk_fn; + + k_work_init_delayable(&intr_priv->work, irq_work_handler); + + ret = rpu_irq_config(&intr_priv->gpio_cb_data, zep_shim_irq_handler); + + if (ret) { + LOG_ERR("%s: request_irq failed", __func__); + k_free(intr_priv); + intr_priv = NULL; + goto out; + } + + status = NRF_WIFI_STATUS_SUCCESS; + +out: + return status; +} + +static void zep_shim_bus_qspi_intr_unreg(void *os_qspi_dev_ctx) +{ + struct k_work_sync sync; + int ret; + + ARG_UNUSED(os_qspi_dev_ctx); + + ret = rpu_irq_remove(&intr_priv->gpio_cb_data); + if (ret) { + LOG_ERR("%s: rpu_irq_remove failed", __func__); + return; + } + + k_work_cancel_delayable_sync(&intr_priv->work, &sync); + + k_free(intr_priv); + intr_priv = NULL; +} + +#ifdef CONFIG_NRF_WIFI_LOW_POWER +static void *zep_shim_timer_alloc(void) +{ + struct timer_list *timer = NULL; + + timer = k_malloc(sizeof(*timer)); + + if (!timer) + LOG_ERR("%s: Unable to allocate memory for work", __func__); + + return timer; +} + +static void zep_shim_timer_init(void *timer, void (*callback)(unsigned long), unsigned long data) +{ + ((struct timer_list *)timer)->function = callback; + ((struct timer_list *)timer)->data = data; + + init_timer(timer); +} + +static void zep_shim_timer_free(void *timer) +{ + k_free(timer); +} + +static void zep_shim_timer_schedule(void *timer, unsigned long duration) +{ + mod_timer(timer, duration); +} + +static void zep_shim_timer_kill(void *timer) +{ + del_timer_sync(timer); +} +#endif /* CONFIG_NRF_WIFI_LOW_POWER */ + +static void zep_shim_assert(int test_val, int val, enum nrf_wifi_assert_op_type op, char *msg) +{ + switch (op) { + case NRF_WIFI_ASSERT_EQUAL_TO: + NET_ASSERT(test_val == val, "%s", msg); + break; + case NRF_WIFI_ASSERT_NOT_EQUAL_TO: + NET_ASSERT(test_val != val, "%s", msg); + break; + case NRF_WIFI_ASSERT_LESS_THAN: + NET_ASSERT(test_val < val, "%s", msg); + break; + case NRF_WIFI_ASSERT_LESS_THAN_EQUAL_TO: + NET_ASSERT(test_val <= val, "%s", msg); + break; + case NRF_WIFI_ASSERT_GREATER_THAN: + NET_ASSERT(test_val > val, "%s", msg); + break; + case NRF_WIFI_ASSERT_GREATER_THAN_EQUAL_TO: + NET_ASSERT(test_val >= val, "%s", msg); + break; + default: + LOG_ERR("%s: Invalid assertion operation", __func__); + } +} + +static unsigned int zep_shim_strlen(const void *str) +{ + return strlen(str); +} + +static const struct nrf_wifi_osal_ops nrf_wifi_os_zep_ops = { + .mem_alloc = zep_shim_mem_alloc, + .mem_zalloc = zep_shim_mem_zalloc, + .mem_free = k_free, + .mem_cpy = zep_shim_mem_cpy, + .mem_set = zep_shim_mem_set, + .mem_cmp = zep_shim_mem_cmp, + + .qspi_read_reg32 = zep_shim_qspi_read_reg32, + .qspi_write_reg32 = zep_shim_qspi_write_reg32, + .qspi_cpy_from = zep_shim_qspi_cpy_from, + .qspi_cpy_to = zep_shim_qspi_cpy_to, + + .spinlock_alloc = zep_shim_spinlock_alloc, + .spinlock_free = zep_shim_spinlock_free, + .spinlock_init = zep_shim_spinlock_init, + .spinlock_take = zep_shim_spinlock_take, + .spinlock_rel = zep_shim_spinlock_rel, + + .spinlock_irq_take = zep_shim_spinlock_irq_take, + .spinlock_irq_rel = zep_shim_spinlock_irq_rel, + + .log_dbg = zep_shim_pr_dbg, + .log_info = zep_shim_pr_info, + .log_err = zep_shim_pr_err, + + .llist_node_alloc = zep_shim_llist_node_alloc, + .llist_node_free = zep_shim_llist_node_free, + .llist_node_data_get = zep_shim_llist_node_data_get, + .llist_node_data_set = zep_shim_llist_node_data_set, + + .llist_alloc = zep_shim_llist_alloc, + .llist_free = zep_shim_llist_free, + .llist_init = zep_shim_llist_init, + .llist_add_node_tail = zep_shim_llist_add_node_tail, + .llist_add_node_head = zep_shim_llist_add_node_head, + .llist_get_node_head = zep_shim_llist_get_node_head, + .llist_get_node_nxt = zep_shim_llist_get_node_nxt, + .llist_del_node = zep_shim_llist_del_node, + .llist_len = zep_shim_llist_len, + + .nbuf_alloc = zep_shim_nbuf_alloc, + .nbuf_free = zep_shim_nbuf_free, + .nbuf_headroom_res = zep_shim_nbuf_headroom_res, + .nbuf_headroom_get = zep_shim_nbuf_headroom_get, + .nbuf_data_size = zep_shim_nbuf_data_size, + .nbuf_data_get = zep_shim_nbuf_data_get, + .nbuf_data_put = zep_shim_nbuf_data_put, + .nbuf_data_push = zep_shim_nbuf_data_push, + .nbuf_data_pull = zep_shim_nbuf_data_pull, + .nbuf_get_priority = zep_shim_nbuf_get_priority, + .nbuf_get_chksum_done = zep_shim_nbuf_get_chksum_done, + .nbuf_set_chksum_done = zep_shim_nbuf_set_chksum_done, + + .tasklet_alloc = zep_shim_work_alloc, + .tasklet_free = zep_shim_work_free, + .tasklet_init = zep_shim_work_init, + .tasklet_schedule = zep_shim_work_schedule, + .tasklet_kill = zep_shim_work_kill, + + .sleep_ms = k_msleep, + .delay_us = k_usleep, + .time_get_curr_us = zep_shim_time_get_curr_us, + .time_elapsed_us = zep_shim_time_elapsed_us, + + .bus_qspi_init = zep_shim_bus_qspi_init, + .bus_qspi_deinit = zep_shim_bus_qspi_deinit, + .bus_qspi_dev_add = zep_shim_bus_qspi_dev_add, + .bus_qspi_dev_rem = zep_shim_bus_qspi_dev_rem, + .bus_qspi_dev_init = zep_shim_bus_qspi_dev_init, + .bus_qspi_dev_deinit = zep_shim_bus_qspi_dev_deinit, + .bus_qspi_dev_intr_reg = zep_shim_bus_qspi_intr_reg, + .bus_qspi_dev_intr_unreg = zep_shim_bus_qspi_intr_unreg, + .bus_qspi_dev_host_map_get = zep_shim_bus_qspi_dev_host_map_get, + +#ifdef CONFIG_NRF_WIFI_LOW_POWER + .timer_alloc = zep_shim_timer_alloc, + .timer_init = zep_shim_timer_init, + .timer_free = zep_shim_timer_free, + .timer_schedule = zep_shim_timer_schedule, + .timer_kill = zep_shim_timer_kill, + + .bus_qspi_ps_sleep = zep_shim_bus_qspi_ps_sleep, + .bus_qspi_ps_wake = zep_shim_bus_qspi_ps_wake, + .bus_qspi_ps_status = zep_shim_bus_qspi_ps_status, +#endif /* CONFIG_NRF_WIFI_LOW_POWER */ + + .assert = zep_shim_assert, + .strlen = zep_shim_strlen, +}; + +const struct nrf_wifi_osal_ops *get_os_ops(void) +{ + return &nrf_wifi_os_zep_ops; +} diff --git a/drivers/wifi/nrfwifi/src/shim.h b/drivers/wifi/nrfwifi/src/shim.h new file mode 100644 index 000000000000..a4162bf89314 --- /dev/null +++ b/drivers/wifi/nrfwifi/src/shim.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing OS interface specific declarations for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#ifndef __SHIM_H__ +#define __SHIM_H__ + +#include +#include +#include + +/** + * struct zep_shim_bus_qspi_priv - Structure to hold context information for the Linux OS + * shim. + * @opriv: Pointer to OSAL context. + * @pcie_callbk_data: Callback data to be passed to the PCIe callback functions. + * @pcie_prb_callbk: The callback function to be called when a PCIe device + * has been probed. + * @pcie_rem_callbk: The callback function to be called when a PCIe device + * has been removed. + * + * This structure maintains the context information necessary for the operation + * of the Linux shim. Some of the elements of the structure need to be + * initialized during the initialization of the Linux shim while others need to + * be kept updated over the duration of the Linux shim operation. + */ +struct zep_shim_bus_qspi_priv { + void *qspi_dev; + + bool dev_added; + bool dev_init; +}; + +struct zep_shim_intr_priv { + struct gpio_callback gpio_cb_data; + void *callbk_data; + int (*callbk_fn)(void *callbk_data); + struct k_work_delayable work; +}; + +struct zep_shim_llist_node { + sys_dnode_t head; + void *data; +}; + +struct zep_shim_llist { + sys_dlist_t head; + unsigned int len; +}; + +void *net_pkt_to_nbuf(struct net_pkt *pkt); +void *net_pkt_from_nbuf(void *iface, void *frm); +#if defined(CONFIG_NRF70_RAW_DATA_RX) || defined(CONFIG_NRF70_PROMISC_DATA_RX) +void *net_raw_pkt_from_nbuf(void *iface, + void *frm, + unsigned short raw_hdr_len, + void *raw_rx_hdr, + bool pkt_free); +#endif /* CONFIG_NRF70_RAW_DATA_RX || CONFIG_NRF70_PROMISC_DATA_RX */ + +#endif /* __SHIM_H__ */ diff --git a/drivers/wifi/nrfwifi/src/timer.c b/drivers/wifi/nrfwifi/src/timer.c new file mode 100644 index 000000000000..c5928b901f85 --- /dev/null +++ b/drivers/wifi/nrfwifi/src/timer.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief File containing timer specific definitons for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#include +#include + +#include +#include +#include + +#include "timer.h" + +static void timer_expiry_function(struct k_work *work) +{ + struct timer_list *timer; + + timer = (struct timer_list *)CONTAINER_OF(work, struct timer_list, work.work); + + timer->function(timer->data); +} + +void init_timer(struct timer_list *timer) +{ + k_work_init_delayable(&timer->work, timer_expiry_function); +} + +void mod_timer(struct timer_list *timer, int msec) +{ + k_work_schedule(&timer->work, K_MSEC(msec)); +} + +void del_timer_sync(struct timer_list *timer) +{ + k_work_cancel_delayable(&timer->work); +} diff --git a/drivers/wifi/nrfwifi/src/timer.h b/drivers/wifi/nrfwifi/src/timer.h new file mode 100644 index 000000000000..cb0fc6ff53bf --- /dev/null +++ b/drivers/wifi/nrfwifi/src/timer.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing timer specific declarations for the + * Zephyr OS layer of the Wi-Fi driver. + */ +#ifndef __TIMER_H__ +#define __TIMER_H__ + +struct timer_list { + void (*function)(unsigned long data); + unsigned long data; + struct k_work_delayable work; +}; + +void init_timer(struct timer_list *timer); + +void mod_timer(struct timer_list *timer, int msec); + +void del_timer_sync(struct timer_list *timer); + +#endif /* __TIMER_H__ */ diff --git a/drivers/wifi/nrfwifi/src/wifi_mgmt.c b/drivers/wifi/nrfwifi/src/wifi_mgmt.c new file mode 100644 index 000000000000..2d67e5695123 --- /dev/null +++ b/drivers/wifi/nrfwifi/src/wifi_mgmt.c @@ -0,0 +1,1021 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief File containing WiFi management operation implementations + * for the Zephyr OS. + */ + +#include + +#include +#include + +#include "util.h" +#include "fmac_api.h" +#include "fmac_tx.h" +#include "fmac_util.h" +#include "fmac_main.h" +#include "wifi_mgmt.h" + +LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL); + +extern struct nrf_wifi_drv_priv_zep rpu_drv_priv_zep; + +int nrf_wifi_set_power_save(const struct device *dev, + struct wifi_ps_params *params) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + int ret = -1; + unsigned int uapsd_queue = UAPSD_Q_MIN; /* Legacy mode */ + + if (!dev || !params) { + LOG_ERR("%s: dev or params is NULL", __func__); + return ret; + } + + vif_ctx_zep = dev->data; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return ret; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + switch (params->type) { + case WIFI_PS_PARAM_LISTEN_INTERVAL: + if ((params->listen_interval < + NRF_WIFI_LISTEN_INTERVAL_MIN) || + (params->listen_interval > + WIFI_LISTEN_INTERVAL_MAX)) { + params->fail_reason = + WIFI_PS_PARAM_LISTEN_INTERVAL_RANGE_INVALID; + return -EINVAL; + } + status = nrf_wifi_fmac_set_listen_interval( + rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + params->listen_interval); + break; + case WIFI_PS_PARAM_TIMEOUT: + if ((vif_ctx_zep->if_type != NRF_WIFI_IFTYPE_STATION) +#ifdef CONFIG_NRF70_RAW_DATA_TX + && (vif_ctx_zep->if_type != NRF_WIFI_STA_TX_INJECTOR) +#endif /* CONFIG_NRF70_RAW_DATA_TX */ +#ifdef CONFIG_NRF70_PROMISC_DATA_RX + && (vif_ctx_zep->if_type != NRF_WIFI_STA_PROMISC_TX_INJECTOR) +#endif /* CONFIG_NRF70_PROMISC_DATA_RX */ + ) { + LOG_ERR("%s: Operation supported only in STA enabled mode", + __func__); + params->fail_reason = + WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL; + goto out; + } + + status = nrf_wifi_fmac_set_power_save_timeout( + rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + params->timeout_ms); + break; + case WIFI_PS_PARAM_MODE: + if (params->mode == WIFI_PS_MODE_WMM) { + uapsd_queue = UAPSD_Q_MAX; /* WMM mode */ + } + + status = nrf_wifi_fmac_set_uapsd_queue(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + uapsd_queue); + break; + case WIFI_PS_PARAM_STATE: + status = nrf_wifi_fmac_set_power_save(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + params->enabled); + break; + case WIFI_PS_PARAM_WAKEUP_MODE: + status = nrf_wifi_fmac_set_ps_wakeup_mode( + rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + params->wakeup_mode); + break; + default: + params->fail_reason = + WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL; + return -ENOTSUP; + } + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Confiuring PS param %d failed", + __func__, params->type); + params->fail_reason = + WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL; + goto out; + } + + + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_get_power_save_config(const struct device *dev, + struct wifi_ps_config *ps_config) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + int ret = -1; + int count = 0; + + if (!dev || !ps_config) { + return ret; + } + + vif_ctx_zep = dev->data; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return ret; + } + + if ((vif_ctx_zep->if_type != NRF_WIFI_IFTYPE_STATION) +#ifdef CONFIG_NRF70_RAW_DATA_TX + && (vif_ctx_zep->if_type != NRF_WIFI_STA_TX_INJECTOR) +#endif /* CONFIG_NRF70_RAW_DATA_TX */ +#ifdef CONFIG_NRF70_PROMISC_DATA_RX + && (vif_ctx_zep->if_type != NRF_WIFI_STA_PROMISC_TX_INJECTOR) +#endif /* CONFIG_NRF70_PROMISC_DATA_RX */ + ) { + LOG_ERR("%s: Operation supported only in STA enabled mode", + __func__); + return ret; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + goto out; + } + + vif_ctx_zep->ps_info = ps_config; + + vif_ctx_zep->ps_config_info_evnt = false; + + status = nrf_wifi_fmac_get_power_save_info(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_get_power_save_info failed", + __func__); + goto out; + } + + do { + nrf_wifi_osal_sleep_ms(fmac_dev_ctx->fpriv->opriv, + 1); + count++; + } while ((vif_ctx_zep->ps_config_info_evnt == false) && + (count < NRF_WIFI_FMAC_PS_CONF_EVNT_RECV_TIMEOUT)); + + if (count == NRF_WIFI_FMAC_PS_CONF_EVNT_RECV_TIMEOUT) { + nrf_wifi_osal_log_err(fmac_dev_ctx->fpriv->opriv, + "%s: Timed out", + __func__); + goto out; + } + + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +/* TWT interval conversion helpers: User <-> Protocol */ +static struct twt_interval_float nrf_wifi_twt_us_to_float(uint32_t twt_interval) +{ + double mantissa = 0.0; + int exponent = 0; + struct twt_interval_float twt_interval_fp; + + double twt_interval_ms = twt_interval / 1000.0; + + mantissa = frexp(twt_interval_ms, &exponent); + /* Ceiling and conversion to milli seconds */ + twt_interval_fp.mantissa = ceil(mantissa * 1000); + twt_interval_fp.exponent = exponent; + + return twt_interval_fp; +} + +static uint64_t nrf_wifi_twt_float_to_us(struct twt_interval_float twt_interval_fp) +{ + /* Conversion to micro-seconds */ + return floor(ldexp(twt_interval_fp.mantissa, twt_interval_fp.exponent) / (1000)) * + 1000; +} + +static unsigned char twt_wifi_mgmt_to_rpu_neg_type(enum wifi_twt_negotiation_type neg_type) +{ + unsigned char rpu_neg_type = 0; + + switch (neg_type) { + case WIFI_TWT_INDIVIDUAL: + rpu_neg_type = NRF_WIFI_TWT_NEGOTIATION_TYPE_INDIVIDUAL; + break; + case WIFI_TWT_BROADCAST: + rpu_neg_type = NRF_WIFI_TWT_NEGOTIATION_TYPE_BROADCAST; + break; + default: + LOG_ERR("%s: Invalid negotiation type: %d", + __func__, neg_type); + break; + } + + return rpu_neg_type; +} + +static enum wifi_twt_negotiation_type twt_rpu_to_wifi_mgmt_neg_type(unsigned char neg_type) +{ + enum wifi_twt_negotiation_type wifi_neg_type = WIFI_TWT_INDIVIDUAL; + + switch (neg_type) { + case NRF_WIFI_TWT_NEGOTIATION_TYPE_INDIVIDUAL: + wifi_neg_type = WIFI_TWT_INDIVIDUAL; + break; + case NRF_WIFI_TWT_NEGOTIATION_TYPE_BROADCAST: + wifi_neg_type = WIFI_TWT_BROADCAST; + break; + default: + LOG_ERR("%s: Invalid negotiation type: %d", + __func__, neg_type); + break; + } + + return wifi_neg_type; +} + +/* Though setup_cmd enums have 1-1 mapping but due to data type different need these */ +static enum wifi_twt_setup_cmd twt_rpu_to_wifi_mgmt_setup_cmd(signed int setup_cmd) +{ + enum wifi_twt_setup_cmd wifi_setup_cmd = WIFI_TWT_SETUP_CMD_REQUEST; + + switch (setup_cmd) { + case NRF_WIFI_REQUEST_TWT: + wifi_setup_cmd = WIFI_TWT_SETUP_CMD_REQUEST; + break; + case NRF_WIFI_SUGGEST_TWT: + wifi_setup_cmd = WIFI_TWT_SETUP_CMD_SUGGEST; + break; + case NRF_WIFI_DEMAND_TWT: + wifi_setup_cmd = WIFI_TWT_SETUP_CMD_DEMAND; + break; + case NRF_WIFI_GROUPING_TWT: + wifi_setup_cmd = WIFI_TWT_SETUP_CMD_GROUPING; + break; + case NRF_WIFI_ACCEPT_TWT: + wifi_setup_cmd = WIFI_TWT_SETUP_CMD_ACCEPT; + break; + case NRF_WIFI_ALTERNATE_TWT: + wifi_setup_cmd = WIFI_TWT_SETUP_CMD_ALTERNATE; + break; + case NRF_WIFI_DICTATE_TWT: + wifi_setup_cmd = WIFI_TWT_SETUP_CMD_DICTATE; + break; + case NRF_WIFI_REJECT_TWT: + wifi_setup_cmd = WIFI_TWT_SETUP_CMD_REJECT; + break; + default: + LOG_ERR("%s: Invalid setup command: %d", + __func__, setup_cmd); + break; + } + + return wifi_setup_cmd; +} + +static signed int twt_wifi_mgmt_to_rpu_setup_cmd(enum wifi_twt_setup_cmd setup_cmd) +{ + signed int rpu_setup_cmd = NRF_WIFI_REQUEST_TWT; + + switch (setup_cmd) { + case WIFI_TWT_SETUP_CMD_REQUEST: + rpu_setup_cmd = NRF_WIFI_REQUEST_TWT; + break; + case WIFI_TWT_SETUP_CMD_SUGGEST: + rpu_setup_cmd = NRF_WIFI_SUGGEST_TWT; + break; + case WIFI_TWT_SETUP_CMD_DEMAND: + rpu_setup_cmd = NRF_WIFI_DEMAND_TWT; + break; + case WIFI_TWT_SETUP_CMD_GROUPING: + rpu_setup_cmd = NRF_WIFI_GROUPING_TWT; + break; + case WIFI_TWT_SETUP_CMD_ACCEPT: + rpu_setup_cmd = NRF_WIFI_ACCEPT_TWT; + break; + case WIFI_TWT_SETUP_CMD_ALTERNATE: + rpu_setup_cmd = NRF_WIFI_ALTERNATE_TWT; + break; + case WIFI_TWT_SETUP_CMD_DICTATE: + rpu_setup_cmd = NRF_WIFI_DICTATE_TWT; + break; + case WIFI_TWT_SETUP_CMD_REJECT: + rpu_setup_cmd = NRF_WIFI_REJECT_TWT; + break; + default: + LOG_ERR("%s: Invalid setup command: %d", + __func__, setup_cmd); + break; + } + + return rpu_setup_cmd; +} + +void nrf_wifi_event_proc_get_power_save_info(void *vif_ctx, + struct nrf_wifi_umac_event_power_save_info *ps_info, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + if (!vif_ctx || !ps_info) { + return; + } + + vif_ctx_zep = vif_ctx; + + vif_ctx_zep->ps_info->ps_params.mode = ps_info->ps_mode; + vif_ctx_zep->ps_info->ps_params.enabled = ps_info->enabled; + vif_ctx_zep->ps_info->num_twt_flows = ps_info->num_twt_flows; + vif_ctx_zep->ps_info->ps_params.timeout_ms = ps_info->ps_timeout; + vif_ctx_zep->ps_info->ps_params.listen_interval = ps_info->listen_interval; + vif_ctx_zep->ps_info->ps_params.wakeup_mode = ps_info->extended_ps; + + for (int i = 0; i < ps_info->num_twt_flows; i++) { + struct twt_interval_float twt_interval_fp; + struct wifi_twt_flow_info *twt_zep = &vif_ctx_zep->ps_info->twt_flows[i]; + struct nrf_wifi_umac_config_twt_info *twt_rpu = &ps_info->twt_flow_info[i]; + + memset(twt_zep, 0, sizeof(struct wifi_twt_flow_info)); + + twt_zep->flow_id = twt_rpu->twt_flow_id; + twt_zep->implicit = twt_rpu->is_implicit ? 1 : 0; + twt_zep->trigger = twt_rpu->ap_trigger_frame ? 1 : 0; + twt_zep->announce = twt_rpu->twt_flow_type == NRF_WIFI_TWT_FLOW_TYPE_ANNOUNCED; + twt_zep->negotiation_type = twt_rpu_to_wifi_mgmt_neg_type(twt_rpu->neg_type); + twt_zep->dialog_token = twt_rpu->dialog_token; + twt_interval_fp.mantissa = twt_rpu->twt_target_wake_interval_mantissa; + twt_interval_fp.exponent = twt_rpu->twt_target_wake_interval_exponent; + twt_zep->twt_interval = nrf_wifi_twt_float_to_us(twt_interval_fp); + twt_zep->twt_wake_interval = twt_rpu->nominal_min_twt_wake_duration; + } + + vif_ctx_zep->ps_config_info_evnt = true; +} + +static void nrf_wifi_twt_update_internal_state(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep, + bool setup, unsigned char flow_id) +{ + if (setup) { + vif_ctx_zep->twt_flows_map |= BIT(flow_id); + vif_ctx_zep->twt_flow_in_progress_map &= ~BIT(flow_id); + } else { + vif_ctx_zep->twt_flows_map &= ~BIT(flow_id); + } +} + +int nrf_wifi_twt_teardown_flows(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep, + unsigned char start_flow_id, unsigned char end_flow_id) +{ + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_umac_config_twt_info twt_info = {0}; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int ret = 0; + struct wifi_twt_params twt_params = {0}; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return ret; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + for (int flow_id = start_flow_id; flow_id < end_flow_id; flow_id++) { + if (!(vif_ctx_zep->twt_flows_map & BIT(flow_id))) { + continue; + } + twt_info.twt_flow_id = flow_id; + status = nrf_wifi_fmac_twt_teardown(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + &twt_info); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: TWT teardown for flow id %d failed", + __func__, flow_id); + ret = -1; + continue; + } + /* UMAC doesn't send TWT teardown event for host initiated teardown */ + nrf_wifi_twt_update_internal_state(vif_ctx_zep, false, flow_id); + /* TODO: Remove this once UMAC sends the status */ + twt_params.operation = WIFI_TWT_TEARDOWN; + twt_params.flow_id = flow_id; + twt_params.teardown_status = WIFI_TWT_TEARDOWN_SUCCESS; + wifi_mgmt_raise_twt_event(vif_ctx_zep->zep_net_if_ctx, &twt_params); + } + +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_set_twt(const struct device *dev, + struct wifi_twt_params *twt_params) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_umac_config_twt_info twt_info = {0}; + int ret = -1; + + if (!dev || !twt_params) { + LOG_ERR("%s: dev or twt_params is NULL", __func__); + return ret; + } + + vif_ctx_zep = dev->data; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return ret; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + if (!(twt_params->operation == WIFI_TWT_TEARDOWN && twt_params->teardown.teardown_all) && + twt_params->flow_id >= WIFI_MAX_TWT_FLOWS) { + LOG_ERR("%s: Invalid flow id: %d", + __func__, twt_params->flow_id); + twt_params->fail_reason = WIFI_TWT_FAIL_INVALID_FLOW_ID; + goto out; + } + + switch (twt_params->operation) { + case WIFI_TWT_SETUP: + if (vif_ctx_zep->twt_flow_in_progress_map & BIT(twt_params->flow_id)) { + twt_params->fail_reason = WIFI_TWT_FAIL_OPERATION_IN_PROGRESS; + goto out; + } + + if (twt_params->setup_cmd == WIFI_TWT_SETUP_CMD_REQUEST) { + if (vif_ctx_zep->twt_flows_map & BIT(twt_params->flow_id)) { + twt_params->fail_reason = WIFI_TWT_FAIL_FLOW_ALREADY_EXISTS; + goto out; + } + } + + struct twt_interval_float twt_interval_fp = + nrf_wifi_twt_us_to_float(twt_params->setup.twt_interval); + + twt_info.twt_flow_id = twt_params->flow_id; + twt_info.neg_type = twt_wifi_mgmt_to_rpu_neg_type(twt_params->negotiation_type); + twt_info.setup_cmd = twt_wifi_mgmt_to_rpu_setup_cmd(twt_params->setup_cmd); + twt_info.ap_trigger_frame = twt_params->setup.trigger; + twt_info.is_implicit = twt_params->setup.implicit; + if (twt_params->setup.announce) { + twt_info.twt_flow_type = NRF_WIFI_TWT_FLOW_TYPE_ANNOUNCED; + } else { + twt_info.twt_flow_type = NRF_WIFI_TWT_FLOW_TYPE_UNANNOUNCED; + } + + twt_info.nominal_min_twt_wake_duration = + twt_params->setup.twt_wake_interval; + twt_info.twt_target_wake_interval_mantissa = twt_interval_fp.mantissa; + twt_info.twt_target_wake_interval_exponent = twt_interval_fp.exponent; + + twt_info.dialog_token = twt_params->dialog_token; + twt_info.twt_wake_ahead_duration = twt_params->setup.twt_wake_ahead_duration; + + status = nrf_wifi_fmac_twt_setup(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + &twt_info); + + break; + case WIFI_TWT_TEARDOWN: + unsigned char start_flow_id = 0; + unsigned char end_flow_id = WIFI_MAX_TWT_FLOWS; + + if (!twt_params->teardown.teardown_all) { + if (!(vif_ctx_zep->twt_flows_map & BIT(twt_params->flow_id))) { + twt_params->fail_reason = WIFI_TWT_FAIL_INVALID_FLOW_ID; + goto out; + } + start_flow_id = twt_params->flow_id; + end_flow_id = twt_params->flow_id + 1; + twt_info.twt_flow_id = twt_params->flow_id; + } + + status = nrf_wifi_twt_teardown_flows(vif_ctx_zep, + start_flow_id, + end_flow_id); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: TWT teardown failed: start_flow_id: %d, end_flow_id: %d", + __func__, start_flow_id, end_flow_id); + goto out; + } + break; + + default: + LOG_ERR("Unknown TWT operation"); + status = NRF_WIFI_STATUS_FAIL; + break; + } + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Failed", __func__); + goto out; + } + + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +void nrf_wifi_event_proc_twt_setup_zep(void *vif_ctx, + struct nrf_wifi_umac_cmd_config_twt *twt_setup_info, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct wifi_twt_params twt_params; + struct twt_interval_float twt_interval_fp; + + if (!vif_ctx || !twt_setup_info) { + return; + } + + vif_ctx_zep = vif_ctx; + + twt_params.operation = WIFI_TWT_SETUP; + twt_params.flow_id = twt_setup_info->info.twt_flow_id; + twt_params.negotiation_type = twt_rpu_to_wifi_mgmt_neg_type(twt_setup_info->info.neg_type); + twt_params.setup_cmd = twt_rpu_to_wifi_mgmt_setup_cmd(twt_setup_info->info.setup_cmd); + twt_params.setup.trigger = twt_setup_info->info.ap_trigger_frame ? 1 : 0; + twt_params.setup.implicit = twt_setup_info->info.is_implicit ? 1 : 0; + twt_params.setup.announce = + twt_setup_info->info.twt_flow_type == NRF_WIFI_TWT_FLOW_TYPE_ANNOUNCED; + twt_params.setup.twt_wake_interval = + twt_setup_info->info.nominal_min_twt_wake_duration; + twt_interval_fp.mantissa = twt_setup_info->info.twt_target_wake_interval_mantissa; + twt_interval_fp.exponent = twt_setup_info->info.twt_target_wake_interval_exponent; + twt_params.setup.twt_interval = nrf_wifi_twt_float_to_us(twt_interval_fp); + twt_params.dialog_token = twt_setup_info->info.dialog_token; + twt_params.resp_status = twt_setup_info->info.twt_resp_status; + + if ((twt_setup_info->info.twt_resp_status == 0) || + (twt_setup_info->info.neg_type == NRF_WIFI_ACCEPT_TWT)) { + nrf_wifi_twt_update_internal_state(vif_ctx_zep, true, twt_params.flow_id); + } + + wifi_mgmt_raise_twt_event(vif_ctx_zep->zep_net_if_ctx, &twt_params); +} + + +void nrf_wifi_event_proc_twt_teardown_zep(void *vif_ctx, + struct nrf_wifi_umac_cmd_teardown_twt *twt_teardown_info, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct wifi_twt_params twt_params = {0}; + + if (!vif_ctx || !twt_teardown_info) { + return; + } + + vif_ctx_zep = vif_ctx; + + twt_params.operation = WIFI_TWT_TEARDOWN; + twt_params.flow_id = twt_teardown_info->info.twt_flow_id; + /* TODO: ADD reason code in the twt_params structure */ + nrf_wifi_twt_update_internal_state(vif_ctx_zep, false, twt_params.flow_id); + + wifi_mgmt_raise_twt_event(vif_ctx_zep->zep_net_if_ctx, &twt_params); +} + +void nrf_wifi_event_proc_twt_sleep_zep(void *vif_ctx, + struct nrf_wifi_umac_event_twt_sleep *sleep_evnt, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + struct nrf_wifi_fmac_priv_def *def_priv = NULL; +#ifdef CONFIG_NRF70_DATA_TX + int desc = 0; + int ac = 0; +#endif + vif_ctx_zep = vif_ctx; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); + def_priv = wifi_fmac_priv(fmac_dev_ctx->fpriv); + + if (!sleep_evnt) { + LOG_ERR("%s: sleep_evnt is NULL", __func__); + return; + } + + switch (sleep_evnt->info.type) { + case TWT_BLOCK_TX: + nrf_wifi_osal_spinlock_take(fmac_dev_ctx->fpriv->opriv, + def_dev_ctx->tx_config.tx_lock); + + def_dev_ctx->twt_sleep_status = NRF_WIFI_FMAC_TWT_STATE_SLEEP; + + wifi_mgmt_raise_twt_sleep_state(vif_ctx_zep->zep_net_if_ctx, + WIFI_TWT_STATE_SLEEP); + nrf_wifi_osal_spinlock_rel(fmac_dev_ctx->fpriv->opriv, + def_dev_ctx->tx_config.tx_lock); + break; + case TWT_UNBLOCK_TX: + nrf_wifi_osal_spinlock_take(fmac_dev_ctx->fpriv->opriv, + def_dev_ctx->tx_config.tx_lock); + def_dev_ctx->twt_sleep_status = NRF_WIFI_FMAC_TWT_STATE_AWAKE; + wifi_mgmt_raise_twt_sleep_state(vif_ctx_zep->zep_net_if_ctx, + WIFI_TWT_STATE_AWAKE); +#ifdef CONFIG_NRF70_DATA_TX + for (ac = NRF_WIFI_FMAC_AC_BE; + ac <= NRF_WIFI_FMAC_AC_MAX; ++ac) { + desc = tx_desc_get(fmac_dev_ctx, ac); + if (desc < def_priv->num_tx_tokens) { + tx_pending_process(fmac_dev_ctx, desc, ac); + } + } +#endif + nrf_wifi_osal_spinlock_rel(fmac_dev_ctx->fpriv->opriv, + def_dev_ctx->tx_config.tx_lock); + break; + default: + break; + } +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); +} + +#ifdef CONFIG_NRF70_SYSTEM_WITH_RAW_MODES +int nrf_wifi_mode(const struct device *dev, + struct wifi_mode_info *mode) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + int ret = -1; + + if (!dev || !mode) { + LOG_ERR("%s: illegal input parameters", __func__); + return ret; + } + + vif_ctx_zep = dev->data; + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return ret; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); + + if (!device_is_ready(dev)) { + LOG_ERR("%s: Device %s is not ready", + __func__, dev->name); + goto out; + } + + if (mode->oper == WIFI_MGMT_SET) { + status = nrf_wifi_check_mode_validity(mode->mode); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: mode setting is not valid", __func__); + goto out; + } + + if (vif_ctx_zep->authorized && (mode->mode == NRF_WIFI_MONITOR_MODE)) { + LOG_ERR("%s: Cannot set monitor mode when station is connected", + __func__); + goto out; + } + + /** + * Send the driver vif_idx instead of upper layer sent if_index. + * we map network if_index 1 to vif_idx of 0 and so on. The vif_ctx_zep + * context maps the correct network interface index to current driver + * interface index. + */ + status = nrf_wifi_fmac_set_mode(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, mode->mode); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: mode set operation failed", __func__); + goto out; + } + + } else { + mode->mode = def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->mode; + /** + * This is a work-around to handle current UMAC mode handling. + * This might be removed in future versions when UMAC has more space. + */ +#ifdef CONFIG_NRF70_RAW_DATA_TX + if (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->txinjection_mode == true) { + mode->mode ^= NRF_WIFI_TX_INJECTION_MODE; + } +#endif /* CONFIG_NRF70_RAW_DATA_TX */ +#ifdef CONFIG_NRF70_PROMISC_DATA_RX + if (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->promisc_mode == true) { + mode->mode ^= NRF_WIFI_PROMISCUOUS_MODE; + } +#endif /* CONFIG_NRF70_PROMISC_DATA_RX */ + } + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} +#endif /* CONFIG_NRF70_SYSTEM_WITH_RAW_MODES */ + +#if defined(CONFIG_NRF70_RAW_DATA_TX) || defined(CONFIG_NRF70_RAW_DATA_RX) +int nrf_wifi_channel(const struct device *dev, + struct wifi_channel_info *channel) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + int ret = -1; + + if (!dev || !channel) { + LOG_ERR("%s: illegal input parameters", __func__); + return ret; + } + + vif_ctx_zep = dev->data; + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return ret; + } + + if (vif_ctx_zep->authorized) { + LOG_ERR("%s: Cannot change channel when in station connected mode", __func__); + return ret; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); + + if (channel->oper == WIFI_MGMT_SET) { + /** + * Send the driver vif_idx instead of upper layer sent if_index. + * we map network if_index 1 to vif_idx of 0 and so on. The vif_ctx_zep + * context maps the correct network interface index to current driver + * interface index. + */ + status = nrf_wifi_fmac_set_channel(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, + channel->channel); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: set channel failed", __func__); + goto out; + } + } else { + channel->channel = def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->channel; + } + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} +#endif /* CONFIG_NRF70_RAW_DATA_TX || CONFIG_NRF70_RAW_DATA_RX */ + +#if defined(CONFIG_NRF70_RAW_DATA_RX) || defined(CONFIG_NRF70_PROMISC_DATA_RX) +int nrf_wifi_filter(const struct device *dev, + struct wifi_filter_info *filter) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + int ret = -1; + + if (!dev || !filter) { + LOG_ERR("%s: Illegal input parameters", __func__); + goto out; + } + + vif_ctx_zep = dev->data; + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL\n", __func__); + goto out; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); + + if (filter->oper == WIFI_MGMT_SET) { + /** + * In case a user sets data + management + ctrl bits + * or all the filter bits. Map it to bit 0 set to + * enable "all" packet filter bit setting. + * In case only filter packet size is configured and filter + * setting is sent as zero, set the filter value to + * previously configured value. + */ + if (filter->filter == WIFI_MGMT_DATA_CTRL_FILTER_SETTING + || filter->filter == WIFI_ALL_FILTER_SETTING) { + filter->filter = 1; + } else if (filter->filter == 0) { + filter->filter = + def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->packet_filter; + } + + /** + * Send the driver vif_idx instead of upper layer sent if_index. + * we map network if_index 1 to vif_idx of 0 and so on. The vif_ctx_zep + * context maps the correct network interface index to current driver + * interface index + */ + status = nrf_wifi_fmac_set_packet_filter(rpu_ctx_zep->rpu_ctx, filter->filter, + vif_ctx_zep->vif_idx, filter->buffer_size); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Set filter operation failed\n", __func__); + goto out; + } + } else { + filter->filter = def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->packet_filter; + } + ret = 0; +out: + return ret; +} +#endif /* CONFIG_NRF70_RAW_DATA_RX || CONFIG_NRF70_PROMISC_DATA_RX */ + +int nrf_wifi_set_rts_threshold(const struct device *dev, + unsigned int rts_threshold) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_umac_set_wiphy_info wiphy_info; + int ret = -1; + + if (!dev) { + LOG_ERR("%s: dev is NULL", __func__); + return ret; + } + + vif_ctx_zep = dev->data; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return ret; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + + if (!rpu_ctx_zep->rpu_ctx) { + LOG_ERR("%s: RPU context not initialized", __func__); + return ret; + } + + if ((int)rts_threshold < -1) { + /* 0 or any positive value is passed to f/w. + * For RTS off, -1 is passed to f/w. + * All other negative values considered as invalid. + */ + LOG_ERR("%s: Invalid threshold value : %d", __func__, (int)rts_threshold); + return ret; + } + + memset(&wiphy_info, 0, sizeof(struct nrf_wifi_umac_set_wiphy_info)); + + wiphy_info.rts_threshold = (int)rts_threshold; + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + + status = nrf_wifi_fmac_set_wiphy_params(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + &wiphy_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Configuring rts threshold failed\n", __func__); + goto out; + } + + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + + return ret; +} diff --git a/drivers/wifi/nrfwifi/src/wifi_mgmt_scan.c b/drivers/wifi/nrfwifi/src/wifi_mgmt_scan.c new file mode 100644 index 000000000000..64bf87628301 --- /dev/null +++ b/drivers/wifi/nrfwifi/src/wifi_mgmt_scan.c @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief File containing display scan specific definitions for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#include + +#include +#include + +#include "util.h" +#include "fmac_api.h" +#include "fmac_tx.h" +#include "fmac_main.h" +#include "wifi_mgmt_scan.h" + +LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL); + +extern struct nrf_wifi_drv_priv_zep rpu_drv_priv_zep; + +static enum nrf_wifi_band nrf_wifi_map_zep_band_to_rpu(enum wifi_frequency_bands zep_band) +{ + switch (zep_band) { + case WIFI_FREQ_BAND_2_4_GHZ: + return NRF_WIFI_BAND_2GHZ; + case WIFI_FREQ_BAND_5_GHZ: + return NRF_WIFI_BAND_5GHZ; + default: + return NRF_WIFI_BAND_INVALID; + } +} + +int nrf_wifi_disp_scan_zep(const struct device *dev, struct wifi_scan_params *params, + scan_result_cb_t cb) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_umac_scan_info *scan_info = NULL; + enum nrf_wifi_band band = NRF_WIFI_BAND_INVALID; + uint8_t band_flags = 0xFF; + uint8_t i = 0; + uint8_t j = 0; + uint8_t k = 0; + uint16_t num_scan_channels = 0; + int ret = -1; + + vif_ctx_zep = dev->data; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return ret; + } + + if (vif_ctx_zep->if_op_state != NRF_WIFI_FMAC_IF_OP_STATE_UP) { + LOG_ERR("%s: Interface not UP", __func__); + return ret; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + + + if (vif_ctx_zep->scan_in_progress) { + LOG_INF("%s: Scan already in progress", __func__); + ret = -EBUSY; + goto out; + } + + if (params) { + band_flags &= (~(1 << WIFI_FREQ_BAND_2_4_GHZ)); + +#ifndef CONFIG_NRF70_2_4G_ONLY + band_flags &= (~(1 << WIFI_FREQ_BAND_5_GHZ)); +#endif /* CONFIG_NRF70_2_4G_ONLY */ + + if (params->bands & band_flags) { + LOG_ERR("%s: Unsupported band(s) (0x%X)", __func__, params->bands); + ret = -EBUSY; + goto out; + } + + for (j = 0; j < CONFIG_WIFI_MGMT_SCAN_CHAN_MAX_MANUAL; j++) { + if (!params->band_chan[j].channel) { + break; + } + + num_scan_channels++; + } + } + + vif_ctx_zep->disp_scan_cb = cb; + + scan_info = k_calloc(sizeof(*scan_info) + + (num_scan_channels * + sizeof(scan_info->scan_params.center_frequency[0])), + sizeof(char)); + + if (!scan_info) { + LOG_ERR("%s: Unable to allocate memory for scan_info (size: %d bytes)", + __func__, + sizeof(*scan_info) + (num_scan_channels * + sizeof(scan_info->scan_params.center_frequency[0]))); + goto out; + } + + memset(scan_info, 0, sizeof(*scan_info) + (num_scan_channels * + sizeof(scan_info->scan_params.center_frequency[0]))); + + static uint8_t skip_local_admin_mac = IS_ENABLED(CONFIG_WIFI_NRF70_SKIP_LOCAL_ADMIN_MAC); + + scan_info->scan_params.skip_local_admin_macs = skip_local_admin_mac; + + scan_info->scan_reason = SCAN_DISPLAY; + + if (params) { + if (params->scan_type == WIFI_SCAN_TYPE_PASSIVE) { + scan_info->scan_params.passive_scan = 1; + } + + scan_info->scan_params.bands = params->bands; + + if (params->dwell_time_active < 0) { + LOG_ERR("%s: Invalid dwell_time_active %d", __func__, + params->dwell_time_active); + goto out; + } else { + scan_info->scan_params.dwell_time_active = params->dwell_time_active; + } + + if (params->dwell_time_passive < 0) { + LOG_ERR("%s: Invalid dwell_time_passive %d", __func__, + params->dwell_time_passive); + goto out; + } else { + scan_info->scan_params.dwell_time_passive = params->dwell_time_passive; + } + + if ((params->max_bss_cnt < 0) || + (params->max_bss_cnt > WIFI_MGMT_SCAN_MAX_BSS_CNT)) { + LOG_ERR("%s: Invalid max_bss_cnt %d", __func__, + params->max_bss_cnt); + goto out; + } else { + vif_ctx_zep->max_bss_cnt = params->max_bss_cnt; + } + + for (i = 0; i < NRF_WIFI_SCAN_MAX_NUM_SSIDS; i++) { + if (!(params->ssids[i]) || !strlen(params->ssids[i])) { + break; + } + + memcpy(scan_info->scan_params.scan_ssids[i].nrf_wifi_ssid, + params->ssids[i], + sizeof(scan_info->scan_params.scan_ssids[i].nrf_wifi_ssid)); + + scan_info->scan_params.scan_ssids[i].nrf_wifi_ssid_len = + strlen(scan_info->scan_params.scan_ssids[i].nrf_wifi_ssid); + + scan_info->scan_params.num_scan_ssids++; + } + + for (i = 0; i < CONFIG_WIFI_MGMT_SCAN_CHAN_MAX_MANUAL; i++) { + if (!params->band_chan[i].channel) { + break; + } + + band = nrf_wifi_map_zep_band_to_rpu(params->band_chan[i].band); + + if (band == NRF_WIFI_BAND_INVALID) { + LOG_ERR("%s: Unsupported band %d", __func__, + params->band_chan[i].band); + goto out; + } + + scan_info->scan_params.center_frequency[k++] = nrf_wifi_utils_chan_to_freq( + fmac_dev_ctx->fpriv->opriv, band, params->band_chan[i].channel); + + if (scan_info->scan_params.center_frequency[k - 1] == -1) { + LOG_ERR("%s: Invalid channel %d", __func__, + params->band_chan[i].channel); + goto out; + } + } + + scan_info->scan_params.num_scan_channels = k; + } + + vif_ctx_zep->scan_res_cnt = 0; + + status = nrf_wifi_fmac_scan(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, scan_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_scan failed", __func__); + goto out; + } + + vif_ctx_zep->scan_type = SCAN_DISPLAY; + vif_ctx_zep->scan_in_progress = true; + + k_work_schedule(&vif_ctx_zep->scan_timeout_work, + K_SECONDS(CONFIG_WIFI_NRF70_SCAN_TIMEOUT_S)); + + ret = 0; +out: + if (scan_info) { + k_free(scan_info); + } + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +enum nrf_wifi_status nrf_wifi_disp_scan_res_get_zep(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return NRF_WIFI_STATUS_FAIL; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + status = nrf_wifi_fmac_scan_res_get(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + SCAN_DISPLAY); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_scan failed", __func__); + goto out; + } + + status = NRF_WIFI_STATUS_SUCCESS; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return status; +} + +static inline enum wifi_mfp_options drv_to_wifi_mgmt_mfp(unsigned char mfp_flag) +{ + if (!mfp_flag) + return WIFI_MFP_DISABLE; + if (mfp_flag & NRF_WIFI_MFP_REQUIRED) + return WIFI_MFP_REQUIRED; + if (mfp_flag & NRF_WIFI_MFP_CAPABLE) + return WIFI_MFP_OPTIONAL; + + return WIFI_MFP_UNKNOWN; +} +static inline enum wifi_security_type drv_to_wifi_mgmt(int drv_security_type) +{ + switch (drv_security_type) { + case NRF_WIFI_OPEN: + return WIFI_SECURITY_TYPE_NONE; + case NRF_WIFI_WEP: + return WIFI_SECURITY_TYPE_WEP; + case NRF_WIFI_WPA: + return WIFI_SECURITY_TYPE_WPA_PSK; + case NRF_WIFI_WPA2: + return WIFI_SECURITY_TYPE_PSK; + case NRF_WIFI_WPA2_256: + return WIFI_SECURITY_TYPE_PSK_SHA256; + case NRF_WIFI_WPA3: + return WIFI_SECURITY_TYPE_SAE; + case NRF_WIFI_WAPI: + return WIFI_SECURITY_TYPE_WAPI; + case NRF_WIFI_EAP: + return WIFI_SECURITY_TYPE_EAP; + default: + return WIFI_SECURITY_TYPE_UNKNOWN; + } +} + +void nrf_wifi_event_proc_disp_scan_res_zep(void *vif_ctx, + struct nrf_wifi_umac_event_new_scan_display_results *scan_res, + unsigned int event_len, + bool more_res) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct umac_display_results *r = NULL; + struct wifi_scan_result res; + uint16_t max_bss_cnt = 0; + unsigned int i = 0; + scan_result_cb_t cb = NULL; + + vif_ctx_zep = vif_ctx; + + cb = (scan_result_cb_t)vif_ctx_zep->disp_scan_cb; + + /* Delayed event (after scan timeout) or rogue event after scan done */ + if (!cb) { + return; + } + + max_bss_cnt = vif_ctx_zep->max_bss_cnt ? + vif_ctx_zep->max_bss_cnt : CONFIG_NRF_WIFI_SCAN_MAX_BSS_CNT; + + for (i = 0; i < scan_res->event_bss_count; i++) { + /* Limit the scan results to the configured maximum */ + if ((max_bss_cnt > 0) && + (vif_ctx_zep->scan_res_cnt >= max_bss_cnt)) { + break; + } + + memset(&res, 0x0, sizeof(res)); + + r = &scan_res->display_results[i]; + + res.ssid_length = MIN(sizeof(res.ssid), r->ssid.nrf_wifi_ssid_len); + + res.band = r->nwk_band; + + res.channel = r->nwk_channel; + + res.security = drv_to_wifi_mgmt(r->security_type); + + res.mfp = drv_to_wifi_mgmt_mfp(r->mfp_flag); + + memcpy(res.ssid, + r->ssid.nrf_wifi_ssid, + res.ssid_length); + + memcpy(res.mac, r->mac_addr, NRF_WIFI_ETH_ADDR_LEN); + res.mac_length = NRF_WIFI_ETH_ADDR_LEN; + + if (r->signal.signal_type == NRF_WIFI_SIGNAL_TYPE_MBM) { + int val = (r->signal.signal.mbm_signal); + + res.rssi = (val / 100); + } else if (r->signal.signal_type == NRF_WIFI_SIGNAL_TYPE_UNSPEC) { + res.rssi = (r->signal.signal.unspec_signal); + } + + vif_ctx_zep->disp_scan_cb(vif_ctx_zep->zep_net_if_ctx, + 0, + &res); + + vif_ctx_zep->scan_res_cnt++; + + /* NET_MGMT dropping events if too many are queued */ + k_yield(); + } + + if (more_res == false) { + vif_ctx_zep->disp_scan_cb(vif_ctx_zep->zep_net_if_ctx, 0, NULL); + vif_ctx_zep->scan_in_progress = false; + vif_ctx_zep->disp_scan_cb = NULL; + k_work_cancel_delayable(&vif_ctx_zep->scan_timeout_work); + } +} + + +#ifdef CONFIG_WIFI_MGMT_RAW_SCAN_RESULTS +void nrf_wifi_rx_bcn_prb_resp_frm(void *vif_ctx, + void *nwb, + unsigned short frequency, + signed short signal) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = vif_ctx; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + struct wifi_raw_scan_result bcn_prb_resp_info; + int frame_length = 0; + int val = signal; + + vif_ctx_zep = vif_ctx; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return; + } + + if (!vif_ctx_zep->scan_in_progress) { + /*LOG_INF("%s: Scan not in progress : raw scan data not available", __func__);*/ + return; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + + frame_length = nrf_wifi_osal_nbuf_data_size(fmac_dev_ctx->fpriv->opriv, + nwb); + + if (frame_length > CONFIG_WIFI_MGMT_RAW_SCAN_RESULT_LENGTH) { + nrf_wifi_osal_mem_cpy(fmac_dev_ctx->fpriv->opriv, + &bcn_prb_resp_info.data, + nrf_wifi_osal_nbuf_data_get( + fmac_dev_ctx->fpriv->opriv, + nwb), + CONFIG_WIFI_MGMT_RAW_SCAN_RESULT_LENGTH); + + } else { + nrf_wifi_osal_mem_cpy(fmac_dev_ctx->fpriv->opriv, + &bcn_prb_resp_info.data, + nrf_wifi_osal_nbuf_data_get( + fmac_dev_ctx->fpriv->opriv, + nwb), + frame_length); + } + + bcn_prb_resp_info.rssi = MBM_TO_DBM(val); + bcn_prb_resp_info.frequency = frequency; + bcn_prb_resp_info.frame_length = frame_length; + + wifi_mgmt_raise_raw_scan_result_event(vif_ctx_zep->zep_net_if_ctx, + &bcn_prb_resp_info); + +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); +} +#endif /* CONFIG_WIFI_MGMT_RAW_SCAN_RESULTS */ diff --git a/drivers/wifi/nrfwifi/src/wifi_util.c b/drivers/wifi/nrfwifi/src/wifi_util.c new file mode 100644 index 000000000000..4b267cb89cda --- /dev/null +++ b/drivers/wifi/nrfwifi/src/wifi_util.c @@ -0,0 +1,1005 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* @file + * @brief NRF Wi-Fi util shell module + */ +#include +#include "host_rpu_umac_if.h" +#include "fmac_api.h" +#include "fmac_util.h" +#include "fmac_main.h" +#include "wifi_util.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; + +static bool check_valid_data_rate(const struct shell *sh, + unsigned char rate_flag, + unsigned int data_rate) +{ + bool ret = false; + + switch (rate_flag) { + case RPU_TPUT_MODE_LEGACY: + if ((data_rate == 1) || + (data_rate == 2) || + (data_rate == 55) || + (data_rate == 11) || + (data_rate == 6) || + (data_rate == 9) || + (data_rate == 12) || + (data_rate == 18) || + (data_rate == 24) || + (data_rate == 36) || + (data_rate == 48) || + (data_rate == 54)) { + ret = true; + } + break; + case RPU_TPUT_MODE_HT: + case RPU_TPUT_MODE_HE_SU: + case RPU_TPUT_MODE_VHT: + if ((data_rate >= 0) && (data_rate <= 7)) { + ret = true; + } + break; + case RPU_TPUT_MODE_HE_ER_SU: + if (data_rate >= 0 && data_rate <= 2) { + ret = true; + } + break; + default: + shell_fprintf(sh, + SHELL_ERROR, + "%s: Invalid rate_flag %d\n", + __func__, + rate_flag); + break; + } + + return ret; +} + + +int nrf_wifi_util_conf_init(struct rpu_conf_params *conf_params) +{ + if (!conf_params) { + return -ENOEXEC; + } + + memset(conf_params, 0, sizeof(*conf_params)); + + /* Initialize values which are other than 0 */ + conf_params->he_ltf = -1; + conf_params->he_gi = -1; + return 0; +} + + +static int nrf_wifi_util_set_he_ltf(const struct shell *sh, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long he_ltf = 0; + + if (ctx->conf_params.set_he_ltf_gi) { + shell_fprintf(sh, + SHELL_ERROR, + "Disable 'set_he_ltf_gi', to set 'he_ltf'\n"); + return -ENOEXEC; + } + + he_ltf = strtoul(argv[1], &ptr, 10); + + if (he_ltf > 2) { + shell_fprintf(sh, + SHELL_ERROR, + "Invalid HE LTF value(%lu).\n", + he_ltf); + shell_help(sh); + return -ENOEXEC; + } + + ctx->conf_params.he_ltf = he_ltf; + + return 0; +} + + +static int nrf_wifi_util_set_he_gi(const struct shell *sh, + size_t argc, + const char *argv[]) +{ + char *ptr = NULL; + unsigned long he_gi = 0; + + if (ctx->conf_params.set_he_ltf_gi) { + shell_fprintf(sh, + SHELL_ERROR, + "Disable 'set_he_ltf_gi', to set 'he_gi'\n"); + return -ENOEXEC; + } + + he_gi = strtoul(argv[1], &ptr, 10); + + if (he_gi > 2) { + shell_fprintf(sh, + SHELL_ERROR, + "Invalid HE GI value(%lu).\n", + he_gi); + shell_help(sh); + return -ENOEXEC; + } + + ctx->conf_params.he_gi = he_gi; + + return 0; +} + + +static int nrf_wifi_util_set_he_ltf_gi(const struct shell *sh, + 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 < 0) || (val > 1)) { + shell_fprintf(sh, + SHELL_ERROR, + "Invalid value(%lu).\n", + val); + shell_help(sh); + return -ENOEXEC; + } + + status = nrf_wifi_fmac_conf_ltf_gi(ctx->rpu_ctx, + ctx->conf_params.he_ltf, + ctx->conf_params.he_gi, + val); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(sh, + SHELL_ERROR, + "Programming ltf_gi failed\n"); + return -ENOEXEC; + } + + ctx->conf_params.set_he_ltf_gi = val; + + return 0; +} + +#ifdef CONFIG_NRF70_STA_MODE +static int nrf_wifi_util_set_uapsd_queue(const struct shell *sh, + 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 < UAPSD_Q_MIN) || (val > UAPSD_Q_MAX)) { + shell_fprintf(sh, + SHELL_ERROR, + "Invalid value(%lu).\n", + val); + shell_help(sh); + return -ENOEXEC; + } + + if (ctx->conf_params.uapsd_queue != val) { + status = nrf_wifi_fmac_set_uapsd_queue(ctx->rpu_ctx, + 0, + val); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(sh, + SHELL_ERROR, + "Programming uapsd_queue failed\n"); + return -ENOEXEC; + } + + ctx->conf_params.uapsd_queue = val; + } + + return 0; +} +#endif /* CONFIG_NRF70_STA_MODE */ + + +static int nrf_wifi_util_show_cfg(const struct shell *sh, + size_t argc, + const char *argv[]) +{ + struct rpu_conf_params *conf_params = NULL; + + conf_params = &ctx->conf_params; + + shell_fprintf(sh, + SHELL_INFO, + "************* Configured Parameters ***********\n"); + shell_fprintf(sh, + SHELL_INFO, + "\n"); + + shell_fprintf(sh, + SHELL_INFO, + "he_ltf = %d\n", + conf_params->he_ltf); + + shell_fprintf(sh, + SHELL_INFO, + "he_gi = %u\n", + conf_params->he_gi); + + shell_fprintf(sh, + SHELL_INFO, + "set_he_ltf_gi = %d\n", + conf_params->set_he_ltf_gi); + + shell_fprintf(sh, + SHELL_INFO, + "uapsd_queue = %d\n", + conf_params->uapsd_queue); + + shell_fprintf(sh, + SHELL_INFO, + "rate_flag = %d, rate_val = %d\n", + ctx->conf_params.tx_pkt_tput_mode, + ctx->conf_params.tx_pkt_rate); + return 0; +} + +#ifdef CONFIG_NRF70_STA_MODE +static int nrf_wifi_util_tx_stats(const struct shell *sh, + size_t argc, + const char *argv[]) +{ + int vif_index = -1; + int peer_index = 0; + int max_vif_index = MAX(MAX_NUM_APS, MAX_NUM_STAS); + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + void *queue = NULL; + unsigned int tx_pending_pkts = 0; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + + vif_index = atoi(argv[1]); + if ((vif_index < 0) || (vif_index >= max_vif_index)) { + shell_fprintf(sh, + SHELL_ERROR, + "Invalid vif index(%d).\n", + vif_index); + shell_help(sh); + return -ENOEXEC; + } + + fmac_dev_ctx = ctx->rpu_ctx; + def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); + + /* TODO: Get peer_index from shell once AP mode is supported */ + shell_fprintf(sh, + SHELL_INFO, + "************* Tx Stats: vif(%d) peer(0) ***********\n", + vif_index); + + for (int i = 0; i < NRF_WIFI_FMAC_AC_MAX ; i++) { + queue = def_dev_ctx->tx_config.data_pending_txq[peer_index][i]; + tx_pending_pkts = nrf_wifi_utils_q_len(fmac_dev_ctx->fpriv->opriv, queue); + + shell_fprintf( + shell, + SHELL_INFO, + "Outstanding tokens: ac: %d -> %d (pending_q_len: %d)\n", + i, + def_dev_ctx->tx_config.outstanding_descs[i], + tx_pending_pkts); + } + + return 0; +} +#endif /* CONFIG_NRF70_STA_MODE */ + + +static int nrf_wifi_util_tx_rate(const struct shell *sh, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + char *ptr = NULL; + long rate_flag = -1; + long data_rate = -1; + + rate_flag = strtol(argv[1], &ptr, 10); + + if (rate_flag >= RPU_TPUT_MODE_MAX) { + shell_fprintf(sh, + SHELL_ERROR, + "Invalid value %ld for rate_flags\n", + rate_flag); + shell_help(sh); + return -ENOEXEC; + } + + + if (rate_flag == RPU_TPUT_MODE_HE_TB) { + data_rate = -1; + } else { + if (argc < 3) { + shell_fprintf(sh, + SHELL_ERROR, + "rate_val needed for rate_flag = %ld\n", + rate_flag); + shell_help(sh); + return -ENOEXEC; + } + + data_rate = strtol(argv[2], &ptr, 10); + + if (!(check_valid_data_rate(shell, + rate_flag, + data_rate))) { + shell_fprintf(sh, + SHELL_ERROR, + "Invalid data_rate %ld for rate_flag %ld\n", + data_rate, + rate_flag); + return -ENOEXEC; + } + + } + + status = nrf_wifi_fmac_set_tx_rate(ctx->rpu_ctx, + rate_flag, + data_rate); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(sh, + SHELL_ERROR, + "Programming tx_rate failed\n"); + return -ENOEXEC; + } + + ctx->conf_params.tx_pkt_tput_mode = rate_flag; + ctx->conf_params.tx_pkt_rate = data_rate; + + return 0; +} + + +#ifdef CONFIG_NRF_WIFI_LOW_POWER +static int nrf_wifi_util_show_host_rpu_ps_ctrl_state(const struct shell *sh, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int rpu_ps_state = -1; + + status = nrf_wifi_fmac_get_host_rpu_ps_ctrl_state(ctx->rpu_ctx, + &rpu_ps_state); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(sh, + SHELL_ERROR, + "Failed to get PS state\n"); + return -ENOEXEC; + } + + shell_fprintf(sh, + SHELL_INFO, + "RPU sleep status = %s\n", rpu_ps_state ? "AWAKE" : "SLEEP"); + return 0; +} +#endif /* CONFIG_NRF_WIFI_LOW_POWER */ + + +static int nrf_wifi_util_show_vers(const struct shell *sh, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + unsigned int fw_ver; + + fmac_dev_ctx = ctx->rpu_ctx; + + shell_fprintf(sh, SHELL_INFO, "Driver version: %s\n", + NRF70_DRIVER_VERSION); + + status = nrf_wifi_fmac_ver_get(fmac_dev_ctx, &fw_ver); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(sh, + SHELL_INFO, + "Failed to get firmware version\n"); + return -ENOEXEC; + } + + shell_fprintf(sh, SHELL_INFO, + "Firmware version: %d.%d.%d.%d\n", + NRF_WIFI_UMAC_VER(fw_ver), + NRF_WIFI_UMAC_VER_MAJ(fw_ver), + NRF_WIFI_UMAC_VER_MIN(fw_ver), + NRF_WIFI_UMAC_VER_EXTRA(fw_ver)); + + return status; +} + +#ifndef CONFIG_NRF70_RADIO_TEST +static int nrf_wifi_util_dump_rpu_stats(const struct shell *sh, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + struct rpu_op_stats stats; + enum rpu_stats_type stats_type = RPU_STATS_TYPE_ALL; + + if (argc == 2) { + const char *type = argv[1]; + + if (!strcmp(type, "umac")) { + stats_type = RPU_STATS_TYPE_UMAC; + } else if (!strcmp(type, "lmac")) { + stats_type = RPU_STATS_TYPE_LMAC; + } else if (!strcmp(type, "phy")) { + stats_type = RPU_STATS_TYPE_PHY; + } else if (!strcmp(type, "all")) { + stats_type = RPU_STATS_TYPE_ALL; + } else { + shell_fprintf(sh, + SHELL_ERROR, + "Invalid stats type %s\n", + type); + return -ENOEXEC; + } + } + + fmac_dev_ctx = ctx->rpu_ctx; + + memset(&stats, 0, sizeof(struct rpu_op_stats)); + status = nrf_wifi_fmac_stats_get(fmac_dev_ctx, 0, &stats); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(sh, + SHELL_ERROR, + "Failed to get stats\n"); + return -ENOEXEC; + } + + if (stats_type == RPU_STATS_TYPE_UMAC || stats_type == RPU_STATS_TYPE_ALL) { + struct rpu_umac_stats *umac = &stats.fw.umac; + + shell_fprintf(sh, SHELL_INFO, + "UMAC TX debug stats:\n" + "======================\n" + "tx_cmd: %u\n" + "tx_non_coalesce_pkts_rcvd_from_host: %u\n" + "tx_coalesce_pkts_rcvd_from_host: %u\n" + "tx_max_coalesce_pkts_rcvd_from_host: %u\n" + "tx_cmds_max_used: %u\n" + "tx_cmds_currently_in_use: %u\n" + "tx_done_events_send_to_host: %u\n" + "tx_done_success_pkts_to_host: %u\n" + "tx_done_failure_pkts_to_host: %u\n" + "tx_cmds_with_crypto_pkts_rcvd_from_host: %u\n" + "tx_cmds_with_non_crypto_pkts_rcvd_from_host: %u\n" + "tx_cmds_with_broadcast_pkts_rcvd_from_host: %u\n" + "tx_cmds_with_multicast_pkts_rcvd_from_host: %u\n" + "tx_cmds_with_unicast_pkts_rcvd_from_host: %u\n" + "xmit: %u\n" + "send_addba_req: %u\n" + "addba_resp: %u\n" + "softmac_tx: %u\n" + "internal_pkts: %u\n" + "external_pkts: %u\n" + "tx_cmds_to_lmac: %u\n" + "tx_dones_from_lmac: %u\n" + "total_cmds_to_lmac: %u\n" + "tx_packet_data_count: %u\n" + "tx_packet_mgmt_count: %u\n" + "tx_packet_beacon_count: %u\n" + "tx_packet_probe_req_count: %u\n" + "tx_packet_auth_count: %u\n" + "tx_packet_deauth_count: %u\n" + "tx_packet_assoc_req_count: %u\n" + "tx_packet_disassoc_count: %u\n" + "tx_packet_action_count: %u\n" + "tx_packet_other_mgmt_count: %u\n" + "tx_packet_non_mgmt_data_count: %u\n\n", + umac->tx_dbg_params.tx_cmd, + umac->tx_dbg_params.tx_non_coalesce_pkts_rcvd_from_host, + umac->tx_dbg_params.tx_coalesce_pkts_rcvd_from_host, + umac->tx_dbg_params.tx_max_coalesce_pkts_rcvd_from_host, + umac->tx_dbg_params.tx_cmds_max_used, + umac->tx_dbg_params.tx_cmds_currently_in_use, + umac->tx_dbg_params.tx_done_events_send_to_host, + umac->tx_dbg_params.tx_done_success_pkts_to_host, + umac->tx_dbg_params.tx_done_failure_pkts_to_host, + umac->tx_dbg_params.tx_cmds_with_crypto_pkts_rcvd_from_host, + umac->tx_dbg_params.tx_cmds_with_non_crypto_pkts_rcvd_from_host, + umac->tx_dbg_params.tx_cmds_with_broadcast_pkts_rcvd_from_host, + umac->tx_dbg_params.tx_cmds_with_multicast_pkts_rcvd_from_host, + umac->tx_dbg_params.tx_cmds_with_unicast_pkts_rcvd_from_host, + umac->tx_dbg_params.xmit, + umac->tx_dbg_params.send_addba_req, + umac->tx_dbg_params.addba_resp, + umac->tx_dbg_params.softmac_tx, + umac->tx_dbg_params.internal_pkts, + umac->tx_dbg_params.external_pkts, + umac->tx_dbg_params.tx_cmds_to_lmac, + umac->tx_dbg_params.tx_dones_from_lmac, + umac->tx_dbg_params.total_cmds_to_lmac, + umac->tx_dbg_params.tx_packet_data_count, + umac->tx_dbg_params.tx_packet_mgmt_count, + umac->tx_dbg_params.tx_packet_beacon_count, + umac->tx_dbg_params.tx_packet_probe_req_count, + umac->tx_dbg_params.tx_packet_auth_count, + umac->tx_dbg_params.tx_packet_deauth_count, + umac->tx_dbg_params.tx_packet_assoc_req_count, + umac->tx_dbg_params.tx_packet_disassoc_count, + umac->tx_dbg_params.tx_packet_action_count, + umac->tx_dbg_params.tx_packet_other_mgmt_count, + umac->tx_dbg_params.tx_packet_non_mgmt_data_count); + + shell_fprintf(sh, SHELL_INFO, + "UMAC RX debug stats\n" + "======================\n" + "lmac_events: %u\n" + "rx_events: %u\n" + "rx_coalesce_events: %u\n" + "total_rx_pkts_from_lmac: %u\n" + "max_refill_gap: %u\n" + "current_refill_gap: %u\n" + "out_of_order_mpdus: %u\n" + "reorder_free_mpdus: %u\n" + "umac_consumed_pkts: %u\n" + "host_consumed_pkts: %u\n" + "rx_mbox_post: %u\n" + "rx_mbox_receive: %u\n" + "reordering_ampdu: %u\n" + "timer_mbox_post: %u\n" + "timer_mbox_rcv: %u\n" + "work_mbox_post: %u\n" + "work_mbox_rcv: %u\n" + "tasklet_mbox_post: %u\n" + "tasklet_mbox_rcv: %u\n" + "userspace_offload_frames: %u\n" + "alloc_buf_fail: %u\n" + "rx_packet_total_count: %u\n" + "rx_packet_data_count: %u\n" + "rx_packet_qos_data_count: %u\n" + "rx_packet_protected_data_count: %u\n" + "rx_packet_mgmt_count: %u\n" + "rx_packet_beacon_count: %u\n" + "rx_packet_probe_resp_count: %u\n" + "rx_packet_auth_count: %u\n" + "rx_packet_deauth_count: %u\n" + "rx_packet_assoc_resp_count: %u\n" + "rx_packet_disassoc_count: %u\n" + "rx_packet_action_count: %u\n" + "rx_packet_probe_req_count: %u\n" + "rx_packet_other_mgmt_count: %u\n" + "max_coalesce_pkts: %d\n" + "null_skb_pointer_from_lmac: %u\n" + "unexpected_mgmt_pkt: %u\n\n", + umac->rx_dbg_params.lmac_events, + umac->rx_dbg_params.rx_events, + umac->rx_dbg_params.rx_coalesce_events, + umac->rx_dbg_params.total_rx_pkts_from_lmac, + umac->rx_dbg_params.max_refill_gap, + umac->rx_dbg_params.current_refill_gap, + umac->rx_dbg_params.out_of_order_mpdus, + umac->rx_dbg_params.reorder_free_mpdus, + umac->rx_dbg_params.umac_consumed_pkts, + umac->rx_dbg_params.host_consumed_pkts, + umac->rx_dbg_params.rx_mbox_post, + umac->rx_dbg_params.rx_mbox_receive, + umac->rx_dbg_params.reordering_ampdu, + umac->rx_dbg_params.timer_mbox_post, + umac->rx_dbg_params.timer_mbox_rcv, + umac->rx_dbg_params.work_mbox_post, + umac->rx_dbg_params.work_mbox_rcv, + umac->rx_dbg_params.tasklet_mbox_post, + umac->rx_dbg_params.tasklet_mbox_rcv, + umac->rx_dbg_params.userspace_offload_frames, + umac->rx_dbg_params.alloc_buf_fail, + umac->rx_dbg_params.rx_packet_total_count, + umac->rx_dbg_params.rx_packet_data_count, + umac->rx_dbg_params.rx_packet_qos_data_count, + umac->rx_dbg_params.rx_packet_protected_data_count, + umac->rx_dbg_params.rx_packet_mgmt_count, + umac->rx_dbg_params.rx_packet_beacon_count, + umac->rx_dbg_params.rx_packet_probe_resp_count, + umac->rx_dbg_params.rx_packet_auth_count, + umac->rx_dbg_params.rx_packet_deauth_count, + umac->rx_dbg_params.rx_packet_assoc_resp_count, + umac->rx_dbg_params.rx_packet_disassoc_count, + umac->rx_dbg_params.rx_packet_action_count, + umac->rx_dbg_params.rx_packet_probe_req_count, + umac->rx_dbg_params.rx_packet_other_mgmt_count, + umac->rx_dbg_params.max_coalesce_pkts, + umac->rx_dbg_params.null_skb_pointer_from_lmac, + umac->rx_dbg_params.unexpected_mgmt_pkt); + + shell_fprintf(sh, SHELL_INFO, + "UMAC control path stats\n" + "======================\n" + "cmd_init: %u\n" + "event_init_done: %u\n" + "cmd_rf_test: %u\n" + "cmd_connect: %u\n" + "cmd_get_stats: %u\n" + "event_ps_state: %u\n" + "cmd_set_reg: %u\n" + "cmd_get_reg: %u\n" + "cmd_req_set_reg: %u\n" + "cmd_trigger_scan: %u\n" + "event_scan_done: %u\n" + "cmd_get_scan: %u\n" + "umac_scan_req: %u\n" + "umac_scan_complete: %u\n" + "umac_scan_busy: %u\n" + "cmd_auth: %u\n" + "cmd_assoc: %u\n" + "cmd_deauth: %u\n" + "cmd_register_frame: %u\n" + "cmd_frame: %u\n" + "cmd_del_key: %u\n" + "cmd_new_key: %u\n" + "cmd_set_key: %u\n" + "cmd_get_key: %u\n" + "event_beacon_hint: %u\n" + "event_reg_change: %u\n" + "event_wiphy_reg_change: %u\n" + "cmd_set_station: %u\n" + "cmd_new_station: %u\n" + "cmd_del_station: %u\n" + "cmd_new_interface: %u\n" + "cmd_set_interface: %u\n" + "cmd_get_interface: %u\n" + "cmd_set_ifflags: %u\n" + "cmd_set_ifflags_done: %u\n" + "cmd_set_bss: %u\n" + "cmd_set_wiphy: %u\n" + "cmd_start_ap: %u\n" + "LMAC_CMD_PS: %u\n" + "CURR_STATE: %u\n\n", + umac->cmd_evnt_dbg_params.cmd_init, + umac->cmd_evnt_dbg_params.event_init_done, + umac->cmd_evnt_dbg_params.cmd_rf_test, + umac->cmd_evnt_dbg_params.cmd_connect, + umac->cmd_evnt_dbg_params.cmd_get_stats, + umac->cmd_evnt_dbg_params.event_ps_state, + umac->cmd_evnt_dbg_params.cmd_set_reg, + umac->cmd_evnt_dbg_params.cmd_get_reg, + umac->cmd_evnt_dbg_params.cmd_req_set_reg, + umac->cmd_evnt_dbg_params.cmd_trigger_scan, + umac->cmd_evnt_dbg_params.event_scan_done, + umac->cmd_evnt_dbg_params.cmd_get_scan, + umac->cmd_evnt_dbg_params.umac_scan_req, + umac->cmd_evnt_dbg_params.umac_scan_complete, + umac->cmd_evnt_dbg_params.umac_scan_busy, + umac->cmd_evnt_dbg_params.cmd_auth, + umac->cmd_evnt_dbg_params.cmd_assoc, + umac->cmd_evnt_dbg_params.cmd_deauth, + umac->cmd_evnt_dbg_params.cmd_register_frame, + umac->cmd_evnt_dbg_params.cmd_frame, + umac->cmd_evnt_dbg_params.cmd_del_key, + umac->cmd_evnt_dbg_params.cmd_new_key, + umac->cmd_evnt_dbg_params.cmd_set_key, + umac->cmd_evnt_dbg_params.cmd_get_key, + umac->cmd_evnt_dbg_params.event_beacon_hint, + umac->cmd_evnt_dbg_params.event_reg_change, + umac->cmd_evnt_dbg_params.event_wiphy_reg_change, + umac->cmd_evnt_dbg_params.cmd_set_station, + umac->cmd_evnt_dbg_params.cmd_new_station, + umac->cmd_evnt_dbg_params.cmd_del_station, + umac->cmd_evnt_dbg_params.cmd_new_interface, + umac->cmd_evnt_dbg_params.cmd_set_interface, + umac->cmd_evnt_dbg_params.cmd_get_interface, + umac->cmd_evnt_dbg_params.cmd_set_ifflags, + umac->cmd_evnt_dbg_params.cmd_set_ifflags_done, + umac->cmd_evnt_dbg_params.cmd_set_bss, + umac->cmd_evnt_dbg_params.cmd_set_wiphy, + umac->cmd_evnt_dbg_params.cmd_start_ap, + umac->cmd_evnt_dbg_params.LMAC_CMD_PS, + umac->cmd_evnt_dbg_params.CURR_STATE); + + shell_fprintf(sh, SHELL_INFO, + "UMAC interface stats\n" + "======================\n" + "tx_unicast_pkt_count: %u\n" + "tx_multicast_pkt_count: %u\n" + "tx_broadcast_pkt_count: %u\n" + "tx_bytes: %u\n" + "rx_unicast_pkt_count: %u\n" + "rx_multicast_pkt_count: %u\n" + "rx_broadcast_pkt_count: %u\n" + "rx_beacon_success_count: %u\n" + "rx_beacon_miss_count: %u\n" + "rx_bytes: %u\n" + "rx_checksum_error_count: %u\n\n" + "replay_attack_drop_cnt: %u\n\n", + umac->interface_data_stats.tx_unicast_pkt_count, + umac->interface_data_stats.tx_multicast_pkt_count, + umac->interface_data_stats.tx_broadcast_pkt_count, + umac->interface_data_stats.tx_bytes, + umac->interface_data_stats.rx_unicast_pkt_count, + umac->interface_data_stats.rx_multicast_pkt_count, + umac->interface_data_stats.rx_broadcast_pkt_count, + umac->interface_data_stats.rx_beacon_success_count, + umac->interface_data_stats.rx_beacon_miss_count, + umac->interface_data_stats.rx_bytes, + umac->interface_data_stats.rx_checksum_error_count, + umac->interface_data_stats.replay_attack_drop_cnt); + } + + if (stats_type == RPU_STATS_TYPE_LMAC || stats_type == RPU_STATS_TYPE_ALL) { + struct rpu_lmac_stats *lmac = &stats.fw.lmac; + + shell_fprintf(sh, SHELL_INFO, + "LMAC stats\n" + "======================\n" + "reset_cmd_cnt: %u\n" + "reset_complete_event_cnt: %u\n" + "unable_gen_event: %u\n" + "ch_prog_cmd_cnt: %u\n" + "channel_prog_done: %u\n" + "tx_pkt_cnt: %u\n" + "tx_pkt_done_cnt: %u\n" + "scan_pkt_cnt: %u\n" + "internal_pkt_cnt: %u\n" + "internal_pkt_done_cnt: %u\n" + "ack_resp_cnt: %u\n" + "tx_timeout: %u\n" + "deagg_isr: %u\n" + "deagg_inptr_desc_empty: %u\n" + "deagg_circular_buffer_full: %u\n" + "lmac_rxisr_cnt: %u\n" + "rx_decryptcnt: %u\n" + "process_decrypt_fail: %u\n" + "prepa_rx_event_fail: %u\n" + "rx_core_pool_full_cnt: %u\n" + "rx_mpdu_crc_success_cnt: %u\n" + "rx_mpdu_crc_fail_cnt: %u\n" + "rx_ofdm_crc_success_cnt: %u\n" + "rx_ofdm_crc_fail_cnt: %u\n" + "rxDSSSCrcSuccessCnt: %u\n" + "rxDSSSCrcFailCnt: %u\n" + "rx_crypto_start_cnt: %u\n" + "rx_crypto_done_cnt: %u\n" + "rx_event_buf_full: %u\n" + "rx_extram_buf_full: %u\n" + "scan_req: %u\n" + "scan_complete: %u\n" + "scan_abort_req: %u\n" + "scan_abort_complete: %u\n" + "internal_buf_pool_null: %u\n" + "rpu_hw_lockup_count: %u\n" + "rpu_hw_lockup_recovery_done: %u\n\n", + lmac->reset_cmd_cnt, + lmac->reset_complete_event_cnt, + lmac->unable_gen_event, + lmac->ch_prog_cmd_cnt, + lmac->channel_prog_done, + lmac->tx_pkt_cnt, + lmac->tx_pkt_done_cnt, + lmac->scan_pkt_cnt, + lmac->internal_pkt_cnt, + lmac->internal_pkt_done_cnt, + lmac->ack_resp_cnt, + lmac->tx_timeout, + lmac->deagg_isr, + lmac->deagg_inptr_desc_empty, + lmac->deagg_circular_buffer_full, + lmac->lmac_rxisr_cnt, + lmac->rx_decryptcnt, + lmac->process_decrypt_fail, + lmac->prepa_rx_event_fail, + lmac->rx_core_pool_full_cnt, + lmac->rx_mpdu_crc_success_cnt, + lmac->rx_mpdu_crc_fail_cnt, + lmac->rx_ofdm_crc_success_cnt, + lmac->rx_ofdm_crc_fail_cnt, + lmac->rxDSSSCrcSuccessCnt, + lmac->rxDSSSCrcFailCnt, + lmac->rx_crypto_start_cnt, + lmac->rx_crypto_done_cnt, + lmac->rx_event_buf_full, + lmac->rx_extram_buf_full, + lmac->scan_req, + lmac->scan_complete, + lmac->scan_abort_req, + lmac->scan_abort_complete, + lmac->internal_buf_pool_null, + lmac->rpu_hw_lockup_count, + lmac->rpu_hw_lockup_recovery_done); + } + + if (stats_type == RPU_STATS_TYPE_PHY || stats_type == RPU_STATS_TYPE_ALL) { + struct rpu_phy_stats *phy = &stats.fw.phy; + + shell_fprintf(sh, SHELL_INFO, + "PHY stats\n" + "======================\n" + "rssi_avg: %d\n" + "pdout_val: %u\n" + "ofdm_crc32_pass_cnt: %u\n" + "ofdm_crc32_fail_cnt: %u\n" + "dsss_crc32_pass_cnt: %u\n" + "dsss_crc32_fail_cnt: %u\n\n", + phy->rssi_avg, + phy->pdout_val, + phy->ofdm_crc32_pass_cnt, + phy->ofdm_crc32_fail_cnt, + phy->dsss_crc32_pass_cnt, + phy->dsss_crc32_fail_cnt); + } + + return 0; +} +#endif /* CONFIG_NRF70_RADIO_TEST */ + +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY +static int nrf_wifi_util_trigger_rpu_recovery(const struct shell *sh, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + + if (!ctx || !ctx->rpu_ctx) { + shell_fprintf(sh, + SHELL_ERROR, + "RPU context not initialized\n"); + return -ENOEXEC; + } + + fmac_dev_ctx = ctx->rpu_ctx; + + status = nrf_wifi_fmac_rpu_recovery_callback(fmac_dev_ctx, NULL, 0); + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(sh, + SHELL_ERROR, + "Failed to trigger RPU recovery\n"); + return -ENOEXEC; + } + + shell_fprintf(sh, + SHELL_INFO, + "RPU recovery triggered\n"); + + return 0; +} +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ + +SHELL_STATIC_SUBCMD_SET_CREATE( + nrf_wifi_util_subcmds, + SHELL_CMD_ARG(he_ltf, + NULL, + "0 - 1x HE LTF\n" + "1 - 2x HE LTF\n" + "2 - 4x HE LTF ", + nrf_wifi_util_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_util_set_he_gi, + 2, + 0), + SHELL_CMD_ARG(set_he_ltf_gi, + NULL, + "0 - Disable\n" + "1 - Enable", + nrf_wifi_util_set_he_ltf_gi, + 2, + 0), +#ifdef CONFIG_NRF70_STA_MODE + SHELL_CMD_ARG(uapsd_queue, + NULL, + " - 0 to 15", + nrf_wifi_util_set_uapsd_queue, + 2, + 0), +#endif /* CONFIG_NRF70_STA_MODE */ + SHELL_CMD_ARG(show_config, + NULL, + "Display the current configuration values", + nrf_wifi_util_show_cfg, + 1, + 0), +#ifdef CONFIG_NRF70_STA_MODE + SHELL_CMD_ARG(tx_stats, + NULL, + "Displays transmit statistics\n" + "vif_index: 0 - 1\n", + nrf_wifi_util_tx_stats, + 2, + 0), +#endif /* CONFIG_NRF70_STA_MODE */ + SHELL_CMD_ARG(tx_rate, + NULL, + "Sets TX data rate to either a fixed value or AUTO\n" + "Parameters:\n" + " : The TX data rate type to be set, where:\n" + " 0 - LEGACY\n" + " 1 - HT\n" + " 2 - VHT\n" + " 3 - HE_SU\n" + " 4 - HE_ER_SU\n" + " 5 - AUTO\n" + " : The TX data rate value to be set, valid values are:\n" + " Legacy : <1, 2, 55, 11, 6, 9, 12, 18, 24, 36, 48, 54>\n" + " Non-legacy: \n" + " AUTO: \n", + nrf_wifi_util_tx_rate, + 2, + 1), +#ifdef CONFIG_NRF_WIFI_LOW_POWER + SHELL_CMD_ARG(sleep_state, + NULL, + "Display current sleep status", + nrf_wifi_util_show_host_rpu_ps_ctrl_state, + 1, + 0), +#endif /* CONFIG_NRF_WIFI_LOW_POWER */ + SHELL_CMD_ARG(show_vers, + NULL, + "Display the driver and the firmware versions", + nrf_wifi_util_show_vers, + 1, + 0), +#ifndef CONFIG_NRF70_RADIO_TEST + SHELL_CMD_ARG(rpu_stats, + NULL, + "Display RPU stats " + "Parameters: umac or lmac or phy or all (default)", + nrf_wifi_util_dump_rpu_stats, + 1, + 1), +#endif /* CONFIG_NRF70_RADIO_TEST */ +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY + SHELL_CMD_ARG(rpu_recovery_test, + NULL, + "Trigger RPU recovery", + nrf_wifi_util_trigger_rpu_recovery, + 1, + 0), +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ + SHELL_SUBCMD_SET_END); + + +SHELL_CMD_REGISTER(wifi_util, + &nrf_wifi_util_subcmds, + "nRF Wi-Fi utility shell commands", + NULL); + + +static int nrf_wifi_util_init(void) +{ + + if (nrf_wifi_util_conf_init(&ctx->conf_params) < 0) + return -1; + + return 0; +} + + +SYS_INIT(nrf_wifi_util_init, + APPLICATION, + CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/drivers/wifi/nrfwifi/src/wifi_util.h b/drivers/wifi/nrfwifi/src/wifi_util.h new file mode 100644 index 000000000000..2ab188efed4b --- /dev/null +++ b/drivers/wifi/nrfwifi/src/wifi_util.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* @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/drivers/wifi/nrfwifi/src/work.c b/drivers/wifi/nrfwifi/src/work.c new file mode 100644 index 000000000000..dbc716a6eeb8 --- /dev/null +++ b/drivers/wifi/nrfwifi/src/work.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief File containing work specific definitions for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#include +#include +#include +#include + +#include "work.h" + +LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL); + +K_THREAD_STACK_DEFINE(bh_wq_stack_area, CONFIG_NRF70_BH_WQ_STACK_SIZE); +struct k_work_q zep_wifi_bh_q; + +K_THREAD_STACK_DEFINE(irq_wq_stack_area, CONFIG_NRF70_IRQ_WQ_STACK_SIZE); +struct k_work_q zep_wifi_intr_q; + +#ifdef CONFIG_NRF70_TX_DONE_WQ_ENABLED +K_THREAD_STACK_DEFINE(tx_done_wq_stack_area, CONFIG_NRF70_TX_DONE_WQ_STACK_SIZE); +struct k_work_q zep_wifi_tx_done_q; +#endif /* CONFIG_NRF70_TX_DONE_WQ_ENABLED */ + +#ifdef CONFIG_NRF70_RX_WQ_ENABLED +K_THREAD_STACK_DEFINE(rx_wq_stack_area, CONFIG_NRF70_RX_WQ_STACK_SIZE); +struct k_work_q zep_wifi_rx_q; +#endif /* CONFIG_NRF70_RX_WQ_ENABLED */ + +struct zep_work_item zep_work_item[CONFIG_NRF70_WORKQ_MAX_ITEMS]; + +int get_free_work_item_index(void) +{ + int i; + + for (i = 0; i < CONFIG_NRF70_WORKQ_MAX_ITEMS; i++) { + if (zep_work_item[i].in_use) + continue; + return i; + } + + return -1; +} + +void workqueue_callback(struct k_work *work) +{ + struct zep_work_item *item = CONTAINER_OF(work, struct zep_work_item, work); + + item->callback(item->data); +} + +struct zep_work_item *work_alloc(enum zep_work_type type) +{ + int free_work_index = get_free_work_item_index(); + + if (free_work_index < 0) { + LOG_ERR("%s: Reached maximum work items", __func__); + return NULL; + } + + zep_work_item[free_work_index].in_use = true; + zep_work_item[free_work_index].type = type; + + return &zep_work_item[free_work_index]; +} + +static int workqueue_init(void) +{ + k_work_queue_init(&zep_wifi_bh_q); + + k_work_queue_start(&zep_wifi_bh_q, + bh_wq_stack_area, + K_THREAD_STACK_SIZEOF(bh_wq_stack_area), + CONFIG_NRF70_BH_WQ_PRIORITY, + NULL); + + k_thread_name_set(&zep_wifi_bh_q.thread, "nrf70_bh_wq"); + + k_work_queue_init(&zep_wifi_intr_q); + + k_work_queue_start(&zep_wifi_intr_q, + irq_wq_stack_area, + K_THREAD_STACK_SIZEOF(irq_wq_stack_area), + CONFIG_NRF70_IRQ_WQ_PRIORITY, + NULL); + + k_thread_name_set(&zep_wifi_intr_q.thread, "nrf70_intr_wq"); +#ifdef CONFIG_NRF70_TX_DONE_WQ_ENABLED + k_work_queue_init(&zep_wifi_tx_done_q); + + k_work_queue_start(&zep_wifi_tx_done_q, + tx_done_wq_stack_area, + K_THREAD_STACK_SIZEOF(tx_done_wq_stack_area), + CONFIG_NRF70_TX_DONE_WQ_PRIORITY, + NULL); + + k_thread_name_set(&zep_wifi_tx_done_q.thread, "nrf70_tx_done_wq"); +#endif /* CONFIG_NRF70_TX_DONE_WQ_ENABLED */ + +#ifdef CONFIG_NRF70_RX_WQ_ENABLED + k_work_queue_init(&zep_wifi_rx_q); + + k_work_queue_start(&zep_wifi_rx_q, + rx_wq_stack_area, + K_THREAD_STACK_SIZEOF(rx_wq_stack_area), + CONFIG_NRF70_RX_WQ_PRIORITY, + NULL); + + k_thread_name_set(&zep_wifi_rx_q.thread, "nrf70_rx_wq"); +#endif /* CONFIG_NRF70_RX_WQ_ENABLED */ + + return 0; +} + +void work_init(struct zep_work_item *item, void (*callback)(unsigned long), + unsigned long data) +{ + item->callback = callback; + item->data = data; + + k_work_init(&item->work, workqueue_callback); +} + +void work_schedule(struct zep_work_item *item) +{ + if (item->type == ZEP_WORK_TYPE_IRQ) + k_work_submit_to_queue(&zep_wifi_intr_q, &item->work); + else if (item->type == ZEP_WORK_TYPE_BH) + k_work_submit_to_queue(&zep_wifi_bh_q, &item->work); +#ifdef CONFIG_NRF70_TX_DONE_WQ_ENABLED + else if (item->type == ZEP_WORK_TYPE_TX_DONE) + k_work_submit_to_queue(&zep_wifi_tx_done_q, &item->work); +#endif /* CONFIG_NRF70_TX_DONE_WQ_ENABLED */ +#ifdef CONFIG_NRF70_RX_WQ_ENABLED + else if (item->type == ZEP_WORK_TYPE_RX) + k_work_submit_to_queue(&zep_wifi_rx_q, &item->work); +#endif /* CONFIG_NRF70_RX_WQ_ENABLED */ +} + +void work_kill(struct zep_work_item *item) +{ + /* TODO: Based on context, use _sync version */ + k_work_cancel(&item->work); +} + +void work_free(struct zep_work_item *item) +{ + item->in_use = 0; +} + +SYS_INIT(workqueue_init, POST_KERNEL, 0); diff --git a/drivers/wifi/nrfwifi/src/work.h b/drivers/wifi/nrfwifi/src/work.h new file mode 100644 index 000000000000..57759fa5924e --- /dev/null +++ b/drivers/wifi/nrfwifi/src/work.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Header containing work specific declarations for the + * Zephyr OS layer of the Wi-Fi driver. + */ + +#ifndef __WORK_H__ +#define __WORK_H__ + + +extern struct k_work_q zep_wifi_bh_q; + +enum zep_work_type { + ZEP_WORK_TYPE_BH, + ZEP_WORK_TYPE_IRQ, + ZEP_WORK_TYPE_TX_DONE, + ZEP_WORK_TYPE_RX, +}; + +struct zep_work_item { + bool in_use; + struct k_work work; + unsigned long data; + void (*callback)(unsigned long data); + enum zep_work_type type; +}; + +struct zep_work_item *work_alloc(enum zep_work_type); + +void work_init(struct zep_work_item *work, void (*callback)(unsigned long callbk_data), + unsigned long data); + +void work_schedule(struct zep_work_item *work); + +void work_kill(struct zep_work_item *work); + +void work_free(struct zep_work_item *work); + +#endif /* __WORK_H__ */ diff --git a/drivers/wifi/nrfwifi/src/wpa_supp_if.c b/drivers/wifi/nrfwifi/src/wpa_supp_if.c new file mode 100644 index 000000000000..d99e4fa2952a --- /dev/null +++ b/drivers/wifi/nrfwifi/src/wpa_supp_if.c @@ -0,0 +1,2761 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief File containing WPA supplicant interface specific definitions for the + * Zephyr OS layer of the Wi-Fi driver. + */ +#include + +#include +#include + +#include "fmac_main.h" +#include "fmac_util.h" +#include "wifi_mgmt.h" +#include "wpa_supp_if.h" + +LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL); + +K_SEM_DEFINE(wait_for_event_sem, 0, 1); +K_SEM_DEFINE(wait_for_scan_resp_sem, 0, 1); +static K_MUTEX_DEFINE(mgmt_tx_lock); + +#define ACTION_FRAME_RESP_TIMEOUT_MS 5000 +#define SET_IFACE_EVENT_TIMEOUT_MS 5000 +#define CARR_ON_TIMEOUT_MS 5000 + +static int get_nrf_wifi_auth_type(int wpa_auth_alg) +{ + if (wpa_auth_alg & WPA_AUTH_ALG_OPEN) { + return NRF_WIFI_AUTHTYPE_OPEN_SYSTEM; + } + if (wpa_auth_alg & WPA_AUTH_ALG_SHARED) { + return NRF_WIFI_AUTHTYPE_SHARED_KEY; + } + if (wpa_auth_alg & WPA_AUTH_ALG_LEAP) { + return NRF_WIFI_AUTHTYPE_NETWORK_EAP; + } + if (wpa_auth_alg & WPA_AUTH_ALG_FT) { + return NRF_WIFI_AUTHTYPE_FT; + } + if (wpa_auth_alg & WPA_AUTH_ALG_SAE) { + return NRF_WIFI_AUTHTYPE_SAE; + } + + return NRF_WIFI_AUTHTYPE_MAX; +} + +static unsigned int wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len) +{ + switch (alg) { + case WPA_ALG_WEP: + if (key_len == 5) { + return RSN_CIPHER_SUITE_WEP40; + } + return RSN_CIPHER_SUITE_WEP104; + case WPA_ALG_TKIP: + return RSN_CIPHER_SUITE_TKIP; + case WPA_ALG_CCMP: + return RSN_CIPHER_SUITE_CCMP; + case WPA_ALG_GCMP: + return RSN_CIPHER_SUITE_GCMP; + case WPA_ALG_CCMP_256: + return RSN_CIPHER_SUITE_CCMP_256; + case WPA_ALG_GCMP_256: + return RSN_CIPHER_SUITE_GCMP_256; + case WPA_ALG_BIP_CMAC_128: + return RSN_CIPHER_SUITE_AES_128_CMAC; + case WPA_ALG_BIP_GMAC_128: + return RSN_CIPHER_SUITE_BIP_GMAC_128; + case WPA_ALG_BIP_GMAC_256: + return RSN_CIPHER_SUITE_BIP_GMAC_256; + case WPA_ALG_BIP_CMAC_256: + return RSN_CIPHER_SUITE_BIP_CMAC_256; + case WPA_ALG_SMS4: + return RSN_CIPHER_SUITE_SMS4; + case WPA_ALG_KRK: + return RSN_CIPHER_SUITE_KRK; + case WPA_ALG_NONE: + LOG_ERR("%s: Unexpected encryption algorithm %d", __func__, alg); + return 0; + } + + LOG_ERR("%s: Unsupported encryption algorithm %d", __func__, alg); + return 0; +} + +static enum chan_width drv2supp_chan_width(int width) +{ + switch (width) { + case NRF_WIFI_CHAN_WIDTH_20_NOHT: + return CHAN_WIDTH_20_NOHT; + case NRF_WIFI_CHAN_WIDTH_20: + return CHAN_WIDTH_20; + case NRF_WIFI_CHAN_WIDTH_40: + return CHAN_WIDTH_40; + case NRF_WIFI_CHAN_WIDTH_80: + return CHAN_WIDTH_80; + case NRF_WIFI_CHAN_WIDTH_80P80: + return CHAN_WIDTH_80P80; + case NRF_WIFI_CHAN_WIDTH_160: + return CHAN_WIDTH_160; + default: + break; + } + return CHAN_WIDTH_UNKNOWN; +} + +void nrf_wifi_wpa_supp_event_proc_scan_start(void *if_priv) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + vif_ctx_zep = if_priv; + + if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.scan_start) { + vif_ctx_zep->supp_callbk_fns.scan_start(vif_ctx_zep->supp_drv_if_ctx); + } +} + +void nrf_wifi_wpa_supp_event_proc_scan_done(void *if_priv, + struct nrf_wifi_umac_event_trigger_scan *scan_done_event, + unsigned int event_len, + int aborted) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + union wpa_event_data event; + struct scan_info *info = NULL; + + vif_ctx_zep = if_priv; + + memset(&event, 0, sizeof(event)); + + info = &event.scan_info; + + info->aborted = aborted; + info->external_scan = 0; + info->nl_scan_event = 1; + + if (vif_ctx_zep->supp_drv_if_ctx && + vif_ctx_zep->supp_callbk_fns.scan_done) { + vif_ctx_zep->supp_callbk_fns.scan_done(vif_ctx_zep->supp_drv_if_ctx, + &event); + } + k_work_cancel_delayable(&vif_ctx_zep->scan_timeout_work); + vif_ctx_zep->scan_in_progress = false; + k_sem_give(&wait_for_scan_resp_sem); +} + +void nrf_wifi_wpa_supp_event_proc_scan_res(void *if_priv, + struct nrf_wifi_umac_event_new_scan_results *scan_res, + unsigned int event_len, + bool more_res) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct wpa_scan_res *r = NULL; + const unsigned char *ie = NULL; + const unsigned char *beacon_ie = NULL; + unsigned int ie_len = 0; + unsigned int beacon_ie_len = 0; + unsigned char *pos = NULL; + + vif_ctx_zep = if_priv; + + if (scan_res->valid_fields & NRF_WIFI_EVENT_NEW_SCAN_RESULTS_IES_VALID) { + ie = scan_res->ies; + ie_len = scan_res->ies_len; + } + + if (scan_res->valid_fields & NRF_WIFI_EVENT_NEW_SCAN_RESULTS_BEACON_IES_VALID) { + beacon_ie = scan_res->ies + scan_res->ies_len; + beacon_ie_len = scan_res->beacon_ies_len; + } + + r = k_calloc(sizeof(*r) + ie_len + beacon_ie_len, sizeof(char)); + + if (!r) { + LOG_ERR("%s: Unable to allocate memory for scan result", __func__); + return; + } + + if (scan_res->valid_fields & NRF_WIFI_EVENT_NEW_SCAN_RESULTS_MAC_ADDR_VALID) { + memcpy(r->bssid, scan_res->mac_addr, ETH_ALEN); + } + + r->freq = scan_res->frequency; + + if (scan_res->valid_fields & NRF_WIFI_EVENT_NEW_SCAN_RESULTS_BEACON_INTERVAL_VALID) { + r->beacon_int = scan_res->beacon_interval; + } + + r->caps = scan_res->capability; + + r->flags |= WPA_SCAN_NOISE_INVALID; + + if (scan_res->signal.signal_type == NRF_WIFI_SIGNAL_TYPE_MBM) { + r->level = scan_res->signal.signal.mbm_signal; + r->level /= 100; /* mBm to dBm */ + r->flags |= (WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID); + } else if (scan_res->signal.signal_type == NRF_WIFI_SIGNAL_TYPE_UNSPEC) { + r->level = scan_res->signal.signal.unspec_signal; + r->flags |= WPA_SCAN_QUAL_INVALID; + } else { + r->flags |= (WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID); + } + + if (scan_res->valid_fields & NRF_WIFI_EVENT_NEW_SCAN_RESULTS_IES_TSF_VALID) { + r->tsf = scan_res->ies_tsf; + } + + if (scan_res->valid_fields & NRF_WIFI_EVENT_NEW_SCAN_RESULTS_BEACON_IES_TSF_VALID) { + if (scan_res->beacon_ies_tsf > r->tsf) { + r->tsf = scan_res->beacon_ies_tsf; + } + } + + if (scan_res->seen_ms_ago) { + r->age = scan_res->seen_ms_ago; + } + + r->ie_len = ie_len; + + pos = (unsigned char *)(r + 1); + + if (ie) { + memcpy(pos, ie, ie_len); + + pos += ie_len; + } + + r->beacon_ie_len = beacon_ie_len; + + if (beacon_ie) { + memcpy(pos, beacon_ie, beacon_ie_len); + } + + if (scan_res->valid_fields & NRF_WIFI_EVENT_NEW_SCAN_RESULTS_STATUS_VALID) { + switch (scan_res->status) { + case NRF_WIFI_BSS_STATUS_ASSOCIATED: + r->flags |= WPA_SCAN_ASSOCIATED; + break; + default: + break; + } + } + + if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.scan_res) { + vif_ctx_zep->supp_callbk_fns.scan_res(vif_ctx_zep->supp_drv_if_ctx, r, more_res); + } + + if (!more_res) { + vif_ctx_zep->scan_in_progress = false; + } + + k_free(r); +} + +void nrf_wifi_wpa_supp_event_proc_auth_resp(void *if_priv, + struct nrf_wifi_umac_event_mlme *auth_resp, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt = NULL; + const unsigned char *frame = NULL; + unsigned int frame_len = 0; + + vif_ctx_zep = if_priv; + + frame = auth_resp->frame.frame; + frame_len = auth_resp->frame.frame_len; + mgmt = (const struct ieee80211_mgmt *)frame; + + if (frame_len < 4 + (2 * NRF_WIFI_ETH_ADDR_LEN)) { + LOG_ERR("%s: MLME event too short", __func__); + return; + } + + + if (frame_len < 24 + sizeof(mgmt->u.auth)) { + LOG_ERR("%s: Authentication response frame too short", __func__); + return; + } + + memset(&event, 0, sizeof(event)); + + memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); + + event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); + + event.auth.auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + + event.auth.status_code = le_to_host16(mgmt->u.auth.status_code); + + if (frame_len > 24 + sizeof(mgmt->u.auth)) { + event.auth.ies = mgmt->u.auth.variable; + event.auth.ies_len = (frame_len - 24 - sizeof(mgmt->u.auth)); + } + + if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.auth_resp) { + vif_ctx_zep->supp_callbk_fns.auth_resp(vif_ctx_zep->supp_drv_if_ctx, &event); + } +} + +void nrf_wifi_wpa_supp_event_proc_assoc_resp(void *if_priv, + struct nrf_wifi_umac_event_mlme *assoc_resp, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt = NULL; + const unsigned char *frame = NULL; + unsigned int frame_len = 0; + unsigned short status = WLAN_STATUS_UNSPECIFIED_FAILURE; + + vif_ctx_zep = if_priv; + + frame = assoc_resp->frame.frame; + frame_len = assoc_resp->frame.frame_len; + mgmt = (const struct ieee80211_mgmt *)frame; + + if (frame_len < 24 + sizeof(mgmt->u.assoc_resp)) { + LOG_ERR("%s: Association response frame too short", __func__); + return; + } + + memset(&event, 0, sizeof(event)); + + status = le_to_host16(mgmt->u.assoc_resp.status_code); + + if (status != WLAN_STATUS_SUCCESS) { + event.assoc_reject.bssid = mgmt->bssid; + + if (frame_len > 24 + sizeof(mgmt->u.assoc_resp)) { + event.assoc_reject.resp_ies = (unsigned char *)mgmt->u.assoc_resp.variable; + event.assoc_reject.resp_ies_len = + (frame_len - 24 - sizeof(mgmt->u.assoc_resp)); + } + + event.assoc_reject.status_code = status; + } else { + event.assoc_info.addr = mgmt->bssid; + event.assoc_info.resp_frame = frame; + event.assoc_info.resp_frame_len = frame_len; + event.assoc_info.freq = vif_ctx_zep->assoc_freq; + + if (frame_len > 24 + sizeof(mgmt->u.assoc_resp)) { + event.assoc_info.resp_ies = (unsigned char *)mgmt->u.assoc_resp.variable; + event.assoc_info.resp_ies_len = + (frame_len - 24 - sizeof(mgmt->u.assoc_resp)); + } + + if (assoc_resp->req_ie_len > 0) { + event.assoc_info.req_ies = (unsigned char *)assoc_resp->req_ie; + event.assoc_info.req_ies_len = assoc_resp->req_ie_len; + } + + } + + if (vif_ctx_zep->supp_drv_if_ctx && + vif_ctx_zep->supp_callbk_fns.assoc_resp) { + vif_ctx_zep->supp_callbk_fns.assoc_resp(vif_ctx_zep->supp_drv_if_ctx, + &event, status); + } +} + +void nrf_wifi_wpa_supp_event_proc_deauth(void *if_priv, + struct nrf_wifi_umac_event_mlme *deauth, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt = NULL; + const unsigned char *frame = NULL; + unsigned int frame_len = 0; + + vif_ctx_zep = if_priv; + + frame = deauth->frame.frame; + frame_len = deauth->frame.frame_len; + mgmt = (const struct ieee80211_mgmt *)frame; + + if (frame_len < 24 + sizeof(mgmt->u.deauth)) { + LOG_ERR("%s: Deauthentication frame too short", __func__); + return; + } + + memset(&event, 0, sizeof(event)); + + event.deauth_info.addr = &mgmt->sa[0]; + event.deauth_info.reason_code = le_to_host16(mgmt->u.deauth.reason_code); + if (frame + frame_len > mgmt->u.deauth.variable) { + event.deauth_info.ie = mgmt->u.deauth.variable; + event.deauth_info.ie_len = (frame + frame_len - mgmt->u.deauth.variable); + } + + if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.deauth) { + vif_ctx_zep->supp_callbk_fns.deauth(vif_ctx_zep->supp_drv_if_ctx, + &event, mgmt); + } +} + +void nrf_wifi_wpa_supp_event_proc_disassoc(void *if_priv, + struct nrf_wifi_umac_event_mlme *disassoc, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt = NULL; + const unsigned char *frame = NULL; + unsigned int frame_len = 0; + + vif_ctx_zep = if_priv; + + frame = disassoc->frame.frame; + frame_len = disassoc->frame.frame_len; + mgmt = (const struct ieee80211_mgmt *)frame; + + if (frame_len < 24 + sizeof(mgmt->u.disassoc)) { + LOG_ERR("%s: Disassociation frame too short", __func__); + return; + } + + memset(&event, 0, sizeof(event)); + + event.disassoc_info.addr = &mgmt->sa[0]; + event.disassoc_info.reason_code = le_to_host16(mgmt->u.disassoc.reason_code); + if (frame + frame_len > mgmt->u.disassoc.variable) { + event.disassoc_info.ie = mgmt->u.disassoc.variable; + event.disassoc_info.ie_len = (frame + frame_len - mgmt->u.disassoc.variable); + } + + if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.disassoc) { + vif_ctx_zep->supp_callbk_fns.disassoc(vif_ctx_zep->supp_drv_if_ctx, &event); + } + + (void) nrf_wifi_twt_teardown_flows(vif_ctx_zep, 0, NRF_WIFI_MAX_TWT_FLOWS); +} + +void *nrf_wifi_wpa_supp_dev_init(void *supp_drv_if_ctx, const char *iface_name, + struct zep_wpa_supp_dev_callbk_fns *supp_callbk_fns) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + const struct device *device = DEVICE_DT_GET(DT_CHOSEN(zephyr_wifi)); + + if (!device) { + LOG_ERR("%s: Interface %s not found", __func__, iface_name); + return NULL; + } + + vif_ctx_zep = device->data; + + if (!vif_ctx_zep || !vif_ctx_zep->rpu_ctx_zep) { + LOG_ERR("%s: Interface %s not properly initialized", __func__, iface_name); + return NULL; + } + + /* Needed to make sure that during initialization, commands like setting regdomain + * does not access it. + */ + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + vif_ctx_zep->supp_drv_if_ctx = supp_drv_if_ctx; + + memcpy(&vif_ctx_zep->supp_callbk_fns, supp_callbk_fns, + sizeof(vif_ctx_zep->supp_callbk_fns)); + + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return vif_ctx_zep; +} + +void nrf_wifi_wpa_supp_dev_deinit(void *if_priv) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + vif_ctx_zep = if_priv; + + vif_ctx_zep->supp_drv_if_ctx = NULL; +} + +int nrf_wifi_wpa_supp_scan2(void *if_priv, struct wpa_driver_scan_params *params) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_umac_scan_info *scan_info = NULL; + int indx = 0; + int ret = -1; + int num_freqs = 0; + + if (!if_priv || !params) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep->rpu_ctx) { + LOG_ERR("%s: RPU context not initialized", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + if (vif_ctx_zep->scan_in_progress) { + LOG_ERR("%s: Scan already in progress", __func__); + ret = -EBUSY; + goto out; + } + + if (params->freqs) { + for (indx = 0; params->freqs[indx]; indx++) + num_freqs++; + } + + scan_info = k_calloc(sizeof(*scan_info) + (num_freqs * sizeof(unsigned int)), + sizeof(char)); + + if (!scan_info) { + LOG_ERR("%s: Unable to allocate memory for scan info", __func__); + ret = -ENOMEM; + goto out; + } + + memset(scan_info, 0x0, sizeof(*scan_info)); + + if (params->freqs) { + for (indx = 0; params->freqs[indx]; indx++) { + scan_info->scan_params.center_frequency[indx] = + params->freqs[indx]; + } + scan_info->scan_params.num_scan_channels = indx; + } + + if (params->filter_ssids) { + scan_info->scan_params.num_scan_ssids = params->num_filter_ssids; + + for (indx = 0; indx < params->num_filter_ssids; indx++) { + memcpy(scan_info->scan_params.scan_ssids[indx].nrf_wifi_ssid, + params->filter_ssids[indx].ssid, + params->filter_ssids[indx].ssid_len); + + scan_info->scan_params.scan_ssids[indx].nrf_wifi_ssid_len = + params->filter_ssids[indx].ssid_len; + } + } + + scan_info->scan_reason = SCAN_CONNECT; + + /* Copy extra_ies */ + if (params->extra_ies_len && params->extra_ies_len <= NRF_WIFI_MAX_IE_LEN) { + memcpy(scan_info->scan_params.ie.ie, params->extra_ies, params->extra_ies_len); + scan_info->scan_params.ie.ie_len = params->extra_ies_len; + } else if (params->extra_ies_len) { + LOG_ERR("%s: extra_ies_len %d is greater than max IE len %d", + __func__, params->extra_ies_len, NRF_WIFI_MAX_IE_LEN); + goto out; + } + + status = nrf_wifi_fmac_scan(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, scan_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Scan trigger failed", __func__); + goto out; + } + + vif_ctx_zep->scan_type = SCAN_CONNECT; + vif_ctx_zep->scan_in_progress = true; + + k_work_schedule(&vif_ctx_zep->scan_timeout_work, + K_SECONDS(CONFIG_WIFI_NRF70_SCAN_TIMEOUT_S)); + + ret = 0; +out: + if (scan_info) + k_free(scan_info); + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_supp_scan_abort(void *if_priv) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int sem_ret; + + vif_ctx_zep = if_priv; + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return -1; + } + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return -1; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + if (!vif_ctx_zep->scan_in_progress) { + LOG_ERR("%s:Ignore scan abort, no scan in progress", __func__); + goto out; + } + + status = nrf_wifi_fmac_abort_scan(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_abort_scan failed", __func__); + goto out; + } + + k_sem_reset(&wait_for_scan_resp_sem); + sem_ret = k_sem_take(&wait_for_scan_resp_sem, K_MSEC(RPU_RESP_EVENT_TIMEOUT)); + if (sem_ret) { + LOG_ERR("%s: Timedout waiting for scan abort response, ret = %d", + __func__, sem_ret); + status = NRF_WIFI_STATUS_FAIL; + goto out; + } + +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return status; +} + +int nrf_wifi_wpa_supp_scan_results_get(void *if_priv) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + int ret = -1; + + if (!if_priv) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + status = nrf_wifi_fmac_scan_res_get(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, + SCAN_CONNECT); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_scan_res_get failed", __func__); + goto out; + } + + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_supp_deauthenticate(void *if_priv, const char *addr, unsigned short reason_code) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_umac_disconn_info deauth_info; + int ret = -1; + + if ((!if_priv) || (!addr)) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + memset(&deauth_info, 0, sizeof(deauth_info)); + + deauth_info.reason_code = reason_code; + + memcpy(deauth_info.mac_addr, addr, sizeof(deauth_info.mac_addr)); + + status = nrf_wifi_fmac_deauth(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, &deauth_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_deauth failed", __func__); + goto out; + } + + ret = nrf_wifi_twt_teardown_flows(vif_ctx_zep, 0, NRF_WIFI_MAX_TWT_FLOWS); +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_supp_add_key(struct nrf_wifi_umac_key_info *key_info, enum wpa_alg alg, + int key_idx, + int defkey, const unsigned char *seq, size_t seq_len, + const unsigned char *key, size_t key_len) +{ + unsigned int suite = 0; + + suite = wpa_alg_to_cipher_suite(alg, key_len); + + if (!suite) { + return -1; + } + + if (defkey && alg == WPA_ALG_BIP_CMAC_128) { + key_info->nrf_wifi_flags = NRF_WIFI_KEY_DEFAULT_MGMT; + } else if (defkey) { + key_info->nrf_wifi_flags = NRF_WIFI_KEY_DEFAULT; + } + + key_info->key_idx = key_idx; + key_info->cipher_suite = suite; + + if (key && key_len) { + memcpy(key_info->key.nrf_wifi_key, key, key_len); + key_info->key.nrf_wifi_key_len = key_len; + } + if (seq && seq_len) { + memcpy(key_info->seq.nrf_wifi_seq, seq, seq_len); + key_info->seq.nrf_wifi_seq_len = seq_len; + } + + return 0; +} + +int nrf_wifi_wpa_supp_authenticate(void *if_priv, struct wpa_driver_auth_params *params, + struct wpa_bss *curr_bss) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_umac_auth_info auth_info; + int ret = -1; + int type; + int count = 0; + int max_ie_len = 0; + + if ((!if_priv) || (!params)) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + +#ifdef CONFIG_NRF70_RAW_DATA_RX + if (vif_ctx_zep->if_type == NRF_WIFI_IFTYPE_MONITOR) { + LOG_ERR("%s: Interface is in Monitor mode - cannot connect to a BSS", __func__); + goto out; + } +#endif /* CONFIG_NRF70_RAW_DATA_RX */ + + memset(&auth_info, 0, sizeof(auth_info)); + + + if (params->bssid) { + memcpy(auth_info.nrf_wifi_bssid, params->bssid, ETH_ALEN); + } + + if (params->freq) { + auth_info.frequency = params->freq; + } + + if (params->ssid) { + memcpy(auth_info.ssid.nrf_wifi_ssid, params->ssid, params->ssid_len); + + auth_info.ssid.nrf_wifi_ssid_len = params->ssid_len; + } + + if (params->ie) { + max_ie_len = (params->ie_len > NRF_WIFI_MAX_IE_LEN) ? + NRF_WIFI_MAX_IE_LEN : params->ie_len; + memcpy(&auth_info.ie.ie, params->ie, max_ie_len); + auth_info.ie.ie_len = max_ie_len; + } else { + auth_info.scan_width = 0; /* hard coded */ + auth_info.nrf_wifi_signal = curr_bss->level; + auth_info.capability = curr_bss->caps; + auth_info.beacon_interval = curr_bss->beacon_int; + auth_info.tsf = curr_bss->tsf; + auth_info.from_beacon = 0; /* hard coded */ + } + + if (params->auth_data) { + auth_info.sae.sae_data_len = params->auth_data_len; + + memcpy(auth_info.sae.sae_data, params->auth_data, params->auth_data_len); + } + + type = get_nrf_wifi_auth_type(params->auth_alg); + + if (type != NRF_WIFI_AUTHTYPE_MAX) { + auth_info.auth_type = type; + } + + if (type == NRF_WIFI_AUTHTYPE_SHARED_KEY) { + size_t key_len = params->wep_key_len[params->wep_tx_keyidx]; + struct nrf_wifi_umac_key_info *key_info = &auth_info.key_info; + + key_info->cipher_suite = wpa_alg_to_cipher_suite(params->auth_alg, key_len); + memcpy(key_info->key.nrf_wifi_key, + params->wep_key[params->wep_tx_keyidx], + key_len); + key_info->key.nrf_wifi_key_len = key_len; + key_info->valid_fields |= NRF_WIFI_KEY_VALID | NRF_WIFI_KEY_IDX_VALID | + NRF_WIFI_CIPHER_SUITE_VALID; + } + + if (params->local_state_change) { + auth_info.nrf_wifi_flags |= NRF_WIFI_CMD_AUTHENTICATE_LOCAL_STATE_CHANGE; + } + + status = nrf_wifi_fmac_auth(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, &auth_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: MLME command failed (auth): count=%d ret=%d", __func__, count, ret); + count++; + ret = -1; + } else { + LOG_DBG("%s:Authentication request sent successfully", __func__); + ret = 0; + } +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_supp_associate(void *if_priv, struct wpa_driver_associate_params *params) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_umac_assoc_info assoc_info; + int ret = -1; + + if ((!if_priv) || (!params)) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + memset(&assoc_info, 0, sizeof(assoc_info)); + + if (params->bssid) { + memcpy(assoc_info.nrf_wifi_bssid, params->bssid, sizeof(assoc_info.nrf_wifi_bssid)); + } + + if (params->freq.freq) { + assoc_info.center_frequency = params->freq.freq; + vif_ctx_zep->assoc_freq = params->freq.freq; + } else { + vif_ctx_zep->assoc_freq = 0; + } + + if (params->prev_bssid) { + assoc_info.prev_bssid_flag = 1; + memcpy(assoc_info.prev_bssid, params->prev_bssid, ETH_ALEN); + } + + if (params->ssid) { + assoc_info.ssid.nrf_wifi_ssid_len = params->ssid_len; + + memcpy(assoc_info.ssid.nrf_wifi_ssid, params->ssid, params->ssid_len); + + } + + if (params->wpa_ie) { + assoc_info.wpa_ie.ie_len = params->wpa_ie_len; + memcpy(assoc_info.wpa_ie.ie, params->wpa_ie, params->wpa_ie_len); + } + + assoc_info.control_port = 1; + + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED) { + assoc_info.use_mfp = NRF_WIFI_MFP_REQUIRED; + } + + if (params->bss_max_idle_period) { + assoc_info.bss_max_idle_time = params->bss_max_idle_period; + } + + status = nrf_wifi_fmac_assoc(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, &assoc_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: MLME command failed (assoc)", __func__); + } else { + LOG_DBG("%s: Association request sent successfully", __func__); + ret = 0; + } + +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_supp_set_key(void *if_priv, const unsigned char *ifname, enum wpa_alg alg, + const unsigned char *addr, int key_idx, int set_tx, + const unsigned char *seq, size_t seq_len, const unsigned char *key, + size_t key_len, enum key_flag key_flag) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_umac_key_info key_info; + const unsigned char *mac_addr = NULL; + unsigned int suite; + int ret = -1; + + + if ((!if_priv) || (!ifname)) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep->rpu_ctx) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + /* Can happen in a positive case where "net if down" is completed, but WPA + * supplicant is still deleting keys. + */ + if (!rpu_ctx_zep->rpu_ctx) { + goto out; + } + + memset(&key_info, 0, sizeof(key_info)); + + if (alg != WPA_ALG_NONE) { + suite = wpa_alg_to_cipher_suite(alg, key_len); + + if (!suite) { + goto out; + } + + memcpy(key_info.key.nrf_wifi_key, key, key_len); + + key_info.key.nrf_wifi_key_len = key_len; + key_info.cipher_suite = suite; + + key_info.valid_fields |= (NRF_WIFI_CIPHER_SUITE_VALID | NRF_WIFI_KEY_VALID); + } + + if (seq && seq_len) { + memcpy(key_info.seq.nrf_wifi_seq, seq, seq_len); + + key_info.seq.nrf_wifi_seq_len = seq_len; + key_info.valid_fields |= NRF_WIFI_SEQ_VALID; + } + + + /* TODO: Implement/check set_tx */ + if (addr && !is_broadcast_ether_addr(addr)) { + mac_addr = addr; + key_info.key_type = NRF_WIFI_KEYTYPE_PAIRWISE; + key_info.valid_fields |= NRF_WIFI_KEY_TYPE_VALID; + } else if (addr && is_broadcast_ether_addr(addr)) { + mac_addr = NULL; + key_info.key_type = NRF_WIFI_KEYTYPE_GROUP; + key_info.valid_fields |= NRF_WIFI_KEY_TYPE_VALID; + key_info.nrf_wifi_flags |= NRF_WIFI_KEY_DEFAULT_TYPE_MULTICAST; + } + + key_info.key_idx = key_idx; + key_info.valid_fields |= NRF_WIFI_KEY_IDX_VALID; + + if (alg == WPA_ALG_NONE) { + status = nrf_wifi_fmac_del_key(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, + &key_info, mac_addr); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_del_key failed", __func__); + } else { + ret = 0; + } + } else { + status = nrf_wifi_fmac_add_key(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, + &key_info, mac_addr); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_add_key failed", __func__); + } else { + ret = 0; + } + } + + /* + * If we failed or don't need to set the default TX key (below), + * we're done here. + */ + if (ret || !set_tx || alg == WPA_ALG_NONE) { + goto out; + } + + + memset(&key_info, 0, sizeof(key_info)); + + key_info.key_idx = key_idx; + key_info.valid_fields |= NRF_WIFI_KEY_IDX_VALID; + + if (alg == WPA_ALG_BIP_CMAC_128 || alg == WPA_ALG_BIP_GMAC_128 || + alg == WPA_ALG_BIP_GMAC_256 || alg == WPA_ALG_BIP_CMAC_256) { + key_info.nrf_wifi_flags = NRF_WIFI_KEY_DEFAULT_MGMT; + } else { + key_info.nrf_wifi_flags = NRF_WIFI_KEY_DEFAULT; + } + + if (addr && is_broadcast_ether_addr(addr)) { + key_info.nrf_wifi_flags |= NRF_WIFI_KEY_DEFAULT_TYPE_MULTICAST; + } else if (addr) { + key_info.nrf_wifi_flags |= NRF_WIFI_KEY_DEFAULT_TYPE_UNICAST; + } + + status = nrf_wifi_fmac_set_key(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, &key_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_set_key failed", __func__); + ret = -1; + } else { + ret = 0; + } +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_set_supp_port(void *if_priv, int authorized, char *bssid) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_umac_chg_sta_info chg_sta_info; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int ret = -1; + + if (!if_priv || !bssid) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + if (vif_ctx_zep->if_op_state != NRF_WIFI_FMAC_IF_OP_STATE_UP) { + LOG_DBG("%s: Interface not UP, ignoring", __func__); + ret = 0; + goto out; + } + + memset(&chg_sta_info, 0x0, sizeof(chg_sta_info)); + + memcpy(chg_sta_info.mac_addr, bssid, ETH_ALEN); + + vif_ctx_zep->authorized = authorized; + + if (authorized) { + /* BIT(NL80211_STA_FLAG_AUTHORIZED) */ + chg_sta_info.sta_flags2.nrf_wifi_mask = 1 << 1; + /* BIT(NL80211_STA_FLAG_AUTHORIZED) */ + chg_sta_info.sta_flags2.nrf_wifi_set = 1 << 1; + } + + status = nrf_wifi_fmac_chg_sta(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, &chg_sta_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_chg_sta failed", __func__); + ret = -1; + goto out; + } + + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_supp_signal_poll(void *if_priv, struct wpa_signal_info *si, unsigned char *bssid) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + enum nrf_wifi_status ret = NRF_WIFI_STATUS_FAIL; + int sem_ret; + int rssi_record_elapsed_time_ms = 0; + + if (!if_priv || !si || !bssid) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + + vif_ctx_zep->signal_info = si; + + rssi_record_elapsed_time_ms = nrf_wifi_osal_time_elapsed_us(fmac_dev_ctx->fpriv->opriv, + vif_ctx_zep->rssi_record_timestamp_us) / 1000; + + if (rssi_record_elapsed_time_ms > CONFIG_NRF70_RSSI_STALE_TIMEOUT_MS) { + ret = nrf_wifi_fmac_get_station(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, bssid); + if (ret != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Failed to send get station info command", __func__); + goto out; + } + + sem_ret = k_sem_take(&wait_for_event_sem, K_MSEC(RPU_RESP_EVENT_TIMEOUT)); + if (sem_ret) { + LOG_ERR("%s: Failed to get station info, ret = %d", __func__, sem_ret); + ret = NRF_WIFI_STATUS_FAIL; + goto out; + } + } else { + si->current_signal = (int)vif_ctx_zep->rssi; + } + + ret = nrf_wifi_fmac_get_interface(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx); + if (ret != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Failed to send get interface info command", __func__); + goto out; + } + + sem_ret = k_sem_take(&wait_for_event_sem, K_MSEC(RPU_RESP_EVENT_TIMEOUT)); + if (sem_ret) { + LOG_ERR("%s: Failed to get interface info, ret = %d", __func__, sem_ret); + ret = NRF_WIFI_STATUS_FAIL; + goto out; + } + vif_ctx_zep->signal_info->frequency = vif_ctx_zep->assoc_freq; +out: + vif_ctx_zep->signal_info = NULL; + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +void nrf_wifi_wpa_supp_event_proc_get_sta(void *if_priv, + struct nrf_wifi_umac_event_new_station *info, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct wpa_signal_info *signal_info = NULL; + + if (!if_priv || !info) { + LOG_ERR("%s: Invalid params", __func__); + goto out; + } + + vif_ctx_zep = if_priv; + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + goto out; + } + +#ifdef CONFIG_NRF70_AP_MODE + vif_ctx_zep->inactive_time_sec = info->sta_info.inactive_time; +#endif /* CONFIG_NRF70_AP_MODE */ + + signal_info = vif_ctx_zep->signal_info; + /* Semaphore timedout */ + if (!signal_info) { + goto out; + } + + if (info->sta_info.valid_fields & NRF_WIFI_STA_INFO_SIGNAL_VALID) { + signal_info->current_signal = info->sta_info.signal; + } else { + signal_info->current_signal = -WPA_INVALID_NOISE; + } + + if (info->sta_info.valid_fields & NRF_WIFI_STA_INFO_SIGNAL_AVG_VALID) { + signal_info->avg_signal = info->sta_info.signal_avg; + } else { + signal_info->avg_signal = -WPA_INVALID_NOISE; + } + + if (info->sta_info.valid_fields & NRF_WIFI_STA_INFO_RX_BEACON_SIGNAL_AVG_VALID) { + signal_info->avg_beacon_signal = info->sta_info.rx_beacon_signal_avg; + } else { + signal_info->avg_beacon_signal = -WPA_INVALID_NOISE; + } + + signal_info->current_txrate = 0; + + if (info->sta_info.valid_fields & NRF_WIFI_STA_INFO_TX_BITRATE_VALID) { + if (info->sta_info.tx_bitrate.valid_fields & NRF_WIFI_RATE_INFO_BITRATE_VALID) { + signal_info->current_txrate = info->sta_info.tx_bitrate.bitrate * 100; + } + } +out: + k_sem_give(&wait_for_event_sem); +} + +void nrf_wifi_wpa_supp_event_proc_get_if(void *if_priv, + struct nrf_wifi_interface_info *info, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_chan_definition *chan_def_info = NULL; + struct wpa_signal_info *signal_info = NULL; + + if (!if_priv || !info) { + LOG_ERR("%s: Invalid params", __func__); + k_sem_give(&wait_for_event_sem); + return; + } + + vif_ctx_zep = if_priv; + signal_info = vif_ctx_zep->signal_info; + + /* Semaphore timedout */ + if (!signal_info) { + LOG_DBG("%s: Get interface Semaphore timedout", __func__); + return; + } + + chan_def_info = (struct nrf_wifi_chan_definition *)(&info->chan_def); + signal_info->chanwidth = drv2supp_chan_width(chan_def_info->width); + signal_info->center_frq1 = chan_def_info->center_frequency1; + signal_info->center_frq2 = chan_def_info->center_frequency2; + + k_sem_give(&wait_for_event_sem); +} + +void nrf_wifi_wpa_supp_event_mgmt_tx_status(void *if_priv, + struct nrf_wifi_umac_event_mlme *mlme_event, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + bool acked = false; + + if (!if_priv) { + LOG_ERR("%s: Missing interface context", __func__); + return; + } + + vif_ctx_zep = if_priv; + + if (!mlme_event) { + LOG_ERR("%s: Missing MLME event data", __func__); + return; + } + + acked = mlme_event->nrf_wifi_flags & NRF_WIFI_EVENT_MLME_ACK ? true : false; + LOG_DBG("%s: Mgmt frame %llx tx status: %s", + __func__, mlme_event->cookie, acked ? "ACK" : "NOACK"); + + if (vif_ctx_zep->supp_drv_if_ctx && + vif_ctx_zep->supp_callbk_fns.mgmt_tx_status) { + vif_ctx_zep->supp_callbk_fns.mgmt_tx_status(vif_ctx_zep->supp_drv_if_ctx, + mlme_event->frame.frame, + mlme_event->frame.frame_len, + acked); + } +} + +void nrf_wifi_wpa_supp_event_proc_unprot_mgmt(void *if_priv, + struct nrf_wifi_umac_event_mlme *unprot_mgmt, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt = NULL; + const unsigned char *frame = NULL; + unsigned int frame_len = 0; + int cmd_evnt = 0; + + vif_ctx_zep = if_priv; + + frame = unprot_mgmt->frame.frame; + frame_len = unprot_mgmt->frame.frame_len; + + mgmt = (const struct ieee80211_mgmt *)frame; + cmd_evnt = ((struct nrf_wifi_umac_hdr *)unprot_mgmt)->cmd_evnt; + + if (frame_len < 24 + sizeof(mgmt->u.deauth)) { + LOG_ERR("%s: Unprotected mgmt frame too short", __func__); + return; + } + + memset(&event, 0, sizeof(event)); + + event.unprot_deauth.sa = &mgmt->sa[0]; + event.unprot_deauth.da = &mgmt->da[0]; + + if (cmd_evnt == NRF_WIFI_UMAC_EVENT_UNPROT_DEAUTHENTICATE) { + event.unprot_deauth.reason_code = le_to_host16(mgmt->u.deauth.reason_code); + if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.unprot_deauth) { + vif_ctx_zep->supp_callbk_fns.unprot_deauth(vif_ctx_zep->supp_drv_if_ctx, + &event); + } + } else if (cmd_evnt == NRF_WIFI_UMAC_EVENT_UNPROT_DISASSOCIATE) { + event.unprot_disassoc.reason_code = le_to_host16(mgmt->u.deauth.reason_code); + if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.unprot_disassoc) { + vif_ctx_zep->supp_callbk_fns.unprot_disassoc(vif_ctx_zep->supp_drv_if_ctx, + &event); + } + } +} + +int nrf_wifi_nl80211_send_mlme(void *if_priv, const u8 *data, + size_t data_len, int noack, + unsigned int freq, int no_cck, + int offchanok, + unsigned int wait_time, + int cookie) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_umac_mgmt_tx_info *mgmt_tx_info = NULL; + unsigned int timeout = 0; + + if (!if_priv || !data) { + LOG_ERR("%s: Invalid params", __func__); + return -1; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return -1; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + k_mutex_lock(&mgmt_tx_lock, K_FOREVER); + + mgmt_tx_info = k_calloc(sizeof(*mgmt_tx_info), sizeof(char)); + + if (!mgmt_tx_info) { + LOG_ERR("%s: Unable to allocate memory", __func__); + goto out; + } + + if (offchanok) + mgmt_tx_info->nrf_wifi_flags |= NRF_WIFI_CMD_FRAME_OFFCHANNEL_TX_OK; + + if (noack) + mgmt_tx_info->nrf_wifi_flags |= NRF_WIFI_CMD_FRAME_DONT_WAIT_FOR_ACK; + + if (no_cck) + mgmt_tx_info->nrf_wifi_flags |= NRF_WIFI_CMD_FRAME_TX_NO_CCK_RATE; + + if (freq) + mgmt_tx_info->frequency = freq; + + if (wait_time) + mgmt_tx_info->dur = wait_time; + + if (data_len) { + memcpy(mgmt_tx_info->frame.frame, data, data_len); + mgmt_tx_info->frame.frame_len = data_len; + } + + mgmt_tx_info->freq_params.frequency = freq; + mgmt_tx_info->freq_params.channel_width = NRF_WIFI_CHAN_WIDTH_20; + mgmt_tx_info->freq_params.center_frequency1 = freq; + mgmt_tx_info->freq_params.center_frequency2 = 0; + mgmt_tx_info->freq_params.channel_type = NRF_WIFI_CHAN_HT20; + + /* Going to RPU */ + mgmt_tx_info->host_cookie = cookie; + vif_ctx_zep->cookie_resp_received = false; + + LOG_DBG("%s: Sending frame to RPU: cookie=%d wait_time=%d no_ack=%d", __func__, + cookie, wait_time, noack); + status = nrf_wifi_fmac_mgmt_tx(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + mgmt_tx_info); + + if (status == NRF_WIFI_STATUS_FAIL) { + LOG_ERR("%s: nrf_wifi_fmac_mgmt_tx failed", __func__); + goto out; + } + + /* Both are needed as we use this to send_action where noack is hardcoded + * to 0 always. + */ + if (wait_time || !noack) { + if (!noack && !wait_time) { + wait_time = ACTION_FRAME_RESP_TIMEOUT_MS; + } + + while (!vif_ctx_zep->cookie_resp_received && + timeout++ < wait_time) { + k_sleep(K_MSEC(1)); + } + + if (!vif_ctx_zep->cookie_resp_received) { + LOG_ERR("%s: cookie response not received (%dms)", __func__, + timeout); + status = NRF_WIFI_STATUS_FAIL; + goto out; + } + status = NRF_WIFI_STATUS_SUCCESS; + } + +out: + if (mgmt_tx_info) + k_free(mgmt_tx_info); + k_mutex_unlock(&mgmt_tx_lock); + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return status; +} + +enum nrf_wifi_status nrf_wifi_parse_sband( + struct nrf_wifi_event_supported_band *event, + struct wpa_supp_event_supported_band *band + ) +{ + int count; + + if (event && (event->nrf_wifi_n_bitrates == 0 || event->nrf_wifi_n_channels == 0)) { + return NRF_WIFI_STATUS_FAIL; + } + memset(band, 0, sizeof(*band)); + + band->wpa_supp_n_channels = event->nrf_wifi_n_channels; + band->wpa_supp_n_bitrates = event->nrf_wifi_n_bitrates; + + for (count = 0; count < band->wpa_supp_n_channels; count++) { + struct wpa_supp_event_channel *chan = &band->channels[count]; + + if (count >= WPA_SUPP_SBAND_MAX_CHANNELS) { + LOG_ERR("%s: Failed to add channel", __func__); + break; + } + + chan->wpa_supp_flags = event->channels[count].nrf_wifi_flags; + chan->wpa_supp_max_power = event->channels[count].nrf_wifi_max_power; + chan->wpa_supp_time = event->channels[count].nrf_wifi_time; + chan->dfs_cac_msec = event->channels[count].dfs_cac_msec; + chan->ch_valid = event->channels[count].ch_valid; + chan->center_frequency = event->channels[count].center_frequency; + chan->dfs_state = event->channels[count].dfs_state; + } + + for (count = 0; count < band->wpa_supp_n_bitrates; count++) { + struct wpa_supp_event_rate *rate = &band->bitrates[count]; + + if (count >= WPA_SUPP_SBAND_MAX_RATES) { + LOG_ERR("%s: Failed to add bitrate", __func__); + break; + } + + rate->wpa_supp_flags = event->bitrates[count].nrf_wifi_flags; + rate->wpa_supp_bitrate = event->bitrates[count].nrf_wifi_bitrate; + } + + band->ht_cap.wpa_supp_ht_supported = event->ht_cap.nrf_wifi_ht_supported; + band->ht_cap.wpa_supp_cap = event->ht_cap.nrf_wifi_cap; + band->ht_cap.mcs.wpa_supp_rx_highest = event->ht_cap.mcs.nrf_wifi_rx_highest; + + for (count = 0; count < WPA_SUPP_HT_MCS_MASK_LEN; count++) { + band->ht_cap.mcs.wpa_supp_rx_mask[count] = + event->ht_cap.mcs.nrf_wifi_rx_mask[count]; + } + + band->ht_cap.mcs.wpa_supp_tx_params = event->ht_cap.mcs.nrf_wifi_tx_params; + + for (count = 0; count < NRF_WIFI_HT_MCS_RES_LEN; count++) { + + if (count >= WPA_SUPP_HT_MCS_RES_LEN) { + LOG_ERR("%s: Failed to add reserved bytes", __func__); + break; + } + + band->ht_cap.mcs.wpa_supp_reserved[count] = + event->ht_cap.mcs.nrf_wifi_reserved[count]; + } + + band->ht_cap.wpa_supp_ampdu_factor = event->ht_cap.nrf_wifi_ampdu_factor; + band->ht_cap.wpa_supp_ampdu_density = event->ht_cap.nrf_wifi_ampdu_density; + + band->vht_cap.wpa_supp_vht_supported = event->vht_cap.nrf_wifi_vht_supported; + band->vht_cap.wpa_supp_cap = event->vht_cap.nrf_wifi_cap; + + band->vht_cap.vht_mcs.rx_mcs_map = event->vht_cap.vht_mcs.rx_mcs_map; + band->vht_cap.vht_mcs.rx_highest = event->vht_cap.vht_mcs.rx_highest; + band->vht_cap.vht_mcs.tx_mcs_map = event->vht_cap.vht_mcs.tx_mcs_map; + band->vht_cap.vht_mcs.tx_highest = event->vht_cap.vht_mcs.tx_highest; + + band->band = event->band; + + return WLAN_STATUS_SUCCESS; +} + +void nrf_wifi_wpa_supp_event_get_wiphy(void *if_priv, + struct nrf_wifi_event_get_wiphy *wiphy_info, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct wpa_supp_event_supported_band band; + + if (!if_priv || !wiphy_info || !event_len) { + LOG_ERR("%s: Invalid parameters", __func__); + return; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + + memset(&band, 0, sizeof(band)); + + for (int i = 0; i < NRF_WIFI_EVENT_GET_WIPHY_NUM_BANDS; i++) { + if (nrf_wifi_parse_sband(&wiphy_info->sband[i], &band) != WLAN_STATUS_SUCCESS) { + continue; + } + if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.get_wiphy_res) { + vif_ctx_zep->supp_callbk_fns.get_wiphy_res(vif_ctx_zep->supp_drv_if_ctx, + &band); + } + } + + if ((wiphy_info->params_valid & NRF_WIFI_GET_WIPHY_VALID_EXTENDED_CAPABILITIES) && + rpu_ctx_zep->extended_capa == NULL) { + + rpu_ctx_zep->extended_capa = k_malloc(wiphy_info->extended_capabilities_len); + + if (rpu_ctx_zep->extended_capa) { + memcpy(rpu_ctx_zep->extended_capa, wiphy_info->extended_capabilities, + wiphy_info->extended_capabilities_len); + } + + rpu_ctx_zep->extended_capa_mask = k_malloc(wiphy_info->extended_capabilities_len); + + if (rpu_ctx_zep->extended_capa_mask) { + memcpy(rpu_ctx_zep->extended_capa_mask, + wiphy_info->extended_capabilities_mask, + wiphy_info->extended_capabilities_len); + } else { + free(rpu_ctx_zep->extended_capa); + rpu_ctx_zep->extended_capa = NULL; + rpu_ctx_zep->extended_capa_len = 0; + } + } + + if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.get_wiphy_res) { + vif_ctx_zep->supp_callbk_fns.get_wiphy_res(vif_ctx_zep->supp_drv_if_ctx, NULL); + } +} + +int nrf_wifi_supp_get_wiphy(void *if_priv) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + + if (!if_priv) { + LOG_ERR("%s: Missing interface context", __func__); + return -1; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return -1; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + status = nrf_wifi_fmac_get_wiphy(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_get_wiphy failed", __func__); + goto out; + } +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return status; +} + +int nrf_wifi_supp_register_frame(void *if_priv, + u16 type, const u8 *match, size_t match_len, + bool multicast) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_umac_mgmt_frame_info frame_info; + + if (!if_priv || !match || !match_len) { + LOG_ERR("%s: Invalid parameters", __func__); + return -1; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return -1; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + memset(&frame_info, 0, sizeof(frame_info)); + + frame_info.frame_type = type; + frame_info.frame_match.frame_match_len = match_len; + memcpy(frame_info.frame_match.frame_match, match, match_len); + + status = nrf_wifi_fmac_register_frame(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, + &frame_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_register_frame failed", __func__); + goto out; + } +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return status; +} + +void nrf_wifi_wpa_supp_event_mgmt_rx_callbk_fn(void *if_priv, + struct nrf_wifi_umac_event_mlme *mlme_event, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + if (!if_priv) { + LOG_ERR("%s: Missing interface context", __func__); + return; + } + + vif_ctx_zep = if_priv; + + if (!mlme_event || !event_len) { + LOG_ERR("%s: Missing MLME event data", __func__); + return; + } + + if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.mgmt_rx) { + vif_ctx_zep->supp_callbk_fns.mgmt_rx(vif_ctx_zep->supp_drv_if_ctx, + mlme_event->frame.frame, + mlme_event->frame.frame_len, + mlme_event->frequency, + mlme_event->rx_signal_dbm); + } +} + +int nrf_wifi_supp_get_capa(void *if_priv, struct wpa_driver_capa *capa) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_SUCCESS; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + + if (!if_priv || !capa) { + LOG_ERR("%s: Invalid parameters", __func__); + return -1; + } + + memset(capa, 0, sizeof(*capa)); + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return -1; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + /* TODO: Get these from RPU*/ + /* Use SME */ + capa->flags = 0; + capa->flags |= WPA_DRIVER_FLAGS_SME; + capa->flags |= WPA_DRIVER_FLAGS_SAE; + capa->flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; + capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM; + capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT; + if (IS_ENABLED(CONFIG_NRF70_AP_MODE)) { + capa->flags |= WPA_DRIVER_FLAGS_AP; + } + + capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP | + WPA_DRIVER_CAPA_ENC_CCMP | + WPA_DRIVER_CAPA_ENC_CCMP_256 | + WPA_DRIVER_CAPA_ENC_GCMP_256; + + if (rpu_ctx_zep->extended_capa && rpu_ctx_zep->extended_capa_mask) { + capa->extended_capa = rpu_ctx_zep->extended_capa; + capa->extended_capa_mask = rpu_ctx_zep->extended_capa_mask; + capa->extended_capa_len = rpu_ctx_zep->extended_capa_len; + } +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return status; +} + + +void nrf_wifi_wpa_supp_event_mac_chgd(void *if_priv) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + if (!if_priv) { + LOG_ERR("%s: Invalid parameters", __func__); + return; + } + + vif_ctx_zep = if_priv; + + if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.mac_changed) { + vif_ctx_zep->supp_callbk_fns.mac_changed(vif_ctx_zep->supp_drv_if_ctx); + } + +} + + +int nrf_wifi_supp_get_conn_info(void *if_priv, struct wpa_conn_info *info) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + enum nrf_wifi_status ret = NRF_WIFI_STATUS_FAIL; + int sem_ret; + + if (!if_priv || !info) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + + vif_ctx_zep->conn_info = info; + ret = nrf_wifi_fmac_get_conn_info(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx); + if (ret != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_get_conn_info failed", __func__); + goto out; + } + + sem_ret = k_sem_take(&wait_for_event_sem, K_MSEC(RPU_RESP_EVENT_TIMEOUT)); + if (sem_ret) { + LOG_ERR("%s: Timeout: failed to get connection info, ret = %d", __func__, sem_ret); + ret = NRF_WIFI_STATUS_FAIL; + goto out; + } + +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + + +void nrf_wifi_supp_event_proc_get_conn_info(void *if_priv, + struct nrf_wifi_umac_event_conn_info *info, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct wpa_conn_info *conn_info = NULL; + + if (!if_priv || !info) { + LOG_ERR("%s: Invalid params", __func__); + k_sem_give(&wait_for_event_sem); + return; + } + vif_ctx_zep = if_priv; + conn_info = vif_ctx_zep->conn_info; + + conn_info->beacon_interval = info->beacon_interval; + conn_info->dtim_period = info->dtim_interval; + conn_info->twt_capable = info->twt_capable; + k_sem_give(&wait_for_event_sem); +} + +#ifdef CONFIG_NRF70_AP_MODE +static int nrf_wifi_vif_state_change(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep, + enum nrf_wifi_fmac_if_op_state state) +{ + struct nrf_wifi_umac_chg_vif_state_info vif_state_info = {0}; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + unsigned int timeout = 0; + struct nrf_wifi_fmac_vif_ctx *vif_ctx = NULL; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + int ret = -1; + + if (!vif_ctx_zep) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + def_dev_ctx = wifi_dev_priv(rpu_ctx_zep->rpu_ctx); + vif_ctx = def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]; + + vif_state_info.state = state; + vif_state_info.if_index = vif_ctx_zep->vif_idx; + vif_ctx->ifflags = false; + status = nrf_wifi_fmac_chg_vif_state(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + &vif_state_info); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_chg_vif_state failed", + __func__); + goto out; + } + + while (!vif_ctx->ifflags && timeout++ < SET_IFACE_EVENT_TIMEOUT_MS) { + k_sleep(K_MSEC(1)); + } + + if (!vif_ctx->ifflags) { + LOG_ERR("%s: set interface state event not received (%dms)", __func__, timeout); + goto out; + } + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +static int nrf_wifi_iftype_change(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep, int iftype) +{ + struct nrf_wifi_umac_chg_vif_attr_info chg_vif_info = {0}; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + int ret = -1; + unsigned int timeout = 0; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + + if (!vif_ctx_zep) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + ret = nrf_wifi_vif_state_change(vif_ctx_zep, NRF_WIFI_FMAC_IF_OP_STATE_DOWN); + if (ret) { + LOG_ERR("%s: Failed to set interface down", __func__); + goto out; + } + + chg_vif_info.iftype = iftype; + vif_ctx_zep->set_if_event_received = false; + vif_ctx_zep->set_if_status = 0; + status = nrf_wifi_fmac_chg_vif(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, &chg_vif_info); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_chg_vif failed", __func__); + goto out; + } + + while (!vif_ctx_zep->set_if_event_received && + timeout++ < SET_IFACE_EVENT_TIMEOUT_MS) { + k_sleep(K_MSEC(1)); + } + + if (!vif_ctx_zep->set_if_event_received) { + LOG_ERR("%s: set interface event not received (%dms)", __func__, timeout); + goto out; + } + + if (vif_ctx_zep->set_if_status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: set interface failed: %d", __func__, vif_ctx_zep->set_if_status); + goto out; + } + + ret = nrf_wifi_vif_state_change(vif_ctx_zep, NRF_WIFI_FMAC_IF_OP_STATE_UP); + if (ret) { + LOG_ERR("%s: Failed to set interface up", __func__); + goto out; + } + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +static int nrf_wifi_wait_for_carrier_status(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep, + enum nrf_wifi_fmac_if_carr_state carrier_status) +{ + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + int ret = -1; + unsigned int timeout = 0; + + if (!vif_ctx_zep) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + while (vif_ctx_zep->if_carr_state != carrier_status && + timeout++ < CARR_ON_TIMEOUT_MS) { + k_sleep(K_MSEC(1)); + } + + if (vif_ctx_zep->if_carr_state != carrier_status) { + LOG_ERR("%s: Carrier %s event not received in %dms", __func__, + carrier_status == NRF_WIFI_FMAC_IF_CARR_STATE_ON ? "ON" : "OFF", + timeout); + goto out; + } + + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_supp_init_ap(void *if_priv, struct wpa_driver_associate_params *params) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + + int ret = -1; + + if (!if_priv || !params) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + if (params->mode != IEEE80211_MODE_AP) { + LOG_ERR("%s: Invalid mode", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + ret = nrf_wifi_iftype_change(vif_ctx_zep, NRF_WIFI_IFTYPE_AP); + if (ret) { + LOG_ERR("%s: Failed to set interface type to AP: %d", __func__, ret); + goto out; + } + +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +static int wpas_cipher_to_nrf(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_NONE: + return 0; + case WPA_CIPHER_WEP40: + return NRF_WIFI_FMAC_CIPHER_SUITE_WEP40; + case WPA_CIPHER_WEP104: + return NRF_WIFI_FMAC_CIPHER_SUITE_WEP104; + case WPA_CIPHER_TKIP: + return NRF_WIFI_FMAC_CIPHER_SUITE_TKIP; + case WPA_CIPHER_CCMP: + return NRF_WIFI_FMAC_CIPHER_SUITE_CCMP; + case WPA_CIPHER_CCMP_256: + return NRF_WIFI_FMAC_CIPHER_SUITE_CCMP_256; + default: + return -1; + } +} + +static int nrf_wifi_set_beacon_data(const struct wpa_driver_ap_params *params, + struct nrf_wifi_beacon_data *beacon_data) +{ + int ret = -1; + + if (params->head_len > ARRAY_SIZE(beacon_data->head)) { + LOG_ERR("%s: head_len too big", __func__); + goto out; + } + + if (params->tail_len > ARRAY_SIZE(beacon_data->tail)) { + LOG_ERR("%s: tail_len too big", __func__); + goto out; + } + + if (params->proberesp_len > ARRAY_SIZE(beacon_data->probe_resp)) { + LOG_ERR("%s: proberesp_len too big", __func__); + goto out; + } + + beacon_data->head_len = params->head_len; + beacon_data->tail_len = params->tail_len; + beacon_data->probe_resp_len = params->proberesp_len; + + if (params->head_len) { + memcpy(&beacon_data->head, params->head, + params->head_len); + } + + if (params->tail_len) { + memcpy(&beacon_data->tail, params->tail, + params->tail_len); + } + + if (params->proberesp_len) { + memcpy(&beacon_data->probe_resp, params->proberesp_ies, + params->proberesp_len); + } + + ret = 0; +out: + return ret; +} + +int nrf_wifi_supp_register_mgmt_frame(void *if_priv, + u16 frame_type, size_t match_len, const u8 *match) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_umac_mgmt_frame_info mgmt_frame_info = {0}; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int ret = -1; + + if (!if_priv || (match_len && !match)) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + mgmt_frame_info.frame_type = frame_type; + mgmt_frame_info.frame_match.frame_match_len = match_len; + if (match_len >= NRF_WIFI_FRAME_MATCH_MAX_LEN) { + LOG_ERR("%s: match_len too big: %d (max %d)", __func__, match_len, + NRF_WIFI_FRAME_MATCH_MAX_LEN); + goto out; + } + memcpy(mgmt_frame_info.frame_match.frame_match, match, match_len); + + status = nrf_wifi_fmac_mgmt_frame_reg(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + &mgmt_frame_info); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_mgmt_frame_reg failed", __func__); + goto out; + } + + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +/* As per current design default is always STA */ +static int is_ap_dynamic_iface(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep) +{ + return (vif_ctx_zep->vif_idx != 0); +} + +static int nrf_wifi_set_bss(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep, + struct wpa_driver_ap_params *params) +{ + struct nrf_wifi_umac_bss_info bss_info = {0}; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + int ret = -1; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int i; + + if (!vif_ctx_zep || !params) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + if (params->basic_rates) { + for (i = 0; params->basic_rates[i] != -1; i++) { + if (i >= ARRAY_SIZE(bss_info.basic_rates)) { + LOG_ERR("%s: basic_rates too big: %d (max %d)", __func__, i, + ARRAY_SIZE(bss_info.basic_rates)); + goto out; + } + bss_info.basic_rates[i] = params->basic_rates[i]; + } + bss_info.num_basic_rates = i; + } + bss_info.p2p_go_ctwindow = params->p2p_go_ctwindow; + bss_info.ht_opmode = params->ht_opmode; + bss_info.nrf_wifi_cts = params->cts_protect; + bss_info.preamble = params->preamble; + bss_info.nrf_wifi_slot = params->short_slot_time; + bss_info.ap_isolate = params->isolate; + + status = nrf_wifi_fmac_set_bss(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, &bss_info); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_set_bss failed", __func__); + goto out; + } + + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + + +static enum nrf_wifi_chan_width wpa_supp_chan_width_to_nrf(enum hostapd_hw_mode mode, + int bandwidth, int cfreq1, int cfreq2) +{ + switch (bandwidth) { + case 20: + if (mode == HOSTAPD_MODE_IEEE80211B) { + return NRF_WIFI_CHAN_WIDTH_20_NOHT; + } else { + return NRF_WIFI_CHAN_WIDTH_20; + }; + case 40: + return NRF_WIFI_CHAN_WIDTH_40; + case 80: + if (cfreq2) { + return NRF_WIFI_CHAN_WIDTH_80P80; + } else { + return NRF_WIFI_CHAN_WIDTH_80; + } + case 160: + return NRF_WIFI_CHAN_WIDTH_160; + }; + + return NRF_WIFI_CHAN_WIDTH_20; +} + +int nrf_wifi_wpa_supp_start_ap(void *if_priv, struct wpa_driver_ap_params *params) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + int ret = -1; + struct nrf_wifi_umac_start_ap_info start_ap_info = {0}; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int ch_width = 0; + + if (!if_priv || !params) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + nrf_wifi_set_beacon_data(params, &start_ap_info.beacon_data); + start_ap_info.beacon_interval = params->beacon_int; + start_ap_info.dtim_period = params->dtim_period; + start_ap_info.ssid.nrf_wifi_ssid_len = params->ssid_len; + memcpy(start_ap_info.ssid.nrf_wifi_ssid, params->ssid, params->ssid_len); + for (int i = 0; i < 32; i++) { + if ((params->pairwise_ciphers & BIT(i)) && + start_ap_info.connect_common_info.num_cipher_suites_pairwise < 7) { + if (wpas_cipher_to_nrf(i) < 0) { + LOG_DBG("%s: Unsupported cipher %d ignored", __func__, i); + continue; + } + start_ap_info.connect_common_info.cipher_suites_pairwise[i] = + wpas_cipher_to_nrf(i); + start_ap_info.connect_common_info.num_cipher_suites_pairwise++; + } + } + + ch_width = wpa_supp_chan_width_to_nrf(params->freq->mode, params->freq->bandwidth, + params->freq->center_freq1, params->freq->center_freq2); + + start_ap_info.freq_params.frequency = params->freq->freq; + start_ap_info.freq_params.channel_width = ch_width; + start_ap_info.freq_params.center_frequency1 = params->freq->center_freq1; + start_ap_info.freq_params.center_frequency2 = params->freq->center_freq2; + start_ap_info.freq_params.channel_type = params->freq->ht_enabled ? NRF_WIFI_CHAN_HT20 : + NRF_WIFI_CHAN_NO_HT; + start_ap_info.freq_params.valid_fields = NRF_WIFI_SET_FREQ_PARAMS_FREQ_VALID | + NRF_WIFI_SET_FREQ_PARAMS_CHANNEL_WIDTH_VALID | + NRF_WIFI_SET_FREQ_PARAMS_CENTER_FREQ1_VALID | + NRF_WIFI_SET_FREQ_PARAMS_CENTER_FREQ2_VALID | + NRF_WIFI_SET_FREQ_PARAMS_CHANNEL_TYPE_VALID; + + vif_ctx_zep->if_carr_state = NRF_WIFI_FMAC_IF_CARR_STATE_OFF; + status = nrf_wifi_fmac_start_ap(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, &start_ap_info); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_start_ap failed", __func__); + goto out; + } + + ret = nrf_wifi_wait_for_carrier_status(vif_ctx_zep, NRF_WIFI_FMAC_IF_CARR_STATE_ON); + if (ret) { + goto out; + } + + ret = nrf_wifi_set_bss(vif_ctx_zep, params); + if (ret) { + LOG_ERR("%s: Failed to set BSS", __func__); + goto out; + } + + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_supp_change_beacon(void *if_priv, struct wpa_driver_ap_params *params) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + int ret = -1; + struct nrf_wifi_umac_set_beacon_info chg_bcn_info = {0}; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + + if (!if_priv || !params) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + nrf_wifi_set_beacon_data(params, &chg_bcn_info.beacon_data); + + status = nrf_wifi_fmac_chg_bcn(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, &chg_bcn_info); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_chg_bcn failed", __func__); + goto out; + } + + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_supp_stop_ap(void *if_priv) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int ret = -1; + + if (!if_priv) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + status = nrf_wifi_fmac_stop_ap(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx); + + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_stop_ap failed", __func__); + goto out; + } + + ret = nrf_wifi_wait_for_carrier_status(vif_ctx_zep, NRF_WIFI_FMAC_IF_CARR_STATE_OFF); + if (ret) { + goto out; + } + + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_supp_deinit_ap(void *if_priv) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int ret = -1; + + if (!if_priv) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_DBG("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + status = nrf_wifi_wpa_supp_stop_ap(if_priv); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Failed to stop AP", __func__); + goto out; + } + + if (!is_ap_dynamic_iface(vif_ctx_zep)) { + ret = nrf_wifi_iftype_change(vif_ctx_zep, NRF_WIFI_IFTYPE_STATION); + if (ret) { + LOG_ERR("%s: Failed to set interface type to STATION: %d", __func__, ret); + goto out; + } + } + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_sta_flags_to_nrf(int wpas_sta_flags) +{ + int nrf_sta_flags = 0; + + if (wpas_sta_flags & WPA_STA_AUTHORIZED) { + nrf_sta_flags |= NRF_WIFI_STA_FLAG_AUTHORIZED; + } + if (wpas_sta_flags & WPA_STA_WMM) { + nrf_sta_flags |= NRF_WIFI_STA_FLAG_WME; + } + if (wpas_sta_flags & WPA_STA_SHORT_PREAMBLE) { + nrf_sta_flags |= NRF_WIFI_STA_FLAG_SHORT_PREAMBLE; + } + if (wpas_sta_flags & WPA_STA_MFP) { + nrf_sta_flags |= NRF_WIFI_STA_FLAG_MFP; + } + if (wpas_sta_flags & WPA_STA_TDLS_PEER) { + nrf_sta_flags |= NRF_WIFI_STA_FLAG_TDLS_PEER; + } + /* Note: Do not set flags > NRF_WIFI_STA_FLAG_TDLS_PEER, else + * nrf_wifi_fmac_chg_sta will fail. This is equivalent to not + * setting WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE flag. + */ + + return nrf_sta_flags; +} + +int nrf_wifi_wpa_supp_sta_add(void *if_priv, struct hostapd_sta_add_params *params) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_umac_add_sta_info sta_info = {0}; + int ret = -1; + int i; + + if (!if_priv || !params) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + sta_info.nrf_wifi_listen_interval = params->listen_interval; + sta_info.aid = params->aid; + sta_info.sta_capability = params->capability; + sta_info.supp_rates.nrf_wifi_num_rates = params->supp_rates_len; + /* TODO: sta_info.supp_rates.band */ + for (i = 0; i < params->supp_rates_len; i++) { + sta_info.supp_rates.rates[i] = params->supp_rates[i] & 0x7f; + } + + sta_info.ext_capability.ext_capability_len = params->ext_capab_len; + if (params->ext_capab_len >= NRF_WIFI_EXT_CAPABILITY_MAX_LEN) { + LOG_ERR("%s: ext_capab_len too big: %d (max %d)", __func__, + params->ext_capab_len, NRF_WIFI_EXT_CAPABILITY_MAX_LEN); + goto out; + } + memcpy(sta_info.ext_capability.ext_capability, params->ext_capab, + params->ext_capab_len); + + sta_info.supported_channels.supported_channels_len = params->supp_channels_len; + if (params->supp_channels_len >= NRF_WIFI_SUPPORTED_CHANNELS_MAX_LEN) { + LOG_ERR("%s: supp_channels_len too big: %d (max %d)", __func__, + params->supp_channels_len, NRF_WIFI_SUPPORTED_CHANNELS_MAX_LEN); + goto out; + } + memcpy(sta_info.supported_channels.supported_channels, params->supp_channels, + params->supp_channels_len); + + sta_info.supported_oper_classes.supported_oper_classes_len = params->supp_oper_classes_len; + if (params->supp_oper_classes_len >= NRF_WIFI_OPER_CLASSES_MAX_LEN) { + LOG_ERR("%s: supp_oper_classes_len too big: %d (max %d)", __func__, + params->supp_oper_classes_len, NRF_WIFI_OPER_CLASSES_MAX_LEN); + goto out; + } + memcpy(sta_info.supported_oper_classes.supported_oper_classes, params->supp_oper_classes, + params->supp_oper_classes_len); + + sta_info.sta_flags2.nrf_wifi_set = nrf_wifi_sta_flags_to_nrf(params->flags); + sta_info.sta_flags2.nrf_wifi_mask = sta_info.sta_flags2.nrf_wifi_set | + nrf_wifi_sta_flags_to_nrf(params->flags_mask); + + if (params->ht_capabilities) { + memcpy(sta_info.ht_capability, + params->ht_capabilities, + sizeof(sta_info.ht_capability)); + } + + if (params->vht_capabilities) { + memcpy(sta_info.vht_capability, + params->vht_capabilities, + sizeof(sta_info.vht_capability)); + } + + memcpy(sta_info.mac_addr, params->addr, sizeof(sta_info.mac_addr)); + + LOG_DBG("%s: %x, %x", __func__, + sta_info.sta_flags2.nrf_wifi_set, sta_info.sta_flags2.nrf_wifi_mask); + + if (params->set) { + status = nrf_wifi_fmac_chg_sta(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + (struct nrf_wifi_umac_chg_sta_info *)&sta_info); + } else { + status = nrf_wifi_fmac_add_sta(rpu_ctx_zep->rpu_ctx, + vif_ctx_zep->vif_idx, + &sta_info); + } + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_add_sta failed", __func__); + goto out; + } + + ret = 0; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_supp_sta_remove(void *if_priv, const u8 *addr) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_umac_del_sta_info del_sta = {0}; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int ret = -1; + + if (!if_priv || !addr) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_DBG("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + memcpy(del_sta.mac_addr, addr, sizeof(del_sta.mac_addr)); + + status = nrf_wifi_fmac_del_sta(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, &del_sta); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_del_sta failed", __func__); + goto out; + } + + ret = 0; + +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_supp_sta_set_flags(void *if_priv, const u8 *addr, + unsigned int total_flags, unsigned int flags_or, + unsigned int flags_and) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_umac_chg_sta_info chg_sta = {0}; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int ret = -1; + + if (!if_priv || !addr) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_DBG("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + memcpy(chg_sta.mac_addr, addr, sizeof(chg_sta.mac_addr)); + + chg_sta.sta_flags2.nrf_wifi_mask = nrf_wifi_sta_flags_to_nrf(flags_or | ~flags_and); + chg_sta.sta_flags2.nrf_wifi_set = nrf_wifi_sta_flags_to_nrf(flags_or); + + LOG_DBG("%s %x, %x", __func__, + chg_sta.sta_flags2.nrf_wifi_set, chg_sta.sta_flags2.nrf_wifi_mask); + + status = nrf_wifi_fmac_chg_sta(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, &chg_sta); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_chg_sta failed", __func__); + goto out; + } + + ret = 0; + +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return ret; +} + +int nrf_wifi_wpa_supp_sta_get_inact_sec(void *if_priv, const u8 *addr) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int ret = -1, sem_ret; + int inactive_time_sec = -1; + + if (!if_priv || !addr) { + LOG_ERR("%s: Invalid params", __func__); + return ret; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_DBG("%s: rpu_ctx_zep is NULL", __func__); + return ret; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + status = nrf_wifi_fmac_get_station(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, + (unsigned char *) addr); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_get_station failed", __func__); + goto out; + } + + sem_ret = k_sem_take(&wait_for_event_sem, K_MSEC(RPU_RESP_EVENT_TIMEOUT)); + if (sem_ret) { + LOG_ERR("%s: Timed out to get station info, ret = %d", __func__, sem_ret); + ret = NRF_WIFI_STATUS_FAIL; + goto out; + } + + inactive_time_sec = vif_ctx_zep->inactive_time_sec; +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); + return inactive_time_sec; +} +#endif /* CONFIG_NRF70_AP_MODE */ diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index 50241b398426..0f5c6d6c9605 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -948,6 +948,7 @@ def check_no_undef_outside_kconfig(self, kconf): "MYFEATURE", "MY_DRIVER_0", "NORMAL_SLEEP", # #defined by RV32M1 in ext/ + "NRF_WIFI_FW_BIN", # Directly passed from CMakeLists.txt "OPT", "OPT_0", "PEDO_THS_MIN", From e9014e73befea65bbf2d3d276f0bb73611acd4c9 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Thu, 13 Jun 2024 01:06:44 +0530 Subject: [PATCH 05/51] [nrf fromtree] boards: shields: Add nRF70 series EKs (Evaluation kits) These shields in the Arduino form factor can be mounted on supported boards and can provide Wi-Fi6 capability. Base shield is nRF7002, with variants as nRF7001 (2.4GHz only) and nRF7000 (dual band scan only). Signed-off-by: Chaitanya Tata (cherry picked from commit 051c63f6a9f8bd2fcb0eb8ad48b48f7f0b861871) --- boards/shields/nrf7002ek/Kconfig.shield | 11 +++ .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 15 ++++ .../boards/nrf9151dk_nrf9151.overlay | 6 ++ .../boards/nrf9151dk_nrf9151_ns.overlay | 6 ++ .../boards/nrf9160dk_nrf9160_ns.overlay | 40 ++++++++++ .../boards/nrf9161dk_nrf9161.overlay | 6 ++ .../boards/nrf9161dk_nrf9161_ns.overlay | 6 ++ boards/shields/nrf7002ek/doc/index.rst | 74 ++++++++++++++++++ boards/shields/nrf7002ek/doc/nrf7002ek.png | Bin 0 -> 60260 bytes boards/shields/nrf7002ek/nrf7002ek.overlay | 27 +++++++ .../shields/nrf7002ek/nrf7002ek_common.dtsi | 35 +++++++++ .../nrf7002ek/nrf7002ek_common_5g.dtsi | 12 +++ .../nrf7002ek/nrf7002ek_nrf7000.overlay | 27 +++++++ .../nrf7002ek/nrf7002ek_nrf7001.overlay | 26 ++++++ 14 files changed, 291 insertions(+) create mode 100644 boards/shields/nrf7002ek/Kconfig.shield create mode 100644 boards/shields/nrf7002ek/boards/nrf5340dk_nrf5340_cpuapp.overlay create mode 100644 boards/shields/nrf7002ek/boards/nrf9151dk_nrf9151.overlay create mode 100644 boards/shields/nrf7002ek/boards/nrf9151dk_nrf9151_ns.overlay create mode 100644 boards/shields/nrf7002ek/boards/nrf9160dk_nrf9160_ns.overlay create mode 100644 boards/shields/nrf7002ek/boards/nrf9161dk_nrf9161.overlay create mode 100644 boards/shields/nrf7002ek/boards/nrf9161dk_nrf9161_ns.overlay create mode 100644 boards/shields/nrf7002ek/doc/index.rst create mode 100644 boards/shields/nrf7002ek/doc/nrf7002ek.png create mode 100644 boards/shields/nrf7002ek/nrf7002ek.overlay create mode 100644 boards/shields/nrf7002ek/nrf7002ek_common.dtsi create mode 100644 boards/shields/nrf7002ek/nrf7002ek_common_5g.dtsi create mode 100644 boards/shields/nrf7002ek/nrf7002ek_nrf7000.overlay create mode 100644 boards/shields/nrf7002ek/nrf7002ek_nrf7001.overlay diff --git a/boards/shields/nrf7002ek/Kconfig.shield b/boards/shields/nrf7002ek/Kconfig.shield new file mode 100644 index 000000000000..7627ff96dbd4 --- /dev/null +++ b/boards/shields/nrf7002ek/Kconfig.shield @@ -0,0 +1,11 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config SHIELD_NRF7002EK + def_bool $(shields_list_contains,nrf7002ek) + +config SHIELD_NRF7002EK_NRF7001 + def_bool $(shields_list_contains,nrf7002ek_nrf7001) + +config SHIELD_NRF7002EK_NRF7000 + def_bool $(shields_list_contains,nrf7002ek_nrf7000) diff --git a/boards/shields/nrf7002ek/boards/nrf5340dk_nrf5340_cpuapp.overlay b/boards/shields/nrf7002ek/boards/nrf5340dk_nrf5340_cpuapp.overlay new file mode 100644 index 000000000000..ada3a5c61d24 --- /dev/null +++ b/boards/shields/nrf7002ek/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -0,0 +1,15 @@ +/* Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* This node by default forwards the UART1 pins to CPUNET, but as UART1 uses + * same pins as bucken and iovdd-ctrl, we need these pins to be controlled by + * the CPUAPP. Since a child node of gpio_fwd cannot be disabled, hence + * the entire node is disabled. If the application needs to forward other pins + * to the CPUNET, it should create a separate instance of nrf-gpio-forwarder + * and use it instead. + */ +&gpio_fwd { + status = "disabled"; +}; diff --git a/boards/shields/nrf7002ek/boards/nrf9151dk_nrf9151.overlay b/boards/shields/nrf7002ek/boards/nrf9151dk_nrf9151.overlay new file mode 100644 index 000000000000..70b5f088a8f9 --- /dev/null +++ b/boards/shields/nrf7002ek/boards/nrf9151dk_nrf9151.overlay @@ -0,0 +1,6 @@ +/* Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include diff --git a/boards/shields/nrf7002ek/boards/nrf9151dk_nrf9151_ns.overlay b/boards/shields/nrf7002ek/boards/nrf9151dk_nrf9151_ns.overlay new file mode 100644 index 000000000000..70b5f088a8f9 --- /dev/null +++ b/boards/shields/nrf7002ek/boards/nrf9151dk_nrf9151_ns.overlay @@ -0,0 +1,6 @@ +/* Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include diff --git a/boards/shields/nrf7002ek/boards/nrf9160dk_nrf9160_ns.overlay b/boards/shields/nrf7002ek/boards/nrf9160dk_nrf9160_ns.overlay new file mode 100644 index 000000000000..410408d5ba71 --- /dev/null +++ b/boards/shields/nrf7002ek/boards/nrf9160dk_nrf9160_ns.overlay @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Disabled because of conflicts on P0.00 and P0.01 - Arduino pins D0 and D1 + * (iovdd-ctrl-gpios and bucken-gpios in nrf7002ek, respectively). + */ +&uart1 { + status = "disabled"; +}; + +/* Typically we use GPIO extender to resolve these conflicts but the + * GPIO pin used by extender itself conflicts with Wi-Fi (0.6), so, + * disable LEDs and Button0/1. + */ +&led0 { + status = "disabled"; +}; + +&led1 { + status = "disabled"; +}; + +&led2 { + status = "disabled"; +}; + +&led3 { + status = "disabled"; +}; + +&button0 { + status = "disabled"; +}; + +&button1 { + status = "disabled"; +}; diff --git a/boards/shields/nrf7002ek/boards/nrf9161dk_nrf9161.overlay b/boards/shields/nrf7002ek/boards/nrf9161dk_nrf9161.overlay new file mode 100644 index 000000000000..77cc7f915073 --- /dev/null +++ b/boards/shields/nrf7002ek/boards/nrf9161dk_nrf9161.overlay @@ -0,0 +1,6 @@ +/* Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include diff --git a/boards/shields/nrf7002ek/boards/nrf9161dk_nrf9161_ns.overlay b/boards/shields/nrf7002ek/boards/nrf9161dk_nrf9161_ns.overlay new file mode 100644 index 000000000000..77cc7f915073 --- /dev/null +++ b/boards/shields/nrf7002ek/boards/nrf9161dk_nrf9161_ns.overlay @@ -0,0 +1,6 @@ +/* Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include diff --git a/boards/shields/nrf7002ek/doc/index.rst b/boards/shields/nrf7002ek/doc/index.rst new file mode 100644 index 000000000000..9f247be3501d --- /dev/null +++ b/boards/shields/nrf7002ek/doc/index.rst @@ -0,0 +1,74 @@ +.. _nrf7002ek: + +nRF7002 EK +########## + +Overview +******** + +The nRF7002 EK is a versatile evaluation kit in the form of an Arduino shield that can be used in +Nordic and non-Nordic host boards. + +The nRF7002 EK unlocks low-power Wi-Fi 6 capabilities for your host device. It support dual-band Wi-Fi +2.4GHz and 5GHz, and is based on the nRF7002 SoC. +Seamlessly connect to Wi-Fi networks and leverage Wi-Fi-based locationing, enabling advanced +features such as SSID sniffing of local Wi-Fi hubs + +.. figure:: nrf7002ek.png + :alt: nRF7002 EK + :align: center + + nRF7002 EK + +Requirements +************ + +The nRF7002 EK board is designed to fit straight into an Arduino connector and uses SPI as the +communication interface. Any host board that supports the Arduino connector can be used with +the nRF7002 EK. + +Prerequisites +------------- + +the nRF70 driver requires firmware binary blobs for Wi-Fi operation. Run the command +below to retrieve those files. + +.. code-block:: console + + west update + west blobs fetch hal_nordic + +Usage +***** + +The shield can be used in any application by setting ``--shield nrf7002ek`` when invoking ``west build``. + +SR Co-existence +############### + +The nRF7002 EK supports SR co-existence provided the host board supports it. The SR co-existence +pins are connected to the host board's GPIO pins. + +Two Kconfig options are available to enable SR co-existence: + +- :kconfig:option:`CONFIG_NRF70_SR_COEX`: Enables SR co-existence. +- :kconfig:option:`CONFIG_NRF70_SR_COEX_RF_SWITCH`: Control SR side RF switch. + +Shield Variants +############### + +The nRF7002 EK is available in three variants: + +- ``nrf7002ek``: The default variant. +- ``nrf7002ek_nrf7001``: Variant for the nRF7001 SoC or nRF7002 SoC emulating nRF7001 + that supports only 2.4GHz Wi-Fi. +- ``nrf7002ek_nrf7000``: Variant for the nRF7000 SoC or nRF7002 SoC emulating nRF7000 + that supports only 2.4GHz Wi-Fi. + + +References +********** + +- `nRF7002 EK product page `_ +- `nRF7002 product specification `_ +- `nRF7002 EK Co-existence `_ diff --git a/boards/shields/nrf7002ek/doc/nrf7002ek.png b/boards/shields/nrf7002ek/doc/nrf7002ek.png new file mode 100644 index 0000000000000000000000000000000000000000..9b4b09753719cc96a10580fd47ccc0642d976e81 GIT binary patch literal 60260 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 RNqP($LxT1aV53VE001RD@a_Nr literal 0 HcmV?d00001 diff --git a/boards/shields/nrf7002ek/nrf7002ek.overlay b/boards/shields/nrf7002ek/nrf7002ek.overlay new file mode 100644 index 000000000000..2a0f09bd955e --- /dev/null +++ b/boards/shields/nrf7002ek/nrf7002ek.overlay @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +/ { + chosen { + zephyr,wifi = &wlan0; + }; +}; + +&arduino_spi { + status = "okay"; + + nrf70: nrf7002-spi@0 { + compatible = "nordic,nrf7002-spi"; + status = "okay"; + reg = <0>; + spi-max-frequency = ; + + /* Include common nRF70 overlays */ + #include "nrf7002ek_common.dtsi" + #include "nrf7002ek_common_5g.dtsi" + }; +}; diff --git a/boards/shields/nrf7002ek/nrf7002ek_common.dtsi b/boards/shields/nrf7002ek/nrf7002ek_common.dtsi new file mode 100644 index 000000000000..102e0078d5f3 --- /dev/null +++ b/boards/shields/nrf7002ek/nrf7002ek_common.dtsi @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Common assignments for any nRF70 shield */ + +/* D0 */ +iovdd-ctrl-gpios = <&arduino_header 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; +/* D1 */ +bucken-gpios = <&arduino_header 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; +/* D7 */ +host-irq-gpios = <&arduino_header 13 GPIO_ACTIVE_HIGH>; +/* Short-range (SR) co-existence */ +/* D2 */ +status0-gpios = <&arduino_header 8 GPIO_ACTIVE_HIGH>; +/* D3 */ +req-gpios = <&arduino_header 9 GPIO_ACTIVE_HIGH>; +/* D4 */ +grant-gpios = <&arduino_header 10 (GPIO_PULL_DOWN | GPIO_ACTIVE_LOW)>; +/* D6 */ +swctrl1-gpios = <&arduino_header 12 GPIO_ACTIVE_HIGH>; +/* D8 */ +srrf-switch-gpios = <&arduino_header 14 GPIO_ACTIVE_HIGH>; + +/* Maximum TX power limits for 2.4 GHz */ +wifi-max-tx-pwr-2g-dsss = <21>; +wifi-max-tx-pwr-2g-mcs0 = <16>; +wifi-max-tx-pwr-2g-mcs7 = <16>; + +/* List of interfaces */ +wlan0: wlan0 { + compatible = "nordic,wlan"; +}; diff --git a/boards/shields/nrf7002ek/nrf7002ek_common_5g.dtsi b/boards/shields/nrf7002ek/nrf7002ek_common_5g.dtsi new file mode 100644 index 000000000000..8faa9b945feb --- /dev/null +++ b/boards/shields/nrf7002ek/nrf7002ek_common_5g.dtsi @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +wifi-max-tx-pwr-5g-low-mcs0 = <13>; +wifi-max-tx-pwr-5g-low-mcs7 = <13>; +wifi-max-tx-pwr-5g-mid-mcs0 = <13>; +wifi-max-tx-pwr-5g-mid-mcs7 = <13>; +wifi-max-tx-pwr-5g-high-mcs0 = <12>; +wifi-max-tx-pwr-5g-high-mcs7 = <12>; diff --git a/boards/shields/nrf7002ek/nrf7002ek_nrf7000.overlay b/boards/shields/nrf7002ek/nrf7002ek_nrf7000.overlay new file mode 100644 index 000000000000..28a3662f1d67 --- /dev/null +++ b/boards/shields/nrf7002ek/nrf7002ek_nrf7000.overlay @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +/ { + chosen { + zephyr,wifi = &wlan0; + }; +}; + +&arduino_spi { + status = "okay"; + + nrf70: nrf7002-spi@0 { + compatible = "nordic,nrf7000-spi"; + status = "okay"; + reg = <0>; + spi-max-frequency = ; + + /* Include common nRF70 overlays */ + #include "nrf7002ek_common.dtsi" + #include "nrf7002ek_common_5g.dtsi" + }; +}; diff --git a/boards/shields/nrf7002ek/nrf7002ek_nrf7001.overlay b/boards/shields/nrf7002ek/nrf7002ek_nrf7001.overlay new file mode 100644 index 000000000000..a3b84d1761d5 --- /dev/null +++ b/boards/shields/nrf7002ek/nrf7002ek_nrf7001.overlay @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +/ { + chosen { + zephyr,wifi = &wlan0; + }; +}; + +&arduino_spi { + status = "okay"; + + nrf70: nrf7001-spi@0 { + compatible = "nordic,nrf7001-spi"; + status = "okay"; + reg = <0>; + spi-max-frequency = ; + + /* Include common nRF70 overlays */ + #include "nrf7002ek_common.dtsi" + }; +}; From 16f997c4b964ece141ba9d4d90a7c4f898624651 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 17 Jul 2024 22:24:20 +0530 Subject: [PATCH 06/51] [nrf fromtree] samples: net: wifi: Fix scan results dropping Wi-Fi shell prints scan results to the console taking time and this puts pressure on net_mgmt Queue, so, increase both timeout and Queue depth to handle crowded Wi-Fi channel (~200 APs). Signed-off-by: Chaitanya Tata (cherry picked from commit bf4f51a7c6690ba0d856432ac506a02c2a9da85f) --- samples/net/wifi/prj.conf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/samples/net/wifi/prj.conf b/samples/net/wifi/prj.conf index de7de625340f..09422d21ce84 100644 --- a/samples/net/wifi/prj.conf +++ b/samples/net/wifi/prj.conf @@ -29,3 +29,8 @@ CONFIG_NET_STATISTICS_PERIODIC_OUTPUT=n CONFIG_WIFI=y CONFIG_WIFI_LOG_LEVEL_ERR=y CONFIG_NET_L2_WIFI_SHELL=y +# printing of scan results puts pressure on queues in new locking +# design in net_mgmt. So, use a higher timeout for a crowded +# environment. +CONFIG_NET_MGMT_EVENT_QUEUE_TIMEOUT=5000 +CONFIG_NET_MGMT_EVENT_QUEUE_SIZE=16 From 11788c65d5dcbfaeaa67c76be547dba57b588e44 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 24 Jul 2024 02:12:55 +0530 Subject: [PATCH 07/51] [nrf fromtree] samples: net: wifi: Fix stack sizes Wi-Fi sample is stack heavy, so, by default increase the stack sizes to work with any driver, esp. those use WPA supplicant like nRF Wi-Fi. Signed-off-by: Chaitanya Tata (cherry picked from commit 65bdf6564b2da7ad2bb16c04db5b6bb2d39cfb1c) --- samples/net/wifi/prj.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/net/wifi/prj.conf b/samples/net/wifi/prj.conf index 09422d21ce84..e1a1b40a4098 100644 --- a/samples/net/wifi/prj.conf +++ b/samples/net/wifi/prj.conf @@ -3,6 +3,8 @@ CONFIG_EARLY_CONSOLE=y CONFIG_NETWORKING=y CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_MAIN_STACK_SIZE=5200 +CONFIG_SHELL_STACK_SIZE=5200 CONFIG_NET_TX_STACK_SIZE=2048 CONFIG_NET_RX_STACK_SIZE=2048 From ace4cdd9ca39db14bb8c8f108c835809fbb4753b Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Mon, 5 Aug 2024 20:46:26 +0530 Subject: [PATCH 08/51] Revert "[nrf noup] samples: net: Add support for nRF700x" This reverts commit a1d4d7b27cb0ae6eb23853bc1e1668f35a43e31b. Now that nRF70 is upstream, this can be reverted. Signed-off-by: Chaitanya Tata --- .../net/dhcpv4_client/overlay-nrf700x.conf | 14 ---------- samples/net/dns_resolve/overlay-nrf700x.conf | 18 ------------- .../net/ipv4_autoconf/overlay-nrf700x.conf | 14 ---------- .../net/mdns_responder/overlay-nrf700x.conf | 18 ------------- .../net/mqtt_publisher/overlay-nrf700x.conf | 19 -------------- .../mqtt_sn_publisher/overlay-nrf700x.conf | 20 -------------- .../sockets/coap_client/overlay-nrf700x.conf | 16 ------------ .../sockets/coap_server/overlay-nrf700x.conf | 26 ------------------- samples/net/sockets/echo/overlay-nrf700x.conf | 18 ------------- .../sockets/echo_async/overlay-nrf700x.conf | 18 ------------- .../sockets/echo_client/overlay-nrf700x.conf | 20 -------------- .../sockets/echo_server/overlay-nrf700x.conf | 20 -------------- .../net/sockets/http_get/overlay-nrf700x.conf | 18 ------------- .../sockets/sntp_client/overlay-nrf700x.conf | 18 ------------- samples/net/syslog_net/overlay-nrf700x.conf | 18 ------------- samples/net/telnet/overlay-nrf700x.conf | 18 ------------- 16 files changed, 293 deletions(-) delete mode 100644 samples/net/dhcpv4_client/overlay-nrf700x.conf delete mode 100644 samples/net/dns_resolve/overlay-nrf700x.conf delete mode 100644 samples/net/ipv4_autoconf/overlay-nrf700x.conf delete mode 100644 samples/net/mdns_responder/overlay-nrf700x.conf delete mode 100644 samples/net/mqtt_publisher/overlay-nrf700x.conf delete mode 100644 samples/net/mqtt_sn_publisher/overlay-nrf700x.conf delete mode 100644 samples/net/sockets/coap_client/overlay-nrf700x.conf delete mode 100644 samples/net/sockets/coap_server/overlay-nrf700x.conf delete mode 100644 samples/net/sockets/echo/overlay-nrf700x.conf delete mode 100644 samples/net/sockets/echo_async/overlay-nrf700x.conf delete mode 100644 samples/net/sockets/echo_client/overlay-nrf700x.conf delete mode 100644 samples/net/sockets/echo_server/overlay-nrf700x.conf delete mode 100644 samples/net/sockets/http_get/overlay-nrf700x.conf delete mode 100644 samples/net/sockets/sntp_client/overlay-nrf700x.conf delete mode 100644 samples/net/syslog_net/overlay-nrf700x.conf delete mode 100644 samples/net/telnet/overlay-nrf700x.conf diff --git a/samples/net/dhcpv4_client/overlay-nrf700x.conf b/samples/net/dhcpv4_client/overlay-nrf700x.conf deleted file mode 100644 index 2d552e9c6231..000000000000 --- a/samples/net/dhcpv4_client/overlay-nrf700x.conf +++ /dev/null @@ -1,14 +0,0 @@ -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/dns_resolve/overlay-nrf700x.conf b/samples/net/dns_resolve/overlay-nrf700x.conf deleted file mode 100644 index aa59e5d5ea2d..000000000000 --- a/samples/net/dns_resolve/overlay-nrf700x.conf +++ /dev/null @@ -1,18 +0,0 @@ -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# DHCPv4 -CONFIG_NET_CONFIG_MY_IPV4_ADDR="" -CONFIG_NET_DHCPV4=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/ipv4_autoconf/overlay-nrf700x.conf b/samples/net/ipv4_autoconf/overlay-nrf700x.conf deleted file mode 100644 index 2d552e9c6231..000000000000 --- a/samples/net/ipv4_autoconf/overlay-nrf700x.conf +++ /dev/null @@ -1,14 +0,0 @@ -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/mdns_responder/overlay-nrf700x.conf b/samples/net/mdns_responder/overlay-nrf700x.conf deleted file mode 100644 index aa59e5d5ea2d..000000000000 --- a/samples/net/mdns_responder/overlay-nrf700x.conf +++ /dev/null @@ -1,18 +0,0 @@ -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# DHCPv4 -CONFIG_NET_CONFIG_MY_IPV4_ADDR="" -CONFIG_NET_DHCPV4=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/mqtt_publisher/overlay-nrf700x.conf b/samples/net/mqtt_publisher/overlay-nrf700x.conf deleted file mode 100644 index a812c7896f62..000000000000 --- a/samples/net/mqtt_publisher/overlay-nrf700x.conf +++ /dev/null @@ -1,19 +0,0 @@ -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# DHCPv4 -CONFIG_NET_CONFIG_MY_IPV4_ADDR="" -CONFIG_NET_CONFIG_NEED_IPV4=y -CONFIG_NET_DHCPV4=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/mqtt_sn_publisher/overlay-nrf700x.conf b/samples/net/mqtt_sn_publisher/overlay-nrf700x.conf deleted file mode 100644 index cbc47b965727..000000000000 --- a/samples/net/mqtt_sn_publisher/overlay-nrf700x.conf +++ /dev/null @@ -1,20 +0,0 @@ -CONFIG_POSIX_MAX_FDS=16 - -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# DHCPv4 -CONFIG_NET_CONFIG_MY_IPV4_ADDR="" -CONFIG_NET_DHCPV4=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/coap_client/overlay-nrf700x.conf b/samples/net/sockets/coap_client/overlay-nrf700x.conf deleted file mode 100644 index a0e436e3537d..000000000000 --- a/samples/net/sockets/coap_client/overlay-nrf700x.conf +++ /dev/null @@ -1,16 +0,0 @@ -CONFIG_HEAP_MEM_POOL_SIZE=153000 - -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/coap_server/overlay-nrf700x.conf b/samples/net/sockets/coap_server/overlay-nrf700x.conf deleted file mode 100644 index 4817a4f73ba6..000000000000 --- a/samples/net/sockets/coap_server/overlay-nrf700x.conf +++ /dev/null @@ -1,26 +0,0 @@ -CONFIG_HEAP_MEM_POOL_SIZE=153000 - -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# DHCPv4 -CONFIG_NET_CONFIG_MY_IPV4_ADDR="" -CONFIG_NET_DHCPV4=y - -# The sample can run either IPv4 or IPv6, not both -CONFIG_NET_IPV6=n -CONFIG_NET_CONFIG_NEED_IPV6=n -CONFIG_NET_IPV4=y -CONFIG_NET_CONFIG_NEED_IPV4=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/echo/overlay-nrf700x.conf b/samples/net/sockets/echo/overlay-nrf700x.conf deleted file mode 100644 index aa59e5d5ea2d..000000000000 --- a/samples/net/sockets/echo/overlay-nrf700x.conf +++ /dev/null @@ -1,18 +0,0 @@ -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# DHCPv4 -CONFIG_NET_CONFIG_MY_IPV4_ADDR="" -CONFIG_NET_DHCPV4=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/echo_async/overlay-nrf700x.conf b/samples/net/sockets/echo_async/overlay-nrf700x.conf deleted file mode 100644 index aa59e5d5ea2d..000000000000 --- a/samples/net/sockets/echo_async/overlay-nrf700x.conf +++ /dev/null @@ -1,18 +0,0 @@ -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# DHCPv4 -CONFIG_NET_CONFIG_MY_IPV4_ADDR="" -CONFIG_NET_DHCPV4=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/echo_client/overlay-nrf700x.conf b/samples/net/sockets/echo_client/overlay-nrf700x.conf deleted file mode 100644 index cbc47b965727..000000000000 --- a/samples/net/sockets/echo_client/overlay-nrf700x.conf +++ /dev/null @@ -1,20 +0,0 @@ -CONFIG_POSIX_MAX_FDS=16 - -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# DHCPv4 -CONFIG_NET_CONFIG_MY_IPV4_ADDR="" -CONFIG_NET_DHCPV4=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/echo_server/overlay-nrf700x.conf b/samples/net/sockets/echo_server/overlay-nrf700x.conf deleted file mode 100644 index cbc47b965727..000000000000 --- a/samples/net/sockets/echo_server/overlay-nrf700x.conf +++ /dev/null @@ -1,20 +0,0 @@ -CONFIG_POSIX_MAX_FDS=16 - -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# DHCPv4 -CONFIG_NET_CONFIG_MY_IPV4_ADDR="" -CONFIG_NET_DHCPV4=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/http_get/overlay-nrf700x.conf b/samples/net/sockets/http_get/overlay-nrf700x.conf deleted file mode 100644 index aa59e5d5ea2d..000000000000 --- a/samples/net/sockets/http_get/overlay-nrf700x.conf +++ /dev/null @@ -1,18 +0,0 @@ -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# DHCPv4 -CONFIG_NET_CONFIG_MY_IPV4_ADDR="" -CONFIG_NET_DHCPV4=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/sockets/sntp_client/overlay-nrf700x.conf b/samples/net/sockets/sntp_client/overlay-nrf700x.conf deleted file mode 100644 index aa59e5d5ea2d..000000000000 --- a/samples/net/sockets/sntp_client/overlay-nrf700x.conf +++ /dev/null @@ -1,18 +0,0 @@ -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# DHCPv4 -CONFIG_NET_CONFIG_MY_IPV4_ADDR="" -CONFIG_NET_DHCPV4=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/syslog_net/overlay-nrf700x.conf b/samples/net/syslog_net/overlay-nrf700x.conf deleted file mode 100644 index aa59e5d5ea2d..000000000000 --- a/samples/net/syslog_net/overlay-nrf700x.conf +++ /dev/null @@ -1,18 +0,0 @@ -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# DHCPv4 -CONFIG_NET_CONFIG_MY_IPV4_ADDR="" -CONFIG_NET_DHCPV4=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" diff --git a/samples/net/telnet/overlay-nrf700x.conf b/samples/net/telnet/overlay-nrf700x.conf deleted file mode 100644 index aa59e5d5ea2d..000000000000 --- a/samples/net/telnet/overlay-nrf700x.conf +++ /dev/null @@ -1,18 +0,0 @@ -# Wi-Fi -CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y -CONFIG_WPA_SUPP=y -CONFIG_NET_L2_ETHERNET=y - -# DHCPv4 -CONFIG_NET_CONFIG_MY_IPV4_ADDR="" -CONFIG_NET_DHCPV4=y - -# Connection manager -CONFIG_NET_CONNECTION_MANAGER=y - -# Credentials -CONFIG_WIFI_CREDENTIALS=y -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" From c5108d1bacaefa56dd554697c06adda24e2e4eb5 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Mon, 5 Aug 2024 21:13:48 +0530 Subject: [PATCH 09/51] [nrf noup] samples: lwm2m_client: Remve nRF70 selection nrf-squash! [nrf noup] samples: lwm2m_client: Add support for nRF91x and nRF700x This is now automated based on board. Signed-off-by: Chaitanya Tata --- samples/net/lwm2m_client/overlay-nrf700x.conf | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/samples/net/lwm2m_client/overlay-nrf700x.conf b/samples/net/lwm2m_client/overlay-nrf700x.conf index 2409886fe420..a1c70559c768 100644 --- a/samples/net/lwm2m_client/overlay-nrf700x.conf +++ b/samples/net/lwm2m_client/overlay-nrf700x.conf @@ -5,7 +5,6 @@ CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 # Wi-Fi CONFIG_WIFI=y -CONFIG_WIFI_NRF700X=y CONFIG_WIFI_MGMT_EXT=y CONFIG_NRF_WIFI_IF_AUTO_START=n @@ -16,8 +15,8 @@ CONFIG_WIFI_CREDENTIALS_STATIC_SSID="" CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="" # WPA -CONFIG_WPA_SUPP=y -CONFIG_WPA_SUPP_LOG_LEVEL_ERR=y +CONFIG_WIFI_NM_WPA_SUPPLICANT=y +CONFIG_WIFI_NM_WPA_SUPPLICANT_LOG_LEVEL_ERR=y CONFIG_NET_NATIVE=y CONFIG_NET_L2_ETHERNET=y From c1d8dc003170bdbd093ab622f0030510c0e4cb08 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Mon, 5 Aug 2024 21:15:05 +0530 Subject: [PATCH 10/51] [nrf noup] drivers: wifi: nrfwifi: Workaround for build External flash support is still WIP, so, to get sysbuild working add dummy Kconfig options. Signed-off-by: Chaitanya Tata --- drivers/wifi/nrfwifi/Kconfig.nrfwifi | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/wifi/nrfwifi/Kconfig.nrfwifi b/drivers/wifi/nrfwifi/Kconfig.nrfwifi index bad9cfb7b8d2..3b3ce26bda73 100644 --- a/drivers/wifi/nrfwifi/Kconfig.nrfwifi +++ b/drivers/wifi/nrfwifi/Kconfig.nrfwifi @@ -667,4 +667,31 @@ config NRF_WIFI_COMBINED_BUCKEN_IOVDD_GPIO there will be a internal hardware switch to add delay between the two operations. This is typically 4ms delay for nRF70. +# TODO: Temporary WAR, implement these options in the future +config NRF_WIFI_PATCHES_EXT_FLASH_DISABLED + bool "nRF70 firmware patch external flash support" + help + Select this option to disable external flash support for nRF70 firmware patches + +config NRF_WIFI_FW_PATCH_DFU + bool "nRF70 firmware patch DFU" + help + Select this option to enable DFU for nRF70 firmware patches + +config NRF_WIFI_PATCHES_EXT_FLASH_STORE + bool "nRF70 firmware patch external flash store" + help + Select this option to enable external flash store for nRF70 firmware patches + +config NRF_WIFI_FW_FLASH_CHUNK_SIZE + int "nRF70 firmware patch flash chunk size" + default 8192 + help + Select this option to set the flash chunk size for nRF70 firmware patches + +config NRF_WIFI_PATCHES_EXT_FLASH_XIP + bool "nRF70 firmware patch external flash XIP" + help + Select this option to enable external flash XIP for nRF70 firmware patches + endif # WIFI_NRF70 From d2ead5c2173ae687160f10433bc5061b8ab6d1e1 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Tue, 6 Aug 2024 00:31:28 +0530 Subject: [PATCH 11/51] [nrf noup] net: conn_mgr: Rename WPA supplicant nrf-squash! [nrf noup] net: Increase connection manager stack size Migrate to use Zephyr upstream WPA supplicant. Signed-off-by: Chaitanya Tata --- subsys/net/conn_mgr/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subsys/net/conn_mgr/Kconfig b/subsys/net/conn_mgr/Kconfig index 541edf0e63ec..71e8b728e6c9 100644 --- a/subsys/net/conn_mgr/Kconfig +++ b/subsys/net/conn_mgr/Kconfig @@ -24,7 +24,7 @@ source "subsys/net/Kconfig.template.log_config.net" config NET_CONNECTION_MANAGER_MONITOR_STACK_SIZE int "Size of the stack allocated for the conn_mgr_monitor thread" - default 8192 if WPA_SUPP + default 8192 if WIFI_NM_WPA_SUPPLICANT default 512 help Sets the stack size which will be used by the connection manager for connectivity monitoring. From 86adbd96a13d9f1a8f12fe4a7a77b5c940e7eec3 Mon Sep 17 00:00:00 2001 From: Fengming Ye Date: Wed, 29 May 2024 17:59:17 +0900 Subject: [PATCH 12/51] [nrf fromtree] hostap: add crypto backend alt for enterprise and DPP Add kconfig CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ALT to get more mbedtls functionality for enterprise and DPP. Split cmake sources related to hostap SME and crypto backend. Default backend CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO use internal crypto and some mbedtls apis. Backend CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ALT use most mbedtls apis and PSA apis, except some apis no longer supported in mbedtls 3.x, or called in the middle of hostap and mbedtls. Signed-off-by: Fengming Ye (cherry picked from commit 6f0c836337329620f7838dbdc7b14649dd53133a) --- modules/hostap/CMakeLists.txt | 111 ++++++++++++++++++++-------------- modules/hostap/Kconfig | 22 +++++++ 2 files changed, 88 insertions(+), 45 deletions(-) diff --git a/modules/hostap/CMakeLists.txt b/modules/hostap/CMakeLists.txt index b340c3882c8b..66968e0e249e 100644 --- a/modules/hostap/CMakeLists.txt +++ b/modules/hostap/CMakeLists.txt @@ -225,17 +225,6 @@ zephyr_library_sources_ifndef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE ${HOSTAP_SRC_BASE}/rsn_supp/wpa.c ${HOSTAP_SRC_BASE}/rsn_supp/preauth.c ${HOSTAP_SRC_BASE}/rsn_supp/wpa_ie.c - ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls-bignum.c - ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls-ec.c - ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls.c - ${HOSTAP_SRC_BASE}/crypto/tls_mbedtls.c - ${HOSTAP_SRC_BASE}/crypto/aes-wrap.c - ${HOSTAP_SRC_BASE}/crypto/aes-unwrap.c - ${HOSTAP_SRC_BASE}/crypto/rc4.c - ${HOSTAP_SRC_BASE}/crypto/sha1-prf.c - ${HOSTAP_SRC_BASE}/crypto/sha256-prf.c - ${HOSTAP_SRC_BASE}/crypto/sha256-prf.c - ${HOSTAP_SRC_BASE}/crypto/sha384-prf.c ) zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_WPA3 @@ -243,7 +232,6 @@ zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_WPA3 ${HOSTAP_SRC_BASE}/common/dragonfly.c ${HOSTAP_SRC_BASE}/crypto/dh_groups.c - ${HOSTAP_SRC_BASE}/crypto/sha256-kdf.c ) zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_WPA3 @@ -255,9 +243,6 @@ zephyr_library_include_directories_ifndef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_N ${CMAKE_SOURCE_DIR} ) -zephyr_library_link_libraries_ifndef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE - mbedTLS) - zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P ${WIFI_NM_WPA_SUPPLICANT_BASE}/p2p_supplicant.c ${WIFI_NM_WPA_SUPPLICANT_BASE}/p2p_supplicant_sd.c @@ -305,26 +290,7 @@ zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_WPS EAP_WSC ) -zephyr_library_sources_ifndef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE - ${HOSTAP_SRC_BASE}/common/wpa_common.c - ${HOSTAP_SRC_BASE}/rsn_supp/wpa.c - ${HOSTAP_SRC_BASE}/rsn_supp/preauth.c - ${HOSTAP_SRC_BASE}/rsn_supp/wpa_ie.c - - ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls-bignum.c - ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls-ec.c - ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls.c - ${HOSTAP_SRC_BASE}/crypto/aes-wrap.c - ${HOSTAP_SRC_BASE}/crypto/aes-unwrap.c - ${HOSTAP_SRC_BASE}/crypto/rc4.c - ${HOSTAP_SRC_BASE}/crypto/sha1-prf.c - ${HOSTAP_SRC_BASE}/crypto/sha256-prf.c - ${HOSTAP_SRC_BASE}/crypto/sha256-prf.c - ${HOSTAP_SRC_BASE}/crypto/sha384-prf.c -) - zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE - ${HOSTAP_SRC_BASE}/crypto/tls_mbedtls.c ${HOSTAP_SRC_BASE}/eap_peer/eap_tls.c ${HOSTAP_SRC_BASE}/eap_peer/eap_tls_common.c @@ -364,17 +330,6 @@ zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE ${HOSTAP_SRC_BASE}/eap_common/eap_ikev2_common.c ${HOSTAP_SRC_BASE}/eap_common/ikev2_common.c - # common - ${HOSTAP_SRC_BASE}/crypto/sha384-tlsprf.c - ${HOSTAP_SRC_BASE}/crypto/sha256-tlsprf.c - ${HOSTAP_SRC_BASE}/crypto/sha1-tlsprf.c - ${HOSTAP_SRC_BASE}/crypto/sha1-tprf.c - ${HOSTAP_SRC_BASE}/crypto/ms_funcs.c - ${HOSTAP_SRC_BASE}/crypto/aes-eax.c - # MD4 removed from MbedTLS - ${HOSTAP_SRC_BASE}/crypto/md4-internal - ${HOSTAP_SRC_BASE}/crypto/aes-encblock.c - ) zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE @@ -410,4 +365,70 @@ zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_EAPOL zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_NW_SEL_RELIABILITY CONFIG_NW_SEL_RELIABILITY ) + +# crypto mbedtls related +if(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO) +zephyr_library_sources( + ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls-bignum.c + ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls-ec.c + ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls.c + ${HOSTAP_SRC_BASE}/crypto/tls_mbedtls.c + ${HOSTAP_SRC_BASE}/crypto/aes-internal.c + ${HOSTAP_SRC_BASE}/crypto/aes-wrap.c + ${HOSTAP_SRC_BASE}/crypto/aes-unwrap.c + ${HOSTAP_SRC_BASE}/crypto/rc4.c + ${HOSTAP_SRC_BASE}/crypto/sha1-internal.c + ${HOSTAP_SRC_BASE}/crypto/sha1-prf.c + ${HOSTAP_SRC_BASE}/crypto/sha1-tlsprf.c + ${HOSTAP_SRC_BASE}/crypto/sha256-prf.c + ${HOSTAP_SRC_BASE}/crypto/sha256-kdf.c + ${HOSTAP_SRC_BASE}/crypto/sha384-prf.c + ${HOSTAP_SRC_BASE}/crypto/sha384-kdf.c + ${HOSTAP_SRC_BASE}/crypto/sha512-internal.c + ${HOSTAP_SRC_BASE}/crypto/sha512.c + ${HOSTAP_SRC_BASE}/crypto/sha512-prf.c + ${HOSTAP_SRC_BASE}/crypto/sha512-kdf.c +) + +zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_WPA3 + ${HOSTAP_SRC_BASE}/crypto/sha256-kdf.c +) + +zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE + # common + ${HOSTAP_SRC_BASE}/crypto/sha384-tlsprf.c + ${HOSTAP_SRC_BASE}/crypto/sha256-tlsprf.c + ${HOSTAP_SRC_BASE}/crypto/sha1-tlsprf.c + ${HOSTAP_SRC_BASE}/crypto/sha1-tprf.c + ${HOSTAP_SRC_BASE}/crypto/ms_funcs.c + ${HOSTAP_SRC_BASE}/crypto/aes-eax.c + # MD4 removed from MbedTLS + ${HOSTAP_SRC_BASE}/crypto/md4-internal.c + ${HOSTAP_SRC_BASE}/crypto/aes-encblock.c +) +endif() + +if(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ALT) +zephyr_include_directories( + ${HOSTAP_BASE}/port/mbedtls +) + +zephyr_library_sources( + ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls_alt.c + ${HOSTAP_SRC_BASE}/crypto/tls_mbedtls_alt.c + ${HOSTAP_SRC_BASE}/crypto/rc4.c +) + +zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE + ${HOSTAP_SRC_BASE}/crypto/ms_funcs.c + ${HOSTAP_SRC_BASE}/crypto/aes-eax.c + ${HOSTAP_SRC_BASE}/crypto/md4-internal.c + ${HOSTAP_SRC_BASE}/crypto/fips_prf_internal.c + ${HOSTAP_SRC_BASE}/crypto/milenage.c +) +endif() + +zephyr_library_link_libraries_ifndef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE + mbedTLS) + endif() diff --git a/modules/hostap/Kconfig b/modules/hostap/Kconfig index 473b6278055c..263c30cf490f 100644 --- a/modules/hostap/Kconfig +++ b/modules/hostap/Kconfig @@ -106,6 +106,8 @@ choice WIFI_NM_WPA_SUPPLICANT_CRYPTO_BACKEND default WIFI_NM_WPA_SUPPLICANT_CRYPTO help Select the crypto implementation to use for WPA supplicant. + WIFI_NM_WPA_SUPPLICANT_CRYPTO_ALT support enterprise + and DPP. And use Mbedtls PSA apis for HW acceleration. config WIFI_NM_WPA_SUPPLICANT_CRYPTO bool "Crypto support for WiFi" @@ -125,6 +127,26 @@ config WIFI_NM_WPA_SUPPLICANT_CRYPTO select MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED select MBEDTLS_KEY_EXCHANGE_ALL_ENABLED +config WIFI_NM_WPA_SUPPLICANT_CRYPTO_ALT + bool "Crypto Mbedtls alt support for WiFi" + select MBEDTLS + select MBEDTLS_CIPHER_MODE_CTR_ENABLED + select MBEDTLS_CIPHER_MODE_CBC_ENABLED + select MBEDTLS_ECP_C + select MBEDTLS_ECP_ALL_ENABLED + select MBEDTLS_CMAC + select MBEDTLS_PKCS5_C + select MBEDTLS_PK_WRITE_C + select MBEDTLS_ECDH_C + select MBEDTLS_ECDSA_C + select MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED + select MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED + select MBEDTLS_NIST_KW_C + select MBEDTLS_DHM_C + select MBEDTLS_HKDF_C + select MBEDTLS_SERVER_NAME_INDICATION + select MBEDTLS_X509_CRL_PARSE_C + config WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE bool "No Crypto support for WiFi" From 0d936436ae4af5b33e54800fb135f101917cbe56 Mon Sep 17 00:00:00 2001 From: Fengming Ye Date: Wed, 29 May 2024 18:09:41 +0900 Subject: [PATCH 13/51] [nrf fromtree] hostap: add PSA apis support for mbedtls 3.x Add Platform Secure Architecture support support to use HW acceleration, which needs to be called under PSA driver wrapper in mbedtls 3.x. Signed-off-by: Fengming Ye (cherry picked from commit d3b3aa1c35e9ff73e1197113653fa713f4e40208) --- modules/hostap/CMakeLists.txt | 4 ++++ modules/hostap/Kconfig | 6 ++++++ modules/hostap/src/supp_main.c | 7 +++++++ 3 files changed, 17 insertions(+) diff --git a/modules/hostap/CMakeLists.txt b/modules/hostap/CMakeLists.txt index 66968e0e249e..de1c18f8e93a 100644 --- a/modules/hostap/CMakeLists.txt +++ b/modules/hostap/CMakeLists.txt @@ -419,6 +419,10 @@ zephyr_library_sources( ${HOSTAP_SRC_BASE}/crypto/rc4.c ) +zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_MBEDTLS_PSA + ${HOSTAP_BASE}/port/mbedtls/supp_psa_api.c +) + zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE ${HOSTAP_SRC_BASE}/crypto/ms_funcs.c ${HOSTAP_SRC_BASE}/crypto/aes-eax.c diff --git a/modules/hostap/Kconfig b/modules/hostap/Kconfig index 263c30cf490f..89667acf6800 100644 --- a/modules/hostap/Kconfig +++ b/modules/hostap/Kconfig @@ -152,6 +152,12 @@ config WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE endchoice +config WIFI_NM_WPA_SUPPLICANT_CRYPTO_MBEDTLS_PSA + bool "Crypto Platform Secure Architecture support for WiFi" + default y if WIFI_NM_WPA_SUPPLICANT_CRYPTO_ALT + help + Support Mbedtls 3.x to use PSA apis instead of legacy apis. + config WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE bool "Enterprise Crypto support for WiFi" depends on !WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE diff --git a/modules/hostap/src/supp_main.c b/modules/hostap/src/supp_main.c index 69a4dd8438b1..80d90c70fb6d 100644 --- a/modules/hostap/src/supp_main.c +++ b/modules/hostap/src/supp_main.c @@ -14,6 +14,9 @@ LOG_MODULE_REGISTER(wifi_supplicant, CONFIG_WIFI_NM_WPA_SUPPLICANT_LOG_LEVEL); #if !defined(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE) && !defined(CONFIG_MBEDTLS_ENABLE_HEAP) #include #endif /* !CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE && !CONFIG_MBEDTLS_ENABLE_HEAP */ +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_MBEDTLS_PSA +#include "supp_psa_api.h" +#endif #include #include @@ -523,6 +526,10 @@ static void handler(void) mbedtls_platform_set_calloc_free(calloc, free); #endif /* !CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE && !CONFIG_MBEDTLS_ENABLE_HEAP */ +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_MBEDTLS_PSA + supp_psa_crypto_init(); +#endif + ctx = get_default_context(); k_work_queue_init(&ctx->iface_wq); From 095467fe705c71205126c5f11d7ea9b247bbfa1c Mon Sep 17 00:00:00 2001 From: Fengming Ye Date: Wed, 29 May 2024 18:13:18 +0900 Subject: [PATCH 14/51] [nrf fromtree] hostap: add DPP support Add kconfig and cmake to support DPP (Easy Connect). Signed-off-by: Fengming Ye (cherry picked from commit 271d7084b5349e77b0d6debc6bdbd1b867b3647b) --- modules/hostap/CMakeLists.txt | 27 +++++++++++++++++++++++++++ modules/hostap/Kconfig | 24 ++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/modules/hostap/CMakeLists.txt b/modules/hostap/CMakeLists.txt index de1c18f8e93a..5ed4d052ab69 100644 --- a/modules/hostap/CMakeLists.txt +++ b/modules/hostap/CMakeLists.txt @@ -366,6 +366,33 @@ zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_NW_SEL_RE CONFIG_NW_SEL_RELIABILITY ) +zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_DPP + ${WIFI_NM_WPA_SUPPLICANT_BASE}/dpp_supplicant.c + ${WIFI_NM_WPA_SUPPLICANT_BASE}/offchannel.c + ${WIFI_NM_WPA_SUPPLICANT_BASE}/gas_query.c + + ${HOSTAP_SRC_BASE}/ap/dpp_hostapd.c + ${HOSTAP_SRC_BASE}/ap/gas_query_ap.c + ${HOSTAP_SRC_BASE}/ap/gas_serv.c + + ${HOSTAP_SRC_BASE}/common/dpp_tcp.c + ${HOSTAP_SRC_BASE}/common/dpp.c + ${HOSTAP_SRC_BASE}/common/dpp_pkex.c + ${HOSTAP_SRC_BASE}/common/dpp_crypto.c + ${HOSTAP_SRC_BASE}/common/dpp_auth.c + ${HOSTAP_SRC_BASE}/common/dpp_reconfig.c + ${HOSTAP_SRC_BASE}/common/gas_server.c + ${HOSTAP_SRC_BASE}/common/gas.c + ${HOSTAP_SRC_BASE}/common/dpp_backup.c + + ${HOSTAP_SRC_BASE}/crypto/aes-siv.c + + ${HOSTAP_SRC_BASE}/utils/json.c + ${HOSTAP_SRC_BASE}/utils/ip_addr.c + + ${HOSTAP_SRC_BASE}/tls/asn1.c +) + # crypto mbedtls related if(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO) zephyr_library_sources( diff --git a/modules/hostap/Kconfig b/modules/hostap/Kconfig index 89667acf6800..5d170cf207c3 100644 --- a/modules/hostap/Kconfig +++ b/modules/hostap/Kconfig @@ -202,6 +202,18 @@ config WIFI_NM_WPA_SUPPLICANT_BSS_MAX_IDLE_TIME config WIFI_NM_WPA_SUPPLICANT_NO_DEBUG bool "Disable printing of debug messages, saves code size significantly" + +config WIFI_NM_WPA_SUPPLICANT_DPP + bool "WFA Easy Connect DPP" + select DPP + select DPP2 + select DPP3 + select GAS + select GAS_SERVER + select OFFCHANNEL + select MBEDTLS_X509_CSR_WRITE_C + select MBEDTLS_X509_CSR_PARSE_C + # Create hidden config options that are used in hostap. This way we do not need # to mark them as allowed for CI checks, and also someone else cannot use the # same name options. @@ -282,6 +294,9 @@ config P2P config GAS bool +config GAS_SERVER + bool + config OFFCHANNEL bool @@ -386,6 +401,15 @@ config RRM config WMM_AC bool +config DPP + bool + +config DPP2 + bool + +config DPP3 + bool + config NW_SEL_RELIABILITY bool default y From d44eff86899e91ad50063284d82063ccadd266b9 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Tue, 6 Aug 2024 00:32:29 +0530 Subject: [PATCH 15/51] [nrf noup] modules: hostap: Use nRF security In NCS to leverage HW acceleration, use nRF security. Also, fix Kconfig warnings in NCS compliance for CRYPTO_ALT. Signed-off-by: Chaitanya Tata --- modules/hostap/CMakeLists.txt | 10 +++++-- modules/hostap/Kconfig | 49 ++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/modules/hostap/CMakeLists.txt b/modules/hostap/CMakeLists.txt index 5ed4d052ab69..e4483d03135e 100644 --- a/modules/hostap/CMakeLists.txt +++ b/modules/hostap/CMakeLists.txt @@ -393,8 +393,10 @@ zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_DPP ${HOSTAP_SRC_BASE}/tls/asn1.c ) -# crypto mbedtls related -if(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO) +# crypto mbedtls related CRYPTO OR LEGACY_NCS +if(DEFINED CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO OR + DEFINED CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_LEGACY_NCS OR + DEFINED CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_LEGACY_NCS_PSA) zephyr_library_sources( ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls-bignum.c ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls-ec.c @@ -462,4 +464,8 @@ endif() zephyr_library_link_libraries_ifndef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE mbedTLS) +zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ALT + MBEDTLS_NIST_KW_C +) + endif() diff --git a/modules/hostap/Kconfig b/modules/hostap/Kconfig index 5d170cf207c3..5a299574b405 100644 --- a/modules/hostap/Kconfig +++ b/modules/hostap/Kconfig @@ -103,7 +103,7 @@ config WIFI_NM_WPA_SUPPLICANT_WEP choice WIFI_NM_WPA_SUPPLICANT_CRYPTO_BACKEND prompt "WPA supplicant crypto implementation" - default WIFI_NM_WPA_SUPPLICANT_CRYPTO + default WIFI_NM_WPA_SUPPLICANT_CRYPTO_LEGACY_NCS help Select the crypto implementation to use for WPA supplicant. WIFI_NM_WPA_SUPPLICANT_CRYPTO_ALT support enterprise @@ -134,19 +134,62 @@ config WIFI_NM_WPA_SUPPLICANT_CRYPTO_ALT select MBEDTLS_CIPHER_MODE_CBC_ENABLED select MBEDTLS_ECP_C select MBEDTLS_ECP_ALL_ENABLED - select MBEDTLS_CMAC + select MBEDTLS_CMAC_C select MBEDTLS_PKCS5_C select MBEDTLS_PK_WRITE_C select MBEDTLS_ECDH_C select MBEDTLS_ECDSA_C select MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED select MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED - select MBEDTLS_NIST_KW_C select MBEDTLS_DHM_C select MBEDTLS_HKDF_C select MBEDTLS_SERVER_NAME_INDICATION select MBEDTLS_X509_CRL_PARSE_C +config WIFI_NM_WPA_SUPPLICANT_CRYPTO_LEGACY_NCS + bool "Legacy Crypto support for WiFi using nRF security" + select MBEDTLS + select NRF_SECURITY + select MBEDTLS_CIPHER_MODE_CBC + select MBEDTLS_CIPHER_MODE_CTR + select MBEDTLS_LEGACY_CRYPTO_C + select MBEDTLS_SHA1_C + select MBEDTLS_ECP_C + select MBEDTLS_CTR_DRBG_C + select MBEDTLS_PK_C + select MBEDTLS_PKCS5_C + select MBEDTLS_PK_PARSE_C + select MBEDTLS_CMAC_C + select MBEDTLS_CIPHER_PADDING_PKCS7 + select MBEDTLS_PK_WRITE_C + select MBEDTLS_KEY_EXCHANGE_ALL_ENABLED + +config WIFI_NM_WPA_SUPPLICANT_CRYPTO_LEGACY_NCS_PSA + bool "PSA Crypto support for WiFi using nRF security" + select MBEDTLS + select NRF_SECURITY + select PSA_WANT_GENERATE_RANDOM + # Legacy crypto, still needed + select MBEDTLS_SHA1_C + select MBEDTLS_LEGACY_CRYPTO_C + select MBEDTLS_CMAC_C + select MBEDTLS_GCM_C + select MBEDTLS_TLS_LIBRARY + select MBEDTLS_PK_C + select MBEDTLS_PK_WRITE_C + select MBEDTLS_X509_LIBRARY + select MBEDTLS_X509_CRT_PARSE_C + select MBEDTLS_CIPHER_C + select MBEDTLS_CIPHER_MODE_CTR + select MBEDTLS_CIPHER_MODE_CBC + select MBEDTLS_SSL_TLS_C + select MBEDTLS_ECP_C + select MBEDTLS_CTR_DRBG_C + select MBEDTLS_KEY_EXCHANGE_ALL_ENABLED + select MBEDTLS_MD_C + select MBEDTLS_CIPHER_PADDING_PKCS7 + select MBEDTLS_PKCS5_C + config WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE bool "No Crypto support for WiFi" From 0064f3a02c53b7b81e01f165f1907c128d99c899 Mon Sep 17 00:00:00 2001 From: Maochen Wang Date: Wed, 22 May 2024 12:11:09 +0800 Subject: [PATCH 16/51] [nrf fromtree] net: wifi: split wifi interface into STA and uAP Split wifi interface into station mode and soft-AP mode, as there may be station and soft-AP two interfaces that work concurrently. Signed-off-by: Maochen Wang (cherry picked from commit 762169034b54313a18c8242392afb89d54e40856) --- include/zephyr/net/net_if.h | 14 ++++++++ include/zephyr/net/wifi_nm.h | 45 +++++++++++++++++++++++-- modules/hostap/src/supp_main.c | 4 +++ subsys/net/ip/net_if.c | 37 ++++++++++++++++++++ subsys/net/l2/wifi/wifi_nm.c | 60 ++++++++++++++++++++++++++++++--- subsys/net/l2/wifi/wifi_shell.c | 8 ++--- 6 files changed, 157 insertions(+), 11 deletions(-) diff --git a/include/zephyr/net/net_if.h b/include/zephyr/net/net_if.h index b1a2ca8925a1..54205794b5e4 100644 --- a/include/zephyr/net/net_if.h +++ b/include/zephyr/net/net_if.h @@ -3013,6 +3013,20 @@ bool net_if_is_wifi(struct net_if *iface); */ struct net_if *net_if_get_first_wifi(void); +/** + * @brief Get Wi-Fi network station interface. + * + * @return Pointer to network interface, NULL if not found. + */ +struct net_if *net_if_get_wifi_sta(void); + +/** + * @brief Get first Wi-Fi network Soft-AP interface. + * + * @return Pointer to network interface, NULL if not found. + */ +struct net_if *net_if_get_wifi_sap(void); + /** * @brief Get network interface name. * diff --git a/include/zephyr/net/wifi_nm.h b/include/zephyr/net/wifi_nm.h index 615d49157572..a09cbd626b7f 100644 --- a/include/zephyr/net/wifi_nm.h +++ b/include/zephyr/net/wifi_nm.h @@ -30,6 +30,24 @@ extern "C" { #endif +/** Types of Wi-Fi interface */ +enum wifi_nm_iface_type { + /** IEEE 802.11 Wi-Fi Station */ + WIFI_TYPE_STA = 0, + /** IEEE 802.11 Wi-Fi Soft AP */ + WIFI_TYPE_SAP, +}; + +/** + * @brief WiFi Network Managed interfaces + */ +struct wifi_nm_mgd_iface { + /** Wi-Fi interface type */ + unsigned char type; + /** Managed net interfaces */ + struct net_if *iface; +}; + /** * @brief WiFi Network manager instance */ @@ -39,7 +57,7 @@ struct wifi_nm_instance { /** Wi-Fi Management operations */ const struct wifi_mgmt_ops *ops; /** List of Managed interfaces */ - struct net_if *mgd_ifaces[CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES]; + struct wifi_nm_mgd_iface mgd_ifaces[CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES]; }; /** @cond INTERNAL_HIDDEN */ @@ -50,7 +68,7 @@ struct wifi_nm_instance { static STRUCT_SECTION_ITERABLE(wifi_nm_instance, WIFI_NM_NAME(_name)) = { \ .name = STRINGIFY(_name), \ .ops = _ops, \ - .mgd_ifaces = { NULL }, \ + .mgd_ifaces = {}, \ } /** @endcond */ @@ -71,6 +89,14 @@ struct wifi_nm_instance *wifi_nm_get_instance(const char *name); */ struct wifi_nm_instance *wifi_nm_get_instance_iface(struct net_if *iface); +/** + * @brief Get a Wi-Fi type for a given interface + * + * @param iface Interface + * + */ +unsigned char wifi_nm_get_type_iface(struct net_if *iface); + /** * @brief Register a managed interface * @@ -84,6 +110,21 @@ struct wifi_nm_instance *wifi_nm_get_instance_iface(struct net_if *iface); */ int wifi_nm_register_mgd_iface(struct wifi_nm_instance *nm, struct net_if *iface); +/** + * @brief Register a managed interface + * + * @param nm Pointer to Network manager instance + * @param type Wi-Fi type + * @param iface Managed interface + * + * @retval 0 If successful. + * @retval -EINVAL If invalid parameters were passed. + * @retval -ENOTSUP If the interface is not a Wi-Fi interface. + * @retval -ENOMEM If the maximum number of managed interfaces has been reached. + */ +int wifi_nm_register_mgd_type_iface(struct wifi_nm_instance *nm, + enum wifi_nm_iface_type type, struct net_if *iface); + /** * @brief Unregister managed interface * diff --git a/modules/hostap/src/supp_main.c b/modules/hostap/src/supp_main.c index 80d90c70fb6d..4558b2b74cbf 100644 --- a/modules/hostap/src/supp_main.c +++ b/modules/hostap/src/supp_main.c @@ -412,6 +412,10 @@ static void iface_cb(struct net_if *iface, void *user_data) return; } + if (wifi_nm_get_type_iface(iface) != (1 << WIFI_TYPE_STA)) { + return; + } + if (!net_if_is_up(iface)) { return; } diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index dc115aaf2c32..8e431241f51f 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -22,6 +22,9 @@ LOG_MODULE_REGISTER(net_if, CONFIG_NET_IF_LOG_LEVEL); #include #include #include +#ifdef CONFIG_WIFI_NM +#include +#endif #include #include #include @@ -5568,6 +5571,40 @@ struct net_if *net_if_get_first_wifi(void) return NULL; } +struct net_if *net_if_get_wifi_sta(void) +{ + struct ethernet_context *eth_ctx = NULL; + + STRUCT_SECTION_FOREACH(net_if, iface) { + eth_ctx = net_if_l2_data(iface); + if (net_if_is_wifi(iface) +#ifdef CONFIG_WIFI_NM + && (wifi_nm_get_type_iface(iface) == (1 << WIFI_TYPE_STA)) +#endif + ) { + return iface; + } + } + return NULL; +} + +struct net_if *net_if_get_wifi_sap(void) +{ + struct ethernet_context *eth_ctx = NULL; + + STRUCT_SECTION_FOREACH(net_if, iface) { + eth_ctx = net_if_l2_data(iface); + if (net_if_is_wifi(iface) +#ifdef CONFIG_WIFI_NM + && (wifi_nm_get_type_iface(iface) == (1 << WIFI_TYPE_SAP)) +#endif + ) { + return iface; + } + } + return NULL; +} + int net_if_get_name(struct net_if *iface, char *buf, int len) { #if defined(CONFIG_NET_INTERFACE_NAME) diff --git a/subsys/net/l2/wifi/wifi_nm.c b/subsys/net/l2/wifi/wifi_nm.c index 588992634bf2..74225cf04548 100644 --- a/subsys/net/l2/wifi/wifi_nm.c +++ b/subsys/net/l2/wifi/wifi_nm.c @@ -33,7 +33,7 @@ struct wifi_nm_instance *wifi_nm_get_instance_iface(struct net_if *iface) k_mutex_lock(&wifi_nm_lock, K_FOREVER); STRUCT_SECTION_FOREACH(wifi_nm_instance, nm) { for (int i = 0; i < CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES; i++) { - if (nm->mgd_ifaces[i] == iface) { + if (nm->mgd_ifaces[i].iface == iface) { k_mutex_unlock(&wifi_nm_lock); return nm; } @@ -44,6 +44,26 @@ struct wifi_nm_instance *wifi_nm_get_instance_iface(struct net_if *iface) return NULL; } +unsigned char wifi_nm_get_type_iface(struct net_if *iface) +{ + if (!iface || !net_if_is_wifi(iface)) { + return 0; + } + + k_mutex_lock(&wifi_nm_lock, K_FOREVER); + STRUCT_SECTION_FOREACH(wifi_nm_instance, nm) { + for (int i = 0; i < CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES; i++) { + if (nm->mgd_ifaces[i].iface == iface) { + k_mutex_unlock(&wifi_nm_lock); + return nm->mgd_ifaces[i].type; + } + } + } + + k_mutex_unlock(&wifi_nm_lock); + return 0; +} + int wifi_nm_register_mgd_iface(struct wifi_nm_instance *nm, struct net_if *iface) { if (!nm || !iface) { @@ -56,8 +76,38 @@ int wifi_nm_register_mgd_iface(struct wifi_nm_instance *nm, struct net_if *iface k_mutex_lock(&wifi_nm_lock, K_FOREVER); for (int i = 0; i < CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES; i++) { - if (!nm->mgd_ifaces[i]) { - nm->mgd_ifaces[i] = iface; + if (nm->mgd_ifaces[i].iface == iface) { + k_mutex_unlock(&wifi_nm_lock); + return 0; + } + + if (!nm->mgd_ifaces[i].iface) { + nm->mgd_ifaces[i].iface = iface; + k_mutex_unlock(&wifi_nm_lock); + return 0; + } + } + k_mutex_unlock(&wifi_nm_lock); + + return -ENOMEM; +} + +int wifi_nm_register_mgd_type_iface(struct wifi_nm_instance *nm, + enum wifi_nm_iface_type type, struct net_if *iface) +{ + if (!nm || !iface) { + return -EINVAL; + } + + if (!net_if_is_wifi(iface)) { + return -ENOTSUP; + } + + k_mutex_lock(&wifi_nm_lock, K_FOREVER); + for (int i = 0; i < CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES; i++) { + if (!nm->mgd_ifaces[i].iface) { + nm->mgd_ifaces[i].iface = iface; + nm->mgd_ifaces[i].type = (1 << type); k_mutex_unlock(&wifi_nm_lock); return 0; } @@ -75,8 +125,8 @@ int wifi_nm_unregister_mgd_iface(struct wifi_nm_instance *nm, struct net_if *ifa k_mutex_lock(&wifi_nm_lock, K_FOREVER); for (int i = 0; i < CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES; i++) { - if (nm->mgd_ifaces[i] == iface) { - nm->mgd_ifaces[i] = NULL; + if (nm->mgd_ifaces[i].iface == iface) { + nm->mgd_ifaces[i].iface = NULL; k_mutex_unlock(&wifi_nm_lock); return 0; } diff --git a/subsys/net/l2/wifi/wifi_shell.c b/subsys/net/l2/wifi/wifi_shell.c index c68c1c07cb59..5adad439a046 100644 --- a/subsys/net/l2/wifi/wifi_shell.c +++ b/subsys/net/l2/wifi/wifi_shell.c @@ -607,7 +607,7 @@ static int __wifi_args_to_params(const struct shell *sh, size_t argc, char *argv static int cmd_wifi_connect(const struct shell *sh, size_t argc, char *argv[]) { - struct net_if *iface = net_if_get_first_wifi(); + struct net_if *iface = net_if_get_wifi_sta(); struct wifi_connect_req_params cnx_params = { 0 }; int ret; @@ -634,7 +634,7 @@ static int cmd_wifi_connect(const struct shell *sh, size_t argc, static int cmd_wifi_disconnect(const struct shell *sh, size_t argc, char *argv[]) { - struct net_if *iface = net_if_get_first_wifi(); + struct net_if *iface = net_if_get_wifi_sta(); int status; context.disconnecting = true; @@ -1283,7 +1283,7 @@ static int cmd_wifi_twt_teardown_all(const struct shell *sh, size_t argc, static int cmd_wifi_ap_enable(const struct shell *sh, size_t argc, char *argv[]) { - struct net_if *iface = net_if_get_first_wifi(); + struct net_if *iface = net_if_get_wifi_sap(); static struct wifi_connect_req_params cnx_params; int ret; @@ -1310,7 +1310,7 @@ static int cmd_wifi_ap_enable(const struct shell *sh, size_t argc, static int cmd_wifi_ap_disable(const struct shell *sh, size_t argc, char *argv[]) { - struct net_if *iface = net_if_get_first_wifi(); + struct net_if *iface = net_if_get_wifi_sap(); int ret; ret = net_mgmt(NET_REQUEST_WIFI_AP_DISABLE, iface, NULL, 0); From 7700c7346c7b40cd0ce5a27ea7a7ebd29ee96448 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Mon, 24 Jun 2024 22:23:08 +0530 Subject: [PATCH 17/51] [nrf fromtree] modules: hostap: Fix check for iface up We only need the interface to be administratively up, the operationl status is managed by the WPA supplicant. Signed-off-by: Chaitanya Tata (cherry picked from commit 650227d8c47f183b8c520b9abbbada2515982d05) --- modules/hostap/src/supp_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hostap/src/supp_main.c b/modules/hostap/src/supp_main.c index 4558b2b74cbf..a42515634444 100644 --- a/modules/hostap/src/supp_main.c +++ b/modules/hostap/src/supp_main.c @@ -416,7 +416,7 @@ static void iface_cb(struct net_if *iface, void *user_data) return; } - if (!net_if_is_up(iface)) { + if (!net_if_is_admin_up(iface)) { return; } From 03e19c0657f44bf9055db0e3a79c30fff9aa61da Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Tue, 2 Jul 2024 21:27:08 +0530 Subject: [PATCH 18/51] [nrf fromtree] modules: hostap: Fix interface addition WPA supplicant as a network manager monitors interface events and registers to the Wi-Fi NM module, so, adding a check for NM type before registering the interface is wrong. Signed-off-by: Chaitanya Tata (cherry picked from commit 0e6db9c918245136bddbcb3ef921c0d607492525) --- modules/hostap/src/supp_main.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/hostap/src/supp_main.c b/modules/hostap/src/supp_main.c index a42515634444..8f4d2245ecdb 100644 --- a/modules/hostap/src/supp_main.c +++ b/modules/hostap/src/supp_main.c @@ -412,10 +412,6 @@ static void iface_cb(struct net_if *iface, void *user_data) return; } - if (wifi_nm_get_type_iface(iface) != (1 << WIFI_TYPE_STA)) { - return; - } - if (!net_if_is_admin_up(iface)) { return; } From 9a0955a01349fd64b2f2f4e57fdac29fc583e6b9 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Mon, 24 Jun 2024 22:22:33 +0530 Subject: [PATCH 19/51] [nrf fromtree] modules: hostap: Fix SoF Due to recent changes to hostap, the stack usage is increased, so, increase the stack size to fix SoF. Signed-off-by: Chaitanya Tata (cherry picked from commit 176a47c2dfa18bb0726b55a7c207eafe3d145351) --- modules/hostap/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hostap/Kconfig b/modules/hostap/Kconfig index 5a299574b405..91ff7fefee3a 100644 --- a/modules/hostap/Kconfig +++ b/modules/hostap/Kconfig @@ -35,7 +35,7 @@ config WIFI_NM_WPA_SUPPLICANT_THREAD_STACK_SIZE config WIFI_NM_WPA_SUPPLICANT_WQ_STACK_SIZE int "Stack size for wpa_supplicant iface workqueue" - default 4096 + default 6144 config WIFI_NM_WPA_SUPPLICANT_WQ_PRIO int "Thread priority of wpa_supplicant iface workqueue" From ac558763af71c28fa321668e8fb4a722d8326201 Mon Sep 17 00:00:00 2001 From: Fengming Ye Date: Wed, 29 May 2024 18:18:01 +0900 Subject: [PATCH 20/51] [nrf fromtree] hostap: remove CONFIG_NO_PBKDF2 and CONFIG_NO_CONFIG_BLOBS in cmake Remove CONFIG_NO_PBKDF2 and CONFIG_NO_CONFIG_BLOBS definition and let them be decided in kconfig. CONFIG_NO_PBKDF2 is default y when crypto backend is WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE. CONFIG_NO_CONFIG_BLOBS is default y when both DPP and enterprise disable. Signed-off-by: Fengming Ye (cherry picked from commit bdb0768882b8bf4051e96e416407067d065dd42f) --- modules/hostap/CMakeLists.txt | 5 ----- modules/hostap/Kconfig | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/modules/hostap/CMakeLists.txt b/modules/hostap/CMakeLists.txt index e4483d03135e..815bd4afec34 100644 --- a/modules/hostap/CMakeLists.txt +++ b/modules/hostap/CMakeLists.txt @@ -25,7 +25,6 @@ zephyr_library_compile_definitions( TLS_DEFAULT_CIPHERS=\""DEFAULT:!EXP:!LOW"\" CONFIG_SME CONFIG_NO_CONFIG_WRITE - CONFIG_NO_CONFIG_BLOBS CONFIG_CTRL_IFACE CONFIG_NO_RANDOM_POOL CONFIG_SHA256 @@ -76,10 +75,6 @@ zephyr_library_include_directories( ${ZEPHYR_BASE}/include/net ) -zephyr_library_compile_definitions_ifndef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO - CONFIG_NO_PBKDF2 -) - zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_NO_DEBUG CONFIG_NO_STDOUT_DEBUG ) diff --git a/modules/hostap/Kconfig b/modules/hostap/Kconfig index 91ff7fefee3a..01014e668fbf 100644 --- a/modules/hostap/Kconfig +++ b/modules/hostap/Kconfig @@ -271,7 +271,7 @@ config NO_CONFIG_WRITE config NO_CONFIG_BLOBS bool - default y + default y if !WIFI_NM_WPA_SUPPLICANT_DPP && !WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE config CTRL_IFACE bool @@ -294,7 +294,7 @@ config NO_WPA config NO_PBKDF2 bool - default y + default y if WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE config SAE_PK bool From e0a8a9439bfe9030ab9650b993f68757edf953c4 Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Tue, 25 Jun 2024 10:29:27 +0200 Subject: [PATCH 21/51] [nrf fromtree] dts: arm: nordic: nrf5340: instantiate regulators Instantiate all available regulators: VREGMAIN, VREGRADIO and VREGH. Signed-off-by: Gerard Marull-Paretas (cherry picked from commit 7feacc62d138c96bbf3ec613ccfe0982bcda46da) --- .../nordic/nrf5340_cpuapp_peripherals.dtsi | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi b/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi index c9f1ff2fae98..199ea262f589 100644 --- a/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi +++ b/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi @@ -4,6 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include + dcnf: dcnf@0 { compatible = "nordic,nrf-dcnf"; reg = <0x0 0x1000>; @@ -20,6 +22,31 @@ regulators: regulator@4000 { compatible = "nordic,nrf-regulators"; reg = <0x4000 0x1000>; status = "okay"; + #address-cells = <1>; + #size-cells = <1>; + + vregmain: regulator@4704 { + compatible = "nordic,nrf5x-regulator"; + reg = <0x4704 0x1>; + status = "okay"; + regulator-name = "VREGMAIN"; + regulator-initial-mode = ; + }; + + vregradio: regulator@4904 { + compatible = "nordic,nrf5x-regulator"; + reg = <0x4904 0x1>; + status = "okay"; + regulator-name = "VREGRADIO"; + regulator-initial-mode = ; + }; + + vregh: regulator@4b00 { + compatible = "nordic,nrf53x-regulator-hv"; + reg = <0x4b00 0x44>; + status = "disabled"; + regulator-name = "VREGH"; + }; }; clock: clock@5000 { From 01f4bb7774b8c34e193e4bcc0a3c4f666c1edb87 Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Thu, 20 Jun 2024 10:11:16 +0200 Subject: [PATCH 22/51] [nrf fromtree] dts: bindings: regulator: add nordic,nrf5[2]x-regulator[-hv] Some Nordic SoCs, like nRF52 contain an internal regulator for the main SoC supply. Depending on the SoC, the regulator can have multiple configurations (e.g. single/double stage), which mainly depends on supporting "high-voltage" mode or not (VDDH pin supply). This patch adds bindings for nRF5X regulator and nRF52X HV regulator. Signed-off-by: Gerard Marull-Paretas (cherry picked from commit 02a30c1c2bdce312c7f99dd08756e9d99e3a7e17) --- .../regulator/nordic,nrf52x-regulator-hv.yaml | 20 ++++++++++++++ .../regulator/nordic,nrf5x-regulator.yaml | 24 +++++++++++++++++ include/zephyr/dt-bindings/regulator/nrf5x.h | 27 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 dts/bindings/regulator/nordic,nrf52x-regulator-hv.yaml create mode 100644 dts/bindings/regulator/nordic,nrf5x-regulator.yaml create mode 100644 include/zephyr/dt-bindings/regulator/nrf5x.h diff --git a/dts/bindings/regulator/nordic,nrf52x-regulator-hv.yaml b/dts/bindings/regulator/nordic,nrf52x-regulator-hv.yaml new file mode 100644 index 000000000000..3af5dbdff735 --- /dev/null +++ b/dts/bindings/regulator/nordic,nrf52x-regulator-hv.yaml @@ -0,0 +1,20 @@ +# Copyright (c), 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: | + Nordic nRF52X regulator (high voltage stage of the main supply) + +compatible: "nordic,nrf52x-regulator-hv" + +include: + - name: base.yaml + - name: regulator.yaml + property-allowlist: + - regulator-name + +properties: + reg: + required: true + + regulator-name: + required: true diff --git a/dts/bindings/regulator/nordic,nrf5x-regulator.yaml b/dts/bindings/regulator/nordic,nrf5x-regulator.yaml new file mode 100644 index 000000000000..530a0634e761 --- /dev/null +++ b/dts/bindings/regulator/nordic,nrf5x-regulator.yaml @@ -0,0 +1,24 @@ +# Copyright (c), 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: | + Nordic nRF5X regulator (fixed stage of the core supply) + +compatible: "nordic,nrf5x-regulator" + +include: + - name: base.yaml + - name: regulator.yaml + property-allowlist: + - regulator-name + - regulator-initial-mode + +properties: + reg: + required: true + + regulator-name: + required: true + + regulator-initial-mode: + required: true diff --git a/include/zephyr/dt-bindings/regulator/nrf5x.h b/include/zephyr/dt-bindings/regulator/nrf5x.h new file mode 100644 index 000000000000..d2507c74a6ea --- /dev/null +++ b/include/zephyr/dt-bindings/regulator/nrf5x.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_REGULATOR_NRF5X_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_REGULATOR_NRF_H_ + +/** + * @defgroup regulator_nrf5x nRF5X regulator devicetree helpers. + * @ingroup regulator_interface + * @{ + */ + +/** + * @name nRF5X regulator modes + * @{ + */ +/** LDO mode */ +#define NRF5X_REG_MODE_LDO 0 +/** DC/DC mode */ +#define NRF5X_REG_MODE_DCDC 1 +/** @} */ + +/** @} */ + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_REGULATOR_NRF5X_H_*/ From b914537d8dcb106fd9791811c8ad3909a35b850e Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 7 Aug 2024 18:46:27 +0530 Subject: [PATCH 23/51] [nrf noup] nrf54: Disable Wi-Fi tests nRF70 Wi-Fi upstream doesn't yet support these boards. Signed-off-by: Chaitanya Tata --- boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.yaml | 4 ++++ boards/nordic/nrf54l15pdk/nrf54l15pdk_nrf54l15_cpuapp.yaml | 4 ++++ boards/nordic/nrf54l15pdk/nrf54l15pdk_nrf54l15_cpuapp_ns.yaml | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.yaml b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.yaml index 1fb5a0398752..528846422f09 100644 --- a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.yaml +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.yaml @@ -22,3 +22,7 @@ supported: - spi - watchdog - usbd +testing: + default: true + ignore_tags: + - ci_samples_wifi diff --git a/boards/nordic/nrf54l15pdk/nrf54l15pdk_nrf54l15_cpuapp.yaml b/boards/nordic/nrf54l15pdk/nrf54l15pdk_nrf54l15_cpuapp.yaml index 9213c49fdd7b..4f415b266d2c 100644 --- a/boards/nordic/nrf54l15pdk/nrf54l15pdk_nrf54l15_cpuapp.yaml +++ b/boards/nordic/nrf54l15pdk/nrf54l15pdk_nrf54l15_cpuapp.yaml @@ -21,3 +21,7 @@ supported: - spi - watchdog - i2s +testing: + default: true + ignore_tags: + - ci_samples_wifi diff --git a/boards/nordic/nrf54l15pdk/nrf54l15pdk_nrf54l15_cpuapp_ns.yaml b/boards/nordic/nrf54l15pdk/nrf54l15pdk_nrf54l15_cpuapp_ns.yaml index 460803eaa53d..b38fd67619a5 100644 --- a/boards/nordic/nrf54l15pdk/nrf54l15pdk_nrf54l15_cpuapp_ns.yaml +++ b/boards/nordic/nrf54l15pdk/nrf54l15pdk_nrf54l15_cpuapp_ns.yaml @@ -20,3 +20,7 @@ supported: - watchdog - adc - i2s +testing: + default: true + ignore_tags: + - ci_samples_wifi From edeb34fead451196e896aea08ffa6e8199282aaf Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Tue, 6 Aug 2024 21:51:03 +0530 Subject: [PATCH 24/51] [nrf fromtree] drivers: wifi: nrf: Fix include path for version The version header is generated during build and generated path included already has "zephyr" directory. Signed-off-by: Chaitanya Tata (cherry picked from commit 8c3f1cf66249c596ec855d70d6f314778f63ca5c) --- drivers/wifi/nrfwifi/inc/fmac_main.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/wifi/nrfwifi/inc/fmac_main.h b/drivers/wifi/nrfwifi/inc/fmac_main.h index dbe6c34baa0f..f5de235a0f51 100644 --- a/drivers/wifi/nrfwifi/inc/fmac_main.h +++ b/drivers/wifi/nrfwifi/inc/fmac_main.h @@ -14,8 +14,9 @@ #include +#include + #include -#include #include #ifndef CONFIG_NRF70_RADIO_TEST #include From 54f403a2c19bbca49b9ad3524b462e478c232943 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Tue, 6 Aug 2024 22:01:07 +0530 Subject: [PATCH 25/51] [nrf fromtree] drivers: wifi: nrf: Fix build error in utils When Wi-Fi utils is enabled it causes build error due to missing rename in a couple of places. Signed-off-by: Chaitanya Tata (cherry picked from commit bf4d8cc2cc7113ca3ca36bcb91be3b32f9f124cf) --- drivers/wifi/nrfwifi/src/wifi_util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/wifi/nrfwifi/src/wifi_util.c b/drivers/wifi/nrfwifi/src/wifi_util.c index 4b267cb89cda..7dc4e78473ac 100644 --- a/drivers/wifi/nrfwifi/src/wifi_util.c +++ b/drivers/wifi/nrfwifi/src/wifi_util.c @@ -298,7 +298,7 @@ static int nrf_wifi_util_tx_stats(const struct shell *sh, tx_pending_pkts = nrf_wifi_utils_q_len(fmac_dev_ctx->fpriv->opriv, queue); shell_fprintf( - shell, + sh, SHELL_INFO, "Outstanding tokens: ac: %d -> %d (pending_q_len: %d)\n", i, @@ -346,7 +346,7 @@ static int nrf_wifi_util_tx_rate(const struct shell *sh, data_rate = strtol(argv[2], &ptr, 10); - if (!(check_valid_data_rate(shell, + if (!(check_valid_data_rate(sh, rate_flag, data_rate))) { shell_fprintf(sh, From b4139394d2a0f15bf6b92c6b56f0f962be89a3e8 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 7 Aug 2024 00:38:11 +0530 Subject: [PATCH 26/51] [nrf fromtree] modules: hostap: Fix interface registration to NM Wi-Fi shell now uses _sta/_ap APIs to getch specific inteface types, so, by default register as a Station. This needs more work to handle multiple modes and mode switching. Signed-off-by: Chaitanya Tata (cherry picked from commit 3409f16604980b478f7f2ab7dcc09a5081ba7cf4) --- modules/hostap/src/supp_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/hostap/src/supp_main.c b/modules/hostap/src/supp_main.c index 8f4d2245ecdb..43d438044bd9 100644 --- a/modules/hostap/src/supp_main.c +++ b/modules/hostap/src/supp_main.c @@ -232,7 +232,9 @@ static int add_interface(struct supplicant_context *ctx, struct net_if *iface) goto out; } - ret = wifi_nm_register_mgd_iface(wifi_nm_get_instance("wifi_supplicant"), iface); + ret = wifi_nm_register_mgd_type_iface(wifi_nm_get_instance("wifi_supplicant"), + WIFI_TYPE_STA, + iface); if (ret) { LOG_ERR("Failed to register mgd iface with native stack %s (%d)", ifname, ret); From b2ee8e8b919d2f3d66496f00b412b76463e49693 Mon Sep 17 00:00:00 2001 From: Kapil Bhatt Date: Tue, 11 Jun 2024 11:32:29 +0530 Subject: [PATCH 27/51] [nrf noup] drivers: wifi: Add kconfig option to disable WMM feature [SHEL-2054] Adding a kconfig option for WMM. By default it will be enabled. If user needs to disable it, set it as n. Tagged as "noup" because I had to fix a conflict because of another "noup". Upstream PR: https://github.com/zephyrproject-rtos/zephyr/pull/76754 Signed-off-by: Kapil Bhatt --- drivers/wifi/nrfwifi/Kconfig.nrfwifi | 6 ++++++ drivers/wifi/nrfwifi/src/fmac_main.c | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/wifi/nrfwifi/Kconfig.nrfwifi b/drivers/wifi/nrfwifi/Kconfig.nrfwifi index 3b3ce26bda73..a83320f864f0 100644 --- a/drivers/wifi/nrfwifi/Kconfig.nrfwifi +++ b/drivers/wifi/nrfwifi/Kconfig.nrfwifi @@ -694,4 +694,10 @@ config NRF_WIFI_PATCHES_EXT_FLASH_XIP help Select this option to enable external flash XIP for nRF70 firmware patches +config NRF_WIFI_FEAT_WMM + bool "WMM/QoS support" + default y + help + This option controls disable/enable of the WMM(Wireless Multi-Media) feature. + endif # WIFI_NRF70 diff --git a/drivers/wifi/nrfwifi/src/fmac_main.c b/drivers/wifi/nrfwifi/src/fmac_main.c index a6ae71e4b3cd..9f762b16f1b9 100644 --- a/drivers/wifi/nrfwifi/src/fmac_main.c +++ b/drivers/wifi/nrfwifi/src/fmac_main.c @@ -72,7 +72,6 @@ BUILD_ASSERT(CONFIG_NRF70_TX_MAX_DATA_SIZE >= MAX_TX_FRAME_SIZE, "TX buffer size must be at least as big as the MTU and headroom"); static const unsigned char aggregation = 1; -static const unsigned char wmm = 1; static const unsigned char max_num_tx_agg_sessions = 4; static const unsigned char max_num_rx_agg_sessions = 8; static const unsigned char reorder_buf_size = 16; @@ -706,7 +705,7 @@ static int nrf_wifi_drv_main_zep(const struct device *dev) #ifdef CONFIG_NRF70_DATA_TX data_config.aggregation = aggregation; - data_config.wmm = wmm; + data_config.wmm = IS_ENABLED(CONFIG_NRF_WIFI_FEAT_WMM); data_config.max_num_tx_agg_sessions = max_num_tx_agg_sessions; data_config.max_num_rx_agg_sessions = max_num_rx_agg_sessions; data_config.max_tx_aggregation = max_tx_aggregation; From 534b193810f641b62c4f5522322236fae8266c6b Mon Sep 17 00:00:00 2001 From: Maochen Wang Date: Wed, 22 May 2024 12:11:34 +0800 Subject: [PATCH 28/51] [nrf fromtree] modules: hostap: fix hostap compile error and support enterprise Fix compile error when enable enterprise security mode. Signed-off-by: Maochen Wang (cherry picked from commit 001ac3976ad77d446fa21b49d5aac1d5f73d4247) --- include/zephyr/net/ethernet.h | 3 ++ include/zephyr/net/wifi_mgmt.h | 2 +- modules/hostap/CMakeLists.txt | 50 +++++++++++++++++++++++----------- modules/hostap/Kconfig | 7 +++++ modules/hostap/src/wpa_cli.c | 2 +- 5 files changed, 46 insertions(+), 18 deletions(-) diff --git a/include/zephyr/net/ethernet.h b/include/zephyr/net/ethernet.h index 30fbb200bce4..e49a381c45ce 100644 --- a/include/zephyr/net/ethernet.h +++ b/include/zephyr/net/ethernet.h @@ -92,6 +92,9 @@ struct net_eth_addr { #if !defined(ETH_P_ECAT) #define ETH_P_ECAT NET_ETH_PTYPE_ECAT #endif +#if !defined(ETH_P_EAPOL) +#define ETH_P_EAPOL NET_ETH_PTYPE_EAPOL +#endif #if !defined(ETH_P_IEEE802154) #define ETH_P_IEEE802154 NET_ETH_PTYPE_IEEE802154 #endif diff --git a/include/zephyr/net/wifi_mgmt.h b/include/zephyr/net/wifi_mgmt.h index deab64cc7562..cce48e90c84d 100644 --- a/include/zephyr/net/wifi_mgmt.h +++ b/include/zephyr/net/wifi_mgmt.h @@ -978,7 +978,7 @@ struct net_wifi_mgmt_offload { #if defined(CONFIG_WIFI_NM_WPA_SUPPLICANT) || defined(__DOXYGEN__) /** Wi-Fi supplicant driver API */ - void *wifi_drv_ops; + const void *wifi_drv_ops; #endif }; diff --git a/modules/hostap/CMakeLists.txt b/modules/hostap/CMakeLists.txt index 815bd4afec34..c80c6fe95ab3 100644 --- a/modules/hostap/CMakeLists.txt +++ b/modules/hostap/CMakeLists.txt @@ -16,9 +16,19 @@ set(CMAKE_EXE_LINKER_FLAGS "--specs=nosys.specs -lnosys") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DMISSING_SYSCALL_NAMES") zephyr_include_directories( + src/ ${HOSTAP_BASE}/ ${WIFI_NM_WPA_SUPPLICANT_BASE}/ ${HOSTAP_SRC_BASE}/ + ${HOSTAP_SRC_BASE}/utils/ + ${HOSTAP_SRC_BASE}/common/ + ${HOSTAP_SRC_BASE}/eap_common + ${HOSTAP_SRC_BASE}/eap_server + ${HOSTAP_SRC_BASE}/radius + ${HOSTAP_SRC_BASE}/crypto/ + ${HOSTAP_SRC_BASE}/ap/ + ${HOSTAP_SRC_BASE}/drivers/ + ${HOSTAP_SRC_BASE}/rsn_supp ) zephyr_library_compile_definitions( @@ -28,6 +38,8 @@ zephyr_library_compile_definitions( CONFIG_CTRL_IFACE CONFIG_NO_RANDOM_POOL CONFIG_SHA256 + CONFIG_SHA384 + CONFIG_SHA512 CONFIG_CTRL_IFACE_ZEPHYR CONFIG_SUITEB192 ) @@ -88,6 +100,7 @@ zephyr_library_sources( ${HOSTAP_SRC_BASE}/drivers/driver_common.c ${HOSTAP_SRC_BASE}/drivers/drivers.c + ${HOSTAP_SRC_BASE}/utils/crc32.c ${HOSTAP_SRC_BASE}/utils/base64.c ${HOSTAP_SRC_BASE}/utils/common.c ${HOSTAP_SRC_BASE}/utils/wpabuf.c @@ -154,6 +167,7 @@ zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_AP ${HOSTAP_SRC_BASE}/ap/bss_load.c ${HOSTAP_SRC_BASE}/ap/dfs.c ${HOSTAP_SRC_BASE}/ap/drv_callbacks.c + ${HOSTAP_SRC_BASE}/ap/ctrl_iface_ap.c ${HOSTAP_SRC_BASE}/ap/eap_user_db.c ${HOSTAP_SRC_BASE}/ap/hostapd.c ${HOSTAP_SRC_BASE}/ap/hw_features.c @@ -328,21 +342,25 @@ zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE ) zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE - CONFIG_EAP_TLS - CONFIG_IEEE8021X_EAPOL - CONFIG_EAP_PEAP - CONFIG_EAP_TTLS - CONFIG_EAP_MD5 - CONFIG_EAP_MSCHAPv2 - CONFIG_EAP_LEAP - CONFIG_EAP_PSK - CONFIG_EAP_FAST - CONFIG_EAP_PAX - CONFIG_EAP_SAKE - CONFIG_EAP_GPSK - CONFIG_EAP_PWD - CONFIG_EAP_EKE - CONFIG_EAP_IKEv2 + EAP_TLS + IEEE8021X_EAPOL + EAP_PEAP + EAP_TTLS + EAP_MD5 + EAP_MSCHAPv2 + EAP_LEAP + EAP_PSK + EAP_FAST + EAP_PAX + EAP_SAKE + EAP_GPSK + EAP_PWD + EAP_EKE + EAP_IKEv2 +) + +zephyr_library_compile_definitions_ifndef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE + CONFIG_NO_CONFIG_BLOBS ) zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_EAPOL @@ -354,7 +372,7 @@ zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_EAPOL ) zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_EAPOL - CONFIG_IEEE8021X_EAPOL + IEEE8021X_EAPOL ) zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_NW_SEL_RELIABILITY diff --git a/modules/hostap/Kconfig b/modules/hostap/Kconfig index 01014e668fbf..62850fd83e07 100644 --- a/modules/hostap/Kconfig +++ b/modules/hostap/Kconfig @@ -10,6 +10,7 @@ config WIFI_NM_WPA_SUPPLICANT select POSIX_CLOCK select POSIX_SIGNAL select POSIX_API + select FILE_SYSTEM select NET_SOCKETS select NET_SOCKETS_PACKET select NET_SOCKETPAIR @@ -415,6 +416,12 @@ config SAE config SHA256 bool +config SHA384 + bool + +config SHA512 + bool + config SUITEB192 bool diff --git a/modules/hostap/src/wpa_cli.c b/modules/hostap/src/wpa_cli.c index cbd66b1279d0..1ea6be54a5da 100644 --- a/modules/hostap/src/wpa_cli.c +++ b/modules/hostap/src/wpa_cli.c @@ -29,7 +29,7 @@ static int cmd_wpa_cli(const struct shell *sh, argc++; /* Remove wpa_cli from the argument list */ - return z_wpa_ctrl_zephyr_cmd(argc - 1, &argv[1]); + return zephyr_wpa_ctrl_zephyr_cmd(argc - 1, &argv[1]); } /* Persisting with "wpa_cli" naming for compatibility with Wi-Fi From d76dcb3ac5e637acd5e7fe5c7855f528c8fedb57 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 7 Aug 2024 21:20:01 +0530 Subject: [PATCH 29/51] [nrf noup] modules: hostap: Fix build errors Include paths all the way down to the file with common header filenames cause compilation errors, e.g., "common.h" can be present and used by multiple modules, temporarily remove this as this is mainly for CYRPTO_ALT, need to fix this properly. Signed-off-by: Chaitanya Tata --- modules/hostap/CMakeLists.txt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/modules/hostap/CMakeLists.txt b/modules/hostap/CMakeLists.txt index c80c6fe95ab3..4716cbf087f6 100644 --- a/modules/hostap/CMakeLists.txt +++ b/modules/hostap/CMakeLists.txt @@ -20,15 +20,6 @@ zephyr_include_directories( ${HOSTAP_BASE}/ ${WIFI_NM_WPA_SUPPLICANT_BASE}/ ${HOSTAP_SRC_BASE}/ - ${HOSTAP_SRC_BASE}/utils/ - ${HOSTAP_SRC_BASE}/common/ - ${HOSTAP_SRC_BASE}/eap_common - ${HOSTAP_SRC_BASE}/eap_server - ${HOSTAP_SRC_BASE}/radius - ${HOSTAP_SRC_BASE}/crypto/ - ${HOSTAP_SRC_BASE}/ap/ - ${HOSTAP_SRC_BASE}/drivers/ - ${HOSTAP_SRC_BASE}/rsn_supp ) zephyr_library_compile_definitions( From e51c3722b1a3afe5940d474c5a4557ed9736a290 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Mon, 24 Jun 2024 20:53:15 +0530 Subject: [PATCH 30/51] [nrf fromtree] modules: hostap: Fix MbedTLS TLS TLS is only for Enterprise, so, move to enterprise macro. Signed-off-by: Chaitanya Tata (cherry picked from commit 515f1fee48a36b88fea04689a620cff5e4a1db53) --- modules/hostap/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/hostap/CMakeLists.txt b/modules/hostap/CMakeLists.txt index 4716cbf087f6..8fe12ad27018 100644 --- a/modules/hostap/CMakeLists.txt +++ b/modules/hostap/CMakeLists.txt @@ -405,7 +405,6 @@ zephyr_library_sources( ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls-bignum.c ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls-ec.c ${HOSTAP_SRC_BASE}/crypto/crypto_mbedtls.c - ${HOSTAP_SRC_BASE}/crypto/tls_mbedtls.c ${HOSTAP_SRC_BASE}/crypto/aes-internal.c ${HOSTAP_SRC_BASE}/crypto/aes-wrap.c ${HOSTAP_SRC_BASE}/crypto/aes-unwrap.c @@ -427,6 +426,10 @@ zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_WPA3 ${HOSTAP_SRC_BASE}/crypto/sha256-kdf.c ) +zephyr_library_sources_ifndef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE + ${HOSTAP_SRC_BASE}/crypto/tls_none.c +) + zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE # common ${HOSTAP_SRC_BASE}/crypto/sha384-tlsprf.c @@ -438,6 +441,7 @@ zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE # MD4 removed from MbedTLS ${HOSTAP_SRC_BASE}/crypto/md4-internal.c ${HOSTAP_SRC_BASE}/crypto/aes-encblock.c + ${HOSTAP_SRC_BASE}/crypto/tls_mbedtls.c ) endif() From 13df3840cc8dc0b7b15207f8fcd2c6a7b3906179 Mon Sep 17 00:00:00 2001 From: Fengming Ye Date: Thu, 20 Jun 2024 19:18:42 +0900 Subject: [PATCH 31/51] [nrf fromtree] west: hostap: update hostap revision Update hostap revision for bug fixes. Signed-off-by: Fengming Ye (cherry picked from commit 24773a1810864027bb73d5bcca4654eebd1af35a) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 1272e32b1bcd..dca83deb0d16 100644 --- a/west.yml +++ b/west.yml @@ -256,7 +256,7 @@ manifest: - name: hostap repo-path: hostap path: modules/lib/hostap - revision: 83574f533fb5c6808af0d9872741d72d48be2f98 + revision: a90df86d7c596a5367ff70c2b50c7f599e6636f3 - name: libmetal revision: 243eed541b9c211a2ce8841c788e62ddce84425e path: modules/hal/libmetal From 584d24c0512a7aac0ebdeeaea3338cc8556f5f58 Mon Sep 17 00:00:00 2001 From: Nikodem Kastelik Date: Mon, 29 Jul 2024 10:20:06 +0200 Subject: [PATCH 32/51] [nrf fromtree] drivers: serial: nrf: add default value for frame timeout cfg This is needed to avoid warnings about uninitialized structure member, which was added in nrfx 3.6. Signed-off-by: Nikodem Kastelik (cherry picked from commit 43128052e78d4e16c6ef75359232f098c5c6fad1) --- drivers/serial/uart_nrfx_uarte.c | 4 ++++ drivers/serial/uart_nrfx_uarte2.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/serial/uart_nrfx_uarte.c b/drivers/serial/uart_nrfx_uarte.c index d2b36017051b..ef1cc20e67a4 100644 --- a/drivers/serial/uart_nrfx_uarte.c +++ b/drivers/serial/uart_nrfx_uarte.c @@ -386,6 +386,10 @@ static int uarte_nrfx_configure(const struct device *dev, struct uarte_nrfx_data *data = dev->data; nrf_uarte_config_t uarte_cfg; +#if NRF_UARTE_HAS_FRAME_TIMEOUT + uarte_cfg.frame_timeout = NRF_UARTE_FRAME_TIMEOUT_DIS; +#endif + #if defined(UARTE_CONFIG_STOP_Msk) switch (cfg->stop_bits) { case UART_CFG_STOP_BITS_1: diff --git a/drivers/serial/uart_nrfx_uarte2.c b/drivers/serial/uart_nrfx_uarte2.c index 99c70f47831d..e00fe7a6d0a3 100644 --- a/drivers/serial/uart_nrfx_uarte2.c +++ b/drivers/serial/uart_nrfx_uarte2.c @@ -643,6 +643,10 @@ static int uarte_nrfx_configure(const struct device *dev, struct uarte_nrfx_data *data = dev->data; nrf_uarte_config_t uarte_cfg; +#if NRF_UARTE_HAS_FRAME_TIMEOUT + uarte_cfg.frame_timeout = NRF_UARTE_FRAME_TIMEOUT_DIS; +#endif + #if defined(UARTE_CONFIG_STOP_Msk) switch (cfg->stop_bits) { case UART_CFG_STOP_BITS_1: From 4f255cda0e8c23ab54bfe69afeb50c2c6c067496 Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Thu, 25 Jul 2024 09:51:29 +0200 Subject: [PATCH 33/51] [nrf fromtree] boards: nordic: add nrf7002dk Add nrf7002dk board. Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit 12559fe6fcacb631e56cef6cab978cd01280c6e9) --- boards/nordic/nrf7002dk/CMakeLists.txt | 11 + boards/nordic/nrf7002dk/Kconfig | 71 +++++ boards/nordic/nrf7002dk/Kconfig.defconfig | 76 +++++ boards/nordic/nrf7002dk/Kconfig.nrf7002dk | 9 + boards/nordic/nrf7002dk/board.cmake | 25 ++ boards/nordic/nrf7002dk/board.yml | 12 + .../nrf7002dk/nrf5340_cpuapp_common.dtsi | 278 ++++++++++++++++++ .../nrf5340_cpuapp_common_partition_conf.dts | 60 ++++ .../nrf5340_cpuapp_common_pinctrl.dtsi | 126 ++++++++ .../nordic/nrf7002dk/nrf5340_cpunet_reset.c | 59 ++++ .../nrf5340_shared_sram_planning_conf.dts | 30 ++ .../nrf7002dk/nrf7002dk_nrf5340_cpuapp.dts | 38 +++ .../nrf7002dk/nrf7002dk_nrf5340_cpuapp.yaml | 20 ++ .../nrf7002dk_nrf5340_cpuapp_defconfig | 23 ++ .../nrf7002dk_nrf5340_cpuapp_nrf7001.dts | 37 +++ .../nrf7002dk_nrf5340_cpuapp_nrf7001.yaml | 20 ++ ...nrf7002dk_nrf5340_cpuapp_nrf7001_defconfig | 24 ++ .../nrf7002dk_nrf5340_cpuapp_nrf7001_ns.dts | 40 +++ .../nrf7002dk_nrf5340_cpuapp_nrf7001_ns.yaml | 19 ++ ...7002dk_nrf5340_cpuapp_nrf7001_ns_defconfig | 27 ++ .../nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.dts | 41 +++ .../nrf7002dk_nrf5340_cpuapp_ns.yaml | 19 ++ .../nrf7002dk_nrf5340_cpuapp_ns_defconfig | 26 ++ .../nrf7002dk_nrf5340_cpuapp_pinctrl.dtsi | 18 ++ .../nrf7002dk/nrf7002dk_nrf5340_cpunet.dts | 176 +++++++++++ .../nrf7002dk/nrf7002dk_nrf5340_cpunet.yaml | 14 + .../nrf7002dk_nrf5340_cpunet_defconfig | 20 ++ .../nrf7002dk_nrf5340_cpunet_pinctrl.dtsi | 55 ++++ boards/nordic/nrf7002dk/nrf70_common.dtsi | 17 ++ boards/nordic/nrf7002dk/nrf70_common_5g.dtsi | 12 + .../nordic/nrf7002dk/nrf70_common_coex.dtsi | 11 + boards/nordic/nrf7002dk/pre_dt_board.cmake | 8 + 32 files changed, 1422 insertions(+) create mode 100644 boards/nordic/nrf7002dk/CMakeLists.txt create mode 100644 boards/nordic/nrf7002dk/Kconfig create mode 100644 boards/nordic/nrf7002dk/Kconfig.defconfig create mode 100644 boards/nordic/nrf7002dk/Kconfig.nrf7002dk create mode 100644 boards/nordic/nrf7002dk/board.cmake create mode 100644 boards/nordic/nrf7002dk/board.yml create mode 100644 boards/nordic/nrf7002dk/nrf5340_cpuapp_common.dtsi create mode 100644 boards/nordic/nrf7002dk/nrf5340_cpuapp_common_partition_conf.dts create mode 100644 boards/nordic/nrf7002dk/nrf5340_cpuapp_common_pinctrl.dtsi create mode 100644 boards/nordic/nrf7002dk/nrf5340_cpunet_reset.c create mode 100644 boards/nordic/nrf7002dk/nrf5340_shared_sram_planning_conf.dts create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp.dts create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp.yaml create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_defconfig create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001.dts create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001.yaml create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_defconfig create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns.dts create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns.yaml create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns_defconfig create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.dts create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.yaml create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns_defconfig create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_pinctrl.dtsi create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet.dts create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet.yaml create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet_defconfig create mode 100644 boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet_pinctrl.dtsi create mode 100644 boards/nordic/nrf7002dk/nrf70_common.dtsi create mode 100644 boards/nordic/nrf7002dk/nrf70_common_5g.dtsi create mode 100644 boards/nordic/nrf7002dk/nrf70_common_coex.dtsi create mode 100644 boards/nordic/nrf7002dk/pre_dt_board.cmake diff --git a/boards/nordic/nrf7002dk/CMakeLists.txt b/boards/nordic/nrf7002dk/CMakeLists.txt new file mode 100644 index 000000000000..db20255712bc --- /dev/null +++ b/boards/nordic/nrf7002dk/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +if((CONFIG_BOARD_NRF7002DK_NRF5340_CPUAPP OR + CONFIG_BOARD_NRF7002DK_NRF5340_CPUAPP_NS OR + CONFIG_BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001 OR + CONFIG_BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001_NS) AND + CONFIG_BOARD_ENABLE_CPUNET) + zephyr_library() + zephyr_library_sources(nrf5340_cpunet_reset.c) +endif() diff --git a/boards/nordic/nrf7002dk/Kconfig b/boards/nordic/nrf7002dk/Kconfig new file mode 100644 index 000000000000..4bd84612e7a9 --- /dev/null +++ b/boards/nordic/nrf7002dk/Kconfig @@ -0,0 +1,71 @@ +# nRF5340 DK board configuration + +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config IPM_NRFX + default IPM + +config MBOX_NRFX_IPC + default MBOX + +if BOARD_NRF7002DK_NRF5340_CPUAPP || \ + BOARD_NRF7002DK_NRF5340_CPUAPP_NS || \ + BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001 || \ + BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001_NS + +config BT_HCI_IPC + default y if BT + +config HEAP_MEM_POOL_ADD_SIZE_BOARD + int + default 4096 if BT_HCI_IPC + +config BOARD_ENABLE_CPUNET + bool "nRF53 Network MCU" + select SOC_NRF_GPIO_FORWARDER_FOR_NRF5340 if \ + $(dt_compat_enabled,$(DT_COMPAT_NORDIC_NRF_GPIO_FORWARDER)) + help + This option enables releasing the Network 'force off' signal, which + as a consequence will power up the Network MCU during system boot. + Additionally, the option allocates GPIO pins that will be used by UARTE + of the Network MCU. + Note: GPIO pin allocation can only be configured by the secure Application + MCU firmware, so when this option is used with the non-secure version of + the board, the application needs to take into consideration, that the + secure firmware image must already have configured GPIO allocation for the + Network MCU. + default y if (BT || NRF_802154_SER_HOST) + +config DOMAIN_CPUNET_BOARD + string + default "nrf7002dk/nrf5340/cpunet" + depends on BOARD_ENABLE_CPUNET + help + The board which will be used for CPUNET domain when creating a multi + image application where one or more images should be located on + another board. For example hci_ipc on the nRF5340_cpunet for + Bluetooth applications. + +endif + +if BOARD_NRF7002DK_NRF5340_CPUNET + +config BT_CTLR + default y if BT + +config BT_ECC + default y if BT + +config DOMAIN_CPUAPP_BOARD + string + default "nrf7002dk/nrf5340/cpuapp" if BOARD_NRF7002DK_NRF5340_CPUAPP + default "nrf7002dk/nrf5340/cpuapp/ns" if BOARD_NRF7002DK_NRF5340_CPUAPP_NS + default "nrf7002dk/nrf5340/cpuapp/nrf7001" if BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001 + default "nrf7002dk/nrf5340/cpuapp/nrf7001/ns" if BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001_NS + help + The board which will be used for CPUAPP domain when creating a multi + image application where one or more images should be located on + another board. + +endif diff --git a/boards/nordic/nrf7002dk/Kconfig.defconfig b/boards/nordic/nrf7002dk/Kconfig.defconfig new file mode 100644 index 000000000000..a6357ae50b2a --- /dev/null +++ b/boards/nordic/nrf7002dk/Kconfig.defconfig @@ -0,0 +1,76 @@ +# nRF5340 DK nRF5340 board configuration + +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_NRF7002DK_NRF5340_CPUAPP || \ + BOARD_NRF7002DK_NRF5340_CPUAPP_NS || \ + BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001 || \ + BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001_NS + +# By default, if we build for a Non-Secure version of the board, +# force building with TF-M as the Secure Execution Environment. +config BUILD_WITH_TFM + default y if BOARD_NRF7002DK_NRF5340_CPUAPP_NS || \ + BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001_NS + +if BUILD_WITH_TFM + +# By default, if we build with TF-M, instruct build system to +# flash the combined TF-M (Secure) & Zephyr (Non Secure) image +config TFM_FLASH_MERGED_BINARY + bool + default y + +endif # BUILD_WITH_TFM + +# Code Partition: +# +# For the secure version of the board the firmware is linked at the beginning +# of the flash, or into the code-partition defined in DT if it is intended to +# be loaded by MCUboot. If the secure firmware is to be combined with a non- +# secure image (TRUSTED_EXECUTION_SECURE=y), the secure FW image shall always +# be restricted to the size of its code partition. +# +# For the non-secure version of the board, the firmware +# must be linked into the code-partition (non-secure) defined in DT, regardless. +# Apply this configuration below by setting the Kconfig symbols used by +# the linker according to the information extracted from DT partitions. + +# SRAM Partition: +# +# If the secure firmware is to be combined with a non-secure image +# (TRUSTED_EXECUTION_SECURE=y), the secure FW image SRAM shall always +# be restricted to the secure image SRAM partition (sram-secure-partition). +# Otherwise (if TRUSTED_EXECUTION_SECURE is not set) the whole zephyr,sram +# may be used by the image. +# +# For the non-secure version of the board, the firmware image SRAM is +# always restricted to the allocated non-secure SRAM partition. +# +# Workaround for not being able to have commas in macro arguments +DT_CHOSEN_Z_CODE_PARTITION := zephyr,code-partition +DT_CHOSEN_Z_SRAM_PARTITION := zephyr,sram-secure-partition + +if (BOARD_NRF7002DK_NRF5340_CPUAPP || BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001) && \ + TRUSTED_EXECUTION_SECURE + +config FLASH_LOAD_SIZE + default $(dt_chosen_reg_size_hex,$(DT_CHOSEN_Z_CODE_PARTITION)) + +config SRAM_SIZE + default $(dt_chosen_reg_size_int,$(DT_CHOSEN_Z_SRAM_PARTITION),0,K) + +endif + +if BOARD_NRF7002DK_NRF5340_CPUAPP_NS || BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001_NS + +config FLASH_LOAD_OFFSET + default $(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_CODE_PARTITION)) + +config FLASH_LOAD_SIZE + default $(dt_chosen_reg_size_hex,$(DT_CHOSEN_Z_CODE_PARTITION)) + +endif + +endif diff --git a/boards/nordic/nrf7002dk/Kconfig.nrf7002dk b/boards/nordic/nrf7002dk/Kconfig.nrf7002dk new file mode 100644 index 000000000000..91f52ee6f08c --- /dev/null +++ b/boards/nordic/nrf7002dk/Kconfig.nrf7002dk @@ -0,0 +1,9 @@ +# Copyright (c) 2024 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_NRF7002DK + select SOC_NRF5340_CPUNET_QKAA if BOARD_NRF7002DK_NRF5340_CPUNET + select SOC_NRF5340_CPUAPP_QKAA if BOARD_NRF7002DK_NRF5340_CPUAPP || \ + BOARD_NRF7002DK_NRF5340_CPUAPP_NS || \ + BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001 || \ + BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001_NS diff --git a/boards/nordic/nrf7002dk/board.cmake b/boards/nordic/nrf7002dk/board.cmake new file mode 100644 index 000000000000..bea0dc92ea9c --- /dev/null +++ b/boards/nordic/nrf7002dk/board.cmake @@ -0,0 +1,25 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +if(CONFIG_BOARD_NRF7002DK_NRF5340_CPUAPP_NS OR + CONFIG_BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001_NS) + set(TFM_PUBLIC_KEY_FORMAT "full") +endif() + +if(CONFIG_BOARD_NRF7002DK_NRF5340_CPUAPP OR + CONFIG_BOARD_NRF7002DK_NRF5340_CPUAPP_NS OR + CONFIG_BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001 OR + CONFIG_BOARD_NRF7002DK_NRF5340_CPUAPP_NRF7001_NS) + board_runner_args(jlink "--device=nrf5340_xxaa_app" "--speed=4000") +endif() + +if(CONFIG_TFM_FLASH_MERGED_BINARY) + set_property(TARGET runners_yaml_props_target PROPERTY hex_file "${CMAKE_BINARY_DIR}/zephyr/tfm_merged.hex") +endif() + +if(CONFIG_BOARD_NRF7002DK_NRF5340_CPUNET) + board_runner_args(jlink "--device=nrf5340_xxaa_net" "--speed=4000") +endif() + +include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake) +include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) diff --git a/boards/nordic/nrf7002dk/board.yml b/boards/nordic/nrf7002dk/board.yml new file mode 100644 index 000000000000..ca94d73429e9 --- /dev/null +++ b/boards/nordic/nrf7002dk/board.yml @@ -0,0 +1,12 @@ +board: + name: nrf7002dk + vendor: nordic + socs: + - name: nrf5340 + variants: + - name: ns + cpucluster: cpuapp + - name: nrf7001 + cpucluster: cpuapp + variants: + - name: ns diff --git a/boards/nordic/nrf7002dk/nrf5340_cpuapp_common.dtsi b/boards/nordic/nrf7002dk/nrf5340_cpuapp_common.dtsi new file mode 100644 index 000000000000..c571d4b1f3b5 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf5340_cpuapp_common.dtsi @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "nrf5340_cpuapp_common_pinctrl.dtsi" +#include + +/ { + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + zephyr,uart-mcumgr = &uart0; + zephyr,bt-mon-uart = &uart0; + zephyr,bt-c2h-uart = &uart0; + zephyr,bt-hci = &bt_hci_ipc0; + nordic,802154-spinel-ipc = &ipc0; + zephyr,ieee802154 = &ieee802154; + }; + + leds { + compatible = "gpio-leds"; + led0: led_0 { + gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>; + label = "Green LED 0"; + }; + led1: led_1 { + gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; + label = "Green LED 1"; + }; + }; + + pwmleds { + compatible = "pwm-leds"; + pwm_led0: pwm_led_0 { + pwms = <&pwm0 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>; + }; + }; + + buttons { + compatible = "gpio-keys"; + button0: button_0 { + gpios = <&gpio1 8 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Push button 1"; + zephyr,code = ; + }; + button1: button_1 { + gpios = <&gpio1 9 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Push button 2"; + zephyr,code = ; + }; + }; + + arduino_header: connector { + compatible = "arduino-header-r3"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map = <0 0 &gpio0 4 0>, /* A0 */ + <1 0 &gpio0 5 0>, /* A1 */ + <2 0 &gpio0 6 0>, /* A2 */ + <3 0 &gpio0 7 0>, /* A3 */ + <4 0 &gpio0 25 0>, /* A4 */ + <5 0 &gpio0 26 0>, /* A5 */ + <6 0 &gpio1 0 0>, /* D0 */ + <7 0 &gpio1 1 0>, /* D1 */ + <8 0 &gpio1 4 0>, /* D2 */ + <9 0 &gpio1 5 0>, /* D3 */ + <10 0 &gpio1 6 0>, /* D4 */ + <11 0 &gpio1 7 0>, /* D5 */ + <12 0 &gpio1 8 0>, /* D6 */ + <13 0 &gpio1 9 0>, /* D7 */ + <14 0 &gpio1 10 0>, /* D8 */ + <15 0 &gpio1 11 0>, /* D9 */ + <16 0 &gpio1 12 0>, /* D10 */ + <17 0 &gpio1 13 0>, /* D11 */ + <18 0 &gpio1 14 0>, /* D12 */ + <19 0 &gpio1 15 0>, /* D13 */ + <20 0 &gpio1 2 0>, /* D14 */ + <21 0 &gpio1 3 0>; /* D15 */ + }; + + arduino_adc: analog-connector { + compatible = "arduino,uno-adc"; + #io-channel-cells = <1>; + io-channel-map = <0 &adc 0>, /* A0 = P0.4 = AIN0 */ + <1 &adc 1>, /* A1 = P0.5 = AIN1 */ + <2 &adc 2>, /* A2 = P0.6 = AIN2 */ + <3 &adc 3>, /* A3 = P0.7 = AIN3 */ + <4 &adc 4>, /* A4 = P0.25 = AIN4 */ + <5 &adc 5>; /* A5 = P0.26 = AIN5 */ + }; + + gpio_fwd: nrf-gpio-forwarder { + compatible = "nordic,nrf-gpio-forwarder"; + status = "okay"; + uart { + gpios = <&gpio1 1 0>, <&gpio1 0 0>, <&gpio1 5 0>, <&gpio1 4 0>; + }; + }; + + /* These aliases are provided for compatibility with samples */ + aliases { + led0 = &led0; + led1 = &led1; + pwm-led0 = &pwm_led0; + sw0 = &button0; + sw1 = &button1; + bootloader-led0 = &led0; + mcuboot-button0 = &button0; + mcuboot-led0 = &led0; + watchdog0 = &wdt0; + }; +}; + +&vregmain { + regulator-initial-mode = ; +}; + +&vregradio { + regulator-initial-mode = ; +}; + +&vregh { + status = "okay"; +}; + +&adc { + status = "okay"; +}; + +&gpiote { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +arduino_i2c: &i2c1 { + compatible = "nordic,nrf-twim"; + status = "okay"; + pinctrl-0 = <&i2c1_default>; + pinctrl-1 = <&i2c1_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&uart0 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart0_default>; + pinctrl-1 = <&uart0_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&pwm0 { + status = "okay"; + pinctrl-0 = <&pwm0_default>; + pinctrl-1 = <&pwm0_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&spi4 { + compatible = "nordic,nrf-spim"; + status = "okay"; + pinctrl-0 = <&spi4_default>; + pinctrl-1 = <&spi4_sleep>; + pinctrl-names = "default", "sleep"; + cs-gpios = <&gpio0 11 GPIO_ACTIVE_LOW>; + mx25r64: mx25r6435f@0 { + compatible = "jedec,spi-nor"; + status = "disabled"; + reg = <0>; + spi-max-frequency = <33000000>; + jedec-id = [c2 28 17]; + sfdp-bfp = [ + e5 20 f1 ff ff ff ff 03 44 eb 08 6b 08 3b 04 bb + ee ff ff ff ff ff 00 ff ff ff 00 ff 0c 20 0f 52 + 10 d8 00 ff 23 72 f5 00 82 ed 04 cc 44 83 68 44 + 30 b0 30 b0 f7 c4 d5 5c 00 be 29 ff f0 d0 ff ff + ]; + size = <67108864>; + has-dpd; + t-enter-dpd = <10000>; + t-exit-dpd = <5000>; + }; +}; + +&qspi { + status = "okay"; + pinctrl-0 = <&qspi_default>; + pinctrl-1 = <&qspi_sleep>; + pinctrl-names = "default", "sleep"; +}; + +arduino_serial: &uart1 { + compatible = "nordic,nrf-uarte"; + current-speed = <115200>; + pinctrl-0 = <&uart1_default>; + pinctrl-1 = <&uart1_sleep>; + pinctrl-names = "default", "sleep"; +}; + +arduino_spi: &spi3 { + compatible = "nordic,nrf-spim"; + cs-gpios = <&arduino_header 16 GPIO_ACTIVE_LOW>; /* D10 */ + pinctrl-0 = <&spi3_default>; + pinctrl-1 = <&spi3_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&flash0 { + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x00010000>; + }; + slot0_partition: partition@10000 { + label = "image-0"; + }; + slot0_ns_partition: partition@50000 { + label = "image-0-nonsecure"; + }; + slot1_partition: partition@80000 { + label = "image-1"; + }; + slot1_ns_partition: partition@c0000 { + label = "image-1-nonsecure"; + }; + /* 0xf0000 to 0xf7fff reserved for TF-M partitions */ + storage_partition: partition@f8000 { + label = "storage"; + reg = <0x000f8000 0x00008000>; + }; + }; +}; + +&ieee802154 { + status = "okay"; +}; + +zephyr_udc0: &usbd { + compatible = "nordic,nrf-usbd"; + status = "okay"; +}; + +/ { + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + sram0_image: image@20000000 { + /* Zephyr image(s) memory */ + }; + + sram0_s: image_s@20000000 { + /* Secure image memory */ + }; + + sram0_ns: image_ns@20040000 { + /* Non-Secure image memory */ + }; + }; +}; + +/* Include partition configuration file */ +#include "nrf5340_cpuapp_common_partition_conf.dts" diff --git a/boards/nordic/nrf7002dk/nrf5340_cpuapp_common_partition_conf.dts b/boards/nordic/nrf7002dk/nrf5340_cpuapp_common_partition_conf.dts new file mode 100644 index 000000000000..b4fa353af3f1 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf5340_cpuapp_common_partition_conf.dts @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Default Flash planning for nrf7002dk_nrf5340 CPUAPP (Application MCU). + * + * Zephyr build for nRF5340 with ARM TrustZone-M support, + * implies building Secure and Non-Secure Zephyr images. + * + * Secure image will be placed, by default, in flash0 + * (or in slot0, if MCUboot is present). + * Secure image will use sram0 for system memory. + * + * Non-Secure image will be placed in slot0_ns, and use + * sram0_ns for system memory. + * + * Note that the Secure image only requires knowledge of + * the beginning of the Non-Secure image (not its size). + */ + +&slot0_partition { + reg = <0x00010000 0x40000>; +}; + +&slot0_ns_partition { + reg = <0x00050000 0x30000>; +}; + +&slot1_partition { + reg = <0x00080000 0x40000>; +}; + +&slot1_ns_partition { + reg = <0x000c0000 0x30000>; +}; + +/* Default SRAM planning when building for nRF5340 with + * ARM TrustZone-M support + * - Lowest 256 kB SRAM allocated to Secure image (sram0_s) + * - Middle 192 kB allocated to Non-Secure image (sram0_ns) + * - Upper 64 kB SRAM allocated as Shared memory (sram0_shared) + * (see nrf5340_shared_sram_planning_conf.dts) + */ +&sram0_image { + reg = <0x20000000 DT_SIZE_K(448)>; +}; + +&sram0_s { + reg = <0x20000000 DT_SIZE_K(256)>; +}; + +&sram0_ns { + reg = <0x20040000 DT_SIZE_K(192)>; +}; + +/* Include shared RAM configuration file */ +#include "nrf5340_shared_sram_planning_conf.dts" diff --git a/boards/nordic/nrf7002dk/nrf5340_cpuapp_common_pinctrl.dtsi b/boards/nordic/nrf7002dk/nrf5340_cpuapp_common_pinctrl.dtsi new file mode 100644 index 000000000000..f04075f374d6 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf5340_cpuapp_common_pinctrl.dtsi @@ -0,0 +1,126 @@ +&pinctrl { + i2c1_default: i2c1_default { + group1 { + psels = , + ; + }; + }; + + i2c1_sleep: i2c1_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + uart0_default: uart0_default { + group1 { + psels = , + ; + }; + group2 { + psels = , + ; + bias-pull-up; + }; + }; + + uart0_sleep: uart0_sleep { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; + + pwm0_default: pwm0_default { + group1 { + psels = ; + }; + }; + + pwm0_sleep: pwm0_sleep { + group1 { + psels = ; + low-power-enable; + }; + }; + + qspi_default: qspi_default { + group1 { + psels = , + , + , + , + , + ; + }; + }; + + qspi_sleep: qspi_sleep { + group1 { + psels = , + , + , + , + , + ; + low-power-enable; + }; + }; + + uart1_default: uart1_default { + group1 { + psels = ; + }; + group2 { + psels = ; + bias-pull-up; + }; + }; + + uart1_sleep: uart1_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + spi3_default: spi3_default { + group1 { + psels = , + , + ; + }; + }; + + spi3_sleep: spi3_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; + + spi4_default: spi4_default { + group1 { + psels = , + , + ; + }; + }; + + spi4_sleep: spi4_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; diff --git a/boards/nordic/nrf7002dk/nrf5340_cpunet_reset.c b/boards/nordic/nrf7002dk/nrf5340_cpunet_reset.c new file mode 100644 index 000000000000..b86b571db4b7 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf5340_cpunet_reset.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include + +LOG_MODULE_REGISTER(nrf7002dk_nrf5340_cpuapp, CONFIG_LOG_DEFAULT_LEVEL); + +#if defined(CONFIG_BT_CTLR_DEBUG_PINS_CPUAPP) +#include <../subsys/bluetooth/controller/ll_sw/nordic/hal/nrf5/debug.h> +#endif + +static void remoteproc_mgr_config(void) +{ +#if defined(CONFIG_BT_CTLR_DEBUG_PINS_CPUAPP) && \ + (!defined(CONFIG_TRUSTED_EXECUTION_NONSECURE) || defined(CONFIG_BUILD_WITH_TFM)) + /* Route Bluetooth Controller Debug Pins */ + DEBUG_SETUP(); +#endif /* !defined(CONFIG_TRUSTED_EXECUTION_NONSECURE) || defined(CONFIG_BUILD_WITH_TFM) */ + +#if !defined(CONFIG_TRUSTED_EXECUTION_NONSECURE) + /* Retain nRF5340 Network MCU in Secure domain (bus + * accesses by Network MCU will have Secure attribute set). + */ + NRF_SPU->EXTDOMAIN[0].PERM = BIT(4); +#endif /* !defined(CONFIG_TRUSTED_EXECUTION_NONSECURE) */ +} + +static int remoteproc_mgr_boot(void) +{ + + /* Secure domain may configure permissions for the Network MCU. */ + remoteproc_mgr_config(); + +#if !defined(CONFIG_TRUSTED_EXECUTION_SECURE) + /* + * Building Zephyr with CONFIG_TRUSTED_EXECUTION_SECURE=y implies + * building also a Non-Secure image. The Non-Secure image will, in + * this case do the remainder of actions to properly configure and + * boot the Network MCU. + */ + + /* Release the Network MCU, 'Release force off signal' */ + nrf_reset_network_force_off(NRF_RESET, false); + + LOG_DBG("Network MCU released."); +#endif /* !CONFIG_TRUSTED_EXECUTION_SECURE */ + + return 0; +} + +SYS_INIT(remoteproc_mgr_boot, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); diff --git a/boards/nordic/nrf7002dk/nrf5340_shared_sram_planning_conf.dts b/boards/nordic/nrf7002dk/nrf5340_shared_sram_planning_conf.dts new file mode 100644 index 000000000000..d009959d3f6b --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf5340_shared_sram_planning_conf.dts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Default shared SRAM planning when building for nRF5340. + * This file is included by both nRF5340 CPUAPP (Application MCU) + * and nRF5340 CPUNET (Network MCU). + * - 64 kB SRAM allocated as Shared memory (sram0_shared) + * - Region defined after the image SRAM of Application MCU + */ + +/ { + chosen { + /* shared memory reserved for the inter-processor communication */ + zephyr,ipc_shm = &sram0_shared; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + sram0_shared: memory@20070000 { + /* SRAM allocated to shared memory */ + reg = <0x20070000 0x10000>; + }; + }; +}; diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp.dts b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp.dts new file mode 100644 index 000000000000..fbe74a822240 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp.dts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; +#include +#include "nrf5340_cpuapp_common.dtsi" +#include "nrf7002dk_nrf5340_cpuapp_pinctrl.dtsi" + +/ { + model = "Nordic NRF7002 DK NRF5340 Application"; + compatible = "nordic,nrf7002-dk-nrf5340-cpuapp"; + + chosen { + zephyr,sram = &sram0_image; + zephyr,flash = &flash0; + zephyr,code-partition = &slot0_partition; + zephyr,sram-secure-partition = &sram0_s; + zephyr,sram-non-secure-partition = &sram0_ns; + zephyr,wifi = &wlan0; + }; +}; + +&qspi { + nrf70: nrf7002@1 { + compatible = "nordic,nrf7002-qspi"; + status = "okay"; + reg = <1>; + qspi-frequency = <24000000>; + qspi-quad-mode; + + #include "nrf70_common.dtsi" + #include "nrf70_common_coex.dtsi" + #include "nrf70_common_5g.dtsi" + }; +}; diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp.yaml b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp.yaml new file mode 100644 index 000000000000..9a6c65314424 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp.yaml @@ -0,0 +1,20 @@ +identifier: nrf7002dk/nrf5340/cpuapp +name: NRF7002-DK-NRF5340-application-MCU +type: mcu +arch: arm +toolchain: + - gnuarmemb + - xtools + - zephyr +ram: 448 +flash: 1024 +supported: + - gpio + - i2c + - i2s + - pwm + - watchdog + - usbd + - usb_device + - netif:openthread +vendor: nordic diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_defconfig b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_defconfig new file mode 100644 index 000000000000..24eee2849027 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_defconfig @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: Apache-2.0 + +# Enable MPU +CONFIG_ARM_MPU=y + +# Enable hardware stack protection +CONFIG_HW_STACK_PROTECTION=y + +# Enable TrustZone-M +CONFIG_ARM_TRUSTZONE_M=y + +# Enable GPIO +CONFIG_GPIO=y + +# Enable PINCTRL +CONFIG_PINCTRL=y + +# Enable uart driver +CONFIG_SERIAL=y + +# enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001.dts b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001.dts new file mode 100644 index 000000000000..1d78dddbe68a --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001.dts @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; +#include +#include "nrf5340_cpuapp_common.dtsi" +#include "nrf7002dk_nrf5340_cpuapp_pinctrl.dtsi" + +/ { + model = "Nordic NRF7002 DK NRF5340 Application"; + compatible = "nordic,nrf7002-dk-nrf5340-cpuapp"; + + chosen { + zephyr,sram = &sram0_image; + zephyr,flash = &flash0; + zephyr,code-partition = &slot0_partition; + zephyr,sram-secure-partition = &sram0_s; + zephyr,sram-non-secure-partition = &sram0_ns; + zephyr,wifi = &wlan0; + }; +}; + +&qspi { + nrf70: nrf7001@1 { + compatible = "nordic,nrf7001-qspi"; + status = "okay"; + reg = <1>; + qspi-frequency = <24000000>; + qspi-quad-mode; + + #include "nrf70_common.dtsi" + #include "nrf70_common_coex.dtsi" + }; +}; diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001.yaml b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001.yaml new file mode 100644 index 000000000000..e74ba0628aba --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001.yaml @@ -0,0 +1,20 @@ +identifier: nrf7002dk/nrf5340/cpuapp/nrf7001 +name: NRF7002-DK-NRF7001-NRF5340-application-MCU +type: mcu +arch: arm +toolchain: + - gnuarmemb + - xtools + - zephyr +ram: 448 +flash: 1024 +supported: + - gpio + - i2c + - i2s + - pwm + - watchdog + - usbd + - usb_device + - netif:openthread +vendor: nordic diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_defconfig b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_defconfig new file mode 100644 index 000000000000..870fdf32425b --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_defconfig @@ -0,0 +1,24 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +# Enable MPU +CONFIG_ARM_MPU=y + +# Enable hardware stack protection +CONFIG_HW_STACK_PROTECTION=y + +# Enable TrustZone-M +CONFIG_ARM_TRUSTZONE_M=y + +# enable GPIO +CONFIG_GPIO=y + +# enable PINCTRL +CONFIG_PINCTRL=y + +# Enable uart driver +CONFIG_SERIAL=y + +# enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns.dts b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns.dts new file mode 100644 index 000000000000..cbbd46dff8be --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns.dts @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; +#include +#include "nrf5340_cpuapp_common.dtsi" + +/ { + model = "Nordic NRF5340 DK NRF5340 Application"; + compatible = "nordic,nrf5340-dk-nrf5340-cpuapp"; + + chosen { + zephyr,sram = &sram0_ns; + zephyr,flash = &flash0; + zephyr,code-partition = &slot0_ns_partition; + zephyr,entropy = &psa_rng; + zephyr,wifi = &wlan0; + }; + + psa_rng: psa-rng { + compatible = "zephyr,psa-crypto-rng"; + status = "okay"; + }; +}; + +&qspi { + nrf70: nrf7001@1 { + compatible = "nordic,nrf7001-qspi"; + status = "okay"; + reg = <1>; + qspi-frequency = <24000000>; + qspi-quad-mode; + + #include "nrf70_common.dtsi" + #include "nrf70_common_coex.dtsi" + }; +}; diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns.yaml b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns.yaml new file mode 100644 index 000000000000..165759691260 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns.yaml @@ -0,0 +1,19 @@ +identifier: nrf7002dk/nrf5340/cpuapp/nrf7001/ns +name: NRF7002-DK-NRF7001-NRF5340-application-MCU-Non-Secure +type: mcu +arch: arm +toolchain: + - gnuarmemb + - xtools + - zephyr +ram: 192 +flash: 192 +supported: + - gpio + - i2c + - pwm + - watchdog + - usbd + - usb_device + - netif:openthread +vendor: nordic diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns_defconfig b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns_defconfig new file mode 100644 index 000000000000..c536aae767dc --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns_defconfig @@ -0,0 +1,27 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +# Enable MPU +CONFIG_ARM_MPU=y + +# Enable hardware stack protection +CONFIG_HW_STACK_PROTECTION=y + +# Enable TrustZone-M +CONFIG_ARM_TRUSTZONE_M=y + +# This Board implies building Non-Secure firmware +CONFIG_TRUSTED_EXECUTION_NONSECURE=y + +# enable GPIO +CONFIG_GPIO=y + +# enable PINCTRL +CONFIG_PINCTRL=y + +# Enable uart driver +CONFIG_SERIAL=y + +# enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.dts b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.dts new file mode 100644 index 000000000000..26ef52132f0f --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.dts @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; +#include +#include "nrf5340_cpuapp_common.dtsi" + +/ { + model = "Nordic NRF5340 DK NRF5340 Application"; + compatible = "nordic,nrf5340-dk-nrf5340-cpuapp"; + + chosen { + zephyr,sram = &sram0_ns; + zephyr,flash = &flash0; + zephyr,code-partition = &slot0_ns_partition; + zephyr,entropy = &psa_rng; + zephyr,wifi = &wlan0; + }; + + psa_rng: psa-rng { + compatible = "zephyr,psa-crypto-rng"; + status = "okay"; + }; +}; + +&qspi { + nrf70: nrf7002@1 { + compatible = "nordic,nrf7002-qspi"; + status = "okay"; + reg = <1>; + qspi-frequency = <24000000>; + qspi-quad-mode; + + #include "nrf70_common.dtsi" + #include "nrf70_common_coex.dtsi" + #include "nrf70_common_5g.dtsi" + }; +}; diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.yaml b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.yaml new file mode 100644 index 000000000000..ea43785b4559 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.yaml @@ -0,0 +1,19 @@ +identifier: nrf7002dk/nrf5340/cpuapp/ns +name: NRF7002-DK-NRF5340-application-MCU-Non-Secure +type: mcu +arch: arm +toolchain: + - gnuarmemb + - xtools + - zephyr +ram: 192 +flash: 192 +supported: + - gpio + - i2c + - pwm + - watchdog + - usbd + - usb_device + - netif:openthread +vendor: nordic diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns_defconfig b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns_defconfig new file mode 100644 index 000000000000..c5b2eaadc168 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns_defconfig @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: Apache-2.0 + +# Enable MPU +CONFIG_ARM_MPU=y + +# Enable hardware stack protection +CONFIG_HW_STACK_PROTECTION=y + +# Enable TrustZone-M +CONFIG_ARM_TRUSTZONE_M=y + +# This Board implies building Non-Secure firmware +CONFIG_TRUSTED_EXECUTION_NONSECURE=y + +# enable GPIO +CONFIG_GPIO=y + +# enable PINCTRL +CONFIG_PINCTRL=y + +# Enable uart driver +CONFIG_SERIAL=y + +# enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_pinctrl.dtsi b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_pinctrl.dtsi new file mode 100644 index 000000000000..a7cde7241e5f --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_pinctrl.dtsi @@ -0,0 +1,18 @@ +&pinctrl { + spi2_default: spi2_default { + group1 { + psels = , + , + ; + }; + }; + + spi2_sleep: spi2_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet.dts b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet.dts new file mode 100644 index 000000000000..bf5a066f2fd7 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet.dts @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; +#include +#include "nrf7002dk_nrf5340_cpunet_pinctrl.dtsi" +#include + +/ { + model = "Nordic NRF7002 DK NRF5340 Network"; + compatible = "nordic,nrf7002-dk-nrf5340-cpunet"; + + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + zephyr,uart-mcumgr = &uart0; + zephyr,bt-mon-uart = &uart0; + zephyr,bt-c2h-uart = &uart0; + zephyr,bt-hci-ipc = &ipc0; + nordic,802154-spinel-ipc = &ipc0; + zephyr,sram = &sram1; + zephyr,flash = &flash1; + zephyr,code-partition = &slot0_partition; + zephyr,ieee802154 = &ieee802154; + }; + + leds { + compatible = "gpio-leds"; + led0: led_0 { + gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>; + label = "Green LED 0"; + }; + led1: led_1 { + gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; + label = "Green LED 1"; + }; + }; + + buttons { + compatible = "gpio-keys"; + button0: button_0 { + gpios = <&gpio1 8 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Push button 1"; + zephyr,code = ; + }; + button1: button_1 { + gpios = <&gpio1 9 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Push button 2"; + zephyr,code = ; + }; + }; + + arduino_header: connector { + compatible = "arduino-header-r3"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map = <0 0 &gpio0 4 0>, /* A0 */ + <1 0 &gpio0 5 0>, /* A1 */ + <2 0 &gpio0 6 0>, /* A2 */ + <3 0 &gpio0 7 0>, /* A3 */ + <4 0 &gpio0 25 0>, /* A4 */ + <5 0 &gpio0 26 0>, /* A5 */ + <6 0 &gpio1 0 0>, /* D0 */ + <7 0 &gpio1 1 0>, /* D1 */ + <8 0 &gpio1 4 0>, /* D2 */ + <9 0 &gpio1 5 0>, /* D3 */ + <10 0 &gpio1 6 0>, /* D4 */ + <11 0 &gpio1 7 0>, /* D5 */ + <12 0 &gpio1 8 0>, /* D6 */ + <13 0 &gpio1 9 0>, /* D7 */ + <14 0 &gpio1 10 0>, /* D8 */ + <15 0 &gpio1 11 0>, /* D9 */ + <16 0 &gpio1 12 0>, /* D10 */ + <17 0 &gpio1 13 0>, /* D11 */ + <18 0 &gpio1 14 0>, /* D12 */ + <19 0 &gpio1 15 0>, /* D13 */ + <20 0 &gpio1 2 0>, /* D14 */ + <21 0 &gpio1 3 0>; /* D15 */ + }; + + nrf70: coex { + status = "okay"; + compatible = "nordic,nrf70-coex"; + + #include "nrf70_common_coex.dtsi" + }; + + /* These aliases are provided for compatibility with samples */ + aliases { + led0 = &led0; + led1 = &led1; + sw0 = &button0; + sw1 = &button1; + bootloader-led0 = &led0; + mcuboot-button0 = &button0; + mcuboot-led0 = &led0; + watchdog0 = &wdt0; + }; +}; + +&gpiote { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&uart0 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart0_default>; + pinctrl-1 = <&uart0_sleep>; + pinctrl-names = "default", "sleep"; +}; + +arduino_serial: &uart0{}; + +arduino_i2c: &i2c0 { + compatible = "nordic,nrf-twim"; + /* Cannot be used together with uart0. */ + /* status = "okay"; */ + pinctrl-0 = <&i2c0_default>; + pinctrl-1 = <&i2c0_sleep>; + pinctrl-names = "default", "sleep"; +}; + +arduino_spi: &spi0 { + compatible = "nordic,nrf-spim"; + /* Cannot be used together with uart0. */ + /* status = "okay"; */ + cs-gpios = <&arduino_header 16 GPIO_ACTIVE_LOW>; /* D10 */ + pinctrl-0 = <&spi0_default>; + pinctrl-1 = <&spi0_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&flash1 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0xc000>; + }; + slot0_partition: partition@c000 { + label = "image-0"; + reg = <0x0000C000 0x17000>; + }; + slot1_partition: partition@23000 { + label = "image-1"; + reg = <0x00023000 0x17000>; + }; + storage_partition: partition@3a000 { + label = "storage"; + reg = <0x0003a000 0x6000>; + }; + }; +}; + +&ieee802154 { + status = "okay"; +}; + +/* Include shared RAM configuration file */ +#include "nrf5340_shared_sram_planning_conf.dts" diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet.yaml b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet.yaml new file mode 100644 index 000000000000..f04ef6cee84c --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet.yaml @@ -0,0 +1,14 @@ +identifier: nrf7002dk/nrf5340/cpunet +name: NRF7002-DK-NRF5340-network-MCU +type: mcu +arch: arm +toolchain: + - gnuarmemb + - xtools + - zephyr +ram: 64 +flash: 256 +supported: + - gpio + - watchdog +vendor: nordic diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet_defconfig b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet_defconfig new file mode 100644 index 000000000000..ec4a0acd7a86 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet_defconfig @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: Apache-2.0 + +# Enable MPU +CONFIG_ARM_MPU=y + +# Enable hardware stack protection +CONFIG_HW_STACK_PROTECTION=y + +# enable GPIO +CONFIG_GPIO=y + +# enable PINCTRL +CONFIG_PINCTRL=y + +# Enable uart driver +CONFIG_SERIAL=y + +# enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet_pinctrl.dtsi b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet_pinctrl.dtsi new file mode 100644 index 000000000000..2e19d95f7f9b --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpunet_pinctrl.dtsi @@ -0,0 +1,55 @@ +&pinctrl { + uart0_default: uart0_default { + group1 { + psels = , + ; + }; + group2 { + psels = , + ; + bias-pull-up; + }; + }; + + uart0_sleep: uart0_sleep { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; + + i2c0_default: i2c0_default { + group1 { + psels = , + ; + }; + }; + + i2c0_sleep: i2c0_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + spi0_default: spi0_default { + group1 { + psels = , + , + ; + }; + }; + + spi0_sleep: spi0_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; diff --git a/boards/nordic/nrf7002dk/nrf70_common.dtsi b/boards/nordic/nrf7002dk/nrf70_common.dtsi new file mode 100644 index 000000000000..f40f8ad9bb74 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf70_common.dtsi @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +iovdd-ctrl-gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>; +bucken-gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>; +host-irq-gpios = <&gpio0 23 GPIO_ACTIVE_HIGH>; + +wifi-max-tx-pwr-2g-dsss = <21>; +wifi-max-tx-pwr-2g-mcs0 = <16>; +wifi-max-tx-pwr-2g-mcs7 = <16>; + +wlan0: wlan { + compatible = "nordic,wlan"; +}; diff --git a/boards/nordic/nrf7002dk/nrf70_common_5g.dtsi b/boards/nordic/nrf7002dk/nrf70_common_5g.dtsi new file mode 100644 index 000000000000..8be559cebc33 --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf70_common_5g.dtsi @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +wifi-max-tx-pwr-5g-low-mcs0 = <9>; +wifi-max-tx-pwr-5g-low-mcs7 = <9>; +wifi-max-tx-pwr-5g-mid-mcs0 = <11>; +wifi-max-tx-pwr-5g-mid-mcs7 = <11>; +wifi-max-tx-pwr-5g-high-mcs0 = <13>; +wifi-max-tx-pwr-5g-high-mcs7 = <13>; diff --git a/boards/nordic/nrf7002dk/nrf70_common_coex.dtsi b/boards/nordic/nrf7002dk/nrf70_common_coex.dtsi new file mode 100644 index 000000000000..03f22c3edbaa --- /dev/null +++ b/boards/nordic/nrf7002dk/nrf70_common_coex.dtsi @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +req-gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; +status0-gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>; +grant-gpios = <&gpio0 24 (GPIO_PULL_DOWN | GPIO_ACTIVE_LOW)>; +swctrl1-gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>; +srrf-switch-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>; diff --git a/boards/nordic/nrf7002dk/pre_dt_board.cmake b/boards/nordic/nrf7002dk/pre_dt_board.cmake new file mode 100644 index 000000000000..5db1310639dc --- /dev/null +++ b/boards/nordic/nrf7002dk/pre_dt_board.cmake @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +# Suppress "unique_unit_address_if_enabled" to handle the following overlaps: +# - flash-controller@39000 & kmu@39000 +# - power@5000 & clock@5000 +# - /reserved-memory/image@20000000 & /reserved-memory/image_s@20000000 +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") From d4cd761bfa058b8ea8f0d5c6e9ab36399335b9aa Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Wed, 7 Aug 2024 09:53:42 +0200 Subject: [PATCH 34/51] [nrf fromtree] boards: nordic: nrf7002dk: add docs Add documentation to nrf7002dk board Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit e8f37c7640b48d32f51e4c2976fcca27644eb55b) --- boards/nordic/nrf7002dk/doc/img/nrf7002dk.jpg | Bin 0 -> 68268 bytes boards/nordic/nrf7002dk/doc/index.rst | 341 ++++++++++++++++++ 2 files changed, 341 insertions(+) create mode 100644 boards/nordic/nrf7002dk/doc/img/nrf7002dk.jpg create mode 100644 boards/nordic/nrf7002dk/doc/index.rst diff --git a/boards/nordic/nrf7002dk/doc/img/nrf7002dk.jpg b/boards/nordic/nrf7002dk/doc/img/nrf7002dk.jpg new file mode 100644 index 0000000000000000000000000000000000000000..51d5572a13be91e18ea26f9c71b70187048a00cb GIT binary patch literal 68268 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, run + this command for more details on the related ``--recover`` option: + + .. code-block:: console + + west flash -H -r nrfjprog --skip-rebuild + +.. note:: + + Flashing and debugging applications on the nRF5340 DK requires + upgrading the nRF Command Line Tools to version 10.12.0. Further + information on how to install the nRF Command Line Tools can be + found in :ref:`nordic_segger_flashing`. + +Here is an example for the :ref:`hello_world` application running on the +nRF5340 application core. + +First, run your favorite terminal program to listen for output. + +.. code-block:: console + + $ minicom -D -b 115200 + +Replace :code:`` with the port where the board nRF7002 DK +can be found. For example, under Linux, :code:`/dev/ttyACM0`. + +Then build and flash the application in the usual way. + +.. zephyr-app-commands:: + :zephyr-app: samples/hello_world + :board: nrf7002dk/nrf5340/cpuapp + :goals: build flash + +Debugging +========= + +Refer to the :ref:`nordic_segger` page to learn about debugging Nordic +boards with a Segger IC. + +Next steps +********** + +You have now completed getting started with the nRF7002 DK. +See the following links for where to go next: + +* `Installation`_ and `Configuring and Building`_ documentation to install the + nRF Connect SDK and learn more about its development environment. +* `Developing with nRF70`_ documentation for more advanced topics related to the nRF70 Series. +* `Wi-Fi`_ documentation for information related to Wi-Fi protocol and Wi-Fi modes of operation. + +References +********** + +.. target-notes:: + +.. _Wi-Fi Certification program: + https://www.wi-fi.org/certification +.. _UG Wi-Fi certification: + https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/wifi/wifi_certification.html#ug-wifi-certification +.. _IDAU: + https://developer.arm.com/docs/100690/latest/attribution-units-sau-and-idau +.. _nRF7002 DK website: + https://www.nordicsemi.com/Products/Development-hardware/nrf7002-dk +.. _nRF7002 DK Product Specification: + https://docs.nordicsemi.com/bundle/ps_nrf5340/page/keyfeatures_html5.html +.. _Trusted Firmware M: + https://www.trustedfirmware.org/projects/tf-m/ +.. _Installation: + https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/installation.html#installation +.. _Configuring and Building: + https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/app_dev/config_and_build/index.html#configuration-and-build +.. _Developing with nRF70: + https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/app_dev/device_guides/nrf70/index.html#ug-nrf70-developing +.. _Wi-Fi: + https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/wifi/index.html#ug-wifi From dc510642b2351b5a06060fc15509a48ca2640b78 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 14 Aug 2024 01:43:58 +0530 Subject: [PATCH 35/51] [nrf noup] nrf70_NS: Disable all tests nRF70 Wi-Fi upstream doesn't yet support NS variants. Signed-off-by: Chaitanya Tata (cherry picked from commit edd656012c076f6fffaaf615dcc7d2d075f38fd0) --- .../nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns.yaml | 4 ++++ boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.yaml | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns.yaml b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns.yaml index 165759691260..efdfcb7a0690 100644 --- a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns.yaml +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_nrf7001_ns.yaml @@ -17,3 +17,7 @@ supported: - usb_device - netif:openthread vendor: nordic +testing: + default: true + ignore_tags: + - ci_samples_wifi diff --git a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.yaml b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.yaml index ea43785b4559..b2f57b48546a 100644 --- a/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.yaml +++ b/boards/nordic/nrf7002dk/nrf7002dk_nrf5340_cpuapp_ns.yaml @@ -17,3 +17,8 @@ supported: - usb_device - netif:openthread vendor: nordic +testing: + default: true + ignore_tags: + - ci_build + - sysbuild From ea833c2764cedbdc1e00d946ae592475e8ab4365 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Sun, 11 Aug 2024 21:44:48 +0530 Subject: [PATCH 36/51] [nrf fromtree] modules: hostap: Convert WPA cli to selectable option This should be configurable by applications in case a full CLI interface to the WPA supplicant is needed. Signed-off-by: Chaitanya Tata (cherry picked from commit 6b79e34b9579cc81ba0a5c9e925b682d18c8a9eb) --- modules/hostap/Kconfig | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/hostap/Kconfig b/modules/hostap/Kconfig index 62850fd83e07..2aa457b3586f 100644 --- a/modules/hostap/Kconfig +++ b/modules/hostap/Kconfig @@ -258,6 +258,12 @@ config WIFI_NM_WPA_SUPPLICANT_DPP select MBEDTLS_X509_CSR_WRITE_C select MBEDTLS_X509_CSR_PARSE_C + +config WPA_CLI + bool "WPA CLI support" + help + Enable WPA CLI support for wpa_supplicant. + # Create hidden config options that are used in hostap. This way we do not need # to mark them as allowed for CI checks, and also someone else cannot use the # same name options. @@ -429,9 +435,6 @@ config WEP bool default y if WIFI_NM_WPA_SUPPLICANT_WEP -config WPA_CLI - bool - config WPA_CRYPTO bool From 4b9969d363c954046150773e44d1ec3d08f466b5 Mon Sep 17 00:00:00 2001 From: Maochen Wang Date: Thu, 8 Aug 2024 16:05:08 +0800 Subject: [PATCH 37/51] [nrf fromtree] manifest: Update hostap to remove els_pkc header file Remove wpa_supp_els_pkc_mbedtls_config.h, as this header file contains PSA_CRYPTO_DRIVER_ELS_PKC, and ELS-PKC is a proprietary component of nxp to provides HW acceleration for psa-apis. Signed-off-by: Maochen Wang (cherry picked from commit 6d01073e79291e24002a4fc7694d4a8fd98e6a2c) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index dca83deb0d16..542225e10168 100644 --- a/west.yml +++ b/west.yml @@ -256,7 +256,7 @@ manifest: - name: hostap repo-path: hostap path: modules/lib/hostap - revision: a90df86d7c596a5367ff70c2b50c7f599e6636f3 + revision: a941086775a865d170743a5d4790f1fa213ec6a4 - name: libmetal revision: 243eed541b9c211a2ce8841c788e62ddce84425e path: modules/hal/libmetal From 98401877f11ebdb205d2c641248c0805bfc5496c Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Mon, 12 Aug 2024 19:02:03 +0530 Subject: [PATCH 38/51] [nrf fromtree] manifest: hostap: Pull fix for SAP SAP build fails due to missing WPA supplicant driver OP. Signed-off-by: Chaitanya Tata (cherry picked from commit 69ad8937b1696a42191f9b5d803bdfa8827192cd) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 542225e10168..f189e98281c0 100644 --- a/west.yml +++ b/west.yml @@ -256,7 +256,7 @@ manifest: - name: hostap repo-path: hostap path: modules/lib/hostap - revision: a941086775a865d170743a5d4790f1fa213ec6a4 + revision: 77a4cad575c91f1b234c8d15630f87999881cde2 - name: libmetal revision: 243eed541b9c211a2ce8841c788e62ddce84425e path: modules/hal/libmetal From cc2162781b51b55c74db44d9b5fe90cdff903af1 Mon Sep 17 00:00:00 2001 From: Herman Berget Date: Mon, 5 Aug 2024 12:54:54 +0200 Subject: [PATCH 39/51] [nrf fromtree] manifest: Update hal_nordic with nonsecure PPIB fix Secure PPIB instances were accessed even when building for nonsecure Signed-off-by: Herman Berget (cherry picked from commit af314643a32db3452dd77f20dee2807197b179cf) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index f189e98281c0..5cd13dcc19cb 100644 --- a/west.yml +++ b/west.yml @@ -183,7 +183,7 @@ manifest: groups: - hal - name: hal_nordic - revision: bda16da6f92380579b8cf681a5faf61e1fe55220 + revision: 0b6040d9b440b769a65f25c6fd9320eeff88ee94 path: modules/hal/nordic groups: - hal From ab1b9470c07b4f2b51d6f134ef7bd3134ad6a79d Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 7 Aug 2024 01:55:04 +0530 Subject: [PATCH 40/51] [nrf fromtree] manifest: hal_nordic: Pull latest Wi-Fi OSAL Pull latest OSAL code including FW. Signed-off-by: Chaitanya Tata (cherry picked from commit 2ec8bfda658cb42284b67c644e8b5f55f554811e) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 5cd13dcc19cb..c4d0f1478907 100644 --- a/west.yml +++ b/west.yml @@ -183,7 +183,7 @@ manifest: groups: - hal - name: hal_nordic - revision: 0b6040d9b440b769a65f25c6fd9320eeff88ee94 + revision: 91654ddc7ce0da523eb4d6be2171208ae2b8fb35 path: modules/hal/nordic groups: - hal From c3a54bd39acf7ba8462fd01a70597822b6584521 Mon Sep 17 00:00:00 2001 From: Kapil Bhatt Date: Tue, 11 Jun 2024 11:32:29 +0530 Subject: [PATCH 41/51] [nrf fromtree] drivers: wifi: Add kconfig option to disable WMM feature [SHEL-2054] Adding a kconfig option for WMM. By default it will be enabled. If user needs to disable it, set it as n. Signed-off-by: Kapil Bhatt (cherry picked from commit 37a98bfa793366a6e9bcd37fbb3e75622117d2f5) --- drivers/wifi/nrfwifi/Kconfig.nrfwifi | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/wifi/nrfwifi/Kconfig.nrfwifi b/drivers/wifi/nrfwifi/Kconfig.nrfwifi index a83320f864f0..c6d6461df31d 100644 --- a/drivers/wifi/nrfwifi/Kconfig.nrfwifi +++ b/drivers/wifi/nrfwifi/Kconfig.nrfwifi @@ -667,6 +667,12 @@ config NRF_WIFI_COMBINED_BUCKEN_IOVDD_GPIO there will be a internal hardware switch to add delay between the two operations. This is typically 4ms delay for nRF70. +config NRF_WIFI_FEAT_WMM + bool "WMM/QoS support" + default y + help + This option controls disable/enable of the WMM(Wireless Multi-Media) feature. + # TODO: Temporary WAR, implement these options in the future config NRF_WIFI_PATCHES_EXT_FLASH_DISABLED bool "nRF70 firmware patch external flash support" @@ -694,10 +700,4 @@ config NRF_WIFI_PATCHES_EXT_FLASH_XIP help Select this option to enable external flash XIP for nRF70 firmware patches -config NRF_WIFI_FEAT_WMM - bool "WMM/QoS support" - default y - help - This option controls disable/enable of the WMM(Wireless Multi-Media) feature. - endif # WIFI_NRF70 From 6181cc1e6041cba55800789e64d18e65fa918764 Mon Sep 17 00:00:00 2001 From: Kapil Bhatt Date: Fri, 19 Jul 2024 18:22:04 +0530 Subject: [PATCH 42/51] [nrf fromtree] drivers: wifi: Reset interface statistics data [SHEL-2542] When reset command is called this will reset all statistics including firmware and host. Signed-off-by: Kapil Bhatt (cherry picked from commit e085d440c21d368f2a4b77e2b8f8eca121a24750) --- drivers/wifi/nrfwifi/inc/net_if.h | 2 ++ drivers/wifi/nrfwifi/src/fmac_main.c | 1 + drivers/wifi/nrfwifi/src/net_if.c | 48 ++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/drivers/wifi/nrfwifi/inc/net_if.h b/drivers/wifi/nrfwifi/inc/net_if.h index f4abf50c13ff..8d97802ba65c 100644 --- a/drivers/wifi/nrfwifi/inc/net_if.h +++ b/drivers/wifi/nrfwifi/inc/net_if.h @@ -61,4 +61,6 @@ struct net_stats_eth *nrf_wifi_eth_stats_get(const struct device *dev); void nrf_wifi_set_iface_event_handler(void *os_vif_ctx, struct nrf_wifi_umac_event_set_interface *event, unsigned int event_len); + +int nrf_wifi_stats_reset(const struct device *dev); #endif /* __ZEPHYR_NET_IF_H__ */ diff --git a/drivers/wifi/nrfwifi/src/fmac_main.c b/drivers/wifi/nrfwifi/src/fmac_main.c index 9f762b16f1b9..c1196ab6a4a4 100644 --- a/drivers/wifi/nrfwifi/src/fmac_main.c +++ b/drivers/wifi/nrfwifi/src/fmac_main.c @@ -819,6 +819,7 @@ static struct wifi_mgmt_ops nrf_wifi_mgmt_ops = { .scan = nrf_wifi_disp_scan_zep, #ifdef CONFIG_NET_STATISTICS_WIFI .get_stats = nrf_wifi_stats_get, + .reset_stats = nrf_wifi_stats_reset, #endif /* CONFIG_NET_STATISTICS_WIFI */ #ifdef CONFIG_NRF70_STA_MODE .set_power_save = nrf_wifi_set_power_save, diff --git a/drivers/wifi/nrfwifi/src/net_if.c b/drivers/wifi/nrfwifi/src/net_if.c index 55d490852411..ea08c7f6cb39 100644 --- a/drivers/wifi/nrfwifi/src/net_if.c +++ b/drivers/wifi/nrfwifi/src/net_if.c @@ -1142,4 +1142,52 @@ int nrf_wifi_stats_get(const struct device *dev, struct net_stats_wifi *zstats) out: return ret; } + +int nrf_wifi_stats_reset(const struct device *dev) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + int ret = -1; + + if (!dev) { + LOG_ERR("%s Device not found", __func__); + goto out; + } + + vif_ctx_zep = dev->data; + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + goto out; + } + + ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (ret != 0) { + LOG_ERR("%s: Failed to lock vif_lock", __func__); + goto out; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: rpu_ctx_zep or rpu_ctx is NULL", + __func__); + goto unlock; + } + + status = nrf_wifi_fmac_stats_reset(rpu_ctx_zep->rpu_ctx); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_stats_reset failed", __func__); + goto unlock; + } + + def_dev_ctx = wifi_dev_priv(rpu_ctx_zep->rpu_ctx); + memset(&def_dev_ctx->host_stats, 0, sizeof(struct rpu_host_stats)); + + ret = 0; +unlock: + k_mutex_unlock(&vif_ctx_zep->vif_lock); +out: + return ret; +} #endif /* CONFIG_NET_STATISTICS_WIFI */ From 77ee23819d965e9813fe159e3e8f7c8ab9c14849 Mon Sep 17 00:00:00 2001 From: Sachin D Kulkarni Date: Wed, 19 Jun 2024 17:54:09 +0530 Subject: [PATCH 43/51] [nrf fromtree] nrf_wifi: Remove dependency on OSAL layer handle Removes the requirement for the different layers in the OS agnostic parts of the driver having to maintain a handle to the OS interface layer in order to call the OS interface calls. The OS interface layer now maitains the handle to OS-specific ops internally and invokes the appropriate functions. Fixes SHEL-2639 Signed-off-by: Sachin D Kulkarni (cherry picked from commit 1f494387b18b0f5e9a91b479c20c55b63d3e2415) --- drivers/wifi/nrfwifi/src/fmac_main.c | 14 +++++++++++++- drivers/wifi/nrfwifi/src/net_if.c | 6 ++---- drivers/wifi/nrfwifi/src/shim.c | 7 +------ drivers/wifi/nrfwifi/src/wifi_mgmt.c | 18 ++++++------------ drivers/wifi/nrfwifi/src/wifi_mgmt_scan.c | 19 ++++++------------- drivers/wifi/nrfwifi/src/wifi_util.c | 2 +- drivers/wifi/nrfwifi/src/wpa_supp_if.c | 4 ++-- 7 files changed, 31 insertions(+), 39 deletions(-) diff --git a/drivers/wifi/nrfwifi/src/fmac_main.c b/drivers/wifi/nrfwifi/src/fmac_main.c index c1196ab6a4a4..99b6c8cdf72d 100644 --- a/drivers/wifi/nrfwifi/src/fmac_main.c +++ b/drivers/wifi/nrfwifi/src/fmac_main.c @@ -44,6 +44,7 @@ LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL); struct nrf_wifi_drv_priv_zep rpu_drv_priv_zep; +extern const struct nrf_wifi_osal_ops nrf_wifi_os_zep_ops; /* 3 bytes for addreess, 3 bytes for length */ #define MAX_PKT_RAM_TX_ALIGN_OVERHEAD 6 @@ -259,7 +260,7 @@ static void nrf_wifi_process_rssi_from_rx(void *vif_ctx, vif_ctx_zep->rssi = MBM_TO_DBM(signal); vif_ctx_zep->rssi_record_timestamp_us = - nrf_wifi_osal_time_get_curr_us(fmac_dev_ctx->fpriv->opriv); + nrf_wifi_osal_time_get_curr_us(); } #endif /* CONFIG_NRF70_STA_MODE */ @@ -762,12 +763,22 @@ static int nrf_wifi_drv_main_zep(const struct device *dev) callbk_fns.get_conn_info_callbk_fn = nrf_wifi_supp_event_proc_get_conn_info; #endif /* CONFIG_NRF70_STA_MODE */ + /* The OSAL layer needs to be initialized before any other initialization + * so that other layers (like FW IF,HW IF etc) have access to OS ops + */ + nrf_wifi_osal_init(&nrf_wifi_os_zep_ops); + rpu_drv_priv_zep.fmac_priv = nrf_wifi_fmac_init(&data_config, rx_buf_pools, &callbk_fns); #else /* !CONFIG_NRF70_RADIO_TEST */ enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + /* The OSAL layer needs to be initialized before any other initialization + * so that other layers (like FW IF,HW IF etc) have access to OS ops + */ + nrf_wifi_osal_init(&nrf_wifi_os_zep_ops); + rpu_drv_priv_zep.fmac_priv = nrf_wifi_fmac_init_rt(); #endif /* CONFIG_NRF70_RADIO_TEST */ @@ -808,6 +819,7 @@ static int nrf_wifi_drv_main_zep(const struct device *dev) #ifdef CONFIG_NRF70_RADIO_TEST fmac_deinit: nrf_wifi_fmac_deinit_rt(rpu_drv_priv_zep.fmac_priv); + nrf_wifi_osal_deinit(); #endif /* CONFIG_NRF70_RADIO_TEST */ err: return -1; diff --git a/drivers/wifi/nrfwifi/src/net_if.c b/drivers/wifi/nrfwifi/src/net_if.c index ea08c7f6cb39..261e67f7bc05 100644 --- a/drivers/wifi/nrfwifi/src/net_if.c +++ b/drivers/wifi/nrfwifi/src/net_if.c @@ -487,8 +487,7 @@ enum nrf_wifi_status nrf_wifi_get_mac_addr(struct nrf_wifi_vif_ctx_zep *vif_ctx_ } #endif - if (!nrf_wifi_utils_is_mac_addr_valid(fmac_dev_ctx->fpriv->opriv, - vif_ctx_zep->mac_addr.addr)) { + if (!nrf_wifi_utils_is_mac_addr_valid(vif_ctx_zep->mac_addr.addr)) { LOG_ERR("%s: Invalid MAC address: %s", __func__, net_sprint_ll_addr(vif_ctx_zep->mac_addr.addr, @@ -682,8 +681,7 @@ int nrf_wifi_if_start_zep(const struct device *dev) mac_addr = net_if_get_link_addr(vif_ctx_zep->zep_net_if_ctx)->addr; mac_addr_len = net_if_get_link_addr(vif_ctx_zep->zep_net_if_ctx)->len; - if (!nrf_wifi_utils_is_mac_addr_valid(fmac_dev_ctx->fpriv->opriv, - mac_addr)) { + if (!nrf_wifi_utils_is_mac_addr_valid(mac_addr)) { status = nrf_wifi_get_mac_addr(vif_ctx_zep); if (status != NRF_WIFI_STATUS_SUCCESS) { LOG_ERR("%s: Failed to get MAC address", diff --git a/drivers/wifi/nrfwifi/src/shim.c b/drivers/wifi/nrfwifi/src/shim.c index 209bc1256526..4db264f9c963 100644 --- a/drivers/wifi/nrfwifi/src/shim.c +++ b/drivers/wifi/nrfwifi/src/shim.c @@ -872,7 +872,7 @@ static unsigned int zep_shim_strlen(const void *str) return strlen(str); } -static const struct nrf_wifi_osal_ops nrf_wifi_os_zep_ops = { +const struct nrf_wifi_osal_ops nrf_wifi_os_zep_ops = { .mem_alloc = zep_shim_mem_alloc, .mem_zalloc = zep_shim_mem_zalloc, .mem_free = k_free, @@ -962,8 +962,3 @@ static const struct nrf_wifi_osal_ops nrf_wifi_os_zep_ops = { .assert = zep_shim_assert, .strlen = zep_shim_strlen, }; - -const struct nrf_wifi_osal_ops *get_os_ops(void) -{ - return &nrf_wifi_os_zep_ops; -} diff --git a/drivers/wifi/nrfwifi/src/wifi_mgmt.c b/drivers/wifi/nrfwifi/src/wifi_mgmt.c index 2d67e5695123..b18e893dcfbc 100644 --- a/drivers/wifi/nrfwifi/src/wifi_mgmt.c +++ b/drivers/wifi/nrfwifi/src/wifi_mgmt.c @@ -203,15 +203,13 @@ int nrf_wifi_get_power_save_config(const struct device *dev, } do { - nrf_wifi_osal_sleep_ms(fmac_dev_ctx->fpriv->opriv, - 1); + nrf_wifi_osal_sleep_ms(1); count++; } while ((vif_ctx_zep->ps_config_info_evnt == false) && (count < NRF_WIFI_FMAC_PS_CONF_EVNT_RECV_TIMEOUT)); if (count == NRF_WIFI_FMAC_PS_CONF_EVNT_RECV_TIMEOUT) { - nrf_wifi_osal_log_err(fmac_dev_ctx->fpriv->opriv, - "%s: Timed out", + nrf_wifi_osal_log_err("%s: Timed out", __func__); goto out; } @@ -697,19 +695,16 @@ void nrf_wifi_event_proc_twt_sleep_zep(void *vif_ctx, switch (sleep_evnt->info.type) { case TWT_BLOCK_TX: - nrf_wifi_osal_spinlock_take(fmac_dev_ctx->fpriv->opriv, - def_dev_ctx->tx_config.tx_lock); + nrf_wifi_osal_spinlock_take(def_dev_ctx->tx_config.tx_lock); def_dev_ctx->twt_sleep_status = NRF_WIFI_FMAC_TWT_STATE_SLEEP; wifi_mgmt_raise_twt_sleep_state(vif_ctx_zep->zep_net_if_ctx, WIFI_TWT_STATE_SLEEP); - nrf_wifi_osal_spinlock_rel(fmac_dev_ctx->fpriv->opriv, - def_dev_ctx->tx_config.tx_lock); + nrf_wifi_osal_spinlock_rel(def_dev_ctx->tx_config.tx_lock); break; case TWT_UNBLOCK_TX: - nrf_wifi_osal_spinlock_take(fmac_dev_ctx->fpriv->opriv, - def_dev_ctx->tx_config.tx_lock); + nrf_wifi_osal_spinlock_take(def_dev_ctx->tx_config.tx_lock); def_dev_ctx->twt_sleep_status = NRF_WIFI_FMAC_TWT_STATE_AWAKE; wifi_mgmt_raise_twt_sleep_state(vif_ctx_zep->zep_net_if_ctx, WIFI_TWT_STATE_AWAKE); @@ -722,8 +717,7 @@ void nrf_wifi_event_proc_twt_sleep_zep(void *vif_ctx, } } #endif - nrf_wifi_osal_spinlock_rel(fmac_dev_ctx->fpriv->opriv, - def_dev_ctx->tx_config.tx_lock); + nrf_wifi_osal_spinlock_rel(def_dev_ctx->tx_config.tx_lock); break; default: break; diff --git a/drivers/wifi/nrfwifi/src/wifi_mgmt_scan.c b/drivers/wifi/nrfwifi/src/wifi_mgmt_scan.c index 64bf87628301..66cd72e4f58f 100644 --- a/drivers/wifi/nrfwifi/src/wifi_mgmt_scan.c +++ b/drivers/wifi/nrfwifi/src/wifi_mgmt_scan.c @@ -192,7 +192,7 @@ int nrf_wifi_disp_scan_zep(const struct device *dev, struct wifi_scan_params *pa } scan_info->scan_params.center_frequency[k++] = nrf_wifi_utils_chan_to_freq( - fmac_dev_ctx->fpriv->opriv, band, params->band_chan[i].channel); + band, params->band_chan[i].channel); if (scan_info->scan_params.center_frequency[k - 1] == -1) { LOG_ERR("%s: Invalid channel %d", __func__, @@ -414,23 +414,16 @@ void nrf_wifi_rx_bcn_prb_resp_frm(void *vif_ctx, fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; - frame_length = nrf_wifi_osal_nbuf_data_size(fmac_dev_ctx->fpriv->opriv, - nwb); + frame_length = nrf_wifi_osal_nbuf_data_size(nwb); if (frame_length > CONFIG_WIFI_MGMT_RAW_SCAN_RESULT_LENGTH) { - nrf_wifi_osal_mem_cpy(fmac_dev_ctx->fpriv->opriv, - &bcn_prb_resp_info.data, - nrf_wifi_osal_nbuf_data_get( - fmac_dev_ctx->fpriv->opriv, - nwb), + nrf_wifi_osal_mem_cpy(&bcn_prb_resp_info.data, + nrf_wifi_osal_nbuf_data_get(nwb), CONFIG_WIFI_MGMT_RAW_SCAN_RESULT_LENGTH); } else { - nrf_wifi_osal_mem_cpy(fmac_dev_ctx->fpriv->opriv, - &bcn_prb_resp_info.data, - nrf_wifi_osal_nbuf_data_get( - fmac_dev_ctx->fpriv->opriv, - nwb), + nrf_wifi_osal_mem_cpy(&bcn_prb_resp_info.data, + nrf_wifi_osal_nbuf_data_get(nwb), frame_length); } diff --git a/drivers/wifi/nrfwifi/src/wifi_util.c b/drivers/wifi/nrfwifi/src/wifi_util.c index 7dc4e78473ac..aec552754ab6 100644 --- a/drivers/wifi/nrfwifi/src/wifi_util.c +++ b/drivers/wifi/nrfwifi/src/wifi_util.c @@ -295,7 +295,7 @@ static int nrf_wifi_util_tx_stats(const struct shell *sh, for (int i = 0; i < NRF_WIFI_FMAC_AC_MAX ; i++) { queue = def_dev_ctx->tx_config.data_pending_txq[peer_index][i]; - tx_pending_pkts = nrf_wifi_utils_q_len(fmac_dev_ctx->fpriv->opriv, queue); + tx_pending_pkts = nrf_wifi_utils_q_len(queue); shell_fprintf( sh, diff --git a/drivers/wifi/nrfwifi/src/wpa_supp_if.c b/drivers/wifi/nrfwifi/src/wpa_supp_if.c index d99e4fa2952a..6abddfb9bea5 100644 --- a/drivers/wifi/nrfwifi/src/wpa_supp_if.c +++ b/drivers/wifi/nrfwifi/src/wpa_supp_if.c @@ -1172,8 +1172,8 @@ int nrf_wifi_wpa_supp_signal_poll(void *if_priv, struct wpa_signal_info *si, uns vif_ctx_zep->signal_info = si; - rssi_record_elapsed_time_ms = nrf_wifi_osal_time_elapsed_us(fmac_dev_ctx->fpriv->opriv, - vif_ctx_zep->rssi_record_timestamp_us) / 1000; + rssi_record_elapsed_time_ms = + nrf_wifi_osal_time_elapsed_us(vif_ctx_zep->rssi_record_timestamp_us) / 1000; if (rssi_record_elapsed_time_ms > CONFIG_NRF70_RSSI_STALE_TIMEOUT_MS) { ret = nrf_wifi_fmac_get_station(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, bssid); From 985a0311ca074cf16d2c53efdd0a731e2e0b1b9c Mon Sep 17 00:00:00 2001 From: Ajay Parida Date: Wed, 17 Jul 2024 12:35:17 +0530 Subject: [PATCH 44/51] [nrf fromtree] drivers: wifi: Option for PS data retrieval SHEL-2947] Option to set either PS-poll or QoS null frame based power save data retrieval mechanism. Signed-off-by: Ajay Parida (cherry picked from commit 41e29c6cadd5e05034fc4e4df214aa212545ca3a) --- drivers/wifi/nrfwifi/Kconfig.nrfwifi | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/wifi/nrfwifi/Kconfig.nrfwifi b/drivers/wifi/nrfwifi/Kconfig.nrfwifi index c6d6461df31d..03109ed26dd9 100644 --- a/drivers/wifi/nrfwifi/Kconfig.nrfwifi +++ b/drivers/wifi/nrfwifi/Kconfig.nrfwifi @@ -700,4 +700,27 @@ config NRF_WIFI_PATCHES_EXT_FLASH_XIP help Select this option to enable external flash XIP for nRF70 firmware patches +choice NRF_WIFI_PS_DATA_RETRIEVAL_MECHANISM + prompt "Power save data retrieval mechanism" + default NRF_WIFI_PS_POLL_BASED_RETRIEVAL + help + Select the mechanism to retrieve buffered data from AP. + +config NRF_WIFI_PS_POLL_BASED_RETRIEVAL + bool "PS-Poll frame based mechanism to retrieve buffered data from AP" + help + When AP notifies about availability of buffered data, the STA stays in power save + and retrieves the frames one-by-one, this conserved more power but adds latency + to the traffic. Ideal for minimum number of frames. + +config NRF_WIFI_QOS_NULL_BASED_RETRIEVAL + bool "QoS null frame based mechanism to retrieve buffered data from AP" + help + When AP notifies about availability of buffered data, the STA comes out of + power save and then AP can deliver all buffered frames without any additional + overhead or latency, but STA enters power save after a delay costing more power + depending on the delay. Ideal for heavy buffered traffic. + +endchoice + endif # WIFI_NRF70 From 55c9e137409d252f6c0a8b392f43951d7bc6e38a Mon Sep 17 00:00:00 2001 From: Kapil Bhatt Date: Mon, 29 Jul 2024 15:09:11 +0530 Subject: [PATCH 45/51] [nrf fromtree] drivers: wifi: Add tx packets drop count calculation [SHEL-1063] Add calculation of drop packets in tx due to lack of buffer memory. Signed-off-by: Kapil Bhatt (cherry picked from commit e5a665d4aa9f032ef937080f7bf7350f20488d66) --- drivers/wifi/nrfwifi/src/net_if.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/wifi/nrfwifi/src/net_if.c b/drivers/wifi/nrfwifi/src/net_if.c index 261e67f7bc05..94d53e2a2761 100644 --- a/drivers/wifi/nrfwifi/src/net_if.c +++ b/drivers/wifi/nrfwifi/src/net_if.c @@ -282,6 +282,9 @@ int nrf_wifi_if_send(const struct device *dev, #ifdef CONFIG_NRF70_DATA_TX struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + struct rpu_host_stats *host_stats = NULL; + void *nbuf = NULL; if (!dev || !pkt) { LOG_ERR("%s: vif_ctx_zep is NULL", __func__); @@ -306,6 +309,15 @@ int nrf_wifi_if_send(const struct device *dev, goto unlock; } + def_dev_ctx = wifi_dev_priv(rpu_ctx_zep->rpu_ctx); + host_stats = &def_dev_ctx->host_stats; + nbuf = net_pkt_to_nbuf(pkt); + if (!nbuf) { + LOG_DBG("Failed to allocate net_pkt"); + host_stats->total_tx_drop_pkts++; + goto out; + } + #ifdef CONFIG_NRF70_RAW_DATA_TX if ((*(unsigned int *)pkt->frags->data) == NRF_WIFI_MAGIC_NUM_RAWTX) { if (vif_ctx_zep->if_carr_state != NRF_WIFI_FMAC_IF_CARR_STATE_ON) { @@ -314,7 +326,7 @@ int nrf_wifi_if_send(const struct device *dev, ret = nrf_wifi_fmac_start_rawpkt_xmit(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, - net_pkt_to_nbuf(pkt)); + nbuf); } else { #endif /* CONFIG_NRF70_RAW_DATA_TX */ if ((vif_ctx_zep->if_carr_state != NRF_WIFI_FMAC_IF_CARR_STATE_ON) || @@ -324,7 +336,7 @@ int nrf_wifi_if_send(const struct device *dev, ret = nrf_wifi_fmac_start_xmit(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, - net_pkt_to_nbuf(pkt)); + nbuf); #ifdef CONFIG_NRF70_RAW_DATA_TX } #endif /* CONFIG_NRF70_RAW_DATA_TX */ From 0385e018135fc688d1cf7edfaea9a5d3243e1696 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Thu, 1 Aug 2024 18:54:32 +0530 Subject: [PATCH 46/51] [nrf fromtree] drivers: wifi: Fix QSPI clock dividers For less than 32MHz using HFCLK192M, /2 divider should be used and only for Anamoly159 /1 divider should be used. Without this fix 8MHz clock in DTS uses 16MHz clock. Signed-off-by: Chaitanya Tata (cherry picked from commit ed58af2ccdb838ace678d59cd6269258192d6c5e) --- drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c | 31 +++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c b/drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c index 968ca72ad273..fdbad39dd70f 100644 --- a/drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c +++ b/drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c @@ -85,18 +85,39 @@ BUILD_ASSERT(QSPI_IF_DEVICE_FREQUENCY >= (NRF_QSPI_BASE_CLOCK_FREQ / 16), * PCLK192M frequency"), but after that operation is complete, the default * divider needs to be restored to avoid increased current consumption. */ -/* To prevent anomaly 159, use only divider /1 for HFCLK192M. */ +#if (INST_0_SCK_FREQUENCY >= NRF_QSPI_BASE_CLOCK_FREQ) +/* For requested SCK >= 96 MHz, use HFCLK192M / 1 / (2*1) = 96 MHz */ #define BASE_CLOCK_DIV NRF_CLOCK_HFCLK_DIV_1 -#if (QSPI_IF_DEVICE_FREQUENCY >= (NRF_QSPI_BASE_CLOCK_FREQ / 4)) -/* For requested SCK >= 24 MHz, use HFCLK192M / 1 / (2*4) = 24 MHz */ -#define INST_0_SCK_CFG NRF_QSPI_FREQ_DIV4 +#define INST_0_SCK_CFG NRF_QSPI_FREQ_DIV1 +/* If anomaly 159 is to be prevented, only /1 divider can be used. */ +#elif NRF53_ERRATA_159_ENABLE_WORKAROUND +#define BASE_CLOCK_DIV NRF_CLOCK_HFCLK_DIV_1 +#define INST_0_SCK_CFG (DIV_ROUND_UP(NRF_QSPI_BASE_CLOCK_FREQ, \ + INST_0_SCK_FREQUENCY) - 1) +#elif (INST_0_SCK_FREQUENCY >= (NRF_QSPI_BASE_CLOCK_FREQ / 2)) +/* For 96 MHz > SCK >= 48 MHz, use HFCLK192M / 2 / (2*1) = 48 MHz */ +#define BASE_CLOCK_DIV NRF_CLOCK_HFCLK_DIV_2 +#define INST_0_SCK_CFG NRF_QSPI_FREQ_DIV1 +#elif (INST_0_SCK_FREQUENCY >= (NRF_QSPI_BASE_CLOCK_FREQ / 3)) +/* For 48 MHz > SCK >= 32 MHz, use HFCLK192M / 1 / (2*3) = 32 MHz */ +#define BASE_CLOCK_DIV NRF_CLOCK_HFCLK_DIV_1 +#define INST_0_SCK_CFG NRF_QSPI_FREQ_DIV3 #else -/* For requested SCK < 24 MHz, calculate the configuration value. */ +/* For requested SCK < 32 MHz, use divider /2 for HFCLK192M. */ +#define BASE_CLOCK_DIV NRF_CLOCK_HFCLK_DIV_2 #define INST_0_SCK_CFG (DIV_ROUND_UP(NRF_QSPI_BASE_CLOCK_FREQ / 2, \ QSPI_IF_DEVICE_FREQUENCY) - 1) #endif + +#if BASE_CLOCK_DIV == NRF_CLOCK_HFCLK_DIV_1 +/* For 8 MHz, use HFCLK192M / 1 / (2*12) */ +#define INST_0_SCK_CFG_WAKE NRF_QSPI_FREQ_DIV12 +#elif BASE_CLOCK_DIV == NRF_CLOCK_HFCLK_DIV_2 /* For 8 MHz, use HFCLK192M / 2 / (2*6) */ #define INST_0_SCK_CFG_WAKE NRF_QSPI_FREQ_DIV6 +#else +#error "Unsupported base clock divider for wake-up frequency." +#endif #else /* From 5d7667643467033e536baea20af4322a709e2468 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Thu, 1 Aug 2024 23:31:41 +0530 Subject: [PATCH 47/51] [nrf fromtree] drivers: wifi: Wait for clock divider to take effect This is a fix from QSPI-NOR driver to wait for clock divider change to be applied and take effect. Signed-off-by: Chaitanya Tata (cherry picked from commit eea8f67b912c6374b717f4052107fca5e312ec38) --- drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c b/drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c index fdbad39dd70f..29fadd940302 100644 --- a/drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c +++ b/drivers/wifi/nrfwifi/src/qspi/src/qspi_if.c @@ -119,6 +119,14 @@ BUILD_ASSERT(QSPI_IF_DEVICE_FREQUENCY >= (NRF_QSPI_BASE_CLOCK_FREQ / 16), #error "Unsupported base clock divider for wake-up frequency." #endif +/* After the base clock divider is changed, some time is needed for the new + * setting to take effect. This value specifies the delay (in microseconds) + * to be applied to ensure that the clock is ready when the QSPI operation + * starts. It was measured with a logic analyzer (unfortunately, the nRF5340 + * specification does not provide any numbers in this regard). + */ +#define BASE_CLOCK_SWITCH_DELAY_US 7 + #else /* * On nRF52 Series SoCs, the base clock divider is not configurable, @@ -374,6 +382,7 @@ static inline void qspi_lock(const struct device *dev) */ #if defined(CONFIG_SOC_SERIES_NRF53X) nrf_clock_hfclk192m_div_set(NRF_CLOCK, BASE_CLOCK_DIV); + k_busy_wait(BASE_CLOCK_SWITCH_DELAY_US); #endif } @@ -383,6 +392,7 @@ static inline void qspi_unlock(const struct device *dev) /* Restore the default base clock divider to reduce power consumption. */ nrf_clock_hfclk192m_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_4); + k_busy_wait(BASE_CLOCK_SWITCH_DELAY_US); #endif #ifdef CONFIG_MULTITHREADING @@ -673,6 +683,7 @@ static inline void qspi_fill_init_struct(nrfx_qspi_config_t *initstruct) /* Configure physical interface */ initstruct->phy_if.sck_freq = INST_0_SCK_CFG; + /* Using MHZ fails checkpatch constant check */ if (QSPI_IF_DEVICE_FREQUENCY >= 16000000) { qspi_cfg->qspi_slave_latency = 1; @@ -708,6 +719,7 @@ static int qspi_nrfx_configure(const struct device *dev) * divider. */ nrf_clock_hfclk192m_div_set(NRF_CLOCK, BASE_CLOCK_DIV); + k_busy_wait(BASE_CLOCK_SWITCH_DELAY_US); #endif nrfx_err_t res = _nrfx_qspi_init(&QSPIconfig, qspi_handler, dev_data); @@ -715,6 +727,7 @@ static int qspi_nrfx_configure(const struct device *dev) #if defined(CONFIG_SOC_SERIES_NRF53X) /* Restore the default /4 divider after the QSPI initialization. */ nrf_clock_hfclk192m_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_4); + k_busy_wait(BASE_CLOCK_SWITCH_DELAY_US); #endif int ret = qspi_get_zephyr_ret_code(res); From b287462e32a055425d2ba525ad97d3ad79085030 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 7 Aug 2024 20:10:30 +0530 Subject: [PATCH 48/51] [nrf fromtree] maintainers: Add colloborators for nRF Wi-Fi Colloborators for both Wi-Fi native driver and Wi-Fi driver in hal_nordic. Signed-off-by: Chaitanya Tata (cherry picked from commit aef70cb94f4978e26f9fff48b24d4036e1dc2fc0) --- MAINTAINERS.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index 46ce64ffa507..6c70b8877702 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -1956,6 +1956,8 @@ Release Notes: maintainers: - krish2718 - jukkar + collaborators: + - sachinthegreen files: - drivers/wifi/nrfwifi/ - dts/bindings/wifi/nordic,nrf70.yaml @@ -4336,6 +4338,11 @@ West: collaborators: - hubertmis - nordic-krch + - krish2718 + - sachinthegreen + - udaynordic + - rajb9 + - srkanordic files: - modules/hal_nordic/ labels: From 12762de15e17094f0a8c1d9cba4f90ec7c84bd00 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Sun, 18 Aug 2024 23:59:23 +0530 Subject: [PATCH 49/51] [nrf fromtree] drivers: wifi: Fix duplicate file inclusion If Wi-Fi management is enabled, then independent of modes scan functionality is always included. Signed-off-by: Chaitanya Tata (cherry picked from commit 464cc9e1613d26f3cdd17d4c41e3c1272547a289) --- drivers/wifi/nrfwifi/CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/wifi/nrfwifi/CMakeLists.txt b/drivers/wifi/nrfwifi/CMakeLists.txt index 4ba57b417ac4..22a75f2495d2 100644 --- a/drivers/wifi/nrfwifi/CMakeLists.txt +++ b/drivers/wifi/nrfwifi/CMakeLists.txt @@ -80,10 +80,6 @@ zephyr_library_sources_ifdef(CONFIG_NRF70_SYSTEM_MODE src/wifi_mgmt.c ) -zephyr_library_sources_ifdef(CONFIG_NRF70_SCAN_ONLY - src/wifi_mgmt_scan.c -) - zephyr_library_sources_ifdef(CONFIG_NRF70_RADIO_TEST ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/radio_test/fmac_api.c ${OS_AGNOSTIC_BASE}/fw_if/umac_if/src/fmac_util.c From 085a078855fa75993bfdf47f4b3434e2d51ee1c9 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Mon, 19 Aug 2024 00:12:17 +0530 Subject: [PATCH 50/51] [nrf fromtree] drivers: wifi: Fix monitor mode handling Monitor mode relies on core Wi-Fi management functions, so, the file should be included for both system and system with raw modes. Fix this by adding a hidden symbol. Signed-off-by: Chaitanya Tata (cherry picked from commit 56055ac4a8bc88d127fdd202d25dbdca83d2429c) --- drivers/wifi/nrfwifi/CMakeLists.txt | 2 +- drivers/wifi/nrfwifi/Kconfig.nrfwifi | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/wifi/nrfwifi/CMakeLists.txt b/drivers/wifi/nrfwifi/CMakeLists.txt index 22a75f2495d2..34c4062cd4b1 100644 --- a/drivers/wifi/nrfwifi/CMakeLists.txt +++ b/drivers/wifi/nrfwifi/CMakeLists.txt @@ -76,7 +76,7 @@ zephyr_library_sources_ifdef(CONFIG_NET_L2_WIFI_MGMT src/wifi_mgmt_scan.c ) -zephyr_library_sources_ifdef(CONFIG_NRF70_SYSTEM_MODE +zephyr_library_sources_ifdef(CONFIG_NRF70_SYSTEM_MODE_COMMON src/wifi_mgmt.c ) diff --git a/drivers/wifi/nrfwifi/Kconfig.nrfwifi b/drivers/wifi/nrfwifi/Kconfig.nrfwifi index 03109ed26dd9..592954bf28e9 100644 --- a/drivers/wifi/nrfwifi/Kconfig.nrfwifi +++ b/drivers/wifi/nrfwifi/Kconfig.nrfwifi @@ -78,6 +78,10 @@ config NRF70_SYSTEM_WITH_RAW_MODES endchoice +config NRF70_SYSTEM_MODE_COMMON + bool + default y if NRF70_SYSTEM_MODE || NRF70_SYSTEM_WITH_RAW_MODES + config NET_L2_ETHERNET default y if !NRF70_RADIO_TEST From 19c96284767cd67016c830fd7c8eb9a9bf0b9afe Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Mon, 19 Aug 2024 00:23:47 +0530 Subject: [PATCH 51/51] [nrf fromtree] drivers: wifi: Fix AP mode build Pass the AP mode configuration based on Zephyr's Kconfig to the OS agnostic code. Signed-off-by: Chaitanya Tata (cherry picked from commit 22a1846e8e7a482de33e283cb714482a8a5664ec) --- drivers/wifi/nrfwifi/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/wifi/nrfwifi/CMakeLists.txt b/drivers/wifi/nrfwifi/CMakeLists.txt index 34c4062cd4b1..3d1ea0ba37b7 100644 --- a/drivers/wifi/nrfwifi/CMakeLists.txt +++ b/drivers/wifi/nrfwifi/CMakeLists.txt @@ -240,6 +240,10 @@ zephyr_compile_definitions_ifdef(CONFIG_NRF70_LOG_VERBOSE -DNRF70_LOG_VERBOSE ) +zephyr_compile_definitions_ifdef(CONFIG_NRF70_AP_MODE + -DNRF70_AP_MODE +) + zephyr_compile_definitions( -DNRF70_RX_NUM_BUFS=${CONFIG_NRF70_RX_NUM_BUFS} -DNRF70_MAX_TX_TOKENS=${CONFIG_NRF70_MAX_TX_TOKENS}