diff mbox series

[v4,1/3] nl80211: Extended ROC support for 40-80 MHz bandwidth

Message ID 1647857797-19749-2-git-send-email-quic_ppranees@quicinc.com (mailing list archive)
State Changes Requested
Delegated to: Johannes Berg
Headers show
Series mac80211: Add extended ROC support for 40-80 MHz bandwidth | expand

Commit Message

P Praneesh March 21, 2022, 10:16 a.m. UTC
Replace struct ieee80211_channel with struct cfg80211_chan_def in
remain on channel apis, because the channel width information is
needed to handle centre frequency of 80MHz, and it is available
in cfg80211_chan_def. Also, add SUPPORTS_EXT_REMAIN_ON_CHAN feature
flag check before driver ops call.

Co-developed-by: Sathishkumar Muruganandam <quic_murugana@quicinc.com>
Signed-off-by: Sathishkumar Muruganandam <quic_murugana@quicinc.com>
Signed-off-by: P Praneesh <quic_ppranees@quicinc.com>
---
 drivers/net/wireless/ath/ath10k/mac.c              |  3 +-
 drivers/net/wireless/ath/ath6kl/cfg80211.c         |  5 +-
 drivers/net/wireless/ath/ath6kl/wmi.c              | 10 +++-
 drivers/net/wireless/ath/ath9k/main.c              |  4 +-
 drivers/net/wireless/ath/wil6210/cfg80211.c        | 13 +++--
 drivers/net/wireless/ath/wil6210/p2p.c             | 27 +++++++--
 .../broadcom/brcm80211/brcmfmac/cfg80211.c         |  2 +-
 .../net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 10 +++-
 .../net/wireless/broadcom/brcm80211/brcmfmac/p2p.h |  2 +-
 drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c  |  3 +-
 drivers/net/wireless/mac80211_hwsim.c              |  3 +-
 drivers/net/wireless/marvell/mwifiex/cfg80211.c    | 10 +++-
 drivers/net/wireless/marvell/mwifiex/sta_event.c   |  4 +-
 drivers/net/wireless/marvell/mwifiex/uap_event.c   |  4 +-
 drivers/net/wireless/mediatek/mt76/mt7615/main.c   |  3 +-
 drivers/net/wireless/microchip/wilc1000/cfg80211.c | 17 ++++--
 drivers/net/wireless/rsi/rsi_91x_mac80211.c        |  3 +-
 drivers/net/wireless/ti/wlcore/main.c              |  3 +-
 drivers/staging/rtl8723bs/include/ioctl_cfg80211.h |  4 +-
 include/net/cfg80211.h                             | 17 +++---
 include/net/mac80211.h                             |  5 +-
 net/mac80211/debugfs.c                             |  1 +
 net/mac80211/driver-ops.h                          |  6 +-
 net/mac80211/ieee80211_i.h                         |  4 +-
 net/mac80211/offchannel.c                          | 64 ++++++++++++++--------
 net/mac80211/trace.h                               |  9 ++-
 net/wireless/nl80211.c                             | 27 ++++-----
 net/wireless/rdev-ops.h                            |  6 +-
 net/wireless/trace.h                               | 44 +++++++--------
 29 files changed, 192 insertions(+), 121 deletions(-)

Comments

Johannes Berg May 4, 2022, 11:14 a.m. UTC | #1
On Mon, 2022-03-21 at 15:46 +0530, P Praneesh wrote:
> Replace struct ieee80211_channel with struct cfg80211_chan_def in
> remain on channel apis, because the channel width information is
> needed to handle centre frequency of 80MHz, and it is available
> in cfg80211_chan_def. Also, add SUPPORTS_EXT_REMAIN_ON_CHAN feature
> flag check before driver ops call.

