/** @file  mlan2040coex.c
 *
 * @brief 11n 20/40 MHz Coex application
 *
 *  Usage:
 *
 *
 * Copyright 2009-2021 NXP
 *
 * NXP CONFIDENTIAL
 * The source code contained or described herein and all documents related to
 * the source code (Materials) are owned by NXP, its
 * suppliers and/or its licensors. Title to the Materials remains with NXP,
 * its suppliers and/or its licensors. The Materials contain
 * trade secrets and proprietary and confidential information of NXP, its
 * suppliers and/or its licensors. The Materials are protected by worldwide
 * copyright and trade secret laws and treaty provisions. No part of the
 * Materials may be used, copied, reproduced, modified, published, uploaded,
 * posted, transmitted, distributed, or disclosed in any way without NXP's prior
 * express written permission.
 *
 * No license under any patent, copyright, trade secret or other intellectual
 * property right is granted to or conferred upon you by disclosure or delivery
 * of the Materials, either expressly, by implication, inducement, estoppel or
 * otherwise. Any license under such intellectual property rights must be
 * express and approved by NXP in writing.
 *
 */
/************************************************************************
Change log:
     06/24/2009: initial version
************************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/wireless.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/ethernet.h>
#include "mlan2040coex.h"
#include "mlan2040misc.h"

/** coex application's version number */
#define COEX_VER "M2.0"

#define MAX_OBSS_SCAN_RETRY 5
/********************************************************
		Local Variables
********************************************************/

static char *usage[] = {"Usage: ",
			"	mlan2040coex [-i <intfname>] [-hvB] ",
			"	-h = help",
			"	-v = version",
			"	-B = run the process in background.",
			"	(if intfname not present then mlan0 assumed)"};

t_s32 sockfd = 0; /**< socket descriptor */
char dev_name[IFNAMSIZ]; /**< device name */

/** Flag: is 2040coex command required */
int coex_cmd_req_flag = FALSE;
/** Flag: is associated */
int assoc_flag = FALSE;
/** Flag: is HT AP */
int is_ht_ap = FALSE;
/** terminate flag */
int terminate_flag = FALSE;

/********************************************************
		Global Variables
********************************************************/
/** OBSS scan parameter of associated AP */
OBSSScanParam_t cur_obss_scan_param;
/** Legacy APs channel list */
chan_intol_t leg_ap_chan_list[WLAN_IOCTL_USER_SCAN_CHAN_MAX];
/** Total number of channel present in Legacy APs channel list */
t_u8 num_leg_ap_chan;
/** Max retry for OBSS scan after association*/
t_u8 max_obss_retry;

/********************************************************
		Local Functions
********************************************************/

/**
 *  @brief Prepare command buffer
 *  @param buffer   Command buffer to be filled
 *  @param cmd      Command id
 *  @param num      Number of arguments
 *  @param args     Arguments list
 *  @return         MLAN_STATUS_SUCCESS
 */
static int prepare_buffer(t_u8 *buffer, char *cmd, t_u32 num, char *args[])
{
	t_u8 *pos = NULL;
	unsigned int i = 0;

	memset(buffer, 0, MRVDRV_SIZE_OF_CMD_BUFFER);

	/* Flag it for our use */
	pos = buffer;
	memcpy((char *)pos, CMD_NXP, strlen(CMD_NXP));
	pos += (strlen(CMD_NXP));

	/* Insert command */
	strncpy((char *)pos, (char *)cmd, strlen(cmd));
	pos += (strlen(cmd));

	/* Insert arguments */
	for (i = 0; i < num; i++) {
		strncpy((char *)pos, args[i], strlen(args[i]));
		pos += strlen(args[i]);
		if (i < (num - 1)) {
			memcpy((char *)pos, " ", strlen(" "));
			pos += 1;
		}
	}

	return MLAN_STATUS_SUCCESS;
}

/**
 *  @brief Process OBSS scan table
 *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
 */
