blob: e090f7f54e791f4448fcba0ffdb63754e303f5dd [file] [log] [blame]
/*
* Copyright (c) 2012-2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: wlan_hdd_cfg.c
*
* WLAN Host Device Driver configuration interface implementation
*/
/* Include Files */
#include <linux/firmware.h>
#include <linux/string.h>
#include <wlan_hdd_includes.h>
#include <wlan_hdd_main.h>
#include <wlan_hdd_assoc.h>
#include <wlan_hdd_cfg.h>
#include <linux/string.h>
#include <qdf_types.h>
#include <csr_api.h>
#include <wlan_hdd_misc.h>
#include <wlan_hdd_napi.h>
#include <cds_api.h>
#include "wlan_hdd_he.h"
#include <wlan_policy_mgr_api.h>
#include "wifi_pos_api.h"
#include "wlan_hdd_green_ap.h"
#include "wlan_hdd_twt.h"
#include "wlan_policy_mgr_ucfg.h"
#include "wlan_mlme_ucfg_api.h"
#include "wlan_mlme_public_struct.h"
#include "wlan_fwol_ucfg_api.h"
#include "cfg_ucfg_api.h"
#include "hdd_dp_cfg.h"
/**
* get_next_line() - find and locate the new line pointer
* @str: pointer to string
*
* This function returns a pointer to the character after the occurrence
* of a new line character. It also modifies the original string by replacing
* the '\n' character with the null character.
*
* Return: the pointer to the character at new line,
* or NULL if no new line character was found
*/
static char *get_next_line(char *str)
{
char c;
if (!str || *str == '\0')
return NULL;
c = *str;
while (c != '\n' && c != '\0' && c != 0xd) {
str = str + 1;
c = *str;
}
if (c == '\0')
return NULL;
*str = '\0';
return str + 1;
}
/** look for space. Ascii values to look are
* 0x09 == horizontal tab
* 0x0a == Newline ("\n")
* 0x0b == vertical tab
* 0x0c == Newpage or feed form.
* 0x0d == carriage return (CR or "\r")
* Null ('\0') should not considered as space.
*/
#define i_isspace(ch) (((ch) >= 0x09 && (ch) <= 0x0d) || (ch) == ' ')
/**
* i_trim() - trims any leading and trailing white spaces
* @str: pointer to string
*
* Return: the pointer of the string
*/
static char *i_trim(char *str)
{
char *ptr;
if (*str == '\0')
return str;
/* Find the first non white-space */
ptr = str;
while (i_isspace(*ptr))
ptr++;
if (*ptr == '\0')
return str;
/* This is the new start of the string */
str = ptr;
/* Find the last non white-space */
ptr += strlen(ptr) - 1;
while (ptr != str && i_isspace(*ptr))
ptr--;
/* Null terminate the following character */
ptr[1] = '\0';
return str;
}
/** struct hdd_cfg_entry - ini configuration entry
* @name: name of the entry
* @value: value of the entry
*/
struct hdd_cfg_entry {
char *name;
char *value;
};
/**
* update_mac_from_string() - convert string to 6 bytes mac address
* @hdd_ctx: the pointer to hdd context
* @mac_table: the mac_table to carry the conversion
* @num: number of the interface
*
* 00AA00BB00CC -> 0x00 0xAA 0x00 0xBB 0x00 0xCC
*
* Return: QDF_STATUS
*/
static QDF_STATUS update_mac_from_string(struct hdd_context *hdd_ctx,
struct hdd_cfg_entry *mac_table,
int num)
{
int i = 0, j = 0, res = 0;
char *candidate = NULL;
struct qdf_mac_addr macaddr[QDF_MAX_CONCURRENCY_PERSONA];
QDF_STATUS status = QDF_STATUS_SUCCESS;
memset(macaddr, 0, sizeof(macaddr));
for (i = 0; i < num; i++) {
candidate = mac_table[i].value;
for (j = 0; j < QDF_MAC_ADDR_SIZE; j++) {
res =
hex2bin(&macaddr[i].bytes[j], &candidate[(j << 1)],
1);
if (res < 0)
break;
}
if (res == 0 && !qdf_is_macaddr_zero(&macaddr[i])) {
qdf_mem_copy((uint8_t *)&hdd_ctx->
provisioned_mac_addr[i].bytes[0],
(uint8_t *) &macaddr[i].bytes[0],
QDF_MAC_ADDR_SIZE);
} else {
status = QDF_STATUS_E_FAILURE;
break;
}
}
return status;
}
/**
* hdd_set_power_save_offload_config() - set power save offload configuration
* @hdd_ctx: the pointer to hdd context
*
* Return: none
*/
static void hdd_set_power_save_offload_config(struct hdd_context *hdd_ctx)
{
uint32_t listen_interval = 0;
char *power_usage = NULL;
power_usage = ucfg_mlme_get_power_usage(hdd_ctx->psoc);
if (!power_usage) {
hdd_err("invalid power usage");
return;
}
if (strcmp(power_usage, "Min") == 0)
ucfg_mlme_get_bmps_min_listen_interval(hdd_ctx->psoc,
&listen_interval);
else if (strcmp(power_usage, "Max") == 0)
ucfg_mlme_get_bmps_max_listen_interval(hdd_ctx->psoc,
&listen_interval);
/*
* Based on Mode Set the LI
* Otherwise default LI value of 1 will
* be taken
*/
if (listen_interval) {
/*
* setcfg for listenInterval.
* Make sure CFG is updated because PE reads this
* from CFG at the time of assoc or reassoc
*/
ucfg_mlme_set_sap_listen_interval(hdd_ctx->psoc,
listen_interval);
}
}
/**
* hdd_update_mac_config() - update MAC address from cfg file
* @hdd_ctx: the pointer to hdd context
*
* It overwrites the MAC address if config file exist.
*
* Return: QDF_STATUS_SUCCESS if the MAC address is found from cfg file
* and overwritten, otherwise QDF_STATUS_E_INVAL
*/
QDF_STATUS hdd_update_mac_config(struct hdd_context *hdd_ctx)
{
int status, i = 0;
const struct firmware *fw = NULL;
char *line, *buffer = NULL;
char *temp = NULL;
char *name, *value;
int max_mac_addr = QDF_MAX_CONCURRENCY_PERSONA;
struct hdd_cfg_entry mac_table[QDF_MAX_CONCURRENCY_PERSONA];
tSirMacAddr custom_mac_addr;
QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
memset(mac_table, 0, sizeof(mac_table));
status = request_firmware(&fw, WLAN_MAC_FILE, hdd_ctx->parent_dev);
if (status) {
/*
* request_firmware "fails" if the file is not found, which is a
* valid setup for us, so log using debug instead of error
*/
hdd_debug("request_firmware failed; status:%d", status);
return QDF_STATUS_E_FAILURE;
}
if (!fw || !fw->data || !fw->size) {
hdd_alert("invalid firmware");
qdf_status = QDF_STATUS_E_INVAL;
goto config_exit;
}
hdd_debug("wlan_mac.bin size %zu", fw->size);
temp = qdf_mem_malloc(fw->size + 1);
if (!temp) {
hdd_err("fail to alloc memory");
qdf_status = QDF_STATUS_E_NOMEM;
goto config_exit;
}
buffer = temp;
qdf_mem_copy(buffer, fw->data, fw->size);
buffer[fw->size] = 0x0;
/* data format:
* Intf0MacAddress=00AA00BB00CC
* Intf1MacAddress=00AA00BB00CD
* END
*/
while (buffer) {
line = get_next_line(buffer);
buffer = i_trim(buffer);
if (strlen((char *)buffer) == 0 || *buffer == '#') {
buffer = line;
continue;
}
if (strncmp(buffer, "END", 3) == 0)
break;
name = buffer;
buffer = strnchr(buffer, strlen(buffer), '=');
if (buffer) {
*buffer++ = '\0';
i_trim(name);
if (strlen(name) != 0) {
buffer = i_trim(buffer);
if (strlen(buffer) == 12) {
value = buffer;
mac_table[i].name = name;
mac_table[i++].value = value;
if (i >= QDF_MAX_CONCURRENCY_PERSONA)
break;
}
}
}
buffer = line;
}
if (i != 0 && i <= QDF_MAX_CONCURRENCY_PERSONA) {
hdd_debug("%d Mac addresses provided", i);
} else {
hdd_err("invalid number of Mac address provided, nMac = %d", i);
qdf_status = QDF_STATUS_E_INVAL;
goto config_exit;
}
qdf_status = update_mac_from_string(hdd_ctx, &mac_table[0], i);
if (QDF_IS_STATUS_ERROR(qdf_status)) {
hdd_err("Invalid MAC addresses provided");
goto config_exit;
}
hdd_ctx->num_provisioned_addr = i;
hdd_debug("Populating remaining %d Mac addresses",
max_mac_addr - i);
hdd_populate_random_mac_addr(hdd_ctx, max_mac_addr - i);
if (hdd_ctx->num_provisioned_addr)
qdf_mem_copy(&custom_mac_addr,
&hdd_ctx->provisioned_mac_addr[0].bytes[0],
sizeof(tSirMacAddr));
else
qdf_mem_copy(&custom_mac_addr,
&hdd_ctx->derived_mac_addr[0].bytes[0],
sizeof(tSirMacAddr));
sme_set_custom_mac_addr(custom_mac_addr);
config_exit:
qdf_mem_free(temp);
release_firmware(fw);
return qdf_status;
}
/**
* hdd_disable_runtime_pm() - Override to disable runtime_pm.
* @cfg_ini: Handle to struct hdd_config
*
* Return: None
*/
#ifdef FEATURE_RUNTIME_PM
static void hdd_disable_runtime_pm(struct hdd_config *cfg_ini)
{
cfg_ini->runtime_pm = 0;
}
#else
static void hdd_disable_runtime_pm(struct hdd_config *cfg_ini)
{
}
#endif
/**
* hdd_disable_auto_shutdown() - Override to disable auto_shutdown.
* @cfg_ini: Handle to struct hdd_config
*
* Return: None
*/
#ifdef FEATURE_WLAN_AUTO_SHUTDOWN
static void hdd_disable_auto_shutdown(struct hdd_config *cfg_ini)
{
cfg_ini->wlan_auto_shutdown = 0;
}
#else
static void hdd_disable_auto_shutdown(struct hdd_config *cfg_ini)
{
}
#endif
void hdd_override_all_ps(struct hdd_context *hdd_ctx)
{
struct hdd_config *cfg_ini = hdd_ctx->config;
ucfg_mlme_override_bmps_imps(hdd_ctx->psoc);
hdd_disable_runtime_pm(cfg_ini);
hdd_disable_auto_shutdown(cfg_ini);
}
/**
* hdd_cfg_xlate_to_csr_phy_mode() - convert PHY mode
* @dot11Mode: the mode to convert
*
* Convert the configuration PHY mode to CSR PHY mode
*
* Return: the CSR phy mode value
*/
eCsrPhyMode hdd_cfg_xlate_to_csr_phy_mode(enum hdd_dot11_mode dot11Mode)
{
if (cds_is_sub_20_mhz_enabled())
return eCSR_DOT11_MODE_abg;
switch (dot11Mode) {
case (eHDD_DOT11_MODE_abg):
return eCSR_DOT11_MODE_abg;
case (eHDD_DOT11_MODE_11b):
return eCSR_DOT11_MODE_11b;
case (eHDD_DOT11_MODE_11g):
return eCSR_DOT11_MODE_11g;
default:
case (eHDD_DOT11_MODE_11n):
return eCSR_DOT11_MODE_11n;
case (eHDD_DOT11_MODE_11g_ONLY):
return eCSR_DOT11_MODE_11g_ONLY;
case (eHDD_DOT11_MODE_11n_ONLY):
return eCSR_DOT11_MODE_11n_ONLY;
case (eHDD_DOT11_MODE_11b_ONLY):
return eCSR_DOT11_MODE_11b_ONLY;
case (eHDD_DOT11_MODE_11ac_ONLY):
return eCSR_DOT11_MODE_11ac_ONLY;
case (eHDD_DOT11_MODE_11ac):
return eCSR_DOT11_MODE_11ac;
case (eHDD_DOT11_MODE_AUTO):
return eCSR_DOT11_MODE_AUTO;
case (eHDD_DOT11_MODE_11a):
return eCSR_DOT11_MODE_11a;
case (eHDD_DOT11_MODE_11ax_ONLY):
return eCSR_DOT11_MODE_11ax_ONLY;
case (eHDD_DOT11_MODE_11ax):
return eCSR_DOT11_MODE_11ax;
}
}
/**
* hdd_set_idle_ps_config() - set idle power save configuration
* @hdd_ctx: the pointer to hdd context
* @val: the value to configure
*
* Return: QDF_STATUS_SUCCESS if command set correctly,
* otherwise the QDF_STATUS return from SME layer
*/
QDF_STATUS hdd_set_idle_ps_config(struct hdd_context *hdd_ctx, bool val)
{
QDF_STATUS status;
hdd_debug("Enter Val %d", val);
if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
hdd_debug("Skipping powersave in FTM");
return QDF_STATUS_SUCCESS;
}
if (hdd_ctx->imps_enabled == val) {
hdd_info("Already in the requested power state:%d", val);
return QDF_STATUS_SUCCESS;
}
status = sme_set_idle_powersave_config(val);
if (QDF_STATUS_SUCCESS != status) {
hdd_err("Fail to Set Idle PS Config val %d", val);
return status;
}
hdd_ctx->imps_enabled = val;
return status;
}
/**
* hdd_set_fine_time_meas_cap() - set fine timing measurement capability
* @hdd_ctx: HDD context
*
* This function is used to pass fine timing measurement capability coming
* from INI to SME. This function make sure that configure INI is supported
* by the device. Use bit mask to mask out the unsupported capabilities.
*
* Return: None
*/
static void hdd_set_fine_time_meas_cap(struct hdd_context *hdd_ctx)
{
uint32_t capability = 0;
ucfg_mlme_get_fine_time_meas_cap(hdd_ctx->psoc, &capability);
ucfg_wifi_pos_set_ftm_cap(hdd_ctx->psoc, capability);
hdd_debug("fine time meas capability - Enabled: %04x", capability);
}
/**
* hdd_convert_string_to_u8_array() - used to convert string into u8 array
* @str: String to be converted
* @hex_array: Array where converted value is stored
* @len: Length of the populated array
* @array_max_len: Maximum length of the array
* @to_hex: true, if conversion required for hex string
*
* This API is called to convert string (each byte separated by
* a comma) into an u8 array
*
* Return: QDF_STATUS
*/
static QDF_STATUS hdd_convert_string_to_array(char *str, uint8_t *array,
uint8_t *len, uint16_t array_max_len, bool to_hex)
{
char *format, *s = str;
if (!str || !array || !len)
return QDF_STATUS_E_INVAL;
format = (to_hex) ? "%02x" : "%d";
*len = 0;
while ((s) && (*len < array_max_len)) {
int val;
/* Increment length only if sscanf successfully extracted
* one element. Any other return value means error.
* Ignore it.
*/
if (sscanf(s, format, &val) == 1) {
array[*len] = (uint8_t) val;
*len += 1;
}
s = strpbrk(s, ",");
if (s)
s++;
}
return QDF_STATUS_SUCCESS;
}
QDF_STATUS hdd_string_to_u8_array(char *str, uint8_t *array,
uint8_t *len, uint16_t array_max_len)
{
return hdd_convert_string_to_array(str, array, len,
array_max_len, false);
}
/**
* hdd_hex_string_to_u16_array() - convert a hex string to a uint16 array
* @str: input string
* @int_array: pointer to input array of type uint16
* @len: pointer to number of elements which the function adds to the array
* @int_array_max_len: maximum number of elements in input uint16 array
*
* This function is used to convert a space separated hex string to an array of
* uint16_t. For example, an input string str = "a b c d" would be converted to
* a unint16 array, int_array = {0xa, 0xb, 0xc, 0xd}, *len = 4.
* This assumes that input value int_array_max_len >= 4.
*
* Return: QDF_STATUS_SUCCESS - if the conversion is successful
* non zero value - if the conversion is a failure
*/
QDF_STATUS hdd_hex_string_to_u16_array(char *str,
uint16_t *int_array, uint8_t *len, uint8_t int_array_max_len)
{
char *s = str;
uint32_t val = 0;
if (!str || !int_array || !len)
return QDF_STATUS_E_INVAL;
hdd_debug("str %pK intArray %pK intArrayMaxLen %d",
s, int_array, int_array_max_len);
*len = 0;
while ((s) && (*len < int_array_max_len)) {
/*
* Increment length only if sscanf successfully extracted one
* element. Any other return value means error. Ignore it.
*/
if (sscanf(s, "%x", &val) == 1) {
int_array[*len] = (uint16_t) val;
hdd_debug("s %pK val %x intArray[%d]=0x%x",
s, val, *len, int_array[*len]);
*len += 1;
}
s = strpbrk(s, " ");
if (s)
s++;
}
return QDF_STATUS_SUCCESS;
}
/**
* hdd_update_config_cfg() - API to update INI setting based on hw/fw caps
* @hdd_ctx: pointer to hdd_ctx
*
* This API reads the cfg file which is updated with hardware/firmware
* capabilities and intersect it with INI setting provided by user. After
* taking intersection it adjust cfg it self. For example, if user has enabled
* RX LDPC through INI but hardware/firmware doesn't support it then disable
* it in CFG file here.
*
* Return: true or false based on outcome.
*/
bool hdd_update_config_cfg(struct hdd_context *hdd_ctx)
{
bool status = true;
/*
* During the initialization both 2G and 5G capabilities should be same.
* So read 5G HT capablity and update 2G and 5G capablities.
*/
if (0 != hdd_update_he_cap_in_cfg(hdd_ctx)) {
status = false;
hdd_err("Couldn't set HE CAP in cfg");
}
return status;
}
/**
* hdd_set_policy_mgr_user_cfg() -initializes the policy manager
* configuration parameters
*
* @hdd_ctx: the pointer to hdd context
*
* Return: QDF_STATUS_SUCCESS if configuration is correctly applied,
* otherwise the appropriate QDF_STATUS would be returned
*/
QDF_STATUS hdd_set_policy_mgr_user_cfg(struct hdd_context *hdd_ctx)
{
QDF_STATUS status;
struct policy_mgr_user_cfg *user_cfg;
user_cfg = qdf_mem_malloc(sizeof(*user_cfg));
if (!user_cfg) {
hdd_err("unable to allocate user_cfg");
return QDF_STATUS_E_NOMEM;
}
status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc,
&user_cfg->enable2x2);
if (!QDF_IS_STATUS_SUCCESS(status))
hdd_err("unable to get vht_enable2x2");
user_cfg->sub_20_mhz_enabled = cds_is_sub_20_mhz_enabled();
status = policy_mgr_set_user_cfg(hdd_ctx->psoc, user_cfg);
qdf_mem_free(user_cfg);
return status;
}
eCsrRoamWmmUserModeType hdd_to_csr_wmm_mode(uint8_t mode)
{
switch (mode) {
case HDD_WMM_USER_MODE_QBSS_ONLY:
return eCsrRoamWmmQbssOnly;
case HDD_WMM_USER_MODE_NO_QOS:
return eCsrRoamWmmNoQos;
case HDD_WMM_USER_MODE_AUTO:
default:
return eCsrRoamWmmAuto;
}
}
static QDF_STATUS
hdd_set_sme_cfgs_related_to_plcy_mgr(struct hdd_context *hdd_ctx,
struct sme_config_params *sme_cfg)
{
uint8_t mcc_to_scc_switch = 0, is_force_1x1 = 0, allow_diff_bi = 0;
uint8_t conc_rule1 = 0, conc_rule2 = 0, sta_cxn_5g = 0;
if (QDF_STATUS_SUCCESS !=
ucfg_policy_mgr_get_mcc_scc_switch(hdd_ctx->psoc,
&mcc_to_scc_switch)) {
hdd_err("can't get mcc to scc switch");
return QDF_STATUS_E_FAILURE;
}
sme_cfg->csr_config.cc_switch_mode = mcc_to_scc_switch;
if (QDF_STATUS_SUCCESS !=
ucfg_policy_mgr_get_conc_rule1(hdd_ctx->psoc,
&conc_rule1)) {
hdd_err("can't get conc rule1");
return QDF_STATUS_E_FAILURE;
}
sme_cfg->csr_config.conc_custom_rule1 = conc_rule1;
if (QDF_STATUS_SUCCESS !=
ucfg_policy_mgr_get_conc_rule2(hdd_ctx->psoc,
&conc_rule2)) {
hdd_err("can't get conc rule2");
return QDF_STATUS_E_FAILURE;
}
sme_cfg->csr_config.conc_custom_rule2 = conc_rule2;
if (QDF_STATUS_SUCCESS !=
ucfg_policy_mgr_get_sta_cxn_5g_band(hdd_ctx->psoc,
&sta_cxn_5g)) {
hdd_err("can't get conc rule2");
return QDF_STATUS_E_FAILURE;
}
sme_cfg->csr_config.is_sta_connection_in_5gz_enabled = sta_cxn_5g;
if (QDF_STATUS_SUCCESS !=
ucfg_policy_mgr_get_force_1x1(hdd_ctx->psoc,
&is_force_1x1)) {
hdd_err("can't get force 1x1 flag");
return QDF_STATUS_E_FAILURE;
}
sme_cfg->csr_config.is_force_1x1 = is_force_1x1;
if (QDF_STATUS_SUCCESS !=
ucfg_policy_mgr_get_allow_mcc_go_diff_bi(hdd_ctx->psoc,
&allow_diff_bi)) {
hdd_err("can't get allow mcc go diff BI flag");
return QDF_STATUS_E_FAILURE;
}
sme_cfg->csr_config.fAllowMCCGODiffBI = allow_diff_bi;
return QDF_STATUS_SUCCESS;
}
#ifdef FEATURE_AP_MCC_CH_AVOIDANCE
static QDF_STATUS hdd_set_sap_mcc_chnl_avoid(struct sme_config_params *sme_cfg,
uint8_t val)
{
sme_cfg->csr_config.sap_channel_avoidance = val;
return QDF_STATUS_SUCCESS;
}
#else
static QDF_STATUS hdd_set_sap_mcc_chnl_avoid(struct sme_config_params *sme_cfg,
uint8_t val)
{
return QDF_STATUS_SUCCESS;
}
#endif
static
QDF_STATUS hdd_set_sme_cfgs_related_to_mlme(struct hdd_context *hdd_ctx,
struct sme_config_params *sme_cfg)
{
QDF_STATUS status;
uint8_t wmm_mode = 0, enable_mcc = 0, sap_mcc_avoid = 0;
uint8_t mcc_rts_cts = 0, mcc_bcast_prob_rsp = 0;
uint32_t mcast_mcc_rest_time = 0;
bool b80211e_enabled = 0;
status = ucfg_mlme_get_80211e_is_enabled(hdd_ctx->psoc,
&b80211e_enabled);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("Get b80211e_enabled failed");
return QDF_STATUS_E_FAILURE;
}
sme_cfg->csr_config.Is11eSupportEnabled = b80211e_enabled;
status = ucfg_mlme_get_wmm_mode(hdd_ctx->psoc, &wmm_mode);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("Get wmm_mode failed");
return QDF_STATUS_E_FAILURE;
}
sme_cfg->csr_config.WMMSupportMode = hdd_to_csr_wmm_mode(wmm_mode);
hdd_debug("wmm_mode=%d 802_11e_enabled=%d", wmm_mode, b80211e_enabled);
status = ucfg_mlme_get_mcc_feature(hdd_ctx->psoc, &enable_mcc);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("ucfg_mlme_get_mcc_feature fail, use def");
return QDF_STATUS_E_FAILURE;
}
sme_cfg->csr_config.fEnableMCCMode = enable_mcc;
status = ucfg_mlme_get_mcc_rts_cts_prot(hdd_ctx->psoc, &mcc_rts_cts);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("ucfg_mlme_get_mcc_rts_cts_prot fail, use def");
return QDF_STATUS_E_FAILURE;
}
sme_cfg->csr_config.mcc_rts_cts_prot_enable = mcc_rts_cts;
status = ucfg_mlme_get_mcc_bcast_prob_resp(hdd_ctx->psoc,
&mcc_bcast_prob_rsp);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("ucfg_mlme_get_mcc_bcast_prob_resp fail, use def");
return QDF_STATUS_E_FAILURE;
}
sme_cfg->csr_config.mcc_bcast_prob_resp_enable = mcc_bcast_prob_rsp;
status = ucfg_mlme_get_sta_miracast_mcc_rest_time(hdd_ctx->psoc,
&mcast_mcc_rest_time);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("ucfg_mlme_get_sta_miracast_mcc_rest_time, use def");
return QDF_STATUS_E_FAILURE;
}
sme_cfg->csr_config.f_sta_miracast_mcc_rest_time_val =
mcast_mcc_rest_time;
status = ucfg_mlme_get_sap_mcc_chnl_avoid(hdd_ctx->psoc,
&sap_mcc_avoid);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("ucfg_mlme_get_sap_mcc_chnl_avoid, use def");
return QDF_STATUS_E_FAILURE;
}
status = hdd_set_sap_mcc_chnl_avoid(sme_cfg, sap_mcc_avoid);
return status;
}
/**
* hdd_set_sme_config() -initializes the sme configuration parameters
*
* @hdd_ctx: the pointer to hdd context
*
* Return: QDF_STATUS_SUCCESS if configuration is correctly applied,
* otherwise the appropriate QDF_STATUS would be returned
*/
QDF_STATUS hdd_set_sme_config(struct hdd_context *hdd_ctx)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
struct sme_config_params *sme_config;
mac_handle_t mac_handle = hdd_ctx->mac_handle;
bool roam_scan_enabled;
bool enable_dfs_scan = true;
uint32_t channel_bonding_mode;
#ifdef FEATURE_WLAN_ESE
bool ese_enabled;
#endif
struct wlan_mlme_ibss_cfg ibss_cfg = {0};
struct hdd_config *config = hdd_ctx->config;
if (QDF_IS_STATUS_ERROR(ucfg_mlme_get_ibss_cfg(
hdd_ctx->psoc, &ibss_cfg))) {
hdd_err("Unable to get IBSS config params");
return QDF_STATUS_E_FAILURE;
}
sme_config = qdf_mem_malloc(sizeof(*sme_config));
if (!sme_config) {
hdd_err("unable to allocate sme_config");
return QDF_STATUS_E_NOMEM;
}
/* Config params obtained from the registry
* To Do: set regulatory information here
*/
sme_config->csr_config.phyMode =
hdd_cfg_xlate_to_csr_phy_mode(config->dot11Mode);
if (config->dot11Mode == eHDD_DOT11_MODE_abg ||
config->dot11Mode == eHDD_DOT11_MODE_11b ||
config->dot11Mode == eHDD_DOT11_MODE_11g ||
config->dot11Mode == eHDD_DOT11_MODE_11b_ONLY ||
config->dot11Mode == eHDD_DOT11_MODE_11g_ONLY) {
sme_config->csr_config.channelBondingMode24GHz = 0;
sme_config->csr_config.channelBondingMode5GHz = 0;
} else {
ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc,
&channel_bonding_mode);
sme_config->csr_config.channelBondingMode24GHz =
channel_bonding_mode;
ucfg_mlme_get_channel_bonding_5ghz(hdd_ctx->psoc,
&channel_bonding_mode);
sme_config->csr_config.channelBondingMode5GHz =
channel_bonding_mode;
}
/* Remaining config params not obtained from registry
* On RF EVB beacon using channel 1.
*/
/* This param cannot be configured from INI */
sme_config->csr_config.send_smps_action = true;
sme_config->csr_config.ad_hoc_ch_freq_5g = ibss_cfg.adhoc_ch_5g;
sme_config->csr_config.ad_hoc_ch_freq_2g = ibss_cfg.adhoc_ch_2g;
sme_config->csr_config.ProprietaryRatesEnabled = 0;
sme_config->csr_config.HeartbeatThresh50 = 40;
ucfg_scan_cfg_get_dfs_chan_scan_allowed(hdd_ctx->psoc,
&enable_dfs_scan);
sme_config->csr_config.fEnableDFSChnlScan = enable_dfs_scan;
sme_config->csr_config.Csr11dinfo.Channels.numChannels = 0;
hdd_set_power_save_offload_config(hdd_ctx);
#ifdef FEATURE_WLAN_ESE
ucfg_mlme_is_ese_enabled(hdd_ctx->psoc, &ese_enabled);
if (ese_enabled)
ucfg_mlme_set_fast_transition_enabled(hdd_ctx->psoc, true);
#endif
ucfg_mlme_is_roam_scan_offload_enabled(hdd_ctx->psoc,
&roam_scan_enabled);
if (!roam_scan_enabled) {
/* Disable roaming in concurrency if roam scan
* offload is disabled
*/
ucfg_mlme_set_fast_roam_in_concurrency_enabled(
hdd_ctx->psoc, false);
}
sme_config->csr_config.isCoalesingInIBSSAllowed =
ibss_cfg.coalesing_enable;
/* Update maximum interfaces information */
sme_config->csr_config.max_intf_count = hdd_ctx->max_intf_count;
hdd_set_fine_time_meas_cap(hdd_ctx);
cds_set_multicast_logging(hdd_ctx->config->multicast_host_fw_msgs);
sme_config->csr_config.sta_roam_policy_params.dfs_mode =
CSR_STA_ROAM_POLICY_DFS_ENABLED;
sme_config->csr_config.sta_roam_policy_params.skip_unsafe_channels = 0;
status = hdd_set_sme_cfgs_related_to_mlme(hdd_ctx, sme_config);
if (!QDF_IS_STATUS_SUCCESS(status))
hdd_err("hdd_set_sme_cfgs_related_to_mlme() fail: %d", status);
status = hdd_set_sme_cfgs_related_to_plcy_mgr(hdd_ctx, sme_config);
if (!QDF_IS_STATUS_SUCCESS(status))
hdd_err("hdd_set_sme_cfgs_related_to_plcy_mgr fail: %d",
status);
hdd_debug("dot11Mode=%d", config->dot11Mode);
status = sme_update_config(mac_handle, sme_config);
if (!QDF_IS_STATUS_SUCCESS(status))
hdd_err("sme_update_config() failure: %d", status);
qdf_mem_free(sme_config);
return status;
}
/**
* hdd_cfg_get_global_config() - get the configuration table
* @hdd_ctx: pointer to hdd context
* @buf: buffer to store the configuration
* @buflen: size of the buffer
*
* Return: none
*/
void hdd_cfg_get_global_config(struct hdd_context *hdd_ctx, char *buf,
int buflen)
{
ucfg_cfg_store_print(hdd_ctx->psoc);
snprintf(buf, buflen,
"WLAN configuration written to debug log");
}
/**
* hdd_cfg_print_global_config() - print the configuration table
* @hdd_ctx: pointer to hdd context
*
* Return: none
*/
void hdd_cfg_print_global_config(struct hdd_context *hdd_ctx)
{
QDF_STATUS status;
status = ucfg_cfg_store_print(hdd_ctx->psoc);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("Failed to log cfg ini");
}
/**
* hdd_get_pmkid_modes() - returns PMKID mode bits
* @hdd_ctx: the pointer to hdd context
*
* Return: value of pmkid_modes
*/
void hdd_get_pmkid_modes(struct hdd_context *hdd_ctx,
struct pmkid_mode_bits *pmkid_modes)
{
uint32_t cur_pmkid_modes;
QDF_STATUS status;
status = ucfg_mlme_get_pmkid_modes(hdd_ctx->psoc, &cur_pmkid_modes);
if (status != QDF_STATUS_SUCCESS)
hdd_err("get pmkid modes fail");
pmkid_modes->fw_okc = (cur_pmkid_modes &
CFG_PMKID_MODES_OKC) ? 1 : 0;
pmkid_modes->fw_pmksa_cache = (cur_pmkid_modes &
CFG_PMKID_MODES_PMKSA_CACHING) ? 1 : 0;
}
static void
hdd_populate_vdev_nss(struct wlan_mlme_nss_chains *user_cfg,
uint8_t tx_nss,
uint8_t rx_nss,
enum nss_chains_band_info band)
{
user_cfg->rx_nss[band] = rx_nss;
user_cfg->tx_nss[band] = tx_nss;
}
static QDF_STATUS
hdd_set_nss_params(struct hdd_adapter *adapter,
uint8_t tx_nss,
uint8_t rx_nss)
{
enum nss_chains_band_info band;
struct wlan_mlme_nss_chains user_cfg;
mac_handle_t mac_handle;
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
qdf_mem_zero(&user_cfg, sizeof(user_cfg));
mac_handle = hdd_ctx->mac_handle;
if (!mac_handle) {
hdd_err("NULL MAC handle");
return QDF_STATUS_E_INVAL;
}
for (band = NSS_CHAINS_BAND_2GHZ; band < NSS_CHAINS_BAND_MAX; band++)
hdd_populate_vdev_nss(&user_cfg, tx_nss,
rx_nss, band);
if (QDF_IS_STATUS_ERROR(
sme_nss_chains_update(mac_handle,
&user_cfg,
adapter->vdev_id)))
return QDF_STATUS_E_FAILURE;
/* Check TDLS status and update antenna mode */
if ((adapter->device_mode == QDF_STA_MODE ||
adapter->device_mode == QDF_P2P_CLIENT_MODE) &&
policy_mgr_is_sta_active_connection_exists(hdd_ctx->psoc))
wlan_hdd_tdls_antenna_switch(hdd_ctx, adapter, rx_nss);
return QDF_STATUS_SUCCESS;
}
/**
* hdd_update_nss() - Update the number of spatial streams supported.
* Ensure that nss is either 1 or 2 before calling this.
*
* @adapter: the pointer to adapter
* @nss: the number of spatial streams to be updated
*
* This function is used to modify the number of spatial streams
* supported when not in connected state.
*
* Return: QDF_STATUS_SUCCESS if nss is correctly updated,
* otherwise QDF_STATUS_E_FAILURE would be returned
*/
QDF_STATUS hdd_update_nss(struct hdd_adapter *adapter, uint8_t nss)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint32_t rx_supp_data_rate, tx_supp_data_rate;
bool status = true;
QDF_STATUS qdf_status;
qdf_size_t val_len;
struct mlme_ht_capabilities_info ht_cap_info;
uint8_t mcs_set[SIZE_OF_SUPPORTED_MCS_SET] = {0};
uint8_t mcs_set_temp[SIZE_OF_SUPPORTED_MCS_SET];
uint8_t enable2x2;
mac_handle_t mac_handle;
bool bval = 0;
uint8_t tx_nss, rx_nss;
uint8_t band, max_supp_nss;
if ((nss == 2) && (hdd_ctx->num_rf_chains != 2)) {
hdd_err("No support for 2 spatial streams");
return QDF_STATUS_E_INVAL;
}
if (nss > MAX_VDEV_NSS) {
hdd_debug("Cannot support %d nss streams", nss);
return QDF_STATUS_E_INVAL;
}
qdf_status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
hdd_err("unable to get vht_enable2x2");
return QDF_STATUS_E_FAILURE;
}
mac_handle = hdd_ctx->mac_handle;
if (!mac_handle) {
hdd_err("NULL MAC handle");
return QDF_STATUS_E_INVAL;
}
max_supp_nss = MAX_VDEV_NSS;
/* Till now we dont have support for different rx, tx nss values */
tx_nss = nss;
rx_nss = nss;
/*
* If FW is supporting the dynamic nss update, this command is meant to
* be per vdev, so update only the ini params of that particular vdev
* and not the global param enable2x2
*/
if (hdd_ctx->dynamic_nss_chains_support) {
if (hdd_is_vdev_in_conn_state(adapter))
return hdd_set_nss_params(adapter, tx_nss, rx_nss);
hdd_debug("Vdev %d in disconnect state, changing ini nss params",
adapter->vdev_id);
if (!bval) {
hdd_err("Nss in 1x1, no change required, 2x2 mode disabled");
return QDF_STATUS_E_FAILURE;
}
for (band = NSS_CHAINS_BAND_2GHZ; band < NSS_CHAINS_BAND_MAX;
band++) {
/* This API will change the global ini in mlme cfg */
sme_update_nss_in_mlme_cfg(mac_handle, rx_nss, tx_nss,
adapter->device_mode, band);
/*
* This API will change the vdev nss params in mac
* context
*/
sme_update_vdev_type_nss(mac_handle, max_supp_nss,
band);
}
/*
* This API will change the ini and dynamic nss params in
* mlme vdev priv obj.
*/
hdd_store_nss_chains_cfg_in_vdev(adapter);
return QDF_STATUS_SUCCESS;
}
/*
* The code below is executed only when fw doesn't support dynamic
* update of nss and chains per vdev feature, for the upcoming
* connection
*/
enable2x2 = (nss == 1) ? 0 : 1;
if (bval == enable2x2) {
hdd_debug("NSS same as requested");
return QDF_STATUS_SUCCESS;
}
if (sme_is_any_session_in_connected_state(mac_handle)) {
hdd_err("Connected sessions present, Do not change NSS");
return QDF_STATUS_E_INVAL;
}
qdf_status = ucfg_mlme_set_vht_enable2x2(hdd_ctx->psoc, enable2x2);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
hdd_err("Failed to set vht_enable2x2");
return QDF_STATUS_E_FAILURE;
}
if (!enable2x2) {
/* 1x1 */
rx_supp_data_rate = VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1;
tx_supp_data_rate = VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1;
} else {
/* 2x2 */
rx_supp_data_rate = VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_2_2;
tx_supp_data_rate = VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_2_2;
}
/* Update Rx Highest Long GI data Rate */
qdf_status =
ucfg_mlme_cfg_set_vht_rx_supp_data_rate(hdd_ctx->psoc,
rx_supp_data_rate);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
hdd_err("Failed to set rx_supp_data_rate");
status = false;
}
/* Update Tx Highest Long GI data Rate */
qdf_status =
ucfg_mlme_cfg_set_vht_tx_supp_data_rate(hdd_ctx->psoc,
tx_supp_data_rate);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
hdd_err("Failed to set tx_supp_data_rate");
status = false;
}
qdf_status = ucfg_mlme_get_ht_cap_info(hdd_ctx->psoc, &ht_cap_info);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
hdd_err("Failed to get HT Cap info");
goto skip_ht_cap_update;
}
if (!(hdd_ctx->ht_tx_stbc_supported && enable2x2)) {
ht_cap_info.tx_stbc = 0;
} else {
qdf_status =
ucfg_mlme_cfg_get_vht_tx_stbc(hdd_ctx->psoc, &bval);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
hdd_err("Failed to get vht_tx_stbc");
ht_cap_info.tx_stbc = bval;
}
}
qdf_status = ucfg_mlme_set_ht_cap_info(hdd_ctx->psoc, ht_cap_info);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
hdd_err("Could not set the HT_CAP_INFO");
}
skip_ht_cap_update:
qdf_status = ucfg_mlme_update_nss_vht_cap(hdd_ctx->psoc);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
hdd_err("Failed to set update_nss_vht_cap");
status = false;
}
#define WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES 0xff
val_len = SIZE_OF_SUPPORTED_MCS_SET;
qdf_status = ucfg_mlme_get_supported_mcs_set(hdd_ctx->psoc,
mcs_set_temp,
&val_len);
if (QDF_IS_STATUS_SUCCESS(qdf_status)) {
mcs_set[0] = mcs_set_temp[0];
if (enable2x2)
for (val_len = 0; val_len < nss; val_len++)
mcs_set[val_len] =
WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES;
if (ucfg_mlme_set_supported_mcs_set(
hdd_ctx->psoc, mcs_set,
(qdf_size_t)SIZE_OF_SUPPORTED_MCS_SET) ==
QDF_STATUS_E_FAILURE) {
status = false;
hdd_err("Could not pass on MCS SET to CFG");
}
} else {
status = false;
hdd_err("Could not get MCS SET from CFG");
}
sme_update_he_cap_nss(mac_handle, adapter->vdev_id, nss);
#undef WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES
if (QDF_STATUS_SUCCESS != sme_update_nss(mac_handle, nss))
status = false;
hdd_set_policy_mgr_user_cfg(hdd_ctx);
return (status == false) ? QDF_STATUS_E_FAILURE : QDF_STATUS_SUCCESS;
}