| diff --git a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh |
| index 27eecf3..ff3169c 100644 |
| --- a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh |
| +++ b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh |
| @@ -61,7 +61,8 @@ drv_mac80211_init_device_config() { |
| rx_stbc \ |
| tx_stbc \ |
| he_bss_color \ |
| - he_spr_non_srg_obss_pd_max_offset |
| + he_spr_non_srg_obss_pd_max_offset \ |
| + radar_offchan |
| config_add_boolean \ |
| ldpc \ |
| greenfield \ |
| @@ -137,12 +138,9 @@ mac80211_hostapd_setup_base() { |
| [ -n "$acs_exclude_dfs" ] && [ "$acs_exclude_dfs" -gt 0 ] && |
| append base_cfg "acs_exclude_dfs=1" "$N" |
| |
| - json_get_vars noscan ht_coex |
| + json_get_vars noscan ht_coex radar_offchan:0 |
| json_get_values ht_capab_list ht_capab tx_burst |
| - json_get_values channel_list channels |
| - |
| - [ "$auto_channel" = 0 ] && [ -z "$channel_list" ] && \ |
| - channel_list="$channel" |
| + json_get_values channels |
| |
| set_default noscan 0 |
| |
| @@ -462,10 +460,11 @@ mac80211_hostapd_setup_base() { |
| append base_cfg "he_mu_edca_ac_vo_timer=255" "$N" |
| fi |
| |
| + append base_cfg "radar_offchan=$radar_offchan" "$N" |
| + |
| hostapd_prepare_device_config "$hostapd_conf_file" nl80211 |
| cat >> "$hostapd_conf_file" <<EOF |
| ${channel:+channel=$channel} |
| -${channel_list:+chanlist=$channel_list} |
| ${hostapd_noscan:+noscan=1} |
| ${tx_burst:+tx_queue_data2_burst=$tx_burst} |
| $base_cfg |
| diff --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 |
| new file mode 100644 |
| index 0000000..ba3ac4b |
| --- /dev/null |
| +++ b/package/kernel/mac80211/patches/subsys/900-mac80211-zero-wait-dfs.patch |
| @@ -0,0 +1,667 @@ |
| +diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h |
| +index 080d220..6278545 100644 |
| +--- a/include/net/cfg80211.h |
| ++++ b/include/net/cfg80211.h |
| +@@ -4024,6 +4024,15 @@ struct mgmt_frame_regs { |
| + * @set_sar_specs: Update the SAR (TX power) settings. |
| + * |
| + * @color_change: Initiate a color change. |
| ++ * |
| ++ * @set_radar_offchan: Configure dedicated offchannel chain available for |
| ++ * radar/CAC detection on some hw. This chain can't be used to transmit |
| ++ * or receive frames and it is bounded to a running wdev. |
| ++ * Offchannel radar/CAC detection allows to avoid the CAC downtime |
| ++ * switching to a different channel during CAC detection on the selected |
| ++ * radar channel. |
| ++ * The caller is expected to set chandef pointer to NULL in order to |
| ++ * disable offchannel CAC/radar detection. |
| + */ |
| + struct cfg80211_ops { |
| + int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); |
| +@@ -4355,6 +4364,8 @@ struct cfg80211_ops { |
| + int (*color_change)(struct wiphy *wiphy, |
| + struct net_device *dev, |
| + struct cfg80211_color_change_settings *params); |
| ++ int (*set_radar_offchan)(struct wiphy *wiphy, |
| ++ struct cfg80211_chan_def *chandef); |
| + }; |
| + |
| + /* |
| +@@ -7529,15 +7540,33 @@ void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer, |
| + void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp); |
| + |
| + /** |
| +- * cfg80211_radar_event - radar detection event |
| ++ * __cfg80211_radar_event - radar detection event |
| + * @wiphy: the wiphy |
| + * @chandef: chandef for the current channel |
| ++ * @offchan: the radar has been detected on the offchannel chain |
| + * @gfp: context flags |
| + * |
| + * This function is called when a radar is detected on the current chanenl. |
| + */ |
| +-void cfg80211_radar_event(struct wiphy *wiphy, |
| +- struct cfg80211_chan_def *chandef, gfp_t gfp); |
| ++void __cfg80211_radar_event(struct wiphy *wiphy, |
| ++ struct cfg80211_chan_def *chandef, |
| ++ bool offchan, gfp_t gfp); |
| ++ |
| ++static inline void |
| ++cfg80211_radar_event(struct wiphy *wiphy, |
| ++ struct cfg80211_chan_def *chandef, |
| ++ gfp_t gfp) |
| ++{ |
| ++ __cfg80211_radar_event(wiphy, chandef, false, gfp); |
| ++} |
| ++ |
| ++static inline void |
| ++cfg80211_offchan_radar_event(struct wiphy *wiphy, |
| ++ struct cfg80211_chan_def *chandef, |
| ++ gfp_t gfp) |
| ++{ |
| ++ __cfg80211_radar_event(wiphy, chandef, true, gfp); |
| ++} |
| + |
| + /** |
| + * cfg80211_sta_opmode_change_notify - STA's ht/vht operation mode change event |
| +@@ -7568,6 +7597,14 @@ void cfg80211_cac_event(struct net_device *netdev, |
| + const struct cfg80211_chan_def *chandef, |
| + enum nl80211_radar_event event, gfp_t gfp); |
| + |
| ++/** |
| ++ * cfg80211_offchan_cac_abort - Channel Availability Check offchan abort event |
| ++ * @wiphy: the wiphy |
| ++ * |
| ++ * This function is called by the driver when a Channel Availability Check |
| ++ * (CAC) is aborted by a offchannel dedicated chain. |
| ++ */ |
| ++void cfg80211_offchan_cac_abort(struct wiphy *wiphy); |
| + |
| + /** |
| + * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying |
| +diff --git a/include/net/mac80211.h b/include/net/mac80211.h |
| +index afab7ec..acf637d 100644 |
| +--- a/include/net/mac80211.h |
| ++++ b/include/net/mac80211.h |
| +@@ -3939,6 +3939,14 @@ struct ieee80211_prep_tx_info { |
| + * twt structure. |
| + * @twt_teardown_request: Update the hw with TWT teardown request received |
| + * from the peer. |
| ++ * @set_radar_offchan: Configure dedicated offchannel chain available for |
| ++ * radar/CAC detection on some hw. This chain can't be used to transmit |
| ++ * or receive frames and it is bounded to a running wdev. |
| ++ * Offchannel radar/CAC detection allows to avoid the CAC downtime |
| ++ * switching to a different channel during CAC detection on the selected |
| ++ * radar channel. |
| ++ * The caller is expected to set chandef pointer to NULL in order to |
| ++ * disable offchannel CAC/radar detection. |
| + */ |
| + struct ieee80211_ops { |
| + void (*tx)(struct ieee80211_hw *hw, |
| +@@ -4267,6 +4275,8 @@ struct ieee80211_ops { |
| + struct ieee80211_twt_setup *twt); |
| + void (*twt_teardown_request)(struct ieee80211_hw *hw, |
| + struct ieee80211_sta *sta, u8 flowid); |
| ++ int (*set_radar_offchan)(struct ieee80211_hw *hw, |
| ++ struct cfg80211_chan_def *chandef); |
| + }; |
| + |
| + /** |
| +diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h |
| +index 5f4a094..7634ba0 100644 |
| +--- a/include/uapi/linux/nl80211.h |
| ++++ b/include/uapi/linux/nl80211.h |
| +@@ -2596,6 +2596,13 @@ enum nl80211_commands { |
| + * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce |
| + * transmit power to stay within regulatory limits. u32, dBi. |
| + * |
| ++ * @NL80211_ATTR_RADAR_OFFCHAN: Configure dedicated offchannel chain available for |
| ++ * radar/CAC detection on some hw. This chain can't be used to transmit |
| ++ * or receive frames and it is bounded to a running wdev. |
| ++ * Offchannel radar/CAC detection allows to avoid the CAC downtime |
| ++ * switching on a different channel during CAC detection on the selected |
| ++ * radar channel. |
| ++ * |
| + * @NUM_NL80211_ATTR: total number of nl80211_attrs available |
| + * @NL80211_ATTR_MAX: highest attribute number currently defined |
| + * @__NL80211_ATTR_AFTER_LAST: internal use |
| +@@ -3101,6 +3108,8 @@ enum nl80211_attrs { |
| + |
| + NL80211_ATTR_WIPHY_ANTENNA_GAIN, |
| + |
| ++ NL80211_ATTR_RADAR_OFFCHAN, |
| ++ |
| + /* add attributes here, update the policy in nl80211.c */ |
| + |
| + __NL80211_ATTR_AFTER_LAST, |
| +@@ -6000,6 +6009,9 @@ enum nl80211_feature_flags { |
| + * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision |
| + * detection and change announcemnts. |
| + * |
| ++ * @NL80211_EXT_FEATURE_RADAR_OFFCHAN: Device supports offchannel radar/CAC |
| ++ * detection. |
| ++ * |
| + * @NUM_NL80211_EXT_FEATURES: number of extended features. |
| + * @MAX_NL80211_EXT_FEATURES: highest extended feature index. |
| + */ |
| +@@ -6065,6 +6077,7 @@ enum nl80211_ext_feature_index { |
| + NL80211_EXT_FEATURE_SECURE_RTT, |
| + NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE, |
| + NL80211_EXT_FEATURE_BSS_COLOR, |
| ++ NL80211_EXT_FEATURE_RADAR_OFFCHAN, |
| + |
| + /* add new features before the definition below */ |
| + NUM_NL80211_EXT_FEATURES, |
| +diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c |
| +index 8b7c12a..887af1e 100644 |
| +--- a/net/mac80211/cfg.c |
| ++++ b/net/mac80211/cfg.c |
| +@@ -4353,6 +4353,18 @@ out: |
| + return err; |
| + } |
| + |
| ++static int |
| ++ieee80211_set_radar_offchan(struct wiphy *wiphy, |
| ++ struct cfg80211_chan_def *chandef) |
| ++{ |
| ++ struct ieee80211_local *local = wiphy_priv(wiphy); |
| ++ |
| ++ if (!local->ops->set_radar_offchan) |
| ++ return -EOPNOTSUPP; |
| ++ |
| ++ return local->ops->set_radar_offchan(&local->hw, chandef); |
| ++} |
| ++ |
| + const struct cfg80211_ops mac80211_config_ops = { |
| + .add_virtual_intf = ieee80211_add_iface, |
| + .del_virtual_intf = ieee80211_del_iface, |
| +@@ -4458,4 +4470,5 @@ const struct cfg80211_ops mac80211_config_ops = { |
| + .reset_tid_config = ieee80211_reset_tid_config, |
| + .set_sar_specs = ieee80211_set_sar_specs, |
| + .color_change = ieee80211_color_change, |
| ++ .set_radar_offchan = ieee80211_set_radar_offchan, |
| + }; |
| +diff --git a/net/wireless/chan.c b/net/wireless/chan.c |
| +index 1382a5f..75b4151 100644 |
| +--- a/net/wireless/chan.c |
| ++++ b/net/wireless/chan.c |
| +@@ -712,6 +712,20 @@ static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy, |
| + return false; |
| + } |
| + |
| ++static bool |
| ++cfg80211_offchan_chain_is_active(struct cfg80211_registered_device *rdev, |
| ++ struct ieee80211_channel *channel) |
| ++{ |
| ++ |
| ++ if (!rdev->offchan_radar_wdev) |
| ++ return false; |
| ++ |
| ++ if (!cfg80211_chandef_valid(&rdev->offchan_radar_chandef)) |
| ++ return false; |
| ++ |
| ++ return cfg80211_is_sub_chan(&rdev->offchan_radar_chandef, channel); |
| ++} |
| ++ |
| + bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, |
| + struct ieee80211_channel *chan) |
| + { |
| +@@ -728,6 +742,9 @@ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, |
| + |
| + if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan)) |
| + return true; |
| ++ |
| ++ if (cfg80211_offchan_chain_is_active(rdev, chan)) |
| ++ return true; |
| + } |
| + |
| + return false; |
| +diff --git a/net/wireless/core.c b/net/wireless/core.c |
| +index b181dad..378fb4e 100644 |
| +--- a/net/wireless/core.c |
| ++++ b/net/wireless/core.c |
| +@@ -551,6 +551,9 @@ use_default_name: |
| + INIT_WORK(&rdev->rfkill_block, cfg80211_rfkill_block_work); |
| + INIT_WORK(&rdev->conn_work, cfg80211_conn_work); |
| + INIT_WORK(&rdev->event_work, cfg80211_event_work); |
| ++ INIT_WORK(&rdev->offchan_cac_abort_wk, cfg80211_offchan_cac_abort_wk); |
| ++ INIT_DELAYED_WORK(&rdev->offchan_cac_done_wk, |
| ++ cfg80211_offchan_cac_done_wk); |
| + |
| + init_waitqueue_head(&rdev->dev_wait); |
| + |
| +@@ -1045,11 +1048,13 @@ void wiphy_unregister(struct wiphy *wiphy) |
| + cancel_work_sync(&rdev->conn_work); |
| + flush_work(&rdev->event_work); |
| + cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); |
| ++ cancel_delayed_work_sync(&rdev->offchan_cac_done_wk); |
| + flush_work(&rdev->destroy_work); |
| + flush_work(&rdev->sched_scan_stop_wk); |
| + flush_work(&rdev->propagate_radar_detect_wk); |
| + flush_work(&rdev->propagate_cac_done_wk); |
| + flush_work(&rdev->mgmt_registrations_update_wk); |
| ++ flush_work(&rdev->offchan_cac_abort_wk); |
| + |
| + #ifdef CONFIG_PM |
| + if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) |
| +@@ -1188,6 +1193,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, |
| + |
| + cfg80211_pmsr_wdev_down(wdev); |
| + |
| ++ cfg80211_stop_offchan_radar_detection(wdev); |
| ++ |
| + switch (wdev->iftype) { |
| + case NL80211_IFTYPE_ADHOC: |
| + __cfg80211_leave_ibss(rdev, dev, true); |
| +diff --git a/net/wireless/core.h b/net/wireless/core.h |
| +index 19fcdd7..7fe31aa 100644 |
| +--- a/net/wireless/core.h |
| ++++ b/net/wireless/core.h |
| +@@ -84,6 +84,11 @@ struct cfg80211_registered_device { |
| + |
| + struct delayed_work dfs_update_channels_wk; |
| + |
| ++ struct wireless_dev *offchan_radar_wdev; |
| ++ struct cfg80211_chan_def offchan_radar_chandef; |
| ++ struct delayed_work offchan_cac_done_wk; |
| ++ struct work_struct offchan_cac_abort_wk; |
| ++ |
| + /* netlink port which started critical protocol (0 means not started) */ |
| + u32 crit_proto_nlportid; |
| + |
| +@@ -489,6 +494,17 @@ cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, |
| + |
| + void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev); |
| + |
| ++int |
| ++cfg80211_start_offchan_radar_detection(struct cfg80211_registered_device *rdev, |
| ++ struct wireless_dev *wdev, |
| ++ struct cfg80211_chan_def *chandef); |
| ++ |
| ++void cfg80211_stop_offchan_radar_detection(struct wireless_dev *wdev); |
| ++ |
| ++void cfg80211_offchan_cac_done_wk(struct work_struct *work); |
| ++ |
| ++void cfg80211_offchan_cac_abort_wk(struct work_struct *work); |
| ++ |
| + bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, |
| + struct ieee80211_channel *chan); |
| + |
| +diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c |
| +index a8a17b9..8f230d3 100644 |
| +--- a/net/wireless/mlme.c |
| ++++ b/net/wireless/mlme.c |
| +@@ -903,13 +903,13 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) |
| + } |
| + |
| + |
| +-void cfg80211_radar_event(struct wiphy *wiphy, |
| +- struct cfg80211_chan_def *chandef, |
| +- gfp_t gfp) |
| ++void __cfg80211_radar_event(struct wiphy *wiphy, |
| ++ struct cfg80211_chan_def *chandef, |
| ++ bool offchan, gfp_t gfp) |
| + { |
| + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
| + |
| +- trace_cfg80211_radar_event(wiphy, chandef); |
| ++ trace_cfg80211_radar_event(wiphy, chandef, offchan); |
| + |
| + /* only set the chandef supplied channel to unavailable, in |
| + * case the radar is detected on only one of multiple channels |
| +@@ -917,6 +917,9 @@ void cfg80211_radar_event(struct wiphy *wiphy, |
| + */ |
| + cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE); |
| + |
| ++ if (offchan) |
| ++ queue_work(cfg80211_wq, &rdev->offchan_cac_abort_wk); |
| ++ |
| + cfg80211_sched_dfs_chan_update(rdev); |
| + |
| + nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp); |
| +@@ -924,7 +927,7 @@ void cfg80211_radar_event(struct wiphy *wiphy, |
| + memcpy(&rdev->radar_chandef, chandef, sizeof(struct cfg80211_chan_def)); |
| + queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk); |
| + } |
| +-EXPORT_SYMBOL(cfg80211_radar_event); |
| ++EXPORT_SYMBOL(__cfg80211_radar_event); |
| + |
| + void cfg80211_cac_event(struct net_device *netdev, |
| + const struct cfg80211_chan_def *chandef, |
| +@@ -968,3 +971,142 @@ void cfg80211_cac_event(struct net_device *netdev, |
| + nl80211_radar_notify(rdev, chandef, event, netdev, gfp); |
| + } |
| + EXPORT_SYMBOL(cfg80211_cac_event); |
| ++ |
| ++static void |
| ++__cfg80211_offchan_cac_event(struct cfg80211_registered_device *rdev, |
| ++ struct wireless_dev *wdev, |
| ++ const struct cfg80211_chan_def *chandef, |
| ++ enum nl80211_radar_event event) |
| ++{ |
| ++ struct wiphy *wiphy = &rdev->wiphy; |
| ++ struct net_device *netdev; |
| ++ |
| ++ lockdep_assert_wiphy(&rdev->wiphy); |
| ++ |
| ++ if (!cfg80211_chandef_valid(chandef)) |
| ++ return; |
| ++ |
| ++ if (!rdev->offchan_radar_wdev) |
| ++ return; |
| ++ |
| ++ switch (event) { |
| ++ case NL80211_RADAR_CAC_FINISHED: |
| ++ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); |
| ++ memcpy(&rdev->cac_done_chandef, chandef, sizeof(*chandef)); |
| ++ queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk); |
| ++ cfg80211_sched_dfs_chan_update(rdev); |
| ++ wdev = rdev->offchan_radar_wdev; |
| ++ break; |
| ++ case NL80211_RADAR_CAC_ABORTED: |
| ++ if (!cancel_delayed_work(&rdev->offchan_cac_done_wk)) |
| ++ return; |
| ++ wdev = rdev->offchan_radar_wdev; |
| ++ break; |
| ++ case NL80211_RADAR_CAC_STARTED: |
| ++ break; |
| ++ default: |
| ++ return; |
| ++ } |
| ++ |
| ++ netdev = wdev ? wdev->netdev : NULL; |
| ++ nl80211_radar_notify(rdev, chandef, event, netdev, GFP_KERNEL); |
| ++} |
| ++ |
| ++static void |
| ++cfg80211_offchan_cac_event(struct cfg80211_registered_device *rdev, |
| ++ const struct cfg80211_chan_def *chandef, |
| ++ enum nl80211_radar_event event) |
| ++{ |
| ++ wiphy_lock(&rdev->wiphy); |
| ++ __cfg80211_offchan_cac_event(rdev, rdev->offchan_radar_wdev, |
| ++ chandef, event); |
| ++ wiphy_unlock(&rdev->wiphy); |
| ++} |
| ++ |
| ++void cfg80211_offchan_cac_done_wk(struct work_struct *work) |
| ++{ |
| ++ struct delayed_work *delayed_work = to_delayed_work(work); |
| ++ struct cfg80211_registered_device *rdev; |
| ++ |
| ++ rdev = container_of(delayed_work, struct cfg80211_registered_device, |
| ++ offchan_cac_done_wk); |
| ++ cfg80211_offchan_cac_event(rdev, &rdev->offchan_radar_chandef, |
| ++ NL80211_RADAR_CAC_FINISHED); |
| ++} |
| ++ |
| ++void cfg80211_offchan_cac_abort_wk(struct work_struct *work) |
| ++{ |
| ++ struct cfg80211_registered_device *rdev; |
| ++ |
| ++ rdev = container_of(work, struct cfg80211_registered_device, |
| ++ offchan_cac_abort_wk); |
| ++ cfg80211_offchan_cac_event(rdev, &rdev->offchan_radar_chandef, |
| ++ NL80211_RADAR_CAC_ABORTED); |
| ++} |
| ++ |
| ++void cfg80211_offchan_cac_abort(struct wiphy *wiphy) |
| ++{ |
| ++ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
| ++ |
| ++ queue_work(cfg80211_wq, &rdev->offchan_cac_abort_wk); |
| ++} |
| ++EXPORT_SYMBOL(cfg80211_offchan_cac_abort); |
| ++ |
| ++int |
| ++cfg80211_start_offchan_radar_detection(struct cfg80211_registered_device *rdev, |
| ++ struct wireless_dev *wdev, |
| ++ struct cfg80211_chan_def *chandef) |
| ++{ |
| ++ unsigned int cac_time_ms; |
| ++ int err; |
| ++ |
| ++ lockdep_assert_wiphy(&rdev->wiphy); |
| ++ |
| ++ if (!wiphy_ext_feature_isset(&rdev->wiphy, |
| ++ NL80211_EXT_FEATURE_RADAR_OFFCHAN)) |
| ++ return -EOPNOTSUPP; |
| ++ |
| ++ /* Offchannel chain already locked by another wdev */ |
| ++ if (rdev->offchan_radar_wdev && rdev->offchan_radar_wdev != wdev) |
| ++ return -EBUSY; |
| ++ |
| ++ /* CAC already in progress on the offchannel chain */ |
| ++ if (rdev->offchan_radar_wdev == wdev && |
| ++ delayed_work_pending(&rdev->offchan_cac_done_wk)) |
| ++ return -EBUSY; |
| ++ |
| ++ err = rdev_set_radar_offchan(rdev, chandef); |
| ++ if (err) |
| ++ return err; |
| ++ |
| ++ cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, chandef); |
| ++ if (!cac_time_ms) |
| ++ cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; |
| ++ |
| ++ rdev->offchan_radar_chandef = *chandef; |
| ++ rdev->offchan_radar_wdev = wdev; /* Get offchain ownership */ |
| ++ |
| ++ __cfg80211_offchan_cac_event(rdev, wdev, chandef, |
| ++ NL80211_RADAR_CAC_STARTED); |
| ++ queue_delayed_work(cfg80211_wq, &rdev->offchan_cac_done_wk, |
| ++ msecs_to_jiffies(cac_time_ms)); |
| ++ |
| ++ return 0; |
| ++} |
| ++ |
| ++void cfg80211_stop_offchan_radar_detection(struct wireless_dev *wdev) |
| ++{ |
| ++ struct wiphy *wiphy = wdev->wiphy; |
| ++ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
| ++ |
| ++ lockdep_assert_wiphy(wiphy); |
| ++ |
| ++ if (wdev != rdev->offchan_radar_wdev) |
| ++ return; |
| ++ |
| ++ rdev_set_radar_offchan(rdev, NULL); |
| ++ rdev->offchan_radar_wdev = NULL; /* Release offchain ownership */ |
| ++ |
| ++ __cfg80211_offchan_cac_event(rdev, wdev, &rdev->offchan_radar_chandef, |
| ++ NL80211_RADAR_CAC_ABORTED); |
| ++} |
| +diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c |
| +index 4a3de61..0cf9f68 100644 |
| +--- a/net/wireless/nl80211.c |
| ++++ b/net/wireless/nl80211.c |
| +@@ -781,6 +781,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { |
| + [NL80211_ATTR_COLOR_CHANGE_COLOR] = { .type = NLA_U8 }, |
| + [NL80211_ATTR_COLOR_CHANGE_ELEMS] = NLA_POLICY_NESTED(nl80211_policy), |
| + [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 }, |
| ++ [NL80211_ATTR_RADAR_OFFCHAN] = { .type = NLA_FLAG }, |
| + }; |
| + |
| + /* policy for the key attributes */ |
| +@@ -9106,38 +9107,60 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, |
| + struct cfg80211_chan_def chandef; |
| + enum nl80211_dfs_regions dfs_region; |
| + unsigned int cac_time_ms; |
| +- int err; |
| ++ int err = -EINVAL; |
| ++ |
| ++ flush_delayed_work(&rdev->dfs_update_channels_wk); |
| ++ |
| ++ wiphy_lock(wiphy); |
| + |
| + dfs_region = reg_get_dfs_region(wiphy); |
| + if (dfs_region == NL80211_DFS_UNSET) |
| +- return -EINVAL; |
| ++ goto unlock; |
| + |
| + err = nl80211_parse_chandef(rdev, info, &chandef); |
| + if (err) |
| +- return err; |
| +- |
| +- if (netif_carrier_ok(dev)) |
| +- return -EBUSY; |
| +- |
| +- if (wdev->cac_started) |
| +- return -EBUSY; |
| ++ goto unlock; |
| + |
| + err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype); |
| + if (err < 0) |
| +- return err; |
| ++ goto unlock; |
| + |
| +- if (err == 0) |
| +- return -EINVAL; |
| ++ if (err == 0) { |
| ++ err = -EINVAL; |
| ++ goto unlock; |
| ++ } |
| + |
| +- if (!cfg80211_chandef_dfs_usable(wiphy, &chandef)) |
| +- return -EINVAL; |
| ++ if (!cfg80211_chandef_dfs_usable(wiphy, &chandef)) { |
| ++ err = -EINVAL; |
| ++ goto unlock; |
| ++ } |
| ++ |
| ++ if (nla_get_flag(info->attrs[NL80211_ATTR_RADAR_OFFCHAN])) { |
| ++ err = cfg80211_start_offchan_radar_detection(rdev, wdev, |
| ++ &chandef); |
| ++ goto unlock; |
| ++ } |
| ++ |
| ++ if (netif_carrier_ok(dev)) { |
| ++ err = -EBUSY; |
| ++ goto unlock; |
| ++ } |
| ++ |
| ++ if (wdev->cac_started) { |
| ++ err = -EBUSY; |
| ++ goto unlock; |
| ++ } |
| + |
| + /* CAC start is offloaded to HW and can't be started manually */ |
| +- if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) |
| +- return -EOPNOTSUPP; |
| ++ if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) { |
| ++ err = -EOPNOTSUPP; |
| ++ goto unlock; |
| ++ } |
| + |
| +- if (!rdev->ops->start_radar_detection) |
| +- return -EOPNOTSUPP; |
| ++ if (!rdev->ops->start_radar_detection) { |
| ++ err = -EOPNOTSUPP; |
| ++ goto unlock; |
| ++ } |
| + |
| + cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef); |
| + if (WARN_ON(!cac_time_ms)) |
| +@@ -9150,6 +9173,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, |
| + wdev->cac_start_time = jiffies; |
| + wdev->cac_time_ms = cac_time_ms; |
| + } |
| ++unlock: |
| ++ wiphy_unlock(wiphy); |
| ++ |
| + return err; |
| + } |
| + |
| +@@ -15762,7 +15788,8 @@ static const struct genl_small_ops nl80211_small_ops[] = { |
| + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
| + .doit = nl80211_start_radar_detection, |
| + .flags = GENL_UNS_ADMIN_PERM, |
| +- .internal_flags = NL80211_FLAG_NEED_NETDEV_UP, |
| ++ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | |
| ++ NL80211_FLAG_NO_WIPHY_MTX, |
| + }, |
| + { |
| + .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES, |
| +diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h |
| +index 36422cf..c566f47 100644 |
| +--- a/net/wireless/rdev-ops.h |
| ++++ b/net/wireless/rdev-ops.h |
| +@@ -1381,4 +1381,21 @@ static inline int rdev_color_change(struct cfg80211_registered_device *rdev, |
| + return ret; |
| + } |
| + |
| ++static inline int |
| ++rdev_set_radar_offchan(struct cfg80211_registered_device *rdev, |
| ++ struct cfg80211_chan_def *chandef) |
| ++{ |
| ++ struct wiphy *wiphy = &rdev->wiphy; |
| ++ int ret; |
| ++ |
| ++ if (!rdev->ops->set_radar_offchan) |
| ++ return -EOPNOTSUPP; |
| ++ |
| ++ trace_rdev_set_radar_offchan(wiphy, chandef); |
| ++ ret = rdev->ops->set_radar_offchan(wiphy, chandef); |
| ++ trace_rdev_return_int(wiphy, ret); |
| ++ |
| ++ return ret; |
| ++} |
| ++ |
| + #endif /* __CFG80211_RDEV_OPS */ |
| +diff --git a/net/wireless/trace.h b/net/wireless/trace.h |
| +index 973ce68..cd60ada 100644 |
| +--- a/net/wireless/trace.h |
| ++++ b/net/wireless/trace.h |
| +@@ -3022,18 +3022,21 @@ TRACE_EVENT(cfg80211_ch_switch_started_notify, |
| + ); |
| + |
| + TRACE_EVENT(cfg80211_radar_event, |
| +- TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), |
| +- TP_ARGS(wiphy, chandef), |
| ++ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, |
| ++ bool offchan), |
| ++ TP_ARGS(wiphy, chandef, offchan), |
| + TP_STRUCT__entry( |
| + WIPHY_ENTRY |
| + CHAN_DEF_ENTRY |
| ++ __field(bool, offchan) |
| + ), |
| + TP_fast_assign( |
| + WIPHY_ASSIGN; |
| + CHAN_DEF_ASSIGN(chandef); |
| ++ __entry->offchan = offchan; |
| + ), |
| +- TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, |
| +- WIPHY_PR_ARG, CHAN_DEF_PR_ARG) |
| ++ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", offchan %d", |
| ++ WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->offchan) |
| + ); |
| + |
| + TRACE_EVENT(cfg80211_cac_event, |
| +@@ -3643,6 +3646,25 @@ TRACE_EVENT(cfg80211_bss_color_notify, |
| + __entry->color_bitmap) |
| + ); |
| + |
| ++TRACE_EVENT(rdev_set_radar_offchan, |
| ++ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), |
| ++ |
| ++ TP_ARGS(wiphy, chandef), |
| ++ |
| ++ TP_STRUCT__entry( |
| ++ WIPHY_ENTRY |
| ++ CHAN_DEF_ENTRY |
| ++ ), |
| ++ |
| ++ TP_fast_assign( |
| ++ WIPHY_ASSIGN; |
| ++ CHAN_DEF_ASSIGN(chandef) |
| ++ ), |
| ++ |
| ++ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, |
| ++ WIPHY_PR_ARG, CHAN_DEF_PR_ARG) |
| ++); |
| ++ |
| + #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ |
| + |
| + #undef TRACE_INCLUDE_PATH |
| + |