/**
 * Copyright 2023-2024 NXP
 * SPDX-License-Identifier: Apache-2.0
 *
 * @file nxp_wifi_drv.c
 * Shim layer between wifi driver connection manager and zephyr
 * Wi-Fi L2 layer
 */

#define DT_DRV_COMPAT nxp_wifi

#include <zephyr/net/ethernet.h>
#include <zephyr/net/dns_resolve.h>
#include <zephyr/device.h>
#include <soc.h>
#include <ethernet/eth_stats.h>
#include <zephyr/logging/log.h>

#include <zephyr/net/net_if.h>
#include <zephyr/net/wifi_mgmt.h>
#ifdef CONFIG_PM_DEVICE
#include <zephyr/pm/device.h>
#endif
#ifdef CONFIG_WIFI_NM
#include <zephyr/net/wifi_nm.h>
#endif

LOG_MODULE_REGISTER(nxp_wifi, CONFIG_WIFI_LOG_LEVEL);

#include "nxp_wifi_drv.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#ifdef CONFIG_NXP_WIFI_TC_RELOCATE
#define NXP_WIFI_SET_FUNC_ATTR __ramfunc
#else
#define NXP_WIFI_SET_FUNC_ATTR
#endif

#ifdef CONFIG_NXP_RW610
#define IMU_IRQ_N        DT_INST_IRQ_BY_IDX(0, 0, irq)
#define IMU_IRQ_P        DT_INST_IRQ_BY_IDX(0, 0, priority)
#define IMU_WAKEUP_IRQ_N DT_INST_IRQ_BY_IDX(0, 1, irq)
#define IMU_WAKEUP_IRQ_P DT_INST_IRQ_BY_IDX(0, 1, priority)
#endif
/*******************************************************************************
 * Variables
 ******************************************************************************/
static int s_nxp_wifi_State = NXP_WIFI_NOT_INITIALIZED;
static bool s_nxp_wifi_StaConnected;
static bool s_nxp_wifi_UapActivated;
static struct k_event s_nxp_wifi_SyncEvent;

static struct nxp_wifi_dev nxp_wifi0; /* static instance */

static struct wlan_network nxp_wlan_network;

#ifndef CONFIG_WIFI_NM_HOSTAPD_AP
static char uap_ssid[IEEEtypes_SSID_SIZE + 1];
#endif

extern struct interface g_mlan;
#ifdef CONFIG_NXP_WIFI_SOFTAP_SUPPORT
extern struct interface g_uap;
#endif
#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT
extern const rtos_wpa_supp_dev_ops wpa_supp_ops;
#endif

#if defined(CONFIG_PM_DEVICE) && defined(CONFIG_NXP_RW610)
extern int is_hs_handshake_done;
extern int wlan_host_sleep_state;
#endif

static int nxp_wifi_recv(struct net_if *iface, struct net_pkt *pkt);

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
#ifdef CONFIG_NXP_WIFI_STA_AUTO_CONN
static void nxp_wifi_auto_connect(void);
#endif

/* Callback Function passed to WLAN Connection Manager. The callback function
 * gets called when there are WLAN Events that need to be handled by the
 * application.
 */
