/*
 * Copyright (c) 2020 Espressif Systems (Shanghai) Co., Ltd.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT espressif_esp32_wifi

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(esp32_wifi, CONFIG_WIFI_LOG_LEVEL);

#include <zephyr/net/ethernet.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/wifi_mgmt.h>
#include <zephyr/net/conn_mgr/connectivity_wifi_mgmt.h>
#include <zephyr/device.h>
#include <soc.h>
#include "esp_private/wifi.h"
#include "esp_event.h"
#include "esp_timer.h"
#include "esp_system.h"
#include "esp_wpa.h"
#include <esp_mac.h>
#include "wifi/wifi_event.h"

#define DHCPV4_MASK (NET_EVENT_IPV4_DHCP_BOUND | NET_EVENT_IPV4_DHCP_STOP)

/* use global iface pointer to support any ethernet driver */
/* necessary for wifi callback functions */
static struct net_if *esp32_wifi_iface;
static struct esp32_wifi_runtime esp32_data;

enum esp32_state_flag {
	ESP32_STA_STOPPED,
	ESP32_STA_STARTED,
	ESP32_STA_CONNECTING,
	ESP32_STA_CONNECTED,
	ESP32_AP_CONNECTED,
	ESP32_AP_DISCONNECTED,
	ESP32_AP_STOPPED,
};

struct esp32_wifi_status {
	char ssid[WIFI_SSID_MAX_LEN + 1];
	char pass[WIFI_PSK_MAX_LEN + 1];
	wifi_auth_mode_t security;
	bool connected;
	uint8_t channel;
	int rssi;
};

struct esp32_wifi_runtime {
	uint8_t mac_addr[6];
	uint8_t frame_buf[NET_ETH_MAX_FRAME_SIZE];
#if defined(CONFIG_NET_STATISTICS_WIFI)
	struct net_stats_wifi stats;
#endif
	struct esp32_wifi_status status;
	scan_result_cb_t scan_cb;
	uint8_t state;
	uint8_t ap_connection_cnt;
};

static struct net_mgmt_event_callback esp32_dhcp_cb;

static void wifi_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event,
			       struct net_if *iface)
{
	const struct wifi_status *status = (const struct wifi_status *)cb->info;

	switch (mgmt_event) {
	case NET_EVENT_IPV4_DHCP_BOUND:
		wifi_mgmt_raise_connect_result_event(esp32_wifi_iface, 0);
		break;
	default:
		break;
	}
}

static int esp32_wifi_send(const struct device *dev, struct net_pkt *pkt)
{
	struct esp32_wifi_runtime *data = dev->data;
	const int pkt_len = net_pkt_get_len(pkt);
	esp_interface_t ifx =
		esp32_data.state == ESP32_AP_CONNECTED ? ESP_IF_WIFI_AP : ESP_IF_WIFI_STA;

	/* Read the packet payload */
	if (net_pkt_read(pkt, data->frame_buf, pkt_len) < 0) {
		goto out;
	}

	/* Enqueue packet for transmission */
	if (esp_wifi_internal_tx(ifx, (void *)data->frame_buf, pkt_len) != ESP_OK) {
		goto out;
	}

#if defined(CONFIG_NET_STATISTICS_WIFI)
	data->stats.bytes.sent += pkt_len;
	data->stats.pkts.tx++;
#endif

	LOG_DBG("pkt sent %p len %d", pkt, pkt_len);
	return 0;

out:

	LOG_ERR("Failed to send packet");
#if defined(CONFIG_NET_STATISTICS_WIFI)
	data->stats.errors.tx++;
#endif
	return -EIO;
}

static esp_err_t eth_esp32_rx(void *buffer, uint16_t len, void *eb)
{
	struct net_pkt *pkt;

	if (esp32_wifi_iface == NULL) {
		LOG_ERR("network interface unavailable");
		return -EIO;
	}

	pkt = net_pkt_rx_alloc_with_buffer(esp32_wifi_iface, len, AF_UNSPEC, 0, K_MSEC(100));
	if (!pkt) {
		LOG_ERR("Failed to get net buffer");
		return -EIO;
	}

	if (net_pkt_write(pkt, buffer, len) < 0) {
		LOG_ERR("Failed to write pkt");
		goto pkt_unref;
	}

	if (net_recv_data(esp32_wifi_iface, pkt) < 0) {
		LOG_ERR("Failed to push received data");
		goto pkt_unref;
	}

#if defined(CONFIG_NET_STATISTICS_WIFI)
	esp32_data.stats.bytes.received += len;
	esp32_data.stats.pkts.rx++;
#endif

	esp_wifi_internal_free_rx_buffer(eb);
	return 0;

pkt_unref:
	net_pkt_unref(pkt);

#if defined(CONFIG_NET_STATISTICS_WIFI)
	esp32_data.stats.errors.rx++;
#endif

	return -EIO;
}

