From a66c6f738e03706d8780558665be6a7ede0bb5a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Ba=C5=82ys?= Date: Thu, 17 Nov 2022 20:15:25 +0100 Subject: [PATCH] [nrfconnect] Initial Matter over WiFi implementation. (#23607) * [nrfconnect] Initial Matter over WiFi implementation. Some WiFi related features might not work as expected yet. This will be fixed when the Zephyr NRF WiFi module is improved. All implementations have been synchronized to sdk-nrf v2.1.1. Commits included: - Prepare configuration for hci_rpmsg - Adapt Android CHIPTool guide to Wi-Fi devices Make the guide a little bit more aware of the world of Wi-Fi devices. - Disable Wi-Fi low-power mode Noticed Wi-Fi low-power mode causes performance issues, at least with some APs. - Implemented the WiFiNetworkIterator. Replaced dummy implementation of WiFiNetworkIterator This fixes an issue with infinite loop when using this iterator in Matter core. - Added including CHIPMemString.h to fix WiFi build Signed-off-by: Marcin Kajor Signed-off-by: Damian Krolik Signed-off-by: Kamil Kasperczyk Signed-off-by: Arkadiusz Balys * Restyled by prettier-markdown Restyled by gn Signed-off-by: Marcin Kajor Signed-off-by: Damian Krolik Signed-off-by: Kamil Kasperczyk Signed-off-by: Arkadiusz Balys Co-authored-by: Restyled.io --- config/nrfconnect/chip-module/CMakeLists.txt | 7 +- .../nrfconnect/chip-module/Kconfig.defaults | 64 ++- .../nrfconnect/chip-module/Kconfig.features | 25 + .../chip-module/Kconfig.hci_rpmsg.defaults | 99 ++++ .../chip-module/Kconfig.hci_rpmsg.root | 21 + config/zephyr/Kconfig | 2 +- .../images/CHIPTool_device_commissioned.jpg | Bin 15897 -> 0 bytes .../images/CHIPTool_device_commissioned.png | Bin 0 -> 15713 bytes .../nrfconnect_android_commissioning.md | 119 ++--- .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 3 - .../nrfconnect/main/AppTask.cpp | 2 + .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 3 - examples/chef/nrfconnect/main.cpp | 7 +- .../nrfconnect/main/AppTask.cpp | 4 + .../lighting-app/nrfconnect/main/AppTask.cpp | 4 + .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 3 - examples/lock-app/nrfconnect/main/AppTask.cpp | 4 + examples/pump-app/nrfconnect/main/AppTask.cpp | 4 + .../nrfconnect/main/AppTask.cpp | 4 + .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 3 - .../window-app/nrfconnect/main/AppTask.cpp | 4 + .../window-app/nrfconnect/prj_no_dfu.conf | 1 + .../window-app/nrfconnect/prj_release.conf | 1 + .../internal/GenericConnectivityManagerImpl.h | 2 +- src/inet/InetConfig.h | 22 + src/inet/UDPEndPointImplSockets.cpp | 9 +- src/platform/Zephyr/BLEManagerImpl.cpp | 1 + .../Zephyr/ConfigurationManagerImpl.cpp | 18 + .../Zephyr/ConfigurationManagerImpl.h | 5 - .../Zephyr/DiagnosticDataProviderImpl.cpp | 7 +- .../Zephyr/DiagnosticDataProviderImpl.h | 3 +- .../DiagnosticDataProviderImplGetter.cpp | 29 ++ src/platform/Zephyr/InetUtils.cpp | 41 ++ src/platform/Zephyr/InetUtils.h | 32 ++ src/platform/nrfconnect/BUILD.gn | 15 + .../nrfconnect/CHIPDevicePlatformConfig.h | 11 +- .../nrfconnect/ConnectivityManagerImpl.cpp | 4 +- .../nrfconnect/ConnectivityManagerImpl.h | 12 +- .../DiagnosticDataProviderImplNrf.cpp | 136 ++++++ .../DiagnosticDataProviderImplNrf.h | 60 +++ .../wifi/ConnectivityManagerImplWiFi.cpp | 228 +++++++++ .../wifi/ConnectivityManagerImplWiFi.h | 82 ++++ .../nrfconnect/wifi/NrfWiFiDriver.cpp | 284 +++++++++++ src/platform/nrfconnect/wifi/NrfWiFiDriver.h | 123 +++++ src/platform/nrfconnect/wifi/WiFiManager.cpp | 447 ++++++++++++++++++ src/platform/nrfconnect/wifi/WiFiManager.h | 171 +++++++ src/platform/telink/BUILD.gn | 1 + 47 files changed, 2029 insertions(+), 98 deletions(-) create mode 100644 config/nrfconnect/chip-module/Kconfig.hci_rpmsg.defaults create mode 100644 config/nrfconnect/chip-module/Kconfig.hci_rpmsg.root delete mode 100644 docs/guides/images/CHIPTool_device_commissioned.jpg create mode 100644 docs/guides/images/CHIPTool_device_commissioned.png create mode 100644 src/platform/Zephyr/DiagnosticDataProviderImplGetter.cpp create mode 100644 src/platform/Zephyr/InetUtils.cpp create mode 100644 src/platform/Zephyr/InetUtils.h create mode 100644 src/platform/nrfconnect/DiagnosticDataProviderImplNrf.cpp create mode 100644 src/platform/nrfconnect/DiagnosticDataProviderImplNrf.h create mode 100644 src/platform/nrfconnect/wifi/ConnectivityManagerImplWiFi.cpp create mode 100644 src/platform/nrfconnect/wifi/ConnectivityManagerImplWiFi.h create mode 100644 src/platform/nrfconnect/wifi/NrfWiFiDriver.cpp create mode 100644 src/platform/nrfconnect/wifi/NrfWiFiDriver.h create mode 100644 src/platform/nrfconnect/wifi/WiFiManager.cpp create mode 100644 src/platform/nrfconnect/wifi/WiFiManager.h diff --git a/config/nrfconnect/chip-module/CMakeLists.txt b/config/nrfconnect/chip-module/CMakeLists.txt index 305edd21e9c2e3..c27f4bfe55e2e6 100644 --- a/config/nrfconnect/chip-module/CMakeLists.txt +++ b/config/nrfconnect/chip-module/CMakeLists.txt @@ -219,6 +219,7 @@ chip_gn_arg_bool ("chip_progress_logging" CONFIG_MATTER_LOG_LE chip_gn_arg_bool ("chip_detail_logging" CONFIG_MATTER_LOG_LEVEL GREATER_EQUAL 4) chip_gn_arg_bool ("chip_automation_logging" "false") chip_gn_arg_bool ("chip_malloc_sys_heap" CONFIG_CHIP_MALLOC_SYS_HEAP) +chip_gn_arg_bool ("chip_enable_wifi" CONFIG_WIFI_NRF700X) if (CONFIG_CHIP_FACTORY_DATA) chip_gn_arg_bool ("chip_use_transitional_commissionable_data_provider" "false") @@ -232,8 +233,12 @@ if (CONFIG_CHIP_ROTATING_DEVICE_ID) chip_gn_arg_bool("chip_enable_additional_data_advertising" "true") endif() -if (CONFIG_CHIP_ENABLE_DNSSD_SRP) +if (CONFIG_NET_L2_OPENTHREAD) chip_gn_arg_string("chip_mdns" "platform") +elseif(CONFIG_WIFI_NRF700X) + chip_gn_arg_string("chip_mdns" "minimal") +else() + chip_gn_arg_string("chip_mdns" "none") endif() if (CONFIG_CHIP_CRYPTO_PSA) diff --git a/config/nrfconnect/chip-module/Kconfig.defaults b/config/nrfconnect/chip-module/Kconfig.defaults index 228dbaeb478089..e02d0d2981cc71 100644 --- a/config/nrfconnect/chip-module/Kconfig.defaults +++ b/config/nrfconnect/chip-module/Kconfig.defaults @@ -213,20 +213,72 @@ config NVS_LOOKUP_CACHE_SIZE int default 512 +# Enable OpenThread + +config NET_L2_OPENTHREAD + bool + default y if !WIFI_NRF700X + +if NET_L2_OPENTHREAD + # Increase the default RX stack size config IEEE802154_NRF5_RX_STACK_SIZE int default 1024 -# Enable OpenThread +endif -config NET_L2_OPENTHREAD +if CHIP_WIFI + +config NRF_WIFI_LOW_POWER bool - default y + default n + +config MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG + bool + default n + +config SYSTEM_WORKQUEUE_STACK_SIZE + int + default 1120 + +# align these numbers to match the OpenThread config +config NET_IF_UNICAST_IPV6_ADDR_COUNT + int + default 6 + +config NET_IF_MCAST_IPV6_ADDR_COUNT + int + default 8 + +config NET_SOCKETS_POSIX_NAMES + bool + default n + +config MBEDTLS_SSL_OUT_CONTENT_LEN + int + default 900 + +# options managed by IP4/IP6 simultaneous support +# aligned here to match OpenThread config +config NET_MAX_ROUTERS + int + default 1 + +config NET_MAX_CONN + int + default 4 + +config SHELL_STACK_SIZE + int + default 2616 + +config HEAP_MEM_POOL_SIZE + int + default 200000 + +endif -choice OPENTHREAD_STACK_VERSION - default OPENTHREAD_THREAD_VERSION_1_3 -endchoice # Enable mbedTLS from nrf_security library diff --git a/config/nrfconnect/chip-module/Kconfig.features b/config/nrfconnect/chip-module/Kconfig.features index 1f12053658f23e..6ff4aa5ea62527 100644 --- a/config/nrfconnect/chip-module/Kconfig.features +++ b/config/nrfconnect/chip-module/Kconfig.features @@ -19,6 +19,31 @@ if CHIP +config CHIP_WIFI + bool "Enable nrfconnect Wi-Fi support" + default y if SHIELD_NRF7002_EK || BOARD_NRF7002DK_NRF5340_CPUAPP + select WIFI_NRF700X + select WIFI + select WPA_SUPP + imply FLASH + imply SETTINGS + imply NVS + imply FLASH_MAP + imply NORDIC_SECURITY_BACKEND + imply MBEDTLS_ENTROPY_C + imply MBEDTLS_PSA_CRYPTO_C + imply NET_STATISTICS + imply NET_L2_ETHERNET + imply NET_PKT_TXTIME + imply NET_PKT_TIMESTAMP + imply MBEDTLS_PROMPTLESS + imply BUILD_OUTPUT_META + imply USE_DT_CODE_PARTITION # might be removed when the OTA is enabled + imply NET_IPV6_ND # enable Neighbor Discovery to handle Router Advertisements + imply NET_IPV6_NBR_CACHE + imply NET_STATISTICS_IPV6 + imply NET_STATISTICS_USER_API + config CHIP_QSPI_NOR bool "Enable QSPI NOR feature set" help diff --git a/config/nrfconnect/chip-module/Kconfig.hci_rpmsg.defaults b/config/nrfconnect/chip-module/Kconfig.hci_rpmsg.defaults new file mode 100644 index 00000000000000..8069d710047338 --- /dev/null +++ b/config/nrfconnect/chip-module/Kconfig.hci_rpmsg.defaults @@ -0,0 +1,99 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# The purpose of this file is to define new default values of settings used when building hci_rpmsg child image for Matter samples. + +config LOG + bool + default n + +config HEAP_MEM_POOL_SIZE + int + default 8192 + +config MAIN_STACK_SIZE + int + default 2048 + +config SYSTEM_WORKQUEUE_STACK_SIZE + int + default 2048 + +config BT + bool + default y + +config BT_HCI_RAW + bool + default y + +config BT_MAX_CONN + int + default 1 + +config BT_PERIPHERAL + bool + default y + +config BT_CENTRAL + bool + default n + +config BT_BUF_ACL_RX_SIZE + int + default 502 + +config BT_BUF_ACL_TX_SIZE + int + default 251 + +config BT_CTLR_DATA_LENGTH_MAX + int + default 251 + +config BT_CTLR_ASSERT_HANDLER + bool + default y + +config BT_HCI_RAW_RESERVE + int + default 1 + +# Workaround: Unable to allocate command buffer when using K_NO_WAIT since +# Host number of completed commands does not follow normal flow control. +config BT_BUF_CMD_TX_COUNT + int + default 10 + +config ASSERT + bool + default y + +config DEBUG_INFO + bool + default y + +config EXCEPTION_STACK_TRACE + bool + default y + +config IPC_SERVICE + bool + default y + +config MBOX + bool + default y diff --git a/config/nrfconnect/chip-module/Kconfig.hci_rpmsg.root b/config/nrfconnect/chip-module/Kconfig.hci_rpmsg.root new file mode 100644 index 00000000000000..8c4f6eee49cbc2 --- /dev/null +++ b/config/nrfconnect/chip-module/Kconfig.hci_rpmsg.root @@ -0,0 +1,21 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# The purpose of this file is to create a wrapper Kconfig file that will be set as +# hci_rpmsg_KCONFIG_ROOT and processed before any other Kconfig for hci_rpmsg child image. + +rsource "Kconfig.hci_rpmsg.defaults" +source "Kconfig.zephyr" diff --git a/config/zephyr/Kconfig b/config/zephyr/Kconfig index 9928bb35b49258..c7c5b356a160d9 100644 --- a/config/zephyr/Kconfig +++ b/config/zephyr/Kconfig @@ -112,7 +112,7 @@ config CHIP_ENABLE_PAIRING_AUTOSTART config CHIP_ENABLE_DNSSD_SRP bool "Enable support for service registration" - default y + default y if NET_L2_OPENTHREAD imply OPENTHREAD_ECDSA imply OPENTHREAD_SRP_CLIENT help diff --git a/docs/guides/images/CHIPTool_device_commissioned.jpg b/docs/guides/images/CHIPTool_device_commissioned.jpg deleted file mode 100644 index 85bbbdc2b8fefcefe2403852c3fa8d0afabf8b4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15897 zcmd_R2UJwg(l5IAWMIfS4>{+YbIv(~gdqndDM>&?k{~(foCPEr02L5H5XnJB6a*9z z5k!#i#(#L{d*40Zx#wGNy|v!Gw`bMv>gww1>gr$j-ZQ)BV)5b~K%%RuqX~c@2wcN` zfQvQYhQ>87cL31Q;RR3t06+ja5CV|kI8Gdu6hi@4TrP>D-$@f2i1l~n$7ltHV?6=_ z!!>a-ERF*I%KdSf_y8Ip1Eif@ zgD&&>d*QUJb!|7<_!j1O$54_8N;9Wff z{cz(q$LX{G=>v`f0EQr35CpKkzE}ZEQGuX950~ITS3xh=-}Co(Vg!N!Q`{`70s^@C z^TmCzIL;Cf#a)ANv*-c10KvH26%YixaM%CX_HP^iukv8*HC$`}KrP^MSfH1OXD~)o zSVV#!qk(f9V-_6XkHv6b#kvG}1!H9~e?!8{AM1wo2*hH8T(G_YSO0*990u#>?By$q z`90vE-wl=h|A`)tAjaI&D+q%_59V@UfV&s28(h)X%N6S%gmuG&_`6{PG0qsZK&*4H zS11;v7U1U>;2(rh2@VeQatR6c3h);&^K=fxs`z^OV1L6IW8r@}ATSu$SpQEe81CPW z2I0W#`j-_Pc7GcY3<&fP`m3uTA(t?LAZH<*0U=+khqJG1fE$)a@OLbJpaiG@8h{p{1Ly$;fC*s6?FuXa8^8{5;Pw}8fCu0O_;6Sf1cU%#9IC_saX12I4> za07?~;(=R00+0wK0V%+3AQeah(t!*h6UYK`fLtIC$Oj66BA^&30m^`KpaQ4_s(~7y z7N`U2fkvPSXa-t8k7O$KzUFRR0dT*HBbZ81hqk3 zP!BW!jX-126f_4dKr7G&v<2-!NAMEp47!3?&;#@Yy+L2l9}EDmfI(mg7z$nk!@)@K zIv5Scf^lFxm;fe$$>41;4NM0!!E7)W%mWL+BCrH311rEPum-FH8^9*88EggHzz(nz z>;`+m``|;c9~=aqfJ5L2I0}w|FThD~3Y-RK!Fg~ITn1l*Z^1S21GoWhg4^I{@C*1A z{04pp55b?{33v*gLjVW_0)rqScn|^zA%qx02BCmZL1-X!5Jm_ygayI|;ec>Mcp>}{ zL5K)M3?c!MhR8zXA&L+ch#Eu#q7Bi7=tGPkCJ-}-CBzzH3vqxrL7X9O5O;_d#0TOB zxeN(}gg~xB!XS~5>yTJT9OM=x5t0H)g``6=AvusdNFk&cQUff($~QK!zcskTJ*vyS;zC&&)uD`X#X2swhB zK+Yf+PzV$bMM3eQgisPFIg}Dg1Eq&DK`~G^C?}K$$`2KUia^DoQczi_0#pgA3e|vW zL-nABP!p&*)Dmg~wTC)EU7%Q~C)5Y(54{2nhF*n+L!+QE&>PTrXd*NPng-2)W<&F# zMbJ`c1+*Gk2W^D5K--}Apxw|u=tJlL^a*qr`W*TK`Vu+~or5kySD>rV_s|XK7W6Z8 z7rGBUg#Lv7f}X=5FgOeaBY+XZ$Y7K(8W;nN8O93ZfbqciVL~u5m?TUFrT|lhsll{h zx-bKn3CtX31+#-W!dzfjm?z8^761!^g~GyMQLq@;O;`df1(pWOgyp~rV8yUQnOa3(k_oCD4S7l4bv#o^L$dAKrM4Xy>(gB!t3;g)b)xFg&J?hf~Y`@yfk zL*QZXD0nRVCOi>-8=el&hUdeJ;pOmZcs;xseiwcZ-UELCAAmoFKZB3MU&3eL3-A^A zD*OX{6aE?g6@CE!2|q;u2p9r|KqE*H6bKpw1A+y?f#5+1AVd%n2pNPTLKUHj&_x&` z%n()xJA@O$72%2SMO;P%Bf=0-h*(5CA{mi}$VB8JiV$UpYD7Jv8PSgDLi8c}5l;{! zh;hVA#4KVF@fxv)*g$+j>>~CNM~GiY00~3lAqkOWNGc>fk{QX4wD z4XKSZK$;*ek+w)Dq$|=B>4&_63`IsDqmeg}Nyt=WCNd9Mge*taARCaa$a}~h}6bp(I#fK6?iKAptiYPUd zHp&2Hin2o4qnuIhC?C{iR0t{@6^*)yNzFs4%LimM|GnfpaxOHs4>(_ z)GTTVwTfCtZKHNk-%&qNXLwLN6doZS86GtrBOWUr7al*J2%Z$4Je~@kCZ0Z?37#dM z9iB6uJDv~T6}(Wq2)tOlTX-pW8F;yPMR*l>wRlZ#^p%I}4p*^7sp%-BQVJKlFVH{x+VLD+h zVF_V1VIyHXVGm(H;V|Ji;WXhA;VR)r!X3izgeOEG5t4|Ah?0n&h?R(kNQg+1NP$S5 zNSDZj$ePHB$eqZSD2OPWD3&OZD2*tGsFVgh0^Vp?JhF&D8Qu>`R^u^O>1u?ev?u@kX7u^(|TaRl)V;w0j9;(X#V;#%Ss z;!ffR#7~IFh+h#e60Z_}B>qBtNPJ2HCBY{lBcUb1kZ_R*kw}s#kf@XBlbDg%lDLp~ zkz6LZMiNbOizJOChopq0nxu*39?5-@CnV2Frbre^R!KHVc1eDaoRPvw(WDfl^rUR0 zyriO}GNdY`+N8#$R-{g(9;E)Hp`=ly@uaDw*`&p!)uc_N_edX*J|P_=ohDr(T_fEl z-6Q=;29Tl1h{>qQn8~=v1j!`H6v;Hn49P6W9LTU_eq@(SSvR~v-asqO4a(Z$$az1h~ayfE!a(!}ha(i+&a$oXb@<{TV z3sFl`t5EAwn^D_SW2ybAuTsZQCsAio7gN_zw^H{|4^ls;o~B-* zUZ?&-eMAG$;L(uL(9^Kf2+~N=DAVZBn9|tOU}^kmuF}NPB-3Qk6w}nw+@9TbWv1n!6{VG<)u1(`wWf8Z^`Q->y-u4zn?YMhTTR6hp~(0`#nVgMNk7$_N-8F(1P7!(+^7)%)K7_bbN8NwNEGNdu&GgLA(GjuaN zW*BFfV_0R_W;kFtXGAfQF)}c6F^VwCF={XxGukq`F$OS(F~%{bG3GN?GPW@GFg|8{ z!8p&j#`u}>kO^SIXQE_cX5wWMXHsO+VKQTKWb$MRVv1r)WXfVHVX9~9V0y&#jA@$b zHPa^3KGP{Pl9`N|ftib0lv$oxi`kUfp4o#rkU5e$fjN`8gt?BngZUBjDDyP)8|E$M z@66{I6owqbgyF%6W0Wwu7;}sh#v2oYiNT~`axoQ{W=s#}31$Mbi1~oo#T>K1ScqBZ zSU6aOS>#wWSxi{$Sv*(*S+27rv1GH9u{5%Du?(`jU|C>!&+>)kCo7DVn3ax|gH@PS zo>hz0l+}^di#3=vnl*(rm$ibmg|(OUDeFttW!8_Z-&jxCP;BIE%xt`D5^O4L`fS#0 zu51Bp;cU0qGT2Jk>e)Kk2H3{g=Goq}?Xn%S!`VsL8Q8hm#n_eD_1G=hUD*BE!`S25 zGuVsS>)AWm2iV8i7uY|r@3Nn8AUMc4m^gSiBsf$!3^;5!upE~;A~_N{vN_5*nmKwo zhB&4;UUO`6eCN2}B;cgxWaAX#l;hOmG~;yQ^x?e9d6P4pvzW7^9AQ3=Q`&e z=NT6s7bO=9mjIUxmnN4fmm`-qS14B;*B!1Rt~#zxu0gIBTuWRVT;I6PxbeBExY@Xc zxaGLDxy`vRar<$Hao^(3$-H^I)x7Pz zk9fy;7kJlszww^);qy`RvGa-WDe~#@S@U7}0{No(Zu1rJ)$n!j4e-6-TjKl3_njZ) zC*r5)=jNB-SLHY2x99ib59N>JPv0UAyOeGA$}oQAuS;bAs3;`Lf3^-gbIY}ggS+u2u%sS5&A52EQ}DQ z5M~t?5>^n_6Sfid5Dpf;A)GE;D%>pGC;Uu!PWXfHH{lBrLJ>LCj?M1ys!$cEAb405|+eHUNCq!S1 zeiHpDh7h9=V-ph*QxY=}vlsIgyC!x^EJv(LtX*tSY*OsC*k`e0ailnvIJ>x*xQe)u zxTCnQc!YS8c)ob8c$fH7@oDjQ;$OwjB+wFc5#lND50TNg7JpOZrHLOD0L?N!ChsNe)TQNUlllNnS`1Nijl-#P^ zSGjX}B6%ix0eJ;^19^LSU-?M+6!{|gCi(mFWAe-LpX84f@Dykjcobw5v=yusJQc1f zBr4=9)G72RJX2Uu*i<-DL@H7%aw$qFYAISNdMI91Oi;{ItW)e!e5SadxT$!ggjAwd z;!=`Q(o(Wg@=&^_l&F-iRIk*lG^(_uw5@ckjHgVi%&RP`tgCFR?4ul^oT6N;+^qai zc|!S(@)zZE6(SWT6(JQR6(f~PDwkDaRWej6RXS9js?4aYs~o7pRVh_DRV7umRIOA! zRj;WgsTQg>sy0Ww{-S-ML!!f~Bc`LF zW2xh*6Rwk@Q=-$VGpIAI^FilO7okg|%d0D=YoP0-dqww#Znkc%ZjbJB-4)$0x)*vR zdaQcldYXFHdR}@FdZ~J4dhL2o_2%?8^^W!N^%?X9^_BHa_1*MC^%M0A^_%tk^{4dT z>mL{(3}_Ab3=|BE44e&u4B`#)3>pj`8cZ6zGuStT8B!ba7|IzM7&;jS8s0R_HEb|^ zU^rp8YPfF%Gomu$F_JSfG`eIIXcTXhXVhr)&}h#mxT%(@t*M`B zjA^E6jcKpxxanKdZ)PwvDl;B4c{3w37qbwvM6)8ZR-M_ zU-M}5O!FG^Uh{GDx90m6a0?m>J_|()6AP@xHH#FBQj2zrVT(nJ&lcyFWR@J3(w2Ib zPL_d|@sk`p^c?hS5gYM%~8R#>XbwCd;PI=Dy9O&3l_4ws^LTwj#C~ zwl=oDwlTKZw)M6TZKrJ4ZGYMk*fHCQ*=gC?*#+3e+2z?a*$vpu*lpVVvM07@vzM~h zv%h2?Y@cXfY~N--WWQ*?;{ZBPI`B9sIG8wK9l{;b94Z~U9mXA29lkrF92p#i9n~Fe z9Q_<`IOaMwIu1C_IBq(gI*~YWILSB}I=MJqbxLt6cj|O{?)1iK{}SR7{UxDG>X)o9 z`CYnkDfd#-rGZPcm$omRIg>eaIm}g%U!|U)ZN29%01J)&i#@5 zwELF(nFpB%w}*mG(PM zh4`iTRrvM#P5Q0-o%oabbNMUyoB4bD$N1;^xA;HxU-I7zKm;%Zhy~~bTnY#cxE)Xx z&=)Wj@G;=*GWliR%gUFnF8f`+dAaa%`{ies-(EhrLU4ukiu4tuEACe!uVh_myfS!Y z;mU3xERa4>G*COxDKIoJHLyDHe&DOXt-y;Qsv!O#wIJJ|D?y1tWkKCR6G7`iC&8q_ z+`&r0mcf3(H-n3UJA$7FzY9JJAqwFPQ3x>)@d>#RQV`M>@+{rC*o&|aVZXx3!+FD1!)?Q_geQepg!hI|g>OXw z5!4Yv5n2&W5up)j5w#Kh5%Uqdk?=^yNQp>;NNi+OWKLvDlzEhI z)Xk{ksLrT~sEw$z>y+08u4`O(xE^vn^?J?q{_FGCccT%}Owm%&M$sP8(b0L)ZPBCA z@1l=mNMm?nRAOvn0%MY6s$w3*%*O1*!eSX?C1MR@-D9I;^J3d#M`PDwk8hCO;Ju-C z!|q1#joUYBZuH-nzwtE=8Hb6Ji8G1wj*E*citCJ}nS_($>c@n3JDZn4~wy=8XG_g4I^(px>ZrfzK~KoaN^ z#1jk>JQ89O3KBXJ#uL^P&Jw8;g%foWT@oV_a}rwu*1~{rdJ{Dsd`Ls%olzYG`VDYD4PN)HkU|X{2d=ub2;-Mi#UrXOFhdm>snS;R!i1s*88l} zZ0c;0Z2fHa?AYw0?C$KT?9Vyy988W}jz!MpoaCIEoWY#soWoq=T;5!bT&LXd+??FD z+_Bt^JRpxgPa@Aa&nNFzUPa!6y!pIu`RIJEeARr1{Hyty`7QaQ`5*Gn3up_(3ycc9 z3*rmP3mz2A7kn#37jhM<7CIJ&6=oOSEgUP{C<2NYilmB6i~Ncbi>iwTik6EGi%E<5 zi?xbfi?0_K6n7O*6@M;4mavv6l-QI6m!y|8m5h|UFF7lvEfp^{F7+u*D6K5*FI_G@ zEF&%BFVik_EsHKID(fzrF54}~E9WRzDYq}bR-RqnRz6<7SpltJu8^&;tO%?~t7xnk zsaUHxtE8=zs5Ghct4yk_seD}drt-LovPz`NpvtQ%zN(_?QPon_VKr&BK($UawmP=D zwEBMaeD!_}Q4Md6R*h>-bWKrBZ_RAYUM;$or&goZx%PT(VQqKqOzqb?f;#Rx^}0)S zQFR4%-F4G-U+W3#x$D*Io$I6O3+ub+PDXyu!=~2^i(@`@;vv9LPvv+esb9M9M=C{qiT4-7% zT1;C4T5h*Aw2ZWTXa!msTV-3VT7z3NTkp2MXx+YxxXX4|`L5&Lh`afByY9~1-D@Lk z<89M!!?xXMD{Fhyw%m5qPT4NnZrtwIp4?vFKHUDH1L$Dtkn6DN2<^!3XzzI0v2zdq z9@jmMdoK54?v>tqaBu0}k4}nC(N3dIzs{7-`p%Kg^)5&kbC*JwT~}CFZdYg5bk|-t zQ8$0LPPa#Qe0Np%avAuD<6}5>q8PFPVABZ2Q9(XdaHV6zd4=N7Y4@L|Y4)zT$4jw(Gd@TOh>~Y}ZjK^({Uq0S_ zLimLLiQW_MCrMB0pFDf=@hSW%`%|^2E>B~hmOmYQy885dh;c}M$ZjZNsBoxnXmRLh zn0i=p*kU+jID7cs@bvKh2C`0Dt@3+5L}FC1T7e^L6P|Ha!E=Mzj5iW80#*C$FR`X}B_oKG@ODor{~ zMo*Sa4ot32UcAJ-RDOBsWz5U+mychrO@UJ^Q>s%gQ#Yn6r=CuIcm;jM{!0B7_Er3= znpY#QK29U2xu&(IJ*N|=8>XL6f11Ic;hWK)@tsMXX_=Xv*_|bx6`nPoy)v6Q+c7&c zdoV{aCoyL^cXcjru6J%}?qr^BUT)rgK5D*neqere0a##JP+f3cxVccXFtV_@h+5=X z)LryhyuH}6IJx+BiDXG+$#f}bDSN4FX@2SFGVQYLvfXmza_REG^1Bsqg>^-J1-o)< zrGDl4%BR-^uLWKkz7BYu@w(&n?CZlfRBxo;*u05&Q~aj?&FWk5E$dtLx7fD{Z|mQV zz1>+QToqb1UJYE$UhP_4SUrA6_fGzu&Q{o(rC4RC{PLu12Z zBWa^)V`5|PBiTpskCq?9J{Em^^l^0)vdO-wx#_i;ve~jZwYk4Vu_d)-vlX#bx;41< zVH>{9y{)_Lw|!^3V|#Y{=o9TH`A?3YVn0=X8u_&Snc%bFXOquCpL0L=eSZDEhmv7f__sVYeZqM%W?%7w&SGBM1Uz5Hz ze|`CNe~)rcde3g}`d-D}@ZQ!pf^UM~Ouhww%m4P^+uMC`pM76@-)BE0#($!QrFBcRyf1xPIvU2>6lt zqx;9wkFz6|BlRQCquWPqM>9u9Kk0ue{&e|y>u2N7$)DejDUW529gbsz7djVy7nv8`7b_PRf1WGBod$sZJ`qs0008EZ0H8ks z00o7(<3qT!KE(DHE4VlJcnBm4iNHf4QTX_H1cX$?glIG&Jq0BR6^42WMVhDFA0R*797v{fD2>q2{a0C>DAW^tlbrJxCLO>)Q0uF&g;lC9? z2oy#NN03oav6yF)Q;MjVn3`3zi@OD7Rjsi~I9~}AdyHfgRgGm2$>8X?_i7Jkg$`#N z0{uq^I4ig!435Or@si@|Ko|gn!~a$QNg+@&7896=$`v@dXhl1#m~(8#-o-M2#@T?7 zLP>EaB2G_%@Ra7i9R8nX)M3!Mtan27vVQu#Z*t#QrDUS_1G_bi4J;BnT7C7hT@NA^ zbv?vH#$T0wKH%C?8U8lP_1qpxTvh5m`>J|bm6gv#tT?$S{AAMJwRA^O9m_EMW?@D# z>!sCpaARF+3v@pmUqGks?o-#~+RBQFX4l1nX)f+utl2Io{3oAOGSogjX1Jr{o-^$n)OYCJY|+8@%5;38?bB&%&5bfX#)P?9 zHH*Z#4qK^_1&JV&BfGo5LV51ymL=uq=a)UYzWKg?)H3?$K`S4jxQvumwuFs>mWmUj z_mk|jq>1F3f~GE4i~ixyvJ3Bn#gBPzy1a55L47J*+DP58#ZKtYb=ZjAjXo9*SxgOO z{n6jwQCTywI9I3ES!_uj--*YVQG(QzqXAxIWp&DaUQ zE{&6d1c3wnPMSee`-H_8HFN-9#{f}t{g@W39z#ely1GhB^wZ8{k6q;%CwgViNz#MA z%ca)m8=lvy7zS9i{#Ve2w%$>Sy5+F&2pFng6KUVj>uw2gK6Xv#c`wyXiQz&#o&mA8~p#hs^(P1ZaI4l z4sI`9!VFLJUGxR-AX|q#lBo=MXZT2I(k#?9Ce3#nj*_sfYl|Up^+gMpIV;`tXR31^ zylEY+Z@RYv0<5VATOPOb7Hf30Sd!Z$@OvlDHdqQq{SvbXSL&mRgtVLG-hNAJ>xPu=0m1dSA|MQi?b<39A?OEI3vw@qB4>EQMYtlEx$uz9Q z_YEj`+g1BA?rW&~2Q7FwsR|5E{h{?gTt*hwXx!PdX@>QscUd%?C`2SH()FH8iu+ht zZ`{AmzC2K;gUC2IHi~uY%-`iO(M!}mX0OXFJMAi&(2}r{o%dVmX^0?n=gImhDI~@@ zwD-xoz&h7w9XtOf@3YY>!^1^OMkn=U-eqad8ozrk5#$oxTRqqTWf7D2G;f)iOxCRB zyR3(@@5EnBj-O#3QlIo6Loa^W+uFA`?;%_~5 z+O_+#egA;DVy)K?L1|Fs+}Spx7P&K8y?k7RC7Xi7K~pnXaf2WsZb-@KPF93iVrz#m>f4PjiJ zOc!RlUV1C^_u$?XcI@`p(}C@EYeX*lgPd>+<%DwRqXX z#2sdloZ&3be@2n-o%=_L3s%0+K86Rpn=ot&C9_R+HEHQ!GaXHuMVOt6o!+>6zHM~@ zjMaAD(bCaVBd@HwCG8sYhP7L~gRi*9;%v_F$Z$#lnWumQE>>yCXuPfL*D%-lcT24% z++6t3TA_m^W$4*Rw<8z2{L_r)fb>Va#DXQ2+snxE{q1GJlJfBXR`HMZ0pVSn6|}H2 z8Ek9{u}GCH4e~>580}Bx0!b5{^f^l7Dt~6G)(|eqDSz_|Kmhqyj&})FyrR-)cG}7C zXT}l3`d91!ju9e~X*07v2e`aRpXpe$3+v)8&MydG+b3!n`%lu87XFRxwbRD`U-kZP z4?%7AX}yVwX&TIYIXR<(`+wM5{O zX;%$lq5AG9Tc0y&^<<=f!ix8{qM`JDFNt^%={b znr4$9kRL(3_Mg)nDKrQ2w)?Id)^v?oH#RyJ%GE{)Sv}`ZZn2W+>*<=oe(l(N;h)J< z7n?(*p4;9Wydst%MjGw2(?)$e=T6Y_GuFNo57rQ#17oqAH1EX9hH$>m*3Wd>9eiId zKYKkh-+U+#JN>rAcHUt}vvtVs{x8c5pqx6UE;Ia=CEt3_jQ%AS&E#uc$Nyp{URb5@)QS2xyoD%XFF}TPsF%B0nbm{fFB>nV5ES4J}`rmRgaif0Od7 zWRQWqsy63EaJuG(i%nz8mRj}Hu)Gh$tF9fVAAN!ei_jY;rqzZ?JRh`Q_XGun^4_eh z(RRQ$8_DSN|M+poLM}%7nT`<|s}|Z%Jy3@KZRh8+q0a0X?MyvbKzOjs#Ql;O2FuI> zkBxcMykqg{n+hJD>5vjkDu+Yg@dLxbe-aX$h|p0d0yaPyMe>k)UvH7)XVX^vJKP?} z$)Ws06*0{AddTNq>*@t?y}Auf)od0(9!6+g`<8QGNSVU69rvDjUBad4TG8kuz{Go4 z@|#rpT(aN$0M-Lgqlsc==&hP1Pfyarl&SyCnyNkZq)m9TRTWX178>&AS5#8W@7Vr~ zyqzk}=Pxa)_1+5BG{|4+3E=H})axU^*s{p$%dC~|LPXNe=Vbtfo;;&{5@D6w~n zXz)Xg*;mpl<^M(TXPTyVUh0?d&@TU|n18jIm0hoU)2ypl(8#S zn4K1Fm3#g%M!*Cc(Dw9ObVq?5OXJw8bHe)z04rL~e`6FNz;pnRQT`a9g8&2Y-#U+5 z^y(*%A{nGVA$G3)X>g#-{-=eJ4cX{VFV6`Nc$?$9&CY!_ z^_6=)<@p-TwUKvkc{0pT;J5k*I#5#b*OzPQZcZy!@($aCakhVn^zG!I6n1&`hazSxX}G2`@YurShLZSl*0M0(e6VI(UOU~g_YYy?0zm> zwl)^gy<~)n+R>_Y$5OBKAJ;u6-oib^aGr3}{W(So8@nChQ>UF;^SQs{Wz6DoxBgb9 z&GtvlK>eVc#_>(-Z#kl7&!`2D%=IL4CAH{S4;;cL52pkS81^Jgn%F4oo92RLjP!V0 z#M=~-_z5kSjwS+mS%u1(g0gi-_}Fr$6+TVs&=riXZ}+eG%IlkQi8PM72d8Dcsm^Fr!I!Z1YhNL-VN))}Y8ll5^&^KrT01bNENSYVGyO)TPFS&`LkU z)ENCucwxAC5;MJn+=q<2>Kco4PG+6b*=9QQrZRNTElgz?k_rzECwf@c4zlz!UJ4X> z7jWRd*?F*5)($zQmENl%qO5$*d>q_3Iv3=oDa8FK@6!d4E`Zys&X8?0k(?(iF;WLi zPYw2pwqmsZh!mJM7CljRO21{EyJU2H?*fR6!cXTr*1dl}`PPj7s15WUxlF;*ZrA*; zaLJ4XlF}MadU}pjPA&cvI*HI8mrAF`bM-pY*{AX^t1{VrKNqiOQ19KJzi(IfL*Yg& zCWOg#cRBPC)tR7`UBJ0NQeLpqyWvwAukL(@hO70u{%p^e?u{g-FuV9=Tc$iElCyn>OkG(`q}<*Zkj#4k7H-k{-hH*zp7tj)M5R;ci(WXF)TUV zH+TY5Gs!$eN(DOyfMBZG( z+(P|+Fpqz3>-gIE&$6d%&oq@9Yn28h451npM)4{PGYWS)TCrJsm3LK?GV;Qr@yS3( zvmpOlY1v+0AL^J)D9_pUMZXHIsf><49EisQo(x^rbl5oMmZ80xw^xAyng3`+^oL9v z&|1g7b$Sx_ry+p!__tE|AF`VW-$B8LouNKL+4wVYV|eP{f)<^%Wl>!tq-$>fRa diff --git a/docs/guides/images/CHIPTool_device_commissioned.png b/docs/guides/images/CHIPTool_device_commissioned.png new file mode 100644 index 0000000000000000000000000000000000000000..8756b4dbf6b0ca3fb98fe2cacb87ca7ebc733c59 GIT binary patch literal 15713 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 literal 0 HcmV?d00001 diff --git a/docs/guides/nrfconnect_android_commissioning.md b/docs/guides/nrfconnect_android_commissioning.md index 7bbc0e3e7aef21..0e2146ba980b8f 100644 --- a/docs/guides/nrfconnect_android_commissioning.md +++ b/docs/guides/nrfconnect_android_commissioning.md @@ -1,13 +1,14 @@ # Commissioning nRF Connect Accessory using Android CHIPTool You can use [CHIPTool](android_building.md) for Android smartphones to -commission a Nordic Semiconductor device running an nRF Connect platform example -onto a Matter-enabled Thread network. +commission a Nordic Semiconductor's development kit programmed with a Matter +example for the nRF Connect platform into a Matter fabric. -This guide references the nRF52840 DK and the door lock example application -based on the nRF Connect platform, but the instructions are also valid for the -nRF Connect lighting example application and can be adapted to other platforms -and applications as well. +This guide references the nRF52840 DK and Matter nRF Connect Lighting Example +Application that communicates with other nodes over a Thread network, but the +instructions can be adapted to other platforms and applications. In particular, +some sections of this guide include deviations from the original procedure that +are needed to test a Wi-Fi device.
@@ -30,8 +31,8 @@ The commissioning process is composed of the following main stages: 1. CHIPTool discovers a Matter accessory device over Bluetooth LE. 2. CHIPTool establishes a secure channel to the device over Bluetooth LE, and - sends Matter operational credentials and Thread provisioning data. -3. The accessory device joins a Matter-enabled Thread network. + sends Matter operational credentials and Thread or Wi-Fi credentials. +3. The accessory device joins the operational IPv6 network. CHIPTool uses both Bluetooth LE and the IPv6 connectivity. Bluetooth LE is used only during the commissioning phase. Afterwards, only the IPv6 connectivity @@ -41,13 +42,13 @@ commissioning process and CHIPTool must use DNS Service Discovery (DNS-SD) to learn or refresh the address before the controller initiates the IPv6-based communication. -Since a typical smartphone does not have a Thread radio built-in, extra effort -is needed to prepare the fully-fledged testing environment that includes a -Thread Border Router configured on a Raspberry Pi. +Since a typical smartphone does not have a Thread radio built-in, preparing the +fully-fledged testing environment for Matter over Thread requires a Thread +Border Router configured on a Raspberry Pi. The following diagram shows the connectivity between network components required -to allow communication between devices running the CHIPTool and Lock -applications: +to allow communication between devices running CHIPTool and Matter nRF Connect +Lighting Example Application: ![Matter nodes connectivity](./images/nrfconnect_android_connectivity.png) @@ -60,20 +61,26 @@ applications: You need the following hardware and software for commissioning the nRF Connect accessory using Android CHIPTool: -- Two nRF52840 DK (PCA10056) +- 1x smartphone with Android 8+ +- 1x Wi-Fi Access Point supporting IPv6 (without the IPv6 Router Advertisement + Guard enabled on the router) +- 1x nRF52840 DK (PCA10056) for running the example application. You can + replace this DK with another compatible device, such as the nRF5340 DK or + nRF7002 DK. nRF52840 DK and nRF5340 DK can be used to test Matter over + Thread, and nRF7002 DK can be used to test Matter over Wi-Fi. +- 1x nRF52840 DK for running the + [OpenThread Radio Co-Processor](https://openthread.io/platforms/co-processor) + firmware. You can replace this DK with another compatible device, such as + the nRF52840 Dongle. - - One nRF52840 DK is needed for running the - [OpenThread Radio Co-Processor](https://openthread.io/platforms/co-processor) - firmware. You can replace this DK with another compatible device, such - as the nRF52840 Dongle. - - One nRF52840 DK is needed for running the example application. You can - replace this DK with another compatible device, such as the nRF5340 DK. + > _Note:_ This piece of hardware is only needed if you're testing a Thread + > device. Skip it if the tested device operates in a Wi-Fi network. -- Smartphone compatible with Android 8.0 or later -- Raspberry Pi Model 3B+ or newer (along with an SD card with at least 8 GB of - memory) -- Wi-Fi Access Point supporting IPv6 (without the IPv6 Router Advertisement - Guard enabled on the router) +- 1x Raspberry Pi Model 3B+ or newer (along with an SD card with at least 8 GB + of memory) + + > _Note:_ This piece of hardware is only needed if you're testing a Thread + > device. Skip it if the tested device operates in a Wi-Fi network.
@@ -81,10 +88,15 @@ accessory using Android CHIPTool: ## Setting up Thread Border Router +> _Note:_ This step is only needed if you're testing a Thread device. Skip it if +> the tested device operates in a Wi-Fi network. + Follow the [OpenThread Border Router](openthread_border_router_pi.md) article to set up OpenThread Border Router on the Raspberry Pi, with either the nRF52840 DK or the nRF52840 Dongle acting as the [OpenThread Radio Co-Processor](https://openthread.io/platforms/co-processor). +During the setup, make sure that the Raspberry Pi is connected to your Wi-Fi +Access Point.
@@ -94,8 +106,8 @@ or the nRF52840 Dongle acting as the Build and program the example application onto your compatible device. -For this guide, see the documentation for the door lock example application to -learn how to build and program the example onto an nRF52840 DK. +For this guide, see the documentation of Matter nRF Connect Lighting Example +Application to learn how to build and program the example onto an nRF52840 DK.
@@ -164,24 +176,24 @@ To prepare the accessory device for commissioning, complete the following steps: ## Commissioning accessory device -To commission the accessory device onto the Thread network created in the -[Setting up Thread Border Router](#setting-up-thread-border-router) section, -complete the following steps: - -1. Enable **Bluetooth** and **Location** services on your smartphone. -2. Connect the smartphone to the same Wi-Fi network as the Raspberry Pi which - runs OpenThread Border Router. -3. Open the CHIPTool application on your smartphone. -4. Tap the **PROVISION CHIP DEVICE WITH THREAD** button and scan the - commissioning QR code. Several notifications will appear, informing you of - commissioning progress with scanning, connection, and pairing. At the end of - this process, the Thread network settings screen appears. -5. In the Thread network settings screen, use the default settings and tap the - **SAVE NETWORK** button to send a Thread provisioning message to the - accessory device. - -You will see the "Network provisioning completed" message when the accessory -device successfully joins the Thread network. +To commission the accessory device into the Matter fabric, complete the +following steps: + +1. Enable **Bluetooth** and **Location** services on your smartphone. +2. Connect the smartphone to your Wi-Fi Access Point. +3. Open the CHIPTool application on your smartphone. +4. Depending on your testing scenario, tap one of the following buttons and + scan the commissioning QR code: + + - **PROVISION CHIP DEVICE WITH THREAD** for Matter over Thread + - **PROVISION CHIP DEVICE WITH WI-FI** for Matter over Wi-Fi + + The network credentials screen appears. + +5. In the network credentials screen, specify parameters of network and tap the + **SAVE NETWORK** button. Several notifications appear, informing you of the + progress of scanning, connecting, and pairing with the device. At the end of + this process, the application returns to the main menu.
@@ -195,21 +207,16 @@ Check the IPv6 connectivity with the device using the following steps: 1. Tap **LIGHT ON/OFF & LEVEL CLUSTER**. The following screen appears: - ![CHIPTool device control screen](./images/CHIPTool_device_commissioned.jpg) + ![CHIPTool device control screen](./images/CHIPTool_device_commissioned.png) The two textboxes at the top contain **Fabric ID** and **Node ID** of the last commissioned device. -2. Tap **UPDATE ADDRESS** to learn or refresh the IPv6 address of the device. - CHIPTool will use a built-in DNS-SD client to resolve **Fabric ID** and - **Node ID** of the device to its IPv6 address. The result of the operation, - be it the address or an error message, will be displayed at the bottom of the - screen. -3. Tap the following buttons to change the lock state of the nRF Connect door - lock example application referenced in this guide: +2. Tap the following buttons to change the lighting state of the Matter nRF + Connect Lighting Example Application referenced in this guide: - - **ON** and **OFF** buttons lock and unlock the door, respectively. - - **TOGGLE** changes the lock state to the opposite. + - **ON** and **OFF** buttons turn on and off the light, respectively. + - **TOGGLE** changes the lighting state to the opposite. -The **LED 2** on the device turns on or off based on the changes of the lock +The **LED 2** on the device turns on or off based on the changes of the lighting state. diff --git a/examples/all-clusters-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay b/examples/all-clusters-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay index b8030f42c1def9..4b2eca8e4cd66b 100644 --- a/examples/all-clusters-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay +++ b/examples/all-clusters-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -25,9 +25,6 @@ &adc { status = "disabled"; }; -&gpio1 { - status = "disabled"; -}; &i2c1 { status = "disabled"; }; diff --git a/examples/all-clusters-app/nrfconnect/main/AppTask.cpp b/examples/all-clusters-app/nrfconnect/main/AppTask.cpp index 60519e928c6632..40b987b620332d 100644 --- a/examples/all-clusters-app/nrfconnect/main/AppTask.cpp +++ b/examples/all-clusters-app/nrfconnect/main/AppTask.cpp @@ -142,6 +142,7 @@ CHIP_ERROR AppTask::Init() return err; } +#if defined(CONFIG_NET_L2_OPENTHREAD) err = ThreadStackMgr().InitThreadStack(); if (err != CHIP_NO_ERROR) { @@ -159,6 +160,7 @@ CHIP_ERROR AppTask::Init() LOG_ERR("ConnectivityMgr().SetThreadDeviceType() failed"); return err; } +#endif // Initialize LEDs LEDWidget::InitGpio(); diff --git a/examples/all-clusters-minimal-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay b/examples/all-clusters-minimal-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay index b8030f42c1def9..4b2eca8e4cd66b 100644 --- a/examples/all-clusters-minimal-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay +++ b/examples/all-clusters-minimal-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -25,9 +25,6 @@ &adc { status = "disabled"; }; -&gpio1 { - status = "disabled"; -}; &i2c1 { status = "disabled"; }; diff --git a/examples/chef/nrfconnect/main.cpp b/examples/chef/nrfconnect/main.cpp index fe031e7145ff35..8c3061945838d7 100644 --- a/examples/chef/nrfconnect/main.cpp +++ b/examples/chef/nrfconnect/main.cpp @@ -79,7 +79,8 @@ int main() #if CHIP_DEVICE_CONFIG_ENABLE_WPA ConnectivityManagerImpl().StartWiFiManagement(); #endif -#if CHIP_ENABLE_OPENTHREAD + +#if defined(CHIP_ENABLE_OPENTHREAD) err = ThreadStackMgr().InitThreadStack(); if (err != CHIP_NO_ERROR) { @@ -97,7 +98,9 @@ int main() ChipLogError(AppServer, "ConnectivityMgr().SetThreadDeviceType() failed"); return 1; } -#endif /* CHIP_ENABLE_OPENTHREAD */ +#elif !defined(CONFIG_WIFI_NRF700X) + return CHIP_ERROR_INTERNAL; +#endif // Device Attestation & Onboarding codes chip::Credentials::SetDeviceAttestationCredentialsProvider(chip::Credentials::Examples::GetExampleDACProvider()); diff --git a/examples/light-switch-app/nrfconnect/main/AppTask.cpp b/examples/light-switch-app/nrfconnect/main/AppTask.cpp index dd388276964660..4eee5a57bfef8d 100644 --- a/examples/light-switch-app/nrfconnect/main/AppTask.cpp +++ b/examples/light-switch-app/nrfconnect/main/AppTask.cpp @@ -113,6 +113,7 @@ CHIP_ERROR AppTask::Init() return err; } +#if defined(CONFIG_NET_L2_OPENTHREAD) err = ThreadStackMgr().InitThreadStack(); if (err != CHIP_NO_ERROR) { @@ -130,6 +131,9 @@ CHIP_ERROR AppTask::Init() LOG_ERR("ConnectivityMgr().SetThreadDeviceType() failed: %s", ErrorStr(err)); return err; } +#elif !defined(CONFIG_WIFI_NRF700X) + return CHIP_ERROR_INTERNAL; +#endif LightSwitch::GetInstance().Init(kLightDimmerSwitchEndpointId, kLightGenericSwitchEndpointId); diff --git a/examples/lighting-app/nrfconnect/main/AppTask.cpp b/examples/lighting-app/nrfconnect/main/AppTask.cpp index c43a7c16334382..1ee75869d3ddfc 100644 --- a/examples/lighting-app/nrfconnect/main/AppTask.cpp +++ b/examples/lighting-app/nrfconnect/main/AppTask.cpp @@ -124,6 +124,7 @@ CHIP_ERROR AppTask::Init() return err; } +#if defined(CONFIG_NET_L2_OPENTHREAD) err = ThreadStackMgr().InitThreadStack(); if (err != CHIP_NO_ERROR) { @@ -138,6 +139,9 @@ CHIP_ERROR AppTask::Init() LOG_ERR("ConnectivityMgr().SetThreadDeviceType() failed"); return err; } +#elif !defined(CONFIG_WIFI_NRF700X) + return CHIP_ERROR_INTERNAL; +#endif // Initialize LEDs LEDWidget::InitGpio(); diff --git a/examples/lock-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay b/examples/lock-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay index 5ae48f5ba4b0da..d1d93c9dbf033d 100644 --- a/examples/lock-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay +++ b/examples/lock-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -25,9 +25,6 @@ &adc { status = "disabled"; }; -&gpio1 { - status = "disabled"; -}; &i2c1 { status = "disabled"; }; diff --git a/examples/lock-app/nrfconnect/main/AppTask.cpp b/examples/lock-app/nrfconnect/main/AppTask.cpp index cb46530b93df85..b7d4d02a42e75c 100644 --- a/examples/lock-app/nrfconnect/main/AppTask.cpp +++ b/examples/lock-app/nrfconnect/main/AppTask.cpp @@ -103,6 +103,7 @@ CHIP_ERROR AppTask::Init() return err; } +#if defined(CONFIG_NET_L2_OPENTHREAD) err = ThreadStackMgr().InitThreadStack(); if (err != CHIP_NO_ERROR) { @@ -120,6 +121,9 @@ CHIP_ERROR AppTask::Init() LOG_ERR("ConnectivityMgr().SetThreadDeviceType() failed"); return err; } +#else + return CHIP_ERROR_INTERNAL; +#endif // Initialize LEDs LEDWidget::InitGpio(); diff --git a/examples/pump-app/nrfconnect/main/AppTask.cpp b/examples/pump-app/nrfconnect/main/AppTask.cpp index 7ab470249da53b..d8ccc47f393f53 100644 --- a/examples/pump-app/nrfconnect/main/AppTask.cpp +++ b/examples/pump-app/nrfconnect/main/AppTask.cpp @@ -104,6 +104,7 @@ CHIP_ERROR AppTask::Init() return err; } +#if defined(CONFIG_NET_L2_OPENTHREAD) err = ThreadStackMgr().InitThreadStack(); if (err != CHIP_NO_ERROR) { @@ -117,6 +118,9 @@ CHIP_ERROR AppTask::Init() LOG_ERR("ConnectivityMgr().SetThreadDeviceType() failed"); return err; } +#elif !defined(CONFIG_WIFI_NRF700X) + return CHIP_ERROR_INTERNAL; +#endif // Initialize LEDs LEDWidget::InitGpio(); diff --git a/examples/pump-controller-app/nrfconnect/main/AppTask.cpp b/examples/pump-controller-app/nrfconnect/main/AppTask.cpp index f473d0fa94534e..ea4bf106e0c437 100644 --- a/examples/pump-controller-app/nrfconnect/main/AppTask.cpp +++ b/examples/pump-controller-app/nrfconnect/main/AppTask.cpp @@ -101,6 +101,7 @@ CHIP_ERROR AppTask::Init() return err; } +#if defined(CONFIG_NET_L2_OPENTHREAD) err = ThreadStackMgr().InitThreadStack(); if (err != CHIP_NO_ERROR) { @@ -114,6 +115,9 @@ CHIP_ERROR AppTask::Init() LOG_ERR("ConnectivityMgr().SetThreadDeviceType() failed"); return err; } +#elif !defined(CONFIG_WIFI_NRF700X) + return CHIP_ERROR_INTERNAL; +#endif // Initialize LEDs LEDWidget::InitGpio(); diff --git a/examples/window-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay b/examples/window-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay index 5310e26238da64..f4559366c7df5c 100644 --- a/examples/window-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay +++ b/examples/window-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -45,9 +45,6 @@ &adc { status = "disabled"; }; -&gpio1 { - status = "disabled"; -}; &i2c1 { status = "disabled"; }; diff --git a/examples/window-app/nrfconnect/main/AppTask.cpp b/examples/window-app/nrfconnect/main/AppTask.cpp index 77ab1af1a4960c..08df914781835b 100644 --- a/examples/window-app/nrfconnect/main/AppTask.cpp +++ b/examples/window-app/nrfconnect/main/AppTask.cpp @@ -105,6 +105,7 @@ CHIP_ERROR AppTask::Init() return err; } +#if defined(CONFIG_NET_L2_OPENTHREAD) err = ThreadStackMgr().InitThreadStack(); if (err != CHIP_NO_ERROR) { @@ -125,6 +126,9 @@ CHIP_ERROR AppTask::Init() LOG_ERR("ConnectivityMgr().SetThreadDeviceType() failed"); return err; } +#elif !defined(CONFIG_WIFI_NRF700X) + return CHIP_ERROR_INTERNAL; +#endif // Initialize LEDs LEDWidget::InitGpio(); diff --git a/examples/window-app/nrfconnect/prj_no_dfu.conf b/examples/window-app/nrfconnect/prj_no_dfu.conf index 1a8816e44557af..f2787a7619fe73 100644 --- a/examples/window-app/nrfconnect/prj_no_dfu.conf +++ b/examples/window-app/nrfconnect/prj_no_dfu.conf @@ -23,6 +23,7 @@ CONFIG_STD_CPP14=y # Add support for LEDs and buttons on Nordic development kits CONFIG_DK_LIBRARY=y +CONFIG_PWM=y # OpenThread configs CONFIG_OPENTHREAD_NORDIC_LIBRARY_MTD=y diff --git a/examples/window-app/nrfconnect/prj_release.conf b/examples/window-app/nrfconnect/prj_release.conf index 0a1e7735a8455e..caf0ee1a5be1e9 100644 --- a/examples/window-app/nrfconnect/prj_release.conf +++ b/examples/window-app/nrfconnect/prj_release.conf @@ -23,6 +23,7 @@ CONFIG_STD_CPP14=y # Add support for LEDs and buttons on Nordic development kits CONFIG_DK_LIBRARY=y +CONFIG_PWM=y # OpenThread configs CONFIG_OPENTHREAD_NORDIC_LIBRARY_MTD=y diff --git a/src/include/platform/internal/GenericConnectivityManagerImpl.h b/src/include/platform/internal/GenericConnectivityManagerImpl.h index 6315c25a701b51..a56e92d17d0d8d 100644 --- a/src/include/platform/internal/GenericConnectivityManagerImpl.h +++ b/src/include/platform/internal/GenericConnectivityManagerImpl.h @@ -33,7 +33,7 @@ namespace Internal { * * This template contains implementations of select features from the ConnectivityManager abstract * interface that are suitable for use on all platforms. It is intended to be inherited (directly - * or indirectly) by the ConfigurationManagerImpl class, which also appears as the template's ImplClass + * or indirectly) by the ConnectivityManagerImpl class, which also appears as the template's ImplClass * parameter. */ template diff --git a/src/inet/InetConfig.h b/src/inet/InetConfig.h index d209328fde87a5..0631a7e14f77da 100644 --- a/src/inet/InetConfig.h +++ b/src/inet/InetConfig.h @@ -250,4 +250,26 @@ #ifndef INET_CONFIG_IP_MULTICAST_HOP_LIMIT #define INET_CONFIG_IP_MULTICAST_HOP_LIMIT (64) #endif // INET_CONFIG_IP_MULTICAST_HOP_LIMIT + +/** + * @def INET_CONFIG_UDP_SOCKET_PKTINFO + * + * @brief + * Use IP_PKTINFO and IPV6_PKTINFO control messages to specify the network + * interface and the source address of a sent UDP packet. + * + * @details + * When this flag is set, the socket-based implementation of UDP endpoints + * requires that IP_PKTINFO and IPV6_PKTINFO be supported. Otherwise, it is + * left to the operating system to select the network interface and the + * source address. + */ +#ifndef INET_CONFIG_UDP_SOCKET_PKTINFO +#ifndef __ZEPHYR__ +#define INET_CONFIG_UDP_SOCKET_PKTINFO 1 +#else +#define INET_CONFIG_UDP_SOCKET_PKTINFO 0 +#endif +#endif // INET_CONFIG_UDP_SOCKET_PKTINFO + // clang-format on diff --git a/src/inet/UDPEndPointImplSockets.cpp b/src/inet/UDPEndPointImplSockets.cpp index cd12fe42fb64d3..6c85906bb7a9ed 100644 --- a/src/inet/UDPEndPointImplSockets.cpp +++ b/src/inet/UDPEndPointImplSockets.cpp @@ -66,7 +66,7 @@ #define INET_IPV6_ADD_MEMBERSHIP IPV6_ADD_MEMBERSHIP #elif defined(IPV6_JOIN_GROUP) #define INET_IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP -#elif !__ZEPHYR__ +#elif !CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API #error \ "Neither IPV6_ADD_MEMBERSHIP nor IPV6_JOIN_GROUP are defined which are required for generalized IPv6 multicast group support." #endif // IPV6_ADD_MEMBERSHIP @@ -75,7 +75,7 @@ #define INET_IPV6_DROP_MEMBERSHIP IPV6_DROP_MEMBERSHIP #elif defined(IPV6_LEAVE_GROUP) #define INET_IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP -#elif !__ZEPHYR__ +#elif !CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API #error \ "Neither IPV6_DROP_MEMBERSHIP nor IPV6_LEAVE_GROUP are defined which are required for generalized IPv6 multicast group support." #endif // IPV6_DROP_MEMBERSHIP @@ -337,6 +337,7 @@ CHIP_ERROR UDPEndPointImplSockets::SendMsgImpl(const IPPacketInfo * aPktInfo, Sy intf = mBoundIntfId; } +#if INET_CONFIG_UDP_SOCKET_PKTINFO // If the packet should be sent over a specific interface, or with a specific source // address, construct an IP_PKTINFO/IPV6_PKTINFO "control message" to that effect // add add it to the message header. If the local OS doesn't support IP_PKTINFO/IPV6_PKTINFO @@ -401,6 +402,7 @@ CHIP_ERROR UDPEndPointImplSockets::SendMsgImpl(const IPPacketInfo * aPktInfo, Sy return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; #endif // !(defined(IP_PKTINFO) && defined(IPV6_PKTINFO)) } +#endif // INET_CONFIG_UDP_SOCKET_PKTINFO // Send IP packet. const ssize_t lenSent = sendmsg(mSocket, &msgHeader, 0); @@ -565,7 +567,8 @@ void UDPEndPointImplSockets::HandlePendingIO(System::SocketEvents events) System::PacketBufferHandle lBuffer; lPacketInfo.Clear(); - lPacketInfo.DestPort = mBoundPort; + lPacketInfo.DestPort = mBoundPort; + lPacketInfo.Interface = mBoundIntfId; lBuffer = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSizeWithoutReserve, 0); diff --git a/src/platform/Zephyr/BLEManagerImpl.cpp b/src/platform/Zephyr/BLEManagerImpl.cpp index d65b7a903e5e34..88edbd3da061f5 100644 --- a/src/platform/Zephyr/BLEManagerImpl.cpp +++ b/src/platform/Zephyr/BLEManagerImpl.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include diff --git a/src/platform/Zephyr/ConfigurationManagerImpl.cpp b/src/platform/Zephyr/ConfigurationManagerImpl.cpp index 107579afbfbb03..378e35e1006fe3 100644 --- a/src/platform/Zephyr/ConfigurationManagerImpl.cpp +++ b/src/platform/Zephyr/ConfigurationManagerImpl.cpp @@ -27,8 +27,11 @@ #include #include + #include +#include "InetUtils.h" + #include #include @@ -201,6 +204,21 @@ void ConfigurationManagerImpl::DoFactoryReset(intptr_t arg) PlatformMgr().Shutdown(); } +CHIP_ERROR ConfigurationManagerImpl::GetPrimaryWiFiMACAddress(uint8_t * buf) +{ +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI + const net_if * const iface = InetUtils::GetInterface(); + VerifyOrReturnError(iface != nullptr && iface->if_dev != nullptr, CHIP_ERROR_INTERNAL); + + const auto linkAddrStruct = iface->if_dev->link_addr; + memcpy(buf, linkAddrStruct.addr, linkAddrStruct.len); + + return CHIP_NO_ERROR; +#else + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +#endif +} + ConfigurationManager & ConfigurationMgrImpl() { return ConfigurationManagerImpl::GetDefaultInstance(); diff --git a/src/platform/Zephyr/ConfigurationManagerImpl.h b/src/platform/Zephyr/ConfigurationManagerImpl.h index 166237fba32d81..87f9e30335b8fc 100644 --- a/src/platform/Zephyr/ConfigurationManagerImpl.h +++ b/src/platform/Zephyr/ConfigurationManagerImpl.h @@ -94,11 +94,6 @@ inline CHIP_ERROR ConfigurationManagerImpl::WritePersistedStorageValue(::chip::P return Internal::ZephyrConfig::WriteConfigValueCounter(key, value); } -inline CHIP_ERROR ConfigurationManagerImpl::GetPrimaryWiFiMACAddress(uint8_t * /* buf */) -{ - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; -} - /** * Returns the platform-specific implementation of the ConfigurationManager object. * diff --git a/src/platform/Zephyr/DiagnosticDataProviderImpl.cpp b/src/platform/Zephyr/DiagnosticDataProviderImpl.cpp index 1eb24394f27be7..c1fb842e054e1d 100644 --- a/src/platform/Zephyr/DiagnosticDataProviderImpl.cpp +++ b/src/platform/Zephyr/DiagnosticDataProviderImpl.cpp @@ -116,7 +116,7 @@ DiagnosticDataProviderImpl & DiagnosticDataProviderImpl::GetDefaultInstance() return sInstance; } -inline DiagnosticDataProviderImpl::DiagnosticDataProviderImpl() : mBootReason(DetermineBootReason()) +DiagnosticDataProviderImpl::DiagnosticDataProviderImpl() : mBootReason(DetermineBootReason()) { ChipLogDetail(DeviceLayer, "Boot reason: %u", static_cast(mBootReason)); } @@ -328,10 +328,5 @@ void DiagnosticDataProviderImpl::ReleaseNetworkInterfaces(NetworkInterface * net } } -DiagnosticDataProvider & GetDiagnosticDataProviderImpl() -{ - return DiagnosticDataProviderImpl::GetDefaultInstance(); -} - } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/Zephyr/DiagnosticDataProviderImpl.h b/src/platform/Zephyr/DiagnosticDataProviderImpl.h index 2b46051d27b125..69bbae5bf04471 100644 --- a/src/platform/Zephyr/DiagnosticDataProviderImpl.h +++ b/src/platform/Zephyr/DiagnosticDataProviderImpl.h @@ -52,9 +52,10 @@ class DiagnosticDataProviderImpl : public DiagnosticDataProvider CHIP_ERROR GetNetworkInterfaces(NetworkInterface ** netifpp) override; void ReleaseNetworkInterfaces(NetworkInterface * netifp) override; -private: +protected: DiagnosticDataProviderImpl(); +private: const BootReasonType mBootReason; }; diff --git a/src/platform/Zephyr/DiagnosticDataProviderImplGetter.cpp b/src/platform/Zephyr/DiagnosticDataProviderImplGetter.cpp new file mode 100644 index 00000000000000..6b920d1661ffc3 --- /dev/null +++ b/src/platform/Zephyr/DiagnosticDataProviderImplGetter.cpp @@ -0,0 +1,29 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DiagnosticDataProviderImpl.h" + +namespace chip { +namespace DeviceLayer { + +DiagnosticDataProvider & GetDiagnosticDataProviderImpl() +{ + return DiagnosticDataProviderImpl::GetDefaultInstance(); +} + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/Zephyr/InetUtils.cpp b/src/platform/Zephyr/InetUtils.cpp new file mode 100644 index 00000000000000..1169cf6362ad88 --- /dev/null +++ b/src/platform/Zephyr/InetUtils.cpp @@ -0,0 +1,41 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InetUtils.h" + +namespace chip { +namespace DeviceLayer { +namespace InetUtils { + +in6_addr ToZephyrAddr(const chip::Inet::IPAddress & address) +{ + in6_addr zephyrAddr; + + static_assert(sizeof(zephyrAddr.s6_addr) == sizeof(address.Addr), "Unexpected address size"); + memcpy(zephyrAddr.s6_addr, address.Addr, sizeof(address.Addr)); + + return zephyrAddr; +} + +net_if * GetInterface(chip::Inet::InterfaceId ifaceId) +{ + return ifaceId.IsPresent() ? net_if_get_by_index(ifaceId.GetPlatformInterface()) : net_if_get_default(); +} + +} // namespace InetUtils +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/Zephyr/InetUtils.h b/src/platform/Zephyr/InetUtils.h new file mode 100644 index 00000000000000..ad7c5e1dc9028f --- /dev/null +++ b/src/platform/Zephyr/InetUtils.h @@ -0,0 +1,32 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +struct in6_addr; +struct net_if; + +namespace chip { +namespace DeviceLayer { +namespace InetUtils { + +in6_addr ToZephyrAddr(const chip::Inet::IPAddress & address); +net_if * GetInterface(chip::Inet::InterfaceId ifaceId = chip::Inet::InterfaceId::Null()); + +} // namespace InetUtils +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/nrfconnect/BUILD.gn b/src/platform/nrfconnect/BUILD.gn index a841a2bb374020..186b45e4a54219 100644 --- a/src/platform/nrfconnect/BUILD.gn +++ b/src/platform/nrfconnect/BUILD.gn @@ -41,6 +41,8 @@ static_library("nrfconnect") { "ConfigurationManagerImpl.h", "ConnectivityManagerImpl.cpp", "ConnectivityManagerImpl.h", + "DiagnosticDataProviderImplNrf.cpp", + "DiagnosticDataProviderImplNrf.h", "ExternalFlashManager.h", "InetPlatformConfig.h", "KeyValueStoreManagerImpl.h", @@ -80,6 +82,19 @@ static_library("nrfconnect") { } } + if (chip_enable_wifi) { + sources += [ + "../Zephyr/InetUtils.cpp", + "../Zephyr/InetUtils.h", + "wifi/ConnectivityManagerImplWiFi.cpp", + "wifi/ConnectivityManagerImplWiFi.h", + "wifi/NrfWiFiDriver.cpp", + "wifi/NrfWiFiDriver.h", + "wifi/WiFiManager.cpp", + "wifi/WiFiManager.h", + ] + } + if (chip_enable_nfc) { sources += [ "../Zephyr/NFCManagerImpl.cpp", diff --git a/src/platform/nrfconnect/CHIPDevicePlatformConfig.h b/src/platform/nrfconnect/CHIPDevicePlatformConfig.h index 3813c860725a56..5a82f16ad4e9d8 100644 --- a/src/platform/nrfconnect/CHIPDevicePlatformConfig.h +++ b/src/platform/nrfconnect/CHIPDevicePlatformConfig.h @@ -87,10 +87,17 @@ #define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING CONFIG_CHIP_DEVICE_SOFTWARE_VERSION_STRING #endif +#define CHIP_DEVICE_CONFIG_ENABLE_THREAD CONFIG_NET_L2_OPENTHREAD + +#define CHIP_DEVICE_CONFIG_ENABLE_WIFI CONFIG_WIFI_NRF700X + +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI +#define CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION 1 +#define CHIP_DEVICE_CONFIG_ENABLE_WIFI_AP 0 +#else #define CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION 0 #define CHIP_DEVICE_CONFIG_ENABLE_WIFI_AP 0 - -#define CHIP_DEVICE_CONFIG_ENABLE_THREAD CONFIG_NET_L2_OPENTHREAD +#endif #define CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE CONFIG_BT diff --git a/src/platform/nrfconnect/ConnectivityManagerImpl.cpp b/src/platform/nrfconnect/ConnectivityManagerImpl.cpp index c5029f37a67862..504b0d0460ccde 100644 --- a/src/platform/nrfconnect/ConnectivityManagerImpl.cpp +++ b/src/platform/nrfconnect/ConnectivityManagerImpl.cpp @@ -51,7 +51,9 @@ CHIP_ERROR ConnectivityManagerImpl::_Init() #if CHIP_DEVICE_CONFIG_ENABLE_THREAD GenericConnectivityManagerImpl_Thread::_Init(); #endif - +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI + ReturnErrorOnFailure(InitWiFi()); +#endif return CHIP_NO_ERROR; } diff --git a/src/platform/nrfconnect/ConnectivityManagerImpl.h b/src/platform/nrfconnect/ConnectivityManagerImpl.h index 4c003f23a4861a..8ea6c2d76b6561 100644 --- a/src/platform/nrfconnect/ConnectivityManagerImpl.h +++ b/src/platform/nrfconnect/ConnectivityManagerImpl.h @@ -28,12 +28,18 @@ #else #include #endif + #if CHIP_DEVICE_CONFIG_ENABLE_THREAD #include #else #include #endif + +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI +#include "wifi/ConnectivityManagerImplWiFi.h" +#else #include +#endif #include @@ -65,7 +71,11 @@ class ConnectivityManagerImpl final : public ConnectivityManager, #else public Internal::GenericConnectivityManagerImpl_NoThread, #endif +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI + public ConnectivityManagerImplWiFi +#else public Internal::GenericConnectivityManagerImpl_NoWiFi +#endif { // Allow the ConnectivityManager interface class to delegate method calls to // the implementation methods provided by this class. @@ -100,7 +110,7 @@ inline ConnectivityManager & ConnectivityMgr(void) * Returns the platform-specific implementation of the ConnectivityManager singleton object. * * chip applications can use this to gain access to features of the ConnectivityManager - * that are specific to the ESP32 platform. + * that are specific to the nrfconnect platform. */ inline ConnectivityManagerImpl & ConnectivityMgrImpl(void) { diff --git a/src/platform/nrfconnect/DiagnosticDataProviderImplNrf.cpp b/src/platform/nrfconnect/DiagnosticDataProviderImplNrf.cpp new file mode 100644 index 00000000000000..5f7bc1a6702b4e --- /dev/null +++ b/src/platform/nrfconnect/DiagnosticDataProviderImplNrf.cpp @@ -0,0 +1,136 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * Provides an implementation of the DiagnosticDataProvider object + * for nrfconnect platform. + */ + +#include "DiagnosticDataProviderImplNrf.h" + +#ifdef CONFIG_WIFI_NRF700X +#include +#endif + +namespace chip { +namespace DeviceLayer { + +DiagnosticDataProvider & GetDiagnosticDataProviderImpl() +{ + return DiagnosticDataProviderImplNrf::GetDefaultInstance(); +} + +DiagnosticDataProviderImplNrf & DiagnosticDataProviderImplNrf::GetDefaultInstance() +{ + static DiagnosticDataProviderImplNrf sInstance; + return sInstance; +} + +#ifdef CONFIG_WIFI_NRF700X +CHIP_ERROR DiagnosticDataProviderImplNrf::GetWiFiBssId(ByteSpan & value) +{ + WiFiManager::WiFiInfo info; + CHIP_ERROR err = WiFiManager::Instance().GetWiFiInfo(info); + value = info.mBssId; + return err; +} + +CHIP_ERROR DiagnosticDataProviderImplNrf::GetWiFiSecurityType(uint8_t & securityType) +{ + WiFiManager::WiFiInfo info; + CHIP_ERROR err = WiFiManager::Instance().GetWiFiInfo(info); + securityType = info.mSecurityType; + return err; +} + +CHIP_ERROR DiagnosticDataProviderImplNrf::GetWiFiVersion(uint8_t & wiFiVersion) +{ + WiFiManager::WiFiInfo info; + CHIP_ERROR err = WiFiManager::Instance().GetWiFiInfo(info); + wiFiVersion = info.mWiFiVersion; + return err; +} + +CHIP_ERROR DiagnosticDataProviderImplNrf::GetWiFiChannelNumber(uint16_t & channelNumber) +{ + WiFiManager::WiFiInfo info; + CHIP_ERROR err = WiFiManager::Instance().GetWiFiInfo(info); + channelNumber = info.mChannel; + (void) err; + // above will return 0 until the wpa_supplicant driver API implementation is refined + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} + +CHIP_ERROR DiagnosticDataProviderImplNrf::GetWiFiRssi(int8_t & rssi) +{ + WiFiManager::WiFiInfo info; + CHIP_ERROR err = WiFiManager::Instance().GetWiFiInfo(info); + rssi = info.mRssi; + (void) err; + // above will return -128 until the wpa_supplicant driver API implementation is refined + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} + +// below will be implemented when the WiFi driver exposes Zephyr NET_STATISTICS API +CHIP_ERROR DiagnosticDataProviderImplNrf::GetWiFiBeaconLostCount(uint32_t & beaconLostCount) +{ + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} + +CHIP_ERROR DiagnosticDataProviderImplNrf::GetWiFiBeaconRxCount(uint32_t & beaconRxCount) +{ + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} + +CHIP_ERROR DiagnosticDataProviderImplNrf::GetWiFiPacketMulticastRxCount(uint32_t & packetMulticastRxCount) +{ + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} +CHIP_ERROR DiagnosticDataProviderImplNrf::GetWiFiPacketMulticastTxCount(uint32_t & packetMulticastTxCount) +{ + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} + +CHIP_ERROR DiagnosticDataProviderImplNrf::GetWiFiPacketUnicastRxCount(uint32_t & packetUnicastRxCount) +{ + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} + +CHIP_ERROR DiagnosticDataProviderImplNrf::GetWiFiPacketUnicastTxCount(uint32_t & packetUnicastTxCount) +{ + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} + +CHIP_ERROR DiagnosticDataProviderImplNrf::GetWiFiCurrentMaxRate(uint64_t & currentMaxRate) +{ + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} + +CHIP_ERROR DiagnosticDataProviderImplNrf::GetWiFiOverrunCount(uint64_t & overrunCount) +{ + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} + +CHIP_ERROR DiagnosticDataProviderImplNrf::ResetWiFiNetworkDiagnosticsCounts() +{ + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} +#endif + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/nrfconnect/DiagnosticDataProviderImplNrf.h b/src/platform/nrfconnect/DiagnosticDataProviderImplNrf.h new file mode 100644 index 00000000000000..3b770e627428e8 --- /dev/null +++ b/src/platform/nrfconnect/DiagnosticDataProviderImplNrf.h @@ -0,0 +1,60 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * Provides an implementation of the DiagnosticDataProvider object + * for nrfconnect platform. + */ + +#pragma once + +#include + +namespace chip { +namespace DeviceLayer { + +class DiagnosticDataProviderImplNrf : public DiagnosticDataProviderImpl +{ +public: +#ifdef CONFIG_WIFI_NRF700X + CHIP_ERROR GetWiFiBssId(ByteSpan & value) override; + CHIP_ERROR GetWiFiSecurityType(uint8_t & securityType) override; + CHIP_ERROR GetWiFiVersion(uint8_t & wiFiVersion) override; + CHIP_ERROR GetWiFiChannelNumber(uint16_t & channelNumber) override; + CHIP_ERROR GetWiFiRssi(int8_t & rssi) override; + CHIP_ERROR GetWiFiBeaconLostCount(uint32_t & beaconLostCount) override; + CHIP_ERROR GetWiFiBeaconRxCount(uint32_t & beaconRxCount) override; + CHIP_ERROR GetWiFiPacketMulticastRxCount(uint32_t & packetMulticastRxCount) override; + CHIP_ERROR GetWiFiPacketMulticastTxCount(uint32_t & packetMulticastTxCount) override; + CHIP_ERROR GetWiFiPacketUnicastRxCount(uint32_t & packetUnicastRxCount) override; + CHIP_ERROR GetWiFiPacketUnicastTxCount(uint32_t & packetUnicastTxCount) override; + CHIP_ERROR GetWiFiCurrentMaxRate(uint64_t & currentMaxRate) override; + CHIP_ERROR GetWiFiOverrunCount(uint64_t & overrunCount) override; + CHIP_ERROR ResetWiFiNetworkDiagnosticsCounts() override; +#endif + + static DiagnosticDataProviderImplNrf & GetDefaultInstance(); + +private: + DiagnosticDataProviderImplNrf() = default; +}; + +DiagnosticDataProvider & GetDiagnosticDataProviderImpl(); + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/nrfconnect/wifi/ConnectivityManagerImplWiFi.cpp b/src/platform/nrfconnect/wifi/ConnectivityManagerImplWiFi.cpp new file mode 100644 index 00000000000000..73e16303a03bc5 --- /dev/null +++ b/src/platform/nrfconnect/wifi/ConnectivityManagerImplWiFi.cpp @@ -0,0 +1,228 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* this file behaves like a config.h, comes first */ +#include + +#include +#include + +#include "ConnectivityManagerImplWiFi.h" +#include "WiFiManager.h" + +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI + +using namespace ::chip; +using namespace ::chip::Inet; +using namespace ::chip::System; +using namespace ::chip::TLV; + +namespace chip { +namespace DeviceLayer { + +CHIP_ERROR ConnectivityManagerImplWiFi::InitWiFi() +{ + return WiFiManager::Instance().Init(); +} + +ConnectivityManager::WiFiStationMode ConnectivityManagerImplWiFi::_GetWiFiStationMode(void) +{ + if (mStationMode != ConnectivityManager::WiFiStationMode::kWiFiStationMode_ApplicationControlled) + { + mStationMode = (WiFiManager::StationStatus::DISABLED == WiFiManager().Instance().GetStationStatus()) + ? ConnectivityManager::WiFiStationMode::kWiFiStationMode_Disabled + : ConnectivityManager::WiFiStationMode::kWiFiStationMode_Enabled; + } + return mStationMode; +} + +CHIP_ERROR ConnectivityManagerImplWiFi::_SetWiFiStationMode(ConnectivityManager::WiFiStationMode aMode) +{ + VerifyOrReturnError(ConnectivityManager::WiFiStationMode::kWiFiStationMode_NotSupported != aMode, + CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + + if (aMode != mStationMode) + { + mStationMode = aMode; + if (mStationMode != ConnectivityManager::WiFiStationMode::kWiFiStationMode_ApplicationControlled) + { + bool doEnable{ ConnectivityManager::WiFiStationMode::kWiFiStationMode_Enabled == mStationMode }; + // TODO: when the connection/disconnection callback API is provided + // below calls should be used as a base of disconnect callback + if (doEnable) + { + OnStationConnected(); + } + else + { + OnStationDisconnected(); + } + } + } + + return CHIP_NO_ERROR; +} + +bool ConnectivityManagerImplWiFi::_IsWiFiStationEnabled(void) +{ + return (WiFiManager::StationStatus::DISABLED <= WiFiManager().Instance().GetStationStatus()); +} + +bool ConnectivityManagerImplWiFi::_IsWiFiStationApplicationControlled(void) +{ + return (ConnectivityManager::WiFiStationMode::kWiFiStationMode_ApplicationControlled == mStationMode); +} + +bool ConnectivityManagerImplWiFi::_IsWiFiStationConnected(void) +{ + return (WiFiManager::StationStatus::FULLY_PROVISIONED == WiFiManager().Instance().GetStationStatus()); +} + +System::Clock::Timeout ConnectivityManagerImplWiFi::_GetWiFiStationReconnectInterval(void) +{ + return mWiFiStationReconnectInterval; +} + +CHIP_ERROR ConnectivityManagerImplWiFi::_SetWiFiStationReconnectInterval(System::Clock::Timeout val) +{ + mWiFiStationReconnectInterval = val; + return CHIP_NO_ERROR; +} + +bool ConnectivityManagerImplWiFi::_IsWiFiStationProvisioned(void) +{ + // from Matter perspective `provisioned` means that the supplicant has been provided + // with SSID and password (doesn't matter if valid or not) + return (WiFiManager::StationStatus::CONNECTING <= WiFiManager().Instance().GetStationStatus()); +} + +void ConnectivityManagerImplWiFi::_ClearWiFiStationProvision(void) +{ + if (_IsWiFiStationProvisioned()) + { + if (CHIP_NO_ERROR != WiFiManager().Instance().ClearStationProvisioningData()) + { + ChipLogError(DeviceLayer, "Cannot clear WiFi station provisioning data"); + } + } +} + +bool ConnectivityManagerImplWiFi::_CanStartWiFiScan() +{ + return (WiFiManager::StationStatus::DISABLED != WiFiManager().Instance().GetStationStatus() && + WiFiManager::StationStatus::SCANNING != WiFiManager().Instance().GetStationStatus() && + WiFiManager::StationStatus::CONNECTING != WiFiManager().Instance().GetStationStatus()); +} + +void ConnectivityManagerImplWiFi::_OnWiFiStationProvisionChange() +{ + // do nothing +} + +void ConnectivityManagerImplWiFi::_OnWiFiScanDone() {} + +CHIP_ERROR ConnectivityManagerImplWiFi::_GetAndLogWiFiStatsCounters(void) +{ + // TODO: when network statistics are enabled + return CHIP_NO_ERROR; +} + +void ConnectivityManagerImplWiFi::OnStationConnected() +{ + // ensure the station is connected + if (_IsWiFiStationConnected()) + { + ChipDeviceEvent connectEvent{}; + connectEvent.Type = DeviceEventType::kWiFiConnectivityChange; + connectEvent.WiFiConnectivityChange.Result = kConnectivity_Established; + PlatformMgr().PostEventOrDie(&connectEvent); + } + else + { + ChipLogError(DeviceLayer, "WiFi Station is not connected!"); + } +} + +void ConnectivityManagerImplWiFi::OnStationDisconnected() +{ + // ensure the station is disconnected + if (WiFiManager::StationStatus::DISCONNECTED == WiFiManager().Instance().GetStationStatus()) + { + ChipDeviceEvent disconnectEvent{}; + disconnectEvent.Type = DeviceEventType::kWiFiConnectivityChange; + disconnectEvent.WiFiConnectivityChange.Result = kConnectivity_Lost; + PlatformMgr().PostEventOrDie(&disconnectEvent); + } + else + { + ChipLogError(DeviceLayer, "WiFi Station is not disconnected!"); + } +} + +ConnectivityManager::WiFiAPMode ConnectivityManagerImplWiFi::_GetWiFiAPMode(void) +{ + /* AP mode is unsupported */ + return ConnectivityManager::WiFiAPMode::kWiFiAPMode_NotSupported; +} + +CHIP_ERROR ConnectivityManagerImplWiFi::_SetWiFiAPMode(ConnectivityManager::WiFiAPMode mode) +{ + /* AP mode is unsupported */ + VerifyOrReturnError(ConnectivityManager::WiFiAPMode::kWiFiAPMode_NotSupported == mode || + ConnectivityManager::WiFiAPMode::kWiFiAPMode_Disabled == mode, + CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + return CHIP_NO_ERROR; +} + +bool ConnectivityManagerImplWiFi::_IsWiFiAPActive(void) +{ + /* AP mode is unsupported */ + return false; +} + +bool ConnectivityManagerImplWiFi::_IsWiFiAPApplicationControlled(void) +{ + /* AP mode is unsupported */ + return false; +} + +void ConnectivityManagerImplWiFi::_DemandStartWiFiAP(void) +{ /* AP mode is unsupported */ +} + +void ConnectivityManagerImplWiFi::_StopOnDemandWiFiAP(void) +{ /* AP mode is unsupported */ +} + +void ConnectivityManagerImplWiFi::_MaintainOnDemandWiFiAP(void) +{ /* AP mode is unsupported */ +} + +System::Clock::Timeout ConnectivityManagerImplWiFi::_GetWiFiAPIdleTimeout(void) +{ + /* AP mode is unsupported */ + return System::Clock::kZero; +} + +void ConnectivityManagerImplWiFi::_SetWiFiAPIdleTimeout(System::Clock::Timeout val) +{ /* AP mode is unsupported */ +} + +} // namespace DeviceLayer +} // namespace chip + +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI diff --git a/src/platform/nrfconnect/wifi/ConnectivityManagerImplWiFi.h b/src/platform/nrfconnect/wifi/ConnectivityManagerImplWiFi.h new file mode 100644 index 00000000000000..8198453038417a --- /dev/null +++ b/src/platform/nrfconnect/wifi/ConnectivityManagerImplWiFi.h @@ -0,0 +1,82 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include + +#include + +namespace chip { +namespace Inet { +class IPAddress; +} // namespace Inet +} // namespace chip + +namespace chip { +namespace DeviceLayer { + +class ConnectivityManagerImplWiFi +{ + friend class ConnectivityManager; + +protected: + CHIP_ERROR InitWiFi(); + +private: + // Wi-Fi station + ConnectivityManager::WiFiStationMode _GetWiFiStationMode(void); + CHIP_ERROR _SetWiFiStationMode(ConnectivityManager::WiFiStationMode val); + bool _IsWiFiStationEnabled(void); + bool _IsWiFiStationApplicationControlled(void); + bool _IsWiFiStationConnected(void); + System::Clock::Timeout _GetWiFiStationReconnectInterval(void); + CHIP_ERROR _SetWiFiStationReconnectInterval(System::Clock::Timeout val); + bool _IsWiFiStationProvisioned(void); + void _ClearWiFiStationProvision(void); + CHIP_ERROR _GetAndLogWiFiStatsCounters(void); + bool _CanStartWiFiScan(); + void _OnWiFiScanDone(); + void _OnWiFiStationProvisionChange(); + void OnStationConnected(); + void OnStationDisconnected(); + + // Wi-Fi access point - not supported + ConnectivityManager::WiFiAPMode _GetWiFiAPMode(void); + CHIP_ERROR _SetWiFiAPMode(ConnectivityManager::WiFiAPMode val); + bool _IsWiFiAPActive(void); + bool _IsWiFiAPApplicationControlled(void); + void _DemandStartWiFiAP(void); + void _StopOnDemandWiFiAP(void); + void _MaintainOnDemandWiFiAP(void); + System::Clock::Timeout _GetWiFiAPIdleTimeout(void); + void _SetWiFiAPIdleTimeout(System::Clock::Timeout val); + + ConnectivityManager::WiFiStationMode mStationMode{ ConnectivityManager::WiFiStationMode::kWiFiStationMode_Disabled }; + ConnectivityManager::WiFiStationState mStationState{ ConnectivityManager::WiFiStationState::kWiFiStationState_NotConnected }; + System::Clock::Timeout mWiFiStationReconnectInterval{}; + + static const char * _WiFiStationModeToStr(ConnectivityManager::WiFiStationMode mode); + static const char * _WiFiAPModeToStr(ConnectivityManager::WiFiAPMode mode); + static const char * _WiFiStationStateToStr(ConnectivityManager::WiFiStationState state); + static const char * _WiFiAPStateToStr(ConnectivityManager::WiFiAPState state); +}; + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/nrfconnect/wifi/NrfWiFiDriver.cpp b/src/platform/nrfconnect/wifi/NrfWiFiDriver.cpp new file mode 100644 index 00000000000000..5092384006fca0 --- /dev/null +++ b/src/platform/nrfconnect/wifi/NrfWiFiDriver.cpp @@ -0,0 +1,284 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NrfWiFiDriver.h" + +#include "WiFiManager.h" +#include + +#include +#include +#include + +using namespace ::chip; +using namespace ::chip::DeviceLayer::Internal; +using namespace ::chip::DeviceLayer::PersistedStorage; + +namespace chip { +namespace DeviceLayer { +namespace NetworkCommissioning { + +size_t NrfWiFiDriver::WiFiNetworkIterator::Count() +{ + VerifyOrReturnValue(mDriver != nullptr, 0); + return mDriver->mStagingNetwork.IsConfigured() ? 1 : 0; +} + +bool NrfWiFiDriver::WiFiNetworkIterator::Next(Network & item) +{ + // we assume only one network is actually supported + // TODO: verify if this can be extended + if (mExhausted || 0 == Count()) + { + return false; + } + + memcpy(item.networkID, mDriver->mStagingNetwork.ssid, mDriver->mStagingNetwork.ssidLen); + item.networkIDLen = mDriver->mStagingNetwork.ssidLen; + item.connected = false; + + mExhausted = true; + + WiFiManager::WiFiInfo wifiInfo; + if (CHIP_NO_ERROR == WiFiManager::Instance().GetWiFiInfo(wifiInfo)) + { + if (WiFiManager::StationStatus::CONNECTED <= WiFiManager::Instance().GetStationStatus()) + { + if (wifiInfo.mSsidLen == item.networkIDLen && 0 == memcmp(wifiInfo.mSsid, item.networkID, wifiInfo.mSsidLen)) + { + item.connected = true; + } + } + } + return true; +} + +bool NrfWiFiScanResponseIterator::Next(WiFiScanResponse & item) +{ + if (mResultId < mResultCount) + { + item = mResults[mResultId++]; + return true; + } + return false; +} + +void NrfWiFiScanResponseIterator::Release() +{ + mResultId = mResultCount = 0; + Platform::MemoryFree(mResults); + mResults = nullptr; +} + +void NrfWiFiScanResponseIterator::Add(const WiFiScanResponse & result) +{ + void * newResults = Platform::MemoryRealloc(mResults, (mResultCount + 1) * sizeof(WiFiScanResponse)); + + if (newResults) + { + mResults = static_cast(newResults); + mResults[mResultCount++] = result; + } +} + +CHIP_ERROR NrfWiFiDriver::Init(NetworkStatusChangeCallback * networkStatusChangeCallback) +{ + mpNetworkStatusChangeCallback = networkStatusChangeCallback; + + LoadFromStorage(); + + if (mStagingNetwork.IsConfigured()) + { + WiFiManager::ConnectionHandling handling{ [] { Instance().OnNetworkStatusChanged(Status::kSuccess); }, + [] { Instance().OnNetworkStatusChanged(Status::kUnknownError); }, + System::Clock::Timeout{ 40000 } }; + ReturnErrorOnFailure( + WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling)); + } + + return CHIP_NO_ERROR; +} + +void NrfWiFiDriver::OnNetworkStatusChanged(Status status) +{ + if (status == Status::kSuccess) + { + ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Enabled); + } + + if (mpNetworkStatusChangeCallback) + mpNetworkStatusChangeCallback->OnNetworkingStatusChange(status, NullOptional, NullOptional); +} + +void NrfWiFiDriver::Shutdown() +{ + mpNetworkStatusChangeCallback = nullptr; +} + +CHIP_ERROR NrfWiFiDriver::CommitConfiguration() +{ + ReturnErrorOnFailure(KeyValueStoreMgr().Put(kPassKey, mStagingNetwork.pass, mStagingNetwork.passLen)); + ReturnErrorOnFailure(KeyValueStoreMgr().Put(kSsidKey, mStagingNetwork.ssid, mStagingNetwork.ssidLen)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR NrfWiFiDriver::RevertConfiguration() +{ + LoadFromStorage(); + + if (mStagingNetwork.IsConfigured()) + { + WiFiManager::ConnectionHandling handling{ [] { Instance().OnConnectWiFiNetwork(); }, + [] { Instance().OnConnectWiFiNetworkFailed(); }, + System::Clock::Timeout{ 40000 } }; + ReturnErrorOnFailure( + WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling)); + } + + return CHIP_NO_ERROR; +} + +Status NrfWiFiDriver::AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials, MutableCharSpan & outDebugText, + uint8_t & outNetworkIndex) +{ + outDebugText = {}; + outNetworkIndex = 0; + + VerifyOrReturnError(!mStagingNetwork.IsConfigured() || ssid.data_equal(mStagingNetwork.GetSsidSpan()), Status::kBoundsExceeded); + VerifyOrReturnError(ssid.size() <= sizeof(mStagingNetwork.ssid), Status::kOutOfRange); + VerifyOrReturnError(credentials.size() <= sizeof(mStagingNetwork.pass), Status::kOutOfRange); + + memcpy(mStagingNetwork.ssid, ssid.data(), ssid.size()); + memcpy(mStagingNetwork.pass, credentials.data(), credentials.size()); + mStagingNetwork.ssidLen = ssid.size(); + mStagingNetwork.passLen = credentials.size(); + + return Status::kSuccess; +} + +Status NrfWiFiDriver::RemoveNetwork(ByteSpan networkId, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) +{ + outDebugText = {}; + outNetworkIndex = 0; + + VerifyOrReturnError(networkId.data_equal(mStagingNetwork.GetSsidSpan()), Status::kNetworkIDNotFound); + mStagingNetwork.Clear(); + + return Status::kSuccess; +} + +Status NrfWiFiDriver::ReorderNetwork(ByteSpan networkId, uint8_t index, MutableCharSpan & outDebugText) +{ + outDebugText = {}; + + // Only one network is supported for now + VerifyOrReturnError(index == 0, Status::kOutOfRange); + VerifyOrReturnError(networkId.data_equal(mStagingNetwork.GetSsidSpan()), Status::kNetworkIDNotFound); + + return Status::kSuccess; +} + +void NrfWiFiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) +{ + Status status = Status::kSuccess; + WiFiManager::ConnectionHandling handling{ [] { Instance().OnConnectWiFiNetwork(); }, + [] { Instance().OnConnectWiFiNetworkFailed(); }, System::Clock::Timeout{ 40000 } }; + + VerifyOrExit(networkId.data_equal(mStagingNetwork.GetSsidSpan()), status = Status::kNetworkIDNotFound); + VerifyOrExit(mpConnectCallback == nullptr, status = Status::kUnknownError); + + mpConnectCallback = callback; + WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling); + +exit: + if (status != Status::kSuccess) + { + mpConnectCallback = nullptr; + callback->OnResult(status, CharSpan(), 0); + } +} + +CHIP_ERROR GetConfiguredNetwork(Network & network) +{ + return CHIP_NO_ERROR; +} + +void NrfWiFiDriver::OnConnectWiFiNetwork() +{ + ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Enabled); + + if (mpConnectCallback) + { + mpConnectCallback->OnResult(Status::kSuccess, CharSpan(), 0); + mpConnectCallback = nullptr; + } +} + +void NrfWiFiDriver::OnConnectWiFiNetworkFailed() +{ + if (mpConnectCallback) + { + mpConnectCallback->OnResult(Status::kNetworkNotFound, CharSpan(), 0); + mpConnectCallback = nullptr; + } +} + +void NrfWiFiDriver::ScanNetworks(ByteSpan ssid, WiFiDriver::ScanCallback * callback) +{ + mScanCallback = callback; + CHIP_ERROR error = WiFiManager::Instance().Scan( + ssid, [](int status, WiFiScanResponse * response) { Instance().OnScanWiFiNetworkDone(status, response); }); + + if (error != CHIP_NO_ERROR) + { + mScanCallback = nullptr; + callback->OnFinished(Status::kUnknownError, CharSpan(), nullptr); + } +} + +void NrfWiFiDriver::OnScanWiFiNetworkDone(int status, WiFiScanResponse * response) +{ + if (response != nullptr) + { + StackLock lock; + VerifyOrReturn(mScanCallback != nullptr); + mScanResponseIterator.Add(*response); + return; + } + + // Scan complete + DeviceLayer::SystemLayer().ScheduleLambda([this, status]() { + VerifyOrReturn(mScanCallback != nullptr); + mScanCallback->OnFinished(status == 0 ? Status::kSuccess : Status::kUnknownError, CharSpan(), &mScanResponseIterator); + mScanCallback = nullptr; + }); +} + +void NrfWiFiDriver::LoadFromStorage() +{ + WiFiNetwork network; + + mStagingNetwork = {}; + ReturnOnFailure(KeyValueStoreMgr().Get(kSsidKey, network.ssid, sizeof(network.ssid), &network.ssidLen)); + ReturnOnFailure(KeyValueStoreMgr().Get(kPassKey, network.pass, sizeof(network.pass), &network.passLen)); + mStagingNetwork = network; +} + +} // namespace NetworkCommissioning +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/nrfconnect/wifi/NrfWiFiDriver.h b/src/platform/nrfconnect/wifi/NrfWiFiDriver.h new file mode 100644 index 00000000000000..880cce80b69171 --- /dev/null +++ b/src/platform/nrfconnect/wifi/NrfWiFiDriver.h @@ -0,0 +1,123 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include + +namespace chip { +namespace DeviceLayer { +namespace NetworkCommissioning { + +constexpr uint8_t kMaxWiFiNetworks = 1; +constexpr uint8_t kWiFiScanNetworksTimeOutSeconds = 10; +constexpr uint8_t kWiFiConnectNetworkTimeoutSeconds = 120; + +class NrfWiFiScanResponseIterator : public Iterator +{ +public: + size_t Count() override { return mResultCount; } + bool Next(WiFiScanResponse & item) override; + void Release() override; + void Add(const WiFiScanResponse & result); + +private: + size_t mResultId = 0; + size_t mResultCount = 0; + WiFiScanResponse * mResults = nullptr; +}; + +class NrfWiFiDriver final : public WiFiDriver +{ +public: + // Define non-volatile storage keys for SSID and password. + // The naming convention is aligned with DefaultStorageKeyAllocator class. + static constexpr const char * kSsidKey = "g/wi/s"; + static constexpr const char * kPassKey = "g/wi/p"; + + class WiFiNetworkIterator final : public NetworkIterator + { + public: + WiFiNetworkIterator(NrfWiFiDriver * aDriver) : mDriver(aDriver) {} + size_t Count() override; + bool Next(Network & item) override; + void Release() override { delete this; } + ~WiFiNetworkIterator() = default; + + private: + NrfWiFiDriver * mDriver; + bool mExhausted{ false }; + }; + + struct WiFiNetwork + { + uint8_t ssid[DeviceLayer::Internal::kMaxWiFiSSIDLength]; + size_t ssidLen = 0; + uint8_t pass[DeviceLayer::Internal::kMaxWiFiKeyLength]; + size_t passLen = 0; + + bool IsConfigured() const { return ssidLen > 0; } + ByteSpan GetSsidSpan() const { return ByteSpan(ssid, ssidLen); } + ByteSpan GetPassSpan() const { return ByteSpan(pass, passLen); } + void Clear() { ssidLen = 0; } + }; + + // BaseDriver + NetworkIterator * GetNetworks() override { return new WiFiNetworkIterator(this); } + CHIP_ERROR Init(NetworkStatusChangeCallback * networkStatusChangeCallback) override; + void Shutdown() override; + + // WirelessDriver + uint8_t GetMaxNetworks() override { return kMaxWiFiNetworks; } + uint8_t GetScanNetworkTimeoutSeconds() override { return kWiFiScanNetworksTimeOutSeconds; } + uint8_t GetConnectNetworkTimeoutSeconds() override { return kWiFiConnectNetworkTimeoutSeconds; } + + CHIP_ERROR CommitConfiguration() override; + CHIP_ERROR RevertConfiguration() override; + + Status RemoveNetwork(ByteSpan networkId, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) override; + Status ReorderNetwork(ByteSpan networkId, uint8_t index, MutableCharSpan & outDebugText) override; + void ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) override; + + // WiFiDriver + Status AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials, MutableCharSpan & outDebugText, + uint8_t & outNetworkIndex) override; + void ScanNetworks(ByteSpan ssid, ScanCallback * callback) override; + + static NrfWiFiDriver & Instance() + { + static NrfWiFiDriver sInstance; + return sInstance; + } + + void OnConnectWiFiNetwork(); + void OnConnectWiFiNetworkFailed(); + void OnNetworkStatusChanged(Status status); + void OnScanWiFiNetworkDone(int status, WiFiScanResponse * result); + +private: + void LoadFromStorage(); + + ConnectCallback * mpConnectCallback{ nullptr }; + NetworkStatusChangeCallback * mpNetworkStatusChangeCallback{ nullptr }; + WiFiNetwork mStagingNetwork; + NrfWiFiScanResponseIterator mScanResponseIterator; + ScanCallback * mScanCallback{ nullptr }; +}; + +} // namespace NetworkCommissioning +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/nrfconnect/wifi/WiFiManager.cpp b/src/platform/nrfconnect/wifi/WiFiManager.cpp new file mode 100644 index 00000000000000..0a0e50cfdd0b1c --- /dev/null +++ b/src/platform/nrfconnect/wifi/WiFiManager.cpp @@ -0,0 +1,447 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * Provides the wrapper for nRF WiFi API + */ + +#include "WiFiManager.h" + +#include +#include +#include +#include +#include + +#include +#include + +extern "C" { +#include +#include +#include +#include +#include +} + +extern struct wpa_global * global; + +static struct wpa_supplicant * wpa_s; + +namespace chip { +namespace DeviceLayer { + +namespace { + +NetworkCommissioning::WiFiScanResponse ToScanResponse(wifi_scan_result * result) +{ + NetworkCommissioning::WiFiScanResponse response = {}; + + if (result != nullptr) + { + static_assert(sizeof(response.ssid) == sizeof(result->ssid), "SSID length mismatch"); + static_assert(sizeof(response.bssid) == sizeof(result->mac), "BSSID length mismatch"); + + // TODO: Distinguish WPA versions + response.security.Set(result->security == WIFI_SECURITY_TYPE_PSK ? NetworkCommissioning::WiFiSecurity::kWpaPersonal + : NetworkCommissioning::WiFiSecurity::kUnencrypted); + response.channel = result->channel; + response.rssi = result->rssi; + response.ssidLen = result->ssid_length; + memcpy(response.ssid, result->ssid, result->ssid_length); + // TODO: MAC/BSSID is not filled by the Wi-Fi driver + memcpy(response.bssid, result->mac, result->mac_length); + } + + return response; +} + +} // namespace + +// These enums shall reflect the overall ordered disconnected->connected flow +const Map + WiFiManager::sStatusMap({ { WPA_DISCONNECTED, WiFiManager::StationStatus::DISCONNECTED }, + { WPA_INTERFACE_DISABLED, WiFiManager::StationStatus::DISABLED }, + { WPA_INACTIVE, WiFiManager::StationStatus::DISABLED }, + { WPA_SCANNING, WiFiManager::StationStatus::SCANNING }, + { WPA_AUTHENTICATING, WiFiManager::StationStatus::CONNECTING }, + { WPA_ASSOCIATING, WiFiManager::StationStatus::CONNECTING }, + { WPA_ASSOCIATED, WiFiManager::StationStatus::CONNECTED }, + { WPA_4WAY_HANDSHAKE, WiFiManager::StationStatus::PROVISIONING }, + { WPA_GROUP_HANDSHAKE, WiFiManager::StationStatus::PROVISIONING }, + { WPA_COMPLETED, WiFiManager::StationStatus::FULLY_PROVISIONED } }); + +// Map WiFi center frequency to the corresponding channel number +const Map WiFiManager::sFreqChannelMap( + { { 4915, 183 }, { 4920, 184 }, { 4925, 185 }, { 4935, 187 }, { 4940, 188 }, { 4945, 189 }, { 4960, 192 }, + { 4980, 196 }, { 5035, 7 }, { 5040, 8 }, { 5045, 9 }, { 5055, 11 }, { 5060, 12 }, { 5080, 16 }, + { 5170, 34 }, { 5180, 36 }, { 5190, 38 }, { 5200, 40 }, { 5210, 42 }, { 5220, 44 }, { 5230, 46 }, + { 5240, 48 }, { 5260, 52 }, { 5280, 56 }, { 5300, 60 }, { 5320, 64 }, { 5500, 100 }, { 5520, 104 }, + { 5540, 108 }, { 5560, 112 }, { 5580, 116 }, { 5600, 120 }, { 5620, 124 }, { 5640, 128 }, { 5660, 132 }, + { 5680, 136 }, { 5700, 140 }, { 5745, 149 }, { 5765, 153 }, { 5785, 157 }, { 5805, 161 }, { 5825, 165 } }); + +CHIP_ERROR WiFiManager::Init() +{ + // wpa_supplicant instance is initialized in dedicated supplicant thread, so wait until + // the initialization is completed. + // TODO: fix thread-safety of the solution. + constexpr size_t kInitTimeoutMs = 5000; + const int64_t initStartTime = k_uptime_get(); + // TODO: Handle multiple VIFs + const char * ifname = "wlan0"; + + while (!global || !(wpa_s = wpa_supplicant_get_iface(global, ifname))) + { + if (k_uptime_get() > initStartTime + kInitTimeoutMs) + { + ChipLogError(DeviceLayer, "wpa_supplicant is not initialized!"); + return CHIP_ERROR_INTERNAL; + } + + k_msleep(200); + } + + // TODO: consider moving these to ConnectivityManagerImpl to be prepared for handling multiple interfaces on a single device. + Inet::UDPEndPointImplSockets::SetJoinMulticastGroupHandler([](Inet::InterfaceId interfaceId, const Inet::IPAddress & address) { + const in6_addr addr = InetUtils::ToZephyrAddr(address); + net_if * iface = InetUtils::GetInterface(interfaceId); + VerifyOrReturnError(iface != nullptr, INET_ERROR_UNKNOWN_INTERFACE); + + net_if_mcast_addr * maddr = net_if_ipv6_maddr_add(iface, &addr); + + if (maddr && !net_if_ipv6_maddr_is_joined(maddr) && !net_ipv6_is_addr_mcast_link_all_nodes(&addr)) + { + net_if_ipv6_maddr_join(maddr); + } + + return CHIP_NO_ERROR; + }); + + Inet::UDPEndPointImplSockets::SetLeaveMulticastGroupHandler([](Inet::InterfaceId interfaceId, const Inet::IPAddress & address) { + const in6_addr addr = InetUtils::ToZephyrAddr(address); + net_if * iface = InetUtils::GetInterface(interfaceId); + VerifyOrReturnError(iface != nullptr, INET_ERROR_UNKNOWN_INTERFACE); + + if (!net_ipv6_is_addr_mcast_link_all_nodes(&addr) && !net_if_ipv6_maddr_rm(iface, &addr)) + { + return CHIP_ERROR_INVALID_ADDRESS; + } + + return CHIP_NO_ERROR; + }); + + ChipLogDetail(DeviceLayer, "wpa_supplicant has been initialized"); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WiFiManager::AddNetwork(const ByteSpan & ssid, const ByteSpan & credentials) +{ + ChipLogDetail(DeviceLayer, "Adding WiFi network"); + mpWpaNetwork = wpa_supplicant_add_network(wpa_s); + if (mpWpaNetwork) + { + static constexpr size_t kMaxSsidLen{ 32 }; + mpWpaNetwork->ssid = (u8 *) k_malloc(kMaxSsidLen); + + if (mpWpaNetwork->ssid) + { + memcpy(mpWpaNetwork->ssid, ssid.data(), ssid.size()); + mpWpaNetwork->ssid_len = ssid.size(); + mpWpaNetwork->key_mgmt = WPA_KEY_MGMT_NONE; + mpWpaNetwork->disabled = 1; + wpa_s->conf->filter_ssids = 1; + + return AddPsk(credentials); + } + } + + return CHIP_ERROR_INTERNAL; +} + +CHIP_ERROR WiFiManager::Scan(const ByteSpan & ssid, ScanCallback callback) +{ + const StationStatus stationStatus = GetStationStatus(); + VerifyOrReturnError(stationStatus != StationStatus::DISABLED && stationStatus != StationStatus::SCANNING && + stationStatus != StationStatus::CONNECTING, + CHIP_ERROR_INCORRECT_STATE); + + net_if * const iface = InetUtils::GetInterface(); + VerifyOrReturnError(iface != nullptr, CHIP_ERROR_INTERNAL); + + const device * dev = net_if_get_device(iface); + VerifyOrReturnError(dev != nullptr, CHIP_ERROR_INTERNAL); + + const net_wifi_mgmt_offload * ops = static_cast(dev->api); + VerifyOrReturnError(ops != nullptr, CHIP_ERROR_INTERNAL); + + mScanCallback = callback; + + // TODO: Use saner API once such exists. + // TODO: Take 'ssid' into account. + VerifyOrReturnError(ops->scan(dev, + [](net_if *, int status, wifi_scan_result * result) { + VerifyOrReturn(Instance().mScanCallback != nullptr); + NetworkCommissioning::WiFiScanResponse response = ToScanResponse(result); + Instance().mScanCallback(status, result != nullptr ? &response : nullptr); + }) == 0, + CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WiFiManager::Connect(const ByteSpan & ssid, const ByteSpan & credentials, const ConnectionHandling & handling) +{ + ChipLogDetail(DeviceLayer, "Connecting to WiFi network"); + + mConnectionSuccessClbk = handling.mOnConnectionSuccess; + mConnectionFailedClbk = handling.mOnConnectionFailed; + mConnectionTimeoutMs = handling.mConnectionTimeoutMs; + + CHIP_ERROR err = AddNetwork(ssid, credentials); + if (CHIP_NO_ERROR == err) + { + EnableStation(true); + wpa_supplicant_select_network(wpa_s, mpWpaNetwork); + WaitForConnectionAsync(); + } + else + { + OnConnectionFailed(); + } + return err; +} + +void WiFiManager::OnConnectionSuccess() +{ + if (mConnectionSuccessClbk) + mConnectionSuccessClbk(); +} + +void WiFiManager::OnConnectionFailed() +{ + if (mConnectionFailedClbk) + mConnectionFailedClbk(); +} + +CHIP_ERROR WiFiManager::AddPsk(const ByteSpan & credentials) +{ + mpWpaNetwork->key_mgmt = WPA_KEY_MGMT_PSK; + str_clear_free(mpWpaNetwork->passphrase); + mpWpaNetwork->passphrase = dup_binstr(credentials.data(), credentials.size()); + + if (mpWpaNetwork->passphrase) + { + wpa_config_update_psk(mpWpaNetwork); + return CHIP_NO_ERROR; + } + + return CHIP_ERROR_INTERNAL; +} + +WiFiManager::StationStatus WiFiManager::GetStationStatus() const +{ + if (wpa_s) + { + return StatusFromWpaStatus(wpa_s->wpa_state); + } + else + { + ChipLogError(DeviceLayer, "wpa_supplicant is not initialized!"); + return StationStatus::NONE; + } +} + +WiFiManager::StationStatus WiFiManager::StatusFromWpaStatus(const wpa_states & status) +{ + ChipLogDetail(DeviceLayer, "WPA internal status: %d", static_cast(status)); + return WiFiManager::sStatusMap[status]; +} + +CHIP_ERROR WiFiManager::EnableStation(bool enable) +{ + VerifyOrReturnError(nullptr != wpa_s && nullptr != mpWpaNetwork, CHIP_ERROR_INTERNAL); + if (enable) + { + wpa_supplicant_enable_network(wpa_s, mpWpaNetwork); + } + else + { + wpa_supplicant_disable_network(wpa_s, mpWpaNetwork); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WiFiManager::ClearStationProvisioningData() +{ + VerifyOrReturnError(nullptr != wpa_s && nullptr != mpWpaNetwork, CHIP_ERROR_INTERNAL); + wpa_supplicant_cancel_scan(wpa_s); + wpa_clear_keys(wpa_s, mpWpaNetwork->bssid); + str_clear_free(mpWpaNetwork->passphrase); + wpa_config_update_psk(mpWpaNetwork); + wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WiFiManager::DisconnectStation() +{ + VerifyOrReturnError(nullptr != wpa_s, CHIP_ERROR_INTERNAL); + wpa_supplicant_cancel_scan(wpa_s); + wpas_request_disconnection(wpa_s); + + return CHIP_NO_ERROR; +} + +void WiFiManager::WaitForConnectionAsync() +{ + chip::DeviceLayer::SystemLayer().StartTimer( + static_cast(1000), [](System::Layer *, void *) { Instance().PollTimerCallback(); }, nullptr); +} + +void WiFiManager::PollTimerCallback() +{ + const uint32_t kMaxRetriesNumber{ mConnectionTimeoutMs.count() / 1000 }; + static uint32_t retriesNumber{ 0 }; + + if (WiFiManager::StationStatus::FULLY_PROVISIONED == GetStationStatus()) + { + retriesNumber = 0; + OnConnectionSuccess(); + } + else + { + if (retriesNumber++ < kMaxRetriesNumber) + { + // wait more time + WaitForConnectionAsync(); + } + else + { + // connection timeout + retriesNumber = 0; + OnConnectionFailed(); + } + } +} + +CHIP_ERROR WiFiManager::GetWiFiInfo(WiFiInfo & info) const +{ + VerifyOrReturnError(nullptr != wpa_s, CHIP_ERROR_INTERNAL); + VerifyOrReturnError(nullptr != mpWpaNetwork, CHIP_ERROR_INTERNAL); + + static uint8_t sBssid[ETH_ALEN]; + if (WiFiManager::StationStatus::CONNECTED <= GetStationStatus()) + { + memcpy(sBssid, wpa_s->bssid, ETH_ALEN); + info.mBssId = ByteSpan(sBssid, ETH_ALEN); + info.mSecurityType = GetSecurityType(); + // TODO: this should reflect the real connection compliance + // i.e. the AP might support WiFi 5 only even though the station + // is WiFi 6 ready (so the connection is WiFi 5 effectively). + // For now just return what the station supports. + info.mWiFiVersion = EMBER_ZCL_WI_FI_VERSION_TYPE_802__11AX; + + wpa_signal_info signalInfo{}; + if (0 == wpa_drv_signal_poll(wpa_s, &signalInfo)) + { + info.mRssi = signalInfo.current_signal; // dBm + info.mChannel = FrequencyToChannel(signalInfo.frequency); + } + else + { + // this values should be nullable according to the Matter spec + info.mRssi = std::numeric_limits::min(); + info.mChannel = std::numeric_limits::min(); + } + + memcpy(info.mSsid, mpWpaNetwork->ssid, mpWpaNetwork->ssid_len); + info.mSsidLen = mpWpaNetwork->ssid_len; + + return CHIP_NO_ERROR; + } + + return CHIP_ERROR_INTERNAL; +} + +uint8_t WiFiManager::GetSecurityType() const +{ + VerifyOrReturnValue(nullptr != mpWpaNetwork, EMBER_ZCL_SECURITY_TYPE_UNSPECIFIED); + + if ((mpWpaNetwork->key_mgmt & WPA_KEY_MGMT_NONE) || !wpa_key_mgmt_wpa_any(mpWpaNetwork->key_mgmt)) + { + return EMBER_ZCL_SECURITY_TYPE_NONE; + } + else if (wpa_key_mgmt_wpa_psk_no_sae(mpWpaNetwork->key_mgmt)) + { + return (mpWpaNetwork->pairwise_cipher & (WPA_CIPHER_TKIP | WPA_CIPHER_CCMP)) ? EMBER_ZCL_SECURITY_TYPE_WPA2 + : EMBER_ZCL_SECURITY_TYPE_WPA3; + } + else if (wpa_key_mgmt_sae(mpWpaNetwork->key_mgmt)) + { + return EMBER_ZCL_SECURITY_TYPE_WPA3; + } + else + { + return EMBER_ZCL_SECURITY_TYPE_WEP; + } + + return EMBER_ZCL_SECURITY_TYPE_UNSPECIFIED; +} + +uint8_t WiFiManager::FrequencyToChannel(uint16_t freq) +{ + static constexpr uint16_t k24MinFreq{ 2401 }; + static constexpr uint16_t k24MaxFreq{ 2484 }; + static constexpr uint8_t k24FreqConstDiff{ 5 }; + + if (freq >= k24MinFreq && freq < k24MaxFreq) + { + return static_cast((freq - k24MinFreq) / k24FreqConstDiff + 1); + } + else if (freq == k24MaxFreq) + { + return 14; + } + else if (freq > k24MaxFreq) + { + // assume we are in 5GH band + return sFreqChannelMap[freq]; + } + return 0; +} + +CHIP_ERROR WiFiManager::GetNetworkStatistics(NetworkStatistics & stats) const +{ + // TODO: below will not work (result will be all zeros) until + // the get_stats handler is implemented in WiFi driver + net_stats_eth data{}; + net_mgmt(NET_REQUEST_STATS_GET_ETHERNET, InetUtils::GetInterface(), &data, sizeof(data)); + + stats.mPacketMulticastRxCount = data.multicast.rx; + stats.mPacketMulticastTxCount = data.multicast.tx; + stats.mPacketUnicastRxCount = data.pkts.rx - data.multicast.rx - data.broadcast.rx; + stats.mPacketUnicastTxCount = data.pkts.tx - data.multicast.tx - data.broadcast.tx; + stats.mOverruns = 0; // TODO: clarify if this can be queried from mgmt API (e.g. data.tx_dropped) + + return CHIP_NO_ERROR; +} + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/nrfconnect/wifi/WiFiManager.h b/src/platform/nrfconnect/wifi/WiFiManager.h new file mode 100644 index 00000000000000..b068cd99705eef --- /dev/null +++ b/src/platform/nrfconnect/wifi/WiFiManager.h @@ -0,0 +1,171 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * Provides the wrapper for nRF wpa_supplicant API + */ + +#pragma once + +#include +#include +#include +#include + +#include + +extern "C" { +#include +#include +} + +struct net_if; +struct wpa_ssid; +using WpaNetwork = struct wpa_ssid; + +namespace chip { +namespace DeviceLayer { + +// emulation of dictionary - might be moved to utils +template +class Map +{ + struct Pair + { + T1 key; + T2 value; + }; + +public: + Map(const Pair (&list)[N]) + { + int idx{ 0 }; + for (const auto & pair : list) + { + mMap[idx++] = pair; + } + } + + T2 operator[](const T1 & key) const + { + for (const auto & it : mMap) + { + if (key == it.key) + return it.value; + } + + return T2{}; + } + + Map() = delete; + Map(const Map &) = delete; + Map(Map &&) = delete; + Map & operator=(const Map &) = delete; + Map & operator=(Map &&) = delete; + ~Map() = default; + +private: + Pair mMap[N]; +}; + +class WiFiManager +{ + using ConnectionCallback = void (*)(); + +public: + enum class StationStatus : uint8_t + { + NONE, + DISCONNECTED, + DISABLED, + SCANNING, + CONNECTING, + CONNECTED, + PROVISIONING, + FULLY_PROVISIONED + }; + + static WiFiManager & Instance() + { + static WiFiManager sInstance; + return sInstance; + } + + using ScanCallback = void (*)(int /* status */, NetworkCommissioning::WiFiScanResponse *); + + struct ConnectionHandling + { + ConnectionCallback mOnConnectionSuccess{}; + ConnectionCallback mOnConnectionFailed{}; + System::Clock::Timeout mConnectionTimeoutMs{}; + }; + + struct WiFiInfo + { + ByteSpan mBssId{}; + uint8_t mSecurityType{}; + uint8_t mWiFiVersion{}; + uint16_t mChannel{}; + int8_t mRssi{}; + uint8_t mSsid[DeviceLayer::Internal::kMaxWiFiSSIDLength]; + size_t mSsidLen{ 0 }; + }; + + struct NetworkStatistics + { + uint32_t mPacketMulticastRxCount{}; + uint32_t mPacketMulticastTxCount{}; + uint32_t mPacketUnicastRxCount{}; + uint32_t mPacketUnicastTxCount{}; + uint32_t mOverruns{}; + }; + + CHIP_ERROR Init(); + CHIP_ERROR Scan(const ByteSpan & ssid, ScanCallback callback); + CHIP_ERROR Connect(const ByteSpan & ssid, const ByteSpan & credentials, const ConnectionHandling & handling); + StationStatus GetStationStatus() const; + CHIP_ERROR ClearStationProvisioningData(); + CHIP_ERROR DisconnectStation(); + CHIP_ERROR GetWiFiInfo(WiFiInfo & info) const; + CHIP_ERROR GetNetworkStatistics(NetworkStatistics & stats) const; + +private: + CHIP_ERROR AddPsk(const ByteSpan & credentials); + CHIP_ERROR EnableStation(bool enable); + CHIP_ERROR AddNetwork(const ByteSpan & ssid, const ByteSpan & credentials); + void PollTimerCallback(); + void WaitForConnectionAsync(); + void OnConnectionSuccess(); + void OnConnectionFailed(); + uint8_t GetSecurityType() const; + + WpaNetwork * mpWpaNetwork{ nullptr }; + ConnectionCallback mConnectionSuccessClbk; + ConnectionCallback mConnectionFailedClbk; + System::Clock::Timeout mConnectionTimeoutMs; + ScanCallback mScanCallback{ nullptr }; + + static uint8_t FrequencyToChannel(uint16_t freq); + static StationStatus StatusFromWpaStatus(const wpa_states & status); + + static const Map sStatusMap; + static const Map sFreqChannelMap; +}; + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/telink/BUILD.gn b/src/platform/telink/BUILD.gn index b13a9d185ee8dd..1a4c90395dbe7c 100644 --- a/src/platform/telink/BUILD.gn +++ b/src/platform/telink/BUILD.gn @@ -26,6 +26,7 @@ static_library("telink") { "../Zephyr/ConfigurationManagerImpl.cpp", "../Zephyr/DiagnosticDataProviderImpl.cpp", "../Zephyr/DiagnosticDataProviderImpl.h", + "../Zephyr/DiagnosticDataProviderImplGetter.cpp", "../Zephyr/KeyValueStoreManagerImpl.cpp", "../Zephyr/Logging.cpp", "../Zephyr/PlatformManagerImpl.cpp",