diff mbox series

[RFC,03/14] wifi: rtl8xxxu: Add beacon functions

Message ID 20230322171905.492855-4-martin.kaistra@linutronix.de
State New
Headers show
Series wifi: rtl8xxxu: Add AP mode support for 8188f | expand

Commit Message

Martin Kaistra March 22, 2023, 5:18 p.m. UTC
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>
---
 .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h  |  3 +
 .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 81 +++++++++++++++++++
 2 files changed, 84 insertions(+)

Comments

Ping-Ke Shih March 27, 2023, 1:19 a.m. UTC | #1
> -----Original Message-----
> From: Martin Kaistra <martin.kaistra@linutronix.de>
> Sent: Thursday, March 23, 2023 1:19 AM
> To: linux-wireless@vger.kernel.org
> Cc: Jes Sorensen <Jes.Sorensen@gmail.com>; Kalle Valo <kvalo@kernel.org>; Ping-Ke Shih
> <pkshih@realtek.com>; Bitterblue Smith <rtl8821cerfe2@gmail.com>; Sebastian Andrzej Siewior
> <bigeasy@linutronix.de>
> Subject: [RFC PATCH 03/14] wifi: rtl8xxxu: Add beacon functions
> 
> 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>
> ---
>  .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h  |  3 +
>  .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 81 +++++++++++++++++++
>  2 files changed, 84 insertions(+)
> 
> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> index 9d48c69ffece1..cac985271628c 100644
> --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> @@ -1744,6 +1744,8 @@ struct rtl8xxxu_priv {
>         bool shutdown;
>         struct work_struct rx_urb_wq;
> 
> +       bool beacon_enabled;
> +
>         u8 mac_addr[ETH_ALEN];
>         char chip_name[8];
>         char chip_vendor[8];
> @@ -1850,6 +1852,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;
> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
> b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
> index daeaa7d6864f9..404fa6e322f58 100644
> --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
> +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
> @@ -1104,6 +1104,24 @@ static void rtl8xxxu_stop_tx_beacon(struct rtl8xxxu_priv *priv)
>         val8 = rtl8xxxu_read8(priv, REG_TBTT_PROHIBIT + 2);
>         val8 &= ~BIT(0);
>         rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 2, val8);
> +
> +       priv->beacon_enabled = false;
> +}
> +
> +static void rtl8xxxu_start_tx_beacon(struct rtl8xxxu_priv *priv)
> +{
> +       u8 val8;
> +
> +       val8 = rtl8xxxu_read8(priv, REG_FWHW_TXQ_CTRL + 2);
> +       val8 |= BIT(6);

#define EN_BCNQ_DL BIT(22)

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);
> +
> +       priv->beacon_enabled = true;
>  }
> 
> 
> @@ -4895,6 +4913,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 ||
> +           (changed & BSS_CHANGED_BEACON_ENABLED &&
> +            bss_conf->enable_beacon)) {
> +               if (!priv->beacon_enabled) {
> +                       dev_dbg(dev, "BSS_CHANGED_BEACON_ENABLED\n");
> +                       rtl8xxxu_start_tx_beacon(priv);
> +                       schedule_work(&priv->update_beacon_work);
> +               }
> +       }
> +
>  error:
>         return;
>  }
> @@ -5476,6 +5505,57 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
>         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, BIT16 of REG_TDECTRL = BIT0 of REG_TDECTRL+2,
> +        * write 1 to clear, cleared by SW.
> +        */
> +       val8 = rtl8xxxu_read8(priv, REG_TDECTRL + 2);
> +       val8 |= BIT(0);

#define BIT_BCN_VALID BIT(16)
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(4);

#define BIT_SW_BCN_SEL BIT(20)
val8 &= ~(BIT_SW_BCN_SEL >> 20);

> +       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(0))

