esp-idf/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c

1219 wiersze
38 KiB
C

/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_wifi.h"
#include "esp_private/wifi.h"
#include "esp_wifi_netif.h"
#include "freertos/event_groups.h"
#include "lwip/netdb.h"
#include "lwip/sockets.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_mac.h"
#include "os.h"
#include "esp_nan.h"
/* NAN States */
#define NAN_STARTED_BIT BIT0
#define NAN_STOPPED_BIT BIT1
/* NAN Events */
#define NDP_INDICATION BIT2
#define NDP_ACCEPTED BIT3
#define NDP_TERMINATED BIT4
#define NDP_REJECTED BIT5
/* Macros */
#define MACADDR_LEN 6
#define MACADDR_EQUAL(a1, a2) (memcmp(a1, a2, MACADDR_LEN))
#define MACADDR_COPY(dst, src) (memcpy(dst, src, MACADDR_LEN))
#define NAN_DW_INTVL_MS 524 /* NAN DW interval (512 TU's ~= 524 mSec) */
#define NAN_NDP_RESP_TIMEOUT_DW 4
#define NAN_NDP_RESP_TIMEOUT NAN_NDP_RESP_TIMEOUT_DW*NAN_DW_INTVL_MS
/* Global Variables */
static const char *TAG = "nan_app";
static EventGroupHandle_t nan_event_group;
static bool s_app_default_handlers_set = false;
static uint8_t null_mac[MACADDR_LEN] = {0};
static void *s_nan_data_lock = NULL;
#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 */
uint8_t svc_id; /**< Identifier of peer's service */
uint8_t own_svc_id; /**< Identifier for own service */
uint8_t type; /**< Service type (Publish/Subscribe) */
uint8_t peer_nmi[MACADDR_LEN]; /**< Peer's NAN Management Interface address */
};
struct own_svc_info {
char svc_name[ESP_WIFI_MAX_SVC_NAME_LEN]; /**< Name identifying a service */
uint8_t svc_id; /**< Identifier for a service */
uint8_t type; /**< Service type (Publish/Subscribe) */
bool ndp_resp_needed; /**< If enabled, NDP response is required */
uint8_t num_peer_records; /**< Count of peer records associated with svc_id */
SLIST_HEAD(peer_list_t, peer_svc_info) peer_list; /**< List of peers matched for specific service */
};
struct ndl_info {
uint8_t ndp_id; /**< Identifier for instance of NDP */
uint8_t peer_ndi[MACADDR_LEN]; /**< Peer's NAN Data Interface address */
uint8_t peer_nmi[MACADDR_LEN]; /**< Peer's NAN Management Interface address */
uint8_t publisher_id; /**< Publisher's service identifier */
uint8_t own_role; /**< Own role (Publisher/Subscriber) */
};
typedef struct {
uint8_t state;
uint8_t event;
struct ndl_info ndl[ESP_WIFI_NAN_DATAPATH_MAX_PEERS]; /**< Record of NDL of all peers */
struct own_svc_info own_svc[ESP_WIFI_NAN_MAX_SVC_SUPPORTED]; /**< Record of own service(s) */
esp_netif_t *nan_netif;
} nan_ctx_t;
static nan_ctx_t s_nan_ctx;
void esp_wifi_nan_get_ipv6_linklocal_from_mac(ip6_addr_t *ip6, uint8_t *mac_addr)
{
if (ip6 == NULL || mac_addr == NULL) {
return;
}
/* Link-local prefix. */
ip6->addr[0] = htonl(0xfe800000ul);
ip6->addr[1] = 0;
/* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */
ip6->addr[2] = htonl((((uint32_t)(mac_addr[0] ^ 0x02)) << 24) |
((uint32_t)(mac_addr[1]) << 16) |
((uint32_t)(mac_addr[2]) << 8) |
(0xff));
ip6->addr[3] = htonl((uint32_t)(0xfeul << 24) |
((uint32_t)(mac_addr[3]) << 16) |
((uint32_t)(mac_addr[4]) << 8) |
(mac_addr[5]));
ip6->zone = IP6_NO_ZONE;
}
static struct own_svc_info *nan_find_own_svc(uint8_t svc_id)
{
struct own_svc_info *p_svc = NULL;
if (svc_id == 0) {
ESP_LOGE(TAG, "Service id cannot be 0!");
return NULL;
}
for (int i = 0; i < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; i++) {
if (s_nan_ctx.own_svc[i].svc_id == svc_id) {
p_svc = &s_nan_ctx.own_svc[i];
break;
}
}
return p_svc;
}
static struct own_svc_info *nan_find_own_svc_by_name(const char *svc_name)
{
struct own_svc_info *p_svc = NULL;
if (!svc_name) {
ESP_LOGE(TAG, "Service name not given!");
return NULL;
}
for (int i = 0; i < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; i++) {
if (s_nan_ctx.own_svc[i].svc_id && !strcmp(s_nan_ctx.own_svc[i].svc_name, svc_name)) {
p_svc = &s_nan_ctx.own_svc[i];
break;
}
}
return p_svc;
}
static struct peer_svc_info *nan_find_peer_svc(uint8_t own_svc_id, uint8_t peer_svc_id, uint8_t peer_nmi[])
{
struct peer_svc_info *p_peer_svc = NULL, *temp;
struct own_svc_info *p_own_svc = NULL;
uint8_t *peer_nmi_valid = NULL;
int idx = 0;
if (MACADDR_EQUAL(peer_nmi, null_mac)) {
/* non-zero Peer NMI given, use it */
peer_nmi_valid = peer_nmi;
}
while (idx < ESP_WIFI_NAN_MAX_SVC_SUPPORTED) {
if (own_svc_id) {
p_own_svc = nan_find_own_svc(own_svc_id);
if (!p_own_svc) {
ESP_LOGE(TAG, "Cannot find own service with id %d!", own_svc_id);
return NULL;
}
} else {
p_own_svc = &s_nan_ctx.own_svc[idx++];
}
SLIST_FOREACH(temp, &(p_own_svc->peer_list), next) {
if (peer_svc_id != 0 && peer_nmi_valid) {
if (temp->svc_id == peer_svc_id && !MACADDR_EQUAL(temp->peer_nmi, peer_nmi_valid)) {
p_peer_svc = temp;
break;
}
} else if (peer_svc_id != 0) {
if (temp->svc_id == peer_svc_id) {
p_peer_svc = temp;
break;
}
} else {
if (peer_nmi_valid && !MACADDR_EQUAL(temp->peer_nmi, peer_nmi_valid)) {
p_peer_svc = temp;
break;
}
}
}
if (p_peer_svc || own_svc_id) { /* If no peer found with given own_svc_id, don't search in other services */
break;
}
}
return p_peer_svc;
}
static bool nan_record_peer_svc(uint8_t own_svc_id, uint8_t peer_svc_id, uint8_t peer_nmi[])
{
struct own_svc_info *p_own_svc;
struct peer_svc_info *p_peer_svc;
p_own_svc = nan_find_own_svc(own_svc_id);
if (!p_own_svc) {
ESP_LOGE(TAG, "Unable to find own service with id %d", own_svc_id);
return false;
}
p_peer_svc = (struct peer_svc_info *)os_zalloc(sizeof(struct peer_svc_info));
if (!p_peer_svc) {
ESP_LOGE(TAG, "Unable to allocate for Peer Service");
return false;
}
p_peer_svc->svc_id = peer_svc_id;
p_peer_svc->own_svc_id = own_svc_id;
p_peer_svc->type = (p_own_svc->type == ESP_NAN_SUBSCRIBE) ? ESP_NAN_PUBLISH : ESP_NAN_SUBSCRIBE;
MACADDR_COPY(p_peer_svc->peer_nmi, peer_nmi);
if (p_own_svc->num_peer_records < NAN_MAX_PEERS_RECORD) {
SLIST_INSERT_HEAD(&(p_own_svc->peer_list), p_peer_svc, next);
p_own_svc->num_peer_records++;
} else {
/* Remove the oldest peer service entry */
struct peer_svc_info *prev_ele = NULL, *cur_ele = NULL;
SLIST_FOREACH(cur_ele, &(p_own_svc->peer_list), next) {
if (SLIST_NEXT(cur_ele, next) == NULL) {
if (SLIST_FIRST(&(p_own_svc->peer_list)) == cur_ele) {
SLIST_REMOVE_HEAD(&(p_own_svc->peer_list), next);
} else {
SLIST_REMOVE_AFTER(prev_ele, next);
}
break;
}
prev_ele = cur_ele;
}
/* Insert new peer service entry */
SLIST_INSERT_HEAD(&(p_own_svc->peer_list), p_peer_svc, next);
os_free(cur_ele);
}
return true;
}
static void nan_reset_service(uint8_t svc_id, bool reset_all)
{
struct own_svc_info *p_own_svc = NULL;
struct peer_svc_info *p_peer_svc = NULL, *temp;
int idx = 0;
if (svc_id == 0 && !reset_all) {
return;
}
while (idx < ESP_WIFI_NAN_MAX_SVC_SUPPORTED) {
p_own_svc = &s_nan_ctx.own_svc[idx++];
if (reset_all || (svc_id && p_own_svc->svc_id == svc_id)) {
SLIST_FOREACH_SAFE(p_peer_svc, &(p_own_svc->peer_list), next, temp) {
SLIST_REMOVE(&(p_own_svc->peer_list), p_peer_svc, peer_svc_info, next);
os_free(p_peer_svc);
}
memset(p_own_svc, 0, sizeof(struct own_svc_info));
}
}
}
static void nan_reset_ndl(uint8_t ndp_id, bool reset_all)
{
struct ndl_info *ndl = NULL;
if (reset_all) {
memset(s_nan_ctx.ndl, 0, sizeof(struct ndl_info) * ESP_WIFI_NAN_DATAPATH_MAX_PEERS);
return;
}
for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) {
ndl = &s_nan_ctx.ndl[i];
if (ndl->ndp_id == ndp_id) {
memset(ndl, 0, sizeof(struct ndl_info));
break;
}
}
}
static bool nan_services_limit_reached(void)
{
for (int i = 0; i < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; i++) {
if (s_nan_ctx.own_svc[i].svc_id == 0) {
return false;
}
}
return true;
}
static void nan_record_own_svc(uint8_t id, uint8_t type, const char svc_name[], bool ndp_resp_needed)
{
struct own_svc_info *p_svc = NULL;
for (int i = 0; i < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; i++) {
if (s_nan_ctx.own_svc[i].svc_id == 0) {
p_svc = &s_nan_ctx.own_svc[i];
break;
}
}
if (!p_svc) {
return;
}
p_svc->svc_id = id;
p_svc->type = type;
strlcpy(p_svc->svc_name, svc_name, ESP_WIFI_MAX_SVC_NAME_LEN);
SLIST_INIT(&(p_svc->peer_list));
if (type == ESP_NAN_PUBLISH) {
p_svc->ndp_resp_needed = ndp_resp_needed;
}
}
static bool ndl_limit_reached(void)
{
for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) {
if (s_nan_ctx.ndl[i].ndp_id == 0) {
return false;
}
}
return true;
}
static void nan_record_new_ndl(uint8_t ndp_id, uint8_t publish_id, uint8_t peer_nmi[], uint8_t own_role)
{
struct ndl_info *ndl = NULL;
for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) {
if (s_nan_ctx.ndl[i].ndp_id == 0) {
ndl = &s_nan_ctx.ndl[i];
break;
}
}
if (!ndl) {
return;
}
ndl->ndp_id = ndp_id;
if (peer_nmi) {
MACADDR_COPY(ndl->peer_nmi, peer_nmi);
}
ndl->publisher_id = publish_id;
ndl->own_role = own_role;
}
static struct ndl_info *nan_find_ndl(uint8_t ndp_id, uint8_t peer_nmi[])
{
struct ndl_info *ndl = NULL;
for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) {
ndl = &s_nan_ctx.ndl[i];
if (ndp_id != 0 && peer_nmi) {
if (ndl->ndp_id == ndp_id && !MACADDR_EQUAL(ndl->peer_nmi, peer_nmi)) {
return ndl;
}
} else if (ndp_id != 0) {
if (ndl->ndp_id == ndp_id) {
return ndl;
}
} else if (peer_nmi) {
if (!MACADDR_EQUAL(ndl->peer_nmi, peer_nmi)) {
return ndl;
}
}
}
return NULL;
}
static bool nan_is_datapath_active(void)
{
for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) {
if (s_nan_ctx.ndl[i].ndp_id != 0) {
return true;
}
}
return false;
}
static void nan_update_peer_svc(uint8_t own_svc_id, uint8_t peer_svc_id, uint8_t peer_nmi[])
{
struct peer_svc_info *peer_info = nan_find_peer_svc(own_svc_id, 0, peer_nmi);
if (peer_info) {
peer_info->svc_id = peer_svc_id;
}
struct ndl_info *ndl = nan_find_ndl(0, peer_nmi);
if (ndl) {
ndl->publisher_id = peer_svc_id;
}
}
static void nan_fill_params_from_event(void *evt_data, uint8_t event)
{
switch (event) {
case WIFI_EVENT_NDP_INDICATION: {
wifi_event_ndp_indication_t *evt = (wifi_event_ndp_indication_t *)evt_data;
nan_record_new_ndl(evt->ndp_id, evt->publish_id, evt->peer_nmi, ESP_WIFI_NDP_ROLE_RESPONDER);
break;
}
case WIFI_EVENT_NDP_CONFIRM: {
wifi_event_ndp_confirm_t *evt = (wifi_event_ndp_confirm_t *)evt_data;
struct ndl_info *ndl = NULL;
if ((ndl = nan_find_ndl(evt->ndp_id, evt->peer_nmi)) == NULL) {
ESP_LOGE(TAG, "No NDL with ndp id %d", evt->ndp_id);
return;
}
MACADDR_COPY(ndl->peer_ndi, evt->peer_ndi);
break;
}
case WIFI_EVENT_NAN_REPLIED: {
wifi_event_nan_replied_t *evt = (wifi_event_nan_replied_t *)evt_data;
if (!nan_find_peer_svc(evt->publish_id, evt->subscribe_id, evt->sub_if_mac)) {
nan_record_peer_svc(evt->publish_id, evt->subscribe_id, evt->sub_if_mac);
}
break;
}
case WIFI_EVENT_NAN_RECEIVE: {
wifi_event_nan_receive_t *evt = (wifi_event_nan_receive_t *)evt_data;
if (!nan_find_peer_svc(evt->inst_id, evt->peer_inst_id, evt->peer_if_mac)) {
nan_record_peer_svc(evt->inst_id, evt->peer_inst_id, evt->peer_if_mac);
}
break;
}
case WIFI_EVENT_NAN_SVC_MATCH: {
wifi_event_nan_svc_match_t *evt = (wifi_event_nan_svc_match_t *)evt_data;
if (evt->update_pub_id) {
nan_update_peer_svc(evt->subscribe_id, evt->publish_id, evt->pub_if_mac);
}
if (!nan_find_peer_svc(evt->subscribe_id, evt->publish_id, evt->pub_if_mac)) {
nan_record_peer_svc(evt->subscribe_id, evt->publish_id, evt->pub_if_mac);
}
break;
}
default:
break;
}
}
static void nan_app_action_service_match(void *arg, esp_event_base_t event_base, int32_t event_id, void *data)
{
if (data == NULL) {
return;
}
wifi_event_nan_svc_match_t *evt = (wifi_event_nan_svc_match_t *)data;
ESP_LOGI(TAG, "Service matched with "MACSTR" [Peer Publish id - %d]",
MAC2STR(evt->pub_if_mac), evt->publish_id);
NAN_DATA_LOCK();
nan_fill_params_from_event(evt, WIFI_EVENT_NAN_SVC_MATCH);
NAN_DATA_UNLOCK();
}
static void nan_app_action_replied(void *arg, esp_event_base_t event_base, int32_t event_id, void *data)
{
if (data == NULL) {
return;
}
wifi_event_nan_replied_t *evt = (wifi_event_nan_replied_t *)data;
ESP_LOGD(TAG, "Sent Publish to Peer "MACSTR" [Peer Subscribe id - %d]",
MAC2STR(evt->sub_if_mac), evt->subscribe_id);
NAN_DATA_LOCK();
nan_fill_params_from_event(evt, WIFI_EVENT_NAN_REPLIED);
NAN_DATA_UNLOCK();
}
static void nan_app_action_receive(void *arg, esp_event_base_t event_base, int32_t event_id, void *data)
{
if (data == NULL) {
return;
}
wifi_event_nan_receive_t *evt = (wifi_event_nan_receive_t *)data;
ESP_LOGI(TAG, "Received message '%s' from Peer "MACSTR" [Peer Service id - %d]",
evt->peer_svc_info, MAC2STR(evt->peer_if_mac), evt->peer_inst_id);
NAN_DATA_LOCK();
nan_fill_params_from_event(evt, WIFI_EVENT_NAN_RECEIVE);
NAN_DATA_UNLOCK();
}
static void nan_app_action_ndp_indication(void *arg, esp_event_base_t event_base, int32_t event_id, void *data)
{
if (data == NULL) {
return;
}
wifi_event_ndp_indication_t *evt = (wifi_event_ndp_indication_t *)data;
NAN_DATA_LOCK();
struct own_svc_info *p_own_svc = nan_find_own_svc(evt->publish_id);
if (!p_own_svc) {
ESP_LOGE(TAG, "No Publish found with id %d", evt->publish_id);
goto done;
}
if (ndl_limit_reached()) {
ESP_LOGE(TAG, "NDP limit reached");
goto done;
}
nan_fill_params_from_event(evt, WIFI_EVENT_NDP_INDICATION);
if (p_own_svc->ndp_resp_needed) {
ESP_LOGI(TAG, "NDP Req from "MACSTR" [NDP Id: %d], Accept OR Deny using NDP command",
MAC2STR(evt->peer_nmi), evt->ndp_id);
s_nan_ctx.event |= NDP_INDICATION;
} else {
wifi_nan_datapath_resp_t ndp_resp = {0};
ndp_resp.accept = true;
ndp_resp.ndp_id = evt->ndp_id;
MACADDR_COPY(ndp_resp.peer_mac, evt->peer_nmi);
esp_nan_internal_datapath_resp(&ndp_resp);
}
done:
NAN_DATA_UNLOCK();
}
static void nan_app_action_ndp_confirm(void *arg, esp_event_base_t event_base, int32_t event_id, void *data)
{
if (data == NULL) {
return;
}
wifi_event_ndp_confirm_t *evt = (wifi_event_ndp_confirm_t *)data;
NAN_DATA_LOCK();
wifi_netif_driver_t driver = esp_netif_get_io_driver(s_nan_ctx.nan_netif);
ip_addr_t target_addr = {0};
if (!s_nan_ctx.nan_netif) {
ESP_LOGE(TAG, "%s: NAN netif is NULL", __func__);
goto done;
}
if (nan_find_ndl(evt->ndp_id, NULL) == NULL) {
/* As ndl isn't found, timeout has occurred for NDP response and datapath request is rejected */
goto done;
}
if (evt->status == NDP_STATUS_REJECTED) {
ESP_LOGE(TAG, "NDP request to Peer "MACSTR" rejected [NDP ID - %d]", MAC2STR(evt->peer_nmi), evt->ndp_id);
nan_reset_ndl(evt->ndp_id, false);
os_event_group_set_bits(nan_event_group, NDP_REJECTED);
goto done;
}
/* If interface not ready when started, rxcb to be registered on connection */
if (esp_wifi_register_if_rxcb(driver, esp_netif_receive, s_nan_ctx.nan_netif) != ESP_OK) {
ESP_LOGE(TAG, "%s: esp_wifi_register_if_rxcb failed", __func__);
goto done;
}
nan_fill_params_from_event(evt, WIFI_EVENT_NDP_CONFIRM);
esp_netif_action_connected(s_nan_ctx.nan_netif, event_base, event_id, data);
esp_netif_create_ip6_linklocal(s_nan_ctx.nan_netif);
NAN_DATA_UNLOCK();
esp_wifi_nan_get_ipv6_linklocal_from_mac(&target_addr.u_addr.ip6, evt->peer_ndi);
target_addr.type = IPADDR_TYPE_V6;
ESP_LOGI(TAG, "NDP confirmed with Peer "MACSTR" [NDP ID - %d, Peer IPv6 - %s]",
MAC2STR(evt->peer_nmi), evt->ndp_id, inet6_ntoa(*ip_2_ip6(&target_addr)));
os_event_group_set_bits(nan_event_group, NDP_ACCEPTED);
return;
done:
NAN_DATA_UNLOCK();
return;
}
static void nan_app_action_ndp_terminated(void *arg, esp_event_base_t event_base, int32_t event_id, void *data)
{
if (data == NULL) {
return;
}
wifi_event_ndp_terminated_t *evt = (wifi_event_ndp_terminated_t *)data;
NAN_DATA_LOCK();
if (s_nan_ctx.nan_netif && !nan_is_datapath_active()) {
esp_netif_action_disconnected(s_nan_ctx.nan_netif, event_base, event_id, data);
}
ESP_LOGI(TAG, "NDP terminated with Peer "MACSTR" [NDP ID - %d]", MAC2STR(evt->init_ndi), evt->ndp_id);
nan_reset_ndl(evt->ndp_id, false);
s_nan_ctx.event &= ~(NDP_INDICATION);
NAN_DATA_UNLOCK();
os_event_group_set_bits(nan_event_group, NDP_TERMINATED);
}
/* types of ipv6 addresses to be displayed on ipv6 events */
static const char *s_ipv6_addr_types[] = {
"UNKNOWN",
"GLOBAL",
"LINK_LOCAL",
"SITE_LOCAL",
"UNIQUE_LOCAL",
"IPV4_MAPPED_IPV6"
};
static void nan_app_action_got_ipv6(void *arg, esp_event_base_t event_base, int32_t event_id, void *data)
{
if (data == NULL) {
return;
}
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)data;
NAN_DATA_LOCK();
if (event->esp_netif == s_nan_ctx.nan_netif) {
esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip);
ESP_LOGD(TAG, "NAN Data Interface ready [IPv6 - "IPV6STR", type - %s]",
IPV62STR(event->ip6_info.ip), s_ipv6_addr_types[ipv6_type]);
}
NAN_DATA_UNLOCK();
}
static esp_err_t nan_clear_app_default_handlers(void)
{
esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NAN_SVC_MATCH, nan_app_action_service_match);
esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NAN_REPLIED, nan_app_action_replied);
esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NAN_RECEIVE, nan_app_action_receive);
esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NDP_INDICATION, nan_app_action_ndp_indication);
esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NDP_CONFIRM, nan_app_action_ndp_confirm);
esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_NDP_TERMINATED, nan_app_action_ndp_terminated);
esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, nan_app_action_got_ipv6);
s_app_default_handlers_set = false;
return ESP_OK;
}
static esp_err_t nan_set_app_default_handlers(void)
{
if (s_app_default_handlers_set) {
return ESP_OK;
}
int ret;
(void) ret;
ESP_GOTO_ON_ERROR(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NAN_SVC_MATCH,
nan_app_action_service_match, NULL), fail, TAG, "Registering event handler failed");
ESP_GOTO_ON_ERROR(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NAN_REPLIED,
nan_app_action_replied, NULL), fail, TAG, "Registering event handler failed");
ESP_GOTO_ON_ERROR(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NAN_RECEIVE,
nan_app_action_receive, NULL), fail, TAG, "Registering event handler failed");
ESP_GOTO_ON_ERROR(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NDP_INDICATION,
nan_app_action_ndp_indication, NULL), fail, TAG, "Registering event handler failed");
ESP_GOTO_ON_ERROR(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NDP_CONFIRM,
nan_app_action_ndp_confirm, NULL), fail, TAG, "Registering event handler failed");
ESP_GOTO_ON_ERROR(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_NDP_TERMINATED,
nan_app_action_ndp_terminated, NULL), fail, TAG, "Registering event handler failed");
ESP_GOTO_ON_ERROR(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6,
nan_app_action_got_ipv6, NULL), fail, TAG, "Registering event handler failed");
s_app_default_handlers_set = true;
return ESP_OK;
fail:
nan_clear_app_default_handlers();
return ESP_FAIL;
}
void esp_nan_app_deinit(void)
{
if (nan_event_group) {
os_event_group_delete(nan_event_group);
nan_event_group = NULL;
}
if (s_nan_data_lock) {
os_semphr_delete(s_nan_data_lock);
s_nan_data_lock = NULL;
}
}
void esp_nan_app_init(void)
{
if (nan_event_group) {
os_event_group_delete(nan_event_group);
nan_event_group = NULL;
}
nan_event_group = os_event_group_create();
s_nan_data_lock = os_recursive_mutex_create();
if (!s_nan_data_lock) {
ESP_LOGE(TAG, "Failed to create NAN data lock");
esp_nan_app_deinit();
}
}
void esp_nan_action_start(esp_netif_t *nan_netif)
{
if (nan_set_app_default_handlers() != ESP_OK) {
ESP_LOGE(TAG, "Registering NAN handlers failed");
return;
}
NAN_DATA_LOCK();
s_nan_ctx.nan_netif = nan_netif;
s_nan_ctx.state = NAN_STARTED_BIT;
NAN_DATA_UNLOCK();
ESP_LOGI(TAG, "NAN Discovery started.");
os_event_group_set_bits(nan_event_group, NAN_STARTED_BIT);
}
void esp_nan_action_stop(void)
{
nan_clear_app_default_handlers();
NAN_DATA_LOCK();
if (nan_is_datapath_active()) {
nan_reset_ndl(0, true);
esp_wifi_internal_reg_rxcb(WIFI_IF_NAN, NULL);
}
nan_reset_service(0, true);
s_nan_ctx.state &= ~NAN_STARTED_BIT;
s_nan_ctx.state |= NAN_STOPPED_BIT;
NAN_DATA_UNLOCK();
os_event_group_set_bits(nan_event_group, NAN_STOPPED_BIT);
}
esp_err_t esp_wifi_nan_start(const wifi_nan_config_t *nan_cfg)
{
wifi_mode_t mode;
esp_err_t ret;
wifi_config_t config = {0};
ret = esp_wifi_get_mode(&mode);
if (ret == ESP_ERR_WIFI_NOT_INIT) {
ESP_LOGE(TAG, "WiFi not initialised!");
return ret;
} else if (ret != ESP_OK) {
ESP_LOGE(TAG, "Unable to get mode");
return ret;
}
if (!s_nan_data_lock) {
ESP_LOGE(TAG, "NAN Data lock doesn't exist");
return ESP_FAIL;
}
NAN_DATA_LOCK();
if (s_nan_ctx.state & NAN_STARTED_BIT) {
ESP_LOGI(TAG, "NAN already started");
NAN_DATA_UNLOCK();
return ESP_OK;
}
NAN_DATA_UNLOCK();
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;
}
esp_err_t esp_wifi_nan_stop(void)
{
NAN_DATA_LOCK();
if (!(s_nan_ctx.state & NAN_STARTED_BIT)) {
ESP_LOGE(TAG, "NAN isn't started");
NAN_DATA_UNLOCK();
return ESP_FAIL;
}
if (nan_is_datapath_active()) {
/* Terminate all NDP's */
wifi_nan_datapath_end_req_t ndp_end = {0};
for (int i=0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) {
if (s_nan_ctx.ndl[i].ndp_id) {
MACADDR_COPY(ndp_end.peer_mac, s_nan_ctx.ndl[i].peer_nmi);
ndp_end.ndp_id = s_nan_ctx.ndl[i].ndp_id;
esp_nan_internal_datapath_end(&ndp_end);
}
}
nan_reset_ndl(0, true);
NAN_DATA_UNLOCK();
os_event_group_clear_bits(nan_event_group, NDP_TERMINATED);
os_event_group_wait_bits(nan_event_group, NDP_TERMINATED, pdFALSE, pdFALSE, portMAX_DELAY);
os_event_group_clear_bits(nan_event_group, NDP_TERMINATED);
/* Wait for 1 NAN DW interval (512 TU's ~= 524 mSec) for successful termination */
g_wifi_osi_funcs._task_delay(NAN_DW_INTVL_MS/portTICK_PERIOD_MS);
} else {
NAN_DATA_UNLOCK();
}
ESP_RETURN_ON_ERROR(esp_wifi_stop(), TAG, "Stopping NAN failed");
EventBits_t bits = os_event_group_wait_bits(nan_event_group, NAN_STOPPED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
if (!(bits & NAN_STOPPED_BIT)) {
return ESP_FAIL;
}
NAN_DATA_LOCK();
memset(&s_nan_ctx, 0, sizeof(nan_ctx_t));
NAN_DATA_UNLOCK();
return ESP_OK;
}
uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg, bool ndp_resp_needed)
{
uint8_t pub_id;
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);
goto fail;
}
if (esp_nan_internal_publish_service(publish_cfg, &pub_id, false) != ESP_OK) {
ESP_LOGE(TAG, "Failed to publish service '%s'", publish_cfg->service_name);
goto fail;
}
ESP_LOGI(TAG, "Started Publishing %s [Service ID - %u]", publish_cfg->service_name, pub_id);
nan_record_own_svc(pub_id, ESP_NAN_PUBLISH, publish_cfg->service_name, ndp_resp_needed);
NAN_DATA_UNLOCK();
return pub_id;
fail:
NAN_DATA_UNLOCK();
return 0;
}
uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe_cfg)
{
uint8_t sub_id;
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(subscribe_cfg->service_name)) {
ESP_LOGE(TAG, "Service name already used!");
goto fail;
}
if (esp_nan_internal_subscribe_service(subscribe_cfg, &sub_id, false) != ESP_OK) {
ESP_LOGE(TAG, "Failed to subscribe to service '%s'", subscribe_cfg->service_name);
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_DATA_UNLOCK();
return sub_id;
fail:
NAN_DATA_UNLOCK();
return 0;
}
esp_err_t esp_wifi_nan_send_message(wifi_nan_followup_params_t *fup_params)
{
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);
if (!p_peer_svc) {
ESP_LOGE(TAG, "Cannot send Follow-up, peer not found!");
NAN_DATA_UNLOCK();
return ESP_FAIL;
}
if (!fup_params->inst_id) {
fup_params->inst_id = p_peer_svc->own_svc_id;
}
if (!fup_params->peer_inst_id) {
fup_params->peer_inst_id = p_peer_svc->svc_id;
}
if (!MACADDR_EQUAL(fup_params->peer_mac, null_mac)) {
MACADDR_COPY(fup_params->peer_mac, p_peer_svc->peer_nmi);
}
NAN_DATA_UNLOCK();
if (esp_nan_internal_send_followup(fup_params) != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Follow-up message!");
return ESP_FAIL;
}
ESP_LOGI(TAG, "Sent message '%s' to Peer "MACSTR" with Service ID %d", fup_params->svc_info,
MAC2STR(fup_params->peer_mac), fup_params->peer_inst_id);
return ESP_OK;
}
esp_err_t esp_wifi_nan_cancel_service(uint8_t service_id)
{
NAN_DATA_LOCK();
struct own_svc_info *p_own_svc = nan_find_own_svc(service_id);
if (!p_own_svc) {
ESP_LOGE(TAG, "Cannot find own service with id %d!", service_id);
goto fail;
}
if (p_own_svc->type == ESP_NAN_PUBLISH) {
if (esp_nan_internal_publish_service(NULL, &service_id, true) == ESP_OK) {
nan_reset_service(service_id, false);
ESP_LOGI(TAG, "Cancelled Publish with Service ID %d", service_id);
goto done;
}
}
if (p_own_svc->type == ESP_NAN_SUBSCRIBE) {
if (esp_nan_internal_subscribe_service(NULL, &service_id, true) == ESP_OK) {
nan_reset_service(service_id, false);
ESP_LOGI(TAG, "Cancelled Subscribe with Service ID %d", service_id);
goto done;
}
}
fail:
NAN_DATA_UNLOCK();
return ESP_FAIL;
done:
NAN_DATA_UNLOCK();
return ESP_OK;
}
uint8_t esp_wifi_nan_datapath_req(wifi_nan_datapath_req_t *req)
{
uint8_t ndp_id = 0;
NAN_DATA_LOCK();
struct peer_svc_info *p_peer_svc = nan_find_peer_svc(0, req->pub_id, req->peer_mac);
if (!p_peer_svc) {
ESP_LOGE(TAG, "Cannot send NDP Req, peer not found!");
goto fail;
}
if (req->pub_id == 0)
req->pub_id = p_peer_svc->svc_id;
if (p_peer_svc->type != ESP_NAN_PUBLISH) {
ESP_LOGE(TAG, "Only subscriber can send an NDP Req to a Publisher");
goto fail;
}
if (ndl_limit_reached()) {
ESP_LOGE(TAG, "Cannot establish new datapath, limit reached!");
goto fail;
}
if (!MACADDR_EQUAL(req->peer_mac, null_mac)) {
MACADDR_COPY(req->peer_mac, p_peer_svc->peer_nmi);
}
if (esp_nan_internal_datapath_req(req, &ndp_id) != ESP_OK) {
ESP_LOGE(TAG, "Failed to initiate NDP req");
goto fail;
}
nan_record_new_ndl(ndp_id, req->pub_id, req->peer_mac, ESP_WIFI_NDP_ROLE_INITIATOR);
NAN_DATA_UNLOCK();
ESP_LOGD(TAG, "Requested NDP with "MACSTR" [NDP ID - %d]", MAC2STR(req->peer_mac), ndp_id);
EventBits_t bits = os_event_group_wait_bits(nan_event_group, NDP_ACCEPTED | NDP_REJECTED, pdFALSE, pdFALSE, pdMS_TO_TICKS(NAN_NDP_RESP_TIMEOUT));
if (bits & NDP_ACCEPTED) {
os_event_group_clear_bits(nan_event_group, NDP_ACCEPTED);
return ndp_id;
} else if (bits & NDP_REJECTED) {
os_event_group_clear_bits(nan_event_group, NDP_REJECTED);
return 0;
} else {
NAN_DATA_LOCK();
nan_reset_ndl(ndp_id, false);
NAN_DATA_UNLOCK();
return 0;
}
fail:
NAN_DATA_UNLOCK();
return 0;
}
esp_err_t esp_wifi_nan_datapath_resp(wifi_nan_datapath_resp_t *resp)
{
NAN_DATA_LOCK();
struct ndl_info *ndl = nan_find_ndl(resp->ndp_id, NULL);
if (!ndl) {
ESP_LOGE(TAG, "No NDL with ndp id %d", resp->ndp_id);
goto fail;
}
if (!(s_nan_ctx.event & NDP_INDICATION)) { //INDICATION of specific peer
ESP_LOGE(TAG, "Need NDP Indication before NDP Response can be sent");
goto fail;
}
if (!MACADDR_EQUAL(resp->peer_mac, null_mac)) {
MACADDR_COPY(resp->peer_mac, ndl->peer_nmi);
}
if (esp_nan_internal_datapath_resp(resp) == ESP_OK) {
s_nan_ctx.event &= ~NDP_INDICATION;
NAN_DATA_UNLOCK();
return ESP_OK;
}
fail:
NAN_DATA_UNLOCK();
return ESP_FAIL;
}
esp_err_t esp_wifi_nan_datapath_end(wifi_nan_datapath_end_req_t *req)
{
struct ndl_info *ndl = NULL;
NAN_DATA_LOCK();
if (!nan_is_datapath_active()) {
ESP_LOGE(TAG, "No Datapath active");
NAN_DATA_UNLOCK();
return ESP_FAIL;
}
ndl = nan_find_ndl(req->ndp_id, NULL);
if (!ndl) {
ESP_LOGE(TAG, "No NDL with ndp id %d", req->ndp_id);
NAN_DATA_UNLOCK();
return ESP_FAIL;
}
if (!MACADDR_EQUAL(req->peer_mac, null_mac)) {
MACADDR_COPY(req->peer_mac, ndl->peer_nmi);
}
NAN_DATA_UNLOCK();
if (esp_nan_internal_datapath_end(req) == ESP_OK) {
return ESP_OK;
}
return ESP_FAIL;
}
esp_err_t esp_wifi_nan_get_own_svc_info(uint8_t *own_svc_id, char *svc_name, int *num_peer_records)
{
struct own_svc_info *own_svc = NULL;
if (!own_svc_id || !num_peer_records || !svc_name) {
ESP_LOGE(TAG, "NULL memory address for input parameters");
return ESP_FAIL;
}
NAN_DATA_LOCK();
if (*own_svc_id == 0) {
own_svc = nan_find_own_svc_by_name(svc_name);
if (!own_svc) {
ESP_LOGE(TAG, "No record found for given service name %s", svc_name);
goto fail;
}
*own_svc_id = own_svc->svc_id;
} else {
own_svc = nan_find_own_svc(*own_svc_id);
if (!own_svc) {
ESP_LOGE(TAG, "No record found for given service ID %d", *own_svc_id);
goto fail;
}
strlcpy(svc_name, own_svc->svc_name, ESP_WIFI_MAX_SVC_NAME_LEN);
}
*num_peer_records = own_svc->num_peer_records;
NAN_DATA_UNLOCK();
return ESP_OK;
fail:
NAN_DATA_UNLOCK();
return ESP_FAIL;
}
esp_err_t esp_wifi_nan_get_peer_records(int *num_peer_records, uint8_t own_svc_id, struct nan_peer_record *peer_record)
{
struct own_svc_info *own_record = NULL;
struct peer_svc_info *temp = NULL;
int peer_num = 0;
if (!peer_record || !num_peer_records) {
ESP_LOGE(TAG, "NULL memory address for input parameters");
return ESP_FAIL;
}
if (own_svc_id == 0) {
ESP_LOGE(TAG, "Invalid service ID");
return ESP_FAIL;
}
if (*num_peer_records == 0) {
ESP_LOGE(TAG, "Number of peer records provided is 0");
return ESP_FAIL;
}
NAN_DATA_LOCK();
own_record = nan_find_own_svc(own_svc_id);
if (own_record) {
SLIST_FOREACH(temp, &(own_record->peer_list), next) {
struct ndl_info *p_ndl;
peer_record[peer_num].peer_svc_id = temp->svc_id;
peer_record[peer_num].own_svc_id = own_svc_id;
peer_record[peer_num].peer_svc_type = temp->type;
MACADDR_COPY(peer_record[peer_num].peer_nmi, temp->peer_nmi);
p_ndl = nan_find_ndl(0, temp->peer_nmi);
if (p_ndl) {
if (p_ndl->own_role == ESP_NAN_PUBLISH) {
if (p_ndl->publisher_id == own_svc_id) {
peer_record[peer_num].ndp_id = p_ndl->ndp_id;
MACADDR_COPY(peer_record[peer_num].peer_ndi, p_ndl->peer_ndi);
}
} else if (p_ndl->own_role == ESP_NAN_SUBSCRIBE) {
struct peer_svc_info *peer_info = NULL;
peer_info = nan_find_peer_svc(own_svc_id, temp->svc_id, temp->peer_nmi);
if (peer_info && peer_info->svc_id == p_ndl->publisher_id) {
peer_record[peer_num].ndp_id = p_ndl->ndp_id;
MACADDR_COPY(peer_record[peer_num].peer_ndi, p_ndl->peer_ndi);
}
}
} else {
peer_record[peer_num].ndp_id = 0;
MACADDR_COPY(peer_record[peer_num].peer_ndi, null_mac);
}
peer_num ++;
if (peer_num == *num_peer_records) {
break;
}
}
if (*num_peer_records > peer_num) {
*num_peer_records = peer_num;
}
NAN_DATA_UNLOCK();
return ESP_OK;
} else {
*num_peer_records = 0;
ESP_LOGD(TAG, "No record found for own service id %d", own_svc_id);
NAN_DATA_UNLOCK();
return ESP_FAIL;
}
}
esp_err_t esp_wifi_nan_get_peer_info(char *svc_name, uint8_t *peer_mac, struct nan_peer_record *peer_info)
{
struct peer_svc_info *peer_svc = NULL;
uint8_t own_svc_id = 0;
if (!peer_mac || !peer_info) {
ESP_LOGE(TAG, "Invalid memory address for input parameters");
return ESP_FAIL;
}
NAN_DATA_LOCK();
if (svc_name) {
struct own_svc_info *own_svc = nan_find_own_svc_by_name(svc_name);
if (!own_svc) {
ESP_LOGE(TAG, "No record found for given service name %s", svc_name);
NAN_DATA_UNLOCK();
return ESP_FAIL;
}
own_svc_id = own_svc->svc_id;
}
peer_svc = nan_find_peer_svc(own_svc_id, 0, peer_mac);
if (peer_svc) {
struct ndl_info *p_ndl;
peer_info->peer_svc_id = peer_svc->svc_id;
peer_info->own_svc_id = peer_svc->own_svc_id;
peer_info->peer_svc_type = peer_svc->type;
MACADDR_COPY(peer_info->peer_nmi, peer_mac);
p_ndl = nan_find_ndl(0, peer_mac);
if (p_ndl) {
peer_info->ndp_id = p_ndl->ndp_id;
MACADDR_COPY(peer_info->peer_ndi, p_ndl->peer_ndi);
} else {
peer_info->ndp_id = 0;
MACADDR_COPY(peer_info->peer_ndi, null_mac);
}
NAN_DATA_UNLOCK();
return ESP_OK;
} else {
ESP_LOGD(TAG, "No record found for Peer "MACSTR, MAC2STR(peer_mac));
NAN_DATA_UNLOCK();
return ESP_FAIL;
}
}