/*
* Copyright (c) 2006-2015 Marvell International Ltd.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/*
*
*   Description:  This file implements frimware host command related functions.
*
*/

#include "mwl_sysadpt.h"
#include "mwl_dev.h"
#include "mwl_debug.h"
#include "mwl_fwcmd.h"

/* CONSTANTS AND MACROS
*/

#define MAX_WAIT_FW_COMPLETE_ITERATIONS         10000

/*
 *      16 bit host command code
 */
#define HOSTCMD_CMD_GET_HW_SPEC                 0x0003
#define HOSTCMD_CMD_SET_HW_SPEC                 0x0004
#define HOSTCMD_CMD_802_11_GET_STAT             0x0014
#define HOSTCMD_CMD_802_11_RADIO_CONTROL        0x001c
#define HOSTCMD_CMD_802_11_TX_POWER             0x001f
#define HOSTCMD_CMD_802_11_RF_ANTENNA           0x0020
#define HOSTCMD_CMD_BROADCAST_SSID_ENABLE       0x0050 /* per-vif */
#define HOSTCMD_CMD_SET_RF_CHANNEL              0x010a
#define HOSTCMD_CMD_SET_AID                     0x010d /* per-vif */
#define HOSTCMD_CMD_SET_INFRA_MODE              0x010e /* per-vif */
#define HOSTCMD_CMD_802_11_RTS_THSD             0x0113
#define HOSTCMD_CMD_SET_EDCA_PARAMS             0x0115
#define HOSTCMD_CMD_SET_WMM_MODE                0x0123
#define HOSTCMD_CMD_SET_FIXED_RATE              0x0126
#define HOSTCMD_CMD_SET_IES                     0x0127
#define HOSTCMD_CMD_SET_MAC_ADDR                0x0202 /* per-vif */
#define HOSTCMD_CMD_SET_RATE_ADAPT_MODE         0x0203
#define HOSTCMD_CMD_GET_WATCHDOG_BITMAP         0x0205
#define HOSTCMD_CMD_DEL_MAC_ADDR                0x0206 /* pre-vif */
#define HOSTCMD_CMD_BSS_START                   0x1100 /* per-vif */
#define HOSTCMD_CMD_AP_BEACON                   0x1101 /* per-vif */
#define HOSTCMD_CMD_SET_NEW_STN                 0x1111 /* per-vif */
#define HOSTCMD_CMD_SET_APMODE                  0x1114
#define HOSTCMD_CMD_UPDATE_ENCRYPTION           0x1122 /* per-vif */
#define HOSTCMD_CMD_BASTREAM                    0x1125
#define HOSTCMD_CMD_DWDS_ENABLE                 0x1144
#define HOSTCMD_CMD_FW_FLUSH_TIMER              0x1148
#define HOSTCMD_CMD_SET_CDD                     0x1150

/*
 *      Define general result code for each command
 */
#define HOSTCMD_RESULT_OK                       0x0000 /* OK */
#define HOSTCMD_RESULT_ERROR                    0x0001 /* Genenral error */
#define HOSTCMD_RESULT_NOT_SUPPORT              0x0002 /* Command is not valid */
#define HOSTCMD_RESULT_PENDING                  0x0003 /* Command is pending (will be processed) */
#define HOSTCMD_RESULT_BUSY                     0x0004 /* System is busy (command ignored) */
#define HOSTCMD_RESULT_PARTIAL_DATA             0x0005 /* Data buffer is not big enough */

/*
 *      Define channel related constants
 */
#define FREQ_BAND_2DOT4GHZ                      0x1
#define FREQ_BAND_4DOT9GHZ                      0x2
#define FREQ_BAND_5GHZ                          0x4
#define FREQ_BAND_5DOT2GHZ                      0x8
#define CH_AUTO_WIDTH	                        0
#define CH_10_MHz_WIDTH                         0x1
#define CH_20_MHz_WIDTH                         0x2
#define CH_40_MHz_WIDTH                         0x4
#define CH_80_MHz_WIDTH                         0x5
#define EXT_CH_ABOVE_CTRL_CH                    0x1
#define EXT_CH_AUTO                             0x2
#define EXT_CH_BELOW_CTRL_CH                    0x3
#define NO_EXT_CHANNEL                          0x0

#define ACT_PRIMARY_CHAN_0                      0 /* active primary 1st 20MHz channel */
#define ACT_PRIMARY_CHAN_1                      1 /* active primary 2nd 20MHz channel */
#define ACT_PRIMARY_CHAN_2                      2 /* active primary 3rd 20MHz channel */
#define ACT_PRIMARY_CHAN_3                      3 /* active primary 4th 20MHz channel */
#define ACT_PRIMARY_CHAN_4                      4 /* active primary 5th 20MHz channel */
#define ACT_PRIMARY_CHAN_5                      5 /* active primary 6th 20MHz channel */
#define ACT_PRIMARY_CHAN_6                      6 /* active primary 7th 20MHz channel */
#define ACT_PRIMARY_CHAN_7                      7 /* active primary 8th 20MHz channel */

/*
 *      Define rate related constants
 */
#define HOSTCMD_ACT_NOT_USE_FIXED_RATE          0x0002

/*
 *      Define station related constants
 */
#define HOSTCMD_ACT_STA_ACTION_ADD              0
#define HOSTCMD_ACT_STA_ACTION_REMOVE           2

/*
 *      Define key related constants
 */
#define MAX_ENCR_KEY_LENGTH                     16
#define MIC_KEY_LENGTH                          8

#define KEY_TYPE_ID_WEP                         0x00       /* Key type is WEP		*/
#define KEY_TYPE_ID_TKIP                        0x01       /* Key type is TKIP		*/
#define KEY_TYPE_ID_AES	                        0x02       /* Key type is AES-CCMP	*/

#define ENCR_KEY_FLAG_TXGROUPKEY                0x00000004 /* Group key for TX */
#define ENCR_KEY_FLAG_PAIRWISE                  0x00000008 /* pairwise */
#define ENCR_KEY_FLAG_TSC_VALID                 0x00000040 /* Sequence counters are valid */
#define ENCR_KEY_FLAG_WEP_TXKEY                 0x01000000 /* Tx key for WEP */
#define ENCR_KEY_FLAG_MICKEY_VALID              0x02000000 /* Tx/Rx MIC keys are valid */

/*
 *      Define block ack related constants
 */
#define BASTREAM_FLAG_IMMEDIATE_TYPE            1
#define BASTREAM_FLAG_DIRECTION_UPSTREAM        0

/*
 *      Define general purpose action
 */
#define HOSTCMD_ACT_GEN_SET                     0x0001
#define HOSTCMD_ACT_GEN_SET_LIST                0x0002
#define HOSTCMD_ACT_GEN_GET_LIST                0x0003

/*      Misc
*/
#define MWL_SPIN_LOCK(X)                        SPIN_LOCK_IRQSAVE(X, flags)
#define MWL_SPIN_UNLOCK(X)                      SPIN_UNLOCK_IRQRESTORE(X, flags)

#define MAX_ENCR_KEY_LENGTH                     16
#define MIC_KEY_LENGTH                          8

/* TYPE DEFINITION
*/

enum {
	WL_DISABLE = 0,
	WL_ENABLE = 1,
	WL_DISABLE_VMAC = 0x80,
};

enum {
	WL_GET = 0,
	WL_SET = 1,
	WL_RESET = 2,
};

enum {
	WL_LONG_PREAMBLE = 1,
	WL_SHORT_PREAMBLE = 3,
	WL_AUTO_PREAMBLE = 5,
};

enum encr_action_type {
	/* request to enable/disable HW encryption */
	ENCR_ACTION_ENABLE_HW_ENCR,
	/* request to set encryption key */
	ENCR_ACTION_TYPE_SET_KEY,
	/* request to remove one or more keys */
	ENCR_ACTION_TYPE_REMOVE_KEY,
	ENCR_ACTION_TYPE_SET_GROUP_KEY,
};

enum ba_action_type {
	BA_CREATE_STREAM,
	BA_UPDATE_STREAM,
	BA_DESTROY_STREAM,
	BA_FLUSH_STREAM,
	BA_CHECK_STREAM,
};

enum mac_type {
	WL_MAC_TYPE_PRIMARY_CLIENT,
	WL_MAC_TYPE_SECONDARY_CLIENT,
	WL_MAC_TYPE_PRIMARY_AP,
	WL_MAC_TYPE_SECONDARY_AP,
};

/*
 *      General host command header
 */

struct hostcmd_header {
	u16 cmd;
	u16 len;
	u8 seq_num;
	u8 macid;
	u16 result;
} __packed;

/*
 *      HOSTCMD_CMD_GET_HW_SPEC
 */

struct hostcmd_cmd_get_hw_spec {
	struct hostcmd_header cmd_hdr;
	u8 version;                  /* version of the HW                    */
	u8 host_if;                  /* host interface                       */
	u16 num_wcb;                 /* Max. number of WCB FW can handle     */
	u16 num_mcast_addr;          /* MaxNbr of MC addresses FW can handle */
	u8 permanent_addr[ETH_ALEN]; /* MAC address programmed in HW         */
	u16 region_code;
	u16 num_antenna;             /* Number of antenna used      */
	u32 fw_release_num;          /* 4 byte of FW release number */
	u32 wcb_base0;
	u32 rxpd_wr_ptr;
	u32 rxpd_rd_ptr;
	u32 fw_awake_cookie;
	u32 wcb_base[SYSADPT_TOTAL_TX_QUEUES - 1];
} __packed;

/*
 *      HOSTCMD_CMD_SET_HW_SPEC
 */

struct hostcmd_cmd_set_hw_spec {
	struct hostcmd_header cmd_hdr;
	u8 version;                             /* HW revision */
	u8 host_if;                             /* Host interface */
	u16 num_mcast_addr;                     /* Max. number of Multicast address FW can handle */
	u8 permanent_addr[ETH_ALEN];            /* MAC address */
	u16 region_code;                        /* Region Code */
	u32 fw_release_num;                     /* 4 byte of FW release number, example 0x1234=1.2.3.4 */
	u32 fw_awake_cookie;                    /* Firmware awake cookie - used to ensure that the device is not in sleep mode */
	u32 device_caps;                        /* Device capabilities (see above) */
	u32 rxpd_wr_ptr;                        /* Rx shared memory queue */
	u32 num_tx_queues;                      /* Actual number of TX queues in WcbBase array */
	u32 wcb_base[SYSADPT_NUM_OF_DESC_DATA]; /* TX WCB Rings */
	u32 max_amsdu_size:2;                   /* Max AMSDU size (00 - AMSDU Disabled, 01 - 4K, 10 - 8K, 11 - not defined) */
	u32 implicit_ampdu_ba:1;                /* Indicates supported AMPDU type (1 = implicit, 0 = explicit (default)) */
	u32 disablembss:1;                      /* indicates mbss features disable in FW */
	u32 host_form_beacon:1;
	u32 host_form_probe_response:1;
	u32 host_power_save:1;
	u32 host_encr_decr_mgt:1;
	u32 host_intra_bss_offload:1;
	u32 host_iv_offload:1;
	u32 host_encr_decr_frame:1;
	u32 reserved: 21;                       /* Reserved */
	u32 tx_wcb_num_per_queue;
	u32 total_rx_wcb;
} __packed;

/*
 *      HOSTCMD_CMD_802_11_GET_STAT
 */