static void scan_done_handler(void)
{
	uint16_t aps = 0;
	wifi_ap_record_t *ap_list_buffer;
	struct wifi_scan_result res = { 0 };

	esp_wifi_scan_get_ap_num(&aps);
	if (!aps) {
		LOG_INF("No Wi-Fi AP found");
		goto out;
	}

	ap_list_buffer = k_malloc(aps * sizeof(wifi_ap_record_t));
	if (ap_list_buffer == NULL) {
		LOG_INF("Failed to malloc buffer to print scan results");
		goto out;
	}

	if (esp_wifi_scan_get_ap_records(&aps, (wifi_ap_record_t *)ap_list_buffer) == ESP_OK) {
		for (int k = 0; k < aps; k++) {
			memset(&res, 0, sizeof(struct wifi_scan_result));
			int ssid_len = strnlen(ap_list_buffer[k].ssid, WIFI_SSID_MAX_LEN);

			res.ssid_length = ssid_len;
			strncpy(res.ssid, ap_list_buffer[k].ssid, ssid_len);
			res.rssi = ap_list_buffer[k].rssi;
			res.channel = ap_list_buffer[k].primary;
			switch (ap_list_buffer[k].authmode) {
			case WIFI_AUTH_OPEN:
				res.security = WIFI_SECURITY_TYPE_NONE;
				break;
			case WIFI_AUTH_WPA2_PSK:
				res.security = WIFI_SECURITY_TYPE_PSK;
				break;
			case WIFI_AUTH_WPA3_PSK:
				res.security = WIFI_SECURITY_TYPE_SAE;
				break;
			case WIFI_AUTH_WAPI_PSK:
				res.security = WIFI_SECURITY_TYPE_WAPI;
				break;
			case WIFI_AUTH_WPA2_ENTERPRISE:
				res.security = WIFI_SECURITY_TYPE_EAP;
				break;
			case WIFI_AUTH_WEP:
				res.security = WIFI_SECURITY_TYPE_WEP;
				break;
			case WIFI_AUTH_WPA_PSK:
				res.security = WIFI_SECURITY_TYPE_WPA_PSK;
				break;
			default:
				res.security = WIFI_SECURITY_TYPE_UNKNOWN;
				break;
			}

			if (esp32_data.scan_cb) {
				esp32_data.scan_cb(esp32_wifi_iface, 0, &res);

				/* ensure notifications get delivered */
				k_yield();
			}
		}
	} else {
		LOG_INF("Unable to retrieve AP records");
	}

	k_free(ap_list_buffer);

out:
	/* report end of scan event */
	esp32_data.scan_cb(esp32_wifi_iface, 0, NULL);
	esp32_data.scan_cb = NULL;
}

static void esp_wifi_handle_sta_connect_event(void *event_data)
{
	ARG_UNUSED(event_data);
	esp32_data.state = ESP32_STA_CONNECTED;
#if defined(CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4)
	net_dhcpv4_start(esp32_wifi_iface);
#else
	wifi_mgmt_raise_connect_result_event(esp32_wifi_iface, 0);
#endif
}

static void esp_wifi_handle_sta_disconnect_event(void *event_data)
{
	wifi_event_sta_disconnected_t *event = (wifi_event_sta_disconnected_t *)event_data;

	if (esp32_data.state == ESP32_STA_CONNECTED) {
#if defined(CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4)
		net_dhcpv4_stop(esp32_wifi_iface);
#endif
		wifi_mgmt_raise_disconnect_result_event(esp32_wifi_iface, 0);
	} else {
		wifi_mgmt_raise_disconnect_result_event(esp32_wifi_iface, -1);
	}

	LOG_DBG("Disconnect reason: %d", event->reason);
	switch (event->reason) {
	case WIFI_REASON_AUTH_EXPIRE:
	case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT:
	case WIFI_REASON_AUTH_FAIL:
	case WIFI_REASON_HANDSHAKE_TIMEOUT:
	case WIFI_REASON_MIC_FAILURE:
		LOG_DBG("STA Auth Error");
		break;
	case WIFI_REASON_NO_AP_FOUND:
		LOG_DBG("AP Not found");
		break;
	default:
		break;
	}

	if (IS_ENABLED(CONFIG_ESP32_WIFI_STA_RECONNECT) &&
	    (event->reason != WIFI_REASON_ASSOC_LEAVE)) {
		esp32_data.state = ESP32_STA_CONNECTING;
		esp_wifi_connect();
	} else {
		esp32_data.state = ESP32_STA_STARTED;
	}
}

static void esp_wifi_handle_ap_connect_event(void *event_data)
{
	wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *) event_data;

	LOG_DBG("Station " MACSTR " join, AID=%d", MAC2STR(event->mac), event->aid);
	wifi_mgmt_raise_connect_result_event(esp32_wifi_iface, 0);

	if (!(esp32_data.ap_connection_cnt++)) {
		esp_wifi_internal_reg_rxcb(WIFI_IF_AP, eth_esp32_rx);
	}
}

static void esp_wifi_handle_ap_disconnect_event(void *event_data)
{
	wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data;

	LOG_DBG("station "MACSTR" leave, AID=%d", MAC2STR(event->mac), event->aid);
	wifi_mgmt_raise_disconnect_result_event(esp32_wifi_iface, 0);

	if (!(--esp32_data.ap_connection_cnt)) {
		esp_wifi_internal_reg_rxcb(WIFI_IF_AP, NULL);
	}
}

void esp_wifi_event_handler(const char *event_base, int32_t event_id, void *event_data,
			    size_t event_data_size, uint32_t ticks_to_wait)
{
	LOG_DBG("Wi-Fi event: %d", event_id);
	switch (event_id) {
	case WIFI_EVENT_STA_START:
		esp32_data.state = ESP32_STA_STARTED;
		net_eth_carrier_on(esp32_wifi_iface);
		break;
	case WIFI_EVENT_STA_STOP:
		esp32_data.state = ESP32_STA_STOPPED;
		net_eth_carrier_off(esp32_wifi_iface);
		break;
	case WIFI_EVENT_STA_CONNECTED:
		esp_wifi_handle_sta_connect_event(event_data);
		break;
	case WIFI_EVENT_STA_DISCONNECTED:
		esp_wifi_handle_sta_disconnect_event(event_data);
		break;
	case WIFI_EVENT_SCAN_DONE:
		scan_done_handler();
		break;
	case WIFI_EVENT_AP_STOP:
		esp32_data.state = ESP32_AP_STOPPED;
		net_eth_carrier_off(esp32_wifi_iface);
		break;
	case WIFI_EVENT_AP_STACONNECTED:
		esp32_data.state = ESP32_AP_CONNECTED;
		esp_wifi_handle_ap_connect_event(event_data);
		break;
	case WIFI_EVENT_AP_STADISCONNECTED:
		esp32_data.state = ESP32_AP_DISCONNECTED;
		esp_wifi_handle_ap_disconnect_event(event_data);
		break;
	default:
		break;
	}
}

static int esp32_wifi_disconnect(const struct device *dev)
{
	struct esp32_wifi_runtime *data = dev->data;
	int ret = esp_wifi_disconnect();

	if (ret != ESP_OK) {
		LOG_INF("Failed to disconnect from hotspot");
		return -EAGAIN;
	}

	return 0;
}

static int esp32_wifi_connect(const struct device *dev,
			    struct wifi_connect_req_params *params)
{
	struct esp32_wifi_runtime *data = dev->data;
	wifi_mode_t mode;
	int ret;

	if (data->state == ESP32_STA_CONNECTING || data->state == ESP32_STA_CONNECTED) {
		wifi_mgmt_raise_connect_result_event(esp32_wifi_iface, -1);
		return -EALREADY;
	}

	ret = esp_wifi_get_mode(&mode);
	if (ret) {
		LOG_ERR("Failed to get Wi-Fi mode (%d)", ret);
		return -EAGAIN;
	}

	if (mode != ESP32_WIFI_MODE_STA) {
		ret = esp_wifi_set_mode(ESP32_WIFI_MODE_STA);
		if (ret) {
			LOG_ERR("Failed to set Wi-Fi mode (%d)", ret);
			return -EAGAIN;
		}
		ret = esp_wifi_start();
		if (ret) {
			LOG_ERR("Failed to start Wi-Fi driver (%d)", ret);
			return -EAGAIN;
		}
	}

