@@ -5030,6 +5030,32 @@ struct ieee80211_iface_limit {
u16 types;
};
+/**
+ * struct ieee80211_iface_per_hw - hardware specific interface combination
+ *
+ * Drivers registering multiple radios under a single wiphy can advertise
+ * radio specific interface combinations through this structure. Please note
+ * that to maintain the compatibility with the user space which is not aware
+ * of this extension of per-hardware interface combination signaling,
+ * the driver should still advertise it's interface combination (mostly
+ * common minimum capability) using the existing interface combination signaling
+ * method.
+ *
+ * @limits: limits for the given interface type
+ * @num_different_channels: number of different channels which can be active
+ * concurrently in this hardware
+ * @max_interfaces: maximum number of total interfaces allowed in this group
+ * @n_limits: number of limitations
+ * @hw_chans_idx: index of hardware specific channel list as per wiphy @hw_chans
+ */
+struct ieee80211_iface_per_hw {
+ const struct ieee80211_iface_limit *limits;
+ u32 num_different_channels;
+ u16 max_interfaces;
+ u8 n_limits;
+ u8 hw_chans_idx;
+};
+
/**
* struct ieee80211_iface_combination - possible interface combination
*
@@ -5088,6 +5114,62 @@ struct ieee80211_iface_limit {
* .num_different_channels = 2,
* };
*
+ *
+ * 4. Hardware specific interface combination with driver supporting two
+ * physical HW, first underlying HW supporting 2 GHz band and the other
+ * supporting 5 GHz band.
+ *
+ * Allow #STA <= 1, #AP <= 1, channels = 1, total 2 in 2 GHz radio and
+ *
+ * Allow #STA <= 1, #AP <= 2, channels = 1, total 3 in 5 GHz radio
+ *
+ * Drivers advertising per-hardware interface combination should also
+ * advertise a sub-set of capabilities using existing interface mainly for
+ * maintaining compatibility with the user space which is not aware of the
+ * new per-hardware advertisement.
+ *
+ * Sub-set interface combination advertised in the existing infrastructure:
+ * Allow #STA <= 1, #AP <= 1, channels = 1, total 2
+ *
+ * .. code-block:: c
+ *
+ * struct ieee80211_iface_limit limits_overall[] = {
+ * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
+ * { .max = 1, .types = BIT(NL80211_IFTYPE_AP), },
+ * };
+ * struct ieee80211_iface_limit limits_2ghz[] = {
+ * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
+ * { .max = 1, .types = BIT(NL80211_IFTYPE_AP), },
+ * };
+ * struct ieee80211_iface_limit limits_5ghz[] = {
+ * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
+ * { .max = 2, .types = BIT(NL80211_IFTYPE_AP), },
+ * };
+ * struct ieee80211_iface_per_hw hw_combinations[] = {
+ * {
+ * .hw_chans_idx = 0,
+ * .limits = limits_2ghz,
+ * .num_different_channels = 1,
+ * .max_interfaces = 2,
+ * .n_limits = ARRAY_SIZE(limits_2ghz),
+ * },
+ * {
+ * .hw_chans_idx = 1,
+ * .limits = limits_5ghz,
+ * .num_different_channels = 1,
+ * .max_interfaces = 3,
+ * .n_limits = ARRAY_SIZE(limits_5ghz),
+ * },
+ * };
+ * struct ieee80211_iface_combination combination4 = {
+ * .limits = limits_overall,
+ * .n_limits = ARRAY_SIZE(limits_overall),
+ * .max_interfaces = 2,
+ * .num_different_channels = 1,
+ * .iface_hw_list = hw_combinations,
+ * .n_hw_list = ARRAY_SIZE(hw_combinations),
+ * };
+ *
*/
struct ieee80211_iface_combination {
/**
@@ -5145,6 +5227,20 @@ struct ieee80211_iface_combination {
* combination must be greater or equal to this value.
*/
u32 beacon_int_min_gcd;
+
+ /**
+ * @iface_hw_list:
+ * This wiphy has multiple underlying radios, describe interface
+ * combination for each of them, valid only when the driver advertises
+ * multi-radio presence in wiphy @hw_chans.
+ */
+ const struct ieee80211_iface_per_hw *iface_hw_list;
+
+ /**
+ * @n_hw_list:
+ * number of hardware in @iface_hw_List
+ */
+ u32 n_hw_list;
};
struct ieee80211_txrx_stypes {
@@ -1083,6 +1083,46 @@ static int ieee80211_init_cipher_suites(struct ieee80211_local *local)
return 0;
}
+static int
+ieee80211_check_per_hw_iface_comb(struct ieee80211_local *local,
+ const struct ieee80211_iface_combination *c)
+{
+ int hw_idx, lmt_idx;
+ u32 hw_idx_bm = 0;
+
+ if (!local->hw.wiphy->num_hw)
+ return -EINVAL;
+
+ if (local->emulate_chanctx)
+ return -EINVAL;
+
+ for (hw_idx = 0; hw_idx < c->n_hw_list; hw_idx++) {
+ const struct ieee80211_iface_per_hw *hl;
+
+ hl = &c->iface_hw_list[hw_idx];
+
+ if (hl->hw_chans_idx >= local->hw.wiphy->num_hw)
+ return -EINVAL;
+
+ /* mac80211 doesn't support more than one IBSS interface right now */
+ for (lmt_idx = 0; lmt_idx < hl->n_limits; lmt_idx++) {
+ const struct ieee80211_iface_limit *limits;
+
+ limits = &hl->limits[lmt_idx];
+ if ((limits->types & BIT(NL80211_IFTYPE_ADHOC)) &&
+ limits->max > 1)
+ return -EINVAL;
+ }
+
+ if (hw_idx_bm & BIT(hw_idx))
+ return -EINVAL;
+
+ hw_idx_bm |= BIT(hw_idx);
+ }
+
+ return 0;
+}
+
int ieee80211_register_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
@@ -1323,6 +1363,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) &&
c->limits[j].max > 1)
return -EINVAL;
+
+ if (!c->n_hw_list)
+ continue;
+
+ if (ieee80211_check_per_hw_iface_comb(local, c))
+ return -EINVAL;
}
local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) +
@@ -600,6 +600,34 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
}
EXPORT_SYMBOL(wiphy_new_nm);
+/**
+ * cfg80211_hw_chans_includes_dfs - check if per-hardware channel includes DFS
+ * @chans: hardware channel list
+ *
+ * Check if the given per-hardware list include channels in DFS range.
+ * Please note the channel is checked against the entire range of DFS
+ * freq in 5 GHz irrespective of regulatory configurations.
+ *
+ * This helper helps for the sanity checks on the channel advertisement from
+ * driver during the hw registration.
+ */
+static bool
+cfg80211_hw_chans_includes_dfs(const struct ieee80211_chans_per_hw *chans)
+{
+ int i;
+
+ for (i = 0; i < chans->n_chans; i++) {
+ if (chans->chans[i].band == NL80211_BAND_5GHZ &&
+ ((chans->chans[i].center_freq >= 5250 &&
+ chans->chans[i].center_freq <= 5340) ||
+ (chans->chans[i].center_freq >= 5480 &&
+ chans->chans[i].center_freq <= 5720)))
+ return true;
+ }
+
+ return false;
+}
+
static int
wiphy_verify_comb_limit(struct wiphy *wiphy,
const struct ieee80211_iface_limit *limits,
@@ -659,6 +687,63 @@ wiphy_verify_comb_limit(struct wiphy *wiphy,
return 0;
}
+static int
+wiphy_verify_comb_per_hw(struct wiphy *wiphy,
+ const struct ieee80211_iface_combination *comb)
+{
+ int i;
+ u32 hw_idx_bitmap = 0;
+ int ret;
+
+ for (i = 0; i < comb->n_hw_list; i++) {
+ const struct ieee80211_iface_per_hw *hl;
+ const struct ieee80211_chans_per_hw *chans;
+ u32 iface_cnt = 0;
+ u16 all_iftypes = 0;
+
+ hl = &comb->iface_hw_list[i];
+
+ if (hl->hw_chans_idx >= wiphy->num_hw)
+ return -EINVAL;
+
+ if (hw_idx_bitmap & BIT(hl->hw_chans_idx))
+ return -EINVAL;
+
+ hw_idx_bitmap |= BIT(hl->hw_chans_idx);
+ chans = wiphy->hw_chans[hl->hw_chans_idx];
+
+ if (WARN_ON(hl->max_interfaces < 2 &&
+ (!comb->radar_detect_widths ||
+ !(cfg80211_hw_chans_includes_dfs(chans)))))
+ return -EINVAL;
+
+ if (WARN_ON(!hl->num_different_channels))
+ return -EINVAL;
+
+ if (WARN_ON(comb->radar_detect_widths &&
+ cfg80211_hw_chans_includes_dfs(chans) &&
+ hl->num_different_channels > 1))
+ return -EINVAL;
+
+ if (WARN_ON(!hl->n_limits))
+ return -EINVAL;
+
+ ret = wiphy_verify_comb_limit(wiphy, hl->limits, hl->n_limits,
+ comb->beacon_int_min_gcd,
+ &iface_cnt, &all_iftypes);
+ if (ret)
+ return ret;
+
+ if (WARN_ON(all_iftypes & BIT(NL80211_IFTYPE_WDS)))
+ return -EINVAL;
+
+ if (WARN_ON(iface_cnt < comb->max_interfaces))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int wiphy_verify_combinations(struct wiphy *wiphy)
{
const struct ieee80211_iface_combination *c;
@@ -701,6 +786,13 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
/* You can't even choose that many! */
if (WARN_ON(cnt < c->max_interfaces))
return -EINVAL;
+
+ /* Do similar validations on the freq range specific interface
+ * combinations when advertised.
+ */
+ if (WARN_ON(c->n_hw_list &&
+ wiphy_verify_comb_per_hw(wiphy, c)))
+ return -EINVAL;
}
return 0;