diff mbox series

[RFC,1/2] nl80211: add common API to configure SAR power limitations.

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

Commit Message

Carl Huang Sept. 22, 2020, 5:36 a.m. UTC
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(+)

Comments

Carl Huang Sept. 22, 2020, 5:47 a.m. UTC | #1
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
Kalle Valo Sept. 22, 2020, 8:09 a.m. UTC | #2
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.
Brian Norris Nov. 4, 2020, 2 a.m. UTC | #3
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
Carl Huang Nov. 4, 2020, 7:32 a.m. UTC | #4
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
Brian Norris Nov. 4, 2020, 5:44 p.m. UTC | #5
+ 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
Kalle Valo Nov. 5, 2020, 8:35 a.m. UTC | #6
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
Carl Huang Nov. 5, 2020, 11:10 a.m. UTC | #7
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.
Carl Huang Nov. 5, 2020, 11:17 a.m. UTC | #8
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
Brian Norris Nov. 5, 2020, 6:25 p.m. UTC | #9
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.
Carl Huang Nov. 6, 2020, 10:11 a.m. UTC | #10
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 mbox series

Patch

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