/** @file  mlanwls.c
 *
 * @brief 11mc/11az Wifi location services application
 *
 *  Usage:
 *
 *
 * Copyright 2022 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:
     01/24/2022: 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 "mlanutl.h"
#include "mlanwls.h"

/** WLS application's version number */
#define WLS_VER "M1.0"

/*Command arguments index*/
#define NXP_ADDR "530 Holgerway SanJose"

#define PROTO_DOT11AZ_NTB 1
#define PROTO_DOT11AZ_TB 2
#define PROTO_DOT11MC 0

#define FTM_SUBCMD_INDEX 3
#define FTM_CFG_SET_CMD_LEN 6
#define FTM_CFG_GET_CMD_LEN 4
#define FTM_CFG_PROTOCOL_INDEX 4
#define FTM_CFG_FILE_ARG_INDEX 5

#define FTM_SESSION_SUBCMD_LEN 8
#define FTM_SESSION_SUBCMD_NONSTOP_LEN 7
#define FTM_SESSION_ACTION_OFFSET 4
#define FTM_SESSION_CHANNEL_OFFSET 5
#define FTM_SESSION_PEER_ADDR_OFFSET 6
#define FTM_SESSION_LOOP_OFFSET 7

#define FTM_ACTION_START 1
#define FTM_ACTION_STOP 2

/********************************************************
		Functions Declarations
********************************************************/

t_u8 hexc2bin(char chr);
t_u32 a2hex(char *s);
t_u32 a2hex_or_atoi(char *value);
t_void hexdump(char *prompt, t_void *p, t_s32 len, char delim);
extern char *config_get_line(char *s, int size, FILE *stream, int *line,
			     char **_pos);
int parse_line(char *line, char *args[], t_u16 args_count);
int mac2raw(char *mac, t_u8 *raw);

static t_void display_help(t_u32 n, char **data);
static int mlanwls_prepare_buffer(t_u8 *buffer, char *cmd, t_u32 num,
				  char *args[]);
static int mlanwls_send_ioctl(t_u8 *cmd_buf);
static void mlanwls_event_handler(int nl_sk);
static int mlanwls_init(void);
static int mlanwls_read_ftm_config(char *file_name);

static int process_subcommand(int argc, char *argv[]);
static int process_ftm_session_cfg(int argc, char *argv[], void *param);
static int process_ftm_session_ctrl(int argc, char *argv[], void *param);

static int process_dot11az_ranging_cfg(int argc, char *argv[], void *param);
static int process_dot11mc_ftm_cfg(int argc, char *argv[], void *param);
static int process_ftm_start(int argc, char *argv[], void *param);
static int process_ftm_stop(int argc, char *argv[], void *param);
static int process_ftm_hostcmd_resp(char *cmd_name, t_u8 *buf);
static int process_ftm_complete_event(t_u8 *buffer, t_u16 size, char *if_name);

static int get_connstatus(int *data);
static void print_event_drv_connected(t_u8 *buffer, t_u16 size);
static int drv_nlevt_handler(struct nlmsghdr *nlh, int bytes_read,
			     int *evt_conn);
static int read_event(int nl_sk, struct msghdr *msg, struct timeval *ptv);
static int get_netlink_num(int dev_index);
static int open_netlink(int dev_index);
static t_void mlanwls_terminate_handler(int signal);

/********************************************************
		Local Variables
********************************************************/
/** WLS app usage */
static char *mlanwls_help[] = {
	"Usage: ",
	"	mlanutl mlan0 ftm <subcommand> [<arg>]",
	"	where subcommand is,",
	"		session_cfg  [<ftm_protocol> <config_file>]",
	"		session_ctrl [<action> <chan> <mac_address> <loop_cnt>]",
	"	For help on each subcommand,",
	"		mlanutl mlan0 ftm <subcommand> -h"
	""};

static char *ftm_session_cfg_help[] = {
	"Usage: ",
	"	mlanutl mlan0 ftm session_cfg [<ftm_protocol> <config_file>] ",
	" 	where,",
	"	<ftm_protocol> : 0:Dot11mc, 1:Dot11az_ntb, 2:Dot11az_tb",
	"	<config_file> : Config file with Dot11mc / dot11az parameters",
	"",
	"	eg:	mlanutl mlan0 ftm session_cfg 0 config/ftm.conf - Sets dot11mc FTM session params from config file",
	"		mlanutl mlan0 ftm session_cfg 1 config/ftm.conf - Sets dot11az ntb session params from config file",
	"		mlanutl mlan0 ftm session_cfg 2 config/ftm.conf - Sets dot11az tb ranging session params from config file",
	" "};

static char *ftm_session_ctrl_help[] = {
	"Usage: ",
	"	mlanutl mlan0 ftm session_ctrl [<action> <chan> <mac_address> <loop_cnt>] ",
	" 	where,",
	"	<action> : 1: Start, 2: Stop ",
	"	<loop_cnt> : number of ftm sessions to run repeatedly ( default:1,  0:non-stop, n>1: n times)",
	"	<chan> 	: Channel on which FTM must be started",
	"	<mac_address> : Mac address of the peer with whom FTM session is required",
	" ",
	"	eg: mlanutl mlan0 ftm session_ctrl 1 6 00:50:43:20:bc:4  	- Starts FTM session and runs it once",
	"		mlanutl mlan0 ftm session_ctrl 1 6 00:50:43:20:bc:4 2 	- Starts FTM session and runs it for 2 cycles",
	"		mlanutl mlan0 ftm session_ctrl 1 6 00:50:43:20:bc:4 0	- Runs FTM session non stop until user terminate",
	"		mlanutl mlan0 ftm session_ctrl 2 						- Stop the FTM session",
	" "};

/** WLS app command ID */
enum user_command_id {
	FTM_SESSION_CFG_CMD_ID = 0,
	FTM_SESSION_CTRL_CMD_ID,
};

/** WLS app command table */
static wls_app_command_table wls_app_command[] = {
	{FTM_SESSION_CFG_CMD_ID, "session_cfg", process_ftm_session_cfg,
	 ftm_session_cfg_help},
	{FTM_SESSION_CTRL_CMD_ID, "session_ctrl", process_ftm_session_ctrl,
	 ftm_session_ctrl_help}};

