diff --git a/components/network_provisioning/.component_hash b/components/network_provisioning/.component_hash new file mode 100644 index 0000000..0ef8ae4 --- /dev/null +++ b/components/network_provisioning/.component_hash @@ -0,0 +1 @@ +72d27784e3daf807418a34fb00be136ec50c6db49d989ce981d22e031fc0e7f8 \ No newline at end of file diff --git a/components/network_provisioning/CHANGELOG.md b/components/network_provisioning/CHANGELOG.md new file mode 100644 index 0000000..6fb1ef9 --- /dev/null +++ b/components/network_provisioning/CHANGELOG.md @@ -0,0 +1,51 @@ +# 1.2.4 (14-April-2026) + +- Fix incorrect fail reason reported in `NETWORK_PROV_WIFI_CRED_FAIL` event. + +# 1.2.3 (2-April-2026) +- Fix possible NULL pointer dereference with malformed protobuf messages +- Fix possible buffer overflow in Thread provisioning +- Fix potential memory leaks + +# 1.2.2 (18-Dec-2025) + +- Fix connection attempts counter not being reset on state reset or new credentials + - Reset `connection_attempts_completed` to 0 in `network_prov_mgr_reset_wifi_sm_state_on_failure()` + and `network_prov_mgr_configure_wifi_sta()` to ensure full `wifi_conn_attempts` retries + after reset or when applying new credentials. + +# 1.2.1 (15-Dec-2025) + +- Fix prov-ctrl reset handler to return success when device is already in provisioning mode + - If firmware has already called `network_prov_mgr_reset_wifi_sm_state_on_failure()` or + `network_prov_mgr_reset_thread_sm_state_on_failure()`, the device state is already reset + to provisioning mode. The prov-ctrl handler now returns success in this case instead of + an invalid state error, allowing phone apps to successfully reset even if firmware has + already performed the reset operation. + +# 07-October-2025 + +- Use managed cJSON component for IDF v6.0 and above + +# 01-April-2025 + +- Extend provisioning check for `ESP_WIFI_REMOTE_ENABLED` as well along with existing `ESP_WIFI_ENABLED` +- This enables provisioning for the devices not having native Wi-Fi (e.g., ESP32-P4) and using external/remote Wi-Fi solution such as esp-hosted for Wi-Fi connectivity. + +# 17-March-2025 + +- Update the network provisioning component to work with the protocomm component which fixes incorrect AES-GCM IV usage in security2 scheme. + +# 19-June-2024 + +- Change the proto files to make the network provisioning component stay backward compatible with the wifi_provisioing + +# 23-April-2024 + +- Add `wifi_prov` or `thread_prov` in provision capabilities in the network provisioning manager for the provisioner to distinguish Thread or Wi-Fi devices + +# 16-April-2024 + +- Move wifi_provisioning component from ESP-IDF at commit 5a40bb8746 and rename it to network_provisioning with the addition of Thread provisioning support. +- Update esp_prov tool to support both Wi-Fi provisioning and Thread provisioning. +- Create thread_prov and wifi_prov examples diff --git a/components/network_provisioning/CHECKSUMS.json b/components/network_provisioning/CHECKSUMS.json new file mode 100644 index 0000000..e0ee672 --- /dev/null +++ b/components/network_provisioning/CHECKSUMS.json @@ -0,0 +1 @@ +{"version":"1.0","algorithm":"sha256","created_at":"2026-04-13T14:00:33.789110+00:00","files":[{"path":"CHANGELOG.md","size":2295,"hash":"de3e4b1254dd52ad9b376b4e709944c0acd2011d8448b5a1a5287b8fdfcd81c1"},{"path":"CMakeLists.txt","size":1279,"hash":"cfeff1d7411a333fecce1d78383fd87ab3bf168443bbffad2f0c3f9abd0b295a"},{"path":"Kconfig","size":3318,"hash":"e1a8b32ec30a20249311e286fc62bf1b45df130b157d1ffdc538d008c3d01be4"},{"path":"LICENSE","size":11365,"hash":"f0f612d1627194ddd07c8b5b238a514ae9794632e5470c00f16237def7dc2246"},{"path":"README.md","size":689,"hash":"765460849767d8753337609ef76cd16e3084a69915e01b211ac06e29d5a894a6"},{"path":"idf_component.yml","size":449,"hash":"ee8b47995d97fd020b6eec6b3f61b04e700b773b79cbca85c50b421f5ff4a45a"},{"path":"examples/README.md","size":683,"hash":"f62f39e521e370fa2a711a8c772efb09268d9f7b4abfbb06522fe7a3541e966c"},{"path":"proto/CMakeLists.txt","size":916,"hash":"d1e1655981f542d0cd5decf434fb899728826ac60976e3eb5d5a73738cd1c58c"},{"path":"proto/README.md","size":1794,"hash":"41f1b5c1accdd1dacab4567806027587464a814a0a33a336a6aa809a38c0d61c"},{"path":"proto/makefile","size":243,"hash":"00a3f16a982a11fb8f758ddf3f47c0fdbf5846de7bec324e8895eae6801a1057"},{"path":"proto/network_config.proto","size":2281,"hash":"596fc1262cfdacfdf357de0296e8767610a656cce66e1d6dd15b9fd6353e6423"},{"path":"proto/network_constants.proto","size":933,"hash":"81074d397e7cee53b0efd9da4158e7a33a04d221bb1427bf7c67cf5651867a95"},{"path":"proto/network_ctrl.proto","size":1165,"hash":"01d3812b0a291c540a4a9152be945ac0564a337409e3cba69749398332547e91"},{"path":"proto/network_scan.proto","size":2449,"hash":"2c756397e596298eef262bf004a37961b152f96a54b33bcc9275fc0700aaf1cc"},{"path":"proto-c/network_config.pb-c.c","size":45723,"hash":"83a80f8c70c43563d6f84d2fa604698beb88f3290f9aa3e2130b79d5256d90cd"},{"path":"proto-c/network_config.pb-c.h","size":22399,"hash":"cf80ca717d39acc9ae8c191f778787dd6df2547209d32928e75b968dbd0ec81f"},{"path":"proto-c/network_constants.pb-c.c","size":15545,"hash":"8240e2e03525f9ba09ad6c7dd9de9c114b3ac79c25be3b9ed7157e7b3c8b45d4"},{"path":"proto-c/network_constants.pb-c.h","size":6989,"hash":"64bbc12b52131eb177ba837b3b32cd4a77b6291d2a375db401ef84773fb09e15"},{"path":"proto-c/network_ctrl.pb-c.c","size":27396,"hash":"b41a3971bcde639582305730283b75b938044cc266fffeec0e002c4efd1dca3f"},{"path":"proto-c/network_ctrl.pb-c.h","size":14775,"hash":"74477506db022bfbf44b2895618c940f9559c3be8e9cb67c276bb5447e530228"},{"path":"proto-c/network_scan.pb-c.c","size":53092,"hash":"b92de47f977b7b7cceb1d55f60ea303380aa55e2fcc6b64fec132b8ed5124a59"},{"path":"proto-c/network_scan.pb-c.h","size":24195,"hash":"163501892e108ba2c3d6d7b2cb41c7ab5b205e315d9848608e70441efecb11dd"},{"path":"python/network_config_pb2.py","size":5376,"hash":"661034b2a3ae032a439cc066cbb46e5ba4c5fcf1ce4891ac66779d9c534dedd9"},{"path":"python/network_constants_pb2.py","size":2822,"hash":"c943b8daaea27de97a5dad44a36775520058f1de3b6819ee18b27a6ef62427db"},{"path":"python/network_ctrl_pb2.py","size":3280,"hash":"fb5f2ee562c45df45e8f680b813b0e2c01a6e356598933e577d0d76b3abd6ec7"},{"path":"python/network_scan_pb2.py","size":5705,"hash":"4718403ef737547bc1a0d07221c7fd98dc3f563571dedcbc0c45228da8128657"},{"path":"src/handlers.c","size":14559,"hash":"74e428f0751d1c5afee949da0d9f0d5773e7164f051970c193e0ba9805874faf"},{"path":"src/manager.c","size":87455,"hash":"ff8f910260027702288cb00999dbe1ffc6cf490dc1a3cbc146db205c716c3046"},{"path":"src/network_config.c","size":24115,"hash":"ccbdf3169f468dea64870d481beec3b0e5a6974439dc63ceb4bd59cac87ec386"},{"path":"src/network_ctrl.c","size":8218,"hash":"6467b9646633083ea22bf7742f8e7e40c97e00b990d23cb42e73899145911f89"},{"path":"src/network_ctrl.h","size":1525,"hash":"1164c2e70fb8421fccc8fdbbc10facfc0920ad8a104098ff32f7485763303500"},{"path":"src/network_provisioning_priv.h","size":4920,"hash":"e1fd955e486e7c6025331ffdfed2f7aec5c1095eb94f80be3976ccd06c7c394e"},{"path":"src/network_scan.c","size":22679,"hash":"c548b27ca4ac83ad5e7d9c2487dfcdd35be6e2c479938fc5839391d6f738eb8a"},{"path":"src/scheme_ble.c","size":10635,"hash":"f05ee74a4819317c9005b7e8df96b085cf547ca0cfca6d2448251334bcdd58e4"},{"path":"src/scheme_console.c","size":2367,"hash":"1e23ee3e56edb89a2791dca8ce6a3e5ff35cb0b16610cb205b2ab8a876b441bf"},{"path":"src/scheme_softap.c","size":5695,"hash":"c1bb69750475076d1b820b58df01ad3706a4c6230f34c7252034d418eb8535e4"},{"path":"tool/esp_prov/README.md","size":7470,"hash":"b180cab5e9678a2f1ea87722915b699fda9e231a54bc4f31d025d2fb1c6dec87"},{"path":"tool/esp_prov/__init__.py","size":178,"hash":"75be31c7745393e5a84efd1de9c93894c36d3d7ed97007a60acc02f88122d044"},{"path":"tool/esp_prov/esp_prov.py","size":31148,"hash":"f033e6a16fc50528724acb27ead33c5607edf1c05ef2cb56eb95a796f26a89af"},{"path":"tool/esp_prov/proto/__init__.py","size":1788,"hash":"77d9616d62f96162ce3f9643af3cebd86cb4ca353278b64eaed1cd60ce7dd1f6"},{"path":"tool/esp_prov/prov/__init__.py","size":276,"hash":"d544661f29da4274f8a408cccfffe136155cd672c9d71c548c2f9576d332811d"},{"path":"tool/esp_prov/prov/custom_prov.py","size":854,"hash":"ccbc5b717d7ee4fc6e3e86078b2dce38af841d08220461c437a3fe6e517bb5a9"},{"path":"tool/esp_prov/prov/network_ctrl.py","size":2371,"hash":"5bcc670ecbdd866375474339296f81e2dedc59a5f117fc63180becd57f831c57"},{"path":"tool/esp_prov/prov/network_prov.py","size":7165,"hash":"c26468c99237ea74f9d017d9c399888392a11106d3811f6c3cd0f64dc400c45d"},{"path":"tool/esp_prov/prov/network_scan.py","size":5994,"hash":"6cabc5f08c5a05cdee7ce812547890bd81ef22ca33bdb2bccead69f193f3b219"},{"path":"tool/esp_prov/security/__init__.py","size":248,"hash":"67f6cb07545b5e01e952ee6f6035dc991cdb824f901c43f14908d22005ed9b8e"},{"path":"tool/esp_prov/security/security.py","size":258,"hash":"37a88d266d730154b0b914e51b68e0e50d2565a192cedf76ac57f2743f79c58e"},{"path":"tool/esp_prov/security/security0.py","size":1860,"hash":"d2ee1fbd64566f93c27c206800d34f1e84e683c6c3e853302d141c6a6303ca5c"},{"path":"tool/esp_prov/security/security1.py","size":6323,"hash":"59bdfe12bfc1f9e635f9fba51683a52aa64eabbe3d44bcd10b780b04a9e692e5"},{"path":"tool/esp_prov/security/security2.py","size":7686,"hash":"bdc90f36a754a9a767aa7298bd697b7ed0ddf42777bda159d13ed646a7c39445"},{"path":"tool/esp_prov/security/srp6a.py","size":10228,"hash":"6074ab48d653669b6da24525cf7b7d6ac6ab4b88e4274ae69c2df8597c2a0834"},{"path":"tool/esp_prov/transport/__init__.py","size":260,"hash":"d80aa038dad20b2498644bc5c2bd0a2c0d5387c4cbd605d3d63339702cdb9480"},{"path":"tool/esp_prov/transport/ble_cli.py","size":7773,"hash":"b854f259a12b5378f964d8bbeba13bdb538b68dc5dbcba660d2cf59cd5712498"},{"path":"tool/esp_prov/transport/transport.py","size":382,"hash":"a656caf1e0d1ab519d3d89bccfdb5c9693adb8d54bb6ad1e1059a068cb594729"},{"path":"tool/esp_prov/transport/transport_ble.py","size":2340,"hash":"9319af583d48a82c008a08a8dc21334da5483c2a3d784b754b8a07d37c1abada"},{"path":"tool/esp_prov/transport/transport_console.py","size":571,"hash":"9cef5a6fbb21dd45362d65593fde39ec50af49e11b2cfb5ce4c2269055d47d7f"},{"path":"tool/esp_prov/transport/transport_http.py","size":2143,"hash":"22bc76990373a82a01302ca0f4ce5c9eb183e07630f88ba25eb1a697d67c9f31"},{"path":"tool/esp_prov/utils/__init__.py","size":155,"hash":"e88fdf01f49df6317af44a84b1328b75909037d0965e7f2936edb48cfeac1cb1"},{"path":"tool/esp_prov/utils/convenience.py","size":754,"hash":"ca6c639a8285bfa2c757d2b3d4879b25c63c8a1fdc71a395479836686c2c667e"},{"path":"include/network_provisioning/manager.h","size":30354,"hash":"faad780c138d4b8e2ffc92fa2247b8fa602782d5f0383d63bc530044680095f7"},{"path":"include/network_provisioning/network_config.h","size":7170,"hash":"5488e5c2321b184480cca94a9f624bf758b3504670a8886d58e61eab387cfbaf"},{"path":"include/network_provisioning/network_scan.h","size":6991,"hash":"7c666e1fb8c82d4338cdc3c1f2d9b3b6579d517f2aa1c8e50ccc2fdcc9509c23"},{"path":"include/network_provisioning/scheme_ble.h","size":5661,"hash":"b79d7ed4b814e6fca3f1d16f99b5c66da4290f5ae12ee659ecdda3398e81f53a"},{"path":"include/network_provisioning/scheme_console.h","size":479,"hash":"b4bb3683763e46ee25c337b39e5b266eb84f543eaf0df97562ae6cb6e51c1b28"},{"path":"include/network_provisioning/scheme_softap.h","size":929,"hash":"e9f309e130e708fcc1e659b3fe401ef39bf38c34a4d169abbd649bcfbee916f5"},{"path":"examples/thread_prov/CMakeLists.txt","size":236,"hash":"2679f255f02c7748c69d7058390862b484a8c848b6fca003ab93d612e34d7e54"},{"path":"examples/thread_prov/README.md","size":18646,"hash":"e8543919fa40a49fb1a2dbf0ef9ba796bb1ff2ef46235ee0c9e71b782772a0bc"},{"path":"examples/thread_prov/partitions.csv","size":268,"hash":"11bd2719678c09eef468e34eee8005b73ce3dcf91c752250d5e661668d6eb045"},{"path":"examples/thread_prov/sdkconfig.defaults","size":787,"hash":"62820996198757bf033a5a3a8f0dea10b0c3c277661239ea2a514a65d602eeb5"},{"path":"examples/thread_prov/sdkconfig.defaults.esp32","size":139,"hash":"c024d7b78bc1193adfa1ff5ad20e84f9ef2319e538458acfa27ad7bc01a406e0"},{"path":"examples/wifi_prov/CMakeLists.txt","size":255,"hash":"4fb16b56b99115b874222a73a668f4f13c8dc5cf08efd6651cef4fd00f20d14d"},{"path":"examples/wifi_prov/README.md","size":19839,"hash":"5d6c729a67055b18eca4492e1f0302671b4150fd41104df8b70cc9a02eff27f3"},{"path":"examples/wifi_prov/partitions.csv","size":268,"hash":"11bd2719678c09eef468e34eee8005b73ce3dcf91c752250d5e661668d6eb045"},{"path":"examples/wifi_prov/sdkconfig.defaults","size":288,"hash":"b10bac356376f16372c790388c93f03231db229ead17ff4bded75ec7321c5c19"},{"path":"examples/wifi_prov/sdkconfig.defaults.esp32","size":139,"hash":"c024d7b78bc1193adfa1ff5ad20e84f9ef2319e538458acfa27ad7bc01a406e0"},{"path":"examples/wifi_prov/main/CMakeLists.txt","size":79,"hash":"149bc0c91991870276494f6fec9900170bc04720874e1c42821909d55e47cf59"},{"path":"examples/wifi_prov/main/Kconfig.projbuild","size":4233,"hash":"b9e6e9cca983d5429f395fbcbe4e221503888626bcc77121b6b6a6b346919546"},{"path":"examples/wifi_prov/main/app_main.c","size":24695,"hash":"67e786402341300108a3667f6dad271193e438d558c2d251ec7e08d688aa8378"},{"path":"examples/wifi_prov/main/idf_component.yml","size":120,"hash":"ef5477fd7b1b470291c1cab237959c3132ff4006ff29bf2d4fadc0f98cebcd8f"},{"path":"examples/thread_prov/main/CMakeLists.txt","size":79,"hash":"149bc0c91991870276494f6fec9900170bc04720874e1c42821909d55e47cf59"},{"path":"examples/thread_prov/main/Kconfig.projbuild","size":3013,"hash":"b2281ec62e33d78f9e3a64e0f0c89f804f86e96eb007920f53248992b0edc634"},{"path":"examples/thread_prov/main/app_main.c","size":21258,"hash":"3152208c0eef7570f947e9f84343dc7ec4cb91054b538047a2a2639bdc39bdaf"},{"path":"examples/thread_prov/main/esp_ot_config.h","size":3232,"hash":"3f40bb64ccb3f3489af8e071abebf6a79cc9181e6ec9e7e8a5753ed5e820f284"},{"path":"examples/thread_prov/main/idf_component.yml","size":123,"hash":"06f83592c6f2a0a2e01de36fa633cdb7f7966977241ea5e956c7ff3d5e309d58"}]} \ No newline at end of file diff --git a/components/network_provisioning/CMakeLists.txt b/components/network_provisioning/CMakeLists.txt new file mode 100644 index 0000000..2c9d3af --- /dev/null +++ b/components/network_provisioning/CMakeLists.txt @@ -0,0 +1,40 @@ +idf_build_get_property(target IDF_TARGET) + +if(${target} STREQUAL "linux") + return() # This component is not supported by the POSIX/Linux simulator +endif() + +set(srcs "src/network_config.c" + "src/network_scan.c" + "src/network_ctrl.c" + "src/manager.c" + "src/handlers.c" + "src/scheme_console.c" + "proto-c/network_config.pb-c.c" + "proto-c/network_scan.pb-c.c" + "proto-c/network_ctrl.pb-c.c" + "proto-c/network_constants.pb-c.c") + +if((CONFIG_ESP_WIFI_ENABLED OR CONFIG_ESP_WIFI_REMOTE_ENABLED) AND CONFIG_ESP_WIFI_SOFTAP_SUPPORT) + list(APPEND srcs "src/scheme_softap.c") +endif() + +if(CONFIG_BT_ENABLED) + if(CONFIG_BT_BLUEDROID_ENABLED OR CONFIG_BT_NIMBLE_ENABLED) + list(APPEND srcs + "src/scheme_ble.c") + endif() +endif() + +set(priv_requires protobuf-c bt esp_timer esp_wifi openthread) + +# For IDF < 6.0, use IDF's built-in json component; for IDF >= 6.0 use managed cJSON component +if("${IDF_VERSION_MAJOR}" VERSION_LESS "6") + list(APPEND priv_requires json) +endif() + +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS include + PRIV_INCLUDE_DIRS src proto-c + REQUIRES lwip protocomm + PRIV_REQUIRES ${priv_requires}) diff --git a/components/network_provisioning/Kconfig b/components/network_provisioning/Kconfig new file mode 100644 index 0000000..ccded35 --- /dev/null +++ b/components/network_provisioning/Kconfig @@ -0,0 +1,88 @@ +menu "Network Provisioning Manager" + + choice NETWORK_PROV_NETWORK_TYPE + prompt "Network Type" + default NETWORK_PROV_NETWORK_TYPE_WIFI if (ESP_WIFI_ENABLED || ESP_WIFI_REMOTE_ENABLED) + default NETWORK_PROV_NETWORK_TYPE_THREAD if !ESP_WIFI_ENABLE && OPENTHREAD_ENABLED + + config NETWORK_PROV_NETWORK_TYPE_WIFI + bool "Network Type - Wi-Fi" + depends on ESP_WIFI_ENABLED || ESP_WIFI_REMOTE_ENABLED + + config NETWORK_PROV_NETWORK_TYPE_THREAD + bool "Network Type - Thread" + depends on OPENTHREAD_ENABLED + + endchoice + + config NETWORK_PROV_SCAN_MAX_ENTRIES + int "Max Network Scan Result Entries" + default 16 + range 1 255 + help + This sets the maximum number of entries of network scan results that will be kept by the + provisioning manager + + config NETWORK_PROV_AUTOSTOP_TIMEOUT + int "Provisioning auto-stop timeout" + default 30 + range 5 600 + help + Time (in seconds) after which the network provisioning manager will auto-stop after connecting to + a network successfully. + + config NETWORK_PROV_BLE_BONDING + bool + prompt "Enable BLE bonding" + depends on BT_ENABLED + help + This option is applicable only when provisioning transport is BLE. Used to enable BLE bonding process + where the information from the pairing process will be stored on the devices. + + config NETWORK_PROV_BLE_SEC_CONN + bool + prompt "Enable BLE Secure connection flag" + depends on BT_NIMBLE_ENABLED + default y + help + Used to enable Secure connection support when provisioning transport is BLE. + + config NETWORK_PROV_BLE_FORCE_ENCRYPTION + bool + prompt "Force Link Encryption during characteristic Read / Write" + depends on BT_ENABLED + help + Used to enforce link encryption when attempting to read / write characteristic + + config NETWORK_PROV_BLE_NOTIFY + bool + prompt "Add support for Notification for provisioning BLE descriptors" + depends on BT_ENABLED + help + Used to enable support Notification in BLE descriptors of prov* characteristics + + config NETWORK_PROV_KEEP_BLE_ON_AFTER_PROV + bool "Keep BT on after provisioning is done" + depends on BT_ENABLED + select ESP_PROTOCOMM_KEEP_BLE_ON_AFTER_BLE_STOP + + config NETWORK_PROV_DISCONNECT_AFTER_PROV + bool "Terminate connection after provisioning is done" + depends on NETWORK_PROV_KEEP_BLE_ON_AFTER_PROV + default y + select ESP_PROTOCOMM_DISCONNECT_AFTER_BLE_STOP + + choice NETWORK_PROV_WIFI_STA_SCAN_METHOD + bool "Wifi Provisioning Scan Method" + depends on NETWORK_PROV_NETWORK_TYPE_WIFI + default NETWORK_PROV_WIFI_STA_ALL_CHANNEL_SCAN + config NETWORK_PROV_WIFI_STA_ALL_CHANNEL_SCAN + bool "All Channel Scan" + help + Scan will end after scanning the entire channel. This option is useful in Mesh WiFi Systems. + config NETWORK_PROV_WIFI_STA_FAST_SCAN + bool "Fast Scan" + help + Scan will end after an AP matching with the SSID has been detected. + endchoice +endmenu diff --git a/components/network_provisioning/LICENSE b/components/network_provisioning/LICENSE new file mode 100644 index 0000000..d613ddc --- /dev/null +++ b/components/network_provisioning/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 Piyush Shah + + 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. diff --git a/components/network_provisioning/README.md b/components/network_provisioning/README.md new file mode 100644 index 0000000..378eccc --- /dev/null +++ b/components/network_provisioning/README.md @@ -0,0 +1,9 @@ +# Network Provisioning component + +[![Component Registry](https://components.espressif.com/components/espressif/network_provisioning/badge.svg)](https://components.espressif.com/components/espressif/network_provisioning) + +The network provisioning component provides APIs that control the network provisioning service for receiving and configuring network credentials via secure [Protocol Communication](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/provisioning/protocomm.html) sessions. + +It currently supports both Wi-Fi and Thread network provisioning: +- Provision Wi-Fi credentials over SoftAP or Bluetooth LE +- Provision Thread credentials over Bluetooth LE diff --git a/components/network_provisioning/examples/README.md b/components/network_provisioning/examples/README.md new file mode 100644 index 0000000..88827ec --- /dev/null +++ b/components/network_provisioning/examples/README.md @@ -0,0 +1,11 @@ +# Provisioning Application Examples + +This primarily consists of two examples `wifi_prov` and `thread_prov`. + +* wifi_prov + Abstracts out most of the complexity of Wi-Fi provisioning and allows easy switching between the SoftAP (using HTTP) and BLE transports. It also demonstrates how applications can register and use additional custom data endpoints. + +* thread_prov + Abstracts out most of the complexity of Thread provisioning over BLE transport. It also demonstrates how applications can register and use additional custom data endpoints. + +Provisioning applications are available for `Linux / Windows / macOS` platform as `esp_prov.py` [script](../tool/esp_prov/esp_prov.py) diff --git a/components/network_provisioning/examples/thread_prov/CMakeLists.txt b/components/network_provisioning/examples/thread_prov/CMakeLists.txt new file mode 100644 index 0000000..d5820b3 --- /dev/null +++ b/components/network_provisioning/examples/thread_prov/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(thread_prov) diff --git a/components/network_provisioning/examples/thread_prov/README.md b/components/network_provisioning/examples/thread_prov/README.md new file mode 100644 index 0000000..48ac778 --- /dev/null +++ b/components/network_provisioning/examples/thread_prov/README.md @@ -0,0 +1,375 @@ +| Supported Targets | ESP32-C6 | ESP32-H2 | +| ----------------- | -------- | -------- | + +# Network Provisioning Manager Example for Thread Provisioning + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +`thread_prov` example demonstrates the usage of `network_provisioning` manager component for building a Thread provisioning application. + +For this example, Bluetooth LE is chosen as the mode of transport, over which the provisioning related communication is to take place. NimBLE has been configured as the host. + +In the provisioning process the device is configured as a Thread FTD with specified dataset. Once configured, the device will retain the Thread configuration, until a flash erase is performed. + +Right after the provisioning is complete, Bluetooth LE is turned off and disabled to free the memory used by the Bluetooth LE stack. Though, that is specific to this example, and the user can choose to keep Bluetooth LE stack intact in their own application. + +`thread_prov` uses the following components : +* `network_provisioning` : Provides provisioning manager, data structures and protocomm endpoint handlers for Thread configuration +* `protocomm` : For protocol based communication and secure session establishment +* `protobuf` : Google's protocol buffer library for serialization of protocomm data structures +* `bt` : ESP-IDF's Bluetooth LE stack for transport of protobuf packets + +This example can be used, as it is, for adding a provisioning service to any application intended for IoT. + +> Note: If you use this example code in your own project, in Bluetooth LE mode, then remember to enable the BT stack and BTDM BLE control settings in your SDK configuration (e.g. by using the `sdkconfig.defaults` file from this project). + +## Security Scheme + +The `protocomm` component is used for establishing secure communication channel at the time of provisioning. It supports two security schemes for establishing secure communication which are [Security 1 Scheme](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/provisioning/provisioning.html#security-1-scheme) and [Security 2 Scheme](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/provisioning/provisioning.html#security-2-scheme). The example uses `Security 2 Scheme` (latest) by default. + +## How to use example + +### Hardware Required + +Example should be able to run on any commonly available ESP32-H2/ESP32-C6 development board. + +### Script Required + +Currently, provisioning script is available for `Linux / Windows / macOS` platforms. + +#### Platform : Linux / Windows / macOS + +To install the dependency packages needed, please refer to the ESP-IDF examples [README file](https://github.com/espressif/esp-idf/blob/master/examples/README.md#running-test-python-script-ttfw). + +`esp_prov` supports Bluetooth LE and SoftAP transport for Linux, MacOS and Windows platforms. For Bluetooth LE, however, if dependencies are not met, the script falls back to console mode and requires another application through which the communication can take place. The `esp_prov` console will guide you through the provisioning process of locating the correct Bluetooth LE GATT services and characteristics, the values to write, and input read values. + +### Configure the project + +``` +idf.py menuconfig +``` + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` +I (445) app: Starting provisioning +I (1035) app: Provisioning started +I (1045) network_prov_mgr: Provisioning started with service name : PROV_F72E6B +``` + +Make sure to note down the Bluetooth LE device name (starting with `PROV_`) displayed in the serial monitor log (eg. PROV_F72E6B). This will depend on the MAC ID and will be unique for every device. + +In a separate terminal run the `esp_prov.py` script under [directory](../../tool/esp_prov/) (make sure to replace `dataset_tlvs` with the dataset of the Thread network to which the device is supposed to connect to after provisioning). Assuming default example configuration, which uses the protocomm security version 2: + +``` +python esp_prov.py --transport ble --service_name PROV_F72E6B --sec_ver 2 --sec2_username threadprov --sec2_pwd abcd1234 --dataset_tlvs +``` + +For security scheme 1 with PoP-based (proof-of-possession) authentication, the following command can be used: +``` +python esp_prov.py --transport ble --service_name PROV_F72E6B --sec_ver 1 --pop abcd1234 --dataset_tlvs +``` + +Above command will perform the provisioning steps, and the monitor log should display something like this : + +``` +I(125493) OPENTHREAD:[N] Mle-----------: Role disabled -> detached +I (125503) OT_STATE: netif up +I(125883) OPENTHREAD:[N] Mle-----------: Attach attempt 1, AnyPartition reattaching with Active Dataset +I(126793) OPENTHREAD:[N] Mle-----------: RLOC16 fffe -> a40d +I(126793) OPENTHREAD:[N] Mle-----------: Role detached -> child +I (126813) OT_STATE: Set dns server address: FDE6:8626:404B:2::808:808 +I (126823) network_prov_mgr: Thread attached +I (126823) app: Provisioning successful +I (126823) app: Hello World! +I (127833) app: Hello World! +I (128833) app: Hello World! +. +. +. +I (131883) network_prov_mgr: Provisioning stopped +. +. +. +I (52355) app: Hello World! +I (53355) app: Hello World! +I (54355) app: Hello World! +I (55355) app: Hello World! +``` + +**Note:** For generating the credentials for security version 2 (`SRP6a` salt and verifier) for the device-side, the following example command can be used. The output can then directly be used in this example. + +The config option `CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE` should be enabled for the example and in `main/app_main.c`, the macro `EXAMPLE_PROV_SEC2_USERNAME` should be set to the same username used in the salt-verifier generation. + +```log +$ python esp_prov.py --transport ble --sec_ver 2 --sec2_gen_cred --sec2_username threadprov --sec2_pwd abcd1234 +==== Salt-verifier for security scheme 2 (SRP6a) ==== +static const char sec2_salt[] = { + 0x1f, 0xff, 0x29, 0xf5, 0xc7, 0x7e, 0x07, 0x48, 0x02, 0xe9, 0x93, 0x3e, 0xa3, 0xa2, 0x26, 0x73 +}; + +static const char sec2_verifier[] = { + 0xa7, 0x29, 0xe6, 0xa5, 0x4d, 0x20, 0x57, 0x71, 0x7c, 0x9d, 0x78, 0x2d, 0x0a, 0xb0, 0x9f, 0xec, + 0x7e, 0x8b, 0xab, 0xf5, 0xe6, 0xc3, 0x36, 0x41, 0x93, 0xfd, 0xb9, 0x49, 0x67, 0xe7, 0x7f, 0x79, + 0x66, 0x25, 0x2e, 0xac, 0x89, 0x19, 0xb2, 0xb3, 0x14, 0xb1, 0x16, 0xb0, 0xb0, 0xe4, 0x34, 0xd4, + 0x99, 0x40, 0x85, 0xa4, 0x99, 0x2b, 0x84, 0x21, 0xa1, 0xfb, 0x15, 0x48, 0x04, 0x91, 0xf5, 0x74, + . + . + . + 0x80, 0x86, 0xf4, 0xd5, 0x08, 0xbc, 0xb0, 0xdd, 0x6b, 0x50, 0xfa, 0xdd, 0x16, 0x10, 0x23, 0x4b +}; + +``` + +### QR Code Scanning + +Enabling `CONFIG_EXAMPLE_PROV_SHOW_QR` will display a QR code on the serial terminal, which can be scanned from the ESP Provisioning phone apps to start the Thread provisioning process. The ESP Provisioning phone apps will be released later. + +The monitor log should display something like this : + +``` +I (673) app: Provisioning started +I (673) app: Scan this QR code from the provisioning application for Provisioning. +I (693) QRCODE: Encoding below text with ECC LVL 0 & QR Code Version 10 +I (774) QRCODE: {"ver":"v1","name":"PROV_F72E6B","username":"threadprov","pop":"abcd1234","transport":"ble","network":"thread"} +> + █▀▀▀▀▀█ ▄▄▄█ ██▄▀█▀▀▀▀ ▀▄ ▄█▀█▄▀ █▀▀▀▀▀█ + █ ███ █ ▄▄██ ▀▄▄▄▀▀▀▄▀▄▀▀▄▄▄▄▄█▀▄ █ ███ █ + █ ▀▀▀ █ ▄ █ █▄ ▀ ▄ ▄▀▄ █▄███▀ █ ▀▀▀ █ + ▀▀▀▀▀▀▀ ▀ ▀ ▀ ▀▄▀ ▀ ▀▄▀ █ █▄█ █▄▀ ▀▀▀▀▀▀▀ + ▀▀▀██▄▀██▄▀██▀▀▀ ▀▄▄█▄▀█ ▀▄▀▀▀█▄ █▄▀▄█ ▀▄ + ██▄ ▀ ▀ ▀▄▄█ ▄█▄ ▀ ██ ▀█▀█▀█ █▀ ▀█▄▀█▄ + ▄█▀▀▄▄▀█▄▀▀ ▀▄▄▄▄ ▀▀▄▄▀▄▀▀▀▀▄▀▄▄ ▄▄▄▄ ▀▀▄ + ▀ █▄▄ ▀ ▀▀█▀▀█ ▄ ▀█▄█▄ ▀▀▀▀▄▀█ ▄█▄ ▄▀▀▄▄ + ▀█▄█▀▀▀ ▄ ▀█▀██ █▄█▄▄▀▄▀▄▀ ▀ ▄▄█▄▀▄█▀▀▄ + █ ▀██ ▀▀▄▄▄▄██▀▀ █▄ ▄█▄▄▄▀█▀▄▀█ █▄█▄▄█ ▀ + ▀▀▄ ▄ ▀▄ ▄█ ▄ ▀█▀ ▄ ▀ ▀▄ ▀█▀▄▀▄██ ▄█▄█▀█▄ + █▄▄ ▀▀ ▄▀ ▄ █▀ ▄ ▀██▄█▀ ▀ █▄ ▄▀▄█ ▀▄ + ▄▄ ██▄▀▄█▀█▄▀▀ ▀ ▀ ▄█▄▀▄ ▀ █▄▄ ▄▄█▄█▄ ▀▀█ + █▄█ ▄▄▀▀ ▀ ▄ ▄▀▄ ▀▄ █▀ ▀ ▀█ ▄ ▀▀▄ ▄ ▀█ + █ ▄▀▀▀▀█▄▄ ▀ ▄█▄ ▀ ▄▄ ▄█▀▀ ▀▄▄█ ▄█▄ ▀ ▄ + █ █▀█ ▀█▄ ██▀▀ ▄ ██▄██▄█▀ █▄▀█▄██▄ ▄ ▀ ▄ + ▀ ▀▀ ▀ ▄ ▀█▀█▄ ██ █ ▄ █ █ ▄█ ▄▀█▀▀▀█ ▀▄█ + █▀▀▀▀▀█ ▀▄ ▄██▀▀██▄▄ ▄ ▄█▀▄▀▄ ██ ▀ █ ▀ ▄ + █ ███ █ █▀▀ ▄▄▀▄▀ ▄▀ ▄█▄▄█ ▀ ██ █▀▀▀▀███▄ + █ ▀▀▀ █ ███ ▄▀ █▀▀█ █▄ ▄█ ▀▀ ▀ ▀██▀ ▀ ▄ + ▀▀▀▀▀▀▀ ▀ ▀▀ ▀ ▀ ▀▀ ▀▀ ▀▀ ▀ ▀ ▀ + +I (1134) app: If QR code is not visible, copy paste the below URL in a browser. +https://espressif.github.io/esp-jumpstart/qrcode.html?data={"ver":"v1","name":"PROV_F72E6B","username":"threadprov","pop":"abcd1234","transport":"ble","network":"thread"} + +``` + +### Thread Scanning + +Provisioning manager also supports providing real-time Thread scan results (performed on the device) during provisioning. This allows the client side applications to choose the Thread network for which the device is to be configured. Various information about the visible Thread networks is available, like signal strength (RSSI) and link quality (LQI), etc. Also, the manager now provides capabilities information which can be used by client applications to determine availability of specific features (like `thread_scan`). + +When using the scan based provisioning, we don't need to specify the `--dataset_tlvs` fields explicitly: + +``` +python esp_prov.py --transport ble --service_name PROV_F72E6B --pop abcd1234 +``` + +See below the sample output from `esp_prov` tool on running above command: + +``` +Connecting... +Connected +Getting Services... +Security scheme determined to be : 1 + +==== Starting Session ==== +==== Session Established ==== + +==== Scanning Thread Networks ==== +++++ Scan process executed in 6.247516632080078 sec +++++ Scan results : 12 + +++++ Scan finished in 7.349079132080078 sec +==== Thread Scan results ==== +S.N. PAN ID EXT PAN ID NAME EXT ADDR CHN RSSI LQI +[ 1] 34121 9a6526ce2aaf4383 ST-34EW e2848e0e9e315357 11 -53 9 +[ 2] 14311 7e010e5a22beb040 OpenThread-37e7 02a2ab187a4fb728 21 -47 10 +[ 3] 14311 7e010e5a22beb040 OpenThread-37e7 22bfc4ba63cf3bb8 21 -44 9 +[ 4] 4660 dead00beef00cafe OpenThread-13b4 1e4d2bb3a614163f 22 -40 10 +[ 5] 48989 516f754f983e50c6 OpenThread-bf5d be64e2845dc1d21a 22 -46 9 +[ 6] 4660 dead00beef00cafe OpenThread-79c0 3e19ed4f89be20ee 22 -53 10 +[ 7] 31233 6c5105b3cb215393 qqqQQQQQQQQ 86ba2d8a2ded00d0 24 -47 8 +[ 8] 32231 0458ef52172c21d6 OpenThread-7de7 deca5eddda6da2a7 25 -50 10 +[ 9] 32231 0458ef52172c21d6 OpenThread-7de7 0e085d0f1d89db89 25 -45 10 +[10] 10299 07f592f684bc4266 MyHome1926416771 b268d0525d81f5c4 25 -59 9 +[11] 10299 07f592f684bc4266 MyHome1926416771 6ee0445b8d49d4b8 25 -56 9 +[12] 51344 7899e5214acf64db OpenThread-c890 b22f21bc5ce8a058 26 -40 10 +Select Network by number (0 to rescan) : 4 +Enter Thread network key string : + +==== Sending Thread Dataset to Target ==== +==== Thread Dataset sent successfully ==== + +==== Applying Thread Config to Target ==== +==== Apply config sent successfully ==== + +==== Thread connection state ==== +==== Thread state: Attaching ==== + +==== Thread connection state ==== +==== Thread state: Attaching ==== + +==== Thread connection state ==== +==== Thread state: Attached ==== +==== Provisioning was successful ==== + +``` + +### Interactive Provisioning + +`esp_prov` supports interactive provisioning. You can trigger the script with a simplified command and input the necessary details +(`Proof-of-possession` for security scheme 1 and `SRP6a username`, `SRP6a password` for security scheme 2) as the provisioning process advances. + +The command `python esp_prov.py --transport ble --sec_ver 2` gives out the following sample output: + +``` +Discovering... +==== BLE Discovery results ==== +S.N. Name Address +. +. +. +[35] PROV_F73E7E 60:55:F9:F7:3E:7E +[36] 62-37-56-08-AA-64 62:37:56:08:AA:64 +. +. +. +Select device by number (0 to rescan) : 35 +Connecting... +Getting Services.. +Security Scheme 2 - SRP6a Username required: threadprov +Security Scheme 2 - SRP6a Password required: + +==== Starting Session ==== +==== Session Established ==== + +==== Scanning Thread Networks ==== +++++ Scan process executed in 6.247041702270508 sec +++++ Scan results : 14 + +++++ Scan finished in 7.40191650390625 sec +==== Thread Scan results ==== +S.N. PAN ID EXT PAN ID NAME EXT ADDR CHN RSSI LQI +[ 1] 34121 9a6526ce2aaf4383 ST-34EW e2848e0e9e315357 11 -53 9 +[ 2] 14311 7e010e5a22beb040 OpenThread-37e7 02a2ab187a4fb728 21 -45 9 +[ 3] 14311 7e010e5a22beb040 OpenThread-37e7 22bfc4ba63cf3bb8 21 -40 10 +[ 4] 4660 dead00beef00cafe OpenThread-79c0 3e19ed4f89be20ee 22 -59 10 +[ 5] 4660 dead00beef00cafe OpenThread-13b4 1e4d2bb3a614163f 22 -43 9 +[ 6] 48989 516f754f983e50c6 OpenThread-bf5d be64e2845dc1d21a 22 -47 10 +[ 7] 23427 5b83dead5b83beef 5b83 8a1e30a60615c16c 24 -57 8 +[ 8] 31233 6c5105b3cb215393 qqqQQQQQQQQ 86ba2d8a2ded00d0 24 -48 10 +[ 9] 10299 07f592f684bc4266 MyHome1926416771 6ee0445b8d49d4b8 25 -55 9 +[10] 32231 0458ef52172c21d6 OpenThread-7de7 deca5eddda6da2a7 25 -50 10 +[11] 10299 07f592f684bc4266 MyHome1926416771 b268d0525d81f5c4 25 -55 9 +[12] 32231 0458ef52172c21d6 OpenThread-7de7 0e085d0f1d89db89 25 -47 10 +[13] 10299 07f592f684bc4266 MyHome1926416771 7e06f10b32fe678f 25 -54 10 +[14] 51344 7899e5214acf64db OpenThread-c890 b22f21bc5ce8a058 26 -42 10 +Select Network by number (0 to rescan) : 5 +Enter Thread network key string : + +==== Sending Thread Dataset to Target ==== +==== Thread Dataset sent successfully ==== + +==== Applying Thread Config to Target ==== +==== Apply config sent successfully ==== + +==== Thread connection state ==== +==== Thread state: Attaching ==== + +==== Thread connection state ==== +==== Thread state: Attaching ==== + +==== Thread connection state ==== +==== Thread state: Attached ==== +==== Provisioning was successful ==== +``` + +### Sending Custom Data + +The provisioning manager allows applications to send some custom data during provisioning, which may be +required for some other operations like connecting to some cloud service. This is achieved by creating +and registering additional endpoints using the below APIs + +``` +network_prov_mgr_endpoint_create(); +network_prov_mgr_endpoint_register(); +``` + +In this particular example, we have added an endpoint named "custom-data" which can be tested +by passing the `--custom_data ` option to the esp\_prov tool. Following output is +expected on success: + +``` +==== Sending Custom data to esp32 ==== +CustomData response: SUCCESS +``` + +## Troubleshooting + +### Provisioning failed + +It is possible that the Thread dataset provided were incorrect, or the device was not able to establish connection to the network, in which the the `esp_prov` script will notify failure (with reason). Serial monitor log will display the failure along with disconnect reason : + +``` +E (367015) app: Provisioning failed! + Reason : Thread network not found + Please reset to factory and retry provisioning +``` + +Once dataset have been applied, even though wrong dataset were provided, the device will no longer go into provisioning mode on subsequent reboots until NVS is erased (see following section). + +### Provisioning does not start + +If the serial monitor log shows the following : + +``` +I (465) app: Already provisioned, enabling netif and starting Thread +``` + +it means either the device has been provisioned earlier with or without success (e.g. scenario covered in above section), or that the Thread dataset was already set by some other application flashed previously onto your device. + +To fix this we simple need to erase the NVS partition from flash. First we need to find out its address and size. This can be seen from the monitor log on the top right after reboot. + +``` +I (47) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (58) boot: 0 nvs WiFi data 01 02 00009000 00006000 +I (65) boot: 1 phy_init RF data 01 01 0000f000 00001000 +I (73) boot: 2 factory factory app 00 00 00010000 00124f80 +I (80) boot: End of partition table +``` + +Now erase NVS partition by running the following commands : + +``` +$IDF_PATH/components/esptool_py/esptool/esptool.py erase_region 0x9000 0x6000 +``` + +### Bluetooth Pairing Request during provisioning + +ESP-IDF now has functionality to enforce link encryption requirement while performing GATT write on characteristics of provisioning service. This will however result in a pairing pop-up dialog, if link is not encrypted. This feature is disabled by default. In order to enable this feature, please set `CONFIG_WIFI_PROV_BLE_FORCE_ENCRYPTION=y` in the sdkconfig or select the configuration using "idf.py menuconfig" . + +``` +Component Config --> Network Provisioning Manager --> Force Link Encryption during Characteristic Read/Write + +``` +Recompiling the application with above changes should suffice to enable this functionality. + diff --git a/components/network_provisioning/examples/thread_prov/main/CMakeLists.txt b/components/network_provisioning/examples/thread_prov/main/CMakeLists.txt new file mode 100644 index 0000000..61fac40 --- /dev/null +++ b/components/network_provisioning/examples/thread_prov/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS ".") diff --git a/components/network_provisioning/examples/thread_prov/main/Kconfig.projbuild b/components/network_provisioning/examples/thread_prov/main/Kconfig.projbuild new file mode 100644 index 0000000..7b778d4 --- /dev/null +++ b/components/network_provisioning/examples/thread_prov/main/Kconfig.projbuild @@ -0,0 +1,82 @@ +menu "Example Configuration" + + choice EXAMPLE_PROV_SECURITY_VERSION + bool "Protocomm security version" + default EXAMPLE_PROV_SECURITY_VERSION_2 + help + Network provisioning component offers 3 security versions. + The example offers a choice between security version 1 and 2. + + config EXAMPLE_PROV_SECURITY_VERSION_1 + bool "Security version 1" + select ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1 + + config EXAMPLE_PROV_SECURITY_VERSION_2 + bool "Security version 2" + select ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2 + endchoice + + choice EXAMPLE_PROV_MODE + bool "Security version 2 mode" + depends on EXAMPLE_PROV_SECURITY_VERSION_2 + default EXAMPLE_PROV_SEC2_DEV_MODE + + config EXAMPLE_PROV_SEC2_DEV_MODE + bool "Security version 2 development mode" + depends on EXAMPLE_PROV_SECURITY_VERSION_2 + help + This enables the development mode for + security version 2. + Please note that this mode is NOT recommended for production purpose. + + config EXAMPLE_PROV_SEC2_PROD_MODE + bool "Security version 2 production mode" + depends on EXAMPLE_PROV_SECURITY_VERSION_2 + help + This enables the production mode for + security version 2. + endchoice + + config EXAMPLE_PROV_TRANSPORT + int + default 1 if EXAMPLE_PROV_TRANSPORT_BLE + + config EXAMPLE_RESET_PROVISIONED + bool + default n + prompt "Reset provisioned status of the device" + help + This erases the NVS to reset provisioned status of the device on every reboot. + Provisioned status is determined by the Wi-Fi STA configuration, saved on the NVS. + + config EXAMPLE_RESET_PROV_MGR_ON_FAILURE + bool + default y + prompt "Reset provisioned credentials and state machine after session failure" + help + Enable resetting provisioned credentials and state machine after session failure. + This will restart the provisioning service after retries are exhausted. + + config EXAMPLE_PROV_MGR_MAX_RETRY_CNT + int + default 5 + prompt "Max retries before resetting provisioning state machine" + depends on EXAMPLE_RESET_PROV_MGR_ON_FAILURE + help + Set the Maximum retry to avoid reconnecting to an inexistent AP or if credentials + are misconfigured. Provisioned credentials are erased and internal state machine + is reset after this threshold is reached. + + config EXAMPLE_PROV_SHOW_QR + bool "Show provisioning QR code" + default y + help + Show the QR code for provisioning. + + config EXAMPLE_REPROVISIONING + bool "Re-provisioning" + help + Enable re-provisioning - allow the device to provision for new credentials + after previous successful provisioning. + +endmenu diff --git a/components/network_provisioning/examples/thread_prov/main/app_main.c b/components/network_provisioning/examples/thread_prov/main/app_main.c new file mode 100644 index 0000000..ecb7f41 --- /dev/null +++ b/components/network_provisioning/examples/thread_prov/main/app_main.c @@ -0,0 +1,495 @@ +/* Network Provisioning Manager Example for Thread network + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include + +#include +#include +#include + +#include +#include +#include "esp_openthread_cli.h" +#include "esp_openthread_netif_glue.h" +#include "esp_openthread_types.h" +#include +#include +#include +#include +#include +#include + +#include + +#include "openthread/cli.h" +#include "openthread/instance.h" +#include "openthread/logging.h" +#include "openthread/tasklet.h" +#include "openthread/thread.h" + +#include + +#include "qrcode.h" + +static const char *TAG = "app"; + +#if CONFIG_EXAMPLE_PROV_SECURITY_VERSION_2 +#if CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE +#define EXAMPLE_PROV_SEC2_USERNAME "threadprov" +#define EXAMPLE_PROV_SEC2_PWD "abcd1234" + +/* This salt,verifier has been generated for username = "threadprov" and password = "abcd1234" + * IMPORTANT NOTE: For production cases, this must be unique to every device + * and should come from device manufacturing partition.*/ +static const char sec2_salt[] = { + 0x1f, 0xff, 0x29, 0xf5, 0xc7, 0x7e, 0x07, 0x48, 0x02, 0xe9, 0x93, 0x3e, 0xa3, 0xa2, 0x26, 0x73 +}; + +static const char sec2_verifier[] = { + 0xa7, 0x29, 0xe6, 0xa5, 0x4d, 0x20, 0x57, 0x71, 0x7c, 0x9d, 0x78, 0x2d, 0x0a, 0xb0, 0x9f, 0xec, + 0x7e, 0x8b, 0xab, 0xf5, 0xe6, 0xc3, 0x36, 0x41, 0x93, 0xfd, 0xb9, 0x49, 0x67, 0xe7, 0x7f, 0x79, + 0x66, 0x25, 0x2e, 0xac, 0x89, 0x19, 0xb2, 0xb3, 0x14, 0xb1, 0x16, 0xb0, 0xb0, 0xe4, 0x34, 0xd4, + 0x99, 0x40, 0x85, 0xa4, 0x99, 0x2b, 0x84, 0x21, 0xa1, 0xfb, 0x15, 0x48, 0x04, 0x91, 0xf5, 0x74, + 0x95, 0x8a, 0x88, 0xd4, 0x4e, 0x25, 0xf6, 0xf3, 0x8e, 0x5c, 0xf9, 0x3c, 0xda, 0xbb, 0x4f, 0xa2, + 0x47, 0xe1, 0x01, 0x8f, 0x1c, 0xf5, 0xe0, 0x34, 0x41, 0x0c, 0x88, 0x76, 0x46, 0xd0, 0x16, 0xd9, + 0xfa, 0x57, 0x3d, 0x78, 0x46, 0xf1, 0xcb, 0xb1, 0x05, 0x16, 0xab, 0xf7, 0xbf, 0x9d, 0xeb, 0x05, + 0x2e, 0xc1, 0xd5, 0xe1, 0xde, 0x92, 0xe6, 0x20, 0x5f, 0xe4, 0x27, 0xda, 0xe3, 0x59, 0x91, 0x27, + 0x7b, 0x40, 0x83, 0x4c, 0xe8, 0xb5, 0xe0, 0x75, 0xe6, 0xbf, 0x26, 0xa9, 0x67, 0x06, 0xa3, 0x15, + 0x2d, 0x20, 0x81, 0xd5, 0x2a, 0x2e, 0x30, 0x84, 0xdf, 0xa2, 0x82, 0x62, 0xc4, 0x47, 0x25, 0xb6, + 0x93, 0x73, 0x87, 0x3c, 0xa7, 0x57, 0x2a, 0x47, 0x96, 0x1d, 0x89, 0xce, 0x49, 0xc6, 0x9d, 0x4f, + 0x6b, 0x39, 0x38, 0x67, 0xbb, 0x85, 0x24, 0xdf, 0xcd, 0xf5, 0xf1, 0x9f, 0x0a, 0x9e, 0x1c, 0x31, + 0xfa, 0xf1, 0x01, 0xa5, 0x30, 0xf0, 0xcb, 0x5e, 0x1c, 0xd6, 0xa0, 0x11, 0x3f, 0xf8, 0xdd, 0x07, + 0x09, 0x53, 0x62, 0x9f, 0x76, 0x5c, 0x69, 0xd1, 0x5e, 0x5b, 0xc7, 0xb0, 0x0e, 0x53, 0xeb, 0x8c, + 0x67, 0x88, 0xc7, 0x45, 0xc0, 0x26, 0xd9, 0xfa, 0xf8, 0x63, 0x0c, 0x64, 0xcb, 0x9e, 0xf4, 0x1b, + 0xb3, 0xfd, 0x78, 0x0c, 0x47, 0x0f, 0x66, 0xf3, 0xf7, 0xcd, 0xe9, 0xc6, 0x36, 0xa5, 0x58, 0xe5, + 0x9d, 0x31, 0x53, 0xb2, 0xe4, 0x8e, 0xdd, 0xd0, 0x8d, 0x13, 0xe8, 0xc6, 0x96, 0x60, 0x30, 0x50, + 0xbc, 0xef, 0xce, 0xbc, 0x23, 0xe3, 0x60, 0x63, 0x54, 0x11, 0x24, 0xba, 0x68, 0x47, 0x6a, 0xb2, + 0x5e, 0x70, 0xa3, 0xa6, 0xc3, 0xad, 0x58, 0xd1, 0x3b, 0xce, 0xce, 0x90, 0xe9, 0x90, 0x7e, 0x7a, + 0xfb, 0x4f, 0x69, 0xa2, 0x81, 0xdf, 0x15, 0xec, 0xa7, 0x8f, 0xd6, 0x5a, 0xb8, 0x1f, 0x42, 0x18, + 0x0e, 0x4f, 0x3e, 0x45, 0x2d, 0x08, 0xf2, 0xd6, 0x51, 0x90, 0xef, 0x64, 0x77, 0xee, 0xcc, 0x3c, + 0xb4, 0xa6, 0x6f, 0x0b, 0x10, 0xb2, 0xce, 0x31, 0x19, 0x10, 0x8d, 0x75, 0x8f, 0xa8, 0xa2, 0x6e, + 0x7a, 0x00, 0x92, 0x91, 0xe2, 0x16, 0xe3, 0x7a, 0xf9, 0x1d, 0x4e, 0x39, 0xe5, 0xd0, 0xd1, 0x7e, + 0x80, 0x86, 0xf4, 0xd5, 0x08, 0xbc, 0xb0, 0xdd, 0x6b, 0x50, 0xfa, 0xdd, 0x16, 0x10, 0x23, 0x4b +}; +#endif + +static esp_err_t example_get_sec2_salt(const char **salt, uint16_t *salt_len) +{ +#if CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE + ESP_LOGI(TAG, "Development mode: using hard coded salt"); + *salt = sec2_salt; + *salt_len = sizeof(sec2_salt); + return ESP_OK; +#elif CONFIG_EXAMPLE_PROV_SEC2_PROD_MODE + ESP_LOGE(TAG, "Not implemented!"); + return ESP_FAIL; +#endif +} + +static esp_err_t example_get_sec2_verifier(const char **verifier, uint16_t *verifier_len) +{ +#if CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE + ESP_LOGI(TAG, "Development mode: using hard coded verifier"); + *verifier = sec2_verifier; + *verifier_len = sizeof(sec2_verifier); + return ESP_OK; +#elif CONFIG_EXAMPLE_PROV_SEC2_PROD_MODE + /* This code needs to be updated with appropriate implementation to provide verifier */ + ESP_LOGE(TAG, "Not implemented!"); + return ESP_FAIL; +#endif +} +#endif + +/* Signal Thread events on this event-group */ +const int THREAD_ATTACHED_EVENT = BIT0; +static EventGroupHandle_t thread_event_group; + +#define PROV_QR_VERSION "v1" +#define PROV_TRANSPORT_BLE "ble" +#define QRCODE_BASE_URL "https://espressif.github.io/esp-jumpstart/qrcode.html" + +/* Event handler for catching system events */ +static void event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_base == NETWORK_PROV_EVENT) { + switch (event_id) { + case NETWORK_PROV_START: + ESP_LOGI(TAG, "Provisioning started"); + break; + case NETWORK_PROV_THREAD_DATASET_RECV: { + // TODO Log thread dataset + break; + } + case NETWORK_PROV_THREAD_DATASET_FAIL: { + network_prov_thread_fail_reason_t *reason = (network_prov_thread_fail_reason_t *)event_data; + ESP_LOGE(TAG, "Provisioning failed!\n\tReason : %s" + "\n\tPlease reset to factory and retry provisioning", + (*reason == NETWORK_PROV_THREAD_DATASET_INVALID) ? + "Invalid Thread dataset" : "Thread network not found"); + break; + } + case NETWORK_PROV_THREAD_DATASET_SUCCESS: + ESP_LOGI(TAG, "Provisioning successful"); + break; + case NETWORK_PROV_END: + /* De-initialize manager once provisioning is finished */ + network_prov_mgr_deinit(); + break; + default: + break; + } + } else if (event_base == OPENTHREAD_EVENT && event_id == OPENTHREAD_EVENT_ATTACHED) { + xEventGroupSetBits(thread_event_group, THREAD_ATTACHED_EVENT); + } else if (event_base == PROTOCOMM_SECURITY_SESSION_EVENT) { + switch (event_id) { + case PROTOCOMM_SECURITY_SESSION_SETUP_OK: + ESP_LOGI(TAG, "Secured session established!"); + break; + case PROTOCOMM_SECURITY_SESSION_INVALID_SECURITY_PARAMS: + ESP_LOGE(TAG, "Received invalid security parameters for establishing secure session!"); + break; + case PROTOCOMM_SECURITY_SESSION_CREDENTIALS_MISMATCH: + ESP_LOGE(TAG, "Received incorrect username and/or PoP for establishing secure session!"); + break; + default: + break; + } + } +} + +static void get_device_service_name(char *service_name, size_t max) +{ + uint8_t ieee802154_mac[8]; + const char *ssid_prefix = "PROV_"; + esp_read_mac(ieee802154_mac, ESP_MAC_IEEE802154); + snprintf(service_name, max, "%s%02X%02X%02X", + ssid_prefix, ieee802154_mac[5], ieee802154_mac[6], ieee802154_mac[7]); +} + +/* Handler for the optional provisioning endpoint registered by the application. + * The data format can be chosen by applications. Here, we are using plain ascii text. + * Applications can choose to use other formats like protobuf, JSON, XML, etc. + */ +esp_err_t custom_prov_data_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, + uint8_t **outbuf, ssize_t *outlen, void *priv_data) +{ + if (inbuf) { + ESP_LOGI(TAG, "Received data: %.*s", inlen, (char *)inbuf); + } + char response[] = "SUCCESS"; + *outbuf = (uint8_t *)strdup(response); + if (*outbuf == NULL) { + ESP_LOGE(TAG, "System out of memory"); + return ESP_ERR_NO_MEM; + } + *outlen = strlen(response) + 1; /* +1 for NULL terminating byte */ + + return ESP_OK; +} + +static void network_prov_print_qr(const char *name, const char *username, const char *pop, const char *transport) +{ + if (!name || !transport) { + ESP_LOGW(TAG, "Cannot generate QR code payload. Data missing."); + return; + } + char payload[150] = {0}; + if (pop) { +#if CONFIG_EXAMPLE_PROV_SECURITY_VERSION_1 + snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \ + ",\"pop\":\"%s\",\"transport\":\"%s\"}", + PROV_QR_VERSION, name, pop, transport); +#elif CONFIG_EXAMPLE_PROV_SECURITY_VERSION_2 + snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \ + ",\"username\":\"%s\",\"pop\":\"%s\",\"transport\":\"%s\"}", + PROV_QR_VERSION, name, username, pop, transport); +#endif + } else { + snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \ + ",\"transport\":\"%s\"}", + PROV_QR_VERSION, name, transport); + } +#ifdef CONFIG_EXAMPLE_PROV_SHOW_QR + ESP_LOGI(TAG, "Scan this QR code from the provisioning application for Provisioning."); + esp_qrcode_config_t cfg = ESP_QRCODE_CONFIG_DEFAULT(); + esp_qrcode_generate(&cfg, payload); + //TODO: Add the network protocol type to the QR code payload +#endif /* CONFIG EXAMPLE_PROV_SHOW_QR */ + ESP_LOGI(TAG, "If QR code is not visible, copy paste the below URL in a browser.\n%s?data=%s", QRCODE_BASE_URL, payload); +} + +static esp_netif_t *init_openthread_netif(const esp_openthread_platform_config_t *config) +{ + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); + esp_netif_t *netif = esp_netif_new(&cfg); + assert(netif != NULL); + ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config))); + + return netif; +} + +static void ot_task_worker(void *aContext) +{ + esp_openthread_platform_config_t config = { + .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), + .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), + .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), + }; + + // Initialize the OpenThread stack + ESP_ERROR_CHECK(esp_openthread_init(&config)); +#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC + // The OpenThread log level directly matches ESP log level + (void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); +#endif +#if CONFIG_OPENTHREAD_CLI + // Initialize the OpenThread cli + esp_openthread_cli_init(); +#endif + esp_netif_t *openthread_netif = init_openthread_netif(&config); + // Initialize the esp_netif bindings + esp_netif_set_default_netif(openthread_netif); + + // Run the main loop +#if CONFIG_OPENTHREAD_CLI + esp_openthread_cli_create_task(); +#endif + esp_openthread_launch_mainloop(); + + // Clean up + esp_netif_destroy(openthread_netif); + esp_openthread_netif_glue_deinit(); + + esp_vfs_eventfd_unregister(); + vTaskDelete(NULL); +} + +void app_main(void) +{ + /* Initialize NVS partition */ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + /* NVS partition was truncated + * and needs to be erased */ + ESP_ERROR_CHECK(nvs_flash_erase()); + + /* Retry nvs_flash_init */ + ESP_ERROR_CHECK(nvs_flash_init()); + } + + /* Initialize TCP/IP */ + ESP_ERROR_CHECK(esp_netif_init()); + + /* Initialize the event loop */ + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + thread_event_group = xEventGroupCreate(); + + /* Register our event handler for OpenThread and Provisioning related events */ + ESP_ERROR_CHECK(esp_event_handler_register(NETWORK_PROV_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_BLE + ESP_ERROR_CHECK(esp_event_handler_register(PROTOCOMM_TRANSPORT_BLE_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); +#endif + ESP_ERROR_CHECK(esp_event_handler_register(PROTOCOMM_SECURITY_SESSION_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(OPENTHREAD_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); + + esp_vfs_eventfd_config_t eventfd_config = { + .max_fds = 3, + }; + ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); + xTaskCreate(ot_task_worker, "ot_task", 6144, xTaskGetCurrentTaskHandle(), 5, NULL); + + /* Configuration for the provisioning manager */ + network_prov_mgr_config_t config = { + /* Use network_prov_scheme_ble as the Provisioning Scheme */ + .scheme = network_prov_scheme_ble, + + /* Any default scheme specific event handler that you would + * like to choose. Since our example application requires + * neither BT nor BLE, we can choose to release the associated + * memory once provisioning is complete, or not needed + * (in case when device is already provisioned). Choosing + * appropriate scheme specific event handler allows the manager + * to take care of this automatically. */ + .scheme_event_handler = NETWORK_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BLE + }; + + /* Initialize provisioning manager with the + * configuration parameters set above */ + ESP_ERROR_CHECK(network_prov_mgr_init(config)); + + bool provisioned = false; +#ifdef CONFIG_EXAMPLE_RESET_PROVISIONED + network_prov_mgr_reset_thread_provisioning(); +#else + /* Let's find out if the device is provisioned */ + ESP_ERROR_CHECK(network_prov_mgr_is_thread_provisioned(&provisioned)); + +#endif + /* If device is not yet provisioned start provisioning service */ + if (!provisioned) { + ESP_LOGI(TAG, "Starting provisioning"); + + /* What is the Device Service Name that we want + * This translates to : + * - device name when scheme is network_prov_scheme_ble + */ + char service_name[12]; + get_device_service_name(service_name, sizeof(service_name)); + +#ifdef CONFIG_EXAMPLE_PROV_SECURITY_VERSION_1 + /* What is the security level that we want (0, 1, 2): + * - NETWORK_PROV_SECURITY_0 is simply plain text communication. + * - NETWORK_PROV_SECURITY_1 is secure communication which consists of secure handshake + * using X25519 key exchange and proof of possession (pop) and AES-CTR + * for encryption/decryption of messages. + * - NETWORK_PROV_SECURITY_2 SRP6a based authentication and key exchange + * + AES-GCM encryption/decryption of messages + */ + network_prov_security_t security = NETWORK_PROV_SECURITY_1; + + /* Do we want a proof-of-possession (ignored if Security 0 is selected): + * - this should be a string with length > 0 + * - NULL if not used + */ + const char *pop = "abcd1234"; + + /* This is the structure for passing security parameters + * for the protocomm security 1. + */ + network_prov_security1_params_t *sec_params = pop; + + const char *username = NULL; + +#elif CONFIG_EXAMPLE_PROV_SECURITY_VERSION_2 + network_prov_security_t security = NETWORK_PROV_SECURITY_2; + /* The username must be the same one, which has been used in the generation of salt and verifier */ + +#if CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE + /* This pop field represents the password that will be used to generate salt and verifier. + * The field is present here in order to generate the QR code containing password. + * In production this password field shall not be stored on the device */ + const char *username = EXAMPLE_PROV_SEC2_USERNAME; + const char *pop = EXAMPLE_PROV_SEC2_PWD; +#elif CONFIG_EXAMPLE_PROV_SEC2_PROD_MODE + /* The username and password shall not be embedded in the firmware, + * they should be provided to the user by other means. + * e.g. QR code sticker */ + const char *username = NULL; + const char *pop = NULL; +#endif + /* This is the structure for passing security parameters + * for the protocomm security 2. + * If dynamically allocated, sec2_params pointer and its content + * must be valid till WIFI_PROV_END event is triggered. + */ + network_prov_security2_params_t sec2_params = {}; + + ESP_ERROR_CHECK(example_get_sec2_salt(&sec2_params.salt, &sec2_params.salt_len)); + ESP_ERROR_CHECK(example_get_sec2_verifier(&sec2_params.verifier, &sec2_params.verifier_len)); + + network_prov_security2_params_t *sec_params = &sec2_params; +#endif + /* What is the service key (could be NULL) + * This translates to : + * - simply ignored when scheme is network_prov_scheme_ble + */ + const char *service_key = NULL; + +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_BLE + /* This step is only useful when scheme is network_prov_scheme_ble. This will + * set a custom 128 bit UUID which will be included in the BLE advertisement + * and will correspond to the primary GATT service that provides provisioning + * endpoints as GATT characteristics. Each GATT characteristic will be + * formed using the primary service UUID as base, with different auto assigned + * 12th and 13th bytes (assume counting starts from 0th byte). The client side + * applications must identify the endpoints by reading the User Characteristic + * Description descriptor (0x2901) for each characteristic, which contains the + * endpoint name of the characteristic */ + uint8_t custom_service_uuid[] = { + /* LSB <--------------------------------------- + * ---------------------------------------> MSB */ + 0xb4, 0xdf, 0x5a, 0x1c, 0x3f, 0x6b, 0xf4, 0xbf, + 0xea, 0x4a, 0x82, 0x03, 0x04, 0x90, 0x1a, 0x02, + }; + + /* If your build fails with linker errors at this point, then you may have + * forgotten to enable the BT stack or BTDM BLE settings in the SDK (e.g. see + * the sdkconfig.defaults in the example project) */ + network_prov_scheme_ble_set_service_uuid(custom_service_uuid); +#endif /* CONFIG_EXAMPLE_PROV_TRANSPORT_BLE */ + + /* An optional endpoint that applications can create if they expect to + * get some additional custom data during provisioning workflow. + * The endpoint name can be anything of your choice. + * This call must be made before starting the provisioning. + */ + network_prov_mgr_endpoint_create("custom-data"); + + /* Do not stop and de-init provisioning even after success, + * so that we can restart it later. */ +#ifdef CONFIG_EXAMPLE_REPROVISIONING + network_prov_mgr_disable_auto_stop(1000); +#endif + /* Start provisioning service */ + ESP_ERROR_CHECK(network_prov_mgr_start_provisioning(security, (const void *) sec_params, service_name, service_key)); + + /* The handler for the optional endpoint created above. + * This call must be made after starting the provisioning, and only if the endpoint + * has already been created above. + */ + network_prov_mgr_endpoint_register("custom-data", custom_prov_data_handler, NULL); + + /* Uncomment the following to wait for the provisioning to finish and then release + * the resources of the manager. Since in this case de-initialization is triggered + * by the default event loop handler, we don't need to call the following */ + // network_prov_mgr_wait(); + // network_prov_mgr_deinit(); + /* Print QR code for provisioning */ + network_prov_print_qr(service_name, username, pop, PROV_TRANSPORT_BLE); + } else { + ESP_LOGI(TAG, "Already provisioned, enabling netif and starting Thread"); + + /* We don't need the manager as device is already provisioned, + * so let's release it's resources */ + network_prov_mgr_deinit(); + + otInstance *instance = esp_openthread_get_instance(); + (void)otIp6SetEnabled(instance, true); + (void)otThreadSetEnabled(instance, true); + } + + /* Wait for Thread connection */ + xEventGroupWaitBits(thread_event_group, THREAD_ATTACHED_EVENT, true, true, portMAX_DELAY); + + /* Start main application now */ +#if CONFIG_EXAMPLE_REPROVISIONING + while (1) { + for (int i = 0; i < 10; i++) { + ESP_LOGI(TAG, "Hello World!"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + + /* Resetting provisioning state machine to enable re-provisioning */ + network_prov_mgr_reset_thread_sm_state_for_reprovision(); + + /* Wait for thread connection */ + xEventGroupWaitBits(thread_event_group, THREAD_ATTACHED_EVENT, true, true, portMAX_DELAY); + } +#else + while (1) { + ESP_LOGI(TAG, "Hello World!"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +#endif + +} diff --git a/components/network_provisioning/examples/thread_prov/main/esp_ot_config.h b/components/network_provisioning/examples/thread_prov/main/esp_ot_config.h new file mode 100644 index 0000000..7f0762f --- /dev/null +++ b/components/network_provisioning/examples/thread_prov/main/esp_ot_config.h @@ -0,0 +1,67 @@ +/* Network Provisioning Manager Example for Thread network + * + * This example code is in the Public Domain (or CC0 licensed, at your option.) + * + * Unless required by applicable law or agreed to in writing, this + * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. + */ + +#pragma once + +#include "esp_openthread_types.h" + +#if SOC_IEEE802154_SUPPORTED +#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ + { \ + .radio_mode = RADIO_MODE_NATIVE, \ + } + +#else +#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ + { \ + .radio_mode = RADIO_MODE_UART_RCP, \ + .radio_uart_config = { \ + .port = 1, \ + .uart_config = \ + { \ + .baud_rate = 115200, \ + .data_bits = UART_DATA_8_BITS, \ + .parity = UART_PARITY_DISABLE, \ + .stop_bits = UART_STOP_BITS_1, \ + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ + .rx_flow_ctrl_thresh = 0, \ + .source_clk = UART_SCLK_DEFAULT, \ + }, \ + .rx_pin = 4, \ + .tx_pin = 5, \ + }, \ + } +#endif + +#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ + { \ + .host_connection_mode = HOST_CONNECTION_MODE_CLI_UART, \ + .host_uart_config = { \ + .port = 0, \ + .uart_config = \ + { \ + .baud_rate = 115200, \ + .data_bits = UART_DATA_8_BITS, \ + .parity = UART_PARITY_DISABLE, \ + .stop_bits = UART_STOP_BITS_1, \ + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ + .rx_flow_ctrl_thresh = 0, \ + .source_clk = UART_SCLK_DEFAULT, \ + }, \ + .rx_pin = UART_PIN_NO_CHANGE, \ + .tx_pin = UART_PIN_NO_CHANGE, \ + }, \ + } + +#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \ + { \ + .storage_partition_name = "nvs", \ + .netif_queue_size = 10, \ + .task_queue_size = 10, \ + } diff --git a/components/network_provisioning/examples/thread_prov/main/idf_component.yml b/components/network_provisioning/examples/thread_prov/main/idf_component.yml new file mode 100644 index 0000000..73acc95 --- /dev/null +++ b/components/network_provisioning/examples/thread_prov/main/idf_component.yml @@ -0,0 +1,6 @@ +dependencies: + espressif/network_provisioning: + version: ^1.0.0 + espressif/qrcode: + version: ^0.1.0 +version: 1.0.0 diff --git a/components/network_provisioning/examples/thread_prov/partitions.csv b/components/network_provisioning/examples/thread_prov/partitions.csv new file mode 100644 index 0000000..d4af98d --- /dev/null +++ b/components/network_provisioning/examples/thread_prov/partitions.csv @@ -0,0 +1,5 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, , 0x6000, +phy_init, data, phy, , 0x1000, +factory, app, factory, , 0x180000, diff --git a/components/network_provisioning/examples/thread_prov/sdkconfig.defaults b/components/network_provisioning/examples/thread_prov/sdkconfig.defaults new file mode 100644 index 0000000..aaf7e75 --- /dev/null +++ b/components/network_provisioning/examples/thread_prov/sdkconfig.defaults @@ -0,0 +1,31 @@ +# Override some defaults so BT stack is enabled +CONFIG_BT_ENABLED=y +CONFIG_BT_NIMBLE_ENABLED=y + +## For Bluedroid as binary is larger than default size +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" + +# mbedTLS +CONFIG_MBEDTLS_CMAC_C=y +CONFIG_MBEDTLS_SSL_PROTO_DTLS=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y +CONFIG_MBEDTLS_ECJPAKE_C=y + +# OpenThread +CONFIG_OPENTHREAD_ENABLED=y +CONFIG_OPENTHREAD_BORDER_ROUTER=n +CONFIG_OPENTHREAD_DNS64_CLIENT=y + +# Network type +CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD=y + +# LwIP +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096 +CONFIG_LWIP_IPV6_NUM_ADDRESSES=8 +CONFIG_LWIP_MULTICAST_PING=y +CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_CUSTOM=y + +# IEEE 802.15.4 +CONFIG_IEEE802154_ENABLED=y diff --git a/components/network_provisioning/examples/thread_prov/sdkconfig.defaults.esp32 b/components/network_provisioning/examples/thread_prov/sdkconfig.defaults.esp32 new file mode 100644 index 0000000..b8dc687 --- /dev/null +++ b/components/network_provisioning/examples/thread_prov/sdkconfig.defaults.esp32 @@ -0,0 +1,5 @@ +# ESP32 specific default configurations + +CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y +CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n +CONFIG_BTDM_CTRL_MODE_BTDM=n diff --git a/components/network_provisioning/examples/wifi_prov/CMakeLists.txt b/components/network_provisioning/examples/wifi_prov/CMakeLists.txt new file mode 100644 index 0000000..6df4dd7 --- /dev/null +++ b/components/network_provisioning/examples/wifi_prov/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +set(COMPONENTS main) +project(wifi_prov) diff --git a/components/network_provisioning/examples/wifi_prov/README.md b/components/network_provisioning/examples/wifi_prov/README.md new file mode 100644 index 0000000..b4dae62 --- /dev/null +++ b/components/network_provisioning/examples/wifi_prov/README.md @@ -0,0 +1,402 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | + +# Wi-Fi Provisioning Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +`wifi_prov` example demonstrates the usage of `network_provisioning` manager component for building a Wi-Fi provisioning application. + +For this example, Bluetooth LE is chosen as the default mode of transport, over which the provisioning related communication is to take place. NimBLE has been configured as the default host, but you can also switch to Bluedroid using menuconfig -> Components -> Bluetooth -> Bluetooth Host. + +> Note: Since ESP32-S2 does not support Bluetooth LE, the SoftAP will be the default mode of transport in that case. Even for ESP32, you can change to SoftAP transport from menuconfig. + +In the provisioning process the device is configured as a Wi-Fi station with specified credentials. Once configured, the device will retain the Wi-Fi configuration, until a flash erase is performed. + +Right after provisioning is complete, Bluetooth LE is turned off and disabled to free the memory used by the Bluetooth LE stack. Though, that is specific to this example, and the user can choose to keep Bluetooth LE stack intact in their own application. + +`wifi_prov` uses the following components : +* `network_provisioning` : Provides provisioning manager, data structures and protocomm endpoint handlers for Wi-Fi configuration +* `protocomm` : For protocol based communication and secure session establishment +* `protobuf` : Google's protocol buffer library for serialization of protocomm data structures +* `bt` : ESP-IDF's Bluetooth LE stack for transport of protobuf packets + +This example can be used, as it is, for adding a provisioning service to any application intended for IoT. + +> Note: If you use this example code in your own project, in Bluetooth LE mode, then remember to enable the BT stack and BTDM BLE control settings in your SDK configuration (e.g. by using the `sdkconfig.defaults` file from this project). + +## How to use example + +### Hardware Required + +Example should be able to run on any commonly available ESP32/ESP32-C2/ESP32-C3/ESP32-C6/ESP32-S2/ESP32-S3 development board. + +### Script Required + +Currently, Provisioning script is available for `Linux / Windows / macOS` platforms. + +#### Platform : Linux / Windows / macOS + +To install the dependency packages needed, please refer to the ESP-IDF examples [README file](https://github.com/espressif/esp-idf/blob/master/examples/README.md#running-test-python-script-ttfw). + +`esp_prov` supports Bluetooth LE and SoftAP transport for Linux, MacOS and Windows platforms. For Bluetooth LE, however, if dependencies are not met, the script falls back to console mode and requires another application through which the communication can take place. The `esp_prov` console will guide you through the provisioning process of locating the correct Bluetooth LE GATT services and characteristics, the values to write, and input read values. + +### Configure the project + +``` +idf.py menuconfig +``` +* Set the Bluetooth LE/Soft AP transport under "Example Configuration" options. ESP32-S2 will have only SoftAP option (SoftAP option cannot be used if IPv4 is disabled in lwIP) + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` +I (445) app: Starting provisioning +I (1035) app: Provisioning started +I (1045) network_prov_mgr: Provisioning started with service name : PROV_01B1E8 +``` + +Make sure to note down the Bluetooth LE device name (starting with `PROV_`) displayed in the serial monitor log (eg. PROV_01B1E8). This will depend on the MAC ID and will be unique for every device. + +In a separate terminal run the `esp_prov.py` script under [directory](../../tool/esp_prov) (make sure to replace `myssid` and `mypassword` with the credentials of the AP to which the device is supposed to connect to after provisioning). Assuming default example configuration, which uses the protocomm security version 2 with username and password authentication: + +``` +python esp_prov.py --transport ble --service_name PROV_01B1E8 --sec_ver 2 --sec2_username wifiprov --sec2_pwd abcd1234 --ssid myssid --passphrase mypassword +``` + +For security scheme 1 with PoP-based (proof-of-possession) authentication, the following command can be used: +``` +python esp_prov.py --transport ble --service_name PROV_01B1E8 --sec_ver 1 --pop abcd1234 --ssid myssid --passphrase mypassword +``` + +Above command will perform the provisioning steps, and the monitor log should display something like this : + +``` +I (39725) app: Received Wi-Fi credentials + SSID : myssid + Password : mypassword +. +. +. +I (45335) esp_netif_handlers: sta ip: 192.168.43.243, mask: 255.255.255.0, gw: 192.168.43.1 +I (45345) app: Provisioning successful +I (45345) app: Connected with IP Address:192.168.43.243 +I (46355) app: Hello World! +I (47355) app: Hello World! +I (48355) app: Hello World! +I (49355) app: Hello World! +. +. +. +I (52315) network_prov_mgr: Provisioning stopped +. +. +. +I (52355) app: Hello World! +I (53355) app: Hello World! +I (54355) app: Hello World! +I (55355) app: Hello World! +``` + +**Note:** For generating the credentials for security version 2 (`SRP6a` salt and verifier) for the device-side, the following example command can be used. The output can then directly be used in this example. + +The config option `CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE` should be enabled for the example and in `main/app_main.c`, the macro `EXAMPLE_PROV_SEC2_USERNAME` should be set to the same username used in the salt-verifier generation. + +```log +$ python esp_prov.py --transport softap --sec_ver 2 --sec2_gen_cred --sec2_username wifiprov --sec2_pwd abcd1234 +==== Salt-verifier for security scheme 2 (SRP6a) ==== +static const char sec2_salt[] = { + 0x03, 0x6e, 0xe0, 0xc7, 0xbc, 0xb9, 0xed, 0xa8, 0x4c, 0x9e, 0xac, 0x97, 0xd9, 0x3d, 0xec, 0xf4 +}; + +static const char sec2_verifier[] = { + 0x7c, 0x7c, 0x85, 0x47, 0x65, 0x08, 0x94, 0x6d, 0xd6, 0x36, 0xaf, 0x37, 0xd7, 0xe8, 0x91, 0x43, + 0x78, 0xcf, 0xfd, 0x61, 0x6c, 0x59, 0xd2, 0xf8, 0x39, 0x08, 0x12, 0x72, 0x38, 0xde, 0x9e, 0x24, + . + . + . + 0xe6, 0xf6, 0x53, 0xc8, 0x31, 0xa8, 0x78, 0xde, 0x50, 0x40, 0xf7, 0x62, 0xde, 0x36, 0xb2, 0xba +}; + +``` + +### QR Code Scanning + +Enabling `CONFIG_EXAMPLE_PROV_SHOW_QR` will display a QR code on the serial terminal, which can be scanned from the ESP Provisioning phone apps to start the Wi-Fi provisioning process. + +The monitor log should display something like this : + +``` +I (690) app: Provisioning started +I (690) app: Scan this QR code from the provisioning application for Provisioning. +I (700) QRCODE: Encoding below text with ECC LVL 0 & QR Code Version 10 +I (710) QRCODE: {"ver":"v1","name":"PROV_01B1E8","username":"wifiprov","pop":"abcd1234","transport":"ble","network":"wifi"} + + █▀▀▀▀▀█ ▄▀▀████▄██▀▀▀▀ ▀▄██▄ ▄▄▀ █▀▀▀▀▀█ + █ ███ █ ▄▀ ▀▀▀▄ █▀▀▀█▀▄▀█▄█▄ ▄▀▀ █ ███ █ + █ ▀▀▀ █ ▄█▄▀▄ █▄▀█ ▄ █▀▀ ▀▄███▀▀ █ ▀▀▀ █ + ▀▀▀▀▀▀▀ ▀▄▀ █ ▀▄▀ ▀ ▀▄▀ █ █ █ █▄▀ ▀▀▀▀▀▀▀ + ▀▀█▀▀ ▀█▀ ██▀▀ ▀▄▀ ▄▄ ▀█ ▀█▄██▄ █▄▀▄█ █▄ + ▀ █▀▀██▄█▀▀ ▄█ ▀ ▀██ ▀ █▀ ▄ █▀▄▀█▄▀█ + ▀▄ ▀▄ ▀█ ██ ▀ ▄█▄ ▀▀▄█ ▄▀▀▀▀█▀▄▄ ▄█▄ ▀ ▄ + █▄▄█ ▄▀▀▀ █▀▀ ▄ ██▄█▄▄▀ ██▄▀█▄ █▄▄ ▀▄▄ + ▀██▀█▄▀ █▄▀█▄█▄ ██ █▄ ▀▄▀█▄▀▀ █▄█▄▀▄▄▀▀▄ + ▄▄▀▀▄ ▀▄▄▀▀ ▀█▀▀ █▄▄█ ▀▄▄▀▄█ ██ ▄▄█ ▀▄ + █▀█▄▀▀ █▀ ▄▄▀█▀ ▄ ▀▀▀▄▀█ ▀█▀██▀▄▄▄▄███▄ + █▄▀▄▀▀▀▀▄█ ▄▀ █▀▀█ ▀██ ▄ ██ █▄ ▄▀▀█ ▀▄ + ▀▄█▀▀ ▀ ▀ ▄▄▀▀ ▀ ▀▄▄█▄▀█ ▀ ▀▀ ▄▄█▄█▄ ▀▀█ + ▀ █▀▀█▀▄▀ ▄▄▄ ▄█▄ ▀▄ █▄ ▀ ▀█▀█ ▀ █ ▄ ▀█▄ + █▀▀█▄▄▀█ ▄ ▀ ▄▄▄▄▀ ▄█▄▄▀▀▄ ▄▀█▄ ▄▄▄ ▀▀▄ + █ ▄ █▀▀▄█▀▄█▀▀ ▄▀ █▄██▄ ▀ ▀▄▀█ ▄█▄ ▄▄▀ ▄ + ▀ ▀ ▀ ▀▀█▄██▀█▀ █ █ ▄ █ ▄█▄▄ █▀▀▀█▀▀▀█ + █▀▀▀▀▀█ ▀▀█▄██▀▀ █▄▄ ▀▄█▀ ▀▄ ▄██ ▀ █ ▀▀ + █ ███ █ █▀▀ ▄ ▀█▀ ▄▀▀█▀▄ █ ▀▄██ █▀▀▀▀█▀█▄ + █ ▀▀▀ █ █▄ ▄ ▄█▀▀▄ ▀▄ ▄█ ▀▀ ▀ ███▀ ▀▄ ▄ + ▀▀▀▀▀▀▀ ▀▀▀ ▀▀▀▀ ▀▀ ▀ ▀▀ ▀▀ ▀ ▀▀ ▀▀ + +I (1000) app: If QR code is not visible, copy paste the below URL in a browser. +https://espressif.github.io/esp-jumpstart/qrcode.html?data={"ver":"v1","name":"PROV_01B1E8","username":"wifiprov","pop":"abcd1234","transport":"ble","network":"wifi"} +``` + +### Wi-Fi Scanning + +Provisioning manager also supports providing real-time Wi-Fi scan results (performed on the device) during provisioning. This allows the client side applications to choose the AP for which the device Wi-Fi station is to be configured. Various information about the visible APs is available, like signal strength (RSSI) and security type, etc. Also, the manager now provides capabilities information which can be used by client applications to determine the security type and availability of specific features (like `wifi_scan`). + +When using the scan based provisioning, we don't need to specify the `--ssid` and `--passphrase` fields explicitly: + +``` +python esp_prov.py --transport ble --service_name PROV_01B1E8 --pop abcd1234 +``` + +See below the sample output from `esp_prov` tool on running above command: + +``` +Connecting... +Connected +Getting Services... +Security scheme determined to be : 1 + +==== Starting Session ==== +==== Session Established ==== + +==== Scanning Wi-Fi APs ==== +++++ Scan process executed in 1.9967520237 sec +++++ Scan results : 5 + +++++ Scan finished in 2.7374596596 sec +==== Wi-Fi Scan results ==== +S.N. SSID BSSID CHN RSSI AUTH +[ 1] MyHomeWiFiAP 788a20841996 1 -45 WPA2_PSK +[ 2] MobileHotspot 7a8a20841996 11 -46 WPA2_PSK +[ 3] MyHomeWiFiAP 788a208daa26 11 -54 WPA2_PSK +[ 4] NeighborsWiFiAP 8a8a20841996 6 -61 WPA2_PSK +[ 5] InsecureWiFiAP dca4caf1227c 7 -74 Open + +Select AP by number (0 to rescan) : 1 +Enter passphrase for MyHomeWiFiAP : + +==== Sending Wi-Fi Credentials to Target ==== +==== Wi-Fi Credentials sent successfully ==== + +==== Applying Wi-Fi Config to Target ==== +==== Apply config sent successfully ==== + +==== Wi-Fi connection state ==== +==== WiFi state: Connected ==== +==== Provisioning was successful ==== +``` + +### Interactive Provisioning + +`esp_prov` supports interactive provisioning. You can trigger the script with a simplified command and input the necessary details +(`Proof-of-possession` for security scheme 1 and `SRP6a username`, `SRP6a password` for security scheme 2) as the provisioning process advances. + +The command `python esp_prov.py --transport ble --sec_ver 1` gives out the following sample output: + +``` +Discovering... +==== BLE Discovery results ==== +S.N. Name Address +[ 1] PROV_4C33E8 01:02:03:04:05:06 +[ 1] BT_DEVICE_SBC 0A:0B:0C:0D:0E:0F +Select device by number (0 to rescan) : 1 +Connecting... +Getting Services... +Proof of Possession required: + +==== Starting Session ==== +==== Session Established ==== + +==== Scanning Wi-Fi APs ==== +++++ Scan process executed in 3.8695244789123535 sec +++++ Scan results : 2 + +++++ Scan finished in 4.4132080078125 sec +==== Wi-Fi Scan results ==== +S.N. SSID BSSID CHN RSSI AUTH +[ 1] MyHomeWiFiAP 788a20841996 1 -45 WPA2_PSK +[ 2] MobileHotspot 7a8a20841996 11 -46 WPA2_PSK + +Select AP by number (0 to rescan) : 1 +Enter passphrase for myssid : + +==== Sending Wi-Fi Credentials to Target ==== +==== Wi-Fi Credentials sent successfully ==== + +==== Applying Wi-Fi Config to Target ==== +==== Apply config sent successfully ==== + +==== Wi-Fi connection state ==== +==== WiFi state: Connected ==== +==== Provisioning was successful ==== +``` + +### Sending Custom Data + +The provisioning manager allows applications to send some custom data during provisioning, which may be +required for some other operations like connecting to some cloud service. This is achieved by creating +and registering additional endpoints using the below APIs + +``` +network_prov_mgr_endpoint_create(); +network_prov_mgr_endpoint_register(); +``` + +In this particular example, we have added an endpoint named "custom-data" which can be tested +by passing the `--custom_data ` option to the esp\_prov tool. Following output is +expected on success: + +``` +==== Sending Custom data to esp32 ==== +CustomData response: SUCCESS +``` + +## Troubleshooting + +### Provisioning failed + +It is possible that the Wi-Fi credentials provided were incorrect, or the device was not able to establish connection to the network, in which the the `esp_prov` script will notify failure (with reason). Serial monitor log will display the failure along with disconnect reason : + +``` +E (367015) app: Provisioning failed! + Reason : Wi-Fi AP password incorrect + Please reset to factory and retry provisioning +``` + +Once credentials have been applied, even though wrong credentials were provided, the device will no longer go into provisioning mode on subsequent reboots until NVS is erased (see following section). + +### Provisioning does not start + +If the serial monitor log shows the following : + +``` +I (465) app: Already provisioned, starting Wi-Fi STA +``` + +it means either the device has been provisioned earlier with or without success (e.g. scenario covered in above section), or that the Wi-Fi credentials were already set by some other application flashed previously onto your device. On setting the log level to DEBUG this is clearly evident : + +``` +D (455) network_prov_mgr: Found Wi-Fi SSID : myssid +D (465) network_prov_mgr: Found Wi-Fi Password : m********d +I (465) app: Already provisioned, starting Wi-Fi STA +``` + +To fix this we simple need to erase the NVS partition from flash. First we need to find out its address and size. This can be seen from the monitor log on the top right after reboot. + +``` +I (47) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (58) boot: 0 nvs WiFi data 01 02 00009000 00006000 +I (65) boot: 1 phy_init RF data 01 01 0000f000 00001000 +I (73) boot: 2 factory factory app 00 00 00010000 00124f80 +I (80) boot: End of partition table +``` + +Now erase NVS partition by running the following commands : + +``` +$IDF_PATH/components/esptool_py/esptool/esptool.py erase_region 0x9000 0x6000 +``` + +### Bluetooth Pairing Request during provisioning + +ESP-IDF now has functionality to enforce link encryption requirement while performing GATT write on characteristics of provisioning service. This will however result in a pairing pop-up dialog, if link is not encrypted. This feature is disabled by default. In order to enable this feature, please set `CONFIG_WIFI_PROV_BLE_FORCE_ENCRYPTION=y` in the sdkconfig or select the configuration using "idf.py menuconfig" . + +``` +Component Config --> Wi-Fi Provisioning Manager --> Force Link Encryption during Characteristic Read/Write + +``` +Recompiling the application with above changes should suffice to enable this functionality. + + +### Unsupported platform + +If the platform requirement, for running `esp_prov` is not satisfied, then the script execution will fallback to console mode, in which case the full process (involving user inputs) will look like this : + +``` +==== Esp_Prov Version: v1.0 ==== +BLE client is running in console mode + This could be due to your platform not being supported or dependencies not being met + Please ensure all pre-requisites are met to run the full fledged client +BLECLI >> Please connect to BLE device `PROV_01B1E8` manually using your tool of choice +BLECLI >> Was the device connected successfully? [y/n] y +BLECLI >> List available attributes of the connected device +BLECLI >> Is the service UUID '0000ffff-0000-1000-8000-00805f9b34fb' listed among available attributes? [y/n] y +BLECLI >> Is the characteristic UUID '0000ff53-0000-1000-8000-00805f9b34fb' listed among available attributes? [y/n] y +BLECLI >> Is the characteristic UUID '0000ff51-0000-1000-8000-00805f9b34fb' listed among available attributes? [y/n] y +BLECLI >> Is the characteristic UUID '0000ff52-0000-1000-8000-00805f9b34fb' listed among available attributes? [y/n] y + +==== Verifying protocol version ==== +BLECLI >> Write following data to characteristic with UUID '0000ff53-0000-1000-8000-00805f9b34fb' : + >> 56302e31 +BLECLI >> Enter data read from characteristic (in hex) : + << 53554343455353 +==== Verified protocol version successfully ==== + +==== Starting Session ==== +BLECLI >> Write following data to characteristic with UUID '0000ff51-0000-1000-8000-00805f9b34fb' : + >> 10015a25a201220a20ae6d9d5d1029f8c366892252d2d5a0ffa7ce1ee5829312545dd5f2aba057294d +BLECLI >> Enter data read from characteristic (in hex) : + << 10015a390801aa0134122048008bfc365fad4753dc75912e0c764d60749cb26dd609595b6fbc72e12614031a1089733af233c7448e7d7fb7963682c6d8 +BLECLI >> Write following data to characteristic with UUID '0000ff51-0000-1000-8000-00805f9b34fb' : + >> 10015a270802b2012212204051088dc294fe4621fac934a8ea22e948fcc3e8ac458aac088ce705c65dbfb9 +BLECLI >> Enter data read from characteristic (in hex) : + << 10015a270803ba01221a20c8d38059d5206a3d92642973ac6ba8ac2f6ecf2b7a3632964eb35a0f20133adb +==== Session Established ==== + +==== Sending Wifi credential to esp32 ==== +BLECLI >> Write following data to characteristic with UUID '0000ff52-0000-1000-8000-00805f9b34fb' : + >> 98471ac4019a46765c28d87df8c8ae71c1ae6cfe0bc9c615bc6d2c +BLECLI >> Enter data read from characteristic (in hex) : + << 3271f39a +==== Wifi Credentials sent successfully ==== + +==== Applying config to esp32 ==== +BLECLI >> Write following data to characteristic with UUID '0000ff52-0000-1000-8000-00805f9b34fb' : + >> 5355 +BLECLI >> Enter data read from characteristic (in hex) : + << 1664db24 +==== Apply config sent successfully ==== + +==== Wifi connection state ==== +BLECLI >> Write following data to characteristic with UUID '0000ff52-0000-1000-8000-00805f9b34fb' : + >> 290d +BLECLI >> Enter data read from characteristic (in hex) : + << 505f72a9f8521025c1964d7789c4d7edc56aedebd144e1b667bc7c0975757b80cc091aa9f3e95b06eaefbc30290fa1 +++++ WiFi state: connected ++++ +==== Provisioning was successful ==== +``` + +The write data is to be copied from the console output ```>>``` to the platform specific application and the data read from the application is to be pasted at the user input prompt ```<<``` of the console, in the format (hex) indicated in above sample log. diff --git a/components/network_provisioning/examples/wifi_prov/main/CMakeLists.txt b/components/network_provisioning/examples/wifi_prov/main/CMakeLists.txt new file mode 100644 index 0000000..61fac40 --- /dev/null +++ b/components/network_provisioning/examples/wifi_prov/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS ".") diff --git a/components/network_provisioning/examples/wifi_prov/main/Kconfig.projbuild b/components/network_provisioning/examples/wifi_prov/main/Kconfig.projbuild new file mode 100644 index 0000000..8df82bd --- /dev/null +++ b/components/network_provisioning/examples/wifi_prov/main/Kconfig.projbuild @@ -0,0 +1,114 @@ +menu "Example Configuration" + + choice EXAMPLE_PROV_TRANSPORT + bool "Provisioning Transport" + default EXAMPLE_PROV_TRANSPORT_SOFTAP if IDF_TARGET_ESP32S2 + default EXAMPLE_PROV_TRANSPORT_BLE + help + Wi-Fi provisioning component offers both, SoftAP and BLE transports. Choose any one. + + config EXAMPLE_PROV_TRANSPORT_BLE + bool "BLE" + select BT_ENABLED + depends on !IDF_TARGET_ESP32S2 + config EXAMPLE_PROV_TRANSPORT_SOFTAP + bool "Soft AP" + select LWIP_IPV4 + endchoice + + choice EXAMPLE_PROV_SECURITY_VERSION + bool "Protocomm security version" + default EXAMPLE_PROV_SECURITY_VERSION_2 + help + Wi-Fi provisioning component offers 3 security versions. + The example offers a choice between security version 1 and 2. + + config EXAMPLE_PROV_SECURITY_VERSION_1 + bool "Security version 1" + select ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1 + + config EXAMPLE_PROV_SECURITY_VERSION_2 + bool "Security version 2" + select ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2 + endchoice + + choice EXAMPLE_PROV_MODE + bool "Security version 2 mode" + depends on EXAMPLE_PROV_SECURITY_VERSION_2 + default EXAMPLE_PROV_SEC2_DEV_MODE + + config EXAMPLE_PROV_SEC2_DEV_MODE + bool "Security version 2 development mode" + depends on EXAMPLE_PROV_SECURITY_VERSION_2 + help + This enables the development mode for + security version 2. + Please note that this mode is NOT recommended for production purpose. + + config EXAMPLE_PROV_SEC2_PROD_MODE + bool "Security version 2 production mode" + depends on EXAMPLE_PROV_SECURITY_VERSION_2 + help + This enables the production mode for + security version 2. + endchoice + + config EXAMPLE_PROV_TRANSPORT + int + default 1 if EXAMPLE_PROV_TRANSPORT_BLE + default 2 if EXAMPLE_PROV_TRANSPORT_SOFTAP + + config EXAMPLE_PROV_ENABLE_APP_CALLBACK + bool "Enable provisioning manager app callback" + default n + help + This is for advanced use-cases like modifying Wi-Fi configuration parameters. This + executes a blocking app callback when any provisioning event is triggered. + + config EXAMPLE_RESET_PROVISIONED + bool + default n + prompt "Reset provisioned status of the device" + help + This erases the NVS to reset provisioned status of the device on every reboot. + Provisioned status is determined by the Wi-Fi STA configuration, saved on the NVS. + + config EXAMPLE_RESET_PROV_MGR_ON_FAILURE + bool + default y + prompt "Reset provisioned credentials and state machine after session failure" + help + Enable resetting provisioned credentials and state machine after session failure. + This will restart the provisioning service after retries are exhausted. + + config EXAMPLE_PROV_MGR_CONNECTION_CNT + int + default 5 + prompt "Max connection attempts before resetting provisioning state machine" + depends on EXAMPLE_RESET_PROV_MGR_ON_FAILURE + help + Set the total number of connection attempts to avoid reconnecting to an inexistent AP or if credentials + are misconfigured. Provisioned credentials are erased and internal state machine + is reset after this threshold is reached. + + config EXAMPLE_PROV_SHOW_QR + bool "Show provisioning QR code" + default y + help + Show the QR code for provisioning. + + config EXAMPLE_PROV_USING_BLUEDROID + bool + depends on (BT_BLUEDROID_ENABLED && (IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3)) + select BT_BLE_42_FEATURES_SUPPORTED + default y + help + This enables BLE 4.2 features for Bluedroid. + + config EXAMPLE_REPROVISIONING + bool "Re-provisioning" + help + Enable re-provisioning - allow the device to provision for new credentials + after previous successful provisioning. + +endmenu diff --git a/components/network_provisioning/examples/wifi_prov/main/app_main.c b/components/network_provisioning/examples/wifi_prov/main/app_main.c new file mode 100644 index 0000000..bf5d911 --- /dev/null +++ b/components/network_provisioning/examples/wifi_prov/main/app_main.c @@ -0,0 +1,560 @@ +/* Wi-Fi Provisioning Manager Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_BLE +#include +#endif /* CONFIG_EXAMPLE_PROV_TRANSPORT_BLE */ + +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_SOFTAP +#include +#endif /* CONFIG_EXAMPLE_PROV_TRANSPORT_SOFTAP */ +#include "qrcode.h" + +static const char *TAG = "app"; + +#if CONFIG_EXAMPLE_PROV_SECURITY_VERSION_2 +#if CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE +#define EXAMPLE_PROV_SEC2_USERNAME "wifiprov" +#define EXAMPLE_PROV_SEC2_PWD "abcd1234" + +/* This salt,verifier has been generated for username = "wifiprov" and password = "abcd1234" + * IMPORTANT NOTE: For production cases, this must be unique to every device + * and should come from device manufacturing partition.*/ +static const char sec2_salt[] = { + 0x03, 0x6e, 0xe0, 0xc7, 0xbc, 0xb9, 0xed, 0xa8, 0x4c, 0x9e, 0xac, 0x97, 0xd9, 0x3d, 0xec, 0xf4 +}; + +static const char sec2_verifier[] = { + 0x7c, 0x7c, 0x85, 0x47, 0x65, 0x08, 0x94, 0x6d, 0xd6, 0x36, 0xaf, 0x37, 0xd7, 0xe8, 0x91, 0x43, + 0x78, 0xcf, 0xfd, 0x61, 0x6c, 0x59, 0xd2, 0xf8, 0x39, 0x08, 0x12, 0x72, 0x38, 0xde, 0x9e, 0x24, + 0xa4, 0x70, 0x26, 0x1c, 0xdf, 0xa9, 0x03, 0xc2, 0xb2, 0x70, 0xe7, 0xb1, 0x32, 0x24, 0xda, 0x11, + 0x1d, 0x97, 0x18, 0xdc, 0x60, 0x72, 0x08, 0xcc, 0x9a, 0xc9, 0x0c, 0x48, 0x27, 0xe2, 0xae, 0x89, + 0xaa, 0x16, 0x25, 0xb8, 0x04, 0xd2, 0x1a, 0x9b, 0x3a, 0x8f, 0x37, 0xf6, 0xe4, 0x3a, 0x71, 0x2e, + 0xe1, 0x27, 0x86, 0x6e, 0xad, 0xce, 0x28, 0xff, 0x54, 0x46, 0x60, 0x1f, 0xb9, 0x96, 0x87, 0xdc, + 0x57, 0x40, 0xa7, 0xd4, 0x6c, 0xc9, 0x77, 0x54, 0xdc, 0x16, 0x82, 0xf0, 0xed, 0x35, 0x6a, 0xc4, + 0x70, 0xad, 0x3d, 0x90, 0xb5, 0x81, 0x94, 0x70, 0xd7, 0xbc, 0x65, 0xb2, 0xd5, 0x18, 0xe0, 0x2e, + 0xc3, 0xa5, 0xf9, 0x68, 0xdd, 0x64, 0x7b, 0xb8, 0xb7, 0x3c, 0x9c, 0xfc, 0x00, 0xd8, 0x71, 0x7e, + 0xb7, 0x9a, 0x7c, 0xb1, 0xb7, 0xc2, 0xc3, 0x18, 0x34, 0x29, 0x32, 0x43, 0x3e, 0x00, 0x99, 0xe9, + 0x82, 0x94, 0xe3, 0xd8, 0x2a, 0xb0, 0x96, 0x29, 0xb7, 0xdf, 0x0e, 0x5f, 0x08, 0x33, 0x40, 0x76, + 0x52, 0x91, 0x32, 0x00, 0x9f, 0x97, 0x2c, 0x89, 0x6c, 0x39, 0x1e, 0xc8, 0x28, 0x05, 0x44, 0x17, + 0x3f, 0x68, 0x02, 0x8a, 0x9f, 0x44, 0x61, 0xd1, 0xf5, 0xa1, 0x7e, 0x5a, 0x70, 0xd2, 0xc7, 0x23, + 0x81, 0xcb, 0x38, 0x68, 0xe4, 0x2c, 0x20, 0xbc, 0x40, 0x57, 0x76, 0x17, 0xbd, 0x08, 0xb8, 0x96, + 0xbc, 0x26, 0xeb, 0x32, 0x46, 0x69, 0x35, 0x05, 0x8c, 0x15, 0x70, 0xd9, 0x1b, 0xe9, 0xbe, 0xcc, + 0xa9, 0x38, 0xa6, 0x67, 0xf0, 0xad, 0x50, 0x13, 0x19, 0x72, 0x64, 0xbf, 0x52, 0xc2, 0x34, 0xe2, + 0x1b, 0x11, 0x79, 0x74, 0x72, 0xbd, 0x34, 0x5b, 0xb1, 0xe2, 0xfd, 0x66, 0x73, 0xfe, 0x71, 0x64, + 0x74, 0xd0, 0x4e, 0xbc, 0x51, 0x24, 0x19, 0x40, 0x87, 0x0e, 0x92, 0x40, 0xe6, 0x21, 0xe7, 0x2d, + 0x4e, 0x37, 0x76, 0x2f, 0x2e, 0xe2, 0x68, 0xc7, 0x89, 0xe8, 0x32, 0x13, 0x42, 0x06, 0x84, 0x84, + 0x53, 0x4a, 0xb3, 0x0c, 0x1b, 0x4c, 0x8d, 0x1c, 0x51, 0x97, 0x19, 0xab, 0xae, 0x77, 0xff, 0xdb, + 0xec, 0xf0, 0x10, 0x95, 0x34, 0x33, 0x6b, 0xcb, 0x3e, 0x84, 0x0f, 0xb9, 0xd8, 0x5f, 0xb8, 0xa0, + 0xb8, 0x55, 0x53, 0x3e, 0x70, 0xf7, 0x18, 0xf5, 0xce, 0x7b, 0x4e, 0xbf, 0x27, 0xce, 0xce, 0xa8, + 0xb3, 0xbe, 0x40, 0xc5, 0xc5, 0x32, 0x29, 0x3e, 0x71, 0x64, 0x9e, 0xde, 0x8c, 0xf6, 0x75, 0xa1, + 0xe6, 0xf6, 0x53, 0xc8, 0x31, 0xa8, 0x78, 0xde, 0x50, 0x40, 0xf7, 0x62, 0xde, 0x36, 0xb2, 0xba +}; +#endif + +static esp_err_t example_get_sec2_salt(const char **salt, uint16_t *salt_len) +{ +#if CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE + ESP_LOGI(TAG, "Development mode: using hard coded salt"); + *salt = sec2_salt; + *salt_len = sizeof(sec2_salt); + return ESP_OK; +#elif CONFIG_EXAMPLE_PROV_SEC2_PROD_MODE + ESP_LOGE(TAG, "Not implemented!"); + return ESP_FAIL; +#endif +} + +static esp_err_t example_get_sec2_verifier(const char **verifier, uint16_t *verifier_len) +{ +#if CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE + ESP_LOGI(TAG, "Development mode: using hard coded verifier"); + *verifier = sec2_verifier; + *verifier_len = sizeof(sec2_verifier); + return ESP_OK; +#elif CONFIG_EXAMPLE_PROV_SEC2_PROD_MODE + /* This code needs to be updated with appropriate implementation to provide verifier */ + ESP_LOGE(TAG, "Not implemented!"); + return ESP_FAIL; +#endif +} +#endif + +/* Signal Wi-Fi events on this event-group */ +const int WIFI_CONNECTED_EVENT = BIT0; +static EventGroupHandle_t wifi_event_group; + +#define PROV_QR_VERSION "v1" +#define PROV_TRANSPORT_SOFTAP "softap" +#define PROV_TRANSPORT_BLE "ble" +#define QRCODE_BASE_URL "https://espressif.github.io/esp-jumpstart/qrcode.html" + +/* Event handler for catching system events */ +static void event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_base == NETWORK_PROV_EVENT) { + switch (event_id) { + case NETWORK_PROV_START: + ESP_LOGI(TAG, "Provisioning started"); + break; + case NETWORK_PROV_WIFI_CRED_RECV: { + wifi_sta_config_t *wifi_sta_cfg = (wifi_sta_config_t *)event_data; + ESP_LOGI(TAG, "Received Wi-Fi credentials" + "\n\tSSID : %s\n\tPassword : %s", + (const char *) wifi_sta_cfg->ssid, + (const char *) wifi_sta_cfg->password); + break; + } + case NETWORK_PROV_WIFI_CRED_FAIL: { + network_prov_wifi_sta_fail_reason_t *reason = (network_prov_wifi_sta_fail_reason_t *)event_data; + ESP_LOGE(TAG, "Provisioning failed!\n\tReason : %s" + "\n\tPlease reset to factory and retry provisioning", + (*reason == NETWORK_PROV_WIFI_STA_AUTH_ERROR) ? + "Wi-Fi station authentication failed" : "Wi-Fi access-point not found"); +#ifdef CONFIG_EXAMPLE_RESET_PROV_MGR_ON_FAILURE + /* Reset the state machine on provisioning failure. + * This is enabled by the CONFIG_EXAMPLE_RESET_PROV_MGR_ON_FAILURE configuration. + * It allows the provisioning manager to retry the provisioning process + * based on the number of attempts specified in wifi_conn_attempts. After attempting + * the maximum number of retries, the provisioning manager will reset the state machine + * and the provisioning process will be terminated. + */ + network_prov_mgr_reset_wifi_sm_state_on_failure(); +#endif + break; + } + case NETWORK_PROV_WIFI_CRED_SUCCESS: + ESP_LOGI(TAG, "Provisioning successful"); + break; + case NETWORK_PROV_END: + /* De-initialize manager once provisioning is finished */ + esp_err_t err = network_prov_mgr_deinit(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to de-initialize provisioning manager: %s", esp_err_to_name(err)); + } + break; + default: + break; + } + } else if (event_base == WIFI_EVENT) { + switch (event_id) { + case WIFI_EVENT_STA_START: + esp_wifi_connect(); + break; + case WIFI_EVENT_STA_DISCONNECTED: + ESP_LOGI(TAG, "Disconnected. Connecting to the AP again..."); + esp_wifi_connect(); + break; +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_SOFTAP + case WIFI_EVENT_AP_STACONNECTED: + ESP_LOGI(TAG, "SoftAP transport: Connected!"); + break; + case WIFI_EVENT_AP_STADISCONNECTED: + ESP_LOGI(TAG, "SoftAP transport: Disconnected!"); + break; +#endif + default: + break; + } + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data; + ESP_LOGI(TAG, "Connected with IP Address:" IPSTR, IP2STR(&event->ip_info.ip)); + /* Signal main application to continue execution */ + xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_EVENT); +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_BLE + } else if (event_base == PROTOCOMM_TRANSPORT_BLE_EVENT) { + switch (event_id) { + case PROTOCOMM_TRANSPORT_BLE_CONNECTED: + ESP_LOGI(TAG, "BLE transport: Connected!"); + break; + case PROTOCOMM_TRANSPORT_BLE_DISCONNECTED: + ESP_LOGI(TAG, "BLE transport: Disconnected!"); + break; + default: + break; + } +#endif + } else if (event_base == PROTOCOMM_SECURITY_SESSION_EVENT) { + switch (event_id) { + case PROTOCOMM_SECURITY_SESSION_SETUP_OK: + ESP_LOGI(TAG, "Secured session established!"); + break; + case PROTOCOMM_SECURITY_SESSION_INVALID_SECURITY_PARAMS: + ESP_LOGE(TAG, "Received invalid security parameters for establishing secure session!"); + break; + case PROTOCOMM_SECURITY_SESSION_CREDENTIALS_MISMATCH: + ESP_LOGE(TAG, "Received incorrect username and/or PoP for establishing secure session!"); + break; + default: + break; + } + } +} + +static void wifi_init_sta(void) +{ + /* Start Wi-Fi in station mode */ + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_start()); +} + +static void get_device_service_name(char *service_name, size_t max) +{ + uint8_t eth_mac[6]; + const char *ssid_prefix = "PROV_"; + esp_wifi_get_mac(WIFI_IF_STA, eth_mac); + snprintf(service_name, max, "%s%02X%02X%02X", + ssid_prefix, eth_mac[3], eth_mac[4], eth_mac[5]); +} + +/* Handler for the optional provisioning endpoint registered by the application. + * The data format can be chosen by applications. Here, we are using plain ascii text. + * Applications can choose to use other formats like protobuf, JSON, XML, etc. + * Note that memory for the response buffer must be allocated using heap as this buffer + * gets freed by the protocomm layer once it has been sent by the transport layer. + */ +esp_err_t custom_prov_data_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, + uint8_t **outbuf, ssize_t *outlen, void *priv_data) +{ + if (inbuf) { + ESP_LOGI(TAG, "Received data: %.*s", inlen, (char *)inbuf); + } + char response[] = "SUCCESS"; + *outbuf = (uint8_t *)strdup(response); + if (*outbuf == NULL) { + ESP_LOGE(TAG, "System out of memory"); + return ESP_ERR_NO_MEM; + } + *outlen = strlen(response) + 1; /* +1 for NULL terminating byte */ + + return ESP_OK; +} + +static void wifi_prov_print_qr(const char *name, const char *username, const char *pop, const char *transport) +{ + if (!name || !transport) { + ESP_LOGW(TAG, "Cannot generate QR code payload. Data missing."); + return; + } + char payload[150] = {0}; + if (pop) { +#if CONFIG_EXAMPLE_PROV_SECURITY_VERSION_1 + snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \ + ",\"pop\":\"%s\",\"transport\":\"%s\"}", + PROV_QR_VERSION, name, pop, transport); +#elif CONFIG_EXAMPLE_PROV_SECURITY_VERSION_2 + snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \ + ",\"username\":\"%s\",\"pop\":\"%s\",\"transport\":\"%s\"}", + PROV_QR_VERSION, name, username, pop, transport); +#endif + } else { + snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \ + ",\"transport\":\"%s\",\"network\":\"wifi\"}", + PROV_QR_VERSION, name, transport); + } + //TODO: Add the network protocol type to the QR code payload +#ifdef CONFIG_EXAMPLE_PROV_SHOW_QR + ESP_LOGI(TAG, "Scan this QR code from the provisioning application for Provisioning."); + esp_qrcode_config_t cfg = ESP_QRCODE_CONFIG_DEFAULT(); + esp_qrcode_generate(&cfg, payload); +#endif /* CONFIG_EXAMPLE_PROV_SHOW_QR */ + ESP_LOGI(TAG, "If QR code is not visible, copy paste the below URL in a browser.\n%s?data=%s", QRCODE_BASE_URL, payload); +} + +#ifdef CONFIG_EXAMPLE_PROV_ENABLE_APP_CALLBACK +void wifi_prov_app_callback(void *user_data, wifi_prov_cb_event_t event, void *event_data) +{ + /** + * This is blocking callback, any configurations that needs to be set when a particular + * provisioning event is triggered can be set here. + */ + switch (event) { + case WIFI_PROV_SET_STA_CONFIG: { + /** + * Wi-Fi configurations can be set here before the Wi-Fi is enabled in + * STA mode. + */ + wifi_config_t *wifi_config = (wifi_config_t *)event_data; + (void) wifi_config; + break; + } + default: + break; + } +} + +const wifi_prov_event_handler_t wifi_prov_event_handler = { + .event_cb = wifi_prov_app_callback, + .user_data = NULL, +}; +#endif /* EXAMPLE_PROV_ENABLE_APP_CALLBACK */ + +void app_main(void) +{ + /* Initialize NVS partition */ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + /* NVS partition was truncated + * and needs to be erased */ + ESP_ERROR_CHECK(nvs_flash_erase()); + + /* Retry nvs_flash_init */ + ESP_ERROR_CHECK(nvs_flash_init()); + } + + /* Initialize TCP/IP */ + ESP_ERROR_CHECK(esp_netif_init()); + + /* Initialize the event loop */ + ESP_ERROR_CHECK(esp_event_loop_create_default()); + wifi_event_group = xEventGroupCreate(); + + /* Register our event handler for Wi-Fi, IP and Provisioning related events */ + ESP_ERROR_CHECK(esp_event_handler_register(NETWORK_PROV_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_BLE + ESP_ERROR_CHECK(esp_event_handler_register(PROTOCOMM_TRANSPORT_BLE_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); +#endif + ESP_ERROR_CHECK(esp_event_handler_register(PROTOCOMM_SECURITY_SESSION_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL)); + + /* Initialize Wi-Fi including netif with default config */ + esp_netif_create_default_wifi_sta(); +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_SOFTAP + esp_netif_create_default_wifi_ap(); +#endif /* CONFIG_EXAMPLE_PROV_TRANSPORT_SOFTAP */ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + /* Configuration for the provisioning manager */ + network_prov_mgr_config_t config = { +#ifdef CONFIG_EXAMPLE_RESET_PROV_MGR_ON_FAILURE + .network_prov_wifi_conn_cfg = { + .wifi_conn_attempts = CONFIG_EXAMPLE_PROV_MGR_CONNECTION_CNT, + }, +#endif + /* What is the Provisioning Scheme that we want ? + * network_prov_scheme_softap or network_prov_scheme_ble */ +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_BLE + .scheme = network_prov_scheme_ble, +#endif /* CONFIG_EXAMPLE_PROV_TRANSPORT_BLE */ +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_SOFTAP + .scheme = network_prov_scheme_softap, +#endif /* CONFIG_EXAMPLE_PROV_TRANSPORT_SOFTAP */ +#ifdef CONFIG_EXAMPLE_PROV_ENABLE_APP_CALLBACK + .app_event_handler = wifi_prov_event_handler, +#endif /* EXAMPLE_PROV_ENABLE_APP_CALLBACK */ + + /* Any default scheme specific event handler that you would + * like to choose. Since our example application requires + * neither BT nor BLE, we can choose to release the associated + * memory once provisioning is complete, or not needed + * (in case when device is already provisioned). Choosing + * appropriate scheme specific event handler allows the manager + * to take care of this automatically. This can be set to + * NETWORK_PROV_EVENT_HANDLER_NONE when using network_prov_scheme_softap*/ +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_BLE + .scheme_event_handler = NETWORK_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM +#endif /* CONFIG_EXAMPLE_PROV_TRANSPORT_BLE */ +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_SOFTAP + .scheme_event_handler = NETWORK_PROV_EVENT_HANDLER_NONE +#endif /* CONFIG_EXAMPLE_PROV_TRANSPORT_SOFTAP */ + }; + + /* Initialize provisioning manager with the + * configuration parameters set above */ + ESP_ERROR_CHECK(network_prov_mgr_init(config)); + + bool provisioned = false; +#ifdef CONFIG_EXAMPLE_RESET_PROVISIONED + network_prov_mgr_reset_wifi_provisioning(); +#else + /* Let's find out if the device is provisioned */ + ESP_ERROR_CHECK(network_prov_mgr_is_wifi_provisioned(&provisioned)); + +#endif + /* If device is not yet provisioned start provisioning service */ + if (!provisioned) { + ESP_LOGI(TAG, "Starting provisioning"); + + /* What is the Device Service Name that we want + * This translates to : + * - Wi-Fi SSID when scheme is network_prov_scheme_softap + * - device name when scheme is network_prov_scheme_ble + */ + char service_name[12]; + get_device_service_name(service_name, sizeof(service_name)); + +#ifdef CONFIG_EXAMPLE_PROV_SECURITY_VERSION_1 + /* What is the security level that we want (0, 1, 2): + * - NETWORK_PROV_SECURITY_0 is simply plain text communication. + * - NETWORK_PROV_SECURITY_1 is secure communication which consists of secure handshake + * using X25519 key exchange and proof of possession (pop) and AES-CTR + * for encryption/decryption of messages. + * - NETWORK_PROV_SECURITY_2 SRP6a based authentication and key exchange + * + AES-GCM encryption/decryption of messages + */ + network_prov_security_t security = NETWORK_PROV_SECURITY_1; + + /* Do we want a proof-of-possession (ignored if Security 0 is selected): + * - this should be a string with length > 0 + * - NULL if not used + */ + const char *pop = "abcd1234"; + + /* This is the structure for passing security parameters + * for the protocomm security 1. + */ + network_prov_security1_params_t *sec_params = pop; + + const char *username = NULL; + +#elif CONFIG_EXAMPLE_PROV_SECURITY_VERSION_2 + network_prov_security_t security = NETWORK_PROV_SECURITY_2; + /* The username must be the same one, which has been used in the generation of salt and verifier */ + +#if CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE + /* This pop field represents the password that will be used to generate salt and verifier. + * The field is present here in order to generate the QR code containing password. + * In production this password field shall not be stored on the device */ + const char *username = EXAMPLE_PROV_SEC2_USERNAME; + const char *pop = EXAMPLE_PROV_SEC2_PWD; +#elif CONFIG_EXAMPLE_PROV_SEC2_PROD_MODE + /* The username and password shall not be embedded in the firmware, + * they should be provided to the user by other means. + * e.g. QR code sticker */ + const char *username = NULL; + const char *pop = NULL; +#endif + /* This is the structure for passing security parameters + * for the protocomm security 2. + * If dynamically allocated, sec2_params pointer and its content + * must be valid till NETWORK_PROV_END event is triggered. + */ + network_prov_security2_params_t sec2_params = {}; + + ESP_ERROR_CHECK(example_get_sec2_salt(&sec2_params.salt, &sec2_params.salt_len)); + ESP_ERROR_CHECK(example_get_sec2_verifier(&sec2_params.verifier, &sec2_params.verifier_len)); + + network_prov_security2_params_t *sec_params = &sec2_params; +#endif + /* What is the service key (could be NULL) + * This translates to : + * - Wi-Fi password when scheme is network_prov_scheme_softap + * (Minimum expected length: 8, maximum 64 for WPA2-PSK) + * - simply ignored when scheme is network_prov_scheme_ble + */ + const char *service_key = NULL; + +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_BLE + /* This step is only useful when scheme is network_prov_scheme_ble. This will + * set a custom 128 bit UUID which will be included in the BLE advertisement + * and will correspond to the primary GATT service that provides provisioning + * endpoints as GATT characteristics. Each GATT characteristic will be + * formed using the primary service UUID as base, with different auto assigned + * 12th and 13th bytes (assume counting starts from 0th byte). The client side + * applications must identify the endpoints by reading the User Characteristic + * Description descriptor (0x2901) for each characteristic, which contains the + * endpoint name of the characteristic */ + uint8_t custom_service_uuid[] = { + /* LSB <--------------------------------------- + * ---------------------------------------> MSB */ + 0xb4, 0xdf, 0x5a, 0x1c, 0x3f, 0x6b, 0xf4, 0xbf, + 0xea, 0x4a, 0x82, 0x03, 0x04, 0x90, 0x1a, 0x02, + }; + + /* If your build fails with linker errors at this point, then you may have + * forgotten to enable the BT stack or BTDM BLE settings in the SDK (e.g. see + * the sdkconfig.defaults in the example project) */ + network_prov_scheme_ble_set_service_uuid(custom_service_uuid); +#endif /* CONFIG_EXAMPLE_PROV_TRANSPORT_BLE */ + + /* An optional endpoint that applications can create if they expect to + * get some additional custom data during provisioning workflow. + * The endpoint name can be anything of your choice. + * This call must be made before starting the provisioning. + */ + network_prov_mgr_endpoint_create("custom-data"); + + /* Do not stop and de-init provisioning even after success, + * so that we can restart it later. */ +#ifdef CONFIG_EXAMPLE_REPROVISIONING + network_prov_mgr_disable_auto_stop(1000); +#endif + /* Start provisioning service */ + ESP_ERROR_CHECK(network_prov_mgr_start_provisioning(security, (const void *) sec_params, service_name, service_key)); + + /* The handler for the optional endpoint created above. + * This call must be made after starting the provisioning, and only if the endpoint + * has already been created above. + */ + network_prov_mgr_endpoint_register("custom-data", custom_prov_data_handler, NULL); + + /* Uncomment the following to wait for the provisioning to finish and then release + * the resources of the manager. Since in this case de-initialization is triggered + * by the default event loop handler, we don't need to call the following */ + // network_prov_mgr_wait(); + // network_prov_mgr_deinit(); + /* Print QR code for provisioning */ +#ifdef CONFIG_EXAMPLE_PROV_TRANSPORT_BLE + wifi_prov_print_qr(service_name, username, pop, PROV_TRANSPORT_BLE); +#else /* CONFIG_EXAMPLE_PROV_TRANSPORT_SOFTAP */ + wifi_prov_print_qr(service_name, username, pop, PROV_TRANSPORT_SOFTAP); +#endif /* CONFIG_EXAMPLE_PROV_TRANSPORT_BLE */ + } else { + ESP_LOGI(TAG, "Already provisioned, starting Wi-Fi STA"); + + /* We don't need the manager as device is already provisioned, + * so let's release it's resources */ + ESP_ERROR_CHECK(network_prov_mgr_deinit()); + + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); + /* Start Wi-Fi station */ + wifi_init_sta(); + } + + /* Wait for Wi-Fi connection */ + xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_EVENT, true, true, portMAX_DELAY); + + /* Start main application now */ +#if CONFIG_EXAMPLE_REPROVISIONING + while (1) { + for (int i = 0; i < 10; i++) { + ESP_LOGI(TAG, "Hello World!"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + + /* Resetting provisioning state machine to enable re-provisioning */ + network_prov_mgr_reset_wifi_sm_state_for_reprovision(); + + /* Wait for Wi-Fi connection */ + xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_EVENT, true, true, portMAX_DELAY); + } +#else + while (1) { + ESP_LOGI(TAG, "Hello World!"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +#endif + +} diff --git a/components/network_provisioning/examples/wifi_prov/main/idf_component.yml b/components/network_provisioning/examples/wifi_prov/main/idf_component.yml new file mode 100644 index 0000000..ce4e14d --- /dev/null +++ b/components/network_provisioning/examples/wifi_prov/main/idf_component.yml @@ -0,0 +1,6 @@ +dependencies: + espressif/network_provisioning: + version: '*' + espressif/qrcode: + version: ^0.1.0 +version: 1.0.0 diff --git a/components/network_provisioning/examples/wifi_prov/partitions.csv b/components/network_provisioning/examples/wifi_prov/partitions.csv new file mode 100644 index 0000000..d4af98d --- /dev/null +++ b/components/network_provisioning/examples/wifi_prov/partitions.csv @@ -0,0 +1,5 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, , 0x6000, +phy_init, data, phy, , 0x1000, +factory, app, factory, , 0x180000, diff --git a/components/network_provisioning/examples/wifi_prov/sdkconfig.defaults b/components/network_provisioning/examples/wifi_prov/sdkconfig.defaults new file mode 100644 index 0000000..06b77ea --- /dev/null +++ b/components/network_provisioning/examples/wifi_prov/sdkconfig.defaults @@ -0,0 +1,8 @@ +# Override some defaults so BT stack is enabled +CONFIG_BT_ENABLED=y +CONFIG_BT_NIMBLE_ENABLED=y + +## For Bluedroid as binary is larger than default size +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" diff --git a/components/network_provisioning/examples/wifi_prov/sdkconfig.defaults.esp32 b/components/network_provisioning/examples/wifi_prov/sdkconfig.defaults.esp32 new file mode 100644 index 0000000..b8dc687 --- /dev/null +++ b/components/network_provisioning/examples/wifi_prov/sdkconfig.defaults.esp32 @@ -0,0 +1,5 @@ +# ESP32 specific default configurations + +CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y +CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n +CONFIG_BTDM_CTRL_MODE_BTDM=n diff --git a/components/network_provisioning/idf_component.yml b/components/network_provisioning/idf_component.yml new file mode 100644 index 0000000..13507b0 --- /dev/null +++ b/components/network_provisioning/idf_component.yml @@ -0,0 +1,13 @@ +dependencies: + espressif/cjson: + rules: + - if: idf_version >= 6.0 + version: ^1.7.19 + idf: '>=5.1' +description: Network provisioning component for Wi-Fi or Thread devices +repository: git://github.com/espressif/idf-extra-components.git +repository_info: + commit_sha: 2de4980640bbe3d2d69473d7251640039e185b92 + path: network_provisioning +url: https://github.com/espressif/idf-extra-components/tree/master/network_provisioning +version: 1.2.4 diff --git a/components/network_provisioning/include/network_provisioning/manager.h b/components/network_provisioning/include/network_provisioning/manager.h new file mode 100644 index 0000000..1aae0e0 --- /dev/null +++ b/components/network_provisioning/include/network_provisioning/manager.h @@ -0,0 +1,792 @@ +/* + * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#include "esp_event.h" +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#include "esp_wifi_types.h" +#endif +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +#include "openthread/dataset.h" +#endif +#include "network_provisioning/network_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +ESP_EVENT_DECLARE_BASE(NETWORK_PROV_EVENT); + +/** + * @brief Events generated by manager + * + * These events are generated in order of declaration and, for the + * stretch of time between initialization and de-initialization of + * the manager, each event is signaled only once + */ +typedef enum { + /** + * Emitted when the manager is initialized + */ + NETWORK_PROV_INIT, + + /** + * Indicates that provisioning has started + */ + NETWORK_PROV_START, +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + /** + * Emitted before accepting the wifi credentials to + * set the wifi configurations according to requirement. + * NOTE - In this case event_data shall be populated with a pointer to `wifi_config_t`. + */ + NETWORK_PROV_SET_WIFI_STA_CONFIG, + + /** + * Emitted when Wi-Fi AP credentials are received via `protocomm` + * endpoint `network_config`. The event data in this case is a pointer + * to the corresponding `wifi_sta_config_t` structure + */ + NETWORK_PROV_WIFI_CRED_RECV, +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + /** + * Emitted when Thread Dataset is received via `protocomm` endpoint + * `network_config`, The event data in this case is a pointer to the + * corresponding `otOperationalDatasetTlvs` structure + */ + NETWORK_PROV_THREAD_DATASET_RECV, +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + /** + * Emitted when device fails to connect to the AP of which the + * credentials were received earlier on event `NETWORK_PROV_WIFI_CRED_RECV`. + * The event data in this case is a pointer to the disconnection + * reason code with type `network_prov_wifi_sta_fail_reason_t` + */ + NETWORK_PROV_WIFI_CRED_FAIL, +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + /** + * Emitted when device fails to connect to the Thread network of which + * dataset was received earlier on event `NETWORK_PROv_THREAD_DATASET_RECV`. + * The event data in this case is a pointer to the disconnection + * reason code with type `network_prov_thread_fail_reason_t` + */ + NETWORK_PROV_THREAD_DATASET_FAIL, +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + /** + * Emitted when device successfully connects to the AP of which the + * credentials were received earlier on event `NETWORK_PROV_WIFI_CRED_RECV` + */ + NETWORK_PROV_WIFI_CRED_SUCCESS, +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + /** + * Emitted when device successfully connects to the Thread etwork of + * which the dataset was received earlier on event + * `NETWORK_PROV_THREAD_DATASET_RECV` + */ + NETWORK_PROV_THREAD_DATASET_SUCCESS, +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + + /** + * Signals that provisioning service has stopped + */ + NETWORK_PROV_END, + + /** + * Signals that manager has been de-initialized + */ + NETWORK_PROV_DEINIT, +} network_prov_cb_event_t; + +typedef void (*network_prov_cb_func_t)(void *user_data, network_prov_cb_event_t event, void *event_data); + +/** + * @brief Event handler that is used by the manager while + * provisioning service is active + */ +typedef struct { + /** + * Callback function to be executed on provisioning events + */ + network_prov_cb_func_t event_cb; + + /** + * User context data to pass as parameter to callback function + */ + void *user_data; +} network_prov_event_handler_t; + +/** + * @brief Structure holding the configuration related to Wi-Fi provisioning + */ +typedef struct { + /** + * Maximum number of allowed connection attempts for Wi-Fi. If value 0 + * same as legacy behavior of infinite connection attempts. + */ + uint32_t wifi_conn_attempts; +} network_prov_wifi_conn_cfg_t; + +/** + * @brief Event handler can be set to none if not used + */ +#define NETWORK_PROV_EVENT_HANDLER_NONE { \ + .event_cb = NULL, \ + .user_data = NULL \ +} + +/** + * @brief Structure for specifying the provisioning scheme to be + * followed by the manager + * + * @note Ready to use schemes are available: + * - network_prov_scheme_ble : for provisioning over BLE transport + GATT server + * - network_prov_scheme_softap : for provisioning over SoftAP transport + HTTP server + * - network_prov_scheme_console : for provisioning over Serial UART transport + Console (for debugging) + */ +typedef struct network_prov_scheme { + /** + * Function which is to be called by the manager when it is to + * start the provisioning service associated with a protocomm instance + * and a scheme specific configuration + */ + esp_err_t (*prov_start) (protocomm_t *pc, void *config); + + /** + * Function which is to be called by the manager to stop the + * provisioning service previously associated with a protocomm instance + */ + esp_err_t (*prov_stop) (protocomm_t *pc); + + /** + * Function which is to be called by the manager to generate + * a new configuration for the provisioning service, that is + * to be passed to prov_start() + */ + void *(*new_config) (void); + + /** + * Function which is to be called by the manager to delete a + * configuration generated using new_config() + */ + void (*delete_config) (void *config); + + /** + * Function which is to be called by the manager to set the + * service name and key values in the configuration structure + */ + esp_err_t (*set_config_service) (void *config, const char *service_name, const char *service_key); + + /** + * Function which is to be called by the manager to set a protocomm endpoint + * with an identifying name and UUID in the configuration structure + */ + esp_err_t (*set_config_endpoint) (void *config, const char *endpoint_name, uint16_t uuid); + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + /** + * Sets mode of operation of Wi-Fi during provisioning + * This is set to : + * - WIFI_MODE_APSTA for SoftAP transport + * - WIFI_MODE_STA for BLE transport + */ + wifi_mode_t wifi_mode; +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +} network_prov_scheme_t; + +/** + * @brief Structure for specifying the manager configuration + */ +typedef struct { + /** + * Provisioning scheme to use. Following schemes are already available: + * - network_prov_scheme_ble : for provisioning over BLE transport + GATT server + * - network_prov_scheme_softap : for provisioning over SoftAP transport + HTTP server + mDNS (optional) + * - network_prov_scheme_console : for provisioning over Serial UART transport + Console (for debugging) + */ + network_prov_scheme_t scheme; + + /** + * Event handler required by the scheme for incorporating scheme specific + * behavior while provisioning manager is running. Various options may be + * provided by the scheme for setting this field. Use NETWORK_PROV_EVENT_HANDLER_NONE + * when not used. When using scheme network_prov_scheme_ble, the following + * options are available: + * - NETWORK_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM + * - NETWORK_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BLE + * - NETWORK_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BT + */ + network_prov_event_handler_t scheme_event_handler; + + /** + * Event handler that can be set for the purpose of incorporating application + * specific behavior. Use NETWORK_PROV_EVENT_HANDLER_NONE when not used. + */ + network_prov_event_handler_t app_event_handler; +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + /** + * This config holds the Wi-Fi provisioning related configurations. + */ + network_prov_wifi_conn_cfg_t network_prov_wifi_conn_cfg; +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +} network_prov_mgr_config_t; + +/** + * @brief Security modes supported by the Provisioning Manager. + * + * These are same as the security modes provided by protocomm + */ +typedef enum network_prov_security { +#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0 + /** + * No security (plain-text communication) + */ + NETWORK_PROV_SECURITY_0 = 0, +#endif +#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1 + /** + * This secure communication mode consists of + * X25519 key exchange + * + proof of possession (pop) based authentication + * + AES-CTR encryption + */ + NETWORK_PROV_SECURITY_1 = 1, +#endif +#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2 + /** + * This secure communication mode consists of + * SRP6a based authentication and key exchange + * + AES-GCM encryption/decryption + */ + NETWORK_PROV_SECURITY_2 = 2 +#endif +#if !CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0 && !CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1 && !CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2 +#error "All of the protocomm security versions are disabled. Make sure to enable at least one security version." +#endif +} network_prov_security_t; + +/** + * @brief Security 1 params structure + * This needs to be passed when using NETWORK_PROV_SECURITY_1 + */ +typedef const char network_prov_security1_params_t; + +/** + * @brief Security 2 params structure + * This needs to be passed when using NETWORK_PROV_SECURITY_2 + */ +typedef protocomm_security2_params_t network_prov_security2_params_t; + +/** + * @brief Initialize provisioning manager instance + * + * Configures the manager and allocates internal resources + * + * Configuration specifies the provisioning scheme (transport) + * and event handlers + * + * Event NETWORK_PROV_INIT is emitted right after initialization + * is complete + * + * @param[in] config Configuration structure + * + * @return + * - ESP_OK : Success + * - ESP_FAIL : Fail + */ +esp_err_t network_prov_mgr_init(network_prov_mgr_config_t config); + +/** + * @brief Stop provisioning (if running) and release + * resource used by the manager + * + * Event NETWORK_PROV_DEINIT is emitted right after de-initialization + * is finished + * + * If provisioning service is still active when this API is called, + * it first stops the service, hence emitting NETWORK_PROV_END, and + * then performs the de-initialization + * + * @return + * - ESP_OK : Success + * - ESP_FAIL : Failed to post event NETWORK_PROV_DEINIT or NETWORK_PROV_END + * - ESP_ERR_NO_MEM : Out of memory (as may be returned by esp_event_post) + */ +esp_err_t network_prov_mgr_deinit(void); + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +/** + * @brief Checks if device is provisioned + * + * This checks if Wi-Fi credentials are present on the NVS + * + * The Wi-Fi credentials are assumed to be kept in the same + * NVS namespace as used by esp_wifi component + * + * If one were to call esp_wifi_set_config() directly instead + * of going through the provisioning process, this function will + * still yield true (i.e. device will be found to be provisioned) + * + * @note Calling network_prov_mgr_start_provisioning() automatically + * resets the provision state, irrespective of what the + * state was prior to making the call. + * + * @param[out] provisioned True if provisioned, else false + * + * @return + * - ESP_OK : Retrieved provision state successfully + * - ESP_FAIL : Wi-Fi not initialized + * - ESP_ERR_INVALID_ARG : Null argument supplied + */ +esp_err_t network_prov_mgr_is_wifi_provisioned(bool *provisioned); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +/** + * @brief Checks if device is provisioned + * + * This checks if there is active dataset for Thread device + * + * @note Calling network_prov_mgr_start_provisioning() automatically + * resets the provision state, irrespective of what the + * state was prior to making the call. + * + * @param[out] provisioned True if provisioned, else false + * + * @return + * - ESP_OK : Retrieved provision state successfully + * - ESP_FAIL : Not initialized + * - ESP_ERR_INVALID_ARG : Null argument supplied + */ +esp_err_t network_prov_mgr_is_thread_provisioned(bool *provisioned); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + +/** + * @brief Checks whether the provisioning state machine is idle + * + * @return True if state machine is idle, else false + */ +bool network_prov_mgr_is_sm_idle(void); + +/** + * @brief Start provisioning service + * + * This starts the provisioning service according to the scheme + * configured at the time of initialization. For scheme : + * - network_prov_scheme_ble : This starts protocomm_ble, which internally initializes + * BLE transport and starts GATT server for handling + * provisioning requests + * - network_prov_scheme_softap : This activates SoftAP mode of Wi-Fi and starts + * protocomm_httpd, which internally starts an HTTP + * server for handling provisioning requests (If mDNS is + * active it also starts advertising service with type + * _esp_wifi_prov._tcp) + * + * Event NETWORK_PROV_START is emitted right after provisioning starts without failure + * + * @note This API will start provisioning service even if device is found to be + * already provisioned, i.e. network_prov_mgr_is_wifi_provisioned() yields true + * + * @param[in] security Specify which protocomm security scheme to use : + * - NETWORK_PROV_SECURITY_0 : For no security + * - NETWORK_PROV_SECURITY_1 : x25519 secure handshake for session + * establishment followed by AES-CTR encryption of provisioning messages + * - NETWORK_PROV_SECURITY_2: SRP6a based authentication and key exchange + * followed by AES-GCM encryption/decryption of provisioning messages + * @param[in] network_prov_sec_params + * Pointer to security params (NULL if not needed). + * This is not needed for protocomm security 0 + * This pointer should hold the struct of type + * network_prov_security1_params_t for protocomm security 1 + * and network_prov_security2_params_t for protocomm security 2 respectively. + * This pointer and its contents should be valid till the provisioning service is + * running and has not been stopped or de-inited. + * @param[in] service_name Unique name of the service. This translates to: + * - Wi-Fi SSID when provisioning mode is softAP + * - Device name when provisioning mode is BLE + * @param[in] service_key Key required by client to access the service (NULL if not needed). + * This translates to: + * - Wi-Fi password when provisioning mode is softAP + * - ignored when provisioning mode is BLE + * + * @return + * - ESP_OK : Provisioning started successfully + * - ESP_FAIL : Failed to start provisioning service + * - ESP_ERR_INVALID_STATE : Provisioning manager not initialized or already started + */ +esp_err_t network_prov_mgr_start_provisioning(network_prov_security_t security, const void *network_prov_sec_params, const char *service_name, const char *service_key); + +/** + * @brief Stop provisioning service + * + * If provisioning service is active, this API will initiate a process to stop + * the service and return. Once the service actually stops, the event NETWORK_PROV_END + * will be emitted. + * + * If network_prov_mgr_deinit() is called without calling this API first, it will + * automatically stop the provisioning service and emit the NETWORK_PROV_END, followed + * by NETWORK_PROV_DEINIT, before returning. + * + * This API will generally be used along with network_prov_mgr_disable_auto_stop() + * in the scenario when the main application has registered its own endpoints, + * and wishes that the provisioning service is stopped only when some protocomm + * command from the client side application is received. + * + * Calling this API inside an endpoint handler, with sufficient cleanup_delay, + * will allow the response / acknowledgment to be sent successfully before the + * underlying protocomm service is stopped. + * + * Cleaup_delay is set when calling network_prov_mgr_disable_auto_stop(). + * If not specified, it defaults to 1000ms. + * + * For straightforward cases, using this API is usually not necessary as + * provisioning is stopped automatically once NETWORK_PROV_CRED_SUCCESS is emitted. + * Stopping is delayed (maximum 30 seconds) thus allowing the client side + * application to query for network state, i.e. after receiving the first query + * and sending `network state connected` response the service is stopped immediately. + */ +void network_prov_mgr_stop_provisioning(void); + +/** + * @brief Wait for provisioning service to finish + * + * Calling this API will block until provisioning service is stopped + * i.e. till event NETWORK_PROV_END is emitted. + * + * This will not block if provisioning is not started or not initialized. + */ +void network_prov_mgr_wait(void); + +/** + * @brief Disable auto stopping of provisioning service upon completion + * + * By default, once provisioning is complete, the provisioning service is automatically + * stopped, and all endpoints (along with those registered by main application) are + * deactivated. + * + * This API is useful in the case when main application wishes to close provisioning service + * only after it receives some protocomm command from the client side app. For example, after + * connecting to network, the device may want to connect to the cloud, and only once that is + * successfully, the device is said to be fully configured. But, then it is upto the main + * application to explicitly call network_prov_mgr_stop_provisioning() later when the device is + * fully configured and the provisioning service is no longer required. + * + * @note This must be called before executing network_prov_mgr_start_provisioning() + * + * @param[in] cleanup_delay Sets the delay after which the actual cleanup of transport related + * resources is done after a call to network_prov_mgr_stop_provisioning() + * returns. Minimum allowed value is 100ms. If not specified, this will + * default to 1000ms. + * + * @return + * - ESP_OK : Success + * - ESP_ERR_INVALID_STATE : Manager not initialized or + * provisioning service already started + */ +esp_err_t network_prov_mgr_disable_auto_stop(uint32_t cleanup_delay); + +/** + * @brief Set application version and capabilities in the JSON data returned by + * proto-ver endpoint + * + * This function can be called multiple times, to specify information about the various + * application specific services running on the device, identified by unique labels. + * + * The provisioning service itself registers an entry in the JSON data, by the label "prov", + * containing only provisioning service version and capabilities. Application services should + * use a label other than "prov" so as not to overwrite this. + * + * @note This must be called before executing network_prov_mgr_start_provisioning() + * + * @param[in] label String indicating the application name. + * + * @param[in] version String indicating the application version. + * There is no constraint on format. + * + * @param[in] capabilities Array of strings with capabilities. + * These could be used by the client side app to know + * the application registered endpoint capabilities + * + * @param[in] total_capabilities Size of capabilities array + * + * @return + * - ESP_OK : Success + * - ESP_ERR_INVALID_STATE : Manager not initialized or + * provisioning service already started + * - ESP_ERR_NO_MEM : Failed to allocate memory for version string + * - ESP_ERR_INVALID_ARG : Null argument + */ +esp_err_t network_prov_mgr_set_app_info(const char *label, const char *version, + const char **capabilities, size_t total_capabilities); + +/** + * @brief Create an additional endpoint and allocate internal resources for it + * + * This API is to be called by the application if it wants to create an additional + * endpoint. All additional endpoints will be assigned UUIDs starting from 0xFF54 + * and so on in the order of execution. + * + * protocomm handler for the created endpoint is to be registered later using + * network_prov_mgr_endpoint_register() after provisioning has started. + * + * @note This API can only be called BEFORE provisioning is started + * + * @note Additional endpoints can be used for configuring client provided + * parameters other than Wi-Fi credentials or Thread dataset, that are necessary + * for the main application and hence must be set prior to starting the application + * + * @note After session establishment, the additional endpoints must be targeted + * first by the client side application before sending Wi-Fi/Thread configuration, + * because once Wi-Fi/Thread configuration finishes the provisioning service is + * stopped and hence all endpoints are unregistered + * + * @param[in] ep_name unique name of the endpoint + * + * @return + * - ESP_OK : Success + * - ESP_FAIL : Failure + */ +esp_err_t network_prov_mgr_endpoint_create(const char *ep_name); + +/** + * @brief Register a handler for the previously created endpoint + * + * This API can be called by the application to register a protocomm handler + * to any endpoint that was created using network_prov_mgr_endpoint_create(). + * + * @note This API can only be called AFTER provisioning has started + * + * @note Additional endpoints can be used for configuring client provided + * parameters other than Wi-Fi credentials or Thread dataset, that are necessary + * for the main application and hence must be set prior to starting the application + * + * @note After session establishment, the additional endpoints must be targeted + * first by the client side application before sending Wi-Fi/Thread configuration, + * because once Wi-Fi/Thread configuration finishes the provisioning service is + * stopped and hence all endpoints are unregistered + * + * @param[in] ep_name Name of the endpoint + * @param[in] handler Endpoint handler function + * @param[in] user_ctx User data + * + * @return + * - ESP_OK : Success + * - ESP_FAIL : Failure + */ +esp_err_t network_prov_mgr_endpoint_register(const char *ep_name, + protocomm_req_handler_t handler, + void *user_ctx); + +/** + * @brief Unregister the handler for an endpoint + * + * This API can be called if the application wants to selectively + * unregister the handler of an endpoint while the provisioning + * is still in progress. + * + * All the endpoint handlers are unregistered automatically when + * the provisioning stops. + * + * @param[in] ep_name Name of the endpoint + */ +void network_prov_mgr_endpoint_unregister(const char *ep_name); + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +/** + * @brief Get state of Wi-Fi Station during provisioning + * + * @param[out] state Pointer to network_prov_wifi_sta_state_t + * variable to be filled + * + * @return + * - ESP_OK : Successfully retrieved Wi-Fi state + * - ESP_FAIL : Provisioning app not running + */ +esp_err_t network_prov_mgr_get_wifi_state(network_prov_wifi_sta_state_t *state); + +/** + * @brief Get reason code in case of Wi-Fi station + * disconnection during provisioning + * +* @param[out] reason Pointer to network_prov_wifi_sta_fail_reason_t +* variable to be filled + * + * @return + * - ESP_OK : Successfully retrieved Wi-Fi disconnect reason + * - ESP_FAIL : Provisioning app not running + */ +esp_err_t network_prov_mgr_get_wifi_disconnect_reason(network_prov_wifi_sta_fail_reason_t *reason); + +/** + * @brief Runs Wi-Fi as Station with the supplied configuration + * + * Configures the Wi-Fi station mode to connect to the AP with + * SSID and password specified in config structure and sets + * Wi-Fi to run as station. + * + * This is automatically called by provisioning service upon + * receiving new credentials. + * + * If credentials are to be supplied to the manager via a + * different mode other than through protocomm, then this + * API needs to be called. + * + * Event NETWORK_PROV_CRED_RECV is emitted after credentials have + * been applied and Wi-Fi station started + * + * @param[in] wifi_cfg Pointer to Wi-Fi configuration structure + * + * @return + * - ESP_OK : Wi-Fi configured and started successfully + * - ESP_FAIL : Failed to set configuration + */ +esp_err_t network_prov_mgr_configure_wifi_sta(wifi_config_t *wifi_cfg); + +/** + * @brief Reset Wi-Fi provisioning config + * + * Calling this API will restore WiFi stack persistent settings to default values. + * + * @return + * - ESP_OK : Reset provisioning config successfully + * - ESP_FAIL : Failed to reset provisioning config + */ +esp_err_t network_prov_mgr_reset_wifi_provisioning(void); + +/** + * @brief Reset internal state machine and clear provisioned credentials. + * + * This API should be used to restart provisioning ONLY in the case + * of provisioning failures without rebooting the device. + * + * @return + * - ESP_OK : Reset provisioning state machine successfully + * - ESP_FAIL : Failed to reset provisioning state machine + * - ESP_ERR_INVALID_STATE : Manager not initialized + */ +esp_err_t network_prov_mgr_reset_wifi_sm_state_on_failure(void); + +/** + * @brief Reset internal state machine and clear provisioned credentials. + * + * This API can be used to restart provisioning ONLY in case the device is + * to be provisioned again for new credentials after a previous successful + * provisioning without rebooting the device. + * + * @note This API can be used only if provisioning auto-stop has been + * disabled using network_prov_mgr_disable_auto_stop() + * + * @return + * - ESP_OK : Reset provisioning state machine successfully + * - ESP_FAIL : Failed to reset provisioning state machine + * - ESP_ERR_INVALID_STATE : Manager not initialized + */ +esp_err_t network_prov_mgr_reset_wifi_sm_state_for_reprovision(void); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +/** + * @brief Reset Thread provisioning config + * + * Calling this API will restore Thread stack persistent settings to default values. + * + * @return + * - ESP_OK : Reset provisioning config successfully + * - ESP_FAIL : Failed to reset provisioning config + */ +esp_err_t network_prov_mgr_reset_thread_provisioning(void); + +/** + * @brief Get state of Thread during provisioning + * + * @param[out] state Pointer to network_prov_thread_state_t + * variable to be filled + * + * @return + * - ESP_OK : Successfully retrieved Thread state + * - ESP_FAIL : Provisioning app not running + */ +esp_err_t network_prov_mgr_get_thread_state(network_prov_thread_state_t *state); + +/** + * @brief Get reason code in case of thread detached during provisioning + * +* @param[out] reason Pointer to network_prov_thread_fail_reason_t +* variable to be filled + * + * @return + * - ESP_OK : Successfully retrieved thread detached reason + * - ESP_FAIL : Provisioning app not running + */ +esp_err_t network_prov_mgr_get_thread_detached_reason(network_prov_thread_fail_reason_t *reason); + +/** + * @brief Runs Thread with the supplied configuration + * + * Configures the Thread Dataset so that the device can be attached + * to a specific Thread network + * + * This is automatically called by provisioning service upon + * receiving new Thread dataset. + * + * If the dataset is to be supplied to the manager via a + * different mode other than through protocomm, then this + * API needs to be called. + * + * Event THREAD_PROV_CRED_RECV is emitted after credentials have + * been applied and Thread started + * + * @param[in] thread_dataset Pointer to Dataset Tlvs structure + * + * @return + * - ESP_OK : Thread dataset configured and started successfully + * - ESP_FAIL : Failed to set configuration + */ +esp_err_t network_prov_mgr_configure_thread_dataset(otOperationalDatasetTlvs *thread_dataset); + +/** + * @brief Reset internal state machine and clear provisioned credentials. + * + * This API should be used to restart provisioning ONLY in the case + * of provisioning failures without rebooting the device. + * + * @return + * - ESP_OK : Reset provisioning state machine successfully + * - ESP_FAIL : Failed to reset provisioning state machine + * - ESP_ERR_INVALID_STATE : Manager not initialized + */ +esp_err_t network_prov_mgr_reset_thread_sm_state_on_failure(void); + +/** + * @brief Reset internal state machine and clear provisioned credentials. + * + * This API can be used to restart provisioning ONLY in case the device is + * to be provisioned again for new credentials after a previous successful + * provisioning without rebooting the device. + * + * @note This API can be used only if provisioning auto-stop has been + * disabled using network_prov_mgr_disable_auto_stop() + * + * @return + * - ESP_OK : Reset provisioning state machine successfully + * - ESP_FAIL : Failed to reset provisioning state machine + * - ESP_ERR_INVALID_STATE : Manager not initialized + */ +esp_err_t network_prov_mgr_reset_thread_sm_state_for_reprovision(void); + +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + + +#ifdef __cplusplus +} +#endif diff --git a/components/network_provisioning/include/network_provisioning/network_config.h b/components/network_provisioning/include/network_provisioning/network_config.h new file mode 100644 index 0000000..2bc6028 --- /dev/null +++ b/components/network_provisioning/include/network_provisioning/network_config.h @@ -0,0 +1,219 @@ +/* + * SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _NETWORK_PROV_CONFIG_H_ +#define _NETWORK_PROV_CONFIG_H_ + +#include "esp_netif_ip_addr.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +/** + * @brief WiFi STA status for conveying back to the provisioning master + */ +typedef enum { + NETWORK_PROV_WIFI_STA_CONNECTING, + NETWORK_PROV_WIFI_STA_CONNECTED, + NETWORK_PROV_WIFI_STA_DISCONNECTED, + NETWORK_PROV_WIFI_STA_CONN_ATTEMPT_FAILED +} network_prov_wifi_sta_state_t; + +/** + * @brief WiFi STA connection fail reason + */ +typedef enum { + NETWORK_PROV_WIFI_STA_AUTH_ERROR, + NETWORK_PROV_WIFI_STA_AP_NOT_FOUND +} network_prov_wifi_sta_fail_reason_t; + +/** + * @brief WiFi STA connected status information + */ +typedef struct { + /** + * IP Address received by station + */ + char ip_addr[IP4ADDR_STRLEN_MAX]; + + char bssid[6]; /*!< BSSID of the AP to which connection was estalished */ + char ssid[33]; /*!< SSID of the to which connection was estalished */ + uint8_t channel; /*!< Channel of the AP */ + uint8_t auth_mode; /*!< Authorization mode of the AP */ +} network_prov_wifi_sta_conn_info_t; + +/** + * @brief WiFi STA connecting status information + */ +typedef struct { + uint32_t attempts_remaining; /*!< Number of Wi-Fi connection attempts remaining */ +} network_prov_wifi_sta_connecting_info_t; + +/** + * @brief WiFi status data to be sent in response to `get_status` request from master + */ +typedef struct { + network_prov_wifi_sta_state_t wifi_state; /*!< WiFi state of the station */ + union { + /** + * Reason for disconnection (valid only when `wifi_state` is `WIFI_STATION_DISCONNECTED`) + */ + network_prov_wifi_sta_fail_reason_t fail_reason; + + /** + * Connection information (valid only when `wifi_state` is `WIFI_STATION_CONNECTED`) + */ + network_prov_wifi_sta_conn_info_t conn_info; + + /** + * Connecting information (valid only when `wifi_state` is `WIFI_STATION_CONNECTING`) + */ + network_prov_wifi_sta_connecting_info_t connecting_info; + }; +} network_prov_config_get_wifi_data_t; + +/** + * @brief WiFi config data received by slave during `set_config` request from master + */ +typedef struct { + char ssid[33]; /*!< SSID of the AP to which the slave is to be connected */ + char password[64]; /*!< Password of the AP */ + char bssid[6]; /*!< BSSID of the AP */ + uint8_t channel; /*!< Channel of the AP */ +} network_prov_config_set_wifi_data_t; +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +/** + * @brief Thread status for conveying back to the provisioning master + */ +typedef enum { + NETWORK_PROV_THREAD_ATTACHING, + NETWORK_PROV_THREAD_ATTACHED, + NETWORK_PROV_THREAD_DETACHED, +} network_prov_thread_state_t; + +/** + * @brief Thread attaching fail reason + */ +typedef enum { + NETWORK_PROV_THREAD_DATASET_INVALID, + NETWORK_PROV_THREAD_NETWORK_NOT_FOUND, +} network_prov_thread_fail_reason_t; + +/** + * @brief Thread attached status information + */ +typedef struct { + uint32_t pan_id; /*!< PAN ID */ + uint16_t channel; /*!< Channel */ + char name[17]; /*!< Network Name */ + uint8_t ext_pan_id[8]; /*!< Extended PAN ID */ +} network_prov_thread_conn_info_t; + +/** + * @brief Thread status data to be sent in response to `get_status` request from master + */ +typedef struct { + network_prov_thread_state_t thread_state; + union { + /** + * Reason for disconnection (valid only when `thread_state` is `THREAD_DETACHED`) + */ + network_prov_thread_fail_reason_t fail_reason; + + /** + * Connection information (valid only when `thread_state` is `THREAD_ATTACHED`) + */ + network_prov_thread_conn_info_t conn_info; + }; +} network_prov_config_get_thread_data_t; + +/** + * @brief Thread config data received by slave during `set_config` request from master + */ +typedef struct { + uint8_t dataset[254]; + uint8_t length; +} network_prov_config_set_thread_data_t; +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + +/** + * @brief Type of context data passed to each get/set/apply handler + * function set in `network_prov_config_handlers` structure. + * + * This is passed as an opaque pointer, thereby allowing it be defined + * later in application code as per requirements. + */ +typedef struct network_prov_ctx network_prov_ctx_t; + +/** + * @brief Internal handlers for receiving and responding to protocomm + * requests from master + * + * This is to be passed as priv_data for protocomm request handler + * (refer to `network_prov_config_data_handler()`) when calling `protocomm_add_endpoint()`. + */ +typedef struct network_prov_config_handlers { +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + /** + * Handler function called when connection status + * of the slave (in WiFi station mode) is requested + */ + esp_err_t (*wifi_get_status_handler)(network_prov_config_get_wifi_data_t *resp_data, + network_prov_ctx_t **ctx); + + /** + * Handler function called when WiFi connection configuration + * (eg. AP SSID, password, etc.) of the slave (in WiFi station mode) + * is to be set to user provided values + */ + esp_err_t (*wifi_set_config_handler)(const network_prov_config_set_wifi_data_t *req_data, + network_prov_ctx_t **ctx); + + /** + * Handler function for applying the configuration that was set in + * `set_config_handler`. After applying the station may get connected to + * the AP or may fail to connect. The slave must be ready to convey the + * updated connection status information when `get_status_handler` is + * invoked again by the master. + */ + esp_err_t (*wifi_apply_config_handler)(network_prov_ctx_t **ctx); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + esp_err_t (*thread_get_status_handler)(network_prov_config_get_thread_data_t *resp_data, + network_prov_ctx_t **ctx); + + esp_err_t (*thread_set_config_handler)(const network_prov_config_set_thread_data_t *req_data, + network_prov_ctx_t **ctx); + + esp_err_t (*thread_apply_config_handler)(network_prov_ctx_t **ctx); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + + /** + * Context pointer to be passed to above handler functions upon invocation + */ + network_prov_ctx_t *ctx; +} network_prov_config_handlers_t; + +/** + * @brief Handler for receiving and responding to requests from master + * + * This is to be registered as the `network_config` endpoint handler + * (protocomm `protocomm_req_handler_t`) using `protocomm_add_endpoint()` + */ +esp_err_t network_prov_config_data_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, + uint8_t **outbuf, ssize_t *outlen, void *priv_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/network_provisioning/include/network_provisioning/network_scan.h b/components/network_provisioning/include/network_provisioning/network_scan.h new file mode 100644 index 0000000..efec873 --- /dev/null +++ b/components/network_provisioning/include/network_provisioning/network_scan.h @@ -0,0 +1,193 @@ +/* + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _PROV_NETWORK_SCAN_H_ +#define _PROV_NETWORK_SCAN_H_ + +#ifdef __cplusplus +extern "C" { +#endif +#include +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#include + +#define WIFI_SSID_LEN sizeof(((wifi_ap_record_t *)0)->ssid) +#define WIFI_BSSID_LEN sizeof(((wifi_ap_record_t *)0)->bssid) +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +#include +#include +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + +/** + * @brief Type of context data passed to each get/set/apply handler + * function set in `network_prov_scan_handlers` structure. + * + * This is passed as an opaque pointer, thereby allowing it be defined + * later in application code as per requirements. + */ +typedef struct network_prov_scan_ctx network_prov_scan_ctx_t; + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +/** + * @brief Structure of entries in the scan results list + */ +typedef struct { + /** + * SSID of Wi-Fi AP + */ + char ssid[WIFI_SSID_LEN]; + + /** + * BSSID of Wi-Fi AP + */ + char bssid[WIFI_BSSID_LEN]; + + /** + * Wi-Fi channel number + */ + uint8_t channel; + + /** + * Signal strength + */ + int rssi; + + /** + * Wi-Fi security mode + */ + uint8_t auth; +} network_prov_scan_wifi_result_t; +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +typedef struct { + uint16_t pan_id; + uint8_t ext_pan_id[OT_EXT_ADDRESS_SIZE]; + char network_name[OT_NETWORK_NAME_MAX_SIZE + 1]; + uint16_t channel; + uint8_t ext_addr[OT_EXT_ADDRESS_SIZE]; + int8_t rssi; + uint8_t lqi; +} network_prov_scan_thread_result_t; +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + +/** + * @brief Internal handlers for receiving and responding to protocomm + * requests from client + * + * This is to be passed as priv_data for protocomm request handler + * (refer to `network_prov_scan_handler()`) when calling `protocomm_add_endpoint()`. + */ +typedef struct network_prov_scan_handlers { +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + /** + * Handler function called when scan start command is received + * with various scan parameters : + * + * blocking (input) - If true, the function should return only + * when the scanning is finished + * + * passive (input) - If true, scan is to be started in passive + * mode (this may be slower) instead of active mode + * + * group_channels (input) - This specifies whether to scan + * all channels in one go (when zero) or perform scanning of + * channels in groups, with 120ms delay between scanning of + * consecutive groups, and the value of this parameter sets the + * number of channels in each group. This is useful when transport + * mode is SoftAP, where scanning all channels in one go may not + * give the Wi-Fi driver enough time to send out beacons, and + * hence may cause disconnection with any connected stations. + * When scanning in groups, the manager will wait for at least + * 120ms after completing scan on a group of channels, and thus + * allow the driver to send out the beacons. For example, given + * that the total number of Wi-Fi channels is 14, then setting + * group_channels to 4, will create 5 groups, with each group + * having 3 channels, except the last one which will have + * 14 % 3 = 2 channels. So, when scan is started, the first 3 + * channels will be scanned, followed by a 120ms delay, and then + * the next 3 channels, and so on, until all the 14 channels have + * been scanned. One may need to adjust this parameter as having + * only few channels in a group may slow down the overall scan + * time, while having too many may again cause disconnection. + * Usually a value of 4 should work for most cases. Note that + * for any other mode of transport, e.g. BLE, this can be safely + * set to 0, and hence achieve the fastest overall scanning time. + * + * period_ms (input) - Scan parameter specifying how long to + * wait on each channel (in milli-seconds) + */ + esp_err_t (*wifi_scan_start)(bool blocking, bool passive, + uint8_t group_channels, uint32_t period_ms, + network_prov_scan_ctx_t **ctx); + + /** + * Handler function called when scan status is requested. Status + * is given the parameters : + * + * scan_finished (output) - When scan has finished this returns true + * + * result_count (output) - This gives the total number of results + * obtained till now. If scan is yet happening this number will + * keep on updating + */ + esp_err_t (*wifi_scan_status)(bool *scan_finished, + uint16_t *result_count, + network_prov_scan_ctx_t **ctx); + + /** + * Handler function called when scan result is requested. Parameters : + * + * scan_result - For fetching scan results. This can be called even + * if scan is still on going + * + * start_index (input) - Starting index from where to fetch the + * entries from the results list + * + * count (input) - Number of entries to fetch from the starting index + * + * entries (output) - List of entries returned. Each entry consists + * of ssid, channel and rssi information + */ + esp_err_t (*wifi_scan_result)(uint16_t result_index, + network_prov_scan_wifi_result_t *result, + network_prov_scan_ctx_t **ctx); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + esp_err_t (*thread_scan_start)(bool blocking, uint32_t channel_mask, network_prov_scan_ctx_t **ctx); + + esp_err_t (*thread_scan_status)(bool *scan_finished, + uint16_t *result_count, + network_prov_scan_ctx_t **ctx); + + esp_err_t (*thread_scan_result)(uint16_t result_index, + network_prov_scan_thread_result_t *result, + network_prov_scan_ctx_t **ctx); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + + /** + * Context pointer to be passed to above handler functions upon invocation + */ + network_prov_scan_ctx_t *ctx; +} network_prov_scan_handlers_t; + +/** + * @brief Handler for sending on demand Wi-Fi scan results + * + * This is to be registered as the `prov-scan` endpoint handler + * (protocomm `protocomm_req_handler_t`) using `protocomm_add_endpoint()` + */ +esp_err_t network_prov_scan_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, + uint8_t **outbuf, ssize_t *outlen, void *priv_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/network_provisioning/include/network_provisioning/scheme_ble.h b/components/network_provisioning/include/network_provisioning/scheme_ble.h new file mode 100644 index 0000000..74c1b93 --- /dev/null +++ b/components/network_provisioning/include/network_provisioning/scheme_ble.h @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#include "network_provisioning/manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Scheme that can be used by manager for provisioning + * over BLE transport with GATT server + */ +extern const network_prov_scheme_t network_prov_scheme_ble; + +/* This scheme specific event handler is to be used when application + * doesn't require BT and BLE after provisioning has finished */ +#define NETWORK_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM { \ + .event_cb = network_prov_scheme_ble_event_cb_free_btdm, \ + .user_data = NULL \ +} + +/* This scheme specific event handler is to be used when application + * doesn't require BLE to be active after provisioning has finished */ +#define NETWORK_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BLE { \ + .event_cb = network_prov_scheme_ble_event_cb_free_ble, \ + .user_data = NULL \ +} + +/* This scheme specific event handler is to be used when application + * doesn't require BT to be active after provisioning has finished */ +#define NETWORK_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BT { \ + .event_cb = network_prov_scheme_ble_event_cb_free_bt, \ + .user_data = NULL \ +} + +void network_prov_scheme_ble_event_cb_free_btdm(void *user_data, network_prov_cb_event_t event, void *event_data); +void network_prov_scheme_ble_event_cb_free_ble (void *user_data, network_prov_cb_event_t event, void *event_data); +void network_prov_scheme_ble_event_cb_free_bt (void *user_data, network_prov_cb_event_t event, void *event_data); + +/** + * @brief Set the 128 bit GATT service UUID used for provisioning + * + * This API is used to override the default 128 bit provisioning + * service UUID, which is 0000ffff-0000-1000-8000-00805f9b34fb. + * + * This must be called before starting provisioning, i.e. before + * making a call to network_prov_mgr_start_provisioning(), otherwise + * the default UUID will be used. + * + * @note The data being pointed to by the argument must be valid + * at least till provisioning is started. Upon start, the + * manager will store an internal copy of this UUID, and + * this data can be freed or invalidated afterwards. + * + * @param[in] uuid128 A custom 128 bit UUID + * + * @return + * - ESP_OK : Success + * - ESP_ERR_INVALID_ARG : Null argument + */ +esp_err_t network_prov_scheme_ble_set_service_uuid(uint8_t *uuid128); + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +/** + * @brief Keep the BLE on after provisioning + * + * This API is used to specify whether the BLE should remain on + * after the provisioning process has stopped. + * + * This must be called before starting provisioning, i.e. before + * making a call to network_prov_mgr_start_provisioning(), otherwise + * the default behavior will be used. + * + * @note The value being pointed to by the argument must be valid + * at least until provisioning is started. Upon start, the + * manager will store an internal copy of this value, and + * this data can be freed or invalidated afterwards. + * + * @param[in] is_on_after_ble_stop A boolean indicating if BLE should remain on after provisioning + * + * @return + * - ESP_OK : Success + * - ESP_ERR_INVALID_ARG : Null argument + */ +esp_err_t network_prov_mgr_keep_ble_on(uint8_t is_on_after_ble_stop); + +/** + * @brief Set Bluetooth Random address + * + * This must be called before starting provisioning, i.e. before + * making a call to network_prov_mgr_start_provisioning(). + * + * This API can be used in cases where a new identity address is to be used during + * provisioning. This will result in this device being treated as a new device by remote + * devices. + * + * @note This API will change the existing BD address for the device. The address once + * set will remain unchanged until BLE stack tear down happens when + * network_prov_mgr_deinit is invoked. + * + * This API is only to be called to set random address. Re-invoking this API + * after provisioning is started will have no effect. + * + * @param[in] rand_addr The static random address to be set of length 6 bytes. + * + * @return + * - ESP_OK : Success + * - ESP_ERR_INVALID_ARG : Null argument + */ +esp_err_t network_prov_scheme_ble_set_random_addr(const uint8_t *rand_addr); +#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) + +/** + * @brief Set manufacturer specific data in scan response + * + * This must be called before starting provisioning, i.e. before + * making a call to network_prov_mgr_start_provisioning(). + * + * @note It is important to understand that length of custom manufacturer + * data should be within limits. The manufacturer data goes into scan + * response along with BLE device name. By default, BLE device name + * length is of 11 Bytes, however it can vary as per application use + * case. So, one has to honour the scan response data size limits i.e. + * (mfg_data_len + 2) < 31 - (device_name_length + 2 ). If the + * mfg_data length exceeds this limit, the length will be truncated. + * + * @param[in] mfg_data Custom manufacturer data + * @param[in] mfg_data_len Manufacturer data length + * + * @return + * - ESP_OK : Success + * - ESP_ERR_INVALID_ARG : Null argument + */ +esp_err_t network_prov_scheme_ble_set_mfg_data(uint8_t *mfg_data, ssize_t mfg_data_len); + +#ifdef __cplusplus +} +#endif diff --git a/components/network_provisioning/include/network_provisioning/scheme_console.h b/components/network_provisioning/include/network_provisioning/scheme_console.h new file mode 100644 index 0000000..2f8e389 --- /dev/null +++ b/components/network_provisioning/include/network_provisioning/scheme_console.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#include "network_provisioning/manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Scheme that can be used by manager for provisioning + * over console (Serial UART) + */ +extern const network_prov_scheme_t network_prov_scheme_console; + +#ifdef __cplusplus +} +#endif diff --git a/components/network_provisioning/include/network_provisioning/scheme_softap.h b/components/network_provisioning/include/network_provisioning/scheme_softap.h new file mode 100644 index 0000000..1d90507 --- /dev/null +++ b/components/network_provisioning/include/network_provisioning/scheme_softap.h @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#include "network_provisioning/manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Scheme that can be used by manager for provisioning + * over SoftAP transport with HTTP server + */ +extern const network_prov_scheme_t network_prov_scheme_softap; + +/** + * + * @brief Provide HTTPD Server handle externally. + * + * Useful in cases wherein applications need the webserver for some + * different operations, and do not want the wifi provisioning component + * to start/stop a new instance. + * + * @note This API should be called before network_prov_mgr_start_provisioning() + * + * @param[in] handle Handle to HTTPD server instance + */ +void network_prov_scheme_softap_set_httpd_handle(void *handle); +#ifdef __cplusplus +} +#endif diff --git a/components/network_provisioning/proto-c/network_config.pb-c.c b/components/network_provisioning/proto-c/network_config.pb-c.c new file mode 100644 index 0000000..97cd677 --- /dev/null +++ b/components/network_provisioning/proto-c/network_config.pb-c.c @@ -0,0 +1,1345 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: network_config.proto */ + +/* Do not generate deprecated warnings for self */ +#ifndef PROTOBUF_C__NO_DEPRECATED +#define PROTOBUF_C__NO_DEPRECATED +#endif + +#include "network_config.pb-c.h" +void cmd_get_wifi_status__init + (CmdGetWifiStatus *message) +{ + static const CmdGetWifiStatus init_value = CMD_GET_WIFI_STATUS__INIT; + *message = init_value; +} +size_t cmd_get_wifi_status__get_packed_size + (const CmdGetWifiStatus *message) +{ + assert(message->base.descriptor == &cmd_get_wifi_status__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_get_wifi_status__pack + (const CmdGetWifiStatus *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_get_wifi_status__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_get_wifi_status__pack_to_buffer + (const CmdGetWifiStatus *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_get_wifi_status__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdGetWifiStatus * + cmd_get_wifi_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdGetWifiStatus *) + protobuf_c_message_unpack (&cmd_get_wifi_status__descriptor, + allocator, len, data); +} +void cmd_get_wifi_status__free_unpacked + (CmdGetWifiStatus *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_get_wifi_status__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_get_wifi_status__init + (RespGetWifiStatus *message) +{ + static const RespGetWifiStatus init_value = RESP_GET_WIFI_STATUS__INIT; + *message = init_value; +} +size_t resp_get_wifi_status__get_packed_size + (const RespGetWifiStatus *message) +{ + assert(message->base.descriptor == &resp_get_wifi_status__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_get_wifi_status__pack + (const RespGetWifiStatus *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_get_wifi_status__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_get_wifi_status__pack_to_buffer + (const RespGetWifiStatus *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_get_wifi_status__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespGetWifiStatus * + resp_get_wifi_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespGetWifiStatus *) + protobuf_c_message_unpack (&resp_get_wifi_status__descriptor, + allocator, len, data); +} +void resp_get_wifi_status__free_unpacked + (RespGetWifiStatus *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_get_wifi_status__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_get_thread_status__init + (CmdGetThreadStatus *message) +{ + static const CmdGetThreadStatus init_value = CMD_GET_THREAD_STATUS__INIT; + *message = init_value; +} +size_t cmd_get_thread_status__get_packed_size + (const CmdGetThreadStatus *message) +{ + assert(message->base.descriptor == &cmd_get_thread_status__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_get_thread_status__pack + (const CmdGetThreadStatus *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_get_thread_status__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_get_thread_status__pack_to_buffer + (const CmdGetThreadStatus *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_get_thread_status__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdGetThreadStatus * + cmd_get_thread_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdGetThreadStatus *) + protobuf_c_message_unpack (&cmd_get_thread_status__descriptor, + allocator, len, data); +} +void cmd_get_thread_status__free_unpacked + (CmdGetThreadStatus *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_get_thread_status__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_get_thread_status__init + (RespGetThreadStatus *message) +{ + static const RespGetThreadStatus init_value = RESP_GET_THREAD_STATUS__INIT; + *message = init_value; +} +size_t resp_get_thread_status__get_packed_size + (const RespGetThreadStatus *message) +{ + assert(message->base.descriptor == &resp_get_thread_status__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_get_thread_status__pack + (const RespGetThreadStatus *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_get_thread_status__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_get_thread_status__pack_to_buffer + (const RespGetThreadStatus *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_get_thread_status__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespGetThreadStatus * + resp_get_thread_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespGetThreadStatus *) + protobuf_c_message_unpack (&resp_get_thread_status__descriptor, + allocator, len, data); +} +void resp_get_thread_status__free_unpacked + (RespGetThreadStatus *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_get_thread_status__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_set_wifi_config__init + (CmdSetWifiConfig *message) +{ + static const CmdSetWifiConfig init_value = CMD_SET_WIFI_CONFIG__INIT; + *message = init_value; +} +size_t cmd_set_wifi_config__get_packed_size + (const CmdSetWifiConfig *message) +{ + assert(message->base.descriptor == &cmd_set_wifi_config__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_set_wifi_config__pack + (const CmdSetWifiConfig *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_set_wifi_config__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_set_wifi_config__pack_to_buffer + (const CmdSetWifiConfig *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_set_wifi_config__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdSetWifiConfig * + cmd_set_wifi_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdSetWifiConfig *) + protobuf_c_message_unpack (&cmd_set_wifi_config__descriptor, + allocator, len, data); +} +void cmd_set_wifi_config__free_unpacked + (CmdSetWifiConfig *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_set_wifi_config__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_set_thread_config__init + (CmdSetThreadConfig *message) +{ + static const CmdSetThreadConfig init_value = CMD_SET_THREAD_CONFIG__INIT; + *message = init_value; +} +size_t cmd_set_thread_config__get_packed_size + (const CmdSetThreadConfig *message) +{ + assert(message->base.descriptor == &cmd_set_thread_config__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_set_thread_config__pack + (const CmdSetThreadConfig *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_set_thread_config__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_set_thread_config__pack_to_buffer + (const CmdSetThreadConfig *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_set_thread_config__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdSetThreadConfig * + cmd_set_thread_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdSetThreadConfig *) + protobuf_c_message_unpack (&cmd_set_thread_config__descriptor, + allocator, len, data); +} +void cmd_set_thread_config__free_unpacked + (CmdSetThreadConfig *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_set_thread_config__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_set_wifi_config__init + (RespSetWifiConfig *message) +{ + static const RespSetWifiConfig init_value = RESP_SET_WIFI_CONFIG__INIT; + *message = init_value; +} +size_t resp_set_wifi_config__get_packed_size + (const RespSetWifiConfig *message) +{ + assert(message->base.descriptor == &resp_set_wifi_config__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_set_wifi_config__pack + (const RespSetWifiConfig *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_set_wifi_config__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_set_wifi_config__pack_to_buffer + (const RespSetWifiConfig *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_set_wifi_config__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespSetWifiConfig * + resp_set_wifi_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespSetWifiConfig *) + protobuf_c_message_unpack (&resp_set_wifi_config__descriptor, + allocator, len, data); +} +void resp_set_wifi_config__free_unpacked + (RespSetWifiConfig *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_set_wifi_config__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_set_thread_config__init + (RespSetThreadConfig *message) +{ + static const RespSetThreadConfig init_value = RESP_SET_THREAD_CONFIG__INIT; + *message = init_value; +} +size_t resp_set_thread_config__get_packed_size + (const RespSetThreadConfig *message) +{ + assert(message->base.descriptor == &resp_set_thread_config__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_set_thread_config__pack + (const RespSetThreadConfig *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_set_thread_config__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_set_thread_config__pack_to_buffer + (const RespSetThreadConfig *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_set_thread_config__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespSetThreadConfig * + resp_set_thread_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespSetThreadConfig *) + protobuf_c_message_unpack (&resp_set_thread_config__descriptor, + allocator, len, data); +} +void resp_set_thread_config__free_unpacked + (RespSetThreadConfig *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_set_thread_config__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_apply_wifi_config__init + (CmdApplyWifiConfig *message) +{ + static const CmdApplyWifiConfig init_value = CMD_APPLY_WIFI_CONFIG__INIT; + *message = init_value; +} +size_t cmd_apply_wifi_config__get_packed_size + (const CmdApplyWifiConfig *message) +{ + assert(message->base.descriptor == &cmd_apply_wifi_config__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_apply_wifi_config__pack + (const CmdApplyWifiConfig *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_apply_wifi_config__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_apply_wifi_config__pack_to_buffer + (const CmdApplyWifiConfig *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_apply_wifi_config__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdApplyWifiConfig * + cmd_apply_wifi_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdApplyWifiConfig *) + protobuf_c_message_unpack (&cmd_apply_wifi_config__descriptor, + allocator, len, data); +} +void cmd_apply_wifi_config__free_unpacked + (CmdApplyWifiConfig *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_apply_wifi_config__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_apply_thread_config__init + (CmdApplyThreadConfig *message) +{ + static const CmdApplyThreadConfig init_value = CMD_APPLY_THREAD_CONFIG__INIT; + *message = init_value; +} +size_t cmd_apply_thread_config__get_packed_size + (const CmdApplyThreadConfig *message) +{ + assert(message->base.descriptor == &cmd_apply_thread_config__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_apply_thread_config__pack + (const CmdApplyThreadConfig *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_apply_thread_config__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_apply_thread_config__pack_to_buffer + (const CmdApplyThreadConfig *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_apply_thread_config__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdApplyThreadConfig * + cmd_apply_thread_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdApplyThreadConfig *) + protobuf_c_message_unpack (&cmd_apply_thread_config__descriptor, + allocator, len, data); +} +void cmd_apply_thread_config__free_unpacked + (CmdApplyThreadConfig *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_apply_thread_config__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_apply_wifi_config__init + (RespApplyWifiConfig *message) +{ + static const RespApplyWifiConfig init_value = RESP_APPLY_WIFI_CONFIG__INIT; + *message = init_value; +} +size_t resp_apply_wifi_config__get_packed_size + (const RespApplyWifiConfig *message) +{ + assert(message->base.descriptor == &resp_apply_wifi_config__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_apply_wifi_config__pack + (const RespApplyWifiConfig *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_apply_wifi_config__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_apply_wifi_config__pack_to_buffer + (const RespApplyWifiConfig *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_apply_wifi_config__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespApplyWifiConfig * + resp_apply_wifi_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespApplyWifiConfig *) + protobuf_c_message_unpack (&resp_apply_wifi_config__descriptor, + allocator, len, data); +} +void resp_apply_wifi_config__free_unpacked + (RespApplyWifiConfig *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_apply_wifi_config__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_apply_thread_config__init + (RespApplyThreadConfig *message) +{ + static const RespApplyThreadConfig init_value = RESP_APPLY_THREAD_CONFIG__INIT; + *message = init_value; +} +size_t resp_apply_thread_config__get_packed_size + (const RespApplyThreadConfig *message) +{ + assert(message->base.descriptor == &resp_apply_thread_config__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_apply_thread_config__pack + (const RespApplyThreadConfig *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_apply_thread_config__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_apply_thread_config__pack_to_buffer + (const RespApplyThreadConfig *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_apply_thread_config__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespApplyThreadConfig * + resp_apply_thread_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespApplyThreadConfig *) + protobuf_c_message_unpack (&resp_apply_thread_config__descriptor, + allocator, len, data); +} +void resp_apply_thread_config__free_unpacked + (RespApplyThreadConfig *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_apply_thread_config__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void network_config_payload__init + (NetworkConfigPayload *message) +{ + static const NetworkConfigPayload init_value = NETWORK_CONFIG_PAYLOAD__INIT; + *message = init_value; +} +size_t network_config_payload__get_packed_size + (const NetworkConfigPayload *message) +{ + assert(message->base.descriptor == &network_config_payload__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t network_config_payload__pack + (const NetworkConfigPayload *message, + uint8_t *out) +{ + assert(message->base.descriptor == &network_config_payload__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t network_config_payload__pack_to_buffer + (const NetworkConfigPayload *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &network_config_payload__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +NetworkConfigPayload * + network_config_payload__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (NetworkConfigPayload *) + protobuf_c_message_unpack (&network_config_payload__descriptor, + allocator, len, data); +} +void network_config_payload__free_unpacked + (NetworkConfigPayload *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &network_config_payload__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +#define cmd_get_wifi_status__field_descriptors NULL +#define cmd_get_wifi_status__field_indices_by_name NULL +#define cmd_get_wifi_status__number_ranges NULL +const ProtobufCMessageDescriptor cmd_get_wifi_status__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdGetWifiStatus", + "CmdGetWifiStatus", + "CmdGetWifiStatus", + "", + sizeof(CmdGetWifiStatus), + 0, + cmd_get_wifi_status__field_descriptors, + cmd_get_wifi_status__field_indices_by_name, + 0, cmd_get_wifi_status__number_ranges, + (ProtobufCMessageInit) cmd_get_wifi_status__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor resp_get_wifi_status__field_descriptors[5] = +{ + { + "status", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(RespGetWifiStatus, status), + &status__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "wifi_sta_state", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(RespGetWifiStatus, wifi_sta_state), + &wifi_station_state__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "wifi_fail_reason", + 10, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + offsetof(RespGetWifiStatus, state_case), + offsetof(RespGetWifiStatus, wifi_fail_reason), + &wifi_connect_failed_reason__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "wifi_connected", + 11, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(RespGetWifiStatus, state_case), + offsetof(RespGetWifiStatus, wifi_connected), + &wifi_connected_state__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "attempt_failed", + 12, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(RespGetWifiStatus, state_case), + offsetof(RespGetWifiStatus, attempt_failed), + &wifi_attempt_failed__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned resp_get_wifi_status__field_indices_by_name[] = { + 4, /* field[4] = attempt_failed */ + 0, /* field[0] = status */ + 3, /* field[3] = wifi_connected */ + 2, /* field[2] = wifi_fail_reason */ + 1, /* field[1] = wifi_sta_state */ +}; +static const ProtobufCIntRange resp_get_wifi_status__number_ranges[2 + 1] = +{ + { 1, 0 }, + { 10, 2 }, + { 0, 5 } +}; +const ProtobufCMessageDescriptor resp_get_wifi_status__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespGetWifiStatus", + "RespGetWifiStatus", + "RespGetWifiStatus", + "", + sizeof(RespGetWifiStatus), + 5, + resp_get_wifi_status__field_descriptors, + resp_get_wifi_status__field_indices_by_name, + 2, resp_get_wifi_status__number_ranges, + (ProtobufCMessageInit) resp_get_wifi_status__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define cmd_get_thread_status__field_descriptors NULL +#define cmd_get_thread_status__field_indices_by_name NULL +#define cmd_get_thread_status__number_ranges NULL +const ProtobufCMessageDescriptor cmd_get_thread_status__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdGetThreadStatus", + "CmdGetThreadStatus", + "CmdGetThreadStatus", + "", + sizeof(CmdGetThreadStatus), + 0, + cmd_get_thread_status__field_descriptors, + cmd_get_thread_status__field_indices_by_name, + 0, cmd_get_thread_status__number_ranges, + (ProtobufCMessageInit) cmd_get_thread_status__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor resp_get_thread_status__field_descriptors[4] = +{ + { + "status", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(RespGetThreadStatus, status), + &status__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "thread_state", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(RespGetThreadStatus, thread_state), + &thread_network_state__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "thread_fail_reason", + 10, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + offsetof(RespGetThreadStatus, state_case), + offsetof(RespGetThreadStatus, thread_fail_reason), + &thread_attach_failed_reason__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "thread_attached", + 11, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(RespGetThreadStatus, state_case), + offsetof(RespGetThreadStatus, thread_attached), + &thread_attach_state__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned resp_get_thread_status__field_indices_by_name[] = { + 0, /* field[0] = status */ + 3, /* field[3] = thread_attached */ + 2, /* field[2] = thread_fail_reason */ + 1, /* field[1] = thread_state */ +}; +static const ProtobufCIntRange resp_get_thread_status__number_ranges[2 + 1] = +{ + { 1, 0 }, + { 10, 2 }, + { 0, 4 } +}; +const ProtobufCMessageDescriptor resp_get_thread_status__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespGetThreadStatus", + "RespGetThreadStatus", + "RespGetThreadStatus", + "", + sizeof(RespGetThreadStatus), + 4, + resp_get_thread_status__field_descriptors, + resp_get_thread_status__field_indices_by_name, + 2, resp_get_thread_status__number_ranges, + (ProtobufCMessageInit) resp_get_thread_status__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor cmd_set_wifi_config__field_descriptors[4] = +{ + { + "ssid", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(CmdSetWifiConfig, ssid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "passphrase", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(CmdSetWifiConfig, passphrase), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "bssid", + 3, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(CmdSetWifiConfig, bssid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "channel", + 4, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_INT32, + 0, /* quantifier_offset */ + offsetof(CmdSetWifiConfig, channel), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned cmd_set_wifi_config__field_indices_by_name[] = { + 2, /* field[2] = bssid */ + 3, /* field[3] = channel */ + 1, /* field[1] = passphrase */ + 0, /* field[0] = ssid */ +}; +static const ProtobufCIntRange cmd_set_wifi_config__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 4 } +}; +const ProtobufCMessageDescriptor cmd_set_wifi_config__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdSetWifiConfig", + "CmdSetWifiConfig", + "CmdSetWifiConfig", + "", + sizeof(CmdSetWifiConfig), + 4, + cmd_set_wifi_config__field_descriptors, + cmd_set_wifi_config__field_indices_by_name, + 1, cmd_set_wifi_config__number_ranges, + (ProtobufCMessageInit) cmd_set_wifi_config__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor cmd_set_thread_config__field_descriptors[1] = +{ + { + "dataset", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(CmdSetThreadConfig, dataset), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned cmd_set_thread_config__field_indices_by_name[] = { + 0, /* field[0] = dataset */ +}; +static const ProtobufCIntRange cmd_set_thread_config__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor cmd_set_thread_config__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdSetThreadConfig", + "CmdSetThreadConfig", + "CmdSetThreadConfig", + "", + sizeof(CmdSetThreadConfig), + 1, + cmd_set_thread_config__field_descriptors, + cmd_set_thread_config__field_indices_by_name, + 1, cmd_set_thread_config__number_ranges, + (ProtobufCMessageInit) cmd_set_thread_config__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor resp_set_wifi_config__field_descriptors[1] = +{ + { + "status", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(RespSetWifiConfig, status), + &status__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned resp_set_wifi_config__field_indices_by_name[] = { + 0, /* field[0] = status */ +}; +static const ProtobufCIntRange resp_set_wifi_config__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor resp_set_wifi_config__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespSetWifiConfig", + "RespSetWifiConfig", + "RespSetWifiConfig", + "", + sizeof(RespSetWifiConfig), + 1, + resp_set_wifi_config__field_descriptors, + resp_set_wifi_config__field_indices_by_name, + 1, resp_set_wifi_config__number_ranges, + (ProtobufCMessageInit) resp_set_wifi_config__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor resp_set_thread_config__field_descriptors[1] = +{ + { + "status", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(RespSetThreadConfig, status), + &status__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned resp_set_thread_config__field_indices_by_name[] = { + 0, /* field[0] = status */ +}; +static const ProtobufCIntRange resp_set_thread_config__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor resp_set_thread_config__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespSetThreadConfig", + "RespSetThreadConfig", + "RespSetThreadConfig", + "", + sizeof(RespSetThreadConfig), + 1, + resp_set_thread_config__field_descriptors, + resp_set_thread_config__field_indices_by_name, + 1, resp_set_thread_config__number_ranges, + (ProtobufCMessageInit) resp_set_thread_config__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define cmd_apply_wifi_config__field_descriptors NULL +#define cmd_apply_wifi_config__field_indices_by_name NULL +#define cmd_apply_wifi_config__number_ranges NULL +const ProtobufCMessageDescriptor cmd_apply_wifi_config__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdApplyWifiConfig", + "CmdApplyWifiConfig", + "CmdApplyWifiConfig", + "", + sizeof(CmdApplyWifiConfig), + 0, + cmd_apply_wifi_config__field_descriptors, + cmd_apply_wifi_config__field_indices_by_name, + 0, cmd_apply_wifi_config__number_ranges, + (ProtobufCMessageInit) cmd_apply_wifi_config__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define cmd_apply_thread_config__field_descriptors NULL +#define cmd_apply_thread_config__field_indices_by_name NULL +#define cmd_apply_thread_config__number_ranges NULL +const ProtobufCMessageDescriptor cmd_apply_thread_config__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdApplyThreadConfig", + "CmdApplyThreadConfig", + "CmdApplyThreadConfig", + "", + sizeof(CmdApplyThreadConfig), + 0, + cmd_apply_thread_config__field_descriptors, + cmd_apply_thread_config__field_indices_by_name, + 0, cmd_apply_thread_config__number_ranges, + (ProtobufCMessageInit) cmd_apply_thread_config__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor resp_apply_wifi_config__field_descriptors[1] = +{ + { + "status", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(RespApplyWifiConfig, status), + &status__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned resp_apply_wifi_config__field_indices_by_name[] = { + 0, /* field[0] = status */ +}; +static const ProtobufCIntRange resp_apply_wifi_config__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor resp_apply_wifi_config__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespApplyWifiConfig", + "RespApplyWifiConfig", + "RespApplyWifiConfig", + "", + sizeof(RespApplyWifiConfig), + 1, + resp_apply_wifi_config__field_descriptors, + resp_apply_wifi_config__field_indices_by_name, + 1, resp_apply_wifi_config__number_ranges, + (ProtobufCMessageInit) resp_apply_wifi_config__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor resp_apply_thread_config__field_descriptors[1] = +{ + { + "status", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(RespApplyThreadConfig, status), + &status__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned resp_apply_thread_config__field_indices_by_name[] = { + 0, /* field[0] = status */ +}; +static const ProtobufCIntRange resp_apply_thread_config__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor resp_apply_thread_config__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespApplyThreadConfig", + "RespApplyThreadConfig", + "RespApplyThreadConfig", + "", + sizeof(RespApplyThreadConfig), + 1, + resp_apply_thread_config__field_descriptors, + resp_apply_thread_config__field_indices_by_name, + 1, resp_apply_thread_config__number_ranges, + (ProtobufCMessageInit) resp_apply_thread_config__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor network_config_payload__field_descriptors[13] = +{ + { + "msg", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(NetworkConfigPayload, msg), + &network_config_msg_type__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_get_wifi_status", + 10, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkConfigPayload, payload_case), + offsetof(NetworkConfigPayload, cmd_get_wifi_status), + &cmd_get_wifi_status__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_get_wifi_status", + 11, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkConfigPayload, payload_case), + offsetof(NetworkConfigPayload, resp_get_wifi_status), + &resp_get_wifi_status__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_set_wifi_config", + 12, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkConfigPayload, payload_case), + offsetof(NetworkConfigPayload, cmd_set_wifi_config), + &cmd_set_wifi_config__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_set_wifi_config", + 13, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkConfigPayload, payload_case), + offsetof(NetworkConfigPayload, resp_set_wifi_config), + &resp_set_wifi_config__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_apply_wifi_config", + 14, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkConfigPayload, payload_case), + offsetof(NetworkConfigPayload, cmd_apply_wifi_config), + &cmd_apply_wifi_config__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_apply_wifi_config", + 15, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkConfigPayload, payload_case), + offsetof(NetworkConfigPayload, resp_apply_wifi_config), + &resp_apply_wifi_config__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_get_thread_status", + 16, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkConfigPayload, payload_case), + offsetof(NetworkConfigPayload, cmd_get_thread_status), + &cmd_get_thread_status__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_get_thread_status", + 17, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkConfigPayload, payload_case), + offsetof(NetworkConfigPayload, resp_get_thread_status), + &resp_get_thread_status__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_set_thread_config", + 18, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkConfigPayload, payload_case), + offsetof(NetworkConfigPayload, cmd_set_thread_config), + &cmd_set_thread_config__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_set_thread_config", + 19, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkConfigPayload, payload_case), + offsetof(NetworkConfigPayload, resp_set_thread_config), + &resp_set_thread_config__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_apply_thread_config", + 20, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkConfigPayload, payload_case), + offsetof(NetworkConfigPayload, cmd_apply_thread_config), + &cmd_apply_thread_config__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_apply_thread_config", + 21, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkConfigPayload, payload_case), + offsetof(NetworkConfigPayload, resp_apply_thread_config), + &resp_apply_thread_config__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned network_config_payload__field_indices_by_name[] = { + 11, /* field[11] = cmd_apply_thread_config */ + 5, /* field[5] = cmd_apply_wifi_config */ + 7, /* field[7] = cmd_get_thread_status */ + 1, /* field[1] = cmd_get_wifi_status */ + 9, /* field[9] = cmd_set_thread_config */ + 3, /* field[3] = cmd_set_wifi_config */ + 0, /* field[0] = msg */ + 12, /* field[12] = resp_apply_thread_config */ + 6, /* field[6] = resp_apply_wifi_config */ + 8, /* field[8] = resp_get_thread_status */ + 2, /* field[2] = resp_get_wifi_status */ + 10, /* field[10] = resp_set_thread_config */ + 4, /* field[4] = resp_set_wifi_config */ +}; +static const ProtobufCIntRange network_config_payload__number_ranges[2 + 1] = +{ + { 1, 0 }, + { 10, 1 }, + { 0, 13 } +}; +const ProtobufCMessageDescriptor network_config_payload__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "NetworkConfigPayload", + "NetworkConfigPayload", + "NetworkConfigPayload", + "", + sizeof(NetworkConfigPayload), + 13, + network_config_payload__field_descriptors, + network_config_payload__field_indices_by_name, + 2, network_config_payload__number_ranges, + (ProtobufCMessageInit) network_config_payload__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCEnumValue network_config_msg_type__enum_values_by_number[12] = +{ + { "TypeCmdGetWifiStatus", "NETWORK_CONFIG_MSG_TYPE__TypeCmdGetWifiStatus", 0 }, + { "TypeRespGetWifiStatus", "NETWORK_CONFIG_MSG_TYPE__TypeRespGetWifiStatus", 1 }, + { "TypeCmdSetWifiConfig", "NETWORK_CONFIG_MSG_TYPE__TypeCmdSetWifiConfig", 2 }, + { "TypeRespSetWifiConfig", "NETWORK_CONFIG_MSG_TYPE__TypeRespSetWifiConfig", 3 }, + { "TypeCmdApplyWifiConfig", "NETWORK_CONFIG_MSG_TYPE__TypeCmdApplyWifiConfig", 4 }, + { "TypeRespApplyWifiConfig", "NETWORK_CONFIG_MSG_TYPE__TypeRespApplyWifiConfig", 5 }, + { "TypeCmdGetThreadStatus", "NETWORK_CONFIG_MSG_TYPE__TypeCmdGetThreadStatus", 6 }, + { "TypeRespGetThreadStatus", "NETWORK_CONFIG_MSG_TYPE__TypeRespGetThreadStatus", 7 }, + { "TypeCmdSetThreadConfig", "NETWORK_CONFIG_MSG_TYPE__TypeCmdSetThreadConfig", 8 }, + { "TypeRespSetThreadConfig", "NETWORK_CONFIG_MSG_TYPE__TypeRespSetThreadConfig", 9 }, + { "TypeCmdApplyThreadConfig", "NETWORK_CONFIG_MSG_TYPE__TypeCmdApplyThreadConfig", 10 }, + { "TypeRespApplyThreadConfig", "NETWORK_CONFIG_MSG_TYPE__TypeRespApplyThreadConfig", 11 }, +}; +static const ProtobufCIntRange network_config_msg_type__value_ranges[] = { +{0, 0},{0, 12} +}; +static const ProtobufCEnumValueIndex network_config_msg_type__enum_values_by_name[12] = +{ + { "TypeCmdApplyThreadConfig", 10 }, + { "TypeCmdApplyWifiConfig", 4 }, + { "TypeCmdGetThreadStatus", 6 }, + { "TypeCmdGetWifiStatus", 0 }, + { "TypeCmdSetThreadConfig", 8 }, + { "TypeCmdSetWifiConfig", 2 }, + { "TypeRespApplyThreadConfig", 11 }, + { "TypeRespApplyWifiConfig", 5 }, + { "TypeRespGetThreadStatus", 7 }, + { "TypeRespGetWifiStatus", 1 }, + { "TypeRespSetThreadConfig", 9 }, + { "TypeRespSetWifiConfig", 3 }, +}; +const ProtobufCEnumDescriptor network_config_msg_type__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "NetworkConfigMsgType", + "NetworkConfigMsgType", + "NetworkConfigMsgType", + "", + 12, + network_config_msg_type__enum_values_by_number, + 12, + network_config_msg_type__enum_values_by_name, + 1, + network_config_msg_type__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; diff --git a/components/network_provisioning/proto-c/network_config.pb-c.h b/components/network_provisioning/proto-c/network_config.pb-c.h new file mode 100644 index 0000000..28fce08 --- /dev/null +++ b/components/network_provisioning/proto-c/network_config.pb-c.h @@ -0,0 +1,556 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: network_config.proto */ + +#ifndef PROTOBUF_C_network_5fconfig_2eproto__INCLUDED +#define PROTOBUF_C_network_5fconfig_2eproto__INCLUDED + +#include + +PROTOBUF_C__BEGIN_DECLS + +#if PROTOBUF_C_VERSION_NUMBER < 1003000 +# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. +#elif 1004000 < PROTOBUF_C_MIN_COMPILER_VERSION +# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. +#endif + +#include "constants.pb-c.h" +#include "network_constants.pb-c.h" + +typedef struct CmdGetWifiStatus CmdGetWifiStatus; +typedef struct RespGetWifiStatus RespGetWifiStatus; +typedef struct CmdGetThreadStatus CmdGetThreadStatus; +typedef struct RespGetThreadStatus RespGetThreadStatus; +typedef struct CmdSetWifiConfig CmdSetWifiConfig; +typedef struct CmdSetThreadConfig CmdSetThreadConfig; +typedef struct RespSetWifiConfig RespSetWifiConfig; +typedef struct RespSetThreadConfig RespSetThreadConfig; +typedef struct CmdApplyWifiConfig CmdApplyWifiConfig; +typedef struct CmdApplyThreadConfig CmdApplyThreadConfig; +typedef struct RespApplyWifiConfig RespApplyWifiConfig; +typedef struct RespApplyThreadConfig RespApplyThreadConfig; +typedef struct NetworkConfigPayload NetworkConfigPayload; + + +/* --- enums --- */ + +typedef enum _NetworkConfigMsgType { + NETWORK_CONFIG_MSG_TYPE__TypeCmdGetWifiStatus = 0, + NETWORK_CONFIG_MSG_TYPE__TypeRespGetWifiStatus = 1, + NETWORK_CONFIG_MSG_TYPE__TypeCmdSetWifiConfig = 2, + NETWORK_CONFIG_MSG_TYPE__TypeRespSetWifiConfig = 3, + NETWORK_CONFIG_MSG_TYPE__TypeCmdApplyWifiConfig = 4, + NETWORK_CONFIG_MSG_TYPE__TypeRespApplyWifiConfig = 5, + NETWORK_CONFIG_MSG_TYPE__TypeCmdGetThreadStatus = 6, + NETWORK_CONFIG_MSG_TYPE__TypeRespGetThreadStatus = 7, + NETWORK_CONFIG_MSG_TYPE__TypeCmdSetThreadConfig = 8, + NETWORK_CONFIG_MSG_TYPE__TypeRespSetThreadConfig = 9, + NETWORK_CONFIG_MSG_TYPE__TypeCmdApplyThreadConfig = 10, + NETWORK_CONFIG_MSG_TYPE__TypeRespApplyThreadConfig = 11 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(NETWORK_CONFIG_MSG_TYPE) +} NetworkConfigMsgType; + +/* --- messages --- */ + +struct CmdGetWifiStatus +{ + ProtobufCMessage base; +}; +#define CMD_GET_WIFI_STATUS__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_get_wifi_status__descriptor) \ + } + + +typedef enum { + RESP_GET_WIFI_STATUS__STATE__NOT_SET = 0, + RESP_GET_WIFI_STATUS__STATE_WIFI_FAIL_REASON = 10, + RESP_GET_WIFI_STATUS__STATE_WIFI_CONNECTED = 11, + RESP_GET_WIFI_STATUS__STATE_ATTEMPT_FAILED = 12 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RESP_GET_WIFI_STATUS__STATE__CASE) +} RespGetWifiStatus__StateCase; + +struct RespGetWifiStatus +{ + ProtobufCMessage base; + Status status; + WifiStationState wifi_sta_state; + RespGetWifiStatus__StateCase state_case; + union { + WifiConnectFailedReason wifi_fail_reason; + WifiConnectedState *wifi_connected; + WifiAttemptFailed *attempt_failed; + }; +}; +#define RESP_GET_WIFI_STATUS__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_get_wifi_status__descriptor) \ + , STATUS__Success, WIFI_STATION_STATE__Connected, RESP_GET_WIFI_STATUS__STATE__NOT_SET, {0} } + + +struct CmdGetThreadStatus +{ + ProtobufCMessage base; +}; +#define CMD_GET_THREAD_STATUS__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_get_thread_status__descriptor) \ + } + + +typedef enum { + RESP_GET_THREAD_STATUS__STATE__NOT_SET = 0, + RESP_GET_THREAD_STATUS__STATE_THREAD_FAIL_REASON = 10, + RESP_GET_THREAD_STATUS__STATE_THREAD_ATTACHED = 11 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RESP_GET_THREAD_STATUS__STATE__CASE) +} RespGetThreadStatus__StateCase; + +struct RespGetThreadStatus +{ + ProtobufCMessage base; + Status status; + ThreadNetworkState thread_state; + RespGetThreadStatus__StateCase state_case; + union { + ThreadAttachFailedReason thread_fail_reason; + ThreadAttachState *thread_attached; + }; +}; +#define RESP_GET_THREAD_STATUS__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_get_thread_status__descriptor) \ + , STATUS__Success, THREAD_NETWORK_STATE__Attached, RESP_GET_THREAD_STATUS__STATE__NOT_SET, {0} } + + +struct CmdSetWifiConfig +{ + ProtobufCMessage base; + ProtobufCBinaryData ssid; + ProtobufCBinaryData passphrase; + ProtobufCBinaryData bssid; + int32_t channel; +}; +#define CMD_SET_WIFI_CONFIG__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_set_wifi_config__descriptor) \ + , {0,NULL}, {0,NULL}, {0,NULL}, 0 } + + +struct CmdSetThreadConfig +{ + ProtobufCMessage base; + ProtobufCBinaryData dataset; +}; +#define CMD_SET_THREAD_CONFIG__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_set_thread_config__descriptor) \ + , {0,NULL} } + + +struct RespSetWifiConfig +{ + ProtobufCMessage base; + Status status; +}; +#define RESP_SET_WIFI_CONFIG__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_set_wifi_config__descriptor) \ + , STATUS__Success } + + +struct RespSetThreadConfig +{ + ProtobufCMessage base; + Status status; +}; +#define RESP_SET_THREAD_CONFIG__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_set_thread_config__descriptor) \ + , STATUS__Success } + + +struct CmdApplyWifiConfig +{ + ProtobufCMessage base; +}; +#define CMD_APPLY_WIFI_CONFIG__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_apply_wifi_config__descriptor) \ + } + + +struct CmdApplyThreadConfig +{ + ProtobufCMessage base; +}; +#define CMD_APPLY_THREAD_CONFIG__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_apply_thread_config__descriptor) \ + } + + +struct RespApplyWifiConfig +{ + ProtobufCMessage base; + Status status; +}; +#define RESP_APPLY_WIFI_CONFIG__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_apply_wifi_config__descriptor) \ + , STATUS__Success } + + +struct RespApplyThreadConfig +{ + ProtobufCMessage base; + Status status; +}; +#define RESP_APPLY_THREAD_CONFIG__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_apply_thread_config__descriptor) \ + , STATUS__Success } + + +typedef enum { + NETWORK_CONFIG_PAYLOAD__PAYLOAD__NOT_SET = 0, + NETWORK_CONFIG_PAYLOAD__PAYLOAD_CMD_GET_WIFI_STATUS = 10, + NETWORK_CONFIG_PAYLOAD__PAYLOAD_RESP_GET_WIFI_STATUS = 11, + NETWORK_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_WIFI_CONFIG = 12, + NETWORK_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_WIFI_CONFIG = 13, + NETWORK_CONFIG_PAYLOAD__PAYLOAD_CMD_APPLY_WIFI_CONFIG = 14, + NETWORK_CONFIG_PAYLOAD__PAYLOAD_RESP_APPLY_WIFI_CONFIG = 15, + NETWORK_CONFIG_PAYLOAD__PAYLOAD_CMD_GET_THREAD_STATUS = 16, + NETWORK_CONFIG_PAYLOAD__PAYLOAD_RESP_GET_THREAD_STATUS = 17, + NETWORK_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_THREAD_CONFIG = 18, + NETWORK_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_THREAD_CONFIG = 19, + NETWORK_CONFIG_PAYLOAD__PAYLOAD_CMD_APPLY_THREAD_CONFIG = 20, + NETWORK_CONFIG_PAYLOAD__PAYLOAD_RESP_APPLY_THREAD_CONFIG = 21 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(NETWORK_CONFIG_PAYLOAD__PAYLOAD__CASE) +} NetworkConfigPayload__PayloadCase; + +struct NetworkConfigPayload +{ + ProtobufCMessage base; + NetworkConfigMsgType msg; + NetworkConfigPayload__PayloadCase payload_case; + union { + CmdGetWifiStatus *cmd_get_wifi_status; + RespGetWifiStatus *resp_get_wifi_status; + CmdSetWifiConfig *cmd_set_wifi_config; + RespSetWifiConfig *resp_set_wifi_config; + CmdApplyWifiConfig *cmd_apply_wifi_config; + RespApplyWifiConfig *resp_apply_wifi_config; + CmdGetThreadStatus *cmd_get_thread_status; + RespGetThreadStatus *resp_get_thread_status; + CmdSetThreadConfig *cmd_set_thread_config; + RespSetThreadConfig *resp_set_thread_config; + CmdApplyThreadConfig *cmd_apply_thread_config; + RespApplyThreadConfig *resp_apply_thread_config; + }; +}; +#define NETWORK_CONFIG_PAYLOAD__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&network_config_payload__descriptor) \ + , NETWORK_CONFIG_MSG_TYPE__TypeCmdGetWifiStatus, NETWORK_CONFIG_PAYLOAD__PAYLOAD__NOT_SET, {0} } + + +/* CmdGetWifiStatus methods */ +void cmd_get_wifi_status__init + (CmdGetWifiStatus *message); +size_t cmd_get_wifi_status__get_packed_size + (const CmdGetWifiStatus *message); +size_t cmd_get_wifi_status__pack + (const CmdGetWifiStatus *message, + uint8_t *out); +size_t cmd_get_wifi_status__pack_to_buffer + (const CmdGetWifiStatus *message, + ProtobufCBuffer *buffer); +CmdGetWifiStatus * + cmd_get_wifi_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_get_wifi_status__free_unpacked + (CmdGetWifiStatus *message, + ProtobufCAllocator *allocator); +/* RespGetWifiStatus methods */ +void resp_get_wifi_status__init + (RespGetWifiStatus *message); +size_t resp_get_wifi_status__get_packed_size + (const RespGetWifiStatus *message); +size_t resp_get_wifi_status__pack + (const RespGetWifiStatus *message, + uint8_t *out); +size_t resp_get_wifi_status__pack_to_buffer + (const RespGetWifiStatus *message, + ProtobufCBuffer *buffer); +RespGetWifiStatus * + resp_get_wifi_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_get_wifi_status__free_unpacked + (RespGetWifiStatus *message, + ProtobufCAllocator *allocator); +/* CmdGetThreadStatus methods */ +void cmd_get_thread_status__init + (CmdGetThreadStatus *message); +size_t cmd_get_thread_status__get_packed_size + (const CmdGetThreadStatus *message); +size_t cmd_get_thread_status__pack + (const CmdGetThreadStatus *message, + uint8_t *out); +size_t cmd_get_thread_status__pack_to_buffer + (const CmdGetThreadStatus *message, + ProtobufCBuffer *buffer); +CmdGetThreadStatus * + cmd_get_thread_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_get_thread_status__free_unpacked + (CmdGetThreadStatus *message, + ProtobufCAllocator *allocator); +/* RespGetThreadStatus methods */ +void resp_get_thread_status__init + (RespGetThreadStatus *message); +size_t resp_get_thread_status__get_packed_size + (const RespGetThreadStatus *message); +size_t resp_get_thread_status__pack + (const RespGetThreadStatus *message, + uint8_t *out); +size_t resp_get_thread_status__pack_to_buffer + (const RespGetThreadStatus *message, + ProtobufCBuffer *buffer); +RespGetThreadStatus * + resp_get_thread_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_get_thread_status__free_unpacked + (RespGetThreadStatus *message, + ProtobufCAllocator *allocator); +/* CmdSetWifiConfig methods */ +void cmd_set_wifi_config__init + (CmdSetWifiConfig *message); +size_t cmd_set_wifi_config__get_packed_size + (const CmdSetWifiConfig *message); +size_t cmd_set_wifi_config__pack + (const CmdSetWifiConfig *message, + uint8_t *out); +size_t cmd_set_wifi_config__pack_to_buffer + (const CmdSetWifiConfig *message, + ProtobufCBuffer *buffer); +CmdSetWifiConfig * + cmd_set_wifi_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_set_wifi_config__free_unpacked + (CmdSetWifiConfig *message, + ProtobufCAllocator *allocator); +/* CmdSetThreadConfig methods */ +void cmd_set_thread_config__init + (CmdSetThreadConfig *message); +size_t cmd_set_thread_config__get_packed_size + (const CmdSetThreadConfig *message); +size_t cmd_set_thread_config__pack + (const CmdSetThreadConfig *message, + uint8_t *out); +size_t cmd_set_thread_config__pack_to_buffer + (const CmdSetThreadConfig *message, + ProtobufCBuffer *buffer); +CmdSetThreadConfig * + cmd_set_thread_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_set_thread_config__free_unpacked + (CmdSetThreadConfig *message, + ProtobufCAllocator *allocator); +/* RespSetWifiConfig methods */ +void resp_set_wifi_config__init + (RespSetWifiConfig *message); +size_t resp_set_wifi_config__get_packed_size + (const RespSetWifiConfig *message); +size_t resp_set_wifi_config__pack + (const RespSetWifiConfig *message, + uint8_t *out); +size_t resp_set_wifi_config__pack_to_buffer + (const RespSetWifiConfig *message, + ProtobufCBuffer *buffer); +RespSetWifiConfig * + resp_set_wifi_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_set_wifi_config__free_unpacked + (RespSetWifiConfig *message, + ProtobufCAllocator *allocator); +/* RespSetThreadConfig methods */ +void resp_set_thread_config__init + (RespSetThreadConfig *message); +size_t resp_set_thread_config__get_packed_size + (const RespSetThreadConfig *message); +size_t resp_set_thread_config__pack + (const RespSetThreadConfig *message, + uint8_t *out); +size_t resp_set_thread_config__pack_to_buffer + (const RespSetThreadConfig *message, + ProtobufCBuffer *buffer); +RespSetThreadConfig * + resp_set_thread_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_set_thread_config__free_unpacked + (RespSetThreadConfig *message, + ProtobufCAllocator *allocator); +/* CmdApplyWifiConfig methods */ +void cmd_apply_wifi_config__init + (CmdApplyWifiConfig *message); +size_t cmd_apply_wifi_config__get_packed_size + (const CmdApplyWifiConfig *message); +size_t cmd_apply_wifi_config__pack + (const CmdApplyWifiConfig *message, + uint8_t *out); +size_t cmd_apply_wifi_config__pack_to_buffer + (const CmdApplyWifiConfig *message, + ProtobufCBuffer *buffer); +CmdApplyWifiConfig * + cmd_apply_wifi_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_apply_wifi_config__free_unpacked + (CmdApplyWifiConfig *message, + ProtobufCAllocator *allocator); +/* CmdApplyThreadConfig methods */ +void cmd_apply_thread_config__init + (CmdApplyThreadConfig *message); +size_t cmd_apply_thread_config__get_packed_size + (const CmdApplyThreadConfig *message); +size_t cmd_apply_thread_config__pack + (const CmdApplyThreadConfig *message, + uint8_t *out); +size_t cmd_apply_thread_config__pack_to_buffer + (const CmdApplyThreadConfig *message, + ProtobufCBuffer *buffer); +CmdApplyThreadConfig * + cmd_apply_thread_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_apply_thread_config__free_unpacked + (CmdApplyThreadConfig *message, + ProtobufCAllocator *allocator); +/* RespApplyWifiConfig methods */ +void resp_apply_wifi_config__init + (RespApplyWifiConfig *message); +size_t resp_apply_wifi_config__get_packed_size + (const RespApplyWifiConfig *message); +size_t resp_apply_wifi_config__pack + (const RespApplyWifiConfig *message, + uint8_t *out); +size_t resp_apply_wifi_config__pack_to_buffer + (const RespApplyWifiConfig *message, + ProtobufCBuffer *buffer); +RespApplyWifiConfig * + resp_apply_wifi_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_apply_wifi_config__free_unpacked + (RespApplyWifiConfig *message, + ProtobufCAllocator *allocator); +/* RespApplyThreadConfig methods */ +void resp_apply_thread_config__init + (RespApplyThreadConfig *message); +size_t resp_apply_thread_config__get_packed_size + (const RespApplyThreadConfig *message); +size_t resp_apply_thread_config__pack + (const RespApplyThreadConfig *message, + uint8_t *out); +size_t resp_apply_thread_config__pack_to_buffer + (const RespApplyThreadConfig *message, + ProtobufCBuffer *buffer); +RespApplyThreadConfig * + resp_apply_thread_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_apply_thread_config__free_unpacked + (RespApplyThreadConfig *message, + ProtobufCAllocator *allocator); +/* NetworkConfigPayload methods */ +void network_config_payload__init + (NetworkConfigPayload *message); +size_t network_config_payload__get_packed_size + (const NetworkConfigPayload *message); +size_t network_config_payload__pack + (const NetworkConfigPayload *message, + uint8_t *out); +size_t network_config_payload__pack_to_buffer + (const NetworkConfigPayload *message, + ProtobufCBuffer *buffer); +NetworkConfigPayload * + network_config_payload__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void network_config_payload__free_unpacked + (NetworkConfigPayload *message, + ProtobufCAllocator *allocator); +/* --- per-message closures --- */ + +typedef void (*CmdGetWifiStatus_Closure) + (const CmdGetWifiStatus *message, + void *closure_data); +typedef void (*RespGetWifiStatus_Closure) + (const RespGetWifiStatus *message, + void *closure_data); +typedef void (*CmdGetThreadStatus_Closure) + (const CmdGetThreadStatus *message, + void *closure_data); +typedef void (*RespGetThreadStatus_Closure) + (const RespGetThreadStatus *message, + void *closure_data); +typedef void (*CmdSetWifiConfig_Closure) + (const CmdSetWifiConfig *message, + void *closure_data); +typedef void (*CmdSetThreadConfig_Closure) + (const CmdSetThreadConfig *message, + void *closure_data); +typedef void (*RespSetWifiConfig_Closure) + (const RespSetWifiConfig *message, + void *closure_data); +typedef void (*RespSetThreadConfig_Closure) + (const RespSetThreadConfig *message, + void *closure_data); +typedef void (*CmdApplyWifiConfig_Closure) + (const CmdApplyWifiConfig *message, + void *closure_data); +typedef void (*CmdApplyThreadConfig_Closure) + (const CmdApplyThreadConfig *message, + void *closure_data); +typedef void (*RespApplyWifiConfig_Closure) + (const RespApplyWifiConfig *message, + void *closure_data); +typedef void (*RespApplyThreadConfig_Closure) + (const RespApplyThreadConfig *message, + void *closure_data); +typedef void (*NetworkConfigPayload_Closure) + (const NetworkConfigPayload *message, + void *closure_data); + +/* --- services --- */ + + +/* --- descriptors --- */ + +extern const ProtobufCEnumDescriptor network_config_msg_type__descriptor; +extern const ProtobufCMessageDescriptor cmd_get_wifi_status__descriptor; +extern const ProtobufCMessageDescriptor resp_get_wifi_status__descriptor; +extern const ProtobufCMessageDescriptor cmd_get_thread_status__descriptor; +extern const ProtobufCMessageDescriptor resp_get_thread_status__descriptor; +extern const ProtobufCMessageDescriptor cmd_set_wifi_config__descriptor; +extern const ProtobufCMessageDescriptor cmd_set_thread_config__descriptor; +extern const ProtobufCMessageDescriptor resp_set_wifi_config__descriptor; +extern const ProtobufCMessageDescriptor resp_set_thread_config__descriptor; +extern const ProtobufCMessageDescriptor cmd_apply_wifi_config__descriptor; +extern const ProtobufCMessageDescriptor cmd_apply_thread_config__descriptor; +extern const ProtobufCMessageDescriptor resp_apply_wifi_config__descriptor; +extern const ProtobufCMessageDescriptor resp_apply_thread_config__descriptor; +extern const ProtobufCMessageDescriptor network_config_payload__descriptor; + +PROTOBUF_C__END_DECLS + + +#endif /* PROTOBUF_C_network_5fconfig_2eproto__INCLUDED */ diff --git a/components/network_provisioning/proto-c/network_constants.pb-c.c b/components/network_provisioning/proto-c/network_constants.pb-c.c new file mode 100644 index 0000000..fb482a6 --- /dev/null +++ b/components/network_provisioning/proto-c/network_constants.pb-c.c @@ -0,0 +1,509 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: network_constants.proto */ + +/* Do not generate deprecated warnings for self */ +#ifndef PROTOBUF_C__NO_DEPRECATED +#define PROTOBUF_C__NO_DEPRECATED +#endif + +#include "network_constants.pb-c.h" +void wifi_connected_state__init + (WifiConnectedState *message) +{ + static const WifiConnectedState init_value = WIFI_CONNECTED_STATE__INIT; + *message = init_value; +} +size_t wifi_connected_state__get_packed_size + (const WifiConnectedState *message) +{ + assert(message->base.descriptor == &wifi_connected_state__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t wifi_connected_state__pack + (const WifiConnectedState *message, + uint8_t *out) +{ + assert(message->base.descriptor == &wifi_connected_state__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t wifi_connected_state__pack_to_buffer + (const WifiConnectedState *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &wifi_connected_state__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +WifiConnectedState * + wifi_connected_state__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (WifiConnectedState *) + protobuf_c_message_unpack (&wifi_connected_state__descriptor, + allocator, len, data); +} +void wifi_connected_state__free_unpacked + (WifiConnectedState *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &wifi_connected_state__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void wifi_attempt_failed__init + (WifiAttemptFailed *message) +{ + static const WifiAttemptFailed init_value = WIFI_ATTEMPT_FAILED__INIT; + *message = init_value; +} +size_t wifi_attempt_failed__get_packed_size + (const WifiAttemptFailed *message) +{ + assert(message->base.descriptor == &wifi_attempt_failed__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t wifi_attempt_failed__pack + (const WifiAttemptFailed *message, + uint8_t *out) +{ + assert(message->base.descriptor == &wifi_attempt_failed__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t wifi_attempt_failed__pack_to_buffer + (const WifiAttemptFailed *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &wifi_attempt_failed__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +WifiAttemptFailed * + wifi_attempt_failed__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (WifiAttemptFailed *) + protobuf_c_message_unpack (&wifi_attempt_failed__descriptor, + allocator, len, data); +} +void wifi_attempt_failed__free_unpacked + (WifiAttemptFailed *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &wifi_attempt_failed__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void thread_attach_state__init + (ThreadAttachState *message) +{ + static const ThreadAttachState init_value = THREAD_ATTACH_STATE__INIT; + *message = init_value; +} +size_t thread_attach_state__get_packed_size + (const ThreadAttachState *message) +{ + assert(message->base.descriptor == &thread_attach_state__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t thread_attach_state__pack + (const ThreadAttachState *message, + uint8_t *out) +{ + assert(message->base.descriptor == &thread_attach_state__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t thread_attach_state__pack_to_buffer + (const ThreadAttachState *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &thread_attach_state__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +ThreadAttachState * + thread_attach_state__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (ThreadAttachState *) + protobuf_c_message_unpack (&thread_attach_state__descriptor, + allocator, len, data); +} +void thread_attach_state__free_unpacked + (ThreadAttachState *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &thread_attach_state__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +static const ProtobufCFieldDescriptor wifi_connected_state__field_descriptors[5] = +{ + { + "ip4_addr", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_STRING, + 0, /* quantifier_offset */ + offsetof(WifiConnectedState, ip4_addr), + NULL, + &protobuf_c_empty_string, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "auth_mode", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(WifiConnectedState, auth_mode), + &wifi_auth_mode__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "ssid", + 3, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(WifiConnectedState, ssid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "bssid", + 4, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(WifiConnectedState, bssid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "channel", + 5, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_INT32, + 0, /* quantifier_offset */ + offsetof(WifiConnectedState, channel), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned wifi_connected_state__field_indices_by_name[] = { + 1, /* field[1] = auth_mode */ + 3, /* field[3] = bssid */ + 4, /* field[4] = channel */ + 0, /* field[0] = ip4_addr */ + 2, /* field[2] = ssid */ +}; +static const ProtobufCIntRange wifi_connected_state__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 5 } +}; +const ProtobufCMessageDescriptor wifi_connected_state__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "WifiConnectedState", + "WifiConnectedState", + "WifiConnectedState", + "", + sizeof(WifiConnectedState), + 5, + wifi_connected_state__field_descriptors, + wifi_connected_state__field_indices_by_name, + 1, wifi_connected_state__number_ranges, + (ProtobufCMessageInit) wifi_connected_state__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor wifi_attempt_failed__field_descriptors[1] = +{ + { + "attempts_remaining", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(WifiAttemptFailed, attempts_remaining), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned wifi_attempt_failed__field_indices_by_name[] = { + 0, /* field[0] = attempts_remaining */ +}; +static const ProtobufCIntRange wifi_attempt_failed__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor wifi_attempt_failed__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "WifiAttemptFailed", + "WifiAttemptFailed", + "WifiAttemptFailed", + "", + sizeof(WifiAttemptFailed), + 1, + wifi_attempt_failed__field_descriptors, + wifi_attempt_failed__field_indices_by_name, + 1, wifi_attempt_failed__number_ranges, + (ProtobufCMessageInit) wifi_attempt_failed__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor thread_attach_state__field_descriptors[4] = +{ + { + "pan_id", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(ThreadAttachState, pan_id), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "ext_pan_id", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(ThreadAttachState, ext_pan_id), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "channel", + 3, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(ThreadAttachState, channel), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "name", + 4, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_STRING, + 0, /* quantifier_offset */ + offsetof(ThreadAttachState, name), + NULL, + &protobuf_c_empty_string, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned thread_attach_state__field_indices_by_name[] = { + 2, /* field[2] = channel */ + 1, /* field[1] = ext_pan_id */ + 3, /* field[3] = name */ + 0, /* field[0] = pan_id */ +}; +static const ProtobufCIntRange thread_attach_state__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 4 } +}; +const ProtobufCMessageDescriptor thread_attach_state__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "ThreadAttachState", + "ThreadAttachState", + "ThreadAttachState", + "", + sizeof(ThreadAttachState), + 4, + thread_attach_state__field_descriptors, + thread_attach_state__field_indices_by_name, + 1, thread_attach_state__number_ranges, + (ProtobufCMessageInit) thread_attach_state__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCEnumValue wifi_station_state__enum_values_by_number[4] = +{ + { "Connected", "WIFI_STATION_STATE__Connected", 0 }, + { "Connecting", "WIFI_STATION_STATE__Connecting", 1 }, + { "Disconnected", "WIFI_STATION_STATE__Disconnected", 2 }, + { "ConnectionFailed", "WIFI_STATION_STATE__ConnectionFailed", 3 }, +}; +static const ProtobufCIntRange wifi_station_state__value_ranges[] = { +{0, 0},{0, 4} +}; +static const ProtobufCEnumValueIndex wifi_station_state__enum_values_by_name[4] = +{ + { "Connected", 0 }, + { "Connecting", 1 }, + { "ConnectionFailed", 3 }, + { "Disconnected", 2 }, +}; +const ProtobufCEnumDescriptor wifi_station_state__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "WifiStationState", + "WifiStationState", + "WifiStationState", + "", + 4, + wifi_station_state__enum_values_by_number, + 4, + wifi_station_state__enum_values_by_name, + 1, + wifi_station_state__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; +static const ProtobufCEnumValue wifi_connect_failed_reason__enum_values_by_number[2] = +{ + { "AuthError", "WIFI_CONNECT_FAILED_REASON__AuthError", 0 }, + { "WifiNetworkNotFound", "WIFI_CONNECT_FAILED_REASON__WifiNetworkNotFound", 1 }, +}; +static const ProtobufCIntRange wifi_connect_failed_reason__value_ranges[] = { +{0, 0},{0, 2} +}; +static const ProtobufCEnumValueIndex wifi_connect_failed_reason__enum_values_by_name[2] = +{ + { "AuthError", 0 }, + { "WifiNetworkNotFound", 1 }, +}; +const ProtobufCEnumDescriptor wifi_connect_failed_reason__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "WifiConnectFailedReason", + "WifiConnectFailedReason", + "WifiConnectFailedReason", + "", + 2, + wifi_connect_failed_reason__enum_values_by_number, + 2, + wifi_connect_failed_reason__enum_values_by_name, + 1, + wifi_connect_failed_reason__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; +static const ProtobufCEnumValue wifi_auth_mode__enum_values_by_number[8] = +{ + { "Open", "WIFI_AUTH_MODE__Open", 0 }, + { "WEP", "WIFI_AUTH_MODE__WEP", 1 }, + { "WPA_PSK", "WIFI_AUTH_MODE__WPA_PSK", 2 }, + { "WPA2_PSK", "WIFI_AUTH_MODE__WPA2_PSK", 3 }, + { "WPA_WPA2_PSK", "WIFI_AUTH_MODE__WPA_WPA2_PSK", 4 }, + { "WPA2_ENTERPRISE", "WIFI_AUTH_MODE__WPA2_ENTERPRISE", 5 }, + { "WPA3_PSK", "WIFI_AUTH_MODE__WPA3_PSK", 6 }, + { "WPA2_WPA3_PSK", "WIFI_AUTH_MODE__WPA2_WPA3_PSK", 7 }, +}; +static const ProtobufCIntRange wifi_auth_mode__value_ranges[] = { +{0, 0},{0, 8} +}; +static const ProtobufCEnumValueIndex wifi_auth_mode__enum_values_by_name[8] = +{ + { "Open", 0 }, + { "WEP", 1 }, + { "WPA2_ENTERPRISE", 5 }, + { "WPA2_PSK", 3 }, + { "WPA2_WPA3_PSK", 7 }, + { "WPA3_PSK", 6 }, + { "WPA_PSK", 2 }, + { "WPA_WPA2_PSK", 4 }, +}; +const ProtobufCEnumDescriptor wifi_auth_mode__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "WifiAuthMode", + "WifiAuthMode", + "WifiAuthMode", + "", + 8, + wifi_auth_mode__enum_values_by_number, + 8, + wifi_auth_mode__enum_values_by_name, + 1, + wifi_auth_mode__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; +static const ProtobufCEnumValue thread_network_state__enum_values_by_number[4] = +{ + { "Attached", "THREAD_NETWORK_STATE__Attached", 0 }, + { "Attaching", "THREAD_NETWORK_STATE__Attaching", 1 }, + { "Dettached", "THREAD_NETWORK_STATE__Dettached", 2 }, + { "AttachingFailed", "THREAD_NETWORK_STATE__AttachingFailed", 3 }, +}; +static const ProtobufCIntRange thread_network_state__value_ranges[] = { +{0, 0},{0, 4} +}; +static const ProtobufCEnumValueIndex thread_network_state__enum_values_by_name[4] = +{ + { "Attached", 0 }, + { "Attaching", 1 }, + { "AttachingFailed", 3 }, + { "Dettached", 2 }, +}; +const ProtobufCEnumDescriptor thread_network_state__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "ThreadNetworkState", + "ThreadNetworkState", + "ThreadNetworkState", + "", + 4, + thread_network_state__enum_values_by_number, + 4, + thread_network_state__enum_values_by_name, + 1, + thread_network_state__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; +static const ProtobufCEnumValue thread_attach_failed_reason__enum_values_by_number[2] = +{ + { "DatasetInvalid", "THREAD_ATTACH_FAILED_REASON__DatasetInvalid", 0 }, + { "ThreadNetworkNotFound", "THREAD_ATTACH_FAILED_REASON__ThreadNetworkNotFound", 1 }, +}; +static const ProtobufCIntRange thread_attach_failed_reason__value_ranges[] = { +{0, 0},{0, 2} +}; +static const ProtobufCEnumValueIndex thread_attach_failed_reason__enum_values_by_name[2] = +{ + { "DatasetInvalid", 0 }, + { "ThreadNetworkNotFound", 1 }, +}; +const ProtobufCEnumDescriptor thread_attach_failed_reason__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "ThreadAttachFailedReason", + "ThreadAttachFailedReason", + "ThreadAttachFailedReason", + "", + 2, + thread_attach_failed_reason__enum_values_by_number, + 2, + thread_attach_failed_reason__enum_values_by_name, + 1, + thread_attach_failed_reason__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; diff --git a/components/network_provisioning/proto-c/network_constants.pb-c.h b/components/network_provisioning/proto-c/network_constants.pb-c.h new file mode 100644 index 0000000..f92d8c3 --- /dev/null +++ b/components/network_provisioning/proto-c/network_constants.pb-c.h @@ -0,0 +1,186 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: network_constants.proto */ + +#ifndef PROTOBUF_C_network_5fconstants_2eproto__INCLUDED +#define PROTOBUF_C_network_5fconstants_2eproto__INCLUDED + +#include + +PROTOBUF_C__BEGIN_DECLS + +#if PROTOBUF_C_VERSION_NUMBER < 1003000 +# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. +#elif 1004000 < PROTOBUF_C_MIN_COMPILER_VERSION +# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. +#endif + + +typedef struct WifiConnectedState WifiConnectedState; +typedef struct WifiAttemptFailed WifiAttemptFailed; +typedef struct ThreadAttachState ThreadAttachState; + + +/* --- enums --- */ + +typedef enum _WifiStationState { + WIFI_STATION_STATE__Connected = 0, + WIFI_STATION_STATE__Connecting = 1, + WIFI_STATION_STATE__Disconnected = 2, + WIFI_STATION_STATE__ConnectionFailed = 3 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(WIFI_STATION_STATE) +} WifiStationState; +typedef enum _WifiConnectFailedReason { + WIFI_CONNECT_FAILED_REASON__AuthError = 0, + WIFI_CONNECT_FAILED_REASON__WifiNetworkNotFound = 1 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(WIFI_CONNECT_FAILED_REASON) +} WifiConnectFailedReason; +typedef enum _WifiAuthMode { + WIFI_AUTH_MODE__Open = 0, + WIFI_AUTH_MODE__WEP = 1, + WIFI_AUTH_MODE__WPA_PSK = 2, + WIFI_AUTH_MODE__WPA2_PSK = 3, + WIFI_AUTH_MODE__WPA_WPA2_PSK = 4, + WIFI_AUTH_MODE__WPA2_ENTERPRISE = 5, + WIFI_AUTH_MODE__WPA3_PSK = 6, + WIFI_AUTH_MODE__WPA2_WPA3_PSK = 7 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(WIFI_AUTH_MODE) +} WifiAuthMode; +typedef enum _ThreadNetworkState { + THREAD_NETWORK_STATE__Attached = 0, + THREAD_NETWORK_STATE__Attaching = 1, + THREAD_NETWORK_STATE__Dettached = 2, + THREAD_NETWORK_STATE__AttachingFailed = 3 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(THREAD_NETWORK_STATE) +} ThreadNetworkState; +typedef enum _ThreadAttachFailedReason { + THREAD_ATTACH_FAILED_REASON__DatasetInvalid = 0, + THREAD_ATTACH_FAILED_REASON__ThreadNetworkNotFound = 1 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(THREAD_ATTACH_FAILED_REASON) +} ThreadAttachFailedReason; + +/* --- messages --- */ + +struct WifiConnectedState +{ + ProtobufCMessage base; + char *ip4_addr; + WifiAuthMode auth_mode; + ProtobufCBinaryData ssid; + ProtobufCBinaryData bssid; + int32_t channel; +}; +#define WIFI_CONNECTED_STATE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&wifi_connected_state__descriptor) \ + , (char *)protobuf_c_empty_string, WIFI_AUTH_MODE__Open, {0,NULL}, {0,NULL}, 0 } + + +struct WifiAttemptFailed +{ + ProtobufCMessage base; + uint32_t attempts_remaining; +}; +#define WIFI_ATTEMPT_FAILED__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&wifi_attempt_failed__descriptor) \ + , 0 } + + +struct ThreadAttachState +{ + ProtobufCMessage base; + uint32_t pan_id; + ProtobufCBinaryData ext_pan_id; + uint32_t channel; + char *name; +}; +#define THREAD_ATTACH_STATE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&thread_attach_state__descriptor) \ + , 0, {0,NULL}, 0, (char *)protobuf_c_empty_string } + + +/* WifiConnectedState methods */ +void wifi_connected_state__init + (WifiConnectedState *message); +size_t wifi_connected_state__get_packed_size + (const WifiConnectedState *message); +size_t wifi_connected_state__pack + (const WifiConnectedState *message, + uint8_t *out); +size_t wifi_connected_state__pack_to_buffer + (const WifiConnectedState *message, + ProtobufCBuffer *buffer); +WifiConnectedState * + wifi_connected_state__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void wifi_connected_state__free_unpacked + (WifiConnectedState *message, + ProtobufCAllocator *allocator); +/* WifiAttemptFailed methods */ +void wifi_attempt_failed__init + (WifiAttemptFailed *message); +size_t wifi_attempt_failed__get_packed_size + (const WifiAttemptFailed *message); +size_t wifi_attempt_failed__pack + (const WifiAttemptFailed *message, + uint8_t *out); +size_t wifi_attempt_failed__pack_to_buffer + (const WifiAttemptFailed *message, + ProtobufCBuffer *buffer); +WifiAttemptFailed * + wifi_attempt_failed__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void wifi_attempt_failed__free_unpacked + (WifiAttemptFailed *message, + ProtobufCAllocator *allocator); +/* ThreadAttachState methods */ +void thread_attach_state__init + (ThreadAttachState *message); +size_t thread_attach_state__get_packed_size + (const ThreadAttachState *message); +size_t thread_attach_state__pack + (const ThreadAttachState *message, + uint8_t *out); +size_t thread_attach_state__pack_to_buffer + (const ThreadAttachState *message, + ProtobufCBuffer *buffer); +ThreadAttachState * + thread_attach_state__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void thread_attach_state__free_unpacked + (ThreadAttachState *message, + ProtobufCAllocator *allocator); +/* --- per-message closures --- */ + +typedef void (*WifiConnectedState_Closure) + (const WifiConnectedState *message, + void *closure_data); +typedef void (*WifiAttemptFailed_Closure) + (const WifiAttemptFailed *message, + void *closure_data); +typedef void (*ThreadAttachState_Closure) + (const ThreadAttachState *message, + void *closure_data); + +/* --- services --- */ + + +/* --- descriptors --- */ + +extern const ProtobufCEnumDescriptor wifi_station_state__descriptor; +extern const ProtobufCEnumDescriptor wifi_connect_failed_reason__descriptor; +extern const ProtobufCEnumDescriptor wifi_auth_mode__descriptor; +extern const ProtobufCEnumDescriptor thread_network_state__descriptor; +extern const ProtobufCEnumDescriptor thread_attach_failed_reason__descriptor; +extern const ProtobufCMessageDescriptor wifi_connected_state__descriptor; +extern const ProtobufCMessageDescriptor wifi_attempt_failed__descriptor; +extern const ProtobufCMessageDescriptor thread_attach_state__descriptor; + +PROTOBUF_C__END_DECLS + + +#endif /* PROTOBUF_C_network_5fconstants_2eproto__INCLUDED */ diff --git a/components/network_provisioning/proto-c/network_ctrl.pb-c.c b/components/network_provisioning/proto-c/network_ctrl.pb-c.c new file mode 100644 index 0000000..423e2a7 --- /dev/null +++ b/components/network_provisioning/proto-c/network_ctrl.pb-c.c @@ -0,0 +1,756 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: network_ctrl.proto */ + +/* Do not generate deprecated warnings for self */ +#ifndef PROTOBUF_C__NO_DEPRECATED +#define PROTOBUF_C__NO_DEPRECATED +#endif + +#include "network_ctrl.pb-c.h" +void cmd_ctrl_wifi_reset__init + (CmdCtrlWifiReset *message) +{ + static const CmdCtrlWifiReset init_value = CMD_CTRL_WIFI_RESET__INIT; + *message = init_value; +} +size_t cmd_ctrl_wifi_reset__get_packed_size + (const CmdCtrlWifiReset *message) +{ + assert(message->base.descriptor == &cmd_ctrl_wifi_reset__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_ctrl_wifi_reset__pack + (const CmdCtrlWifiReset *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_ctrl_wifi_reset__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_ctrl_wifi_reset__pack_to_buffer + (const CmdCtrlWifiReset *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_ctrl_wifi_reset__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdCtrlWifiReset * + cmd_ctrl_wifi_reset__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdCtrlWifiReset *) + protobuf_c_message_unpack (&cmd_ctrl_wifi_reset__descriptor, + allocator, len, data); +} +void cmd_ctrl_wifi_reset__free_unpacked + (CmdCtrlWifiReset *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_ctrl_wifi_reset__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_ctrl_wifi_reset__init + (RespCtrlWifiReset *message) +{ + static const RespCtrlWifiReset init_value = RESP_CTRL_WIFI_RESET__INIT; + *message = init_value; +} +size_t resp_ctrl_wifi_reset__get_packed_size + (const RespCtrlWifiReset *message) +{ + assert(message->base.descriptor == &resp_ctrl_wifi_reset__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_ctrl_wifi_reset__pack + (const RespCtrlWifiReset *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_ctrl_wifi_reset__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_ctrl_wifi_reset__pack_to_buffer + (const RespCtrlWifiReset *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_ctrl_wifi_reset__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespCtrlWifiReset * + resp_ctrl_wifi_reset__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespCtrlWifiReset *) + protobuf_c_message_unpack (&resp_ctrl_wifi_reset__descriptor, + allocator, len, data); +} +void resp_ctrl_wifi_reset__free_unpacked + (RespCtrlWifiReset *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_ctrl_wifi_reset__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_ctrl_wifi_reprov__init + (CmdCtrlWifiReprov *message) +{ + static const CmdCtrlWifiReprov init_value = CMD_CTRL_WIFI_REPROV__INIT; + *message = init_value; +} +size_t cmd_ctrl_wifi_reprov__get_packed_size + (const CmdCtrlWifiReprov *message) +{ + assert(message->base.descriptor == &cmd_ctrl_wifi_reprov__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_ctrl_wifi_reprov__pack + (const CmdCtrlWifiReprov *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_ctrl_wifi_reprov__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_ctrl_wifi_reprov__pack_to_buffer + (const CmdCtrlWifiReprov *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_ctrl_wifi_reprov__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdCtrlWifiReprov * + cmd_ctrl_wifi_reprov__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdCtrlWifiReprov *) + protobuf_c_message_unpack (&cmd_ctrl_wifi_reprov__descriptor, + allocator, len, data); +} +void cmd_ctrl_wifi_reprov__free_unpacked + (CmdCtrlWifiReprov *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_ctrl_wifi_reprov__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_ctrl_wifi_reprov__init + (RespCtrlWifiReprov *message) +{ + static const RespCtrlWifiReprov init_value = RESP_CTRL_WIFI_REPROV__INIT; + *message = init_value; +} +size_t resp_ctrl_wifi_reprov__get_packed_size + (const RespCtrlWifiReprov *message) +{ + assert(message->base.descriptor == &resp_ctrl_wifi_reprov__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_ctrl_wifi_reprov__pack + (const RespCtrlWifiReprov *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_ctrl_wifi_reprov__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_ctrl_wifi_reprov__pack_to_buffer + (const RespCtrlWifiReprov *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_ctrl_wifi_reprov__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespCtrlWifiReprov * + resp_ctrl_wifi_reprov__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespCtrlWifiReprov *) + protobuf_c_message_unpack (&resp_ctrl_wifi_reprov__descriptor, + allocator, len, data); +} +void resp_ctrl_wifi_reprov__free_unpacked + (RespCtrlWifiReprov *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_ctrl_wifi_reprov__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_ctrl_thread_reset__init + (CmdCtrlThreadReset *message) +{ + static const CmdCtrlThreadReset init_value = CMD_CTRL_THREAD_RESET__INIT; + *message = init_value; +} +size_t cmd_ctrl_thread_reset__get_packed_size + (const CmdCtrlThreadReset *message) +{ + assert(message->base.descriptor == &cmd_ctrl_thread_reset__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_ctrl_thread_reset__pack + (const CmdCtrlThreadReset *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_ctrl_thread_reset__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_ctrl_thread_reset__pack_to_buffer + (const CmdCtrlThreadReset *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_ctrl_thread_reset__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdCtrlThreadReset * + cmd_ctrl_thread_reset__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdCtrlThreadReset *) + protobuf_c_message_unpack (&cmd_ctrl_thread_reset__descriptor, + allocator, len, data); +} +void cmd_ctrl_thread_reset__free_unpacked + (CmdCtrlThreadReset *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_ctrl_thread_reset__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_ctrl_thread_reset__init + (RespCtrlThreadReset *message) +{ + static const RespCtrlThreadReset init_value = RESP_CTRL_THREAD_RESET__INIT; + *message = init_value; +} +size_t resp_ctrl_thread_reset__get_packed_size + (const RespCtrlThreadReset *message) +{ + assert(message->base.descriptor == &resp_ctrl_thread_reset__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_ctrl_thread_reset__pack + (const RespCtrlThreadReset *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_ctrl_thread_reset__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_ctrl_thread_reset__pack_to_buffer + (const RespCtrlThreadReset *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_ctrl_thread_reset__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespCtrlThreadReset * + resp_ctrl_thread_reset__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespCtrlThreadReset *) + protobuf_c_message_unpack (&resp_ctrl_thread_reset__descriptor, + allocator, len, data); +} +void resp_ctrl_thread_reset__free_unpacked + (RespCtrlThreadReset *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_ctrl_thread_reset__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_ctrl_thread_reprov__init + (CmdCtrlThreadReprov *message) +{ + static const CmdCtrlThreadReprov init_value = CMD_CTRL_THREAD_REPROV__INIT; + *message = init_value; +} +size_t cmd_ctrl_thread_reprov__get_packed_size + (const CmdCtrlThreadReprov *message) +{ + assert(message->base.descriptor == &cmd_ctrl_thread_reprov__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_ctrl_thread_reprov__pack + (const CmdCtrlThreadReprov *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_ctrl_thread_reprov__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_ctrl_thread_reprov__pack_to_buffer + (const CmdCtrlThreadReprov *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_ctrl_thread_reprov__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdCtrlThreadReprov * + cmd_ctrl_thread_reprov__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdCtrlThreadReprov *) + protobuf_c_message_unpack (&cmd_ctrl_thread_reprov__descriptor, + allocator, len, data); +} +void cmd_ctrl_thread_reprov__free_unpacked + (CmdCtrlThreadReprov *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_ctrl_thread_reprov__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_ctrl_thread_reprov__init + (RespCtrlThreadReprov *message) +{ + static const RespCtrlThreadReprov init_value = RESP_CTRL_THREAD_REPROV__INIT; + *message = init_value; +} +size_t resp_ctrl_thread_reprov__get_packed_size + (const RespCtrlThreadReprov *message) +{ + assert(message->base.descriptor == &resp_ctrl_thread_reprov__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_ctrl_thread_reprov__pack + (const RespCtrlThreadReprov *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_ctrl_thread_reprov__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_ctrl_thread_reprov__pack_to_buffer + (const RespCtrlThreadReprov *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_ctrl_thread_reprov__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespCtrlThreadReprov * + resp_ctrl_thread_reprov__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespCtrlThreadReprov *) + protobuf_c_message_unpack (&resp_ctrl_thread_reprov__descriptor, + allocator, len, data); +} +void resp_ctrl_thread_reprov__free_unpacked + (RespCtrlThreadReprov *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_ctrl_thread_reprov__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void network_ctrl_payload__init + (NetworkCtrlPayload *message) +{ + static const NetworkCtrlPayload init_value = NETWORK_CTRL_PAYLOAD__INIT; + *message = init_value; +} +size_t network_ctrl_payload__get_packed_size + (const NetworkCtrlPayload *message) +{ + assert(message->base.descriptor == &network_ctrl_payload__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t network_ctrl_payload__pack + (const NetworkCtrlPayload *message, + uint8_t *out) +{ + assert(message->base.descriptor == &network_ctrl_payload__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t network_ctrl_payload__pack_to_buffer + (const NetworkCtrlPayload *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &network_ctrl_payload__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +NetworkCtrlPayload * + network_ctrl_payload__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (NetworkCtrlPayload *) + protobuf_c_message_unpack (&network_ctrl_payload__descriptor, + allocator, len, data); +} +void network_ctrl_payload__free_unpacked + (NetworkCtrlPayload *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &network_ctrl_payload__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +#define cmd_ctrl_wifi_reset__field_descriptors NULL +#define cmd_ctrl_wifi_reset__field_indices_by_name NULL +#define cmd_ctrl_wifi_reset__number_ranges NULL +const ProtobufCMessageDescriptor cmd_ctrl_wifi_reset__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdCtrlWifiReset", + "CmdCtrlWifiReset", + "CmdCtrlWifiReset", + "", + sizeof(CmdCtrlWifiReset), + 0, + cmd_ctrl_wifi_reset__field_descriptors, + cmd_ctrl_wifi_reset__field_indices_by_name, + 0, cmd_ctrl_wifi_reset__number_ranges, + (ProtobufCMessageInit) cmd_ctrl_wifi_reset__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define resp_ctrl_wifi_reset__field_descriptors NULL +#define resp_ctrl_wifi_reset__field_indices_by_name NULL +#define resp_ctrl_wifi_reset__number_ranges NULL +const ProtobufCMessageDescriptor resp_ctrl_wifi_reset__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespCtrlWifiReset", + "RespCtrlWifiReset", + "RespCtrlWifiReset", + "", + sizeof(RespCtrlWifiReset), + 0, + resp_ctrl_wifi_reset__field_descriptors, + resp_ctrl_wifi_reset__field_indices_by_name, + 0, resp_ctrl_wifi_reset__number_ranges, + (ProtobufCMessageInit) resp_ctrl_wifi_reset__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define cmd_ctrl_wifi_reprov__field_descriptors NULL +#define cmd_ctrl_wifi_reprov__field_indices_by_name NULL +#define cmd_ctrl_wifi_reprov__number_ranges NULL +const ProtobufCMessageDescriptor cmd_ctrl_wifi_reprov__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdCtrlWifiReprov", + "CmdCtrlWifiReprov", + "CmdCtrlWifiReprov", + "", + sizeof(CmdCtrlWifiReprov), + 0, + cmd_ctrl_wifi_reprov__field_descriptors, + cmd_ctrl_wifi_reprov__field_indices_by_name, + 0, cmd_ctrl_wifi_reprov__number_ranges, + (ProtobufCMessageInit) cmd_ctrl_wifi_reprov__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define resp_ctrl_wifi_reprov__field_descriptors NULL +#define resp_ctrl_wifi_reprov__field_indices_by_name NULL +#define resp_ctrl_wifi_reprov__number_ranges NULL +const ProtobufCMessageDescriptor resp_ctrl_wifi_reprov__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespCtrlWifiReprov", + "RespCtrlWifiReprov", + "RespCtrlWifiReprov", + "", + sizeof(RespCtrlWifiReprov), + 0, + resp_ctrl_wifi_reprov__field_descriptors, + resp_ctrl_wifi_reprov__field_indices_by_name, + 0, resp_ctrl_wifi_reprov__number_ranges, + (ProtobufCMessageInit) resp_ctrl_wifi_reprov__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define cmd_ctrl_thread_reset__field_descriptors NULL +#define cmd_ctrl_thread_reset__field_indices_by_name NULL +#define cmd_ctrl_thread_reset__number_ranges NULL +const ProtobufCMessageDescriptor cmd_ctrl_thread_reset__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdCtrlThreadReset", + "CmdCtrlThreadReset", + "CmdCtrlThreadReset", + "", + sizeof(CmdCtrlThreadReset), + 0, + cmd_ctrl_thread_reset__field_descriptors, + cmd_ctrl_thread_reset__field_indices_by_name, + 0, cmd_ctrl_thread_reset__number_ranges, + (ProtobufCMessageInit) cmd_ctrl_thread_reset__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define resp_ctrl_thread_reset__field_descriptors NULL +#define resp_ctrl_thread_reset__field_indices_by_name NULL +#define resp_ctrl_thread_reset__number_ranges NULL +const ProtobufCMessageDescriptor resp_ctrl_thread_reset__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespCtrlThreadReset", + "RespCtrlThreadReset", + "RespCtrlThreadReset", + "", + sizeof(RespCtrlThreadReset), + 0, + resp_ctrl_thread_reset__field_descriptors, + resp_ctrl_thread_reset__field_indices_by_name, + 0, resp_ctrl_thread_reset__number_ranges, + (ProtobufCMessageInit) resp_ctrl_thread_reset__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define cmd_ctrl_thread_reprov__field_descriptors NULL +#define cmd_ctrl_thread_reprov__field_indices_by_name NULL +#define cmd_ctrl_thread_reprov__number_ranges NULL +const ProtobufCMessageDescriptor cmd_ctrl_thread_reprov__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdCtrlThreadReprov", + "CmdCtrlThreadReprov", + "CmdCtrlThreadReprov", + "", + sizeof(CmdCtrlThreadReprov), + 0, + cmd_ctrl_thread_reprov__field_descriptors, + cmd_ctrl_thread_reprov__field_indices_by_name, + 0, cmd_ctrl_thread_reprov__number_ranges, + (ProtobufCMessageInit) cmd_ctrl_thread_reprov__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define resp_ctrl_thread_reprov__field_descriptors NULL +#define resp_ctrl_thread_reprov__field_indices_by_name NULL +#define resp_ctrl_thread_reprov__number_ranges NULL +const ProtobufCMessageDescriptor resp_ctrl_thread_reprov__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespCtrlThreadReprov", + "RespCtrlThreadReprov", + "RespCtrlThreadReprov", + "", + sizeof(RespCtrlThreadReprov), + 0, + resp_ctrl_thread_reprov__field_descriptors, + resp_ctrl_thread_reprov__field_indices_by_name, + 0, resp_ctrl_thread_reprov__number_ranges, + (ProtobufCMessageInit) resp_ctrl_thread_reprov__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor network_ctrl_payload__field_descriptors[10] = +{ + { + "msg", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(NetworkCtrlPayload, msg), + &network_ctrl_msg_type__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "status", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(NetworkCtrlPayload, status), + &status__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_ctrl_wifi_reset", + 11, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkCtrlPayload, payload_case), + offsetof(NetworkCtrlPayload, cmd_ctrl_wifi_reset), + &cmd_ctrl_wifi_reset__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_ctrl_wifi_reset", + 12, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkCtrlPayload, payload_case), + offsetof(NetworkCtrlPayload, resp_ctrl_wifi_reset), + &resp_ctrl_wifi_reset__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_ctrl_wifi_reprov", + 13, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkCtrlPayload, payload_case), + offsetof(NetworkCtrlPayload, cmd_ctrl_wifi_reprov), + &cmd_ctrl_wifi_reprov__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_ctrl_wifi_reprov", + 14, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkCtrlPayload, payload_case), + offsetof(NetworkCtrlPayload, resp_ctrl_wifi_reprov), + &resp_ctrl_wifi_reprov__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_ctrl_thread_reset", + 15, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkCtrlPayload, payload_case), + offsetof(NetworkCtrlPayload, cmd_ctrl_thread_reset), + &cmd_ctrl_thread_reset__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_ctrl_thread_reset", + 16, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkCtrlPayload, payload_case), + offsetof(NetworkCtrlPayload, resp_ctrl_thread_reset), + &resp_ctrl_thread_reset__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_ctrl_thread_reprov", + 17, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkCtrlPayload, payload_case), + offsetof(NetworkCtrlPayload, cmd_ctrl_thread_reprov), + &cmd_ctrl_thread_reprov__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_ctrl_thread_reprov", + 18, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkCtrlPayload, payload_case), + offsetof(NetworkCtrlPayload, resp_ctrl_thread_reprov), + &resp_ctrl_thread_reprov__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned network_ctrl_payload__field_indices_by_name[] = { + 8, /* field[8] = cmd_ctrl_thread_reprov */ + 6, /* field[6] = cmd_ctrl_thread_reset */ + 4, /* field[4] = cmd_ctrl_wifi_reprov */ + 2, /* field[2] = cmd_ctrl_wifi_reset */ + 0, /* field[0] = msg */ + 9, /* field[9] = resp_ctrl_thread_reprov */ + 7, /* field[7] = resp_ctrl_thread_reset */ + 5, /* field[5] = resp_ctrl_wifi_reprov */ + 3, /* field[3] = resp_ctrl_wifi_reset */ + 1, /* field[1] = status */ +}; +static const ProtobufCIntRange network_ctrl_payload__number_ranges[2 + 1] = +{ + { 1, 0 }, + { 11, 2 }, + { 0, 10 } +}; +const ProtobufCMessageDescriptor network_ctrl_payload__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "NetworkCtrlPayload", + "NetworkCtrlPayload", + "NetworkCtrlPayload", + "", + sizeof(NetworkCtrlPayload), + 10, + network_ctrl_payload__field_descriptors, + network_ctrl_payload__field_indices_by_name, + 2, network_ctrl_payload__number_ranges, + (ProtobufCMessageInit) network_ctrl_payload__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCEnumValue network_ctrl_msg_type__enum_values_by_number[9] = +{ + { "TypeCtrlReserved", "NETWORK_CTRL_MSG_TYPE__TypeCtrlReserved", 0 }, + { "TypeCmdCtrlWifiReset", "NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlWifiReset", 1 }, + { "TypeRespCtrlWifiReset", "NETWORK_CTRL_MSG_TYPE__TypeRespCtrlWifiReset", 2 }, + { "TypeCmdCtrlWifiReprov", "NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlWifiReprov", 3 }, + { "TypeRespCtrlWifiReprov", "NETWORK_CTRL_MSG_TYPE__TypeRespCtrlWifiReprov", 4 }, + { "TypeCmdCtrlThreadReset", "NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlThreadReset", 5 }, + { "TypeRespCtrlThreadReset", "NETWORK_CTRL_MSG_TYPE__TypeRespCtrlThreadReset", 6 }, + { "TypeCmdCtrlThreadReprov", "NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlThreadReprov", 7 }, + { "TypeRespCtrlThreadReprov", "NETWORK_CTRL_MSG_TYPE__TypeRespCtrlThreadReprov", 8 }, +}; +static const ProtobufCIntRange network_ctrl_msg_type__value_ranges[] = { +{0, 0},{0, 9} +}; +static const ProtobufCEnumValueIndex network_ctrl_msg_type__enum_values_by_name[9] = +{ + { "TypeCmdCtrlThreadReprov", 7 }, + { "TypeCmdCtrlThreadReset", 5 }, + { "TypeCmdCtrlWifiReprov", 3 }, + { "TypeCmdCtrlWifiReset", 1 }, + { "TypeCtrlReserved", 0 }, + { "TypeRespCtrlThreadReprov", 8 }, + { "TypeRespCtrlThreadReset", 6 }, + { "TypeRespCtrlWifiReprov", 4 }, + { "TypeRespCtrlWifiReset", 2 }, +}; +const ProtobufCEnumDescriptor network_ctrl_msg_type__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "NetworkCtrlMsgType", + "NetworkCtrlMsgType", + "NetworkCtrlMsgType", + "", + 9, + network_ctrl_msg_type__enum_values_by_number, + 9, + network_ctrl_msg_type__enum_values_by_name, + 1, + network_ctrl_msg_type__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; diff --git a/components/network_provisioning/proto-c/network_ctrl.pb-c.h b/components/network_provisioning/proto-c/network_ctrl.pb-c.h new file mode 100644 index 0000000..3e48f09 --- /dev/null +++ b/components/network_provisioning/proto-c/network_ctrl.pb-c.h @@ -0,0 +1,374 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: network_ctrl.proto */ + +#ifndef PROTOBUF_C_network_5fctrl_2eproto__INCLUDED +#define PROTOBUF_C_network_5fctrl_2eproto__INCLUDED + +#include + +PROTOBUF_C__BEGIN_DECLS + +#if PROTOBUF_C_VERSION_NUMBER < 1003000 +# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. +#elif 1004000 < PROTOBUF_C_MIN_COMPILER_VERSION +# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. +#endif + +#include "constants.pb-c.h" + +typedef struct CmdCtrlWifiReset CmdCtrlWifiReset; +typedef struct RespCtrlWifiReset RespCtrlWifiReset; +typedef struct CmdCtrlWifiReprov CmdCtrlWifiReprov; +typedef struct RespCtrlWifiReprov RespCtrlWifiReprov; +typedef struct CmdCtrlThreadReset CmdCtrlThreadReset; +typedef struct RespCtrlThreadReset RespCtrlThreadReset; +typedef struct CmdCtrlThreadReprov CmdCtrlThreadReprov; +typedef struct RespCtrlThreadReprov RespCtrlThreadReprov; +typedef struct NetworkCtrlPayload NetworkCtrlPayload; + + +/* --- enums --- */ + +typedef enum _NetworkCtrlMsgType { + NETWORK_CTRL_MSG_TYPE__TypeCtrlReserved = 0, + NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlWifiReset = 1, + NETWORK_CTRL_MSG_TYPE__TypeRespCtrlWifiReset = 2, + NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlWifiReprov = 3, + NETWORK_CTRL_MSG_TYPE__TypeRespCtrlWifiReprov = 4, + NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlThreadReset = 5, + NETWORK_CTRL_MSG_TYPE__TypeRespCtrlThreadReset = 6, + NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlThreadReprov = 7, + NETWORK_CTRL_MSG_TYPE__TypeRespCtrlThreadReprov = 8 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(NETWORK_CTRL_MSG_TYPE) +} NetworkCtrlMsgType; + +/* --- messages --- */ + +struct CmdCtrlWifiReset +{ + ProtobufCMessage base; +}; +#define CMD_CTRL_WIFI_RESET__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_ctrl_wifi_reset__descriptor) \ + } + + +struct RespCtrlWifiReset +{ + ProtobufCMessage base; +}; +#define RESP_CTRL_WIFI_RESET__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_ctrl_wifi_reset__descriptor) \ + } + + +struct CmdCtrlWifiReprov +{ + ProtobufCMessage base; +}; +#define CMD_CTRL_WIFI_REPROV__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_ctrl_wifi_reprov__descriptor) \ + } + + +struct RespCtrlWifiReprov +{ + ProtobufCMessage base; +}; +#define RESP_CTRL_WIFI_REPROV__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_ctrl_wifi_reprov__descriptor) \ + } + + +struct CmdCtrlThreadReset +{ + ProtobufCMessage base; +}; +#define CMD_CTRL_THREAD_RESET__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_ctrl_thread_reset__descriptor) \ + } + + +struct RespCtrlThreadReset +{ + ProtobufCMessage base; +}; +#define RESP_CTRL_THREAD_RESET__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_ctrl_thread_reset__descriptor) \ + } + + +struct CmdCtrlThreadReprov +{ + ProtobufCMessage base; +}; +#define CMD_CTRL_THREAD_REPROV__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_ctrl_thread_reprov__descriptor) \ + } + + +struct RespCtrlThreadReprov +{ + ProtobufCMessage base; +}; +#define RESP_CTRL_THREAD_REPROV__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_ctrl_thread_reprov__descriptor) \ + } + + +typedef enum { + NETWORK_CTRL_PAYLOAD__PAYLOAD__NOT_SET = 0, + NETWORK_CTRL_PAYLOAD__PAYLOAD_CMD_CTRL_WIFI_RESET = 11, + NETWORK_CTRL_PAYLOAD__PAYLOAD_RESP_CTRL_WIFI_RESET = 12, + NETWORK_CTRL_PAYLOAD__PAYLOAD_CMD_CTRL_WIFI_REPROV = 13, + NETWORK_CTRL_PAYLOAD__PAYLOAD_RESP_CTRL_WIFI_REPROV = 14, + NETWORK_CTRL_PAYLOAD__PAYLOAD_CMD_CTRL_THREAD_RESET = 15, + NETWORK_CTRL_PAYLOAD__PAYLOAD_RESP_CTRL_THREAD_RESET = 16, + NETWORK_CTRL_PAYLOAD__PAYLOAD_CMD_CTRL_THREAD_REPROV = 17, + NETWORK_CTRL_PAYLOAD__PAYLOAD_RESP_CTRL_THREAD_REPROV = 18 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(NETWORK_CTRL_PAYLOAD__PAYLOAD__CASE) +} NetworkCtrlPayload__PayloadCase; + +struct NetworkCtrlPayload +{ + ProtobufCMessage base; + NetworkCtrlMsgType msg; + Status status; + NetworkCtrlPayload__PayloadCase payload_case; + union { + CmdCtrlWifiReset *cmd_ctrl_wifi_reset; + RespCtrlWifiReset *resp_ctrl_wifi_reset; + CmdCtrlWifiReprov *cmd_ctrl_wifi_reprov; + RespCtrlWifiReprov *resp_ctrl_wifi_reprov; + CmdCtrlThreadReset *cmd_ctrl_thread_reset; + RespCtrlThreadReset *resp_ctrl_thread_reset; + CmdCtrlThreadReprov *cmd_ctrl_thread_reprov; + RespCtrlThreadReprov *resp_ctrl_thread_reprov; + }; +}; +#define NETWORK_CTRL_PAYLOAD__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&network_ctrl_payload__descriptor) \ + , NETWORK_CTRL_MSG_TYPE__TypeCtrlReserved, STATUS__Success, NETWORK_CTRL_PAYLOAD__PAYLOAD__NOT_SET, {0} } + + +/* CmdCtrlWifiReset methods */ +void cmd_ctrl_wifi_reset__init + (CmdCtrlWifiReset *message); +size_t cmd_ctrl_wifi_reset__get_packed_size + (const CmdCtrlWifiReset *message); +size_t cmd_ctrl_wifi_reset__pack + (const CmdCtrlWifiReset *message, + uint8_t *out); +size_t cmd_ctrl_wifi_reset__pack_to_buffer + (const CmdCtrlWifiReset *message, + ProtobufCBuffer *buffer); +CmdCtrlWifiReset * + cmd_ctrl_wifi_reset__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_ctrl_wifi_reset__free_unpacked + (CmdCtrlWifiReset *message, + ProtobufCAllocator *allocator); +/* RespCtrlWifiReset methods */ +void resp_ctrl_wifi_reset__init + (RespCtrlWifiReset *message); +size_t resp_ctrl_wifi_reset__get_packed_size + (const RespCtrlWifiReset *message); +size_t resp_ctrl_wifi_reset__pack + (const RespCtrlWifiReset *message, + uint8_t *out); +size_t resp_ctrl_wifi_reset__pack_to_buffer + (const RespCtrlWifiReset *message, + ProtobufCBuffer *buffer); +RespCtrlWifiReset * + resp_ctrl_wifi_reset__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_ctrl_wifi_reset__free_unpacked + (RespCtrlWifiReset *message, + ProtobufCAllocator *allocator); +/* CmdCtrlWifiReprov methods */ +void cmd_ctrl_wifi_reprov__init + (CmdCtrlWifiReprov *message); +size_t cmd_ctrl_wifi_reprov__get_packed_size + (const CmdCtrlWifiReprov *message); +size_t cmd_ctrl_wifi_reprov__pack + (const CmdCtrlWifiReprov *message, + uint8_t *out); +size_t cmd_ctrl_wifi_reprov__pack_to_buffer + (const CmdCtrlWifiReprov *message, + ProtobufCBuffer *buffer); +CmdCtrlWifiReprov * + cmd_ctrl_wifi_reprov__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_ctrl_wifi_reprov__free_unpacked + (CmdCtrlWifiReprov *message, + ProtobufCAllocator *allocator); +/* RespCtrlWifiReprov methods */ +void resp_ctrl_wifi_reprov__init + (RespCtrlWifiReprov *message); +size_t resp_ctrl_wifi_reprov__get_packed_size + (const RespCtrlWifiReprov *message); +size_t resp_ctrl_wifi_reprov__pack + (const RespCtrlWifiReprov *message, + uint8_t *out); +size_t resp_ctrl_wifi_reprov__pack_to_buffer + (const RespCtrlWifiReprov *message, + ProtobufCBuffer *buffer); +RespCtrlWifiReprov * + resp_ctrl_wifi_reprov__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_ctrl_wifi_reprov__free_unpacked + (RespCtrlWifiReprov *message, + ProtobufCAllocator *allocator); +/* CmdCtrlThreadReset methods */ +void cmd_ctrl_thread_reset__init + (CmdCtrlThreadReset *message); +size_t cmd_ctrl_thread_reset__get_packed_size + (const CmdCtrlThreadReset *message); +size_t cmd_ctrl_thread_reset__pack + (const CmdCtrlThreadReset *message, + uint8_t *out); +size_t cmd_ctrl_thread_reset__pack_to_buffer + (const CmdCtrlThreadReset *message, + ProtobufCBuffer *buffer); +CmdCtrlThreadReset * + cmd_ctrl_thread_reset__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_ctrl_thread_reset__free_unpacked + (CmdCtrlThreadReset *message, + ProtobufCAllocator *allocator); +/* RespCtrlThreadReset methods */ +void resp_ctrl_thread_reset__init + (RespCtrlThreadReset *message); +size_t resp_ctrl_thread_reset__get_packed_size + (const RespCtrlThreadReset *message); +size_t resp_ctrl_thread_reset__pack + (const RespCtrlThreadReset *message, + uint8_t *out); +size_t resp_ctrl_thread_reset__pack_to_buffer + (const RespCtrlThreadReset *message, + ProtobufCBuffer *buffer); +RespCtrlThreadReset * + resp_ctrl_thread_reset__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_ctrl_thread_reset__free_unpacked + (RespCtrlThreadReset *message, + ProtobufCAllocator *allocator); +/* CmdCtrlThreadReprov methods */ +void cmd_ctrl_thread_reprov__init + (CmdCtrlThreadReprov *message); +size_t cmd_ctrl_thread_reprov__get_packed_size + (const CmdCtrlThreadReprov *message); +size_t cmd_ctrl_thread_reprov__pack + (const CmdCtrlThreadReprov *message, + uint8_t *out); +size_t cmd_ctrl_thread_reprov__pack_to_buffer + (const CmdCtrlThreadReprov *message, + ProtobufCBuffer *buffer); +CmdCtrlThreadReprov * + cmd_ctrl_thread_reprov__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_ctrl_thread_reprov__free_unpacked + (CmdCtrlThreadReprov *message, + ProtobufCAllocator *allocator); +/* RespCtrlThreadReprov methods */ +void resp_ctrl_thread_reprov__init + (RespCtrlThreadReprov *message); +size_t resp_ctrl_thread_reprov__get_packed_size + (const RespCtrlThreadReprov *message); +size_t resp_ctrl_thread_reprov__pack + (const RespCtrlThreadReprov *message, + uint8_t *out); +size_t resp_ctrl_thread_reprov__pack_to_buffer + (const RespCtrlThreadReprov *message, + ProtobufCBuffer *buffer); +RespCtrlThreadReprov * + resp_ctrl_thread_reprov__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_ctrl_thread_reprov__free_unpacked + (RespCtrlThreadReprov *message, + ProtobufCAllocator *allocator); +/* NetworkCtrlPayload methods */ +void network_ctrl_payload__init + (NetworkCtrlPayload *message); +size_t network_ctrl_payload__get_packed_size + (const NetworkCtrlPayload *message); +size_t network_ctrl_payload__pack + (const NetworkCtrlPayload *message, + uint8_t *out); +size_t network_ctrl_payload__pack_to_buffer + (const NetworkCtrlPayload *message, + ProtobufCBuffer *buffer); +NetworkCtrlPayload * + network_ctrl_payload__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void network_ctrl_payload__free_unpacked + (NetworkCtrlPayload *message, + ProtobufCAllocator *allocator); +/* --- per-message closures --- */ + +typedef void (*CmdCtrlWifiReset_Closure) + (const CmdCtrlWifiReset *message, + void *closure_data); +typedef void (*RespCtrlWifiReset_Closure) + (const RespCtrlWifiReset *message, + void *closure_data); +typedef void (*CmdCtrlWifiReprov_Closure) + (const CmdCtrlWifiReprov *message, + void *closure_data); +typedef void (*RespCtrlWifiReprov_Closure) + (const RespCtrlWifiReprov *message, + void *closure_data); +typedef void (*CmdCtrlThreadReset_Closure) + (const CmdCtrlThreadReset *message, + void *closure_data); +typedef void (*RespCtrlThreadReset_Closure) + (const RespCtrlThreadReset *message, + void *closure_data); +typedef void (*CmdCtrlThreadReprov_Closure) + (const CmdCtrlThreadReprov *message, + void *closure_data); +typedef void (*RespCtrlThreadReprov_Closure) + (const RespCtrlThreadReprov *message, + void *closure_data); +typedef void (*NetworkCtrlPayload_Closure) + (const NetworkCtrlPayload *message, + void *closure_data); + +/* --- services --- */ + + +/* --- descriptors --- */ + +extern const ProtobufCEnumDescriptor network_ctrl_msg_type__descriptor; +extern const ProtobufCMessageDescriptor cmd_ctrl_wifi_reset__descriptor; +extern const ProtobufCMessageDescriptor resp_ctrl_wifi_reset__descriptor; +extern const ProtobufCMessageDescriptor cmd_ctrl_wifi_reprov__descriptor; +extern const ProtobufCMessageDescriptor resp_ctrl_wifi_reprov__descriptor; +extern const ProtobufCMessageDescriptor cmd_ctrl_thread_reset__descriptor; +extern const ProtobufCMessageDescriptor resp_ctrl_thread_reset__descriptor; +extern const ProtobufCMessageDescriptor cmd_ctrl_thread_reprov__descriptor; +extern const ProtobufCMessageDescriptor resp_ctrl_thread_reprov__descriptor; +extern const ProtobufCMessageDescriptor network_ctrl_payload__descriptor; + +PROTOBUF_C__END_DECLS + + +#endif /* PROTOBUF_C_network_5fctrl_2eproto__INCLUDED */ diff --git a/components/network_provisioning/proto-c/network_scan.pb-c.c b/components/network_provisioning/proto-c/network_scan.pb-c.c new file mode 100644 index 0000000..0b2a020 --- /dev/null +++ b/components/network_provisioning/proto-c/network_scan.pb-c.c @@ -0,0 +1,1626 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: network_scan.proto */ + +/* Do not generate deprecated warnings for self */ +#ifndef PROTOBUF_C__NO_DEPRECATED +#define PROTOBUF_C__NO_DEPRECATED +#endif + +#include "network_scan.pb-c.h" +void cmd_scan_wifi_start__init + (CmdScanWifiStart *message) +{ + static const CmdScanWifiStart init_value = CMD_SCAN_WIFI_START__INIT; + *message = init_value; +} +size_t cmd_scan_wifi_start__get_packed_size + (const CmdScanWifiStart *message) +{ + assert(message->base.descriptor == &cmd_scan_wifi_start__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_scan_wifi_start__pack + (const CmdScanWifiStart *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_scan_wifi_start__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_scan_wifi_start__pack_to_buffer + (const CmdScanWifiStart *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_scan_wifi_start__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdScanWifiStart * + cmd_scan_wifi_start__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdScanWifiStart *) + protobuf_c_message_unpack (&cmd_scan_wifi_start__descriptor, + allocator, len, data); +} +void cmd_scan_wifi_start__free_unpacked + (CmdScanWifiStart *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_scan_wifi_start__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_scan_thread_start__init + (CmdScanThreadStart *message) +{ + static const CmdScanThreadStart init_value = CMD_SCAN_THREAD_START__INIT; + *message = init_value; +} +size_t cmd_scan_thread_start__get_packed_size + (const CmdScanThreadStart *message) +{ + assert(message->base.descriptor == &cmd_scan_thread_start__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_scan_thread_start__pack + (const CmdScanThreadStart *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_scan_thread_start__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_scan_thread_start__pack_to_buffer + (const CmdScanThreadStart *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_scan_thread_start__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdScanThreadStart * + cmd_scan_thread_start__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdScanThreadStart *) + protobuf_c_message_unpack (&cmd_scan_thread_start__descriptor, + allocator, len, data); +} +void cmd_scan_thread_start__free_unpacked + (CmdScanThreadStart *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_scan_thread_start__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_scan_wifi_start__init + (RespScanWifiStart *message) +{ + static const RespScanWifiStart init_value = RESP_SCAN_WIFI_START__INIT; + *message = init_value; +} +size_t resp_scan_wifi_start__get_packed_size + (const RespScanWifiStart *message) +{ + assert(message->base.descriptor == &resp_scan_wifi_start__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_scan_wifi_start__pack + (const RespScanWifiStart *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_scan_wifi_start__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_scan_wifi_start__pack_to_buffer + (const RespScanWifiStart *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_scan_wifi_start__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespScanWifiStart * + resp_scan_wifi_start__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespScanWifiStart *) + protobuf_c_message_unpack (&resp_scan_wifi_start__descriptor, + allocator, len, data); +} +void resp_scan_wifi_start__free_unpacked + (RespScanWifiStart *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_scan_wifi_start__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_scan_thread_start__init + (RespScanThreadStart *message) +{ + static const RespScanThreadStart init_value = RESP_SCAN_THREAD_START__INIT; + *message = init_value; +} +size_t resp_scan_thread_start__get_packed_size + (const RespScanThreadStart *message) +{ + assert(message->base.descriptor == &resp_scan_thread_start__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_scan_thread_start__pack + (const RespScanThreadStart *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_scan_thread_start__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_scan_thread_start__pack_to_buffer + (const RespScanThreadStart *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_scan_thread_start__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespScanThreadStart * + resp_scan_thread_start__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespScanThreadStart *) + protobuf_c_message_unpack (&resp_scan_thread_start__descriptor, + allocator, len, data); +} +void resp_scan_thread_start__free_unpacked + (RespScanThreadStart *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_scan_thread_start__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_scan_wifi_status__init + (CmdScanWifiStatus *message) +{ + static const CmdScanWifiStatus init_value = CMD_SCAN_WIFI_STATUS__INIT; + *message = init_value; +} +size_t cmd_scan_wifi_status__get_packed_size + (const CmdScanWifiStatus *message) +{ + assert(message->base.descriptor == &cmd_scan_wifi_status__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_scan_wifi_status__pack + (const CmdScanWifiStatus *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_scan_wifi_status__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_scan_wifi_status__pack_to_buffer + (const CmdScanWifiStatus *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_scan_wifi_status__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdScanWifiStatus * + cmd_scan_wifi_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdScanWifiStatus *) + protobuf_c_message_unpack (&cmd_scan_wifi_status__descriptor, + allocator, len, data); +} +void cmd_scan_wifi_status__free_unpacked + (CmdScanWifiStatus *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_scan_wifi_status__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_scan_thread_status__init + (CmdScanThreadStatus *message) +{ + static const CmdScanThreadStatus init_value = CMD_SCAN_THREAD_STATUS__INIT; + *message = init_value; +} +size_t cmd_scan_thread_status__get_packed_size + (const CmdScanThreadStatus *message) +{ + assert(message->base.descriptor == &cmd_scan_thread_status__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_scan_thread_status__pack + (const CmdScanThreadStatus *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_scan_thread_status__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_scan_thread_status__pack_to_buffer + (const CmdScanThreadStatus *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_scan_thread_status__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdScanThreadStatus * + cmd_scan_thread_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdScanThreadStatus *) + protobuf_c_message_unpack (&cmd_scan_thread_status__descriptor, + allocator, len, data); +} +void cmd_scan_thread_status__free_unpacked + (CmdScanThreadStatus *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_scan_thread_status__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_scan_wifi_status__init + (RespScanWifiStatus *message) +{ + static const RespScanWifiStatus init_value = RESP_SCAN_WIFI_STATUS__INIT; + *message = init_value; +} +size_t resp_scan_wifi_status__get_packed_size + (const RespScanWifiStatus *message) +{ + assert(message->base.descriptor == &resp_scan_wifi_status__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_scan_wifi_status__pack + (const RespScanWifiStatus *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_scan_wifi_status__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_scan_wifi_status__pack_to_buffer + (const RespScanWifiStatus *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_scan_wifi_status__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespScanWifiStatus * + resp_scan_wifi_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespScanWifiStatus *) + protobuf_c_message_unpack (&resp_scan_wifi_status__descriptor, + allocator, len, data); +} +void resp_scan_wifi_status__free_unpacked + (RespScanWifiStatus *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_scan_wifi_status__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_scan_thread_status__init + (RespScanThreadStatus *message) +{ + static const RespScanThreadStatus init_value = RESP_SCAN_THREAD_STATUS__INIT; + *message = init_value; +} +size_t resp_scan_thread_status__get_packed_size + (const RespScanThreadStatus *message) +{ + assert(message->base.descriptor == &resp_scan_thread_status__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_scan_thread_status__pack + (const RespScanThreadStatus *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_scan_thread_status__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_scan_thread_status__pack_to_buffer + (const RespScanThreadStatus *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_scan_thread_status__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespScanThreadStatus * + resp_scan_thread_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespScanThreadStatus *) + protobuf_c_message_unpack (&resp_scan_thread_status__descriptor, + allocator, len, data); +} +void resp_scan_thread_status__free_unpacked + (RespScanThreadStatus *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_scan_thread_status__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_scan_wifi_result__init + (CmdScanWifiResult *message) +{ + static const CmdScanWifiResult init_value = CMD_SCAN_WIFI_RESULT__INIT; + *message = init_value; +} +size_t cmd_scan_wifi_result__get_packed_size + (const CmdScanWifiResult *message) +{ + assert(message->base.descriptor == &cmd_scan_wifi_result__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_scan_wifi_result__pack + (const CmdScanWifiResult *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_scan_wifi_result__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_scan_wifi_result__pack_to_buffer + (const CmdScanWifiResult *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_scan_wifi_result__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdScanWifiResult * + cmd_scan_wifi_result__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdScanWifiResult *) + protobuf_c_message_unpack (&cmd_scan_wifi_result__descriptor, + allocator, len, data); +} +void cmd_scan_wifi_result__free_unpacked + (CmdScanWifiResult *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_scan_wifi_result__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_scan_thread_result__init + (CmdScanThreadResult *message) +{ + static const CmdScanThreadResult init_value = CMD_SCAN_THREAD_RESULT__INIT; + *message = init_value; +} +size_t cmd_scan_thread_result__get_packed_size + (const CmdScanThreadResult *message) +{ + assert(message->base.descriptor == &cmd_scan_thread_result__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_scan_thread_result__pack + (const CmdScanThreadResult *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_scan_thread_result__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_scan_thread_result__pack_to_buffer + (const CmdScanThreadResult *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_scan_thread_result__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdScanThreadResult * + cmd_scan_thread_result__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdScanThreadResult *) + protobuf_c_message_unpack (&cmd_scan_thread_result__descriptor, + allocator, len, data); +} +void cmd_scan_thread_result__free_unpacked + (CmdScanThreadResult *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_scan_thread_result__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void wi_fi_scan_result__init + (WiFiScanResult *message) +{ + static const WiFiScanResult init_value = WI_FI_SCAN_RESULT__INIT; + *message = init_value; +} +size_t wi_fi_scan_result__get_packed_size + (const WiFiScanResult *message) +{ + assert(message->base.descriptor == &wi_fi_scan_result__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t wi_fi_scan_result__pack + (const WiFiScanResult *message, + uint8_t *out) +{ + assert(message->base.descriptor == &wi_fi_scan_result__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t wi_fi_scan_result__pack_to_buffer + (const WiFiScanResult *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &wi_fi_scan_result__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +WiFiScanResult * + wi_fi_scan_result__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (WiFiScanResult *) + protobuf_c_message_unpack (&wi_fi_scan_result__descriptor, + allocator, len, data); +} +void wi_fi_scan_result__free_unpacked + (WiFiScanResult *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &wi_fi_scan_result__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void thread_scan_result__init + (ThreadScanResult *message) +{ + static const ThreadScanResult init_value = THREAD_SCAN_RESULT__INIT; + *message = init_value; +} +size_t thread_scan_result__get_packed_size + (const ThreadScanResult *message) +{ + assert(message->base.descriptor == &thread_scan_result__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t thread_scan_result__pack + (const ThreadScanResult *message, + uint8_t *out) +{ + assert(message->base.descriptor == &thread_scan_result__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t thread_scan_result__pack_to_buffer + (const ThreadScanResult *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &thread_scan_result__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +ThreadScanResult * + thread_scan_result__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (ThreadScanResult *) + protobuf_c_message_unpack (&thread_scan_result__descriptor, + allocator, len, data); +} +void thread_scan_result__free_unpacked + (ThreadScanResult *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &thread_scan_result__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_scan_wifi_result__init + (RespScanWifiResult *message) +{ + static const RespScanWifiResult init_value = RESP_SCAN_WIFI_RESULT__INIT; + *message = init_value; +} +size_t resp_scan_wifi_result__get_packed_size + (const RespScanWifiResult *message) +{ + assert(message->base.descriptor == &resp_scan_wifi_result__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_scan_wifi_result__pack + (const RespScanWifiResult *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_scan_wifi_result__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_scan_wifi_result__pack_to_buffer + (const RespScanWifiResult *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_scan_wifi_result__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespScanWifiResult * + resp_scan_wifi_result__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespScanWifiResult *) + protobuf_c_message_unpack (&resp_scan_wifi_result__descriptor, + allocator, len, data); +} +void resp_scan_wifi_result__free_unpacked + (RespScanWifiResult *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_scan_wifi_result__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_scan_thread_result__init + (RespScanThreadResult *message) +{ + static const RespScanThreadResult init_value = RESP_SCAN_THREAD_RESULT__INIT; + *message = init_value; +} +size_t resp_scan_thread_result__get_packed_size + (const RespScanThreadResult *message) +{ + assert(message->base.descriptor == &resp_scan_thread_result__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_scan_thread_result__pack + (const RespScanThreadResult *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_scan_thread_result__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_scan_thread_result__pack_to_buffer + (const RespScanThreadResult *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_scan_thread_result__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespScanThreadResult * + resp_scan_thread_result__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespScanThreadResult *) + protobuf_c_message_unpack (&resp_scan_thread_result__descriptor, + allocator, len, data); +} +void resp_scan_thread_result__free_unpacked + (RespScanThreadResult *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_scan_thread_result__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void network_scan_payload__init + (NetworkScanPayload *message) +{ + static const NetworkScanPayload init_value = NETWORK_SCAN_PAYLOAD__INIT; + *message = init_value; +} +size_t network_scan_payload__get_packed_size + (const NetworkScanPayload *message) +{ + assert(message->base.descriptor == &network_scan_payload__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t network_scan_payload__pack + (const NetworkScanPayload *message, + uint8_t *out) +{ + assert(message->base.descriptor == &network_scan_payload__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t network_scan_payload__pack_to_buffer + (const NetworkScanPayload *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &network_scan_payload__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +NetworkScanPayload * + network_scan_payload__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (NetworkScanPayload *) + protobuf_c_message_unpack (&network_scan_payload__descriptor, + allocator, len, data); +} +void network_scan_payload__free_unpacked + (NetworkScanPayload *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &network_scan_payload__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +static const ProtobufCFieldDescriptor cmd_scan_wifi_start__field_descriptors[4] = +{ + { + "blocking", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BOOL, + 0, /* quantifier_offset */ + offsetof(CmdScanWifiStart, blocking), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "passive", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BOOL, + 0, /* quantifier_offset */ + offsetof(CmdScanWifiStart, passive), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "group_channels", + 3, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(CmdScanWifiStart, group_channels), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "period_ms", + 4, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(CmdScanWifiStart, period_ms), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned cmd_scan_wifi_start__field_indices_by_name[] = { + 0, /* field[0] = blocking */ + 2, /* field[2] = group_channels */ + 1, /* field[1] = passive */ + 3, /* field[3] = period_ms */ +}; +static const ProtobufCIntRange cmd_scan_wifi_start__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 4 } +}; +const ProtobufCMessageDescriptor cmd_scan_wifi_start__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdScanWifiStart", + "CmdScanWifiStart", + "CmdScanWifiStart", + "", + sizeof(CmdScanWifiStart), + 4, + cmd_scan_wifi_start__field_descriptors, + cmd_scan_wifi_start__field_indices_by_name, + 1, cmd_scan_wifi_start__number_ranges, + (ProtobufCMessageInit) cmd_scan_wifi_start__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor cmd_scan_thread_start__field_descriptors[2] = +{ + { + "blocking", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BOOL, + 0, /* quantifier_offset */ + offsetof(CmdScanThreadStart, blocking), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "channel_mask", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(CmdScanThreadStart, channel_mask), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned cmd_scan_thread_start__field_indices_by_name[] = { + 0, /* field[0] = blocking */ + 1, /* field[1] = channel_mask */ +}; +static const ProtobufCIntRange cmd_scan_thread_start__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor cmd_scan_thread_start__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdScanThreadStart", + "CmdScanThreadStart", + "CmdScanThreadStart", + "", + sizeof(CmdScanThreadStart), + 2, + cmd_scan_thread_start__field_descriptors, + cmd_scan_thread_start__field_indices_by_name, + 1, cmd_scan_thread_start__number_ranges, + (ProtobufCMessageInit) cmd_scan_thread_start__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define resp_scan_wifi_start__field_descriptors NULL +#define resp_scan_wifi_start__field_indices_by_name NULL +#define resp_scan_wifi_start__number_ranges NULL +const ProtobufCMessageDescriptor resp_scan_wifi_start__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespScanWifiStart", + "RespScanWifiStart", + "RespScanWifiStart", + "", + sizeof(RespScanWifiStart), + 0, + resp_scan_wifi_start__field_descriptors, + resp_scan_wifi_start__field_indices_by_name, + 0, resp_scan_wifi_start__number_ranges, + (ProtobufCMessageInit) resp_scan_wifi_start__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define resp_scan_thread_start__field_descriptors NULL +#define resp_scan_thread_start__field_indices_by_name NULL +#define resp_scan_thread_start__number_ranges NULL +const ProtobufCMessageDescriptor resp_scan_thread_start__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespScanThreadStart", + "RespScanThreadStart", + "RespScanThreadStart", + "", + sizeof(RespScanThreadStart), + 0, + resp_scan_thread_start__field_descriptors, + resp_scan_thread_start__field_indices_by_name, + 0, resp_scan_thread_start__number_ranges, + (ProtobufCMessageInit) resp_scan_thread_start__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define cmd_scan_wifi_status__field_descriptors NULL +#define cmd_scan_wifi_status__field_indices_by_name NULL +#define cmd_scan_wifi_status__number_ranges NULL +const ProtobufCMessageDescriptor cmd_scan_wifi_status__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdScanWifiStatus", + "CmdScanWifiStatus", + "CmdScanWifiStatus", + "", + sizeof(CmdScanWifiStatus), + 0, + cmd_scan_wifi_status__field_descriptors, + cmd_scan_wifi_status__field_indices_by_name, + 0, cmd_scan_wifi_status__number_ranges, + (ProtobufCMessageInit) cmd_scan_wifi_status__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define cmd_scan_thread_status__field_descriptors NULL +#define cmd_scan_thread_status__field_indices_by_name NULL +#define cmd_scan_thread_status__number_ranges NULL +const ProtobufCMessageDescriptor cmd_scan_thread_status__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdScanThreadStatus", + "CmdScanThreadStatus", + "CmdScanThreadStatus", + "", + sizeof(CmdScanThreadStatus), + 0, + cmd_scan_thread_status__field_descriptors, + cmd_scan_thread_status__field_indices_by_name, + 0, cmd_scan_thread_status__number_ranges, + (ProtobufCMessageInit) cmd_scan_thread_status__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor resp_scan_wifi_status__field_descriptors[2] = +{ + { + "scan_finished", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BOOL, + 0, /* quantifier_offset */ + offsetof(RespScanWifiStatus, scan_finished), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "result_count", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(RespScanWifiStatus, result_count), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned resp_scan_wifi_status__field_indices_by_name[] = { + 1, /* field[1] = result_count */ + 0, /* field[0] = scan_finished */ +}; +static const ProtobufCIntRange resp_scan_wifi_status__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor resp_scan_wifi_status__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespScanWifiStatus", + "RespScanWifiStatus", + "RespScanWifiStatus", + "", + sizeof(RespScanWifiStatus), + 2, + resp_scan_wifi_status__field_descriptors, + resp_scan_wifi_status__field_indices_by_name, + 1, resp_scan_wifi_status__number_ranges, + (ProtobufCMessageInit) resp_scan_wifi_status__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor resp_scan_thread_status__field_descriptors[2] = +{ + { + "scan_finished", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BOOL, + 0, /* quantifier_offset */ + offsetof(RespScanThreadStatus, scan_finished), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "result_count", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(RespScanThreadStatus, result_count), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned resp_scan_thread_status__field_indices_by_name[] = { + 1, /* field[1] = result_count */ + 0, /* field[0] = scan_finished */ +}; +static const ProtobufCIntRange resp_scan_thread_status__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor resp_scan_thread_status__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespScanThreadStatus", + "RespScanThreadStatus", + "RespScanThreadStatus", + "", + sizeof(RespScanThreadStatus), + 2, + resp_scan_thread_status__field_descriptors, + resp_scan_thread_status__field_indices_by_name, + 1, resp_scan_thread_status__number_ranges, + (ProtobufCMessageInit) resp_scan_thread_status__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor cmd_scan_wifi_result__field_descriptors[2] = +{ + { + "start_index", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(CmdScanWifiResult, start_index), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "count", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(CmdScanWifiResult, count), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned cmd_scan_wifi_result__field_indices_by_name[] = { + 1, /* field[1] = count */ + 0, /* field[0] = start_index */ +}; +static const ProtobufCIntRange cmd_scan_wifi_result__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor cmd_scan_wifi_result__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdScanWifiResult", + "CmdScanWifiResult", + "CmdScanWifiResult", + "", + sizeof(CmdScanWifiResult), + 2, + cmd_scan_wifi_result__field_descriptors, + cmd_scan_wifi_result__field_indices_by_name, + 1, cmd_scan_wifi_result__number_ranges, + (ProtobufCMessageInit) cmd_scan_wifi_result__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor cmd_scan_thread_result__field_descriptors[2] = +{ + { + "start_index", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(CmdScanThreadResult, start_index), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "count", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(CmdScanThreadResult, count), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned cmd_scan_thread_result__field_indices_by_name[] = { + 1, /* field[1] = count */ + 0, /* field[0] = start_index */ +}; +static const ProtobufCIntRange cmd_scan_thread_result__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor cmd_scan_thread_result__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdScanThreadResult", + "CmdScanThreadResult", + "CmdScanThreadResult", + "", + sizeof(CmdScanThreadResult), + 2, + cmd_scan_thread_result__field_descriptors, + cmd_scan_thread_result__field_indices_by_name, + 1, cmd_scan_thread_result__number_ranges, + (ProtobufCMessageInit) cmd_scan_thread_result__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor wi_fi_scan_result__field_descriptors[5] = +{ + { + "ssid", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(WiFiScanResult, ssid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "channel", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(WiFiScanResult, channel), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "rssi", + 3, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_INT32, + 0, /* quantifier_offset */ + offsetof(WiFiScanResult, rssi), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "bssid", + 4, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(WiFiScanResult, bssid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "auth", + 5, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(WiFiScanResult, auth), + &wifi_auth_mode__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned wi_fi_scan_result__field_indices_by_name[] = { + 4, /* field[4] = auth */ + 3, /* field[3] = bssid */ + 1, /* field[1] = channel */ + 2, /* field[2] = rssi */ + 0, /* field[0] = ssid */ +}; +static const ProtobufCIntRange wi_fi_scan_result__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 5 } +}; +const ProtobufCMessageDescriptor wi_fi_scan_result__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "WiFiScanResult", + "WiFiScanResult", + "WiFiScanResult", + "", + sizeof(WiFiScanResult), + 5, + wi_fi_scan_result__field_descriptors, + wi_fi_scan_result__field_indices_by_name, + 1, wi_fi_scan_result__number_ranges, + (ProtobufCMessageInit) wi_fi_scan_result__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor thread_scan_result__field_descriptors[7] = +{ + { + "pan_id", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(ThreadScanResult, pan_id), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "channel", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(ThreadScanResult, channel), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "rssi", + 3, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_INT32, + 0, /* quantifier_offset */ + offsetof(ThreadScanResult, rssi), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "lqi", + 4, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(ThreadScanResult, lqi), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "ext_addr", + 5, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(ThreadScanResult, ext_addr), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "network_name", + 6, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_STRING, + 0, /* quantifier_offset */ + offsetof(ThreadScanResult, network_name), + NULL, + &protobuf_c_empty_string, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "ext_pan_id", + 7, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(ThreadScanResult, ext_pan_id), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned thread_scan_result__field_indices_by_name[] = { + 1, /* field[1] = channel */ + 4, /* field[4] = ext_addr */ + 6, /* field[6] = ext_pan_id */ + 3, /* field[3] = lqi */ + 5, /* field[5] = network_name */ + 0, /* field[0] = pan_id */ + 2, /* field[2] = rssi */ +}; +static const ProtobufCIntRange thread_scan_result__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 7 } +}; +const ProtobufCMessageDescriptor thread_scan_result__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "ThreadScanResult", + "ThreadScanResult", + "ThreadScanResult", + "", + sizeof(ThreadScanResult), + 7, + thread_scan_result__field_descriptors, + thread_scan_result__field_indices_by_name, + 1, thread_scan_result__number_ranges, + (ProtobufCMessageInit) thread_scan_result__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor resp_scan_wifi_result__field_descriptors[1] = +{ + { + "entries", + 1, + PROTOBUF_C_LABEL_REPEATED, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(RespScanWifiResult, n_entries), + offsetof(RespScanWifiResult, entries), + &wi_fi_scan_result__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned resp_scan_wifi_result__field_indices_by_name[] = { + 0, /* field[0] = entries */ +}; +static const ProtobufCIntRange resp_scan_wifi_result__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor resp_scan_wifi_result__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespScanWifiResult", + "RespScanWifiResult", + "RespScanWifiResult", + "", + sizeof(RespScanWifiResult), + 1, + resp_scan_wifi_result__field_descriptors, + resp_scan_wifi_result__field_indices_by_name, + 1, resp_scan_wifi_result__number_ranges, + (ProtobufCMessageInit) resp_scan_wifi_result__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor resp_scan_thread_result__field_descriptors[1] = +{ + { + "entries", + 1, + PROTOBUF_C_LABEL_REPEATED, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(RespScanThreadResult, n_entries), + offsetof(RespScanThreadResult, entries), + &thread_scan_result__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned resp_scan_thread_result__field_indices_by_name[] = { + 0, /* field[0] = entries */ +}; +static const ProtobufCIntRange resp_scan_thread_result__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor resp_scan_thread_result__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespScanThreadResult", + "RespScanThreadResult", + "RespScanThreadResult", + "", + sizeof(RespScanThreadResult), + 1, + resp_scan_thread_result__field_descriptors, + resp_scan_thread_result__field_indices_by_name, + 1, resp_scan_thread_result__number_ranges, + (ProtobufCMessageInit) resp_scan_thread_result__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor network_scan_payload__field_descriptors[14] = +{ + { + "msg", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(NetworkScanPayload, msg), + &network_scan_msg_type__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "status", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(NetworkScanPayload, status), + &status__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_scan_wifi_start", + 10, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkScanPayload, payload_case), + offsetof(NetworkScanPayload, cmd_scan_wifi_start), + &cmd_scan_wifi_start__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_scan_wifi_start", + 11, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkScanPayload, payload_case), + offsetof(NetworkScanPayload, resp_scan_wifi_start), + &resp_scan_wifi_start__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_scan_wifi_status", + 12, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkScanPayload, payload_case), + offsetof(NetworkScanPayload, cmd_scan_wifi_status), + &cmd_scan_wifi_status__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_scan_wifi_status", + 13, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkScanPayload, payload_case), + offsetof(NetworkScanPayload, resp_scan_wifi_status), + &resp_scan_wifi_status__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_scan_wifi_result", + 14, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkScanPayload, payload_case), + offsetof(NetworkScanPayload, cmd_scan_wifi_result), + &cmd_scan_wifi_result__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_scan_wifi_result", + 15, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkScanPayload, payload_case), + offsetof(NetworkScanPayload, resp_scan_wifi_result), + &resp_scan_wifi_result__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_scan_thread_start", + 16, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkScanPayload, payload_case), + offsetof(NetworkScanPayload, cmd_scan_thread_start), + &cmd_scan_thread_start__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_scan_thread_start", + 17, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkScanPayload, payload_case), + offsetof(NetworkScanPayload, resp_scan_thread_start), + &resp_scan_thread_start__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_scan_thread_status", + 18, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkScanPayload, payload_case), + offsetof(NetworkScanPayload, cmd_scan_thread_status), + &cmd_scan_thread_status__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_scan_thread_status", + 19, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkScanPayload, payload_case), + offsetof(NetworkScanPayload, resp_scan_thread_status), + &resp_scan_thread_status__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_scan_thread_result", + 20, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkScanPayload, payload_case), + offsetof(NetworkScanPayload, cmd_scan_thread_result), + &cmd_scan_thread_result__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_scan_thread_result", + 21, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(NetworkScanPayload, payload_case), + offsetof(NetworkScanPayload, resp_scan_thread_result), + &resp_scan_thread_result__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned network_scan_payload__field_indices_by_name[] = { + 12, /* field[12] = cmd_scan_thread_result */ + 8, /* field[8] = cmd_scan_thread_start */ + 10, /* field[10] = cmd_scan_thread_status */ + 6, /* field[6] = cmd_scan_wifi_result */ + 2, /* field[2] = cmd_scan_wifi_start */ + 4, /* field[4] = cmd_scan_wifi_status */ + 0, /* field[0] = msg */ + 13, /* field[13] = resp_scan_thread_result */ + 9, /* field[9] = resp_scan_thread_start */ + 11, /* field[11] = resp_scan_thread_status */ + 7, /* field[7] = resp_scan_wifi_result */ + 3, /* field[3] = resp_scan_wifi_start */ + 5, /* field[5] = resp_scan_wifi_status */ + 1, /* field[1] = status */ +}; +static const ProtobufCIntRange network_scan_payload__number_ranges[2 + 1] = +{ + { 1, 0 }, + { 10, 2 }, + { 0, 14 } +}; +const ProtobufCMessageDescriptor network_scan_payload__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "NetworkScanPayload", + "NetworkScanPayload", + "NetworkScanPayload", + "", + sizeof(NetworkScanPayload), + 14, + network_scan_payload__field_descriptors, + network_scan_payload__field_indices_by_name, + 2, network_scan_payload__number_ranges, + (ProtobufCMessageInit) network_scan_payload__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCEnumValue network_scan_msg_type__enum_values_by_number[12] = +{ + { "TypeCmdScanWifiStart", "NETWORK_SCAN_MSG_TYPE__TypeCmdScanWifiStart", 0 }, + { "TypeRespScanWifiStart", "NETWORK_SCAN_MSG_TYPE__TypeRespScanWifiStart", 1 }, + { "TypeCmdScanWifiStatus", "NETWORK_SCAN_MSG_TYPE__TypeCmdScanWifiStatus", 2 }, + { "TypeRespScanWifiStatus", "NETWORK_SCAN_MSG_TYPE__TypeRespScanWifiStatus", 3 }, + { "TypeCmdScanWifiResult", "NETWORK_SCAN_MSG_TYPE__TypeCmdScanWifiResult", 4 }, + { "TypeRespScanWifiResult", "NETWORK_SCAN_MSG_TYPE__TypeRespScanWifiResult", 5 }, + { "TypeCmdScanThreadStart", "NETWORK_SCAN_MSG_TYPE__TypeCmdScanThreadStart", 6 }, + { "TypeRespScanThreadStart", "NETWORK_SCAN_MSG_TYPE__TypeRespScanThreadStart", 7 }, + { "TypeCmdScanThreadStatus", "NETWORK_SCAN_MSG_TYPE__TypeCmdScanThreadStatus", 8 }, + { "TypeRespScanThreadStatus", "NETWORK_SCAN_MSG_TYPE__TypeRespScanThreadStatus", 9 }, + { "TypeCmdScanThreadResult", "NETWORK_SCAN_MSG_TYPE__TypeCmdScanThreadResult", 10 }, + { "TypeRespScanThreadResult", "NETWORK_SCAN_MSG_TYPE__TypeRespScanThreadResult", 11 }, +}; +static const ProtobufCIntRange network_scan_msg_type__value_ranges[] = { +{0, 0},{0, 12} +}; +static const ProtobufCEnumValueIndex network_scan_msg_type__enum_values_by_name[12] = +{ + { "TypeCmdScanThreadResult", 10 }, + { "TypeCmdScanThreadStart", 6 }, + { "TypeCmdScanThreadStatus", 8 }, + { "TypeCmdScanWifiResult", 4 }, + { "TypeCmdScanWifiStart", 0 }, + { "TypeCmdScanWifiStatus", 2 }, + { "TypeRespScanThreadResult", 11 }, + { "TypeRespScanThreadStart", 7 }, + { "TypeRespScanThreadStatus", 9 }, + { "TypeRespScanWifiResult", 5 }, + { "TypeRespScanWifiStart", 1 }, + { "TypeRespScanWifiStatus", 3 }, +}; +const ProtobufCEnumDescriptor network_scan_msg_type__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "NetworkScanMsgType", + "NetworkScanMsgType", + "NetworkScanMsgType", + "", + 12, + network_scan_msg_type__enum_values_by_number, + 12, + network_scan_msg_type__enum_values_by_name, + 1, + network_scan_msg_type__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; diff --git a/components/network_provisioning/proto-c/network_scan.pb-c.h b/components/network_provisioning/proto-c/network_scan.pb-c.h new file mode 100644 index 0000000..37c79b9 --- /dev/null +++ b/components/network_provisioning/proto-c/network_scan.pb-c.h @@ -0,0 +1,614 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: network_scan.proto */ + +#ifndef PROTOBUF_C_network_5fscan_2eproto__INCLUDED +#define PROTOBUF_C_network_5fscan_2eproto__INCLUDED + +#include + +PROTOBUF_C__BEGIN_DECLS + +#if PROTOBUF_C_VERSION_NUMBER < 1003000 +# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. +#elif 1004000 < PROTOBUF_C_MIN_COMPILER_VERSION +# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. +#endif + +#include "constants.pb-c.h" +#include "network_constants.pb-c.h" + +typedef struct CmdScanWifiStart CmdScanWifiStart; +typedef struct CmdScanThreadStart CmdScanThreadStart; +typedef struct RespScanWifiStart RespScanWifiStart; +typedef struct RespScanThreadStart RespScanThreadStart; +typedef struct CmdScanWifiStatus CmdScanWifiStatus; +typedef struct CmdScanThreadStatus CmdScanThreadStatus; +typedef struct RespScanWifiStatus RespScanWifiStatus; +typedef struct RespScanThreadStatus RespScanThreadStatus; +typedef struct CmdScanWifiResult CmdScanWifiResult; +typedef struct CmdScanThreadResult CmdScanThreadResult; +typedef struct WiFiScanResult WiFiScanResult; +typedef struct ThreadScanResult ThreadScanResult; +typedef struct RespScanWifiResult RespScanWifiResult; +typedef struct RespScanThreadResult RespScanThreadResult; +typedef struct NetworkScanPayload NetworkScanPayload; + + +/* --- enums --- */ + +typedef enum _NetworkScanMsgType { + NETWORK_SCAN_MSG_TYPE__TypeCmdScanWifiStart = 0, + NETWORK_SCAN_MSG_TYPE__TypeRespScanWifiStart = 1, + NETWORK_SCAN_MSG_TYPE__TypeCmdScanWifiStatus = 2, + NETWORK_SCAN_MSG_TYPE__TypeRespScanWifiStatus = 3, + NETWORK_SCAN_MSG_TYPE__TypeCmdScanWifiResult = 4, + NETWORK_SCAN_MSG_TYPE__TypeRespScanWifiResult = 5, + NETWORK_SCAN_MSG_TYPE__TypeCmdScanThreadStart = 6, + NETWORK_SCAN_MSG_TYPE__TypeRespScanThreadStart = 7, + NETWORK_SCAN_MSG_TYPE__TypeCmdScanThreadStatus = 8, + NETWORK_SCAN_MSG_TYPE__TypeRespScanThreadStatus = 9, + NETWORK_SCAN_MSG_TYPE__TypeCmdScanThreadResult = 10, + NETWORK_SCAN_MSG_TYPE__TypeRespScanThreadResult = 11 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(NETWORK_SCAN_MSG_TYPE) +} NetworkScanMsgType; + +/* --- messages --- */ + +struct CmdScanWifiStart +{ + ProtobufCMessage base; + protobuf_c_boolean blocking; + protobuf_c_boolean passive; + uint32_t group_channels; + uint32_t period_ms; +}; +#define CMD_SCAN_WIFI_START__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_scan_wifi_start__descriptor) \ + , 0, 0, 0, 0 } + + +struct CmdScanThreadStart +{ + ProtobufCMessage base; + protobuf_c_boolean blocking; + uint32_t channel_mask; +}; +#define CMD_SCAN_THREAD_START__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_scan_thread_start__descriptor) \ + , 0, 0 } + + +struct RespScanWifiStart +{ + ProtobufCMessage base; +}; +#define RESP_SCAN_WIFI_START__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_scan_wifi_start__descriptor) \ + } + + +struct RespScanThreadStart +{ + ProtobufCMessage base; +}; +#define RESP_SCAN_THREAD_START__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_scan_thread_start__descriptor) \ + } + + +struct CmdScanWifiStatus +{ + ProtobufCMessage base; +}; +#define CMD_SCAN_WIFI_STATUS__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_scan_wifi_status__descriptor) \ + } + + +struct CmdScanThreadStatus +{ + ProtobufCMessage base; +}; +#define CMD_SCAN_THREAD_STATUS__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_scan_thread_status__descriptor) \ + } + + +struct RespScanWifiStatus +{ + ProtobufCMessage base; + protobuf_c_boolean scan_finished; + uint32_t result_count; +}; +#define RESP_SCAN_WIFI_STATUS__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_scan_wifi_status__descriptor) \ + , 0, 0 } + + +struct RespScanThreadStatus +{ + ProtobufCMessage base; + protobuf_c_boolean scan_finished; + uint32_t result_count; +}; +#define RESP_SCAN_THREAD_STATUS__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_scan_thread_status__descriptor) \ + , 0, 0 } + + +struct CmdScanWifiResult +{ + ProtobufCMessage base; + uint32_t start_index; + uint32_t count; +}; +#define CMD_SCAN_WIFI_RESULT__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_scan_wifi_result__descriptor) \ + , 0, 0 } + + +struct CmdScanThreadResult +{ + ProtobufCMessage base; + uint32_t start_index; + uint32_t count; +}; +#define CMD_SCAN_THREAD_RESULT__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_scan_thread_result__descriptor) \ + , 0, 0 } + + +struct WiFiScanResult +{ + ProtobufCMessage base; + ProtobufCBinaryData ssid; + uint32_t channel; + int32_t rssi; + ProtobufCBinaryData bssid; + WifiAuthMode auth; +}; +#define WI_FI_SCAN_RESULT__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&wi_fi_scan_result__descriptor) \ + , {0,NULL}, 0, 0, {0,NULL}, WIFI_AUTH_MODE__Open } + + +struct ThreadScanResult +{ + ProtobufCMessage base; + uint32_t pan_id; + uint32_t channel; + int32_t rssi; + uint32_t lqi; + ProtobufCBinaryData ext_addr; + char *network_name; + ProtobufCBinaryData ext_pan_id; +}; +#define THREAD_SCAN_RESULT__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&thread_scan_result__descriptor) \ + , 0, 0, 0, 0, {0,NULL}, (char *)protobuf_c_empty_string, {0,NULL} } + + +struct RespScanWifiResult +{ + ProtobufCMessage base; + size_t n_entries; + WiFiScanResult **entries; +}; +#define RESP_SCAN_WIFI_RESULT__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_scan_wifi_result__descriptor) \ + , 0,NULL } + + +struct RespScanThreadResult +{ + ProtobufCMessage base; + size_t n_entries; + ThreadScanResult **entries; +}; +#define RESP_SCAN_THREAD_RESULT__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_scan_thread_result__descriptor) \ + , 0,NULL } + + +typedef enum { + NETWORK_SCAN_PAYLOAD__PAYLOAD__NOT_SET = 0, + NETWORK_SCAN_PAYLOAD__PAYLOAD_CMD_SCAN_WIFI_START = 10, + NETWORK_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_WIFI_START = 11, + NETWORK_SCAN_PAYLOAD__PAYLOAD_CMD_SCAN_WIFI_STATUS = 12, + NETWORK_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_WIFI_STATUS = 13, + NETWORK_SCAN_PAYLOAD__PAYLOAD_CMD_SCAN_WIFI_RESULT = 14, + NETWORK_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_WIFI_RESULT = 15, + NETWORK_SCAN_PAYLOAD__PAYLOAD_CMD_SCAN_THREAD_START = 16, + NETWORK_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_THREAD_START = 17, + NETWORK_SCAN_PAYLOAD__PAYLOAD_CMD_SCAN_THREAD_STATUS = 18, + NETWORK_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_THREAD_STATUS = 19, + NETWORK_SCAN_PAYLOAD__PAYLOAD_CMD_SCAN_THREAD_RESULT = 20, + NETWORK_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_THREAD_RESULT = 21 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(NETWORK_SCAN_PAYLOAD__PAYLOAD__CASE) +} NetworkScanPayload__PayloadCase; + +struct NetworkScanPayload +{ + ProtobufCMessage base; + NetworkScanMsgType msg; + Status status; + NetworkScanPayload__PayloadCase payload_case; + union { + CmdScanWifiStart *cmd_scan_wifi_start; + RespScanWifiStart *resp_scan_wifi_start; + CmdScanWifiStatus *cmd_scan_wifi_status; + RespScanWifiStatus *resp_scan_wifi_status; + CmdScanWifiResult *cmd_scan_wifi_result; + RespScanWifiResult *resp_scan_wifi_result; + CmdScanThreadStart *cmd_scan_thread_start; + RespScanThreadStart *resp_scan_thread_start; + CmdScanThreadStatus *cmd_scan_thread_status; + RespScanThreadStatus *resp_scan_thread_status; + CmdScanThreadResult *cmd_scan_thread_result; + RespScanThreadResult *resp_scan_thread_result; + }; +}; +#define NETWORK_SCAN_PAYLOAD__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&network_scan_payload__descriptor) \ + , NETWORK_SCAN_MSG_TYPE__TypeCmdScanWifiStart, STATUS__Success, NETWORK_SCAN_PAYLOAD__PAYLOAD__NOT_SET, {0} } + + +/* CmdScanWifiStart methods */ +void cmd_scan_wifi_start__init + (CmdScanWifiStart *message); +size_t cmd_scan_wifi_start__get_packed_size + (const CmdScanWifiStart *message); +size_t cmd_scan_wifi_start__pack + (const CmdScanWifiStart *message, + uint8_t *out); +size_t cmd_scan_wifi_start__pack_to_buffer + (const CmdScanWifiStart *message, + ProtobufCBuffer *buffer); +CmdScanWifiStart * + cmd_scan_wifi_start__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_scan_wifi_start__free_unpacked + (CmdScanWifiStart *message, + ProtobufCAllocator *allocator); +/* CmdScanThreadStart methods */ +void cmd_scan_thread_start__init + (CmdScanThreadStart *message); +size_t cmd_scan_thread_start__get_packed_size + (const CmdScanThreadStart *message); +size_t cmd_scan_thread_start__pack + (const CmdScanThreadStart *message, + uint8_t *out); +size_t cmd_scan_thread_start__pack_to_buffer + (const CmdScanThreadStart *message, + ProtobufCBuffer *buffer); +CmdScanThreadStart * + cmd_scan_thread_start__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_scan_thread_start__free_unpacked + (CmdScanThreadStart *message, + ProtobufCAllocator *allocator); +/* RespScanWifiStart methods */ +void resp_scan_wifi_start__init + (RespScanWifiStart *message); +size_t resp_scan_wifi_start__get_packed_size + (const RespScanWifiStart *message); +size_t resp_scan_wifi_start__pack + (const RespScanWifiStart *message, + uint8_t *out); +size_t resp_scan_wifi_start__pack_to_buffer + (const RespScanWifiStart *message, + ProtobufCBuffer *buffer); +RespScanWifiStart * + resp_scan_wifi_start__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_scan_wifi_start__free_unpacked + (RespScanWifiStart *message, + ProtobufCAllocator *allocator); +/* RespScanThreadStart methods */ +void resp_scan_thread_start__init + (RespScanThreadStart *message); +size_t resp_scan_thread_start__get_packed_size + (const RespScanThreadStart *message); +size_t resp_scan_thread_start__pack + (const RespScanThreadStart *message, + uint8_t *out); +size_t resp_scan_thread_start__pack_to_buffer + (const RespScanThreadStart *message, + ProtobufCBuffer *buffer); +RespScanThreadStart * + resp_scan_thread_start__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_scan_thread_start__free_unpacked + (RespScanThreadStart *message, + ProtobufCAllocator *allocator); +/* CmdScanWifiStatus methods */ +void cmd_scan_wifi_status__init + (CmdScanWifiStatus *message); +size_t cmd_scan_wifi_status__get_packed_size + (const CmdScanWifiStatus *message); +size_t cmd_scan_wifi_status__pack + (const CmdScanWifiStatus *message, + uint8_t *out); +size_t cmd_scan_wifi_status__pack_to_buffer + (const CmdScanWifiStatus *message, + ProtobufCBuffer *buffer); +CmdScanWifiStatus * + cmd_scan_wifi_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_scan_wifi_status__free_unpacked + (CmdScanWifiStatus *message, + ProtobufCAllocator *allocator); +/* CmdScanThreadStatus methods */ +void cmd_scan_thread_status__init + (CmdScanThreadStatus *message); +size_t cmd_scan_thread_status__get_packed_size + (const CmdScanThreadStatus *message); +size_t cmd_scan_thread_status__pack + (const CmdScanThreadStatus *message, + uint8_t *out); +size_t cmd_scan_thread_status__pack_to_buffer + (const CmdScanThreadStatus *message, + ProtobufCBuffer *buffer); +CmdScanThreadStatus * + cmd_scan_thread_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_scan_thread_status__free_unpacked + (CmdScanThreadStatus *message, + ProtobufCAllocator *allocator); +/* RespScanWifiStatus methods */ +void resp_scan_wifi_status__init + (RespScanWifiStatus *message); +size_t resp_scan_wifi_status__get_packed_size + (const RespScanWifiStatus *message); +size_t resp_scan_wifi_status__pack + (const RespScanWifiStatus *message, + uint8_t *out); +size_t resp_scan_wifi_status__pack_to_buffer + (const RespScanWifiStatus *message, + ProtobufCBuffer *buffer); +RespScanWifiStatus * + resp_scan_wifi_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_scan_wifi_status__free_unpacked + (RespScanWifiStatus *message, + ProtobufCAllocator *allocator); +/* RespScanThreadStatus methods */ +void resp_scan_thread_status__init + (RespScanThreadStatus *message); +size_t resp_scan_thread_status__get_packed_size + (const RespScanThreadStatus *message); +size_t resp_scan_thread_status__pack + (const RespScanThreadStatus *message, + uint8_t *out); +size_t resp_scan_thread_status__pack_to_buffer + (const RespScanThreadStatus *message, + ProtobufCBuffer *buffer); +RespScanThreadStatus * + resp_scan_thread_status__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_scan_thread_status__free_unpacked + (RespScanThreadStatus *message, + ProtobufCAllocator *allocator); +/* CmdScanWifiResult methods */ +void cmd_scan_wifi_result__init + (CmdScanWifiResult *message); +size_t cmd_scan_wifi_result__get_packed_size + (const CmdScanWifiResult *message); +size_t cmd_scan_wifi_result__pack + (const CmdScanWifiResult *message, + uint8_t *out); +size_t cmd_scan_wifi_result__pack_to_buffer + (const CmdScanWifiResult *message, + ProtobufCBuffer *buffer); +CmdScanWifiResult * + cmd_scan_wifi_result__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_scan_wifi_result__free_unpacked + (CmdScanWifiResult *message, + ProtobufCAllocator *allocator); +/* CmdScanThreadResult methods */ +void cmd_scan_thread_result__init + (CmdScanThreadResult *message); +size_t cmd_scan_thread_result__get_packed_size + (const CmdScanThreadResult *message); +size_t cmd_scan_thread_result__pack + (const CmdScanThreadResult *message, + uint8_t *out); +size_t cmd_scan_thread_result__pack_to_buffer + (const CmdScanThreadResult *message, + ProtobufCBuffer *buffer); +CmdScanThreadResult * + cmd_scan_thread_result__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_scan_thread_result__free_unpacked + (CmdScanThreadResult *message, + ProtobufCAllocator *allocator); +/* WiFiScanResult methods */ +void wi_fi_scan_result__init + (WiFiScanResult *message); +size_t wi_fi_scan_result__get_packed_size + (const WiFiScanResult *message); +size_t wi_fi_scan_result__pack + (const WiFiScanResult *message, + uint8_t *out); +size_t wi_fi_scan_result__pack_to_buffer + (const WiFiScanResult *message, + ProtobufCBuffer *buffer); +WiFiScanResult * + wi_fi_scan_result__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void wi_fi_scan_result__free_unpacked + (WiFiScanResult *message, + ProtobufCAllocator *allocator); +/* ThreadScanResult methods */ +void thread_scan_result__init + (ThreadScanResult *message); +size_t thread_scan_result__get_packed_size + (const ThreadScanResult *message); +size_t thread_scan_result__pack + (const ThreadScanResult *message, + uint8_t *out); +size_t thread_scan_result__pack_to_buffer + (const ThreadScanResult *message, + ProtobufCBuffer *buffer); +ThreadScanResult * + thread_scan_result__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void thread_scan_result__free_unpacked + (ThreadScanResult *message, + ProtobufCAllocator *allocator); +/* RespScanWifiResult methods */ +void resp_scan_wifi_result__init + (RespScanWifiResult *message); +size_t resp_scan_wifi_result__get_packed_size + (const RespScanWifiResult *message); +size_t resp_scan_wifi_result__pack + (const RespScanWifiResult *message, + uint8_t *out); +size_t resp_scan_wifi_result__pack_to_buffer + (const RespScanWifiResult *message, + ProtobufCBuffer *buffer); +RespScanWifiResult * + resp_scan_wifi_result__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_scan_wifi_result__free_unpacked + (RespScanWifiResult *message, + ProtobufCAllocator *allocator); +/* RespScanThreadResult methods */ +void resp_scan_thread_result__init + (RespScanThreadResult *message); +size_t resp_scan_thread_result__get_packed_size + (const RespScanThreadResult *message); +size_t resp_scan_thread_result__pack + (const RespScanThreadResult *message, + uint8_t *out); +size_t resp_scan_thread_result__pack_to_buffer + (const RespScanThreadResult *message, + ProtobufCBuffer *buffer); +RespScanThreadResult * + resp_scan_thread_result__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_scan_thread_result__free_unpacked + (RespScanThreadResult *message, + ProtobufCAllocator *allocator); +/* NetworkScanPayload methods */ +void network_scan_payload__init + (NetworkScanPayload *message); +size_t network_scan_payload__get_packed_size + (const NetworkScanPayload *message); +size_t network_scan_payload__pack + (const NetworkScanPayload *message, + uint8_t *out); +size_t network_scan_payload__pack_to_buffer + (const NetworkScanPayload *message, + ProtobufCBuffer *buffer); +NetworkScanPayload * + network_scan_payload__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void network_scan_payload__free_unpacked + (NetworkScanPayload *message, + ProtobufCAllocator *allocator); +/* --- per-message closures --- */ + +typedef void (*CmdScanWifiStart_Closure) + (const CmdScanWifiStart *message, + void *closure_data); +typedef void (*CmdScanThreadStart_Closure) + (const CmdScanThreadStart *message, + void *closure_data); +typedef void (*RespScanWifiStart_Closure) + (const RespScanWifiStart *message, + void *closure_data); +typedef void (*RespScanThreadStart_Closure) + (const RespScanThreadStart *message, + void *closure_data); +typedef void (*CmdScanWifiStatus_Closure) + (const CmdScanWifiStatus *message, + void *closure_data); +typedef void (*CmdScanThreadStatus_Closure) + (const CmdScanThreadStatus *message, + void *closure_data); +typedef void (*RespScanWifiStatus_Closure) + (const RespScanWifiStatus *message, + void *closure_data); +typedef void (*RespScanThreadStatus_Closure) + (const RespScanThreadStatus *message, + void *closure_data); +typedef void (*CmdScanWifiResult_Closure) + (const CmdScanWifiResult *message, + void *closure_data); +typedef void (*CmdScanThreadResult_Closure) + (const CmdScanThreadResult *message, + void *closure_data); +typedef void (*WiFiScanResult_Closure) + (const WiFiScanResult *message, + void *closure_data); +typedef void (*ThreadScanResult_Closure) + (const ThreadScanResult *message, + void *closure_data); +typedef void (*RespScanWifiResult_Closure) + (const RespScanWifiResult *message, + void *closure_data); +typedef void (*RespScanThreadResult_Closure) + (const RespScanThreadResult *message, + void *closure_data); +typedef void (*NetworkScanPayload_Closure) + (const NetworkScanPayload *message, + void *closure_data); + +/* --- services --- */ + + +/* --- descriptors --- */ + +extern const ProtobufCEnumDescriptor network_scan_msg_type__descriptor; +extern const ProtobufCMessageDescriptor cmd_scan_wifi_start__descriptor; +extern const ProtobufCMessageDescriptor cmd_scan_thread_start__descriptor; +extern const ProtobufCMessageDescriptor resp_scan_wifi_start__descriptor; +extern const ProtobufCMessageDescriptor resp_scan_thread_start__descriptor; +extern const ProtobufCMessageDescriptor cmd_scan_wifi_status__descriptor; +extern const ProtobufCMessageDescriptor cmd_scan_thread_status__descriptor; +extern const ProtobufCMessageDescriptor resp_scan_wifi_status__descriptor; +extern const ProtobufCMessageDescriptor resp_scan_thread_status__descriptor; +extern const ProtobufCMessageDescriptor cmd_scan_wifi_result__descriptor; +extern const ProtobufCMessageDescriptor cmd_scan_thread_result__descriptor; +extern const ProtobufCMessageDescriptor wi_fi_scan_result__descriptor; +extern const ProtobufCMessageDescriptor thread_scan_result__descriptor; +extern const ProtobufCMessageDescriptor resp_scan_wifi_result__descriptor; +extern const ProtobufCMessageDescriptor resp_scan_thread_result__descriptor; +extern const ProtobufCMessageDescriptor network_scan_payload__descriptor; + +PROTOBUF_C__END_DECLS + + +#endif /* PROTOBUF_C_network_5fscan_2eproto__INCLUDED */ diff --git a/components/network_provisioning/proto/CMakeLists.txt b/components/network_provisioning/proto/CMakeLists.txt new file mode 100644 index 0000000..8e53ee7 --- /dev/null +++ b/components/network_provisioning/proto/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.22) + +set(PROTO_COMPILER "protoc") +set(PROTO_C_COMPILER "protoc-c") +set(C_OUT_PATH "${CMAKE_CURRENT_LIST_DIR}/../proto-c") +set(PY_OUT_PATH "${CMAKE_CURRENT_LIST_DIR}/../python") +set(PROTOCOMM_INCL_PATH "${IDF_PATH}/component/protocomm/proto") + +set(PROTO_SRCS "network_constants.proto" + "network_config.proto" + "network_scan.proto") + +add_custom_target(c_proto + COMMAND ${PROTO_C_COMPILER} --c_out=${C_OUT_PATH} -I . -I ${PROTOCOMM_INCL_PATH} ${PROTO_SRCS} + VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + ) + +add_custom_target(python_proto + COMMAND ${PROTO_COMPILER} --python_out=${PY_OUT_PATH} -I . -I ${PROTOCOMM_INCL_PATH} ${PROTO_SRCS} + VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + ) + +add_custom_target(proto ALL + DEPENDS c_proto python_proto + VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + ) diff --git a/components/network_provisioning/proto/README.md b/components/network_provisioning/proto/README.md new file mode 100644 index 0000000..f10208d --- /dev/null +++ b/components/network_provisioning/proto/README.md @@ -0,0 +1,29 @@ +# Protobuf files for defining Network provisioning packet structures + +`network_provisioning` uses Google Protobuf for language, transport and architecture agnostic protocol communication. These proto files define the protocomm packet structure, separated across multiple files: +* network_contants.proto - Defines the various enums for indicating state of network (connected / disconnect / connecting), disconnect reasons, auth modes, etc. +* network_config.proto - Defines network configuration structures and commands for setting Wi-Fi credentials (SSID, passphrase, BSSID) or Thread dataset, applying credentials and getting connection state. +* network_scan.proto - Defines network scan commands and result structures +* network_ctrl.proto - Defines network control commands(reset and re-provision) and result structures + +Note : These proto files are not automatically compiled during the build process. + +# Compilation + +Compilation requires protoc (Protobuf Compiler) and protoc-c (Protobuf C Compiler) installed. Since the generated files are to remain the same, as long as the proto files are not modified, therefore the generated files are already available under `components/network_provisioning/proto-c` and `components/network_provisioning/python` directories, and thus running cmake / make (and installing the Protobuf compilers) is optional. + +If using `cmake` follow the below steps. If using `make`, jump to Step 2 directly. + +## Step 1 (Only for cmake) + +When using cmake, first create a build directory and call cmake from inside: + +``` +mkdir build +cd build +cmake .. +``` + +## Step 2 + +Simply run `make` to generate the respective C and Python files. The newly created files will overwrite those under `components/network_provisioning/proto-c` and `components/network_provisioning/python` diff --git a/components/network_provisioning/proto/makefile b/components/network_provisioning/proto/makefile new file mode 100644 index 0000000..127bd53 --- /dev/null +++ b/components/network_provisioning/proto/makefile @@ -0,0 +1,7 @@ +all: c_proto python_proto + +c_proto: *.proto + @protoc-c --c_out=../proto-c/ -I . -I ${IDF_PATH}/components/protocomm/proto/ *.proto + +python_proto: *.proto + @protoc --python_out=../python/ -I . -I ${IDF_PATH}/components/protocomm/proto/ *.proto diff --git a/components/network_provisioning/proto/network_config.proto b/components/network_provisioning/proto/network_config.proto new file mode 100644 index 0000000..6dd8df2 --- /dev/null +++ b/components/network_provisioning/proto/network_config.proto @@ -0,0 +1,96 @@ +syntax = "proto3"; + +import "constants.proto"; +import "network_constants.proto"; + +message CmdGetWifiStatus { +} + +message RespGetWifiStatus { + Status status = 1; + WifiStationState wifi_sta_state = 2; + oneof state { + WifiConnectFailedReason wifi_fail_reason = 10; + WifiConnectedState wifi_connected = 11; + WifiAttemptFailed attempt_failed = 12; + } +} + +message CmdGetThreadStatus { +} + +message RespGetThreadStatus { + Status status = 1; + ThreadNetworkState thread_state = 2; + oneof state { + ThreadAttachFailedReason thread_fail_reason = 10; + ThreadAttachState thread_attached = 11; + } +} + +message CmdSetWifiConfig { + bytes ssid = 1; + bytes passphrase = 2; + bytes bssid = 3; + int32 channel = 4; +} + +message CmdSetThreadConfig { + bytes dataset = 1; +} + +message RespSetWifiConfig { + Status status = 1; +} + +message RespSetThreadConfig { + Status status = 1; +} + +message CmdApplyWifiConfig { +} + +message CmdApplyThreadConfig { +} + +message RespApplyWifiConfig { + Status status = 1; +} + +message RespApplyThreadConfig { + Status status = 1; +} + +enum NetworkConfigMsgType { + TypeCmdGetWifiStatus = 0; + TypeRespGetWifiStatus = 1; + TypeCmdSetWifiConfig = 2; + TypeRespSetWifiConfig = 3; + TypeCmdApplyWifiConfig = 4; + TypeRespApplyWifiConfig = 5; + TypeCmdGetThreadStatus = 6; + TypeRespGetThreadStatus = 7; + TypeCmdSetThreadConfig = 8; + TypeRespSetThreadConfig = 9; + TypeCmdApplyThreadConfig = 10; + TypeRespApplyThreadConfig = 11; + +} + +message NetworkConfigPayload { + NetworkConfigMsgType msg = 1; + oneof payload { + CmdGetWifiStatus cmd_get_wifi_status = 10; + RespGetWifiStatus resp_get_wifi_status = 11; + CmdSetWifiConfig cmd_set_wifi_config = 12; + RespSetWifiConfig resp_set_wifi_config = 13; + CmdApplyWifiConfig cmd_apply_wifi_config = 14; + RespApplyWifiConfig resp_apply_wifi_config = 15; + CmdGetThreadStatus cmd_get_thread_status = 16; + RespGetThreadStatus resp_get_thread_status = 17; + CmdSetThreadConfig cmd_set_thread_config = 18; + RespSetThreadConfig resp_set_thread_config = 19; + CmdApplyThreadConfig cmd_apply_thread_config = 20; + RespApplyThreadConfig resp_apply_thread_config = 21; + } +} diff --git a/components/network_provisioning/proto/network_constants.proto b/components/network_provisioning/proto/network_constants.proto new file mode 100644 index 0000000..b3e08d1 --- /dev/null +++ b/components/network_provisioning/proto/network_constants.proto @@ -0,0 +1,55 @@ +syntax = "proto3"; + +enum WifiStationState { + Connected = 0; + Connecting = 1; + Disconnected = 2; + ConnectionFailed = 3; +} + +enum WifiConnectFailedReason { + AuthError = 0; + WifiNetworkNotFound = 1; +} + +enum WifiAuthMode { + Open = 0; + WEP = 1; + WPA_PSK = 2; + WPA2_PSK = 3; + WPA_WPA2_PSK = 4; + WPA2_ENTERPRISE = 5; + WPA3_PSK = 6; + WPA2_WPA3_PSK = 7; +} + +message WifiConnectedState { + string ip4_addr = 1; + WifiAuthMode auth_mode = 2; + bytes ssid = 3; + bytes bssid = 4; + int32 channel = 5; +} + +message WifiAttemptFailed { + uint32 attempts_remaining = 1; +} + +enum ThreadNetworkState { + Attached = 0; + Attaching = 1; + Dettached = 2; + AttachingFailed = 3; +} + +enum ThreadAttachFailedReason { + DatasetInvalid = 0; + ThreadNetworkNotFound = 1; +} + +message ThreadAttachState { + uint32 pan_id = 1; + bytes ext_pan_id = 2; + uint32 channel = 3; + string name = 4; +} diff --git a/components/network_provisioning/proto/network_ctrl.proto b/components/network_provisioning/proto/network_ctrl.proto new file mode 100644 index 0000000..3da679d --- /dev/null +++ b/components/network_provisioning/proto/network_ctrl.proto @@ -0,0 +1,55 @@ +syntax = "proto3"; + +import "constants.proto"; + +message CmdCtrlWifiReset { +} + +message RespCtrlWifiReset { +} + +message CmdCtrlWifiReprov { +} + +message RespCtrlWifiReprov{ +} + +message CmdCtrlThreadReset { +} + +message RespCtrlThreadReset { +} + +message CmdCtrlThreadReprov { +} + +message RespCtrlThreadReprov{ +} + +enum NetworkCtrlMsgType { + TypeCtrlReserved = 0; + TypeCmdCtrlWifiReset = 1; + TypeRespCtrlWifiReset = 2; + TypeCmdCtrlWifiReprov = 3; + TypeRespCtrlWifiReprov = 4; + TypeCmdCtrlThreadReset = 5; + TypeRespCtrlThreadReset = 6; + TypeCmdCtrlThreadReprov = 7; + TypeRespCtrlThreadReprov = 8; + +} + +message NetworkCtrlPayload { + NetworkCtrlMsgType msg = 1; + Status status = 2; + oneof payload { + CmdCtrlWifiReset cmd_ctrl_wifi_reset = 11; + RespCtrlWifiReset resp_ctrl_wifi_reset = 12; + CmdCtrlWifiReprov cmd_ctrl_wifi_reprov = 13; + RespCtrlWifiReprov resp_ctrl_wifi_reprov = 14; + CmdCtrlThreadReset cmd_ctrl_thread_reset = 15; + RespCtrlThreadReset resp_ctrl_thread_reset = 16; + CmdCtrlThreadReprov cmd_ctrl_thread_reprov = 17; + RespCtrlThreadReprov resp_ctrl_thread_reprov = 18; + } +} diff --git a/components/network_provisioning/proto/network_scan.proto b/components/network_provisioning/proto/network_scan.proto new file mode 100644 index 0000000..5907999 --- /dev/null +++ b/components/network_provisioning/proto/network_scan.proto @@ -0,0 +1,109 @@ +syntax = "proto3"; + +import "constants.proto"; +import "network_constants.proto"; + +message CmdScanWifiStart { + bool blocking = 1; + bool passive = 2; + uint32 group_channels = 3; + uint32 period_ms = 4; +} + +message CmdScanThreadStart { + bool blocking = 1; + uint32 channel_mask = 2; +} + +message RespScanWifiStart { +} + +message RespScanThreadStart { +} + +message CmdScanWifiStatus { +} + +message CmdScanThreadStatus { +} + +message RespScanWifiStatus { + bool scan_finished = 1; + uint32 result_count = 2; +} + +message RespScanThreadStatus { + bool scan_finished = 1; + uint32 result_count = 2; +} + +message CmdScanWifiResult { + uint32 start_index = 1; + uint32 count = 2; +} + +message CmdScanThreadResult { + uint32 start_index = 1; + uint32 count = 2; +} + +message WiFiScanResult { + bytes ssid = 1; + uint32 channel = 2; + int32 rssi = 3; + bytes bssid = 4; + WifiAuthMode auth = 5; +} + +message ThreadScanResult { + uint32 pan_id = 1; + uint32 channel = 2; + int32 rssi = 3; + uint32 lqi = 4; + bytes ext_addr = 5; + string network_name = 6; + bytes ext_pan_id = 7; +} + +message RespScanWifiResult { + repeated WiFiScanResult entries = 1; +} + +message RespScanThreadResult { + repeated ThreadScanResult entries = 1; +} + + +enum NetworkScanMsgType { + TypeCmdScanWifiStart = 0; + TypeRespScanWifiStart = 1; + TypeCmdScanWifiStatus = 2; + TypeRespScanWifiStatus = 3; + TypeCmdScanWifiResult = 4; + TypeRespScanWifiResult = 5; + TypeCmdScanThreadStart = 6; + TypeRespScanThreadStart = 7; + TypeCmdScanThreadStatus = 8; + TypeRespScanThreadStatus = 9; + TypeCmdScanThreadResult = 10; + TypeRespScanThreadResult = 11; +} + +message NetworkScanPayload { + NetworkScanMsgType msg = 1; + Status status = 2; + oneof payload { + CmdScanWifiStart cmd_scan_wifi_start = 10; + RespScanWifiStart resp_scan_wifi_start = 11; + CmdScanWifiStatus cmd_scan_wifi_status = 12; + RespScanWifiStatus resp_scan_wifi_status = 13; + CmdScanWifiResult cmd_scan_wifi_result = 14; + RespScanWifiResult resp_scan_wifi_result = 15; + CmdScanThreadStart cmd_scan_thread_start = 16; + RespScanThreadStart resp_scan_thread_start = 17; + CmdScanThreadStatus cmd_scan_thread_status = 18; + RespScanThreadStatus resp_scan_thread_status = 19; + CmdScanThreadResult cmd_scan_thread_result = 20; + RespScanThreadResult resp_scan_thread_result = 21; + } +} diff --git a/components/network_provisioning/python/network_config_pb2.py b/components/network_provisioning/python/network_config_pb2.py new file mode 100644 index 0000000..f876ef3 --- /dev/null +++ b/components/network_provisioning/python/network_config_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: network_config.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import constants_pb2 as constants__pb2 +import network_constants_pb2 as network__constants__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14network_config.proto\x1a\x0f\x63onstants.proto\x1a\x17network_constants.proto\"\x12\n\x10\x43mdGetWifiStatus\"\xf3\x01\n\x11RespGetWifiStatus\x12\x17\n\x06status\x18\x01 \x01(\x0e\x32\x07.Status\x12)\n\x0ewifi_sta_state\x18\x02 \x01(\x0e\x32\x11.WifiStationState\x12\x34\n\x10wifi_fail_reason\x18\n \x01(\x0e\x32\x18.WifiConnectFailedReasonH\x00\x12-\n\x0ewifi_connected\x18\x0b \x01(\x0b\x32\x13.WifiConnectedStateH\x00\x12,\n\x0e\x61ttempt_failed\x18\x0c \x01(\x0b\x32\x12.WifiAttemptFailedH\x00\x42\x07\n\x05state\"\x14\n\x12\x43mdGetThreadStatus\"\xca\x01\n\x13RespGetThreadStatus\x12\x17\n\x06status\x18\x01 \x01(\x0e\x32\x07.Status\x12)\n\x0cthread_state\x18\x02 \x01(\x0e\x32\x13.ThreadNetworkState\x12\x37\n\x12thread_fail_reason\x18\n \x01(\x0e\x32\x19.ThreadAttachFailedReasonH\x00\x12-\n\x0fthread_attached\x18\x0b \x01(\x0b\x32\x12.ThreadAttachStateH\x00\x42\x07\n\x05state\"T\n\x10\x43mdSetWifiConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\x0c\x12\x12\n\npassphrase\x18\x02 \x01(\x0c\x12\r\n\x05\x62ssid\x18\x03 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\x05\"%\n\x12\x43mdSetThreadConfig\x12\x0f\n\x07\x64\x61taset\x18\x01 \x01(\x0c\",\n\x11RespSetWifiConfig\x12\x17\n\x06status\x18\x01 \x01(\x0e\x32\x07.Status\".\n\x13RespSetThreadConfig\x12\x17\n\x06status\x18\x01 \x01(\x0e\x32\x07.Status\"\x14\n\x12\x43mdApplyWifiConfig\"\x16\n\x14\x43mdApplyThreadConfig\".\n\x13RespApplyWifiConfig\x12\x17\n\x06status\x18\x01 \x01(\x0e\x32\x07.Status\"0\n\x15RespApplyThreadConfig\x12\x17\n\x06status\x18\x01 \x01(\x0e\x32\x07.Status\"\xd1\x05\n\x14NetworkConfigPayload\x12\"\n\x03msg\x18\x01 \x01(\x0e\x32\x15.NetworkConfigMsgType\x12\x30\n\x13\x63md_get_wifi_status\x18\n \x01(\x0b\x32\x11.CmdGetWifiStatusH\x00\x12\x32\n\x14resp_get_wifi_status\x18\x0b \x01(\x0b\x32\x12.RespGetWifiStatusH\x00\x12\x30\n\x13\x63md_set_wifi_config\x18\x0c \x01(\x0b\x32\x11.CmdSetWifiConfigH\x00\x12\x32\n\x14resp_set_wifi_config\x18\r \x01(\x0b\x32\x12.RespSetWifiConfigH\x00\x12\x34\n\x15\x63md_apply_wifi_config\x18\x0e \x01(\x0b\x32\x13.CmdApplyWifiConfigH\x00\x12\x36\n\x16resp_apply_wifi_config\x18\x0f \x01(\x0b\x32\x14.RespApplyWifiConfigH\x00\x12\x34\n\x15\x63md_get_thread_status\x18\x10 \x01(\x0b\x32\x13.CmdGetThreadStatusH\x00\x12\x36\n\x16resp_get_thread_status\x18\x11 \x01(\x0b\x32\x14.RespGetThreadStatusH\x00\x12\x34\n\x15\x63md_set_thread_config\x18\x12 \x01(\x0b\x32\x13.CmdSetThreadConfigH\x00\x12\x36\n\x16resp_set_thread_config\x18\x13 \x01(\x0b\x32\x14.RespSetThreadConfigH\x00\x12\x38\n\x17\x63md_apply_thread_config\x18\x14 \x01(\x0b\x32\x15.CmdApplyThreadConfigH\x00\x12:\n\x18resp_apply_thread_config\x18\x15 \x01(\x0b\x32\x16.RespApplyThreadConfigH\x00\x42\t\n\x07payload*\xe8\x02\n\x14NetworkConfigMsgType\x12\x18\n\x14TypeCmdGetWifiStatus\x10\x00\x12\x19\n\x15TypeRespGetWifiStatus\x10\x01\x12\x18\n\x14TypeCmdSetWifiConfig\x10\x02\x12\x19\n\x15TypeRespSetWifiConfig\x10\x03\x12\x1a\n\x16TypeCmdApplyWifiConfig\x10\x04\x12\x1b\n\x17TypeRespApplyWifiConfig\x10\x05\x12\x1a\n\x16TypeCmdGetThreadStatus\x10\x06\x12\x1b\n\x17TypeRespGetThreadStatus\x10\x07\x12\x1a\n\x16TypeCmdSetThreadConfig\x10\x08\x12\x1b\n\x17TypeRespSetThreadConfig\x10\t\x12\x1c\n\x18TypeCmdApplyThreadConfig\x10\n\x12\x1d\n\x19TypeRespApplyThreadConfig\x10\x0b\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'network_config_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _NETWORKCONFIGMSGTYPE._serialized_start=1647 + _NETWORKCONFIGMSGTYPE._serialized_end=2007 + _CMDGETWIFISTATUS._serialized_start=66 + _CMDGETWIFISTATUS._serialized_end=84 + _RESPGETWIFISTATUS._serialized_start=87 + _RESPGETWIFISTATUS._serialized_end=330 + _CMDGETTHREADSTATUS._serialized_start=332 + _CMDGETTHREADSTATUS._serialized_end=352 + _RESPGETTHREADSTATUS._serialized_start=355 + _RESPGETTHREADSTATUS._serialized_end=557 + _CMDSETWIFICONFIG._serialized_start=559 + _CMDSETWIFICONFIG._serialized_end=643 + _CMDSETTHREADCONFIG._serialized_start=645 + _CMDSETTHREADCONFIG._serialized_end=682 + _RESPSETWIFICONFIG._serialized_start=684 + _RESPSETWIFICONFIG._serialized_end=728 + _RESPSETTHREADCONFIG._serialized_start=730 + _RESPSETTHREADCONFIG._serialized_end=776 + _CMDAPPLYWIFICONFIG._serialized_start=778 + _CMDAPPLYWIFICONFIG._serialized_end=798 + _CMDAPPLYTHREADCONFIG._serialized_start=800 + _CMDAPPLYTHREADCONFIG._serialized_end=822 + _RESPAPPLYWIFICONFIG._serialized_start=824 + _RESPAPPLYWIFICONFIG._serialized_end=870 + _RESPAPPLYTHREADCONFIG._serialized_start=872 + _RESPAPPLYTHREADCONFIG._serialized_end=920 + _NETWORKCONFIGPAYLOAD._serialized_start=923 + _NETWORKCONFIGPAYLOAD._serialized_end=1644 +# @@protoc_insertion_point(module_scope) diff --git a/components/network_provisioning/python/network_constants_pb2.py b/components/network_provisioning/python/network_constants_pb2.py new file mode 100644 index 0000000..88359f1 --- /dev/null +++ b/components/network_provisioning/python/network_constants_pb2.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: network_constants.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17network_constants.proto\"v\n\x12WifiConnectedState\x12\x10\n\x08ip4_addr\x18\x01 \x01(\t\x12 \n\tauth_mode\x18\x02 \x01(\x0e\x32\r.WifiAuthMode\x12\x0c\n\x04ssid\x18\x03 \x01(\x0c\x12\r\n\x05\x62ssid\x18\x04 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x05 \x01(\x05\"/\n\x11WifiAttemptFailed\x12\x1a\n\x12\x61ttempts_remaining\x18\x01 \x01(\r\"V\n\x11ThreadAttachState\x12\x0e\n\x06pan_id\x18\x01 \x01(\r\x12\x12\n\next_pan_id\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\r\x12\x0c\n\x04name\x18\x04 \x01(\t*Y\n\x10WifiStationState\x12\r\n\tConnected\x10\x00\x12\x0e\n\nConnecting\x10\x01\x12\x10\n\x0c\x44isconnected\x10\x02\x12\x14\n\x10\x43onnectionFailed\x10\x03*A\n\x17WifiConnectFailedReason\x12\r\n\tAuthError\x10\x00\x12\x17\n\x13WifiNetworkNotFound\x10\x01*\x84\x01\n\x0cWifiAuthMode\x12\x08\n\x04Open\x10\x00\x12\x07\n\x03WEP\x10\x01\x12\x0b\n\x07WPA_PSK\x10\x02\x12\x0c\n\x08WPA2_PSK\x10\x03\x12\x10\n\x0cWPA_WPA2_PSK\x10\x04\x12\x13\n\x0fWPA2_ENTERPRISE\x10\x05\x12\x0c\n\x08WPA3_PSK\x10\x06\x12\x11\n\rWPA2_WPA3_PSK\x10\x07*U\n\x12ThreadNetworkState\x12\x0c\n\x08\x41ttached\x10\x00\x12\r\n\tAttaching\x10\x01\x12\r\n\tDettached\x10\x02\x12\x13\n\x0f\x41ttachingFailed\x10\x03*I\n\x18ThreadAttachFailedReason\x12\x12\n\x0e\x44\x61tasetInvalid\x10\x00\x12\x19\n\x15ThreadNetworkNotFound\x10\x01\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'network_constants_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _WIFISTATIONSTATE._serialized_start=284 + _WIFISTATIONSTATE._serialized_end=373 + _WIFICONNECTFAILEDREASON._serialized_start=375 + _WIFICONNECTFAILEDREASON._serialized_end=440 + _WIFIAUTHMODE._serialized_start=443 + _WIFIAUTHMODE._serialized_end=575 + _THREADNETWORKSTATE._serialized_start=577 + _THREADNETWORKSTATE._serialized_end=662 + _THREADATTACHFAILEDREASON._serialized_start=664 + _THREADATTACHFAILEDREASON._serialized_end=737 + _WIFICONNECTEDSTATE._serialized_start=27 + _WIFICONNECTEDSTATE._serialized_end=145 + _WIFIATTEMPTFAILED._serialized_start=147 + _WIFIATTEMPTFAILED._serialized_end=194 + _THREADATTACHSTATE._serialized_start=196 + _THREADATTACHSTATE._serialized_end=282 +# @@protoc_insertion_point(module_scope) diff --git a/components/network_provisioning/python/network_ctrl_pb2.py b/components/network_provisioning/python/network_ctrl_pb2.py new file mode 100644 index 0000000..97c40b3 --- /dev/null +++ b/components/network_provisioning/python/network_ctrl_pb2.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: network_ctrl.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import constants_pb2 as constants__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12network_ctrl.proto\x1a\x0f\x63onstants.proto\"\x12\n\x10\x43mdCtrlWifiReset\"\x13\n\x11RespCtrlWifiReset\"\x13\n\x11\x43mdCtrlWifiReprov\"\x14\n\x12RespCtrlWifiReprov\"\x14\n\x12\x43mdCtrlThreadReset\"\x15\n\x13RespCtrlThreadReset\"\x15\n\x13\x43mdCtrlThreadReprov\"\x16\n\x14RespCtrlThreadReprov\"\x8a\x04\n\x12NetworkCtrlPayload\x12 \n\x03msg\x18\x01 \x01(\x0e\x32\x13.NetworkCtrlMsgType\x12\x17\n\x06status\x18\x02 \x01(\x0e\x32\x07.Status\x12\x30\n\x13\x63md_ctrl_wifi_reset\x18\x0b \x01(\x0b\x32\x11.CmdCtrlWifiResetH\x00\x12\x32\n\x14resp_ctrl_wifi_reset\x18\x0c \x01(\x0b\x32\x12.RespCtrlWifiResetH\x00\x12\x32\n\x14\x63md_ctrl_wifi_reprov\x18\r \x01(\x0b\x32\x12.CmdCtrlWifiReprovH\x00\x12\x34\n\x15resp_ctrl_wifi_reprov\x18\x0e \x01(\x0b\x32\x13.RespCtrlWifiReprovH\x00\x12\x34\n\x15\x63md_ctrl_thread_reset\x18\x0f \x01(\x0b\x32\x13.CmdCtrlThreadResetH\x00\x12\x36\n\x16resp_ctrl_thread_reset\x18\x10 \x01(\x0b\x32\x14.RespCtrlThreadResetH\x00\x12\x36\n\x16\x63md_ctrl_thread_reprov\x18\x11 \x01(\x0b\x32\x14.CmdCtrlThreadReprovH\x00\x12\x38\n\x17resp_ctrl_thread_reprov\x18\x12 \x01(\x0b\x32\x15.RespCtrlThreadReprovH\x00\x42\t\n\x07payload*\x8a\x02\n\x12NetworkCtrlMsgType\x12\x14\n\x10TypeCtrlReserved\x10\x00\x12\x18\n\x14TypeCmdCtrlWifiReset\x10\x01\x12\x19\n\x15TypeRespCtrlWifiReset\x10\x02\x12\x19\n\x15TypeCmdCtrlWifiReprov\x10\x03\x12\x1a\n\x16TypeRespCtrlWifiReprov\x10\x04\x12\x1a\n\x16TypeCmdCtrlThreadReset\x10\x05\x12\x1b\n\x17TypeRespCtrlThreadReset\x10\x06\x12\x1b\n\x17TypeCmdCtrlThreadReprov\x10\x07\x12\x1c\n\x18TypeRespCtrlThreadReprov\x10\x08\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'network_ctrl_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _NETWORKCTRLMSGTYPE._serialized_start=741 + _NETWORKCTRLMSGTYPE._serialized_end=1007 + _CMDCTRLWIFIRESET._serialized_start=39 + _CMDCTRLWIFIRESET._serialized_end=57 + _RESPCTRLWIFIRESET._serialized_start=59 + _RESPCTRLWIFIRESET._serialized_end=78 + _CMDCTRLWIFIREPROV._serialized_start=80 + _CMDCTRLWIFIREPROV._serialized_end=99 + _RESPCTRLWIFIREPROV._serialized_start=101 + _RESPCTRLWIFIREPROV._serialized_end=121 + _CMDCTRLTHREADRESET._serialized_start=123 + _CMDCTRLTHREADRESET._serialized_end=143 + _RESPCTRLTHREADRESET._serialized_start=145 + _RESPCTRLTHREADRESET._serialized_end=166 + _CMDCTRLTHREADREPROV._serialized_start=168 + _CMDCTRLTHREADREPROV._serialized_end=189 + _RESPCTRLTHREADREPROV._serialized_start=191 + _RESPCTRLTHREADREPROV._serialized_end=213 + _NETWORKCTRLPAYLOAD._serialized_start=216 + _NETWORKCTRLPAYLOAD._serialized_end=738 +# @@protoc_insertion_point(module_scope) diff --git a/components/network_provisioning/python/network_scan_pb2.py b/components/network_provisioning/python/network_scan_pb2.py new file mode 100644 index 0000000..72c2122 --- /dev/null +++ b/components/network_provisioning/python/network_scan_pb2.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: network_scan.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import constants_pb2 as constants__pb2 +import network_constants_pb2 as network__constants__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12network_scan.proto\x1a\x0f\x63onstants.proto\x1a\x17network_constants.proto\"`\n\x10\x43mdScanWifiStart\x12\x10\n\x08\x62locking\x18\x01 \x01(\x08\x12\x0f\n\x07passive\x18\x02 \x01(\x08\x12\x16\n\x0egroup_channels\x18\x03 \x01(\r\x12\x11\n\tperiod_ms\x18\x04 \x01(\r\"<\n\x12\x43mdScanThreadStart\x12\x10\n\x08\x62locking\x18\x01 \x01(\x08\x12\x14\n\x0c\x63hannel_mask\x18\x02 \x01(\r\"\x13\n\x11RespScanWifiStart\"\x15\n\x13RespScanThreadStart\"\x13\n\x11\x43mdScanWifiStatus\"\x15\n\x13\x43mdScanThreadStatus\"A\n\x12RespScanWifiStatus\x12\x15\n\rscan_finished\x18\x01 \x01(\x08\x12\x14\n\x0cresult_count\x18\x02 \x01(\r\"C\n\x14RespScanThreadStatus\x12\x15\n\rscan_finished\x18\x01 \x01(\x08\x12\x14\n\x0cresult_count\x18\x02 \x01(\r\"7\n\x11\x43mdScanWifiResult\x12\x13\n\x0bstart_index\x18\x01 \x01(\r\x12\r\n\x05\x63ount\x18\x02 \x01(\r\"9\n\x13\x43mdScanThreadResult\x12\x13\n\x0bstart_index\x18\x01 \x01(\r\x12\r\n\x05\x63ount\x18\x02 \x01(\r\"i\n\x0eWiFiScanResult\x12\x0c\n\x04ssid\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\r\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\x12\r\n\x05\x62ssid\x18\x04 \x01(\x0c\x12\x1b\n\x04\x61uth\x18\x05 \x01(\x0e\x32\r.WifiAuthMode\"\x8a\x01\n\x10ThreadScanResult\x12\x0e\n\x06pan_id\x18\x01 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\r\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\x12\x0b\n\x03lqi\x18\x04 \x01(\r\x12\x10\n\x08\x65xt_addr\x18\x05 \x01(\x0c\x12\x14\n\x0cnetwork_name\x18\x06 \x01(\t\x12\x12\n\next_pan_id\x18\x07 \x01(\x0c\"6\n\x12RespScanWifiResult\x12 \n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x0f.WiFiScanResult\":\n\x14RespScanThreadResult\x12\"\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x11.ThreadScanResult\"\xe6\x05\n\x12NetworkScanPayload\x12 \n\x03msg\x18\x01 \x01(\x0e\x32\x13.NetworkScanMsgType\x12\x17\n\x06status\x18\x02 \x01(\x0e\x32\x07.Status\x12\x30\n\x13\x63md_scan_wifi_start\x18\n \x01(\x0b\x32\x11.CmdScanWifiStartH\x00\x12\x32\n\x14resp_scan_wifi_start\x18\x0b \x01(\x0b\x32\x12.RespScanWifiStartH\x00\x12\x32\n\x14\x63md_scan_wifi_status\x18\x0c \x01(\x0b\x32\x12.CmdScanWifiStatusH\x00\x12\x34\n\x15resp_scan_wifi_status\x18\r \x01(\x0b\x32\x13.RespScanWifiStatusH\x00\x12\x32\n\x14\x63md_scan_wifi_result\x18\x0e \x01(\x0b\x32\x12.CmdScanWifiResultH\x00\x12\x34\n\x15resp_scan_wifi_result\x18\x0f \x01(\x0b\x32\x13.RespScanWifiResultH\x00\x12\x34\n\x15\x63md_scan_thread_start\x18\x10 \x01(\x0b\x32\x13.CmdScanThreadStartH\x00\x12\x36\n\x16resp_scan_thread_start\x18\x11 \x01(\x0b\x32\x14.RespScanThreadStartH\x00\x12\x36\n\x16\x63md_scan_thread_status\x18\x12 \x01(\x0b\x32\x14.CmdScanThreadStatusH\x00\x12\x38\n\x17resp_scan_thread_status\x18\x13 \x01(\x0b\x32\x15.RespScanThreadStatusH\x00\x12\x36\n\x16\x63md_scan_thread_result\x18\x14 \x01(\x0b\x32\x14.CmdScanThreadResultH\x00\x12\x38\n\x17resp_scan_thread_result\x18\x15 \x01(\x0b\x32\x15.RespScanThreadResultH\x00\x42\t\n\x07payload*\xe6\x02\n\x12NetworkScanMsgType\x12\x18\n\x14TypeCmdScanWifiStart\x10\x00\x12\x19\n\x15TypeRespScanWifiStart\x10\x01\x12\x19\n\x15TypeCmdScanWifiStatus\x10\x02\x12\x1a\n\x16TypeRespScanWifiStatus\x10\x03\x12\x19\n\x15TypeCmdScanWifiResult\x10\x04\x12\x1a\n\x16TypeRespScanWifiResult\x10\x05\x12\x1a\n\x16TypeCmdScanThreadStart\x10\x06\x12\x1b\n\x17TypeRespScanThreadStart\x10\x07\x12\x1b\n\x17TypeCmdScanThreadStatus\x10\x08\x12\x1c\n\x18TypeRespScanThreadStatus\x10\t\x12\x1b\n\x17TypeCmdScanThreadResult\x10\n\x12\x1c\n\x18TypeRespScanThreadResult\x10\x0b\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'network_scan_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _NETWORKSCANMSGTYPE._serialized_start=1674 + _NETWORKSCANMSGTYPE._serialized_end=2032 + _CMDSCANWIFISTART._serialized_start=64 + _CMDSCANWIFISTART._serialized_end=160 + _CMDSCANTHREADSTART._serialized_start=162 + _CMDSCANTHREADSTART._serialized_end=222 + _RESPSCANWIFISTART._serialized_start=224 + _RESPSCANWIFISTART._serialized_end=243 + _RESPSCANTHREADSTART._serialized_start=245 + _RESPSCANTHREADSTART._serialized_end=266 + _CMDSCANWIFISTATUS._serialized_start=268 + _CMDSCANWIFISTATUS._serialized_end=287 + _CMDSCANTHREADSTATUS._serialized_start=289 + _CMDSCANTHREADSTATUS._serialized_end=310 + _RESPSCANWIFISTATUS._serialized_start=312 + _RESPSCANWIFISTATUS._serialized_end=377 + _RESPSCANTHREADSTATUS._serialized_start=379 + _RESPSCANTHREADSTATUS._serialized_end=446 + _CMDSCANWIFIRESULT._serialized_start=448 + _CMDSCANWIFIRESULT._serialized_end=503 + _CMDSCANTHREADRESULT._serialized_start=505 + _CMDSCANTHREADRESULT._serialized_end=562 + _WIFISCANRESULT._serialized_start=564 + _WIFISCANRESULT._serialized_end=669 + _THREADSCANRESULT._serialized_start=672 + _THREADSCANRESULT._serialized_end=810 + _RESPSCANWIFIRESULT._serialized_start=812 + _RESPSCANWIFIRESULT._serialized_end=866 + _RESPSCANTHREADRESULT._serialized_start=868 + _RESPSCANTHREADRESULT._serialized_end=926 + _NETWORKSCANPAYLOAD._serialized_start=929 + _NETWORKSCANPAYLOAD._serialized_end=1671 +# @@protoc_insertion_point(module_scope) diff --git a/components/network_provisioning/src/handlers.c b/components/network_provisioning/src/handlers.c new file mode 100644 index 0000000..6356d54 --- /dev/null +++ b/components/network_provisioning/src/handlers.c @@ -0,0 +1,412 @@ +/* + * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#include +#endif +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +#include +#endif +#include + +#include "network_provisioning/network_config.h" +#include "network_provisioning/network_scan.h" +#include "network_ctrl.h" +#include "network_provisioning/manager.h" +#include "network_provisioning_priv.h" + +#if defined(CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI) || defined(CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD) +static const char *TAG = "network_prov_handlers"; + +/* Provide definition of network_prov_ctx_t */ +struct network_prov_ctx { +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + wifi_config_t wifi_cfg; +#endif +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + otOperationalDatasetTlvs thread_dataset; +#endif +}; + +static void free_network_prov_ctx(network_prov_ctx_t **ctx) +{ + free(*ctx); + *ctx = NULL; +} +#endif + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +static wifi_config_t *get_wifi_config(network_prov_ctx_t **ctx) +{ + return (*ctx ? & (*ctx)->wifi_cfg : NULL); +} + +static wifi_config_t *new_wifi_config(network_prov_ctx_t **ctx) +{ + free(*ctx); + (*ctx) = (network_prov_ctx_t *) calloc(1, sizeof(network_prov_ctx_t)); + return get_wifi_config(ctx); +} + +static esp_err_t wifi_get_status_handler(network_prov_config_get_wifi_data_t *resp_data, network_prov_ctx_t **ctx) +{ + /* Initialize to zero */ + memset(resp_data, 0, sizeof(network_prov_config_get_wifi_data_t)); + + if (network_prov_mgr_get_wifi_state(&resp_data->wifi_state) != ESP_OK) { + ESP_LOGW(TAG, "Network provisioning manager for Wi-Fi not running"); + return ESP_ERR_INVALID_STATE; + } + + if (resp_data->wifi_state == NETWORK_PROV_WIFI_STA_CONNECTED) { + ESP_LOGD(TAG, "Got state : connected"); + + /* IP Addr assigned to STA */ + esp_netif_ip_info_t ip_info; + esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info); + esp_ip4addr_ntoa(&ip_info.ip, resp_data->conn_info.ip_addr, sizeof(resp_data->conn_info.ip_addr)); + + + /* AP information to which STA is connected */ + wifi_ap_record_t ap_info; + esp_wifi_sta_get_ap_info(&ap_info); + memcpy(resp_data->conn_info.bssid, (char *)ap_info.bssid, sizeof(ap_info.bssid)); + memcpy(resp_data->conn_info.ssid, (char *)ap_info.ssid, sizeof(ap_info.ssid)); + resp_data->conn_info.channel = ap_info.primary; + resp_data->conn_info.auth_mode = ap_info.authmode; + + /* Tell manager to stop provisioning service */ + network_prov_mgr_done(); + } else if (resp_data->wifi_state == NETWORK_PROV_WIFI_STA_DISCONNECTED) { + ESP_LOGD(TAG, "Got state : disconnected"); + + /* If disconnected, convey reason */ + network_prov_mgr_get_wifi_disconnect_reason(&resp_data->fail_reason); + } else { + if (network_prov_mgr_get_wifi_remaining_conn_attempts(&resp_data->connecting_info.attempts_remaining) != ESP_OK) { + ESP_LOGW(TAG, "network provisioning manager not running"); + return ESP_ERR_INVALID_STATE; + } + ESP_LOGD(TAG, "Got state : connecting"); + } + return ESP_OK; +} + +static esp_err_t wifi_set_config_handler(const network_prov_config_set_wifi_data_t *req_data, network_prov_ctx_t **ctx) +{ + wifi_config_t *wifi_cfg = get_wifi_config(ctx); + if (wifi_cfg) { + free_network_prov_ctx(ctx); + } + + wifi_cfg = new_wifi_config(ctx); + if (!wifi_cfg) { + ESP_LOGE(TAG, "Unable to allocate Wi-Fi config"); + return ESP_ERR_NO_MEM; + } + + ESP_LOGD(TAG, "Wi-Fi Credentials Received"); + + /* Using memcpy allows the max SSID length to be 32 bytes (as per 802.11 standard). + * But this doesn't guarantee that the saved SSID will be null terminated, because + * wifi_cfg->sta.ssid is also 32 bytes long (without extra 1 byte for null character). + * Although, this is not a matter for concern because esp_wifi library reads the SSID + * upto 32 bytes in absence of null termination */ + const size_t ssid_len = strnlen(req_data->ssid, sizeof(wifi_cfg->sta.ssid)); + /* Ensure SSID less than 32 bytes is null terminated */ + memset(wifi_cfg->sta.ssid, 0, sizeof(wifi_cfg->sta.ssid)); + memcpy(wifi_cfg->sta.ssid, req_data->ssid, ssid_len); + + /* Using strlcpy allows both max passphrase length (63 bytes) and ensures null termination + * because size of wifi_cfg->sta.password is 64 bytes (1 extra byte for null character) */ + strlcpy((char *) wifi_cfg->sta.password, req_data->password, sizeof(wifi_cfg->sta.password)); + +#ifdef CONFIG_NETWORK_PROV_WIFI_STA_ALL_CHANNEL_SCAN + wifi_cfg->sta.scan_method = WIFI_ALL_CHANNEL_SCAN; +#else /* CONFIG_NETWORK_PROV_WIFI_STA_FAST_SCAN */ + wifi_cfg->sta.scan_method = WIFI_FAST_SCAN; +#endif + + return ESP_OK; +} + +static esp_err_t wifi_apply_config_handler(network_prov_ctx_t **ctx) +{ + wifi_config_t *wifi_cfg = get_wifi_config(ctx); + if (!wifi_cfg) { + ESP_LOGE(TAG, "Wi-Fi config not set"); + return ESP_ERR_INVALID_STATE; + } + + esp_err_t ret = network_prov_mgr_configure_wifi_sta(wifi_cfg); + if (ret == ESP_OK) { + ESP_LOGD(TAG, "Wi-Fi Credentials Applied"); + } else { + ESP_LOGE(TAG, "Failed to apply Wi-Fi Credentials"); + } + + free_network_prov_ctx(ctx); + return ret; +} +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +static otOperationalDatasetTlvs *get_thread_dataset(network_prov_ctx_t **ctx) +{ + return (*ctx ? & (*ctx)->thread_dataset : NULL); +} + +static otOperationalDatasetTlvs *new_thread_dataset(network_prov_ctx_t **ctx) +{ + free(*ctx); + (*ctx) = (network_prov_ctx_t *) calloc(1, sizeof(network_prov_ctx_t)); + return get_thread_dataset(ctx); +} + +static esp_err_t thread_get_status_handler(network_prov_config_get_thread_data_t *resp_data, network_prov_ctx_t **ctx) +{ + /* Initialize to zero */ + memset(resp_data, 0, sizeof(network_prov_config_get_thread_data_t)); + + if (network_prov_mgr_get_thread_state(&resp_data->thread_state) != ESP_OK) { + ESP_LOGW(TAG, "Network provisioning manager not running"); + return ESP_ERR_INVALID_STATE; + } + + if (resp_data->thread_state == NETWORK_PROV_THREAD_ATTACHED) { + ESP_LOGD(TAG, "Got state : attached"); + otOperationalDataset dataset; + if (otDatasetGetActive(esp_openthread_get_instance(), &dataset) != OT_ERROR_NONE) { + ESP_LOGE(TAG, "Failed to get Thread dataset"); + return ESP_FAIL; + } + resp_data->conn_info.channel = dataset.mChannel; + memcpy(resp_data->conn_info.ext_pan_id, dataset.mExtendedPanId.m8, sizeof(dataset.mExtendedPanId.m8)); + strncpy(resp_data->conn_info.name, dataset.mNetworkName.m8, + strnlen(dataset.mNetworkName.m8, sizeof(dataset.mNetworkName.m8))); + resp_data->conn_info.pan_id = dataset.mPanId; + /* Tell manager to stop provisioning service */ + network_prov_mgr_done(); + } else if (resp_data->thread_state == NETWORK_PROV_THREAD_DETACHED) { + ESP_LOGD(TAG, "Got state : disconnected"); + + /* If disconnected, convey reason */ + network_prov_mgr_get_thread_detached_reason(&resp_data->fail_reason); + } else { + ESP_LOGD(TAG, "Got state : attaching"); + } + return ESP_OK; +} + +static esp_err_t thread_set_config_handler(const network_prov_config_set_thread_data_t *req_data, network_prov_ctx_t **ctx) +{ + otOperationalDatasetTlvs *thread_dataset = get_thread_dataset(ctx); + if (thread_dataset) { + free_network_prov_ctx(ctx); + } + + thread_dataset = new_thread_dataset(ctx); + if (!thread_dataset) { + ESP_LOGE(TAG, "Unable to allocate Thread dataset"); + return ESP_ERR_NO_MEM; + } + + ESP_LOGD(TAG, "Thread Dataset Received"); + + thread_dataset->mLength = req_data->length; + memcpy(thread_dataset->mTlvs, req_data->dataset, thread_dataset->mLength); + + return ESP_OK; +} + +static esp_err_t thread_apply_config_handler(network_prov_ctx_t **ctx) +{ + otOperationalDatasetTlvs *thread_dataset = get_thread_dataset(ctx); + if (!thread_dataset) { + ESP_LOGE(TAG, "Thread Dataset not set"); + return ESP_ERR_INVALID_STATE; + } + + esp_err_t ret = network_prov_mgr_configure_thread_dataset(thread_dataset); + if (ret == ESP_OK) { + ESP_LOGD(TAG, "Thread Dataset Applied"); + } else { + ESP_LOGE(TAG, "Failed to apply Thread Dataset"); + } + + free_network_prov_ctx(ctx); + return ret; +} +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + + +esp_err_t get_network_prov_handlers(network_prov_config_handlers_t *ptr) +{ + if (!ptr) { + return ESP_ERR_INVALID_ARG; + } +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + ptr->wifi_get_status_handler = wifi_get_status_handler; + ptr->wifi_set_config_handler = wifi_set_config_handler; + ptr->wifi_apply_config_handler = wifi_apply_config_handler; +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + ptr->thread_get_status_handler = thread_get_status_handler; + ptr->thread_set_config_handler = thread_set_config_handler; + ptr->thread_apply_config_handler = thread_apply_config_handler; +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + ptr->ctx = NULL; + return ESP_OK; +} + +/*************************************************************************/ +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +static esp_err_t wifi_scan_start(bool blocking, bool passive, + uint8_t group_channels, uint32_t period_ms, + network_prov_scan_ctx_t **ctx) +{ + return network_prov_mgr_wifi_scan_start(blocking, passive, group_channels, period_ms); +} + +static esp_err_t wifi_scan_status(bool *scan_finished, + uint16_t *result_count, + network_prov_scan_ctx_t **ctx) +{ + *scan_finished = network_prov_mgr_wifi_scan_finished(); + *result_count = network_prov_mgr_wifi_scan_result_count(); + return ESP_OK; +} + +static esp_err_t wifi_scan_result(uint16_t result_index, + network_prov_scan_wifi_result_t *result, + network_prov_scan_ctx_t **ctx) +{ + const wifi_ap_record_t *record = network_prov_mgr_wifi_scan_result(result_index); + if (!record) { + return ESP_FAIL; + } + + /* Compile time check ensures memory safety in case SSID length in + * record / result structure definition changes in future */ + _Static_assert(sizeof(result->ssid) == sizeof(record->ssid), + "source and destination should be of same size"); + memcpy(result->ssid, record->ssid, sizeof(record->ssid)); + memcpy(result->bssid, record->bssid, sizeof(record->bssid)); + result->channel = record->primary; + result->rssi = record->rssi; + result->auth = record->authmode; + return ESP_OK; +} +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +static esp_err_t thread_scan_start(bool blocking, uint32_t channel_mask, network_prov_scan_ctx_t **ctx) +{ + return network_prov_mgr_thread_scan_start(blocking, channel_mask); +} + +static esp_err_t thread_scan_status(bool *scan_finished, + uint16_t *result_count, + network_prov_scan_ctx_t **ctx) +{ + *scan_finished = network_prov_mgr_thread_scan_finished(); + *result_count = network_prov_mgr_thread_scan_result_count(); + return ESP_OK; +} + +static esp_err_t thread_scan_result(uint16_t result_index, + network_prov_scan_thread_result_t *result, + network_prov_scan_ctx_t **ctx) +{ + const otActiveScanResult *record = network_prov_mgr_thread_scan_result(result_index); + if (!record) { + return ESP_FAIL; + } + + result->channel = record->mChannel; + result->pan_id = record->mPanId; + result->rssi = record->mRssi; + result->lqi = record->mLqi; + memcpy(result->ext_addr, record->mExtAddress.m8, sizeof(record->mExtAddress.m8)); + + if (record->mDiscover) { + memcpy(result->ext_pan_id, record->mExtendedPanId.m8, sizeof(result->ext_pan_id)); + memcpy(result->network_name, record->mNetworkName.m8, sizeof(record->mNetworkName.m8)); + } else { + memset(result->ext_pan_id, 0, sizeof(result->ext_pan_id)); + memset(result->network_name, 0, sizeof(result->network_name)); + } + + return ESP_OK; +} + +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + +esp_err_t get_network_scan_handlers(network_prov_scan_handlers_t *ptr) +{ + if (!ptr) { + return ESP_ERR_INVALID_ARG; + } +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + ptr->wifi_scan_start = wifi_scan_start; + ptr->wifi_scan_status = wifi_scan_status; + ptr->wifi_scan_result = wifi_scan_result; +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + ptr->thread_scan_start = thread_scan_start; + ptr->thread_scan_status = thread_scan_status; + ptr->thread_scan_result = thread_scan_result; +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + ptr->ctx = NULL; + return ESP_OK; +} + +/*************************************************************************/ + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +static esp_err_t wifi_ctrl_reset(void) +{ + return network_prov_mgr_reset_wifi_sm_state_on_failure(); +} + +static esp_err_t wifi_ctrl_reprov(void) +{ + return network_prov_mgr_reset_wifi_sm_state_for_reprovision(); +} +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +static esp_err_t thread_ctrl_reset(void) +{ + return network_prov_mgr_reset_thread_sm_state_on_failure(); +} + +static esp_err_t thread_ctrl_reprov(void) +{ + return network_prov_mgr_reset_thread_sm_state_for_reprovision(); +} +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + +esp_err_t get_network_ctrl_handlers(network_ctrl_handlers_t *ptr) +{ + if (!ptr) { + return ESP_ERR_INVALID_ARG; + } +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + ptr->wifi_ctrl_reset = wifi_ctrl_reset; + ptr->wifi_ctrl_reprov = wifi_ctrl_reprov; +#endif +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + ptr->thread_ctrl_reset = thread_ctrl_reset; + ptr->thread_ctrl_reprov = thread_ctrl_reprov; +#endif + return ESP_OK; +} diff --git a/components/network_provisioning/src/manager.c b/components/network_provisioning/src/manager.c new file mode 100644 index 0000000..b4ed59a --- /dev/null +++ b/components/network_provisioning/src/manager.c @@ -0,0 +1,2566 @@ +/* + * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "network_provisioning_priv.h" + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +#include +#include +#include +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + +#define NETWORK_PROV_MGR_VERSION "netprov-v1.2" +#define WIFI_PROV_STORAGE_BIT BIT0 +#define WIFI_PROV_SETTING_BIT BIT1 +#define MAX_SCAN_RESULTS CONFIG_NETWORK_PROV_SCAN_MAX_ENTRIES + +#define ACQUIRE_LOCK(mux) assert(xSemaphoreTake(mux, portMAX_DELAY) == pdTRUE) +#define RELEASE_LOCK(mux) assert(xSemaphoreGive(mux) == pdTRUE) + +static const char *TAG = "network_prov_mgr"; + +ESP_EVENT_DEFINE_BASE(NETWORK_PROV_EVENT); +ESP_EVENT_DEFINE_BASE(NETWORK_PROV_MGR_PVT_EVENT); + +typedef enum { + NETWORK_PROV_STATE_IDLE, + NETWORK_PROV_STATE_STARTING, + NETWORK_PROV_STATE_STARTED, +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + NETWORK_PROV_STATE_CRED_RECV, +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + NETWORK_PROV_STATE_DATASET_RECV, +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + NETWORK_PROV_STATE_FAIL, + NETWORK_PROV_STATE_SUCCESS, + NETWORK_PROV_STATE_STOPPING +} network_prov_mgr_state_t; + +typedef enum { + NETWORK_PROV_MGR_STOP, +} network_prov_mgr_pvt_event_t; + +/** + * @brief Structure for storing capabilities supported by + * the provisioning service + */ +struct network_prov_capabilities { + /* Security 0 is used */ + bool no_sec; + + /* Proof of Possession is not required for establishing session */ + bool no_pop; + + /* Provisioning doesn't stop on it's own after receiving Wi-Fi credentials or + * Thread dataset instead application has to explicitly call + * network_prov_mgr_stop_provisioning() */ + bool no_auto_stop; +}; + +/** + * @brief Structure for storing miscellaneous information about + * provisioning service that will be conveyed to clients + */ +struct network_prov_info { + const char *version; + struct network_prov_capabilities capabilities; +}; + +/** + * @brief Context data for provisioning manager + */ +struct network_prov_mgr_ctx { + /* Provisioning manager configuration */ + network_prov_mgr_config_t mgr_config; + + /* State of the provisioning service */ + network_prov_mgr_state_t prov_state; + + /* Provisioning scheme configuration */ + void *prov_scheme_config; + + /* Protocomm handle */ + protocomm_t *pc; + + /* Type of security to use with protocomm */ + int security; + + /* Pointer to security params */ + const void *protocomm_sec_params; + + /* Handle for Provisioning Auto Stop timer */ + esp_timer_handle_t autostop_timer; + + /* Handle for delayed cleanup timer */ + esp_timer_handle_t cleanup_delay_timer; + + /* Protocomm handlers for network configuration endpoint */ + network_prov_config_handlers_t *network_prov_handlers; + + /* Protocomm handlers for network scan endpoint */ + network_prov_scan_handlers_t *network_scan_handlers; + + /* Protocomm handlers for network ctrl endpoint */ + network_ctrl_handlers_t *network_ctrl_handlers; + + /* Count of used endpoint UUIDs */ + unsigned int endpoint_uuid_used; + + /* Provisioning service information */ + struct network_prov_info mgr_info; + + /* Application related information in JSON format */ + cJSON *app_info_json; + + /* Delay after which resources will be cleaned up asynchronously + * upon execution of network_prov_mgr_stop_provisioning() */ + uint32_t cleanup_delay; + /* Scan status */ + bool scanning; +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + /* Handle for delayed wifi connection timer */ + esp_timer_handle_t wifi_connect_timer; + + /* State of Wi-Fi Station */ + network_prov_wifi_sta_state_t wifi_state; + + /* Code for Wi-Fi station disconnection (if disconnected) */ + network_prov_wifi_sta_fail_reason_t wifi_disconnect_reason; + + /* Wi-Fi scan parameters and state variables */ + uint8_t channels_per_group; + uint16_t curr_channel; + uint16_t ap_list_len[14]; // 14 entries corresponding to each channel + wifi_ap_record_t *ap_list[14]; + wifi_ap_record_t *ap_list_sorted[MAX_SCAN_RESULTS]; + wifi_scan_config_t scan_cfg; + + /* Total number of attempts done for connecting to Wi-Fi */ + uint32_t connection_attempts_completed; +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + /* Handle for Thread attaching timeout timer */ + esp_timer_handle_t thread_timeout_timer; + + /* State of Thread */ + network_prov_thread_state_t thread_state; + + /* Code for Thread detached (if detached) */ + network_prov_thread_fail_reason_t thread_detached_reason; + + /* Thread scan parameters and state variables */ + uint16_t scan_result_count; + otActiveScanResult *scan_result[MAX_SCAN_RESULTS]; +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +}; + +/* Mutex to lock/unlock access to provisioning singleton + * context data. This is allocated only once on first init + * and never deleted as network_prov_mgr is a singleton */ +static SemaphoreHandle_t prov_ctx_lock = NULL; + +/* Pointer to provisioning context data */ +static struct network_prov_mgr_ctx *prov_ctx; + +/* This executes registered app_event_callback for a particular event + * + * NOTE : By the time this function returns, it is possible that + * the manager got de-initialized due to a call to network_prov_mgr_deinit() + * either inside the event callbacks or from another thread. Therefore + * post execution of execute_event_cb(), the validity of prov_ctx must + * always be checked. A cleaner way, to avoid this pitfall safely, would + * be to limit the usage of this function to only public APIs, and that + * too at the very end, just before returning. + * + * NOTE: This function should be called only after ensuring that the + * context is valid and the control mutex is locked. */ +static void execute_event_cb(network_prov_cb_event_t event_id, void *event_data, size_t event_data_size) +{ + ESP_LOGD(TAG, "execute_event_cb : %d", event_id); + + if (prov_ctx) { + network_prov_cb_func_t app_cb = prov_ctx->mgr_config.app_event_handler.event_cb; + void *app_data = prov_ctx->mgr_config.app_event_handler.user_data; + + network_prov_cb_func_t scheme_cb = prov_ctx->mgr_config.scheme_event_handler.event_cb; + void *scheme_data = prov_ctx->mgr_config.scheme_event_handler.user_data; + + /* Release the mutex before executing the callbacks. This is done so that + * network_prov_mgr_event_handler() doesn't stay blocked for the duration */ + RELEASE_LOCK(prov_ctx_lock); + + if (scheme_cb) { + /* Call scheme specific event handler */ + scheme_cb(scheme_data, event_id, event_data); + } + + if (app_cb) { + /* Call application specific event handler */ + app_cb(app_data, event_id, event_data); + } + + if (esp_event_post(NETWORK_PROV_EVENT, event_id, + event_data, event_data_size, + portMAX_DELAY) != ESP_OK) { + ESP_LOGE(TAG, "Failed to post event %d to default event loop", event_id); + } + + ACQUIRE_LOCK(prov_ctx_lock); + } +} + +esp_err_t network_prov_mgr_set_app_info(const char *label, const char *version, + const char **capabilities, size_t total_capabilities) +{ + if (!label || !version || !capabilities) { + return ESP_ERR_INVALID_ARG; + } + + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + esp_err_t ret = ESP_FAIL; + ACQUIRE_LOCK(prov_ctx_lock); + + if (prov_ctx && prov_ctx->prov_state == NETWORK_PROV_STATE_IDLE) { + if (!prov_ctx->app_info_json) { + prov_ctx->app_info_json = cJSON_CreateObject(); + } + + cJSON *new_entry_json = cJSON_CreateObject(); + cJSON *capabilities_json = cJSON_CreateArray(); + cJSON_AddItemToObject(prov_ctx->app_info_json, label, new_entry_json); + + /* Version ("ver") */ + cJSON_AddStringToObject(new_entry_json, "ver", version); + + /* List of capabilities ("cap") */ + cJSON_AddItemToObject(new_entry_json, "cap", capabilities_json); + for (unsigned int i = 0; i < total_capabilities; i++) { + if (capabilities[i]) { + cJSON_AddItemToArray(capabilities_json, cJSON_CreateString(capabilities[i])); + } + } + ret = ESP_OK; + } else { + ret = ESP_ERR_INVALID_STATE; + } + + RELEASE_LOCK(prov_ctx_lock); + return ret; +} + +static cJSON *network_prov_get_info_json(void) +{ + cJSON *full_info_json = prov_ctx->app_info_json ? + cJSON_Duplicate(prov_ctx->app_info_json, 1) : cJSON_CreateObject(); + cJSON *prov_info_json = cJSON_CreateObject(); + cJSON *prov_capabilities = cJSON_CreateArray(); + + /* Use label "prov" to indicate provisioning related information */ + cJSON_AddItemToObject(full_info_json, "prov", prov_info_json); + + /* Version field */ + cJSON_AddStringToObject(prov_info_json, "ver", prov_ctx->mgr_info.version); + + /* Security field */ + cJSON_AddNumberToObject(prov_info_json, "sec_ver", prov_ctx->security); +#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_PATCH_VERSION + int sec_ver = 0; + uint8_t sec_patch_ver = 0; + protocomm_get_sec_version(prov_ctx->pc, &sec_ver, &sec_patch_ver); + assert(sec_ver == prov_ctx->security); + cJSON_AddNumberToObject(prov_info_json, "sec_patch_ver", sec_patch_ver); +#endif + + /* Capabilities field */ + cJSON_AddItemToObject(prov_info_json, "cap", prov_capabilities); + + /* If Security / Proof of Possession is not used, indicate in capabilities */ + if (prov_ctx->mgr_info.capabilities.no_sec) { + cJSON_AddItemToArray(prov_capabilities, cJSON_CreateString("no_sec")); + } else if (prov_ctx->mgr_info.capabilities.no_pop) { + cJSON_AddItemToArray(prov_capabilities, cJSON_CreateString("no_pop")); + } + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + /* Indicate capability for performing Wi-Fi provision */ + cJSON_AddItemToArray(prov_capabilities, cJSON_CreateString("wifi_prov")); + /* Indicate capability for performing Wi-Fi scan */ + cJSON_AddItemToArray(prov_capabilities, cJSON_CreateString("wifi_scan")); +#endif +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + /* Indicate capability for performing Thread provision */ + cJSON_AddItemToArray(prov_capabilities, cJSON_CreateString("thread_prov")); + /* Indicate capability for performing Thread scan */ + cJSON_AddItemToArray(prov_capabilities, cJSON_CreateString("thread_scan")); +#endif + return full_info_json; +} + +/* Declare the internal event handler */ +static void network_prov_mgr_event_handler_internal(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data); + +static esp_err_t network_prov_mgr_start_service(const char *service_name, const char *service_key) +{ + const network_prov_scheme_t *scheme = &prov_ctx->mgr_config.scheme; + esp_err_t ret; + + /* Create new protocomm instance */ + prov_ctx->pc = protocomm_new(); + if (prov_ctx->pc == NULL) { + ESP_LOGE(TAG, "Failed to create new protocomm instance"); + return ESP_FAIL; + } + + ret = scheme->set_config_service(prov_ctx->prov_scheme_config, service_name, service_key); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to configure service"); + protocomm_delete(prov_ctx->pc); + return ret; + } + + /* Start provisioning */ + ret = scheme->prov_start(prov_ctx->pc, prov_ctx->prov_scheme_config); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to start service"); + protocomm_delete(prov_ctx->pc); + return ret; + } + + /* Set protocomm security type for endpoint */ + if (prov_ctx->security == 0) { +#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0 + ret = protocomm_set_security(prov_ctx->pc, "prov-session", + &protocomm_security0, NULL); +#else + // Enable SECURITY_VERSION_0 in Protocomm configuration menu + return ESP_ERR_NOT_SUPPORTED; +#endif + } else if (prov_ctx->security == 1) { +#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1 + ret = protocomm_set_security(prov_ctx->pc, "prov-session", + &protocomm_security1, prov_ctx->protocomm_sec_params); +#else + // Enable SECURITY_VERSION_1 in Protocomm configuration menu + return ESP_ERR_NOT_SUPPORTED; +#endif + } else if (prov_ctx->security == 2) { +#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2 + ret = protocomm_set_security(prov_ctx->pc, "prov-session", + &protocomm_security2, prov_ctx->protocomm_sec_params); +#else + // Enable SECURITY_VERSION_2 in Protocomm configuration menu + return ESP_ERR_NOT_SUPPORTED; +#endif + } else { + ESP_LOGE(TAG, "Unsupported protocomm security version %d", prov_ctx->security); + ret = ESP_ERR_INVALID_ARG; + } + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set security endpoint"); + scheme->prov_stop(prov_ctx->pc); + protocomm_delete(prov_ctx->pc); + return ret; + } + + /* Set version information / capabilities of provisioning service and application */ + cJSON *version_json = network_prov_get_info_json(); + char *version_str = cJSON_Print(version_json); + ret = protocomm_set_version(prov_ctx->pc, "proto-ver", version_str); + free(version_str); + cJSON_Delete(version_json); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set version endpoint"); + scheme->prov_stop(prov_ctx->pc); + protocomm_delete(prov_ctx->pc); + return ret; + } + + prov_ctx->network_prov_handlers = malloc(sizeof(network_prov_config_handlers_t)); + ret = get_network_prov_handlers(prov_ctx->network_prov_handlers); + if (ret != ESP_OK) { + ESP_LOGD(TAG, "Failed to allocate memory for provisioning handlers"); + scheme->prov_stop(prov_ctx->pc); + protocomm_delete(prov_ctx->pc); + return ESP_ERR_NO_MEM; + } + + /* Add protocomm endpoint for network configuration */ + ret = protocomm_add_endpoint(prov_ctx->pc, "prov-config", + network_prov_config_data_handler, + prov_ctx->network_prov_handlers); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set provisioning endpoint"); + free(prov_ctx->network_prov_handlers); + scheme->prov_stop(prov_ctx->pc); + protocomm_delete(prov_ctx->pc); + return ret; + } + + prov_ctx->network_scan_handlers = malloc(sizeof(network_prov_scan_handlers_t)); + ret = get_network_scan_handlers(prov_ctx->network_scan_handlers); + if (ret != ESP_OK) { + ESP_LOGD(TAG, "Failed to allocate memory for network scan handlers"); + free(prov_ctx->network_prov_handlers); + scheme->prov_stop(prov_ctx->pc); + protocomm_delete(prov_ctx->pc); + return ESP_ERR_NO_MEM; + } + + /* Add endpoint for scanning networks and sending scan list */ + ret = protocomm_add_endpoint(prov_ctx->pc, "prov-scan", + network_prov_scan_handler, + prov_ctx->network_scan_handlers); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set network scan endpoint"); + free(prov_ctx->network_scan_handlers); + free(prov_ctx->network_prov_handlers); + scheme->prov_stop(prov_ctx->pc); + protocomm_delete(prov_ctx->pc); + return ret; + } + + prov_ctx->network_ctrl_handlers = malloc(sizeof(network_ctrl_handlers_t)); + ret = get_network_ctrl_handlers(prov_ctx->network_ctrl_handlers); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to allocate memory for network ctrl handlers"); + free(prov_ctx->network_prov_handlers); + scheme->prov_stop(prov_ctx->pc); + protocomm_delete(prov_ctx->pc); + return ESP_ERR_NO_MEM; + } + + /* Add endpoint for controlling state of network provisioning */ + ret = protocomm_add_endpoint(prov_ctx->pc, "prov-ctrl", + network_ctrl_handler, + prov_ctx->network_ctrl_handlers); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set network ctrl endpoint"); + free(prov_ctx->network_ctrl_handlers); + free(prov_ctx->network_prov_handlers); + scheme->prov_stop(prov_ctx->pc); + protocomm_delete(prov_ctx->pc); + return ret; + } + + /* Register global event handler */ +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + ret = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, + network_prov_mgr_event_handler_internal, NULL); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to register WiFi event handler"); + free(prov_ctx->network_scan_handlers); + free(prov_ctx->network_ctrl_handlers); + free(prov_ctx->network_prov_handlers); + scheme->prov_stop(prov_ctx->pc); + protocomm_delete(prov_ctx->pc); + return ret; + } + + ret = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, + network_prov_mgr_event_handler_internal, NULL); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to register IP event handler"); + esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, + network_prov_mgr_event_handler_internal); + free(prov_ctx->network_scan_handlers); + free(prov_ctx->network_ctrl_handlers); + free(prov_ctx->network_prov_handlers); + scheme->prov_stop(prov_ctx->pc); + protocomm_delete(prov_ctx->pc); + return ret; + } +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + ret = esp_event_handler_register(OPENTHREAD_EVENT, ESP_EVENT_ANY_ID, + network_prov_mgr_event_handler_internal, NULL); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to register OpenThread event handler"); + free(prov_ctx->network_scan_handlers); + free(prov_ctx->network_ctrl_handlers); + free(prov_ctx->network_prov_handlers); + scheme->prov_stop(prov_ctx->pc); + protocomm_delete(prov_ctx->pc); + return ret; + } +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + + ret = esp_event_handler_register(NETWORK_PROV_MGR_PVT_EVENT, NETWORK_PROV_MGR_STOP, + network_prov_mgr_event_handler_internal, NULL); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to register provisioning event handler"); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, + network_prov_mgr_event_handler_internal); + esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, + network_prov_mgr_event_handler_internal); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + esp_event_handler_unregister(OPENTHREAD_EVENT, ESP_EVENT_ANY_ID, + network_prov_mgr_event_handler_internal); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + free(prov_ctx->network_scan_handlers); + free(prov_ctx->network_ctrl_handlers); + free(prov_ctx->network_prov_handlers); + scheme->prov_stop(prov_ctx->pc); + protocomm_delete(prov_ctx->pc); + return ret; + } + + + ESP_LOGI(TAG, "Provisioning started with service name : %s ", + service_name ? service_name : ""); + return ESP_OK; +} + +esp_err_t network_prov_mgr_endpoint_create(const char *ep_name) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + esp_err_t err = ESP_FAIL; + + ACQUIRE_LOCK(prov_ctx_lock); + if (prov_ctx && + prov_ctx->prov_state == NETWORK_PROV_STATE_IDLE) { + err = prov_ctx->mgr_config.scheme.set_config_endpoint( + prov_ctx->prov_scheme_config, ep_name, + prov_ctx->endpoint_uuid_used + 1); + } + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to create additional endpoint"); + } else { + prov_ctx->endpoint_uuid_used++; + } + RELEASE_LOCK(prov_ctx_lock); + return err; +} + +esp_err_t network_prov_mgr_endpoint_register(const char *ep_name, protocomm_req_handler_t handler, void *user_ctx) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + esp_err_t err = ESP_FAIL; + + ACQUIRE_LOCK(prov_ctx_lock); + if (prov_ctx && + prov_ctx->prov_state > NETWORK_PROV_STATE_STARTING && + prov_ctx->prov_state < NETWORK_PROV_STATE_STOPPING) { + err = protocomm_add_endpoint(prov_ctx->pc, ep_name, handler, user_ctx); + } + RELEASE_LOCK(prov_ctx_lock); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to register handler for endpoint"); + } + return err; +} + +void network_prov_mgr_endpoint_unregister(const char *ep_name) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (prov_ctx && + prov_ctx->prov_state > NETWORK_PROV_STATE_STARTING && + prov_ctx->prov_state < NETWORK_PROV_STATE_STOPPING) { + protocomm_remove_endpoint(prov_ctx->pc, ep_name); + } + RELEASE_LOCK(prov_ctx_lock); +} + +static void prov_stop_and_notify(bool is_async) +{ + esp_event_handler_unregister(NETWORK_PROV_MGR_PVT_EVENT, NETWORK_PROV_MGR_STOP, + network_prov_mgr_event_handler_internal); + + if (prov_ctx->cleanup_delay_timer) { + esp_timer_stop(prov_ctx->cleanup_delay_timer); + esp_timer_delete(prov_ctx->cleanup_delay_timer); + prov_ctx->cleanup_delay_timer = NULL; + } + + network_prov_cb_func_t app_cb = prov_ctx->mgr_config.app_event_handler.event_cb; + void *app_data = prov_ctx->mgr_config.app_event_handler.user_data; + + network_prov_cb_func_t scheme_cb = prov_ctx->mgr_config.scheme_event_handler.event_cb; + void *scheme_data = prov_ctx->mgr_config.scheme_event_handler.user_data; + + /* This delay is so that the client side app is notified first + * and then the provisioning is stopped. Generally 1000ms is enough. */ + if (!is_async) { + uint32_t cleanup_delay = prov_ctx->cleanup_delay > 100 ? prov_ctx->cleanup_delay : 100; + vTaskDelay(cleanup_delay / portTICK_PERIOD_MS); + } + + protocomm_remove_endpoint(prov_ctx->pc, "prov-ctrl"); + + protocomm_remove_endpoint(prov_ctx->pc, "prov-scan"); + + protocomm_remove_endpoint(prov_ctx->pc, "prov-config"); + + protocomm_unset_security(prov_ctx->pc, "prov-session"); + + protocomm_unset_version(prov_ctx->pc, "proto-ver"); + + /* All the extra application added endpoints are also + * removed automatically when prov_stop is called */ + prov_ctx->mgr_config.scheme.prov_stop(prov_ctx->pc); + + /* Delete protocomm instance */ + protocomm_delete(prov_ctx->pc); + prov_ctx->pc = NULL; + + /* Free provisioning handlers */ + free(prov_ctx->network_prov_handlers->ctx); + free(prov_ctx->network_prov_handlers); + prov_ctx->network_prov_handlers = NULL; + + free(prov_ctx->network_scan_handlers->ctx); + free(prov_ctx->network_scan_handlers); + prov_ctx->network_scan_handlers = NULL; + + free(prov_ctx->network_ctrl_handlers); + prov_ctx->network_ctrl_handlers = NULL; +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + /* Switch device to Wi-Fi STA mode irrespective of + * whether provisioning was completed or not */ + esp_wifi_set_mode(WIFI_MODE_STA); +#endif + + ESP_LOGI(TAG, "Provisioning stopped"); + + if (is_async) { + /* NOTE: While calling this API in an async fashion, + * the context lock prov_ctx_lock has already been taken + */ + prov_ctx->prov_state = NETWORK_PROV_STATE_IDLE; + + ESP_LOGD(TAG, "execute_event_cb : %d", NETWORK_PROV_END); + if (scheme_cb) { + scheme_cb(scheme_data, NETWORK_PROV_END, NULL); + } + if (app_cb) { + app_cb(app_data, NETWORK_PROV_END, NULL); + } + if (esp_event_post(NETWORK_PROV_EVENT, NETWORK_PROV_END, NULL, 0, portMAX_DELAY) != ESP_OK) { + ESP_LOGE(TAG, "Failed to post event THREAD_PROV_END"); + } + } +} + +/* This will do one of these: + * 1) if blocking is false, start a cleanup timer for stopping the provisioning service (returns true) + * 2) if blocking is true, stop provisioning service immediately (returns true) + * 3) if service was already in the process of termination, in blocking mode this will + * wait till the service is stopped (returns false) + * 4) if service was not running, this will return immediately (returns false) + * + * NOTE: This function should be called only after ensuring that the context + * is valid and the control mutex is locked + * + * NOTE: When blocking mode is selected, the event callbacks are not executed. + * This help with de-initialization. + */ +static bool network_prov_mgr_stop_service(bool blocking) +{ + if (blocking) { + /* Wait for any ongoing calls to network_prov_mgr_start_service() or + * network_prov_mgr_stop_service() from another thread to finish */ + while (prov_ctx && ( + prov_ctx->prov_state == NETWORK_PROV_STATE_STARTING || + prov_ctx->prov_state == NETWORK_PROV_STATE_STOPPING)) { + RELEASE_LOCK(prov_ctx_lock); + vTaskDelay(100 / portTICK_PERIOD_MS); + ACQUIRE_LOCK(prov_ctx_lock); + } + } else { + /* Wait for any ongoing call to network_prov_mgr_start_service() + * from another thread to finish */ + while (prov_ctx && + prov_ctx->prov_state == NETWORK_PROV_STATE_STARTING) { + RELEASE_LOCK(prov_ctx_lock); + vTaskDelay(100 / portTICK_PERIOD_MS); + ACQUIRE_LOCK(prov_ctx_lock); + } + + if (prov_ctx && prov_ctx->prov_state == NETWORK_PROV_STATE_STOPPING) { + ESP_LOGD(TAG, "Provisioning is already stopping"); + return false; + } + } + + if (!prov_ctx || prov_ctx->prov_state == NETWORK_PROV_STATE_IDLE) { + ESP_LOGD(TAG, "Provisioning not running"); + return false; + } + + /* Timers not needed anymore */ + if (prov_ctx->autostop_timer) { + esp_timer_stop(prov_ctx->autostop_timer); + esp_timer_delete(prov_ctx->autostop_timer); + prov_ctx->autostop_timer = NULL; + } +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + if (prov_ctx->wifi_connect_timer) { + esp_timer_stop(prov_ctx->wifi_connect_timer); + esp_timer_delete(prov_ctx->wifi_connect_timer); + prov_ctx->wifi_connect_timer = NULL; + } +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + if (prov_ctx->thread_timeout_timer) { + esp_timer_stop(prov_ctx->thread_timeout_timer); + esp_timer_delete(prov_ctx->thread_timeout_timer); + prov_ctx->thread_timeout_timer = NULL; + } +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + + ESP_LOGD(TAG, "Stopping provisioning"); + prov_ctx->prov_state = NETWORK_PROV_STATE_STOPPING; + + /* Free proof of possession */ + if (prov_ctx->protocomm_sec_params) { + if (prov_ctx->security == 1) { + // In case of security 1 we keep an internal copy of "pop". + // Hence free it at this point + uint8_t *pop = (uint8_t *)((protocomm_security1_params_t *) prov_ctx->protocomm_sec_params)->data; + free(pop); + } + prov_ctx->protocomm_sec_params = NULL; + } + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + /* Delete all scan results */ + for (uint16_t channel = 0; channel < 14; channel++) { + free(prov_ctx->ap_list[channel]); + prov_ctx->ap_list[channel] = NULL; + } + prov_ctx->scanning = false; + for (uint8_t i = 0; i < MAX_SCAN_RESULTS; i++) { + prov_ctx->ap_list_sorted[i] = NULL; + } + + /* Remove event handler */ + esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, + network_prov_mgr_event_handler_internal); + esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, + network_prov_mgr_event_handler_internal); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + /* Delete all scan results */ + prov_ctx->scanning = false; + for (uint8_t i = 0; i < MAX_SCAN_RESULTS; ++i) { + if (prov_ctx->scan_result[i]) { + free(prov_ctx->scan_result[i]); + } + prov_ctx->scan_result[i] = NULL; + } + prov_ctx->scan_result_count = 0; + + /* Remove event handler */ + esp_event_handler_unregister(OPENTHREAD_EVENT, ESP_EVENT_ANY_ID, + network_prov_mgr_event_handler_internal); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + + if (blocking) { + /* Run the cleanup without launching a separate task. Also the + * NETWORK_PROV_END event is not emitted in this case */ + RELEASE_LOCK(prov_ctx_lock); + prov_stop_and_notify(false); + ACQUIRE_LOCK(prov_ctx_lock); + prov_ctx->prov_state = NETWORK_PROV_STATE_IDLE; + } else { + /* Launch cleanup timer to perform the cleanup asynchronously. + * It is important to do this asynchronously because, there are + * situations in which the transport level resources have to be + * released - some duration after - returning from a call to + * network_prov_mgr_stop_provisioning(), like when it is called + * inside a protocomm handler */ + uint64_t cleanup_delay_ms = prov_ctx->cleanup_delay > 100 ? prov_ctx->cleanup_delay : 100; + esp_timer_start_once(prov_ctx->cleanup_delay_timer, cleanup_delay_ms * 1000U); + ESP_LOGD(TAG, "Provisioning scheduled for stopping"); + } + return true; +} + +/* Task spawned by timer callback */ +static void stop_prov_timer_cb(void *arg) +{ + network_prov_mgr_stop_provisioning(); +} + +static void cleanup_delay_timer_cb(void *arg) +{ + esp_err_t ret = esp_event_post(NETWORK_PROV_MGR_PVT_EVENT, NETWORK_PROV_MGR_STOP, NULL, 0, pdMS_TO_TICKS(100)); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to post NETWORK_PROV_MGR_STOP event! %d %s", ret, esp_err_to_name(ret)); + } +} + +esp_err_t network_prov_mgr_disable_auto_stop(uint32_t cleanup_delay) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + esp_err_t ret = ESP_FAIL; + ACQUIRE_LOCK(prov_ctx_lock); + + if (prov_ctx && prov_ctx->prov_state == NETWORK_PROV_STATE_IDLE) { + prov_ctx->mgr_info.capabilities.no_auto_stop = true; + prov_ctx->cleanup_delay = cleanup_delay; + ret = ESP_OK; + } else { + ret = ESP_ERR_INVALID_STATE; + } + + RELEASE_LOCK(prov_ctx_lock); + return ret; +} + +/* Call this if provisioning is completed before the timeout occurs */ +esp_err_t network_prov_mgr_done(void) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + bool auto_stop_enabled = false; + ACQUIRE_LOCK(prov_ctx_lock); + if (prov_ctx && !prov_ctx->mgr_info.capabilities.no_auto_stop) { + auto_stop_enabled = true; + } + RELEASE_LOCK(prov_ctx_lock); + + /* Stop provisioning if auto stop is enabled */ + if (auto_stop_enabled) { + network_prov_mgr_stop_provisioning(); + } + return ESP_OK; +} + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +static esp_err_t update_wifi_scan_results(void) +{ + if (!prov_ctx->scanning) { + return ESP_ERR_INVALID_STATE; + } + ESP_LOGD(TAG, "Scan finished"); + + esp_err_t ret = ESP_FAIL; + uint16_t count = 0; + uint16_t curr_channel = prov_ctx->curr_channel; + + if (prov_ctx->ap_list[curr_channel]) { + free(prov_ctx->ap_list[curr_channel]); + prov_ctx->ap_list[curr_channel] = NULL; + prov_ctx->ap_list_len[curr_channel] = 0; + } + + if (esp_wifi_scan_get_ap_num(&count) != ESP_OK) { + ESP_LOGE(TAG, "Failed to get count of scanned APs"); + goto exit; + } + + if (!count) { + ESP_LOGD(TAG, "Scan result empty"); + ret = ESP_OK; + goto exit; + } + + uint16_t get_count = MIN(count, MAX_SCAN_RESULTS); + prov_ctx->ap_list[curr_channel] = (wifi_ap_record_t *) calloc(get_count, sizeof(wifi_ap_record_t)); + if (!prov_ctx->ap_list[curr_channel]) { + ESP_LOGE(TAG, "Failed to allocate memory for AP list"); + esp_wifi_clear_ap_list(); + goto exit; + } + if (esp_wifi_scan_get_ap_records(&get_count, prov_ctx->ap_list[curr_channel]) != ESP_OK) { + ESP_LOGE(TAG, "Failed to get scanned AP records"); + goto exit; + } + prov_ctx->ap_list_len[curr_channel] = get_count; + + if (prov_ctx->channels_per_group) { + ESP_LOGD(TAG, "Scan results for channel %d :", curr_channel); + } else { + ESP_LOGD(TAG, "Scan results :"); + } + ESP_LOGD(TAG, "\tS.N. %-32s %-12s %s %s", "SSID", "BSSID", "RSSI", "AUTH"); + for (uint8_t i = 0; i < prov_ctx->ap_list_len[curr_channel]; i++) { + ESP_LOGD(TAG, "\t[%2d] %-32s %02x%02x%02x%02x%02x%02x %4d %4d", i, + prov_ctx->ap_list[curr_channel][i].ssid, + prov_ctx->ap_list[curr_channel][i].bssid[0], + prov_ctx->ap_list[curr_channel][i].bssid[1], + prov_ctx->ap_list[curr_channel][i].bssid[2], + prov_ctx->ap_list[curr_channel][i].bssid[3], + prov_ctx->ap_list[curr_channel][i].bssid[4], + prov_ctx->ap_list[curr_channel][i].bssid[5], + prov_ctx->ap_list[curr_channel][i].rssi, + prov_ctx->ap_list[curr_channel][i].authmode); + } + + /* Store results in sorted list */ + { + int rc = get_count; + int is = MAX_SCAN_RESULTS - rc - 1; + while (rc > 0 && is >= 0) { + if (prov_ctx->ap_list_sorted[is]) { + if (prov_ctx->ap_list_sorted[is]->rssi > prov_ctx->ap_list[curr_channel][rc - 1].rssi) { + prov_ctx->ap_list_sorted[is + rc] = &prov_ctx->ap_list[curr_channel][rc - 1]; + rc--; + continue; + } + prov_ctx->ap_list_sorted[is + rc] = prov_ctx->ap_list_sorted[is]; + } + is--; + } + while (rc > 0) { + prov_ctx->ap_list_sorted[rc - 1] = &prov_ctx->ap_list[curr_channel][rc - 1]; + rc--; + } + } + + ret = ESP_OK; +exit: + + if (!prov_ctx->channels_per_group) { + /* All channel scan was performed + * so nothing more to do */ + prov_ctx->scanning = false; + goto final; + } + + curr_channel = prov_ctx->curr_channel = (prov_ctx->curr_channel + 1) % 14; + if (ret != ESP_OK || curr_channel == 0) { + prov_ctx->scanning = false; + goto final; + } + + if ((curr_channel % prov_ctx->channels_per_group) == 0) { + vTaskDelay(120 / portTICK_PERIOD_MS); + } + + ESP_LOGD(TAG, "Scan starting on channel %u...", curr_channel); + prov_ctx->scan_cfg.channel = curr_channel; + ret = esp_wifi_scan_start(&prov_ctx->scan_cfg, false); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to start scan"); + prov_ctx->scanning = false; + goto final; + } + ESP_LOGD(TAG, "Scan started"); + +final: + + return ret; +} + +esp_err_t network_prov_mgr_wifi_scan_start(bool blocking, bool passive, + uint8_t group_channels, uint32_t period_ms) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + ACQUIRE_LOCK(prov_ctx_lock); + + if (!prov_ctx) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_ERR_INVALID_STATE; + } + + if (prov_ctx->scanning) { + ESP_LOGD(TAG, "Scan already running"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_OK; + } + + /* Clear sorted list for new entries */ + for (uint8_t i = 0; i < MAX_SCAN_RESULTS; i++) { + prov_ctx->ap_list_sorted[i] = NULL; + } + + if (passive) { + prov_ctx->scan_cfg.scan_type = WIFI_SCAN_TYPE_PASSIVE; + /* We do not recommend scan configuration modification in Wi-Fi and BT coexistence mode */ +#if !CONFIG_BT_ENABLED + prov_ctx->scan_cfg.scan_time.passive = period_ms; +#endif + } else { + prov_ctx->scan_cfg.scan_type = WIFI_SCAN_TYPE_ACTIVE; + /* We do not recommend scan configuration modification in Wi-Fi and BT coexistence mode */ +#if !CONFIG_BT_ENABLED + prov_ctx->scan_cfg.scan_time.active.min = period_ms; + prov_ctx->scan_cfg.scan_time.active.max = period_ms; +#endif + } + prov_ctx->channels_per_group = group_channels; + + if (prov_ctx->channels_per_group) { + ESP_LOGD(TAG, "Scan starting on channel 1..."); + prov_ctx->scan_cfg.channel = 1; + } else { + ESP_LOGD(TAG, "Scan starting..."); + prov_ctx->scan_cfg.channel = 0; + } + + if (esp_wifi_scan_start(&prov_ctx->scan_cfg, false) != ESP_OK) { + ESP_LOGE(TAG, "Failed to start scan"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + + ESP_LOGD(TAG, "Scan started"); + prov_ctx->scanning = true; + prov_ctx->curr_channel = prov_ctx->scan_cfg.channel; + RELEASE_LOCK(prov_ctx_lock); + + /* If scan is to be non-blocking, return immediately */ + if (!blocking) { + return ESP_OK; + } + + /* Loop till scan is complete */ + bool scanning = true; + while (scanning) { + ACQUIRE_LOCK(prov_ctx_lock); + scanning = (prov_ctx && prov_ctx->scanning); + RELEASE_LOCK(prov_ctx_lock); + + /* 120ms delay is sufficient for Wi-Fi beacons to be sent */ + vTaskDelay(120 / portTICK_PERIOD_MS); + } + return ESP_OK; +} + +bool network_prov_mgr_wifi_scan_finished(void) +{ + bool scan_finished = true; + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return scan_finished; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (!prov_ctx) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + RELEASE_LOCK(prov_ctx_lock); + return scan_finished; + } + + scan_finished = !prov_ctx->scanning; + RELEASE_LOCK(prov_ctx_lock); + return scan_finished; +} + +static bool wifi_scan_result_is_hidden(const wifi_ap_record_t *ap) +{ + if (!ap) { + return true; + } + + return (strnlen((const char *)ap->ssid, sizeof(ap->ssid)) == 0); +} + +uint16_t network_prov_mgr_wifi_scan_result_count(void) +{ + uint16_t rval = 0; + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return rval; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (!prov_ctx) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + RELEASE_LOCK(prov_ctx_lock); + return rval; + } + + uint16_t idx = 0; + while (idx < MAX_SCAN_RESULTS) { + const wifi_ap_record_t *ap = prov_ctx->ap_list_sorted[idx]; + if (!ap) { + break; + } + if (!wifi_scan_result_is_hidden(ap)) { + rval++; + } + idx++; + } + RELEASE_LOCK(prov_ctx_lock); + return rval; +} + +const wifi_ap_record_t *network_prov_mgr_wifi_scan_result(uint16_t index) +{ + const wifi_ap_record_t *rval = NULL; + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return rval; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (!prov_ctx) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + RELEASE_LOCK(prov_ctx_lock); + return rval; + } + + uint16_t visible_idx = 0; + for (uint16_t idx = 0; idx < MAX_SCAN_RESULTS; idx++) { + const wifi_ap_record_t *candidate = prov_ctx->ap_list_sorted[idx]; + if (!candidate) { + break; + } + if (wifi_scan_result_is_hidden(candidate)) { + continue; + } + if (visible_idx == index) { + rval = candidate; + break; + } + visible_idx++; + } + RELEASE_LOCK(prov_ctx_lock); + return rval; +} + +esp_err_t network_prov_mgr_get_wifi_state(network_prov_wifi_sta_state_t *state) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (prov_ctx == NULL || state == NULL) { + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + + *state = prov_ctx->wifi_state; + RELEASE_LOCK(prov_ctx_lock); + return ESP_OK; +} + +esp_err_t network_prov_mgr_get_wifi_remaining_conn_attempts(uint32_t *attempts_remaining) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (prov_ctx == NULL || attempts_remaining == NULL) { + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + + *attempts_remaining = + prov_ctx->mgr_config.network_prov_wifi_conn_cfg.wifi_conn_attempts - prov_ctx->connection_attempts_completed; + RELEASE_LOCK(prov_ctx_lock); + return ESP_OK; +} + +esp_err_t network_prov_mgr_get_wifi_disconnect_reason(network_prov_wifi_sta_fail_reason_t *reason) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (prov_ctx == NULL || reason == NULL) { + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + + if (prov_ctx->wifi_state != NETWORK_PROV_WIFI_STA_DISCONNECTED) { + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + + *reason = prov_ctx->wifi_disconnect_reason; + RELEASE_LOCK(prov_ctx_lock); + return ESP_OK; +} + +static void debug_print_wifi_credentials(wifi_sta_config_t sta, const char *pretext) +{ + size_t passlen = strlen((const char *) sta.password); + ESP_LOGD(TAG, "%s Wi-Fi SSID : %.*s", pretext, + strnlen((const char *) sta.ssid, sizeof(sta.ssid)), (const char *) sta.ssid); + + if (passlen) { + /* Mask password partially if longer than 3, else mask it completely */ + memset(sta.password + (passlen > 3), '*', passlen - 2 * (passlen > 3)); + ESP_LOGD(TAG, "%s Wi-Fi Password : %s", pretext, (const char *) sta.password); + } +} + +esp_err_t network_prov_mgr_is_wifi_provisioned(bool *provisioned) +{ + if (!provisioned) { + return ESP_ERR_INVALID_ARG; + } + + *provisioned = false; + + /* Get Wi-Fi Station configuration */ + wifi_config_t wifi_cfg; + if (esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) { + return ESP_FAIL; + } + + if (strlen((const char *) wifi_cfg.sta.ssid)) { + *provisioned = true; + debug_print_wifi_credentials(wifi_cfg.sta, "Found"); + } + return ESP_OK; +} + +bool network_prov_mgr_is_sm_idle(void) +{ + return (prov_ctx->prov_state == NETWORK_PROV_STATE_IDLE); +} + +static void wifi_connect_timer_cb(void *arg) +{ + if (esp_wifi_connect() != ESP_OK) { + ESP_LOGE(TAG, "Failed to connect Wi-Fi"); + } +} + +esp_err_t network_prov_mgr_configure_wifi_sta(wifi_config_t *wifi_cfg) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (!prov_ctx) { + ESP_LOGE(TAG, "Invalid state of Provisioning app"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + + execute_event_cb(NETWORK_PROV_SET_WIFI_STA_CONFIG, (void *)wifi_cfg, sizeof(wifi_config_t)); + + if (prov_ctx->prov_state >= NETWORK_PROV_STATE_CRED_RECV) { + ESP_LOGE(TAG, "Wi-Fi credentials already received by provisioning app"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + debug_print_wifi_credentials(wifi_cfg->sta, "Received"); + + /* Configure Wi-Fi as both AP and/or Station */ + if (esp_wifi_set_mode(prov_ctx->mgr_config.scheme.wifi_mode) != ESP_OK) { + ESP_LOGE(TAG, "Failed to set Wi-Fi mode"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + + /* Don't release mutex yet as it is possible that right after + * esp_wifi_connect() is called below, the related Wi-Fi event + * happens even before manager state is updated in the next + * few lines causing the internal event handler to miss */ + + /* Set Wi-Fi storage again to flash to keep the newly + * provided credentials on NVS */ + if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) != ESP_OK) { + ESP_LOGE(TAG, "Failed to set storage Wi-Fi"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + /* Configure Wi-Fi station with host credentials + * provided during provisioning */ + if (esp_wifi_set_config(WIFI_IF_STA, wifi_cfg) != ESP_OK) { + ESP_LOGE(TAG, "Failed to set Wi-Fi configuration"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + /* Connect to AP after one second so that the response can + * be sent to the client successfully, before a channel change happens*/ + if (esp_timer_start_once(prov_ctx->wifi_connect_timer, 1000 * 1000U) != ESP_OK) { + ESP_LOGE(TAG, "Failed to start Wi-Fi connect timer"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + + /* Reset Wi-Fi station state for provisioning app */ + prov_ctx->wifi_state = NETWORK_PROV_WIFI_STA_CONNECTING; + /* Reset connection attempts counter for new credentials */ + prov_ctx->connection_attempts_completed = 0; + prov_ctx->prov_state = NETWORK_PROV_STATE_CRED_RECV; + /* Execute user registered callback handler */ + execute_event_cb(NETWORK_PROV_WIFI_CRED_RECV, (void *)&wifi_cfg->sta, sizeof(wifi_cfg->sta)); + RELEASE_LOCK(prov_ctx_lock); + + return ESP_OK; +} +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +static char hex_byte_to_char(uint8_t byte) +{ + byte = byte & 0x0f; + if (byte <= 9) { + return '0' + byte; + } + return 'A' + byte - 10; +} + +static bool hex_to_string(uint8_t *hex, size_t hex_len, char *str, size_t str_len) +{ + if (hex_len * 2 < str_len) { + for (size_t i = 0; i < hex_len; ++i) { + str[2 * i + 1] = hex_byte_to_char(hex[i]); + str[2 * i] = hex_byte_to_char(hex[i] >> 4); + } + str[hex_len * 2] = 0; + return true; + } + return false; +} + +static void debug_print_thread_dataset(otOperationalDataset *dataset) +{ + if (dataset) { + ESP_LOGD(TAG, "Thread Network Name: %s", dataset->mNetworkName.m8); + char str[33]; + if (hex_to_string(dataset->mNetworkKey.m8, sizeof(dataset->mNetworkKey.m8), str, sizeof(str))) { + ESP_LOGD(TAG, "Thread Network Key: %s", str); + } + if (hex_to_string(dataset->mExtendedPanId.m8, sizeof(dataset->mExtendedPanId.m8), str, sizeof(str))) { + ESP_LOGD(TAG, "Thread Extended PAN ID: %s", str); + } + ESP_LOGD(TAG, "Thread Channel: %d", dataset->mChannel); + ESP_LOGD(TAG, "Thread PAN ID: %d", dataset->mPanId); + } +} + +static void thread_timeout_timer_cb(void *arg) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (!prov_ctx) { + ESP_LOGE(TAG, "Invalid state of Provisioning app"); + RELEASE_LOCK(prov_ctx_lock); + return; + } + + prov_ctx->thread_state = NETWORK_PROV_THREAD_DETACHED; + prov_ctx->prov_state = NETWORK_PROV_STATE_FAIL; + prov_ctx->thread_detached_reason = NETWORK_PROV_THREAD_NETWORK_NOT_FOUND; + execute_event_cb(NETWORK_PROV_THREAD_DATASET_FAIL, (void *)&prov_ctx->thread_detached_reason, + sizeof(prov_ctx->thread_detached_reason)); + RELEASE_LOCK(prov_ctx_lock); + return; +} + + +static esp_err_t set_thread_enable(bool val) +{ + esp_openthread_lock_acquire(portMAX_DELAY); + otInstance *instance = esp_openthread_get_instance(); + bool is_enabled = (otThreadGetDeviceRole(instance) != OT_DEVICE_ROLE_DISABLED); + bool is_ip6_enabled = otIp6IsEnabled(instance); + if (val && !is_ip6_enabled) { + if (otIp6SetEnabled(instance, val) != OT_ERROR_NONE) { + esp_openthread_lock_release(); + return ESP_FAIL; + } + } + if (val != is_enabled) { + if (otThreadSetEnabled(instance, val) != OT_ERROR_NONE) { + esp_openthread_lock_release(); + return ESP_FAIL; + } + } + if (!val && is_ip6_enabled) { + if (otIp6SetEnabled(instance, val) != OT_ERROR_NONE) { + esp_openthread_lock_release(); + return ESP_FAIL; + } + } + esp_openthread_lock_release(); + return ESP_OK; +} + +static esp_err_t set_thread_dataset(otOperationalDatasetTlvs *dataset_tlvs) +{ + otError err = OT_ERROR_NONE; + esp_openthread_lock_acquire(portMAX_DELAY); + otInstance *instance = esp_openthread_get_instance(); + err = otDatasetSetActiveTlvs(instance, dataset_tlvs); + esp_openthread_lock_release(); + return err == OT_ERROR_NONE ? ESP_OK : ESP_FAIL; +} + +static void update_thread_scan_result(otActiveScanResult *result, void *context) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return; + } + ACQUIRE_LOCK(prov_ctx_lock); + + if (!prov_ctx) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + RELEASE_LOCK(prov_ctx_lock); + return; + } + + if (!prov_ctx->scanning) { + ESP_LOGD(TAG, "Scan not running"); + RELEASE_LOCK(prov_ctx_lock); + return; + } + + if (!result) { + prov_ctx->scanning = false; + ESP_LOGD(TAG, "Scan finished"); + RELEASE_LOCK(prov_ctx_lock); + return; + } + + otActiveScanResult *new_result = (otActiveScanResult *)malloc(sizeof(otActiveScanResult)); + if (!new_result) { + ESP_LOGE(TAG, "Failed to allocate memory for Thread scan result"); + RELEASE_LOCK(prov_ctx_lock); + return; + } + memcpy(new_result, result, sizeof(otActiveScanResult)); + + if (prov_ctx->scan_result_count < MAX_SCAN_RESULTS) { + prov_ctx->scan_result[prov_ctx->scan_result_count] = new_result; + prov_ctx->scan_result_count++; + } + RELEASE_LOCK(prov_ctx_lock); +} + +esp_err_t network_prov_mgr_thread_scan_start(bool blocking, uint32_t channel_mask) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + ACQUIRE_LOCK(prov_ctx_lock); + + if (!prov_ctx) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_ERR_INVALID_STATE; + } + + if (prov_ctx->scanning) { + ESP_LOGD(TAG, "Scan already running"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_OK; + } + + /* Clear sorted list for new entries */ + for (uint8_t i = 0; i < MAX_SCAN_RESULTS; i++) { + if (prov_ctx->scan_result[i]) { + free(prov_ctx->scan_result[i]); + prov_ctx->scan_result[i] = 0; + } + } + prov_ctx->scan_result_count = 0; + + esp_openthread_lock_acquire(portMAX_DELAY); + otInstance *instance = esp_openthread_get_instance(); + // Make netif enabled before start scanning + if (!otIp6IsEnabled(instance)) { + if (otIp6SetEnabled(instance, true) != OT_ERROR_NONE) { + ESP_LOGE(TAG, "Failed to enable netif"); + RELEASE_LOCK(prov_ctx_lock); + esp_openthread_lock_release(); + return ESP_FAIL; + } + } + + if (otThreadDiscover(instance, channel_mask, OT_PANID_BROADCAST, false, false, update_thread_scan_result, NULL) != OT_ERROR_NONE) { + ESP_LOGE(TAG, "Failed to start scan"); + RELEASE_LOCK(prov_ctx_lock); + esp_openthread_lock_release(); + return ESP_FAIL; + } + esp_openthread_lock_release(); + + ESP_LOGI(TAG, "Scan started"); + prov_ctx->scanning = true; + RELEASE_LOCK(prov_ctx_lock); + + /* If scan is to be non-blocking, return immediately */ + if (!blocking) { + return ESP_OK; + } + + /* Loop till scan is complete */ + bool scanning = true; + while (scanning) { + ACQUIRE_LOCK(prov_ctx_lock); + scanning = (prov_ctx && prov_ctx->scanning); + RELEASE_LOCK(prov_ctx_lock); + + /* 500ms delay is sufficient for Thread beacons to be sent */ + vTaskDelay(500 / portTICK_PERIOD_MS); + } + return ESP_OK; +} + +bool network_prov_mgr_thread_scan_finished(void) +{ + bool scan_finished = true; + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return scan_finished; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (!prov_ctx) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + RELEASE_LOCK(prov_ctx_lock); + return scan_finished; + } + + scan_finished = !prov_ctx->scanning; + RELEASE_LOCK(prov_ctx_lock); + return scan_finished; +} + +uint16_t network_prov_mgr_thread_scan_result_count(void) +{ + uint16_t rval = 0; + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return rval; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (!prov_ctx) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + RELEASE_LOCK(prov_ctx_lock); + return rval; + } + + rval = prov_ctx->scan_result_count; + RELEASE_LOCK(prov_ctx_lock); + return rval; +} + +const otActiveScanResult *network_prov_mgr_thread_scan_result(uint16_t index) +{ + const otActiveScanResult *rval = NULL; + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return rval; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (!prov_ctx) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + RELEASE_LOCK(prov_ctx_lock); + return rval; + } + + if (index < MAX_SCAN_RESULTS) { + rval = prov_ctx->scan_result[index]; + } + RELEASE_LOCK(prov_ctx_lock); + return rval; +} + +esp_err_t network_prov_mgr_get_thread_state(network_prov_thread_state_t *state) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (prov_ctx == NULL || state == NULL) { + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + + *state = prov_ctx->thread_state; + RELEASE_LOCK(prov_ctx_lock); + return ESP_OK; +} + +esp_err_t network_prov_mgr_get_thread_detached_reason(network_prov_thread_fail_reason_t *reason) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (prov_ctx == NULL || reason == NULL) { + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + + if (prov_ctx->thread_state != NETWORK_PROV_THREAD_DETACHED) { + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + + *reason = prov_ctx->thread_detached_reason; + RELEASE_LOCK(prov_ctx_lock); + return ESP_OK; +} + +esp_err_t network_prov_mgr_is_thread_provisioned(bool *provisioned) +{ + if (!provisioned) { + return ESP_ERR_INVALID_ARG; + } + + *provisioned = false; + otOperationalDataset dataset; + esp_openthread_lock_acquire(portMAX_DELAY); + otError ot_err = otDatasetGetActive(esp_openthread_get_instance(), &dataset); + esp_openthread_lock_release(); + if (ot_err == OT_ERROR_NONE) { + *provisioned = true; + debug_print_thread_dataset(&dataset); + } + + return ESP_OK; +} + + +esp_err_t network_prov_mgr_configure_thread_dataset(otOperationalDatasetTlvs *dataset_tlvs) +{ + esp_err_t err = ESP_OK; + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (!prov_ctx) { + ESP_LOGE(TAG, "Invalid state of Provisioning app"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + if (prov_ctx->prov_state >= NETWORK_PROV_STATE_DATASET_RECV) { + ESP_LOGE(TAG, "Dataset already received by provisioning app"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + + otOperationalDataset dataset; + otError ot_err = otDatasetParseTlvs(dataset_tlvs, &dataset); + if (ot_err != OT_ERROR_NONE) { + prov_ctx->thread_state = NETWORK_PROV_THREAD_DETACHED; + prov_ctx->prov_state = NETWORK_PROV_STATE_FAIL; + prov_ctx->thread_detached_reason = NETWORK_PROV_THREAD_DATASET_INVALID; + execute_event_cb(NETWORK_PROV_THREAD_DATASET_FAIL, (void *)&prov_ctx->thread_detached_reason, + sizeof(prov_ctx->thread_detached_reason)); + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + debug_print_thread_dataset(&dataset); + + err = set_thread_enable(false); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to stop Thread"); + RELEASE_LOCK(prov_ctx_lock); + return err; + } + err = set_thread_dataset(dataset_tlvs); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to set Thread dataset"); + RELEASE_LOCK(prov_ctx_lock); + return err; + } + err = set_thread_enable(true); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to start Thread"); + RELEASE_LOCK(prov_ctx_lock); + return err; + } + + /* Start the timeout timer*/ + if (esp_timer_start_once(prov_ctx->thread_timeout_timer, 20000 * 1000U) != ESP_OK) { + ESP_LOGE(TAG, "Failed to start Thread attaching timer"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_FAIL; + } + + /* Reset Thread state for provisioning app */ + prov_ctx->thread_state = NETWORK_PROV_THREAD_ATTACHING; + prov_ctx->prov_state = NETWORK_PROV_STATE_DATASET_RECV; + /* Execute user registered callback handler */ + execute_event_cb(NETWORK_PROV_THREAD_DATASET_RECV, &dataset, sizeof(dataset)); + RELEASE_LOCK(prov_ctx_lock); + + return ESP_OK; +} +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + +static void network_prov_mgr_event_handler_internal( + void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return; + } + + ACQUIRE_LOCK(prov_ctx_lock); + + /* If pointer to provisioning application data is NULL + * then provisioning manager is not running, therefore + * return with error to allow the global handler to act */ + if (!prov_ctx) { + RELEASE_LOCK(prov_ctx_lock); + return; + } +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + /* If scan completed then update scan result */ + if (prov_ctx->prov_state == NETWORK_PROV_STATE_STARTED && + event_base == WIFI_EVENT && + event_id == WIFI_EVENT_SCAN_DONE) { + update_wifi_scan_results(); + } + + /* Only handle events when credential is received and + * Wi-Fi STA is yet to complete trying the connection */ + if (prov_ctx->prov_state < NETWORK_PROV_STATE_CRED_RECV) { + RELEASE_LOCK(prov_ctx_lock); + return; + } + + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + ESP_LOGI(TAG, "STA Start"); + /* Once configuration is received through protocomm, + * device is started as station. Once station starts, + * wait for connection to establish with configured + * host SSID and password */ + prov_ctx->wifi_state = NETWORK_PROV_WIFI_STA_CONNECTING; + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ESP_LOGI(TAG, "STA Got IP"); + /* Station got IP. That means configuration is successful. */ + prov_ctx->wifi_state = NETWORK_PROV_WIFI_STA_CONNECTED; + prov_ctx->prov_state = NETWORK_PROV_STATE_SUCCESS; + + /* If auto stop is enabled (default), schedule timer to + * stop provisioning after configured timeout. */ + if (!prov_ctx->mgr_info.capabilities.no_auto_stop) { + ESP_LOGD(TAG, "Starting %d sec timer for stop_prov_timer_cb()", + CONFIG_NETWORK_PROV_AUTOSTOP_TIMEOUT); + esp_timer_start_once(prov_ctx->autostop_timer, CONFIG_NETWORK_PROV_AUTOSTOP_TIMEOUT * 1000000U); + } + + /* Execute user registered callback handler */ + execute_event_cb(NETWORK_PROV_WIFI_CRED_SUCCESS, NULL, 0); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + if (prov_ctx->mgr_config.network_prov_wifi_conn_cfg.wifi_conn_attempts > 0) { + prov_ctx->connection_attempts_completed += 1; /* Increasing attempt after every failure */ + if (prov_ctx->connection_attempts_completed < prov_ctx->mgr_config.network_prov_wifi_conn_cfg.wifi_conn_attempts) { + /* Set WiFi state to NETWORK_PROV_WIFI_STA_CONN_ATTEMPT_FAILED only if the user configure wifi_conn_attempts + * and connection_attempts_completed are less than wifi_conn_attempts. + */ + prov_ctx->wifi_state = NETWORK_PROV_WIFI_STA_CONN_ATTEMPT_FAILED; + esp_wifi_connect(); + } else { + /* Station couldn't connect to configured host SSID */ + ESP_LOGE(TAG, "STA Disconnected"); + prov_ctx->wifi_state = NETWORK_PROV_WIFI_STA_DISCONNECTED; + } + } else { + ESP_LOGE(TAG, "STA Disconnected"); + prov_ctx->wifi_state = NETWORK_PROV_WIFI_STA_DISCONNECTED; + } + + /* In case of disconnection, update state of service and + * run the event handler with disconnection reason as data */ + if (prov_ctx->wifi_state == NETWORK_PROV_WIFI_STA_DISCONNECTED) { + prov_ctx->prov_state = NETWORK_PROV_STATE_FAIL; + wifi_event_sta_disconnected_t *disconnected = (wifi_event_sta_disconnected_t *) event_data; + ESP_LOGE(TAG, "Disconnect reason : %d", disconnected->reason); + + /* Set code corresponding to the reason for disconnection */ + switch (disconnected->reason) { + case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: + case WIFI_REASON_AUTH_FAIL: + case WIFI_REASON_HANDSHAKE_TIMEOUT: + case WIFI_REASON_MIC_FAILURE: + ESP_LOGE(TAG, "STA Auth Error"); + prov_ctx->wifi_disconnect_reason = NETWORK_PROV_WIFI_STA_AUTH_ERROR; + break; + case WIFI_REASON_NO_AP_FOUND: + ESP_LOGE(TAG, "STA AP Not found"); + prov_ctx->wifi_disconnect_reason = NETWORK_PROV_WIFI_STA_AP_NOT_FOUND; + break; + default: + if (prov_ctx->mgr_config.network_prov_wifi_conn_cfg.wifi_conn_attempts == 0) { + /* If none of the expected reasons, retry connecting to host SSID */ + prov_ctx->wifi_state = NETWORK_PROV_WIFI_STA_CONNECTING; + esp_wifi_connect(); + } + } + if (prov_ctx->wifi_state == NETWORK_PROV_WIFI_STA_DISCONNECTED) { + /* Execute user registered callback handler */ + network_prov_wifi_sta_fail_reason_t reason = prov_ctx->wifi_disconnect_reason; + execute_event_cb(NETWORK_PROV_WIFI_CRED_FAIL, (void *)&reason, sizeof(reason)); + } + } + } +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + /* Only handle events when dataset is received and + * Thread is yet to complete trying the connection */ + if (prov_ctx->prov_state < NETWORK_PROV_STATE_DATASET_RECV) { + RELEASE_LOCK(prov_ctx_lock); + return; + } + + if (event_base == OPENTHREAD_EVENT && event_id == OPENTHREAD_EVENT_ATTACHED) { + ESP_LOGI(TAG, "Thread attached"); + prov_ctx->thread_state = NETWORK_PROV_THREAD_ATTACHED; + prov_ctx->prov_state = NETWORK_PROV_STATE_SUCCESS; + /* If auto stop is enabled (default), schedule timer to + * stop provisioning after configured timeout. */ + if (!prov_ctx->mgr_info.capabilities.no_auto_stop) { + ESP_LOGD(TAG, "Starting %d sec timer for stop_prov_timer_cb()", + CONFIG_NETWORK_PROV_AUTOSTOP_TIMEOUT); + esp_timer_start_once(prov_ctx->autostop_timer, CONFIG_NETWORK_PROV_AUTOSTOP_TIMEOUT * 1000000U); + } + esp_timer_stop(prov_ctx->thread_timeout_timer); + /* Execute user registered callback handler */ + execute_event_cb(NETWORK_PROV_THREAD_DATASET_SUCCESS, NULL, 0); + } + +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + + if (event_base == NETWORK_PROV_MGR_PVT_EVENT && event_id == NETWORK_PROV_MGR_STOP) { + prov_stop_and_notify(true); + } + + RELEASE_LOCK(prov_ctx_lock); +} + + +esp_err_t network_prov_mgr_init(network_prov_mgr_config_t config) +{ + if (!prov_ctx_lock) { + /* Create mutex if this is the first time init is being called. + * This is created only once and never deleted because if some + * other thread is trying to take this mutex while it is being + * deleted from another thread then the reference may become + * invalid and cause exception */ + prov_ctx_lock = xSemaphoreCreateMutex(); + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Failed to create mutex"); + return ESP_ERR_NO_MEM; + } + } + + void *fn_ptrs[] = { + config.scheme.prov_stop, + config.scheme.prov_start, + config.scheme.new_config, + config.scheme.delete_config, + config.scheme.set_config_service, + config.scheme.set_config_endpoint + }; + + /* All function pointers in the scheme structure must be non-null */ + for (size_t i = 0; i < sizeof(fn_ptrs) / sizeof(fn_ptrs[0]); i++) { + if (!fn_ptrs[i]) { + return ESP_ERR_INVALID_ARG; + } + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (prov_ctx) { + ESP_LOGE(TAG, "Provisioning manager already initialized"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_ERR_INVALID_STATE; + } + + /* Allocate memory for provisioning app data */ + prov_ctx = (struct network_prov_mgr_ctx *) calloc(1, sizeof(struct network_prov_mgr_ctx)); + if (!prov_ctx) { + ESP_LOGE(TAG, "Error allocating memory for singleton instance"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_ERR_NO_MEM; + } + + prov_ctx->mgr_config = config; + prov_ctx->prov_state = NETWORK_PROV_STATE_IDLE; + prov_ctx->mgr_info.version = NETWORK_PROV_MGR_VERSION; + + /* Allocate memory for provisioning scheme configuration */ + const network_prov_scheme_t *scheme = &prov_ctx->mgr_config.scheme; + esp_err_t ret = ESP_OK; + prov_ctx->prov_scheme_config = scheme->new_config(); + if (!prov_ctx->prov_scheme_config) { + ESP_LOGE(TAG, "failed to allocate provisioning scheme configuration"); + ret = ESP_ERR_NO_MEM; + goto exit; + } + + ret = scheme->set_config_endpoint(prov_ctx->prov_scheme_config, "prov-ctrl", 0xFF4F); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "failed to configure Network state control endpoint"); + goto exit; + } + + ret = scheme->set_config_endpoint(prov_ctx->prov_scheme_config, "prov-scan", 0xFF50); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "failed to configure Network scanning endpoint"); + goto exit; + } + + ret = scheme->set_config_endpoint(prov_ctx->prov_scheme_config, "prov-session", 0xFF51); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "failed to configure security endpoint"); + goto exit; + } + + ret = scheme->set_config_endpoint(prov_ctx->prov_scheme_config, "prov-config", 0xFF52); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "failed to configure Network configuration endpoint"); + goto exit; + } + + ret = scheme->set_config_endpoint(prov_ctx->prov_scheme_config, "proto-ver", 0xFF53); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "failed to configure version endpoint"); + goto exit; + } + + /* Application specific custom endpoints will be assigned + * incremental UUIDs starting after this value */ + prov_ctx->endpoint_uuid_used = 0xFF53; + + /* This delay is so that the client side app is notified first + * and then the provisioning is stopped. Default is 1000ms. */ + prov_ctx->cleanup_delay = 1000; + +exit: + if (ret != ESP_OK) { + if (prov_ctx->prov_scheme_config) { + config.scheme.delete_config(prov_ctx->prov_scheme_config); + } + free(prov_ctx); + } else { + execute_event_cb(NETWORK_PROV_INIT, NULL, 0); + } + RELEASE_LOCK(prov_ctx_lock); + return ret; +} + +void network_prov_mgr_wait(void) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return; + } + + while (1) { + ACQUIRE_LOCK(prov_ctx_lock); + if (prov_ctx && + prov_ctx->prov_state != NETWORK_PROV_STATE_IDLE) { + RELEASE_LOCK(prov_ctx_lock); + vTaskDelay(1000 / portTICK_PERIOD_MS); + continue; + } + break; + } + RELEASE_LOCK(prov_ctx_lock); +} + +esp_err_t network_prov_mgr_deinit(void) +{ + esp_err_t ret = ESP_FAIL; + + if (!prov_ctx_lock) { + ESP_LOGW(TAG, "Provisioning manager not initialized"); + return ESP_OK; + } + + ACQUIRE_LOCK(prov_ctx_lock); + + /* This will do one of these: + * 1) if found running, stop the provisioning service (returns true) + * 2) if service was already in the process of termination, this will + * wait till the service is stopped (returns false) + * 3) if service was not running, this will return immediately (returns false) + */ + bool service_was_running = network_prov_mgr_stop_service(1); + + /* If service was not running, its also possible that manager + * was not even initialized */ + if (!service_was_running && !prov_ctx) { + ESP_LOGD(TAG, "Manager already de-initialized"); + RELEASE_LOCK(prov_ctx_lock); + vSemaphoreDelete(prov_ctx_lock); + prov_ctx_lock = NULL; + return ESP_OK; + } + + if (prov_ctx->app_info_json) { + cJSON_Delete(prov_ctx->app_info_json); + } + + if (prov_ctx->prov_scheme_config) { + prov_ctx->mgr_config.scheme.delete_config(prov_ctx->prov_scheme_config); + } + + /* Extract the callbacks to be called post deinit */ + network_prov_cb_func_t app_cb = prov_ctx->mgr_config.app_event_handler.event_cb; + void *app_data = prov_ctx->mgr_config.app_event_handler.user_data; + + network_prov_cb_func_t scheme_cb = prov_ctx->mgr_config.scheme_event_handler.event_cb; + void *scheme_data = prov_ctx->mgr_config.scheme_event_handler.user_data; + + /* Free manager context */ + free(prov_ctx); + prov_ctx = NULL; + RELEASE_LOCK(prov_ctx_lock); + + /* If a running service was also stopped during de-initialization + * then NETWORK_PROV_END event also needs to be emitted before deinit */ + if (service_was_running) { + ESP_LOGD(TAG, "execute_event_cb : %d", NETWORK_PROV_END); + if (scheme_cb) { + scheme_cb(scheme_data, NETWORK_PROV_END, NULL); + } + if (app_cb) { + app_cb(app_data, NETWORK_PROV_END, NULL); + } + ret = esp_event_post(NETWORK_PROV_EVENT, NETWORK_PROV_END, NULL, 0, portMAX_DELAY); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to post event NETWORK_PROV_END"); + return ret; + } + } + + ESP_LOGD(TAG, "execute_event_cb : %d", NETWORK_PROV_DEINIT); + + /* Execute deinit event callbacks */ + if (scheme_cb) { + scheme_cb(scheme_data, NETWORK_PROV_DEINIT, NULL); + } + if (app_cb) { + app_cb(app_data, NETWORK_PROV_DEINIT, NULL); + } + if (esp_event_post(NETWORK_PROV_EVENT, NETWORK_PROV_DEINIT, NULL, 0, portMAX_DELAY) != ESP_OK) { + ESP_LOGE(TAG, "Failed to post event NETWORK_PROV_DEINIT"); + } + + vSemaphoreDelete(prov_ctx_lock); + prov_ctx_lock = NULL; + return ESP_OK; +} + +esp_err_t network_prov_mgr_start_provisioning(network_prov_security_t security, const void *network_prov_sec_params, + const char *service_name, const char *service_key) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + ACQUIRE_LOCK(prov_ctx_lock); + if (!prov_ctx) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_ERR_INVALID_STATE; + } + + if (prov_ctx->prov_state != NETWORK_PROV_STATE_IDLE) { + ESP_LOGE(TAG, "Provisioning service already started"); + RELEASE_LOCK(prov_ctx_lock); + return ESP_ERR_INVALID_STATE; + } + + esp_err_t ret = ESP_OK; + /* Update state so that parallel call to network_prov_mgr_start_provisioning() + * or network_prov_mgr_stop_provisioning() or network_prov_mgr_deinit() from another + * thread doesn't interfere with this process */ + prov_ctx->prov_state = NETWORK_PROV_STATE_STARTING; +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + uint8_t restore_wifi_flag = 0; + /* Start Wi-Fi in Station Mode. + * This is necessary for scanning to work */ + ret = esp_wifi_set_mode(WIFI_MODE_STA); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set Wi-Fi mode to STA"); + goto err; + } + ret = esp_wifi_start(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to start Wi-Fi"); + goto err; + } + + /* Change Wi-Fi storage to RAM temporarily and erase any old + * credentials in RAM(i.e. without erasing the copy on NVS). Also + * call disconnect to make sure device doesn't remain connected + * to the AP whose credentials were present earlier */ + wifi_config_t wifi_cfg_empty, wifi_cfg_old; + memset(&wifi_cfg_empty, 0, sizeof(wifi_config_t)); + esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg_old); + ret = esp_wifi_set_storage(WIFI_STORAGE_RAM); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set Wi-Fi storage to RAM"); + goto err; + } + + /* WiFi storage needs to be restored before exiting this API */ + restore_wifi_flag |= WIFI_PROV_STORAGE_BIT; + /* Erase Wi-Fi credentials in RAM, when call disconnect and user code + * receive WIFI_EVENT_STA_DISCONNECTED and maybe call esp_wifi_connect, at + * this time Wi-Fi will have no configuration to connect */ + ret = esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg_empty); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set empty Wi-Fi credentials"); + goto err; + } + /* WiFi settings needs to be restored if provisioning error before exiting this API */ + restore_wifi_flag |= WIFI_PROV_SETTING_BIT; + + ret = esp_wifi_disconnect(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to disconnect"); + goto err; + } +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0 + /* Initialize app data */ + if (security == NETWORK_PROV_SECURITY_0) { + prov_ctx->mgr_info.capabilities.no_sec = true; + } +#endif +#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1 + if (security == NETWORK_PROV_SECURITY_1) { + if (network_prov_sec_params) { + static protocomm_security1_params_t sec1_params; + // Generate internal copy of "pop", that shall be freed at the end + char *pop = strdup(network_prov_sec_params); + if (pop == NULL) { + ESP_LOGE(TAG, "Failed to allocate memory for pop"); + ret = ESP_ERR_NO_MEM; + goto err; + } + sec1_params.data = (const uint8_t *)pop; + sec1_params.len = strlen(pop); + prov_ctx->protocomm_sec_params = (const void *) &sec1_params; + } else { + prov_ctx->mgr_info.capabilities.no_pop = true; + } + } +#endif +#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2 + if (security == NETWORK_PROV_SECURITY_2) { + if (network_prov_sec_params) { + prov_ctx->protocomm_sec_params = network_prov_sec_params; + } + } +#endif + prov_ctx->security = security; + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + esp_timer_create_args_t wifi_connect_timer_conf = { + .callback = wifi_connect_timer_cb, + .arg = NULL, + .dispatch_method = ESP_TIMER_TASK, + .name = "network_prov_wifi_connect_tm" + }; + ret = esp_timer_create(&wifi_connect_timer_conf, &prov_ctx->wifi_connect_timer); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create Wi-Fi connect timer"); + goto err; + } +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + esp_timer_create_args_t thread_timeout_timer_conf = { + .callback = thread_timeout_timer_cb, + .arg = NULL, + .dispatch_method = ESP_TIMER_TASK, + .name = "thread_prov_timeout_tm" + }; + ret = esp_timer_create(&thread_timeout_timer_conf, &prov_ctx->thread_timeout_timer); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create Thread attaching timeout timer"); + goto err; + } +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + + /* If auto stop on completion is enabled (default) create the stopping timer */ + if (!prov_ctx->mgr_info.capabilities.no_auto_stop) { + /* Create timer object as a member of app data */ + esp_timer_create_args_t autostop_timer_conf = { + .callback = stop_prov_timer_cb, + .arg = NULL, + .dispatch_method = ESP_TIMER_TASK, + .name = "network_prov_autostop_tm" + }; + ret = esp_timer_create(&autostop_timer_conf, &prov_ctx->autostop_timer); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create auto-stop timer"); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + esp_timer_delete(prov_ctx->wifi_connect_timer); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + esp_timer_delete(prov_ctx->thread_timeout_timer); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + goto err; + } + } + + esp_timer_create_args_t cleanup_delay_timer = { + .callback = cleanup_delay_timer_cb, + .arg = NULL, + .dispatch_method = ESP_TIMER_TASK, + .name = "cleanup_delay_tm" + }; + ret = esp_timer_create(&cleanup_delay_timer, &prov_ctx->cleanup_delay_timer); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create cleanup delay timer"); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + esp_timer_delete(prov_ctx->wifi_connect_timer); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + esp_timer_delete(prov_ctx->thread_timeout_timer); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + esp_timer_delete(prov_ctx->autostop_timer); + goto err; + } + + + /* System APIs for BLE / Wi-Fi / Thread will be called inside network_prov_mgr_start_service(), + * which may trigger system level events. Hence, releasing the context lock will + * ensure that network_prov_mgr_event_handler() doesn't block the global event_loop + * handler when system events need to be handled */ + RELEASE_LOCK(prov_ctx_lock); + + /* Start provisioning service */ + ret = network_prov_mgr_start_service(service_name, service_key); + if (ret != ESP_OK) { + esp_timer_delete(prov_ctx->autostop_timer); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + esp_timer_delete(prov_ctx->wifi_connect_timer); +#endif +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + esp_timer_delete(prov_ctx->thread_timeout_timer); +#endif + esp_timer_delete(prov_ctx->cleanup_delay_timer); + } + ACQUIRE_LOCK(prov_ctx_lock); + if (ret == ESP_OK) { + prov_ctx->prov_state = NETWORK_PROV_STATE_STARTED; + /* Execute user registered callback handler */ + execute_event_cb(NETWORK_PROV_START, NULL, 0); + goto exit; + } + +err: + prov_ctx->prov_state = NETWORK_PROV_STATE_IDLE; +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + if (restore_wifi_flag & WIFI_PROV_SETTING_BIT) { + /* Restore current WiFi settings, since provisioning start has failed */ + esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg_old); + } +#endif +exit: +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + if (restore_wifi_flag & WIFI_PROV_STORAGE_BIT) { + /* Restore WiFi storage back to FLASH */ + esp_wifi_set_storage(WIFI_STORAGE_FLASH); + } +#endif + RELEASE_LOCK(prov_ctx_lock); + return ret; +} + +void network_prov_mgr_stop_provisioning(void) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return; + } + + ACQUIRE_LOCK(prov_ctx_lock); + + /* Launches task for stopping the provisioning service. This will do one of these: + * 1) start a task for stopping the provisioning service (returns true) + * 2) if service was already in the process of termination, this will + * wait till the service is stopped (returns false) + * 3) if service was not running, this will return immediately (returns false) + */ + network_prov_mgr_stop_service(0); + + RELEASE_LOCK(prov_ctx_lock); +} + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +esp_err_t network_prov_mgr_reset_wifi_provisioning(void) +{ + esp_err_t ret = esp_wifi_restore(); + + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_wifi_restore fail, ret is %d", ret); + ret = ESP_FAIL; + } + + return ret; +} + +esp_err_t network_prov_mgr_reset_wifi_sm_state_on_failure(void) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + ACQUIRE_LOCK(prov_ctx_lock); + + esp_err_t err = ESP_OK; + /* If already in STARTED state, reset has already been performed (e.g., by firmware). + * Return success as the device is effectively already reset and in provisioning mode. */ + if (prov_ctx->prov_state == NETWORK_PROV_STATE_STARTED) { + ESP_LOGD(TAG, "Reset already performed, device already in provisioning mode"); + goto exit; + } + + if (prov_ctx->prov_state != NETWORK_PROV_STATE_FAIL) { + ESP_LOGE(TAG, "Trying reset when not in failure state. Current state: %d", prov_ctx->prov_state); + err = ESP_ERR_INVALID_STATE; + goto exit; + } + + wifi_config_t wifi_cfg = {0}; + + err = esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to set wifi config, 0x%x", err); + goto exit; + } + + /* Reset connection attempts counter to allow full wifi_conn_attempts retries */ + prov_ctx->connection_attempts_completed = 0; + prov_ctx->prov_state = NETWORK_PROV_STATE_STARTED; + +exit: + RELEASE_LOCK(prov_ctx_lock); + return err; +} + +esp_err_t network_prov_mgr_reset_wifi_sm_state_for_reprovision(void) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + ACQUIRE_LOCK(prov_ctx_lock); + + esp_err_t ret = ESP_OK; + wifi_config_t wifi_cfg_empty = {0}; + uint8_t restore_wifi_flag = 0; + + if (!prov_ctx->mgr_info.capabilities.no_auto_stop) { + ESP_LOGE(TAG, "Execute network_prov_mgr_disable_auto_stop() before calling this API"); + ret = ESP_ERR_INVALID_STATE; + goto exit; + } + + ret = esp_wifi_set_storage(WIFI_STORAGE_RAM); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set Wi-Fi storage to RAM"); + goto exit; + } + restore_wifi_flag |= WIFI_PROV_STORAGE_BIT; + + ret = esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg_empty); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set empty Wi-Fi credentials, 0x%x", ret); + goto exit; + } + + ret = esp_wifi_disconnect(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to disconnect wifi, 0x%x", ret); + goto exit; + } + + prov_ctx->prov_state = NETWORK_PROV_STATE_STARTED; + execute_event_cb(NETWORK_PROV_START, NULL, 0); + +exit: + if (restore_wifi_flag & WIFI_PROV_STORAGE_BIT) { + esp_wifi_set_storage(WIFI_STORAGE_FLASH); + } + RELEASE_LOCK(prov_ctx_lock); + return ret; +} +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +esp_err_t network_prov_mgr_reset_thread_provisioning(void) +{ + otInstance *instance = esp_openthread_get_instance(); + esp_openthread_lock_acquire(portMAX_DELAY); + if (otInstanceErasePersistentInfo(instance) != OT_ERROR_NONE) { + ESP_LOGE(TAG, "Failed to Erase Thread Network Info"); + esp_openthread_lock_release(); + return ESP_FAIL; + } + esp_openthread_lock_release(); + return ESP_OK; +} + +esp_err_t network_prov_mgr_reset_thread_sm_state_on_failure(void) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + ACQUIRE_LOCK(prov_ctx_lock); + + esp_err_t err = ESP_OK; + otInstance *instance = esp_openthread_get_instance(); + + esp_openthread_lock_acquire(portMAX_DELAY); + /* If already in STARTED state, reset has already been performed (e.g., by firmware). + * Return success as the device is effectively already reset and in provisioning mode. */ + if (prov_ctx->prov_state == NETWORK_PROV_STATE_STARTED) { + ESP_LOGD(TAG, "Reset already performed, device already in provisioning mode"); + goto exit; + } + + if (prov_ctx->prov_state != NETWORK_PROV_STATE_FAIL) { + ESP_LOGE(TAG, "Trying reset when not in failure state. Current state: %d", prov_ctx->prov_state); + err = ESP_ERR_INVALID_STATE; + goto exit; + } + + if (otThreadSetEnabled(instance, false) != OT_ERROR_NONE) { + ESP_LOGE(TAG, "Failed to stop Thread"); + err = ESP_FAIL; + goto exit; + } + + if (otIp6SetEnabled(instance, false) != OT_ERROR_NONE) { + ESP_LOGE(TAG, "Failed to disable Thread netif"); + err = ESP_FAIL; + goto exit; + } + + if (otInstanceErasePersistentInfo(instance) != OT_ERROR_NONE) { + ESP_LOGE(TAG, "Failed to erase Thread network info"); + err = ESP_FAIL; + goto exit; + } + + prov_ctx->prov_state = NETWORK_PROV_STATE_STARTED; + +exit: + esp_openthread_lock_release(); + RELEASE_LOCK(prov_ctx_lock); + return err; +} + +esp_err_t network_prov_mgr_reset_thread_sm_state_for_reprovision(void) +{ + if (!prov_ctx_lock) { + ESP_LOGE(TAG, "Provisioning manager not initialized"); + return ESP_ERR_INVALID_STATE; + } + + ACQUIRE_LOCK(prov_ctx_lock); + + esp_err_t ret = ESP_OK; + otInstance *instance = esp_openthread_get_instance(); + + esp_openthread_lock_acquire(portMAX_DELAY); + if (!prov_ctx->mgr_info.capabilities.no_auto_stop) { + ESP_LOGE(TAG, "Execute network_prov_mgr_disable_auto_stop() before calling this API"); + ret = ESP_ERR_INVALID_STATE; + goto exit; + } + + if (otThreadSetEnabled(instance, false) != OT_ERROR_NONE) { + ESP_LOGE(TAG, "Failed to stop Thread"); + ret = ESP_FAIL; + goto exit; + } + + if (otIp6SetEnabled(instance, false) != OT_ERROR_NONE) { + ESP_LOGE(TAG, "Failed to disable Thread netif"); + ret = ESP_FAIL; + goto exit; + } + + prov_ctx->prov_state = NETWORK_PROV_STATE_STARTED; + execute_event_cb(NETWORK_PROV_START, NULL, 0); + +exit: + esp_openthread_lock_release(); + RELEASE_LOCK(prov_ctx_lock); + return ret; +} +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD diff --git a/components/network_provisioning/src/network_config.c b/components/network_provisioning/src/network_config.c new file mode 100644 index 0000000..506c082 --- /dev/null +++ b/components/network_provisioning/src/network_config.c @@ -0,0 +1,546 @@ +/* + * SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include "network_constants.pb-c.h" +#include "network_config.pb-c.h" + +#include + +static const char *TAG = "NetworkProvConfig"; + +typedef struct network_prov_config_cmd { + int cmd_num; + esp_err_t (*command_handler)(NetworkConfigPayload *req, + NetworkConfigPayload *resp, void *priv_data); +} network_prov_config_cmd_t; + +static esp_err_t cmd_get_status_handler(NetworkConfigPayload *req, + NetworkConfigPayload *resp, void *priv_data); + +static esp_err_t cmd_set_config_handler(NetworkConfigPayload *req, + NetworkConfigPayload *resp, void *priv_data); + +static esp_err_t cmd_apply_config_handler(NetworkConfigPayload *req, + NetworkConfigPayload *resp, void *priv_data); + +static network_prov_config_cmd_t cmd_table[] = { + { + .cmd_num = NETWORK_CONFIG_MSG_TYPE__TypeCmdGetWifiStatus, + .command_handler = cmd_get_status_handler + }, + { + .cmd_num = NETWORK_CONFIG_MSG_TYPE__TypeCmdSetWifiConfig, + .command_handler = cmd_set_config_handler + }, + { + .cmd_num = NETWORK_CONFIG_MSG_TYPE__TypeCmdApplyWifiConfig, + .command_handler = cmd_apply_config_handler + }, + { + .cmd_num = NETWORK_CONFIG_MSG_TYPE__TypeCmdGetThreadStatus, + .command_handler = cmd_get_status_handler + }, + { + .cmd_num = NETWORK_CONFIG_MSG_TYPE__TypeCmdSetThreadConfig, + .command_handler = cmd_set_config_handler + }, + { + .cmd_num = NETWORK_CONFIG_MSG_TYPE__TypeCmdApplyThreadConfig, + .command_handler = cmd_apply_config_handler + } +}; + +static esp_err_t cmd_get_status_handler(NetworkConfigPayload *req, + NetworkConfigPayload *resp, void *priv_data) +{ + ESP_LOGD(TAG, "Enter cmd_get_status_handler"); + network_prov_config_handlers_t *h = (network_prov_config_handlers_t *) priv_data; + if (!h) { + ESP_LOGE(TAG, "Command invoked without handlers"); + return ESP_ERR_INVALID_STATE; + } + + if (req->msg == NETWORK_CONFIG_MSG_TYPE__TypeCmdGetWifiStatus) { + RespGetWifiStatus *resp_payload = (RespGetWifiStatus *) malloc(sizeof(RespGetWifiStatus)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_get_wifi_status__init(resp_payload); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + network_prov_config_get_wifi_data_t resp_data; + if (h->wifi_get_status_handler) { + if (h->wifi_get_status_handler(&resp_data, &h->ctx) == ESP_OK) { + if (resp_data.wifi_state == NETWORK_PROV_WIFI_STA_CONNECTING) { + resp_payload->wifi_sta_state = WIFI_STATION_STATE__Connecting; + resp_payload->state_case = RESP_GET_WIFI_STATUS__STATE_WIFI_CONNECTED; + } else if (resp_data.wifi_state == NETWORK_PROV_WIFI_STA_CONNECTED) { + resp_payload->wifi_sta_state = WIFI_STATION_STATE__Connected; + resp_payload->state_case = RESP_GET_WIFI_STATUS__STATE_WIFI_CONNECTED; + WifiConnectedState *connected = (WifiConnectedState *)( + malloc(sizeof(WifiConnectedState))); + if (!connected) { + free(resp_payload); + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_payload->wifi_connected = connected; + wifi_connected_state__init(connected); + + connected->ip4_addr = strdup(resp_data.conn_info.ip_addr); + if (connected->ip4_addr == NULL) { + free(connected); + free(resp_payload); + return ESP_ERR_NO_MEM; + } + + connected->bssid.len = sizeof(resp_data.conn_info.bssid); + connected->bssid.data = (uint8_t *) strndup(resp_data.conn_info.bssid, + sizeof(resp_data.conn_info.bssid)); + if (connected->bssid.data == NULL) { + free(connected->ip4_addr); + free(connected); + free(resp_payload); + return ESP_ERR_NO_MEM; + } + + connected->ssid.len = strlen(resp_data.conn_info.ssid); + connected->ssid.data = (uint8_t *) strdup(resp_data.conn_info.ssid); + if (connected->ssid.data == NULL) { + free(connected->bssid.data); + free(connected->ip4_addr); + free(connected); + free(resp_payload); + return ESP_ERR_NO_MEM; + } + + connected->channel = resp_data.conn_info.channel; + connected->auth_mode = resp_data.conn_info.auth_mode; + } else if (resp_data.wifi_state == NETWORK_PROV_WIFI_STA_DISCONNECTED) { + resp_payload->wifi_sta_state = WIFI_STATION_STATE__ConnectionFailed; + resp_payload->state_case = RESP_GET_WIFI_STATUS__STATE_WIFI_FAIL_REASON; + + if (resp_data.fail_reason == NETWORK_PROV_WIFI_STA_AUTH_ERROR) { + resp_payload->wifi_fail_reason = WIFI_CONNECT_FAILED_REASON__AuthError; + } else if (resp_data.fail_reason == NETWORK_PROV_WIFI_STA_AP_NOT_FOUND) { + resp_payload->wifi_fail_reason = WIFI_CONNECT_FAILED_REASON__WifiNetworkNotFound; + } + } else if (resp_data.wifi_state == NETWORK_PROV_WIFI_STA_CONN_ATTEMPT_FAILED) { + resp_payload->wifi_sta_state = WIFI_STATION_STATE__Connecting; + resp_payload->state_case = RESP_GET_WIFI_STATUS__STATE_ATTEMPT_FAILED; + WifiAttemptFailed *attempt_failed = (WifiAttemptFailed *)( + calloc(1, sizeof(WifiAttemptFailed))); + if (!attempt_failed) { + free(resp_payload); + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + wifi_attempt_failed__init(attempt_failed); + attempt_failed->attempts_remaining = resp_data.connecting_info.attempts_remaining; + resp_payload->attempt_failed = attempt_failed; + } + resp_payload->status = STATUS__Success; + } else { + resp_payload->status = STATUS__InternalError; + } + } else { + resp_payload->status = STATUS__InternalError; + } +#else // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + resp_payload->status = STATUS__InvalidArgument; +#endif // !CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + resp->payload_case = NETWORK_CONFIG_PAYLOAD__PAYLOAD_RESP_GET_WIFI_STATUS; + resp->resp_get_wifi_status = resp_payload; + } else if (req->msg == NETWORK_CONFIG_MSG_TYPE__TypeCmdGetThreadStatus) { + RespGetThreadStatus *resp_payload = (RespGetThreadStatus *) malloc(sizeof(RespGetThreadStatus)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_get_thread_status__init(resp_payload); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + network_prov_config_get_thread_data_t resp_data; + if (h->thread_get_status_handler) { + if (h->thread_get_status_handler(&resp_data, &h->ctx) == ESP_OK) { + if (resp_data.thread_state == NETWORK_PROV_THREAD_ATTACHING) { + resp_payload->thread_state = THREAD_NETWORK_STATE__Attaching; + resp_payload->state_case = RESP_GET_THREAD_STATUS__STATE_THREAD_ATTACHED; + } else if (resp_data.thread_state == NETWORK_PROV_THREAD_ATTACHED) { + resp_payload->thread_state = THREAD_NETWORK_STATE__Attached; + resp_payload->state_case = RESP_GET_THREAD_STATUS__STATE_THREAD_ATTACHED; + ThreadAttachState *attached = (ThreadAttachState *)malloc(sizeof(ThreadAttachState)); + if (!attached) { + free(resp_payload); + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_payload->thread_attached = attached; + thread_attach_state__init(attached); + attached->channel = resp_data.conn_info.channel; + attached->ext_pan_id.len = sizeof(resp_data.conn_info.ext_pan_id); + attached->ext_pan_id.data = (uint8_t *)malloc(attached->ext_pan_id.len); + if (!attached->ext_pan_id.data) { + free(attached); + free(resp_payload); + return ESP_ERR_NO_MEM; + } + memcpy(attached->ext_pan_id.data, resp_data.conn_info.ext_pan_id, sizeof(resp_data.conn_info.ext_pan_id)); + attached->pan_id = resp_data.conn_info.pan_id; + + attached->name = (char *)malloc(sizeof(resp_data.conn_info.name)); + if (!attached->name) { + free(attached->ext_pan_id.data); + free(attached); + free(resp_payload); + return ESP_ERR_NO_MEM; + } + memcpy(attached->name, resp_data.conn_info.name, sizeof(resp_data.conn_info.name)); + } else if (resp_data.thread_state == NETWORK_PROV_THREAD_DETACHED) { + resp_payload->thread_state = THREAD_NETWORK_STATE__AttachingFailed; + resp_payload->state_case = RESP_GET_THREAD_STATUS__STATE_THREAD_FAIL_REASON; + + if (resp_data.fail_reason == NETWORK_PROV_THREAD_DATASET_INVALID) { + resp_payload->thread_fail_reason = THREAD_ATTACH_FAILED_REASON__DatasetInvalid; + } else if (resp_data.fail_reason == NETWORK_PROV_THREAD_NETWORK_NOT_FOUND) { + resp_payload->thread_fail_reason = THREAD_ATTACH_FAILED_REASON__ThreadNetworkNotFound; + } + } + resp_payload->status = STATUS__Success; + } else { + resp_payload->status = STATUS__InternalError; + } + } else { + resp_payload->status = STATUS__InternalError; + } +#else // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + resp_payload->status = STATUS__InvalidArgument; +#endif // !CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + resp->payload_case = NETWORK_CONFIG_PAYLOAD__PAYLOAD_RESP_GET_THREAD_STATUS; + resp->resp_get_thread_status = resp_payload; + } + return ESP_OK; +} + +static esp_err_t cmd_set_config_handler(NetworkConfigPayload *req, + NetworkConfigPayload *resp, void *priv_data) +{ + ESP_LOGD(TAG, "Enter cmd_set_config_handler"); + network_prov_config_handlers_t *h = (network_prov_config_handlers_t *) priv_data; + if (!h) { + ESP_LOGE(TAG, "Command invoked without handlers"); + return ESP_ERR_INVALID_STATE; + } + + if (req->msg == NETWORK_CONFIG_MSG_TYPE__TypeCmdSetWifiConfig) { + RespSetWifiConfig *resp_payload = (RespSetWifiConfig *) malloc(sizeof(RespSetWifiConfig)); + if (resp_payload == NULL) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_set_wifi_config__init(resp_payload); + + if (req->payload_case != NETWORK_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_WIFI_CONFIG || !req->cmd_set_wifi_config) { + ESP_LOGE(TAG, "Invalid set WiFi config command"); + resp->resp_set_wifi_config = resp_payload; + return ESP_ERR_INVALID_ARG; + } + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + network_prov_config_set_wifi_data_t req_data; + memset(&req_data, 0, sizeof(req_data)); + + /* Check arguments provided in protobuf packet: + * - SSID / Passphrase string length must be within the standard limits + * - BSSID must either be NULL or have length equal to that imposed by the standard + * If any of these conditions are not satisfied, don't invoke the handler and + * send error status without closing connection */ + resp_payload->status = STATUS__InvalidArgument; + if (req->cmd_set_wifi_config->bssid.len != 0 && + req->cmd_set_wifi_config->bssid.len != sizeof(req_data.bssid)) { + ESP_LOGD(TAG, "Received invalid BSSID"); + } else if (req->cmd_set_wifi_config->ssid.len >= sizeof(req_data.ssid)) { + ESP_LOGD(TAG, "Received invalid SSID"); + } else if (req->cmd_set_wifi_config->passphrase.len >= sizeof(req_data.password)) { + ESP_LOGD(TAG, "Received invalid Passphrase"); + } else { + /* The received SSID and Passphrase are not NULL terminated so + * we memcpy over zeroed out arrays. Above length checks ensure + * that there is at least 1 extra byte for null termination */ + memcpy(req_data.ssid, req->cmd_set_wifi_config->ssid.data, + req->cmd_set_wifi_config->ssid.len); + memcpy(req_data.password, req->cmd_set_wifi_config->passphrase.data, + req->cmd_set_wifi_config->passphrase.len); + memcpy(req_data.bssid, req->cmd_set_wifi_config->bssid.data, + req->cmd_set_wifi_config->bssid.len); + req_data.channel = req->cmd_set_wifi_config->channel; + if (h->wifi_set_config_handler(&req_data, &h->ctx) == ESP_OK) { + resp_payload->status = STATUS__Success; + } else { + resp_payload->status = STATUS__InternalError; + } + } +#else // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + resp_payload->status = STATUS__InvalidArgument; +#endif // !CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + resp->payload_case = NETWORK_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_WIFI_CONFIG; + resp->resp_set_wifi_config = resp_payload; + } else if (req->msg == NETWORK_CONFIG_MSG_TYPE__TypeCmdSetThreadConfig) { + RespSetThreadConfig *resp_payload = (RespSetThreadConfig *) malloc(sizeof(RespSetThreadConfig)); + if (resp_payload == NULL) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_set_thread_config__init(resp_payload); + + if (req->payload_case != NETWORK_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_THREAD_CONFIG || !req->cmd_set_thread_config) { + ESP_LOGE(TAG, "Invalid set Thread config command"); + resp->resp_set_thread_config = resp_payload; + return ESP_ERR_INVALID_ARG; + } + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + network_prov_config_set_thread_data_t req_data; + memset(&req_data, 0, sizeof(req_data)); + resp_payload->status = STATUS__InvalidArgument; + if (req->cmd_set_thread_config->dataset.len > sizeof(req_data.dataset)) { + ESP_LOGD(TAG, "Received invalid dataset"); + } else { + memcpy(req_data.dataset, req->cmd_set_thread_config->dataset.data, + req->cmd_set_thread_config->dataset.len); + req_data.length = req->cmd_set_thread_config->dataset.len; + if (h->thread_set_config_handler(&req_data, &h->ctx) == ESP_OK) { + resp_payload->status = STATUS__Success; + } else { + resp_payload->status = STATUS__InternalError; + } + } +#else // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + resp_payload->status = STATUS__InvalidArgument; +#endif // !CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + resp->payload_case = NETWORK_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_THREAD_CONFIG; + resp->resp_set_thread_config = resp_payload; + } + return ESP_OK; +} + +static esp_err_t cmd_apply_config_handler(NetworkConfigPayload *req, + NetworkConfigPayload *resp, void *priv_data) +{ + ESP_LOGD(TAG, "Enter cmd_apply_config_handler"); + network_prov_config_handlers_t *h = (network_prov_config_handlers_t *) priv_data; + if (!h) { + ESP_LOGE(TAG, "Command invoked without handlers"); + return ESP_ERR_INVALID_STATE; + } + if (req->msg == NETWORK_CONFIG_MSG_TYPE__TypeCmdApplyWifiConfig) { + RespApplyWifiConfig *resp_payload = (RespApplyWifiConfig *) malloc(sizeof(RespApplyWifiConfig)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_apply_wifi_config__init(resp_payload); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + if (h->wifi_apply_config_handler && h->wifi_apply_config_handler(&h->ctx) == ESP_OK) { + resp_payload->status = STATUS__Success; + } else { + resp_payload->status = STATUS__InternalError; + } +#else // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + resp_payload->status = STATUS__InvalidArgument; +#endif // !CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + resp->payload_case = NETWORK_CONFIG_PAYLOAD__PAYLOAD_RESP_APPLY_WIFI_CONFIG; + resp->resp_apply_wifi_config = resp_payload; + } else if (req->msg == NETWORK_CONFIG_MSG_TYPE__TypeCmdApplyThreadConfig) { + RespApplyThreadConfig *resp_payload = (RespApplyThreadConfig *) malloc(sizeof(RespApplyThreadConfig)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_apply_thread_config__init(resp_payload); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + if (h->thread_apply_config_handler && h->thread_apply_config_handler(&h->ctx) == ESP_OK) { + resp_payload->status = STATUS__Success; + } else { + resp_payload->status = STATUS__InternalError; + } +#else // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + resp_payload->status = STATUS__InvalidArgument; +#endif // !CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + resp->payload_case = NETWORK_CONFIG_PAYLOAD__PAYLOAD_RESP_APPLY_THREAD_CONFIG; + resp->resp_apply_thread_config = resp_payload; + } + return ESP_OK; +} + +static int lookup_cmd_handler(int cmd_id) +{ + for (size_t i = 0; i < sizeof(cmd_table) / sizeof(network_prov_config_cmd_t); i++) { + if (cmd_table[i].cmd_num == cmd_id) { + return i; + } + } + + return -1; +} +static void network_prov_config_command_cleanup(NetworkConfigPayload *resp, void *priv_data) +{ + if (!resp) { + return; + } + + switch (resp->msg) { + case NETWORK_CONFIG_MSG_TYPE__TypeRespGetWifiStatus: { + if (!resp->resp_get_wifi_status) { + break; + } +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + switch (resp->resp_get_wifi_status->wifi_sta_state) { + case WIFI_STATION_STATE__Connecting: + break; + case WIFI_STATION_STATE__Connected: + if (resp->resp_get_wifi_status->wifi_connected) { + if (resp->resp_get_wifi_status->wifi_connected->ip4_addr) { + free(resp->resp_get_wifi_status->wifi_connected->ip4_addr); + } + if (resp->resp_get_wifi_status->wifi_connected->bssid.data) { + free(resp->resp_get_wifi_status->wifi_connected->bssid.data); + } + if (resp->resp_get_wifi_status->wifi_connected->ssid.data) { + free(resp->resp_get_wifi_status->wifi_connected->ssid.data); + } + free(resp->resp_get_wifi_status->wifi_connected); + } + break; + case WIFI_STATION_STATE__ConnectionFailed: + break; + default: + break; + } +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + free(resp->resp_get_wifi_status); + } + break; + case NETWORK_CONFIG_MSG_TYPE__TypeRespGetThreadStatus: { + if (!resp->resp_get_thread_status) { + break; + } +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + switch (resp->resp_get_thread_status->thread_state) { + case THREAD_NETWORK_STATE__Attaching: + break; + case THREAD_NETWORK_STATE__Attached: + if (resp->resp_get_thread_status->thread_attached) { + if (resp->resp_get_thread_status->thread_attached->name) { + free(resp->resp_get_thread_status->thread_attached->name); + } + free(resp->resp_get_thread_status->thread_attached); + } + break; + case THREAD_NETWORK_STATE__AttachingFailed: + break; + default: + break; + } +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + free(resp->resp_get_thread_status); + } + break; + case NETWORK_CONFIG_MSG_TYPE__TypeRespSetWifiConfig: { + free(resp->resp_set_wifi_config); + } + break; + case NETWORK_CONFIG_MSG_TYPE__TypeRespSetThreadConfig: { + free(resp->resp_set_thread_config); + } + break; + case NETWORK_CONFIG_MSG_TYPE__TypeRespApplyWifiConfig: { + free(resp->resp_apply_wifi_config); + } + break; + case NETWORK_CONFIG_MSG_TYPE__TypeRespApplyThreadConfig: { + free(resp->resp_apply_thread_config); + } + break; + default: + ESP_LOGE(TAG, "Unsupported response type in cleanup_handler"); + break; + } + return; +} + +static esp_err_t network_prov_config_command_dispatcher(NetworkConfigPayload *req, + NetworkConfigPayload *resp, void *priv_data) +{ + esp_err_t ret; + + ESP_LOGD(TAG, "In network_prov_config_command_dispatcher Cmd=%d", req->msg); + + int cmd_index = lookup_cmd_handler(req->msg); + if (cmd_index < 0) { + ESP_LOGE(TAG, "Invalid command handler lookup"); + return ESP_FAIL; + } + + ret = cmd_table[cmd_index].command_handler(req, resp, priv_data); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Error executing command handler"); + return ESP_FAIL; + } + + return ESP_OK; +} + +esp_err_t network_prov_config_data_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, + uint8_t **outbuf, ssize_t *outlen, void *priv_data) +{ + NetworkConfigPayload *req; + NetworkConfigPayload resp; + esp_err_t ret; + + req = network_config_payload__unpack(NULL, inlen, inbuf); + if (!req) { + ESP_LOGE(TAG, "Unable to unpack config data"); + return ESP_ERR_INVALID_ARG; + } + + network_config_payload__init(&resp); + /* Validate req->msg before arithmetic to avoid signed overflow on attacker-controlled + * wire values. For unknown commands the dispatcher returns ESP_FAIL without calling + * any handler, so nothing is allocated and resp.msg = 0 is safe for cleanup. */ + if (lookup_cmd_handler(req->msg) >= 0) { + resp.msg = req->msg + 1; + } + ret = network_prov_config_command_dispatcher(req, &resp, priv_data); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Proto command dispatcher error %d", ret); + ret = ESP_FAIL; + goto exit; + } + + *outlen = network_config_payload__get_packed_size(&resp); + if (*outlen <= 0) { + ESP_LOGE(TAG, "Invalid encoding for response"); + ret = ESP_FAIL; + goto exit; + } + + *outbuf = (uint8_t *) malloc(*outlen); + if (!*outbuf) { + ESP_LOGE(TAG, "System out of memory"); + ret = ESP_ERR_NO_MEM; + goto exit; + } + network_config_payload__pack(&resp, *outbuf); +exit: + network_config_payload__free_unpacked(req, NULL); + network_prov_config_command_cleanup(&resp, priv_data); + return ret; +} diff --git a/components/network_provisioning/src/network_ctrl.c b/components/network_provisioning/src/network_ctrl.c new file mode 100644 index 0000000..93ca5f0 --- /dev/null +++ b/components/network_provisioning/src/network_ctrl.c @@ -0,0 +1,247 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include "network_ctrl.pb-c.h" + +#include "network_ctrl.h" + +static const char *TAG = "proto_network_ctrl"; + +typedef struct network_ctrl_cmd { + int cmd_id; + esp_err_t (*command_handler)(NetworkCtrlPayload *req, + NetworkCtrlPayload *resp, void *priv_data); +} network_ctrl_cmd_t; + +static esp_err_t cmd_ctrl_reset_handler(NetworkCtrlPayload *req, + NetworkCtrlPayload *resp, + void *priv_data); + +static esp_err_t cmd_ctrl_reprov_handler(NetworkCtrlPayload *req, + NetworkCtrlPayload *resp, + void *priv_data); + +static network_ctrl_cmd_t cmd_table[] = { + { + .cmd_id = NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlWifiReset, + .command_handler = cmd_ctrl_reset_handler + }, + { + .cmd_id = NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlThreadReset, + .command_handler = cmd_ctrl_reset_handler + }, + { + .cmd_id = NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlWifiReprov, + .command_handler = cmd_ctrl_reprov_handler + }, + { + .cmd_id = NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlThreadReprov, + .command_handler = cmd_ctrl_reprov_handler + }, +}; + +static esp_err_t cmd_ctrl_reset_handler(NetworkCtrlPayload *req, + NetworkCtrlPayload *resp, void *priv_data) +{ + network_ctrl_handlers_t *h = (network_ctrl_handlers_t *) priv_data; + if (!h) { + ESP_LOGE(TAG, "Command invoked without handlers"); + return ESP_ERR_INVALID_STATE; + } + if (req->msg == NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlWifiReset) { + RespCtrlWifiReset *resp_payload = (RespCtrlWifiReset *) malloc(sizeof(RespCtrlWifiReset)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_ctrl_wifi_reset__init(resp_payload); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + if (h->wifi_ctrl_reset) { + resp->status = (h->wifi_ctrl_reset() == ESP_OK ? STATUS__Success : STATUS__InternalError); + } else { + resp->status = STATUS__InternalError; + } +#else + resp->status = STATUS__InvalidArgument; +#endif + resp->payload_case = NETWORK_CTRL_PAYLOAD__PAYLOAD_RESP_CTRL_WIFI_RESET; + resp->resp_ctrl_wifi_reset = resp_payload; + } else if (req->msg == NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlThreadReset) { + RespCtrlThreadReset *resp_payload = (RespCtrlThreadReset *) malloc(sizeof(RespCtrlThreadReset)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_ctrl_thread_reset__init(resp_payload); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + if (h->thread_ctrl_reset) { + resp->status = (h->thread_ctrl_reset() == ESP_OK ? STATUS__Success : STATUS__InternalError); + } else { + resp->status = STATUS__InternalError; + } +#else + resp->status = STATUS__InvalidArgument; +#endif + resp->payload_case = NETWORK_CTRL_PAYLOAD__PAYLOAD_RESP_CTRL_THREAD_RESET; + resp->resp_ctrl_thread_reset = resp_payload; + } + return ESP_OK; +} + +static esp_err_t cmd_ctrl_reprov_handler(NetworkCtrlPayload *req, + NetworkCtrlPayload *resp, void *priv_data) +{ + network_ctrl_handlers_t *h = (network_ctrl_handlers_t *) priv_data; + if (!h) { + ESP_LOGE(TAG, "Command invoked without handlers"); + return ESP_ERR_INVALID_STATE; + } + if (req->msg == NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlWifiReprov) { + RespCtrlWifiReprov *resp_payload = (RespCtrlWifiReprov *) malloc(sizeof(RespCtrlWifiReprov)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_ctrl_wifi_reprov__init(resp_payload); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + if (h->wifi_ctrl_reprov) { + resp->status = (h->wifi_ctrl_reprov() == ESP_OK ? STATUS__Success : STATUS__InternalError); + } else { + resp->status = STATUS__InternalError; + } +#else + resp->status = STATUS__InvalidArgument; +#endif + resp->payload_case = NETWORK_CTRL_PAYLOAD__PAYLOAD_RESP_CTRL_WIFI_REPROV; + resp->resp_ctrl_wifi_reprov = resp_payload; + } else if (req->msg == NETWORK_CTRL_MSG_TYPE__TypeCmdCtrlThreadReprov) { + RespCtrlThreadReprov *resp_payload = (RespCtrlThreadReprov *) malloc(sizeof(RespCtrlThreadReprov)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_ctrl_thread_reprov__init(resp_payload); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + if (h->thread_ctrl_reprov) { + resp->status = (h->thread_ctrl_reprov() == ESP_OK ? STATUS__Success : STATUS__InternalError); + } else { + resp->status = STATUS__InternalError; + } +#else + resp->status = STATUS__InvalidArgument; +#endif + resp->payload_case = NETWORK_CTRL_PAYLOAD__PAYLOAD_RESP_CTRL_THREAD_REPROV; + resp->resp_ctrl_thread_reprov = resp_payload; + } + return ESP_OK; +} + +static int lookup_cmd_handler(int cmd_id) +{ + for (size_t i = 0; i < sizeof(cmd_table) / sizeof(network_ctrl_cmd_t); i++) { + if (cmd_table[i].cmd_id == cmd_id) { + return i; + } + } + + return -1; +} + +static void network_ctrl_cmd_cleanup(NetworkCtrlPayload *resp, void *priv_data) +{ + switch (resp->msg) { + case NETWORK_CTRL_MSG_TYPE__TypeRespCtrlWifiReset: { + free(resp->resp_ctrl_wifi_reset); + } + break; + case NETWORK_CTRL_MSG_TYPE__TypeRespCtrlWifiReprov: { + free(resp->resp_ctrl_wifi_reprov); + } + break; + case NETWORK_CTRL_MSG_TYPE__TypeRespCtrlThreadReset: { + free(resp->resp_ctrl_thread_reset); + } + break; + case NETWORK_CTRL_MSG_TYPE__TypeRespCtrlThreadReprov: { + free(resp->resp_ctrl_thread_reprov); + } + break; + default: + ESP_LOGE(TAG, "Unsupported response type in cleanup_handler"); + break; + } + return; +} + +static esp_err_t network_ctrl_cmd_dispatcher(NetworkCtrlPayload *req, + NetworkCtrlPayload *resp, void *priv_data) +{ + esp_err_t ret; + + ESP_LOGD(TAG, "In network_ctrl_cmd_dispatcher Cmd=%d", req->msg); + + int cmd_index = lookup_cmd_handler(req->msg); + if (cmd_index < 0) { + ESP_LOGE(TAG, "Failed to find cmd with ID = %d in the command table", req->msg); + return ESP_FAIL; + } + + ret = cmd_table[cmd_index].command_handler(req, resp, priv_data); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Error executing command handler"); + } + + return ret; +} + +esp_err_t network_ctrl_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, + uint8_t **outbuf, ssize_t *outlen, void *priv_data) +{ + NetworkCtrlPayload *req; + NetworkCtrlPayload resp; + esp_err_t ret = ESP_OK; + + req = network_ctrl_payload__unpack(NULL, inlen, inbuf); + if (!req) { + ESP_LOGE(TAG, "Unable to unpack ctrl message"); + return ESP_ERR_INVALID_ARG; + } + + network_ctrl_payload__init(&resp); + ret = network_ctrl_cmd_dispatcher(req, &resp, priv_data); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Command dispatcher error %02X", ret); + ret = ESP_FAIL; + goto exit; + } + + resp.msg = req->msg + 1; /* Response is request + 1 */ + *outlen = network_ctrl_payload__get_packed_size(&resp); + if (*outlen <= 0) { + ESP_LOGE(TAG, "Invalid encoding for response"); + ret = ESP_FAIL; + goto exit; + } + + *outbuf = (uint8_t *) malloc(*outlen); + if (!*outbuf) { + ESP_LOGE(TAG, "Failed to allocate memory for the output buffer"); + ret = ESP_ERR_NO_MEM; + goto exit; + } + network_ctrl_payload__pack(&resp, *outbuf); + ESP_LOGD(TAG, "Response packet size : %d", *outlen); +exit: + + network_ctrl_payload__free_unpacked(req, NULL); + network_ctrl_cmd_cleanup(&resp, priv_data); + return ret; +} diff --git a/components/network_provisioning/src/network_ctrl.h b/components/network_provisioning/src/network_ctrl.h new file mode 100644 index 0000000..65b2b51 --- /dev/null +++ b/components/network_provisioning/src/network_ctrl.h @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _PROV_NETWORK_CTRL_H_ +#define _PROV_NETWORK_CTRL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Internal handlers for receiving and responding to protocomm + * requests from client + * + * This is to be passed as priv_data for protocomm request handler + * (refer to `network_ctrl_handler()`) when calling `protocomm_add_endpoint()`. + */ +typedef struct network_ctrl_handlers { +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + /** + * Handler functions called when ctrl reset command is received + */ + esp_err_t (*wifi_ctrl_reset)(void); + + /** + * Handler functions called when ctrl reprov command is received + */ + esp_err_t (*wifi_ctrl_reprov)(void); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + esp_err_t (*thread_ctrl_reset)(void); + esp_err_t (*thread_ctrl_reprov)(void); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +} network_ctrl_handlers_t; + +/** + * @brief Handler for sending on demand network ctrl results + * + * This is to be registered as the `prov-ctrl` endpoint handler + * (protocomm `protocomm_req_handler_t`) using `protocomm_add_endpoint()` + */ +esp_err_t network_ctrl_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, + uint8_t **outbuf, ssize_t *outlen, void *priv_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/network_provisioning/src/network_provisioning_priv.h b/components/network_provisioning/src/network_provisioning_priv.h new file mode 100644 index 0000000..5af6b6c --- /dev/null +++ b/components/network_provisioning/src/network_provisioning_priv.h @@ -0,0 +1,168 @@ +/* + * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#include "network_provisioning/manager.h" +#include "network_provisioning/network_config.h" +#include "network_provisioning/network_scan.h" +#include "network_ctrl.h" + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +#include "openthread/link.h" +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + +/** + * @brief Notify manager that provisioning is done + * + * Stops the provisioning. This is called by the get_status_handler() + * when the status is connected. This has no effect if main application + * has disabled auto stop on completion by calling + * network_prov_mgr_disable_auto_stop() + * + * @return + * - ESP_OK : Provisioning will be stopped + * - ESP_FAIL : Failed to stop provisioning + */ +esp_err_t network_prov_mgr_done(void); + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI +/** + * @brief Start Wi-Fi AP Scan + * + * @param[in] blocking Set true to return only after scanning is complete + * @param[in] passive Set true to perform passive scan instead of default active scan + * @param[in] group_channels Number of channels to scan in one go + * (set to 0 for scanning all channels in one go) + * @param[in] period_ms Scan time (in milli-seconds) on each channel + * + * @return + * - ESP_OK : Successfully started Wi-Fi scanning + * - ESP_FAIL : Provisioning app not running + */ +esp_err_t network_prov_mgr_wifi_scan_start(bool blocking, bool passive, + uint8_t group_channels, + uint32_t period_ms); + +/** + * @brief Use to query the state of Wi-Fi scan + * + * @return + * - true : Scan finished + * - false : Scan running + */ +bool network_prov_mgr_wifi_scan_finished(void); + +/** + * @brief Get the count of results in the scan list + * + * @return + * - count : Number of Wi-Fi Access Points detected while scanning + */ +uint16_t network_prov_mgr_wifi_scan_result_count(void); + +/** + * @brief Get AP record for a particular index in the scan list result + * + * @param[out] index Index of the result to fetch + * + * @return + * - result : Pointer to Access Point record + */ +const wifi_ap_record_t *network_prov_mgr_wifi_scan_result(uint16_t index); +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD +/** + * @brief Start Thread network Scan + * + * @param[in] blocking Set true to return only after scanning is complete + * + * @return + * - ESP_OK : Successfully started Thread scanning + * - ESP_FAIL : Provisioning app not running + */ +esp_err_t network_prov_mgr_thread_scan_start(bool blocking, uint32_t channel_mask); + +/** + * @brief Use to query the state of Thread scan + * + * @return + * - true : Scan finished + * - false : Scan running + */ +bool network_prov_mgr_thread_scan_finished(void); + +/** + * @brief Get the count of results in the scan list + * + * @return + * - count : Number of Thread networks detected while scanning + */ +uint16_t network_prov_mgr_thread_scan_result_count(void); + +/** + * @brief Get Thread network record for a particular index in the scan list result + * + * @param[out] index Index of the result to fetch + * + * @return + * - result : Pointer to Thread network record + */ +const otActiveScanResult *network_prov_mgr_thread_scan_result(uint16_t index); + +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + +/** + * @brief Get protocomm handlers for network_config provisioning endpoint + * + * @param[out] ptr pointer to structure to be set + * + * @return + * - ESP_OK : success + * - ESP_ERR_INVALID_ARG : null argument + */ +esp_err_t get_network_prov_handlers(network_prov_config_handlers_t *ptr); + +/** + * @brief Get protocomm handlers for network_scan provisioning endpoint + * + * @param[out] ptr pointer to structure to be set + * + * @return + * - ESP_OK : success + * - ESP_ERR_INVALID_ARG : null argument + */ +esp_err_t get_network_scan_handlers(network_prov_scan_handlers_t *ptr); + +/** + * @brief Get protocomm handlers for network_ctrl provisioning endpoint + * + * @param[in] ptr pointer to structure to be set + * + * @return + * - ESP_OK : success + * - ESP_ERR_INVALID_ARG : null argument + */ +esp_err_t get_network_ctrl_handlers(network_ctrl_handlers_t *ptr); + +/** + * @brief Retrieve the remaining number of Wi-Fi connection attempts + * + * This function provides the number of Wi-Fi connection attempts left for + * the network provisioning manager before reaching the maximum retry limit. + * + * @param[out] attempts_remaining Pointer to store the remaining connection attempts + * + * @return + * - ESP_OK: Success + * - ESP_ERR_INVALID_ARG: Null pointer provided for attempts_remaining + * - ESP_FAIL: Failed to retrieve the remaining attempts + */ +esp_err_t network_prov_mgr_get_wifi_remaining_conn_attempts(uint32_t *attempts_remaining); diff --git a/components/network_provisioning/src/network_scan.c b/components/network_provisioning/src/network_scan.c new file mode 100644 index 0000000..e03e3fc --- /dev/null +++ b/components/network_provisioning/src/network_scan.c @@ -0,0 +1,675 @@ +/* + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include "network_scan.pb-c.h" + +#include + +static const char *TAG = "proto_network_scan"; + +typedef struct network_prov_scan_cmd { + int cmd_num; + esp_err_t (*command_handler)(NetworkScanPayload *req, + NetworkScanPayload *resp, void *priv_data); +} network_prov_scan_cmd_t; + +static esp_err_t cmd_scan_start_handler(NetworkScanPayload *req, + NetworkScanPayload *resp, + void *priv_data); + +static esp_err_t cmd_scan_status_handler(NetworkScanPayload *req, + NetworkScanPayload *resp, + void *priv_data); + +static esp_err_t cmd_scan_result_handler(NetworkScanPayload *req, + NetworkScanPayload *resp, + void *priv_data); + +static network_prov_scan_cmd_t cmd_table[] = { + { + .cmd_num = NETWORK_SCAN_MSG_TYPE__TypeCmdScanWifiStart, + .command_handler = cmd_scan_start_handler + }, + { + .cmd_num = NETWORK_SCAN_MSG_TYPE__TypeCmdScanWifiStatus, + .command_handler = cmd_scan_status_handler + }, + { + .cmd_num = NETWORK_SCAN_MSG_TYPE__TypeCmdScanWifiResult, + .command_handler = cmd_scan_result_handler + }, + { + .cmd_num = NETWORK_SCAN_MSG_TYPE__TypeCmdScanThreadStart, + .command_handler = cmd_scan_start_handler + }, + { + .cmd_num = NETWORK_SCAN_MSG_TYPE__TypeCmdScanThreadStatus, + .command_handler = cmd_scan_status_handler + }, + { + .cmd_num = NETWORK_SCAN_MSG_TYPE__TypeCmdScanThreadResult, + .command_handler = cmd_scan_result_handler + } +}; + +static size_t sanitize_ssid_for_client(const char *src, char *dst, size_t dst_len) +{ + if (!src || !dst || dst_len == 0) { + return 0; + } + + size_t src_len = strnlen(src, 32); + size_t out = 0; + for (size_t i = 0; i < src_len && out < (dst_len - 1); i++) { + unsigned char c = (unsigned char)src[i]; + /* Keep returned SSID bytes ASCII-safe to avoid client UTF-8 decode failures. */ + if (c >= 0x20 && c <= 0x7E) { + dst[out++] = (char)c; + } else { + dst[out++] = '?'; + } + } + + dst[out] = '\0'; + return out; +} + +static uint32_t map_auth_for_client(uint8_t authmode) +{ + switch (authmode) { + case WIFI_AUTH_OPEN: + return WIFI_AUTH_MODE__Open; + case WIFI_AUTH_WEP: + return WIFI_AUTH_MODE__WEP; + case WIFI_AUTH_WPA_PSK: + return WIFI_AUTH_MODE__WPA_PSK; + case WIFI_AUTH_WPA2_PSK: + return WIFI_AUTH_MODE__WPA2_PSK; + case WIFI_AUTH_WPA_WPA2_PSK: + return WIFI_AUTH_MODE__WPA_WPA2_PSK; + case WIFI_AUTH_WPA2_ENTERPRISE: + return WIFI_AUTH_MODE__WPA2_ENTERPRISE; +#ifdef WIFI_AUTH_WPA3_PSK + case WIFI_AUTH_WPA3_PSK: + return WIFI_AUTH_MODE__WPA3_PSK; +#endif +#ifdef WIFI_AUTH_WPA2_WPA3_PSK + case WIFI_AUTH_WPA2_WPA3_PSK: + return WIFI_AUTH_MODE__WPA2_WPA3_PSK; +#endif + default: + /* Keep a known enum value for older clients when AP auth mode is newer/unknown. */ + return WIFI_AUTH_MODE__WPA2_PSK; + } +} + +static esp_err_t cmd_scan_start_handler(NetworkScanPayload *req, + NetworkScanPayload *resp, void *priv_data) +{ + network_prov_scan_handlers_t *h = (network_prov_scan_handlers_t *) priv_data; + if (!h) { + ESP_LOGE(TAG, "Command invoked without handlers"); + return ESP_ERR_INVALID_STATE; + } + if (req->msg == NETWORK_SCAN_MSG_TYPE__TypeCmdScanWifiStart) { + RespScanWifiStart *resp_payload = (RespScanWifiStart *) malloc(sizeof(RespScanWifiStart)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_scan_wifi_start__init(resp_payload); + + bool blocking = false; + bool passive = false; + uint8_t group_channels = 4; + uint32_t period_ms = 120; + + if (req->payload_case != NETWORK_SCAN_PAYLOAD__PAYLOAD_CMD_SCAN_WIFI_START || !req->cmd_scan_wifi_start) { + ESP_LOGW(TAG, "Invalid WiFi scan start command payload; using safe defaults"); + } else { + blocking = req->cmd_scan_wifi_start->blocking; + passive = req->cmd_scan_wifi_start->passive; + group_channels = req->cmd_scan_wifi_start->group_channels; + period_ms = req->cmd_scan_wifi_start->period_ms; + } + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + if (h->wifi_scan_start) { + resp->status = (h->wifi_scan_start(blocking, + passive, + group_channels, + period_ms, + &h->ctx) == ESP_OK ? STATUS__Success : STATUS__InternalError); + } else { + resp->status = STATUS__InternalError; + } +#else + resp->status = STATUS__InvalidArgument; +#endif + resp->payload_case = NETWORK_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_WIFI_START; + resp->resp_scan_wifi_start = resp_payload; + } else if (req->msg == NETWORK_SCAN_MSG_TYPE__TypeCmdScanThreadStart) { + RespScanThreadStart *resp_payload = (RespScanThreadStart *) malloc(sizeof(RespScanThreadStart)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_scan_thread_start__init(resp_payload); + + if (req->payload_case != NETWORK_SCAN_PAYLOAD__PAYLOAD_CMD_SCAN_THREAD_START || !req->cmd_scan_thread_start) { + ESP_LOGE(TAG, "Invalid Thread scan start command"); + resp->resp_scan_thread_start = resp_payload; + return ESP_ERR_INVALID_ARG; + } + +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + if (h->thread_scan_start) { + resp->status = (h->thread_scan_start(req->cmd_scan_thread_start->blocking, + req->cmd_scan_thread_start->channel_mask, + &h->ctx) == ESP_OK ? STATUS__Success : STATUS__InternalError); + } else { + resp->status = STATUS__InternalError; + } +#else + resp->status = STATUS__InvalidArgument; +#endif + resp->payload_case = NETWORK_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_THREAD_START; + resp->resp_scan_thread_start = resp_payload; + } + return ESP_OK; +} + +static esp_err_t cmd_scan_status_handler(NetworkScanPayload *req, + NetworkScanPayload *resp, void *priv_data) +{ + bool scan_finished = false; + uint16_t result_count = 0; + + network_prov_scan_handlers_t *h = (network_prov_scan_handlers_t *) priv_data; + if (!h) { + ESP_LOGE(TAG, "Command invoked without handlers"); + return ESP_ERR_INVALID_STATE; + } + if (req->msg == NETWORK_SCAN_MSG_TYPE__TypeCmdScanWifiStatus) { + RespScanWifiStatus *resp_payload = (RespScanWifiStatus *) malloc(sizeof(RespScanWifiStatus)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_scan_wifi_status__init(resp_payload); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + if (h->wifi_scan_status) { + resp->status = (h->wifi_scan_status(&scan_finished, &result_count, &h->ctx) == ESP_OK ? + STATUS__Success : STATUS__InternalError); + } else { + resp->status = STATUS__InternalError; + } +#else + resp->status = STATUS__InvalidArgument; +#endif + resp_payload->scan_finished = scan_finished; + resp_payload->result_count = result_count; + resp->payload_case = NETWORK_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_WIFI_STATUS; + resp->resp_scan_wifi_status = resp_payload; + } else if (req->msg == NETWORK_SCAN_MSG_TYPE__TypeCmdScanThreadStatus) { + RespScanThreadStatus *resp_payload = (RespScanThreadStatus *) malloc(sizeof(RespScanThreadStatus)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_scan_thread_status__init(resp_payload); +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + if (h->thread_scan_status) { + resp->status = (h->thread_scan_status(&scan_finished, &result_count, &h->ctx) == ESP_OK ? + STATUS__Success : STATUS__InternalError); + } else { + resp->status = STATUS__InternalError; + } +#else + resp->status = STATUS__InvalidArgument; +#endif + resp_payload->scan_finished = scan_finished; + resp_payload->result_count = result_count; + resp->payload_case = NETWORK_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_THREAD_STATUS; + resp->resp_scan_thread_status = resp_payload; + } + return ESP_OK; +} + +static esp_err_t cmd_scan_result_handler(NetworkScanPayload *req, + NetworkScanPayload *resp, void *priv_data) +{ + esp_err_t err = ESP_OK; + network_prov_scan_handlers_t *h = (network_prov_scan_handlers_t *) priv_data; + if (!h) { + ESP_LOGE(TAG, "Command invoked without handlers"); + return ESP_ERR_INVALID_STATE; + } + if (req->msg == NETWORK_SCAN_MSG_TYPE__TypeCmdScanWifiResult) { + RespScanWifiResult *resp_payload = (RespScanWifiResult *) malloc(sizeof(RespScanWifiResult)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_scan_wifi_result__init(resp_payload); + + uint16_t start_index = 0; + uint16_t req_wifi_count = 4; + + if (req->payload_case != NETWORK_SCAN_PAYLOAD__PAYLOAD_CMD_SCAN_WIFI_RESULT || !req->cmd_scan_wifi_result) { + ESP_LOGW(TAG, "Invalid WiFi scan result command payload; using defaults start=0 count=4"); + } else { + start_index = req->cmd_scan_wifi_result->start_index; + req_wifi_count = req->cmd_scan_wifi_result->count; + } + + if (start_index >= CONFIG_NETWORK_PROV_SCAN_MAX_ENTRIES) { + ESP_LOGE(TAG, "WiFi scan result count/start_index out of bounds"); + resp->resp_scan_wifi_result = resp_payload; + return ESP_ERR_INVALID_ARG; + } + + uint16_t max_wifi_count = CONFIG_NETWORK_PROV_SCAN_MAX_ENTRIES - start_index; + uint16_t effective_wifi_count = MIN(req_wifi_count, max_wifi_count); + ESP_LOGI(TAG, "WiFi scan result requested: start=%u count=%u (effective=%u)", + start_index, + req_wifi_count, + effective_wifi_count); + if (req_wifi_count != effective_wifi_count) { + ESP_LOGW(TAG, "Clamping WiFi scan result count from %u to %u (start_index=%u)", + req_wifi_count, effective_wifi_count, start_index); + } + + resp->status = STATUS__Success; + resp->payload_case = NETWORK_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_WIFI_RESULT; + resp->resp_scan_wifi_result = resp_payload; +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + network_prov_scan_wifi_result_t scan_result = {{0}, {0}, 0, 0, 0}; + WiFiScanResult **results = NULL; + + /* Allocate memory only if there are non-zero scan results */ + if (effective_wifi_count) { + results = (WiFiScanResult **) calloc(effective_wifi_count, + sizeof(WiFiScanResult *)); + if (!results) { + ESP_LOGE(TAG, "Failed to allocate memory for results array"); + return ESP_ERR_NO_MEM; + } + } + resp_payload->entries = results; + resp_payload->n_entries = effective_wifi_count; + + /* If req->cmd_scan_wifi_result->count is 0, the below loop will + * be skipped. + */ + for (uint32_t i = 0; i < effective_wifi_count; i++) { + if (!h->wifi_scan_result) { + resp_payload->n_entries = i; + resp->status = STATUS__InternalError; + break; + } + /* start_index and count are validated above to sum to at most + * CONFIG_NETWORK_PROV_SCAN_MAX_ENTRIES (max 255), so this cast is safe. */ + uint16_t result_index = (uint16_t)(i + start_index); + err = h->wifi_scan_result(result_index, &scan_result, &h->ctx); + if (err != ESP_OK) { + resp_payload->n_entries = i; + resp->status = STATUS__InternalError; + break; + } + + results[i] = (WiFiScanResult *) malloc(sizeof(WiFiScanResult)); + if (!results[i]) { + ESP_LOGE(TAG, "Failed to allocate memory for result entry"); + resp_payload->n_entries = i; + resp->status = STATUS__InternalError; + return ESP_ERR_NO_MEM; + } + wi_fi_scan_result__init(results[i]); + + char ssid_sanitized[33] = {0}; + size_t ssid_len = sanitize_ssid_for_client(scan_result.ssid, ssid_sanitized, sizeof(ssid_sanitized)); + results[i]->ssid.len = ssid_len; + results[i]->ssid.data = (uint8_t *) strdup(ssid_sanitized); + if (!results[i]->ssid.data) { + ESP_LOGE(TAG, "Failed to allocate memory for scan result entry SSID"); + results[i]->ssid.len = 0; + resp_payload->n_entries = i + 1; + resp->status = STATUS__InternalError; + return ESP_ERR_NO_MEM; + } + + results[i]->channel = scan_result.channel; + results[i]->rssi = scan_result.rssi; + results[i]->auth = map_auth_for_client(scan_result.auth); + + results[i]->bssid.len = sizeof(scan_result.bssid); + results[i]->bssid.data = malloc(results[i]->bssid.len); + if (!results[i]->bssid.data) { + ESP_LOGE(TAG, "Failed to allocate memory for scan result entry BSSID"); + results[i]->bssid.len = 0; + resp_payload->n_entries = i + 1; + resp->status = STATUS__InternalError; + return ESP_ERR_NO_MEM; + } + memcpy(results[i]->bssid.data, scan_result.bssid, results[i]->bssid.len); + + ESP_LOGI(TAG, + "WiFi scan entry[%u]: ssid='%s' ch=%u rssi=%d auth_raw=%u auth_sent=%u", + (unsigned int)i, + ssid_sanitized, + (unsigned int)results[i]->channel, + (int)results[i]->rssi, + (unsigned int)scan_result.auth, + (unsigned int)results[i]->auth); + } + ESP_LOGI(TAG, "WiFi scan result responded: entries=%u status=%d", + (unsigned int)resp_payload->n_entries, + (int)resp->status); +#else // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + resp->status = STATUS__InvalidArgument; +#endif // !CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + } else if (req->msg == NETWORK_SCAN_MSG_TYPE__TypeCmdScanThreadResult) { + RespScanThreadResult *resp_payload = (RespScanThreadResult *) malloc(sizeof(RespScanThreadResult)); + if (!resp_payload) { + ESP_LOGE(TAG, "Error allocating memory"); + return ESP_ERR_NO_MEM; + } + resp_scan_thread_result__init(resp_payload); + + if (req->payload_case != NETWORK_SCAN_PAYLOAD__PAYLOAD_CMD_SCAN_THREAD_RESULT || !req->cmd_scan_thread_result) { + ESP_LOGE(TAG, "Invalid Thread scan result command"); + resp->resp_scan_thread_result = resp_payload; + return ESP_ERR_INVALID_ARG; + } + + if (req->cmd_scan_thread_result->start_index >= CONFIG_NETWORK_PROV_SCAN_MAX_ENTRIES) { + ESP_LOGE(TAG, "Thread scan result count/start_index out of bounds"); + resp->resp_scan_thread_result = resp_payload; + return ESP_ERR_INVALID_ARG; + } + + uint16_t req_thread_count = req->cmd_scan_thread_result->count; + uint16_t max_thread_count = CONFIG_NETWORK_PROV_SCAN_MAX_ENTRIES - req->cmd_scan_thread_result->start_index; + uint16_t effective_thread_count = MIN(req_thread_count, max_thread_count); + if (req_thread_count != effective_thread_count) { + ESP_LOGW(TAG, "Clamping Thread scan result count from %u to %u (start_index=%u)", + req_thread_count, effective_thread_count, req->cmd_scan_thread_result->start_index); + } + + resp->status = STATUS__Success; + resp->payload_case = NETWORK_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_THREAD_RESULT; + resp->resp_scan_thread_result = resp_payload; +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + network_prov_scan_thread_result_t scan_result; + memset(&scan_result, 0, sizeof(scan_result)); + ThreadScanResult **results = NULL; + + /* Allocate memory only if there are non-zero scan results */ + if (effective_thread_count) { + results = (ThreadScanResult **) calloc(effective_thread_count, + sizeof(ThreadScanResult *)); + if (!results) { + ESP_LOGE(TAG, "Failed to allocate memory for results array"); + return ESP_ERR_NO_MEM; + } + } + resp_payload->entries = results; + resp_payload->n_entries = effective_thread_count; + + /* If req->cmd_scan_result->count is 0, the below loop will + * be skipped. + */ + for (uint32_t i = 0; i < effective_thread_count; i++) { + if (!h->thread_scan_result) { + resp_payload->n_entries = i; + resp->status = STATUS__InternalError; + break; + } + /* start_index and count are validated above to sum to at most + * CONFIG_NETWORK_PROV_SCAN_MAX_ENTRIES (max 255), so this cast is safe. */ + uint16_t result_index = (uint16_t)(i + req->cmd_scan_thread_result->start_index); + err = h->thread_scan_result(result_index, &scan_result, &h->ctx); + if (err != ESP_OK) { + resp_payload->n_entries = i; + resp->status = STATUS__InternalError; + break; + } + + results[i] = (ThreadScanResult *) malloc(sizeof(ThreadScanResult)); + if (!results[i]) { + ESP_LOGE(TAG, "Failed to allocate memory for result entry"); + resp_payload->n_entries = i; + resp->status = STATUS__InternalError; + return ESP_ERR_NO_MEM; + } + thread_scan_result__init(results[i]); + results[i]->pan_id = scan_result.pan_id; + results[i]->channel = scan_result.channel; + results[i]->rssi = scan_result.rssi; + results[i]->lqi = scan_result.lqi; + + results[i]->ext_addr.len = sizeof(scan_result.ext_addr); + results[i]->ext_addr.data = (uint8_t *)malloc(results[i]->ext_addr.len); + if (!results[i]->ext_addr.data) { + ESP_LOGE(TAG, "Failed to allocate memory for scan result entry extended address"); + results[i]->ext_addr.len = 0; + resp_payload->n_entries = i + 1; + resp->status = STATUS__InternalError; + return ESP_ERR_NO_MEM; + } + memcpy(results[i]->ext_addr.data, scan_result.ext_addr, results[i]->ext_addr.len); + + results[i]->ext_pan_id.len = sizeof(scan_result.ext_pan_id); + results[i]->ext_pan_id.data = (uint8_t *)malloc(results[i]->ext_pan_id.len); + if (!results[i]->ext_pan_id.data) { + ESP_LOGE(TAG, "Failed to allocate memory for scan result entry extended PAN ID"); + results[i]->ext_pan_id.len = 0; + resp_payload->n_entries = i + 1; + resp->status = STATUS__InternalError; + return ESP_ERR_NO_MEM; + } + memcpy(results[i]->ext_pan_id.data, scan_result.ext_pan_id, results[i]->ext_pan_id.len); + + results[i]->network_name = (char *)malloc(sizeof(scan_result.network_name)); + if (!results[i]->network_name) { + ESP_LOGE(TAG, "Failed to allocate memory for scan result entry network name"); + resp_payload->n_entries = i + 1; + resp->status = STATUS__InternalError; + return ESP_ERR_NO_MEM; + } + memcpy(results[i]->network_name, scan_result.network_name, sizeof(scan_result.network_name)); + } +#else // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + resp->status = STATUS__InvalidArgument; + err = ESP_ERR_INVALID_ARG; +#endif // !CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + } + return err; +} + + +static int lookup_cmd_handler(int cmd_id) +{ + for (size_t i = 0; i < sizeof(cmd_table) / sizeof(network_prov_scan_cmd_t); i++) { + if (cmd_table[i].cmd_num == cmd_id) { + return i; + } + } + + return -1; +} + +static void network_prov_scan_cmd_cleanup(NetworkScanPayload *resp, void *priv_data) +{ + switch (resp->msg) { + case NETWORK_SCAN_MSG_TYPE__TypeRespScanWifiStart: { + free(resp->resp_scan_wifi_start); + } + break; + case NETWORK_SCAN_MSG_TYPE__TypeRespScanThreadStart: { + free(resp->resp_scan_thread_start); + } + break; + case NETWORK_SCAN_MSG_TYPE__TypeRespScanWifiStatus: { + free(resp->resp_scan_wifi_status); + } + break; + case NETWORK_SCAN_MSG_TYPE__TypeRespScanThreadStatus: { + free(resp->resp_scan_thread_status); + } + break; + + case NETWORK_SCAN_MSG_TYPE__TypeRespScanWifiResult: { + if (!resp->resp_scan_wifi_result) { + return; + } +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + if (resp->resp_scan_wifi_result->entries) { + for (uint32_t i = 0; i < resp->resp_scan_wifi_result->n_entries; i++) { + if (!resp->resp_scan_wifi_result->entries[i]) { + continue; + } + free(resp->resp_scan_wifi_result->entries[i]->ssid.data); + free(resp->resp_scan_wifi_result->entries[i]->bssid.data); + free(resp->resp_scan_wifi_result->entries[i]); + } + free(resp->resp_scan_wifi_result->entries); + } +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + free(resp->resp_scan_wifi_result); + } + break; + case NETWORK_SCAN_MSG_TYPE__TypeRespScanThreadResult: { + if (!resp->resp_scan_thread_result) { + return; + } +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + if (resp->resp_scan_thread_result->entries) { + for (size_t i = 0; i < resp->resp_scan_thread_result->n_entries; i++) { + if (!resp->resp_scan_thread_result->entries[i]) { + continue; + } + free(resp->resp_scan_thread_result->entries[i]->ext_addr.data); + free(resp->resp_scan_thread_result->entries[i]->ext_pan_id.data); + if (resp->resp_scan_thread_result->entries[i]->network_name != protobuf_c_empty_string) { + free(resp->resp_scan_thread_result->entries[i]->network_name); + } + free(resp->resp_scan_thread_result->entries[i]); + } + free(resp->resp_scan_thread_result->entries); + } +#endif // CONFIG_NETWORK_PROV_NETWORK_TYPE_THREAD + free(resp->resp_scan_thread_result); + } + break; + default: + ESP_LOGE(TAG, "Unsupported response type in cleanup_handler"); + break; + } + return; +} + +static esp_err_t network_prov_scan_cmd_dispatcher(NetworkScanPayload *req, + NetworkScanPayload *resp, void *priv_data) +{ + esp_err_t ret; + + ESP_LOGD(TAG, "In network_prov_scan_cmd_dispatcher Cmd=%d", req->msg); + + int cmd_index = lookup_cmd_handler(req->msg); + if (cmd_index < 0) { + ESP_LOGE(TAG, "Invalid command handler lookup"); + return ESP_FAIL; + } + + ret = cmd_table[cmd_index].command_handler(req, resp, priv_data); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Error executing command handler"); + return ESP_FAIL; + } + + return ESP_OK; +} + +static esp_err_t network_prov_scan_pack_response(NetworkScanPayload *resp, + uint8_t **outbuf, ssize_t *outlen) +{ + if (!resp || !outbuf || !outlen) { + return ESP_ERR_INVALID_ARG; + } + + *outlen = network_scan_payload__get_packed_size(resp); + if (*outlen <= 0) { + return ESP_FAIL; + } + + *outbuf = (uint8_t *) malloc(*outlen); + if (!*outbuf) { + return ESP_ERR_NO_MEM; + } + + network_scan_payload__pack(resp, *outbuf); + ESP_LOGD(TAG, "Response packet size : %d", *outlen); + return ESP_OK; +} + +esp_err_t network_prov_scan_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, + uint8_t **outbuf, ssize_t *outlen, void *priv_data) +{ + NetworkScanPayload *req; + NetworkScanPayload resp; + esp_err_t ret = ESP_OK; + + req = network_scan_payload__unpack(NULL, inlen, inbuf); + if (!req) { + NetworkScanPayload decode_err_resp; + network_scan_payload__init(&decode_err_resp); + + /* Ensure encrypted transport always has a non-empty protobuf payload. */ + decode_err_resp.msg = NETWORK_SCAN_MSG_TYPE__TypeRespScanWifiStatus; + decode_err_resp.status = STATUS__InvalidArgument; + + ESP_LOGW(TAG, "Unable to unpack scan message (len=%d), returning error response", (int)inlen); + return network_prov_scan_pack_response(&decode_err_resp, outbuf, outlen); + } + + network_scan_payload__init(&resp); + /* Validate req->msg before arithmetic to avoid signed overflow on attacker-controlled + * wire values. For unknown commands the dispatcher returns ESP_FAIL without calling + * any handler, so nothing is allocated and resp.msg = 0 is safe for cleanup. */ + if (lookup_cmd_handler(req->msg) >= 0) { + resp.msg = req->msg + 1; + } + ret = network_prov_scan_cmd_dispatcher(req, &resp, priv_data); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Command dispatcher error %d", ret); + /* Keep session stable: serialize an explicit error response. */ + resp.status = STATUS__InternalError; + if (resp.msg == 0) { + resp.msg = NETWORK_SCAN_MSG_TYPE__TypeRespScanWifiStatus; + } + ret = network_prov_scan_pack_response(&resp, outbuf, outlen); + goto exit; + } + + ret = network_prov_scan_pack_response(&resp, outbuf, outlen); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Invalid encoding for response"); + goto exit; + } +exit: + + network_scan_payload__free_unpacked(req, NULL); + network_prov_scan_cmd_cleanup(&resp, priv_data); + return ret; +} diff --git a/components/network_provisioning/src/scheme_ble.c b/components/network_provisioning/src/scheme_ble.c new file mode 100644 index 0000000..565284b --- /dev/null +++ b/components/network_provisioning/src/scheme_ble.c @@ -0,0 +1,344 @@ +/* + * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#ifdef CONFIG_BT_CONTROLLER_ENABLED +#include "esp_bt.h" +#endif + +#include +#include + +#include "network_provisioning/scheme_ble.h" +#include "network_provisioning_priv.h" + +static const char *TAG = "network_prov_scheme_ble"; + +extern const network_prov_scheme_t network_prov_scheme_ble; + +static uint8_t *custom_service_uuid; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +static uint8_t *custom_ble_addr; +static uint8_t custom_keep_ble_on; +#endif + +static uint8_t *custom_manufacturer_data; +static size_t custom_manufacturer_data_len; + +static esp_err_t prov_start(protocomm_t *pc, void *config) +{ + if (!pc) { + ESP_LOGE(TAG, "Protocomm handle cannot be null"); + return ESP_ERR_INVALID_ARG; + } + + if (!config) { + ESP_LOGE(TAG, "Cannot start with null configuration"); + return ESP_ERR_INVALID_ARG; + } + + protocomm_ble_config_t *ble_config = (protocomm_ble_config_t *) config; + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) + ble_config->keep_ble_on = custom_keep_ble_on; +#endif + +#if defined(CONFIG_NETWORK_PROV_BLE_BONDING) + ble_config->ble_bonding = 1; +#endif + +#if defined(CONFIG_NETWORK_PROV_BLE_SEC_CONN) || defined(CONFIG_BT_BLUEDROID_ENABLED) + ble_config->ble_sm_sc = 1; +#endif + +#if defined(CONFIG_NETWORK_PROV_BLE_FORCE_ENCRYPTION) + ble_config->ble_link_encryption = 1; +#endif + +#if defined(CONFIG_NETWORK_PROV_BLE_NOTIFY) + ble_config->ble_notify = 1; +#endif + /* Start protocomm as BLE service */ + if (protocomm_ble_start(pc, ble_config) != ESP_OK) { + ESP_LOGE(TAG, "Failed to start protocomm BLE service"); + return ESP_FAIL; + } + return ESP_OK; +} + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +esp_err_t network_prov_scheme_ble_set_random_addr(const uint8_t *addr) +{ + if (!addr) { + return ESP_ERR_INVALID_ARG; + } + + custom_ble_addr = (uint8_t *) malloc(BLE_ADDR_LEN); + if (custom_ble_addr == NULL) { + ESP_LOGE(TAG, "Error allocating memory for random address"); + return ESP_ERR_NO_MEM; + } + + memcpy(custom_ble_addr, addr, BLE_ADDR_LEN); + return ESP_OK; +} + +esp_err_t network_prov_mgr_keep_ble_on(uint8_t is_on_after_ble_stop) +{ + if (!is_on_after_ble_stop) { + return ESP_ERR_INVALID_ARG; + } + + custom_keep_ble_on = is_on_after_ble_stop; + return ESP_OK; +} +#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) + + +esp_err_t network_prov_scheme_ble_set_service_uuid(uint8_t *uuid128) +{ + if (!uuid128) { + return ESP_ERR_INVALID_ARG; + } + custom_service_uuid = uuid128; + return ESP_OK; +} + +esp_err_t network_prov_scheme_ble_set_mfg_data(uint8_t *mfg_data, ssize_t mfg_data_len) +{ + if (!mfg_data || !mfg_data_len) { + return ESP_ERR_INVALID_ARG; + } + + custom_manufacturer_data = (uint8_t *) malloc(mfg_data_len); + if (custom_manufacturer_data == NULL) { + ESP_LOGE(TAG, "Error allocating memory for mfg_data"); + return ESP_ERR_NO_MEM; + } + + custom_manufacturer_data_len = mfg_data_len; + memcpy(custom_manufacturer_data, mfg_data, mfg_data_len); + return ESP_OK; +} + +static void *new_config(void) +{ + protocomm_ble_config_t *ble_config = calloc(1, sizeof(protocomm_ble_config_t)); + if (!ble_config) { + ESP_LOGE(TAG, "Error allocating memory for new configuration"); + return NULL; + } + + /* The default provisioning service UUID */ + const uint8_t service_uuid[16] = { + /* LSB <--------------------------------------- + * ---------------------------------------> MSB */ + 0x07, 0xed, 0x9b, 0x2d, 0x0f, 0x06, 0x7c, 0x87, + 0x9b, 0x43, 0x43, 0x6b, 0x4d, 0x24, 0x75, 0x17, + }; + + memcpy(ble_config->service_uuid, service_uuid, sizeof(ble_config->service_uuid)); + return ble_config; +} + +static void delete_config(void *config) +{ + if (!config) { + ESP_LOGE(TAG, "Cannot delete null configuration"); + return; + } + + protocomm_ble_config_t *ble_config = (protocomm_ble_config_t *) config; + for (unsigned int i = 0; i < ble_config->nu_lookup_count; i++) { + free((void *)ble_config->nu_lookup[i].name); + } + free(ble_config->nu_lookup); + free(ble_config); +} + +static esp_err_t set_config_service(void *config, const char *service_name, const char *service_key) +{ + if (!config) { + ESP_LOGE(TAG, "Cannot set null configuration"); + return ESP_ERR_INVALID_ARG; + } + + if (!service_name) { + ESP_LOGE(TAG, "Service name cannot be NULL"); + return ESP_ERR_INVALID_ARG; + } + + protocomm_ble_config_t *ble_config = (protocomm_ble_config_t *) config; + strlcpy(ble_config->device_name, service_name, sizeof(ble_config->device_name)); + + /* If a custom service UUID has been provided, override the default one */ + if (custom_service_uuid) { + memcpy(ble_config->service_uuid, custom_service_uuid, sizeof(ble_config->service_uuid)); + } + /* Set manufacturer data if it is provided by app */ + if (custom_manufacturer_data) { + size_t mfg_data_len = custom_manufacturer_data_len; + size_t dev_name_len = strnlen(ble_config->device_name, MAX_BLE_DEVNAME_LEN); + + if ((dev_name_len + 2) >= MAX_BLE_MANUFACTURER_DATA_LEN) { + /* No space left for manufacturer data */ + ESP_LOGE(TAG, "No space left for Manufacturer data "); + ble_config->manufacturer_data = NULL; + ble_config->manufacturer_data_len = 0; + } else { + if ((mfg_data_len + (dev_name_len ? (dev_name_len + 2) : 0)) > MAX_BLE_MANUFACTURER_DATA_LEN) { + ESP_LOGE(TAG, "Manufacturer data length is more than the max allowed size; expect truncated mfg_data "); + /* Truncate the mfg_data to fit in the available length */ + mfg_data_len = MAX_BLE_MANUFACTURER_DATA_LEN - (dev_name_len ? (dev_name_len + 2) : 0); + } + + ble_config->manufacturer_data = custom_manufacturer_data; + ble_config->manufacturer_data_len = mfg_data_len; + } + } else { + ble_config->manufacturer_data = NULL; + ble_config->manufacturer_data_len = 0; + } + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) + if (custom_ble_addr) { + ble_config->ble_addr = custom_ble_addr; + } else { + ble_config->ble_addr = NULL; + } +#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) + + return ESP_OK; +} + +static esp_err_t set_config_endpoint(void *config, const char *endpoint_name, uint16_t uuid) +{ + if (!config) { + ESP_LOGE(TAG, "Cannot set null configuration"); + return ESP_ERR_INVALID_ARG; + } + + if (!endpoint_name) { + ESP_LOGE(TAG, "EP name cannot be null"); + return ESP_ERR_INVALID_ARG; + } + + protocomm_ble_config_t *ble_config = (protocomm_ble_config_t *) config; + + char *copy_ep_name = strdup(endpoint_name); + if (!copy_ep_name) { + ESP_LOGE(TAG, "Error allocating memory for EP name"); + return ESP_ERR_NO_MEM; + } + + protocomm_ble_name_uuid_t *lookup_table = ( + realloc(ble_config->nu_lookup, (ble_config->nu_lookup_count + 1) * sizeof(protocomm_ble_name_uuid_t))); + if (!lookup_table) { + ESP_LOGE(TAG, "Error allocating memory for EP-UUID lookup table"); + free(copy_ep_name); + return ESP_ERR_NO_MEM; + } + + lookup_table[ble_config->nu_lookup_count].name = copy_ep_name; + lookup_table[ble_config->nu_lookup_count].uuid = uuid; + ble_config->nu_lookup = lookup_table; + ble_config->nu_lookup_count += 1; + return ESP_OK; +} + +/* Used when both BT and BLE are not needed by application */ +void network_prov_scheme_ble_event_cb_free_btdm(void *user_data, network_prov_cb_event_t event, void *event_data) +{ +#ifdef CONFIG_BT_CONTROLLER_ENABLED + esp_err_t err; + switch (event) { + case NETWORK_PROV_INIT: + /* Release BT memory, as we need only BLE */ + err = esp_bt_mem_release(ESP_BT_MODE_CLASSIC_BT); + if (err != ESP_OK) { + ESP_LOGE(TAG, "bt_mem_release of classic BT failed %d", err); + } else { + ESP_LOGI(TAG, "BT memory released"); + } + break; + + case NETWORK_PROV_DEINIT: +#ifndef CONFIG_NETWORK_PROV_KEEP_BLE_ON_AFTER_PROV + /* Release memory used by BLE and Bluedroid host stack */ + err = esp_bt_mem_release(ESP_BT_MODE_BTDM); + if (err != ESP_OK) { + ESP_LOGE(TAG, "bt_mem_release of BTDM failed %d", err); + } else { + ESP_LOGI(TAG, "BTDM memory released"); + } +#endif /* !CONFIG_NETWORK_PROV_KEEP_BLE_ON_AFTER_PROV */ + break; + + default: + break; + } +#endif /* CONFIG_BT_CONTROLLER_ENABLED */ +} + +/* Used when BT is not needed by application */ +void network_prov_scheme_ble_event_cb_free_bt(void *user_data, network_prov_cb_event_t event, void *event_data) +{ +#ifdef CONFIG_BT_CONTROLLER_ENABLED + esp_err_t err; + switch (event) { + case NETWORK_PROV_INIT: + /* Release BT memory, as we need only BLE */ + err = esp_bt_mem_release(ESP_BT_MODE_CLASSIC_BT); + if (err != ESP_OK) { + ESP_LOGE(TAG, "bt_mem_release of classic BT failed %d", err); + } else { + ESP_LOGI(TAG, "BT memory released"); + } + break; + + default: + break; + } +#endif /* CONFIG_BT_CONTROLLER_ENABLED */ +} + +/* Used when BLE is not needed by application */ +void network_prov_scheme_ble_event_cb_free_ble(void *user_data, network_prov_cb_event_t event, void *event_data) +{ +#ifdef CONFIG_BT_CONTROLLER_ENABLED + esp_err_t err; + switch (event) { + case NETWORK_PROV_DEINIT: +#ifndef CONFIG_NETWORK_PROV_KEEP_BLE_ON_AFTER_PROV + /* Release memory used by BLE stack */ + err = esp_bt_mem_release(ESP_BT_MODE_BLE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "bt_mem_release of BLE failed %d", err); + } else { + ESP_LOGI(TAG, "BLE memory released"); + } +#endif /* !CONFIG_NETWORK_PROV_KEEP_BLE_ON_AFTER_PROV */ + break; + + default: + break; + } +#endif /* CONFIG_BT_CONTROLLER_ENABLED */ +} + +const network_prov_scheme_t network_prov_scheme_ble = { + .prov_start = prov_start, + .prov_stop = protocomm_ble_stop, + .new_config = new_config, + .delete_config = delete_config, + .set_config_service = set_config_service, + .set_config_endpoint = set_config_endpoint, +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + .wifi_mode = WIFI_MODE_STA +#endif +}; diff --git a/components/network_provisioning/src/scheme_console.c b/components/network_provisioning/src/scheme_console.c new file mode 100644 index 0000000..e380c81 --- /dev/null +++ b/components/network_provisioning/src/scheme_console.c @@ -0,0 +1,86 @@ +/* + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include + +#include "network_provisioning/scheme_console.h" +#include "network_provisioning_priv.h" + +static const char *TAG = "network_prov_scheme_console"; + +extern const network_prov_scheme_t network_prov_scheme_console; + +static esp_err_t prov_start(protocomm_t *pc, void *config) +{ + if (!pc) { + ESP_LOGE(TAG, "Protocomm handle cannot be null"); + return ESP_ERR_INVALID_ARG; + } + + if (!config) { + ESP_LOGE(TAG, "Cannot start with null configuration"); + return ESP_ERR_INVALID_ARG; + } + + protocomm_console_config_t *console_config = (protocomm_console_config_t *) config; + + /* Start protocomm console */ + esp_err_t err = protocomm_console_start(pc, console_config); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to start protocomm HTTP server"); + return ESP_FAIL; + } + return ESP_OK; +} + +static void *new_config(void) +{ + protocomm_console_config_t *console_config = malloc(sizeof(protocomm_console_config_t)); + if (!console_config) { + ESP_LOGE(TAG, "Error allocating memory for new configuration"); + return NULL; + } + protocomm_console_config_t default_config = PROTOCOMM_CONSOLE_DEFAULT_CONFIG(); + memcpy(console_config, &default_config, sizeof(default_config)); + return console_config; +} + +static void delete_config(void *config) +{ + if (!config) { + ESP_LOGE(TAG, "Cannot delete null configuration"); + return; + } + free(config); +} + +static esp_err_t set_config_service(void *config, const char *service_name, const char *service_key) +{ + return ESP_OK; +} + +static esp_err_t set_config_endpoint(void *config, const char *endpoint_name, uint16_t uuid) +{ + return ESP_OK; +} + +const network_prov_scheme_t network_prov_scheme_console = { + .prov_start = prov_start, + .prov_stop = protocomm_console_stop, + .new_config = new_config, + .delete_config = delete_config, + .set_config_service = set_config_service, + .set_config_endpoint = set_config_endpoint, +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + .wifi_mode = WIFI_MODE_STA +#endif +}; diff --git a/components/network_provisioning/src/scheme_softap.c b/components/network_provisioning/src/scheme_softap.c new file mode 100644 index 0000000..a4a9e32 --- /dev/null +++ b/components/network_provisioning/src/scheme_softap.c @@ -0,0 +1,188 @@ +/* + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "sdkconfig.h" + +#include +#include +#include +#include + +#include +#include + +#include "network_provisioning/scheme_softap.h" +#include "network_provisioning_priv.h" + +typedef struct softap_config { + protocomm_httpd_config_t httpd_config; + char ssid[33]; + char password[65]; +} network_prov_softap_config_t; + +static const char *TAG = "network_prov_scheme_softap"; + +extern const network_prov_scheme_t network_prov_scheme_softap; +static void *scheme_softap_prov_httpd_handle; +static esp_err_t start_wifi_ap(const char *ssid, const char *pass) +{ + /* Build Wi-Fi configuration for AP mode */ + wifi_config_t wifi_config = { + .ap = { + .max_connection = 5, + }, + }; + + /* SSID can be a non NULL terminated string if `ap.ssid_len` is specified + * Hence, memcpy is used to support 32 bytes long SSID per 802.11 standard */ + const size_t ssid_len = strnlen(ssid, sizeof(wifi_config.ap.ssid)); + memcpy(wifi_config.ap.ssid, ssid, ssid_len); + wifi_config.ap.ssid_len = ssid_len; + + if (strlen(pass) == 0) { + memset(wifi_config.ap.password, 0, sizeof(wifi_config.ap.password)); + wifi_config.ap.authmode = WIFI_AUTH_OPEN; + } else { + strlcpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password)); + wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK; + } + + /* Run Wi-Fi in AP + STA mode with configuration built above */ + esp_err_t err = esp_wifi_set_mode(WIFI_MODE_APSTA); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to set Wi-Fi mode : %d", err); + return err; + } + err = esp_wifi_set_config(WIFI_IF_AP, &wifi_config); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to set Wi-Fi config : %d", err); + return err; + } + + return ESP_OK; +} + +static esp_err_t prov_start(protocomm_t *pc, void *config) +{ + if (!pc) { + ESP_LOGE(TAG, "Protocomm handle cannot be null"); + return ESP_ERR_INVALID_ARG; + } + + if (!config) { + ESP_LOGE(TAG, "Cannot start with null configuration"); + return ESP_ERR_INVALID_ARG; + } + + network_prov_softap_config_t *softap_config = (network_prov_softap_config_t *) config; + + protocomm_httpd_config_t *httpd_config = &softap_config->httpd_config; + + if (scheme_softap_prov_httpd_handle) { + httpd_config->ext_handle_provided = true; + httpd_config->data.handle = scheme_softap_prov_httpd_handle; + } + + /* Start protocomm server on top of HTTP */ + esp_err_t err = protocomm_httpd_start(pc, httpd_config); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to start protocomm HTTP server"); + return err; + } + + /* Start Wi-Fi softAP with specified ssid and password */ + err = start_wifi_ap(softap_config->ssid, softap_config->password); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to start Wi-Fi AP"); + protocomm_httpd_stop(pc); + return err; + } + + return ESP_OK; +} + +static esp_err_t prov_stop(protocomm_t *pc) +{ + esp_err_t err = protocomm_httpd_stop(pc); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Error occurred while stopping protocomm_httpd"); + } + + return err; +} + +static void *new_config(void) +{ + network_prov_softap_config_t *softap_config = calloc(1, sizeof(network_prov_softap_config_t)); + if (!softap_config) { + ESP_LOGE(TAG, "Error allocating memory for new configuration"); + return NULL; + } + protocomm_httpd_config_t default_config = { + .data = { + .config = PROTOCOMM_HTTPD_DEFAULT_CONFIG() + } + }; + softap_config->httpd_config = default_config; + return softap_config; +} + +static void delete_config(void *config) +{ + if (!config) { + ESP_LOGE(TAG, "Cannot delete null configuration"); + return; + } + + network_prov_softap_config_t *softap_config = (network_prov_softap_config_t *) config; + free(softap_config); +} + +static esp_err_t set_config_service(void *config, const char *service_name, const char *service_key) +{ + if (!config) { + ESP_LOGE(TAG, "Cannot set null configuration"); + return ESP_ERR_INVALID_ARG; + } + + if (!service_name) { + ESP_LOGE(TAG, "Service name cannot be NULL"); + return ESP_ERR_INVALID_ARG; + } + + network_prov_softap_config_t *softap_config = (network_prov_softap_config_t *) config; + if (service_key) { + const int service_key_len = strlen(service_key); + if (service_key_len < 8 || service_key_len >= sizeof(softap_config->password)) { + ESP_LOGE(TAG, "Incorrect passphrase length for softAP: %d (Expected: Min - 8, Max - 64)", service_key_len); + return ESP_ERR_INVALID_ARG; + } + strlcpy(softap_config->password, service_key, sizeof(softap_config->password)); + } + strlcpy(softap_config->ssid, service_name, sizeof(softap_config->ssid)); + return ESP_OK; +} + +static esp_err_t set_config_endpoint(void *config, const char *endpoint_name, uint16_t uuid) +{ + return ESP_OK; +} + +void network_prov_scheme_softap_set_httpd_handle(void *handle) +{ + scheme_softap_prov_httpd_handle = handle; +} + +const network_prov_scheme_t network_prov_scheme_softap = { + .prov_start = prov_start, + .prov_stop = prov_stop, + .new_config = new_config, + .delete_config = delete_config, + .set_config_service = set_config_service, + .set_config_endpoint = set_config_endpoint, +#ifdef CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI + .wifi_mode = WIFI_MODE_APSTA +#endif +}; diff --git a/components/network_provisioning/tool/esp_prov/README.md b/components/network_provisioning/tool/esp_prov/README.md new file mode 100644 index 0000000..e354d34 --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/README.md @@ -0,0 +1,132 @@ +# ESP Provisioning Tool + +## Description + +`esp_prov` - A python-based utility for testing the provisioning examples over a host machine. + +Usage of `esp-prov` assumes that the provisioning app has specific protocomm endpoints active. These endpoints are active in the provisioning examples and accept specific protobuf data structure: + +| Endpoint Name | URI (HTTP server on ip:port) | Description | +|---------------|------------------------------|------------------------------------------------------------------------------------------| +| prov-session | http://ip:port/prov-session | Security endpoint used for session establishment | +| prov-config | http://ip:port/prov-config | Endpoint used for configuring Wi-Fi credentials or Thread dataset on device | +| proto-ver | http://ip:port/proto-ver | Version endpoint for checking protocol compatibility | +| prov-scan | http://ip:port/prov-scan | Endpoint used for scanning Wi-Fi APs or Thread network | +| prov-ctrl | http://ip:port/prov-ctrl | Endpoint used for controlling Wi-Fi / Thread provisioning state | +| custom-data | http://ip:port/custom-data | Optional endpoint for sending custom data (refer `wifi_prov` or `thread_prov` example) | + + +## Usage + +``` +python esp_prov.py --transport < mode of provisioning : softap \ ble \ console > [ Optional parameters... ] +``` +### Parameters + +* `--help` + Print the list of options along with brief descriptions + +* `--verbose`, `-v` + Sets the verbosity level of output log + +* `--transport ` + - Three options are available: + * `softap` - for SoftAP + HTTPD based provisioning + * Requires the device to be running in Wi-Fi SoftAP mode and hosting an HTTP server supporting specific endpoint URIs + * The client needs to be connected to the device softAP network before running the `esp_prov` tool. + * `ble` - for Bluetooth LE based provisioning + * Supports Linux, Windows and macOS; redirected to console if dependencies are not met + * Assumes that the provisioning endpoints are active on the device with specific Bluetooth LE service UUIDs + * `console` - for debugging via console-based provisioning + * The client->device commands are printed to STDOUT and device->client messages are accepted via STDIN. + * This is to be used when the device is accepting provisioning commands on UART console. + * `httpd` - the script works the same as for `softap`. This could be used on any other network interface than WiFi soft AP, e.g. Ethernet or USB. + +* `--service_name ` (Optional) + - When transport mode is `ble`, this specifies the Bluetooth LE device name to which connection is to be established for provisioned. If not provided, Bluetooth LE scanning is initiated and a list of nearby devices, as seen by the host, is displayed, of which the target device can be chosen. + - When transport mode is `softap` or `httpd`, this specifies the HTTP server hostname / IP which is running the provisioning service, on the SoftAP network (or any other interface for `httpd` mode) of the device which is to be provisioned. This defaults to `192.168.4.1:80` if not specified + +* `--ssid ` (Optional) + - For specifying the SSID of the Wi-Fi AP to which the device is to connect after provisioning. + - If not provided, scanning is initiated and scan results, as seen by the device, are displayed, of which an SSID can be picked and the corresponding password specified. + +* `--passphrase ` (Optional) + - For specifying the password of the Wi-Fi AP to which the device is to connect after provisioning. + - Only used when corresponding SSID is provided using the `--ssid` option + +* `--dataset_tlvs ` (Optional) + - For specifying the Dataset Tlvs of the Thread network to which the device is to connect after provisioning. + +* `--sec_ver ` + - For specifying the version of protocomm endpoint security to use. Following 3 versions are supported: + * `0` for `protocomm_security0` - No security + * `1` for `protocomm_security1` - X25519 key exchange + Authentication using Proof of Possession (PoP) + AES-CTR encryption + * `2` for `protocomm_security2` - Secure Remote Password protocol (SRP6a) + AES-GCM encryption + +* `--pop ` (Optional) + - For specifying optional Proof of Possession string to use for protocomm endpoint security version 1 + - Ignored when other security versions are used + +* `--sec2_username ` (Optional) + - For specifying optional username to use for protocomm endpoint security version 2 + - Ignored when other security versions are used + +* `--sec2_pwd ` (Optional) + - For specifying optional password to use for protocomm endpoint security version 2 + - Ignored when other security versions are used + +* `--sec2_gen_cred` (Optional) + - For generating the `SRP6a` credentials (salt and verifier) from the provided username and password for protocomm endpoint security version 2 + - Ignored when other security versions are used + +* `--sec2_salt_len ` (Optional) + - For specifying the optional `SRP6a` salt length to be used for generating protocomm endpoint security version 2 credentials + - Ignored when other security versions are used and the `--sec2_gen_cred` option is not set + +* `--reset` (Optional) + - Resets internal state machine of the device and clears provisioned credentials; to be used only in case of provisioning failures + +* `--reprov` (Optional) + - Resets internal state machine of the device and clears provisioned credentials; to be used only in case the device is to be provisioned again for new credentials after a previous successful provisioning + +* `--ble_adapter ` (Optional) + - For specifying the HCI adapter to use for Bluetooth LE transport (default: `hci0`). + - Useful when working with `vhci_bridge` for emulated devices, e.g. `--ble_adapter hci1`. + - Only relevant when transport mode is `ble` + +* `--custom_data ` (Optional) + An information string can be sent to the `custom-data` endpoint during provisioning using this argument. + (Assumes the provisioning app has an endpoint called `custom-data` - see [wifi_prov](../../examples/wifi_prov/) example for implementation details). + + +### Example Usage + +Please refer to the `README.md` file with the `wifi_prov` or `thread_prov` example present under `{path-to-network_provisioning-component}/examples`. + +This example uses specific options of the `esp_prov` tool and gives an overview of simple as well as advanced usage scenarios. + +## Dependencies + +This requires the following python libraries to run: +* `bleak` +* `protobuf` +* `cryptography` + +To install the dependency packages needed, please run the following command in `$IDF_PATH` directory: + +For ESP-IDF v5.1: +```shell +bash install.sh --enable-ttfw +``` + +For ESP-IDF v5.2 to v5.5: +```shell +bash install.sh --enable-pytest +``` + +For ESP-IDF v6.0 or later: +```shell +bash install.sh --enable-ci +``` + +**Note:** For troubleshooting errors with Bluetooth LE transport, please refer this [link](https://bleak.readthedocs.io/en/latest/troubleshooting.html). diff --git a/components/network_provisioning/tool/esp_prov/__init__.py b/components/network_provisioning/tool/esp_prov/__init__.py new file mode 100644 index 0000000..b1423ba --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/__init__.py @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +from .esp_prov import * # noqa: export esp_prov module to users diff --git a/components/network_provisioning/tool/esp_prov/esp_prov.py b/components/network_provisioning/tool/esp_prov/esp_prov.py new file mode 100644 index 0000000..75f5bc1 --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/esp_prov.py @@ -0,0 +1,763 @@ +#!/usr/bin/env python +# +# SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +import argparse +import asyncio +import json +import os +import sys +import textwrap +import time +from getpass import getpass + +from utils import int_to_hex_str + +try: + import prov + import security + import transport + +except ImportError: + idf_path = os.environ['IDF_PATH'] + sys.path.insert(0, idf_path + '/components/protocomm/python') + sys.path.insert(1, idf_path + '/tools/esp_prov') + + import prov + import security + import transport + +# Set this to true to allow exceptions to be thrown +config_throw_except = False + + +def on_except(err): + if config_throw_except: + raise RuntimeError(err) + else: + print(err) + + +def get_security(secver, sec_patch_ver, username, password, pop='', verbose=False): + if secver == 2: + return security.Security2(sec_patch_ver, username, password, verbose) + elif secver == 1: + return security.Security1(pop, verbose) + elif secver == 0: + return security.Security0(verbose) + return None + + +def get_thread_dataset_tlvs(network_info, network_key): + # Get the simple dataset tlvs from the PAN ID, Channel, Extended PAN ID, and Network Key + pan_id = network_info['pan_id'] + channel = network_info['channel'] + ext_pan_id = network_info['ext_pan_id'] + dataset_tlvs = '00030000' + int_to_hex_str(channel) + '0208' + ext_pan_id + '0510' + network_key + '0102' + int_to_hex_str(pan_id) + return dataset_tlvs + + +async def get_transport(sel_transport, service_name, ble_adapter=transport.DEFAULT_BLE_ADAPTER): + try: + tp = None + if (sel_transport in ['softap', 'httpd']): + if service_name is None: + service_name = '192.168.4.1:80' + tp = transport.Transport_HTTP(service_name) + elif (sel_transport == 'ble'): + # BLE client is now capable of automatically figuring out + # the primary service from the advertisement data and the + # characteristics corresponding to each endpoint. + # Below, the service_uuid field and 16bit UUIDs in the nu_lookup + # table are provided only to support devices running older firmware, + # in which case, the automated discovery will fail and the client + # will fallback to using the provided UUIDs instead + nu_lookup = {'prov-session': 'ff51', 'prov-config': 'ff52', 'proto-ver': 'ff53'} + tp = transport.Transport_BLE(service_uuid='021a9004-0382-4aea-bff4-6b3f1c5adfb4', + nu_lookup=nu_lookup, ble_adapter=ble_adapter) + await tp.connect(devname=service_name) + elif (sel_transport == 'console'): + tp = transport.Transport_Console() + return tp + except RuntimeError as e: + on_except(e) + return None + + +async def version_match(tp, protover, verbose=False): + try: + response = await tp.send_data('proto-ver', protover) + + if verbose: + print('proto-ver response : ', response) + + # First assume this to be a simple version string + if response.lower() == protover.lower(): + return True + + try: + # Else interpret this as JSON structure containing + # information with versions and capabilities of both + # provisioning service and application + info = json.loads(response) + if info['prov']['ver'].lower() == protover.lower(): + return True + + except ValueError: + # If decoding as JSON fails, it means that capabilities + # are not supported + return False + + except Exception as e: + on_except(e) + return None + + +async def has_capability(tp, capability='none', verbose=False): + # Note : default value of `capability` argument cannot be empty string + # because protocomm_httpd expects non zero content lengths + try: + response = await tp.send_data('proto-ver', capability) + + if verbose: + print('proto-ver response : ', response) + + try: + # Interpret this as JSON structure containing + # information with versions and capabilities of both + # provisioning service and application + info = json.loads(response) + supported_capabilities = info['prov']['cap'] + if capability.lower() == 'none': + # No specific capability to check, but capabilities + # feature is present so return True + return True + elif capability in supported_capabilities: + return True + return False + + except ValueError: + # If decoding as JSON fails, it means that capabilities + # are not supported + return False + + except RuntimeError as e: + on_except(e) + + return False + + +async def get_version(tp): + response = None + try: + response = await tp.send_data('proto-ver', '---') + except RuntimeError as e: + on_except(e) + response = '' + return response + + +async def get_sec_patch_ver(tp, verbose=False): + response = await get_version(tp) + + if verbose: + print('proto-ver response : ', response) + + try: + # Interpret this as JSON structure containing + # information with security version information + info = json.loads(response) + try: + sec_patch_ver = info['prov']['sec_patch_ver'] + except KeyError: + sec_patch_ver = 0 + return sec_patch_ver + + except ValueError: + # If decoding as JSON fails, we assume default patch level + return 0 + + +async def establish_session(tp, sec): + try: + response = None + while True: + request = sec.security_session(response) + if request is None: + break + response = await tp.send_data('prov-session', request) + if (response is None): + return False + return True + except RuntimeError as e: + on_except(e) + return None + + +async def custom_config(tp, sec, custom_info, custom_ver): + try: + message = prov.custom_config_request(sec, custom_info, custom_ver) + response = await tp.send_data('custom-config', message) + return (prov.custom_config_response(sec, response) == 0) + except RuntimeError as e: + on_except(e) + return None + + +async def custom_data(tp, sec, custom_data): + try: + message = prov.custom_data_request(sec, custom_data) + response = await tp.send_data('custom-data', message) + return (prov.custom_data_response(sec, response) == 0) + except RuntimeError as e: + on_except(e) + return None + + +async def scan_wifi_APs(sel_transport, tp, sec): + APs = [] + group_channels = 0 + readlen = 100 + if sel_transport in ['softap', 'httpd']: + # In case of softAP/httpd we must perform the scan on individual channels, one by one, + # so that the Wi-Fi controller gets ample time to send out beacons (necessary to + # maintain connectivity with authenticated stations. As scanning one channel at a + # time will be slow, we can group more than one channels to be scanned in quick + # succession, hence speeding up the scan process. Though if too many channels are + # present in a group, the controller may again miss out on sending beacons. Hence, + # the application must should use an optimum value. The following value usually + # works out in most cases + group_channels = 5 + elif sel_transport == 'ble': + # Read at most 4 entries at a time. This is because if we are using BLE transport + # then the response packet size should not exceed the present limit of 256 bytes of + # characteristic value imposed by protocomm_ble. This limit may be removed in the + # future + readlen = 4 + try: + message = prov.scan_start_request('wifi', sec, blocking=True, group_channels=group_channels) + start_time = time.time() + response = await tp.send_data('prov-scan', message) + stop_time = time.time() + print('++++ Scan process executed in ' + str(stop_time - start_time) + ' sec') + prov.scan_start_response(sec, response) + + message = prov.scan_status_request('wifi', sec) + response = await tp.send_data('prov-scan', message) + result = prov.scan_status_response(sec, response) + print('++++ Scan results : ' + str(result['count'])) + if result['count'] != 0: + index = 0 + remaining = result['count'] + while remaining: + count = [remaining, readlen][remaining > readlen] + message = prov.scan_result_request('wifi', sec, index, count) + response = await tp.send_data('prov-scan', message) + APs += prov.scan_result_response(sec, response) + remaining -= count + index += count + + except RuntimeError as e: + on_except(e) + return None + + return APs + + +async def send_wifi_config(tp, sec, ssid, passphrase): + try: + message = prov.config_set_config_request('wifi', sec, ssid, passphrase) + response = await tp.send_data('prov-config', message) + return (prov.config_set_config_response(sec, response) == 0) + except RuntimeError as e: + on_except(e) + return None + + +async def apply_wifi_config(tp, sec): + try: + message = prov.config_apply_config_request('wifi', sec) + response = await tp.send_data('prov-config', message) + return (prov.config_apply_config_response(sec, response) == 0) + except RuntimeError as e: + on_except(e) + return None + + +async def get_wifi_config(tp, sec): + try: + message = prov.config_get_status_request('wifi', sec) + response = await tp.send_data('prov-config', message) + return prov.config_get_status_response(sec, response) + except RuntimeError as e: + on_except(e) + return None + + +async def wait_wifi_connected(tp, sec): + """ + Wait for provisioning to report Wi-Fi is connected + + Returns True if Wi-Fi connection succeeded, False if connection consistently failed + """ + TIME_PER_POLL = 5 + retry = 3 + + while True: + time.sleep(TIME_PER_POLL) + print('\n==== Wi-Fi connection state ====') + ret = await get_wifi_config(tp, sec) + if ret == 'connecting': + continue + elif ret == 'connected': + print('==== Provisioning was successful ====') + return True + elif retry > 0: + retry -= 1 + print('Waiting to poll status again (status %s, %d tries left)...' % (ret, retry)) + else: + print('---- Provisioning failed! ----') + return False + + +async def reset_wifi(tp, sec): + try: + message = prov.ctrl_reset_request('wifi', sec) + response = await tp.send_data('prov-ctrl', message) + prov.ctrl_reset_response(sec, response) + + except RuntimeError as e: + on_except(e) + return None + + +async def reprov_wifi(tp, sec): + try: + message = prov.ctrl_reprov_request('wifi', sec) + response = await tp.send_data('prov-ctrl', message) + prov.ctrl_reprov_response(sec, response) + + except RuntimeError as e: + on_except(e) + return None + + +async def scan_thread_networks(sel_transport, tp, sec): + Networks = [] + # Read at most 4 entries at a time. This is because if we are using BLE transport + # then the response packet size should not exceed the present limit of 256 bytes of + # characteristic value imposed by protocomm_ble. This limit may be removed in the + # future + readlen = 4 + try: + message = prov.scan_start_request('thread', sec, blocking=True, group_channels=0) + start_time = time.time() + response = await tp.send_data('prov-scan', message) + stop_time = time.time() + print('++++ Scan process executed in ' + str(stop_time - start_time) + ' sec') + prov.scan_start_response(sec, response) + + message = prov.scan_status_request('thread', sec) + response = await tp.send_data('prov-scan', message) + result = prov.scan_status_response(sec, response) + print('++++ Scan results : ' + str(result['count'])) + if result['count'] != 0: + index = 0 + remaining = result['count'] + while remaining: + count = [remaining, readlen][remaining > readlen] + message = prov.scan_result_request('thread', sec, index, count) + response = await tp.send_data('prov-scan', message) + Networks += prov.scan_result_response(sec, response) + remaining -= count + index += count + + except RuntimeError as e: + on_except(e) + return None + + return Networks + + +async def send_thread_config(tp, sec, dataset_tlvs): + try: + message = prov.config_set_config_request('thread', sec, dataset_tlvs) + response = await tp.send_data('prov-config', message) + return (prov.config_set_config_response(sec, response) == 0) + except RuntimeError as e: + on_except(e) + return None + + +async def apply_thread_config(tp, sec): + try: + message = prov.config_apply_config_request('thread', sec) + response = await tp.send_data('prov-config', message) + return (prov.config_apply_config_response(sec, response) == 0) + except RuntimeError as e: + on_except(e) + return None + + +async def get_thread_config(tp, sec): + try: + message = prov.config_get_status_request('thread', sec) + response = await tp.send_data('prov-config', message) + return prov.config_get_status_response(sec, response) + except RuntimeError as e: + on_except(e) + return None + + +async def wait_thread_connected(tp, sec): + """ + Wait for provisioning to report Thread is attached + + Returns True if Thread connection succeeded, False if connection consistently failed + """ + TIME_PER_POLL = 5 + retry = 3 + + while True: + time.sleep(TIME_PER_POLL) + print('\n==== Thread connection state ====') + ret = await get_thread_config(tp, sec) + if ret == 'attaching': + continue + elif ret == 'attached': + print('==== Provisioning was successful ====') + return True + elif retry > 0: + retry -= 1 + print('Waiting to poll status again (status %s, %d tries left)...' % (ret, retry)) + else: + print('---- Provisioning failed! ----') + return False + + +async def reset_thread(tp, sec): + try: + message = prov.ctrl_reset_request('thread', sec) + response = await tp.send_data('prov-ctrl', message) + prov.ctrl_reset_response(sec, response) + + except RuntimeError as e: + on_except(e) + return None + + +async def reprov_thread(tp, sec): + try: + message = prov.ctrl_reprov_request('thread', sec) + response = await tp.send_data('prov-ctrl', message) + prov.ctrl_reprov_response(sec, response) + + except RuntimeError as e: + on_except(e) + return None + + +def desc_format(*args): + desc = '' + for arg in args: + desc += textwrap.fill(replace_whitespace=False, text=arg) + '\n' + return desc + + +async def main(): + parser = argparse.ArgumentParser(description=desc_format( + 'ESP Provisioning tool for configuring devices ' + 'running protocomm based provisioning service.', + 'See esp-idf/examples/provisioning for sample applications'), + formatter_class=argparse.RawTextHelpFormatter) + + parser.add_argument('--transport', required=True, dest='mode', type=str, + help=desc_format( + 'Mode of transport over which provisioning is to be performed.', + 'This should be one of "softap", "ble", "console" (or "httpd" which is an alias of softap)')) + + parser.add_argument('--service_name', dest='name', type=str, + help=desc_format( + 'This specifies the name of the provisioning service to connect to, ' + 'depending upon the mode of transport :', + '\t- transport "ble" : The BLE Device Name', + '\t- transport "softap/httpd" : HTTP Server hostname or IP', + '\t (default "192.168.4.1:80")')) + + parser.add_argument('--proto_ver', dest='version', type=str, default='', + help=desc_format( + 'This checks the protocol version of the provisioning service running ' + 'on the device before initiating Wi-Fi configuration')) + + parser.add_argument('--sec_ver', dest='secver', type=int, default=None, + help=desc_format( + 'Protocomm security scheme used by the provisioning service for secure ' + 'session establishment. Accepted values are :', + '\t- 0 : No security', + '\t- 1 : X25519 key exchange + AES-CTR encryption', + '\t + Authentication using Proof of Possession (PoP)', + '\t- 2 : SRP6a + AES-GCM encryption', + 'In case device side application uses IDF\'s provisioning manager, ' + 'the compatible security version is automatically determined from ' + 'capabilities retrieved via the version endpoint')) + + parser.add_argument('--pop', dest='sec1_pop', type=str, default='', + help=desc_format( + 'This specifies the Proof of possession (PoP) when security scheme 1 ' + 'is used')) + + parser.add_argument('--sec2_username', dest='sec2_usr', type=str, default='', + help=desc_format( + 'Username for security scheme 2 (SRP6a)')) + + parser.add_argument('--sec2_pwd', dest='sec2_pwd', type=str, default='', + help=desc_format( + 'Password for security scheme 2 (SRP6a)')) + + parser.add_argument('--sec2_gen_cred', help='Generate salt and verifier for security scheme 2 (SRP6a)', action='store_true') + + parser.add_argument('--sec2_salt_len', dest='sec2_salt_len', type=int, default=16, + help=desc_format( + 'Salt length for security scheme 2 (SRP6a)')) + + parser.add_argument('--ssid', dest='ssid', type=str, default='', + help=desc_format( + 'This configures the device to use SSID of the Wi-Fi network to which ' + 'we would like it to connect to permanently, once provisioning is complete. ' + 'If Wi-Fi scanning is supported by the provisioning service, this need not ' + 'be specified')) + + parser.add_argument('--passphrase', dest='passphrase', type=str, + help=desc_format( + 'This configures the device to use Passphrase for the Wi-Fi network to which ' + 'we would like it to connect to permanently, once provisioning is complete. ' + 'If Wi-Fi scanning is supported by the provisioning service, this need not ' + 'be specified')) + + parser.add_argument('--dataset_tlvs', dest='dataset_tlvs', type=str, + help=desc_format( + 'This configures the device to use Dataset Tlvs of the Thread network to ' + 'which we would like it to attach to permanently, once provisioning is ' + 'complete. If Thread scanning is supported by the provisioning service, this ' + 'need not be specified')) + + parser.add_argument('--custom_data', dest='custom_data', type=str, default='', + help=desc_format( + 'This is an optional parameter, only intended for use with ' + '"examples/wifi_prov"')) + + parser.add_argument('--reset', help='Reset WiFi', action='store_true') + + parser.add_argument('--reprov', help='Reprovision WiFi', action='store_true') + + parser.add_argument('-v','--verbose', help='Increase output verbosity', action='store_true') + + parser.add_argument('--ble_adapter', dest='ble_adapter', type=str, default=transport.DEFAULT_BLE_ADAPTER, + help=desc_format( + f'HCI adapter to use for BLE (default: {transport.DEFAULT_BLE_ADAPTER}).', + 'Use with vhci_bridge for emulated devices, e.g. --ble_adapter hci1')) + + args = parser.parse_args() + + if args.secver == 2 and args.sec2_gen_cred: + if not args.sec2_usr or not args.sec2_pwd: + raise ValueError('Username/password cannot be empty for security scheme 2 (SRP6a)') + + print('==== Salt-verifier for security scheme 2 (SRP6a) ====') + security.sec2_gen_salt_verifier(args.sec2_usr, args.sec2_pwd, args.sec2_salt_len) + sys.exit() + + obj_transport = await get_transport(args.mode.lower(), args.name, args.ble_adapter) + if obj_transport is None: + raise RuntimeError('Failed to establish connection') + + try: + sec_patch_ver = 0 + # If security version not specified check in capabilities + if args.secver is None: + # First check if capabilities are supported or not + if not await has_capability(obj_transport): + print('Security capabilities could not be determined, please specify "--sec_ver" explicitly') + raise ValueError('Invalid Security Version') + + # When no_sec is present, use security 0, else security 1 + args.secver = int(not await has_capability(obj_transport, 'no_sec')) + print(f'==== Security Scheme: {args.secver} ====') + + if (args.secver == 1): + if not await has_capability(obj_transport, 'no_pop'): + if len(args.sec1_pop) == 0: + prompt_str = 'Proof of Possession required: ' + args.sec1_pop = getpass(prompt_str) + elif len(args.sec1_pop) != 0: + print('Proof of Possession will be ignored') + args.sec1_pop = '' + + if (args.secver == 2): + sec_patch_ver = await get_sec_patch_ver(obj_transport, args.verbose) + if len(args.sec2_usr) == 0: + args.sec2_usr = input('Security Scheme 2 - SRP6a Username required: ') + if len(args.sec2_pwd) == 0: + prompt_str = 'Security Scheme 2 - SRP6a Password required: ' + args.sec2_pwd = getpass(prompt_str) + + obj_security = get_security(args.secver, sec_patch_ver, args.sec2_usr, args.sec2_pwd, args.sec1_pop, args.verbose) + if obj_security is None: + raise ValueError('Invalid Security Version') + + if args.version != '': + print('\n==== Verifying protocol version ====') + if not await version_match(obj_transport, args.version, args.verbose): + raise RuntimeError('Error in protocol version matching') + print('==== Verified protocol version successfully ====') + + print('\n==== Starting Session ====') + if not await establish_session(obj_transport, obj_security): + print('Failed to establish session. Ensure that security scheme and proof of possession are correct') + raise RuntimeError('Error in establishing session') + print('==== Session Established ====') + + if await has_capability(obj_transport, 'thread_prov'): + if args.reset: + print('==== Resetting Thread====') + await reset_thread(obj_transport, obj_security) + sys.exit() + + if args.reprov: + print('==== Reprovisioning Thread====') + await reprov_thread(obj_transport, obj_security) + sys.exit() + + if args.dataset_tlvs is None: + if not await has_capability(obj_transport, 'thread_scan'): + prompt_str = 'Enter Thread dataset tlvs string : ' + args.dataset_tlvs = input(prompt_str) + else: + while True: + print('\n==== Scanning Thread Networks ====') + start_time = time.time() + Networks = await scan_thread_networks(args.mode.lower(), obj_transport, obj_security) + end_time = time.time() + print('\n++++ Scan finished in ' + str(end_time - start_time) + ' sec') + if Networks is None: + raise RuntimeError('Error in scanning Thread Networks') + + if len(Networks) == 0: + print('No Thread Networks found!') + sys.exit() + + print('==== Thread Scan results ====') + print('{0: >4} {1: <8} {2: <18} {3: <18} {4: <18} {5: <4} {6: <4} {7: <16}'.format( + 'S.N.', 'PAN ID', 'EXT PAN ID', 'NAME', 'EXT ADDR', 'CHN', 'RSSI', 'LQI')) + for i in range(len(Networks)): + print('[{0: >2}] {1: <8} {2: <18} {3: <18} {4: <18} {5: <4} {6: <4} {7: <16}'.format( + i + 1, Networks[i]['pan_id'], Networks[i]['ext_pan_id'], Networks[i]['network_name'], + Networks[i]['ext_addr'], Networks[i]['channel'], Networks[i]['rssi'], Networks[i]['lqi'])) + + while True: + try: + select = int(input('Select Network by number (0 to rescan) : ')) + if select < 0 or select > len(Networks): + raise ValueError + break + except ValueError: + print('Invalid input! Retry') + + if select != 0: + network_key = getpass('Enter Thread network key string : ') + args.dataset_tlvs = get_thread_dataset_tlvs(Networks[select], network_key) + break + + print('\n==== Sending Thread Dataset to Target ====') + if not await send_thread_config(obj_transport, obj_security, args.dataset_tlvs): + raise RuntimeError('Error in send Thread config') + print('==== Thread Dataset sent successfully ====') + + print('\n==== Applying Thread Config to Target ====') + if not await apply_thread_config(obj_transport, obj_security): + raise RuntimeError('Error in apply Thread config') + print('==== Apply config sent successfully ====') + + await wait_thread_connected(obj_transport, obj_security) + else: + if not await has_capability(obj_transport, 'wifi_prov'): + print('Use wifi_provisioning for device without "wifi_prov" capabilities') + print('The device might be using previous wifi_provisioning in ESP-IDF') + + if args.reset: + print('==== Resetting WiFi====') + await reset_wifi(obj_transport, obj_security) + sys.exit() + + if args.reprov: + print('==== Reprovisioning WiFi====') + await reprov_wifi(obj_transport, obj_security) + sys.exit() + + if args.custom_data != '': + print('\n==== Sending Custom data to Target ====') + if not await custom_data(obj_transport, obj_security, args.custom_data): + raise RuntimeError('Error in custom data') + print('==== Custom data sent successfully ====') + + if args.ssid == '': + if not await has_capability(obj_transport, 'wifi_scan'): + raise RuntimeError('Wi-Fi Scan List is not supported by provisioning service') + + while True: + print('\n==== Scanning Wi-Fi APs ====') + start_time = time.time() + APs = await scan_wifi_APs(args.mode.lower(), obj_transport, obj_security) + end_time = time.time() + print('\n++++ Scan finished in ' + str(end_time - start_time) + ' sec') + if APs is None: + raise RuntimeError('Error in scanning Wi-Fi APs') + + if len(APs) == 0: + print('No APs found!') + sys.exit() + + print('==== Wi-Fi Scan results ====') + print('{0: >4} {1: <33} {2: <12} {3: >4} {4: <4} {5: <16}'.format( + 'S.N.', 'SSID', 'BSSID', 'CHN', 'RSSI', 'AUTH')) + for i in range(len(APs)): + print('[{0: >2}] {1: <33} {2: <12} {3: >4} {4: <4} {5: <16}'.format( + i + 1, APs[i]['ssid'], APs[i]['bssid'], APs[i]['channel'], APs[i]['rssi'], APs[i]['auth'])) + + while True: + try: + select = int(input('Select AP by number (0 to rescan) : ')) + if select < 0 or select > len(APs): + raise ValueError + break + except ValueError: + print('Invalid input! Retry') + + if select != 0: + break + + args.ssid = APs[select - 1]['ssid'] + + if args.passphrase is None: + prompt_str = 'Enter passphrase for {0} : '.format(args.ssid) + args.passphrase = getpass(prompt_str) + + print('\n==== Sending Wi-Fi Credentials to Target ====') + if not await send_wifi_config(obj_transport, obj_security, args.ssid, args.passphrase): + raise RuntimeError('Error in send Wi-Fi config') + print('==== Wi-Fi Credentials sent successfully ====') + + print('\n==== Applying Wi-Fi Config to Target ====') + if not await apply_wifi_config(obj_transport, obj_security): + raise RuntimeError('Error in apply Wi-Fi config') + print('==== Apply config sent successfully ====') + + await wait_wifi_connected(obj_transport, obj_security) + + finally: + await obj_transport.disconnect() + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/components/network_provisioning/tool/esp_prov/proto/__init__.py b/components/network_provisioning/tool/esp_prov/proto/__init__.py new file mode 100644 index 0000000..29a3d9e --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/proto/__init__.py @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +import importlib.util +import os +import sys +from importlib.abc import Loader +from typing import Any + + +def _load_source(name: str, path: str) -> Any: + spec = importlib.util.spec_from_file_location(name, path) + if not spec: + return None + + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + assert isinstance(spec.loader, Loader) + spec.loader.exec_module(module) + return module + + +idf_path = os.environ['IDF_PATH'] +esp_prov_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# protocomm component related python files generated from .proto files +constants_pb2 = _load_source('constants_pb2', idf_path + '/components/protocomm/python/constants_pb2.py') +sec0_pb2 = _load_source('sec0_pb2', idf_path + '/components/protocomm/python/sec0_pb2.py') +sec1_pb2 = _load_source('sec1_pb2', idf_path + '/components/protocomm/python/sec1_pb2.py') +sec2_pb2 = _load_source('sec2_pb2', idf_path + '/components/protocomm/python/sec2_pb2.py') +session_pb2 = _load_source('session_pb2', idf_path + '/components/protocomm/python/session_pb2.py') + +# network_provisioning component related python files generated from .proto files +network_constants_pb2 = _load_source('network_constants_pb2', esp_prov_path + '/../../python/network_constants_pb2.py') +network_config_pb2 = _load_source('network_config_pb2', esp_prov_path + '/../../python/network_config_pb2.py') +network_scan_pb2 = _load_source('network_scan_pb2', esp_prov_path + '/../../python/network_scan_pb2.py') +network_ctrl_pb2 = _load_source('network_ctrl_pb2', esp_prov_path + '/../../python/network_ctrl_pb2.py') diff --git a/components/network_provisioning/tool/esp_prov/prov/__init__.py b/components/network_provisioning/tool/esp_prov/prov/__init__.py new file mode 100644 index 0000000..8305b0c --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/prov/__init__.py @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +from .custom_prov import * # noqa F403 +from .network_ctrl import * # noqa F403 +from .network_prov import * # noqa F403 +from .network_scan import * # noqa F403 diff --git a/components/network_provisioning/tool/esp_prov/prov/custom_prov.py b/components/network_provisioning/tool/esp_prov/prov/custom_prov.py new file mode 100644 index 0000000..7b798dc --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/prov/custom_prov.py @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +# APIs for interpreting and creating protobuf packets for `custom-config` protocomm endpoint + +from utils import str_to_bytes + + +def print_verbose(security_ctx, data): + if (security_ctx.verbose): + print(f'\x1b[32;20m++++ {data} ++++\x1b[0m') + + +def custom_data_request(security_ctx, data): + # Encrypt the custom data + enc_cmd = security_ctx.encrypt_data(str_to_bytes(data)) + print_verbose(security_ctx, f'Client -> Device (CustomData cmd): 0x{enc_cmd.hex()}') + return enc_cmd.decode('latin-1') + + +def custom_data_response(security_ctx, response_data): + # Decrypt response packet + decrypt = security_ctx.decrypt_data(str_to_bytes(response_data)) + print(f'++++ CustomData response: {str(decrypt)}++++') + return 0 diff --git a/components/network_provisioning/tool/esp_prov/prov/network_ctrl.py b/components/network_provisioning/tool/esp_prov/prov/network_ctrl.py new file mode 100644 index 0000000..1f785c2 --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/prov/network_ctrl.py @@ -0,0 +1,61 @@ +# SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +# APIs for interpreting and creating protobuf packets for Wi-Fi State Controlling + +import proto +from utils import str_to_bytes + + +def print_verbose(security_ctx, data): + if (security_ctx.verbose): + print(f'\x1b[32;20m++++ {data} ++++\x1b[0m') + + +def ctrl_reset_request(network_type, security_ctx): + # Form protobuf request packet for CtrlReset command + cmd = proto.network_ctrl_pb2.NetworkCtrlPayload() + if network_type == 'wifi': + cmd.msg = proto.network_ctrl_pb2.TypeCmdCtrlWifiReset + elif network_type == 'thread': + cmd.msg = proto.network_ctrl_pb2.TypeCmdCtrlThreadReset + else: + raise RuntimeError + enc_cmd = security_ctx.encrypt_data(cmd.SerializeToString()) + print_verbose(security_ctx, f'Client -> Device (Encrypted CmdCtrlReset): 0x{enc_cmd.hex()}') + return enc_cmd.decode('latin-1') + + +def ctrl_reset_response(security_ctx, response_data): + # Interpret protobuf response packet from CtrlReset command + dec_resp = security_ctx.decrypt_data(str_to_bytes(response_data)) + resp = proto.network_ctrl_pb2.NetworkCtrlPayload() + resp.ParseFromString(dec_resp) + print_verbose(security_ctx, f'CtrlReset status: 0x{str(resp.status)}') + if resp.status != 0: + raise RuntimeError + + +def ctrl_reprov_request(network_type, security_ctx): + # Form protobuf request packet for CtrlReprov command + cmd = proto.network_ctrl_pb2.NetworkCtrlPayload() + if network_type == 'wifi': + cmd.msg = proto.network_ctrl_pb2.TypeCmdCtrlWifiReprov + elif network_type == 'thread': + cmd.msg = proto.network_ctrl_pb2.TypeCmdCtrlThreadReprov + else: + raise RuntimeError + enc_cmd = security_ctx.encrypt_data(cmd.SerializeToString()) + print_verbose(security_ctx, f'Client -> Device (Encrypted CmdCtrlReset): 0x{enc_cmd.hex()}') + return enc_cmd.decode('latin-1') + + +def ctrl_reprov_response(security_ctx, response_data): + # Interpret protobuf response packet from CtrlReprov command + dec_resp = security_ctx.decrypt_data(str_to_bytes(response_data)) + resp = proto.network_ctrl_pb2.NetworkCtrlPayload() + resp.ParseFromString(dec_resp) + print_verbose(security_ctx, f'CtrlReset status: 0x{str(resp.status)}') + if resp.status != 0: + raise RuntimeError diff --git a/components/network_provisioning/tool/esp_prov/prov/network_prov.py b/components/network_provisioning/tool/esp_prov/prov/network_prov.py new file mode 100644 index 0000000..9d1b1d9 --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/prov/network_prov.py @@ -0,0 +1,140 @@ +# SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +# APIs for interpreting and creating protobuf packets for Wi-Fi provisioning + +import proto +from utils import hex_str_to_bytes, str_to_bytes + + +def print_verbose(security_ctx, data): + if (security_ctx.verbose): + print(f'++++ {data} ++++') + + +def config_get_status_request(network_type, security_ctx): + # Form protobuf request packet for GetStatus command + cfg1 = proto.network_config_pb2.NetworkConfigPayload() + if network_type == 'wifi': + cfg1.msg = proto.network_config_pb2.TypeCmdGetWifiStatus + cmd_get_wifi_status = proto.network_config_pb2.CmdGetWifiStatus() + cfg1.cmd_get_wifi_status.MergeFrom(cmd_get_wifi_status) + elif network_type == 'thread': + cfg1.msg = proto.network_config_pb2.TypeCmdGetThreadStatus + cmd_get_thread_status = proto.network_config_pb2.CmdGetThreadStatus() + cfg1.cmd_get_thread_status.MergeFrom(cmd_get_thread_status) + else: + raise RuntimeError + encrypted_cfg = security_ctx.encrypt_data(cfg1.SerializeToString()) + print_verbose(security_ctx, f'Client -> Device (Encrypted CmdGetStatus): 0x{encrypted_cfg.hex()}') + return encrypted_cfg.decode('latin-1') + + +def config_get_status_response(security_ctx, response_data): + # Interpret protobuf response packet from GetStatus command + decrypted_message = security_ctx.decrypt_data(str_to_bytes(response_data)) + cmd_resp1 = proto.network_config_pb2.NetworkConfigPayload() + cmd_resp1.ParseFromString(decrypted_message) + print_verbose(security_ctx, f'CmdGetStatus type: {str(cmd_resp1.msg)}') + + if cmd_resp1.msg == proto.network_config_pb2.TypeRespGetWifiStatus: + print_verbose(security_ctx, f'CmdGetStatus status: {str(cmd_resp1.resp_get_wifi_status.status)}') + if cmd_resp1.resp_get_wifi_status.wifi_sta_state == 0: + print('==== WiFi state: Connected ====') + return 'connected' + elif cmd_resp1.resp_get_wifi_status.wifi_sta_state == 1: + print('++++ WiFi state: Connecting... ++++') + if cmd_resp1.resp_get_wifi_status.HasField('attempt_failed'): + if cmd_resp1.resp_get_wifi_status.attempt_failed.attempts_remaining: + print(cmd_resp1.resp_get_wifi_status) + else: + print('attempt_failed {\n attempts_remaining: 0\n}') + return 'connecting' + elif cmd_resp1.resp_get_wifi_status.wifi_sta_state == 2: + print('---- WiFi state: Disconnected ----') + return 'disconnected' + elif cmd_resp1.resp_get_wifi_status.wifi_sta_state == 3: + print('---- WiFi state: Connection Failed ----') + if cmd_resp1.resp_get_wifi_status.wifi_fail_reason == 0: + print('---- Failure reason: Incorrect Password ----') + elif cmd_resp1.resp_get_wifi_status.wifi_fail_reason == 1: + print('---- Failure reason: Incorrect SSID ----') + return 'failed' + elif cmd_resp1.msg == proto.network_config_pb2.TypeRespGetThreadStatus: + print_verbose(security_ctx, f'CmdGetStatus status: {str(cmd_resp1.resp_get_thread_status.status)}') + if cmd_resp1.resp_get_thread_status.thread_state == 0: + print('==== Thread state: Attached ====') + return 'attached' + elif cmd_resp1.resp_get_thread_status.thread_state == 1: + print('==== Thread state: Attaching ====') + return 'attaching' + elif cmd_resp1.resp_get_thread_status.thread_state == 2: + print('==== Thread state: Detached ====') + return 'detached' + elif cmd_resp1.resp_get_thread_status.thread_state == 3: + print('==== Thread state: Attaching Failed ====') + if cmd_resp1.resp_get_thread_status.thread_fail_reason == 0: + print('---- Failure reason: Invalid Dataset ----') + elif cmd_resp1.resp_get_thread_status.thread_fail_reason == 1: + print('---- Failure reason: Network Not Found ----') + return 'failed' + + return 'unknown' + + +def config_set_config_request(network_type, security_ctx, ssid_or_dataset_tlvs, passphrase=''): + # Form protobuf request packet for SetConfig command + cmd = proto.network_config_pb2.NetworkConfigPayload() + if network_type == 'wifi': + cmd.msg = proto.network_config_pb2.TypeCmdSetWifiConfig + cmd.cmd_set_wifi_config.ssid = str_to_bytes(ssid_or_dataset_tlvs) + cmd.cmd_set_wifi_config.passphrase = str_to_bytes(passphrase) + elif network_type == 'thread': + cmd.msg = proto.network_config_pb2.TypeCmdSetThreadConfig + cmd.cmd_set_thread_config.dataset = hex_str_to_bytes(ssid_or_dataset_tlvs) + else: + raise RuntimeError + enc_cmd = security_ctx.encrypt_data(cmd.SerializeToString()) + print_verbose(security_ctx, f'Client -> Device (SetConfig cmd): 0x{enc_cmd.hex()}') + return enc_cmd.decode('latin-1') + + +def config_set_config_response(security_ctx, response_data): + # Interpret protobuf response packet from SetConfig command + decrypt = security_ctx.decrypt_data(str_to_bytes(response_data)) + cmd_resp4 = proto.network_config_pb2.NetworkConfigPayload() + cmd_resp4.ParseFromString(decrypt) + if cmd_resp4.msg == proto.network_config_pb2.TypeRespSetWifiConfig: + print_verbose(security_ctx, f'SetConfig status: 0x{str(cmd_resp4.resp_set_wifi_config.status)}') + return cmd_resp4.resp_set_wifi_config.status + elif cmd_resp4.msg == proto.network_config_pb2.TypeRespSetThreadConfig: + print_verbose(security_ctx, f'SetConfig status: 0x{str(cmd_resp4.resp_set_thread_config.status)}') + return cmd_resp4.resp_set_thread_config.status + + +def config_apply_config_request(network_type, security_ctx): + # Form protobuf request packet for ApplyConfig command + cmd = proto.network_config_pb2.NetworkConfigPayload() + if network_type == 'wifi': + cmd.msg = proto.network_config_pb2.TypeCmdApplyWifiConfig + elif network_type == 'thread': + cmd.msg = proto.network_config_pb2.TypeCmdApplyThreadConfig + else: + raise RuntimeError + enc_cmd = security_ctx.encrypt_data(cmd.SerializeToString()) + print_verbose(security_ctx, f'Client -> Device (ApplyConfig cmd): 0x{enc_cmd.hex()}') + return enc_cmd.decode('latin-1') + + +def config_apply_config_response(security_ctx, response_data): + # Interpret protobuf response packet from ApplyConfig command + decrypt = security_ctx.decrypt_data(str_to_bytes(response_data)) + cmd_resp5 = proto.network_config_pb2.NetworkConfigPayload() + cmd_resp5.ParseFromString(decrypt) + if cmd_resp5.msg == proto.network_config_pb2.TypeRespApplyWifiConfig: + print_verbose(security_ctx, f'ApplyConfig status: 0x{str(cmd_resp5.resp_apply_wifi_config.status)}') + return cmd_resp5.resp_apply_wifi_config.status + elif cmd_resp5.msg == proto.network_config_pb2.TypeRespApplyThreadConfig: + print_verbose(security_ctx, f'ApplyConfig status: 0x{str(cmd_resp5.resp_apply_thread_config.status)}') + return cmd_resp5.resp_apply_thread_config.status diff --git a/components/network_provisioning/tool/esp_prov/prov/network_scan.py b/components/network_provisioning/tool/esp_prov/prov/network_scan.py new file mode 100644 index 0000000..d6106c2 --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/prov/network_scan.py @@ -0,0 +1,129 @@ +# SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +# APIs for interpreting and creating protobuf packets for Wi-Fi Scanning +import proto +from utils import str_to_bytes + + +def print_verbose(security_ctx, data): + if (security_ctx.verbose): + print(f'\x1b[32;20m++++ {data} ++++\x1b[0m') + + +def scan_start_request(network_type, security_ctx, blocking=True, passive=False, group_channels=5, period_ms=120): + # Form protobuf request packet for ScanStart command + cmd = proto.network_scan_pb2.NetworkScanPayload() + if network_type == 'wifi': + cmd.msg = proto.network_scan_pb2.TypeCmdScanWifiStart + cmd.cmd_scan_wifi_start.blocking = blocking + cmd.cmd_scan_wifi_start.passive = passive + cmd.cmd_scan_wifi_start.group_channels = group_channels + cmd.cmd_scan_wifi_start.period_ms = period_ms + elif network_type == 'thread': + cmd.msg = proto.network_scan_pb2.TypeCmdScanThreadStart + cmd.cmd_scan_thread_start.blocking = blocking + cmd.cmd_scan_thread_start.channel_mask = 0 + else: + raise RuntimeError + + enc_cmd = security_ctx.encrypt_data(cmd.SerializeToString()) + print_verbose(security_ctx, f'Client -> Device (Encrypted CmdScanStart): 0x{enc_cmd.hex()}') + return enc_cmd.decode('latin-1') + + +def scan_start_response(security_ctx, response_data): + # Interpret protobuf response packet from ScanStart command + dec_resp = security_ctx.decrypt_data(str_to_bytes(response_data)) + resp = proto.network_scan_pb2.NetworkScanPayload() + resp.ParseFromString(dec_resp) + print_verbose(security_ctx, f'ScanStart status: 0x{str(resp.status)}') + if resp.status != 0: + raise RuntimeError + + +def scan_status_request(network_type, security_ctx): + # Form protobuf request packet for ScanStatus command + cmd = proto.network_scan_pb2.NetworkScanPayload() + if network_type == 'wifi': + cmd.msg = proto.network_scan_pb2.TypeCmdScanWifiStatus + elif network_type == 'thread': + cmd.msg = proto.network_scan_pb2.TypeCmdScanThreadStatus + else: + raise RuntimeError + enc_cmd = security_ctx.encrypt_data(cmd.SerializeToString()) + print_verbose(security_ctx, f'Client -> Device (Encrypted CmdScanStatus): 0x{enc_cmd.hex()}') + return enc_cmd.decode('latin-1') + + +def scan_status_response(security_ctx, response_data): + # Interpret protobuf response packet from ScanStatus command + dec_resp = security_ctx.decrypt_data(str_to_bytes(response_data)) + resp = proto.network_scan_pb2.NetworkScanPayload() + resp.ParseFromString(dec_resp) + print_verbose(security_ctx, f'ScanStatus status: 0x{str(resp.status)}') + if resp.status != 0: + raise RuntimeError + if resp.msg == proto.network_scan_pb2.TypeRespScanWifiStatus: + return {'finished': resp.resp_scan_wifi_status.scan_finished, 'count': resp.resp_scan_wifi_status.result_count} + elif resp.msg == proto.network_scan_pb2.TypeRespScanThreadStatus: + return {'finished': resp.resp_scan_thread_status.scan_finished, 'count': resp.resp_scan_thread_status.result_count} + else: + raise RuntimeError + + +def scan_result_request(network_type, security_ctx, index, count): + # Form protobuf request packet for ScanResult command + cmd = proto.network_scan_pb2.NetworkScanPayload() + if network_type == 'wifi': + cmd.msg = proto.network_scan_pb2.TypeCmdScanWifiResult + cmd.cmd_scan_wifi_result.start_index = index + cmd.cmd_scan_wifi_result.count = count + elif network_type == 'thread': + cmd.msg = proto.network_scan_pb2.TypeCmdScanThreadResult + cmd.cmd_scan_thread_result.start_index = index + cmd.cmd_scan_thread_result.count = count + else: + raise RuntimeError + enc_cmd = security_ctx.encrypt_data(cmd.SerializeToString()) + print_verbose(security_ctx, f'Client -> Device (Encrypted CmdScanResult): 0x{enc_cmd.hex()}') + return enc_cmd.decode('latin-1') + + +def scan_result_response(security_ctx, response_data): + # Interpret protobuf response packet from ScanResult command + dec_resp = security_ctx.decrypt_data(str_to_bytes(response_data)) + resp = proto.network_scan_pb2.NetworkScanPayload() + resp.ParseFromString(dec_resp) + print_verbose(security_ctx, f'ScanResult status: 0x{str(resp.status)}') + if resp.status != 0: + raise RuntimeError + results = [] + if resp.msg == proto.network_scan_pb2.TypeRespScanWifiResult: + authmode_str = ['Open', 'WEP', 'WPA_PSK', 'WPA2_PSK', 'WPA_WPA2_PSK', + 'WPA2_ENTERPRISE', 'WPA3_PSK', 'WPA2_WPA3_PSK'] + for entry in resp.resp_scan_wifi_result.entries: + results += [{'ssid': entry.ssid.decode('latin-1').rstrip('\x00'), + 'bssid': entry.bssid.hex(), + 'channel': entry.channel, + 'rssi': entry.rssi, + 'auth': authmode_str[entry.auth]}] + print_verbose(security_ctx, f"ScanResult SSID : {str(results[-1]['ssid'])}") + print_verbose(security_ctx, f"ScanResult BSSID : {str(results[-1]['bssid'])}") + print_verbose(security_ctx, f"ScanResult Channel : {str(results[-1]['channel'])}") + print_verbose(security_ctx, f"ScanResult RSSI : {str(results[-1]['rssi'])}") + print_verbose(security_ctx, f"ScanResult AUTH : {str(results[-1]['auth'])}") + elif resp.msg == proto.network_scan_pb2.TypeRespScanThreadResult: + for entry in resp.resp_scan_thread_result.entries: + results += [{'pan_id': entry.pan_id, + 'ext_pan_id': entry.ext_pan_id.hex(), + 'network_name': entry.network_name, + 'channel': entry.channel, + 'rssi': entry.rssi, + 'lqi': entry.lqi, + 'ext_addr': entry.ext_addr.hex()}] + else: + raise RuntimeError + + return results diff --git a/components/network_provisioning/tool/esp_prov/security/__init__.py b/components/network_provisioning/tool/esp_prov/security/__init__.py new file mode 100644 index 0000000..4bec5d6 --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/security/__init__.py @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +from .security0 import * # noqa: F403, F401 +from .security1 import * # noqa: F403, F401 +from .security2 import * # noqa: F403, F401 diff --git a/components/network_provisioning/tool/esp_prov/security/security.py b/components/network_provisioning/tool/esp_prov/security/security.py new file mode 100644 index 0000000..527df3b --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/security/security.py @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +# Base class for protocomm security + + +class Security: + def __init__(self, security_session): + self.security_session = security_session diff --git a/components/network_provisioning/tool/esp_prov/security/security0.py b/components/network_provisioning/tool/esp_prov/security/security0.py new file mode 100644 index 0000000..e155ba8 --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/security/security0.py @@ -0,0 +1,53 @@ +# SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +# APIs for interpreting and creating protobuf packets for +# protocomm endpoint with security type protocomm_security0 + +import proto +from utils import str_to_bytes + +from .security import Security + + +class Security0(Security): + def __init__(self, verbose): + # Initialize state of the security1 FSM + self.session_state = 0 + self.verbose = verbose + Security.__init__(self, self.security0_session) + + def security0_session(self, response_data): + # protocomm security0 FSM which interprets/forms + # protobuf packets according to present state of session + if (self.session_state == 0): + self.session_state = 1 + return self.setup0_request() + if (self.session_state == 1): + self.setup0_response(response_data) + return None + + def setup0_request(self): + # Form protocomm security0 request packet + setup_req = proto.session_pb2.SessionData() + setup_req.sec_ver = 0 + session_cmd = proto.sec0_pb2.S0SessionCmd() + setup_req.sec0.sc.MergeFrom(session_cmd) + return setup_req.SerializeToString().decode('latin-1') + + def setup0_response(self, response_data): + # Interpret protocomm security0 response packet + setup_resp = proto.session_pb2.SessionData() + setup_resp.ParseFromString(str_to_bytes(response_data)) + # Check if security scheme matches + if setup_resp.sec_ver != proto.session_pb2.SecScheme0: + raise RuntimeError('Incorrect security scheme') + + def encrypt_data(self, data): + # Passive. No encryption when security0 used + return data + + def decrypt_data(self, data): + # Passive. No encryption when security0 used + return data diff --git a/components/network_provisioning/tool/esp_prov/security/security1.py b/components/network_provisioning/tool/esp_prov/security/security1.py new file mode 100644 index 0000000..e3afdc3 --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/security/security1.py @@ -0,0 +1,140 @@ +# SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +# APIs for interpreting and creating protobuf packets for +# protocomm endpoint with security type protocomm_security1 + +import proto +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from utils import long_to_bytes, str_to_bytes + +from .security import Security + + +def a_xor_b(a: bytes, b: bytes) -> bytes: + return b''.join(long_to_bytes(a[i] ^ b[i]) for i in range(0, len(b))) + + +# Enum for state of protocomm_security1 FSM +class security_state: + REQUEST1 = 0 + RESPONSE1_REQUEST2 = 1 + RESPONSE2 = 2 + FINISHED = 3 + + +class Security1(Security): + def __init__(self, pop, verbose): + # Initialize state of the security1 FSM + self.session_state = security_state.REQUEST1 + self.pop = str_to_bytes(pop) + self.verbose = verbose + Security.__init__(self, self.security1_session) + + def security1_session(self, response_data): + # protocomm security1 FSM which interprets/forms + # protobuf packets according to present state of session + if (self.session_state == security_state.REQUEST1): + self.session_state = security_state.RESPONSE1_REQUEST2 + return self.setup0_request() + elif (self.session_state == security_state.RESPONSE1_REQUEST2): + self.session_state = security_state.RESPONSE2 + self.setup0_response(response_data) + return self.setup1_request() + elif (self.session_state == security_state.RESPONSE2): + self.session_state = security_state.FINISHED + self.setup1_response(response_data) + return None + + print('Unexpected state') + return None + + def __generate_key(self): + # Generate private and public key pair for client + self.client_private_key = X25519PrivateKey.generate() + self.client_public_key = self.client_private_key.public_key().public_bytes( + encoding=serialization.Encoding.Raw, + format=serialization.PublicFormat.Raw) + + def _print_verbose(self, data): + if (self.verbose): + print(f'\x1b[32;20m++++ {data} ++++\x1b[0m') + + def setup0_request(self): + # Form SessionCmd0 request packet using client public key + setup_req = proto.session_pb2.SessionData() + setup_req.sec_ver = proto.session_pb2.SecScheme1 + self.__generate_key() + setup_req.sec1.sc0.client_pubkey = self.client_public_key + self._print_verbose(f'Client Public Key:\t0x{self.client_public_key.hex()}') + return setup_req.SerializeToString().decode('latin-1') + + def setup0_response(self, response_data): + # Interpret SessionResp0 response packet + setup_resp = proto.session_pb2.SessionData() + setup_resp.ParseFromString(str_to_bytes(response_data)) + self._print_verbose('Security version:\t' + str(setup_resp.sec_ver)) + if setup_resp.sec_ver != proto.session_pb2.SecScheme1: + raise RuntimeError('Incorrect security scheme') + + self.device_public_key = setup_resp.sec1.sr0.device_pubkey + # Device random is the initialization vector + device_random = setup_resp.sec1.sr0.device_random + self._print_verbose(f'Device Public Key:\t0x{self.device_public_key.hex()}') + self._print_verbose(f'Device Random:\t0x{device_random.hex()}') + + # Calculate Curve25519 shared key using Client private key and Device public key + sharedK = self.client_private_key.exchange(X25519PublicKey.from_public_bytes(self.device_public_key)) + self._print_verbose(f'Shared Key:\t0x{sharedK.hex()}') + + # If PoP is provided, XOR SHA256 of PoP with the previously + # calculated Shared Key to form the actual Shared Key + if len(self.pop) > 0: + # Calculate SHA256 of PoP + h = hashes.Hash(hashes.SHA256(), backend=default_backend()) + h.update(self.pop) + digest = h.finalize() + # XOR with and update Shared Key + sharedK = a_xor_b(sharedK, digest) + self._print_verbose(f'Updated Shared Key (Shared key XORed with PoP):\t0x{sharedK.hex()}') + # Initialize the encryption engine with Shared Key and initialization vector + cipher = Cipher(algorithms.AES(sharedK), modes.CTR(device_random), backend=default_backend()) + self.cipher = cipher.encryptor() + + def setup1_request(self): + # Form SessionCmd1 request packet using encrypted device public key + setup_req = proto.session_pb2.SessionData() + setup_req.sec_ver = proto.session_pb2.SecScheme1 + setup_req.sec1.msg = proto.sec1_pb2.Session_Command1 + # Encrypt device public key and attach to the request packet + client_verify = self.cipher.update(self.device_public_key) + self._print_verbose(f'Client Proof:\t0x{client_verify.hex()}') + setup_req.sec1.sc1.client_verify_data = client_verify + return setup_req.SerializeToString().decode('latin-1') + + def setup1_response(self, response_data): + # Interpret SessionResp1 response packet + setup_resp = proto.session_pb2.SessionData() + setup_resp.ParseFromString(str_to_bytes(response_data)) + # Ensure security scheme matches + if setup_resp.sec_ver == proto.session_pb2.SecScheme1: + # Read encrypyed device verify string + device_verify = setup_resp.sec1.sr1.device_verify_data + self._print_verbose(f'Device Proof:\t0x{device_verify.hex()}') + # Decrypt the device verify string + enc_client_pubkey = self.cipher.update(setup_resp.sec1.sr1.device_verify_data) + # Match decrypted string with client public key + if enc_client_pubkey != self.client_public_key: + raise RuntimeError('Failed to verify device!') + else: + raise RuntimeError('Unsupported security protocol') + + def encrypt_data(self, data): + return self.cipher.update(data) + + def decrypt_data(self, data): + return self.cipher.update(data) diff --git a/components/network_provisioning/tool/esp_prov/security/security2.py b/components/network_provisioning/tool/esp_prov/security/security2.py new file mode 100644 index 0000000..9d427f1 --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/security/security2.py @@ -0,0 +1,182 @@ +# SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# APIs for interpreting and creating protobuf packets for +# protocomm endpoint with security type protocomm_security2 +import struct +from typing import Any +from typing import Type + +import proto +from cryptography.hazmat.primitives.ciphers.aead import AESGCM +from utils import long_to_bytes +from utils import str_to_bytes + +from .security import Security +from .srp6a import generate_salt_and_verifier +from .srp6a import Srp6a + +AES_KEY_LEN = 256 // 8 + + +# Enum for state of protocomm_security1 FSM +class security_state: + REQUEST1 = 0 + RESPONSE1_REQUEST2 = 1 + RESPONSE2 = 2 + FINISHED = 3 + + +def sec2_gen_salt_verifier(username: str, password: str, salt_len: int) -> Any: + salt, verifier = generate_salt_and_verifier(username, password, len_s=salt_len) + + salt_str = ', '.join([format(b, '#04x') for b in salt]) + salt_c_arr = '\n '.join(salt_str[i: i + 96] for i in range(0, len(salt_str), 96)) + print(f'static const char sec2_salt[] = {{\n {salt_c_arr}\n}};\n') # noqa E702 + + verifier_str = ', '.join([format(b, '#04x') for b in verifier]) + verifier_c_arr = '\n '.join(verifier_str[i: i + 96] for i in range(0, len(verifier_str), 96)) + print(f'static const char sec2_verifier[] = {{\n {verifier_c_arr}\n}};\n') # noqa E702 + + +class Security2(Security): + def __init__(self, sec_patch_ver:int, username: str, password: str, verbose: bool) -> None: + # Initialize state of the security2 FSM + self.session_state = security_state.REQUEST1 + self.sec_patch_ver = sec_patch_ver + self.username = username + self.password = password + self.verbose = verbose + + self.srp6a_ctx: Type[Srp6a] + self.cipher: Type[AESGCM] + + self.client_pop_key = None + self.nonce = bytearray() + + Security.__init__(self, self.security2_session) + + def security2_session(self, response_data: bytes) -> Any: + # protocomm security2 FSM which interprets/forms + # protobuf packets according to present state of session + if (self.session_state == security_state.REQUEST1): + self.session_state = security_state.RESPONSE1_REQUEST2 + return self.setup0_request() + + if (self.session_state == security_state.RESPONSE1_REQUEST2): + self.session_state = security_state.RESPONSE2 + self.setup0_response(response_data) + return self.setup1_request() + + if (self.session_state == security_state.RESPONSE2): + self.session_state = security_state.FINISHED + self.setup1_response(response_data) + return None + + print('---- Unexpected state! ----') + return None + + def _print_verbose(self, data: str) -> None: + if (self.verbose): + print(f'\x1b[32;20m++++ {data} ++++\x1b[0m') # noqa E702 + + def setup0_request(self) -> Any: + # Form SessionCmd0 request packet using client public key + setup_req = proto.session_pb2.SessionData() + setup_req.sec_ver = proto.session_pb2.SecScheme2 + setup_req.sec2.msg = proto.sec2_pb2.S2Session_Command0 + + setup_req.sec2.sc0.client_username = str_to_bytes(self.username) + self.srp6a_ctx = Srp6a(self.username, self.password) + if self.srp6a_ctx is None: + raise RuntimeError('Failed to initialize SRP6a instance!') + + client_pubkey = long_to_bytes(self.srp6a_ctx.A) + setup_req.sec2.sc0.client_pubkey = client_pubkey + + self._print_verbose(f'Client Public Key:\t0x{client_pubkey.hex()}') + return setup_req.SerializeToString().decode('latin-1') + + def setup0_response(self, response_data: bytes) -> None: + # Interpret SessionResp0 response packet + setup_resp = proto.session_pb2.SessionData() + setup_resp.ParseFromString(str_to_bytes(response_data)) + self._print_verbose(f'Security version:\t{str(setup_resp.sec_ver)}') + if setup_resp.sec_ver != proto.session_pb2.SecScheme2: + raise RuntimeError('Incorrect security scheme') + + # Device public key, random salt and password verifier + device_pubkey = setup_resp.sec2.sr0.device_pubkey + device_salt = setup_resp.sec2.sr0.device_salt + + self._print_verbose(f'Device Public Key:\t0x{device_pubkey.hex()}') + self.client_pop_key = self.srp6a_ctx.process_challenge(device_salt, device_pubkey) + + def setup1_request(self) -> Any: + # Form SessionCmd1 request packet using encrypted device public key + setup_req = proto.session_pb2.SessionData() + setup_req.sec_ver = proto.session_pb2.SecScheme2 + setup_req.sec2.msg = proto.sec2_pb2.S2Session_Command1 + + # Encrypt device public key and attach to the request packet + if self.client_pop_key is None: + raise RuntimeError('Failed to generate client proof!') + + self._print_verbose(f'Client Proof:\t0x{self.client_pop_key.hex()}') + setup_req.sec2.sc1.client_proof = self.client_pop_key + + return setup_req.SerializeToString().decode('latin-1') + + def setup1_response(self, response_data: bytes) -> Any: + # Interpret SessionResp1 response packet + setup_resp = proto.session_pb2.SessionData() + setup_resp.ParseFromString(str_to_bytes(response_data)) + # Ensure security scheme matches + if setup_resp.sec_ver == proto.session_pb2.SecScheme2: + # Read encrypyed device proof string + device_proof = setup_resp.sec2.sr1.device_proof + self._print_verbose(f'Device Proof:\t0x{device_proof.hex()}') + self.srp6a_ctx.verify_session(device_proof) + if not self.srp6a_ctx.authenticated(): + raise RuntimeError('Failed to verify device proof') + else: + raise RuntimeError('Unsupported security protocol') + + # Getting the shared secret + shared_secret = self.srp6a_ctx.get_session_key() + self._print_verbose(f'Shared Secret:\t0x{shared_secret.hex()}') + + # Using the first 256 bits of a 512 bit key + session_key = shared_secret[:AES_KEY_LEN] + self._print_verbose(f'Session Key:\t0x{session_key.hex()}') + + # 96-bit nonce + self.nonce = bytearray(setup_resp.sec2.sr1.device_nonce) + if self.nonce is None: + raise RuntimeError('Received invalid nonce from device!') + self._print_verbose(f'Nonce:\t0x{self.nonce.hex()}') + + # Initialize the encryption engine with Shared Key and initialization vector + self.cipher = AESGCM(session_key) + if self.cipher is None: + raise RuntimeError('Failed to initialize AES-GCM cryptographic engine!') + + def _increment_nonce(self) -> None: + """Increment the last 4 bytes of nonce (big-endian counter).""" + if self.sec_patch_ver == 1: + counter = struct.unpack('>I', self.nonce[8:])[0] # Read last 4 bytes as big-endian integer + counter += 1 # Increment counter + if counter > 0xFFFFFFFF: # Check for overflow + raise RuntimeError('Nonce counter overflow') + self.nonce[8:] = struct.pack('>I', counter) # Store back as big-endian + + def encrypt_data(self, data: bytes) -> Any: + self._print_verbose(f'Nonce:\t0x{self.nonce.hex()}') + ciphertext = self.cipher.encrypt(self.nonce, data, None) + self._increment_nonce() + return ciphertext + + def decrypt_data(self, data: bytes) -> Any: + self._print_verbose(f'Nonce:\t0x{self.nonce.hex()}') + plaintext = self.cipher.decrypt(self.nonce, data, None) + self._increment_nonce() + return plaintext diff --git a/components/network_provisioning/tool/esp_prov/security/srp6a.py b/components/network_provisioning/tool/esp_prov/security/srp6a.py new file mode 100644 index 0000000..757bc4d --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/security/srp6a.py @@ -0,0 +1,301 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +# N A large safe prime (N = 2q+1, where q is prime) [All arithmetic is done modulo N] +# g A generator modulo N +# k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) +# s User's salt +# Iu Username +# p Cleartext Password +# H() One-way hash function +# ^ (Modular) Exponentiation +# u Random scrambling parameter +# a, b Secret ephemeral values +# A, B Public ephemeral values +# x Private key (derived from p and s) +# v Password verifier + +import hashlib +import os +from typing import Any, Callable, Optional, Tuple + +from utils import bytes_to_long, long_to_bytes + +SHA1 = 0 +SHA224 = 1 +SHA256 = 2 +SHA384 = 3 +SHA512 = 4 + +NG_1024 = 0 +NG_2048 = 1 +NG_3072 = 2 +NG_4096 = 3 +NG_8192 = 4 + +_hash_map = {SHA1: hashlib.sha1, + SHA224: hashlib.sha224, + SHA256: hashlib.sha256, + SHA384: hashlib.sha384, + SHA512: hashlib.sha512} + + +_ng_const = ( + # 1024-bit + ('''\ +EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496\ +EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E\ +F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA\ +9AFD5138FE8376435B9FC61D2FC0EB06E3''', + '2'), + # 2048 + ('''\ +AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4\ +A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60\ +95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF\ +747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907\ +8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861\ +60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB\ +FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73''', + '2'), + # 3072 + ('''\ +FFFFFFFFFFFFFFFFC90FDAA22168C2\ +34C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E\ +3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B5\ +76625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE\ +9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D3\ +9A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED5290770\ +96966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E77\ +2C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF69558171839\ +95497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A\ +33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6\ +E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA\ +06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C77\ +0988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2\ +CAFFFFFFFFFFFFFFFF''', + '5'), + # 4096 + ('''\ +FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\ +8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\ +302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\ +A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\ +49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\ +FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\ +670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\ +180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\ +3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\ +04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\ +B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\ +1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ +BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\ +E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\ +99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\ +04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\ +233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\ +D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199\ +FFFFFFFFFFFFFFFF''', + '5'), + # 8192 + ('''\ +FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\ +8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\ +302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\ +A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\ +49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\ +FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\ +670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\ +180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\ +3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\ +04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\ +B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\ +1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ +BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\ +E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\ +99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\ +04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\ +233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\ +D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492\ +36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406\ +AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918\ +DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151\ +2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03\ +F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F\ +BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA\ +CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B\ +B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632\ +387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E\ +6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA\ +3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C\ +5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9\ +22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886\ +2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6\ +6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5\ +0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268\ +359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6\ +FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71\ +60C980DD98EDD3DFFFFFFFFFFFFFFFFF''', + '0x13') +) + + +def get_ng(ng_type: int) -> Tuple[int, int]: + n_hex, g_hex = _ng_const[ng_type] + return int(n_hex, 16), int(g_hex, 16) + + +def get_random(nbytes: int) -> Any: + return bytes_to_long(os.urandom(nbytes)) + + +def get_random_of_length(nbytes: int) -> Any: + offset = (nbytes * 8) - 1 + return get_random(nbytes) | (1 << offset) + + +def H(hash_class: Callable, *args: Any, **kwargs: Any) -> int: + width = kwargs.get('width', None) + + h = hash_class() + + for s in args: + if s is not None: + data = long_to_bytes(s) if isinstance(s, int) else s + if width is not None: + h.update(bytes(width - len(data))) + h.update(data) + + return int(h.hexdigest(), 16) + + +def H_N_xor_g(hash_class: Callable, N: int, g: int) -> bytes: + bin_N = long_to_bytes(N) + bin_g = long_to_bytes(g) + + padding = len(bin_N) - len(bin_g) + + hN = hash_class(bin_N).digest() + hg = hash_class(b''.join([b'\0' * padding, bin_g])).digest() + + return b''.join(long_to_bytes(hN[i] ^ hg[i]) for i in range(0, len(hN))) + + +def calculate_x(hash_class: Callable, s: Any, Iu: str, p: str) -> int: + _Iu = Iu.encode() + _p = p.encode() + + return H(hash_class, s, H(hash_class, _Iu + b':' + _p)) + + +def generate_salt_and_verifier(Iu: str, p: str, len_s: int, hash_alg: int = SHA512, ng_type: int = NG_3072) -> Tuple[bytes, bytes]: + hash_class = _hash_map[hash_alg] + N, g = get_ng(ng_type) + + _s = long_to_bytes(get_random(len_s)) + _v = long_to_bytes(pow(g, calculate_x(hash_class, _s, Iu, p), N)) + + return _s, _v + + +def calculate_M(hash_class: Callable, N: int, g: int, Iu: str, s: int, A: int, B: int, K: bytes) -> Any: + _Iu = Iu.encode() + h = hash_class() + h.update(H_N_xor_g(hash_class, N, g)) + h.update(hash_class(_Iu).digest()) + h.update(long_to_bytes(s)) + h.update(long_to_bytes(A)) + h.update(long_to_bytes(B)) + h.update(K) + return h.digest() + + +def calculate_H_AMK(hash_class: Callable, A: int, M: bytes, K: bytes) -> Any: + h = hash_class() + h.update(long_to_bytes(A)) + h.update(M) + h.update(K) + return h.digest() + + +class Srp6a (object): + def __init__(self, username: str, password: str, hash_alg: int = SHA512, ng_type: int = NG_3072): + hash_class = _hash_map[hash_alg] + + N, g = get_ng(ng_type) + k = H(hash_class, N, g, width=len(long_to_bytes(N))) + + self.Iu = username + self.p = password + + self.a = get_random_of_length(32) + self.A = pow(g, self.a, N) + + self.v: Optional[int] = None + self.K: Optional[bytes] = None + self.H_AMK = None + self._authenticated = False + + self.hash_class = hash_class + self.N = N + self.g = g + self.k = k + + def authenticated(self) -> bool: + return self._authenticated + + def get_username(self) -> str: + return self.Iu + + def get_ephemeral_secret(self) -> Any: + return long_to_bytes(self.a) + + def get_session_key(self) -> Any: + return self.K if self._authenticated else None + + def start_authentication(self) -> Tuple[str, bytes]: + return (self.Iu, long_to_bytes(self.A)) + + # Returns M or None if SRP-6a safety check is violated + def process_challenge(self, bytes_s: bytes, bytes_B: bytes) -> Any: + s = bytes_to_long(bytes_s) + B = bytes_to_long(bytes_B) + + N = self.N + g = self.g + k = self.k + + hash_class = self.hash_class + + # SRP-6a safety check + if (B % N) == 0: + return None + + u = H(hash_class, self.A, B, width=len(long_to_bytes(N))) + if u == 0: # SRP-6a safety check + return None + + x = calculate_x(hash_class, s, self.Iu, self.p) + + v = pow(g, x, N) + + S = pow((B - k * v), (self.a + u * x), N) + + self.K = hash_class(long_to_bytes(S)).digest() + + M = calculate_M(hash_class, N, g, self.Iu, s, self.A, B, self.K) + if not M: + return None + + self.H_AMK = calculate_H_AMK(hash_class, self.A, M, self.K) + + return M + + def verify_session(self, host_HAMK: bytes) -> None: + if self.H_AMK == host_HAMK: + self._authenticated = True + + +class AuthenticationFailed (Exception): + pass diff --git a/components/network_provisioning/tool/esp_prov/transport/__init__.py b/components/network_provisioning/tool/esp_prov/transport/__init__.py new file mode 100644 index 0000000..82ae39a --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/transport/__init__.py @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +from .transport_ble import * # noqa: F403, F401 +from .transport_console import * # noqa: F403, F401 +from .transport_http import * # noqa: F403, F401 diff --git a/components/network_provisioning/tool/esp_prov/transport/ble_cli.py b/components/network_provisioning/tool/esp_prov/transport/ble_cli.py new file mode 100644 index 0000000..29ca2d8 --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/transport/ble_cli.py @@ -0,0 +1,213 @@ +# SPDX-FileCopyrightText: 2018-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +import platform + +from utils import hex_str_to_bytes, str_to_bytes + +fallback = True + + +# Check if required packages are installed +# else fallback to console mode +try: + import bleak + fallback = False +except ImportError: + pass + + +# -------------------------------------------------------------------- + +def device_sort(device): + return device[0].address + + +class BLE_Bleak_Client: + def __init__(self): + self.adapter = None + self.adapter_props = None + self.characteristics = dict() + self.chrc_names = None + self.device = None + self.devname = None + self.iface = None + self.nu_lookup = None + self.services = None + self.srv_uuid_adv = None + self.srv_uuid_fallback = None + + async def connect(self, devname, iface, chrc_names, fallback_srv_uuid): + self.devname = devname + self.srv_uuid_fallback = fallback_srv_uuid + self.chrc_names = [name.lower() for name in chrc_names] + self.iface = iface + + print('Discovering...') + try: + discovery = await bleak.BleakScanner.discover(return_adv=True, adapter=self.iface) + devices = list(discovery.values()) + except bleak.exc.BleakDBusError as e: + if str(e) == '[org.bluez.Error.NotReady] Resource Not Ready': + raise RuntimeError('Bluetooth is not ready. Maybe try `bluetoothctl power on`?') + raise + + found_device = None + + if self.devname is None: + if len(devices) == 0: + print('No devices found!') + exit(1) + + while True: + devices.sort(key=device_sort) + print('==== BLE Discovery results ====') + print('{0: >4} {1: <33} {2: <12}'.format( + 'S.N.', 'Name', 'Address')) + for i, _ in enumerate(devices): + print('[{0: >2}] {1: <33} {2: <12}'.format(i + 1, devices[i][0].name or 'Unknown', devices[i][0].address)) + + while True: + try: + select = int(input('Select device by number (0 to rescan) : ')) + if select < 0 or select > len(devices): + raise ValueError + break + except ValueError: + print('Invalid input! Retry') + + if select != 0: + break + + discovery = await bleak.BleakScanner.discover(return_adv=True, adapter=self.iface) + devices = list(discovery.values()) + + self.devname = devices[select - 1][0].name + found_device = devices[select - 1] + else: + for d in devices: + if d[0].name == self.devname: + found_device = d + + if not found_device: + raise RuntimeError('Device not found') + + uuids = found_device[1].service_uuids + # There should be 1 service UUID in advertising data + # If bluez had cached an old version of the advertisement data + # the list of uuids may be incorrect, in which case connection + # or service discovery may fail the first time. If that happens + # the cache will be refreshed before next retry + if len(uuids) == 1: + self.srv_uuid_adv = uuids[0] + + print('Connecting...') + self.device = bleak.BleakClient(found_device[0].address, adapter=self.iface) + await self.device.connect() + # must be paired on Windows to access characteristics; + # cannot be paired on Mac + if platform.system() == 'Windows': + await self.device.pair() + + print('Getting Services...') + services = self.device.services + + service = services[self.srv_uuid_adv] or services[self.srv_uuid_fallback] + if not service: + await self.device.disconnect() + self.device = None + raise RuntimeError('Provisioning service not found') + + nu_lookup = dict() + for characteristic in service.characteristics: + for descriptor in characteristic.descriptors: + if descriptor.uuid[4:8] != '2901': + continue + readval = await self.device.read_gatt_descriptor(descriptor.handle) + found_name = ''.join(chr(b) for b in readval).lower() + nu_lookup[found_name] = characteristic.uuid + self.characteristics[characteristic.uuid] = characteristic + + match_found = True + for name in self.chrc_names: + if name not in nu_lookup: + # Endpoint name not present + match_found = False + break + + # Create lookup table only if all endpoint names found + self.nu_lookup = [None, nu_lookup][match_found] + + return True + + def get_nu_lookup(self): + return self.nu_lookup + + def has_characteristic(self, uuid): + print('checking for characteristic ' + uuid) + if uuid in self.characteristics: + return True + return False + + async def disconnect(self): + if self.device: + print('Disconnecting...') + if platform.system() == 'Windows': + await self.device.unpair() + await self.device.disconnect() + self.device = None + self.nu_lookup = None + self.characteristics = dict() + + async def send_data(self, characteristic_uuid, data): + await self.device.write_gatt_char(characteristic_uuid, bytearray(data.encode('latin-1')), True) + readval = await self.device.read_gatt_char(characteristic_uuid) + return ''.join(chr(b) for b in readval) +# -------------------------------------------------------------------- + + +# Console based BLE client for Cross Platform support +class BLE_Console_Client: + async def connect(self, devname, iface, chrc_names, fallback_srv_uuid): + print('BLE client is running in console mode') + print('\tThis could be due to your platform not being supported or dependencies not being met') + print('\tPlease ensure all pre-requisites are met to run the full fledged client') + print('BLECLI >> Please connect to BLE device `' + devname + '` manually using your tool of choice') + resp = input('BLECLI >> Was the device connected successfully? [y/n] ') + if resp != 'Y' and resp != 'y': + return False + print('BLECLI >> List available attributes of the connected device') + resp = input("BLECLI >> Is the service UUID '" + fallback_srv_uuid + "' listed among available attributes? [y/n] ") + if resp != 'Y' and resp != 'y': + return False + return True + + def get_nu_lookup(self): + return None + + def has_characteristic(self, uuid): + resp = input("BLECLI >> Is the characteristic UUID '" + uuid + "' listed among available attributes? [y/n] ") + if resp != 'Y' and resp != 'y': + return False + return True + + async def disconnect(self): + pass + + async def send_data(self, characteristic_uuid, data): + print("BLECLI >> Write following data to characteristic with UUID '" + characteristic_uuid + "' :") + print('\t>> ' + str_to_bytes(data).hex()) + print('BLECLI >> Enter data read from characteristic (in hex) :') + resp = input('\t<< ') + return hex_str_to_bytes(resp) + + +# -------------------------------------------------------------------- + + +# Function to get client instance depending upon platform +def get_client(): + if fallback: + return BLE_Console_Client() + return BLE_Bleak_Client() diff --git a/components/network_provisioning/tool/esp_prov/transport/transport.py b/components/network_provisioning/tool/esp_prov/transport/transport.py new file mode 100644 index 0000000..4dab81e --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/transport/transport.py @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +# Base class for protocomm transport + +import abc + + +class Transport(): + + @abc.abstractmethod + def send_session_data(self, data): + pass + + @abc.abstractmethod + def send_config_data(self, data): + pass + + async def disconnect(self): + pass diff --git a/components/network_provisioning/tool/esp_prov/transport/transport_ble.py b/components/network_provisioning/tool/esp_prov/transport/transport_ble.py new file mode 100644 index 0000000..bb8eeeb --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/transport/transport_ble.py @@ -0,0 +1,53 @@ +# SPDX-FileCopyrightText: 2018-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +from . import ble_cli +from .transport import Transport + +DEFAULT_BLE_ADAPTER = 'hci0' + + +class Transport_BLE(Transport): + def __init__(self, service_uuid, nu_lookup, ble_adapter=DEFAULT_BLE_ADAPTER): + self.nu_lookup = nu_lookup + self.service_uuid = service_uuid + self.name_uuid_lookup = None + self.ble_adapter = ble_adapter + # Expect service UUID like '0000ffff-0000-1000-8000-00805f9b34fb' + for name in nu_lookup.keys(): + # Calculate characteristic UUID for each endpoint + nu_lookup[name] = service_uuid[:4] + '{:02x}'.format( + int(nu_lookup[name], 16) & int(service_uuid[4:8], 16)) + service_uuid[8:] + + # Get BLE client module + self.cli = ble_cli.get_client() + + async def connect(self, devname): + # Use client to connect to BLE device and bind to service + if not await self.cli.connect(devname=devname, iface=self.ble_adapter, + chrc_names=self.nu_lookup.keys(), + fallback_srv_uuid=self.service_uuid): + raise RuntimeError('Failed to initialize transport') + + # Irrespective of provided parameters, let the client + # generate a lookup table by reading advertisement data + # and characteristic user descriptors + self.name_uuid_lookup = self.cli.get_nu_lookup() + + # If that doesn't work, use the lookup table provided as parameter + if self.name_uuid_lookup is None: + self.name_uuid_lookup = self.nu_lookup + # Check if expected characteristics are provided by the service + for name in self.name_uuid_lookup.keys(): + if not self.cli.has_characteristic(self.name_uuid_lookup[name]): + raise RuntimeError(f"'{name}' endpoint not found") + + async def disconnect(self): + await self.cli.disconnect() + + async def send_data(self, ep_name, data): + # Write (and read) data to characteristic corresponding to the endpoint + if ep_name not in self.name_uuid_lookup.keys(): + raise RuntimeError(f'Invalid endpoint: {ep_name}') + return await self.cli.send_data(self.name_uuid_lookup[ep_name], data) diff --git a/components/network_provisioning/tool/esp_prov/transport/transport_console.py b/components/network_provisioning/tool/esp_prov/transport/transport_console.py new file mode 100644 index 0000000..8fbcc2f --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/transport/transport_console.py @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +from utils import hex_str_to_bytes, str_to_bytes + +from .transport import Transport + + +class Transport_Console(Transport): + + async def send_data(self, path, data, session_id=0): + print('Client->Device msg :', path, session_id, str_to_bytes(data).hex()) + try: + resp = input('Enter device->client msg : ') + except Exception as err: + print('error:', err) + return None + return hex_str_to_bytes(resp) diff --git a/components/network_provisioning/tool/esp_prov/transport/transport_http.py b/components/network_provisioning/tool/esp_prov/transport/transport_http.py new file mode 100644 index 0000000..1d9f7a3 --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/transport/transport_http.py @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: 2018-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# +import socket +from http.client import HTTPConnection, HTTPSConnection + +from utils import str_to_bytes + +from .transport import Transport + + +class Transport_HTTP(Transport): + def __init__(self, hostname, ssl_context=None): + try: + socket.gethostbyname(hostname.split(':')[0]) + except socket.gaierror: + raise RuntimeError(f'Unable to resolve hostname: {hostname}') + + if ssl_context is None: + self.conn = HTTPConnection(hostname, timeout=60) + else: + self.conn = HTTPSConnection(hostname, context=ssl_context, timeout=60) + try: + print(f'++++ Connecting to {hostname}++++') + self.conn.connect() + except Exception as err: + raise RuntimeError('Connection Failure : ' + str(err)) + self.headers = {'Content-type': 'application/x-www-form-urlencoded','Accept': 'text/plain'} + + def _send_post_request(self, path, data): + data = str_to_bytes(data) if isinstance(data, str) else data + try: + self.conn.request('POST', path, data, self.headers) + response = self.conn.getresponse() + # While establishing a session, the device sends the Set-Cookie header + # with value 'session=cookie_session_id' in its first response of the session to the tool. + # To maintain the same session, successive requests from the tool should include + # an additional 'Cookie' header with the above received value. + for hdr_key, hdr_val in response.getheaders(): + if hdr_key == 'Set-Cookie': + self.headers['Cookie'] = hdr_val + if response.status == 200: + return response.read().decode('latin-1') + except Exception as err: + raise RuntimeError('Connection Failure : ' + str(err)) + raise RuntimeError('Server responded with error code ' + str(response.status)) + + async def send_data(self, ep_name, data): + return self._send_post_request('/' + ep_name, data) diff --git a/components/network_provisioning/tool/esp_prov/utils/__init__.py b/components/network_provisioning/tool/esp_prov/utils/__init__.py new file mode 100644 index 0000000..24d1842 --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/utils/__init__.py @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +from .convenience import * # noqa: F403, F401 diff --git a/components/network_provisioning/tool/esp_prov/utils/convenience.py b/components/network_provisioning/tool/esp_prov/utils/convenience.py new file mode 100644 index 0000000..8441308 --- /dev/null +++ b/components/network_provisioning/tool/esp_prov/utils/convenience.py @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +# Convenience functions for commonly used data type conversions + +def bytes_to_long(s: bytes) -> int: + return int.from_bytes(s, 'big') + + +def long_to_bytes(n: int) -> bytes: + if n == 0: + return b'\x00' + return n.to_bytes((n.bit_length() + 7) // 8, 'big') + + +# 'deadbeef' -> b'deadbeef' +def str_to_bytes(s: str) -> bytes: + return bytes(s, encoding='latin-1') + + +# 'deadbeef' -> b'\xde\xad\xbe\xef' +def hex_str_to_bytes(s: str) -> bytes: + return bytes.fromhex(s) + + +def int_to_hex_str(n: int) -> str: + hex_string = format(n, 'x') + if len(hex_string) % 2 != 0: + hex_string = '0' + hex_string + return hex_string diff --git a/dependencies.lock b/dependencies.lock index 7f66f2d..b277c5a 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -20,7 +20,7 @@ dependencies: require: private version: '>=5.0' source: - registry_url: https://components.espressif.com + registry_url: https://components.espressif.com/ type: service version: 1.7.19~2 espressif/cmake_utilities: @@ -229,20 +229,16 @@ dependencies: type: service version: 1.6.58 espressif/network_provisioning: - component_hash: 72d27784e3daf807418a34fb00be136ec50c6db49d989ce981d22e031fc0e7f8 dependencies: - name: espressif/cjson - registry_url: https://components.espressif.com - require: private rules: - if: idf_version >= 6.0 version: ^1.7.19 - name: idf - require: private version: '>=5.1' source: - registry_url: https://components.espressif.com/ - type: service + path: components/network_provisioning + type: local version: 1.2.4 espressif/zlib: component_hash: 64ea2fd6754c5a05951f1ddb312ac835cd782e68cb5bd9dfcd1c3a107c8fbfbc @@ -300,6 +296,7 @@ dependencies: type: service version: 1.0.1 direct_dependencies: +- espressif/cjson - espressif/esp_codec_dev - espressif/esp_lcd_sh8601 - espressif/esp_lvgl_adapter @@ -309,6 +306,6 @@ direct_dependencies: - waveshare/esp_lcd_touch_cst9217 - waveshare/pcf85063a - waveshare/qmi8658 -manifest_hash: 674b865190c20edea7d5cd705b9cf52cb0fdc7084c5249c466fb87ad5be9409e +manifest_hash: d7f80c63188d128ef11ea954c98cf33d0ce1eeeb230509bb1d96b2729a01ca42 target: esp32c6 version: 3.0.0 diff --git a/main/idf_component.yml b/main/idf_component.yml index 5c21a97..8d952ed 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -12,4 +12,6 @@ dependencies: waveshare/pcf85063a: ^1.1.1 waveshare/qmi8658: ^1.0.1 espressif/esp_lvgl_adapter: ^0.3.0 - espressif/network_provisioning: "*" + espressif/network_provisioning: + version: "*" + override_path: ../components/network_provisioning diff --git a/main/modules/desktop_ui/desktop_ui.cpp b/main/modules/desktop_ui/desktop_ui.cpp index 7f8487d..f4beb04 100644 --- a/main/modules/desktop_ui/desktop_ui.cpp +++ b/main/modules/desktop_ui/desktop_ui.cpp @@ -221,7 +221,9 @@ static void desktop_temperature_task(void *arg) LV_UNUSED(arg); while(1) { - (void)desktop_fetch_temperature_history(); + if(!WifiProvisioning_IsProvisioning()) { + (void)desktop_fetch_temperature_history(); + } vTaskDelay(pdMS_TO_TICKS(TEMP_FETCH_INTERVAL_MS)); } } diff --git a/main/modules/wifi_provisioning/wifi_provisioning.cpp b/main/modules/wifi_provisioning/wifi_provisioning.cpp index 035bd3c..207fb4a 100644 --- a/main/modules/wifi_provisioning/wifi_provisioning.cpp +++ b/main/modules/wifi_provisioning/wifi_provisioning.cpp @@ -31,6 +31,7 @@ static bool wifi_mgr_initialized = false; static bool wifi_is_provisioning = false; static bool wifi_sntp_initialized = false; static bool wifi_is_connected = false; +static bool wifi_ps_overridden_for_prov = false; static char prov_service_name[20] = {0}; static char prov_qr_payload[200] = {0}; static const char *SINGAPORE_TZ = "SGT-8"; @@ -132,9 +133,15 @@ static void get_device_service_name(char *service_name, size_t max_len) static void wifi_prov_print_url(const char *name, const char *username, const char *pop) { - snprintf(prov_qr_payload, sizeof(prov_qr_payload), - "{\"ver\":\"%s\",\"name\":\"%s\",\"username\":\"%s\",\"pop\":\"%s\",\"transport\":\"%s\"}", - PROV_QR_VERSION, name, username, pop, PROV_TRANSPORT_SOFTAP); + if(username != NULL && username[0] != '\0') { + snprintf(prov_qr_payload, sizeof(prov_qr_payload), + "{\"ver\":\"%s\",\"name\":\"%s\",\"username\":\"%s\",\"pop\":\"%s\",\"transport\":\"%s\",\"network\":\"wifi\"}", + PROV_QR_VERSION, name, username, pop, PROV_TRANSPORT_SOFTAP); + } else { + snprintf(prov_qr_payload, sizeof(prov_qr_payload), + "{\"ver\":\"%s\",\"name\":\"%s\",\"pop\":\"%s\",\"transport\":\"%s\",\"network\":\"wifi\"}", + PROV_QR_VERSION, name, pop, PROV_TRANSPORT_SOFTAP); + } ESP_LOGI(TAG, "Provisioning URL:\n%s?data=%s", QRCODE_BASE_URL, prov_qr_payload); } @@ -161,33 +168,40 @@ static esp_err_t wifi_prov_manager_init_if_needed(void) static esp_err_t wifi_start_softap_provisioning(void) { char service_name[16] = {0}; - const char *username = "wifiprov"; + const char *username = NULL; const char *pop = "abcd1234"; - const char *service_key = "prov1234"; - network_prov_security2_params_t sec2_params = {}; + const char *service_key = NULL; get_device_service_name(service_name, sizeof(service_name)); strncpy(prov_service_name, service_name, sizeof(prov_service_name) - 1); prov_service_name[sizeof(prov_service_name) - 1] = '\0'; - sec2_params.salt = sec2_salt; - sec2_params.salt_len = sizeof(sec2_salt); - sec2_params.verifier = sec2_verifier; - sec2_params.verifier_len = sizeof(sec2_verifier); + /* Block normal STA reconnect flow while SoftAP provisioning is active. */ + wifi_is_provisioning = true; + wifi_is_connected = false; + xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_EVENT); - ESP_LOGI(TAG, "Starting SoftAP provisioning (service name: %s)", service_name); - esp_err_t ret = network_prov_mgr_start_provisioning(NETWORK_PROV_SECURITY_2, - (const void *)&sec2_params, + if(!wifi_ps_overridden_for_prov) { + esp_err_t ps_ret = esp_wifi_set_ps(WIFI_PS_NONE); + if(ps_ret == ESP_OK) { + wifi_ps_overridden_for_prov = true; + ESP_LOGI(TAG, "Disabled Wi-Fi power save during provisioning"); + } else { + ESP_LOGW(TAG, "Failed to disable Wi-Fi power save during provisioning (error: %d)", ps_ret); + } + } + + ESP_LOGI(TAG, "Starting SoftAP provisioning (service name: %s, auth: open + sec1)", service_name); + esp_err_t ret = network_prov_mgr_start_provisioning(NETWORK_PROV_SECURITY_1, + (const void *)pop, service_name, service_key); if(ret != ESP_OK) { + wifi_is_provisioning = false; return ret; } wifi_prov_print_url(service_name, username, pop); - wifi_is_provisioning = true; - wifi_is_connected = false; - xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_EVENT); return ESP_OK; } @@ -214,6 +228,13 @@ static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_ ESP_ERROR_CHECK(network_prov_mgr_deinit()); wifi_mgr_initialized = false; wifi_is_provisioning = false; + if(wifi_ps_overridden_for_prov) { + esp_err_t ps_ret = esp_wifi_set_ps(WIFI_PS_MIN_MODEM); + if(ps_ret != ESP_OK) { + ESP_LOGW(TAG, "Failed to restore Wi-Fi power save after provisioning (error: %d)", ps_ret); + } + wifi_ps_overridden_for_prov = false; + } break; default: break; @@ -221,13 +242,19 @@ static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_ } else if(event_base == WIFI_EVENT) { switch(event_id) { case WIFI_EVENT_STA_START: - esp_wifi_connect(); + if(!wifi_is_provisioning) { + esp_wifi_connect(); + } break; case WIFI_EVENT_STA_DISCONNECTED: - ESP_LOGI(TAG, "Wi-Fi disconnected, retrying..."); wifi_is_connected = false; xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_EVENT); - esp_wifi_connect(); + if(wifi_is_provisioning) { + ESP_LOGI(TAG, "Wi-Fi STA disconnected during provisioning; reconnect suppressed"); + } else { + ESP_LOGI(TAG, "Wi-Fi disconnected, retrying..."); + esp_wifi_connect(); + } break; case WIFI_EVENT_AP_STACONNECTED: ESP_LOGI(TAG, "Provisioning SoftAP client connected"); @@ -235,6 +262,15 @@ static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_ case WIFI_EVENT_AP_STADISCONNECTED: ESP_LOGI(TAG, "Provisioning SoftAP client disconnected"); break; + case WIFI_EVENT_SCAN_DONE: { + const wifi_event_sta_scan_done_t *scan = (const wifi_event_sta_scan_done_t *)event_data; + if(scan != NULL) { + ESP_LOGI(TAG, "Provisioning scan done (status: %u, ap_num: %u)", + (unsigned int)scan->status, + (unsigned int)scan->number); + } + break; + } default: break; }