struct hostcmd_cmd_802_11_get_stat {
	struct hostcmd_header cmd_hdr;
	u32 tx_retry_successes;
	u32 tx_multiple_retry_successes;
	u32 tx_failures;
	u32 rts_successes;
	u32 rts_failures;
	u32 ack_failures;
	u32 rx_duplicate_frames;
	u32 rx_fcs_errors;
	u32 tx_watchdog_timeouts;
	u32 rx_overflows;
	u32 rx_frag_errors;
	u32 rx_mem_errors;
	u32 pointer_errors;
	u32 tx_underflows;
	u32 tx_done;
	u32 tx_done_buf_try_put;
	u32 tx_done_buf_put;
	u32 wait_for_tx_buf;                    /* Put size of requested buffer in here */
	u32 tx_attempts;
	u32 tx_successes;
	u32 tx_fragments;
	u32 tx_multicasts;
	u32 rx_non_ctl_pkts;
	u32 rx_multicasts;
	u32 rx_undecryptable_frames;
	u32 rx_icv_errors;
	u32 rx_excluded_frames;
	u32 rx_weak_iv_count;
	u32 rx_unicasts;
	u32 rx_bytes;
	u32 rx_errors;
	u32 rx_rts_count;
	u32 tx_cts_count;
} __packed;

/*
 *      HOSTCMD_CMD_802_11_RADIO_CONTROL
 */

struct hostcmd_cmd_802_11_radio_control {
	struct hostcmd_header cmd_hdr;
	u16 action;
	u16 control;	             /* @bit0: 1/0,on/off, @bit1: 1/0, long/short @bit2: 1/0,auto/fix */
	u16 radio_on;
} __packed;

/*
 *     HOSTCMD_CMD_802_11_TX_POWER
 */

struct hostcmd_cmd_802_11_tx_power {
	struct hostcmd_header cmd_hdr;
	u16 action;
	u16 band;
	u16 ch;
	u16 bw;
	u16 sub_ch;
	u16 power_level_list[SYSADPT_TX_POWER_LEVEL_TOTAL];
} __packed;

/*
 *     HOSTCMD_CMD_802_11_RF_ANTENNA
 */

struct hostcmd_cmd_802_11_rf_antenna {
	struct hostcmd_header cmd_hdr;
	u16 action;
	u16 antenna_mode;            /* Number of antennas or 0xffff(diversity) */
} __packed;

/*
 *     HOSTCMD_CMD_BROADCAST_SSID_ENABLE
 */

struct hostcmd_cmd_broadcast_ssid_enable {
	struct hostcmd_header cmd_hdr;
	u32 enable;
} __packed;

/*
 *     HOSTCMD_CMD_SET_RF_CHANNEL
 */

struct chnl_flags_11ac {
	u32	freq_band:6;             /* bit0=1: 2.4GHz,bit1=1: 4.9GHz,bit2=1: 5GHz,bit3=1: 5.2GHz, */
	u32	chnl_width:5;            /* bit6=1:10MHz, bit7=1:20MHz, bit8=1:40MHz */
	u32	act_primary:3;           /* 000: 1st 20MHz chan, 001:2nd 20MHz chan, 011:3rd 20MHz chan, 100:4th 20MHz chan */
	u32	reserved:18;
} __packed;

struct hostcmd_cmd_set_rf_channel {
	struct hostcmd_header cmd_hdr;
	u16 action;
	u8 curr_chnl;
	struct chnl_flags_11ac chnl_flags;
} __packed;

/*
 *     HOSTCMD_CMD_SET_AID
 */

struct hostcmd_cmd_set_aid {
	struct hostcmd_header cmd_hdr;
	u16 aid;
	u8 mac_addr[ETH_ALEN];       /* AP's Mac Address(BSSID) */
	u32 gprotect;
	u8 ap_rates[SYSADPT_MAX_DATA_RATES_G];
} __packed;

/*
 *     HOSTCMD_CMD_SET_INFRA_MODE
 */

struct hostcmd_cmd_set_infra_mode {
	struct hostcmd_header cmd_hdr;
} __packed;

/*
 *     HOSTCMD_CMD_802_11_RTS_THSD
 */

struct hostcmd_cmd_802_11_rts_thsd {
	struct hostcmd_header cmd_hdr;
	u16 action;
	u16	threshold;
} __packed;

/*
 *     HOSTCMD_CMD_SET_EDCA_PARAMS
 */

struct hostcmd_cmd_set_edca_params {
	struct hostcmd_header cmd_hdr;
	u16 action;                  /* 0 = get all, 0x1 =set CWMin/Max,  0x2 = set TXOP , 0x4 =set AIFSN */
	u16 txop;                    /* in unit of 32 us */
	u32 cw_max;                  /* 0~15 */
	u32 cw_min;                  /* 0~15 */
	u8 aifsn;
	u8 txq_num;                  /* Tx Queue number. */
} __packed;

/*
 *      HOSTCMD_CMD_SET_WMM_MODE
 */

struct hostcmd_cmd_set_wmm_mode {
	struct hostcmd_header cmd_hdr;
	u16 action;                  /* 0->unset, 1->set */
} __packed;

/*
 *     HOSTCMD_CMD_SET_FIXED_RATE
 */

struct fix_rate_flag {           /* lower rate after the retry count */
	u32 fix_rate_type;           /* 0: legacy, 1: HT */
	u32 retry_count_valid;       /* 0: retry count is not valid, 1: use retry count specified */
} __packed;

struct fix_rate_entry {
	struct fix_rate_flag fix_rate_type_flags;
	u32 fixed_rate;              /* depending on the flags above, this can be either a legacy rate(not index) or an MCS code. */
	u32 retry_count;
} __packed;

struct hostcmd_cmd_set_fixed_rate {
	struct hostcmd_header cmd_hdr;
	u32 action;                  /* HOSTCMD_ACT_NOT_USE_FIXED_RATE 0x0002 */
	u32 allow_rate_drop;         /* use fixed rate specified but firmware can drop to */
	u32 entry_count;
	struct fix_rate_entry fixed_rate_table[4];
	u8 multicast_rate;
	u8 multi_rate_tx_type;
	u8 management_rate;
} __packed;

/*
 *     HOSTCMD_CMD_SET_IES
 */

struct hostcmd_cmd_set_ies {
	struct hostcmd_header cmd_hdr;
	u16 action;                  /* 0->unset, 1->set */
	u16 ie_list_len_ht;
	u16 ie_list_len_vht;
	u16 ie_list_len_proprietary;
	/*Buffer size same as Generic_Beacon*/
	u8 ie_list_ht[148];
	u8 ie_list_vht[24];
	u8 ie_list_proprietary[112];
} __packed;

/*
 *      HOSTCMD_CMD_SET_RATE_ADAPT_MODE
 */

struct hostcmd_cmd_set_rate_adapt_mode {
	struct hostcmd_header cmd_hdr;
	u16 action;
	u16 rate_adapt_mode;         /* 0:Indoor, 1:Outdoor */
} __packed;

/*
 *      HOSTCMD_CMD_SET_MAC_ADDR, HOSTCMD_CMD_DEL_MAC_ADDR
 */

struct hostcmd_cmd_set_mac_addr {
	struct hostcmd_header cmd_hdr;
	u16 mac_type;
	u8 mac_addr[ETH_ALEN];
} __packed;

/*
 *     HOSTCMD_CMD_GET_WATCHDOG_BITMAP
 */

struct hostcmd_cmd_get_watchdog_bitmap {
	struct hostcmd_header cmd_hdr;
	u8 watchdog_bitmap;          /* for SW/BA */
} __packed;

/*
 *     HOSTCMD_CMD_BSS_START
 */

struct hostcmd_cmd_bss_start {
	struct hostcmd_header cmd_hdr;
	u32 enable;                  /* FALSE: Disable or TRUE: Enable */
} __packed;

/*
 *     HOSTCMD_CMD_AP_BEACON
 */

struct cf_params {
	u8 elem_id;
	u8 len;
	u8 cfp_cnt;
	u8 cfp_period;
	u16 cfp_max_duration;
	u16 cfp_duration_remaining;
} __packed;

struct ibss_params {
	u8 elem_id;
	u8 len;
	u16	atim_window;
} __packed;

union ss_params {
	struct cf_params cf_param_set;
	struct ibss_params ibss_param_set;
} __packed;

struct fh_params {
	u8 elem_id;
	u8 len;
	u16 dwell_time;
	u8 hop_set;
	u8 hop_pattern;
	u8 hop_index;
} __packed;

struct ds_params {
	u8 elem_id;
	u8 len;
	u8 current_chnl;
} __packed;

union phy_params {
	struct fh_params fh_param_set;
	struct ds_params ds_param_set;
} __packed;

struct rsn_ie {
	u8 elem_id;
	u8 len;
	u8 oui_type[4];              /* 00:50:f2:01 */
	u8 ver[2];
	u8 grp_key_cipher[4];
	u8 pws_key_cnt[2];
	u8 pws_key_cipher_list[4];
	u8 auth_key_cnt[2];
	u8 auth_key_list[4];
} __packed;

struct rsn48_ie {
	u8 elem_id;
	u8 len;
	u8 ver[2];
	u8 grp_key_cipher[4];
	u8 pws_key_cnt[2];
	u8 pws_key_cipher_list[4];
	u8 auth_key_cnt[2];
	u8 auth_key_list[4];
	u8 rsn_cap[2];
	u8 pmk_id_cnt[2];
	u8 pmk_id_list[16];          /* Should modify to 16 * S */
	u8 reserved[8];
} __packed;

struct aci_aifsn_field {
	u8 aifsn:4;
	u8 acm:1;
	u8 aci:2;
	u8 rsvd:1;
} __packed;

struct ecw_min_max_field {
	u8 ecw_min:4;
	u8 ecw_max:4;
} __packed;

struct ac_param_rcd {
	struct aci_aifsn_field aci_aifsn;
	struct ecw_min_max_field ecw_min_max;
	u16 txop_lim;
} __packed;

struct wmm_param_elem {
	u8 elem_id;
	u8 len;
	u8 oui[3];
	u8 type;
	u8 sub_type;
	u8 version;
	u8 rsvd;
	struct ac_param_rcd ac_be;
	struct ac_param_rcd ac_bk;
	struct ac_param_rcd ac_vi;
	struct ac_param_rcd ac_vo;
} __packed;

struct channel_info {
	u8 first_channel_num;
	u8 num_channels;
	u8 max_tx_pwr_level;
} __packed;

struct country {
	u8 elem_id;
	u8 len;
	u8 country_str[3];
	struct channel_info channel_info[40];
} __packetd;

struct start_cmd {
	u8 sta_mac_addr[ETH_ALEN];
	u8 ssid[IEEE80211_MAX_SSID_LEN];
	u8 bss_type;
	u16 bcn_period;
	u8 dtim_period;
	union ss_params ss_param_set;
	union phy_params phy_param_set;
	u16 probe_delay;
	u16 cap_info;
	u8 bss_basic_rate_set[SYSADPT_MAX_DATA_RATES_G];
	u8 op_rate_set[SYSADPT_MAX_DATA_RATES_G];
	struct rsn_ie rsn_ie;
	struct rsn48_ie rsn48_ie;
	struct wmm_param_elem wmm_param;
	struct country country;
	u32 ap_rf_type;              /* 0->B, 1->G, 2->Mixed, 3->A, 4->11J */
} __packed;

struct hostcmd_cmd_ap_beacon {
	struct hostcmd_header cmd_hdr;
	struct start_cmd start_cmd;
} __packed;

/*
 *     HOSTCMD_CMD_SET_NEW_STN
 */

struct cap_info {
	u16 ess:1;
	u16 ibss:1;
	u16 cf_pollable:1;
	u16 cf_poll_rqst:1;
	u16 privacy:1;
	u16 short_preamble:1;
	u16 pbcc:1;
	u16 chan_agility:1;
	u16 spectrum_mgmt:1;
	u16 qoS:1;
	u16 short_slot_time:1;
	u16 apsd:1;
	u16 rsrvd1:1;
	u16 dsss_ofdm:1;
	u16 block_ack:1;
	u16 rsrvd2:1;
} __packed;

struct add_ht_chnl {
	u8 ext_chnl_offset:2;
	u8 sta_chnl_width:1;
	u8 rifs_mode:1;
	u8 psmp_stas_only:1;
	u8 srvc_intvl_gran:3;
} __packed;