int nxp_wifi_wlan_event_callback(enum wlan_event_reason reason, void *data)
{
	int ret;
#ifndef CONFIG_WIFI_NM_WPA_SUPPLICANT
	struct wlan_ip_config addr;
	char ssid[IEEEtypes_SSID_SIZE + 1];
	char ip[16];
#endif
	static int auth_fail;
#ifdef CONFIG_NXP_WIFI_SOFTAP_SUPPORT
	wlan_uap_client_disassoc_t *disassoc_resp = data;
	struct in_addr dhcps_addr4;
	struct in_addr base_addr;
	struct in_addr netmask_addr;
#endif
	struct wifi_iface_status status = { 0 };
	struct wifi_ap_sta_info ap_sta_info = { 0 };

	LOG_DBG("WLAN: received event %d", reason);

	if (s_nxp_wifi_State >= NXP_WIFI_INITIALIZED) {
		/* Do not replace the current set of events  */
		k_event_post(&s_nxp_wifi_SyncEvent, NXP_WIFI_EVENT_BIT(reason));
	}

	switch (reason) {
	case WLAN_REASON_INITIALIZED:
		LOG_DBG("WLAN initialized");

#ifdef CONFIG_NET_INTERFACE_NAME
		ret = net_if_set_name(g_mlan.netif, "ml");
		if (ret < 0) {
			LOG_ERR("Failed to set STA nxp_wlan_network interface name");
			return 0;
		}
#ifdef CONFIG_NXP_WIFI_SOFTAP_SUPPORT
		ret = net_if_set_name(g_uap.netif, "ua");
		if (ret < 0) {
			LOG_ERR("Failed to set uAP nxp_wlan_network interface name");
			return 0;
		}
#endif
#endif

#ifdef CONFIG_NXP_WIFI_STA_AUTO_CONN
		nxp_wifi_auto_connect();
#endif
		break;
	case WLAN_REASON_INITIALIZATION_FAILED:
		LOG_ERR("WLAN: initialization failed");
		break;
	case WLAN_REASON_AUTH_SUCCESS:
		LOG_DBG("WLAN: authenticated to nxp_wlan_network");
		break;
	case WLAN_REASON_ASSOC_SUCCESS:
		net_if_dormant_off(g_mlan.netif);
		LOG_DBG("WLAN: associated to nxp_wlan_network");
		break;
	case WLAN_REASON_SUCCESS:
		LOG_DBG("WLAN: connected to nxp_wlan_network");
#ifndef CONFIG_WIFI_NM_WPA_SUPPLICANT
		ret = wlan_get_address(&addr);
		if (ret != WM_SUCCESS) {
			LOG_ERR("failed to get IP address");
			return 0;
		}

		net_inet_ntoa(addr.ipv4.address, ip);

		ret = wlan_get_current_network_ssid(ssid);
		if (ret != WM_SUCCESS) {
			LOG_ERR("Failed to get External AP nxp_wlan_network ssid");
			return 0;
		}

		LOG_DBG("Connected to following BSS:");
		LOG_DBG("SSID = [%s]", ssid);
		if (addr.ipv4.address != 0U) {
			LOG_DBG("IPv4 Address: [%s]", ip);
		}
#ifdef CONFIG_NXP_WIFI_IPV6
		ARRAY_FOR_EACH(addr.ipv6, i) {
			if ((addr.ipv6[i].addr_state == NET_ADDR_TENTATIVE) ||
			    (addr.ipv6[i].addr_state == NET_ADDR_PREFERRED)) {
				LOG_DBG("IPv6 Address: %-13s:\t%s (%s)",
					ipv6_addr_type_to_desc(
						(struct net_ipv6_config *)&addr.ipv6[i]),
					ipv6_addr_addr_to_desc(
						(struct net_ipv6_config *)&addr.ipv6[i]),
					ipv6_addr_state_to_desc(addr.ipv6[i].addr_state));
			}
		}
		LOG_DBG("");
#endif
#endif
		auth_fail = 0;
		s_nxp_wifi_StaConnected = true;
		wifi_mgmt_raise_connect_result_event(g_mlan.netif, 0);
		break;
	case WLAN_REASON_CONNECT_FAILED:
		net_eth_carrier_off(g_mlan.netif);
		LOG_WRN("WLAN: connect failed");
		break;
	case WLAN_REASON_NETWORK_NOT_FOUND:
		net_eth_carrier_off(g_mlan.netif);
		LOG_WRN("WLAN: nxp_wlan_network not found");
		break;
	case WLAN_REASON_NETWORK_AUTH_FAILED:
		net_eth_carrier_off(g_mlan.netif);
		LOG_WRN("WLAN: nxp_wlan_network authentication failed");
		auth_fail++;
		if (auth_fail >= 3) {
			LOG_WRN("Authentication Failed. Disconnecting ... ");
			wlan_disconnect();
			auth_fail = 0;
		}
		break;
	case WLAN_REASON_ADDRESS_SUCCESS:
		LOG_DBG("wlan_network mgr: DHCP new lease");
		break;
	case WLAN_REASON_ADDRESS_FAILED:
		LOG_WRN("failed to obtain an IP address");
		break;
	case WLAN_REASON_USER_DISCONNECT:
		net_eth_carrier_off(g_mlan.netif);
		LOG_DBG("disconnected");
		auth_fail = 0;
		s_nxp_wifi_StaConnected = false;
		wifi_mgmt_raise_disconnect_result_event(g_mlan.netif, 0);
		break;
	case WLAN_REASON_LINK_LOST:
		net_if_dormant_on(g_mlan.netif);
		LOG_WRN("WLAN: link lost");
		break;
	case WLAN_REASON_DISCONNECTED:
		net_if_dormant_on(g_mlan.netif);
		LOG_DBG("WLAN: deauth leaving");
		break;
	case WLAN_REASON_CHAN_SWITCH:
		LOG_DBG("WLAN: channel switch");
		break;
#ifdef CONFIG_NXP_WIFI_SOFTAP_SUPPORT
	case WLAN_REASON_UAP_SUCCESS:
		net_eth_carrier_on(g_uap.netif);
		LOG_DBG("WLAN: UAP Started");
#ifndef CONFIG_WIFI_NM_HOSTAPD_AP
		ret = wlan_get_current_uap_network_ssid(uap_ssid);
		if (ret != WM_SUCCESS) {
			LOG_ERR("Failed to get Soft AP nxp_wlan_network ssid");
			return 0;
		}

		LOG_DBG("Soft AP \"%s\" started successfully", uap_ssid);
#endif
		if (net_addr_pton(AF_INET, CONFIG_NXP_WIFI_SOFTAP_IP_ADDRESS, &dhcps_addr4) < 0) {
			LOG_ERR("Invalid CONFIG_NXP_WIFI_SOFTAP_IP_ADDRESS");
			return 0;
		}
		net_if_ipv4_addr_add(g_uap.netif, &dhcps_addr4, NET_ADDR_MANUAL, 0);
		net_if_ipv4_set_gw(g_uap.netif, &dhcps_addr4);

		if (net_addr_pton(AF_INET, CONFIG_NXP_WIFI_SOFTAP_IP_MASK, &netmask_addr) < 0) {
			LOG_ERR("Invalid CONFIG_NXP_WIFI_SOFTAP_IP_MASK");
			return 0;
		}
		net_if_ipv4_set_netmask_by_addr(g_uap.netif, &dhcps_addr4, &netmask_addr);
		net_if_up(g_uap.netif);

		if (net_addr_pton(AF_INET, CONFIG_NXP_WIFI_SOFTAP_IP_BASE, &base_addr) < 0) {
			LOG_ERR("Invalid CONFIG_NXP_WIFI_SOFTAP_IP_BASE");
			return 0;
		}

		if (net_dhcpv4_server_start(g_uap.netif, &base_addr) < 0) {
			LOG_ERR("DHCP Server start failed");
			return 0;
		}

		LOG_DBG("DHCP Server started successfully");
		s_nxp_wifi_UapActivated = true;
		break;
	case WLAN_REASON_UAP_CLIENT_ASSOC:
		LOG_DBG("WLAN: UAP a Client Associated");
		LOG_DBG("Client => ");
		print_mac((const char *)data);
		LOG_DBG("Associated with Soft AP");
		break;
	case WLAN_REASON_UAP_CLIENT_CONN:
		wlan_get_current_uap_network(&nxp_wlan_network);
#ifdef CONFIG_NXP_WIFI_11AX
			if (nxp_wlan_network.dot11ax) {
				ap_sta_info.link_mode = WIFI_6;
			}
#endif
#ifdef CONFIG_NXP_WIFI_11AC
			else if (nxp_wlan_network.dot11ac) {
				ap_sta_info.link_mode = WIFI_5;
			}
#endif
			else if (nxp_wlan_network.dot11n) {
				ap_sta_info.link_mode = WIFI_4;
			} else {
				ap_sta_info.link_mode = WIFI_3;
			}

		memcpy(ap_sta_info.mac, data, WIFI_MAC_ADDR_LEN);
		ap_sta_info.mac_length  = WIFI_MAC_ADDR_LEN;
		ap_sta_info.twt_capable = status.twt_capable;

		wifi_mgmt_raise_ap_sta_connected_event(g_uap.netif, &ap_sta_info);
		LOG_DBG("WLAN: UAP a Client Connected");
		LOG_DBG("Client => ");
		print_mac((const char *)data);
		LOG_DBG("Connected with Soft AP");
		break;
	case WLAN_REASON_UAP_CLIENT_DISSOC:
		memcpy(ap_sta_info.mac, disassoc_resp->sta_addr, WIFI_MAC_ADDR_LEN);
		wifi_mgmt_raise_ap_sta_disconnected_event(g_uap.netif, &ap_sta_info);

		LOG_DBG("WLAN: UAP a Client Dissociated:");
		LOG_DBG(" Client MAC => ");
		print_mac((const char *)(disassoc_resp->sta_addr));
		LOG_DBG(" Reason code => ");
		LOG_DBG("%d", disassoc_resp->reason_code);
		break;
	case WLAN_REASON_UAP_STOPPED:
		net_eth_carrier_off(g_uap.netif);
		LOG_DBG("WLAN: UAP Stopped");

		net_dhcpv4_server_stop(g_uap.netif);
		LOG_DBG("DHCP Server stopped successfully");
		s_nxp_wifi_UapActivated = false;
		break;
#endif
	case WLAN_REASON_PS_ENTER:
		LOG_DBG("WLAN: PS_ENTER");
		break;
	case WLAN_REASON_PS_EXIT:
		LOG_DBG("WLAN: PS EXIT");
		break;
#ifdef CONFIG_NXP_WIFI_SUBSCRIBE_EVENT_SUPPORT
	case WLAN_REASON_RSSI_HIGH:
	case WLAN_REASON_SNR_LOW:
	case WLAN_REASON_SNR_HIGH:
	case WLAN_REASON_MAX_FAIL:
	case WLAN_REASON_BEACON_MISSED:
	case WLAN_REASON_DATA_RSSI_LOW:
	case WLAN_REASON_DATA_RSSI_HIGH:
	case WLAN_REASON_DATA_SNR_LOW:
	case WLAN_REASON_DATA_SNR_HIGH:
	case WLAN_REASON_LINK_QUALITY:
	case WLAN_REASON_PRE_BEACON_LOST:
		break;
#endif
	default:
		LOG_WRN("WLAN: Unknown Event: %d", reason);
	}
	return 0;
}

static int nxp_wifi_wlan_init(void)
{
	int status = NXP_WIFI_RET_SUCCESS;
	int ret;

	if (s_nxp_wifi_State != NXP_WIFI_NOT_INITIALIZED) {
		status = NXP_WIFI_RET_FAIL;
	}

	if (status == NXP_WIFI_RET_SUCCESS) {
		k_event_init(&s_nxp_wifi_SyncEvent);
	}

	if (status == NXP_WIFI_RET_SUCCESS) {
		ret = wlan_init(wlan_fw_bin, wlan_fw_bin_len);
		if (ret != WM_SUCCESS) {
			status = NXP_WIFI_RET_FAIL;
		}
	}

	if (status != NXP_WIFI_RET_SUCCESS) {
		return -1;
	}

	s_nxp_wifi_State = NXP_WIFI_INITIALIZED;

	nxp_wifi_internal_register_rx_cb(nxp_wifi_recv);

	return 0;
}

static int nxp_wifi_wlan_start(void)
{
	int status = NXP_WIFI_RET_SUCCESS;
	int ret;
	uint32_t syncBit;

	if (s_nxp_wifi_State != NXP_WIFI_INITIALIZED) {
		status = NXP_WIFI_RET_NOT_READY;
	}

	if (status == NXP_WIFI_RET_SUCCESS) {
		k_event_clear(&s_nxp_wifi_SyncEvent, NXP_WIFI_SYNC_INIT_GROUP);

		ret = wlan_start(nxp_wifi_wlan_event_callback);
		if (ret != WM_SUCCESS) {
			status = NXP_WIFI_RET_FAIL;
		}
	}

	if (status == NXP_WIFI_RET_SUCCESS) {
		syncBit = k_event_wait(&s_nxp_wifi_SyncEvent, NXP_WIFI_SYNC_INIT_GROUP, false,
				       NXP_WIFI_SYNC_TIMEOUT_MS);
		k_event_clear(&s_nxp_wifi_SyncEvent, NXP_WIFI_SYNC_INIT_GROUP);
		if (syncBit & NXP_WIFI_EVENT_BIT(WLAN_REASON_INITIALIZED)) {
			status = NXP_WIFI_RET_SUCCESS;
		} else if (syncBit & NXP_WIFI_EVENT_BIT(WLAN_REASON_INITIALIZATION_FAILED)) {
			status = NXP_WIFI_RET_FAIL;
		} else {
			status = NXP_WIFI_RET_TIMEOUT;
		}
	}

	if (status != NXP_WIFI_RET_SUCCESS) {
		return -1;
	}

	s_nxp_wifi_State = NXP_WIFI_STARTED;

	/* Initialize device as dormant */
	net_if_dormant_on(g_mlan.netif);

	/* L1 network layer (physical layer) is up */
	net_eth_carrier_on(g_mlan.netif);

	return 0;
}

#ifdef CONFIG_NXP_WIFI_SOFTAP_SUPPORT

