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