diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index 5aa2c0cbc38b..37fb6cb0a8ac 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -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 { diff --git a/core/hdd/src/wlan_hdd_cfg80211.h b/core/hdd/src/wlan_hdd_cfg80211.h index b82da6aadb25..69899d87c2b8 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.h +++ b/core/hdd/src/wlan_hdd_cfg80211.h @@ -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 diff --git a/core/hdd/src/wlan_hdd_sar_limits.c b/core/hdd/src/wlan_hdd_sar_limits.c index bec6d26f3af3..4890ae01516f 100644 --- a/core/hdd/src/wlan_hdd_sar_limits.c +++ b/core/hdd/src/wlan_hdd_sar_limits.c @@ -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, diff --git a/core/hdd/src/wlan_hdd_sar_limits.h b/core/hdd/src/wlan_hdd_sar_limits.h index b4411ff2d9b1..dd6c3a3cc72a 100644 --- a/core/hdd/src/wlan_hdd_sar_limits.h +++ b/core/hdd/src/wlan_hdd_sar_limits.h @@ -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 */