static int nxp_wifi_start_ap(const struct device *dev, struct wifi_connect_req_params *params)
{
	int status = NXP_WIFI_RET_SUCCESS;
	int ret;
	struct interface *if_handle = (struct interface *)&g_uap;

	if (if_handle->state.interface != WLAN_BSS_TYPE_UAP) {
		LOG_ERR("Wi-Fi not in uAP mode");
		return -EIO;
	}

	if ((s_nxp_wifi_State != NXP_WIFI_STARTED) || (s_nxp_wifi_UapActivated != false)) {
		status = NXP_WIFI_RET_NOT_READY;
	}

	if (status == NXP_WIFI_RET_SUCCESS) {
		if ((params->ssid_length == 0) || (params->ssid_length > IEEEtypes_SSID_SIZE)) {
			status = NXP_WIFI_RET_BAD_PARAM;
		}
	}

	if (status == NXP_WIFI_RET_SUCCESS) {
		wlan_remove_network(nxp_wlan_network.name);

		wlan_initialize_uap_network(&nxp_wlan_network);

		memcpy(nxp_wlan_network.name, NXP_WIFI_UAP_NETWORK_NAME,
		       strlen(NXP_WIFI_UAP_NETWORK_NAME));

		memcpy(nxp_wlan_network.ssid, params->ssid, params->ssid_length);

		if (params->channel == WIFI_CHANNEL_ANY) {
			nxp_wlan_network.channel = 0;
		} else {
			nxp_wlan_network.channel = params->channel;
		}

		if (params->mfp == WIFI_MFP_REQUIRED) {
			nxp_wlan_network.security.mfpc = true;
			nxp_wlan_network.security.mfpr = true;
		} else if (params->mfp == WIFI_MFP_OPTIONAL) {
			nxp_wlan_network.security.mfpc = true;
			nxp_wlan_network.security.mfpr = false;
		}

		if (params->security == WIFI_SECURITY_TYPE_NONE) {
			nxp_wlan_network.security.type = WLAN_SECURITY_NONE;
		} else if (params->security == WIFI_SECURITY_TYPE_PSK) {
			nxp_wlan_network.security.type = WLAN_SECURITY_WPA2;
			nxp_wlan_network.security.psk_len = params->psk_length;
			strncpy(nxp_wlan_network.security.psk, params->psk, params->psk_length);
		}
#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT
		else if (params->security == WIFI_SECURITY_TYPE_PSK_SHA256) {
			nxp_wlan_network.security.type = WLAN_SECURITY_WPA2;
			nxp_wlan_network.security.key_mgmt |= WLAN_KEY_MGMT_PSK_SHA256;
			nxp_wlan_network.security.psk_len = params->psk_length;
			strncpy(nxp_wlan_network.security.psk, params->psk, params->psk_length);
		}
#endif
		else if (params->security == WIFI_SECURITY_TYPE_SAE) {
			nxp_wlan_network.security.type = WLAN_SECURITY_WPA3_SAE;
			nxp_wlan_network.security.password_len = params->psk_length;
			strncpy(nxp_wlan_network.security.password, params->psk,
				params->psk_length);
		} else {
			status = NXP_WIFI_RET_BAD_PARAM;
		}
	}

	if (status != NXP_WIFI_RET_SUCCESS) {
		LOG_ERR("Failed to enable Wi-Fi AP mode");
		return -EAGAIN;
	}

	ret = wlan_add_network(&nxp_wlan_network);
	if (ret != WM_SUCCESS) {
		status = NXP_WIFI_RET_FAIL;
	}

	ret = wlan_start_network(nxp_wlan_network.name);
	if (ret != WM_SUCCESS) {
		wlan_remove_network(nxp_wlan_network.name);
		status = NXP_WIFI_RET_FAIL;
	}

	return 0;
}

static int nxp_wifi_stop_ap(const struct device *dev)
{
	int status = NXP_WIFI_RET_SUCCESS;
	int ret;
	struct interface *if_handle = (struct interface *)&g_uap;

	if (if_handle->state.interface != WLAN_BSS_TYPE_UAP) {
		LOG_ERR("Wi-Fi not in uAP mode");
		return -EIO;
	}

	if ((s_nxp_wifi_State != NXP_WIFI_STARTED) || (s_nxp_wifi_UapActivated != true)) {
		status = NXP_WIFI_RET_NOT_READY;
	}

	if (status == NXP_WIFI_RET_SUCCESS) {
		ret = wlan_stop_network(NXP_WIFI_UAP_NETWORK_NAME);
		if (ret != WM_SUCCESS) {
			status = NXP_WIFI_RET_FAIL;
		}
	}

	if (status != NXP_WIFI_RET_SUCCESS) {
		LOG_ERR("Failed to disable Wi-Fi AP mode");
		return -EAGAIN;
	}

	return 0;
}

static int nxp_wifi_ap_config_params(const struct device *dev, struct wifi_ap_config_params *params)
{
	int status = NXP_WIFI_RET_SUCCESS;
	int ret = WM_SUCCESS;
	interface_t *if_handle = (interface_t *)dev->data;

	if (if_handle->state.interface != WLAN_BSS_TYPE_UAP) {
		LOG_ERR("Wi-Fi not in uAP mode");
		return -EIO;
	}

	if (s_nxp_wifi_State != NXP_WIFI_STARTED) {
		status = NXP_WIFI_RET_NOT_READY;
	}

	if (status == NXP_WIFI_RET_SUCCESS) {
		if (params->type & WIFI_AP_CONFIG_PARAM_MAX_INACTIVITY) {
			ret = wlan_uap_set_sta_ageout_timer(params->max_inactivity * 10);
			if (ret != WM_SUCCESS) {
				status = NXP_WIFI_RET_FAIL;
			}
		} else {
			return -EINVAL;
		}
	}

	if (status != NXP_WIFI_RET_SUCCESS) {
		return -EAGAIN;
	}

	return 0;
}
#endif

static int nxp_wifi_process_results(unsigned int count)
{
	struct wlan_scan_result scan_result = {0};
	struct wifi_scan_result res = {0};

	if (!count) {
		LOG_DBG("No Wi-Fi AP found");
		goto out;
	}

	if (g_mlan.max_bss_cnt) {
		count = g_mlan.max_bss_cnt > count ? count : g_mlan.max_bss_cnt;
	}

	for (int i = 0; i < count; i++) {
		wlan_get_scan_result(i, &scan_result);

		memset(&res, 0, sizeof(struct wifi_scan_result));

		memcpy(res.mac, scan_result.bssid, WIFI_MAC_ADDR_LEN);
		res.mac_length = WIFI_MAC_ADDR_LEN;
		res.ssid_length = scan_result.ssid_len;
		strncpy(res.ssid, scan_result.ssid, scan_result.ssid_len);

		res.rssi = -scan_result.rssi;
		res.channel = scan_result.channel;
		res.band = scan_result.channel > 14 ? WIFI_FREQ_BAND_5_GHZ : WIFI_FREQ_BAND_2_4_GHZ;

		res.security = WIFI_SECURITY_TYPE_NONE;

		if (scan_result.wpa2_entp) {
			res.security = WIFI_SECURITY_TYPE_EAP_TLS;
		}
		if (scan_result.wpa2) {
			res.security = WIFI_SECURITY_TYPE_PSK;
		}
#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT
		if (scan_result.wpa2_sha256) {
			res.security = WIFI_SECURITY_TYPE_PSK_SHA256;
		}
#endif
		if (scan_result.wpa3_sae) {
			res.security = WIFI_SECURITY_TYPE_SAE;
		}

		if (scan_result.ap_mfpr) {
			res.mfp = WIFI_MFP_REQUIRED;
		} else if (scan_result.ap_mfpc) {
			res.mfp = WIFI_MFP_OPTIONAL;
		}

		if (g_mlan.scan_cb) {
			g_mlan.scan_cb(g_mlan.netif, 0, &res);

			/* ensure notifications get delivered */
			k_yield();
		}
	}

out:
	/* report end of scan event */
	if (g_mlan.scan_cb) {
		g_mlan.scan_cb(g_mlan.netif, 0, NULL);
	}

	g_mlan.scan_cb = NULL;

	return WM_SUCCESS;
}