	if (data->state != ESP32_STA_STARTED) {
		LOG_ERR("Wi-Fi not in station mode");
		wifi_mgmt_raise_connect_result_event(esp32_wifi_iface, -1);
		return -EIO;
	}

	data->state = ESP32_STA_CONNECTING;

	memcpy(data->status.ssid, params->ssid, params->ssid_length);
	data->status.ssid[params->ssid_length] = '\0';

	wifi_config_t wifi_config;

	memset(&wifi_config, 0, sizeof(wifi_config_t));

	memcpy(wifi_config.sta.ssid, params->ssid, params->ssid_length);
	wifi_config.sta.ssid[params->ssid_length] = '\0';
	switch (params->security) {
	case WIFI_SECURITY_TYPE_NONE:
		wifi_config.sta.threshold.authmode = WIFI_AUTH_OPEN;
		data->status.security = WIFI_AUTH_OPEN;
		wifi_config.sta.pmf_cfg.required = false;
		break;
	case WIFI_SECURITY_TYPE_PSK:
		memcpy(wifi_config.sta.password, params->psk, params->psk_length);
		wifi_config.sta.password[params->psk_length] = '\0';
		wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
		wifi_config.sta.pmf_cfg.required = false;
		data->status.security = WIFI_AUTH_WPA2_PSK;
		break;
	case WIFI_SECURITY_TYPE_SAE:
#if defined(CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE)
		if (params->sae_password) {
			memcpy(wifi_config.sta.password, params->sae_password,
			       params->sae_password_length);
			wifi_config.sta.password[params->sae_password_length] = '\0';
		} else {
			memcpy(wifi_config.sta.password, params->psk, params->psk_length);
			wifi_config.sta.password[params->psk_length] = '\0';
		}
		data->status.security = WIFI_AUTH_WPA3_PSK;
		wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA3_PSK;
		wifi_config.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH;
		break;
#else
		LOG_ERR("WPA3 not supported for STA mode. Enable "
			"CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE");
		return -EINVAL;
#endif /* CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE */
	default:
		LOG_ERR("Authentication method not supported");
		return -EIO;
	}

	if (params->channel == WIFI_CHANNEL_ANY) {
		wifi_config.sta.channel = 0U;
		data->status.channel = 0U;
	} else {
		wifi_config.sta.channel = params->channel;
		data->status.channel = params->channel;
	}

	ret = esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);
	if (ret) {
		LOG_ERR("Failed to set Wi-Fi configuration (%d)", ret);
		return -EINVAL;
	}

	ret = esp_wifi_connect();
	if (ret) {
		LOG_ERR("Failed to connect to Wi-Fi access point (%d)", ret);
		return -EAGAIN;
	}

	return 0;
}

static int esp32_wifi_scan(const struct device *dev,
			   struct wifi_scan_params *params,
			   scan_result_cb_t cb)
{
	struct esp32_wifi_runtime *data = dev->data;
	int ret = 0;

	if (data->scan_cb != NULL) {
		LOG_INF("Scan callback in progress");
		return -EINPROGRESS;
	}

	data->scan_cb = cb;

	wifi_scan_config_t scan_config = { 0 };

	if (params) {
		/* The enum values are same, so, no conversion needed */
		scan_config.scan_type = params->scan_type;
	}

	ret = esp_wifi_set_mode(ESP32_WIFI_MODE_STA);
	ret |= esp_wifi_scan_start(&scan_config, false);

	if (ret != ESP_OK) {
		LOG_ERR("Failed to start Wi-Fi scanning");
		return -EAGAIN;
	}

	return 0;
};

static int esp32_wifi_ap_enable(const struct device *dev,
			 struct wifi_connect_req_params *params)
{
	struct esp32_wifi_runtime *data = dev->data;
	esp_err_t err = 0;

	/* Build Wi-Fi configuration for AP mode */
	wifi_config_t wifi_config = {
		.ap = {
			.max_connection = 5,
			.channel = params->channel == WIFI_CHANNEL_ANY ?
				0 : params->channel,
		},
	};

	memcpy(data->status.ssid, params->ssid, params->ssid_length);
	data->status.ssid[params->ssid_length] = '\0';

	strncpy((char *) wifi_config.ap.ssid, params->ssid, params->ssid_length);
	wifi_config.ap.ssid_len = params->ssid_length;