/** WLS app data*/
wls_app_data_t gwls_data;

/**DBG Printf*/
#define DBG_LOG(x)                                                             \
	if (gwls_data.debug_level == 2) {                                      \
		printf(x);                                                     \
	}
#define DBG_ERROR(x)                                                           \
	if (gwls_data.debug_level >= 0) {                                      \
		printf(x);                                                     \
	}
#define PRINT_CFG(x, y) printf(x, y)

/********************************************************
		Global Variables
********************************************************/
/**< socket descriptor */
t_s32 nl_sk = 0; /**< netlink socket descriptor to receive an event */

/** Flag: is associated */
int assoc_flag = FALSE;

/** terminate flag */
int mlanwls_terminate_flag = FALSE;

/********************************************************
		Local Functions
********************************************************/
/**
 *  @brief Display help text
 *
 *  @return       NA
 */
static t_void display_help(t_u32 n, char **data)
{
	t_u32 i;
	for (i = 0; i < n; i++)
		fprintf(stderr, "%s\n", data[i]);
}

/**
 *  @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 mlanwls_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 host_cmd response
 *
 *  @param cmd_name	The command string
 *  @param buf		A pointer to the response buffer
 *
 *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
 */
static int process_ftm_hostcmd_resp(char *cmd_name, t_u8 *buf)
{
	t_u32 hostcmd_size = 0;
	HostCmd_DS_GEN *hostcmd = NULL;
	int ret = MLAN_STATUS_SUCCESS;
	hostcmd_ds_ftm_session_cmd *phostcmd = NULL;

	buf += strlen(CMD_NXP) + strlen(cmd_name);
	memcpy((t_u8 *)&hostcmd_size, buf, sizeof(t_u32));
	buf += sizeof(t_u32);

	hostcmd = (HostCmd_DS_GEN *)buf;
	hostcmd->command = le16_to_cpu(hostcmd->command);
	hostcmd->size = le16_to_cpu(hostcmd->size);

	hostcmd->command &= ~HostCmd_RET_BIT;

	switch (hostcmd->command) {
	case HostCmd_CMD_FTM_SESSION_CTRL:
		phostcmd = (hostcmd_ds_ftm_session_cmd *)buf;
		if (!le16_to_cpu(phostcmd->cmd_hdr.result)) {
			if (phostcmd->cmd.ftm_session_ctrl.action == 1) {
				printf("[INFO] FTM Session Control Started on %d channel with Peer %02X:%02X:%02X:%02X:%02X:%02X \n",
				       phostcmd->cmd.ftm_session_ctrl.chan,
				       phostcmd->cmd.ftm_session_ctrl
					       .peer_mac[0],
				       phostcmd->cmd.ftm_session_ctrl
					       .peer_mac[1],
				       phostcmd->cmd.ftm_session_ctrl
					       .peer_mac[2],
				       phostcmd->cmd.ftm_session_ctrl
					       .peer_mac[3],
				       phostcmd->cmd.ftm_session_ctrl
					       .peer_mac[4],
				       phostcmd->cmd.ftm_session_ctrl
					       .peer_mac[5]);
			} else if (phostcmd->cmd.ftm_session_ctrl.action == 2) {
				printf("[INFO] FTM Session Stopped\n");
			} else {
				printf("[ERROR] Hostcmd action not supported\n");
			}
		} else {
			printf("[ERROR] Hostcmd failed: ReturnCode=%#04x, Result=%#04x\n",
			       le16_to_cpu(phostcmd->cmd_hdr.command),
			       le16_to_cpu(phostcmd->cmd_hdr.result));
			ret = MLAN_STATUS_FAILURE;
			goto done;
		}
		break;

	case HostCmd_CMD_FTM_SESSION_CFG:
		phostcmd = (hostcmd_ds_ftm_session_cmd *)buf;
		if ((le16_to_cpu(phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az
					 .range_tlv.type) ==
		     FTM_NTB_RANGING_CFG_TLV_ID) ||
		    (le16_to_cpu(phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az
					 .range_tlv.type) ==
		     FTM_TB_RANGING_CFG_TLV_ID)) {
			if (le16_to_cpu(phostcmd->cmd.ftm_session_cfg.action) ==
			    MLAN_ACT_GET) {
				/* Get */
				printf("\n\nGet Ranging Parameters: \n");
			} else {
				/* Set */
				printf("\n\nSet Ranging Parameters: \n");
			}
			printf("---------------------------------\n");
			printf("format_bw:%d \n",
			       phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az
				       .range_tlv.val.format_bw);
			printf("az_measurement_freq:%d \n",
			       phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az
				       .range_tlv.val.az_measurement_freq);
			printf("az_number_of_measurements:%d \n",
			       phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az
				       .range_tlv.val.az_number_of_measurements);
			printf("max_i2r_sts_upto80:%d \n",
			       phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az
				       .range_tlv.val.max_i2r_sts_upto80);
			printf("max_r2i_sts_upto80:%d \n",
			       phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az
				       .range_tlv.val.max_r2i_sts_upto80);
			printf("i2r_lmr_feedback:%d \n\n",
			       phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az
				       .range_tlv.val.i2r_lmr_feedback);
		} else if ((le16_to_cpu(phostcmd->cmd.ftm_session_cfg.tlv
						.cfg_11mc.sess_tlv.type) ==
			    FTM_SESSION_CFG_INITATOR_TLV_ID)) {
			if (le16_to_cpu(phostcmd->cmd.ftm_session_cfg.action) ==
			    MLAN_ACT_GET) {
				/* Get */
				printf("\n\nGet dot11mc ftm session config: \n");
			} else {
				/* Set */
				printf("\n\nSet dot11mc ftm session config: \n");
			}
			printf("---------------------------------\n");
			printf("burst_exponent:%d \n",
			       phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc
				       .sess_tlv.val.burst_exponent);
			printf("burst_duration:%d \n",
			       phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc
				       .sess_tlv.val.burst_duration);
			printf("min_delta_FTM:%d \n",
			       phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc
				       .sess_tlv.val.min_delta_FTM);
			printf("is_ASAP:%d \n",
			       phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc
				       .sess_tlv.val.is_ASAP);
			printf("per_burst_FTM:%d \n",
			       phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc
				       .sess_tlv.val.per_burst_FTM);
			printf("channel_spacing:%d \n",
			       phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc
				       .sess_tlv.val.channel_spacing);
			printf("burst_period:%d \n\n",
			       phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc
				       .sess_tlv.val.burst_period);
		} else {
			printf("[ERROR] Hostcmd failed: Invalid TLV ReturnCode=%#04x, Result=%#04x\n",
			       le16_to_cpu(phostcmd->cmd_hdr.command),
			       le16_to_cpu(phostcmd->cmd_hdr.result));
			ret = MLAN_STATUS_FAILURE;
			goto done;
		}
		break;

	default:
		ret = MLAN_STATUS_FAILURE;
		printf("[ERROR] Invalid Hostcmd %x\n",
		       phostcmd->cmd_hdr.command);
		break;
	}

done:
	return ret;
}

