/**
 * @file mlan_cfp.c
 *
 *  @brief This file contains WLAN client mode channel, frequency and power
 *  related code
 *
 *
 *  Copyright 2009-2023 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.
 *
 *  Alternatively, this software may be distributed under the terms of GPL v2.
 *  SPDX-License-Identifier:    GPL-2.0
 *
 *
 */

/*************************************************************
 * Change Log:
 *	04/16/2009: initial version
 ************************************************************/

#include "mlan.h"
#include "mlan_util.h"
#include "mlan_fw.h"
#include "mlan_join.h"
#include "mlan_main.h"
#include "mlan_11h.h"

/********************************************************
 *			Local Variables
 ********************************************************/

/** 100mW */
#define WLAN_TX_PWR_DEFAULT 20
/** 100mW */
#define WLAN_TX_PWR_00_DEFAULT 20
/** 100mW */
#define WLAN_TX_PWR_US_DEFAULT 20
/** 100mW */
#define WLAN_TX_PWR_JP_BG_DEFAULT 20
/** 200mW */
#define WLAN_TX_PWR_JP_A_DEFAULT 23
/** 100mW */
#define WLAN_TX_PWR_EMEA_DEFAULT 20
/** 2000mW */
#define WLAN_TX_PWR_CN_2000MW 33
/** 200mW */
#define WLAN_TX_PWR_200MW 23
/** 1000mW */
#define WLAN_TX_PWR_1000MW 30
/** 250mW */
#define WLAN_TX_PWR_250MW 24

/** Region code mapping */
typedef struct _country_code_mapping {
	/** Region */
	t_u8 country_code[COUNTRY_CODE_LEN];
	/** Code for B/G CFP table */
	t_u8 cfp_code_bg;
	/** Code for A CFP table */
	t_u8 cfp_code_a;
} country_code_mapping_t;

#define EU_CFP_CODE_BG 0x30
#define EU_CFP_CODE_A 0x30

/** Region code mapping table */
static country_code_mapping_t country_code_mapping[] = {
	{"WW", 0x00, 0x00}, /* World       */
	{"US", 0x10, 0x10}, /* US FCC      */
	{"CA", 0x10, 0x20}, /* IC Canada   */
	{"SG", 0x10, 0x10}, /* Singapore   */
	{"EU", 0x30, 0x30}, /* ETSI        */
	{"AU", 0x30, 0x30}, /* Australia   */
	{"KR", 0x30, 0x30}, /* Republic Of Korea */
	{"JP", 0xFF, 0x40}, /* Japan       */
	{"CN", 0x30, 0x50}, /* China       */
	{"BR", 0x01, 0x09}, /* Brazil      */
	{"RU", 0x30, 0x0f}, /* Russia      */
	{"IN", 0x10, 0x06}, /* India       */
	{"MY", 0x30, 0x06}, /* Malaysia    */
	{"NZ", 0x30, 0x30}, /* New Zeland  */
	{"MX", 0x30, 0x07}, /* Mexico */
};

/** Country code for ETSI */
static t_u8 eu_country_code_table[][COUNTRY_CODE_LEN] = {
	"AL", "AD", "AT", "AU", "BE", "BA", "BG", "HR", "CY", "CZ", "DK", "EE",
	"FI", "FR", "MK", "DE", "GR", "HU", "IS", "IE", "IT", "LV", "LI", "LT",
	"LU", "MT", "MD", "MC", "ME", "NL", "NO", "PL", "RO", "SM", "RS", "SI",
	"SK", "ES", "SE", "CH", "TR", "UA", "UK", "GB", "NZ", "DZ", "AO", "AM",
	"AW", "BH", "BD", "BT", "BO", "BQ", "BW", "VG", "BF", "BI", "KH", "CL",
	"KM", "CG", "CD", "CW", "EG", "FO", "GF", "PF", "GE", "GI", "GP", "HK",
	"IN", "ID", "IM", "IL", "JE", "KE", "XK", "KW", "LA", "LR", "MW", "MV",
	"MQ", "MR", "YT", "MA", "MZ", "MM", "NA", "NC", "NE", "NG", "OM", "PS",
	"PT", "QA", "RW", "RE", "BL", "MF", "VC", "SA", "SC", "ZA", "SZ", "SY",
	"TZ", "TG", "TN", "AE", "VA", "EH", "YE", "ZM", "ZW"};

/**
 * The structure for Channel-Frequency-Power table
 */
typedef struct _cfp_table {
	/** Region or Code */
	t_u8 code;
	/** Frequency/Power */
	chan_freq_power_t *cfp;
	/** No of CFP flag */
	int cfp_no;
} cfp_table_t;

