From 15c38e2b3e192923b5962430cf0d6eba757aba09 Mon Sep 17 00:00:00 2001 From: Liangwei Dong Date: Wed, 7 Jun 2023 19:05:10 +0800 Subject: [PATCH] qcacld-3.0: Handle nlink force mode by link id bitmap Add APIs to handle force link active/inactive by link id bitmap. For N-link mlo STA (N > 2), some link may have no vdev attached, we have to use link id bitmap to do force active/inactive. If the service WMI_SERVICE_N_LINK_MLO_SUPPORT is not enabled, vdev based handler will be invoked. Change-Id: I1c48a7227185923a38f3e2b75f1274df9a186437 CRs-Fixed: 3525174 --- .../policy_mgr/inc/wlan_policy_mgr_api.h | 89 ++ .../inc/wlan_policy_mgr_public_struct.h | 17 + .../src/wlan_policy_mgr_get_set_utils.c | 247 +++- .../policy_mgr/src/wlan_policy_mgr_i.h | 17 - .../mlme/mlo_mgr/inc/wlan_mlo_link_force.h | 78 + .../mlme/mlo_mgr/src/wlan_mlo_link_force.c | 1267 +++++++++++++++++ 6 files changed, 1664 insertions(+), 51 deletions(-) diff --git a/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h index 29d7c1756f..22f236a62c 100644 --- a/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h +++ b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h @@ -4778,6 +4778,95 @@ bool policy_mgr_is_mlo_sta_disconnected(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id); #ifdef WLAN_FEATURE_11BE_MLO +/* + * policy_mgr_is_ml_sta_links_in_mcc() - Check ML links are in MCC or not + * @psoc: psoc ctx + * @ml_freq_lst: ML STA freq list + * @ml_vdev_lst: ML STA vdev id list + * @ml_linkid_lst: ML STA link id list + * @num_ml_sta: Number of total ML STA links + * @affected_linkid_bitmap: link id bitmap which home channels are in MCC + * with each other + * + * Return: true if ML link in MCC else false + */ +bool +policy_mgr_is_ml_sta_links_in_mcc(struct wlan_objmgr_psoc *psoc, + qdf_freq_t *ml_freq_lst, + uint8_t *ml_vdev_lst, + uint8_t *ml_linkid_lst, + uint8_t num_ml_sta, + uint32_t *affected_linkid_bitmap); + +/** + * policy_mgr_is_vdev_high_tput_or_low_latency() - Check vdev has + * high througput or low latency flag + * @psoc: PSOC object information + * @vdev_id: vdev id + * + * Return: true if vdev has high throughput or low latency flag + */ +bool +policy_mgr_is_vdev_high_tput_or_low_latency(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + +/** + * policy_mgr_check_2ghz_only_sap_affected_link() - Check force inactive + * link is needed for 2.4 GHz only sap + * @psoc: PSOC object information + * @sap_vdev_id: sap vdev id + * @sap_ch_freq: sap channel frequency + * @ml_ch_freq_num: ML STA link num + * @ml_freq_lst: ML STA link frequency list + * + * Return: true if 2.4 GHz only sap present and need to force inactive + * ML link + */ +bool +policy_mgr_check_2ghz_only_sap_affected_link( + struct wlan_objmgr_psoc *psoc, + uint8_t sap_vdev_id, + qdf_freq_t sap_ch_freq, + uint8_t ml_ch_freq_num, + qdf_freq_t *ml_freq_lst); + +/** + * policy_mgr_vdev_is_force_inactive() - Check force inactive or not + * for the vdev id + * @psoc: PSOC object information + * @vdev_id: vdev id + * + * Return: true if the vdev is in force inactive + */ +bool policy_mgr_vdev_is_force_inactive(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + +/** + * policy_mgr_get_legacy_conn_info() - Get legacy connection info + * @psoc: PSOC object information + * @vdev_lst: vdev id list + * @freq_lst: channel frequency list + * @mode_lst: vdev mode list + * @lst_sz: array size of above parameters + * + * This API will return the legacy STA/SAP/P2P connection info. + * If a connection want to avoid MCC with ML STA, that connection + * will be put in head of array list. And in 3 Port concurrency + * case (ML STA + 2 legacy Connections), usually we can only meet + * the high priority connection's MCC avoidance, so this API will + * return sorted lists based on the priority. Right now we don't + * clear requirement on which legacy interface has higher priority, + * here we follow this order: STA, SAP, P2P. + * + * Return: number of legacy connection count + */ +uint8_t +policy_mgr_get_legacy_conn_info(struct wlan_objmgr_psoc *psoc, + uint8_t *vdev_lst, + qdf_freq_t *freq_lst, + enum policy_mgr_con_mode *mode_lst, + uint8_t lst_sz); + /* * policy_mgr_get_ml_sta_info_psoc() - Get number of ML STA vdev ids and * freq list diff --git a/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_public_struct.h b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_public_struct.h index 63596bdfeb..9b46bd0b83 100644 --- a/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_public_struct.h +++ b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_public_struct.h @@ -1730,6 +1730,23 @@ struct sta_ap_intf_check_work_ctx { uint8_t nan_force_scc_in_progress; }; +/** + * union conc_ext_flag - extended flags for concurrency check + * + * @mlo: the new connection is MLO + * @mlo_link_assoc_connected: the new connection is secondary MLO link and + * the corresponding assoc link is connected + * @value: uint32 value for extended flags + */ +union conc_ext_flag { + struct { + uint32_t mlo: 1; + uint32_t mlo_link_assoc_connected: 1; + }; + + uint32_t value; +}; + /** * enum indoor_conc_update_type - Indoor concurrency update type * @CONNECT: On a new STA connection diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_get_set_utils.c b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_get_set_utils.c index 7bd19f3748..52c9ed6baa 100644 --- a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_get_set_utils.c +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_get_set_utils.c @@ -5554,6 +5554,184 @@ static bool policy_mgr_is_6g_channel_allowed( } #ifdef WLAN_FEATURE_11BE_MLO +static bool policy_mgr_is_acs_2ghz_only_sap(struct wlan_objmgr_psoc *psoc, + uint8_t sap_vdev_id) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t acs_band = QCA_ACS_MODE_IEEE80211ANY; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + if (pm_ctx->hdd_cbacks.wlan_get_sap_acs_band) + pm_ctx->hdd_cbacks.wlan_get_sap_acs_band(psoc, + sap_vdev_id, + &acs_band); + + if (acs_band == QCA_ACS_MODE_IEEE80211B || + acs_band == QCA_ACS_MODE_IEEE80211G) + return true; + + return false; +} + +bool policy_mgr_vdev_is_force_inactive(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + bool force_inactive = false; + uint8_t conn_index; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + /* Get disabled link info as well and keep it at last */ + for (conn_index = 0; conn_index < MAX_NUMBER_OF_DISABLE_LINK; + conn_index++) { + if (pm_disabled_ml_links[conn_index].in_use && + pm_disabled_ml_links[conn_index].mode == PM_STA_MODE && + pm_disabled_ml_links[conn_index].vdev_id == vdev_id) { + force_inactive = true; + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return force_inactive; +} + +/* MCC avoidance priority value for different legacy connection type. + * Internal macro, not expected used other code. + * Bigger value have higher priority. + */ +#define PRIORITY_STA 3 +#define PRIORITY_SAP 2 +#define PRIORITY_P2P 1 +#define PRIORITY_OTHER 0 + +uint8_t +policy_mgr_get_legacy_conn_info(struct wlan_objmgr_psoc *psoc, + uint8_t *vdev_lst, + qdf_freq_t *freq_lst, + enum policy_mgr_con_mode *mode_lst, + uint8_t lst_sz) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint8_t conn_index, j = 0, i, k, n; + struct wlan_objmgr_vdev *vdev; + uint8_t vdev_id; + uint8_t has_priority[MAX_NUMBER_OF_CONC_CONNECTIONS]; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return 0; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (j >= lst_sz) + break; + if (!pm_conc_connection_list[conn_index].in_use) + continue; + vdev_id = pm_conc_connection_list[conn_index].vdev_id; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + pm_ctx->psoc, vdev_id, WLAN_POLICY_MGR_ID); + if (!vdev) { + policy_mgr_err("invalid vdev for id %d", vdev_id); + continue; + } + + if (wlan_vdev_mlme_is_mlo_vdev(vdev) && + pm_conc_connection_list[conn_index].mode == + PM_STA_MODE) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_POLICY_MGR_ID); + continue; + } + if (pm_conc_connection_list[conn_index].mode != + PM_STA_MODE && + pm_conc_connection_list[conn_index].mode != + PM_SAP_MODE && + pm_conc_connection_list[conn_index].mode != + PM_P2P_CLIENT_MODE && + pm_conc_connection_list[conn_index].mode != + PM_P2P_GO_MODE) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_POLICY_MGR_ID); + continue; + } + + /* Set mcc avoidance priority value. The bigger value + * have higher priority to avoid MCC. In 3 Port concurrency + * case, usually we can only meet the higher priority intf's + * MCC avoidance by force inactive link. + */ + if (pm_conc_connection_list[conn_index].mode == PM_STA_MODE) + has_priority[j] = PRIORITY_STA; + else if (pm_conc_connection_list[conn_index].mode == + PM_SAP_MODE && + policy_mgr_is_acs_2ghz_only_sap(psoc, vdev_id)) + has_priority[j] = PRIORITY_SAP; + else if ((pm_conc_connection_list[conn_index].mode == + PM_P2P_CLIENT_MODE || + pm_conc_connection_list[conn_index].mode == + PM_P2P_GO_MODE) && + policy_mgr_is_vdev_high_tput_or_low_latency( + psoc, vdev_id)) + has_priority[j] = PRIORITY_P2P; + else + has_priority[j] = PRIORITY_OTHER; + + vdev_lst[j] = vdev_id; + freq_lst[j] = pm_conc_connection_list[conn_index].freq; + mode_lst[j] = pm_conc_connection_list[conn_index].mode; + policy_mgr_debug("vdev %d freq %d mode %s pri %d", + vdev_id, freq_lst[j], + device_mode_to_string(mode_lst[j]), + has_priority[j]); + j++; + wlan_objmgr_vdev_release_ref(vdev, WLAN_POLICY_MGR_ID); + } + /* sort the list based on priority */ + for (i = 0; i < j; i++) { + uint8_t tmp_vdev_lst; + qdf_freq_t tmp_freq_lst; + enum policy_mgr_con_mode tmp_mode_lst; + + n = i; + for (k = i + 1; k < j; k++) { + if (has_priority[n] < has_priority[k]) + n = k; + else if ((has_priority[n] == has_priority[k]) && + (vdev_lst[n] > vdev_lst[k])) + n = k; + } + if (n == i) + continue; + tmp_vdev_lst = vdev_lst[i]; + tmp_freq_lst = freq_lst[i]; + tmp_mode_lst = mode_lst[i]; + + vdev_lst[i] = vdev_lst[n]; + freq_lst[i] = freq_lst[n]; + mode_lst[i] = mode_lst[n]; + + vdev_lst[n] = tmp_vdev_lst; + freq_lst[n] = tmp_freq_lst; + mode_lst[n] = tmp_mode_lst; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return j; +} + static void policy_mgr_fill_ml_active_link_vdev_bitmap(struct mlo_link_set_active_req *req, uint8_t *mlo_vdev_lst, @@ -6731,7 +6909,7 @@ policy_mgr_is_mode_p2p_sap(enum policy_mgr_con_mode mode) (mode == PM_P2P_GO_MODE); } -static bool +bool policy_mgr_is_vdev_high_tput_or_low_latency(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) { @@ -6750,31 +6928,7 @@ policy_mgr_is_vdev_high_tput_or_low_latency(struct wlan_objmgr_psoc *psoc, return is_vdev_ll_ht; } -static bool policy_mgr_is_acs_2ghz_only_sap(struct wlan_objmgr_psoc *psoc, - uint8_t sap_vdev_id) -{ - struct policy_mgr_psoc_priv_obj *pm_ctx; - uint32_t acs_band = QCA_ACS_MODE_IEEE80211ANY; - - pm_ctx = policy_mgr_get_context(psoc); - if (!pm_ctx) { - policy_mgr_err("Invalid Context"); - return false; - } - - if (pm_ctx->hdd_cbacks.wlan_get_sap_acs_band) - pm_ctx->hdd_cbacks.wlan_get_sap_acs_band(psoc, - sap_vdev_id, - &acs_band); - - if (acs_band == QCA_ACS_MODE_IEEE80211B || - acs_band == QCA_ACS_MODE_IEEE80211G) - return true; - - return false; -} - -static bool +bool policy_mgr_check_2ghz_only_sap_affected_link( struct wlan_objmgr_psoc *psoc, uint8_t sap_vdev_id, @@ -6955,27 +7109,49 @@ policy_mgr_get_ml_sta_and_p2p_cli_go_sap_info( * @psoc: psoc ctx * @ml_freq_lst: ML STA freq list * @ml_vdev_lst: ML STA vdev id list + * @ml_linkid_lst: ML STA link id list * @num_ml_sta: Number of total ML STA links + * @affected_linkid_bitmap: link id bitmap which home channels are in MCC + * with each other * * Return: true if ML link in MCC else false */ -static bool +bool policy_mgr_is_ml_sta_links_in_mcc(struct wlan_objmgr_psoc *psoc, qdf_freq_t *ml_freq_lst, uint8_t *ml_vdev_lst, - uint8_t num_ml_sta) + uint8_t *ml_linkid_lst, + uint8_t num_ml_sta, + uint32_t *affected_linkid_bitmap) { uint8_t i, j; + uint32_t link_id_bitmap; for (i = 0; i < num_ml_sta; i++) { + link_id_bitmap = 0; + if (ml_linkid_lst) + link_id_bitmap = 1 << ml_linkid_lst[i]; for (j = i + 1; j < num_ml_sta; j++) { if (ml_freq_lst[i] != ml_freq_lst[j] && policy_mgr_2_freq_always_on_same_mac( psoc, ml_freq_lst[i], ml_freq_lst[j])) { - policy_mgr_debug("vdev %d and %d are in MCC with freq %d and freq %d", - ml_vdev_lst[i], ml_vdev_lst[j], - ml_freq_lst[i], - ml_freq_lst[j]); + if (ml_vdev_lst) + policy_mgr_debug("vdev %d and %d are in MCC with freq %d and freq %d", + ml_vdev_lst[i], + ml_vdev_lst[j], + ml_freq_lst[i], + ml_freq_lst[j]); + if (ml_linkid_lst) { + link_id_bitmap |= 1 << ml_linkid_lst[j]; + policy_mgr_debug("link %d and %d are in MCC with freq %d and freq %d", + ml_linkid_lst[i], + ml_linkid_lst[j], + ml_freq_lst[i], + ml_freq_lst[j]); + if (affected_linkid_bitmap) + *affected_linkid_bitmap = + link_id_bitmap; + } return true; } } @@ -7022,7 +7198,9 @@ policy_mgr_handle_mcc_ml_sta(struct wlan_objmgr_psoc *psoc, } if (!policy_mgr_is_ml_sta_links_in_mcc(psoc, ml_freq_lst, - ml_sta_vdev_lst, num_ml_sta)) + ml_sta_vdev_lst, NULL, + num_ml_sta, + NULL)) return QDF_STATUS_E_FAILURE; /* @@ -7067,7 +7245,8 @@ policy_mgr_sta_ml_link_enable_allowed(struct wlan_objmgr_psoc *psoc, if (!num_disabled_ml_sta || num_ml_sta < 2) return false; if (policy_mgr_is_ml_sta_links_in_mcc(psoc, ml_freq_lst, ml_vdev_lst, - num_ml_sta)) + NULL, num_ml_sta, + NULL)) return false; /* Disabled link is at the last index */ disabled_link_vdev_id = ml_vdev_lst[num_ml_sta - 1]; diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_i.h b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_i.h index b773b4b402..e0476143b0 100644 --- a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_i.h +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_i.h @@ -453,23 +453,6 @@ struct policy_mgr_mac_ss_bw_info { bool support_6ghz_band; }; -/** - * union conc_ext_flag - extended flags for concurrency check - * - * @mlo: the new connection is MLO - * @mlo_link_assoc_connected: the new connection is secondary MLO link and - * the corresponding assoc link is connected - * @value: uint32 value for extended flags - */ -union conc_ext_flag { - struct { - uint32_t mlo: 1; - uint32_t mlo_link_assoc_connected: 1; - }; - - uint32_t value; -}; - #ifdef WLAN_FEATURE_SR /** * policy_mgr_get_same_mac_conc_sr_status() - Function returns value of INI diff --git a/components/umac/mlme/mlo_mgr/inc/wlan_mlo_link_force.h b/components/umac/mlme/mlo_mgr/inc/wlan_mlo_link_force.h index 81c8abec37..9efae820e3 100644 --- a/components/umac/mlme/mlo_mgr/inc/wlan_mlo_link_force.h +++ b/components/umac/mlme/mlo_mgr/inc/wlan_mlo_link_force.h @@ -23,6 +23,40 @@ #include #include +/** + * enum ml_nlink_change_event_type - Ml link state change trigger event + * @ml_nlink_link_switch_start_evt: link switch start + * @ml_nlink_link_switch_completion_evt: link switch done + * @ml_nlink_roam_sync_start_evt: roam sync start + * @ml_nlink_roam_sync_completion_evt: roam sync completion + * @ml_nlink_connect_start_evt: STA/CLI connect start + * @ml_nlink_connect_completion_evt: STA/CLI connect completion + * @ml_nlink_disconnect_start_evt: STA/CLI disconnect start + * @ml_nlink_disconnect_completion_evt: STA/CLI disconnect completion + * @ml_nlink_ap_started_evt: SAP/GO bss started + * @ml_nlink_ap_stopped_evt: SAP/GO bss stopped + * @ml_nlink_connection_updated_evt: connection home channel changed + */ +enum ml_nlink_change_event_type { + ml_nlink_link_switch_start_evt, + ml_nlink_link_switch_completion_evt, + ml_nlink_roam_sync_start_evt, + ml_nlink_roam_sync_completion_evt, + ml_nlink_connect_start_evt, + ml_nlink_connect_completion_evt, + ml_nlink_disconnect_start_evt, + ml_nlink_disconnect_completion_evt, + ml_nlink_ap_started_evt, + ml_nlink_ap_stopped_evt, + ml_nlink_connection_updated_evt, +}; + +/** + * struct ml_nlink_change_event - connection change event data struct + */ +struct ml_nlink_change_event { +}; + #ifdef WLAN_FEATURE_11BE_MLO static inline const char *force_mode_to_string(uint32_t mode) { @@ -48,6 +82,41 @@ static inline const char *force_mode_to_string(uint32_t mode) (_force_state)->force_active_num_bitmap, \ ##args); +static inline const char *link_evt_to_string(uint32_t evt) +{ + switch (evt) { + CASE_RETURN_STRING(ml_nlink_link_switch_start_evt); + CASE_RETURN_STRING(ml_nlink_link_switch_completion_evt); + CASE_RETURN_STRING(ml_nlink_roam_sync_start_evt); + CASE_RETURN_STRING(ml_nlink_roam_sync_completion_evt); + CASE_RETURN_STRING(ml_nlink_connect_start_evt); + CASE_RETURN_STRING(ml_nlink_connect_completion_evt); + CASE_RETURN_STRING(ml_nlink_disconnect_start_evt); + CASE_RETURN_STRING(ml_nlink_disconnect_completion_evt); + CASE_RETURN_STRING(ml_nlink_ap_started_evt); + CASE_RETURN_STRING(ml_nlink_ap_stopped_evt); + CASE_RETURN_STRING(ml_nlink_connection_updated_evt); + default: + return "Unknown"; + } +}; + +/** + * ml_nlink_conn_change_notify() - Notify connection state + * change to force link module + * @psoc: pointer to pdev + * @vdev_id: vdev id + * @evt: event type + * @data: event data + * + * Return: QDF_STATUS_SUCCESS in the case of success + */ +QDF_STATUS +ml_nlink_conn_change_notify(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum ml_nlink_change_event_type evt, + struct ml_nlink_change_event *data); + /** * ml_nlink_convert_linkid_bitmap_to_vdev_bitmap() - convert link * id bitmap to vdev id bitmap @@ -200,5 +269,14 @@ ml_nlink_get_curr_force_state(struct wlan_objmgr_psoc *psoc, void ml_nlink_clr_force_state(struct wlan_objmgr_psoc *psoc, struct wlan_objmgr_vdev *vdev); +#else +static inline QDF_STATUS +ml_nlink_conn_change_notify(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum ml_nlink_change_event_type evt, + struct ml_nlink_change_event *data) +{ + return QDF_STATUS_SUCCESS; +} #endif #endif diff --git a/components/umac/mlme/mlo_mgr/src/wlan_mlo_link_force.c b/components/umac/mlme/mlo_mgr/src/wlan_mlo_link_force.c index a5aee8997b..700893f908 100644 --- a/components/umac/mlme/mlo_mgr/src/wlan_mlo_link_force.c +++ b/components/umac/mlme/mlo_mgr/src/wlan_mlo_link_force.c @@ -19,6 +19,10 @@ */ #include "wlan_mlo_link_force.h" #include "wlan_mlo_mgr_sta.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_cm_roam_public_struct.h" +#include "wlan_cm_roam_api.h" +#include "wlan_mlo_mgr_roam.h" void ml_nlink_convert_linkid_bitmap_to_vdev_bitmap( @@ -327,3 +331,1266 @@ ml_nlink_set_curr_force_inactive_num_state(struct wlan_objmgr_psoc *psoc, link_num, link_bitmap); mlo_dev_lock_release(mlo_dev_ctx); } + +/** + * ml_nlink_get_affect_ml_sta() - Get ML STA whose link can be + * force inactive + * @psoc: PSOC object information + * + * At present we only support one ML STA. so ml_nlink_get_affect_ml_sta + * is invoked to get one ML STA vdev from policy mgr table. + * In future if ML STA+ML STA supported, we may need to extend it + * to find one ML STA which is required to force inactve/active. + * + * Return: vdev object + */ +static struct wlan_objmgr_vdev * +ml_nlink_get_affect_ml_sta(struct wlan_objmgr_psoc *psoc) +{ + uint8_t num_ml_sta = 0, num_disabled_ml_sta = 0; + uint8_t ml_sta_vdev_lst[MAX_NUMBER_OF_CONC_CONNECTIONS] = {0}; + qdf_freq_t ml_freq_lst[MAX_NUMBER_OF_CONC_CONNECTIONS] = {0}; + struct wlan_objmgr_vdev *vdev; + + policy_mgr_get_ml_sta_info_psoc(psoc, &num_ml_sta, + &num_disabled_ml_sta, + ml_sta_vdev_lst, ml_freq_lst, NULL, + NULL, NULL); + if (!num_ml_sta || num_ml_sta > MAX_NUMBER_OF_CONC_CONNECTIONS) { + mlo_debug("ml sta num is %d", num_ml_sta); + return NULL; + } + if (num_ml_sta > MAX_NUMBER_OF_CONC_CONNECTIONS) { + mlo_debug("unexpected num_ml_sta %d", num_ml_sta); + return NULL; + } + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + psoc, ml_sta_vdev_lst[0], + WLAN_MLO_MGR_ID); + if (!vdev) { + mlo_err("invalid vdev for id %d", ml_sta_vdev_lst[0]); + return NULL; + } + + return vdev; +} + +static bool ml_is_nlink_service_supported(void) +{ + /*todo: check WMI_SERVICE_N_LINK_MLO_SUPPORT service bit */ + return false; +} + +/* Exclude AP removed link */ +#define NLINK_EXCLUDE_REMOVED_LINK 0x01 +/* Include AP removed link only, can't work with other flags */ +#define NLINK_INCLUDE_REMOVED_LINK_ONLY 0x02 +/* Exclude QUITE link */ +#define NLINK_EXCLUDE_QUIET_LINK 0x04 + +/** + * ml_nlink_get_link_info() - Get ML STA link info + * @psoc: PSOC object information + * @vdev: ml sta vdev object + * @flag: flag NLINK_* to specify what links should be returned + * @ml_num_link_sz: input array size of ml_link_info and + * other parameters. + * @ml_link_info: ml link info array + * @ml_freq_lst: channel frequency list + * @ml_vdev_lst: vdev id list + * @ml_linkid_lst: link id list + * @ml_num_link: num of links + * @ml_link_bitmap: link bitmaps. + * + * Return: void + */ +static void ml_nlink_get_link_info(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + uint8_t flag, + uint8_t ml_num_link_sz, + struct ml_link_info *ml_link_info, + qdf_freq_t *ml_freq_lst, + uint8_t *ml_vdev_lst, + uint8_t *ml_linkid_lst, + uint8_t *ml_num_link, + uint32_t *ml_link_bitmap) +{ + struct wlan_mlo_dev_context *mlo_dev_ctx; + struct wlan_mlo_sta *sta_ctx; + uint8_t i, num_link = 0; + uint32_t link_bitmap = 0; + uint16_t link_id; + uint8_t vdev_id; + + *ml_num_link = 0; + *ml_link_bitmap = 0; + qdf_mem_zero(ml_link_info, sizeof(*ml_link_info) * ml_num_link_sz); + qdf_mem_zero(ml_freq_lst, sizeof(*ml_freq_lst) * ml_num_link_sz); + qdf_mem_zero(ml_linkid_lst, sizeof(*ml_linkid_lst) * ml_num_link_sz); + qdf_mem_zero(ml_vdev_lst, sizeof(*ml_vdev_lst) * ml_num_link_sz); + + mlo_dev_ctx = wlan_vdev_get_mlo_dev_ctx(vdev); + if (!mlo_dev_ctx || !mlo_dev_ctx->sta_ctx) { + mlo_err("mlo_ctx or sta_ctx null"); + return; + } + + mlo_dev_lock_acquire(mlo_dev_ctx); + sta_ctx = mlo_dev_ctx->sta_ctx; + + link_bitmap = 0; + for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { + /*todo: add standby link */ + if (!mlo_dev_ctx->wlan_vdev_list[i]) + continue; + if (!qdf_test_bit(i, sta_ctx->wlan_connected_links)) + continue; + + if (!wlan_cm_is_vdev_connected( + mlo_dev_ctx->wlan_vdev_list[i])) { + mlo_debug("Vdev id %d is not in connected state", + wlan_vdev_get_id( + mlo_dev_ctx->wlan_vdev_list[i])); + continue; + } + + vdev_id = wlan_vdev_get_id(mlo_dev_ctx->wlan_vdev_list[i]); + link_id = wlan_vdev_get_link_id( + mlo_dev_ctx->wlan_vdev_list[i]); + if (link_id >= MAX_MLO_LINK_ID) { + mlo_debug("invalid link id %x for vdev %d", + link_id, vdev_id); + continue; + } + + if ((flag & NLINK_EXCLUDE_REMOVED_LINK) && + wlan_get_vdev_link_removed_flag_by_vdev_id( + psoc, vdev_id)) { + mlo_debug("vdev id %d link %d is removed", + vdev_id, link_id); + continue; + } + if ((flag & NLINK_INCLUDE_REMOVED_LINK_ONLY) && + !wlan_get_vdev_link_removed_flag_by_vdev_id( + psoc, vdev_id)) { + continue; + } + if ((flag & NLINK_EXCLUDE_QUIET_LINK) && + mlo_is_sta_in_quiet_status(mlo_dev_ctx, link_id)) { + mlo_debug("vdev id %d link %d is quiet", + vdev_id, link_id); + continue; + } + + if (num_link >= ml_num_link_sz) + break; + ml_freq_lst[num_link] = wlan_get_operation_chan_freq( + mlo_dev_ctx->wlan_vdev_list[i]); + ml_vdev_lst[num_link] = vdev_id; + ml_linkid_lst[num_link] = link_id; + link_bitmap |= 1 << link_id; + + mlo_debug("vdev %d link %d freq %d bitmap 0x%x flag 0x%x", + ml_vdev_lst[num_link], ml_linkid_lst[num_link], + ml_freq_lst[num_link], link_bitmap, flag); + num_link++; + } + mlo_dev_lock_release(mlo_dev_ctx); + *ml_num_link = num_link; + *ml_link_bitmap = link_bitmap; +} + +/** + * convert_link_bitmap_to_link_ids() - Convert link bitmap to link ids + * @link_bitmap: PSOC object information + * @link_id_sz: link_ids array size + * @link_ids: link id array + * + * Return: num of link id in link_ids array converted from link bitmap + */ +static uint32_t +convert_link_bitmap_to_link_ids(uint32_t link_bitmap, + uint8_t link_id_sz, + uint8_t *link_ids) +{ + uint32_t i = 0; + uint8_t id = 0; + + while (link_bitmap) { + if (link_bitmap & 1) { + if (id >= 15) { + /* warning */ + mlo_err("linkid invalid %d 0x%x", + id, link_bitmap); + break; + } + if (link_ids) { + if (i >= link_id_sz) { + /* warning */ + mlo_err("linkid buff overflow 0x%x", + link_bitmap); + break; + } + link_ids[i] = id; + } + i++; + } + link_bitmap >>= 1; + id++; + } + + return i; +} + +/** + * ml_nlink_handle_mcc_links() - Check force inactive needed + * if ML STA links are in MCC channels + * @psoc: PSOC object information + * @vdev: vdev object + * @force_cmd: force command to be returned + * + * This API will return force inactive number 1 in force_cmd + * if STA links are in MCC channels with the link bitmap including + * the MCC links id. + * If the link is marked removed by AP MLD, return force inactive + * bitmap with removed link id bitmap as well. + * + * Return: void + */ +static void +ml_nlink_handle_mcc_links(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct ml_link_force_state *force_cmd) +{ + uint8_t ml_num_link = 0; + uint32_t ml_link_bitmap = 0, affected_link_bitmap = 0; + uint32_t force_inactive_link_bitmap = 0; + uint8_t ml_vdev_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + qdf_freq_t ml_freq_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t ml_linkid_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct ml_link_info ml_link_info[MAX_NUMBER_OF_CONC_CONNECTIONS]; + + ml_nlink_get_link_info(psoc, vdev, NLINK_INCLUDE_REMOVED_LINK_ONLY, + QDF_ARRAY_SIZE(ml_linkid_lst), + ml_link_info, ml_freq_lst, ml_vdev_lst, + ml_linkid_lst, &ml_num_link, + &force_inactive_link_bitmap); + if (force_inactive_link_bitmap) { + /* AP removed link will be force inactive always */ + force_cmd->force_inactive_bitmap = force_inactive_link_bitmap; + mlo_debug("AP removed link 0x%x", force_inactive_link_bitmap); + } + + ml_nlink_get_link_info(psoc, vdev, NLINK_EXCLUDE_REMOVED_LINK, + QDF_ARRAY_SIZE(ml_linkid_lst), + ml_link_info, ml_freq_lst, ml_vdev_lst, + ml_linkid_lst, &ml_num_link, + &ml_link_bitmap); + if (ml_num_link < 2) + return; + + policy_mgr_is_ml_sta_links_in_mcc(psoc, ml_freq_lst, + ml_vdev_lst, + ml_linkid_lst, + ml_num_link, + &affected_link_bitmap); + if (affected_link_bitmap) { + force_cmd->force_inactive_num = + convert_link_bitmap_to_link_ids( + affected_link_bitmap, 0, NULL); + if (force_cmd->force_inactive_num > 1) { + force_cmd->force_inactive_num--; + force_cmd->force_inactive_num_bitmap = + affected_link_bitmap; + } else { + force_cmd->force_inactive_num = 0; + } + } + if (force_inactive_link_bitmap || affected_link_bitmap) + ml_nlink_dump_force_state(force_cmd, ""); +} + +/** + * ml_nlink_handle_legacy_sta_intf() - Check force inactive needed + * with legacy STA + * @psoc: PSOC object information + * @vdev: vdev object + * @force_cmd: force command to be returned + * @sta_vdev_id: legacy STA vdev id + * @non_ml_sta_freq: legacy STA channel frequency + * + * If legacy STA is MCC with any link of MLO STA, the mlo link + * will be forced inactive. And if 3 link MLO case, the left + * 2 links have to be force inactive with num 1. For example, + * ML STA 2+5+6, legacy STA on MCC channel of 5G link, then + * 5G will be force inactive, and left 2+6 link will be force + * inactive by inactive link num = 1 (with link bitmap 2+6). + * + * Return: void + */ +static void +ml_nlink_handle_legacy_sta_intf(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct ml_link_force_state *force_cmd, + uint8_t sta_vdev_id, + qdf_freq_t non_ml_sta_freq) +{ + uint8_t ml_num_link = 0; + uint32_t ml_link_bitmap = 0, affected_link_bitmap = 0; + uint32_t force_inactive_link_bitmap = 0; + uint8_t ml_vdev_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + qdf_freq_t ml_freq_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t ml_linkid_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct ml_link_info ml_link_info[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t i = 0; + uint32_t scc_link_bitmap = 0; + + ml_nlink_get_link_info(psoc, vdev, NLINK_EXCLUDE_REMOVED_LINK, + QDF_ARRAY_SIZE(ml_linkid_lst), + ml_link_info, ml_freq_lst, ml_vdev_lst, + ml_linkid_lst, &ml_num_link, + &ml_link_bitmap); + if (ml_num_link < 2) + return; + + for (i = 0; i < ml_num_link; i++) { + /*todo add removed link to force_inactive_link_bitmap*/ + if (ml_freq_lst[i] == non_ml_sta_freq) { + scc_link_bitmap = 1 << ml_linkid_lst[i]; + } else if (policy_mgr_2_freq_always_on_same_mac( + psoc, ml_freq_lst[i], non_ml_sta_freq)) { + force_inactive_link_bitmap |= 1 << ml_linkid_lst[i]; + } else if (!wlan_cm_same_band_sta_allowed(psoc) && + (wlan_reg_is_24ghz_ch_freq(ml_freq_lst[i]) == + wlan_reg_is_24ghz_ch_freq(non_ml_sta_freq)) && + !policy_mgr_are_sbs_chan(psoc, ml_freq_lst[i], + non_ml_sta_freq)) { + force_inactive_link_bitmap |= 1 << ml_linkid_lst[i]; + } + } + + /* If no left active link, don't send the force inactive command for + * concurrency purpose. + */ + if (!(ml_link_bitmap & ~force_inactive_link_bitmap)) { + mlo_debug("unexpected ML conc with legacy STA freq %d", + non_ml_sta_freq); + return; + } + + if (force_inactive_link_bitmap) { + /* for example SBS rd, ML 2G+5G high, Legacy intf on 5G high, + * set force inactive with bitmap of 5g link. + * + * for SBS rd, ML 2G + 5G low + 6G, Legacy intf on 2G. + * set force inactive with bitmap 2G link, + * and set force inactive link num to 1 for left 5g and 6g + * link. + * for SBS rd, ML 2G + 5G low + 6G, Legacy intf on 5G low. + * set force inactive with bitmap 5G low link, + * and set force inactive link num to 1 for left 2g and 6g + * link. + * for SBS rd, ML 2G + 5G low + 6G, Legacy intf on 5G high. + * set force inactive with bitmap 6G link, + * and set force inactive link num to 1 for left 2g and 5g + * link. + * In above 3 link cases, if legacy intf is SCC with ml link + * don't force inactive by bitmap, only send force inactive + * num with bitmap + */ + force_cmd->force_inactive_bitmap = force_inactive_link_bitmap; + + affected_link_bitmap = + ml_link_bitmap & ~force_inactive_link_bitmap; + affected_link_bitmap &= ~scc_link_bitmap; + force_cmd->force_inactive_num = + convert_link_bitmap_to_link_ids( + affected_link_bitmap, 0, NULL); + if (force_cmd->force_inactive_num > 1) { + force_cmd->force_inactive_num--; + force_cmd->force_inactive_num_bitmap = + affected_link_bitmap; + + } else { + force_cmd->force_inactive_num = 0; + } + } else { + /* for example SBS rd, ML 2G+5G high, Legacy intf on 5G low, + * set force inactive num to 1 with bitmap of 2g+5g link. + * + * for SBS rd, ML 2G + 5G low + 6G, Legacy intf on 5G low SCC. + * set force inactive link num to 1 for left 2g and 6g + * link. + */ + affected_link_bitmap = ml_link_bitmap; + affected_link_bitmap &= ~scc_link_bitmap; + + force_cmd->force_inactive_num = + convert_link_bitmap_to_link_ids( + affected_link_bitmap, 0, NULL); + if (force_cmd->force_inactive_num > 1) { + force_cmd->force_inactive_num--; + force_cmd->force_inactive_num_bitmap = + affected_link_bitmap; + } else { + force_cmd->force_inactive_num = 0; + } + } +} + +/** + * ml_nlink_handle_legacy_sap_intf() - Check force inactive needed + * with legacy SAP + * @psoc: PSOC object information + * @vdev: vdev object + * @force_cmd: force command to be returned + * @sap_vdev_id: legacy SAP vdev id + * @sap_freq: legacy SAP channel frequency + * + * If legacy SAP is 2g only SAP and MLO STA is 5+6, + * 2 links have to be force inactive with num 1. + * + * Return: void + */ +static void +ml_nlink_handle_legacy_sap_intf(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct ml_link_force_state *force_cmd, + uint8_t sap_vdev_id, + qdf_freq_t sap_freq) +{ + uint8_t ml_num_link = 0; + uint32_t ml_link_bitmap = 0, affected_link_bitmap = 0; + uint8_t ml_vdev_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + qdf_freq_t ml_freq_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t ml_linkid_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct ml_link_info ml_link_info[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t i = 0; + bool sap_2g_only = false; + + /* SAP MCC with MLO STA link is not preferred. + * If SAP is 2Ghz only by ACS and two ML link are + * 5/6 band, then force SCC may not happen. In such + * case inactive one link. + */ + if (policy_mgr_check_2ghz_only_sap_affected_link( + psoc, sap_vdev_id, sap_freq, + ml_num_link, ml_freq_lst)) { + mlo_debug("2G only SAP vdev %d ch freq %d is not SCC with any MLO STA link", + sap_vdev_id, sap_freq); + sap_2g_only = true; + } + /* + * If SAP is on 5G or 6G, SAP can always force SCC to 5G/6G ML STA or + * 2G ML STA, no need force SCC link. + */ + if (!sap_2g_only) + return; + + ml_nlink_get_link_info(psoc, vdev, NLINK_EXCLUDE_REMOVED_LINK, + QDF_ARRAY_SIZE(ml_linkid_lst), + ml_link_info, ml_freq_lst, ml_vdev_lst, + ml_linkid_lst, &ml_num_link, + &ml_link_bitmap); + if (ml_num_link < 2) + return; + + for (i = 0; i < ml_num_link; i++) { + if (!wlan_reg_is_24ghz_ch_freq(ml_freq_lst[i])) + affected_link_bitmap |= 1 << ml_linkid_lst[i]; + } + + if (affected_link_bitmap) { + /* for SBS rd, ML 2G + 5G low, Legacy SAP on 2G. + * no force any link + * for SBS rd, ML 5G low + 5G high/6G, Legacy SAP on 2G. + * set force inactive num 1 with bitmap 5g and 6g. + * + * for SBS rd, ML 2G + 5G low + 6G, Legacy SAP on 2G. + * set force inactive link num to 1 for 5g and 6g + * link. + */ + force_cmd->force_inactive_num = + convert_link_bitmap_to_link_ids( + affected_link_bitmap, 0, NULL); + if (force_cmd->force_inactive_num > 1) { + force_cmd->force_inactive_num--; + force_cmd->force_inactive_num_bitmap = + affected_link_bitmap; + } else { + force_cmd->force_inactive_num = 0; + } + } +} + +/** + * ml_nlink_handle_legacy_p2p_intf() - Check force inactive needed + * with p2p + * @psoc: PSOC object information + * @vdev: vdev object + * @force_cmd: force command to be returned + * @p2p_vdev_id: p2p vdev id + * @p2p_freq: p2p channel frequency + * + * If P2P has low latency flag and MCC with any link of MLO STA, the mlo link + * will be forced inactive. And if 3 link MLO case, the left 2 links have to + * be force inactive with num 1. + * + * Return: void + */ +static void +ml_nlink_handle_legacy_p2p_intf(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct ml_link_force_state *force_cmd, + uint8_t p2p_vdev_id, + qdf_freq_t p2p_freq) +{ + uint8_t ml_num_link = 0; + uint32_t ml_link_bitmap = 0, affected_link_bitmap = 0; + uint32_t force_inactive_link_bitmap = 0; + uint8_t ml_vdev_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + qdf_freq_t ml_freq_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t ml_linkid_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct ml_link_info ml_link_info[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t i = 0; + uint32_t scc_link_bitmap = 0; + + /* If high tput or low latency is not set, mcc is allowed for p2p */ + if (!policy_mgr_is_vdev_high_tput_or_low_latency( + psoc, p2p_vdev_id)) + return; + ml_nlink_get_link_info(psoc, vdev, NLINK_EXCLUDE_REMOVED_LINK, + QDF_ARRAY_SIZE(ml_linkid_lst), + ml_link_info, ml_freq_lst, ml_vdev_lst, + ml_linkid_lst, &ml_num_link, + &ml_link_bitmap); + if (ml_num_link < 2) + return; + + for (i = 0; i < ml_num_link; i++) { + if (ml_freq_lst[i] == p2p_freq) { + scc_link_bitmap = 1 << ml_linkid_lst[i]; + } else if (policy_mgr_2_freq_always_on_same_mac( + psoc, ml_freq_lst[i], p2p_freq)) { + force_inactive_link_bitmap |= 1 << ml_linkid_lst[i]; + } + } + /* If no left active link, don't send the force inactive command for + * concurrency purpose. + */ + if (!(ml_link_bitmap & ~force_inactive_link_bitmap)) { + mlo_debug("unexpected ML conc with legacy P2P freq %d", + p2p_freq); + return; + } + + if (force_inactive_link_bitmap) { + /* for example SBS rd, ML 2G+5G high, Legacy intf on 5G high, + * set force inactive with bitmap of 5g link. + * + * for SBS rd, ML 2G + 5G low + 6G, Legacy intf on 2G. + * set force inactive with bitmap 2G link, + * and set force inactive link num to 1 for left 5g and 6g + * link. + * for SBS rd, ML 2G + 5G low + 6G, Legacy intf on 5G low. + * set force inactive with bitmap 5G low link, + * and set force inactive link num to 1 for left 2g and 6g + * link. + * for SBS rd, ML 2G + 5G low + 6G, Legacy intf on 5G high.. + * set force inactive with bitmap 6G low link, + * and set force inactive link num to 1 for left 2g and 5g + * link. + */ + force_cmd->force_inactive_bitmap = force_inactive_link_bitmap; + + affected_link_bitmap = + ml_link_bitmap & ~force_inactive_link_bitmap; + affected_link_bitmap &= ~scc_link_bitmap; + force_cmd->force_inactive_num = + convert_link_bitmap_to_link_ids( + affected_link_bitmap, 0, NULL); + if (force_cmd->force_inactive_num > 1) { + force_cmd->force_inactive_num--; + force_cmd->force_inactive_num_bitmap = + affected_link_bitmap; + + } else { + force_cmd->force_inactive_num = 0; + } + } else { + /* for example SBS rd, ML 2G+5G high, Legacy intf on 5G low, + * set force inactive num to 1 with bitmap of 2g+5g link. + * + * for SBS rd, ML 2G + 5G low + 6G, Legacy intf on 5G low SCC. + * set force inactive link num to 1 for left 2g and 6g + * link. + */ + affected_link_bitmap = ml_link_bitmap; + affected_link_bitmap &= ~scc_link_bitmap; + + force_cmd->force_inactive_num = + convert_link_bitmap_to_link_ids( + affected_link_bitmap, 0, NULL); + if (force_cmd->force_inactive_num > 1) { + force_cmd->force_inactive_num--; + force_cmd->force_inactive_num_bitmap = + affected_link_bitmap; + } else { + force_cmd->force_inactive_num = 0; + } + } +} + +/** + * ml_nlink_handle_legacy_intf_3_ports() - Check force inactive needed + * with 2 legacy interfaces + * @psoc: PSOC object information + * @vdev: vdev object + * @force_cmd: force command to be returned + * @legacy_intf_freq1: legacy interface frequency + * @legacy_intf_freq2: legacy interface frequency + * + * If legacy interface is mcc with any link based on current hw mode, then + * force inactive the link. + * And if standby link is mcc with legacy interface, then disable standby + * link as well. + * + * Return: void + */ +static void +ml_nlink_handle_legacy_intf_3_ports(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct ml_link_force_state *force_cmd, + qdf_freq_t legacy_intf_freq1, + qdf_freq_t legacy_intf_freq2) +{ + uint8_t ml_num_link = 0; + uint32_t ml_link_bitmap = 0; + uint32_t force_inactive_link_bitmap = 0; + uint8_t ml_vdev_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + qdf_freq_t ml_freq_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t ml_linkid_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct ml_link_info ml_link_info[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t i = 0; + uint32_t scc_link_bitmap = 0; + + ml_nlink_get_link_info(psoc, vdev, NLINK_EXCLUDE_REMOVED_LINK, + QDF_ARRAY_SIZE(ml_linkid_lst), + ml_link_info, ml_freq_lst, ml_vdev_lst, + ml_linkid_lst, &ml_num_link, + &ml_link_bitmap); + if (ml_num_link < 2) + return; + + for (i = 0; i < ml_num_link; i++) { + if (ml_vdev_lst[i] == 0xff) { + /*standby link will be handled later. */ + continue; + } + if (ml_freq_lst[i] == legacy_intf_freq1) { + scc_link_bitmap = 1 << ml_linkid_lst[i]; + if (ml_freq_lst[i] == legacy_intf_freq2) { + mlo_debug("3 vdev scc no-op"); + return; + } + } else if (policy_mgr_are_2_freq_on_same_mac( + psoc, ml_freq_lst[i], legacy_intf_freq1)) { + force_inactive_link_bitmap |= 1 << ml_linkid_lst[i]; + } + } + /* usually it can't happen in 3 Port */ + if (!force_inactive_link_bitmap && !scc_link_bitmap) { + mlo_debug("legacy vdev freq %d standalone on dedicated mac", + legacy_intf_freq1); + return; + } + /* handle standby link */ + for (i = 0; i < ml_num_link; i++) { + if (ml_vdev_lst[i] == 0xff) { + /*standby link will be forced inactive if mcc with + * legacy sta + */ + if (ml_freq_lst[i] != legacy_intf_freq1) + force_inactive_link_bitmap |= + 1 << ml_linkid_lst[i]; + } + } + + if (force_inactive_link_bitmap) + force_cmd->force_inactive_bitmap = force_inactive_link_bitmap; +} + +/** + * ml_nlink_handle_legacy_intf() - Check force inactive needed + * with legacy interface + * @psoc: PSOC object information + * @vdev: vdev object + * @force_cmd: force command to be returned + * + * Return: void + */ +static void +ml_nlink_handle_legacy_intf(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct ml_link_force_state *force_cmd) +{ + uint8_t vdev_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + qdf_freq_t freq_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + enum policy_mgr_con_mode mode_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t num_legacy_vdev; + + num_legacy_vdev = policy_mgr_get_legacy_conn_info( + psoc, vdev_lst, + freq_lst, mode_lst, + QDF_ARRAY_SIZE(vdev_lst)); + if (!num_legacy_vdev) + return; + /* 2 port case with 2 ml sta links or + * 2 port case with 3 ml sta links + */ + if (num_legacy_vdev == 1) { + switch (mode_lst[0]) { + case PM_STA_MODE: + ml_nlink_handle_legacy_sta_intf( + psoc, vdev, force_cmd, vdev_lst[0], + freq_lst[0]); + break; + case PM_SAP_MODE: + ml_nlink_handle_legacy_sap_intf( + psoc, vdev, force_cmd, vdev_lst[0], + freq_lst[0]); + break; + case PM_P2P_CLIENT_MODE: + case PM_P2P_GO_MODE: + ml_nlink_handle_legacy_p2p_intf( + psoc, vdev, force_cmd, vdev_lst[0], + freq_lst[0]); + break; + default: + /* unexpected legacy connection count */ + mlo_debug("unexpected legacy intf mode %d", + mode_lst[0]); + return; + } + ml_nlink_dump_force_state(force_cmd, ""); + return; + } + /* 3 ports case with ml sta 2 or 3 links, suppose port 3 vdev is + * low latency legacy vdev: + * 6G: ML Link + Port2 + Port3 | 5G: ML Link + * => no op + * 6G: ML Link + Port2 | 5G: ML Link + Port3 + * => disable 5G link if 5G mcc + * 6G: ML Link + Port3 | 5G: ML Link + Port2 + * => disable 6G link if 6G mcc + * 6G: ML Link | 5G: ML Link + Port3 | 2G: Port2 + * => disable 5G link if 5G mcc. + * 6G: ML Link | 5G: ML Link + Port2 | 2G: Port3 + * => disable 6g link. + * 6G: ML Link + Port3 | 5G: ML Link | 2G: Port2 + * => disable 6G link if 6G mcc. + * 6G: ML Link + Port2 | 5G: ML Link | 2G: Port3 + * => disable 6g link. + * 6G: ML Link + Port2 + Port3 | 2G: ML Link + * => no op + * 6G: ML Link + Port2 | 2G: ML Link + Port3 + * => disable 2G link if 2G mcc + * 6G: ML Link + Port3 | 2G: ML Link + Port2 + * => disable 6G link if 6G mcc + * 6G: ML Link | 2G: ML Link + Port3 | 5G: Port2 + * => disable 2G link if 2G mcc. + * 6G: ML Link | 2G: ML Link + Port2 | 5GL: Port3 + * => disable 6G link + * 6G: ML Link + Port3 | 2G: ML Link | 5G: Port2 + * => disable 6G link if 6G mcc. + * 6G: ML Link + Port2 | 2G: ML Link | 5GL: Port3 + * => disable 2G link + * general rule: + * If Port3 is mcc with any link based on current hw mode, then + * force inactive the link. + * And if standby link is mcc with Port3, then disable standby + * link as well. + */ + switch (mode_lst[0]) { + case PM_P2P_CLIENT_MODE: + case PM_P2P_GO_MODE: + if (!policy_mgr_is_vdev_high_tput_or_low_latency( + psoc, vdev_lst[0])) + return; + fallthrough; + case PM_STA_MODE: + ml_nlink_handle_legacy_intf_3_ports( + psoc, vdev, force_cmd, freq_lst[0], freq_lst[1]); + break; + case PM_SAP_MODE: + /* if 2g only sap present, force inactive num to fw. */ + ml_nlink_handle_legacy_sap_intf( + psoc, vdev, force_cmd, vdev_lst[0], freq_lst[0]); + break; + default: + /* unexpected legacy connection count */ + mlo_debug("unexpected legacy intf mode %d", mode_lst[0]); + return; + } + ml_nlink_dump_force_state(force_cmd, ""); +} + +/** + * ml_nlink_sta_inactivity_allowed_with_quiet() - Check force inactive allowed + * for links in bitmap + * @psoc: PSOC object information + * @vdev: vdev object + * @force_inactive_bitmap: force inactive link bimap + * + * If left links (exclude removed link and QUITE link) are zero, the force + * inactive bitmap is not allowed. + * + * Return: true if allow to force inactive links in force_inactive_bitmap + */ +static bool ml_nlink_sta_inactivity_allowed_with_quiet( + struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + uint16_t force_inactive_bitmap) +{ + uint8_t ml_num_link = 0; + uint32_t ml_link_bitmap = 0; + uint8_t ml_vdev_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + qdf_freq_t ml_freq_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t ml_linkid_lst[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct ml_link_info ml_link_info[MAX_NUMBER_OF_CONC_CONNECTIONS]; + + ml_nlink_get_link_info(psoc, vdev, (NLINK_EXCLUDE_REMOVED_LINK | + NLINK_EXCLUDE_QUIET_LINK), + QDF_ARRAY_SIZE(ml_linkid_lst), + ml_link_info, ml_freq_lst, ml_vdev_lst, + ml_linkid_lst, &ml_num_link, + &ml_link_bitmap); + ml_link_bitmap &= ~force_inactive_bitmap; + if (!ml_link_bitmap) { + mlo_debug("not allow - no active link after force inactive 0x%x", + force_inactive_bitmap); + return false; + } + + return true; +} + +/** + * ml_nlink_allow_conc() - Check force inactive allowed for links in bitmap + * @psoc: PSOC object information + * @vdev: vdev object + * @no_forced_bitmap: no force link bitmap + * @force_inactive_bitmap: force inactive link bimap + * + * Check the no force bitmap and force inactive bitmap are allowed to send + * to firmware + * + * Return: true if allow to "no force" and force inactive links. + */ +static bool +ml_nlink_allow_conc(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + uint16_t no_forced_bitmap, + uint16_t force_inactive_bitmap) +{ + uint8_t vdev_id_num = 0; + uint8_t vdev_ids[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint32_t vdev_id_bitmap_sz; + uint32_t vdev_id_bitmap[MLO_VDEV_BITMAP_SZ]; + uint32_t i; + union conc_ext_flag conc_ext_flags; + struct wlan_objmgr_vdev *ml_vdev; + bool allow = true; + qdf_freq_t freq = 0; + struct wlan_channel *bss_chan; + + if (!ml_nlink_sta_inactivity_allowed_with_quiet( + psoc, vdev, force_inactive_bitmap)) + return false; + + ml_nlink_convert_linkid_bitmap_to_vdev_bitmap( + psoc, vdev, no_forced_bitmap, NULL, &vdev_id_bitmap_sz, + vdev_id_bitmap, &vdev_id_num, vdev_ids); + + for (i = 0; i < vdev_id_num; i++) { + ml_vdev = + wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + vdev_ids[i], + WLAN_MLO_MGR_ID); + if (!ml_vdev) { + mlo_err("invalid vdev id %d ", vdev_ids[i]); + continue; + } + + /* If link is active, no need to check allow conc */ + if (!policy_mgr_vdev_is_force_inactive(psoc, vdev_ids[i])) { + wlan_objmgr_vdev_release_ref(ml_vdev, + WLAN_MLO_MGR_ID); + continue; + } + + conc_ext_flags.value = + policy_mgr_get_conc_ext_flags(ml_vdev, true); + + bss_chan = wlan_vdev_mlme_get_bss_chan(ml_vdev); + if (bss_chan) + freq = bss_chan->ch_freq; + + if (!policy_mgr_allow_concurrency(psoc, PM_STA_MODE, + freq, + HW_MODE_20_MHZ, + conc_ext_flags.value, + vdev_ids[i])) { + wlan_objmgr_vdev_release_ref(ml_vdev, + WLAN_MLO_MGR_ID); + break; + } + wlan_objmgr_vdev_release_ref(ml_vdev, WLAN_MLO_MGR_ID); + } + + if (i < vdev_id_num) { + mlo_err("not allow - vdev %d freq %d active due to conc", + vdev_ids[i], freq); + allow = false; + } + + return allow; +} + +static QDF_STATUS +ml_nlink_update_no_force_for_all(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct ml_link_force_state *curr, + struct ml_link_force_state *new, + enum mlo_link_force_reason reason) +{ + uint16_t no_force_links; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + /* Special handling for clear all force mode in target. + * send MLO_LINK_FORCE_MODE_NO_FORCE to clear "all" + * to target + */ + if (!new->force_inactive_bitmap && + !new->force_inactive_num && + !new->force_active_bitmap && + !new->force_active_num && + (curr->force_inactive_bitmap || + curr->force_inactive_num || + curr->force_active_bitmap || + curr->force_active_num)) { + /* If link is force inactive already, but new command will + * mark it non-force, need to check conc allow or not. + */ + no_force_links = curr->force_inactive_bitmap; + /* Check non forced links allowed by conc */ + if (!ml_nlink_allow_conc(psoc, vdev, no_force_links, 0)) { + status = QDF_STATUS_E_INVAL; + goto end; + } + + status = policy_mgr_mlo_sta_set_nlink( + psoc, vdev, reason, + MLO_LINK_FORCE_MODE_NO_FORCE, + 0, 0, 0, 0); + } + +end: + return status; +} + +static QDF_STATUS +ml_nlink_update_force_inactive(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct ml_link_force_state *curr, + struct ml_link_force_state *new, + enum mlo_link_force_reason reason) +{ + uint16_t no_force_links; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (new->force_inactive_bitmap != curr->force_inactive_bitmap) { + /* If link is force inactive already, but new command will + * mark it non-force, need to check conc allow or not. + */ + no_force_links = curr->force_inactive_bitmap & + new->force_inactive_bitmap; + no_force_links ^= curr->force_inactive_bitmap; + + /* Check non forced links allowed by conc */ + if (!ml_nlink_allow_conc(psoc, vdev, no_force_links, + new->force_inactive_bitmap)) { + status = QDF_STATUS_E_INVAL; + goto end; + } + status = policy_mgr_mlo_sta_set_nlink( + psoc, vdev, reason, + MLO_LINK_FORCE_MODE_INACTIVE, + 0, + new->force_inactive_bitmap, + 0, link_ctrl_f_overwrite_inactive_bitmap); + } + +end: + return status; +} + +static QDF_STATUS +ml_nlink_update_force_inactive_num(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct ml_link_force_state *curr, + struct ml_link_force_state *new, + enum mlo_link_force_reason reason) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (new->force_inactive_num != + curr->force_inactive_num || + new->force_inactive_num_bitmap != + curr->force_inactive_num_bitmap) { + status = policy_mgr_mlo_sta_set_nlink( + psoc, vdev, reason, + MLO_LINK_FORCE_MODE_INACTIVE_NUM, + new->force_inactive_num, + new->force_inactive_num_bitmap, + 0, + 0); + } + + return status; +} + +static QDF_STATUS +ml_nlink_update_force_active(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct ml_link_force_state *curr, + struct ml_link_force_state *new, + enum mlo_link_force_reason reason) +{ + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +ml_nlink_update_force_active_num(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct ml_link_force_state *curr, + struct ml_link_force_state *new, + enum mlo_link_force_reason reason) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * ml_nlink_state_change() - Handle ML STA link force + * with concurrency internal function + * @psoc: PSOC object information + * @reason: reason code of trigger force mode change. + * + * This API handle link force for connected ML STA. + * At present we only support one ML STA. so ml_nlink_get_affect_ml_sta + * is invoked to get one ML STA vdev from policy mgr table. + * + * The flow is to get current force command which has been sent to target + * and compute a new force command based on current connection table. + * If any difference between "current" and "new", driver sends update + * command to target. Driver will update the current force command + * record after get successful respone from target. + * + * Return: QDF_STATUS_SUCCESS if no new command updated to target. + * QDF_STATUS_E_PENDING if new command is sent to target. + * otherwise QDF_STATUS error code + */ +static QDF_STATUS ml_nlink_state_change(struct wlan_objmgr_psoc *psoc, + enum mlo_link_force_reason reason) +{ + struct ml_link_force_state force_state = {0}; + struct ml_link_force_state legacy_intf_force_state = {0}; + struct ml_link_force_state curr_force_state = {0}; + struct wlan_objmgr_vdev *vdev = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + mlo_debug("enter evaluate force state change"); + + /* + * eMLSR is allowed in MCC mode also. So, don't disable any links + * if current connection happens in eMLSR mode. + * eMLSR is handled by wlan_handle_emlsr_sta_concurrency + */ + if (policy_mgr_is_mlo_in_mode_emlsr(psoc, NULL, NULL)) { + mlo_debug("Don't disable eMLSR links"); + goto end; + } + + vdev = ml_nlink_get_affect_ml_sta(psoc); + if (!vdev) + goto end; + if (!mlo_check_if_all_links_up(vdev)) + goto end; + + ml_nlink_get_curr_force_state(psoc, vdev, &curr_force_state); + + ml_nlink_handle_mcc_links(psoc, vdev, &force_state); + + ml_nlink_handle_legacy_intf(psoc, vdev, &legacy_intf_force_state); + + force_state.force_inactive_bitmap |= + legacy_intf_force_state.force_inactive_bitmap; + + if (legacy_intf_force_state.force_inactive_num && + legacy_intf_force_state.force_inactive_num >= + force_state.force_inactive_num) { + force_state.force_inactive_num = + legacy_intf_force_state.force_inactive_num; + force_state.force_inactive_num_bitmap = + legacy_intf_force_state.force_inactive_num_bitmap; + } + + status = ml_nlink_update_no_force_for_all(psoc, vdev, + &curr_force_state, + &force_state, + reason); + if (status == QDF_STATUS_E_PENDING || status != QDF_STATUS_SUCCESS) + goto end; + + status = ml_nlink_update_force_inactive(psoc, vdev, + &curr_force_state, + &force_state, + reason); + if (status == QDF_STATUS_E_PENDING || status != QDF_STATUS_SUCCESS) + goto end; + + status = ml_nlink_update_force_inactive_num(psoc, vdev, + &curr_force_state, + &force_state, + reason); + if (status == QDF_STATUS_E_PENDING || status != QDF_STATUS_SUCCESS) + goto end; + + /* At present, only force inactive/inactive num mode have been used + * to avoid MCC, force active/active num APIs are no-op for now. + */ + status = ml_nlink_update_force_active(psoc, vdev, + &curr_force_state, + &force_state, + reason); + if (status == QDF_STATUS_E_PENDING || status != QDF_STATUS_SUCCESS) + goto end; + + status = ml_nlink_update_force_active_num(psoc, vdev, + &curr_force_state, + &force_state, + reason); +end: + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID); + + if (status == QDF_STATUS_SUCCESS) + mlo_debug("exit no force state change"); + else if (status == QDF_STATUS_E_PENDING) + mlo_debug("exit pending force state change"); + else + mlo_err("exit err %d state change", status); + + return status; +} + +/** + * ml_nlink_state_change_handler() - Handle ML STA link force + * with concurrency + * @psoc: PSOC object information + * @vdev: ml sta vdev object + * @reason: reason code of trigger force mode change. + * + * Return: void + */ +static void +ml_nlink_state_change_handler(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + enum mlo_link_force_reason reason) +{ + enum QDF_OPMODE mode = wlan_vdev_mlme_get_opmode(vdev); + uint8_t vdev_id = wlan_vdev_get_id(vdev); + + /* If WMI_SERVICE_N_LINK_MLO_SUPPORT = 381 is enabled, + * indicate FW support N MLO link & vdev re-purpose between links, + * host will use linkid bitmap to force inactive/active links + * by API ml_nlink_state_change. + * Otherwise, use legacy policy mgr API to inactive/active based + * on vdev id bitmap. + */ + if (ml_is_nlink_service_supported()) + ml_nlink_state_change(psoc, reason); + else if (reason == MLO_LINK_FORCE_REASON_CONNECT) + policy_mgr_handle_ml_sta_links_on_vdev_up_csa(psoc, mode, + vdev_id); + else + policy_mgr_handle_ml_sta_links_on_vdev_down(psoc, mode, + vdev_id); +} + +QDF_STATUS +ml_nlink_conn_change_notify(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum ml_nlink_change_event_type evt, + struct ml_nlink_change_event *data) +{ + struct wlan_objmgr_vdev *vdev; + + mlo_debug("vdev %d %s", vdev_id, link_evt_to_string(evt)); + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_MLO_MGR_ID); + if (!vdev) { + mlo_err("invalid vdev id %d ", vdev_id); + return QDF_STATUS_E_INVAL; + } + + switch (evt) { + case ml_nlink_link_switch_start_evt: + /* todo: allow concurrenct check */ + break; + case ml_nlink_link_switch_completion_evt: + ml_nlink_state_change_handler( + psoc, vdev, MLO_LINK_FORCE_REASON_CONNECT); + break; + case ml_nlink_roam_sync_start_evt: + ml_nlink_clr_force_state(psoc, vdev); + break; + case ml_nlink_roam_sync_completion_evt: + ml_nlink_state_change_handler( + psoc, vdev, MLO_LINK_FORCE_REASON_CONNECT); + break; + case ml_nlink_connect_start_evt: + ml_nlink_clr_force_state(psoc, vdev); + break; + case ml_nlink_connect_completion_evt: + ml_nlink_state_change_handler( + psoc, vdev, MLO_LINK_FORCE_REASON_CONNECT); + break; + case ml_nlink_disconnect_start_evt: + ml_nlink_clr_force_state(psoc, vdev); + break; + case ml_nlink_disconnect_completion_evt: + ml_nlink_state_change_handler( + psoc, vdev, MLO_LINK_FORCE_REASON_DISCONNECT); + break; + case ml_nlink_ap_started_evt: + ml_nlink_state_change_handler( + psoc, vdev, MLO_LINK_FORCE_REASON_CONNECT); + break; + case ml_nlink_ap_stopped_evt: + ml_nlink_state_change_handler( + psoc, vdev, MLO_LINK_FORCE_REASON_DISCONNECT); + break; + case ml_nlink_connection_updated_evt: + ml_nlink_state_change_handler( + psoc, vdev, MLO_LINK_FORCE_REASON_CONNECT); + break; + default: + break; + } + + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID); + + return QDF_STATUS_SUCCESS; +}