static int process_scantable(void)
{
	char ssid[MRVDRV_MAX_SSID_LENGTH + 1] = {0};
	unsigned int scan_start;
	int idx, i = 0, j, already_listed, ssid_len = 0, ssid_idx;

	t_u8 *pcurrent;
	t_u8 *pnext;
	IEEEtypes_ElementId_e *pelement_id;
	t_u8 *pelement_len, ht_cap_present, intol_bit_is_set;
	int ret = MLAN_STATUS_SUCCESS;
	t_s32 bss_info_len = 0;
	t_u32 fixed_field_length = 0;

	IEEEtypes_CapInfo_t cap_info;
	t_u8 tsf[8];
	t_u16 beacon_interval;
	IEEEtypes_HTCap_t *pht_cap;

	wlan_ioctl_get_scan_table_info *prsp_info;
	wlan_get_scan_table_fixed fixed_fields;

	t_u8 *buffer = NULL;
	struct eth_priv_cmd *cmd = NULL;
	struct ifreq ifr;

	/* Start preparing the buffer */
	/* Initialize buffer */
	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
	if (!buffer) {
		printf("ERR:Cannot allocate buffer for command!\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
	if (!cmd) {
		printf("ERR:Cannot allocate buffer for command!\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	/* Fill up buffer */
#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
	memset(cmd, 0, sizeof(struct eth_priv_cmd));
	memcpy(&cmd->buf, &buffer, sizeof(buffer));
#else
	cmd->buf = buffer;
#endif
	cmd->used_len = 0;
	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;

	memset(&cap_info, 0x00, sizeof(cap_info));
	memset(leg_ap_chan_list, 0, sizeof(leg_ap_chan_list));
	num_leg_ap_chan = 0;

	scan_start = 1;

	do {
		/* buffer = CMD_NXP + <cmd_string>*/
		prepare_buffer(buffer, "getscantable", 0, NULL);
		prsp_info = (wlan_ioctl_get_scan_table_info
				     *)(buffer + strlen(CMD_NXP) +
					strlen("getscantable"));

		prsp_info->scan_number = scan_start;

		/* Perform IOCTL */
		memset(&ifr, 0, sizeof(struct ifreq));
		strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
		ifr.ifr_ifru.ifru_data = (void *)cmd;

		if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
			if (errno == EAGAIN) {
				ret = -EAGAIN;
			} else {
				perror("mlan2040coex");
				fprintf(stderr,
					"mlan2040coex: getscantable fail\n");
				ret = MLAN_STATUS_FAILURE;
			}
			goto done;
		}

		prsp_info = (wlan_ioctl_get_scan_table_info *)buffer;
		pcurrent = 0;
		pnext = prsp_info->scan_table_entry_buf;

		if (scan_start == 1) {
			printf("----------------------------------------------\n");
		}

		for (idx = 0; (unsigned int)idx < prsp_info->scan_number;
		     idx++) {
			/*
			 * Set pcurrent to pnext in case pad bytes are at the
			 * end of the last IE we processed.
			 */
			pcurrent = pnext;

			memcpy((t_u8 *)&fixed_field_length, (t_u8 *)pcurrent,
			       sizeof(fixed_field_length));
			pcurrent += sizeof(fixed_field_length);

			memcpy((t_u8 *)&bss_info_len, (t_u8 *)pcurrent,
			       sizeof(bss_info_len));
			pcurrent += sizeof(bss_info_len);

			memcpy((t_u8 *)&fixed_fields, (t_u8 *)pcurrent,
			       sizeof(fixed_fields));
			pcurrent += fixed_field_length;

			pnext = pcurrent + bss_info_len;

			if (bss_info_len >=
			    (t_s32)(sizeof(tsf) + sizeof(beacon_interval) +
				    sizeof(cap_info))) {
				pcurrent +=
					(sizeof(tsf) + sizeof(beacon_interval) +
					 sizeof(cap_info));
				bss_info_len -=
					(sizeof(tsf) + sizeof(beacon_interval) +
					 sizeof(cap_info));
			}
			ht_cap_present = FALSE;
			intol_bit_is_set = FALSE;
			memset(ssid, 0, MRVDRV_MAX_SSID_LENGTH + 1);
			ssid_len = 0;
			while (bss_info_len >= 2) {
				pelement_id = (IEEEtypes_ElementId_e *)pcurrent;
				pelement_len = pcurrent + 1;
				pcurrent += 2;

				switch (*pelement_id) {
				case SSID:
					if (*pelement_len &&
					    *pelement_len <=
						    MRVDRV_MAX_SSID_LENGTH) {
						memcpy(ssid, pcurrent,
						       *pelement_len);
						ssid_len = *pelement_len;
					}
					break;

				case HT_CAPABILITY:
					pht_cap = (IEEEtypes_HTCap_t *)
						pelement_id;
					ht_cap_present = TRUE;
					if (IS_INTOL_BIT_SET(le16_to_cpu(
						    pht_cap->ht_cap
							    .ht_cap_info))) {
						intol_bit_is_set = TRUE;
					}
					break;
				default:
					break;
				}
				pcurrent += *pelement_len;
				bss_info_len -= (2 + *pelement_len);
			}
			if (!ht_cap_present || intol_bit_is_set) {
				printf("%s AP found on channel number: %-3d ",
				       intol_bit_is_set ? "40 MHZ intolerant" :
							  "Legacy",
				       fixed_fields.channel);
				if (ssid_len) {
					printf("SSID: ");
					/* Print out the ssid or the hex values
					 * if non-printable */
					for (ssid_idx = 0; ssid_idx < ssid_len;
					     ssid_idx++) {
						if (isprint((unsigned char)ssid
								    [ssid_idx])) {
							printf("%c",
							       ssid[ssid_idx]);
						} else {
							printf("\\%02x",
							       ssid[ssid_idx]);
						}
					}
				}
				printf("\n");

				/* Verify that the channel is already listed or
				 * not */
				already_listed = FALSE;
				for (j = 0; j < i; j++) {
					if (leg_ap_chan_list[j].chan_num ==
					    fixed_fields.channel) {
						already_listed = TRUE;
						if (intol_bit_is_set)
							leg_ap_chan_list[j]
								.is_intol_set =
								intol_bit_is_set;
						break;
					}
				}
				if (!already_listed) {
					/* add the channel in list */
					leg_ap_chan_list[i].chan_num =
						fixed_fields.channel;
					leg_ap_chan_list[i].is_intol_set =
						intol_bit_is_set;
					i++;
					coex_cmd_req_flag = TRUE;
					num_leg_ap_chan++;
				}
			}
		}
		scan_start += prsp_info->scan_number;

	} while (prsp_info->scan_number);

done:
	if (cmd)
		free(cmd);
	if (buffer)
		free(buffer);
	return ret;
}

/** BSS Mode any (both infra and adhoc) */
#define BSS_MODE_ANY 3

/** current scan config parameters */
#define SCAN_CFG_PARAMS 7

/** Active : 1 , Passive : 2 */
#define SCAN_TYPE_ACTIVE 1

/**
 *  @brief Issue get scan type command
 *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
 */
static int get_scan_cfg(int *scan_param)
{
	t_u8 *buffer = NULL;
	struct eth_priv_cmd *cmd = NULL;
	struct ifreq ifr;
	int ret = MLAN_STATUS_SUCCESS;

	/* Start preparing the buffer */
	/* Initialize buffer */
	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
	if (!buffer) {
		printf("ERR:Cannot allocate buffer for command!\n");
		return MLAN_STATUS_FAILURE;
	}

	/* buffer = CMD_NXP + <cmd_string>*/
	prepare_buffer(buffer, "scancfg", 0, NULL);

	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
	if (!cmd) {
		printf("ERR:Cannot allocate buffer for command!\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	/* Fill up buffer */
#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
	memset(cmd, 0, sizeof(struct eth_priv_cmd));
	memcpy(&cmd->buf, &buffer, sizeof(buffer));
#else
	cmd->buf = buffer;
#endif
	cmd->used_len = 0;
	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;

	/* Perform IOCTL */
	memset(&ifr, 0, sizeof(struct ifreq));
	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
	ifr.ifr_ifru.ifru_data = (void *)cmd;

	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
		perror("mlan2040coex");
		fprintf(stderr, "mlan2040coex: get_scan_cfg fail\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}
	scan_param = (int *)(buffer);
done:
	if (cmd)
		free(cmd);
	if (buffer)
		free(buffer);

	return ret;
}

/**
 *  @brief Issue OBSS scan command
 *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
 */
static int process_setuserscan(void)
{
	wlan_ioctl_user_scan_cfg scan_req;
	int scan_cfg[SCAN_CFG_PARAMS];
	t_u8 *buffer = NULL, *pos = NULL;
	struct eth_priv_cmd *cmd = NULL;
	struct ifreq ifr;
	int status = 0;

	memset(&scan_req, 0x00, sizeof(scan_req));
	memset(scan_cfg, 0x00, sizeof(scan_cfg));
	coex_cmd_req_flag = FALSE;

	if (get_scan_cfg(scan_cfg) != MLAN_STATUS_SUCCESS) {
		printf("mlan2040coex: scancfg ioctl failure");
		return -EFAULT;
	}

	if (scan_cfg[0] == SCAN_TYPE_ACTIVE)
		scan_req.chan_list[0].scan_time = (t_u32)le16_to_cpu(
			cur_obss_scan_param.obss_scan_active_dwell);
	else
		scan_req.chan_list[0].scan_time = (t_u32)le16_to_cpu(
			cur_obss_scan_param.obss_scan_passive_total);
	scan_req.bss_mode = (scan_cfg[1]) ? scan_cfg[1] : BSS_MODE_ANY;

	/* Start preparing the buffer */
	/* Initialize buffer */
	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
	if (!buffer) {
		printf("ERR:Cannot allocate buffer for command!\n");
		return MLAN_STATUS_FAILURE;
	}

	/* buffer = CMD_NXP + <cmd_string>*/
	prepare_buffer(buffer, "setuserscan", 0, NULL);
	pos = buffer + strlen(CMD_NXP) + strlen("setuserscan");

	/* buffer = buffer + 'scan_req' */
	memcpy(pos, &scan_req, sizeof(wlan_ioctl_user_scan_cfg));

	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
	if (!cmd) {
		printf("ERR:Cannot allocate buffer for command!\n");
		free(buffer);
		return MLAN_STATUS_FAILURE;
	}

	/* Fill up buffer */
#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
	memset(cmd, 0, sizeof(struct eth_priv_cmd));
	memcpy(&cmd->buf, &buffer, sizeof(buffer));
#else
	cmd->buf = buffer;
#endif
	cmd->used_len = 0;
	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;

	/* Perform IOCTL */
	memset(&ifr, 0, sizeof(struct ifreq));
	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
	ifr.ifr_ifru.ifru_data = (void *)cmd;

	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
		perror("mlan2040coex");
		fprintf(stderr, "mlan2040coex: setuserscan fail\n");
		if (cmd)
			free(cmd);
		if (buffer)
			free(buffer);
		return MLAN_STATUS_FAILURE;
	}

	/** process scan results */
	do {
		status = process_scantable();
	} while (status == -EAGAIN);

	if (cmd)
		free(cmd);
	if (buffer)
		free(buffer);

	return MLAN_STATUS_SUCCESS;
}

/**
 *  @brief Display usage
 *
 *  @return       NA
 */
static t_void display_usage(t_void)
{
	t_u32 i;
	for (i = 0; i < NELEMENTS(usage); i++)
		fprintf(stderr, "%s\n", usage[i]);
}

/**
 *  @brief              get connection status
 *
 *  @param data         Pointer to the output buffer holding connection status
 *  @return             MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
 */
static int get_connstatus(int *data)
{
	struct ether_addr apaddr;
	struct ether_addr etherzero = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
	t_u8 *buffer = NULL;
	struct eth_priv_cmd *cmd = NULL;
	struct ifreq ifr;

	/* Initialize buffer */
	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
	if (!buffer) {
		printf("ERR:Cannot allocate buffer for command!\n");
		return MLAN_STATUS_FAILURE;
	}

	/* buffer = CMD_NXP + <cmd_string>*/
	prepare_buffer(buffer, "getwap", 0, NULL);

	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
	if (!cmd) {
		printf("ERR:Cannot allocate buffer for command!\n");
		free(buffer);
		return MLAN_STATUS_FAILURE;
	}

	/* Fill up buffer */
#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
	memset(cmd, 0, sizeof(struct eth_priv_cmd));
	memcpy(&cmd->buf, &buffer, sizeof(buffer));
#else
	cmd->buf = buffer;
#endif
	cmd->used_len = 0;
	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;

	/* Perform IOCTL */
	memset(&ifr, 0, sizeof(struct ifreq));
	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
	ifr.ifr_ifru.ifru_data = (void *)cmd;

	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
		perror("mlan2040coex");
		fprintf(stderr, "mlan2040coex: getwap fail\n");
		if (cmd)
			free(cmd);
		if (buffer)
			free(buffer);
		return MLAN_STATUS_FAILURE;
	}

	memset(&apaddr, 0, sizeof(struct ether_addr));
	memcpy(&apaddr, (struct ether_addr *)(buffer),
	       sizeof(struct ether_addr));

	if (!memcmp(&apaddr, &etherzero, sizeof(struct ether_addr))) {
		/* not associated */
		*data = FALSE;
	} else {
		/* associated */
		*data = TRUE;
	}

	if (buffer)
		free(buffer);
	if (cmd)
		free(cmd);

	return MLAN_STATUS_SUCCESS;
}

/**
 *  @brief Print connect and disconnect event related information
 *
 *  @param buffer   Pointer to received event buffer
 *  @param size     Length of the received event
 *
 *  @return         N/A
 */
static void print_event_drv_connected(t_u8 *buffer, t_u16 size)
{
	struct ether_addr *wap;
	struct ether_addr etherzero = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
	char buf[32];

	wap = (struct ether_addr *)(buffer + strlen(CUS_EVT_AP_CONNECTED));

	if (!memcmp(wap, &etherzero, sizeof(struct ether_addr))) {
		printf("---< Disconnected from AP >---\n");
		memset(&cur_obss_scan_param, 0, sizeof(cur_obss_scan_param));
		assoc_flag = FALSE;
		is_ht_ap = FALSE;
	} else {
		memset(buf, 0, sizeof(buf));
		snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X",
			 wap->ether_addr_octet[0], wap->ether_addr_octet[1],
			 wap->ether_addr_octet[2], wap->ether_addr_octet[3],
			 wap->ether_addr_octet[4], wap->ether_addr_octet[5]);
		printf("---< Connected to AP: %s >---\n", buf);
		/** set TRUE, if connected */
		assoc_flag = TRUE;
	}
}

/**
 *  @brief Parse and print received event information
 *
 *  @param event    Pointer to received event
 *  @param size     Length of the received event
 *  @param evt_conn     A pointer to a output buffer. It sets TRUE when it gets
 *  					the event CUS_EVT_OBSS_SCAN_PARAM, otherwise
 * FALSE
 *  @param if_name  The interface name
 *
 *  @return         N/A
 */
static void print_event(event_header *event, t_u16 size, int *evt_conn,
			char *if_name)
{
	if (!strncmp(CUS_EVT_AP_CONNECTED, (char *)event,
		     strlen(CUS_EVT_AP_CONNECTED))) {
		if (strlen(if_name))
			printf("EVENT for interface %s\n", if_name);
		print_event_drv_connected((t_u8 *)event, size);
		return;
	}
	if (!strncmp(CUS_EVT_OBSS_SCAN_PARAM, (char *)event,
		     strlen(CUS_EVT_OBSS_SCAN_PARAM))) {
		if (strlen(if_name))
			printf("EVENT for interface %s\n", if_name);
		printf("---< %s >---\n", CUS_EVT_OBSS_SCAN_PARAM);
		memset(&cur_obss_scan_param, 0, sizeof(cur_obss_scan_param));
		memcpy(&cur_obss_scan_param,
		       ((t_u8 *)event + strlen(CUS_EVT_OBSS_SCAN_PARAM) + 1),
		       sizeof(cur_obss_scan_param));
		/** set TRUE, since it is an HT AP */
		is_ht_ap = TRUE;
		*evt_conn = TRUE;
		return;
	}
	if (!strncmp(CUS_EVT_BW_CHANGED, (char *)event,
		     strlen(CUS_EVT_BW_CHANGED))) {
		if (strlen(if_name))
			printf("EVENT for interface %s\n", if_name);
		printf("---< %s >---\n", CUS_EVT_BW_CHANGED);
		return;
	}
}

/**
 *  @brief              This function parses for NETLINK events
 *
 *  @param nlh          Pointer to Netlink message header
 *  @param bytes_read   Number of bytes to be read
 *  @param evt_conn     A pointer to a output buffer. It sets TRUE when it gets
 *  					the event CUS_EVT_OBSS_SCAN_PARAM, otherwise
 * FALSE
 *  @return             MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
 */
static int drv_nlevt_handler(struct nlmsghdr *nlh, int bytes_read,
			     int *evt_conn)
{
	int len, plen;
	t_u8 *buffer = NULL;
	t_u32 event_id = 0;
	event_header *event = NULL;
	char if_name[IFNAMSIZ + 1];

	/* Initialize receive buffer */
	buffer = (t_u8 *)malloc(NL_MAX_PAYLOAD);
	if (!buffer) {
		printf("ERR: Could not alloc buffer\n");
		return MLAN_STATUS_FAILURE;
	}
	memset(buffer, 0, NL_MAX_PAYLOAD);

	*evt_conn = FALSE;
	while ((unsigned int)bytes_read >= NLMSG_HDRLEN) {
		len = nlh->nlmsg_len; /* Length of message including header */
		plen = len - NLMSG_HDRLEN;
		if (len > bytes_read || plen < 0) {
			free(buffer);
			/* malformed netlink message */
			return MLAN_STATUS_FAILURE;
		}
		if ((unsigned int)len > NLMSG_SPACE(NL_MAX_PAYLOAD)) {
			printf("ERR:Buffer overflow!\n");
			free(buffer);
			return MLAN_STATUS_FAILURE;
		}
		memset(buffer, 0, NL_MAX_PAYLOAD);
		memcpy(buffer, NLMSG_DATA(nlh), plen);

		if (NLMSG_OK(nlh, len)) {
			memcpy(&event_id, buffer, sizeof(event_id));

			if (((event_id & 0xFF000000) == 0x80000000) ||
			    ((event_id & 0xFF000000) == 0)) {
				event = (event_header *)buffer;
			} else {
				memset(if_name, 0, IFNAMSIZ + 1);
				memcpy(if_name, buffer, IFNAMSIZ);
				event = (event_header *)(buffer + IFNAMSIZ);
			}
		}

		if (event)
			print_event(event, bytes_read, evt_conn, if_name);
		len = NLMSG_ALIGN(len);
		bytes_read -= len;
		nlh = (struct nlmsghdr *)((char *)nlh + len);
	}
	free(buffer);
	return MLAN_STATUS_SUCCESS;
}

/**
 *  @brief Configure and read event data from netlink socket
 *
 *  @param nl_sk        Netlink socket handler
 *  @param msg          Pointer to message header
 *  @param ptv          Pointer to struct timeval
 *
 *  @return             Number of bytes read or MLAN_STATUS_FAILURE
 */
static int read_event(int nl_sk, struct msghdr *msg, struct timeval *ptv)
{
	int count = -1;
	int ret = MLAN_STATUS_FAILURE;
	fd_set rfds;

	/* Setup read fds and initialize event buffers */
	FD_ZERO(&rfds);
	FD_SET(nl_sk, &rfds);

	/* Wait for reply */
	ret = select(nl_sk + 1, &rfds, NULL, NULL, ptv);

	if (ret == MLAN_STATUS_FAILURE) {
		/* Error */
		terminate_flag++;
		ptv->tv_sec = DEFAULT_SCAN_INTERVAL;
		ptv->tv_usec = 0;
		goto done;
	} else if (!ret) {
		if (assoc_flag && is_ht_ap) {
			/** Issue OBSS scan */
			process_setuserscan();
			/** Invoke 2040coex command, if any legacy AP found or
			 *  any AP has 40MHz intolarent bit set */
			if (coex_cmd_req_flag)
				invoke_coex_command();
		}
		if (assoc_flag && is_ht_ap) {
			/* Timeout. Try again after BSS channel width triger
			   scan
			   interval when the STA is connected with a HT AP */
			ptv->tv_sec = (t_u32)le16_to_cpu(
				cur_obss_scan_param
					.bss_chan_width_trigger_scan_int);
		} else {
			/* Timeout. Try again after default duration when the
			   STA is not connected with a HT AP */
			ptv->tv_sec = DEFAULT_SCAN_INTERVAL;
		}
		ptv->tv_usec = 0;
		goto done;
	}

	if (!FD_ISSET(nl_sk, &rfds)) {
		/* Unexpected error. Try again */
		ptv->tv_sec = DEFAULT_SCAN_INTERVAL;
		ptv->tv_usec = 0;
		goto done;
	}
	/* Success */
	count = recvmsg(nl_sk, msg, 0);

done:
	return count;
}

/**
 *  @brief Run the application
 *
 *  @param nl_sk    Netlink socket
 *
 *  @return         N/A
 */
static void run_app(int nl_sk)
{
	struct timeval tv;
	int bytes_read, evt_conn;
	struct msghdr msg;
	struct sockaddr_nl dest_addr;
	struct nlmsghdr *nlh;
	struct iovec iov;
	t_u8 ret = 0;
	/** Get connection status */
	if (get_connstatus(&assoc_flag) == MLAN_STATUS_FAILURE)
		return;

	/* Initialize timeout value */
	tv.tv_sec = DEFAULT_SCAN_INTERVAL;
	tv.tv_usec = 0;

	/* Initialize netlink header */
	nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(NL_MAX_PAYLOAD));
	if (!nlh) {
		printf("ERR: Could not allocate space for netlink header\n");
		goto done;
	}
	memset(nlh, 0, NLMSG_SPACE(NL_MAX_PAYLOAD));
	/* Fill the netlink message header */
	nlh->nlmsg_len = NLMSG_SPACE(NL_MAX_PAYLOAD);
	nlh->nlmsg_pid = getpid(); /* self pid */
	nlh->nlmsg_flags = 0;

	/* Initialize I/O vector */
	memset(&iov, 0, sizeof(struct iovec));
	iov.iov_base = (void *)nlh;
	iov.iov_len = nlh->nlmsg_len;

	/* Set destination address */
	memset(&dest_addr, 0, sizeof(struct sockaddr_nl));
	dest_addr.nl_family = AF_NETLINK;
	dest_addr.nl_pid = 0; /* Kernel */
	dest_addr.nl_groups = NL_MULTICAST_GROUP;

	/* Initialize message header */
	memset(&msg, 0, sizeof(struct msghdr));
	msg.msg_name = (void *)&dest_addr;
	msg.msg_namelen = sizeof(dest_addr);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	while (!terminate_flag) {
		/* event buffer is received for all the interfaces */
		bytes_read = read_event(nl_sk, &msg, &tv);
		/* handle only NETLINK events here */
		drv_nlevt_handler((struct nlmsghdr *)nlh, bytes_read,
				  &evt_conn);
		if (assoc_flag && is_ht_ap) {
			/** If the event is connected with an HT AP then issue
			 * OBSS scan immediately */
			if (evt_conn) {
				while (max_obss_retry < MAX_OBSS_SCAN_RETRY) {
					/** Issue OBSS scan */
					max_obss_retry++;
					ret = process_setuserscan();
					if (ret == MLAN_STATUS_SUCCESS) {
						/** Invoke 2040coex command, if
						 * any legacy AP found or
						 *  any AP has 40MHz intolarent
						 * bit set */
						if (coex_cmd_req_flag)
							invoke_coex_command();
						break;
					}
					sleep(1);
				}
				max_obss_retry = 0;
			}
			tv.tv_sec = (t_u32)le16_to_cpu(
				cur_obss_scan_param
					.bss_chan_width_trigger_scan_int);
		} else {
			tv.tv_sec = DEFAULT_SCAN_INTERVAL;
		}
		tv.tv_usec = 0;
	}

done:
	//    if (nl_sk > 0)
	//        close(nl_sk);
	if (nlh)
		free(nlh);
	return;
}

/**
 *  @brief Determine the netlink number
 *
 *  @return         Netlink number to use
 */
static int get_netlink_num(int dev_index)
{
	FILE *fp = NULL;
	int netlink_num = -1;
	char str[64];
	char *srch = "netlink_num";
	char filename[64];
	t_u8 *buffer = NULL;
	struct eth_priv_cmd *cmd = NULL;
	struct ifreq ifr;

	/* if dev_index is specified by user */
	if (dev_index >= 0) {
		/* Try to open old driver proc: /proc/mwlan/configX first */
		if (dev_index == 0)
			strncpy(filename, "/proc/mwlan/config",
				sizeof(filename));
		else if (dev_index > 0)
			snprintf(filename, sizeof(filename),
				 "/proc/mwlan/config%d", dev_index);
		fp = fopen(filename, "r");
		if (!fp) {
			/* Try to open multi-adapter driver proc:
			 * /proc/mwlan/adapterX/config if old proc access fail
			 */
			snprintf(filename, sizeof(filename),
				 "/proc/mwlan/adapter%d/config", dev_index);
			fp = fopen(filename, "r");
		}

		if (fp) {
			while (fgets(str, sizeof(str), fp)) {
				if (strncmp(str, srch, strlen(srch)) == 0) {
					netlink_num =
						atoi(str + strlen(srch) + 1);
					break;
				}
			}
			fclose(fp);
		} else {
			return -1;
		}
	} else {
		/* Start preparing the buffer */
		/* Initialize buffer */
		buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
		if (!buffer) {
			printf("ERR:Cannot allocate buffer for command!\n");
			return -1;
		}
		/* buffer = CMD_NXP + <cmd_string>*/
		prepare_buffer(buffer, "getnlnum", 0, NULL);

		cmd = (struct eth_priv_cmd *)malloc(
			sizeof(struct eth_priv_cmd));
		if (!cmd) {
			printf("ERR:Cannot allocate buffer for command!\n");
			goto done;
		}

		/* Fill up buffer */
#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
		memset(cmd, 0, sizeof(struct eth_priv_cmd));
		memcpy(&cmd->buf, &buffer, sizeof(buffer));
#else
		cmd->buf = buffer;
#endif
		cmd->used_len = 0;
		cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;

		/* Perform IOCTL */
		memset(&ifr, 0, sizeof(struct ifreq));
		strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
		ifr.ifr_ifru.ifru_data = (void *)cmd;

		if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
			perror("mlan2040coex");
			fprintf(stderr, "mlan2040coex: getnlnum fail\n");
			goto done;
		}
		netlink_num = *(int *)(buffer);
	}

done:
	if (cmd)
		free(cmd);
	if (buffer)
		free(buffer);
	printf("Netlink number = %d\n", netlink_num);
	return netlink_num;
}

/**
 *  @brief opens netlink socket to receive NETLINK events
 *  @return  socket id --success, otherwise--MLAN_STATUS_FAILURE
 */
static int open_netlink(int dev_index)
{
	int sk = -1;
	struct sockaddr_nl src_addr;
	int netlink_num = 0;

	netlink_num = get_netlink_num(dev_index);
	if (netlink_num < 0) {
		printf("ERR:Could not get netlink socket. Invalid device number.\n");
		return sk;
	}

	/* Open netlink socket */
	sk = socket(PF_NETLINK, SOCK_RAW, netlink_num);
	if (sk < 0) {
		printf("ERR:Could not open netlink socket.\n");
		return sk;
	}

	/* Set source address */
	memset(&src_addr, 0, sizeof(src_addr));
	src_addr.nl_family = AF_NETLINK;
	src_addr.nl_pid = getpid();
	src_addr.nl_groups = NL_MULTICAST_GROUP;

	/* Bind socket with source address */
	if (bind(sk, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) {
		printf("ERR:Could not bind socket!\n");
		close(sk);
		return -1;
	}
	return sk;
}

/**
 *  @brief Terminate signal handler
 *  @param signal   Signal to handle
 *  @return         NA
 */
static t_void terminate_handler(int signal)
{
	printf("Stopping application.\n");
#if DEBUG
	printf("Process ID of process killed = %d\n", getpid());
#endif
	terminate_flag = TRUE;
}

/********************************************************
		Global Functions
********************************************************/
/**
 *  @brief Process host command
 *  @param hostcmd_idx  Host command index
 *  @param chan_list    A pointer to a channel list
 *  @param chan_num     Number of channels in the channel list
 *  @param reg_class    Regulatory class of the channels
 *  @param is_intol_ap_present Flag:is there any 40 MHz intolerant AP is present
 * or not
 *
 *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
 */
int process_host_cmd(int hostcmd_idx, t_u8 *chan_list, t_u8 chan_num,
		     t_u8 reg_class, t_u8 is_intol_ap_present)
{
	int ret = MLAN_STATUS_SUCCESS;
	t_u8 *buffer = NULL;
	struct eth_priv_cmd *cmd = NULL;
	struct ifreq ifr;

	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
	if (!buffer) {
		printf("ERR:Cannot allocate buffer for command!\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	prepare_buffer(buffer, "hostcmd", 0, NULL);
	switch (hostcmd_idx) {
	case CMD_2040COEX:
		prepare_coex_cmd_buff(
			buffer + strlen(CMD_NXP) + strlen("hostcmd"), chan_list,
			chan_num, reg_class, is_intol_ap_present);
		break;
	default:
		break;
	}

	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
	if (!cmd) {
		printf("ERR:Cannot allocate buffer for command!\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	/* Fill up buffer */
#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
	memset(cmd, 0, sizeof(struct eth_priv_cmd));
	memcpy(&cmd->buf, &buffer, sizeof(buffer));
#else
	cmd->buf = buffer;
#endif
	cmd->used_len = 0;
	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;

	/* Perform IOCTL */
	memset(&ifr, 0, sizeof(struct ifreq));
	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
	ifr.ifr_ifru.ifru_data = (void *)cmd;

	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
		perror("mlan2040coex");
		fprintf(stderr, "mlan2040coex: hostcmd fail\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	ret = process_host_cmd_resp("hostcmd", buffer);

done:
	if (cmd)
		free(cmd);
	if (buffer)
		free(buffer);
	return ret;
}

/**
 *  @brief Check the STA is 40 MHz intolerant or not
 *  @param intol	Flag: TRUE when the STA is 40 MHz intolerant, otherwise
 * FALSE
 *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
 */
int is_intolerant_sta(int *intol)
{
	t_u8 *buffer = NULL;
	struct eth_priv_cmd *cmd = NULL;
	struct ifreq ifr;
	int htcap_info, ret = MLAN_STATUS_SUCCESS;

	*intol = FALSE;

	/* Initialize buffer */
	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
	if (!buffer) {
		printf("ERR:Cannot allocate buffer for command!\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	/* buffer = CMD_NXP + <cmd_string>*/
	prepare_buffer(buffer, "htcapinfo", 0, NULL);

	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
	if (!cmd) {
		printf("ERR:Cannot allocate buffer for command!\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	/* Fill up buffer */
#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
	memset(cmd, 0, sizeof(struct eth_priv_cmd));
	memcpy(&cmd->buf, &buffer, sizeof(buffer));
#else
	cmd->buf = buffer;
#endif
	cmd->used_len = 0;
	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;

	/* Perform IOCTL */
	memset(&ifr, 0, sizeof(struct ifreq));
	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
	ifr.ifr_ifru.ifru_data = (void *)cmd;

	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
		perror("mlan2040coex");
		fprintf(stderr, "mlan2040coex: htcapinfo fail\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	htcap_info = *((int *)(buffer));

	if (htcap_info & MBIT(8))
		*intol = TRUE;

done:
	if (cmd)
		free(cmd);
	if (buffer)
		free(buffer);
	return ret;
}

/**
 *  @brief get region code
 *  @param reg_code	Pointer to region code
 *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
 */
int get_region_code(int *reg_code)
{
	t_u8 *buffer = NULL;
	struct eth_priv_cmd *cmd = NULL;
	struct ifreq ifr;
	int ret = MLAN_STATUS_SUCCESS;

	/* Initialize buffer */
	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
	if (!buffer) {
		printf("ERR:Cannot allocate buffer for command!\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	/* buffer = CMD_NXP + <cmd_string>*/
	prepare_buffer(buffer, "regioncode", 0, NULL);

	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
	if (!cmd) {
		printf("ERR:Cannot allocate buffer for command!\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	/* Fill up buffer */
#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
	memset(cmd, 0, sizeof(struct eth_priv_cmd));
	memcpy(&cmd->buf, &buffer, sizeof(buffer));
#else
	cmd->buf = buffer;
#endif
	cmd->used_len = 0;
	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;

	/* Perform IOCTL */
	memset(&ifr, 0, sizeof(struct ifreq));
	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
	ifr.ifr_ifru.ifru_data = (void *)cmd;

	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
		perror("mlan2040coex");
		fprintf(stderr, "mlan2040coex: regioncode fail\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	memcpy(reg_code, buffer, sizeof(int));
done:
	if (cmd)
		free(cmd);
	if (buffer)
		free(buffer);
	return ret;
}

/** No option */
#define NO_OPTION -1

/**
 *  @brief Entry function for coex
 *  @param argc		number of arguments
 *  @param argv     A pointer to arguments array
 *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
 */
int main(int argc, char *argv[])
{
	char ifname[IFNAMSIZ + 1] = "mlan0";
	int c, daemonize = FALSE;
	t_s32 nl_sk = 0; /**< netlink socket descriptor to receive an event */
	int dev_index = -1; /** initialise with -1 to open multiple NETLINK
			       Sockets */

	char temp[2];
	int arg_count = 0;
	int ifname_given = FALSE;

	memset(dev_name, 0, sizeof(dev_name));

	for (;;) {
		c = getopt(argc, argv, "Bhi:vd:");
		/* check if all command-line options have been parsed */
		if (c == NO_OPTION)
			break;

		switch (c) {
		case 'B':
			daemonize = TRUE;
			break;
		case 'h':
			display_usage();
			return MLAN_STATUS_SUCCESS;
		case 'v':
			fprintf(stdout,
				"NXP 20/40coex application version %s\n",
				COEX_VER);
			return MLAN_STATUS_SUCCESS;
		case 'i':
			ifname_given = TRUE;
			if (strlen(optarg) < IFNAMSIZ)
				strncpy(ifname, optarg, IFNAMSIZ - 1);
			else {
				fprintf(stdout, "Interface name too long\n");
				display_usage();
				return MLAN_STATUS_SUCCESS;
			}
			arg_count += 1;
			break;
		case 'd':
			strncpy(temp, optarg, sizeof(temp) - 1);
			if (isdigit((unsigned char)temp[0]))
				dev_index = atoi(temp);
			arg_count += 1;
			break;
		default:
			fprintf(stdout, "Invalid argument\n");
			display_usage();
			return MLAN_STATUS_SUCCESS;
		}
	}

	if (!ifname_given) {
		snprintf(ifname, sizeof(ifname), "mlan%d", dev_index);
	}

	if (optind < argc) {
		fputs("Too many arguments.\n", stderr);
		display_usage();
		goto done;
	}

	strncpy(dev_name, ifname, IFNAMSIZ - 1);

	/* create a socket */
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		fprintf(stderr, "mlan2040coex: Cannot open socket.\n");
		goto done;
	}

	/* create netlink sockets and bind them with app side addr */
	nl_sk = open_netlink(dev_index);

	if (nl_sk < 0) {
		fprintf(stderr, "mlan2040coex: Cannot open netlink socket.\n");
		goto done;
	}

	signal(SIGHUP, terminate_handler); /* catch hangup signal */
	signal(SIGTERM, terminate_handler); /* catch kill signal */
	signal(SIGINT, terminate_handler); /* catch kill signal */
	signal(SIGALRM, terminate_handler); /* catch kill signal */

	/** Make the process background-process */
	if (daemonize) {
		if (daemon(0, 0))
			fprintf(stderr, "mlan2040coex: Cannot start daemon\n");
	}

	/** run the application */
	run_app(nl_sk);

done:
	if (sockfd > 0)
		close(sockfd);
	if (nl_sk > 0)
		close(nl_sk);

	return MLAN_STATUS_SUCCESS;
}
