/* SPDX-License-Identifier: GPL-2.0 */
/******************************************************************************
 *
 * Copyright(c) 2007 - 2015 Realtek Corporation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
 *
 *
 ******************************************************************************/
#define _RTW_MI_C_

#include <drv_types.h>
#include <hal_data.h>

void rtw_mi_update_union_chan_inf(_adapter *adapter, u8 ch, u8 offset , u8 bw)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct mi_state *iface_state = &dvobj->iface_state;

	iface_state->union_ch = ch;
	iface_state->union_bw = bw;
	iface_state->union_offset = offset;
}

/* Find union about ch, bw, ch_offset of all linked/linking interfaces */
int _rtw_mi_get_ch_setting_union(_adapter *adapter, u8 *ch, u8 *bw, u8 *offset, bool include_self)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	_adapter *iface;
	struct mlme_ext_priv *mlmeext;
	int i;
	u8 ch_ret = 0;
	u8 bw_ret = CHANNEL_WIDTH_20;
	u8 offset_ret = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
	int num = 0;

	if (ch)
		*ch = 0;
	if (bw)
		*bw = CHANNEL_WIDTH_20;
	if (offset)
		*offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;

	for (i = 0; i < dvobj->iface_nums; i++) {
		iface = dvobj->padapters[i];
		mlmeext = &iface->mlmeextpriv;

		if (!check_fwstate(&iface->mlmepriv, _FW_LINKED | _FW_UNDER_LINKING))
			continue;

		if (check_fwstate(&iface->mlmepriv, WIFI_OP_CH_SWITCHING))
			continue;

		if (include_self == _FALSE && adapter == iface)
			continue;

		if (num == 0) {
			ch_ret = mlmeext->cur_channel;
			bw_ret = mlmeext->cur_bwmode;
			offset_ret = mlmeext->cur_ch_offset;
			num++;
			continue;
		}

		if (ch_ret != mlmeext->cur_channel) {
			num = 0;
			break;
		}

		if (bw_ret < mlmeext->cur_bwmode) {
			bw_ret = mlmeext->cur_bwmode;
			offset_ret = mlmeext->cur_ch_offset;
		} else if (bw_ret == mlmeext->cur_bwmode && offset_ret != mlmeext->cur_ch_offset) {
			num = 0;
			break;
		}

		num++;
	}

	if (num) {
		if (ch)
			*ch = ch_ret;
		if (bw)
			*bw = bw_ret;
		if (offset)
			*offset = offset_ret;
	}

	return num;
}

inline int rtw_mi_get_ch_setting_union(_adapter *adapter, u8 *ch, u8 *bw, u8 *offset)
{
	return _rtw_mi_get_ch_setting_union(adapter, ch, bw, offset, 1);
}

inline int rtw_mi_get_ch_setting_union_no_self(_adapter *adapter, u8 *ch, u8 *bw, u8 *offset)
{
	return _rtw_mi_get_ch_setting_union(adapter, ch, bw, offset, 0);
}

/* For now, not return union_ch/bw/offset */
void _rtw_mi_status(_adapter *adapter, struct mi_state *mstate, bool include_self)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	_adapter *iface;
	int i;

	_rtw_memset(mstate, 0, sizeof(struct mi_state));

	for (i = 0; i < dvobj->iface_nums; i++) {
		iface = dvobj->padapters[i];

		if (include_self == _FALSE && iface == adapter)
			continue;

		if (check_fwstate(&iface->mlmepriv, WIFI_STATION_STATE) == _TRUE) {
			MSTATE_STA_NUM(mstate)++;
			if (check_fwstate(&iface->mlmepriv, _FW_LINKED) == _TRUE)
				MSTATE_STA_LD_NUM(mstate)++;

			if (check_fwstate(&iface->mlmepriv, _FW_UNDER_LINKING) == _TRUE)
				MSTATE_STA_LG_NUM(mstate)++;

		} else if (check_fwstate(&iface->mlmepriv, WIFI_AP_STATE) == _TRUE
			&& check_fwstate(&iface->mlmepriv, _FW_LINKED) == _TRUE
		) {
			MSTATE_AP_NUM(mstate)++;
			if (iface->stapriv.asoc_sta_count > 2)
				MSTATE_AP_LD_NUM(mstate)++;

		} else if (check_fwstate(&iface->mlmepriv, WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE) == _TRUE
			&& check_fwstate(&iface->mlmepriv, _FW_LINKED) == _TRUE
		) {
			MSTATE_ADHOC_NUM(mstate)++;
			if (iface->stapriv.asoc_sta_count > 2)
				MSTATE_ADHOC_LD_NUM(mstate)++;
		}

		if (check_fwstate(&iface->mlmepriv, WIFI_UNDER_WPS) == _TRUE)
			MSTATE_WPS_NUM(mstate)++;


	}
}

