wifi: rtl8xxxu: Add beacon functions
authorMartin Kaistra <martin.kaistra@linutronix.de>
Fri, 28 Apr 2023 15:08:18 +0000 (17:08 +0200)
committerKalle Valo <kvalo@kernel.org>
Fri, 5 May 2023 07:30:11 +0000 (10:30 +0300)
Add a workqueue to update the beacon contents asynchronously and
implement downloading the beacon to the HW and starting beacon tx like
the vendor driver.

Signed-off-by: Martin Kaistra <martin.kaistra@linutronix.de>
Reviewed-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20230428150833.218605-4-martin.kaistra@linutronix.de
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h

index 9cd6d171e9933caa37f78bc69b0b7034ea05a5c6..971f1cc38d323d60d4cc302afd1362079b986516 100644 (file)
@@ -1851,6 +1851,7 @@ struct rtl8xxxu_priv {
        struct delayed_work ra_watchdog;
        struct work_struct c2hcmd_work;
        struct sk_buff_head c2hcmd_queue;
+       struct work_struct update_beacon_work;
        struct rtl8xxxu_btcoex bt_coex;
        struct rtl8xxxu_ra_report ra_report;
        struct rtl8xxxu_cfo_tracking cfo_tracking;
index 9dc6f3ec7a305876cd00c336ac123fa361058219..a152e5c9ea6910628cfa8931038e16cf70ae8498 100644 (file)
@@ -1185,6 +1185,20 @@ static void rtl8xxxu_stop_tx_beacon(struct rtl8xxxu_priv *priv)
        rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 2, val8);
 }
 
+static void rtl8xxxu_start_tx_beacon(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+
+       val8 = rtl8xxxu_read8(priv, REG_FWHW_TXQ_CTRL + 2);
+       val8 |= EN_BCNQ_DL >> 16;
+       rtl8xxxu_write8(priv, REG_FWHW_TXQ_CTRL + 2, val8);
+
+       rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 1, 0x80);
+       val8 = rtl8xxxu_read8(priv, REG_TBTT_PROHIBIT + 2);
+       val8 &= 0xF0;
+       rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 2, val8);
+}
+
 
 /*
  * The rtl8723a has 3 channel groups for it's efuse settings. It only
@@ -4964,6 +4978,17 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                dev_dbg(dev, "Changed BASIC_RATES!\n");
                rtl8xxxu_set_basic_rates(priv, bss_conf->basic_rates);
        }
+
+       if (changed & BSS_CHANGED_BEACON_ENABLED) {
+               if (bss_conf->enable_beacon)
+                       rtl8xxxu_start_tx_beacon(priv);
+               else
+                       rtl8xxxu_stop_tx_beacon(priv);
+       }
+
+       if (changed & BSS_CHANGED_BEACON)
+               schedule_work(&priv->update_beacon_work);
+
 error:
        return;
 }
@@ -5545,6 +5570,55 @@ error:
        dev_kfree_skb(skb);
 }
 
+static void rtl8xxxu_send_beacon_frame(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       struct sk_buff *skb = ieee80211_beacon_get(hw, vif, 0);
+       struct device *dev = &priv->udev->dev;
+       int retry;
+       u8 val8;
+
+       /* BCN_VALID, write 1 to clear, cleared by SW */
+       val8 = rtl8xxxu_read8(priv, REG_TDECTRL + 2);
+       val8 |= BIT_BCN_VALID >> 16;
+       rtl8xxxu_write8(priv, REG_TDECTRL + 2, val8);
+
+       /* SW_BCN_SEL - Port0 */
+       val8 = rtl8xxxu_read8(priv, REG_DWBCN1_CTRL_8723B + 2);
+       val8 &= ~(BIT_SW_BCN_SEL >> 16);
+       rtl8xxxu_write8(priv, REG_DWBCN1_CTRL_8723B + 2, val8);
+
+       if (skb)
+               rtl8xxxu_tx(hw, NULL, skb);
+
+       retry = 100;
+       do {
+               val8 = rtl8xxxu_read8(priv, REG_TDECTRL + 2);
+               if (val8 & (BIT_BCN_VALID >> 16))
+                       break;
+               usleep_range(10, 20);
+       } while (--retry);
+
+       if (!retry)
+               dev_err(dev, "%s: Failed to read beacon valid bit\n", __func__);
+}
+
+static void rtl8xxxu_update_beacon_work_callback(struct work_struct *work)
+{
+       struct rtl8xxxu_priv *priv =
+               container_of(work, struct rtl8xxxu_priv, update_beacon_work);
+       struct ieee80211_hw *hw = priv->hw;
+       struct ieee80211_vif *vif = priv->vif;
+
+       if (!vif) {
+               WARN_ONCE(true, "no vif to update beacon\n");
+               return;
+       }
+
+       rtl8xxxu_send_beacon_frame(hw, vif);
+}
+
 void rtl8723au_rx_parse_phystats(struct rtl8xxxu_priv *priv,
                                 struct ieee80211_rx_status *rx_status,
                                 struct rtl8723au_phy_stats *phy_stats,
@@ -7311,6 +7385,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface,
        spin_lock_init(&priv->rx_urb_lock);
        INIT_WORK(&priv->rx_urb_wq, rtl8xxxu_rx_urb_work);
        INIT_DELAYED_WORK(&priv->ra_watchdog, rtl8xxxu_watchdog_callback);
+       INIT_WORK(&priv->update_beacon_work, rtl8xxxu_update_beacon_work_callback);
        skb_queue_head_init(&priv->c2hcmd_queue);
 
        usb_set_intfdata(interface, hw);
index 4dffbab494c3b8dc8c72a85429b4c4c4fd04ff42..ad285e4ac0ec497663498e8f5dab910108f47908 100644 (file)
 
 #define REG_FIFOPAGE                   0x0204
 #define REG_TDECTRL                    0x0208
+#define  BIT_BCN_VALID                 BIT(16)
 
 #define REG_DWBCN0_CTRL_8188F          REG_TDECTRL
 
 #define  AUTO_LLT_INIT_LLT             BIT(16)
 
 #define REG_DWBCN1_CTRL_8723B          0x0228
+#define  BIT_SW_BCN_SEL                        BIT(20)
 
 /* 0x0280 ~ 0x02FF     RXDMA Configuration */
 #define REG_RXDMA_AGG_PG_TH            0x0280  /* 0-7 : USB DMA size bits
 #define REG_FWHW_TXQ_CTRL              0x0420
 #define  FWHW_TXQ_CTRL_AMPDU_RETRY     BIT(7)
 #define  FWHW_TXQ_CTRL_XMIT_MGMT_ACK   BIT(12)
+#define  EN_BCNQ_DL                    BIT(22)
 
 #define REG_HWSEQ_CTRL                 0x0423
 #define REG_TXPKTBUF_BCNQ_BDNY         0x0424