static int nxp_wifi_scan(const struct device *dev, struct wifi_scan_params *params,
			 scan_result_cb_t cb)
{
	int ret;
	struct interface *if_handle = (struct interface *)dev->data;
	wlan_scan_params_v2_t wlan_scan_params_v2 = {0};
	uint8_t i = 0;

	if (if_handle->state.interface != WLAN_BSS_TYPE_STA) {
		LOG_ERR("Wi-Fi not in station mode");
		return -EIO;
	}

	if (s_nxp_wifi_State != NXP_WIFI_STARTED) {
		LOG_ERR("Wi-Fi not started status %d", s_nxp_wifi_State);
		return -EBUSY;
	}

	if (g_mlan.scan_cb != NULL) {
		LOG_WRN("Scan callback in progress");
		return -EINPROGRESS;
	}

	g_mlan.scan_cb = cb;

	if (params == NULL) {
		goto do_scan;
	}

	g_mlan.max_bss_cnt = params->max_bss_cnt;

	if (params->bands & (1 << WIFI_FREQ_BAND_6_GHZ)) {
		LOG_ERR("Wi-Fi band 6 GHz not supported");
		g_mlan.scan_cb = NULL;
		return -EIO;
	}
#if (CONFIG_WIFI_MGMT_SCAN_SSID_FILT_MAX > 0)
	if (params->ssids[0]) {
		strncpy(wlan_scan_params_v2.ssid[0], params->ssids[0], WIFI_SSID_MAX_LEN);
		wlan_scan_params_v2.ssid[0][WIFI_SSID_MAX_LEN - 1] = 0;
	}

#if (CONFIG_WIFI_MGMT_SCAN_SSID_FILT_MAX > 1)
	if (params->ssids[1]) {
		strncpy(wlan_scan_params_v2.ssid[1], params->ssids[1], WIFI_SSID_MAX_LEN);
		wlan_scan_params_v2.ssid[1][WIFI_SSID_MAX_LEN - 1] = 0;
	}
#endif
#endif

#ifdef CONFIG_WIFI_MGMT_SCAN_CHAN_MAX_MANUAL

	for (i = 0; i < WIFI_MGMT_SCAN_CHAN_MAX_MANUAL && params->band_chan[i].channel; i++) {
		if (params->scan_type == WIFI_SCAN_TYPE_ACTIVE) {
			wlan_scan_params_v2.chan_list[i].scan_type = MLAN_SCAN_TYPE_ACTIVE;
			wlan_scan_params_v2.chan_list[i].scan_time = params->dwell_time_active;
		} else {
			wlan_scan_params_v2.chan_list[i].scan_type = MLAN_SCAN_TYPE_PASSIVE;
			wlan_scan_params_v2.chan_list[i].scan_time = params->dwell_time_passive;
		}

		wlan_scan_params_v2.chan_list[i].chan_number =
			(uint8_t)(params->band_chan[i].channel);
	}
#endif

	wlan_scan_params_v2.num_channels = i;

	if (params->bands & (1 << WIFI_FREQ_BAND_2_4_GHZ)) {
		wlan_scan_params_v2.chan_list[0].radio_type = 0 | BAND_SPECIFIED;
	}
#ifdef CONFIG_5GHz_SUPPORT
	if (params->bands & (1 << WIFI_FREQ_BAND_5_GHZ)) {
		if (wlan_scan_params_v2.chan_list[0].radio_type & BAND_SPECIFIED) {
			wlan_scan_params_v2.chan_list[0].radio_type = 0;
		} else {
			wlan_scan_params_v2.chan_list[0].radio_type = 1 | BAND_SPECIFIED;
		}
	}
#else
	if (params->bands & (1 << WIFI_FREQ_BAND_5_GHZ)) {
		LOG_ERR("Wi-Fi band 5Hz not supported");
		g_mlan.scan_cb = NULL;
		return -EIO;
	}
#endif

do_scan:
	wlan_scan_params_v2.cb = nxp_wifi_process_results;

	ret = wlan_scan_with_opt(wlan_scan_params_v2);
	if (ret != WM_SUCCESS) {
		LOG_ERR("Failed to start Wi-Fi scanning");
		g_mlan.scan_cb = NULL;
		return -EAGAIN;
	}

	return 0;
}

static int nxp_wifi_version(const struct device *dev, struct wifi_version *params)
{
	int status = NXP_WIFI_RET_SUCCESS;

	if (s_nxp_wifi_State != NXP_WIFI_STARTED) {
		status = NXP_WIFI_RET_NOT_READY;
	}

	if (status != NXP_WIFI_RET_SUCCESS) {
		LOG_ERR("Failed to get Wi-Fi driver/firmware version");
		return -EAGAIN;
	}

	params->drv_version = WLAN_DRV_VERSION;

	params->fw_version = wlan_get_firmware_version_ext();

	return 0;
}

static int nxp_wifi_connect(const struct device *dev, struct wifi_connect_req_params *params)
{
	int status = NXP_WIFI_RET_SUCCESS;
	int ret;
	struct interface *if_handle = (struct interface *)dev->data;

	if (s_nxp_wifi_State != NXP_WIFI_STARTED) {
		LOG_ERR("Wi-Fi not started");
		wifi_mgmt_raise_connect_result_event(g_mlan.netif, -1);
		return -EALREADY;
	}

	if (if_handle->state.interface != WLAN_BSS_TYPE_STA) {
		LOG_ERR("Wi-Fi not in station mode");
		wifi_mgmt_raise_connect_result_event(g_mlan.netif, -1);
		return -EIO;
	}

	if ((params->ssid_length == 0) || (params->ssid_length > IEEEtypes_SSID_SIZE)) {
		status = NXP_WIFI_RET_BAD_PARAM;
	}

	if (status == NXP_WIFI_RET_SUCCESS) {
		wlan_disconnect();

		wlan_remove_network(nxp_wlan_network.name);

		wlan_initialize_sta_network(&nxp_wlan_network);

		memcpy(nxp_wlan_network.name, NXP_WIFI_STA_NETWORK_NAME,
		       strlen(NXP_WIFI_STA_NETWORK_NAME));

		memcpy(nxp_wlan_network.ssid, params->ssid, params->ssid_length);

		nxp_wlan_network.ssid_specific = 1;

		if (params->channel == WIFI_CHANNEL_ANY) {
			nxp_wlan_network.channel = 0;
		} else {
			nxp_wlan_network.channel = params->channel;
		}

		if (params->mfp == WIFI_MFP_REQUIRED) {
			nxp_wlan_network.security.mfpc = true;
			nxp_wlan_network.security.mfpr = true;
		} else if (params->mfp == WIFI_MFP_OPTIONAL) {
			nxp_wlan_network.security.mfpc = true;
			nxp_wlan_network.security.mfpr = false;
		}

		if (params->security == WIFI_SECURITY_TYPE_NONE) {
			nxp_wlan_network.security.type = WLAN_SECURITY_NONE;
		} else if (params->security == WIFI_SECURITY_TYPE_PSK) {
			nxp_wlan_network.security.type = WLAN_SECURITY_WPA2;
			nxp_wlan_network.security.psk_len = params->psk_length;
			strncpy(nxp_wlan_network.security.psk, params->psk, params->psk_length);
		}
#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT
		else if (params->security == WIFI_SECURITY_TYPE_PSK_SHA256) {
			nxp_wlan_network.security.type = WLAN_SECURITY_WPA2;
			nxp_wlan_network.security.key_mgmt |= WLAN_KEY_MGMT_PSK_SHA256;
			nxp_wlan_network.security.psk_len = params->psk_length;
			strncpy(nxp_wlan_network.security.psk, params->psk, params->psk_length);
		}
#endif
		else if (params->security == WIFI_SECURITY_TYPE_SAE) {
			nxp_wlan_network.security.type = WLAN_SECURITY_WPA3_SAE;
			nxp_wlan_network.security.password_len = params->psk_length;
			strncpy(nxp_wlan_network.security.password, params->psk,
				params->psk_length);
		} else {
			status = NXP_WIFI_RET_BAD_PARAM;
		}
	}

	if (status != NXP_WIFI_RET_SUCCESS) {
		LOG_ERR("Failed to connect to Wi-Fi access point");
		return -EAGAIN;
	}

	ret = wlan_add_network(&nxp_wlan_network);
	if (ret != WM_SUCCESS) {
		status = NXP_WIFI_RET_FAIL;
	}

	ret = wlan_connect(nxp_wlan_network.name);
	if (ret != WM_SUCCESS) {
		status = NXP_WIFI_RET_FAIL;
	}

	return 0;
}

