diff mbox series

[13/13] wifi: ath12k: Advertise multi hardware iface combination

Message ID 20240328072916.1164195-14-quic_periyasa@quicinc.com
State New
Headers show
Series wifi: Add multi physical hardware iface combination support | expand

Commit Message

Karthikeyan Periyasamy March 28, 2024, 7:29 a.m. UTC
From: Harshitha Prem <quic_hprem@quicinc.com>

The prerequisite for MLO support in cfg80211/mac80211 requires that all
the links participating in MLO belong to the same wiphy/ieee80211_hw.
The driver needs to group multiple discrete hardware each acting as a
link in MLO, under one wiphy. Consequently, the driver advertises multi
hardware interface combination capabilities with a list of supported
frequencies.

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.1.1-00188-QCAHKSWPL_SILICONZ-1

Signed-off-by: Harshitha Prem <quic_hprem@quicinc.com>
Co-developed-by: Karthikeyan Periyasamy <quic_periyasa@quicinc.com>
Signed-off-by: Karthikeyan Periyasamy <quic_periyasa@quicinc.com>
---
 drivers/net/wireless/ath/ath12k/mac.c | 128 +++++++++++++++++++++++++-
 1 file changed, 127 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 44c8bf6eb6ae..3d0cae8d883a 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -7781,10 +7781,126 @@  static bool ath12k_mac_is_iface_mode_enable(struct ath12k_hw *ah,
 	return is_enable;
 }
 
+static
+struct ieee80211_chans_per_hw *ath12k_setup_per_hw_chan(struct ath12k *ar)
+{
+	struct ieee80211_chans_per_hw *chans;
+	struct ieee80211_supported_band *band;
+
+	if (ar->pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP)
+		band = &ar->mac.sbands[NL80211_BAND_2GHZ];
+	else if (ar->pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP &&
+		 !ar->supports_6ghz)
+		band = &ar->mac.sbands[NL80211_BAND_5GHZ];
+	else if (ar->pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP &&
+		 ar->supports_6ghz)
+		band = &ar->mac.sbands[NL80211_BAND_6GHZ];
+
+	chans = kzalloc(struct_size(chans, chans, band->n_channels),
+			GFP_KERNEL);
+	if (!chans)
+		return NULL;
+
+	memcpy(chans->chans, band->channels,
+	       sizeof(*band->channels) * band->n_channels);
+	chans->n_chans = band->n_channels;
+
+	return chans;
+}
+
+static void ath12k_mac_cleanup_hw_channels(struct ath12k_hw *ah)
+{
+	struct wiphy *wiphy = ah->hw->wiphy;
+	int i;
+
+	for (i = 0; i < ah->num_radio; i++)
+		kfree(wiphy->hw_chans[i]);
+
+	kfree(wiphy->hw_chans);
+}
+
+static int
+ath12k_mac_setup_hw_channels(struct ath12k_hw *ah)
+{
+	struct wiphy *wiphy = ah->hw->wiphy;
+	int i, ret;
+
+	wiphy->hw_chans = kcalloc(ah->num_radio, sizeof(*wiphy->hw_chans),
+				  GFP_KERNEL);
+	if (!wiphy->hw_chans)
+		return -ENOMEM;
+
+	for (i = 0; i < ah->num_radio; i++) {
+		wiphy->hw_chans[i] = ath12k_setup_per_hw_chan(&ah->radio[i]);
+		if (!wiphy->hw_chans[i]) {
+			ret = -ENOMEM;
+			goto cleanup_hw_chan;
+		}
+	}
+
+	wiphy->num_hw = ah->num_radio;
+
+	return 0;
+
+cleanup_hw_chan:
+	for (i = i - 1; i >= 0; i--)
+		kfree(wiphy->hw_chans[i]);
+
+	kfree(wiphy->hw_chans);
+
+	return ret;
+}
+
+static void
+ath12k_mac_cleanup_per_hw_iface_comb(struct ath12k_hw *ah)
+{
+	struct wiphy *wiphy = ah->hw->wiphy;
+
+	ath12k_mac_cleanup_hw_channels(ah);
+
+	kfree(wiphy->iface_combinations[0].iface_hw_list);
+}
+
+static int
+ath12k_mac_setup_per_hw_iface_comb(struct ath12k_hw *ah,
+				   struct ieee80211_iface_combination *comb)
+{
+	struct ieee80211_iface_per_hw *iface_hw;
+	struct ieee80211_hw *hw = ah->hw;
+	int i, ret;
+
+	ret = ath12k_mac_setup_hw_channels(ah);
+	if (ret)
+		return ret;
+
+	iface_hw  = kcalloc(ah->num_radio, sizeof(*iface_hw), GFP_KERNEL);
+	if (!iface_hw) {
+		ath12k_mac_cleanup_hw_channels(ah);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < ah->num_radio; i++) {
+		iface_hw[i].hw_chans_idx = i;
+		iface_hw[i].num_different_channels =
+					comb->num_different_channels;
+		iface_hw[i].max_interfaces = comb->max_interfaces;
+		iface_hw[i].limits = comb->limits;
+		iface_hw[i].n_limits = comb->n_limits;
+	}
+
+	comb->iface_hw_list = iface_hw;
+	comb->n_hw_list = hw->wiphy->num_hw;
+
+	return 0;
+}
+
 static void ath12k_mac_cleanup_iface_combinations(struct ath12k_hw *ah)
 {
 	struct wiphy *wiphy = ah->hw->wiphy;
 
+	if (ah->num_radio > 1)
+		ath12k_mac_cleanup_per_hw_iface_comb(ah);
+
 	kfree(wiphy->iface_combinations[0].limits);
 	kfree(wiphy->iface_combinations);
 }
@@ -7794,7 +7910,7 @@  static int ath12k_mac_setup_iface_combinations(struct ath12k_hw *ah)
 	struct wiphy *wiphy = ah->hw->wiphy;
 	struct ieee80211_iface_combination *combinations;
 	struct ieee80211_iface_limit *limits;
-	int n_limits, max_interfaces;
+	int n_limits, max_interfaces, ret;
 	bool ap, mesh, p2p;
 
 	ap = ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_AP);
@@ -7857,6 +7973,16 @@  static int ath12k_mac_setup_iface_combinations(struct ath12k_hw *ah)
 						BIT(NL80211_CHAN_WIDTH_40) |
 						BIT(NL80211_CHAN_WIDTH_80);
 
+	if (ah->num_radio > 1) {
+		ret = ath12k_mac_setup_per_hw_iface_comb(ah, combinations);
+		if (ret) {
+			kfree(limits);
+			kfree(combinations);
+
+			return ret;
+		}
+	}
+
 	wiphy->iface_combinations = combinations;
 	wiphy->n_iface_combinations = 1;