#define BIT_BCN_VALID BIT(16)

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,
> @@ -7244,6 +7324,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);
> --
> 2.30.2
Bitterblue Smith March 27, 2023, 1:10 p.m. UTC | #2
On 22/03/2023 19:18, Martin Kaistra wrote:
> 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>
> ---
>  .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h  |  3 +
>  .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 81 +++++++++++++++++++
>  2 files changed, 84 insertions(+)
> 
> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> index 9d48c69ffece1..cac985271628c 100644
> --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> @@ -1744,6 +1744,8 @@ struct rtl8xxxu_priv {
>  	bool shutdown;
>  	struct work_struct rx_urb_wq;
>  
> +	bool beacon_enabled;
> +
>  	u8 mac_addr[ETH_ALEN];
>  	char chip_name[8];
>  	char chip_vendor[8];
> @@ -1850,6 +1852,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;
> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
> index daeaa7d6864f9..404fa6e322f58 100644
> --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
> +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
> @@ -1104,6 +1104,24 @@ static void rtl8xxxu_stop_tx_beacon(struct rtl8xxxu_priv *priv)
>  	val8 = rtl8xxxu_read8(priv, REG_TBTT_PROHIBIT + 2);
>  	val8 &= ~BIT(0);
>  	rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 2, val8);
> +
> +	priv->beacon_enabled = false;
> +}
> +
> +static void rtl8xxxu_start_tx_beacon(struct rtl8xxxu_priv *priv)
> +{
> +	u8 val8;
> +
> +	val8 = rtl8xxxu_read8(priv, REG_FWHW_TXQ_CTRL + 2);
> +	val8 |= BIT(6);
> +	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);
> +
> +	priv->beacon_enabled = true;
>  }
>  
>  
> @@ -4895,6 +4913,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 ||
> +	    (changed & BSS_CHANGED_BEACON_ENABLED &&
> +	     bss_conf->enable_beacon)) {
> +		if (!priv->beacon_enabled) {
> +			dev_dbg(dev, "BSS_CHANGED_BEACON_ENABLED\n");
> +			rtl8xxxu_start_tx_beacon(priv);
> +			schedule_work(&priv->update_beacon_work);
> +		}

Is it not necessary to stop transmitting the beacons when
bss_conf->enable_beacon is false?

> +	}
> +
>  error:
>  	return;
>  }
> @@ -5476,6 +5505,57 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
>  	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, BIT16 of REG_TDECTRL = BIT0 of REG_TDECTRL+2,
> +	 * write 1 to clear, cleared by SW.
> +	 */
> +	val8 = rtl8xxxu_read8(priv, REG_TDECTRL + 2);
> +	val8 |= BIT(0);
> +	rtl8xxxu_write8(priv, REG_TDECTRL + 2, val8);
> +
> +	/* SW_BCN_SEL - Port0 */
> +	val8 = rtl8xxxu_read8(priv, REG_DWBCN1_CTRL_8723B + 2);
> +	val8 &= ~BIT(4);
> +	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(0))
> +			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,
> @@ -7244,6 +7324,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);
diff mbox series

Patch

diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index 9d48c69ffece1..cac985271628c 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -1744,6 +1744,8 @@  struct rtl8xxxu_priv {
 	bool shutdown;
 	struct work_struct rx_urb_wq;
 
+	bool beacon_enabled;
+
 	u8 mac_addr[ETH_ALEN];
 	char chip_name[8];
 	char chip_vendor[8];
@@ -1850,6 +1852,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;
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
index daeaa7d6864f9..404fa6e322f58 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
@@ -1104,6 +1104,24 @@  static void rtl8xxxu_stop_tx_beacon(struct rtl8xxxu_priv *priv)
 	val8 = rtl8xxxu_read8(priv, REG_TBTT_PROHIBIT + 2);
 	val8 &= ~BIT(0);
 	rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 2, val8);
+
+	priv->beacon_enabled = false;
+}
+
+static void rtl8xxxu_start_tx_beacon(struct rtl8xxxu_priv *priv)
+{
+	u8 val8;
+
+	val8 = rtl8xxxu_read8(priv, REG_FWHW_TXQ_CTRL + 2);
+	val8 |= BIT(6);
+	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);
+
+	priv->beacon_enabled = true;
 }
 
 
@@ -4895,6 +4913,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 ||
+	    (changed & BSS_CHANGED_BEACON_ENABLED &&
+	     bss_conf->enable_beacon)) {
+		if (!priv->beacon_enabled) {
+			dev_dbg(dev, "BSS_CHANGED_BEACON_ENABLED\n");
+			rtl8xxxu_start_tx_beacon(priv);
+			schedule_work(&priv->update_beacon_work);
+		}
+	}
+
 error:
 	return;
 }
@@ -5476,6 +5505,57 @@  static void rtl8xxxu_tx(struct ieee80211_hw *hw,
 	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, BIT16 of REG_TDECTRL = BIT0 of REG_TDECTRL+2,
+	 * write 1 to clear, cleared by SW.
+	 */
+	val8 = rtl8xxxu_read8(priv, REG_TDECTRL + 2);
+	val8 |= BIT(0);
+	rtl8xxxu_write8(priv, REG_TDECTRL + 2, val8);
+
+	/* SW_BCN_SEL - Port0 */
+	val8 = rtl8xxxu_read8(priv, REG_DWBCN1_CTRL_8723B + 2);
+	val8 &= ~BIT(4);
+	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(0))
+			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,
@@ -7244,6 +7324,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);