From 4c76af3f68cb54e71b1f8f025571ff25b62cb5b8 Mon Sep 17 00:00:00 2001 From: Nachiket Kukade Date: Tue, 14 Feb 2023 10:06:40 +0530 Subject: [PATCH] esp_wifi: Add support for NAN Discovery and Datapath Update wifi lib with below - 1. Create NAN Discovery SM for beaconing & cluster formation 2. Create NAN interface for Tx/Rx of beacons & action frames 3. Add commands & events for NAN Services Publish/Subscribe/Followup 4. Add NAN Datapath definitions, Events, Peer structures 5. Support for forming and parsing of Datapath related attributes 6. Modules for NDP Req, Resp, Confirm, Term, Peer management 7. NAN Interface related additions in Datapath, Data Tx Q's In addition include below changes - 1. Add netif and driver support for NAN Interface 2. Add simple examples for Publisher-Subscriber usecases 3. Add an advanced console example that supports commands for NAN Discovery, Services & Datapath 4. Add wifi_apps for providing better NAN API's and Peer management Co-authored-by: Shyamal Khachane --- .../esp_hw_support/include/esp_interface.h | 7 +- components/esp_netif/esp_netif_defaults.c | 6 +- .../esp_netif/include/esp_netif_defaults.h | 40 + .../include/lwip/esp_netif_net_stack.h | 9 +- .../esp_netif/lwip/esp_netif_lwip_defaults.c | 7 + components/esp_netif/lwip/netif/wlanif.c | 6 + components/esp_wifi/CMakeLists.txt | 7 +- components/esp_wifi/Kconfig | 8 +- .../esp_wifi/include/esp_private/wifi.h | 84 ++ components/esp_wifi/include/esp_wifi.h | 23 +- .../esp_wifi/include/esp_wifi_crypto_types.h | 22 + .../esp_wifi/include/esp_wifi_default.h | 18 + components/esp_wifi/include/esp_wifi_netif.h | 2 +- components/esp_wifi/include/esp_wifi_types.h | 200 +++- components/esp_wifi/lib | 2 +- components/esp_wifi/src/wifi_default.c | 82 +- components/esp_wifi/src/wifi_init.c | 39 + components/esp_wifi/src/wifi_netif.c | 15 +- .../include/apps_private/wifi_apps_private.h | 56 + .../esp_wifi/wifi_apps/include/esp_nan.h | 202 ++++ components/esp_wifi/wifi_apps/src/nan_app.c | 1036 +++++++++++++++++ .../soc/esp32/include/soc/Kconfig.soc_caps.in | 4 + components/soc/esp32/include/soc/soc_caps.h | 1 + .../esp32s2/include/soc/Kconfig.soc_caps.in | 4 + components/soc/esp32s2/include/soc/soc_caps.h | 1 + .../include/esp_supplicant_utils.h | 31 + .../esp_supplicant/src/esp_common.c | 10 + components/wpa_supplicant/port/include/os.h | 6 + .../wpa_supplicant/src/crypto/crypto_ops.c | 8 + docs/doxygen/Doxyfile | 2 + docs/en/api-reference/network/esp_nan.rst | 20 + docs/en/api-reference/network/index.rst | 1 + docs/zh_CN/api-reference/network/esp_nan.rst | 1 + docs/zh_CN/api-reference/network/index.rst | 1 + examples/wifi/.build-test-rules.yml | 5 + .../wifi_aware/nan_console/CMakeLists.txt | 8 + .../wifi/wifi_aware/nan_console/README.md | 187 +++ .../nan_console/main/CMakeLists.txt | 2 + .../wifi_aware/nan_console/main/nan_main.c | 621 ++++++++++ .../wifi_aware/nan_console/sdkconfig.defaults | 1 + .../wifi_aware/nan_publisher/CMakeLists.txt | 6 + .../wifi/wifi_aware/nan_publisher/README.md | 55 + .../nan_publisher/main/CMakeLists.txt | 2 + .../nan_publisher/main/Kconfig.projbuild | 26 + .../nan_publisher/main/publisher_main.c | 119 ++ .../nan_publisher/sdkconfig.defaults | 1 + .../wifi_aware/nan_subscriber/CMakeLists.txt | 6 + .../wifi/wifi_aware/nan_subscriber/README.md | 65 ++ .../nan_subscriber/main/CMakeLists.txt | 2 + .../nan_subscriber/main/Kconfig.projbuild | 36 + .../nan_subscriber/main/subscriber_main.c | 240 ++++ .../nan_subscriber/sdkconfig.defaults | 1 + tools/ci/check_copyright_ignore.txt | 1 - 53 files changed, 3302 insertions(+), 43 deletions(-) create mode 100644 components/esp_wifi/wifi_apps/include/apps_private/wifi_apps_private.h create mode 100644 components/esp_wifi/wifi_apps/include/esp_nan.h create mode 100644 components/esp_wifi/wifi_apps/src/nan_app.c create mode 100644 components/wpa_supplicant/esp_supplicant/include/esp_supplicant_utils.h create mode 100644 docs/en/api-reference/network/esp_nan.rst create mode 100644 docs/zh_CN/api-reference/network/esp_nan.rst create mode 100644 examples/wifi/wifi_aware/nan_console/CMakeLists.txt create mode 100644 examples/wifi/wifi_aware/nan_console/README.md create mode 100644 examples/wifi/wifi_aware/nan_console/main/CMakeLists.txt create mode 100644 examples/wifi/wifi_aware/nan_console/main/nan_main.c create mode 100644 examples/wifi/wifi_aware/nan_console/sdkconfig.defaults create mode 100644 examples/wifi/wifi_aware/nan_publisher/CMakeLists.txt create mode 100644 examples/wifi/wifi_aware/nan_publisher/README.md create mode 100644 examples/wifi/wifi_aware/nan_publisher/main/CMakeLists.txt create mode 100644 examples/wifi/wifi_aware/nan_publisher/main/Kconfig.projbuild create mode 100644 examples/wifi/wifi_aware/nan_publisher/main/publisher_main.c create mode 100644 examples/wifi/wifi_aware/nan_publisher/sdkconfig.defaults create mode 100644 examples/wifi/wifi_aware/nan_subscriber/CMakeLists.txt create mode 100644 examples/wifi/wifi_aware/nan_subscriber/README.md create mode 100644 examples/wifi/wifi_aware/nan_subscriber/main/CMakeLists.txt create mode 100644 examples/wifi/wifi_aware/nan_subscriber/main/Kconfig.projbuild create mode 100644 examples/wifi/wifi_aware/nan_subscriber/main/subscriber_main.c create mode 100644 examples/wifi/wifi_aware/nan_subscriber/sdkconfig.defaults diff --git a/components/esp_hw_support/include/esp_interface.h b/components/esp_hw_support/include/esp_interface.h index fdb4c0056c..278825bee0 100644 --- a/components/esp_hw_support/include/esp_interface.h +++ b/components/esp_hw_support/include/esp_interface.h @@ -15,9 +15,10 @@ extern "C" { #endif typedef enum { - ESP_IF_WIFI_STA = 0, /**< ESP32 station interface */ - ESP_IF_WIFI_AP, /**< ESP32 soft-AP interface */ - ESP_IF_ETH, /**< ESP32 ethernet interface */ + ESP_IF_WIFI_STA = 0, /**< Station interface */ + ESP_IF_WIFI_AP, /**< Soft-AP interface */ + ESP_IF_WIFI_NAN, /**< NAN interface */ + ESP_IF_ETH, /**< Ethernet interface */ ESP_IF_MAX } esp_interface_t; diff --git a/components/esp_netif/esp_netif_defaults.c b/components/esp_netif/esp_netif_defaults.c index 7031d63e11..e98105b1ae 100644 --- a/components/esp_netif/esp_netif_defaults.c +++ b/components/esp_netif/esp_netif_defaults.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -28,6 +28,10 @@ const esp_netif_ip_info_t _g_esp_netif_soft_ap_ip = { const esp_netif_inherent_config_t _g_esp_netif_inherent_ap_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_AP(); #endif +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +const esp_netif_inherent_config_t _g_esp_netif_inherent_nan_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_NAN(); +#endif + const esp_netif_inherent_config_t _g_esp_netif_inherent_eth_config = ESP_NETIF_INHERENT_DEFAULT_ETH(); #ifdef CONFIG_PPP_SUPPORT diff --git a/components/esp_netif/include/esp_netif_defaults.h b/components/esp_netif/include/esp_netif_defaults.h index 3dbfbedb07..46cf10eeb4 100644 --- a/components/esp_netif/include/esp_netif_defaults.h +++ b/components/esp_netif/include/esp_netif_defaults.h @@ -64,6 +64,18 @@ extern "C" { } #endif +#define ESP_NETIF_INHERENT_DEFAULT_WIFI_NAN() \ + { \ + .flags = 0, \ + ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(mac) \ + ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(ip_info) \ + .get_ip_event = 0, \ + .lost_ip_event = 0, \ + .if_key = "WIFI_NAN_DEF", \ + .if_desc = "nan", \ + .route_prio = 10 \ + }; + #define ESP_NETIF_INHERENT_DEFAULT_ETH() \ { \ .flags = (esp_netif_flags_t)(ESP_NETIF_IPV4_ONLY_FLAGS(ESP_NETIF_DHCP_CLIENT) | ESP_NETIF_DEFAULT_ARP_FLAGS | ESP_NETIF_FLAG_EVENT_IP_MODIFIED), \ @@ -130,6 +142,18 @@ extern "C" { } #endif +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +/** +* @brief Default configuration reference of WIFI NAN +*/ +#define ESP_NETIF_DEFAULT_WIFI_NAN() \ + { \ + .base = ESP_NETIF_BASE_DEFAULT_WIFI_NAN, \ + .driver = NULL, \ + .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_NAN, \ + } +#endif + /** * @brief Default configuration reference of WIFI STA */ @@ -164,6 +188,13 @@ extern "C" { #define ESP_NETIF_BASE_DEFAULT_WIFI_AP &_g_esp_netif_inherent_ap_config #endif +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +/** + * @brief Default base config (esp-netif inherent) of WIFI NAN + */ +#define ESP_NETIF_BASE_DEFAULT_WIFI_NAN &_g_esp_netif_inherent_nan_config +#endif + /** * @brief Default base config (esp-netif inherent) of ethernet interface */ @@ -183,6 +214,9 @@ extern "C" { #ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT #define ESP_NETIF_NETSTACK_DEFAULT_WIFI_AP _g_esp_netif_netstack_default_wifi_ap #endif +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#define ESP_NETIF_NETSTACK_DEFAULT_WIFI_NAN _g_esp_netif_netstack_default_wifi_nan +#endif #ifdef CONFIG_PPP_SUPPORT #define ESP_NETIF_NETSTACK_DEFAULT_PPP _g_esp_netif_netstack_default_ppp #endif @@ -199,6 +233,9 @@ extern const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_wifi_sta #ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT extern const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_wifi_ap; #endif +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +extern const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_wifi_nan; +#endif #ifdef CONFIG_PPP_SUPPORT extern const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_ppp; #endif @@ -211,6 +248,9 @@ extern const esp_netif_inherent_config_t _g_esp_netif_inherent_sta_config; #ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT extern const esp_netif_inherent_config_t _g_esp_netif_inherent_ap_config; #endif +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +extern const esp_netif_inherent_config_t _g_esp_netif_inherent_nan_config; +#endif extern const esp_netif_inherent_config_t _g_esp_netif_inherent_eth_config; #ifdef CONFIG_PPP_SUPPORT extern const esp_netif_inherent_config_t _g_esp_netif_inherent_ppp_config; diff --git a/components/esp_netif/include/lwip/esp_netif_net_stack.h b/components/esp_netif/include/lwip/esp_netif_net_stack.h index 0550b82e87..11aac99ae9 100644 --- a/components/esp_netif/include/lwip/esp_netif_net_stack.h +++ b/components/esp_netif/include/lwip/esp_netif_net_stack.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -65,6 +65,13 @@ err_t wlanif_init_ap(struct netif *netif); */ err_t wlanif_init_sta(struct netif *netif); +/** + * @brief LWIP's network stack init function for WiFi Aware interface (NAN) + * @param netif LWIP's network interface handle + * @return ERR_OK on success + */ +err_t wlanif_init_nan(struct netif *netif); + /** * @brief LWIP's network stack input packet function for WiFi (both STA/AP) * @param h LWIP's network interface handle diff --git a/components/esp_netif/lwip/esp_netif_lwip_defaults.c b/components/esp_netif/lwip/esp_netif_lwip_defaults.c index 444ca0fee9..936251ec6a 100644 --- a/components/esp_netif/lwip/esp_netif_lwip_defaults.c +++ b/components/esp_netif/lwip/esp_netif_lwip_defaults.c @@ -48,6 +48,12 @@ static const struct esp_netif_netstack_config s_wifi_netif_config_sta = { .input_fn = wlanif_input } }; +static const struct esp_netif_netstack_config s_wifi_netif_config_nan = { + .lwip = { + .init_fn = wlanif_init_nan, + .input_fn = wlanif_input + } +}; #if defined(CONFIG_PPP_SUPPORT) static const struct esp_netif_netstack_config s_netif_config_ppp = { @@ -64,6 +70,7 @@ const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_ppp = &s_n const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_eth = &s_eth_netif_config; const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_wifi_sta = &s_wifi_netif_config_sta; +const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_wifi_nan = &s_wifi_netif_config_nan; const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_wifi_ap = &s_wifi_netif_config_ap; #endif /*CONFIG_ESP_NETIF_TCPIP_LWIP*/ diff --git a/components/esp_netif/lwip/netif/wlanif.c b/components/esp_netif/lwip/netif/wlanif.c index 17337fe23d..fda9bee3c8 100644 --- a/components/esp_netif/lwip/netif/wlanif.c +++ b/components/esp_netif/lwip/netif/wlanif.c @@ -230,3 +230,9 @@ err_t wlanif_init_ap(struct netif *netif) { netif->name[1] = 'p'; return wlanif_init(netif); } + +err_t wlanif_init_nan(struct netif *netif) { + netif->name[0] = 'n'; + netif->name[1] = 'a'; + return wlanif_init(netif); +} diff --git a/components/esp_wifi/CMakeLists.txt b/components/esp_wifi/CMakeLists.txt index c3343a6647..d853898a6d 100644 --- a/components/esp_wifi/CMakeLists.txt +++ b/components/esp_wifi/CMakeLists.txt @@ -29,10 +29,15 @@ if(CONFIG_ESP_WIFI_ENABLED) list(APPEND srcs "src/smartconfig_ack.c") endif() + + if(CONFIG_ESP_WIFI_NAN_ENABLE) + list(APPEND srcs "wifi_apps/src/nan_app.c") + endif() + endif() idf_component_register(SRCS "${srcs}" - INCLUDE_DIRS "include" + INCLUDE_DIRS "include" "wifi_apps/include" REQUIRES esp_event esp_phy esp_netif PRIV_REQUIRES driver esptool_py esp_pm esp_timer nvs_flash wpa_supplicant hal lwip esp_coex ${extra_priv_requires} diff --git a/components/esp_wifi/Kconfig b/components/esp_wifi/Kconfig index 61988a83c3..23d9dda6a0 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -397,9 +397,15 @@ menu "Wi-Fi" The number of hardware keys for encryption is fixed. And the espnow and SoftAP share the same hardware keys. So this configuration will affect the maximum connection number of SoftAP. Maximum espnow encrypted peers number + maximum number of connections of SoftAP = Max hardware keys number. - When using ESP mesh, this value should be set to a maximum of 6. + config ESP_WIFI_NAN_ENABLE + bool "WiFi Aware" + default n + depends on SOC_WIFI_NAN_SUPPORT + help + Enable WiFi Aware (NAN) feature. + config ESP_WIFI_ENABLE_WIFI_TX_STATS bool "Enable Wi-Fi transmission statistics" depends on SOC_WIFI_HE_SUPPORT diff --git a/components/esp_wifi/include/esp_private/wifi.h b/components/esp_wifi/include/esp_private/wifi.h index 90edc7e3b3..db206d0796 100644 --- a/components/esp_wifi/include/esp_private/wifi.h +++ b/components/esp_wifi/include/esp_private/wifi.h @@ -639,6 +639,90 @@ void esp_wifi_beacon_monitor_configure(wifi_beacon_monitor_config_t *config); */ void esp_wifi_internal_mac_sleep_configure(bool light_sleep_enable, bool modem_state_enable); +/** + * @brief Start Publishing a service in the NAN cluster + * + * @attention This API should be called after esp_wifi_start() in NAN Mode. + * + * @param publish_cfg Configuration parameters for publishing a service. + * @param id Identifier for the Publish service. + * @param cancel Cancel the service identified by the id. + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_nan_internal_publish_service(const wifi_nan_publish_cfg_t *publish_cfg, + uint8_t *id, bool cancel); + +/** + * @brief Subscribe for a service within the NAN cluster + * + * @attention This API should be called after esp_wifi_start() in NAN Mode. + * + * @param subscribe_cfg Configuration parameters for subscribing for a service. + * @param id Identifier for the Subscribe service. + * @param cancel Cancel the service identified by the id. + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_nan_internal_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe_cfg, + uint8_t *id, bool cancel); + +/** + * @brief Send Follow-up to the Publisher with matching service + * + * @attention This API should be called after WIFI_EVENT_NAN_SVC_MATCH event is received. + * + * @param fup_params Configuration parameters for sending a Follow-up to the Peer. + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_nan_internal_send_followup(const wifi_nan_followup_params_t *fup_params); + +/** + * @brief Send Datapath Request to the Publisher with matching service + * + * @attention This API should be called after WIFI_EVENT_NAN_SVC_MATCH event is received. + * + * @param req NAN Datapath Request parameters. + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_nan_internal_datapath_req(wifi_nan_datapath_req_t *req, uint8_t *ndp_id); + +/** + * @brief Send Datapath Response to accept or reject the received request + * + * @attention This API should be called on the Publisher after receiving WIFI_EVENT_NDP_INDICATION event. + * + * @param resp NAN Datapath Response parameters. + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_nan_internal_datapath_resp(wifi_nan_datapath_resp_t *resp); + +/** + * @brief End NAN Datapath that is active + * + * @attention This API should be called after receiving WIFI_EVENT_NDP_CONFIRM event. + * + * @param req NAN Datapath end request parameters. + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_nan_internal_datapath_end(wifi_nan_datapath_end_req_t *req); + #ifdef __cplusplus } #endif diff --git a/components/esp_wifi/include/esp_wifi.h b/components/esp_wifi/include/esp_wifi.h index 3bf4f760a9..863ebb7d70 100644 --- a/components/esp_wifi/include/esp_wifi.h +++ b/components/esp_wifi/include/esp_wifi.h @@ -268,7 +268,7 @@ esp_err_t esp_wifi_deinit(void); /** * @brief Set the WiFi operating mode * - * Set the WiFi operating mode as station, soft-AP or station+soft-AP, + * Set the WiFi operating mode as station, soft-AP, station+soft-AP or NAN. * The default mode is station mode. * * @param mode WiFi operating mode @@ -295,9 +295,10 @@ esp_err_t esp_wifi_get_mode(wifi_mode_t *mode); /** * @brief Start WiFi according to current configuration - * If mode is WIFI_MODE_STA, it create station control block and start station - * If mode is WIFI_MODE_AP, it create soft-AP control block and start soft-AP - * If mode is WIFI_MODE_APSTA, it create soft-AP and station control block and start soft-AP and station + * If mode is WIFI_MODE_STA, it creates station control block and starts station + * If mode is WIFI_MODE_AP, it creates soft-AP control block and starts soft-AP + * If mode is WIFI_MODE_APSTA, it creates soft-AP and station control block and starts soft-AP and station + * If mode is WIFI_MODE_NAN, it creates NAN control block and starts NAN * * @return * - ESP_OK: succeed @@ -311,9 +312,10 @@ esp_err_t esp_wifi_start(void); /** * @brief Stop WiFi - * If mode is WIFI_MODE_STA, it stop station and free station control block - * If mode is WIFI_MODE_AP, it stop soft-AP and free soft-AP control block - * If mode is WIFI_MODE_APSTA, it stop station/soft-AP and free station/soft-AP control block + * If mode is WIFI_MODE_STA, it stops station and frees station control block + * If mode is WIFI_MODE_AP, it stops soft-AP and frees soft-AP control block + * If mode is WIFI_MODE_APSTA, it stops station/soft-AP and frees station/soft-AP control block + * If mode is WIFI_MODE_NAN, it stops NAN and frees NAN control block * * @return * - ESP_OK: succeed @@ -790,16 +792,16 @@ esp_err_t esp_wifi_set_promiscuous_ctrl_filter(const wifi_promiscuous_filter_t * esp_err_t esp_wifi_get_promiscuous_ctrl_filter(wifi_promiscuous_filter_t *filter); /** - * @brief Set the configuration of the STA or AP + * @brief Set the configuration of the STA, AP or NAN * * @attention 1. This API can be called only when specified interface is enabled, otherwise, API fail * @attention 2. For station configuration, bssid_set needs to be 0; and it needs to be 1 only when users need to check the MAC address of the AP. * @attention 3. ESP devices are limited to only one channel, so when in the soft-AP+station mode, the soft-AP will adjust its channel automatically to be the same as * the channel of the station. - * @attention 4. The configuration will be stored in NVS + * @attention 4. The configuration will be stored in NVS for station and soft-AP * * @param interface interface - * @param conf station or soft-AP configuration + * @param conf station, soft-AP or NAN configuration * * @return * - ESP_OK: succeed @@ -1345,6 +1347,7 @@ esp_err_t esp_wifi_sta_get_aid(uint16_t *aid); * - ESP_OK: succeed */ esp_err_t esp_wifi_sta_get_negotiated_phymode(wifi_phy_mode_t *phymode); + #ifdef __cplusplus } #endif diff --git a/components/esp_wifi/include/esp_wifi_crypto_types.h b/components/esp_wifi/include/esp_wifi_crypto_types.h index 6fbf3dbcfe..9eded75a76 100644 --- a/components/esp_wifi/include/esp_wifi_crypto_types.h +++ b/components/esp_wifi/include/esp_wifi_crypto_types.h @@ -365,6 +365,26 @@ typedef uint8_t * (*esp_ccmp_encrypt_t)(const uint8_t *tk, uint8_t *frame, size_ typedef int (*esp_aes_gmac_t)(const uint8_t *key, size_t keylen, const uint8_t *iv, size_t iv_len, const uint8_t *aad, size_t aad_len, uint8_t *mic); +/** + * @brief SHA256 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +typedef int (*esp_sha256_vector_t)(size_t num_elem, const uint8_t *addr[], const size_t *len, uint8_t *buf); + +/** + * @brief CRC32 value in little endian. + * + * @param crc: Initial CRC value (result of last calculation or 0 for the first time) + * @param buf: Data buffer that used to calculate the CRC value + * @param len: Length of the data buffer + * @return CRC32 value + */ +typedef uint32_t (*esp_crc32_le_t)(uint32_t crc, uint8_t const *buf, uint32_t len); + /** * @brief The crypto callback function structure used when do station security connect. * The structure can be set as software crypto or the crypto optimized by device's @@ -398,6 +418,8 @@ typedef struct { esp_ccmp_decrypt_t ccmp_decrypt; esp_ccmp_encrypt_t ccmp_encrypt; esp_aes_gmac_t aes_gmac; + esp_sha256_vector_t sha256_vector; + esp_crc32_le_t crc32; }wpa_crypto_funcs_t; /** diff --git a/components/esp_wifi/include/esp_wifi_default.h b/components/esp_wifi/include/esp_wifi_default.h index f5a1e3382c..b1e1c24d91 100644 --- a/components/esp_wifi/include/esp_wifi_default.h +++ b/components/esp_wifi/include/esp_wifi_default.h @@ -51,6 +51,14 @@ esp_err_t esp_wifi_set_default_wifi_sta_handlers(void); */ esp_err_t esp_wifi_set_default_wifi_ap_handlers(void); +/** + * @brief Sets default wifi event handlers for NAN interface + * + * @return + * - ESP_OK on success, error returned from esp_event_handler_register if failed + */ +esp_err_t esp_wifi_set_default_wifi_nan_handlers(void); + /** * @brief Clears default wifi event handlers for supplied network interface * @@ -81,6 +89,16 @@ esp_netif_t* esp_netif_create_default_wifi_ap(void); */ esp_netif_t* esp_netif_create_default_wifi_sta(void); +/** + * @brief Creates default WIFI NAN. In case of any init error this API aborts. + * + * @note The API creates esp_netif object with default WiFi station config, + * attaches the netif to wifi and registers default wifi handlers. + * + * @return pointer to esp-netif instance + */ +esp_netif_t* esp_netif_create_default_wifi_nan(void); + /** * @brief Destroys default WIFI netif created with esp_netif_create_default_wifi_...() API. * diff --git a/components/esp_wifi/include/esp_wifi_netif.h b/components/esp_wifi/include/esp_wifi_netif.h index d5a5d285ab..c25c52edd9 100644 --- a/components/esp_wifi/include/esp_wifi_netif.h +++ b/components/esp_wifi/include/esp_wifi_netif.h @@ -13,7 +13,7 @@ extern "C" { /** * @brief Number of WiFi interfaces used by wifi-netif abstraction */ -#define MAX_WIFI_IFS (2) +#define MAX_WIFI_IFS WIFI_IF_MAX /** * @brief Forward declaration of WiFi interface handle diff --git a/components/esp_wifi/include/esp_wifi_types.h b/components/esp_wifi/include/esp_wifi_types.h index c815a9fe0e..ac12c34b49 100644 --- a/components/esp_wifi/include/esp_wifi_types.h +++ b/components/esp_wifi/include/esp_wifi_types.h @@ -22,12 +22,17 @@ typedef enum { WIFI_MODE_STA, /**< WiFi station mode */ WIFI_MODE_AP, /**< WiFi soft-AP mode */ WIFI_MODE_APSTA, /**< WiFi station + soft-AP mode */ + WIFI_MODE_NAN, /**< WiFi NAN mode */ WIFI_MODE_MAX } wifi_mode_t; typedef enum { WIFI_IF_STA = ESP_IF_WIFI_STA, WIFI_IF_AP = ESP_IF_WIFI_AP, +#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) + WIFI_IF_NAN = ESP_IF_WIFI_NAN, +#endif + WIFI_IF_MAX } wifi_interface_t; #define WIFI_OFFCHAN_TX_REQ 1 @@ -326,15 +331,27 @@ typedef struct { uint8_t sae_h2e_identifier[SAE_H2E_IDENTIFIER_LEN];/**< Password identifier for H2E. this needs to be null terminated string */ } wifi_sta_config_t; -/** @brief Configuration data for device's AP or STA. +/** + * @brief NAN Discovery start configuration + * + */ +typedef struct { + uint8_t op_channel; /**< NAN Discovery operating channel */ + uint8_t master_pref; /**< Device's preference value to serve as NAN Master */ + uint8_t scan_time; /**< Scan time in seconds while searching for a NAN cluster */ + uint16_t warm_up_sec; /**< Warm up time before assuming NAN Anchor Master role */ +} wifi_nan_config_t; + +/** @brief Configuration data for device's AP or STA or NAN. * - * The usage of this union (for ap or sta configuration) is determined by the accompanying + * The usage of this union (for ap, sta or nan configuration) is determined by the accompanying * interface argument passed to esp_wifi_set_config() or esp_wifi_get_config() * */ typedef union { wifi_ap_config_t ap; /**< configuration of AP */ wifi_sta_config_t sta; /**< configuration of STA */ + wifi_nan_config_t nan; /**< configuration of NAN */ } wifi_config_t; /** @brief Description of STA associated with AP */ @@ -632,6 +649,115 @@ typedef struct { uint16_t burst_period; /**< Requested time period between consecutive FTM bursts in 100's of milliseconds (0 - No pref) */ } wifi_ftm_initiator_cfg_t; +/** + * @brief WiFi beacon monitor parameter configuration + * + */ +typedef struct { + bool enable; /**< Enable or disable beacon monitor */ + uint8_t loss_timeout; /**< Beacon lost timeout */ + uint8_t loss_threshold; /**< Maximum number of consecutive lost beacons allowed */ + uint8_t delta_intr_early; /**< Delta early time for RF PHY on */ + uint8_t delta_loss_timeout; /**< Delta timeout time for RF PHY off */ +#if MAC_SUPPORT_PMU_MODEM_STATE + uint8_t beacon_abort: 1, /**< Enable or disable beacon abort */ + broadcast_wakeup: 1, /**< Enable or disable TIM element multicast wakeup */ + reserved: 6; /**< Reserved */ + uint8_t tsf_time_sync_deviation; /**< Deviation range to sync with AP TSF timestamp */ + uint16_t modem_state_consecutive; /**< PMU MODEM state consecutive count limit */ + uint16_t rf_ctrl_wait_cycle; /**< RF on wait time (unit: Modem APB clock cycle) */ +#endif +} wifi_beacon_monitor_config_t; + +#define ESP_WIFI_NAN_MAX_SVC_SUPPORTED 2 +#define ESP_WIFI_NAN_DATAPATH_MAX_PEERS 2 + +#define ESP_WIFI_NDP_ROLE_INITIATOR 1 +#define ESP_WIFI_NDP_ROLE_RESPONDER 2 + +#define ESP_WIFI_MAX_SVC_NAME_LEN 256 +#define ESP_WIFI_MAX_FILTER_LEN 256 +#define ESP_WIFI_MAX_SVC_INFO_LEN 64 + +/** + * @brief NAN Services types + * + */ +typedef enum { + NAN_PUBLISH_SOLICITED, /**< Send unicast Publish frame to Subscribers that match the requirement */ + NAN_PUBLISH_UNSOLICITED,/**< Send broadcast Publish frames in every Discovery Window(DW) */ + NAN_SUBSCRIBE_ACTIVE, /**< Send broadcast Subscribe frames in every DW */ + NAN_SUBSCRIBE_PASSIVE, /**< Passively listens to Publish frames */ +} wifi_nan_service_type_t; + +/** + * @brief NAN Publish service configuration parameters + * + */ +typedef struct { + char service_name[ESP_WIFI_MAX_SVC_NAME_LEN]; /**< Service name identifier */ + wifi_nan_service_type_t type; /**< Service type */ + char matching_filter[ESP_WIFI_MAX_FILTER_LEN]; /**< Comma separated filters for filtering services */ + char svc_info[ESP_WIFI_MAX_SVC_INFO_LEN]; /**< Service info shared in Publish frame */ + uint8_t single_replied_event:1; /**< Give single Replied event or every time */ + uint8_t datapath_reqd:1; /**< NAN Datapath required for the service */ + uint8_t fsd_reqd:1; /**< Further Service Discovery required */ + uint8_t reserved:5; /**< Reserved */ +} wifi_nan_publish_cfg_t; + +/** + * @brief NAN Subscribe service configuration parameters + * + */ +typedef struct { + char service_name[ESP_WIFI_MAX_SVC_NAME_LEN]; /**< Service name identifier */ + wifi_nan_service_type_t type; /**< Service type */ + char matching_filter[ESP_WIFI_MAX_FILTER_LEN]; /**< Comma separated filters for filtering services */ + char svc_info[ESP_WIFI_MAX_SVC_INFO_LEN]; /**< Service info shared in Subscribe frame */ + uint8_t single_match_event:1; /**< Give single Match event or every time */ + uint8_t reserved:7; /**< Reserved */ +} wifi_nan_subscribe_cfg_t; + +/** + * @brief NAN Follow-up parameters + * + */ +typedef struct { + uint8_t inst_id; /**< Own service instance id */ + uint8_t peer_inst_id; /**< Peer's service instance id */ + uint8_t peer_mac[6]; /**< Peer's MAC address */ + char svc_info[ESP_WIFI_MAX_SVC_INFO_LEN];/**< Service info(or message) to be shared */ +} wifi_nan_followup_params_t; + +/** + * @brief NAN Datapath Request parameters + * + */ +typedef struct { + uint8_t pub_id; /**< Publisher's service instance id */ + uint8_t peer_mac[6]; /**< Peer's MAC address */ + bool confirm_required; /**< NDP Confirm frame required */ +} wifi_nan_datapath_req_t; + +/** + * @brief NAN Datapath Response parameters + * + */ +typedef struct { + bool accept; /**< True - Accept incoming NDP, False - Reject it */ + uint8_t ndp_id; /**< NAN Datapath Identifier */ + uint8_t peer_mac[6]; /**< Peer's MAC address */ +} wifi_nan_datapath_resp_t; + +/** + * @brief NAN Datapath End parameters + * + */ +typedef struct { + uint8_t ndp_id; /**< NAN Datapath Identifier */ + uint8_t peer_mac[6]; /**< Peer's MAC address */ +} wifi_nan_datapath_end_req_t; + /** * @brief WiFi PHY rate encodings * @@ -726,6 +852,15 @@ typedef enum { WIFI_EVENT_ITWT_PROBE, /**< iTWT probe */ WIFI_EVENT_ITWT_SUSPEND, /**< iTWT suspend */ + WIFI_EVENT_NAN_STARTED, /**< NAN Discovery has started */ + WIFI_EVENT_NAN_STOPPED, /**< NAN Discovery has stopped */ + WIFI_EVENT_NAN_SVC_MATCH, /**< NAN Service Discovery match found */ + WIFI_EVENT_NAN_REPLIED, /**< Replied to a NAN peer with Service Discovery match */ + WIFI_EVENT_NAN_RECEIVE, /**< Received a Follow-up message */ + WIFI_EVENT_NDP_INDICATION, /**< Received NDP Request from a NAN Peer */ + WIFI_EVENT_NDP_CONFIRM, /**< NDP Confirm Indication */ + WIFI_EVENT_NDP_TERMINATED, /**< NAN Datapath terminated indication */ + WIFI_EVENT_MAX, /**< Invalid WiFi event ID */ } wifi_event_t; @@ -893,22 +1028,53 @@ typedef struct { uint8_t peer_macaddr[6]; /**< Enrollee mac address */ } wifi_event_ap_wps_rg_success_t; -/** WiFi beacon monitor parameter configuration */ +/** Argument structure for WIFI_EVENT_NAN_SVC_MATCH event */ typedef struct { - bool enable; /**< Enable or disable beacon monitor */ - uint8_t loss_timeout; /**< Beacon lost timeout */ - uint8_t loss_threshold; /**< Maximum number of consecutive lost beacons allowed */ - uint8_t delta_intr_early; /**< Delta early time for RF PHY on */ - uint8_t delta_loss_timeout; /**< Delta timeout time for RF PHY off */ -#if MAC_SUPPORT_PMU_MODEM_STATE - uint8_t beacon_abort: 1, /**< Enable or disable beacon abort */ - broadcast_wakeup: 1, /**< Enable or disable TIM element multicast wakeup */ - reserved: 6; /**< Reserved */ - uint8_t tsf_time_sync_deviation; /**< Deviation range to sync with AP TSF timestamp */ - uint16_t modem_state_consecutive; /**< PMU MODEM state consecutive count limit */ - uint16_t rf_ctrl_wait_cycle; /**< RF on wait time (unit: Modem APB clock cycle) */ -#endif -} wifi_beacon_monitor_config_t; + uint8_t subscribe_id; /**< Subscribe Service Identifier */ + uint8_t publish_id; /**< Publish Service Identifier */ + uint8_t pub_if_mac[6]; /**< NAN Interface MAC of the Publisher */ +} wifi_event_nan_svc_match_t; + +/** Argument structure for WIFI_EVENT_NAN_REPLIED event */ +typedef struct { + uint8_t publish_id; /**< Publish Service Identifier */ + uint8_t subscribe_id; /**< Subscribe Service Identifier */ + uint8_t sub_if_mac[6]; /**< NAN Interface MAC of the Subscriber */ +} wifi_event_nan_replied_t; + +/** Argument structure for WIFI_EVENT_NAN_RECEIVE event */ +typedef struct { + uint8_t inst_id; /**< Our Service Identifier */ + uint8_t peer_inst_id; /**< Peer's Service Identifier */ + uint8_t peer_if_mac[6]; /**< Peer's NAN Interface MAC */ + uint8_t peer_svc_info[ESP_WIFI_MAX_SVC_INFO_LEN];/**< Peer Service Info */ +} wifi_event_nan_receive_t; + +/** Argument structure for WIFI_EVENT_NDP_INDICATION event */ +typedef struct { + uint8_t publish_id; /**< Publish Id for NAN Service */ + uint8_t ndp_id; /**< NDP instance id */ + uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */ + uint8_t peer_ndi[6]; /**< Peer's NAN Data Interface MAC */ + uint8_t svc_info[ESP_WIFI_MAX_SVC_INFO_LEN];/**< Service Specific Info */ +} wifi_event_ndp_indication_t; + +/** Argument structure for WIFI_EVENT_NDP_CONFIRM event */ +typedef struct { + uint8_t status; /**< NDP status code */ + uint8_t ndp_id; /**< NDP instance id */ + uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */ + uint8_t peer_ndi[6]; /**< Peer's NAN Data Interface MAC */ + uint8_t own_ndi[6]; /**< Own NAN Data Interface MAC */ + uint8_t svc_info[ESP_WIFI_MAX_SVC_INFO_LEN];/**< Service Specific Info */ +} wifi_event_ndp_confirm_t; + +/** Argument structure for WIFI_EVENT_NDP_TERMINATED event */ +typedef struct { + uint8_t reason; /**< Termination reason code */ + uint8_t ndp_id; /**< NDP instance id */ + uint8_t init_ndi[6]; /**< Initiator's NAN Data Interface MAC */ +} wifi_event_ndp_terminated_t; #ifdef __cplusplus } diff --git a/components/esp_wifi/lib b/components/esp_wifi/lib index 5ec56cf377..a821d6aef8 160000 --- a/components/esp_wifi/lib +++ b/components/esp_wifi/lib @@ -1 +1 @@ -Subproject commit 5ec56cf3775998a69b7946a5b2318d22f6650a95 +Subproject commit a821d6aef8e9ba26b1003a0fa3276774a2113433 diff --git a/components/esp_wifi/src/wifi_default.c b/components/esp_wifi/src/wifi_default.c index e3c8266136..ee7b906e41 100644 --- a/components/esp_wifi/src/wifi_default.c +++ b/components/esp_wifi/src/wifi_default.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,6 +10,9 @@ #include "esp_private/wifi.h" #include "esp_wifi_netif.h" #include +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#include "apps_private/wifi_apps_private.h" +#endif // // Purpose of this module is to provide basic wifi initialization setup for @@ -131,6 +134,24 @@ static void wifi_default_action_sta_got_ip(void *arg, esp_event_base_t base, int } } +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +static void wifi_default_action_nan_started(void *arg, esp_event_base_t base, int32_t event_id, void *data) +{ + if (s_wifi_netifs[WIFI_IF_NAN] != NULL) { + wifi_start(s_wifi_netifs[WIFI_IF_NAN], base, event_id, data); + esp_nan_action_start(s_wifi_netifs[WIFI_IF_NAN]); + } +} + +static void wifi_default_action_nan_stopped(void *arg, esp_event_base_t base, int32_t event_id, void *data) +{ + if (s_wifi_netifs[WIFI_IF_NAN] != NULL) { + esp_netif_action_stop(s_wifi_netifs[WIFI_IF_NAN], base, event_id, data); + esp_nan_action_stop(); + } +} +#endif + /** * @brief Clear default handlers */ @@ -145,6 +166,10 @@ static esp_err_t clear_default_wifi_handlers(void) esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_AP_STOP, wifi_default_action_ap_stop); #endif esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_default_action_sta_got_ip); +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NAN_STARTED, wifi_default_action_nan_started); + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NAN_STOPPED, wifi_default_action_nan_stopped); +#endif esp_unregister_shutdown_handler((shutdown_handler_t)esp_wifi_stop); wifi_default_handlers_set = false; return ESP_OK; @@ -197,6 +222,18 @@ static esp_err_t set_default_wifi_handlers(void) goto fail; } +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE + err = esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NAN_STARTED, wifi_default_action_nan_started, NULL); + if (err != ESP_OK) { + goto fail; + } + + err = esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NAN_STOPPED, wifi_default_action_nan_stopped, NULL); + if (err != ESP_OK) { + goto fail; + } +#endif + err = esp_register_shutdown_handler((shutdown_handler_t)esp_wifi_stop); if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { goto fail; @@ -225,6 +262,14 @@ esp_err_t esp_wifi_set_default_wifi_ap_handlers(void) return set_default_wifi_handlers(); } +/** + * @brief Set default handlers for NAN (official API) + */ +esp_err_t esp_wifi_set_default_wifi_nan_handlers(void) +{ + return set_default_wifi_handlers(); +} + /** * @brief Clear default handlers and destroy appropriate objects (official API) */ @@ -283,6 +328,9 @@ static inline esp_err_t esp_netif_attach_wifi(esp_netif_t *esp_netif, wifi_inter if (esp_netif == NULL || (wifi_if != WIFI_IF_STA #ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT && wifi_if != WIFI_IF_AP +#endif +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE + && wifi_if != WIFI_IF_NAN #endif )) { return ESP_ERR_INVALID_ARG; @@ -303,6 +351,13 @@ esp_err_t esp_netif_attach_wifi_ap(esp_netif_t *esp_netif) } #endif +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +esp_err_t esp_netif_attach_wifi_nan(esp_netif_t *esp_netif) +{ + return esp_netif_attach_wifi(esp_netif, WIFI_IF_NAN); +} +#endif + // // Default WiFi creation from user code @@ -336,6 +391,21 @@ esp_netif_t* esp_netif_create_default_wifi_sta(void) return netif; } +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +/** + * @brief User init default NAN (official API) + */ +esp_netif_t* esp_netif_create_default_wifi_nan(void) +{ + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_WIFI_NAN(); + esp_netif_t *netif = esp_netif_new(&cfg); + assert(netif); + esp_netif_attach_wifi_nan(netif); + esp_wifi_set_default_wifi_nan_handlers(); + return netif; +} +#endif + /** * @brief User init default wifi esp_netif object (official API) */ @@ -357,13 +427,15 @@ esp_netif_t* esp_netif_create_wifi(wifi_interface_t wifi_if, const esp_netif_inh }; if (wifi_if == WIFI_IF_STA) { cfg.stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA; - } else #ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT - if (wifi_if == WIFI_IF_AP) { + } else if (wifi_if == WIFI_IF_AP) { cfg.stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_AP; - } else #endif - { +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE + } else if (wifi_if == WIFI_IF_NAN) { + cfg.stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_NAN; +#endif + } else { return NULL; } diff --git a/components/esp_wifi/src/wifi_init.c b/components/esp_wifi/src/wifi_init.c index 3118f7384c..c7c7d84c07 100644 --- a/components/esp_wifi/src/wifi_init.c +++ b/components/esp_wifi/src/wifi_init.c @@ -19,6 +19,9 @@ #include "esp_coexist_internal.h" #include "esp_phy_init.h" #include "esp_private/phy.h" +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#include "apps_private/wifi_apps_private.h" +#endif #if (CONFIG_ESP_WIFI_RX_BA_WIN > CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM) #error "WiFi configuration check: WARNING, WIFI_RX_BA_WIN should not be larger than WIFI_DYNAMIC_RX_BUFFER_NUM!" @@ -127,6 +130,10 @@ esp_err_t esp_wifi_deinit(void) ESP_LOGW(TAG, "Failed to unregister Rx callbacks"); } +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE + esp_nan_app_deinit(); +#endif + esp_supplicant_deinit(); err = esp_wifi_deinit_internal(); if (err != ESP_OK) { @@ -305,6 +312,11 @@ esp_err_t esp_wifi_init(const wifi_init_config_t *config) adc2_cal_include(); //This enables the ADC2 calibration constructor at start up. esp_wifi_config_info(); + +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE + esp_nan_app_init(); +#endif + return result; } @@ -337,3 +349,30 @@ void net80211_softap_funcs_init(void) { } #endif + +#ifndef CONFIG_ESP_WIFI_NAN_ENABLE + +esp_err_t nan_start(void) +{ + /* Do not remove, stub to overwrite weak link in Wi-Fi Lib */ + return ESP_OK; +} + +esp_err_t nan_stop(void) +{ + /* Do not remove, stub to overwrite weak link in Wi-Fi Lib */ + return ESP_OK; +} + +int nan_input(void *p1, int p2, int p3) +{ + /* Do not remove, stub to overwrite weak link in Wi-Fi Lib */ + return 0; +} + +void nan_sm_handle_event(void *p1, int p2) +{ + /* Do not remove, stub to overwrite weak link in Wi-Fi Lib */ +} + +#endif diff --git a/components/esp_wifi/src/wifi_netif.c b/components/esp_wifi/src/wifi_netif.c index 2c559b185f..4fe30acfce 100644 --- a/components/esp_wifi/src/wifi_netif.c +++ b/components/esp_wifi/src/wifi_netif.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -46,6 +46,13 @@ static esp_err_t wifi_ap_receive(void *buffer, uint16_t len, void *eb) } #endif +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +static esp_err_t wifi_nan_receive(void *buffer, uint16_t len, void *eb) +{ + return s_wifi_rxcbs[WIFI_IF_NAN](s_wifi_netifs[WIFI_IF_NAN], buffer, len, eb); +} +#endif + static void wifi_free(void *h, void* buffer) { if (buffer) { @@ -146,6 +153,12 @@ esp_err_t esp_wifi_register_if_rxcb(wifi_netif_driver_t ifx, esp_netif_receive_t break; #endif +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE + case WIFI_IF_NAN: + rxcb = wifi_nan_receive; + break; +#endif + default: break; } diff --git a/components/esp_wifi/wifi_apps/include/apps_private/wifi_apps_private.h b/components/esp_wifi/wifi_apps/include/apps_private/wifi_apps_private.h new file mode 100644 index 0000000000..5fe106f289 --- /dev/null +++ b/components/esp_wifi/wifi_apps/include/apps_private/wifi_apps_private.h @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_log.h" +#include "esp_err.h" +#include "esp_wifi_types.h" +#include "esp_netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE + +/** + * @brief Initialize the NAN App and required Data Structures + * + * @attention This API should be called in esp_wifi_init() + */ +void esp_nan_app_init(void); + +/** + * @brief De-initialize the NAN App and complete the cleanup + * + * @attention This API should be called in esp_wifi_deinit() + */ +void esp_nan_app_deinit(void); + +/** + * @brief NAN App action handler for NAN Started event. Sets up other event handlers and + * initializes NAN App context + * + * @attention This API should be called in WIFI_EVENT_NAN_STARTED event handler + * + * @param nan_netif Netif handle corresponding to NAN interface. + */ +void esp_nan_action_start(esp_netif_t *nan_netif); + +/** + * @brief NAN App action handler for NAN Stopped event. Clears other event handlers and + * resets NAN App context + * + * @attention This API should be called in WIFI_EVENT_NAN_STOPPED event handler + */ +void esp_nan_action_stop(void); + +#endif /* CONFIG_ESP_WIFI_NAN_ENABLE */ + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_wifi/wifi_apps/include/esp_nan.h b/components/esp_wifi/wifi_apps/include/esp_nan.h new file mode 100644 index 0000000000..0fba2bf5f5 --- /dev/null +++ b/components/esp_wifi/wifi_apps/include/esp_nan.h @@ -0,0 +1,202 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_log.h" +#include "esp_err.h" +#include "lwip/inet.h" +#include "esp_wifi_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WIFI_NAN_CONFIG_DEFAULT() { \ + .op_channel = 6, \ + .master_pref = 2, \ + .scan_time = 3, \ + .warm_up_sec = 5, \ +}; + +#define NDP_STATUS_ACCEPTED 1 +#define NDP_STATUS_REJECTED 2 + +#define NAN_MAX_PEERS_RECORD 15 +#define ESP_NAN_PUBLISH 1 +#define ESP_NAN_SUBSCRIBE 2 + +/** Parameters of a peer service record */ +struct nan_peer_record { + uint8_t peer_svc_id; /**< Identifier of Peer's service */ + uint8_t own_svc_id; /**< Identifier of own service associated with Peer */ + uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface address */ + uint8_t peer_svc_type; /**< Peer's service type (Publish/Subscribe) */ + uint8_t ndp_id; /**< Specifies if the peer has any active datapath */ + uint8_t peer_ndi[6]; /**< Peer's NAN Data Interface address, only valid when ndp_id is non-zero */ +}; + +/** + * @brief Start NAN Discovery with provided configuration + * + * @attention This API should be called after esp_wifi_init(). + * + * @param nan_cfg NAN related parameters to be configured. + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_wifi_nan_start(const wifi_nan_config_t *nan_cfg); + +/** + * @brief Stop NAN Discovery, end NAN Services and Datapaths + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_wifi_nan_stop(void); + +/** + * @brief Start Publishing a service to the NAN Peers in vicinity + * + * @attention This API should be called after esp_wifi_nan_start(). + * + * @param publish_cfg Configuration parameters for publishing a service. + * @param ndp_resp_needed Setting this true will require user response for every NDP Req using esp_wifi_nan_datapath_resp API. + * + * @return + * - non-zero: Publish service identifier + * - zero: failed + */ +uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg, bool ndp_resp_needed); + +/** + * @brief Subscribe for a service within the NAN cluster + * + * @attention This API should be called after esp_wifi_nan_start(). + * + * @param subscribe_cfg Configuration parameters for subscribing for a service. + * + * @return + * - non-zero: Subscribe service identifier + * - zero: failed + */ +uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe_cfg); + +/** + * @brief Send a follow-up message to the NAN Peer with matched service + * + * @attention This API should be called after a NAN service is discovered due to a match. + * + * @param fup_params Configuration parameters for sending a Follow-up message. + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_wifi_nan_send_message(wifi_nan_followup_params_t *fup_params); + +/** + * @brief Cancel a NAN service + * + * @param service_id Publish/Subscribe service id to be cancelled. + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_wifi_nan_cancel_service(uint8_t service_id); + +/** + * @brief Send NAN Datapath Request to a NAN Publisher with matched service + * + * @attention This API should be called by the Subscriber after a match occurs with a Publisher. + * + * @param req NAN Datapath Request parameters. + * + * @return + * - non-zero: NAN Datapath Identifier + * - zero: failed + */ +uint8_t esp_wifi_nan_datapath_req(wifi_nan_datapath_req_t *req); + +/** + * @brief Respond to a NAN Datapath request with Accept or Reject + * + * @attention This API should be called if ndp_auto_accept is not set True by the Publisher and + * a WIFI_EVENT_NDP_INDICATION event is received due to an incoming NDP request. + * + * @param resp NAN Datapath Response parameters. + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_wifi_nan_datapath_resp(wifi_nan_datapath_resp_t *resp); + +/** + * @brief Terminate a NAN Datapath + * + * @param req NAN Datapath end request parameters. + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_wifi_nan_datapath_end(wifi_nan_datapath_end_req_t *req); + +/** + * @brief Get IPv6 Link Local address using MAC address + * + * @param[out] ip6 Derived IPv6 Link Local address. + * @param[in] mac_addr Input MAC Address. + */ +void esp_wifi_nan_get_ipv6_linklocal_from_mac(ip6_addr_t *ip6, uint8_t *mac_addr); + +/** + * brief Get own Service information from Service ID OR Name. + * + * @param[inout] own_svc_id As input, it indicates Service ID to search for. + * As output, it indicates Service ID of the service found using Service Name. + * @param[inout] svc_name As input, it indicates Service Name to search for. + * As output, it indicates Service Name of the service found using Service ID. + * @param[out] num_peer_records Number of peers discovered by corresponding service. + * @return + * - ESP_OK: succeed + * - ESP_FAIL: failed + */ +esp_err_t esp_wifi_nan_get_own_svc_info(uint8_t *own_svc_id, char *svc_name, int *num_peer_records); + +/** + * brief Get a list of Peers discovered by the given Service. + * + * @param[inout] num_peer_records As input param, it stores max peers peer_record can hold. + * As output param, it specifies the actual number of peers this API returns. + * @param own_svc_id Service ID of own service. + * @param[out] peer_record Pointer to first peer record. + * @return + * - ESP_OK: succeed + * - ESP_FAIL: failed + */ +esp_err_t esp_wifi_nan_get_peer_records(int *num_peer_records, uint8_t own_svc_id, struct nan_peer_record *peer_record); + +/** + * brief Find Peer's Service information using Peer MAC and optionally Service Name. + * + * @param svc_name Service Name of the published/subscribed service. + * @param peer_mac Peer's NAN Management Interface MAC address. + * @param[out] peer_info Peer's service information structure. + * @return + * - ESP_OK: succeed + * - ESP_FAIL: failed + */ +esp_err_t esp_wifi_nan_get_peer_info(char *svc_name, uint8_t *peer_mac, struct nan_peer_record *peer_info); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_wifi/wifi_apps/src/nan_app.c b/components/esp_wifi/wifi_apps/src/nan_app.c new file mode 100644 index 0000000000..9290cb0f87 --- /dev/null +++ b/components/esp_wifi/wifi_apps/src/nan_app.c @@ -0,0 +1,1036 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_wifi.h" +#include "esp_private/wifi.h" +#include "esp_wifi_netif.h" +#include "freertos/event_groups.h" +#include "lwip/netdb.h" +#include "lwip/sockets.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_mac.h" +#include "os.h" +#include "esp_nan.h" + +/* NAN States */ +#define NAN_STARTED_BIT BIT0 +#define NAN_STOPPED_BIT BIT1 + +/* NAN Events */ +#define NDP_INDICATION BIT2 +#define NDP_CONFIRMED BIT3 +#define NDP_TERMINATED BIT4 + +/* Macros */ +#define MACADDR_LEN 6 +#define MACADDR_EQUAL(a1, a2) (memcmp(a1, a2, MACADDR_LEN)) +#define MACADDR_COPY(dst, src) (memcpy(dst, src, MACADDR_LEN)) + +/* Global Variables */ +static const char *TAG = "nan_app"; +static EventGroupHandle_t nan_event_group; +static bool s_app_default_handlers_set = false; +static uint8_t null_mac[MACADDR_LEN] = {0}; + +struct peer_svc_info { + SLIST_ENTRY(peer_svc_info) next; + uint8_t peer_svc_info[ESP_WIFI_MAX_SVC_INFO_LEN]; /**< Information for followup message */ + uint8_t svc_id; /**< Identifier of peer's service */ + uint8_t own_svc_id; /**< Identifier for own service */ + uint8_t type; /**< Service type (Publish/Subscribe) */ + uint8_t peer_nmi[MACADDR_LEN]; /**< Peer's NAN Management Interface address */ +}; + +struct own_svc_info { + char svc_name[ESP_WIFI_MAX_SVC_NAME_LEN]; /**< Name identifying a service */ + uint8_t svc_id; /**< Identifier for a service */ + uint8_t type; /**< Service type (Publish/Subscribe) */ + bool ndp_resp_needed; /**< If enabled, NDP response is required */ + uint8_t num_peer_records; /**< Count of peer records associated with svc_id */ + SLIST_HEAD(peer_list_t, peer_svc_info) peer_list; /**< List of peers matched for specific service */ +}; + +struct ndl_info { + uint8_t ndp_id; /**< Identifier for instance of NDP */ + uint8_t peer_ndi[MACADDR_LEN]; /**< Peer's NAN Data Interface address */ + uint8_t peer_nmi[MACADDR_LEN]; /**< Peer's NAN Management Interface address */ + uint8_t publisher_id; /**< Publisher's service identifier */ + uint8_t own_role; /**< Own role (Publisher/Subscriber) */ +}; + +typedef struct { + uint8_t state; + uint8_t event; + struct ndl_info ndl[ESP_WIFI_NAN_DATAPATH_MAX_PEERS]; /**< Record of NDL of all peers */ + struct own_svc_info own_svc[ESP_WIFI_NAN_MAX_SVC_SUPPORTED]; /**< Record of own service(s) */ + esp_netif_t *nan_netif; +} nan_ctx_t; + +static nan_ctx_t s_nan_ctx; + +void esp_wifi_nan_get_ipv6_linklocal_from_mac(ip6_addr_t *ip6, uint8_t *mac_addr) +{ + if (ip6 == NULL || mac_addr == NULL) + return; + /* Link-local prefix. */ + ip6->addr[0] = htonl(0xfe800000ul); + ip6->addr[1] = 0; + + /* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */ + ip6->addr[2] = htonl((((uint32_t)(mac_addr[0] ^ 0x02)) << 24) | + ((uint32_t)(mac_addr[1]) << 16) | + ((uint32_t)(mac_addr[2]) << 8) | + (0xff)); + ip6->addr[3] = htonl((uint32_t)(0xfeul << 24) | + ((uint32_t)(mac_addr[3]) << 16) | + ((uint32_t)(mac_addr[4]) << 8) | + (mac_addr[5])); + + ip6->zone = IP6_NO_ZONE; +} + +static struct own_svc_info *nan_find_own_svc(uint8_t svc_id) +{ + struct own_svc_info *p_svc = NULL; + + if (svc_id == 0) { + ESP_LOGE(TAG, "Service id cannot be 0!"); + return NULL; + } + + for (int i = 0; i < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; i++) { + if (s_nan_ctx.own_svc[i].svc_id == svc_id) { + p_svc = &s_nan_ctx.own_svc[i]; + break; + } + } + + return p_svc; +} + +static struct own_svc_info *nan_find_own_svc_by_name(const char *svc_name) +{ + struct own_svc_info *p_svc = NULL; + + if (!svc_name) { + ESP_LOGE(TAG, "Service name not given!"); + return NULL; + } + + for (int i = 0; i < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; i++) { + if (s_nan_ctx.own_svc[i].svc_id && !strcmp(s_nan_ctx.own_svc[i].svc_name, svc_name)) { + p_svc = &s_nan_ctx.own_svc[i]; + break; + } + } + + return p_svc; +} + +static struct peer_svc_info *nan_find_peer_svc(uint8_t own_svc_id, uint8_t peer_svc_id, uint8_t peer_nmi[]) +{ + struct peer_svc_info *p_peer_svc = NULL, *temp; + struct own_svc_info *p_own_svc = NULL; + uint8_t *peer_nmi_valid = NULL; + int idx = 0; + + if (MACADDR_EQUAL(peer_nmi, null_mac)) { + /* non-zero Peer NMI given, use it */ + peer_nmi_valid = peer_nmi; + } + while (idx < ESP_WIFI_NAN_MAX_SVC_SUPPORTED) { + if (own_svc_id) { + p_own_svc = nan_find_own_svc(own_svc_id); + if (!p_own_svc) { + ESP_LOGE(TAG, "Cannot find own service with id %d!", own_svc_id); + return NULL; + } + } else { + p_own_svc = &s_nan_ctx.own_svc[idx++]; + } + SLIST_FOREACH(temp, &(p_own_svc->peer_list), next) { + if (peer_svc_id != 0 && peer_nmi_valid) { + if (temp->svc_id == peer_svc_id && !MACADDR_EQUAL(temp->peer_nmi, peer_nmi_valid)) { + p_peer_svc = temp; + break; + } + } else if (peer_svc_id != 0) { + if (temp->svc_id == peer_svc_id) { + p_peer_svc = temp; + break; + } + } else { + if (peer_nmi_valid && !MACADDR_EQUAL(temp->peer_nmi, peer_nmi_valid)) { + p_peer_svc = temp; + break; + } + } + } + if (p_peer_svc || own_svc_id) { /* If no peer found with given own_svc_id, don't search in other services */ + break; + } + } + return p_peer_svc; +} + +static bool nan_record_peer_svc(uint8_t own_svc_id, uint8_t peer_svc_id, uint8_t peer_nmi[]) +{ + struct own_svc_info *p_own_svc; + struct peer_svc_info *p_peer_svc; + + p_own_svc = nan_find_own_svc(own_svc_id); + if (!p_own_svc) { + ESP_LOGE(TAG, "Unable to find own service with id %d", own_svc_id); + return false; + } + p_peer_svc = (struct peer_svc_info *)os_zalloc(sizeof(struct peer_svc_info)); + if (!p_peer_svc) { + ESP_LOGE(TAG, "Unable to allocate for Peer Service"); + return false; + } + p_peer_svc->svc_id = peer_svc_id; + p_peer_svc->own_svc_id = own_svc_id; + p_peer_svc->type = (p_own_svc->type == ESP_NAN_SUBSCRIBE) ? ESP_NAN_PUBLISH : ESP_NAN_SUBSCRIBE; + MACADDR_COPY(p_peer_svc->peer_nmi, peer_nmi); + + if (p_own_svc->num_peer_records < NAN_MAX_PEERS_RECORD) { + SLIST_INSERT_HEAD(&(p_own_svc->peer_list), p_peer_svc, next); + p_own_svc->num_peer_records++; + } else { + struct peer_svc_info *temp; + temp = SLIST_FIRST(&(p_own_svc->peer_list)); + SLIST_REMOVE_HEAD(&(p_own_svc->peer_list), next); + os_free(temp); + } + + return true; +} + +static void nan_reset_service(uint8_t svc_id, bool reset_all) +{ + struct own_svc_info *p_own_svc = NULL; + struct peer_svc_info *p_peer_svc = NULL, *temp; + int idx = 0; + + if (svc_id == 0 && !reset_all) { + return; + } + + while (idx < ESP_WIFI_NAN_MAX_SVC_SUPPORTED) { + p_own_svc = &s_nan_ctx.own_svc[idx++]; + if (reset_all || (svc_id && p_own_svc->svc_id == svc_id)) { + SLIST_FOREACH_SAFE(p_peer_svc, &(p_own_svc->peer_list), next, temp) { + SLIST_REMOVE(&(p_own_svc->peer_list), p_peer_svc, peer_svc_info, next); + os_free(p_peer_svc); + } + memset(p_own_svc, 0, sizeof(struct own_svc_info)); + } + } +} + +static void nan_reset_ndl(uint8_t ndp_id, bool reset_all) +{ + struct ndl_info *ndl = NULL; + + if (reset_all) { + memset(s_nan_ctx.ndl, 0, sizeof(struct ndl_info) * ESP_WIFI_NAN_DATAPATH_MAX_PEERS); + return; + } + for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) { + ndl = &s_nan_ctx.ndl[i]; + if (ndl->ndp_id == ndp_id) { + memset(ndl, 0, sizeof(struct ndl_info)); + break; + } + } +} + +static bool nan_services_limit_reached(void) +{ + for (int i = 0; i < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; i++) { + if (s_nan_ctx.own_svc[i].svc_id == 0) { + return false; + } + } + return true; +} + +static void nan_record_own_svc(uint8_t id, uint8_t type, const char svc_name[], bool ndp_resp_needed) +{ + struct own_svc_info *p_svc = NULL; + + for (int i = 0; i < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; i++) { + if (s_nan_ctx.own_svc[i].svc_id == 0) { + p_svc = &s_nan_ctx.own_svc[i]; + break; + } + } + + if (!p_svc) { + return; + } + + p_svc->svc_id = id; + p_svc->type = type; + strlcpy(p_svc->svc_name, svc_name, ESP_WIFI_MAX_SVC_NAME_LEN); + SLIST_INIT(&(p_svc->peer_list)); + if (type == ESP_NAN_PUBLISH) { + p_svc->ndp_resp_needed = ndp_resp_needed; + } +} + +static bool ndl_limit_reached(void) +{ + for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) { + if (s_nan_ctx.ndl[i].ndp_id == 0) { + return false; + } + } + return true; +} + +static void nan_record_new_ndl(uint8_t ndp_id, uint8_t publish_id, uint8_t peer_nmi[], uint8_t own_role) +{ + struct ndl_info *ndl = NULL; + + for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) { + if (s_nan_ctx.ndl[i].ndp_id == 0) { + ndl = &s_nan_ctx.ndl[i]; + break; + } + } + if (!ndl) { + return; + } + ndl->ndp_id = ndp_id; + if (peer_nmi) { + MACADDR_COPY(ndl->peer_nmi, peer_nmi); + } + ndl->publisher_id = publish_id; + ndl->own_role = own_role; +} + +static struct ndl_info *nan_find_ndl(uint8_t ndp_id, uint8_t peer_nmi[]) +{ + struct ndl_info *ndl = NULL; + + for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) { + ndl = &s_nan_ctx.ndl[i]; + if (ndp_id != 0 && peer_nmi) { + if (ndl->ndp_id == ndp_id && !MACADDR_EQUAL(ndl->peer_nmi, peer_nmi)) { + return ndl; + } + } else if (ndp_id != 0) { + if (ndl->ndp_id == ndp_id) { + return ndl; + } + } else if (peer_nmi) { + if (!MACADDR_EQUAL(ndl->peer_nmi, peer_nmi)) { + return ndl; + } + } + } + return NULL; +} + +static bool nan_is_datapath_active(void) +{ + for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) { + if (s_nan_ctx.ndl[i].ndp_id != 0) { + return true; + } + } + return false; +} + +static void nan_fill_params_from_event(void *evt_data, uint8_t event) +{ + switch (event) { + case WIFI_EVENT_NDP_INDICATION: { + wifi_event_ndp_indication_t *evt = (wifi_event_ndp_indication_t *)evt_data; + nan_record_new_ndl(evt->ndp_id, evt->publish_id, evt->peer_nmi, ESP_WIFI_NDP_ROLE_RESPONDER); + break; + } + case WIFI_EVENT_NDP_CONFIRM: { + wifi_event_ndp_confirm_t *evt = (wifi_event_ndp_confirm_t *)evt_data; + struct ndl_info *ndl = NULL; + + if ((ndl = nan_find_ndl(evt->ndp_id, evt->peer_nmi)) == NULL) { + ESP_LOGE(TAG, "No NDL with ndp id %d", evt->ndp_id); + return; + } + MACADDR_COPY(ndl->peer_ndi, evt->peer_ndi); + break; + } + case WIFI_EVENT_NAN_REPLIED: { + wifi_event_nan_replied_t *evt = (wifi_event_nan_replied_t *)evt_data; + + if (!nan_find_peer_svc(evt->publish_id, evt->subscribe_id, evt->sub_if_mac)) { + nan_record_peer_svc(evt->publish_id, evt->subscribe_id, evt->sub_if_mac); + } + break; + } + case WIFI_EVENT_NAN_RECEIVE: { + wifi_event_nan_receive_t *evt = (wifi_event_nan_receive_t *)evt_data; + + if (!nan_find_peer_svc(evt->inst_id, evt->peer_inst_id, evt->peer_if_mac)) { + nan_record_peer_svc(evt->inst_id, evt->peer_inst_id, evt->peer_if_mac); + } + break; + } + case WIFI_EVENT_NAN_SVC_MATCH: { + wifi_event_nan_svc_match_t *evt = (wifi_event_nan_svc_match_t *)evt_data; + + if (!nan_find_peer_svc(evt->subscribe_id, evt->publish_id, evt->pub_if_mac)) { + nan_record_peer_svc(evt->subscribe_id, evt->publish_id, evt->pub_if_mac); + } + break; + } + default: + break; + } +} + +static void nan_app_action_service_match(void *arg, esp_event_base_t event_base, int32_t event_id, void *data) +{ + if (data == NULL) { + return; + } + wifi_event_nan_svc_match_t *evt = (wifi_event_nan_svc_match_t *)data; + + ESP_LOGI(TAG, "Service matched with "MACSTR" [Peer Publish id - %d]", + MAC2STR(evt->pub_if_mac), evt->publish_id); + nan_fill_params_from_event(evt, WIFI_EVENT_NAN_SVC_MATCH); +} + +static void nan_app_action_replied(void *arg, esp_event_base_t event_base, int32_t event_id, void *data) +{ + if (data == NULL) { + return; + } + wifi_event_nan_replied_t *evt = (wifi_event_nan_replied_t *)data; + + ESP_LOGD(TAG, "Sent Publish to Peer "MACSTR" [Peer Subscribe id - %d]", + MAC2STR(evt->sub_if_mac), evt->subscribe_id); + nan_fill_params_from_event(evt, WIFI_EVENT_NAN_REPLIED); +} + +static void nan_app_action_receive(void *arg, esp_event_base_t event_base, int32_t event_id, void *data) +{ + if (data == NULL) { + return; + } + wifi_event_nan_receive_t *evt = (wifi_event_nan_receive_t *)data; + + ESP_LOGI(TAG, "Received message '%s' from Peer "MACSTR" [Peer Service id - %d]", + evt->peer_svc_info, MAC2STR(evt->peer_if_mac), evt->peer_inst_id); + nan_fill_params_from_event(evt, WIFI_EVENT_NAN_RECEIVE); +} + +static void nan_app_action_ndp_indication(void *arg, esp_event_base_t event_base, int32_t event_id, void *data) +{ + if (data == NULL) { + return; + } + wifi_event_ndp_indication_t *evt = (wifi_event_ndp_indication_t *)data; + struct own_svc_info *p_own_svc = nan_find_own_svc(evt->publish_id); + + if (!p_own_svc) { + ESP_LOGE(TAG, "No Publish found with id %d", evt->publish_id); + return; + } + if (ndl_limit_reached()) { + ESP_LOGE(TAG, "NDP limit reached"); + return; + } + + nan_fill_params_from_event(evt, WIFI_EVENT_NDP_INDICATION); + if (p_own_svc->ndp_resp_needed) { + ESP_LOGI(TAG, "NDP Req from "MACSTR" [NDP Id: %d], Accept OR Deny using NDP command", + MAC2STR(evt->peer_nmi), evt->ndp_id); + s_nan_ctx.event |= NDP_INDICATION; + } else { + wifi_nan_datapath_resp_t ndp_resp = {0}; + ndp_resp.accept = true; + ndp_resp.ndp_id = evt->ndp_id; + MACADDR_COPY(ndp_resp.peer_mac, evt->peer_nmi); + + esp_nan_internal_datapath_resp(&ndp_resp); + } +} + +static void nan_app_action_ndp_confirm(void *arg, esp_event_base_t event_base, int32_t event_id, void *data) +{ + if (data == NULL) { + return; + } + wifi_event_ndp_confirm_t *evt = (wifi_event_ndp_confirm_t *)data; + wifi_netif_driver_t driver = esp_netif_get_io_driver(s_nan_ctx.nan_netif); + ip_addr_t target_addr = {0}; + + if (!s_nan_ctx.nan_netif) { + ESP_LOGE(TAG, "%s: NAN netif is NULL", __func__); + return; + } + if (evt->status == NDP_STATUS_REJECTED) { + ESP_LOGE(TAG, "NDP request to Peer "MACSTR" rejected [NDP ID - %d]", MAC2STR(evt->peer_nmi), evt->ndp_id); + nan_reset_ndl(evt->ndp_id, false); + return; + } + + // if interface not ready when started, rxcb to be registered on connection + if (esp_wifi_register_if_rxcb(driver, esp_netif_receive, s_nan_ctx.nan_netif) != ESP_OK) { + ESP_LOGE(TAG, "%s: esp_wifi_register_if_rxcb failed", __func__); + return; + } + + nan_fill_params_from_event(evt, WIFI_EVENT_NDP_CONFIRM); + + esp_netif_action_connected(s_nan_ctx.nan_netif, event_base, event_id, data); + + esp_netif_create_ip6_linklocal(s_nan_ctx.nan_netif); + s_nan_ctx.state |= NDP_CONFIRMED; + esp_wifi_nan_get_ipv6_linklocal_from_mac(&target_addr.u_addr.ip6, evt->peer_ndi); + target_addr.type = IPADDR_TYPE_V6; + ESP_LOGI(TAG, "NDP confirmed with Peer "MACSTR" [NDP ID - %d, Peer IPv6 - %s]", + MAC2STR(evt->peer_nmi), evt->ndp_id, inet6_ntoa(*ip_2_ip6(&target_addr))); +} + +static void nan_app_action_ndp_terminated(void *arg, esp_event_base_t event_base, int32_t event_id, void *data) +{ + if (data == NULL) { + return; + } + wifi_event_ndp_terminated_t *evt = (wifi_event_ndp_terminated_t *)data; + + if (s_nan_ctx.nan_netif && !nan_is_datapath_active()) { + esp_netif_action_disconnected(s_nan_ctx.nan_netif, event_base, event_id, data); + } + ESP_LOGI(TAG, "NDP terminated with Peer "MACSTR" [NDP ID - %d]", MAC2STR(evt->init_ndi), evt->ndp_id); + nan_reset_ndl(evt->ndp_id, false); + + s_nan_ctx.state &= ~(NDP_CONFIRMED); + s_nan_ctx.event &= ~(NDP_INDICATION); + os_event_group_set_bits(nan_event_group, NDP_TERMINATED); +} + +/* types of ipv6 addresses to be displayed on ipv6 events */ +static const char *s_ipv6_addr_types[] = { + "UNKNOWN", + "GLOBAL", + "LINK_LOCAL", + "SITE_LOCAL", + "UNIQUE_LOCAL", + "IPV4_MAPPED_IPV6" +}; + +static void nan_app_action_got_ipv6(void *arg, esp_event_base_t event_base, int32_t event_id, void *data) +{ + if (data == NULL) { + return; + } + ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)data; + + if (event->esp_netif == s_nan_ctx.nan_netif) { + esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip); + ESP_LOGD(TAG, "NAN Data Interface ready [IPv6 - "IPV6STR", type - %s]", + IPV62STR(event->ip6_info.ip), s_ipv6_addr_types[ipv6_type]); + } +} + +static esp_err_t nan_clear_app_default_handlers(void) +{ + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NAN_SVC_MATCH, nan_app_action_service_match); + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NAN_REPLIED, nan_app_action_replied); + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NAN_RECEIVE, nan_app_action_receive); + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NDP_INDICATION, nan_app_action_ndp_indication); + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NDP_CONFIRM, nan_app_action_ndp_confirm); + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NDP_TERMINATED, nan_app_action_ndp_terminated); + esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, nan_app_action_got_ipv6); + s_app_default_handlers_set = false; + + return ESP_OK; +} + +static esp_err_t nan_set_app_default_handlers(void) +{ + if (s_app_default_handlers_set) { + return ESP_OK; + } + + int ret; + (void) ret; + ESP_GOTO_ON_ERROR(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NAN_SVC_MATCH, + nan_app_action_service_match, NULL), fail, TAG, "Registering event handler failed"); + + ESP_GOTO_ON_ERROR(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NAN_REPLIED, + nan_app_action_replied, NULL), fail, TAG, "Registering event handler failed"); + + ESP_GOTO_ON_ERROR(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NAN_RECEIVE, + nan_app_action_receive, NULL), fail, TAG, "Registering event handler failed"); + + ESP_GOTO_ON_ERROR(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NDP_INDICATION, + nan_app_action_ndp_indication, NULL), fail, TAG, "Registering event handler failed"); + + ESP_GOTO_ON_ERROR(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NDP_CONFIRM, + nan_app_action_ndp_confirm, NULL), fail, TAG, "Registering event handler failed"); + + ESP_GOTO_ON_ERROR(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NDP_TERMINATED, + nan_app_action_ndp_terminated, NULL), fail, TAG, "Registering event handler failed"); + + ESP_GOTO_ON_ERROR(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, + nan_app_action_got_ipv6, NULL), fail, TAG, "Registering event handler failed"); + + s_app_default_handlers_set = true; + return ESP_OK; + +fail: + nan_clear_app_default_handlers(); + return ESP_FAIL; +} + +void esp_nan_app_init(void) +{ + if (nan_event_group) { + os_event_group_delete(nan_event_group); + nan_event_group = NULL; + } + nan_event_group = os_event_group_create(); +} + +void esp_nan_app_deinit(void) +{ + if (nan_event_group) { + os_event_group_delete(nan_event_group); + nan_event_group = NULL; + } +} + +void esp_nan_action_start(esp_netif_t *nan_netif) +{ + if (nan_set_app_default_handlers() != ESP_OK) { + ESP_LOGE(TAG, "Registering NAN handlers failed"); + return; + } + + s_nan_ctx.nan_netif = nan_netif; + + s_nan_ctx.state = NAN_STARTED_BIT; + ESP_LOGI(TAG, "NAN Discovery started."); + os_event_group_set_bits(nan_event_group, NAN_STARTED_BIT); +} + +void esp_nan_action_stop(void) +{ + nan_clear_app_default_handlers(); + + if (s_nan_ctx.state & NDP_CONFIRMED) { + nan_reset_ndl(0, true); + esp_wifi_internal_reg_rxcb(WIFI_IF_NAN, NULL); + } + + nan_reset_service(0, true); + s_nan_ctx.state &= ~NAN_STARTED_BIT; + s_nan_ctx.state |= NAN_STOPPED_BIT; + os_event_group_set_bits(nan_event_group, NAN_STOPPED_BIT); +} + +esp_err_t esp_wifi_nan_start(const wifi_nan_config_t *nan_cfg) +{ + wifi_mode_t mode; + esp_err_t ret; + wifi_config_t config = {0}; + + ret = esp_wifi_get_mode(&mode); + if (ret == ESP_ERR_WIFI_NOT_INIT) { + ESP_LOGE(TAG, "WiFi not initialised!"); + return ret; + } else if (ret != ESP_OK) { + ESP_LOGE(TAG, "Unable to get mode"); + return ret; + } + if (s_nan_ctx.state & NAN_STARTED_BIT) { + ESP_LOGI(TAG, "NAN already started"); + return ESP_OK; + } + + ESP_RETURN_ON_ERROR(esp_wifi_set_mode(WIFI_MODE_NAN), TAG, "Set mode NAN failed"); + + memcpy(&config.nan, nan_cfg, sizeof(wifi_nan_config_t)); + ESP_RETURN_ON_ERROR(esp_wifi_set_config(WIFI_IF_NAN, &config), TAG, "Setting NAN config failed"); + + if (esp_wifi_start() != ESP_OK) { + ESP_LOGE(TAG, "Starting wifi failed"); + s_nan_ctx.nan_netif = NULL; + return ESP_FAIL; + } + + EventBits_t bits = os_event_group_wait_bits(nan_event_group, NAN_STARTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + if (!(bits & NAN_STARTED_BIT)) { + s_nan_ctx.nan_netif = NULL; + return ESP_FAIL; + } + return ESP_OK; +} + +esp_err_t esp_wifi_nan_stop(void) +{ + if (!(s_nan_ctx.state & NAN_STARTED_BIT)) { + ESP_LOGE(TAG, "NAN isn't started"); + return ESP_FAIL; + } + + if (s_nan_ctx.state & NDP_CONFIRMED) { + /* Terminate all NDP's */ + wifi_nan_datapath_end_req_t ndp_end = {0}; + for (int i=0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) { + if (s_nan_ctx.ndl[i].ndp_id) { + MACADDR_COPY(ndp_end.peer_mac, s_nan_ctx.ndl[i].peer_nmi); + ndp_end.ndp_id = s_nan_ctx.ndl[i].ndp_id; + esp_nan_internal_datapath_end(&ndp_end); + } + } + nan_reset_ndl(0, true); + + os_event_group_clear_bits(nan_event_group, NDP_TERMINATED); + os_event_group_wait_bits(nan_event_group, NDP_TERMINATED, pdFALSE, pdFALSE, portMAX_DELAY); + os_event_group_clear_bits(nan_event_group, NDP_TERMINATED); + /* Wait for 1 NAN DW interval (512 TU's ~= 524 mSec) for successful termination */ + g_wifi_osi_funcs._task_delay(524/portTICK_PERIOD_MS); + } + + ESP_RETURN_ON_ERROR(esp_wifi_stop(), TAG, "Stopping NAN failed"); + + EventBits_t bits = os_event_group_wait_bits(nan_event_group, NAN_STOPPED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + if (!(bits & NAN_STOPPED_BIT)) { + return ESP_FAIL; + } + + memset(&s_nan_ctx, 0, sizeof(nan_ctx_t)); + return ESP_OK; +} + +uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg, bool ndp_resp_needed) +{ + uint8_t pub_id; + + if (!(s_nan_ctx.state & NAN_STARTED_BIT)) { + ESP_LOGE(TAG, "NAN not started!"); + return 0; + } + if (nan_services_limit_reached()) { + ESP_LOGE(TAG, "Maximum services limit reached"); + return 0; + } + + if (nan_find_own_svc_by_name(publish_cfg->service_name)) { + ESP_LOGE(TAG, "Service name already used!"); + return 0; + } + + if (esp_nan_internal_publish_service(publish_cfg, &pub_id, false) != ESP_OK) { + ESP_LOGE(TAG, "Failed to publish service '%s'", publish_cfg->service_name); + return 0; + } + + ESP_LOGI(TAG, "Started Publishing %s [Service ID - %lu]", publish_cfg->service_name, pub_id); + nan_record_own_svc(pub_id, ESP_NAN_PUBLISH, publish_cfg->service_name, ndp_resp_needed); + + return pub_id; +} + +uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe_cfg) +{ + uint8_t sub_id; + + if (!(s_nan_ctx.state & NAN_STARTED_BIT)) { + ESP_LOGE(TAG, "NAN not started!"); + return 0; + } + if (nan_services_limit_reached()) { + ESP_LOGE(TAG, "Maximum services limit reached"); + return 0; + } + + if (nan_find_own_svc_by_name(subscribe_cfg->service_name)) { + ESP_LOGE(TAG, "Service name already used!"); + return 0; + } + + if (esp_nan_internal_subscribe_service(subscribe_cfg, &sub_id, false) != ESP_OK) { + ESP_LOGE(TAG, "Failed to subscribe to service '%s'", subscribe_cfg->service_name); + return 0; + } + + ESP_LOGI(TAG, "Started Subscribing to %s [Service ID - %lu]", subscribe_cfg->service_name, sub_id); + nan_record_own_svc(sub_id, ESP_NAN_SUBSCRIBE, subscribe_cfg->service_name, false); + + return sub_id; +} + +esp_err_t esp_wifi_nan_send_message(wifi_nan_followup_params_t *fup_params) +{ + struct peer_svc_info *p_peer_svc; + + p_peer_svc = nan_find_peer_svc(fup_params->inst_id, fup_params->peer_inst_id, + fup_params->peer_mac); + if (!p_peer_svc) { + ESP_LOGE(TAG, "Cannot send Follow-up, peer not found!"); + return ESP_FAIL; + } + + if (!fup_params->inst_id) { + fup_params->inst_id = p_peer_svc->own_svc_id; + } + if (!MACADDR_EQUAL(fup_params->peer_mac, null_mac)) { + MACADDR_COPY(fup_params->peer_mac, p_peer_svc->peer_nmi); + } + + if (esp_nan_internal_send_followup(fup_params) != ESP_OK) { + ESP_LOGE(TAG, "Failed to send Follow-up message!"); + return ESP_FAIL; + } + ESP_LOGI(TAG, "Sent message '%s' to Peer "MACSTR" with Service ID %d", fup_params->svc_info, + MAC2STR(fup_params->peer_mac), fup_params->peer_inst_id); + return ESP_OK; +} + +esp_err_t esp_wifi_nan_cancel_service(uint8_t service_id) +{ + struct own_svc_info *p_own_svc = nan_find_own_svc(service_id); + + if (!p_own_svc) { + ESP_LOGE(TAG, "Cannot find own service with id %d!", service_id); + return ESP_FAIL; + } + + if (p_own_svc->type == ESP_NAN_PUBLISH) { + if (esp_nan_internal_publish_service(NULL, &service_id, true) == ESP_OK) { + nan_reset_service(service_id, false); + ESP_LOGI(TAG, "Cancelled Publish with Service ID %d", service_id); + return ESP_OK; + } + } + + if (p_own_svc->type == ESP_NAN_SUBSCRIBE) { + if (esp_nan_internal_subscribe_service(NULL, &service_id, true) == ESP_OK) { + nan_reset_service(service_id, false); + ESP_LOGI(TAG, "Cancelled Subscribe with Service ID %d", service_id); + return ESP_OK; + } + } + + return ESP_FAIL; +} + +uint8_t esp_wifi_nan_datapath_req(wifi_nan_datapath_req_t *req) +{ + uint8_t ndp_id = 0; + struct peer_svc_info *p_peer_svc = nan_find_peer_svc(0, req->pub_id, req->peer_mac); + + if (!p_peer_svc) { + ESP_LOGE(TAG, "Cannot send NDP Req, peer not found!"); + return 0; + } + if (req->pub_id == 0) + req->pub_id = p_peer_svc->svc_id; + + if (p_peer_svc->type != ESP_NAN_PUBLISH) { + ESP_LOGE(TAG, "Only subscriber can send an NDP Req to a Publisher"); + return 0; + } + if (ndl_limit_reached()) { + ESP_LOGE(TAG, "Cannot establish new datapath, limit reached!"); + return 0; + } + + if (!MACADDR_EQUAL(req->peer_mac, null_mac)) { + MACADDR_COPY(req->peer_mac, p_peer_svc->peer_nmi); + } + + if (esp_nan_internal_datapath_req(req, &ndp_id) != ESP_OK) { + ESP_LOGE(TAG, "Failed to initiate NDP req"); + return 0; + } + nan_record_new_ndl(ndp_id, req->pub_id, req->peer_mac, ESP_WIFI_NDP_ROLE_INITIATOR); + ESP_LOGD(TAG, "Requested NDP with "MACSTR" [NDP ID - %d]", MAC2STR(req->peer_mac), ndp_id); + + return ndp_id; +} + +esp_err_t esp_wifi_nan_datapath_resp(wifi_nan_datapath_resp_t *resp) +{ + struct ndl_info *ndl = nan_find_ndl(resp->ndp_id, NULL); + + if (!ndl) { + ESP_LOGE(TAG, "No NDL with ndp id %d", resp->ndp_id); + return ESP_FAIL; + } + if (!(s_nan_ctx.event & NDP_INDICATION)) { //INDICATION of specific peer + ESP_LOGE(TAG, "Need NDP Indication before NDP Response can be sent"); + return ESP_FAIL; + } + + if (!MACADDR_EQUAL(resp->peer_mac, null_mac)) { + MACADDR_COPY(resp->peer_mac, ndl->peer_nmi); + } + + if (esp_nan_internal_datapath_resp(resp) == ESP_OK) { + s_nan_ctx.event &= ~NDP_INDICATION; + return ESP_OK; + } + + return ESP_FAIL; +} + +esp_err_t esp_wifi_nan_datapath_end(wifi_nan_datapath_end_req_t *req) +{ + struct ndl_info *ndl = NULL; + + if (!(s_nan_ctx.state & NDP_CONFIRMED)) { + ESP_LOGE(TAG, "No Datapath active"); + return ESP_FAIL; + } + + ndl = nan_find_ndl(req->ndp_id, NULL); + if (!ndl) { + ESP_LOGE(TAG, "No NDL with ndp id %d", req->ndp_id); + return ESP_FAIL; + } + if (!MACADDR_EQUAL(req->peer_mac, null_mac)) { + MACADDR_COPY(req->peer_mac, ndl->peer_nmi); + } + + if (esp_nan_internal_datapath_end(req) == ESP_OK) { + return ESP_OK; + } + + return ESP_FAIL; +} + +esp_err_t esp_wifi_nan_get_own_svc_info(uint8_t *own_svc_id, char *svc_name, int *num_peer_records) +{ + struct own_svc_info *own_svc = NULL; + + if (!own_svc_id || !num_peer_records || !svc_name) { + ESP_LOGE(TAG, "NULL memory address for input parameters"); + return ESP_FAIL; + } + + if (*own_svc_id == 0) { + own_svc = nan_find_own_svc_by_name(svc_name); + if (!own_svc) { + ESP_LOGE(TAG, "No record found for given service name %s", svc_name); + return ESP_FAIL; + } + *own_svc_id = own_svc->svc_id; + } else { + own_svc = nan_find_own_svc(*own_svc_id); + if (!own_svc) { + ESP_LOGE(TAG, "No record found for given service ID %d", *own_svc_id); + return ESP_FAIL; + } + strlcpy(svc_name, own_svc->svc_name, ESP_WIFI_MAX_SVC_NAME_LEN); + } + + *num_peer_records = own_svc->num_peer_records; + + return ESP_OK; +} + +esp_err_t esp_wifi_nan_get_peer_records(int *num_peer_records, uint8_t own_svc_id, struct nan_peer_record *peer_record) +{ + struct own_svc_info *own_record = NULL; + struct peer_svc_info *temp = NULL; + int peer_num = 0; + + if (!peer_record || !num_peer_records) { + ESP_LOGE(TAG, "NULL memory address for input parameters"); + return ESP_FAIL; + } + if (own_svc_id == 0) { + ESP_LOGE(TAG, "Invalid service ID"); + return ESP_FAIL; + } + if (*num_peer_records == 0) { + ESP_LOGE(TAG, "Number of peer records provided is 0"); + return ESP_FAIL; + } + + own_record = nan_find_own_svc(own_svc_id); + if (own_record) { + SLIST_FOREACH(temp, &(own_record->peer_list), next) { + struct ndl_info *p_ndl; + peer_record[peer_num].peer_svc_id = temp->svc_id; + peer_record[peer_num].own_svc_id = own_svc_id; + peer_record[peer_num].peer_svc_type = temp->type; + MACADDR_COPY(peer_record[peer_num].peer_nmi, temp->peer_nmi); + p_ndl = nan_find_ndl(0, temp->peer_nmi); + if (p_ndl) { + peer_record[peer_num].ndp_id = p_ndl->ndp_id; + MACADDR_COPY(peer_record[peer_num].peer_ndi, p_ndl->peer_ndi); + } else { + peer_record[peer_num].ndp_id = 0; + MACADDR_COPY(peer_record[peer_num].peer_ndi, null_mac); + } + peer_num ++; + if (peer_num == *num_peer_records) { + break; + } + } + if (*num_peer_records > peer_num) { + *num_peer_records = peer_num; + } + return ESP_OK; + } else { + *num_peer_records = 0; + ESP_LOGD(TAG, "No record found for own service id %d", own_svc_id); + return ESP_FAIL; + } +} + +esp_err_t esp_wifi_nan_get_peer_info(char *svc_name, uint8_t *peer_mac, struct nan_peer_record *peer_info) +{ + struct peer_svc_info *peer_svc = NULL; + uint8_t own_svc_id = 0; + + if (!peer_mac || !peer_info) { + ESP_LOGE(TAG, "Invalid memory address for input parameters"); + return ESP_FAIL; + } + + if (svc_name) { + struct own_svc_info *own_svc = nan_find_own_svc_by_name(svc_name); + if (!own_svc) { + ESP_LOGE(TAG, "No record found for given service name %s", svc_name); + return ESP_FAIL; + } + own_svc_id = own_svc->svc_id; + } + + peer_svc = nan_find_peer_svc(own_svc_id, 0, peer_mac); + if (peer_svc) { + struct ndl_info *p_ndl; + peer_info->peer_svc_id = peer_svc->svc_id; + peer_info->own_svc_id = peer_svc->own_svc_id; + peer_info->peer_svc_type = peer_svc->type; + MACADDR_COPY(peer_info->peer_nmi, peer_mac); + p_ndl = nan_find_ndl(0, peer_mac); + if (p_ndl) { + peer_info->ndp_id = p_ndl->ndp_id; + MACADDR_COPY(peer_info->peer_ndi, p_ndl->peer_ndi); + } else { + peer_info->ndp_id = 0; + MACADDR_COPY(peer_info->peer_ndi, null_mac); + } + return ESP_OK; + } else { + ESP_LOGD(TAG, "No record found for Peer "MACSTR, MAC2STR(peer_mac)); + return ESP_FAIL; + } +} diff --git a/components/soc/esp32/include/soc/Kconfig.soc_caps.in b/components/soc/esp32/include/soc/Kconfig.soc_caps.in index e5cabe3a06..51971ce8a1 100644 --- a/components/soc/esp32/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32/include/soc/Kconfig.soc_caps.in @@ -823,6 +823,10 @@ config SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW bool default y +config SOC_WIFI_NAN_SUPPORT + bool + default y + config SOC_BLE_SUPPORTED bool default y diff --git a/components/soc/esp32/include/soc/soc_caps.h b/components/soc/esp32/include/soc/soc_caps.h index 73084e087b..b5b129a40a 100644 --- a/components/soc/esp32/include/soc/soc_caps.h +++ b/components/soc/esp32/include/soc/soc_caps.h @@ -406,6 +406,7 @@ #define SOC_WIFI_CSI_SUPPORT (1) /*!< Support CSI */ #define SOC_WIFI_MESH_SUPPORT (1) /*!< Support WIFI MESH */ #define SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW (1) /*!< Support delta early time for rf phy on/off */ +#define SOC_WIFI_NAN_SUPPORT (1) /*!< Support WIFI Aware (NAN) */ /*---------------------------------- Bluetooth CAPS ----------------------------------*/ #define SOC_BLE_SUPPORTED (1) /*!< Support Bluetooth Low Energy hardware */ diff --git a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in index 16fda2cc10..aea363ac0b 100644 --- a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in @@ -1070,3 +1070,7 @@ config SOC_WIFI_MESH_SUPPORT config SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW bool default y + +config SOC_WIFI_NAN_SUPPORT + bool + default y diff --git a/components/soc/esp32s2/include/soc/soc_caps.h b/components/soc/esp32s2/include/soc/soc_caps.h index 90ef2b613e..b12b702794 100644 --- a/components/soc/esp32s2/include/soc/soc_caps.h +++ b/components/soc/esp32s2/include/soc/soc_caps.h @@ -459,3 +459,4 @@ #define SOC_WIFI_CSI_SUPPORT (1) /*!< Support CSI */ #define SOC_WIFI_MESH_SUPPORT (1) /*!< Support WIFI MESH */ #define SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW (1) /*!< Support delta early time for rf phy on/off */ +#define SOC_WIFI_NAN_SUPPORT (1) /*!< Support WIFI Aware (NAN) */ diff --git a/components/wpa_supplicant/esp_supplicant/include/esp_supplicant_utils.h b/components/wpa_supplicant/esp_supplicant/include/esp_supplicant_utils.h new file mode 100644 index 0000000000..ca8960ce9f --- /dev/null +++ b/components/wpa_supplicant/esp_supplicant/include/esp_supplicant_utils.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Convert user input colon separated MAC Address into 6 byte MAC Address + * + * @param[in] str User input colon separated MAC Address. + * @param[out] dest Output 6 byte MAC Address. + * + * @return + * - ESP_OK: Succeed + * - ESP_FAIL: Invalid input format + */ +esp_err_t esp_supplicant_str_to_mac(const char *str, uint8_t dest[6]); + +#ifdef __cplusplus +} +#endif diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_common.c b/components/wpa_supplicant/esp_supplicant/src/esp_common.c index 974870dced..322f5000ed 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_common.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_common.c @@ -23,6 +23,16 @@ #include "rsn_supp/wpa_i.h" #include "rsn_supp/wpa.h" +/* Utility Functions */ +esp_err_t esp_supplicant_str_to_mac(const char *str, uint8_t dest[6]) +{ + if (hwaddr_aton2(str, dest) < 0) { + return ESP_FAIL; + } + + return ESP_OK; +} + struct wpa_supplicant g_wpa_supp; #if defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) || defined(CONFIG_SAE_PK) diff --git a/components/wpa_supplicant/port/include/os.h b/components/wpa_supplicant/port/include/os.h index 012de5a65f..51334ad340 100644 --- a/components/wpa_supplicant/port/include/os.h +++ b/components/wpa_supplicant/port/include/os.h @@ -368,6 +368,12 @@ extern const wifi_osi_funcs_t *wifi_funcs; #define os_task_ms_to_tick(a) wifi_funcs->_task_ms_to_tick((a)) #define os_timer_get_time(void) wifi_funcs->_esp_timer_get_time(void) +#define os_event_group_create(void) wifi_funcs->_event_group_create(void) +#define os_event_group_delete(void) wifi_funcs->_event_group_delete(void) +#define os_event_group_wait_bits(a, b, c, d, e) wifi_funcs->_event_group_wait_bits((a), (b), (c), (d), (e)) +#define os_event_group_clear_bits(a, b) wifi_funcs->_event_group_clear_bits((a), (b)) +#define os_event_group_set_bits(a, b) wifi_funcs->_event_group_set_bits((a), (b)) + static inline void os_timer_setfn(void *ptimer, void *pfunction, void *parg) { return wifi_funcs->_timer_setfn(ptimer, pfunction, parg); diff --git a/components/wpa_supplicant/src/crypto/crypto_ops.c b/components/wpa_supplicant/src/crypto/crypto_ops.c index 128af4e1ae..17c052c986 100644 --- a/components/wpa_supplicant/src/crypto/crypto_ops.c +++ b/components/wpa_supplicant/src/crypto/crypto_ops.c @@ -13,6 +13,7 @@ #include "aes.h" #include "esp_wpa.h" #include "ccmp.h" +#include "esp_rom_crc.h" #define DEFAULT_KEK_LEN 16 @@ -46,6 +47,11 @@ static int esp_aes_gmac(const u8 *key, size_t key_len, const u8 *iv, size_t iv_l #endif } +static uint32_t esp_supp_crc32(uint32_t crc, uint8_t const *buf, uint32_t len) +{ + return esp_rom_crc32_le(crc, buf, len); +} + /* * This structure is used to set the cyrpto callback function for station to connect when in security mode. * These functions either call MbedTLS API's if CONFIG_CRYPTO_MBEDTLS flag is set through Kconfig, or native @@ -80,6 +86,8 @@ const wpa_crypto_funcs_t g_wifi_default_wpa_crypto_funcs = { .ccmp_decrypt = (esp_ccmp_decrypt_t)ccmp_decrypt, .ccmp_encrypt = (esp_ccmp_encrypt_t)ccmp_encrypt, .aes_gmac = (esp_aes_gmac_t)esp_aes_gmac, + .sha256_vector = (esp_sha256_vector_t)sha256_vector, + .crc32 = (esp_crc32_le_t)esp_supp_crc32, }; const mesh_crypto_funcs_t g_wifi_default_mesh_crypto_funcs = { diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index bfe4826982..e16546bc1f 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -178,6 +178,7 @@ INPUT = \ $(PROJECT_PATH)/components/esp_wifi/include/esp_wifi_default.h \ $(PROJECT_PATH)/components/esp_wifi/include/esp_wifi_types.h \ $(PROJECT_PATH)/components/esp_wifi/include/esp_wifi.h \ + $(PROJECT_PATH)/components/esp_wifi/wifi_apps/include/esp_nan.h \ $(PROJECT_PATH)/components/esp-tls/esp_tls_errors.h \ $(PROJECT_PATH)/components/esp-tls/esp_tls.h \ $(PROJECT_PATH)/components/fatfs/diskio/diskio_impl.h \ @@ -277,6 +278,7 @@ INPUT = \ $(PROJECT_PATH)/components/wifi_provisioning/include/wifi_provisioning/wifi_config.h \ $(PROJECT_PATH)/components/wifi_provisioning/include/wifi_provisioning/wifi_scan.h \ $(PROJECT_PATH)/components/wpa_supplicant/esp_supplicant/include/esp_dpp.h \ + $(PROJECT_PATH)/components/wpa_supplicant/esp_supplicant/include/esp_supplicant_utils.h \ ## Target specific headers are in seperate Doxyfile files @INCLUDE = $(PROJECT_PATH)/docs/doxygen/Doxyfile_$(IDF_TARGET) diff --git a/docs/en/api-reference/network/esp_nan.rst b/docs/en/api-reference/network/esp_nan.rst new file mode 100644 index 0000000000..7107588d62 --- /dev/null +++ b/docs/en/api-reference/network/esp_nan.rst @@ -0,0 +1,20 @@ +Wi-Fi Aware\ :sup:`TM` (NAN) +=================================== + +Wi-Fi Aware\ :sup:`TM` or NAN (Neighbor Awareness Networking) is a protocol that allows Wi-Fi devices to discover services in their proximity. Typically, location-based services are based on querying servers for information about the environment and the location knowledge is based on GPS or other location reckoning techniques. However NAN does not require real-time connection to servers, GPS or other geo-location, but instead uses direct device-to-device Wi-Fi to discover and exchange information. NAN scales effectively in dense Wi-Fi environments and complements the connectivity of Wi-Fi by providing information about people and services in the proximity. + +Multiple NAN devices which are in the vicinity will form a NAN cluster which allows them to communicate with each other. Devices within a NAN cluster can advertise (Publish method) or look for (Subscribe method) services using NAN Service Discovery protocols. Matching of services is done by service name, once a match is found a device can either send a message or establish an IPv6 datapath with the peer. + +{IDF_TARGET_NAME} supports Wi-Fi Aware in standalone mode with support for both Service Discovery and Datapath. Wi-Fi Aware is still an evolving protocol. Please refer to Wi-Fi Alliance's official page on `Wi-Fi Aware `_ for more information. Many Android smartphones with Android 8 or higher support Wi-Fi Aware. Refer to Android's developer guide on Wi-Fi Aware `Wi-Fi Aware `_ for more information. + +Application Example +------------------- + +A pair of examples for a Publisher-Subscriber use case: :example:`wifi/wifi_aware/nan_publisher` and :example:`wifi/wifi_aware/nan_subscriber`. +A user interactive console example to explore full functionality of Wi-Fi Aware: :example:`wifi/wifi_aware/nan_console`. +Please check the `README` for more details in respective example directories. + +API Reference +------------- + +.. include-build-file:: inc/esp_nan.inc diff --git a/docs/en/api-reference/network/index.rst b/docs/en/api-reference/network/index.rst index 6be6a55004..da467c760f 100644 --- a/docs/en/api-reference/network/index.rst +++ b/docs/en/api-reference/network/index.rst @@ -14,6 +14,7 @@ Wi-Fi esp_smartconfig esp_wifi esp_dpp + esp_nan Code examples for the Wi-Fi API are provided in the :example:`wifi` directory of ESP-IDF examples. diff --git a/docs/zh_CN/api-reference/network/esp_nan.rst b/docs/zh_CN/api-reference/network/esp_nan.rst new file mode 100644 index 0000000000..5a3c60d319 --- /dev/null +++ b/docs/zh_CN/api-reference/network/esp_nan.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/network/esp_nan.rst diff --git a/docs/zh_CN/api-reference/network/index.rst b/docs/zh_CN/api-reference/network/index.rst index 450be153f1..6c33e14f74 100644 --- a/docs/zh_CN/api-reference/network/index.rst +++ b/docs/zh_CN/api-reference/network/index.rst @@ -14,6 +14,7 @@ Wi-Fi esp_smartconfig esp_wifi esp_dpp + esp_nan 本部分的 Wi-Fi API 示例代码存放在 ESP-IDF 示例项目的 :example:`wifi` 目录下。 diff --git a/examples/wifi/.build-test-rules.yml b/examples/wifi/.build-test-rules.yml index d42b3bfa35..1678d58eed 100644 --- a/examples/wifi/.build-test-rules.yml +++ b/examples/wifi/.build-test-rules.yml @@ -29,3 +29,8 @@ examples/wifi/iperf: examples/wifi/itwt: disable: - if: SOC_WIFI_HE_SUPPORT != 1 + +examples/wifi/wifi_aware: + disable: + - if: SOC_WIFI_NAN_SUPPORT != 1 + reason: targets esp32c3, esp32s3, esp32c2 and esp32c6 are not supported diff --git a/examples/wifi/wifi_aware/nan_console/CMakeLists.txt b/examples/wifi/wifi_aware/nan_console/CMakeLists.txt new file mode 100644 index 0000000000..4805700b81 --- /dev/null +++ b/examples/wifi/wifi_aware/nan_console/CMakeLists.txt @@ -0,0 +1,8 @@ +# 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) + +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/advanced/components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(nan_console) diff --git a/examples/wifi/wifi_aware/nan_console/README.md b/examples/wifi/wifi_aware/nan_console/README.md new file mode 100644 index 0000000000..21be95000d --- /dev/null +++ b/examples/wifi/wifi_aware/nan_console/README.md @@ -0,0 +1,187 @@ +| Supported Targets | ESP32 | ESP32-S2 | +| ----------------- | ----- | -------- | + +# NAN Console Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +## Introduction +Neighbor Awareness Networking (NAN) is a protocol that allows Wi-Fi devices to discover services in their proximity. NAN uses direct device-to-device communication and does not require any Internet or AP connection. +Multiple NAN devices which are in the vicinity will form a NAN cluster which allows them to communicate with each other. Devices within a NAN cluster can advertise or look for services using NAN Service Discovery protocols. +A Publisher advertises a service and a Subscriber searches for a service. A subscriber either listens passively (Passive mode) or sends out broadcast Action frames (Active mode). Similarly a publisher either broadcasts its services (Unsolicited mode) or replies to Active subscribers (Solicited Mode). Matching of services is done by service name and optionally matching filters. Once a Subscriber gets a match to the service, it can either send Follow-up frames or negotiate using NDP frames to establish a datapath. After NDP is setup both devices will obtain a IPv6 address and can use it for further communication. + +## How to use example +With the console users can configure NAN and its services on the fly. Flashing the example will first show a brief guide on the commands available on the console - + +```bash +================================================================== + | Basic Steps to test NAN Discovery & Datapath | + | | + | Below are short commands that use defaults, use 'help' | + | to see detailed command parameters available | + | | + | # NAN Discovery - | + | 1. To start NAN issue 'nan -S', to stop it use 'nan -T' | + | 2. Publish OR Subscribe a service using 'publish' OR 'subscribe'| + | 4. After service match, send a text to Peer with service id 5 - | + | > send -p 5 -t Hello | + | 5. Cancel an ongoing service with id 5 - | + | > publish -C -i 5 | + | | + | # NAN Datapath - | + | 1. Subscriber can initiate datapath using 'ndp -I -p [pub_id]' | + | 2. After NDP setup, use 'ping [Peer's IPv6]' to test datapath | + | 3. Terminate the NDP using 'ndp -D' | + | | + ==================================================================== + +nan> +``` + +Mode detailed information about command parameters is given below - + +1. **Starting NAN** +>nan> nan -S + *OR* +>nan> nan -S -p 2 -c 6 -w 5 +>p - Master Preference +>c - Operating channel +>w - Warmup time + +2. **Stopping NAN** +> nan> nan -T + +3. **Publish a service with name 'ESP_NAN-Service'** +>nan> publish + *OR* +>nan> publish -n test -t 0 -f GREEN +>n - Service name +>t - Type (0: Unsolicited, 1: Solicited) +>f - Matching filter + +4. **Subscribe to a NAN service 'ESP_NAN-Service'** +>nan> subscribe + *OR* +>nan> subscribe -n test -t 0 -f GREEN +>n - Service name +>t - Type (0: passive, 1: active) +>f - Matching filter + +5. **Cancel a NAN service** +> Cancel a Subscribe service with Inst Id 5 +>nan> subscribe -C -i 5 +> Cancel a Publish service with Inst Id 5 +>nan> publish -C -i 5 + +6. **Send a Follow-up message** +*To send message to amatched Publish OR Subscribe service* +>nan> send -p 5 -t Hello +>p - Peer’s service instance id +>t - Message + +7. **Send a Datapath request** +*To Initiate NDP Req to the last matched Publisher -* +>nan> ndp -I -p 5 +>p - Publish Id + +9. **Start a IPv6 Ping with Peer** +>nan> ping FE80::E2E2:E6FF:FE7B:C132 + +10. **Terminate the current NAN Datapath** +>nan> ndp -T -d 5 +>d - NDP id + +## NAN Datapath Example Output using defaults +Device 1 : Publish a service with default configuration - + +``` +nan> nan -S +I (28125) NAN: State Init => Scanning + +I (31135) NAN: Start own Cluster, Id 50:6f:9a:01:0e:76 +I (31135) NAN: State Scanning => AnchorMaster + +I (31135) nan_app: NAN Discovery started. +nan> +nan> publish +I (77245) nan_console: Publishing service 'ESP_NAN-Service' [Publish id - 5] +nan> +I (21775) nan_app: NDP confirmed with NDP id 1 [Peer IPv6 - FE80::E2E2:E6FF:FE6A:7AFE] +I (23625) nan_app: NAN Data Interface ready [IPv6 - fe80:0000:0000:0000:e2e2:e6ff:fe7b:c132, type - LINK_LOCAL] +I (30265) nan_app: NDP id 1 with Peer e0:e2:e6:6a:7a:fe terminated (reason: 0) +``` + +Device 2 : Subscribe a service with default configuration and start a datapath with matched Publisher + +``` +nan> nan -S +I (142915) NAN: State Init => Scanning + +I (145915) NAN: Join Cluster with Id 50:6f:9a:01:0e:76 +I (145915) NAN: State Scanning => AnchorMaster + +I (145915) nan_app: NAN Discovery started. +nan> +nan> subscribe +I (159685) nan_console: Subscribed to Service 'ESP_NAN-Service' [Subscribe id - 5] +nan> I (159845) nan_app: Service matched with e0:e2:e6:7b:c1:32 [Peer Publish id - 5] +nan> +nan> ndp -I -p 5 +I (196125) nan_console: Initiated NDP with e0:e2:e6:7b:c1:32 [NDP id - 1] +nan> E (196425) wifi:Committed slots 0x7fff0000 for Peer e0:e2:e6:7b:c1:32 on Channel 6 +I (196425) nan_app: NDP confirmed with NDP id 1 [Peer IPv6 - FE80::E2E2:E6FF:FE7B:C132] +I (197625) nan_app: NAN Data Interface ready [IPv6 - fe80:0000:0000:0000:e2e2:e6ff:fe6a:7afe, type - LINK_LOCAL] +nan> ping FE80::E2E2:E6FF:FE7B:C132 +I (212975) nan_console: Pinging Peer with IPv6 addr FE80::E2E2:E6FF:FE7B:C132 +nan> W (213495) wifi:idx:5 (ifx:2, e0:e2:e6:7b:c1:32), tid:0, ssn:0, winSize:64 +I (213555) nan_console: 64 bytes from FE80::E2E2:E6FF:FE7B:C132 icmp_seq=1 ttl=0 time=577 ms +I (214015) nan_console: 64 bytes from FE80::E2E2:E6FF:FE7B:C132 icmp_seq=2 ttl=0 time=40 ms +I (215095) nan_console: 64 bytes from FE80::E2E2:E6FF:FE7B:C132 icmp_seq=3 ttl=0 time=121 ms +I (216165) nan_console: 64 bytes from FE80::E2E2:E6FF:FE7B:C132 icmp_seq=4 ttl=0 time=190 ms +I (217445) nan_console: 64 bytes from FE80::E2E2:E6FF:FE7B:C132 icmp_seq=5 ttl=0 time=475 ms +I (217975) nan_console: +--- FE80::E2E2:E6FF:FE7B:C132 ping statistics --- +I (217975) nan_console: 5 packets transmitted, 5 received, 0% packet loss, time 1403ms +nan> +nan> ndp -T -d 1 +nan> I (28175) nan_app: NDP id 1 with Peer e0:e2:e6:7b:c1:32 terminated (reason: 0) +``` + +## NAN Follow-up example output using advanced commands + +Device 1 : Publisher uses a filter and responds to a message from a Subscriber +``` +nan> nan -S +I (142915) NAN: State Init => Scanning + +I (145915) NAN: Join Cluster with Id 50:6f:9a:01:0e:76 +I (145915) NAN: State Scanning => AnchorMaster + +I (145915) nan_app: NAN Discovery started. +nan> +nan> publish -n TEST -f GREEN +I (61935) nan_console: Publishing service 'TEST' [Publish id - 5] +nan> +I (93635) nan_app: Received message 'Hello' from Peer e0:e2:e6:6a:7a:fe [Peer Service id - 5] +nan> send -p 5 -t Welcome +I (114605) nan_console: Sent message 'Welcome' to NAN Peer e0:e2:e6:6a:7a:fe. +``` + +Device 2 : Subscriber uses a filter and sends a message to the matched Publisher +``` +nan> nan -S +I (142915) NAN: State Init => Scanning + +I (145915) NAN: Join Cluster with Id 50:6f:9a:01:0e:76 +I (145915) NAN: State Scanning => AnchorMaster + +I (145915) nan_app: NAN Discovery started. +nan> +nan> subscribe -n TEST -f GREEN +I (159685) nan_console: Subscribed to Service 'TEST' [Subscribe id - 5] +nan> I (159845) nan_app: Service matched with e0:e2:e6:7b:c1:32 [Peer Publish id - 5] +nan> send -p 5 -t Hello +I (99445) nan_console: Sent message 'Hello' to NAN Peer e0:e2:e6:7b:c1:32. +nan> +I (120685) nan_app: Received message 'Welcome' from Peer e0:e2:e6:7b:c1:32 [Peer Service id - 5] +``` diff --git a/examples/wifi/wifi_aware/nan_console/main/CMakeLists.txt b/examples/wifi/wifi_aware/nan_console/main/CMakeLists.txt new file mode 100644 index 0000000000..ce2ee7b1ae --- /dev/null +++ b/examples/wifi/wifi_aware/nan_console/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "nan_main.c" + INCLUDE_DIRS ".") diff --git a/examples/wifi/wifi_aware/nan_console/main/nan_main.c b/examples/wifi/wifi_aware/nan_console/main/nan_main.c new file mode 100644 index 0000000000..ad353148e1 --- /dev/null +++ b/examples/wifi/wifi_aware/nan_console/main/nan_main.c @@ -0,0 +1,621 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* NAN Console 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 "nvs_flash.h" +#include "cmd_system.h" +#include "argtable3/argtable3.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_err.h" +#include "esp_wifi.h" +#include "esp_nan.h" +#include "esp_mac.h" +#include "esp_console.h" +#include "esp_private/wifi.h" +#include "esp_netif.h" +#include "esp_supplicant_utils.h" + +#include "ping/ping_sock.h" +#include "lwip/inet.h" +#include "lwip/netdb.h" +#include "lwip/sockets.h" + +typedef struct { + /* NAN Discovery parameters */ + struct arg_lit *init; + struct arg_int *master_pref; + struct arg_int *op_channel; + struct arg_int *warmup_time; + struct arg_lit *deinit; + struct arg_end *end; +} wifi_nan_args_t; + +static wifi_nan_args_t nan_args; + +typedef struct { + /* NAN Publish parameters */ + struct arg_str *name; + struct arg_int *type; + struct arg_str *filter; + struct arg_lit *ndp_ask; + struct arg_lit *cancel; + struct arg_int *id; + struct arg_end *end; +} wifi_publish_args_t; + +static wifi_publish_args_t pub_args; + +typedef struct { + /* NAN Subscribe parameters */ + struct arg_str *name; + struct arg_int *type; + struct arg_str *filter; + struct arg_lit *cancel; + struct arg_int *id; + struct arg_end *end; +} wifi_subscribe_args_t; + +static wifi_subscribe_args_t sub_args; + +typedef struct { + /* NAN Follow-up parameters */ + struct arg_int *own_id; + struct arg_int *peer_id; + struct arg_str *mac_addr; + struct arg_str *text; + struct arg_end *end; +} wifi_followup_args_t; + +static wifi_followup_args_t fup_args; + +typedef struct { + /* NDP Init/Deinit parameters */ + struct arg_lit *init; + struct arg_int *peer_pub_id; + struct arg_str *mac_addr; + /* NDP Accept/Reject parameters */ + struct arg_lit *accept; + struct arg_lit *reject; + /* NDP Terminate parameters */ + struct arg_lit *terminate; + struct arg_int *ndp_id; + struct arg_end *end; +} wifi_ndp_args_t; + +static wifi_ndp_args_t ndp_args; + +static struct { + struct arg_str *host; + struct arg_end *end; +} ping_args; + +static const char *TAG = "nan_console"; +static esp_netif_t *g_nan_netif; + +#define NAN_EXAMPLE_SERVICE_NAME "ESP_NAN-Service" + +static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args) +{ + uint8_t ttl; + uint16_t seqno; + uint32_t elapsed_time, recv_len; + ip_addr_t target_addr; + esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); + esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len)); + esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time)); + ESP_LOGI(TAG, "%lu bytes from %s icmp_seq=%u ttl=%u time=%lu ms", + recv_len, ipaddr_ntoa((ip_addr_t *)&target_addr), seqno, ttl, elapsed_time); +} + +static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args) +{ + uint16_t seqno; + ip_addr_t target_addr; + esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + ESP_LOGI(TAG, "From %s icmp_seq=%d timeout", ipaddr_ntoa((ip_addr_t *)&target_addr), seqno); +} + +static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args) +{ + ip_addr_t target_addr; + uint32_t transmitted; + uint32_t received; + uint32_t total_time_ms; + esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted)); + esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms)); + uint32_t loss = (uint32_t)((1 - ((float)received) / transmitted) * 100); + ESP_LOGI(TAG, "\n--- %s ping statistics ---", inet6_ntoa(*ip_2_ip6(&target_addr))); + ESP_LOGI(TAG, "%lu packets transmitted, %lu received, %lu%% packet loss, time %lums", + transmitted, received, loss, total_time_ms); + // delete the ping sessions, so that we clean up all resources and can create a new ping session + // we don't have to call delete function in the callback, instead we can call delete function from other tasks + esp_ping_delete_session(hdl); +} + +static int do_ping_cmd(int argc, char **argv) +{ + esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG(); + config.task_stack_size = 4096; + ip_addr_t target_addr = {0}; + + int nerrors = arg_parse(argc, argv, (void **)&ping_args); + if (nerrors != 0) { + arg_print_errors(stderr, ping_args.end, argv[0]); + return 1; + } + + if (!g_nan_netif) { + ESP_LOGE(TAG, "NAN not started successfully"); + return 1; + } + + if (ping_args.host->count) { + /* convert ip6 string to ip6 address */ + ipaddr_aton(ping_args.host->sval[0], &target_addr); + } else { + ESP_LOGE(TAG, "No Active datapath for ping"); + return 1; + } + + config.target_addr = target_addr; + config.interface = esp_netif_get_netif_impl_index(g_nan_netif); + + /* set callback functions */ + esp_ping_callbacks_t cbs = { + .on_ping_success = cmd_ping_on_ping_success, + .on_ping_timeout = cmd_ping_on_ping_timeout, + .on_ping_end = cmd_ping_on_ping_end, + .cb_args = NULL + }; + esp_ping_handle_t ping; + if (esp_ping_new_session(&config, &cbs, &ping) == ESP_OK) { + ESP_LOGI(TAG, "Pinging Peer with IPv6 addr %s", ipaddr_ntoa((ip_addr_t *)&target_addr)); + esp_ping_start(ping); + return 0; + } else { + ESP_LOGI(TAG, "Failed to ping Peer with IPv6 addr %s", ipaddr_ntoa((ip_addr_t *)&target_addr)); + return 1; + } +} + +void initialise_wifi(void) +{ + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); + ESP_ERROR_CHECK(esp_wifi_start()); +} + +static int wifi_cmd_nan_disc(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &nan_args); + esp_err_t ret; + + if (nerrors != 0) { + arg_print_errors(stderr, nan_args.end, argv[0]); + return 1; + } + + if ((nan_args.init->count == 0) && (nan_args.deinit->count == 0)) { + ESP_LOGE(TAG, "Invalid NAN Discovery command"); + return 1; + } + + if (nan_args.init->count) { + wifi_nan_config_t nan_cfg = WIFI_NAN_CONFIG_DEFAULT(); + + if (nan_args.master_pref->count) { + nan_cfg.master_pref = nan_args.master_pref->ival[0]; + } + if (nan_args.op_channel->count) { + nan_cfg.op_channel = nan_args.op_channel->ival[0]; + } + if (nan_args.warmup_time->count) { + nan_cfg.warm_up_sec = nan_args.warmup_time->ival[0]; + } + + g_nan_netif = esp_netif_create_default_wifi_nan(); + if ((esp_wifi_nan_start(&nan_cfg)) != ESP_OK) { + ESP_LOGI(TAG, "Failed to start NAN"); + esp_netif_destroy_default_wifi(g_nan_netif); + return 1; + } + return 0; + } + + if (nan_args.deinit->count) { + ret = esp_wifi_nan_stop(); + + if (ret != ESP_OK) { + ESP_LOGI(TAG, "Failed to stop NAN"); + return 1; + } + esp_netif_destroy_default_wifi(g_nan_netif); + } + + return 0; +} + +static int wifi_cmd_nan_publish(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &pub_args); + uint32_t pub_id; + bool ndp_resp_needed = false; + + if (nerrors != 0) { + arg_print_errors(stderr, pub_args.end, argv[0]); + return 1; + } + + if (pub_args.cancel->count && pub_args.id->count) { + pub_id = pub_args.id->ival[0]; + if (esp_wifi_nan_cancel_service(pub_id) != ESP_OK) { + ESP_LOGE(TAG, "Failed to cancel service"); + return 1; + } + return 0; + } + + wifi_nan_publish_cfg_t publish = { + .service_name = NAN_EXAMPLE_SERVICE_NAME, + .type = NAN_PUBLISH_UNSOLICITED, + .single_replied_event = 1, + }; + + if (pub_args.name->count) { + strlcpy(publish.service_name, pub_args.name->sval[0], ESP_WIFI_MAX_SVC_NAME_LEN); + } + + if (pub_args.type->count && pub_args.type->ival[0] == 1) { + publish.type = NAN_PUBLISH_SOLICITED; + } + + if (pub_args.filter->count) { + strlcpy(publish.matching_filter, pub_args.filter->sval[0], ESP_WIFI_MAX_SVC_NAME_LEN); + } + + if (pub_args.ndp_ask->count) { + ndp_resp_needed = true; + ESP_LOGI(TAG, "Issue 'ndp -A -d [id]' to Accept OR 'ndp -R -d [id]' to Reject incoming NDP requests"); + } + + if (!esp_wifi_nan_publish_service(&publish, ndp_resp_needed)) { + return 1; + } + + return 0; +} + +static int wifi_cmd_nan_subscribe(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &sub_args); + uint32_t sub_id; + + if (nerrors != 0) { + arg_print_errors(stderr, sub_args.end, argv[0]); + return 1; + } + + if (sub_args.cancel->count && sub_args.id->count) { + sub_id = sub_args.id->ival[0]; + if (esp_wifi_nan_cancel_service(sub_id) != ESP_OK) { + ESP_LOGE(TAG, "Failed to cancel service"); + return 1; + } + return 0; + } + + wifi_nan_subscribe_cfg_t subscribe = { + .service_name = NAN_EXAMPLE_SERVICE_NAME, + .type = NAN_SUBSCRIBE_PASSIVE, + .single_match_event = true, + }; + + if (sub_args.name->count) { + strlcpy(subscribe.service_name, sub_args.name->sval[0], ESP_WIFI_MAX_SVC_NAME_LEN); + } + + if (sub_args.type->count && sub_args.type->ival[0] == 1) { + subscribe.type = NAN_SUBSCRIBE_ACTIVE; + } + + if (sub_args.filter->count) { + strlcpy(subscribe.matching_filter, sub_args.filter->sval[0], ESP_WIFI_MAX_SVC_NAME_LEN); + } + + if (!esp_wifi_nan_subscribe_service(&subscribe)) { + return 1; + } + + return 0; +} + +static int wifi_cmd_nan_followup(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &fup_args); + + if (nerrors != 0) { + arg_print_errors(stderr, fup_args.end, argv[0]); + return 1; + } + + wifi_nan_followup_params_t fup = {0}; + if (fup_args.own_id->count) { + fup.inst_id = fup_args.own_id->ival[0]; + } + + if (!fup_args.peer_id->count && !fup_args.mac_addr->count) { + ESP_LOGE(TAG, "Missing peer's service instance id or peer's MAC."); + return 1; + } + if (fup_args.peer_id->count) { + fup.peer_inst_id = fup_args.peer_id->ival[0]; + } + if (fup_args.mac_addr->count && + esp_supplicant_str_to_mac((char *)fup_args.mac_addr->sval[0], fup.peer_mac) != ESP_OK) { + return 1; + } + + if (fup_args.text->count) { + strlcpy(fup.svc_info, fup_args.text->sval[0], ESP_WIFI_MAX_SVC_INFO_LEN); + } + + if (esp_wifi_nan_send_message(&fup) != ESP_OK) { + return 1; + } + + return 0; +} + +static int wifi_cmd_ndp(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &ndp_args); + + if (nerrors != 0) { + arg_print_errors(stderr, ndp_args.end, argv[0]); + return 1; + } + + if ((ndp_args.init->count == 0) && (ndp_args.terminate->count == 0) && + (ndp_args.accept->count == 0) && (ndp_args.reject->count == 0)) { + ESP_LOGE(TAG, "Invalid NDP command"); + return 1; + } + + if (ndp_args.init->count) { + wifi_nan_datapath_req_t ndp_req = {0}; + ndp_req.confirm_required = true; + + if (!ndp_args.peer_pub_id->count && !ndp_args.mac_addr->count) { + ESP_LOGE(TAG, "Missing Peer's publish id or peer's MAC"); + return 1; + } + if (ndp_args.peer_pub_id->count) { + ndp_req.pub_id = ndp_args.peer_pub_id->ival[0]; + } + if (ndp_args.mac_addr->count && + esp_supplicant_str_to_mac((char *)ndp_args.mac_addr->sval[0], ndp_req.peer_mac) != ESP_OK) { + return 1; + } + + if (!esp_wifi_nan_datapath_req(&ndp_req)) { + return 1; + } + goto out; + } + + if (ndp_args.accept->count || ndp_args.reject->count) { + wifi_nan_datapath_resp_t ndp_resp = {0}; + ndp_resp.accept = ndp_args.accept->count ? true : false; + if (ndp_args.ndp_id->count) { + ndp_resp.ndp_id = ndp_args.ndp_id->ival[0]; + } else { + ESP_LOGE(TAG, "Missing own NDP id, add using '-d' parameter"); + return 1; + } + + esp_wifi_nan_datapath_resp(&ndp_resp); + goto out; + } + + if (ndp_args.terminate->count) { + wifi_nan_datapath_end_req_t ndp_end = {0}; + if (ndp_args.ndp_id->count) { + ndp_end.ndp_id = ndp_args.ndp_id->ival[0]; + } else { + ESP_LOGE(TAG, "Missing own NDP id, add using '-d' parameter"); + return 1; + } + + esp_wifi_nan_datapath_end(&ndp_end); + goto out; + } + +out: + return 0; +} + + +void register_nan(void) +{ + /* NAN Init/Deinit parameters */ + nan_args.init = arg_lit0("S", "start", "NAN Start"); + nan_args.deinit = arg_lit0("T", "stop", "NAN Stop"); + nan_args.master_pref = arg_int0("p", "mast_pref", "<1-254>", "NAN Master Preference"); + nan_args.op_channel = arg_int0("c", "op_chan", "<1-11>", "NAN Operating Channe"); + nan_args.warmup_time = arg_int0("w", "warmup", "<5-120>", "NAN Warmup Time in Sec"); + nan_args.end = arg_end(1); + + const esp_console_cmd_t nan_cmd = { + .command = "nan", + .help = "NAN Discovery command", + .hint = NULL, + .func = &wifi_cmd_nan_disc, + .argtable = &nan_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&nan_cmd) ); + + /* NAN Publish parameters */ + pub_args.name = arg_str0("n", "name", "", "Name for the service"); + pub_args.type = arg_int0("t", "type", "<0/1>", "0 - Unsolicited(Default), 1 - Solicited"); + pub_args.filter = arg_str0("f", "filter", "", "Comma separated Matching Filter"); + pub_args.ndp_ask = arg_lit0("a", "ndp_ask", "Explicitly Accept OR Reject incoming NDP Requests"); + /* NAN Publish cancel parameters */ + pub_args.cancel = arg_lit0("C", "cancel", "Cancel a service"); + pub_args.id = arg_int0("i", "id", "<0-255>", "Publish service id"); + pub_args.end = arg_end(1); + + const esp_console_cmd_t pub_cmd = { + .command = "publish", + .help = "NAN Publish Service command", + .hint = NULL, + .func = &wifi_cmd_nan_publish, + .argtable = &pub_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&pub_cmd) ); + + /* NAN Subscribe parameters */ + sub_args.name = arg_str0("n", "name", "", "Name for the service"); + sub_args.type = arg_int0("t", "type", "<0/1>", "0 - Passive(Default), 1 - Active"); + sub_args.filter = arg_str0("f", "filter", "", "Comma separated Matching Filter"); + /* NAN Subscribe cancel parameters */ + sub_args.cancel = arg_lit0("C", "cancel", "Cancel a service"); + sub_args.id = arg_int0("i", "id", "<0-255>", "Subscribe service id"); + sub_args.end = arg_end(1); + + const esp_console_cmd_t sub_cmd = { + .command = "subscribe", + .help = "NAN Subscribe to Service command", + .hint = NULL, + .func = &wifi_cmd_nan_subscribe, + .argtable = &sub_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&sub_cmd) ); + + /* NAN Follow-up parameters */ + fup_args.own_id = arg_int0("i", "own_id", "<0-255>", "Own service id"); + fup_args.peer_id = arg_int0("p", "peer_id", "<0-255>", "Peer's service id"); + fup_args.mac_addr = arg_str0("m", "mac", "", "Peer's MAC Address"); + fup_args.text = arg_str1("t", "text", "", "Text to be shared as Service Info"); + fup_args.end = arg_end(1); + + const esp_console_cmd_t fup_cmd = { + .command = "send", + .help = "NAN Follow-up command", + .hint = NULL, + .func = &wifi_cmd_nan_followup, + .argtable = &fup_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&fup_cmd) ); + + /* NDP Init/Deinit parameters */ + ndp_args.init = arg_lit0("I", "initiate", "NDP Initiate"); + ndp_args.peer_pub_id = arg_int0("p", "peer_pub_id", "<1-254>", "Peer's Publish Id"); + ndp_args.mac_addr = arg_str0("m", "mac", "", "Peer's MAC Address"); + /* NDP Accept/Reject/Terminate parameters */ + ndp_args.accept = arg_lit0("A", "accept", "Accept NDP Request"); + ndp_args.reject = arg_lit0("R", "reject", "Reject NDP Request"); + ndp_args.terminate = arg_lit0("T", "terminate", "NDP Terminate"); + ndp_args.ndp_id = arg_int0("d", "ndp_id", "<1-254>", "NDP ID"); + ndp_args.end = arg_end(1); + + const esp_console_cmd_t ndp_cmd = { + .command = "ndp", + .help = "NDP command", + .hint = NULL, + .func = &wifi_cmd_ndp, + .argtable = &ndp_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&ndp_cmd) ); + + ping_args.host = arg_str1(NULL, NULL, "", "Host address"); + ping_args.end = arg_end(1); + + const esp_console_cmd_t ping_cmd = { + .command = "ping", + .help = "send ICMP ECHO_REQUEST to network hosts", + .hint = NULL, + .func = &do_ping_cmd, + .argtable = &ping_args + }; + + ESP_ERROR_CHECK(esp_console_cmd_register(&ping_cmd)); +} + +void app_main(void) +{ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK( ret ); + + initialise_wifi(); + + esp_console_repl_t *repl = NULL; + esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); + esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); + repl_config.prompt = "nan>"; + // init console REPL environment + ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl)); + /* Register commands */ + register_system(); + register_nan(); + + printf("\n ==================================================================\n"); + printf(" | Basic Steps to test NAN Discovery & Datapath |\n"); + printf(" | |\n"); + printf(" | Below are short commands that use defaults, use 'help' |\n"); + printf(" | to see detailed command parameters available |\n"); + printf(" | |\n"); + printf(" | # NAN Discovery - |\n"); + printf(" | 1. To start NAN issue 'nan -S', to stop it use 'nan -T' |\n"); + printf(" | 2. Publish OR Subscribe a service with name 'TEST' - |\n"); + printf(" | > publish -n TEST |\n"); + printf(" | > subscribe -n TEST |\n"); + printf(" | 4. After service match, send a text to Peer with service id 5 - |\n"); + printf(" | > send -p 5 -t Hello |\n"); + printf(" | 5. Cancel an ongoing service with id 5 - |\n"); + printf(" | > publish -C -i 5 |\n"); + printf(" | |\n"); + printf(" | # NAN Datapath - |\n"); + printf(" | 1. Subscriber can initiate datapath using 'ndp -I -p [pub_id]' |\n"); + printf(" | 2. After NDP setup, use 'ping [Peer's IPv6]' to test datapath |\n"); + printf(" | 3. Terminate the NDP using 'ndp -T' |\n"); + printf(" | |\n"); + printf(" ====================================================================\n\n"); + + // start console REPL + ESP_ERROR_CHECK(esp_console_start_repl(repl)); +} diff --git a/examples/wifi/wifi_aware/nan_console/sdkconfig.defaults b/examples/wifi/wifi_aware/nan_console/sdkconfig.defaults new file mode 100644 index 0000000000..be625a880c --- /dev/null +++ b/examples/wifi/wifi_aware/nan_console/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_ESP_WIFI_NAN_ENABLE=y diff --git a/examples/wifi/wifi_aware/nan_publisher/CMakeLists.txt b/examples/wifi/wifi_aware/nan_publisher/CMakeLists.txt new file mode 100644 index 0000000000..1b0072c926 --- /dev/null +++ b/examples/wifi/wifi_aware/nan_publisher/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following five 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(nan_publisher) diff --git a/examples/wifi/wifi_aware/nan_publisher/README.md b/examples/wifi/wifi_aware/nan_publisher/README.md new file mode 100644 index 0000000000..ea3f6d1b07 --- /dev/null +++ b/examples/wifi/wifi_aware/nan_publisher/README.md @@ -0,0 +1,55 @@ +| Supported Targets | ESP32 | ESP32-S2 | +| ----------------- | ----- | -------- | + +# NAN Publisher Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +## Introduction +Neighbor Awareness Networking (NAN) is a protocol that allows Wi-Fi devices to discover services in their proximity. NAN uses direct device-to-device communication and does not require any Internet or AP connection. +Multiple NAN devices which are in the vicinity will form a NAN cluster which allows them to communicate with each other. Devices within a NAN cluster can advertise or look for services using NAN Service Discovery protocols. +A Publisher advertises a service and a Subscriber searches for a service. A subscriber either listens passively (Passive mode) or sends out broadcast Action frames (Active mode). Similarly a publisher either broadcasts its services (Unsolicited mode) or replies to Active subscribers (Solicited Mode). Matching of services is done by service name and optionally matching filters. + +## How to use example +Publishing and Subscribing is possible dynamically on the NAN Interface. For simplicity this example demonstrates only the Publish method with some configuration options. Use option `Example Configuration -> NAN Service Name` for naming the Publish service. Select `Publish Type` and optionally set the `Matching Filter` and `Reply message` in the same menu. Another device in the vicinity running the Subscriber example will discover the Publisher. A service match happens when the service name (case insensitive) matches as well as the Matching filter(s) matche(s) (case sensitive). After this the Subscriber may send a Follow-up or initiate Datapath with the Publisher for further communication. The Publisher will send a reply to the Follow-up and will accept the Datapath request given that it has enough resources. + +## Example Output + +Usecase 1 : Publisher with a Follow-up exchange with a Subscriber +``` +I (449) phy_init: phy_version 1800,e7ef680,Apr 13 2021,11:45:08 +I (549) wifi:mode : NAN (18:fe:34:72:50:c8) +Set Vif NAN +I (559) NAN: State Init => Scanning + +I (3559) NAN: Start own Cluster, Id 50:6f:9a:01:cc:4d +I (3559) wifi:Init max length of beacon: 752/752 +I (3559) wifi:Init max length of beacon: 752/752 +I (3559) NAN: State Scanning => AnchorMaster + +I (3794) nan_app: NAN Discovery started. +I (5604) NAN: State Master => AnchorMaster + +I (8244) nan_app: Received message 'Hello' from Peer e0:e2:e6:6a:7a:fe [Peer Service id - 5] +I (8244) nan_pub: Replied 'Welcome' to the Subscriber... +``` + +Usecase 2 : Publisher accepting Datapath request by a Subscriber +``` +I (449) phy_init: phy_version 1800,e7ef680,Apr 13 2021,11:45:08 +I (559) wifi:mode : NAN (18:fe:34:72:50:c8) +Set Vif NAN +I (559) NAN: State Init => Scanning + +I (3559) NAN: Join Cluster with Id 50:6f:9a:01:cc:4d +I (3559) wifi:Init max length of beacon: 752/752 +I (3559) wifi:Init max length of beacon: 752/752 +I (3559) NAN: State Scanning => Master + +I (3559) nan_pub: NAN Discovery started. +I (3569) nan_pub: Publishing Service test with ID 5 +E (18809) wifi:Committed slots 0x3fff0000 for Peer 18:fe:34:72:50:b3 on Channel 6 +I (18809) nan_pub: Datapath setup with 18:fe:34:72:50:b3 +I (20349) nan_pub: Got IPv6 event: Interface "nan_pub: nan" address: fe80:0000:0000:0000:1afe:34ff:fe72:50ca, type: ESP_IP6_ADDR_IS_LINK_LOCAL +W (20929) wifi:idx:5 (ifx:2, 18:fe:34:72:50:b3), tid:0, ssn:0, winSize:64 +``` diff --git a/examples/wifi/wifi_aware/nan_publisher/main/CMakeLists.txt b/examples/wifi/wifi_aware/nan_publisher/main/CMakeLists.txt new file mode 100644 index 0000000000..297c8de71e --- /dev/null +++ b/examples/wifi/wifi_aware/nan_publisher/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "publisher_main.c" + INCLUDE_DIRS ".") diff --git a/examples/wifi/wifi_aware/nan_publisher/main/Kconfig.projbuild b/examples/wifi/wifi_aware/nan_publisher/main/Kconfig.projbuild new file mode 100644 index 0000000000..1e410615f6 --- /dev/null +++ b/examples/wifi/wifi_aware/nan_publisher/main/Kconfig.projbuild @@ -0,0 +1,26 @@ +menu "Example Configuration" + + config ESP_WIFI_NAN_SVC_NAME + string "NAN Service Name" + default "test" + + choice + prompt "Publish Type" + default EXAMPLE_NAN_PUBLISH_UNSOLICITED + config EXAMPLE_NAN_PUBLISH_UNSOLICITED + bool "UNSOLICITED" + config EXAMPLE_NAN_PUBLISH_SOLICITED + bool "SOLICITED" + endchoice + + config ESP_WIFI_NAN_MATCHING_FILTER + string "Matching Filter" + default "" + + config ESP_WIFI_NAN_SERVICE_MESSAGE + string "Reply message" + default "Welcome" + help + Send a reply to the Follow Up sent by a Subscriber + +endmenu diff --git a/examples/wifi/wifi_aware/nan_publisher/main/publisher_main.c b/examples/wifi/wifi_aware/nan_publisher/main/publisher_main.c new file mode 100644 index 0000000000..fb30028864 --- /dev/null +++ b/examples/wifi/wifi_aware/nan_publisher/main/publisher_main.c @@ -0,0 +1,119 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* WiFi Aware (NAN) Publisher 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 "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_nan.h" +#include "esp_mac.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" + +#define EXAMPLE_NAN_SERV_NAME CONFIG_ESP_WIFI_NAN_SVC_NAME +#define EXAMPLE_NAN_MATCHING_FILTER CONFIG_ESP_WIFI_NAN_MATCHING_FILTER +#ifdef CONFIG_ESP_WIFI_NAN_SERVICE_MESSAGE +#define EXAMPLE_NAN_SVC_MSG CONFIG_ESP_WIFI_NAN_SERVICE_MESSAGE +#else +#define EXAMPLE_NAN_SVC_MSG "Welcome" +#endif + +static EventGroupHandle_t nan_event_group; + +static int NAN_RECEIVE = BIT0; +uint8_t g_peer_inst_id; + +static void nan_receive_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + wifi_event_nan_receive_t *evt = (wifi_event_nan_receive_t *)event_data; + g_peer_inst_id = evt->peer_inst_id; + xEventGroupSetBits(nan_event_group, NAN_RECEIVE); +} + +void wifi_nan_publish(void) +{ + nan_event_group = xEventGroupCreate(); + esp_event_handler_instance_t instance_any_id; + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + WIFI_EVENT_NAN_RECEIVE, + &nan_receive_event_handler, + NULL, + &instance_any_id)); + + /* Start NAN Discovery */ + wifi_nan_config_t nan_cfg = WIFI_NAN_CONFIG_DEFAULT(); + + esp_netif_create_default_wifi_nan(); + esp_wifi_nan_start(&nan_cfg); + + /* Publish a service */ + uint8_t pub_id; + wifi_nan_publish_cfg_t publish_cfg = { + .service_name = EXAMPLE_NAN_SERV_NAME, +#if CONFIG_EXAMPLE_NAN_PUBLISH_UNSOLICITED + .type = NAN_PUBLISH_UNSOLICITED, +#else + .type = NAN_PUBLISH_SOLICITED, +#endif + .matching_filter = EXAMPLE_NAN_MATCHING_FILTER, + .single_replied_event = 1, + }; + + pub_id = esp_wifi_nan_publish_service(&publish_cfg, false); + if (pub_id == 0) { + return; + } + + while (1) { + EventBits_t bits = xEventGroupWaitBits(nan_event_group, NAN_RECEIVE, pdFALSE, pdFALSE, portMAX_DELAY); + if (bits & NAN_RECEIVE) { + xEventGroupClearBits(nan_event_group, NAN_RECEIVE); + wifi_nan_followup_params_t fup = {0}; + fup.inst_id = pub_id, + fup.peer_inst_id = g_peer_inst_id, + strlcpy(fup.svc_info, EXAMPLE_NAN_SVC_MSG, ESP_WIFI_MAX_SVC_INFO_LEN); + + /* Reply to the message from a subscriber */ + esp_wifi_nan_send_message(&fup); + } + vTaskDelay(10); + } +} + +void initialise_wifi(void) +{ + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM) ); +} + +void app_main(void) +{ + //Initialize NVS + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + initialise_wifi(); + + wifi_nan_publish(); +} diff --git a/examples/wifi/wifi_aware/nan_publisher/sdkconfig.defaults b/examples/wifi/wifi_aware/nan_publisher/sdkconfig.defaults new file mode 100644 index 0000000000..be625a880c --- /dev/null +++ b/examples/wifi/wifi_aware/nan_publisher/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_ESP_WIFI_NAN_ENABLE=y diff --git a/examples/wifi/wifi_aware/nan_subscriber/CMakeLists.txt b/examples/wifi/wifi_aware/nan_subscriber/CMakeLists.txt new file mode 100644 index 0000000000..d682097459 --- /dev/null +++ b/examples/wifi/wifi_aware/nan_subscriber/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following five 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(nan_subscriber) diff --git a/examples/wifi/wifi_aware/nan_subscriber/README.md b/examples/wifi/wifi_aware/nan_subscriber/README.md new file mode 100644 index 0000000000..013770468b --- /dev/null +++ b/examples/wifi/wifi_aware/nan_subscriber/README.md @@ -0,0 +1,65 @@ +| Supported Targets | ESP32 | ESP32-S2 | +| ----------------- | ----- | -------- | + +# NAN Subscriber Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +## Introduction +Neighbor Awareness Networking (NAN) is a protocol that allows Wi-Fi devices to discover services in their proximity. NAN uses direct device-to-device communication and does not require any Internet or AP connection. +Multiple NAN devices which are in the vicinity will form a NAN cluster which allows them to communicate with each other. Devices within a NAN cluster can advertise or look for services using NAN Service Discovery protocols. +A Publisher advertises a service and a Subscriber searches for a service. A subscriber either listens passively (Passive mode) or sends out broadcast Action frames (Active mode). Similarly a publisher either broadcasts its services (Unsolicited mode) or replies to Active subscribers (Solicited Mode). Matching of services is done by service name and optionally matching filters. + +## How to use example +Publishing and Subscribing is possible dynamically on the NAN Interface. For simplicity this example demonstrates only the Subscribe method with some configuration options. Use option `Example Configuration -> NAN Service Name` for naming a service to subscribe to. Select `Subscribe Type` and optionally set the `Matching Filter`. Using option `Communication Type` it's possible to either send a message using Follow-up or initiate a datapath with the Publisher. The device will search for another device in the vicinity Publishing the same service. A service match happens when the service name (case insensitive) matches as well as the Matching filter(s) matche(s) (case sensitive). After this the Subscriber will initiate further communication as par configuration. + +## Example Output + +Usecase 1 : Subscriber sending a Follow-up to the Publisher +``` +I (449) phy_init: phy_version 1800,e7ef680,Apr 13 2021,11:45:08 +I (549) wifi:mode : NAN (18:fe:34:72:50:b1) +Set Vif NAN +I (549) NAN: State Init => Scanning + +I (3549) NAN: Join Cluster with Id 50:6f:9a:01:cc:4d +I (3549) wifi:Init max length of beacon: 752/752 +I (3549) wifi:Init max length of beacon: 752/752 +I (3559) NAN: State Scanning => AnchorMaster + +I (3762) nan_app: NAN Discovery started. +I (4222) nan_sub: NAN Publisher found for Serv ID 5 +I (4222) nan_app: Service matched with e0:e2:e6:7b:c1:32 [Peer Publish id - 5] +I (4222) nan_sub: Sending message 'Hello' to Publisher e0:e2:e6:7b:c1:32 ... +I (4762) nan_app: Received message 'Welcome' from Peer e0:e2:e6:7b:c1:32 [Peer Service id - 5] +``` + +Usecase 2 : Subscriber setting up Datapath with the Publisher, then testing IPv6 ping +``` +I (452) phy_init: phy_version 1800,e7ef680,Apr 13 2021,11:45:08 +I (552) wifi:mode : NAN (18:fe:34:72:50:b1) +Set Vif NAN +I (552) NAN: State Init => Scanning + +I (3552) NAN: Join Cluster with Id 50:6f:9a:01:cc:4d +I (3562) wifi:Init max length of beacon: 752/752 +I (3562) wifi:Init max length of beacon: 752/752 +I (3562) NAN: State Scanning => AnchorMaster + +I (3562) nan_sub: NAN Discovery started. +I (3572) nan_sub: Subscribing to Service test with ID 5 +I (3612) nan_sub: NAN Publisher found for Serv ID 5 +E (3612) wifi:Committed slots 0x7fff0000 for Peer 18:fe:34:72:50:ca on Channel 6 +I (3622) nan_sub: Datapath setup with 18:fe:34:72:50:ca +I (5452) nan_sub: Got IPv6 event: Interface "nan_sub: nan" address: fe80:0000:0000:0000:1afe:34ff:fe72:50b3, type: ESP_IP6_ADDR_IS_LINK_LOCAL +I (5452) nan_sub: Pinging Peer with IPv6 addr FE80::1AFE:34FF:FE72:50CA +W (5722) wifi:idx:5 (ifx:2, 18:fe:34:72:50:ca), tid:0, ssn:0, winSize:64 +I (5742) nan_sub: 64 bytes from FE80::1AFE:34FF:FE72:50CA icmp_seq=1 ttl=0 time=278 ms +I (7462) nan_sub: 64 bytes from FE80::1AFE:34FF:FE72:50CA icmp_seq=2 ttl=0 time=2 ms +I (7462) nan_sub: 64 bytes from FE80::1AFE:34FF:FE72:50CA icmp_seq=3 ttl=0 time=2 ms +I (8462) nan_sub: 64 bytes from FE80::1AFE:34FF:FE72:50CA icmp_seq=4 ttl=0 time=1 ms +I (9462) nan_sub: 64 bytes from FE80::1AFE:34FF:FE72:50CA icmp_seq=5 ttl=0 time=1 ms +I (10462) nan_sub: +--- FE80::1AFE:34FF:FE72:50CA ping statistics --- +I (10462) nan_sub: 5 packets transmitted, 4 received, 19% packet loss, time 1281ms +``` diff --git a/examples/wifi/wifi_aware/nan_subscriber/main/CMakeLists.txt b/examples/wifi/wifi_aware/nan_subscriber/main/CMakeLists.txt new file mode 100644 index 0000000000..7d6b6a2e07 --- /dev/null +++ b/examples/wifi/wifi_aware/nan_subscriber/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "subscriber_main.c" + INCLUDE_DIRS ".") diff --git a/examples/wifi/wifi_aware/nan_subscriber/main/Kconfig.projbuild b/examples/wifi/wifi_aware/nan_subscriber/main/Kconfig.projbuild new file mode 100644 index 0000000000..0bba7d9338 --- /dev/null +++ b/examples/wifi/wifi_aware/nan_subscriber/main/Kconfig.projbuild @@ -0,0 +1,36 @@ +menu "Example Configuration" + + config ESP_WIFI_NAN_SVC_NAME + string "NAN Service Name" + default "test" + + choice + prompt "Subscribe Type" + default EXAMPLE_NAN_SUBSCRIBE_PASSIVE + config EXAMPLE_NAN_SUBSCRIBE_PASSIVE + bool "PASSIVE" + config EXAMPLE_NAN_SUBSCRIBE_ACTIVE + bool "ACTIVE" + endchoice + + config ESP_WIFI_NAN_MATCHING_FILTER + string "Matching Filter" + default "" + + choice + prompt "Communication Type" + default EXAMPLE_NAN_SEND_MESSAGE + config EXAMPLE_NAN_SEND_MESSAGE + bool "Send Message" + config EXAMPLE_NAN_SEND_PING + bool "IPv6 Ping" + endchoice + + config ESP_WIFI_NAN_SERVICE_MESSAGE + depends on EXAMPLE_NAN_SEND_MESSAGE + string "Message" + default "Hello" + help + Send a message to the Publisher using NAN Follow Up + +endmenu diff --git a/examples/wifi/wifi_aware/nan_subscriber/main/subscriber_main.c b/examples/wifi/wifi_aware/nan_subscriber/main/subscriber_main.c new file mode 100644 index 0000000000..f5fe8cc274 --- /dev/null +++ b/examples/wifi/wifi_aware/nan_subscriber/main/subscriber_main.c @@ -0,0 +1,240 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* WiFi Aware (NAN) Subscriber 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 "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_nan.h" +#include "esp_mac.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "ping/ping_sock.h" + +#define EXAMPLE_NAN_SERV_NAME CONFIG_ESP_WIFI_NAN_SVC_NAME +#define EXAMPLE_NAN_MATCHING_FILTER CONFIG_ESP_WIFI_NAN_MATCHING_FILTER +#ifdef CONFIG_ESP_WIFI_NAN_SERVICE_MESSAGE +#define EXAMPLE_NAN_SVC_MSG CONFIG_ESP_WIFI_NAN_SERVICE_MESSAGE +#else +#define EXAMPLE_NAN_SVC_MSG "Hello" +#endif + +static const char *TAG = "nan_sub"; + +static EventGroupHandle_t nan_event_group; + +const int NAN_SERVICE_MATCH = BIT0; +const int NDP_CONFIRMED = BIT1; +const int NDP_FAILED = BIT2; + +static wifi_event_nan_svc_match_t g_svc_match_evt; + +#ifdef CONFIG_EXAMPLE_NAN_SEND_PING +static uint8_t g_peer_ndi[6]; + +static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args) +{ + uint8_t ttl; + uint16_t seqno; + uint32_t elapsed_time, recv_len; + ip_addr_t target_addr; + esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); + esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len)); + esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time)); + ESP_LOGI(TAG, "%lu bytes from %s icmp_seq=%u ttl=%u time=%lu ms", + recv_len, ipaddr_ntoa((ip_addr_t *)&target_addr), seqno, ttl, elapsed_time); +} + +static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args) +{ + uint16_t seqno; + ip_addr_t target_addr; + esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + ESP_LOGI(TAG, "From %s icmp_seq=%d timeout", ipaddr_ntoa((ip_addr_t *)&target_addr), seqno); +} + +static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args) +{ + ip_addr_t target_addr; + uint32_t transmitted; + uint32_t received; + uint32_t total_time_ms; + esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted)); + esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms)); + uint32_t loss = (uint32_t)((1 - ((float)received) / transmitted) * 100); + ESP_LOGI(TAG, "\n--- %s ping statistics ---", inet6_ntoa(*ip_2_ip6(&target_addr))); + ESP_LOGI(TAG, "%lu packets transmitted, %lu received, %lu%% packet loss, time %lums", + transmitted, received, loss, total_time_ms); + // delete the ping sessions, so that we clean up all resources and can create a new ping session + // we don't have to call delete function in the callback, instead we can call delete function from other tasks + esp_ping_delete_session(hdl); +} + +static void nan_ndp_confirmed_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + wifi_event_ndp_confirm_t *evt = (wifi_event_ndp_confirm_t *)event_data; + + if (evt->status == NDP_STATUS_REJECTED) { + ESP_LOGI(TAG, "NDP request to Peer "MACSTR" rejected [NDP ID - %d]", MAC2STR(evt->peer_nmi), evt->ndp_id); + xEventGroupSetBits(nan_event_group, NDP_FAILED); + } else { + memcpy(g_peer_ndi, evt->peer_ndi, sizeof(g_peer_ndi)); + xEventGroupSetBits(nan_event_group, NDP_CONFIRMED); + } +} + +static void ping_nan_peer(esp_netif_t *netif) +{ + esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG(); + config.task_stack_size = 4096; + ip_addr_t target_addr = {0}; + + esp_wifi_nan_get_ipv6_linklocal_from_mac(&target_addr.u_addr.ip6, g_peer_ndi); + target_addr.type = IPADDR_TYPE_V6; + config.target_addr = target_addr; + config.interface = esp_netif_get_netif_impl_index(netif); + + /* set callback functions */ + esp_ping_callbacks_t cbs = { + .on_ping_success = cmd_ping_on_ping_success, + .on_ping_timeout = cmd_ping_on_ping_timeout, + .on_ping_end = cmd_ping_on_ping_end, + .cb_args = NULL + }; + + esp_ping_handle_t ping; + if (esp_ping_new_session(&config, &cbs, &ping) == ESP_OK) { + ESP_LOGI(TAG, "Pinging Peer with IPv6 addr %s", ipaddr_ntoa((ip_addr_t*)&target_addr)); + esp_ping_start(ping); + } else { + ESP_LOGI(TAG, "Failed to ping Peer with IPv6 addr %s", ipaddr_ntoa((ip_addr_t*)&target_addr)); + } +} +#endif + +static void nan_svc_match_event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + wifi_event_nan_svc_match_t *evt = (wifi_event_nan_svc_match_t *)event_data; + ESP_LOGI(TAG, "NAN Publisher found for Serv ID %d", evt->subscribe_id); + memcpy(&g_svc_match_evt, evt, sizeof(wifi_event_nan_svc_match_t)); + xEventGroupSetBits(nan_event_group, NAN_SERVICE_MATCH); +} + +void wifi_nan_subscribe(void) +{ + nan_event_group = xEventGroupCreate(); + esp_event_handler_instance_t instance_any_id; + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + WIFI_EVENT_NAN_SVC_MATCH, + &nan_svc_match_event_handler, + NULL, + &instance_any_id)); + +#ifdef CONFIG_EXAMPLE_NAN_SEND_PING + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + WIFI_EVENT_NDP_CONFIRM, + &nan_ndp_confirmed_event_handler, + NULL, + &instance_any_id)); +#endif + + /* Start NAN Discovery */ + wifi_nan_config_t nan_cfg = WIFI_NAN_CONFIG_DEFAULT(); + esp_netif_t *nan_netif = esp_netif_create_default_wifi_nan(); + (void) nan_netif; + esp_wifi_nan_start(&nan_cfg); + + /* Subscribe a service */ + uint8_t sub_id; + wifi_nan_subscribe_cfg_t subscribe_cfg = { + .service_name = EXAMPLE_NAN_SERV_NAME, +#if CONFIG_EXAMPLE_NAN_SUBSCRIBE_PASSIVE + .type = NAN_SUBSCRIBE_PASSIVE, +#else + .type = NAN_SUBSCRIBE_ACTIVE, +#endif + .matching_filter = EXAMPLE_NAN_MATCHING_FILTER, + .single_match_event = 1, + }; + + sub_id = esp_wifi_nan_subscribe_service(&subscribe_cfg); + if (sub_id == 0) { + return; + } + + EventBits_t bits_1 = xEventGroupWaitBits(nan_event_group, NAN_SERVICE_MATCH, pdFALSE, pdFALSE, + portMAX_DELAY); + if (bits_1 & NAN_SERVICE_MATCH) { +#ifdef CONFIG_EXAMPLE_NAN_SEND_MESSAGE + wifi_nan_followup_params_t fup = { + .inst_id = sub_id, + .peer_inst_id = g_svc_match_evt.publish_id, + .svc_info = EXAMPLE_NAN_SVC_MSG, + }; + memcpy(fup.peer_mac, g_svc_match_evt.pub_if_mac, sizeof(fup.peer_mac)); + + if (esp_wifi_nan_send_message(&fup) == ESP_OK) + ESP_LOGI(TAG, "Sending message '%s' to Publisher "MACSTR" ...", + EXAMPLE_NAN_SVC_MSG, MAC2STR(g_svc_match_evt.pub_if_mac)); +#endif +#ifdef CONFIG_EXAMPLE_NAN_SEND_PING + wifi_nan_datapath_req_t ndp_req = {0}; + ndp_req.confirm_required = true; + ndp_req.pub_id = g_svc_match_evt.publish_id; + memcpy(ndp_req.peer_mac, g_svc_match_evt.pub_if_mac, sizeof(ndp_req.peer_mac)); + esp_wifi_nan_datapath_req(&ndp_req); + + EventBits_t bits_2 = xEventGroupWaitBits(nan_event_group, NDP_CONFIRMED, pdFALSE, pdFALSE, portMAX_DELAY); + if (bits_2 & NDP_CONFIRMED) { + vTaskDelay(5000 / portTICK_PERIOD_MS); + ping_nan_peer(nan_netif); + } else if (bits_2 & NDP_FAILED) { + ESP_LOGI(TAG, "Failed to setup NAN Datapath"); + } +#endif + } +} + +void initialise_wifi(void) +{ + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM) ); +} + +void app_main(void) +{ + //Initialize NVS + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + initialise_wifi(); + + wifi_nan_subscribe(); +} diff --git a/examples/wifi/wifi_aware/nan_subscriber/sdkconfig.defaults b/examples/wifi/wifi_aware/nan_subscriber/sdkconfig.defaults new file mode 100644 index 0000000000..be625a880c --- /dev/null +++ b/examples/wifi/wifi_aware/nan_subscriber/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_ESP_WIFI_NAN_ENABLE=y diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index fecc8763c2..2e154f41d2 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -404,7 +404,6 @@ components/esp_eth/test_apps/component_ut_test.py components/esp_eth/test_apps/main/esp_eth_test.c components/esp_event/esp_event_private.c components/esp_event/event_loop_legacy.c -components/esp_event/event_send.c components/esp_event/host_test/esp_event_unit_test/main/esp_event_test.cpp components/esp_event/host_test/fixtures.hpp components/esp_event/include/esp_event_loop.h