blob: cbf29b2afac25ac4411fb39038c4b44c751a39c9 [file] [log] [blame]
developer94abd9f2021-12-09 15:21:27 +08001diff --git a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
2index 27eecf3..ff3169c 100644
3--- a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
4+++ b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
5@@ -61,7 +61,8 @@ drv_mac80211_init_device_config() {
6 rx_stbc \
7 tx_stbc \
8 he_bss_color \
9- he_spr_non_srg_obss_pd_max_offset
10+ he_spr_non_srg_obss_pd_max_offset \
11+ radar_offchan
12 config_add_boolean \
13 ldpc \
14 greenfield \
15@@ -137,12 +138,9 @@ mac80211_hostapd_setup_base() {
16 [ -n "$acs_exclude_dfs" ] && [ "$acs_exclude_dfs" -gt 0 ] &&
17 append base_cfg "acs_exclude_dfs=1" "$N"
18
19- json_get_vars noscan ht_coex
20+ json_get_vars noscan ht_coex radar_offchan:0
21 json_get_values ht_capab_list ht_capab tx_burst
22- json_get_values channel_list channels
23-
24- [ "$auto_channel" = 0 ] && [ -z "$channel_list" ] && \
25- channel_list="$channel"
26+ json_get_values channels
27
28 set_default noscan 0
29
30@@ -462,10 +460,11 @@ mac80211_hostapd_setup_base() {
31 append base_cfg "he_mu_edca_ac_vo_timer=255" "$N"
32 fi
33
34+ append base_cfg "radar_offchan=$radar_offchan" "$N"
35+
36 hostapd_prepare_device_config "$hostapd_conf_file" nl80211
37 cat >> "$hostapd_conf_file" <<EOF
38 ${channel:+channel=$channel}
39-${channel_list:+chanlist=$channel_list}
40 ${hostapd_noscan:+noscan=1}
41 ${tx_burst:+tx_queue_data2_burst=$tx_burst}
42 $base_cfg
43diff --git a/package/kernel/mac80211/patches/subsys/900-mac80211-zero-wait-dfs.patch b/package/kernel/mac80211/patches/subsys/900-mac80211-zero-wait-dfs.patch
44new file mode 100644
45index 0000000..ba3ac4b
46--- /dev/null
47+++ b/package/kernel/mac80211/patches/subsys/900-mac80211-zero-wait-dfs.patch
48@@ -0,0 +1,667 @@
49+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
50+index 080d220..6278545 100644
51+--- a/include/net/cfg80211.h
52++++ b/include/net/cfg80211.h
53+@@ -4024,6 +4024,15 @@ struct mgmt_frame_regs {
54+ * @set_sar_specs: Update the SAR (TX power) settings.
55+ *
56+ * @color_change: Initiate a color change.
57++ *
58++ * @set_radar_offchan: Configure dedicated offchannel chain available for
59++ * radar/CAC detection on some hw. This chain can't be used to transmit
60++ * or receive frames and it is bounded to a running wdev.
61++ * Offchannel radar/CAC detection allows to avoid the CAC downtime
62++ * switching to a different channel during CAC detection on the selected
63++ * radar channel.
64++ * The caller is expected to set chandef pointer to NULL in order to
65++ * disable offchannel CAC/radar detection.
66+ */
67+ struct cfg80211_ops {
68+ int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
69+@@ -4355,6 +4364,8 @@ struct cfg80211_ops {
70+ int (*color_change)(struct wiphy *wiphy,
71+ struct net_device *dev,
72+ struct cfg80211_color_change_settings *params);
73++ int (*set_radar_offchan)(struct wiphy *wiphy,
74++ struct cfg80211_chan_def *chandef);
75+ };
76+
77+ /*
78+@@ -7529,15 +7540,33 @@ void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer,
79+ void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp);
80+
81+ /**
82+- * cfg80211_radar_event - radar detection event
83++ * __cfg80211_radar_event - radar detection event
84+ * @wiphy: the wiphy
85+ * @chandef: chandef for the current channel
86++ * @offchan: the radar has been detected on the offchannel chain
87+ * @gfp: context flags
88+ *
89+ * This function is called when a radar is detected on the current chanenl.
90+ */
91+-void cfg80211_radar_event(struct wiphy *wiphy,
92+- struct cfg80211_chan_def *chandef, gfp_t gfp);
93++void __cfg80211_radar_event(struct wiphy *wiphy,
94++ struct cfg80211_chan_def *chandef,
95++ bool offchan, gfp_t gfp);
96++
97++static inline void
98++cfg80211_radar_event(struct wiphy *wiphy,
99++ struct cfg80211_chan_def *chandef,
100++ gfp_t gfp)
101++{
102++ __cfg80211_radar_event(wiphy, chandef, false, gfp);
103++}
104++
105++static inline void
106++cfg80211_offchan_radar_event(struct wiphy *wiphy,
107++ struct cfg80211_chan_def *chandef,
108++ gfp_t gfp)
109++{
110++ __cfg80211_radar_event(wiphy, chandef, true, gfp);
111++}
112+
113+ /**
114+ * cfg80211_sta_opmode_change_notify - STA's ht/vht operation mode change event
115+@@ -7568,6 +7597,14 @@ void cfg80211_cac_event(struct net_device *netdev,
116+ const struct cfg80211_chan_def *chandef,
117+ enum nl80211_radar_event event, gfp_t gfp);
118+
119++/**
120++ * cfg80211_offchan_cac_abort - Channel Availability Check offchan abort event
121++ * @wiphy: the wiphy
122++ *
123++ * This function is called by the driver when a Channel Availability Check
124++ * (CAC) is aborted by a offchannel dedicated chain.
125++ */
126++void cfg80211_offchan_cac_abort(struct wiphy *wiphy);
127+
128+ /**
129+ * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
130+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
131+index afab7ec..acf637d 100644
132+--- a/include/net/mac80211.h
133++++ b/include/net/mac80211.h
134+@@ -3939,6 +3939,14 @@ struct ieee80211_prep_tx_info {
135+ * twt structure.
136+ * @twt_teardown_request: Update the hw with TWT teardown request received
137+ * from the peer.
138++ * @set_radar_offchan: Configure dedicated offchannel chain available for
139++ * radar/CAC detection on some hw. This chain can't be used to transmit
140++ * or receive frames and it is bounded to a running wdev.
141++ * Offchannel radar/CAC detection allows to avoid the CAC downtime
142++ * switching to a different channel during CAC detection on the selected
143++ * radar channel.
144++ * The caller is expected to set chandef pointer to NULL in order to
145++ * disable offchannel CAC/radar detection.
146+ */
147+ struct ieee80211_ops {
148+ void (*tx)(struct ieee80211_hw *hw,
149+@@ -4267,6 +4275,8 @@ struct ieee80211_ops {
150+ struct ieee80211_twt_setup *twt);
151+ void (*twt_teardown_request)(struct ieee80211_hw *hw,
152+ struct ieee80211_sta *sta, u8 flowid);
153++ int (*set_radar_offchan)(struct ieee80211_hw *hw,
154++ struct cfg80211_chan_def *chandef);
155+ };
156+
157+ /**
158+diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
159+index 5f4a094..7634ba0 100644
160+--- a/include/uapi/linux/nl80211.h
161++++ b/include/uapi/linux/nl80211.h
162+@@ -2596,6 +2596,13 @@ enum nl80211_commands {
163+ * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
164+ * transmit power to stay within regulatory limits. u32, dBi.
165+ *
166++ * @NL80211_ATTR_RADAR_OFFCHAN: Configure dedicated offchannel chain available for
167++ * radar/CAC detection on some hw. This chain can't be used to transmit
168++ * or receive frames and it is bounded to a running wdev.
169++ * Offchannel radar/CAC detection allows to avoid the CAC downtime
170++ * switching on a different channel during CAC detection on the selected
171++ * radar channel.
172++ *
173+ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
174+ * @NL80211_ATTR_MAX: highest attribute number currently defined
175+ * @__NL80211_ATTR_AFTER_LAST: internal use
176+@@ -3101,6 +3108,8 @@ enum nl80211_attrs {
177+
178+ NL80211_ATTR_WIPHY_ANTENNA_GAIN,
179+
180++ NL80211_ATTR_RADAR_OFFCHAN,
181++
182+ /* add attributes here, update the policy in nl80211.c */
183+
184+ __NL80211_ATTR_AFTER_LAST,
185+@@ -6000,6 +6009,9 @@ enum nl80211_feature_flags {
186+ * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
187+ * detection and change announcemnts.
188+ *
189++ * @NL80211_EXT_FEATURE_RADAR_OFFCHAN: Device supports offchannel radar/CAC
190++ * detection.
191++ *
192+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
193+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
194+ */
195+@@ -6065,6 +6077,7 @@ enum nl80211_ext_feature_index {
196+ NL80211_EXT_FEATURE_SECURE_RTT,
197+ NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
198+ NL80211_EXT_FEATURE_BSS_COLOR,
199++ NL80211_EXT_FEATURE_RADAR_OFFCHAN,
200+
201+ /* add new features before the definition below */
202+ NUM_NL80211_EXT_FEATURES,
203+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
204+index 8b7c12a..887af1e 100644
205+--- a/net/mac80211/cfg.c
206++++ b/net/mac80211/cfg.c
207+@@ -4353,6 +4353,18 @@ out:
208+ return err;
209+ }
210+
211++static int
212++ieee80211_set_radar_offchan(struct wiphy *wiphy,
213++ struct cfg80211_chan_def *chandef)
214++{
215++ struct ieee80211_local *local = wiphy_priv(wiphy);
216++
217++ if (!local->ops->set_radar_offchan)
218++ return -EOPNOTSUPP;
219++
220++ return local->ops->set_radar_offchan(&local->hw, chandef);
221++}
222++
223+ const struct cfg80211_ops mac80211_config_ops = {
224+ .add_virtual_intf = ieee80211_add_iface,
225+ .del_virtual_intf = ieee80211_del_iface,
226+@@ -4458,4 +4470,5 @@ const struct cfg80211_ops mac80211_config_ops = {
227+ .reset_tid_config = ieee80211_reset_tid_config,
228+ .set_sar_specs = ieee80211_set_sar_specs,
229+ .color_change = ieee80211_color_change,
230++ .set_radar_offchan = ieee80211_set_radar_offchan,
231+ };
232+diff --git a/net/wireless/chan.c b/net/wireless/chan.c
233+index 1382a5f..75b4151 100644
234+--- a/net/wireless/chan.c
235++++ b/net/wireless/chan.c
236+@@ -712,6 +712,20 @@ static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy,
237+ return false;
238+ }
239+
240++static bool
241++cfg80211_offchan_chain_is_active(struct cfg80211_registered_device *rdev,
242++ struct ieee80211_channel *channel)
243++{
244++
245++ if (!rdev->offchan_radar_wdev)
246++ return false;
247++
248++ if (!cfg80211_chandef_valid(&rdev->offchan_radar_chandef))
249++ return false;
250++
251++ return cfg80211_is_sub_chan(&rdev->offchan_radar_chandef, channel);
252++}
253++
254+ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
255+ struct ieee80211_channel *chan)
256+ {
257+@@ -728,6 +742,9 @@ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
258+
259+ if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan))
260+ return true;
261++
262++ if (cfg80211_offchan_chain_is_active(rdev, chan))
263++ return true;
264+ }
265+
266+ return false;
267+diff --git a/net/wireless/core.c b/net/wireless/core.c
268+index b181dad..378fb4e 100644
269+--- a/net/wireless/core.c
270++++ b/net/wireless/core.c
271+@@ -551,6 +551,9 @@ use_default_name:
272+ INIT_WORK(&rdev->rfkill_block, cfg80211_rfkill_block_work);
273+ INIT_WORK(&rdev->conn_work, cfg80211_conn_work);
274+ INIT_WORK(&rdev->event_work, cfg80211_event_work);
275++ INIT_WORK(&rdev->offchan_cac_abort_wk, cfg80211_offchan_cac_abort_wk);
276++ INIT_DELAYED_WORK(&rdev->offchan_cac_done_wk,
277++ cfg80211_offchan_cac_done_wk);
278+
279+ init_waitqueue_head(&rdev->dev_wait);
280+
281+@@ -1045,11 +1048,13 @@ void wiphy_unregister(struct wiphy *wiphy)
282+ cancel_work_sync(&rdev->conn_work);
283+ flush_work(&rdev->event_work);
284+ cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
285++ cancel_delayed_work_sync(&rdev->offchan_cac_done_wk);
286+ flush_work(&rdev->destroy_work);
287+ flush_work(&rdev->sched_scan_stop_wk);
288+ flush_work(&rdev->propagate_radar_detect_wk);
289+ flush_work(&rdev->propagate_cac_done_wk);
290+ flush_work(&rdev->mgmt_registrations_update_wk);
291++ flush_work(&rdev->offchan_cac_abort_wk);
292+
293+ #ifdef CONFIG_PM
294+ if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
295+@@ -1188,6 +1193,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
296+
297+ cfg80211_pmsr_wdev_down(wdev);
298+
299++ cfg80211_stop_offchan_radar_detection(wdev);
300++
301+ switch (wdev->iftype) {
302+ case NL80211_IFTYPE_ADHOC:
303+ __cfg80211_leave_ibss(rdev, dev, true);
304+diff --git a/net/wireless/core.h b/net/wireless/core.h
305+index 19fcdd7..7fe31aa 100644
306+--- a/net/wireless/core.h
307++++ b/net/wireless/core.h
308+@@ -84,6 +84,11 @@ struct cfg80211_registered_device {
309+
310+ struct delayed_work dfs_update_channels_wk;
311+
312++ struct wireless_dev *offchan_radar_wdev;
313++ struct cfg80211_chan_def offchan_radar_chandef;
314++ struct delayed_work offchan_cac_done_wk;
315++ struct work_struct offchan_cac_abort_wk;
316++
317+ /* netlink port which started critical protocol (0 means not started) */
318+ u32 crit_proto_nlportid;
319+
320+@@ -489,6 +494,17 @@ cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
321+
322+ void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
323+
324++int
325++cfg80211_start_offchan_radar_detection(struct cfg80211_registered_device *rdev,
326++ struct wireless_dev *wdev,
327++ struct cfg80211_chan_def *chandef);
328++
329++void cfg80211_stop_offchan_radar_detection(struct wireless_dev *wdev);
330++
331++void cfg80211_offchan_cac_done_wk(struct work_struct *work);
332++
333++void cfg80211_offchan_cac_abort_wk(struct work_struct *work);
334++
335+ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
336+ struct ieee80211_channel *chan);
337+
338+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
339+index a8a17b9..8f230d3 100644
340+--- a/net/wireless/mlme.c
341++++ b/net/wireless/mlme.c
342+@@ -903,13 +903,13 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
343+ }
344+
345+
346+-void cfg80211_radar_event(struct wiphy *wiphy,
347+- struct cfg80211_chan_def *chandef,
348+- gfp_t gfp)
349++void __cfg80211_radar_event(struct wiphy *wiphy,
350++ struct cfg80211_chan_def *chandef,
351++ bool offchan, gfp_t gfp)
352+ {
353+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
354+
355+- trace_cfg80211_radar_event(wiphy, chandef);
356++ trace_cfg80211_radar_event(wiphy, chandef, offchan);
357+
358+ /* only set the chandef supplied channel to unavailable, in
359+ * case the radar is detected on only one of multiple channels
360+@@ -917,6 +917,9 @@ void cfg80211_radar_event(struct wiphy *wiphy,
361+ */
362+ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
363+
364++ if (offchan)
365++ queue_work(cfg80211_wq, &rdev->offchan_cac_abort_wk);
366++
367+ cfg80211_sched_dfs_chan_update(rdev);
368+
369+ nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
370+@@ -924,7 +927,7 @@ void cfg80211_radar_event(struct wiphy *wiphy,
371+ memcpy(&rdev->radar_chandef, chandef, sizeof(struct cfg80211_chan_def));
372+ queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk);
373+ }
374+-EXPORT_SYMBOL(cfg80211_radar_event);
375++EXPORT_SYMBOL(__cfg80211_radar_event);
376+
377+ void cfg80211_cac_event(struct net_device *netdev,
378+ const struct cfg80211_chan_def *chandef,
379+@@ -968,3 +971,142 @@ void cfg80211_cac_event(struct net_device *netdev,
380+ nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
381+ }
382+ EXPORT_SYMBOL(cfg80211_cac_event);
383++
384++static void
385++__cfg80211_offchan_cac_event(struct cfg80211_registered_device *rdev,
386++ struct wireless_dev *wdev,
387++ const struct cfg80211_chan_def *chandef,
388++ enum nl80211_radar_event event)
389++{
390++ struct wiphy *wiphy = &rdev->wiphy;
391++ struct net_device *netdev;
392++
393++ lockdep_assert_wiphy(&rdev->wiphy);
394++
395++ if (!cfg80211_chandef_valid(chandef))
396++ return;
397++
398++ if (!rdev->offchan_radar_wdev)
399++ return;
400++
401++ switch (event) {
402++ case NL80211_RADAR_CAC_FINISHED:
403++ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
404++ memcpy(&rdev->cac_done_chandef, chandef, sizeof(*chandef));
405++ queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
406++ cfg80211_sched_dfs_chan_update(rdev);
407++ wdev = rdev->offchan_radar_wdev;
408++ break;
409++ case NL80211_RADAR_CAC_ABORTED:
410++ if (!cancel_delayed_work(&rdev->offchan_cac_done_wk))
411++ return;
412++ wdev = rdev->offchan_radar_wdev;
413++ break;
414++ case NL80211_RADAR_CAC_STARTED:
415++ break;
416++ default:
417++ return;
418++ }
419++
420++ netdev = wdev ? wdev->netdev : NULL;
421++ nl80211_radar_notify(rdev, chandef, event, netdev, GFP_KERNEL);
422++}
423++
424++static void
425++cfg80211_offchan_cac_event(struct cfg80211_registered_device *rdev,
426++ const struct cfg80211_chan_def *chandef,
427++ enum nl80211_radar_event event)
428++{
429++ wiphy_lock(&rdev->wiphy);
430++ __cfg80211_offchan_cac_event(rdev, rdev->offchan_radar_wdev,
431++ chandef, event);
432++ wiphy_unlock(&rdev->wiphy);
433++}
434++
435++void cfg80211_offchan_cac_done_wk(struct work_struct *work)
436++{
437++ struct delayed_work *delayed_work = to_delayed_work(work);
438++ struct cfg80211_registered_device *rdev;
439++
440++ rdev = container_of(delayed_work, struct cfg80211_registered_device,
441++ offchan_cac_done_wk);
442++ cfg80211_offchan_cac_event(rdev, &rdev->offchan_radar_chandef,
443++ NL80211_RADAR_CAC_FINISHED);
444++}
445++
446++void cfg80211_offchan_cac_abort_wk(struct work_struct *work)
447++{
448++ struct cfg80211_registered_device *rdev;
449++
450++ rdev = container_of(work, struct cfg80211_registered_device,
451++ offchan_cac_abort_wk);
452++ cfg80211_offchan_cac_event(rdev, &rdev->offchan_radar_chandef,
453++ NL80211_RADAR_CAC_ABORTED);
454++}
455++
456++void cfg80211_offchan_cac_abort(struct wiphy *wiphy)
457++{
458++ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
459++
460++ queue_work(cfg80211_wq, &rdev->offchan_cac_abort_wk);
461++}
462++EXPORT_SYMBOL(cfg80211_offchan_cac_abort);
463++
464++int
465++cfg80211_start_offchan_radar_detection(struct cfg80211_registered_device *rdev,
466++ struct wireless_dev *wdev,
467++ struct cfg80211_chan_def *chandef)
468++{
469++ unsigned int cac_time_ms;
470++ int err;
471++
472++ lockdep_assert_wiphy(&rdev->wiphy);
473++
474++ if (!wiphy_ext_feature_isset(&rdev->wiphy,
475++ NL80211_EXT_FEATURE_RADAR_OFFCHAN))
476++ return -EOPNOTSUPP;
477++
478++ /* Offchannel chain already locked by another wdev */
479++ if (rdev->offchan_radar_wdev && rdev->offchan_radar_wdev != wdev)
480++ return -EBUSY;
481++
482++ /* CAC already in progress on the offchannel chain */
483++ if (rdev->offchan_radar_wdev == wdev &&
484++ delayed_work_pending(&rdev->offchan_cac_done_wk))
485++ return -EBUSY;
486++
487++ err = rdev_set_radar_offchan(rdev, chandef);
488++ if (err)
489++ return err;
490++
491++ cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, chandef);
492++ if (!cac_time_ms)
493++ cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
494++
495++ rdev->offchan_radar_chandef = *chandef;
496++ rdev->offchan_radar_wdev = wdev; /* Get offchain ownership */
497++
498++ __cfg80211_offchan_cac_event(rdev, wdev, chandef,
499++ NL80211_RADAR_CAC_STARTED);
500++ queue_delayed_work(cfg80211_wq, &rdev->offchan_cac_done_wk,
501++ msecs_to_jiffies(cac_time_ms));
502++
503++ return 0;
504++}
505++
506++void cfg80211_stop_offchan_radar_detection(struct wireless_dev *wdev)
507++{
508++ struct wiphy *wiphy = wdev->wiphy;
509++ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
510++
511++ lockdep_assert_wiphy(wiphy);
512++
513++ if (wdev != rdev->offchan_radar_wdev)
514++ return;
515++
516++ rdev_set_radar_offchan(rdev, NULL);
517++ rdev->offchan_radar_wdev = NULL; /* Release offchain ownership */
518++
519++ __cfg80211_offchan_cac_event(rdev, wdev, &rdev->offchan_radar_chandef,
520++ NL80211_RADAR_CAC_ABORTED);
521++}
522+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
523+index 4a3de61..0cf9f68 100644
524+--- a/net/wireless/nl80211.c
525++++ b/net/wireless/nl80211.c
526+@@ -781,6 +781,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
527+ [NL80211_ATTR_COLOR_CHANGE_COLOR] = { .type = NLA_U8 },
528+ [NL80211_ATTR_COLOR_CHANGE_ELEMS] = NLA_POLICY_NESTED(nl80211_policy),
529+ [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 },
530++ [NL80211_ATTR_RADAR_OFFCHAN] = { .type = NLA_FLAG },
531+ };
532+
533+ /* policy for the key attributes */
534+@@ -9106,38 +9107,60 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
535+ struct cfg80211_chan_def chandef;
536+ enum nl80211_dfs_regions dfs_region;
537+ unsigned int cac_time_ms;
538+- int err;
539++ int err = -EINVAL;
540++
541++ flush_delayed_work(&rdev->dfs_update_channels_wk);
542++
543++ wiphy_lock(wiphy);
544+
545+ dfs_region = reg_get_dfs_region(wiphy);
546+ if (dfs_region == NL80211_DFS_UNSET)
547+- return -EINVAL;
548++ goto unlock;
549+
550+ err = nl80211_parse_chandef(rdev, info, &chandef);
551+ if (err)
552+- return err;
553+-
554+- if (netif_carrier_ok(dev))
555+- return -EBUSY;
556+-
557+- if (wdev->cac_started)
558+- return -EBUSY;
559++ goto unlock;
560+
561+ err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype);
562+ if (err < 0)
563+- return err;
564++ goto unlock;
565+
566+- if (err == 0)
567+- return -EINVAL;
568++ if (err == 0) {
569++ err = -EINVAL;
570++ goto unlock;
571++ }
572+
573+- if (!cfg80211_chandef_dfs_usable(wiphy, &chandef))
574+- return -EINVAL;
575++ if (!cfg80211_chandef_dfs_usable(wiphy, &chandef)) {
576++ err = -EINVAL;
577++ goto unlock;
578++ }
579++
580++ if (nla_get_flag(info->attrs[NL80211_ATTR_RADAR_OFFCHAN])) {
581++ err = cfg80211_start_offchan_radar_detection(rdev, wdev,
582++ &chandef);
583++ goto unlock;
584++ }
585++
586++ if (netif_carrier_ok(dev)) {
587++ err = -EBUSY;
588++ goto unlock;
589++ }
590++
591++ if (wdev->cac_started) {
592++ err = -EBUSY;
593++ goto unlock;
594++ }
595+
596+ /* CAC start is offloaded to HW and can't be started manually */
597+- if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD))
598+- return -EOPNOTSUPP;
599++ if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) {
600++ err = -EOPNOTSUPP;
601++ goto unlock;
602++ }
603+
604+- if (!rdev->ops->start_radar_detection)
605+- return -EOPNOTSUPP;
606++ if (!rdev->ops->start_radar_detection) {
607++ err = -EOPNOTSUPP;
608++ goto unlock;
609++ }
610+
611+ cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef);
612+ if (WARN_ON(!cac_time_ms))
613+@@ -9150,6 +9173,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
614+ wdev->cac_start_time = jiffies;
615+ wdev->cac_time_ms = cac_time_ms;
616+ }
617++unlock:
618++ wiphy_unlock(wiphy);
619++
620+ return err;
621+ }
622+
623+@@ -15762,7 +15788,8 @@ static const struct genl_small_ops nl80211_small_ops[] = {
624+ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
625+ .doit = nl80211_start_radar_detection,
626+ .flags = GENL_UNS_ADMIN_PERM,
627+- .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
628++ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
629++ NL80211_FLAG_NO_WIPHY_MTX,
630+ },
631+ {
632+ .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
633+diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
634+index 36422cf..c566f47 100644
635+--- a/net/wireless/rdev-ops.h
636++++ b/net/wireless/rdev-ops.h
637+@@ -1381,4 +1381,21 @@ static inline int rdev_color_change(struct cfg80211_registered_device *rdev,
638+ return ret;
639+ }
640+
641++static inline int
642++rdev_set_radar_offchan(struct cfg80211_registered_device *rdev,
643++ struct cfg80211_chan_def *chandef)
644++{
645++ struct wiphy *wiphy = &rdev->wiphy;
646++ int ret;
647++
648++ if (!rdev->ops->set_radar_offchan)
649++ return -EOPNOTSUPP;
650++
651++ trace_rdev_set_radar_offchan(wiphy, chandef);
652++ ret = rdev->ops->set_radar_offchan(wiphy, chandef);
653++ trace_rdev_return_int(wiphy, ret);
654++
655++ return ret;
656++}
657++
658+ #endif /* __CFG80211_RDEV_OPS */
659+diff --git a/net/wireless/trace.h b/net/wireless/trace.h
660+index 973ce68..cd60ada 100644
661+--- a/net/wireless/trace.h
662++++ b/net/wireless/trace.h
663+@@ -3022,18 +3022,21 @@ TRACE_EVENT(cfg80211_ch_switch_started_notify,
664+ );
665+
666+ TRACE_EVENT(cfg80211_radar_event,
667+- TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
668+- TP_ARGS(wiphy, chandef),
669++ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef,
670++ bool offchan),
671++ TP_ARGS(wiphy, chandef, offchan),
672+ TP_STRUCT__entry(
673+ WIPHY_ENTRY
674+ CHAN_DEF_ENTRY
675++ __field(bool, offchan)
676+ ),
677+ TP_fast_assign(
678+ WIPHY_ASSIGN;
679+ CHAN_DEF_ASSIGN(chandef);
680++ __entry->offchan = offchan;
681+ ),
682+- TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
683+- WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
684++ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", offchan %d",
685++ WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->offchan)
686+ );
687+
688+ TRACE_EVENT(cfg80211_cac_event,
689+@@ -3643,6 +3646,25 @@ TRACE_EVENT(cfg80211_bss_color_notify,
690+ __entry->color_bitmap)
691+ );
692+
693++TRACE_EVENT(rdev_set_radar_offchan,
694++ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
695++
696++ TP_ARGS(wiphy, chandef),
697++
698++ TP_STRUCT__entry(
699++ WIPHY_ENTRY
700++ CHAN_DEF_ENTRY
701++ ),
702++
703++ TP_fast_assign(
704++ WIPHY_ASSIGN;
705++ CHAN_DEF_ASSIGN(chandef)
706++ ),
707++
708++ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
709++ WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
710++);
711++
712+ #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
713+
714+ #undef TRACE_INCLUDE_PATH
715+