inline void rtw_mi_status(_adapter *adapter, struct mi_state *mstate)
{
	return _rtw_mi_status(adapter, mstate, 1);
}
inline void rtw_mi_status_no_self(_adapter *adapter, struct mi_state *mstate)
{
	return _rtw_mi_status(adapter, mstate, 0);
}
void dump_mi_status(void *sel, struct dvobj_priv *dvobj)
{
	RTW_PRINT_SEL(sel, "== dvobj-iface_state ==\n");
	RTW_PRINT_SEL(sel, "sta_num:%d\n", DEV_STA_NUM(dvobj));
	RTW_PRINT_SEL(sel, "linking_sta_num:%d\n", DEV_STA_LG_NUM(dvobj));
	RTW_PRINT_SEL(sel, "linked_sta_num:%d\n", DEV_STA_LD_NUM(dvobj));
	RTW_PRINT_SEL(sel, "ap_num:%d\n", DEV_AP_NUM(dvobj));
	RTW_PRINT_SEL(sel, "linked_ap_num:%d\n", DEV_AP_LD_NUM(dvobj));
	RTW_PRINT_SEL(sel, "adhoc_num:%d\n", DEV_ADHOC_NUM(dvobj));
	RTW_PRINT_SEL(sel, "linked_adhoc_num:%d\n", DEV_ADHOC_LD_NUM(dvobj));
	RTW_PRINT_SEL(sel, "p2p_device_num:%d\n", rtw_mi_stay_in_p2p_mode(dvobj->padapters[IFACE_ID0]));
	RTW_PRINT_SEL(sel, "under_wps_num:%d\n", DEV_WPS_NUM(dvobj));
	RTW_PRINT_SEL(sel, "union_ch:%d\n", DEV_U_CH(dvobj));
	RTW_PRINT_SEL(sel, "union_bw:%d\n", DEV_U_BW(dvobj));
	RTW_PRINT_SEL(sel, "union_offset:%d\n", DEV_U_OFFSET(dvobj));
	RTW_PRINT_SEL(sel, "================\n\n");
}

