@@ -337,6 +337,9 @@ mt7921_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
if (phy->chip_cap & MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN)
vif->driver_flags |= IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+
+ INIT_WORK(&mvif->csa_work, mt7921_csa_work);
+ timer_setup(&mvif->csa_timer, mt792x_csa_timer, 0);
out:
mt792x_mutex_release(dev);
@@ -1337,6 +1340,9 @@ static int
mt7921_add_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+ dev->new_ctx = ctx;
return 0;
}
@@ -1344,6 +1350,10 @@ static void
mt7921_remove_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+ if (dev->new_ctx == ctx)
+ dev->new_ctx = NULL;
}
static void mt7921_ctx_iter(void *priv, u8 *mac,
@@ -1399,6 +1409,89 @@ static void mt7921_mgd_complete_tx(struct ieee80211_hw *hw,
mt7921_abort_roc(mvif->phy, mvif);
}
+static int mt7921_switch_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs,
+ int n_vifs,
+ enum ieee80211_chanctx_switch_mode mode)
+{
+ return mt792x_assign_vif_chanctx(hw, vifs->vif, vifs->link_conf,
+ vifs->new_ctx);
+}
+
+void mt7921_csa_work(struct work_struct *work)
+{
+ struct mt792x_vif *mvif;
+ struct mt792x_dev *dev;
+ struct ieee80211_vif *vif;
+ int ret;
+
+ mvif = (struct mt792x_vif *)container_of(work, struct mt792x_vif,
+ csa_work);
+ dev = mvif->phy->dev;
+ vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv);
+
+ mt792x_mutex_acquire(dev);
+ ret = mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->mt76,
+ dev->new_ctx);
+ mt792x_mutex_release(dev);
+
+ ieee80211_chswitch_done(vif, !ret, 0);
+}
+
+static int mt7921_pre_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc)
+ return -EOPNOTSUPP;
+
+ /* Avoid beacon loss due to the CAC(Channel Availability Check) time
+ * of the AP.
+ */
+ if (!cfg80211_chandef_usable(hw->wiphy, &chsw->chandef,
+ IEEE80211_CHAN_RADAR))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static void mt7921_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+ u16 beacon_interval = vif->bss_conf.beacon_int;
+
+ mvif->csa_timer.expires = TU_TO_EXP_TIME(beacon_interval * chsw->count);
+ add_timer(&mvif->csa_timer);
+}
+
+static void mt7921_abort_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf)
+{
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+
+ del_timer_sync(&mvif->csa_timer);
+ cancel_work_sync(&mvif->csa_work);
+}
+
+static void mt7921_channel_switch_rx_beacon(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+ u16 beacon_interval = vif->bss_conf.beacon_int;
+
+ if (cfg80211_chandef_identical(&chsw->chandef,
+ &dev->new_ctx->def) &&
+ chsw->count) {
+ mod_timer(&mvif->csa_timer,
+ TU_TO_EXP_TIME(beacon_interval * chsw->count));
+ }
+}
+
const struct ieee80211_ops mt7921_ops = {
.tx = mt792x_tx,
.start = mt7921_start,
@@ -1458,6 +1551,11 @@ const struct ieee80211_ops mt7921_ops = {
.unassign_vif_chanctx = mt792x_unassign_vif_chanctx,
.mgd_prepare_tx = mt7921_mgd_prepare_tx,
.mgd_complete_tx = mt7921_mgd_complete_tx,
+ .switch_vif_chanctx = mt7921_switch_vif_chanctx,
+ .pre_channel_switch = mt7921_pre_channel_switch,
+ .channel_switch = mt7921_channel_switch,
+ .abort_channel_switch = mt7921_abort_channel_switch,
+ .channel_switch_rx_beacon = mt7921_channel_switch_rx_beacon,
};
EXPORT_SYMBOL_GPL(mt7921_ops);
@@ -272,6 +272,7 @@ int mt7921_mcu_uni_rx_ba(struct mt792x_dev *dev,
bool enable);
void mt7921_scan_work(struct work_struct *work);
void mt7921_roc_work(struct work_struct *work);
+void mt7921_csa_work(struct work_struct *work);
int mt7921_mcu_uni_bss_ps(struct mt792x_dev *dev, struct ieee80211_vif *vif);
void mt7921_coredump_work(struct work_struct *work);
int mt7921_get_txpwr_info(struct mt792x_dev *dev, struct mt7921_txpwr *txpwr);
@@ -109,6 +109,9 @@ struct mt792x_vif {
struct ewma_rssi rssi;
struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+
+ struct work_struct csa_work;
+ struct timer_list csa_timer;
};
struct mt792x_phy {
@@ -209,6 +212,8 @@ struct mt792x_dev {
enum environment_cap country_ie_env;
u32 backup_l1;
u32 backup_l2;
+
+ struct ieee80211_chanctx_conf *new_ctx;
};
static inline struct mt792x_dev *
@@ -278,6 +283,7 @@ void mt792x_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u64 timestamp);
void mt792x_tx_worker(struct mt76_worker *w);
void mt792x_roc_timer(struct timer_list *timer);
+void mt792x_csa_timer(struct timer_list *timer);
void mt792x_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u32 queues, bool drop);
int mt792x_assign_vif_chanctx(struct ieee80211_hw *hw,
@@ -246,6 +246,14 @@ void mt792x_roc_timer(struct timer_list *timer)
}
EXPORT_SYMBOL_GPL(mt792x_roc_timer);
+void mt792x_csa_timer(struct timer_list *timer)
+{
+ struct mt792x_vif *mvif = from_timer(mvif, timer, csa_timer);
+
+ ieee80211_queue_work(mvif->phy->mt76->hw, &mvif->csa_work);
+}
+EXPORT_SYMBOL_GPL(mt792x_csa_timer);
+
void mt792x_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u32 queues, bool drop)
{
@@ -283,6 +291,11 @@ void mt792x_unassign_vif_chanctx(struct ieee80211_hw *hw,
mutex_lock(&dev->mt76.mutex);
mvif->mt76.ctx = NULL;
mutex_unlock(&dev->mt76.mutex);
+
+ if (vif->bss_conf.csa_active) {
+ del_timer_sync(&mvif->csa_timer);
+ cancel_work_sync(&mvif->csa_work);
+ }
}
EXPORT_SYMBOL_GPL(mt792x_unassign_vif_chanctx);
@@ -603,6 +616,7 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw)
ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
ieee80211_hw_set(hw, CONNECTION_MONITOR);
+ ieee80211_hw_set(hw, CHANCTX_STA_CSA);
if (dev->pm.enable)
ieee80211_hw_set(hw, CONNECTION_MONITOR);