diff --git a/components/esp_wifi/CMakeLists.txt b/components/esp_wifi/CMakeLists.txt index 569bc2efb7..9886f0f07e 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_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 c2fe6a2aee..24cea7102b 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -584,6 +584,12 @@ menu "Wi-Fi" help Enable WiFi Aware (NAN) feature. + config ESP_WIFI_NAN_USD_ENABLE + bool "Enable NAN USD" + default n + help + Enable NAN(WiFi Aware) Unsynchronized service discovery + config ESP_WIFI_MBEDTLS_CRYPTO bool "Use MbedTLS crypto APIs" default y diff --git a/components/esp_wifi/include/esp_wifi_types_generic.h b/components/esp_wifi/include/esp_wifi_types_generic.h index 5b7e2cb363..6d9cdbd6bc 100644 --- a/components/esp_wifi/include/esp_wifi_types_generic.h +++ b/components/esp_wifi/include/esp_wifi_types_generic.h @@ -592,6 +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 */ + bool discovery_flag; /**< False in case of NAN Synchronization, True in case of Unsynchronized service discovery (USD)*/ } wifi_nan_config_t; /** @@ -830,7 +831,7 @@ 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 will be received @@ -850,7 +851,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 */ @@ -882,7 +883,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; @@ -897,6 +898,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 * @@ -910,9 +924,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; /** @@ -927,9 +945,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; /** diff --git a/components/esp_wifi/lib b/components/esp_wifi/lib index 37a69947e1..21eacc3229 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/wifi_apps/nan_app/include/esp_nan.h b/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h index c370b76ac5..2dd65d7fcc 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 @@ -20,6 +20,12 @@ extern "C" { .master_pref = 2, \ .scan_time = 3, \ .warm_up_sec = 5, \ + .discovery_flag = 0, \ +}; + +/* For USD, all parameters other than discovery_flag are ignored */ +#define WIFI_USD_NAN_CONFIG_DEFAULT() { \ + .discovery_flag = 1, \ }; #define NDP_STATUS_ACCEPTED 1 @@ -40,7 +46,7 @@ struct nan_peer_record { }; /** - * @brief Start NAN Discovery with provided configuration + * @brief Start NAN Discovery or Unsynchronized service discovery (USD) with provided configuration * * @attention This API should be called after esp_wifi_init(). * @@ -53,7 +59,10 @@ struct nan_peer_record { esp_err_t esp_wifi_nan_start(const wifi_nan_config_t *nan_cfg); /** - * @brief Stop NAN Discovery, end NAN Services and Datapaths + * @brief Stop NAN Discovery or USD and end publish/subscribe services + * + * @attention This API will end datapaths if any in NAN synchronization + * @attention This API will stop USD if discovery_flag is set to true, else it will stop NAN Discovery * * @return * - ESP_OK: succeed @@ -62,9 +71,13 @@ esp_err_t esp_wifi_nan_start(const wifi_nan_config_t *nan_cfg); esp_err_t esp_wifi_nan_stop(void); /** - * @brief Start Publishing a service to the NAN Peers in vicinity + * @brief Start Publishing a service to the NAN/USD Peers in vicinity * * @attention This API should be called after esp_wifi_nan_start(). + * @attention This API will start a publisher in USD if discovery_flag is true + * @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. * @@ -75,9 +88,13 @@ esp_err_t esp_wifi_nan_stop(void); uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg); /** - * @brief Subscribe for a service within the NAN cluster + * @brief Subscribe to a service published by a NAN peer within a cluster or a USD peer * * @attention This API should be called after esp_wifi_nan_start(). + * @attention This API will start a subscriber in USD if discovery_flag is true + * @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. * @@ -88,9 +105,9 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg); 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 + * @brief Send a follow-up message to a matched peer in NAN synchronization or USD * - * @attention This API should be called after a NAN service is discovered due to a match. + * @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. * @@ -101,7 +118,7 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe esp_err_t esp_wifi_nan_send_message(wifi_nan_followup_params_t *fup_params); /** - * @brief Cancel a NAN service + * @brief Cancel a NAN synchronization or NAN-USD service * * @param service_id Publish/Subscribe service id to be cancelled. * @@ -198,6 +215,41 @@ esp_err_t esp_wifi_nan_get_peer_records(int *num_peer_records, uint8_t own_svc_i */ esp_err_t esp_wifi_nan_get_peer_info(char *svc_name, uint8_t *peer_mac, struct nan_peer_record *peer_info); +/** + * @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); + #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 df08a10ad0..cfe32d10cf 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_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_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_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 */ @@ -782,11 +794,13 @@ void esp_nan_action_stop(void) os_event_group_set_bits(nan_event_group, NAN_STOPPED_BIT); } +#endif /* CONFIG_ESP_WIFI_NAN_ENABLE */ + esp_err_t esp_wifi_nan_start(const wifi_nan_config_t *nan_cfg) { wifi_mode_t mode; esp_err_t ret; - wifi_config_t config = {0}; + s_usd_in_progress = false; ret = esp_wifi_get_mode(&mode); if (ret == ESP_ERR_WIFI_NOT_INIT) { @@ -797,44 +811,89 @@ esp_err_t esp_wifi_nan_start(const wifi_nan_config_t *nan_cfg) return ret; } - if (!s_nan_data_lock) { - ESP_LOGE(TAG, "NAN Data lock doesn't exist"); +#if (!CONFIG_ESP_WIFI_NAN_ENABLE) + if (!nan_cfg->discovery_flag) { + ESP_LOGE(TAG, "discovery_flag must be set when" + "ESP_WIFI_NAN_USD_ENABLE is enabled"); return ESP_FAIL; } +#endif - NAN_DATA_LOCK(); - if (s_nan_ctx.state & NAN_STARTED_BIT) { - ESP_LOGI(TAG, "NAN already started"); - NAN_DATA_UNLOCK(); + /* XXX: For now, NAN-USD and NAN-Sync can not coexist. */ +#ifdef CONFIG_ESP_WIFI_NAN_USD_ENABLE + if (nan_cfg->discovery_flag) { + /* NAN-USD Only */ + ESP_RETURN_ON_ERROR(esp_wifi_set_mode(WIFI_MODE_STA), "NAN-USD", "Setting STA mode failed"); + ESP_RETURN_ON_ERROR(esp_wifi_start(), "NAN-USD", "Starting WiFi 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; } - NAN_DATA_UNLOCK(); +#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */ +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE + else { + if (nan_cfg->discovery_flag) { + /* Should reach here only if ESP_WIFI_NAN_USD_ENABLE is disabled */ + ESP_LOGE(TAG, "Can not set discovery_flag without " + "ESP_WIFI_NAN_USD_ENABLE enabled"); + return ESP_FAIL; + } - ESP_RETURN_ON_ERROR(esp_wifi_set_mode(WIFI_MODE_NAN), TAG, "Set mode NAN failed"); + /* 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; + } - memcpy(&config.nan, nan_cfg, sizeof(wifi_nan_config_t)); - ESP_RETURN_ON_ERROR(esp_wifi_set_config(WIFI_IF_NAN, &config), TAG, "Setting NAN config failed"); - - if (esp_wifi_start() != ESP_OK) { - ESP_LOGE(TAG, "Starting wifi failed"); NAN_DATA_LOCK(); - s_nan_ctx.nan_netif = NULL; + if (s_nan_ctx.state & NAN_STARTED_BIT) { + ESP_LOGI(TAG, "NAN already started"); + NAN_DATA_UNLOCK(); + return ESP_OK; + } NAN_DATA_UNLOCK(); - return ESP_FAIL; - } - EventBits_t bits = os_event_group_wait_bits(nan_event_group, NAN_STARTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); - if (!(bits & NAN_STARTED_BIT)) { - NAN_DATA_LOCK(); - s_nan_ctx.nan_netif = NULL; - NAN_DATA_UNLOCK(); - return ESP_FAIL; + ESP_RETURN_ON_ERROR(esp_wifi_set_mode(WIFI_MODE_NAN), TAG, "Set mode NAN failed"); + + memcpy(&config.nan, nan_cfg, sizeof(wifi_nan_config_t)); + ESP_RETURN_ON_ERROR(esp_wifi_set_config(WIFI_IF_NAN, &config), TAG, "Setting NAN config failed"); + + if (esp_wifi_start() != ESP_OK) { + ESP_LOGE(TAG, "Starting wifi failed"); + NAN_DATA_LOCK(); + s_nan_ctx.nan_netif = NULL; + NAN_DATA_UNLOCK(); + return ESP_FAIL; + } + + EventBits_t bits = os_event_group_wait_bits(nan_event_group, NAN_STARTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + if (!(bits & NAN_STARTED_BIT)) { + NAN_DATA_LOCK(); + s_nan_ctx.nan_netif = NULL; + NAN_DATA_UNLOCK(); + return ESP_FAIL; + } + return ESP_OK; } - return ESP_OK; +#endif /* CONFIG_ESP_WIFI_NAN_ENABLE */ + return ESP_FAIL; } esp_err_t esp_wifi_nan_stop(void) { +#ifdef CONFIG_ESP_WIFI_NAN_USD_ENABLE + if (s_usd_in_progress) { + s_usd_in_progress = false; + esp_nan_usd_deinit(); + ESP_RETURN_ON_ERROR(esp_wifi_stop(), TAG, "Stopping NAN-USD failed"); + ESP_LOGI(TAG, "NaN-USD Stopped"); + return ESP_OK; + } +#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */ + +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE NAN_DATA_LOCK(); if (!(s_nan_ctx.state & NAN_STARTED_BIT)) { ESP_LOGE(TAG, "NAN isn't started"); @@ -871,43 +930,79 @@ esp_err_t esp_wifi_nan_stop(void) NAN_DATA_LOCK(); memset(&s_nan_ctx, 0, sizeof(nan_ctx_t)); NAN_DATA_UNLOCK(); +#endif /* CONFIG_ESP_WIFI_NAN_ENABLE */ return ESP_OK; } 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_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 +1015,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_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_ENABLE NAN_DATA_LOCK(); if (!(s_nan_ctx.state & NAN_STARTED_BIT)) { ESP_LOGE(TAG, "NAN not started!"); @@ -941,63 +1079,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_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_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 +1163,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 +1178,20 @@ 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_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); + } + else +#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */ +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE NAN_DATA_LOCK(); struct own_svc_info *p_own_svc = nan_find_own_svc(service_id); @@ -1067,8 +1223,12 @@ fail: done: NAN_DATA_UNLOCK(); return ESP_OK; +#endif /* CONFIG_ESP_WIFI_NAN_ENABLE */ + return ESP_FAIL; } + +#ifdef CONFIG_ESP_WIFI_NAN_ENABLE uint8_t esp_wifi_nan_datapath_req(wifi_nan_datapath_req_t *req) { uint8_t ndp_id = 0; @@ -1337,3 +1497,58 @@ 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_ENABLE */ + +#ifdef CONFIG_ESP_WIFI_NAN_USD_ENABLE +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 ff4659ee42..3ebc9155bb 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 0000000000..fbb8914624 --- /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 0000000000..92a95a00ac --- /dev/null +++ b/components/wpa_supplicant/esp_supplicant/src/esp_nan_usd.c @@ -0,0 +1,678 @@ +/* + * 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_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); + 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/nan_de.c b/components/wpa_supplicant/src/common/nan_de.c index 0098eb8a92..8afe7d9671 100644 --- a/components/wpa_supplicant/src/common/nan_de.c +++ b/components/wpa_supplicant/src/common/nan_de.c @@ -15,6 +15,7 @@ #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 }; @@ -282,6 +283,12 @@ static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv, 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 @@ -544,9 +551,11 @@ static int nan_de_srv_time_to_next(struct nan_de *de, 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; @@ -656,7 +665,9 @@ static void nan_de_timer(void *eloop_ctx, void *timeout_ctx) 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); @@ -1001,6 +1012,12 @@ static void nan_de_rx_subscribe(struct nan_de *de, struct nan_de_service *srv, 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 @@ -1038,7 +1055,18 @@ static void nan_de_rx_subscribe(struct nan_de *de, struct nan_de_service *srv, de->nmi, a3, buf); wpabuf_free(buf); - nan_de_pause_state(srv, peer_addr, instance_id); + 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) @@ -1540,3 +1568,20 @@ int nan_de_transmit(struct nan_de *de, int handle, 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 index 2c95829bcc..fcbf5c9fc6 100644 --- a/components/wpa_supplicant/src/common/nan_de.h +++ b/components/wpa_supplicant/src/common/nan_de.h @@ -10,10 +10,15 @@ #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; @@ -149,4 +154,7 @@ 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 51acf7d6f3..ef36edc7dc 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 998870e90f..4fa4e11798 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 d3e759e3e9..ae655e788e 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/examples/wifi/.build-test-rules.yml b/examples/wifi/.build-test-rules.yml index 2d379ddcfd..3aa9f4c908 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/usd_publisher/CMakeLists.txt b/examples/wifi/wifi_aware/usd_publisher/CMakeLists.txt new file mode 100644 index 0000000000..95c1103419 --- /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 0000000000..1c35092948 --- /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 0000000000..19b52737d1 --- /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 0000000000..d79aba9e76 --- /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 0000000000..8426737e87 --- /dev/null +++ b/examples/wifi/wifi_aware/usd_publisher/main/usd_publisher_example_main.c @@ -0,0 +1,127 @@ +/* + * 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)); + + /* Start USD-NAN Discovery */ + wifi_nan_config_t usd_nan_cfg = WIFI_USD_NAN_CONFIG_DEFAULT(); + ESP_RETURN_VOID_ON_ERROR(esp_wifi_nan_start(&usd_nan_cfg), 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_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) ); +} + +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 0000000000..47d739bb22 --- /dev/null +++ b/examples/wifi/wifi_aware/usd_publisher/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_ESP_WIFI_NAN_USD_ENABLE=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 0000000000..75d5bab606 --- /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 0000000000..1c35092948 --- /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 0000000000..2c34987a32 --- /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 0000000000..7b00aa4015 --- /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 0000000000..af7a65b8bf --- /dev/null +++ b/examples/wifi/wifi_aware/usd_subscriber/main/usd_subscriber_example_main.c @@ -0,0 +1,151 @@ +/* + * 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)); + + /* Start NAN-USD Discovery */ + wifi_nan_config_t nan_usd_cfg = WIFI_USD_NAN_CONFIG_DEFAULT(); + esp_wifi_nan_start(&nan_usd_cfg); + + 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) ); +} + +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 0000000000..47d739bb22 --- /dev/null +++ b/examples/wifi/wifi_aware/usd_subscriber/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_ESP_WIFI_NAN_USD_ENABLE=y