inline void rtw_mi_update_iface_status(struct mlme_priv *pmlmepriv, sint state)
{
	_adapter *adapter = container_of(pmlmepriv, _adapter, mlmepriv);
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct mi_state *iface_state = &dvobj->iface_state;
	struct mi_state tmp_mstate;
	u8 u_ch, u_offset, u_bw;

	if (state == WIFI_MONITOR_STATE
		|| state == WIFI_SITE_MONITOR
		|| state == 0xFFFFFFFF
	)
		return;

	if (0)
		RTW_INFO("%s => will change or clean state to 0x%08x\n", __func__, state);

	rtw_mi_status(adapter, &tmp_mstate);
	_rtw_memcpy(iface_state, &tmp_mstate, sizeof(struct mi_state));

	if (rtw_mi_get_ch_setting_union(adapter, &u_ch, &u_bw, &u_offset))
		rtw_mi_update_union_chan_inf(adapter , u_ch, u_offset , u_bw);
	else {
		if (0) {
			dump_adapters_status(RTW_DBGDUMP , dvobj);
			RTW_INFO("%s-[ERROR] cannot get union channel\n", __func__);
			rtw_warn_on(1);
		}
	}

}
u8 rtw_mi_check_status(_adapter *adapter, u8 type)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct mi_state *iface_state = &dvobj->iface_state;
	u8 ret = _FALSE;


	switch (type) {
	case MI_LINKED:
		if (MSTATE_STA_LD_NUM(iface_state) || MSTATE_AP_NUM(iface_state) || MSTATE_ADHOC_NUM(iface_state)) /*check_fwstate(&iface->mlmepriv, _FW_LINKED)*/
			ret = _TRUE;
		break;
	case MI_ASSOC:
		if (MSTATE_STA_LD_NUM(iface_state) || MSTATE_AP_LD_NUM(iface_state) || MSTATE_ADHOC_LD_NUM(iface_state))
			ret = _TRUE;
		break;
	case MI_UNDER_WPS:
		if (MSTATE_WPS_NUM(iface_state))
			ret = _TRUE;
		break;

	case MI_AP_MODE:
		if (MSTATE_AP_NUM(iface_state))
			ret = _TRUE;
		break;
	case MI_AP_ASSOC:
		if (MSTATE_AP_LD_NUM(iface_state))
			ret = _TRUE;
		break;

	case MI_ADHOC:
		if (MSTATE_ADHOC_NUM(iface_state))
			ret = _TRUE;
		break;
	case MI_ADHOC_ASSOC:
		if (MSTATE_ADHOC_LD_NUM(iface_state))
			ret = _TRUE;
		break;

	case MI_STA_NOLINK: /* this is misleading, but not used now */
		if (MSTATE_STA_NUM(iface_state) && (!(MSTATE_STA_LD_NUM(iface_state) || MSTATE_STA_LG_NUM(iface_state))))
			ret = _TRUE;
		break;
	case MI_STA_LINKED:
		if (MSTATE_STA_LD_NUM(iface_state))
			ret = _TRUE;
		break;
	case MI_STA_LINKING:
		if (MSTATE_STA_LG_NUM(iface_state))
			ret = _TRUE;
		break;

	default:
		break;
	}
	return ret;
}

u8 rtw_mi_mp_mode_check(_adapter *padapter)
{
	if (padapter->registrypriv.mp_mode == 1)
		return _TRUE;
	return _FALSE;
}

/*
* return value : 0 is failed or have not interface meet condition
* return value : !0 is success or interface numbers which meet condition
* return value of ops_func must be _TRUE or _FALSE
*/
static u8 _rtw_mi_process(_adapter *padapter, bool exclude_self,
		  void *data, u8(*ops_func)(_adapter *padapter, void *data))
{
	int i;
	_adapter *iface;
	struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);

	u8 ret = 0;

	for (i = 0; i < dvobj->iface_nums; i++) {
		iface = dvobj->padapters[i];
		if ((iface) && rtw_is_adapter_up(iface)) {

			if ((exclude_self) && (iface == padapter))
				continue;

			if (ops_func)
				if (_TRUE == ops_func(iface, data))
					ret++;
		}
	}
	return ret;
}
static u8 _rtw_mi_process_without_schk(_adapter *padapter, bool exclude_self,
		  void *data, u8(*ops_func)(_adapter *padapter, void *data))
{
	int i;
	_adapter *iface;
	struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);

	u8 ret = 0;

	for (i = 0; i < dvobj->iface_nums; i++) {
		iface = dvobj->padapters[i];
		if (iface) {
			if ((exclude_self) && (iface == padapter))
				continue;

			if (ops_func)
				if (ops_func(iface, data) == _TRUE)
					ret++;
		}
	}
	return ret;
}