struct add_ht_op_mode {
	u16 op_mode:2;
	u16 non_gf_sta_present:1;
	u16 rrans_burst_limit:1;
	u16 non_ht_sta_present:1;
	u16 rsrv:11;
} __packed;

struct add_ht_stbc {
	u16 bsc_stbc:7;
	u16 dual_stbc_proc:1;
	u16 scd_bcn:1;
	u16 lsig_txop_proc_full_sup:1;
	u16 pco_active:1;
	u16 pco_phase:1;
	u16 rsrv:4;
} __packed;

struct add_ht_info {
	u8 control_chnl;
	struct add_ht_chnl add_chnl;
	struct add_ht_op_mode op_mode;
	struct add_ht_stbc stbc;
} __packed;

struct peer_info {
	u32 legacy_rate_bitmap;
	u8 ht_rates[4];
	struct cap_info cap_info;
	u16 ht_cap_info;
	u8 mac_ht_param_info;
	u8 mrvl_sta;
	struct add_ht_info add_ht_info;
	u32 tx_bf_capabilities;      /* EXBF_SUPPORT */
	u32 vht_max_rx_mcs;
	u32 vht_cap;
	u8 vht_rx_channel_width;     /* 0:20Mhz, 1:40Mhz, 2:80Mhz, 3:160 or 80+80Mhz */
} __packed;

struct hostcmd_cmd_set_new_stn {
	struct hostcmd_header cmd_hdr;
	u16 aid;
	u8 mac_addr[ETH_ALEN];
	u16 stn_id;
	u16 action;
	u16 reserved;
	struct peer_info peer_info;
	/* UAPSD_SUPPORT */
	u8 qos_info;
	u8 is_qos_sta;
	u32 fw_sta_ptr;
} __packed;

/*
 *     HOSTCMD_CMD_SET_APMODE
 */

struct hostcmd_cmd_set_apmode {
	struct hostcmd_header cmd_hdr;
	u8 apmode;
} __packed;

/*
 *     HOSTCMD_CMD_UPDATE_ENCRYPTION
 */

struct hostcmd_cmd_update_encryptoin {
	struct hostcmd_header cmd_hdr;
	/* Action type - see encr_action_type */
	u32 action_type;             /* encr_action_type */
	/* size of the data buffer attached. */
	u32 data_length;
	u8 mac_addr[ETH_ALEN];
	u8 action_data[1];
} __packed;

struct wep_type_key {
	/* WEP key material (max 128bit) */
	u8 key_material[MAX_ENCR_KEY_LENGTH];
} __packed;

struct encr_tkip_seqcnt {
	u16 low;
	u32 high;
} __packed;

struct tkip_type_key {
	/* TKIP Key material. Key type (group or pairwise key) is determined by flags */
	/* in KEY_PARAM_SET structure. */
	u8 key_material[MAX_ENCR_KEY_LENGTH];
	/* MIC keys */
	u8 tkip_tx_mic_key[MIC_KEY_LENGTH];
	u8 tkip_rx_mic_key[MIC_KEY_LENGTH];
	struct encr_tkip_seqcnt	tkip_rsc;
	struct encr_tkip_seqcnt	tkip_tsc;
} __packed;

struct aes_type_key {
	/* AES Key material */
	u8 key_material[MAX_ENCR_KEY_LENGTH];
} __packed;

union mwl_key_type {
	struct wep_type_key	wep_key;
	struct tkip_type_key tkip_key;
	struct aes_type_key	aes_key;
} __packed;

struct key_param_set {
	/* Total length of this structure (Key is variable size array) */
	u16 length;
	/* Key type - WEP, TKIP or AES-CCMP. */
	/* See definitions above */
	u16 key_type_id;
	/* key flags (ENCR_KEY_FLAG_XXX_ */
	u32 key_info;
	/* For WEP only - actual key index */
	u32 key_index;
	/* Size of the key */
	u16 key_len;
	/* Key material (variable size array) */
	union mwl_key_type key;
	u8 mac_addr[ETH_ALEN];
} __packed;

struct hostcmd_cmd_set_key {
	struct hostcmd_header cmd_hdr;
	/* Action type - see encr_action_type */
	u32 action_type;             /* encr_action_type */
	/* size of the data buffer attached. */
	u32 data_length;
	/* data buffer - maps to one KEY_PARAM_SET structure */
	struct key_param_set key_param;
} __packed;

/*
 *     HOSTCMD_CMD_BASTREAM
 */

struct ba_stream_flags {
	u32	ba_type:1;
	u32 ba_direction:3;
	u32 reserved:24;
} __packed;

struct ba_context {
	u32 context;
} __packed;

/* parameters for block ack creation */
struct create_ba_params {
	/* BA Creation flags - see above */
	struct ba_stream_flags flags;
	/* idle threshold */
	u32	idle_thrs;
	/* block ack transmit threshold (after how many pkts should we send BAR?) */
	u32	bar_thrs;
	/* receiver window size */
	u32 window_size;
	/* MAC Address of the BA partner */
	u8 peer_mac_addr[ETH_ALEN];
	/* Dialog Token */
	u8 dialog_token;
	/* TID for the traffic stream in this BA */
	u8 tid;
	/* shared memory queue ID (not sure if this is required) */
	u8 queue_id;
	u8 param_info;
	/* returned by firmware - firmware context pointer. */
	/* this context pointer will be passed to firmware for all future commands. */
	struct ba_context fw_ba_context;
	u8 reset_seq_no;             /** 0 or 1**/
	u16 current_seq;
	u8 sta_src_mac_addr[ETH_ALEN]; /* This is for virtual station in Sta proxy mode for V6FW */
} __packed;

/* new transmit sequence number information */
struct ba_update_seq_num {
	/* BA flags - see above */
	struct ba_stream_flags flags;
	/* returned by firmware in the create ba stream response */
	struct ba_context fw_ba_context;
	/* new sequence number for this block ack stream */
	u16 ba_seq_num;
} __packed;

struct ba_stream_context {
	/* BA Stream flags */
	struct ba_stream_flags flags;
	/* returned by firmware in the create ba stream response */
	struct ba_context fw_ba_context;
} __packed;

union ba_info {
	/* information required to create BA Stream... */
	struct create_ba_params	create_params;
	/* update starting/new sequence number etc. */
	struct ba_update_seq_num updt_seq_num;
	/* destroy an existing stream... */
	struct ba_stream_context destroy_params;
	/* destroy an existing stream... */
	struct ba_stream_context flush_params;
} __packed;

struct hostcmd_cmd_bastream {
	struct hostcmd_header cmd_hdr;
	u32 action_type;
	union ba_info ba_info;
} __packed;

/*
 *     HOSTCMD_CMD_DWDS_ENABLE
 */

struct hostcmd_cmd_dwds_enable {
	struct hostcmd_header cmd_hdr;
	u32 enable;                  /* 0 -- Disable. or 1 -- Enable. */
} __packed;

/*
 *     HOSTCMD_CMD_FW_FLUSH_TIMER
 */

struct hostcmd_cmd_fw_flush_timer {
	struct hostcmd_header cmd_hdr;
	u32 value;                   /* 0 -- Disable. > 0 -- holds time value in usecs. */
} __packed;

/*
 *     HOSTCMD_CMD_SET_CDD
 */

struct hostcmd_cmd_set_cdd {
	struct hostcmd_header cmd_hdr;
	u32 enable;
} __packed;

/* PRIVATE FUNCTION DECLARATION
*/

static bool mwl_fwcmd_chk_adapter(struct mwl_priv *priv);
static int mwl_fwcmd_exec_cmd(struct mwl_priv *priv, unsigned short cmd);
static void mwl_fwcmd_send_cmd(struct mwl_priv *priv);
static int mwl_fwcmd_wait_complete(struct mwl_priv *priv, unsigned short cmd);

static int mwl_fwcmd_802_11_radio_control(struct mwl_priv *priv, bool enable, bool force);
static int mwl_fwcmd_get_tx_powers(struct mwl_priv *priv, u16 *powlist, u16 ch,
				   u16 band, u16 width, u16 sub_ch);
static int mwl_fwcmd_set_tx_powers(struct mwl_priv *priv, u16 txpow[], u8 action,
				   u16 ch, u16 band, u16 width, u16 sub_ch);
static u8 mwl_fwcmd_get_80m_pri_chnl_offset(u8 channel);
static void mwl_fwcmd_parse_beacon(struct mwl_priv *priv,
				   struct mwl_vif *vif, u8 *beacon, int len);
static int mwl_fwcmd_set_ies(struct mwl_priv *priv, struct mwl_vif *mwl_vif);
static int mwl_fwcmd_set_ap_beacon(struct mwl_priv *priv, struct mwl_vif *mwl_vif,
				   struct ieee80211_bss_conf *bss_conf);
static int mwl_fwcmd_encryption_set_cmd_info(struct hostcmd_cmd_set_key *cmd,
					     u8 *addr, struct ieee80211_key_conf *key);

#ifdef MWL_DEBUG
static char *mwl_fwcmd_get_cmd_string(unsigned short cmd);
#endif

/* PUBLIC FUNCTION DEFINITION
*/

void mwl_fwcmd_reset(struct ieee80211_hw *hw)
{
	struct mwl_priv *priv;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	if (mwl_fwcmd_chk_adapter(priv)) {
		writel(ISR_RESET, priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
	}

	WLDBG_EXIT(DBG_LEVEL_2);
}

void mwl_fwcmd_int_enable(struct ieee80211_hw *hw)
{
	struct mwl_priv *priv;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	if (mwl_fwcmd_chk_adapter(priv)) {
		writel(0x00, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);
		writel((MACREG_A2HRIC_BIT_MASK),
		       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);
	}

	WLDBG_EXIT(DBG_LEVEL_2);
}

void mwl_fwcmd_int_disable(struct ieee80211_hw *hw)
{
	struct mwl_priv *priv;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	if (mwl_fwcmd_chk_adapter(priv)) {
		writel(0x00, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);
	}

	WLDBG_EXIT(DBG_LEVEL_2);
}

int mwl_fwcmd_get_hw_specs(struct ieee80211_hw *hw)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_get_hw_spec *pcmd;
	unsigned long flags;
	int i;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_get_hw_spec *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	WLDBG_PRINT("pcmd = %x", (unsigned int)pcmd);
	memset(pcmd, 0x00, sizeof(*pcmd));
	memset(&pcmd->permanent_addr[0], 0xff, ETH_ALEN);
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_GET_HW_SPEC);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->fw_awake_cookie = ENDIAN_SWAP32(priv->pphys_cmd_buf + 2048);

	WLDBG_DUMP_DATA(DBG_LEVEL_2, (void *)pcmd, sizeof(*pcmd));

	while (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_HW_SPEC)) {
		WLDBG_PRINT("failed execution");
		WL_MSEC_SLEEP(1000);
		WLDBG_PRINT("repeat command = %x", (unsigned int)pcmd);
	}

	memcpy(&priv->hw_data.mac_addr[0], pcmd->permanent_addr, ETH_ALEN);
	priv->desc_data[0].wcb_base = ENDIAN_SWAP32(pcmd->wcb_base0) & 0x0000ffff;
#if SYSADPT_NUM_OF_DESC_DATA > 3
	for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)
		priv->desc_data[i].wcb_base = ENDIAN_SWAP32(pcmd->wcb_base[i - 1]) & 0x0000ffff;
