1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Off-channel operation helpers |
4 | * |
5 | * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> |
6 | * Copyright 2004, Instant802 Networks, Inc. |
7 | * Copyright 2005, Devicescape Software, Inc. |
8 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
9 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> |
10 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> |
11 | * Copyright (C) 2019, 2022-2023 Intel Corporation |
12 | */ |
13 | #include <linux/export.h> |
14 | #include <net/mac80211.h> |
15 | #include "ieee80211_i.h" |
16 | #include "driver-ops.h" |
17 | |
18 | /* |
19 | * Tell our hardware to disable PS. |
20 | * Optionally inform AP that we will go to sleep so that it will buffer |
21 | * the frames while we are doing off-channel work. This is optional |
22 | * because we *may* be doing work on-operating channel, and want our |
23 | * hardware unconditionally awake, but still let the AP send us normal frames. |
24 | */ |
25 | static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata) |
26 | { |
27 | struct ieee80211_local *local = sdata->local; |
28 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
29 | bool offchannel_ps_enabled = false; |
30 | |
31 | /* FIXME: what to do when local->pspolling is true? */ |
32 | |
33 | del_timer_sync(timer: &local->dynamic_ps_timer); |
34 | del_timer_sync(timer: &ifmgd->bcn_mon_timer); |
35 | del_timer_sync(timer: &ifmgd->conn_mon_timer); |
36 | |
37 | wiphy_work_cancel(wiphy: local->hw.wiphy, work: &local->dynamic_ps_enable_work); |
38 | |
39 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { |
40 | offchannel_ps_enabled = true; |
41 | local->hw.conf.flags &= ~IEEE80211_CONF_PS; |
42 | ieee80211_hw_config(local, changed: IEEE80211_CONF_CHANGE_PS); |
43 | } |
44 | |
45 | if (!offchannel_ps_enabled || |
46 | !ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) |
47 | /* |
48 | * If power save was enabled, no need to send a nullfunc |
49 | * frame because AP knows that we are sleeping. But if the |
50 | * hardware is creating the nullfunc frame for power save |
51 | * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not |
52 | * enabled) and power save was enabled, the firmware just |
53 | * sent a null frame with power save disabled. So we need |
54 | * to send a new nullfunc frame to inform the AP that we |
55 | * are again sleeping. |
56 | */ |
57 | ieee80211_send_nullfunc(local, sdata, powersave: true); |
58 | } |
59 | |
60 | /* inform AP that we are awake again */ |
61 | static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata) |
62 | { |
63 | struct ieee80211_local *local = sdata->local; |
64 | |
65 | if (!local->ps_sdata) |
66 | ieee80211_send_nullfunc(local, sdata, powersave: false); |
67 | else if (local->hw.conf.dynamic_ps_timeout > 0) { |
68 | /* |
69 | * the dynamic_ps_timer had been running before leaving the |
70 | * operating channel, restart the timer now and send a nullfunc |
71 | * frame to inform the AP that we are awake so that AP sends |
72 | * the buffered packets (if any). |
73 | */ |
74 | ieee80211_send_nullfunc(local, sdata, powersave: false); |
75 | mod_timer(timer: &local->dynamic_ps_timer, expires: jiffies + |
76 | msecs_to_jiffies(m: local->hw.conf.dynamic_ps_timeout)); |
77 | } |
78 | |
79 | ieee80211_sta_reset_beacon_monitor(sdata); |
80 | ieee80211_sta_reset_conn_monitor(sdata); |
81 | } |
82 | |
83 | void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) |
84 | { |
85 | struct ieee80211_sub_if_data *sdata; |
86 | |
87 | lockdep_assert_wiphy(local->hw.wiphy); |
88 | |
89 | if (WARN_ON(!local->emulate_chanctx)) |
90 | return; |
91 | |
92 | /* |
93 | * notify the AP about us leaving the channel and stop all |
94 | * STA interfaces. |
95 | */ |
96 | |
97 | /* |
98 | * Stop queues and transmit all frames queued by the driver |
99 | * before sending nullfunc to enable powersave at the AP. |
100 | */ |
101 | ieee80211_stop_queues_by_reason(hw: &local->hw, queues: IEEE80211_MAX_QUEUE_MAP, |
102 | reason: IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, |
103 | refcounted: false); |
104 | ieee80211_flush_queues(local, NULL, drop: false); |
105 | |
106 | list_for_each_entry(sdata, &local->interfaces, list) { |
107 | if (!ieee80211_sdata_running(sdata)) |
108 | continue; |
109 | |
110 | if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE || |
111 | sdata->vif.type == NL80211_IFTYPE_NAN) |
112 | continue; |
113 | |
114 | if (sdata->vif.type != NL80211_IFTYPE_MONITOR) |
115 | set_bit(nr: SDATA_STATE_OFFCHANNEL, addr: &sdata->state); |
116 | |
117 | /* Check to see if we should disable beaconing. */ |
118 | if (sdata->vif.bss_conf.enable_beacon) { |
119 | set_bit(nr: SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, |
120 | addr: &sdata->state); |
121 | sdata->vif.bss_conf.enable_beacon = false; |
122 | ieee80211_link_info_change_notify( |
123 | sdata, link: &sdata->deflink, |
124 | changed: BSS_CHANGED_BEACON_ENABLED); |
125 | } |
126 | |
127 | if (sdata->vif.type == NL80211_IFTYPE_STATION && |
128 | sdata->u.mgd.associated) |
129 | ieee80211_offchannel_ps_enable(sdata); |
130 | } |
131 | } |
132 | |
133 | void ieee80211_offchannel_return(struct ieee80211_local *local) |
134 | { |
135 | struct ieee80211_sub_if_data *sdata; |
136 | |
137 | lockdep_assert_wiphy(local->hw.wiphy); |
138 | |
139 | if (WARN_ON(!local->emulate_chanctx)) |
140 | return; |
141 | |
142 | list_for_each_entry(sdata, &local->interfaces, list) { |
143 | if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) |
144 | continue; |
145 | |
146 | if (sdata->vif.type != NL80211_IFTYPE_MONITOR) |
147 | clear_bit(nr: SDATA_STATE_OFFCHANNEL, addr: &sdata->state); |
148 | |
149 | if (!ieee80211_sdata_running(sdata)) |
150 | continue; |
151 | |
152 | /* Tell AP we're back */ |
153 | if (sdata->vif.type == NL80211_IFTYPE_STATION && |
154 | sdata->u.mgd.associated) |
155 | ieee80211_offchannel_ps_disable(sdata); |
156 | |
157 | if (test_and_clear_bit(nr: SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, |
158 | addr: &sdata->state)) { |
159 | sdata->vif.bss_conf.enable_beacon = true; |
160 | ieee80211_link_info_change_notify( |
161 | sdata, link: &sdata->deflink, |
162 | changed: BSS_CHANGED_BEACON_ENABLED); |
163 | } |
164 | } |
165 | |
166 | ieee80211_wake_queues_by_reason(hw: &local->hw, queues: IEEE80211_MAX_QUEUE_MAP, |
167 | reason: IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, |
168 | refcounted: false); |
169 | } |
170 | |
171 | static void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc) |
172 | { |
173 | /* was never transmitted */ |
174 | if (roc->frame) { |
175 | cfg80211_mgmt_tx_status(wdev: &roc->sdata->wdev, cookie: roc->mgmt_tx_cookie, |
176 | buf: roc->frame->data, len: roc->frame->len, |
177 | ack: false, GFP_KERNEL); |
178 | ieee80211_free_txskb(hw: &roc->sdata->local->hw, skb: roc->frame); |
179 | } |
180 | |
181 | if (!roc->mgmt_tx_cookie) |
182 | cfg80211_remain_on_channel_expired(wdev: &roc->sdata->wdev, |
183 | cookie: roc->cookie, chan: roc->chan, |
184 | GFP_KERNEL); |
185 | else |
186 | cfg80211_tx_mgmt_expired(wdev: &roc->sdata->wdev, |
187 | cookie: roc->mgmt_tx_cookie, |
188 | chan: roc->chan, GFP_KERNEL); |
189 | |
190 | list_del(entry: &roc->list); |
191 | kfree(objp: roc); |
192 | } |
193 | |
194 | static unsigned long ieee80211_end_finished_rocs(struct ieee80211_local *local, |
195 | unsigned long now) |
196 | { |
197 | struct ieee80211_roc_work *roc, *tmp; |
198 | long remaining_dur_min = LONG_MAX; |
199 | |
200 | lockdep_assert_wiphy(local->hw.wiphy); |
201 | |
202 | list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { |
203 | long remaining; |
204 | |
205 | if (!roc->started) |
206 | break; |
207 | |
208 | remaining = roc->start_time + |
209 | msecs_to_jiffies(m: roc->duration) - |
210 | now; |
211 | |
212 | /* In case of HW ROC, it is possible that the HW finished the |
213 | * ROC session before the actual requested time. In such a case |
214 | * end the ROC session (disregarding the remaining time). |
215 | */ |
216 | if (roc->abort || roc->hw_begun || remaining <= 0) |
217 | ieee80211_roc_notify_destroy(roc); |
218 | else |
219 | remaining_dur_min = min(remaining_dur_min, remaining); |
220 | } |
221 | |
222 | return remaining_dur_min; |
223 | } |
224 | |
225 | static bool ieee80211_recalc_sw_work(struct ieee80211_local *local, |
226 | unsigned long now) |
227 | { |
228 | long dur = ieee80211_end_finished_rocs(local, now); |
229 | |
230 | if (dur == LONG_MAX) |
231 | return false; |
232 | |
233 | wiphy_delayed_work_queue(wiphy: local->hw.wiphy, dwork: &local->roc_work, delay: dur); |
234 | return true; |
235 | } |
236 | |
237 | static void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc, |
238 | unsigned long start_time) |
239 | { |
240 | if (WARN_ON(roc->notified)) |
241 | return; |
242 | |
243 | roc->start_time = start_time; |
244 | roc->started = true; |
245 | |
246 | if (roc->mgmt_tx_cookie) { |
247 | if (!WARN_ON(!roc->frame)) { |
248 | ieee80211_tx_skb_tid_band(sdata: roc->sdata, skb: roc->frame, tid: 7, |
249 | band: roc->chan->band); |
250 | roc->frame = NULL; |
251 | } |
252 | } else { |
253 | cfg80211_ready_on_channel(wdev: &roc->sdata->wdev, cookie: roc->cookie, |
254 | chan: roc->chan, duration: roc->req_duration, |
255 | GFP_KERNEL); |
256 | } |
257 | |
258 | roc->notified = true; |
259 | } |
260 | |
261 | static void ieee80211_hw_roc_start(struct wiphy *wiphy, struct wiphy_work *work) |
262 | { |
263 | struct ieee80211_local *local = |
264 | container_of(work, struct ieee80211_local, hw_roc_start); |
265 | struct ieee80211_roc_work *roc; |
266 | |
267 | lockdep_assert_wiphy(local->hw.wiphy); |
268 | |
269 | list_for_each_entry(roc, &local->roc_list, list) { |
270 | if (!roc->started) |
271 | break; |
272 | |
273 | roc->hw_begun = true; |
274 | ieee80211_handle_roc_started(roc, start_time: local->hw_roc_start_time); |
275 | } |
276 | } |
277 | |
278 | void ieee80211_ready_on_channel(struct ieee80211_hw *hw) |
279 | { |
280 | struct ieee80211_local *local = hw_to_local(hw); |
281 | |
282 | local->hw_roc_start_time = jiffies; |
283 | |
284 | trace_api_ready_on_channel(local); |
285 | |
286 | wiphy_work_queue(wiphy: hw->wiphy, work: &local->hw_roc_start); |
287 | } |
288 | EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel); |
289 | |
290 | static void _ieee80211_start_next_roc(struct ieee80211_local *local) |
291 | { |
292 | struct ieee80211_roc_work *roc, *tmp; |
293 | enum ieee80211_roc_type type; |
294 | u32 min_dur, max_dur; |
295 | |
296 | lockdep_assert_wiphy(local->hw.wiphy); |
297 | |
298 | if (WARN_ON(list_empty(&local->roc_list))) |
299 | return; |
300 | |
301 | roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, |
302 | list); |
303 | |
304 | if (WARN_ON(roc->started)) |
305 | return; |
306 | |
307 | min_dur = roc->duration; |
308 | max_dur = roc->duration; |
309 | type = roc->type; |
310 | |
311 | list_for_each_entry(tmp, &local->roc_list, list) { |
312 | if (tmp == roc) |
313 | continue; |
314 | if (tmp->sdata != roc->sdata || tmp->chan != roc->chan) |
315 | break; |
316 | max_dur = max(tmp->duration, max_dur); |
317 | min_dur = min(tmp->duration, min_dur); |
318 | type = max(tmp->type, type); |
319 | } |
320 | |
321 | if (local->ops->remain_on_channel) { |
322 | int ret = drv_remain_on_channel(local, sdata: roc->sdata, chan: roc->chan, |
323 | duration: max_dur, type); |
324 | |
325 | if (ret) { |
326 | wiphy_warn(local->hw.wiphy, |
327 | "failed to start next HW ROC (%d)\n" , ret); |
328 | /* |
329 | * queue the work struct again to avoid recursion |
330 | * when multiple failures occur |
331 | */ |
332 | list_for_each_entry(tmp, &local->roc_list, list) { |
333 | if (tmp->sdata != roc->sdata || |
334 | tmp->chan != roc->chan) |
335 | break; |
336 | tmp->started = true; |
337 | tmp->abort = true; |
338 | } |
339 | wiphy_work_queue(wiphy: local->hw.wiphy, work: &local->hw_roc_done); |
340 | return; |
341 | } |
342 | |
343 | /* we'll notify about the start once the HW calls back */ |
344 | list_for_each_entry(tmp, &local->roc_list, list) { |
345 | if (tmp->sdata != roc->sdata || tmp->chan != roc->chan) |
346 | break; |
347 | tmp->started = true; |
348 | } |
349 | } else { |
350 | /* If actually operating on the desired channel (with at least |
351 | * 20 MHz channel width) don't stop all the operations but still |
352 | * treat it as though the ROC operation started properly, so |
353 | * other ROC operations won't interfere with this one. |
354 | * |
355 | * Note: scan can't run, tmp_channel is what we use, so this |
356 | * must be the currently active channel. |
357 | */ |
358 | roc->on_channel = roc->chan == local->hw.conf.chandef.chan && |
359 | local->hw.conf.chandef.width != NL80211_CHAN_WIDTH_5 && |
360 | local->hw.conf.chandef.width != NL80211_CHAN_WIDTH_10; |
361 | |
362 | /* start this ROC */ |
363 | ieee80211_recalc_idle(local); |
364 | |
365 | if (!roc->on_channel) { |
366 | ieee80211_offchannel_stop_vifs(local); |
367 | |
368 | local->tmp_channel = roc->chan; |
369 | ieee80211_hw_conf_chan(local); |
370 | } |
371 | |
372 | wiphy_delayed_work_queue(wiphy: local->hw.wiphy, dwork: &local->roc_work, |
373 | delay: msecs_to_jiffies(m: min_dur)); |
374 | |
375 | /* tell userspace or send frame(s) */ |
376 | list_for_each_entry(tmp, &local->roc_list, list) { |
377 | if (tmp->sdata != roc->sdata || tmp->chan != roc->chan) |
378 | break; |
379 | |
380 | tmp->on_channel = roc->on_channel; |
381 | ieee80211_handle_roc_started(roc: tmp, start_time: jiffies); |
382 | } |
383 | } |
384 | } |
385 | |
386 | void ieee80211_start_next_roc(struct ieee80211_local *local) |
387 | { |
388 | struct ieee80211_roc_work *roc; |
389 | |
390 | lockdep_assert_wiphy(local->hw.wiphy); |
391 | |
392 | if (list_empty(head: &local->roc_list)) { |
393 | ieee80211_run_deferred_scan(local); |
394 | return; |
395 | } |
396 | |
397 | /* defer roc if driver is not started (i.e. during reconfig) */ |
398 | if (local->in_reconfig) |
399 | return; |
400 | |
401 | roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, |
402 | list); |
403 | |
404 | if (WARN_ON_ONCE(roc->started)) |
405 | return; |
406 | |
407 | if (local->ops->remain_on_channel) { |
408 | _ieee80211_start_next_roc(local); |
409 | } else { |
410 | /* delay it a bit */ |
411 | wiphy_delayed_work_queue(wiphy: local->hw.wiphy, dwork: &local->roc_work, |
412 | delay: round_jiffies_relative(HZ / 2)); |
413 | } |
414 | } |
415 | |
416 | static void __ieee80211_roc_work(struct ieee80211_local *local) |
417 | { |
418 | struct ieee80211_roc_work *roc; |
419 | bool on_channel; |
420 | |
421 | lockdep_assert_wiphy(local->hw.wiphy); |
422 | |
423 | if (WARN_ON(local->ops->remain_on_channel)) |
424 | return; |
425 | |
426 | roc = list_first_entry_or_null(&local->roc_list, |
427 | struct ieee80211_roc_work, list); |
428 | if (!roc) |
429 | return; |
430 | |
431 | if (!roc->started) { |
432 | WARN_ON(!local->emulate_chanctx); |
433 | _ieee80211_start_next_roc(local); |
434 | } else { |
435 | on_channel = roc->on_channel; |
436 | if (ieee80211_recalc_sw_work(local, now: jiffies)) |
437 | return; |
438 | |
439 | /* careful - roc pointer became invalid during recalc */ |
440 | |
441 | if (!on_channel) { |
442 | ieee80211_flush_queues(local, NULL, drop: false); |
443 | |
444 | local->tmp_channel = NULL; |
445 | ieee80211_hw_conf_chan(local); |
446 | |
447 | ieee80211_offchannel_return(local); |
448 | } |
449 | |
450 | ieee80211_recalc_idle(local); |
451 | ieee80211_start_next_roc(local); |
452 | } |
453 | } |
454 | |
455 | static void ieee80211_roc_work(struct wiphy *wiphy, struct wiphy_work *work) |
456 | { |
457 | struct ieee80211_local *local = |
458 | container_of(work, struct ieee80211_local, roc_work.work); |
459 | |
460 | lockdep_assert_wiphy(local->hw.wiphy); |
461 | |
462 | __ieee80211_roc_work(local); |
463 | } |
464 | |
465 | static void ieee80211_hw_roc_done(struct wiphy *wiphy, struct wiphy_work *work) |
466 | { |
467 | struct ieee80211_local *local = |
468 | container_of(work, struct ieee80211_local, hw_roc_done); |
469 | |
470 | lockdep_assert_wiphy(local->hw.wiphy); |
471 | |
472 | ieee80211_end_finished_rocs(local, now: jiffies); |
473 | |
474 | /* if there's another roc, start it now */ |
475 | ieee80211_start_next_roc(local); |
476 | } |
477 | |
478 | void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw) |
479 | { |
480 | struct ieee80211_local *local = hw_to_local(hw); |
481 | |
482 | trace_api_remain_on_channel_expired(local); |
483 | |
484 | wiphy_work_queue(wiphy: hw->wiphy, work: &local->hw_roc_done); |
485 | } |
486 | EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired); |
487 | |
488 | static bool |
489 | ieee80211_coalesce_hw_started_roc(struct ieee80211_local *local, |
490 | struct ieee80211_roc_work *new_roc, |
491 | struct ieee80211_roc_work *cur_roc) |
492 | { |
493 | unsigned long now = jiffies; |
494 | unsigned long remaining; |
495 | |
496 | if (WARN_ON(!cur_roc->started)) |
497 | return false; |
498 | |
499 | /* if it was scheduled in the hardware, but not started yet, |
500 | * we can only combine if the older one had a longer duration |
501 | */ |
502 | if (!cur_roc->hw_begun && new_roc->duration > cur_roc->duration) |
503 | return false; |
504 | |
505 | remaining = cur_roc->start_time + |
506 | msecs_to_jiffies(m: cur_roc->duration) - |
507 | now; |
508 | |
509 | /* if it doesn't fit entirely, schedule a new one */ |
510 | if (new_roc->duration > jiffies_to_msecs(j: remaining)) |
511 | return false; |
512 | |
513 | /* add just after the current one so we combine their finish later */ |
514 | list_add(new: &new_roc->list, head: &cur_roc->list); |
515 | |
516 | /* if the existing one has already begun then let this one also |
517 | * begin, otherwise they'll both be marked properly by the work |
518 | * struct that runs once the driver notifies us of the beginning |
519 | */ |
520 | if (cur_roc->hw_begun) { |
521 | new_roc->hw_begun = true; |
522 | ieee80211_handle_roc_started(roc: new_roc, start_time: now); |
523 | } |
524 | |
525 | return true; |
526 | } |
527 | |
528 | static int ieee80211_start_roc_work(struct ieee80211_local *local, |
529 | struct ieee80211_sub_if_data *sdata, |
530 | struct ieee80211_channel *channel, |
531 | unsigned int duration, u64 *cookie, |
532 | struct sk_buff *txskb, |
533 | enum ieee80211_roc_type type) |
534 | { |
535 | struct ieee80211_roc_work *roc, *tmp; |
536 | bool queued = false, combine_started = true; |
537 | int ret; |
538 | |
539 | lockdep_assert_wiphy(local->hw.wiphy); |
540 | |
541 | if (channel->freq_offset) |
542 | /* this may work, but is untested */ |
543 | return -EOPNOTSUPP; |
544 | |
545 | if (!local->emulate_chanctx && !local->ops->remain_on_channel) |
546 | return -EOPNOTSUPP; |
547 | |
548 | roc = kzalloc(size: sizeof(*roc), GFP_KERNEL); |
549 | if (!roc) |
550 | return -ENOMEM; |
551 | |
552 | /* |
553 | * If the duration is zero, then the driver |
554 | * wouldn't actually do anything. Set it to |
555 | * 10 for now. |
556 | * |
557 | * TODO: cancel the off-channel operation |
558 | * when we get the SKB's TX status and |
559 | * the wait time was zero before. |
560 | */ |
561 | if (!duration) |
562 | duration = 10; |
563 | |
564 | roc->chan = channel; |
565 | roc->duration = duration; |
566 | roc->req_duration = duration; |
567 | roc->frame = txskb; |
568 | roc->type = type; |
569 | roc->sdata = sdata; |
570 | |
571 | /* |
572 | * cookie is either the roc cookie (for normal roc) |
573 | * or the SKB (for mgmt TX) |
574 | */ |
575 | if (!txskb) { |
576 | roc->cookie = ieee80211_mgmt_tx_cookie(local); |
577 | *cookie = roc->cookie; |
578 | } else { |
579 | roc->mgmt_tx_cookie = *cookie; |
580 | } |
581 | |
582 | /* if there's no need to queue, handle it immediately */ |
583 | if (list_empty(head: &local->roc_list) && |
584 | !local->scanning && !ieee80211_is_radar_required(local)) { |
585 | /* if not HW assist, just queue & schedule work */ |
586 | if (!local->ops->remain_on_channel) { |
587 | list_add_tail(new: &roc->list, head: &local->roc_list); |
588 | wiphy_delayed_work_queue(wiphy: local->hw.wiphy, |
589 | dwork: &local->roc_work, delay: 0); |
590 | } else { |
591 | /* otherwise actually kick it off here |
592 | * (for error handling) |
593 | */ |
594 | ret = drv_remain_on_channel(local, sdata, chan: channel, |
595 | duration, type); |
596 | if (ret) { |
597 | kfree(objp: roc); |
598 | return ret; |
599 | } |
600 | roc->started = true; |
601 | list_add_tail(new: &roc->list, head: &local->roc_list); |
602 | } |
603 | |
604 | return 0; |
605 | } |
606 | |
607 | /* otherwise handle queueing */ |
608 | |
609 | list_for_each_entry(tmp, &local->roc_list, list) { |
610 | if (tmp->chan != channel || tmp->sdata != sdata) |
611 | continue; |
612 | |
613 | /* |
614 | * Extend this ROC if possible: If it hasn't started, add |
615 | * just after the new one to combine. |
616 | */ |
617 | if (!tmp->started) { |
618 | list_add(new: &roc->list, head: &tmp->list); |
619 | queued = true; |
620 | break; |
621 | } |
622 | |
623 | if (!combine_started) |
624 | continue; |
625 | |
626 | if (!local->ops->remain_on_channel) { |
627 | /* If there's no hardware remain-on-channel, and |
628 | * doing so won't push us over the maximum r-o-c |
629 | * we allow, then we can just add the new one to |
630 | * the list and mark it as having started now. |
631 | * If it would push over the limit, don't try to |
632 | * combine with other started ones (that haven't |
633 | * been running as long) but potentially sort it |
634 | * with others that had the same fate. |
635 | */ |
636 | unsigned long now = jiffies; |
637 | u32 elapsed = jiffies_to_msecs(j: now - tmp->start_time); |
638 | struct wiphy *wiphy = local->hw.wiphy; |
639 | u32 max_roc = wiphy->max_remain_on_channel_duration; |
640 | |
641 | if (elapsed + roc->duration > max_roc) { |
642 | combine_started = false; |
643 | continue; |
644 | } |
645 | |
646 | list_add(new: &roc->list, head: &tmp->list); |
647 | queued = true; |
648 | roc->on_channel = tmp->on_channel; |
649 | ieee80211_handle_roc_started(roc, start_time: now); |
650 | ieee80211_recalc_sw_work(local, now); |
651 | break; |
652 | } |
653 | |
654 | queued = ieee80211_coalesce_hw_started_roc(local, new_roc: roc, cur_roc: tmp); |
655 | if (queued) |
656 | break; |
657 | /* if it wasn't queued, perhaps it can be combined with |
658 | * another that also couldn't get combined previously, |
659 | * but no need to check for already started ones, since |
660 | * that can't work. |
661 | */ |
662 | combine_started = false; |
663 | } |
664 | |
665 | if (!queued) |
666 | list_add_tail(new: &roc->list, head: &local->roc_list); |
667 | |
668 | return 0; |
669 | } |
670 | |
671 | int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, |
672 | struct ieee80211_channel *chan, |
673 | unsigned int duration, u64 *cookie) |
674 | { |
675 | struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); |
676 | struct ieee80211_local *local = sdata->local; |
677 | |
678 | lockdep_assert_wiphy(local->hw.wiphy); |
679 | |
680 | return ieee80211_start_roc_work(local, sdata, channel: chan, |
681 | duration, cookie, NULL, |
682 | type: IEEE80211_ROC_TYPE_NORMAL); |
683 | } |
684 | |
685 | static int ieee80211_cancel_roc(struct ieee80211_local *local, |
686 | u64 cookie, bool mgmt_tx) |
687 | { |
688 | struct ieee80211_roc_work *roc, *tmp, *found = NULL; |
689 | int ret; |
690 | |
691 | lockdep_assert_wiphy(local->hw.wiphy); |
692 | |
693 | if (!cookie) |
694 | return -ENOENT; |
695 | |
696 | wiphy_work_flush(wiphy: local->hw.wiphy, work: &local->hw_roc_start); |
697 | |
698 | list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { |
699 | if (!mgmt_tx && roc->cookie != cookie) |
700 | continue; |
701 | else if (mgmt_tx && roc->mgmt_tx_cookie != cookie) |
702 | continue; |
703 | |
704 | found = roc; |
705 | break; |
706 | } |
707 | |
708 | if (!found) { |
709 | return -ENOENT; |
710 | } |
711 | |
712 | if (!found->started) { |
713 | ieee80211_roc_notify_destroy(roc: found); |
714 | goto out_unlock; |
715 | } |
716 | |
717 | if (local->ops->remain_on_channel) { |
718 | ret = drv_cancel_remain_on_channel(local, sdata: roc->sdata); |
719 | if (WARN_ON_ONCE(ret)) { |
720 | return ret; |
721 | } |
722 | |
723 | /* |
724 | * We could be racing against the notification from the driver: |
725 | * + driver is handling the notification on CPU0 |
726 | * + user space is cancelling the remain on channel and |
727 | * schedules the hw_roc_done worker. |
728 | * |
729 | * Now hw_roc_done might start to run after the next roc will |
730 | * start and mac80211 will think that this second roc has |
731 | * ended prematurely. |
732 | * Cancel the work to make sure that all the pending workers |
733 | * have completed execution. |
734 | * Note that this assumes that by the time the driver returns |
735 | * from drv_cancel_remain_on_channel, it has completed all |
736 | * the processing of related notifications. |
737 | */ |
738 | wiphy_work_cancel(wiphy: local->hw.wiphy, work: &local->hw_roc_done); |
739 | |
740 | /* TODO: |
741 | * if multiple items were combined here then we really shouldn't |
742 | * cancel them all - we should wait for as much time as needed |
743 | * for the longest remaining one, and only then cancel ... |
744 | */ |
745 | list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { |
746 | if (!roc->started) |
747 | break; |
748 | if (roc == found) |
749 | found = NULL; |
750 | ieee80211_roc_notify_destroy(roc); |
751 | } |
752 | |
753 | /* that really must not happen - it was started */ |
754 | WARN_ON(found); |
755 | |
756 | ieee80211_start_next_roc(local); |
757 | } else { |
758 | /* go through work struct to return to the operating channel */ |
759 | found->abort = true; |
760 | wiphy_delayed_work_queue(wiphy: local->hw.wiphy, dwork: &local->roc_work, delay: 0); |
761 | } |
762 | |
763 | out_unlock: |
764 | |
765 | return 0; |
766 | } |
767 | |
768 | int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, |
769 | struct wireless_dev *wdev, u64 cookie) |
770 | { |
771 | struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); |
772 | struct ieee80211_local *local = sdata->local; |
773 | |
774 | return ieee80211_cancel_roc(local, cookie, mgmt_tx: false); |
775 | } |
776 | |
777 | int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, |
778 | struct cfg80211_mgmt_tx_params *params, u64 *cookie) |
779 | { |
780 | struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); |
781 | struct ieee80211_local *local = sdata->local; |
782 | struct sk_buff *skb; |
783 | struct sta_info *sta = NULL; |
784 | const struct ieee80211_mgmt *mgmt = (void *)params->buf; |
785 | bool need_offchan = false; |
786 | bool mlo_sta = false; |
787 | int link_id = -1; |
788 | u32 flags; |
789 | int ret; |
790 | u8 *data; |
791 | |
792 | lockdep_assert_wiphy(local->hw.wiphy); |
793 | |
794 | if (params->dont_wait_for_ack) |
795 | flags = IEEE80211_TX_CTL_NO_ACK; |
796 | else |
797 | flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | |
798 | IEEE80211_TX_CTL_REQ_TX_STATUS; |
799 | |
800 | if (params->no_cck) |
801 | flags |= IEEE80211_TX_CTL_NO_CCK_RATE; |
802 | |
803 | switch (sdata->vif.type) { |
804 | case NL80211_IFTYPE_ADHOC: |
805 | if (!sdata->vif.cfg.ibss_joined) |
806 | need_offchan = true; |
807 | #ifdef CONFIG_MAC80211_MESH |
808 | fallthrough; |
809 | case NL80211_IFTYPE_MESH_POINT: |
810 | if (ieee80211_vif_is_mesh(vif: &sdata->vif) && |
811 | !sdata->u.mesh.mesh_id_len) |
812 | need_offchan = true; |
813 | #endif |
814 | fallthrough; |
815 | case NL80211_IFTYPE_AP: |
816 | case NL80211_IFTYPE_AP_VLAN: |
817 | case NL80211_IFTYPE_P2P_GO: |
818 | if (sdata->vif.type != NL80211_IFTYPE_ADHOC && |
819 | !ieee80211_vif_is_mesh(vif: &sdata->vif) && |
820 | !sdata->bss->active) |
821 | need_offchan = true; |
822 | |
823 | rcu_read_lock(); |
824 | sta = sta_info_get_bss(sdata, addr: mgmt->da); |
825 | mlo_sta = sta && sta->sta.mlo; |
826 | |
827 | if (!ieee80211_is_action(fc: mgmt->frame_control) || |
828 | mgmt->u.action.category == WLAN_CATEGORY_PUBLIC || |
829 | mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED || |
830 | mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) { |
831 | rcu_read_unlock(); |
832 | break; |
833 | } |
834 | |
835 | if (!sta) { |
836 | rcu_read_unlock(); |
837 | return -ENOLINK; |
838 | } |
839 | if (params->link_id >= 0 && |
840 | !(sta->sta.valid_links & BIT(params->link_id))) { |
841 | rcu_read_unlock(); |
842 | return -ENOLINK; |
843 | } |
844 | link_id = params->link_id; |
845 | rcu_read_unlock(); |
846 | break; |
847 | case NL80211_IFTYPE_STATION: |
848 | case NL80211_IFTYPE_P2P_CLIENT: |
849 | if (!sdata->u.mgd.associated || |
850 | (params->offchan && params->wait && |
851 | local->ops->remain_on_channel && |
852 | memcmp(p: sdata->vif.cfg.ap_addr, q: mgmt->bssid, ETH_ALEN))) { |
853 | need_offchan = true; |
854 | } else if (sdata->u.mgd.associated && |
855 | ether_addr_equal(addr1: sdata->vif.cfg.ap_addr, addr2: mgmt->da)) { |
856 | sta = sta_info_get_bss(sdata, addr: mgmt->da); |
857 | mlo_sta = sta && sta->sta.mlo; |
858 | } |
859 | break; |
860 | case NL80211_IFTYPE_P2P_DEVICE: |
861 | need_offchan = true; |
862 | break; |
863 | case NL80211_IFTYPE_NAN: |
864 | default: |
865 | return -EOPNOTSUPP; |
866 | } |
867 | |
868 | /* configurations requiring offchan cannot work if no channel has been |
869 | * specified |
870 | */ |
871 | if (need_offchan && !params->chan) |
872 | return -EINVAL; |
873 | |
874 | /* Check if the operating channel is the requested channel */ |
875 | if (!params->chan && mlo_sta) { |
876 | need_offchan = false; |
877 | } else if (!need_offchan) { |
878 | struct ieee80211_chanctx_conf *chanctx_conf = NULL; |
879 | int i; |
880 | |
881 | rcu_read_lock(); |
882 | /* Check all the links first */ |
883 | for (i = 0; i < ARRAY_SIZE(sdata->vif.link_conf); i++) { |
884 | struct ieee80211_bss_conf *conf; |
885 | |
886 | conf = rcu_dereference(sdata->vif.link_conf[i]); |
887 | if (!conf) |
888 | continue; |
889 | |
890 | chanctx_conf = rcu_dereference(conf->chanctx_conf); |
891 | if (!chanctx_conf) |
892 | continue; |
893 | |
894 | if (mlo_sta && params->chan == chanctx_conf->def.chan && |
895 | ether_addr_equal(addr1: sdata->vif.addr, addr2: mgmt->sa)) { |
896 | link_id = i; |
897 | break; |
898 | } |
899 | |
900 | if (ether_addr_equal(addr1: conf->addr, addr2: mgmt->sa)) |
901 | break; |
902 | |
903 | chanctx_conf = NULL; |
904 | } |
905 | |
906 | if (chanctx_conf) { |
907 | need_offchan = params->chan && |
908 | (params->chan != |
909 | chanctx_conf->def.chan); |
910 | } else { |
911 | need_offchan = true; |
912 | } |
913 | rcu_read_unlock(); |
914 | } |
915 | |
916 | if (need_offchan && !params->offchan) { |
917 | ret = -EBUSY; |
918 | goto out_unlock; |
919 | } |
920 | |
921 | skb = dev_alloc_skb(length: local->hw.extra_tx_headroom + params->len); |
922 | if (!skb) { |
923 | ret = -ENOMEM; |
924 | goto out_unlock; |
925 | } |
926 | skb_reserve(skb, len: local->hw.extra_tx_headroom); |
927 | |
928 | data = skb_put_data(skb, data: params->buf, len: params->len); |
929 | |
930 | /* Update CSA counters */ |
931 | if (sdata->vif.bss_conf.csa_active && |
932 | (sdata->vif.type == NL80211_IFTYPE_AP || |
933 | sdata->vif.type == NL80211_IFTYPE_MESH_POINT || |
934 | sdata->vif.type == NL80211_IFTYPE_ADHOC) && |
935 | params->n_csa_offsets) { |
936 | int i; |
937 | struct beacon_data *beacon = NULL; |
938 | |
939 | rcu_read_lock(); |
940 | |
941 | if (sdata->vif.type == NL80211_IFTYPE_AP) |
942 | beacon = rcu_dereference(sdata->deflink.u.ap.beacon); |
943 | else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) |
944 | beacon = rcu_dereference(sdata->u.ibss.presp); |
945 | else if (ieee80211_vif_is_mesh(vif: &sdata->vif)) |
946 | beacon = rcu_dereference(sdata->u.mesh.beacon); |
947 | |
948 | if (beacon) |
949 | for (i = 0; i < params->n_csa_offsets; i++) |
950 | data[params->csa_offsets[i]] = |
951 | beacon->cntdwn_current_counter; |
952 | |
953 | rcu_read_unlock(); |
954 | } |
955 | |
956 | IEEE80211_SKB_CB(skb)->flags = flags; |
957 | |
958 | skb->dev = sdata->dev; |
959 | |
960 | if (!params->dont_wait_for_ack) { |
961 | /* make a copy to preserve the frame contents |
962 | * in case of encryption. |
963 | */ |
964 | ret = ieee80211_attach_ack_skb(local, skb, cookie, GFP_KERNEL); |
965 | if (ret) { |
966 | kfree_skb(skb); |
967 | goto out_unlock; |
968 | } |
969 | } else { |
970 | /* Assign a dummy non-zero cookie, it's not sent to |
971 | * userspace in this case but we rely on its value |
972 | * internally in the need_offchan case to distinguish |
973 | * mgmt-tx from remain-on-channel. |
974 | */ |
975 | *cookie = 0xffffffff; |
976 | } |
977 | |
978 | if (!need_offchan) { |
979 | ieee80211_tx_skb_tid(sdata, skb, tid: 7, link_id); |
980 | ret = 0; |
981 | goto out_unlock; |
982 | } |
983 | |
984 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN | |
985 | IEEE80211_TX_INTFL_OFFCHAN_TX_OK; |
986 | if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) |
987 | IEEE80211_SKB_CB(skb)->hw_queue = |
988 | local->hw.offchannel_tx_hw_queue; |
989 | |
990 | /* This will handle all kinds of coalescing and immediate TX */ |
991 | ret = ieee80211_start_roc_work(local, sdata, channel: params->chan, |
992 | duration: params->wait, cookie, txskb: skb, |
993 | type: IEEE80211_ROC_TYPE_MGMT_TX); |
994 | if (ret) |
995 | ieee80211_free_txskb(hw: &local->hw, skb); |
996 | out_unlock: |
997 | return ret; |
998 | } |
999 | |
1000 | int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, |
1001 | struct wireless_dev *wdev, u64 cookie) |
1002 | { |
1003 | struct ieee80211_local *local = wiphy_priv(wiphy); |
1004 | |
1005 | return ieee80211_cancel_roc(local, cookie, mgmt_tx: true); |
1006 | } |
1007 | |
1008 | void ieee80211_roc_setup(struct ieee80211_local *local) |
1009 | { |
1010 | wiphy_work_init(work: &local->hw_roc_start, func: ieee80211_hw_roc_start); |
1011 | wiphy_work_init(work: &local->hw_roc_done, func: ieee80211_hw_roc_done); |
1012 | wiphy_delayed_work_init(dwork: &local->roc_work, func: ieee80211_roc_work); |
1013 | INIT_LIST_HEAD(list: &local->roc_list); |
1014 | } |
1015 | |
1016 | void ieee80211_roc_purge(struct ieee80211_local *local, |
1017 | struct ieee80211_sub_if_data *sdata) |
1018 | { |
1019 | struct ieee80211_roc_work *roc, *tmp; |
1020 | bool work_to_do = false; |
1021 | |
1022 | lockdep_assert_wiphy(local->hw.wiphy); |
1023 | |
1024 | list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { |
1025 | if (sdata && roc->sdata != sdata) |
1026 | continue; |
1027 | |
1028 | if (roc->started) { |
1029 | if (local->ops->remain_on_channel) { |
1030 | /* can race, so ignore return value */ |
1031 | drv_cancel_remain_on_channel(local, sdata: roc->sdata); |
1032 | ieee80211_roc_notify_destroy(roc); |
1033 | } else { |
1034 | roc->abort = true; |
1035 | work_to_do = true; |
1036 | } |
1037 | } else { |
1038 | ieee80211_roc_notify_destroy(roc); |
1039 | } |
1040 | } |
1041 | if (work_to_do) |
1042 | __ieee80211_roc_work(local); |
1043 | } |
1044 | |