@@ -8359,6 +8359,20 @@ void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer,
*/
void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp);
+/**
+ * cfg80211_cqm_links_state_change_notify - inform CQM of change in AP MLD
+ * links
+ *
+ * @dev: network device
+ * @removed_links: bitmap of links on which connection was removed due to links
+ * being removed by the AP MLD.
+ *
+ * Caller must acquire wdev_lock, therefore must only be called from sleepable
+ * driver context!
+ */
+void cfg80211_cqm_links_state_change_notify(struct net_device *dev,
+ u16 removed_links);
+
/**
* __cfg80211_radar_event - radar detection event
* @wiphy: the wiphy
@@ -5350,6 +5350,8 @@ enum nl80211_ps_state {
* loss event
* @NL80211_ATTR_CQM_RSSI_LEVEL: the RSSI value in dBm that triggered the
* RSSI threshold event.
+ * @NL80211_ATTR_CQM_REMOVED_LINKS: bitmap of links on which connection was
+ * removed due to links being removed by the AP MLD.
* @__NL80211_ATTR_CQM_AFTER_LAST: internal
* @NL80211_ATTR_CQM_MAX: highest key attribute
*/
@@ -5364,6 +5366,7 @@ enum nl80211_attr_cqm {
NL80211_ATTR_CQM_TXE_INTVL,
NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
NL80211_ATTR_CQM_RSSI_LEVEL,
+ NL80211_ATTR_CQM_REMOVED_LINKS,
/* keep last */
__NL80211_ATTR_CQM_AFTER_LAST,
@@ -19108,6 +19108,49 @@ void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp)
}
EXPORT_SYMBOL(cfg80211_cqm_beacon_loss_notify);
+void cfg80211_cqm_links_state_change_notify(struct net_device *dev,
+ u16 removed_links)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct sk_buff *msg;
+ unsigned long removed = removed_links;
+ u8 link_id;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ trace_cfg80211_cqm_links_state_change_notify(dev, removed_links);
+
+ if (!removed_links)
+ return;
+
+ WARN_ON((wdev->valid_links & removed_links) != removed_links);
+ wdev->valid_links &= ~removed_links;
+
+ for_each_set_bit(link_id, &removed, IEEE80211_MLD_MAX_NUM_LINKS) {
+ if (!wdev->links[link_id].client.current_bss)
+ continue;
+
+ cfg80211_unhold_bss(wdev->links[link_id].client.current_bss);
+ cfg80211_put_bss(wdev->wiphy,
+ &wdev->links[link_id].client.current_bss->pub);
+ wdev->links[link_id].client.current_bss = NULL;
+ }
+
+ msg = cfg80211_prepare_cqm(dev, NULL, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ if (nla_put_u16(msg, NL80211_ATTR_CQM_REMOVED_LINKS, removed_links))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, GFP_KERNEL);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_links_state_change_notify);
+
static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid,
const u8 *replay_ctr, gfp_t gfp)
@@ -3966,6 +3966,22 @@ TRACE_EVENT(rdev_set_hw_timestamp,
__entry->enable)
);
+TRACE_EVENT(cfg80211_cqm_links_state_change_notify,
+ TP_PROTO(struct net_device *netdev,
+ u16 removed_links),
+ TP_ARGS(netdev, removed_links),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ __field(u16, removed_links)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ __entry->removed_links = removed_links;
+ ),
+ TP_printk(NETDEV_PR_FMT ", removed_links=0x%x",
+ NETDEV_PR_ARG, __entry->removed_links)
+);
+
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH