diff --git a/os_if/linux/mlme/src/osif_cm_disconnect_rsp.c b/os_if/linux/mlme/src/osif_cm_disconnect_rsp.c index a1c9118cab..931c10ed27 100644 --- a/os_if/linux/mlme/src/osif_cm_disconnect_rsp.c +++ b/os_if/linux/mlme/src/osif_cm_disconnect_rsp.c @@ -151,7 +151,7 @@ osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev, struct vdev_osif_priv *osif_priv = NULL; struct wlan_objmgr_vdev *assoc_vdev = NULL; - if (!wlan_vdev_mlme_is_mlo_vdev(vdev)) { + if (!wlan_vdev_mlme_is_mlo_vdev(vdev) || (link_id != -1)) { osif_cm_indicate_disconnect_result( netdev, reason, ie, ie_len, locally_generated, link_id, gfp); @@ -274,6 +274,10 @@ QDF_STATUS osif_disconnect_handler(struct wlan_objmgr_vdev *vdev, return status; } + /* If disconnect due to ML Reconfig, fill link id */ + if (rsp->req.req.reason_code == REASON_HOST_TRIGGERED_LINK_DELETE) + link_id = wlan_vdev_get_link_id(vdev); + osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_PRE_USERSPACE_UPDATE); osif_cm_indicate_disconnect(vdev, osif_priv->wdev->netdev, ieee80211_reason, diff --git a/qdf/inc/qdf_time.h b/qdf/inc/qdf_time.h index 3e8500a998..ab0fffc56b 100644 --- a/qdf/inc/qdf_time.h +++ b/qdf/inc/qdf_time.h @@ -32,6 +32,8 @@ typedef __qdf_ktime_t qdf_ktime_t; typedef __qdf_timespec_t qdf_timespec_t; typedef __qdf_work_struct_t qdf_work_struct_t; +#define qdf_time_uint_to_ms(tu) (((tu) * 1024) / 1000) + #ifdef ENHANCED_OS_ABSTRACTION /** * qdf_ns_to_ktime - Converts nanoseconds to a qdf_ktime_t object diff --git a/umac/cmn_services/cmn_defs/inc/wlan_cmn_ieee80211.h b/umac/cmn_services/cmn_defs/inc/wlan_cmn_ieee80211.h index 60aeda7f62..b51cedd0ad 100644 --- a/umac/cmn_services/cmn_defs/inc/wlan_cmn_ieee80211.h +++ b/umac/cmn_services/cmn_defs/inc/wlan_cmn_ieee80211.h @@ -793,6 +793,7 @@ enum extn_element_ie { * accordingly. * * @REASON_PROP_START: Start of prop reason code + * @REASON_HOST_TRIGGERED_LINK_DELETE: Dynamic link removal * @REASON_OCI_MISMATCH: Reason OCI Mismatch happens * @REASON_HOST_TRIGGERED_ROAM_FAILURE: Reason host triggered roam failed * @REASON_FW_TRIGGERED_ROAM_FAILURE: Firmware triggered roam failed @@ -884,7 +885,8 @@ enum wlan_reason_code { * REASON_PROP_START and decrease the value of REASON_PROP_START * accordingly. */ - REASON_PROP_START = 65517, + REASON_PROP_START = 65516, + REASON_HOST_TRIGGERED_LINK_DELETE = 65517, REASON_OCI_MISMATCH = 65518, REASON_HOST_TRIGGERED_ROAM_FAILURE = 65519, REASON_FW_TRIGGERED_ROAM_FAILURE = 65520, diff --git a/umac/mlme/include/wlan_mlme_cmn.h b/umac/mlme/include/wlan_mlme_cmn.h index 7a69cf186a..4281606f50 100644 --- a/umac/mlme/include/wlan_mlme_cmn.h +++ b/umac/mlme/include/wlan_mlme_cmn.h @@ -1224,4 +1224,11 @@ mlme_twt_vdev_destroy_notification(struct wlan_objmgr_vdev *vdev) #endif /* WLAN_SUPPORT_TWT && WLAN_TWT_CONV_SUPPORTED */ +/** + * mlme_vdev_reconfig_timer_cb() - vdev ml reconfig timer callback + * @arg: timer argument + * + * Return: None + */ +void mlme_vdev_reconfig_timer_cb(void *arg); #endif diff --git a/umac/mlme/include/wlan_vdev_mlme.h b/umac/mlme/include/wlan_vdev_mlme.h index 43188d3c19..f85ca076c6 100644 --- a/umac/mlme/include/wlan_vdev_mlme.h +++ b/umac/mlme/include/wlan_vdev_mlme.h @@ -696,6 +696,8 @@ enum vdev_start_resp_type { * @mlme_vdev_dfs_cac_wait_notify: callback to notify about CAC state * @mlme_vdev_csa_complete: callback to indicate CSA complete * @mlme_vdev_sta_disconn_start: callback to initiate STA disconnection + * @mlme_vdev_reconfig_timer_complete: callback to process ml reconfing + * operation */ struct vdev_mlme_ops { QDF_STATUS (*mlme_vdev_validate_basic_params)( @@ -774,6 +776,8 @@ struct vdev_mlme_ops { QDF_STATUS (*mlme_vdev_sta_disconn_start)( struct vdev_mlme_obj *vdev_mlme, uint16_t event_data_len, void *event_data); + void (*mlme_vdev_reconfig_timer_complete)( + struct vdev_mlme_obj *vdev_mlme); QDF_STATUS (*mlme_vdev_notify_mlo_sync_wait_entry)( struct vdev_mlme_obj *vdev_mlme); }; @@ -790,8 +794,8 @@ struct vdev_mlme_ops { * @ops: VDEV MLME callback table * @ext_vdev_ptr: VDEV MLME legacy pointer * @reg_tpc_obj: Regulatory transmit power info - * @vdev_rt: VDEV response timer - * @vdev_wakelock: vdev wakelock sub structure + * @ml_reconfig_timer: VDEV ml reconfig timer + * @ml_reconfig_started: Flag to indicate reconfig status for vdev */ struct vdev_mlme_obj { struct vdev_mlme_proto proto; @@ -808,6 +812,8 @@ struct vdev_mlme_obj { struct vdev_mlme_ops *ops; mlme_vdev_ext_t *ext_vdev_ptr; struct reg_tpc_power_info reg_tpc_obj; + qdf_timer_t ml_reconfig_timer; + bool ml_reconfig_started; }; /** diff --git a/umac/mlme/mlme_objmgr/dispatcher/src/wlan_cmn_mlme_main.c b/umac/mlme/mlme_objmgr/dispatcher/src/wlan_cmn_mlme_main.c index a02de874d8..f56c95cc41 100644 --- a/umac/mlme/mlme_objmgr/dispatcher/src/wlan_cmn_mlme_main.c +++ b/umac/mlme/mlme_objmgr/dispatcher/src/wlan_cmn_mlme_main.c @@ -836,3 +836,15 @@ mlme_twt_vdev_destroy_notification(struct wlan_objmgr_vdev *vdev) #endif +void mlme_vdev_reconfig_timer_cb(void *arg) +{ + struct vdev_mlme_obj *vdev_mlme; + + vdev_mlme = (struct vdev_mlme_obj *)arg; + if (!vdev_mlme) + return; + + if ((vdev_mlme->ops) && + vdev_mlme->ops->mlme_vdev_reconfig_timer_complete) + vdev_mlme->ops->mlme_vdev_reconfig_timer_complete(vdev_mlme); +} diff --git a/umac/mlme/mlme_objmgr/dispatcher/src/wlan_vdev_mlme_main.c b/umac/mlme/mlme_objmgr/dispatcher/src/wlan_vdev_mlme_main.c index a43b1904ff..b29d11462b 100644 --- a/umac/mlme/mlme_objmgr/dispatcher/src/wlan_vdev_mlme_main.c +++ b/umac/mlme/mlme_objmgr/dispatcher/src/wlan_vdev_mlme_main.c @@ -116,6 +116,10 @@ static QDF_STATUS mlme_vdev_obj_create_handler(struct wlan_objmgr_vdev *vdev, goto ext_hdl_create_failed; } + qdf_timer_init(NULL, &vdev_mlme->ml_reconfig_timer, + mlme_vdev_reconfig_timer_cb, (void *)(vdev_mlme), + QDF_TIMER_TYPE_WAKE_APPS); + wlan_objmgr_vdev_component_obj_attach((struct wlan_objmgr_vdev *)vdev, WLAN_UMAC_COMP_MLME, (void *)vdev_mlme, @@ -134,6 +138,7 @@ static QDF_STATUS mlme_vdev_obj_create_handler(struct wlan_objmgr_vdev *vdev, return QDF_STATUS_SUCCESS; ext_hdl_post_create_failed: + qdf_timer_free(&vdev_mlme->ml_reconfig_timer); mlme_vdev_ops_ext_hdl_destroy(vdev_mlme); wlan_objmgr_vdev_component_obj_detach(vdev, WLAN_UMAC_COMP_MLME, vdev_mlme); @@ -165,6 +170,7 @@ static QDF_STATUS mlme_vdev_obj_destroy_handler(struct wlan_objmgr_vdev *vdev, return QDF_STATUS_SUCCESS; } + qdf_timer_free(&vdev_mlme->ml_reconfig_timer); wlan_cm_deinit(vdev_mlme); mlme_vdev_sm_destroy(vdev_mlme); mlme_vdev_ops_ext_hdl_destroy(vdev_mlme); diff --git a/umac/mlo_mgr/inc/wlan_mlo_mgr_sta.h b/umac/mlo_mgr/inc/wlan_mlo_mgr_sta.h index 400c18835d..eb80c4d3c5 100644 --- a/umac/mlo_mgr/inc/wlan_mlo_mgr_sta.h +++ b/umac/mlo_mgr/inc/wlan_mlo_mgr_sta.h @@ -561,6 +561,19 @@ void mlo_internal_disconnect_links(struct wlan_objmgr_vdev *vdev); */ void mlo_sta_get_vdev_list(struct wlan_objmgr_vdev *vdev, uint16_t *vdev_count, struct wlan_objmgr_vdev **wlan_vdev_list); + +/** + * mlo_process_ml_reconfig_ie() - process ml reconfig ie for vdev + * @vdev: vdev pointer + * @scan_entry: RootAP scan entry + * @ml_ie: Pointer to ML IE + * @ml_ie_len: Length of ML IE + * + * Return: None + */ +void mlo_process_ml_reconfig_ie(struct wlan_objmgr_vdev *vdev, + struct scan_cache_entry *scan_entry, + uint8_t *ml_ie, qdf_size_t ml_ie_len); #else static inline QDF_STATUS mlo_connect(struct wlan_objmgr_vdev *vdev, @@ -707,5 +720,11 @@ bool mlo_get_keys_saved(struct wlan_objmgr_vdev *vdev, { return false; } + +static inline +void mlo_process_ml_reconfig_ie(struct wlan_objmgr_vdev *vdev, + struct scan_cache_entry *scan_entry, + uint8_t *ml_ie, qdf_size_t ml_ie_len) +{ } #endif #endif diff --git a/umac/mlo_mgr/src/wlan_mlo_mgr_sta.c b/umac/mlo_mgr/src/wlan_mlo_mgr_sta.c index 38b6196378..10d257e65e 100644 --- a/umac/mlo_mgr/src/wlan_mlo_mgr_sta.c +++ b/umac/mlo_mgr/src/wlan_mlo_mgr_sta.c @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include #ifdef WLAN_FEATURE_11BE_MLO static inline void @@ -1911,4 +1914,151 @@ bool mlo_get_keys_saved(struct wlan_objmgr_vdev *vdev, return false; } + +static uint16_t +mlo_get_bcn_interval_by_bssid(struct wlan_objmgr_pdev *pdev, + uint8_t *bssid) +{ + struct scan_filter *scan_filter; + uint16_t bcn_int = 0; + qdf_list_t *list = NULL; + struct scan_cache_node *first_node = NULL; + qdf_list_node_t *cur_node = NULL; + + scan_filter = qdf_mem_malloc(sizeof(*scan_filter)); + if (!scan_filter) + return bcn_int; + + scan_filter->num_of_bssid = 1; + qdf_mem_copy(scan_filter->bssid_list[0].bytes, + bssid, sizeof(struct qdf_mac_addr)); + list = wlan_scan_get_result(pdev, scan_filter); + qdf_mem_free(scan_filter); + + if (!list || (list && !qdf_list_size(list))) { + mlo_debug("scan list empty"); + goto error; + } + + qdf_list_peek_front(list, &cur_node); + first_node = qdf_container_of(cur_node, + struct scan_cache_node, + node); + if (first_node && first_node->entry) + bcn_int = first_node->entry->bcn_int; +error: + if (list) + wlan_scan_purge_results(list); + + return bcn_int; +} + +static void mlo_process_link_remove(struct wlan_objmgr_vdev *vdev, + struct ml_rv_partner_link_info *link_info) +{ + struct vdev_mlme_obj *vdev_mlme = NULL; + struct wlan_objmgr_peer *bss_peer = NULL; + uint16_t bcn_int = 0; + uint16_t tbtt_count = 0; + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) + return; + + if (vdev_mlme->ml_reconfig_started == true) + return; + + bss_peer = wlan_vdev_get_bsspeer(vdev); + if (!bss_peer) + return; + + /* Link delete triggered from AP, + * start timer with tbtt count * beacon interval + */ + tbtt_count = link_info->delete_timer; + bcn_int = mlo_get_bcn_interval_by_bssid( + wlan_vdev_get_pdev(vdev), + wlan_peer_get_macaddr(bss_peer)); + if (!bcn_int) + return; + + vdev_mlme->ml_reconfig_started = true; + qdf_timer_mod(&vdev_mlme->ml_reconfig_timer, + qdf_time_uint_to_ms(tbtt_count * bcn_int)); +} + +void mlo_process_ml_reconfig_ie(struct wlan_objmgr_vdev *vdev, + struct scan_cache_entry *scan_entry, + uint8_t *ml_ie, qdf_size_t ml_ie_len) +{ + struct wlan_objmgr_vdev *co_mld_vdev = NULL; + struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS] = {NULL}; + uint16_t vdev_count = 0; + uint8_t idx = 0; + uint8_t i = 0; + uint8_t link_ix = 0; + struct ml_rv_info reconfig_info = {0}; + uint8_t *ml_rv_ie = NULL; + qdf_size_t ml_rv_ie_len = 0; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!vdev || !mlo_is_mld_sta(vdev)) + return; + + mlo_get_ml_vdev_list(vdev, &vdev_count, wlan_vdev_list); + if (!vdev_count) { + mlo_debug("Number of VDEVs under MLD is reported as 0"); + return; + } + + /* Add code for link add here */ + + /* Processing for ML Reconfig IE */ + if (vdev_count == 1) { + /* Single link MLO, no need to process link delete */ + goto err_release_refs; + } + + status = util_find_mlie_by_variant(ml_ie, + ml_ie_len, + &ml_rv_ie, + &ml_rv_ie_len, + WLAN_ML_VARIANT_RECONFIG); + if (QDF_IS_STATUS_ERROR(status) || !ml_rv_ie) { + mlo_debug("ML IE for reconfig variant not found"); + goto err_release_refs; + } + + status = util_get_rvmlie_persta_link_info(ml_rv_ie, ml_rv_ie_len, + &reconfig_info); + if (QDF_IS_STATUS_ERROR(status)) { + mlo_err("Unable to get persta link info from ML RV IE"); + goto err_release_refs; + } + + if (!reconfig_info.num_links) { + mlo_err("No. of links is 0 in ML reconfig IE"); + goto err_release_refs; + } + + for (idx = 0; idx < vdev_count; idx++) { + co_mld_vdev = wlan_vdev_list[idx]; + if (!co_mld_vdev) { + mlo_debug("VDEV in MLD VDEV list is NULL"); + goto err_release_refs; + } + + link_ix = wlan_vdev_get_link_id(co_mld_vdev); + for (i = 0; i < reconfig_info.num_links; i++) { + if (link_ix == reconfig_info.link_info[i].link_id) + mlo_process_link_remove(co_mld_vdev, + &reconfig_info.link_info[i]); + } + } + +err_release_refs: + + for (i = 0; i < vdev_count; i++) + mlo_release_vdev_ref(wlan_vdev_list[i]); +} #endif