@@ -631,6 +631,7 @@ struct ieee80211_fils_discovery {
* @s1g: BSS is S1G BSS (affects Association Request format).
* @beacon_tx_rate: The configured beacon transmit rate that needs to be passed
* to driver when rate control is offloaded to firmware.
+ * @mbssid: Multiple bssid settings for AP mode
*/
struct ieee80211_bss_conf {
const u8 *bssid;
@@ -700,6 +701,7 @@ struct ieee80211_bss_conf {
u32 unsol_bcast_probe_resp_interval;
bool s1g;
struct cfg80211_bitrate_mask beacon_tx_rate;
+ struct cfg80211_mbssid_config mbssid;
};
/**
@@ -1663,6 +1665,20 @@ enum ieee80211_offload_flags {
IEEE80211_OFFLOAD_DECAP_ENABLED = BIT(2),
};
+/**
+ * enum ieee80211_vif_mbssid_flags - virtual interface multiple bssid flags
+ *
+ * @IEEE80211_VIF_MBSSID_TX: Set for the transmitting profile.
+ * @IEEE80211_VIF_MBSSID_NON_TX: Set for non-transmitting profiles.
+ * @IEEE80211_VIF_MBSSID_EMA: Set when enhanced multi-BSS advertisements
+ * are enabled.
+ */
+enum ieee80211_vif_mbssid_flags {
+ IEEE80211_VIF_MBSSID_TX = BIT(1),
+ IEEE80211_VIF_MBSSID_NON_TX = BIT(2),
+ IEEE80211_VIF_MBSSID_EMA = BIT(3),
+};
+
/**
* struct ieee80211_vif - per-interface data
*
@@ -1709,6 +1725,10 @@ enum ieee80211_offload_flags {
* protected by fq->lock.
* @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see
* &enum ieee80211_offload_flags.
+ *
+ * @mbssid: Multiple BSSID configurations.
+ * @mbssid.parent: Interface index of the transmitted BSS.
+ * @mbssid.flags: multiple bssid flags, see enum ieee80211_vif_mbssid_flags.
*/
struct ieee80211_vif {
enum nl80211_iftype type;
@@ -1737,6 +1757,11 @@ struct ieee80211_vif {
bool txqs_stopped[IEEE80211_NUM_ACS];
+ struct {
+ struct ieee80211_vif *parent;
+ u32 flags;
+ } mbssid;
+
/* must be last */
u8 drv_priv[] __aligned(sizeof(void *));
};
@@ -2381,7 +2406,7 @@ struct ieee80211_txq {
* @IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN: Driver does not report accurate A-MPDU
* length in tx status information
*
- * @IEEE80211_HW_SUPPORTS_MULTI_BSSID: Hardware supports multi BSSID
+ * @IEEE80211_HW_SUPPORTS_MULTI_BSSID: Hardware supports multi BSSID in STA mode
*
* @IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID: Hardware supports multi BSSID
* only for HE APs. Applies if @IEEE80211_HW_SUPPORTS_MULTI_BSSID is set.
@@ -2402,6 +2427,11 @@ struct ieee80211_txq {
* usage and 802.11 frames with %RX_FLAG_ONLY_MONITOR set for monitor to
* the stack.
*
+ * @IEEE80211_HW_SUPPORTS_MBSSID_AP: Hardware supports multiple BSSID
+ * advertisements in AP mode.
+ * @IEEE80211_HW_SUPPORTS_EMA_AP: Hardware supports enhanced multiple BSSID
+ * advertisements in AP mode.
+ *
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/
enum ieee80211_hw_flags {
@@ -2457,6 +2487,8 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD,
IEEE80211_HW_SUPPORTS_RX_DECAP_OFFLOAD,
IEEE80211_HW_SUPPORTS_CONC_MON_RX_DECAP,
+ IEEE80211_HW_SUPPORTS_MBSSID_AP,
+ IEEE80211_HW_SUPPORTS_EMA_AP,
/* keep last, obviously */
NUM_IEEE80211_HW_FLAGS
@@ -111,6 +111,44 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
return 0;
}
+static int ieee80211_set_mbssid_options(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_mbssid_config params)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct wiphy *wiphy = local->hw.wiphy;
+ struct net_device *parent;
+ struct ieee80211_sub_if_data *psdata;
+
+ if (!params.count || sdata->vif.type != NL80211_IFTYPE_AP ||
+ !ieee80211_hw_check(&local->hw, SUPPORTS_MBSSID_AP))
+ return -EINVAL;
+
+ sdata->vif.mbssid.parent = NULL;
+ sdata->vif.mbssid.flags = IEEE80211_VIF_MBSSID_TX;
+ if (params.parent) {
+ parent = __dev_get_by_index(wiphy_net(wiphy), params.parent);
+ if (!parent || !parent->ieee80211_ptr)
+ return -EINVAL;
+
+ psdata = IEEE80211_WDEV_TO_SUB_IF(parent->ieee80211_ptr);
+ if (psdata != sdata) {
+ if (psdata->vif.mbssid.parent)
+ return -EINVAL;
+ sdata->vif.mbssid.parent = &psdata->vif;
+ sdata->vif.mbssid.flags = IEEE80211_VIF_MBSSID_NON_TX;
+ }
+ }
+
+ if (params.ema) {
+ if (!ieee80211_hw_check(&local->hw, SUPPORTS_EMA_AP))
+ return -EINVAL;
+ sdata->vif.mbssid.flags |= IEEE80211_VIF_MBSSID_EMA;
+ }
+
+ sdata->vif.bss_conf.mbssid = params;
+ return 0;
+}
+
static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
const char *name,
unsigned char name_assign_type,
@@ -141,6 +179,36 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
static int ieee80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ struct ieee80211_local *local;
+ struct ieee80211_vif *vif;
+
+ if (!sdata)
+ return 0;
+
+ local = sdata->local;
+ vif = &sdata->vif;
+ if (vif->type == NL80211_IFTYPE_AP &&
+ ieee80211_hw_check(&local->hw, SUPPORTS_MBSSID_AP)) {
+ if (vif->mbssid.flags & IEEE80211_VIF_MBSSID_TX) {
+ struct ieee80211_sub_if_data *child, *tmpsdata;
+
+ wiphy_unlock(local->hw.wiphy);
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry_safe(child, tmpsdata,
+ &local->interfaces, list) {
+ if (child->vif.mbssid.parent == vif &&
+ ieee80211_sdata_running(child))
+ dev_close(child->wdev.netdev);
+ }
+ mutex_unlock(&local->iflist_mtx);
+ wiphy_lock(local->hw.wiphy);
+ } else {
+ vif->mbssid.parent = NULL;
+ vif->mbssid.flags = 0;
+ }
+ }
+
ieee80211_if_remove(IEEE80211_WDEV_TO_SUB_IF(wdev));
return 0;
@@ -1078,6 +1146,12 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
changed |= BSS_CHANGED_HE_BSS_COLOR;
}
+ if (params->mbssid.count) {
+ err = ieee80211_set_mbssid_options(sdata, params->mbssid);
+ if (err)
+ return err;
+ }
+
mutex_lock(&local->mtx);
err = ieee80211_vif_use_channel(sdata, ¶ms->chandef,
IEEE80211_CHANCTX_SHARED);
@@ -457,6 +457,8 @@ static const char *hw_flag_names[] = {
FLAG(SUPPORTS_TX_ENCAP_OFFLOAD),
FLAG(SUPPORTS_RX_DECAP_OFFLOAD),
FLAG(SUPPORTS_CONC_MON_RX_DECAP),
+ FLAG(SUPPORTS_MBSSID_AP),
+ FLAG(SUPPORTS_EMA_AP),
#undef FLAG
};
@@ -375,6 +375,18 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
struct cfg80211_chan_def chandef;
bool cancel_scan;
struct cfg80211_nan_func *func;
+ struct ieee80211_sub_if_data *parent;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP &&
+ ieee80211_hw_check(&local->hw, SUPPORTS_MBSSID_AP) &&
+ sdata->vif.mbssid.flags & IEEE80211_VIF_MBSSID_NON_TX) {
+ parent = vif_to_sdata(sdata->vif.mbssid.parent);
+ if (parent && ieee80211_sdata_running(parent)) {
+ wiphy_unlock(local->hw.wiphy);
+ dev_close(parent->wdev.netdev);
+ wiphy_lock(local->hw.wiphy);
+ }
+ }
clear_bit(SDATA_STATE_RUNNING, &sdata->state);