static int nxp_wifi_disconnect(const struct device *dev)
{
	int status = NXP_WIFI_RET_SUCCESS;
	int ret;
	struct interface *if_handle = (struct interface *)dev->data;
	enum wlan_connection_state connection_state = WLAN_DISCONNECTED;

	if (s_nxp_wifi_State != NXP_WIFI_STARTED) {
		status = NXP_WIFI_RET_NOT_READY;
	}

	if (if_handle->state.interface != WLAN_BSS_TYPE_STA) {
		LOG_ERR("Wi-Fi not in station mode");
		return -EIO;
	}

	wlan_get_connection_state(&connection_state);
	if (connection_state == WLAN_DISCONNECTED) {
		s_nxp_wifi_StaConnected = false;
		wifi_mgmt_raise_disconnect_result_event(g_mlan.netif, -1);
		return NXP_WIFI_RET_SUCCESS;
	}

	if (status == NXP_WIFI_RET_SUCCESS) {
		ret = wlan_disconnect();
		if (ret != WM_SUCCESS) {
			status = NXP_WIFI_RET_FAIL;
		}
	}

	if (status != NXP_WIFI_RET_SUCCESS) {
		LOG_ERR("Failed to disconnect from AP");
		wifi_mgmt_raise_disconnect_result_event(g_mlan.netif, -1);
		return -EAGAIN;
	}

	wifi_mgmt_raise_disconnect_result_event(g_mlan.netif, 0);

	return 0;
}

static inline enum wifi_security_type nxp_wifi_security_type(enum wlan_security_type type)
{
	switch (type) {
	case WLAN_SECURITY_NONE:
		return WIFI_SECURITY_TYPE_NONE;
	case WLAN_SECURITY_WPA2:
		return WIFI_SECURITY_TYPE_PSK;
	case WLAN_SECURITY_WPA3_SAE:
		return WIFI_SECURITY_TYPE_SAE;
	default:
		return WIFI_SECURITY_TYPE_UNKNOWN;
	}
}

static int nxp_wifi_status(const struct device *dev, struct wifi_iface_status *status)
{
	enum wlan_connection_state connection_state = WLAN_DISCONNECTED;
	struct interface *if_handle = (struct interface *)dev->data;

	wlan_get_connection_state(&connection_state);

	if (s_nxp_wifi_State != NXP_WIFI_STARTED) {
		status->state = WIFI_STATE_INTERFACE_DISABLED;

		return 0;
	}

	if (connection_state == WLAN_DISCONNECTED) {
		status->state = WIFI_STATE_DISCONNECTED;
	} else if (connection_state == WLAN_SCANNING) {
		status->state = WIFI_STATE_SCANNING;
	} else if (connection_state == WLAN_ASSOCIATING) {
		status->state = WIFI_STATE_ASSOCIATING;
	} else if (connection_state == WLAN_ASSOCIATED) {
		status->state = WIFI_STATE_ASSOCIATED;
	} else if (connection_state == WLAN_CONNECTED) {
		status->state = WIFI_STATE_COMPLETED;

		if (!wlan_get_current_network(&nxp_wlan_network)) {
			strncpy(status->ssid, nxp_wlan_network.ssid, WIFI_SSID_MAX_LEN - 1);
			status->ssid[WIFI_SSID_MAX_LEN - 1] = '\0';
			status->ssid_len = strlen(nxp_wlan_network.ssid);

			memcpy(status->bssid, nxp_wlan_network.bssid, WIFI_MAC_ADDR_LEN);

			status->rssi = nxp_wlan_network.rssi;

			status->channel = nxp_wlan_network.channel;

			status->beacon_interval = nxp_wlan_network.beacon_period;

			status->dtim_period = nxp_wlan_network.dtim_period;

			if (if_handle->state.interface == WLAN_BSS_TYPE_STA) {
				status->iface_mode = WIFI_MODE_INFRA;
			}
#ifdef CONFIG_NXP_WIFI_SOFTAP_SUPPORT
			else if (if_handle->state.interface == WLAN_BSS_TYPE_UAP) {
				status->iface_mode = WIFI_MODE_AP;
			}
#endif

#ifdef CONFIG_NXP_WIFI_11AX
			if (nxp_wlan_network.dot11ax) {
				status->link_mode = WIFI_6;
			}
#endif
#ifdef CONFIG_NXP_WIFI_11AC
			else if (nxp_wlan_network.dot11ac) {
				status->link_mode = WIFI_5;
			}
#endif
			else if (nxp_wlan_network.dot11n) {
				status->link_mode = WIFI_4;
			} else {
				status->link_mode = WIFI_3;
			}

			status->band = nxp_wlan_network.channel > 14 ? WIFI_FREQ_BAND_5_GHZ
								     : WIFI_FREQ_BAND_2_4_GHZ;
			status->security = nxp_wifi_security_type(nxp_wlan_network.security.type);
			status->mfp = nxp_wlan_network.security.mfpr ? WIFI_MFP_REQUIRED :
				(nxp_wlan_network.security.mfpc ? WIFI_MFP_OPTIONAL : 0);
		}
	}

	return 0;
}

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

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

	return 0;
}
#endif

#ifdef CONFIG_NXP_WIFI_STA_AUTO_CONN
static void nxp_wifi_auto_connect(void)
{
	int ssid_len = strlen(CONFIG_NXP_WIFI_STA_AUTO_SSID);
	int psk_len = strlen(CONFIG_NXP_WIFI_STA_AUTO_PASSWORD);
	struct wifi_connect_req_params params = {0};
	char ssid[IEEEtypes_SSID_SIZE] = {0};
	char psk[WLAN_PSK_MAX_LENGTH] = {0};

	if (ssid_len >= IEEEtypes_SSID_SIZE) {
		LOG_ERR("AutoConnect SSID too long");
		return;
	}
	if (ssid_len == 0) {
		LOG_ERR("AutoConnect SSID NULL");
		return;
	}

	strcpy(ssid, CONFIG_NXP_WIFI_STA_AUTO_SSID);

	if (psk_len == 0) {
		params.security = WIFI_SECURITY_TYPE_NONE;
	} else if (psk_len >= WLAN_PSK_MIN_LENGTH && psk_len < WLAN_PSK_MAX_LENGTH) {
		strcpy(psk, CONFIG_NXP_WIFI_STA_AUTO_PASSWORD);
		params.security = WIFI_SECURITY_TYPE_PSK;
	} else {
		LOG_ERR("AutoConnect invalid password length %d", psk_len);
		return;
	}

	params.channel = WIFI_CHANNEL_ANY;
	params.ssid = &ssid[0];
	params.ssid_length = ssid_len;
	params.psk = &psk[0];
	params.psk_length = psk_len;

	LOG_DBG("AutoConnect SSID[%s]", ssid);
	nxp_wifi_connect(g_mlan.netif->if_dev->dev, &params);
}
#endif

static int nxp_wifi_11k_cfg(const struct device *dev, struct wifi_11k_params *params)
{
	if (params->oper == WIFI_MGMT_GET) {
		params->enable_11k = wlan_get_host_11k_status();
	} else {
		wlan_host_11k_cfg(params->enable_11k);
	}

	return 0;
}