static u8 _rtw_mi_netif_stop_queue(_adapter *padapter, void *data)
{
	bool carrier_off = *(bool *)data;
	struct net_device *pnetdev = padapter->pnetdev;

	if (carrier_off)
		netif_carrier_off(pnetdev);
	rtw_netif_stop_queue(pnetdev);
	return _TRUE;
}
u8 rtw_mi_netif_stop_queue(_adapter *padapter, bool carrier_off)
{
	bool in_data = carrier_off;

	return _rtw_mi_process(padapter, _FALSE, &in_data, _rtw_mi_netif_stop_queue);
}
u8 rtw_mi_buddy_netif_stop_queue(_adapter *padapter, bool carrier_off)
{
	bool in_data = carrier_off;

	return _rtw_mi_process(padapter, _TRUE, &in_data, _rtw_mi_netif_stop_queue);
}

static u8 _rtw_mi_netif_wake_queue(_adapter *padapter, void *data)
{
	struct net_device *pnetdev = padapter->pnetdev;

	if (pnetdev)
		rtw_netif_wake_queue(pnetdev);
	return _TRUE;
}
u8 rtw_mi_netif_wake_queue(_adapter *padapter)
{
	return _rtw_mi_process(padapter, _FALSE, NULL, _rtw_mi_netif_wake_queue);
}
u8 rtw_mi_buddy_netif_wake_queue(_adapter *padapter)
{
	return _rtw_mi_process(padapter, _TRUE, NULL, _rtw_mi_netif_wake_queue);
}

static u8 _rtw_mi_netif_carrier_on(_adapter *padapter, void *data)
{
	struct net_device *pnetdev = padapter->pnetdev;

	if (pnetdev)
		rtw_netif_carrier_on(pnetdev);
	return _TRUE;
}
u8 rtw_mi_netif_carrier_on(_adapter *padapter)
{
	return _rtw_mi_process(padapter, _FALSE, NULL, _rtw_mi_netif_carrier_on);
}
u8 rtw_mi_buddy_netif_carrier_on(_adapter *padapter)
{
	return _rtw_mi_process(padapter, _TRUE, NULL, _rtw_mi_netif_carrier_on);
}

static u8 _rtw_mi_scan_abort(_adapter *adapter, void *data)
{
	bool bwait = *(bool *)data;

	if (bwait)
		rtw_scan_abort(adapter);
	else
		rtw_scan_abort_no_wait(adapter);

	return _TRUE;
}
void rtw_mi_scan_abort(_adapter *adapter, bool bwait)
{
	bool in_data = bwait;

	_rtw_mi_process(adapter, _FALSE, &in_data, _rtw_mi_scan_abort);

}
void rtw_mi_buddy_scan_abort(_adapter *adapter, bool bwait)
{
	bool in_data = bwait;

	_rtw_mi_process(adapter, _TRUE, &in_data, _rtw_mi_scan_abort);
}

static u8 _rtw_mi_start_drv_threads(_adapter *adapter, void *data)
{
	rtw_start_drv_threads(adapter);
	return _TRUE;
}
void rtw_mi_start_drv_threads(_adapter *adapter)
{
	_rtw_mi_process(adapter, _FALSE, NULL, _rtw_mi_start_drv_threads);
}
void rtw_mi_buddy_start_drv_threads(_adapter *adapter)
{
	_rtw_mi_process(adapter, _TRUE, NULL, _rtw_mi_start_drv_threads);
}

static u8 _rtw_mi_stop_drv_threads(_adapter *adapter, void *data)
{
	rtw_stop_drv_threads(adapter);
	return _TRUE;
}
void rtw_mi_stop_drv_threads(_adapter *adapter)
{
	_rtw_mi_process(adapter, _FALSE, NULL, _rtw_mi_stop_drv_threads);
}
void rtw_mi_buddy_stop_drv_threads(_adapter *adapter)
{
	_rtw_mi_process(adapter, _TRUE, NULL, _rtw_mi_stop_drv_threads);
}

