@@ -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.
+ * @multiple_bssid: the multiple bssid settings of the AP.
*/
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_multiple_bssid multiple_bssid;
};
/**
@@ -1656,6 +1658,19 @@ enum ieee80211_offload_flags {
IEEE80211_OFFLOAD_ENCAP_4ADDR = BIT(1),
};
+/**
+ * enum ieee80211_vif_multiple_bssid_flags - virtual interface multiple bssid flags
+ *
+ * @IEEE80211_VIF_MBSS_TRANSMITTING: this BSS is transmitting beacons
+ * @IEEE80211_VIF_MBSS_NON_TRANSMITTING: this BSS is not transmitting beacons
+ * @IEEE80211_VIF_MBSS_EMA_BEACON: beacons should be send out in EMA mode
+ */
+enum ieee80211_vif_multiple_bssid_flags {
+ IEEE80211_VIF_MBSS_TRANSMITTING = BIT(1),
+ IEEE80211_VIF_MBSS_NON_TRANSMITTING = BIT(2),
+ IEEE80211_VIF_MBSS_EMA_BEACON = BIT(3),
+};
+
/**
* struct ieee80211_vif - per-interface data
*
@@ -1702,6 +1717,11 @@ enum ieee80211_offload_flags {
* protected by fq->lock.
* @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see
* &enum ieee80211_offload_flags.
+ *
+ * @multiple_bssid: Multiple BSSID configurations.
+ * @multiple_bssid.parent: Interface index of the transmitted BSS.
+ * @multiple_bssid.flags: multiple bssid flags, see
+ * enum ieee80211_vif_multiple_bssid_flags.
*/
struct ieee80211_vif {
enum nl80211_iftype type;
@@ -1730,6 +1750,11 @@ struct ieee80211_vif {
bool txqs_stopped[IEEE80211_NUM_ACS];
+ struct {
+ struct ieee80211_vif *parent;
+ u32 flags;
+ } multiple_bssid;
+
/* must be last */
u8 drv_priv[] __aligned(sizeof(void *));
};
@@ -2377,7 +2402,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.
@@ -2389,6 +2414,8 @@ struct ieee80211_txq {
* @IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD: Hardware supports tx encapsulation
* offload
*
+ * @IEEE80211_HW_SUPPORTS_MULTI_BSSID_AP: Hardware supports multi BSSID in AP mode
+ *
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/
enum ieee80211_hw_flags {
@@ -2442,6 +2469,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
IEEE80211_HW_AMPDU_KEYBORDER_SUPPORT,
IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD,
+ IEEE80211_HW_SUPPORTS_MULTI_BSSID_AP,
/* keep last, obviously */
NUM_IEEE80211_HW_FLAGS
@@ -111,6 +111,39 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
return 0;
}
+static void ieee80211_set_multiple_bssid_options(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_ap_settings *params)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct wiphy *wiphy = local->hw.wiphy;
+ struct net_device *parent;
+ struct ieee80211_sub_if_data *psdata;
+
+ if (!ieee80211_hw_check(&local->hw, SUPPORTS_MULTI_BSSID_AP))
+ return;
+
+ if (!params->multiple_bssid.count)
+ return;
+
+ if (params->multiple_bssid.parent) {
+ parent = __dev_get_by_index(wiphy_net(wiphy),
+ params->multiple_bssid.parent);
+ if (!parent || !parent->ieee80211_ptr)
+ return;
+ psdata = IEEE80211_WDEV_TO_SUB_IF(parent->ieee80211_ptr);
+ if (psdata->vif.multiple_bssid.parent)
+ return;
+ sdata->vif.multiple_bssid.parent = &psdata->vif;
+ sdata->vif.multiple_bssid.flags |= IEEE80211_VIF_MBSS_NON_TRANSMITTING;
+ } else {
+ sdata->vif.multiple_bssid.flags |= IEEE80211_VIF_MBSS_TRANSMITTING;
+ }
+
+ if (params->multiple_bssid.ema)
+ sdata->vif.multiple_bssid.flags |= IEEE80211_VIF_MBSS_EMA_BEACON;
+ sdata->vif.bss_conf.multiple_bssid = params->multiple_bssid;
+}
+
static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
const char *name,
unsigned char name_assign_type,
@@ -141,6 +174,23 @@ 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;
+
+ sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ if (sdata && sdata->vif.type == NL80211_IFTYPE_AP) {
+ if (sdata->vif.multiple_bssid.flags & IEEE80211_VIF_MBSS_TRANSMITTING) {
+ struct ieee80211_sub_if_data *child;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(child, &sdata->local->interfaces, list)
+ if (child->vif.multiple_bssid.parent == &sdata->vif)
+ dev_close(child->wdev.netdev);
+ rcu_read_unlock();
+ } else {
+ sdata->vif.multiple_bssid.parent = NULL;
+ }
+ }
+
ieee80211_if_remove(IEEE80211_WDEV_TO_SUB_IF(wdev));
return 0;
@@ -1078,6 +1128,9 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
changed |= BSS_CHANGED_HE_BSS_COLOR;
}
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ ieee80211_set_multiple_bssid_options(sdata, params);
+
mutex_lock(&local->mtx);
err = ieee80211_vif_use_channel(sdata, ¶ms->chandef,
IEEE80211_CHANCTX_SHARED);
@@ -409,6 +409,7 @@ static const char *hw_flag_names[] = {
FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
FLAG(AMPDU_KEYBORDER_SUPPORT),
FLAG(SUPPORTS_TX_ENCAP_OFFLOAD),
+ FLAG(SUPPORTS_MULTI_BSSID_AP),
#undef FLAG
};
@@ -373,6 +373,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
bool cancel_scan;
struct cfg80211_nan_func *func;
+ /* make sure the parent is already down */
+ if (sdata->vif.type == NL80211_IFTYPE_AP &&
+ sdata->vif.multiple_bssid.parent &&
+ ieee80211_sdata_running(vif_to_sdata(sdata->vif.multiple_bssid.parent)))
+ dev_close(vif_to_sdata(sdata->vif.multiple_bssid.parent)->wdev.netdev);
+
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;