	switch (params->security) {
	case WIFI_SECURITY_TYPE_NONE:
		memset(wifi_config.ap.password, 0, sizeof(wifi_config.ap.password));
		wifi_config.ap.authmode = WIFI_AUTH_OPEN;
		data->status.security = WIFI_AUTH_OPEN;
		wifi_config.ap.pmf_cfg.required = false;
		break;
	case WIFI_SECURITY_TYPE_PSK:
		strncpy((char *) wifi_config.ap.password, params->psk, params->psk_length);
		wifi_config.ap.authmode = WIFI_AUTH_WPA2_PSK;
		data->status.security = WIFI_AUTH_WPA2_PSK;
		wifi_config.ap.pmf_cfg.required = false;
		break;
	default:
		LOG_ERR("Authentication method not supported");
		return -EINVAL;
	}

	/* Start Wi-Fi in AP mode with configuration built above */
	err = esp_wifi_set_mode(ESP32_WIFI_MODE_AP);
	if (err) {
		LOG_ERR("Failed to set Wi-Fi mode (%d)", err);
		return -EINVAL;
	}

	err = esp_wifi_set_config(WIFI_IF_AP, &wifi_config);
	if (err) {
		LOG_ERR("Failed to set Wi-Fi configuration (%d)", err);
		return -EINVAL;
	}

	err = esp_wifi_start();
	if (err) {
		LOG_ERR("Failed to enable Wi-Fi AP mode");
		return -EAGAIN;
	}

	net_eth_carrier_on(esp32_wifi_iface);

	return 0;
};

static int esp32_wifi_ap_disable(const struct device *dev)
{
	int err = 0;

	err = esp_wifi_stop();
	if (err) {
		LOG_ERR("Failed to disable Wi-Fi AP mode: (%d)", err);
		return -EAGAIN;
	}

	return 0;
};

static int esp32_wifi_status(const struct device *dev, struct wifi_iface_status *status)
{
	struct esp32_wifi_runtime *data = dev->data;
	wifi_mode_t mode;
	wifi_config_t conf;
	wifi_ap_record_t ap_info;

	switch (data->state) {
	case ESP32_STA_STOPPED:
	case ESP32_AP_STOPPED:
		status->state = WIFI_STATE_INACTIVE;
		break;
	case ESP32_STA_STARTED:
	case ESP32_AP_DISCONNECTED:
		status->state = WIFI_STATE_DISCONNECTED;
		break;
	case ESP32_STA_CONNECTING:
		status->state = WIFI_STATE_SCANNING;
		break;
	case ESP32_STA_CONNECTED:
	case ESP32_AP_CONNECTED:
		status->state = WIFI_STATE_COMPLETED;
		break;
	default:
		break;
	}

	strncpy(status->ssid, data->status.ssid, WIFI_SSID_MAX_LEN);
	status->ssid_len = strnlen(data->status.ssid, WIFI_SSID_MAX_LEN);
	status->band = WIFI_FREQ_BAND_2_4_GHZ;
	status->link_mode = WIFI_LINK_MODE_UNKNOWN;
	status->mfp = WIFI_MFP_DISABLE;

	if (esp_wifi_get_mode(&mode) == ESP_OK) {
		if (mode == ESP32_WIFI_MODE_STA) {
			esp_wifi_get_config(ESP_IF_WIFI_STA, &conf);
			esp_wifi_sta_get_ap_info(&ap_info);

			status->iface_mode = WIFI_MODE_INFRA;
			status->channel = ap_info.primary;
			status->rssi = ap_info.rssi;
			memcpy(status->bssid, ap_info.bssid, WIFI_MAC_ADDR_LEN);

			if (ap_info.phy_11b) {
				status->link_mode = WIFI_1;
			} else if (ap_info.phy_11g) {
				status->link_mode = WIFI_3;
			} else if (ap_info.phy_11n) {
				status->link_mode = WIFI_4;
			} else if (ap_info.phy_11ax) {
				status->link_mode = WIFI_6;
			}

			status->beacon_interval = conf.sta.listen_interval;
		} else if (mode == ESP32_WIFI_MODE_AP) {
			esp_wifi_get_config(ESP_IF_WIFI_AP, &conf);
			status->iface_mode = WIFI_MODE_AP;
			status->link_mode = WIFI_LINK_MODE_UNKNOWN;
			status->channel = conf.ap.channel;
			status->beacon_interval = conf.ap.beacon_interval;

		} else {
			status->iface_mode = WIFI_MODE_UNKNOWN;
			status->link_mode = WIFI_LINK_MODE_UNKNOWN;
			status->channel = 0;
		}
	}

	switch (data->status.security) {
	case WIFI_AUTH_OPEN:
		status->security = WIFI_SECURITY_TYPE_NONE;
		break;
	case WIFI_AUTH_WPA2_PSK:
		status->security = WIFI_SECURITY_TYPE_PSK;
		break;
	case WIFI_AUTH_WPA3_PSK:
		status->security = WIFI_SECURITY_TYPE_SAE;
		break;
	default:
		status->security = WIFI_SECURITY_TYPE_UNKNOWN;
	}

	return 0;
}