static int nxp_wifi_power_save(const struct device *dev, struct wifi_ps_params *params)
{
	int status = NXP_WIFI_RET_SUCCESS;
	int ret = WM_SUCCESS;
	uint32_t syncBit;
	struct interface *if_handle = (struct interface *)dev->data;

	if (if_handle->state.interface != WLAN_BSS_TYPE_STA) {
		LOG_ERR("Wi-Fi not in station mode");
		return -EIO;
	}

	if (s_nxp_wifi_State != NXP_WIFI_STARTED) {
		status = NXP_WIFI_RET_NOT_READY;
	}

	if (status == NXP_WIFI_RET_SUCCESS) {

		switch (params->type) {
		case WIFI_PS_PARAM_STATE:
			if (params->enabled == WIFI_PS_DISABLED) {
				k_event_clear(&s_nxp_wifi_SyncEvent, NXP_WIFI_SYNC_PS_GROUP);
				ret = wlan_deepsleepps_off();
				if (ret != WM_SUCCESS) {
					status = NXP_WIFI_RET_FAIL;
				}

				if (status == NXP_WIFI_RET_SUCCESS) {
					syncBit = k_event_wait(&s_nxp_wifi_SyncEvent,
							       NXP_WIFI_SYNC_PS_GROUP, false,
							       NXP_WIFI_SYNC_TIMEOUT_MS);
					k_event_clear(&s_nxp_wifi_SyncEvent,
						      NXP_WIFI_SYNC_PS_GROUP);
					if (syncBit & NXP_WIFI_EVENT_BIT(WLAN_REASON_PS_EXIT)) {
						status = NXP_WIFI_RET_SUCCESS;
					} else {
						status = NXP_WIFI_RET_TIMEOUT;
					}
				}

				if (status != NXP_WIFI_RET_SUCCESS) {
					LOG_DBG("Wi-Fi power save is already disabled");
					return -EAGAIN;
				}

				k_event_clear(&s_nxp_wifi_SyncEvent, NXP_WIFI_SYNC_PS_GROUP);
				ret = wlan_ieeeps_off();
				if (ret != WM_SUCCESS) {
					status = NXP_WIFI_RET_FAIL;
				}

				if (status == NXP_WIFI_RET_SUCCESS) {
					syncBit = k_event_wait(&s_nxp_wifi_SyncEvent,
							       NXP_WIFI_SYNC_PS_GROUP, false,
							       NXP_WIFI_SYNC_TIMEOUT_MS);
					k_event_clear(&s_nxp_wifi_SyncEvent,
						      NXP_WIFI_SYNC_PS_GROUP);
					if (syncBit & NXP_WIFI_EVENT_BIT(WLAN_REASON_PS_EXIT)) {
						status = NXP_WIFI_RET_SUCCESS;
					} else {
						status = NXP_WIFI_RET_TIMEOUT;
					}
				}

				if (status != NXP_WIFI_RET_SUCCESS) {
					LOG_DBG("Wi-Fi power save is already disabled");
					return -EAGAIN;
				}
			} else if (params->enabled == WIFI_PS_ENABLED) {
				k_event_clear(&s_nxp_wifi_SyncEvent, NXP_WIFI_SYNC_PS_GROUP);
				ret = wlan_deepsleepps_on();
				if (ret != WM_SUCCESS) {
					status = NXP_WIFI_RET_FAIL;
				}

				if (status == NXP_WIFI_RET_SUCCESS) {
					syncBit = k_event_wait(&s_nxp_wifi_SyncEvent,
							       NXP_WIFI_SYNC_PS_GROUP, false,
							       NXP_WIFI_SYNC_TIMEOUT_MS);
					k_event_clear(&s_nxp_wifi_SyncEvent,
						      NXP_WIFI_SYNC_PS_GROUP);
					if (syncBit & NXP_WIFI_EVENT_BIT(WLAN_REASON_PS_ENTER)) {
						status = NXP_WIFI_RET_SUCCESS;
					} else {
						status = NXP_WIFI_RET_TIMEOUT;
					}
				}

				if (status != NXP_WIFI_RET_SUCCESS) {
					LOG_DBG("Wi-Fi power save is already enabled");
					return -EAGAIN;
				}

				k_event_clear(&s_nxp_wifi_SyncEvent, NXP_WIFI_SYNC_PS_GROUP);
				ret = wlan_ieeeps_on((unsigned int)WAKE_ON_UNICAST |
						     (unsigned int)WAKE_ON_MAC_EVENT |
						     (unsigned int)WAKE_ON_MULTICAST |
						     (unsigned int)WAKE_ON_ARP_BROADCAST);

				if (ret != WM_SUCCESS) {
					status = NXP_WIFI_RET_FAIL;
				}

				if (status == NXP_WIFI_RET_SUCCESS) {
					syncBit = k_event_wait(&s_nxp_wifi_SyncEvent,
							       NXP_WIFI_SYNC_PS_GROUP, false,
							       NXP_WIFI_SYNC_TIMEOUT_MS);
					k_event_clear(&s_nxp_wifi_SyncEvent,
						      NXP_WIFI_SYNC_PS_GROUP);
					if (syncBit & NXP_WIFI_EVENT_BIT(WLAN_REASON_PS_ENTER)) {
						status = NXP_WIFI_RET_SUCCESS;
					} else {
						status = NXP_WIFI_RET_TIMEOUT;
					}
				}

				if (status != NXP_WIFI_RET_SUCCESS) {
					LOG_DBG("Wi-Fi power save is already enabled");
					return -EAGAIN;
				}
			}
			break;
		case WIFI_PS_PARAM_LISTEN_INTERVAL:
			wlan_configure_listen_interval((int)params->listen_interval);
			break;
		case WIFI_PS_PARAM_WAKEUP_MODE:
			if (params->wakeup_mode == WIFI_PS_WAKEUP_MODE_DTIM) {
				wlan_configure_listen_interval(0);
			}
			break;
		case WIFI_PS_PARAM_MODE:
			if (params->mode == WIFI_PS_MODE_WMM) {
				ret = wlan_set_wmm_uapsd(1);

				if (ret != WM_SUCCESS) {
					status = NXP_WIFI_RET_FAIL;
				}
			} else if (params->mode == WIFI_PS_MODE_LEGACY) {
				ret = wlan_set_wmm_uapsd(0);

				if (ret != WM_SUCCESS) {
					status = NXP_WIFI_RET_FAIL;
				}
			}
			break;
		case WIFI_PS_PARAM_TIMEOUT:
			wlan_configure_delay_to_ps((int)params->timeout_ms);
			break;
		default:
			status = NXP_WIFI_RET_FAIL;
			break;
		}
	}

	if (status != NXP_WIFI_RET_SUCCESS) {
		LOG_ERR("Failed to set Wi-Fi power save");
		return -EAGAIN;
	}

	return 0;
}

int nxp_wifi_get_power_save(const struct device *dev, struct wifi_ps_config *config)
{
	int status = NXP_WIFI_RET_SUCCESS;
	struct interface *if_handle = (struct interface *)dev->data;

	if (if_handle->state.interface != WLAN_BSS_TYPE_STA) {
		LOG_ERR("Wi-Fi not in station mode");
		return -EIO;
	}

	if (s_nxp_wifi_State != NXP_WIFI_STARTED) {
		status = NXP_WIFI_RET_NOT_READY;
	}

	if (status == NXP_WIFI_RET_SUCCESS) {
		if (config) {
			if (wlan_is_power_save_enabled()) {
				config->ps_params.enabled = WIFI_PS_ENABLED;
			} else {
				config->ps_params.enabled = WIFI_PS_DISABLED;
			}

			config->ps_params.listen_interval = wlan_get_listen_interval();

			config->ps_params.timeout_ms = wlan_get_delay_to_ps();
			if (config->ps_params.listen_interval) {
				config->ps_params.wakeup_mode = WIFI_PS_WAKEUP_MODE_LISTEN_INTERVAL;
			} else {
				config->ps_params.wakeup_mode = WIFI_PS_WAKEUP_MODE_DTIM;
			}

			if (wlan_is_wmm_uapsd_enabled()) {
				config->ps_params.mode = WIFI_PS_MODE_WMM;
			} else {
				config->ps_params.mode = WIFI_PS_MODE_LEGACY;
			}
		} else {
			status = NXP_WIFI_RET_FAIL;
		}
	}

	if (status != NXP_WIFI_RET_SUCCESS) {
		LOG_ERR("Failed to get Wi-Fi power save config");
		return -EAGAIN;
	}

	return 0;
}

static int nxp_wifi_reg_domain(const struct device *dev, struct wifi_reg_domain *reg_domain)
{
	int ret;
	uint8_t index = 0;
	chan_freq_power_t *nxp_wifi_chan_freq_power = NULL;
	int nxp_wifi_cfp_no = 0;

	if (reg_domain->oper == WIFI_MGMT_GET) {
		nxp_wifi_chan_freq_power = (chan_freq_power_t *)wlan_get_regulatory_domain(0,
				&nxp_wifi_cfp_no);
		if (nxp_wifi_chan_freq_power != NULL) {
			for (int i = 0; i < nxp_wifi_cfp_no; i++) {
				reg_domain->chan_info[i].center_frequency =
					nxp_wifi_chan_freq_power[i].freq;
				reg_domain->chan_info[i].max_power =
					nxp_wifi_chan_freq_power[i].max_tx_power;
				reg_domain->chan_info[i].supported = 1;
				reg_domain->chan_info[i].passive_only =
					nxp_wifi_chan_freq_power[i].passive_scan_or_radar_detect;
				reg_domain->chan_info[i].dfs =
					nxp_wifi_chan_freq_power[i].passive_scan_or_radar_detect;
			}
			index = nxp_wifi_cfp_no;
		}

		nxp_wifi_chan_freq_power = (chan_freq_power_t *)wlan_get_regulatory_domain(1,
				&nxp_wifi_cfp_no);
		if (nxp_wifi_chan_freq_power != NULL) {
			for (int i = 0; i < nxp_wifi_cfp_no; i++) {
				reg_domain->chan_info[index + i].center_frequency =
					nxp_wifi_chan_freq_power[i].freq;
				reg_domain->chan_info[index + i].max_power =
					nxp_wifi_chan_freq_power[i].max_tx_power;
				reg_domain->chan_info[index + i].supported = 1;
				reg_domain->chan_info[index + i].passive_only =
					nxp_wifi_chan_freq_power[i].passive_scan_or_radar_detect;
				reg_domain->chan_info[index + i].dfs =
					nxp_wifi_chan_freq_power[i].passive_scan_or_radar_detect;
			}
			index += nxp_wifi_cfp_no;
		}
		reg_domain->num_channels = index;
		wifi_get_country_code(reg_domain->country_code);
	} else {
		if (is_uap_started()) {
			LOG_ERR("region code can not be set after uAP start!");
			return -EAGAIN;
		}

		ret = wlan_set_country_code(reg_domain->country_code);
		if (ret != WM_SUCCESS) {
			LOG_ERR("Unable to set country code: %s", reg_domain->country_code);
			return -EAGAIN;
		}
	}
	return 0;
}

