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

Karthikeyan Periyasamy
2024-03-28T07:30:23Z

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 --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;

--
2.34.1