#endif
	priv->desc_data[0].rx_desc_read = ENDIAN_SWAP32(pcmd->rxpd_rd_ptr) & 0x0000ffff;
	priv->desc_data[0].rx_desc_write = ENDIAN_SWAP32(pcmd->rxpd_wr_ptr) & 0x0000ffff;
	priv->hw_data.region_code = ENDIAN_SWAP16(pcmd->region_code) & 0x00ff;
	priv->hw_data.fw_release_num = ENDIAN_SWAP32(pcmd->fw_release_num);
	priv->hw_data.max_num_tx_desc = ENDIAN_SWAP16(pcmd->num_wcb);
	priv->hw_data.max_num_mc_addr = ENDIAN_SWAP16(pcmd->num_mcast_addr);
	priv->hw_data.num_antennas = ENDIAN_SWAP16(pcmd->num_antenna);
	priv->hw_data.hw_version = pcmd->version;
	priv->hw_data.host_interface = pcmd->host_if;

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);

	WLDBG_EXIT_INFO(DBG_LEVEL_2,
			"region code is %i (0x%x), HW version is %i (0x%x)",
		priv->hw_data.region_code, priv->hw_data.region_code,
		priv->hw_data.hw_version, priv->hw_data.hw_version);

	return 0;
}

int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_set_hw_spec *pcmd;
	unsigned long flags;
	int i;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	/* Info for debugging
	*/
    WLDBG_PRINT("%s ...", __FUNCTION__);
    WLDBG_PRINT("  -->pPhysTxRing[0] = %x", priv->desc_data[0].pphys_tx_ring);
    WLDBG_PRINT("  -->pPhysTxRing[1] = %x", priv->desc_data[1].pphys_tx_ring);
    WLDBG_PRINT("  -->pPhysTxRing[2] = %x", priv->desc_data[2].pphys_tx_ring);
    WLDBG_PRINT("  -->pPhysTxRing[3] = %x", priv->desc_data[3].pphys_tx_ring);
    WLDBG_PRINT("  -->pPhysRxRing    = %x", priv->desc_data[0].pphys_rx_ring);
    WLDBG_PRINT("  -->numtxq %d wcbperq %d totalrxwcb %d",
		SYSADPT_NUM_OF_DESC_DATA, SYSADPT_MAX_NUM_TX_DESC, SYSADPT_MAX_NUM_RX_DESC);

	pcmd = (struct hostcmd_cmd_set_hw_spec *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_HW_SPEC);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->wcb_base[0] = ENDIAN_SWAP32(priv->desc_data[0].pphys_tx_ring);
#if SYSADPT_NUM_OF_DESC_DATA > 3
	for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)
		pcmd->wcb_base[i] = ENDIAN_SWAP32(priv->desc_data[i].pphys_tx_ring);
#endif
	pcmd->tx_wcb_num_per_queue = ENDIAN_SWAP32(SYSADPT_MAX_NUM_TX_DESC);
	pcmd->num_tx_queues = ENDIAN_SWAP32(SYSADPT_NUM_OF_DESC_DATA);
	pcmd->total_rx_wcb = ENDIAN_SWAP32(SYSADPT_MAX_NUM_RX_DESC);
	pcmd->rxpd_wr_ptr = ENDIAN_SWAP32(priv->desc_data[0].pphys_rx_ring);
	pcmd->disablembss = 0;

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_HW_SPEC)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_get_stat(struct ieee80211_hw *hw,
		       struct ieee80211_low_level_stats *stats)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_802_11_get_stat *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_802_11_get_stat *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_802_11_GET_STAT);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_GET_STAT)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	stats->dot11ACKFailureCount =
		ENDIAN_SWAP32(pcmd->ack_failures);
	stats->dot11RTSFailureCount =
		ENDIAN_SWAP32(pcmd->rts_failures);
	stats->dot11FCSErrorCount =
		ENDIAN_SWAP32(pcmd->rx_fcs_errors);
	stats->dot11RTSSuccessCount =
		ENDIAN_SWAP32(pcmd->rts_successes);

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_radio_enable(struct ieee80211_hw *hw)
{
	struct mwl_priv *priv;
	int rc;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	rc = mwl_fwcmd_802_11_radio_control(priv, true, false);

	WLDBG_EXIT(DBG_LEVEL_2);

	return rc;
}

int mwl_fwcmd_radio_disable(struct ieee80211_hw *hw)
{
	struct mwl_priv *priv;
	int rc;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	rc = mwl_fwcmd_802_11_radio_control(priv, false, false);

	WLDBG_EXIT(DBG_LEVEL_2);

	return rc;
}

int mwl_fwcmd_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble)
{
	struct mwl_priv *priv;
	int rc;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	priv->radio_short_preamble = short_preamble;
	rc = mwl_fwcmd_802_11_radio_control(priv, true, true);

	WLDBG_EXIT(DBG_LEVEL_2);

	return rc;
}

int mwl_fwcmd_max_tx_power(struct ieee80211_hw *hw,
			   struct ieee80211_conf *conf, u8 fraction)
{
	struct ieee80211_channel *channel = conf->chandef.chan;
	struct mwl_priv *priv;
	int reduce_val = 0;
	u16 band = 0, width = 0, sub_ch = 0;
	u16 maxtxpow[SYSADPT_TX_POWER_LEVEL_TOTAL];
	int i, tmp;
	int rc;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	switch (fraction) {
	case 0:
		reduce_val = 0;    /* Max */
		break;
	case 1:
		reduce_val = 2;    /* 75% -1.25db */
		break;
	case 2:
		reduce_val = 3;    /* 50% -3db */
		break;
	case 3:
		reduce_val = 6;    /* 25% -6db */
		break;
	default:
		reduce_val = 0xff; /* larger than case 3,  pCmd->MaxPowerLevel is min */
		break;
	}

	if (channel->band == IEEE80211_BAND_2GHZ)
		band = FREQ_BAND_2DOT4GHZ;
	else if (channel->band == IEEE80211_BAND_5GHZ)
		band = FREQ_BAND_5GHZ;

	if ((conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) ||
	    (conf->chandef.width == NL80211_CHAN_WIDTH_20)) {
		width = CH_20_MHz_WIDTH;
		sub_ch = NO_EXT_CHANNEL;

	} else if (conf->chandef.width == NL80211_CHAN_WIDTH_40) {
		width = CH_40_MHz_WIDTH;
		if (conf->chandef.center_freq1 > channel->center_freq)
			sub_ch = EXT_CH_ABOVE_CTRL_CH;
		else
			sub_ch = EXT_CH_BELOW_CTRL_CH;
	} else if (conf->chandef.width == NL80211_CHAN_WIDTH_80) {
		width = CH_80_MHz_WIDTH;
		if (conf->chandef.center_freq1 > channel->center_freq)
			sub_ch = EXT_CH_ABOVE_CTRL_CH;
		else
			sub_ch = EXT_CH_BELOW_CTRL_CH;
	}

	if ((priv->powinited & 2) == 0) {
		mwl_fwcmd_get_tx_powers(priv, priv->max_tx_pow,
					channel->hw_value, band, width, sub_ch);
		priv->powinited |= 2;
	}

	if ((priv->powinited & 1) == 0) {
		mwl_fwcmd_get_tx_powers(priv, priv->target_powers,
					channel->hw_value, band, width, sub_ch);
		priv->powinited |= 1;
	}

	for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++) {
		if (priv->target_powers[i] > priv->max_tx_pow[i])
			tmp = priv->max_tx_pow[i];
		else
			tmp = priv->target_powers[i];
		maxtxpow[i] = ((tmp - reduce_val) > 0) ? (tmp - reduce_val) : 0;
	}

	rc = mwl_fwcmd_set_tx_powers(priv, maxtxpow, HOSTCMD_ACT_GEN_SET,
				     channel->hw_value, band, width, sub_ch);

	WLDBG_EXIT_INFO(DBG_LEVEL_2, "return code: %d", rc);

	return rc;
}

int mwl_fwcmd_tx_power(struct ieee80211_hw *hw,
		       struct ieee80211_conf *conf, u8 fraction)
{
	struct ieee80211_channel *channel = conf->chandef.chan;
	struct mwl_priv *priv;
	int reduce_val = 0;
	u16 band = 0, width = 0, sub_ch = 0;
	u16 txpow[SYSADPT_TX_POWER_LEVEL_TOTAL];
	int index, found = 0;
	int i, tmp;
	int rc;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	switch (fraction) {
	case 0:
		reduce_val = 0;    /* Max */
		break;
	case 1:
		reduce_val = 2;    /* 75% -1.25db */
		break;
	case 2:
		reduce_val = 3;    /* 50% -3db */
		break;
	case 3:
		reduce_val = 6;    /* 25% -6db */
		break;
	default:
		reduce_val = 0xff; /* larger than case 3,  pCmd->MaxPowerLevel is min */
		break;
	}

	if (channel->band == IEEE80211_BAND_2GHZ)
		band = FREQ_BAND_2DOT4GHZ;
	else if (channel->band == IEEE80211_BAND_5GHZ)
		band = FREQ_BAND_5GHZ;

	if ((conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) ||
	    (conf->chandef.width == NL80211_CHAN_WIDTH_20)) {
		width = CH_20_MHz_WIDTH;
		sub_ch = NO_EXT_CHANNEL;
	} else if (conf->chandef.width == NL80211_CHAN_WIDTH_40) {
		width = CH_40_MHz_WIDTH;
		if (conf->chandef.center_freq1 > channel->center_freq)
			sub_ch = EXT_CH_ABOVE_CTRL_CH;
		else
			sub_ch = EXT_CH_BELOW_CTRL_CH;
	} else if (conf->chandef.width == NL80211_CHAN_WIDTH_80) {
		width = CH_80_MHz_WIDTH;
		if (conf->chandef.center_freq1 > channel->center_freq)
			sub_ch = EXT_CH_ABOVE_CTRL_CH;
		else
			sub_ch = EXT_CH_BELOW_CTRL_CH;
	}

	/* search tx power table if exist
	*/
	for (index = 0; index < SYSADPT_MAX_NUM_CHANNELS; index++) {
		/* do nothing if table is not loaded
		*/
		if (priv->tx_pwr_tbl[index].channel == 0)
			break;

		if (priv->tx_pwr_tbl[index].channel == channel->hw_value) {
			priv->cdd = priv->tx_pwr_tbl[index].cdd;
			priv->txantenna2 = priv->tx_pwr_tbl[index].txantenna2;

			if (priv->tx_pwr_tbl[index].setcap)
				priv->powinited = 0x01;
			else
				priv->powinited = 0x02;

			for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++) {
				if (priv->tx_pwr_tbl[index].setcap)
					priv->max_tx_pow[i] = priv->tx_pwr_tbl[index].tx_power[i];
				else
					priv->target_powers[i] = priv->tx_pwr_tbl[index].tx_power[i];
			}

			found = 1;
			break;
		}
	}

	if ((priv->powinited & 2) == 0) {
		mwl_fwcmd_get_tx_powers(priv, priv->max_tx_pow,
					channel->hw_value, band, width, sub_ch);

		priv->powinited |= 2;
	}

	if ((priv->powinited & 1) == 0) {
		mwl_fwcmd_get_tx_powers(priv, priv->target_powers,
					channel->hw_value, band, width, sub_ch);

		priv->powinited |= 1;
	}

	for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++) {
		if (found) {
			if ((priv->tx_pwr_tbl[index].setcap)
				&& (priv->tx_pwr_tbl[index].tx_power[i] > priv->max_tx_pow[i]))
				tmp = priv->max_tx_pow[i];
			else
				tmp = priv->tx_pwr_tbl[index].tx_power[i];
		} else {
			if (priv->target_powers[i] > priv->max_tx_pow[i])
				tmp = priv->max_tx_pow[i];
			else
				tmp = priv->target_powers[i];
		}

		txpow[i] = ((tmp - reduce_val) > 0) ? (tmp - reduce_val) : 0;
	}

	rc = mwl_fwcmd_set_tx_powers(priv, txpow, HOSTCMD_ACT_GEN_SET_LIST,
				     channel->hw_value, band, width, sub_ch);

	WLDBG_EXIT_INFO(DBG_LEVEL_2, "return code: %d", rc);

	return rc;
}

