mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-16 21:35:07 +00:00
wifi: ath12k: advertise multi device interface combination
The prerequisite for MLO support in cfg80211/mac80211 requires that all the links participating in MLO belong to the same wiphy/struct ieee80211_hw. The driver needs to group multiple discrete hardware components, each acting as a link in MLO, under one wiphy. Consequently, the driver advertises multi-hardware device interface combination capabilities specific to the radio, including supported frequencies. The global interface combination represent the combined interface capabilities. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Karthikeyan Periyasamy <quic_periyasa@quicinc.com> Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com> Link: https://patch.msgid.link/20241209185421.376381-7-kvalo@kernel.org Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
This commit is contained in:
parent
3c9bc818b8
commit
ae6b065282
@ -10336,14 +10336,20 @@ static bool ath12k_mac_is_iface_mode_enable(struct ath12k_hw *ah,
|
||||
{
|
||||
struct ath12k *ar;
|
||||
int i;
|
||||
u16 interface_modes, mode;
|
||||
bool is_enable = true;
|
||||
u16 interface_modes, mode = 0;
|
||||
bool is_enable = false;
|
||||
|
||||
if (type == NL80211_IFTYPE_MESH_POINT) {
|
||||
if (IS_ENABLED(CONFIG_MAC80211_MESH))
|
||||
mode = BIT(type);
|
||||
} else {
|
||||
mode = BIT(type);
|
||||
}
|
||||
|
||||
mode = BIT(type);
|
||||
for_each_ar(ah, ar, i) {
|
||||
interface_modes = ar->ab->hw_params->interface_modes;
|
||||
if (!(interface_modes & mode)) {
|
||||
is_enable = false;
|
||||
if (interface_modes & mode) {
|
||||
is_enable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -10351,31 +10357,20 @@ static bool ath12k_mac_is_iface_mode_enable(struct ath12k_hw *ah,
|
||||
return is_enable;
|
||||
}
|
||||
|
||||
static void ath12k_mac_cleanup_iface_combinations(struct ath12k_hw *ah)
|
||||
static int
|
||||
ath12k_mac_setup_radio_iface_comb(struct ath12k *ar,
|
||||
struct ieee80211_iface_combination *comb)
|
||||
{
|
||||
struct wiphy *wiphy = ah->hw->wiphy;
|
||||
|
||||
kfree(wiphy->iface_combinations[0].limits);
|
||||
kfree(wiphy->iface_combinations);
|
||||
}
|
||||
|
||||
static int ath12k_mac_setup_iface_combinations(struct ath12k_hw *ah)
|
||||
{
|
||||
struct wiphy *wiphy = ah->hw->wiphy;
|
||||
struct ieee80211_iface_combination *combinations;
|
||||
u16 interface_modes = ar->ab->hw_params->interface_modes;
|
||||
struct ieee80211_iface_limit *limits;
|
||||
int n_limits, max_interfaces;
|
||||
bool ap, mesh, p2p;
|
||||
|
||||
ap = ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_AP);
|
||||
p2p = ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_P2P_DEVICE);
|
||||
ap = interface_modes & BIT(NL80211_IFTYPE_AP);
|
||||
p2p = interface_modes & BIT(NL80211_IFTYPE_P2P_DEVICE);
|
||||
|
||||
mesh = IS_ENABLED(CONFIG_MAC80211_MESH) &&
|
||||
ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_MESH_POINT);
|
||||
|
||||
combinations = kzalloc(sizeof(*combinations), GFP_KERNEL);
|
||||
if (!combinations)
|
||||
return -ENOMEM;
|
||||
(interface_modes & BIT(NL80211_IFTYPE_MESH_POINT));
|
||||
|
||||
if ((ap || mesh) && !p2p) {
|
||||
n_limits = 2;
|
||||
@ -10392,10 +10387,8 @@ static int ath12k_mac_setup_iface_combinations(struct ath12k_hw *ah)
|
||||
}
|
||||
|
||||
limits = kcalloc(n_limits, sizeof(*limits), GFP_KERNEL);
|
||||
if (!limits) {
|
||||
kfree(combinations);
|
||||
if (!limits)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
limits[0].max = 1;
|
||||
limits[0].types |= BIT(NL80211_IFTYPE_STATION);
|
||||
@ -10411,26 +10404,181 @@ static int ath12k_mac_setup_iface_combinations(struct ath12k_hw *ah)
|
||||
|
||||
if (p2p) {
|
||||
limits[1].types |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
||||
BIT(NL80211_IFTYPE_P2P_GO);
|
||||
BIT(NL80211_IFTYPE_P2P_GO);
|
||||
limits[2].max = 1;
|
||||
limits[2].types |= BIT(NL80211_IFTYPE_P2P_DEVICE);
|
||||
}
|
||||
|
||||
combinations[0].limits = limits;
|
||||
combinations[0].n_limits = n_limits;
|
||||
combinations[0].max_interfaces = max_interfaces;
|
||||
combinations[0].num_different_channels = 1;
|
||||
combinations[0].beacon_int_infra_match = true;
|
||||
combinations[0].beacon_int_min_gcd = 100;
|
||||
combinations[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
|
||||
BIT(NL80211_CHAN_WIDTH_20) |
|
||||
BIT(NL80211_CHAN_WIDTH_40) |
|
||||
BIT(NL80211_CHAN_WIDTH_80);
|
||||
comb[0].limits = limits;
|
||||
comb[0].n_limits = n_limits;
|
||||
comb[0].max_interfaces = max_interfaces;
|
||||
comb[0].num_different_channels = 1;
|
||||
comb[0].beacon_int_infra_match = true;
|
||||
comb[0].beacon_int_min_gcd = 100;
|
||||
comb[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
|
||||
BIT(NL80211_CHAN_WIDTH_20) |
|
||||
BIT(NL80211_CHAN_WIDTH_40) |
|
||||
BIT(NL80211_CHAN_WIDTH_80);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ath12k_mac_setup_global_iface_comb(struct ath12k_hw *ah,
|
||||
struct wiphy_radio *radio,
|
||||
u8 n_radio,
|
||||
struct ieee80211_iface_combination *comb)
|
||||
{
|
||||
const struct ieee80211_iface_combination *iter_comb;
|
||||
struct ieee80211_iface_limit *limits;
|
||||
int i, j, n_limits;
|
||||
bool ap, mesh, p2p;
|
||||
|
||||
if (!n_radio)
|
||||
return 0;
|
||||
|
||||
ap = ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_AP);
|
||||
p2p = ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_P2P_DEVICE);
|
||||
mesh = ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_MESH_POINT);
|
||||
|
||||
if ((ap || mesh) && !p2p)
|
||||
n_limits = 2;
|
||||
else if (p2p)
|
||||
n_limits = 3;
|
||||
else
|
||||
n_limits = 1;
|
||||
|
||||
limits = kcalloc(n_limits, sizeof(*limits), GFP_KERNEL);
|
||||
if (!limits)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < n_radio; i++) {
|
||||
iter_comb = radio[i].iface_combinations;
|
||||
for (j = 0; j < iter_comb->n_limits && j < n_limits; j++) {
|
||||
limits[j].types |= iter_comb->limits[j].types;
|
||||
limits[j].max += iter_comb->limits[j].max;
|
||||
}
|
||||
|
||||
comb->max_interfaces += iter_comb->max_interfaces;
|
||||
comb->num_different_channels += iter_comb->num_different_channels;
|
||||
comb->radar_detect_widths |= iter_comb->radar_detect_widths;
|
||||
}
|
||||
|
||||
comb->limits = limits;
|
||||
comb->n_limits = n_limits;
|
||||
comb->beacon_int_infra_match = true;
|
||||
comb->beacon_int_min_gcd = 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void ath12k_mac_cleanup_iface_comb(const struct ieee80211_iface_combination *iface_comb)
|
||||
{
|
||||
kfree(iface_comb[0].limits);
|
||||
kfree(iface_comb);
|
||||
}
|
||||
|
||||
static void ath12k_mac_cleanup_iface_combinations(struct ath12k_hw *ah)
|
||||
{
|
||||
struct wiphy *wiphy = ah->hw->wiphy;
|
||||
const struct wiphy_radio *radio;
|
||||
int i;
|
||||
|
||||
if (wiphy->n_radio > 0) {
|
||||
radio = wiphy->radio;
|
||||
for (i = 0; i < wiphy->n_radio; i++)
|
||||
ath12k_mac_cleanup_iface_comb(radio[i].iface_combinations);
|
||||
|
||||
kfree(wiphy->radio);
|
||||
}
|
||||
|
||||
ath12k_mac_cleanup_iface_comb(wiphy->iface_combinations);
|
||||
}
|
||||
|
||||
static int ath12k_mac_setup_iface_combinations(struct ath12k_hw *ah)
|
||||
{
|
||||
struct ieee80211_iface_combination *combinations, *comb;
|
||||
struct wiphy *wiphy = ah->hw->wiphy;
|
||||
struct wiphy_radio *radio;
|
||||
struct ath12k *ar;
|
||||
int i, ret;
|
||||
|
||||
combinations = kzalloc(sizeof(*combinations), GFP_KERNEL);
|
||||
if (!combinations)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ah->num_radio == 1) {
|
||||
ret = ath12k_mac_setup_radio_iface_comb(&ah->radio[0],
|
||||
combinations);
|
||||
if (ret) {
|
||||
ath12k_hw_warn(ah, "failed to setup radio interface combinations for one radio: %d",
|
||||
ret);
|
||||
goto err_free_combinations;
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* there are multiple radios */
|
||||
|
||||
radio = kcalloc(ah->num_radio, sizeof(*radio), GFP_KERNEL);
|
||||
if (!radio) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_combinations;
|
||||
}
|
||||
|
||||
for_each_ar(ah, ar, i) {
|
||||
comb = kzalloc(sizeof(*comb), GFP_KERNEL);
|
||||
if (!comb) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_radios;
|
||||
}
|
||||
|
||||
ret = ath12k_mac_setup_radio_iface_comb(ar, comb);
|
||||
if (ret) {
|
||||
ath12k_hw_warn(ah, "failed to setup radio interface combinations for radio %d: %d",
|
||||
i, ret);
|
||||
kfree(comb);
|
||||
goto err_free_radios;
|
||||
}
|
||||
|
||||
radio[i].freq_range = &ar->freq_range;
|
||||
radio[i].n_freq_range = 1;
|
||||
|
||||
radio[i].iface_combinations = comb;
|
||||
radio[i].n_iface_combinations = 1;
|
||||
}
|
||||
|
||||
ret = ath12k_mac_setup_global_iface_comb(ah, radio, ah->num_radio, combinations);
|
||||
if (ret) {
|
||||
ath12k_hw_warn(ah, "failed to setup global interface combinations: %d",
|
||||
ret);
|
||||
goto err_free_all_radios;
|
||||
}
|
||||
|
||||
wiphy->radio = radio;
|
||||
wiphy->n_radio = ah->num_radio;
|
||||
|
||||
out:
|
||||
wiphy->iface_combinations = combinations;
|
||||
wiphy->n_iface_combinations = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_all_radios:
|
||||
i = ah->num_radio;
|
||||
|
||||
err_free_radios:
|
||||
while (i--)
|
||||
ath12k_mac_cleanup_iface_comb(radio[i].iface_combinations);
|
||||
|
||||
kfree(radio);
|
||||
|
||||
err_free_combinations:
|
||||
kfree(combinations);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const u8 ath12k_if_types_ext_capa[] = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user