static int nxp_wifi_set_twt(const struct device *dev, struct wifi_twt_params *params)
{
	wlan_twt_setup_config_t twt_setup_conf;
	wlan_twt_teardown_config_t teardown_conf;
	int ret = -1;

	if (params->negotiation_type == WIFI_TWT_INDIVIDUAL) {
		params->negotiation_type = 0;
	} else if (params->negotiation_type == WIFI_TWT_BROADCAST) {
		params->negotiation_type = 3;
	}

	if (params->operation == WIFI_TWT_SETUP) {
		twt_setup_conf.implicit = params->setup.implicit;
		twt_setup_conf.announced = params->setup.announce;
		twt_setup_conf.trigger_enabled = params->setup.trigger;
		twt_setup_conf.negotiation_type = params->negotiation_type;
		twt_setup_conf.twt_wakeup_duration = params->setup.twt_wake_interval;
		twt_setup_conf.flow_identifier = params->flow_id;
		twt_setup_conf.hard_constraint = 1;
		twt_setup_conf.twt_mantissa = params->setup.twt_interval;
		twt_setup_conf.twt_request = params->setup.responder;
		ret = wlan_set_twt_setup_cfg(&twt_setup_conf);
	} else if (params->operation == WIFI_TWT_TEARDOWN) {
		teardown_conf.flow_identifier = params->flow_id;
		teardown_conf.negotiation_type = params->negotiation_type;
		teardown_conf.teardown_all_twt = params->teardown.teardown_all;
		ret = wlan_set_twt_teardown_cfg(&teardown_conf);
	}

	return ret;
}

static void nxp_wifi_sta_init(struct net_if *iface)
{
	const struct device *dev = net_if_get_device(iface);
	struct ethernet_context *eth_ctx = net_if_l2_data(iface);
	struct interface *intf = dev->data;

	eth_ctx->eth_if_type = L2_ETH_IF_TYPE_WIFI;
	intf->netif = iface;
#ifdef CONFIG_WIFI_NM
	wifi_nm_register_mgd_type_iface(wifi_nm_get_instance("wifi_supplicant"),
			WIFI_TYPE_STA, iface);
#endif
	g_mlan.state.interface = WLAN_BSS_TYPE_STA;

#ifndef CONFIG_NXP_WIFI_SOFTAP_SUPPORT
	int ret;

	if (s_nxp_wifi_State == NXP_WIFI_NOT_INITIALIZED) {
		/* Initialize the wifi subsystem */
		ret = nxp_wifi_wlan_init();
		if (ret) {
			LOG_ERR("wlan initialization failed");
			return;
		}
		ret = nxp_wifi_wlan_start();
		if (ret) {
			LOG_ERR("wlan start failed");
			return;
		}
	}
#endif
}

#ifdef CONFIG_NXP_WIFI_SOFTAP_SUPPORT

static void nxp_wifi_uap_init(struct net_if *iface)
{
	const struct device *dev = net_if_get_device(iface);
	struct ethernet_context *eth_ctx = net_if_l2_data(iface);
	struct interface *intf = dev->data;
	int ret;

	eth_ctx->eth_if_type = L2_ETH_IF_TYPE_WIFI;
	intf->netif = iface;
#ifdef CONFIG_WIFI_NM
	wifi_nm_register_mgd_type_iface(wifi_nm_get_instance("hostapd"),
			WIFI_TYPE_SAP, iface);
#endif
	g_uap.state.interface = WLAN_BSS_TYPE_UAP;

	if (s_nxp_wifi_State == NXP_WIFI_NOT_INITIALIZED) {
		/* Initialize the wifi subsystem */
		ret = nxp_wifi_wlan_init();
		if (ret) {
			LOG_ERR("wlan initialization failed");
			return;
		}
		ret = nxp_wifi_wlan_start();
		if (ret) {
			LOG_ERR("wlan start failed");
			return;
		}
	}
}

#endif

static NXP_WIFI_SET_FUNC_ATTR int nxp_wifi_send(const struct device *dev, struct net_pkt *pkt)
{
#if defined(CONFIG_NET_STATISTICS_WIFI)
	struct interface *if_handle = (struct interface *)dev->data;
	const int pkt_len = net_pkt_get_len(pkt);
	struct net_eth_hdr *hdr = NET_ETH_HDR(pkt);
#endif

	/* Enqueue packet for transmission */
	if (nxp_wifi_internal_tx(dev, pkt) != WM_SUCCESS) {
		goto out;
	}

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

	if (net_eth_is_addr_multicast(&hdr->dst)) {
		if_handle->stats.multicast.tx++;
	} else if (net_eth_is_addr_broadcast(&hdr->dst)) {
		if_handle->stats.broadcast.tx++;
	}
#endif

	return 0;

out:
#if defined(CONFIG_NET_STATISTICS_WIFI)
	if_handle->stats.errors.tx++;
#endif
	return -EIO;
}

static NXP_WIFI_SET_FUNC_ATTR int nxp_wifi_recv(struct net_if *iface, struct net_pkt *pkt)
{
#if defined(CONFIG_NET_STATISTICS_WIFI)
	struct interface *if_handle = (struct interface *)(net_if_get_device(iface)->data);
	const int pkt_len = net_pkt_get_len(pkt);
	struct net_eth_hdr *hdr = NET_ETH_HDR(pkt);
#endif

	if (net_recv_data(iface, pkt) < 0) {
		goto out;
	}

#if defined(CONFIG_NET_STATISTICS_WIFI)
	if (net_eth_is_addr_broadcast(&hdr->dst)) {
		if_handle->stats.broadcast.rx++;
	} else if (net_eth_is_addr_multicast(&hdr->dst)) {
		if_handle->stats.multicast.rx++;
	}

	if_handle->stats.bytes.received += pkt_len;
	if_handle->stats.pkts.rx++;
#endif

	return 0;

out:
#if defined(CONFIG_NET_STATISTICS_WIFI)
	if_handle->stats.errors.rx++;
#endif
	return -EIO;
}

#ifdef CONFIG_NXP_RW610
extern void WL_MCI_WAKEUP0_DriverIRQHandler(void);
extern void WL_MCI_WAKEUP_DONE0_DriverIRQHandler(void);
#endif

static int nxp_wifi_dev_init(const struct device *dev)
{
	struct nxp_wifi_dev *nxp_wifi = &nxp_wifi0;

	k_mutex_init(&nxp_wifi->mutex);

	nxp_wifi_shell_register(nxp_wifi);

#ifdef CONFIG_NXP_RW610
	IRQ_CONNECT(IMU_IRQ_N, IMU_IRQ_P, WL_MCI_WAKEUP0_DriverIRQHandler, 0, 0);
	irq_enable(IMU_IRQ_N);
	IRQ_CONNECT(IMU_WAKEUP_IRQ_N, IMU_WAKEUP_IRQ_P, WL_MCI_WAKEUP_DONE0_DriverIRQHandler, 0, 0);
	irq_enable(IMU_WAKEUP_IRQ_N);
#if (DT_INST_PROP(0, wakeup_source))
	EnableDeepSleepIRQ(IMU_IRQ_N);
#endif
#endif

	return 0;
}

static int nxp_wifi_set_config(const struct device *dev, enum ethernet_config_type type,
			       const struct ethernet_config *config)
{
	struct interface *if_handle = (struct interface *)dev->data;

	switch (type) {
	case ETHERNET_CONFIG_TYPE_MAC_ADDRESS:
		memcpy(if_handle->mac_address, config->mac_address.addr, 6);

		net_if_set_link_addr(if_handle->netif, if_handle->mac_address,
				     sizeof(if_handle->mac_address), NET_LINK_ETHERNET);

		if (if_handle->state.interface == WLAN_BSS_TYPE_STA) {
			if (wlan_set_sta_mac_addr(if_handle->mac_address)) {
				LOG_ERR("Failed to set Wi-Fi MAC Address");
				return -ENOEXEC;
			}
#ifdef CONFIG_NXP_WIFI_SOFTAP_SUPPORT
		} else if (if_handle->state.interface == WLAN_BSS_TYPE_UAP) {
			if (wlan_set_uap_mac_addr(if_handle->mac_address)) {
				LOG_ERR("Failed to set Wi-Fi MAC Address");
				return -ENOEXEC;
			}
#endif
		} else {
			LOG_ERR("Invalid Interface index");
			return -ENOEXEC;
		}
		break;
	default:
		return -ENOTSUP;
	}

	return 0;
}