int mwl_fwcmd_rf_antenna(struct ieee80211_hw *hw, int dir, int antenna)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_802_11_rf_antenna *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_802_11_rf_antenna *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_802_11_RF_ANTENNA);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));

	pcmd->action = ENDIAN_SWAP16(dir);

	if (dir == WL_ANTENNATYPE_RX) {
		u8 rx_antenna = 4; /* if auto, set 4 rx antennas in SC2 */
		
		if (antenna != 0)
			pcmd->antenna_mode = ENDIAN_SWAP16(antenna);
		else
			pcmd->antenna_mode = ENDIAN_SWAP16(rx_antenna);
	} else {
		u8 tx_antenna = 0xf; /* if auto, set 4 tx antennas in SC2 */

		if (antenna != 0)
			pcmd->antenna_mode = ENDIAN_SWAP16(antenna);
		else
			pcmd->antenna_mode = ENDIAN_SWAP16(tx_antenna);
	}

	WLDBG_DUMP_DATA(DBG_LEVEL_2, (void *)pcmd, sizeof(*pcmd));

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_RF_ANTENNA)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_broadcast_ssid_enable(struct ieee80211_hw *hw,
				    struct ieee80211_vif *vif, bool enable)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_broadcast_ssid_enable *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	pcmd = (struct hostcmd_cmd_broadcast_ssid_enable *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_BROADCAST_SSID_ENABLE);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;
	pcmd->enable = ENDIAN_SWAP32(enable);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BROADCAST_SSID_ENABLE)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_rf_channel(struct ieee80211_hw *hw,
			     struct ieee80211_conf *conf)
{
	struct ieee80211_channel *channel = conf->chandef.chan;
	struct mwl_priv *priv;
	struct hostcmd_cmd_set_rf_channel *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_set_rf_channel *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_RF_CHANNEL);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->action = ENDIAN_SWAP16(WL_SET);
	pcmd->curr_chnl = channel->hw_value;

	if (channel->band == IEEE80211_BAND_2GHZ)
		pcmd->chnl_flags.freq_band = FREQ_BAND_2DOT4GHZ;
	else if (channel->band == IEEE80211_BAND_5GHZ)
		pcmd->chnl_flags.freq_band = FREQ_BAND_5GHZ;

	if ((conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) ||
	    (conf->chandef.width == NL80211_CHAN_WIDTH_20)) {
		pcmd->chnl_flags.chnl_width = CH_20_MHz_WIDTH;
		pcmd->chnl_flags.act_primary = ACT_PRIMARY_CHAN_0;
	} else if (conf->chandef.width == NL80211_CHAN_WIDTH_40) {
		pcmd->chnl_flags.chnl_width = CH_40_MHz_WIDTH;
		if (conf->chandef.center_freq1 > channel->center_freq)
			pcmd->chnl_flags.act_primary = ACT_PRIMARY_CHAN_0;
		else
			pcmd->chnl_flags.act_primary = ACT_PRIMARY_CHAN_1;
	} else if (conf->chandef.width == NL80211_CHAN_WIDTH_80) {
		pcmd->chnl_flags.chnl_width = CH_80_MHz_WIDTH;
		pcmd->chnl_flags.act_primary =
			mwl_fwcmd_get_80m_pri_chnl_offset(pcmd->curr_chnl);
	}

	WLDBG_DUMP_DATA(DBG_LEVEL_2, (void *)pcmd, sizeof(*pcmd));

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_RF_CHANNEL)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_aid(struct ieee80211_hw *hw,
		      struct ieee80211_vif *vif, u8 *bssid, u16 aid)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_set_aid *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	pcmd = (struct hostcmd_cmd_set_aid *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_AID);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;
	pcmd->aid = ENDIAN_SWAP16(aid);
	memcpy(pcmd->mac_addr, bssid, ETH_ALEN);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_AID)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_infra_mode(struct ieee80211_hw *hw,
			     struct ieee80211_vif *vif)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_set_infra_mode *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	pcmd = (struct hostcmd_cmd_set_infra_mode *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_INFRA_MODE);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_INFRA_MODE)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_rts_threshold(struct ieee80211_hw *hw, int threshold)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_802_11_rts_thsd *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_802_11_rts_thsd *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_802_11_RTS_THSD);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->action  = ENDIAN_SWAP16(WL_SET);
	pcmd->threshold = ENDIAN_SWAP16(threshold);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_RTS_THSD)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_edca_params(struct ieee80211_hw *hw, u8 index,
			      u16 cw_min, u16 cw_max, u8 aifs, u16 txop)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_set_edca_params *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_set_edca_params *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_EDCA_PARAMS);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));

	pcmd->action = ENDIAN_SWAP16(0xffff);;
	pcmd->txop = ENDIAN_SWAP16(txop);
	pcmd->cw_max = ENDIAN_SWAP32(ilog2(cw_max + 1));
	pcmd->cw_min = ENDIAN_SWAP32(ilog2(cw_min + 1));
	pcmd->aifsn = aifs;
	pcmd->txq_num = index;

	/* The array index defined in qos.h has a reversed bk and be.
	 * The HW queue was not used this way; the qos code needs to be changed or
	 * checked
	 */
	if (index == 0)
		pcmd->txq_num = 1;
	else if (index == 1)
		pcmd->txq_num = 0;

	WLDBG_DUMP_DATA(DBG_LEVEL_2, (void *)pcmd, sizeof(*pcmd));

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_EDCA_PARAMS)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_set_wmm_mode *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_set_wmm_mode *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_WMM_MODE);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->action = ENDIAN_SWAP16(enable ? WL_ENABLE : WL_DISABLE);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_WMM_MODE)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_use_fixed_rate(struct ieee80211_hw *hw, int mcast, int mgmt)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_set_fixed_rate *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_set_fixed_rate *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_FIXED_RATE);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));

	pcmd->action = ENDIAN_SWAP32(HOSTCMD_ACT_NOT_USE_FIXED_RATE);
	pcmd->multicast_rate = mcast;
	pcmd->management_rate = mgmt;

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_FIXED_RATE)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_rate_adapt_mode(struct ieee80211_hw *hw, u16 mode)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_set_rate_adapt_mode *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_set_rate_adapt_mode *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_RATE_ADAPT_MODE);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->action = ENDIAN_SWAP16(WL_SET);
	pcmd->rate_adapt_mode = ENDIAN_SWAP16(mode);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_RATE_ADAPT_MODE)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_mac_addr_client(struct ieee80211_hw *hw,
				  struct ieee80211_vif *vif, u8 *mac_addr)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_set_mac_addr *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	pcmd = (struct hostcmd_cmd_set_mac_addr *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_MAC_ADDR);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;
	pcmd->mac_type = WL_MAC_TYPE_SECONDARY_CLIENT;
	memcpy(pcmd->mac_addr, mac_addr, ETH_ALEN);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_MAC_ADDR)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_get_watchdog_bitmap *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_get_watchdog_bitmap *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_GET_WATCHDOG_BITMAP);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_WATCHDOG_BITMAP)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	*bitmap = pcmd->watchdog_bitmap;

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_remove_mac_addr(struct ieee80211_hw *hw,
			      struct ieee80211_vif *vif, u8 *mac_addr)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_set_mac_addr *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	pcmd = (struct hostcmd_cmd_set_mac_addr *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_DEL_MAC_ADDR);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;
	memcpy(pcmd->mac_addr, mac_addr, ETH_ALEN);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_DEL_MAC_ADDR)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_bss_start(struct ieee80211_hw *hw,
			struct ieee80211_vif *vif, bool enable)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_bss_start *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	if (enable && (priv->running_bsses & (1 << mwl_vif->macid)))
		return 0;

	if (!enable && !(priv->running_bsses & (1 << mwl_vif->macid)))
		return 0;

	pcmd = (struct hostcmd_cmd_bss_start *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_BSS_START);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;

	if (enable) {
		pcmd->enable = ENDIAN_SWAP32(WL_ENABLE);
	} else {
		if (mwl_vif->macid == 0)
			pcmd->enable = ENDIAN_SWAP32(WL_DISABLE);
		else
			pcmd->enable = ENDIAN_SWAP32(WL_DISABLE_VMAC);
	}

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BSS_START)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	if (enable)
		priv->running_bsses |= (1 << mwl_vif->macid);
	else
		priv->running_bsses &= ~(1 << mwl_vif->macid);

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_beacon(struct ieee80211_hw *hw,
			 struct ieee80211_vif *vif, u8 *beacon, int len)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	mwl_fwcmd_parse_beacon(priv, mwl_vif, beacon, len);

	if (mwl_fwcmd_set_ies(priv, mwl_vif))
		goto err;

	if (mwl_fwcmd_set_ap_beacon(priv, mwl_vif, &vif->bss_conf))
		goto err;

	mwl_vif->beacon_info.valid = false;

	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;

err:

	mwl_vif->beacon_info.valid = false;

	WLDBG_EXIT_INFO(DBG_LEVEL_2, "set beacon failed");

	return -EIO;
}

