#endif /* CONFIG_WEP */
-static void hostapd_clear_drv_priv(struct hostapd_data *hapd)
-{
-#ifdef CONFIG_IEEE80211BE
- unsigned int i;
-
- for (i = 0; i < hapd->iface->interfaces->count; i++) {
- struct hostapd_iface *iface = hapd->iface->interfaces->iface[i];
-
- if (hapd->iface == iface || !iface)
- continue;
-
- if (iface->bss && iface->bss[0] &&
- hostapd_mld_get_first_bss(iface->bss[0]) == hapd)
- iface->bss[0]->drv_priv = NULL;
- }
-#endif /* CONFIG_IEEE80211BE */
-
- hapd->drv_priv = NULL;
-}
-
-
#ifdef CONFIG_IEEE80211BE
#ifdef CONFIG_TESTING_OPTIONS
* driver wrapper may have removed its internal instance
* and hapd->drv_priv is not valid anymore.
*/
- hostapd_clear_drv_priv(hapd);
+ hapd->drv_priv = NULL;
}
}
+#ifdef CONFIG_IEEE80211BE
+ /* If the interface was not added as well as it is not the first BSS,
+ * at least the link should be removed here since deinit will take care
+ * of only the first BSS. */
+ if (hapd->conf->mld_ap && !hapd->interface_added &&
+ hapd->iface->bss[0] != hapd)
+ hostapd_if_link_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface,
+ hapd->mld_link_id);
+#endif /* CONFIG_IEEE80211BE */
+
wpabuf_free(hapd->time_adv);
hapd->time_adv = NULL;
}
+static void hostapd_cleanup_driver(const struct wpa_driver_ops *driver,
+ void *drv_priv, struct hostapd_iface *iface)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (!driver || !driver->hapd_deinit || !drv_priv)
+ return;
+
+ /* In case of non-ML operation, de-init. But if ML operation exist,
+ * even if that's the last BSS in the interface, the driver (drv) could
+ * be in use for a different AP MLD. Hence, need to check if drv is
+ * still being used by some other BSS before de-initiallizing. */
+ if (!iface->bss[0]->conf->mld_ap) {
+ driver->hapd_deinit(drv_priv);
+ } else if (hostapd_mld_is_first_bss(iface->bss[0]) &&
+ driver->is_drv_shared &&
+ !driver->is_drv_shared(drv_priv, iface->bss[0])) {
+ driver->hapd_deinit(drv_priv);
+ } else if (hostapd_if_link_remove(iface->bss[0],
+ WPA_IF_AP_BSS,
+ iface->bss[0]->conf->iface,
+ iface->bss[0]->mld_link_id)) {
+ wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
+ iface->bss[0]->conf->iface);
+ }
+#else /* CONFIG_IEEE80211BE */
+ driver->hapd_deinit(drv_priv);
+#endif /* CONFIG_IEEE80211BE */
+ iface->bss[0]->drv_priv = NULL;
+}
+
+
void hostapd_interface_deinit_free(struct hostapd_iface *iface)
{
const struct wpa_driver_ops *driver;
hostapd_interface_deinit(iface);
wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
__func__, driver, drv_priv);
- if (driver && driver->hapd_deinit && drv_priv) {
- if (hostapd_mld_is_first_bss(iface->bss[0]))
- driver->hapd_deinit(drv_priv);
- hostapd_clear_drv_priv(iface->bss[0]);
- }
+ hostapd_cleanup_driver(driver, drv_priv, iface);
hostapd_interface_free(iface);
}
wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
__func__, driver, drv_priv);
+
+ hostapd_cleanup_driver(driver, drv_priv, hapd_iface);
+
if (driver && driver->hapd_deinit && drv_priv) {
- if (hostapd_mld_is_first_bss(hapd_iface->bss[0]))
- driver->hapd_deinit(drv_priv);
for (j = 0; j < hapd_iface->num_bss; j++) {
wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p",
__func__, (int) j,
hapd_iface->bss[j]->drv_priv);
if (hapd_iface->bss[j]->drv_priv == drv_priv) {
- hostapd_clear_drv_priv(hapd_iface->bss[j]);
+ hapd_iface->bss[j]->drv_priv = NULL;
hapd_iface->extended_capa = NULL;
hapd_iface->extended_capa_mask = NULL;
hapd_iface->extended_capa_len = 0;
int (*link_remove)(void *priv, enum wpa_driver_if_type type,
const char *ifname, u8 link_id);
+ /**
+ * is_drv_shared - Check whether the driver interface is shared
+ * @priv: Private driver interface data from init()
+ * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
+ *
+ * Checks whether the driver interface is being used by other partner
+ * BSS(s) or not. This is used to decide whether the driver interface
+ * needs to be deinitilized when one interface is getting deinitialized.
+ *
+ * Returns: true if it is being used or else false.
+ */
+ bool (*is_drv_shared)(void *priv, void *bss_ctx);
+
#ifdef CONFIG_TESTING_OPTIONS
int (*register_frame)(void *priv, u16 type,
const u8 *match, size_t match_len,
#ifdef CONFIG_IEEE80211BE
+
static int driver_nl80211_link_remove(void *priv, enum wpa_driver_if_type type,
const char *ifname, u8 link_id)
{
return 0;
}
+
+
+static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ unsigned int num_bss = 0;
+
+ /* If any other BSS exist, someone else is using this since at this
+ * time, we would have removed all BSSs created by this driver and only
+ * this BSS should be remaining if the driver is not shared by anyone.
+ */
+ for (bss = drv->first_bss; bss; bss = bss->next) {
+ num_bss++;
+ if (num_bss > 1)
+ return true;
+ }
+
+ /* This is the only BSS present */
+ bss = priv;
+
+ /* If only one/no link is there no one is sharing */
+ if (bss->valid_links <= 1)
+ return false;
+
+ /* More than one link means someone is still using. To check if
+ * only 1 bit is set, power of 2 condition can be checked. */
+ if (!(bss->valid_links & (bss->valid_links - 1)))
+ return false;
+
+ return true;
+}
+
#endif /* CONFIG_IEEE80211BE */
.link_add = nl80211_link_add,
#ifdef CONFIG_IEEE80211BE
.link_remove = driver_nl80211_link_remove,
+ .is_drv_shared = nl80211_is_drv_shared,
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_TESTING_OPTIONS
.register_frame = testing_nl80211_register_frame,