#if defined(CONFIG_PM_DEVICE) && defined(CONFIG_NXP_RW610)
void device_pm_dump_wakeup_source(void)
{
	if (POWER_GetWakeupStatus(IMU_IRQ_N)) {
		LOG_INF("Wakeup by WLAN");
		POWER_ClearWakeupStatus(IMU_IRQ_N);
	} else if (POWER_GetWakeupStatus(41)) {
		LOG_INF("Wakeup by OSTIMER");
		POWER_ClearWakeupStatus(41);
	} else if (POWER_GetWakeupStatus(32)) {
		LOG_INF("Wakeup by RTC");
		POWER_ClearWakeupStatus(32);
	}
}

static int device_wlan_pm_action(const struct device *dev, enum pm_device_action pm_action)
{
	int ret = 0;

	switch (pm_action) {
	case PM_DEVICE_ACTION_SUSPEND:
		if (!wlan_host_sleep_state || !wlan_is_started() || wakelock_isheld()
#ifdef CONFIG_NXP_WIFI_WMM_UAPSD
		    || wlan_is_wmm_uapsd_enabled()
#endif
		)
			return -EBUSY;
		/*
		 * Trigger host sleep handshake here. Before handshake is done, host is not allowed
		 * to enter low power mode
		 */
		if (!is_hs_handshake_done) {
			is_hs_handshake_done = WLAN_HOSTSLEEP_IN_PROCESS;
			ret = wlan_hs_send_event(HOST_SLEEP_HANDSHAKE, NULL);
			if (ret != 0) {
				return -EFAULT;
			}
			return -EBUSY;
		}
		if (is_hs_handshake_done == WLAN_HOSTSLEEP_IN_PROCESS) {
			return -EBUSY;
		}
		if (is_hs_handshake_done == WLAN_HOSTSLEEP_FAIL) {
			is_hs_handshake_done = 0;
			return -EFAULT;
		}
		break;
	case PM_DEVICE_ACTION_RESUME:
		/*
		 * Cancel host sleep in firmware and dump wakekup source.
		 * If sleep state is periodic, start timer to keep host in full power state for 5s.
		 * User can use this time to issue other commands.
		 */
		if (is_hs_handshake_done == WLAN_HOSTSLEEP_SUCCESS) {
			ret = wlan_hs_send_event(HOST_SLEEP_EXIT, NULL);
			if (ret != 0) {
				return -EFAULT;
			}
			device_pm_dump_wakeup_source();
			/* reset hs hanshake flag after waking up */
			is_hs_handshake_done = 0;
			if (wlan_host_sleep_state == HOST_SLEEP_ONESHOT) {
				wlan_host_sleep_state = HOST_SLEEP_DISABLE;
			}
		}
		break;
	default:
		break;
	}
	return ret;
}

PM_DEVICE_DT_INST_DEFINE(0, device_wlan_pm_action);
#endif

static const struct wifi_mgmt_ops nxp_wifi_sta_mgmt = {
	.get_version = nxp_wifi_version,
	.scan = nxp_wifi_scan,
	.connect = nxp_wifi_connect,
	.disconnect = nxp_wifi_disconnect,
	.reg_domain = nxp_wifi_reg_domain,
#ifdef CONFIG_NXP_WIFI_SOFTAP_SUPPORT
	.ap_enable = nxp_wifi_start_ap,
	.ap_disable = nxp_wifi_stop_ap,
#endif
	.iface_status = nxp_wifi_status,
#if defined(CONFIG_NET_STATISTICS_WIFI)
	.get_stats = nxp_wifi_stats,
#endif
	.cfg_11k = nxp_wifi_11k_cfg,
	.set_power_save = nxp_wifi_power_save,
	.get_power_save_config = nxp_wifi_get_power_save,
	.set_twt = nxp_wifi_set_twt,
};

#if defined(CONFIG_WIFI_NM_WPA_SUPPLICANT)
static const struct zep_wpa_supp_dev_ops nxp_wifi_drv_ops = {
	.init = wifi_nxp_wpa_supp_dev_init,
	.deinit                   = wifi_nxp_wpa_supp_dev_deinit,
	.scan2                    = wifi_nxp_wpa_supp_scan2,
	.scan_abort               = wifi_nxp_wpa_supp_scan_abort,
	.get_scan_results2        = wifi_nxp_wpa_supp_scan_results_get,
	.deauthenticate           = wifi_nxp_wpa_supp_deauthenticate,
	.authenticate             = wifi_nxp_wpa_supp_authenticate,
	.associate                = wifi_nxp_wpa_supp_associate,
	.set_key                  = wifi_nxp_wpa_supp_set_key,
	.set_supp_port            = wifi_nxp_wpa_supp_set_supp_port,
	.signal_poll              = wifi_nxp_wpa_supp_signal_poll,
	.send_mlme                = wifi_nxp_wpa_supp_send_mlme,
	.get_wiphy                = wifi_nxp_wpa_supp_get_wiphy,
	.get_capa                 = wifi_nxp_wpa_supp_get_capa,
	.get_conn_info            = wifi_nxp_wpa_supp_get_conn_info,
	.set_country              = wifi_nxp_wpa_supp_set_country,
	.get_country              = wifi_nxp_wpa_supp_get_country,
#ifdef CONFIG_NXP_WIFI_SOFTAP_SUPPORT
#ifdef CONFIG_WIFI_NM_HOSTAPD_AP
	.hapd_init                = wifi_nxp_hostapd_dev_init,
	.hapd_deinit              = wifi_nxp_hostapd_dev_deinit,
#endif
	.init_ap                  = wifi_nxp_wpa_supp_init_ap,
	.set_ap                   = wifi_nxp_hostapd_set_ap,
	.stop_ap                  = wifi_nxp_hostapd_stop_ap,
	.sta_remove               = wifi_nxp_hostapd_sta_remove,
	.sta_add                  = wifi_nxp_hostapd_sta_add,
	.do_acs                   = wifi_nxp_hostapd_do_acs,
#endif
	.dpp_listen               = wifi_nxp_wpa_dpp_listen,
	.remain_on_channel        = wifi_nxp_wpa_supp_remain_on_channel,
	.cancel_remain_on_channel = wifi_nxp_wpa_supp_cancel_remain_on_channel,
};
#endif

static const struct net_wifi_mgmt_offload nxp_wifi_sta_apis = {
	.wifi_iface.iface_api.init = nxp_wifi_sta_init,
	.wifi_iface.set_config = nxp_wifi_set_config,
	.wifi_iface.send = nxp_wifi_send,
	.wifi_mgmt_api = &nxp_wifi_sta_mgmt,
#if defined(CONFIG_WIFI_NM_WPA_SUPPLICANT)
	.wifi_drv_ops = &nxp_wifi_drv_ops,
#endif
};

NET_DEVICE_INIT_INSTANCE(wifi_nxp_sta, "ml", 0, nxp_wifi_dev_init, PM_DEVICE_DT_INST_GET(0),
			 &g_mlan,
#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT
			 &wpa_supp_ops,
#else
			 NULL,
#endif
			 CONFIG_WIFI_INIT_PRIORITY, &nxp_wifi_sta_apis, ETHERNET_L2,
			 NET_L2_GET_CTX_TYPE(ETHERNET_L2), NET_ETH_MTU);

#ifdef CONFIG_NXP_WIFI_SOFTAP_SUPPORT
static const struct wifi_mgmt_ops nxp_wifi_uap_mgmt = {
	.ap_enable = nxp_wifi_start_ap,
	.ap_disable = nxp_wifi_stop_ap,
	.iface_status = nxp_wifi_status,
#if defined(CONFIG_NET_STATISTICS_WIFI)
	.get_stats = nxp_wifi_stats,
#endif
	.set_power_save = nxp_wifi_power_save,
	.get_power_save_config = nxp_wifi_get_power_save,
	.ap_config_params = nxp_wifi_ap_config_params,
};

static const struct net_wifi_mgmt_offload nxp_wifi_uap_apis = {
	.wifi_iface.iface_api.init = nxp_wifi_uap_init,
	.wifi_iface.set_config = nxp_wifi_set_config,
	.wifi_iface.send = nxp_wifi_send,
	.wifi_mgmt_api = &nxp_wifi_uap_mgmt,
#if defined(CONFIG_WIFI_NM_WPA_SUPPLICANT)
	.wifi_drv_ops = &nxp_wifi_drv_ops,
#endif
};

NET_DEVICE_INIT_INSTANCE(wifi_nxp_uap, "ua", 1, NULL, NULL, &g_uap,
#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT
			 &wpa_supp_ops,
#else
			 NULL,
#endif
			 CONFIG_WIFI_INIT_PRIORITY, &nxp_wifi_uap_apis, ETHERNET_L2,
			 NET_L2_GET_CTX_TYPE(ETHERNET_L2), NET_ETH_MTU);
#endif