static u8 _rtw_mi_cancel_all_timer(_adapter *adapter, void *data)
{
	rtw_cancel_all_timer(adapter);
	return _TRUE;
}
void rtw_mi_cancel_all_timer(_adapter *adapter)
{
	_rtw_mi_process(adapter, _FALSE, NULL, _rtw_mi_cancel_all_timer);
}
void rtw_mi_buddy_cancel_all_timer(_adapter *adapter)
{
	_rtw_mi_process(adapter, _TRUE, NULL, _rtw_mi_cancel_all_timer);
}

static u8 _rtw_mi_reset_drv_sw(_adapter *adapter, void *data)
{
	rtw_reset_drv_sw(adapter);
	return _TRUE;
}
void rtw_mi_reset_drv_sw(_adapter *adapter)
{
	_rtw_mi_process_without_schk(adapter, _FALSE, NULL, _rtw_mi_reset_drv_sw);
}
void rtw_mi_buddy_reset_drv_sw(_adapter *adapter)
{
	_rtw_mi_process_without_schk(adapter, _TRUE, NULL, _rtw_mi_reset_drv_sw);
}

static u8 _rtw_mi_intf_start(_adapter *adapter, void *data)
{
	rtw_intf_start(adapter);
	return _TRUE;
}
void rtw_mi_intf_start(_adapter *adapter)
{
	_rtw_mi_process(adapter, _FALSE, NULL, _rtw_mi_intf_start);
}
void rtw_mi_buddy_intf_start(_adapter *adapter)
{
	_rtw_mi_process(adapter, _TRUE, NULL, _rtw_mi_intf_start);
}

static u8 _rtw_mi_intf_stop(_adapter *adapter, void *data)
{
	rtw_intf_stop(adapter);
	return _TRUE;
}
void rtw_mi_intf_stop(_adapter *adapter)
{
	_rtw_mi_process(adapter, _FALSE, NULL, _rtw_mi_intf_stop);
}
void rtw_mi_buddy_intf_stop(_adapter *adapter)
{
	_rtw_mi_process(adapter, _TRUE, NULL, _rtw_mi_intf_stop);
}

static u8 _rtw_mi_suspend_free_assoc_resource(_adapter *padapter, void *data)
{
	return rtw_suspend_free_assoc_resource(padapter);
}
void rtw_mi_suspend_free_assoc_resource(_adapter *adapter)
{
	_rtw_mi_process(adapter, _FALSE, NULL, _rtw_mi_suspend_free_assoc_resource);
}
void rtw_mi_buddy_suspend_free_assoc_resource(_adapter *adapter)
{
	_rtw_mi_process(adapter, _TRUE, NULL, _rtw_mi_suspend_free_assoc_resource);
}

static u8 _rtw_mi_is_scan_deny(_adapter *adapter, void *data)
{
	return rtw_is_scan_deny(adapter);
}

u8 rtw_mi_is_scan_deny(_adapter *adapter)
{
	return _rtw_mi_process(adapter, _FALSE, NULL, _rtw_mi_is_scan_deny);

}
u8 rtw_mi_buddy_is_scan_deny(_adapter *adapter)
{
	return _rtw_mi_process(adapter, _TRUE, NULL, _rtw_mi_is_scan_deny);
}


struct nulldata_param {
	unsigned char *da;
	unsigned int power_mode;
	int try_cnt;
	int wait_ms;
};

static u8 _rtw_mi_issue_nulldata(_adapter *padapter, void *data)
{
	struct nulldata_param *pnulldata_param = (struct nulldata_param *)data;

	if (is_client_associated_to_ap(padapter) == _TRUE) {
		/* TODO: TDLS peers */
		issue_nulldata(padapter, pnulldata_param->da, pnulldata_param->power_mode, pnulldata_param->try_cnt, pnulldata_param->wait_ms);
		return _TRUE;
	}
	return _FALSE;
}