int mwl_fwcmd_set_new_stn_add(struct ieee80211_hw *hw,
			      struct ieee80211_vif *vif,
			      struct ieee80211_sta *sta)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_set_new_stn *pcmd;
	unsigned long flags;
	u32 rates;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	BUG_ON(!sta);

	pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_NEW_STN);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;

	pcmd->action = ENDIAN_SWAP16(HOSTCMD_ACT_STA_ACTION_ADD);
	if (mwl_vif->is_sta == true) {
		pcmd->aid = 0;
		pcmd->stn_id = 0;
	} else {
		pcmd->aid = ENDIAN_SWAP16(sta->aid);
		pcmd->stn_id = ENDIAN_SWAP16(sta->aid);
	}
	memcpy(pcmd->mac_addr, sta->addr, ETH_ALEN);

	if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ)
		rates = sta->supp_rates[IEEE80211_BAND_2GHZ];
	else
		rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5;
	pcmd->peer_info.legacy_rate_bitmap = ENDIAN_SWAP32(rates);

	if (sta->ht_cap.ht_supported) {
		pcmd->peer_info.ht_rates[0] = sta->ht_cap.mcs.rx_mask[0];
		pcmd->peer_info.ht_rates[1] = sta->ht_cap.mcs.rx_mask[1];
		pcmd->peer_info.ht_rates[2] = sta->ht_cap.mcs.rx_mask[2];
		pcmd->peer_info.ht_rates[3] = sta->ht_cap.mcs.rx_mask[3];
		pcmd->peer_info.ht_cap_info = ENDIAN_SWAP16(sta->ht_cap.cap);
		pcmd->peer_info.mac_ht_param_info = (sta->ht_cap.ampdu_factor & 3) |
			((sta->ht_cap.ampdu_density & 7) << 2);
	}

	if (sta->vht_cap.vht_supported) {
		pcmd->peer_info.vht_max_rx_mcs = ENDIAN_SWAP32(*((u32 *)&sta->vht_cap.vht_mcs.rx_mcs_map));
		pcmd->peer_info.vht_cap = ENDIAN_SWAP32(sta->vht_cap.cap);
		pcmd->peer_info.vht_rx_channel_width = sta->bandwidth;
	}

	pcmd->is_qos_sta = sta->wme;
	pcmd->qos_info = ((sta->uapsd_queues << 4) | (sta->max_sp << 1));

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	if (mwl_vif->is_sta == true) {
		memcpy(pcmd->mac_addr, mwl_vif->sta_mac, ETH_ALEN);
		
		if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
			MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
			WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
			return -EIO;
		}
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_new_stn_add_self(struct ieee80211_hw *hw,
				   struct ieee80211_vif *vif)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_set_new_stn *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_NEW_STN);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;

	pcmd->action = ENDIAN_SWAP16(HOSTCMD_ACT_STA_ACTION_ADD);
	memcpy(pcmd->mac_addr, vif->addr, ETH_ALEN);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_new_stn_del(struct ieee80211_hw *hw,
			      struct ieee80211_vif *vif, u8 *addr)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_set_new_stn *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_NEW_STN);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;

	pcmd->action = ENDIAN_SWAP16(HOSTCMD_ACT_STA_ACTION_REMOVE);
	memcpy(pcmd->mac_addr, addr, ETH_ALEN);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	if  (mwl_vif->is_sta == true) {
		memcpy(pcmd->mac_addr, mwl_vif->sta_mac, ETH_ALEN);
		
		if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
			MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
			WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
			return -EIO;
		}
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_apmode(struct ieee80211_hw *hw, u8 apmode)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_set_apmode *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_set_apmode *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_APMODE);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->apmode = apmode;

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_APMODE)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_update_encryption_enable(struct ieee80211_hw *hw,
				       struct ieee80211_vif *vif, u8 *addr, u8 encr_type)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_update_encryptoin *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	pcmd = (struct hostcmd_cmd_update_encryptoin *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_UPDATE_ENCRYPTION);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;

	pcmd->action_type = ENDIAN_SWAP32(ENCR_ACTION_ENABLE_HW_ENCR);
	memcpy(pcmd->mac_addr, addr, ETH_ALEN);
	pcmd->action_data[0] = encr_type;

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	if (mwl_vif->is_sta == true) {
		if (memcmp(mwl_vif->bssid, addr, ETH_ALEN) == 0)
			memcpy(pcmd->mac_addr, mwl_vif->sta_mac, ETH_ALEN);
		else
			memcpy(pcmd->mac_addr, mwl_vif->bssid, ETH_ALEN);

		if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
			MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
			WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
			return -EIO;
		}
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_encryption_set_key(struct ieee80211_hw *hw,
				 struct ieee80211_vif *vif, u8 *addr,
				 struct ieee80211_key_conf *key)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_set_key *pcmd;
	unsigned long flags;
	int rc;
	int keymlen;
	u32 action;
	u8 idx;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	pcmd = (struct hostcmd_cmd_set_key *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_UPDATE_ENCRYPTION);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;

	rc = mwl_fwcmd_encryption_set_cmd_info(pcmd, addr, key);
	if (rc) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "encryption not support");
		return rc;
	}

	idx = key->keyidx;

	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
		action = ENCR_ACTION_TYPE_SET_KEY;
	else
		action = ENCR_ACTION_TYPE_SET_GROUP_KEY;

	switch (key->cipher) {
	case WLAN_CIPHER_SUITE_WEP40:
	case WLAN_CIPHER_SUITE_WEP104:
		if (!mwl_vif->wep_key_conf[idx].enabled) {
			memcpy(mwl_vif->wep_key_conf[idx].key, key,
			       sizeof(*key) + key->keylen);
			mwl_vif->wep_key_conf[idx].enabled = 1;
		}

		keymlen = key->keylen;
		action = ENCR_ACTION_TYPE_SET_KEY;
		break;
	case WLAN_CIPHER_SUITE_TKIP:
		keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH;
		break;
	case WLAN_CIPHER_SUITE_CCMP:
		keymlen = key->keylen;
		break;
	default:
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "encryption not support");
		return -ENOTSUPP;
	}

	memcpy((void *)&pcmd->key_param.key, key->key, keymlen);
	pcmd->action_type = ENDIAN_SWAP32(action);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	if (mwl_vif->is_sta == true) {
		if (memcmp(mwl_vif->bssid, addr, ETH_ALEN) == 0)
			memcpy(pcmd->key_param.mac_addr, mwl_vif->sta_mac, ETH_ALEN);
		else
			memcpy(pcmd->key_param.mac_addr, mwl_vif->bssid, ETH_ALEN);

		if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
			MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
			WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
			return -EIO;
		}
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_encryption_remove_key(struct ieee80211_hw *hw,
				    struct ieee80211_vif *vif, u8 *addr,
				    struct ieee80211_key_conf *key)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_set_key *pcmd;
	unsigned long flags;
	int rc;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	pcmd = (struct hostcmd_cmd_set_key *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_UPDATE_ENCRYPTION);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;

	rc = mwl_fwcmd_encryption_set_cmd_info(pcmd, addr, key);
	if (rc) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "encryption not support");
		return rc;
	}

	pcmd->action_type = ENDIAN_SWAP32(ENCR_ACTION_TYPE_REMOVE_KEY);

	if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
	    key->cipher == WLAN_CIPHER_SUITE_WEP104)
		mwl_vif->wep_key_conf[key->keyidx].enabled = 0;

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_check_ba(struct ieee80211_hw *hw,
		       struct mwl_ampdu_stream *stream,
		       struct ieee80211_vif *vif)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_bastream *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!stream);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_BASTREAM);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;
	pcmd->cmd_hdr.result = ENDIAN_SWAP16(0xffff);

	pcmd->action_type = ENDIAN_SWAP32(BA_CHECK_STREAM);
	memcpy(&pcmd->ba_info.create_params.peer_mac_addr[0],
	       stream->sta->addr, ETH_ALEN);
	pcmd->ba_info.create_params.tid = stream->tid;
	pcmd->ba_info.create_params.flags.ba_type =
		BASTREAM_FLAG_IMMEDIATE_TYPE;
	pcmd->ba_info.create_params.flags.ba_direction =
		BASTREAM_FLAG_DIRECTION_UPSTREAM;
	pcmd->ba_info.create_params.queue_id = stream->idx;

	WLDBG_DUMP_DATA(DBG_LEVEL_2, (void *)pcmd, sizeof(*pcmd));

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BASTREAM)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	if (pcmd->cmd_hdr.result != 0) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "result error");
		return -EINVAL;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_create_ba(struct ieee80211_hw *hw,
			struct mwl_ampdu_stream *stream,
			u8 buf_size, struct ieee80211_vif *vif)
{
	struct mwl_priv *priv;
	struct mwl_vif *mwl_vif;
	struct hostcmd_cmd_bastream *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!stream);

	BUG_ON(!vif);
	mwl_vif = MWL_VIF(vif);
	BUG_ON(!mwl_vif);

	pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_BASTREAM);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;
	pcmd->cmd_hdr.result = ENDIAN_SWAP16(0xffff);

	pcmd->action_type = ENDIAN_SWAP32(BA_CREATE_STREAM);
	pcmd->ba_info.create_params.bar_thrs = ENDIAN_SWAP32(buf_size);
	pcmd->ba_info.create_params.window_size = ENDIAN_SWAP32(buf_size);
	memcpy(&pcmd->ba_info.create_params.peer_mac_addr[0],
	       stream->sta->addr, ETH_ALEN);
	pcmd->ba_info.create_params.tid = stream->tid;
	pcmd->ba_info.create_params.flags.ba_type =
		BASTREAM_FLAG_IMMEDIATE_TYPE;
	pcmd->ba_info.create_params.flags.ba_direction =
		BASTREAM_FLAG_DIRECTION_UPSTREAM;
	pcmd->ba_info.create_params.queue_id = stream->idx;
	pcmd->ba_info.create_params.param_info =
		(stream->sta->ht_cap.ampdu_factor &
		 IEEE80211_HT_AMPDU_PARM_FACTOR) |
		((stream->sta->ht_cap.ampdu_density << 2) &
		 IEEE80211_HT_AMPDU_PARM_DENSITY);
	pcmd->ba_info.create_params.reset_seq_no = 1;
	pcmd->ba_info.create_params.current_seq = ENDIAN_SWAP16(0);

	WLDBG_DUMP_DATA(DBG_LEVEL_2, (void *)pcmd, sizeof(*pcmd));

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BASTREAM)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	if (pcmd->cmd_hdr.result != 0) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "result error");
		return -EINVAL;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_destroy_ba(struct ieee80211_hw *hw,
			 u8 idx)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_bastream *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_BASTREAM);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));

	pcmd->action_type = ENDIAN_SWAP32(BA_DESTROY_STREAM);
	pcmd->ba_info.destroy_params.flags.ba_type =
		BASTREAM_FLAG_IMMEDIATE_TYPE;
	pcmd->ba_info.destroy_params.flags.ba_direction =
		BASTREAM_FLAG_DIRECTION_UPSTREAM;
	pcmd->ba_info.destroy_params.fw_ba_context.context = ENDIAN_SWAP32(idx);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BASTREAM)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

/* caller must hold priv->locks.stream_lock when calling the stream functions
*/
struct mwl_ampdu_stream *mwl_fwcmd_add_stream(struct ieee80211_hw *hw,
					      struct ieee80211_sta *sta, u8 tid)
{
	struct mwl_priv *priv;
	struct mwl_ampdu_stream *stream;
	int i;

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	BUG_ON(!sta);

	for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) {
		stream = &priv->ampdu[i];

		if (stream->state == AMPDU_NO_STREAM) {
			stream->sta = sta;
			stream->state = AMPDU_STREAM_NEW;
			stream->tid = tid;
			stream->idx = i;
			return stream;
		}
	}

	return NULL;
}

int mwl_fwcmd_start_stream(struct ieee80211_hw *hw,
			   struct mwl_ampdu_stream *stream)
{
	BUG_ON(!hw);
	BUG_ON(!stream);

	/* if the stream has already been started, don't start it again
	*/
	if (stream->state != AMPDU_STREAM_NEW)
		return 0;

	return (ieee80211_start_tx_ba_session(stream->sta, stream->tid, 0));
}

void mwl_fwcmd_remove_stream(struct ieee80211_hw *hw,
			     struct mwl_ampdu_stream *stream)
{
	BUG_ON(!hw);
	BUG_ON(!stream);

	memset(stream, 0, sizeof(*stream));
}

struct mwl_ampdu_stream *mwl_fwcmd_lookup_stream(struct ieee80211_hw *hw,
						 u8 *addr, u8 tid)
{
	struct mwl_priv *priv;
	struct mwl_ampdu_stream *stream;
	int i;

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) {
		stream = &priv->ampdu[i];

		if (stream->state == AMPDU_NO_STREAM)
			continue;

		if (!memcmp(stream->sta->addr, addr, ETH_ALEN) &&
		    stream->tid == tid)
			return stream;
	}

	return NULL;
}

bool mwl_fwcmd_ampdu_allowed(struct ieee80211_sta *sta, u8 tid)
{
	struct mwl_sta *sta_info;
	struct mwl_tx_info *tx_stats;

	BUG_ON(!sta);
	sta_info = MWL_STA(sta);
	BUG_ON(!sta_info);

	BUG_ON(tid >= SYSADPT_MAX_TID);

	tx_stats = &sta_info->tx_stats[tid];

	return (sta_info->is_ampdu_allowed &&
		tx_stats->pkts > SYSADPT_AMPDU_PACKET_THRESHOLD);
}

int mwl_fwcmd_set_dwds_stamode(struct ieee80211_hw *hw, bool enable)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_dwds_enable *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_dwds_enable *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_DWDS_ENABLE);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->enable = ENDIAN_SWAP32(enable);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_DWDS_ENABLE)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_fw_flush_timer(struct ieee80211_hw *hw, u32 value)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_fw_flush_timer *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_fw_flush_timer *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_FW_FLUSH_TIMER);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->value = ENDIAN_SWAP32(value);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_FW_FLUSH_TIMER)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw)
{
	struct mwl_priv *priv;
	struct hostcmd_cmd_set_cdd *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!hw);
	priv = hw->priv;
	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_set_cdd *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_CDD);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->enable = ENDIAN_SWAP32(priv->cdd);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_CDD)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

/* PRIVATE FUNCTION DEFINITION
*/

static bool mwl_fwcmd_chk_adapter(struct mwl_priv *priv)
{
	u32 regval;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!priv);

	regval = readl(priv->iobase1 + MACREG_REG_INT_CODE);

	if (regval == 0xffffffff) {
		WLDBG_ERROR(DBG_LEVEL_2, "adapter is not existed");
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "adapter is not existed");

		return false;
	}

	WLDBG_EXIT(DBG_LEVEL_2);

	return true;
}