static void esp32_wifi_init(struct net_if *iface)
{
	const struct device *dev = net_if_get_device(iface);
	struct esp32_wifi_runtime *dev_data = dev->data;
	struct ethernet_context *eth_ctx = net_if_l2_data(iface);

	eth_ctx->eth_if_type = L2_ETH_IF_TYPE_WIFI;
	esp32_wifi_iface = iface;
	dev_data->state = ESP32_STA_STOPPED;

	/* Start interface when we are actually connected with Wi-Fi network */
	esp_read_mac(dev_data->mac_addr, ESP_MAC_WIFI_STA);

	/* Assign link local address. */
	net_if_set_link_addr(iface, dev_data->mac_addr, 6, NET_LINK_ETHERNET);

	ethernet_init(iface);
	net_if_carrier_off(iface);

	wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT();

	esp_err_t ret = esp_wifi_init(&config);

	esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, eth_esp32_rx);

	ret |= esp_wifi_set_mode(ESP32_WIFI_MODE_STA);
	ret |= esp_wifi_start();

	if (ret != ESP_OK) {
		LOG_ERR("Failed to start Wi-Fi driver");
	}
}

#if defined(CONFIG_NET_STATISTICS_WIFI)
static int esp32_wifi_stats(const struct device *dev, struct net_stats_wifi *stats)
{
	struct esp32_wifi_runtime *data = dev->data;

	stats->bytes.received = data->stats.bytes.received;
	stats->bytes.sent = data->stats.bytes.sent;
	stats->pkts.rx = data->stats.pkts.rx;
	stats->pkts.tx = data->stats.pkts.tx;
	stats->errors.rx = data->stats.errors.rx;
	stats->errors.tx = data->stats.errors.tx;
	stats->broadcast.rx = data->stats.broadcast.rx;
	stats->broadcast.tx = data->stats.broadcast.tx;
	stats->multicast.rx = data->stats.multicast.rx;
	stats->multicast.tx = data->stats.multicast.tx;
	stats->sta_mgmt.beacons_rx = data->stats.sta_mgmt.beacons_rx;
	stats->sta_mgmt.beacons_miss = data->stats.sta_mgmt.beacons_miss;

	return 0;
}
#endif

static int esp32_wifi_dev_init(const struct device *dev)
{
	esp_timer_init();

	if (IS_ENABLED(CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4)) {
		net_mgmt_init_event_callback(&esp32_dhcp_cb, wifi_event_handler, DHCPV4_MASK);
		net_mgmt_add_event_callback(&esp32_dhcp_cb);
	}

	return 0;
}

static const struct wifi_mgmt_ops esp32_wifi_mgmt = {
	.scan		   = esp32_wifi_scan,
	.connect	   = esp32_wifi_connect,
	.disconnect	   = esp32_wifi_disconnect,
	.ap_enable	   = esp32_wifi_ap_enable,
	.ap_disable	   = esp32_wifi_ap_disable,
	.iface_status	   = esp32_wifi_status,
#if defined(CONFIG_NET_STATISTICS_WIFI)
	.get_stats	   = esp32_wifi_stats,
#endif
};

static const struct net_wifi_mgmt_offload esp32_api = {
	.wifi_iface.iface_api.init	  = esp32_wifi_init,
	.wifi_iface.send = esp32_wifi_send,
	.wifi_mgmt_api = &esp32_wifi_mgmt,
};

NET_DEVICE_DT_INST_DEFINE(0,
		esp32_wifi_dev_init, NULL,
		&esp32_data, NULL, CONFIG_WIFI_INIT_PRIORITY,
		&esp32_api, ETHERNET_L2,
		NET_L2_GET_CTX_TYPE(ETHERNET_L2), NET_ETH_MTU);

CONNECTIVITY_WIFI_MGMT_BIND(Z_DEVICE_DT_DEV_ID(DT_DRV_INST(0)));