u8 rtw_mi_issue_nulldata(_adapter *padapter, unsigned char *da, unsigned int power_mode, int try_cnt, int wait_ms)
{
	struct nulldata_param nparam;

	nparam.da = da;
	nparam.power_mode = power_mode;/*0 or 1*/
	nparam.try_cnt = try_cnt;
	nparam.wait_ms = wait_ms;

	return _rtw_mi_process(padapter, _FALSE, &nparam, _rtw_mi_issue_nulldata);
}
u8 rtw_mi_buddy_issue_nulldata(_adapter *padapter, unsigned char *da, unsigned int power_mode, int try_cnt, int wait_ms)
{
	struct nulldata_param nparam;

	nparam.da = da;
	nparam.power_mode = power_mode;
	nparam.try_cnt = try_cnt;
	nparam.wait_ms = wait_ms;

	return _rtw_mi_process(padapter, _TRUE, &nparam, _rtw_mi_issue_nulldata);
}

static u8 _rtw_mi_beacon_update(_adapter *padapter, void *data)
{
	struct mlme_ext_priv *mlmeext = &padapter->mlmeextpriv;

	if (mlmeext_msr(mlmeext) == WIFI_FW_AP_STATE
	    && check_fwstate(&padapter->mlmepriv, _FW_LINKED) == _TRUE) {
		RTW_INFO(ADPT_FMT"-WIFI_FW_AP_STATE - update_beacon\n", ADPT_ARG(padapter));
		update_beacon(padapter, 0, NULL, _TRUE);
	}
	return _TRUE;
}

void rtw_mi_beacon_update(_adapter *padapter)
{
	_rtw_mi_process(padapter, _FALSE, NULL, _rtw_mi_beacon_update);
}

void rtw_mi_buddy_beacon_update(_adapter *padapter)
{
	_rtw_mi_process(padapter, _TRUE, NULL, _rtw_mi_beacon_update);
}

static u8 _rtw_mi_hal_dump_macaddr(_adapter *padapter, void *data)
{
	u8 mac_addr[ETH_ALEN] = {0};

	rtw_hal_get_macaddr_port(padapter, mac_addr);
	RTW_INFO(ADPT_FMT"MAC Address ="MAC_FMT"\n", ADPT_ARG(padapter), MAC_ARG(mac_addr));
	return _TRUE;
}
void rtw_mi_hal_dump_macaddr(_adapter *padapter)
{
	_rtw_mi_process(padapter, _FALSE, NULL, _rtw_mi_hal_dump_macaddr);
}
void rtw_mi_buddy_hal_dump_macaddr(_adapter *padapter)
{
	_rtw_mi_process(padapter, _TRUE, NULL, _rtw_mi_hal_dump_macaddr);
}

static u8 _rtw_mi_xmit_tasklet_schedule(_adapter *padapter, void *data)
{
	if (rtw_txframes_pending(padapter)) {
		/* try to deal with the pending packets */
		tasklet_hi_schedule(&(padapter->xmitpriv.xmit_tasklet));
	}
	return _TRUE;
}
void rtw_mi_xmit_tasklet_schedule(_adapter *padapter)
{
	_rtw_mi_process(padapter, _FALSE, NULL, _rtw_mi_xmit_tasklet_schedule);
}
void rtw_mi_buddy_xmit_tasklet_schedule(_adapter *padapter)
{
	_rtw_mi_process(padapter, _TRUE, NULL, _rtw_mi_xmit_tasklet_schedule);
}

u8 _rtw_mi_busy_traffic_check(_adapter *padapter, void *data)
{
	u32 passtime;
	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
	bool check_sc_interval = *(bool *)data;

	if (pmlmepriv->LinkDetectInfo.bBusyTraffic == _TRUE) {
		if (check_sc_interval) {
			/* Miracast can't do AP scan*/
			passtime = rtw_get_passing_time_ms(pmlmepriv->lastscantime);
			pmlmepriv->lastscantime = rtw_get_current_time();
			if (passtime > BUSY_TRAFFIC_SCAN_DENY_PERIOD) {
				RTW_INFO(ADPT_FMT" bBusyTraffic == _TRUE\n", ADPT_ARG(padapter));
				return _TRUE;
			}
		} else
			return _TRUE;
	}

	return _FALSE;
}

