summaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/core.c3
-rw-r--r--net/wireless/core.h13
-rw-r--r--net/wireless/mlme.c113
-rw-r--r--net/wireless/nl80211.c17
-rw-r--r--net/wireless/rdev-ops.h17
-rw-r--r--net/wireless/trace.h19
6 files changed, 176 insertions, 6 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c
index eb297e1015e0..39b2d4ae581d 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -545,6 +545,7 @@ 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_DELAYED_WORK(&rdev->offchan_cac_work, cfg80211_offchan_cac_work);
init_waitqueue_head(&rdev->dev_wait);
@@ -1207,6 +1208,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 1720abf36f92..612d460dcde0 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -84,6 +84,10 @@ 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_work;
+
/* netlink port which started critical protocol (0 means not started) */
u32 crit_proto_nlportid;
@@ -491,6 +495,15 @@ 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_work(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 783acd2c4211..46f2ec4d50d7 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -970,3 +970,116 @@ void cfg80211_cac_event(struct net_device *netdev,
nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
}
EXPORT_SYMBOL(cfg80211_cac_event);
+
+void cfg80211_offchan_cac_work(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_work);
+ cfg80211_offchan_cac_event(&rdev->wiphy, &rdev->offchan_radar_chandef,
+ NL80211_RADAR_CAC_FINISHED);
+}
+
+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 (event != NL80211_RADAR_CAC_STARTED && !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;
+ rdev->offchan_radar_wdev = NULL;
+ break;
+ case NL80211_RADAR_CAC_ABORTED:
+ cancel_delayed_work(&rdev->offchan_cac_work);
+ wdev = rdev->offchan_radar_wdev;
+ rdev->offchan_radar_wdev = NULL;
+ break;
+ case NL80211_RADAR_CAC_STARTED:
+ WARN_ON(!wdev);
+ rdev->offchan_radar_wdev = wdev;
+ break;
+ default:
+ return;
+ }
+
+ netdev = wdev ? wdev->netdev : NULL;
+ nl80211_radar_notify(rdev, chandef, event, netdev, GFP_KERNEL);
+}
+
+void cfg80211_offchan_cac_event(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ wiphy_lock(wiphy);
+ __cfg80211_offchan_cac_event(rdev, NULL, chandef, event);
+ wiphy_unlock(wiphy);
+}
+EXPORT_SYMBOL(cfg80211_offchan_cac_event);
+
+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;
+
+ if (rdev->offchan_radar_wdev)
+ 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;
+ __cfg80211_offchan_cac_event(rdev, wdev, chandef,
+ NL80211_RADAR_CAC_STARTED);
+ queue_delayed_work(cfg80211_wq, &rdev->offchan_cac_work,
+ 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);
+
+ __cfg80211_offchan_cac_event(rdev, NULL, NULL,
+ NL80211_RADAR_CAC_ABORTED);
+}
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index a27b3b5fa210..83a1ba96e172 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -776,6 +776,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_MBSSID_CONFIG] =
NLA_POLICY_NESTED(nl80211_mbssid_config_policy),
[NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED },
+ [NL80211_ATTR_RADAR_OFFCHAN] = { .type = NLA_FLAG },
};
/* policy for the key attributes */
@@ -9284,12 +9285,6 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
if (err)
return err;
- if (netif_carrier_ok(dev))
- return -EBUSY;
-
- if (wdev->cac_started)
- return -EBUSY;
-
err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype);
if (err < 0)
return err;
@@ -9300,6 +9295,16 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
if (!cfg80211_chandef_dfs_usable(wiphy, &chandef))
return -EINVAL;
+ if (nla_get_flag(info->attrs[NL80211_ATTR_RADAR_OFFCHAN]))
+ return cfg80211_start_offchan_radar_detection(rdev, wdev,
+ &chandef);
+
+ if (netif_carrier_ok(dev))
+ return -EBUSY;
+
+ if (wdev->cac_started)
+ return -EBUSY;
+
/* 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;
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index cc1efec4b27b..8672b3ef99e4 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -1395,4 +1395,21 @@ rdev_set_fils_aad(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 ad6c16a06bcb..0b27eaa14a18 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -3674,6 +3674,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