Commit bc2dfc02 authored by Lorenzo Bianconi's avatar Lorenzo Bianconi Committed by Johannes Berg
Browse files

cfg80211: implement APIs for dedicated radar detection HW



If a dedicated (off-channel) radar detection hardware (chain)
is available in the hardware/driver, allow this to be used by
calling the NL80211_CMD_RADAR_DETECT command with a new flag
attribute requesting off-channel radar detection is used.

Offchannel CAC (channel availability check) avoids the CAC
downtime when switching to a radar channel or when turning on
the AP.

Drivers advertise support for this using the new feature flag
NL80211_EXT_FEATURE_RADAR_OFFCHAN.

Tested-by: default avatarEvelyn Tsai <evelyn.tsai@mediatek.com>
Signed-off-by: default avatarLorenzo Bianconi <lorenzo@kernel.org>
Link: https://lore.kernel.org/r/7468e291ef5d05d692c1738d25b8f778d8ea5c3f.1634979655.git.lorenzo@kernel.org
Link: https://lore.kernel.org/r/1e60e60fef00e14401adae81c3d49f3e5f307537.1634979655.git.lorenzo@kernel.org
Link: https://lore.kernel.org/r/85fa50f57fc3adb2934c8d9ca0be30394de6b7e8.1634979655.git.lorenzo@kernel.org
Link: https://lore.kernel.org/r/4b6c08671ad59aae0ac46fc94c02f31b1610eb72.1634979655.git.lorenzo@kernel.org
Link: https://lore.kernel.org/r/241849ccaf2c228873c6f8495bf87b19159ba458.1634979655.git.lorenzo@kernel.org


[remove offchan_mutex, fix cfg80211_stop_offchan_radar_detection(),
 remove gfp_t argument, fix documentation, fix tracing]
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 3b1abcf1
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -4072,6 +4072,15 @@ struct mgmt_frame_regs {
 * @set_fils_aad: Set FILS AAD data to the AP driver so that the driver can use
 *	those to decrypt (Re)Association Request and encrypt (Re)Association
 *	Response frame.
 *
 * @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);
@@ -4404,6 +4413,8 @@ struct cfg80211_ops {
				struct cfg80211_color_change_settings *params);
	int     (*set_fils_aad)(struct wiphy *wiphy, struct net_device *dev,
				struct cfg80211_fils_aad *fils_aad);
	int	(*set_radar_offchan)(struct wiphy *wiphy,
				     struct cfg80211_chan_def *chandef);
};

/*
@@ -7633,6 +7644,20 @@ 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_event - Channel Availability Check (CAC) offchan event
 * @wiphy: the wiphy
 * @chandef: chandef for the current channel
 * @event: type of event
 *
 * This function is called when a Channel Availability Check (CAC) is finished,
 * started or aborted by a offchannel dedicated chain.
 *
 * Note that this acquires the wiphy lock.
 */
void cfg80211_offchan_cac_event(struct wiphy *wiphy,
				const struct cfg80211_chan_def *chandef,
				enum nl80211_radar_event event);

/**
 * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
+13 −0
Original line number Diff line number Diff line
@@ -2639,6 +2639,13 @@ enum nl80211_commands {
 *	Mandatory parameter for the transmitting interface to enable MBSSID.
 *	Optional for the non-transmitting interfaces.
 *
 * @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
@@ -3145,6 +3152,8 @@ enum nl80211_attrs {
	NL80211_ATTR_MBSSID_CONFIG,
	NL80211_ATTR_MBSSID_ELEMS,

	NL80211_ATTR_RADAR_OFFCHAN,

	/* add attributes here, update the policy in nl80211.c */

	__NL80211_ATTR_AFTER_LAST,
@@ -6051,6 +6060,9 @@ enum nl80211_feature_flags {
 *	frames. Userspace has to share FILS AAD details to the driver by using
 *	@NL80211_CMD_SET_FILS_AAD.
 *
 * @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.
 */
@@ -6117,6 +6129,7 @@ enum nl80211_ext_feature_index {
	NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
	NL80211_EXT_FEATURE_BSS_COLOR,
	NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
	NL80211_EXT_FEATURE_RADAR_OFFCHAN,

	/* add new features before the definition below */
	NUM_NL80211_EXT_FEATURES,
+3 −0
Original line number Diff line number Diff line
@@ -545,6 +545,7 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
	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);
+13 −0
Original line number Diff line number Diff line
@@ -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);

+113 −0
Original line number Diff line number Diff line
@@ -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);
}
Loading