static int mwl_fwcmd_exec_cmd(struct mwl_priv *priv, unsigned short cmd)
{
	bool busy = false;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!priv);

	if (!mwl_fwcmd_chk_adapter(priv)) {
		WLDBG_ERROR(DBG_LEVEL_2, "no adapter existed");
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "no adapter plugged in");
		priv->in_send_cmd = false;
		return -EIO;
	}

	if (!priv->in_send_cmd) {
		priv->in_send_cmd = true;
		mwl_fwcmd_send_cmd(priv);
		if (mwl_fwcmd_wait_complete(priv, 0x8000 | cmd)) {
			WLDBG_ERROR(DBG_LEVEL_2, "timeout");
			WLDBG_EXIT_INFO(DBG_LEVEL_2, "timeout");
			priv->in_send_cmd = false;
			return -EIO;
		}
	} else {
		WLDBG_WARNING(DBG_LEVEL_2, "previous command is still running");
		busy = true;
	}

	WLDBG_EXIT(DBG_LEVEL_2);
	if (!busy)
		priv->in_send_cmd = false;

	return 0;
}

static void mwl_fwcmd_send_cmd(struct mwl_priv *priv)
{
	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!priv);

	writel(priv->pphys_cmd_buf, priv->iobase1 + MACREG_REG_GEN_PTR);
	writel(MACREG_H2ARIC_BIT_DOOR_BELL,
	       priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);

	WLDBG_EXIT(DBG_LEVEL_2);
}

static int mwl_fwcmd_wait_complete(struct mwl_priv *priv, unsigned short cmd)
{
	unsigned int curr_iteration = MAX_WAIT_FW_COMPLETE_ITERATIONS;

	volatile unsigned short int_code = 0;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!priv);

	do {
		int_code = ENDIAN_SWAP16(priv->pcmd_buf[0]);
		WL_MSEC_SLEEP(1);
	} while ((int_code != cmd) && (--curr_iteration));

	if (curr_iteration == 0) {
		WLDBG_ERROR(DBG_LEVEL_2, "cmd 0x%04x=%s timed out",
			    cmd, mwl_fwcmd_get_cmd_string(cmd));
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "timeout");
		return -EIO;
	}

	WL_MSEC_SLEEP(3);

	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

static int mwl_fwcmd_802_11_radio_control(struct mwl_priv *priv, bool enable, bool force)
{
	struct hostcmd_cmd_802_11_radio_control *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!priv);

	if (enable == priv->radio_on && !force)
		return 0;

	pcmd = (struct hostcmd_cmd_802_11_radio_control *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_802_11_RADIO_CONTROL);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->action = ENDIAN_SWAP16(WL_SET);
	pcmd->control = ENDIAN_SWAP16(priv->radio_short_preamble ?
		WL_AUTO_PREAMBLE : WL_LONG_PREAMBLE);
	pcmd->radio_on = ENDIAN_SWAP16(enable ? WL_ENABLE : WL_DISABLE);

	WLDBG_DUMP_DATA(DBG_LEVEL_2, (void *)pcmd, sizeof(*pcmd));

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_RADIO_CONTROL)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	priv->radio_on = enable;

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

static int mwl_fwcmd_get_tx_powers(struct mwl_priv *priv, u16 *powlist, u16 ch,
				   u16 band, u16 width, u16 sub_ch)
{
	struct hostcmd_cmd_802_11_tx_power *pcmd;
	unsigned long flags;
	int i;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_802_11_tx_power *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_802_11_TX_POWER);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->action = ENDIAN_SWAP16(HOSTCMD_ACT_GEN_GET_LIST);
	pcmd->ch = ENDIAN_SWAP16(ch);
	pcmd->bw = ENDIAN_SWAP16(width);
	pcmd->band = ENDIAN_SWAP16(band);
	pcmd->sub_ch = ENDIAN_SWAP16(sub_ch);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_TX_POWER)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++)
		powlist[i] = (u8)ENDIAN_SWAP16(pcmd->power_level_list[i]);

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

static int mwl_fwcmd_set_tx_powers(struct mwl_priv *priv, u16 txpow[], u8 action,
				   u16 ch, u16 band, u16 width, u16 sub_ch)
{
	struct hostcmd_cmd_802_11_tx_power *pcmd;
	unsigned long flags;
	int i;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!priv);

	pcmd = (struct hostcmd_cmd_802_11_tx_power *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_802_11_TX_POWER);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->action = ENDIAN_SWAP16(action);
	pcmd->ch = ENDIAN_SWAP16(ch);
	pcmd->bw = ENDIAN_SWAP16(width);
	pcmd->band = ENDIAN_SWAP16(band);
	pcmd->sub_ch = ENDIAN_SWAP16(sub_ch);

	for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++)
		pcmd->power_level_list[i] = ENDIAN_SWAP16(txpow[i]);

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_TX_POWER)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;
}

static u8 mwl_fwcmd_get_80m_pri_chnl_offset(u8 channel)
{
	u8 act_primary = ACT_PRIMARY_CHAN_0;

	switch (channel) {
	case 36:
		act_primary = ACT_PRIMARY_CHAN_0;
		break;
	case 40:
		act_primary = ACT_PRIMARY_CHAN_1;
		break;
	case 44:
		act_primary = ACT_PRIMARY_CHAN_2;
		break;
	case 48:
		act_primary = ACT_PRIMARY_CHAN_3;
		break;
	case 52:
		act_primary = ACT_PRIMARY_CHAN_0;
		break;
	case 56:
		act_primary = ACT_PRIMARY_CHAN_1;
		break;
	case 60:
		act_primary = ACT_PRIMARY_CHAN_2;
		break;
	case 64:
		act_primary = ACT_PRIMARY_CHAN_3;
		break;
	case 100:
		act_primary = ACT_PRIMARY_CHAN_0;
		break;
	case 104:
		act_primary = ACT_PRIMARY_CHAN_1;
		break;
	case 108:
		act_primary = ACT_PRIMARY_CHAN_2;
		break;
	case 112:
		act_primary = ACT_PRIMARY_CHAN_3;
		break;
	case 116:
		act_primary = ACT_PRIMARY_CHAN_0;
		break;
	case 120:
		act_primary = ACT_PRIMARY_CHAN_1;
		break;
	case 124:
		act_primary = ACT_PRIMARY_CHAN_2;
		break;
	case 128:
		act_primary = ACT_PRIMARY_CHAN_3;
		break;
	case 132:
		act_primary = ACT_PRIMARY_CHAN_0;
		break;
	case 136:
		act_primary = ACT_PRIMARY_CHAN_1;
		break;
	case 140:
		act_primary = ACT_PRIMARY_CHAN_2;
		break;
	case 144:
		act_primary = ACT_PRIMARY_CHAN_3;
		break;
	case 149:
		act_primary = ACT_PRIMARY_CHAN_0;
		break;
	case 153:
		act_primary = ACT_PRIMARY_CHAN_1;
		break;
	case 157:
		act_primary = ACT_PRIMARY_CHAN_2;
		break;
	case 161:
		act_primary = ACT_PRIMARY_CHAN_3;
		break;
	}

	return act_primary;
}

static void mwl_fwcmd_parse_beacon(struct mwl_priv *priv,
				   struct mwl_vif *vif, u8 *beacon, int len)
{
	struct ieee80211_mgmt *mgmt;
	struct beacon_info *beacon_info;
	int baselen;
	u8 *pos;
	size_t left;
	bool elem_parse_failed;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!vif);
	BUG_ON(!beacon);

	mgmt = (struct ieee80211_mgmt *)beacon;

	baselen = (u8 *)mgmt->u.beacon.variable - (u8 *)mgmt;
	if (baselen > len)
		return;

	beacon_info = &vif->beacon_info;
	memset(beacon_info, 0, sizeof(struct beacon_info));
	beacon_info->valid = false;
	beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0];
	beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0];

	beacon_info->cap_info = mgmt->u.beacon.capab_info;

	pos = (u8 *)mgmt->u.beacon.variable;
	left = len - baselen;

	elem_parse_failed = false;

	while (left >= 2) {
		u8 id, elen;

		id = *pos++;
		elen = *pos++;
		left -= 2;

		if (elen > left) {
			elem_parse_failed = true;
			break;
		}

		switch (id) {
		case WLAN_EID_SUPP_RATES:
		case WLAN_EID_EXT_SUPP_RATES:
			{
				int idx, basic_idx, oprate_idx;
				u8 rate;

				for (basic_idx = 0; basic_idx < SYSADPT_MAX_DATA_RATES_G; basic_idx++) {
					if (beacon_info->bss_basic_rate_set[basic_idx] == 0)
						break;
				}

				for (oprate_idx = 0; oprate_idx < SYSADPT_MAX_DATA_RATES_G; oprate_idx++) {
					if (beacon_info->op_rate_set[oprate_idx] == 0)
						break;
				}

				for (idx = 0; idx < elen; idx++) {
					rate = pos[idx];

					if ((rate & 0x80) != 0) {
						if (basic_idx < SYSADPT_MAX_DATA_RATES_G)
							beacon_info->bss_basic_rate_set[basic_idx++] = rate & 0x7f;
						else {
							elem_parse_failed = true;
							break;
						}
					}

					if (oprate_idx < SYSADPT_MAX_DATA_RATES_G)
						beacon_info->op_rate_set[oprate_idx++] = rate & 0x7f;
					else {
						elem_parse_failed = true;
						break;
					}
				}
			}
			break;
		case WLAN_EID_RSN:
			beacon_info->ie_rsn48_len = (elen + 2);
			beacon_info->ie_rsn48_ptr = (pos - 2);
			break;
		case WLAN_EID_HT_CAPABILITY:
		case WLAN_EID_HT_OPERATION:
		case WLAN_EID_OVERLAP_BSS_SCAN_PARAM:
		case WLAN_EID_EXT_CAPABILITY:
			beacon_info->ie_ht_len += (elen + 2);
			if (beacon_info->ie_ht_len > sizeof(beacon_info->ie_list_ht)) {
				elem_parse_failed = true;
				break;
			} else {
				*beacon_info->ie_ht_ptr++ = id;
				*beacon_info->ie_ht_ptr++ = elen;
				memcpy(beacon_info->ie_ht_ptr, pos, elen);
				beacon_info->ie_ht_ptr += elen;
			}
			break;
		case WLAN_EID_VHT_CAPABILITY:
		case WLAN_EID_VHT_OPERATION:
		case WLAN_EID_OPMODE_NOTIF:
			beacon_info->ie_vht_len += (elen + 2);
			if (beacon_info->ie_vht_len > sizeof(beacon_info->ie_list_vht)) {
				elem_parse_failed = true;
				break;
			} else {
				*beacon_info->ie_vht_ptr++ = id;
				*beacon_info->ie_vht_ptr++ = elen;
				memcpy(beacon_info->ie_vht_ptr, pos, elen);
				beacon_info->ie_vht_ptr += elen;
			}
			break;
		case WLAN_EID_VENDOR_SPECIFIC:
			if ((pos[0] == 0x00) && (pos[1] == 0x50) && (pos[2] == 0xf2)) {
				if (pos[3] == 0x01) {
					beacon_info->ie_rsn_len = (elen + 2);
					beacon_info->ie_rsn_ptr = (pos - 2);
				}

				if (pos[3] == 0x02) {
					beacon_info->ie_wmm_len = (elen + 2);
					beacon_info->ie_wmm_ptr = (pos - 2);
				}
			}
			break;
		default:
			break;
		}

		left -= elen;
		pos += elen;
	}

	if (elem_parse_failed == false) {
		beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0];
		beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0];
		beacon_info->valid = true;

		WLDBG_INFO(DBG_LEVEL_2, "wmm:%d, rsn:%d, rsn48:%d, ht:%d, vht:%d",
			   beacon_info->ie_wmm_len, beacon_info->ie_rsn_len, beacon_info->ie_rsn48_len,
			beacon_info->ie_ht_len, beacon_info->ie_vht_len);

		WLDBG_DUMP_DATA(DBG_LEVEL_2, beacon_info->bss_basic_rate_set,
				SYSADPT_MAX_DATA_RATES_G);

		WLDBG_DUMP_DATA(DBG_LEVEL_2, beacon_info->op_rate_set,
				SYSADPT_MAX_DATA_RATES_G);
	}

	WLDBG_EXIT_INFO(DBG_LEVEL_2, "parse valid:%d", beacon_info->valid);

	return;
}

