From 8759cf1c1e169bb1ab4a1213237641051404d39d Mon Sep 17 00:00:00 2001 From: Amruta Kulkarni Date: Tue, 6 Dec 2022 21:46:34 +0530 Subject: [PATCH] qcacmn: Add support for T2LM timer handling Adds api's to handle t2lm timer functionality. Api's added are timer initialize, start, stop, expiry handler. Change-Id: If52453135921067d04f8349ece64c33cd524af00 CRs-Fixed: 3342822 --- target_if/mlo_mgr/src/target_if_mlo_mgr.c | 90 +++++ .../lmac_if/inc/wlan_lmac_if_def.h | 3 + .../mlo_mgr/inc/wlan_mlo_mgr_public_structs.h | 15 +- umac/mlo_mgr/inc/wlan_mlo_t2lm.h | 180 ++++++++++ umac/mlo_mgr/src/wlan_mlo_mgr_main.c | 9 +- umac/mlo_mgr/src/wlan_mlo_t2lm.c | 340 +++++++++++++++++- 6 files changed, 628 insertions(+), 9 deletions(-) diff --git a/target_if/mlo_mgr/src/target_if_mlo_mgr.c b/target_if/mlo_mgr/src/target_if_mlo_mgr.c index a4205e1b56..b1450cb39c 100644 --- a/target_if/mlo_mgr/src/target_if_mlo_mgr.c +++ b/target_if/mlo_mgr/src/target_if_mlo_mgr.c @@ -23,6 +23,8 @@ #include #include #include "target_if_mlo_mgr.h" +#include +#include /** * target_if_mlo_link_set_active_resp_handler() - function to handle mlo link @@ -238,6 +240,92 @@ target_if_mlo_unregister_vdev_tid_to_link_map_event( wmi_handle, wmi_mlo_ap_vdev_tid_to_link_map_eventid); } +#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE +/** + * target_if_fill_provisioned_links() - API to fill the provisioned links + * @params: Pointer to T2LM params structure + * @t2lm: Pointer to T2LM info structure + * + * Return: none + */ +static inline void target_if_fill_provisioned_links( + struct wmi_host_tid_to_link_map_params *params, + struct wlan_t2lm_info *t2lm) +{ + qdf_mem_copy(¶ms->t2lm_info[params->num_dir].t2lm_provisioned_links, + &t2lm->ieee_link_map_tid, + sizeof(uint16_t) * T2LM_MAX_NUM_TIDS); +} +#else +static inline void target_if_fill_provisioned_links( + struct wmi_host_tid_to_link_map_params *params, + struct wlan_t2lm_info *t2lm) +{ + qdf_mem_copy(¶ms->t2lm_info[params->num_dir].t2lm_provisioned_links, + &t2lm->hw_link_map_tid, + sizeof(uint16_t) * T2LM_MAX_NUM_TIDS); +} +#endif + +static QDF_STATUS +target_if_mlo_send_tid_to_link_mapping(struct wlan_objmgr_vdev *vdev, + struct wlan_t2lm_info *t2lm) +{ + struct wmi_unified *wmi_handle = NULL; + struct wmi_host_tid_to_link_map_params params = {0}; + struct wlan_objmgr_pdev *pdev = NULL; + int tid = 0; + QDF_STATUS status; + + pdev = wlan_vdev_get_pdev(vdev); + if (!pdev) { + t2lm_err("null pdev"); + return QDF_STATUS_E_NULL_VALUE; + } + + wmi_handle = lmac_get_pdev_wmi_handle(pdev); + if (!wmi_handle) { + t2lm_err("null wmi handle"); + return QDF_STATUS_E_NULL_VALUE; + } + + params.pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); + qdf_mem_copy(params.peer_macaddr, vdev->vdev_objmgr.bss_peer->macaddr, + QDF_MAC_ADDR_SIZE); + + t2lm_debug("Fill T2LM WMI info for peer: " QDF_MAC_ADDR_FMT " pdev_id:%d", + QDF_MAC_ADDR_REF(params.peer_macaddr), params.pdev_id); + + params.t2lm_info[params.num_dir].direction = t2lm->direction; + params.t2lm_info[params.num_dir].default_link_mapping = + t2lm->default_link_mapping; + + if (!params.t2lm_info[params.num_dir].default_link_mapping) + target_if_fill_provisioned_links(¶ms, t2lm); + + t2lm_debug("num_dir:%d direction:%d default_link_mapping:%d", + params.num_dir, params.t2lm_info[params.num_dir].direction, + params.t2lm_info[params.num_dir].default_link_mapping); + + for (tid = 0; tid < T2LM_MAX_NUM_TIDS; tid++) { + t2lm_debug("tid:%d hw_link_map:%x ieee_lin_map:%x", tid, + params.t2lm_info[params.num_dir].t2lm_provisioned_links[tid], + t2lm->ieee_link_map_tid[tid]); + } + + params.num_dir++; + + status = wmi_send_mlo_peer_tid_to_link_map_cmd(wmi_handle, ¶ms); + if (QDF_IS_STATUS_ERROR(status)) { + t2lm_err("Failed to send T2LM WMI command for pdev_id:%d peer_mac: " QDF_MAC_ADDR_FMT, + params.pdev_id, + QDF_MAC_ADDR_REF(params.peer_macaddr)); + return status; + } + + return status; +} + /** * target_if_mlo_register_tx_ops() - lmac handler to register mlo tx ops * callback functions @@ -266,6 +354,8 @@ target_if_mlo_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops) mlo_tx_ops->unregister_events = target_if_mlo_unregister_event_handler; mlo_tx_ops->link_set_active = target_if_mlo_link_set_active; + mlo_tx_ops->send_tid_to_link_mapping = + target_if_mlo_send_tid_to_link_mapping; return QDF_STATUS_SUCCESS; } diff --git a/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h b/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h index 4f2d2e3689..36eae90315 100644 --- a/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h +++ b/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h @@ -1387,6 +1387,7 @@ struct wlan_lmac_if_son_rx_ops { * @register_events: function to register event handlers with FW * @unregister_events: function to de-register event handlers with FW * @link_set_active: function to send mlo link set active command to FW + * @send_tid_to_link_mapping: function to send T2LM command to FW */ struct wlan_lmac_if_mlo_tx_ops { QDF_STATUS (*register_events)(struct wlan_objmgr_psoc *psoc); @@ -1396,6 +1397,8 @@ struct wlan_lmac_if_mlo_tx_ops { #ifdef WLAN_MLO_GLOBAL_SHMEM_SUPPORT struct wlan_lmac_if_global_shmem_local_ops shmem_local_ops; #endif + QDF_STATUS (*send_tid_to_link_mapping)(struct wlan_objmgr_vdev *vdev, + struct wlan_t2lm_info *t2lm); }; /** diff --git a/umac/mlo_mgr/inc/wlan_mlo_mgr_public_structs.h b/umac/mlo_mgr/inc/wlan_mlo_mgr_public_structs.h index 4649cfd3ae..670a8a1d81 100644 --- a/umac/mlo_mgr/inc/wlan_mlo_mgr_public_structs.h +++ b/umac/mlo_mgr/inc/wlan_mlo_mgr_public_structs.h @@ -300,6 +300,7 @@ enum wlan_t2lm_direction { WLAN_T2LM_INVALID_DIRECTION, }; +#define T2LM_EXPECTED_DURATION_MAX_VALUE 0xFFFFFF /** * struct wlan_t2lm_info - TID-to-Link mapping information for the frames * transmitted on the uplink, downlink and bidirectional. @@ -352,11 +353,21 @@ struct wlan_mlo_t2lm_ie { * struct wlan_t2lm_timer - T2LM timer information * * @t2lm_timer: T2LM timer - * @timer_interval: T2LM timer interval value + * @timer_interval: T2LM Timer value + * @timer_started: T2LM timer started or not + * @t2lm_ie_index: T2LM IE index value + * @t2lm_dev_lock: lock to access struct */ struct wlan_t2lm_timer { qdf_timer_t t2lm_timer; uint32_t timer_interval; + bool timer_started; + uint8_t t2lm_ie_index; +#ifdef WLAN_MLO_USE_SPINLOCK + qdf_spinlock_t t2lm_dev_lock; +#else + qdf_mutex_t t2lm_dev_lock; +#endif }; /** @@ -366,6 +377,7 @@ struct wlan_t2lm_timer { * @t2lm_ie: T2LM IE information * @t2lm_timer: T2LM timer information * @t2lm_dev_lock: t2lm dev context lock + * @tsf: time sync func value received via beacon */ struct wlan_t2lm_context { uint8_t num_of_t2lm_ie; @@ -376,6 +388,7 @@ struct wlan_t2lm_context { #else qdf_mutex_t t2lm_dev_lock; #endif + uint64_t tsf; }; /* diff --git a/umac/mlo_mgr/inc/wlan_mlo_t2lm.h b/umac/mlo_mgr/inc/wlan_mlo_t2lm.h index fd24911c77..dfe09afa5a 100644 --- a/umac/mlo_mgr/inc/wlan_mlo_t2lm.h +++ b/umac/mlo_mgr/inc/wlan_mlo_t2lm.h @@ -45,6 +45,86 @@ #define WLAN_T2LM_MAX_NUM_LINKS 16 +#ifdef WLAN_MLO_USE_SPINLOCK +/** + * t2lm_dev_lock_create - Create T2LM device mutex/spinlock + * @t2lm_ctx: T2LM context + * + * Creates mutex/spinlock + * + * Return: void + */ +static inline void +t2lm_dev_lock_create(struct wlan_t2lm_context *t2lm_ctx) +{ + qdf_spinlock_create(&t2lm_ctx->t2lm_dev_lock); +} + +/** + * t2lm_dev_lock_destroy - Destroy T2LM mutex/spinlock + * @t2lm_ctx: T2LM context + * + * Destroy mutex/spinlock + * + * Return: void + */ +static inline void +t2lm_dev_lock_destroy(struct wlan_t2lm_context *t2lm_ctx) +{ + qdf_spinlock_destroy(&t2lm_ctx->t2lm_dev_lock); +} + +/** + * t2lm_dev_lock_acquire - acquire T2LM mutex/spinlock + * @t2lm_ctx: T2LM context + * + * acquire mutex/spinlock + * + * return: void + */ +static inline +void t2lm_dev_lock_acquire(struct wlan_t2lm_context *t2lm_ctx) +{ + qdf_spin_lock_bh(&t2lm_ctx->t2lm_dev_lock); +} + +/** + * t2lm_dev_lock_release - release T2LM dev mutex/spinlock + * @t2lm_ctx: T2LM context + * + * release mutex/spinlock + * + * return: void + */ +static inline +void t2lm_dev_lock_release(struct wlan_t2lm_context *t2lm_ctx) +{ + qdf_spin_unlock_bh(&t2lm_ctx->t2lm_dev_lock); +} +#else /* WLAN_MLO_USE_SPINLOCK */ +static inline +void t2lm_dev_lock_create(struct wlan_t2lm_context *t2lm_ctx) +{ + qdf_mutex_create(&t2lm_ctx->t2lm_dev_lock); +} + +static inline +void t2lm_dev_lock_destroy(struct wlan_t2lm_context *t2lm_ctx) +{ + qdf_mutex_destroy(&t2lm_ctx->t2lm_dev_lock); +} + +static inline void t2lm_dev_lock_acquire(struct wlan_t2lm_context *t2lm_ctx) +{ + qdf_mutex_acquire(&t2lm_ctx->t2lm_dev_lock); +} + +static inline void t2lm_dev_lock_release(struct wlan_t2lm_context *t2lm_ctx) +{ + qdf_mutex_release(&t2lm_ctx->t2lm_dev_lock); +} +#endif + /** * wlan_mlo_parse_t2lm_ie() - API to parse the T2LM IE * @t2lm: Pointer to T2LM structure @@ -122,6 +202,69 @@ QDF_STATUS wlan_mlo_parse_bcn_prbresp_t2lm_ie( * Return: Updated frame pointer */ uint8_t *wlan_mlo_add_t2lm_info_ie(uint8_t *frm, struct wlan_t2lm_info *t2lm); + +/** + * wlan_mlo_t2lm_timer_init() - API to add TID-to-link mapping IE + * @vdev: Pointer to vdev + * + * Return: qdf status + */ +QDF_STATUS +wlan_mlo_t2lm_timer_init(struct wlan_objmgr_vdev *vdev); + +/** + * wlan_mlo_t2lm_timer_start() - API to start T2LM timer + * @vdev: Pointer to vdev + * @interval: T2LM timer interval + * @t2lm_ie_index: T2LM IE index + * + * Return: qdf status + */ +QDF_STATUS +wlan_mlo_t2lm_timer_start(struct wlan_objmgr_vdev *vdev, + uint32_t interval, uint8_t t2lm_ie_index); + +/** + * wlan_mlo_t2lm_timer_stop() - API to stop TID-to-link mapping timer + * @vdev: Pointer to vdev + * + * Return: qdf status + */ +QDF_STATUS +wlan_mlo_t2lm_timer_stop(struct wlan_objmgr_vdev *vdev); + +/** + * wlan_mlo_t2lm_timer_expiry_handler() - API to handle t2lm timer expiry + * @vdev: Pointer to vdev structure + * + * Return: none + */ +void +wlan_mlo_t2lm_timer_expiry_handler(void *vdev); + +/** + * wlan_handle_t2lm_timer() - API to handle TID-to-link mapping timer + * @vdev: Pointer to vdev + * @ie_idx: ie index value + * + * Return: qdf status + */ +QDF_STATUS +wlan_handle_t2lm_timer(struct wlan_objmgr_vdev *vdev, uint8_t ie_idx); + +/** + * wlan_process_bcn_prbrsp_t2lm_ie() - API to process the received T2LM IE from + * beacon/probe response. + * @vdev: Pointer to vdev + * @rx_t2lm_ie: Received T2LM IE + * @tsf: Local TSF value + * + * Return QDF_STATUS + */ +QDF_STATUS wlan_process_bcn_prbrsp_t2lm_ie(struct wlan_objmgr_vdev *vdev, + struct wlan_t2lm_context *rx_t2lm_ie, + uint64_t tsf); + #else static inline QDF_STATUS wlan_mlo_parse_t2lm_ie( struct wlan_t2lm_onging_negotiation_info *t2lm, uint8_t *ie) @@ -165,5 +308,42 @@ uint8_t *wlan_mlo_add_t2lm_info_ie(uint8_t *frm, struct wlan_t2lm_info *t2lm) { return frm; } + +static inline QDF_STATUS +wlan_mlo_t2lm_timer_init(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline QDF_STATUS +wlan_mlo_t2lm_timer_start(struct wlan_objmgr_vdev *vdev, + uint32_t interval, uint8_t t2lm_ie_index) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline QDF_STATUS +wlan_mlo_t2lm_timer_stop(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline void +wlan_mlo_t2lm_timer_expiry_handler(void *vdev) +{} + +static inline QDF_STATUS +wlan_handle_t2lm_timer(struct wlan_objmgr_vdev *vdev, uint8_t ie_idx) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline QDF_STATUS +wlan_process_bcn_prbrsp_t2lm_ie(struct wlan_objmgr_vdev *vdev, + struct wlan_t2lm_context *rx_t2lm_ie, + uint64_t tsf) +{ + return QDF_STATUS_SUCCESS; +} #endif /* WLAN_FEATURE_11BE */ #endif /* _WLAN_MLO_T2LM_H_ */ diff --git a/umac/mlo_mgr/src/wlan_mlo_mgr_main.c b/umac/mlo_mgr/src/wlan_mlo_mgr_main.c index 87b94ee674..a03e288574 100644 --- a/umac/mlo_mgr/src/wlan_mlo_mgr_main.c +++ b/umac/mlo_mgr/src/wlan_mlo_mgr_main.c @@ -29,6 +29,7 @@ #include #include "wlan_mlo_mgr_msgq.h" #include +#include static void mlo_global_ctx_deinit(void) { @@ -436,10 +437,12 @@ QDF_STATUS wlan_mlo_check_valid_config(struct wlan_mlo_dev_context *ml_dev, * mlo_t2lm_ctx_init() - API to initialize the t2lm context with the default * values. * @ml_dev: Pointer to ML Dev context + * @vdev: Pointer to vdev structure * * Return: None */ -static inline void mlo_t2lm_ctx_init(struct wlan_mlo_dev_context *ml_dev) +static inline void mlo_t2lm_ctx_init(struct wlan_mlo_dev_context *ml_dev, + struct wlan_objmgr_vdev *vdev) { struct wlan_t2lm_info *t2lm; @@ -450,6 +453,8 @@ static inline void mlo_t2lm_ctx_init(struct wlan_mlo_dev_context *ml_dev) ml_dev->t2lm_ctx.num_of_t2lm_ie = 1; t2lm->direction = WLAN_T2LM_BIDI_DIRECTION; t2lm->default_link_mapping = 1; + + wlan_mlo_t2lm_timer_init(vdev); } static QDF_STATUS mlo_dev_ctx_init(struct wlan_objmgr_vdev *vdev) @@ -527,7 +532,7 @@ static QDF_STATUS mlo_dev_ctx_init(struct wlan_objmgr_vdev *vdev) qdf_list_insert_back(&g_mlo_ctx->ml_dev_list, &ml_dev->node); ml_link_lock_release(g_mlo_ctx); - mlo_t2lm_ctx_init(ml_dev); + mlo_t2lm_ctx_init(ml_dev, vdev); return status; } diff --git a/umac/mlo_mgr/src/wlan_mlo_t2lm.c b/umac/mlo_mgr/src/wlan_mlo_t2lm.c index d65c5a4440..a5e8806fce 100644 --- a/umac/mlo_mgr/src/wlan_mlo_t2lm.c +++ b/umac/mlo_mgr/src/wlan_mlo_t2lm.c @@ -25,6 +25,7 @@ #include #include #include +#include /** * wlan_mlo_parse_t2lm_info() - Parse T2LM IE fields @@ -621,12 +622,31 @@ static void wlan_mlo_t2lm_handle_expected_duration_expiry( if (!t2lm_ctx->t2lm_ie[i].t2lm.expected_duration_present) continue; - qdf_mem_zero(&t2lm_ctx->t2lm_ie[i], - sizeof(struct wlan_mlo_t2lm_ie)); - t2lm_ctx->t2lm_ie[i].t2lm.direction = WLAN_T2LM_BIDI_DIRECTION; - t2lm_ctx->t2lm_ie[i].t2lm.default_link_mapping = 1; - t2lm_debug("vdev_id:%d Expected duration is expired", - vdev_id); + /* If two T2LM IEs are present, and expected duration of first + * T2LM IE is expired, copy the T2LM IE from index 1 to index 0. + * Mark mapping switch time present as false and clear the + * mapping switch time value. + * If one T2LM IE is present, and the expected duration is + * expired, configure the T2LM IE with the default values. + */ + if (!i && t2lm_ctx->num_of_t2lm_ie == WLAN_MAX_T2LM_IE) { + qdf_mem_copy(&t2lm_ctx->t2lm_ie[0], + &t2lm_ctx->t2lm_ie[1], + sizeof(struct wlan_mlo_t2lm_ie)); + t2lm_ctx->t2lm_ie[0].t2lm.mapping_switch_time_present = + false; + t2lm_ctx->t2lm_ie[0].t2lm.mapping_switch_time = 0; + t2lm_debug("vdev_id:%d mark the advertised T2LM as established", + vdev_id); + } else { + qdf_mem_zero(&t2lm_ctx->t2lm_ie[i], + sizeof(struct wlan_mlo_t2lm_ie)); + t2lm_ctx->t2lm_ie[i].t2lm.direction = + WLAN_T2LM_BIDI_DIRECTION; + t2lm_ctx->t2lm_ie[i].t2lm.default_link_mapping = 1; + t2lm_debug("vdev_id:%d Expected duration is expired", + vdev_id); + } } } @@ -678,3 +698,311 @@ QDF_STATUS wlan_mlo_vdev_tid_to_link_map_event( return QDF_STATUS_SUCCESS; } + +static +QDF_STATUS wlan_send_tid_to_link_mapping(struct wlan_objmgr_vdev *vdev, + struct wlan_t2lm_info *t2lm) +{ + struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops; + struct wlan_objmgr_vdev *co_mld_vdev; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS] = {NULL}; + uint16_t vdev_count = 0; + int i = 0; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + t2lm_err("null psoc"); + return QDF_STATUS_E_NULL_VALUE; + } + + mlo_tx_ops = &psoc->soc_cb.tx_ops->mlo_ops; + if (!mlo_tx_ops) { + t2lm_err("tx_ops is null!"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (!mlo_tx_ops->send_tid_to_link_mapping) { + t2lm_err("send_tid_to_link_mapping is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + mlo_get_ml_vdev_list(vdev, &vdev_count, wlan_vdev_list); + if (!vdev_count) { + t2lm_err("Number of VDEVs under MLD is reported as 0"); + return QDF_STATUS_E_NULL_VALUE; + } + + for (i = 0; i < vdev_count; i++) { + co_mld_vdev = wlan_vdev_list[i]; + if (!co_mld_vdev) { + t2lm_err("co_mld_vdev is null"); + mlo_release_vdev_ref(co_mld_vdev); + continue; + } + + status = mlo_tx_ops->send_tid_to_link_mapping(co_mld_vdev, + t2lm); + if (QDF_IS_STATUS_ERROR(status)) + t2lm_err("Failed to send T2LM command to FW"); + mlo_release_vdev_ref(co_mld_vdev); + } + + return status; +} + +void wlan_mlo_t2lm_timer_expiry_handler(void *vdev) +{ + struct wlan_objmgr_vdev *vdev_ctx = (struct wlan_objmgr_vdev *)vdev; + + struct wlan_t2lm_timer *t2lm_timer; + struct wlan_t2lm_context *t2lm_ctx; + uint8_t t2lm_ie_idx; + + if (!vdev_ctx || !vdev_ctx->mlo_dev_ctx) + return; + + t2lm_ctx = &vdev_ctx->mlo_dev_ctx->t2lm_ctx; + t2lm_timer = &vdev_ctx->mlo_dev_ctx->t2lm_ctx.t2lm_timer; + t2lm_ie_idx = t2lm_timer->t2lm_ie_index; + if (t2lm_ie_idx >= WLAN_MAX_T2LM_IE) + return; + + wlan_mlo_t2lm_timer_stop(vdev_ctx); + + if (t2lm_ctx->t2lm_ie[t2lm_ie_idx].t2lm.mapping_switch_time_present) { + wlan_send_tid_to_link_mapping( + vdev, &t2lm_ctx->t2lm_ie[t2lm_ie_idx].t2lm); + wlan_mlo_t2lm_handle_mapping_switch_time_expiry(t2lm_ctx, vdev); + wlan_handle_t2lm_timer(vdev_ctx, t2lm_timer->t2lm_ie_index); + } else if (!t2lm_ie_idx) { + wlan_mlo_t2lm_handle_expected_duration_expiry(t2lm_ctx, vdev); + wlan_handle_t2lm_timer(vdev_ctx, t2lm_timer->t2lm_ie_index); + } +} + +QDF_STATUS +wlan_mlo_t2lm_timer_init(struct wlan_objmgr_vdev *vdev) +{ + struct wlan_t2lm_timer *t2lm_timer = NULL; + + if (!vdev || !vdev->mlo_dev_ctx) + return QDF_STATUS_E_FAILURE; + + t2lm_timer = &vdev->mlo_dev_ctx->t2lm_ctx.t2lm_timer; + if (!t2lm_timer) { + t2lm_err("t2lm timer ctx is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + t2lm_dev_lock_acquire(&vdev->mlo_dev_ctx->t2lm_ctx); + qdf_timer_init(NULL, &t2lm_timer->t2lm_timer, + wlan_mlo_t2lm_timer_expiry_handler, + vdev, QDF_TIMER_TYPE_WAKE_APPS); + + t2lm_timer->timer_started = false; + t2lm_timer->timer_interval = 0; + t2lm_timer->t2lm_ie_index = 0; + t2lm_dev_lock_release(&vdev->mlo_dev_ctx->t2lm_ctx); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlo_t2lm_timer_start(struct wlan_objmgr_vdev *vdev, + uint32_t interval, uint8_t t2lm_ie_index) +{ + struct wlan_t2lm_timer *t2lm_timer; + struct wlan_t2lm_context *t2lm_ctx; + struct vdev_mlme_obj *vdev_mlme; + + if (!vdev || !vdev->mlo_dev_ctx) + return QDF_STATUS_E_NULL_VALUE; + + if (interval == 0) { + t2lm_debug("Timer interval is 0"); + return QDF_STATUS_E_NULL_VALUE; + } + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) + return QDF_STATUS_E_FAILURE; + + t2lm_ctx = &vdev->mlo_dev_ctx->t2lm_ctx; + t2lm_timer = &vdev->mlo_dev_ctx->t2lm_ctx.t2lm_timer; + if (!t2lm_timer) { + t2lm_err("t2lm timer ctx is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + t2lm_debug("t2lm timer started with interval %d", interval); + t2lm_dev_lock_acquire(&vdev->mlo_dev_ctx->t2lm_ctx); + if (t2lm_ctx->t2lm_ie[t2lm_ie_index].t2lm.mapping_switch_time_present) + t2lm_timer->timer_interval = + t2lm_ctx->t2lm_ie[t2lm_ie_index].t2lm.mapping_switch_time; + else + t2lm_timer->timer_interval = interval * + vdev_mlme->proto.generic.beacon_interval * 1000; + + t2lm_timer->t2lm_ie_index = t2lm_ie_index; + t2lm_timer->timer_started = true; + qdf_timer_start(&t2lm_timer->t2lm_timer, t2lm_timer->timer_interval); + t2lm_dev_lock_release(&vdev->mlo_dev_ctx->t2lm_ctx); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlo_t2lm_timer_stop(struct wlan_objmgr_vdev *vdev) +{ + struct wlan_t2lm_timer *t2lm_timer; + + if (!vdev || !vdev->mlo_dev_ctx) + return QDF_STATUS_E_NULL_VALUE; + + t2lm_timer = &vdev->mlo_dev_ctx->t2lm_ctx.t2lm_timer; + if (!t2lm_timer) { + t2lm_err("t2lm timer ctx is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + t2lm_dev_lock_acquire(&vdev->mlo_dev_ctx->t2lm_ctx); + if (t2lm_timer->timer_started) { + qdf_timer_stop(&t2lm_timer->t2lm_timer); + t2lm_timer->timer_started = false; + t2lm_timer->timer_interval = 0; + } + t2lm_dev_lock_release(&vdev->mlo_dev_ctx->t2lm_ctx); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_handle_t2lm_timer(struct wlan_objmgr_vdev *vdev, uint8_t ie_idx) +{ + struct wlan_t2lm_context *t2lm_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!vdev || !vdev->mlo_dev_ctx) + return QDF_STATUS_E_NULL_VALUE; + + t2lm_ctx = &vdev->mlo_dev_ctx->t2lm_ctx; + if (!t2lm_ctx->num_of_t2lm_ie) { + t2lm_err("No T2LM IE present"); + return QDF_STATUS_SUCCESS; + } + + if (ie_idx >= WLAN_MAX_T2LM_IE) { + t2lm_err("Invalid T2LM IE index"); + return QDF_STATUS_E_FAILURE; + } + + if (!t2lm_ctx->t2lm_ie[ie_idx].t2lm.mapping_switch_time_present && + !t2lm_ctx->t2lm_ie[ie_idx].t2lm.expected_duration_present) { + /* non-default to default mapping case */ + wlan_send_tid_to_link_mapping(vdev, + &t2lm_ctx->t2lm_ie[ie_idx].t2lm); + } else if (t2lm_ctx->t2lm_ie[ie_idx].t2lm.mapping_switch_time_present) { + /* Default to non-default mapping case */ + status = wlan_mlo_t2lm_timer_start( + vdev, + t2lm_ctx->t2lm_ie[ie_idx].t2lm.mapping_switch_time, + ie_idx); + } else if (t2lm_ctx->t2lm_ie[ie_idx].t2lm.expected_duration_present) { + wlan_send_tid_to_link_mapping( + vdev, &t2lm_ctx->t2lm_ie[ie_idx].t2lm); + + if (t2lm_ctx->t2lm_ie[ie_idx].t2lm.expected_duration != + T2LM_EXPECTED_DURATION_MAX_VALUE) + status = wlan_mlo_t2lm_timer_start( + vdev, + t2lm_ctx->t2lm_ie[ie_idx].t2lm.expected_duration, + ie_idx); + } + + return status; +} + +/** + * wlan_update_mapping_switch_time_expected_dur() - API to update the mapping + * switch time and expected duration. + * @vdev:Pointer to vdev + * @rx_t2lm: Pointer to received T2LM IE + * @tsf: TSF value of beacon/probe response + * + * Return: None + */ +static void wlan_update_mapping_switch_time_expected_dur( + struct wlan_objmgr_vdev *vdev, struct wlan_t2lm_info *rx_t2lm, + uint64_t tsf) +{ + struct wlan_t2lm_context *t2lm_ctx; + uint16_t tsf_bit25_16, ms_time; + bool match_found = false; + int j; + + tsf_bit25_16 = (tsf & 0x3FF0000) >> 16; + t2lm_ctx = &vdev->mlo_dev_ctx->t2lm_ctx; + + for (j = 0; j < t2lm_ctx->num_of_t2lm_ie; j++) { + /* Match not found */ + if (qdf_mem_cmp(rx_t2lm->ieee_link_map_tid, + t2lm_ctx->t2lm_ie[j].t2lm.ieee_link_map_tid, + sizeof(uint16_t) * T2LM_MAX_NUM_TIDS)) + continue; + + if (t2lm_ctx->t2lm_ie[j].t2lm.mapping_switch_time_present) { + ms_time = rx_t2lm->mapping_switch_time; + + if (ms_time > tsf_bit25_16) { + t2lm_ctx->t2lm_ie[j].t2lm.mapping_switch_time = + ((ms_time - tsf_bit25_16) * 1024) / 1000; + } else { + t2lm_ctx->t2lm_ie[j].t2lm.mapping_switch_time = + ((0xFFFF - (tsf_bit25_16 - ms_time)) * 1024) / 1000; + } + } + + if (t2lm_ctx->t2lm_ie[j].t2lm.expected_duration_present) { + t2lm_ctx->t2lm_ie[j].t2lm.expected_duration = + rx_t2lm->expected_duration; + } + + match_found = true; + break; + } + + if (!match_found && + t2lm_ctx->num_of_t2lm_ie < WLAN_MAX_T2LM_IE) { + qdf_mem_copy(&t2lm_ctx->t2lm_ie[t2lm_ctx->num_of_t2lm_ie].t2lm, + rx_t2lm, sizeof(struct wlan_t2lm_info)); + t2lm_ctx->num_of_t2lm_ie++; + } +} + +QDF_STATUS wlan_process_bcn_prbrsp_t2lm_ie( + struct wlan_objmgr_vdev *vdev, + struct wlan_t2lm_context *rx_t2lm_ie, uint64_t tsf) +{ + struct wlan_t2lm_context *t2lm_ctx; + int i; + + t2lm_ctx = &vdev->mlo_dev_ctx->t2lm_ctx; + + for (i = 0; i < rx_t2lm_ie->num_of_t2lm_ie; i++) { + wlan_update_mapping_switch_time_expected_dur( + vdev, &rx_t2lm_ie->t2lm_ie[i].t2lm, tsf); + } + + if (!wlan_cm_is_vdev_connected(vdev)) + return QDF_STATUS_SUCCESS; + + /* Do not start the timer if STA is not in connected state */ + for (i = 0; i < t2lm_ctx->num_of_t2lm_ie; i++) { + if (t2lm_ctx->t2lm_ie[i].t2lm.mapping_switch_time_present || + t2lm_ctx->t2lm_ie[i].t2lm.expected_duration_present) { + wlan_handle_t2lm_timer(vdev, i); + break; + } + } + + return QDF_STATUS_SUCCESS; +}