On Fri Apr 12, 2024 at 11:44 AM CEST, kangyang wrote: > From: Kang Yang <quic_kangyang@xxxxxxxxxxx> > > When AP goes down or too far away without indication to STA, beacon miss > will be detected. Then for WCN7850's firmware, it will use roam event > to send beacon miss to host. > > If STA doesn't handle the beacon miss, will keep the fake connection > and unable to roam. > > So add support for WCN7850 to trigger disconnection from AP when > receiving this event from firmware. > > It has to be noted that beacon miss event notification for QCN9274 > to be handled in a separate patch as it uses STA kickout WMI event > to notify beacon miss and the current STA kickout event is processed > as low_ack. > > Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 > > Signed-off-by: Kang Yang <quic_kangyang@xxxxxxxxxxx> > --- > > v3: move INIT_DELAYED_WORK to add_interface(). > v2: rebased on latest tag: ath-202404101413. > > --- > drivers/net/wireless/ath/ath12k/core.h | 2 + > drivers/net/wireless/ath/ath12k/mac.c | 75 +++++++++++++++++++++++++- > drivers/net/wireless/ath/ath12k/mac.h | 2 + > drivers/net/wireless/ath/ath12k/wmi.c | 34 ++++++------ > drivers/net/wireless/ath/ath12k/wmi.h | 3 ++ > 5 files changed, 98 insertions(+), 18 deletions(-) > > diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h > index 397d8c973265..e125efe20dde 100644 > --- a/drivers/net/wireless/ath/ath12k/core.h > +++ b/drivers/net/wireless/ath/ath12k/core.h > @@ -46,6 +46,7 @@ > #define ATH12K_SMBIOS_BDF_EXT_MAGIC "BDF_" > > #define ATH12K_INVALID_HW_MAC_ID 0xFF > +#define ATH12K_CONNECTION_LOSS_HZ (3 * HZ) > #define ATH12K_RX_RATE_TABLE_NUM 320 > #define ATH12K_RX_RATE_TABLE_11AX_NUM 576 > > @@ -275,6 +276,7 @@ struct ath12k_vif { > u32 aid; > u8 bssid[ETH_ALEN]; > struct cfg80211_bitrate_mask bitrate_mask; > + struct delayed_work connection_loss_work; > int num_legacy_stations; > int rtscts_prot_mode; > int txpower; > diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c > index f15dcd75157d..e8ce9b940753 100644 > --- a/drivers/net/wireless/ath/ath12k/mac.c > +++ b/drivers/net/wireless/ath/ath12k/mac.c > @@ -1398,6 +1398,75 @@ static void ath12k_control_beaconing(struct ath12k_vif *arvif, > ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id); > } > > +static void ath12k_mac_handle_beacon_iter(void *data, u8 *mac, > + struct ieee80211_vif *vif) > +{ > + struct sk_buff *skb = data; > + struct ieee80211_mgmt *mgmt = (void *)skb->data; > + struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); > + > + if (vif->type != NL80211_IFTYPE_STATION) > + return; > + > + if (!ether_addr_equal(mgmt->bssid, vif->bss_conf.bssid)) > + return; > + > + cancel_delayed_work(&arvif->connection_loss_work); > +} > + > +void ath12k_mac_handle_beacon(struct ath12k *ar, struct sk_buff *skb) > +{ > + ieee80211_iterate_active_interfaces_atomic(ath12k_ar_to_hw(ar), > + IEEE80211_IFACE_ITER_NORMAL, > + ath12k_mac_handle_beacon_iter, > + skb); > +} > + > +static void ath12k_mac_handle_beacon_miss_iter(void *data, u8 *mac, > + struct ieee80211_vif *vif) > +{ > + u32 *vdev_id = data; > + struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); > + struct ath12k *ar = arvif->ar; > + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); > + > + if (arvif->vdev_id != *vdev_id) > + return; > + > + if (!arvif->is_up) > + return; > + > + ieee80211_beacon_loss(vif); > + > + /* Firmware doesn't report beacon loss events repeatedly. If AP probe > + * (done by mac80211) succeeds but beacons do not resume then it > + * doesn't make sense to continue operation. Queue connection loss work > + * which can be cancelled when beacon is received. > + */ > + ieee80211_queue_delayed_work(hw, &arvif->connection_loss_work, > + ATH12K_CONNECTION_LOSS_HZ); > +} > + > +void ath12k_mac_handle_beacon_miss(struct ath12k *ar, u32 vdev_id) > +{ > + ieee80211_iterate_active_interfaces_atomic(ath12k_ar_to_hw(ar), > + IEEE80211_IFACE_ITER_NORMAL, > + ath12k_mac_handle_beacon_miss_iter, > + &vdev_id); > +} > + > +static void ath12k_mac_vif_sta_connection_loss_work(struct work_struct *work) > +{ > + struct ath12k_vif *arvif = container_of(work, struct ath12k_vif, > + connection_loss_work.work); > + struct ieee80211_vif *vif = arvif->vif; > + > + if (!arvif->is_up) > + return; > + > + ieee80211_connection_loss(vif); > +} > + > static void ath12k_peer_assoc_h_basic(struct ath12k *ar, > struct ieee80211_vif *vif, > struct ieee80211_sta *sta, > @@ -2570,7 +2639,7 @@ static void ath12k_bss_disassoc(struct ath12k *ar, > > arvif->is_up = false; > > - /* TODO: cancel connection_loss_work */ > + cancel_delayed_work(&arvif->connection_loss_work); > } > > static u32 ath12k_mac_get_rate_hw_value(int bitrate) > @@ -6317,6 +6386,8 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, > arvif->vif = vif; > > INIT_LIST_HEAD(&arvif->list); > + INIT_DELAYED_WORK(&arvif->connection_loss_work, > + ath12k_mac_vif_sta_connection_loss_work); > > for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { > arvif->bitrate_mask.control[i].legacy = 0xffffffff; > @@ -6449,6 +6520,8 @@ static void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw, > ar = arvif->ar; > ab = ar->ab; > > + cancel_delayed_work_sync(&arvif->connection_loss_work); > + > mutex_lock(&ar->conf_mutex); > > ath12k_dbg(ab, ATH12K_DBG_MAC, "mac remove interface (vdev %d)\n", > diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h > index 3f5e1be0dff9..bfc655a4dfce 100644 > --- a/drivers/net/wireless/ath/ath12k/mac.h > +++ b/drivers/net/wireless/ath/ath12k/mac.h > @@ -78,4 +78,6 @@ enum ath12k_supported_bw ath12k_mac_mac80211_bw_to_ath12k_bw(enum rate_info_bw b > enum hal_encrypt_type ath12k_dp_tx_get_encrypt_type(u32 cipher); > int ath12k_mac_rfkill_enable_radio(struct ath12k *ar, bool enable); > int ath12k_mac_rfkill_config(struct ath12k *ar); > +void ath12k_mac_handle_beacon(struct ath12k *ar, struct sk_buff *skb); > +void ath12k_mac_handle_beacon_miss(struct ath12k *ar, u32 vdev_id); > #endif > diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c > index a5575ce9eed4..4fe39e920902 100644 > --- a/drivers/net/wireless/ath/ath12k/wmi.c > +++ b/drivers/net/wireless/ath/ath12k/wmi.c > @@ -5927,10 +5927,8 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) > } > } > > - /* TODO: Pending handle beacon implementation > - *if (ieee80211_is_beacon(hdr->frame_control)) > - * ath12k_mac_handle_beacon(ar, skb); > - */ > + if (ieee80211_is_beacon(hdr->frame_control)) > + ath12k_mac_handle_beacon(ar, skb); > > ath12k_dbg(ab, ATH12K_DBG_MGMT, > "event mgmt rx skb %pK len %d ftype %02x stype %02x\n", > @@ -6137,42 +6135,44 @@ static void ath12k_roam_event(struct ath12k_base *ab, struct sk_buff *skb) > { > struct wmi_roam_event roam_ev = {}; > struct ath12k *ar; > + u32 vdev_id; > + u8 roam_reason; > > if (ath12k_pull_roam_ev(ab, skb, &roam_ev) != 0) { > ath12k_warn(ab, "failed to extract roam event"); > return; > } > > + vdev_id = le32_to_cpu(roam_ev.vdev_id); > + roam_reason = u32_get_bits(le32_to_cpu(roam_ev.reason), > + WMI_ROAM_REASON_MASK); > + > ath12k_dbg(ab, ATH12K_DBG_WMI, > - "wmi roam event vdev %u reason 0x%08x rssi %d\n", > - roam_ev.vdev_id, roam_ev.reason, roam_ev.rssi); > + "wmi roam event vdev %u reason %d rssi %d\n", > + vdev_id, roam_reason, roam_ev.rssi); > > rcu_read_lock(); > - ar = ath12k_mac_get_ar_by_vdev_id(ab, le32_to_cpu(roam_ev.vdev_id)); > + ar = ath12k_mac_get_ar_by_vdev_id(ab, vdev_id); > if (!ar) { > - ath12k_warn(ab, "invalid vdev id in roam ev %d", > - roam_ev.vdev_id); > + ath12k_warn(ab, "invalid vdev id in roam ev %d", vdev_id); > rcu_read_unlock(); > return; > } > > - if (le32_to_cpu(roam_ev.reason) >= WMI_ROAM_REASON_MAX) > + if (roam_reason >= WMI_ROAM_REASON_MAX) > ath12k_warn(ab, "ignoring unknown roam event reason %d on vdev %i\n", > - roam_ev.reason, roam_ev.vdev_id); > + roam_reason, vdev_id); > > - switch (le32_to_cpu(roam_ev.reason)) { > + switch (roam_reason) { > case WMI_ROAM_REASON_BEACON_MISS: > - /* TODO: Pending beacon miss and connection_loss_work > - * implementation > - * ath12k_mac_handle_beacon_miss(ar, vdev_id); > - */ > + ath12k_mac_handle_beacon_miss(ar, vdev_id); > break; > case WMI_ROAM_REASON_BETTER_AP: > case WMI_ROAM_REASON_LOW_RSSI: > case WMI_ROAM_REASON_SUITABLE_AP_FOUND: > case WMI_ROAM_REASON_HO_FAILED: > ath12k_warn(ab, "ignoring not implemented roam event reason %d on vdev %i\n", > - roam_ev.reason, roam_ev.vdev_id); > + roam_reason, vdev_id); > break; > } > > diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h > index 78afc94a815d..ebf73ddcadc6 100644 > --- a/drivers/net/wireless/ath/ath12k/wmi.h > +++ b/drivers/net/wireless/ath/ath12k/wmi.h > @@ -4182,6 +4182,9 @@ struct wmi_peer_sta_kickout_event { > struct ath12k_wmi_mac_addr_params peer_macaddr; > } __packed; > > +#define WMI_ROAM_REASON_MASK GENMASK(3, 0) > +#define WMI_ROAM_SUBNET_STATUS_MASK GENMASK(5, 4) > + > enum wmi_roam_reason { > WMI_ROAM_REASON_BETTER_AP = 1, > WMI_ROAM_REASON_BEACON_MISS = 2, > > base-commit: 363e7193eaf258fe7f04e8db560bd8a282a12cd9 LGTM, you can have my reviewed by if needed. Reviewed-by: Nicolas Escande <nico.escande@xxxxxxxxx>