Couple of more things ...

   1. Please split this into cfg80211 / mac80211, many of the changes
      are related to mac80211, but also many related to cfg80211 only
      (all the fullmac drivers), so I think it'd be nicer to have it as
      two separate patches.
      
   2. You're not exposing _any_ such capability to userspace, so when
      userspace requests a wider channel but not supported, it's
      silently ignored for all the cfg80211 drivers. OTOH, for mac80211
      drivers, you reject a wider channel. This can move up to cfg80211.
      
   3. Why limit (per the commit message, not sure about the code) to 40
      and 80 MHz, rather than allowing any bandwidth? Maybe somebody
      would want to use even smaller bandwidth (5/10)? Perhaps drivers
      should expose a bitmap of permitted bandwidths (rather than a
      single flag indicating support)?
      
   4. At least in the brcmfmac, mwifiex and wilc1000 cases, not only do
      you ignore the bandwidth, but you even pretend in
      ready_on_channel() that you used it ... that seems even worse than
      just ignoring. Also, in some of those cases you send it as higher
      BW in the ready event, but lower BW in the expired event ...
      
   5. (I don't want to know what happens in that staging driver ...)


johannes
diff mbox series

Patch

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 1f73fbf..c8662aa 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -7828,12 +7828,13 @@  static int ath10k_conf_tx(struct ieee80211_hw *hw,
 
 static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
 				    struct ieee80211_vif *vif,
-				    struct ieee80211_channel *chan,
+				    struct cfg80211_chan_def *chandef,
 				    int duration,
 				    enum ieee80211_roc_type type)
 {
 	struct ath10k *ar = hw->priv;
 	struct ath10k_vif *arvif = (void *)vif->drv_priv;
+	struct ieee80211_channel *chan = chandef->chan;
 	struct wmi_start_scan_arg arg;
 	int ret = 0;
 	u32 scan_time_msec;
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index bd11838..0505028 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -3023,12 +3023,13 @@  static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
 
 static int ath6kl_remain_on_channel(struct wiphy *wiphy,
 				    struct wireless_dev *wdev,
-				    struct ieee80211_channel *chan,
+				    struct cfg80211_chan_def *chandef,
 				    unsigned int duration,
 				    u64 *cookie)
 {
 	struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
 	struct ath6kl *ar = ath6kl_priv(vif->ndev);
+	struct ieee80211_channel *chan = chandef->chan;
 	u32 id;
 
 	/* TODO: if already pending or ongoing remain-on-channel,
@@ -3185,7 +3186,7 @@  static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 {
 	struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
 	struct ath6kl *ar = ath6kl_priv(vif->ndev);
-	struct ieee80211_channel *chan = params->chan;
+	struct ieee80211_channel *chan = params->chandef.chan;
 	const u8 *buf = params->buf;
 	size_t len = params->len;
 	unsigned int wait = params->wait;
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index bd1ef63..640d1e7 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -508,6 +508,7 @@  static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap,
 	u32 dur;
 	struct ieee80211_channel *chan;
 	struct ath6kl *ar = wmi->parent_dev;
+	struct cfg80211_chan_def chandef;
 	u32 id;
 
 	if (len < sizeof(*ev))
@@ -526,7 +527,8 @@  static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap,
 		return -EINVAL;
 	}
 	id = vif->last_roc_id;
-	cfg80211_ready_on_channel(&vif->wdev, id, chan,
+	cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
+	cfg80211_ready_on_channel(&vif->wdev, id, &chandef,
 				  dur, GFP_ATOMIC);
 
 	return 0;
@@ -541,6 +543,7 @@  static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi,
 	u32 dur;
 	struct ieee80211_channel *chan;
 	struct ath6kl *ar = wmi->parent_dev;
+	struct cfg80211_chan_def chandef;
 	u32 id;
 
 	if (len < sizeof(*ev))
@@ -559,13 +562,16 @@  static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi,
 			   freq);
 		return -EINVAL;
 	}
+
+	cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
+
 	if (vif->last_cancel_roc_id &&
 	    vif->last_cancel_roc_id + 1 == vif->last_roc_id)
 		id = vif->last_cancel_roc_id; /* event for cancel command */
 	else
 		id = vif->last_roc_id; /* timeout on uncanceled r-o-c */
 	vif->last_cancel_roc_id = 0;
-	cfg80211_remain_on_channel_expired(&vif->wdev, id, chan, GFP_ATOMIC);
+	cfg80211_remain_on_channel_expired(&vif->wdev, id, &chandef, GFP_ATOMIC);
 
 	return 0;
 }
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 98090e4..990f13b 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2468,11 +2468,13 @@  static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw,
 
 static int ath9k_remain_on_channel(struct ieee80211_hw *hw,
 				   struct ieee80211_vif *vif,
-				   struct ieee80211_channel *chan, int duration,
+				   struct cfg80211_chan_def *chandef,
+				   int duration,
 				   enum ieee80211_roc_type type)
 {
 	struct ath_softc *sc = hw->priv;
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+	struct ieee80211_channel *chan = chandef->chan;
 	int ret = 0;
 
 	mutex_lock(&sc->mutex);
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 764d1d1..45bcf45 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -1443,7 +1443,7 @@  int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 	bool tx_status;
 
 	wil_dbg_misc(wil, "mgmt_tx: channel %d offchan %d, wait %d\n",
-		     params->chan ? params->chan->hw_value : -1,
+		     params->chandef.chan ? params->chandef.chan->hw_value : -1,
 		     params->offchan,
 		     params->wait);
 
@@ -1452,7 +1452,7 @@  int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 	 * mgmt_tx or listen on a channel other than active one.
 	 */
 
-	if (params->chan && params->chan->hw_value == 0) {
+	if (params->chandef.chan && params->chandef.chan->hw_value == 0) {
 		wil_err(wil, "invalid channel\n");
 		return -EINVAL;
 	}
@@ -1464,7 +1464,7 @@  int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 		goto out;
 	}
 
-	if (!params->chan || params->chan->hw_value == vif->channel) {
+	if (!params->chandef.chan || params->chandef.chan->hw_value == vif->channel) {
 		wil_dbg_misc(wil,
 			     "send WMI_SW_TX_REQ_CMDID for on-channel\n");
 		rc = wmi_mgmt_tx(vif, buf, len);
@@ -1474,12 +1474,12 @@  int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 	if (params->offchan == 0) {
 		wil_err(wil,
 			"invalid channel params: current %d requested %d, off-channel not allowed\n",
-			vif->channel, params->chan->hw_value);
+			vif->channel, params->chandef.chan->hw_value);
 		return -EBUSY;
 	}
 
 	/* use the wmi_mgmt_tx_ext only on AP mode and off-channel */
-	rc = wmi_mgmt_tx_ext(vif, buf, len, params->chan->hw_value,
+	rc = wmi_mgmt_tx_ext(vif, buf, len, params->chandef.chan->hw_value,
 			     params->wait);
 
 out:
@@ -1736,11 +1736,12 @@  static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
 
 static int wil_remain_on_channel(struct wiphy *wiphy,
 				 struct wireless_dev *wdev,
-				 struct ieee80211_channel *chan,
+				 struct cfg80211_chan_def *chandef,
 				 unsigned int duration,
 				 u64 *cookie)
 {
 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+	struct ieee80211_channel *chan = chandef->chan;
 	int rc;
 
 	wil_dbg_misc(wil,
diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c
index f26bf04..6fd624f 100644
--- a/drivers/net/wireless/ath/wil6210/p2p.c
+++ b/drivers/net/wireless/ath/wil6210/p2p.c
@@ -128,6 +128,7 @@  int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev,
 {
 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
 	struct wil_p2p_info *p2p = &vif->p2p;
+	struct cfg80211_chan_def chandef;
 	int rc;
 
 	if (!chan)
@@ -166,7 +167,8 @@  int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev,
 	if (vif->mid == 0)
 		wil->radio_wdev = wdev;
 
-	cfg80211_ready_on_channel(wdev, *cookie, chan, duration,
+	cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
+	cfg80211_ready_on_channel(wdev, *cookie, &chandef, duration,
 				  GFP_KERNEL);
 
 out:
@@ -197,6 +199,7 @@  int wil_p2p_cancel_listen(struct wil6210_vif *vif, u64 cookie)
 {
 	struct wil6210_priv *wil = vif_to_wil(vif);
 	struct wil_p2p_info *p2p = &vif->p2p;
+	struct cfg80211_chan_def chandef;
 	u8 started;
 
 	mutex_lock(&wil->mutex);
@@ -218,9 +221,12 @@  int wil_p2p_cancel_listen(struct wil6210_vif *vif, u64 cookie)
 	}
 
 	mutex_lock(&wil->vif_mutex);
+
+	cfg80211_chandef_create(&chandef, &p2p->listen_chan, NL80211_CHAN_HT20);
+
 	cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif),
 					   p2p->cookie,
-					   &p2p->listen_chan,
+					   &chandef,
 					   GFP_KERNEL);
 	if (vif->mid == 0)
 		wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
@@ -235,6 +241,7 @@  void wil_p2p_listen_expired(struct work_struct *work)
 	struct wil6210_vif *vif = container_of(p2p,
 			struct wil6210_vif, p2p);
 	struct wil6210_priv *wil = vif_to_wil(vif);
+	struct cfg80211_chan_def chandef;
 	u8 started;
 
 	wil_dbg_misc(wil, "p2p_listen_expired\n");
@@ -247,9 +254,12 @@  void wil_p2p_listen_expired(struct work_struct *work)
 		return;
 
 	mutex_lock(&wil->vif_mutex);
+
+	cfg80211_chandef_create(&chandef, &p2p->listen_chan, NL80211_CHAN_HT20);
+
 	cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif),
 					   p2p->cookie,
-					   &p2p->listen_chan,
+					   &chandef,
 					   GFP_KERNEL);
 	if (vif->mid == 0)
 		wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
@@ -295,6 +305,7 @@  void wil_p2p_delayed_listen_work(struct work_struct *work)
 	struct wil6210_vif *vif = container_of(p2p,
 			struct wil6210_vif, p2p);
 	struct wil6210_priv *wil = vif_to_wil(vif);
+	struct cfg80211_chan_def chandef;
 	int rc;
 
 	mutex_lock(&wil->mutex);
@@ -313,17 +324,19 @@  void wil_p2p_delayed_listen_work(struct work_struct *work)
 
 	rc = wil_p2p_start_listen(vif);
 
+	cfg80211_chandef_create(&chandef, &p2p->listen_chan, NL80211_CHAN_HT20);
+
 	mutex_lock(&wil->vif_mutex);
 	if (rc) {
 		cfg80211_remain_on_channel_expired(p2p->pending_listen_wdev,
 						   p2p->cookie,
-						   &p2p->listen_chan,
+						   &chandef,
 						   GFP_KERNEL);
 		if (vif->mid == 0)
 			wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
 	} else {
 		cfg80211_ready_on_channel(p2p->pending_listen_wdev, p2p->cookie,
-					  &p2p->listen_chan,
+					  &chandef,
 					  p2p->listen_duration, GFP_KERNEL);
 		if (vif->mid == 0)
 			wil->radio_wdev = p2p->pending_listen_wdev;
@@ -339,6 +352,7 @@  void wil_p2p_stop_radio_operations(struct wil6210_priv *wil)
 {
 	struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
 	struct wil_p2p_info *p2p = &vif->p2p;
+	struct cfg80211_chan_def chandef;
 	struct cfg80211_scan_info info = {
 		.aborted = true,
 	};
@@ -367,10 +381,11 @@  void wil_p2p_stop_radio_operations(struct wil6210_priv *wil)
 		cfg80211_scan_done(vif->scan_request, &info);
 		vif->scan_request = NULL;
 	} else {
+		cfg80211_chandef_create(&chandef, &p2p->listen_chan, NL80211_CHAN_HT20);
 		/* listen */
 		cfg80211_remain_on_channel_expired(wil->radio_wdev,
 						   p2p->cookie,
-						   &p2p->listen_chan,
+						   &chandef,
 						   GFP_KERNEL);
 	}
 
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index fb72777..a852135 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -5109,7 +5109,7 @@  brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 		       struct cfg80211_mgmt_tx_params *params, u64 *cookie)
 {
 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
-	struct ieee80211_channel *chan = params->chan;
+	struct ieee80211_channel *chan = params->chandef.chan;
 	struct brcmf_pub *drvr = cfg->pub;
 	const u8 *buf = params->buf;
 	size_t len = params->len;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
index 4735063..bfa26779 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -972,10 +972,11 @@  brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration)
  * @cookie: cookie.
  */
 int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
-				struct ieee80211_channel *channel,
+				struct cfg80211_chan_def *chandef,
 				unsigned int duration, u64 *cookie)
 {
 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+	struct ieee80211_channel *channel = chandef->chan;
 	struct brcmf_p2p_info *p2p = &cfg->p2p;
 	s32 err;
 	u16 channel_nr;
@@ -993,7 +994,7 @@  int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
 
 	memcpy(&p2p->remain_on_channel, channel, sizeof(*channel));
 	*cookie = p2p->remain_on_channel_cookie;
-	cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL);
+	cfg80211_ready_on_channel(wdev, *cookie, chandef, duration, GFP_KERNEL);
 
 exit:
 	return err;
@@ -1014,6 +1015,7 @@  int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp,
 {
 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
 	struct brcmf_p2p_info *p2p = &cfg->p2p;
+	struct cfg80211_chan_def chandef;
 
 	brcmf_dbg(TRACE, "Enter\n");
 	if (test_and_clear_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN,
@@ -1026,9 +1028,11 @@  int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp,
 			complete(&p2p->wait_next_af);
 		}
 
+		cfg80211_chandef_create(&chandef, &p2p->remain_on_channel,
+					NL80211_CHAN_HT20);
 		cfg80211_remain_on_channel_expired(&ifp->vif->wdev,
 						   p2p->remain_on_channel_cookie,
-						   &p2p->remain_on_channel,
+						   &chandef,
 						   GFP_KERNEL);
 	}
 	return 0;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
index d2ecee5..1646e6e 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
@@ -156,7 +156,7 @@  int brcmf_p2p_scan_prep(struct wiphy *wiphy,
 			struct cfg80211_scan_request *request,
 			struct brcmf_cfg80211_vif *vif);
 int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
-				struct ieee80211_channel *channel,
+				struct cfg80211_chan_def *chandef,
 				unsigned int duration, u64 *cookie);
 int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp,
 				     const struct brcmf_event_msg *e,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 897e3b9..b3644dd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -3874,7 +3874,7 @@  static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
 
 static int iwl_mvm_roc(struct ieee80211_hw *hw,
 		       struct ieee80211_vif *vif,
-		       struct ieee80211_channel *channel,
+		       struct cfg80211_chan_def *chandefcfg,
 		       int duration,
 		       enum ieee80211_roc_type type)
 {
@@ -3882,6 +3882,7 @@  static int iwl_mvm_roc(struct ieee80211_hw *hw,
 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 	struct cfg80211_chan_def chandef;
 	struct iwl_mvm_phy_ctxt *phy_ctxt;
+	struct ieee80211_channel *channel = chandefcfg->chan;
 	bool band_change_removal;
 	int ret, i;
 
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 23219f3..1d0c684 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -2475,11 +2475,12 @@  static void hw_roc_done(struct work_struct *work)
 
 static int mac80211_hwsim_roc(struct ieee80211_hw *hw,
 			      struct ieee80211_vif *vif,
-			      struct ieee80211_channel *chan,
+			      struct cfg80211_chan_def *chandef,
 			      int duration,
 			      enum ieee80211_roc_type type)
 {
 	struct mac80211_hwsim_data *hwsim = hw->priv;
+	struct ieee80211_channel *chan = chandef->chan;
 
 	mutex_lock(&hwsim->mutex);
 	if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) {
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index 6f23ec3..9008c11 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -292,10 +292,11 @@  mwifiex_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
 static int
 mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy,
 				   struct wireless_dev *wdev,
-				   struct ieee80211_channel *chan,
+				   struct cfg80211_chan_def *chandef,
 				   unsigned int duration, u64 *cookie)
 {
 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
+	struct ieee80211_channel *chan = chandef->chan;
 	int ret;
 
 	if (!chan || !cookie) {
@@ -318,7 +319,7 @@  mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy,
 		priv->roc_cfg.cookie = *cookie;
 		priv->roc_cfg.chan = *chan;
 
-		cfg80211_ready_on_channel(wdev, *cookie, chan,
+		cfg80211_ready_on_channel(wdev, *cookie, chandef,
 					  duration, GFP_ATOMIC);
 
 		mwifiex_dbg(priv->adapter, INFO,
@@ -336,6 +337,7 @@  mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
 					  struct wireless_dev *wdev, u64 cookie)
 {
 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
+	struct cfg80211_chan_def chandef;
 	int ret;
 
 	if (cookie != priv->roc_cfg.cookie)
@@ -345,8 +347,10 @@  mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
 					 &priv->roc_cfg.chan, 0);
 
 	if (!ret) {
+		cfg80211_chandef_create(&chandef, &priv->roc_cfg.chan,
+					NL80211_CHAN_HT20);
 		cfg80211_remain_on_channel_expired(wdev, cookie,
-						   &priv->roc_cfg.chan,
+						   &chandef,
 						   GFP_ATOMIC);
 
 		memset(&priv->roc_cfg, 0, sizeof(struct mwifiex_roc_cfg));
diff --git a/drivers/net/wireless/marvell/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c
index 68c6326..b13883e 100644
--- a/drivers/net/wireless/marvell/mwifiex/sta_event.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c
@@ -705,6 +705,7 @@  int mwifiex_process_sta_event(struct mwifiex_private *priv)
 	u16 ctrl, reason_code;
 	u8 ibss_sta_addr[ETH_ALEN];
 	struct mwifiex_sta_node *sta_ptr;
+	struct cfg80211_chan_def chandef;
 
 	switch (eventcause) {
 	case EVENT_DUMMY_HOST_WAKEUP_SIGNAL:
@@ -1007,9 +1008,10 @@  int mwifiex_process_sta_event(struct mwifiex_private *priv)
 	case EVENT_REMAIN_ON_CHAN_EXPIRED:
 		mwifiex_dbg(adapter, EVENT,
 			    "event: Remain on channel expired\n");
+		cfg80211_chandef_create(&chandef, &priv->roc_cfg.chan, NL80211_CHAN_HT20);
 		cfg80211_remain_on_channel_expired(&priv->wdev,
 						   priv->roc_cfg.cookie,
-						   &priv->roc_cfg.chan,
+						   &chandef,
 						   GFP_ATOMIC);
 
 		memset(&priv->roc_cfg, 0x00, sizeof(struct mwifiex_roc_cfg));
diff --git a/drivers/net/wireless/marvell/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c
index 2e25d72..7d79331 100644
--- a/drivers/net/wireless/marvell/mwifiex/uap_event.c
+++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c
@@ -113,6 +113,7 @@  int mwifiex_process_uap_event(struct mwifiex_private *priv)
 	struct mwifiex_sta_node *node;
 	u8 *deauth_mac;
 	struct host_cmd_ds_11n_batimeout *ba_timeout;
+	struct cfg80211_chan_def chandef;
 	u16 ctrl;
 
 	switch (eventcause) {
@@ -322,9 +323,10 @@  int mwifiex_process_uap_event(struct mwifiex_private *priv)
 	case EVENT_REMAIN_ON_CHAN_EXPIRED:
 		mwifiex_dbg(adapter, EVENT,
 			    "event: uap: Remain on channel expired\n");
+		cfg80211_chandef_create(&chandef, &priv->roc_cfg.chan, NL80211_CHAN_HT20);
 		cfg80211_remain_on_channel_expired(&priv->wdev,
 						   priv->roc_cfg.cookie,
-						   &priv->roc_cfg.chan,
+						   &chandef,
 						   GFP_ATOMIC);
 		memset(&priv->roc_cfg, 0x00, sizeof(struct mwifiex_roc_cfg));
 		break;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
index 890d9b0..1bfdc4e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
@@ -1130,11 +1130,12 @@  mt7615_stop_sched_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 
 static int mt7615_remain_on_channel(struct ieee80211_hw *hw,
 				    struct ieee80211_vif *vif,
-				    struct ieee80211_channel *chan,
+				    struct cfg80211_chan_def *chandef,
 				    int duration,
 				    enum ieee80211_roc_type type)
 {
 	struct mt7615_phy *phy = mt7615_hw_phy(hw);
+	struct ieee80211_channel *chan = chandef->chan;
 	int err;
 
 	if (test_and_set_bit(MT76_STATE_ROC, &phy->mt76->state))
diff --git a/drivers/net/wireless/microchip/wilc1000/cfg80211.c b/drivers/net/wireless/microchip/wilc1000/cfg80211.c
index dc4bfe7..cedaf5f 100644
--- a/drivers/net/wireless/microchip/wilc1000/cfg80211.c
+++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c
@@ -1051,24 +1051,28 @@  static void wilc_wfi_remain_on_channel_expired(void *data, u64 cookie)
 	struct wilc_vif *vif = data;
 	struct wilc_priv *priv = &vif->priv;
 	struct wilc_wfi_p2p_listen_params *params = &priv->remain_on_ch_params;
+	struct cfg80211_chan_def chandef;
 
 	if (cookie != params->listen_cookie)
 		return;
 
 	priv->p2p_listen_state = false;
 
+	cfg80211_chandef_create(&chandef, params->listen_ch, NL80211_CHAN_HT20);
+
 	cfg80211_remain_on_channel_expired(&priv->wdev, params->listen_cookie,
-					   params->listen_ch, GFP_KERNEL);
+					   &chandef, GFP_KERNEL);
 }
 
 static int remain_on_channel(struct wiphy *wiphy,
 			     struct wireless_dev *wdev,
-			     struct ieee80211_channel *chan,
+			     struct cfg80211_chan_def *chandef,
 			     unsigned int duration, u64 *cookie)
 {
 	int ret = 0;
 	struct wilc_vif *vif = netdev_priv(wdev->netdev);
 	struct wilc_priv *priv = &vif->priv;
+	struct ieee80211_channel *chan = chandef->chan;
 	u64 id;
 
 	if (wdev->iftype == NL80211_IFTYPE_AP) {
@@ -1094,7 +1098,7 @@  static int remain_on_channel(struct wiphy *wiphy,
 	priv->p2p_listen_state = true;
 	priv->remain_on_ch_params.listen_duration = duration;
 
-	cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_KERNEL);
+	cfg80211_ready_on_channel(wdev, *cookie, chandef, duration, GFP_KERNEL);
 	mod_timer(&vif->hif_drv->remain_on_ch_timer,
 		  jiffies + msecs_to_jiffies(duration + 1000));
 
@@ -1119,7 +1123,7 @@  static int mgmt_tx(struct wiphy *wiphy,
 		   struct cfg80211_mgmt_tx_params *params,
 		   u64 *cookie)
 {
-	struct ieee80211_channel *chan = params->chan;
+	struct ieee80211_channel *chan = params->chandef.chan;
 	unsigned int wait = params->wait;
 	const u8 *buf = params->buf;
 	size_t len = params->len;
@@ -1205,6 +1209,7 @@  static int mgmt_tx_cancel_wait(struct wiphy *wiphy,
 	struct wilc_vif *vif = netdev_priv(wdev->netdev);
 	struct wilc_priv *priv = &vif->priv;
 	struct host_if_drv *wfi_drv = priv->hif_drv;
+	struct cfg80211_chan_def chandef;
 
 	wfi_drv->p2p_timeout = jiffies;
 
@@ -1213,9 +1218,11 @@  static int mgmt_tx_cancel_wait(struct wiphy *wiphy,
 
 		params = &priv->remain_on_ch_params;
 
+		cfg80211_chandef_create(&chandef, params->listen_ch, NL80211_CHAN_HT20);
+
 		cfg80211_remain_on_channel_expired(wdev,
 						   params->listen_cookie,
-						   params->listen_ch,
+						   &chandef,
 						   GFP_KERNEL);
 	}
 
diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
index e70c1c7f..019d07c 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
@@ -1758,11 +1758,12 @@  void rsi_roc_timeout(struct timer_list *t)
 }
 
 static int rsi_mac80211_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-			    struct ieee80211_channel *chan, int duration,
+			    struct cfg80211_chan_def *chandef, int duration,
 			    enum ieee80211_roc_type type)
 {
 	struct rsi_hw *adapter = (struct rsi_hw *)hw->priv;
 	struct rsi_common *common = (struct rsi_common *)adapter->priv;
+	struct ieee80211_channel *chan = chandef->chan;
 	int status = 0;
 
 	rsi_dbg(INFO_ZONE, "***** Remain on channel *****\n");
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 5669f17..48f2d42 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -5640,13 +5640,14 @@  static void wlcore_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 
 static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
 				       struct ieee80211_vif *vif,
-				       struct ieee80211_channel *chan,
+				       struct cfg80211_chan_def *chandef,
 				       int duration,
 				       enum ieee80211_roc_type type)
 {
 	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
 	struct wl1271 *wl = hw->priv;
 	int channel, active_roc, ret = 0;
+	struct ieee80211_channel *chan = chandef->chan;
 
 	channel = ieee80211_frequency_to_channel(chan->center_freq);
 
diff --git a/drivers/staging/rtl8723bs/include/ioctl_cfg80211.h b/drivers/staging/rtl8723bs/include/ioctl_cfg80211.h
index 993a7b3..af75a55 100644
--- a/drivers/staging/rtl8723bs/include/ioctl_cfg80211.h
+++ b/drivers/staging/rtl8723bs/include/ioctl_cfg80211.h
@@ -111,7 +111,7 @@  bool rtw_cfg80211_pwr_mgmt(struct adapter *adapter);
 #define rtw_cfg80211_rx_mgmt(adapter, freq, sig_dbm, buf, len, gfp) cfg80211_rx_mgmt((adapter)->rtw_wdev, freq, sig_dbm, buf, len, 0)
 #define rtw_cfg80211_send_rx_assoc(adapter, bss, buf, len) cfg80211_send_rx_assoc((adapter)->pnetdev, bss, buf, len)
 #define rtw_cfg80211_mgmt_tx_status(adapter, cookie, buf, len, ack, gfp) cfg80211_mgmt_tx_status((adapter)->rtw_wdev, cookie, buf, len, ack, gfp)
-#define rtw_cfg80211_ready_on_channel(adapter, cookie, chan, channel_type, duration, gfp)  cfg80211_ready_on_channel((adapter)->rtw_wdev, cookie, chan, duration, gfp)
-#define rtw_cfg80211_remain_on_channel_expired(adapter, cookie, chan, chan_type, gfp) cfg80211_remain_on_channel_expired((adapter)->rtw_wdev, cookie, chan, gfp)
+#define rtw_cfg80211_ready_on_channel(adapter, cookie, chandef, channel_type, duration, gfp)  cfg80211_ready_on_channel((adapter)->rtw_wdev, cookie, chandef, duration, gfp)
+#define rtw_cfg80211_remain_on_channel_expired(adapter, cookie, chandef, chan_type, gfp) cfg80211_remain_on_channel_expired((adapter)->rtw_wdev, cookie, chandef, gfp)
 
 #endif /* __IOCTL_CFG80211_H__ */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 423f97b..6fe8348 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3238,9 +3238,9 @@  struct cfg80211_update_ft_ies_params {
  * @dont_wait_for_ack: tells the low level not to wait for an ack
  * @n_csa_offsets: length of csa_offsets array
  * @csa_offsets: array of all the csa offsets in the frame
+ * @chandef: channel definition
  */
 struct cfg80211_mgmt_tx_params {
-	struct ieee80211_channel *chan;
 	bool offchan;
 	unsigned int wait;
 	const u8 *buf;
@@ -3249,6 +3249,7 @@  struct cfg80211_mgmt_tx_params {
 	bool dont_wait_for_ack;
 	int n_csa_offsets;
 	const u16 *csa_offsets;
+	struct cfg80211_chan_def chandef;
 };
 
 /**
@@ -4235,7 +4236,7 @@  struct cfg80211_ops {
 
 	int	(*remain_on_channel)(struct wiphy *wiphy,
 				     struct wireless_dev *wdev,
-				     struct ieee80211_channel *chan,
+				     struct cfg80211_chan_def *chandef,
 				     unsigned int duration,
 				     u64 *cookie);
 	int	(*cancel_remain_on_channel)(struct wiphy *wiphy,
@@ -7338,35 +7339,35 @@  void cfg80211_disconnected(struct net_device *dev, u16 reason,
  * cfg80211_ready_on_channel - notification of remain_on_channel start
  * @wdev: wireless device
  * @cookie: the request cookie
- * @chan: The current channel (from remain_on_channel request)
+ * @chandef: chandef for the current channel (from remain_on_channel request)
  * @duration: Duration in milliseconds that the driver intents to remain on the
  *	channel
  * @gfp: allocation flags
  */
 void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
-			       struct ieee80211_channel *chan,
+			       struct cfg80211_chan_def *chandef,
 			       unsigned int duration, gfp_t gfp);
 
 /**
  * cfg80211_remain_on_channel_expired - remain_on_channel duration expired
  * @wdev: wireless device
  * @cookie: the request cookie
- * @chan: The current channel (from remain_on_channel request)
+ * @chandef: chandef for the current channel
  * @gfp: allocation flags
  */
 void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
-					struct ieee80211_channel *chan,
+					struct cfg80211_chan_def *chandef,
 					gfp_t gfp);
 
 /**
  * cfg80211_tx_mgmt_expired - tx_mgmt duration expired
  * @wdev: wireless device
  * @cookie: the requested cookie
- * @chan: The current channel (from tx_mgmt request)
+ * @chandef: chandef for the current channel (from tx_mgmt request)
  * @gfp: allocation flags
  */
 void cfg80211_tx_mgmt_expired(struct wireless_dev *wdev, u64 cookie,
-			      struct ieee80211_channel *chan, gfp_t gfp);
+			      struct cfg80211_chan_def *chandef, gfp_t gfp);
 
 /**
  * cfg80211_sinfo_alloc_tid_stats - allocate per-tid statistics.
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index dd757f0..177679d 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -2422,6 +2422,8 @@  struct ieee80211_txq {
  *	usage and 802.11 frames with %RX_FLAG_ONLY_MONITOR set for monitor to
  *	the stack.
  *
+ * @IEEE80211_HW_SUPPORTS_EXT_REMAIN_ON_CHAN: Hardware supports extended remain
+ *	on channel for bandwidth higher than 20MHz.
  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
  */
 enum ieee80211_hw_flags {
@@ -2477,6 +2479,7 @@  enum ieee80211_hw_flags {
 	IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD,
 	IEEE80211_HW_SUPPORTS_RX_DECAP_OFFLOAD,
 	IEEE80211_HW_SUPPORTS_CONC_MON_RX_DECAP,
+	IEEE80211_HW_SUPPORTS_EXT_REMAIN_ON_CHAN,
 
 	/* keep last, obviously */
 	NUM_IEEE80211_HW_FLAGS
@@ -4118,7 +4121,7 @@  struct ieee80211_ops {
 
 	int (*remain_on_channel)(struct ieee80211_hw *hw,
 				 struct ieee80211_vif *vif,
-				 struct ieee80211_channel *chan,
+				 struct cfg80211_chan_def *chandef,
 				 int duration,
 				 enum ieee80211_roc_type type);
 	int (*cancel_remain_on_channel)(struct ieee80211_hw *hw,
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 8dbfe32..a355fe4 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -504,6 +504,7 @@  static const char *hw_flag_names[] = {
 	FLAG(SUPPORTS_TX_ENCAP_OFFLOAD),
 	FLAG(SUPPORTS_RX_DECAP_OFFLOAD),
 	FLAG(SUPPORTS_CONC_MON_RX_DECAP),
+	FLAG(SUPPORTS_EXT_REMAIN_ON_CHAN),
 #undef FLAG
 };
 
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index c336267..8babc13 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -675,7 +675,7 @@  static inline int drv_get_antenna(struct ieee80211_local *local,
 
 static inline int drv_remain_on_channel(struct ieee80211_local *local,
 					struct ieee80211_sub_if_data *sdata,
-					struct ieee80211_channel *chan,
+					struct cfg80211_chan_def *chandef,
 					unsigned int duration,
 					enum ieee80211_roc_type type)
 {
@@ -683,9 +683,9 @@  static inline int drv_remain_on_channel(struct ieee80211_local *local,
 
 	might_sleep();
 
-	trace_drv_remain_on_channel(local, sdata, chan, duration, type);
+	trace_drv_remain_on_channel(local, sdata, chandef, duration, type);
 	ret = local->ops->remain_on_channel(&local->hw, &sdata->vif,
-					    chan, duration, type);
+					    chandef, duration, type);
 	trace_drv_return_int(local, ret);
 
 	return ret;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 482c98e..115e68a 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -337,7 +337,7 @@  struct ieee80211_roc_work {
 
 	struct ieee80211_sub_if_data *sdata;
 
-	struct ieee80211_channel *chan;
+	struct cfg80211_chan_def chandef;
 
 	bool started, abort, hw_begun, notified;
 	bool on_channel;
@@ -1907,7 +1907,7 @@  void ieee80211_start_next_roc(struct ieee80211_local *local);
 void ieee80211_roc_purge(struct ieee80211_local *local,
 			 struct ieee80211_sub_if_data *sdata);
 int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
-				struct ieee80211_channel *chan,
+				struct cfg80211_chan_def *chandef,
 				unsigned int duration, u64 *cookie);
 int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
 				       struct wireless_dev *wdev, u64 cookie);
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 853c9a3..fa5c06c 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -178,12 +178,13 @@  static void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
 
 	if (!roc->mgmt_tx_cookie)
 		cfg80211_remain_on_channel_expired(&roc->sdata->wdev,
-						   roc->cookie, roc->chan,
+						   roc->cookie,
+						   &roc->chandef,
 						   GFP_KERNEL);
 	else
 		cfg80211_tx_mgmt_expired(&roc->sdata->wdev,
 					 roc->mgmt_tx_cookie,
-					 roc->chan, GFP_KERNEL);
+					 &roc->chandef, GFP_KERNEL);
 
 	list_del(&roc->list);
 	kfree(roc);
@@ -235,21 +236,24 @@  static bool ieee80211_recalc_sw_work(struct ieee80211_local *local,
 static void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc,
 					 unsigned long start_time)
 {
+	enum nl80211_band band;
+
 	if (WARN_ON(roc->notified))
 		return;
 
+	band = roc->chandef.chan->band;
 	roc->start_time = start_time;
 	roc->started = true;
 
 	if (roc->mgmt_tx_cookie) {
 		if (!WARN_ON(!roc->frame)) {
 			ieee80211_tx_skb_tid_band(roc->sdata, roc->frame, 7,
-						  roc->chan->band);
+						  band);
 			roc->frame = NULL;
 		}
 	} else {
 		cfg80211_ready_on_channel(&roc->sdata->wdev, roc->cookie,
-					  roc->chan, roc->req_duration,
+					  &roc->chandef, roc->req_duration,
 					  GFP_KERNEL);
 	}
 
@@ -311,7 +315,8 @@  static void _ieee80211_start_next_roc(struct ieee80211_local *local)
 	list_for_each_entry(tmp, &local->roc_list, list) {
 		if (tmp == roc)
 			continue;
-		if (tmp->sdata != roc->sdata || tmp->chan != roc->chan)
+		if (tmp->sdata != roc->sdata ||
+		    cfg80211_chandef_identical(&tmp->chandef, &roc->chandef))
 			break;
 		max_dur = max(tmp->duration, max_dur);
 		min_dur = min(tmp->duration, min_dur);
@@ -319,7 +324,8 @@  static void _ieee80211_start_next_roc(struct ieee80211_local *local)
 	}
 
 	if (local->ops->remain_on_channel) {
-		int ret = drv_remain_on_channel(local, roc->sdata, roc->chan,
+		int ret = drv_remain_on_channel(local, roc->sdata,
+						&roc->chandef,
 						max_dur, type);
 
 		if (ret) {
@@ -331,7 +337,8 @@  static void _ieee80211_start_next_roc(struct ieee80211_local *local)
 			 */
 			list_for_each_entry(tmp, &local->roc_list, list) {
 				if (tmp->sdata != roc->sdata ||
-				    tmp->chan != roc->chan)
+				    cfg80211_chandef_identical(&tmp->chandef,
+							       &roc->chandef))
 					break;
 				tmp->started = true;
 				tmp->abort = true;
@@ -342,7 +349,8 @@  static void _ieee80211_start_next_roc(struct ieee80211_local *local)
 
 		/* we'll notify about the start once the HW calls back */
 		list_for_each_entry(tmp, &local->roc_list, list) {
-			if (tmp->sdata != roc->sdata || tmp->chan != roc->chan)
+			if (tmp->sdata != roc->sdata ||
+			    cfg80211_chandef_identical(&tmp->chandef, &roc->chandef))
 				break;
 			tmp->started = true;
 		}
@@ -352,7 +360,7 @@  static void _ieee80211_start_next_roc(struct ieee80211_local *local)
 		 * treat it as though the ROC operation started properly, so
 		 * other ROC operations won't interfere with this one.
 		 */
-		roc->on_channel = roc->chan == local->_oper_chandef.chan &&
+		roc->on_channel = roc->chandef.chan == local->_oper_chandef.chan &&
 				  local->_oper_chandef.width != NL80211_CHAN_WIDTH_5 &&
 				  local->_oper_chandef.width != NL80211_CHAN_WIDTH_10;
 
@@ -362,7 +370,7 @@  static void _ieee80211_start_next_roc(struct ieee80211_local *local)
 		if (!roc->on_channel) {
 			ieee80211_offchannel_stop_vifs(local);
 
-			local->tmp_channel = roc->chan;
+			local->tmp_channel = roc->chandef.chan;
 			ieee80211_hw_config(local, 0);
 		}
 
@@ -371,7 +379,8 @@  static void _ieee80211_start_next_roc(struct ieee80211_local *local)
 
 		/* tell userspace or send frame(s) */
 		list_for_each_entry(tmp, &local->roc_list, list) {
-			if (tmp->sdata != roc->sdata || tmp->chan != roc->chan)
+			if (tmp->sdata != roc->sdata ||
+			    cfg80211_chandef_identical(&tmp->chandef, &roc->chandef))
 				break;
 
 			tmp->on_channel = roc->on_channel;
@@ -526,12 +535,13 @@  ieee80211_coalesce_hw_started_roc(struct ieee80211_local *local,
 
 static int ieee80211_start_roc_work(struct ieee80211_local *local,
 				    struct ieee80211_sub_if_data *sdata,
-				    struct ieee80211_channel *channel,
+				    struct cfg80211_chan_def *chandef,
 				    unsigned int duration, u64 *cookie,
 				    struct sk_buff *txskb,
 				    enum ieee80211_roc_type type)
 {
 	struct ieee80211_roc_work *roc, *tmp;
+	struct ieee80211_channel *channel = chandef->chan;
 	bool queued = false, combine_started = true;
 	int ret;
 
@@ -560,7 +570,7 @@  static int ieee80211_start_roc_work(struct ieee80211_local *local,
 	if (!duration)
 		duration = 10;
 
-	roc->chan = channel;
+	roc->chandef = *chandef;
 	roc->duration = duration;
 	roc->req_duration = duration;
 	roc->frame = txskb;
@@ -590,7 +600,7 @@  static int ieee80211_start_roc_work(struct ieee80211_local *local,
 			/* otherwise actually kick it off here
 			 * (for error handling)
 			 */
-			ret = drv_remain_on_channel(local, sdata, channel,
+			ret = drv_remain_on_channel(local, sdata, chandef,
 						    duration, type);
 			if (ret) {
 				kfree(roc);
@@ -606,7 +616,7 @@  static int ieee80211_start_roc_work(struct ieee80211_local *local,
 	/* otherwise handle queueing */
 
 	list_for_each_entry(tmp, &local->roc_list, list) {
-		if (tmp->chan != channel || tmp->sdata != sdata)
+		if (tmp->chandef.chan != channel || tmp->sdata != sdata)
 			continue;
 
 		/*
@@ -668,7 +678,7 @@  static int ieee80211_start_roc_work(struct ieee80211_local *local,
 }
 
 int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
-				struct ieee80211_channel *chan,
+				struct cfg80211_chan_def *chandef,
 				unsigned int duration, u64 *cookie)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
@@ -676,9 +686,13 @@  int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
 	int ret;
 
 	mutex_lock(&local->mtx);
-	ret = ieee80211_start_roc_work(local, sdata, chan,
-				       duration, cookie, NULL,
-				       IEEE80211_ROC_TYPE_NORMAL);
+
+	if (!ieee80211_hw_check(&local->hw, SUPPORTS_EXT_REMAIN_ON_CHAN))
+		goto err;
+
+	ret = ieee80211_start_roc_work(local, sdata, chandef, duration,
+				       cookie, NULL, IEEE80211_ROC_TYPE_NORMAL);
+err:
 	mutex_unlock(&local->mtx);
 
 	return ret;
@@ -766,6 +780,7 @@  int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
 	struct ieee80211_local *local = sdata->local;
+	struct cfg80211_chan_def chandef;
 	struct sk_buff *skb;
 	struct sta_info *sta;
 	const struct ieee80211_mgmt *mgmt = (void *)params->buf;
@@ -835,7 +850,7 @@  int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 	/* configurations requiring offchan cannot work if no channel has been
 	 * specified
 	 */
-	if (need_offchan && !params->chan)
+	if (need_offchan && !params->chandef.chan)
 		return -EINVAL;
 
 	mutex_lock(&local->mtx);
@@ -848,10 +863,10 @@  int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 
 		if (chanctx_conf) {
-			need_offchan = params->chan &&
-				       (params->chan !=
+			need_offchan = params->chandef.chan &&
+				       (params->chandef.chan !=
 					chanctx_conf->def.chan);
-		} else if (!params->chan) {
+		} else if (!params->chandef.chan) {
 			ret = -EINVAL;
 			rcu_read_unlock();
 			goto out_unlock;
@@ -935,8 +950,9 @@  int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 		IEEE80211_SKB_CB(skb)->hw_queue =
 			local->hw.offchannel_tx_hw_queue;
 
+	chandef = params->chandef;
 	/* This will handle all kinds of coalescing and immediate TX */
-	ret = ieee80211_start_roc_work(local, sdata, params->chan,
+	ret = ieee80211_start_roc_work(local, sdata, &chandef,
 				       params->wait, cookie, skb,
 				       IEEE80211_ROC_TYPE_MGMT_TX);
 	if (ret)
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 9e8381b..720e56c 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -1226,17 +1226,17 @@  TRACE_EVENT(drv_get_antenna,
 TRACE_EVENT(drv_remain_on_channel,
 	TP_PROTO(struct ieee80211_local *local,
 		 struct ieee80211_sub_if_data *sdata,
-		 struct ieee80211_channel *chan,
+		 struct cfg80211_chan_def *chandef,
 		 unsigned int duration,
 		 enum ieee80211_roc_type type),
 
-	TP_ARGS(local, sdata, chan, duration, type),
+	TP_ARGS(local, sdata, chandef, duration, type),
 
 	TP_STRUCT__entry(
 		LOCAL_ENTRY
 		VIF_ENTRY
+		CHANDEF_ENTRY
 		__field(int, center_freq)
-		__field(int, freq_offset)
 		__field(unsigned int, duration)
 		__field(u32, type)
 	),
@@ -1244,8 +1244,7 @@  TRACE_EVENT(drv_remain_on_channel,
 	TP_fast_assign(
 		LOCAL_ASSIGN;
 		VIF_ASSIGN;
-		__entry->center_freq = chan->center_freq;
-		__entry->freq_offset = chan->freq_offset;
+		CHANDEF_ASSIGN(chandef);
 		__entry->duration = duration;
 		__entry->type = type;
 	),
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index a27b3b5..ee8e7f2 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -11486,7 +11486,7 @@  static int nl80211_remain_on_channel(struct sk_buff *skb,
 		goto free_msg;
 	}
 
-	err = rdev_remain_on_channel(rdev, wdev, chandef.chan,
+	err = rdev_remain_on_channel(rdev, wdev, &chandef,
 				     duration, &cookie);
 
 	if (err)
@@ -11704,7 +11704,7 @@  static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 		}
 	}
 
-	params.chan = chandef.chan;
+	params.chandef = chandef;
 	err = cfg80211_mlme_mgmt_tx(rdev, wdev, &params, &cookie);
 	if (err)
 		goto free_msg;
@@ -16993,10 +16993,11 @@  void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
 static void nl80211_send_remain_on_chan_event(
 	int cmd, struct cfg80211_registered_device *rdev,
 	struct wireless_dev *wdev, u64 cookie,
-	struct ieee80211_channel *chan,
+	struct cfg80211_chan_def *chandef,
 	unsigned int duration, gfp_t gfp)
 {
 	struct sk_buff *msg;
+	struct ieee80211_channel *chan = chandef->chan;
 	void *hdr;
 
 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
@@ -17036,42 +17037,42 @@  static void nl80211_send_remain_on_chan_event(
 }
 
 void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
-			       struct ieee80211_channel *chan,
+			       struct cfg80211_chan_def *chandef,
 			       unsigned int duration, gfp_t gfp)
 {
 	struct wiphy *wiphy = wdev->wiphy;
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
-	trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
+	trace_cfg80211_ready_on_channel(wdev, cookie, chandef, duration);
 	nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
-					  rdev, wdev, cookie, chan,
+					  rdev, wdev, cookie, chandef,
 					  duration, gfp);
 }
 EXPORT_SYMBOL(cfg80211_ready_on_channel);
 
 void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
-					struct ieee80211_channel *chan,
+					struct cfg80211_chan_def *chandef,
 					gfp_t gfp)
 {
 	struct wiphy *wiphy = wdev->wiphy;
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
-	trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
+	trace_cfg80211_ready_on_channel_expired(wdev, cookie, chandef);
 	nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
-					  rdev, wdev, cookie, chan, 0, gfp);
+					  rdev, wdev, cookie, chandef, 0, gfp);
 }
 EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
 
 void cfg80211_tx_mgmt_expired(struct wireless_dev *wdev, u64 cookie,
-					struct ieee80211_channel *chan,
-					gfp_t gfp)
+			      struct cfg80211_chan_def *chandef,
+			      gfp_t gfp)
 {
 	struct wiphy *wiphy = wdev->wiphy;
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
-	trace_cfg80211_tx_mgmt_expired(wdev, cookie, chan);
+	trace_cfg80211_tx_mgmt_expired(wdev, cookie, chandef);
 	nl80211_send_remain_on_chan_event(NL80211_CMD_FRAME_WAIT_CANCEL,
-					  rdev, wdev, cookie, chan, 0, gfp);
+					  rdev, wdev, cookie, chandef, 0, gfp);
 }
 EXPORT_SYMBOL(cfg80211_tx_mgmt_expired);
 
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index cc1efec..e0fd09b 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -710,12 +710,12 @@  static inline int rdev_flush_pmksa(struct cfg80211_registered_device *rdev,
 static inline int
 rdev_remain_on_channel(struct cfg80211_registered_device *rdev,
 		       struct wireless_dev *wdev,
-		       struct ieee80211_channel *chan,
+		       struct cfg80211_chan_def *chandef,
 		       unsigned int duration, u64 *cookie)
 {
 	int ret;
-	trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, duration);
-	ret = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan,
+	trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chandef, duration);
+	ret = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chandef,
 					   duration, cookie);
 	trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
 	return ret;
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index ad6c16a..c08399e 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1913,9 +1913,9 @@  DEFINE_EVENT(rdev_pmksa, rdev_del_pmksa,
 
 TRACE_EVENT(rdev_remain_on_channel,
 	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
-		 struct ieee80211_channel *chan,
+		 struct cfg80211_chan_def *chandef,
 		 unsigned int duration),
-	TP_ARGS(wiphy, wdev, chan, duration),
+	TP_ARGS(wiphy, wdev, chandef, duration),
 	TP_STRUCT__entry(
 		WIPHY_ENTRY
 		WDEV_ENTRY
@@ -1925,7 +1925,7 @@  TRACE_EVENT(rdev_remain_on_channel,
 	TP_fast_assign(
 		WIPHY_ASSIGN;
 		WDEV_ASSIGN;
-		CHAN_ASSIGN(chan);
+		CHAN_ASSIGN(chandef->chan);
 		__entry->duration = duration;
 	),
 	TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", duration: %u",
@@ -1982,7 +1982,7 @@  TRACE_EVENT(rdev_mgmt_tx,
 	TP_fast_assign(
 		WIPHY_ASSIGN;
 		WDEV_ASSIGN;
-		CHAN_ASSIGN(params->chan);
+		CHAN_ASSIGN(params->chandef.chan);
 		__entry->offchan = params->offchan;
 		__entry->wait = params->wait;
 		__entry->no_cck = params->no_cck;
@@ -2812,60 +2812,60 @@  TRACE_EVENT(cfg80211_michael_mic_failure,
 
 TRACE_EVENT(cfg80211_ready_on_channel,
 	TP_PROTO(struct wireless_dev *wdev, u64 cookie,
-		 struct ieee80211_channel *chan,
+		 struct cfg80211_chan_def *chandef,
 		 unsigned int duration),
-	TP_ARGS(wdev, cookie, chan, duration),
+	TP_ARGS(wdev, cookie, chandef, duration),
 	TP_STRUCT__entry(
 		WDEV_ENTRY
 		__field(u64, cookie)
-		CHAN_ENTRY
+		CHAN_DEF_ENTRY
 		__field(unsigned int, duration)
 	),
 	TP_fast_assign(
 		WDEV_ASSIGN;
 		__entry->cookie = cookie;
-		CHAN_ASSIGN(chan);
+		CHAN_DEF_ASSIGN(chandef);
 		__entry->duration = duration;
 	),
-	TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", duration: %u",
-		  WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG,
+	TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_DEF_PR_FMT ", duration: %u",
+		  WDEV_PR_ARG, __entry->cookie, CHAN_DEF_PR_ARG,
 		  __entry->duration)
 );
 
 TRACE_EVENT(cfg80211_ready_on_channel_expired,
 	TP_PROTO(struct wireless_dev *wdev, u64 cookie,
-		 struct ieee80211_channel *chan),
-	TP_ARGS(wdev, cookie, chan),
+		 struct cfg80211_chan_def *chandef),
+	TP_ARGS(wdev, cookie, chandef),
 	TP_STRUCT__entry(
 		WDEV_ENTRY
 		__field(u64, cookie)
-		CHAN_ENTRY
+		CHAN_DEF_ENTRY
 	),
 	TP_fast_assign(
 		WDEV_ASSIGN;
 		__entry->cookie = cookie;
-		CHAN_ASSIGN(chan);
+		CHAN_DEF_ASSIGN(chandef);
 	),
-	TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT,
-		  WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG)
+	TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_DEF_PR_FMT,
+		  WDEV_PR_ARG, __entry->cookie, CHAN_DEF_PR_ARG)
 );
 
 TRACE_EVENT(cfg80211_tx_mgmt_expired,
 	TP_PROTO(struct wireless_dev *wdev, u64 cookie,
-		 struct ieee80211_channel *chan),
-	TP_ARGS(wdev, cookie, chan),
+		 struct cfg80211_chan_def *chandef),
+	TP_ARGS(wdev, cookie, chandef),
 	TP_STRUCT__entry(
 		WDEV_ENTRY
 		__field(u64, cookie)
-		CHAN_ENTRY
+		CHAN_DEF_ENTRY
 	),
 	TP_fast_assign(
 		WDEV_ASSIGN;
 		__entry->cookie = cookie;
-		CHAN_ASSIGN(chan);
+		CHAN_DEF_ASSIGN(chandef);
 	),
-	TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT,
-		  WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG)
+	TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_DEF_PR_FMT,
+		  WDEV_PR_ARG, __entry->cookie, CHAN_DEF_PR_ARG)
 );
 
 TRACE_EVENT(cfg80211_new_sta,