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

Carl Huang cjhuang at codeaurora.org
Wed Nov 4 03:44:15 EST 2020


On 2020-09-28 20:36, Johannes Berg wrote:
> On Tue, 2020-09-22 at 13:49 +0800, 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
> 
> typo: queries
> 
sure, I'll fix it in v2.

>> 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
> 
> Heh, that's not super useful, though that doesn't matter much I guess 
> :)
> 
>> 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)
> 
> that makes more sense :)
> 
As this is an explanation of the message above, is it good to keep the 
message?

>> +/**
>> + * struct cfg80211_sar_sub_specs - sub specs limit
>> + * @power: value in 0.25dbm
> 
> I guess the documentation should state that it's a power limit, or
> something?
> 
>> + * @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;
> 
> Wouldn't it make more sense to just have sub_specs[] and inline? The
> pointers here are bigger than many of the sub-specs I guess...
> 
> Also, is num_sub_specs even needed? It seems we could just require that
> userspace sends all of them all the time?
> 
Agree as long as user space sends all of them.

>> +struct cfg80211_sar_freq_ranges {
>> +	u8 index;
> 
> Does an index here make sense?
> 
With agreement from Google, it's OK to remove it.

>> +	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;
> 
> You end up with an array here, so some kind of index is implicit, the
> array index. I don't see why you'd want to have a separate index
> *within* the frequency range, then you just have to check those make
> sense, etc.?
> 
>> + * @NL80211_ATTR_SAR_SPEC: SAR power limitation specification when
>> + *	used with %NL80211_CMD_SET_SAR_SPECS. It contains array of
>> + *	%nl80211_sar_specs_attrs.
> 
> I think I know what you mean (a nested array where each item has
> attributes from &enum nl80211_sar_specs_attrs), but could state that a
> bit clearer.
> 

I'll try to describe it more clear in v2.

>> +#define NL80211_SAR_ALL_FREQ_RNAGES	0xff
> 
> typo
> 
sure, will fix in v2.

>> +#define NUM_MAX_NL80211_SAR_FREQ_RANGES 0xfe
> 
> but I'm not sure what these are used for in the first place, they seem
> more like internal implementation details?
> 
In v1 design, userspace doesn't need to specify all the sub bands if the 
a power
limitation applies to all sub bands, so NL80211_SAR_ALL_FREQ_RNAGES is 
defined
for this purpose. To distinguish from NL80211_SAR_ALL_FREQ_RNAGES, the 
maximum
number of freq ranges should not exceed  (NL80211_SAR_ALL_FREQ_RNAGES - 
1), so
define NUM_MAX_NL80211_SAR_FREQ_RANGES as 0xfe.

It's right these two definitions are internal, userspace doesn't use 
them.

Again as user-space will sends all the ranges, let me remove these 2 
definitions.

>> +/**
>> + * 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
> 
> Especially since this says "[i]f this attribute is not present".
> 
> Does that make sense though? It seems possible to just use the nesting
> entry as the index, and just require userspace to set up all indexes if
> it wants to apply to all of them, why bother having to deal with that
> case in the kernel and perhaps even drivers?
> 

Ok, let me drop it in v2.

Johannes, want to confirm with you "nesting entry as the index". Do you 
mean that kernel
take the first entry as index 0, then second entry as index 1 and etc?


>> + * @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
> 
> typo: wiphy
> 
>> + *	is kHZ
> 
> kHz
> 
Will fix it in v2.

>> + *
>> + * @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
> 
> kHz
> 
> But hm, are you mixing the capability advertisement and the actual
> request into the same attributes? Perhaps that makes sense, but the
> documentation could be a bit clearer IMHO.
> 
>> +++ b/net/mac80211/cfg.c
> 
> The mac80211 bits could probably be in a separate patch, just for
> clarity.
> 
>> +static const struct nla_policy
>> +sar_specs_policy[NL80211_SAR_ATTR_SPECS_MAX + 1] = {
>> +		[NL80211_SAR_ATTR_SPECS_POWER] = { .type = NLA_U8 },
> 
> double indentation?
> 
>> +		[NL80211_SAR_ATTR_SPECS_FREQ_RANGE_INDEX] =
>> +			NLA_POLICY_MAX(NLA_U8, NUM_MAX_NL80211_SAR_FREQ_RANGES),
> 
> I don't see a good reason to limit to 0xfe? The kernel could just use a
> wider datatype internally.
> 
> Anyway, I'm not sure the attribute is even needed, see above.
> 
If userspace has to sends all the sub bands, then
NL80211_SAR_ALL_FREQ_RNAGES is not required, and therefore 
NUM_MAX_NL80211_SAR_FREQ_RANGES
is also not required.

I'll delete these 2 definitions, and change policy to { .type = NLA_U8 }

>> @@ -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);
> 
> Don't add to _unsplit() please.
> 
Do you mean add it to below block?
if (state->split) {
...
CMD(set_sar_specs, SET_SAR_SPECS);
}


>> +	specs = nla_nest_start_noflag(msg, NL80211_SAR_ATTR_SPECS);
> 
> Don't use _noflag() please, that's just for backward compatibility.
> 
sure.

>> +	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);
> 
> Same here.
> 
>> @@ -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;
> 
> Perhaps better to have a separate message (case entry) since it might
> get fairly big?
> 
>> +	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));
> 
> If you anyway set the pointer there then you can actually just make it 
> a
> [] array instead of having the pointer in the first place ... would be
> equivalent but save you from these contortions :)
> 
Right.

>> +	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;
>> +		}
> 
> Somewhere you should probably also check that the type matches the
> advertised type.
> 
Agree.

>> +		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;
>> +			}
>> +		}
> 
> Yeah... not sure I see this as being worth it, vs. userspace just
> filling all the possible entries.
> 
Yes, let me remove this logic in v2.

>> +		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;
> 
> Might be worth checking this early and avoiding the allocation and all?
> 
> OTOH, that gives you error checking even if the driver was completely
> confused and advertised sar specs without the method ...
> 
Here, it checks whether mac80211 layer implements the function. It's 
ieee80211_set_sar_specs
to check whether driver has implemented set_sar function.

I'll move the check to the beginning of this function.

> johannes



More information about the ath10k mailing list