From: Felix Fietkau Date: Wed, 14 Jul 2021 15:14:35 +0000 (+0200) Subject: mac80211: backport SAR power limit support X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=890bf06cef20015e2cec0274c87a9a2232691b6b;p=openwrt%2Fstaging%2Fjow.git mac80211: backport SAR power limit support Needed for an upcoming mt76 update Signed-off-by: Felix Fietkau --- 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..0c9ae3595d --- /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 +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 +Reviewed-by: Brian Norris +Reviewed-by: Abhishek Kumar +Link: https://lore.kernel.org/r/20201203103728.3034-2-cjhuang@codeaurora.org +[minor edits, NLA parse cleanups] +Signed-off-by: Johannes Berg +--- + +--- 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; +@@ -14713,6 +14783,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, +@@ -15576,6 +15751,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 diff --git a/package/kernel/mac80211/patches/subsys/385-mac80211-add-ieee80211_set_sar_specs.patch b/package/kernel/mac80211/patches/subsys/385-mac80211-add-ieee80211_set_sar_specs.patch new file mode 100644 index 0000000000..c351bc812a --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/385-mac80211-add-ieee80211_set_sar_specs.patch @@ -0,0 +1,51 @@ +From: Carl Huang +Date: Thu, 3 Dec 2020 05:37:27 -0500 +Subject: [PATCH] mac80211: add ieee80211_set_sar_specs + +This change registers ieee80211_set_sar_specs to +mac80211_config_ops, so cfg80211 can call it. + +Signed-off-by: Carl Huang +Reviewed-by: Brian Norris +Reviewed-by: Abhishek Kumar +Link: https://lore.kernel.org/r/20201203103728.3034-3-cjhuang@codeaurora.org +Signed-off-by: Johannes Berg +--- + +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -4207,6 +4207,8 @@ struct ieee80211_ops { + struct ieee80211_vif *vif); + void (*sta_set_4addr)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enabled); ++ int (*set_sar_specs)(struct ieee80211_hw *hw, ++ const struct cfg80211_sar_specs *sar); + void (*sta_set_decap_offload)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enabled); +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -4136,6 +4136,17 @@ static int ieee80211_reset_tid_config(st + return ret; + } + ++static int ieee80211_set_sar_specs(struct wiphy *wiphy, ++ struct cfg80211_sar_specs *sar) ++{ ++ struct ieee80211_local *local = wiphy_priv(wiphy); ++ ++ if (!local->ops->set_sar_specs) ++ return -EOPNOTSUPP; ++ ++ return local->ops->set_sar_specs(&local->hw, sar); ++} ++ + const struct cfg80211_ops mac80211_config_ops = { + .add_virtual_intf = ieee80211_add_iface, + .del_virtual_intf = ieee80211_del_iface, +@@ -4239,4 +4250,5 @@ const struct cfg80211_ops mac80211_confi + .probe_mesh_link = ieee80211_probe_mesh_link, + .set_tid_config = ieee80211_set_tid_config, + .reset_tid_config = ieee80211_reset_tid_config, ++ .set_sar_specs = ieee80211_set_sar_specs, + }; diff --git a/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch b/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch index 990ef04eca..b2ee61a6dc 100644 --- a/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch +++ b/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch @@ -1,6 +1,6 @@ --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h -@@ -3745,6 +3745,7 @@ struct mgmt_frame_regs { +@@ -3793,6 +3793,7 @@ struct mgmt_frame_regs { * (as advertised by the nl80211 feature flag.) * @get_tx_power: store the current TX power into the dbm variable; * return 0 if successful @@ -8,7 +8,7 @@ * * @set_wds_peer: set the WDS peer for a WDS interface * -@@ -4067,6 +4068,7 @@ struct cfg80211_ops { +@@ -4115,6 +4116,7 @@ struct cfg80211_ops { enum nl80211_tx_power_setting type, int mbm); int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev, int *dbm); @@ -57,7 +57,7 @@ __NL80211_ATTR_AFTER_LAST, --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c -@@ -2742,6 +2742,19 @@ static int ieee80211_get_tx_power(struct +@@ -2761,6 +2761,19 @@ static int ieee80211_get_tx_power(struct return 0; } @@ -77,7 +77,7 @@ static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev, const u8 *addr) { -@@ -4172,6 +4185,7 @@ const struct cfg80211_ops mac80211_confi +@@ -4202,6 +4215,7 @@ const struct cfg80211_ops mac80211_confi .set_wiphy_params = ieee80211_set_wiphy_params, .set_tx_power = ieee80211_set_tx_power, .get_tx_power = ieee80211_get_tx_power, @@ -129,15 +129,15 @@ local->hw.max_mtu = IEEE80211_MAX_DATA_LEN; --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c -@@ -740,6 +740,7 @@ static const struct nla_policy nl80211_p - NLA_POLICY_RANGE(NLA_U8, NL80211_SAE_PWE_HUNT_AND_PECK, +@@ -753,6 +753,7 @@ static const struct nla_policy nl80211_p NL80211_SAE_PWE_BOTH), + [NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy), [NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT }, + [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 }, }; /* policy for the key attributes */ -@@ -3248,6 +3249,20 @@ static int nl80211_set_wiphy(struct sk_b +@@ -3318,6 +3319,20 @@ static int nl80211_set_wiphy(struct sk_b if (result) return result; }