/**
 *  @brief Send hostcmd IOCTL to driver
 *  @param cmd_buf  pointer to Host Command buffer
 *
 *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
 */

static int mlanwls_send_ioctl(t_u8 *cmd_buf)
{
	struct ifreq ifr;
	struct eth_priv_cmd *cmd = NULL;
	int ret = MLAN_STATUS_SUCCESS;

	/*hexdump((void *) cmd_buf, MRVDRV_SIZE_OF_CMD_BUFFER, ' ');*/

	if (!cmd_buf) {
		printf("ERR:IOCTL Failed due to null cmd buffer!\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, cmd_buf, sizeof(cmd_buf));
#else
	cmd->buf = cmd_buf;
#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("mlanwls");
		fprintf(stderr, "IOCTL fail\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	/* Process result */
	ret = process_ftm_hostcmd_resp("hostcmd", cmd_buf);

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

/**
 *  @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) {
		DBG_ERROR("[ERROR] Cannot allocate buffer for command!\n");
		return MLAN_STATUS_FAILURE;
	}

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

	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
	if (!cmd) {
		DBG_ERROR("[ERROR] 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)) {
		DBG_ERROR("[ERROR] mlanwls: 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));
	memcpy(&gwls_data.ap_mac[0], (t_u8 *)(buffer),
	       sizeof(gwls_data.ap_mac));

	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 Parse FTM complete event data
 *
 *  @param buffer   Pointer to received event buffer
 *  @param size     Length of the received event data
 *  @return         N/A
 */
static int process_ftm_complete_event(t_u8 *buffer, t_u16 size, char *if_name)
{
	wls_event_t *ftm_event = NULL;
	int ret = MLAN_STATUS_SUCCESS;
	float distance = 0.0;

	if (!buffer) {
		DBG_ERROR("[ERROR] Event buffer null\n");
		return MLAN_STATUS_FAILURE;
	}
	ftm_event = (wls_event_t *)buffer;

	printf("[INFO] Event received for interface %s\n", if_name);
	printf("[INFO] EventID: 0x%x SubeventID:%d\n", ftm_event->event_id,
	       ftm_event->sub_event_id);
	hexdump("EventData:", (void *)buffer, size, ' ');

	switch (ftm_event->sub_event_id) {
	case WLS_SUB_EVENT_FTM_COMPLETE:

		printf("\n\nFTM Session Complete:\n");
		printf("=====================\n");
		printf("Average RTT: %d ns\n",
		       ftm_event->e.ftm_complete.avg_rtt);
		printf("Average Clockoffset:%d ns\n",
		       ftm_event->e.ftm_complete.avg_clk_offset);

		distance = ((ftm_event->e.ftm_complete.avg_clk_offset / 2) *
			    (0.0003));
		printf("Distance: %f meters\n\n", distance);

		gwls_data.loop_cnt--;
		if ((gwls_data.loop_cnt > 0) || (gwls_data.run_nonstop)) {
			/*Stop and restart the FTM*/
			process_ftm_start(0, NULL, &gwls_data);
		} else {
			process_ftm_stop(0, NULL, &gwls_data);
			gwls_data.terminate_app = TRUE;
		}

		break;
	case WLS_SUB_EVENT_RADIO_RECEIVED:
		printf("WLS_SUB_EVENT_RADIO_RECEIVED\n");
		break;
	case WLS_SUB_EVENT_RADIO_RPT_RECEIVED:
		printf("WLS_SUB_EVENT_RADIO_RPT_RECEIVED\n");
		break;
	case WLS_SUB_EVENT_ANQP_RESP_RECEIVED:
		printf("WLS_SUB_EVENT_ANQP_RESP_RECEIVED\n");
		break;
	default:
		printf("[ERROR] Unknown sub event\n");
		break;
	}
	return ret;
}

/**
 *  @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");
		assoc_flag = 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              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);
			}
		}

		/*Prints the events*/
		if (event) {
			switch (event->event_id) {
			case EVENT_WLS_FTM_COMPLETE:
				process_ftm_complete_event((t_u8 *)event,
							   bytes_read, if_name);
				break;
			default:
				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,
								  bytes_read);
				}
				break;
			}
		}
		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 */
		mlanwls_terminate_flag++;
		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 mlanwls_event_handler(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;

	/* 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("[ERROR] 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 (!gwls_data.terminate_app) {
		/* 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);
	}

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

/**
 *  @brief Process ftm session ntb ranging configuration
 *  @param argc   Number of arguments for the ntb_ranging_cfg command
 *  @param argv   A pointer to start of ntb_ranging_cfg cmd arguments list
 *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
 */

static int process_dot11az_ranging_cfg(int argc, char *argv[], void *param)
{
	int ret = MLAN_STATUS_SUCCESS;
	t_u8 *buffer = NULL;
	wls_app_data_t *app_data = NULL;
	hostcmd_ds_ftm_session_cmd *phostcmd = NULL;

	if (!param) {
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}
	app_data = (wls_app_data_t *)param;

	/* Initialize buffer */
	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
	if (!buffer) {
		DBG_ERROR("[ERROR] Cannot allocate buffer for command!\n");
		return MLAN_STATUS_FAILURE;
	}

	mlanwls_prepare_buffer(buffer, "hostcmd", 0, NULL);
	phostcmd = (hostcmd_ds_ftm_session_cmd *)(buffer + (strlen(CMD_NXP) +
							    strlen("hostcmd") +
							    sizeof(t_u32)));

	/*Parse the arguments*/
	phostcmd->cmd_hdr.command = cpu_to_le16(HostCmd_CMD_FTM_SESSION_CFG);
	phostcmd->cmd_hdr.size = S_DS_GEN + sizeof(hostcmd_ds_ftm_session_cmd);
	phostcmd->cmd_hdr.size = cpu_to_le16(phostcmd->cmd_hdr.size);
	phostcmd->cmd.ftm_session_cfg.action =
		cpu_to_le16(app_data->hostcmd_action);
	phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az.range_tlv.len =
		cpu_to_le16(sizeof(ranging_cfg_t));

	if (app_data->protocol_type == PROTO_DOT11AZ_TB) {
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az.range_tlv.type =
			cpu_to_le16(FTM_TB_RANGING_CFG_TLV_ID);
	} else {
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az.range_tlv.type =
			cpu_to_le16(FTM_NTB_RANGING_CFG_TLV_ID);
	}

	if (app_data->hostcmd_action == MLAN_ACT_SET) {
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az.range_tlv.val
			.format_bw = app_data->range_cfg.format_bw;
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az.range_tlv.val
			.max_i2r_sts_upto80 =
			app_data->range_cfg.max_i2r_sts_upto80;
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az.range_tlv.val
			.max_r2i_sts_upto80 =
			app_data->range_cfg.max_r2i_sts_upto80;
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az.range_tlv.val
			.az_measurement_freq =
			app_data->range_cfg.az_measurement_freq;
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az.range_tlv.val
			.az_number_of_measurements =
			app_data->range_cfg.az_number_of_measurements;
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11az.range_tlv.val
			.i2r_lmr_feedback =
			app_data->range_cfg.i2r_lmr_feedback;
	}
	/*Perform ioctl and process response*/
	ret = mlanwls_send_ioctl(buffer);

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

/**
 *  @brief Process dot11mc ftm session  configuration
 *  @param argc   Number of arguments for the ntb_ranging_cfg command
 *  @param argv   A pointer to start of ntb_ranging_cfg cmd arguments list
 *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
 */

static int process_dot11mc_ftm_cfg(int argc, char *argv[], void *param)
{
	int ret = MLAN_STATUS_SUCCESS;
	t_u8 *buffer = NULL;
	wls_app_data_t *app_data = NULL;
	hostcmd_ds_ftm_session_cmd *phostcmd = NULL;

	if (!param) {
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}
	app_data = (wls_app_data_t *)param;

	/* Initialize buffer */
	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
	if (!buffer) {
		DBG_ERROR("[ERROR] Cannot allocate buffer for command!\n");
		return MLAN_STATUS_FAILURE;
	}

	mlanwls_prepare_buffer(buffer, "hostcmd", 0, NULL);
	phostcmd = (hostcmd_ds_ftm_session_cmd *)(buffer + (strlen(CMD_NXP) +
							    strlen("hostcmd") +
							    sizeof(t_u32)));

	/*Parse the arguments*/
	phostcmd->cmd_hdr.command = cpu_to_le16(HostCmd_CMD_FTM_SESSION_CFG);
	phostcmd->cmd_hdr.size = S_DS_GEN + sizeof(t_u16);
	phostcmd->cmd.ftm_session_cfg.action =
		cpu_to_le16(app_data->hostcmd_action);

	if (app_data->hostcmd_action == MLAN_ACT_SET) {
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.sess_tlv.type =
			cpu_to_le16(FTM_SESSION_CFG_INITATOR_TLV_ID);
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.sess_tlv.len =
			cpu_to_le16(sizeof(ftm_session_cfg_t) + sizeof(t_u16));
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.sess_tlv.val
			.burst_exponent = app_data->session_cfg.burst_exponent;
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.sess_tlv.val
			.burst_duration = app_data->session_cfg.burst_duration;
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.sess_tlv.val
			.min_delta_FTM = app_data->session_cfg.min_delta_FTM;
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.sess_tlv.val.is_ASAP =
			app_data->session_cfg.is_ASAP;
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.sess_tlv.val
			.per_burst_FTM = app_data->session_cfg.per_burst_FTM;
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.sess_tlv.val
			.channel_spacing =
			app_data->session_cfg.channel_spacing;
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.sess_tlv.val
			.burst_period =
			cpu_to_le16(app_data->session_cfg.burst_period);
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.sess_tlv.civic_req =
			app_data->civic_request;
		phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.sess_tlv.lci_req =
			app_data->lci_request;
		phostcmd->cmd_hdr.size += sizeof(ftm_session_cfg_tlv_t);

		if (app_data->lci_request) {
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.lci_tlv.type =
				cpu_to_le16(FTM_SESSION_CFG_LCI_TLV_ID);
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.lci_tlv.len =
				cpu_to_le16(sizeof(lci_cfg_t));
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.lci_tlv.val
				.altitude = app_data->lci_cfg.altitude;
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.lci_tlv.val
				.alt_unc = app_data->lci_cfg.alt_unc;
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.lci_tlv.val
				.latitude = app_data->lci_cfg.latitude;
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.lci_tlv.val
				.lat_unc = app_data->lci_cfg.lat_unc;
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.lci_tlv.val
				.longitude = app_data->lci_cfg.longitude;
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.lci_tlv.val
				.long_unc = app_data->lci_cfg.long_unc;
			phostcmd->cmd_hdr.size += sizeof(lci_tlv_t);
		}

		if (app_data->civic_request) {
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.civic_tlv
				.type = cpu_to_le16(
				FTM_SESSION_CFG_LOCATION_CIVIC_TLV_ID);
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.civic_tlv
				.len = cpu_to_le16(
				(sizeof(civic_loc_cfg_t) -
				 sizeof(app_data->civic_cfg.civic_address)) +
				app_data->civic_cfg.civic_address_length);
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.civic_tlv.val
				.civic_address_type =
				app_data->civic_cfg.civic_address_type;
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.civic_tlv.val
				.civic_location_type =
				app_data->civic_cfg.civic_location_type;
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.civic_tlv.val
				.country_code =
				cpu_to_le16(app_data->civic_cfg.country_code);
			phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc.civic_tlv.val
				.civic_address_length =
				app_data->civic_cfg.civic_address_length;
			memcpy(&(phostcmd->cmd.ftm_session_cfg.tlv.cfg_11mc
					 .civic_tlv.val.civic_address[0]),
			       &app_data->civic_cfg.civic_address[0],
			       app_data->civic_cfg.civic_address_length);
			phostcmd->cmd_hdr.size +=
				sizeof(civic_loc_tlv_t) -
				sizeof(app_data->civic_cfg.civic_address) +
				app_data->civic_cfg
					.civic_address_length; /*copy the
								  variable len
								  addr size*/
		}
		phostcmd->cmd_hdr.size = cpu_to_le16(phostcmd->cmd_hdr.size);
	}
	/*Perform ioctl and process response*/
	ret = mlanwls_send_ioctl(buffer);

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

/**
 *  @brief Process 11az/11mc ftm_start command
 *  @param argc   Number of arguments for ftm_start command
 *  @param argv   A pointer to first argument of ftm_start command
 *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
 */

static int process_ftm_start(int argc, char *argv[], void *param)
{
	int ret = MLAN_STATUS_SUCCESS;
	t_u8 *buffer = NULL;
	wls_app_data_t *app_data = NULL;
	hostcmd_ds_ftm_session_cmd *phostcmd = NULL;

	if (!param) {
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}
	app_data = (wls_app_data_t *)param;

	/** Get connection status */
	if (get_connstatus(&assoc_flag) == MLAN_STATUS_FAILURE) {
		DBG_ERROR("[ERROR] Cannot Start FTM, STA not associated !\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

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

	mlanwls_prepare_buffer(buffer, "hostcmd", 0, NULL);
	phostcmd = (hostcmd_ds_ftm_session_cmd *)(buffer + (strlen(CMD_NXP) +
							    strlen("hostcmd") +
							    sizeof(t_u32)));

	/*Parse the arguments*/
	phostcmd->cmd_hdr.command = cpu_to_le16(HostCmd_CMD_FTM_SESSION_CTRL);
	phostcmd->cmd_hdr.size = S_DS_GEN + sizeof(hostcmd_ftm_session_ctrl);
	phostcmd->cmd_hdr.size = cpu_to_le16(phostcmd->cmd_hdr.size);
	phostcmd->cmd.ftm_session_ctrl.action = cpu_to_le16(FTM_ACTION_START);
	phostcmd->cmd.ftm_session_ctrl.chan = app_data->channel;
	memcpy(&phostcmd->cmd.ftm_session_ctrl.peer_mac[0],
	       &app_data->peer_mac[0], ETH_ALEN);

	/*Perform ioctl and process response*/
	ret = mlanwls_send_ioctl(buffer);

	if (ret == MLAN_STATUS_SUCCESS) {
		app_data->ftm_started = TRUE;

		DBG_LOG("[INFO] Wait for session complete event.. \n");
		/** run the application */
		mlanwls_event_handler(nl_sk);
	} else {
		DBG_ERROR("[ERROR] Starting FTM Session failed\n");
		app_data->ftm_started = FALSE;
	}

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

/**
 *  @brief Handle ftm stop procedure
 *  @param argc   Number of arguments
 *  @param argv   A pointer to arguments array
 *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
 */

static int process_ftm_stop(int argc, char *argv[], void *param)
{
	int ret = MLAN_STATUS_SUCCESS;
	t_u8 *buffer = NULL;
	wls_app_data_t *app_data = NULL;
	hostcmd_ds_ftm_session_cmd *phostcmd = NULL;

	if (!param) {
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}
	app_data = (wls_app_data_t *)param;

	if (!app_data->ftm_started) {
		DBG_LOG("[INFO] FTM Session already stopped!\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	/* Initialize buffer */
	buffer = (t_u8 *)malloc(BUFFER_LENGTH);
	if (!buffer) {
		DBG_ERROR("[ERROR] Cannot allocate buffer for command!\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	mlanwls_prepare_buffer(buffer, "hostcmd", 0, NULL);
	phostcmd = (hostcmd_ds_ftm_session_cmd *)(buffer + (strlen(CMD_NXP) +
							    strlen("hostcmd") +
							    sizeof(t_u32)));

	/*Parse the arguments*/
	phostcmd->cmd_hdr.command = cpu_to_le16(HostCmd_CMD_FTM_SESSION_CTRL);
	phostcmd->cmd_hdr.size = S_DS_GEN + sizeof(hostcmd_ftm_session_ctrl);
	phostcmd->cmd_hdr.size = cpu_to_le16(phostcmd->cmd_hdr.size);
	phostcmd->cmd.ftm_session_ctrl.action = cpu_to_le16(FTM_ACTION_STOP);
	phostcmd->cmd.ftm_session_ctrl.chan = app_data->channel;
	memcpy(&phostcmd->cmd.ftm_session_ctrl.peer_mac[0],
	       &app_data->peer_mac[0], ETH_ALEN);

	/*Perform ioctl and process response*/
	ret = mlanwls_send_ioctl(buffer);
	app_data->ftm_started = FALSE;

	if (ret == MLAN_STATUS_FAILURE) {
		DBG_ERROR("[ERROR] Hostcmd ftm stop failed\n");
	}

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

/**
 *  @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)
			strcpy(filename, "/proc/mwlan/config");
		else if (dev_index > 0)
			sprintf(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) {
			DBG_ERROR(
				"[ERROR] Cannot allocate buffer for command!\n");
			return -1;
		}
		/* buffer = CMD_NXP + <cmd_string>*/
		mlanwls_prepare_buffer(buffer, "getnlnum", 0, NULL);

		cmd = (struct eth_priv_cmd *)malloc(
			sizeof(struct eth_priv_cmd));
		if (!cmd) {
			DBG_ERROR(
				"[ERROR] 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)) {
			DBG_ERROR("[ERROR] mlanwls: getnlnum fail\n");
			goto done;
		}
		netlink_num = *(int *)(buffer);
	}

done:
	if (cmd)
		free(cmd);
	if (buffer)
		free(buffer);
	printf("[INFO] 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) {
		DBG_ERROR(
			"[ERROR] 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) {
		DBG_ERROR("[ERROR] 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) {
		DBG_ERROR("[ERROR] 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 mlanwls_terminate_handler(int signal)
{
	printf("[INFO] Stopping application.\n");
#if DEBUG
	printf("Process ID of process killed = %d\n", getpid());
#endif
	gwls_data.run_nonstop = 0;
	gwls_data.terminate_app = 1;
	process_ftm_stop(0, NULL, &gwls_data);
}

/**
 *  @brief Process session ctrl cmd to send hostcmd
 *  @param param pointer to cmd priv data
 *  @return MLAN_STATUS_SUCCESS--success, otherwise--fail
 */

static int process_ftm_session_cfg(int argc, char *argv[], void *param)
{
	int ret = MLAN_STATUS_SUCCESS;
	wls_app_data_t *app_data = NULL;

	if (!param) {
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}
	app_data = (wls_app_data_t *)param;

	if ((app_data->protocol_type == PROTO_DOT11AZ_NTB) ||
	    (app_data->protocol_type == PROTO_DOT11AZ_TB)) {
		printf("[INFO] Set/Get DOT11AZ Ranging Config \n");
		ret = process_dot11az_ranging_cfg(argc, argv, param);
	} else {
		printf("[INFO] Set/Get DOT11MC (Legacy) Config \n");
		ret = process_dot11mc_ftm_cfg(argc, argv, param);
	}

done:
	return ret;
}

/**
 *  @brief  Process session ctrl cmd to send hostcmd
 *  @param param pointer to cmd priv data
 *  @return MLAN_STATUS_SUCCESS--success, otherwise--fail
 */
static int process_ftm_session_ctrl(int argc, char *argv[], void *param)
{
	int ret = MLAN_STATUS_SUCCESS;
	wls_app_data_t *app_data = NULL;

	if (!param) {
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}
	app_data = (wls_app_data_t *)param;

	if (app_data->hostcmd_action == FTM_ACTION_START) {
		ret = process_ftm_start(argc, argv, param);
	} else {
		ret = process_ftm_stop(argc, argv, param);
	}
done:
	return ret;
}

/**
 *  @brief Parse the user command and process it
 *  @param start_idx   Start of ftm command argument in user command
 *  @return MLAN_STATUS_SUCCESS--success, otherwise--fail
 */

static int process_subcommand(int argc, char *argv[])
{
	int i = 0;
	int ret = MLAN_STATUS_SUCCESS;

	/*Parse the user command to update the priv data and call subcommand
	 * handlers*/
	for (i = 0; wls_app_command[i].cmd; i++) {
		if (strncmp(wls_app_command[i].cmd, argv[FTM_SUBCMD_INDEX],
			    strlen(wls_app_command[i].cmd)))
			continue;
		if (strlen(wls_app_command[i].cmd) !=
		    strlen(argv[FTM_SUBCMD_INDEX]))
			continue;

		switch (wls_app_command[i].cmd_id) {
		case FTM_SESSION_CFG_CMD_ID:
			if (FTM_CFG_SET_CMD_LEN == argc) {
				gwls_data.hostcmd_action = MLAN_ACT_SET;
				gwls_data.protocol_type =
					atoi(argv[FTM_CFG_PROTOCOL_INDEX]);
				mlanwls_read_ftm_config(
					argv[FTM_CFG_FILE_ARG_INDEX]);

			} else if (FTM_CFG_GET_CMD_LEN == argc) {
				/*ToDo: FW Implemention for GET operation*/
				gwls_data.hostcmd_action = MLAN_ACT_GET;
			} else {
				DBG_ERROR(
					"[ERROR] Invalid number of arguments\n\n");
				display_help(NELEMENTS(ftm_session_cfg_help),
					     ftm_session_cfg_help);
				ret = MLAN_STATUS_FAILURE;
				goto done;
			}
			break;
		case FTM_SESSION_CTRL_CMD_ID:

			if (FTM_SESSION_SUBCMD_LEN == (argc) ||
			    (FTM_SESSION_SUBCMD_NONSTOP_LEN == (argc))) {
				gwls_data.channel = a2hex_or_atoi(
					argv[FTM_SESSION_CHANNEL_OFFSET]);
				gwls_data.hostcmd_action = a2hex_or_atoi(
					argv[FTM_SESSION_ACTION_OFFSET]);

				ret = mac2raw(
					argv[FTM_SESSION_PEER_ADDR_OFFSET],
					&(gwls_data.peer_mac[0]));
				if (ret != MLAN_STATUS_SUCCESS) {
					printf("[ERROR] %s Address \n",
					       ret == MLAN_STATUS_FAILURE ?
						       "Invalid MAC" :
						       ret == MAC_BROADCAST ?
						       "Broadcast" :
						       "Multicast");
					ret = MLAN_STATUS_FAILURE;
					goto done;
				}

				if (FTM_SESSION_SUBCMD_LEN == (argc)) {
					gwls_data.loop_cnt = a2hex_or_atoi(
						argv[FTM_SESSION_LOOP_OFFSET]);
					gwls_data.run_nonstop =
						(gwls_data.loop_cnt == 0) ? 1 :
									    0;

				} else {
					gwls_data.loop_cnt = 1;
				}

			} else {
				DBG_ERROR(
					"[ERROR] Invalid number of arguments\n");
				display_help(NELEMENTS(ftm_session_ctrl_help),
					     ftm_session_ctrl_help);
				ret = MLAN_STATUS_FAILURE;
				goto done;
			}
			break;
		default:
			printf("[ERROR] SubCommand %s is not supported\n",
			       argv[FTM_SUBCMD_INDEX]);
			display_help(NELEMENTS(mlanwls_help), mlanwls_help);
			ret = MLAN_STATUS_FAILURE;
			goto done;
			break;
		}

		ret = wls_app_command[i].func(argc, argv, &gwls_data);
		printf("[INFO] Command %s processed. Return:%d \n",
		       wls_app_command[i].cmd, ret);
		break;
	}

done:
	if (!wls_app_command[i].cmd)
		display_help(NELEMENTS(mlanwls_help), mlanwls_help);
	return ret;
}

/**
 *  @brief Read ftm config param from conf file
 *  @param file_name  config file name
 *
 *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
 */

static int mlanwls_read_ftm_config(char *file_name)
{
	int ret = MLAN_STATUS_SUCCESS;
	FILE *config_file = NULL;
	char *line = NULL;
	char *data = NULL;
	int arg_num, li;
	char *args[30];
	t_u8 param = 0;

	// read config
	config_file = fopen(file_name, "r");
	if (config_file == NULL) {
		perror("CONFIG");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}
	line = (char *)malloc(MAX_CONFIG_LINE);
	if (!line) {
		printf("ERR:Cannot allocate memory for line\n");
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}
	memset(line, 0, MAX_CONFIG_LINE);

	printf("[INFO] Read FTM config from file %s\n", file_name);
	while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li,
			       &data)) {
		arg_num = parse_line(line, args, 30);

		if (arg_num > 1)
			param = atoi(args[1]);

		if (gwls_data.protocol_type == PROTO_DOT11MC) {
			if (strcmp(args[0], "DOT11MC_CFG") == 0) {
				printf("DOT11MC_CFG\n");
			} else if (strcmp(args[0], "BURST_EXP") == 0) {
				gwls_data.session_cfg.burst_exponent =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t BURST_EXP=%d\n", param);
			} else if (strcmp(args[0], "BURST_DURATION") == 0) {
				gwls_data.session_cfg.burst_duration =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t BURST_DURATION=%d\n", param);
			} else if (strcmp(args[0], "MIN_DELTA") == 0) {
				gwls_data.session_cfg.min_delta_FTM =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t MIN_DELTA=%d\n", param);
			} else if (strcmp(args[0], "IS_ASAP") == 0) {
				gwls_data.session_cfg.is_ASAP =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t IS_ASAP=%d\n", param);
			} else if (strcmp(args[0], "FTM_PER_BURST") == 0) {
				gwls_data.session_cfg.per_burst_FTM =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t FTM_PER_BURST=%d\n", param);
			} else if (strcmp(args[0], "BW") == 0) {
				gwls_data.session_cfg.channel_spacing =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t BW=%d\n", param);
			} else if (strcmp(args[0], "BURST_PERIOD") == 0) {
				gwls_data.session_cfg.burst_period =
					(t_u16)(atoi(args[1]));
				PRINT_CFG("\t BURST_PERIOD=%d\n", param);

			} else if (strcmp(args[0], "LCI_REQUEST") == 0) {
				gwls_data.lci_request = (t_u8)(atoi(args[1]));
				PRINT_CFG("\t LCI_REQUEST=%d\n", param);

			} else if (strcmp(args[0], "LATITIUDE") == 0) {
				gwls_data.lci_cfg.latitude =
					(double)(atof(args[1]));
				PRINT_CFG("\t LATITIUDE=%lf\n",
					  gwls_data.lci_cfg.latitude);

			} else if (strcmp(args[0], "LONGITUDE") == 0) {
				gwls_data.lci_cfg.longitude =
					(double)(atof(args[1]));
				PRINT_CFG("\t LATITIUDE=%lf\n",
					  gwls_data.lci_cfg.longitude);

			} else if (strcmp(args[0], "LATITUDE_UNCERTAINITY") ==
				   0) {
				gwls_data.lci_cfg.lat_unc =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t LATITUDE_UNCERTAINITY=%d\n",
					  param);

			} else if (strcmp(args[0], "LONGITUDE_UNCERTAINITY") ==
				   0) {
				gwls_data.lci_cfg.long_unc =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t LONGITUDE_UNCERTAINITY=%d\n",
					  param);

			} else if (strcmp(args[0], "ALTITUDE") == 0) {
				gwls_data.lci_cfg.altitude =
					(double)(atof(args[1]));
				PRINT_CFG("\t ALTITUDE=%lf\n",
					  gwls_data.lci_cfg.altitude);

			} else if (strcmp(args[0], "ALTITUDE_UNCERTAINITY") ==
				   0) {
				gwls_data.lci_cfg.alt_unc =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t ALTITUDE_UNCERTAINITY=%d\n",
					  param);

			} else if (strcmp(args[0], "CIVIC_LOCATION") == 0) {
				gwls_data.civic_request = (t_u8)(atoi(args[1]));
				PRINT_CFG("\t CIVIC_LOCATION=%d\n", param);

			} else if (strcmp(args[0], "CIVIC_LOCATION_TYPE") ==
				   0) {
				gwls_data.civic_cfg.civic_location_type =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t CIVIC_LOCATION_TYPE=%d\n", param);

			} else if (strcmp(args[0], "COUNTRY_CODE") == 0) {
				gwls_data.civic_cfg.country_code =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t COUNTRY_CODE=%d\n", param);

			} else if (strcmp(args[0], "CIVIC_ADDRESS_TYPE") == 0) {
				gwls_data.civic_cfg.civic_address_type =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t CIVIC_ADDRESS_TYPE=%d\n", param);

			} else if (strcmp(args[0], "ADDRESS") == 0) {
				if (strlen(args[1]) <= 255) {
					gwls_data.civic_cfg
						.civic_address_length =
						strlen(args[1]);
					strncpy((char *)&gwls_data.civic_cfg
							.civic_address[0],
						args[1],
						gwls_data.civic_cfg
							.civic_address_length);
					PRINT_CFG("\t ADDRESS=%s\n", args[1]);
				} else {
					DBG_ERROR(
						"\t [ERROR] Invalid Civic Address Len\n");
				}

			} else {
				// printf("Invalid line entry\n %s",args[1]);
			}
		}

		if ((gwls_data.protocol_type == PROTO_DOT11AZ_NTB) ||
		    (gwls_data.protocol_type == PROTO_DOT11AZ_TB)) {
			if (strcmp(args[0], "DOT11AZ_RANGING_CFG") == 0) {
				if (gwls_data.protocol_type ==
				    PROTO_DOT11AZ_NTB) {
					printf("DOT11AZ_NTB_RANGING_CFG\n\n");
				} else {
					printf("DOT11AZ_TB_RANGING_CFG\n\n");
				}
			} else if (strcmp(args[0], "FORMAT_BW") == 0) {
				gwls_data.range_cfg.format_bw =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t FORMAT_BW=%d\n", param);
			} else if (strcmp(args[0], "MAX_I2R_STS_UPTO80") == 0) {
				gwls_data.range_cfg.max_i2r_sts_upto80 =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t MAX_I2R_STS_UPTO80=%d\n", param);
			} else if (strcmp(args[0], "MAX_R2I_STS_UPTO80") == 0) {
				gwls_data.range_cfg.max_r2i_sts_upto80 =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t MAX_R2I_STS_UPTO80=%d\n", param);
			} else if (strcmp(args[0], "AZ_MEASUREMENT_FREQ") ==
				   0) {
				gwls_data.range_cfg.az_measurement_freq =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t AZ_MEASUREMENT_FREQ=%d\n", param);
			} else if (strcmp(args[0],
					  "AZ_NUMBER_OF_MEASUREMENTS") == 0) {
				gwls_data.range_cfg.az_number_of_measurements =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t AZ_NUMBER_OF_MEASUREMENTS=%d\n",
					  param);
			} else if (strcmp(args[0], "I2R_LMR_FEEDBACK") == 0) {
				gwls_data.range_cfg.i2r_lmr_feedback =
					(t_u8)(atoi(args[1]));
				PRINT_CFG("\t I2R_LMR_FEEDBACK=%d\n\n", param);
			} else {
				// printf("Invalid line entry%s\n",args[1]);
			}
		}
	}

done:
	if (line)
		free(line);
	if (config_file)
		fclose(config_file);
	return ret;
}

/**
 *  @brief Initialize ftm command private data
 *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
 */
static int mlanwls_init(void)
{
	int ret = MLAN_STATUS_SUCCESS;

	memset(&gwls_data, 0, sizeof(wls_app_data_t));

	/*Initialize app private data with default values*/
	gwls_data.associated = 0;
	gwls_data.bss_type = 0;
	gwls_data.channel = 0;
	gwls_data.loop_cnt = 1;
	gwls_data.run_nonstop = 0;
	gwls_data.debug_level = 1;
	gwls_data.ftm_started = 0;
	gwls_data.terminate_app = 0;
	gwls_data.protocol_type = 0;

	/*DOT11AZ NTB/TB Ranging default config*/
	gwls_data.range_cfg.az_measurement_freq = 1;
	gwls_data.range_cfg.az_number_of_measurements = 6;
	gwls_data.range_cfg.format_bw = 2;
	gwls_data.range_cfg.max_i2r_sts_upto80 = 0;
	gwls_data.range_cfg.max_r2i_sts_upto80 = 1;
	gwls_data.range_cfg.i2r_lmr_feedback = 0;

	/*DOT11MC  FTM session default config*/
	gwls_data.session_cfg.burst_exponent = 0;
	gwls_data.session_cfg.burst_duration = 10;
	gwls_data.session_cfg.burst_period = 5;
	gwls_data.session_cfg.is_ASAP = 1;
	gwls_data.session_cfg.per_burst_FTM = 10;
	gwls_data.session_cfg.min_delta_FTM = 10;
	gwls_data.session_cfg.channel_spacing = 13;

	/*DOT11MC  FTM civic location config*/
	gwls_data.civic_request = 1;
	strncpy((char *)&gwls_data.civic_cfg.civic_address[0], NXP_ADDR,
		strlen(NXP_ADDR));
	gwls_data.civic_cfg.civic_address_length = strlen(NXP_ADDR);
	gwls_data.civic_cfg.civic_address_type = 22;
	gwls_data.civic_cfg.civic_location_type = 1;
	gwls_data.civic_cfg.country_code = 0;

	/*DOT11MC  FTM LCI config*/
	gwls_data.lci_request = 1;
	gwls_data.lci_cfg.altitude = 11.2;
	gwls_data.lci_cfg.alt_unc = 15;
	gwls_data.lci_cfg.latitude = -33.8570095;
	gwls_data.lci_cfg.lat_unc = 18;
	gwls_data.lci_cfg.longitude = +151.2152005;
	gwls_data.lci_cfg.long_unc = 18;
	gwls_data.lci_cfg.z_info = 0;

	return ret;
}

/********************************************************
		Global Functions
********************************************************/
/**
 *  @brief Entry function for mlan location service
 *  @param argc		number of arguments
 *  @param argv     A pointer to arguments array
 *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
 */
int mlanwls_main(int argc, char *argv[])
{
	int dev_index = 0; //-1; /** initialise with -1 to open multiple NETLINK
			   //Sockets */
	int ret = MLAN_STATUS_SUCCESS;

	printf("\n\n---------------------------------------------\n");
	printf("------- NXP Wifi Location Service (WLS)------\n");
	printf("--------------------------------------------------\n\n");

	if (argc < 4) {
		display_help(NELEMENTS(mlanwls_help), mlanwls_help);
		return MLAN_STATUS_FAILURE;
	}

	/*Initialize private data*/
	printf("[INFO] Initializing App\n");
	ret = mlanwls_init();

	/*Set the interface*/
	memset(dev_name, 0, sizeof(dev_name));
	strncpy(dev_name, argv[1], IFNAMSIZ);

	/* create a socket */
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		DBG_ERROR("[ERROR] mlanwls: 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) {
		DBG_ERROR("[ERROR] mlanwls: Cannot open netlink socket.\n");
		goto done;
	}
	signal(SIGHUP, mlanwls_terminate_handler); /* catch hangup signal */
	signal(SIGTERM, mlanwls_terminate_handler); /* catch kill signal */
	signal(SIGINT, mlanwls_terminate_handler); /* catch kill signal */
	signal(SIGALRM, mlanwls_terminate_handler); /* catch kill signal */

	/*Process the wlscmd sub command argument*/
	ret = process_subcommand(argc, argv);

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

	return ret;
}
