qcacld-3.0: Move sar related functions to wlan_hdd_sar_limits.c

Move sar related functions from wlan_hdd_cfg80211.c to
wlan_hdd_sar_limits.c.

Change-Id: I65f4e33469d5fc2dd09ae37fd0138b3d3d2c5f9d
CRs-Fixed: 2638028
This commit is contained in:
Srinivas Girigowda 2020-03-09 14:02:02 -07:00 • committed by nshrivas
commit c7d8c585ee
4 changed files with 571 additions and 555 deletions

View file

@ -11999,517 +11999,6 @@ static int wlan_hdd_cfg80211_setband(struct wiphy *wiphy,
return errno;
}
/**
* wlan_hdd_cfg80211_sar_convert_limit_set() - Convert limit set value
* @nl80211_value: Vendor command attribute value
* @wmi_value: Pointer to return converted WMI return value
*
* Convert NL80211 vendor command value for SAR limit set to WMI value
* Return: 0 on success, -1 on invalid value
*/
static int wlan_hdd_cfg80211_sar_convert_limit_set(u32 nl80211_value,
u32 *wmi_value)
{
int ret = 0;
switch (nl80211_value) {
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE:
*wmi_value = WMI_SAR_FEATURE_OFF;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0:
*wmi_value = WMI_SAR_FEATURE_ON_SET_0;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1:
*wmi_value = WMI_SAR_FEATURE_ON_SET_1;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2:
*wmi_value = WMI_SAR_FEATURE_ON_SET_2;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3:
*wmi_value = WMI_SAR_FEATURE_ON_SET_3;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4:
*wmi_value = WMI_SAR_FEATURE_ON_SET_4;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER:
*wmi_value = WMI_SAR_FEATURE_ON_USER_DEFINED;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0:
*wmi_value = WMI_SAR_FEATURE_ON_SAR_V2_0;
break;
default:
ret = -1;
}
return ret;
}
#ifdef WLAN_FEATURE_SARV1_TO_SARV2
/**
* hdd_convert_sarv1_to_sarv2() - convert SAR V1 BDF reference to SAR V2
* @hdd_ctx: The HDD global context
* @tb: The parsed array of netlink attributes
* @sar_limit_cmd: The WMI command to be filled
*
* This feature/function is designed to solve the following problem:
* 1) Userspace application was written to use SARv1 BDF entries
* 2) Product is configured with SAR V2 BDF entries
*
* So if this feature is enabled, and if the firmware is configured
* with SAR V2 support, and if the incoming request is to enable a SAR
* V1 BDF entry, then the WMI command is generated to actually
* configure a SAR V2 BDF entry.
*
* Return: true if conversion was performed and @sar_limit_cmd is
* ready to be sent to firmware. Otherwise false in which case the
* normal parsing logic should be applied.
*/
static bool
hdd_convert_sarv1_to_sarv2(struct hdd_context *hdd_ctx,
struct nlattr *tb[],
struct sar_limit_cmd_params *sar_limit_cmd)
{
struct nlattr *attr;
uint32_t bdf_index, set;
struct sar_limit_cmd_row *row;
hdd_enter();
if (hdd_ctx->sar_version != SAR_VERSION_2) {
hdd_debug("SAR version: %d", hdd_ctx->sar_version);
return false;
}
attr = tb[QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE];
if (!attr)
return false;
bdf_index = nla_get_u32(attr);
if ((bdf_index >= QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0) &&
(bdf_index <= QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4)) {
set = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0;
} else if (bdf_index == QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE) {
set = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE;
bdf_index = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0;
} else {
return false;
}
/* Need two rows to hold the per-chain V2 power index
* To disable SARv2 limit, send chain, num_limits_row and
* power limit set to 0 (except power index 0xff)
*/
row = qdf_mem_malloc(2 * sizeof(*row));
if (!row)
return false;
if (wlan_hdd_cfg80211_sar_convert_limit_set(
set, &sar_limit_cmd->sar_enable)) {
hdd_err("Failed to convert SAR limit to WMI value");
return false;
}
sar_limit_cmd->commit_limits = 1;
sar_limit_cmd->num_limit_rows = 2;
sar_limit_cmd->sar_limit_row_list = row;
row[0].limit_value = bdf_index;
row[1].limit_value = row[0].limit_value;
row[0].chain_id = 0;
row[1].chain_id = 1;
row[0].validity_bitmap = WMI_SAR_CHAIN_ID_VALID_MASK;
row[1].validity_bitmap = WMI_SAR_CHAIN_ID_VALID_MASK;
hdd_exit();
return true;
}
#else /* WLAN_FEATURE_SARV1_TO_SARV2 */
static bool
hdd_convert_sarv1_to_sarv2(struct hdd_context *hdd_ctx,
struct nlattr *tb[],
struct sar_limit_cmd_params *sar_limit_cmd)
{
return false;
}
#endif /* WLAN_FEATURE_SARV1_TO_SARV2 */
/**
* wlan_hdd_cfg80211_sar_convert_band() - Convert WLAN band value
* @nl80211_value: Vendor command attribute value
* @wmi_value: Pointer to return converted WMI return value
*
* Convert NL80211 vendor command value for SAR BAND to WMI value
* Return: 0 on success, -1 on invalid value
*/
static int wlan_hdd_cfg80211_sar_convert_band(u32 nl80211_value, u32 *wmi_value)
{
int ret = 0;
switch (nl80211_value) {
case HDD_NL80211_BAND_2GHZ:
*wmi_value = WMI_SAR_2G_ID;
break;
case HDD_NL80211_BAND_5GHZ:
*wmi_value = WMI_SAR_5G_ID;
break;
default:
ret = -1;
}
return ret;
}
/**
* wlan_hdd_cfg80211_sar_convert_modulation() - Convert WLAN modulation value
* @nl80211_value: Vendor command attribute value
* @wmi_value: Pointer to return converted WMI return value
*
* Convert NL80211 vendor command value for SAR Modulation to WMI value
* Return: 0 on success, -1 on invalid value
*/
static int wlan_hdd_cfg80211_sar_convert_modulation(u32 nl80211_value,
u32 *wmi_value)
{
int ret = 0;
switch (nl80211_value) {
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK:
*wmi_value = WMI_SAR_MOD_CCK;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM:
*wmi_value = WMI_SAR_MOD_OFDM;
break;
default:
ret = -1;
}
return ret;
}
void hdd_store_sar_config(struct hdd_context *hdd_ctx,
struct sar_limit_cmd_params *sar_limit_cmd)
{
/* Free the previously stored sar_limit_cmd */
wlan_hdd_free_sar_config(hdd_ctx);
hdd_ctx->sar_cmd_params = sar_limit_cmd;
}
void wlan_hdd_free_sar_config(struct hdd_context *hdd_ctx)
{
struct sar_limit_cmd_params *sar_limit_cmd;
if (!hdd_ctx->sar_cmd_params)
return;
sar_limit_cmd = hdd_ctx->sar_cmd_params;
hdd_ctx->sar_cmd_params = NULL;
qdf_mem_free(sar_limit_cmd->sar_limit_row_list);
qdf_mem_free(sar_limit_cmd);
}
#define SAR_LIMITS_SAR_ENABLE QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE
#define SAR_LIMITS_NUM_SPECS QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS
#define SAR_LIMITS_SPEC QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC
#define SAR_LIMITS_SPEC_BAND QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND
#define SAR_LIMITS_SPEC_CHAIN QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN
#define SAR_LIMITS_SPEC_MODULATION \
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION
#define SAR_LIMITS_SPEC_POWER_LIMIT \
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT
#define SAR_LIMITS_SPEC_POWER_LIMIT_INDEX \
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX
#define SAR_LIMITS_MAX QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX
static const struct nla_policy
sar_limits_policy[SAR_LIMITS_MAX + 1] = {
[SAR_LIMITS_SAR_ENABLE] = {.type = NLA_U32},
[SAR_LIMITS_NUM_SPECS] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_BAND] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_CHAIN] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_MODULATION] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_POWER_LIMIT] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_POWER_LIMIT_INDEX] = {.type = NLA_U32},
};
/**
* hdd_extract_sar_nested_attrs() - Extract nested SAR attribute
* @spec: nested nla attribue
* @row: output to hold extract nested attribute
*
* This function extracts nested SAR attribute one at a time which means
* for each nested attribute this has to be invoked from
* __wlan_hdd_set_sar_power_limits().
*
* Return: On success - 0
* On Failure - Negative value
*/
static int hdd_extract_sar_nested_attrs(struct nlattr *spec[],
struct sar_limit_cmd_row *row)
{
uint32_t limit;
uint32_t band;
uint32_t modulation;
int ret;
row->validity_bitmap = 0;
if (spec[SAR_LIMITS_SPEC_POWER_LIMIT]) {
limit = nla_get_u32(spec[SAR_LIMITS_SPEC_POWER_LIMIT]);
row->limit_value = limit;
} else if (spec[SAR_LIMITS_SPEC_POWER_LIMIT_INDEX]) {
limit = nla_get_u32(spec[SAR_LIMITS_SPEC_POWER_LIMIT_INDEX]);
row->limit_value = limit;
} else {
hdd_err("SAR Spec does not have power limit or index value");
return -EINVAL;
}
if (spec[SAR_LIMITS_SPEC_BAND]) {
band = nla_get_u32(spec[SAR_LIMITS_SPEC_BAND]);
ret = wlan_hdd_cfg80211_sar_convert_band(band, &row->band_id);
if (ret) {
hdd_err("Invalid SAR Band attr");
return ret;
}
row->validity_bitmap |= WMI_SAR_BAND_ID_VALID_MASK;
}
if (spec[SAR_LIMITS_SPEC_CHAIN]) {
row->chain_id = nla_get_u32(spec[SAR_LIMITS_SPEC_CHAIN]);
row->validity_bitmap |= WMI_SAR_CHAIN_ID_VALID_MASK;
}
if (spec[SAR_LIMITS_SPEC_MODULATION]) {
modulation = nla_get_u32(spec[SAR_LIMITS_SPEC_MODULATION]);
ret = wlan_hdd_cfg80211_sar_convert_modulation(modulation,
&row->mod_id);
if (ret) {
hdd_err("Invalid SAR Modulation attr");
return ret;
}
row->validity_bitmap |= WMI_SAR_MOD_ID_VALID_MASK;
}
return 0;
}
/**
* __wlan_hdd_set_sar_power_limits() - Set SAR power limits
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* This function is used to setup Specific Absorption Rate limit specs.
*
* Return: 0 on success, negative errno on failure
*/
static int __wlan_hdd_set_sar_power_limits(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *spec[SAR_LIMITS_MAX + 1];
struct nlattr *tb[SAR_LIMITS_MAX + 1];
struct nlattr *spec_list;
struct sar_limit_cmd_params *sar_limit_cmd;
int ret = -EINVAL, i = 0, rem = 0;
QDF_STATUS status;
uint32_t num_limit_rows = 0;
struct sar_limit_cmd_row *row;
uint32_t sar_enable;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
if (wlan_hdd_validate_context(hdd_ctx))
return -EINVAL;
if (wlan_cfg80211_nla_parse(tb, SAR_LIMITS_MAX, data, data_len,
sar_limits_policy)) {
hdd_err("Invalid SAR attributes");
return -EINVAL;
}
if (tb[SAR_LIMITS_SAR_ENABLE]) {
sar_enable = nla_get_u32(tb[SAR_LIMITS_SAR_ENABLE]);
if ((sar_enable >=
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0 &&
sar_enable <=
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4) &&
hdd_ctx->sar_version == SAR_VERSION_2 &&
!hdd_ctx->config->enable_sar_conversion) {
hdd_err("SARV1 to SARV2 is disabled from ini");
return -EINVAL;
} else if (sar_enable ==
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0 &&
hdd_ctx->sar_version == SAR_VERSION_1) {
hdd_err("FW expects SARV1 given command is SARV2");
return -EINVAL;
}
}
sar_limit_cmd = qdf_mem_malloc(sizeof(struct sar_limit_cmd_params));
if (!sar_limit_cmd)
return -ENOMEM;
wlan_hdd_sar_timers_reset(hdd_ctx);
/* is special SAR V1 => SAR V2 logic enabled and applicable? */
if (hdd_ctx->config->enable_sar_conversion &&
(hdd_convert_sarv1_to_sarv2(hdd_ctx, tb, sar_limit_cmd)))
goto send_sar_limits;
/* Vendor command manadates all SAR Specs in single call */
sar_limit_cmd->commit_limits = 1;
sar_limit_cmd->sar_enable = WMI_SAR_FEATURE_NO_CHANGE;
if (tb[SAR_LIMITS_SAR_ENABLE]) {
uint32_t *sar_ptr = &sar_limit_cmd->sar_enable;
sar_enable = nla_get_u32(tb[SAR_LIMITS_SAR_ENABLE]);
ret = wlan_hdd_cfg80211_sar_convert_limit_set(sar_enable,
sar_ptr);
if (ret) {
hdd_err("Invalid SAR Enable attr");
goto fail;
}
}
hdd_debug("attr sar sar_enable %d", sar_limit_cmd->sar_enable);
if (tb[SAR_LIMITS_NUM_SPECS]) {
num_limit_rows = nla_get_u32(tb[SAR_LIMITS_NUM_SPECS]);
hdd_debug("attr sar num_limit_rows %u", num_limit_rows);
}
if (num_limit_rows > MAX_SAR_LIMIT_ROWS_SUPPORTED) {
hdd_err("SAR Spec list exceed supported size");
goto fail;
}
if (num_limit_rows == 0)
goto send_sar_limits;
row = qdf_mem_malloc(sizeof(*row) * num_limit_rows);
if (!row) {
hdd_err("Failed to allocate memory for sar_limit_row_list");
goto fail;
}
sar_limit_cmd->num_limit_rows = num_limit_rows;
sar_limit_cmd->sar_limit_row_list = row;
if (!tb[SAR_LIMITS_SPEC]) {
hdd_err("Invalid SAR specification list");
goto fail;
}
nla_for_each_nested(spec_list, tb[SAR_LIMITS_SPEC], rem) {
if (i == num_limit_rows) {
hdd_warn("SAR Cmd has excess SPECs in list");
break;
}
if (wlan_cfg80211_nla_parse(spec,
SAR_LIMITS_MAX,
nla_data(spec_list),
nla_len(spec_list),
sar_limits_policy)) {
hdd_err("nla_parse failed for SAR Spec list");
goto fail;
}
ret = hdd_extract_sar_nested_attrs(spec, row);
if (ret) {
hdd_err("Failed to extract SAR nested attrs");
goto fail;
}
hdd_debug("Spec_ID: %d, Band: %d Chain: %d Mod: %d POW_Limit: %d Validity_Bitmap: %d",
i, row->band_id, row->chain_id, row->mod_id,
row->limit_value, row->validity_bitmap);
i++;
row++;
}
if (i < sar_limit_cmd->num_limit_rows) {
hdd_warn("SAR Cmd has less SPECs in list");
sar_limit_cmd->num_limit_rows = i;
}
send_sar_limits:
status = sme_set_sar_power_limits(hdd_ctx->mac_handle, sar_limit_cmd);
if (QDF_IS_STATUS_ERROR(status)) {
hdd_err("Failed to set sar power limits");
goto fail;
}
/* After SSR, the SAR configuration is lost. As SSR is hidden from
* userland, this command will not come from userspace after a SSR. To
* restore this configuration, save this in hdd context and restore
* after re-init.
*/
hdd_store_sar_config(hdd_ctx, sar_limit_cmd);
return 0;
fail:
if (sar_limit_cmd) {
qdf_mem_free(sar_limit_cmd->sar_limit_row_list);
qdf_mem_free(sar_limit_cmd);
}
return ret;
}
#undef SAR_LIMITS_SAR_ENABLE
#undef SAR_LIMITS_NUM_SPECS
#undef SAR_LIMITS_SPEC
#undef SAR_LIMITS_SPEC_BAND
#undef SAR_LIMITS_SPEC_CHAIN
#undef SAR_LIMITS_SPEC_MODULATION
#undef SAR_LIMITS_SPEC_POWER_LIMIT
#undef SAR_LIMITS_SPEC_POWER_LIMIT_INDEX
#undef SAR_LIMITS_MAX
/**
* wlan_hdd_cfg80211_set_sar_power_limits() - Set SAR power limits
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* Wrapper function of __wlan_hdd_cfg80211_set_sar_power_limits()
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_set_sar_power_limits(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_set_sar_power_limits(wiphy, wdev, data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
static const struct
nla_policy qca_wlan_vendor_attr[QCA_WLAN_VENDOR_ATTR_MAX+1] = {
[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY] = {.type = NLA_U32},
@ -14786,17 +14275,6 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = {
FEATURE_TDLS_VENDOR_COMMANDS
FEATURE_SAR_LIMITS_VENDOR_COMMANDS
BCN_RECV_FEATURE_VENDOR_COMMANDS
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_sar_power_limits,
vendor_command_policy(sar_limits_policy,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX)
},
FEATURE_VENDOR_SUBCMD_SET_TRACE_LEVEL
{

View file

@ -766,32 +766,6 @@ int wlan_hdd_send_mode_change_event(void);
int wlan_hdd_restore_channels(struct hdd_context *hdd_ctx,
bool notify_sap_event);
/**
* hdd_store_sar_config() - Store SAR config in HDD context
* @hdd_ctx: The HDD context
* @sar_limit_cmd: The sar_limit_cmd_params struct to save
*
* After SSR, the SAR configuration is lost. As SSR is hidden from
* userland, this command will not come from userspace after a SSR. To
* restore this configuration, save this in hdd context and restore
* after re-init.
*
* Return: None
*/
void hdd_store_sar_config(struct hdd_context *hdd_ctx,
struct sar_limit_cmd_params *sar_limit_cmd);
/**
* hdd_free_sar_config() - Free the resources allocated while storing SAR config
* @hdd_ctx: HDD context
*
* The driver stores the SAR config values in HDD context so that it can be
* restored in the case SSR is invoked. Free those resources.
*
* Return: None
*/
void wlan_hdd_free_sar_config(struct hdd_context *hdd_ctx);
/*
* wlan_hdd_send_sta_authorized_event: Function to send station authorized
* event to user space in case of SAP

View file

@ -305,6 +305,506 @@ cleanup:
return ret;
}
#define SAR_LIMITS_SAR_ENABLE QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE
#define SAR_LIMITS_NUM_SPECS QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS
#define SAR_LIMITS_SPEC QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC
#define SAR_LIMITS_SPEC_BAND QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND
#define SAR_LIMITS_SPEC_CHAIN QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN
#define SAR_LIMITS_SPEC_MODULATION \
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION
#define SAR_LIMITS_SPEC_POWER_LIMIT \
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT
#define SAR_LIMITS_SPEC_POWER_LIMIT_INDEX \
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX
#define SAR_LIMITS_MAX QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX
const struct nla_policy
wlan_hdd_sar_limits_policy[SAR_LIMITS_MAX + 1] = {
[SAR_LIMITS_SAR_ENABLE] = {.type = NLA_U32},
[SAR_LIMITS_NUM_SPECS] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_BAND] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_CHAIN] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_MODULATION] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_POWER_LIMIT] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_POWER_LIMIT_INDEX] = {.type = NLA_U32},
};
void hdd_store_sar_config(struct hdd_context *hdd_ctx,
struct sar_limit_cmd_params *sar_limit_cmd)
{
/* Free the previously stored sar_limit_cmd */
wlan_hdd_free_sar_config(hdd_ctx);
hdd_ctx->sar_cmd_params = sar_limit_cmd;
}
void wlan_hdd_free_sar_config(struct hdd_context *hdd_ctx)
{
struct sar_limit_cmd_params *sar_limit_cmd;
if (!hdd_ctx->sar_cmd_params)
return;
sar_limit_cmd = hdd_ctx->sar_cmd_params;
hdd_ctx->sar_cmd_params = NULL;
qdf_mem_free(sar_limit_cmd->sar_limit_row_list);
qdf_mem_free(sar_limit_cmd);
}
/**
* wlan_hdd_cfg80211_sar_convert_limit_set() - Convert limit set value
* @nl80211_value: Vendor command attribute value
* @wmi_value: Pointer to return converted WMI return value
*
* Convert NL80211 vendor command value for SAR limit set to WMI value
* Return: 0 on success, -1 on invalid value
*/
static int wlan_hdd_cfg80211_sar_convert_limit_set(u32 nl80211_value,
u32 *wmi_value)
{
int ret = 0;
switch (nl80211_value) {
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE:
*wmi_value = WMI_SAR_FEATURE_OFF;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0:
*wmi_value = WMI_SAR_FEATURE_ON_SET_0;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1:
*wmi_value = WMI_SAR_FEATURE_ON_SET_1;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2:
*wmi_value = WMI_SAR_FEATURE_ON_SET_2;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3:
*wmi_value = WMI_SAR_FEATURE_ON_SET_3;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4:
*wmi_value = WMI_SAR_FEATURE_ON_SET_4;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER:
*wmi_value = WMI_SAR_FEATURE_ON_USER_DEFINED;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0:
*wmi_value = WMI_SAR_FEATURE_ON_SAR_V2_0;
break;
default:
ret = -1;
}
return ret;
}
#ifdef WLAN_FEATURE_SARV1_TO_SARV2
/**
* hdd_convert_sarv1_to_sarv2() - convert SAR V1 BDF reference to SAR V2
* @hdd_ctx: The HDD global context
* @tb: The parsed array of netlink attributes
* @sar_limit_cmd: The WMI command to be filled
*
* This feature/function is designed to solve the following problem:
* 1) Userspace application was written to use SARv1 BDF entries
* 2) Product is configured with SAR V2 BDF entries
*
* So if this feature is enabled, and if the firmware is configured
* with SAR V2 support, and if the incoming request is to enable a SAR
* V1 BDF entry, then the WMI command is generated to actually
* configure a SAR V2 BDF entry.
*
* Return: true if conversion was performed and @sar_limit_cmd is
* ready to be sent to firmware. Otherwise false in which case the
* normal parsing logic should be applied.
*/
static bool
hdd_convert_sarv1_to_sarv2(struct hdd_context *hdd_ctx,
struct nlattr *tb[],
struct sar_limit_cmd_params *sar_limit_cmd)
{
struct nlattr *attr;
uint32_t bdf_index, set;
struct sar_limit_cmd_row *row;
hdd_enter();
if (hdd_ctx->sar_version != SAR_VERSION_2) {
hdd_debug("SAR version: %d", hdd_ctx->sar_version);
return false;
}
attr = tb[QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE];
if (!attr)
return false;
bdf_index = nla_get_u32(attr);
if ((bdf_index >= QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0) &&
(bdf_index <= QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4)) {
set = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0;
} else if (bdf_index == QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE) {
set = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE;
bdf_index = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0;
} else {
return false;
}
/* Need two rows to hold the per-chain V2 power index
* To disable SARv2 limit, send chain, num_limits_row and
* power limit set to 0 (except power index 0xff)
*/
row = qdf_mem_malloc(2 * sizeof(*row));
if (!row)
return false;
if (wlan_hdd_cfg80211_sar_convert_limit_set(
set, &sar_limit_cmd->sar_enable)) {
hdd_err("Failed to convert SAR limit to WMI value");
return false;
}
sar_limit_cmd->commit_limits = 1;
sar_limit_cmd->num_limit_rows = 2;
sar_limit_cmd->sar_limit_row_list = row;
row[0].limit_value = bdf_index;
row[1].limit_value = row[0].limit_value;
row[0].chain_id = 0;
row[1].chain_id = 1;
row[0].validity_bitmap = WMI_SAR_CHAIN_ID_VALID_MASK;
row[1].validity_bitmap = WMI_SAR_CHAIN_ID_VALID_MASK;
hdd_exit();
return true;
}
#else /* WLAN_FEATURE_SARV1_TO_SARV2 */
static bool
hdd_convert_sarv1_to_sarv2(struct hdd_context *hdd_ctx,
struct nlattr *tb[],
struct sar_limit_cmd_params *sar_limit_cmd)
{
return false;
}
#endif /* WLAN_FEATURE_SARV1_TO_SARV2 */
/**
* wlan_hdd_cfg80211_sar_convert_band() - Convert WLAN band value
* @nl80211_value: Vendor command attribute value
* @wmi_value: Pointer to return converted WMI return value
*
* Convert NL80211 vendor command value for SAR BAND to WMI value
* Return: 0 on success, -1 on invalid value
*/
static int wlan_hdd_cfg80211_sar_convert_band(u32 nl80211_value, u32 *wmi_value)
{
int ret = 0;
switch (nl80211_value) {
case HDD_NL80211_BAND_2GHZ:
*wmi_value = WMI_SAR_2G_ID;
break;
case HDD_NL80211_BAND_5GHZ:
*wmi_value = WMI_SAR_5G_ID;
break;
default:
ret = -1;
}
return ret;
}
/**
* wlan_hdd_cfg80211_sar_convert_modulation() - Convert WLAN modulation value
* @nl80211_value: Vendor command attribute value
* @wmi_value: Pointer to return converted WMI return value
*
* Convert NL80211 vendor command value for SAR Modulation to WMI value
* Return: 0 on success, -1 on invalid value
*/
static int wlan_hdd_cfg80211_sar_convert_modulation(u32 nl80211_value,
u32 *wmi_value)
{
int ret = 0;
switch (nl80211_value) {
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK:
*wmi_value = WMI_SAR_MOD_CCK;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM:
*wmi_value = WMI_SAR_MOD_OFDM;
break;
default:
ret = -1;
}
return ret;
}
/**
* hdd_extract_sar_nested_attrs() - Extract nested SAR attribute
* @spec: nested nla attribue
* @row: output to hold extract nested attribute
*
* This function extracts nested SAR attribute one at a time which means
* for each nested attribute this has to be invoked from
* __wlan_hdd_set_sar_power_limits().
*
* Return: On success - 0
* On Failure - Negative value
*/
static int hdd_extract_sar_nested_attrs(struct nlattr *spec[],
struct sar_limit_cmd_row *row)
{
uint32_t limit;
uint32_t band;
uint32_t modulation;
int ret;
row->validity_bitmap = 0;
if (spec[SAR_LIMITS_SPEC_POWER_LIMIT]) {
limit = nla_get_u32(spec[SAR_LIMITS_SPEC_POWER_LIMIT]);
row->limit_value = limit;
} else if (spec[SAR_LIMITS_SPEC_POWER_LIMIT_INDEX]) {
limit = nla_get_u32(spec[SAR_LIMITS_SPEC_POWER_LIMIT_INDEX]);
row->limit_value = limit;
} else {
hdd_err("SAR Spec does not have power limit or index value");
return -EINVAL;
}
if (spec[SAR_LIMITS_SPEC_BAND]) {
band = nla_get_u32(spec[SAR_LIMITS_SPEC_BAND]);
ret = wlan_hdd_cfg80211_sar_convert_band(band, &row->band_id);
if (ret) {
hdd_err("Invalid SAR Band attr");
return ret;
}
row->validity_bitmap |= WMI_SAR_BAND_ID_VALID_MASK;
}
if (spec[SAR_LIMITS_SPEC_CHAIN]) {
row->chain_id = nla_get_u32(spec[SAR_LIMITS_SPEC_CHAIN]);
row->validity_bitmap |= WMI_SAR_CHAIN_ID_VALID_MASK;
}
if (spec[SAR_LIMITS_SPEC_MODULATION]) {
modulation = nla_get_u32(spec[SAR_LIMITS_SPEC_MODULATION]);
ret = wlan_hdd_cfg80211_sar_convert_modulation(modulation,
&row->mod_id);
if (ret) {
hdd_err("Invalid SAR Modulation attr");
return ret;
}
row->validity_bitmap |= WMI_SAR_MOD_ID_VALID_MASK;
}
return 0;
}
/**
* __wlan_hdd_set_sar_power_limits() - Set SAR power limits
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* This function is used to setup Specific Absorption Rate limit specs.
*
* Return: 0 on success, negative errno on failure
*/
static int __wlan_hdd_set_sar_power_limits(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *spec[SAR_LIMITS_MAX + 1];
struct nlattr *tb[SAR_LIMITS_MAX + 1];
struct nlattr *spec_list;
struct sar_limit_cmd_params *sar_limit_cmd;
int ret = -EINVAL, i = 0, rem = 0;
QDF_STATUS status;
uint32_t num_limit_rows = 0;
struct sar_limit_cmd_row *row;
uint32_t sar_enable;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
if (wlan_hdd_validate_context(hdd_ctx))
return -EINVAL;
if (wlan_cfg80211_nla_parse(tb, SAR_LIMITS_MAX, data, data_len,
wlan_hdd_sar_limits_policy)) {
hdd_err("Invalid SAR attributes");
return -EINVAL;
}
if (tb[SAR_LIMITS_SAR_ENABLE]) {
sar_enable = nla_get_u32(tb[SAR_LIMITS_SAR_ENABLE]);
if ((sar_enable >=
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0 &&
sar_enable <=
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4) &&
hdd_ctx->sar_version == SAR_VERSION_2 &&
!hdd_ctx->config->enable_sar_conversion) {
hdd_err("SARV1 to SARV2 is disabled from ini");
return -EINVAL;
} else if (sar_enable ==
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0 &&
hdd_ctx->sar_version == SAR_VERSION_1) {
hdd_err("FW expects SARV1 given command is SARV2");
return -EINVAL;
}
}
sar_limit_cmd = qdf_mem_malloc(sizeof(struct sar_limit_cmd_params));
if (!sar_limit_cmd)
return -ENOMEM;
wlan_hdd_sar_timers_reset(hdd_ctx);
/* is special SAR V1 => SAR V2 logic enabled and applicable? */
if (hdd_ctx->config->enable_sar_conversion &&
(hdd_convert_sarv1_to_sarv2(hdd_ctx, tb, sar_limit_cmd)))
goto send_sar_limits;
/* Vendor command manadates all SAR Specs in single call */
sar_limit_cmd->commit_limits = 1;
sar_limit_cmd->sar_enable = WMI_SAR_FEATURE_NO_CHANGE;
if (tb[SAR_LIMITS_SAR_ENABLE]) {
uint32_t *sar_ptr = &sar_limit_cmd->sar_enable;
sar_enable = nla_get_u32(tb[SAR_LIMITS_SAR_ENABLE]);
ret = wlan_hdd_cfg80211_sar_convert_limit_set(sar_enable,
sar_ptr);
if (ret) {
hdd_err("Invalid SAR Enable attr");
goto fail;
}
}
hdd_debug("attr sar sar_enable %d", sar_limit_cmd->sar_enable);
if (tb[SAR_LIMITS_NUM_SPECS]) {
num_limit_rows = nla_get_u32(tb[SAR_LIMITS_NUM_SPECS]);
hdd_debug("attr sar num_limit_rows %u", num_limit_rows);
}
if (num_limit_rows > MAX_SAR_LIMIT_ROWS_SUPPORTED) {
hdd_err("SAR Spec list exceed supported size");
goto fail;
}
if (num_limit_rows == 0)
goto send_sar_limits;
row = qdf_mem_malloc(sizeof(*row) * num_limit_rows);
if (!row) {
hdd_err("Failed to allocate memory for sar_limit_row_list");
goto fail;
}
sar_limit_cmd->num_limit_rows = num_limit_rows;
sar_limit_cmd->sar_limit_row_list = row;
if (!tb[SAR_LIMITS_SPEC]) {
hdd_err("Invalid SAR specification list");
goto fail;
}
nla_for_each_nested(spec_list, tb[SAR_LIMITS_SPEC], rem) {
if (i == num_limit_rows) {
hdd_warn("SAR Cmd has excess SPECs in list");
break;
}
if (wlan_cfg80211_nla_parse(spec,
SAR_LIMITS_MAX,
nla_data(spec_list),
nla_len(spec_list),
wlan_hdd_sar_limits_policy)) {
hdd_err("nla_parse failed for SAR Spec list");
goto fail;
}
ret = hdd_extract_sar_nested_attrs(spec, row);
if (ret) {
hdd_err("Failed to extract SAR nested attrs");
goto fail;
}
hdd_debug("Spec_ID: %d, Band: %d Chain: %d Mod: %d POW_Limit: %d Validity_Bitmap: %d",
i, row->band_id, row->chain_id, row->mod_id,
row->limit_value, row->validity_bitmap);
i++;
row++;
}
if (i < sar_limit_cmd->num_limit_rows) {
hdd_warn("SAR Cmd has less SPECs in list");
sar_limit_cmd->num_limit_rows = i;
}
send_sar_limits:
status = sme_set_sar_power_limits(hdd_ctx->mac_handle, sar_limit_cmd);
if (QDF_IS_STATUS_ERROR(status)) {
hdd_err("Failed to set sar power limits");
goto fail;
}
/* After SSR, the SAR configuration is lost. As SSR is hidden from
* userland, this command will not come from userspace after a SSR. To
* restore this configuration, save this in hdd context and restore
* after re-init.
*/
hdd_store_sar_config(hdd_ctx, sar_limit_cmd);
return 0;
fail:
if (sar_limit_cmd) {
qdf_mem_free(sar_limit_cmd->sar_limit_row_list);
qdf_mem_free(sar_limit_cmd);
}
return ret;
}
#undef SAR_LIMITS_SAR_ENABLE
#undef SAR_LIMITS_NUM_SPECS
#undef SAR_LIMITS_SPEC
#undef SAR_LIMITS_SPEC_BAND
#undef SAR_LIMITS_SPEC_CHAIN
#undef SAR_LIMITS_SPEC_MODULATION
#undef SAR_LIMITS_SPEC_POWER_LIMIT
#undef SAR_LIMITS_SPEC_POWER_LIMIT_INDEX
#undef SAR_LIMITS_MAX
int wlan_hdd_cfg80211_set_sar_power_limits(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_set_sar_power_limits(wiphy, wdev, data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
int wlan_hdd_cfg80211_get_sar_power_limits(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,

View file

@ -163,17 +163,81 @@ int wlan_hdd_cfg80211_get_sar_power_limits(struct wiphy *wiphy,
const void *data,
int data_len);
#define FEATURE_SAR_LIMITS_VENDOR_COMMANDS \
{ \
.info.vendor_id = QCA_NL80211_VENDOR_ID, \
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS, \
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | \
WIPHY_VENDOR_CMD_NEED_RUNNING, \
.doit = wlan_hdd_cfg80211_get_sar_power_limits, \
/**
* wlan_hdd_cfg80211_set_sar_power_limits() - Set SAR power limits
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* Wrapper function of __wlan_hdd_cfg80211_set_sar_power_limits()
*
* Return: 0 on success, negative errno on failure
*/
int wlan_hdd_cfg80211_set_sar_power_limits(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len);
/**
* hdd_store_sar_config() - Store SAR config in HDD context
* @hdd_ctx: The HDD context
* @sar_limit_cmd: The sar_limit_cmd_params struct to save
*
* After SSR, the SAR configuration is lost. As SSR is hidden from
* userland, this command will not come from userspace after a SSR. To
* restore this configuration, save this in hdd context and restore
* after re-init.
*
* Return: None
*/
void hdd_store_sar_config(struct hdd_context *hdd_ctx,
struct sar_limit_cmd_params *sar_limit_cmd);
/**
* hdd_free_sar_config() - Free the resources allocated while storing SAR config
* @hdd_ctx: HDD context
*
* The driver stores the SAR config values in HDD context so that it can be
* restored in the case SSR is invoked. Free those resources.
*
* Return: None
*/
void wlan_hdd_free_sar_config(struct hdd_context *hdd_ctx);
extern const struct nla_policy
wlan_hdd_sar_limits_policy[QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX + 1];
#define FEATURE_SAR_LIMITS_VENDOR_COMMANDS \
{ \
.info.vendor_id = QCA_NL80211_VENDOR_ID, \
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS, \
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | \
WIPHY_VENDOR_CMD_NEED_RUNNING, \
.doit = wlan_hdd_cfg80211_get_sar_power_limits, \
vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) \
}, \
{ \
.info.vendor_id = QCA_NL80211_VENDOR_ID, \
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS, \
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | \
WIPHY_VENDOR_CMD_NEED_RUNNING, \
.doit = wlan_hdd_cfg80211_set_sar_power_limits, \
vendor_command_policy(wlan_hdd_sar_limits_policy, \
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX) \
},
#else /* FEATURE_SAR_LIMITS */
#define FEATURE_SAR_LIMITS_VENDOR_COMMANDS
static inline
void hdd_store_sar_config(struct hdd_context *hdd_ctx,
struct sar_limit_cmd_params *sar_limit_cmd)
{
}
static inline void wlan_hdd_free_sar_config(struct hdd_context *hdd_ctx)
{
}
#endif /* FEATURE_SAR_LIMITS */
#endif /* __WLAN_HDD_SAR_LIMITS_H */