u8 rtw_mi_busy_traffic_check(_adapter *padapter, bool check_sc_interval)
{
	bool in_data = check_sc_interval;

	return _rtw_mi_process(padapter, _FALSE, &in_data, _rtw_mi_busy_traffic_check);
}
u8 rtw_mi_buddy_busy_traffic_check(_adapter *padapter, bool check_sc_interval)
{
	bool in_data = check_sc_interval;

	return _rtw_mi_process(padapter, _TRUE, &in_data, _rtw_mi_busy_traffic_check);
}
static u8 _rtw_mi_check_mlmeinfo_state(_adapter *padapter, void *data)
{
	u32 state = *(u32 *)data;
	struct mlme_ext_priv *mlmeext = &padapter->mlmeextpriv;

	/*if (mlmeext_msr(mlmeext) == state)*/
	if (check_mlmeinfo_state(mlmeext, state))
		return _TRUE;
	else
		return _FALSE;
}

u8 rtw_mi_check_mlmeinfo_state(_adapter *padapter, u32 state)
{
	u32 in_data = state;

	return _rtw_mi_process(padapter, _FALSE, &in_data, _rtw_mi_check_mlmeinfo_state);
}

u8 rtw_mi_buddy_check_mlmeinfo_state(_adapter *padapter, u32 state)
{
	u32 in_data = state;

	return _rtw_mi_process(padapter, _TRUE, &in_data, _rtw_mi_check_mlmeinfo_state);
}


static u8 _rtw_mi_check_fwstate(_adapter *padapter, void *data)
{
	u8 ret = _FALSE;

	sint state = *(sint *)data;

	if ((state == WIFI_FW_NULL_STATE) &&
	    (padapter->mlmepriv.fw_state == WIFI_FW_NULL_STATE))
		ret = _TRUE;
	else if (_TRUE == check_fwstate(&padapter->mlmepriv, state))
		ret = _TRUE;
	return ret;
}
u8 rtw_mi_check_fwstate(_adapter *padapter, sint state)
{
	sint in_data = state;

	return _rtw_mi_process(padapter, _FALSE, &in_data, _rtw_mi_check_fwstate);
}
u8 rtw_mi_buddy_check_fwstate(_adapter *padapter, sint state)
{
	sint in_data = state;

	return _rtw_mi_process(padapter, _TRUE, &in_data, _rtw_mi_check_fwstate);
}

static u8 _rtw_mi_traffic_statistics(_adapter *padapter , void *data)
{
	struct dvobj_priv	*pdvobjpriv = adapter_to_dvobj(padapter);

	/* Tx */
	pdvobjpriv->traffic_stat.tx_bytes += padapter->xmitpriv.tx_bytes;
	pdvobjpriv->traffic_stat.tx_pkts += padapter->xmitpriv.tx_pkts;
	pdvobjpriv->traffic_stat.tx_drop += padapter->xmitpriv.tx_drop;

	/* Rx */
	pdvobjpriv->traffic_stat.rx_bytes += padapter->recvpriv.rx_bytes;
	pdvobjpriv->traffic_stat.rx_pkts += padapter->recvpriv.rx_pkts;
	pdvobjpriv->traffic_stat.rx_drop += padapter->recvpriv.rx_drop;
	return _TRUE;
}
u8 rtw_mi_traffic_statistics(_adapter *padapter)
{
	return _rtw_mi_process(padapter, _FALSE, NULL, _rtw_mi_traffic_statistics);
}

