Message ID | 1600753017-4614-1-git-send-email-cjhuang@codeaurora.org (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | Johannes Berg |
Headers | show |
Series | [RFC,1/2] nl80211: add common API to configure SAR power limitations. | expand |
Sorry, send to wrong ath11k list. Let me resend to ath10k list for these 2 patches. Thanks, Carl On 2020-09-22 13:36, Carl Huang wrote: > 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 quries 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 03 00 15 00 00 00 38 00 26 81 > 08 00 01 00 00 00 00 00 2c 00 02 80 14 00 01 80 > 05 00 02 00 00 00 00 00 05 00 01 00 38 00 00 00 > 14 00 02 80 05 00 02 00 01 00 00 00 05 00 01 00 > 48 00 00 00 > > NL80211_CMD_SET_SAR_SPECS: 0x8c > NL80211_ATTR_SAR_SPEC: 0x8126 (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> > --- > include/net/cfg80211.h | 52 +++++++++++++ > include/net/mac80211.h | 2 + > include/uapi/linux/nl80211.h | 86 +++++++++++++++++++++ > net/mac80211/cfg.c | 12 +++ > net/wireless/nl80211.c | 173 > +++++++++++++++++++++++++++++++++++++++++++ > net/wireless/rdev-ops.h | 12 +++ > net/wireless/trace.h | 19 +++++ > 7 files changed, 356 insertions(+) > > diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h > index c9bce9b..94f09dd 100644 > --- a/include/net/cfg80211.h > +++ b/include/net/cfg80211.h > @@ -1663,6 +1663,55 @@ struct station_info { > u8 connected_to_as; > }; > > +/** > + * struct cfg80211_sar_sub_specs - sub specs limit > + * @power: value in 0.25dbm > + * @freq_range_index: index the power limitation applies to > + */ > +struct cfg80211_sar_sub_specs { > + u8 power; > + u8 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; > + u16 num_sub_specs; > + struct cfg80211_sar_sub_specs *sub_specs; > +}; > + > + > +/** > + * @struct cfg80211_sar_chan_ranges - sar frequency ranges > + * @index: the index of this range. It's used to specify > + * the frequency range when setting SAR power limitation > + * @start_freq: start channel frequency in kHZ. For example, > + * 2.4G channel 1 is represented as 2412000 > + * @end_freq: end channel frequency in kHZ > + */ > +struct cfg80211_sar_freq_ranges { > + u8 index; > + 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 > + * @chan_ranges: memory to hold the channel ranges. > + */ > +struct cfg80211_sar_capa { > + enum nl80211_sar_type type; > + u8 num_freq_ranges; > + const struct cfg80211_sar_freq_ranges *freq_ranges; > +}; > + > #if IS_ENABLED(CONFIG_CFG80211) > /** > * cfg80211_get_station - retrieve information about a given station > @@ -4153,6 +4202,7 @@ 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); > }; > > /* > @@ -4919,6 +4969,8 @@ struct wiphy { > > u8 max_data_retry_count; > > + const struct cfg80211_sar_capa *sar_capa; > + > char priv[] __aligned(NETDEV_ALIGN); > }; > > diff --git a/include/net/mac80211.h b/include/net/mac80211.h > index ec148b3..df758ee 100644 > --- a/include/net/mac80211.h > +++ b/include/net/mac80211.h > @@ -4125,6 +4125,8 @@ struct ieee80211_ops { > int (*reset_tid_config)(struct ieee80211_hw *hw, > struct ieee80211_vif *vif, > struct ieee80211_sta *sta, u8 tids); > + int (*set_sar_specs)(struct ieee80211_hw *hw, > + const struct cfg80211_sar_specs *sar); > }; > > /** > diff --git a/include/uapi/linux/nl80211.h > b/include/uapi/linux/nl80211.h > index 0584e0d..2dcfed4 100644 > --- a/include/uapi/linux/nl80211.h > +++ b/include/uapi/linux/nl80211.h > @@ -1177,6 +1177,9 @@ > * includes the contents of the frame. %NL80211_ATTR_ACK flag is > included > * if the recipient acknowledged the frame. > * > + * @NL80211_CMD_SET_SAR_SPECS: SAR power limitation configuration is > + * passed using %NL80211_ATTR_SAR_SPEC. > + * > * @NL80211_CMD_MAX: highest used command number > * @__NL80211_CMD_AFTER_LAST: internal use > */ > @@ -1407,6 +1410,8 @@ enum nl80211_commands { > > NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS, > > + NL80211_CMD_SET_SAR_SPECS, > + > /* add new commands above here */ > > /* used to define NL80211_CMD_MAX below */ > @@ -2513,6 +2518,10 @@ enum nl80211_commands { > * @NL80211_ATTR_HE_6GHZ_CAPABILITY: HE 6 GHz Band Capability element > (from > * association request when used with NL80211_CMD_NEW_STATION). > * > + * @NL80211_ATTR_SAR_SPEC: SAR power limitation specification when > + * used with %NL80211_CMD_SET_SAR_SPECS. It contains array of > + * %nl80211_sar_specs_attrs. > + * > * @NUM_NL80211_ATTR: total number of nl80211_attrs available > * @NL80211_ATTR_MAX: highest attribute number currently defined > * @__NL80211_ATTR_AFTER_LAST: internal use > @@ -2995,6 +3004,8 @@ enum nl80211_attrs { > > NL80211_ATTR_HE_6GHZ_CAPABILITY, > > + NL80211_ATTR_SAR_SPEC, > + > /* add attributes here, update the policy in nl80211.c */ > > __NL80211_ATTR_AFTER_LAST, > @@ -7004,4 +7015,79 @@ enum nl80211_iftype_akm_attributes { > NL80211_IFTYPE_AKM_ATTR_MAX = __NL80211_IFTYPE_AKM_ATTR_LAST - 1, > }; > > +/** > + * enum nl80211_sar_type - type of SAR specs > + * > + * @NL80211_SAR_TYPE_POWER: the value is specified in 0.25 dbm > + * > + */ > +enum nl80211_sar_type { > + NL80211_SAR_TYPE_POWER, > + > + /* add new type here */ > + > + /* Keep last */ > + NUM_NL80211_SAR_TYPE, > +}; > + > +/** > + * nl80211_sar_attrs - Attributes for SAR spec > + * > + * @NL80211_SAR_ATTR_TYPE: Type of SAR: power or index > + * > + * @NL80211_SAR_ATTR_SPECS: Nested array of SAR power > + * limit specifications. Each specification contains a set > + * of %nl80211_sar_specs_attrs > + * > + * @__NL80211_SAR_ATTR_LAST: Internal > + * @NL80211_SAR_ATTR_MAX: highest sar attribute > + * > + * These attributes are used with %NL80211_CMD_SET_SAR_SPEC > + */ > +enum nl80211_sar_attrs { > + __NL80211_SAR_ATTR_INVALID, > + > + NL80211_SAR_ATTR_TYPE, > + NL80211_SAR_ATTR_SPECS, > + > + __NL80211_SAR_ATTR_LAST, > + NL80211_SAR_ATTR_MAX = __NL80211_SAR_ATTR_LAST - 1, > +}; > + > +#define NL80211_SAR_ALL_FREQ_RNAGES 0xff > +#define NUM_MAX_NL80211_SAR_FREQ_RANGES 0xfe > + > +/** > + * nl80211_sar_specs_attrs - Attributes for SAR power limit specs > + * > + * @NL80211_SAR_ATTR_SPECS_POWER: Required (u32)value to specify the > actual > + * power limit value in units of 0.25 dBm if type is > + * NL80211_SAR_TYPE_POWER. (i.e., a value of 44 represents 11 dBm) > + * > + * @NL80211_SAR_ATTR_SPECS_FREQ_RANGES_INDEX: optional (u32) value to > specify the > + * index of exported freq ranges table. If this attribute is not > present, then > + * the power is applied to all freq ranges, i.e, all bands > + * > + * @NL80211_SAR_ATTR_SPECS_START_FREQ: Required (u32) value to > specify the start > + * frequency of this range to register SAR capability to wihpy and the > unit > + * is kHZ > + * > + * @NL80211_SAR_ATTR_SPECS_END_FREQ: Required (u32) value to specify > the end frequency > + * of this range to register SAR capability to wiphy and the unit is > kHZ > + * > + * @__NL80211_SAR_ATTR_SPECS_LAST: Internal > + * @NL80211_SAR_ATTR_SPECS_MAX: highest sar specs attribute > + */ > +enum nl80211_sar_specs_attrs { > + __NL80211_SAR_ATTR_SPECS_INVALID, > + > + NL80211_SAR_ATTR_SPECS_POWER, > + NL80211_SAR_ATTR_SPECS_FREQ_RANGE_INDEX, > + NL80211_SAR_ATTR_SPECS_START_FREQ, > + NL80211_SAR_ATTR_SPECS_END_FREQ, > + > + __NL80211_SAR_ATTR_SPECS_LAST, > + NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1, > +}; > + > #endif /* __LINUX_NL80211_H */ > diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c > index b4e39e3..cc74322 100644 > --- a/net/mac80211/cfg.c > +++ b/net/mac80211/cfg.c > @@ -3993,6 +3993,17 @@ static int ieee80211_reset_tid_config(struct > wiphy *wiphy, > 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, > @@ -4096,4 +4107,5 @@ const struct cfg80211_ops mac80211_config_ops = { > .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/net/wireless/nl80211.c b/net/wireless/nl80211.c > index 52a35e7..9318b3a 100644 > --- a/net/wireless/nl80211.c > +++ b/net/wireless/nl80211.c > @@ -367,6 +367,19 @@ > nl80211_tid_config_attr_policy[NL80211_TID_CONFIG_ATTR_MAX + 1] = { > NLA_POLICY_NESTED(nl80211_txattr_policy), > }; > > +static const struct nla_policy > +sar_specs_policy[NL80211_SAR_ATTR_SPECS_MAX + 1] = { > + [NL80211_SAR_ATTR_SPECS_POWER] = { .type = NLA_U8 }, > + [NL80211_SAR_ATTR_SPECS_FREQ_RANGE_INDEX] = > + NLA_POLICY_MAX(NLA_U8, NUM_MAX_NL80211_SAR_FREQ_RANGES), > +}; > + > +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 }, > @@ -675,6 +688,7 @@ static const struct nla_policy > nl80211_policy[NUM_NL80211_ATTR] = { > [NL80211_ATTR_SCAN_FREQ_KHZ] = { .type = NLA_NESTED }, > [NL80211_ATTR_HE_6GHZ_CAPABILITY] = > NLA_POLICY_EXACT_LEN(sizeof(struct ieee80211_he_6ghz_capa)), > + [NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy), > }; > > /* policy for the key attributes */ > @@ -1856,6 +1870,8 @@ static int nl80211_add_commands_unsplit(struct > cfg80211_registered_device *rdev, > goto nla_put_failure; > } > > + CMD(set_sar_specs, SET_SAR_SPECS); > + > return i; > nla_put_failure: > return -ENOBUFS; > @@ -2034,6 +2050,55 @@ nl80211_put_tid_config_support(struct > cfg80211_registered_device *rdev, > 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_u16(msg, NL80211_SAR_ATTR_TYPE, > rdev->wiphy.sar_capa->type)) > + goto fail; > + > + specs = nla_nest_start_noflag(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_noflag(msg, i + 1); > + > + nla_put_u32(msg, NL80211_SAR_ATTR_SPECS_START_FREQ, > + rdev->wiphy.sar_capa->freq_ranges[i].start_freq); > + > + nla_put_u32(msg, NL80211_SAR_ATTR_SPECS_END_FREQ, > + rdev->wiphy.sar_capa->freq_ranges[i].end_freq); > + > + nla_put_u8(msg, NL80211_SAR_ATTR_SPECS_FREQ_RANGE_INDEX, > + rdev->wiphy.sar_capa->freq_ranges[i].index); > + > + 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; > @@ -2598,6 +2663,9 @@ static int nl80211_send_wiphy(struct > cfg80211_registered_device *rdev, > if (nl80211_put_tid_config_support(rdev, msg)) > goto nla_put_failure; > > + if (nl80211_put_sar_specs(rdev, msg)) > + goto nla_put_failure; > + > /* done */ > state->split_start = 0; > break; > @@ -14473,6 +14541,103 @@ static void nl80211_post_doit(const struct > genl_ops *ops, struct sk_buff *skb, > } > } > > +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; > + u8 type, power, index, specs; > + struct nlattr *spec_list; > + int rem, err; > + > + if (!rdev->wiphy.sar_capa) > + 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], > + sar_policy, info->extack); > + > + if (!tb[NL80211_SAR_ATTR_TYPE]) > + return -EINVAL; > + > + type = nla_get_u32(tb[NL80211_SAR_ATTR_TYPE]); > + > + if (!tb[NL80211_SAR_ATTR_SPECS]) > + return -EINVAL; > + > + specs = 0; > + nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem) > + specs++; > + > + sar_spec = kzalloc(sizeof(*sar_spec) + > + specs * sizeof(struct cfg80211_sar_sub_specs), > + GFP_KERNEL); > + if (!sar_spec) > + return -ENOMEM; > + > + sar_spec->sub_specs = (struct cfg80211_sar_sub_specs *) > + ((char *)sar_spec + sizeof(*sar_spec)); > + specs = 0; > + sar_spec->type = type; > + > + nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem) { > + if (nla_parse(spec, > + NL80211_SAR_ATTR_SPECS_MAX, > + nla_data(spec_list), > + nla_len(spec_list), > + sar_specs_policy, > + NULL)) { > + err = -EINVAL; > + goto error; > + } > + > + /* for power type, power value must be presented */ > + if (!spec[NL80211_SAR_ATTR_SPECS_POWER] && > + type == NL80211_SAR_TYPE_POWER) { > + err = -EINVAL; > + goto error; > + } > + > + power = nla_get_u8(spec[NL80211_SAR_ATTR_SPECS_POWER]); > + sar_spec->sub_specs[specs].power = power; > + > + /* if NL80211_SAR_ATTR_SPECS_FREQ_RANGE_INDEX isn't present, > + * then the power applies to all bands. But it's only valid > + * for the first entry. > + */ > + if (!spec[NL80211_SAR_ATTR_SPECS_FREQ_RANGE_INDEX]) { > + if (specs) { > + err = -EINVAL; > + goto error; > + } else { > + sar_spec->sub_specs[specs].freq_range_index = > + NL80211_SAR_ALL_FREQ_RNAGES; > + specs++; > + break; > + } > + } > + > + index = nla_get_u8(spec[NL80211_SAR_ATTR_SPECS_FREQ_RANGE_INDEX]); > + sar_spec->sub_specs[specs].freq_range_index = index; > + specs++; > + } > + > + sar_spec->num_sub_specs = specs; > + > + rdev->cur_cmd_info = info; > + if (rdev->ops->set_sar_specs) > + err = rdev_set_sar_specs(rdev, sar_spec); > + else > + err = -EOPNOTSUPP; > + rdev->cur_cmd_info = NULL; > +error: > + kfree(sar_spec); > + return err; > +} > + > static const struct genl_ops nl80211_ops[] = { > { > .cmd = NL80211_CMD_GET_WIPHY, > @@ -15331,6 +15496,14 @@ static const struct genl_ops nl80211_ops[] = { > .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_NETDEV_UP | > + NL80211_FLAG_NEED_RTNL, > + }, > }; > > static struct genl_family nl80211_fam __ro_after_init = { > diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h > index 950d574..d9931c5 100644 > --- a/net/wireless/rdev-ops.h > +++ b/net/wireless/rdev-ops.h > @@ -1356,4 +1356,16 @@ static inline int rdev_reset_tid_config(struct > cfg80211_registered_device *rdev, > 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 */ > diff --git a/net/wireless/trace.h b/net/wireless/trace.h > index 6e218a0..116be64 100644 > --- a/net/wireless/trace.h > +++ b/net/wireless/trace.h > @@ -3547,6 +3547,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 > -- > 2.7.4
cjhuang@codeaurora.org writes: > Sorry, send to wrong ath11k list. > Let me resend to ath10k list for these 2 patches. When you submit a new version mark it as "v2". Otherwise people don't know what's the latest version.
On Mon, Sep 21, 2020 at 10:37 PM Carl Huang <cjhuang@codeaurora.org> wrote: > +/** > + * 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 > + * @chan_ranges: memory to hold the channel ranges. > + */ > +struct cfg80211_sar_capa { > + enum nl80211_sar_type type; > + u8 num_freq_ranges; > + const struct cfg80211_sar_freq_ranges *freq_ranges; > +}; ... > u8 max_data_retry_count; > > + const struct cfg80211_sar_capa *sar_capa; > + > char priv[] __aligned(NETDEV_ALIGN); > }; What are the ABI guarantees around a given driver/chip's 'sar_capa'? Do we guarantee that if the driver supports N ranges of certain bands, that it will always continue to support those bands? What if, for instance, ath10k grows a new set of subbands, supporting sub-sections of the 5GHz band -- does it still need to support both a contiguous [5, 5 + X] and a split [5, 5 + X/2], [5 + X/2, 5 + X]? Basically, do we intend to put the burden on user space to figure out how to map its power tables to the supported frequency band(s), or on the kernel, to support a backwards-compatible set of frequency ranges? The latter doesn't really work if you expect user space to always specify all ranges in a SET command. To be clear, I'm not as worried about differences between chips or drivers (I expect that different driver or chips may have different range support); just about stability for a given chip. Brian
On 2020-11-04 10:00, Brian Norris wrote: > On Mon, Sep 21, 2020 at 10:37 PM Carl Huang <cjhuang@codeaurora.org> > wrote: >> +/** >> + * 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 >> + * @chan_ranges: memory to hold the channel ranges. >> + */ >> +struct cfg80211_sar_capa { >> + enum nl80211_sar_type type; >> + u8 num_freq_ranges; >> + const struct cfg80211_sar_freq_ranges *freq_ranges; >> +}; > ... >> u8 max_data_retry_count; >> >> + const struct cfg80211_sar_capa *sar_capa; >> + >> char priv[] __aligned(NETDEV_ALIGN); >> }; > > What are the ABI guarantees around a given driver/chip's 'sar_capa'? > Do we guarantee that if the driver supports N ranges of certain bands, > that it will always continue to support those bands? What if, for > instance, ath10k grows a new set of subbands, supporting sub-sections > of the 5GHz band -- does it still need to support both a contiguous > [5, 5 + X] and a split [5, 5 + X/2], [5 + X/2, 5 + X]? Basically, do > we intend to put the burden on user space to figure out how to map its > power tables to the supported frequency band(s), or on the kernel, to > support a backwards-compatible set of frequency ranges? The latter > doesn't really work if you expect user space to always specify all > ranges in a SET command. > > To be clear, I'm not as worried about differences between chips or > drivers (I expect that different driver or chips may have different > range support); just about stability for a given chip. > For a given chip(at least a QCOM chip), we don't see that the range will grow or change. In addition, with current index-power SET method, it's hard for driver to know what the index mean given your example. Does the index mean [5,5 + x] or [5, 5 + x/2] ? So it's required for user-space to specify all the ranges. The number of ranges is quite small, so the SET itself is not a problem to specify all. Brian, are you fine that we go with this proposal? I'll send V2 based on the comments from Johannes and Abhishek. > Brian
+ ath10k [ I realize I replied to the "wrong" RFC v1; I fell trap to Kalle's note: "When you submit a new version mark it as "v2". Otherwise people don't know what's the latest version." ] On Tue, Nov 3, 2020 at 11:32 PM Carl Huang <cjhuang@codeaurora.org> wrote: > On 2020-11-04 10:00, Brian Norris wrote: > > What are the ABI guarantees around a given driver/chip's 'sar_capa'? > > Do we guarantee that if the driver supports N ranges of certain bands, > > that it will always continue to support those bands? ... > For a given chip(at least a QCOM chip), we don't see that the > range will grow or change. That's good to know. But that's not quite the same as an ABI guarantee. > In addition, with current index-power SET method, it's hard for driver > to know what the index mean given your example. Does the index mean > [5,5 + x] or [5, 5 + x/2] ? So it's required for user-space to specify > all the ranges. Well, we'd have to change the API (which is why I'm asking now) if we wanted the kernel to handle this gracefully. We'd have to retain the index (which, it sounds like you might be dropping if things go as Johannes suggested), so user space can continue to request the old range even if the driver also splits up a new range. More explicitly, something like this: Linux version A: ath10k supports: 0: 2G band 1: 5G band Linux version B: ath10k supports: 0: 2G band 1: 5G band 2: 1st half of 5G band 3: 2nd half of 5G band Indexes 2 and 3 would be overlapping with 1 of course, but it does mean that the following SET request will work on both A and B: SET index 0 -> power X index 1 -> power Y Notably, that also requires nl80211 allow specifying only a subset of bands in a SET request. But spelling that out, the proposal sounds more complicated than it's worth -- it's probably better to let user space handle the complexity. So that goes back to making ABI requirements clear, so we don't have kernel/user mixups/regressions down the line. > The number of ranges is quite small, so the SET itself is not a > problem to specify all. Sure, but it does mean that if I (user space) don't trust that driver support remains constant, I have to do some negotiation. I would already do some verification via the get/dump API of course, but negotiation is pretty complex if there is unbounded flexibility. It would be nice to spell out what sort of negotiation is reasonable as part of the ABI. For example, it might be reasonable to say that bands should never be combined; only ever added or split. BTW, considering the possibility of "added" bands, how about this example: if ath10k were to add 6GHz support (and SAR to go with it), user space would have to learn to specify limits for it too, due to the "all or nothing" limitation? It'd be good to be up front about this, similar to the previous paragraph. > Brian, are you fine that we go with this proposal? I'll send > V2 based on the comments from Johannes and Abhishek. I think I'm fine with it; my main concerns around ambiguities, so maybe it just needs more explicit mentions in the docs (commit messages and/or comments). I'd be happy to see v2 and go from there. Brian
Brian Norris <briannorris@chromium.org> writes: > + ath10k > > [ I realize I replied to the "wrong" RFC v1; I fell trap to Kalle's note: > > "When you submit a new version mark it as "v2". Otherwise people don't > know what's the latest version." ] > > On Tue, Nov 3, 2020 at 11:32 PM Carl Huang <cjhuang@codeaurora.org> wrote: >> On 2020-11-04 10:00, Brian Norris wrote: >> > What are the ABI guarantees around a given driver/chip's 'sar_capa'? >> > Do we guarantee that if the driver supports N ranges of certain bands, >> > that it will always continue to support those bands? > ... >> For a given chip(at least a QCOM chip), we don't see that the >> range will grow or change. > > That's good to know. But that's not quite the same as an ABI guarantee. I'm not sure if I understood Brian's question correctly, but I have concerns on the assumption that frequency ranges never change. For example, in ath10k we have a patch[1] under discussion which adds more channels and in ath11k we added 6 GHz band after initial ath11k support landed. And I would not be surprised if in some boards/platforms a certain band is disabled due to cotting costs (no antenna etc). My preference is to have a robust interface which would be designed to handle these kind of changes. [1] [PATCH] ath10k: enable advertising support for channels 32, 68 and 98
On 2020-11-05 16:35, Kalle Valo wrote: > Brian Norris <briannorris@chromium.org> writes: > >> + ath10k >> >> [ I realize I replied to the "wrong" RFC v1; I fell trap to Kalle's >> note: >> >> "When you submit a new version mark it as "v2". Otherwise people don't >> know what's the latest version." ] >> >> On Tue, Nov 3, 2020 at 11:32 PM Carl Huang <cjhuang@codeaurora.org> >> wrote: >>> On 2020-11-04 10:00, Brian Norris wrote: >>> > What are the ABI guarantees around a given driver/chip's 'sar_capa'? >>> > Do we guarantee that if the driver supports N ranges of certain bands, >>> > that it will always continue to support those bands? >> ... >>> For a given chip(at least a QCOM chip), we don't see that the >>> range will grow or change. >> >> That's good to know. But that's not quite the same as an ABI >> guarantee. > > I'm not sure if I understood Brian's question correctly, but I have > concerns on the assumption that frequency ranges never change. For > example, in ath10k we have a patch[1] under discussion which adds more > channels and in ath11k we added 6 GHz band after initial ath11k support > landed. And I would not be surprised if in some boards/platforms a > certain band is disabled due to cotting costs (no antenna etc). My > preference is to have a robust interface which would be designed to > handle these kind of changes. > > [1] [PATCH] ath10k: enable advertising support for channels 32, 68 and > 98 So the trick here is even if more channels are supported, it doesn't mean that it can support different SAR setting on these new channels. In this case, it likely falls into 5G range. It's safe for driver to extend the 5G range and doesn't break userspace. (68 and 98 are already in the 5G range, so driver just extends the start edge freq to 32 here.). But for flexibility, given 6 GHz as example here, let's keep the explicit index for SET command. For sar_capa advertisement, the explicit index is dropped as Johannes suggested. New ranges can only be appended to existing ones. Like Brian said, only add or split is allowed. The complexity to handle splitted range Vs whole range is left to WLAN driver itself. Userspace can SET any ranges which are advertised by WLAN driver. It's not required to set all ranges and userspace can skip any ranges.
On 2020-11-05 01:44, Brian Norris wrote: > + ath10k > > [ I realize I replied to the "wrong" RFC v1; I fell trap to Kalle's > note: > > "When you submit a new version mark it as "v2". Otherwise people don't > know what's the latest version." ] > > On Tue, Nov 3, 2020 at 11:32 PM Carl Huang <cjhuang@codeaurora.org> > wrote: >> On 2020-11-04 10:00, Brian Norris wrote: >> > What are the ABI guarantees around a given driver/chip's 'sar_capa'? >> > Do we guarantee that if the driver supports N ranges of certain bands, >> > that it will always continue to support those bands? > ... >> For a given chip(at least a QCOM chip), we don't see that the >> range will grow or change. > > That's good to know. But that's not quite the same as an ABI guarantee. > >> In addition, with current index-power SET method, it's hard for driver >> to know what the index mean given your example. Does the index mean >> [5,5 + x] or [5, 5 + x/2] ? So it's required for user-space to >> specify >> all the ranges. > > Well, we'd have to change the API (which is why I'm asking now) if we > wanted the kernel to handle this gracefully. We'd have to retain the > index (which, it sounds like you might be dropping if things go as > Johannes suggested), so user space can continue to request the old > range even if the driver also splits up a new range. > > More explicitly, something like this: > > Linux version A: > ath10k supports: > 0: 2G band > 1: 5G band > > Linux version B: > ath10k supports: > 0: 2G band > 1: 5G band > 2: 1st half of 5G band > 3: 2nd half of 5G band > > Indexes 2 and 3 would be overlapping with 1 of course, but it does > mean that the following SET request will work on both A and B: > > SET > index 0 -> power X > index 1 -> power Y > > Notably, that also requires nl80211 allow specifying only a subset of > bands in a SET request. > > But spelling that out, the proposal sounds more complicated than it's > worth -- it's probably better to let user space handle the complexity. > So that goes back to making ABI requirements clear, so we don't have > kernel/user mixups/regressions down the line. > >> The number of ranges is quite small, so the SET itself is not a >> problem to specify all. > > Sure, but it does mean that if I (user space) don't trust that driver > support remains constant, I have to do some negotiation. I would > already do some verification via the get/dump API of course, but > negotiation is pretty complex if there is unbounded flexibility. It > would be nice to spell out what sort of negotiation is reasonable as > part of the ABI. For example, it might be reasonable to say that bands > should never be combined; only ever added or split. > > BTW, considering the possibility of "added" bands, how about this > example: if ath10k were to add 6GHz support (and SAR to go with it), > user space would have to learn to specify limits for it too, due to > the "all or nothing" limitation? It'd be good to be up front about > this, similar to the previous paragraph. > Like what I've replied in another email. Let's remove "all or nothing" limitation. If certain ranges are not SET, then these ranges just don't have SAR power limitation. Just FYI, ath10k will never support 6GHz, it's ath11k to support it. >> Brian, are you fine that we go with this proposal? I'll send >> V2 based on the comments from Johannes and Abhishek. > > I think I'm fine with it; my main concerns around ambiguities, so > maybe it just needs more explicit mentions in the docs (commit > messages and/or comments). I'd be happy to see v2 and go from there. > > Brian
On Thu, Nov 5, 2020 at 3:10 AM Carl Huang <cjhuang@codeaurora.org> wrote: > On 2020-11-05 16:35, Kalle Valo wrote: > > Brian Norris <briannorris@chromium.org> writes: > >> On Tue, Nov 3, 2020 at 11:32 PM Carl Huang <cjhuang@codeaurora.org> > >> wrote: > >>> On 2020-11-04 10:00, Brian Norris wrote: > >>> > What are the ABI guarantees around a given driver/chip's 'sar_capa'? > >>> > Do we guarantee that if the driver supports N ranges of certain bands, > >>> > that it will always continue to support those bands? To be clear: the answer here is "no." So we have to map out what the user/kernel interaction looks like when they change. > >> ... > >>> For a given chip(at least a QCOM chip), we don't see that the > >>> range will grow or change. > >> > >> That's good to know. But that's not quite the same as an ABI > >> guarantee. > > > > I'm not sure if I understood Brian's question correctly, but I have > > concerns on the assumption that frequency ranges never change. For > > example, in ath10k we have a patch[1] under discussion which adds more > > channels and in ath11k we added 6 GHz band after initial ath11k support > > landed. And I would not be surprised if in some boards/platforms a > > certain band is disabled due to cotting costs (no antenna etc). Right, I certainly was not taking the "never change bands" claim from Carl at face value ;) This is exactly why I was asking. > > My > > preference is to have a robust interface which would be designed to > > handle these kind of changes. Sure. > > [1] [PATCH] ath10k: enable advertising support for channels 32, 68 and > > 98 > > So the trick here is even if more channels are supported, it doesn't > mean > that it can support different SAR setting on these new channels. In this > case, > it likely falls into 5G range. It's safe for driver to extend the 5G > range and > doesn't break userspace. (68 and 98 are already in the 5G range, so > driver just > extends the start edge freq to 32 here.). You can't just wave your hands and say it "doesn't break userspace" -- you have to think about how user space can use this API. Specifically, consider that user space is not going to memorize indeces, as those are per-driver implementation details; it's going to memorize frequency bands. It wants to cross reference those with the results of the GET/DUMP API before it translates those into indeces for SET. As you're describing it, user space will have to have some kind of "fuzziness" to its logic -- today, it thinks the 5G band is [X,Y], but tomorrow it might expand to [X-N, Y+M]. So user space should just ensure that it configures any band that intersects with [X,Y], even though it didn't know about [X-N,X] or [Y,Y+M]? That logic covers splits too, I suppose. There's still the question of ranges that user space has no knowledge of (i.e., no intersection with any known [X,Y]). I think there's two approaches that are roughly equivalent: 1) require SET operations to specify all bands, and designate a NULL or MAX value that user space should use for unknown/unconfigured bands [or, user space uses some kind of "extension" from the nearest known band, just to be safe?] 2) allow SET operations to specify a subset of supported bands [gray area: what happens with the unconfigured band(s)? left as-is? use max?] We're approximately in #1 right now. If we're explicit about how that's supposed to work, then I think we can stay with that. Although it sounds like Carl is moving toward #2 (allow subsets). > But for flexibility, given 6 GHz as example here, let's keep the > explicit > index for SET command. For sar_capa advertisement, the explicit index is > dropped as Johannes suggested. New ranges can only be appended to > existing > ones. Like Brian said, only add or split is allowed. > The complexity to > handle > splitted range Vs whole range is left to WLAN driver itself. Hmm? I thought we're keeping the driver simple. I'm OK with that (and moving a little more complexity into user space) as long as we're clear about it. Brian > Userspace can SET any ranges which are advertised by WLAN driver. It's > not required to set all ranges and userspace can skip any ranges.
On 2020-11-06 02:25, Brian Norris wrote: > On Thu, Nov 5, 2020 at 3:10 AM Carl Huang <cjhuang@codeaurora.org> > wrote: >> On 2020-11-05 16:35, Kalle Valo wrote: >> > Brian Norris <briannorris@chromium.org> writes: >> >> On Tue, Nov 3, 2020 at 11:32 PM Carl Huang <cjhuang@codeaurora.org> >> >> wrote: >> >>> On 2020-11-04 10:00, Brian Norris wrote: >> >>> > What are the ABI guarantees around a given driver/chip's 'sar_capa'? >> >>> > Do we guarantee that if the driver supports N ranges of certain bands, >> >>> > that it will always continue to support those bands? > > To be clear: the answer here is "no." So we have to map out what the > user/kernel interaction looks like when they change. > >> >> ... >> >>> For a given chip(at least a QCOM chip), we don't see that the >> >>> range will grow or change. >> >> >> >> That's good to know. But that's not quite the same as an ABI >> >> guarantee. >> > >> > I'm not sure if I understood Brian's question correctly, but I have >> > concerns on the assumption that frequency ranges never change. For >> > example, in ath10k we have a patch[1] under discussion which adds more >> > channels and in ath11k we added 6 GHz band after initial ath11k support >> > landed. And I would not be surprised if in some boards/platforms a >> > certain band is disabled due to cotting costs (no antenna etc). > > Right, I certainly was not taking the "never change bands" claim from > Carl at face value ;) This is exactly why I was asking. > >> > My >> > preference is to have a robust interface which would be designed to >> > handle these kind of changes. > > Sure. > >> > [1] [PATCH] ath10k: enable advertising support for channels 32, 68 and >> > 98 >> >> So the trick here is even if more channels are supported, it doesn't >> mean >> that it can support different SAR setting on these new channels. In >> this >> case, >> it likely falls into 5G range. It's safe for driver to extend the 5G >> range and >> doesn't break userspace. (68 and 98 are already in the 5G range, so >> driver just >> extends the start edge freq to 32 here.). > > You can't just wave your hands and say it "doesn't break userspace" -- > you have to think about how user space can use this API. > > Specifically, consider that user space is not going to memorize > indeces, as those are per-driver implementation details; it's going to > memorize frequency bands. It wants to cross reference those with the > results of the GET/DUMP API before it translates those into indeces > for SET. As you're describing it, user space will have to have some > kind of "fuzziness" to its logic -- today, it thinks the 5G band is > [X,Y], but tomorrow it might expand to [X-N, Y+M]. So user space > should just ensure that it configures any band that intersects with > [X,Y], even though it didn't know about [X-N,X] or [Y,Y+M]? That logic > covers splits too, I suppose. > > There's still the question of ranges that user space has no knowledge > of (i.e., no intersection with any known [X,Y]). I think there's two > approaches that are roughly equivalent: > 1) require SET operations to specify all bands, and designate a NULL > or MAX value that user space should use for unknown/unconfigured bands > [or, user space uses some kind of "extension" from the nearest known > band, just to be safe?] > 2) allow SET operations to specify a subset of supported bands [gray > area: what happens with the unconfigured band(s)? left as-is? use > max?] > > We're approximately in #1 right now. If we're explicit about how > that's supposed to work, then I think we can stay with that. Although > it sounds like Carl is moving toward #2 (allow subsets). > >> But for flexibility, given 6 GHz as example here, let's keep the >> explicit >> index for SET command. For sar_capa advertisement, the explicit index >> is >> dropped as Johannes suggested. New ranges can only be appended to >> existing >> ones. Like Brian said, only add or split is allowed. > >> The complexity to >> handle >> splitted range Vs whole range is left to WLAN driver itself. > > Hmm? I thought we're keeping the driver simple. I'm OK with that (and > moving a little more complexity into user space) as long as we're > clear about it. > I've sent [PATCH 0/3] add common API to configure SAR, please let's start from there again. > Brian > >> Userspace can SET any ranges which are advertised by WLAN driver. It's >> not required to set all ranges and userspace can skip any ranges. > > _______________________________________________ > ath10k mailing list > ath10k@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/ath10k
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c9bce9b..94f09dd 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1663,6 +1663,55 @@ struct station_info { u8 connected_to_as; }; +/** + * struct cfg80211_sar_sub_specs - sub specs limit + * @power: value in 0.25dbm + * @freq_range_index: index the power limitation applies to + */ +struct cfg80211_sar_sub_specs { + u8 power; + u8 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; + u16 num_sub_specs; + struct cfg80211_sar_sub_specs *sub_specs; +}; + + +/** + * @struct cfg80211_sar_chan_ranges - sar frequency ranges + * @index: the index of this range. It's used to specify + * the frequency range when setting SAR power limitation + * @start_freq: start channel frequency in kHZ. For example, + * 2.4G channel 1 is represented as 2412000 + * @end_freq: end channel frequency in kHZ + */ +struct cfg80211_sar_freq_ranges { + u8 index; + 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 + * @chan_ranges: memory to hold the channel ranges. + */ +struct cfg80211_sar_capa { + enum nl80211_sar_type type; + u8 num_freq_ranges; + const struct cfg80211_sar_freq_ranges *freq_ranges; +}; + #if IS_ENABLED(CONFIG_CFG80211) /** * cfg80211_get_station - retrieve information about a given station @@ -4153,6 +4202,7 @@ 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); }; /* @@ -4919,6 +4969,8 @@ struct wiphy { u8 max_data_retry_count; + const struct cfg80211_sar_capa *sar_capa; + char priv[] __aligned(NETDEV_ALIGN); }; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ec148b3..df758ee 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4125,6 +4125,8 @@ struct ieee80211_ops { int (*reset_tid_config)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u8 tids); + int (*set_sar_specs)(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar); }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 0584e0d..2dcfed4 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1177,6 +1177,9 @@ * includes the contents of the frame. %NL80211_ATTR_ACK flag is included * if the recipient acknowledged the frame. * + * @NL80211_CMD_SET_SAR_SPECS: SAR power limitation configuration is + * passed using %NL80211_ATTR_SAR_SPEC. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1407,6 +1410,8 @@ enum nl80211_commands { NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS, + NL80211_CMD_SET_SAR_SPECS, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2513,6 +2518,10 @@ enum nl80211_commands { * @NL80211_ATTR_HE_6GHZ_CAPABILITY: HE 6 GHz Band Capability element (from * association request when used with NL80211_CMD_NEW_STATION). * + * @NL80211_ATTR_SAR_SPEC: SAR power limitation specification when + * used with %NL80211_CMD_SET_SAR_SPECS. It contains array of + * %nl80211_sar_specs_attrs. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2995,6 +3004,8 @@ enum nl80211_attrs { NL80211_ATTR_HE_6GHZ_CAPABILITY, + NL80211_ATTR_SAR_SPEC, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -7004,4 +7015,79 @@ enum nl80211_iftype_akm_attributes { NL80211_IFTYPE_AKM_ATTR_MAX = __NL80211_IFTYPE_AKM_ATTR_LAST - 1, }; +/** + * enum nl80211_sar_type - type of SAR specs + * + * @NL80211_SAR_TYPE_POWER: the value is specified in 0.25 dbm + * + */ +enum nl80211_sar_type { + NL80211_SAR_TYPE_POWER, + + /* add new type here */ + + /* Keep last */ + NUM_NL80211_SAR_TYPE, +}; + +/** + * nl80211_sar_attrs - Attributes for SAR spec + * + * @NL80211_SAR_ATTR_TYPE: Type of SAR: power or index + * + * @NL80211_SAR_ATTR_SPECS: Nested array of SAR power + * limit specifications. Each specification contains a set + * of %nl80211_sar_specs_attrs + * + * @__NL80211_SAR_ATTR_LAST: Internal + * @NL80211_SAR_ATTR_MAX: highest sar attribute + * + * These attributes are used with %NL80211_CMD_SET_SAR_SPEC + */ +enum nl80211_sar_attrs { + __NL80211_SAR_ATTR_INVALID, + + NL80211_SAR_ATTR_TYPE, + NL80211_SAR_ATTR_SPECS, + + __NL80211_SAR_ATTR_LAST, + NL80211_SAR_ATTR_MAX = __NL80211_SAR_ATTR_LAST - 1, +}; + +#define NL80211_SAR_ALL_FREQ_RNAGES 0xff +#define NUM_MAX_NL80211_SAR_FREQ_RANGES 0xfe + +/** + * nl80211_sar_specs_attrs - Attributes for SAR power limit specs + * + * @NL80211_SAR_ATTR_SPECS_POWER: Required (u32)value to specify the actual + * power limit value in units of 0.25 dBm if type is + * NL80211_SAR_TYPE_POWER. (i.e., a value of 44 represents 11 dBm) + * + * @NL80211_SAR_ATTR_SPECS_FREQ_RANGES_INDEX: optional (u32) value to specify the + * index of exported freq ranges table. If this attribute is not present, then + * the power is applied to all freq ranges, i.e, all bands + * + * @NL80211_SAR_ATTR_SPECS_START_FREQ: Required (u32) value to specify the start + * frequency of this range to register SAR capability to wihpy and the unit + * is kHZ + * + * @NL80211_SAR_ATTR_SPECS_END_FREQ: Required (u32) value to specify the end frequency + * of this range to register SAR capability to wiphy and the unit is kHZ + * + * @__NL80211_SAR_ATTR_SPECS_LAST: Internal + * @NL80211_SAR_ATTR_SPECS_MAX: highest sar specs attribute + */ +enum nl80211_sar_specs_attrs { + __NL80211_SAR_ATTR_SPECS_INVALID, + + NL80211_SAR_ATTR_SPECS_POWER, + NL80211_SAR_ATTR_SPECS_FREQ_RANGE_INDEX, + NL80211_SAR_ATTR_SPECS_START_FREQ, + NL80211_SAR_ATTR_SPECS_END_FREQ, + + __NL80211_SAR_ATTR_SPECS_LAST, + NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b4e39e3..cc74322 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3993,6 +3993,17 @@ static int ieee80211_reset_tid_config(struct wiphy *wiphy, 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, @@ -4096,4 +4107,5 @@ const struct cfg80211_ops mac80211_config_ops = { .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/net/wireless/nl80211.c b/net/wireless/nl80211.c index 52a35e7..9318b3a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -367,6 +367,19 @@ nl80211_tid_config_attr_policy[NL80211_TID_CONFIG_ATTR_MAX + 1] = { NLA_POLICY_NESTED(nl80211_txattr_policy), }; +static const struct nla_policy +sar_specs_policy[NL80211_SAR_ATTR_SPECS_MAX + 1] = { + [NL80211_SAR_ATTR_SPECS_POWER] = { .type = NLA_U8 }, + [NL80211_SAR_ATTR_SPECS_FREQ_RANGE_INDEX] = + NLA_POLICY_MAX(NLA_U8, NUM_MAX_NL80211_SAR_FREQ_RANGES), +}; + +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 }, @@ -675,6 +688,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_SCAN_FREQ_KHZ] = { .type = NLA_NESTED }, [NL80211_ATTR_HE_6GHZ_CAPABILITY] = NLA_POLICY_EXACT_LEN(sizeof(struct ieee80211_he_6ghz_capa)), + [NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy), }; /* policy for the key attributes */ @@ -1856,6 +1870,8 @@ static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev, goto nla_put_failure; } + CMD(set_sar_specs, SET_SAR_SPECS); + return i; nla_put_failure: return -ENOBUFS; @@ -2034,6 +2050,55 @@ nl80211_put_tid_config_support(struct cfg80211_registered_device *rdev, 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_u16(msg, NL80211_SAR_ATTR_TYPE, rdev->wiphy.sar_capa->type)) + goto fail; + + specs = nla_nest_start_noflag(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_noflag(msg, i + 1); + + nla_put_u32(msg, NL80211_SAR_ATTR_SPECS_START_FREQ, + rdev->wiphy.sar_capa->freq_ranges[i].start_freq); + + nla_put_u32(msg, NL80211_SAR_ATTR_SPECS_END_FREQ, + rdev->wiphy.sar_capa->freq_ranges[i].end_freq); + + nla_put_u8(msg, NL80211_SAR_ATTR_SPECS_FREQ_RANGE_INDEX, + rdev->wiphy.sar_capa->freq_ranges[i].index); + + 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; @@ -2598,6 +2663,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, if (nl80211_put_tid_config_support(rdev, msg)) goto nla_put_failure; + if (nl80211_put_sar_specs(rdev, msg)) + goto nla_put_failure; + /* done */ state->split_start = 0; break; @@ -14473,6 +14541,103 @@ static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb, } } +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; + u8 type, power, index, specs; + struct nlattr *spec_list; + int rem, err; + + if (!rdev->wiphy.sar_capa) + 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], + sar_policy, info->extack); + + if (!tb[NL80211_SAR_ATTR_TYPE]) + return -EINVAL; + + type = nla_get_u32(tb[NL80211_SAR_ATTR_TYPE]); + + if (!tb[NL80211_SAR_ATTR_SPECS]) + return -EINVAL; + + specs = 0; + nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem) + specs++; + + sar_spec = kzalloc(sizeof(*sar_spec) + + specs * sizeof(struct cfg80211_sar_sub_specs), + GFP_KERNEL); + if (!sar_spec) + return -ENOMEM; + + sar_spec->sub_specs = (struct cfg80211_sar_sub_specs *) + ((char *)sar_spec + sizeof(*sar_spec)); + specs = 0; + sar_spec->type = type; + + nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem) { + if (nla_parse(spec, + NL80211_SAR_ATTR_SPECS_MAX, + nla_data(spec_list), + nla_len(spec_list), + sar_specs_policy, + NULL)) { + err = -EINVAL; + goto error; + } + + /* for power type, power value must be presented */ + if (!spec[NL80211_SAR_ATTR_SPECS_POWER] && + type == NL80211_SAR_TYPE_POWER) { + err = -EINVAL; + goto error; + } + + power = nla_get_u8(spec[NL80211_SAR_ATTR_SPECS_POWER]); + sar_spec->sub_specs[specs].power = power; + + /* if NL80211_SAR_ATTR_SPECS_FREQ_RANGE_INDEX isn't present, + * then the power applies to all bands. But it's only valid + * for the first entry. + */ + if (!spec[NL80211_SAR_ATTR_SPECS_FREQ_RANGE_INDEX]) { + if (specs) { + err = -EINVAL; + goto error; + } else { + sar_spec->sub_specs[specs].freq_range_index = + NL80211_SAR_ALL_FREQ_RNAGES; + specs++; + break; + } + } + + index = nla_get_u8(spec[NL80211_SAR_ATTR_SPECS_FREQ_RANGE_INDEX]); + sar_spec->sub_specs[specs].freq_range_index = index; + specs++; + } + + sar_spec->num_sub_specs = specs; + + rdev->cur_cmd_info = info; + if (rdev->ops->set_sar_specs) + err = rdev_set_sar_specs(rdev, sar_spec); + else + err = -EOPNOTSUPP; + rdev->cur_cmd_info = NULL; +error: + kfree(sar_spec); + return err; +} + static const struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -15331,6 +15496,14 @@ static const struct genl_ops nl80211_ops[] = { .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_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_family nl80211_fam __ro_after_init = { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 950d574..d9931c5 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1356,4 +1356,16 @@ static inline int rdev_reset_tid_config(struct cfg80211_registered_device *rdev, 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 */ diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 6e218a0..116be64 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -3547,6 +3547,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
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 quries 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 03 00 15 00 00 00 38 00 26 81 08 00 01 00 00 00 00 00 2c 00 02 80 14 00 01 80 05 00 02 00 00 00 00 00 05 00 01 00 38 00 00 00 14 00 02 80 05 00 02 00 01 00 00 00 05 00 01 00 48 00 00 00 NL80211_CMD_SET_SAR_SPECS: 0x8c NL80211_ATTR_SAR_SPEC: 0x8126 (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> --- include/net/cfg80211.h | 52 +++++++++++++ include/net/mac80211.h | 2 + include/uapi/linux/nl80211.h | 86 +++++++++++++++++++++ net/mac80211/cfg.c | 12 +++ net/wireless/nl80211.c | 173 +++++++++++++++++++++++++++++++++++++++++++ net/wireless/rdev-ops.h | 12 +++ net/wireless/trace.h | 19 +++++ 7 files changed, 356 insertions(+)