diff --git a/components/esp_netif/esp_netif_defaults.c b/components/esp_netif/esp_netif_defaults.c index e98105b1ae4..895594f0de1 100644 --- a/components/esp_netif/esp_netif_defaults.c +++ b/components/esp_netif/esp_netif_defaults.c @@ -28,7 +28,7 @@ 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 +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE const esp_netif_inherent_config_t _g_esp_netif_inherent_nan_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_NAN(); #endif diff --git a/components/esp_netif/include/esp_netif_defaults.h b/components/esp_netif/include/esp_netif_defaults.h index b215aca1dca..8bce84aea45 100644 --- a/components/esp_netif/include/esp_netif_defaults.h +++ b/components/esp_netif/include/esp_netif_defaults.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -158,7 +158,7 @@ extern "C" { } #endif -#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE /** * @brief Default configuration reference of WIFI NAN */ @@ -204,7 +204,7 @@ extern "C" { #define ESP_NETIF_BASE_DEFAULT_WIFI_AP &_g_esp_netif_inherent_ap_config #endif -#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE /** * @brief Default base config (esp-netif inherent) of WIFI NAN */ @@ -230,7 +230,7 @@ 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 +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE #define ESP_NETIF_NETSTACK_DEFAULT_WIFI_NAN _g_esp_netif_netstack_default_wifi_nan #endif #ifdef CONFIG_PPP_SUPPORT @@ -249,7 +249,7 @@ 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 +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE extern const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_wifi_nan; #endif #ifdef CONFIG_PPP_SUPPORT @@ -264,7 +264,7 @@ 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 +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_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; diff --git a/components/esp_wifi/CMakeLists.txt b/components/esp_wifi/CMakeLists.txt index 569bc2efb75..1a9e8094ba1 100644 --- a/components/esp_wifi/CMakeLists.txt +++ b/components/esp_wifi/CMakeLists.txt @@ -48,7 +48,7 @@ if(CONFIG_ESP_WIFI_ENABLED OR CONFIG_ESP_HOST_WIFI_ENABLED) "src/smartconfig_ack.c") endif() - if(CONFIG_ESP_WIFI_NAN_ENABLE) + if(CONFIG_ESP_WIFI_NAN_SYNC_ENABLE OR CONFIG_ESP_WIFI_NAN_USD_ENABLE) list(APPEND srcs "wifi_apps/nan_app/src/nan_app.c") endif() if(CONFIG_ESP_WIFI_ENABLE_ROAMING_APP) diff --git a/components/esp_wifi/Kconfig b/components/esp_wifi/Kconfig index c2fe6a2aeea..bceb21fe32a 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -576,13 +576,20 @@ menu "Wi-Fi" 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" + config ESP_WIFI_NAN_SYNC_ENABLE + bool "Enable Wi-Fi Aware: Synchronization (NAN-Sync)" default n depends on SOC_WIFI_NAN_SUPPORT select LWIP_IPV6 help - Enable WiFi Aware (NAN) feature. + Enable Wi-Fi Aware: Synchronization feature (NAN-Sync). + + config ESP_WIFI_NAN_USD_ENABLE + bool "Enable Wi-Fi Aware: Unsynchronized service discovery (NAN-USD)" + depends on IDF_EXPERIMENTAL_FEATURES + default n + help + Enable Wi-Fi Aware: Unsynchronized service discovery (NAN-USD) config ESP_WIFI_MBEDTLS_CRYPTO bool "Use MbedTLS crypto APIs" diff --git a/components/esp_wifi/include/esp_private/wifi.h b/components/esp_wifi/include/esp_private/wifi.h index aca83b7e4aa..e8e6dc2830d 100644 --- a/components/esp_wifi/include/esp_private/wifi.h +++ b/components/esp_wifi/include/esp_private/wifi.h @@ -39,7 +39,7 @@ typedef struct { void *storage; /**< storage for FreeRTOS queue */ } wifi_static_queue_t; -struct nan_callbacks { +struct nan_sync_callbacks { void (* service_match)(uint8_t sub_id, uint8_t pub_id, uint8_t pub_mac[6], uint16_t capab, uint8_t ssi_ver, uint8_t *ssi, uint16_t ssi_len); void (* replied)(uint8_t pub_id, uint8_t sub_id, uint8_t pub_mac[6], uint8_t *ssi, uint16_t ssi_len); @@ -780,7 +780,7 @@ esp_err_t esp_nan_internal_datapath_end(wifi_nan_datapath_end_req_t *req); * - ESP_OK: succeed * - others: failed */ -esp_err_t esp_nan_internal_register_callbacks(struct nan_callbacks *cb); +esp_err_t esp_nan_internal_register_callbacks(struct nan_sync_callbacks *cb); /** * @brief Connect WiFi station to the AP. diff --git a/components/esp_wifi/include/esp_wifi.h b/components/esp_wifi/include/esp_wifi.h index 7a50c8b0118..787ec9d9b33 100644 --- a/components/esp_wifi/include/esp_wifi.h +++ b/components/esp_wifi/include/esp_wifi.h @@ -1788,6 +1788,8 @@ esp_err_t esp_wifi_action_tx_req(wifi_action_tx_req_t *req); /** * @brief Remain on the target channel for required duration * + * @attention 1. The API returns ESP_ERR_INVALID_ARG when `req->allow_broadcast` is true and the device operates in AP+STA mode. + * * @param req roc request structure containing relevant fields * * @return diff --git a/components/esp_wifi/include/esp_wifi_types_generic.h b/components/esp_wifi/include/esp_wifi_types_generic.h index 49fdb2e16ed..f5694c19cf8 100644 --- a/components/esp_wifi/include/esp_wifi_types_generic.h +++ b/components/esp_wifi/include/esp_wifi_types_generic.h @@ -592,7 +592,7 @@ typedef struct { 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; +} wifi_nan_sync_config_t; /** * @brief Configuration data for device's AP or STA or NAN. @@ -604,7 +604,7 @@ typedef struct { 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_nan_sync_config_t nan; /**< Configuration of NAN */ } wifi_config_t; /** @@ -798,6 +798,7 @@ typedef struct { bool no_ack; /**< Indicates no ack required */ wifi_action_rx_cb_t rx_cb; /**< Rx Callback to receive action frames */ uint8_t op_id; /**< Unique Identifier for operation provided by wifi driver */ + uint8_t bssid[6]; /**< BSSID (A3) address. If all zeroes, broadcast address will be used */ uint32_t data_len; /**< Length of the appended Data */ uint8_t data[0]; /**< Appended Data payload */ } wifi_action_tx_req_t; @@ -830,9 +831,14 @@ typedef struct { uint8_t channel; /**< Channel on which to perform ROC Operation */ wifi_second_chan_t sec_channel; /**< Secondary channel */ uint32_t wait_time_ms; /**< Duration to wait for on target channel */ - wifi_action_rx_cb_t rx_cb; /**< Rx Callback to receive any response */ + wifi_action_rx_cb_t rx_cb; /**< Rx Callback to receive action mgmt frames */ uint8_t op_id; /**< ID of this specific ROC operation provided by wifi driver */ wifi_action_roc_done_cb_t done_cb; /**< Callback to function that will be called upon ROC done. If assigned, WIFI_EVENT_ROC_DONE event will not be posted */ + bool allow_broadcast; /**< If set to true, broadcast/multicast action frames from any network (Address3/BSSID=ANY) + will be received in the ROC Rx callback, enabling peer discovery. + If false (default), broadcast/multicast action frames from other networks + will be filtered out in hardware and not passed to the Rx callback, reducing CPU usage. + Frames whose Address3/BSSID is already broadcast are always delivered. */ } wifi_roc_req_t; /** @@ -846,7 +852,7 @@ typedef struct { uint16_t burst_period; /**< Requested period between FTM bursts in 100's of milliseconds (allowed values 0(No pref) - 100) */ } wifi_ftm_initiator_cfg_t; -#define ESP_WIFI_NAN_MAX_SVC_SUPPORTED 2 /**< Maximum number of NAN services supported */ +#define ESP_WIFI_NAN_MAX_SVC_SUPPORTED 2 /**< Maximum number of NAN or NAN-USD services supported */ #define ESP_WIFI_NAN_DATAPATH_MAX_PEERS 2 /**< Maximum number of NAN datapath peers supported */ #define ESP_WIFI_NDP_ROLE_INITIATOR 1 /**< Initiator role for NAN Data Path */ @@ -878,7 +884,7 @@ typedef enum { */ typedef struct { uint8_t wfa_oui[WIFI_OUI_LEN]; /**< WFA OUI - 0x50, 0x6F, 0x9A */ - wifi_nan_svc_proto_t proto; /**< WFA defined protocol types */ + uint8_t proto; /**< WFA defined protocol of type wifi_nan_svc_proto_t */ uint8_t payload[0]; /**< Service Info payload */ } wifi_nan_wfa_ssi_t; @@ -893,6 +899,19 @@ typedef enum { NAN_SUBSCRIBE_PASSIVE, /**< Passively listens to Publish frames */ } wifi_nan_service_type_t; +/** + * @brief USD specific configuration parameters + * + */ +typedef struct { + uint8_t usd_default_channel; /**< If not specified, default channel 6 is used */ + wifi_scan_channel_bitmap_t usd_chan_bitmap; /**< Indicates publish channel list used in USD */ + uint8_t n_min; /**< Indicates minimum value of dwell period N used in the USD (Nmin) */ + uint8_t n_max; /**< Indicates maximum value of dwell period N used in the USD (Nmax) */ + uint8_t m_min; /**< Indicates minimum value of dwell period M used in the USD (Mmin) */ + uint8_t m_max; /**< Indicates maximum value of dwell period M used in the USD (Mmax)*/ +} wifi_nan_usd_config_t; + /** * @brief NAN Publish service configuration parameters * @@ -906,9 +925,13 @@ typedef struct { uint8_t fsd_reqd: 1; /**< Further Service Discovery(FSD) required */ uint8_t fsd_gas: 1; /**< 0 - Follow-up used for FSD, 1 - GAS used for FSD */ uint8_t ndp_resp_needed: 1; /**< 0 - Auto-Accept NDP Requests, 1 - Require explicit response with esp_wifi_nan_datapath_resp */ - uint8_t reserved: 3; /**< Reserved */ + uint8_t usd_discovery_flag: 1; /**< 0 - NAN Synchronization for Discovery, 1 - USD for Discovery. 'NAN Discovery flag' from specification */ + uint8_t reserved: 2; /**< Reserved */ uint16_t ssi_len; /**< Length of service specific info, maximum allowed length - ESP_WIFI_MAX_SVC_SSI_LEN */ uint8_t *ssi; /**< Service Specific Info of type wifi_nan_wfa_ssi_t for WFA defined protocols, otherwise proprietary and defined by Applications */ + unsigned int ttl; /**< Run publish function for a given time interval in seconds. If ttl=0 and usd_discovery_flag is enabled, + only one Publish message is transmitted */ + wifi_nan_usd_config_t usd_publish_config; /**< USD configuration parameters. Relevant only when 'usd_discovery_flag' is set. */ } wifi_nan_publish_cfg_t; /** @@ -923,9 +946,13 @@ typedef struct { uint8_t datapath_reqd: 1; /**< NAN Datapath required for the service */ uint8_t fsd_reqd: 1; /**< Further Service Discovery(FSD) required */ uint8_t fsd_gas: 1; /**< 0 - Follow-up used for FSD, 1 - GAS used for FSD */ - uint8_t reserved: 4; /**< Reserved */ + uint8_t usd_discovery_flag: 1; /**< 0 - NAN Synchronization for Discovery, 1 - USD for Discovery. 'NAN Discovery flag' from specification */ + uint8_t reserved: 3; /**< Reserved */ uint16_t ssi_len; /**< Length of service specific info, maximum allowed length - ESP_WIFI_MAX_SVC_SSI_LEN */ uint8_t *ssi; /**< Service Specific Info of type wifi_nan_wfa_ssi_t for WFA defined protocols, otherwise proprietary and defined by Applications */ + unsigned int ttl; /**< Run subscribe function for a given time interval in seconds. If ttl=0 and usd_discovery_flag is enabled, + the subscriber listens until the first service match is reported. */ + wifi_nan_usd_config_t usd_subscribe_config; /**< USD configuration parameters. Relevant only when 'usd_discovery_flag' is set. */ } wifi_nan_subscribe_cfg_t; /** @@ -1098,8 +1125,8 @@ typedef enum { WIFI_EVENT_BTWT_SETUP, /**< bTWT setup */ WIFI_EVENT_BTWT_TEARDOWN, /**< bTWT teardown*/ - WIFI_EVENT_NAN_STARTED, /**< NAN Discovery has started */ - WIFI_EVENT_NAN_STOPPED, /**< NAN Discovery has stopped */ + WIFI_EVENT_NAN_SYNC_STARTED, /**< NAN Synchronization Discovery has started */ + WIFI_EVENT_NAN_SYNC_STOPPED, /**< NAN Synchronization 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 */ diff --git a/components/esp_wifi/lib b/components/esp_wifi/lib index 37a69947e1e..21eacc3229f 160000 --- a/components/esp_wifi/lib +++ b/components/esp_wifi/lib @@ -1 +1 @@ -Subproject commit 37a69947e1e7c1e98f63ddd3ce11b05aca036057 +Subproject commit 21eacc3229fba6ca5e2c8a92062184b53db65651 diff --git a/components/esp_wifi/sdkconfig.rename b/components/esp_wifi/sdkconfig.rename index 273b291fb56..c4b403e54bd 100644 --- a/components/esp_wifi/sdkconfig.rename +++ b/components/esp_wifi/sdkconfig.rename @@ -28,6 +28,7 @@ CONFIG_ESP32_WIFI_IRAM_OPT CONFIG_ESP_WIFI_IRAM_OPT CONFIG_ESP32_WIFI_RX_IRAM_OPT CONFIG_ESP_WIFI_RX_IRAM_OPT CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE CONFIG_ESP_WIFI_ENABLE_WPA3_SAE CONFIG_ESP32_WIFI_ENABLE_WPA3_OWE_STA CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA +CONFIG_ESP_WIFI_NAN_ENABLE CONFIG_ESP_WIFI_NAN_SYNC_ENABLE # renaming supplicant kconfig to esp_wifi kconfig CONFIG_WPA_MBEDTLS_CRYPTO CONFIG_ESP_WIFI_MBEDTLS_CRYPTO diff --git a/components/esp_wifi/src/wifi_default.c b/components/esp_wifi/src/wifi_default.c index d5d07855e28..3773618cdfb 100644 --- a/components/esp_wifi/src/wifi_default.c +++ b/components/esp_wifi/src/wifi_default.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,7 +12,7 @@ #include "esp_wifi_netif.h" #include #include -#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE #include "apps_private/wifi_apps_private.h" #endif #if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP @@ -170,7 +170,7 @@ static void wifi_default_action_sta_got_ip(void *arg, esp_event_base_t base, int } } -#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_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) { @@ -202,9 +202,9 @@ 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); +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NAN_SYNC_STARTED, wifi_default_action_nan_started); + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NAN_SYNC_STOPPED, wifi_default_action_nan_stopped); #endif esp_unregister_shutdown_handler((shutdown_handler_t)esp_wifi_stop); wifi_default_handlers_set = false; @@ -258,13 +258,13 @@ 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); +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE + err = esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NAN_SYNC_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); + err = esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NAN_SYNC_STOPPED, wifi_default_action_nan_stopped, NULL); if (err != ESP_OK) { goto fail; } @@ -364,7 +364,7 @@ static inline esp_err_t esp_netif_attach_wifi(esp_netif_t *esp_netif, wifi_inter #ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT && wifi_if != WIFI_IF_AP #endif -#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE && wifi_if != WIFI_IF_NAN #endif )) { @@ -386,7 +386,7 @@ esp_err_t esp_netif_attach_wifi_ap(esp_netif_t *esp_netif) } #endif -#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE esp_err_t esp_netif_attach_wifi_nan(esp_netif_t *esp_netif) { return esp_netif_attach_wifi(esp_netif, WIFI_IF_NAN); @@ -425,7 +425,7 @@ esp_netif_t* esp_netif_create_default_wifi_sta(void) return netif; } -#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE /** * @brief User init default NAN (official API) */ @@ -465,7 +465,7 @@ esp_netif_t* esp_netif_create_wifi(wifi_interface_t wifi_if, const esp_netif_inh } else if (wifi_if == WIFI_IF_AP) { cfg.stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_AP; #endif -#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE } else if (wifi_if == WIFI_IF_NAN) { cfg.stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_NAN; #endif diff --git a/components/esp_wifi/src/wifi_init.c b/components/esp_wifi/src/wifi_init.c index ea050fe0abb..2bf79c6f9e4 100644 --- a/components/esp_wifi/src/wifi_init.c +++ b/components/esp_wifi/src/wifi_init.c @@ -27,7 +27,7 @@ #if __has_include("esp_psram.h") #include "esp_psram.h" #endif -#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE #include "apps_private/wifi_apps_private.h" #endif #ifdef CONFIG_ESP_WIFI_FTM_ENABLE @@ -187,7 +187,7 @@ static esp_err_t wifi_deinit_internal(void) ESP_LOGW(TAG, "Failed to unregister Rx callbacks"); } -#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE esp_nan_app_deinit(); #endif @@ -484,7 +484,7 @@ esp_err_t esp_wifi_init(const wifi_init_config_t *config) esp_wifi_config_info(); -#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE esp_nan_app_init(); #endif @@ -649,7 +649,7 @@ void create_new_bss_for_sa_query_failed_sta(uint8_t arg) } #endif /* CONFIG_ESP_WIFI_SOFTAP_SUPPORT */ -#ifndef CONFIG_ESP_WIFI_NAN_ENABLE +#ifndef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE esp_err_t nan_start(void) { @@ -680,7 +680,7 @@ int wifi_create_nan(void) return 0; } -int wifi_nan_set_config_local(wifi_nan_config_t *p) +int wifi_nan_set_config_local(wifi_nan_sync_config_t *p) { /* Do not remove, stub to overwrite weak link in Wi-Fi Lib */ return 0; @@ -707,7 +707,7 @@ void nan_ndp_resp_timeout_process(void *p) { /* Do not remove, stub to overwrite weak link in Wi-Fi Lib */ } -#endif /* CONFIG_ESP_WIFI_NAN_ENABLE */ +#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */ #if CONFIG_IDF_TARGET_ESP32C2 #if CONFIG_ESP32C2_REV_MIN_FULL < 200 diff --git a/components/esp_wifi/src/wifi_netif.c b/components/esp_wifi/src/wifi_netif.c index f42ff6bd52e..96d4135dd21 100644 --- a/components/esp_wifi/src/wifi_netif.c +++ b/components/esp_wifi/src/wifi_netif.c @@ -46,7 +46,7 @@ static esp_err_t wifi_ap_receive(void *buffer, uint16_t len, void *eb) } #endif -#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_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); @@ -158,7 +158,7 @@ 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 +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE case WIFI_IF_NAN: rxcb = wifi_nan_receive; break; diff --git a/components/esp_wifi/test_apps/bin_size_apsta/sdkconfig.ci.enable_nan b/components/esp_wifi/test_apps/bin_size_apsta/sdkconfig.ci.enable_nan index be625a880c8..3d8f4d46579 100644 --- a/components/esp_wifi/test_apps/bin_size_apsta/sdkconfig.ci.enable_nan +++ b/components/esp_wifi/test_apps/bin_size_apsta/sdkconfig.ci.enable_nan @@ -1 +1 @@ -CONFIG_ESP_WIFI_NAN_ENABLE=y +CONFIG_ESP_WIFI_NAN_SYNC_ENABLE=y 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 index 5fe106f2894..df2b9883cf9 100644 --- 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 @@ -15,7 +15,7 @@ extern "C" { #endif -#ifdef CONFIG_ESP_WIFI_NAN_ENABLE +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE /** * @brief Initialize the NAN App and required Data Structures @@ -35,7 +35,7 @@ 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 + * @attention This API should be called in WIFI_EVENT_NAN_SYNC_STARTED event handler * * @param nan_netif Netif handle corresponding to NAN interface. */ @@ -45,11 +45,11 @@ 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 + * @attention This API should be called in WIFI_EVENT_NAN_SYNC_STOPPED event handler */ void esp_nan_action_stop(void); -#endif /* CONFIG_ESP_WIFI_NAN_ENABLE */ +#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */ #ifdef __cplusplus } diff --git a/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h b/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h index c370b76ac5e..98391602c0b 100644 --- a/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h +++ b/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h @@ -15,7 +15,8 @@ extern "C" { #endif -#define WIFI_NAN_CONFIG_DEFAULT() { \ +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE +#define WIFI_NAN_SYNC_CONFIG_DEFAULT() { \ .op_channel = 6, \ .master_pref = 2, \ .scan_time = 3, \ @@ -40,7 +41,8 @@ struct nan_peer_record { }; /** - * @brief Start NAN Discovery with provided configuration + * @brief Start NAN Synchronization using the provided parameters. + * @note Discovery traffic begins only after publish/subscribe services are started. * * @attention This API should be called after esp_wifi_init(). * @@ -50,69 +52,21 @@ struct nan_peer_record { * - ESP_OK: succeed * - others: failed */ -esp_err_t esp_wifi_nan_start(const wifi_nan_config_t *nan_cfg); +esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg); /** - * @brief Stop NAN Discovery, end NAN Services and Datapaths + * @brief Stop NAN Synchronization discovery and end publish/subscribe services + * + * @attention This API will end datapaths if any in NAN synchronization * * @return * - ESP_OK: succeed * - others: failed */ -esp_err_t esp_wifi_nan_stop(void); +esp_err_t esp_wifi_nan_sync_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. - * - * @return - * - non-zero: Publish service identifier - * - zero: failed - */ -uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg); - -/** - * @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 + * @brief Send a NAN datapath request to a matched publisher within the synchronized NAN cluster * * @attention This API should be called by the Subscriber after a match occurs with a Publisher. * @@ -125,7 +79,7 @@ esp_err_t esp_wifi_nan_cancel_service(uint8_t service_id); uint8_t esp_wifi_nan_datapath_req(wifi_nan_datapath_req_t *req); /** - * @brief Respond to a NAN Datapath request with Accept or Reject + * @brief Respond to a NAN datapath request within the synchronized NAN cluster * * @attention This API should be called if ndp_resp_needed is set 1 in wifi_nan_publish_cfg_t and * a WIFI_EVENT_NDP_INDICATION event is received due to an incoming NDP request. @@ -139,7 +93,7 @@ uint8_t esp_wifi_nan_datapath_req(wifi_nan_datapath_req_t *req); esp_err_t esp_wifi_nan_datapath_resp(wifi_nan_datapath_resp_t *resp); /** - * @brief Terminate a NAN Datapath + * @brief Terminate an active NAN datapath within the synchronized NAN cluster * * @param req NAN Datapath end request parameters. * @@ -161,6 +115,7 @@ 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. * * @attention If service information is to be fetched from service name, set own_svc_id as zero. + * @note Returns records discovered while participating in a synchronized NAN cluster. * * @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. @@ -171,11 +126,14 @@ void esp_wifi_nan_get_ipv6_linklocal_from_mac(ip6_addr_t *ip6, uint8_t *mac_addr * - 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. * + * @note Reports peers discovered via synchronized NAN operations. + * * @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. @@ -184,11 +142,14 @@ esp_err_t esp_wifi_nan_get_own_svc_info(uint8_t *own_svc_id, char *svc_name, int * - 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. * + * @note Provides peer information available from synchronized NAN discovery. + * * @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. @@ -196,8 +157,141 @@ esp_err_t esp_wifi_nan_get_peer_records(int *num_peer_records, uint8_t own_svc_i * - 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); +#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */ + +#if defined(CONFIG_ESP_WIFI_NAN_SYNC_ENABLE) || defined(CONFIG_ESP_WIFI_NAN_USD_ENABLE) +/** + * @brief Start publishing a service to NAN peers within a synchronized cluster or to NAN-USD peers + * + * @attention Call after `esp_wifi_nan_sync_start()` when operating in NAN-Sync, or after + * `esp_wifi_nan_usd_start()` when operating in NAN-USD. + * @attention When NAN-USD discovery is active the publish request is routed through the + * USD engine, otherwise it uses NAN synchronization. + * @attention A maximum of two services is allowed simultaneously + * (e.g., one publish and one subscribe, or two publish/subscribe). + * This limit is defined by the ESP_WIFI_NAN_MAX_SVC_SUPPORTED. + * + * @param publish_cfg Configuration parameters for publishing a service. + * When operating in NAN-USD, populate `publish_cfg->usd_publish_config` + * (for example using `esp_wifi_usd_get_default_publish_cfg()`) to tune + * the USD dwell and channel parameters. + * + * @return + * - non-zero: Publish service identifier + * - zero: failed + */ +uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg); + +/** + * @brief Subscribe to a service published by a NAN peer within a synchronized cluster or by a NAN-USD peer + * + * @attention Call after `esp_wifi_nan_sync_start()` when operating in NAN-Sync, or after + * `esp_wifi_nan_usd_start()` when operating in NAN-USD. + * @attention With NAN-USD discovery enabled the subscribe request is handled by the USD + * engine, otherwise it uses NAN synchronization. + * @attention A maximum of two services is allowed simultaneously + * (e.g., one publish and one subscribe, or two publish/subscribe). + * This limit is defined by the ESP_WIFI_NAN_MAX_SVC_SUPPORTED. + * + * @param subscribe_cfg Configuration parameters for subscribing for a service. + * When operating in NAN-USD, populate `subscribe_cfg->usd_subscribe_config` + * (for example using `esp_wifi_usd_get_default_subscribe_cfg()`) to tune + * the USD dwell and channel parameters. + * + * @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 a matched peer in NAN synchronization or USD + * + * @attention This API should be called after a NAN/USD 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 synchronization or NAN-USD 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); + +#endif /* NAN-Sync || NAN-USD */ + +#ifdef CONFIG_ESP_WIFI_NAN_USD_ENABLE +/** + * @brief Start NAN Unsynchronized service discovery (USD) + * + * @attention This API should be called after esp_wifi_start(). + * @note USD currently operates over the Station interface; this may change in future releases. + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_wifi_nan_usd_start(void); + +/** + * @brief Stop NAN Unsynchronized service discovery (USD) and end publish/subscribe services + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_wifi_nan_usd_stop(void); + +/** + * @brief Get default configuration for USD publish operation. + * + * @note This function returns a default configuration structure for initiating + * a USD (Unsynchronized Service Discovery) publish session. + * - The default channel is set to channel 6 (2.437 GHz) in the 2.4 GHz band. + * - The channel list will include all 20 MHz channels in the 2.4 GHz frequency band, + * as permitted by the regulatory domain of the current geographic location. + * - Default values for dwell periods are set as per Wi-Fi Aware Specification: + * - Nmin = 5, Nmax = 10 + * - Mmin = 5, Mmax = 10 + * + * @return wifi_nan_usd_config_t structure with pre-filled default values for publishing. + */ +wifi_nan_usd_config_t esp_wifi_usd_get_default_publish_cfg(void); + +/** + * @brief Get default configuration for USD subscribe operation. + * + * @note This function returns a default configuration structure for initiating + * a USD (Unsynchronized Service Discovery) subscribe session. + * - The default channel is set to channel 6 (2.437 GHz) in the 2.4 GHz band. + * - The channel list will include all 20 MHz channels in the 2.4 GHz frequency band, + * as permitted by the regulatory domain of the current geographic location. + * - Default values for dwell periods are set as per Wi-Fi Aware Specification: + * - Nmin = 5, Nmax = 10 + * - Mmin = 5, Mmax = 10 + * + * @warning Currently, the subscribe function operates only on the default channel. + * It does not alternate between the default channel and channels in the channel list. + * + * @return wifi_nan_usd_config_t structure with pre-filled default values for subscribing. + */ +wifi_nan_usd_config_t esp_wifi_usd_get_default_subscribe_cfg(void); + +#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */ + #ifdef __cplusplus } #endif diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c b/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c index df08a10ad02..be4818c62c0 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c @@ -17,6 +17,9 @@ #include "os.h" #include "esp_nan.h" #include "utils/common.h" +#ifdef CONFIG_ESP_WIFI_NAN_USD_ENABLE +#include "esp_private/esp_nan_usd.h" +#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */ /* NAN States */ #define NAN_STARTED_BIT BIT0 @@ -42,18 +45,27 @@ /* Global Variables */ static const char *TAG = "nan_app"; +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE static EventGroupHandle_t nan_event_group; static bool s_app_default_handlers_set = false; static uint8_t null_mac[MACADDR_LEN] = {0}; static void *s_nan_data_lock = NULL; -static const uint8_t s_wfa_oui[3] = {0x50, 0x6f, 0x9a}; static uint32_t s_fup_context; +#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */ +#ifdef CONFIG_ESP_WIFI_NAN_USD_ENABLE +#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */ +static bool s_usd_in_progress = false; +static const uint8_t s_wfa_oui[3] = {0x50, 0x6f, 0x9a}; /* Definitions */ #define NAN_SDEA_CTRL_FSD_REQD BIT(0) #define NAN_SDEA_CTRL_FSD_GAS BIT(1) #define NAN_SDEA_CTRL_DATAPATH_REQD BIT(2) +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE +#define NAN_DATA_LOCK() os_mutex_lock(s_nan_data_lock) +#define NAN_DATA_UNLOCK() os_mutex_unlock(s_nan_data_lock) + 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 */ @@ -748,7 +760,7 @@ void esp_nan_action_start(esp_netif_t *nan_netif) s_nan_ctx.state = NAN_STARTED_BIT; NAN_DATA_UNLOCK(); - struct nan_callbacks nan_cb = { + struct nan_sync_callbacks nan_cb = { .service_match = nan_app_service_match_cb, .replied = nan_app_replied_cb, .receive = nan_app_receive_cb, @@ -782,11 +794,10 @@ void esp_nan_action_stop(void) 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) +esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_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) { @@ -797,6 +808,10 @@ esp_err_t esp_wifi_nan_start(const wifi_nan_config_t *nan_cfg) return ret; } + + /* XXX: For now, NAN-USD and NAN-Sync can not coexist. */ + /* NAN-Synchronization Only */ + wifi_config_t config = {0}; if (!s_nan_data_lock) { ESP_LOGE(TAG, "NAN Data lock doesn't exist"); return ESP_FAIL; @@ -812,7 +827,7 @@ esp_err_t esp_wifi_nan_start(const wifi_nan_config_t *nan_cfg) 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)); + memcpy(&config.nan, nan_cfg, sizeof(wifi_nan_sync_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) { @@ -833,7 +848,7 @@ esp_err_t esp_wifi_nan_start(const wifi_nan_config_t *nan_cfg) return ESP_OK; } -esp_err_t esp_wifi_nan_stop(void) +esp_err_t esp_wifi_nan_sync_stop(void) { NAN_DATA_LOCK(); if (!(s_nan_ctx.state & NAN_STARTED_BIT)) { @@ -873,41 +888,77 @@ esp_err_t esp_wifi_nan_stop(void) NAN_DATA_UNLOCK(); return ESP_OK; } +#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg) { - uint8_t pub_id; + int pub_id; + if (publish_cfg->usd_discovery_flag && !s_usd_in_progress) { + ESP_LOGE(TAG, "Can not start Publish function with USD Discovery " + "when USD is not enabled"); + return 0; + } + + if (!publish_cfg->usd_discovery_flag && s_usd_in_progress) { + // XXX Add support for NAN-Sync and NAN-USD concurrent discovery + ESP_LOGE(TAG, "Can not start Publish function with " + "NAN-Synchronization Discovery when USD is enabled"); + return 0; + } + + if ((publish_cfg->ssi_len && publish_cfg->ssi == NULL) || + (publish_cfg->ssi && + (!publish_cfg->ssi_len || + publish_cfg->ssi_len > ESP_WIFI_MAX_SVC_SSI_LEN))) { + ESP_LOGE(TAG, "Configured ssi and ssi_len(%d) incorrect", + publish_cfg->ssi_len); + return 0; + } + + if (publish_cfg->ssi && + !memcmp(publish_cfg->ssi, s_wfa_oui, sizeof(s_wfa_oui))) { + /* WFA defined Service Specific Info */ + wifi_nan_wfa_ssi_t *wfa_ssi = (wifi_nan_wfa_ssi_t *)publish_cfg->ssi; + if (wfa_ssi->proto >= WIFI_SVC_PROTO_MAX) { + ESP_LOGI(TAG, "Unrecognized WFA Defined SSI protocol (%d)", + wfa_ssi->proto); + } + } + +#ifdef CONFIG_ESP_WIFI_NAN_USD_ENABLE + if (s_usd_in_progress) { + if ((pub_id = esp_nan_usd_publish(publish_cfg)) == -1) { + ESP_LOGE(TAG, "Failed to publish service '%s'", + publish_cfg->service_name); + return 0; + } + ESP_LOGI(TAG, "Started Publishing %s [Service ID - %u]", + publish_cfg->service_name, pub_id); + return pub_id; + } +#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */ + +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE NAN_DATA_LOCK(); if (!(s_nan_ctx.state & NAN_STARTED_BIT)) { ESP_LOGE(TAG, "NAN not started!"); goto fail; } + if (nan_services_limit_reached()) { ESP_LOGE(TAG, "Maximum services limit reached"); goto fail; } if (nan_find_own_svc_by_name(publish_cfg->service_name)) { - ESP_LOGE(TAG, "Service name %s already used!", publish_cfg->service_name); + ESP_LOGE(TAG, "Service name %s already used!", + publish_cfg->service_name); goto fail; } - if ((publish_cfg->ssi_len && publish_cfg->ssi == NULL) || - (publish_cfg->ssi && (!publish_cfg->ssi_len || publish_cfg->ssi_len > ESP_WIFI_MAX_SVC_SSI_LEN))) { - ESP_LOGE(TAG, "Configured ssi and ssi_len(%d) incorrect", publish_cfg->ssi_len); - goto fail; - } - if (publish_cfg->ssi && !memcmp(publish_cfg->ssi, s_wfa_oui, sizeof(s_wfa_oui))) { - /* WFA defined Service Specific Info */ - wifi_nan_wfa_ssi_t *wfa_ssi = (wifi_nan_wfa_ssi_t *)publish_cfg->ssi; - if (wfa_ssi->proto >= WIFI_SVC_PROTO_MAX) { - ESP_LOGI(TAG, "Unrecognized WFA Defined SSI protocol (%d)", wfa_ssi->proto); - } - } - - if (esp_nan_internal_publish_service(publish_cfg, &pub_id, false) != ESP_OK) { + if (esp_nan_internal_publish_service(publish_cfg, (uint8_t *) &pub_id, false) != ESP_OK) { ESP_LOGE(TAG, "Failed to publish service '%s'", publish_cfg->service_name); goto fail; } @@ -920,12 +971,55 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg) fail: NAN_DATA_UNLOCK(); return 0; +#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */ + return 0; } uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe_cfg) { - uint8_t sub_id; + int sub_id; + if (subscribe_cfg->usd_discovery_flag && !s_usd_in_progress) { + ESP_LOGE(TAG, "Can not start Subscribe function with USD Discovery " + "when USD is not enabled"); + return 0; + } + + if (!subscribe_cfg->usd_discovery_flag && s_usd_in_progress) { + // XXX Add support for NAN-Sync and NAN-USD concurrent discovery + ESP_LOGE(TAG, "Can not start Subscribe function with " + "NAN-Synchronization Discovery when USD is enabled"); + return 0; + } + + if ((subscribe_cfg->ssi_len && subscribe_cfg->ssi == NULL) || + (subscribe_cfg->ssi && (!subscribe_cfg->ssi_len || subscribe_cfg->ssi_len > ESP_WIFI_MAX_SVC_SSI_LEN))) { + ESP_LOGE(TAG, "Configured ssi and ssi_len(%d) incorrect", subscribe_cfg->ssi_len); + return 0; + } + + if (subscribe_cfg->ssi && !memcmp(subscribe_cfg->ssi, s_wfa_oui, sizeof(s_wfa_oui))) { + /* WFA defined Service Specific Info */ + wifi_nan_wfa_ssi_t *wfa_ssi = (wifi_nan_wfa_ssi_t *)subscribe_cfg->ssi; + if (wfa_ssi->proto >= WIFI_SVC_PROTO_MAX) { + ESP_LOGI(TAG, "Unrecognized WFA Defined SSI protocol (%d)", wfa_ssi->proto); + } + } + +#ifdef CONFIG_ESP_WIFI_NAN_USD_ENABLE + if (s_usd_in_progress) { + if ((sub_id = esp_nan_usd_subscribe(subscribe_cfg)) == -1) { + ESP_LOGE(TAG, "Failed to subscribe to service '%s'", + subscribe_cfg->service_name); + return 0; + } + ESP_LOGI(TAG, "Started Subscribing to %s [Service ID - %u]", + subscribe_cfg->service_name, sub_id); + return sub_id; + } +#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */ + +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE NAN_DATA_LOCK(); if (!(s_nan_ctx.state & NAN_STARTED_BIT)) { ESP_LOGE(TAG, "NAN not started!"); @@ -941,63 +1035,70 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe goto fail; } - if ((subscribe_cfg->ssi_len && subscribe_cfg->ssi == NULL) || - (subscribe_cfg->ssi && (!subscribe_cfg->ssi_len || subscribe_cfg->ssi_len > ESP_WIFI_MAX_SVC_SSI_LEN))) { - ESP_LOGE(TAG, "Configured ssi and ssi_len(%d) incorrect", subscribe_cfg->ssi_len); - goto fail; - } - if (subscribe_cfg->ssi && !memcmp(subscribe_cfg->ssi, s_wfa_oui, sizeof(s_wfa_oui))) { - /* WFA defined Service Specific Info */ - wifi_nan_wfa_ssi_t *wfa_ssi = (wifi_nan_wfa_ssi_t *)subscribe_cfg->ssi; - if (wfa_ssi->proto >= WIFI_SVC_PROTO_MAX) { - ESP_LOGI(TAG, "Unrecognized WFA Defined SSI protocol (%d)", wfa_ssi->proto); - } - } - - if (esp_nan_internal_subscribe_service(subscribe_cfg, &sub_id, false) != ESP_OK) { + if (esp_nan_internal_subscribe_service(subscribe_cfg, (uint8_t*) &sub_id, false) != ESP_OK) { ESP_LOGE(TAG, "Failed to subscribe to service '%s'", subscribe_cfg->service_name); goto fail; } ESP_LOGI(TAG, "Started Subscribing to %s [Service ID - %u]", subscribe_cfg->service_name, sub_id); - nan_record_own_svc(sub_id, ESP_NAN_SUBSCRIBE, subscribe_cfg->service_name, false); + nan_record_own_svc((uint8_t) sub_id, ESP_NAN_SUBSCRIBE, subscribe_cfg->service_name, false); NAN_DATA_UNLOCK(); return sub_id; fail: NAN_DATA_UNLOCK(); return 0; +#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */ + return 0; } esp_err_t esp_wifi_nan_send_message(wifi_nan_followup_params_t *fup_params) { - struct peer_svc_info *p_peer_svc; esp_err_t ret = ESP_OK; + if ((fup_params->ssi_len && fup_params->ssi == NULL) || + (fup_params->ssi && + (!fup_params->ssi_len || + fup_params->ssi_len > ESP_WIFI_MAX_FUP_SSI_LEN))) { + ESP_LOGE(TAG, "Configured ssi and ssi_len(%d) incorrect", + fup_params->ssi_len); + return ESP_FAIL; + } + + if (fup_params->ssi && + !memcmp(fup_params->ssi, s_wfa_oui, sizeof(s_wfa_oui))) { + /* WFA defined Service Specific Info */ + wifi_nan_wfa_ssi_t *wfa_ssi = (wifi_nan_wfa_ssi_t *)fup_params->ssi; + if (wfa_ssi->proto >= WIFI_SVC_PROTO_MAX) { + ESP_LOGI(TAG, "Unrecognized WFA Defined SSI protocol (%d)", + wfa_ssi->proto); + } + } + +#ifdef CONFIG_ESP_WIFI_NAN_USD_ENABLE + if (s_usd_in_progress) { + if (esp_nan_usd_transmit(fup_params->inst_id, fup_params->ssi, + fup_params->ssi_len, fup_params->peer_mac, + fup_params->peer_inst_id) != ESP_OK) { + ESP_LOGE(TAG, "Failed to send Follow-up message!"); + return ESP_FAIL; + } + return ESP_OK; + } +#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */ + +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE + struct peer_svc_info *p_peer_svc; NAN_DATA_LOCK(); - p_peer_svc = nan_find_peer_svc(fup_params->inst_id, fup_params->peer_inst_id, - fup_params->peer_mac); + 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!"); NAN_DATA_UNLOCK(); return ESP_FAIL; } - if ((fup_params->ssi_len && fup_params->ssi == NULL) || - (fup_params->ssi && (!fup_params->ssi_len || fup_params->ssi_len > ESP_WIFI_MAX_FUP_SSI_LEN))) { - ESP_LOGE(TAG, "Configured ssi and ssi_len(%d) incorrect", fup_params->ssi_len); - return ESP_FAIL; - } - - if (fup_params->ssi && !memcmp(fup_params->ssi, s_wfa_oui, sizeof(s_wfa_oui))) { - /* WFA defined Service Specific Info */ - wifi_nan_wfa_ssi_t *wfa_ssi = (wifi_nan_wfa_ssi_t *)fup_params->ssi; - if (wfa_ssi->proto >= WIFI_SVC_PROTO_MAX) { - ESP_LOGI(TAG, "Unrecognized WFA Defined SSI protocol (%d)", wfa_ssi->proto); - } - } - if (!fup_params->inst_id) { fup_params->inst_id = p_peer_svc->own_svc_id; } @@ -1018,9 +1119,12 @@ esp_err_t esp_wifi_nan_send_message(wifi_nan_followup_params_t *fup_params) EventBits_t bits = os_event_group_wait_bits(nan_event_group, NAN_TX_SUCCESS | NAN_TX_FAILURE, pdFALSE, pdFALSE, pdMS_TO_TICKS(NAN_ACTION_TIMEOUT)); if (bits & NAN_TX_SUCCESS) { if (fup_params->ssi) { - ESP_LOGD(TAG, "Sent below payload to Peer "MACSTR" with Service ID %d", + ESP_LOGD(TAG, "Sent below payload in Follow-up message to Peer "MACSTR" with Service ID %d", MAC2STR(fup_params->peer_mac), fup_params->peer_inst_id); ESP_LOG_BUFFER_HEXDUMP(TAG, fup_params->ssi, fup_params->ssi_len, ESP_LOG_DEBUG); + } else { + ESP_LOGI(TAG, "Sent message to Peer "MACSTR" with Service ID %d", + MAC2STR(fup_params->peer_mac), fup_params->peer_inst_id); } ret = ESP_OK; } else if (bits & NAN_TX_FAILURE) { @@ -1030,12 +1134,19 @@ esp_err_t esp_wifi_nan_send_message(wifi_nan_followup_params_t *fup_params) ESP_LOGE(TAG, "Timeout, failed to send Follow-up message!"); ret = ESP_FAIL; } +#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */ return ret; } esp_err_t esp_wifi_nan_cancel_service(uint8_t service_id) { +#ifdef CONFIG_ESP_WIFI_NAN_USD_ENABLE + if (s_usd_in_progress) { + return esp_nan_usd_cancel_service(service_id); + } +#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */ +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE NAN_DATA_LOCK(); struct own_svc_info *p_own_svc = nan_find_own_svc(service_id); @@ -1067,8 +1178,12 @@ fail: done: NAN_DATA_UNLOCK(); return ESP_OK; +#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */ + return ESP_FAIL; } + +#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE uint8_t esp_wifi_nan_datapath_req(wifi_nan_datapath_req_t *req) { uint8_t ndp_id = 0; @@ -1337,3 +1452,81 @@ esp_err_t esp_wifi_nan_get_peer_info(char *svc_name, uint8_t *peer_mac, struct n return ESP_FAIL; } } +#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */ + +#ifdef CONFIG_ESP_WIFI_NAN_USD_ENABLE +esp_err_t esp_wifi_nan_usd_start(void) +{ + if (!s_usd_in_progress) { + ESP_RETURN_ON_ERROR(esp_wifi_set_mode(WIFI_MODE_STA), "NAN-USD", "Setting STA mode failed"); + ESP_RETURN_ON_ERROR(esp_nan_usd_init(), "NAN-USD", "Failed to initialise NAN USD engine"); + s_usd_in_progress = true; + ESP_LOGI(TAG, "NaN-USD Started"); + return ESP_OK; + } + return ESP_OK; +} + +esp_err_t esp_wifi_nan_usd_stop(void) +{ + if (s_usd_in_progress) { + s_usd_in_progress = false; + esp_nan_usd_deinit(); + ESP_LOGI(TAG, "NaN-USD Stopped"); + return ESP_OK; + } + return ESP_OK; +} + +wifi_nan_usd_config_t esp_wifi_usd_get_default_publish_cfg(void) +{ + wifi_country_t country; + uint8_t start_chan, end_chan; + + wifi_nan_usd_config_t cfg = { + .usd_default_channel = 6, + .n_min = 5, + .n_max = 10, + .m_min = 5, + .m_max = 10, + }; + + esp_wifi_get_country(&country); + start_chan = country.schan; + end_chan = country.schan + country.nchan - 1; + for (uint8_t chan = start_chan; chan <= end_chan; chan++) { + cfg.usd_chan_bitmap.ghz_2_channels |= CHANNEL_TO_BIT(chan); + } +#if CONFIG_SOC_WIFI_SUPPORT_5G + cfg.usd_chan_bitmap.ghz_5_channels = country.wifi_5g_channel_mask; +#endif + + return cfg; +} + +wifi_nan_usd_config_t esp_wifi_usd_get_default_subscribe_cfg(void) +{ + wifi_country_t country; + uint8_t start_chan, end_chan; + + wifi_nan_usd_config_t cfg = { + .usd_default_channel = 6, + .n_min = 5, + .n_max = 10, + .m_min = 5, + .m_max = 10 + }; + + esp_wifi_get_country(&country); + start_chan = country.schan; + end_chan = country.schan + country.nchan - 1; + for (uint8_t chan = start_chan; chan <= end_chan; chan++) { + cfg.usd_chan_bitmap.ghz_2_channels |= CHANNEL_TO_BIT(chan); + } +#if CONFIG_SOC_WIFI_SUPPORT_5G + cfg.usd_chan_bitmap.ghz_5_channels = country.wifi_5g_channel_mask; +#endif + + return cfg; +} +#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */ diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index ff4659ee42e..3ebc9155bbb 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -240,12 +240,21 @@ else() set(wps_registrar_src "") endif() +if(CONFIG_ESP_WIFI_NAN_USD_ENABLE) + set(usd_src + "src/common/nan_de.c" + "esp_supplicant/src/esp_nan_usd.c") +else() + set(usd_src "") +endif() + idf_component_register(SRCS "${srcs}" "${esp_srcs}" "${tls_src}" "${roaming_src}" - "${crypto_src}" "${mbo_src}" "${dpp_src}" "${wps_registrar_src}" + "${crypto_src}" "${mbo_src}" "${dpp_src}" "${wps_registrar_src}" "${usd_src}" INCLUDE_DIRS include port/include esp_supplicant/include PRIV_INCLUDE_DIRS src src/utils esp_supplicant/src src/crypto ../esp_wifi/wifi_apps/roaming_app/include ../esp_wifi/wifi_apps/roaming_app/src + esp_supplicant/include/esp_private LDFRAGMENTS ${linker_fragments} PRIV_REQUIRES mbedtls esp_timer esp_wifi) @@ -349,6 +358,9 @@ endif() if(CONFIG_ESP_WIFI_EAP_TLS1_3) target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_TLSV13) endif() +if(CONFIG_ESP_WIFI_NAN_USD_ENABLE) + target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_NAN_USD) +endif() set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY LINK_INTERFACE_MULTIPLICITY 3) target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/components/wpa_supplicant/esp_supplicant/include/esp_private/esp_nan_usd.h b/components/wpa_supplicant/esp_supplicant/include/esp_private/esp_nan_usd.h new file mode 100644 index 00000000000..fbb89146245 --- /dev/null +++ b/components/wpa_supplicant/esp_supplicant/include/esp_private/esp_nan_usd.h @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_err.h" +#include "esp_wifi_types.h" + +#define GLOBAL_OPERATING_CLASS_81 81 + +#define ESP_USD_SUBSCRIBE_DEFAULT_PARAMS() { \ + .active = false, \ + .ttl = 0, \ + .freq = NAN_USD_DEFAULT_FREQ, \ + .query_period = 0, \ +}; + +#define ESP_USD_PUBLISH_DEFAULT_PARAMS() { \ + .unsolicited = true, \ + .solicited = true, \ + .ttl = 100, \ + .fsd = true, \ + .freq = NAN_USD_DEFAULT_FREQ, \ + .freq_list = NULL, \ +}; + +/* Note: These NaN-USD APIs are intended for internal use only. + * For application development, use the public APIs provided in: + * 'wifi_apps/nan_app/include/esp_nan.h' + */ +esp_err_t esp_nan_usd_init(void); + +esp_err_t esp_nan_usd_deinit(void); + +int esp_nan_usd_publish(const wifi_nan_publish_cfg_t *publish_cfg); + +esp_err_t esp_nan_usd_cancel_publish(int publish_id); + +int esp_nan_usd_subscribe(const wifi_nan_subscribe_cfg_t *subscribe_cfg); + +esp_err_t esp_nan_usd_cancel_subscribe(int subscribe_id); + +esp_err_t esp_nan_usd_transmit(int handle, const uint8_t *ssi, uint16_t ssi_len, + const uint8_t *peer_addr, uint8_t req_instance_id); + +esp_err_t esp_nan_usd_cancel_service(int service_id); +#ifdef __cplusplus +} +#endif diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_nan_usd.c b/components/wpa_supplicant/esp_supplicant/src/esp_nan_usd.c new file mode 100644 index 00000000000..f4a4d941343 --- /dev/null +++ b/components/wpa_supplicant/esp_supplicant/src/esp_nan_usd.c @@ -0,0 +1,679 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_wifi.h" +#include "esp_check.h" +#include "esp_nan_usd.h" +#include "common/nan_de.h" +#include "esp_wifi_driver.h" +#include "common/ieee802_11_common.h" +#include "utils/eloop.h" +#include "esp_nan.h" + +struct nan_de *g_nan_de = NULL; + +static void *s_nan_usd_data_lock = NULL; +#define NAN_USD_DATA_LOCK() os_mutex_lock(s_nan_usd_data_lock) +#define NAN_USD_DATA_UNLOCK() os_mutex_unlock(s_nan_usd_data_lock) + +#ifdef DEBUG_PRINT +static const char *nan_reason_txt(enum nan_de_reason reason) +{ + switch (reason) { + case NAN_DE_REASON_TIMEOUT: + return "timeout"; + case NAN_DE_REASON_USER_REQUEST: + return "user-request"; + case NAN_DE_REASON_FAILURE: + return "failure"; + } + + return "unknown"; +} +#endif /* DEBUG_PRINT */ +static void nan_sta_stop_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_id == WIFI_EVENT_STA_STOP) { + esp_wifi_nan_usd_stop(); + } +} + +/* Per section 4.5.3 of the Wi-Fi Aware Specification v4.0 (NaN-USD), + * only 20 MHz bandwidth channels are permitted for NAN-USD operation + * in both the 2.4 GHz and 5 GHz frequency bands. + * */ +static int esp_nan_chan_to_freq(uint8_t chan) +{ + // 2.4 GHz band + if (chan >= 1 && chan <= 13) { + return 2407 + 5 * chan; + } else if (chan == 14) { + return 2414 + 5 * chan; + } + + // 5 GHz band — standard 20 MHz channel ranges + if ((chan >= 36 && chan <= 64) || + (chan >= 100 && chan <= 144) || + (chan >= 149 && chan <= 165)) { + return 5000 + chan * 5; + } + + return -1; +} + +static int esp_nan_freq_to_chan(int freq) +{ + // 2.4 GHz band + if (freq >= 2412 && freq <= 2472) { + return (freq - 2407) / 5; + } else if (freq == 2484) { + return 14; + } + + // 5 GHz band + if ((freq >= 5180 && freq <= 5240) || // Channels 36–64 + (freq >= 5500 && freq <= 5720) || // Channels 100–144 + (freq >= 5745 && freq <= 5825)) { // Channels 149–165 + return (freq - 5000) / 5; + } + return -1; +} + +static void nan_de_tx_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_id == WIFI_EVENT_ACTION_TX_STATUS) { + wifi_event_action_tx_status_t *evt = (wifi_event_action_tx_status_t *)event_data; + if (evt->status == WIFI_ACTION_TX_DONE) { + int freq = esp_nan_chan_to_freq(evt->channel); + if (freq == -1) { + wpa_printf(MSG_ERROR, "Invalid channel received from Action Tx handler"); + return; + } + NAN_USD_DATA_LOCK(); + nan_de_tx_status(g_nan_de, freq, NULL); + NAN_USD_DATA_UNLOCK(); + } else if (evt->status == WIFI_ACTION_TX_DURATION_COMPLETED) { + NAN_USD_DATA_LOCK(); + nan_de_tx_wait_ended(g_nan_de); + NAN_USD_DATA_UNLOCK(); + } + } else if (event_id == WIFI_EVENT_ROC_DONE) { + wifi_event_roc_done_t *evt = (wifi_event_roc_done_t *)event_data; + int freq = esp_nan_chan_to_freq(evt->channel); + if (freq == -1) { + wpa_printf(MSG_ERROR, "Invalid channel received from ROC done handler"); + } + NAN_USD_DATA_LOCK(); + nan_de_listen_ended(g_nan_de, freq); + NAN_USD_DATA_UNLOCK(); + } +} + +int esp_nan_de_rx_action(uint8_t *hdr, uint8_t *payload, size_t len, uint8_t channel) +{ + struct ieee80211_hdr *rx_hdr = (struct ieee80211_hdr *)hdr; + int freq; + + if (len < 6) { + /* Frame too short for NAN-SDF frame */ + return ESP_FAIL; + } + /* NAN SDF + * Category code - 1 byte + * Public Action - 1 byte + * OUI - 3 bytes + * WFA subtype - 1 byte + * NAN PROTOCOL - Variable */ + + uint8_t category = *payload++; + uint8_t public_action = *payload++; + uint32_t oui_value = WPA_GET_BE24(payload); + payload += 3; + uint8_t oui_type = *payload++; + + if (category == WLAN_ACTION_PUBLIC && + public_action == WLAN_PA_VENDOR_SPECIFIC && + oui_value == OUI_WFA && + oui_type == NAN_OUI_TYPE) { + /* Received a valid NAN Service Discovery Frame */ + } else { + /* Frame is not a NAN SDF frame */ + return ESP_FAIL; + } + + freq = esp_nan_chan_to_freq(channel); + if (freq == -1) { + wpa_printf(MSG_ERROR, "Invalid channel from Rx action frame"); + return ESP_FAIL; + } + + NAN_USD_DATA_LOCK(); + nan_de_rx_sdf(g_nan_de, rx_hdr->addr2, rx_hdr->addr3, freq, payload, len - 6); + NAN_USD_DATA_UNLOCK(); + return ESP_OK; +} + +static int esp_nan_de_tx(void *ctx, unsigned int freq, unsigned int wait_time, + const u8 *dst, const u8 *src, const u8 *bssid, + const struct wpabuf *buf) +{ + int buf_len = buf->used; + int channel; + + wifi_action_tx_req_t *req = os_zalloc(sizeof(*req) + buf_len); + if (!req) { + wpa_printf(MSG_ERROR, "Allocation for tx request failed"); + return ESP_FAIL; + } + + req->ifx = WIFI_IF_STA; + req->type = WIFI_OFFCHAN_TX_REQ; + req->wait_time_ms = wait_time; + memcpy(req->dest_mac, dst, ETH_ALEN); + memcpy(req->bssid, bssid, ETH_ALEN); + req->no_ack = false; + req->data_len = buf_len; + req->rx_cb = esp_nan_de_rx_action; + memcpy(req->data, buf->buf, buf_len); + + channel = esp_nan_freq_to_chan(freq); + if (channel == -1) { + wpa_printf(MSG_ERROR, "Could not determine channel for freq %d", freq); + os_free(req); + return ESP_FAIL; + } + req->channel = channel; + + if (esp_wifi_action_tx_req(req) != ESP_OK) { + wpa_printf(MSG_ERROR, "Offchannel tx request failed"); + os_free(req); + return ESP_FAIL; + } + + os_free(req); + return ESP_OK; +} + +static int esp_nan_de_listen(void *ctx, unsigned int freq, unsigned int duration) +{ + int channel; + + channel = esp_nan_freq_to_chan(freq); + if (channel == -1) { + wpa_printf(MSG_ERROR, "Could not determine channel for freq %d", freq); + return ESP_FAIL; + } + + wifi_roc_req_t *req = os_zalloc(sizeof(wifi_roc_req_t)); + if (req == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate for ROC request"); + return ESP_FAIL; + } + req->ifx = WIFI_IF_STA; + req->type = WIFI_ROC_REQ; + req->channel = channel; + req->wait_time_ms = duration; + req->rx_cb = esp_nan_de_rx_action; + req->done_cb = NULL; + req->allow_broadcast = true; + + if (esp_wifi_remain_on_channel(req) != ESP_OK) { + wpa_printf(MSG_ERROR, "ROC request failure"); + os_free(req); + return ESP_FAIL; + } + + NAN_USD_DATA_LOCK(); + nan_de_listen_started(g_nan_de, freq, duration); + NAN_USD_DATA_UNLOCK(); + os_free(req); + return ESP_OK; +} + +static void esp_nan_de_discovery_result(void *ctx, int subscribe_id, enum nan_service_protocol_type srv_proto_type, + const u8 *ssi, size_t ssi_len, int peer_publish_id, const u8 *peer_addr, bool fsd, bool fsd_gas) +{ + wpa_printf(MSG_INFO, "NAN_USD DISCOVERY_RESULT - subscribe_id = %d peer_publish_id = %d peer_address = "MACSTR" service_protocol_type = %d", + subscribe_id, peer_publish_id, MAC2STR(peer_addr), srv_proto_type); + + wifi_event_nan_svc_match_t *evt = os_zalloc(sizeof(wifi_event_nan_svc_match_t) + ssi_len); + if (evt == NULL) { + return; + } + evt->subscribe_id = subscribe_id; + evt->publish_id = peer_publish_id; + memcpy(evt->pub_if_mac, peer_addr, ETH_ALEN); + if (ssi && ssi_len) { + memcpy(evt->ssi, ssi, ssi_len); + evt->ssi_len = ssi_len; + } + esp_event_post(WIFI_EVENT, WIFI_EVENT_NAN_SVC_MATCH, evt, sizeof(wifi_event_nan_svc_match_t) + ssi_len, portMAX_DELAY); + os_free(evt); +} + +static void esp_nan_de_replied(void *ctx, int publish_id, const u8 *peer_addr, + int peer_subscribe_id, + enum nan_service_protocol_type srv_proto_type, + const u8 *ssi, size_t ssi_len) +{ + + wpa_printf(MSG_INFO, "NAN_USD REPLIED - publish_id = %d peer_subscribe_id = %d peer_address = "MACSTR" service_protocol_type = %d", + publish_id, peer_subscribe_id, MAC2STR(peer_addr), srv_proto_type); + + wifi_event_nan_replied_t *evt = os_zalloc(sizeof(wifi_event_nan_replied_t) + ssi_len); + if (evt == NULL) { + return; + } + evt->publish_id = publish_id; + evt->subscribe_id = peer_subscribe_id; + memcpy(evt->sub_if_mac, peer_addr, ETH_ALEN); + if (ssi && ssi_len) { + memcpy(evt->ssi, ssi, ssi_len); + evt->ssi_len = ssi_len; + } + esp_event_post(WIFI_EVENT, WIFI_EVENT_NAN_REPLIED, evt, sizeof(wifi_event_nan_replied_t) + ssi_len, portMAX_DELAY); + os_free(evt); +} + +static void esp_nan_de_publish_terminated(void *ctx, int publish_id, + enum nan_de_reason reason) +{ + wpa_printf(MSG_INFO, "NAN_USD PUBLISH_TERMINATED - publish_id = %d reason = %s", publish_id, nan_reason_txt(reason)); +} + +static void esp_nan_de_subscribe_terminated(void *ctx, int subscribe_id, + enum nan_de_reason reason) +{ + wpa_printf(MSG_INFO, "NAN_USD SUBSCRIBE_TERMINATED - subscribe_id = %d reason = %s", subscribe_id, nan_reason_txt(reason)); +} + +static void esp_nan_de_receive(void *ctx, int id, int peer_instance_id, + const u8 *ssi, size_t ssi_len, + const u8 *peer_addr) +{ + wpa_hexdump(MSG_INFO, "NAN_RECEIVE", ssi, ssi_len); + + wifi_event_nan_receive_t *evt = os_zalloc(sizeof(wifi_event_nan_receive_t) + ssi_len); + if (evt == NULL) { + return; + } + evt->inst_id = id; + evt->peer_inst_id = peer_instance_id; + memcpy(evt->peer_if_mac, peer_addr, ETH_ALEN); + if (ssi && ssi_len) { + memcpy(evt->ssi, ssi, ssi_len); + evt->ssi_len = ssi_len; + } + esp_event_post(WIFI_EVENT, WIFI_EVENT_NAN_RECEIVE, evt, sizeof(wifi_event_nan_receive_t) + ssi_len, portMAX_DELAY); + os_free(evt); +} + +esp_err_t esp_nan_usd_deinit() +{ + + NAN_USD_DATA_LOCK(); + if (!g_nan_de) { + NAN_USD_DATA_UNLOCK(); + return ESP_FAIL; + } + + nan_de_deinit(g_nan_de); + g_nan_de = NULL; + NAN_USD_DATA_UNLOCK(); + + if (s_nan_usd_data_lock) { + os_mutex_delete(s_nan_usd_data_lock); + s_nan_usd_data_lock = NULL; + } + + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_ACTION_TX_STATUS, &nan_de_tx_event_handler); + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_ROC_DONE, &nan_de_tx_event_handler); + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_STOP, + &nan_sta_stop_handler); + + return ESP_OK; +} + +esp_err_t esp_nan_usd_init(void) +{ + struct nan_callbacks cb; + uint8_t mac[ETH_ALEN]; +#ifndef ESP_SUPPLICANT + bool offload = wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_NAN_OFFLOAD; +#else + bool offload = false; +#endif + int max_listen = 1000; // default supplicant value; hardcoded for now + + os_memset(&cb, 0, sizeof(cb)); + cb.ctx = NULL; + cb.tx = esp_nan_de_tx; + cb.listen = esp_nan_de_listen; + cb.discovery_result = esp_nan_de_discovery_result; + cb.replied = esp_nan_de_replied; + cb.publish_terminated = esp_nan_de_publish_terminated; + cb.subscribe_terminated = esp_nan_de_subscribe_terminated; + cb.receive = esp_nan_de_receive; + + s_nan_usd_data_lock = os_recursive_mutex_create(); + if (!s_nan_usd_data_lock) { + ESP_LOGE("NAN-USD", "Failed to create NAN-USD data lock"); + return ESP_FAIL; + } + + ESP_RETURN_ON_ERROR(esp_wifi_get_mac(WIFI_IF_STA, mac), "NAN-USD", "Fetching MAC of STA ifx failed"); + g_nan_de = nan_de_init(mac, offload, false, max_listen, &cb); + if (!g_nan_de) { + return ESP_FAIL; + } + esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_ACTION_TX_STATUS, + &nan_de_tx_event_handler, NULL); + esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_ROC_DONE, + &nan_de_tx_event_handler, NULL); + esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_STOP, + &nan_sta_stop_handler, NULL); + + return ESP_OK; +} + +int esp_nan_get_freq_list(int *freq_list, const uint8_t *chan_list, uint8_t chan_list_len) +{ + int freq, freq_ind = 0; + for (int i = 0; i < chan_list_len; i++) { + freq = esp_nan_chan_to_freq(chan_list[i]); + if (freq != -1) { + freq_list[freq_ind++] = freq; + } + } + freq_list[freq_ind++] = 0; + return freq_ind; +} + +static int esp_nan_usd_publish_internal(const char *service_name, enum nan_service_protocol_type srv_proto_type, + unsigned int ttl, uint8_t *ssi, uint16_t ssi_len, uint8_t default_channel, + const wifi_scan_channel_bitmap_t channel_bitmap) +{ + int publish_id; + struct wpabuf *buf = NULL; + int freq, *freq_list = NULL; + struct nan_publish_params pub_params = ESP_USD_PUBLISH_DEFAULT_PARAMS(); + bool p2p = false; + uint8_t i = 0, chan_list_len; + uint8_t bitmap_idx_2g = 1; // BIT-0 is not used in channel bitmap + uint16_t channel_2ghz_bitmap; +#if CONFIG_SOC_WIFI_SUPPORT_5G + uint8_t bitmap_idx_5g = 1; // BIT-0 is not used in channel bitmap + uint32_t channel_5ghz_bitmap; +#endif + + NAN_USD_DATA_LOCK(); + if (!g_nan_de) { + goto fail; + } + + pub_params.ttl = ttl; + if (ssi && ssi_len) { + buf = wpabuf_alloc(ssi_len); + if (!buf) { + wpa_printf(MSG_ERROR, "Allocating memory failed for NaN-USD Publish"); + goto fail; + } + wpabuf_put_data(buf, ssi, ssi_len); + } + + freq = esp_nan_chan_to_freq(default_channel); + if (freq != -1) { + pub_params.freq = freq; + } else { + pub_params.freq = NAN_USD_DEFAULT_FREQ; + } + + channel_2ghz_bitmap = channel_bitmap.ghz_2_channels; + channel_2ghz_bitmap &= ~BIT(0); // BIT-0 is not used for channel + chan_list_len = __builtin_popcount(channel_2ghz_bitmap); + +#if CONFIG_SOC_WIFI_SUPPORT_5G + channel_5ghz_bitmap = channel_bitmap.ghz_5_channels; + channel_5ghz_bitmap &= ~BIT(0); // BIT-0 is not used for channel + chan_list_len += __builtin_popcount(channel_5ghz_bitmap); +#endif + + if (chan_list_len) { + uint8_t chan_list[chan_list_len]; + while (channel_2ghz_bitmap) { + bitmap_idx_2g = __builtin_ctz(channel_2ghz_bitmap); + uint8_t chan_num = BIT_NUMBER_TO_CHANNEL(bitmap_idx_2g, WIFI_BAND_2G); + if (chan_num != 0) { + chan_list[i++] = chan_num; + } + channel_2ghz_bitmap &= ~BIT(bitmap_idx_2g); + } +#if CONFIG_SOC_WIFI_SUPPORT_5G + while (channel_5ghz_bitmap) { + bitmap_idx_5g = __builtin_ctz(channel_5ghz_bitmap); + uint8_t chan_num = BIT_NUMBER_TO_CHANNEL(bitmap_idx_5g, WIFI_BAND_5G); + if (chan_num != 0) { + chan_list[i++] = chan_num; + } + channel_5ghz_bitmap &= ~BIT(bitmap_idx_5g); + } +#endif + + chan_list_len = i; + freq_list = (int *)os_malloc(sizeof(int) * (chan_list_len + 1)); + if (freq_list) { + esp_nan_get_freq_list(freq_list, chan_list, chan_list_len); + pub_params.freq_list = freq_list; + } else { + wpa_printf(MSG_ERROR, "Allocating memory failed for frequency list"); + wpabuf_free(buf); + goto fail; + } + } + + publish_id = nan_de_publish(g_nan_de, service_name, srv_proto_type, + buf, NULL, &pub_params, p2p); + + wpabuf_free(buf); + if (freq_list) { + os_free(freq_list); + } + NAN_USD_DATA_UNLOCK(); + return publish_id; +fail: + NAN_USD_DATA_UNLOCK(); + return -1; +} + +int esp_nan_usd_publish(const wifi_nan_publish_cfg_t *publish_cfg) +{ + return esp_nan_usd_publish_internal(publish_cfg->service_name, WIFI_SVC_PROTO_RESERVED, + publish_cfg->ttl, publish_cfg->ssi, + publish_cfg->ssi_len, publish_cfg->usd_publish_config.usd_default_channel, + publish_cfg->usd_publish_config.usd_chan_bitmap); +} + +esp_err_t esp_nan_usd_update_publish(int publish_id, uint8_t *ssi, uint16_t ssi_len) +{ + struct wpabuf *buf = NULL; + esp_err_t ret; + + NAN_USD_DATA_LOCK(); + if (!g_nan_de) { + goto fail; + } + + if (ssi && ssi_len) { + buf = wpabuf_alloc(ssi_len); + if (!buf) { + wpa_printf(MSG_ERROR, "Allocating memory failed for NaN-USD Update Publish"); + goto fail; + } + wpabuf_put_data(buf, ssi, ssi_len); + } + + ret = nan_de_update_publish(g_nan_de, publish_id, buf); + wpabuf_free(buf); + NAN_USD_DATA_UNLOCK(); + return ret; +fail: + NAN_USD_DATA_UNLOCK(); + return ESP_FAIL; +} + +esp_err_t esp_nan_usd_cancel_publish(int publish_id) +{ + NAN_USD_DATA_LOCK(); + if (!g_nan_de) { + NAN_USD_DATA_UNLOCK(); + return ESP_FAIL; + } + + nan_de_cancel_publish(g_nan_de, publish_id); + NAN_USD_DATA_UNLOCK(); + return ESP_OK; +} + +/* Note: Our USD implementation uses the Service Protocol Type provided in SSI. + * The 'srv_proto_type' parameter will be ignored. + */ +static int esp_nan_usd_subscribe_internal(const char *service_name, enum nan_service_protocol_type srv_proto_type, + unsigned int ttl, uint8_t default_channel, uint8_t *ssi, uint16_t ssi_len, + const wifi_scan_channel_bitmap_t channel_bitmap) +{ + int subscribe_id; + struct wpabuf *buf = NULL; + int freq, *freq_list = NULL; + /* USD Specification allows either active or passive mode for subscriber. + * By default USD-Subscriber will be in passive mode */ + struct nan_subscribe_params sub_params = ESP_USD_SUBSCRIBE_DEFAULT_PARAMS(); + bool p2p = false; + uint8_t i = 0, chan_list_len; + uint8_t bitmap_idx_2g = 1; // BIT-0 is not used in channel bitmap + uint16_t channel_2ghz_bitmap; + + NAN_USD_DATA_LOCK(); + if (!g_nan_de) { + goto fail; + } + + sub_params.ttl = ttl; + freq = esp_nan_chan_to_freq(default_channel); + if (freq != -1) { + sub_params.freq = freq; + } else { + sub_params.freq = NAN_USD_DEFAULT_FREQ; + } + + if (ssi && ssi_len) { + buf = wpabuf_alloc(ssi_len); + if (!buf) { + wpa_printf(MSG_ERROR, "Allocating memory failed for NaN-USD Subscribe"); + goto fail; + } + wpabuf_put_data(buf, ssi, ssi_len); + } + + channel_2ghz_bitmap = channel_bitmap.ghz_2_channels; + channel_2ghz_bitmap &= ~BIT(0); // BIT-0 is not used for channel + chan_list_len = __builtin_popcount(channel_2ghz_bitmap); + if (chan_list_len) { + uint8_t chan_list[chan_list_len]; + while (channel_2ghz_bitmap) { + bitmap_idx_2g = __builtin_ctz(channel_2ghz_bitmap); + uint8_t chan_num = BIT_NUMBER_TO_CHANNEL(bitmap_idx_2g, WIFI_BAND_2G); + if (chan_num != 0) { + chan_list[i++] = chan_num; + } + channel_2ghz_bitmap &= ~BIT(bitmap_idx_2g); + } + freq_list = (int *)os_malloc(sizeof(int) * (chan_list_len + 1)); + if (freq_list) { + esp_nan_get_freq_list(freq_list, chan_list, chan_list_len); + sub_params.freq_list = freq_list; + } else { + wpa_printf(MSG_ERROR, "Allocating memory failed for frequency list"); + wpabuf_free(buf); + goto fail; + } + } + + subscribe_id = nan_de_subscribe(g_nan_de, service_name, srv_proto_type, buf, NULL, &sub_params, p2p); + + wpabuf_free(buf); + if (freq_list) { + os_free(freq_list); + } + NAN_USD_DATA_UNLOCK(); + return subscribe_id; +fail: + NAN_USD_DATA_UNLOCK(); + return -1; +} + +int esp_nan_usd_subscribe(const wifi_nan_subscribe_cfg_t *subscribe_cfg) +{ + return esp_nan_usd_subscribe_internal(subscribe_cfg->service_name, WIFI_SVC_PROTO_RESERVED, subscribe_cfg->ttl, + subscribe_cfg->usd_subscribe_config.usd_default_channel, subscribe_cfg->ssi, subscribe_cfg->ssi_len, + subscribe_cfg->usd_subscribe_config.usd_chan_bitmap); +} + +esp_err_t esp_nan_usd_cancel_subscribe(int subscribe_id) +{ + NAN_USD_DATA_LOCK(); + if (!g_nan_de) { + NAN_USD_DATA_UNLOCK(); + return ESP_FAIL; + } + + nan_de_cancel_subscribe(g_nan_de, subscribe_id); + NAN_USD_DATA_UNLOCK(); + return ESP_OK; +} + +esp_err_t esp_nan_usd_cancel_service(int service_id) +{ + NAN_USD_DATA_LOCK(); + if (!g_nan_de) { + NAN_USD_DATA_UNLOCK(); + return ESP_FAIL; + } + + nan_de_cancel_service(g_nan_de, service_id); + NAN_USD_DATA_UNLOCK(); + return ESP_OK; +} + +esp_err_t esp_nan_usd_transmit(int handle, const uint8_t *ssi, uint16_t ssi_len, const u8 *peer_addr, u8 req_instance_id) +{ + struct wpabuf *buf = NULL; + esp_err_t ret; + + NAN_USD_DATA_LOCK(); + if (!g_nan_de || !peer_addr) { + goto fail; + } + + if (ssi && ssi_len) { + buf = wpabuf_alloc(ssi_len); + if (!buf) { + wpa_printf(MSG_ERROR, "Allocating memory failed for NaN-USD transmit"); + goto fail; + } + wpabuf_put_data(buf, ssi, ssi_len); + } + + ret = nan_de_transmit(g_nan_de, handle, buf, NULL, peer_addr, req_instance_id); + + wpabuf_free(buf); + NAN_USD_DATA_UNLOCK(); + return ret; +fail: + NAN_USD_DATA_UNLOCK(); + return ESP_FAIL; +} diff --git a/components/wpa_supplicant/src/common/ieee802_11_defs.h b/components/wpa_supplicant/src/common/ieee802_11_defs.h index e8adaeef0c4..86231b09a1b 100644 --- a/components/wpa_supplicant/src/common/ieee802_11_defs.h +++ b/components/wpa_supplicant/src/common/ieee802_11_defs.h @@ -567,8 +567,12 @@ struct ieee80211_ht_operation { /* 2 - Reserved */ #define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3 -#define MBO_IE_VENDOR_TYPE 0x506f9a16 #define OSEN_IE_VENDOR_TYPE 0x506f9a12 +#define NAN_IE_VENDOR_TYPE 0x506f9a13 +#define NAN_SDF_VENDOR_TYPE 0x506f9a13 +#define NAN_OUI_TYPE 0x13 +#define MBO_IE_VENDOR_TYPE 0x506f9a16 +#define NAN_NAF_VENDOR_TYPE 0x506f9a18 #define SAE_PK_IE_VENDOR_TYPE 0x506f9a1f #define SAE_PK_OUI_TYPE 0x1f #define MBO_OUI_TYPE 22 diff --git a/components/wpa_supplicant/src/common/nan.h b/components/wpa_supplicant/src/common/nan.h new file mode 100644 index 00000000000..19ab7468711 --- /dev/null +++ b/components/wpa_supplicant/src/common/nan.h @@ -0,0 +1,98 @@ +/* + * NAN (Wi-Fi Aware) definitions + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NAN_H +#define NAN_H + +enum nan_attr_id { + NAN_ATTR_MASTER_INDICATION = 0x00, + NAN_ATTR_CLUSTER = 0x01, + NAN_ATTR_NAN_ATTR_SERVICE_ID_LIST = 0x02, + NAN_ATTR_SDA = 0x03, /* Service Descriptor attribute */ + NAN_ATTR_CONN_CAPA = 0x04, + NAN_ATTR_WLAN_INFRA = 0x05, + NAN_ATTR_P2P_OPER = 0x06, + NAN_ATTR_IBSS = 0x07, + NAN_ATTR_MESH = 0x08, + NAN_ATTR_FURTHER_NAN_SD = 0x09, + NAN_ATTR_FURTHER_AVAIL_MAP = 0x0A, + NAN_ATTR_COUNTRY_CODE = 0x0B, + NAN_ATTR_RANGIN = 0x0C, + NAN_ATTR_CLUSTER_DISCOVERY = 0x0D, + NAN_ATTR_SDEA = 0x0E, /* Service Descriptor Extension attribute */ + NAN_ATTR_DEVICE_CAPABILITY = 0x0F, + NAN_ATTR_NDP = 0x10, + NAN_ATTR_NAN_AVAILABILITY = 0x12, + NAN_ATTR_NDC = 0x13, + NAN_ATTR_NDL = 0x14, + NAN_ATTR_NDL_QOS = 0x15, + NAN_ATTR_UNALIGNED_SCHEDULE = 0x17, + NAN_ATTR_RANGING_INFO = 0x1A, + NAN_ATTR_RANGING_SETUP = 0x1B, + NAN_ATTR_FTM_RANGING_REPORT = 0x1C, + NAN_ATTR_ELEM_CONTAINER = 0x1D, + NAN_ATTR_EXT_WLAN_INFRA = 0x1E, + NAN_ATTR_EXT_P2P_OPER = 0x1FE, + NAN_ATTR_EXT_IBSS = 0x20, + NAN_ATTR_EXT_MESH = 0x21, + NAN_ATTR_CSIA = 0x22, /* Cipher Suite Info attribute */ + NAN_ATTR_SCIA = 0x23, /* Security Context Info attribute */ + NAN_ATTR_SHARED_KEY_DESCR = 0x24, + NAN_ATTR_PUBLIC_AVAILABILITY = 0x27, + NAN_ATTR_SUBSC_SERVICE_ID_LIST = 0x28, + NAN_ATTR_NDP_EXT = 0x29, + NAN_ATTR_DCEA = 0x2A, /* Device Capability Extension attribute */ + NAN_ATTR_NIRA = 0x2B, /* NAN Identity Resolution attribute */ + NAN_ATTR_BPBA = 0x2C, /* NAN Pairing Bootstrapping attribute */ + NAN_ATTR_S3 = 0x2D, + NAN_ATTR_TPEA = 0x2E, /* Transmit Power Envelope attribute */ + NAN_ATTR_VENDOR_SPECIFIC = 0xDD, +}; + +/* Service Descriptor attribute (SDA) */ + +/* Service Control field */ +#define NAN_SRV_CTRL_TYPE_MASK (BIT(0) | BIT(1)) +#define NAN_SRV_CTRL_MATCHING_FILTER BIT(2) +#define NAN_SRV_CTRL_RESP_FILTER BIT(3) +#define NAN_SRV_CTRL_SRV_INFO BIT(4) +#define NAN_SRV_CTRL_DISCOVERY_RANGE_LIMITED BIT(5) +#define NAN_SRV_CTRL_BINDING_BITMAP BIT(6) + +enum nan_service_control_type { + NAN_SRV_CTRL_PUBLISH = 0, + NAN_SRV_CTRL_SUBSCRIBE = 1, + NAN_SRV_CTRL_FOLLOW_UP = 2, +}; + +/* Service Descriptor Extension attribute (SDEA) */ + +/* SDEA Control field */ +#define NAN_SDEA_CTRL_FSD_REQ BIT(0) +#define NAN_SDEA_CTRL_FSD_GAS BIT(1) +#define NAN_SDEA_CTRL_DATA_PATH_REQ BIT(2) +#define NAN_SDEA_CTRL_DATA_PATH_TYPE BIT(3) +#define NAN_SDEA_CTRL_QOS_REQ BIT(5) +#define NAN_SDEA_CTRL_SECURITY_REQ BIT(6) +#define NAN_SDEA_CTRL_RANGING_REQ BIT(7) +#define NAN_SDEA_CTRL_RANGE_LIMIT BIT(8) +#define NAN_SDEA_CTRL_SRV_UPD_INDIC BIT(9) +#define NAN_SDEA_CTRL_GTK_REQ BIT(10) + +enum nan_service_protocol_type { + NAN_SRV_PROTO_BONJOUR = 1, + NAN_SRV_PROTO_GENERIC = 2, + NAN_SRV_PROTO_CSA_MATTER = 3, +}; + +#define NAN_ATTR_HDR_LEN 3 +#define NAN_SERVICE_ID_LEN 6 + +#define NAN_USD_DEFAULT_FREQ 2437 + +#endif /* NAN_H */ diff --git a/components/wpa_supplicant/src/common/nan_de.c b/components/wpa_supplicant/src/common/nan_de.c new file mode 100644 index 00000000000..8afe7d96714 --- /dev/null +++ b/components/wpa_supplicant/src/common/nan_de.c @@ -0,0 +1,1587 @@ +/* + * NAN Discovery Engine + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/crypto.h" +#include "crypto/sha256.h" +#include "ieee802_11_defs.h" +#include "nan.h" +#include "nan_de.h" +#include "ctype.h" + +static const u8 nan_network_id[ETH_ALEN] = +{ 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 }; + +enum nan_de_service_type { + NAN_DE_PUBLISH, + NAN_DE_SUBSCRIBE, +}; + +static const u8 p2p_network_id[ETH_ALEN] = +{ 0x51, 0x6f, 0x9a, 0x02, 0x00, 0x00 }; + +static const u8 wildcard_bssid[ETH_ALEN] = +{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +struct nan_de_service { + int id; + enum nan_de_service_type type; + char *service_name; + u8 service_id[NAN_SERVICE_ID_LEN]; + struct nan_publish_params publish; + struct nan_subscribe_params subscribe; + enum nan_service_protocol_type srv_proto_type; + struct wpabuf *ssi; + struct wpabuf *elems; + struct os_reltime time_started; + struct os_reltime end_time; + struct os_reltime last_multicast; + struct os_reltime first_discovered; + bool needs_fsd; + unsigned int freq; + unsigned int default_freq; + int *freq_list; + u8 a3[ETH_ALEN]; + bool a3_set; + + /* pauseState information for Publish function */ + struct os_reltime pause_state_end; + u8 sel_peer_id; + u8 sel_peer_addr[ETH_ALEN]; + + /* Publish state - channel iteration */ + bool in_multi_chan; + bool first_multi_chan; + int multi_chan_idx; /* index to freq_list[] */ + struct os_reltime next_publish_state; + struct os_reltime next_publish_chan; + unsigned int next_publish_duration; + bool is_p2p; +}; + +struct nan_de { + u8 nmi[ETH_ALEN]; + bool offload; + bool ap; + unsigned int max_listen; + struct nan_callbacks cb; + + struct nan_de_service *service[NAN_DE_MAX_SERVICE]; + unsigned int num_service; + + int next_handle; + + unsigned int ext_listen_freq; + unsigned int listen_freq; + unsigned int tx_wait_status_freq; + unsigned int tx_wait_end_freq; +}; + + +bool nan_de_is_nan_network_id(const u8 *addr) +{ + return ether_addr_equal(addr, nan_network_id); +} + + +bool nan_de_is_p2p_network_id(const u8 *addr) +{ + return ether_addr_equal(addr, p2p_network_id); +} + + +struct nan_de * nan_de_init(const u8 *nmi, bool offload, bool ap, + unsigned int max_listen, + const struct nan_callbacks *cb) +{ + struct nan_de *de; + + de = os_zalloc(sizeof(*de)); + if (!de) + return NULL; + + os_memcpy(de->nmi, nmi, ETH_ALEN); + de->offload = offload; + de->ap = ap; + de->max_listen = max_listen ? max_listen : 1000; + os_memcpy(&de->cb, cb, sizeof(*cb)); + + return de; +} + + +static void nan_de_service_free(struct nan_de_service *srv) +{ + os_free(srv->service_name); + wpabuf_free(srv->ssi); + wpabuf_free(srv->elems); + os_free(srv->freq_list); + os_free(srv); +} + + +static void nan_de_service_deinit(struct nan_de *de, struct nan_de_service *srv, + enum nan_de_reason reason) +{ + if (!srv) + return; + if (srv->type == NAN_DE_PUBLISH && de->cb.publish_terminated) + de->cb.publish_terminated(de->cb.ctx, srv->id, reason); + if (srv->type == NAN_DE_SUBSCRIBE && de->cb.subscribe_terminated) + de->cb.subscribe_terminated(de->cb.ctx, srv->id, reason); + nan_de_service_free(srv); +} + + +static void nan_de_clear_pending(struct nan_de *de) +{ + de->listen_freq = 0; + de->tx_wait_status_freq = 0; + de->tx_wait_end_freq = 0; +} + + +void nan_de_flush(struct nan_de *de) +{ + unsigned int i; + + if (!de) + return; + + for (i = 0; i < NAN_DE_MAX_SERVICE; i++) { + nan_de_service_deinit(de, de->service[i], + NAN_DE_REASON_USER_REQUEST); + de->service[i] = NULL; + } + + de->num_service = 0; + nan_de_clear_pending(de); +} + + +static void nan_de_pause_state(struct nan_de_service *srv, const u8 *peer_addr, + u8 peer_id) +{ + wpa_printf(MSG_DEBUG, "NAN: Start pauseState"); + os_get_reltime(&srv->pause_state_end); + srv->pause_state_end.sec += 60; + if (os_reltime_initialized(&srv->end_time) && + os_reltime_before(&srv->end_time, &srv->pause_state_end)) + srv->pause_state_end = srv->end_time; + os_memcpy(srv->sel_peer_addr, peer_addr, ETH_ALEN); + srv->sel_peer_id = peer_id; +} + + +static void nan_de_unpause_state(struct nan_de_service *srv) +{ + wpa_printf(MSG_DEBUG, "NAN: Stop pauseState"); + srv->pause_state_end.sec = 0; + srv->pause_state_end.usec = 0; + os_memset(srv->sel_peer_addr, 0, ETH_ALEN); + srv->sel_peer_id = 0; +} + + +static struct wpabuf * nan_de_alloc_sdf(size_t len) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(2 + 4 + len); + if (buf) { + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC); + wpabuf_put_be32(buf, NAN_SDF_VENDOR_TYPE); + } + + return buf; +} + + +static int nan_de_tx(struct nan_de *de, unsigned int freq, + unsigned int wait_time, + const u8 *dst, const u8 *src, const u8 *bssid, + const struct wpabuf *buf) +{ + int res; + + if (!de->cb.tx) + return -1; + + res = de->cb.tx(de->cb.ctx, freq, wait_time, dst, src, bssid, buf); + if (res < 0) + return res; + + de->tx_wait_status_freq = freq; + de->tx_wait_end_freq = wait_time ? freq : 0; + + return res; +} + + +static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv, + unsigned int wait_time, + enum nan_service_control_type type, + const u8 *dst, const u8 *a3, u8 req_instance_id, + const struct wpabuf *ssi) +{ + struct wpabuf *buf; + size_t len = 0, sda_len, sdea_len; + u8 ctrl = type; + u16 sdea_ctrl = 0; + + /* Service Descriptor attribute */ + sda_len = NAN_SERVICE_ID_LEN + 1 + 1 + 1; + len += NAN_ATTR_HDR_LEN + sda_len; + + /* Service Descriptor Extension attribute */ + sdea_len = 1 + 2; +#ifdef ESP_SUPPLICANT + if (ssi) + sdea_len += 2 + wpabuf_len(ssi); +#else + if (ssi) + sdea_len += 2 + 4 + wpabuf_len(ssi); +#endif /* ESP_SUPPLICANT */ + len += NAN_ATTR_HDR_LEN + sdea_len; + + /* Element Container attribute */ + if (srv->elems) + len += NAN_ATTR_HDR_LEN + 1 + wpabuf_len(srv->elems); + + buf = nan_de_alloc_sdf(len); + if (!buf) + return; + + /* Service Descriptor attribute */ + wpabuf_put_u8(buf, NAN_ATTR_SDA); + wpabuf_put_le16(buf, sda_len); + wpabuf_put_data(buf, srv->service_id, NAN_SERVICE_ID_LEN); + wpabuf_put_u8(buf, srv->id); /* Instance ID */ + wpabuf_put_u8(buf, req_instance_id); /* Requestor Instance ID */ + wpabuf_put_u8(buf, ctrl); + + /* Service Descriptor Extension attribute */ + if (srv->type == NAN_DE_PUBLISH || ssi) { + wpabuf_put_u8(buf, NAN_ATTR_SDEA); + wpabuf_put_le16(buf, sdea_len); + wpabuf_put_u8(buf, srv->id); /* Instance ID */ + if (srv->type == NAN_DE_PUBLISH) { + if (srv->publish.fsd) + sdea_ctrl |= NAN_SDEA_CTRL_FSD_REQ; + if (srv->publish.fsd_gas) + sdea_ctrl |= NAN_SDEA_CTRL_FSD_GAS; + } + wpabuf_put_le16(buf, sdea_ctrl); + if (ssi) { +#ifdef ESP_SUPPLICANT + /* Note: In our NaN-USD implementation, the user-provided SSI value must + * include both the OUI and the Service Protocol Type. + * This differs from the upstream wpa_supplicant implementation, where + * the USD supplicant driver constructs the SSI internally using the + * Wi-Fi OUI and a separately provided srv_proto_type. + */ + wpabuf_put_le16(buf, wpabuf_len(ssi)); + wpabuf_put_buf(buf, ssi); +#else + wpabuf_put_le16(buf, 4 + wpabuf_len(ssi)); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, srv->srv_proto_type); + wpabuf_put_buf(buf, ssi); +#endif /* ESP_SUPPLICANT */ + } + } + + /* Element Container attribute */ + if (srv->elems) { + wpabuf_put_u8(buf, NAN_ATTR_ELEM_CONTAINER); + wpabuf_put_le16(buf, 1 + wpabuf_len(srv->elems)); + wpabuf_put_u8(buf, 0); /* Map ID */ + wpabuf_put_buf(buf, srv->elems); + } + + nan_de_tx(de, srv->freq, wait_time, dst, de->nmi, a3, buf); + wpabuf_free(buf); +} + + +static int nan_de_time_to_next_chan_change(struct nan_de_service *srv) +{ + struct os_reltime tmp, diff, now; + + if (os_reltime_before(&srv->next_publish_state, + &srv->next_publish_chan)) + tmp = srv->next_publish_state; + else if (srv->in_multi_chan) + tmp = srv->next_publish_chan; + else + tmp = srv->next_publish_state; + + os_get_reltime(&now); + os_reltime_sub(&tmp, &now, &diff); + return os_reltime_in_ms(&diff); +} + + +static void nan_de_set_publish_times(struct nan_de_service *srv) +{ + os_get_reltime(&srv->next_publish_state); + srv->next_publish_chan = srv->next_publish_state; + /* Swap single/multi channel state in N * 100 TU */ + os_reltime_add_ms(&srv->next_publish_state, + srv->next_publish_duration * 1024 / 1000); + + /* Swap channel in multi channel state after 150 ms */ + os_reltime_add_ms(&srv->next_publish_chan, 150); +} + + +static void nan_de_check_chan_change(struct nan_de_service *srv) +{ + if (srv->next_publish_duration) { + /* Update end times for the first operation of the publish + * iteration */ + nan_de_set_publish_times(srv); + srv->next_publish_duration = 0; + } else if (srv->in_multi_chan) { + if (!os_reltime_initialized(&srv->pause_state_end)) { + srv->multi_chan_idx++; + if (srv->freq_list[srv->multi_chan_idx] == 0) + srv->multi_chan_idx = 0; + srv->freq = srv->freq_list[srv->multi_chan_idx]; + wpa_printf(MSG_DEBUG, + "NAN: Publish multi-channel change to %u MHz", + srv->freq); + } + os_get_reltime(&srv->next_publish_chan); + os_reltime_add_ms(&srv->next_publish_chan, 150); + } +} + + +static void nan_de_tx_multicast(struct nan_de *de, struct nan_de_service *srv, + u8 req_instance_id) +{ + enum nan_service_control_type type; + unsigned int wait_time = 100; + const u8 *network_id; + const u8 *bssid; + + if (srv->type == NAN_DE_PUBLISH) { + int ms; + + type = NAN_SRV_CTRL_PUBLISH; + + nan_de_check_chan_change(srv); + ms = nan_de_time_to_next_chan_change(srv); + if (ms < 100) + ms = 100; + wait_time = ms; + } else if (srv->type == NAN_DE_SUBSCRIBE) { + type = NAN_SRV_CTRL_SUBSCRIBE; + } else { + return; + } + + if (srv->is_p2p) { + network_id = p2p_network_id; + bssid = wildcard_bssid; + } else { + network_id = nan_network_id; + bssid = nan_network_id; + } + + nan_de_tx_sdf(de, srv, wait_time, type, network_id, bssid, + req_instance_id, srv->ssi); + os_get_reltime(&srv->last_multicast); +} + + +static void nan_de_add_srv(struct nan_de *de, struct nan_de_service *srv) +{ + int ttl; + + os_get_reltime(&srv->time_started); + ttl = srv->type == NAN_DE_PUBLISH ? srv->publish.ttl : + srv->subscribe.ttl; + if (ttl) { + srv->end_time = srv->time_started; + srv->end_time.sec += ttl; + } + + de->service[srv->id - 1] = srv; + de->num_service++; +} + + +static void nan_de_del_srv(struct nan_de *de, struct nan_de_service *srv, + enum nan_de_reason reason) +{ + de->service[srv->id - 1] = NULL; + nan_de_service_deinit(de, srv, reason); + de->num_service--; + if (de->num_service == 0) + nan_de_clear_pending(de); +} + + +static bool nan_de_srv_expired(struct nan_de_service *srv, + struct os_reltime *now) +{ + if (os_reltime_initialized(&srv->end_time)) + return os_reltime_before(&srv->end_time, now); + + if (srv->type == NAN_DE_PUBLISH) { + /* Time out after one transmission (and wait for FSD) */ + if (!os_reltime_initialized(&srv->last_multicast)) + return false; + if (!srv->publish.fsd) + return true; + } + + if (srv->type == NAN_DE_SUBSCRIBE) { + /* Time out after first DiscoveryResult event (and wait for + * FSD) */ + if (!os_reltime_initialized(&srv->first_discovered)) + return false; + if (!srv->needs_fsd) + return true; + } + + return false; +} + + +static int nan_de_next_multicast(struct nan_de *de, struct nan_de_service *srv, + struct os_reltime *now) +{ + unsigned int period; + struct os_reltime next, diff; + + if (srv->type == NAN_DE_PUBLISH && !srv->publish.unsolicited) + return -1; + if (srv->type == NAN_DE_SUBSCRIBE && !srv->subscribe.active) + return -1; + + if (!os_reltime_initialized(&srv->last_multicast)) + return 0; + + if (srv->type == NAN_DE_PUBLISH && srv->publish.ttl == 0) + return -1; + + if (srv->type == NAN_DE_PUBLISH && + os_reltime_initialized(&srv->pause_state_end)) + return -1; + + period = srv->type == NAN_DE_PUBLISH ? + srv->publish.announcement_period : + srv->subscribe.query_period; + if (period == 0) + period = 100; + next = srv->last_multicast; + os_reltime_add_ms(&next, period); + + if (srv->type == NAN_DE_PUBLISH) { + if (!de->tx_wait_end_freq && srv->publish.unsolicited && + os_reltime_before(&next, now)) + return 0; + next = srv->next_publish_state; + } + + if (os_reltime_before(&next, now)) + return 0; + + os_reltime_sub(&next, now, &diff); + return os_reltime_in_ms(&diff); +} + + +static int nan_de_srv_time_to_next(struct nan_de *de, + struct nan_de_service *srv, + struct os_reltime *now) +{ + struct os_reltime diff; + int next = -1, tmp; + + if (os_reltime_initialized(&srv->end_time)) { + os_reltime_sub(&srv->end_time, now, &diff); + tmp = os_reltime_in_ms(&diff); + if (next == -1 || tmp < next) + next = tmp; + } + + if (srv->type == NAN_DE_PUBLISH && + srv->publish.fsd && + os_reltime_initialized(&srv->pause_state_end)) { + os_reltime_sub(&srv->pause_state_end, now, &diff); + tmp = os_reltime_in_ms(&diff); + if (next == -1 || tmp < next) + next = tmp; + return next; + } + + tmp = nan_de_next_multicast(de, srv, now); + if (tmp >= 0 && (next == -1 || tmp < next)) + next = tmp; + + if (srv->type == NAN_DE_PUBLISH && + os_reltime_initialized(&srv->last_multicast)) { + /* Time out after one transmission (and wait for FSD) */ + tmp = srv->publish.fsd ? 1000 : 100; + if (next == -1 || tmp < next) + next = tmp; + } + + if (srv->type == NAN_DE_SUBSCRIBE && + os_reltime_initialized(&srv->first_discovered)) { + /* Time out after first DiscoveryResult event (and wait for + * FSD) */ + tmp = srv->needs_fsd ? 1000 : 100; + if (next == -1 || tmp < next) + next = tmp; + } + + if (os_reltime_initialized(&srv->next_publish_state)) { + os_reltime_sub(&srv->next_publish_state, now, &diff); +#ifndef ESP_SUPPLICANT + if (diff.sec < 0 || (diff.sec == 0 && diff.usec < 0)) + tmp = 0; + else +#endif /* ESP_SUPPLICANT */ + tmp = os_reltime_in_ms(&diff); + if (next == -1 || tmp < next) + next = tmp; + } + + return next; +} + + +static void nan_de_start_new_publish_state(struct nan_de_service *srv, + bool force_single) +{ + unsigned int n; + + if (force_single || !srv->freq_list || srv->freq_list[0] == 0) + srv->in_multi_chan = false; + else + srv->in_multi_chan = !srv->in_multi_chan; + + /* Use hardcoded Nmin=5 and Nmax=10 and pick a random N from that range. + * Use same values for M. */ + n = 5 + os_random() % 5; + srv->next_publish_duration = n * 100; + + nan_de_set_publish_times(srv); + + if (os_reltime_initialized(&srv->pause_state_end)) + return; + + if (srv->in_multi_chan && srv->freq_list && srv->freq_list[0]) { + if (!srv->first_multi_chan) + srv->multi_chan_idx++; + if (srv->freq_list[srv->multi_chan_idx] == 0) + srv->multi_chan_idx = 0; + srv->first_multi_chan = false; + srv->freq = srv->freq_list[srv->multi_chan_idx]; + } else { + srv->freq = srv->default_freq; + } + + wpa_printf(MSG_DEBUG, + "NAN: Publish in %s channel state for %u TU; starting with %u MHz", + srv->in_multi_chan ? "multi" : "single", n * 100, srv->freq); +} + + +static void nan_de_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct nan_de *de = eloop_ctx; + unsigned int i; + int next = -1; + bool started = false; + struct os_reltime now; + + os_get_reltime(&now); + + for (i = 0; i < NAN_DE_MAX_SERVICE; i++) { + struct nan_de_service *srv = de->service[i]; + int srv_next; + + if (!srv) + continue; + + if (nan_de_srv_expired(srv, &now)) { + wpa_printf(MSG_DEBUG, "NAN: Service id %d expired", + srv->id); + nan_de_del_srv(de, srv, NAN_DE_REASON_TIMEOUT); + continue; + } + + if (os_reltime_initialized(&srv->next_publish_state) && + os_reltime_before(&srv->next_publish_state, &now)) + nan_de_start_new_publish_state(srv, false); + + if (srv->type == NAN_DE_PUBLISH && + os_reltime_initialized(&srv->pause_state_end) && + (os_reltime_before(&srv->pause_state_end, &now))) + nan_de_unpause_state(srv); + + srv_next = nan_de_srv_time_to_next(de, srv, &now); + if (srv_next >= 0 && (next == -1 || srv_next < next)) + next = srv_next; + + if (srv->type == NAN_DE_PUBLISH && + srv->publish.fsd && + os_reltime_initialized(&srv->pause_state_end) && + de->tx_wait_end_freq == 0 && + de->listen_freq == 0 && de->ext_listen_freq == 0) { + struct os_reltime diff; + int duration; + + os_reltime_sub(&srv->pause_state_end, &now, &diff); + duration = os_reltime_in_ms(&diff); + if (duration < 0) + continue; + if ((unsigned int) duration > de->max_listen) + duration = de->max_listen; + if (de->cb.listen(de->cb.ctx, srv->freq, duration) == + 0) { + wpa_printf(MSG_DEBUG, + "NAN: Publisher in pauseState - started listen on %u MHz", + srv->freq); + de->listen_freq = srv->freq; + return; + } + } + + if (srv_next == 0 && !started && !de->offload && + de->listen_freq == 0 && de->ext_listen_freq == 0 && +#ifndef ESP_SUPPLICANT + de->tx_wait_end_freq == 0 && +#endif + nan_de_next_multicast(de, srv, &now) == 0) { + started = true; + nan_de_tx_multicast(de, srv, 0); + } + + if (!started && !de->offload && de->cb.listen && + de->listen_freq == 0 && de->ext_listen_freq == 0 && + de->tx_wait_end_freq == 0 && + ((srv->type == NAN_DE_PUBLISH && + !srv->publish.unsolicited && srv->publish.solicited) || + (srv->type == NAN_DE_SUBSCRIBE && + !srv->subscribe.active))) { + int duration = 1000; + + if (srv->type == NAN_DE_PUBLISH) { + nan_de_check_chan_change(srv); + duration = nan_de_time_to_next_chan_change(srv); + if (duration < 150) + duration = 150; + } + + started = true; + if (de->cb.listen(de->cb.ctx, srv->freq, duration) == 0) + de->listen_freq = srv->freq; + } + + } + + if (next < 0) + return; + + if (next == 0) + next = 1; + + eloop_register_timeout(next / 1000, (next % 1000) * 1000, nan_de_timer, + de, NULL); +} + + +static void nan_de_run_timer(struct nan_de *de) +{ + eloop_cancel_timeout(nan_de_timer, de, NULL); + eloop_register_timeout(0, 0, nan_de_timer, de, NULL); +} + + +void nan_de_deinit(struct nan_de *de) +{ + eloop_cancel_timeout(nan_de_timer, de, NULL); + nan_de_flush(de); + os_free(de); +} + + +void nan_de_listen_started(struct nan_de *de, unsigned int freq, + unsigned int duration) +{ + if (freq != de->listen_freq) + de->ext_listen_freq = freq; +} + + +void nan_de_listen_ended(struct nan_de *de, unsigned int freq) +{ + if (freq == de->ext_listen_freq) + de->ext_listen_freq = 0; + + if (freq == de->listen_freq) { + de->listen_freq = 0; + nan_de_run_timer(de); + } +} + + +void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst) +{ + if (freq == de->tx_wait_status_freq) + de->tx_wait_status_freq = 0; +} + + +void nan_de_tx_wait_ended(struct nan_de *de) +{ + if (de->tx_wait_end_freq) + wpa_printf(MSG_DEBUG, + "NAN: TX wait for response ended (freq=%u)", + de->tx_wait_end_freq); + de->tx_wait_end_freq = 0; + nan_de_run_timer(de); +} + + +static const u8 * +nan_de_get_attr(const u8 *buf, size_t len, enum nan_attr_id id, + unsigned int skip) +{ + const u8 *pos = buf, *end = buf + len; + + while (end - pos >= NAN_ATTR_HDR_LEN) { + const u8 *attr = pos; + u8 attr_id; + u16 attr_len; + + attr_id = *pos++; + attr_len = WPA_GET_LE16(pos); + pos += 2; + if (attr_len > end - pos) { + wpa_printf(MSG_DEBUG, + "NAN: Truncated attribute %u (len %u; left %zu)", + attr_id, attr_len, end - pos); + break; + } + + if (attr_id == id) { + if (skip == 0) + return attr; + skip--; + } + + pos += attr_len; + } + + return NULL; +} + + +static void nan_de_get_sdea(const u8 *buf, size_t len, u8 instance_id, + u16 *sdea_control, + enum nan_service_protocol_type *srv_proto_type, + const u8 **ssi, size_t *ssi_len) +{ + unsigned int skip; + const u8 *sdea, *end; + u16 sdea_len; + + for (skip = 0; ; skip++) { + sdea = nan_de_get_attr(buf, len, NAN_ATTR_SDEA, skip); + if (!sdea) + break; + + sdea++; + sdea_len = WPA_GET_LE16(sdea); + sdea += 2; + if (sdea_len < 1 + 2) + continue; + end = sdea + sdea_len; + + if (instance_id != *sdea++) + continue; /* Mismatching Instance ID */ + + *sdea_control = WPA_GET_LE16(sdea); + sdea += 2; + + if (*sdea_control & NAN_SDEA_CTRL_RANGE_LIMIT) { + if (end - sdea < 4) + continue; + sdea += 4; + } + + if (*sdea_control & NAN_SDEA_CTRL_SRV_UPD_INDIC) { + if (end - sdea < 1) + continue; + sdea++; + } + + if (end - sdea >= 2) { + u16 srv_info_len; + + srv_info_len = WPA_GET_LE16(sdea); + sdea += 2; + + if (srv_info_len > end - sdea) + continue; + +#ifdef ESP_SUPPLICANT + if (srv_info_len > 0) { + *ssi = sdea; + *ssi_len = srv_info_len; + } +#else + if (srv_info_len >= 4 && + WPA_GET_BE24(sdea) == OUI_WFA) { + *srv_proto_type = sdea[3]; + *ssi = sdea + 4; + *ssi_len = srv_info_len - 4; + } +#endif /* ESP_SUPPLICANT */ + } + } +} + + +static void nan_de_rx_publish(struct nan_de *de, struct nan_de_service *srv, + const u8 *peer_addr, const u8 *a3, u8 instance_id, + u8 req_instance_id, u16 sdea_control, + enum nan_service_protocol_type srv_proto_type, + const u8 *ssi, size_t ssi_len) +{ + /* Subscribe function processing of a receive Publish message */ + if (!os_reltime_initialized(&srv->first_discovered)) { + os_get_reltime(&srv->first_discovered); + srv->needs_fsd = sdea_control & NAN_SDEA_CTRL_FSD_REQ; + nan_de_run_timer(de); + } + + if (!de->offload && srv->subscribe.active && req_instance_id == 0) { + /* Active subscriber replies with a Subscribe message if it + * received a matching unsolicited Publish message. */ + nan_de_tx_multicast(de, srv, instance_id); + } + + if (!de->offload && !srv->subscribe.active && req_instance_id == 0) { + /* Passive subscriber replies with a Follow-up message without + * Service Specific Info field if it received a matching + * unsolicited Publish message. */ + nan_de_transmit(de, srv->id, NULL, NULL, peer_addr, + instance_id); + } + + if (de->cb.discovery_result) + de->cb.discovery_result( + de->cb.ctx, srv->id, srv_proto_type, + ssi, ssi_len, instance_id, + peer_addr, + sdea_control & NAN_SDEA_CTRL_FSD_REQ, + sdea_control & NAN_SDEA_CTRL_FSD_GAS); +} + + +static bool nan_de_filter_match(struct nan_de_service *srv, + const u8 *matching_filter, + size_t matching_filter_len) +{ + const u8 *pos, *end; + + /* Since we do not currently support matching_filter_rx values for the + * local Publish function, any matching filter with at least one + * pair with length larger than zero implies a mismatch. + */ + + if (!matching_filter) + return true; + + pos = matching_filter; + end = matching_filter + matching_filter_len; + + while (pos < end) { + u8 len; + + len = *pos++; + if (len > end - pos) + break; + if (len) { + /* A non-empty Matching Filter entry: no match since + * there is no local matching_filter_rx. */ + return false; + } + } + + return true; +} + + +static void nan_de_rx_subscribe(struct nan_de *de, struct nan_de_service *srv, + const u8 *peer_addr, const u8 *a3, + u8 instance_id, + const u8 *matching_filter, + size_t matching_filter_len, + enum nan_service_protocol_type srv_proto_type, + const u8 *ssi, size_t ssi_len) +{ + struct wpabuf *buf; + size_t len = 0, sda_len, sdea_len; + u8 ctrl = 0; + u16 sdea_ctrl = 0; + const u8 *network_id; + + /* Publish function processing of a receive Subscribe message */ + + if (!nan_de_filter_match(srv, matching_filter, matching_filter_len)) + return; + + if (!srv->publish.solicited) + return; + + if (os_reltime_initialized(&srv->pause_state_end) && + (!ether_addr_equal(peer_addr, srv->sel_peer_addr) || + instance_id != srv->sel_peer_id)) { + wpa_printf(MSG_DEBUG, + "NAN: In pauseState - ignore Subscribe message from another subscriber"); + return; + } + + if (de->offload) + goto offload; + + /* Reply with a solicited Publish message */ + /* Service Descriptor attribute */ + sda_len = NAN_SERVICE_ID_LEN + 1 + 1 + 1; + len += NAN_ATTR_HDR_LEN + sda_len; + + /* Service Descriptor Extension attribute */ + sdea_len = 1 + 2; + +#ifdef ESP_SUPPLICANT + if (srv->ssi) + sdea_len += 2 + wpabuf_len(srv->ssi); +#else + if (srv->ssi) + sdea_len += 2 + 4 + wpabuf_len(srv->ssi); +#endif /* ESP_SUPPLICANT */ + len += NAN_ATTR_HDR_LEN + sdea_len; + + /* Element Container attribute */ + if (srv->elems) + len += NAN_ATTR_HDR_LEN + 1 + wpabuf_len(srv->elems); + + buf = nan_de_alloc_sdf(len); + if (!buf) + return; + + /* Service Descriptor attribute */ + wpabuf_put_u8(buf, NAN_ATTR_SDA); + wpabuf_put_le16(buf, sda_len); + wpabuf_put_data(buf, srv->service_id, NAN_SERVICE_ID_LEN); + wpabuf_put_u8(buf, srv->id); /* Instance ID */ + wpabuf_put_u8(buf, instance_id); /* Requestor Instance ID */ + ctrl |= NAN_SRV_CTRL_PUBLISH; + wpabuf_put_u8(buf, ctrl); + + /* Service Descriptor Extension attribute */ + if (srv->type == NAN_DE_PUBLISH || srv->ssi) { + wpabuf_put_u8(buf, NAN_ATTR_SDEA); + wpabuf_put_le16(buf, sdea_len); + wpabuf_put_u8(buf, srv->id); /* Instance ID */ + if (srv->type == NAN_DE_PUBLISH) { + if (srv->publish.fsd) + sdea_ctrl |= NAN_SDEA_CTRL_FSD_REQ; + if (srv->publish.fsd_gas) + sdea_ctrl |= NAN_SDEA_CTRL_FSD_GAS; + } + wpabuf_put_le16(buf, sdea_ctrl); + if (srv->ssi) { +#ifdef ESP_SUPPLICANT + /* Note: In our NaN-USD implementation, the SSI value must + * include both the OUI and the Service Protocol Type. + * This differs from the upstream wpa_supplicant implementation, where + * the USD supplicant driver constructs the SSI internally using the + * Wi-Fi OUI and a separately provided srv_proto_type. + */ + wpabuf_put_le16(buf, wpabuf_len(srv->ssi)); + wpabuf_put_buf(buf, srv->ssi); +#else + wpabuf_put_le16(buf, 4 + wpabuf_len(srv->ssi)); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, srv->srv_proto_type); + wpabuf_put_buf(buf, srv->ssi); +#endif /* ESP_SUPPLICANT */ + } + } + + /* Element Container attribute */ + if (srv->elems) { + wpabuf_put_u8(buf, NAN_ATTR_ELEM_CONTAINER); + wpabuf_put_le16(buf, 1 + wpabuf_len(srv->elems)); + wpabuf_put_u8(buf, 0); /* Map ID */ + wpabuf_put_buf(buf, srv->elems); + } + + if (srv->is_p2p) + network_id = p2p_network_id; + else + network_id = nan_network_id; + + /* Wi-Fi Aware specification v4.0 uses NAN Cluster ID as A3 for USD, + * but there is no synchronization in USD as as such, no NAN Cluster + * either. Use Wildcard BSSID instead. */ + if (srv->publish.solicited_multicast || !a3) + a3 = network_id; + else if (srv->is_p2p) + a3 = de->nmi; + + nan_de_tx(de, srv->freq, 100, + srv->publish.solicited_multicast ? network_id : peer_addr, + de->nmi, a3, buf); + wpabuf_free(buf); + + if (!srv->is_p2p) { +#ifdef ESP_SUPPLICANT + if (os_reltime_initialized(&srv->pause_state_end) && + ether_addr_equal(peer_addr, srv->sel_peer_addr) && + instance_id == srv->sel_peer_id) { + wpa_printf(MSG_DEBUG, + "NAN: In pauseState - skipping pauseStateTimeout reset for" + " Subscribe message from selected peer"); + } else +#endif /* ESP_SUPPLICANT */ + nan_de_pause_state(srv, peer_addr, instance_id); + } + +offload: + if (!srv->publish.disable_events && de->cb.replied) + de->cb.replied(de->cb.ctx, srv->id, peer_addr, instance_id, + srv_proto_type, ssi, ssi_len); +} + + +static void nan_de_rx_follow_up(struct nan_de *de, struct nan_de_service *srv, + const u8 *peer_addr, const u8 *a3, + u8 instance_id, const u8 *ssi, size_t ssi_len) +{ + /* Follow-up function processing of a receive Follow-up message for a + * Subscribe or Publish instance */ + + if (srv->type == NAN_DE_PUBLISH && + os_reltime_initialized(&srv->pause_state_end) && + (!ether_addr_equal(peer_addr, srv->sel_peer_addr) || + instance_id != srv->sel_peer_id || + !ssi)) { + wpa_printf(MSG_DEBUG, + "NAN: In pauseState - ignore Follow-up message from another subscriber or without ssi"); + return; + } + + if (srv->type == NAN_DE_PUBLISH && !ssi) + nan_de_pause_state(srv, peer_addr, instance_id); + + os_memcpy(srv->a3, a3, ETH_ALEN); + srv->a3_set = true; + + if (de->cb.receive) + de->cb.receive(de->cb.ctx, srv->id, instance_id, ssi, ssi_len, + peer_addr); +} + + +static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, const u8 *a3, + unsigned int freq, const u8 *buf, size_t len, + const u8 *sda, size_t sda_len) +{ + const u8 *service_id; + u8 instance_id, req_instance_id, ctrl; + u16 sdea_control = 0; + unsigned int i; + enum nan_service_control_type type = 0; + enum nan_service_protocol_type srv_proto_type = 0; + const u8 *ssi = NULL; + size_t ssi_len = 0; + bool first = true; + const u8 *end; + const u8 *matching_filter = NULL; + size_t matching_filter_len = 0; + + if (sda_len < NAN_SERVICE_ID_LEN + 1 + 1 + 1) + return; + end = sda + sda_len; + + service_id = sda; + sda += NAN_SERVICE_ID_LEN; + instance_id = *sda++; + req_instance_id = *sda++; + ctrl = *sda; + type = ctrl & NAN_SRV_CTRL_TYPE_MASK; + wpa_printf(MSG_DEBUG, + "NAN: SDA - Service ID %02x%02x%02x%02x%02x%02x Instance ID %u Requestor Instance ID %u Service Control 0x%x (Service Control Type %u)", + MAC2STR(service_id), instance_id, req_instance_id, + ctrl, type); + if (type != NAN_SRV_CTRL_PUBLISH && + type != NAN_SRV_CTRL_SUBSCRIBE && + type != NAN_SRV_CTRL_FOLLOW_UP) { + wpa_printf(MSG_DEBUG, + "NAN: Discard SDF with unknown Service Control Type %u", + type); + return; + } + + if (ctrl & NAN_SRV_CTRL_BINDING_BITMAP) { + if (end - sda < 2) + return; + sda += 2; + } + + if (ctrl & NAN_SRV_CTRL_MATCHING_FILTER) { + u8 flen; + + if (end - sda < 1) + return; + flen = *sda++; + if (end - sda < flen) + return; + matching_filter = sda; + matching_filter_len = flen; + sda += flen; + } + + if (ctrl & NAN_SRV_CTRL_RESP_FILTER) { + u8 flen; + + if (end - sda < 1) + return; + flen = *sda++; + if (end - sda < flen) + return; + sda += flen; + } + + if (ctrl & NAN_SRV_CTRL_SRV_INFO) { + u8 flen; + + if (end - sda < 1) + return; + flen = *sda++; + if (end - sda < flen) + return; +#ifdef ESP_SUPPLICANT + if (flen > 0) { + ssi = sda; + ssi_len = flen; + wpa_hexdump(MSG_MSGDUMP, "NAN: ssi", ssi, ssi_len); + } +#else + if (flen >= 4 && WPA_GET_BE24(sda) == OUI_WFA) { + srv_proto_type = sda[3]; + ssi = sda + 4; + ssi_len = flen - 4; + wpa_printf(MSG_DEBUG, "NAN: Service Protocol Type %d", + srv_proto_type); + wpa_hexdump(MSG_MSGDUMP, "NAN: ssi", ssi, ssi_len); + } +#endif /* ESP_SUPPLICANT */ + sda += flen; + } + + for (i = 0; i < NAN_DE_MAX_SERVICE; i++) { + struct nan_de_service *srv = de->service[i]; + + if (!srv) + continue; + if (os_memcmp(srv->service_id, service_id, + NAN_SERVICE_ID_LEN) != 0) + continue; + if (type == NAN_SRV_CTRL_PUBLISH) { + if (srv->type == NAN_DE_PUBLISH) + continue; + if (req_instance_id && srv->id != req_instance_id) + continue; + } + if (type == NAN_SRV_CTRL_SUBSCRIBE && + srv->type == NAN_DE_SUBSCRIBE) + continue; + wpa_printf(MSG_DEBUG, "NAN: Received SDF matches service ID %u", + i + 1); + + if (first) { + first = false; + nan_de_get_sdea(buf, len, instance_id, &sdea_control, + &srv_proto_type, &ssi, &ssi_len); + + if (ssi) { + wpa_printf(MSG_DEBUG, + "NAN: Service Protocol Type %d", + srv_proto_type); + wpa_hexdump(MSG_MSGDUMP, "NAN: ssi", + ssi, ssi_len); + } + } + + switch (type) { + case NAN_SRV_CTRL_PUBLISH: + nan_de_rx_publish(de, srv, peer_addr, a3, instance_id, + req_instance_id, + sdea_control, srv_proto_type, + ssi, ssi_len); + break; + case NAN_SRV_CTRL_SUBSCRIBE: + nan_de_rx_subscribe(de, srv, peer_addr, a3, instance_id, + matching_filter, + matching_filter_len, + srv_proto_type, + ssi, ssi_len); + break; + case NAN_SRV_CTRL_FOLLOW_UP: + nan_de_rx_follow_up(de, srv, peer_addr, a3, instance_id, + ssi, ssi_len); + break; + } + } +} + + +void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, const u8 *a3, + unsigned int freq, const u8 *buf, size_t len) +{ + const u8 *sda; + u16 sda_len; + unsigned int skip; + + if (!de->num_service) + return; + + wpa_printf(MSG_DEBUG, "NAN: RX SDF from " MACSTR " freq=%u len=%zu", + MAC2STR(peer_addr), freq, len); + + wpa_hexdump(MSG_MSGDUMP, "NAN: SDF payload", buf, len); + + for (skip = 0; ; skip++) { + sda = nan_de_get_attr(buf, len, NAN_ATTR_SDA, skip); + if (!sda) + break; + + sda++; + sda_len = WPA_GET_LE16(sda); + sda += 2; + nan_de_rx_sda(de, peer_addr, a3, freq, buf, len, sda, sda_len); + } +} + + +static int nan_de_get_handle(struct nan_de *de) +{ + int i = de->next_handle; + + if (de->num_service >= NAN_DE_MAX_SERVICE) + goto fail; + + do { + if (!de->service[i]) { + de->next_handle = (i + 1) % NAN_DE_MAX_SERVICE; + return i + 1; + } + i = (i + 1) % NAN_DE_MAX_SERVICE; + } while (i != de->next_handle); + +fail: + wpa_printf(MSG_DEBUG, "NAN: No more room for a new service"); + return -1; +} + + +static int nan_de_derive_service_id(struct nan_de_service *srv) +{ + u8 hash[SHA256_MAC_LEN]; + char *name, *pos; + int ret; + const u8 *addr[1]; + size_t len[1]; + + name = os_strdup(srv->service_name); + if (!name) + return -1; + pos = name; + while (*pos) { + *pos = tolower(*pos); + pos++; + } + + addr[0] = (u8 *) name; + len[0] = os_strlen(name); + ret = sha256_vector(1, addr, len, hash); + os_free(name); + if (ret == 0) + os_memcpy(srv->service_id, hash, NAN_SERVICE_ID_LEN); + + return ret; +} + + +const u8 * nan_de_get_service_id(struct nan_de *de, int id) +{ + struct nan_de_service *srv; + + if (id < 1 || id > NAN_DE_MAX_SERVICE) + return NULL; + srv = de->service[id - 1]; + if (!srv) + return NULL; + return srv->service_id; +} + + +int nan_de_publish(struct nan_de *de, const char *service_name, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, const struct wpabuf *elems, + struct nan_publish_params *params, bool p2p) +{ + int publish_id; + struct nan_de_service *srv; + + if (!service_name) { + wpa_printf(MSG_DEBUG, "NAN: Publish() - no service_name"); + return -1; + } + + if (!params->unsolicited && !params->solicited) { + wpa_printf(MSG_INFO, + "NAN: Publish() - both unsolicited and solicited disabled is invalid"); + return -1; + } + + publish_id = nan_de_get_handle(de); + if (publish_id < 1) + return -1; + + srv = os_zalloc(sizeof(*srv)); + if (!srv) + return -1; + srv->type = NAN_DE_PUBLISH; + srv->freq = srv->default_freq = params->freq; + srv->service_name = os_strdup(service_name); + if (!srv->service_name) + goto fail; + if (nan_de_derive_service_id(srv) < 0) + goto fail; + os_memcpy(&srv->publish, params, sizeof(*params)); + + if (params->freq_list) { + size_t len; + + len = (int_array_len(params->freq_list) + 1) * sizeof(int); + srv->freq_list = os_memdup(params->freq_list, len); + if (!srv->freq_list) + goto fail; + } + srv->publish.freq_list = NULL; + + srv->srv_proto_type = srv_proto_type; + if (ssi) { + srv->ssi = wpabuf_dup(ssi); + if (!srv->ssi) + goto fail; + } + if (elems) { + srv->elems = wpabuf_dup(elems); + if (!srv->elems) + goto fail; + } + + /* Prepare for single and multi-channel states; starting with + * single channel */ + srv->first_multi_chan = true; + nan_de_start_new_publish_state(srv, true); + + wpa_printf(MSG_DEBUG, "NAN: Assigned new publish handle %d for %s", + publish_id, service_name); + srv->id = publish_id; + srv->is_p2p = p2p; + nan_de_add_srv(de, srv); + nan_de_run_timer(de); + return publish_id; +fail: + nan_de_service_free(srv); + return -1; +} + + +void nan_de_cancel_publish(struct nan_de *de, int publish_id) +{ + struct nan_de_service *srv; + + wpa_printf(MSG_DEBUG, "NAN: CancelPublish(publish_id=%d)", publish_id); + + if (publish_id < 1 || publish_id > NAN_DE_MAX_SERVICE) + return; + srv = de->service[publish_id - 1]; + if (!srv || srv->type != NAN_DE_PUBLISH) + return; + nan_de_del_srv(de, srv, NAN_DE_REASON_USER_REQUEST); +} + + +int nan_de_update_publish(struct nan_de *de, int publish_id, + const struct wpabuf *ssi) +{ + struct nan_de_service *srv; + + wpa_printf(MSG_DEBUG, "NAN: UpdatePublish(publish_id=%d)", publish_id); + + if (publish_id < 1 || publish_id > NAN_DE_MAX_SERVICE) + return -1; + srv = de->service[publish_id - 1]; + if (!srv || srv->type != NAN_DE_PUBLISH) + return -1; + + wpabuf_free(srv->ssi); + srv->ssi = NULL; + if (!ssi) + return 0; + srv->ssi = wpabuf_dup(ssi); + if (!srv->ssi) + return -1; + return 0; +} + + +int nan_de_subscribe(struct nan_de *de, const char *service_name, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, const struct wpabuf *elems, + struct nan_subscribe_params *params, bool p2p) +{ + int subscribe_id; + struct nan_de_service *srv; + + if (!service_name) { + wpa_printf(MSG_DEBUG, "NAN: Subscribe() - no service_name"); + return -1; + } + + subscribe_id = nan_de_get_handle(de); + if (subscribe_id < 1) + return -1; + + srv = os_zalloc(sizeof(*srv)); + if (!srv) + return -1; + srv->type = NAN_DE_SUBSCRIBE; + srv->freq = params->freq; + srv->service_name = os_strdup(service_name); + if (!srv->service_name) + goto fail; + if (nan_de_derive_service_id(srv) < 0) + goto fail; + os_memcpy(&srv->subscribe, params, sizeof(*params)); + + if (params->freq_list) { + size_t len; + + len = (int_array_len(params->freq_list) + 1) * sizeof(int); + srv->freq_list = os_memdup(params->freq_list, len); + if (!srv->freq_list) + goto fail; + } + srv->subscribe.freq_list = NULL; + + srv->srv_proto_type = srv_proto_type; + if (ssi) { + srv->ssi = wpabuf_dup(ssi); + if (!srv->ssi) + goto fail; + } + if (elems) { + srv->elems = wpabuf_dup(elems); + if (!srv->elems) + goto fail; + } + + wpa_printf(MSG_DEBUG, "NAN: Assigned new subscribe handle %d for %s", + subscribe_id, service_name); + srv->id = subscribe_id; + srv->is_p2p = p2p; + nan_de_add_srv(de, srv); + nan_de_run_timer(de); + return subscribe_id; +fail: + nan_de_service_free(srv); + return -1; +} + + +void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id) +{ + struct nan_de_service *srv; + + if (subscribe_id < 1 || subscribe_id > NAN_DE_MAX_SERVICE) + return; + srv = de->service[subscribe_id - 1]; + if (!srv || srv->type != NAN_DE_SUBSCRIBE) + return; + nan_de_del_srv(de, srv, NAN_DE_REASON_USER_REQUEST); +} + + +int nan_de_transmit(struct nan_de *de, int handle, + const struct wpabuf *ssi, const struct wpabuf *elems, + const u8 *peer_addr, u8 req_instance_id) +{ + struct nan_de_service *srv; + const u8 *a3; + const u8 *network_id; + + if (handle < 1 || handle > NAN_DE_MAX_SERVICE) + return -1; + + srv = de->service[handle - 1]; + if (!srv) + return -1; + + if (srv->is_p2p) + network_id = p2p_network_id; + else + network_id = nan_network_id; + + if (srv->a3_set) + a3 = srv->a3; + else + a3 = network_id; + nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_FOLLOW_UP, + peer_addr, a3, req_instance_id, ssi); + + return 0; +} + +#ifdef ESP_SUPPLICANT +void nan_de_cancel_service(struct nan_de *de, int service_id) +{ + struct nan_de_service *srv; + + wpa_printf(MSG_DEBUG, "NAN: CancelService(service_id=%d)", service_id); + + if (service_id < 1 || service_id > NAN_DE_MAX_SERVICE) + return; + + srv = de->service[service_id - 1]; + if (!srv) + return; + nan_de_del_srv(de, srv, NAN_DE_REASON_USER_REQUEST); +} +#endif diff --git a/components/wpa_supplicant/src/common/nan_de.h b/components/wpa_supplicant/src/common/nan_de.h new file mode 100644 index 00000000000..fcbf5c9fc6b --- /dev/null +++ b/components/wpa_supplicant/src/common/nan_de.h @@ -0,0 +1,160 @@ +/* + * NAN Discovery Engine + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NAN_DE_H +#define NAN_DE_H + +#include "nan.h" +#include "utils/common.h" + +/* Maximum number of active local publish and subscribe instances */ +#ifndef NAN_DE_MAX_SERVICE +#ifndef ESP_SUPPLICANT +#define NAN_DE_MAX_SERVICE 20 +#else +#define NAN_DE_MAX_SERVICE 2 +#endif +#endif /* NAN_DE_MAX_SERVICE */ + +struct nan_de; + +enum nan_de_reason { + NAN_DE_REASON_TIMEOUT, + NAN_DE_REASON_USER_REQUEST, + NAN_DE_REASON_FAILURE, +}; + +struct nan_callbacks { + void *ctx; + + int (*tx)(void *ctx, unsigned int freq, unsigned int wait_time, + const u8 *dst, const u8 *src, const u8 *bssid, + const struct wpabuf *buf); + int (*listen)(void *ctx, unsigned int freq, unsigned int duration); + + /* NAN DE Events */ + void (*discovery_result)(void *ctx, int subscribe_id, + enum nan_service_protocol_type srv_proto_type, + const u8 *ssi, size_t ssi_len, + int peer_publish_id, + const u8 *peer_addr, bool fsd, bool fsd_gas); + + void (*replied)(void *ctx, int publish_id, const u8 *peer_addr, + int peer_subscribe_id, + enum nan_service_protocol_type srv_proto_type, + const u8 *ssi, size_t ssi_len); + + void (*publish_terminated)(void *ctx, int publish_id, + enum nan_de_reason reason); + + void (*subscribe_terminated)(void *ctx, int subscribe_id, + enum nan_de_reason reason); + + void (*receive)(void *ctx, int id, int peer_instance_id, + const u8 *ssi, size_t ssi_len, + const u8 *peer_addr); +}; + +bool nan_de_is_nan_network_id(const u8 *addr); +bool nan_de_is_p2p_network_id(const u8 *addr); +struct nan_de * nan_de_init(const u8 *nmi, bool offload, bool ap, + unsigned int max_listen, + const struct nan_callbacks *cb); +void nan_de_flush(struct nan_de *de); +void nan_de_deinit(struct nan_de *de); + +void nan_de_listen_started(struct nan_de *de, unsigned int freq, + unsigned int duration); +void nan_de_listen_ended(struct nan_de *de, unsigned int freq); +void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst); +void nan_de_tx_wait_ended(struct nan_de *de); + +void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, const u8 *a3, + unsigned int freq, const u8 *buf, size_t len); +const u8 * nan_de_get_service_id(struct nan_de *de, int id); + +struct nan_publish_params { + /* configuration_parameters */ + + /* Publish type */ + bool unsolicited; + bool solicited; + + /* Solicited transmission type */ + bool solicited_multicast; + + /* Time to live (in seconds); 0 = one TX only */ + unsigned int ttl; + + /* Event conditions */ + bool disable_events; + + /* Further Service Discovery flag */ + bool fsd; + + /* Further Service Discovery function */ + bool fsd_gas; + + /* Default frequency (defaultPublishChannel) */ + unsigned int freq; + + /* Multi-channel frequencies (publishChannelList) */ + const int *freq_list; + + /* Announcement period in ms; 0 = use default */ + unsigned int announcement_period; +}; + +/* Returns -1 on failure or >0 publish_id */ +int nan_de_publish(struct nan_de *de, const char *service_name, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, const struct wpabuf *elems, + struct nan_publish_params *params, bool p2p); + +void nan_de_cancel_publish(struct nan_de *de, int publish_id); + +int nan_de_update_publish(struct nan_de *de, int publish_id, + const struct wpabuf *ssi); + +struct nan_subscribe_params { + /* configuration_parameters */ + + /* Subscribe type */ + bool active; + + /* Time to live (in seconds); 0 = until first result */ + unsigned int ttl; + + /* Selected frequency */ + unsigned int freq; + + /* Multi-channel frequencies (publishChannelList) */ + const int *freq_list; + + /* Query period in ms; 0 = use default */ + unsigned int query_period; +}; + +/* Returns -1 on failure or >0 subscribe_id */ +int nan_de_subscribe(struct nan_de *de, const char *service_name, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, const struct wpabuf *elems, + struct nan_subscribe_params *params, bool p2p); + +void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id); + +/* handle = publish_id or subscribe_id + * req_instance_id = peer publish_id or subscribe_id */ +int nan_de_transmit(struct nan_de *de, int handle, + const struct wpabuf *ssi, const struct wpabuf *elems, + const u8 *peer_addr, u8 req_instance_id); + +#ifdef ESP_SUPPLICANT +void nan_de_cancel_service(struct nan_de *de, int service_id); +#endif +#endif /* NAN_DE_H */ diff --git a/components/wpa_supplicant/src/utils/common.c b/components/wpa_supplicant/src/utils/common.c index 51acf7d6f3d..ef36edc7dca 100644 --- a/components/wpa_supplicant/src/utils/common.c +++ b/components/wpa_supplicant/src/utils/common.c @@ -543,6 +543,23 @@ int os_reltime_initialized(struct os_reltime *t) return t->sec != 0 || t->usec != 0; } +void os_reltime_add_ms(struct os_reltime *ts, int ms) +{ + ts->usec += ms * 1000; + while (ts->usec >= 1000000) { + ts->sec++; + ts->usec -= 1000000; + } + while (ts->usec < 0) { + ts->sec--; + ts->usec += 1000000; + } +} + +int os_reltime_in_ms(struct os_reltime *ts) +{ + return ts->sec * 1000 + ts->usec / 1000; +} u8 rssi_to_rcpi(int rssi) { diff --git a/components/wpa_supplicant/src/utils/common.h b/components/wpa_supplicant/src/utils/common.h index 998870e90f4..4fa4e11798a 100644 --- a/components/wpa_supplicant/src/utils/common.h +++ b/components/wpa_supplicant/src/utils/common.h @@ -398,6 +398,8 @@ int os_reltime_expired(struct os_time *now, struct os_time *ts, os_time_t timeout_secs); int os_reltime_initialized(struct os_reltime *t); +void os_reltime_add_ms(struct os_reltime *ts, int ms); +int os_reltime_in_ms(struct os_reltime *ts); #ifdef CONFIG_NATIVE_WINDOWS void wpa_unicode2ascii_inplace(TCHAR *str); @@ -435,6 +437,11 @@ static inline int is_multicast_ether_addr(const u8 *a) return a[0] & 0x01; } +static inline bool ether_addr_equal(const u8 *a, const u8 *b) +{ + return os_memcmp(a, b, ETH_ALEN) == 0; +} + #define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff" diff --git a/docs/en/api-guides/wifi.rst b/docs/en/api-guides/wifi.rst index d3e759e3e90..ae655e788e5 100644 --- a/docs/en/api-guides/wifi.rst +++ b/docs/en/api-guides/wifi.rst @@ -2094,6 +2094,14 @@ Detailed information on creating certificates and how to run wpa2_enterprise exa Refer to ESP-IDF examples :idf_file:`examples/wifi/wifi_aware/nan_publisher/README.md` and :idf_file:`examples/wifi/wifi_aware/nan_subscriber/README.md` to setup a NAN Publisher and Subscriber. +Wi-Fi Aware\ :sup:`TM` (NAN): Unsynchronized Service Discovery (USD) +-------------------------------------------------------------------- +Unsynchronized Service Discovery (USD) is a mechanism for devices to discover the services that have been made discoverable on new devices that enter the RF environment, without requiring synchronization between the devices. + +USD uses Service Info field in the SDEA of a Publish and Follow-up message to convey the service specific information. + +Refer to ESP-IDF examples :idf_file:`examples/wifi/wifi_aware/usd_publisher/README.md` and :idf_file:`examples/wifi/wifi_aware/usd_subscriber/README.md` to setup a NAN-USD Publisher and Subscriber. + Wireless Network Management ---------------------------- diff --git a/docs/en/api-reference/network/esp_nan.rst b/docs/en/api-reference/network/esp_nan.rst index 56d2afa18be..42c7cdfd118 100644 --- a/docs/en/api-reference/network/esp_nan.rst +++ b/docs/en/api-reference/network/esp_nan.rst @@ -18,6 +18,10 @@ Application Examples - :example:`wifi/wifi_aware/nan_subscriber` demonstrates how to use the NAN protocol to discover other devices that publishes the required service in the proximity, and communicate with them either by sending a message or initiating a datapath. +- :example:`wifi/wifi_aware/usd_publisher` demonstrates how to use lightweight NAN Unsynchronized Service Discovery (NAN-USD) to advertise a service to nearby devices without forming NAN clusters. It walks through bringing up USD, publishing a service, responding to subscribers with follow-up messages, and returning to the idle state once discovery completes. + +- :example:`wifi/wifi_aware/usd_subscriber` demonstrates how to use the lightweight NAN Unsynchronized Service Discovery (NAN-USD) protocol to discover services advertised by nearby devices and interact with them. It covers enabling USD discovery, subscribing to services, handling follow-up exchanges, and terminating discovery after the session ends. + API Reference ------------- diff --git a/docs/en/migration-guides/release-6.x/6.0/wifi.rst b/docs/en/migration-guides/release-6.x/6.0/wifi.rst index 129eaf2c674..bf81351511f 100644 --- a/docs/en/migration-guides/release-6.x/6.0/wifi.rst +++ b/docs/en/migration-guides/release-6.x/6.0/wifi.rst @@ -46,4 +46,18 @@ The following Wi-Fi functions have been modified. - **WPS:** The function ``esp_wifi_wps_start`` no longer accepts a ``timeout_ms`` argument. It should now be called as ``esp_wifi_wps_start(void)``. -- **NAN:** The function argument ``ndp_resp_needed`` of ``esp_wifi_nan_publish_service`` has been moved to structure ``wifi_nan_publish_cfg_t``. +- **NAN:** + - NAN API's are modified so that they can be shared between two separate NAN modes of operation called NAN Synchronization (NAN-Sync) and NAN Unsynchronized Discovery (NAN-USD) roles: + - ``esp_wifi_nan_sync_start`` and ``esp_wifi_nan_sync_stop`` replace the previous ``esp_wifi_nan_start`` / ``esp_wifi_nan_stop`` pair. + - Functions starting with prefix ``esp_wifi_nan_*`` are shared by both NAN-Sync and NAN-USD modes (for example ``esp_wifi_nan_publish_service`` and ``esp_wifi_nan_subscribe_service``). + - Use ``WIFI_NAN_SYNC_CONFIG_DEFAULT()`` instead of the former ``WIFI_NAN_CONFIG_DEFAULT()`` macro when initialising synchronized NAN configuration. + - The structure ``wifi_nan_config_t`` has been renamed to ``wifi_nan_sync_config_t``; update declarations such as ``wifi_config_t.nan`` to use the new type. + - NAN datapath APIs and associated structures are valid for NAN Synchronization (NAN-Sync) clusters only. + - Wi-Fi events ``WIFI_EVENT_NAN_STARTED`` / ``WIFI_EVENT_NAN_STOPPED`` have been renamed to ``WIFI_EVENT_NAN_SYNC_STARTED`` / ``WIFI_EVENT_NAN_SYNC_STOPPED``. + - The function argument ``ndp_resp_needed`` of ``esp_wifi_nan_publish_service`` has been moved to structure ``wifi_nan_publish_cfg_t``. + - The structure ``wifi_nan_wfa_ssi_t`` now declares its ``proto`` field as ``uint8_t`` (previously ``wifi_nan_svc_proto_t``). Update code that stores pointers to this field or relies on its size to treat it as a single octet. + - The structures ``wifi_nan_publish_cfg_t`` and ``wifi_nan_subscribe_cfg_t`` now include a ``usd_discovery_flag`` bitfield together with corresponding ``wifi_nan_usd_config_t`` members. Add designated initialisers for these fields when constructing the structures to avoid accidental field shifts. + +- **Off-Channel Operations:** + - The structure :cpp:struct:`wifi_action_tx_req_t` now includes a ``bssid`` field that is placed in Address3 of the transmitted action frame. Initialise this field explicitly; leaving it all zeros retains the previous behaviour where the Wi-Fi driver inserts the broadcast address, so frames do not carry unintended BSSID values. + - The structure :cpp:struct:`wifi_roc_req_t` now includes an ``allow_broadcast`` flag that controls whether broadcast and multicast action frames are passed to the ``rx_cb`` callback during remain-on-channel operations. Existing applications should review the default ``false`` value and enable it when discovery of broadcast peers is required. Frames whose Address3/BSSID is already broadcast continue to reach the callback even when ``allow_broadcast`` remains ``false``. diff --git a/docs/zh_CN/migration-guides/release-6.x/6.0/wifi.rst b/docs/zh_CN/migration-guides/release-6.x/6.0/wifi.rst index 797a38bb2dd..68cb03ad4c4 100644 --- a/docs/zh_CN/migration-guides/release-6.x/6.0/wifi.rst +++ b/docs/zh_CN/migration-guides/release-6.x/6.0/wifi.rst @@ -46,5 +46,18 @@ Wi-Fi - **WPS:** 函数 ``esp_wifi_wps_start`` 不再接受 ``timeout_ms`` 参数。现在应调用为 ``esp_wifi_wps_start(void)``。 -- **NAN:** 函数 ``esp_wifi_nan_publish_service`` 的参数 ``ndp_resp_needed`` 已移至结构体 ``wifi_nan_publish_cfg_t``。 +- **NAN:** + - 已调整 NAN API,使其可在两个独立的 NAN 工作模式间共享:NAN 同步(NAN-Sync)与 NAN 非同步发现(NAN-USD): + - ``esp_wifi_nan_sync_start`` 与 ``esp_wifi_nan_sync_stop`` 取代原有的 ``esp_wifi_nan_start`` / ``esp_wifi_nan_stop``。 + - 以 ``esp_wifi_nan_*`` 为前缀的函数在 NAN-Sync 与 NAN 非同步发现(NAN-USD) 模式间通用(例如 ``esp_wifi_nan_publish_service``、``esp_wifi_nan_subscribe_service``)。 + - 初始化 NAN 同步配置时,请使用 ``WIFI_NAN_SYNC_CONFIG_DEFAULT()``,原 ``WIFI_NAN_CONFIG_DEFAULT()`` 已停用。 + - 结构体 ``wifi_nan_config_t`` 已重命名为 ``wifi_nan_sync_config_t``,例如需将 ``wifi_config_t.nan`` 等声明更新为新类型。 + - NAN 数据通道相关的 API 与结构体目前仅适用于 NAN 同步(NAN-Sync)集群。 + - Wi-Fi 事件 ``WIFI_EVENT_NAN_STARTED`` / ``WIFI_EVENT_NAN_STOPPED`` 已重命名为 ``WIFI_EVENT_NAN_SYNC_STARTED`` / ``WIFI_EVENT_NAN_SYNC_STOPPED``。 + - 函数 ``esp_wifi_nan_publish_service`` 的参数 ``ndp_resp_needed`` 已移至结构体 ``wifi_nan_publish_cfg_t``。 + - 结构体 ``wifi_nan_wfa_ssi_t`` 中的 ``proto`` 字段类型现为 ``uint8_t`` (之前为 ``wifi_nan_svc_proto_t``)。如果代码存储该字段的指针或依赖其大小,请按单个八位字节处理。 + - 结构体 ``wifi_nan_publish_cfg_t`` 和 ``wifi_nan_subscribe_cfg_t`` 新增 ``usd_discovery_flag`` 位域及对应的 ``wifi_nan_usd_config_t`` 成员。构造这些结构体时,请为新增字段提供指定初始化器,以避免字段偏移。 +- **离信道操作:** + - 结构体 :cpp:struct:`wifi_action_tx_req_t` 新增 ``bssid`` 字段,该字段会写入发送动作帧的 Address3。请显式初始化该字段;保持全零时将沿用之前的行为,由 Wi-Fi 驱动填入广播地址,从而避免帧携带意外的 BSSID。 + - 结构体 :cpp:struct:`wifi_roc_req_t` 新增 ``allow_broadcast`` 标志,用于控制在驻留信道期间是否将广播/组播动作帧传递给 ``rx_cb`` 回调。现有应用应结合默认值 ``false`` 评估是否需要启用该选项,以便在需要时进行广播节点发现。若动作帧的 Address3/BSSID 本身为广播地址,即便 ``allow_broadcast`` 保持 ``false``,回调仍会收到该帧。 diff --git a/examples/wifi/.build-test-rules.yml b/examples/wifi/.build-test-rules.yml index 2d379ddcfdb..3aa9f4c9083 100644 --- a/examples/wifi/.build-test-rules.yml +++ b/examples/wifi/.build-test-rules.yml @@ -98,7 +98,7 @@ examples/wifi/softap_sta: disable: - if: (SOC_WIFI_SUPPORTED != 1) and (SOC_WIRELESS_HOST_SUPPORTED != 1) -examples/wifi/wifi_aware: +examples/wifi/wifi_aware/nan_console: disable: - if: SOC_WIFI_NAN_SUPPORT != 1 reason: targets esp32c3, esp32s3, esp32c2 and esp32c6 are not supported @@ -114,3 +114,43 @@ examples/wifi/wifi_aware: - nvs_flash depends_filepatterns: - examples/system/console/advanced/components/**/* + +examples/wifi/wifi_aware/nan_publisher: + disable: + - if: SOC_WIFI_NAN_SUPPORT != 1 + reason: targets esp32c3, esp32s3, esp32c2 and esp32c6 are not supported + depends_components: + - esp_wifi + - esp_phy + - esp_netif + - lwip + - esp_event + - esp_coex + - wpa_supplicant + - mbedtls + - nvs_flash + +examples/wifi/wifi_aware/nan_subscriber: + disable: + - if: SOC_WIFI_NAN_SUPPORT != 1 + reason: targets esp32c3, esp32s3, esp32c2 and esp32c6 are not supported + depends_components: + - esp_wifi + - esp_phy + - esp_netif + - lwip + - esp_event + - esp_coex + - wpa_supplicant + - mbedtls + - nvs_flash + +examples/wifi/wifi_aware/usd_publisher: + <<: *wifi_depends_default + disable: + - if: SOC_WIFI_SUPPORTED != 1 + +examples/wifi/wifi_aware/usd_subscriber: + <<: *wifi_depends_default + disable: + - if: SOC_WIFI_SUPPORTED != 1 diff --git a/examples/wifi/wifi_aware/nan_console/main/nan_main.c b/examples/wifi/wifi_aware/nan_console/main/nan_main.c index b32f1db2880..8c91fcf09d1 100644 --- a/examples/wifi/wifi_aware/nan_console/main/nan_main.c +++ b/examples/wifi/wifi_aware/nan_console/main/nan_main.c @@ -234,7 +234,7 @@ static int wifi_cmd_nan_disc(int argc, char **argv) } if (nan_args.init->count) { - wifi_nan_config_t nan_cfg = WIFI_NAN_CONFIG_DEFAULT(); + wifi_nan_sync_config_t nan_cfg = WIFI_NAN_SYNC_CONFIG_DEFAULT(); if (nan_args.master_pref->count) { nan_cfg.master_pref = nan_args.master_pref->ival[0]; @@ -250,7 +250,7 @@ static int wifi_cmd_nan_disc(int argc, char **argv) g_nan_netif = esp_netif_create_default_wifi_nan(); } - if ((esp_wifi_nan_start(&nan_cfg)) != ESP_OK) { + if ((esp_wifi_nan_sync_start(&nan_cfg)) != ESP_OK) { ESP_LOGI(TAG, "Failed to start NAN"); esp_netif_destroy_default_wifi(g_nan_netif); g_nan_netif = NULL; @@ -266,7 +266,7 @@ static int wifi_cmd_nan_disc(int argc, char **argv) } if (nan_args.deinit->count) { - ret = esp_wifi_nan_stop(); + ret = esp_wifi_nan_sync_stop(); if (ret != ESP_OK) { ESP_LOGI(TAG, "Failed to stop NAN"); diff --git a/examples/wifi/wifi_aware/nan_console/sdkconfig.defaults b/examples/wifi/wifi_aware/nan_console/sdkconfig.defaults index be625a880c8..3d8f4d46579 100644 --- a/examples/wifi/wifi_aware/nan_console/sdkconfig.defaults +++ b/examples/wifi/wifi_aware/nan_console/sdkconfig.defaults @@ -1 +1 @@ -CONFIG_ESP_WIFI_NAN_ENABLE=y +CONFIG_ESP_WIFI_NAN_SYNC_ENABLE=y diff --git a/examples/wifi/wifi_aware/nan_publisher/main/publisher_main.c b/examples/wifi/wifi_aware/nan_publisher/main/publisher_main.c index 7a7cad7cdc3..2ea6cd3cc4e 100644 --- a/examples/wifi/wifi_aware/nan_publisher/main/publisher_main.c +++ b/examples/wifi/wifi_aware/nan_publisher/main/publisher_main.c @@ -81,10 +81,10 @@ void wifi_nan_publish(void) &instance_any_id)); /* Start NAN Discovery */ - wifi_nan_config_t nan_cfg = WIFI_NAN_CONFIG_DEFAULT(); + wifi_nan_sync_config_t nan_cfg = WIFI_NAN_SYNC_CONFIG_DEFAULT(); esp_netif_create_default_wifi_nan(); - esp_wifi_nan_start(&nan_cfg); + esp_wifi_nan_sync_start(&nan_cfg); ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, WIFI_EVENT_NDP_INDICATION, diff --git a/examples/wifi/wifi_aware/nan_publisher/sdkconfig.defaults b/examples/wifi/wifi_aware/nan_publisher/sdkconfig.defaults index be625a880c8..3d8f4d46579 100644 --- a/examples/wifi/wifi_aware/nan_publisher/sdkconfig.defaults +++ b/examples/wifi/wifi_aware/nan_publisher/sdkconfig.defaults @@ -1 +1 @@ -CONFIG_ESP_WIFI_NAN_ENABLE=y +CONFIG_ESP_WIFI_NAN_SYNC_ENABLE=y diff --git a/examples/wifi/wifi_aware/nan_subscriber/main/subscriber_main.c b/examples/wifi/wifi_aware/nan_subscriber/main/subscriber_main.c index 44aa622301d..88fbca9807d 100644 --- a/examples/wifi/wifi_aware/nan_subscriber/main/subscriber_main.c +++ b/examples/wifi/wifi_aware/nan_subscriber/main/subscriber_main.c @@ -177,10 +177,10 @@ void wifi_nan_subscribe(void) #endif /* Start NAN Discovery */ - wifi_nan_config_t nan_cfg = WIFI_NAN_CONFIG_DEFAULT(); + wifi_nan_sync_config_t nan_cfg = WIFI_NAN_SYNC_CONFIG_DEFAULT(); esp_netif_t *nan_netif = esp_netif_create_default_wifi_nan(); (void) nan_netif; - esp_wifi_nan_start(&nan_cfg); + esp_wifi_nan_sync_start(&nan_cfg); /* Subscribe a service */ uint8_t sub_id; diff --git a/examples/wifi/wifi_aware/nan_subscriber/sdkconfig.defaults b/examples/wifi/wifi_aware/nan_subscriber/sdkconfig.defaults index be625a880c8..3d8f4d46579 100644 --- a/examples/wifi/wifi_aware/nan_subscriber/sdkconfig.defaults +++ b/examples/wifi/wifi_aware/nan_subscriber/sdkconfig.defaults @@ -1 +1 @@ -CONFIG_ESP_WIFI_NAN_ENABLE=y +CONFIG_ESP_WIFI_NAN_SYNC_ENABLE=y diff --git a/examples/wifi/wifi_aware/usd_publisher/CMakeLists.txt b/examples/wifi/wifi_aware/usd_publisher/CMakeLists.txt new file mode 100644 index 00000000000..95c1103419e --- /dev/null +++ b/examples/wifi/wifi_aware/usd_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(usd_publisher) diff --git a/examples/wifi/wifi_aware/usd_publisher/README.md b/examples/wifi/wifi_aware/usd_publisher/README.md new file mode 100644 index 00000000000..1c350929488 --- /dev/null +++ b/examples/wifi/wifi_aware/usd_publisher/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | diff --git a/examples/wifi/wifi_aware/usd_publisher/main/CMakeLists.txt b/examples/wifi/wifi_aware/usd_publisher/main/CMakeLists.txt new file mode 100644 index 00000000000..19b52737d14 --- /dev/null +++ b/examples/wifi/wifi_aware/usd_publisher/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "usd_publisher_example_main.c" + INCLUDE_DIRS ".") diff --git a/examples/wifi/wifi_aware/usd_publisher/main/Kconfig.projbuild b/examples/wifi/wifi_aware/usd_publisher/main/Kconfig.projbuild new file mode 100644 index 00000000000..d79aba9e76a --- /dev/null +++ b/examples/wifi/wifi_aware/usd_publisher/main/Kconfig.projbuild @@ -0,0 +1,20 @@ +menu "Example Configuration" + + config ESP_WIFI_USD_PUB_SVC_NAME + string "USD publish service name" + default "ESP-USD-TEST" + + config ESP_WIFI_USD_PUB_TTL + int "Time to live (in seconds)" + range 0 1000 + default 100 + help + Time to live of the Publish instance, in seconds. If TTL = 0, only one publish message is transmitted. + + config ESP_WIFI_USD_SVC_SPECIFIC_INFO + string "Service specific info to be transmitted in a followup message" + default "Welcome" + help + SSI to be transmitted in a followup frame + +endmenu diff --git a/examples/wifi/wifi_aware/usd_publisher/main/usd_publisher_example_main.c b/examples/wifi/wifi_aware/usd_publisher/main/usd_publisher_example_main.c new file mode 100644 index 00000000000..09dec2dbded --- /dev/null +++ b/examples/wifi/wifi_aware/usd_publisher/main/usd_publisher_example_main.c @@ -0,0 +1,129 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#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_check.h" +#include "esp_log.h" +#include "nvs_flash.h" + +#define MAC_ADDR_LEN 6 + +#define EXAMPLE_USD_SVC_NAME CONFIG_ESP_WIFI_USD_PUB_SVC_NAME +#define EXAMPLE_USD_TTL CONFIG_ESP_WIFI_USD_PUB_TTL +#define EXAMPLE_USD_SVC_INFO CONFIG_ESP_WIFI_USD_SVC_SPECIFIC_INFO + +static EventGroupHandle_t s_nan_event_group; +static int NAN_RECEIVE = BIT0; +const char *TAG = "usd_publisher"; +uint8_t g_peer_inst_id; +uint8_t g_peer_mac[MAC_ADDR_LEN]; +uint8_t g_publish_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; + if (evt == NULL) { + return; + } + g_peer_inst_id = evt->peer_inst_id; + memcpy(g_peer_mac, evt->peer_if_mac, MAC_ADDR_LEN); + ESP_LOGI(TAG, "Received Follow-up message from peer with ID %d and MAC "MACSTR"", + evt->peer_inst_id, MAC2STR(evt->peer_if_mac)); + if (evt->ssi_len) { + ESP_LOG_BUFFER_HEXDUMP(TAG, evt->ssi, evt->ssi_len, ESP_LOG_INFO); + xEventGroupSetBits(s_nan_event_group, NAN_RECEIVE); + } else { + ESP_LOGI(TAG, "SSI - [NULL]"); + } +} + +void wifi_usd_publish(void) +{ + s_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)); + + ESP_RETURN_VOID_ON_ERROR(esp_wifi_start(), TAG, "NAN-USD failed to start Wi-Fi"); + + /* Start USD-NAN Discovery */ + ESP_RETURN_VOID_ON_ERROR(esp_wifi_nan_usd_start(), TAG, "NAN-USD initialization failed"); + + wifi_nan_publish_cfg_t publish_cfg = { + .service_name = EXAMPLE_USD_SVC_NAME, + .ttl = EXAMPLE_USD_TTL, + .ssi = NULL, + .ssi_len = 0, + .usd_discovery_flag = 1, + .usd_publish_config = esp_wifi_usd_get_default_publish_cfg(), + }; + + g_publish_id = esp_wifi_nan_publish_service(&publish_cfg); + + + if (g_publish_id == 0) { + ESP_LOGE(TAG, "Publishing to %s failed", publish_cfg.service_name); + return; + } + + EventBits_t bits = xEventGroupWaitBits(s_nan_event_group, NAN_RECEIVE, pdFALSE, pdFALSE, pdMS_TO_TICKS(EXAMPLE_USD_TTL*1000)); + if (bits & NAN_RECEIVE) { + xEventGroupClearBits(s_nan_event_group, NAN_RECEIVE); + wifi_nan_followup_params_t fup_params = { + .inst_id = g_publish_id, + .peer_inst_id = g_peer_inst_id, + .ssi = (uint8_t *)EXAMPLE_USD_SVC_INFO, + .ssi_len = strlen(EXAMPLE_USD_SVC_INFO), + }; + memcpy(fup_params.peer_mac, g_peer_mac, MAC_ADDR_LEN); + + ESP_LOGI(TAG, "Sending message to the peer with ID %d", g_peer_inst_id); + if (fup_params.ssi_len) { + ESP_LOG_BUFFER_HEXDUMP(TAG, fup_params.ssi, fup_params.ssi_len, ESP_LOG_INFO); + } else { + ESP_LOGI(TAG, "SSI - [NULL]"); + } + ESP_RETURN_VOID_ON_ERROR(esp_wifi_nan_send_message(&fup_params), TAG, "Sending message to the peer with ID %d failed!", g_peer_inst_id); + } + + esp_wifi_nan_cancel_service(g_publish_id); + esp_wifi_nan_usd_stop(); +} + +void initialise_wifi(void) +{ + 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)); +} + +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(); + wifi_usd_publish(); + return; +} diff --git a/examples/wifi/wifi_aware/usd_publisher/sdkconfig.defaults b/examples/wifi/wifi_aware/usd_publisher/sdkconfig.defaults new file mode 100644 index 00000000000..d0ffed238ef --- /dev/null +++ b/examples/wifi/wifi_aware/usd_publisher/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_ESP_WIFI_NAN_USD_ENABLE=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/examples/wifi/wifi_aware/usd_subscriber/CMakeLists.txt b/examples/wifi/wifi_aware/usd_subscriber/CMakeLists.txt new file mode 100644 index 00000000000..75d5bab606e --- /dev/null +++ b/examples/wifi/wifi_aware/usd_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(usd_subscriber) diff --git a/examples/wifi/wifi_aware/usd_subscriber/README.md b/examples/wifi/wifi_aware/usd_subscriber/README.md new file mode 100644 index 00000000000..1c350929488 --- /dev/null +++ b/examples/wifi/wifi_aware/usd_subscriber/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | diff --git a/examples/wifi/wifi_aware/usd_subscriber/main/CMakeLists.txt b/examples/wifi/wifi_aware/usd_subscriber/main/CMakeLists.txt new file mode 100644 index 00000000000..2c34987a324 --- /dev/null +++ b/examples/wifi/wifi_aware/usd_subscriber/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "usd_subscriber_example_main.c" + INCLUDE_DIRS ".") diff --git a/examples/wifi/wifi_aware/usd_subscriber/main/Kconfig.projbuild b/examples/wifi/wifi_aware/usd_subscriber/main/Kconfig.projbuild new file mode 100644 index 00000000000..7b00aa4015f --- /dev/null +++ b/examples/wifi/wifi_aware/usd_subscriber/main/Kconfig.projbuild @@ -0,0 +1,28 @@ +menu "Example Configuration" + + config ESP_WIFI_USD_SUB_SVC_NAME + string "USD subscribe service name" + default "ESP-USD-TEST" + + config ESP_WIFI_USD_SUB_TTL + int "Time to live (in seconds)" + range 0 1000 + default 100 + help + Time to live of the Subscribe instance, in seconds. If TTL = 0, + the subscriber listens until the first service match is reported. + + config ESP_WIFI_USD_DEFAULT_CHAN + int "Default Subscribe channel" + range 1 13 + default 6 + help + Default channel of subscriber + + config ESP_WIFI_USD_SVC_SPECIFIC_INFO + string "Service specific info to be transmitted in a followup message" + default "Hello" + help + SSI to be transmitted in a followup frame + +endmenu diff --git a/examples/wifi/wifi_aware/usd_subscriber/main/usd_subscriber_example_main.c b/examples/wifi/wifi_aware/usd_subscriber/main/usd_subscriber_example_main.c new file mode 100644 index 00000000000..32dc7132773 --- /dev/null +++ b/examples/wifi/wifi_aware/usd_subscriber/main/usd_subscriber_example_main.c @@ -0,0 +1,153 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#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 "esp_check.h" +#include "nvs_flash.h" + +#define MAC_ADDR_LEN 6 + +#define EXAMPLE_USD_SVC_NAME CONFIG_ESP_WIFI_USD_SUB_SVC_NAME +#define EXAMPLE_USD_TTL CONFIG_ESP_WIFI_USD_SUB_TTL +#define EXAMPLE_USD_DEFAULT_SUB_CHANNEL CONFIG_ESP_WIFI_USD_DEFAULT_CHAN +#define EXAMPLE_USD_SVC_INFO CONFIG_ESP_WIFI_USD_SVC_SPECIFIC_INFO + +static EventGroupHandle_t s_nan_event_group; +static int NAN_RECEIVE = BIT0; +static int NAN_SRV_MATCH = BIT1; +const char *TAG = "usd_subscriber"; +uint8_t g_peer_inst_id; +uint8_t g_peer_mac[MAC_ADDR_LEN]; +uint8_t g_subscribe_id; + +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; + if (evt == NULL) { + return; + } + g_peer_inst_id = evt->publish_id; + memcpy(g_peer_mac, evt->pub_if_mac, MAC_ADDR_LEN); + ESP_LOGI(TAG, "Service matched with peer_id %d peer mac "MACSTR"", evt->publish_id, MAC2STR(evt->pub_if_mac)); + if (evt->ssi_len) { + ESP_LOGI(TAG, "SSI -"); + ESP_LOG_BUFFER_HEXDUMP(TAG, evt->ssi, + evt->ssi_len, ESP_LOG_INFO); + } else { + ESP_LOGI(TAG, "SSI - [NULL]"); + } + xEventGroupSetBits(s_nan_event_group, NAN_SRV_MATCH); +} + +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; + if (evt == NULL) { + return; + } + + ESP_LOGI(TAG, "Received Follow-up message from peer with ID %d and MAC "MACSTR"", + evt->peer_inst_id, MAC2STR(evt->peer_if_mac)); + if (evt->ssi_len) { + ESP_LOG_BUFFER_HEXDUMP(TAG, evt->ssi, evt->ssi_len, ESP_LOG_INFO); + } else { + ESP_LOGI(TAG, "SSI - [NULL]"); + } + xEventGroupSetBits(s_nan_event_group, NAN_RECEIVE); + esp_wifi_nan_cancel_service(g_subscribe_id); +} + +void wifi_usd_subscribe(void) +{ + s_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)); + + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + WIFI_EVENT_NAN_RECEIVE, + &nan_receive_event_handler, + NULL, + &instance_any_id)); + + ESP_RETURN_VOID_ON_ERROR(esp_wifi_start(), TAG, "NAN-USD failed to start Wi-Fi"); + + /* Start NAN-USD Discovery */ + ESP_RETURN_VOID_ON_ERROR(esp_wifi_nan_usd_start(), TAG, "NAN-USD initialization failed"); + + wifi_nan_subscribe_cfg_t subscribe_cfg = { + .service_name = EXAMPLE_USD_SVC_NAME, + .ttl = EXAMPLE_USD_TTL, + .ssi = NULL, + .ssi_len = 0, + .usd_discovery_flag = 1, + .usd_subscribe_config = esp_wifi_usd_get_default_subscribe_cfg(), + }; + subscribe_cfg.usd_subscribe_config.usd_default_channel = EXAMPLE_USD_DEFAULT_SUB_CHANNEL; + + g_subscribe_id = esp_wifi_nan_subscribe_service(&subscribe_cfg); + if (g_subscribe_id == 0) { + ESP_LOGE(TAG, "Subscribing to %s failed", subscribe_cfg.service_name); + return; + } + + EventBits_t bits = xEventGroupWaitBits(s_nan_event_group, NAN_SRV_MATCH, pdTRUE, pdFALSE, pdMS_TO_TICKS(EXAMPLE_USD_TTL*1000)); + if (bits & NAN_SRV_MATCH) { + wifi_nan_followup_params_t fup_params = { + .inst_id = g_subscribe_id, + .peer_inst_id = g_peer_inst_id, + .ssi = (uint8_t *)EXAMPLE_USD_SVC_INFO, + .ssi_len = strlen(EXAMPLE_USD_SVC_INFO), + }; + memcpy(fup_params.peer_mac, g_peer_mac, MAC_ADDR_LEN); + + ESP_LOGI(TAG, "Sending message to the peer with ID %d", g_peer_inst_id); + if (fup_params.ssi_len) { + ESP_LOG_BUFFER_HEXDUMP(TAG, fup_params.ssi, fup_params.ssi_len, ESP_LOG_INFO); + } else { + ESP_LOGI(TAG, "SSI - [NULL]"); + } + ESP_RETURN_VOID_ON_ERROR(esp_wifi_nan_send_message(&fup_params), TAG, "Sending message to the peer with ID %d failed!", g_peer_inst_id); + } + return; +} + +void initialise_wifi(void) +{ + 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)); +} + +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(); + wifi_usd_subscribe(); + + return; +} diff --git a/examples/wifi/wifi_aware/usd_subscriber/sdkconfig.defaults b/examples/wifi/wifi_aware/usd_subscriber/sdkconfig.defaults new file mode 100644 index 00000000000..d0ffed238ef --- /dev/null +++ b/examples/wifi/wifi_aware/usd_subscriber/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_ESP_WIFI_NAN_USD_ENABLE=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/tools/idf_py_actions/hints.yml b/tools/idf_py_actions/hints.yml index ce4385d3666..c7af7f187d3 100644 --- a/tools/idf_py_actions/hints.yml +++ b/tools/idf_py_actions/hints.yml @@ -624,6 +624,10 @@ re: "error: implicit declaration of function 'esp_wifi_config_espnow_rate'" hint: "The function 'esp_wifi_config_espnow_rate' has been removed. Use 'esp_now_set_peer_rate_config' instead." +- + re: "(initialization|assignment) of 'wifi_nan_svc_proto_t \\*' from incompatible pointer type 'uint8_t \\*'" + hint: "The field 'proto' in structure 'wifi_nan_wfa_ssi_t' is now declared as 'uint8_t'. Update pointer usage accordingly, for example by using a 'uint8_t *' or copying the enum value into a separate variable." + - re: "error: too many arguments to function 'esp_wifi_wps_start'" hint: "The function 'esp_wifi_wps_start' no longer accepts a 'timeout_ms' argument." @@ -656,10 +660,36 @@ re: "error: 'peer_svc_info' undeclared" hint: "The field 'peer_svc_info' from structure 'wifi_event_nan_receive_t' has been removed. Please use the equivalent fields 'ssi' and 'ssi_len' instead." +- + re: "error: 'WIFI_EVENT_NAN_STARTED' undeclared" + hint: "The Wi-Fi NAN event 'WIFI_EVENT_NAN_STARTED' has been renamed to 'WIFI_EVENT_NAN_SYNC_STARTED'." + +- + re: "error: 'WIFI_EVENT_NAN_STOPPED' undeclared" + hint: "The Wi-Fi NAN event 'WIFI_EVENT_NAN_STOPPED' has been renamed to 'WIFI_EVENT_NAN_SYNC_STOPPED'." + - re: "error: too few arguments to function 'esp_wifi_nan_publish_service'" hint: "The function argument 'ndp_resp_needed' of 'esp_wifi_nan_publish_service' has been moved to structure 'wifi_nan_publish_cfg_t'." +- + re: "error: implicit declaration of function '{}'" + hint: "The NAN synchronization API '{}' has been renamed to '{}'." + variables: + - + re_variables: ['esp_wifi_nan_start'] + hint_variables: ['esp_wifi_nan_start', 'esp_wifi_nan_sync_start'] + - + re_variables: ['esp_wifi_nan_stop'] + hint_variables: ['esp_wifi_nan_stop', 'esp_wifi_nan_sync_stop'] + - + re_variables: ['WIFI_NAN_CONFIG_DEFAULT'] + hint_variables: ['WIFI_NAN_CONFIG_DEFAULT', 'WIFI_NAN_SYNC_CONFIG_DEFAULT'] + +- + re: "error: unknown type name 'wifi_nan_config_t'" + hint: "The structure 'wifi_nan_config_t' has been renamed to 'wifi_nan_sync_config_t'." + - re: "error: '(WIFI_REASON_ASSOC_EXPIRE|WIFI_REASON_NOT_AUTHED|WIFI_REASON_NOT_ASSOCED)' undeclared" hint: "The Wi-Fi disconnection reason codes '{0}' have been removed. Please use '{1}', '{2}', and '{3}' respectively."