From: Vasanthakumar Thiagarajan <quic_vthiagar@xxxxxxxxxxx> As originally discussed in the RFC [1], the prerequisite for MLO support in cfg80211/mac80211 is that all the links participating in MLO must belong to the same wiphy/ieee80211_hw. To meet this expectation, some drivers may need to group multiple discrete hardware, each acting as a link in MLO, under one wiphy. Though most of the hardware abstractions must be handled within the driver itself, it may be required to have some sort of mapping while describing interface combination capabilities for each of the underlying hardware. Add an advertisement provision for drivers supporting such MLO mode of operation. Capability advertisement such as the number of supported channels for each physical hardware has been identified to support some of the common use cases. [1]: https://lore.kernel.org/linux-wireless/20220920100518.19705-2-quic_vthiagar@xxxxxxxxxxx/ Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.1.1-00188-QCAHKSWPL_SILICONZ-1 Signed-off-by: Vasanthakumar Thiagarajan <quic_vthiagar@xxxxxxxxxxx> Co-developed-by: Karthikeyan Periyasamy <quic_periyasa@xxxxxxxxxxx> Signed-off-by: Karthikeyan Periyasamy <quic_periyasa@xxxxxxxxxxx> --- include/net/cfg80211.h | 24 ++++++++++ net/wireless/core.c | 100 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 2e2be4fd2bb6..dde129e61b60 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5394,6 +5394,18 @@ struct wiphy_iftype_akm_suites { #define CFG80211_HW_TIMESTAMP_ALL_PEERS 0xffff +/** + * struct ieee80211_chans_per_hw - supported channels as per the + * underlying physical hardware configuration + * + * @n_chans: number of channels in @chans + * @chans: list of channels supported by the constituent hardware + */ +struct ieee80211_chans_per_hw { + u32 n_chans; + struct ieee80211_channel chans[]; +}; + /** * struct wiphy - wireless hardware description * @mtx: mutex for the data (structures) of this device @@ -5610,6 +5622,15 @@ struct wiphy_iftype_akm_suites { * A value of %CFG80211_HW_TIMESTAMP_ALL_PEERS indicates the driver * supports enabling HW timestamping for all peers (i.e. no need to * specify a mac address). + * + * @hw_chans: list of the channels supported by every constituent underlying + * hardware. Drivers abstracting multiple discrete hardware (radio) under + * one wiphy can advertise the list of channels supported by each physical + * hardware in this list. Underlying hardware specific channel list can be + * used while describing interface combination for each of them. + * @num_hw: number of underlying hardware for which the channels list are + * advertised in @hw_chans, 0 if multi hardware is not support. Expect >2 + * if multi hardware is support. */ struct wiphy { struct mutex mtx; @@ -5760,6 +5781,9 @@ struct wiphy { u16 hw_timestamp_max_peers; + struct ieee80211_chans_per_hw **hw_chans; + u8 num_hw; + char priv[] __aligned(NETDEV_ALIGN); }; diff --git a/net/wireless/core.c b/net/wireless/core.c index 3fb1b637352a..119937d0f2e0 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -690,6 +690,103 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) return 0; } +static int cfg80211_check_hw_chans(const struct ieee80211_chans_per_hw *chans1, + const struct ieee80211_chans_per_hw *chans2) +{ + int i, j; + + if (!chans1 || !chans2) + return -EINVAL; + + if (!chans1->n_chans || !chans2->n_chans) + return -EINVAL; + + /* for now same channel is not allowed in more than one + * physical hardware. + */ + for (i = 0; i < chans1->n_chans; i++) + for (j = 0; j < chans2->n_chans; j++) + if (chans1->chans[i].center_freq == + chans2->chans[j].center_freq) + return -EINVAL; + return 0; +} + +static bool +cfg80211_hw_chans_in_supported_list(struct wiphy *wiphy, + const struct ieee80211_chans_per_hw *chans) +{ + enum nl80211_band band; + struct ieee80211_supported_band *sband; + int i, j; + + for (i = 0; i < chans->n_chans; i++) { + bool found = false; + + for (band = 0; band < NUM_NL80211_BANDS; band++) { + sband = wiphy->bands[band]; + if (!sband) + continue; + for (j = 0; j < sband->n_channels; j++) { + if (chans->chans[i].center_freq == + sband->channels[j].center_freq) { + found = true; + break; + } + } + + if (found) + break; + } + + if (!found) + return false; + } + + return true; +} + +static int cfg80211_validate_per_hw_chans(struct wiphy *wiphy) +{ + int i, j; + int ret; + + if (!wiphy->num_hw) + return 0; + + if (!wiphy->hw_chans) + return -EINVAL; + + /* advertisement of supported channels in wiphy->bands should be + * sufficient when physical hardware is one. + */ + if (wiphy->num_hw < 2) + return -EINVAL; + + for (i = 0; i < wiphy->num_hw; i++) { + for (j = i + 1; j < wiphy->num_hw; j++) { + const struct ieee80211_chans_per_hw *hw_chans1; + const struct ieee80211_chans_per_hw *hw_chans2; + + hw_chans1 = wiphy->hw_chans[i]; + hw_chans2 = wiphy->hw_chans[j]; + ret = cfg80211_check_hw_chans(hw_chans1, hw_chans2); + if (ret) + return ret; + } + } + + for (i = 0; i < wiphy->num_hw; i++) { + const struct ieee80211_chans_per_hw *hw_chans; + + hw_chans = wiphy->hw_chans[i]; + if (!cfg80211_hw_chans_in_supported_list(wiphy, hw_chans)) + return -EINVAL; + } + + return 0; +} + int wiphy_register(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); @@ -927,6 +1024,9 @@ int wiphy_register(struct wiphy *wiphy) return -EINVAL; } + if (WARN_ON(cfg80211_validate_per_hw_chans(&rdev->wiphy))) + return -EINVAL; + for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) { /* * Validate we have a policy (can be explicitly set to -- 2.34.1