static u8 _rtw_mi_check_miracast_enabled(_adapter *padapter , void *data)
{
	return is_miracast_enabled(padapter);
}
u8 rtw_mi_check_miracast_enabled(_adapter *padapter)
{
	return _rtw_mi_process(padapter, _FALSE, NULL, _rtw_mi_check_miracast_enabled);
}

static u8 _rtw_mi_dynamic_check_timer_handlder(_adapter *adapter, void *data)
{
	rtw_iface_dynamic_check_timer_handlder(adapter);
	return _TRUE;
}
u8 rtw_mi_dynamic_check_timer_handlder(_adapter *padapter)
{
	return _rtw_mi_process(padapter, _FALSE, NULL, _rtw_mi_dynamic_check_timer_handlder);
}

static u8 _rtw_mi_dynamic_chk_wk_hdl(_adapter *adapter, void *data)
{
	rtw_iface_dynamic_chk_wk_hdl(adapter);
	return _TRUE;
}
u8 rtw_mi_dynamic_chk_wk_hdl(_adapter *padapter)
{
	return _rtw_mi_process(padapter, _FALSE, NULL, _rtw_mi_dynamic_chk_wk_hdl);
}

static u8 _rtw_mi_os_xmit_schedule(_adapter *adapter, void *data)
{
	rtw_os_xmit_schedule(adapter);
	return _TRUE;
}
u8 rtw_mi_os_xmit_schedule(_adapter *padapter)
{
	return _rtw_mi_process(padapter, _FALSE, NULL, _rtw_mi_os_xmit_schedule);
}
u8 rtw_mi_buddy_os_xmit_schedule(_adapter *padapter)
{
	return _rtw_mi_process(padapter, _TRUE, NULL, _rtw_mi_os_xmit_schedule);
}

static u8 _rtw_mi_report_survey_event(_adapter *adapter, void *data)
{
	union recv_frame *precv_frame = (union recv_frame *)data;

	report_survey_event(adapter, precv_frame);
	return _TRUE;
}
u8 rtw_mi_report_survey_event(_adapter *padapter, union recv_frame *precv_frame)
{
	return _rtw_mi_process(padapter, _FALSE, precv_frame, _rtw_mi_report_survey_event);
}

static u8 _rtw_mi_tx_beacon_hdl(_adapter *adapter, void *data)
{
	if (check_fwstate(&adapter->mlmepriv, WIFI_AP_STATE) == _TRUE
	    && check_fwstate(&adapter->mlmepriv, WIFI_ASOC_STATE) == _TRUE
	   ) {
		adapter->mlmepriv.update_bcn = _TRUE;
	}
	return _TRUE;
}
u8 rtw_mi_tx_beacon_hdl(_adapter *padapter)
{
	return _rtw_mi_process(padapter, _FALSE, NULL, _rtw_mi_tx_beacon_hdl);
}

static u8 _rtw_mi_stay_in_p2p_mode(_adapter *adapter, void *data)
{
	struct wifidirect_info *pwdinfo = &(adapter->wdinfo);

	if (rtw_p2p_role(pwdinfo) != P2P_ROLE_DISABLE)
		return _TRUE;
	return _FALSE;
}
u8 rtw_mi_stay_in_p2p_mode(_adapter *padapter)
{
	return _rtw_mi_process(padapter, _FALSE, NULL, _rtw_mi_stay_in_p2p_mode);
}

/*API be created temporary for MI, caller is interrupt-handler, PCIE's interrupt handler cannot apply to multi-AP*/
_adapter *rtw_mi_get_ap_adapter(_adapter *padapter)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);
	int i;
	_adapter *iface = NULL;

	for (i = 0; i < dvobj->iface_nums; i++) {
		iface = dvobj->padapters[i];
		if (!iface)
			continue;

		if (check_fwstate(&iface->mlmepriv, WIFI_AP_STATE) == _TRUE
		    && check_fwstate(&iface->mlmepriv, WIFI_ASOC_STATE) == _TRUE)
			break;

	}
	return iface;
}
