diff options
author | Felix Fietkau <nbd@nbd.name> | 2021-07-14 17:14:35 +0200 |
---|---|---|
committer | Felix Fietkau <nbd@nbd.name> | 2021-11-23 18:30:04 +0100 |
commit | 52300733cd4a8e603e5d545a7e63b25889b895c2 (patch) | |
tree | 0ff78c513cf14f116a41857e80a8af193455c226 /package/kernel/mac80211/patches/subsys/384-nl80211-add-common-API-to-configure-SAR-power-limita.patch | |
parent | 68886f301850c6c35bdff002a719fed1ded584c9 (diff) | |
download | upstream-52300733cd4a8e603e5d545a7e63b25889b895c2.tar.gz upstream-52300733cd4a8e603e5d545a7e63b25889b895c2.tar.bz2 upstream-52300733cd4a8e603e5d545a7e63b25889b895c2.zip |
mac80211: backport SAR power limit support
Needed for an upcoming mt76 update
Signed-off-by: Felix Fietkau <nbd@nbd.name>
(cherry-picked from commit 890bf06cef20015e2cec0274c87a9a2232691b6b)
Diffstat (limited to 'package/kernel/mac80211/patches/subsys/384-nl80211-add-common-API-to-configure-SAR-power-limita.patch')
-rw-r--r-- | package/kernel/mac80211/patches/subsys/384-nl80211-add-common-API-to-configure-SAR-power-limita.patch | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/subsys/384-nl80211-add-common-API-to-configure-SAR-power-limita.patch b/package/kernel/mac80211/patches/subsys/384-nl80211-add-common-API-to-configure-SAR-power-limita.patch new file mode 100644 index 0000000000..a47e29794c --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/384-nl80211-add-common-API-to-configure-SAR-power-limita.patch @@ -0,0 +1,398 @@ +From: Carl Huang <cjhuang@codeaurora.org> +Date: Thu, 3 Dec 2020 05:37:26 -0500 +Subject: [PATCH] nl80211: add common API to configure SAR power limitations + +NL80211_CMD_SET_SAR_SPECS is added to configure SAR from +user space. NL80211_ATTR_SAR_SPEC is used to pass the SAR +power specification when used with NL80211_CMD_SET_SAR_SPECS. + +Wireless driver needs to register SAR type, supported frequency +ranges to wiphy, so user space can query it. The index in +frequency range is used to specify which sub band the power +limitation applies to. The SAR type is for compatibility, so later +other SAR mechanism can be implemented without breaking the user +space SAR applications. + +Normal process is user space queries the SAR capability, and +gets the index of supported frequency ranges and associates the +power limitation with this index and sends to kernel. + +Here is an example of message send to kernel: +8c 00 00 00 08 00 01 00 00 00 00 00 38 00 2b 81 +08 00 01 00 00 00 00 00 2c 00 02 80 14 00 00 80 +08 00 02 00 00 00 00 00 08 00 01 00 38 00 00 00 +14 00 01 80 08 00 02 00 01 00 00 00 08 00 01 00 +48 00 00 00 + +NL80211_CMD_SET_SAR_SPECS: 0x8c +NL80211_ATTR_WIPHY: 0x01(phy idx is 0) +NL80211_ATTR_SAR_SPEC: 0x812b (NLA_NESTED) +NL80211_SAR_ATTR_TYPE: 0x00 (NL80211_SAR_TYPE_POWER) +NL80211_SAR_ATTR_SPECS: 0x8002 (NLA_NESTED) +freq range 0 power: 0x38 in 0.25dbm unit (14dbm) +freq range 1 power: 0x48 in 0.25dbm unit (18dbm) + +Signed-off-by: Carl Huang <cjhuang@codeaurora.org> +Reviewed-by: Brian Norris <briannorris@chromium.org> +Reviewed-by: Abhishek Kumar <kuabhs@chromium.org> +Link: https://lore.kernel.org/r/20201203103728.3034-2-cjhuang@codeaurora.org +[minor edits, NLA parse cleanups] +Signed-off-by: Johannes Berg <johannes.berg@intel.com> +--- + +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -1737,6 +1737,54 @@ struct station_info { + u8 connected_to_as; + }; + ++/** ++ * struct cfg80211_sar_sub_specs - sub specs limit ++ * @power: power limitation in 0.25dbm ++ * @freq_range_index: index the power limitation applies to ++ */ ++struct cfg80211_sar_sub_specs { ++ s32 power; ++ u32 freq_range_index; ++}; ++ ++/** ++ * struct cfg80211_sar_specs - sar limit specs ++ * @type: it's set with power in 0.25dbm or other types ++ * @num_sub_specs: number of sar sub specs ++ * @sub_specs: memory to hold the sar sub specs ++ */ ++struct cfg80211_sar_specs { ++ enum nl80211_sar_type type; ++ u32 num_sub_specs; ++ struct cfg80211_sar_sub_specs sub_specs[]; ++}; ++ ++ ++/** ++ * @struct cfg80211_sar_chan_ranges - sar frequency ranges ++ * @start_freq: start range edge frequency ++ * @end_freq: end range edge frequency ++ */ ++struct cfg80211_sar_freq_ranges { ++ u32 start_freq; ++ u32 end_freq; ++}; ++ ++/** ++ * struct cfg80211_sar_capa - sar limit capability ++ * @type: it's set via power in 0.25dbm or other types ++ * @num_freq_ranges: number of frequency ranges ++ * @freq_ranges: memory to hold the freq ranges. ++ * ++ * Note: WLAN driver may append new ranges or split an existing ++ * range to small ones and then append them. ++ */ ++struct cfg80211_sar_capa { ++ enum nl80211_sar_type type; ++ u32 num_freq_ranges; ++ const struct cfg80211_sar_freq_ranges *freq_ranges; ++}; ++ + #if IS_ENABLED(CPTCFG_CFG80211) + /** + * cfg80211_get_station - retrieve information about a given station +@@ -4259,6 +4307,8 @@ struct cfg80211_ops { + struct cfg80211_tid_config *tid_conf); + int (*reset_tid_config)(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 tids); ++ int (*set_sar_specs)(struct wiphy *wiphy, ++ struct cfg80211_sar_specs *sar); + }; + + /* +@@ -5030,6 +5080,8 @@ struct wiphy { + + u8 max_data_retry_count; + ++ const struct cfg80211_sar_capa *sar_capa; ++ + char priv[] __aligned(NETDEV_ALIGN); + }; + +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -405,6 +405,18 @@ nl80211_unsol_bcast_probe_resp_policy[NL + .len = IEEE80211_MAX_DATA_LEN } + }; + ++static const struct nla_policy ++sar_specs_policy[NL80211_SAR_ATTR_SPECS_MAX + 1] = { ++ [NL80211_SAR_ATTR_SPECS_POWER] = { .type = NLA_S32 }, ++ [NL80211_SAR_ATTR_SPECS_RANGE_INDEX] = {.type = NLA_U32 }, ++}; ++ ++static const struct nla_policy ++sar_policy[NL80211_SAR_ATTR_MAX + 1] = { ++ [NL80211_SAR_ATTR_TYPE] = NLA_POLICY_MAX(NLA_U32, NUM_NL80211_SAR_TYPE), ++ [NL80211_SAR_ATTR_SPECS] = NLA_POLICY_NESTED_ARRAY(sar_specs_policy), ++}; ++ + static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { + [0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD }, + [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, +@@ -739,6 +751,7 @@ static const struct nla_policy nl80211_p + [NL80211_ATTR_SAE_PWE] = + NLA_POLICY_RANGE(NLA_U8, NL80211_SAE_PWE_HUNT_AND_PECK, + NL80211_SAE_PWE_BOTH), ++ [NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy), + [NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT }, + }; + +@@ -2117,6 +2130,56 @@ fail: + return -ENOBUFS; + } + ++static int ++nl80211_put_sar_specs(struct cfg80211_registered_device *rdev, ++ struct sk_buff *msg) ++{ ++ struct nlattr *sar_capa, *specs, *sub_freq_range; ++ u8 num_freq_ranges; ++ int i; ++ ++ if (!rdev->wiphy.sar_capa) ++ return 0; ++ ++ num_freq_ranges = rdev->wiphy.sar_capa->num_freq_ranges; ++ ++ sar_capa = nla_nest_start(msg, NL80211_ATTR_SAR_SPEC); ++ if (!sar_capa) ++ return -ENOSPC; ++ ++ if (nla_put_u32(msg, NL80211_SAR_ATTR_TYPE, rdev->wiphy.sar_capa->type)) ++ goto fail; ++ ++ specs = nla_nest_start(msg, NL80211_SAR_ATTR_SPECS); ++ if (!specs) ++ goto fail; ++ ++ /* report supported freq_ranges */ ++ for (i = 0; i < num_freq_ranges; i++) { ++ sub_freq_range = nla_nest_start(msg, i + 1); ++ if (!sub_freq_range) ++ goto fail; ++ ++ if (nla_put_u32(msg, NL80211_SAR_ATTR_SPECS_START_FREQ, ++ rdev->wiphy.sar_capa->freq_ranges[i].start_freq)) ++ goto fail; ++ ++ if (nla_put_u32(msg, NL80211_SAR_ATTR_SPECS_END_FREQ, ++ rdev->wiphy.sar_capa->freq_ranges[i].end_freq)) ++ goto fail; ++ ++ nla_nest_end(msg, sub_freq_range); ++ } ++ ++ nla_nest_end(msg, specs); ++ nla_nest_end(msg, sar_capa); ++ ++ return 0; ++fail: ++ nla_nest_cancel(msg, sar_capa); ++ return -ENOBUFS; ++} ++ + struct nl80211_dump_wiphy_state { + s64 filter_wiphy; + long start; +@@ -2366,6 +2429,8 @@ static int nl80211_send_wiphy(struct cfg + CMD(set_multicast_to_unicast, SET_MULTICAST_TO_UNICAST); + CMD(update_connect_params, UPDATE_CONNECT_PARAMS); + CMD(update_ft_ies, UPDATE_FT_IES); ++ if (rdev->wiphy.sar_capa) ++ CMD(set_sar_specs, SET_SAR_SPECS); + } + #undef CMD + +@@ -2691,6 +2756,11 @@ static int nl80211_send_wiphy(struct cfg + + if (nl80211_put_tid_config_support(rdev, msg)) + goto nla_put_failure; ++ state->split_start++; ++ break; ++ case 16: ++ if (nl80211_put_sar_specs(rdev, msg)) ++ goto nla_put_failure; + + /* done */ + state->split_start = 0; +@@ -14712,6 +14782,111 @@ static void nl80211_post_doit(__genl_con + } + } + ++static int nl80211_set_sar_sub_specs(struct cfg80211_registered_device *rdev, ++ struct cfg80211_sar_specs *sar_specs, ++ struct nlattr *spec[], int index) ++{ ++ u32 range_index, i; ++ ++ if (!sar_specs || !spec) ++ return -EINVAL; ++ ++ if (!spec[NL80211_SAR_ATTR_SPECS_POWER] || ++ !spec[NL80211_SAR_ATTR_SPECS_RANGE_INDEX]) ++ return -EINVAL; ++ ++ range_index = nla_get_u32(spec[NL80211_SAR_ATTR_SPECS_RANGE_INDEX]); ++ ++ /* check if range_index exceeds num_freq_ranges */ ++ if (range_index >= rdev->wiphy.sar_capa->num_freq_ranges) ++ return -EINVAL; ++ ++ /* check if range_index duplicates */ ++ for (i = 0; i < index; i++) { ++ if (sar_specs->sub_specs[i].freq_range_index == range_index) ++ return -EINVAL; ++ } ++ ++ sar_specs->sub_specs[index].power = ++ nla_get_s32(spec[NL80211_SAR_ATTR_SPECS_POWER]); ++ ++ sar_specs->sub_specs[index].freq_range_index = range_index; ++ ++ return 0; ++} ++ ++static int nl80211_set_sar_specs(struct sk_buff *skb, struct genl_info *info) ++{ ++ struct cfg80211_registered_device *rdev = info->user_ptr[0]; ++ struct nlattr *spec[NL80211_SAR_ATTR_SPECS_MAX + 1]; ++ struct nlattr *tb[NL80211_SAR_ATTR_MAX + 1]; ++ struct cfg80211_sar_specs *sar_spec; ++ enum nl80211_sar_type type; ++ struct nlattr *spec_list; ++ u32 specs; ++ int rem, err; ++ ++ if (!rdev->wiphy.sar_capa || !rdev->ops->set_sar_specs) ++ return -EOPNOTSUPP; ++ ++ if (!info->attrs[NL80211_ATTR_SAR_SPEC]) ++ return -EINVAL; ++ ++ nla_parse_nested(tb, NL80211_SAR_ATTR_MAX, ++ info->attrs[NL80211_ATTR_SAR_SPEC], ++ NULL, NULL); ++ ++ if (!tb[NL80211_SAR_ATTR_TYPE] || !tb[NL80211_SAR_ATTR_SPECS]) ++ return -EINVAL; ++ ++ type = nla_get_u32(tb[NL80211_SAR_ATTR_TYPE]); ++ if (type != rdev->wiphy.sar_capa->type) ++ return -EINVAL; ++ ++ specs = 0; ++ nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem) ++ specs++; ++ ++ if (specs > rdev->wiphy.sar_capa->num_freq_ranges) ++ return -EINVAL; ++ ++ sar_spec = kzalloc(sizeof(*sar_spec) + ++ specs * sizeof(struct cfg80211_sar_sub_specs), ++ GFP_KERNEL); ++ if (!sar_spec) ++ return -ENOMEM; ++ ++ sar_spec->type = type; ++ specs = 0; ++ nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem) { ++ nla_parse_nested(spec, NL80211_SAR_ATTR_SPECS_MAX, ++ spec_list, NULL, NULL); ++ ++ switch (type) { ++ case NL80211_SAR_TYPE_POWER: ++ if (nl80211_set_sar_sub_specs(rdev, sar_spec, ++ spec, specs)) { ++ err = -EINVAL; ++ goto error; ++ } ++ break; ++ default: ++ err = -EINVAL; ++ goto error; ++ } ++ specs++; ++ } ++ ++ sar_spec->num_sub_specs = specs; ++ ++ rdev->cur_cmd_info = info; ++ err = rdev_set_sar_specs(rdev, sar_spec); ++ rdev->cur_cmd_info = NULL; ++error: ++ kfree(sar_spec); ++ return err; ++} ++ + static __genl_const struct genl_ops nl80211_ops[] = { + { + .cmd = NL80211_CMD_GET_WIPHY, +@@ -15575,6 +15750,14 @@ static const struct genl_small_ops nl802 + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, ++ { ++ .cmd = NL80211_CMD_SET_SAR_SPECS, ++ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, ++ .doit = nl80211_set_sar_specs, ++ .flags = GENL_UNS_ADMIN_PERM, ++ .internal_flags = NL80211_FLAG_NEED_WIPHY | ++ NL80211_FLAG_NEED_RTNL, ++ }, + }; + + static struct genl_family nl80211_fam __genl_ro_after_init = { +--- a/net/wireless/rdev-ops.h ++++ b/net/wireless/rdev-ops.h +@@ -1356,4 +1356,16 @@ static inline int rdev_reset_tid_config( + return ret; + } + ++static inline int rdev_set_sar_specs(struct cfg80211_registered_device *rdev, ++ struct cfg80211_sar_specs *sar) ++{ ++ int ret; ++ ++ trace_rdev_set_sar_specs(&rdev->wiphy, sar); ++ ret = rdev->ops->set_sar_specs(&rdev->wiphy, sar); ++ trace_rdev_return_int(&rdev->wiphy, ret); ++ ++ return ret; ++} ++ + #endif /* __CFG80211_RDEV_OPS */ +--- a/net/wireless/trace.h ++++ b/net/wireless/trace.h +@@ -3551,6 +3551,25 @@ TRACE_EVENT(rdev_reset_tid_config, + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", tids: 0x%x", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tids) + ); ++ ++TRACE_EVENT(rdev_set_sar_specs, ++ TP_PROTO(struct wiphy *wiphy, struct cfg80211_sar_specs *sar), ++ TP_ARGS(wiphy, sar), ++ TP_STRUCT__entry( ++ WIPHY_ENTRY ++ __field(u16, type) ++ __field(u16, num) ++ ), ++ TP_fast_assign( ++ WIPHY_ASSIGN; ++ __entry->type = sar->type; ++ __entry->num = sar->num_sub_specs; ++ ++ ), ++ TP_printk(WIPHY_PR_FMT ", Set type:%d, num_specs:%d", ++ WIPHY_PR_ARG, __entry->type, __entry->num) ++); ++ + #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ + + #undef TRACE_INCLUDE_PATH |