}
+static void qca_nl80211_link_reconfig_event(struct wpa_driver_nl80211_data *drv,
+ u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_MAX + 1];
+ u16 removed_links;
+ u8 *ap_mld;
+ int i;
+
+ if (!data)
+ return;
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_AP_MLD_ADDR])
+ return;
+
+ ap_mld = nla_data(tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_AP_MLD_ADDR]);
+ wpa_printf(MSG_DEBUG, "nl80211: AP MLD address " MACSTR
+ " received in link reconfig event", MAC2STR(ap_mld));
+ if (!drv->sta_mlo_info.valid_links ||
+ os_memcmp(drv->sta_mlo_info.ap_mld_addr, ap_mld, ETH_ALEN) != 0) {
+ if (drv->pending_link_reconfig_data == data) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Drop pending link reconfig event since AP MLD not matched even after new connect/roam event");
+ os_free(drv->pending_link_reconfig_data);
+ drv->pending_link_reconfig_data = NULL;
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Cache new link reconfig event till next connect/roam event");
+ if (drv->pending_link_reconfig_data) {
+ wpa_printf(MSG_DEBUG, "nl80211: Override old link reconfig event data");
+ os_free(drv->pending_link_reconfig_data);
+ }
+ drv->pending_link_reconfig_data = os_memdup(data, len);
+ if (!drv->pending_link_reconfig_data)
+ return;
+ drv->pending_link_reconfig_data_len = len;
+ return;
+ }
+
+ if (!tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_REMOVED_LINKS])
+ return;
+ removed_links = nla_get_u16(
+ tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_REMOVED_LINKS]);
+
+ drv->sta_mlo_info.valid_links &= ~removed_links;
+
+ /*
+ * Set default BSSID to the BSSID of the lowest link ID of remaining
+ * links when the link used for (re)association is removed.
+ */
+ if (removed_links & BIT(drv->sta_mlo_info.assoc_link_id)) {
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(drv->sta_mlo_info.valid_links & BIT(i)))
+ continue;
+
+ os_memcpy(drv->bssid, drv->sta_mlo_info.links[i].bssid,
+ ETH_ALEN);
+ drv->sta_mlo_info.assoc_link_id = i;
+ break;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Removed MLO links bitmap: 0x%x",
+ removed_links);
+
+ wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, NULL);
+}
+
+
static void
nl80211_parse_qca_vendor_mlo_link_info(struct driver_sta_mlo_info *mlo,
struct nlattr *mlo_links)
drv->pending_t2lm_data_len);
else
drv->sta_mlo_info.default_map = true;
+
+ if (drv->pending_link_reconfig_data)
+ qca_nl80211_link_reconfig_event(
+ drv, drv->pending_link_reconfig_data,
+ drv->pending_link_reconfig_data_len);
#endif /* CONFIG_DRIVER_NL80211_QCA */
}
case QCA_NL80211_VENDOR_SUBCMD_TID_TO_LINK_MAP:
qca_nl80211_tid_to_link_map_event(drv, data, len);
break;
+ case QCA_NL80211_VENDOR_SUBCMD_LINK_RECONFIG:
+ qca_nl80211_link_reconfig_event(drv, data, len);
+ break;
#endif /* CONFIG_DRIVER_NL80211_QCA */
default:
wpa_printf(MSG_DEBUG,
}
+static void wpas_link_reconfig(struct wpa_supplicant *wpa_s)
+{
+ u8 bssid[ETH_ALEN];
+
+ if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+ wpa_printf(MSG_ERROR, "LINK_RECONFIG: Failed to get BSSID");
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ return;
+ }
+
+ if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
+ os_memcpy(wpa_s->bssid, bssid, ETH_ALEN);
+ wpa_supplicant_update_current_bss(wpa_s, wpa_s->bssid);
+ wpas_notify_bssid_changed(wpa_s);
+ }
+
+ if (wpa_drv_get_mlo_info(wpa_s) < 0) {
+ wpa_printf(MSG_ERROR,
+ "LINK_RECONFIG: Failed to get MLO connection info");
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ return;
+ }
+
+ if (wpa_sm_set_ml_info(wpa_s)) {
+ wpa_printf(MSG_ERROR,
+ "LINK_RECONFIG: Failed to set MLO connection info to wpa_sm");
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_LINK_RECONFIG "valid_links=0x%x",
+ wpa_s->valid_links);
+}
+
+
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
wpas_event_deauth(wpa_s,
data ? &data->deauth_info : NULL);
break;
+ case EVENT_LINK_RECONFIG:
+ wpas_link_reconfig(wpa_s);
+ break;
case EVENT_MICHAEL_MIC_FAILURE:
wpa_supplicant_event_michael_mic_failure(wpa_s, data);
break;