a47e29794cc890eb495b964d7f169dd6d580ecbd
[openwrt/staging/linusw.git] /
1 From: Carl Huang <cjhuang@codeaurora.org>
2 Date: Thu, 3 Dec 2020 05:37:26 -0500
3 Subject: [PATCH] nl80211: add common API to configure SAR power limitations
4
5 NL80211_CMD_SET_SAR_SPECS is added to configure SAR from
6 user space. NL80211_ATTR_SAR_SPEC is used to pass the SAR
7 power specification when used with NL80211_CMD_SET_SAR_SPECS.
8
9 Wireless driver needs to register SAR type, supported frequency
10 ranges to wiphy, so user space can query it. The index in
11 frequency range is used to specify which sub band the power
12 limitation applies to. The SAR type is for compatibility, so later
13 other SAR mechanism can be implemented without breaking the user
14 space SAR applications.
15
16 Normal process is user space queries the SAR capability, and
17 gets the index of supported frequency ranges and associates the
18 power limitation with this index and sends to kernel.
19
20 Here is an example of message send to kernel:
21 8c 00 00 00 08 00 01 00 00 00 00 00 38 00 2b 81
22 08 00 01 00 00 00 00 00 2c 00 02 80 14 00 00 80
23 08 00 02 00 00 00 00 00 08 00 01 00 38 00 00 00
24 14 00 01 80 08 00 02 00 01 00 00 00 08 00 01 00
25 48 00 00 00
26
27 NL80211_CMD_SET_SAR_SPECS: 0x8c
28 NL80211_ATTR_WIPHY: 0x01(phy idx is 0)
29 NL80211_ATTR_SAR_SPEC: 0x812b (NLA_NESTED)
30 NL80211_SAR_ATTR_TYPE: 0x00 (NL80211_SAR_TYPE_POWER)
31 NL80211_SAR_ATTR_SPECS: 0x8002 (NLA_NESTED)
32 freq range 0 power: 0x38 in 0.25dbm unit (14dbm)
33 freq range 1 power: 0x48 in 0.25dbm unit (18dbm)
34
35 Signed-off-by: Carl Huang <cjhuang@codeaurora.org>
36 Reviewed-by: Brian Norris <briannorris@chromium.org>
37 Reviewed-by: Abhishek Kumar <kuabhs@chromium.org>
38 Link: https://lore.kernel.org/r/20201203103728.3034-2-cjhuang@codeaurora.org
39 [minor edits, NLA parse cleanups]
40 Signed-off-by: Johannes Berg <johannes.berg@intel.com>
41 ---
42
43 --- a/include/net/cfg80211.h
44 +++ b/include/net/cfg80211.h
45 @@ -1737,6 +1737,54 @@ struct station_info {
46 u8 connected_to_as;
47 };
48
49 +/**
50 + * struct cfg80211_sar_sub_specs - sub specs limit
51 + * @power: power limitation in 0.25dbm
52 + * @freq_range_index: index the power limitation applies to
53 + */
54 +struct cfg80211_sar_sub_specs {
55 + s32 power;
56 + u32 freq_range_index;
57 +};
58 +
59 +/**
60 + * struct cfg80211_sar_specs - sar limit specs
61 + * @type: it's set with power in 0.25dbm or other types
62 + * @num_sub_specs: number of sar sub specs
63 + * @sub_specs: memory to hold the sar sub specs
64 + */
65 +struct cfg80211_sar_specs {
66 + enum nl80211_sar_type type;
67 + u32 num_sub_specs;
68 + struct cfg80211_sar_sub_specs sub_specs[];
69 +};
70 +
71 +
72 +/**
73 + * @struct cfg80211_sar_chan_ranges - sar frequency ranges
74 + * @start_freq: start range edge frequency
75 + * @end_freq: end range edge frequency
76 + */
77 +struct cfg80211_sar_freq_ranges {
78 + u32 start_freq;
79 + u32 end_freq;
80 +};
81 +
82 +/**
83 + * struct cfg80211_sar_capa - sar limit capability
84 + * @type: it's set via power in 0.25dbm or other types
85 + * @num_freq_ranges: number of frequency ranges
86 + * @freq_ranges: memory to hold the freq ranges.
87 + *
88 + * Note: WLAN driver may append new ranges or split an existing
89 + * range to small ones and then append them.
90 + */
91 +struct cfg80211_sar_capa {
92 + enum nl80211_sar_type type;
93 + u32 num_freq_ranges;
94 + const struct cfg80211_sar_freq_ranges *freq_ranges;
95 +};
96 +
97 #if IS_ENABLED(CPTCFG_CFG80211)
98 /**
99 * cfg80211_get_station - retrieve information about a given station
100 @@ -4259,6 +4307,8 @@ struct cfg80211_ops {
101 struct cfg80211_tid_config *tid_conf);
102 int (*reset_tid_config)(struct wiphy *wiphy, struct net_device *dev,
103 const u8 *peer, u8 tids);
104 + int (*set_sar_specs)(struct wiphy *wiphy,
105 + struct cfg80211_sar_specs *sar);
106 };
107
108 /*
109 @@ -5030,6 +5080,8 @@ struct wiphy {
110
111 u8 max_data_retry_count;
112
113 + const struct cfg80211_sar_capa *sar_capa;
114 +
115 char priv[] __aligned(NETDEV_ALIGN);
116 };
117
118 --- a/net/wireless/nl80211.c
119 +++ b/net/wireless/nl80211.c
120 @@ -405,6 +405,18 @@ nl80211_unsol_bcast_probe_resp_policy[NL
121 .len = IEEE80211_MAX_DATA_LEN }
122 };
123
124 +static const struct nla_policy
125 +sar_specs_policy[NL80211_SAR_ATTR_SPECS_MAX + 1] = {
126 + [NL80211_SAR_ATTR_SPECS_POWER] = { .type = NLA_S32 },
127 + [NL80211_SAR_ATTR_SPECS_RANGE_INDEX] = {.type = NLA_U32 },
128 +};
129 +
130 +static const struct nla_policy
131 +sar_policy[NL80211_SAR_ATTR_MAX + 1] = {
132 + [NL80211_SAR_ATTR_TYPE] = NLA_POLICY_MAX(NLA_U32, NUM_NL80211_SAR_TYPE),
133 + [NL80211_SAR_ATTR_SPECS] = NLA_POLICY_NESTED_ARRAY(sar_specs_policy),
134 +};
135 +
136 static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
137 [0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD },
138 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
139 @@ -739,6 +751,7 @@ static const struct nla_policy nl80211_p
140 [NL80211_ATTR_SAE_PWE] =
141 NLA_POLICY_RANGE(NLA_U8, NL80211_SAE_PWE_HUNT_AND_PECK,
142 NL80211_SAE_PWE_BOTH),
143 + [NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy),
144 [NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT },
145 };
146
147 @@ -2117,6 +2130,56 @@ fail:
148 return -ENOBUFS;
149 }
150
151 +static int
152 +nl80211_put_sar_specs(struct cfg80211_registered_device *rdev,
153 + struct sk_buff *msg)
154 +{
155 + struct nlattr *sar_capa, *specs, *sub_freq_range;
156 + u8 num_freq_ranges;
157 + int i;
158 +
159 + if (!rdev->wiphy.sar_capa)
160 + return 0;
161 +
162 + num_freq_ranges = rdev->wiphy.sar_capa->num_freq_ranges;
163 +
164 + sar_capa = nla_nest_start(msg, NL80211_ATTR_SAR_SPEC);
165 + if (!sar_capa)
166 + return -ENOSPC;
167 +
168 + if (nla_put_u32(msg, NL80211_SAR_ATTR_TYPE, rdev->wiphy.sar_capa->type))
169 + goto fail;
170 +
171 + specs = nla_nest_start(msg, NL80211_SAR_ATTR_SPECS);
172 + if (!specs)
173 + goto fail;
174 +
175 + /* report supported freq_ranges */
176 + for (i = 0; i < num_freq_ranges; i++) {
177 + sub_freq_range = nla_nest_start(msg, i + 1);
178 + if (!sub_freq_range)
179 + goto fail;
180 +
181 + if (nla_put_u32(msg, NL80211_SAR_ATTR_SPECS_START_FREQ,
182 + rdev->wiphy.sar_capa->freq_ranges[i].start_freq))
183 + goto fail;
184 +
185 + if (nla_put_u32(msg, NL80211_SAR_ATTR_SPECS_END_FREQ,
186 + rdev->wiphy.sar_capa->freq_ranges[i].end_freq))
187 + goto fail;
188 +
189 + nla_nest_end(msg, sub_freq_range);
190 + }
191 +
192 + nla_nest_end(msg, specs);
193 + nla_nest_end(msg, sar_capa);
194 +
195 + return 0;
196 +fail:
197 + nla_nest_cancel(msg, sar_capa);
198 + return -ENOBUFS;
199 +}
200 +
201 struct nl80211_dump_wiphy_state {
202 s64 filter_wiphy;
203 long start;
204 @@ -2366,6 +2429,8 @@ static int nl80211_send_wiphy(struct cfg
205 CMD(set_multicast_to_unicast, SET_MULTICAST_TO_UNICAST);
206 CMD(update_connect_params, UPDATE_CONNECT_PARAMS);
207 CMD(update_ft_ies, UPDATE_FT_IES);
208 + if (rdev->wiphy.sar_capa)
209 + CMD(set_sar_specs, SET_SAR_SPECS);
210 }
211 #undef CMD
212
213 @@ -2691,6 +2756,11 @@ static int nl80211_send_wiphy(struct cfg
214
215 if (nl80211_put_tid_config_support(rdev, msg))
216 goto nla_put_failure;
217 + state->split_start++;
218 + break;
219 + case 16:
220 + if (nl80211_put_sar_specs(rdev, msg))
221 + goto nla_put_failure;
222
223 /* done */
224 state->split_start = 0;
225 @@ -14712,6 +14782,111 @@ static void nl80211_post_doit(__genl_con
226 }
227 }
228
229 +static int nl80211_set_sar_sub_specs(struct cfg80211_registered_device *rdev,
230 + struct cfg80211_sar_specs *sar_specs,
231 + struct nlattr *spec[], int index)
232 +{
233 + u32 range_index, i;
234 +
235 + if (!sar_specs || !spec)
236 + return -EINVAL;
237 +
238 + if (!spec[NL80211_SAR_ATTR_SPECS_POWER] ||
239 + !spec[NL80211_SAR_ATTR_SPECS_RANGE_INDEX])
240 + return -EINVAL;
241 +
242 + range_index = nla_get_u32(spec[NL80211_SAR_ATTR_SPECS_RANGE_INDEX]);
243 +
244 + /* check if range_index exceeds num_freq_ranges */
245 + if (range_index >= rdev->wiphy.sar_capa->num_freq_ranges)
246 + return -EINVAL;
247 +
248 + /* check if range_index duplicates */
249 + for (i = 0; i < index; i++) {
250 + if (sar_specs->sub_specs[i].freq_range_index == range_index)
251 + return -EINVAL;
252 + }
253 +
254 + sar_specs->sub_specs[index].power =
255 + nla_get_s32(spec[NL80211_SAR_ATTR_SPECS_POWER]);
256 +
257 + sar_specs->sub_specs[index].freq_range_index = range_index;
258 +
259 + return 0;
260 +}
261 +
262 +static int nl80211_set_sar_specs(struct sk_buff *skb, struct genl_info *info)
263 +{
264 + struct cfg80211_registered_device *rdev = info->user_ptr[0];
265 + struct nlattr *spec[NL80211_SAR_ATTR_SPECS_MAX + 1];
266 + struct nlattr *tb[NL80211_SAR_ATTR_MAX + 1];
267 + struct cfg80211_sar_specs *sar_spec;
268 + enum nl80211_sar_type type;
269 + struct nlattr *spec_list;
270 + u32 specs;
271 + int rem, err;
272 +
273 + if (!rdev->wiphy.sar_capa || !rdev->ops->set_sar_specs)
274 + return -EOPNOTSUPP;
275 +
276 + if (!info->attrs[NL80211_ATTR_SAR_SPEC])
277 + return -EINVAL;
278 +
279 + nla_parse_nested(tb, NL80211_SAR_ATTR_MAX,
280 + info->attrs[NL80211_ATTR_SAR_SPEC],
281 + NULL, NULL);
282 +
283 + if (!tb[NL80211_SAR_ATTR_TYPE] || !tb[NL80211_SAR_ATTR_SPECS])
284 + return -EINVAL;
285 +
286 + type = nla_get_u32(tb[NL80211_SAR_ATTR_TYPE]);
287 + if (type != rdev->wiphy.sar_capa->type)
288 + return -EINVAL;
289 +
290 + specs = 0;
291 + nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem)
292 + specs++;
293 +
294 + if (specs > rdev->wiphy.sar_capa->num_freq_ranges)
295 + return -EINVAL;
296 +
297 + sar_spec = kzalloc(sizeof(*sar_spec) +
298 + specs * sizeof(struct cfg80211_sar_sub_specs),
299 + GFP_KERNEL);
300 + if (!sar_spec)
301 + return -ENOMEM;
302 +
303 + sar_spec->type = type;
304 + specs = 0;
305 + nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem) {
306 + nla_parse_nested(spec, NL80211_SAR_ATTR_SPECS_MAX,
307 + spec_list, NULL, NULL);
308 +
309 + switch (type) {
310 + case NL80211_SAR_TYPE_POWER:
311 + if (nl80211_set_sar_sub_specs(rdev, sar_spec,
312 + spec, specs)) {
313 + err = -EINVAL;
314 + goto error;
315 + }
316 + break;
317 + default:
318 + err = -EINVAL;
319 + goto error;
320 + }
321 + specs++;
322 + }
323 +
324 + sar_spec->num_sub_specs = specs;
325 +
326 + rdev->cur_cmd_info = info;
327 + err = rdev_set_sar_specs(rdev, sar_spec);
328 + rdev->cur_cmd_info = NULL;
329 +error:
330 + kfree(sar_spec);
331 + return err;
332 +}
333 +
334 static __genl_const struct genl_ops nl80211_ops[] = {
335 {
336 .cmd = NL80211_CMD_GET_WIPHY,
337 @@ -15575,6 +15750,14 @@ static const struct genl_small_ops nl802
338 .internal_flags = NL80211_FLAG_NEED_NETDEV |
339 NL80211_FLAG_NEED_RTNL,
340 },
341 + {
342 + .cmd = NL80211_CMD_SET_SAR_SPECS,
343 + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
344 + .doit = nl80211_set_sar_specs,
345 + .flags = GENL_UNS_ADMIN_PERM,
346 + .internal_flags = NL80211_FLAG_NEED_WIPHY |
347 + NL80211_FLAG_NEED_RTNL,
348 + },
349 };
350
351 static struct genl_family nl80211_fam __genl_ro_after_init = {
352 --- a/net/wireless/rdev-ops.h
353 +++ b/net/wireless/rdev-ops.h
354 @@ -1356,4 +1356,16 @@ static inline int rdev_reset_tid_config(
355 return ret;
356 }
357
358 +static inline int rdev_set_sar_specs(struct cfg80211_registered_device *rdev,
359 + struct cfg80211_sar_specs *sar)
360 +{
361 + int ret;
362 +
363 + trace_rdev_set_sar_specs(&rdev->wiphy, sar);
364 + ret = rdev->ops->set_sar_specs(&rdev->wiphy, sar);
365 + trace_rdev_return_int(&rdev->wiphy, ret);
366 +
367 + return ret;
368 +}
369 +
370 #endif /* __CFG80211_RDEV_OPS */
371 --- a/net/wireless/trace.h
372 +++ b/net/wireless/trace.h
373 @@ -3551,6 +3551,25 @@ TRACE_EVENT(rdev_reset_tid_config,
374 TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", tids: 0x%x",
375 WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tids)
376 );
377 +
378 +TRACE_EVENT(rdev_set_sar_specs,
379 + TP_PROTO(struct wiphy *wiphy, struct cfg80211_sar_specs *sar),
380 + TP_ARGS(wiphy, sar),
381 + TP_STRUCT__entry(
382 + WIPHY_ENTRY
383 + __field(u16, type)
384 + __field(u16, num)
385 + ),
386 + TP_fast_assign(
387 + WIPHY_ASSIGN;
388 + __entry->type = sar->type;
389 + __entry->num = sar->num_sub_specs;
390 +
391 + ),
392 + TP_printk(WIPHY_PR_FMT ", Set type:%d, num_specs:%d",
393 + WIPHY_PR_ARG, __entry->type, __entry->num)
394 +);
395 +
396 #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
397
398 #undef TRACE_INCLUDE_PATH