/* Format { Channel, Frequency (MHz), MaxTxPower } */
/** Band : 'B/G', Region: World Wide Safe */
static chan_freq_power_t channel_freq_power_00_BG[] = {
	{1, 2412, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{2, 2417, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{3, 2422, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{4, 2427, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{5, 2432, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{6, 2437, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{7, 2442, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{8, 2447, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{9, 2452, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{10, 2457, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{11, 2462, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{12, 2467, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x1d, 0, 0}},
	{13, 2472, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x1d, 0, 0}}};
/* Format { Channel, Frequency (MHz), MaxTxPower } */
/** Band: 'B/G', Region: USA FCC/Canada IC */
static chan_freq_power_t channel_freq_power_US_BG[] = {
	{1, 2412, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{2, 2417, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{3, 2422, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{4, 2427, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{5, 2432, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{6, 2437, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{7, 2442, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{8, 2447, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{9, 2452, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{10, 2457, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{11, 2462, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x1c, 0, 0}}};

/** Band: 'B/G', Region: Europe ETSI/China */
static chan_freq_power_t channel_freq_power_EU_BG[] = {
	{1, 2412, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{2, 2417, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{3, 2422, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{4, 2427, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{5, 2432, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{6, 2437, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{7, 2442, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{8, 2447, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{9, 2452, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{10, 2457, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{11, 2462, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{12, 2467, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{13, 2472, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x1c, 0, 0}}};

/** Band: 'B/G', Region: Japan */
static chan_freq_power_t channel_freq_power_JPN41_BG[] = {
	{1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}}};

/** Band: 'B/G', Region: Japan */
static chan_freq_power_t channel_freq_power_JPN40_BG[] = {
	{14, 2484, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1d, 0, 0}}};

/** Band: 'B/G', Region: Japan */
static chan_freq_power_t channel_freq_power_JPNFE_BG[] = {
	{1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT, MTRUE, {0x1d, 0, 0}},
	{13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT, MTRUE, {0x1d, 0, 0}}};

/** Band : 'B/G', Region: Brazil */
static chan_freq_power_t channel_freq_power_BR_BG[] = {
	{1, 2412, WLAN_TX_PWR_1000MW, MFALSE, {0x1c, 0, 0}},
	{2, 2417, WLAN_TX_PWR_1000MW, MFALSE, {0x1c, 0, 0}},
	{3, 2422, WLAN_TX_PWR_1000MW, MFALSE, {0x1c, 0, 0}},
	{4, 2427, WLAN_TX_PWR_1000MW, MFALSE, {0x1c, 0, 0}},
	{5, 2432, WLAN_TX_PWR_1000MW, MFALSE, {0x1c, 0, 0}},
	{6, 2437, WLAN_TX_PWR_1000MW, MFALSE, {0x1c, 0, 0}},
	{7, 2442, WLAN_TX_PWR_1000MW, MFALSE, {0x1c, 0, 0}},
	{8, 2447, WLAN_TX_PWR_1000MW, MFALSE, {0x1c, 0, 0}},
	{9, 2452, WLAN_TX_PWR_1000MW, MFALSE, {0x1c, 0, 0}},
	{10, 2457, WLAN_TX_PWR_1000MW, MFALSE, {0x1c, 0, 0}},
	{11, 2462, WLAN_TX_PWR_1000MW, MFALSE, {0x1c, 0, 0}},
	{12, 2467, WLAN_TX_PWR_1000MW, MFALSE, {0x1c, 0, 0}},
	{13, 2472, WLAN_TX_PWR_1000MW, MFALSE, {0x1c, 0, 0}}};

/** Band : 'B/G', Region: Special */
static chan_freq_power_t channel_freq_power_SPECIAL_BG[] = {
	{1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}},
	{14, 2484, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE, {0x1c, 0, 0}}};

/**
 * The 2.4GHz CFP tables
 */
static cfp_table_t cfp_table_BG[] = {
	{
		0x01, /* Brazil */
		channel_freq_power_BR_BG,
		NELEMENTS(channel_freq_power_BR_BG),
	},
	{
		0x00, /* World FCC */
		channel_freq_power_00_BG,
		NELEMENTS(channel_freq_power_00_BG),
	},
	{
		0x10, /* US FCC */
		channel_freq_power_US_BG,
		NELEMENTS(channel_freq_power_US_BG),
	},
	{
		0x20, /* CANADA IC */
		channel_freq_power_US_BG,
		NELEMENTS(channel_freq_power_US_BG),
	},
	{
		0x30, /* EU */
		channel_freq_power_EU_BG,
		NELEMENTS(channel_freq_power_EU_BG),
	},
	{
		0x40, /* JAPAN */
		channel_freq_power_JPN40_BG,
		NELEMENTS(channel_freq_power_JPN40_BG),
	},
	{
		0x41, /* JAPAN */
		channel_freq_power_JPN41_BG,
		NELEMENTS(channel_freq_power_JPN41_BG),
	},
	{
		0x50, /* China */
		channel_freq_power_EU_BG,
		NELEMENTS(channel_freq_power_EU_BG),
	},
	{
		0xfe, /* JAPAN */
		channel_freq_power_JPNFE_BG,
		NELEMENTS(channel_freq_power_JPNFE_BG),
	},
	{
		0xff, /* Special */
		channel_freq_power_SPECIAL_BG,
		NELEMENTS(channel_freq_power_SPECIAL_BG),
	},
	/* Add new region here */
};

/** Number of the CFP tables for 2.4GHz */
#define MLAN_CFP_TABLE_SIZE_BG (NELEMENTS(cfp_table_BG))

/* Format { Channel, Frequency (MHz), MaxTxPower, DFS } */
/** Band: 'A', Region: World Wide Safe */
static chan_freq_power_t channel_freq_power_00_A[] = {
	{36, 5180, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x10, 0, 0}},
	{40, 5200, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x10, 0, 0}},
	{44, 5220, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x10, 0, 0}},
	{48, 5240, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x10, 0, 0}},
	{52, 5260, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{56, 5280, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{60, 5300, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{64, 5320, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{100, 5500, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{104, 5520, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{108, 5540, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{112, 5560, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{116, 5580, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{120, 5600, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{124, 5620, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{128, 5640, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{132, 5660, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{136, 5680, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{140, 5700, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{144, 5720, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0, 0}},
	{149, 5745, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x10, 0, 0}},
	{153, 5765, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x10, 0, 0}},
	{157, 5785, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x10, 0, 0}},
	{161, 5805, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x10, 0, 0}},
	{165, 5825, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x10, 0, 0}}};
/* Format { Channel, Frequency (MHz), MaxTxPower, DFS } */
/** Band: 'A', Region: USA FCC */
static chan_freq_power_t channel_freq_power_A[] = {
	{36, 5180, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{40, 5200, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{44, 5220, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{48, 5240, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{52, 5260, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{56, 5280, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{60, 5300, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{64, 5320, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{100, 5500, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{104, 5520, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{108, 5540, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{112, 5560, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{116, 5580, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{120, 5600, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{124, 5620, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{128, 5640, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{132, 5660, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{136, 5680, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{140, 5700, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{144, 5720, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{149, 5745, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{153, 5765, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{157, 5785, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{161, 5805, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{165, 5825, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{169, 5845, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{173, 5865, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{177, 5885, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}}};

/** Band: 'A', Region: Canada IC */
static chan_freq_power_t channel_freq_power_CAN_A[] = {
	{36, 5180, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{40, 5200, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{44, 5220, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{48, 5240, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{52, 5260, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{56, 5280, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{60, 5300, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{64, 5320, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{100, 5500, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{104, 5520, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{108, 5540, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{112, 5560, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{116, 5580, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{132, 5660, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{136, 5680, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{140, 5700, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{144, 5720, WLAN_TX_PWR_US_DEFAULT, MTRUE, {0x13, 0, 0}},
	{149, 5745, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{153, 5765, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{157, 5785, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{161, 5805, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}},
	{165, 5825, WLAN_TX_PWR_US_DEFAULT, MFALSE, {0x10, 0, 0}}};

/** Band: 'A', Region: Europe ETSI */
static chan_freq_power_t channel_freq_power_EU_A[] = {
	{36, 5180, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{40, 5200, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{44, 5220, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{48, 5240, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{52, 5260, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{56, 5280, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{60, 5300, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{64, 5320, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{100, 5500, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{104, 5520, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{108, 5540, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{112, 5560, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{116, 5580, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{120, 5600, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{124, 5620, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{128, 5640, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{132, 5660, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{136, 5680, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{140, 5700, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{149, 5745, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{153, 5765, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{157, 5785, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{161, 5805, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{165, 5825, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}}};

/** Band: 'A', Region: Japan */
static chan_freq_power_t channel_freq_power_JPN_A[] = {
	{36, 5180, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE, {0x10, 0, 0}},
	{40, 5200, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE, {0x10, 0, 0}},
	{44, 5220, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE, {0x10, 0, 0}},
	{48, 5240, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE, {0x10, 0, 0}},
	{52, 5260, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{56, 5280, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{60, 5300, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{64, 5320, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{100, 5500, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{104, 5520, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{108, 5540, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{112, 5560, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{116, 5580, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{120, 5600, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{124, 5620, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{128, 5640, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{132, 5660, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{136, 5680, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{140, 5700, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}},
	{144, 5720, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE, {0x13, 0, 0}}};

/** Band: 'A', Region: China */
static chan_freq_power_t channel_freq_power_CN_A[] = {
	{36, 5180, WLAN_TX_PWR_200MW, MFALSE, {0x10, 0, 0}},
	{40, 5200, WLAN_TX_PWR_200MW, MFALSE, {0x10, 0, 0}},
	{44, 5220, WLAN_TX_PWR_200MW, MFALSE, {0x10, 0, 0}},
	{48, 5240, WLAN_TX_PWR_200MW, MFALSE, {0x10, 0, 0}},
	{52, 5260, WLAN_TX_PWR_200MW, MTRUE, {0x13, 0, 0}},
	{56, 5280, WLAN_TX_PWR_200MW, MTRUE, {0x13, 0, 0}},
	{60, 5300, WLAN_TX_PWR_200MW, MTRUE, {0x13, 0, 0}},
	{64, 5320, WLAN_TX_PWR_200MW, MTRUE, {0x13, 0, 0}},
	{149, 5745, WLAN_TX_PWR_CN_2000MW, MFALSE, {0x10, 0, 0}},
	{153, 5765, WLAN_TX_PWR_CN_2000MW, MFALSE, {0x10, 0, 0}},
	{157, 5785, WLAN_TX_PWR_CN_2000MW, MFALSE, {0x10, 0, 0}},
	{161, 5805, WLAN_TX_PWR_CN_2000MW, MFALSE, {0x10, 0, 0}},
	{165, 5825, WLAN_TX_PWR_CN_2000MW, MFALSE, {0x10, 0, 0}}};

/** Band: 'A', NULL */
static chan_freq_power_t channel_freq_power_NULL_A[] = {};

/** Band: 'A', Region: Spain/Austria/Brazil */
static chan_freq_power_t channel_freq_power_SPN2_A[] = {
	{36, 5180, WLAN_TX_PWR_200MW, MFALSE, {0x10, 0, 0}},
	{40, 5200, WLAN_TX_PWR_200MW, MFALSE, {0x10, 0, 0}},
	{44, 5220, WLAN_TX_PWR_200MW, MFALSE, {0x10, 0, 0}},
	{48, 5240, WLAN_TX_PWR_200MW, MFALSE, {0x10, 0, 0}},
	{52, 5260, WLAN_TX_PWR_200MW, MTRUE, {0x13, 0, 0}},
	{56, 5280, WLAN_TX_PWR_200MW, MTRUE, {0x13, 0, 0}},
	{60, 5300, WLAN_TX_PWR_200MW, MTRUE, {0x13, 0, 0}},
	{64, 5320, WLAN_TX_PWR_200MW, MTRUE, {0x13, 0, 0}}};

/** Band: 'A', Region: Brazil */
static chan_freq_power_t channel_freq_power_BR1_A[] = {
	{100, 5500, WLAN_TX_PWR_250MW, MTRUE, {0x13, 0, 0}},
	{104, 5520, WLAN_TX_PWR_250MW, MTRUE, {0x13, 0, 0}},
	{108, 5540, WLAN_TX_PWR_250MW, MTRUE, {0x13, 0, 0}},
	{112, 5560, WLAN_TX_PWR_250MW, MTRUE, {0x13, 0, 0}},
	{116, 5580, WLAN_TX_PWR_250MW, MTRUE, {0x13, 0, 0}},
	{120, 5600, WLAN_TX_PWR_250MW, MTRUE, {0x13, 0, 0}},
	{124, 5620, WLAN_TX_PWR_250MW, MTRUE, {0x13, 0, 0}},
	{128, 5640, WLAN_TX_PWR_250MW, MTRUE, {0x13, 0, 0}},
	{132, 5660, WLAN_TX_PWR_250MW, MTRUE, {0x13, 0, 0}},
	{136, 5680, WLAN_TX_PWR_250MW, MTRUE, {0x13, 0, 0}},
	{140, 5700, WLAN_TX_PWR_250MW, MTRUE, {0x13, 0, 0}}};

/** Band: 'A', Region: Brazil */
static chan_freq_power_t channel_freq_power_BR2_A[] = {
	{149, 5745, WLAN_TX_PWR_1000MW, MFALSE, {0x10, 0, 0}},
	{153, 5765, WLAN_TX_PWR_1000MW, MFALSE, {0x10, 0, 0}},
	{157, 5785, WLAN_TX_PWR_1000MW, MFALSE, {0x10, 0, 0}},
	{161, 5805, WLAN_TX_PWR_1000MW, MFALSE, {0x10, 0, 0}},
	{165, 5825, WLAN_TX_PWR_1000MW, MFALSE, {0x10, 0, 0}}};

/** Band: 'A', Region: Russia */
static chan_freq_power_t channel_freq_power_RU_A[] = {
	{36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{52, 5260, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{56, 5280, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{60, 5300, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{64, 5320, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{132, 5660, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{136, 5680, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{140, 5700, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}}};

/** Band: 'A', Region: Mexico */
static chan_freq_power_t channel_freq_power_MX_A[] = {
	{36, 5180, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{40, 5200, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{44, 5220, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{48, 5240, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{52, 5260, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{56, 5280, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{60, 5300, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{64, 5320, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{100, 5500, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{104, 5520, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{108, 5540, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{112, 5560, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{116, 5580, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{132, 5660, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{136, 5680, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{140, 5700, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE, {0x13, 0, 0}},
	{149, 5745, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{153, 5765, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{157, 5785, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{161, 5805, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}},
	{165, 5825, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE, {0x10, 0, 0}}};

/** Band: 'A', Code: 1, Low band (5150-5250 MHz) channels */
static chan_freq_power_t channel_freq_power_low_band[] = {
	{36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}}};

/** Band: 'A', Code: 2, Lower middle band (5250-5350 MHz) channels */
static chan_freq_power_t channel_freq_power_lower_middle_band[] = {
	{52, 5260, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{56, 5280, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{60, 5300, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{64, 5320, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}}};

/** Band: 'A', Code: 3, Upper middle band (5470-5725 MHz) channels */
static chan_freq_power_t channel_freq_power_upper_middle_band[] = {
	{100, 5500, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{104, 5520, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{108, 5540, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{112, 5560, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{116, 5580, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{120, 5600, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{124, 5620, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{128, 5640, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{132, 5660, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{136, 5680, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
	{140, 5700, WLAN_TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}}};

/** Band: 'A', Code: 4, High band (5725-5850 MHz) channels */
static chan_freq_power_t channel_freq_power_high_band[] = {
	{149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{165, 5825, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}}};

/** Band: 'A', Code: 5, Low band (5150-5250 MHz) and
 *  High band (5725-5850 MHz) channels
 */
static chan_freq_power_t channel_freq_power_low_high_band[] = {
	{36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{165, 5825, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}}};

/** Band: 'A', Code: 6, Low band (5150-5250 MHz) and
 *  mid low (5260-5320) and High band (5725-5850 MHz) channels
 */
static chan_freq_power_t channel_freq_power_low_middle_high_band[] = {
	{36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{52, 5260, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{56, 5280, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{60, 5300, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{64, 5320, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
	{165, 5825, WLAN_TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}}};

/**
 * The 5GHz CFP tables
 */
static cfp_table_t cfp_table_A[] = {
	{0x1, /* Low band (5150-5250 MHz) channels */
	 channel_freq_power_low_band, NELEMENTS(channel_freq_power_low_band)},
	{0x2, /* Lower middle band (5250-5350 MHz) channels */
	 channel_freq_power_lower_middle_band,
	 NELEMENTS(channel_freq_power_lower_middle_band)},
	{0x3, /* Upper middle band (5470-5725 MHz) channels */
	 channel_freq_power_upper_middle_band,
	 NELEMENTS(channel_freq_power_upper_middle_band)},
	{0x4, /* High band (5725-5850 MHz) channels */
	 channel_freq_power_high_band, NELEMENTS(channel_freq_power_high_band)},
	{0x5, /* Low band (5150-5250 MHz) and
	       * High band (5725-5850 MHz) channels
	       */
	 channel_freq_power_low_high_band,
	 NELEMENTS(channel_freq_power_low_high_band)},
	{0x6, /* Low band (5150-5250 MHz)
	       * Mid band (5260-5320) and
	       * High band (5725-5850 MHz) channels
	       */
	 channel_freq_power_low_middle_high_band,
	 NELEMENTS(channel_freq_power_low_middle_high_band)},
	{
		0x07, /* Mexico */
		channel_freq_power_MX_A,
		NELEMENTS(channel_freq_power_MX_A),
	},

	{
		0x09, /* SPAIN/Austria/Brazil */
		channel_freq_power_SPN2_A,
		NELEMENTS(channel_freq_power_SPN2_A),
	},
	{
		0x0c, /* Brazil */
		channel_freq_power_BR1_A,
		NELEMENTS(channel_freq_power_BR1_A),
	},
	{
		0x0e, /* Brazil */
		channel_freq_power_BR2_A,
		NELEMENTS(channel_freq_power_BR2_A),
	},
	{
		0x0f, /* Russia */
		channel_freq_power_RU_A,
		NELEMENTS(channel_freq_power_RU_A),
	},
	{
		0x00, /* World */
		channel_freq_power_00_A,
		NELEMENTS(channel_freq_power_00_A),
	},
	{
		0x10, /* US FCC */
		channel_freq_power_A,
		NELEMENTS(channel_freq_power_A),
	},
	{
		0x20, /* CANADA IC */
		channel_freq_power_CAN_A,
		NELEMENTS(channel_freq_power_CAN_A),
	},
	{
		0x30, /* EU */
		channel_freq_power_EU_A,
		NELEMENTS(channel_freq_power_EU_A),
	},
	{
		0x40, /* JAPAN */
		channel_freq_power_JPN_A,
		NELEMENTS(channel_freq_power_JPN_A),
	},
	{
		0x41, /* JAPAN */
		channel_freq_power_JPN_A,
		NELEMENTS(channel_freq_power_JPN_A),
	},
	{
		0x50, /* China */
		channel_freq_power_CN_A,
		NELEMENTS(channel_freq_power_CN_A),
	},
	{
		0xfe, /* JAPAN */
		channel_freq_power_NULL_A,
		NELEMENTS(channel_freq_power_NULL_A),
	},
	{
		0xff, /* Special */
		channel_freq_power_JPN_A,
		NELEMENTS(channel_freq_power_JPN_A),
	},
	/* Add new region here */
};
/** Number of the CFP tables for 5GHz */
#define MLAN_CFP_TABLE_SIZE_A (NELEMENTS(cfp_table_A))

enum { RATEID_DBPSK1Mbps, //(0)
       RATEID_DQPSK2Mbps, //(1)
       RATEID_CCK5_5Mbps, //(2)
       RATEID_CCK11Mbps, //(3)
       RATEID_CCK22Mbps, //(4)
       RATEID_OFDM6Mbps, //(5)
       RATEID_OFDM9Mbps, //(6)
       RATEID_OFDM12Mbps, //(7)
       RATEID_OFDM18Mbps, //(8)
       RATEID_OFDM24Mbps, //(9)
       RATEID_OFDM36Mbps, //(10)
       RATEID_OFDM48Mbps, //(11)
       RATEID_OFDM54Mbps, //(12)
       RATEID_OFDM72Mbps, //(13)
};

static const t_u8 rateUnit_500Kbps[] = {
	(10 / 5), /* 1Mbps */
	(20 / 5), /* 2Mbps */

	(55 / 5), /* 5.5Mbps */
	(110 / 5), /* 11Mbps */
	(10 / 5), /* 22Mbps, intentionally set to 1Mbps
		   * because it's not available
		   */

	(60 / 5), /* 6Mbps */
	(90 / 5), /* 9Mbps */
	(120 / 5), /* 12Mbps */
	(180 / 5), /* 18Mbps */
	(240 / 5), /* 24Mbps */
	(360 / 5), /* 36Mbps */
	(480 / 5), /* 48Mbps */
	(540 / 5), /* 54Mbps */
	(60 / 5), /* 72Mbps, intentionally set to 6Mbps
		   * because it's not available
		   */
};

typedef struct _rate_map {
	/** Rate, in 0.5Mbps */
	t_u32 rate;
	/** Mrvl rate id, refer to RATEID_XXX in FW */
	t_u32 id;
	/** nss: 0-nss1, 1-nss2 */
	t_u8 nss;
} rate_map;

/** If user configure to 1x1 or we found peer device only support 1x1,
 * then we need skip the nss1 part when map to Mrvl rate.
 */
static const rate_map rate_map_table_2x2[] = {
	/* LG <--> Mrvl rate idx */
	{2, 0, 0}, // RATEID_DBPSK1Mbps
	{4, 1, 0}, // RATEID_DQPSK2Mbps
	{11, 2, 0}, // RATEID_CCK5_5Mbps
	{22, 3, 0}, // RATEID_CCK11Mbps
	{44, 4, 0}, // RATEID_CCK22Mbps
	{12, 5, 0}, // RATEID_OFDM6Mbps
	{18, 6, 0}, // RATEID_OFDM9Mbps
	{24, 7, 0}, // RATEID_OFDM12Mbps
	{36, 8, 0}, // RATEID_OFDM18Mbps
	{48, 9, 0}, // RATEID_OFDM24Mbps
	{72, 10, 0}, // RATEID_OFDM36Mbps
	{96, 11, 0}, // RATEID_OFDM48Mbps
	{108, 12, 0}, // RATEID_OFDM54Mbps
	{144, 13, 0}, // RATEID_OFDM72Mbps

	/* HT bw20 <--> Mrvl rate idx - nss2 */
	{26, 22, 1}, // RATEID_MCS8_13Mbps
	{52, 23, 1}, // RATEID_MCS9_26Mbps
	{78, 24, 1}, // RATEID_MCS10_39Mbps
	{104, 25, 1}, // RATEID_MCS11_52Mbps
	{156, 26, 1}, // RATEID_MCS12_78Mbps
	{208, 27, 1}, // RATEID_MCS13_104Mbps
	{234, 28, 1}, // RATEID_MCS14_117Mbps
	{260, 29, 1}, // RATEID_MCS15_130Mbps
	/* HT bw20 <--> Mrvl rate idx - nss1 */
	{13, 14, 0}, // RATEID_MCS0_6d5Mbps
	{26, 15, 0}, // RATEID_MCS1_13Mbps
	{39, 16, 0}, // RATEID_MCS2_19d5Mbps
	{52, 17, 0}, // RATEID_MCS3_26Mbps
	{78, 18, 0}, // RATEID_MCS4_39Mbps
	{104, 19, 0}, // RATEID_MCS5_52Mbps
	{117, 20, 0}, // RATEID_MCS6_58d5Mbps
	{130, 21, 0}, // RATEID_MCS7_65Mbps

	/* HT bw40<--> Mrvl rate idx - nss2 */
	{54, 39, 1}, // RATEID_MCS8BW40_27Mbps
	{108, 40, 1}, // RATEID_MCS9BW40_54Mbps
	{162, 41, 1}, // RATEID_MCS10BW40_81Mbps
	{216, 42, 1}, // RATEID_MCS11BW40_108Mbps
	{324, 43, 1}, // RATEID_MCS12BW40_162Mbps
	{432, 44, 1}, // RATEID_MCS13BW40_216Mbps
	{486, 45, 1}, // RATEID_MCS14BW40_243Mbps
	{540, 46, 1}, // RATEID_MCS15BW40_270Mbps
	/* HT bw40<--> Mrvl rate idx - nss1 */
	{12, 30, 0}, // RATEID_MCS32BW40_6Mbps
	{27, 31, 0}, // RATEID_MCS0BW40_13d5Mbps
	{54, 32, 0}, // RATEID_MCS1BW40_27Mbps
	{81, 33, 0}, // RATEID_MCS2BW40_40d5Mbps
	{108, 34, 0}, // RATEID_MCS3BW40_54Mbps
	{162, 35, 0}, // RATEID_MCS4BW40_81Mbps
	{216, 36, 0}, // RATEID_MCS5BW40_108Mbps
	{243, 37, 0}, // RATEID_MCS6BW40_121d5Mbps
	{270, 38, 0}, // RATEID_MCS7BW40_135Mbps

	/* VHT bw20<--> Mrvl rate idx - nss2 */
	{26, 57, 1}, // RATEID_VHT_MCS0_2SS_BW20   13    Mbps
	{52, 58, 1}, // RATEID_VHT_MCS1_2SS_BW20   26    Mbps
	{78, 59, 1}, // RATEID_VHT_MCS2_2SS_BW20   39    Mbps
	{104, 60, 1}, // RATEID_VHT_MCS3_2SS_BW20   52    Mbps
	{156, 61, 1}, // RATEID_VHT_MCS4_2SS_BW20   78    Mbps
	{208, 62, 1}, // RATEID_VHT_MCS5_2SS_BW20   104   Mbps
	{234, 63, 1}, // RATEID_VHT_MCS6_2SS_BW20   117   Mbps
	{260, 64, 1}, // RATEID_VHT_MCS7_2SS_BW20   130   Mbps
	{312, 65, 1}, // RATEID_VHT_MCS8_2SS_BW20   156   Mbps
	{0, 66, 1}, // RATEID_VHT_MCS9_2SS_BW20   173.3 Mbps(INVALID)
	/* VHT bw20<--> Mrvl rate idx - nss1 */
	{13, 47, 0}, // RATEID_VHT_MCS0_1SS_BW20   6.5  Mbps
	{26, 48, 0}, // RATEID_VHT_MCS1_1SS_BW20   13   Mbps
	{39, 49, 0}, // RATEID_VHT_MCS2_1SS_BW20   19.5 Mbps
	{52, 50, 0}, // RATEID_VHT_MCS3_1SS_BW20   26   Mbps
	{78, 51, 0}, // RATEID_VHT_MCS4_1SS_BW20   39   Mbps
	{104, 52, 0}, // RATEID_VHT_MCS5_1SS_BW20   52   Mbps
	{117, 53, 0}, // RATEID_VHT_MCS6_1SS_BW20   58.5 Mbps
	{130, 54, 0}, // RATEID_VHT_MCS7_1SS_BW20   65   Mbps
	{156, 55, 0}, // RATEID_VHT_MCS8_1SS_BW20   78   Mbps
	{0, 56, 0}, // RATEID_VHT_MCS9_1SS_BW20   86.7 Mbps(INVALID)

	/* VHT bw40<--> Mrvl rate idx - nss2 */
	{54, 77, 1}, // RATEID_VHT_MCS0_2SS_BW40   27  Mbps
	{108, 78, 1}, // RATEID_VHT_MCS1_2SS_BW40   54  Mbps
	{162, 79, 1}, // RATEID_VHT_MCS2_2SS_BW40   81  Mbps
	{216, 80, 1}, // RATEID_VHT_MCS3_2SS_BW40   108 Mbps
	{324, 81, 1}, // RATEID_VHT_MCS4_2SS_BW40   162 Mbps
	{432, 82, 1}, // RATEID_VHT_MCS5_2SS_BW40   216 Mbps
	{486, 83, 1}, // RATEID_VHT_MCS6_2SS_BW40   243 Mbps
	{540, 84, 1}, // RATEID_VHT_MCS7_2SS_BW40   270 Mbps
	{648, 85, 1}, // RATEID_VHT_MCS8_2SS_BW40   324 Mbps
	{720, 86, 1}, // RATEID_VHT_MCS9_2SS_BW40   360 Mbps
	/* VHT bw40<--> Mrvl rate idx - nss1 */
	{27, 67, 0}, // RATEID_VHT_MCS0_1SS_BW40   13.5  Mbps
	{54, 68, 0}, // RATEID_VHT_MCS1_1SS_BW40   27    Mbps
	{81, 69, 0}, // RATEID_VHT_MCS2_1SS_BW40   40.5  Mbps
	{108, 70, 0}, // RATEID_VHT_MCS3_1SS_BW40   54    Mbps
	{162, 71, 0}, // RATEID_VHT_MCS4_1SS_BW40   81    Mbps
	{216, 72, 0}, // RATEID_VHT_MCS5_1SS_BW40   108   Mbps
	{243, 73, 0}, // RATEID_VHT_MCS6_1SS_BW40   121.5 Mbps
	{270, 74, 0}, // RATEID_VHT_MCS7_1SS_BW40   135   Mbps
	{324, 75, 0}, // RATEID_VHT_MCS8_1SS_BW40   162   Mbps
	{360, 76, 0}, // RATEID_VHT_MCS9_1SS_BW40   180   Mbps

	/* VHT bw80<--> Mrvl rate idx - nss2 */
	{117, 97, 1}, // RATEID_VHT_MCS0_2SS_BW80   58.5  Mbps
	{234, 98, 1}, // RATEID_VHT_MCS1_2SS_BW80   117   Mbps
	{350, 99, 1}, // RATEID_VHT_MCS2_2SS_BW80   175   Mbps
	{468, 100, 1}, // RATEID_VHT_MCS3_2SS_BW80   234   Mbps
	{702, 101, 1}, // RATEID_VHT_MCS4_2SS_BW80   351   Mbps
	{936, 102, 1}, // RATEID_VHT_MCS5_2SS_BW80   468   Mbps
	{1053, 103, 1}, // RATEID_VHT_MCS6_2SS_BW80   526.5 Mbps
	{1170, 104, 1}, // RATEID_VHT_MCS7_2SS_BW80   585   Mbps
	{1404, 105, 1}, // RATEID_VHT_MCS8_2SS_BW80   702   Mbps
	{1560, 106, 1}, // RATEID_VHT_MCS9_2SS_BW80   780   Mbps
	/* VHT bw80<--> Mrvl rate idx - nss1 */
	{58, 87, 0}, // RATEID_VHT_MCS0_1SS_BW80   29.3  Mbps,  29.3x2 could
		     // correspond to 58
	{59, 87, 0}, // RATEID_VHT_MCS0_1SS_BW80   29.3  Mbps,  29.3*2 could
		     // correspond to 59 too
	{117, 88, 0}, // RATEID_VHT_MCS1_1SS_BW80   58.5  Mbps
	{175, 89, 0}, // RATEID_VHT_MCS2_1SS_BW80   87.8  Mbps,  87.8x2 could
		      // correspond to 175
	{176, 89, 0}, // RATEID_VHT_MCS2_1SS_BW80   87.8  Mbps,  87.8x2 could
		      // correspond to 176 too
	{234, 90, 0}, // RATEID_VHT_MCS3_1SS_BW80   117   Mbps
	{351, 91, 0}, // RATEID_VHT_MCS4_1SS_BW80   175.5 Mbps
	{468, 92, 0}, // RATEID_VHT_MCS5_1SS_BW80   234   Mbps
	{526, 93, 0}, // RATEID_VHT_MCS6_1SS_BW80   263.3 Mbps,  263.3x2 could
		      // correspond to 526
	{527, 93, 0}, // RATEID_VHT_MCS6_1SS_BW80   263.3 Mbps,  263.3x2 could
		      // correspond to 527 too
	{585, 94, 0}, // RATEID_VHT_MCS7_1SS_BW80   292.5 Mbps
	{702, 95, 0}, // RATEID_VHT_MCS8_1SS_BW80   351   Mbps
	{780, 96, 0}, // RATEID_VHT_MCS9_1SS_BW80   390   Mbps
};

/** rate_map_table_1x1 is based on rate_map_table_2x2 and remove nss2 part.
 * For the chip who only support 1x1, Mrvl rate idx define is different with 2x2
 * in FW We need redefine a bitrate to Mrvl rate idx table for 1x1 chip.
 */
static const rate_map rate_map_table_1x1[] = {
	/* LG <--> Mrvl rate idx */
	{2, 0, 0}, // RATEID_DBPSK1Mbps
	{4, 1, 0}, // RATEID_DQPSK2Mbps
	{11, 2, 0}, // RATEID_CCK5_5Mbps
	{22, 3, 0}, // RATEID_CCK11Mbps
	{44, 4, 0}, // RATEID_CCK22Mbps
	{12, 5, 0}, // RATEID_OFDM6Mbps
	{18, 6, 0}, // RATEID_OFDM9Mbps
	{24, 7, 0}, // RATEID_OFDM12Mbps
	{36, 8, 0}, // RATEID_OFDM18Mbps
	{48, 9, 0}, // RATEID_OFDM24Mbps
	{72, 10, 0}, // RATEID_OFDM36Mbps
	{96, 11, 0}, // RATEID_OFDM48Mbps
	{108, 12, 0}, // RATEID_OFDM54Mbps
	{144, 13, 0}, // RATEID_OFDM72Mbps

	/* HT bw20 <--> Mrvl rate idx */
	{13, 14, 0}, // RATEID_MCS0_6d5Mbps
	{26, 15, 0}, // RATEID_MCS1_13Mbps
	{39, 16, 0}, // RATEID_MCS2_19d5Mbps
	{52, 17, 0}, // RATEID_MCS3_26Mbps
	{78, 18, 0}, // RATEID_MCS4_39Mbps
	{104, 19, 0}, // RATEID_MCS5_52Mbps
	{117, 20, 0}, // RATEID_MCS6_58d5Mbps
	{130, 21, 0}, // RATEID_MCS7_65Mbps

	/* HT bw40<--> Mrvl rate idx */
	{12, 22, 0}, // RATEID_MCS32BW40_6Mbps,   for 1x1 start from 22
	{27, 23, 0}, // RATEID_MCS0BW40_13d5Mbps
	{54, 24, 0}, // RATEID_MCS1BW40_27Mbps
	{81, 25, 0}, // RATEID_MCS2BW40_40d5Mbps
	{108, 26, 0}, // RATEID_MCS3BW40_54Mbps
	{162, 27, 0}, // RATEID_MCS4BW40_81Mbps
	{216, 28, 0}, // RATEID_MCS5BW40_108Mbps
	{243, 29, 0}, // RATEID_MCS6BW40_121d5Mbps
	{270, 30, 0}, // RATEID_MCS7BW40_135Mbps

	/* VHT bw20<--> Mrvl rate idx */
	{13, 31, 0}, // RATEID_VHT_MCS0_1SS_BW20   6.5  Mbps
	{26, 32, 0}, // RATEID_VHT_MCS1_1SS_BW20   13   Mbps
	{39, 33, 0}, // RATEID_VHT_MCS2_1SS_BW20   19.5 Mbps
	{52, 34, 0}, // RATEID_VHT_MCS3_1SS_BW20   26   Mbps
	{78, 35, 0}, // RATEID_VHT_MCS4_1SS_BW20   39   Mbps
	{104, 36, 0}, // RATEID_VHT_MCS5_1SS_BW20   52   Mbps
	{117, 37, 0}, // RATEID_VHT_MCS6_1SS_BW20   58.5 Mbps
	{130, 38, 0}, // RATEID_VHT_MCS7_1SS_BW20   65   Mbps
	{156, 39, 0}, // RATEID_VHT_MCS8_1SS_BW20   78   Mbps
	{0, 40, 0}, // RATEID_VHT_MCS9_1SS_BW20   86.7 Mbps(INVALID)

	/* VHT bw40<--> Mrvl rate idx */
	{27, 41, 0}, // RATEID_VHT_MCS0_1SS_BW40   13.5  Mbps
	{54, 42, 0}, // RATEID_VHT_MCS1_1SS_BW40   27    Mbps
	{81, 43, 0}, // RATEID_VHT_MCS2_1SS_BW40   40.5  Mbps
	{108, 44, 0}, // RATEID_VHT_MCS3_1SS_BW40   54    Mbps
	{162, 45, 0}, // RATEID_VHT_MCS4_1SS_BW40   81    Mbps
	{216, 46, 0}, // RATEID_VHT_MCS5_1SS_BW40   108   Mbps
	{243, 47, 0}, // RATEID_VHT_MCS6_1SS_BW40   121.5 Mbps
	{270, 48, 0}, // RATEID_VHT_MCS7_1SS_BW40   135   Mbps
	{324, 49, 0}, // RATEID_VHT_MCS8_1SS_BW40   162   Mbps
	{360, 50, 0}, // RATEID_VHT_MCS9_1SS_BW40   180   Mbps

	/* VHT bw80<--> Mrvl rate idx */
	{58, 51, 0}, // RATEID_VHT_MCS0_1SS_BW80   29.3  Mbps,  29.3x2 could
		     // correspond to 58
	{59, 51, 0}, // RATEID_VHT_MCS0_1SS_BW80   29.3  Mbps,  29.3x2 could
		     // correspond to 59 too
	{117, 52, 0}, // RATEID_VHT_MCS1_1SS_BW80   58.5  Mbps
	{175, 53, 0}, // RATEID_VHT_MCS2_1SS_BW80   87.8  Mbps,  87.8x2 could
		      // correspond to 175
	{176, 53, 0}, // RATEID_VHT_MCS2_1SS_BW80   87.8  Mbps,  87.8x2 could
		      // correspond to 176 too
	{234, 54, 0}, // RATEID_VHT_MCS3_1SS_BW80   117   Mbps
	{351, 55, 0}, // RATEID_VHT_MCS4_1SS_BW80   175.5 Mbps
	{468, 56, 0}, // RATEID_VHT_MCS5_1SS_BW80   234   Mbps
	{526, 57, 0}, // RATEID_VHT_MCS6_1SS_BW80   263.3 Mbps,  263.3x2 could
		      // correspond to 526
	{527, 57, 0}, // RATEID_VHT_MCS6_1SS_BW80   263.3 Mbps,  263.3x2 could
		      // correspond to 527 too
	{585, 58, 0}, // RATEID_VHT_MCS7_1SS_BW80   292.5 Mbps
	{702, 59, 0}, // RATEID_VHT_MCS8_1SS_BW80   351   Mbps
	{780, 60, 0}, // RATEID_VHT_MCS9_1SS_BW80   390   Mbps
};

/********************************************************
 *			Global Variables
 ********************************************************/
/**
 * The table to keep region code
 */

t_u16 region_code_index[MRVDRV_MAX_REGION_CODE] = {0x00, 0x10, 0x20, 0x30, 0x40,
						   0x41, 0x50, 0xfe, 0xff};

/** The table to keep CFP code for A */
t_u16 cfp_code_index_a[MRVDRV_MAX_CFP_CODE_A] = {0x1, 0x2, 0x3, 0x4, 0x5};

/**
 * The rates supported for ad-hoc B mode
 */
t_u8 AdhocRates_B[B_SUPPORTED_RATES] = {0x82, 0x84, 0x8b, 0x96, 0};

/**
 * The rates supported for ad-hoc G mode
 */
t_u8 AdhocRates_G[G_SUPPORTED_RATES] = {0x8c, 0x12, 0x98, 0x24, 0xb0,
					0x48, 0x60, 0x6c, 0x00};

/**
 * The rates supported for ad-hoc BG mode
 */
t_u8 AdhocRates_BG[BG_SUPPORTED_RATES] = {0x82, 0x84, 0x8b, 0x96, 0x0c,
					  0x12, 0x18, 0x24, 0x30, 0x48,
					  0x60, 0x6c, 0x00};

/**
 * The rates supported in A mode for ad-hoc
 */
t_u8 AdhocRates_A[A_SUPPORTED_RATES] = {0x8c, 0x12, 0x98, 0x24, 0xb0,
					0x48, 0x60, 0x6c, 0x00};

/**
 * The rates supported in A mode (used for BAND_A)
 */
t_u8 SupportedRates_A[A_SUPPORTED_RATES] = {0x0c, 0x12, 0x18, 0x24, 0xb0,
					    0x48, 0x60, 0x6c, 0x00};

/**
 * The rates supported by the card
 */
static t_u16 WlanDataRates[WLAN_SUPPORTED_RATES_EXT] = {
	0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, 0x18, 0x24,  0x30, 0x48,
	0x60, 0x6C, 0x90, 0x0D, 0x1A, 0x27, 0x34, 0x4E, 0x68,  0x75, 0x82,
	0x0C, 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x00};

/**
 * The rates supported in B mode
 */
t_u8 SupportedRates_B[B_SUPPORTED_RATES] = {0x02, 0x04, 0x0b, 0x16, 0x00};

/**
 * The rates supported in G mode (BAND_G, BAND_G|BAND_GN)
 */
t_u8 SupportedRates_G[G_SUPPORTED_RATES] = {0x0c, 0x12, 0x18, 0x24, 0x30,
					    0x48, 0x60, 0x6c, 0x00};

/**
 * The rates supported in BG mode (BAND_B|BAND_G, BAND_B|BAND_G|BAND_GN)
 */
t_u8 SupportedRates_BG[BG_SUPPORTED_RATES] = {0x02, 0x04, 0x0b, 0x0c, 0x12,
					      0x16, 0x18, 0x24, 0x30, 0x48,
					      0x60, 0x6c, 0x00};

/**
 * The rates supported in N mode
 */
t_u8 SupportedRates_N[N_SUPPORTED_RATES] = {0x02, 0x04, 0};

#define MCS_NUM_AX 12
// for MCS0/MCS1/MCS3/MCS4 have 4 additional DCM=1 value
// note: the value in the table is 2 multiplier of the actual rate
static t_u16 ax_mcs_rate_nss1[12][MCS_NUM_AX + 4] = {
	{0x90, 0x48, 0x120, 0x90, 0x1B0, 0x240, 0x120, 0x360, 0x1B0, 0x481,
	 0x511, 0x5A1, 0x6C1, 0x781, 0x871, 0x962}, /*SG 160M*/
	{0x88, 0x44, 0x110, 0x88, 0x198, 0x220, 0x110, 0x330, 0x198, 0x440,
	 0x4C9, 0x551, 0x661, 0x716, 0x7F9, 0x8DC}, /*MG 160M*/
	{0x7A, 0x3D, 0xF5, 0x7A, 0x16F, 0x1EA, 0xF5, 0x2DF, 0x16F, 0x3D4, 0x44E,
	 0x4C9, 0x5BE, 0x661, 0x72D, 0x7F9}, /*LG 160M*/
	{0x48, 0x24, 0x90, 0x48, 0xD8, 0x120, 0x90, 0x1B0, 0xD8, 0x240, 0x288,
	 0x2D0, 0x360, 0x3C0, 0x438, 0x4B0}, /*SG 80M*/
	{0x44, 0x22, 0x88, 0x44, 0xCC, 0x110, 0x88, 0x198, 0xCC, 0x220, 0x264,
	 0x2A8, 0x330, 0x38B, 0x3FC, 0x46E}, /*MG 80M*/
	{0x3D, 0x1E, 0x7A, 0x3D, 0xB7, 0xF5, 0x7A, 0x16F, 0xB7, 0x1EA, 0x227,
	 0x264, 0x2DF, 0x330, 0x396, 0x3FC}, /*LG 80M*/
	{0x22, 0x11, 0x44, 0x22, 0x67, 0x89, 0x44, 0xCE, 0x67, 0x113, 0x135,
	 0x158, 0x19D, 0x1CA, 0x204, 0x23D}, /*SG 40M*/
	{0x20, 0x10, 0x41, 0x20, 0x61, 0x82, 0x41, 0xC3, 0x61, 0x104, 0x124,
	 0x145, 0x186, 0x1B1, 0x1E7, 0x21D}, /*MG 40M*/
	{0x1D, 0xE, 0x3A, 0x1D, 0x57, 0x75, 0x3A, 0xAF, 0x57, 0xEA, 0x107,
	 0x124, 0x15F, 0x186, 0x1B6, 0x1E7}, /*LG 40M*/
	{0x11, 0x8, 0x22, 0x11, 0x33, 0x44, 0x22, 0x67, 0x33, 0x89, 0x9A, 0xAC,
	 0xCE, 0xE5, 0x102, 0x11E}, /*SG 20M*/
	{0x10, 0x8, 0x20, 0x10, 0x30, 0x41, 0x20, 0x61, 0x30, 0x82, 0x92, 0xA2,
	 0xC3, 0xD8, 0xF3, 0x10E}, /*MG 20M*/
	{0xE, 0x7, 0x1D, 0xE, 0x2B, 0x3A, 0x1D, 0x57, 0x2B, 0x75, 0x83, 0x92,
	 0xAF, 0xC3, 0xDB, 0xF3} /*LG 20M*/
};

#if 0
// note: the value in the table is 2 multiplier of the actual rate
t_u16 ax_tone_ru_rate_nss1[9][MCS_NUM_AX + 4] = {
	{0x8, 0x4, 0xF, 0x8, 0x17, 0x1E, 0xF, 0x2D, 0x17, 0x3C, 0x44, 0x4B,
	 0x5A, 0x64, 0x71, 0x7D}, /*SG 106-tone*/
	{0x7, 0x4, 0xF, 0x7, 0x16, 0x1D, 0xF, 0x2B, 0x16, 0x39, 0x40, 0x47,
	 0x55, 0x5F, 0x6B, 0x76}, /*MG 106-tone*/
	{0x7, 0x3, 0xD, 0x6, 0x14, 0x1A, 0xD, 0x27, 0x14, 0x33, 0x3A, 0x40,
	 0x4D, 0x55, 0x60, 0x6B}, /*LG 106-tone*/
	{0x4, 0x2, 0x7, 0x4, 0xB, 0xF, 0x7, 0x16, 0xB, 0x1D, 0x20, 0x22, 0x2B,
	 0x2F, 0x35, 0x3B}, /*SG 52-tone*/
	{0x4, 0x2, 0x7, 0x4, 0xA, 0xE, 0x7, 0x14, 0xA, 0x1B, 0x1E, 0x22, 0x28,
	 0x2D, 0x32, 0x38}, /*MG 52-tone*/
	{0x3, 0x2, 0x6, 0x3, 0x9, 0xC, 0x6, 0x12, 0x9, 0x18, 0x1B, 0x1E, 0x24,
	 0x28, 0x2D, 0x32}, /*LG 52-tone*/
	{0x2, 0x1, 0x4, 0x2, 0x6, 0x7, 0x4, 0xB, 0x5, 0xE, 0x10, 0x12, 0x15,
	 0x18, 0x1A, 0x1D}, /*SG 26-tone*/
	{0x2, 0x1, 0x4, 0x2, 0x5, 0x6, 0x4, 0xA, 0x5, 0xD, 0xF, 0x11, 0x14,
	 0x16, 0x19, 0x1C}, /*MG 26-tone*/
	{0x2, 0x1, 0x3, 0x2, 0x5, 0x6, 0x3, 0x9, 0x4, 0xC, 0xE, 0xF, 0x12, 0x14,
	 0x17, 0x19} /*LG 26-tone*/
};
#endif

// note: the value in the table is 2 multiplier of the actual rate
static t_u16 ax_mcs_rate_nss2[12][MCS_NUM_AX + 4] = {
	{0x120, 0x90, 0x240, 0x120, 0x360, 0x481, 0x240, 0x61C, 0x360, 0x901,
	 0xA22, 0xB42, 0xD82, 0xF03, 0x10E3, 0x12C3}, /*SG 160M*/
	{0x110, 0x88, 0x220, 0x110, 0x330, 0x440, 0x220, 0x661, 0x330, 0x881,
	 0x992, 0xAA2, 0xCAC, 0xE2D, 0xFF3, 0x11B9}, /*MG 160M*/
	{0xF5, 0x7A, 0x1EA, 0xF5, 0x2DF, 0x3D4, 0x1EA, 0x5BE, 0x2DF, 0x7A8,
	 0x1134, 0x992, 0xB7C, 0xCC2, 0xE5B, 0xFF3}, /*LG 160M*/
	{0x90, 0x48, 0x120, 0x90, 0x1B0, 0x240, 0x120, 0x360, 0x1B0, 0x481,
	 0x511, 0x5A1, 0x6C1, 0x781, 0x871, 0x962}, /*SG 80M*/
	{0x88, 0x44, 0x110, 0x88, 0x198, 0x220, 0x110, 0x330, 0x198, 0x440,
	 0x4C9, 0x551, 0x661, 0x716, 0x7F9, 0x8DC}, /*MG 80M*/
	{0x7A, 0x3D, 0xF5, 0x7A, 0x16F, 0x1EA, 0xF5, 0x2DF, 0x16F, 0x3D4, 0x44E,
	 0x4C9, 0x5BE, 0x661, 0x72D, 0x7F9}, /*LG 80M*/
	{0x44, 0x22, 0x89, 0x44, 0xCE, 0x113, 0x89, 0x19D, 0xCE, 0x226, 0x26B,
	 0x2B0, 0x339, 0x395, 0x408, 0x47B}, /*SG 40M*/
	{0x41, 0x20, 0x82, 0x41, 0xC3, 0x104, 0x82, 0x186, 0xC3, 0x208, 0x249,
	 0x28A, 0x30C, 0x362, 0x3CE, 0x43B}, /*MG 40M*/
	{0x3A, 0x1D, 0x75, 0x3A, 0xAF, 0xEA, 0x75, 0x15F, 0xAF, 0x1D4, 0x20E,
	 0x249, 0x2BE, 0x30C, 0x36D, 0x3CF}, /*LG 40M*/
	{0x22, 0x11, 0x44, 0x22, 0x67, 0x89, 0x44, 0xCE, 0x67, 0x113, 0x135,
	 0x158, 0x19D, 0x1CA, 0x204, 0x23D}, /*SG 20M*/
	{0x20, 0x10, 0x41, 0x20, 0x61, 0x82, 0x41, 0xC3, 0x61, 0x104, 0x124,
	 0x145, 0x186, 0x1B1, 0x1E7, 0x21D}, /*MG 20M*/
	{0x1D, 0xE, 0x3A, 0x1D, 0x57, 0x75, 0x3A, 0xAF, 0x57, 0xEA, 0x107,
	 0x124, 0x15F, 0x186, 0x1B6, 0x1E7} /*LG 20M*/
};

#if 0
// note: the value in the table is 2 multiplier of the actual rate
t_u16 ax_tone_ru_rate_nss2[9][MCS_NUM_AX + 4] = {
	{0xF, 0x8, 0x1E, 0xF, 0x2D, 0x3C, 0x1E, 0x5A, 0x2D, 0x78, 0x87, 0x96,
	 0xB4, 0xC8, 0xE1, 0xFA}, /*SG 106-tone*/
	{0xE, 0x7, 0x1D, 0xE, 0x2B, 0x39, 0x1D, 0x55, 0x2B, 0x72, 0x80, 0x8E,
	 0xAA, 0xBD, 0xD5, 0xED}, /*MG 106-tone*/
	{0xD, 0x7, 0x1A, 0xD, 0x27, 0x33, 0x1A, 0x4D, 0x27, 0x66, 0x73, 0x80,
	 0x99, 0xAA, 0xC0, 0xD5}, /*LG 106-tone*/
	{0x7, 0x4, 0xF, 0x7, 0x16, 0x1D, 0xF, 0x2A, 0x16, 0x39, 0x40, 0x47,
	 0x55, 0x5F, 0x6A, 0x76}, /*SG 52-tone*/
	{0x7, 0x4, 0xE, 0x7, 0x14, 0x1B, 0xE, 0x28, 0x14, 0x36, 0x3C, 0x43,
	 0x50, 0x59, 0x64, 0x70}, /*MG 52-tone*/
	{0x6, 0x3, 0xC, 0x6, 0x12, 0x18, 0xC, 0x24, 0x12, 0x30, 0x36, 0x3C,
	 0x48, 0x50, 0x5A, 0x64}, /*LG 52-tone*/
	{0x4, 0x2, 0x7, 0x4, 0xB, 0xF, 0x7, 0x16, 0xB, 0x1D, 0x20, 0x22, 0x2B,
	 0x2F, 0x35, 0x3B}, /*SG 26-tone*/
	{0x4, 0x2, 0x7, 0x4, 0xA, 0xE, 0x7, 0x14, 0xA, 0x1B, 0x1E, 0x22, 0x28,
	 0x2D, 0x32, 0x38}, /*MG 26-tone*/
	{0x3, 0x2, 0x6, 0x3, 0x9, 0xC, 0x6, 0x12, 0x9, 0x18, 0x1B, 0x1E, 0x24,
	 0x28, 0x2D, 0x32} /*LG 26-tone*/
};
#endif

/********************************************************
 *			Local Functions
 ********************************************************/
/**
 *  @brief Find a character in a string.
 *
 *  @param pmadapter    A pointer to mlan_adapter structure
 *  @param s            A pointer to string
 *  @param c            Character to be located
 *  @param n            The length of string
 *
 *  @return        A pointer to the first occurrence of c in string, or MNULL if
 * c is not found.
 */
static void *wlan_memchr(pmlan_adapter pmadapter, void *s, int c, int n)
{
	const t_u8 *p = (t_u8 *)s;

	ENTER();

	while (n--) {
		if ((t_u8)c == *p++) {
			LEAVE();
			return (void *)(p - 1);
		}
	}

	LEAVE();
	return MNULL;
}

/**
 *  @brief This function finds the CFP in
 *          cfp_table_BG/A based on region/code and band parameter.
 *
 *  @param pmadapter  A pointer to mlan_adapter structure
 *  @param region     The region code
 *  @param band       The band
 *  @param cfp_no     A pointer to CFP number
 *
 *  @return           A pointer to CFP
 */
static chan_freq_power_t *wlan_get_region_cfp_table(pmlan_adapter pmadapter,
						    t_u8 region, t_u16 band,
						    int *cfp_no)
{
	t_u32 i;
	t_u8 cfp_bg, cfp_a;

	ENTER();

	cfp_bg = cfp_a = region;
	if (!region) {
		/* Invalid region code, use CFP code */
		cfp_bg = pmadapter->cfp_code_bg;
		cfp_a = pmadapter->cfp_code_a;
	}

	if (band & (BAND_B | BAND_G | BAND_GN | BAND_GAC)) {
		/* Return the FW cfp table for requested region code, if
		 * available. If region is not forced and the requested region
		 * code is different, simply return the corresponding
		 * pre-defined table.
		 */
		if (pmadapter->otp_region && pmadapter->cfp_otp_bg) {
			if (pmadapter->otp_region->force_reg ||
			    (cfp_bg ==
			     (t_u8)pmadapter->otp_region->region_code)) {
				*cfp_no = pmadapter->tx_power_table_bg_rows;
				LEAVE();
				return pmadapter->cfp_otp_bg;
			}
		}
		for (i = 0; i < MLAN_CFP_TABLE_SIZE_BG; i++) {
			PRINTM(MINFO, "cfp_table_BG[%d].code=%d\n", i,
			       cfp_table_BG[i].code);
			/* Check if region/code matches for BG bands */
			if (cfp_table_BG[i].code == cfp_bg) {
				/* Select by band */
				*cfp_no = cfp_table_BG[i].cfp_no;
				LEAVE();
				return cfp_table_BG[i].cfp;
			}
		}
	}
	if (band & (BAND_A | BAND_AN | BAND_AAC)) {
		/* Return the FW cfp table for requested region code */
		if (pmadapter->otp_region && pmadapter->cfp_otp_a) {
			if (pmadapter->otp_region->force_reg ||
			    (cfp_a ==
			     (t_u8)pmadapter->otp_region->region_code)) {
				*cfp_no = pmadapter->tx_power_table_a_rows;
				LEAVE();
				return pmadapter->cfp_otp_a;
			}
		}
		for (i = 0; i < MLAN_CFP_TABLE_SIZE_A; i++) {
			PRINTM(MINFO, "cfp_table_A[%d].code=%d\n", i,
			       cfp_table_A[i].code);
			/* Check if region/code matches for A bands */
			if (cfp_table_A[i].code == cfp_a) {
				/* Select by band */
				*cfp_no = cfp_table_A[i].cfp_no;
				LEAVE();
				return cfp_table_A[i].cfp;
			}
		}
	}
	if (!region)
		PRINTM(MERROR, "Error Band[0x%x] or code[BG:%#x, A:%#x]\n",
		       band, cfp_bg, cfp_a);
	else
		PRINTM(MERROR, "Error Band[0x%x] or region[%#x]\n", band,
		       region);

	LEAVE();
	return MNULL;
}

/**
 *  @brief This function copies dynamic CFP elements from one table to another.
 *         Only copy elements where channel numbers match.
 *
 *  @param pmadapter   A pointer to mlan_adapter structure
 *  @param cfp         Destination table
 *  @param num_cfp     Number of elements in dest table
 *  @param cfp_src     Source table
 *  @param num_cfp_src Number of elements in source table
 */
static t_void wlan_cfp_copy_dynamic(pmlan_adapter pmadapter,
				    chan_freq_power_t *cfp, t_u8 num_cfp,
				    chan_freq_power_t *cfp_src,
				    t_u8 num_cfp_src)
{
	int i, j;

	ENTER();

	if (cfp == cfp_src) {
		LEAVE();
		return;
	}

	/* first clear dest dynamic blacklisted entries */
	/* do not clear the flags */
	for (i = 0; i < num_cfp; i++) {
		cfp[i].dynamic.blacklist = MFALSE;
		cfp[i].dynamic.dfs_state = DFS_USABLE;
	}

	/* copy dynamic blacklisted entries from source where channels match */
	if (cfp_src) {
		for (i = 0; i < num_cfp; i++)
			for (j = 0; j < num_cfp_src; j++)
				if (cfp[i].channel == cfp_src[j].channel) {
					cfp[i].dynamic.blacklist =
						cfp_src[j].dynamic.blacklist;
					cfp[i].dynamic.dfs_state =
						cfp_src[j].dynamic.dfs_state;
					break;
				}
	}

	LEAVE();
}

/********************************************************
 *			Global Functions
 ********************************************************/
/**
 *  @brief This function converts region string to integer code
 *
 *  @param pmadapter        A pointer to mlan_adapter structure
 *  @param country_code     Country string
 *  @param cfp_bg           Pointer to buffer
 *  @param cfp_a            Pointer to buffer
 *
 *  @return                 MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
 */
mlan_status wlan_misc_country_2_cfp_table_code(pmlan_adapter pmadapter,
					       t_u8 *country_code, t_u8 *cfp_bg,
					       t_u8 *cfp_a)
{
	t_u8 i;

	ENTER();

	if (pmadapter->otp_region) {
		if (!memcmp(pmadapter, pmadapter->otp_region->country_code,
			    country_code, COUNTRY_CODE_LEN - 1)) {
			if (pmadapter->cfp_otp_bg)
				*cfp_bg = pmadapter->otp_region->region_code;
			if (pmadapter->cfp_otp_a)
				*cfp_a = pmadapter->otp_region->region_code;
			LEAVE();
			return MLAN_STATUS_SUCCESS;
		}
	}
	/* Look for code in mapping table */
	for (i = 0; i < NELEMENTS(country_code_mapping); i++) {
		if (!memcmp(pmadapter, country_code_mapping[i].country_code,
			    country_code, COUNTRY_CODE_LEN - 1)) {
			*cfp_bg = country_code_mapping[i].cfp_code_bg;
			*cfp_a = country_code_mapping[i].cfp_code_a;
			LEAVE();
			return MLAN_STATUS_SUCCESS;
		}
	}

	/* If still not found, look for code in EU country code table */
	for (i = 0; i < NELEMENTS(eu_country_code_table); i++) {
		if (!memcmp(pmadapter, eu_country_code_table[i], country_code,
			    COUNTRY_CODE_LEN - 1)) {
			*cfp_bg = EU_CFP_CODE_BG;
			*cfp_a = EU_CFP_CODE_A;
			LEAVE();
			return MLAN_STATUS_SUCCESS;
		}
	}

	LEAVE();
	return MLAN_STATUS_FAILURE;
}

/**
 *  @brief This function finds if given country code is in EU table
 *
 *  @param pmadapter        A pointer to mlan_adapter structure
 *  @param country_code     Country string
 *
 *  @return                 MTRUE or MFALSE
 */
t_bool wlan_is_etsi_country(pmlan_adapter pmadapter, t_u8 *country_code)
{
	t_u8 i;

	ENTER();

	/* Look for code in EU country code table */
	for (i = 0; i < NELEMENTS(eu_country_code_table); i++) {
		if (!memcmp(pmadapter, eu_country_code_table[i], country_code,
			    COUNTRY_CODE_LEN - 1)) {
			LEAVE();
			return MTRUE;
		}
	}

	LEAVE();
	return MFALSE;
}

/**
 *   @brief This function adjust the antenna index
 *
 *   V16_FW_API: Bit0: ant A, Bit 1:ant B, Bit0 & Bit 1: A+B
 *   8887: case1: 0 - 2.4G ant A,  1- 2.4G antB, 2-- 5G ant C
 *   case2: 0 - 2.4G ant A,  1- 2.4G antB, 0x80- 5G antA, 0x81-5G ant B
 *   @param priv	A pointer to mlan_private structure
 *   @param prx_pd	A pointer to the RxPD structure
 *
 *   @return        MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
 */
t_u8 wlan_adjust_antenna(pmlan_private priv, RxPD *prx_pd)
{
	t_u8 antenna = prx_pd->antenna;
#if defined(SD8887) || defined(SD8987)
	t_u32 rx_channel = (prx_pd->rx_info & RXPD_CHAN_MASK) >> 5;
#endif
	if (prx_pd->antenna == 0xff)
		return 0;
	if (priv->adapter->pcard_info->v16_fw_api) {
		if ((antenna & MBIT(0)) && (antenna & MBIT(1)))
			antenna = 2;
		else if (antenna & MBIT(1))
			antenna = 1;
		else if (antenna & MBIT(0))
			antenna = 0;
	}

#if defined(SD8887) || defined(SD8987)
#define ANTENNA_OFFSET 2
	if (MFALSE
#ifdef SD8887
	    || IS_SD8887(priv->adapter->card_type)
#endif
#ifdef SD8987
	    || IS_SD8987(priv->adapter->card_type)
#endif
	) {
		if ((priv->adapter->antinfo & ANT_DIVERSITY_2G) &&
		    (priv->adapter->antinfo & ANT_DIVERSITY_5G)) {
#define MAX_2G_CHAN 14
			if (rx_channel > MAX_2G_CHAN)
				antenna += ANTENNA_OFFSET;
		}
	}
#endif

	return antenna;
}

/**
 *  @brief This function adjust the rate index
 *
 *  @param priv    A pointer to mlan_private structure
 *  @param rx_rate rx rate
 *  @param rate_info rate info
 *  @return        rate index
 */
t_u16 wlan_adjust_data_rate(mlan_private *priv, t_u8 rx_rate, t_u8 rate_info)
{
	t_u16 rate_index = 0;
	t_u8 bw = 0;
	t_u8 nss = 0;
	t_bool sgi_enable = 0;
	t_u8 gi = 0;
#define MAX_MCS_NUM_AX 12

#define MAX_MCS_NUM_SUPP 16
#define MAX_MCS_NUM_AC 10
#define RATE_INDEX_MCS0 12
	bw = (rate_info & 0xC) >> 2;
	sgi_enable = (rate_info & 0x10) >> 4;
	if ((rate_info & 0x3) == 0) {
		rate_index = (rx_rate > MLAN_RATE_INDEX_OFDM0) ? rx_rate - 1 :
								 rx_rate;
	} else if ((rate_info & 0x03) == 1) {
		rate_index = RATE_INDEX_MCS0 +
			     MAX_MCS_NUM_SUPP * 2 * sgi_enable +
			     MAX_MCS_NUM_SUPP * bw + rx_rate;
	} else if ((rate_info & 0x3) == 2) {
		if (IS_STREAM_2X2(priv->adapter->feature_control))
			nss = rx_rate >> 4; // 0:NSS1, 1:NSS2
		rate_index = RATE_INDEX_MCS0 + MAX_MCS_NUM_SUPP * 4 +
			     MAX_MCS_NUM_AC * 6 * sgi_enable +
			     MAX_MCS_NUM_AC * 2 * bw + MAX_MCS_NUM_AC * nss +
			     (rx_rate & 0x0f);
	} else if ((rate_info & 0x3) == 3) {
		gi = (rate_info & 0x10) >> 4 | (rate_info & 0x80) >> 6;
		if (IS_STREAM_2X2(priv->adapter->feature_control))
			nss = rx_rate >> 4; // 0:NSS1, 1:NSS2
		rate_index = RATE_INDEX_MCS0 + MAX_MCS_NUM_SUPP * 4 +
			     MAX_MCS_NUM_AC * 12 + MAX_MCS_NUM_AX * 6 * gi +
			     MAX_MCS_NUM_AX * 2 * bw + MAX_MCS_NUM_AX * nss +
			     (rx_rate & 0x0f);
	}
	return rate_index;
}

#ifdef STA_SUPPORT
#endif /* STA_SUPPORT */

/**
 *  @brief convert TX rate_info from v14 to v15+ FW rate_info
 *
 *  @param v14_rate_info      v14 rate info
 *
 *  @return             v15+ rate info
 */
t_u8 wlan_convert_v14_tx_rate_info(pmlan_private pmpriv, t_u8 v14_rate_info)
{
	t_u8 rate_info = 0;

	if (!pmpriv->adapter->pcard_info->v14_fw_api) {
		PRINTM(MERROR, "%s: Not convert for this is not V14 FW\n",
		       __func__);
		return v14_rate_info;
	}

	rate_info = v14_rate_info & 0x01;
	/* band */
	rate_info |= (v14_rate_info & MBIT(1)) << 1;
	/* short GI */
	rate_info |= (v14_rate_info & MBIT(2)) << 2;
	return rate_info;
}

/**
 *  @brief convert RX rate_info from v14 to v15+ FW rate_info
 *
 *  @param v14_rate_info      v14 rate info
 *
 *  @return             v15+ rate info
 */
t_u8 wlan_convert_v14_rx_rate_info(pmlan_private pmpriv, t_u8 v14_rate_info)
{
	t_u8 rate_info = 0;
	t_u8 mode = 0;
	t_u8 bw = 0;
	t_u8 sgi = 0;

	if (!pmpriv->adapter->pcard_info->v14_fw_api) {
		PRINTM(MERROR, "%s: Not convert for this is not V14 FW\n",
		       __func__);
		return v14_rate_info;
	}

	mode = v14_rate_info & MBIT(0);
	bw = v14_rate_info & MBIT(1);
	sgi = (v14_rate_info & 0x04) >> 2;

	rate_info = (mode & 0x01) | ((bw & 0x01) << 2) | ((sgi & 0x01) << 4);

	return rate_info;
}

/**
 *  @brief Use index to get the data rate
 *
 *  @param pmadapter        A pointer to mlan_adapter structure
 *  @param index            The index of data rate
 *  @param tx_rate_info     Tx rate info
 *  @param ext_rate_info    Extend tx rate info
 *
 *  @return                 Data rate or 0
 */
t_u32 wlan_index_to_data_rate(pmlan_adapter pmadapter, t_u8 index,
			      t_u8 tx_rate_info, t_u8 ext_rate_info)
{
#define MCS_NUM_SUPP 16
	t_u16 mcs_rate[4][MCS_NUM_SUPP] = {
		{0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, 0x36, 0x6c,
		 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c}, /*LG 40M*/
		{0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, 0x3c, 0x78,
		 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258}, /*SG 40M */
		{0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, 0x1a, 0x34,
		 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104}, /*LG 20M */
		{0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, 0x1c, 0x39,
		 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120}}; /*SG 20M */

#define MCS_NUM_AC 10
	/* NSS 1. note: the value in the table is 2 multiplier of the actual
	 * rate in other words, it is in the unit of 500 Kbs
	 */
	t_u16 ac_mcs_rate_nss1[8][MCS_NUM_AC] = {
		{0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, 0x492, 0x57C,
		 0x618}, /* LG 160M*/
		{0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, 0x514, 0x618,
		 0x6C6}, /* SG 160M*/
		{0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F, 0x249, 0x2BE,
		 0x30C}, /* LG 80M */
		{0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249, 0x28A, 0x30C,
		 0x363}, /* SG 80M */
		{0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x144,
		 0x168}, /* LG 40M */
		{0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E, 0x12C, 0x168,
		 0x190}, /* SG 40M */
		{0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C,
		 0x00}, /* LG 20M */
		{0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE,
		 0x00}, /* SG 20M */
	};
	/* NSS 2. note: the value in the table is 2 multiplier of the actual
	 * rate
	 */
	t_u16 ac_mcs_rate_nss2[8][MCS_NUM_AC] = {
		{0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A, 0x924, 0xAF8,
		 0xC30}, /*LG 160M*/
		{0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924, 0xA28, 0xC30,
		 0xD8B}, /*SG 160M*/

		{0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, 0x492, 0x57C,
		 0x618}, /*LG 80M*/
		{0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, 0x514, 0x618,
		 0x6C6}, /*SG 80M*/
		{0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6, 0x21C, 0x288,
		 0x2D0}, /*LG 40M*/
		{0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C, 0x258, 0x2D0,
		 0x320}, /*SG 40M*/
		{0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104, 0x138,
		 0x00}, /*LG 20M*/
		{0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121, 0x15B,
		 0x00}, /*SG 20M*/
	};

	t_u32 rate = 0;
	t_u8 mcs_index = 0;
	t_u8 he_dcm = 0;
	//	t_u8 he_tone = 0;
	t_u8 stbc = 0;

	t_u8 bw = 0;
	t_u8 gi = 0;

	ENTER();

	PRINTM(MINFO, "%s:index=%d, tx_rate_info=%d, ext_rate_info=%d\n",
	       __func__, index, tx_rate_info, ext_rate_info);

	if ((tx_rate_info & 0x3) == MLAN_RATE_FORMAT_VHT) {
		/* VHT rate */
		mcs_index = index & 0xF;

		if (mcs_index > 9)
			mcs_index = 9;

		/* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */
		bw = (tx_rate_info & 0xC) >> 2;
		/* LGI: gi =0, SGI: gi = 1 */
		gi = (tx_rate_info & 0x10) >> 4;
		if ((index >> 4) == 1) {
			/* NSS = 2 */
			rate = ac_mcs_rate_nss2[2 * (3 - bw) + gi][mcs_index];
		} else
			/* NSS = 1 */
			rate = ac_mcs_rate_nss1[2 * (3 - bw) + gi][mcs_index];
	} else

		if ((tx_rate_info & 0x3) == MLAN_RATE_FORMAT_HE) {
		/* VHT rate */
		mcs_index = index & 0xF;
		he_dcm = ext_rate_info & MBIT(0);

		if (mcs_index > MCS_NUM_AX - 1)
			mcs_index = MCS_NUM_AX - 1;

		/* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */
		bw = (tx_rate_info & (MBIT(3) | MBIT(2))) >> 2;
		/* BIT7:BIT4 0:0= 0.8us,0:1= 0.8us, 1:0=1.6us, 1:1=3.2us or
		 * 0.8us
		 */
		gi = (tx_rate_info & MBIT(4)) >> 4 |
		     (tx_rate_info & MBIT(7)) >> 6;
		/* STBC: BIT5 in tx rate info */
		stbc = (tx_rate_info & MBIT(5)) >> 5;

		if (gi > 3) {
			PRINTM(MERROR, "Invalid gi value");
			return 0;
		}

		if ((gi == 3) && stbc && he_dcm) {
			gi = 0;
			stbc = 0;
			he_dcm = 0;
		}
		/* map to gi 0:0.8us,1:1.6us 2:3.2us*/
		if (gi > 0)
			gi = gi - 1;

		//#ifdef ENABLE_802_11AX
		// TODO: hardcode he_tone here, wait for FW value ready.
		//		he_tone = 4;

		// he_tone = (ext_rate_info & 0xE) >> 1;
		//#endif

		if ((index >> 4) == 1) {
			switch (mcs_index) {
			case 0:
			case 1:
				// #if 0
				// if (he_tone < 3) {
				//	rate =
				//ax_tone_ru_rate_nss2[3*(2-he_tone)+gi][mcs_index*2
				//+ he_dcm];
				// } else {
				// #endif
				rate = ax_mcs_rate_nss2[3 * (3 - bw) + gi]
						       [mcs_index * 2 + he_dcm];
				break;
			case 2:
				// #if 0
				// if (he_tone < 3) {
				//	rate =
				//ax_tone_ru_rate_nss2[3*(2-he_tone)+gi][mcs_index*2];
				// } else {
				// #endif
				rate = ax_mcs_rate_nss2[3 * (3 - bw) + gi]
						       [mcs_index * 2];
				break;
			case 3:
			case 4:
				// #if 0
				// if (he_tone < 3) {
				//	rate =
				//ax_tone_ru_rate_nss2[3*(2-he_tone)+gi][mcs_index*2
				//- 1 + he_dcm];
				// } else {
				// #endif
				rate = ax_mcs_rate_nss2[3 * (3 - bw) + gi]
						       [mcs_index * 2 - 1 +
							he_dcm];
				break;

			default:
				// #if 0
				// if (he_tone < 3) {
				//	rate =
				//ax_tone_ru_rate_nss2[3*(2-he_tone)+gi][mcs_index
				//+ 4];
				// } else {
				// #endif
				rate = ax_mcs_rate_nss2[3 * (3 - bw) + gi]
						       [mcs_index + 4];
				break;
			}
		} else {
			switch (mcs_index) {
			case 0:
			case 1:
				// #if 0
				// if (he_tone < 3) {
				//	rate =
				//ax_tone_ru_rate_nss1[3*(2-he_tone)+gi][mcs_index*2
				//+ he_dcm];
				// } else {
				// #endif
				rate = ax_mcs_rate_nss1[3 * (3 - bw) + gi]
						       [mcs_index * 2 + he_dcm];
				break;
			case 2:
				// #if 0
				// if (he_tone < 3) {
				//	rate =
				//ax_tone_ru_rate_nss1[3*(2-he_tone)+gi][mcs_index*2];
				// } else {
				// #endif
				rate = ax_mcs_rate_nss1[3 * (3 - bw) + gi]
						       [mcs_index * 2];
				break;
			case 3:
			case 4:
				// #if 0
				// if (he_tone < 3) {
				//	rate =
				//ax_tone_ru_rate_nss1[3*(2-he_tone)+gi][mcs_index*2
				//- 1 + he_dcm];
				// } else {
				// #endif
				rate = ax_mcs_rate_nss1[3 * (3 - bw) + gi]
						       [mcs_index * 2 - 1 +
							he_dcm];
				break;

			default:
				// #if 0
				// if (he_tone < 3) {
				//	rate =
				//ax_tone_ru_rate_nss1[3*(2-he_tone)+gi][mcs_index
				//+ 4];
				// } else {
				// #endif
				rate = ax_mcs_rate_nss1[3 * (3 - bw) + gi]
						       [mcs_index + 4];
				break;
			}
		}
	} else if ((tx_rate_info & 0x3) == MLAN_RATE_FORMAT_HT) {
		/* HT rate */
		/* 20M: bw=0, 40M: bw=1 */
		bw = (tx_rate_info & 0xC) >> 2;
		/* LGI: gi =0, SGI: gi = 1 */
		gi = (tx_rate_info & 0x10) >> 4;
		if (index == MLAN_RATE_BITMAP_MCS0) {
			if (gi == 1)
				rate = 0x0D; /* MCS 32 SGI rate */
			else
				rate = 0x0C; /* MCS 32 LGI rate */
		} else if (index < MCS_NUM_SUPP) {
			if (bw <= 1)
				rate = mcs_rate[2 * (1 - bw) + gi][index];
			else
				rate = WlanDataRates[0];
		} else
			rate = WlanDataRates[0];
	} else {
		/* 11n non HT rates */
		if (index >= WLAN_SUPPORTED_RATES_EXT)
			index = 0;
		rate = WlanDataRates[index];
	}
	LEAVE();
	return rate;
}

/**
 *  @brief Use rate to get the index
 *
 *  @param pmadapter    A pointer to mlan_adapter structure
 *  @param rate         Data rate
 *
 *  @return                     Index or 0
 */
t_u8 wlan_data_rate_to_index(pmlan_adapter pmadapter, t_u32 rate)
{
	t_u16 *ptr;

	ENTER();
	if (rate) {
		ptr = wlan_memchr(pmadapter, WlanDataRates, (t_u8)rate,
				  sizeof(WlanDataRates));
		if (ptr) {
			LEAVE();
			return (t_u8)(ptr - WlanDataRates);
		}
	}
	LEAVE();
	return 0;
}

/**
 *  @brief Get active data rates
 *
 *  @param pmpriv           A pointer to mlan_private structure
 *  @param bss_mode         The specified BSS mode (Infra/IBSS)
 *  @param config_bands     The specified band configuration
 *  @param rates            The buf to return the active rates
 *
 *  @return                 The number of Rates
 */
t_u32 wlan_get_active_data_rates(mlan_private *pmpriv, t_u32 bss_mode,
				 t_u16 config_bands, WLAN_802_11_RATES rates)
{
	t_u32 k;

	ENTER();

	if (pmpriv->media_connected != MTRUE) {
		k = wlan_get_supported_rates(pmpriv, bss_mode, config_bands,
					     rates);
	} else {
		k = wlan_copy_rates(rates, 0,
				    pmpriv->curr_bss_params.data_rates,
				    pmpriv->curr_bss_params.num_of_rates);
	}

	LEAVE();
	return k;
}

#ifdef STA_SUPPORT
/**
 *  @brief This function search through all the regions cfp table to find the
 * channel, if the channel is found then gets the MIN txpower of the channel
 *            present in all the regions.
 *
 *  @param pmpriv       A pointer to mlan_private structure
 *  @param band      	band.
 *  @param channel      Channel number.
 *
 *  @return             The Tx power
 */
t_u8 wlan_get_txpwr_of_chan_from_cfp(mlan_private *pmpriv, t_u16 band,
				     t_u8 channel)
{
	t_u8 i = 0;
	t_u8 j = 0;
	t_u8 tx_power = 0;
	t_u32 cfp_no;
	chan_freq_power_t *cfp = MNULL;
	chan_freq_power_t *cfp_a = MNULL;
	t_u32 cfp_no_a;

	ENTER();

	if (band & (BAND_B | BAND_G)) {
		for (i = 0; i < MLAN_CFP_TABLE_SIZE_BG; i++) {
			/* Get CFP */
			cfp = cfp_table_BG[i].cfp;
			cfp_no = cfp_table_BG[i].cfp_no;
			/* Find matching channel and get Tx power */
			for (j = 0; j < cfp_no; j++) {
				if ((cfp + j)->channel == channel) {
					if (tx_power != 0)
						tx_power = MIN(
							tx_power,
							(cfp + j)->max_tx_power);
					else
						tx_power =
							(t_u8)(cfp + j)
								->max_tx_power;
					break;
				}
			}
		}
	}

	if (band & BAND_A) {
		for (i = 0; i < MLAN_CFP_TABLE_SIZE_A; i++) {
			/* Get CFP */
			cfp_a = cfp_table_A[i].cfp;
			cfp_no_a = cfp_table_A[i].cfp_no;
			for (j = 0; j < cfp_no_a; j++) {
				if ((cfp_a + j)->channel == channel) {
					if (tx_power != 0)
						tx_power = MIN(
							tx_power,
							(cfp_a + j)
								->max_tx_power);
					else
						tx_power = (t_u8)(
							(cfp_a + j)
								->max_tx_power);
					break;
				}
			}
		}
	}

	LEAVE();
	return tx_power;
}

/**
 *  @brief Get the channel frequency power info for a specific channel
 *
 *  @param pmadapter            A pointer to mlan_adapter structure
 *  @param band                 It can be BAND_A, BAND_G or BAND_B
 *  @param channel              The channel to search for
 *  @param region_channel       A pointer to region_chan_t structure
 *
 *  @return                     A pointer to chan_freq_power_t structure or
 * MNULL if not found.
 */

chan_freq_power_t *
wlan_get_cfp_by_band_and_channel(pmlan_adapter pmadapter, t_u16 band,
				 t_u16 channel, region_chan_t *region_channel)
{
	region_chan_t *rc;
	chan_freq_power_t *cfp = MNULL;
	int i, j;

	ENTER();

	for (j = 0; !cfp && (j < MAX_REGION_CHANNEL_NUM); j++) {
		rc = &region_channel[j];

		if (!rc->valid || !rc->pcfp)
			continue;
		switch (rc->band) {
		case BAND_A:
			switch (band) {
			case BAND_AN:
			case BAND_A | BAND_AN:
			case BAND_A | BAND_AN | BAND_AAC:
			/* Fall Through */
			case BAND_A: /* Matching BAND_A */
				break;

			default:
				continue;
			}
			break;
		case BAND_B:
		case BAND_G:
			switch (band) {
			case BAND_GN:
			case BAND_B | BAND_G | BAND_GN:
			case BAND_G | BAND_GN:
			case BAND_GN | BAND_GAC:
			case BAND_B | BAND_G | BAND_GN | BAND_GAC:
			case BAND_G | BAND_GN | BAND_GAC:
			case BAND_B | BAND_G:
			/* Fall Through */
			case BAND_B: /* Matching BAND_B/G */
			/* Fall Through */
			case BAND_G:
			/* Fall Through */
			case 0:
				break;
			default:
				continue;
			}
			break;
		default:
			continue;
		}
		if (channel == FIRST_VALID_CHANNEL)
			cfp = &rc->pcfp[0];
		else {
			for (i = 0; i < rc->num_cfp; i++) {
				if (rc->pcfp[i].channel == channel) {
					cfp = &rc->pcfp[i];
					break;
				}
			}
		}
	}

	if (!cfp && channel)
		PRINTM(MCMND, "%s: can not find cfp by band %d & channel %d\n",
		       __func__, band, channel);

	LEAVE();
	return cfp;
}

/**
 *  @brief Find the channel frequency power info for a specific channel
 *
 *  @param pmadapter    A pointer to mlan_adapter structure
 *  @param band         It can be BAND_A, BAND_G or BAND_B
 *  @param channel      The channel to search for
 *
 *  @return             A pointer to chan_freq_power_t structure or MNULL if not
 * found.
 */
chan_freq_power_t *wlan_find_cfp_by_band_and_channel(mlan_adapter *pmadapter,
						     t_u16 band, t_u16 channel)
{
	chan_freq_power_t *cfp = MNULL;

	ENTER();

	/* Any station(s) with 11D enabled */
	if (wlan_count_priv_cond(pmadapter, wlan_11d_is_enabled,
				 wlan_is_station) > 0)
		cfp = wlan_get_cfp_by_band_and_channel(
			pmadapter, band, channel, pmadapter->universal_channel);
	else
		cfp = wlan_get_cfp_by_band_and_channel(
			pmadapter, band, channel, pmadapter->region_channel);

	LEAVE();
	return cfp;
}

/**
 *  @brief Find the channel frequency power info for a specific frequency
 *
 *  @param pmadapter    A pointer to mlan_adapter structure
 *  @param band         It can be BAND_A, BAND_G or BAND_B
 *  @param freq         The frequency to search for
 *
 *  @return         Pointer to chan_freq_power_t structure; MNULL if not found
 */
chan_freq_power_t *wlan_find_cfp_by_band_and_freq(mlan_adapter *pmadapter,
						  t_u16 band, t_u32 freq)
{
	chan_freq_power_t *cfp = MNULL;
	region_chan_t *rc;
	int i, j;

	ENTER();

	for (j = 0; !cfp && (j < MAX_REGION_CHANNEL_NUM); j++) {
		rc = &pmadapter->region_channel[j];

		/* Any station(s) with 11D enabled */
		if (wlan_count_priv_cond(pmadapter, wlan_11d_is_enabled,
					 wlan_is_station) > 0)
			rc = &pmadapter->universal_channel[j];

		if (!rc->valid || !rc->pcfp)
			continue;
		switch (rc->band) {
		case BAND_A:
			switch (band) {
			case BAND_AN:
			case BAND_A | BAND_AN:
			case BAND_A | BAND_AN | BAND_AAC:
			/* Fall Through */
			case BAND_A: /* Matching BAND_A */
				break;
			default:
				continue;
			}
			break;
		case BAND_B:
		case BAND_G:
			switch (band) {
			case BAND_GN:
			case BAND_B | BAND_G | BAND_GN:
			case BAND_G | BAND_GN:
			case BAND_GN | BAND_GAC:
			case BAND_B | BAND_G | BAND_GN | BAND_GAC:
			case BAND_G | BAND_GN | BAND_GAC:
			case BAND_B | BAND_G:
			/* Fall Through */
			case BAND_B:
			/* Fall Through */
			case BAND_G:
			/* Fall Through */
			case 0:
				break;
			default:
				continue;
			}
			break;
		default:
			continue;
		}
		for (i = 0; i < rc->num_cfp; i++) {
			if (rc->pcfp[i].freq == freq) {
				cfp = &rc->pcfp[i];
				break;
			}
		}
	}

	if (!cfp && freq)
		PRINTM(MERROR, "%s: cannot find cfp by band %d & freq %d\n",
		       __func__, band, freq);

	LEAVE();
	return cfp;
}
#endif /* STA_SUPPORT */

/**
 *  @brief Check if Rate Auto
 *
 *  @param pmpriv               A pointer to mlan_private structure
 *
 *  @return                     MTRUE or MFALSE
 */
t_u8 wlan_is_rate_auto(mlan_private *pmpriv)
{
	t_u32 i;
	int rate_num = 0;

	ENTER();

	for (i = 0; i < NELEMENTS(pmpriv->bitmap_rates); i++)
		if (pmpriv->bitmap_rates[i])
			rate_num++;

	LEAVE();
	if (rate_num > 1)
		return MTRUE;
	else
		return MFALSE;
}

/**
 *  @brief Covert Rate Bitmap to Rate index
 *
 *  @param pmadapter    Pointer to mlan_adapter structure
 *  @param rate_bitmap  Pointer to rate bitmap
 *  @param size         Size of the bitmap array
 *
 *  @return             Rate index
 */
int wlan_get_rate_index(pmlan_adapter pmadapter, t_u16 *rate_bitmap, int size)
{
	int i;

	ENTER();

	for (i = 0; i < size * 8; i++) {
		if (rate_bitmap[i / 16] & (1 << (i % 16))) {
			LEAVE();
			return i;
		}
	}

	LEAVE();
	return -1;
}

/**
 *  @brief Convert config_bands to B/G/A band
 *
 *  @param config_bands     The specified band configuration
 *
 *  @return                 BAND_B|BAND_G|BAND_A
 */
t_u16 wlan_convert_config_bands(t_u16 config_bands)
{
	t_u16 bands = 0;
	if (config_bands & BAND_B)
		bands |= BAND_B;
	if (config_bands & BAND_G || config_bands & BAND_GN ||
	    config_bands & BAND_GAC || config_bands & BAND_GAX)
		bands |= BAND_G;
	if (config_bands & BAND_A || config_bands & BAND_AN ||
	    config_bands & BAND_AAC || config_bands & BAND_AAX)
		bands |= BAND_A;
	return bands;
}

/**
 *  @brief Get supported data rates
 *
 *  @param pmpriv           A pointer to mlan_private structure
 *  @param bss_mode         The specified BSS mode (Infra/IBSS)
 *  @param config_bands     The specified band configuration
 *  @param rates            The buf to return the supported rates
 *
 *  @return                 The number of Rates
 */
t_u32 wlan_get_supported_rates(mlan_private *pmpriv, t_u32 bss_mode,
			       t_u16 config_bands, WLAN_802_11_RATES rates)
{
	t_u32 k = 0;
	t_u16 bands = 0;

	ENTER();
	bands = wlan_convert_config_bands(config_bands);

	if (bss_mode == MLAN_BSS_MODE_INFRA) {
		/* Infra. mode */
		if (bands == BAND_B) {
			/* B only */
			PRINTM(MINFO, "Band: Infra B\n");
			k = wlan_copy_rates(rates, k, SupportedRates_B,
					    sizeof(SupportedRates_B));
		} else if (bands == BAND_G) {
			/* G only */
			PRINTM(MINFO, "Band: Infra G\n");
			k = wlan_copy_rates(rates, k, SupportedRates_G,
					    sizeof(SupportedRates_G));

		} else if (bands & (BAND_B | BAND_G)) {
			/* BG only */
			PRINTM(MINFO, "Band: Infra BG\n");
#ifdef WIFI_DIRECT_SUPPORT
			if (pmpriv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT)
				k = wlan_copy_rates(rates, k, SupportedRates_G,
						    sizeof(SupportedRates_G));
			else
#endif
				k = wlan_copy_rates(rates, k, SupportedRates_BG,
						    sizeof(SupportedRates_BG));
		} else if (bands & BAND_A) {
			/* support A */
			PRINTM(MINFO, "Band: Infra A\n");
			k = wlan_copy_rates(rates, k, SupportedRates_A,
					    sizeof(SupportedRates_A));
		}
	} else {
		/* Adhoc. mode */
		if (bands == BAND_B) {
			/* B only */
			PRINTM(MINFO, "Band: Adhoc B\n");
			k = wlan_copy_rates(rates, k, AdhocRates_B,
					    sizeof(AdhocRates_B));
		} else if (bands == BAND_G) {
			/* G only */
			PRINTM(MINFO, "Band: Adhoc G\n");
			k = wlan_copy_rates(rates, k, AdhocRates_G,
					    sizeof(AdhocRates_G));

		} else if (bands & BAND_A) {
			/* support A */
			PRINTM(MINFO, "Band: Adhoc A\n");
			k = wlan_copy_rates(rates, k, AdhocRates_A,
					    sizeof(AdhocRates_A));

		} else {
			PRINTM(MINFO, "Band: Adhoc BG\n");
			k = wlan_copy_rates(rates, k, AdhocRates_BG,
					    sizeof(AdhocRates_BG));
		}
	}

	LEAVE();
	return k;
}

#define COUNTRY_ID_US 0
#define COUNTRY_ID_JP 1
#define COUNTRY_ID_CN 2
#define COUNTRY_ID_EU 3
typedef struct _oper_bw_chan {
	/*non-global operating class*/
	t_u8 oper_class;
	/*global operating class*/
	t_u8 global_oper_class;
	/*bandwidth 0-20M 1-40M 2-80M 3-160M*/
	t_u8 bandwidth;
	/*channel list*/
	t_u8 channel_list[13];
} oper_bw_chan;

/** oper class table for US*/
static oper_bw_chan oper_bw_chan_us[] = {
	/** non-Global oper class, global oper class, bandwidth, channel list*/
	{1, 115, 0, {36, 40, 44, 48}},
	{2, 118, 0, {52, 56, 60, 64}},
	{3, 124, 0, {149, 153, 157, 161}},
	{4,
	 121,
	 0,
	 {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144}},
	{5, 125, 0, {149, 153, 157, 161, 165}},
	{12, 81, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}},
	{22, 116, 1, {36, 44}},
	{23, 119, 1, {52, 60}},
	{24, 122, 1, {100, 108, 116, 124, 132, 140}},
	{25, 126, 1, {149, 157}},
	{26, 126, 1, {149, 157}},
	{27, 117, 1, {40, 48}},
	{28, 120, 1, {56, 64}},
	{29, 123, 1, {104, 112, 120, 128, 136, 144}},
	{30, 127, 1, {153, 161}},
	{31, 127, 1, {153, 161}},
	{32, 83, 1, {1, 2, 3, 4, 5, 6, 7}},
	{33, 84, 1, {5, 6, 7, 8, 9, 10, 11}},
	{128, 128, 2, {42, 58, 106, 122, 138, 155}},
	{129, 129, 3, {50, 114}},
	{130, 130, 2, {42, 58, 106, 122, 138, 155}},
};
/** oper class table for EU*/
static oper_bw_chan oper_bw_chan_eu[] = {
	/** non-global oper class,global oper class, bandwidth, channel list*/
	{1, 115, 0, {36, 40, 44, 48}},
	{2, 118, 0, {52, 56, 60, 64}},
	{3, 121, 0, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}},
	{4, 81, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}},
	{5, 116, 1, {36, 44}},
	{6, 119, 1, {52, 60}},
	{7, 122, 1, {100, 108, 116, 124, 132}},
	{8, 117, 1, {40, 48}},
	{9, 120, 1, {56, 64}},
	{10, 123, 1, {104, 112, 120, 128, 136}},
	{11, 83, 1, {1, 2, 3, 4, 5, 6, 7, 8, 9}},
	{12, 84, 1, {5, 6, 7, 8, 9, 10, 11, 12, 13}},
	{17, 125, 0, {149, 153, 157, 161, 165, 169}},
	{128, 128, 2, {42, 58, 106, 122, 138, 155}},
	{129, 129, 3, {50, 114}},
	{130, 130, 2, {42, 58, 106, 122, 138, 155}},
};
/** oper class table for Japan*/
static oper_bw_chan oper_bw_chan_jp[] = {
	/** non-Global oper class,global oper class, bandwidth, channel list*/
	{1, 115, 0, {34, 38, 42, 46, 36, 40, 44, 48}},
	{30, 81, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}},
	{31, 82, 0, {14}},
	{32, 118, 0, {52, 56, 60, 64}},
	{33, 118, 0, {52, 56, 60, 64}},
	{34, 121, 0, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}},
	{35, 121, 0, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}},
	{36, 116, 1, {36, 44}},
	{37, 119, 1, {52, 60}},
	{38, 119, 1, {52, 60}},
	{39, 122, 1, {100, 108, 116, 124, 132}},
	{40, 122, 1, {100, 108, 116, 124, 132}},
	{41, 117, 1, {40, 48}},
	{42, 120, 1, {56, 64}},
	{43, 120, 1, {56, 64}},
	{44, 123, 1, {104, 112, 120, 128, 136}},
	{45, 123, 1, {104, 112, 120, 128, 136}},
	{56, 83, 1, {1, 2, 3, 4, 5, 6, 7, 8, 9}},
	{57, 84, 1, {5, 6, 7, 8, 9, 10, 11, 12, 13}},
	{58, 121, 0, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}},
	{128, 128, 2, {42, 58, 106, 122, 138, 155}},
	{129, 129, 3, {50, 114}},
	{130, 130, 2, {42, 58, 106, 122, 138, 155}},
};
/** oper class table for China*/
static oper_bw_chan oper_bw_chan_cn[] = {
	/** non-Global oper class,global oper class, bandwidth, channel list*/
	{1, 115, 0, {36, 40, 44, 48}},
	{2, 118, 0, {52, 56, 60, 64}},
	{3, 125, 0, {149, 153, 157, 161, 165}},
	{4, 116, 1, {36, 44}},
	{5, 119, 1, {52, 60}},
	{6, 126, 1, {149, 157}},
	{7, 81, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}},
	{8, 83, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9}},
	{9, 84, 1, {5, 6, 7, 8, 9, 10, 11, 12, 13}},
	{128, 128, 2, {42, 58, 106, 122, 138, 155}},
	{129, 129, 3, {50, 114}},
	{130, 130, 2, {42, 58, 106, 122, 138, 155}},
};

/**
 *  @brief Get non-global operaing class table according to country
 *
 *  @param pmpriv             A pointer to mlan_private structure
 *  @param arraysize          A pointer to table size
 *
 *  @return                   A pointer to oper_bw_chan
 */
static oper_bw_chan *wlan_get_nonglobal_operclass_table(mlan_private *pmpriv,
							int *arraysize)
{
	t_u8 country_code[][COUNTRY_CODE_LEN] = {"US", "JP", "CN"};
	int country_id = 0;
	oper_bw_chan *poper_bw_chan = MNULL;

	ENTER();

	for (country_id = 0; country_id < 3; country_id++)
		if (!memcmp(pmpriv->adapter, pmpriv->adapter->country_code,
			    country_code[country_id], COUNTRY_CODE_LEN - 1))
			break;
	if (country_id >= 3)
		country_id = COUNTRY_ID_US; /*Set default to US*/
	if (wlan_is_etsi_country(pmpriv->adapter,
				 pmpriv->adapter->country_code))
		country_id = COUNTRY_ID_EU; /** Country in EU */

	switch (country_id) {
	case COUNTRY_ID_US:
		poper_bw_chan = oper_bw_chan_us;
		*arraysize = sizeof(oper_bw_chan_us);
		break;
	case COUNTRY_ID_JP:
		poper_bw_chan = oper_bw_chan_jp;
		*arraysize = sizeof(oper_bw_chan_jp);
		break;
	case COUNTRY_ID_CN:
		poper_bw_chan = oper_bw_chan_cn;
		*arraysize = sizeof(oper_bw_chan_cn);
		break;
	case COUNTRY_ID_EU:
		poper_bw_chan = oper_bw_chan_eu;
		*arraysize = sizeof(oper_bw_chan_eu);
		break;
	}

	LEAVE();
	return poper_bw_chan;
}

/**
 *  @brief Check validation of given channel and oper class
 *
 *  @param pmpriv             A pointer to mlan_private structure
 *  @param channel            Channel number
 *  @param oper_class         operating class
 *  @param bandwidth          band width
 *
 *  @return                   MLAN_STATUS_PENDING --success, otherwise fail
 */
mlan_status wlan_check_operclass_validation(mlan_private *pmpriv, t_u8 channel,
					    t_u8 oper_class, t_u8 bandwidth)
{
	int arraysize = 0, i = 0, channum = 0;
	oper_bw_chan *poper_bw_chan = MNULL;
	t_u8 center_freq_idx = 0;
	t_u8 center_freqs[] = {42, 50, 58, 106, 114, 122, 138, 155};

	ENTER();

	for (i = 0; i < (int)sizeof(center_freqs); i++) {
		if (channel == center_freqs[i]) {
			PRINTM(MERROR, "Invalid channel number %d!\n", channel);
			LEAVE();
			return MLAN_STATUS_FAILURE;
		}
	}
	if (oper_class <= 0 || oper_class > 130) {
		PRINTM(MERROR, "Invalid operating class!\n");
		LEAVE();
		return MLAN_STATUS_FAILURE;
	}
	if (oper_class >= 128) {
		center_freq_idx = wlan_get_center_freq_idx(
			pmpriv, BAND_5GHZ, channel, CHANNEL_BW_80MHZ);
		channel = center_freq_idx;
	}
	poper_bw_chan = wlan_get_nonglobal_operclass_table(pmpriv, &arraysize);

	if (!poper_bw_chan) {
		PRINTM(MCMND, "Operating class table do not find!\n");
		LEAVE();
		return MLAN_STATUS_FAILURE;
	}

	for (i = 0; i < (int)(arraysize / sizeof(oper_bw_chan)); i++) {
		if (poper_bw_chan[i].oper_class == oper_class ||
		    poper_bw_chan[i].global_oper_class == oper_class) {
			for (channum = 0;
			     channum <
			     (int)sizeof(poper_bw_chan[i].channel_list);
			     channum++) {
				if (poper_bw_chan[i].channel_list[channum] &&
				    poper_bw_chan[i].channel_list[channum] ==
					    channel) {
					LEAVE();
					return MLAN_STATUS_SUCCESS;
				}
			}
		}
	}

	PRINTM(MCMND, "Operating class %d do not match channel %d!\n",
	       oper_class, channel);
	LEAVE();
	return MLAN_STATUS_FAILURE;
}

/**
 *  @brief Get current operating class from channel and bandwidth
 *
 *  @param pmpriv             A pointer to mlan_private structure
 *  @param channel            Channel number
 *  @param bw                 Bandwidth
 *  @param oper_class         A pointer to current operating class
 *
 *  @return                   MLAN_STATUS_PENDING --success, otherwise fail
 */
mlan_status wlan_get_curr_oper_class(mlan_private *pmpriv, t_u8 channel,
				     t_u8 bw, t_u8 *oper_class)
{
	oper_bw_chan *poper_bw_chan = MNULL;
	t_u8 center_freq_idx = 0;
	t_u8 center_freqs[] = {42, 50, 58, 106, 114, 122, 138, 155};
	int i = 0, arraysize = 0, channum = 0;

	ENTER();

	poper_bw_chan = wlan_get_nonglobal_operclass_table(pmpriv, &arraysize);

	if (!poper_bw_chan) {
		PRINTM(MCMND, "Operating class table do not find!\n");
		LEAVE();
		return MLAN_STATUS_FAILURE;
	}
	for (i = 0; i < (int)sizeof(center_freqs); i++) {
		if (channel == center_freqs[i]) {
			PRINTM(MERROR, "Invalid channel number %d!\n", channel);
			LEAVE();
			return MLAN_STATUS_FAILURE;
		}
	}
	if (bw == BW_80MHZ) {
		center_freq_idx = wlan_get_center_freq_idx(
			pmpriv, BAND_5GHZ, channel, CHANNEL_BW_80MHZ);
		channel = center_freq_idx;
	}

	for (i = 0; i < (int)(arraysize / sizeof(oper_bw_chan)); i++) {
		if (poper_bw_chan[i].bandwidth == bw) {
			for (channum = 0;
			     channum <
			     (int)(sizeof(poper_bw_chan[i].channel_list));
			     channum++) {
				if (poper_bw_chan[i].channel_list[channum] &&
				    poper_bw_chan[i].channel_list[channum] ==
					    channel) {
					*oper_class =
						poper_bw_chan[i].oper_class;
					return MLAN_STATUS_SUCCESS;
				}
			}
		}
	}

	PRINTM(MCMND, "Operating class not found!\n");
	LEAVE();
	return MLAN_STATUS_FAILURE;
}

/**
 *  @brief Add Supported operating classes IE
 *
 *  @param pmpriv             A pointer to mlan_private structure
 *  @param pptlv_out          A pointer to TLV to fill in
 *  @param curr_oper_class    Current operating class
 *
 *  @return                   Length
 */
int wlan_add_supported_oper_class_ie(mlan_private *pmpriv, t_u8 **pptlv_out,
				     t_u8 curr_oper_class)
{
	t_u8 oper_class_us[] = {115, 118, 124, 121, 125, 81,  116,
				119, 122, 126, 126, 117, 120, 123,
				127, 127, 83,  84,  128, 129, 130};
	t_u8 oper_class_eu[] = {115, 118, 121, 81, 116, 119, 122, 117,
				120, 123, 83,  84, 125, 128, 129, 130};
	t_u8 oper_class_jp[] = {115, 81,  82,  118, 118, 121, 121, 116,
				119, 119, 122, 122, 117, 120, 120, 123,
				123, 83,  84,  121, 128, 129, 130};
	t_u8 oper_class_cn[] = {115, 118, 125, 116, 119, 126,
				81,  83,  84,  128, 129, 130};
	t_u8 country_code[][COUNTRY_CODE_LEN] = {"US", "JP", "CN"};
	int country_id = 0, ret = 0;
	MrvlIETypes_SuppOperClass_t *poper_class = MNULL;

	ENTER();

	for (country_id = 0; country_id < 3; country_id++)
		if (!memcmp(pmpriv->adapter, pmpriv->adapter->country_code,
			    country_code[country_id], COUNTRY_CODE_LEN - 1))
			break;
	if (country_id >= 3)
		country_id = COUNTRY_ID_US; /*Set default to US*/
	if (wlan_is_etsi_country(pmpriv->adapter,
				 pmpriv->adapter->country_code))
		country_id = COUNTRY_ID_EU; /** Country in EU */
	poper_class = (MrvlIETypes_SuppOperClass_t *)*pptlv_out;
	memset(pmpriv->adapter, poper_class, 0,
	       sizeof(MrvlIETypes_SuppOperClass_t));
	poper_class->header.type = wlan_cpu_to_le16(REGULATORY_CLASS);
	if (country_id == COUNTRY_ID_US) {
		poper_class->header.len = sizeof(oper_class_us);
		memcpy_ext(pmpriv->adapter, &poper_class->oper_class,
			   oper_class_us, sizeof(oper_class_us),
			   poper_class->header.len);
	} else if (country_id == COUNTRY_ID_JP) {
		poper_class->header.len = sizeof(oper_class_jp);
		memcpy_ext(pmpriv->adapter, &poper_class->oper_class,
			   oper_class_jp, sizeof(oper_class_jp),
			   poper_class->header.len);
	} else if (country_id == COUNTRY_ID_CN) {
		poper_class->header.len = sizeof(oper_class_cn);
		memcpy_ext(pmpriv->adapter, &poper_class->oper_class,
			   oper_class_cn, sizeof(oper_class_cn),
			   poper_class->header.len);
	} else if (country_id == COUNTRY_ID_EU) {
		poper_class->header.len = sizeof(oper_class_eu);
		memcpy_ext(pmpriv->adapter, &poper_class->oper_class,
			   oper_class_eu, sizeof(oper_class_eu),
			   poper_class->header.len);
	}
	poper_class->current_oper_class = curr_oper_class;
	poper_class->header.len += sizeof(poper_class->current_oper_class);
	DBG_HEXDUMP(MCMD_D, "Operating class", (t_u8 *)poper_class,
		    sizeof(MrvlIEtypesHeader_t) + poper_class->header.len);
	ret = sizeof(MrvlIEtypesHeader_t) + poper_class->header.len;
	*pptlv_out += ret;
	poper_class->header.len = wlan_cpu_to_le16(poper_class->header.len);

	LEAVE();
	return ret;
}

/**
 *  @brief This function sets region table.
 *
 *  @param pmpriv  A pointer to mlan_private structure
 *  @param region  The region code
 *  @param band    The band
 *
 *  @return        MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
 */
mlan_status wlan_set_regiontable(mlan_private *pmpriv, t_u8 region, t_u16 band)
{
	mlan_adapter *pmadapter = pmpriv->adapter;
	int i = 0, j;
	chan_freq_power_t *cfp;
	int cfp_no;
	region_chan_t region_chan_old[MAX_REGION_CHANNEL_NUM];
	t_u8 cfp_code_bg = region;
	t_u8 cfp_code_a = region;

	ENTER();

	memcpy_ext(pmadapter, region_chan_old, pmadapter->region_channel,
		   sizeof(pmadapter->region_channel), sizeof(region_chan_old));
	memset(pmadapter, pmadapter->region_channel, 0,
	       sizeof(pmadapter->region_channel));

	if (band & (BAND_B | BAND_G | BAND_GN)) {
		if (pmadapter->cfp_code_bg)
			cfp_code_bg = pmadapter->cfp_code_bg;
		PRINTM(MCMND, "%s: 2.4G 0x%x\n", __func__, cfp_code_bg);
		cfp = wlan_get_region_cfp_table(pmadapter, cfp_code_bg,
						BAND_G | BAND_B | BAND_GN,
						&cfp_no);
		if (cfp) {
			pmadapter->region_channel[i].num_cfp = (t_u8)cfp_no;
			pmadapter->region_channel[i].pcfp = cfp;
		} else {
			PRINTM(MERROR, "wrong region code %#x in Band B-G\n",
			       region);
			LEAVE();
			return MLAN_STATUS_FAILURE;
		}
		pmadapter->region_channel[i].valid = MTRUE;
		pmadapter->region_channel[i].region = region;
		if (band & BAND_GN)
			pmadapter->region_channel[i].band = BAND_G;
		else
			pmadapter->region_channel[i].band =
				(band & BAND_G) ? BAND_G : BAND_B;

		for (j = 0; j < MAX_REGION_CHANNEL_NUM; j++) {
			if (region_chan_old[j].band & (BAND_B | BAND_G))
				break;
		}

		if ((j < MAX_REGION_CHANNEL_NUM) &&
		    (region_chan_old[j].valid == MTRUE)) {
			wlan_cfp_copy_dynamic(pmadapter, cfp, (t_u8)cfp_no,
					      region_chan_old[j].pcfp,
					      region_chan_old[j].num_cfp);
		} else if (cfp) {
			wlan_cfp_copy_dynamic(pmadapter, cfp, (t_u8)cfp_no,
					      MNULL, 0);
		}
		i++;
	}
	if (band & (BAND_A | BAND_AN | BAND_AAC)) {
		if (pmadapter->cfp_code_a)
			cfp_code_a = pmadapter->cfp_code_a;
		PRINTM(MCMND, "%s: 5G 0x%x\n", __func__, cfp_code_a);
		cfp = wlan_get_region_cfp_table(pmadapter, cfp_code_a, BAND_A,
						&cfp_no);
		if (cfp) {
			pmadapter->region_channel[i].num_cfp = (t_u8)cfp_no;
			pmadapter->region_channel[i].pcfp = cfp;
		} else {
			PRINTM(MERROR, "wrong region code %#x in Band A\n",
			       region);
			LEAVE();
			return MLAN_STATUS_FAILURE;
		}
		pmadapter->region_channel[i].valid = MTRUE;
		pmadapter->region_channel[i].region = region;
		pmadapter->region_channel[i].band = BAND_A;

		for (j = 0; j < MAX_REGION_CHANNEL_NUM; j++) {
			if (region_chan_old[j].band & BAND_A)
				break;
		}
		if ((j < MAX_REGION_CHANNEL_NUM) && region_chan_old[j].valid) {
			wlan_cfp_copy_dynamic(pmadapter, cfp, (t_u8)cfp_no,
					      region_chan_old[j].pcfp,
					      region_chan_old[j].num_cfp);
		} else if (cfp) {
			wlan_cfp_copy_dynamic(pmadapter, cfp, (t_u8)cfp_no,
					      MNULL, 0);
		}
		i++;
	}
	LEAVE();
	return MLAN_STATUS_SUCCESS;
}

/**
 *  @brief Get if radar detection is enabled or not on a certain channel
 *
 *  @param priv    Private driver information structure
 *  @param chnl Channel to determine radar detection requirements
 *
 *  @return
 *    - MTRUE if radar detection is required
 *    - MFALSE otherwise
 */
t_bool wlan_get_cfp_radar_detect(mlan_private *priv, t_u8 chnl)
{
	int i, j;
	t_bool required = MFALSE;
	chan_freq_power_t *pcfp = MNULL;

	ENTER();

	/*get the cfp table first*/
	for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) {
		if (priv->adapter->region_channel[i].band == BAND_A) {
			pcfp = priv->adapter->region_channel[i].pcfp;
			break;
		}
	}

	if (!pcfp) {
		/* This means operation in BAND-A is not support, we can
		 * just return false here, it's harmless
		 */
		goto done;
	}

	/*get the radar detection requirements according to chan num*/
	for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) {
		if (pcfp[j].channel == chnl) {
			required = pcfp[j].passive_scan_or_radar_detect;
			break;
		}
	}

done:
	LEAVE();
	return required;
}

/**
 *  @brief Get if scan type is passive or not on a certain channel for b/g band
 *
 *  @param priv    Private driver information structure
 *  @param chnl Channel to determine scan type
 *
 *  @return
 *    - MTRUE if scan type is passive
 *    - MFALSE otherwise
 */
t_bool wlan_bg_scan_type_is_passive(mlan_private *priv, t_u8 chnl)
{
	int i, j;
	t_bool passive = MFALSE;
	chan_freq_power_t *pcfp = MNULL;

	ENTER();

	/*get the cfp table first*/
	for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) {
		if (priv->adapter->region_channel[i].band & (BAND_B | BAND_G)) {
			pcfp = priv->adapter->region_channel[i].pcfp;
			break;
		}
	}

	if (!pcfp) {
		/*This means operation in BAND-B or BAND_G is not support, we
		 * can just return false here
		 */
		goto done;
	}

	/*get the bg scan type according to chan num*/
	for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) {
		if (pcfp[j].channel == chnl) {
			passive = pcfp[j].passive_scan_or_radar_detect;
			break;
		}
	}

done:
	LEAVE();
	return passive;
}

/**
 *  @brief Get if a channel is NO_IR (passive) or not
 *
 *  @param priv     Private driver information structure
 *  @param band     Band to check
 *  @param chan     Channel to check
 *
 *  @return
 *    - MTRUE if channel is passive
 *    - MFALSE otherwise
 */

t_bool wlan_is_chan_passive(mlan_private *priv, t_u16 band, t_u8 chan)
{
	int i, j;
	t_bool passive = MFALSE;
	chan_freq_power_t *pcfp = MNULL;

	ENTER();

	/* get the cfp table first */
	for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) {
		if (priv->adapter->region_channel[i].band & band) {
			pcfp = priv->adapter->region_channel[i].pcfp;
			break;
		}
	}

	if (pcfp) {
		/* check table according to chan num */
		for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) {
			if (pcfp[j].channel == chan) {
				if (pcfp[j].dynamic.flags & NXP_CHANNEL_PASSIVE)
					passive = MTRUE;
				break;
			}
		}
	}

	LEAVE();
	return passive;
}

/**
 *  @brief Get if a channel is disabled or not
 *
 *  @param priv     Private driver information structure
 *  @param band     Band to check
 *  @param chan     Channel to check
 *
 *  @return
 *    - MTRUE if channel is disabled
 *    - MFALSE otherwise
 */

t_bool wlan_is_chan_disabled(mlan_private *priv, t_u16 band, t_u8 chan)
{
	int i, j;
	t_bool disabled = MFALSE;
	chan_freq_power_t *pcfp = MNULL;

	ENTER();

	/* get the cfp table first */
	for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) {
		if (priv->adapter->region_channel[i].band & band) {
			pcfp = priv->adapter->region_channel[i].pcfp;
			break;
		}
	}

	if (pcfp) {
		/* check table according to chan num */
		for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) {
			if (pcfp[j].channel == chan) {
				if (pcfp[j].dynamic.flags &
				    NXP_CHANNEL_DISABLED)
					disabled = MTRUE;
				break;
			}
		}
	}

	LEAVE();
	return disabled;
}

/**
 *  @brief Get if a channel is blacklisted or not
 *
 *  @param priv     Private driver information structure
 *  @param band     Band to check
 *  @param chan     Channel to check
 *
 *  @return
 *    - MTRUE if channel is blacklisted
 *    - MFALSE otherwise
 */

t_bool wlan_is_chan_blacklisted(mlan_private *priv, t_u16 band, t_u8 chan)
{
	int i, j;
	t_bool blacklist = MFALSE;
	chan_freq_power_t *pcfp = MNULL;

	ENTER();

	/*get the cfp table first*/
	for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) {
		if (priv->adapter->region_channel[i].band & band) {
			pcfp = priv->adapter->region_channel[i].pcfp;
			break;
		}
	}

	if (pcfp) {
		/*check table according to chan num*/
		for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) {
			if (pcfp[j].channel == chan) {
				blacklist = pcfp[j].dynamic.blacklist;
				break;
			}
		}
	}

	LEAVE();
	return blacklist;
}

/**
 *  @brief Set a channel as blacklisted or not
 *
 *  @param priv     Private driver information structure
 *  @param band     Band to check
 *  @param chan     Channel to check
 *  @param bl       Blacklist if MTRUE
 *
 *  @return
 *    - MTRUE if channel setting is updated
 *    - MFALSE otherwise
 */

t_bool wlan_set_chan_blacklist(mlan_private *priv, t_u16 band, t_u8 chan,
			       t_bool bl)
{
	int i, j;
	t_bool set_bl = MFALSE;
	chan_freq_power_t *pcfp = MNULL;

	ENTER();

	/*get the cfp table first*/
	for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) {
		if (priv->adapter->region_channel[i].band & band) {
			pcfp = priv->adapter->region_channel[i].pcfp;
			break;
		}
	}

	if (pcfp) {
		/*check table according to chan num*/
		for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) {
			if (pcfp[j].channel == chan) {
				pcfp[j].dynamic.blacklist = bl;
				set_bl = MTRUE;
				break;
			}
		}
	}

	LEAVE();
	return set_bl;
}

/**
 *  @brief Set channel's dfs state
 *
 *  @param priv         Private driver information structure
 *  @param band         Band to check
 *  @param chan         Channel to check
 *  @param dfs_state    dfs state
 *
 *  @return  N/A
 */
t_void wlan_set_chan_dfs_state(mlan_private *priv, t_u16 band, t_u8 chan,
			       dfs_state_t dfs_state)
{
	int i, j;
	chan_freq_power_t *pcfp = MNULL;

	ENTER();

	/*get the cfp table first*/
	for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) {
		if (priv->adapter->region_channel[i].band & band) {
			pcfp = priv->adapter->region_channel[i].pcfp;
			break;
		}
	}

	if (pcfp) {
		/*check table according to chan num*/
		for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) {
			if (pcfp[j].channel == chan) {
				pcfp[j].dynamic.dfs_state = dfs_state;
				break;
			}
		}
	}

	LEAVE();
}

/**
 *  @brief get channel's dfs state
 *
 *  @param priv         Private driver information structure
 *  @param band         Band to check
 *  @param chan         Channel to check
 *
 *  @return  channel's dfs state
 */
dfs_state_t wlan_get_chan_dfs_state(mlan_private *priv, t_u16 band, t_u8 chan)
{
	int i, j;
	chan_freq_power_t *pcfp = MNULL;
	dfs_state_t dfs_state = DFS_USABLE;

	ENTER();

	/*get the cfp table first*/
	for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) {
		if (priv->adapter->region_channel[i].band & band) {
			pcfp = priv->adapter->region_channel[i].pcfp;
			break;
		}
	}

	if (pcfp) {
		/*check table according to chan num*/
		for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) {
			if (pcfp[j].channel == chan) {
				dfs_state = pcfp[j].dynamic.dfs_state;
				break;
			}
		}
	}

	LEAVE();
	return dfs_state;
}

/**
 *  @brief reset all channel's dfs state
 *
 *  @param priv         Private driver information structure
 *  @param band         Band to check
 *  @param dfs_state    dfs state
 *
 *  @return  N/A
 */
t_void wlan_reset_all_chan_dfs_state(mlan_private *priv, t_u16 band,
				     dfs_state_t dfs_state)
{
	int i, j;
	chan_freq_power_t *pcfp = MNULL;

	ENTER();

	/*get the cfp table first*/
	for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) {
		if (priv->adapter->region_channel[i].band & band) {
			pcfp = priv->adapter->region_channel[i].pcfp;
			break;
		}
	}

	if (pcfp) {
		/*check table according to chan num*/
		for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) {
			pcfp[j].dynamic.dfs_state = dfs_state;
		}
	}

	LEAVE();
}

/**
 *  @brief Convert rateid in IEEE format to MRVL format
 *
 *  @param priv     Private driver information structure
 *  @param IeeeMacRate  Rate in terms of IEEE format
 *  @param pmbuf     A pointer to packet buffer
 *
 *  @return
 *    Rate ID in terms of MRVL format
 */
t_u8 wlan_ieee_rateid_to_mrvl_rateid(mlan_private *priv, t_u16 IeeeMacRate,
				     t_u8 *dst_mac)
{
	/* Set default rate ID to RATEID_DBPSK1Mbps */
	t_u8 mrvlRATEID = 0;
	const rate_map *rate_tbl = rate_map_table_1x1;
	t_u32 cnt = sizeof(rate_map_table_1x1) / sizeof(rate_map);
	t_u8 skip_nss2 = MTRUE;
	t_u32 i = 0;
	IEEEtypes_HTCap_t *htcap = MNULL;
	t_u8 tx_mcs_supp = GET_TXMCSSUPP(priv->usr_dev_mcs_support);
#ifdef UAP_SUPPORT
	psta_node sta_ptr = MNULL;
#endif

	ENTER();

	if (priv->adapter->hw_dev_mcs_support == HT_STREAM_MODE_2X2) {
		rate_tbl = rate_map_table_2x2;
		cnt = sizeof(rate_map_table_2x2) / sizeof(rate_map);
	}

#ifdef UAP_SUPPORT
	if (priv->bss_role == MLAN_BSS_ROLE_UAP) {
		if (!dst_mac) {
			LEAVE();
			return mrvlRATEID;
		}
		sta_ptr = (sta_node *)util_peek_list(
			priv->adapter->pmoal_handle, &priv->sta_list,
			priv->adapter->callbacks.moal_spin_lock,
			priv->adapter->callbacks.moal_spin_unlock);
		if (!sta_ptr) {
			LEAVE();
			return mrvlRATEID;
		}
		while (sta_ptr != (sta_node *)&priv->sta_list) {
			if (memcmp(priv->adapter, dst_mac, sta_ptr->mac_addr,
				   MLAN_MAC_ADDR_LENGTH)) {
				htcap = &(sta_ptr->HTcap);
				break;
			}
			sta_ptr = sta_ptr->pnext;
		}
	}
#endif
#ifdef STA_SUPPORT
	if (priv->bss_role == MLAN_BSS_ROLE_STA)
		htcap = priv->curr_bss_params.bss_descriptor.pht_cap;
#endif
	if (htcap) {
		/* If user configure tx to 2x2 and peer device rx is 2x2 */
		if (tx_mcs_supp >= 2 && htcap->ht_cap.supported_mcs_set[1])
			skip_nss2 = MFALSE;
	}

	for (i = 0; i < cnt; i++) {
		if (rate_tbl[i].nss && skip_nss2)
			continue;
		if (rate_tbl[i].rate == IeeeMacRate) {
			mrvlRATEID = rate_tbl[i].id;
			break;
		}
	}

	return mrvlRATEID;
}

/**
 *  @brief Convert rateid in MRVL format to IEEE format
 *
 *  @param IeeeMacRate  Rate in terms of MRVL format
 *
 *  @return
 *    Rate ID in terms of IEEE format
 */
t_u8 wlan_mrvl_rateid_to_ieee_rateid(t_u8 rate)
{
	return rateUnit_500Kbps[rate];
}

/**
 *  @brief	 sort cfp otp table
 *
 *  @param pmapdater	a pointer to mlan_adapter structure
 *
 *  @return
 *    None
 */
static void wlan_sort_cfp_otp_table(mlan_adapter *pmadapter)
{
	t_u8 c, d;
	chan_freq_power_t *ch1;
	chan_freq_power_t *ch2;
	chan_freq_power_t swap;

	if (pmadapter->tx_power_table_a_rows <= 1)
		return;
	for (c = 0; c < pmadapter->tx_power_table_a_rows - 1; c++) {
		for (d = 0; d < pmadapter->tx_power_table_a_rows - c - 1; d++) {
			ch1 = (chan_freq_power_t *)(pmadapter->cfp_otp_a + d);
			ch2 = (chan_freq_power_t *)(pmadapter->cfp_otp_a + d +
						    1);
			if (ch1->channel > ch2->channel) {
				memcpy_ext(pmadapter, &swap, ch1,
					   sizeof(chan_freq_power_t),
					   sizeof(chan_freq_power_t));
				memcpy_ext(pmadapter, ch1, ch2,
					   sizeof(chan_freq_power_t),
					   sizeof(chan_freq_power_t));
				memcpy_ext(pmadapter, ch2, &swap,
					   sizeof(chan_freq_power_t),
					   sizeof(chan_freq_power_t));
			}
		}
	}
}

/**
 *  @brief	Update CFP tables and power tables from FW
 *
 *  @param priv		Private driver information structure
 *  @param buf		Pointer to the buffer holding TLV data
 *					from 0x242 command response.
 *  @param buf_left	bufsize
 *
 *  @return
 *    None
 */
void wlan_add_fw_cfp_tables(pmlan_private pmpriv, t_u8 *buf, t_u16 buf_left)
{
	mlan_adapter *pmadapter = pmpriv->adapter;
	mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks;
	MrvlIEtypesHeader_t *head;
	t_u16 tlv;
	t_u16 tlv_buf_len;
	t_u16 tlv_buf_left;
	t_u16 i;
	int k = 0, rows, cols;
	t_u16 max_tx_pwr_bg = WLAN_TX_PWR_DEFAULT;
	t_u16 max_tx_pwr_a = WLAN_TX_PWR_DEFAULT;
	t_u8 *tlv_buf;
	t_u8 *data;
	t_u8 *tmp;
	mlan_status ret;

	ENTER();

	if (!buf) {
		PRINTM(MERROR, "CFP table update failed!\n");
		goto out;
	}
	if (pmadapter->otp_region) {
		memset(pmadapter, pmadapter->region_channel, 0,
		       sizeof(pmadapter->region_channel));
		wlan_free_fw_cfp_tables(pmadapter);
	}
	pmadapter->tx_power_table_bg_rows = FW_CFP_TABLE_MAX_ROWS_BG;
	pmadapter->tx_power_table_bg_cols = FW_CFP_TABLE_MAX_COLS_BG;
	pmadapter->tx_power_table_a_rows = FW_CFP_TABLE_MAX_ROWS_A;
	pmadapter->tx_power_table_a_cols = FW_CFP_TABLE_MAX_COLS_A;
	tlv_buf = (t_u8 *)buf;
	tlv_buf_left = buf_left;

	while (tlv_buf_left >= sizeof(*head)) {
		head = (MrvlIEtypesHeader_t *)tlv_buf;
		tlv = wlan_le16_to_cpu(head->type);
		tlv_buf_len = wlan_le16_to_cpu(head->len);

		if (tlv_buf_left < (sizeof(*head) + tlv_buf_len))
			break;
		data = (t_u8 *)head + sizeof(*head);

		switch (tlv) {
		case TLV_TYPE_REGION_INFO:
			/* Skip adding fw region info if it already exists or
			 * if this TLV has no set data
			 */
			if (*data == 0)
				break;
			if (pmadapter->otp_region)
				break;

			ret = pcb->moal_malloc(pmadapter->pmoal_handle,
					       sizeof(otp_region_info_t),
					       MLAN_MEM_DEF,
					       (t_u8 **)&pmadapter->otp_region);
			if (ret != MLAN_STATUS_SUCCESS ||
			    !pmadapter->otp_region) {
				PRINTM(MERROR,
				       "Memory allocation for the otp region info struct failed!\n");
				break;
			}
			/* Save region info values from OTP in the otp_region
			 * structure
			 */
			memcpy_ext(pmadapter, pmadapter->otp_region, data,
				   sizeof(otp_region_info_t),
				   sizeof(otp_region_info_t));
			data += sizeof(otp_region_info_t);
			/* Get pre-defined cfp tables corresponding to the
			 * region code in OTP
			 */
			for (i = 0; i < MLAN_CFP_TABLE_SIZE_BG; i++) {
				if (cfp_table_BG[i].code ==
				    pmadapter->otp_region->region_code) {
					max_tx_pwr_bg = (cfp_table_BG[i].cfp)
								->max_tx_power;
					break;
				}
			}
			for (i = 0; i < MLAN_CFP_TABLE_SIZE_A; i++) {
				if (cfp_table_A[i].code ==
				    pmadapter->otp_region->region_code) {
					max_tx_pwr_a = (cfp_table_A[i].cfp)
							       ->max_tx_power;
					break;
				}
			}
			/* Update the region code and the country code in
			 * pmadapter
			 */
			pmadapter->region_code =
				pmadapter->otp_region->region_code;
			pmadapter->country_code[0] =
				pmadapter->otp_region->country_code[0];
			pmadapter->country_code[1] =
				pmadapter->otp_region->country_code[1];
			pmadapter->country_code[2] = '\0';
			pmadapter->domain_reg.country_code[0] =
				pmadapter->otp_region->country_code[0];
			pmadapter->domain_reg.country_code[1] =
				pmadapter->otp_region->country_code[1];
			pmadapter->domain_reg.country_code[2] = '\0';
			PRINTM(MCMND,
			       "OTP region: region_code=%d %c%c dfs_region=%d\n",
			       pmadapter->otp_region->region_code,
			       pmadapter->country_code[0],
			       pmadapter->country_code[1],
			       pmadapter->otp_region->dfs_region);
			pmadapter->cfp_code_bg =
				pmadapter->otp_region->region_code;
			pmadapter->cfp_code_a =
				pmadapter->otp_region->region_code;
			break;
		case TLV_TYPE_CHAN_ATTR_CFG:
			/* Skip adding fw cfp tables if they already exist or
			 * if this TLV has no set data
			 */
			if (*data == 0)
				break;
			if (pmadapter->cfp_otp_bg || pmadapter->cfp_otp_a) {
				break;
			}

			ret = pcb->moal_malloc(
				pmadapter->pmoal_handle,
				pmadapter->tx_power_table_bg_rows *
					sizeof(chan_freq_power_t),
				MLAN_MEM_DEF, (t_u8 **)&pmadapter->cfp_otp_bg);
			if (ret != MLAN_STATUS_SUCCESS ||
			    !pmadapter->cfp_otp_bg) {
				PRINTM(MERROR,
				       "Memory allocation for storing otp bg table data failed!\n");
				break;
			}
			/* Save channel usability flags from OTP data in the fw
			 * cfp bg table and set frequency and max_tx_power
			 * values
			 */
			for (i = 0; i < pmadapter->tx_power_table_bg_rows;
			     i++) {
				(pmadapter->cfp_otp_bg + i)->channel = *data;
				if (*data == 14)
					(pmadapter->cfp_otp_bg + i)->freq =
						2484;
				else
					(pmadapter->cfp_otp_bg + i)->freq =
						2412 + 5 * (*data - 1);
				(pmadapter->cfp_otp_bg + i)->max_tx_power =
					max_tx_pwr_bg;
				data++;
				(pmadapter->cfp_otp_bg + i)->dynamic.flags =
					*data;
				if (*data & NXP_CHANNEL_DFS)
					(pmadapter->cfp_otp_bg + i)
						->passive_scan_or_radar_detect =
						MTRUE;
				PRINTM(MCMD_D,
				       "OTP Region (BG): chan=%d flags=0x%x\n",
				       (pmadapter->cfp_otp_bg + i)->channel,
				       (pmadapter->cfp_otp_bg + i)
					       ->dynamic.flags);
				data++;
			}
			ret = pcb->moal_malloc(
				pmadapter->pmoal_handle,
				pmadapter->tx_power_table_a_rows *
					sizeof(chan_freq_power_t),
				MLAN_MEM_DEF, (t_u8 **)&pmadapter->cfp_otp_a);
			if (ret != MLAN_STATUS_SUCCESS ||
			    !pmadapter->cfp_otp_a) {
				PRINTM(MERROR,
				       "Memory allocation for storing otp a table data failed!\n");
				break;
			}
			/* Save channel usability flags from OTP data in the fw
			 * cfp a table and set frequency and max_tx_power values
			 */
			for (i = 0; i < pmadapter->tx_power_table_a_rows; i++) {
				(pmadapter->cfp_otp_a + i)->channel = *data;
				if (*data < 183)
					/* 5GHz channels */
					(pmadapter->cfp_otp_a + i)->freq =
						5035 + 5 * (*data - 7);
				else
					/* 4GHz channels */
					(pmadapter->cfp_otp_a + i)->freq =
						4915 + 5 * (*data - 183);
				(pmadapter->cfp_otp_a + i)->max_tx_power =
					max_tx_pwr_a;
				data++;
				(pmadapter->cfp_otp_a + i)->dynamic.flags =
					*data;
				(pmadapter->cfp_otp_a + i)->dynamic.dfs_state =
					DFS_USABLE;
				if (*data & NXP_CHANNEL_DFS)
					(pmadapter->cfp_otp_a + i)
						->passive_scan_or_radar_detect =
						MTRUE;
				PRINTM(MCMD_D,
				       "OTP Region (A): chan=%d flags=0x%x\n",
				       (pmadapter->cfp_otp_a + i)->channel,
				       (pmadapter->cfp_otp_a + i)
					       ->dynamic.flags);

				data++;
			}
			break;
		case TLV_TYPE_POWER_TABLE:
			/* Skip adding fw power tables if this TLV has no data
			 * or if they already exists but force reg rule is set
			 * in the otp
			 */
			if (*data == 0)
				break;
			if (pmadapter->otp_region &&
			    pmadapter->otp_region->force_reg &&
			    pmadapter->tx_power_table_bg)
				break;

			/* Save the tlv data in power tables for band BG and A
			 */
			tmp = data;
			i = 0;
			while ((i <
				pmadapter->tx_power_table_bg_rows *
					pmadapter->tx_power_table_bg_cols) &&
			       (i < tlv_buf_len)) {
				i++;
				tmp++;
			}
			if (!pmadapter->tx_power_table_bg) {
				ret = pcb->moal_malloc(
					pmadapter->pmoal_handle, i,
					MLAN_MEM_DEF,
					(t_u8 **)&pmadapter->tx_power_table_bg);
				if (ret != MLAN_STATUS_SUCCESS ||
				    !pmadapter->tx_power_table_bg) {
					PRINTM(MERROR,
					       "Memory allocation for the BG-band power table failed!\n");
					break;
				}
			}
			memcpy_ext(pmadapter, pmadapter->tx_power_table_bg,
				   data, i, i);
			pmadapter->tx_power_table_bg_size = i;
			data += i;
			i = 0;
			while ((i < pmadapter->tx_power_table_a_rows *
					    pmadapter->tx_power_table_a_cols) &&
			       (i < (tlv_buf_len -
				     pmadapter->tx_power_table_bg_size))) {
				i++;
			}
			if (!pmadapter->tx_power_table_a) {
				ret = pcb->moal_malloc(
					pmadapter->pmoal_handle, i,
					MLAN_MEM_DEF,
					(t_u8 **)&pmadapter->tx_power_table_a);
				if (ret != MLAN_STATUS_SUCCESS ||
				    !pmadapter->tx_power_table_a) {
					PRINTM(MERROR,
					       "Memory allocation for the A-band power table failed!\n");
					break;
				}
			}
			memcpy_ext(pmadapter, pmadapter->tx_power_table_a, data,
				   i, i);
			pmadapter->tx_power_table_a_size = i;
			break;
		case TLV_TYPE_POWER_TABLE_ATTR:
			pmadapter->tx_power_table_bg_rows =
				((power_table_attr_t *)data)->rows_2g;
			pmadapter->tx_power_table_bg_cols =
				((power_table_attr_t *)data)->cols_2g;
			pmadapter->tx_power_table_a_rows =
				((power_table_attr_t *)data)->rows_5g;
			pmadapter->tx_power_table_a_cols =
				((power_table_attr_t *)data)->cols_5g;
			PRINTM(MCMD_D,
			       "OTP region: bg_row=%d,bg_cols=%d a_row=%d, a_cols=%d\n",
			       pmadapter->tx_power_table_bg_rows,
			       pmadapter->tx_power_table_bg_cols,
			       pmadapter->tx_power_table_a_rows,
			       pmadapter->tx_power_table_a_cols);
			break;
		default:
			break;
		}
		tlv_buf += (sizeof(*head) + tlv_buf_len);
		tlv_buf_left -= (sizeof(*head) + tlv_buf_len);
	}
	if (!pmadapter->cfp_otp_bg || !pmadapter->tx_power_table_bg)
		goto out;
	/* Set remaining flags for BG */
	rows = pmadapter->tx_power_table_bg_rows;
	cols = pmadapter->tx_power_table_bg_cols;

	for (i = 0; i < rows; i++) {
		k = (i * cols) + 1;
		if ((pmadapter->cfp_otp_bg + i)->dynamic.flags &
		    NXP_CHANNEL_DISABLED)
			continue;

		if (pmadapter->tx_power_table_bg[k + MOD_CCK] == 0)
			(pmadapter->cfp_otp_bg + i)->dynamic.flags |=
				NXP_CHANNEL_NO_CCK;

		if (pmadapter->tx_power_table_bg[k + MOD_OFDM_PSK] == 0 &&
		    pmadapter->tx_power_table_bg[k + MOD_OFDM_QAM16] == 0 &&
		    pmadapter->tx_power_table_bg[k + MOD_OFDM_QAM64] == 0) {
			(pmadapter->cfp_otp_bg + i)->dynamic.flags |=
				NXP_CHANNEL_NO_OFDM;
		}
	}
	if (pmadapter->cfp_otp_a)
		wlan_sort_cfp_otp_table(pmadapter);
out:
	LEAVE();
}

/**
 *  @brief	This function deallocates otp cfp and power tables memory.
 *
 *  @param pmadapter	A pointer to mlan_adapter structure
 */
void wlan_free_fw_cfp_tables(mlan_adapter *pmadapter)
{
	pmlan_callbacks pcb;

	ENTER();

	pcb = &pmadapter->callbacks;
	if (pmadapter->otp_region)
		pcb->moal_mfree(pmadapter->pmoal_handle,
				(t_u8 *)pmadapter->otp_region);
	if (pmadapter->cfp_otp_bg)
		pcb->moal_mfree(pmadapter->pmoal_handle,
				(t_u8 *)pmadapter->cfp_otp_bg);
	if (pmadapter->tx_power_table_bg)
		pcb->moal_mfree(pmadapter->pmoal_handle,
				(t_u8 *)pmadapter->tx_power_table_bg);
	pmadapter->otp_region = MNULL;
	pmadapter->cfp_otp_bg = MNULL;
	pmadapter->tx_power_table_bg = MNULL;
	pmadapter->tx_power_table_bg_size = 0;
	if (pmadapter->cfp_otp_a)
		pcb->moal_mfree(pmadapter->pmoal_handle,
				(t_u8 *)pmadapter->cfp_otp_a);
	if (pmadapter->tx_power_table_a)
		pcb->moal_mfree(pmadapter->pmoal_handle,
				(t_u8 *)pmadapter->tx_power_table_a);
	pmadapter->cfp_otp_a = MNULL;
	pmadapter->tx_power_table_a = MNULL;
	pmadapter->tx_power_table_a_size = 0;
	LEAVE();
}

/**
 *  @brief Get DFS chan list
 *
 *  @param pmadapter    Pointer to mlan_adapter
 *  @param pioctl_req   Pointer to mlan_ioctl_req
 *
 *  @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
 */
mlan_status wlan_get_cfp_table(pmlan_adapter pmadapter,
			       pmlan_ioctl_req pioctl_req)
{
	mlan_ds_misc_cfg *ds_misc_cfg = MNULL;
	mlan_status ret = MLAN_STATUS_FAILURE;
	chan_freq_power_t *cfp = MNULL;
	t_u32 cfp_no = 0;

	ENTER();
	if (pioctl_req) {
		ds_misc_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf;
		if (pioctl_req->action == MLAN_ACT_GET) {
			cfp = wlan_get_region_cfp_table(
				pmadapter, pmadapter->region_code,
				ds_misc_cfg->param.cfp.band, &cfp_no);
			if (cfp) {
				ds_misc_cfg->param.cfp.num_chan = cfp_no;
				memcpy_ext(pmadapter,
					   ds_misc_cfg->param.cfp.cfp_tbl, cfp,
					   cfp_no * sizeof(chan_freq_power_t),
					   cfp_no * sizeof(chan_freq_power_t));
			}
			ret = MLAN_STATUS_SUCCESS;
		}
	}
	LEAVE();
	return ret;
}

/**
 *  @brief	Get power tables and cfp tables for set region code
 *			into the IOCTL request buffer
 *
 *  @param pmadapter	Private mlan adapter structure
 *  @param pioctl_req	Pointer to the IOCTL request structure
 *
 *  @return	success, otherwise fail
 *
 */
mlan_status wlan_get_cfpinfo(pmlan_adapter pmadapter,
			     pmlan_ioctl_req pioctl_req)
{
	chan_freq_power_t *cfp_bg = MNULL;
	t_u32 cfp_no_bg = 0;
	chan_freq_power_t *cfp_a = MNULL;
	t_u32 cfp_no_a = 0;
	t_u8 cfp_code_a = pmadapter->region_code;
	t_u8 cfp_code_bg = pmadapter->region_code;
	t_u32 len = 0, size = 0;
	t_u8 *req_buf, *tmp;
	mlan_status ret = MLAN_STATUS_SUCCESS;

	ENTER();

	if (!pioctl_req || !pioctl_req->pbuf) {
		PRINTM(MERROR, "MLAN IOCTL information is not present!\n");
		ret = MLAN_STATUS_FAILURE;
		goto out;
	}
	ret = wlan_11h_ioctl_nop_channel_list(pmadapter, pioctl_req);
	if (ret) {
		PRINTM(MERROR,
		       "cfpinfo wlan_11h_ioctl_nop_channel_list failed!\n");
		ret = MLAN_STATUS_FAILURE;
		goto out;
	}
	/* Calculate the total response size required to return region,
	 * country codes, cfp tables and power tables
	 */
	size = sizeof(pmadapter->country_code) + sizeof(pmadapter->region_code);
	/* Add size to store region, country and environment codes */
	size += sizeof(t_u32);
	if (pmadapter->cfp_code_bg)
		cfp_code_bg = pmadapter->cfp_code_bg;

	/* Get cfp table and its size corresponding to the region code */
	cfp_bg = wlan_get_region_cfp_table(pmadapter, cfp_code_bg,
					   BAND_G | BAND_B, &cfp_no_bg);
	size += cfp_no_bg * sizeof(chan_freq_power_t);
	if (pmadapter->cfp_code_a)
		cfp_code_a = pmadapter->cfp_code_a;
	cfp_a = wlan_get_region_cfp_table(pmadapter, cfp_code_a, BAND_A,
					  &cfp_no_a);
	size += cfp_no_a * sizeof(chan_freq_power_t);
	if (pmadapter->otp_region)
		size += sizeof(pmadapter->otp_region->environment);

	/* Get power table size */
	if (pmadapter->tx_power_table_bg) {
		size += pmadapter->tx_power_table_bg_size;
		/* Add size to store table size, rows and cols */
		size += 3 * sizeof(t_u32);
	}
	if (pmadapter->tx_power_table_a) {
		size += pmadapter->tx_power_table_a_size;
		size += 3 * sizeof(t_u32);
	}
	/* Check information buffer length of MLAN IOCTL */
	if (pioctl_req->buf_len < size) {
		PRINTM(MWARN,
		       "MLAN IOCTL information buffer length is too short.\n");
		pioctl_req->buf_len_needed = size;
		pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER;
		ret = MLAN_STATUS_RESOURCE;
		goto out;
	}
	/* Copy the total size of region code, country code and environment
	 * in first four bytes of the IOCTL request buffer and then copy
	 * codes respectively in following bytes
	 */
	req_buf = (t_u8 *)pioctl_req->pbuf;
	size = sizeof(pmadapter->country_code) + sizeof(pmadapter->region_code);
	if (pmadapter->otp_region)
		size += sizeof(pmadapter->otp_region->environment);
	tmp = (t_u8 *)&size;
	memcpy_ext(pmadapter, req_buf, tmp, sizeof(size), sizeof(size));
	len += sizeof(size);
	memcpy_ext(pmadapter, req_buf + len, &pmadapter->region_code,
		   sizeof(pmadapter->region_code),
		   sizeof(pmadapter->region_code));
	len += sizeof(pmadapter->region_code);
	memcpy_ext(pmadapter, req_buf + len, &pmadapter->country_code,
		   sizeof(pmadapter->country_code),
		   sizeof(pmadapter->country_code));
	len += sizeof(pmadapter->country_code);
	if (pmadapter->otp_region) {
		memcpy_ext(pmadapter, req_buf + len,
			   &pmadapter->otp_region->environment,
			   sizeof(pmadapter->otp_region->environment),
			   sizeof(pmadapter->otp_region->environment));
		len += sizeof(pmadapter->otp_region->environment);
	}
	/* copy the cfp table size followed by the entire table */
	if (!cfp_bg)
		goto out;
	size = cfp_no_bg * sizeof(chan_freq_power_t);
	memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size));
	len += sizeof(size);
	memcpy_ext(pmadapter, req_buf + len, cfp_bg, size, size);
	len += size;
	if (!cfp_a)
		goto out;
	size = cfp_no_a * sizeof(chan_freq_power_t);
	memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size));
	len += sizeof(size);
	memcpy_ext(pmadapter, req_buf + len, cfp_a, size, size);
	len += size;
	/* Copy the size of the power table, number of rows, number of cols
	 * and the entire power table
	 */
	if (!pmadapter->tx_power_table_bg)
		goto out;
	size = pmadapter->tx_power_table_bg_size;
	memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size));
	len += sizeof(size);

	/* No. of rows */
	size = pmadapter->tx_power_table_bg_rows;
	memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size));
	len += sizeof(size);

	/* No. of cols */
	size = pmadapter->tx_power_table_bg_size /
	       pmadapter->tx_power_table_bg_rows;
	memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size));
	len += sizeof(size);
	memcpy_ext(pmadapter, req_buf + len, pmadapter->tx_power_table_bg,
		   pmadapter->tx_power_table_bg_size,
		   pmadapter->tx_power_table_bg_size);
	len += pmadapter->tx_power_table_bg_size;
	if (!pmadapter->tx_power_table_a)
		goto out;
	size = pmadapter->tx_power_table_a_size;
	memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size));
	len += sizeof(size);

	/* No. of rows */
	size = pmadapter->tx_power_table_a_rows;
	memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size));
	len += sizeof(size);

	/* No. of cols */
	size = pmadapter->tx_power_table_a_size /
	       pmadapter->tx_power_table_a_rows;
	memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size));
	len += sizeof(size);
	memcpy_ext(pmadapter, req_buf + len, pmadapter->tx_power_table_a,
		   pmadapter->tx_power_table_a_size,
		   pmadapter->tx_power_table_a_size);
	len += pmadapter->tx_power_table_a_size;
out:
	if (pioctl_req)
		pioctl_req->data_read_written = len;

	LEAVE();
	return ret;
}