static int mwl_fwcmd_set_ies(struct mwl_priv *priv, struct mwl_vif *mwl_vif)
{
	struct hostcmd_cmd_set_ies *pcmd;
	unsigned long flags;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!priv);
	BUG_ON(!mwl_vif);

	if (!mwl_vif->beacon_info.valid)
		return -EINVAL;

	if (mwl_vif->beacon_info.ie_ht_len > sizeof(pcmd->ie_list_ht))
		goto einval;

	if (mwl_vif->beacon_info.ie_vht_len > sizeof(pcmd->ie_list_vht))
		goto einval;

	pcmd = (struct hostcmd_cmd_set_ies *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_SET_IES);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;

	pcmd->action = ENDIAN_SWAP16(HOSTCMD_ACT_GEN_SET);

	pcmd->ie_list_len_ht = mwl_vif->beacon_info.ie_ht_len;
	memcpy(pcmd->ie_list_ht, mwl_vif->beacon_info.ie_ht_ptr,
	       pcmd->ie_list_len_ht);

	pcmd->ie_list_len_vht = mwl_vif->beacon_info.ie_vht_len;
	memcpy(pcmd->ie_list_vht, mwl_vif->beacon_info.ie_vht_ptr,
	       pcmd->ie_list_len_vht);

	WLDBG_DUMP_DATA(DBG_LEVEL_2, (void *)pcmd, sizeof(*pcmd));

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_IES)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;

einval:

	WLDBG_EXIT_INFO(DBG_LEVEL_2, "length of IE is too long");

	return -EINVAL;
}

static int mwl_fwcmd_set_ap_beacon(struct mwl_priv *priv, struct mwl_vif *mwl_vif,
				   struct ieee80211_bss_conf *bss_conf)
{
	struct hostcmd_cmd_ap_beacon *pcmd;
	unsigned long flags;
	struct ds_params *phy_ds_param_set;

	WLDBG_ENTER(DBG_LEVEL_2);

	BUG_ON(!priv);
	BUG_ON(!mwl_vif);
	BUG_ON(!bss_conf);

	if (!mwl_vif->beacon_info.valid)
		return -EINVAL;

	/* wmm structure of start command is defined less one byte, due to following
	 * field country is not used, add byte one to bypass the check.
	 */
	if (mwl_vif->beacon_info.ie_wmm_len > (sizeof(pcmd->start_cmd.wmm_param) + 1))
		goto ielenerr;

	if (mwl_vif->beacon_info.ie_rsn_len > sizeof(pcmd->start_cmd.rsn_ie))
		goto ielenerr;

	if (mwl_vif->beacon_info.ie_rsn48_len > sizeof(pcmd->start_cmd.rsn48_ie))
		goto ielenerr;

	pcmd = (struct hostcmd_cmd_ap_beacon *)&priv->pcmd_buf[0];

	MWL_SPIN_LOCK(&priv->locks.fwcmd_lock);

	memset(pcmd, 0x00, sizeof(*pcmd));
	pcmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_AP_BEACON);
	pcmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*pcmd));
	pcmd->cmd_hdr.macid = mwl_vif->macid;

	memcpy(pcmd->start_cmd.sta_mac_addr, mwl_vif->bssid, ETH_ALEN);
	memcpy(pcmd->start_cmd.ssid, bss_conf->ssid, bss_conf->ssid_len);
	pcmd->start_cmd.bss_type = 1;
	pcmd->start_cmd.bcn_period  = ENDIAN_SWAP16(bss_conf->beacon_int);
	pcmd->start_cmd.dtim_period = bss_conf->dtim_period; /* 8bit */

	phy_ds_param_set = &pcmd->start_cmd.phy_param_set.ds_param_set;
	phy_ds_param_set->elem_id = WLAN_EID_DS_PARAMS;
	phy_ds_param_set->len = sizeof(phy_ds_param_set->current_chnl);
	phy_ds_param_set->current_chnl = bss_conf->chandef.chan->hw_value;

	pcmd->start_cmd.probe_delay = ENDIAN_SWAP16(10);
	pcmd->start_cmd.cap_info = ENDIAN_SWAP16(mwl_vif->beacon_info.cap_info);

	memcpy(&pcmd->start_cmd.wmm_param, mwl_vif->beacon_info.ie_wmm_ptr,
	       mwl_vif->beacon_info.ie_wmm_len);

	memcpy(&pcmd->start_cmd.rsn_ie, mwl_vif->beacon_info.ie_rsn_ptr,
	       mwl_vif->beacon_info.ie_rsn_len);

	memcpy(&pcmd->start_cmd.rsn48_ie, mwl_vif->beacon_info.ie_rsn48_ptr,
	       mwl_vif->beacon_info.ie_rsn48_len);

	memcpy(pcmd->start_cmd.bss_basic_rate_set, mwl_vif->beacon_info.bss_basic_rate_set,
	       SYSADPT_MAX_DATA_RATES_G);

	memcpy(pcmd->start_cmd.op_rate_set, mwl_vif->beacon_info.op_rate_set,
	       SYSADPT_MAX_DATA_RATES_G);

	WLDBG_DUMP_DATA(DBG_LEVEL_2, (void *)pcmd, sizeof(*pcmd));

	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_AP_BEACON)) {
		MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
		WLDBG_EXIT_INFO(DBG_LEVEL_2, "failed execution");
		return -EIO;
	}

	MWL_SPIN_UNLOCK(&priv->locks.fwcmd_lock);
	WLDBG_EXIT(DBG_LEVEL_2);

	return 0;

ielenerr:

	WLDBG_EXIT_INFO(DBG_LEVEL_2, "length of IE is too long");

	return -EINVAL;
}

static int mwl_fwcmd_encryption_set_cmd_info(struct hostcmd_cmd_set_key *cmd,
					     u8 *addr, struct ieee80211_key_conf *key)
{
	cmd->cmd_hdr.cmd = ENDIAN_SWAP16(HOSTCMD_CMD_UPDATE_ENCRYPTION);
	cmd->cmd_hdr.len = ENDIAN_SWAP16(sizeof(*cmd));
	cmd->key_param.length = ENDIAN_SWAP16(sizeof(*cmd) -
		offsetof(struct hostcmd_cmd_set_key, key_param));
	cmd->key_param.key_index = ENDIAN_SWAP32(key->keyidx);
	cmd->key_param.key_len = ENDIAN_SWAP16(key->keylen);
	memcpy(cmd->key_param.mac_addr, addr, ETH_ALEN);

	switch (key->cipher) {
	case WLAN_CIPHER_SUITE_WEP40:
	case WLAN_CIPHER_SUITE_WEP104:
		cmd->key_param.key_type_id = ENDIAN_SWAP16(KEY_TYPE_ID_WEP);
		if (key->keyidx == 0)
			cmd->key_param.key_info = ENDIAN_SWAP32(ENCR_KEY_FLAG_WEP_TXKEY);
		break;
	case WLAN_CIPHER_SUITE_TKIP:
		cmd->key_param.key_type_id = ENDIAN_SWAP16(KEY_TYPE_ID_TKIP);
		cmd->key_param.key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
			? ENDIAN_SWAP32(ENCR_KEY_FLAG_PAIRWISE)
			: ENDIAN_SWAP32(ENCR_KEY_FLAG_TXGROUPKEY);
		cmd->key_param.key_info |= ENDIAN_SWAP32(ENCR_KEY_FLAG_MICKEY_VALID
			| ENCR_KEY_FLAG_TSC_VALID);
		break;
	case WLAN_CIPHER_SUITE_CCMP:
		cmd->key_param.key_type_id = ENDIAN_SWAP16(KEY_TYPE_ID_AES);
		cmd->key_param.key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
			? ENDIAN_SWAP32(ENCR_KEY_FLAG_PAIRWISE)
			: ENDIAN_SWAP32(ENCR_KEY_FLAG_TXGROUPKEY);
		break;
	default:
		return -ENOTSUPP;
	}

	return 0;
}

#ifdef MWL_DEBUG
static char *mwl_fwcmd_get_cmd_string(unsigned short cmd)
{
	int max_entries = 0;
	int curr_cmd = 0;

	static const struct {
		u16 cmd;
		char *cmd_string;
	} cmds[] = {
		{ HOSTCMD_CMD_GET_HW_SPEC, "GetHwSpecifications" },
		{ HOSTCMD_CMD_SET_HW_SPEC, "SetHwSepcifications" },
		{ HOSTCMD_CMD_802_11_GET_STAT, "80211GetStat" },
		{ HOSTCMD_CMD_802_11_RADIO_CONTROL, "80211RadioControl" },
		{ HOSTCMD_CMD_802_11_TX_POWER, "80211TxPower" },
		{ HOSTCMD_CMD_802_11_RF_ANTENNA, "80211RfAntenna" },
		{ HOSTCMD_CMD_BROADCAST_SSID_ENABLE, "broadcast_ssid_enable" },
		{ HOSTCMD_CMD_SET_RF_CHANNEL, "SetRfChannel" },
		{ HOSTCMD_CMD_SET_AID, "SetAid" },
		{ HOSTCMD_CMD_SET_INFRA_MODE, "SetInfraMode" },
		{ HOSTCMD_CMD_802_11_RTS_THSD, "80211RtsThreshold" },
		{ HOSTCMD_CMD_SET_EDCA_PARAMS, "SetEDCAParams" },
		{ HOSTCMD_CMD_SET_WMM_MODE, "SetWMMMode" },
		{ HOSTCMD_CMD_SET_FIXED_RATE, "SetFixedRate" },
		{ HOSTCMD_CMD_SET_IES, "SetInformationElements" },
		{ HOSTCMD_CMD_SET_RATE_ADAPT_MODE, "SetRateAdaptationMode" },
		{ HOSTCMD_CMD_SET_MAC_ADDR, "SetMacAddr" },
		{ HOSTCMD_CMD_GET_WATCHDOG_BITMAP, "GetWatchdogBitMap" },
		{ HOSTCMD_CMD_DEL_MAC_ADDR. "DelMacAddr" },
		{ HOSTCMD_CMD_BSS_START, "BssStart" },
		{ HOSTCMD_CMD_AP_BEACON, "SetApBeacon" },
		{ HOSTCMD_CMD_SET_NEW_STN, "SetNewStation" },
		{ HOSTCMD_CMD_SET_APMODE, "SetApMode" },
		{ HOSTCMD_CMD_UPDATE_ENCRYPTION, "UpdateEncryption" },
		{ HOSTCMD_CMD_BASTREAM, "BAStream" },
		{ HOSTCMD_CMD_DWDS_ENABLE, "DwdsEnable" },
		{ HOSTCMD_CMD_FW_FLUSH_TIMER, "FwFlushTimer" },
		{ HOSTCMD_CMD_SET_CDD, "SetCDD" },
	};

	WLDBG_ENTER(DBG_LEVEL_2);

	max_entries = sizeof(cmds) / sizeof(cmds[0]);

	for (curr_cmd = 0; curr_cmd < max_entries; curr_cmd++) {
		if ((cmd & 0x7fff) == cmds[curr_cmd].cmd) {
			WLDBG_EXIT(DBG_LEVEL_2);
			return cmds[curr_cmd].cmd_string;
		}
	}

	WLDBG_EXIT_INFO(DBG_LEVEL_2, "unknown");

	return "unknown";
}
#endif
